# Question 5 Lab Assignment 2
# AB Satyaprakash - 180123062

# imports ----------------------------------------------------------------------
from math import erf, pow
import numpy as np
import matplotlib.pyplot as plt

# functions --------------------------------------------------------------------

def f(x):
    return erf(x)

def monomialBasis(X, F):
    xArrayNN = []
    for x in X:
        row = []
        for i in range(X.shape[0]):
            row.append(pow(x, i))
        xArrayNN.append(row)
    xArrayNN = np.array(xArrayNN)
    A = np.linalg.solve(xArrayNN, F)
    A = np.flip(A)
    return A

def lagrangeBasis(X, F):
    L = []  # to store a list of all li(x) polynomials
    for x in X:
        l = np.array([1])
        for i in range(X.shape[0]):
            if(X[i] != x):
                l = np.polymul(l, np.array([1, -X[i]]))
                const = x-X[i]
                l = l/const
        L.append(l)
    A = L[0]*F[0]
    for i in range(1, len(L)):
        A += L[i]*F[i]
    return A

def newtonBasis(X, F):
    N = []  # to store a list of all Nj(x) polynomials
    temp = np.array([1])
    for i in range(X.shape[0]):
        N.append(temp)
        temp = np.polymul(temp, np.array([1, -X[i]]))

    xArrayNN = []
    for j in range(X.shape[0]):
        row = []
        for i in range(X.shape[0]):
            if(i > j):
                row.append(0)
            else:
                row.append(np.polyval(N[i], X[j]))
        xArrayNN.append(row)
    xArrayNN = np.array(xArrayNN)
    A = np.linalg.solve(xArrayNN, F)

    newtonPoly = A[0]*N[0]
    for i in range(1, A.shape[0]):
        newtonPoly = np.polyadd(newtonPoly, A[i]*N[i])
    return newtonPoly

# ------------------------------------------------------------------------------
# Prepare the inputs X and F. Here X = [x0,x1,...,xn] and F = [f0,f1,...,fn]
# Given X = [1,1.2,1.4...,3] in question
X = np.arange(1, 3.2, 0.2)
F = np.zeros(X.shape[0])
for i in range(X.shape[0]):
    F[i] = f(X[i])

# Compute the interpolating polynomial P(f[x1, . . . , xn]) using:
# (i) Monomial basis
pxMonomial = monomialBasis(X, F)
print('The interpolating polynomial using monomial basis is \n{}\n'.format(np.poly1d(pxMonomial)))

# (ii) Lagrange basis
pxLagrange = lagrangeBasis(X, F)
print('The interpolating polynomial using lagrange basis is \n{}\n'.format(np.poly1d(pxLagrange)))

# (iii) Newton basis
pxNewton = newtonBasis(X, F)
print('The interpolating polynomial using newton basis is \n{}\n'.format(np.poly1d(pxNewton)))

# Plot the error for each of the 3 cases with z = (0 : 0.01 : 4) (here, 0.01 is step size)
Z = np.arange(0, 4.01, 0.01)
errMonomial, errLagrange, errNewton = [], [], []
for z in Z:
    errMonomial.append(abs(f(z)-np.polyval(pxMonomial, z)))
    errLagrange.append(abs(f(z)-np.polyval(pxLagrange, z)))
    errNewton.append(abs(f(z)-np.polyval(pxNewton, z)))

plt.plot(Z, errMonomial, color='red')
plt.title('Error between erf and the interpolating polynomial using Monomial Basis vs Z values')
plt.xlabel('Z values - 0: 0.01 : 4')
plt.ylabel('Error between erf and the interpolating polynomial')
plt.show()

plt.plot(Z, errLagrange, color='green')
plt.title('Error between erf and the interpolating polynomial using Lagrange Basis vs Z values')
plt.xlabel('Z values - 0: 0.01 : 4')
plt.ylabel('Error between erf and the interpolating polynomial')
plt.show()

plt.plot(Z, errNewton, color='blue')
plt.title('Error between erf and the interpolating polynomial using Newton Basis vs Z values')
plt.xlabel('Z values - 0: 0.01 : 4')
plt.ylabel('Error between erf and the interpolating polynomial')
plt.show()

print(
    'As we can see from the plots, the error value becomes very high as we move out of the range [1 3] - especially towards 4')
print(
    'So it is not recommended to use polynomial interpolation to approximate erf at points outside [1 3]')

# Error between Monomial and Newton bases
errMonoNewton = []
Z = np.arange(1, 3.001, 0.001)
for z in Z:
    errMonoNewton.append(abs(np.polyval(pxMonomial, z)-np.polyval(pxNewton, z)))
plt.plot(Z, errMonoNewton)
plt.title('Error of the interpolating polynomial between using Monomial and Newton Bases vs Z values')
plt.xlabel('Z values - 1: 0.001 : 3')
plt.ylabel('Error the interpolating polynomials')
plt.show()

# Error between Lagrange and Newton bases
errLangNewton = []
Z = np.arange(1, 3.001, 0.001)
for z in Z:
    errLangNewton.append(abs(np.polyval(pxLagrange, z)-np.polyval(pxNewton, z)))
plt.plot(Z, errLangNewton)
plt.title('Error of the interpolating polynomial between using Lagrange and Newton Bases vs Z values')
plt.xlabel('Z values - 1: 0.001 : 3')
plt.ylabel('Error the interpolating polynomials')
plt.show()

# Find the one with max errors
Z = np.arange(1, 3.001, 0.001)
errMonomial, errLagrange, errNewton = [], [], []
for z in Z:
    errMonomial.append(abs(f(z)-np.polyval(pxMonomial, z)))
    errLagrange.append(abs(f(z)-np.polyval(pxLagrange, z)))
    errNewton.append(abs(f(z)-np.polyval(pxNewton, z)))

Semester 6/MA 322 (Scientific Computing Theory)/Labs/Lab 2/Code/q5.py


import numpy as np

def lower_triangular_solve(A,b0):
    """
    Solve the system  A x = b  where A is assumed to be lower triangular,
    i.e. A(i,j) = 0 for j > i, and the diagonal is assumed to be nonzero,
    i.e. A(i,i) \= 0.

    ARGUMENTS:  A   lower triangular n x n array
                b0   right hand side column n-vector

    RETURNS:    x   column n-vector solution
    """

    #Check that A is lower triangular
    if not np.allclose(A,np.tril(A)):
        print "Error: The input array is not lower triangular!"
        return None

    #Get n-dimension
    n = len(b0)

    #Copy vector b0 and convert to float
    b = np.copy(b0).astype(float)

    #Initialise x
    x = np.zeros([n,1])

    #Loop through the remaining rows, calculating the solution components
    #in turn by backward substitution

    for i in xrange(n):
        for j in xrange(i):
            b[i] = b[i] - A[i,j]*x[j]
        x[i] = b[i] / A[i,i]

    return x

def upper_triangular_solve(A,b0):
    """
    Solve the system  A x = b  where A is assumed to be upper triangular,
    i.e. A(i,j) = 0 for j < i, and the diagonal is assumed to be nonzero,
    i.e. A(i,i) \= 0.

    ARGUMENTS:  A   upper triangular n x n array
                b0   right hand side column n-vector

    RETURNS:    x   column n-vector solution
                count   number of operations in the inner loop
    """

    #Check that A is upper triangular
    if not np.allclose(A,np.triu(A)):
        print "Error: The input array is not upper triangular!"
        return None

    #Get n-dimension
    n = len(b0)

    #Copy vector b0 and covert to float
    b = np.copy(b0).astype(float)

    #Initialise x & count
    x = np.zeros([n,1])
    count = 0

    #Loop through the remaining rows, calculating the solution components
    #in turn by backward substitution

    for i in xrange(n-1,-1,-1):
        for j in xrange(i+1,n):
            b[i] = b[i] - A[i,j]*x[j]
            count += 1
        x[i] = b[i] / A[i,i]

    return x,count

def gauss_elimination(A,b,**args):
    """
    Reduce the system  A x = b  to upper triangular form, assuming that
    the diagonal is nonzero, i.e. A(i,i) \= 0.

    ARGUMENTS:  A   n x n matrix
                b   right hand side column n-vector

                print  (optional) prints elimination steps

    RETURNS:    A   upper triangular n x n matrix
                b   modified column n-vector
                count   count the number of operations in the inner loop
    """
    #Get dimensions
    n = len(b)

    #Make sure entries are set to float
    A = A.astype(float)
    b = b.astype(float)

    #Initalizing
    count = 0

    #Loop through the rows (i) of the system
    for i in xrange(n-1):
        #Print solution information
        if 'print' in args:
            print 'Eliminate column %d\n' %i
            raw_input('Press key to continue')

        #Pick out the diagonal entry (and assume that it isn't zero
        r = 1. / A[i,i]

        #Loop through the rows (j) of the system below row i
        for j in xrange(i+1,n):
            #Calculate the multiplier for that row for elimination
            d = r * A[j,i]

            #Loop through the elements of row j which have yet to be set to zero
            for k in xrange(i,n):
                #For column k, subtract the scaled element in row i from row j.
                A[j,k] = A[j,k] - d*A[i,k]
                count += 1

            #Subtract scalded right hand side of row i from row j
            b[j] = b[j] - d*b[i]

        if 'print' in args:
            print A
            print b

    return A,b,count

def gauss_elimination_pivot(A,b):
    """
    Reduce the system  A x = b  to upper triangular form, making use of
    row pivoting.

    ARGUMENTS:  A   n x n matrix
                b   right hand side column n-vector

    RETURNS:    A   upper triangular n x n matrix
                b   modified column n-vector
    """
    #Get dimensions
    n = len(b)

    #Make sure entries are set to float
    A = A.astype(float)
    b = b.astype(float)

    #Loop through the rows (i) of the system.
    for i in range(n-1):
        #Find the value and position of the largest entry in
        #column i on or below the diagonal
        amax = abs(A[i,i])
        kmax = -1
        for k in xrange(i+1,n):
            bmax = abs(A[k,i])
            if bmax > amax:
                amax = bmax
                kmax = k

        #If an entry below the diagonal is larger than the one on the
        #diagonal then swap the rows
        if kmax > i:
            temp = np.copy(A[i,:])
            A[i,:] = A[kmax,:]
            A[kmax,:] = temp[:]

            tempb = np.copy(b[i])
            b[i] = b[kmax]
            b[kmax] = tempb;

        #%% Now carry out the usual elimination process
        A,b = gauss_elimination(A,b)

    return A,b

def gauss_elim_count(n):
    """
    Solve a nxn example system A x = b by first using Gaussian Elimination
    and solving the resulting upper triangular system, but return the number
    of operations executed by Elimination and the backward substitution.

    ARGUMENTS:  n   dimension of system

    RETURNS:    count1 operations in forward elimination (GE)
                count2 operations in the backward substitution
    """

    #Create a matrix of size nxn
    A = np.random.rand(n,n)
    #Create a column vector of size n
    b0 = np.random.rand(n,1)

    A, b0, count1 = gauss_elimination(A, b0)
    x, count2 = upper_triangular_solve(A, b0)

    return count1, count2

def test_gauss_elim_count():
    """
    Prints a table that displays the amount of operations, count1 & count2,
    (and other related measurements) returned by gauss_elim_count. This is
    done for n = [2,4,8,16,32,64,128,256,512,1024].
    """

    n_list = [2,4,8,16,32,64,128,256,512,1024]
    # Non-existent values for n < 2 hence set initially to infinity
    # so that the ratios would equal 0 (but don't have any meaning)
    prev_count1 = float("inf")
    prev_count2 = float("inf")

    #Prints the header of the table
    print "{:^10} {:^10} {:^15} {:^10} {:^15}" \
        .format("n", "Forward Elim", "Ratio", "Backward Subs", "Ratio")

    for n in n_list:
        count1, count2 = gauss_elim_count(n)

        ratio1 = float(count1)/prev_count1
        ratio2 = float(count2)/prev_count2

        #Prints the entries of the table
        print "{:5} {:11} {:15f} {:13} {:15f}"\
            .format(n, count1, ratio1, count2, ratio2)

        prev_count1 = count1
        prev_count2 = count2

test_gauss_elim_count()

def lu_factorise(A):
    """
    LU factorise the matrix A into a lower triangular matrix L and an
    upper triangular matrix U.

    ARGUMENTS:  A   n x n matrix

    RETURNS:    L   lower triangular matrix
                U   upper triangular matrix
    """

    #Get matrix dimension
    n = len(A)

    #Make sure A entries are float
    A = A.astype(float)

    #Initialise L to be the n x n identity matrix I and U to be the
    #n x n zero matrix
    L = np.eye(n,dtype=float)
    U = np.zeros([n,n],dtype=float)

    #Loop through the column of the matrix
    for j in xrange(n-1):
        #Compute the elements of U on and above the diagonal in column j
        #using previously computed elements of L and U.
        U[0,j] = A[0,j]
        for i in xrange(1,j+1):
            U[i,j] = A[i,j] - np.dot(L[i,:i],U[:i,j])

        #Compute the elements of L below the diagonal in column j using
        #previously computed elements of L and U
        r = 1. / U[j,j]

        for i in xrange(j+1,n):
            L[i,j] = r*( A[i,j] - np.dot(L[i,:j],U[:j,j]))

    #For column n there are no entries of L to be computed s only compute
    #the % elemens of U on and above the diagonal in column n, again using
    #previously computed elements of L and U
    U[0,-1] = A[0,-1]
    for i in xrange(1,n):
        U[i,-1] = A[i,-1] - np.dot(L[i,:i],U[:i,-1])

    return L,U

def gauss_seidel(A,u,b,n_iterations):
    """
    Solve the system A u = b using a Gauss-Seidel iteration

    ARGUMENTS:  A   k x k matrix
                u   k-vector storing initial estimate
                b   k-vector storing right-hand side
                n_iterations
                    integer number of iterations to carry out

    RESULTS:    u   k-vector storing solution
    """

    #Get dimension
    k = len(A)

    #Make sure matrix A is float
    A = A.astype(float)

    for i in xrange(n_iterations):
        for j in xrange(k):
            u[j] = u[j] + (b[j] - np.dot(A[j,:],u))/A[j,j]
        print u

    return u

