import time
import numpy as np
from scipy.special import eval_legendre as lp
import matplotlib.pyplot as plt
#
def polleg(x, kmax):
    '''
    Purpose:
	    To compute the Legendre polynomials, Pk(x), for all orders k=0:kmax
    In:
        x     d[nx]          abscissas, x = [-1:+1], nx=len(x)
	    kmax  i[1]           maximum order, k = 0,1,2...kmax
    Out:
	    pk    [nx, kmax+1]   Legendre polynomials
    Tree:
	    -
    Comments:
        Q&A:

        Q1 shall we add theory/equations like this (will not be possible for the RT solver) >>>
	    Pk(x) = Qkm(x) for m = 0. The Bonnet recursion formula [1, 2]:
	    (k+1)P{k+1}(x) = (2k+1)*P{k}(x) - k*P{k-1}(x),                           (1)
	    where k = 0:K, P{0}(x) = 1.0, P{1}(x) = x.
	    For fast summation over k, this index changes first.

        Q2: name of the subroutine - why not 'legendre', 'legpol', 'lp', etc...
        A2: Later we will have other polynomials, so i belive it is a good idea to
        start with 'pol' and then specify the type of the polynomial: 'polleg', 'polqkm', 'polrtm'...
    References:
	    1. https://en.wikipedia.org/wiki/Legendre_polynomials
	    2. http://mathworld.wolfram.com/LegendrePolynomial.html
        3. https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.legendre.html
        4. https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.eval_legendre.html
    '''
    nx = len(x)
    nk = kmax+1
    pk = np.zeros((nk, nx))
    if kmax == 0:
        pk[0,:] = 1.0
    else:
        pk[0,:] = 1.0
        pk[1,:] = x
        for ik in range(2,nk):
            pk[ik,:] = (2.0 - 1.0/ik)*x*pk[ik-1,:] - (1.0 - 1.0/ik)*pk[ik-2,:]
    return pk
#==============================================================================
#
if __name__ == "__main__":
    x = np.linspace(-1.0, 1.0, 201)
    nx = len(x)
    fig, axs = plt.subplots(nrows=1, ncols=2)
#
#   Plot a few first orders
    axs[0].plot(x, lp(0, x), 'r', x, lp(1, x), 'g', x, lp(2, x), 'b', x, lp(10, x), 'k')
    axs[0].set_title('python')
    axs[0].grid(True)
#
    pk = polleg(x, 10)
    axs[1].plot(x, pk[0,:], 'r', x, pk[1,:], 'g', x, pk[2,:], 'b', x, pk[10,:], 'k')
    axs[1].set_title('home-cooked')
    axs[1].grid(True)
#
#   Compare for all k=0:kmax & all x
    kmax = 999
    time_start = time.time()
    pk = polleg(x, kmax)
    time_end = time.time()
    print('polleg runtime = %.1f sec.'%(time_end-time_start))
    pk_py = np.zeros_like(pk)
    time_start = time.time()
    for ik in range(kmax+1):
        pk_py[ik,:] = lp(ik, x)
    time_end = time.time()
    print("python's runtime = %.1f sec."%(time_end-time_start))
    print("max absolute difference = %.1e."%np.amax(np.abs(pk - pk_py)))


# -*- coding: utf-8 -*-
"""
Módulo de diferencias finitas en Python 3.x
"""

# Escribire los objetos que me interese importar
from numpy import diag, ones, linspace, array

def laplace1d(f, ua, ub, n, a=0, b=1):
    """
    Esta función resuelve el problema de Laplace 1d
        u''  = f     en [a,b]
        u(a) = ua
        u(b) = ub
    mediante el método de las diferencias finitas
    """
    h = (b-a)/(n+1) # Tamaño de la partición
    # 1. Matriz
    A_h = (1./h**2) * (
            2*diag( ones(n) )
            - diag( ones(n-1), +1 )
            - diag( ones(n-1), -1)
            )
    # 2. Segundo miembro
    f_h = []
    x = linspace(0, 1, num=n+2) # x_0, ..., x_{n-1}
    x_interior = x[1:n+1]

    f_h = f(x_interior) # f_h es el array resultante de aplicar f a cada elmento del array x
    f_h[0]  += ua/h**2
    f_h[-1] += ub/h**2

    # 3. Resolver sistema
    from numpy.linalg import solve
    u_h = solve(A_h, f_h)

    # Concatenamos la solución con los datos en los extremos del intervalo
    u = array( [ua] + list(u_h) + [ub] )

    # Devolvemos la partición x y la solución obtenida
    return x, u

def calor_1d_EulerImp(f, ua, ub, u0, n, k, a=0, b=1):
    """
    Esta función resuelve el problema de Laplace 1d
        u''  = f     en [a,b]
        u(a) = ua
        u(b) = ub
    mediante el método de las diferencias finitas
    """
    h = (b-a)/(n+1) # Tamaño de la partición
    # 1. Matriz
    A_h = (k/h**2) * (
            2*diag( ones(n) )
            - diag( ones(n-1), +1 )
            - diag( ones(n-1), -1)
            )
    # 2. Segundo miembro
    f_h = []
    x = linspace(0, 1, num=n+2) # x_0, ..., x_{n-1}
    x_interior = x[1:n+1]

    f_h = f(x_interior) # f_h es el array resultante de aplicar f a cada elmento del array x
    f_h[0]  += ua/h**2
    f_h[-1] += ub/h**2

    # 3. Resolver sistema
    from numpy.linalg import solve
    u_h = solve(A_h, f_h)

    # Concatenamos la solución con los datos en los extremos del intervalo
    u = array( [ua] + list(u_h) + [ub] )

    # Devolvemos la partición x y la solución obtenida
    return x, u


import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d
from matplotlib import cm
import time

class Poisson(object):
    '''
    This class contain the five kind of solvers to solve the 2D Poisson equation.
    They are the Jacobi method, Gauss-Seidel (GS), Successive over-relaxation (SoR) (w = 1.5), Steepest descent (SD), Conjugate gradient (CG).
    with the convergence condition '||R||2 < 10^(-6)'. For all solvers, we let the vectors 'live in' 2D grids.
    '''
    def __init__(self, N = 100, nx = 20, ny = 20, w=1.5):   #initialize variables
        self.N = N
        self.nx = nx
        self.ny = ny
        self.w = w   #relaxiation factor
        self.phi = np.zeros((self.nx+2,self.ny+2))    #add ghost cell to get 22*22 matrix
        self.rho = np.zeros((self.nx+2,self.ny+2))
        for i in range(1,nx+1):
            for j in range(1,nx+1):
                self.rho[i][j] = np.exp(-0.5*((-10.5+i)**2+(-10.5+j)**2))

    '''Jacobi solver use the potentials calculated from the last step'''
    def Jacobi(self):
        self.ti = time.clock()
        self.iteration = 0
        self.R = np.zeros((self.nx, self.ny))
        self.next_phi = np.zeros((self.nx+2,self.ny+2))
        self.norm = []    #add the 2-norm value after each iteration
        while True:
            self.boundary()    #add boundary condition for each iteration
            for i in range(1,self.nx+1):    #update potential values
                for j in range(1,self.ny+1):
                    self.next_phi[i][j] = 0.25*(self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-self.rho[i][j])
            self.phi = self.next_phi
            for i in range(1, self.nx+1):    #get a compact residual matrix
                for j in range(1, self.ny+1):
                    self.R[i-1][j-1] = (self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-4*self.phi[i][j]-self.rho[i][j])
            self.norm.append(self.cal_norm(self.R))    #calculate 2-norm
            self.iteration +=1
            if (self.norm[self.iteration-1] < 10**(-6) or self.iteration == self.N):
                print ('Jacobi solver iteration: %d'%(self.iteration))
                print ('Jacobi last norm: %e'%self.norm[-1])
                break
        self.te = time.clock()
        print ('Jacobi solver time:\t{0:e}. \t Nr. iteration {1}'.format(self.te - self.ti, self.iteration))

    '''Gauss-Seidel method updates the potential using the ones with one lower index from last step'''
    def GS(self):
        self.ti = time.clock()
        self.iteration = 0
        self.R = np.zeros((self.nx, self.ny))
        self.norm = []
        while True:
            self.boundary()
            for i in range(1,self.nx+1):
                for j in range(1,self.ny+1):
                    self.phi[i][j] = 0.25*(self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-self.rho[i][j])
            for i in range(1, self.nx+1):
                for j in range(1, self.ny+1):
                    self.R[i-1][j-1] = (self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-4*self.phi[i][j]-self.rho[i][j])
            self.norm.append(self.cal_norm(self.R))
            self.iteration +=1
            if (self.norm[self.iteration-1] < 10**(-6) or self.iteration == self.N):
                print ('Gauss-Seidel solver iteration: %d'%(self.iteration))
                print ('Gauss-Seidel solver last norm: %e'%self.norm[-1])
                break
        self.te = time.clock()
        print ('Gauss-Seidel solver time:\t{0:e}. \t Nr. iteration {1}'.format(self.te - self.ti, self.iteration))

    '''Successive over-relaxation (SoR) (w = 1.5), using a relaxation factor w to modify the GS method'''
    def SoR(self):
        self.ti = time.clock()
        self.iteration = 0
        self.R = np.zeros((self.nx, self.ny))
        self.norm = []
        while True:
            self.boundary()
            for i in range(1,self.nx+1):
                for j in range(1,self.ny+1):
                    self.phi[i][j] = (1-self.w)*self.phi[i][j]+0.25*self.w*(self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-self.rho[i][j])
            for i in range(1, self.nx+1):
                for j in range(1, self.ny+1):
                    self.R[i-1][j-1] = (self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-4*self.phi[i][j]-self.rho[i][j])
            self.norm.append(self.cal_norm(self.R))
            self.iteration +=1
            if (self.norm[self.iteration-1] < 10**(-6) or self.iteration == self.N):
                print ('Successive over-relaxation solver iteration: %d'%(self.iteration))
                print ('Successive over-relaxation solver last norm: %e'%self.norm[-1])
                break
        self.te = time.clock()
        print ('Successive over-relaxation solver time:\t{0:e}. \t Nr. iteration {1}'.format(self.te - self.ti, self.iteration))

    '''Steepest descent method, where iteration follows a zig-zag trajectory'''
    def SD(self):
        self.ti = time.clock()
        self.iteration = 0
        self.R = np.zeros((self.nx+2, self.ny+2))    #the residual matrix
        self.R_20 = np.zeros((self.nx, self.ny))    #for a compact 20*20 residual matrix
        self.V = np.zeros((self.nx+2, self.ny+2))    #product of AR

        for i in range(1,self.nx+1):
            for j in range(1,self.ny+1):
                self.R[i][j] = self.rho[i][j]-(self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-4*self.phi[i][j])
        self.norm = []
        while True:
            self.boundary()
            for i in range(1,self.nx+1):
                for j in range(1,self.ny+1):
                    self.V[i][j] = self.R[i-1][j]+self.R[i+1][j]+self.R[i][j-1]+self.R[i][j+1]-4*self.R[i][j]
            self.alpha = np.dot(np.matrix(self.R).getA1(), np.matrix(self.R).getA1())/np.dot(np.matrix(self.R).getA1(), np.matrix(self.V).getA1())   #perform inner product using matrix to vector
            self.phi += self.alpha*self.R
            for i in range(1,self.nx+1):
                for j in range(1,self.ny+1):
                    self.R[i][j] = self.rho[i][j]-(self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-4*self.phi[i][j])

            for i in range(1, self.nx+1):
                for j in range(1, self.ny+1):
                    self.R_20[i-1][j-1] = self.R[i][j]
            self.norm.append(self.cal_norm(self.R_20))
            self.iteration +=1

            if (self.norm[self.iteration-1] < 10**(-6) or self.iteration == self.N):
                print ('steepest descent solver iteration: %d'%(self.iteration))
                print ('steepest descent solver last norm: %e'%self.norm[-1])
                break
        self.te = time.clock()
        print ('steepest descent solver time:\t{0:e}. \t Nr. iteration {1}'.format(self.te - self.ti, self.iteration))

    '''Conjugate gradient, combine the steepest descent direction with the direction used in previous step'''
    def CG(self):
        self.ti = time.clock()
        self.iteration = 0
        self.R = np.zeros((self.nx+2, self.ny+2))
        self.new_R = np.zeros((self.nx+2, self.ny+2))
        self.R_20 = np.zeros((self.nx, self.ny))    #for a 20*20 residual matrix
        self.V = np.zeros((self.nx+2, self.ny+2))    #product of AP (previous direction vector/matrix)

        for i in range(1,self.nx+1):
            for j in range(1,self.ny+1):
                self.R[i][j] = self.rho[i][j]-(self.phi[i-1][j]+self.phi[i+1][j]+self.phi[i][j-1]+self.phi[i][j+1]-4*self.phi[i][j])
        self.P = self.R
        self.norm = []

        while True:
            self.boundary()
            for i in range(1,self.nx+1):
                for j in range(1,self.ny+1):
                    self.V[i][j] = self.P[i-1][j]+self.P[i+1][j]+self.P[i][j-1]+self.P[i][j+1]-4*self.P[i][j]
            self.alpha = np.dot(np.matrix(self.R).getA1(), np.matrix(self.R).getA1())/np.dot(np.matrix(self.P).getA1(), np.matrix(self.V).getA1())
            self.phi += self.alpha*self.P
            self.new_R = self.R-self.alpha*self.V
            self.beta = np.dot(np.matrix(self.new_R).getA1(), np.matrix(self.new_R).getA1())/np.dot(np.matrix(self.R).getA1(), np.matrix(self.R).getA1())
            self.P = self.new_R+self.beta*self.P
            self.R = self.new_R

            for i in range(1, self.nx+1):
                for j in range(1, self.ny+1):
                    self.R_20[i-1][j-1] = self.R[i][j]
            self.norm.append(self.cal_norm(self.R_20))
            self.iteration +=1

            if (self.norm[self.iteration-1] < 10**(-6) or self.iteration == self.N):
            #if (self.iteration == self.N):
                print ('Conjugate gradient solver iteration: %d'%(self.iteration))
                print ('Conjugate gradient solver last norm: %e'%self.norm[-1])
                break
        self.te = time.clock()
        print ('Conjugate gradient solver time:\t{0:e}. \t Nr. iteration {1}'.format(self.te - self.ti, self.iteration))

    '''boundary condition'''
    def boundary(self):
        for j in range(self.nx+2):
            self.phi[0][j] = -self.phi[1][j]
            self.phi[self.nx+1][j] = -self.phi[self.nx][j]
        for i in range(self.nx+2):
            self.phi[i][0] = -self.phi[i][1]
            self.phi[i][self.ny+1] = -self.phi[i][self.ny]

    '''calculate the 2-norm from a 1D vector form'''
    def cal_norm(self, R):
        vector = np.matrix(R).getA1()
        sum = 0
        for i in range(self.nx**2):
            sum += vector[i]**2
        sum = np.sqrt(sum)
        return (sum)

    '''plot the potential surface'''
    def plot3d(self, ax, x1,x2,y1,y2):
        self.x = np.linspace(x1,x2,self.nx+2)
        self.y = np.linspace(y1,y2,self.nx+2)
        self.x, self.y = np.meshgrid(self.x, self.y)
        self.sur = ax.plot_surface(self.x, self.y, self.phi, rstride = 1, cstride = 1, cmap = cm.coolwarm)
        ax.set_xlim(x1,x2)
        ax.set_ylim(y1,y2)
        ax.set_xlabel('X',fontsize = 14)
        ax.set_ylabel('Y',fontsize = 14)
        ax.set_zlabel('Potential (Conjugate gradient)',fontsize = 14)

    '''plot the 2-norm'''
    def plot_norm(self,ax,color,label):
        self.x = np.linspace(1,self.iteration,self.iteration)
        self.y = self.norm
        ax.plot(self.x, self.y, color+'-', markersize = 7, label = label)
        ax.set_yscale('log')
        ax.set_xlabel('Iteration number', fontsize = 14)
        ax.set_ylabel('$||R||_2$', fontsize = 14)

