import numpy as np
from matplotlib import pyplot as plt
from random import random as rd
def predictValue(X,theta):
    return np.sum(predict(X,theta))

def predict(X,theta):
    return X@theta

def hypothesis(X,Y,theta):
    X = np.append([[1]]*np.size(X,0),X,1)
    return (np.transpose(theta)@X - Y)

def costFunction(X,Y,theta):
    cost = X@theta - Y
    J = np.sum(np.transpose(cost)@cost)
    return J*(1/2*np.size(X,0))

    # def valueCostFunction(X,Y,theta):
    #     X = np.append([[1]]*np.size(X,0),X,1)
    #     m = np.size((Y,0))
    #     n = len(X[0,:])
    #     J = 0
    #     for i in range(1,m):
    #         J += np.sum(hypothesis(X,Y,theta)**2)
    #     J /= 2*m
    #     return J
    # def deritativeCostFunction(X,Y,theta):
    #     pass

def Normalization(x):
    _max = np.empty((1,x.shape[1]))
    for i in range(x.shape[1]):
        _max[0,i] = max(abs(x[:,i].max()),abs(x[:,i].min()))
    for i in range (x.shape[1]):
        x[:,i] = x[:,i] / max(abs(x[:,i].max()),abs(x[:,i].min()))
    return x,_max
def GradientDescent(X,Y, alpha = 0.0005,iter = 10000):
    X, _maxX = Normalization(X)
    Y, _maxY = Normalization(Y)
    X = np.append([[1]]*np.size(X,0),X,1) #check
    print('_maxX:\n',_maxX)
    m = np.size(X,0) #check
    n = np.size(X,1)
    J_hist = np.zeros((iter,2))
    theta = np.array([[0]]*n)
    preCost = costFunction(X,Y,theta)

    for i in range(250):
        theta = theta - (alpha / m) * (np.transpose(X)@(X@theta - Y))
        cost = costFunction(X,Y,theta)
        if np.round(cost,15) == np.round(preCost,15):
            print('Found optimal value of costFunction at {} is {}'.format(i,cost))
            #thêm tất cả các index còn lại sau khi break
            J_hist[i:,0] = range(i,iter)
            #giá trị J sau khi break sẽ như cũ trong những điểm còn lại
            J_hist[i:,1] = cost
            break
        else:
            J_hist[i,1] = cost
            J_hist[i,0] = i
            preCost = cost
    #print(theta)
    for i in range(_maxX.shape[1]):
        theta[1:,i] *= _maxX[0,i]
    #print(theta)

    yield theta
    yield J_hist


from numpy import linspace, arcsin as asin, pi
from scitools.std import plot, savefig, hold

def tilde_w(w, dt):
    return (2./dt)*asin(w*dt/2.)

def plot_phase_error():
    w = 1  # relevant value in a scaled problem
    m = linspace(1, 101, 101)
    period = 2*pi/w
    dt_values = [period/num_timesteps_per_period
                 for num_timesteps_per_period in (4, 8, 16, 32)]
    for dt in dt_values:
        e = m*2*pi*(1./w - 1/tilde_w(w, dt))
        plot(m, e, '-', title='peak location error (phase error)',
             xlabel='no of periods', ylabel='phase error')
        hold('on')
    savefig('phase_error.png')

plot_phase_error()


import numpy

def cyclic_swap(x, i, j, k, xhat):
    x[i], x[j], x[k] = x[j], x[k], xhat

def f(x):
    return x**4 - 2*x**3 + 3*x**2 - 2*x + 1

## it is a minimization method that does not use the gradient
def parabolic_search(f, a, b, tol, maxiter=100):
    """
    Successive parabolic interpolation is a technique
    for finding the extremum (minimum or maximum) of a
    continuous unimodal function by successively fitting parabolas
    (polynomials of degree two) to a function of one variable at three unique points or,
    in general, a function of n variables at 1+n(n+3)/2 points,
    and at each iteration replacing the "oldest" point with the extremum of the fitted parabola.
    # https://web.engr.oregonstate.edu/~webbky/MAE4020_5020_files/Section%205%20Root%20Finding%20and%20Optimization.pdf
    # MATH 381
    """
    c = (a + b)/2
    inds = array([f(a), f(b), f(c)]).argsort()
    x = array([a, b, c])[inds]
    y = array([f(xi) for xi in x])
    n = 0
    while absolute(x[-1] - x[1]) > tol and n < maxiter:
        p = (x[2] - x[0])**2*(y[2] - y[1]) - (x[2] - x[1])**2*(y[2] - y[0])
        q = ((x[2] - x[0])*(y[2] - y[1]) - (x[2] - x[1])*(y[2] - y[0]))
        xhat = x[2] - 0.5*(p/q)
        cyclic_swap(x, 0, 1, 2, xhat)
        cyclic_swap(y, 0, 1, 2, f(xhat))
        n += 1
    if n >= maxiter:
        print("max iterations reached without convergence")
    return x[-1]

# example
tol = 10.**-4
xmin = parabolic_search(f, -pi, 2*pi, tol, 1000)


import numpy as np

def running_mean(x):
    """
    Calculate running average
    Initialize M1 = x1 and S1 = 0.

    For subsequent x‘s, use the recurrence formulas

    Mk = Mk-1+ (xk – Mk-1)/k
    Sk = Sk-1+ (xk – Mk-1)*(xk – Mk).

    For 2 ≤ k ≤ n, the kth estimate of the variance is s2 = Sk/(k – 1).
    """
    out = np.zeros((x.size,3))
    for i in range(x.size):
        if i == 0:
            out[i,:] = (x[i],0,0)
        else:
            Mi = out[i-1,0] + (x[i] - out[i-1,0])/(i+1)
            Si = out[i-1,2] + (x[i] - out[i-1,0])*(x[i] - out[i,0])
            out[i,:] = (Mi,np.sqrt(np.abs(Si)/i**2),Si)
    return out[:,:2]

def runavr(x):
    out = np.zeros((x.size-1,3))
    for i in range(x.size-1):
        if i == 0:
            out[i,:] = (x[i],0,0)
        else:
            Mi = out[i-1,0] + (x[i] - out[i-1,0])/(i+1)
            Si = out[i-1,2] + (x[i] - out[i-1,0])*(x[i] - out[i,0])
            out[i,:] = (Mi,np.sqrt(Si/i**2),Si)
    return out


import math
import numpy as np
from numpy import random
import pandas as pd
import matplotlib.pyplot as plt
from cycler import cycler
from scipy.integrate import odeint, solve_ivp
plt.style.use('seaborn-whitegrid')

def OU_process():
    '''
    Solve the Langevin equation (describes time evolution of Brownian motion) using the Ornstein-Uhlenbeck process and the Euler-Maruyama method.
    '''

    mu = 10.0    # mean
    sigma = 1.0    # standard deviation
    tau = 0.05   # time constant (mean free time)

    T = 1.0    # total time
    dt = 0.001   # time step
    n = int(T/dt)    # number of time steps
    t = np.linspace(0.0, T, n)    # create time list (from 0 to T with step size T/N)

    x = np.zeros(n)     # create array to hold x values

    ### Define renormalised variables
    sigma_bis = sigma * np.sqrt(2.0/tau)
    sqrtdt = np.sqrt(dt)

    ### Implement the Euler-Maruyama method
    for i in range(n - 1):
        x[i+1] = x[i] + dt*(-(x[i] - mu) / tau) + (sigma_bis * sqrtdt * np.random.randn())

    fig, ax = plt.subplots(1, 1, figsize=(8, 4))
    ax.plot(t, x, lw=2)
    plt.show()

    ### Calculate estimated distribution
    ntrials = 10000
    X = np.zeros(ntrials)

    ### Create bins for the histograms.
    bins = np.linspace(-2., 14., 100)
    fig, ax = plt.subplots(1, 1, figsize=(8, 4))

    for i in range(n):
        # Update the process independently for all trials
        X += dt * (-(X - mu) / tau) + \
            sigma_bis * sqrtdt * np.random.randn(ntrials)

        # Display the histogram for various points in time
        if i in (5, 50, 500):
            hist, _ = np.histogram(X, bins=bins)
            ax.plot((bins[1:] + bins[:-1]) / 2, hist,
                    {5: '-', 50: '.', 500: '-.', }[i],
                    label=f"t={i * dt:.2f}")

        ax.legend()
    plt.show()

def langevin(t, inicon):

  m = 1.5
  k = 0.000001
  R = np.random.normal(0,1)

  x0 = inicon[0]
  v0 = inicon[1]

  dvdt = -m*v0 + k*R

  return [v0, dvdt]   # returns v0 as x_n+1

t = np.linspace(0, 1000, 1000000)

output = solve_ivp(langevin, [0, 1000], [0, 1], t_eval=t, method='RK45')

fig, ax = plt.figure(), plt.axes()
ax.plot(t, output.y[0])
ax.plot(t, output.y[1])
plt.show()

### Call function
# OU_process()


import numpy as np
import pandas as pd

# Numerical Analysis with Numpy Arrays

# 1. Create your first array with the elements [1,22.4,5,35,4,6.7,3,8,40] and print it.
# Experiment what the following functions do: ndim, shape, size and dtype:

array = np.array([1, 22.4, 5, 35, 4, 6.7, 3, 8, 40])

print('Answer : 01')
print(array)
print('Ndim value:')
print(array.ndim)
print('shape value:')
print(array.shape)
print('size value:')
print(array.size)
print('dtype value:')
print(array.dtype)

# 2. Create your first matrix with the elements [['a', 'b'],['c', 'd'],[3, 3]] and print it.
# Experiment what the following functions do: ndim, shape, size and dtype:

array2 = np.array([['a', 'b'],['c', 'd'],[3, 3]])

print('Answer : 02')
print(array2)
print('Ndim value:')
print(array2.ndim)
print('shape value:')
print(array2.shape)
print('size value:')
print(array2.size)
print('dtype value:')
print(array2.dtype)

# 3. Create numpy 1 dimension array using each of the functions arange and rand

array3 = np.arange(10)
array4 = np.random.rand(10)*10

print('Answer : 03')
print(array3)
print(array4)

# 4. Create numpy 2 dimensions matrix using each of the functions zeros and rand

matrix = np.zeros((2, 2))
matrix2 = np.random.rand(2, 2)

print('Answer : 04')
print(matrix)
print(matrix2)

# 5. Create an array containing 20 times the value 7. Reshape it to a 4 x 5 Matrix

array5 = np.ones(20)*7
matrix3 = array5.reshape(4,5)

print('Answer : 05')
print(array5)
print(matrix3)

# 6. Create a 6 x 6 matrix with all numbers up to 36, then print:
# only the first element on it
# only the last 2 rows for it
# only the two mid columns and 2 mid rows for it
# the sum of values for each column

matrix4 = np.arange(36)+1
matrix4 = matrix4.reshape(6,6)

print('Answer : 06')
print(matrix4)

print('sum of each column')
print(np.sum(matrix4,axis=0))

print('First element')
print(matrix4[0,0])

print('last 2 rows')
print(matrix4[4:,:])

print('two mid columns and 2 mid rows')
print(matrix4[2:4,2:4])


import scipy.stats as st
import scipy as sp

def tauchenhussey(N,mu,rho,sigma, baseSigma):
	"""
	Function tauchenhussey

	Purpose:    Finds a Markov chain whose sample paths
				approximate those of the AR(1) process
					z(t+1) = (1-rho)*mu + rho * z(t) + eps(t+1)
				where eps are normal with stddev sigma

	Format:     {Z, Zprob} = TauchenHussey(N,mu,rho,sigma,m)

	Input:      N         scalar, number of nodes for Z
		    mu        scalar, unconditional mean of process
		    rho       scalar
		    sigma     scalar, std. dev. of epsilons
		    baseSigma scalar, std. dev. used to calculate Gaussian
			quadrature weights and nodes, i.e. to build the
			grid. I recommend that you use
                        baseSigma = w*sigma +(1-w)*sigmaZ where sigmaZ = sigma/sqrt(1-rho^2),
				and w = 0.5 + rho/4. Tauchen & Hussey recommend
				baseSigma = sigma, and also mention baseSigma = sigmaZ.

	Output:     Z       N*1 vector, nodes for Z
				Zprob   N*N matrix, transition probabilities

	Author:		Benjamin Tengelsen, Brigham Young University (python)
				Martin Floden, Stockholm School of Economics (original)
				January 2007 (updated August 2007)

	This procedure is an implementation of Tauchen and Hussey's
	algorithm, Econometrica (1991, Vol. 59(2), pp. 371-396)
	"""

	Z     = sp.zeros((N,1))
	Zprob = sp.zeros((N,N))
	[Z,w] = gaussnorm(N,mu,baseSigma**2)
	for i in range(N):
		for j in range(N):
			EZprime    = (1-rho)*mu + rho*Z[i]
			Zprob[i,j] = w[j] * st.norm.pdf(Z[j],EZprime,sigma) / st.norm.pdf(Z[j],mu,baseSigma)

	for i in range(N):
		Zprob[i,:] = Zprob[i,:] / sum(Zprob[i,:])

	return Z.T,Zprob

