import statistics
import math
import numpy as np

#Author: Deepak Yadav
#E-mail: dky.united@gmail.com

def bias_corr(n):
    # find out if the number of observations is even or odd
    even = bool((n + 1) % 2)

    # find out if the number of observations is greater than 12
    magnitude_n = min(n, 13)

    # from that a key is generated
    n_key = (even, magnitude_n)

    # now the bias correction is calculated
    # for even n greater 12
    if n_key == (True, 13):

            corr_factor = 3.67561 + ((1.9654 + ((6.987 - (77 / n)) / n)) / n)
            corr_factor = 1 / ((corr_factor / n) + 1)

    # for odd n greater 12
    elif n_key == (False, 13):

            corr_factor = 1.60188 + ((- 2.1284 - (5.172 / n)) /n)
            corr_factor = 1 / ((corr_factor / n) + 1)

    # for n less or equal 12
    else:

            # the index of the list reaches from 0 to 10, while n reaches from 2 to 12, so 2 has to be subtracted
            corr_factor = [0.399356, 0.99365, 0.51321, 0.84401, 0.61220,
                                0.85877, 0.66993, 0.87344, 0.72014, 0.88906, 0.75743][n - 2]

    return corr_factor

def median(x, low=False, high=False):
    if low==True:
        return statistics.median_low(x)

    if high==True:
        return statistics.median_high(x)

    return statistics.median_high(x)

def mad(x, center = None, constant = 1.4826, na = False,
    low = False, high = False):
    """
    Median absolute deviation (MAD), Gaussian efficiency 37%

    """
    if len(x)== 0:
        raise Exception("x sholud be non-empty !!!")
    if len(x)==1:
        return 0
    # if low TRUE, compute the ‘lo-median’, i.e., for even sample size,
    # do not average the two middle values, but take the smaller one.

    # if high TRUE, compute the ‘hi-median’, i.e., take the
    # larger of the two middle values for even sample size.

    center = median(x, low, high) if center==None else center

    amd=[abs(i-center) for i in x]

    return round(constant*median(amd), 6)

def Sn(x, constant = 1.1926, finite_corr=True):
    """
    Sn scale estimator , Gaussian efficiency 58%
    """
    n = len(x)

    if n==0:
        raise Exception("x sholud be non-empty !!!")
    if n==1:
        return 0

    med=[]
    for i in x:
        diff=[]
        for j in x:
            diff.append(abs(i-j))
        med.append(median(diff))
    return round(bias_corr(n) * median(med) * constant, 6) if finite_corr==True else round(median(med) * constant, 6)

def iqr(x):
    """
    Interquartile range
    """

    if len(x)==0:
        raise Exception("x sholud be non-empty !!!")

    if len(x)==1:
        return 0

    q75,q25=np.percentile(x,[75,25])
    return (q75, q25)

def Qn(x, constant = 2.21914, finite_corr=True):
    """
    Qn scale estimator, Gaussian effieciency 82%
    """
    n = len(x)

    if n==0:
        raise Exception("x sholud be non-empty !!!")
    if n==1:
        return 0

    diff = []
    h=0
    k=0
    for i in range(0,n):
        for j in range(0,n):
            if i<j:
                diff.append(abs(x[i]-x[j]))

    diff.sort()
    h=int(math.floor(n/2)+1)
    k=int(h*(h-1)/2)
    return round(constant*diff[k-1]*bias_corr(n), 6) if finite_corr==True else round(constant*diff[k-1], 6)

if __name__ == '__main__':

    x = [i for i in range(1,11)]
    #a = robustbase()
    # print(median(x, low=True))
    # print(mad(x,high=True))
    # print(iqr([1]))
    print(Sn(x))
    print(Sn(x, finite_corr=False))


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

def euler1(F, a, b, x0, y0, n):
    Ly = [y0]
    Lx = [x0]
    x= x0
    y= y0
    h=(b-a)/n
    while x <= b:
        y= h*F(x,y)+y
        x+=h
        Lx.append(x)
        Ly.append(y)
    x= x0
    y= y0
    while x >= a:
        y= -h*F(x,y)+y
        x-=h
        Lx.insert(0, x)
        Ly.insert(0,y)
    return Lx, Ly

def show_euler1():
    a = -5.0
    b = 5.0
    n = 60
    x0, y0 = 0, 1

    Lx, Ly = euler1(lambda x, y:y, a, b, x0, y0, n)
    pl.plot(Lx, Ly, label='euler1') # courbe approché
    x= np.linspace(a, b, n)
    pl.plot(x, np.exp(x), label='exp') # courbe exact
    pl.legend()
    pl.show()

#show_euler1()

def euler2(F, a, b, x0, y0, z0, n):
    Ly = [y0]
    Lx = [x0]
    x= x0
    y= y0
    y1 = z0
    h=(b-a)/n
    while x <= b:
        y2=F(x,y,y1)
        y= h*y1+y
        y1= h*y2+y1
        x+=h
        Lx.append(x)
        Ly.append(y)
    x= x0
    y= y0
    y1 = z0
    while x >= a:
        y2=F(x,y,y1)
        y= -h*y1+y
        y1= -h*y2+y1
        x-=h
        Lx.insert(0, x)
        Ly.insert(0,y)
    return Lx, Ly

def show_euler2():
    a = -30
    b = 30
    n = 1000
    x0, y0, z0 = 0, 0, 1

    Lx, Ly = euler2(lambda x, y, y1:-y, a, b, x0, y0, z0, n)
    pl.plot(Lx, Ly, label='euler2') # courbe approché
    x= np.linspace(a, b, n)
    pl.plot(x, np.sin(x), label='sin') # courbe exact
    pl.legend()
    pl.show()

#show_euler2()

def G(x, Y):
    u,v = Y
    return np.array([v,-u])

def show_methode2():
    a = -30
    b = 30
    n = 1000
    x0, y0, z0 = 0, 0, 1

    Lx, Ly = euler1(G, a, b, x0, np.array([y0, z0]), n)
    pl.plot(Lx, np.array(Ly)[:,0], label='euler1 vect') # courbe methode 2

    Lx, Ly = euler2(lambda x, y, y1:-y, a, b, x0, y0, z0, n)
    pl.plot(Lx, Ly, label='euler2') # courbe approché
    x= np.linspace(a, b, n)
    pl.plot(x, np.sin(x), label='sin') # courbe exact
    pl.legend()

#show_methode2()

def pend(x, Y):
    u, v = Y
    return np.array([v, -np.sin(u)])

def show_pend(z0):

    a = 0
    b = 20
    n = 1000
    x0, y0 = a, 0

    for i in range(len(z0)):
        Lx, Ly = euler1(pend, a, b, x0, np.array([y0, z0[i]]), n)
        l = 'z0 =' + str(z0[i])
        pl.plot(np.array(Ly)[:,0], np.array(Ly)[:,1], label=l) # courbe methode 2
        pl.legend()
        pl.show()

#show_pend([0.5,1,2,2.05,2.4])

def heun(F, a, b, x0, y0, n):
    Ly = [y0]
    Lx = [x0]
    x= x0
    y= y0
    h=(b-a)/n
    while x <= b:
        f = F(x,y)
        y= (h/2)*(f+F(x+h,y+h*f))+y
        x+=h
        Lx.append(x)
        Ly.append(y)
    x= x0
    y= y0
    while x >= a:
        f = F(x,y)
        y= (-h/2)*(f+F(x-h,y-h*f))+y
        x-=h
        Lx.insert(0, x)
        Ly.insert(0,y)
    return Lx, Ly

def show_heun():
    a = -5.0
    b = 5.0
    n = 30
    x0, y0 = 0, 1

    Lx, Ly = euler1(lambda x, y:y, a, b, x0, y0, n)
    pl.plot(Lx, Ly, label='euler1') # courbe approché
    Lx, Ly = heun(lambda x, y:y, a, b, x0, y0, n)
    pl.plot(Lx, Ly, label='heun') # courbe approché
    x= np.linspace(a, b, n)
    pl.plot(x, np.exp(x), label='exp') # courbe exact
    pl.legend()
    pl.show()

show_heun()


### Part 2

# import libraries
import numpy as np
from matplotlib import pyplot as plt

# Spectral Clustering Algorithm
def weight(X, c):
    # Calculate Adjacency weight matrix W.
    l,n = X.shape
    W = np.ones((l,l))
    for i in range(l):
        for j in range(l):
            W[i,j] = np.exp(-c * (np.linalg.norm(X[i]-X[j]))**2)
    return W

def laplacian(W):
    # Calculate Graph Laplacian L.
    l,l = W.shape
    D = np.zeros((l,l))
    for i in range(l):
        D[i,i] = np.sum(W[i])
    L = D - W
    return L

def sec_eig(L):
    # Find the second smallest eigenvector of L.
    val, vec = np.linalg.eig(L)
    a = np.argsort(val)[1]
    return vec[:,a]

def sign(x):
    # Return the sign of x.
    if x >= 0:
        y = 1
    else:
        y = -1
    return y

def spectral_cluster(X, c):
    # Use data X and c to give weight W, laplacian L,
    # cluster vector v2, and use them to do the clustering.
    W = weight(X, c)
    L = laplacian(W)
    v2 = sec_eig(L)
    y = np.ones(v2.shape)
    for i in range(len(v2)):
        y[i] = sign(v2[i])
    return y

def best_cluster(X, Y, c):
    # Implement spectral_cluster function with different c
    # to determine the best c which gives the best cluster.
    # The return value is the best c, corresponding correctness and prediction.
    l,n = X.shape
    final_y = []
    final_c = 0
    final_cor = 0
    for i in c:
        y = spectral_cluster(X, i)
        cor = max((y == Y).sum(), (-y == Y).sum())
        if final_cor < cor:
            final_cor = cor
            final_c = i
            final_y = y
    final_cor_rate = final_cor / l

    return final_c, final_cor_rate, final_y

### Experiment 1

# Load data
twomoons = np.loadtxt('C:/Users/james007/Desktop/twomoons.dat.txt')
X_moons = twomoons[:, 1:]
Y_moons = twomoons[:, 0]

# Give a sequence of c and find the best cluster of data
c1 = np.linspace(0, 100, 101)[1:]
moons_c, moons_cor_rate, moons_y = best_cluster(X_moons, Y_moons, c1)
print(f'The best c is {moons_c} and it gives a correctness of{moons_cor_rate: .2%}')

# Plot of the comparision
plt.figure(figsize=(12,6))

# Plot of original data
plt.subplot(121)
plt.scatter(X_moons[:, 0], X_moons[:, 1], c = Y_moons)
plt.title('Original data')

# Plot of spectral clustering
plt.subplot(122)
plt.scatter(X_moons[:, 0], X_moons[:, 1], c = moons_y)
plt.title('Spectral clustering algorithm')

plt.savefig('P2E1')

### Experiment 2
I = np.eye(2)

# Generate random class '-1'
x_1 = np.random.multivariate_normal((-0.3,-0.3), 0.04*I, 20)
y_1 = -1 * np.ones(20)

# Generate random class '+1'
x_2 = np.random.multivariate_normal((0.15,0.15), 0.01*I, 20)
y_2 = np.ones(20)

# Combine two classes to give the data and label
X_random = np.array(list(x_1) + list(x_2))
Y_random = np.array(list(y_1) + list(y_2))

# Give a sequence of c and find the best cluster of data
c2 = np.linspace(0, 2000, 2001)[1:]
random_c, random_cor_rate, random_y = best_cluster(X_random, Y_random, c2)
print(f'The best c is {random_c} and it gives a correctness of{random_cor_rate: .2%}')

# Plot of the comparision
plt.figure(figsize=(12,8))

# Plot of original data
plt.subplot(211)
plt.scatter(X_random[:, 0], X_random[:, 1], c = Y_random)
plt.title('Original data')

# Plot of spectral clustering
plt.subplot(212)
plt.scatter(X_random[:, 0], X_random[:, 1], c = random_y)
plt.title('Spectral clustering algorithm')

plt.savefig('P2E2')

### Experiment 3

# Load data
dtrain123 = np.loadtxt('C:/Users/james007/Desktop/dtrain123.dat.txt')

# Define dtrain13 to only contain data with label (1,3)
dtrain13 = []
for i in range(dtrain123.shape[0]):
    if dtrain123[i, 0] == 1 or dtrain123[i, 0] == 3:
        dtrain13.append(dtrain123[i])
dtrain13 = np.array(dtrain13)

X_dt = dtrain13[:, 1:]
Y_dt = dtrain13[:, 0]

#Relabel the data, change label (1,3) to label (1,-1)
classy = np.ones(Y_dt.shape)
for i in range(len(Y_dt)):
    classy[i] = 2 * (Y_dt[i] == 1) -1

# Give a sequence of c and find the best cluster of data
# Give the result of best c and best CP
c3 = np.linspace(0, 0.1, 11)
CP = np.ones(len(c3))
for i in range(len(c3)):
    y = spectral_cluster(X_dt, c3[i])
    cor = max((y == classy).sum(), (-y == classy).sum())
    CP[i] = cor / len(classy)
best_CP = np.max(CP)
best_c = c3[np.argmax(CP)]
print(f'The best c is {best_c} and the corresponding CP is{best_CP: .5f}')

# Plot of correctness vs c
plt.figure(figsize=(12,6))
plt.plot(c3, CP)
plt.xlabel('Parameter (scale factor)')
plt.ylabel('Correct cluster percentage')
plt.title('Model Selection of parameters')
plt.scatter(c3, CP, marker='*')