fig = plt.figure(figsize=(15,7))
ax1 = plt.subplot(1,2,1, projection = '3d')
ax2 = plt.subplot(1,2,2)

cmd =Poisson()
cmd.Jacobi()
cmd.plot_norm(ax2,'r|','Jacobi')
cmd =Poisson()
cmd.GS()
cmd.plot_norm(ax2,'g','Gauss-Seidel')
cmd =Poisson()
cmd.SoR()
cmd.plot_norm(ax2,'m','Successive over-relaxation')
cmd =Poisson()
cmd.SD()
cmd.plot_norm(ax2,'c','Steepest descent')
cmd =Poisson()
cmd.CG()
cmd.plot_norm(ax2,'k','Conjugate gradient')
cmd.plot3d(ax1, -10.5, 10.5, -10.5, 10.5)

plt.legend()
plt.savefig('norm-log.pdf')


from numba import jit
import numpy as np
from math import sqrt

@jit(nopython=True)
def matrix_multiply(X, Y):
    """ Matrix multiplication
    Inputs:
      - X: A numpy array of shape (N, M)
      - Y: A numpy array of shape (M, K)
    Output:
      - out: A numpy array of shape (N, K)
    """
    N, M = X.shape
    P = Y.shape[1]
    out = np.zeros((N, P))
    for i in range(N):
        for j in range(P):
            for k in range(M):
                out[i, j] += X[i, k] * Y[k, j]
    return out

@jit(nopython=True)
def matrix_rowmean(X, weights=np.empty(0)):
    """ Calculate mean of each row.
    In case of weights do weighted mean.
    For example, for matrix [[1, 2, 3]] and weights [0, 1, 2]
    weighted mean equals 2.6666 (while ordinary mean equals 2)
    Inputs:
      - X: A numpy array of shape (N, M)
      - weights: A numpy array of shape (M,)
    Output:
      - out: A numpy array of shape (N,)
    """
    N, M = X.shape
    out = np.zeros(N)
    if weights.size <= 0:
        weights = np.ones(M)
    denominator = 0
    for i in range(M):
        denominator += weights[i]
    for i in range(N):
        row_sum = 0
        for j in range(M):
            row_sum += X[i, j] * weights[j]
        out[i] = np.divide(row_sum, denominator)
    return out

@jit(nopython=True)
def matrix_rowstd(X):
    N, M = X.shape
    mean = matrix_rowmean(X)
    out = np.zeros(N)
    for i in range(N):
        var_sum = 0
        for j in range(M):
            var_sum += (X[i, j] - mean[i]) ** 2
        out[i] = sqrt(var_sum / (M))
    return out

@jit(nopython=True)
def matrix_l2_norm(X):
    N, M = X.shape
    out = np.zeros(N)
    for i in range(N):
        row_sum = 0
        for j in range(M):
            row_sum += X[i, j] ** 2
        out[i] = sqrt(row_sum)
    return out.reshape(N, -1)

@jit(nopython=True)
def matrix_bdiv(X, Y):
    N, M = X.shape
    out = np.zeros((N, M))
    for i in range(N):
        for j in range(M):
            out[i, j] = X[i, j] / Y[i, j]
    return out

@jit(nopython=True)
def matrix_b_multiply(X, Y):
    N = X.shape[0]
    K = Y.shape[1]
    out = np.zeros((N, K))
    for i in range(N):
        for j in range(K):
            out[i, j] = X[i, 0] * Y[0, j]
    return out

@jit(nopython=False)
def row_argsort(row):
    # it is allowed @ slack
    return np.argsort(row)

@jit(nopython=True)
def matrix_argsort(X):
    N, M = X.shape
    out = np.zeros((N, M), dtype=np.int64)
    for i in range(N):
        sorted_row = row_argsort(X[i])
        for j in range(M):
            out[i, j] = sorted_row[j]
    return out

@jit(nopython=True)
def cosine_similarity(X, top_n=10, with_mean=True, with_std=True):
    """ Calculate cosine similarity between each pair of row.
    1. In case of with_mean: subtract mean of each row from row
    2. In case of with_std: divide each row on it's std
    3. Select top_n best elements in each row or set other to zero.
    4. Compute cosine similarity between each pair of rows.
    Inputs:
      - X: A numpy array of shape (N, M)
      - top_n: int, number of best elements in each row
      - with_mean: bool, in case of subtracting each row's mean
      - with_std: bool, in case of subtracting each row's std
    Output:
      - out: A numpy array of shape (N, N)

    Example (with top_n=1, with_mean=True, with_std=True):
        X = array([[1, 2], [4, 3]])
        after mean and std transform:
        X = array([[-1.,  1.], [ 1., -1.]])
        after top n choice
        X = array([[0.,  1.], [ 1., 0]])
        cosine similarity:
        X = array([[ 1.,  0.], [ 0.,  1.]])

    """
    N, M = X.shape
    if with_mean:
        mean = matrix_rowmean(X)
        for i in range(N):
            for j in range(M):
                X[i, j] = X[i, j] - mean[i]
    if with_std:
        std = matrix_rowstd(X)
        for i in range(N):
            for j in range(M):
                X[i, j] /= std[i]
    if top_n is not None:
        sorted_indexes = matrix_argsort(X)
        for i in range(N):
            for j in range(M - top_n):
                X[i, sorted_indexes[i, j]] = 0
    l2_norm_matrix = matrix_b_multiply(matrix_l2_norm(X), matrix_l2_norm(X).T)
    return matrix_bdiv(matrix_multiply(X, X.T), l2_norm_matrix)


import numpy as np
import matplotlib.pyplot as plt

# The function we Want to Solve
def f(x):
    return np.tan(x) - x / (1 - x**2)

if __name__ == "__main__":

    # Plot the Function to Visualize the Root
    x = np.linspace(2,4.5,1000)
    y = f(x)

    plt.plot(x, y)
    plt.xlabel('x')
    plt.ylabel('f(x)')
    plt.tight_layout
    plt.show()

    # Derivative of f(x)
    def df(x):
        return 1 / np.cos(x) ** 2 - (1 + x ** 2) / (1 - x ** 2) ** 2

    # Retroactive Newton-Rapshon Relation
    def g(xn):
        return xn - f(xn) / df(xn)

    # Where it Begins
    x0 = 3.0

    # Loop
    for i in range(7):
        x = g(x0)
        x0 = x

    root = x

    print(f'The root of f is: x = {root:.4f}.')
    print(f'The valus of f for the above x is {f(root):.16f}.')


# Compute and plot the optimum risk-return trade-off for 10 assets, restricting ourselves to a long only portfolio

# Generate data for long only portfolio optimization.
import numpy as np
np.random.seed(1)
n = 10								# Number of assets
mu = np.abs(np.random.randn(n, 1))	# Mean returns of n assets
Sigma = np.random.randn(n, n)
Sigma = Sigma.T.dot(Sigma)			# Covarience for n assets

# Long only portfolio optimization.
from cvxpy import *
w = Variable(n)								# Portfolio allocation vector
gamma = Parameter(sign='positive')			# Risk aversion parameter
ret = mu.T*	w
risk = quad_form(w, Sigma)					# w.T * Sigma * w
prob = Problem(Maximize(ret - gamma*risk),	# Restricting to long-only portfolio
			   [sum_entries(w) == 1,
				w >= 0])

# Compute trade-off curve.
SAMPLES = 100
risk_data = np.zeros(SAMPLES)
ret_data = np.zeros(SAMPLES)
gamma_vals = np.logspace(-2, 3, num=SAMPLES)	# SAMPLES evenly spaced values on logspace
for i in range(SAMPLES):
	gamma.value = gamma_vals[i]
	prob.solve()
	risk_data[i] = sqrt(risk).value
	ret_data[i] = ret.value

# Plot long only trade-off curve.
import matplotlib.pyplot as plt

markers_on = [29, 40]
fig = plt.figure()
ax = fig.add_subplot(111)
plt.plot(risk_data, ret_data, 'g-')
for marker in markers_on:
	plt.plot(risk_data[marker], ret_data[marker], 'bs')
	ax.annotate(r"$\gamma = %.2f$" % gamma_vals[marker], xy=(risk_data[marker]+.08, ret_data[marker]-.03))
for i in range(n):
	plt.plot(sqrt(Sigma[i,i]).value, mu[i], 'ro')
plt.xlabel('Standard deviation')
plt.ylabel('Return')
# plt.savefig('tradeoff.png')
plt.show()

# Plot return distributions for two points on the trade-off curve.
import matplotlib.mlab as mlab
plt.figure()
for midx, idx in enumerate(markers_on):
	gamma.value = gamma_vals[idx]
	prob.solve()
	x = np.linspace(-2, 5, 1000)
	plt.plot(x, mlab.normpdf(x, ret.value, risk.value), label=r"$\gamma = %.2f$" % gamma.value)

plt.xlabel('Return')
plt.ylabel('Density')
plt.legend(loc='upper right')
# plt.savefig('return_dist.png')
plt.show()

# The probability of a loss is near 0 for the low risk value and far above 0 for the high risk value.

####################################################################################################

# Portfolio optimization with the leverage limit constraint.
Lmax = Parameter()							# Leverage limit (Leverage = L1 norm of w)
prob = Problem(Maximize(ret - gamma*risk),
			   [sum_entries(w) == 1,
				norm(w, 1) <= Lmax])

# Compute trade-off curve for each leverage limit.
L_vals = [1, 2, 4]
SAMPLES = 100
risk_data = np.zeros((len(L_vals), SAMPLES))
ret_data = np.zeros((len(L_vals), SAMPLES))
gamma_vals = np.logspace(-2, 3, num=SAMPLES)
w_vals = []
for k, L_val in enumerate(L_vals):
	for i in range(SAMPLES):
		Lmax.value = L_val
		gamma.value = gamma_vals[i]
		prob.solve()
		risk_data[k, i] = sqrt(risk).value
		ret_data[k, i] = ret.value

# Plot trade-off curves for each leverage limit.
for idx, L_val in enumerate(L_vals):
	plt.plot(risk_data[idx,:], ret_data[idx,:], label=r"$L^{\max}$ = %d" % L_val)
plt.xlabel('Standard deviation')
plt.ylabel('Return')
plt.legend(loc='lower right')
# plt.savefig('tradeoff_leverage.png')
plt.show()

####################################################################################################

# Portfolio optimization with a leverage limit and a bound on risk.
prob = Problem(Maximize(ret),
			  [sum_entries(w) == 1,
			   norm(w, 1) <= Lmax,
			   risk <= 2])

# Compute solution for different leverage limits.
for k, L_val in enumerate(L_vals):
	Lmax.value = L_val
	prob.solve()
	w_vals.append( w.value )

# Plot bar graph of holdings for different leverage limits. (Negative holdings indicate a short position)
colors = ['b', 'g', 'r']
indices = np.argsort(mu.flatten())
for idx, L_val in enumerate(L_vals):
	 plt.bar(np.arange(1,n+1) + 0.25*idx - 0.375, w_vals[idx][indices], color=colors[idx],
			 label=r"$L^{\max}$ = %d" % L_val, width = 0.25)
plt.ylabel(r"$w_i$", fontsize=16)
plt.xlabel(r"$i$", fontsize=16)
plt.xlim([1-0.375, 10+0.375])
plt.xticks(np.arange(1,n+1))
# plt.savefig('holdings.png')
plt.show()