def gaussnorm(n,mu,s2):
	"""
	Find Gaussian nodes and weights for the normal distribution
	n  = # nodes
	mu = mean
	s2 = variance
	"""
	[x0,w0] = gausshermite(n)
	x = x0*sp.sqrt(2.*s2) + mu
	w = w0/sp.sqrt(sp.pi)
	return [x,w]

def gausshermite(n):
	"""
	Gauss Hermite nodes and weights following 'Numerical Recipes for C'
	"""

	MAXIT = 10
	EPS   = 3e-14
	PIM4  = 0.7511255444649425

	x = sp.zeros((n,1))
	w = sp.zeros((n,1))

	m = int((n+1)/2)
	for i in range(m):
		if i == 0:
			z = sp.sqrt((2.*n+1)-1.85575*(2.*n+1)**(-0.16667))
		elif i == 1:
			z = z - 1.14*(n**0.426)/z
		elif i == 2:
			z = 1.86*z - 0.86*x[0]
		elif i == 3:
			z = 1.91*z - 0.91*x[1]
		else:
			z = 2*z - x[i-1]

		for iter in range(MAXIT):
			p1 = PIM4
			p2 = 0.
			for j in range(n):
				p3 = p2
				p2 = p1
				p1 = z*sp.sqrt(2./(j+1))*p2 - sp.sqrt(float(j)/(j+1))*p3
			pp = sp.sqrt(2.*n)*p2
			z1 = z
			z = z1 - p1/pp
			if sp.absolute(z-z1) <= EPS:
				break

		if iter>MAXIT:
			error('too many iterations'), end
		x[i,0]     = z
		x[n-i-1,0] = -z
		w[i,0]     = 2./pp/pp
		w[n-i-1,0] = w[i]

	x = x[::-1]
	return [x,w]


import datetime

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

infected_today = 15160
infected_two_weeks_ago = 653

population_size = 83042200
initially_infected = infected_today - infected_two_weeks_ago
initially_recovered = infected_two_weeks_ago
initially_susceptible = population_size - initially_infected - initially_recovered
contacts_per_person_per_day = 8
infection_rate_per_contact = 0.06
days_infectious = 5

beta = infection_rate_per_contact * contacts_per_person_per_day
gamma = 1 / days_infectious
R0 = beta / gamma

print("Contacts per person per day:", contacts_per_person_per_day)
print("Infection rate per contact:", infection_rate_per_contact)
print("Days infectious:", infection_rate_per_contact)
print("Beta", beta)
print("Gamma", gamma)
print("R0", R0)

# A grid of time points (in days)
plot_t = np.array(
    [datetime.date.today() + datetime.timedelta(days=i) for i in range(150)]
)
t = np.arange(150)

# The SIR model differential equations.
def deriv(y, t, N, beta, gamma):
    S, I, R = y
    dSdt = -beta * S * I / N
    dIdt = beta * S * I / N - gamma * I
    dRdt = gamma * I
    return dSdt, dIdt, dRdt

# Initial conditions vector
y0 = initially_susceptible, initially_infected, initially_recovered
# Integrate the SIR equations over the time grid, t.
ret = odeint(deriv, y0, t, args=(population_size, beta, gamma))
S, I, R = ret.T

# Plot the data on three separate curves for S(t), I(t) and R(t)
fig = plt.figure(facecolor="w")
ax = fig.add_subplot(111, axisbelow=True)
ax.plot(plot_t, S / 1000, alpha=0.5, lw=2, label="Susceptible")
ax.plot(plot_t, I / 1000, alpha=0.5, lw=2, label="Infected")
ax.plot(plot_t, R / 1000, alpha=0.5, lw=2, label="Recovered with immunity")
ax.set_xlabel("Time /days")
ax.set_ylabel("Number (1000s)")
# ax.set_ylim(0, 1.2)
ax.yaxis.set_tick_params(length=0)
ax.xaxis.set_tick_params(length=0)
ax.grid(b=True, which="major", c="w", lw=2, ls="-")
legend = ax.legend()
legend.get_frame().set_alpha(0.5)
for spine in ("top", "right", "bottom", "left"):
    ax.spines[spine].set_visible(False)
plt.show()

plt.plot(
    plot_t[1:],
    np.diff(I + R) / population_size,
    alpha=0.5,
    lw=2,
    label="Daily new infected",
)
plt.show()


from typing import Callable

import scipy.special as sci

def forward(order: int, f: Callable[[float], float], x: float, h: float, exact: bool = False) -> float:
    """Use forward finite difference formula of order `order` to compute the derivative of `f` at `x`.

    Args:
        order (int): Order of the derivate. (first, second ...).
        f (Callable[[float], float]): Function for which the derivative will be computed.
            Only one argument will be passed to it, `x`.
        x (float): Point at which the derivative will be computed.
        h (float): Error of the approximation # TODO: Is it the error?
        exact (bool, optional): Set the precision of the method.
            If False, floating comma numbers are used. Set to True to use long numbers. Defaults to False.

    Note:
        See more of `exact` parameter in https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.comb.html

    Returns:
        float: Derivative of order `order` of `f` evaluated at `x`.
    """
    return sum([(-1)**(order-k) * sci.comb(order, k, exact) * f(x + k*h) for k in range(order+1)]) / h**order

def backward(order: int, f: Callable[[float], float], x: float, h: float, exact: bool = False) -> float:
    """Use backward finite difference formula of order `order` to compute the derivative of `f` at `x`.

    Args:
        order (int): Order of the derivate. (first, second ...).
        f (Callable[[float], float]): Function for which the derivative will be computed.
            Only one argument will be passed to it, `x`.
        x (float): Point at which the derivative will be computed.
        h (float): Error of the approximation # TODO: Is it the error?
        exact (bool, optional): Set the precision of the method.
            If False, floating comma numbers are used. Set to True to use long numbers. Defaults to False.

    Note:
        See more of `exact` parameter in https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.comb.html

    Returns:
        float: Derivative of order `order` of `f` evaluated at `x`.
    """
    return sum([(-1)**(k) * sci.comb(order, k, exact) * f(x - k*h) for k in range(order+1)]) / h**order

def central(order: int, f: Callable[[float], float], x: float, h: float, exact: bool = False) -> float:
    """Use central finite difference formula of order `order` to compute the derivative of `f` at `x`.

    Args:
        order (int): Order of the derivate. (first, second ...).
        f (Callable[[float], float]): Function for which the derivative will be computed.
            Only one argument will be passed to it, `x`.
        x (float): Point at which the derivative will be computed.
        h (float): Error of the approximation # TODO: Is it the error?
        exact (bool, optional): Set the precision of the method.
            If False, floating comma numbers are used. Set to True to use long numbers. Defaults to False.

    Note:
        See more of `exact` parameter in https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.comb.html

    Returns:
        float: Derivative of order `order` of `f` evaluated at `x`.
    """
    return sum([(-1)**(k) * sci.comb(order, k, exact) * f(x - (order/2 - k)*h) for k in range(order+1)]) / h**order

if __name__ == '__main__':
    pass


from math import sqrt

from scipy.stats import t
from numpy import mean

def degrees_of_freedom(s1, s2, n1, n2):
    """
    Compute the number of degrees of freedom using the Satterhwaite Formula

    @param s1 The unbiased sample variance of the first sample
    @param s2 The unbiased sample variance of the second sample
    @param n1 Thu number of observations in the first sample
    @param n2 The number of observations in the second sample
    """
    #degreeoffreedom= numberofobservation-1

    a=s1 /n1
    b=s2 /n2
    c=1/(n1-1)
    d=1/(n2-1)

    degreeoffreedom=(a+b)**2 /( c * a**2 + d * b**2)

    return degreeoffreedom

def unbiased_sample_variance(observations, mean):
    """
    Compute the unbiased sample variance

    @param observations Iterable set of observations
    @param mean The estimated mean
    """
    n=1/ (len(observations) - 1)
    sample_=0
    for i in observations:
        sum0=(i-mean)**2
        sample_=sample_+sum0

    sample_var= n*sample_

    return sample_var

def t_statistic(mean1, mean2, n1, n2, svar1, svar2):
    """
    Compute the t-statistic for the given estimates
    """
    # Complete this funtion
    a=svar1
    b=svar2
    c=a/n1
    d=b/n2
    e= 1/sqrt(c+d)
    t_stat = e*(mean1-mean2)
    return t_stat

def t_test(sample1, sample2):
    """
    Return the two-tailed p-value of a t test with unequal variance for two samples.

    @param sample1 An iterable of the first sample
    @param sample2 An iterable of the second sample
    """
    m1=mean(sample1)
    m2=mean(sample2)
    n1=len(sample1)
    n2=len(sample2)
    s1=unbiased_sample_variance(sample1,m1)
    s2=unbiased_sample_variance(sample2,m2)

    degree=degrees_of_freedom(s1,s2,n1,n2)
    tsta=t_statistic(m1,m2,n1,n2,s1,s2)
    p=2*(1.0-t.cdf(abs(tsta),degree))
    return p

if __name__ == "__main__":
    v1 = [5, 7, 5, 3, 5, 3, 3, 9]
    v2 = [8, 1, 4, 6, 6, 4, 1, 2]

    print("p-value is %f" % t_test(v1, v2))


import numpy as np

#crear un array y y sumarle un número
arr = np.arange(11)
arr + 55    # array([55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65])

#multiplicarlo por un número
arr * 2     # array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18, 20])

#elevarlo al cuadrado
arr ** 2    # array([  0,   1,   4,   9,  16,  25,  36,  49,  64,  81, 100])

#calcular una función
np.tanh(arr)

"""
La salida será

array([ 0.        ,  0.76159416,  0.96402758,  0.99505475,  0.9993293 ,
        0.9999092 ,  0.99998771,  0.99999834,  0.99999977,  0.99999997,  1.        ])

"""

#creamos dos arrays
arr1 = np.arange(0,11)
arr2 = np.arange(20,31)

#los sumamos
arr1 + arr2     # array([20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40])

#multiplicamos
arr1 * arr2     # array([  0,  21,  44,  69,  96, 125, 156, 189, 224, 261, 300])

# >,<
arr1 > arr2

"""
    La salida será

    array([False, False, False, False, False, False, False, False, False,
           False, False], dtype=bool)

"""
# ==
arr1 == arr2 # ¡ojo! los arrays son de integers, no de floats

"""
    La salida será

    array([False, False, False, False, False, False, False, False, False,
       False, False], dtype=bool)
"""

A = np.array([[n+m*10 for n in range(5)] for m in range(5)])
A * A # el termino en ingles es element-wise
"""
array([[   0,    1,    4,    9,   16],
       [ 100,  121,  144,  169,  196],
       [ 400,  441,  484,  529,  576],
       [ 900,  961, 1024, 1089, 1156],
       [1600, 1681, 1764, 1849, 1936]])
"""

v1 = np.arange(0, 5)    # [0 1 2 3 4]
v1 * v1                 # array([ 0,  1,  4,  9, 16])


# import libraries
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

# Importing the dataset
dataset = pd.read_csv("Position_Salaries.csv")
x = dataset.iloc[:, 1:-1].values
y = dataset.iloc[:, -1].values
print('Level')
print(x)
print('Salaries')
print(y)

# Training the Linear Regression model on the whole dataset
lin_reg_1 = LinearRegression()
lin_reg_1.fit(x, y)

# Training the Polynomial Regression model on the whole dataset
poly_reg = PolynomialFeatures(degree=4)
x_poly = poly_reg.fit_transform(x)

lin_reg_2 = LinearRegression()
lin_reg_2.fit(x_poly,y)

# Getting the final linear regression equation with the values of the coefficients
print(lin_reg_1.coef_)
print(lin_reg_1.intercept_)

# Predicting the Test set results
y_pred = lin_reg_1.predict(x)

# Visualising the Training set results
plt.scatter(x, y, color='red')
plt.plot(x, y_pred, color='blue')
plt.title('Salary vs Position (linear)')
plt.xlabel('Level')
plt.ylabel('Salary')
plt.show()

# Predicting the Test set results
y_poly_pred = lin_reg_2.predict(x_poly)
# Visualising the Training set results
plt.scatter(x, y, color='red')
plt.plot(x, y_poly_pred, color='blue')
plt.title('Salary vs Position (Polynomial)')
plt.xlabel('Level')
plt.ylabel('Salary')
plt.show()

# predict
x1 = [[6.5]]
y1 = lin_reg_1.predict(x1)
print('As per linear regression salary for ' + str(x1) + ' is :' + str(y1))

x_poly2 = poly_reg.fit_transform(x1)
y_poly_pred2 = lin_reg_2.predict(x_poly2)
print('As per polynomial regression salary for ' + str(x1) + ' is :' + str(y_poly_pred2))

Regression/4 Polynomial Linear Regression/possition.py


#------------------------------------------------------------------------------------------------------------#
#Chapter 3 - Decorrelating your data and dimension reduction

#------------------------------------------------------------------------------------------------------------#

#Correlated data in nature
# Perform the necessary imports
import matplotlib.pyplot as plt
from scipy.stats import pearsonr

# Assign the 0th column of grains: width
width = grains[:,0]

# Assign the 1st column of grains: length
length = grains[:,1]