plt.savefig('P2E3.png')


'''

Catherine Kellar
Nov 24 17
Groebner basis generator given roots

'''
import numpy as np
import scipy as sp
from scipy.misc import comb

nchoosek = lambda n,k: int(comb(n,k))

def getMonBase(d,n):
    '''base = getMonBase(d,n)
    ---------------------
    Returns a set of monomials of total degree d in n variables. Each row of
    mon refers to a n-tuple of exponents of a monomial whereby each column
    corresponds to a variable.

    Inputs:
    d (int): maximum total degree of the monomials in the base

    n (int): number of exponents

    returns:
    base (matrix): matrix of lexicographic ordered monomials of degree d and n exponents

    example: d= 2, n = 3

    base =

          2     0     0
          1     1     0
          1     0     1
          0     2     0
          0     1     1
          0     0     2
    '''
    #MATLAB - ';' = supress output
    # x = [1 2] is just an array
    # x = [7;9] => [[7],[9]]
    # x' => x.T
    # x = zeros(6,3) => 6row x 3row of zeros
    if n == 1:
        base = [[d]]
    else:
        base = np.zeros(n)
        base[0] = d

        for i in range(d-1,-1,-1):
            ones_col = np.reshape(i*np.ones(int(comb(d-i+n-2,n-2))),(-1,1))
            square = getMonBase(d-i, n-1)
            bottom_rows = np.hstack((ones_col, square))
            base = np.vstack((base, bottom_rows))
    return np.array(base).astype(int)

def getMon(d,n):
    '''#fullBase = getMon(d,n) or getMon(d,n,d0)
    ----------------------------------------------

    Returns a full canonical base of monomials of total degree d and in n
    variables. Each row of mon refers to a n-tuple of exponents of a monomial
    whereby each column corresponds to a variable.

    example: d= 2, n = 3

    fullBase =

         0     0     0
         1     0     0
         0     1     0
         0     0     1
         2     0     0
         1     1     0
         1     0     1
         0     2     0
         0     1     1
         0     0     2
    '''
    if ~isempty(varargin)
        d0 = varargin{1};
        if d0 > d
            fullBase = [];
            return
    else
        d0 = 0

    if d0 == 0
        fullBase = zeros(nchoosek(d+n,n),n);
        rowCounter = 2;
        for i = 1 : d,
            tempbase = getMonBase(i,n);
            fullBase(rowCounter:rowCounter+length(tempbase)-1,:)  = tempbase;
            rowCounter = rowCounter+length(tempbase);
    else
        rowCounter = 1;
        for i = d0 : d,
            tempbase = getMonBase(i,n);
            fullBase(rowCounter:rowCounter+length(tempbase)-1,:)  = tempbase;
            rowCounter = rowCounter+length(tempbase);

    return fullBase
'''
def diffBase(d,root,x):
    #y = diffBase(d,root,x)
    #----------------------
    #
    #Applies the (partial) differential operator on a polynomial base vector
    #of degree 'd' and evaluates it with 'root'. Graded xelicographic ordering
    #is implicitly assumed. Multiple differentiation is also supported, x
    #should then be a vector which indicates in which order there needs to be
    #differentiated.
    #
    #y     =   column vector, contains the evaluated polynomial base vector
    #
    #          d^n1x1 ... d^nnxn  |
    #          ------------------ |
    #          dx1^n1 ... dxn^nn  |x = root
    #
    #d     =   scalar, degree of the multivariate polynomial base vector
    #
    #root  =   row vector, used to evaluate differentiated base with
    #
    #x     =   row vector, index of the variable to which the differentation needs
    #          to take place,
    #
    #               d        d        d
    #          1 = ---, 2 = ---, 3 = ---, etc...
    #              dx1      dx2      dx3
    #
    #          when a higher order differentiation is required then x is a row
    #          vector indicating the order in which the variables need to be
    #          differentiated, eg. x = [1 1 2] means first a 2nd order
    #          differentiation to x1, then 1st order differentiation to x2.
    #
    #EXAMPLE
    #-------
    #
    #second order derivative to y (= Dyy) of degree 6
    #evaluated in the point (2,3):
    #
    #diffBase(6,[2 3],[2 2])
    #
    #first 1st order derivative to y, then 1st order derivative to x (= Dyx),
    #degree 4 and evaluated in (-5,9):
    #
    #diffBase(4,[-5 9],[2 1])
    #
    #CALLS
    #-----
    #
    #getMon.m
    #
    #Kim Batselier, 2010-01-28

    n = size(root,2);
    monBase = getMon(d,n);
    l = length(monBase);
    #coef = zeros(l,1);
    Dn = size(x,2);

    if size(x,2) ~= Dn
        error('The ''x'' argument provided should be a vector indicating for each differentiation step to which variable needs to be differentiated')
    end

    #check function inputs
    if x > n
            error(['You cannot differentiate with respect to x' num2str(x) ', there are only ' num2str(n) ' variables.'])
    end

    coef = ones(l,1);

    #derivative of the monomial base
    DmonBase  = monBase;
    for i = 1 : Dn
        if exist('indices','var')
            clear indices
        end
        #first run of the coefficients
        indices = find(DmonBase(:,x(i)) ~= 0);
        coef(indices) = coef(indices).*DmonBase(indices,x(i));

        DmonBase = DmonBase-[zeros(l,x(i)-1) ones(l,1) zeros(l,n-x(i))];

        #negative exponents are manually put to zero
        DmonBase = DmonBase.*(~(DmonBase(:,x(i)) < 0)*ones(1,n));
    end

    temp = zeros(l,n);
    for i = 1:length(indices)
        temp(indices(i),:) = root.^DmonBase(indices(i),:);
    end

    y = zeros(l,1);
    y = coef.*prod(temp,2)./getDenom(x);

        function denom = getDenom(x)
            xmax = max(x);
            exp = zeros(1,xmax);
            for i = 1 : xmax
                exp(i) = length(find(x== i));
            end
            denom = prod(factorial(exp));
        end
    end

def makeRoot(d,root):
    #sol = makeRoot(d,root)
    #----------------------
    #
    #Evaluates a multivariate polynomial base vector.
    #
    #sol   =   column vector, contains the evaluated polynomial base vector
    #
    #d     =   scalar, degree of the multivariate polynomial base vector
    #
    #root  =   row vector, used to evaluate differentiated base with
    #
    #CALLS
    #-----
    #
    #getMon.m
    #
    #Kim Batselier, 2010-01

    n = size(root,2);

    monBase = getMon(d,n);

    l = length(monBase);

    for j = 1 : size(root,1)
        temp = zeros(l,n);
        for i = 1 : n
            temp(:,i) = (root(j,i)*ones(l,1)).^monBase(:,i);
        end

        sol(:,j) = prod(temp,2);
    end

    end

def getBasis(d, depth, root):
    """calculates the subspace basis for the kernel, used in calculating a groebner basis.
    Uses partial derivatives to achieve this.

    input: d(int): degree of monomial base
           depth(int): max dgree of differentiation
           root (array): the root at which we evaluate the derivatives
    return: D(matrix): matrix with each column being the partial evaluated at the root
            I(matrix): indices of differentiation
    """
    n = size(root,2) #2nd dimension of root - finding n for number of variables that polys should be in
    D(:,1) = makeRoot(d,root)

    I = getMon(depth,n);

    for i = 2 : size(I,1)
        temp = ones(1,I(i,1))
        for j = 2 : n
            temp = [temp j*ones(1,I(i,j))]
        D(:,i) = diffBase(d,root,temp)

    I = I'

def groebner_from_roots(rootList):
    """generates a groebner basis from a list of roots in n dimensions

    input: list of roots in n dimensions - must all be unique

    return: list of polynomials that form a reduced groebner basis for said roots
    """
    a=[];
    b=1;
    polysys=[];

    m,n = rootList.size;

    mult=np.ones((1,m)); #there are no multiplicities allowed - m multiplicities total

    stop=0;
    d=0;

    while not stop
        d+=1;
        # construct kernel K
        # for now, make whole K everytime
        K=[];
        for i in range(1,m):      # for each root
            # determine order of differentiation
            ddiff=0;
            while sp.misc.comb(ddiff+n,n) < m:
                ddiff+=1;
            ####################
            D=getKSB(d,ddiff,root(i,:));
            ####################
            K=[K D];

        indices = nchoosek(d-1+n,n)+1:nchoosek(d+n,n); #indices of all monomials of degree d

        # remove multiples of A from indices
        for i=1:length(indices) #for each new monomial
            for j=1:length(a)   #check whether it is multiple of a(j)
                if  sum((fite(indices(i),n)-fite(a(j),n)) >= 0) == n
                    #we found a multiple
                    indices(i) = 0;
                end
            end
        end
        indices(indices==0)=[];
        if isempty(indices)
            stop=1;
        else
            #canonical decomposition
            for i=1:length(indices)
                [U,S,Z]=svd(full(K([b indices(i)],:)'));
                if size(S,2)==1
                    S=S(1,1);
                else
                    S=diag(S);
                end
                tol=m*S(1)*eps;
                rs=sum(S > tol);

                if (S(end) < tol) || (rs < length([b indices(i)]))
                    a=[a indices(i)];
                    temp=zeros(1,nchoosek(d+n,n));
                    temp([b indices(i)])=Z(:,end);
                    temp(abs(temp)<tol)=0;  #remove numerically zero coefficients
                    polysys=[polysys;vec2polysys(temp,n)];
                else
                    b=[b indices(i)];
                end
            end
        end
    end

    end'''


import numpy as np

def gen_gaussian_distribution(size, mean=None, cov=None):
    if not mean:
        mean = np.random.randn(2)
    if not cov:
        cov = np.eye(2)
    data = np.random.multivariate_normal(mean, cov, size)
    return data

class Perceptron(object):

    def __init__(self, n_inputs, max_epochs=1e3, learning_rate=1e-2):
        """
        Initializes perceptron object.
        Args:
            n_inputs: number of inputs.
            max_epochs: maximum number of training cycles.
            learning_rate: magnitude of weight changes at each training cycle
        """
        self.n_inputs = n_inputs
        self.max_epochs = max_epochs
        self.learning_rate = learning_rate
        self.weights = np.zeros(self.n_inputs)
        self.bias = 0

    def forward(self, input):
        """
        Predict label from input
        Args:
            input: array of dimension equal to n_inputs.
        """
        sum = np.sign(np.dot(input, self.weights))
        label = np.where(sum > 0, 1, -1)
        return label

    def train(self, training_inputs, labels):
        """
        Train the perceptron
        Args:
            training_inputs: list of numpy arrays of training points.
            labels: arrays of expected output value for the corresponding point in training_inputs.
        """
        train_size = len(training_inputs)
        epochs = 0
        while epochs < self.max_epochs:
            epochs += 1
            for i in range(train_size):
                if np.any(labels[i] * (np.dot(self.weights, training_inputs[i]) + self.bias) <= 0):
                    self.weights = self.weights + (self.learning_rate * labels[i] * training_inputs[i]).T
                    self.bias = self.bias + self.learning_rate * labels[i]

    def score(self, test_inputs, test_labels):
        pred_arr = np.where(self.forward(test_inputs) > 0, 1, -1)
        true_size = len(np.where(pred_arr == test_labels)[0])
        return true_size / len(test_labels)

def main():
    p = Perceptron(2)

    """
    gen dataset
    """
    data_size = 100
    train_size = 80
    x1 = gen_gaussian_distribution(data_size, [5, 5])
    x2 = gen_gaussian_distribution(data_size, [-5, -5])
    y1 = a_label = np.ones(data_size, dtype=np.int16)
    y2 = -y1
    x_train = np.concatenate((x1[:train_size], x2[:train_size]), axis=0)
    y_train = np.concatenate((y1[:train_size], y2[:train_size]), axis=0)
    x_test = np.concatenate((x1[train_size:], x2[train_size:]), axis=0)
    y_test = np.concatenate((y1[train_size:], y2[train_size:]), axis=0)

    """
    train model
    """
    p.train(x_train, y_train)

    """
    test model
    """
    acc = p.score(x_test, y_test)
    print(f'Perceptron test accuracy: {acc * 100}%')

if __name__ == "__main__":
    main()


#!/usr/local/Cellar/python/2.7.6/bin/python
# -*- coding: utf-8 -*-

import sys
from numpy import *

import scipy.misc, scipy.io, scipy.optimize

from matplotlib import pyplot, cm, colors, lines
from mpl_toolkits.mplot3d import Axes3D

import inspect

def multivariateGaussian( X, mu, sigma2 ):
	k 			= len( mu )
	m, n 		= shape( X )
	temp_sigma2 = diag( sigma2 )
	temp_X 		= X - mu

	p  	= (2.0 * pi) ** (-k / 2.0) * linalg.det( temp_sigma2 ) ** -0.5 \
		* exp(-0.5 * sum((temp_X * diagonal( linalg.pinv( temp_sigma2 ) )) * temp_X, axis=1))

	return p

def estimateGaussian( X ):
	mu 		= mean( X, axis=0 )
	sigma2 	= var( X, axis=0 )
	return mu, sigma2

def plot( X ):
	pyplot.plot( X[:, 0], X[:, 1], 'bx' )
	pyplot.ylim([0, 30])
	pyplot.xlim([0, 30])

	pyplot.xlabel('Latency (ms)')
	pyplot.ylabel('Throughput (mb/s)')