def jacobi(A,u,b,n_iterations):
    """
    Solve the system A u = b using a Jacobi iteration

    ARGUMENTS:  A   k x k matrix
                u   k-vector storing initial estimate
                b   k-vector storing right-hand side
                n_iterations
                    integer number of iterations to carry out

    RESULTS:    u   k-vector storing solution
    """

    #Get dimension
    k = len(A)

    #Make sure matrix A is float
    A = A.astype(float)

    r = np.zeros([k,1])
    for i in xrange(n_iterations):
        r = (b - np.dot(A,u))
        for j in xrange(k):
            r[j] = r[j] / A[j,j]

        u = u + r

    return u

def jacobi2(A,u,b,n_iterations):
    """
    Solve the system A u = b using a Jacobi iteration

    ARGUMENTS:  A   k x k matrix
                u   k-vector storing initial estimate
                b   k-vector storing right-hand side
                n_iterations
                    integer number of iterations to carry out

    RESULTS:    u   k-vector storing solution
    """

    #Get dimension
    k = len(A)

    #Make sure matrix A is float
    A = A.astype(float)

    unew = np.zeros([k,1])
    for i in xrange(n_iterations):
         for j in xrange(k):
            unew[j] = u[j] + (b[j] - np.dot(A[j,:],u)) / A[j,j]

         u = np.copy(unew)

    return u

def gauss_seidel_new(A,u,b,tol):
    """
    Solve the system A u = b using a Gauss-Seidel iteration

    ARGUMENTS:  A   k x k matrix
                u   k-vector storing initial estimate
                b   k-vector storing right-hand side
                tol real number providing the required convergence tolerance

    RESULTS:    u   k-vector storing solution
    """

    #Get dimension
    k = len(A)

    #Make sure matrix A is float
    A = A.astype(float)

    #Set the maximum number of iterations
    maxit = 1000

    #Initialise the iteration counter
    it = 0

    #Initialise diffRMS to exceed tol for the first iteration
    diffRMS = 2*tol

    while ((diffRMS > tol) and (it < maxit)):
        uold = u
        for j in xrange(k):
            u[j] = u[j] + (b[j] - np.dot(A[j,:],u))/A[j,j]

        diffRMS = 0
        for j in xrange(k):
            diffRMS = diffRMS + (u[j] - uold[j])**2

        it += 1
        diffRMS = np.sqrt(diffRMS)

    if (difffRMS) > tol:
        print 'Warning! Iteration has not converged'

    return u

def jacobi_new(A,u,b,tol):
    """
    Solve the system A u = b using a Jacobi iteration

    ARGUMENTS:  A   k x k matrix
                u   k-vector storing initial estimate
                b   k-vector storing right-hand side
                tol real number providing the required convergence tolerance

    RESULTS:    u   k-vector storing solution
    """
    #Get dimension
    k = len(A)

    #Make sure matrix A is float
    A = A.astype(float)

    #Set the maximum number of iterations
    maxit = 1000

    #Initialise the iteration counter
    it = 0

    #Initialise diffRMS to exceed tol for the first iteration
    diffRMS = 2*tol

    unew = np.zeros([k,1])
    while ((diffRMS > tol) and (it < maxit)):
        for j in xrange(k):
            unew[j] = u[j] + (b[j] - np.dot(A[j,:],u)) / A[j,j]

        diffRMS = 0
        for j in xrange(k):
            diffRMS = diffRMS + (unew[j] - u[j])**2

        it += 1
        diffRMS = np.sqrt(diffRMS)
        print it,diffRMS
        u = np.copy(unew)

    if (diffRMS) > tol:
        print 'Warning! Iteration has not converged'

    return u


import numpy as np

a=np.array([[1,2,3,4],[5,6,7,8]])
print(a)
# Get dimensions
print(a.ndim)
#Get shape
print(a.shape)
b= np.array([3,4,5,6],dtype='int16')
print(a*b)

print(b.ndim)
print(b.dtype)

print(b.itemsize)
print(b.nbytes)

a=np.array([[1,2,3,4,5,6,7],[8,9,10,11,12,13,14]])
print(a)
print(a.shape)

print(a[0,5])
print(a[0,:])
print(a[:,3])
print(a[0,1:7:2])
a[1,:]=22
print(a)

b= np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
print(b)
print(b[0,1,1])
print(b[:,1,:])

# Initialize all zero matrix

a=np.zeros((2,3))
print(a)

b=np.ones((2,2))
print(b)

# initalize with any random value

b=np.full((2,2),98,dtype='float32')
print(b)
#full_like method
b=np.full_like(b,4)
print(b)

# initialize random decimal numbers
b=np.random.rand(4,2)
print(b)

b= np.random.random_sample(b.shape)
print(b)

b=np.random.randint(-2,20,size=(3,3))
print(b)

# identity matrix

b=np.identity(5)
print(b)

#Repeat an array

a=np.array([1,2,3])
a=np.repeat(a,3)
print(a)

a=np.array([[1,2,3],[4,5,6]])
a=np.array([[1,2,3]])
a=np.repeat(a,4,axis=0)
print(a)

# print
# [[1. 1. 1. 1. 1.]
# [1. 0. 0. 0. 1.]
# [1. 0. 9. 0. 1.]
# [1. 0. 0. 0. 1.]
# [1. 1. 1. 1. 1.]]

a=np.ones((5,5))
b=np.zeros((3,3))
b[1,1]=9
a[1:4,1:4]=b
print(a)

# Coping array
a=np.array([1,2,3])
#b=a
b=a.copy()
b[0]=100
print(b)
print(a)

#Mathematics

a=np.array([1,2,3,4])
a=a+2
a=a*2

a=np.sin(a)
print(a)

# Linear algebra

a=np.ones((2,4))
print(a)
b=np.full((4,2),2)
print(b)

print(np.matmul(a,b))

# finding determinant
a=np.identity(3)
a=np.linalg.det(a)
print(a)

#Statistic

a= np.array([[1,2,3],[4,5,6]])
a=np.max(a,axis=0)
#a=np.min(a)

a=np.sum(a)
print(a)

#Reorganizing arrays

a=np.array([1,2,3,4])
a=a.reshape((2,2))
print(a)

#Vertical stack
v1= np.array([1,2,3,4])
v2=np.array([5,6,7,8])
a=np.vstack([v2,v1])
print(a)

# horizontal stack

a=np.hstack([v1,v2])
print(a)

import os
# Load data from file
#os.chdir('C:\\USMAN FILES\\Data Science\\Numpy')

a=np.genfromtxt('data.txt', delimiter=",")
print(a)
print(a.astype('int32'))

# Advance indexing & boolean masking

print(np.any(a<20,axis=0))
print("\n")

print(~((a>10) & (a<20) | (a==20)))
print("\n")
print((a>10) & (a<20) | (a==20))
#print values greater check
print(a[a>20])
# index a list in numpy
a=np.array([1,2,3,4,5,6,7,8,9])
print(a[[2,5,7]])

a=np.ones((6,5))

b=np.array([[11,12],[16,17]])

a[2:4,0:2]=b
print(a)
print("\n")

#c=np.identity(4)

c=np.array([2,8,14,20])

a[[0,1,2,3],[1,2,3,4]]=c

#a[0,1]=c[0]
#a[1,2]=c[1]
#a[2,3]=c[2]
#a[3,4]=c[3]

print(a)
print("\n")
d=np.array([4,5,24,25,29,30])
#a[0,3]=d[0]
#a[0,4]=d[1]
#a[4,3]=d[2]
#a[4,4]=d[3]
#a[5,3]=d[4]
#a[5,4]=d[5]
a[[0,0,4,4,5,5],[3,4,3,4,3,4]]=d
print(a)


#!/usr/bin/env python

"""
task 1.4 Unit circles

# ToDo: Implement it in three dimensions
# L_p norm
# https://www.youtube.com/watch?v=SXEYIGqXSxk
# unit circle:
# https://www.youtube.com/watch?v=qTbDQ9gkKJg
"""

import pylab
import matplotlib.pyplot as plt
import numpy as np
from auxiliar import *

@save_figure("out/unityball.png")
def unitball(p):
    """
    """

    # x points created in the order of quadrants
    q1 = np.linspace(1.0, 0.0, 1000)
    q2 = np.linspace(0.0, -1.0, 1000)
    q3 = q2[::-1]
    q4 = q1[::-1]

    plt.xlim(-1.5, 1.5)
    plt.ylim(-1.5, 1.5)
    plt.axhline(0, color='black')
    plt.axvline(0, color='black')

    # draw quadrants
    y1 =  (1 - np.abs(q1) ** p) ** (1.0/p)
    y2 =  (1 - np.abs(q2) ** p) ** (1.0/p)
    y3 = -(1 - np.abs(q3) ** p) ** (1.0/p)
    y4 = -(1 - np.abs(q4) ** p) ** (1.0/p)

    x = np.concatenate(( q1, q2, q3, q4))
    y = np.concatenate(( y1, y2, y3, y4))

    plt.plot(x, y, label = "p = " + str(p))
    plt.legend(loc='upper right')
    # plt.savefig("out/unityball.png", bbox_inches="tight", pad_inches=0)
    # plt.show()
    # plt.close()
    return plt

unitball(0.5)

# By picking any two vectors; i.e. (x1, y1) = (0, 1) and (x2, y2) = (1, 0). Then
# by calculating there norms,  we will find that this will be equal to 1.
p = 0.5
v1_x = 0
v1_y = 1
print("Norm of v1: ", (np.abs(v1_x) ** p + np.abs(v1_y) ** p) ** (1.0/p))
v2_x = 1
v2_y = 0
print("Norm of v2: ", (np.abs(v2_x) ** p + np.abs(v2_y) ** p) ** (1.0/p))
# But by calculating the norm of v1 + v2 = (0, 1) + (1, 0) = (1, 1); This will
# be equal 4.
v3_x = 1
v3_y = 1
print("Norm of (v1 + v2): ", (np.abs(v3_x) ** p + np.abs(v3_y) ** p) ** (1.0/p))
# This result is larger than the summation of norm(v1) and norm(v2) and does not
# satisfy the property of norm for: norm(v1 + v2) <= norm(v1) + norm(v2).
# As a conclusion, this is not really a norm.


import pandas as pd
#%matplotlib inline
import matplotlib.pyplot as plt
from scipy import integrate
import numpy as np
def simpe_harmonic(X, t, cons):  #simple harmonic oscillator
    k,m = cons
    x, y = X
    dotx = y
    dot2x = -k*x/m
    return np.array([dotx, dot2x])
def damped_harmonic(X, t, cons):  #damped oscillator
    k,m,b = cons
    x, y = X
    dotx = y
    doty = -k*x-b*dotx
    return np.array([dotx, doty])
def simple_pendulum(X, t, cons):  #Simple Pendulum
    g,L = cons
    x, y = X
    dotx = y
    doty = -g*x/L
    return np.array([dotx, doty])
def RK2(func, X0, t,cons):
    dt = t[1] - t[0]
    nt = len(t)
    X  = np.zeros([nt, len(X0)])
    X[0] = X0
    for i in range(nt-1):
        k1 =dt* func(X[i], t[i], cons)
        k2 = dt*func(X[i] +  k1, t[i] + dt, cons)
        X[i+1] = X[i] + (k1 +  k2 )/2
    return X, t

if __name__ == "__main__":
    k = 1; m = 1; cons=(k,m); t_p = 2*np.pi*np.sqrt(m/k); tmin=0; tmax=7*t_p; Nt = 1000; x0 = [2,0]
    t = np.linspace(tmin,tmax,Nt)
    Xrk2 = RK2(simpe_harmonic, x0, t, cons)
    x,y= Xrk2[0].T
    t=Xrk2[1]
    tdimless=t/t_p
    #Plotting

    plt.title('Simple Harmonic Oscillator', fontsize=20)
    plt.plot(tdimless,x,color='black',label="Displacement");plt.plot(tdimless,y,color='brown',label="Velocity")
    plt.grid(); plt.legend()
    plt.show()
#Damped Harmonic Oscillator
    k = 1; m = 1; t_p = 2*np.pi*np.sqrt(m/k); tmin=0; tmax=30*t_p; Nt = 1000; x0 = [1,0]
    t = np.linspace(tmin,tmax,Nt)
    tdimless=t/t_p; dis=[];vel=[];time=[]
    ba=[0.15,2,5]
    for b in ba:
        cons=(m,k,b)
        Xrk2=RK2(damped_harmonic,x0,tdimless, cons)
        x,y= Xrk2[0].T
        tdimless=Xrk2[1]
        dis.append(x)
        vel.append(y)
    fig, axs = plt.subplots(3,figsize=(11,15))
    fig.suptitle('Damped Harmonic Oscillator', fontsize=20)
    axs[0].plot(tdimless,dis[0],label = "displacement")
    axs[0].set(xlabel="time ",title="Underdamped, b = 0.15")
    axs[0].plot(tdimless,vel[0],label = "velocity")
    axs[0].plot(tdimless,1/2*k*dis[0]**2)
    axs[0].grid(); axs[0].legend()
    axs[1].plot(tdimless,dis[1],label = "displacement")
    axs[1].set(xlabel="time ",title="Critically Damped, b = 2")
    axs[1].plot(tdimless,vel[1],label = "velocity")
    axs[1].plot(tdimless,1/2*k*dis[1]**2)
    axs[1].grid(); axs[1].legend()
    axs[2].plot(tdimless,dis[2],label = "displacement")
    axs[2].plot(tdimless,1/2*k*dis[2]**2)
    axs[2].set(xlabel="time ",title="Overdamped, b =5")
    axs[2].plot(tdimless,vel[2],label = "velocity")
    axs[2].grid(); axs[2].legend()
    plt.show()