# Scatter plot width vs length
plt.scatter(width, length)
plt.axis('equal')
plt.xlabel('width')
plt.ylabel('length')
plt.title('I suspect that width and length will be correlated.')
plt.show()

# Calculate the Pearson correlation
correlation, pvalue = pearsonr(width,length)

# Display the correlation
print(correlation)

#------------------------------------------------------------------------------------------------------------#
#Decorrelating the grain measurements with PCA
# Import PCA
from sklearn.decomposition import PCA

# Create PCA instance: model
model = PCA()

# Apply the fit_transform method of model to grains: pca_features
pca_features = model.fit_transform(grains)

# Assign 0th column of pca_features: xs
xs = pca_features[:,0]

# Assign 1st column of pca_features: ys
ys = pca_features[:,1]

# Scatter plot xs vs ys
plt.scatter(xs, ys)
plt.axis('equal')
plt.show()

# Calculate the Pearson correlation of xs and ys
correlation, pvalue = pearsonr(xs, ys)

# Display the correlation
print(correlation)

#------------------------------------------------------------------------------------------------------------#

#The first principal component
# Make a scatter plot of the untransformed points
plt.scatter(grains[:,0], grains[:,1])

# Create a PCA instance: model
model = PCA()

# Fit model to points=points
points=model.fit(grains)

# Get the mean of the grain samples: mean
mean = model.mean_

# Get the first principal component: first_pc
first_pc = model.components_[0,:]

# Plot first_pc as an arrow, starting at mean
plt.arrow(mean[0],mean[1], first_pc[0], first_pc[1], color='red', width=0.01)

# Keep axes on same scale
plt.axis('equal')
plt.show()

#------------------------------------------------------------------------------------------------------------#
#Variance of the PCA features
# Perform the necessary imports
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
import matplotlib.pyplot as plt

# Create scaler: scaler
scaler = StandardScaler()

# Create a PCA instance: pca
pca = PCA()

# Create pipeline: pipeline
pipeline = make_pipeline(scaler,pca)

# Fit the pipeline to 'samples'
pipeline.fit(samples)

# Plot the explained variances
features = range(pca.n_components_)
plt.bar(features, pca.explained_variance_)
plt.xlabel('PCA feature')
plt.ylabel('variance')
plt.xticks(features)
plt.show()

#------------------------------------------------------------------------------------------------------------#

#Dimension reduction of the fish measurements
# Import PCA
from sklearn.decomposition import PCA

# Create a PCA instance with 2 components: pca
pca = PCA(n_components=2)

# Fit the PCA instance to the scaled samples
pca.fit(scaled_samples)

# Transform the scaled samples: pca_features
pca_features = pca.transform(scaled_samples)

# Print the shape of pca_features
print(pca_features.shape)

#------------------------------------------------------------------------------------------------------------#
#A tf-idf word-frequency array
# Import TfidfVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer

# Create a TfidfVectorizer: tfidf
tfidf = TfidfVectorizer()

# Apply fit_transform to document: csr_mat
csr_mat = tfidf.fit_transform(documents)

# Print result of toarray() method
print(csr_mat.toarray())

# Get the words: words
words = tfidf.get_feature_names()

# Print words
print(words)

#------------------------------------------------------------------------------------------------------------#
#Clustering Wikipedia part I
# Perform the necessary imports
from sklearn.decomposition import TruncatedSVD
from sklearn.cluster import KMeans
from sklearn.pipeline import make_pipeline

# Create a TruncatedSVD instance: svd
svd = TruncatedSVD(n_components=50)

# Create a KMeans instance: kmeans
kmeans = KMeans(n_clusters=6)

# Create a pipeline: pipeline
pipeline = make_pipeline(svd,kmeans)

#------------------------------------------------------------------------------------------------------------#

#Clustering Wikipedia part II
# Import pandas
import pandas as pd

# Fit the pipeline to articles
pipeline.fit(articles)

# Calculate the cluster labels: labels
labels = pipeline.predict(articles)

# Create a DataFrame aligning labels and titles: df
df = pd.DataFrame({'label': labels, 'article': titles})

# Display df sorted by cluster label
print(df.sort_values('label'))

#------------------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------------------#

#------------------------------------------------------------------------------------------------------------#

18.Unsupervised Learning in Python/Chapter 3 - Decorrelating your data and dimension reduction.py


# Bimodal distribution (mixture of two 1d Gaussians)
# Based on https://github.com/probml/pmtk3/blob/master/demos/bimodalDemo.m

import numpy as np
import matplotlib.pyplot as plt
import pyprobml_utils as pml

from scipy.stats import norm

# Define two normal distrubutions and their corresponding weights.
mu = [0, 2]
sigma = [1, 0.05]
n = [norm(loc=mu[i], scale=sigma[i]) for i in range(2)]
w = [0.5, 0.5]

# Define a set of x points for graphing.
xs = np.linspace(-2, 2*mu[1], 600)

# Combine the two distributions by their weights, evaluated at the x points.
p = sum(w[i] * n[i].pdf(xs) for i in range(2))

# Calculate the mean of the final distribution.
mean_p = np.mean(xs * p)

# Plot the final distribution and its mean.
linewidth = 3
plt.figure()
plt.plot(xs, p, 'black', linewidth=linewidth)
plt.vlines(mean_p, ymin=0, ymax=max(p), color='red', linewidth=linewidth)
pml.savefig('bimodalSpike.pdf')
plt.show()

# Another example, with two modes
mu = [0, 2]
sigma = [0.5, 0.5]
n = [norm(loc=mu[i], scale=sigma[i]) for i in range(2)]
w = [0.5, 0.5]
xs = np.linspace(-2, 2*mu[1], 600)
p = sum(w[i] * n[i].pdf(xs) for i in range(2))

plt.figure()
linewidth = 3
plt.plot(xs, p, 'black', linewidth=linewidth)
pml.savefig('bimodalDistribution.pdf')
plt.show()


# ------------------------------------------------------------------------------------------------------ #
# Author: Anna Lonergan                                                                                #
# Purpose: Script defining Newton-Raphson method for solving systems of linear and nonlinear equations   #
# ------------------------------------------------------------------------------------------------------ #

# Necessary Libraries:
import numpy as np
import numdifftools as nd
import pandas as pd

# Define NR (Newton-Raphson) function
def NR(F, x, tol = 0.000001, maxiter = 50):
    # Newton - Raphson Algorithm
    # Inputs:
    # F - a function. Defines the function(s) of the system, length = n.
    # x - list/array. initial guess for variables. length = n
    # tol - numeric. The error tolerance level. Default is 1e-6
    # maxiter - numeric. Number of maximum iterations to run algorithm. Default is 50.

    # Output:
    # solution - vector of length n which solves the system
    # nrout - data frame with information on norms and iteration number.
    ###############################################################################

    # Generate a dummy variable equal to zero. Dummy will be set to 1 if a solution is found.
    sol_found = 0

    # Create a base array to append results to
    results = np.zeros(len(x)+3)

    # Loop for Newton-Raphson Algorithm
    for iteration in range(1, maxiter + 1):

        # Step 2: Evaluate function at x, called Fval, and its Euclidean norm
        Fval = F(x)
        fnorm = np.linalg.norm(Fval, ord=2) # ord = 2 for Euclidean norm

        # Step 3: Compute the Jacobian Matrix
        jac = nd.Jacobian(F)(x)

        # Step 4: Solve for delta
        delta = np.linalg.solve(jac, -1*Fval)
        dnorm = np.linalg.norm(delta, ord = 2) # ord = 2 for Euclidean norm

        # Store iteration results
        row = np.zeros(len(x)+3)
        row[0] = iteration
        row[1] = fnorm
        row[2] = dnorm
        for i in range(len(x)):
            row[i+3] = x[i]
        results = np.vstack([results, row])

        # Stopping condition 1:
        if abs(fnorm) < tol:
            solution = x
            sol_found = 1
            break
        # Stopping condition 2:
        elif abs(dnorm) < tol:
            solution = x
            sol_found = 1
            break
        # If neither stopping condition is met, repeat steps 2 - 5 with updated x
        # Update x
        x = x + delta

    if sol_found == 1:
        iteration_table = pd.DataFrame(results[1:,0:])
        names1 = np.array(["Iteration #", "F norm", "D norm"])
        names2 = ['']*len(x)
        for i in range(len(x)):
            names2[i] = 'X_'+str(i)
        names = np.append(names1, names2)
        iteration_table.columns = names

        return solution, iteration_table
    else:
        print "Could not find a solution with given parameters."
        return results


import numpy as np
import matplotlib.pyplot as plt

# Generate a distribution
x = 0.5*np.random.randn(1000)+4

# Standard (mean=0, stdev=1) Scaler
y = (x-np.mean(x))/np.std(x)

# Min-Max (0-1) Scaler
z = (x-np.min(x))/(np.max(x)-np.min(x))

# Plot distributions
plt.figure(figsize=(8,4))
plt.hist(x, bins=30, label='original')
plt.hist(y, alpha=0.7, bins=30, label='standard scaler')
plt.hist(z, alpha=0.7, bins=30, label='minmax scaler')
plt.legend()
plt.show()


#! /usr/bin/env python3
import numpy as np
""" common maths functions that are not provided by default"""

def gaussian(x, mu, sig):
	""" return a Gaussian distribution over x
	inputs:
		x => x axis data of distribution
		mu => the mean value of data
		sig => standard deviation of data
	returns: Gaussian distribution
	"""
	return 1 / (sig * np.sqrt(2 * np.pi)) * np.exp((-1 / 2) * ((x - mu) / sig)**2)

def gaussian_max1(x, mu, sig):
	""" return a Gaussian distribution over x where the pean of the gaussian is 1
	inputs:
		x => x axis data of distribution
		mu => the mean value of data
		sig => standard deviation of data
	returns: Gaussian distribution with a peak of 1
	"""
	return np.exp((-1 / 2) * ((x - mu) / sig)**2)

def pink_power(f, alpha=1, scale=1):
	""" power spectral density for pink noise
	inputs:
		f => frequency
		alpha => exponent characteristic to pink noise
		scale => scaling factor of proportionality
	returns: power spectral density"""
	return scale / f**alpha


import string
import numpy as np
import math
from sympy import Matrix #inverse key

#-------------------------
def letterToNumber(letter):
	return string.ascii_lowercase.index(letter)

def numberToLetter(number):
	return chr(int(number) + 97)
#-------------------------

module = 26 #english alphabet

#raw_message = "act"
raw_message = "attack is to night"
print("raw message: ",raw_message)

message = []

key = np.array([
	[3, 10, 20],
	[20, 9, 17],
	[9, 4, 17]
]) #3x3

key_rows = key.shape[0]
key_columns = key.shape[1]

if key_rows != key_columns:
	raise Exception('key must be square matrix!')

#-------------------------
for i in range(0, len(raw_message)):
	current_letter = raw_message[i:i+1].lower()
	if current_letter != ' ':
		letter_index = letterToNumber(current_letter)
		message.append(letter_index)

#-------------------------
#message must be multiplier of key line count.
#if not append beginning of the message to the end

if len(message) % key_rows != 0:
	for i in range(0, len(message)):
		message.append(message[i])
		if len(message) % key_rows == 0:
			break

#------------------------
#transform message to numpy array. we'll use numpy's matrix multiplication
message = np.array(message)
message_length = message.shape[0]
print("message: ",message)

#transform message array to matrix
message.resize(int(message_length/key_rows), key_rows)
#------------------------

encryption = np.matmul(message, key)
encryption = np.remainder(encryption, module)
print("encrypted text: \n",encryption)

#------------------------

#decryption
#------------------------
print("finding inverse key")
inverse_key = Matrix(key).inv_mod(module)
inverse_key = np.array(inverse_key) #sympy to numpy
inverse_key = inverse_key.astype(float)
print("inverse key: ",inverse_key)

#------------------------
print("validating inverse key. key times inverse key must be idendity matrix")
check = np.matmul(key, inverse_key)
check = np.remainder(check, module)
print("key times inverse key: ",check)
print("it is ",np.allclose(check, np.eye(3)))

#------------------------
print("decryption:")
decryption = np.matmul(encryption, inverse_key)
decryption = np.remainder(decryption, module).flatten()
print("decryption: ",decryption)

decrypted_message = ""
for i in range(0, len(decryption)):
	letter_num = int(decryption[i])
	letter = numberToLetter(decryption[i])
	decrypted_message = decrypted_message + letter

print("decrypted message: ",decrypted_message)


from __future__ import print_function
import matplotlib.pyplot as plt
import numpy as np

"""
Compute lookup table
"""
data_type = 16    # 16 or 32 signed integer
samp_freq = 32000
f_sine = 400

# periods
samp_per = 1./samp_freq
sine_per = 1./f_sine

# compute time instances
t_vals = np.arange(0, sine_per*1, samp_per)
LOOKUP_SIZE = len(t_vals)
n_vals = np.arange(LOOKUP_SIZE)