def visualizeFit( X, mu, sigma2 ):

	X1 = mgrid[0:35.5:0.5]
	X2 = mgrid[0:35.5:0.5]
	X1, X2 = meshgrid( X1, X2 )

	Z = multivariateGaussian( c_[X1.T.ravel().T, X2.T.ravel().T], mu, sigma2 )
	Z = Z.reshape( shape(X1)[0], shape(X1)[1] ).T

	a = arange( -20, 0, 3 )
	b = ones( shape( a ) ) * 10

	pyplot.contour(X1, X2, Z, pow( b, a ) )

def selectThreshold( y_val, p_val ):
	best_epsilon = 0
	best_f1 = 0
	f1 = 0

	step_size = (max(p_val) - min(p_val)) / 1000.0

	for epsilon in arange(min(p_val), max(p_val)+step_size, step_size ):
		prediction = (p_val < epsilon).reshape( shape( y_val)[0], 1 )

		true_pos  = sum((prediction == 1) & (y_val == 1))
		false_pos = sum((prediction == 1) & (y_val == 0))
		false_neg = sum((prediction == 0) & (y_val == 1))

		precision 	= true_pos * 1.0 / (true_pos + false_pos)
		recall  	= true_pos * 1.0 / (true_pos + false_neg)

		f1 = (2 * precision * recall) / (precision + recall)

		if f1 > best_f1:
			best_f1 		= f1
			best_epsilon 	= epsilon

	return best_f1, best_epsilon

def part1_1():
	mat 		 = scipy.io.loadmat( "/Users/saburookita/Downloads/mlclass-ex8-004/mlclass-ex8/ex8data1.mat" )
	X 			 = mat['X']
	X_val, y_val = mat['Xval'], mat['yval']

	plot( X )

def part1_2():
	mat 		 = scipy.io.loadmat( "/Users/saburookita/Downloads/mlclass-ex8-004/mlclass-ex8/ex8data1.mat" )
	X 			 = mat['X']

	mu, sigma2 = estimateGaussian( X )
	print mu
	print sigma2

	plot( X )
	visualizeFit( X, mu, sigma2 )
	pyplot.show( block=True )

def part1_3():
	mat 		 = scipy.io.loadmat( "/Users/saburookita/Downloads/mlclass-ex8-004/mlclass-ex8/ex8data1.mat" )
	X 			 = mat['X']
	X_val, y_val = mat['Xval'], mat['yval']

	mu, sigma2 = estimateGaussian( X )

	p 	  = multivariateGaussian( X, mu, sigma2 )
	p_val = multivariateGaussian( X_val, mu, sigma2 )

	f1, epsilon = selectThreshold( y_val, p_val )
	outliers 	= where( p < epsilon )

	plot( X )
	visualizeFit( X, mu, sigma2 )
	pyplot.plot( X[outliers, 0], X[outliers, 1], 'ro', lw=2, ms=10 )
	pyplot.show()

def part1_4():
	mat = scipy.io.loadmat( "/Users/saburookita/Downloads/mlclass-ex8-004/mlclass-ex8/ex8data2.mat" )
	X 			 = mat['X']
	X_val, y_val = mat['Xval'], mat['yval']

	mu, sigma2 = estimateGaussian( X )

	p_val = multivariateGaussian( X_val, mu, sigma2 )

	f1, epsilon = selectThreshold( y_val, p_val )
	print epsilon
	print f1

def main():
	set_printoptions(precision=6, linewidth=200)
	part1_1()
	part1_2()
	part1_3()
	part1_4()

if __name__ == '__main__':
	main()


# Use the numpy library
import numpy as np

def prepare_inputs(inputs):
    # TODO: create a 2-dimensional ndarray from the given 1-dimensional list;
    #       assign it to input_array
    input_array = None

    # TODO: find the minimum value in input_array and subtract that
    #       value from all the elements of input_array. Store the
    #       result in inputs_minus_min
    inputs_minus_min = None

    # TODO: find the maximum value in inputs_minus_min and divide
    #       all of the values in inputs_minus_min by the maximum value.
    #       Store the results in inputs_div_max.
    inputs_div_max = None

    # return the three arrays we've created
    return input_array, inputs_minus_min, inputs_div_max

def multiply_inputs(m1, m2):
    # TODO: Check the shapes of the matrices m1 and m2.
    #       m1 and m2 will be ndarray objects.
    #
    #       Return False if the shapes cannot be used for matrix
    #       multiplication. You may not use a transpose
    pass

    # TODO: If you have not returned False, then calculate the matrix product
    #       of m1 and m2 and return it. Do not use a transpose,
    #       but you swap their order if necessary
    pass

def find_mean(values):
    # TODO: Return the average of the values in the given Python list
    pass

input_array, inputs_minus_min, inputs_div_max = prepare_inputs([-1,2,7])
print("Input as Array: {}".format(input_array))
print("Input minus min: {}".format(inputs_minus_min))
print("Input  Array: {}".format(inputs_div_max))

print("Multiply 1:\n{}".format(multiply_inputs(np.array([[1,2,3],[4,5,6]]), np.array([[1],[2],[3],[4]]))))
print("Multiply 2:\n{}".format(multiply_inputs(np.array([[1,2,3],[4,5,6]]), np.array([[1],[2],[3]]))))
print("Multiply 3:\n{}".format(multiply_inputs(np.array([[1,2,3],[4,5,6]]), np.array([[1,2]]))))

print("Mean == {}".format(find_mean([1,3,4])))


import numpy as np
import matplotlib
matplotlib.use('GTK3Cairo')
import matplotlib.pyplot as plt
from temp_schedule import EASchedule
from simulated_annealing import sa_solve

a, b, c, d = -2, 2, -1, 3

def f_obj(x, y): return -(1 - x)**2 - 100 * (y - x**2)**2

grid3 = np.array([
    [f_obj(a + i * (b - a) / 99., c + j * (d - c) / 99.) for j in range(100)]
    for i in range(100)])

# Create exponential additive cooling schedule
cooling_sched = EASchedule(initial_temp=10,
                           final_temp=0,
                           num_cycles=5000)

# Find global maximum with simulated annealing.
optim_state, f_max, stats = sa_solve(grid3,
                                     temp_schedule=cooling_sched,
                                     initial_state=(0, 0),
                                     log_interval=5000)

# Transform state trajectory from grid to ℝ².
s_traj = np.array([[a + (p[0]) * (b - a) / 99. for p in stats.traj_history],
                   [c + (p[1]) * (d - c) / 99. for p in stats.traj_history]])

# Transform EMA of state trajectory from grid to ℝ².
ema_traj = np.array([[a + (p[0]) * (b - a) / 99. for p in stats.traj_ema_history],
                     [c + (p[1]) * (d - c) / 99. for p in stats.traj_ema_history]])

# Sample f₃(x, y) on 100 x 100 grid.
x = np.linspace(a, b, 100)
y = np.linspace(c, d, 100)
x, y = np.meshgrid(x, y)
z = np.zeros(x.shape)
for i in range(x.shape[0]):
    for j in range(x.shape[1]):
        z[i, j] = f_obj(x[i, j], y[i, j])

# Create figure 2.a.
fig, ax = plt.subplots()
ax.contourf(x, y, -np.log(-z), cmap='viridis', zorder=-40)
plt_ema = ax.plot(ema_traj[0], ema_traj[1], linewidth=1.5,
                  c='white', zorder=-30)
plt_ema[0].set_label('EMA trajectory')

plt_f_actual = ax.scatter([1], [1], c='red', s=400, marker='+', zorder=3)
plt_f_actual.set_label('Analytical solution')

plt_f_sol = ax.scatter(s_traj[0, -1], s_traj[1, -1], s=400,
                       marker='+', c='blue', zorder=4)
plt_f_sol.set_label('SA solution')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend()

# Save figure 2.a to file.
plt.savefig(fname='figures/figure2-1.svg')

# plt.show()

# Create figure 2.b.
plt.cla()
ax.plot(stats.temp_history[:8000])
ax.set_xlabel('Cycle k')
ax.set_ylabel('Temperature T')

plt.show()

# Save figure 2.b to file.
plt.savefig(fname='figures/figure2-2.svg')


import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from copy import deepcopy

center_1 = np.array([1,1])
center_2 = np.array([5,5])
center_3 = np.array([8,1])

data_1 = np.random.randn(200, 2) + center_1
data_2 = np.random.randn(200,2) + center_2
data_3 = np.random.randn(200,2) + center_3

data = np.concatenate((data_1, data_2, data_3), axis = 0)

plt.scatter(data[:,0], data[:,1], s=7)

k = 3

n = data.shape[0]

c = data.shape[1]

mean = np.mean(data, axis = 0)
std = np.std(data, axis = 0)
centers = np.random.randn(k,c)*std + mean

plt.scatter(data[:,0], data[:,1], s=7)
plt.scatter(centers[:,0], centers[:,1], marker='*', c='g', s=150)

centers_old = np.zeros(centers.shape)
centers_new = deepcopy(centers)
clusters = np.zeros(data.shape[0])
distances = np.zeros((data.shape[0], k))
error = np.linalg.norm(centers_new - centers_old)

while error != 0:
    for i in range(k):
        distances[:,i] = np.linalg.norm(data - centers_new[i], axis = 1)
    clusters = np.argmin(distances, axis=1)
    centers_old = deepcopy(centers_new)
    for i in range(k):
        centers_new[i] = np.mean(data[clusters == i], axis = 0)
    error = np.linalg.norm(centers_new - centers_old)

plt.scatter(data[:,0], data[:,1], s=7)
plt.scatter(centers_new[:,0], centers_new[:,1], marker='*', c='g', s=150)


import numpy as np

from pymanopt.manifolds.manifold import Manifold

class PoincareBall(Manifold):
    """
    Factory class for the Poincare ball model of hyperbolic geometry.
    An instance represents the Cartesian product of n (defaults to 1)
    Poincare balls of dimension k.
    Elements are represented as matrices of size k x n, or arrays of
    size k if n = 1.
    The Poincare ball is embedded in R^k and is a Riemannian manifold,
    but it is not an embedded Riemannian submanifold.
    At every point x, the tangent space at x is R^k since the manifold
    is open.
    The metric is conformal to the Euclidean one (angles are preserved),
    and it is given at every point x by
        <u, v>_x = lambda_x^2 <u, v>,
    where lambda_x = 2 / (1 - norm{x}^2) is the conformal factor.
    This induces the following distance between any two points x and y:
        dist(x, y) = acosh(
            1 + 2 norm{x - y}^2 / (1 - norm{x}^2) / (1 - norm{y}^2)
        ).
    """

    def __init__(self, k: int, n: int = 1):
        self._k = k
        self._n = n

        if k < 1:
            raise ValueError("Need k >= 1. Value supplied was k = %d." % k)
        if n < 1:
            raise ValueError("Need n >= 1. Value supplied was n = %d." % n)

        if n == 1:
            name = "Poincare ball B({:d})".format(k)
        elif n >= 2:
            name = "Product Poincare ball B({:d})^{:d}".format(k, n)

        dimension = k * n
        super().__init__(name, dimension)

    @property
    def typicaldist(self):
        return self.dim / 8

    # The metric in the Poincare ball is conformal to the Euclidean one.
    def conformal_factor(self, X):
        return 2 / (1 - np.sum(X * X, axis=0))

    def inner(self, X, G, H):
        factors = np.square(self.conformal_factor(X))
        return np.sum(G * H * factors)

    # Identity map since the tangent space is the ambient space.
    def proj(self, X, G):
        return G

    def norm(self, X, G):
        return np.sqrt(self.inner(X, G, G))

    # Generates points sampled uniformly at random in the unit ball.
    # In high dimension (large k), sampled points are very likely to
    # be close to the boundary because of the curse of dimensionality.
    def rand(self):
        if self._n == 1:
            N = np.random.randn(self._k)
        else:
            N = np.random.randn(self._k, self._n)
        norms = np.linalg.norm(N, axis=0)
        radiuses = np.random.rand(self._n) ** (1.0 / self._k)
        return radiuses * N / norms

    def randvec(self, X):
        return np.random.randn(*np.shape(X))

    def zerovec(self, X):
        return np.zeros(np.shape(X))

    # Geodesic distance.
    def dist(self, X, Y):
        norms2_X = np.sum(X * X, axis=0)
        norms2_Y = np.sum(Y * Y, axis=0)
        difference = X - Y
        norms2_difference = np.sum(difference * difference, axis=0)

        columns_dist = np.arccosh(
            1 + 2 * norms2_difference / ((1 - norms2_X) * (1 - norms2_Y))
        )
        return np.sqrt(np.sum(np.square(columns_dist)))

    # The hyperbolic metric tensor is conformal to the Euclidean one,
    # so the Euclidean gradient is simply rescaled.
    def egrad2rgrad(self, X, G):
        factors = np.square(1 / self.conformal_factor(X))
        return G * factors

    # Derived from the Koszul formula.
    def ehess2rhess(self, X, G, H, U):
        lambda_x = self.conformal_factor(X)
        return (
            np.sum(G * X, axis=0) * U
            - np.sum(X * U, axis=0) * G
            - np.sum(G * U, axis=0) * X
            + H / lambda_x
        ) / lambda_x

    # Exponential map is cheap so use it as a retraction.
    def retr(self, X, G):
        return self.exp(X, G)

    # Special non-associative and non-commutative operation
    # which is closed in the Poincare ball.
    # Performed column-wise here.
    def mobius_addition(self, X, Y):
        scalar_product = np.sum(X * Y, axis=0)
        norm2X = np.sum(X * X, axis=0)
        norm2Y = np.sum(Y * Y, axis=0)

        return (X * (1 + 2 * scalar_product + norm2Y) + Y * (1 - norm2X)) / (
            1 + 2 * scalar_product + norm2X * norm2Y
        )

    def exp(self, X, U):
        norm_U = np.linalg.norm(U, axis=0)
        # Handle the case where U is null.
        W = U * np.divide(
            np.tanh(norm_U / (1 - np.sum(X * X, axis=0))),
            norm_U,
            out=np.zeros_like(U),
            where=norm_U != 0,
        )
        return self.mobius_addition(X, W)

    def log(self, X, Y):
        W = self.mobius_addition(-X, Y)
        norm_W = np.linalg.norm(W, axis=0)
        return (1 - np.sum(X * X, axis=0)) * np.arctanh(norm_W) * W / norm_W

    # I don't have a nice expression for this.
    # To be completed in the future.
    def transp(self, X1, X2, G):
        raise NotImplementedError

    def pairmean(self, X, Y):
        return self.exp(X, self.log(X, Y) / 2)