# Some assets are held in a long position for the low leverage portfolio but in a short position in the higher leverage portfolios.


import numpy as np

# Error_{(m,b)} = \cfrac{1}{N} \displaystyle \sum_{n=1}^N (y_i-(mx_i + b))^2
def compute_error_for_given_points(b, m, points):
  totalError = 0
  for i in range(0, len(points)):
    x = points(i, 0)
    y = points[i, 1]
    totalError += (y - (m * x *b))**2
  N = float(len(points))
  return totalError / N

# Calculate the partial derivative
# For m: \cfrac{∂}{∂m} = \cfrac{2}{N} \displaystyle \sum_{n=1}^N -x_i(y_i-(mx_i + b))
# For b: \cfrac{∂}{∂b} = \cfrac{2}{N} \displaystyle \sum_{n=1}^N -(y_i-(mx_i + b))
def step_gradient(b_current, m_current, points, learning_rate):
  b_gradient = 0
  m_gradient = 0
  N = float(len(points))
  for i in range(0, len(points)):
    x = points[i, 0]
    y = points[i, 1]
    b_gradient += -(2/N) * (y - ((m_current * x) + b_current))
    m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current))
  new_b = b_current - (learning_rate * b_gradient)
  new_m = m_current - (learning_rate * m_gradient)
  return [new_b, new_m]

def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations):
  b = starting_b
  m = starting_m
  for i in range(num_iterations):
    b, m = step_gradient(b, m, np.array(points), learning_rate)
  return [b, m]

def run():
  points = np.genfromtxt('./02-Linear-Regression-With-Gradient-descent/data.csv', delimiter=',')
  # Hyperparameters
  learning_rate = 0.0001
  # y = mx + b (slope formula)
  initial_b = 0
  initial_m = 0
  num_iterations = 1000
  # Infere optimal values for our slope
  [b, m] = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations)
  print(b, m)

if __name__ == '__main__':
  run()


import pandas as pd
import numpy as np

def log_return(list_stock_prices): # Stock prices are estimated through wap values
    return np.log(list_stock_prices).diff()

def realized_volatility(series_log_return):
    return np.sqrt(np.sum(series_log_return**2))

def compute_wap(book_pd):
    wap = (book_pd['bid_price1'] * book_pd['ask_size1'] + book_pd['ask_price1'] * book_pd['bid_size1']) / (book_pd['bid_size1']+ book_pd['ask_size1'])
    return wap

def realized_volatility_per_time_id(file_path, prediction_column_name):
    df_book_data = pd.read_parquet(file_path)

    # Estimate stock price per time point
    df_book_data['wap'] = compute_wap(df_book_data)

    # Compute log return from wap values per time_id
    df_book_data['log_return'] = df_book_data.groupby(['time_id'])['wap'].apply(log_return)
    df_book_data = df_book_data[~df_book_data['log_return'].isnull()]

    # Compute the square root of the sum of log return squared to get realized volatility
    df_realized_vol_per_stock =  pd.DataFrame(df_book_data.groupby(['time_id'])['log_return'].agg(realized_volatility)).reset_index()

    # Formatting
    df_realized_vol_per_stock = df_realized_vol_per_stock.rename(columns = {'log_return':prediction_column_name})
    stock_id = file_path.split('=')[1]
    df_realized_vol_per_stock['row_id'] = df_realized_vol_per_stock['time_id'].apply(lambda x:f'{stock_id}-{x}')

    return df_realized_vol_per_stock[['row_id',prediction_column_name]]

def past_realized_volatility_per_stock(list_file,prediction_column_name):
    df_past_realized = pd.DataFrame()
    for file in list_file:
        df_past_realized = pd.concat([df_past_realized,
                                     realized_volatility_per_time_id(file,prediction_column_name)])
    return df_past_realized


#!/usr/bin/python3

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# FROM https://stackoverflow.com/questions/15979937/trying-to-get-a-diagonal-sum
#
# Different ways to compute a diagonal sum
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Assuming that the matrix is square, this is a standard solution using a loop:
#
# def diagonalsum(m):
#   count = 0
#   for i in xrange(0, len(m)):
#     count += m[i][i]
#   return count
#
# ... Which can be written in a more concise way, by using generator expressions and the sum function:
#
# def diagonalsum(m):
#     return sum(m[i][i] for i in xrange(len(m)))
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Use numpy.trace: http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.trace.html. It returns the sum along diagonals of the array.
#
# import numpy as np
#
# M = np.array([[2,3,1],[1,1,1],[5,6,4]])
#
# print(M.trace())
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# To get the diagonal elements you take the first in the first row, the second in the second row ... the nth in the nth row.
#
# def diagonalsum(x):
#     a = randomlists(x)
#     return sum(row[i] for i, row in enumerate(a))
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# The not so concise alternative that uses a normal for loop reads
#
# def diagonalsum(x):
#     a = randomlists(x)
#     result=0
#     for i, row in enumerate(a):
#         result += row[i]
#     return result
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def diagonalsum(m,*oargs):
  if 'reverse' in oargs:
    return sum(m[len(m)-i-1][i] for i in range(len(m)))
  else:
    return sum(m[i][i] for i in range(len(m)))

m = [
 [10,20,30,4001],
 [100,200,30001,400],
 [1000,200001,3000,4000],
 [1000001,20000,30000,40000]
]

#-- Using a function..

print('\nUsing a function..\n')
diagsumForward = diagonalsum(m)
diagsumReverse = diagonalsum(m,'reverse')
print('Forward:',diagsumForward)
print('Reverse:',diagsumReverse)

#-- Using a lambda..

print('\nUsing a lambda..\n')
for reverse in [False,True]:
  sumLambda = reverse and (lambda s: sum(s[len(s)-i-1][i] for i in range(len(m)))) or (lambda s: sum(s[i][i] for i in range(len(m))))
  newdiagonal = sumLambda(m)
  print('Reverse:',newdiagonal) if reverse else print('Forward:',newdiagonal)


"""
NOTE: The optimal values of m and b can be calculated with less effort than doing a linear regression.
This is just to demonstrate gradient descent.
"""
import numpy

class LinearRegression:
    """
    TODO: docstring
    """
    def __init__(self, points):
        """
        TODO: docstring
        """
        self.initial_b = 0
        self.initial_m = 0
        self.learning_rate = 0.0001
        self.num_iterations = 1000
        self.points = points

    def __call__(self):
        """
        TODO: docstring
        """
        print('Starting gradient descent at b = {0}, m = {1}, error = {2}'.format(
            self.initial_b, self.initial_m, self.compute_error(self.initial_b, self.initial_m)))
        print('Running...')
        [b, m] = self.gradient_descent()
        print('After {0} descent iterations b = {1}, m = {2}, error = {3}'.format(
            self.num_iterations, b, m, self.compute_error(b, m)))
        print('Done.')

    def compute_error(self, b, m):
        """
        Compute Error

        This function computes the error for a line using given points.

        Arguments:
            m: slope
            b: y-intercept
        """
        total_error = 0
        for i in range(len(self.points)):
            x = self.points[i, 0]
            y = self.points[i, 1]
            total_error += (y - (m * x + b)) ** 2
        return total_error / float(len(self.points))

    def gradient_descent(self):
        """
        TODO: docstring
        """
        b = self.initial_b
        m = self.initial_m
        for i in range(self.num_iterations):
            b, m = self.step_gradient(b, m, numpy.array(self.points), self.learning_rate)
        return [b, m]

    def step_gradient(self, b, m, points, learning_rate):
        """
        TODO: docstring
        """
        b_gradient, m_gradient = 0, 0
        N = float(len(points))
        for i in range(len(points)):
            x = points[i, 0]
            y = points[i, 1]
            b_gradient += -(2 / N) * 1 * (y - ((m * x) + b))
            m_gradient += -(2 / N) * x * (y - ((m * x) + b))
        b = b - (learning_rate * b_gradient)
        m = m - (learning_rate * m_gradient)
        return [b, m]

class Main:
    """
    TODO: docstring
    """
    def main():
        """
        TODO: docstring
        """
        points = numpy.genfromtxt('data\\input.csv', delimiter=',')
        linear_regression = LinearRegression(points)
        linear_regression()

if __name__ == '__main__':
    Main.main()


# python3
import numpy

def LCS2(s1, s2, n1, n2):
    """ Finds the length of the longest common subsequence of two strings
    (str, str, int, int) -> (int, 2D-array) """

    # Initializing the matrix
    Matrix = numpy.zeros((n1+1 , n2+1))

    for i in range(1, n1+1):
        for j in range(1, n2+1):
            if s1[i-1] == s2[j-1]:
                Matrix[i][j] = Matrix[i-1][j-1] + 1
            if s1[i-1] != s2[j-1]:
                Matrix[i][j] = max(Matrix[i][j-1], Matrix[i-1][j])

    return (int(Matrix[n1][n2]), Matrix)

def printSubsequence(Matrix, s1, s2, i, j, seq):
    """ Returns the longest common subsequence of two strings
    (2D-array, str, str, int, int, str) -> (str) """
    if i == 0 or j == 0:
        if seq == []: return None
        return ''.join(seq[::-1])
        # If inputs for s1, s2 are numbers uncomment below line.
        # return ' '.join([str(i) for i in seq][::-1])

    if s1[i-1] == s2[j-1]:
        seq.append(s1[i-1])
        return printSubsequence(Matrix, s1, s2, i-1, j-1, seq)

    if Matrix[i-1][j] > Matrix[i][j-1]:
        return printSubsequence(Matrix, s1, s2, i-1, j, seq)
    else:
        return printSubsequence(Matrix, s1, s2, i, j-1, seq)

if __name__ == '__main__':
    n1, s1, n2, s2 = int(input()), input(), int(input()), input()
    # If inputs for s1, s2 are numbers uncomment below line.
    # s1, s2 = [int(i) for i in s1.split()], [int(i) for i in s2.split()]

    LCS_length, Matrix = LCS2(s1, s2, n1, n2)
    sequence = printSubsequence(Matrix, s1, s2, n1, n2, [])
    print('Length of LCS:', LCS_length)
    print('LCS:', sequence)


#!/usr/bin/env python
"""
newton_no_deps.py

Josh Kaplan
contact@joshkaplan.org

A script for using Newton's method for solving roots of
a function. Modify the functions f and df which should be the original
function and it's derivative respectively.

Based on the blog post by Daniel Homola found here:
http://danielhomola.com/2016/02/09/newtons-method-with-10-lines-of-python/
"""
from __future__ import print_function, division
import sys
from sympy import *

MAJ = sys.version_info.major
MIN = sys.version_info.minor

# These paramaters may be tweaked to adjust the performance of the program
# TOL   - the tolerance used to test if the answer is "close enough"
# MAX   - the maximum number of iterations before giving up
# GUESS - the initial guess for the root
TOL = 1e-5
MAX = 1e5
GUESS = -1

# Define your symbols, function, and symbol you're solving for.
#M, E, e, p = symbols('M E e p')
#FUNC = E - e*sin(E) - M
#SOLVE_FOR = E

x, y, z = symbols('x y z')
FUNC = x**2 + 2*x
SOLVE_FOR = E

# This is the function being used.
def f(x):
    return FUNC.subs(SOLVE_FOR, x)

# This is the derivative of the function, f.
def df(x):
    return diff(FUNC).subs(SOLVE_FOR, x)

# This is Newton's Method. This should not be changed.
def newtons_method(f, df, x0, tol):
    x = x0
    for n in xrange(0, int(MAX)):
        x = x - f(x)/df(x)    # This is the heart of Newton's Method
        if abs(0 - f(x)) < tol:  # If within tolerance of zero, stop
            break
    else:
        print('Maximum number of iterations reached.')

    print('Root:       ', x)
    print('f(%.2f) =   ' % x, f(x))
    print('Iterations: ', n)

if __name__ == '__main__':
    newtons_method(f, df, GUESS, TOL)


import numpy as np
np.random.seed(0)

def array_info(array: np.ndarray) -> None:
    print(f"ndim: {array.ndim}")
    print(f"shape: {array.shape}")
    print(f"size: {array.size}")
    print(f"dtype: {array.dtype}")
    print(f"values:\n{array}\n")

print("=1=")
M = np.random.random_integers(low=-10, high=10, size=[5, 5])
print(M)

print("=2=")
lo = -10
hi = 10
N = (hi - lo) * np.random.random_sample(size=[5, 5]) + lo
print(N)

print("=3=")
print(np.sum(M < 0), "elements less than 0 in M")
print(np.sum(N < 0), "elements less than 0 in N")

print("=4=")
M[M < 0] = 0
N[N < 0] = 0
print(M)
print(N)

print("=5=")
O = np.concatenate((M, N), axis=0)
print(O)

print("=6=")
print(np.cos(O * np.pi))