# compute the sine table
MAX_SINE = 2**(data_type-1)-1   # [-(2*data_type-1), 2**(data_type-1)]
w_mod = 2*np.pi*(f_sine/samp_freq)
sine_table = np.sin(w_mod*n_vals) * MAX_SINE
if data_type == 16:
    sine_table = sine_table.astype(np.int16)
    print_type = np.uint16
    print_format = '0x%04x'
elif data_type == 32:
    sine_table = sine_table.astype(np.int32)
    print_type = np.uint32
    print_format = '0x%08x'
else:
    raise ValueError("Invalid data type!")

"""
Print C code.
"""
print('#define SINE_TABLE_SIZE', str(LOOKUP_SIZE))
print('#define SIN_MAX', (print_format % MAX_SINE))
if data_type == 16:
    print('const int16_t sine_table[SINE_TABLE_SIZE] = {')
elif data_type == 32:
    print('const int32_t sine_table[SINE_TABLE_SIZE] = {')
print(','.join([print_format % i.astype(print_type) for i in sine_table]))
print('};')

"""
Visualize
"""
plt.figure()
plt.stem(n_vals, sine_table)
plt.grid()
plt.autoscale(enable=True, axis='x', tight=True)
plt.xlabel("Index")
plt.title("Sine table for %d Hz at %d Hz sampling rate" % (f_sine, samp_freq))
plt.show()


import numpy as np

def sigmoid(x, derivative=False, x_precomp=False):
    """Sigmoid activation function. Works on scalars, vectors, matrices.
    Set derivative to true to compute derivative (wrt to x)"""
    s = 1.0/(1.0+np.exp(-x))
    if not derivative:
        return s
    else:
        if not x_precomp:
            return s*(1.0-s)
        else:
            return x*(1.0-x)

def column_vector(v):
    if v.shape[0] == 1:
        return v.T
    else:
        return v

def cross_entropy(output, target):
    """Get cross entropy between ouput and desired target"""
    output = np.array(output)
    target = np.array(target)
    s = -1.0/float(len(output))
    return s*np.sum(target*np.log(output)+(1-target)*np.log(1-output))

def sum_of_squares_error(output, target, derivative=False):
    """Sum of squares error function"""
    if not derivative:
        return .5*np.sum(np.power(np.subtract(output, target), 2))
    else:
        return output - target


import numpy as np

def err_div(x, y, ex, ey):
    Q = x / y
    dQ = np.abs(Q) * np.sqrt((ex / x) ** 2 + (ey / y) ** 2)
    return Q, dQ

def err_multiply(x, y, ex, ey):
    Q = x * y
    dQ = np.abs(Q) * np.sqrt( (ex/x)**2 + (ey/y)**2 )
    return Q, dQ

def err_add(x, y, ex, ey):
    Q = x + y
    dQ = np.sqrt(ex**2 + ey**2)
    return Q, dQ

def err_power_1(x, p, ex):
    """error propogation for
    x^p where p is a constant
    """
    Q = x ** p
    dQ = np.abs(p * x**(p-1) * ex)
    return Q, dQ

def err_power_2(x, p, ex, ep):
    """error propogation for
    x^p where x & p have errors
    """
    Q = x ** p
    dQ_sq = Q**2 * ( (p * ex / x)**2 + (np.log(x) * ep)**2 )
    return Q, dQ_sq**0.5

def log_err(x, dx):
    return dx / (np.log(10) * x)

def log_errorbar(x, dx):
    logxerr = log_err(x,dx)
    logxupp = np.log10(x) + logxerr
    logxlow = np.log10(x) - logxerr
    xupp = 10**logxupp - x
    xlow = x - 10**logxlow
    return xupp, xlow

# 1st moment error is v * sqrt(2) sigma_w / w

# 2nd moment error


#!/usr/bin/env python3
# Marcos del Cueto
# Import libraries
import math
import random
import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.kernel_ridge import KernelRidge

# Initialize lists and set random seed
list_x = []
list_y = []
list_x_pred = []
list_y_real = []
random.seed(2020)
# Create database with 21 points following quasi-lienar relation in interval x:[-5,5]
for i in range(-10,11):
    x = i/2
    rnd_number= random.uniform(-1,1)
    y = (x+4)*(x+1)*(x-1)*(x-3) + rnd_number
    list_x.append(x)
    list_y.append(y)
    print(x,y)
# Create list with 1060 points in interval x:[-5,5]
for i in range(-50,51):
    x = 0.1*i
    list_x_pred.append(x)
    list_y_real.append((x+4)*(x+1)*(x-1)*(x-3))
# Transform lists to np arrays
list_x = np.array(list_x).reshape(-1, 1)
list_x_pred = np.array(list_x_pred).reshape(-1, 1)
# Do linear regression using database with 21 points
list_y_pred = []
short_list_y_pred = []
rmse_list = []
# For each of the tested polynomial degree values
for alpha_value in [0.0001,0.1,10000,100000000]:
    krr = KernelRidge(alpha=alpha_value,kernel='polynomial',degree=4)
    krr.fit(list_x,list_y)
    list_y_pred.append(krr.predict(list_x_pred))
    new_y = krr.predict(list_x)
    short_list_y_pred.append(new_y)
    # Print rmse value
    rmse = math.sqrt(mean_squared_error(new_y, list_y))
    rmse_list.append(rmse)
    print('###########################')
    print('Alpha:', alpha_value)
    print('Root-mean-square error: %.1f' % rmse)
# Set axes and labels
fig, axs = plt.subplots(2, 2)
for ax in axs.flat:
    ax.set(xlabel='x', ylabel='y')
# Hide x labels and tick labels for top plots and y ticks for right plots.
for ax in axs.flat:
    ax.label_outer()
# Subplot top-left
axs[0, 0].scatter(list_x, list_y,color='C0')
axs[0, 0].plot(list_x_pred,list_y_pred[0],color='C1')
axs[0, 0].set_title(r'$\alpha = 10^{-4}$')
axs[0, 0].set_xlim(-5.5,5.5)
axs[0, 0].set_ylim(-100,500)
axs[0, 0].annotate(u'$RMSE$ = %.1f' % rmse_list[0], xy=(0.15,0.85), xycoords='axes fraction')
# Subplot top-right
axs[0, 1].scatter(list_x, list_y,color='C0')
axs[0, 1].plot(list_x_pred,list_y_pred[1], color='C1')
axs[0, 1].set_title(r'$\alpha = 10^{-1}$')
axs[0, 1].set_xlim(-5.5,5.5)
axs[0, 1].set_ylim(-100,500)
axs[0, 1].annotate(u'$RMSE$ = %.1f' % rmse_list[1], xy=(0.15,0.85), xycoords='axes fraction')
# Subplot bottom-left
axs[1, 0].scatter(list_x, list_y,color='C0')
axs[1, 0].plot(list_x_pred,list_y_pred[2], color='C1')
axs[1, 0].set_title(r'$\alpha = 10^{4}$')
axs[1, 0].set_xlim(-5.5,5.5)
axs[1, 0].set_ylim(-100,500)
axs[1, 0].annotate(u'$RMSE$ = %.1f' % rmse_list[2], xy=(0.15,0.85), xycoords='axes fraction')
# Subplot bottom-right
axs[1, 1].scatter(list_x, list_y,color='C0')
axs[1, 1].plot(list_x_pred,list_y_pred[3], color='C1')
axs[1, 1].set_title(r'$\alpha = 10^{8}$')
axs[1, 1].set_xlim(-5.5,5.5)
axs[1, 1].set_ylim(-100,500)
axs[1, 1].annotate(u'$RMSE$ = %.1f' % rmse_list[3], xy=(0.15,0.85), xycoords='axes fraction')
# Print plot to file
file_name='Figure_4.png'
plt.savefig(file_name,format='png',dpi=600)
plt.close()


# -*- coding: utf-8 -*-
"""
Created on Tue Nov  9 20:47:01 2021

@author: Oscar
"""

# importing modules
import numpy as np
import matplotlib.pyplot as plt
import math
# ------------------------------------------------------
# inputs
# functions that returns dy/dx
# i.e. the equation we want to solve: dy/dx = - y
def model(y,x):
    dydx = y*(1 - y)
    return dydx
# initial conditions
x0 = 0
y0 = math.exp(-4)/ (math.exp(-4)+1)
# total solution interval
x_final = 10
# step size
h = 0.1
# ------------------------------------------------------
# Euler method

# number of steps
n_step = math.ceil(x_final/h)

# Definition of arrays to store the solution
y_eul = np.zeros(n_step+1)
x_eul = np.zeros(n_step+1)

# Initialize first element of solution arrays
# with initial condition
y_eul[0] = y0
x_eul[0] = x0

# Populate the x array
for i in range(n_step):
    x_eul[i+1] = x_eul[i] + h

# Apply Euler method n_step times
for i in range(n_step):
    # compute the slope using the differential equation
    slope = model(y_eul[i],x_eul[i])
    # use the Euler method
    y_eul[i+1] = y_eul[i] + h * slope
# ------------------------------------------------------
# super refined sampling of the exact solution c*e^(-x)
# n_exact linearly spaced numbers
# only needed for plotting reference solution
# Definition of array to store the exact solution
n_exact = 1000
x_exact = np.linspace(0,x_final,n_exact+1)
y_exact = np.zeros(n_exact+1)
# exact values of the solution
for i in range(n_exact+1):
    y_exact[i] = math.exp(x_exact[i]-4)/(math.exp(x_exact[i]-4) + 1)
# ------------------------------------------------------
# ------------------------------------------------------
# plot results
plt.plot(x_eul, y_eul , 'b.-',x_exact, y_exact ,'r-')
plt.xlabel('x')
plt.ylabel('y(x)')
plt.show()
# ------------------------------------------------------


import pandas as pd
import numpy as np

from datetime import datetime
import pandas as pd

from scipy import optimize
from scipy import integrate

# %matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
from plotly.tools import mpl_to_plotly
import seaborn as sns

sns.set(style="darkgrid")

mpl.rcParams['figure.figsize'] = (16, 9)
pd.set_option('display.max_rows', 500)

df_analyse=pd.read_csv('data/processed/datetime.csv',sep=';')
df_analyse.sort_values('date',ascending=True).head()

N0=1000000 #max susceptible population
beta=0.4   # infection spread dynamics
gamma=0.1  # recovery rate
# condition I0+S0+R0=N0
I0=df_analyse.Germany[35]
S0=N0-I0
R0=0

def SIR_model_t(SIR,t,beta,gamma):
    ''' Simple SIR model
        S: susceptible population
        t: time step, mandatory for integral.odeint
        I: infected people
        R: recovered people
        beta:

        overall condition is that the sum of changes (differnces) sum up to 0
        dS+dI+dR=0
        S+I+R= N (constant size of population)

    '''

    S,I,R=SIR
    dS_dt=-beta*S*I/N0          #S*I is the
    dI_dt=beta*S*I/N0-gamma*I
    dR_dt=gamma*I
    return dS_dt,dI_dt,dR_dt

def fit_odeint(x, beta, gamma):
    '''
    helper function for the integration
    '''
    ydata = np.array(df_analyse.Germany[35:])
    t=np.arange(len(ydata))
    return integrate.odeint(SIR_model_t, (S0, I0, R0), t, args=(beta, gamma))[:,1]
    # we only would like to get dI

def SIR_model(SIR,beta,gamma):
    ''' Simple SIR model
        S: susceptible population
        I: infected people
        R: recovered people
        beta:

        overall condition is that the sum of changes (differnces) sum up to 0
        dS+dI+dR=0
        S+I+R= N (constant size of population)

    '''

    S,I,R=SIR
    dS_dt=-beta*S*I/N0          #S*I is the
    dI_dt=beta*S*I/N0-gamma*I
    dR_dt=gamma*I
    return([dS_dt,dI_dt,dR_dt])

def SIR_fig():
    I0=df_analyse.Germany[35]
    S0=N0-I0
    R0=0
    SIR=np.array([S0,I0,R0])
    propagation_rates=pd.DataFrame(columns={'susceptible':S0,
                                            'infected':I0,
                                            'recoverd':R0})

    for each_t in np.arange(100):

        new_delta_vec=SIR_model(SIR,beta,gamma)

        SIR=SIR+new_delta_vec

        propagation_rates=propagation_rates.append({'susceptible':SIR[0],
                                                    'infected':SIR[1],
                                                    'recovered':SIR[2]}, ignore_index=True)
    ydata = np.array(df_analyse.Germany[35:])
    t=np.arange(len(ydata))
    # ensure re-initialization
    I0=ydata[0]
    S0=N0-I0
    R0=0
    popt=[0.4,0.1]
    fit_odeint(t, *popt)
    popt, pcov = optimize.curve_fit(fit_odeint, t, ydata)
    perr = np.sqrt(np.diag(pcov))
    fitted=fit_odeint(t, *popt)
    fig, ax = plt.subplots()
    plt.semilogy(t, ydata, 'o')
    plt.semilogy(t, fitted)
    plt.title("Fit of SIR model for Germany cases")
    plt.ylabel("Population infected")
    plt.xlabel("Days")
    #plt.show()
    html_fig = mpl_to_plotly(fig)
    return html_fig


from sympy.solvers import solve
from sympy import Symbol
from sympy import *
import matplotlib.pyplot as plt
import numpy as np
from scipy.sparse import *
from numpy.linalg import inv
from array import *
from scipy import linalg
x=Symbol('x')