import numpy as np
np.seterr(divide='ignore', invalid='ignore') #para ignorar el error de dividir por 0
import matplotlib.pyplot as plt

# Orden
n_terms = 5

# valores de x
x_vals = np.linspace(0,1,100)

# valores de y
y_vals = np.zeros([n_terms,len(x_vals)])

for n in range(n_terms):
    #Para orden 0
    if n == 0:
        y_vals[n,:] = np.ones(len(x_vals))
    else:
        new_term = 1**n * (x_vals**n)
        y_vals[n,:] = y_vals[n-1,:] + new_term

# Graficar los valores de los terminos
for n in range(n_terms):
    #if n == 25:
        label = str(n) + " order"
        plt.plot(x_vals,y_vals[n,:], label=label)

# grafica la funcion real
plt.plot(x_vals, 1/(1-x_vals), label="$1/(1-x)$")
plt.xlabel("x")
plt.ylabel("y")
plt.ylim([0,25])
plt.legend()


import math
import numpy as np
import torch

'''
Point cloud transform:
    pc:
        torch: [b, 3, p]
        np: [(b, )3, p]
'''

# translation normalization
def centralize(pc):
    return pc - pc.mean(dim=2, keepdim=True)

def centralize_np(pc, batch=False):
    axis = 2 if batch else 1
    return pc - pc.mean(axis=axis, keepdims=True)

# scale/translation normalization
def normalize(pc):
    pc = centralize(pc)
    var = pc.pow(2).sum(dim=1, keepdim=True).sqrt()
    return pc / var.max(dim=2, keepdim=True)

def normalize_np(pc, batch=False):
    pc = centralize_np(pc, batch)
    axis = 1 if batch else 0
    var = np.sqrt((pc**2).sum(axis=axis, keepdims=True))
    return pc / var.max(axis=axis+1, keepdims=True)


import numpy as np
import math

'''
The spherical geometry module is a simple collection of calculations on a sphere
'''

def sphere_distance(lat1,lon1,lat2,lon2):
    '''
    Calculate the great circle distance between two points on the sphere designated by their latitude and longitude

    :param lat1: latitude of first point
    :param lon1: longitude of first point
    :param lat2: latitude of second point
    :param lon2: longitude of second point
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the great circle distance in kilometres between the two points
    :rtype: float

    The great circle distance is the shortest distance between any two points on the Earths surface.
    The calculation is done by first calulating the angular distance between the points and then
    multiplying that by the radius of the Earth. The angular distance calculation is handled by
    another function.
    '''
    earths_radius = 6400.0
    delta = angular_distance(lat1,lon1,lat2,lon2)*earths_radius
    return delta

def angular_distance(lat1,lon1,lat2,lon2):
    '''
    :param lat1: latitude of first point
    :param lon1: longitude of first point
    :param lat2: latitude of second point
    :param lon2: longitude of second point
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the angular great circle distance between the two points in radians
    :rtype: float
    '''
#calculate distance between two points on a sphere
#input latitudes and longitudes should be in degrees
#output is in radians
    assert lat1 != None and not(math.isnan(lat1))
    assert lon1 != None and not(math.isnan(lon1))
    assert lat2 != None and not(math.isnan(lat2))
    assert lon2 != None and not(math.isnan(lon2))

    radians_per_degree = np.pi / 180.

#convert degrees to radians
    lat1 = lat1 * radians_per_degree
    lon1 = lon1 * radians_per_degree
    lat2 = lat2 * radians_per_degree
    lon2 = lon2 * radians_per_degree

    delta_lambda = abs(lon1-lon2)
    bit1 = (np.cos(lat2)*np.sin(delta_lambda))
    bit1 = bit1*bit1
    bit2 = ( np.cos(lat1)*np.sin(lat2) - np.sin(lat1)*np.cos(lat2)*np.cos(delta_lambda) )
    bit2 = bit2 * bit2
    topbit = bit1 + bit2
    topbit = np.sqrt(topbit)
    bottombit = np.sin(lat1)*np.sin(lat2) + np.cos(lat1)*np.cos(lat2)*np.cos(delta_lambda)
    delta = np.arctan2(topbit, bottombit)

    return delta

def lat_lon_from_course_and_distance(lat1, lon1, tc, d):
    '''
    :param lat1: latitude of first point in degrees
    :param lon1: longitude of first point in degrees
    :param tc: true course measured clockwise from north in degrees
    :param d: distance travelled in kilometres
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the new latitude and longitude
    :rtype: float
    '''
#calculate a latitude and longitude given a starting point, course (in radian) and angular distance (also in radians)
#http://williams.best.vwh.net/avform.htm#LL
    radians_per_degree = np.pi / 180.
    lat1 = lat1 * radians_per_degree
    lon1 = lon1 * radians_per_degree
    tcr = tc * radians_per_degree

    dr = d/6400.

    lat =np.arcsin(np.sin(lat1)*np.cos(dr)+np.cos(lat1)*np.sin(dr)*np.cos(tcr))
    dlon=np.arctan2(np.sin(tcr)*np.sin(dr)*np.cos(lat1),np.cos(dr)-np.sin(lat1)*np.sin(lat))
    lon= math.fmod( lon1 + dlon + np.pi, 2.*np.pi ) - np.pi

    lat = lat / radians_per_degree
    lon = lon / radians_per_degree

    return (lat,lon)

def course_between_points(lat1,lon1,lat2,lon2):
    '''
    :param lat1: latitude of first point in degrees
    :param lon1: longitude of first point in degrees
    :param lat2: latitude of second point in degrees
    :param lon2: longitude of second point in degrees
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the initial true course in degrees at point one along the great circle between point one and point two
    :rtype: float
    '''
#given two points find the initial true course at point1
#inputs are in degrees and output is in degrees
    d = angular_distance(lat1,lon1,lat2,lon2)
    radians_per_degree = np.pi / 180.

    if d != 0:

        lat1 = lat1 * radians_per_degree
        lon1 = lon1 * radians_per_degree
        lat2 = lat2 * radians_per_degree
        lon2 = lon2 * radians_per_degree

        if np.cos(lat1) < 0.0000001:
            if lat1 > 0:
                tc1 = np.pi
            else:
                tc1 = 2.*np.pi
        else:
            if np.sin(lon2-lon1) > 0:
                if ((np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) <= 1.0 and
                    (np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) >= -1.0):
                    tc1 = np.arccos(
                                    (np.sin(lat2)-np.sin(lat1)*np.cos(d))/
                                    (np.sin(d)*np.cos(lat1))
                                    )
                else:
                    tc1 = float('nan')
            else:
                if ((np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) <= 1.0 and
                    (np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) >= -1.0):
                    tc1 = 2.*np.pi-np.arccos(
                                             (np.sin(lat2)-np.sin(lat1)*np.cos(d))/
                                             (np.sin(d)*np.cos(lat1))
                                             )
                else:
                    tc1 = float('nan')

        if math.isnan(tc1):
            tc1=math.fmod(np.arctan2(np.sin(lon1-lon2)*np.cos(lat2),
                    np.cos(lat1)*np.sin(lat2)-np.sin(lat1)*np.cos(lat2)*np.cos(lon1-lon2)), 2*np.pi)

    else:

        tc1 = 0.0

    return tc1/radians_per_degree

def intermediate_point(lat1,lon1,lat2,lon2,f):
    '''
    :param lat1: latitude of first point in degrees
    :param lon1: longitude of first point in degrees
    :param lat2: latitude of second point in degrees
    :param lon2: longitude of second point in degrees
    :param f: fraction of distance between the two points
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :type f: float
    :return: return the latitude and longitude of the point a fraction f along the great circle between the first and second points.
    :rtype: float
    '''
#given two lat,lon point find the latitude and longitude that are a fraction f
#of the great circle distance between them
#http://williams.best.vwh.net/avform.htm#Intermediate
    assert f <= 1.0,f

    d = angular_distance(lat1,lon1,lat2,lon2)

    if d != 0.0:
#convert degrees to radians
        radians_per_degree = np.pi / 180.
        lat1 = lat1 * radians_per_degree
        lon1 = lon1 * radians_per_degree
        lat2 = lat2 * radians_per_degree
        lon2 = lon2 * radians_per_degree

        A = np.sin((1-f)*d)/np.sin(d)
        B = np.sin(f*d)/np.sin(d)
        x = A*np.cos(lat1)*np.cos(lon1) +  B*np.cos(lat2)*np.cos(lon2)
        y = A*np.cos(lat1)*np.sin(lon1) +  B*np.cos(lat2)*np.sin(lon2)
        z = A*np.sin(lat1)              +  B*np.sin(lat2)
        lat=np.arctan2(z,np.sqrt(x*x+y*y)) / radians_per_degree
        lon=np.arctan2(y,x) / radians_per_degree
    else:
        lat = lat1
        lon = lon1

    return (lat,lon)


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import math

#################################
#        General helpers        #
#################################

def binary_search(t, x, start, end):
    """Find the index i of t with t[i] <= x <= t[i+1].
    Under the assumption that t is nondecreasing t[j] <= t[j+1]."""
    mid = math.floor((start + end) / 2)
    if ((start - end) == 0) or ((t[mid] <= x) and (x < t[mid+1])):
        return mid
    elif t[mid] <= x:
        return binary_search(t, x, mid + 1, end)
    else:
        return binary_search(t, x, start, mid - 1)

def nielson_metric(Q, p1, p2):
    U = np.asarray(p1)
    V = np.asarray(p2)
    return math.sqrt((U-V).dot(Q.dot(U-V)))

def nielson_matrix(points):
    x = np.asarray(points[0])
    y = np.asarray(points[1])
    n = len(x)
    x_hat = np.sum(x) / n
    y_hat = np.sum(y) / n
    v_x = sum([(x[i]-x_hat)**2 for i in range(0, n)]) / n
    v_y = sum([(y[i]-y_hat)**2 for i in range(0, n)]) / n
    v_xy = sum([(x[i]-x_hat)*(y[i]-y_hat) for i in range(0, n)]) / n
    g = v_x * v_y - v_xy**2
    Q = np.array([[v_y/g, -(v_xy/g)],
                  [-(v_xy/g), v_x/g]])
    return Q


def compute_likelihood_normal(x, mean_val, standard_dev_val):
  """ Computes the log-likelihood values given a observed data sample x, and
  potential mean and variance values for a normal distribution

    Args:
      x (ndarray): 1-D array with all the observed data
      mean_val (scalar): value of mean for which to compute likelihood
      standard_dev_val (scalar): value of variance for which to compute likelihood

    Returns:
      likelihood (scalar): value of likelihood for this combination of means/variances
  """

  # Get probability of each data point (use norm.pdf from scipy stats)
  p_data = norm.pdf(x, mean_val, standard_dev_val)

  # Compute likelihood (sum over the log of the probabilities)
  likelihood = np.sum(np.log(p_data))

  return likelihood

# Set random seed
np.random.seed(0)

# Generate data
true_mean = 5
true_standard_dev = 1
n_samples = 1000
x = np.random.normal(true_mean, true_standard_dev, size = (n_samples,))

# Compute likelihood for a guessed mean/standard dev
guess_mean = 4
guess_standard_dev = .1
likelihood = compute_likelihood_normal(x, guess_mean, guess_standard_dev)
print(likelihood)


import numpy as np

def cosine_similarity(vec1 : np.ndarray, vec2 : np.ndarray):
    """the cosine similarity of two vectors

    using this formula : $$ cosine_similarity(x,y) = \frac{ |x·y| } { |x|·|y| } $$

    Parameters
    ----------

    vec1: shape (1, n_features) or shape(n_features,)

    vec2: shape (1, n_features) or shape(n_features,)

    Returns
    --------

    distances : shape (1, 1)

    """
    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    mult_sum = (vec1 * vec2).sum()
    norm_vec1 = np.linalg.norm(vec1)
    norm_vec2 = np.linalg.norm(vec2)
    eps = 0.000000001
    # print(mult_sum)
    return mult_sum/(norm_vec1 * norm_vec2 + eps)