import numpy as np
# numpy bilimsel hesaplamalarda kullanilir
"""
py_l = [0, 1, 2]
np_l = np.array([0,1,2])

print(np_l.dtype, np.array((0,1.2,1,2)).dtype) # dtype ile tip doner
print(np.array(((1,2),(3,4))).ndim) # ndim dizi boyutu
print(np.array((((1,2,3),(4,5,6), (7,8,9)),((1,2,3),(4,5,6), (7,8,9)))))
print(np.array((((1,2,3),(4,5,6), (7,8,9)),((1,2,3),(4,5,6), (7,8,9)))).shape)
print(np.array(((1,2),(3,4))).shape) # matris sayısı, sütun ve satır sayısı

print(np.array([0,1,2,3,4,5]).reshape(2,3)) # matrisi tekrar boyutlandır
print(np.arange(0,10).reshape(2,5)) # arange başlangıç,bitiş,artış miktarı parametrelerine bağlı olarak değer üretir

np_l2 = np_l.copy() # dizi kopylar
print(np_l,np_l2)

print(np.random.random((3,3))) # 3x3 rastgele değerli matris
print(np.random.randint(10, size = 6)) # 0-9 arası 6 rastgele tam sayı
"""
r = np.arange(1,11)
r = r.reshape(2,5)
r2 = np.arange(11,22)
r2 = r.reshape(5,2)
"""
rR1 = r[0] # 1.satır
rR2 = r[1]

rC1 = r[:, 0] # 1.sütun
rC2 = r[:, 1]

print(rR1, rR2, '\n',r[0:2])
print(rC1,rC2)
print(r[0,0]) # 1.eleman (satır, sütun)
"""
"""
r = r[::-1] # ters çevrim
print(r+r[::-1])
z = np.zeros((3,3)) # 3x3 0 olan matris
o = np.ones((3,3)) # 3x3 1 olan matris
z2 = np.zeros((3,3,3)) # 3 adet 3x3 0 olan matris
print(z,'\n- - - -')
print(z2,'\n- - - -')
print(o)
print(np.eye(3)) # 3x3 matris olusturur, 3 adet 1 içerir
"""
"""
print(np.concatenate([r,r2], axis=1)) # axis0 alt alta birleştirir
print(r.max()) # max en büyük değer min en küçük
print(r.sum()) # matristeki tüm değerleri toplar
print(r.sum(axis=1)) # matristeki satırları toplar
print(r.sum(axis=0)) # matristeki sütunları toplar
print(r.mean()) # ortalama
print(r.mean(axis=1)) # satır ortalama
print(r.mean(axis=0)) # sütun ortalama
print(r.var()) # varyans
print(r.var()**(1/2), r.std()) # standart sapma
"""
"""
#print(r+r2) # matrisin elemanları toplanır *-/%** için aynı- +1 veya -5 gibi değerler ile işlem yapılırsa her bir elemanı o değerde işler
print(np.sin(r)) # elemanların sinüsünü verir- cos
print(np.sqrt(r)) # elemanların karekökü
print(np.exp(r)) # elemanları e^ olarak işler
print(np.log(r)) # elemanların 10 tabanında logunu alur,
"""
#print(np.dot(r,r2)) # matrisleri çarpar (2,3) => (3,2) olmalıdır
#print(r.T) # transpoz

#print(r[r > 5]) # dizinin elemanlarının 5 ten büyük mü diye kontrol eder r[] ifadesi kaldırılırsa boolean tipte gösterim olur

#np.save("npStest", r) # veri yazar

#print(np.load('npStest.npy')) # veri okur

#np.genfromtxt('test.csv', delimiter=',', names=True) # csv veri okur


"""
Rekurencyjne obliczanie wyznacznika z macierzy (treść oczywista).
"""
import numpy as np

# RECURSION SOLUTION

def calculate_minor(minor, x, y):
    return [row[: y] + row[y + 1:] for row in (minor[:x] + minor[x + 1:])]

def determinant_of_matrix(array):
    if (len(array) == 2):
        determinant = array[0][0] * array[1][1] - array[0][1] * array[1][0]
        return determinant
    summary = 0
    for i in range(len(array)):
        algebraic_complement = (-1) ** i
        determinant_subset = determinant_of_matrix(
            calculate_minor(array, 0, i))
        summary += (algebraic_complement * array[0][i] * determinant_subset)
    return summary

N = 5
array = [[1, 3, 1, 4],
         [3, 9, 5, 15],
         [0, 2, 1, 1],
         [7, 3, 4, 2]]
print(determinant_of_matrix(array))

# NUMPY SOLUTION

def numpy_determinant(N):
    np_array = np.array([[1, 3, 1, 4],
                         [3, 9, 5, 15],
                         [0, 2, 1, 1],
                         [7, 3, 4, 2]])
    determinant = np.linalg.det(np_array)
    return round(determinant)

N = 5
print(numpy_determinant(N))


import math
import numpy as np

def LinEx(y: int, est: int):
    """
    A function that return the error for a given estimation of the number of calls
    :param y: truth
    :param est: estimation (ŷ in the folowing formula)
    :return: LinEx(y, ŷ) = exp[α(y − ŷ)] − α(y − ŷ) − 1
    """
    alpha = 0.1
    prod = alpha * (y - est)
    return math.exp(prod) - prod - 1

def get_error(ys: np.array, ests: np.array):
    """
    A function that calculates the average error over a set of estimations
    :param ys: a numpy array with shape (n,1)
    :param ests: a numpy array with shape (n,1)
    :return: the average error
    """
    n = ests.shape[0]
    assert n == ys.shape[0]

    sum_errors = 0.
    i = 0
    while i < n:
        error = LinEx(ys[i], int(3*ests[i]))
        # print(error)
        sum_errors += error
        i += 1

    #print(sum_errors/n)

    return sum_errors / n

def test():
    y = np.array([[2], [0], [1], [2], [1]])
    est = np.array([[3], [0], [1], [1], [1]])

    print(y.shape)

    print(get_error(y, est))
    L

if __name__ == "__main__":
    test()


import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt

# Total population, N.
N = 1000
# Initial number of infective and recovered individuals, I0 and R0.
I0, R0 = 1, 0
# Everyone else, S0, is susceptible to infection initially.
S0 = N - I0 - R0
# Contact rate, beta, and mean recovery rate, alpha, (in 1/days).
beta = 0.0002
alpha = 1./10
# A grid of ticks
t = np.linspace(0, 200, 200)

# The SIR model differential equations.
def SIR_eq(y, t, N, beta, alpha):
    S, I, R = y
    Sdot = -beta * S * I
    Idot = beta * S * I - alpha * I
    Rdot = alpha * I
    return Sdot, Idot, Rdot

# Initial conditions vector
y0 = S0, I0, R0
# Integrate the SIR equations over the time grid, t.
ret = odeint(SIR_eq, y0, t, args=(N, beta, alpha))
S, I, R = ret.T

# Plot the data on three separate curves for S(t), I(t) and R(t)
#
s_colour='#ADD694'
i_colour='#F2728C'
r_colour='#67B8C7'
# s_colour='Green'
# i_colour='Red'
# r_colour='Blue'

fig = plt.figure(facecolor='#dddddd')
ax = fig.add_subplot(111, axis_bgcolor='w', axisbelow=True)
ax.plot(t, S, 'b', alpha=0.8, lw=2, label='Susceptible', color=s_colour)
ax.plot(t, I, 'r', alpha=0.8, lw=2, label='Infective', color=i_colour)
ax.plot(t, R, 'g', alpha=0.8, lw=2, label='Removed', color=r_colour)
ax.set_xlabel('Time (Days)')
ax.set_ylabel('Number of Individuals')
ax.set_xlim(0)
ax.set_ylim(0,N*1.1)
ax.yaxis.set_tick_params(length=0)
ax.xaxis.set_tick_params(length=0)
ax.grid(b=True, which='major', c='black', lw=1, ls='-')
legend = ax.legend()
legend.get_frame().set_alpha(0.9)
for spine in ('top', 'right'):
    ax.spines[spine].set_visible(False)
plt.show()


import numpy as np
# from numpy.linalg import lstq as least_square

x = np.array([0.9702, 0.6376, 0.4024, 0.1633])
y = np.array([0.073, 0.415, 0.657, 0.902])

# A = np.vstack([x, np.ones(len(x))]).T
# B = y

# # y = a*x+b
# a, b = least_square(A,B,rcond=None)[0]

# print("y = ", a, " * x + ", b)

# Polynomial Regression
def polyfit(x, y, degree):
    results = {}

    coeffs = np.polyfit(x, y, degree)

     # Polynomial Coefficients
    results['polynomial'] = coeffs.tolist()

    # r-squared
    p = np.poly1d(coeffs)
    # fit values, and mean
    yhat = p(x)                         # or [p(z) for z in x]
    ybar = np.sum(y)/len(y)          # or sum(y)/len(y)
    ssreg = np.sum((yhat-ybar)**2)   # or sum([ (yihat - ybar)**2 for yihat in yhat])
    sstot = np.sum((y - ybar)**2)    # or sum([ (yi - ybar)**2 for yi in y])
    results['determination'] = ssreg / sstot

    return results

print(polyfit(x,y,1))


# compute parameters of the fitting equation and chi square

import numpy as np

def fit(time,data,n):

    '''
     Fit for circular motion with a constant acceleration.
     Input: time is the absolute time, data is the actual data points, n is the order of the fitting function.
     Data does not include uncertainty.
    '''

    # define matrix A, Y
    n += 1
    Y = np.arange(n)
    A = np.zeros((time.shape[0],Y.shape[0]))

    # fill the matrix A
    for i in Y:
        A[:,i] = time ** i

    # construct left and right hand side of the equation
    left = A.T @ A
    right = A.T @ data

    # use numpy to compute parameter set a
    a = np.linalg.solve(left,right)

    return a

def func (time, para):
    '''
    Get the fitting values.
    Input: time is measured time data while para is parameters for the fitting function.
    '''
    n = para.shape[0]

    # the first parameter should be a constant
    f = para[0]

    # fill the function
    for i in np.arange(1,n):
        f = f + para[i] * (time**i)

    return f

def chi2 (data,fit,m):
    '''
    Compute chi square and the variance.
    Input: data is the actual data points and fit is the fitting points;
    and m here means the number of constraints.
    Since the uncertainty of data is always considered as 1 so chi2 is the mean-squared distance.
    '''

    # calculate the chi square
    chi2 = np.sum((data-fit)**2)

    # calculate the variance
    dof = data.shape[0]
    var = chi2 / (dof - m)

    return chi2, var


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

# # Implementation of Gradient Descent Algorithm from scratch

# In[18]:

#Importing all the required libraries
import pandas as pd
import matplotlib.pyplot as plt
import pylab
import seaborn as sns
import numpy as np
import random
from scipy import stats

# In[22]:

#Computing cost function
def cost_function(m,theta0,theta1,x,y):
    return 1/2/m* sum([(theta0 + theta1*np.asarray([x[i]])-y[i])**2 for i in range(m)])      # Here m is the number of training exaples, theta0 and theta1 are the parameters
                                                                                # theta0+theta1*X is our hypotheses function

#Gradient Descent Algorithm function
def gradient_descent(alpha,x,y,precion_rate=0.0001,max_iterations=1000):
    converged=False
    iterations=0
    m=x.shape[0]         #number of training samples

    #initializing theta
    theta0=0
    theta1=0

    #cost function J(theta) for mean squared error
    J = cost_function(m,theta0,theta1,x,y)
    print('J=',J)

    #Iterate loop
    num_iterations=0
    while not converged:
        # for each training we will compute the gradient (d/d_theta J(theta))
        grad0= 1.0/m*sum([(theta0 + theta1*np.asarray([x[i]])-y[i]) for i in range (m)])
        grad1= 1.0/m*sum([(theta0 + theta1*np.asarray([x[i]])-y[i])*np.asarray([x[i]]) for i in range(m)])

        #update the temporary parameters
        temp0= theta0 - alpha*grad0
        temp1= theta1 - alpha*grad1

        #update actual parameters
        theta0=temp0
        theta1=temp1

        #mean squared error
        e = cost_function(m,theta0,theta1,x,y)
        print('J=',e)
        J=e             #update error
        iterations+=1   #update iterations

        if iterations== max_iterations:
            print("Maximum iterations exceeded!")
            converged=True

    return theta0,theta1

if __name__== '__main__':
    df=pd.read_csv('file1.txt',names=['x','y'])                           #the file has been added to the repository
    x=df['x']
    y=df['y']

    alpha=0.01  #learning rate
    precision_rate=0.01     #convergence criteria

    #call gradient descent, and get the values for intercept(=theta0) and slope(=theta1) according to the equation y=m*x+c
    theta0,theta1= gradient_descent(alpha,x,y,precision_rate,max_iterations=1000)
    print('theta0='+str(theta0)+ 'theta1='+str(theta1))

    #plot to evaluate perfromance
    for i in range (x.shape[0]):
        y_predict=theta0+theta1*x

    pylab.plot(x,y,'o')
    pylab.plot(x,y_predict,'k-')
    pylab.show()


# P027

from sympy import sieve, isprime

def main():
    ab = get_possible_ab_values()
    max_consecutive_primes = 0

    for a in range(0, len(ab)):
        for b in range(a, len(ab)):
            c = num_of_consecutive_primes(-ab[a], ab[b])
            if c > max_consecutive_primes:
                max_consecutive_primes = c
                x = (-ab[a], ab[b], max_consecutive_primes)
                product = -ab[a] * ab[b]
    print(x)
    print(product)
    # -59231

def num_of_consecutive_primes(a: int, b: int):
    n = 0
    while isprime(n**2 + a*n + b):
        n += 1
    return n

def get_possible_ab_values():
    ab_values = list(sieve.primerange(1, 1000))
    ab_values.append(1)
    return ab_values

if __name__ == "__main__":
    main()


import logging as log
from math import pi, e, sqrt, cos

from matplotlib import pyplot as p
from scipy import sinc as sinc

def normal(x, mean, variance):

    return pow(e, -(pow((x-mean), 2.0) / 2.0*variance)) / sqrt(2.0*pi*variance)

def raised(x, beta, T):

    return (pi/4.0*T) * sinc(1/2.0*beta)

def raised2(t, beta, T):

    a = 1.0/T
    b = sinc(t/T)
    c = cos((pi * beta * t) / T)
    d = 1 - pow(2.0 * beta * t / T, 2)

    # print "a:%s, b:%s, c:%s, d:%s" % (a,b,c,d)

    return a * b * (c / d)

def frange(x, y, jump):

    while x < y:
        yield x
        x += jump