# function to find k and f for linear elements
def kfmatixLin(nEle,domainLen,a,c,f):
    xb=[]
    xa=[]
    for i in range(1,nEle+1):
        xb.append(i*(domainLen/nEle))
        xa.append((i-1)*(domainLen/nEle))
    k=[]
    Ftemp=[]
    # for ith element  # NOTE: [[element 1 k's],[element 2 k's], ...]
    for i in range(nEle):
        psi_1 = (xb[i]-x)/(xb[i]-xa[i])
        psi_2 = (x-xa[i])/(xb[i]-xa[i])
        # print(psi_1)
        # print(psi_2)
        k.append([])
        Ftemp.append(integrate(f*psi_1 ,(x, xa[i], xb[i])))
        Ftemp.append(integrate(f*psi_2 ,(x, xa[i], xb[i])))
        k[i].append(integrate( a*diff(psi_1,x)*diff(psi_1,x)+c*psi_1*psi_1,(x, xa[i], xb[i])))
        k[i].append(integrate( a*diff(psi_1,x)*diff(psi_2,x)+c*psi_1*psi_2,(x, xa[i], xb[i])))
        k[i].append(integrate( a*diff(psi_2,x)*diff(psi_1,x)+c*psi_2*psi_1,(x, xa[i], xb[i])))
        k[i].append(integrate( a*diff(psi_2,x)*diff(psi_2,x)+c*psi_2*psi_2,(x, xa[i], xb[i])))

    F=[]
    F.append(Ftemp[0])
    for i in range(0,len(Ftemp)-2,2):
        F.append(Ftemp[i+1]+Ftemp[i+2])
    F.append(Ftemp[len(Ftemp)-1])

    print('k = ',k)
    # print('Ftemp',Ftemp)
    # print('F = ',F)
    # two diagonals of the tridiagonal matrix
    diagA=[]
    diagB=[]
    # for three element 4*4 k matrix
    for i in range(nEle):
        diagA.append(k[i][1])
    # print('diagA',diagA)
    # NOTE: no need for diagC as it will always be same as diagA

    diagB.append(k[0][0])
    for i in range(nEle-1):
        diagB.append(k[i][3]+k[i+1][0])
    diagB.append(k[nEle-1][3])
    # print('diagB',diagB)
    diagA=np.array(diagA, dtype=np.float64)
    diagB=np.array(diagB, dtype=np.float64)

    K = np.array( diags([diagB,diagA,diagA], [0,-1, 1]).todense() )
    return(K,F)

# function to find Q at the left and right ends
def solQ(k,f,nEle,bcs,method):

    k_modified = k[1:len(k[0])-1,1:len(k[0])-1]
    # print(k_modified)
    f_modified = f[1:len(f)-1]
    # print(f_modified)
    f_modified[0]=f_modified[0]-k[1][0]*bcs[0]-k[1][len(k[0])-1]*bcs[1]
    f_modified[1]=f_modified[1]-k[len(k)-2][0]*bcs[0]-k[len(k)-2][len(k[0])-1]*bcs[1]

    # print(f_modified)
    # print(linalg.inv(k_modified).dot(f_modified))
    sl=linalg.inv(k_modified).dot(f_modified)
    sol = []
    sol.append(bcs[0])
    for i in range(len(sl)):
        sol.append(sl[i])
    sol.append(bcs[1])
    # print(sol)
    return(sol)

# user inputs
domainLen=1
nEle=3
a=1
c=-1
f=-x*x
bcs=[0,10]

k,f=kfmatixLin(nEle,domainLen,a,c,f)
print("k",k)
print("\nf",f)
print("\nSolution",solQ(k,f,nEle,bcs,'linear'))

# for i in range(10):
#     nEle=i+3
#     print(nEle)
#     k,f=kfmatixLin(nEle,domainLen,a,c,f)
#     print("\nSolution",solQ(k,f,nEle,bcs,'linear'))

import numpy,from numpy,from scipy,from sympy


import numpy as np

def invert_matrix(mat_to_be_inverted):
    """
    Returns the inverse of the passed in matrix.
        :param A: The matrix to be inversed

        :return: The inverse of the matrix A
    """
    # assuming matrix to be invertible

    # Section 2: Make copies of A & I, AM & IM, to use for row ops
    n = len(mat_to_be_inverted)
    AM = mat_to_be_inverted.copy()
    I = np.identity(n)
    IM = I.copy()

    # Section 3: Perform row operations
    indices = list(range(n)) # to allow flexible row referencing ***
    for fd in range(n): # fd stands for focus diagonal
        fdScaler = 1.0 / AM[fd][fd]
        # FIRST: scale fd row with fd inverse.
        for j in range(n): # Use j to indicate column looping.
            AM[fd][j] *= fdScaler
            IM[fd][j] *= fdScaler
        # SECOND: operate on all rows except fd row as follows:
        for i in indices[0:fd] + indices[fd+1:]:
            # *** skip row with fd in it.
            crScaler = AM[i][fd] # cr stands for "current row".
            for j in range(n):
                # cr - crScaler * fdRow, but one element at a time.
                AM[i][j] = AM[i][j] - crScaler * AM[fd][j]
                IM[i][j] = IM[i][j] - crScaler * IM[fd][j]
    """
    # check is inverted matrix is correct
    print("this should be I")
    print(IM.dot(mat_to_be_inverted))
    """
    return IM


## 2. Calculating expected values ##

males_over50k = .241 * .669 * 32561
males_under50k = .759 * .669 * 32561
females_over50k = .241 * .331 * 32561
females_under50k = .759 * .331 * 32561

## 3. Calculating chi-squared ##

observed = [6662, 1179, 15128, 9592]
expected = [5249.8, 2597.4, 16533.5, 8180.3]
values = []

for i, obs in enumerate(observed):
    exp = expected[i]
    value = (obs - exp) ** 2 / exp
    values.append(value)

chisq_gender_income = sum(values)

## 4. Finding statistical significance ##

observed = [6662, 1179, 15128, 9592]
expected = [5249.8, 2597.4, 16533.5, 8180.3]

import numpy as np
from scipy.stats import chisquare

chisq_value, pvalue_gender_income = chisquare(observed, expected)

## 5. Cross tables ##

import pandas
table = pandas.crosstab(income["sex"], [income["race"]])
print(table)

## 6. Finding expected values ##

import pandas
from scipy.stats import chi2_contingency

table = pandas.crosstab(income["sex"], [income["race"]])
chisq_value, pvalue_gender_race, df, expected = chi2_contingency(table)

Probability Statistics Intermediate/Multi category chi-squared tests-173.py


import h5py
import matplotlib.pyplot as plt
import numpy as np
from sklearn.decomposition import PCA

# from utils import slicer, split, plot_red_comp,format_data, flat_2d

def pca_fit(n_components, train, test, shape):
    # Set and fit PCA
    pca = PCA(n_components=n_components)
    pca.fit(train)
    cum_exp = np.cumsum(pca.explained_variance_ratio_)
    # Reduce dimension
    test_reduced = pca.transform(test)
    # Recover data from the lower dimension
    test_recovered = pca.inverse_transform(test_reduced)
    # Calculate the MSE
    mse = np.mean((test_recovered - test) ** 2)
    # Reshape into a matrix
    test_recovered = test_recovered.reshape(shape)
    return pca, cum_exp, test_recovered, mse

def pca_eval(pca, data):
    # Flatten data
    shape = data.shape
    data = data.reshape((1, np.prod(data.shape)))
    # Reduce dimension
    reduced = pca.transform(data)
    # Recover data from the lower dimension
    rec_data = pca.inverse_transform(reduced)
    return rec_data.reshape(shape)

dt_fl = "nn_data.h5"
dt_dst = "scaled_data"

n_train = 0.8
n_valid = 0.1

# Open data file
f = h5py.File(dt_fl, "r")
dt = f[dt_dst]

# x_data = format_data(dt, wd=100)

# idxs = split(x_data.shape[0], n_train, n_valid)
# slc_trn, slc_vld, slc_tst = slicer(x_data.shape, idxs)

# trn = x_data[slc_trn[0]][0:2]
# vld = x_data[slc_vld[0]][0:2]

# # Flatten
# trn_flt = flat_2d(trn, 1)
# vld_flt = flat_2d(vld, 1)

idxs = split(dt.shape[0], n_train, n_valid)
slc_trn, slc_vld, slc_tst = slicer(dt.shape, idxs)

trn = dt[slc_trn]
vld = dt[slc_vld]

# Flatten
trn_flt = flat_2d(trn)
vld_flt = flat_2d(vld)

# Full PCA
pca = PCA()
pca.fit(trn_flt)

cum_exp = np.cumsum(pca.explained_variance_ratio_)

idx_95 = np.where(cum_exp > 0.95)[0][0]
idx_98 = np.where(cum_exp > 0.98)[0][0]
idx_99 = np.where(cum_exp > 0.99)[0][0]

plt.plot(cum_exp)
plt.grid(True)
plt.xlabel("Number of variables")
plt.ylabel("Cumulative explained variance ratio")
plt.plot([0, cum_exp.shape[0]-1], [0.95, 0.95], '--k', lw=1.5, alpha=0.75)
plt.plot([0, cum_exp.shape[0]-1], [0.98, 0.98], '--r', lw=1.5, alpha=0.75)
plt.plot([0, cum_exp.shape[0]-1], [0.99, 0.99], '--b', lw=1.5, alpha=0.75)
plt.legend(['Cumlative variance', '95%', '98%', '99%'])

shape = vld.shape
p95, cum_95exp, vld_95rec, mse_95 = pca_fit(idx_95 + 1, trn_flt, vld_flt, shape)
p98, cum_98exp, vld_98rec, mse_98 = pca_fit(idx_98 + 1, trn_flt, vld_flt, shape)
p99, cum_99exp, vld_99rec, mse_99 = pca_fit(idx_99 + 1, trn_flt, vld_flt, shape)

# vld = np.squeeze(vld)
# vld_95rec = np.squeeze(vld_95rec)
# vld_98rec = np.squeeze(vld_98rec)
# vld_99rec = np.squeeze(vld_99rec)

alg = "PCA"
i = 50
var = 1
# 95%
plot_red_comp(vld[i], vld_95rec[i], var,idx_95 + 1, mse_95, alg)
# 98%
plot_red_comp(vld[i], vld_98rec[i], var,idx_98 + 1, mse_98, alg)
# 99%
plot_red_comp(vld[i], vld_99rec[i], var,idx_99 + 1, mse_99, alg)

# trn_rec = pca_eval(p99, trn[50])
# plot_red_comp(trn[i], trn_rec,1, idx_99 + 1, mse_99, alg)

# Norm error
((np.sum(np.abs(vld[i] - vld_95rec[i])**2))**0.5)/(np.sum(np.abs(vld[i])**2))**0.5


import numpy as np
from math import e
import pandas as  pd

data=np.linspace(-1,1,19)
x_value=np.around(data,decimals=3)
count=(len(x_value))
print(count)
table=np.zeros((count,count))
y_value=[]
for i in x_value:
    y_value.append(np.around((e**i),decimals=3))
table[:,0]=y_value
#to calculate diffrence
for i in range(1, count):
    for j in range(count - i):
        table[j][i] = np.around(table[j + 1][i - 1] - table[j][i - 1],3)

#now to draw table
column_names = ["Y", "△1", "△2", "△3", "△4", "△5", "△6", "△7", "△8", "△9", "△10", "△11", "△12", "△13", "△14", "△15", "△16", "△17", "△18"]
draw=pd.DataFrame(table,x_value,column_names)
print(draw)


import numpy as np

__name__="Initialization"
def xavier_initiliazer(shape=None,uniform=True):
        seed = np.random.randint(1,1E3)
        random_engine = np.random.RandomState(seed=seed)
        if(uniform==True):
            range_init = np.sqrt(6.0/(shape[0]+shape[1]))
            return random_engine.uniform(-range_init,range_init,size=(shape[0],shape[1]))
        else:
            stdv = np.sqrt(3.0/(shape[0]+shape[1]))
            return random_engine.normal(0.0,scale=stdv,size=(shape[0],shape[1]))
def initialize_param(weights_layer,bias_layer,uniform_init=True):
    W = []
    b = []
    for weight in weights_layer:
        W.append(xavier_initiliazer(weight,uniform_init))
    for bias in bias_layer:
        b.append(np.zeros(bias))
    return W,b
def construct_candidate(neural_shape,uniform_init=True):
    weights_layer = [(neural_shape[t-1],neural_shape[t]) for t in range(1,len(neural_shape))]
    bias_layer = [neural_shape[t] for t in range(1,len(neural_shape))]
    W,b = initialize_param(weights_layer,bias_layer,uniform_init)
    param_network = zip(W,b)
    total_weights = []
    for param in param_network:
        temp_weight = np.concatenate([param[0].flatten(),param[1].flatten()])
        total_weights.append(temp_weight)
    return np.concatenate([total_weights[0],total_weights[1]]).flatten()
