'''
Created on May 18, 2013
@author: Duong Nguyen
'''

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.decomposition import KernelPCA, PCA
from sklearn.metrics.pairwise import euclidean_distances
from sklearn.datasets import make_circles, make_moons, load_iris, load_digits

def myKPCA(X, Y, kernel_type='gauss', c=3, deg=2, ncomp=2, dataset='wine', show=False):
    """ Kernel PCA.
        Params:
        --------------
        X: n x d samples matrix
        Y: n x 1 vector of class labels
        kernel_type: Kernel type (Polynomial, RBF, etc.)
        c: Gaussian width (Ignored if kernel_type is polynomial)
        deg: Degree (Ignored if kernel_type is Gaussian kernel.)
        ncomp: #principal components
        dataset: Name of dataset
        show: True to show the embedding.
    """

    def poly_kernel(deg):
        """ Polynomial kernel.
            Params:
            ------------
            deg: Degree of polynomial.

            Returns:
            ------------
            K: kernel matrix of size n x n, where K_ij = k(x_i,x_j) = <x_i, x_j> ^ deg.
        """
        return np.dot(X,X.T)**deg

    def gaussian_kernel(bw):
        """ Gaussian kernel.
            Params:
            ------------
            bw: bandwidth parameter of Gaussian kernel

            Returns:
            -------------
            K: kernel matrix of size n x n, where K_ij = exp(-||x_i-x_j||^2 / bw^2).
        """
        sqDist = euclidean_distances(X, X, squared=True)
        return np.exp(-sqDist/(bw*bw))

    n, d = X.shape
    if ncomp > d:
        ncomp = d

    H = np.eye(n, dtype='float64') - 1.0/n * np.ones((n,n),dtype='float64') # centering matrix

    if kernel_type == 'gauss':
        K = gaussian_kernel(c)
    elif kernel_type == 'poly':
        K = poly_kernel(d)
    else:
        raise ValueError, "Invalid kernel type!"

    HKH = np.dot(np.dot(H,K),H)
    w, v = np.linalg.eig(HKH)
    sorted_ind = np.argsort(w)[::-1]
    w = w[sorted_ind]
    v = v[:,sorted_ind]
    A = v[:,0:ncomp]
    D = np.diag(w[0:ncomp]**(-0.5))
    P1 = np.dot(np.dot(D, A.T),H) # D tA H
    X_kpca = np.dot(P1, K) - 1.0/n * np.dot(P1, np.dot(K, np.ones((n,n))))
    X_kpca = X_kpca.T

    def myplot():
        """ Plot Kernel PCA embedding.
            For simplicity of color scheme, assume up to 3 classes.
        """
        fig = plt.figure()
        fig.clf()
        n_class = len(np.unique(Y))
        assert n_class <= 3, "Up to 3 classes!"
        cs = ['r', 'g', 'b']
        ms = ['x', 'o', 's']
        labels = ['Class 1', 'Class 2', 'Class 3']

        if ncomp == 2:
            for i in range(n_class):
                plt.plot(X_kpca[Y==i, 0], X_kpca[Y==i, 1], cs[i]+ms[i], label=labels[i])

            plt.legend(loc='best', prop={'size':20})
            plt.axis('equal')
            plt.axis('off')
        elif ncomp == 3:
            ax = fig.add_subplot(111, projection="3d")
            for i in range(n_class):
                ax.plot(X_kpca[Y==i, 0], X_kpca[Y==i, 1], X_kpca[Y==i, 2], cs[i]+ms[i], label=labels[i])

            ax.legend(loc='best', prop={'size':20})
            ax.axis('equal')
            ax.axis('off')
            ax.view_init(50,70)

        fname='kpca-%s-%s-%dd-c_%d-deg_%d.png' %(dataset, kernel_type, ncomp, c, deg)
        plt.savefig(fname)
        if show:
            plt.show()
    myplot()

def testPCA(X, Y, ncomp=2, dataset='wine'):
    """ PCA. To compare with KPCA."""
    pca = PCA(n_components=ncomp, whiten=True)
    pca.fit(X)
    X_pca = pca.transform(X)
    n_class = len(np.unique(Y))

    ms = ['x', 'o', 's']
    cs = ['r', 'g', 'b']
    labels = ['Class 1', 'Class 2', 'Class 3']

    fig = plt.figure()
    fig.clf()

    if ncomp == 2:
        for i in range(n_class):
            plt.plot(X_pca[Y==i, 0], X_pca[Y==i, 1], cs[i]+ms[i], label=labels[i])
        plt.legend(loc='best', prop={'size':20})
        plt.axis('equal')
        plt.axis('off')
    elif ncomp == 3:
        ax = fig.add_subplot(111, projection="3d")
        for i in range(n_class):
            ax.plot(X_pca[Y==i, 0], X_pca[Y==i, 1], X_pca[Y==i, 2], cs[i]+ms[i], label=labels[i])

        ax.legend(loc='best', prop={'size':20})
        ax.axis('equal')
        ax.axis('off')
        ax.view_init(50,70)

    figname = 'pca-%s-%dd' %(dataset, ncomp)
    plt.savefig(figname)

def plotData(X, Y, fname):
    """ Use to plot original 2d dataset."""
    n_class = len(np.unique(Y))
    assert n_class <= 3, "Dim <= 3"
    ms = ['x', 'o', 's']
    cs = ['r', 'g', 'b']
    labels = ['Class 1', 'Class 2', 'Class 3']
    fig = plt.figure()
    fig.clf()
    for i in range(n_class):
        plt.plot(X[Y==i, 0], X[Y==i, 1], cs[i]+ms[i], label=labels[i])
    plt.legend(loc='best', prop={'size':20})
    plt.axis('equal')
    plt.savefig(fname)

def withWineData():
    def loadWineData(fnames=['normalized-wine-c1.txt', 'normalized-wine-c2.txt', 'normalized-wine-c3.txt']):
        X = []
        Y = []
        label = 0
        for fname in fnames:
            with open(fname, 'rt') as fin:
                for line in fin:
                    row = map(float, line.strip().split(','))
                    X.append(row)
                    Y.append(label)
            label += 1

        X = np.asarray(X, dtype='float64')
        Y = np.asarray(Y, dtype='int16')
        return X, Y

    X, Y = loadWineData()
    testPCA(X, Y, ncomp=2, dataset='wine')
    myKPCA(X, Y, kernel_type='gauss', c=3, deg=2, ncomp=2, dataset='wine')
    #myKPCA(X, Y, kernel_type='poly', c=3, deg=1, ncomp=2, dataset='wine')

def withCircleData():
    np.random.seed(0)
    X, Y = make_circles(n_samples=400, noise=.05, factor=.3)
    #plotData(X, Y, 'original-circle.png')
    #testPCA(X, Y, ncomp=2, dataset='circles')
    #myKPCA(X, Y, kernel_type='gauss', c=1, deg=2, ncomp=2, dataset='circles')
    myKPCA(X, Y, kernel_type='poly', c=1, deg=10, ncomp=2, dataset='circles')

def withMoonData():
    np.random.seed(0)
    X, Y = make_moons(n_samples=400, noise=.05)
    plotData(X, Y, 'original-moon.png')
    testPCA(X, Y, ncomp=2, dataset='moons')
    myKPCA(X, Y, kernel_type='gauss', c=3, deg=2, ncomp=2, dataset='moons')

def withIrisData():
    iris = load_iris()
    X = iris.data
    Y = iris.target
    #testPCA(X, Y, ncomp=2, dataset='iris')
    myKPCA(X, Y, kernel_type='poly', c=1, deg=3, ncomp=2, dataset='iris')

def withDigitData(n_class):
    digits = load_digits(n_class=n_class)
    X = digits.data
    Y = digits.target
    #testPCA(X, Y, ncomp=2, dataset='digit')
    myKPCA(X, Y, kernel_type='gauss', c=10, deg=3, ncomp=2, dataset='digits')
if __name__ == '__main__':
    #withWineData()
    #withCircleData()
    #withMoonData()
    #withIrisData()
    withDigitData(3)


"""
By setting the transition and observation matrices to the identity, we can use
the Kalman Filter to estimate an exponentially weighted moving average, where
the window is decided by the Kalman Gain K.

See
[1] Quantopian - Kalman Filters:
https://github.com/quantopian/research_public/blob/master/notebooks/lectures/Kalman_Filters/notebook.ipynb
"""
import numpy as np
import matplotlib.pyplot as plt
from kalmanfilter import KalmanFilter

# -----------------------------------------------------------------------------
# function to generate observations
def generate_func(start, end, step, beta, var):
    """
    Generate a noisy sine wave function

    Parameters
    ----------
    start : int
        Initial X value.
    end : int
        Final X value.
    step : float or int
        Space between values.
    beta : float or int
        Slope of the sine wave.
    var : float or int
        Noise variance.

    Returns
    -------
    out : numpy.array
        Noisy sine wave.
    """
    _space = np.arange(start=start, stop=end, step=step)
    _sin = np.sin(_space)

    out = np.array(
        [beta*x + var*np.random.randn() for x in range(len(_space))]
    )
    out += _sin
    return out

if __name__ == "__main__":
    # -------------------------------------------------------------------------
    # generate noisy observations
    Z = generate_func(start=-10, end=10, step=0.1, beta=0.02, var=0.3)

    # kalman settings
    A = np.expand_dims(np.ones((len(Z),1)), axis=1) # transition matrix
    xk = np.array([[1]]) # initial mean estimate

    B = np.expand_dims(np.zeros((len(Z),1)), axis=1) # control-input matrix
    U = np.zeros((len(Z), 1)) # control-input vector

    Pk = np.array([[1]]) # initial covariance estimate
    Q = np.ones((len(Z))) * 0.005 # process noise covariance

    H = A.copy() # observation matrix
    R = np.ones((len(Z))) * 0.01 # measurement noise covariance

    # -------------------------------------------------------------------------
    # run Kalman filter
    kf = KalmanFilter(A=A, xk=xk, B=B, Pk=Pk, H=H, Q=Q, R=R)
    states, errors = kf.filter(Z=Z, U=U)
    kalman_gain = np.stack([val.item() for val in kf.kalman_gains])

    # as a flat array
    states = np.stack([val.item() for val in states])
    errors = np.stack([val.item() for val in errors])

    # plot
    fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(15,4))
    ax[0].plot(Z)
    ax[0].plot(states)
    ax[0].set_title('Estimated means over signal')

    ax[1].plot(errors)
    ax[1].set_title('Covariances')

    ax[2].plot(kalman_gain)
    ax[2].set_title('Kalman Gain')

    plt.show()


import numpy as np