def raised_cosine(beta=0.01, T=1.0, number_of_values=15):

    log.debug("Generating raised cosine with beta=%s, T=%s" % (beta,T))
    xvals = []
    yvals = []
    step = 2.0 / number_of_values

    for t in frange(-1.0/T,1.0/T, step):

        xvals.append(t)
        # yvals.append(normal(x,0.0,0.00005))
        if t == (T / (2.0*beta)) or t == -(T / (2.0*beta)):
            yvals.append(raised(t,beta,T))
            log.debug( "value is constant %s " % yvals[-1])
        else:
            yvals.append(raised2(t,beta,T))

       # print("Value for %s: %s" % (t,yvals[-1]))

    log.debug("Generated %s raised_cosine values: %s" % (len(yvals), yvals))
    return yvals

def plot_values(vals):

    p.plot(vals[0],vals[1])
    p.show()

#plot_raised_cosine(beta=0)
#plot_raised_cosine(beta=0.01)
#a = raised_cosine(beta=0.1)
#b = raised_cosine(beta=0.01)
#for i in range(0,len(a[0])):
#    print "%s:%s diff %s" % (a[0][i], b[0][i], a[1][i]-b[1][i])
#plot_values(raised_cosine(beta=0.000000000001))
#plot_values(raised_cosine(beta=0.1))


import numpy as np
from test_functions import *
from utilities import *
def local_search(x, y, f, delta, xy_range):

    best = f(x,y)

    current = f(x+delta, y)
    flag = False

    if current < best and x+delta < xy_range[1] and x+delta > xy_range[0]:
        best = current
        xf = x + delta
        yf = y
        flag = True

    current = f(x, y+delta)

    if current < best and y+delta < xy_range[3] and y+delta > xy_range[2]:
        best = current
        xf = x
        yf = y + delta
        flag = True

    current = f(x-delta, y)

    if current < best and x-delta < xy_range[1] and x-delta > xy_range[0]:
        best = current
        xf = x - delta
        yf = y
        flag = True

    current = f(x, y-delta)

    if current < best and y-delta < xy_range[3] and y-delta > xy_range[2]:
        best = current
        xf = x
        yf = y - delta
        flag = True

    if flag == True:
        return xf, yf

    else:
        return x, y

@Counter.count
def hooke_jeeves(f, tol=1e-7, max_iter=100, loc=None, verbose=False, plotting=False):

    trange = get_range(f)

    if loc:
        xy_range = [trange[0] + (np.abs(trange[0])/2),
                    trange[0] - (np.abs(trange[0])/2),
                    trange[1] + (np.abs(trange[1])/2),
                    trange[1] - (np.abs(trange[1])/2)]

        x = loc[0]
        y = loc[1]

    else:
        # get search space range
        xy_range = trange

        # get random initial points
        x = np.random.uniform(xy_range[0], xy_range[1])
        y = np.random.uniform(xy_range[2], xy_range[3])

    # the contraction ratio
    alpha = 0.5

    # set delta as 20% of the size of the search space
    delta = max(np.abs(xy_range[0] - xy_range[1]), np.abs(xy_range[2] - xy_range[3])) * .20

    last_x = x
    last_y = y

    # set initial best minimum
    best = f(x,y)

    for i in range(0, max_iter):
        #print(best)
        current = f(x,y)

        if current < best:
            best = current

        last_x = x
        last_y = y
        x, y = local_search(x, y, f, delta, xy_range)

        # if we dont have a new best point reduce the delta of our pattern search
        if last_x == x and last_y == y:
            delta = delta * alpha

        if delta < tol:
            break

    if verbose:
        return best, (x, y)

    else:
        return best


import numpy as np
import pandas as pd

# 101 Pandas Exercises

# 1 Show Pandas Version
pd.__version__
pd.show_versions(as_json=True)

# 2 Create a series from a list, numpy array, and dictionary
mylist = list("abcdefghijklmnopqrstuvwxyz")
myarr = np.arange(26)
mydict = dict(zip(mylist, myarr))
from_list = pd.Series(mylist)
from_array = pd.Series(myarr)
from_dictionary = pd.Series(mydict)

# 3 Convert series <ser> into a DataFrame, add its index as a column
data = pd.DataFrame(zip(from_dictionary, from_dictionary.index)) # my solution
data = from_dictionary.to_frame().reset_index()
data = from_dictionary.to_frame().reset_index(drop=True) # avoid saving old index

# 4 Combine multiple series into a dataframe
ser1 = pd.Series(list("abcdefghijklmnopqrstuvwxyz"))
ser2 = pd.Series(np.arange(len(ser1)))
data = pd.DataFrame({"ser1": ser1, "ser2": ser2}) # my solution
data = pd.concat([ser1, ser2], axis=0) # append ser1 by ser2
data = pd.concat([ser1, ser2], axis=1) # cbind ser1 by ser2

# 5 Assign a name to the series
ser = pd.Series(list("abcdefghijklmnopqrstuvwxyz"))
ser.name = "alphabets"
ser.head()

# 6 How to get the items of series A not present in series B?
ser1 = pd.Series(np.arange(1, 6))
ser2 = pd.Series(np.arange(4, 9))
ser1[~ser1.isin(ser2)]

# 7 How to get the items not common to both series A and B
ser1 = pd.Series(np.arange(1, 6))
ser2 = pd.Series(np.arange(4, 9))
ser1.index.difference(ser2)

serU = ser1.append(ser2)  # My solution
serU[~(serU.isin(ser1) & serU.isin(ser2))]

ser_u = pd.Series(np.union1d(ser1, ser2))  # Online solution
ser_i = pd.Series(np.intersect1d(ser1, ser2))
ser_u[~ser_u.isin(ser_i)]

# 9 Get the minimum, p25, p50, p75, and max of a numeric series
ser = pd.Series(np.random.normal(10, 5, 25))  # mu, sigma, N
ser.mean()
ser.var() ** (1 / 2)
len(ser)
fivepoint = [ser.quantile(p) for p in np.arange(0.0, 1.1, .25)]  # my solution
fivepoint = np.percentile(ser, q=np.arange(0, 101, 25))  # online solution

# 10 get the frequency counts of unique items of a series
ser = pd.Series(np.take(list("abcdefgh"), np.random.randint(8, size = 30)))
ser.value_counts()

# 10 How to keep only top 2 most frequent values as it is and replace everything
#   else as "Other"
np.random.RandomState(100)
ser = pd.Series(np.random.randint(1, 5, [12]))

top2 = pd.Series(ser.value_counts()[:2].index)  # my solution
ser[~ser.isin(top2)] = "Other"
ser

print("Top 2 Freq:", ser.value_counts())  # Online solution
ser[~ser.isin(ser.value_counts().index[:2])] = "Other"
ser

# 11 Bin a numeric series to 10 groups of equal size
ser = pd.Series(np.random.random(20))
ser_cut1 = pd.cut(ser, bins=10, retbins=False)  # my solution
pd.Series(ser_cut1).value_counts()  # does not ensure equally populated bins
ser_cut2 = pd.qcut(ser, q=np.arange(0, 1.1, .1),
                   labels=['1st', '2nd', '3rd', '4th', '5th', '6th', '7th',
                   '8th', '9th', '10th'])

# 12 Convert a numpy array to a dataframe of given shape
ser = pd.Series(np.random.randint(1, 10, 35))  # convert to shape=(7, 5)
pd.DataFrame(np.array(ser).reshape((7, 5)))  # my solution
pd.DataFrame(ser.values.res hape(7, 5))  # smarter solution
# 26 Get mean of a series grouped by another series
fruit = pd.Series(np.random.choice(["apple", "banana", "carrot"], 10))
weights = pd.Series(np.linspace(1, 10, 10))
print(weights.tolist())
print(fruit.tolist())
weights.groupby(fruit).mean()

# 13 Find the positions of numbers that are multiples of 3 from a series
ser = pd.Series(np.random.randint(1, 10, 7))
ser.index[(ser % 3) == 0]  # My solution: Boolean subsetting
np.argwhere(ser % 3 == 0)

import pandas as pd
import numpy as np

# 14
ser = pd.Series(list('abcdefghijklmnopqrstuvwxyz'))
pos = [0, 4, 8, 14, 20]
ser2 = ser[pos]

# 15
ser1 = pd.Series(range(5))
ser2 = pd.Series(list('abcde'))
ser1.append(ser2)  # my solution
pd.concat([ser1, ser2], axis=0)
pd.concat([ser1, ser2], axis=1)

# 16
ser1 = pd.Series([10, 9, 6, 5, 3, 1, 12, 8, 13])
ser2 = pd.Series([1, 3, 10, 13])
ser1[ser1.isin(ser2)].index  # my solution

[np.where(i == ser1)[0].tolist()[0] for i in ser2]
[pd.Index(ser1).get_loc(i) for i in ser2]

# 17
truth = pd.Series(range(10))
pred = pd.Series(range(10)) + np.random.random(10)
pd.Series((pred - truth) ** 2).mean()  # my solution

np.mean((pred - truth) ** 2)

# 18
ser = pd.Series(["how", "to", "kick", "ass?"])
ser_capitalized = [s[0].upper() + s[1:] for s in ser]  # my solution
# draw back: returns a list, possibly informative index is lost

# better
ser.map(lambda x: x[0].upper() + x[1:])

# even better
ser.map(lambda x: x.title())

# 19
ser = pd.series(["how", "to", "kick", "ass"])
ser.map(lambda x: len(x))

# 20
ser = pd.Series([1, 3, 6, 10, 15, 21, 27, 35])
ser.diff(1).tolist()
ser.diff(2).tolist()
# 27 Compute the euclidean distance between two series
p = pd.Series(np.arange(1, 11))
q = pd.Series([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])
sum((p - q) ** 2) ** (1 / 2)

# 28 find all the local maxima in a series
ser = pd.Series([2, 10, 3, 4, 9, 10, 2, 7, 3])
np.sign(np.diff(ser, 2))
dd = np.diff(np.sign(np.diff(ser)))
np.where(dd == -2)[0] + 1

# 29 Replace missing spaces with least frequence string
my_str = 'dbc deb abed gade'
freqs = {}
for s in my_str:
    freqs[s] = 1 + freqs.get(s, 0)

pd.Series(my_str).str.replace(" ", freqs.index[freqs == freqs.min()][0])


import numpy as np
import time as time

# Exercitiul 1 punctul b)

def subsdesc(A,b,ptol):
    """
    subsdesc rezolva sisteme superior triungiulare
    Input: A = matricea asociata sistemului
           b = vectorul termenilor liberi
           ptol = valoarea numerica in raport cu care comparam numerele egale cu 0
    Output: x = solitia sistemului Ax = b
    """
    #---------------------------------------------
    #Verificare daca matricea este patratica
    dim = np.shape(A)
    m = dim[0] #Numarul de linii
    n = dim[1] #Numarul de coloane
    if m != n:
        print('Matricea nu este patratica')
        x = None
        return

    #----------------------------------------------
    #Verificare daca matricea este superior triunghiulara
    for i in range(m):
        for j in range(i):
            if (np.abs(A[i][j])>ptol):
                print('Matricea nu este superior triunghiulara')
                x = None
                return

    #------------------------------------------------------
    x = np.zeros((n,1))
    x[n-1] = b[n-1]/A[n-1][n-1]
    k = n-2
    while k>-1:
        sum = 0
        for j in range(k+1,n):
            sum = sum + A[k][j] * x[j]
        x[k] = (b[k] - sum)/A[k][k]
        k = k - 1

    return x

# A = np.array([[1.0,2.0,3.0], [0 , 2.0,3.0], [0, 0 ,3.0]])
# b = np.array([[1.0],[2.0],[3.0]])
# x = subsdesc(A,b,10**(-12))
# print('Solutia sistemului Ax = b\n')
# print('A = \n',A)
# print('b = \n',b)
# print('x = \n',x)

def subsasc(A,b,ptol):
    """
    subsdesc rezolva sisteme inferior triungiulare
    Input: A = matricea asociata sistemului
           b = vectorul termenilor liberi
           ptol = valoarea numerica in raport cu care comparam numerele egale cu 0
    Output: x = solitia sistemului Ax = b
    """
    #---------------------------------------------
    #Verificare daca matricea este patratica
    dim = np.shape(A)
    m = dim[0] #Numarul de linii
    n = dim[1] #Numarul de coloane
    if m != n:
        print('Matricea nu este patratica')
        x = None
        return

    #----------------------------------------------
    #Verificare daca matricea este superior triunghiulara
    for j in range(m):
        for i in range(j):
            if (np.abs(A[i][j])>ptol):
                print('Matricea nu este superior triunghiulara')
                x = None
                return

    #------------------------------------------------------
    x = np.zeros((n,1))
    x[0] = b[0]/A[0][0]
    for k in range(1,n):
        sum = 0
        for j in range(k):
            sum = sum + A[k][j] * x[j]
        x[k] = (b[k] - sum)/A[k][k]
    return x

# A = np.array([[1.0,0.0,0.0], [3 , 2.0,0.0], [4., 5. ,3.0]])
# b = np.array([[1.0],[2.0],[3.0]])
# x = subsasc(A,b,10**(-12))
# print('Solutia sistemului Ax = b\n')
# print('A = \n',A)
# print('b = \n',b)
# print('x = \n',x)