def construct_solution(number_of_solutions,neural_shape,uniform_init=True):
    return np.array([construct_candidate(neural_shape,uniform_init) for t in range(number_of_solutions)])


import matplotlib.pyplot as plt
import numpy as np
from numpy.random import exponential
import statsmodels
import scipy.stats

# PROBLEM 3
n = 10000
lambda_1 = 1
lambda_2 = 1 / 2
lambda_3 = 1 / 3
lambda_4 = 1 / 4
lambda_5 = 1 / 15
lambda_6 = 1 / 6

def longest_path_length(z1, z2, z3, z4, z5, z6):
    return max(z1 + z2 + z4, z1 + z3, z5 + z6)

def inverse_cdf_exponential_law(x,mean) :
    return mean * np.log(1 / (1-x))

## Simulations
Z1 = exponential(1/lambda_1,n)
Z2 = exponential(1/lambda_2,n)
Z3 = exponential(1/lambda_3,n)
Z4 = exponential(1/lambda_4,n)
Z5 = exponential(1/lambda_5,n)
Z6 = exponential(1/lambda_6,n)

L = []
for i in range(n) :
    L.append(longest_path_length(Z1[i],Z2[i],Z3[i],Z4[i],Z5[i],Z6[i]))
L = np.array(L)

# 1 - MC Standard
print("MC (standard) estimator : ", np.mean(L))
print("MC (standard) estimated variance : ", (1/n) * np.var(L))

# 2 - MC Control Variate
C = np.array([Z1 - np.mean(Z1),Z2 - np.mean(Z2), Z3 - np.mean(Z3),
              Z4 - np.mean(Z4),Z5 - np.mean(Z5), Z6 - np.mean(Z6)])

cov_vec = np.array([np.cov(L,C[0,])[0,1],np.cov(L,C[1,])[0,1],np.cov(L,C[2,])[0,1],
                    np.cov(L,C[3,])[0,1],np.cov(L,C[4,])[0,1],np.cov(L,C[5,])[0,1]])
Sigma = np.cov([C[0,],C[1,],C[2,],C[3,],C[4,],C[5,]])
lambda_star = np.dot(np.linalg.inv(Sigma), cov_vec)

L_CV = L - np.dot(lambda_star, C)
print("MC (control variate) estimator : ", np.mean(L_CV))
print("MC (control variate) estimated variance : ", (1/n) * np.var(L_CV))

# (1-alpha) % confidence interval.
alpha = 0.1
z = scipy.stats.norm.ppf(1-alpha/2)
inf = np.mean(L_CV) - (z/np.sqrt(n)) * np.var(L_CV)
sup = np.mean(L_CV) + (z/np.sqrt(n)) * np.var(L_CV)
print("({})% confidence interval : ".format((1-alpha)*100),"[",inf,",", sup, "]")

# 3 - MC Antithetic Variates
# TODO : Better to sample 6 uniforms at each simulation
U = np.random.uniform(0,1,n)

Z1_bis = [] ; Z1_bis_trans = []
Z2_bis = [] ; Z2_bis_trans = []
Z3_bis = [] ; Z3_bis_trans = []
Z4_bis = [] ; Z4_bis_trans = []
Z5_bis = [] ; Z5_bis_trans = []
Z6_bis = [] ; Z6_bis_trans = []

for i in range(n) :
    Z1_bis.append(inverse_cdf_exponential_law(U[i], 1 / lambda_1))
    Z2_bis.append(inverse_cdf_exponential_law(U[i], 1 / lambda_2))
    Z3_bis.append(inverse_cdf_exponential_law(U[i], 1 / lambda_3))
    Z4_bis.append(inverse_cdf_exponential_law(U[i], 1 / lambda_4))
    Z5_bis.append(inverse_cdf_exponential_law(U[i], 1 / lambda_5))
    Z6_bis.append(inverse_cdf_exponential_law(U[i], 1 / lambda_6))
    # antithetic tranformation of the sample U
    Z1_bis_trans.append(inverse_cdf_exponential_law(1-U[i], 1 / lambda_1))
    Z2_bis_trans.append(inverse_cdf_exponential_law(1-U[i], 1 / lambda_2))
    Z3_bis_trans.append(inverse_cdf_exponential_law(1-U[i], 1 / lambda_3))
    Z4_bis_trans.append(inverse_cdf_exponential_law(1-U[i], 1 / lambda_4))
    Z5_bis_trans.append(inverse_cdf_exponential_law(1-U[i], 1 / lambda_5))
    Z6_bis_trans.append(inverse_cdf_exponential_law(1-U[i], 1 / lambda_6))

L_anti = []
L_anti_trans = []
for i in range(n) :
    L_anti.append(longest_path_length(Z1_bis[i],Z2_bis[i],Z3_bis[i],
                                      Z4_bis[i],Z5_bis[i],Z6_bis[i]))
    L_anti_trans.append(longest_path_length(Z1_bis_trans[i], Z2_bis_trans[i],
                                            Z3_bis_trans[i], Z4_bis_trans[i],
                                            Z5_bis_trans[i], Z6_bis_trans[i]))
L_anti = np.array(L_anti)
L_anti_trans = np.array(L_anti_trans)

print("MC (antithetic variate) estimator : ",
      0.5 * np.mean(L_anti + L_anti_trans))
print("MC (antithetic variate) estimated variance : ",
      (0.25 / n) * np.var(L_anti + L_anti_trans))

# (1-alpha) % confidence interval.
alpha = 0.1
z = scipy.stats.norm.ppf(1-alpha/2)
inf = 0.5 * np.mean(L_anti + L_anti_trans) - (z/np.sqrt(n)) * np.var(L_var_red)
sup = 0.5 * np.mean(L_anti + L_anti_trans) + (z/np.sqrt(n)) * np.var(L_var_red)
print("({})% confidence interval : ".format((1-alpha)*100),"[",inf,",", sup, "]")

# PROBLEM 4
mean = np.array([0,0])
cov = np.array([[1,-0.7],[-0.7,1]])
# Simluations
n = 100000
X_Y = np.random.multivariate_normal(mean, cov, size=n)
# np.cov(X_Y[:,0], X_Y[:,1])
# Plots
plt.scatter(x=X_Y[:,0], y=X_Y[:,1])
plt.vlines(3, ymin=3, ymax = 8, color = "red", linestyles='solid')
plt.hlines(3, xmin=3, xmax = 8, color = "red")
plt.title("n = {} simulations".format(n))
plt.show()

# zone = [(x,y) for x,y in X_Y if x >= 1 and y >= 1]
# while len(zone) == 0 :
#     X_Y = np.random.multivariate_normal(mean, cov, size=n)
#     zone = [(x, y) for x, y in X_Y if x > 3 and y > 3]

# Importance Sampling
n = 100000
mean_2 = np.array([3,3])
Z = np.random.multivariate_normal(mean_2, cov, size=n)
f = scipy.stats.multivariate_normal(mean=[0,0], cov=[[1,-0.7],[-0.7,1]])
g = scipy.stats.multivariate_normal(mean=[3,3], cov=[[1,-0.7],[-0.7,1]])

# Plots
plt.scatter(x=Z[:,0], y=Z[:,1])
plt.vlines(3, ymin=3, ymax = 8, color = "red", linestyles='solid')
plt.hlines(3, xmin=3, xmax = 8, color = "red")
plt.title("n = {} simulations".format(n))
plt.show()

ratio = [] # only if Z belongs to A
for i in range(n) :
    if Z[i][0] >= 3 and Z[i][1] > 3 :
        ratio.append(f.pdf(Z[i]) / g.pdf(Z[i]))
ratio = np.array(ratio)

print("p_n = ", np.mean(ratio))

import numpy,from numpy,import scipy,import statsmodels


import numpy as np

def leastsq(x: np.ndarray, y: np.ndarray, sigma: float) -> 'tuple[[float, float], [float, float]]':
    """ Computes the least squares of the 1D vectors x and y.
    Raises:
        ValueError: If the lengths of the arrays are not equal.
        ValueError: If the array x is empty. It is checked after the lengths so y would also be empty.
    Returns:
        tuple((float, float), (float, float)): Returns a tuple containing two tuples.
            The first one contains at position 0 the slope (m in literature) and at position 1 its error.
            The second one contains at position 0 the y-intercept (b in literature) and at position 1 its error.
    """
    n = len(x)

    if n != len(y):
        raise ValueError(f'Length of the data array must be equal, length of x is {n} and y is {len(y)}. Please check. ')

    if n == 0:
        raise ValueError('Arrays cannot be empty. Please check.')

    sum_x, sum_y = np.sum(x), np.sum(y)
    sum_x2 = np.sum(x*x)
    sum_xy = np.sum(x*y)

    div = 1 / (sum_x**2 - n*sum_x2)
    m = (sum_x*sum_y - n*sum_xy) * div
    b = (sum_x*sum_xy - sum_y*sum_x2) * div

    m_e = np.sqrt(n*sigma**2 * (-1) * div)
    b_e = np.sqrt(sum_x2*sigma**2 * (-1) * div)

    return (m, m_e), (b, b_e)

if __name__ == '__main__':

    x = eval('[' + input("Introduce x data separated by ,:") + ']')
    y = eval('[' + input("Introduce y data separated by ,:") + ']')

    sigma = float(input("Introduce Sigma: "))

    leastsq = leastsq(np.array(x), np.array(y), sigma)

    print(f'm={leastsq[0][0]}  m_error={leastsq[0][1]} \n b={leastsq[1][0]}  b_error={leastsq[1][1]}')


import numpy as np
import matplotlib.pyplot as plt

#Part a
#Text Exercise 6.10 a
#we can modify the number of loops to acheive different levels of accuracy
#in our case we achieved 10^-6 by setting the number of loops to 15
xa=1.0
numOfLoops = 15
for k in range(numOfLoops):
    xa = 1 - np.exp(-2*xa)

print("value of x with accuracy 10**-6:", xa)
print("value of 1 - exp(-2*x) with accuracy 10**-6:", 1 - np.exp(-2*xa))

#Text Exercise 6.10 b
#similar as above, we first reset the variables and instead of a constant c
#we generated a range of values from 0 to 300 with a stepsize of 0.01
x=1.0
numOfLoops = 15
c = np.arange(0, 3, 0.01)
for k in range(numOfLoops):
    x = 1 - np.exp(-c*x)

fig, graph = plt.subplots()
graph.plot(c, x)
graph.set(xlabel="c", ylabel="x",
       title="Solutions of x for a range of c=(0, 3, 0.01) (Q3a)")
graph.grid()
fig.savefig("q3_A.png")
plt.show()

#Part b
#Text Exercise 6.11 b
def F(c):
    """
    This function uses the similar method as above and find the iterations
    when it approaches the convergence.
    """
    sigdig = 10 ** -6
    iterations = 1
    def f(x):
        return 1 - np.exp(-c*x)

    def error(x1, x2):
        return (x1 - x2) / (1 - 1 / (c * np.exp(-c * x1)))

    x1 = 1.0 # starting value
    x2 = f(x1)
    while(abs(error(x1, x2)) > sigdig):
        x1, x2 = x2, f(x2)
        iterations += 1
    print('The minimum number of iterations for an accuracy of 10**-6 = ', iterations)
    print("value of x:", x2)
    print("value of 1 - exp(-2*x):", 1 - np.exp(-2*x2))

#Text Exercise 6.11 c
def F_over(c, w):
    """
    This function uses the over-relaxation method to increases the rate of
    convergence by artifically increasing the gradient of f(x)
    """
    sigdig = 10 ** -6
    iterations = 1
    def f(x):
        return 1 - np.exp(-c*x)

    def derivf(x):
        return c * np.exp(-c*x)

    def error(x1, x2):
        return (x1 - x2)/(1 - 1/((1 + w)*derivf(x1) - w))

    x1 = 1.0  # starting value
    x2 = (1 + w) * f(x1) - w * x1
    while abs(error(x1, x2)) > sigdig:
        x1, x2 = x2, (1 + w) * f(x2) - w * x2
        iterations += 1
    print('When we set omega to be ', w,'the minimum number of iterations for an accuracy of 10**-6 is ', iterations)
    print("value of x:", x2)
    print("value of 1 - exp(-2*x):", 1 - np.exp(-2*x2))

print()
print("part b answer")
F(2)
print()
F_over(2, 0.5)

#Part c
sigfig = 10 ** -6
a = 1
b = 2
def f(x, y):
    return y*(a+x**2)

def g(x, y):
    return b/(a+x**2)

def F(x,y):
    return np.sqrt(b/y-a)

def G(x,y):
    return x/(a+x**2)

def solution(f, g):
    iterations = 1
    def delta(x1, x2):
        return (x1 - x2) / x1
    x1 = 0.5
    y1 = 0.5
    x2 = f(x1, y1)
    y2 = g(x1, y1)

    while abs(delta(x1, x2)) > sigfig and abs(delta(y1, y2)) > sigfig:
        if iterations > 1000:
            return 'error'

        x1, x2, y1, y2 = x2, f(x2, y2), y2, g(x2, y2)
        iterations += 1
    print('The number of iterations = ', iterations, 'the solution for x ~', x2, 'the solution for y ~', y2)
    return [ x2, y2 ]