#Simple pendulum
    g = 9.8; L = 1; cons=(g,L); t_p = 2*np.pi*np.sqrt(L/g); tmin=0; tmax=7*t_p; Nt = 1000; x0 = [2,0]
    t = np.linspace(tmin,tmax,Nt)
    Xrk2 = RK2(simple_pendulum, x0, t, cons)
    x,y= Xrk2[0].T
    t=Xrk2[1]
    tdimless=t/t_p
#Plotting
    plt.title('Simple Pendulum', fontsize=20)
    plt.plot(tdimless,x,color='black',label="Angular Displacement");plt.plot(tdimless,y,color='brown',label="Angular Velocity")
    plt.grid(); plt.legend()
    plt.show()


# todo: Outlier detection and removal using z-score and standard deviation in python pandas

# todo: import library
import pandas as pd
import matplotlib
from matplotlib import pyplot as plt
from scipy.stats import norm
import numpy as np

# get_ipython().run_line_magic('matplotlib', 'inline')
matplotlib.rcParams['figure.figsize'] = (10, 6)

# todo: We are going to use heights dataset from kaggle.com.
#  Dataset has heights and weights both but I have removed weights to make it simple

# todo: load dataset
df = pd.read_csv("heights.csv")
df.sample(5)

# todo: plotting histogram for visualization of height data
plt.hist(df.height, bins=20, rwidth=0.8)
plt.xlabel('Height (inches)')
plt.ylabel('Count')
plt.show()

# todo: Plot bell curve along with histogram for our dataset using scipy.stats.norm
plt.hist(df.height, bins=20, rwidth=0.8, density=True)
plt.xlabel('Height (inches)')
plt.ylabel('Count')
rng = np.arange(df.height.min(), df.height.max(), 0.1)
plt.plot(rng, norm.pdf(rng, df.height.mean(), df.height.std()))

# todo: mean and std of normal distribution-
df.height.mean()
df.height.std()

# todo: Outlier detection and removal using 3 standard deviation
# One of the ways we can remove outliers is remove any data points that are beyond 3 standard deviation from mean.
# Which means we can come up with following upper and lower bounds

# todo: creating uppper  and lower limit for outlier using mean and std
upper_limit = df.height.mean() + 3 * df.height.std()
print(upper_limit)
lower_limit = df.height.mean() - 3 * df.height.std()
print(lower_limit)

# todo: Here are the outliers that are beyond 3 std dev from mean
print(df[(df.height > upper_limit) | (df.height < lower_limit)])

# Above the heights on higher end is **78 inch** which is around **6 ft 6 inch**. Now that is quite unusual height.
# There are people who have this height but it is very uncommon and it is ok if you remove those data points.
# Similarly on lower end it is **54 inch** which is around **4 ft 6 inch**.
# While this is also a legitimate height you don't find many people having this height
# so it is safe to consider both of these cases as outliers

# todo: Now remove these outliers and generate new dataframe
df_no_outlier_std_dev = df[(df.height < upper_limit) & (df.height > lower_limit)]
df_no_outlier_std_dev.head()
print(df_no_outlier_std_dev.shape)
print(df.shape)

# Above shows original dataframe data 10000 data points. Out of that we removed 7 outliers (i.e. 10000-9993)

# todo: (2) Outlier detection and removal using Z Score
# Z score is a way to achieve same thing that we did above in part (1)
# Z score indicates how many standard deviation away a data point is.
# For example in our case mean is 66.37 and standard deviation is 3.84.
# If a value of a data point is 77.91 then Z score for that is 3 because
# it is 3 standard deviation away (77.91 = 66.37 + 3 * 3.84)

# todo: Calculate the Z Score
# Z = (X - mean)/std
df['zscore'] = (df.height - df.height.mean()) / df.height.std()
df.head(5)
# Above for first record with height 73.84, z score is 1.94. This means 73.84 is 1.94 standard deviation away from mean.
# if Z is the above 3 or below -3 then that height is outlier.

# todo: Get data points that has z score higher than 3 or lower than -3.
# Another way of saying same thing is get data points that are more than 3 standard deviation away**
print(df[df['zscore'] > 3])
print(df[df['zscore'] < -3])

# todo: Here is the list of all outliers
print(df[(df.zscore < -3) | (df.zscore > 3)])

# todo: Remove the outliers and produce new dataframe
df_no_outliers = df[(df.zscore > -3) & (df.zscore < 3)]
df_no_outliers.head()
print(df_no_outliers.shape)
print(df.shape)
# Above shows original dataframe data 10000 data points. Out of that we removed 7 outliers (i.e. 10000-9993)

# (1) Remove outliers using percentile technique first. Use [0.001, 0.999] for lower and upper bound percentiles
# (2) After removing outliers in step 1, you get a new dataframe.
# (3) On step(2) dataframe, use 4 standard deviation to remove outliers
# (4) Plot histogram for new dataframe that is generated after step (3). Also plot bell curve on same histogram
# (5) On step(2) dataframe, use zscore of 4 to remove outliers.
# This is quite similar to step (3) and you will get exact same result

Python/Machine Learning/Code Basics/Feature Engineering/2 Using Standard deviation and Z_score for remove outlier.py


from numpy import *
import numpy as np
from math import *
from matplotlib.pyplot import *
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

'''
Write down the function that we need to solve for
x' = t^{-2}(tx-x^2)
'''

def f(t,x):
    return (t**(-2))*(t*x - (x**2))

'''
another derivative of the function function
'''

def dfx(t,x):
    return (1/np.power(t,2))*(t-2*x)

# analytical solution

# backward Euler
def beuler(t0, tn, n, x0):
    #h = abs(tn - t0)/n
    t = linspace(t0, tn, n+1)
    x = zeros(n+1)
    x[0] = x0

    for k in range(0,n):
        err = 1
        zold = x[k] + h*f(t[k], x[k])
        I = 0

        while err > 10**(-10) and I < 5:
            F = x[k] + h*f(t[k+1], zold) - zold
            dF = h*dfx(t[k+1], zold)-1
            znew = zold - F/dF
            err = abs(znew- zold)
            I += 1
        x[k+1] = znew
    return x

def RK4(t0,tn,n,x0):
    #h = abs(tn-t0)/n
    t = linspace(t0, tn, n+1)
    x = zeros(n+1)
    x[0] = x0
    for i in range(0,n):
        K1 = f(t[i], x[i])
        K2 = f(t[i]+h/2, x[i]+K1*h/2)
        K3 = f(t[i] + h/2, x[i]+K2*h/2)
        K4 = f(t[i] + h, x[i]+K3*h)

        x[i+1] = x[i] + h*(K1+2*K2+ 2*K3+K4)/6
    return x

def AdBash3(t0,tn,n,x0):
    #h = abs(tn-t0)/n
    t = linspace(t0, tn, n+1)
    x = zeros(n+1)

    x[0:3] = RK4(t0, t0+2*h,2,x0)
    K1 = f(t[1], x[1])
    K2 = f(t[0], x[0])

    for i in range(2,n):
        K3= K2
        K2= K1
        K1 = f(t[i], x[i])

        # prediction_resul
        x[i+1] = x[i] + h*(23*K1 - 16*K2+5*K3)/12
    return x

def PreCorr3(t0,tn,n,x0):
    #h = abs(tn-t0)/n
    t = linspace(t0, tn, n+1)
    x = zeros(n+1)

    x[0:3] = RK4(t0, t0+2*h,2,x0)
    K1 = f(t[1], x[1])
    K2 = f(t[0], x[0])

    for i in range(2,n):
        K3= K2
        K2= K1
        K1 = f(t[i], x[i])

        # prediction_resul
        x[i+1] = x[i] + h*(23*K1 - 16*K2+5*K3)/12

        K0 = f(t[i+1],x[i+1])

        # correctorical

        x[i+1] = x[i] + h*(9*K0 + 19*K1 - 5*K2 + K3)/24

    return x

fg = 1
n= 200
t0 = 1
h = 0.01
tn = 3
x0 = 2
t = linspace(t0, tn , n+1)
xe = AdBash3(t0,tn, n, x0)
xb = beuler(t0,tn,n,x0)
xpc = PreCorr3(t0, tn, n,x0)

# print
#exact = sol(t0, tn ,n , x0)

plot(t,xe,'o',color='yellow',label ='Adam Bashforth 3')
plot(t, xb, '--', color='red', label ='Backward Euler')
plot(t,xpc,'cyan',label='Predictor Corrector')
#plot(t,(t* (1/2 + log(t))**(-1)),'blue',label ='Exact Solution')

#t = linspace(t0, tn, 401)
#xsol = sol(t,t0,x0)

#plot(t,xsol, color='green', label='Exact')

##title('n = %d' %n)
t = t0
exact = []
time = []
while(t<=tn):

    m = (t* (1/2 + log(t))**(-1))

    t = t+0.006666666666666821
    exact.append(m)
    time.append(t)

plt.plot(time,exact,'blue',label='Exact')
#axis([0,tn, -60,40])
legend(loc='upper right')
plt.savefig('ADM.png')


""" Orbital Mechanics for Engineering Students Problem 1.21
Question:
Numerically solve the system
    xDot + 0.5*y - z = 0
    -0.5*x + yDot + 1/sqrt(2)*z = 0
    0.5*x - 1/sqrt(2)*y + zDot = 0
for x, y, and z at t = 20, if the initial conditions at t=0 are:
    - x = 1
    - y = z = 0
Note: the answer in the book is likely wrong, I cannot see anything wrong
with my working yet it yields a different answer.
Written by: J.X.J. Bannwarth
"""
import numpy as np
import matplotlib.pyplot as plt
from orbitutils.solvers import rkf45, rk14

# Differential equations
def rates(t, Y):
    F = np.zeros(Y.shape)
    F[0] = -0.5*Y[1] + Y[2]
    F[1] = 0.5*Y[0] - Y[2]/np.sqrt(2.)
    F[2] = -0.5*Y[0] + Y[1]/np.sqrt(2.)
    return F

# Title
print("Orbital Mechanics for Engineering Students Problem 1.21")

# Parameters
tSpan = np.array([0., 20.])
Y0 = np.array([1., 0., 0.])

# Solve numerically
y, t = rkf45(rates, Y0, tSpan)
y, t = rk14(rates, Y0, tMax=20, h=1e-4)

# Show answer
print(f"x({t[-1]:.3f}) = {y[-1,0]:.3f}")
print(f"y({t[-1]:.3f}) = {y[-1,1]:.3f}")
print(f"z({t[-1]:.3f}) = {y[-1,2]:.3f}")

# Plot answer
plt.figure()
plt.plot(t, y[:, 0], label="x")
plt.plot(t, y[:, 1], label="y")
plt.plot(t, y[:, 2], label="z")
plt.xlabel("Time (-)")
plt.ylabel("Value (-)")
plt.legend()
plt.show()


# =============================================================================
#
# Explicit Finite Difference Method Code to Solve the 1D Linear Transport Equation
# Adapted by: Cameron Armstrong (2019)
# Source: Lorena Barba, 12 Steps to NS in Python
# Institution: Virginia Commonwealth University
#
# =============================================================================

# Required Modules
import numpy as np
from matplotlib import pyplot as plt
import time

xl = 2                                      # x length
nx = 600                                    # number of grid points
x = np.linspace(0,xl,nx)                    # x grid
dx = xl/(nx-1)                              # x stepsize
nt = 350                                    # number of timesteps
dt = 0.0025                                 # time stepsize
c = 1                                       # wave speed
g = .01                                     # gaussian variance parameter (peak width)
theta = x/(0.5*xl)                          # gaussian mean parameter (peak position)
cfl = round(c*dt/dx,2)                      # cfl condition 2 decimal places

# Fun little CFL condition check and print report
if cfl >= 1:
    print('Hold your horses! The CFL is %s, which is over 1' %(cfl))
else:
    print('CFL = %s' %(cfl))

# Array Initialization
u = np.ones(nx)                             # initializing solution array
un = np.ones(nx)                            # initializing temporary solution array
u = (1/(2*np.sqrt(np.pi*(g))))*np.exp(-(1-theta)**2/(4*g)) # initial condition (IC) as a gaussian
ui = u.copy()
plt.plot(x,u); # plots IC

# BDS/Upwind with inner for-loop with example on process timing
start = time.process_time()
for n in range(nt):
    un = u.copy()
    for i in range(1,nx-1):
        u[i] = un[i] - c*dt/(dx)*(un[i]-un[i-1])
        # periodic BC's
        u[0] = u[nx-2]
        u[nx-1] = u[1]

end = time.process_time()
print(end-start)

# # BDS/Upwind with vectorization
# for n in range(nt):
#     un = u.copy()
#     u[1:-1] = un[1:-1] - c*dt/(dx)*(un[1:-1]-un[:-2])
#     # periodic BC's
#     u[0] = u[nx-2]
#     u[nx-1] = u[1]

# # CDS with inner for-loop
#for n in range(nt):
#    un = u.copy()
#    for i in range(1,nx-1):
#        u[i] = un[i] - c*dt/(2*dx)*(un[i+1]-un[i-1])
#        # periodic BC's
#        u[0] = u[nx-2]
#        u[nx-1] = u[1]

# # CDS with vectorization
#for n in range(nt):
#    un = u.copy()
#    u[1:-1] = un[1:-1] - c*dt/(2*dx)*(un[2:]-un[:-2])
#    # periodic BC's
#    u[0] = u[nx-2]
#    u[nx-1] = u[1]

plt.plot(x,u);


# %%[markdown]
# First we load and configure the libraries we need:
# %%
import numpy as np
import matplotlib.pyplot as plt

# Make numpy print 4 significant digits for prettiness
np.set_printoptions(precision=4, suppress=True)
np.random.seed(5)

# %%[markdown]
# Here is a set of data, made out of random numbers, that we will use as a pretend time series,
# or a single line of data from one plane of an image.

# %%
n_points = 40

x_vals = np.arange(n_points)
y_vals = np.random.normal(size=n_points)
plt.bar(x_vals, y_vals)

# %%[markdown]
# ## The Gaussian kernel
# The ‘kernel’ for smoothing, defines the shape of the function that
# is used to take the average of the neighboring points. A Gaussian
# kernel is a kernel with the shape of a Gaussian (normal distribution)
# curve. Here is a standard Gaussian, with a mean of 0 and a
# σ (=population standard deviation) of 1.