def cosine_distance(vec1, vec2):
    """the cosine distance of two vectors

    Cosine distance is defined as 1.0 minus the cosine similarity.

    Parameters
    ----------

    vec1 : shape (1, n_features) or shape(n_features,)

    vec2 : shape (1, n_features) or shape(n_features,)

    Returns
    --------

    distances : shape (1, 1)

    """
    return 1.0 - cosine_similarity(vec1, vec2)

def euclidean_distance(vec1, vec2):
    """the euclidean distance of two vectors

    Parameters
    -----------

    vec1: shape (1, n_features) or shape(n_features,)

    vec2: shape (1, n_features) or shape(n_features,)

    Returns
    -------

    distances : shape (1, 1)

    """

    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    return np.linalg.norm(vec1-vec2)

def absolute_distance(vec1, vec2):
    """the euclidean distance of two vectors

    Parameters
    -----------

    vec1: shape (1, n_features) or shape(n_features,)

    vec2: shape (1, n_features) or shape(n_features,)

    Returns
    -------

    distances : shape (1, 1)

    """
    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    return np.abs(vec1-vec2).sum()

def l_p_distance(vec1, vec2,p=1):
    """ calculate the p(default 1) norm of two vectors

    Parameters
    ------------

    vec1 : shape (1, n_features) or shape(n_features,)

    vec2 : shape (1, n_features) or shape(n_features,)

    Returns
    --------

    distances : shape (1, 1)

    """

    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    if (p == np.Inf):
        return np.max(np.abs(vec1-vec2))
    return np.power(np.power(np.abs(vec1-vec2),p).sum(),1/p)


import numpy as np
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt

class LogisticRegression:

    def __init__(self):
        pass

    @staticmethod
    def sigmoid(X):
        return 1 / (1 + np.exp(-X))

    @staticmethod
    def init_params(dims):
        W = np.random.rand(dims, 1)
        b = 0
        return W, b

    def loss_grad(self, X, Y, W, b):
        """Calculate logistic regression cross-entropy loss, weight and bias gradients."""
        # number of samples
        num_samples = X.shape[0]
        # predicted Y probability
        Y_hat = self.sigmoid(np.dot(X, W) + b)
        # loss
        loss = (-1) / num_samples * np.sum(Y * np.log(Y_hat) + (1 - Y) * np.log(1 - Y_hat), axis=None)
        # weight and bias gradients
        dW = np.dot(X.T, (Y_hat - Y)) / num_samples
        db = np.sum(Y_hat - Y) / num_samples
        return loss, dW, db

    def train(self, X, Y, lr, epochs):
        W, b = self.init_params(X.shape[1])
        loss_list = []
        for epoch in range(epochs):
            loss, dW, db = self.loss_grad(X, Y, W, b)
            loss_list.append(loss)
            W -= lr * dW
            b -= lr * db
            if epoch % 100 == 0:
                print(f'Epoch {epoch:4d} loss {loss:.4f}')

        return loss_list, W, b

    def predict_prob(self, X, W, b):
        return self.sigmoid(np.dot(X, W) + b)

    def predict(self, X, W, b):
        prob = self.predict_prob(X, W, b)
        return (prob > 0.5).astype(np.int8)

    @staticmethod
    def load_data():
        X, Y = make_classification(n_samples=1000, n_features=30, n_informative=2, n_redundant=2, n_repeated=10,
                                   n_classes=2, n_clusters_per_class=2, random_state=42)
        Y = Y.reshape(-1, 1)
        return X, Y

if __name__ == '__main__':
    model = LogisticRegression()
    X, Y = model.load_data()
    loss_list, W, b = model.train(X, Y, 1e-3, int(1e5))
    plt.plot(loss_list)
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Dec 22 10:54:50 2020

@author: Jane Hsieh (Hsing-Chuan, Hsieh)

@article:
    Generate random sample of dice (with Faif and Loaded dice used interchangeably)