print()
print("part c answer")
print(solution(F,G))


import numpy as np
##Import other packages here##
import cupy as cp
import matplotlib.pyplot as plt
from cupy.fft import fftfreq, fft, ifft
import math
from cupyx import scipy

# cupy source
# https://docs-cupy.chainer.org/_/downloads/en/v7.0.0/pdf/

# 1a) install cupy
# fft.fftfreq to get frequency array corresponding to number of sample points
# cupy.fft.fftfreq #Return the FFT sample frequencies.

# compute the fft of the full signal
# cupy.fft.fft #Compute the one-dimensional FFT.

# invert the filtered fft with numpy.fft.ifft
# cupy.fft.ifft #Compute the one-dimensional inverse FFT.

# move the array to the host.
# plot -> plt.plot(cp.asnumpy(x), cp.asnumpy(y))

def cupy_fft():

    """
    Modify the code below so it calls the cupy fft function
    """

    pts = 1000
    L = 100
    w0 = 2.0 * cp.pi/L
    n1, n2, n3 = 10.0, 20.0, 30.0
    a1, a2, a3 = 1., 2., 3.

    #create signal data with 3 frequency components
    x = cp.linspace(0,L,pts)
    y1 = a1*cp.cos(n1*w0*x)
    y2 = a2*cp.sin(n2*w0*x)
    y3 = a3*cp.sin(n3*w0*x)
    y = y1 + y2 + y3

    #create signal including only 2 components
    y12 = y1 + y2

    #analytic derivative of signal
    dy = w0*(-n1*a1*cp.sin(n1*w0*x)
            +n2*a2*cp.cos(n2*w0*x)
            +n3*a3*cp.cos(n3*w0*x) )

    #use fft.fftfreq to get frequency array corresponding to number of sample points
    freqs = fftfreq(pts)
    #compute number of cycles and radians in sample window for each frequency
    nwaves = freqs*pts
    nwaves_2pi = w0*nwaves

    # compute the fft of the full signal
    fft_vals = fft(y)

    #mask the negative frequencies
    mask = freqs>0
    #double count at positive frequencies
    fft_theo = 2.0 * cp.abs(fft_vals/pts)
    #plot fft of signal
    plt.xlim((0,50))
    plt.xlabel('cycles in window')
    plt.ylabel('original amplitude')
    plt.plot(cp.asnumpy(nwaves[mask]), cp.asnumpy(fft_theo[mask]))
    plt.show()

    #create a copy of the original fft to be used for filtering
    fft_new = cp.copy(fft_vals)
    #filter out y3 by setting corr. frequency component(s) to zero
    fft_new[cp.abs(nwaves)==n3] = 0.
    #plot fft of filtered signal
    plt.xlim((0,50))
    plt.xlabel('cycles in window')
    plt.ylabel('filtered amplitude')
    plt.plot(cp.asnumpy(nwaves[mask]), cp.asnumpy(2.0*cp.abs(fft_new[mask]/pts)))
    plt.show()

    #invert the filtered fft with numpy.fft.ifft
    filt_data = cp.real(ifft(fft_new))
    #plot filtered data and compare with y12
    plt.plot(cp.asnumpy(x), cp.asnumpy(y12), label='original signal')
    plt.plot(cp.asnumpy(x), cp.asnumpy(filt_data), label='filtered signal')
    plt.xlim((0,50))
    plt.legend()
    plt.show()

    #multiply fft by 2*pi*sqrt(-1)*frequency to get fft of derivative
    dy_fft = 1.0j*nwaves_2pi*fft_vals
    #invert to reconstruct sampled values of derivative
    dy_recon = cp.real(ifft(dy_fft))
    #plot reconstructed derivative and compare with analuytical version
    plt.plot(cp.asnumpy(x), cp.asnumpy(dy), label='exact derivative')
    plt.plot(cp.asnumpy(x), cp.asnumpy(dy_recon), label='fft derivative')
    plt.xlim((0,50))
    plt.legend()
    plt.show()

def cupy_filter():

    """
    Implement code below to:
    Create noise consisting of an array of pts random values chosen from a uniform distribution over the interval [−3,3]
    Create a noisy signal by adding noise to the original signal: y_n = y + noise
    Compute and plot the frequency content of the noisy signal.
    Create and apply an appropriate filter to suppress noise in the frequency domain.
    Invert the filtered fft to obtain a "denoised signal".
    Plot and compare the original, noisy, and denoised signals.
    """

    # pass
    pts = 1000
    L = 100
    w0 = 2.0 * cp.pi/L
    n1, n2, n3 = 10.0, 20.0, 30.0
    a1, a2, a3 = 1., 2., 3.

    #create signal data with 3 frequency components
    x = cp.linspace(0,L,pts)
    y1 = a1*cp.cos(n1*w0*x)
    y2 = a2*cp.sin(n2*w0*x)
    y3 = a3*cp.sin(n3*w0*x)
    y = y1 + y2 + y3

    #noisy signal: uniform distribution range[-3 3]
    ns = cp.random.uniform(-3, 3, pts)

    # y_n = y + noise
    y_n = y + ns

    # #create signal including only 2 components
    # y12 = y1 + y2

    # #analytic derivative of signal
    # dy = w0*(-n1*a1*cp.sin(n1*w0*x)
    #         +n2*a2*cp.cos(n2*w0*x)
    #         +n3*a3*cp.cos(n3*w0*x) )

    #use fft.fftfreq to get frequency array corresponding to number of sample points
    freqs = fftfreq(pts)
    #compute number of cycles and radians in sample window for each frequency
    nwaves = freqs*pts
    # nwaves_2pi = w0*nwaves

    # compute the fft of the full signal
    fft_vals = fft(y)

    # with noisy signal:
    fft_vals_ns = fft(y_n)

    #mask the negative frequencies
    mask = freqs>0

    #double count at positive frequencies
    fft_theo = 2.0 * cp.abs(fft_vals/pts)

    # with noisy signal:
    fft_theo_ns = 2.0 * cp.abs(fft_vals_ns/pts)

# plot
    #plot fft of signal
    plt.xlim((0,50))
    plt.xlabel('cycles in window')
    plt.ylabel('original amplitude')
    plt.plot(cp.asnumpy(nwaves[mask]), cp.asnumpy(fft_theo[mask]))
    plt.show()

    #create a copy of the original fft to be used for filtering
    fft_new = cp.copy(fft_vals)
    #filter out y3 by setting corr. amplitude component(s) to zero  ##helping source # checked about single side spectrum
    fft_new[cp.abs(fft_new)<=a1/2] = 0.
    #plot fft of filtered signal

# plot fft noisy signal
    plt.xlim((0,50))
    plt.xlabel('cycles in window')
    plt.ylabel('filtered amplitude')
    # plt.plot(cp.asnumpy(nwaves[mask]), cp.asnumpy(2.0*cp.abs(fft_new[mask]/pts)))
    plt.plot(cp.asnumpy(nwaves[mask]),cp.asnumpy(fft_theo_ns[mask]))
    plt.show()

    #invert the filtered fft with numpy.fft.ifft
    filt_data = cp.real(ifft(fft_new))

    #plot filtered data and compare origin, noisy signal, denoised signal
    plt.plot(cp.asnumpy(x), cp.asnumpy(y), label='original signal')
    plt.plot(cp.asnumpy(x), cp.asnumpy(y_n), label='filtered signal')
    plt.plot(cp.asnumpy(x), cp.asnumpy(filt_data), label='denoised signal')
    plt.xlim((0,50))
    plt.legend()
    plt.show()

    # #multiply fft by 2*pi*sqrt(-1)*frequency to get fft of derivative
    # dy_fft = 1.0j*nwaves_2pi*fft_vals
    # #invert to reconstruct sampled values of derivative
    # dy_recon = cp.real(ifft(dy_fft))
    # #plot reconstructed derivative and compare with analuytical version
    # plt.plot(cp.asnumpy(x), cp.asnumpy(dy), label='exact derivative')
    # plt.plot(cp.asnumpy(x), cp.asnumpy(dy_recon), label='fft derivative')
    # plt.xlim((0,50))
    # plt.legend()
    # plt.show()

def cupy_eig(mat):
    """
    Returns the eigen values of square matrix mat
    """
    # https://docs-cupy.chainer.org/en/stable/reference/generated/cupy.linalg.eigh.html
    w, v = cp.linalg.eigh(mat)
    # w eigenvalues
    # v eigenvectors
    return w, v

def cupy_J(n):
    """
    Constructs the nxn matrix J (as described in q2)
    Returns J, leading eigenvalue of J, and the associated eigenvector
    """
    # main diagonal where each of the entries is 1/2
    # Use cupyx.scipy.spare.diags()
    # http://wwwens.aero.jussieu.fr/lefrere/master/SPE/docs-python/scipy-doc/generated/scipy.sparse.diags.html
    # https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.diags.html
    # 𝑛×𝑛 matrix 𝐽(𝑛) full of zeros except for the diagonals adjacent to the main diagonal where each of the entries is  1/2
    J = scipy.sparse.diags([1/2, 1/2], [1,-1], shape=(n,n)).toarray()
    w, v = cupy_eig(J)

    # max leading eigval (one with large magnitude)
    idx = cp.argmax(abs(w))

    return J, abs(w[idx]), v[idx]

def rand_mat_gauss(n):
    """
    Returns nxn size array of random numbers sampled from a normal distribution with a mean of 0 and standard deviation of 1
    (Uses cupy to generate the random matrix)
    """
    # 3a)
    #B 𝑁(0,1) , the normal distribution with mean zero and standard deviation 1.
    # cupy.random.normal #Returns an array of normally distributed samples.
    B = cp.random.normal(0, 1, (n,n))
    A = (1/cp.sqrt(2)) * (B + cp.transpose(B))
    return A

def rand_mat_plusminus(n):
    """
    Returns nxn size array of random numbers uniformly distributed on the 2-value set {-1,1}
    OPTIONAL
    """
    # p4a)
    # repeat problem 3 but with matrix entries chosen uniformly from  {−1,1}
    # instead of from a normal distribution
    # cupy.random.rand #Returns an array of uniform random values over the in- terval[0, 1).
    # cupy.random.randint #Returns a scalar or an array of integer values over [low, high).
    # cupy.random.random_integers #Returnascalaroranarrayofintegervaluesover[low, high]

    B = cp.random.random_integers(-1, 1, (n,n))
    A = (1/2) * (B + cp.transpose(B))
    return A

def rand_mat_uniform(n):
    """
    Returns nxn size array of random numbers uniformly distrubted on the range [-1,1]
    OPTIONAL
    """
    # p4b)
    # Repeat 4a but with matrix entries chosen randomly from a uniform distribution on the interval  [−1,1]
    # produces an  𝑛×𝑛  array that is symmetric and any individual element is chosen randomly from  [−1,1] .
    B = cp.random.uniform(-1, 1, (n,n))
    A = (1/2) * (B + cp.transpose(B))
    return A

# https://stackoverflow.com/questions/42908334/checking-if-a-matrix-is-symmetric-in-numpy
def check_symmetric(a, rtol=1e-05, atol=1e-08):
    return np.allclose(a, a.T, rtol=rtol, atol=atol)

if __name__ == "__main__":

    #Fill in code here to call functions
    # pass

# Problem1)
    # 1b)
    cupy_fft()

    # 1c)
    cupy_filter()

#
#
#

# Problem2)
    # 2) For n = 50
    n = 50
    J, w, v = cupy_J(n)
    print('For n = {0}, The eigenvalue of leading eigenvalue: {1}'.format(n, w))
    print('For n = {0}, The eigenvector that associate with the leading eigenvalue: \n{1}'.format(n, v))
    # For n = 500
    n = 500
    J, w, v = cupy_J(n)
    print('For n = {0}, The eigenvalue of leading eigenvalue: {1}'.format(n, w))
    print('For n = {0}, The eigenvector that associate with the leading eigenvalue: \n{1}'.format(n, v))

    # What is the value of the leading eigenvalue and how does it behave as  𝑛  becomes large?
    print('As n increases, the leading eigenvalue also increases')
    print('The magnitude of eigenvector matrix are shows symmetric')

#
#
#

# problem 3)
    # 3b)
    # 10x10 array
    # Verify symmetric matrix
    # plot histogram N
    n = 10

    Anxn = rand_mat_gauss(n)
    # verify symmetric matrix
    check = check_symmetric(Anxn)
    print('This is {} that the matrix is symmetric'.format(check))

    # plot histogram N
    mu, sig = cp.mean(Anxn), cp.std(Anxn)
    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))