# %%
x = np.arange(-6, 6, 0.1)  # x from -6 to 6 in steps of 0.1
y = 1 / np.sqrt(2 * np.pi) * np.exp(-x ** 2 / 2.)
plt.plot(x, y)

# %%[markdown]
# In the standard statistical way, we have defined the width of the
# Gaussian shape in terms of σ. However, when the Gaussian is used
# for smoothing, it is common for imagers to describe the width of
# the Gaussian with another related measure, the Full Width at Half
# Maximum (FWHM).
#
# The FWHM is the width of the kernel, at half of the maximum of the
# height of the Gaussian. Thus, for the standard Gaussian above, the
# maximum height is ~0.4. The width of the kernel at 0.2 (on the Y axis)
# is the FWHM. As x = -1.175 and 1.175 when y = 0.2, the FWHM is roughly 2.35.
#
# The FWHM is related to sigma by the following formulae (in Python):

# %%
def sigma2fwhm(sigma):
    return sigma * np.sqrt(8 * np.log(2))

def fwhm2sigma(fwhm):
    return fwhm / np.sqrt(8 * np.log(2))


'''Matrix Symmetry

Author: Andrei-Claudiu Roibu, 2019

This code has been created to compare the speed difference between a numpy dot product and a slow dot product. This code was written in support of my learning.

The original code comes from these two sources:

    # https://deeplearningcourses.com/c/deep-learning-prerequisites-the-numpy-stack-in-python
    # https://www.udemy.com/deep-learning-prerequisites-the-numpy-stack-in-python

Description:

This code tests if a matrix is symmetric. This is done either manually, and using numpy functions.

'''

import numpy as np
import matplotlib.pyplot as plt

def symmetric_test(array):
    """ Simple symmetry test

    Function wrapper which calls to check if a matrix is symmetric.

    Args:
        array (np.array): Array to be tested if symmetric
    Returns:
        (bool): Truth value indicating if array is symmetric
    """

    return np.all(array == array.T)

def symmetric_check(array, truth):
    """ Symmetric assertion checker

    This function checks if an array is symmetric or not.

    Args:
        array (np.array): Array to be tested if symmetric
        truth (bool): Truth value indicating if array is symmetric or not
    """
    print("Testing: ", '\n', array)
    assert(symmetric_test(array) == truth)

if __name__ == '__main__':
    A = np.zeros((3, 3))
    symmetric_check(A, True)

    A = np.eye(3)
    symmetric_check(A, True)

    A = np.random.randn(3, 2)
    A = A.dot(A.T)
    symmetric_check(A, True)

    A = np.array([[1, 2, 3], [2, 4, 5], [3, 5, 6]])
    symmetric_check(A, True)

    A = np.random.randn(3, 2)
    symmetric_check(A, False)

    A = np.random.randn(3, 3)
    symmetric_check(A, False)

    A = np.arange(9).reshape(3, 3)
    symmetric_check(A, False)


"""
Space derivatives approximation using differentiation matrices

Implements differentiation matrices for:
    - Finite Differences of second and fourth order of accuracy
    - Chebyshev differentiation matrix

Details in: Trefethen, L. N. (2000). Spectral methods in MATLAB. Society for industrial and applied mathematics.
https://doi.org/10.1137/1.9780898719598
"""
import numpy as np
import scipy as sp
from scipy.linalg import circulant, toeplitz
from scipy.sparse import csr_matrix

# First derivative with Finite Difference Matrix
def FD1Matrix(N, h, acc=2, sparse=False):
    """
    Compute first derivative using Finite Difference Matrix
    with O(h^acc) of accuracy.

    Parameters
    ----------
    N : int
        Number of nodes.
    h : float
        Step size.
    acc	: int
        Order of accuracy (2 or 4)
    sparse : boolean
        If true, return sparse matrix.

    Returns
    -------
    D1 : (N, N) ndarray
        Finite difference dense matrix; or
    sD1 : sparse-like
        Finite difference sparse matrix.
    """
    d1 = np.zeros(N)

    if acc == 2: # Coefficients for second order
        d1[1] = -1/2
        d1[-1] = 1/2
    elif acc == 4: # Coefficients for fourth order
        d1[1] = -2/3
        d1[2] = 1/12
        d1[-1] = 2/3
        d1[-2] = -1/12

    D1 = circulant(d1) # Central difference inside the domain

    # Finite difference at boundary.
    # To keep accuracy, it uses forward and backward coefficients of second or fourth order
    if acc == 2:
        D1[0,:3] = np.array([-3/2, 2, -1/2]) # Forward difference at left boundary
        D1[-1,-3:] = np.array([1/2, -2, 3/2]) # Backward difference at right boundary
    elif acc == 4:
        D1[0,:5] = np.array([-25/12	, 4, -3, 4/3, -1/4]) # Forward difference at left boundary
        D1[-1,-5:] = np.array([1/4, -4/3, 3, -4, 25/12]) # Backward difference at right boundary

    D1 = D1 / h # Include step

    if sparse:
        D1 = csr_matrix(D1)

    return D1

# Second derivative with Finite Difference Matrix
def FD2Matrix(N, h, acc=2, sparse=False):
    """
    Compute second derivative using Finite Difference Matrix
    with O(h^acc) of accuracy.

    Parameters
    ----------
    N : int
        Number of nodes.
    h : float
        Step size.
    acc : int
        Order of accuracy (2 or 4)
    sparse : boolean
        If true, return sparse matrix.

    Returns
    -------
    D2 : (N, N) ndarray
        Finite difference dense matrix; or
    sD2 : sparse-like
        Finite difference sparse matrix.
    """
    d2 = np.zeros(N)

    if acc == 2:
        d2[0] = -2
        d2[1] = 1
        d2[-1] = 1
    elif acc == 4:
        d2[0] = -5/2
        d2[1] = 4/3
        d2[2] = -1/12
        d2[-1] = 4/3
        d2[-2] = -1/12

    D2 = circulant(d2)

    if acc == 2:
        D2[0,:4] = np.array([2, -5, 4, -1]) # Forward difference at left boundary
        D2[-1,-4:] = np.array([-1, 4, -5, 2]) # Backward difference at right boundary
    elif acc == 4:
        D2[0,:6] = np.array([15/4, -77/6, 107/6, -13, 61/12, -5/6]) # Forward difference at left boundary
        D2[-1,:6] = np.array([-5/6, 61/12, -13, 107/6, -77/6, 15/4]) # Backward difference at right boundary

    D2 = D2 / (h ** 2)

    if sparse: D2 = csr_matrix(D2)

    return D2

# Chebyshev differentiation matrix
def chebyshevMatrix(N):
    """
    Compute derivative using Chebyshev differentiation Matrix.

    Parameters
    ----------
    N : int
        Number of nodes.

    Returns
    -------
    D2 : (N+1, N+1) ndarray
        Chebyshev differentation matrix
    x : (N+1) array
        Chebyshev x domain
    """
    if N == 0:
        D = 0
        x = 1
        return D, x
    x = np.cos(np.pi * np.arange(N + 1) / N)
    c = np.hstack((2, np.ones(N - 1), 2)) * ((-1.)**np.arange(N + 1))
    X = np.tile(x, (N + 1, 1)).T
    dX = X - X.T
    D = np.outer(c, 1./c) / (dX + np.eye(N + 1))
    D = D - np.diag(np.sum(D.T, axis=0))
    return D, x


import numpy as np
import matplotlib.pyplot as plt

if __name__ == "__main__":
    t = [0.000, 0.375, 0.475, 1.450, 2.050]
    s = [0.411, 0.330, 0.320, 0.300, 0.410]
    t0 = 0.621
    star = '*' * 15

    # ********* Q1 *********
    print('\n', '*' * 12, 'Q1', '*' * 12)
    start = -0.1
    stop = 2.2
    steps = 0.005
    tt = [np.round(step, 3) for step in np.arange(start, stop, steps)]
    print(f"t_min: {start}, dt: {steps}, t_max:{stop}")

    # ********* Q2 *********
    print('\n', '*' * 12, 'Q2', '*' * 12)
    a_mat = np.empty((0, 3))
    for i in t:
        a_mat = np.append(a_mat, np.array([[1, i, i ** 2]]), axis=0)
    x_cap = np.linalg.inv(np.transpose(a_mat).dot(a_mat)).dot(np.transpose(a_mat).dot(s))
    x_cap = np.round(x_cap, 6)
    print(f"x_cap: {x_cap}")
    ss_second_poly = [np.round(np.array([1, t, t ** 2]).dot(x_cap), 6) for t in tt]
    print('Plot 1')
    plt.gcf().number
    plt.figure(1)
    plt.plot(t, s, 'ro', tt, ss_second_poly, 'b-')
    plt.legend(['Points', '2nd Order Polynomial'], loc='best')
    plt.title('2nd Order Polynomial')
    plt.xlabel('t')
    plt.ylabel('s')

    # ********* Q3 *********
    print('\n', '*' * 12, 'Q3', '*' * 12)
    s0_second_poly = np.round(np.array([1, t0, t0 ** 2]).dot(x_cap), 6)
    print(f"s0 second order polynomial: {s0_second_poly}")

    # ********* Q4 *********
    print('\n', '*' * 12, 'Q4', '*' * 12)
    v = a_mat.dot(x_cap) - np.array(s)
    u = 3
    n = len(s)
    rms = np.sqrt((np.transpose(v).dot(v)) / (n - u))
    rms = np.round(rms, 6)
    print(f"rmse: {rms}")

    # ********* Q5 *********
    print('\n', '*' * 12, 'Q5', '*' * 12)
    di = np.empty((0, 1))
    for i in range(len(t)):
        di = np.append(di, 1 / (0.000001 + abs(t0 - t[i])))
    s0_idw = di.dot(s) / sum(di)
    s0_idw = np.round(s0_idw, 6)
    print(f"s0_idw: {s0_idw}")

    di = np.empty((0, 1))
    ss_idw = np.empty((0, 1))
    for ti in tt:
        for i in range(len(t)):
            di = np.append(di, 1 / (0.000001 + abs(ti - t[i])))
        ss_idw = np.append(ss_idw, np.round(di.dot(s) / sum(di), 6))
        di = np.empty((0, 1))
    print('Plot 2')
    plt.figure(2)
    plt.plot(t, s, 'ro', tt, ss_idw, 'g-')
    plt.legend(['Points', 'IDW'], loc='best')
    plt.title('IDW')
    plt.xlabel('t')
    plt.ylabel('s')

    # ********* Q6 *********
    print('\n', '*' * 12, 'Q6', '*' * 12)
    di = np.empty((0, 1))
    for i in range(len(t)):
        di = np.append(di, 1 / (0.000001 + abs(t0 - t[i])) ** 2)
    s0_isdw = di.dot(s) / sum(di)
    s0_isdw = np.round(s0_isdw, 6)
    print(f"s0_isdw: {s0_isdw}")

    di = np.empty((0, 1))
    ss_isdw = np.empty((0, 1))
    for ti in tt:
        for i in range(len(t)):
            di = np.append(di, 1 / (0.000001 + abs(ti - t[i])) ** 2)
        ss_isdw = np.append(ss_isdw, np.round(di.dot(s) / sum(di), 6))
        di = np.empty((0, 1))
    print('Plot 3')
    plt.figure(3)
    plt.plot(t, s, 'ro', tt, ss_isdw, 'y-')

    plt.legend(['Points', 'ISDW'], loc='best')
    plt.title('ISDW')
    plt.xlabel('t')
    plt.ylabel('s')

    print('Plot 4')
    plt.figure(4)
    plt.plot(t, s, 'ro', tt, ss_idw, 'g-', tt, ss_isdw, 'y-')
    plt.legend(['Points', 'IDW', 'ISDW'], loc='best')
    plt.title('IDW & ISDW')
    plt.xlabel('t')
    plt.ylabel('s')

    # ********* Q7 *********
    print('\n', '*' * 12, 'Q7', '*' * 12)
    d0 = np.round(np.mean(s), 6)
    print(f"d0: {d0}")
    a_mat = np.empty((0, 2))
    for ti in t:
        a_mat = np.append(a_mat, np.array([[1, ti]]), axis=0)
    ss_ll = np.empty((0, 1))
    for ti in tt:
        p = np.zeros([5, 5])
        for i in range(len(t)):
            d = np.abs(ti - t[i])
            p[i][i] = np.exp(-((d ** 2) / (2 * (d0 ** 2))))
        x_cap = np.linalg.inv(np.transpose(a_mat).dot(p).dot(a_mat)).dot(np.transpose(a_mat).dot(p).dot(s))
        a0_mat = np.empty((0, 2))
        a0_mat = np.append(a0_mat, np.array([[1, ti]]), axis=0)
        ss_ll = np.append(ss_ll, a0_mat.dot(x_cap))
    print('Plot 5')
    plt.figure(5)
    plt.plot(t, s, 'ro', tt, ss_ll, 'c-')
    plt.legend(['Points', 'LLP'], loc='best')
    plt.title('Local Linear Interpolation -- d0=0.3542')
    plt.xlabel('t')
    plt.ylabel('s')

    d0 = 0.1
    ss_ll_small = np.empty((0, 1))
    for ti in tt:
        p = np.zeros([5, 5])
        for i in range(len(t)):
            d = np.abs(ti - t[i])
            p[i][i] = np.exp(-((d ** 2) / (2 * (d0 ** 2))))
        x_cap = np.linalg.inv(np.transpose(a_mat).dot(p).dot(a_mat)).dot(np.transpose(a_mat).dot(p).dot(s))
        a0_mat = np.empty((0, 2))
        a0_mat = np.append(a0_mat, np.array([[1, ti]]), axis=0)
        ss_ll_small = np.append(ss_ll_small, a0_mat.dot(x_cap))
    print('Plot 6')
    plt.figure(6)
    plt.plot(t, s, 'ro', tt, ss_ll_small, 'c-')
    plt.legend(['Points', 'LLP'], loc='best')
    plt.title('Local Linear Interpolation - d0=0.1')
    plt.xlabel('t')
    plt.ylabel('s')

    d0 = 6
    ss_ll_big = np.empty((0, 1))
    for ti in tt:
        p = np.zeros([5, 5])
        for i in range(len(t)):
            d = np.abs(ti - t[i])
            p[i][i] = np.exp(-((d ** 2) / (2 * (d0 ** 2))))
        x_cap = np.linalg.inv(np.transpose(a_mat).dot(p).dot(a_mat)).dot(np.transpose(a_mat).dot(p).dot(s))
        a0_mat = np.empty((0, 2))
        a0_mat = np.append(a0_mat, np.array([[1, ti]]), axis=0)
        ss_ll_big = np.append(ss_ll_big, a0_mat.dot(x_cap))
    print('Plot 7')
    plt.figure(7)
    plt.plot(t, s, 'ro', tt, ss_ll_big, 'c-')
    plt.legend(['Points', 'LLP'], loc='best')
    plt.title('Local Linear Interpolation - d0=5')
    plt.xlabel('t')
    plt.ylabel('s')

    # ********* Q8 *********
    print('\n', '*' * 12, 'Q8', '*' * 12)
    nearest_num_t0 = np.abs(np.array(tt) - t0)
    t0_index = np.where(nearest_num_t0 == np.min(nearest_num_t0))
    s0_ll = np.round(ss_ll[t0_index][0], 6)
    print(f"s0_ll: {s0_ll}")

    plt.show()