c, v = np.loadtxt("data.csv", delimiter=',', usecols=(6, 7), unpack=True)
print("median = ", np.median(c))
sorted = np.msort(c)
print("sorted = ", sorted)
N = len(c)
print("middle = ", sorted[(N - 1) // 2])
print("avg middle = ", (sorted[(N - 1) // 2] + sorted[(N // 2)]) / 2)
print("variance = ", np.var(c))
print("variance from definition = ", np.mean((c - c.mean()) ** 2))

"""
3.12 stock returns ratio.
"""
returns = np.diff(c) / c[: -1]
print("Standard deviation = ", np.std(returns))

log_returns = np.diff(np.log(c))

pos_ret_ids = np.where(returns > 0)
print("ids with positive returns: ", pos_ret_ids)
annual_volatility = np.std(log_returns) / np.mean(log_returns)
annual_volatility = annual_volatility / np.sqrt(1. / 252.)
print("annual_volability: ", annual_volatility)
print("Monthly volatility: ", annual_volatility * np.sqrt(1. / 12.))

"""
3.13,3.14 Analysis date data.
"""
import datetime

def date_str_2_num(s):
    return datetime.datetime.strptime(s.decode("ascii"), "%d-%m-%Y").date().weekday()

dates, open, high, low, close, volumn = np.loadtxt("data.csv", delimiter=",",
                                                   usecols=(1, 3, 4, 5, 6, 7),
                                                   converters={1: date_str_2_num}, unpack=True)

print("dates: ", dates)
avgs = np.zeros(5)
weighted_avgs = np.zeros(5)
for i in range(5):
    ids = np.where(dates == i)
    prices = np.take(close, ids)
    v = np.take(volumn, ids)
    avg = np.average(prices)
    weighted_avg = np.average(prices, weights=v)
    print("Day: ", i, "Prices: ", prices, "Average: ", avgs, "weighted_avgs: ", weighted_avgs)
    avgs[i] = avg
    weighted_avgs[i] = weighted_avg

top = np.max(avgs)
w_top = np.max(weighted_avgs)
print("Highest average by weekdays: ", top, "weighted: ", w_top)
print("Top day of the week: ", np.argmax(avgs), "weighted: ", np.argmax(weighted_avgs))
bottom = np.min(avgs)
w_bottom = np.min(weighted_avgs)
print("Lowest average by weekdays:", bottom, "weighted: ", w_bottom)
print("Bottom day of the week: ", np.argmin(avgs), "weighted", np.argmin(weighted_avgs))

"""
3.15, 3.16, sumup data
"""

close = close[:16]
dates = dates[:16]
first_monday = np.ravel(np.where(dates == 0))[0]
print("The first Monday index is: ", first_monday)

last_friday = np.ravel(np.where(dates == 4))[-1]
print("The last Friday index is ", last_friday)

week_ids = np.arange(first_monday, last_friday + 1)
print("Weeks ids initial", week_ids)
week_ids = np.split(week_ids, 3)
print("Weeks ids after split: ", week_ids)

def summarize(a, o, h, l, c):
    monday_open = o[a[0]]
    week_high = np.max(np.take(h, a))
    week_low = np.min(np.take(l, a))
    friday_close = c[a[-1]]
    return "APPL", monday_open, week_high, week_low, friday_close

week_summary = np.apply_along_axis(summarize, 1, week_ids, open, high, low, close)
print("week_summary: ", week_summary)
np.savetxt("week_summary.txt", week_summary, delimiter=",", fmt="%s")

"""
3.17, 1.18, ATR(Average True, Range)
"""
import numpy as np
import sys

h, l, c = np.loadtxt("data.csv", delimiter=",", usecols=(4, 5, 6), unpack=True)
N = int(10)
print("l_h", len(h), "l_l", len(l))
print("close:", c)
h = h[-N:]
l = l[-N:]
previous_close = c[-N - 1:-1]
print("previous_close, len: ", previous_close, len(previous_close))
true_range = np.maximum(h - l, h - previous_close, previous_close - l)
print("true range", true_range)

atr = np.zeros(N)
atr[0] = np.mean(true_range)

for i in range(1, N):
    atr[i] = (N - 1) * atr[i - 1] + true_range[i]
    atr[i] /= N
print("ATR: ", atr)

"""
3.19, 3.20 Simple Moving Average
"""
import numpy as np
import sys
from matplotlib.pyplot import plot
from matplotlib.pyplot import show

if 0:
    # floor((w + k - 1) / s)
    N = 5
    ws = np.ones(N) / N
    print("weights: ", ws)
    close = np.loadtxt("data.csv", delimiter=',', usecols=(6,), unpack=True)
    sma_all = np.convolve(ws, close)
    print("sma_all", sma_all, len(sma_all))  # len(close) = 30, len(ws) = 5, out = 30 + 5 - 1 = 34
    sma = sma_all[N - 1: -N + 1]  # weights, wave, len(sma) = len(sma_all) - 2 * (N - 1)  = 26
    print("sma", sma, len(sma))
    t = np.arange(N - 1, len(close))  # 4~30, 30 - 4 = 26
    print("t,", len(t))

    plot(t, close[N - 1:], lw=1.0)
    plot(t, sma, lw=2.0)  #
    show()

"""
3.21, 3.22 Exponential Moving Average
"""
if 0:
    x = np.arange(5)
    print("exp", np.exp(x))
    print("linespace", np.linspace(-1, 0, 5))
    N = 5
    ws = np.exp(np.linspace(-1., 0., N))
    ws /= ws.sum()
    print("weights", ws)

    close = np.loadtxt("data.csv", delimiter=",", usecols=(6,), unpack=True)
    ema = np.convolve(ws, close)[N - 1: -N + 1]
    t = np.arange(N - 1, len(close))
    plot(t, close[N - 1:], lw=1.0)
    plot(t, ema, lw=2.0)
    show()

"""
3.23, 3.24, Bollinger Band
"""

import numpy as np
import sys
from matplotlib.pyplot import plot
from matplotlib.pyplot import show

N = 5
weights = np.ones(N) / N
print("weights", weights)
c = np.loadtxt("data.csv", delimiter=',', usecols=(6,), unpack=True)
sma = np.convolve(weights, c)[N - 1: -N + 1]

deviation = []
C = len(c)
for i in range(N - 1, C):  # [4,30)
    if i + N < C:  # spoil
        dev = c[i: i + N]
    else:
        dev = c[-N:]
    averages = np.zeros(N)
    averages.fill(sma[i - N - 1])
    dev = (dev - averages) ** 2  # deviation is not std.
    dev = np.sqrt(np.mean(dev))

    deviation.append(dev)
deviation = 2 * np.array(deviation)
print(len(deviation), len(sma))
upperBB = sma + deviation
lowerBB = sma - deviation
c_slice = c[N - 1:]

t = np.arange(N - 1, C)

# plot(t, c_slice, lw=1.0)
# plot(t, sma, lw=2.0)
# plot(t, upperBB, lw=3.0)
# plot(t, lowerBB, lw=4.0)
# show()

"""
3.25, 3.26
"""

N = 5
c = np.loadtxt("data.csv", delimiter=',', usecols=(6,), unpack=True)
print("c", c)
b = c[-N:]  # last N in c.
b = b[::-1]  # from -1 to -N.
print("b", b)
A = np.zeros((N, N), float)
print("Zeros N by N: ", A)
for i in range(N):
    start = -N - 1 - i
    end = -1 - i
    print("start, end: ", start, end)
    A[i,] = c[-N - 1 - i: -1 - i]
print("A", A)

(x, residuals, rank, s) = np.linalg.lstsq(A, b)  # line algebra, least square.
print("x:", x)  # solve vector.
print("rediduals: ", residuals)  # residual value.
print("rank: ", rank)  # rank of A
print("s:", s)  # special value of A.

print("b * x", np.dot(b, x))

"""
3.27, 3.28
Tending curve
"""

highest, lowest, close = np.loadtxt('data.csv', delimiter=',', usecols=(4, 5, 6), unpack=True)

pivots = (highest + lowest + close) / 3
print("Pivots", pivots)

def fit_line(t, y):
    print(t.shape)
    A = np.vstack([t, np.ones_like(t)]).T
    print(A.shape)
    return np.linalg.lstsq(A, y)[0]

t = np.arange(len(close))
sa, sb = fit_line(t, pivots - (highest - lowest))
ra, rb = fit_line(t, pivots + (highest - lowest))
support = sa * t + sb
resistance = ra * t + rb

condition = (close > support) & (close < resistance)
print("condition", condition)
between_bands = np.ravel(np.where(condition)) #ids
print("between_bands", between_bands, "len: ", len(between_bands), type(between_bands))
print("condition support", support[between_bands])
print("condition close", close[between_bands])
print("condition resistance", resistance[between_bands])
print("Number points of between bands:", between_bands)

print("Tomorrow support: ", sa * (t[-1] + 1) + sb)
print("Tomorrow resistance: ", ra * (t[-1] + 1) + rb)

a1 = close[close > support]
a2 = close[close < resistance]

print("Number of points between 2nd approach: ", len(np.intersect1d(a1, a2)))

# plot(t, close)
# plot(t, support)
# plot(t, resistance)
# show()

""""""
"""
3.30  ndarray trim and compress.
"""

print("3.30 ndarray trim and compress.")

a = np.arange(5)
print("a = ", a)
print("Clipped: ", a.clip(1, 2))

a = np.arange(4)
print(a)
print("Compressed: ", a.compress(a > 2))

"""
3.31, 3.32 Factorial.
"""
print("3.31 Factorial")

b = np.arange(1, 9)
print("b = ", b)
print("Factorial: ", b.prod())
print("Factorials: ", b.cumprod())


## load dependency - third party
import numpy as np

## euclidean distance calculation
def euclidean_dist(a, b, d):

    """
    calculates the euclidean distance between observations for data
    containing only numeric / continuous features, returns float value
    """

    ## create list to store distances
    dist = [None] * d

    ## loop through columns to calculate euclidean
    ## distance for numeric / continuous features
    for i in range(d):

        ## the squared difference of values in
        ## vectors a and b of equal length
        dist[i] = (a.iloc[i] - b.iloc[i]) ** 2

    ## sum all the squared differences and take the square root
    dist = np.sqrt(sum(dist))

    ## return distance list
    return dist

## heom distance calculation
def heom_dist(a_num, b_num, d_num, ranges_num, a_nom, b_nom, d_nom):

    """
    calculates the heterogenous euclidean overlap (heom) distance between
    observations for data containing both numeric / continuous and nominal
    / categorical features, returns float value

    ref:

    Wilson, D., Martinez, T. (1997).
    Improved Heterogeneous Distance Functions.
    Journal of Artificial Intelligence Research, 6:1-34.
    https://arxiv.org/pdf/cs/9701101.pdf.
    """

    ## load dependency
    # import numpy as np

    ## create list to store distances
    dist = [None] * d_num

    ## specify epsilon
    eps = 1e-30

    ## loop through columns to calculate euclidean
    ## distance for numeric / continuous features
    for i in range(d_num):

        ## epsilon utilized to avoid division by zero
        if ranges_num[i] > eps:

            ## the absolute value of the differences between values in
            ## vectors a and b of equal length, divided by their range, squared
            ## (division by range conducted for normalization)
            dist[i] = (abs(a_num.iloc[i] - b_num.iloc[i]) / ranges_num[i]) ** 2

    ## loop through columns to calculate hamming
    ## distance for nominal / categorical features
    for i in range(d_nom):

        ## distance equals 0 for values that are equal
        ## in two vectors a and b of equal length
        if a_nom.iloc[i] == b_nom.iloc[i]:
            dist[i] = 0

        ## distance equals 1 for values that are not equal
        else:
            dist[i] = 1

        ## theoretically, hamming differences are squared when utilized
        ## within heom distance, however, procedurally not required,
        ## as squaring [0,1] returns same result

    ## sum all the squared differences and take the square root
    dist = np.sqrt(sum(dist))

    ## return distance list
    return dist

## hamming distance calculation
def overlap_dist(a, b, d):

    """
    calculates the hamming (overlap) distance between observations for data
    containing only nominal / categorical features, returns float value
    """

    ## create list to store distances
    dist = [None] * d

    ## loop through columns to calculate hamming
    ## distance for nominal / categorical features
    for i in range(d):

        ## distance equals 0 for values that are equal
        ## in two vectors a and b of equal length
        if a.iloc[i] == b.iloc[i]:
            dist[i] = 0

        ## distance equals 1 for values that are not equal
        else:
            dist[i] = 1

    ## sum all the differences
    dist = sum(dist)

    ## return distance list
    return dist


from sympy import *

x = symbols('x[0]')
y = symbols('x[1]')

u = sin(y)*exp(x+y)+cos(y)*exp(x+y)
v = -sin(y)*exp(x+y)
p = pow(x,3)*sin(y)+exp(x+y)
print "u:    ",u
print "v:    ",v
print "p:    ",p
print "\n"

L1 = diff(u,x,x)+diff(u,y,y)
L2 = diff(v,x,x)+diff(v,y,y)
print "Laplacian 1:    ",L1
print "Laplacian 2:    ",L2
print "\n"
A1 = u*diff(u,x)+v*diff(u,y)
A2 = u*diff(v,x)+v*diff(v,y)

print "Advection 1:    ",A1
print "Advection 2:    ",A2

print "\n"
P1 = diff(p,x)
P2 = diff(p,y)
print "Pressure 1:    ",P1
print "Pressure 2:    ",P2
print "\n"

x = symbols('x[0]')
y = symbols('x[1]')
uu = cos(x)*exp(x+y)
b = diff(uu,y)
d = -diff(uu,x)
r = sin(2*pi*y)*sin(2*pi*x)
print "b:    ",b
print "d:    ",d
print "r:    ",r
print "\n"
L1 = diff(d,x,y) - diff(b,y,y)
L2 = diff(b,x,y) - diff(d,x,x)
print "Curl 1:    ",L1
print "Curl 2:    ",L2
print "\n"

print "\n"
R1 = diff(r,x)
R2 = diff(r,y)
print "Multi 1:    ",R1
print "Multi 2:    ",R2

print "\n\n"
print "NS couple"

NS1 = -d*(diff(d,x)-diff(b,y))
NS2 = b*(diff(d,x)-diff(b,y))
print "\n"
print "NS_couple 1:    ",NS1
print "\n"
print "NS_couple 2:    ",NS2

print "\n\n"
print "Maxwell couple"

M1 = diff(u*d-v*b,y)
M2 = -diff(u*d-v*b,x)
print "\n"
print "Maxwell_couple 1:    ",M1

print "\n"
print "Maxwell_couple 2:    ",M2


import logging
import numpy as np
from typing import Optional, Tuple

class ModelFitter:
    """
    | Fit a linear regression model to inputted X and y data. Contains methods for fitting
    | via the normal equation or by gradient descent
    """

    def normal_equation(self,
                        X: np.ndarray,
                        y: np.ndarray) -> np.ndarray:
        """
        | Solve for the vector of coefficients using the normal equation
        |  (X^T X)^-1 X^T y
        """
        self._check_matrix_vector_shapes(X, y, axis=0)

        X = self._add_intercept(X)
        return np.linalg.pinv(X.T.dot(X)).dot(X.T).dot(y)

    def _check_matrix_vector_shapes(self,
                                    X: np.ndarray,
                                    y: np.ndarray,
                                    axis: int) -> None:
        """
        | Checks whether number of rows or columns in X matches length of y
        | - Columns : normal equation
        | - Rows : predictions with theta vector
        """
        if X.shape[axis] != len(y):
            raise ValueError(f"Length of axis {axis} in X ({X.shape[axis]}) does not match length " +
                             f"of y ({len(y)})")

    def _add_intercept(self,
                       X: np.ndarray) -> np.ndarray:
        """
        | Checks whether first column of inputted data is the intercept. If not,
        | adds a column of intercepts to the beginning of the array
        |
        | ----------------------------------------------------------------------
        | Parameters
        | ----------
        |  X : np.ndarray
        |    Array of predictors
        |
        |
        | Returns
        | -------
        |  np.ndarray
        |    Original array if first column is all 1, else original array plus
        |    column with intercept
        """
        if all(X[:, 0] == 1):
            return X
        else:
            return np.c_[np.ones((X.shape[0], 1)), X]

    def predict(self,
                X: np.ndarray,
                theta: np.ndarray):
        """
        | Generate predictions based off of predictors (X) and linear regression
        | coefficients (theta)
        |
        | ----------------------------------------------------------------------
        | Parameters
        | ----------
        |  X : np.ndarray
        |    Matrix of predictors. Must include intercept
        |
        |  theta : np.ndarray
        |    Vector of coefficients
        """
        self._check_matrix_vector_shapes(X, theta, axis=1)
        return X.dot(theta)

    def gradient_descent(self,
                         X: np.ndarray,
                         y: np.ndarray,
                         alpha: Optional[float] = None,
                         max_iter: int = 10000,
                         min_diff_mse: float = 1e-5,
                         scale: bool = True) -> Tuple[np.ndarray, list]:
        """
        | Find linear regression coefficients via gradient descent. Descent occurs
        | for n_iter or until the improvement in MSE falls below stop_thresh.
        |
        | -------------------------------------------------------------------------
        | Parameters
        | ----------
        |  X : np.ndarray
        |    Predictors
        |
        |  y : np.ndarray
        |    Target
        |
        |  alpha : float
        |    The learning rate
        |
        |  max_iter : int
        |    The max number of iterations to run gradient descent, if stop_thresh isn't
        |    reached first
        |
        |  min_diff_mse : float
        |    The improvement in MSE between iterations below which gradient descent stops
        |
        |  scale : bool
        |    Whether to scale the predictors. If True, the value of each feature is subtracted
        |    by the feature mean, then divided by the feature standard deviation
        |
        |
        | Returns
        | -------
        |  np.ndarray
        |    The best fit for linear regression coefficients
        """
        X = self._add_intercept(X)

        # Initialize coefficients at zero
        theta = np.zeros((X.shape[1], 1))

        if alpha is None:
            alpha = self._set_alpha(len(y))

        if scale:
            X = self.scale(X)

        mse = []

        # Perform descent
        for i in range(max_iter):
            preds = self.predict(X, theta)
            theta = self._update_theta(theta, preds, y, X, alpha)
            mse.append(self.get_mse(preds, y))

            # Stop if change in MSE below threshold
            if i > 1 and (mse[i-1] - mse[i]) < min_diff_mse:
                break

            # If MSE is increasing, stop
            if mse[i] > mse[i-1]:
                logging.error("MSE is increasing; a smaller alpha should be used.")
                return theta, mse

        return theta, mse

    def _set_alpha(self,
                   m: int) -> float:
        """
        | Set alpha based on the number of samples.
        |
        | Examples:
        |   10 -> 1e-3
        |   100 -> 1e-5
        |   1000 -> 1e-7
        |
        | ----------------------------------------------------------
        | Parameters
        | ----------
        |  m : int
        |    Number of samples in data
        |
        |
        | Returns
        | -------
        |  float
        """
        return 10 ** -(round(np.log10(m)) * 2 + 1)

    def _update_theta(self,
                      theta: np.ndarray,
                      preds: np.ndarray,
                      y: np.ndarray,
                      X: np.ndarray,
                      alpha: float) -> np.ndarray:
        """
        | Update theta via gradient descent. Derivative of squared errors is
        | multiplied by the learning rate and added to vector of coefficients
        |
        | -------------------------------------------------------------------
        | Parameters
        | ----------
        |  theta : np.ndarray
        |    Vector of coefficients
        |
        |  preds : np.ndarray
        |    Vector of predictions (h(x), or y-hat)
        |
        |  y : np.ndarray
        |    Vector of target variable
        |
        |  X : np.ndarray
        |    Matrix of features
        |
        |  alpha : float
        |    Learning rate
        |
        |
        | Returns
        | -------
        |  np.ndarray
        |    Updated vector of coefficients
        """
        return theta - (1/len(y)) * alpha * (X.T.dot(preds - y))

    def get_mse(self,
                preds: np.ndarray,
                actuals: np.ndarray) -> float:
        """
        | Calculate the mean squared error
        |
        | ------------------------------------
        | Parameters
        | ----------
        |  preds : np.ndarray
        |    Predictions
        |
        |  actuals : np.ndarray
        |    Actual values
        |
        |
        | Returns
        | -------
        |  float
        |    Average squared error
        """
        return np.mean((preds - actuals) ** 2)

    # TODO: fix issue with Nans
    def scale(self,
              X: np.ndarray) -> np.ndarray:
        """
        | For each column of X, subtract the mean and divide by the standard deviation
        | of the column
        |
        | ----------------------------------------------------------------------------
        | Parameters
        | ----------
        |  X : np.ndarray
        |    Matrix of features
        |
        |
        | Returns
        | -------
        |  np.ndarray
        |    Original matrix with each column scaled by mean and standard deviation
        """
        feat_means = np.apply_along_axis(np.mean, 0, X)
        feat_stds = np.apply_along_axis(np.std, 0, X)

        return (X - feat_means) / feat_stds


import numpy as np
from scipy.io import mmread

def is_pos_def(x):
    """check if a matrix is symmetric positive definite"""
    return np.all(np.linalg.eigvals(x) > 0)

def check_symmetric(x, tol=1e-8):
    return np.all(np.abs(x-x.T) < tol)

def precon(A):
    M_inv = np.diag(np.diag(A))
    for i in range(A.shape[0]):
        M_inv[i,i] = 1.0 / A[i,i]
    return M_inv

A = mmread('/home/anaxsouza/Documents/GitHub/coc757_Trabalho_05/bcsstk16.mtx').todense()
b = np.ones((A.shape[0], 1))
x = np.ones((A.shape[0], 1))
M_inv = precon(A)
i_max = 20000
epsilon = np.finfo(float).eps

i = 0
r = b - A @ x
z = M_inv @ r
delta = z.T @ r
delta_0 = delta

#if (is_pos_def(A) == False) | (check_symmetric(A) == False):
    #raise ValueError('Matrix A needs to be symmetric positive definite (SPD)')
while (i < i_max) and (delta > (epsilon ** 2)*delta_0):
    q = A @ z
    alpha = delta/(z.T @ q)
    alpha = alpha.item()
    x = x + alpha * z
    if (i % 50 == 0):
        print(i)
        r = b - A @ x
    else:
        r = r - alpha * q
    z = M_inv @ r
    delta = z.T @ r
    i += 1
print('\ni:\n',i)
print('\nx:')
print(x)
print(x.shape)


# Code is from Osvaldo Martin et al,
# "Bayesian Modeling and Comptuation In Python"
# https://github.com/aloctavodia/BMCP/blob/master/Code/chp_3_5/splines.py

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from patsy import bs, dmatrix

x = np.linspace(0., 1., 20)
knots = [0.25, 0.5, 0.75]

B0 = dmatrix("bs(x, knots=knots, degree=0, include_intercept=True) - 1", {"x": x, "knots":knots})
B1 = dmatrix("bs(x, knots=knots, degree=1, include_intercept=True) - 1", {"x": x, "knots":knots})
B3 = dmatrix("bs(x, knots=knots, degree=3, include_intercept=True) - 1", {"x": x, "knots":knots})

_, axes = plt.subplots(1, 3, sharey=True)
for idx, (B, title, ax) in enumerate(zip((B0, B1, B3),
                                     ("Piecewise constant", "Piecewise linear", "Cubic spline"),
                                      axes)):
    ax.imshow(B, cmap="cet_gray_r", aspect="auto")
    ax.set_xticks(np.arange(B.shape[1]))
    ax.set_yticks(np.arange(B.shape[0]))
    ax.set_yticklabels([np.round(v,1) for v in x])
    ax.spines['left'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.set_title(title)

axes[1].set_xlabel("B-splines")
axes[0].set_ylabel("x", rotation=0, labelpad=15);
plt.savefig('../figures/splines_basis_heatmap.pdf', dpi=300)

titles = ["Piecewise constant", "Piecewise linear", "Cubic spline"]
Bs = [B0, B1, B3]
for i in range(3):
    B= Bs[i]
    title= titles[i]
    fig, ax = plt.subplots()
    ax.imshow(B, cmap="cet_gray_r", aspect="auto")
    ax.set_xticks(np.arange(B.shape[1]))
    ax.set_yticks(np.arange(B.shape[0]))
    ax.set_yticklabels([np.round(v,1) for v in x])
    ax.spines['left'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.set_title(title)
    plt.tight_layout()
    plt.savefig(f'../figures/splines_basis_heatmap{i}.pdf', dpi=300)


import numpy as np

'''

Product of Gaussians 1D

gij(x) =  exp( wi*(x-xi)^2 ) * exp( wj*(x-xj)^2 )

wi*( x^2 + 2*xi*x + xi^2 ) +  wj*( x^2 + 2*xj*x + xj^2 )
(wi+wj)*x^2  +   (wi*xi + wj*xj)*2*x   +   wi*x^2 + wj*x^2

wij*( x^2   +  2*x*xij +   xij^2 )   +    C

wij = (wi+wj)
wij*xij =   (wi*xi + wj*xj)

xij =    (wi*xi + wj*xj) / (wi + wj)

C =   wi*xi^2 + wj*xj^2     -    wij*xij^2
C =   wi*xi^2 + wj*xj^2     -   (wi*xi + wj*xj)^2 / (wi + wj)
C = wi*wj*(xi**2 - 2*xi*xj + xj**2)/(wi + wj)
C = wi*wj*(xi-xj)**2/(wi+wj)
'''

'''
# ======== Sympy Derivation
import sympy as sy
x,wi,xi,Ci,wj,xj,Cj = sy.symbols('x wi xi Ci wj xj Cj')
wij = (wi+wj)
xij = (wi*xi + wj*xj)/wij
C = wi*xi**2 + wj*xj**2   -  wij*xij**2

P1  = wi*(x-xi)**2 +  wj*(x-xj)**2
P1cx = sy.collect(P1.expand(),x,evaluate=False)
print P1cx

P2 = wij*(x-xij)**2   +   C
P2=P2.expand()
P1=P1.expand()
Err = P1  - P2
print Err.simplify()
exit()
'''

'''
# ======== Sympy Check
import sympy as sy
x,wi,xi,Ci,wj,xj,Cj = sy.symbols('x wi xi Ci wj xj Cj')
wxi =  wi*xi
wxj =  wj*xj
W   = (wi+wj)
wx  = (wxi + wxj)
X   = wx/W
C   = wxi*xi + wxj*xj - wx*X

P1  = wi*(x-xi)**2 + wj*(x-xj)**2
P2  = W*(x-X)**2   +   C
P2=P2.expand()
P1=P1.expand()
Err = P1  - P2
print Err.simplify()
exit()
'''

def gaussProduct1D( wi=1.0,xi=0.0,Ci=1, wj=1.0,xj=0.0,Cj=1 ):
    wxi  =  wi*xi
    wxj  =  wj*xj
    W    = (wi+wj)
    wx   = (wxi + wxj)
    X    = wx/W
    logC = wxi*xi + wxj*xj - wx*X
    C   = np.exp(-logC) * Ci * Cj
    return (W,X,C)

def evalGauss1D( xs, w=1.0, x0=0.0, C=1.0 ):
    return np.exp( -w*(xs-x0)**2 )*C

# ================== Main

if __name__ == "__main__":
    xs = np.linspace(-5,5,1000)
    import matplotlib.pyplot as plt

    cs = [1.0,-0.8]
    g1 = ( 1.2,-0.8,1.0 )
    g2 = ( 0.6, 1.2,1.0 )
    g12    =  gaussProduct1D( wi=g1[0],xi=g1[1],Ci=g1[2],  wj=g2[0],xj=g2[1],Cj=g2[2] )

    y1     = evalGauss1D(xs,w=g1 [0],x0=g1 [1],C=g1 [2])
    y2     = evalGauss1D(xs,w=g2 [0],x0=g2 [1],C=g2 [2])
    y12    = evalGauss1D(xs,w=g12[0],x0=g12[1],C=g12[2])
    y12num = y1*y2

    plt.plot( xs, y1 , label='g1')
    plt.plot( xs, y2 , label='g1')
    plt.plot( xs, y12,    label='(g1*g2)ana')
    plt.plot( xs, y12num,':', label='(g1*g2)num')

    gsum  = cs[0]*y1 + cs[1]*y2
    gsum2 = gsum*gsum

    #gsum2_ = (cs[0]**2)*(y1**2) + (cs[1]**2)*(y2**2) +  (2*cs[0]*cs[1])*(y1*y2)
    gsum2_ = (cs[0]**2)*(y1**2) + (cs[1]**2)*(y2**2) +  (2*cs[0]*cs[1])*(y12)

    plt.figure()
    plt.plot( xs, gsum , label='gsum')
    plt.plot( xs, gsum2, label='gsum^2')
    plt.plot( xs, gsum2_ , label='gsum^2 _')

    plt.legend()
    plt.grid()
    #plt.ylim(0,1.5)
    plt.show()


import numpy as np

# def AND(x1, x2):
#     w1, w2, theta = 0.5, .5, .7
#     tmp = x1*w1 + x2*w2
#     if tmp <= theta:
#         return 0
#     elif tmp > theta:
#         return 1

def AND(x1, x2):
    x = np.array([x1, x2])
    w = np.array([.5, .5])
    b = -.7
    tmp = np.sum(w*x) + b
    if tmp <= 0:
        return 0
    else:
        return 1


from sympy import *
import sys
sys.path.insert(1, '..')
from tait_bryan_R_utils import *
from rodrigues_R_utils import *
from quaternion_R_utils import *

x_L, y_L, z_L = symbols('x_L y_L z_L')
x_s, y_s, z_s = symbols('x_s y_s z_s')
px, py, pz = symbols('px py pz')
#om, fi, ka = symbols('om fi ka')
sx, sy, sz = symbols('sx sy sz')
#q0, q1, q2, q3 = symbols('q0 q1 q2 q3')

position_symbols = [px, py, pz]
#orientation_symbols = [om, fi, ka]
orientation_symbols = [sx, sy, sz]
#orientation_symbols = [q0, q1, q2, q3]
landmark_symbols = [x_L, y_L, z_L]

all_symbols = position_symbols + orientation_symbols + landmark_symbols

point_Landmark = Matrix([x_L, y_L, z_L]).vec()
point_source = Matrix([x_s, y_s, z_s, 1]).vec()
#transformed_point_source = (matrix44FromTaitBryan(px, py, pz, om, fi, ka) * point_source)[:-1,:]
transformed_point_source = (matrix44FromRodrigues(px, py, pz, sx, sy, sz) * point_source)[:-1,:]
#transformed_point_source = (matrix44FromQuaternion(px, py, pz, q0, q1, q2, q3) * point_source)[:-1,:]

target_value = Matrix([0,0,0]).vec()
model_function = transformed_point_source-point_Landmark
delta = target_value - model_function
delta_jacobian=delta.jacobian(all_symbols)

print(delta)
print(delta_jacobian)

with open("point_to_point_source_to_landmark_rodrigues_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void point_to_point_source_to_landmark_rodrigues_wc(double &delta_x, double &delta_y, double &delta_z, double px, double py, double pz, double sx, double sy, double sz, double x_s, double y_s, double z_s, double x_L, double y_L, double z_L)\n")
    f_cpp.write("{")
    f_cpp.write("delta_x = %s;\n"%(ccode(delta[0,0])))
    f_cpp.write("delta_y = %s;\n"%(ccode(delta[1,0])))
    f_cpp.write("delta_z = %s;\n"%(ccode(delta[2,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void point_to_point_source_to_landmark_rodrigues_wc_jacobian(Eigen::Matrix<double, 3, 9, Eigen::RowMajor> &j, double px, double py, double pz, double sx, double sy, double sz, double x_s, double y_s, double z_s)\n")
    f_cpp.write("{")
    for i in range (3):
        for j in range (9):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(delta_jacobian[i,j])))
    f_cpp.write("}")


# -*- coding: utf-8 -*-
"""
Created on Wed Apr  1 21:24:02 2020

@author: Leonardo Saccotelli
"""

import numpy as np
import matplotlib.pyplot as plt
import InterpolazionePolinomiale_Algoritmi as IP

def createMyFunction(x):
    y = np.sqrt(x)
    return y

#----------------  DEFINIZIONI DATI DI INTERPOLAZIONE
#Estremo inferiore dell'intervallo da cui estrapolo i nodi
a = 0
#Estremo superiore dell'intervallo da cui estrapolo i nodi
b = 1

#Numero di nodi da estrapolare dall'intervallo [a,b]
n = 50
#Estrapolo n+1 nodi equidistanti dall'intervallo [a,b]
xNodes = np.linspace(a, b, n + 1)
#Calcolo la funzione f(x) = sin(x) negli n+1 nodi
yNodes = createMyFunction(xNodes)

#-------------- COSTRUZIONE POLINOMIO DI INTERPOLAZIONE
#Estrapolo i punti in cui calcolare il polinomio
xPoints = np.linspace(a, b, 20 * len(xNodes) + 1)

#Creo e calcolo il polinomio con il metodo dei coefficienti indeterminati
pol_coeff_indet = IP.indeterminateCoefficientFormula(xNodes, yNodes, xPoints)

#Creo e calcolo il polinomio con la prima formula baricentrica di Lagrange in ogni punto xPoint
pol_1_Lagrange = IP.lagrangeFirstBarycentricFormula(xNodes, yNodes, xPoints)

#Creo e calcolo il polinomio con la seconda formula baricentrica di Lagrange in ogni punto xPoint
pol_2_Lagrange = IP.lagrangeSecondBarycentricFormula(xNodes, yNodes, xPoints)

#Creo e calcolo il polinomio con la formula di Newton in ogni punto xPoint
pol_Newton = IP.NewtonPolynomialInterpolation(xNodes, yNodes, xPoints)

#Calcolo la funzione in ogni punto xPoints
f_xPoints = createMyFunction(xPoints)

#------------- Grafico del polinomio di interpolazione
plt.figure(1, figsize=(12, 12))
plt.plot(xPoints, f_xPoints, label='F(x) = Sqrt(x)')

p_lab = 'Coeff Indet: %d nodi' % n
plt.plot(xPoints, pol_coeff_indet, label=p_lab)

p_lab = 'Lagrange_1: %d nodi' % n
plt.plot(xPoints, pol_1_Lagrange, label=p_lab)

p_lab = 'Lagrange_2: %d nodi' % n
plt.plot(xPoints, pol_2_Lagrange, label=p_lab)

p_lab = 'Newton: %d nodi' % n
plt.plot(xPoints, pol_Newton, label=p_lab)

plt.plot(xNodes, yNodes, 'ro')
plt.xlabel('x')
plt.legend(prop={'size': 20}, loc ='best')

#----------------Grafico del resto del polinomio di interpolazione
plt.figure(2, figsize=(12, 12))

r_lab = 'Coeff Indet: %d nodi' % n
plt.semilogy(xPoints, abs(f_xPoints - pol_coeff_indet), label=r_lab)

r_lab = 'Lagrange_1: %d nodi' % n
plt.semilogy(xPoints, abs(f_xPoints - pol_1_Lagrange), label=r_lab)

r_lab = 'Lagrange_2: %d nodi' % n
plt.semilogy(xPoints, abs(f_xPoints - pol_2_Lagrange), label=r_lab)

r_lab = 'Newton: %d nodi' % n
plt.semilogy(xPoints, abs(f_xPoints - pol_Newton), label=r_lab)

plt.xlabel('x')

plt.legend(prop={'size': 20}, loc ='best')

plt.show()


"""
several loss functions:
CrossEntropy
L1 distance
L2 distance
"""
import numpy as np

class CrossEntropy:
    def __init__(self):
        self.middle = None

    def __call__(self, pred, label):
        return self.forward(pred, label)

    def forward(self, pred, label):
        # default : rows represent the different samples
        #           cols represent the scores
        assert pred.shape == label.shape
        result = (-1 / pred.shape[0]) * np.sum(label * np.log(pred))
        self.middle = pred, label
        return result

    def backward(self):
        pred, label = self.middle
        grad = (-1 / pred.shape[0]) * (label / pred)
        return grad

    def __repr__(self):
        return "Loss Function : CrossEntropy"

class L1:
    def __init__(self):
        self.middle = None

    def __call__(self, pred, label):
        return self.forward(pred, label)

    def forward(self, pred, label):
        assert pred.shape == label.shape
        result = np.mean(np.abs(pred - label))
        self.middle = pred, label
        return result

    def backward(self):
        pred, label = self.middle
        mask = (pred - label) >= 0
        grad = (1 / np.product(pred.shape)) * (2 * mask - 1)
        return grad

    def __repr__(self):
        return "Loss Function : L1"

class L2:
    def __init__(self):
        self.middle = None

    def __call__(self, pred, label):
        return self.forward(pred, label)

    def forward(self, pred, label):
        assert pred.shape == label.shape
        result = np.mean(np.square(pred - label))
        self.middle = pred, label
        return result

    def backward(self):
        pred, label = self.middle
        grad = (2 / np.product(pred.shape)) * (pred - label)
        return grad

    def __repr__(self):
        return "Loss Function : L2"


import matplotlib.pyplot as plt
import numpy as np

import tensor as ts

def plot_signals(s1, s2, signal, t):
    plt.ylabel("Amplitude")
    plt.xlabel("Time [s]")

    ax: plt.Axes = plt.subplot(3, 1, 1)
    ax.plot(t, s1)
    ax.set_title(r"$s_1 = \sin(33Hz * 2\pi t)$")

    ax: plt.Axes = plt.subplot(3, 1, 2)
    ax.plot(t, s2)
    ax.set_title(r"$s_2 = \sin(70Hz * 2\pi t)$")

    ax: plt.Axes = plt.subplot(3, 1, 3)
    ax.plot(t, signal)
    ax.set_title(r"$s = s_1 + s_2$")

    figure: plt.Figure = ax.get_figure()
    # figure.set_size_inches((10, 10))
    figure.tight_layout(h_pad=2)
    figure.show()

def plot_spectrum(amplitudes: np.array, t: np.array, N: int):
    K = len(amplitudes)
    T = t[1] - t[0]  # sampling interval
    # The frequency resolution is determined by: df = sampling frequency / number of samples
    f = np.linspace(0, 1 / T, N)  # (0, 1000), 500 samples

    # we don't care about negative frequencies
    amplitudes = amplitudes[:K // 2]
    f = f[:K // 2]

    figure, ax = plt.subplots()
    ax.bar(f, amplitudes, width=1.5)
    ax.set_ylabel("Amplitude")
    ax.set_xlabel("Frequency [Hz]")
    figure.show()

def main():
    t = np.linspace(0, 0.5, 500)  # 1/1000 -> sampling with 1kH frequency

    # signal = sin(f * 2PIt), where f is signal frequency
    s1 = np.sin(40 * 2 * np.pi * t)
    s2 = 0.5 * np.sin(90 * 2 * np.pi * t)
    signal = s1 + s2
    plot_signals(s1, s2, signal, t)

    N = len(signal)
    K = 200

    frequencies = np.arange(0, K)
    y = ts.fft.dft_naive(signal, frequencies)
    amplitudes = np.abs(y) * 1 / N

    plot_spectrum(amplitudes, t, N)

if __name__ == '__main__':
    main()


#!/usr/bin/python

########################################################
# K-means clustering #
# Sk. Mashfiqur Rahman (CWID: A20102717) #
# collect data from: http://yann.lecun.com/exdb/mnist/#
#######################################################

import numpy as np
import matplotlib.pyplot as plt

def k_means(k, data, _mean):
    N = data.shape[0]
    min_d = np.zeros((N,1),dtype="double")
    min_k = np.zeros((N,1),dtype=int)
    z = np.zeros(shape=(N, k),dtype=int)
    mean_init = np.zeros(np.shape(_mean))
    count = 0
    allowance = 1e-7
    change = np.linalg.norm(_mean - mean_init)
    while change > allowance:
        mean_init = _mean.copy()
        mean_update = np.zeros((k,data.shape[1]),dtype=np.float)
        for i in range(N):
            dist = np.zeros((k,1),dtype="double")
            for j in range(k):
                dist[j] = np.linalg.norm(data[i] - _mean[j])
            min_k[i] = np.argmin(dist) # return the indices of minimum value
            min_d[i] = np.amin(dist)

        for i,j in enumerate(min_k):
                z[i][j] = 1.

        for l in range(k):
            clusters = []
            for i in range(len(min_k)):
                if min_k[i] == l:
                    clusters.append(i)
            center = np.mean(data[clusters],axis=0)
            mean_update[l,:] = center

        _mean = mean_update
        count = count + 1
        change = np.linalg.norm(_mean - mean_init)
        cost = np.sum(min_d)
        print('iteration:',count,'Change:',change,'Cost:',cost)

    return z,_mean

# Test images
test_images_file = open('t10k-images.idx3-ubyte','rb')
test_images = test_images_file.read()
test_images = bytearray(test_images)
test_images = test_images[16:]
test_images_file.close()

test_images = np.array(test_images,"float64")
d = 28 * 28
n = test_images.shape[0] / d
test_images = test_images.reshape(int(n), d)

# Test labels
test_labels_file = open('t10k-labels.idx1-ubyte','rb')
test_labels = test_labels_file.read()
test_labels = bytearray(test_labels)
test_labels = test_labels[8:]
test_labels_file.close()

test_labels = np.array(test_labels)
test_labels_1 = test_labels.reshape(test_labels.shape[0], 1)

N = test_images.shape[0]
flag = 0  # 0: random data points, 1: k-means++ assignment, 2: data point drawn from each label class, 3:final

if flag == 0:
    k = 10
    mean = test_images[np.random.randint(low=0, high=N, size=k)]
    figure, axes = plt.subplots(nrows=5, ncols=2)
    ind = 1
    for axis in axes.flat:
        axis.imshow(mean[ind-1, :].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("centroid for random datapoints")
    plt.savefig("kmeans1.png", format='png')
    plt.show()

    Z, m = k_means(k, test_images, mean)

    figure, axes = plt.subplots(nrows=5, ncols=2)
    ind = 1
    for axis in axes.flat:
        axis.imshow(m[ind-1].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("K-means centroid for random datapoints")
    plt.savefig("kmeans2.png", format='png')
    plt.show()

if flag == 1:
    k = 10
    mean = np.zeros(shape=(k, test_images.shape[1]))
    mean[0] = test_images[np.random.randint(N)]
    for j in range(1,k):
        dist = np.array([np.min([np.linalg.norm(x-c)**2 for c in mean]) for x in test_images])
        p = np.true_divide(dist,np.sum(dist))
        cp = p.cumsum()
        r = np.random.random()
        ix = np.where(cp >= r)[0][0]
        mean[j] = np.copy(test_images[ix])

    figure, axes = plt.subplots(nrows=5, ncols=2)
    ind = 1
    for axis in axes.flat:
        axis.imshow(mean[ind-1, :].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("centroid by K-means++")
    plt.savefig("kmeans3.png", format='png')
    plt.show()

    Z, m = k_means(k, test_images, mean)

    figure, axes = plt.subplots(nrows=5, ncols=2)
    ind = 1
    for axis in axes.flat:
        axis.imshow(m[ind-1].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("K-means centroid for K-means++")
    plt.savefig("kmeans4.png", format='png')
    plt.show()

if flag == 2:
    k = 10
    mean = np.zeros(shape=(k, test_images.shape[1]))

    for i in range(k):
        label_images = np.copy(test_images[test_labels == i])
        np.random.shuffle(label_images)
        mean[i] = label_images[0]

    figure, axes = plt.subplots(nrows=5, ncols=2)
    ind = 1
    for axis in axes.flat:
        axis.imshow(mean[ind-1, :].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("centroid by known label")
    plt.savefig("kmeans5.png", format='png')
    plt.show()

    Z, m = k_means(k, test_images, mean)

    figure, axes = plt.subplots(nrows=5, ncols=2)
    ind = 1
    for axis in axes.flat:
        axis.imshow(m[ind-1].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("K-means centroid for known label")
    plt.savefig("kmeans6.png", format='png')
    plt.show()

if flag == 3:
    k = 3
    mean = np.zeros(shape=(k, test_images.shape[1]))
    mean[0] = test_images[np.random.randint(N)]
    for j in range(1,k):
        dist = np.array([np.min([np.linalg.norm(x-c)**2 for c in mean]) for x in test_images])
        p = np.true_divide(dist,np.sum(dist))
        cp = p.cumsum()
        r = np.random.random()
        ix = np.where(cp >= r)[0][0]
        mean[j] = np.copy(test_images[ix])

    figure, axes = plt.subplots(nrows=1, ncols=3)
    ind = 1
    for axis in axes.flat:
        axis.imshow(mean[ind-1, :].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("centroid for K-means++ using k=3")
    plt.savefig("kmeans7.png", format='png')
    plt.show()

    Z, m = k_means(k, test_images, mean)

    figure, axes = plt.subplots(nrows=1, ncols=3)
    ind = 1
    for axis in axes.flat:
        axis.imshow(m[ind-1].reshape(28, 28), cmap='bone')
        axis.set_axis_off()
        ind += 1
    plt.suptitle("K-means centroid for K-means++ using k=3")
    plt.savefig("kmeans8.png", format='png')
    plt.show()


from __future__ import division
import numpy as np

# -------------------------------- ProNel ------------------------------------------------------------------
def ProNel(Mix):
    """
    Input parameters
    ----------
    Mix : numpy.ndarray data type (mixing matrix.)

    Returns
    ----------
    ProNel_ret : float (ProNel score for the mixing matrix.)
    """
    Mix = Mix/np.sum(Mix[:,:])

    Mix_MargR = Mix.sum(axis=0)
    Mix_MargR = Mix_MargR[ np.nonzero(Mix_MargR)[0] ]
    Mix_MargC = Mix.sum(axis=1)
    Mix_MargC = Mix_MargC[ np.nonzero(Mix_MargC)[0] ]

    Num_T1 = Mix_MargR * np.log(Mix_MargR)
    Num_T1 = np.sum(Num_T1)

    Num_T2 = Mix_MargC * np.log(Mix_MargC)
    Num_T2 = np.sum(Num_T2)

    MixNz = Mix[np.nonzero(Mix)[0],np.nonzero(Mix)[1]]
    Num_T3 = np.multiply( MixNz, np.log(MixNz) )
    Num_T3 = np.sum(Num_T3)

    Num = Num_T1 + Num_T2 - Num_T3

    Den_T1 = 0.5*Num_T1
    Den_T2 = 0.5*Num_T2
    Den = Den_T1 + Den_T2

    ProNel_ret = Num / Den
    return ProNel_ret

# -------------------------------- ProNe2 ------------------------------------------------------------------
def ProNe2(Mix):
    """
    Input parameters
    ----------
    Mix : numpy.ndarray data type (mixing matrix.)

    Returns
    ----------
    ProNe2_ret : float (ProNe2 score for the mixing matrix.)
    """
    Mix = Mix/np.sum(Mix[:,:])

    Mix_MargR = Mix.sum(axis=0)
    Mix_MargR = Mix_MargR[ np.nonzero(Mix_MargR)[0] ]
    Mix_MargC = Mix.sum(axis=1)
    Mix_MargC = Mix_MargC[ np.nonzero(Mix_MargC)[0] ]

    Num_T1 = np.multiply( Mix, Mix )
    Num_T1 = np.sum(Num_T1)

    Num_T2 = np.sum(Mix_MargC*Mix_MargC) * np.sum(Mix_MargR*Mix_MargR)

    Num = Num_T1 - Num_T2

    Den_T1 = 0.5 * (np.sum(Mix_MargC*Mix_MargC) + np.sum(Mix_MargR*Mix_MargR))
    Den_T2 = Num_T2
    Den = Den_T1 - Den_T2

    ProNe2_ret = Num / Den
    return ProNe2_ret

# -------------------------------- Choice of ProNe ---------------------------------------------------------
def choice(Mix, Type):
    """
    Input parameters
    ----------
    Mix : numpy.ndarray data type (mixing matrix.)
    Type : string data type ('ProNel' or 'ProNe2')
    """
    if Type == 'ProNel':
        return ProNel(Mix=Mix)
    if Type == 'ProNe2':
        return ProNe2(Mix=Mix)


import math
import numpy as np
import matplotlib.pyplot as plt

def func(x,k):
    #k = 0.707 阻尼比系数
    b = math.acos(k) #阻尼角
    wn = 1 #无阻尼振荡角频率
    c1 = math.sqrt(1-k**2)
    wd = wn*c1 #阻尼振荡角频率
    ans = 1-math.exp(-k*wn*x)*math.sin(wd*x+b)/c1
    return ans

def realTime(k):
    t = np.linspace(0,30,num=30000) # delta t = 0.001
    seqk = [k]*30000
    y = list(map(func,t,seqk))
    realtime = 0
    ttt = []
    for i in range(1,30000+1):
        if  y[-i]<= 1-0.05 or y[-i] >= 1+0.05:
            realtime=t[-i]
            break
    return realtime

def envelopeTime(k):
    return -(math.log(0.05)+math.log(math.sqrt(1-k**2)))/k
def estimateTime(k):
    return 3/k

def solve():
    k = np.linspace(0.1,0.999,num=1000)
    realtime = list(map(realTime,k))
    envelopetime = list(map(envelopeTime,k))
    estimatetime = list(map(estimateTime,k))
    plt.figure()
    plt.plot(k,realtime,label='Real Time')
    plt.plot(k,envelopetime,label ='Envelope Time')
    plt.plot(k,estimatetime,label = 'Estimate Time')
    plt.legend()
    plt.xlabel(r'$\zeta$')
    plt.ylabel(r'$\omega_nt$')
    plt.show()

solve()


import math
import numpy as np
from scipy import optimize

def fit_gauss_1d_zero_c(x, y):
    """
    Fits a*gaussian(0, sig) + sky of mu=0

    Returns
    -------
    model, a, sig, c, rmse
    """
    x, y = np.asarray(x), np.asarray(y)
    gauss0 = lambda x, a, c, sig2: c + a * np.exp(-x ** 2 / (2 * sig2))

    opt, cov = optimize.curve_fit(gauss0, x, y, p0=[1.0, 0.0, 1.0])
    res = gauss0(x, *opt) - y
    rmse = math.sqrt((res * res).sum() / len(res))
    try:
        sig = math.sqrt(opt[2])
    except ValueError:
        sig = 0
    return gauss0, opt[0], sig, opt[1], rmse

def fit_gauss_2d_c(xy ,values, initial_mu = None, mu_radius=(np.inf, np.inf)):
    """
    Fits a*gaussian(mu_x, mu_y, sig) + sky

    Parameters
    ----------
    xy:
        List of coordinates, shape: Nx2

    Returns
    -------
    model, a, mu_x, mu_y, sig, c, rmse
    """
    if initial_mu is None: # initial guess
        initial_mu = xy[0]

    xy = np.asarray(xy).T  # transpose xy[0]=x  xy[1] = y

    def gauss(xy, a, c, mu_x, mu_y, sig2):
        val = c + a * np.exp(-((mu_x - xy[0])** 2 + (mu_y - xy[1])** 2) / (2 * sig2))
        return val

    # gauss = lambda xy, a, c, mu_x, mu_y, sig2: c + a * np.exp(-((mu_x - xy[0])** 2 + (mu_y - xy[1])** 2) / (2 * sig2))

    minimal = np.nanmin(values)
    maximal = np.nanmax(values)
    opt, cov = optimize.curve_fit(gauss, xy, values,
                                  p0=[maximal - minimal, minimal, initial_mu[0], initial_mu[1], 1.0],
                                  bounds=(
                                      [0.0, -np.inf, initial_mu[0]-mu_radius[0], initial_mu[1]-mu_radius[1], -np.inf],
                                      [ np.inf,  np.inf, initial_mu[0]+mu_radius[0], initial_mu[1]+mu_radius[1],  np.inf])
                                  )
    res = (gauss(xy, *opt) - values)
    rmse = math.sqrt((res * res).sum() / len(values)),
    try:
        sig = math.sqrt(opt[4])
    except ValueError:
        sig = 0
    return gauss, opt[0], opt[2], opt[3], sig, opt[1], rmse


# The code to print out the mean height is already included. Complete the code for the median height.
# Replace None with the correct code.
# Use np.std() on the first column of np_baseball to calculate stddev. Replace None with the correct code.
# Do big players tend to be heavier ? Use np.corrcoef() to store the correlation between the first and second column off np_baseball in corr.
# Replace None with the correct code.
# np_baseball is available

# Import numpy
import numpy as np

# Print mean height (first column)
avg = np.mean(np_baseball[:,0])
print("Average: " + str(avg))

# Print median height. Replace 'None'
med = np.median(np_baseball[:,0])
print("Median: " + str(med))

# Print out the standard deviation on height. Replace 'None'
stddev = np.std(np_baseball[:,0])
print("Standard Deviation: " + str(stddev))

# Print out correlation between first and second column. Replace 'None'
corr = np.corrcoef(np_baseball[:,0], np_baseball[:,1])
print("Correlation: " + str(corr))

01-introduction to python for data science/04-numpy/explore-the-baseball-data.py


from scipy.optimize import linprog
import numpy as np

def __linprog_solver_col(value_matrix):
    m, n = value_matrix.shape

    # solve col
    # objectif vector c is 0*x1+0*x2+...+0*xn+v
    C = []
    for i in range(n):
        C.append(0)
    C.append(-1)
    A = []
    for i_col in range(n):
        col = value_matrix[:, i_col]
        constraint_row = []
        for item in col:
            constraint_row.append(-item)
        constraint_row.append(1)
        A.append(constraint_row)
    B = []
    for i in range(m):
        B.append(0)

    A_eq = []
    A_eq_row = []
    for i in range(n):
        A_eq_row.append(1)
    A_eq_row.append(0)
    A_eq.append(A_eq_row)
    B_eq = [1]

    bounds = []
    for i in range(n):
        bounds.append((0, 1))
    bounds.append((None, None))

    res = linprog(C, A_ub=A, b_ub=B, A_eq=A_eq, b_eq=B_eq, bounds=bounds)
    return res['x'][:-1], -res['fun']

def __linprog_solver_row(value_matrix):
    policy, value = __linprog_solver_col(-value_matrix.T)
    return policy, -value

def linprog_solve(value_matrix):
    # rps = nash.Game(np.array(value_matrix))
    # eqs = rps.support_enumeration()
    px, value = __linprog_solver_row(value_matrix)
    py, v2 = __linprog_solver_col(value_matrix)
    # policy_x, policy_y = list(eqs)[0]
    return value, py, px

def run():
    v = [
        [4, 3, 7],
        [1, 2, 3],
        [2, 4, 6]
    ]
    v = np.array(v)
    policy_x, value_x = __linprog_solver_row(v)
    policy_y, value_y = __linprog_solver_col(v)
    linv, linx, liny = linprog_solve(v)
    print('done')


'''
Provides the exact solutions to some SDOF problems, in order
to run tests and estimate the efficiency of the method

All functions return displacement, speed and acceleration in
this order
'''

import numpy as np

# arbitrary parameters and initial conditions, 0 excitation function

def hom_sol(t: float, c: float, k: float, x0: float, dx0: float):

	w_n = np.sqrt(k)

	xi  = c/(2 * w_n)

	w_d = w_n * np.sqrt(1 - xi **2)

	exact_disp = np.exp(- xi * w_n * t)*(x0 * np.cos(w_d *t) +
						(dx0 + xi * w_n * x0)/w_d * np.sin(w_d *t))

	aux_term = - w_d *x0 * np.sin(w_d *t) + (dx0 + xi * w_n * x0) * np.cos(w_d *t)
	d_aux_term = - w_d **2 *x0 * np.cos(w_d *t) - (dx0 + xi * w_n * x0) * w_d * np.sin(w_d *t)

	exact_speed = - xi * w_n * exact_disp + np.exp(- xi * w_n * t)* aux_term

	exact_acc =  - xi * w_n * exact_speed - xi * w_n * np.exp(- xi * w_n * t)* aux_term + np.exp(- xi * w_n * t)* d_aux_term

	return exact_disp, exact_speed, exact_acc

# m = k = 1, c = 0, sin(t) excitation function, 0 initial conditions

def sin_exc_fun(t: float):
	return (np.sin(t) - t* np.cos(t))/2, t * np.sin(t)/2, (np.sin(t)+ t* np.cos(t))/2

# m = k = 1, c = 0, sin(2t) excitation function, 0 initial conditions

def sin2_exc_fun(t: float):
	return -2*np.sin(t) *(np.cos(t)-1)/3, 2*(np.cos(t) - np.cos(2*t))/3, -2*(np.sin(t) - 2* np.sin(2*t))/3


import numpy as np
import matplotlib.pyplot as mpl
from scipy.stats import chisquare
from scipy.optimize import fmin_powell as fmin

t = np.asfarray([ 300 , 350 , 400 , 450 , 500 , 550 , 600 , 650 , 700 , 750 ])
N = np.asfarray([ 803000 , 581083 , 429666 , 320016 , 242783 , 188487 , 150737 , 124103 , 105397 , 92748])

def getN(parameters,t_values):
	A, L, B = parameters
	delta_t	= 10.0
	return (2.0*A/L)*np.exp(-L*t_values)*np.sinh(L*delta_t/2.0)+B*delta_t

p = fmin(lambda x: np.sum(np.power(N-getN(x,t),2.0)), np.asfarray([5*N[0],0.1,0.0]), disp=False)

bestdata = getN(p,t)
bestA, bestL, bestB = p

chi2	= chisquare(N, bestdata)[0]

bestA_err = np.abs(bestA-fmin(lambda x: np.power(chi2 - chisquare(N, getN([x, bestL, bestB],t))[0]-1,2.0), bestA, disp=False))
bestL_err = np.abs(bestL-fmin(lambda x: np.power(chi2 - chisquare(N, getN([bestA, x, bestB],t))[0]-1,2.0), bestL, disp=False))
bestB_err = np.abs(bestB-fmin(lambda x: np.power(chi2 - chisquare(N, getN([bestA, bestL, x],t))[0]-1,2.0), bestL, disp=False))

bestT		= np.log(2.0)/bestL
bestT_err	= bestT/bestL*bestL_err

print 'Best A: ', bestA, ' Error:', bestA_err
print 'Best L: ', bestL, ' Error:', bestL_err
print 'Best B: ', bestB, ' Error:', bestB_err
print 'Best Tau: ', bestT, ' Error:', bestT_err

mpl.figure(facecolor='white')
mpl.plot(t, N, 'b^', label='Measured N')
mpl.plot(t, bestdata, 'g-', label='Curve fit')
mpl.legend()
mpl.savefig('p06_fit.pdf')
#mpl.show()

Statistical Methods 2014 - Home exam/Code/exam_p06.py


import numpy as np
from scipy.special import factorial
import scipy as sp
import matplotlib.pyplot as plt
import scipy.io as sio
"""Calculate the intensities of electric fields.
"""
def calculate_e_field_intensity(l, p, w0, x, y, petaledbeam):
    """
    calculates intensity field of LG beam on a grid with size set by x and y

    Args:
        l (int): OAM azimuthal mode number
        p (int): OAM radial mode number
        w0 (float): beam waist
        x (int): grid size in x direction
        y (int): grid size in y direction
        petaledbeam (bool): calculates interference fringes of conjugate (+/-l) beams if true, calculates intensity field of single beam if false

    Returns:
        intensity_normalized (np array): intensity field calculated on x,y grid normalized by maximum intensity
        intensity_shape (tuple of ints): shape of intensity_normalized

    """
    x = np.arange(-np.floor(x/2.), np.floor(x/2.))
    y = np.arange(-np.floor(y/2.), np.floor(y/2.))

    x_, y_ = np.meshgrid(x, y)
    r = np.sqrt(x_**2+y_**2)
    phi = np.arctan2(y_, x_)
    gaussian = np.sqrt(2) / (np.sqrt(np.pi)*w0) * np.exp(-r**2/(w0**2))

    # LG beam for arbitrary l & p
    LG = sp.special.genlaguerre(p, abs(l))

    LGlp = np.sqrt(factorial(p)) / np.sqrt(factorial(p+np.absolute(l)))\
           * (np.sqrt(2)*r/w0)**(np.absolute(l)) * np.exp(1j*l*phi) * gaussian\
           * LG(2*r**2/w0**2)

    if petaledbeam:
        fieldout = (LGlp+np.conj(LGlp))/2
        intensity = np.abs(fieldout*np.conj(fieldout))
    else:
        intensity = np.abs(LGlp*np.conj(LGlp))

    intensity_normalized = np.double(intensity/np.max(intensity))
    intensity_shape = intensity_normalized.shape
    return intensity_normalized, intensity_shape

def calculate_e_field_intensity_linear(w0, x, y, alpha, fringes=True):
    """
    calculates intensity field of Gaussian beam on a grid with size set by x and y

    Args:
        w0 (float): beam waist
        x (int): grid size in x direction
        y (int): grid size in y direction
        alpha (float): angle between beams
        fringes (bool): calculates interference fringes if True

    Returns:
        intensity_normalized (np array): intensity field calculated on x,y grid normalized by maximum intensity
        intensity_shape (tuple of ints): shape of intensity_normalized

    """
    x = np.arange(-np.floor(x/2.),np.floor(x/2.))
    y = np.arange(-np.floor(y/2.),np.floor(y/2.))

    x_, y_ = np.meshgrid(x,y)
    r = np.sqrt(x_**2+y_**2)
    phi = np.arctan2(y_, x_)
    gaussian = np.sqrt(2) / (np.sqrt(np.pi)*w0) * np.exp(-r**2/(w0**2)) * np.exp(-alpha*1j*x_)
    if fringes:
        fieldout = (gaussian+np.conj(gaussian))/2
        intensity = np.abs(fieldout*np.conj(fieldout))
    else:
        intensity = np.abs(gaussian*np.conj(gaussian))

    intensity_normalized = np.double(intensity/np.max(intensity))
    intensity_shape = intensity_normalized.shape
    return intensity_normalized, intensity_shape


import math
import numpy

global result_matrix, result
result_matrix = [[0]*5]*4
result = []*4

def gauss_method(matrix, size):
    if size == 0:
        return
    global result_matrix
    a_max = -1000
    i_max = 0
    j_max = 0

    for i in range(len(matrix)):
        for j in range(len(matrix[i])-1):
            if abs(matrix[i][j]) > a_max:
                a_max = abs(matrix[i][j])
                i_max = i
                j_max = j
    m = [0]*len(matrix)

    for i in range(len(m)):
        m[i] = -1 * matrix[i][j_max]/matrix[i_max][j_max]

    for i in range(len(matrix)):
        if i != i_max:
            for j in range(len(matrix[i])):
                matrix[i][j] = round(matrix[i][j] + matrix[i_max][j] * m[i], 2)

    result_matrix[i_max] = matrix[i_max]
    matrix[i_max] = [0]*len(matrix[i_max])
    gauss_method(matrix, size-1)

def solve_matrix(matrix):
    size = len(matrix)
    m1 = [[0]*size]*size
    v1 = [0]*size

    for i in range(size):
        v1[i] = matrix[i][size]
        m1[i] = matrix[i][:size]

    nm1 = numpy.array(m1)
    nv1 = numpy.array(v1)

    return numpy.linalg.solve(nm1, nv1)

def yakobi(A, b, eps=0.001):
    def check_conv():
        for i in range(0, A.shape[0]):
            sum = 0
            for j in range(0, A.shape[1]):
                if i != j:
                    sum += abs(A[i][j])
            if sum > abs(A[i][i]):
                return False
        return True

    if not check_conv():
        raise ValueError("Method does not converge")

    def calc_iter_process():
        iter_process = numpy.empty([4, 5])
        for i in range(0, A.shape[0]):
            arr = numpy.array(-A[i] / A[i][i])
            arr[i] = 0
            arr = numpy.append(arr, b[i] / A[i][i])
            iter_process[i] = arr
        return iter_process

    def calc_x_k(matrix, x_prev):
        return matrix @ x_prev

    iter = calc_iter_process()
    x_cur = numpy.array([0, 0, 0, 0])
    x_prev = numpy.array([100, 100, 100, 100])
    while numpy.abs(numpy.amax(x_cur - x_prev)) > eps:
        x1 = calc_x_k(iter, numpy.append(x_cur, 1))
        x_prev = x_cur
        x_cur = x1
    return x_cur

my_matrix = [[3, 1, -1, -1, 10],  [1, 10, -1, 1, 3],  [1, -1, 10, 1, -8],  [-1, 1, -1, 10, 10]]
gauss_method(my_matrix, 4)
result = solve_matrix(result_matrix)
print("Gauss result:")
print(result)

xA = numpy.array([[3, 1, -1, -1],  [1, 10, -1, 1],  [1, -1, 10, 1],  [-1, 1, -1, 10]])
xb = numpy.array([10, 3, -8, 10])
print("Yakobi result:")
print(yakobi(xA, xb).astype(numpy.float32))

xA_inv = numpy.linalg.inv(xA)
print("Number of conditionality is ", numpy.linalg.norm(xA, numpy.inf) * numpy.linalg.norm(xA_inv, numpy.inf))


import numpy as np
import pandas as pd
from collections import Counter
import math
import scipy.stats as ss

# These functions are adapted from the SweetViz package, which took them over
# from dython. SweetViz can be found at https://github.com/fbdesignpro/sweetviz

def convert(data, to):
    converted = None
    if to == 'array':
        if isinstance(data, np.ndarray):
            converted = data
        elif isinstance(data, pd.Series):
            converted = data.values
        elif isinstance(data, list):
            converted = np.array(data)
        elif isinstance(data, pd.DataFrame):
            converted = data.as_matrix()
    elif to == 'list':
        if isinstance(data, list):
            converted = data
        elif isinstance(data, pd.Series):
            converted = data.values.tolist()
        elif isinstance(data, np.ndarray):
            converted = data.tolist()
    elif to == 'dataframe':
        if isinstance(data, pd.DataFrame):
            converted = data
        elif isinstance(data, np.ndarray):
            converted = pd.DataFrame(data)
    else:
        raise ValueError("Unknown data conversion: {}".format(to))
    if converted is None:
        raise TypeError(
            'cannot handle data conversion of type: {} to {}'.format(
                type(data), to))
    else:
        return converted

def conditional_entropy(x, y):
    """
    Calculates the conditional entropy of x given y: S(x|y)
    Wikipedia: https://en.wikipedia.org/wiki/Conditional_entropy
    **Returns:** float
    Parameters
    ----------
    x : list / NumPy ndarray / Pandas Series
        A sequence of measurements
    y : list / NumPy ndarray / Pandas Series
        A sequence of measurements
    """
    y_counter = Counter(y)
    xy_counter = Counter(list(zip(x, y)))
    total_occurrences = sum(y_counter.values())
    entropy = 0.0
    for xy in xy_counter.keys():
        p_xy = xy_counter[xy] / total_occurrences
        p_y = y_counter[xy[1]] / total_occurrences
        entropy += p_xy * math.log(p_y / p_xy)
    return entropy

# IMPORTANT: look at the order of arguments y and x
def theils_u(y, x):
    """
    IMPORTANT: look at the order of arguments y and x
    Calculates Theil's U statistic (Uncertainty coefficient) for categorical-
    categorical association. This is the uncertainty of x given y: value is
    on the range of [0,1] - where 0 means y provides no information about
    x, and 1 means y provides full information about x.
    This is an asymmetric coefficient: U(x,y) != U(y,x)
    Wikipedia: https://en.wikipedia.org/wiki/Uncertainty_coefficient
    **Returns:** float in the range of [0,1]
    Parameters
    ----------
    x : list / NumPy ndarray / Pandas Series
        A sequence of categorical measurements
    y : list / NumPy ndarray / Pandas Series
        A sequence of categorical measurements
    """
    s_xy = conditional_entropy(x, y)
    x_counter = Counter(x)
    total_occurrences = sum(x_counter.values())
    p_x = list(map(lambda n: n / total_occurrences, x_counter.values()))
    s_x = ss.entropy(p_x)
    if s_x == 0:
        return 1
    else:
        return (s_x - s_xy) / s_x

def theils_sym_u(y, x):
    """
    Calculates the symmetric version of the Uncertainty coefficient.
    Wikipedia: https://en.wikipedia.org/wiki/Uncertainty_coefficient
    **Returns:** float in the range of [0,1]
    Parameters
    ----------
    x : list / NumPy ndarray / Pandas Series
        A sequence of categorical measurements
    y : list / NumPy ndarray / Pandas Series
        A sequence of categorical measurements
    """
    s_xy = conditional_entropy(x, y)
    s_yx = conditional_entropy(y, x)

    x_counter = Counter(x)
    x_total_occurrences = sum(x_counter.values())

    y_counter = Counter(y)
    y_total_occurrences = sum(y_counter.values())

    p_x = list(map(lambda n: n / x_total_occurrences, x_counter.values()))
    p_y = list(map(lambda n: n / y_total_occurrences, y_counter.values()))

    s_x = ss.entropy(p_x)
    s_y = ss.entropy(p_y)

    if s_x == 0:
        u_xy = 1
    else:
        u_xy = (s_x - s_xy) / s_x

    if s_y == 0:
        u_yx = 1
    else:
        u_yx = (s_y - s_yx) / s_y

    return (s_x * u_xy + s_y * u_yx) / (s_x + s_y)

def correlation_ratio(categories, measurements):
    """
    Calculates the Correlation Ratio (sometimes marked by the greek letter Eta)
    for categorical-continuous association.
    Answers the question - given a continuous value of a measurement, is it
    possible to know which category is it associated with?
    Value is in the range [0,1], where 0 means a category cannot be determined
    by a continuous measurement, and 1 means a category can be determined with
    absolute certainty.
    Wikipedia: https://en.wikipedia.org/wiki/Correlation_ratio
    **Returns:** float in the range of [0,1]
    Parameters
    ----------
    categories : list / NumPy ndarray / Pandas Series
        A sequence of categorical measurements
    measurements : list / NumPy ndarray / Pandas Series
        A sequence of continuous measurements
    """
    categories = convert(categories, 'array')
    measurements = convert(measurements, 'array')
    fcat, _ = pd.factorize(categories)
    cat_num = np.max(fcat) + 1
    y_avg_array = np.zeros(cat_num)
    n_array = np.zeros(cat_num)
    for i in range(0, cat_num):
        cat_measures = measurements[np.argwhere(fcat == i).flatten()]
        n_array[i] = len(cat_measures)
        y_avg_array[i] = np.average(cat_measures)
    y_total_avg = np.sum(np.multiply(y_avg_array, n_array)) / np.sum(n_array)
    numerator = np.sum(
        np.multiply(n_array, np.power(np.subtract(y_avg_array, y_total_avg),
                                      2)))
    denominator = np.sum(np.power(np.subtract(measurements, y_total_avg), 2))
    if numerator == 0:
        eta = 0.0
    else:
        eta = np.sqrt(numerator / denominator)
    return eta


# -*- coding: utf-8 -*-
"""
Created on Tue Aug 31 09:59:05 2021

@author: Julien

https://en.wikipedia.org/wiki/L%C3%A9vy_C_curve
"""

#  _____                            _
# |_   _|                          | |
#   | |  _ __ ___  _ __   ___  _ __| |_ ___
#   | | | '_ ` _ \| '_ \ / _ \| '__| __/ __|
#  _| |_| | | | | | |_) | (_) | |  | |_\__ \
# |_____|_| |_| |_| .__/ \___/|_|   \__|___/
#                 | |
#                 |_|

import cmath
import numpy as np

#   _____                _              _
#  / ____|              | |            | |
# | |     ___  _ __  ___| |_ __ _ _ __ | |_ ___
# | |    / _ \| '_ \/ __| __/ _` | '_ \| __/ __|
# | |___| (_) | | | \__ \ || (_| | | | | |_\__ \
#  \_____\___/|_| |_|___/\__\__,_|_| |_|\__|___/

X_LIMITS = (-0.6, 1.6)
Y_LIMITS = (-0.4, 1.1)

#  ______                _   _
# |  ____|              | | (_)
# | |__ _   _ _ __   ___| |_ _  ___  _ __  ___
# |  __| | | | '_ \ / __| __| |/ _ \| '_ \/ __|
# | |  | |_| | | | | (__| |_| | (_) | | | \__ \
# |_|   \__,_|_| |_|\___|\__|_|\___/|_| |_|___/

def compute_new(z1, z2, up):
    '''
    uses complex numbers to compute the next points to add for each edge.

    Parameters
    ----------
    z1 : complex
        first vertice.
    z2 : complex
        second vertice.
    up : boolean
        wether or not add the point in a clockwise or counterclockwise fashion.

    Returns
    -------
    complex
        the new point(s) generated from the given edge.
    '''

    v = (z2-z1) /2
    v_mod, v_phas = cmath.polar(v)
    v_mod *= np.sqrt(2)

    return cmath.rect(v_mod, v_phas)*1j**0.5

def partial_Levi_curve(T):
    '''
    Generates the next generation of the fractal curve algorithm. For each pair of consecutive points of T
    that make a vertice, we add between those two points the new point(s) generated by the "compute_new"
    method.

    Parameters
    ----------
    T : list[complex]
        the current state of the algorithm.

    Returns
    -------
    res : list[complex]
        The next step of the algorithm.
    '''

    res = []
    for i in range(len(T)-1):
        res.append(T[i])
        res.append(T[i]+compute_new(T[i], T[i+1], i%2==0))
    res.append(T[-1])
    return res

def Levi_curve(n):
    '''
    Recursive method to build the final fractal curve of degree n.

    Parameters
    ----------
    n : int
        The degree of the fractal curve to build.

    Returns
    -------
    list[complex]
        The list of complex numbers that represent the n-th degree fractal curve.
    '''

    if n==0 :
        return [complex(0,0), complex(1,0)]
    else :
        return partial_Levi_curve(Levi_curve(n-1))

def pts_num(n):
    '''
    Optional function that returns the amount of points in the n-th generation of the algorithm.

    Parameters
    ----------
    n : int
        The degree of the fractal curve.

    Returns
    -------
    int
        The number of vertices needed to build the n-th degree fractal curve.
    '''

    n0 = 2
    return (2**n)*(n0-1)+1

#  _            _
# | |          | |
# | |_ ___  ___| |_ ___
# | __/ _ \/ __| __/ __|
# | ||  __/\__ \ |_\__ \
#  \__\___||___/\__|___/

if __name__ == '__main__' :
    import matplotlib.pyplot as plt

    N = 20
    L = Levi_curve(N)

    def plot(C_List):
        Lr = [el.real for el in C_List]
        Li = [el.imag for el in C_List]
        plt.plot(Lr, Li, lw=np.exp(-N/7))

    plot(L)

    plt.axis('equal')
    plt.show()


import numpy as np
import numpy.linalg as linalg

def compute_Z(X, centering=True, scaling=False):
    if centering == True:
        col_averages = np.mean(X, axis=0)
        for i in range(X.shape[1]):
            X[:,i] = X[:,i] - col_averages[i]

    if scaling == True:
        col_std = np.std(X, axis=0)

        for i in range(X.shape[1]):
            X[:,i] = X[:,i] / col_std[i]

    return X

def compute_covariance_matrix(Z):
    cov = np.matmul(Z.T,Z)
    return cov

def find_pcs(COV):
    eigenValues, eigenVectors = linalg.eig(COV)
    idx = eigenValues.argsort()[::-1]
    eigenValues = eigenValues[idx]
    eigenVectors = eigenVectors[:,idx]

    return eigenValues, eigenVectors

def project_data(Z, PCS, L, k, var):

    return 1

# Test Data
X = np.array([[-1,-1],[-1,1],[1,-1],[1,1]])

# Test Script
Z = compute_Z(X)
COV = compute_covariance_matrix(Z)
L, PCS = find_pcs(COV)
Z_star = project_data(Z, PCS, L, 1, 0)


# -*- coding: utf-8 -*-

import numpy as np

def euclidean_distant(vector1, vector2):
    """欧式距离"""
    vector1 = np.mat(vector1)
    vector2 = np.mat(vector2)
    return np.sqrt((vector1 - vector2) * (vector1 - vector2).T).item()

def manhattan_distant(vector1, vector2):
    """曼哈顿距离"""
    vector1 = np.mat(vector1)
    vector2 = np.mat(vector2)
    return np.sum(np.abs(vector1 - vector2))

def cosine_distant(vector1, vector2):
    """余弦距离"""
    vector1 = np.mat(vector1)
    vector2 = np.mat(vector2)
    vector1_norm = np.linalg.norm(vector1)
    vector2_norm = np.linalg.norm(vector2)
    dot_norm = vector1_norm * vector2_norm
    dot_vs = np.dot(vector1, vector2)
    return np.divide(dot_vs, dot_norm).item()

def pearson_similar(vector1, vector2):
    """皮尔逊相关系数"""
    if len(vector1) < 3:
        return 1.0
    return 0.5 + 0.5 * np.corrcoef(vector1, vector2, rowvar=0)[0][1]


"""
Example app demonstrating how to use the HoloViews API to generate
a bokeh app with complex interactivity. Uses a RangeXY stream to allow
interactive exploration of the mandelbrot set.
"""

import numpy as np
import holoviews as hv

from holoviews import opts
from holoviews.streams import RangeXY
from numba import jit

renderer = hv.renderer('bokeh')

@jit
def mandel(x, y, max_iters):
    """
    Given the real and imaginary parts of a complex number,
    determine if it is a candidate for membership in the Mandelbrot
    set given a fixed number of iterations.
    """
    i = 0
    c = complex(x,y)
    z = 0.0j
    for i in range(max_iters):
        z = z*z + c
        if (z.real*z.real + z.imag*z.imag) >= 4:
            return i

    return 255

@jit
def create_fractal(min_x, max_x, min_y, max_y, image, iters):
    height = image.shape[0]
    width = image.shape[1]

    pixel_size_x = (max_x - min_x) / width
    pixel_size_y = (max_y - min_y) / height
    for x in range(width):
        real = min_x + x * pixel_size_x
        for y in range(height):
            imag = min_y + y * pixel_size_y
            color = mandel(real, imag, iters)
            image[y, x] = color

    return image

def get_fractal(x_range, y_range):
    (x0, x1), (y0, y1) = x_range, y_range
    image = np.zeros((600, 600), dtype=np.uint8)
    return hv.Image(create_fractal(x0, x1, -y1, -y0, image, 200),
                    bounds=(x0, y0, x1, y1))

# Define stream linked to axis XY-range
range_stream = RangeXY(x_range=(-1., 1.), y_range=(-1., 1.))

# Create DynamicMap to compute fractal per zoom range and
# adjoin a logarithmic histogram
dmap = hv.DynamicMap(get_fractal, label='Manderbrot Explorer',
                     streams=[range_stream]).hist(log=True)

# Apply options
dmap.opts(
    opts.Histogram(framewise=True, logy=True, width=200, xlim=(1, None)),
    opts.Image(cmap='fire', logz=True, height=600, width=600,
               xaxis=None, yaxis=None))

doc = renderer.server_doc(dmap)
doc.title = 'Mandelbrot Explorer'


import random
from math import exp,fabs,cos,pi
from numpy import zeros,multiply,add,ndarray
from typing import Callable

RealFunc = Callable[[ndarray], float]  # type alias for a real -> real function

def ConstrainX(x:ndarray,lowerbound:ndarray, upperbound:ndarray)-> ndarray:
    for i in range(len(x)):
        x[i]=min(max(x[i],lowerbound[i]),upperbound[i])

    return x.copy()

def ConstrainV(tempV:ndarray,Vmax:float)->ndarray:
    for i in range(len(tempV)):
        tempV[i]=min(max(tempV[i],-Vmax),Vmax)

    return tempV.copy()

def GenerateR(u0:float,y0:float,length:int)->(float,float,ndarray):
    result=zeros((length,1))
    for i in range(length):
       y0 = cos(2 * pi * u0) + y0 * exp(-3)
       u0 = (u0 + 400 + 12 * y0) % 1.0
       result[i] = min(max(u0, 0), 1)
    return (u0,y0,result)

def swaplocalbest(function:RealFunc,oldx:ndarray, newx:ndarray)->ndarray:
     olderror=function(oldx)
     newerror=function(newx)
     if newerror<olderror:
         return newx.copy()
     if newerror>=olderror:
         return oldx.copy()

def chaoticPSOOptimize(function: RealFunc,lowerbound:ndarray,upperbound:ndarray,
     maximumiteration:int,initialgusssize:int,initialguess:ndarray,numofswarms:int,
     tolerance:float
) -> float:
   inertiaweightmax=1.2
   inertiaweightmin=0.1
   chi=0.73
   c1=2.0
   c2=2.0
   Vmax=4.0
   detalweight = (inertiaweightmax - inertiaweightmin) / maximumiteration
   minerror =9999999999999.999
   localswarm ={}
   localbest ={}
   Velocity = {}
   # Initialize
   temp=zeros((len(lowerbound),1))
   tempV=zeros((len(lowerbound),1))
   globalbest=initialguess.copy()
   minerror = function(globalbest)
   u0=1.0
   y0=1.0
   for i in range(initialgusssize):
     for j in range(len(lowerbound)):
        y0 = cos(2 * pi * u0) + y0 * exp(-3)
        u0 = (u0 + 400 + 12 * y0) % 1.0
        temp[j]=(upperbound[j] - lowerbound[j]) * min(max(u0, 0), 1) + lowerbound[j]
        y0 = cos(2 * pi * u0) + y0 * exp(-3)
        u0 = (u0 + 400 + 12 * y0) % 1.0
        tempV[j] = 2 * Vmax * min(max(u0, 0), 1) - Vmax
     localswarm[i]=ConstrainX(temp,lowerbound,upperbound)
     localbest[i]=initialguess.copy()
     Velocity[i]=ConstrainV(tempV,Vmax)
     error = function(temp)
     if error<minerror:
        minerror=error
        globalbest=temp.copy()
   oldglobalerror=minerror
   u0 = 1.00
   y0 = 1.00
   for i in range(maximumiteration):
       tempweight = (inertiaweightmin+(inertiaweightmax-inertiaweightmin) / maximumiteration* i)
       for j in range(numofswarms):
            (u0,y0,R1)=GenerateR(u0,y0,len(lowerbound))
            (u0,y0,R2)=GenerateR(u0,y0,len(lowerbound))
            item2 = multiply((localbest[j].copy()-localswarm[j].copy()),R1*c1)
            item3 = multiply((globalbest-localswarm[j].copy()), R2*c2)
            item1 = add( Velocity[j].copy()*tempweight, item2)
            item1 = add(item1, item3)
            localswarm[j] = ConstrainX(add(localswarm[j].copy(), ConstrainV(item1*chi, Vmax)),lowerbound,upperbound)
            Velocity[j] = ConstrainV(item1*chi, Vmax)
            newlocalbest = swaplocalbest(function,localswarm[j].copy(), localswarm[j])
            localbest[j] = newlocalbest
            localerror = function(localbest[j])
            if localerror < minerror:
               globalbest = localbest[j].copy()
               minerror = localerror

       if fabs(oldglobalerror - minerror) < tolerance and i>5:
          print("Objective function value: = %f" %minerror)
          break
       else:
         oldglobalerror = minerror
         print("Objective function value: = %f" %minerror)
   return globalbest


import numpy
import matplotlib.pyplot as plot

from base.elementspace_1D import NodeSpace1D, ElementSpace1D
from base.polynomial import Polynomial
from base.matrix import Matrix

class FiniteDifferenceMethod:

    mesh = []               # ElementSpace

    material_matrix = []    # Matrix(Square)
    force_vector = []       # Matrix(Vector)
    material_function = []  # Polynomial

    solution_space = []     # Matrix(Vector)

    def __init__(self, element_space, material_property, bc_type1, bc_type2):
        self.mesh = element_space
        self.material_matrix = Matrix(numpy.zeros((element_space.n_nodes, element_space.n_nodes)))

        # Define the linear material function for constant material properties
        self.material_function = Polynomial([material_property, 0])

        # Define the solution space to accomodate the initial type1 boundary conditions
        if isinstance(bc_type1, NodeSpace1D):
            self.solution_space = Matrix(bc_type1.nodes)
        elif isinstance(bc_type1, numpy.ndarray):
            self.solution_space = Matrix(bc_type1)
        elif isinstance(bc_type1, Matrix):
            self.solution_space = bc_type1
        else:
            raise TypeError("bc_type1: Unknown Type")

        # Define the force vector to include the initial type2 boundary conditions
        if isinstance(bc_type2, NodeSpace1D):
            self.force_vector = Matrix(bc_type2.nodes)
        elif isinstance(bc_type2, numpy.ndarray):
            self.force_vector = Matrix(bc_type2)
        elif isinstance(bc_type2, Matrix):
            self.force_vector = bc_type2
        else:
            raise TypeError("bc_type2: Unknown Type")

    # The partial differential equations (PDEs) are solved using a Centred-Space,
    # finite difference scheme:
    #       f'(x) = f(x + 1/2*h)/h - f(x-1/2*h)/h
    #
    # When this method is applied to the following differential equation:
    #   -d/dx(K(x) * dU(x)/dx) = F(X)               (Diffusion equation)
    # this results in:
    #
    #   -1/DX * ( K(x_i + DX/2) * ( U(x_i+1) - U(x_i) )/DX
    #           + K(x_i - DX/2) * ( U(x_i) - U(x_i-1) )/DX ) = F(x_i)
    #
    # Rewriting the above equation in terms of U(x) results in the creation
    # of the matrices required to solve the problem:
    def setup(self):
        self.material_matrix[0,0] = 1.0
        for i in range(1, self.mesh.n_nodes - 1):
            x_i = self.mesh.nodes[i]
            DX = x_i - self.mesh.nodes[i - 1]
            DXdiv2 = DX/2
            DXDX = DX*DX
            #print("{}:, DX:{}, DXdiv2:{}, DXDX:{}", i, DX, DXdiv2, DXDX)
            self.material_matrix[i, i - 1] += -self.material_function.evaluate(x_i - DXdiv2)/DXDX
            self.material_matrix[i, i] += self.material_function.evaluate(x_i - DXdiv2)/DXDX
            self.material_matrix[i, i] += self.material_function.evaluate(x_i + DXdiv2)/DXDX
            self.material_matrix[i, i + 1] += -self.material_function.evaluate(x_i + DXdiv2)/DXDX

        self.material_matrix[self.mesh.n_nodes - 1, self.mesh.n_nodes - 1] = 1.0
        #print(self.material_matrix)

    # Solve the matrix equations to generate the solution_space:
    def solve(self):
        self.solution_space = self.material_matrix.get_inverse() * self.force_vector
        #print("Inverse:", self.material_matrix.get_inverse())
        #print("Force_Vector:", self.force_vector)
        #print("Solution_space:", self.solution_space)

    # Plot the nodes at their respective coordinates vs their respective solution values.
    def plot(self):
        plot.plot(self.mesh.nodes, self.solution_space.matrix)
        plot.xlabel("X coordinates")
        plot.ylabel("Degree-of-Freedom Value")
        plot.show()

# Test Methods and Classes
def main():
    # Heat transfer test method
    # Create mesh using parameters:
    x_dimension = 12        # Distance specification (meters)
    n_elements = 8          # Number of finite elements in mesh
    start_pos = 0           # First Node position
    nodes_per_element = 2   # Number of Nodes per element

    # Create mesh of discrete elements that consist of nodes_per_element
    fdm_espace = ElementSpace1D(n_elements, x_dimension, start_pos, nodes_per_element)

    # Analysis Conditions:
    #   - Material properties:
    K = 20      # Constant Stiffness coefficient

    #   - Type 1 (Dirichlet) Boundary Conditions(BCs):
    Type1_BC = 24       # Temperature specification
    Type1_Nodes = [0]   # Node indices subject to Type 1 BC
    BC_Type1 = NodeSpace1D( numpy.zeros( fdm_espace.n_nodes ) )
    BC_Type1.assign_values(Type1_BC, Type1_Nodes)
    #print("BC_Type1:", BC_Type1)

    #   - Type 2 (Neumann) Boundary Conditions(BCs):
    Type2_BC = 16                   # Heat Flux specification
    Type2_Nodes = [n_elements]  # Node indices subject to Type 2 BC
    BC_Type2 = NodeSpace1D( numpy.zeros( fdm_espace.n_nodes ) )
    BC_Type2.assign_values(Type2_BC, Type2_Nodes)
    #print("BC_Type2P:", BC_Type2)

    FDM = FiniteDifferenceMethod(fdm_espace, K, BC_Type1, BC_Type2)
    FDM.setup()
    FDM.solve()
    FDM.plot()

if __name__ == "__main__":
    main()


"""
Author: Abraham Lemus Ruiz
Last change: 7pm 8/12/2020
linkedin: https://www.linkedin.com/in/abraham-lemus-ruiz/
"""

import numpy as np

class LinearRegressionByHand():
    def __init__(self, learning_rate = 0.001, iterations = 100000):
        self.learning_rate = learning_rate
        self.iterations = iterations

    def compute_loss(self, y, y_true):
        loss = np.sum( (y - y_true)**2  )
        return loss / ( 2 * self.n )

    def predict(self, X):  #returns y = b + p1x1 + p2x2 + ... + pnxn
        y = self.b + X @ self.W
        return y

    def gd(self, X, y_true, y_hat):   #Compute the derivatives from the loss function
        db = np.sum(y_hat - y_true) / self.m
        dW = np.sum(  (y_hat - y_true).T @ X, axis=0)/ self.m
        return dW, db

    def update_params(self, dW, db):
        self.W = self.W - self.learning_rate * np.reshape(dW, (self.n, 1))
        self.b = self.b - self.learning_rate * db

    def fit(self, X, y):
        y = y.values.reshape(-1,1)
        self.m, self.n = X.shape # m is # of rows. n is # of features
        self.W = np.random.randn(self.n, 1) # init random params
        self.b = np.random.randn() # bias
        self.losses = []
        for _ in range(self.iterations):
            y_hat = self.predict(X)
            loss = self.compute_loss(y_hat, y)
            self.losses.append(loss)
            dW, db = self.gd(X, y, y_hat)
            self.update_params(dW, db)
        return self.losses


import numpy as np
from astropy import constants as const

h   = const.h.cgs.value
c   = const.c.cgs.value
k_B = const.k_B.cgs.value
sigma = const.sigma_sb.cgs.value

def B_nu(nu, T):
	"""
	Specific brightness

	Parameters
	----------
	nu : float
		frequency [Hz]
	T : float
		Temperature [K]

	Returns
	-------
	B_nu : float
		Specific brightness [ergs s**-1 cm**-2 steradian**-1 Hz**-1]
	"""
	return (2*h*nu**3 / c**2) / (np.exp(h*nu/(k_B*T))-1)

def B(T):
	"""
	Total brightness as a function of temperature (analytic)

	Parameters
	----------
	T : float
		Temperature [K]

	Returns
	-------
	B : float
		Brightness [ergs s**-1 cm**-2 steradian**-1]
	"""
	return sigma * T**4 / np.pi

def d_B_nu_d_nu_dimensionless(x):
	"""
	Calculates d(B_nu) / d (nu),
	as a function of dimensionless units, x = (h nu / k_B T)

	Parameters
	----------
	x : float

	Returns
	-------
	d_B_nu_d_nu_dimensionless : float
		Not normalized to anything meaningful

	"""
	return (3*x**2 / (np.exp(x)-1)) - (x**3 * np.exp(x) / (np.exp(x)-1)**2)

def d_B_lambda_d_lambda_dimensionless(y):
	"""
	Calculates d(B_lambda) / d (lambda),
	as a function of dimensionless units, y = (h c / lambda k_B T)

	Parameters
	----------
	y: float

	Returns
	-------
	d_B_lambda_d_lambda_dimensionless : float
		Not normalized to anything meaningful

	"""
	return (5*y**4 / (np.exp(y)-1)) - (y**5 * np.exp(y) / (np.exp(y)-1)**2)

def d_B_nu_d_T_dimensionless(x):
	"""
	Calculates d(B_nu) / d (T),
	as a function of dimensionless units, x = (h nu / k_B T)

	Parameters
	----------
	x : float

	Returns
	-------
	d_B_nu_d_T_dimensionless : float
		Not normalized to anything meaningful

	"""
	return x**4 * np.exp(x) / (np.exp(x)-1)**2

def d_B_nu_d_T_dimensionless(x):
	"""
	Calculates d(B_nu) / d (T),
	as a function of dimensionless units, x = (h nu / k_B T)

	Parameters
	----------
	x : float

	Returns
	-------
	d_B_nu_d_T_dimensionless : float
		Not normalized to anything meaningful

	"""
	return x**4 * np.exp(x) / (np.exp(x)-1)**2

def d_B_nu_d_T_d_nu_dimensionless(x):
	"""
	Calculates d^2(B_nu) / d (T) / d (nu),
	as a function of dimensionless units, x = (h nu / k_B T)

	Parameters
	----------
	x : float

	Returns
	-------
	d_B_nu_d_T_d_nu_dimensionless : float
		Not normalized to anything meaningful

	"""
	return - np.exp(x)*x**3 * (np.exp(x)*(x-4)+x+4) / (np.exp(x)-1)**3