# # https://www.tutorialspoint.com/python_data_science/python_normal_distribution.htm

    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)
    s = cp.squeeze(Anxn)
    s = cp.asnumpy(s)

    plt.figure()
    count, bins, ignored = plt.hist(s, density=True)
    plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) *
             np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.show()

    # 3c)
    # Create a matrix m = rand_mat_gauss(n) with  𝑛=1000
    n = 1000
    m = rand_mat_gauss(n)

    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('3c) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('The normal distribution of the eigenvalues smoothly follows in histogram\n\n')
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

    # 3d)
    # repeat 3c with  𝑛=2000  and  𝑛=4000
    # For n = 2000
    n = 2000 #200
    m = rand_mat_gauss(n)

    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('3d) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('The normal distribution of the eigenvalues smoothly follows in histogram')
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

    # For n = 4000
    n = 4000
    m = rand_mat_gauss(n)

    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('3d) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('The normal distribution of the eigenvalues smoothly follows in histogram')
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

# What features are independent of  𝑛 ?
    print('As shown in graph when n is increases, they are proportionally increases. \nThe distribution of the eigenvalues are independent of n. \nLeading value of the eigvalues are proprotional to n')

#
#
#

# problem4)
    # p4a)
    # epeat problem 3 but with matrix entries chosen uniformly from  {−1,1}
    # instead of from a normal distribution
    # a)
    # For n = 1000, 2000, 4000
# For n = 1000
    n = 1000
    m = rand_mat_plusminus(n)
    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))
    # For this problem, exclude the largest eigenvalue before computing the histogram.
    # max leading eigval (one with large magnitude)
    idx = cp.argmax(abs(w))
    w = np.delete(cp.asnumpy(w), cp.asnumpy(idx))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('4a) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

# For n = 2000
    n = 2000
    m = rand_mat_plusminus(n)
    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))
    # For this problem, exclude the largest eigenvalue before computing the histogram.
    # max leading eigval (one with large magnitude)
    idx = cp.argmax(abs(w))
    w = np.delete(cp.asnumpy(w), cp.asnumpy(idx))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('4a) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

# For n = 4000
    n = 4000
    m = rand_mat_plusminus(n)
    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))
    # For this problem, exclude the largest eigenvalue before computing the histogram.
    # max leading eigval (one with large magnitude)
    idx = cp.argmax(abs(w))
    w = np.delete(cp.asnumpy(w), cp.asnumpy(idx))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('4a) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

# When the distribution from which elements are chosen is changed, what is preserved and what changes?
    print('As shown in histgram, when n increases, histogram also increases.\n As n is increases the the distribution doesnt changing much.')

#
#
#
    # p4b)
    # b)
# Again explore the distribution of the eigenvalues and the dependence on  𝑛 . When the distribution is changed this time, what is preserved and what changes?
    # For n = 1000, 2000, 4000
# For n = 1000
    n = 1000
    m = rand_mat_uniform(n)
    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))
    # For this problem, exclude the largest eigenvalue before computing the histogram.
    # max leading eigval (one with large magnitude)
    idx = cp.argmax(abs(w))
    w = np.delete(cp.asnumpy(w), cp.asnumpy(idx))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('4b) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

# For n = 2000
    n = 2000
    m = rand_mat_uniform(n)
    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))
    # For this problem, exclude the largest eigenvalue before computing the histogram.
    # max leading eigval (one with large magnitude)
    idx = cp.argmax(abs(w))
    w = np.delete(cp.asnumpy(w), cp.asnumpy(idx))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('4b) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

# For n = 4000
    n = 4000
    m = rand_mat_uniform(n)
    # cupy to compute the eigenvalues of the matrix
    w, v = cupy_eig(m)
    # print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))
    # For this problem, exclude the largest eigenvalue before computing the histogram.
    # max leading eigval (one with large magnitude)
    idx = cp.argmax(abs(w))
    w = np.delete(cp.asnumpy(w), cp.asnumpy(idx))

    # plot the histrogram of the eigenvalues
    mu, sig = cp.mean(m), cp.std(m)
    mu = cp.asnumpy(mu)
    sigma = cp.asnumpy(sig)

    plt.figure()
    count, bins, ignored = plt.hist(cp.asnumpy(w), bins = 30)
    # plt.plot(bins, 1 / (sigma * np.sqrt(2 * np.pi)) * np.exp(- (bins - mu) ** 2 / (2 * sigma ** 2)), linewidth=3, color='y')
    plt.title('4b) n = ' + str(n))
    plt.show()

    print('For n = {0}, mu = {1}, sig = {2}'.format(n, mu, sig))
    print('For n = {0}, in rand_mat_gauss: \neigval = \n{1}'.format(n, w))

# Again explore the distribution of the eigenvalues and the dependence on  𝑛 . When the distribution is changed this time, what is preserved and what changes?
    print('As shown in the graph we can see now that it does not change as n increases. It shows that distribution are most likely set')

import numpy,import scipy,import cupy,from cupy


# **************************************************************************** #
#                                                                              #
#                                                         :::      ::::::::    #
#    trainer.py                                         :+:      :+:    :+:    #
#                                                     +:+ +:+         +:+      #
#    By: alngo <alngo@student.42.fr>                +#+  +:+       +#+         #
#                                                 +#+#+#+#+#+   +#+            #
#    Created: 2020/02/24 09:43:48 by alngo             #+#    #+#              #
#    Updated: 2020/03/02 10:19:17 by alngo            ###   ########.fr        #
#                                                                              #
# **************************************************************************** #

import numpy as np
import sys
from .utils.csv import read_csv, write_csv, check_csv
from .utils.plot import plot_gradient_descent
from .utils.maths import mean_squared_error, normalize, denormalize_theta
import matplotlib.pyplot as plt

class Trainer:
    def __init__(self, data_path=None, out_path=None, epochs=1000, lrate=0.5,
                 plot=False):
        """Trainer Model"""
        self.data_path = data_path
        self.out = out_path
        self.epochs = epochs
        self.lrate = lrate
        if lrate < 0 or lrate > 1:
            print(f"Invalid learning rate: expect a value between 0 and 1")
            sys.exit(1)
        self.plot = plot
        self.data = read_csv(self.data_path)

    @check_csv('km', 'price')
    def gradient_descent(self):
        p0 = np.float32(0.0)
        p1 = np.float32(0.0)
        cost = []
        p0_array = []
        p1_array = []

        [X, Xmin, Xmax] = normalize(
            self.data.iloc[:, 0].to_numpy(dtype=np.float32))
        [y, ymin, ymax] = normalize(
            self.data.iloc[:, 1].to_numpy(dtype=np.float32))

        m = np.float32(len(X))
        d = np.float32(1.0 / m)
        lrate = np.float32(self.lrate)

        for i in range(self.epochs):
            hyp = p0 + (X * p1)
            tmp0 = p0 - lrate * (d * sum(hyp - y))
            tmp1 = p1 - lrate * (d * sum((hyp - y) * X))
            p0 = tmp0
            p1 = tmp1
            p0_array.append(p0)
            p1_array.append(p1)
            cost.append(mean_squared_error(p0, p1, X, y))
            print(f"theta0: {p0} - theta1: {p1}")

        print(f"Mean squared error: {mean_squared_error(p0, p1, X, y)}")

        p0, p1 = denormalize_theta(self.data, X, p0, p1, ymin, ymax)

        data = {'theta0': p0, 'theta1': p1}

        if (self.out):
            write_csv(data=data, output=self.out)

        if (self.plot):
            plot_gradient_descent(X, y, p0_array, p1_array, cost)

        return data


# --------------
# Code starts here

import numpy as np

# Code starts here

# Adjacency matrix
adj_mat = np.array([[0,0,0,0,0,0,1/3,0],
                   [1/2,0,1/2,1/3,0,0,0,0],
                   [1/2,0,0,0,0,0,0,0],
                   [0,1,0,0,0,0,0,0],
                  [0,0,1/2,1/3,0,0,1/3,0],
                   [0,0,0,1/3,1/3,0,0,1/2],
                   [0,0,0,0,1/3,0,0,1/2],
                   [0,0,0,0,1/3,1,1/3,0]])

# Compute eigenvalues and eigencevectrs
eigenvalues, eigenvectors = np.linalg.eig(adj_mat)

# Eigen vector corresponding to 1
eigen_1 = abs(eigenvectors[:,0])/np.linalg.norm(eigenvectors[:,0], 1)

# most important page
#page = np.where(eigen_1 == eigen_1.max())
page = 8
print(eigen_1, page, sep = "\n")

# Code ends here

# --------------
# Code starts here

# Initialize stationary vector I
print(page)
adj2 = np.array([[  0,      0,      0,      0,      0,      0,      0,      0],
                    [0.5,   0,      0.5,    1/3,    0,      0,      0,      0],
                    [0.5,   0,      0,      0,      0,      0,      0,      0],
                    [0,     1,      0,      0,      0,      0,      0,      0],
                    [0,     0,      0.5,    1/3,    0,      0,      0.5,    0],
                    [0,     0,      0,      1/3,    1/3,    0,      0,      0.5],
                    [0,     0,      0,      0,      1/3,    0,      0,      0.5],
                    [0,     0,      0,      0,      1/3,    1,      0.5,    0]])
# Perform iterations for power method
print(adj_mat)
#init_I = np.zeros((8,11))
init_I=np.array([1,0,0,0,0,0,0,0])
#init_I[0,0] = 1
#print(init_I)
#print(init_I[:,1])

for i in range(10):
    #init_I[:,1] = np.dot(adj_mat, init_I[:,0])
    init_temp = np.dot(adj_mat, init_I)
    init_I = init_temp/np.linalg.norm(init_I,1)
    #init_I[:,i+1] = init_temp
    #init_I[:,i+1] = np.linalg.norm(init_I,1)

print(init_I)
power_page=8

# Code ends here

# --------------
# Code starts here

# New Adjancency matrix
# New Adjancency matrix
new_adj_mat = np.array([[0,0,0,0,0,0,0,0],
                   [1/2,0,1/2,1/3,0,0,0,0],
                  [1/2,0,0,0,0,0,0,0],
                   [0,1,0,0,0,0,0,0],
                   [0,0,1/2,1/3,0,0,1/2,0],
                   [0,0,0,1/3,1/3,0,0,1/2],
                   [0,0,0,0,1/3,0,0,1/2],
                   [0,0,0,0,1/3,1,1/2,0]])

# Initialize stationary vector I
new_init_I = np.array([1,0,0,0,0,0,0,0])

# Perform iterations for power method
for i in range(10):
    new_init_I = np.dot(new_adj_mat, new_init_I)/np.linalg.norm(new_init_I, 1)

print(new_init_I)

# Code ends here

# --------------
# Alpha value
alpha = 0.85

# Code starts here
n = len(new_adj_mat)
one = np.ones_like(new_adj_mat)
# Modified adjancency matrix
G = alpha*new_adj_mat + (one - alpha)*1/n

# Initialize stationary vector I
final_init_I = np.array([1,0,0,0,0,0,0,0])

# Perform iterations for power method
for i in range(1000):
    final_init_I = np.dot(G,final_init_I)/np.linalg.norm(final_init_I,1)

print(final_init_I)
# Code ends here


"""
What Color is Your Name?

Do names have colors?
Now they do.

Make a function that takes in a name (Any string two chars or longer really, but the name is the idea) and use the ascii values of it's substrings to produce the hex value of its color! Here is how it's going to work:

The first two hexadecimal digits are the SUM of the value of characters (modulo 256).
The second two are the PRODUCT of all the characters (again, modulo 256, which is one more than FF in hexadecimal).
The last two are the ABSOLUTE VALUE of the DIFFERENCE between the first letter, and the sum of every other letter. (I think you get the idea with the modulo thing).
For example "Jack" returns "79CAE5", which is... baby blue!

"Jack"  #  "J" = 74, "a" = 97, "c" = 99, "k" = 107

74 + 97 + 99 + 107 = 377                   -->  mod 256 = 121  -->  hex: 79
74 * 97 * 99 * 107 = 76036554              -->  mod 256 = 202  -->  hex: CA
74 - (97 + 99 + 107) = -229  --> abs: 229  -->  mod 256 = 229  -->  hex: E5
NOTE: The function should return None/nil when the input is less than two chars.
"""

import numpy

def string_color(name):
    if len(name) < 2:
        return None
    ascii_values = [ord(char) for char in name]
    first = sum(ascii_values) % 256
    product = numpy.prod(ascii_values) % 256
    last = abs(ascii_values[0] - sum(ascii_values[1:])) % 256
    return (format(first, 'x').zfill(2) + format(product, 'x').zfill(2) + format(last, 'x').zfill(2)).upper()


import numpy as np
from distributionLib import Dist

pois = Dist.poissonDist
xBar = Dist.expt
var = Dist.var
coolPrint = Dist.coolPrint
#test-1
'''
https://en.wikipedia.org/wiki/Poisson_distribution
Example
Ugarte and colleagues report that the average number of goals in a World Cup
soccer match is approximately 2.5 and the Poisson model is appropriate.
Because the average event rate is 2.5 goals per match, λ = 2.5.
'''
print('Test-1')
Lambda=2.5
k = 10 #(number of goals in match)
P = pois(Lambda,k,plot=True)
coolPrint(P)
print('-'*80)

# test-2
'''
https://www.intmath.com/counting-probability/13-poisson-probability-distribution.php
If electricity power failures occur according to a Poisson distribution with an
average of display 3 failures every twenty weeks, calculate the probability that
there will not be more than one failure during a particular week.
'''
Lambda = 3./20
k = 10
P = pois(Lambda,k,plot=True)
coolPrint(P)
#more than one failure
print('Number of Failure = {}'.format(np.sum(P[:2])))
print('-'*80)