# By
# ████████╗██╗   ██╗ █████╗ ███╗   ██╗    ██╗  ██╗ ██████╗
# ╚══██╔══╝██║   ██║██╔══██╗████╗  ██║    ██║  ██║██╔═══██╗
#    ██║   ██║   ██║███████║██╔██╗ ██║    ███████║██║   ██║
#    ██║   ██║   ██║██╔══██║██║╚██╗██║    ██╔══██║██║   ██║
#    ██║   ╚██████╔╝██║  ██║██║ ╚████║    ██║  ██║╚██████╔╝
#    ╚═╝    ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═══╝    ╚═╝  ╚═╝ ╚═════╝

# Email: ttuan.ho@outlook.com

from scipy.stats import norm, binom, poisson, expon, uniform
from scipy import stats
import numpy as np

"""
Ex2
"""

data = np.array([15.2,14.2,14.0,12.2,14.4,12.5,14.3,14.2,13.5,11.8,15.2])
# a) Construct a 99% two-sided confidence interval on the mean temperature.
# % matlab verson:
# tCrit=tinv(0.995,10)
tCrit = stats.t.ppf(0.995,10)

# interval  is :
np.mean(data) + np.std(data) / ((len(data))**(1/2)) * tCrit * np.array([-1,1])

# c) Suppose that we wanted to be 95% confident that the error in estimating the mean temperature is
# less than 0.4°C. What sample size should be used?
# % matlab verson:
# tStar = tinv(0.975,10)
# s / sqrt(n) * tStar < 0.4

"""
Ex3:
The wall thickness of 25 glass 2-litre bottles was measured by a quality-control engineer.
The sample mean was m = 4.05 millimetres, and the sample standard deviation was s = 0.08
millimetre. Find a 95% lower confidence bound for mean wall thickness. Interpret the interval
you have obtained. Assume the normal distribution for wall thickness.
"""

m = 4.05
n = 25
alpha = 0.05
s = 0.08

tStar = stats.t.ppf(0.95,n-1)

# The lower bound is:
float(m) - s / ((float(n))**(1/2)) * tStar

"""
Ex6:
The article “Repeatability and Reproducibility for Pass/Fail data” (J. of Testing and Eval., 1997,
151-153) reported that in n = 48 trials in a particular laboratory, 16 resulted in ignition of a
particular type of substrate by a lighted cigarette. Find a 95% approximate confidence interval for
π, the true long-run proportion (or probability) of all such trials that would result in ignition.
"""
n = float(48)
k = float(16)
pHat = 16/48

print(f"Checking if the the sample is large enough:")
print(f"n*pHat*(1-pHat)>5={n*pHat*(1-pHat)>5}")

# tStar = norm.ppf(0.975)
tStar = 1.96
print(f"95% CI for pi:")
res = pHat + (tStar * ((pHat*(1-pHat)/n)**(1/2)) * np.array([-1,1]))
print(f"{res}")


"""
Solution to the stride diagonal exercise
=========================================

Solution showing how to use as_strided to stride in diagonal.

"""

import numpy as np
from numpy.lib.stride_tricks import as_strided

#
# Part 1
#

x = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]], dtype=np.int32)

x_diag = as_strided(x, shape=(3,), strides=((3+1)*x.itemsize,))
x_supdiag = as_strided(x[0,1:], shape=(2,), strides=((3+1)*x.itemsize,))
x_subdiag = as_strided(x[1:,0], shape=(2,), strides=((3+1)*x.itemsize,))

print(x_diag)
print(x_supdiag)
print(x_subdiag)

#
# Mini-exercise: (assume C memory order)
#
# 0. How to pick diagonal entries of the matrix
#
# 1. How to pick the super-diagonal entries [2, 6]
#
# 2. The sub-diagonal entries [4, 8]
#
# 99. Can you generalize this for any stride and shape combinations
#     in the initial array?
#
#     If you can, tell me, and maybe numpy.trace can be made faster :)
#

#
# Part 2
#

# Compute the tensor trace

x = np.arange(5*5*5*5).reshape(5,5,5,5)

s = 0
for i in range(5):
    for j in range(5):
        s += x[j,i,j,i]

# by striding and using .sum()

y = as_strided(x, shape=(5, 5), strides=((5*5*5+5)*x.itemsize,
                                         (5*5+1)*x.itemsize))
s2 = y.sum()

assert s == s2


import numpy as np

"""
A2-Part-4: Implement the inverse discrete Fourier transform (IDFT)

Write a function that implements the inverse discrete Fourier transform (IDFT). Given a frequency
spectrum X of length N, the function should return its IDFT x, also of length N. Assume that the
frequency index of the input spectrum ranges from 0 to N-1.

The input argument to the function is a numpy array X of the frequency spectrum and the function should return
a numpy array of the IDFT of X.

Remember to scale the output appropriately.

EXAMPLE: If you run your function using X = np.array([1 ,1 ,1 ,1]), the function should return the following numpy
array x: array([  1.00000000e+00 +0.00000000e+00j,   -4.59242550e-17 +5.55111512e-17j,
    0.00000000e+00 +6.12323400e-17j,   8.22616137e-17 +8.32667268e-17j])

Notice that the output numpy array is essentially [1, 0, 0, 0]. Instead of exact 0 we get very small
numerical values of the order of 1e-15, which can be ignored. Also, these small numerical errors are
machine dependent and might be different in your case.

In addition, an interesting test of the IDFT function can be done by providing the output of the DFT of
a sequence as the input to the IDFT. See if you get back the original time domain sequence.

"""
def IDFT(X):
    """
    Input:
        X (numpy array) = frequency spectrum (length N)
    Output:
        The function should return a numpy array of length N
        x (numpy array) = The N point IDFT of the frequency spectrum X
    """
    ## Your code here
    N = X.shape[-1]
    x = np.zeros(N, dtype=np.complex)

    for n in range(N):
        s = np.exp(1j * 2 * np.pi * n / N * np.arange(N))
        x[n] = X.dot(s)
    x /= N

    return x


# solutions.py
import numpy as np
from matplotlib import pyplot as plt

# Problem 1: Implement this function.
def Newtons_method(f, x0, Df, iters=15, tol=.002):
    '''
    Use Newton's method to approximate a zero of a function.
    Inputs:
        f (function): A function handle. Should represent a function
            from R to R.
        x0 (float): Initial guess.
        Df (function): A function handle. Should represent the derivative
             of f.
        iters (int): Maximum number of iterations before the function
            returns. Defaults to 15.
        tol (float): The function returns when the difference between
            successive approximations is less than tol.
    Returns:
        A tuple (x, converged, numiters) with
        x (float): the approximation for a zero of f
        converged (bool): a Boolean telling whether Newton's method converged
        numiters (int): the number of iterations the method computed
    '''
    xold = x0
    for numiters in range(1,iters+1):
        xnew = xold-f(xold)*1./Df(xold)
        if abs(xnew-xold)<tol:
            return xnew,True,numiters
        else:
            xold = xnew
    return xnew,False,numiters

# Problem 2.1: Implement this function.
def problemTwoOne():
    '''
    Return a tuple of the number of iterations to get five digits of accuracy
    for f = cos(x) with x_0 = 1 and x_0 = 2.
    '''
    f = lambda x : np.cos(x)
    df = lambda x: -1*np.sin(x)
    iter1 = Newtons_method(f,1,df,tol=.00001)
    iter2 = Newtons_method(f,2,df,tol=.00001)
    return iter1[2],iter2[2]

# Problem 2.2: Implement this function.
def problemTwoTwo():
    '''
    Plot f(x) = sin(x)/x - x on [-4,4].  Return the zero of this function to
    7 digits of accuracy.
    '''
    f = lambda x : (1.*np.sin(x))/x - x
    df = lambda x : -1*(x**2+np.sin(x)-x*np.cos(x))/(x**2)
    x = np.linspace(-4,4,num=100)
    plt.plot(x,f(x))
    plt.show()
    xnew,converged,numiters = Newtons_method(f,1,df,tol = .0000001)
    return xnew

# Problem 2.3: Implement this function.
def problemTwoThree():
    '''
    Return a tuple of
    1. The number of iterations to get five digits of accuracy for f(x) = x^9
        with x_0 = 1.
    2. A string with the reason to why you think the convergence is slow for
        this function.
    '''
    f = lambda x : x**9
    df = lambda x : 9*x**8
    xnew,converged,numiters = Newtons_method(f,1,df,iters=3000,tol=1e-5)
    return numiters,"The derivative is very close to zero so it will converge slowly"

# Problem 2.4: Implement this function.
def problemTwoFour():
    '''
    Return a string as to what happens and why for the function f(x) = x^(1/3) where
    x_0 = .01.
    '''
    f = lambda x: np.sign(x)*np.power(np.abs(x), 1./3)
    df = lambda x :1./3./np.power(np.abs(x), 2./3)
    xnew,converged,numiters = Newtons_method(f,.01,df)
    return "The values are getting further and further away from the correct value so it never converges.  Because the derivative at the root is infinity."

# Problem 3 (Optional): Modify the function Newtons_method() to calculate the numerical
# derivative of f using centered coefficients.

def Newtons_method_II(f, x0, Df=None, iters=15, tol=.002):

    if Df == None:
        h = 1e-5
        Df = lambda x: .5 * (f(x+h) - f(x-h))/h

    xold = x0
    for numiters in range(1,iters+1):
        xnew = xold-f(xold)*1./Df(xold)
        if abs(xnew-xold)<tol:
            return xnew,True,numiters
        else:
            xold = xnew
    return xnew,False,numiters

# Problem 4: Implement this function.
def plot_basins(f, Df, roots, xmin, xmax, ymin, ymax, numpoints=1000,iters=15, colormap='brg'):
    '''
    Plot the basins of attraction of f.
    INPUTS:
        f (function): Should represent a function from C to C.
        Df (function): Should be the derivative of f.
        roots (array): An array of the zeros of f.
        xmin, xmax, ymin, ymax (float,float,float,float): Scalars that define the domain
            for the plot.
        numpoints (int): A scalar that determines the resolution of the plot. Defaults to 100.
        iters (int): Number of times to iterate Newton's method. Defaults to 15.
        colormap (str): A colormap to use in the plot. Defaults to 'brg'.
    '''
    xreal = np.linspace(xmin,xmax,numpoints)
    ximag = np.linspace(ymin,ymax,numpoints)
    Xreal,Ximag = np.meshgrid(xreal,ximag)
    Xold = Xreal + 1j*Ximag
    for numiters in xrange(iters):
        x = Xold - 1.* f(Xold)/Df(Xold)
        Xold = x

    convergedarray = np.zeros(Xold.shape)
    for i in range(Xold.shape[0]):
        for j in range(Xold.shape[1]):
            convergedarray[i,j] = np.abs(roots-x[i,j]).argmin()
    plt.pcolormesh(Xreal,Ximag,convergedarray,cmap=colormap)
    plt.show()

# Problem 5: Implement this function.
def problemFive():
    '''
    Run plot_basins() on the function x^3-1 on the domain [-1.5,1.5]x[-1.5,1.5].
    '''
    f = lambda x : x**3 - 1
    Df = lambda x : 3*x**2
    roots = np.array([1,-1j**(1./3),1j**(2./3)])
    xmin = -1.5
    xmax = 1.5
    ymin = -1.5
    ymax = 1.5
    plot_basins(f,Df,roots,xmin,xmax,ymin,ymax)

def testing():
    f = lambda x : x**3-x
    Df = lambda x : 3*x**2 - 1
    roots = np.array([0,1,-1])
    xmin = -1.5
    xmax = 1.5
    ymin = -1.5
    ymax = 1.5
    plot_basins(f,Df,roots, xmin,xmax,ymin,ymax)

if __name__ == '__main__':
    print problemTwoOne()
    print problemTwoTwo()
    print problemTwoThree()
    print problemTwoFour()
    print problemFive()
    print testing()


import scipy.linalg
import numpy as np
from numba import jit

@jit
def sym_decorrelation_jit(W):
    """ Symmetric decorrelation """
    K = np.dot(W, W.T)
    s, u = np.linalg.eigh(K)
    W = (u @ np.diag(1.0/np.sqrt(s)) @ u.T) @ W
    return W

def g_logcosh_jit(wx,alpha):
    """derivatives of logcosh"""
    return np.tanh(alpha * wx)
def gprime_logcosh_jit(wx,alpha):
    """second derivatives of logcosh"""
    return alpha * (1-np.square(np.tanh(alpha*wx)))
# exp
def g_exp_jit(wx,alpha):
    """derivatives of exp"""
    return wx * np.exp(-np.square(wx)/2)