def GaussPP(A,b,tol):
    '''
    Parameters
    ----------
    A : float
        Matricea asociata sistemului
    b : TYPE
        DESCRIPTION.
    metoda : TYPE
        DESCRIPTION.
    tol : TYPE
        DESCRIPTION.

    Returns
    -------
    x = solutia sistemului
    '''
    #--------------------------------------
    #Se verifica daca matricea este patratica
    dim = np.shape(A)
    m = dim[0]
    n = dim[1]
    if m!=n:
        print('Matricea nu este patratica.')

    #---------------------------------------
    #Definim matricea extinsa
    Aext = np.concatenate((A,b),axis = 1)
    aux = np.zeros((1,n+1))
    #---------------------------------------
    for k in range(n-1):
        max = np.abs(Aext[k][k])
        p = k
        for j in range(k+1,n):
            if np.abs(Aext[j][k])>max:
                max = np.abs(Aext[j][k])
                p = j
        if p!=k:
            aux [:]= Aext[k]
            Aext[k] = Aext[p]
            Aext[p] = aux
        for l in range(k+1,n):
            Aext[l,k+1:n+1] = Aext[l,k+1:n+1] - Aext[l,k]/Aext[k,k] * Aext[k,k+1:n+1]
            Aext[l][k] = 0
    if (np.abs(Aext[n-1][n-1])<=tol):
        print('Sistemul nu admite solutii')
        x = None
        return
    x = subsdesc(Aext[:,0:n],Aext[:,n],tol)
    return x

def FactLU(A,tol):
    nr_lin = np.shape(A)[0]
    nr_col = np.shape(A)[1]

    L = np.eye(nr_lin, nr_col) # Return a 2-D array with ones on the diagonal and zeros elsewhere
    W = np.arange(0, nr_lin) # array cu elem de la 0 la nr_lin - 1

    U = np.copy(A) # copie a matricei A

    for k in range(0, nr_lin - 1):
        max = abs(U[k][k])
        p = k
        for j in range(k + 1, nr_lin):
            if abs(U[j][k]) > max:
                max = abs(U[j][k])
                p = j

        if abs(U[p][k]) <= tol:
            print('A nu admite factorizarea LU') # Aici in loc de == scriem <=
            return None, None, None

        if p != k:
            U[[p, k],:] = U[[k, p], :]
            W[[p, k]] = W[[k, p]]

            if k > 0:
                L[[p, k], 0 :k] = L[[k, p], 0:k]

        for l in range(k + 1, nr_lin):
            L[l][k] = U[l][k]  / U[k][k]
            U[l][k] = 0
            U[l, k+1:nr_lin]  = U[l, k+1:nr_lin] - L[l][k] * U[k, k+1:nr_lin]

    if abs(U[nr_lin - 1][nr_lin - 1]) <= tol: # Aici in loc de == scriem <=
        print('A nu admite factorizarea LU')
        return None, None, None

    return L, U, W

# print('--PARTEA 1--nn')

# A1 = np.array([[0., 1., 1.], [2., 1., 5.], [4., 2., 1.]])
# b1 = np.array([[3.], [1.], [5.]])
# tol = 10**(-10)

# L1, U1, W1 = FactLU(A1, tol)

# print('--SOLUTIE 1--\n')

# print('Matricea L este \n {}'.format(L1))

# print('Matricea U este \n {}'.format(U1))

# print('Vectorul W este \n {}'.format(W1))

# print('--VERIFICARE 1--n')

# print('Matricea A este  \n {}'.format(A1))

# print('Inmultire LU =   \n {}'.format(L1@U1))

# print('LU = A cu liniile inversate conform Wn')

# bPrim = np.copy(b1)
# for i in range(len(bPrim)):
#     bPrim[i] = b1[W1[i]]

# print('--VECTORUL b DUPA INVERSAREA W  \n {}'.format(bPrim))

# y = subsasc(L1, bPrim, tol) # Aici se foloseste metoda substitutiei ascendente
# x = subsdesc(U1, y, tol)

# print('--X  \n {}'.format(x))
# print('--VERIFICARE (Ax = b)  \n {} = \n {}'.format(A1@x,b1))

print('--PARTEA 2--nn')
print('n--SUBPUNCTUL B--n')

A2 = np.random.rand(100,100)*10

b2 = np.zeros((100, 1))

for i in range(100):
    b2[i] = np.sum(A2[i])

print('Vectorul coloana b este \n {}'.format(b2))

t1 = time.time()

tol = 10**(-10)
L2, U2, W2 = FactLU(A2, tol)

bPrim2 = np.copy(b2)

for i in range(len(b2)):
    bPrim2[i] = b2[W2[i]]

y2 = subsasc(L2, bPrim2, tol)

x2 = subsdesc(U2, y2, tol)

print('Solutia sistemului este \n {}'.format(x2))

print('--PARTEA 3--\n')
print('--SUBPUNCTUL C--\n')

t1 = time.time()

tol = 10**(-10)
L2, U2, W2 = FactLU(A2, tol)
x_old = np.copy(x2)
bPrim3 = np.copy(b2)

for k in range(100):
      for i in range(len(b2)):
        bPrim3[i] = x_old[W2[i]] + 2.
      y3 = subsasc(L2, bPrim3, tol)
      x_new = subsdesc(U2, y3, tol)
      x_old = x_new

#print('Solutia sistemului este \n {}'.format(x_old))
t2 = time.time()

print('Solutia finala prin LU(primele 5 elemente)  \n {}'.format(x_old[0:5,0]))

print('Timp de executie \n' + str(t2 - t1))

t1 = time.time()
x_old_Gauss = np.copy(x2)
for k in range(100):
    x_old_Gauss += 2
    x_new_Gauss = GaussPP(A2, x_old_Gauss, tol)
    x_old_Gauss = x_new_Gauss

t2 = time.time()

print('n---------------------------------------------n')

print('Solutia finala prin GPP (primele 5 elemente)  \n {}'.format(x_old_Gauss[0:5,0]))

print('Timp de executie \n' + str(t2 - t1))

#Pentru verificaare

# t1 = time.time()
# x_old_linalg = np.copy(x2)
# for k in range(100):
#     x_old_linalg +=2
#     x_new_linalg = np.linalg.solve(A2, x_old_linalg)
#     x_old_linalg = x_new_linalg

# t2 = time.time()

# print('n---------------------------------------------n')

# print('Solutia finala prin linalg (primele 5 elemente)  \n {}'.format(x_old_linalg[0:5,0]))

# print('Timp de executie \n' + str(t2 - t1))

Calcul Numeric (CN)/Laborator/Laborator 5/Lab5 - profa.py


from IMLearn.learners import UnivariateGaussian, MultivariateGaussian
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
import plotly.express as px
import pandas as pd
pio.templates.default = "simple_white"

def test_univariate_gaussian():
    # Question 1 - Draw samples and print fitted model
    estimator = UnivariateGaussian()
    mu, var, sample_size = 10, 1, 1000
    samples = np.random.normal(mu, var, sample_size)
    estimator.fit(samples)
    print("({},{})".format(estimator.mu_, estimator.var_))

    # Question 2 - Empirically showing sample mean is consistent
    x = [i for i in range(10, 1001, 10)]
    y = [0.0]*100
    for i in range(100):
        estimator.fit(samples[:(i+1)*10])
        y[i] = abs(estimator.mu_ - mu)
    px.line(x=x, y=y, labels={"x": "Sample size", "y": "Estimator distance from expectation"},
            title="Deviance from actual expectation as a function of sample size (samples taken from a univariate "
                 "Gaussian X~N(10,1))").show()

    # Question 3 - Plotting Empirical PDF of fitted model
    pdf = estimator.pdf(samples)
    df2 = pd.DataFrame({"Observed samples": samples, "PDF": pdf})
    px.scatter(df2, x="Observed samples", y="PDF", title="PDF as a function "
                "of observed samples from a univariate Gaussian X~N(10,1)").show()