"""
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

os.getcwd()

#0. ================================================ Functions ===============================================

def InitialState(pi_0=0.5):
    '''
    function to generate initial state S_0 out of {F,L} of the dice with prob-p(S_0=F) = p(F)=pi_0 (0:F; 1:L)
    Input:
        pi_0: defaul= 0.5 which is the initial prob of hiddent state p(S_0 = 0)  [0: fair dice; 1: Loaded dice]
                (pi1 := p(S_0 = 1) = 1-pi_0)
    Output:
        S_0: initial generated hidden state
    '''
    S_0 = np.random.choice(2, 1, p=[pi_0, 1-pi_0])[0] #Default: replace=True
    return S_0

def EmitObs(S_t, Dice, pF, pL):
    '''
    function to generate observation O_t according to the mechanism of hidden state S_t --> Emit Observation
    Input:
        S_t: current hidden state
        Dice: face point of dice, e.g., [1,2,...,6]
        pF: distributions of Fair Dice, say, with point = [1,2,...,6]
        pL: distributions of Loaded Dice, say, with point = [1,2,...,6]
    '''
    if S_t == 0:
        #Fair dice state with prob = pF
        O_t = np.random.choice(Dice, 1, replace=True, p=pF)[0]
    if S_t == 1:
        #Loaded dice state with prob = pL
        O_t = np.random.choice(Dice, 1, replace=True, p=pL)[0]
    return O_t

def TransitState(S_t, p01, p10):
    '''
    function to generate the new hidden state S_t according to the mechanism of previous hidden state S_t
    Input:
        S_t: current hidden state
        p01: Transition prob from 0(F) to 1(L)); p00 =  1- p01
        p10: Transition prob from 1(L) to 0(F); p11 = 1-p10
    '''
    if S_t == 0:
        #Fair dice state with prob = pF
        S_new = np.random.choice(2, 1, replace=True, p=[1-p01, p01])[0]
    if S_t == 1:
        #Loaded dice state with prob = pL
        S_new = np.random.choice(2, 1, replace=True, p=[p10, 1-p10])[0]
    return S_new

def GenerateFLDice(N, p01, p10, pL, pF=[1/6 for i in range(6)], Dice = [i+1 for i in range(6)], pi_0=0.5, seed=None):
    '''
    Generate random sample of dice (with predefined Fair(F) and Loaded(L) dice used interchangeably)

    Input:
        N: sample size
        seed: random seed– None or int
        Dice: face point of the dice; default = [1,2,...,6]
        pF, pL: distributions of Fair vs Loaded Dice (length of pF, pL must be the same with Dice)
        pi_0: initial probability of hidden state S_0 – p(S_0 = 0)  [0: fair dice; 1: Loaded dice]
        p01, p10: Transition prob of hidden state from 0(F) to 1(L)) & from 1(L) to 0(F), respectively
    Output:
        Sample_S: sequence of hidden states {S_t} from t=0,...,N-1
        Sample_O: sequence of observations {O_t} from t=0,...,N-1

    '''
    np.random.seed(seed)
    Sample_S = []
    Sample_O = []

    t=0
    # Generate initial hidden state S_t & the Observed state O_t out of it
    S_t = InitialState(pi_0)
    O_t = EmitObs(S_t, Dice, pF, pL)

    Sample_S.append(S_t)
    Sample_O.append(O_t)

    t += 1

    while t < N:
        #Generate New S_t at next time (i.e., t+1) point according to previous mechanism of S_t;
        #and O_t out of New S_t
        S_t = TransitState(S_t, p01, p10)
        O_t = EmitObs(S_t, Dice, pF, pL)

        Sample_S.append(S_t)
        Sample_O.append(O_t)

        t += 1

    return Sample_S, Sample_O

#1. =============== Set parameters for sampling & those of Hidden Markov Model(HMM) =============================
## Sampling-related parameters
seed = 123
N = 5000  #sample size

## HMM-related parameters
pi_0 = 0.5 # initial prob of hiddent state p(S_0 = 0)  [0: fair dice; 1: Loaded dice]; p(S_0=1) = 1-pi_0
p01 = 0.05  # Transition prob from 0(F) to 1(L)); p00 =  1- p01
p10 = 0.05  # Transition prob from 1(L) to 0(F); p11 = 1-p10

### Emission probs: pF vs pL are distributions of Fair vs Loaded Dice  with point = [1,2,...,6], respectively
### Note: length of Dice, pF, pL must be the same
Dice = [i+1 for i in range(6)]
pF = [1/6 for i in range(6)]
pL=[0.1, 0.1, 0.1, 0.1, 0.1, 0.5]

#2. ============================= Generate sequence of {O_t}, {S_t} from t=0,...,N-1 ====================
## To reproduce the experiment, seed is fixed; o.w. you can set seed = None for fully random gereration
Sample_Hid, Sample_Obs  = GenerateFLDice(N=N, p01=p01, p10=p10, pL=pL, pF=pF, Dice = Dice, pi_0=pi_0, seed=seed)
#or equal to below:
#Sample_Hid, Sample_Obs = GenerateFLDice (N=N, p01=p01, p10=p10, pL=pL, seed=seed, pi_0 = pi_0)

Sample_Hid  = pd.Series(Sample_Hid, name = 'State')
Sample_Obs = pd.Series(Sample_Obs, name = 'Observation')

# Output data
Sample_Obs.to_csv('Sample_Obs.csv')
Sample_Hid.to_csv('Sample_Hid.csv')

## Data Visualization --------------------------------------------------------------------------------------------
data = pd.concat([Sample_Hid,Sample_Obs],axis=1)

def plot_timeseries(axes, x, y, color, xlabel, ylabel, linestyle='-', marker='None'):
    axes.plot(x, y, color=color, linestyle = linestyle, marker = marker, ms = 2)
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel, color=color)
    axes.tick_params('y', colors=color)

n= 200
sample = data[:n]

fig, ax = plt.subplots(figsize = (10,5))
plot_timeseries(ax, sample.index, sample['Observation'],'blue', 'index', 'Dice Observations', linestyle='None', marker='o')
ax2 = ax.twinx()
plot_timeseries(ax2, sample.index, sample['State'],'orange', 'index', 'Hidden States')
ax2.set_yticks([0,1])
ax.set_title('First {} cases of dice sequence'.format(n))
plt.show()

fig.savefig('First {} cases of dice sequence.png'.format(n), dpi = 300)

Hidden Markov Model (HMM)/Dice Simulation Data/Step1. Generate Fair and Loaded Dice Sample/Step1. Generate Fair and Loaded Dice Sample.py


# -*- coding: utf-8 -*-
"""
Esecizio 3
"""

from scipy.fft  import fft
import math
import numpy as np

import matplotlib.pyplot as plt
'''
Il problema fornisce n+1 (xi,yi), i=0,.n n=9 misurazioni del flusso sanguigno
attraverso una sezione dell’arteria carotide durante un battito cardiaco.---
ad istanti di tempo equistanti con step 1/10
Gli istanti appartengono all'intervallo [0,1)

'''

n = 9;
A = 0
B = 1
l = 0
r = 2 * math.pi

step = (B - A) / (n + 1)
x = np.arange(A, B, step)

y = np.array([3.7, 13.5, 5, 4.6, 4.1, 4.5, 4, 3.8, 3.7, 3.7])

if n % 2 == 0:
    m = n // 2
else:
    m = (n - 1) // 2
c = fft(y)
a = np.zeros((m + 2, ))
b = np.zeros((m + 2, ))
a[1 : m + 1] = 2 * c[1 : m + 1].real / (n + 1)
b[1 : m + 1] = -2 * c[1 : m + 1].imag / (n + 1)
a0 = c[0] / (n + 1)
if n % 2 == 0:
    a[m + 1] = 0
    b[m + 1] = 0
else:
    a[m + 1] = c[m + 1] / (n + 1)
    b[m + 1] = 0
pol = a0 * np.ones((100,))
z = np.linspace(A, B, 100)
zm = ((z - A) / (B - A)) * r
for k in range(1, m + 2):
    pol += a[k] * np.cos(k * zm) + b[k] * np.sin(k * zm)
plt.plot(z, pol, "b", x, y, "ro")
plt.legend(["Polinomio", "Pti. interpolati"])
plt.show()


import numpy as np
import matplotlib.pyplot as plt

import sys, getopt

def gauss2D(xy, sigma_x, sigma_y, rho):
    '''

    Returns a 2D Gaussian centered at (0,0)

    Arguments:
    xy = tuple with x and y coordinates (assumes mu=0)
    sigma_x = standard deviation in x-coordinate
    sigma_y = standard deviation in y-coordinate
    rho = correlation in x-y plane. -1 < rho < 1

    '''
    x, y = xy
    return np.exp((np.square(x/sigma_x) + np.square(y/sigma_y) - (2*rho*x*y/(sigma_x*sigma_y)))/(2*(np.square(rho)-1)))/(2*np.pi*sigma_x*sigma_y*np.sqrt(1-np.square(rho)))

def j_0(xy, k):
    '''

    Returns the spherical Bessel function j_0 centered at (0,0)

    Arguments:
    xy = tuple with x and y coordinates (assumes mu=0)
    k = scaling factor; rho=kr

    '''
    x, y = xy
    rho = np.sqrt(np.square(x) + np.square(y)) * k
    if rho==0:
        return 1
    else:
        return np.sin(rho)/rho

def PSF(f=gauss2D, f_arg = [5, 5, 0], pix_size=2, patch_size = 100):
    '''

    Calculates a pixelated PSF given telescope parameters and psf function

    Arguments:
    f = PSF shape; default gaussian.
    f_arg = arguments of the psf function f. Default circular gaussian with sigma 5 arcsec.
    pix_size = size of the pixels in arcsec. This should be smaller than the psf width.
    patch_size = How large in arcsec should the pixelated PSF patch be. Default 100 arcsec

    '''

    num_pix = int(patch_size/pix_size)
    mu = (num_pix-1)/2

    pix_psf = np.array([[f(((x-mu) * pix_size, (y-mu) * pix_size), *f_arg) for x in range(num_pix)] for y in range(num_pix)])
    return pix_psf

def main(argv):
    '''

    When called from the command line, plot and show a PSF

    '''
    pix_size = 2
    f_arg = [5,5,0]
    try:
        opts, args = getopt.getopt(argv,"hp:s:r:",["pixel_size=","sigma=", "rho="])
    except getopt.GetoptError:
        print('usage: construct_psf.py -p <pixel size (arcsec)> -s <1 sigma of PSF (arcsec)> -r <correlation>')
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-h':
            print('usage: construct_psf.py -p <pixel size (arcsec)> -s <1 sigma of PSF (arcsec)> -r <correlation>')
            sys.exit()
        elif opt in ("-p", "--pixel_size"):
            pix_size = float(arg)
        elif opt in ("-s", "--sigma"):
            f_arg[0] = float(arg)
            f_arg[1] = float(arg)
        elif opt in ("-r", "--rho"):
            f_arg[2] = float(arg)
    plt.imshow(PSF(pix_size=pix_size, f_arg=f_arg))
    plt.show()
    plt.close()

if __name__ == '__main__':
    # Demo
    main(sys.argv[1:])


import networkx as nx
from scipy.stats import norm
import numpy as np

#######################################################################################################################
### European Options
### CRR model
### Geometric Random Walk
###
### SOURCES:
## SYLLABUS Derivatives Pricing
# CRR model description, Part 1 : pages 91 -> 94
# Rep Strat CRR implementation, Part 2 pages: 45 -> 58
## Yuxing Yan Python for Finance 2nd edition -> the script for the binomial tree was adapted
#######################################################################################################################

def p_bs(spot_price, strike, riskfree_rate, T, volatility, phi):
    d1 = (np.log(spot_price / strike) + T * (riskfree_rate + 0.5 * volatility * volatility)) / (volatility * np.sqrt(T))
    d2 = (np.log(spot_price / strike) + T * (riskfree_rate - 0.5 * volatility * volatility)) / (volatility * np.sqrt(T))
    return phi * (spot_price * norm.cdf(phi * d1) - strike * np.exp(-riskfree_rate * T) * norm.cdf(phi * d2))

def RepStrat_EU_Option_CRR_GRW(CallOrPut, S, K, rf, T, mu, vol, tree__periods):

    ####################################################################################################################
    #####################  START derivative/model specifics, user input transformation             #####################

    arguments = [CallOrPut, S, K, rf, T, mu, vol, tree__periods] #TC skipped bc can just assume its 0
    for arg in arguments:
        if arg == None:
            return [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]

    if  S < 0 or K < 0  or tree__periods < 1:
        return [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]
    # nbrofsharesLabel, cashLabel, portfolioLabel, optionpriceLabel, intrinsicLabel, stocksLabel,
    #edge_x, edge_y, node_x, node_y, round(u,2), round(d,2), round(probUp,2), round(probDown,2), edge_y_Stock,
    # node_y_Stock, edge_y_Intrinsic, node_y_Intrinsic, edge_y_Optionprice, node_y_Optionprice, edge_y_Portfolio, node_y_Portfolio,
    # edge_y_Cash, node_y_Cash, edge_y_NbrOfShares, node_y_NbrOfShares, tree__periods :27 returns

    if CallOrPut == "Call":
        phi = 1
    elif CallOrPut == "Put":
        phi = -1

    #####################  END derivative/model specifics, user input transformation             #####################
    ####################################################################################################################

    ####################################################################################################################
    #####################                  START accounts initialization                           #####################

    G_Stock, G_Intrinsic, G_Price, G_Portfolio, G_CashAccount, G_Shares = nx.Graph(), nx.Graph(), nx.Graph(), \
                                                                          nx.Graph(), nx.Graph(), nx.Graph()

    stockprices, optionintrinsicvalue, optionprice, labelStocks, labelsIntrinsic, labelsOptionPrice, CashAccount, \
    NbrOfShares, Portfolio, labelPortfolio, labelCash, labelNbrOfShares, EquityAccount, \
    labelEquity = {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}

    ## For the spatial chart
    edge_Y_stock, edge_Y_intrinsic, edge_Y_optionprice, edge_Y_portfolio, edge_Y_cash, edge_Y_nbrofshares = {}, {}, {}, {}, {}, {}
    #####################                      END accounts initialization                         #####################
    ####################################################################################################################

    ####################################################################################################################
    #####################                  START replication strategy                              #####################
    #### dt, up factor, down factor, discounting factor, probUpFactor, probDownFact
    step = T / tree__periods
    u = np.exp(mu * step + vol * np.sqrt(step))
    d = np.exp(mu * step - vol * np.sqrt(step))

    discFact = np.exp(-rf * step)

    probUp = (np.exp(rf * step) - d) / (u - d)
    probDown = 1 - probUp

    stockprices[(0, 1)] = S
    stockprices[(1, 1)] = S * u
    stockprices[(1, 2)] = S * d

    # Create the edges, compute the stock price and option intrinsic value at all nodes
    for i in range(0, tree__periods + 1):
        for j in range(1, i + 2):
            if i < tree__periods:
                # CREATION OF THE EDGES
                G_Stock.add_edge((i, j), (i + 1, j))
                G_Stock.add_edge((i, j), (i + 1, j + 1))

                G_Intrinsic.add_edge((i, j), (i + 1, j))
                G_Intrinsic.add_edge((i, j), (i + 1, j + 1))

                G_Price.add_edge((i, j), (i + 1, j))
                G_Price.add_edge((i, j), (i + 1, j + 1))

                G_Portfolio.add_edge((i, j), (i + 1, j))
                G_Portfolio.add_edge((i, j), (i + 1, j + 1))

                G_CashAccount.add_edge((i, j), (i + 1, j))
                G_CashAccount.add_edge((i, j), (i + 1, j + 1))

                G_Shares.add_edge((i, j), (i + 1, j))
                G_Shares.add_edge((i, j), (i + 1, j + 1))

                # stock price and option intrinsic value at all nodes
                stockprices[(i, j)] = stockprices[(i, j)]
                stockprices[(i + 1, j)] = stockprices[(i, j)] * u
                stockprices[(i + 1, j + 1)] = stockprices[(i, j)] * d

                # labelStocks[(i, j)] = round(stockprices[(i, j)], 5)
                # labelStocks[(i + 1, j)] = round(stockprices[(i + 1, j)], 5)
                # labelStocks[(i + 1, j + 1)] = round(stockprices[(i + 1, j + 1)],5)

                # option intrinsic value at all nodes
                optionintrinsicvalue[(i, j)] = max(phi * (stockprices[(i, j)] - K), 0)
                optionintrinsicvalue[(i + 1, j)] = max(phi * (stockprices[(i + 1, j)] - K), 0)
                optionintrinsicvalue[(i + 1, j + 1)] = max(phi * (stockprices[(i + 1, j + 1)] - K), 0)

                # labelsIntrinsic[(i, j)] = round(optionintrinsicvalue[(i, j)], 5)
                # labelsIntrinsic[(i + 1, j)] = round(optionintrinsicvalue[(i + 1, j)], 5)
                # labelsIntrinsic[(i + 1, j + 1)] = round(optionintrinsicvalue[(i + 1, j + 1)], 5)

                # for the spatial chart
                # edge_Y_stock[(i,j),(i+1,j)] = (labelStocks[(i, j)],labelStocks[(i + 1, j)])
                # edge_Y_stock[(i, j), (i + 1, j + 1)] = (labelStocks[(i, j)],labelStocks[(i + 1, j + 1)])
                # edge_Y_intrinsic[(i, j), (i + 1, j)] = (labelsIntrinsic[(i, j)], labelsIntrinsic[(i + 1, j)])
                # edge_Y_intrinsic[(i, j), (i + 1, j + 1)] = (labelsIntrinsic[(i, j)], labelsIntrinsic[(i + 1, j + 1)])

                edge_Y_stock[(i,j),(i+1,j)] = (stockprices[(i, j)],stockprices[(i + 1, j)])
                edge_Y_stock[(i, j), (i + 1, j + 1)] = (stockprices[(i, j)],stockprices[(i + 1, j + 1)])
                edge_Y_intrinsic[(i, j), (i + 1, j)] = (optionintrinsicvalue[(i, j)], optionintrinsicvalue[(i + 1, j)])
                edge_Y_intrinsic[(i, j), (i + 1, j + 1)] = (optionintrinsicvalue[(i, j)], optionintrinsicvalue[(i + 1, j + 1)])

    # option price at all nodes
    for i in range(tree__periods, -1, -1):
        for j in range(1, i + 2):

            # values at maturity are not discounted given we start there
            if i == tree__periods:
                optionprice[(i, j)] = optionintrinsicvalue[(i, j)]
                # labelsOptionPrice[(i, j)] = round(optionprice[(i, j)], 5)

            # all the others, until price at t=0
            else:
                optionprice[(i, j)] = discFact * (probUp * optionprice[(i + 1, j)] + probDown * optionprice[(i + 1, j + 1)])
                # labelsOptionPrice[(i, j)] = round(optionprice[(i, j)], 5)

                # for the spatial chart
                # edge_Y_optionprice[(i, j), (i + 1, j)] = (labelsOptionPrice[(i, j)], labelsOptionPrice[(i + 1, j)])
                # edge_Y_optionprice[(i, j), (i + 1, j + 1)] = (labelsOptionPrice[(i, j)], labelsOptionPrice[(i + 1, j + 1)])

                edge_Y_optionprice[(i, j), (i + 1, j)] = (optionprice[(i, j)], optionprice[(i + 1, j)])
                edge_Y_optionprice[(i, j), (i + 1, j + 1)] = (optionprice[(i, j)], optionprice[(i + 1, j + 1)])

    Portfolio[(0, 1)] = optionprice[(0, 1)]
    # labelPortfolio[(0, 1)] = str(round(Portfolio[(0, 1)], 5))

    # changer l'algo qui tourne autour des nodes, car trop lent
    for i in range(0, tree__periods + 1):
        for j in range(1, i + 2):
            if i < tree__periods:
                # print(i, j)
                # initialize / After Rebalancing
                NbrOfShares[(i, j)] = (optionprice[(i + 1, j)] - optionprice[(i + 1, j + 1)]) / (stockprices[(i + 1, j)] - stockprices[(i + 1, j + 1)])
                CashAccount[(i, j)] = Portfolio[(i, j)] - NbrOfShares[(i, j)] * stockprices[(i, j)]

                # Labels for the graph
                # if i == 0:
                #     labelCash[(i, j)] = str(round(CashAccount[(i, j)], 5))
                #     labelNbrOfShares[(i, j)] = str(round(NbrOfShares[(i, j)], 5))

                # labelCash[(i, j)] += f",{round(CashAccount[(i, j)], 2)}"
                # labelNbrOfShares[(i, j)] += f",{round(NbrOfShares[(i, j)], 2)}"
                # labelPortfolio[(i, j)] += f",{round(CashAccount[(i, j)] + NbrOfShares[(i, j)] * stockprices[(i, j)], 2)}"

                # labelCash[(i, j)] = f"{round(CashAccount[(i, j)], 5)}"
                # labelNbrOfShares[(i, j)] = f"{round(NbrOfShares[(i, j)], 5)}"
                # labelPortfolio[(i, j)] = f"{round(CashAccount[(i, j)] + NbrOfShares[(i, j)] * stockprices[(i, j)], 5)}"

                # Before Rebalancing
                NbrOfShares[(i + 1, j)] = NbrOfShares[(i, j)]
                CashAccount[(i + 1, j)] = CashAccount[(i, j)] * np.exp(rf * step)
                Portfolio[(i + 1, j)] = CashAccount[(i + 1, j)] + NbrOfShares[(i + 1, j)] * stockprices[(i + 1, j)]

                NbrOfShares[(i + 1, j + 1)] = NbrOfShares[(i, j)]
                CashAccount[(i + 1, j + 1)] = CashAccount[(i, j)] * np.exp(rf * step)
                Portfolio[(i + 1, j + 1)] = CashAccount[(i + 1, j + 1)] + NbrOfShares[(i + 1, j + 1)] * stockprices[(i + 1, j + 1)]

                # Labels for the graph
                # labelCash[(i + 1, j)] = f"{round(CashAccount[(i + 1, j)], 5)}"
                # labelCash[(i + 1, j + 1)] = f"{round(CashAccount[(i + 1, j + 1)], 5)}"

                # labelNbrOfShares[(i + 1, j)] = f"{round(NbrOfShares[(i + 1, j)], 5)}"
                # labelNbrOfShares[(i + 1, j + 1)] = f"{round(NbrOfShares[(i + 1, j + 1)], 5)}"

                # labelPortfolio[(i + 1, j + 1)] = f"{round(Portfolio[(i + 1, j + 1)], 5)}"
                # labelPortfolio[(i + 1, j)] = f"{round(Portfolio[(i + 1, j)], 5)}"

    # re-running the loop now that we have the values for the spatial chart
    for i in range(0, tree__periods + 1):
        for j in range(1, i + 2):
            if i < tree__periods:
                # edge_Y_portfolio[(i, j), (i + 1, j)] = (labelPortfolio[(i, j)], labelPortfolio[(i + 1, j)])
                # edge_Y_portfolio[(i, j), (i + 1, j + 1)] = (labelPortfolio[(i, j)], labelPortfolio[(i + 1, j + 1)])

                # edge_Y_cash[(i, j), (i + 1, j)] = (labelCash[(i, j)], labelCash[(i + 1, j)])
                # edge_Y_cash[(i, j), (i + 1, j + 1)] = (labelCash[(i, j)], labelCash[(i + 1, j + 1)])

                # edge_Y_nbrofshares[(i, j), (i + 1, j)] = (labelNbrOfShares[(i, j)], labelNbrOfShares[(i + 1, j)])
                # edge_Y_nbrofshares[(i, j), (i + 1, j + 1)] = (labelNbrOfShares[(i, j)], labelNbrOfShares[(i + 1, j + 1)])

                edge_Y_portfolio[(i, j), (i + 1, j)] = (Portfolio[(i, j)], Portfolio[(i + 1, j)])
                edge_Y_portfolio[(i, j), (i + 1, j + 1)] = (Portfolio[(i, j)], Portfolio[(i + 1, j + 1)])

                edge_Y_cash[(i, j), (i + 1, j)] = (CashAccount[(i, j)], CashAccount[(i + 1, j)])
                edge_Y_cash[(i, j), (i + 1, j + 1)] = (CashAccount[(i, j)], CashAccount[(i + 1, j + 1)])

                edge_Y_nbrofshares[(i, j), (i + 1, j)] = (NbrOfShares[(i, j)], NbrOfShares[(i + 1, j)])
                edge_Y_nbrofshares[(i, j), (i + 1, j + 1)] = (NbrOfShares[(i, j)], NbrOfShares[(i + 1, j + 1)])

    # print(optionprice[(0,1)])

    # creating the nodes itself in the networkx graph
    pos = {}
    for node in G_Stock.nodes():
        pos[node] = (node[0], tree__periods + 2 + node[0] - 2 * node[1])

    edge_x = []
    edge_y = []
    node_x = []
    node_y = []
    stocksLabel = []
    intrinsicLabel = []
    optionpriceLabel = []
    portfolioLabel = []
    cashLabel = []
    nbrofsharesLabel = []

    for edge in G_Stock.edges():
        x0, y0 = pos[edge[0]]
        x1, y1 = pos[edge[1]]
        edge_x.append(x0)
        edge_x.append(x1)
        edge_x.append(None)
        edge_y.append(y0)
        edge_y.append(y1)
        edge_y.append(None)

    for node in pos:
        x, y = pos[node]
        node_x.append(x)
        node_y.append(y)
        # stocksLabel.append(labelStocks[node])
        # intrinsicLabel.append(labelsIntrinsic[node])
        # optionpriceLabel.append(labelsOptionPrice[node])
        # portfolioLabel.append(labelPortfolio[node])
        # cashLabel.append(labelCash[node])
        # nbrofsharesLabel.append(labelNbrOfShares[node])

        stocksLabel.append(stockprices[node])
        intrinsicLabel.append(optionintrinsicvalue[node])
        optionpriceLabel.append(optionprice[node])
        portfolioLabel.append(Portfolio[node])
        cashLabel.append(CashAccount[node])
        nbrofsharesLabel.append(NbrOfShares[node])

    edge_y_Stock, edge_y_Intrinsic, edge_y_Optionprice, edge_y_Portfolio, edge_y_Cash, edge_y_NbrOfShares = [], [], [], [], [], []
    node_y_Stock, node_y_Intrinsic, node_y_Optionprice, node_y_Portfolio, node_y_Cash, node_y_NbrOfShares = node_y[:], node_y[:], node_y[:], node_y[:], node_y[:], node_y[:]

    # EDGES SPATIAL CHART
    for key, value in edge_Y_stock.items():
        edge_y_Stock.append(value[0])
        edge_y_Stock.append(value[1])
        edge_y_Stock.append(None)
    for key, value in edge_Y_intrinsic.items():
    	edge_y_Intrinsic.append(value[0])
    	edge_y_Intrinsic.append(value[1])
    	edge_y_Intrinsic.append(None)
    for key,value in edge_Y_optionprice.items():
    	edge_y_Optionprice.append(value[0])
    	edge_y_Optionprice.append(value[1])
    	edge_y_Optionprice.append(None)
    for key, value in edge_Y_portfolio.items():
    	edge_y_Portfolio.append(value[0])
    	edge_y_Portfolio.append(value[1])
    	edge_y_Portfolio.append(None)
    for key, value in edge_Y_cash.items():
    	edge_y_Cash.append(value[0])
    	edge_y_Cash.append(value[1])
    	edge_y_Cash.append(None)
    for key, value in edge_Y_nbrofshares.items():
    	edge_y_NbrOfShares.append(value[0])
    	edge_y_NbrOfShares.append(value[1])
    	edge_y_NbrOfShares.append(None)

    # NODES SPATIAL CHART
    for i in range(0, len(node_x)):
    	node_y_Stock[i]       = stocksLabel[i]
    	node_y_Intrinsic[i]   = intrinsicLabel[i]
    	node_y_Optionprice[i] = optionpriceLabel[i]
    	node_y_Portfolio[i]   = portfolioLabel[i]
    	node_y_Cash[i]        = cashLabel[i]
    	node_y_NbrOfShares[i] = nbrofsharesLabel[i]

    return nbrofsharesLabel, cashLabel, portfolioLabel, optionpriceLabel, intrinsicLabel, stocksLabel, edge_x, edge_y, node_x, node_y, round(u,2), round(d,2), round(probUp,2), round(probDown,2), edge_y_Stock, node_y_Stock, edge_y_Intrinsic, node_y_Intrinsic, edge_y_Optionprice, node_y_Optionprice, edge_y_Portfolio, node_y_Portfolio, edge_y_Cash, node_y_Cash, edge_y_NbrOfShares, node_y_NbrOfShares, tree__periods


import numpy as np
import scipy.linalg

#Consider
# 2X1 + X2 +3X3 = 17
# 4X1 - X2 + 3X3 = 31
# -2X1 + 5X2 + 5X3 = -5

A = np.array([[2., 1., 3.],
             [4., -1., 3.],
            [-2., 5., 5.]
])

B = np.array([17., 31., -5.]).T

#We can get the L and U by lu function
(P,L,U) = scipy.linalg.lu(A)

# Or solve by hand
# I Got L and U by row reduction.

L =  np.array([[1,0,0],
             [2,1,0],
             [-1,-2,1]])

U = np.array([[2,1,3],
             [0,-3,-3],
             [0,-0,2]])

# Solve LY = B for Y.
# The solve UX = Y for X.

Y = np.linalg.solve(L, B)

X = np.linalg.solve(U, Y)

# Get the correct solution to compare
solution = np.linalg.solve(A, B)

assert np.allclose(X,solution)


from random import random
from numba import jit

@jit(nopython=True)
def monte_carlo_pi(n):
    counter = 0
    for _ in range(n+1):
        x = random()
        y = random()
        if (x ** 2 + y ** 2) < 1:
            counter += 1
    return 4 * counter / n

if __name__ == "__main__":
    print(monte_carlo_pi(10000))


import numpy as np

def normalize(v):
    '''Normalize a vector based on its 2 norm.'''
    if 0 == np.linalg.norm(v):
        return v
    return v / np.linalg.norm(v)

def rotation_matrix_from_axis_and_angle(u, theta):
    '''Calculate a rotation matrix from an axis and an angle.'''

    x = u[0]
    y = u[1]
    z = u[2]
    s = np.sin(theta)
    c = np.cos(theta)

    return np.array([[c + x**2 * (1 - c), x * y * (1 - c) - z * s, x * z * (1 - c) + y * s],
                     [y * x * (1 - c) + z * s, c + y**2 * (1 - c), y * z * (1 - c) - x * s ],
                     [z * x * (1 - c) - y * s, z * y * (1 - c) + x * s, c + z**2 * (1 - c) ]])

def point_line_distance(p, l_p, l_v):
    '''Calculate the distance between a point and a line defined
    by a point and a direction vector.
    '''
    l_v = normalize(l_v)
    u = p - l_p
    return np.linalg.norm(u - np.dot(u, l_v) * l_v)


#!/usr/bin/env python3

import numpy as np
from scipy.spatial.distance import pdist
from sympy.utilities.iterables import multiset_permutations

def distance(x1, y1, x2, y2):
  """
    The Squared Euclidean Distance is given by the following formula:
    round(math.pow((x1-x2), 2) + math.pow((y1-y2), 2))
  """
  return pdist(np.array([[x1, y1], [x2, y2]]), metric='sqeuclidean')

def distance_for_array(array):
  return distance(*array)

def solution(A, B, C, D):
  """
    Finds the maximum squared distance between two points,
    created assigning the integers A, B, C and D to the coordinates
    (A, B, C and D must be used one time each).
  :return:
    Maximum squared distance for points created with A, B, C and D (used one time each).
  """

  # Creates all possible permutations
  p_array = list(multiset_permutations(np.array([A, B, C, D])))

  # Calculates the distance for each permutation
  distances = np.apply_along_axis(distance_for_array, 1, p_array)

  # Returns the maximum distance
  return np.nanmax(distances)


import math
import numpy as np

def f(x, y): #objective function
    return 2*x + y**2 + math.exp(-x-y+1)

def gradient(x, y): #gradient(f(x_(n)))
    return np.array([-math.exp(-x-y+1) + 2, -math.exp(-x-y+1) + 2*y])

def delta(x, y): #delta = -1 * inverse_hessian_matrix_of_f(x_(n)) * gradient(f(x_(n)))
    return np.array([-(((math.exp(1-x-y)+2)*(2-math.exp(1-x-y))-math.exp(1-x-y)*(2*y-math.exp(1-x-y)))/(2*math.exp(1-x-y))), -y + 1])

def backtracking(x_n, c, t):
    global counter
    counter += 1
    x_n_new = np.round(x_n + t*delta(x_n[0], x_n[1]), 7) #calculate x_(n + 1)
    temp = round((gradient(x_n[0], x_n[1])[0])*((x_n_new-x_n)[0]) + (gradient(x_n[0], x_n[1])[1])*((x_n_new-x_n)[1]), 7)
    if round(f(x_n_new[0], x_n_new[1]), 7) <= round(f(x_n[0], x_n[1]) + c*temp, 7): #condition is true, algorithm stops
        print('Best approximation of x_n is:', x_n_new)
        return x_n_new
    else: #condition is false, algorithm is repeated with x_(n + 1) and t = t/2
        print('Current x_n is:', x_n_new)
        backtracking(x_n_new, c, t/2)

counter = 0
x_n = np.array([3, 3]) #inital point
c = 0.8
t = 1
print('Initial x_n is:', x_n)
backtracking(x_n, c, t)
print('\nThe Newton backtracking algorithm ran', counter, 'times.')
print('Total number of steps:', counter*3)


import numpy as np
from scipy import linalg

from ..base import Preprocessor
from ..statistics import calculate_covariance_matrix

__all__ = [
    "PrincipalComponentAnalysis",
]

class PrincipalComponentAnalysis(Preprocessor):
    """Principal Component Analysis (PCA)

    A statistical procedure that uses an orthogonal transformation to convert
    a set of observations of possibly correlated variables (entities each of
    which takes on various numerical values) into a set of values of linearly
    uncorrelated variables called principal components.

    Parameters:
    -----------
    n_components : integer

    """

    def __init__(self, n_components=2):
        self.n_components = n_components

        self.components = None
        self.explained_variance = []
        self.explained_variance_ratio = []

    def fit(self, X):
        X = super().fit(X)

        # Computing the Covariance Matrix
        S = calculate_covariance_matrix(X)

        # Compute the eigenvalues and eigenvectors
        eigvals, eigvecs = linalg.eig(S)
        eigvals_sum = np.sum(eigvals)

        # Selecting principal components for the new feature subspace
        eig_pairs = [(eigvals[i], eigvecs[:, i]) for i in range(S.shape[0])]
        eig_pairs.sort(key=lambda x: x[0], reverse=True)

        self.components = np.column_stack((
            eig_pairs[i][1] for i in range(self.n_components)
        ))
        self.explained_variance = np.array([
            eig_pairs[i][0] for i in range(self.n_components)
        ])
        self.explained_variance_ratio = np.array([
            v / eigvals_sum for v in self.explained_variance
        ])

        return self

    def transform(self, X):
        X = super().transform(X)

        # Transforming the samples onto the new subspace
        return X @ self.components


from rollerpy.models.curve import Curve, ParametricCurve, NoramlizedCurve
import numpy as np

class HelixCircleParam(Curve, ParametricCurve, NoramlizedCurve):

    def __init__(
        self, A, B, C=1, tmin=0, tmax=np.pi, n=100, initialPosition=[0, 0, 0]
    ):
        # Helix parameters
        self.A = A
        self.B = B
        self.C = C
        self.tmin = tmin
        self.tmax = tmax

        # Initial positions
        self._setInitialPosition(initialPosition)

        # Calculations
        self.t = np.linspace(self.tmin, self.tmax, n)
        self._calcParameters()
        self._calcDerivative()

    def _setInitialPosition(self, initialPosition):

        self.x0 = initialPosition[0]
        self.y0 = initialPosition[1]
        self.z0 = initialPosition[2]

    def _calcParameters(self):
        self.x = self.B*np.cos(self.t) + self.x0
        self.y = self.C*self.t + self.y0 - self.tmin
        self.z = self.A*np.sin(self.t) + self.z0

    def _calcDerivative(self):
        self.dx = -1*self.B*np.sin(self.t)
        self.dy = self.t*0 + self.C
        self.dz = self.A*np.cos(self.t)

class InvHelixCircleParam(HelixCircleParam):

    def _calcParameters(self):
        self.x = self.B*np.cos(self.t) + self.x0
        self.y = -self.C*(self.t - self.tmax) + self.y0 - self.tmin
        self.z = self.A*np.sin(self.t) + self.z0

    def _calcDerivative(self):
        self.dx = -1*self.B*np.sin(self.t)
        self.dy = self.t*0 - self.C
        self.dz = self.A*np.cos(self.t)

class Line(Curve, ParametricCurve, NoramlizedCurve):

    def __init__(self, point1, point2, tmin=0, tmax=1, n=100):

        self._point1 = point1
        self._point2 = point2

        self._v = [
            (point2[0] - point1[0])/tmax,
            (point2[1] - point1[1])/tmax,
            (point2[2] - point1[2])/tmax
        ]

        self.t = np.linspace(tmin, tmax, n)

        self._calcParameters()
        self._calcDerivative()

    def _calcParameters(self):
        self.x = self._point1[0] + self.t*self._v[0]
        self.y = self._point1[1] + self.t*self._v[1]
        self.z = self._point1[2] + self.t*self._v[2]

    def _calcDerivative(self):
        self.dx = 0*self.t + (self._point2[0] - self._point1[0])/self.t[-1]
        self.dy = 0*self.t + (self._point2[1] - self._point1[1])/self.t[-1]
        self.dz = 0*self.t + (self._point2[2] - self._point1[2])/self.t[-1]


from scipy.stats import multivariate_normal as mvn
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')
np.random.seed(1234)

# create data set
n = 1000
_mus = np.array([[0, 4], [-2, 0]])
_sigmas = np.array([[[3, 0], [0, 0.5]], [[1, 0], [0, 2]]])
_pis = np.array([0.6, 0.4])
xs = np.concatenate([np.random.multivariate_normal(mu, sigma, int(pi*n)) for pi, mu, sigma in zip(_pis, _mus, _sigmas)])

# initial guesses for parameters
pis = np.random.random(2)
pis /= pis.sum()
mus = np.random.random((2, 2))
sigmas = np.array([np.eye(2)] * 2)

def plot_result(pis, mus, sigmas, filename):
    intervals = 101
    ys = np.linspace(-7, 7, intervals)
    X, Y = np.meshgrid(ys, ys)
    _ys = np.vstack([X.ravel(), Y.ravel()]).T

    z = np.zeros(len(_ys))
    for pi, mu, sigma in zip(pis, mus, sigmas):
        z += pi*mvn(mu, sigma).pdf(_ys)
    z = z.reshape((intervals, intervals))

    fig = plt.figure(figsize=(10, 10), dpi=80)
    ax = fig.add_subplot(111)
    plt.scatter(xs[:, 0], xs[:, 1], alpha=0.2, linewidths=2.0)
    plt.contour(X, Y, z, linewidths=3.0)
    # plt.contour(X, Y, z, N=10)
    # plt.axis([-8, 6, -6, 8])
    ax.axes.set_aspect('equal')
    # plt.tight_layout()
    ax.set_xlabel("x1")
    ax.set_ylabel("x2")
    ax.xaxis.label.set_size(25)
    ax.yaxis.label.set_size(25)
    ax.tick_params(axis='both', which='both', labelsize=15)

    plt.savefig(filename)

def em_gmm_orig(xs, pis, mus, sigmas, tol=0.001, max_iter=100):
    n, p = xs.shape
    k = len(pis)

    ll_old = 0
    for iter in range(max_iter):
        ll_new = 0

        # E-step
        ws = np.zeros((k, n))
        for j in range(len(mus)):
            for i in range(n):
                ws[j, i] = pis[j] * mvn(mus[j], sigmas[j]).pdf(xs[i])
        ws /= ws.sum(0)

        # M-step
        pis = np.zeros(k)
        for j in range(len(mus)):
            for i in range(n):
                pis[j] += ws[j, i]
        pis /= n

        mus = np.zeros((k, p))
        for j in range(k):
            for i in range(n):
                mus[j] += ws[j, i] * xs[i]
            mus[j] /= ws[j, :].sum()

        sigmas = np.zeros((k, p, p))
        for j in range(k):
            for i in range(n):
                ys = np.reshape(xs[i] - mus[j], (2, 1))
                sigmas[j] += ws[j, i] * np.dot(ys, ys.T)
            sigmas[j] /= ws[j, :].sum()

        # update complete log likelihoood
        ll_new = 0.0
        for i in range(n):
            s = 0
            for j in range(k):
                s += pis[j] * mvn(mus[j], sigmas[j]).pdf(xs[i])
            ll_new += np.log(s)

        if np.abs(ll_new - ll_old) < tol:
            break
        ll_old = ll_new

        plot_result(pis=pis, mus=mus, sigmas=sigmas, filename="another_" + str(iter) + ".png")

    return pis, mus, sigmas

def em_gmm_vect(xs, pis, mus, sigmas, tol=0.001, max_iter=100):
    n, p = xs.shape
    k = len(pis)

    ll_old = 0
    for iter in range(max_iter):
        ll_new = 0

        # E-step
        ws = np.zeros((k, n))
        for j in range(k):
            ws[j, :] = pis[j] * mvn(mus[j], sigmas[j]).pdf(xs)
        ws /= ws.sum(0)

        # M-step
        pis = ws.sum(axis=1)
        pis /= n

        mus = np.dot(ws, xs)
        mus /= ws.sum(1)[:, None]

        sigmas = np.zeros((k, p, p))
        for j in range(k):
            ys = xs - mus[j, :]
            sigmas[j] = (ws[j, :, None, None] * np.matmul(ys[:, :, None], ys[:, None, :])).sum(axis=0)
        sigmas /= ws.sum(axis=1)[:, None, None]

        # update complete log likelihood
        ll_new = 0
        for pi, mu, sigma in zip(pis, mus, sigmas):
            ll_new += pi*mvn(mu, sigma).pdf(xs)
        ll_new = np.log(ll_new).sum()

        if np.abs(ll_new - ll_old) < tol:
            break
        ll_old = ll_new

        plot_result(pis=pis, mus=mus, sigmas=sigmas, filename="em2d_" + str(iter + 1) + ".png")

    return pis, mus, sigmas

plot_result(pis=pis, mus=mus, sigmas=sigmas, filename="em2d_0.png")
pis2, mus2, sigmas2 = em_gmm_vect(xs, pis, mus, sigmas)


import numpy as np
EPS = 1e-8

def si_sdr(reference, estimation):
    """
    Scale-Invariant Signal-to-Distortion Ratio (SI-SDR)

    Args:
        reference: numpy.ndarray, [..., T]
        estimation: numpy.ndarray, [..., T]

    Returns:
        SI-SDR

    [1] SDR– Half- Baked or Well Done?
    http://www.merl.com/publications/docs/TR2019-013.pdf

    >>> np.random.seed(0)
    >>> reference = np.random.randn(100)
    >>> si_sdr(reference, reference)
    inf
    >>> si_sdr(reference, reference * 2)
    inf
    >>> si_sdr(reference, np.flip(reference))
    -25.127672346460717
    >>> si_sdr(reference, reference + np.flip(reference))
    0.481070445785553
    >>> si_sdr(reference, reference + 0.5)
    6.3704606032577304
    >>> si_sdr(reference, reference * 2 + 1)
    6.3704606032577304
    >>> si_sdr([reference, reference], [reference * 2 + 1, reference * 1 + 0.5])
    array([6.3704606, 6.3704606])

    """
    estimation, reference = np.broadcast_arrays(estimation, reference)
    # Zero mean
    estimation = estimation - np.mean(estimation, -1, keepdims=True)
    reference = reference - np.mean(reference, -1, keepdims=True)
    # Reference energy has a better estimate
    reference_energy = np.sum(reference ** 2, axis=-1, keepdims=True) + EPS

    # This is $\alpha$ after Equation (3) in [1].
    optimal_scaling = np.sum(reference * estimation, axis=-1, keepdims=True) \
        / reference_energy

    # This is $e_{\text{target}}$ in Equation (4) in [1].
    projection = optimal_scaling * reference

    # This is $e_{\text{res}}$ in Equation (4) in [1].
    noise = estimation - projection

    ratio = np.sum(projection ** 2, axis=-1) / (np.sum(noise ** 2, axis=-1) +
                                                EPS)
    return np.array(10 * np.log10(ratio + EPS), dtype=np.float64)


import matplotlib.pyplot as plot
import matplotlib.animation as animation
import numpy as np
import seaborn as sns

def getGradient(X, Y, weight, bias = 0):
    weight_loss = X * (X * weight + bias - Y)
    bias_loss = (X * weight + bias - Y)
    weight_gradient = 2 * np.average(weight_loss)
    bias_gradient = 2 * np.average(bias_loss)
    return (weight_gradient, bias_gradient)

def trainByGradientDecent(X, Y):
    weight = 0
    bias = 0
    learningRate = 0.01
    for i in range(20000):
        (weight_gradient, bias_gradient) = getGradient(X, Y, weight, bias)
        weight -= weight_gradient * learningRate
        bias -= bias_gradient * learningRate

        if i % 200 == 0:
            plot.plot(X, Y, "bo")
            image = plot.plot([0, 10], [0 + bias, 10 * weight + bias], linewidth = 1.0, color = "g")
            images.append(image)
            print(i, weight, bias)

        if weight_gradient == 0 and bias_gradient == 0:
            return (weight, bias)

    return (weight, bias)

def prepareImage():
    sns.set()
    plot.xticks(fontsize = 10)
    plot.yticks(fontsize = 10)
    plot.xlabel("RM", fontsize = 10)
    plot.ylabel("MEDV", fontsize = 10)

images = []

data = np.genfromtxt('./dataset/housing.csv', delimiter = ',', skip_header = 1)
X = data[:, 0]
Y = data[:, 3]

print("---------gradient decent----------")
prepareImage()
figure1 = plot.figure()
[weight1, bias1] = trainByGradientDecent(X, Y)

ani1 = animation.ArtistAnimation(figure1, images, interval = 150)
ani1.save("/src/images/regression-gradient-decent.gif", writer = "imagemagick")

plot.plot(X, Y,"bo")
plot.plot([0, 10], [0 + bias1, 10 * weight1 + bias1], linewidth = 1.0, color = "g")
plot.savefig("/src/images/regression-gradient-decent.png")


import numpy as n

def get_rotation_angles(number):
    '''
    Returns a list of theta, phi values, approximately evenly
    distributed on the sphere.

    Uses the generalised spiral points algorithm explained in
    E B Saff and
    A B J Kuijlaars, Distributing many points on a sphere, The
    Mathematical Intelligencer 19(1) 1997.
    '''

    angles = n.zeros((number, 2))
    angles[0] = n.array([n.arccos(-1), 0])

    for k in range(2, number+1):
        h_k = -1. + (2. * (k - 1)) / (number - 1)
        theta = n.arccos(h_k)
        phi = (angles[k-2, 1] + 3.6/n.sqrt(number) *
               1. / n.sqrt(1 - h_k**2)) % (2*n.pi)
        angles[k-1, 0] = theta
        angles[k-1, 1] = phi
    angles[-1, 1] = 0.  # Last phi will be inf otherwise

    return angles

def rotate_vector_to_top(vector):
    '''Returns a rotation matrix that will rotate the given vector to
    point upwards.

    Parameters
    ----------
    vector : ndarray
        The (3D) vector to rotate.
    '''

    theta = n.arccos(vector[2] / n.linalg.norm(vector))
    phi = n.arctan2(vector[1], vector[0])
    return rotate_to_top(theta, phi)

def rotate_to_top(theta, phi):

    '''
    Returns a rotation matrix that will rotate a sphere such that
    the given positions are at the top.

    Parameters
    ----------
    theta : float
        The latitudinal variable.
    phi : float
        The longitudinal variable.
    '''

    chi = -1 * phi
    alpha = theta

    cc = n.cos(chi)
    sc = n.sin(chi)
    ca = n.cos(alpha)
    sa = n.sin(alpha)
    first_rotation = n.array([[cc, -sc, 0],
                              [sc, cc, 0],
                              [0, 0, 1]])
    second_rotation = n.array([[ca, 0, -sa],
                               [0, 1, 0],
                               [sa, 0, ca]])

    return second_rotation.dot(first_rotation)

def rotate_axis_angle(axis, angle):
    axis = n.array(axis) / n.linalg.norm(axis)
    ux, uy, uz = axis

    ct = n.cos(angle)
    st = n.sin(angle)

    arr = n.array(
        [[ct + ux**2*(1-ct), ux*uy*(1-ct) - uz*st, ux*uz*(1-ct) + uy*st],
         [uy*ux*(1-ct) + uz*st, ct + uy**2*(1-ct), uy*ux*(1-ct) - ux*st],
         [uz*ux*(1-ct) - uy*st, uz*uy*(1-ct) + ux*st, ct + uz**2*(1-ct)]])

    return arr