def gprime_exp_jit(wx,alpha):
    """second derivatives of exp"""
    return (1-np.square(wx)) * np.exp(-np.square(wx)/2)

def fastICA_jit(X, f,alpha=None,n_comp=None,maxit=200, tol=1e-04):
    """FastICA algorithm for several units"""
    n,p = X.shape
    #check if n_comp is valid
    if n_comp is None:
        n_comp = min(n,p)
    elif n_comp > min(n,p):
        print("n_comp is too large")
        n_comp = min(n,p)

    #centering
    #by subtracting the mean of each column of X (array).
    X = X - X.mean(axis=0)[None,:]
    X = X.T

    #whitening
    s = np.linalg.svd(X @ (X.T) / n)
    D = np.diag(1/np.sqrt(s[1]))
    k = D @ (s[0].T)
    k = k[:n_comp,:]
    X1 = k @ X

    # initial random weght vector
    w_init = np.random.normal(size=(n_comp, n_comp))
    W = sym_decorrelation_jit(w_init)

    lim = 1
    it = 0

    # The FastICA algorithm
    while lim > tol and it < maxit :
        wx = W @ X1
        if f =="logcosh":
            gwx = g_logcosh_jit(wx,alpha)
            g_wx = gprime_logcosh_jit(wx,alpha)
        elif f =="exp":
            gwx = g_exp_jit(wx,alpha)
            g_wx = gprimeg_exp_jit(wx,alpha)
        else:
            print("doesn't support this approximation negentropy function")
        W1 = np.dot(gwx,X1.T)/X1.shape[1] - np.dot(np.diag(g_wx.mean(axis=1)),W)
        W1 = sym_decorrelation_jit(W1)
        it = it +1
        lim = np.max(np.abs(np.abs(np.diag(W1 @ W.T))) - 1.0)
        W = W1

    S = W @ X1
    A = scipy.linalg.pinv2(W @ k)
    return{'X':X1.T,'A':A.T,'S':S.T}


"""
FEniCS tutorial demo program: Poisson equation with Dirichlet conditions.
Test problem is chosen to give an exact solution at all nodes of the mesh.

  -Laplace(u) = f    in the unit square
            u = u_D  on the boundary

  u_D = 1 + x^2 + 2y^2
    f = -6
"""
#%%
from __future__ import print_function
from fenics import *

# Create mesh and define function space
mesh = UnitSquareMesh(8, 8)
V = FunctionSpace(mesh, 'P', 1)

# Define boundary condition
u_D = Expression('1 + x[0]*x[0] + 2*x[1]*x[1]', degree=2)

def boundary(x, on_boundary):
    return on_boundary

bc = DirichletBC(V, u_D, boundary)

# Define variational problem
u = TrialFunction(V)
v = TestFunction(V)
f = Constant(-6.0)
a = dot(grad(u), grad(v))*dx
L = f*v*dx

# Compute solution
u = Function(V)
solve(a == L, u, bc)

# Plot solution and mesh
plot(u)
plot(mesh)

# Save solution to file in VTK format
vtkfile = File('poisson/solution.pvd')
vtkfile << u

# Compute error in L2 norm
error_L2 = errornorm(u_D, u, 'L2')

# Compute maximum error at vertices
vertex_values_u_D = u_D.compute_vertex_values(mesh)
vertex_values_u = u.compute_vertex_values(mesh)
import numpy as np
error_max = np.max(np.abs(vertex_values_u_D - vertex_values_u))

# Print errors
print('error_L2  =', error_L2)
print('error_max =', error_max)

# Hold plot
#interactive()


# Given two data sets with test scores vs hours of study
# We can run a linear regression to predict test score for a given hour and see the graph

import numpy as np
import matplotlib.pyplot as plt

# the checkError method goes over the list of data once and returns a sum of errors
def checkError(x, y, theta):
    N = len(x)
    y_hat = np.zeros(N)
    sumError = 0
    for i in range(0, N):
        y_hat[i] = theta[0] + theta[1] * x[i]
        sumError += (y_hat[i] - y[i]) **2
    return y_hat, theta, sumError

# the tain method goes over the list of data, finds error and runs gradient descent for correction of error
def train(iterCount, learn_rate, x, y):
    theta = np.zeros(2)
    N = len(x)
    y_hat = np.zeros(N)
    for c in range(0, iterCount):
        sumError = 0
        for i in range(0, N):
            y_hat[i] = theta[0] + theta[1] * x[i]
            sumError += (y_hat[i] - y[i]) **2
        theta = gradientDescent(x, y, theta, learn_rate)
    return y_hat, theta, sumError

# gradientDescent method adjusts theta parameters and returns a rectified theta
def gradientDescent(x, y, theta, l_rate):
    M = len(x)
    y_hat = np.zeros(M)
    for i in range(0, M):
        y_hat[i] = theta[0] + theta[1] * x[i]
        theta[0] = theta[0] - (l_rate * (1/M) * (y_hat[i] - y[i]))
        theta[1] = theta[1] - (l_rate * (1/M) * (y_hat[i] - y[i]) * x[i])
    return theta

# predict method predicts a y values for a given x value and rectified theta parameters
def predict(x, theta):
    print("Theta used for prediction: ", theta)
    y = theta[0] + theta[1] * x
    return y

# readData method reads data from file in columns and loads the columns in x and y data arrays
def readData(file_name, delim=','):
    points = np.genfromtxt(file_name, delimiter=delim)
    y = points[:,0]
    x = points[:,1]
    return x, y

# main method runs the steps of linear regression in sequence
def main():
    #train model on data
    iterCount = 100
    learn_rate = 0.0001

    # test scores are loaded in y while hours of study are loaded in x
    y, x = readData("input/test_score_vs_hour_studied.csv")
    theta = np.zeros(2)

    print("Initial theta: ", theta)

    y_hat, theta, err = checkError(x, y, theta)
    print("Initial Error: {0}  theta: [{1}, {2}]".format(err, theta[0], theta[1]))

    y_hat, theta, err = train(iterCount, learn_rate, x, y)
    print("Afer {0} iterations, Error: {1}  theta: [{2}, {3}]".format(iterCount, err, theta[0], theta[1]))

    # plot actual values of x and y
    plt.subplot(1,2,1)
    plt.scatter(x, y)
    plt.xlabel('x')
    plt.ylabel('y')

    # plot actual values of x against predicted values of y
    plt.subplot(1,2,2)
    plt.scatter(x, y_hat)
    plt.plot(x, y_hat)
    plt.xlabel('x')
    plt.ylabel('y_hat')
    plt.show()

    print(predict(53, theta))

if True:
    main()


# Problem: https://projecteuler.net/problem=286

"""
    . At each distance x, there are two possibilities:
        1. P(scoring at distance x) = 1 - x/q
        2. P(not scoring at distance x) = x/q
    . We can use dynamic programming/memoization to calculate the probability of scoring exactly 20 points:
        DP[x][j] = DP[x-1][j-1] * (1 - x/q) + DP[x-1][j] * x/q
        where DP[x][j] -> the number of ways of scoring exactly j points after trying to score at distances from 1 to x.
    OR we can find a close form function of calculating the probability of scoring exactly 20 points:
        Let f(q) = product_{k from 1 to 50} [k/q + (1-k/q) * x]
                 = (1/q + (1-1/q) * x) * (2/q + (1-2/q) * x) * ... * (50/q + (1-50/q) * x)
        Then the probability of scoring exactly 20 points is the coefficient of x^20.
        For simplicity, let p = 1/q.
    . Either way, we use binary search to find q within (50, +inf)  (or p in (0, 50)) such that the probability = 0.02
"""

from sage.all import *
from decimal import *

getcontext().prec = 12

N = 50

if __name__ == "__main__":
    var('x')
    var('p')

    f = 1
    for i in range(1, N+1):
        f = f * (i*p + (1-i*p)*x)

    g = f.expand()
    h = g.coefficient(x**20)

    target = 0.02
    lowerbound = Rational('0')       # switch to Rational() for higher precision
    upperbound = Rational('1/50')
    count = 0                        # count the number of time we have the correct value
    stop_threshold = 50              # this is the stopping criteria
                                     # if the count of the number of time we have the correct value is higher than stop_threshold, the algorithm will stop
    tol = 10**-12                    # the algorithm will see values in [target-tol, target+tol] to be the same as target

    # binary search for p
    while True:
        m = (lowerbound + upperbound) / 2
        new_val = h(p=m)
        if abs(new_val - target) < tol:
            count = count + 1
            if count == stop_threshold:
                break
        else:
            count = 0
        # this is a monotically increasing polynomial for p in [0, 1/50]
        # so we'll move the upperbound to the middle if the current value is bigger than the target value
        if new_val > target:
            upperbound = m
        else:
            lowerbound = m
    q = float(1/m)

    print("{:.10f}".format(q))


# Question: https://projecteuler.net/problem=239

"""
 . There are 25 primes between 1 and 100. So, if we want exactly 22 primes displaced, there are always 3 primes that are in the correct places.
 . The number of arrangements having exactly 3 primes at their correct place is,
    C(25, 3) * n_derangements
  where C(25, 3) is the number of ways of choosing 3 primes to be fixed in their correct positions,
    and n_derangements is the number of ways of constructing an arrangments of the other 97 numbers such that, none of each of the other 22 primes would be at its correct place. (we don't care about non-primes)
 . To find n_derangements, we use the inclusion-exclusion principle.
 . Let F(m) be number of arrangments that at least m primes would be at correct positions.
     F(m) = C(22, m) * factorial(97-m)
   where C(22, m) is the number of ways of choosing m primes to be fixed at their correct positions,
     and factorial(97-m) is the number of ways of arranging the other 97-m numbers.
 . We have, n_derangements = F(0) - F(1) + F(2) - ... (Principle of inclusion and exclusion)
"""

from sage.all import *

N = 100
N_PRIMES = len(prime_range(N))
N_DISPLACED_PRIMES = 22
N_FIXED_PRIMES = N_PRIMES - N_DISPLACED_PRIMES

if __name__ == "__main__":
    ans = binomial(N_PRIMES, N_FIXED_PRIMES)

    # Principle of inclusion and exclusion
    n_remaining_positions = N - N_FIXED_PRIMES
    n_derangements = 0 # number of ways of rearranging the other 97 numbers so that none of the displaced primes are in their correct position.
    for k in range(N_DISPLACED_PRIMES + 1):
        n_ways_having_at_least_k_primes_inplaced = factorial(n_remaining_positions - k) # fix the k primes, place other numbers randomly
        n_tuples_of_k_primes = binomial(22, k)
        if k % 2 == 1:
            n_derangements -= n_ways_having_at_least_k_primes_inplaced * n_tuples_of_k_primes
        else:
            n_derangements += n_ways_having_at_least_k_primes_inplaced * n_tuples_of_k_primes

    ans *= n_derangements

    ans = ans / factorial(N)

    print(float(ans))


# activation.py
'''
All the most common activations, along with their prime.
Use the function get_activation to pass the activation-type as text
and get the activation and prime back.
'''

# Things to fix
'''
Nothing yet.
'''

# Importing dependencies
import numpy as np

# Activations
def sigmoid(X, act_clip=100):
    X = np.clip(X, -act_clip, act_clip)
    return np.divide(1,np.add(1,np.exp(-X)))
def sigmoid_prime(X, act_clip=100):
    X = np.clip(X, -act_clip, act_clip)
    return sigmoid(X)*(1-sigmoid(X))
def ReLU(X):
    return np.maximum(X, 0, X)
def ReLU_prime(X):
    return 1 * (X > -1e-05)
def tanh(X, act_clip=100):
    X = np.clip(X, -act_clip, act_clip)
    return np.tanh(X)
def tanh_prime(X, act_clip=100):
    X = np.clip(X, -act_clip, act_clip)
    return 1/(np.square(np.cosh(X)))
def arctan(X):
    return np.arctan(X)
def arctan_prime(X):
    return 1/(np.power(X,2)+1.)
def sine(X):
    return np.sin(X)
def sine_prime(X):
    return np.cos(X)
def getActivation(act='tanh'):
    if act.lower() == 'sigmoid': return sigmoid, sigmoid_prime
    if act.lower() == 'relu': return ReLU, ReLU_prime
    if act.lower() == 'tanh': return tanh, tanh_prime
    if act.lower() == 'arctan': return arctan, arctan_prime
    if act.lower() == 'sine': return sine, sine_prime


"""
A fact that I like is that base-exp(1) is the most efficient base for encoding
in terms of the number of total distinct states you have to have access to.

To see this, we simply consider the number of nodes in a tree with branching
factor b needed to encode all states at the leaves.

References:
    https://en.m.wikipedia.org/wiki/Radix_economy
    https://en.wikipedia.org/wiki/Ternary_computer
    https://en.wikipedia.org/wiki/Decimal_computer
"""
import numpy as np
import kwplot
import pandas as pd

# amount_of_information = np.arange(10)

amount_of_information = 1_000_000  # we have 1_000_000 bits

base = np.arange(2, 16)

min_base = 2
max_base = 16
base = np.linspace(min_base, max_base, min(100, min_base * (max_base - 2) + 1)).round()
base = np.unique(np.hstack([base, [0.1, 0.2, 0.3, 0.5, 0.8, 0.9, 1.1, 1.5, 1.9, 2.0, 3.0, 8, 10, 16, np.exp(1), np.pi, np.pi * 2]]))
# It doesn't make much sense to have a base < 2, and the analysis of the
# function in this case is out of scope of this tutorial
base = base[base >= 2]
base.sort()

# First let's consider the integer case (as that is what we can actually
# realize with bits)
df = pd.DataFrame({'base': base})
df['tree_height'] = np.log(amount_of_information) / np.log(df['base'])
df['number_of_nodes'] = df['tree_height'] * df['base']

sns = kwplot.autosns()
ax = sns.lineplot(data=df, x='base', y='number_of_nodes', marker='o')
ax.set_title('Nodes in a decision tree with base-b')