def test_multivariate_gaussian():
    # Question 4 - Draw samples and print fitted model
    estimator = MultivariateGaussian()
    mu = np.array([0, 0, 4, 0])
    cov = np.array([1, 0.2, 0, 0.5,
                    0.2, 2, 0, 0,
                    0, 0, 1, 0,
                    0.5, 0, 0, 1]).reshape(4, 4)
    sample_size = 1000
    samples = np.random.multivariate_normal(mu, cov, sample_size)
    estimator.fit(samples)
    print(estimator.mu_)
    print(estimator.cov_)

    # Question 5 - Likelihood evaluation
    numbers = np.linspace(-10, 10, 200)
    data = np.array([[0.0]*200 for i in range(200)])
    for i, f1 in enumerate(numbers):
        for j, f3 in enumerate(numbers):
            mu = np.array([f1, 0, f3, 0])
            data[i][j] = MultivariateGaussian.log_likelihood(mu, cov, samples)

    fig = px.imshow(data, x=numbers, y=numbers)
    fig.layout.update(title="Log-likelihood for mu=(f1, 0, f3, 0) and fixed covariance")
    fig.layout["xaxis1"].update(title="f3")
    fig.layout["yaxis1"].update(title="f1")
    fig.show()

    # Question 6 - Maximum likelihood
    argmax = data.argmax()
    print("f1: {}, f3: {}".format(numbers[argmax // 200], numbers[argmax % 200]))

if __name__ == '__main__':
    np.random.seed(0)
    test_univariate_gaussian()
    test_multivariate_gaussian()


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

def normal_distribution(x, mean, sigma):
    return np.exp(-1*((x-mean)**2)/(2*(sigma**2)))/(math.sqrt(2*np.pi) * sigma)

#-------------------------------------------------------------------------
#   load data from csv
data_train = np.array(pd.read_csv(r'data/train_data.csv',header=None))
data_test = np.array(pd.read_csv(r'data/test_data.csv',header=None))

#-------------------------------------------------------------------------
#   data segmentation based on labels
for i in range(0,data_train.shape[0]):
    if data_train[i][0]==2:
        label1 = i-1
        break
for j in range(i,data_train.shape[0]):
    if data_train[j][0]==3:
        label2 = j-1
        break

sample1=data_train[0:label1,1:data_train.shape[1]]
sample2=data_train[label1+1:label2,1:data_train.shape[1]]
sample3=data_train[label2+1:data_train.shape[0],1:data_train.shape[1]]

#-------------------------------------------------------------------------
#   calculate mean value and standard deviation of each feature
mean1=sample1.mean(axis=0)
sigma1=sample1.std(axis=0,ddof=1)
# np.savetxt("mean1.csv", mean1, delimiter=',')
# np.savetxt("sigma1.csv", sigma1, delimiter=',')

mean2=sample2.mean(axis=0)
sigma2=sample2.std(axis=0,ddof=1)
# np.savetxt("mean2.csv", mean2, delimiter=',')
# np.savetxt("sigma2.csv", sigma2, delimiter=',')

mean3=sample3.mean(axis=0)
sigma3=sample3.std(axis=0,ddof=1)
# np.savetxt("mean3.csv", mean3, delimiter=',')
# np.savetxt("sigma3.csv", sigma3, delimiter=',')

#-------------------------------------------------------------------------
#   calculate posterior probability based on
#   class conditional probability density and prior probability

#   class 1
p=np.ones((data_test.shape[0],3))
for i in range(0,data_test.shape[0]):
    x = data_test[i,1:data_test.shape[1]]
    for j in range(0,data_test.shape[1]-1):
        m=normal_distribution(x[j],mean1[j],sigma1[j])
        p[i,0]=p[i,0]*normal_distribution(x[j],mean1[j],sigma1[j])
    p[i,0]=p[i,0]*(label1+1)/(data_train.shape[0]+1)

#   class 2
for i in range(0,data_test.shape[0]):
    x = data_test[i,1:data_test.shape[1]]
    for j in range(0,data_test.shape[1]-1):
        p[i,1]=p[i,1]*normal_distribution(x[j],mean2[j],sigma2[j])
    p[i,1]=p[i,1]*(label2-label1)/(data_train.shape[0]+1)

#   class 3
for i in range(0,data_test.shape[0]):
    x = data_test[i,1:data_test.shape[1]]
    for j in range(0,data_test.shape[1]-1):
        p[i,2]=p[i,2]*normal_distribution(x[j],mean3[j],sigma3[j])
    p[i,2]=p[i,2]*(data_train.shape[0]-label2)/(data_train.shape[0]+1)

#-------------------------------------------------------------------------
#   predict and normalize
result=np.zeros((data_test.shape[0],4))
count_correct=0
for i in range(0,data_test.shape[0]):
    #   search the maximum probability
    result[i][0]=format(np.argmax(p[i])+1,'.0f')
    #   normalization
    sum_line=np.sum(p[i])
    result[i,1]=format(p[i,0]/sum_line,'.2f')
    result[i,2]=format(p[i,1]/sum_line,'.2f')
    result[i,3]=format(p[i,2]/sum_line,'.2f')
    #   count accuracy
    if result[i][0]==data_test[i][0]:
        count_correct=count_correct+1

print("test_prediction:")
print(result)
print("Classification accuracy:",count_correct/data_test.shape[0])
# np.savetxt("test_prediction.csv", result, delimiter=',')


# -*- coding: utf-8 -*-
"""
@author: David Hu
"""

print('Loaded distribution function G(x)...')

from math import exp, log
from scipy.stats import norm

# The distribution identical to that in the lecture notes
def G(x):
    '''
    It is the CDF
    Restrictions on parameters of the distribution:
        alpha > 0
        0 < gamma < beta
    '''
    alpha = 5.0     # bigger alpha will force k (S) to converge to 1 more quickely
    gamma = 1.0     # bigger gamma will extend the concave part of k(S)
    beta = 100.0    # bigger beta will extend the convex part of k(S)

    return (gamma+beta)/(1+beta) * (1+beta)*exp(-alpha/x) / (gamma+beta*exp(-alpha/x))

def invG(U):
    '''
    It is the inverse function of G(x) and will be used to draw samples from the distribution via inverse transform
    Parameters should be the same as in G(x)
    Restrictions on parameters of the distribution:
        alpha > 0
        0 < gamma < beta
    '''
    alpha = 5.0     # bigger alpha will force k (S) to converge to 1 more quickely
    gamma = 1.0     # bigger gamma will extend the concave part of k(S)
    beta = 100.0    # bigger beta will extend the convex part of k(S)

    return -alpha / log(gamma*U/(gamma+beta-U*beta))

"""
# An example of another distribution - normal distribution
def G(x):
    return norm.cdf(x, loc = 3.0, scale = 1.0) # CDF of normal distribution, loc is mean, scale is standard deviation

def invG(U):
    return norm.ppf(U, loc = 3.0, scale = 1.0) # inverse of CDF of normal distribution, loc is mean, scale is standard deviation

"""


import sys
import numpy as np

def min_max_scalar_product(arr1, arr2):
    """
    Calculates the minimum and the maximum scalar product
    between two given 1D numpy arrays.
    """

    # Dot product between: first array ascending, second array descending
    min_sp = np.dot(np.sort(arr1), np.sort(arr2)[::-1])

    # Dot product between: first array ascending, second array ascending
    max_sp = np.dot(np.sort(arr1), np.sort(arr2))

    return min_sp, max_sp

if __name__ == "__main__":
    if len(sys.argv) != 3:
        raise ValueError('Two input arrays needed!')

    # Split the input strings and turn them into numpy arrays of dtype float
    arr1 = np.asarray(sys.argv[1].split(','), dtype=np.float)
    arr2 = np.asarray(sys.argv[2].split(','), dtype=np.float)

    # Print input arrays
    print('Input array 1: ', arr1)
    print('Input array 2: ', arr2)

    # Calculate scalar product
    min_sp, max_sp = min_max_scalar_product(arr1, arr2)

    # Print result
    print('Minimum scalar product: %.2f' % min_sp)
    print('Maximum scalar product: %.2f' % max_sp)


import numpy as np

def slowDFT(t):
    N = t.shape[0]
    omega = np.zeros(N, dtype=np.complex_)
    for n in range(N):
        omega[n] = np.sum(t[m] * np.exp(-1j*2*np.pi*m*n/N) for m in range(N))
    return omega

def slowInvDFT(omega):
    N = omega.shape[0]
    t = np.zeros(N, dtype=np.complex_)
    for m in range(N):
        t[m] = np.sum(omega[n] * np.exp(1j*2*np.pi*m*n/N) for n in range(N)) / N
    return t


import numpy as np
from matplotlib import pyplot as plt
from hw3.libs.common.dataset import ImgDataset

class PCA(ImgDataset):
    def __init__(self, img_size, n_dataset, n_comp, img_dataset_path=None, input_index=None, man_dataset=None):
        super().__init__(img_size, n_dataset, img_dataset_path, man_dataset=man_dataset)
        self.n_comp = n_comp

        self.input_index = None
        if input_index is not None:
            self.input_index = input_index

    """
    Step for PCA:
    1. Centering data
    2. Computing correlation matrix
    3. Eigendecomposition of correlation matrix
    4. Computing the projection matrix
    5. Computing principal components
    6. Reconstructing the data
    7. Computing reconstruction error
    8. Printing the graph
    """
    def run(self, plot=True):
        centered_data, mean_vector = self.center_data()
        corr = self.correlation_matrix(centered_data)
        eigvals, eigvecs = self.eigen_decomposition(corr)

        proj_matrix = self.compute_projection_matrix(eigvals, eigvecs, self.n_comp)
        princ_comps = self.compute_principal_components(centered_data, proj_matrix)
        reconstructed_data = self.reconstruct_data(princ_comps, proj_matrix, mean_vector)

        self.error, self.err_var = self.compute_rec_error(self.data, reconstructed_data)
        print(" >>> Error = ", self.error)
        print("\n >>> err_var = ", self.err_var)

        if plot:
            self.plot_sample_result(self.data, reconstructed_data)

    def run_multiple(self):
        centered_data, mean_vector = self.center_data()
        corr = self.correlation_matrix(centered_data)
        eigvals, eigvecs = self.eigen_decomposition(corr)

        rec_data_list = []
        for i in range(0, len(self.n_comp)):
            n_comp = self.n_comp[i]
            proj_matrix = self.compute_projection_matrix(eigvals, eigvecs, n_comp)
            princ_comps = self.compute_principal_components(centered_data, proj_matrix)
            reconstructed_data = self.reconstruct_data(princ_comps, proj_matrix, mean_vector)
            rec_data_list.append(reconstructed_data)

            self.error, self.err_var = self.compute_rec_error(self.data, reconstructed_data)
            print(" >>> Error [%s] = %s" % (i, self.error))
            print("\n >>> err_var[%s] = %s" % (i, self.err_var))

        self.plot_sample_multi_result(self.data, rec_data_list)
        # if self.input_index is None:
        #     self.plot_sample_multi_result(self.data, rec_data_list)
        # else:
        #     self.plot_sample_multi_result_multi_index(self.data, rec_data_list)

    def get_error(self):
        return self.error

    def center_data(self):
        print('\nCentering data...')
        mean_vector = np.mean(self.data, axis=1).reshape(self.data.shape[0], 1)
        centered_data = self.data - mean_vector

        # if (globalv.print_results):
        #     print('Mean vector: \n', mean_vector)
        #     print('Centered data: \n', centered_data)
        return centered_data, mean_vector

    def correlation_matrix(self, centered_data):
        print('\nComputing correlation matrix...')
        # D = globalv.D
        # N = globalv.N

        correlation_matrix = np.zeros((self.D, self.D))
        for i in range(self.N):
            correlation_matrix += (centered_data[:, i].reshape(self.D, 1)).dot(centered_data[:, i].reshape(self.D, 1).T)
        correlation_matrix = correlation_matrix / self.N

        # if (globalv.print_results):
        #     print(correlation_matrix)

        return correlation_matrix

    def eigen_decomposition(self, corr):
        print('\nEigendecomposition of correlation matrix...')
        # D = globalv.D
        # N = globalv.N

        eigvals, eigvecs = np.linalg.eig(corr)

        for i in range(len(eigvals)):
            eigv = eigvecs[:, i].reshape(1, self.D).T
            np.testing.assert_array_almost_equal(corr.dot(eigv), eigvals[i] * eigv, decimal=6, err_msg='', verbose=True)

        # if (globalv.print_results):
        #     print('Eigenvalues: \n', eigvals)
        #     print('Eigenvectors: \n', eigvecs)

        return eigvals, eigvecs

    def compute_projection_matrix(self, eigvals, eigvecs, r):
        print('\nComputing the projection matrix...')
        # D = globalv.D
        # N = globalv.N

        # List of (eigenvalue, eigenvector) tuples
        eig_pairs = [(np.abs(eigvals[i]), eigvecs[:, i]) for i in range(len(eigvals))]

        # Sort the (eigenvalue, eigenvector) tuples from high to low (reverse=True)
        eig_pairs.sort(key=lambda tup: tup[0], reverse=True)

        # Projection matrix
        proj_matrix = np.zeros((self.D, r))
        for i in range(r):
            proj_matrix[:, i] = eig_pairs[i][1].reshape(1, self.D)

        # if (globalv.print_results):
        #     print('Projection matrix: \n', proj_matrix)

        return proj_matrix

    def compute_principal_components(self, centered_data, proj_matrix):
        print('\nComputing principal components...')
        p = (proj_matrix.T).dot(centered_data)

        # if (globalv.print_results):
        #     print('Principal components: \n', p)

        return p

    def reconstruct_data(self, princ_comps, proj_matrix, mean_vector):
        print('\nReconstructing the data...')
        self.reconstructed_data = (proj_matrix).dot(princ_comps) + mean_vector
        # reconstructed_data = proj_matrix.dot(princ_comps) + mean_vector # kudu ngene kan?

        # if (globalv.print_results):
        #     print('Reconstructed data: \n', reconstructed_data)

        return self.reconstructed_data

    def compute_rec_error(self, data, rec_data):
        print('\nComputing reconstruction error...')
        # D = globalv.D
        # N = globalv.N

        error = data - rec_data
        error_var = 0
        for i in range(self.N):
            error_var += np.linalg.norm(error[:, i]) ** 2
        error_var = error_var / self.N

        # if (globalv.print_results):
        #     print('Error: \n', error)
        # print('Error variance: ', error_var)

        return error, error_var

    def get_reconstructed_data(self):
        return self.reconstructed_data

    def get_original_data(self):
        return self.data

    def plot_sample_result(self, data, rec_data):
        # print(" ** DISINI ..")
        # print(data.shape)
        # print(data)
        idx = 1
        # print(" ****** ", type(data[:, idx]))
        ori_data = data[:, idx].reshape(self.img_size, self.img_size)
        rec_data = rec_data[:, idx].reshape(self.img_size, self.img_size)

        # Default, ONLY index = 0
        # ori_data = data[:, 0].reshape(self.img_size, self.img_size)
        # rec_data = rec_data[:, 0].reshape(self.img_size, self.img_size)

        # print(len(ori_data))
        # print(len(ori_data[0]))
        # print(ori_data)

        f, (ax1, ax2) = plt.subplots(1, 2)
        f.suptitle('PCA comparison with #Dim = %s' % self.n_comp)
        ax1.imshow(ori_data, cmap='gray', interpolation='none')
        ax1.set_title('Original Image')
        ax2.imshow(rec_data, cmap='gray', interpolation='none')
        ax2.set_title('Compressed Image')
        plt.show()

    def plot_sample_multi_result(self, data, rec_data_list):
        ori_data = data[:, 0].reshape(self.img_size, self.img_size)

        # fig4, axarr = plt.subplots(3,2,figsize=(8,8))
        fig4, axarr = plt.subplots(4, 2, figsize=(9, 9))
        for i in range(0, len(self.n_comp)):
            tmp_rec_data = rec_data_list[i][:, 0].reshape(self.img_size, self.img_size)
            axarr[i, 0].imshow(ori_data, cmap='gray', interpolation='none')
            axarr[i, 0].set_title('Original Image')
            axarr[i, 0].axis('off')
            axarr[i, 1].imshow(tmp_rec_data, cmap='gray', interpolation='none')
            axarr[i, 1].set_title('Compressed with n_comp = %s' % self.n_comp[i])
            axarr[i, 1].axis('off')
        plt.show()

    # def plot_sample_multi_result_multi_index(self, data, rec_data_list):
    #     ori_data = data[:, 0].reshape(self.img_size, self.img_size)
    #     ori_data = []
    #     for j in range(0, len(self.input_index)):
    #         ori_data.append(data[:, j].reshape(self.img_size, self.img_size))
    #
    #     # fig4, axarr = plt.subplots(3,2,figsize=(8,8))
    #     fig4, axarr = plt.subplots(4, 2, figsize=(9, 9))
    #     for i in range(0, len(self.n_comp)):
    #         tmp_rec_data = rec_data_list[i][:, 0].reshape(self.img_size, self.img_size)
    #         axarr[i, 0].imshow(ori_data[i], cmap='gray', interpolation='none')
    #         axarr[i, 0].set_title('Original Image')
    #         axarr[i, 0].axis('off')
    #         axarr[i, 1].imshow(tmp_rec_data, cmap='gray', interpolation='none')
    #         axarr[i, 1].set_title('Compressed with n_comp = %s' % self.n_comp[i])
    #         axarr[i, 1].axis('off')
    #     plt.show()


import math
import numpy as np
import tensorflow as tf
import tensorflow_probability as tfp
tfd = tfp.distributions

def make_checkerboard(x_range, y_range, block_size=(10,10), n_samples=100, add_noise=False):
    """Generates a checkerboard distribution spanning x_range
    across the x axis and y_range across the y axis. The
    block_size is the size of the checker pattern that is tiled
    and n_samples is the number of points of the initial grid
    along each axis.

    board = make_checkerboard((-1, 1), (-1, 1), block_size=(11,11), n_samples=80, add_noise=True)
    plt.figure(figsize=(6,6))
    plt.scatter(board[:,0], board[:,1], s=2);
    """
    xm, xM = (float(e) for e in x_range)
    ym, yM = (float(e) for e in y_range)
    x = np.linspace(xm, xM, n_samples)     # x.shape = (100,)
    y = np.linspace(ym, yM, n_samples)     # y.shape = (100,)
    X, Y = np.meshgrid(x, y)               # X.shape = (100, 100), Y.shape = (100, 100)
    XY = np.transpose(
            np.stack((X,Y)))               # XY.shape = (100, 100, 2)

    # Calculate the checkboard's dimensions
    block_nx, block_ny = block_size
    board_nx = math.ceil((float(n_samples)) / (2 * block_nx))
    board_ny = math.ceil((float(n_samples)) / (2 * block_ny))

    # one_zero is    vs.    zero_one which is
    # 1 1 0 0               0 0 1 1
    # 1 1 0 0               0 0 1 1
    one_zero = np.concatenate((
        tf.ones((block_nx, block_ny)),
        tf.zeros((block_nx, block_ny))),
        axis=1)
    zero_one = np.flip(one_zero, axis=1)

    # A block is the concatenation of the former
    # 1 1 0 0
    # 1 1 0 0
    # 0 0 1 1
    # 0 0 1 1
    block = np.concatenate((one_zero, zero_one))

    # And a board is the tiling of many building blocks
    # 1 1 0 0  1 1 0 0 . .
    # 1 1 0 0  1 1 0 0 . .
    # 0 0 1 1  0 0 1 1 . .
    # 0 0 1 1  0 0 1 1 . .
    # . . . .  . . . .
    # . . . .  . . . .
    board = np.tile(block, ((board_nx, board_ny)))

    # Multiply the board with the grid element-wise, as if we were applying
    # a binary mask, but first clip the board so that it has the same shape
    # as our grid
    board = board[0:XY.shape[0], 0:XY.shape[1]]
    board = np.expand_dims(board, axis=2)
    new_grid = np.multiply(board, XY)
    sx, sy, _ = new_grid.shape
    new_grid = tf.reshape(new_grid, (sx * sy, 2))

    # Drop all rows with zero elements [0., 0.]
    # XXX: Is there any vector operation for this?
    final_grid = []
    for row in new_grid:
        if any(row):
            final_grid.append(row)
    final_grid = np.array(final_grid)

    # Add some Gaussian noise
    if add_noise:
        wx = 0.5 * (xM - xm) / n_samples    # Weighting factors
        wy = 0.5 * (yM - ym) / n_samples
        gaussian_dist = tfd.MultivariateNormalDiag(
            loc=[0., 0], scale_diag=[wx, wy])
        gaussian_noise = gaussian_dist.sample(final_grid.shape[0])
        final_grid = final_grid + gaussian_noise

    return final_grid


from numpy import exp, cos, linspace
import matplotlib.pyplot as plt
import os, time, glob

def damped_vibrations(t, A, b, w):
    return A*exp(-b*t)*cos(w*t)

def compute(A, b, w, T, resolution=500):
    """Return filename of plot of the damped_vibration function."""
    t = linspace(0, T, resolution+1)
    y = damped_vibrations(t, A, b, w)
    plt.figure()  # needed to avoid adding curves in plot
    plt.plot(t, y)
    plt.title('A=%g, b=%g, w=%g' % (A, b, w))

    # Make Matplotlib write to BytesIO file object and grab
    # return the object's string
    from io import BytesIO
    figfile = BytesIO()
    plt.savefig(figfile, format='png')
    figfile.seek(0)  # rewind to beginning of file
    import base64
    figdata_png = base64.b64encode(figfile.getvalue())
    return figdata_png

def compute_png_svg(A, b, w, T, resolution=500):
    """Return filename of plot of the damped_vibration function."""
    t = linspace(0, T, resolution+1)
    y = damped_vibrations(t, A, b, w)
    plt.figure()  # needed to avoid adding curves in plot
    plt.plot(t, y)
    plt.title('A=%g, b=%g, w=%g' % (A, b, w))

    # Make Matplotlib write to BytesIO file object and grab
    # return the object's string
    from io import BytesIO
    figfile = BytesIO()
    plt.savefig(figfile, format='png')
    figfile.seek(0)  # rewind to beginning of file
    import base64
    figdata_png = base64.b64encode(figfile.getvalue())
    figfile = BytesIO()
    plt.savefig(figfile, format='svg')
    figfile.seek(0)
    figdata_svg = '<svg' + figfile.getvalue().split('<svg')[1]
    figdata_svg = unicode(figdata_svg,'utf-8')
    return figdata_png, figdata_svg


from statistics import mean
import numpy as np
import matplotlib.pylab as plt

from matplotlib import style

style.use("fivethirtyeight")
#dtype important later
xs = np.array([1,2,3,4,5,6],dtype=np.float64)
ys = np.array([5,4,6,5,6,7],dtype=np.float64)

plt.scatter(xs,ys)
plt.show()

def best_fit_slope(xs,ys):
    m = (mean(xs)*mean(ys)-mean(xs*ys))/(mean(xs)**2.-mean(xs**2.))
    return m

m=best_fit_slope(xs,ys)
print(m)

########################PEMDAS
plt.scatter(xs,ys)
plt.plot(xs,m*xs,color='C1')
plt.show()

b=mean(ys)-m*mean(xs)

plt.scatter(xs,ys)
plt.plot(xs,m*xs+b,color='C1')
plt.show()

def best_fit_slope_and_intercept(xs,ys):
        m = (mean(xs)*mean(ys)-mean(xs*ys))/(mean(xs)**2.-mean(xs**2.))
        b=mean(ys)-m*mean(xs)
        return m,b

m,b=best_fit_slope_and_intercept(xs,ys)

plt.scatter(xs,ys)
plt.plot(xs,m*xs+b,color='C1')
plt.show()

regression_line=[(m*x)+b for x in xs]

print(m,b)

predicted_x=8

predicted_y=m*predicted_x+b

plt.scatter(xs,ys)
plt.plot(xs,m*xs+b,color='C1')
plt.scatter(predicted_x,predicted_y, color='C2')
plt.show()


from sympy.core.numbers import igcd
from primetest import isprime
from factor_ import factorint, trailing

def totient_(n):
    """returns the number of integers less than n
    and relatively prime to n"""
    if n < 1:
        raise ValueError("n must be a positive integer")
    tot = 0
    for x in xrange(1, n):
        if igcd(x, n) == 1:
            tot += 1
    return tot

def n_order(a, n):
    """ returns the order of a modulo n
    Order of a modulo n is the smallest integer
    k such that a^k leaves a remainder of 1 with n.
    """
    if igcd(a, n) != 1:
        raise ValueError("The two numbers should be relatively prime")
    group_order = totient_(n)
    factors = factorint(group_order)
    order = 1
    if a > n:
        a = a % n
    for p, e in factors.iteritems():
        exponent = group_order
        for f in xrange(0, e + 1):
            if (a ** (exponent)) % n != 1:
                order *= p ** (e - f + 1)
                break
            exponent = exponent // p
    return order

def is_primitive_root(a, p):
    """
    returns True if a is a primitive root of p
    """
    if igcd(a, p) != 1:
        raise ValueError("The two numbers should be relatively prime")
    if a > p:
        a = a % p
    if n_order(a, p) == totient_(p):
        return True
    else:
        return False

def is_quad_residue(a, p):
    """
    returns True if a is a quadratic residue of p
    p should be a prime and a should be relatively
    prime to p
    """
    if not isprime(p) or p == 2:
        raise ValueError("p should be an odd prime")
    if igcd(a, p) != 1:
        raise ValueError("The two numbers should be relatively prime")
    if a > p:
        a = a % p

    def square_and_multiply(a, n, p):
        if n == 0:
            return 1
        elif n == 1:
            return a
        elif n % 2 == 1:
            return ((square_and_multiply(a, n // 2, p) ** 2) * a) % p
        else:
            return (square_and_multiply(a, n // 2, p) ** 2) % p

    return (square_and_multiply(a, (p - 1) // 2, p) % p) == 1

def legendre_symbol(a, p):
    """
    return 1 if a is a quadratic residue of p, 0 if a is multiple of p,
    else return -1
    p should be an odd prime by definition
    """
    if not isprime(p) or p == 2:
        raise ValueError("p should be an odd prime")
    _, a = divmod(a, p)
    if not a:
        return 0
    if is_quad_residue(a, p):
        return 1
    else:
        return -1

def jacobi_symbol(m, n):
    """
    Returns 0 if m cong 0 mod(n),
    Return 1 if x**2 cong m mod(n) has a solution else return -1.

    jacobi_symbol(m, n) is product of the legendre_symbol(m, p) for all the prime factors p of n.

    **Examples**

    >>> from sympy.ntheory import jacobi_symbol
    >>> jacobi_symbol(45, 77)
    -1
    >>> jacobi_symbol(60, 121)
    1

    """
    if not n % 2:
        raise ValueError("n should be an odd integer")
    if m < 0 or m > n:
        m = m % n
    if igcd(m, n) != 1:
        return 0

    j = 1
    s = trailing(m)
    m = m >> s
    if s % 2 and n % 8 in [3, 5]:
        j = (-1)**s

    while m != 1:
        if m % 4 == 3 and n % 4 == 3:
            j *= -1
        m, n = n % m, m
        s = trailing(m)
        m = m >> s
        if s % 2 and n % 8 in [3, 5]:
            j = (-1)**s
    return j


#Import critical gui functions and libraries for math manipulation
import tkinter as tk
from tkinter import *
from tkinter import ttk
import numpy as np
import matplotlib.pyplot as plt
from scipy import random

a = 0
b = np.pi
c = 0
d = np.pi
e = 0
g = np.pi
N = 1000

def f(x):
    return np.sin(x)

def Integrate_Run(list,number):
    plt.figure(1)
    plt.subplot(311)
    for i in range (int(number)):
        ar = np.zeros(N)

        for i in range(len(ar)):
            ar[i] = random.uniform(a,b)

        integral = 0.0

        for i in ar:
            integral += f(i)

        ans = (b-a)/float(N)*integral
        #print("The value calculated by monte carlo integration is ",format(ans))
        list.append(ans)
    plt.title("Monte Carlo Distribution values 1D Integral")
    plt.hist(list, bins = 30, ec="black")
    plt.pause(0.05)
    #print(list)

def f2(x,y):
    return np.sin(x*y)

def DIntegrate_Run(list1,number):
    plt.subplot(312)
    plt.xlabel("Areas")
    plt.title("Monte Carlo Distribution values 2D Integral")
    for i in range (int(number)):
        arx = np.zeros(N)
        ary = np.zeros(N)

        for i in range(len(arx)):
            arx[i] = random.uniform(a,b)
        for i in range(len(ary)):
            ary[i] = random.uniform(c,d)

        IntegralFy = 0.0

        for i in range(len(arx)):
                IntegralFy += f2(arx[i],ary[i])
        Area = (b-a)*(d-c)
        final = (Area/float(N))*IntegralFy
        list1.append(final)
        #print(list)

    plt.hist(list1, bins = 30, ec="black")

def f3(x,y,z):
    q = x**2 + y**2 + z**2
    return q

def TIntegrate_Run(list2,number):
    plt.subplot(313)
    plt.xlabel("Areas")
    plt.title("Monte Carlo Distribution values 3D Integral")
    for i in range (int(number)):
        arx = np.zeros(N)
        ary = np.zeros(N)
        arz = np.zeros(N)

        for i in range(len(arx)):
            arx[i] = random.uniform(a,b)
        for i in range(len(ary)):
            ary[i] = random.uniform(c,d)
        for i in range(len(arz)):
            arz[i] = random.uniform(e,g)

        IntegralF3 = 0.0

        for i in range(len(arx)):
                IntegralF3 += f3(arx[i],ary[i],arz[i])
        Area = (b-a)*(d-c)*(g-e)
        final = (Area/float(N))*IntegralF3
        list2.append(final)
        #print(list)

    plt.hist(list2, bins = 30, ec="black")
    plt.draw()
    plt.tight_layout()
    plt.show()

Ans_list = []
Anslist = []
Ans_list3 = []
Integrate_Run(Ans_list,1000)
DIntegrate_Run(Anslist,1000)
TIntegrate_Run(Ans_list3,1000)


%matplotlib inline
import numpy as np
from pylab import *

# Hard code a linear relationship between
# page speeds and the amount of money spent
# on a website. These should be inversely correlated
pageSpeeds = np.random.normal(3.0, 1.0, 1000)
purchaseAmount = 100 - (pageSpeeds + np.random.normal(0, 0.1, 1000)) * 3

# Render a scatterplot for this relationship
scatter(pageSpeeds, purchaseAmount)

from scipy import stats

# Run an actual linear regression model calculation using stats library
# This will return the slope (m), y intercept (c)
# The r-value, p-value (determines whether null hypothesis is valid)
slope, intercept, r_value, p_value, std_err = stats.linregress(pageSpeeds, purchaseAmount)

# The null hypothesis is rejected if p < 0.05.
# This would mean that there is no statistically significant
# relationship between the two variables that are being measured

import matplotlib.pyplot as plt

def predict(x):
    # Calculate y = mx + c from given variables
    return slope * x + intercept

fitLine = predict(pageSpeeds)

# Plot the scatter
plt.scatter(pageSpeeds, purchaseAmount)
# Plot the resulting fit line from the linear regression
plt.plot(pageSpeeds, fitLine, c='r')
plt.show()

# Exercise
# Increase the random variation in the data and observe the impact
# it has on the r-squared error value.

# Mean of 3.0
# Standard deviation of 10 (was 1)
# Sample size of 1000
pageSpeeds = np.random.normal(3.0, 10, 1000)
# Mean of 0
# Standard deviation of 5 (was 0.1)
# Sample size of 1000
purchaseAmount = 100 - (pageSpeeds + np.random.normal(0, 5, 1000)) * 3

slope, intercept, r_value, p_value, std_err = stats.linregress(pageSpeeds, purchaseAmount)

fitLine = predict(pageSpeeds)

# Plot the scatter
plt.scatter(pageSpeeds, purchaseAmount)
# Plot the resulting fit line from the linear regression
plt.plot(pageSpeeds, fitLine, c='r')
plt.show()

# The resulting r-value from the modified values is lower
# Which indicates that less of the variance is encompassed
# by the line of best fit.
print(r_value**2)
print(std_err)


"""
Filter kernels (Szeliski 3.2)
"""

import numpy as np

KERNEL_BILINEAR = 1.0/16 * np.array(((1, 2, 1),
                                    (2, 4, 2),
                                    (1, 2, 1)))

KERNEL_GAUSSIAN = 1.0/256 * np.array(((1, 4,  6,  4,  1),
                                      (4, 16, 24, 16, 4),
                                      (6, 24, 36, 24, 6),
                                      (4, 16, 24, 16, 4),
                                      (1, 4,  6,  4,  1)))

KERNEL_SOBEL = 1.0/8 * np.array(((-1, 0, 1),
                                 (-2, 0, 2),
                                 (-1, 0, 1)))

KERNEL_CORNER = 1.0/4 * np.array((( 1, -2,  1),
                                  (-2,  4, -2),
                                  ( 1, -2,  1)))

def gaussian_kernel(sigma_x, sigma_y=None):
    """
    Normalized Gaussian kernel
    """
    if not sigma_y:
        sigma_y = sigma_x

    X, Y = np.meshgrid(np.arange(-sigma_x, sigma_x+1, dtype=float),
                       np.arange(-sigma_y, sigma_y+1, dtype=float))
    G = np.exp(- (X**2 / (2*sigma_x**2) + Y**2 / (2*sigma_y**2)))

    # Normalize
    return G / G.sum()