from math import sqrt, exp, log, sin, pi
from sympy import Symbol, Limit, Derivative, solve, sympify, S, simplify, lambdify, pprint, init_printing, symbols
from fractions import Fraction

init_printing( order = 'rev-lex', use_unicode = True )

# 4.2.1

# Suppose f (x) = (4x 3 + 3) (1 − x 2 ).
# What is the equation of the line tangent to f at the point (1, 0)?

x0 = 1
y0 = 0

Fx = ( 4*x**3 + 3 ) * ( 1 - x ** 2 )
x = Symbol( 'x' )
Dx = Derivative( Fx, x ).doit()

m = Dx.subs( { x: x0 } )
x1 = Fx.subs( { x: x0 } )

# 4.2.2

# Suppose f (x) = (x 2 − 2x) (3x + 2).
# What is the equation of the line normal to f
# (i.e., the line perpendicular to the tangent line)
# at the point (1, −5)?

x0 = 1
y0 = -5

Fx = ( x ** 2 - 2*x ) * ( 3*x + 2 )
x = Symbol( 'x' )
Dx = Derivative( Fx, x ).doit()

m = Dx.subs( { x: x0 } )
m1 = -1/m

# Line
y = m1 * ( x - x0 ) + y0

# 4.2.3

# Find the derivative: f(x) = ( x -2 ) / x^2

Fx = ( x - 2 ) / x ** 2
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.2.4

# Find the derivative: f(x) = x^2 + 1 / x + 1

Fx = ( x ** 2 + 1 ) / ( x + 1 )
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.2.5

# Find the derivative:
# v(x) =  ( x + 2 ) ^ 1/2 / ( x - 3 ) ^ 1/3

Vx = ( ( x + 2 ) ** 1/2 ) / ( ( x - 3 ) ** 1/3 )
x = Symbol( 'x' )
Derivative( Vx, x ).doit()

# 4.2.6

# Find the derivative:
# f(x) = ( x + 2 ) ^ 4/3 / 2x
Fx = ( x + 2 ) ** 4/3 / 2*x
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.2.7

# Find the derivative:
# h(x) = ( 2*x**4 + 3*x + 7 ) * ( x**5 - 3*x**2 )
Fx = ( 2*x**4 + 3*x + 7 ) * ( x**5 - 3*x**2 )
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.2.8

# Suppose f (x) = (3x − x 2 ) (2x − x 2 ).
# Find the equation of the line tangent to f at the point (1, 2).

x0 = 1
y0 = 2

Fx = ( 3 * x - x**2 ) * ( 2 * x - x**2 )
x = Symbol( 'x' )
Dx = Derivative( Fx, x ).doit()

m = Dx.subs( { x: x0 } )

# Line
y = m1 * ( x - x0 ) + y0

# 4.2.9

# Find the derivative:
# f(x) = ( t + 3 ) ** 1/2 / ( t - 3 ) ** 3/2
Fx = ( t + 3 ) ** 1/2 / ( t - 3 ) ** 3/2
x = Symbol( 'x' )
Derivative( Fx, x ).doit()


from typing import Union

import numpy as np
import numpy.linalg
import scipy.linalg

def norm(A: np.ndarray,
         p: Union[int, str]) -> float:
    """
    Calculates the induced p-norm of matrix A.

    :param A: matrix to calculate the norm for
    :param p: either the number for L1, L2, ..., Lp norm or "inf" string for
    the infinity norm
    :return: p-norm of matrix A
    """
    A = A.astype(np.float)

    if p == 1:
        return np.max(np.sum(np.abs(A), axis=0))
    elif p == "inf":
        return np.max(np.sum(np.abs(A), axis=1))

    xs = np.random.randn(A.shape[0], 1000)

    # compute the norms and normalized A
    norm_xs = np.sum(np.abs(xs) ** p, axis=0) ** (1 / p)
    normalized_xs = xs / norm_xs

    # apply A to normalized vectors, i.e. calculate Ax
    Ax = A.dot(normalized_xs)

    # compute norms of Ax vectors
    norm_Ax = np.sum(np.abs(Ax) ** p, axis=0) ** (1 / p)

    # get the highest norm
    p_norm = np.max(norm_Ax)

    return p_norm

if __name__ == '__main__':
    A = np.array([[1, 2],
                  [3, 4]])

    print("My:", norm(A, 1), ", library:", np.linalg.norm(A, ord=1))
    print("My:", norm(A, 2), ", library:", np.linalg.norm(A, ord=2))
    print("My:", norm(A, "inf"), ", library:", np.linalg.norm(A, ord=np.inf))


import numpy as np

import matplotlib.pyplot as plt

def calc_pca(data, samples, classes=None):
    exit = []
    previous = []
    mean = np.mean(data, axis = 0)
    mean_sub = data - mean
    cov = np.cov(mean_sub, rowvar = 0)
    eigen_values, eigen_vectors = np.linalg.eig(np.mat(cov))
    eigen_values_sorted = np.argsort(-eigen_values)
    for num_eigen in samples:
        eigen_values_filtered = eigen_values_sorted[:num_eigen]
        eigen_vectors_filtered = eigen_vectors[:,eigen_values_filtered]
        data_matrix = mean_sub * eigen_vectors_filtered
        reconstructed = (data_matrix * eigen_vectors_filtered.T) + mean
        exit.append((num_eigen, reconstructed.real, eigen_vectors_filtered.T.real))
        if classes is not None:
            for i in classes:
                mean_class = np.mean(classes[i], axis = 0)
                mean_sub_class = classes[i] - mean_class
                data_matrix_class = mean_sub_class * eigen_vectors_filtered
                reconstructed_class = (data_matrix_class * eigen_vectors_filtered.T) + mean_class
                exit.append((num_eigen, reconstructed_class.real, None))
    return exit

def print_pca(data):
    for row in data:
        for j in range(row[0]):
            plt.subplot(5,4,j+1)
            reshaped = np.array(row[1][j]).reshape(28,28)
            plt.imshow(reshaped, cmap='gray')
            plt.axis('off')
        plt.show()
        if row[2] is not None:
            for j in range(row[0]):
                plt.subplot(5,4,j+1)
                reshaped = np.array(row[2][j]).reshape(28,28)
                plt.imshow(reshaped, cmap='gray')
                plt.axis('off')
            plt.show()

def read_data(filename):
    with open(filename, 'r') as f:
        lines = f.readlines()

    num_points = len(lines)
    dim_points = 28 * 28
    data = np.empty((num_points, dim_points))
    labels = np.empty(num_points)

    for ind, line in enumerate(lines):
        num = line.split(',')
        labels[ind] = int(num[0])
        data[ind] = [ int(x) for x in num[1:] ]

    return (data, labels)

train_images, train_labels = read_data("C:/Users/suagrawa/Desktop/Spring_2019_IIIT/Monsoon 2019/SMAI Assignments/Assignment-1/sample_train.csv")
test_images, test_labels = read_data("C:/Users/suagrawa/Desktop/Spring_2019_IIIT/Monsoon 2019/SMAI Assignments/Assignment-1/sample_test.csv")

num_classes = 10
samples = [2, 5, 10, 20]
classes = {}
for i in range(num_classes):
    classes[i] = []
for i, image in enumerate(train_images):
    classes[train_labels[i]].append(image)
for i in range(num_classes):
    values = calc_pca(classes[i], samples)
    print_pca(values)
values = calc_pca(train_images, samples, classes)
print_pca(values)
#one_of_each = {}
#for i in classes:
#	one_of_each[i] = [classes[i][0]]
#values = calc_pca(train_images, samples, one_of_each)
#print_pca(values)


import sympy
from sympy import *
import random

def choose_modulus(k,l,m):
	p1 = find_prime(k,l)
	p2 = find_prime(k,l)
	#while not (log(p1,2) + m < log(p2, 2)):
	#	p1 = find_prime(k,l)
	#	p2 = find_prime(k,l)
	#	print(p1, p2)
	print(p1, p2)
	print(log(p1,2)+m < log(p2,2))
	return p1,p2

def choose_encryption_key_old(m):
	e = random.randrange(2,m)
	while not gcd(e, totient(m)) == 1:
		e = random.randrange(2,m)
	return e

def choose_encryption_key(p1,p2):
	m = p1*p2
	e = random.randrange(2, p1*p2)
	while not gcd(e, (p1-1)*(p2-1)) == 1:
		e = random.randrange(2, p1*p2)
	return e

def compute_decryption_key(e, p1, p2):
	# d = e inverse mod phi(m) m = p1*p2
	#phi(m) = (p1-1)*(p2-1)
	d = inv_mod(e, (p1-1)*(p2-1))
	return d

def RSA_encrypt(P, e, m):
	return power_mod(P,e,m)

def RSA_decrypt(C, d, m):
	return power_mod(C,d,m)

def RSA_crack(C, e, m):
	return power_mod(C, inv_mod(e,totient(m)), m)

def power_mod(a, b, m):
	#replace with better version for large numbers
	#return ((a%m)**(b%m))%m
	return pow(a,b,m)

def string_to_int(s):
	return int.from_bytes(s.encode(),'big')

def int_to_string(n):
	return n.to_bytes((n.bit_length()+7)//8,'big').decode()

def find_prime(k, l):
	x = random.randrange(2**k+2, 2**l-1)
	while (not isprime(x)):
		x = random.randrange(2**k+2, 2**l-1)
	return x

def mod_inv_old(a,m):
	#replace with euclid's algorithm - will be using large numbers
	for i in range(0,m):
		if (i*a)%m == 1:
			return i
	return -1

def xgcd(b, n):
    """ Return g, x0, y0
        such that x0*b + y0*n = g
        and g is the gcd of (b,n)"""
    x0, x1, y0, y1 = 1, 0, 0, 1
    while n != 0:
        q, b, n = b // n, n, b % n
        x0, x1 = x1, x0 - q * x1
        y0, y1 = y1, y0 - q * y1
    return b, x0, y0

def inv_mod(b, n):
    """ Return the modular inverse of b mod n
     or None if gcd(b,n) > 1 """
    g, x, _ = xgcd(b, n)
    if g == 1:
        return x % n


from numpy import *

def gaussian(x, amp=1, loc=0, width=1):
  out_ = amp*exp(-(x-loc)**2/(2*width**2))
  if isinstance(out_,ndarray):
    out_[isnan(out_)] = 0
  elif isnan(out_):
    out_ = 0
  return out_

def normalized_gaussian(x,loc=0,width=1):
  amp = 1/(width*sqrt(2*pi))
  return gaussian(x,amp=amp,loc=loc,width=width)

def IRF(x_,t_,celerity=1, loc=1,width=1):
  i__ = empty([len(t_),len(x_)])
  for i,t in enumerate(t_):
    i__[i,:] = normalized_gaussian(x_,loc=loc + celerity*t,width=width)
  return i__

def fundamental_solution(x_,t_,diffusivity=1,celerity=1):
  out__ = empty([len(t_),len(x_)])
  for i,t in enumerate(t_):
    for j,x in enumerate(x_):
      amp = x / (2*t*sqrt(pi*t*diffusivity))
      loc = celerity*t
      width = sqrt(2*diffusivity*t)
      out__[i,j] = gaussian(x,amp=amp,loc=loc,width=width)
  out__[0,:] = 0
  return out__

def H(A, **kwargs):
  '''Returns the conjugate (Hermitian) transpose of a matrix.'''
  return transpose(A, **kwargs).conj()

def mdot(*args):
  '''
  Left-to-right associative matrix multiplication of multiple 2D ndarrays.
  '''
  try:
    ret = args[0]
    for a in args[1:]:
      ret = dot(ret, a)
  except:
    raise
  return ret


# PACKAGE
# Here are the imports again, just in case you need them.
# There is no need to edit or submit this cell.
import numpy as np
import numpy.linalg as la
from readonly.PageRankFunctions import *
np.set_printoptions(suppress=True)
# GRADED FUNCTION
# Complete this function to provide the PageRank for an arbitrarily sized internet.
# I.e. the principal eigenvector of the damped system, using the power iteration method.
# (Normalisation doesn't matter here)
# The functions inputs are the linkMatrix, and d the damping parameter - as defined in this worksheet.
def pageRank(linkMatrix, d) :
    n = linkMatrix.shape[0]
    M = d * linkMatrix + ((1-d) / n) * np.ones([n, n])
    r = 100 * np.ones(n) / n
    lastR = r
    r = M @ r
    i = 0
    while la.norm(lastR - r) > 0.01 :
        lastR = r
        r = M @ r
        i += 1
    print(str(i) + " iterations to convergence.")
    r
    return r


import numpy as np

# Matriz de 10 zeros:
m = np.zeros(10).reshape(1, 10)
print(m)
print()

# Matriz de 10 ones:
m = np.ones(10).reshape(1, 10)
print(m)
print()

# Matriz de 10 cincos:
m = (np.ones(10) * 5).reshape(1, 10)
# ou
m = (np.zeros(10) + 5).reshape(1, 10)
# ou
m = np.linspace(5, 5, 10).reshape(1, 10)
print(m)
print()

# Array de inteiros de 10 a 50
a = np.arange(10, 51)
print(a)
print()

# Array de números pares de 10 a 50
booleanValues = a % 2 == 0
a2 = a[booleanValues]
print(a2)
# ou
a2 = np.arange(10, 51, 2)
print()

# Matriz de 3x3 com valores de 0 a 8
m = np.arange(0, 9).reshape(3, 3)
print(m)
print()

# Matriz identidade de 3x3
m = np.eye(3)
print(m)
print()

# Gerar números aleatorios entre 0 e 1
n = np.random.rand(1)
print(n)
print()

# Gerar array de 25 números aleatorios de uma distribuição normal
n = np.random.randn(25)
print(n)
print()

# Matriz 10x10 ?
m = (np.arange(0, 100) / 100).reshape(10, 10)
print(m)
print()

# Array igualmente espaçado com tamanho 20 de 0 e 1 ?
print(np.linspace(0, 1, 20))

print()
print(' ----------------------- ')
# Slice array 5x5 ignorando 2 primeiras linhas e 1 coluna
m = np.arange(1, 26).reshape(5, 5)
print(m)
print()

print(m[2:5, 1:5])
# ou
# print(m[2:, 1:])
print()

# Acessar o elemento de valor 20
print(m[3][4])
# ou
print(m[3, 4])
# ou
print(m[3, -1])
print()

# Retornar [2, 7, 12]
print(m[:3, 1:2])
# ou
# print(m[0:3, 1])
# ou
# print(m[:3, 1])
print()

# Retornar [21, 22, 23, 24, 24]
print(m[4, :])
# ou
# print(m[-1, :])
# ou
# print(m[4])
print()

print(m[3:, :5])

print()
print(' ----------------------- ')
# Obter a soma dos valores
print(m.sum())
print(np.sum(m))
print()

# Obter o desvio padrão
print(np.std(m))
print()

# Soma de todas as colunas
print(np.sum(m, axis=0))
# ou
# m.sum(axis=0)


import numpy as np
import numpy.linalg as LA

# マスの数(0からNまで)
N = 4

# 遷移行列Mを作る
M = np.zeros((N + 1, N + 1))
for i in range(N + 1):
    if i in (0, N):
        M[i][i] = 1.0
    else:
        M[i + 1][i] = 0.5
        M[i - 1][i] = 0.5

# Mのべき乗を計算し、M^{\infty}がどうなりそうか見てみる
np.set_printoptions(precision=3)
Minf = M
for _ in range(100):
    Minf = M@Minf
print(Minf)

# 固有ベクトルを求め、Mを対角化する
w, P = LA.eig(M)
Pinv = LA.inv(P)
D = np.diag(w)
print("D=")
print(D)
print("P^-1MP=")
print(Pinv@M@P)

# M^{\infty}を求める
Dinf = np.diag([1, 1, 0, 0, 0])
Minf = P@Dinf@Pinv
print(Minf)

# 1のマスから始めた場合
a = (np.array([0, 1, 0, 0, 0]))
print(Pinv@a)

# 2のマスから始めた場合
a = (np.array([0, 0, 1, 0, 0]))
print(Pinv@a)


import numpy as np
import statsmodels.api as sm
import math
import matplotlib.pyplot as plt
from scipy.integrate import quad
import sys
import os
import logging
from brd_mod.brdgeo import *
from brd_mod.brdecon import *

def dot(x, y):
	'''
	Dot product between two vector-like arrays
	'''
	if len(x) != len(y):
		print("Array sizes are not equal.")
		return

	sum = 0
	for i in range(0, len(x)):
		sum += (x[i]*y[i])

	return sum

def sum(x):
	'''
	Calculates sum of array
	'''
	total= 0
	for i in range(0, len(x)):
		total += x[i]

	return total

def mean(x):
	'''
	Calculates the mean value of an array
	'''
	return sum(x)/len(x)

def square(x):
	'''
	Squares each value of an array
	'''
	series= []
	for i in range(0, len(x)):
		series.append(x[i]**2)

	return series

def variance(x):
	'''
	Calculates population variance of an array
	'''
	sample= mean(x)
	ses= []
	for i in range(0, len(x)):
		ses.append((x[i]-sample)**2)

	return sum(ses)/(len(x))

def covariance(x, y):
	'''
	Calculates co-variance between two arrays
	'''
	if len(x) != len(y):
		print("Array sizes are not equal.")
		return

	x_mean= mean(x)
	y_mean= mean(y)
	x_ses= []
	y_ses= []
	for i in range(0, len(x)):
		x_ses.append(x[i]-x_mean)
		y_ses.append(y[i]-y_mean)

	return dot(x_ses, y_ses)/len(x)

def std_dev(x):
	'''
	Calculates population standard deviation
	of an array
	'''
	return math.sqrt(variance(x))

def median(x):
	'''
	Calculates the median value of an array
	'''
	x.sort()
	n= len(x)
	if n < 1:
		print("List too short.")
		return

	if n % 2 ==1:
		return x[n//2]

	else:
		return sum(x[n//2-1:n//2+1])/2.0

def corr_coef(x, y):
	'''
	Calculates Pearson Correlation Coefficient
	of an array
	'''
	if len(x) != len(y):
		print("Array sizes are not equal.")
		return

	x_mean= mean(x)
	y_mean= mean(y)
	length= len(x)
	xx_arr= []
	yy_arr= []
	for i in range(0, len(x)):
		xx_arr.append((x[i]**2)-length*(x_mean**2))
		yy_arr.append((y[i]**2)-length*(y_mean**2))

	ss_xy = ((dot(x, y))-(length*x_mean*y_mean))
	ss_xx= sum(xx_arr)
	ss_yy= sum(yy_arr)

	return (length*dot(x, y) - sum(x)*sum(y))/math.sqrt(( \
		length*sum(square(x))-sum(x)**2)*(length*sum(square(y))-sum(y)**2))

def cointegration_strength(data1, data2):
	'''
	Runs Cointegration Test on Two Datasets
	data1= data series 1
	data2= data series 2
	Returns t-stat and p-value of Cointegration
	'''
	data1= series_to_array(data1).flatten()
	data2= series_to_array(data2).flatten()

	if len(data1) != len(data2):
		return "Sizes do not Match"

	return ts.coint(data1, data2)[:2]

def norm_pdf(x, mean=0, std=1):
	'''
	Probability density function using
	normal distribution with pre-specified
	mean and standard deviation (default: standard
	normal distribution)
	'''
	expo= -(x-mean)**2/(2*std**2)
	prefix= 1/math.sqrt(2*math.pi*std**2)
	return prefix*math.exp(expo)

def error_func(t):
	'''
	Integrand for cumulative distribution function
	for normal distribution
	'''
	return math.exp(-t**2)

def norm_cdf(x, mean=0, std=1):
	'''
	Cumulative distribution function using
	normal distribution with pre-specified
	mean and standard deviation (default: standard
	normal distribution)
	'''
	inp= (x-mean)/(std*math.sqrt(2))
	integ =quad(error_func, 0, inp)[0]
	return (1/2)*(1+(2/math.sqrt(math.pi))*integ)

def skewness(x):
	'''
	Calculates skewness of an array from a
	standard normal distribution
	'''
	x_mean= mean(x)
	length= len(x)
	x_cu= []
	for i in range(0, len(x)):
		x_cu.append(math.pow(x[i]-x_mean, 3))

	return sum(x_cu)/(math.pow(std_dev(x),3))

def kurtosis(x):
	'''
	Calculates kurtosis of an array from a
	standard normal distribution
	'''
	x_mean= mean(x)
	length= len(x)
	x_qu= []
	for i in range(0, len(x)):
		x_qu.append(math.pow(x[i]-x_mean, 4))

	return sum(x_qu)/(length*math.pow(std_dev(x), 4))

def step_generation(x_min=-3, x_max=3, step=0.0001):
	'''
	Generates an array of higher resolution
	between two boundaries and a specified
	step value
	'''
	n = int(round((x_max - x_min)/float(step)))
	return([x_min + step*i for i in range(n+1)])

def plot_norm(mean=0, std=1, pdf=True, x_min=-3, x_max=3, step=0.0001):
	'''
	Plots a normal distribution from a
	pre-specified mean and standard deviation
	using two boundaries and a specified step
	value
	'''
	x_list= step_generation(x_min, x_max, step)
	y_list= []
	for x in x_list:
		if pdf:
			y_list.append(norm_pdf(x, mean, std))
		else:
			y_list.append(norm_cdf(x, mean, std))

	plt.plot(x_list, y_list)
	plt.show()

def boxcox_transformation(data, param):
	'''
	BoxCox Transformation on Dataset:
	log(y_t) if param=0
	(y_t^param-1)/param otherwise
	'''
	return data.apply(boxcox_aux, args=(param,))

def boxcox_aux(param, value):
	'''
	Auxillary Function to Apply BoxCox to Individual Data
	'''
	if param ==0:
		return math.log(value)
	else:
		return (math.pow(value, param)-1)/float(param)

def reverse_boxcox_transformation(data, param):
	'''
	BoxCox Transformation on Dataset:
	log(y_t) if param=0
	(y_t^param-1)/param otherwise
	'''
	return data.apply(reverse_boxcox_aux, args=(param,))

def reverse_boxcox_aux(param, value):
	'''
	Auxillary Function to Apply Reverse BoxCox to Individual Data
	'''
	if param ==0:
		return math.exp(value)
	else:
		try:
			base= (param*value)+1
			expo= 1/param
			return math.pow(base, expo)
		except:
 			print("Negative Value Encountered, Reverse Transformation Failed.")
 			return -1

def back_transform(data, param):
	'''
	Back-Transforms Mean for Box-Cox Transformation
	'''
	var= data.var()
	return data.apply(back_transform_aux, args=(param, var,))

def back_transform_aux(param, var, value):
	'''
	Auxillary Function to Back-Transform Mean for Individual Data
	'''
	if param==0:
		return math.exp(value)*(1+(var/2))
	else:
		try:
			base= (param*value+1)
			expo= 1/param
			expart= math.pow(base, expo)
			varnum= var*(1-param)
			varden= 2*math.pow(base, 2)
			return expart*(1+(varnum/varden))
		except:
			print("Negative Value Encountered, Reverse Transformation Failed.")
			return -1

def log_transformation(data):
	'''
	Basic function to take log of each data point
	'''
	return data.apply(math.log)

def set_union(x, y):
	'''
	Returns array that represents the sorted union
	of two input arrays
	'''
	temp= x
	for i in y:
		try:
			val= temp.index(i)

		except:
			temp.append(i)

	temp.sort()
	return temp

def set_intersection(x, y):
	'''
	Returns array that represents the sorted intersection
	of two input arrays
	'''
	temp= []
	for i in x:
		try:
			val= y.index(i)
			if val > -1:
				temp.append(i)
		except:
			pass

	temp.sort()
	return temp

import numpy,from scipy,import statsmodels


"""
==================================================
Using histograms to plot a cumulative distribution
==================================================

This shows how to plot a cumulative, normalized histogram as a
step function in order to visualize the empirical cumulative
distribution function (CDF) of a sample. We also show the theoretical CDF.

A couple of other options to the ``hist`` function are demonstrated.
Namely, we use the ``normed`` parameter to normalize the histogram and
a couple of different options to the ``cumulative`` parameter.
The ``normed`` parameter takes a boolean value. When ``True``, the bin
heights are scaled such that the total area of the histogram is 1. The
``cumulative`` kwarg is a little more nuanced. Like ``normed``, you
can pass it True or False, but you can also pass it -1 to reverse the
distribution.

Since we're showing a normalized and cumulative histogram, these curves
are effectively the cumulative distribution functions (CDFs) of the
samples. In engineering, empirical CDFs are sometimes called
"non-exceedance" curves. In other words, you can look at the
y-value for a given-x-value to get the probability of and observation
from the sample not exceeding that x-value. For example, the value of
225 on the x-axis corresponds to about 0.85 on the y-axis, so there's an
85% chance that an observation in the sample does not exceed 225.
Conversely, setting, ``cumulative`` to -1 as is done in the
last series for this example, creates a "exceedance" curve.

Selecting different bin counts and sizes can significantly affect the
shape of a histogram. The Astropy docs have a great section on how to
select these parameters:
http://docs.astropy.org/en/stable/visualization/histogram.html

"""

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(19680801)

mu = 200
sigma = 25
n_bins = 50
x = np.random.normal(mu, sigma, size=100)

fig, ax = plt.subplots(figsize=(8, 4))

# plot the cumulative histogram
n, bins, patches = ax.hist(x, n_bins, density=True, histtype='step',
                           cumulative=True, label='Empirical')

# Add a line showing the expected distribution.
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
     np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
y = y.cumsum()
y /= y[-1]

ax.plot(bins, y, 'k--', linewidth=1.5, label='Theoretical')

# Overlay a reversed cumulative histogram.
ax.hist(x, bins=bins, density=True, histtype='step', cumulative=-1,
        label='Reversed emp.')

# tidy up the figure
ax.grid(True)
ax.legend(loc='right')
ax.set_title('Cumulative step histograms')
ax.set_xlabel('Annual rainfall (mm)')
ax.set_ylabel('Likelihood of occurrence')

plt.show()

#############################################################################
#
# .. admonition:: References
#
#    The use of the following functions, methods, classes and modules is shown
#    in this example:
#
#    - `matplotlib.axes.Axes.hist` / `matplotlib.pyplot.hist`


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

" In this pyfile we will give the solution of linear equation system in details. That is, not only the final result will be given, but also the L, U matrix and the determination of A will be listed here for your convinience. "

import numpy as np

""" Input: Ax=b
A = np.array([[1,3,5],
              [2,5,2],
              [9,3,4]
              ]) # Here input your coefficient matrix
b = np.array([10,24,31]) # Here input the vector
"""

def check(A):
    row = A.shape[0]
    col = A.shape[1]
    if row!=col:
        print("Input error: A is not a square matrix")
        return 0
    else:
        if np.linalg.det(A)==0:
            print("The determination of A is equal to zero")
            return 0
        else:
            if row == 1:
                print("The dimension of matrix is 1")
                return 0
            else:
                return row
def Decomposition(A):
    if check(A) == 0:
        print("Error")
    else:
        print("det(A)=%r"%np.linalg.det(A))
        dim = check(A)
        L = np.eye(dim)
        U = np.zeros_like(A)
        U[0,:]=A[0,:]
        L[1:,0]=A[1:,0]/U[0,0]
        for r in range(1,dim):
            for l in range(1,r):
                L[r,l]=1/U[l,l]*(A[r,l]-L[r,:l]@U[:l,l])
            for u in range(r,dim):
                U[r,u]=A[r,u]-L[r,:r]@U[:r,u]
    print("L=\n",L,"\n","U=\n",U)
    return L,U

#Decomposition(A)

def Solve(A,b):
    L,U = Decomposition(A)
    y = np.linalg.solve(L,b)
    x = np.linalg.solve(U,y)
    print("y=\n",y,"\n","x=\n",x)
    return y,x

#Solve(A,b)