import numpy as np

class LogisticRegression:
    '''
    将线性模型的结果用对数几率+阈值输出，转为离散变量，实现了回归到分类的连接。
    模型：sigmod函数：
    预测目标属于伯努利分布--伯努利分布同正态分布一样属于指数分布族--指数分布族的3个假设--推导出sigmod表达式
    详见：http://logos.name/archives/187
    策略：损失函数：
    由极大似然函数取对数再取反所得。
    首先要统一概率表达式：P(y|x,θ)=hθ(x)^y*(1−hθ(x))^(1−y)
    然后极大似然函数是P的连乘，损失函数是其对数取反。
    算法：梯度下降：
    利用g'(z) = g(z)(1-g(z))  z=w*x
    求导即得梯度为 X(hθ(X)−Y)
    故θ=θ−αX(hθ(X)−Y)
    '''
    def __init__(self,X,y,lr=0.01):
        self.X = np.c_[np.ones(len(X)),X]
        self.y = y
        self.theta = np.ones(self.X.shape[1])
        self.lr = lr
        self.sigmod = lambda X: 1/(1+np.e**(-X.dot(self.theta)))

    def loss(self):
        '''
        J(θ)=−lnL(θ)=−∑(y(i)log(hθ(x(i)))+(1−y(i))log(1−hθ(x(i))))其中i=[1,m]
        '''
        id1 = np.where(self.y==1)[0]
        id0 = np.where(self.y==0)[0]
        loss1 = -np.log(self.sigmod(self.X[id1])).sum()
        loss0 = -np.log(1-self.sigmod(self.X[id0])).sum()
        return loss1+loss0

    def train(self,max_iter = 400):
        '''BGD'''
        self.lossrst = []
        while len(self.lossrst)<=max_iter:
            errors = self.sigmod(self.X)-self.y
            self.theta -= self.lr*self.X.T.dot(errors)
            missed = ((self.X.dot(self.theta)>0)!=self.y).sum()
            self.lossrst.append((self.loss(), missed, self.theta))
        return 'train done!'

if __name__=='__main__':

    import pandas as pd
    df = pd.read_csv('./StatlogHeart.csv',header=None)
    y = df.pop(13).values-1
#    from sklearn.datasets import load_iris
#    iris = load_iris()
#    df = pd.DataFrame(iris.data, columns=iris.feature_names).iloc[:100,:2]
#    y = iris.target[:100]

    df = df.transform(lambda x:(x-x.mean())/x.std()) ## 标准化不可少

    LR = LogisticRegression(df.values,y,lr=0.0008)
    LR.train()
    train_info = pd.DataFrame(LR.lossrst,columns=['loss','missed','theta'])
    train_info.plot(y=['loss','missed'],secondary_y=['missed'],marker='o')


"""
Structure of a Neural Network to learn the `XOR` function
- 2 input features
- 2 hidden neurons
- 1 output neuron
"""
import numpy as np
import random
from time import time

def sigmoid(z):
    return 1. / (1. + np.exp(-z))

if __name__ == '__main__':
    # Generate dataset of `XOR` examples
    # We define the array as suggested by Andrew Ng: features are rows, examples are columns
    # This makes the computation of the linear combination match the math closer: W^T * X
    x = np.array([[0., 0., 1., 1.], [0., 1., 0., 1.]])
    y = np.array([[0., 1., 1., 0.]])
    n = x.shape[1]

    seeds = [2, 10, 24, 45, 98, 120, 350, 600, 899, 1000]
    runtimes = []

    # Execute the training with each different seed and time them
    for seed in seeds:
        print(f"Running with seed: {seed}")
        random.seed(seed)
        start = time()

        # Initialize weights and biases
        # Remember: they have to be initialized to random values in order to break symmetry!
        # Input to hidden
        w0 = np.random.rand(2, 2)
        b0 = np.random.rand(2, 1)
        # Hidden to output
        w1 = np.random.rand(2, 1)
        b1 = np.random.rand(1, 1)

        # Train using gradient descent
        epochs = 100000
        alpha = 0.5

        for _ in range(epochs):
            # Forward propagation
            a1 = sigmoid(np.dot(w0.T, x) + b0)
            y_hat = sigmoid(np.dot(w1.T, a1) + b1)

            # Loss (MSE) multiplied by a 1/2 term to make the derivative easier
            _loss = (1. / (2. * n)) * np.sum((y_hat - y) ** 2)

            # Backpropagation
            dy_hat = (y_hat - y) / float(n)
            dz2 = (y_hat * (1. - y_hat)) * dy_hat
            dw1 = np.dot(a1, dz2.T)
            db1 = np.sum(dz2, axis=1, keepdims=True)
            dz1 = (a1 * (1. - a1)) * np.dot(w1, (dy_hat * (y_hat * (1. - y_hat))))
            dw0 = np.dot(x, dz1.T)
            db0 = np.sum(dz1, axis=1, keepdims=True)

            # Weight and bias update
            # We average the gradients so that we can use a larger learning rate
            w0 = w0 - alpha * (dw0 / float(n))
            w1 = w1 - alpha * (dw1 / float(n))
            b0 = b0 - alpha * (db0 / float(n))
            b1 = b1 - alpha * (db1 / float(n))

        # Record the time
        runtimes.append(round((time() - start) * 1000.))

    # Save runtimes to a file for further processing
    print('Saving runtimes to `python.txt`')
    with open('data/python.txt', 'w') as f:
        for t in runtimes:
            f.write(str(t) + '\n')


#! /usr/bin/env python3

import numpy as np
import scipy.fftpack
import numpy as np

class Signals:

    def __init__(self, Amplitude=0, Frequency=0, Phase=0, SamplesPerSecond=0, StopTime=0):
        self.Amplitude = Amplitude
        self.Frequency = Frequency
        self.Phase = Phase
        self.SamplesPerSecond = SamplesPerSecond
        self.StopTime = StopTime
        self.x = 0
        self.signal = 0
        self.fft = 0
        self.max_freq = 0
        return

    def SineWave(self):
        dt = 1/self.SamplesPerSecond
        self.x = np.arange(0, self.StopTime-dt, dt)
        self.signal = self.Amplitude * np.sin((2*np.pi*self.Frequency*self.x) + self.Phase)
        return self.signal

    def CosineWave(self):
        dt = 1/self.SamplesPerSecond
        self.x = np.arange(0, self.StopTime-dt, dt)
        self.signal = self.Amplitude * np.cos((2*np.pi*self.Frequency*self.x) + self.Phase)
        return self.signal

    def do_fft(self):
        self.fft = scipy.fftpack.fft(self.signal)
        return self.fft

    def max_frequency(self):
        """
        # https://stackoverflow.com/questions/3694918/how-to-extract-frequency-associated-with-fft-values-in-python
        """
        freqs = scipy.fftpack.fftfreq(len(self.fft))
        idx = np.argmax(np.abs(self.fft))
        freq = freqs[idx]
        self.max_freq = abs(freq * self.SamplesPerSecond)
        return self.max_freq

    def __add__(self, x):
        z = x.signal + self.signal
        signal =Signals()
        signal.signal = z
        return signal


'''
    Package: cs771
    Module: helloWorld
    Author: Puru
    Institution: CSE, IIT Kanpur
    License: GNU GPL v3.0

    Generate synthetic data of various sorts
'''

import numpy as np
import numpy.linalg as lin
import numpy.random as rnd

# Return n data points (as an n x d array) shaped as a hemisphere
# The manner of generation used below is similar to the sklearn
# method make_moons.However, the method below gives the additional
# flexibility of flipping and translating the moons
def genMoonData( d, n, mu, r, flipped = False ):
    X = np.vstack( (np.cos( np.linspace( 0, np.pi, n ) ), np.sin( np.linspace( 0, np.pi, n ) ) ) ).T
    if flipped:
	    X[:,1] = -np.abs( X[:,1] )
    else:
        X[:,1] = np.abs( X[:,1] )
    X = (X * r) + mu
    return X

# Return n data points (as an n x d array) sampled from N(mu, sigma^2 . I)
def genSphericalNormalData( d, n, mu, sigma ):
    X = rnd.normal( 0, sigma, (n, d) ) + mu
    return X

# Return n data points (as an n x d array) sampled from N(mu, cov)
def genNormalData( d, n, mu, cov ):
    X = rnd.multivariate_normal( mu, cov, n )
    return X

# Return n data points (as an n x d array) sampled from the surface of sphere of radius r centered at mu
def genSphericalData( d, n, mu, r ):
    X = rnd.normal( 0, 1, (n, d) )
    norms = lin.norm( X, axis = 1 )
    X = X / norms[:, np.newaxis]
    X = (X * r) + mu
    return X

# Return n data points (as an n x d array) sampled from the surface of ellipse of covariance cov centered at mu
def genEllipticalData( d, n, mu, cov ):
    X = genSphericalData( d, n, np.zeros((d,)), 1 )
    L = lin.cholesky( cov )
    X = np.matmul( X, L ) + mu
    return X


import numpy as np

data_path = "data/problem_07.txt"
# data_path = "data/problem_07_test.txt"

data = np.genfromtxt(data_path, dtype=int, delimiter=",")

# 🦀 🦀 🦀 🦀 🦀
# data = (np.random.random(162) * 100203).astype(np.int64) + 5

# part 1
print(f"Part 1 solution: {np.abs(data - int(np.median(data))).sum()}")

# part 2
# barbaric O(N * M) brute force way
# distances = np.abs(data.reshape(-1, 1) - np.arange(data.min(), data.max() + 1, dtype=np.int64))
# fuel_cost = (distances * (distances + 1) // 2).sum(axis=0)
# centroid = np.argmin(fuel_cost) + data.min()

# print(f"best position: {centroid}")
# print(f"Part 2 solution: {fuel_cost.min()}")

# elegant O(N) calculus way
# Unfortunately the cost function for 🦀_i is not continuously differentiable, so let's instead use
# the equation for the sum of an arithmetic series,
#
#      ε_i = s(s + 1)/2
#
# where
#        c = the centroid
#        s = (x_i - c) = the distance the 🦀 boi has to move
#
# find centroid c that minimizes the total cost ε:
#
#        ε = Σ[(x_i + c)(x_i - c + 1) / 2]
#
#    δε/δc = 1/2 δ/δc Σ[c² - (2x_i + 1)c]
#          = 1/2 Σ[2c - 2x_i - 1]
#          = 0
#      2cN = 2 Σ[x_i] - N
#        c = (2 Σ[x_i] - N) / 2N

centroid = int(np.round((2 * data.sum() - len(data)) / (2 * len(data))))

# Since our continuous cost function is not symmetrical about s = 0 and actually has a minumum at
# s = -1/2, the nearest integer centroid value is occasionally (when???) too low by 1.
# Let's just check the next integer up as well.
centroid_candidates = np.array([centroid, centroid + 1])
distances = np.abs(data.reshape(-1, 1) - centroid_candidates)
fuel_cost = (distances * (distances + 1) // 2).sum(axis=0)
centroid = centroid_candidates[np.argmin(fuel_cost)]

print(f"best position: {centroid}")
print(f"Part 2 solution: {fuel_cost.min()}")


from scipy.spatial import Delaunay, delaunay_plot_2d, Voronoi, voronoi_plot_2d
import matplotlib.pyplot as plt
import numpy as np

w = h = 360

n = 6
np.random.seed(0)
pts = np.random.randint(0, w, (n, 2))

print(pts)
# [[172  47]
#  [117 192]
#  [323 251]
#  [195 359]
#  [  9 211]
#  [277 242]]

print(type(pts))
# <class 'numpy.ndarray'>

print(pts.shape)
# (6, 2)

tri = Delaunay(pts)

print(type(tri))
# <class 'scipy.spatial.qhull.Delaunay'>

fig = delaunay_plot_2d(tri)
fig.savefig('data/dst/scipy_matplotlib_delaunay.png')
plt.close()

# ![data/dst/scipy_matplotlib_delaunay.png](data/dst/scipy_matplotlib_delaunay.png)

print(tri.points)
# [[172.  47.]
#  [117. 192.]
#  [323. 251.]
#  [195. 359.]
#  [  9. 211.]
#  [277. 242.]]

print(tri.points == pts)
# [[ True  True]
#  [ True  True]
#  [ True  True]
#  [ True  True]
#  [ True  True]
#  [ True  True]]

print(tri.simplices)
# [[0 1 4]
#  [1 3 4]
#  [5 0 2]
#  [5 1 0]
#  [3 5 2]
#  [5 3 1]]

print(pts[tri.simplices])
# [[[172  47]
#   [117 192]
#   [  9 211]]
#
#  [[117 192]
#   [195 359]
#   [  9 211]]
#
#  [[277 242]
#   [172  47]
#   [323 251]]
#
#  [[277 242]
#   [117 192]
#   [172  47]]
#
#  [[195 359]
#   [277 242]
#   [323 251]]
#
#  [[277 242]
#   [195 359]
#   [117 192]]]

vor = Voronoi(pts)

print(type(vor))
# <class 'scipy.spatial.qhull.Voronoi'>

fig = voronoi_plot_2d(vor)
fig.savefig('data/dst/scipy_matplotlib_voronoi.png')
plt.close()

# ![data/dst/scipy_matplotlib_voronoi.png](data/dst/scipy_matplotlib_voronoi.png)

print(vor.vertices)
# [[ 41.71518987  80.51265823]
#  [ 82.09150528 310.02013526]
#  [331.19719626  87.04766355]
#  [218.67630058 147.63583815]
#  [282.99117647 333.43398693]
#  [182.6014461  263.07537248]]

print(vor.regions)
# [[-1, 0, 1], [], [5, 3, 2, 4], [3, 0, -1, 2], [4, -1, 2], [5, 1, 0, 3], [5, 1, -1, 4]]

print([r for r in vor.regions if -1 not in r and r])
# [[5, 3, 2, 4], [5, 1, 0, 3]]

for region in [r for r in vor.regions if -1 not in r and r]:
    print(vor.vertices[region])
# [[182.6014461  263.07537248]
#  [218.67630058 147.63583815]
#  [331.19719626  87.04766355]
#  [282.99117647 333.43398693]]
# [[182.6014461  263.07537248]
#  [ 82.09150528 310.02013526]
#  [ 41.71518987  80.51265823]
#  [218.67630058 147.63583815]]

fig, ax = plt.subplots()
voronoi_plot_2d(vor, ax)

for region, c in zip([r for r in vor.regions if -1 not in r and r], ['yellow', 'pink']):
    ax.fill(vor.vertices[region][:, 0],
            vor.vertices[region][:, 1],
            color=c)

fig.savefig('data/dst/scipy_matplotlib_voronoi_fill.png')
plt.close()
# /usr/local/lib/python3.7/site-packages/scipy/spatial/_plotutils.py:20: MatplotlibDeprecationWarning: The ishold function was deprecated in version 2.0.
#   was_held = ax.ishold()

# ![data/dst/scipy_matplotlib_voronoi_fill.png](data/dst/scipy_matplotlib_voronoi_fill.png)

fig, ax = plt.subplots(figsize=(4, 4))
delaunay_plot_2d(tri, ax)
voronoi_plot_2d(vor, ax, show_vertices=False)

ax.set_xlim(0, w)
ax.set_ylim(0, h)
ax.grid(linestyle='--')

fig.savefig('data/dst/scipy_matplotlib_delaunay_voronoi.png')
plt.close()
# /usr/local/lib/python3.7/site-packages/scipy/spatial/_plotutils.py:20: MatplotlibDeprecationWarning: The ishold function was deprecated in version 2.0.
#   was_held = ax.ishold()

# ![data/dst/scipy_matplotlib_delaunay_voronoi.png](data/dst/scipy_matplotlib_delaunay_voronoi.png)


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

data = pd.read_csv("Salary_Data.csv")
#print(data.head())

X,y = data.iloc[:, 0],data.iloc[:, 1]
print(X,y)

#data visualisation

plt.scatter(X,y)
plt.show()

#inital declaration

m = 0
c = 0
l = 0.0001         #learning rate - this value should me minimum for a minimization problem
no_of_iter = 1000 #number of iteration required for reaching the global minimum point for optimizing the cost function
n = float(len(X))

for i in range(no_of_iter):
	y_pred = m*X + c
	#print(y_pred)
	d_m = (-2/n)*sum(X*(y - y_pred)) #partial derivation wrt slope
	d_c = (-2/n)*sum(y - y_pred)     #partial derivation wrt y-intercept
	m = m - l*d_m
	c = c - l*d_c

print(round(m,2),round(c,2)) #this is the required slope and intercept of the regression line

#---------------------------plottting regression line----------------------------------------------------

y_pred = m*X + c
plt.scatter(X, y)
plt.scatter(X, y_pred)
plt.show()

print("the required regression line:","y = ",round(m,2),"X + ",round(c,2))


import numpy as np
from numpy import array
from numpy import mean
from numpy.linalg import eig

D = array([[4, -1], [-2, 3]])
print("We have the data: \n D=")
print(D)
MU = mean(D.T, axis=1)
print("Step1: Compute the mean vector: \n mu =")
print(MU)
Z = D - MU
print("Step2: Subtract the mean from data: Z = X - MU \n Z =")
print(Z)
S = len(Z) * np.cov(Z.T)
print("Step3: Calculate the scatter matrix: \n S =")
print(S)
values, vectors = eig(S)
print("Step4: Compute the eigen vectors and eigen values.")
print("Eigen vectors (e_i):")
print(vectors)
print("Eigen values (lambda_i:")
print(values)
P = vectors.T.dot(Z.T)
print("Step5: Projected Data:")
print(P.T)


import numpy as np

def sigmoid(z, grad=False):
    return 1 / (1 + np.exp(-z)) if not grad else sigmoid(z) * (1 - sigmoid(z))

def relu(z, grad=False):
    if not grad:
        return np.maximum(z, 0)
    else:
        dz = np.zeros_like(z)
        for i in range(z.shape[1]):
            dz[:, i][z[:, i] > 0] = 1
        return dz

def softmax(z):
    exp = np.exp(z)
    return exp / exp.sum(axis=0)

registry = {
    "relu": relu,
    "sigmoid": sigmoid
}


"""
Credit: mholtrop
Edited by: npgh2009
"""

from matplotlib import pyplot as plt
import numpy as np
import scipy.sparse as sp
from scipy.sparse.linalg import eigs

#initialize harmonic oscillator parameters
hbar=1
m=1
omega=1

#initialize x grid
N = 200 # grid points
a = 20.0 # x limits
x = np.linspace(-a/2.,a/2.,N)
dx = x[1]-x[0] # step

#Potential
V = .5*m*omega*x*x
# V[N/2]=2/dx   # This would add a "delta" spike in the center.

# Construct hamiltonian matrix
DerivativeTerm = 1/(dx*dx)*sp.diags([1, -2, 1], [-1, 0, 1], shape=(N, N))
Hamiltonian = -(hbar*hbar)/(2.0*m)*DerivativeTerm + sp.diags(V)

En,psiT = eigs(Hamiltonian) # This computes the eigen values and eigenvectors
psi = np.transpose(psiT)
# The psi now contain the wave functions ordered so that psi[n] if the n-th eigen state.

#Check the normalization of the wave function arrays.
notok=False
for n in range(len(psi)):
    # s = np.sum(psi[n]*psi[n])
    s = np.linalg.norm(psi[n])  # This does the same as the line above.
    if np.abs(s - 1) > 0.00001: # Check if it is different from one.
        print("Wave function {} is not normalized to 1 but {}".format(n,s))
        notok=True

if not notok:
    print("All the psi_n(x) are normalized.")

fig2 = plt.figure(figsize=[10,7])
plt.title('Harmonic Oscillator')
plt.ylabel('$\psi(x)$')
plt.xlabel('$x$')
#plt.plot([0,0],[-6,V[0]],color="blue")
#plt.plot([-a/2.,a/2.],[0,0],color="blue")
plt.plot(x, 0.1*V, color="grey", label="V(x) scaled by 0.1")

plt.ylim(-1,1)
plt.xlim(-6,6)

for i in range(5):
    if psi[i][int(N/8)] < 0:
        plt.plot(x,-abs(psi[i])/np.sqrt(dx),label="$E_{}$={:3.1f}".format(i,En[i]))
    else:
        plt.plot(x,abs(psi[i])/np.sqrt(dx),label="$E_{}$={:3.1f}".format(i,En[i]))

plt.title("Solution to harmonic oscillator")
plt.legend()
plt.show()


import numpy as np

f = open('p102_text.txt')
fr = f.readline()

triangles = []
while fr:
    fs = (fr.strip()).split(',')
    triangles.append([[int(fs[0]),int(fs[1])],
                     [int(fs[2]),int(fs[3])],
                     [int(fs[4]),int(fs[5])]])
    fr = f.readline()

# By using cross products, we can test if two points lie on
# the same side of a line (if they both point in same direction,
# i.e. their dot product is greater than or equal to zero).  Using this,
# we can test to see if a given point is on the same side of the line AB
# as the third point of the triangle, C.  Permute this, and if they are all true
# then we are good to go.

def cross_product(a,b):
    return a[0]*b[1] - b[0]*a[1]

def same_side(p1,p2,a,b):
    b_minus_a = [b[i] - a[i] for i in range(len(a))]
    p1_minus_a = [p1[i] - a[i] for i in range(len(a))]
    p2_minus_a = [p2[i] - a[i] for i in range(len(a))]
    cross_prod_1 = cross_product(b_minus_a, p1_minus_a)
    cross_prod_2 = cross_product(b_minus_a, p2_minus_a)
    return cross_prod_1 / abs(cross_prod_1) == cross_prod_2 / abs(cross_prod_2)

def in_triangle(p, tri):
    res_1 = same_side(p,tri[0],tri[1],tri[2])
    res_2 = same_side(p,tri[1],tri[0],tri[2])
    res_3 = same_side(p,tri[2],tri[0],tri[1])
    if res_1 == res_2 == res_3 == True:
        return 1
    else:
        return 0

s = 0
for tri in triangles:
    s += in_triangle([0,0], tri)
print s


import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.integrate import odeint
import time as pctime

#  load data

print('Loading data')

data = pd.read_csv('https://github.com/pcm-dpc/COVID-19/raw/master/dati-andamento-nazionale/dpc-covid19-ita-andamento-nazionale.csv')
xdata=pd.to_numeric(range(data.shape[0]))
ydata=data['totale_casi']
ydata_death=data['deceduti']
ydata_rec=data['dimessi_guariti']

ydata_inf=ydata-ydata_rec-ydata_death

### definition of SIR 1.0

print('Initialization of SIR models')

# The SIR model differential equations.
def deriv_SIR(y, t, N, beta,gamma):
    S,I,R = y

    dSdt = -(beta*I/N)*S
    dIdt = (beta*S/N)*I - gamma*I
    dRdt = gamma*I

    return dSdt, dIdt, dRdt

def SIR(N,beta,gamma,I0=1,R0=0,t=np.arange(0,365)):
    # Definition of the initial conditions
    # I0 and R0 denotes the number of initial infected people (I0)
    # and the number of people that recovered and are immunized (R0)

    # t ise the timegrid

    S0=N-I0-R0  # number of people that can still contract the virus

    # Initial conditions vector
    y0 = S0, I0, R0

    # Integrate the SIR equations over the time grid, t.
    ret = odeint(deriv_SIR, y0, t, args=(N,beta,gamma))
    S, I, R = np.transpose(ret)

    return (t,S,I,R)

# ### definition of SIR 2.0

# The SIR model differential equations.
def deriv_SIR_2(y, t, N, beta1,gamma,tau=10**6,t_thresh=14):
    S,I,R = y

    if t<=t_thresh:      # il lockdown nazionale inizia al 14° giorno
        B=beta1
    else:
        B=beta1*np.exp(-(t-t_thresh)/tau)

    dSdt = -(B*I/N)*S
    dIdt = (B*S/N)*I - gamma*I
    dRdt = gamma*I

    return dSdt, dIdt, dRdt

def SIR_2(N,beta1,gamma,tau=10**6,t_thresh=14,I0=1,R0=0,t=np.arange(0,365)):
    # Definition of the initial conditions
    # I0 and R0 denotes the number of initial infected people (I0)
    # and the number of people that recovered and are immunized (R0)

    # t ise the timegrid

    S0=N-I0-R0  # number of people that can still contract the virus

    # Initial conditions vector
    y0 = S0, I0, R0

    # Integrate the SIR equations over the time grid, t.
    ret = odeint(deriv_SIR_2, y0, t, args=(N,beta1,gamma,tau,t_thresh))
    S, I, R = np.transpose(ret)

    return (t,S,I,R)

# # fitting the active infected

print('Fitting the model')

# 3 parameter fit for the active infected

def optimizer_adv(R0,tau,t_thresh):

    asympt=3

    fin_result_updated=SIR_2(6*10**7,R0*1/21,1/21,tau,t_thresh,I0=asympt*ydata_inf[0])
    i_vec=fin_result_updated[2]/asympt

    pesi=np.exp(-np.arange(len(ydata_inf))/7)    #
    pesi=pesi[::-1]

    accuracy=np.sum((ydata_inf-i_vec[0:len(ydata_inf)])**2)   #mean squared error. This is used for the optimization

    errperc=np.mean(np.abs((ydata_inf-i_vec[0:len(ydata_inf)])/ydata_inf))*100
    stdperc=np.std(np.abs((ydata_inf-i_vec[0:len(ydata_inf)])/ydata_inf))*100

    return accuracy,errperc,stdperc

# grid on 3 parameters
R0grid=np.linspace(7.5,8.5,50)
taugrid=np.linspace(25,27,200)
#ttreshgrid=np.linspace(0,10,15)

res_scan=[]
counter=0
counter2=0
cycle_tot=len(R0grid)*len(taugrid) #*len(ttreshgrid)

time1=pctime.time()

# minimization

for r in R0grid:
    for tau in taugrid:
        opt_res=optimizer_adv(r,tau,0)
        res_scan.append([r,tau,0,opt_res[0],opt_res[1],opt_res[2]])
        counter=counter+1
        counter2=counter2+1
        if counter2==cycle_tot/10:
            time2=pctime.time()
            remtime=round((time2-time1)/counter*(cycle_tot-counter),1)
            print('completed ',round(counter/cycle_tot*100,1),'%. Remaining time ',remtime,'sec')
            counter2=0

res_scan=np.array(res_scan)

df_res_scan=pd.DataFrame(res_scan)
df_res_scan.columns=['R0','tau','t_thresh','MSE','mean err%','std err%']

print(df_res_scan.sort_values(by='MSE').head())

par_ideal=np.array(df_res_scan.sort_values(by='MSE').iloc[0,0:])

fin_result_updated=SIR_2(6*10**7,par_ideal[0]*1/21,1/21,par_ideal[1],t_thresh=par_ideal[2],I0=3*ydata_inf[0])

t=fin_result_updated[0]
s_vec=fin_result_updated[1]
i_vec=fin_result_updated[2]
r_vec=fin_result_updated[3]

plt.figure(figsize=(7,5))
plt.plot(np.arange(len(ydata_inf)),ydata_inf,color='red',label='Active infected (data)')
plt.plot(t, i_vec/3, label='Active infected (model, best-fit)',color='blue',linestyle='--')
#plt.fill_between(t, i_vec*(1+par_ideal[5]/100*2.65)/3,i_vec*(1-par_ideal[5]/100*2.65)/3, label='Active infected (model, 99% of data)',color='blue',alpha=.2)
plt.plot(np.zeros(2)+70,[-1000,250000],color='purple')
plt.xticks(np.arange(0,100,7),['24 Feb','2 Mar','9 Mar','16 Mar','23 Mar','30 Mar','6 Apr','13 Apr','20 Apr','27 Apr','4 Mag','11 Mag','18 Mag','25 Mag','1 Giu'],rotation=30)
plt.xlim(0,100)
plt.ylim(100,2*10**5)
#plt.yscale('log')
plt.annotate("Lockdown", xy=(14,10000), xytext=(3,40000), arrowprops=dict(arrowstyle="->"),fontsize=13)
plt.xlabel('Data')
plt.ylabel('Counter')
plt.yscale('log')
plt.legend()
plt.grid(color='gray', linestyle='--', linewidth=0.5)
plt.tight_layout()
plt.savefig('output/fit_con_incertezza.png',dpi=300)
#plt.show()

print('Figure model-data saved')

#export

t_date=pd.to_datetime(t,unit='D',origin=pd.Timestamp('2020-02-24'))

export_v2 = pd.DataFrame({'date':t_date,'t':t,'s':s_vec,'i':i_vec,'r':r_vec})
export_v2.to_csv('output/nazionale.csv',index=False)

# # computation of national R0

# ### using data of last 5 days

today=len(xdata)

print('Computing R0 as function of time')

def minimizer(R0,t1=today-5,t2=today):

    #true data
    ydata_inf_2=np.array(ydata_inf[t1:t2])
    xdata_2=np.arange(0,len(ydata_inf_2))

    #print(ydata_inf_2)

    #model
    fin_result=SIR(60*10**6,1/14*R0,1/14,I0=ydata_inf_2[0])
    i_vec=fin_result[2]
    i_vec_2=i_vec[0:len(xdata_2)]

    #average error
    error=np.sum(np.abs(ydata_inf_2-i_vec_2)/ydata_inf_2)*100

    return error

minimizer_vec=np.vectorize(minimizer)

# ### scan of R0 over time

time_window=5

def minimizer_gen(t1,t2,xgrid=np.arange(0.1,5,0.01)):

    ygrid=minimizer_vec(xgrid,t1=t1,t2=t2)
    r0_ideal=round(xgrid[np.argmin(ygrid)],2)

    return r0_ideal

r0_time=[]
scangrid=np.linspace(0,5,100)

for i in range(0,today-(time_window-1)):
    min_val=minimizer_gen(i,i+time_window,xgrid=scangrid)
    r0_time.append(min_val)
    scangrid=np.linspace(0,min_val*2,100)  #the grid change over the time (this speed up the process)
    print('Day n',i,' R0=',min_val)
    print(i,i+time_window,len(ydata_inf))

r0_time=np.array(r0_time)

plt.figure(figsize=(5,5))
plt.plot(r0_time)
plt.xticks(np.arange(0-(time_window-5),80,7),['28 Feb','6 Mar','13 Mar','20 Mar','27 Mar','3 Apr','10 Apr','17 Apr','24 Apr','1 Mag','8 Mag','15 Mag'],rotation=20)
plt.xlim(0,today-4)
#plt.xlim(19,today-9)
plt.ylabel('$R_0$')
plt.ylim(0,5)
plt.grid()
plt.title('Evolution of $R_0$')
plt.savefig('output/r0-evo.png',dpi=300)
#plt.show()

print('Figure R0 saved')

# export R0 over time

df_r0=pd.DataFrame(pd.to_datetime(np.arange(len(r0_time)),unit='D',origin='2020-02-28'))
df_r0['R0']=r0_time
df_r0.columns=['Data','R0']

df_r0.to_csv('output/r0.csv',index=False)

# # Future forecasting

print('Future predictions as function of R0')

# The SIR model differential equations.
def deriv_SIR_2_future(y, t, N, beta1,gamma,newR0,tau=10**6,t_thresh=14):
    S,I,R = y

    if t<=t_thresh:      # il lockdown nazionale inizia al 14° giorno
        B=beta1
    elif t>t_thresh and t<=70:
        B=beta1*np.exp(-(t-t_thresh)/tau)
    elif t>70:
        B=newR0*gamma
        #B=beta1*np.exp(-(70-t_thresh)/tau)+0.05*gamma*(t-70)
        #print(B,t)

    dSdt = -(B*I/N)*S
    dIdt = (B*S/N)*I - gamma*I
    dRdt = gamma*I

    return dSdt, dIdt, dRdt

def SIR_2_future(N,beta1,gamma,newR0,tau=10**6,t_thresh=14,I0=1,R0=0,t=np.arange(0,365)):
    # Definition of the initial conditions
    # I0 and R0 denotes the number of initial infected people (I0)
    # and the number of people that recovered and are immunized (R0)

    # t ise the timegrid

    S0=N-I0-R0  # number of people that can still contract the virus

    # Initial conditions vector
    y0 = S0, I0, R0

    # Integrate the SIR equations over the time grid, t.
    ret = odeint(deriv_SIR_2_future, y0, t, args=(N,beta1,gamma,newR0,tau,t_thresh))
    S, I, R = np.transpose(ret)

    return (t,S,I,R)

asympt=3

i_vec_future050=SIR_2_future(6*10**7,par_ideal[0]*1/21,1/21,0.5,par_ideal[1],t_thresh=par_ideal[2],I0=asympt*ydata_inf[0])[2]
i_vec_future100=SIR_2_future(6*10**7,par_ideal[0]*1/21,1/21,1.0,par_ideal[1],t_thresh=par_ideal[2],I0=asympt*ydata_inf[0])[2]
i_vec_future110=SIR_2_future(6*10**7,par_ideal[0]*1/21,1/21,1.1,par_ideal[1],t_thresh=par_ideal[2],I0=asympt*ydata_inf[0])[2]
i_vec_future120=SIR_2_future(6*10**7,par_ideal[0]*1/21,1/21,1.20,par_ideal[1],t_thresh=par_ideal[2],I0=asympt*ydata_inf[0])[2]

plt.figure(figsize=(7,5))
plt.plot(np.arange(len(ydata_inf)),ydata_inf,color='red',linestyle='None',marker='.',label='Infetti sintomatici attivi (dati)')
plt.fill_between(t, i_vec_future100/asympt,i_vec_future050/asympt,color='green',label='Modello, $R_0$ fra 0.5 e 1',alpha=0.5)
plt.fill_between(t, i_vec_future110/asympt,i_vec_future100/asympt,color='orange',label='Modello, $R_0$ fra 1 e 1.1',alpha=0.5)
plt.fill_between(t, i_vec_future120/asympt,i_vec_future110/asympt,color='red',label='Modello, $R_0$ fra 1.1 e 1.2',alpha=0.5)
#plt.plot(t, i_vec_future05/asympt, label='Modello, R$_0^{riapertura}$=0.5')
#plt.plot(t, i_vec_future10/asympt, label='Modello, R$_0^{riapertura}$=1')
#plt.plot(t, i_vec_future15/asympt, label='Modello, R$_0^{riapertura}$=1.1')
#plt.plot(t, i_vec_future20/asympt, label='Modello, R$_0^{riapertura}$=1.2')
#plt.fill_between(t, i_vec_future*(1+par_ideal[4]/100*2)/asympt,i_vec_future*(1-par_ideal[4]/100*2)/asympt, label='Infetti attivi (modello, 95% C.L.)',color='red',alpha=.2)
plt.plot(np.zeros(2)+70,[-1000,10**7],color='purple')
plt.xticks(np.arange(6,360,30),['1 Mar','1 Apr','1 Mag','1 Giu','1 Lug','1 Ago','1 Set','1 Ott','1 Nov','1 Dic','1 Gen','1 Feb'],rotation=30)
plt.xlim(0,360)
plt.ylim(100,ydata_inf.max()*4.3)
#plt.ylim(100,10**7)
#plt.yscale('log')
plt.annotate("Lockdown", xy=(14,ydata_inf.max()*0.1), xytext=(3,ydata_inf.max()*2), arrowprops=dict(arrowstyle="->"),fontsize=13)
plt.annotate("Riapertura", xy=(70,ydata_inf.max()), xytext=(80,ydata_inf.max()*2), arrowprops=dict(arrowstyle="->"),fontsize=13)
plt.xlabel('Data')
plt.ylabel('Counter')
plt.legend()
plt.grid(color='gray', linestyle='--', linewidth=0.5)
plt.title('2 asintomatici per ogni sintomatico')
plt.tight_layout()
plt.savefig('output/predizioni_future.png',dpi=300)
#plt.show()

print('Figure prediction saved')

#export

ydata_inf_toexport=np.empty(len(i_vec))
ydata_inf_toexport[0:len(ydata_inf)]=ydata_inf
ydata_inf_toexport[len(ydata_inf):]=np.nan

t_date=pd.to_datetime(t,unit='D',origin=pd.Timestamp('2020-02-24'))

export_forecast = pd.DataFrame({'date':t_date,'t':t,'R0=0.5':i_vec_future050/asympt,'R0=1':i_vec_future100/asympt,'R0=1.1':i_vec_future110/asympt,'R0=1.2':i_vec_future120/asympt,'infetti_reali':ydata_inf_toexport})
export_forecast.to_csv('output/predizioni_future_export.csv',index=False)


import numpy as np
from math import sqrt
from data import generate_data_line, plot_generated_data
import matplotlib.pyplot as plt

def plot_data(x, y, LR, name):
    plt.scatter(x, y)
    # plt = abline(LR.m, LR.c, plt)
    # axes = plt.gca()
    x_vals = np.array([x.min(), x.max()])
    y_vals = LR.m * x_vals + LR.c
    plt.plot(x_vals, y_vals, "--")
    plt.savefig("images/" + name)
    plt.clf()
    return

class LinearReg:
    """ A bi-variable linear regressor class """

    def __init__(self, m=np.random.randint(0, 5), c=np.random.randint(0, 5), lr=0.1):
        """initialize the class with:
        slope(m), intercept(c) and learning rate(lr)"""
        self.m = m
        self.c = c
        self.lr = lr
        # print(f"Initialized with LR.m: {self.m:.4f}, LR.c: {self.c:.4f}, lr: {self.lr:.4f}")

    def mean_squared_error(self, y, pred_y):
        """Calculate the MSE for vectors

        Args:
            y (np.array): actual y values
            pred_y (np.array): predicted y values

        Returns:
            float: the MSE from y and pred_y
        """
        return sqrt(np.mean((y - pred_y) ** 2))

    def update_parameters(self, err, x):
        N = x.shape[0]
        dm = (2.0 / N) * (-np.sum(np.multiply(x, (err))))
        dc = (2.0 / N) * (-np.sum(err))
        # print(dm, dc)
        self.m = self.m - (self.lr * dm)
        self.c = self.c - (self.lr * dc)
        return

    def train(self, x, y, epochs=501):
        cost_history = []
        for epoch in range(epochs):
            pred_y = self.m * x + self.c
            err = y - pred_y
            self.update_parameters(err, x)

            cost = self.mean_squared_error(y, pred_y)
            cost_history.append(cost)

            if epoch % 100 == 0:
                print(f"LR.m: {self.m:.4f}, LR.c: {self.c:.4f}, cost: {cost:.4f}")
                name = str(epoch) + "_epoch.png"
                plot_data(x, y, self, name)

        return cost_history

if __name__ == "__main__":
    data = generate_data_line(num=10)
    x, y = np.array(list(zip(*data)))
    plot_generated_data(x, y)

    LR = LinearReg(m=np.mean(x), c=np.mean(y))
    cost_history = LR.train(x, y)

    print(LR.m)
    print(LR.c)

    plt.plot(cost_history)
    plt.savefig("images/cost_history.png")


import numpy as np
import matplotlib.pyplot as plt
#Heat equation
A1 = np.array([[4,-1,0,-1,0,0,0,0,0],
 [-1,4,-1,0,-1,0,0,0,0],
 [0,-1,4,0,0,-1,0,0,0],
 [-1,0,0,4,-1,0,-1,0,0],
 [0,-1,0,-1,4,-1,0,-1,0],
 [0,0,-1,0,-1,4,0,0,-1],
 [0,0,0,-1,0,0,4,-1,0],
 [0,0,0,0,-1,0,-1,4,-1],
 [0,0,0,0,0,-1,0,-1,4]])
b= np.array([0,0,100,0,0,100,200,200,300])
T = np.matmul(np.linalg.inv(A1),b)
print("Temprature at given points:",T)
Z = np.array([[0,0,0,0,50],
 [0,T[0],T[1],T[2],100],
 [0,T[3],T[4],T[5],100],
 [0,T[6],T[7],T[8],100],
 [100,200,200,200,150]])
plt.imshow(Z)
plt.colorbar(label="Temprature")
plt.title('Heat Map of temperatures at defined points')
plt.show()
#1-D wave equation
A=np.linalg.inv(np.array([[4,-1,0,0],
 [-1,4,-1,0],
 [0,-1,4,-1],
 [0,0,-1,4]]))
Tj0= np.array([np.sin(0.4*np.pi),
 np.sin(0.2*np.pi)+np.sin(0.6*np.pi),
np.sin(0.4*np.pi)+np.sin(0.8*np.pi),
np.sin(0.6*np.pi)])
Tu1=np.matmul(A,Tj0)
u11,u21,u31,u41=Tu1
print("\n"+"Tempratures at t= 0.04:",Tu1)
Tj1=np.array([u21,u31+u11,u41+u21,u31])
u12,u22,u32,u42=np.matmul(A,Tj1)
Tu2=np.array([u12,u22,u32,u42])
print("Tempratures at t= 0.08:",Tu2)
Tj2=np.array([u22,u12+u32,u22+u42,u32])
u13,u23,u33,u43=np.matmul(A,Tj2)
Tu3=np.array([u13,u23,u33,u43])
print("Tempratures at t= 0.12:",Tu3)
Tj3=np.array([u23,u13+u33,u23+u43,u33])
u14,u24,u34,u44=np.matmul(A,Tj3)
Tu4=np.array([u14,u24,u34,u44])

print("Tempratures at t= 0.16:",Tu4)
Tj4=np.array([u24,u14+u34,u24+u44,u34])
u15,u25,u35,u45=np.matmul(A,Tj4)
Tu5=np.array([u15,u25,u35,u45])
print("Tempratures at t= 0.20:",Tu5)
#The border points are =0 meaning that
#appending them on will give the correct
#result when plotted
u1=np.append([0],np.append(Tu1,[0]))
u2=np.append([0],np.append(Tu2,[0]))
u3=np.append([0],np.append(Tu3,[0]))
u4=np.append([0],np.append(Tu4,[0]))
u5=np.append([0],np.append(Tu5,[0]))
x=np.arange(0,1.2,0.2)
plt.plot(x,u1,label='t=0.04',c='k')
plt.scatter(x,u1,c='k')
plt.plot(x,u2,label='t=0.08',c='b')
plt.scatter(x,u2,c='b')
plt.plot(x,u3,label='t=0.12',c='g')
plt.scatter(x,u3,c='g')
plt.plot(x,u4,label='t=0.16',c='y')
plt.scatter(x,u4,c='y')
plt.plot(x,u5,label='t=0.20',c='c')
plt.scatter(x,u5,c='c')
plt.title("Temperature against distance with varying times")
plt.ylabel("Temprature")
plt.xlabel("Distance")
plt.legend(loc=0)
plt.show()


"""
Summarize primes
http://www.rosettacode.org/wiki/Summarize_primes
"""

import itertools
import sympy

def get_subseq(n: int):
    # prime list [2, n)
    primes = list(sympy.primerange(n))
    # cummulative sum list of primes
    cum_primes = list(itertools.accumulate(primes))
    # Internally primes exntends to [2, sum of primes in range [2, n)]
    # to see if sum of items in primes is also prime
    primes = primes + list(sympy.primerange(n, sum(primes)))

    subseqs = []
    for idx, val in enumerate(cum_primes):
        if val in primes:  # if cumulative sum is also prime
            # (length of subseq, last prime num in subseq, sum of subseq)
            subseq = (idx + 1, primes[idx], val)
            subseqs.append(subseq)

    return subseqs

def main():
    rst = get_subseq(1000)
    print("prime   last  prime")
    print("count  prime    sum")
    for item in rst:
        sb = f"{item[0]:5d}  {item[1]:5d}  {item[2]:5d}"
        print(sb)

if __name__ == "__main__":
    main()


import numpy as np

def accuracy(ytrue: np.ndarray, ypred: np.ndarray):
    return np.sum(ytrue == ypred) / ytrue.size

def error_rate(ytrue: np.ndarray, ypred: np.ndarray):
    return np.sum(ytrue != ypred) / ytrue.size

def mean_squared_error(ytrue: np.ndarray, ypred: np.ndarray):
    return np.sum((ytrue - ypred) ** 2) / ytrue.size

def rooted_mean_squared_error(ytrue: np.ndarray, ypred: np.ndarray):
    return np.sqrt(np.sum((ytrue - ypred) ** 2) / ytrue.size)

def f1_score(ytrue: np.ndarray, ypred: np.ndarray):
    tp = np.sum(np.logical_and(ytrue == 1, ypred == 1))
    recall = tp / np.sum(ytrue == 1)
    prec = tp / np.sum(ypred == 1)
    return 2 * prec * recall / (prec + recall)


"""Transformation Functions"""

# Authors: Jeffrey Wang
# License: BSD 3 clause

import numpy as np
from scipy import stats

def log_transform(data, base=np.e, axis=0):
	"""
	Transform the data into a logarithm.

	Parameters
	----------
	data : ndarray
		Data to be log transformed.

	base : {scalar, 'mean', 'median'}, default=np.e
		Log base for normalization. Must be one of:
		 - scalar : Constant base.
		 - 'mean' : Set the base to the mean of the data.
		 - 'median' : Set the base to the median of the data.

	axis : int, default=0
		Axis to transform along.

	Returns
	-------
	transform : ndarray
		Log transformed data.
	"""
	if base == 'mean':
		base = np.mean(data, axis=axis)
	elif base == 'median':
		base = np.median(data, axis=axis)
	return np.log(data) / np.log(base)

def boxcox_transform(data, lmbda=None):
	"""
	Transform the data by a Box-Cox power transformation.

	Parameters
	----------
	data : ndarray
		Data to be transformed.

	lmbda : scalar, default=np.e
		If lmbda is not None, do the transformation for that value.
		If lmbda is None, find the lambda that maximizes
		the log-likelihood function and return it as the
		second output argument.

	Returns
	-------
	transform : ndarray
		Transformed data.

	maxlog : float, optional
		If the lmbda parameter is None, maxlog is
		the lambda that maximizes the log-likelihood function.
	"""
	return stats.boxcox(data, lmbda=lmbda)

def power_transform(data, a):
	"""
	Transform the data into by a power.

	Parameters
	----------
	data : ndarray
		Data to be power transformed.

	a : scalar
		Exponent to use for transformation.

	Returns
	-------
	transform : ndarray
		Power transformed data.
	"""
	return np.sign(data) * np.power(np.absolute(data), a)


from numpy import array # pip install numpy
from pprint import pprint # pip install pprint

#################
#   Functions   #
#################
def mod3(arg):
    return arg % 3

def weight(word):
    count = 0
    for i in word:
        if i != 0: count += 1
    return count

def d(word1, word2):
    if len(word1) != len(word2): return 0
    return weight([word1[i] - word2[i] for i in range(len(word1))])

def code(g):
    return array(
        [
            mod3(vec) for vec in [
                g[0] + g[0] + g[0],
                g[0],
                g[1],
                g[0] + g[0],
                g[1] + g[1],
                g[0] + g[1],
                g[0] + g[0] + g[1],
                g[0] + g[1] + g[1],
                g[0] + g[0] + g[1] + g[1],
            ]
        ]
    )

def dot(v1, v2):
    prod = 0
    if len(v1) == len(v2):
        for i in range(len(v1)):
            prod += v1[i] * v2[i]
    return prod

def matMul(m1, m2):
    return array([[dot(m, n) for m in m2] for n in m1])

def test(testCodeWord, ParityCheckMatrix, silent = False):
    syndrome = [dot(v, testCodeWord) for v in ParityCheckMatrix]
    if not silent:
        pprint('Test code:')
        pprint(testCodeWord)

        pprint('Syndrome:')
        pprint(mod3(array(syndrome)))

    return mod3(array(syndrome))

def minD(code):
    weightList = []
    ncode = [mod3(word1 - word2) for word1 in code for word2 in code]
    for word in ncode:
        w = 0
        for i in word:
            if i != 0: w += 1

        if w != 0: weightList.append(w)

    return min(weightList)

#################
#   Exercise 1. #
#################
pprint('------- Exercise 1. -------')

n = 4
k = 2

# Basis vectors
x = array([1, 0, 1, 2])
y = array([0, 1, 1, 1])

# Generator matrix
G = array([ x, y ])

# The generated code
C = code(G)

pprint('The code C:')
pprint(C.tolist())

# 'The code C:'
# [[0, 0, 0, 0],
#  [1, 0, 1, 2],
#  [0, 1, 1, 1],
#  [2, 0, 2, 1],
#  [0, 2, 2, 2],
#  [1, 1, 2, 0],
#  [2, 1, 0, 2],
#  [1, 2, 0, 1]]

# Min Hamming distance
d = minD(C)

pprint('Min Hamming distance:')
pprint(d)

# 'Min Hamming distance:'
# 3

# Parity check matrix
h1 = mod3(array([-x[2], -y[2], 1, 0]))
h2 = mod3(array([-x[3], -y[3], 0, 1]))
H = array([ h1, h2 ])

pprint('Parity check matrix H:')
pprint(H)

# Test H
test([0, 1, 0, 1], H) # syndrome == [2, 0]

for v in C:
    test(v, H) # syndrome == [0, 0]

# Find if MDS
if d == n - k + 1: pprint('MDS') # This will be printed
else: pprint('Not MDS')

#################
#   Exercise 2. #
#################
pprint('------- Exercise 2. -------')

zeroVec = [0, 0, 0, 0]

BallWithDistanceOfOnefromZeroVector = [
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1],
    [2, 0, 0, 0],
    [0, 2, 0, 0],
    [0, 0, 2, 0],
    [0, 0, 0, 2],
]

for word in BallWithDistanceOfOnefromZeroVector:
    test(word, H)

incomingMessage = [2, 0, 0, 1]

def findCoset(syndrome):
    for word in BallWithDistanceOfOnefromZeroVector:
        cosetHeaderSyndrome = test(word, H, True)

        isCoset = True;
        for index, value in enumerate(cosetHeaderSyndrome):
            if value != syndrome[index]: isCoset = False

        if isCoset:
            pprint('Coset:')
            pprint(word)
            return word

incomingSyndrome = test(incomingMessage, H, True) # [1, 0]

coset = findCoset(incomingSyndrome)

correctWord = mod3(array(incomingMessage) - array(coset)).tolist()
pprint('Correct word:')
pprint(correctWord)

#################
#   Exercise 3. #
#################
pprint('------- Exercise 3. -------')

h1 = array([1, 1, 1, 0])
h2 = array([2, 0, 1, 1])

H = array([ h1, h2 ])

C = code(H)

N = array(
    [
        [0],
        [1],
        [2],
        [1],
    ]
).transpose()

pprint(mod3(matMul(H,N)))


# A python program that can generate the formula of a given sequence.
# Dependencies: SymPy

import sympy
from sympy.parsing.sympy_parser import parse_expr
import itertools as it
import math

class Sequence(object):
    '''A sequence object'''

    def __init__(self, sequence='1, 3, 5, 7, 217341'):
        try:
            self._seq = [float(i) for i in sequence.replace(' ','').split(',')]
        except ValueError:
            raise ValueError('invalid numbers')
        self._seqLen = len(self._seq)

    def _isArithmetic(self):
        '''When A_n = pn + q'''
        if len(self._seq) <= 2:
            if len(self._seq) == 1:
                return False, None
            elif len(self._seq) == 2:
                return True, self._seq[1] - self._seq[0]
        d = self._seq[1] - self._seq[0]
        if all([self._seq[i+1]-self._seq[i]==d for i in range(1, len(self._seq)-1)]):
            return True, d
        return False, None

    def _isGeometric(self):
        '''When A_n = p^(n-1) * q, or in special cases, A_n = p^(n+q)'''
        if len(self._seq) <= 2:
            if len(self._seq) == 1:
                return False, None
            elif len(self._seq) == 2:
                return True, self._seq[1] / self._seq[0]
        r = self._seq[1] / self._seq[0]
        if all([self._seq[i+1]/self._seq[i]==r for i in range(1, len(self._seq)-1)]):
            return True, r
        return False, None

    def generateFormula(self):
        '''Generates a formula for the sequence, returns a sympy object.\nMay not work for complex sequences.'''
        arithmetic, d = self._isArithmetic()
        if arithmetic:
            self.formula = sympy.nsimplify(parse_expr('{}*n + {}'.format(d, self._seq[0]-d)))
            return self.formula
        geometric, r = self._isGeometric()
        if geometric:
            log = math.log(self._seq[0], r)
            if log.is_integer():
                self.formula = sympy.nsimplify(parse_expr('{}**(n-1+{})'.format(r, log)))
            else:
                self.formula = sympy.nsimplify(parse_expr('{}**(n-1) * {}'.format(r, self._seq[0])))
            return self.formula
        else:
            tmp = []
            combinations = [i for i in it.combinations([l+1 for l in range(self._seqLen)], self._seqLen-1)]
            for combination, leftOut in zip(combinations, reversed([l+1 for l in range(self._seqLen)])):
                numerator = sympy.expand(parse_expr('*'.join(['(n-{})'.format(element) for element in combination])))
                denominator = parse_expr('*'.join(['({}-{})'.format(leftOut,element) for element in combination]))
                tmp.append(numerator / denominator)
            fractions = []
            for sequenceItem, element in zip(reversed(self._seq), tmp):
                fractions.append(sympy.nsimplify(sequenceItem) * element)
            self.formula = sympy.expand(sum(fractions))
            return self.formula

#Example Usage:
seq = Sequence(input('Input the sequence:'))
print('Formula:', end=' ')
print(seq.generateFormula())


import numpy as np
from chol_factor import chol_factor
from triangular import triangular

# TEST: Cholesky factorization (LL')

# Symmetric positive definite matrix
A = np.matrix('5    1.2  0.3 -0.6;'
              '1.2  6   -0.4  0.9;'
              '0.3 -0.4  8    1.7;'
             '-0.6  0.9  1.7  10');
print('A = \n', A)
# Computation of the L factor
L = chol_factor(A)
print('L = \n', L)
# Check
if np.allclose(A, np.dot(L, L.transpose())) == False:
    raise Exception('QR factorizzation test failure')

# TEST: System Resolution

# Ax = LL'x = b
b = np.matrix("68; 9; 45; 35")
print('b = \n', b)
# Lk = b
k = triangular(L, b, 1)
print('k = \n', k)
# L'x = k
x = triangular(L.transpose(), k, 0)
print('x = \n', x)
# Check
b1 = np.dot(A, x)
print('b1 = \n', b1)
if np.allclose(b, b1) == False:
    raise Exception('System resolution failure')


import math
import warnings

import numpy as np
import torch

from .geometry import cartesian_to_spherical

def rand_sphere(size):
    """
    Randomly sample on the sphere uniformly
    See http://corysimon.github.io/articles/uniformdistn-on-sphere/
    Our convention is 0 < theta < pi ; -pi < phi < pi
    :param size: torch.size or list of ints, size of returned tensors
    :return: theta, phi
    """
    theta = torch.acos(1 - 2 * torch.rand(size))
    phi = 2 * math.pi * torch.rand(size) - math.pi
    return theta, phi

def fibonacci_sphere(samples: int = 1000):
    """
    Evenly sample on the sphere
    See https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere
    :param samples: size of the array sampled
    :return : [samples, 3], torch.float, X, Y, and Z coordinates of the points in Euclidean space
    """
    increment = np.linspace(0, samples - 1, samples)
    phi = np.pi * (3. - np.sqrt(5.))  # golden angle in radians

    y = 1 - (increment / float(samples - 1)) * 2  # y goes from 1 to -1
    radius = np.sqrt(1 - y * y)  # radius at y

    theta = phi * increment  # golden angle increment

    x = np.cos(theta) * radius
    z = np.sin(theta) * radius

    return np.stack([x, y, z], axis=1)

def rejection_sphere_sampling(pdf, n: int = 1000, device=None, iter_max: int = 100):
    """
    Sample from a probability density function using rejection sampling
    See https://en.wikipedia.org/wiki/Rejection_sampling
    :param pdf: a function that takes as input a 1D vector theta and a 1D vector phi and return their densities
    :param n: number of samples
    :param device: torch.device
    :param iter_max: This algorithm can fail when the density function has big spikes,
        iter_max is the maximal number of iterations allowed
    :return: torch.float, [n, 2], theta and phi arrays
    """
    warnings.warn('rejection_sphere_sampling is a very basic loop, and can be very slow when the pdf function is' +
                  ' slow or has big spikes')
    if device is None:
        device = 'cpu'

    # Approximate upper bound value of the pdf
    xyz = torch.tensor(fibonacci_sphere(samples=5000), device=device)
    _, theta, phi = cartesian_to_spherical(xyz[:, 0], xyz[:, 1], xyz[:, 2])
    p_max = pdf(theta=theta, phi=phi).max()

    sample = torch.empty([0, 2], dtype=torch.float, device=device)
    n_iters = 0
    while sample.shape[0] < n:
        if n_iters > iter_max:
            raise RuntimeError('rejection_sphere_sampling did not succeed in time. Try to increase iter_max')
        # Rejection sampling
        y = torch.rand([2, n], device=device)  # Uniform distribution to create initial distribution
        theta = torch.acos(1 - 2 * y[0])
        phi = 2 * math.pi * y[1] - math.pi
        u = torch.rand([n], device=device)  # Uniform distribution to select valid and invalid samples

        valid = u < pdf(theta=theta, phi=phi) / p_max
        sample = torch.cat([sample,
                            torch.stack([theta[valid], phi[valid]], dim=1)],
                           dim=0)
        n_iters += 1

    sample = sample[:n]
    return sample


import sympy as sp

x = sp.symbols("x")

y_exact = sp.exp(-x)

x1, x2 = sp.symbols("x1 x2")

# x1 = 0
# x2 = 1

y1 = sp.exp(-x1)
y2 = sp.exp(-x2)

y_approx = (y2 - y1)/(x2 - x1) * (x - x1) + y1
y_approx = sp.simplify(y_approx)

difference = y_approx - y_exact
difference = sp.simplify(difference)

L1error = sp.integrate(difference,(x,x1,x2))
L1error = sp.simplify(L1error)
# L2error = sp.integrate(difference**2,[x,x1,x2])


import math
import numpy as np
from numpy import *
from PIL import Image
from PIL import ImageDraw

# calculate derivative of Image in x and y directions
def derivative(arr):
    Ix = np.zeros((arr.shape[0], arr.shape[1]))
    Iy = np.zeros((arr.shape[0], arr.shape[1]))
    derivativeMask = np.array([-1,0,1])
    for i in range(1, arr.shape[0]-1):
        for j in range(1, arr.shape[1]-1):
            xtemp1 = arr[i, j - 1 : j + 2]
            xtemp2 = np.multiply(xtemp1, derivativeMask)
            ytemp1 = arr[i - 1 : i + 2, j]
            ytemp2 = np.multiply(ytemp1, derivativeMask.T)
            Ix[i, j] = xtemp2.sum()
            Iy[i, j] = ytemp2.sum()
    return Ix, Iy

# calculate eigen-value of the given matrix H1 and use it to find corners
def hessianCornerDetection(image):
    arr = np.array(image)
    lambda1 = np.zeros((arr.shape[0],arr.shape[1]))
    lambda2 = np.zeros((arr.shape[0],arr.shape[1]))
    Ix , Iy = derivative(arr)
    Ixx , Ixy = derivative(Ix)
    Iyx , Iyy = derivative(Iy)
    markArr = np.zeros((arr.shape[0],arr.shape[1]))
    count = 0
    for i in range(arr.shape[0]):
        for j in range(arr.shape[1]):
            temp = np.array( [ [ Ixx[i, j] , Iyx[i, j] ], [ Ixy[i, j], Iyy[i, j] ] ] )
            eigValue , eigVector = np.linalg.eig(temp)
            lambda1[i][j] = eigValue[0]
            lambda2[i][j] = eigValue[1]
            if lambda1[i,j] > 120 and lambda2[i,j] > 120:
                markArr[i,j] = 1
    return markArr

#  create a function to draw the corners which is detected by former algorithms
def drawCircleOnImg(Img, markArr):
    Img = Img.convert('RGB')
    draw = ImageDraw.Draw(Img)
    count = 0
    for i in range(markArr.shape[0]):
        for j in range(markArr.shape[1]):
            if markArr[i, j] > 0:
                draw.ellipse( (j - 2, i - 2, j + 2, i + 2), outline ='orange')
                count = count + 1
    print( "total circle drawing : " + str(count) )
    Img.show()

# main function to implement other functions
def main(imgPath):
    Img = Image.open(imgPath).convert('L')
    mark = hessianCornerDetection(Img)
    drawCircleOnImg(Img,mark)

main('input1.png')


import numpy as np
def trap(h, y1, y2):
	""" h is defined as (b-a) in which a and b are the limits of integration """
	return h * (y1+y2)/2

def trapm(h, y):
	""" h = (b-a)/(len(y)-1) where b and a are the limits of integration
		y  is an numpy compatability array
	"""
	n = len(y)-1
	sum = y[0] + y[-1]
	sum += np.sum(y[1:-1]) * 2
	return h * (sum/2)

def simp13(h, y1, y2, y3):
	""" h is defined as (b-a) in which a and b are the limits of integration """
	return (h) * (y1 + 4*y2 + y3)/6

def simp13m(h, y):
	""" h is defined as (b-a)/(len(y)-1) in which a and b are the limits of integration """
	sum = y[0] + y[-1]
	sum += np.sum(y[1:-1:2]) * 4
	sum += np.sum(y[2:-1:2]) * 2
	return sum * h / 3

def simp38(h, y1, y2, y3, y4):
	""" h is defined as (b-a) in which a and b are the limits of integration """
	return h * (y1 + 3*y2 + 3*y3 + y4)/8

print(trap(0.8, 0.2, 0.232))
#Data was taken as f(0), f(0.4), f(0.8); a = 0, b = 0.8, n = 2 or (3-1)
y = [0.2, 2.456, 0.232]
print(trapm(0.4, y))
print(simp13(0.8, *y))
#Data was taken as f(0), f(0.4), f(0.8); a = 0, b = 0.8, n = 2 or (3-1)
y2 = [0.2, 1.288, 2.456, 3.464, 0.232]
print(simp13m(0.8/4, y2))
y3 = [0.2, 1.432724, 3.487177, 0.232]
print(simp38(0.8, *y3))


import math
import matplotlib.patches
import matplotlib.pyplot
import numpy.random

from matplotlib.path import Path

def calculate_angle(source, point):
    x0, y0 = source
    x1, y1 = point
    dx = x1 - x0
    dy = y1 - y0
    angle = math.atan2(dy, dx)
    return (angle, point)

def calculate_angles(source, points):
    return [calculate_angle(source, point) for point in points]

def normalize_angle(theta, delta):
    if theta > delta:
        return theta - delta
    else:
        return theta - delta + math.pi * 2

def convexhull(points):
    """using the gift wrapping method"""
    start = max(points, key = lambda t : (t[1], t[0]))
    hull = [start] # start with the leftmost point
    while True:
        last = hull[-1]
        angles = calculate_angles(last, points)
        output = [angle for angle in angles if angle[1] not in hull] # list of (angle, points)
        if len(hull) == 1:
            next_point = min(output, key=lambda x: x[0])[1]
        else:
            second_last = hull[-2]
            output += [calculate_angle(last, hull[0])] # first hull point is always a candidate.
            delta = calculate_angle(second_last, last)[0]
            next_point = min(output, key=lambda x: normalize_angle(x[0], delta))[1]
        hull.append(next_point)
        if hull[-1] == hull[0]: break # terminal condition
    return hull

def generate_code(n):
    """generate path command codes for matplotlib for a convex hull"""
    result = []
    def index_to_code(i):
        if i == 0: return Path.MOVETO
        elif i == n - 1: return Path.CLOSEPOLY
        else: return Path.LINETO
    return map(index_to_code, xrange(n))

count = 0
fig = matplotlib.pyplot.figure()

def execute(points):
    global count
    global fig
    count += 1
    hull = convexhull(points)
    codes = generate_code(len(hull))
    path = Path(hull, codes)
    ax = fig.add_subplot(3, 2, count)
    limit = 10
    patch = matplotlib.patches.PathPatch(path, facecolor='orange', alpha = 0.1, lw=2)
    # plot the points
    x = [p[0] for p in points]
    y = [p[1] for p in points]
    ax.scatter(x, y, s = 10)
    # plot the convex hull
    ax.add_patch(patch)
    ax.set_xlim(-limit, limit)
    ax.set_ylim(-limit, limit)

def test_1():
    execute([(1, 0), (-1, 0), (0, 1), (0, -1), (0, 0)])

def test_2():
    execute([(1, 0), (-1, 0), (0, 1), (0, -1), (0, 0), (0, -0.25)])

def test_3():
    result = []
    for i in range(-2, 3):
        for j in range(-2, 3):
            # add a bit of variation
            result.append((i + numpy.random.normal(scale=0.01), j + numpy.random.normal(scale=0.01)))
    execute(result)

def test_random_1():
    result = []
    for i in range(100):
        result.append((numpy.random.normal(), numpy.random.normal()))
    execute(result)

def test_random_2():
    """two gaussian distributions"""
    result = []
    for i in range(1000):
        result.append((numpy.random.normal(2), numpy.random.normal(2)))
    for i in range(1000):
        result.append((numpy.random.normal(-2), numpy.random.normal(-2)))
    execute(result)

def test_random_3():
    """three gaussian distributions"""
    result = []
    for i in range(100):
        result.append((numpy.random.normal(3), numpy.random.normal()))
    for i in range(100):
        result.append((numpy.random.normal(-3), numpy.random.normal()))
    for i in range(100):
        result.append((numpy.random.normal(), numpy.random.normal(-3)))
    execute(result)

test_random_1()
test_random_2()
test_random_3()
test_1()
test_2()
test_3()
matplotlib.pyplot.show()


# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd

def fractal_katz(signal):

    """**Katz's Fractal Dimension (KFD)**

    Computes Katz's Fractal Dimension (KFD). The euclidean distances between successive points in
    the signal are summed and averaged, and the maximum distance between the starting point and any
    other point in the sample.

    Here, fractal dimensions range from 1.0 for straight lines, through
    approximately 1.15 for random-walk waveforms, to approaching 1.5 for the most
    convoluted waveforms.

    Parameters
    ----------
    signal : Union[list, np.array, pd.Series]
        The signal (i.e., a time series) in the form of a vector of values.

    Returns
    -------
    kfd : float
        Katz's fractal dimension of the single time series.
    info : dict
        A dictionary containing additional information (currently empty, but returned nonetheless
        for consistency with other functions).

    Examples
    ----------
    * **Step 1.** Simulate different kinds of signals

    .. ipython:: python

      import neurokit2 as nk
      import numpy as np

      # Simulate straight line
      straight = np.linspace(-1, 1, 2000)

      # Simulate random
      random = nk.complexity_simulate(duration=2, method="randomwalk")
      random = nk.rescale(random, [-1, 1])

      # Simulate simple
      simple = nk.signal_simulate(duration=2, frequency=[5, 10])

      # Simulate complex
      complex = nk.signal_simulate(duration=2,
                                   frequency=[1, 3, 6, 12],
                                   noise = 0.1)

      @savefig p_katz.png scale=100%
      nk.signal_plot([straight, random, simple, complex])

    * **Step 2.** Compute KFD for each of them

    .. ipython:: python

      KFD, _ = nk.fractal_katz(straight)
      KFD
      KFD, _ = nk.fractal_katz(random)
      KFD
      KFD, _ = nk.fractal_katz(simple)
      KFD
      KFD, _ = nk.fractal_katz(complex)
      KFD

    References
    ----------
    * Katz, M. J. (1988). Fractals and the analysis of waveforms.
      Computers in Biology and Medicine, 18(3), 145-156. doi:10.1016/0010-4825(88)90041-8.

    """

    # Sanity checks
    if isinstance(signal, (np.ndarray, pd.DataFrame)) and signal.ndim > 1:
        raise ValueError(
            "Multidimensional inputs (e.g., matrices or multichannel data) are not supported yet."
        )

    # Force to array
    signal = np.array(signal)

    # Drop missing values
    signal = signal[~np.isnan(signal)]

    # Define total length of curve
    dists = np.abs(np.diff(signal))
    length = np.sum(dists)

    # Average distance between successive points
    a = np.mean(dists)

    # Compute farthest distance between starting point and any other point
    d = np.max(np.abs(signal - signal[0]))

    kfd = np.log10(length / a) / (np.log10(d / a))

    return kfd, {}


"""Algorithms for integration lecture."""
import chaospy as cp
import numpy as np

def quadrature_newton_trapezoid_one(f, a, b, n):
    """Return quadrature newton trapezoid example."""
    xvals = np.linspace(a, b, n + 1)
    fvals = np.tile(np.nan, n + 1)
    h = xvals[1] - xvals[0]

    weights = np.tile(h, n + 1)
    weights[0] = weights[-1] = 0.5 * h

    for i, xval in enumerate(xvals):
        fvals[i] = f(xval)

    return np.sum(weights * fvals)

def quadrature_newton_simpson_one(f, a, b, n):
    """Return quadrature newton simpson example."""
    if n % 2 == 0:
        raise Warning("n must be an odd integer. Increasing by 1")
        n += 1

    xvals = np.linspace(a, b, n)
    fvals = np.tile(np.nan, n)

    h = xvals[1] - xvals[0]

    weights = np.tile(np.nan, n)
    weights[0::2] = 2 * h / 3
    weights[1::2] = 4 * h / 3
    weights[0] = weights[-1] = h / 3

    for i, xval in enumerate(xvals):
        fvals[i] = f(xval)

    return np.sum(weights * fvals)

def quadrature_gauss_legendre_one(f, a, b, n):
    """Return quadrature gauss legendre example."""
    xvals, weights = np.polynomial.legendre.leggauss(n)
    xval_trans = (b - a) * (xvals + 1.0) / 2.0 + a

    fvals = np.tile(np.nan, n)
    for i, xval in enumerate(xval_trans):
        fvals[i] = ((b - a) / 2.0) * f(xval)

    return np.sum(weights * fvals)

def quadrature_gauss_legendre_two(f, a=-1, b=1, n=10):
    """Return quadrature gauss legendre example."""
    n_dim = int(np.sqrt(n))

    xvals, weight_uni = np.polynomial.legendre.leggauss(n_dim)
    xvals_transformed = (b - a) * (xvals + 1.0) / 2.0 + a

    weights = np.tile(np.nan, n_dim ** 2)
    fvals = np.tile(np.nan, n_dim ** 2)

    counter = 0
    for i, x in enumerate(xvals_transformed):
        for j, y in enumerate(xvals_transformed):
            weights[counter] = weight_uni[i] * weight_uni[j]
            fvals[counter] = f([x, y])
            counter += 1

    return ((b - a) / 2) ** 2 * np.sum(weights * np.array(fvals))

def monte_carlo_naive_one(f, a=0, b=1, n=10, seed=123):
    """Return naive monte carlo example."""
    np.random.seed(seed)
    xvals = np.random.uniform(size=n)
    fvals = np.tile(np.nan, n)
    weights = np.tile(1 / n, n)

    scale = b - a
    for i, xval in enumerate(xvals):
        fvals[i] = f(a + xval * (b - a))

    return scale * np.sum(weights * fvals)

def monte_carlo_naive_two_dimensions(f, a=0, b=1, n=10, seed=128):
    """Return naive monte carlo example (two-dimensional).

    Restricted to same integration domain for both variables.
    """
    np.random.seed(seed)
    xvals = np.random.uniform(low=a, high=b, size=2 * n).reshape(n, 2)
    volume = (b - a) ** 2

    fvals = np.tile(np.nan, n)
    weights = np.tile(1 / n, n)

    for i, xval in enumerate(xvals):
        fvals[i] = f(xval)

    return volume * np.sum(weights * fvals)

def monte_carlo_quasi_two_dimensions(f, a=0, b=1, n=10, rule="random"):
    """Return Monte Carlo example (two-dimensional).

    Corresponds to naive Monthe Carlo for `rule='random'`. Restricted to same
    integration domain for both variables.
    """
    distribution = cp.J(cp.Uniform(a, b), cp.Uniform(a, b))
    samples = distribution.sample(n, rule=rule).T
    volume = (b - a) ** 2

    fvals = np.tile(np.nan, n)
    weights = np.tile(1 / n, n)

    for i, xval in enumerate(samples):
        fvals[i] = f(xval)

    return volume * np.sum(weights * fvals)


""" Autor V M Krushnarao Kotteda (Murali)
date Oct 31 2020 """

import numpy as np
import numpy.linalg as nl

def Jacobi_expand(A, b, n):
    n = len(b)
    X = np.zeros(n)
    Y = np.zeros(n)
    D = np.zeros(n)
    X1 = np.zeros([n, 1])
    b1 = np.zeros([n, 1])
    for i in range(0, n):
        b1[i][0] = b[i]
    k = 0
    while (True):
        for i in range(0, n):
            tmp = 0
            for j in range(0, n):
                if j != i:
                    tmp += A[i][j] * X[j]
            Y[i] = (b[i] - tmp) / A[i][i]
        for i in range(0, n):
            X1[i][0] = Y[i]
        D = np.dot(A, X1)
        D = D - b1
        k = k + 1
        if k >= 100:
            break
        if  (nl.norm(D) / nl.norm(b) < 1e-6) & (nl.norm(D) / nl.norm(b) > -(1e-6)):
            break
        X = Y.copy()
    return Y

def jacobi(A, b, x_init, epsilon=1e-6, max_iterations=100):
    D = np.diag(np.diag(A))
    LU = A - D
    x = x_init
    for i in range(max_iterations):
        D_inv = np.diag(1 / np.diag(D))
        x_new = np.dot(D_inv, b - np.dot(LU, x))
        residual = np.linalg.norm(x_new - x)
        print('i, Residual %s' %[i,residual])
        if np.linalg.norm(x_new - x) < epsilon:
            return x_new
        x = x_new
    return x

def GaussSeidel(A, b, n):
    X = np.zeros(n)
    Y = np.zeros(n)
    D = np.zeros(n)
    X1 = np.zeros([n, 1])
    b1 = np.zeros([n, 1])
    for i in range(0, n):
        b1[i][0] = b[i]
    k = 0
    while (True):
        for i in range(0, n):
            tmp = 0
            for j in range(0, n):
                if j < i:
                    tmp += A[i][j] * Y[j]
                if j > i:
                    tmp += A[i][j] * X[j]
            Y[i] = (b[i] - tmp) / A[i][i]
        for i in range(0, n):
            X1[i][0] = Y[i]
        D = np.dot(A, X1)
        D = D - b1
        k = k + 1
        if k >= 50:
            break
        if  (nl.norm(D) / nl.norm(b) < 1e-6) & (nl.norm(D) / nl.norm(b) > -(1e-6)):
            break
        X = Y.copy()
    return Y

def sor_solver(A, b, omega, initial_guess, convergence_criteria):
  """
  This is an implementation of the pseduo-code provided in the Wikipedia article.
  Inputs:
    A: nxn numpy matrix
    b: n dimensional numpy vector
    omega: relaxation factor
    initial_guess: An initial solution guess for the solver to start with
  Returns:
    phi: solution vector of dimension n
  """
  phi = initial_guess[:]
  residual = np.linalg.norm(np.matmul(A, phi) - b) #Initial residual
  ic = 0
  while residual > convergence_criteria:
    ic = ic + 1
    for i in range(A.shape[0]):
      sigma = 0
      for j in range(A.shape[1]):
        if j != i:
          sigma += A[i][j] * phi[j]
      phi[i] = (1 - omega) * phi[i] + (omega / A[i][i]) * (b[i] - sigma)
    residual = np.linalg.norm(np.matmul(A, phi) - b)
#    print('Residual: {0:10.6g}'.format(residual))
    print('ic, Residual %s' %[ic,residual])
  return phi

def SOR(A, b, n, w):
    n = len(b)
    X = np.zeros(n)
    Y = np.zeros(n)
    D = np.zeros(n)
    X1 = np.zeros([n, 1])
    b1 = np.zeros([n, 1])
    for i in range(0, n):
        b1[i][0] = b[i]
    k = 0
    while (True):
        for i in range(0, n):
            tmp1 = 0
            tmp2 = 0
            for j in range(0, n):
                if j < i:
                    tmp1 += A[i][j] * Y[j]
                if j > i:
                    tmp2 += A[i][j] * X[j]
            Y[i] = X[i] + (w * (b[i] - tmp1 - tmp2)) / A[i][i]
        for i in range(0, n):
            X1[i][0] = Y[i]
        D = np.dot(A, X1)
        D = D - b1
        k = k + 1
        if k >= 50:
            break
        if  (nl.norm(D) / nl.norm(b) < 1e-6) & (nl.norm(D) / nl.norm(b) > -(1e-6)):
            break
        X = Y.copy()

#An example case that mirrors the one in the Wikipedia article
residual_convergence = 1e-8
omega = 0.5 #Relaxation factor

#A = np.array([ [4, -1, -6, 0], [-5, -4, 10, 8], [0,9,4,-2], [1,0,-7,5]])
#b = np.array([2,21,-12,-6])
A = np.array([    [5, 2, 1, 1],     [2, 6, 2, 1],     [1, 2, 7, 1],     [1, 1, 2, 8] ])
#A = np.array([    [3, 2, 1, 1],     [2, 4, 2, 1],     [1, 2, 3, 1],     [1, 1, 2, 3] ])
b = np.array([29, 31, 26, 19])

#A = np.zeros([100, 100]);
#for i in range(0, 100):
#    A[i][i] = 2
#    if i == 0:
#        A[i][i + 1] = -1
#    elif i == 99:
#        A[i][i - 1] = -1
#    else:
#        A[i][i - 1] = A[i][i + 1] = -1

initial_guess = np.ones(4)
# 4x1  + 2x2 + x3 + x4 = 29
# 2x1  + 5x2 + 2x3 + x4 = 31
# x1   + 2x2 + 4x3 + x4 = 26
# x1   + x2  + 2x3 + 4x4 = 19
#n = 500
x = sor_solver(A, b, omega, initial_guess, 1e-6)
print("SOR")
print(x)
x = jacobi(A, b, initial_guess)
residual = np.linalg.norm(np.matmul(A, x) - b)
print('Residual: {0:10.6g}'.format(residual))
print(x)
print(np.matmul(A, x) - b)
#w = 0.5
#x=sor(A,b,n,w)
#residual = np.linalg.norm(np.matmul(A, x) - b)
#print('Residual: {0:10.6g}'.format(residual))
#print(x)

n=len(b)
result1 = Jacobi_expand(A, b, n)
print("Jacobi")
print(result1)
result2 = GaussSeidel(A, b, n)
print("Gauss seidel")
print(result2)
#result3 = SOR(A, b, n, 0.5)
#print("SOR")
#print(result3)


# Referenec Book: Deep Learning with Python

import numpy as np

# 2.3.1 Element-wise operations
x = np.array([[1, 2, 3],
              [0, -1, 3]])
y = np.array([[-1, 2, 0],
              [0, -1, 3]])

z = x + y
z = np.maximum(z, 0)
print(z)

# 2.3.2 Broadcasting
'''
When possible, and if there’s no ambiguity, the smaller tensor will be broadcasted to
match the shape of the larger tensor. Broadcasting consists of two steps:
    1. Axes (called broadcast axes) are added to the smaller tensor to match the ndim of
       the larger tensor.
    2. The smaller tensor is repeated alongside these new axes to match the full shape
       of the larger tensor.
'''
x = np.random.random((1,3,4,3))
y = np.random.random((4,3))
z = np.maximum(x, y)
print(z)
'''
[[[[0.61973766 0.66481166 0.81795199]
   [0.86333686 0.14431782 0.4936027 ]
   [0.65448209 0.85841484 0.9671061 ]
   [0.92107294 0.92064691 0.96485797]]

  [[0.36866181 0.33668766 0.81795199]
   [0.86333686 0.10121043 0.65585511]
   [0.53269415 0.5442037  0.33534223]
   [0.42725368 0.35350347 0.96485797]]

  [[0.67249464 0.4645158  0.81795199]
   [0.86333686 0.30171043 0.73432242]
   [0.53269415 0.40951789 0.42569725]
   [0.4497047  0.63681454 0.96485797]]]]
'''

# 2.3.3 Tensor dot
x = np.array([[1, 2, 3],
              [0, -1, 3]])
y = np.array([[-1, 2, 0],
              [0, -1, 3]])

# z = np.dot(x, y)
# print(z)
# ValueError: shapes (2,3) and (2,3) not aligned: 3 (dim 1) != 2 (dim 0)

# 2.3.4 Tensor reshaping

y = y.reshape(3,2)
z = np.dot(x, y)
print(z)
'''
[[-4 11]
 [-3  9]]
'''
x = np.array([[0., 1.],
              [2., 3.],
              [4., 5.]])

print(x.shape)
x = x.reshape((6,1))
print(x)
'''
[[0.]
 [1.]
 [2.]
 [3.]
 [4.]
 [5.]]
'''
print(x.shape)  # (6, 1)

# 2.3.5 Geometric interpretation of tensor operations
'''
 In general, elementary geometric operations such as affine transformations, rotations,
 scaling, and so on can be expressed as tensor operations. For instance, a rotation of a
 2D vector by an angle theta can be achieved via a dot product with a 2 × 2 matrix
 R = [u, v] , where u and v are both vectors of the plane: u = [cos(theta),
 sin(theta)] and v = [-sin(theta), cos(theta)] .
'''

# 2.3.6 A geometric interpretation of deep learning
'''
 You just learned that neural networks consist entirely of chains of tensor operations and
 that all of these tensor operations are just geometric transformations of the input data.
 It follows that you can interpret a neural network as a very complex geometric transfor-
 mation in a high-dimensional space, implemented via a long series of simple steps.
    In 3D , the following mental image may prove useful. Imagine two sheets of colored
 paper: one red and one blue. Put one on top of the other. Now crumple them
 together into a small ball. That crumpled paper ball is your input data, and each sheet
 of paper is a class of data in a classification problem. What a neural network (or any
 other machine-learning model) is meant to do is figure out a transformation of the
 paper ball that would uncrumple it, so as to make the two classes cleanly separable
 again. With deep learning, this would be implemented as a series of simple transfor-
 mations of the 3D space, such as those you could apply on the paper ball with your fin-
 gers, one movement at a time.

    Uncrumpling paper balls is what machine learning is about: finding neat representa-
 tions for complex, highly folded data manifolds. At this point, you should have a
 pretty good intuition as to why deep learning excels at this: it takes the approach of
 incrementally decomposing a complicated geometric transformation into a long
 chain of elementary ones, which is pretty much the strategy a human would follow to
 uncrumple a paper ball. Each layer in a deep network applies a transformation that
 disentangles the data a little—and a deep stack of layers makes tractable an extremely
 complicated disentanglement process.
'''


# -*- coding: utf-8 -*-
"""
Created on Fri Jun  5 11:56:32 2020
Some functions to do linear regression
@author: Ji-Ping Dai
"""
import numpy as np
import matplotlib.pyplot as plt
plt.rc('text',usetex=True)
plt.rc('font',family='Times New Roman')

def plotdata(X,y,ax):
    '''PLOTDATA Plots the data points x and y into a new figure'''
    plt.xticks(fontsize=20)
    plt.yticks(fontsize=20)
    ax.set_ylabel('Profit in \$10,000',size=20)
    ax.set_xlabel('Population of City in 10,000s',size=20)
    ax.plot(X, y, 'rx',label='Training data')
    return

def costfunc(x,y,theta):
    '''Compute cost for linear regression'''
    m = len(y)
    J = 1/2/m*np.sum((x@theta-y)**2)
    return J

def gradientDescent(x,y,theta,iterations,alpha):
    '''Performs gradient descent to learn theta'''
    m = len(y)
    cost = np.zeros(iterations)
    for i in range(iterations):
        theta = theta-1/m*alpha*x.T@(x@theta-y)
        cost[i] = costfunc(x,y,theta)
    return theta, cost

def featureNormalize(X):
    '''Normalizes the features in X '''
    mu = np.mean(X,axis=0)
    sigma = np.std(X,axis=0)
    X = (X-mu)/sigma
    return X, mu, sigma


import math

import numpy as np
import matplotlib.pyplot as plt

def create_plot():
    "Funcție ajutătoare pentru crearea graficului."
    # Creez un subplot
    fig, ax = plt.subplots(1, dpi=200)

    # Setez axele de coordonate
    ax.spines['bottom'].set_position('zero')
    ax.spines['top'].set_color('none')
    ax.spines['right'].set_color('none')

    return ax

###
print("Exercițiul 1")

# Pentru a aproxima valoarea lui radical din 8,
# căutăm soluția pozitivă a ecuației x^2 - 8 = 0
f = lambda x: x ** 2 - 8

# Metoda bisecției, dezvoltată la primul laborator
def bisectie(f, a, b, epsilon=1e-8):
    """Găsește rădăcina funcției `f` pe intervalul `[a, b]`
    cu precizia `epsilon`.
    """

    # Calculăm valorile în capătul stâng
    f_a = f(a)

    # Prima estimare, mijlocul intervalului inițial
    x_num = (a + b) / 2

    # Numărul necesar de iterații
    num_iterations = math.floor(math.log2((b - a) / epsilon) - 1) + 1

    # Aplicăm algoritmul
    for step in range(num_iterations):
        value = f(x_num)

        # Am găsit fix valoarea căutată, ieșim
        if value == 0:
            break
        elif f_a * value < 0:
            b = x_num
        else:
            a = x_num

        # Luăm mijlocul noului interval
        x_num = (a + b) / 2

    return x_num

# Căutăm rădăcina pozitivă
root = bisectie(f, 0, 8)

print("Radical din 8 este", root)
print()

# Afișez funcția folosită pentru calcularea radicalului
xs = np.linspace(0, 8, int(1e5))

ax = create_plot()
ax.set_title("Găsirea rădăcinii pătrate")

ax.plot(xs, f(xs), label="x^2 - 8")
ax.scatter(root, 0, c='red')

ax.legend()
plt.show()

###
print("Exercițiul 2")

# Definesc cele două părți ale egalității
f = lambda x: np.exp(x - 2)
g = lambda x: np.cos(np.exp(x - 2)) + 1

h = lambda x: f(x) - g(x)

# Găsim soluția ecuației h(x) = 0, care va fi și soluția ecuației inițiale
# Intervalul [1, 3] a fost ales analizând graficul
root = bisectie(h, 1, 3)
print("Funcțiile se intersectează în", root)

# Afișăm grafic rezultatul
xs = np.linspace(0, 5, int(1e5))

ax = create_plot()
ax.set_title("Rezolvarea unei ecuații")

ax.plot(xs, f(xs), label="e^(x - 2)")
ax.plot(xs, g(xs), label="cos(e^(x - 2)) + 1")
ax.scatter(root, f(root), c='red')

ax.legend()
plt.show()

print()

###
print("Exercițiul 3")

def pozitie_falsa(f, a, b, epsilon=1e-5):
    """Găsește soluția ecuației `f(x) = 0` folosind metoda poziției false
    în intervalul [a, b], cu precizie `epsilon`.
    """

    f_a = f(a)
    f_b = f(b)

    prev = (a * f_b - b * f_a)/(f_b - f_a)
    f_prev = f(prev)

    # Setez o valoare lui new, pentru cazul când f_prev == 0
    # și ies imediat din buclă.
    new = prev

    num_iterations = 0
    while True:
        # Dacă am nimerit soluția exactă, mă opresc
        if f_prev == 0:
            break
        elif f_a * f_prev < 0:
            # Intervalul bun e cel din stânga
            b = prev
            f_b = f(b)
        else:
            # Intervalul bun e cel din dreapta
            a = prev
            f_a = f(a)

        # Calculez noul punct de intersecție
        new = (a * f_b - b * f_a)/(f_b - f_a)
        f_new = f(new)

        if np.abs(new - prev) / np.abs(prev) < epsilon:
            break

        prev, f_prev = new, f_new
        num_iterations += 1

    return new, num_iterations

f = lambda x: (x ** 3) - 19 * x + 30

# Calculez rădăcinile
# Intervalele au fost alese pe baza graficului
r1, n1 = pozitie_falsa(f, -6, -4)
print("Am găsit soluția", r1, "în", n1, "iterații")
r2, n2 = pozitie_falsa(f, 1, 2.5)
print("Am găsit soluția", r2, "în", n2, "iterații")
r3, n3 = pozitie_falsa(f, 2.5, 4)
print("Am găsit soluția", r3, "în", n3, "iterații")

xs = np.linspace(-5, 5, int(1e5))

ax = create_plot()
ax.set_title("Metoda poziției false")

# Afișez funcția
ax.plot(xs, f(xs), label='x^3 - 19x + 30')
# Afișez rădăcinile
ax.scatter([r1, r2, r3], [0, 0, 0], c='red')

ax.legend()
plt.show()

print()

###
print("Exercițiul 4")

def secanta(f, a, b, x0, x1, epsilon=1e-5):
    """Găsește rădăcina lui f pe intervalul [a, b],
    plecând de la punctele x0 și x1.
    """
    num_iterations = 0
    # Ne oprim când eroarea relativă scade sub epsilon
    while np.abs(x1 - x0) / np.abs(x0) >= epsilon:
        # Calculăm următorul punct folosind secanta
        x_new = (x0 * f(x1) - x1 * f(x0)) / (f(x1) - f(x0))

        if x_new < a or x_new > b:
            raise Exception("Valorile alese pentru x0 și x1 nu converg")

        x0, x1 = x1, x_new
        num_iterations += 1

    return x1, num_iterations

f = lambda x: (x ** 3) - 7 * x + 6

a, b = -3, 3

r1, n1 = secanta(f, a, b, -3, -2.5)
print("Am găsit soluția", r1, "în", n1, "iterații")
r2, n2 = secanta(f, a, b, 0.5, 1.5)
print("Am găsit soluția", r2, "în", n2, "iterații")
r3, n3 = secanta(f, a, b, 1.5, 2.5)
print("Am găsit soluția", r3, "în", n3, "iterații")

xs = np.linspace(a, b, int(1e5))

ax = create_plot()
ax.set_title("Metoda secantei")

# Afișez funcția
ax.plot(xs, f(xs), label='x^3 - 7x + 6')
# Afișez rădăcinile
ax.scatter([r1, r2, r3], [0, 0, 0], c='red')

ax.legend()
plt.show()


#！/usr/bin/python
#-*- coding: <encoding name> -*-
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

class Burgers:
	'''
	A Burgers 1-demension partial differential equation numerical solver with:
		- 3 inputs: viscosity ,time and iteration times N
		- viscosity = 0, 0.01, 0.05
		- time = 0.5, 1.2, 2.0
	Initial and boundary conditions are:
		- u(x,0) = -0.5*x, x belongs to [-1, 1]
	    - u(-1, t) = 0.5, u(1, t) = -0.5
	'''

	def __init__(self, visco, time, N):
		# Give the viscosity, time to stop and gird spacing.
	    self.visco = visco
	    self.time = time
	    self.N = N

	def discretization(self):
		# Discretization of the calculation zone.
		dx = 2.0 / self.N
		dt = 0.0001
		return (dx, dt)

	def macCormack(self, u):
		'''
		u is a numpy array with initial condition.
			- Predictive step: us;
			- Patial of test step: up
			- Intermediate step: unew
			- Storage of all the time step: U
		'''
		dx, dt = self.discretization()
		nt = int(self.time / dt)

		us = np.zeros(self.N + 1)
		up = np.zeros(self.N + 1)
		unew = np.zeros(self.N + 1)
		U = np.zeros((nt + 1, self.N + 1))
		U[0] = u

		for i in range(1, nt + 1):
			# Predictive step
			us[1: -1] = u[1: -1] + (self.visco * dt / (dx) ** 2) * (u[2:] - 2.0 * u[1:-1] + u[0:-2]) \
			- dt / (2.0 * dx) * (u[2:] ** 2 - u[1:-1] ** 2)
			us[0] = 0.5
			us[-1] = -0.5

			# Correction step and combine step
			up[1: -1] = (self.visco / (dx) ** 2) * (us[2:] - 2 * us[1: -1] + us[0: -2]) \
			- 1.0 / (2.0 * dx) * (us[1:-1] ** 2 - us[0: -2] ** 2)
			unew[1: -1] = 0.5 * (u[1: -1] + us[1: -1] + dt * up[1: -1])
			u[1: -1] = unew[1: -1]

			U[i] = u

		return U

if __name__ == '__main__':
	N = 200
	seper = os.sep 		# Use this seperator to satisfy both Windows and Linux usage

	# Read input file of time and viscosity
	time_visco = pd.read_csv('input.txt', sep = ',', usecols=['time', 'mu'])
	time = max(time_visco['time'])
	visco = time_visco['mu']

	# Compute Burgers equation at different viscosity
	for i in range(0, len(visco)):
		u = np.zeros(N + 1)
		x = np.zeros(N + 1)

		for j in range(0, N + 1):
			u[j] = -0.5 * (-1 + j * 2 / N)
			x[j] = -1 + j * 2 / N

		burgers = Burgers(visco[i], time, N)
		uall = burgers.macCormack(u)

		# Save 2-D data to files
		output = pd.DataFrame({'x': x, 't='+str(time_visco['time'][0]): uall[int(time_visco['time'][0] * 10000)], \
			't='+str(time_visco['time'][1]): uall[int(time_visco['time'][1] * 10000)], \
			't='+str(time_visco['time'][2]): uall[int(time_visco['time'][2] * 10000)]})
		output.to_csv('2D' + seper + 'mu=' + str(visco[i]) + '.dat', sep='\t', index=False)

		'''
			Plot 3-D Burgers results, this cost about 16 s, so I comment code below.
			If you want to generate 3-D images by yourself, please uncomment the code below,
			and change some value of the code, to see if there are some differences between
			my output 3-D images
		'''

		'''
		fig = plt.figure()
		ax = fig.add_subplot(111, projection='3d')
		y = np.array([[i * 0.0001] for i in range(0, len(uall))])
		ax.plot_surface(x, y, uall, cmap='viridis')
		ax.set_xlabel('x')
		ax.set_ylabel('t')
		ax.set_zlabel('u')
		ax.set_title(r'$\mu = $' + str(visco[i]))
		plt.savefig('3D' + seper + 'mu=' + str(visco[i]) + '.png', bbox_inches='tight')
		'''


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

# In[1]:

import numpy as np
import pandas as pd
import math
from sklearn.model_selection import KFold
from scipy.stats import poisson
import matplotlib.pyplot as plt
from sklearn.metrics import pairwise_distances

# # Problem-2 Classification
#
# #### 1- Naive Bayes,  2- KNN

# In[2]:

X = pd.read_csv("Bayes_classifier\\X.csv",header=None).values
y = pd.read_csv("Bayes_classifier\\y.csv",header=None).values
kf = KFold(n_splits=10,shuffle=True,random_state=0)

# In[3]:

def compute_confusion_matrix(y_test,y_prediction):
    TP = 0
    FP = 0
    TN = 0
    FN = 0
    for i in range(len(y_test)):
        if(y_prediction[i] == y_test[i]):
            if(y_prediction[i] == 0):
                TN+=1
            else:
                TP+=1
        else:
            if(y_prediction[i] == 0):
                FN+=1
            else:
                FP+=1
    return [TP,FP,FN,TN]

# ####  Naive Bayes

# In[4]:

TP = []
FP = []
TN = []
FN = []
lambda0=[]
lambda1=[]
for train_index,test_index in kf.split(X):
    X_train,X_test = X[train_index],X[test_index]
    y_train,y_test = y[train_index],y[test_index]

    py0 = len(np.where(y_train == 0)[0])/len(X_train)
    py1 = len(np.where(y_train == 1)[0])/len(X_train)

    lambda_0_values = []
    lambda_1_values = []

    for j in range(len(X_train[0])):
        lambda0_numerator = 1
        lambda0_denominator = 1
        lambda1_numerator = 1
        lambda1_denominator = 1

        for i in range(len(X_train)):
            lambda1_numerator+=(y_train[i] * X_train[i][j])
            lambda1_denominator+=(y_train[i])

            lambda0_numerator+=((1-y_train[i]) * X_train[i][j])
            lambda0_denominator+=(1-y_train[i])
        lambda_0_values.append((lambda0_numerator/lambda0_denominator)[0])
        lambda_1_values.append((lambda1_numerator/lambda1_denominator)[0])

    lambda0.append(lambda_0_values)
    lambda1.append(lambda_1_values)
    y_predictions = []
    for i in range(len(X_test)):

        y_prob = []
        product_0 = 1
        product_1 = 1

        for j in range(len(X_test[0])):
            poisson_prob_0 = poisson.pmf(X_test[i][j],lambda_0_values[j])
            poisson_prob_1 = poisson.pmf(X_test[i][j],lambda_1_values[j])

            product_0*=poisson_prob_0
            product_1*=poisson_prob_1

        y_prob.append(product_0*py0)
        y_prob.append(product_1*py1)

        y_predictions.append(np.argmax(y_prob))

    results = compute_confusion_matrix(y_test,y_predictions)

    TP.append(results[0])
    FP.append(results[1])
    FN.append(results[2])
    TN.append(results[3])

# In[5]:

confusion_matrix = [[sum(TP),sum(FP)],[sum(FN),sum(TN)]]
print("The Confusion Matrix is:")

print(confusion_matrix[0])
print(confusion_matrix[1])

print("The Accuracy is:", ((confusion_matrix[0][0] + confusion_matrix[1][1]) /
                           (confusion_matrix[0][0] + confusion_matrix[0][1] +confusion_matrix[1][0] + confusion_matrix[1][1])))

# In[6]:

avg_poisson_parameter_0 = np.mean(lambda0, axis=0)
avg_poisson_parameter_1 = np.mean(lambda1, axis=0)

# In[23]:

fig, ax = plt.subplots(nrows=2, ncols=1, figsize=(15, 10))
ax[0].stem(range(1,55),avg_poisson_parameter_0,"blue", markerfmt="bo")
ax[0].set_title("Stem plot of Poisson Parameters (Class 0: Not Spam)")
ax[0].set_xlabel("feature number")
ax[0].set_ylabel("value of lambda")
ax[0].set_xticks(np.arange(1,55, step=1))

ax[1].stem(range(1,55),avg_poisson_parameter_1,"red", markerfmt="ro")
ax[1].set_title("Stem plot of Poisson Parameters (Class 1: Spam)")
ax[1].set_xlabel("feature number")
ax[1].set_ylabel("value of lambda")
ax[1].set_xticks(np.arange(1,55, step=1))
plt.show()

# In[21]:

plt.figure(figsize=(15, 5))
plt.stem(range(1,55), avg_poisson_parameter_0, "blue", markerfmt="bo", label="0 Class: Not Spam")
plt.stem(range(1,55), avg_poisson_parameter_1, "red", markerfmt="ro", label="1 Class: Spam")
plt.title("Stem plot of Poisson Parameters (seperated by classes)")
plt.xlabel("Feature number")
plt.ylabel("Value of Lambda")
plt.xticks(np.arange(1,55, step=1))
plt.legend()
plt.show()

# #### KNN

# In[140]:

TP = []
FP = []
TN = []
FN = []
all_predictions=[]
all_labels = []
for train_index,test_index in kf.split(X):
    X_train,X_test = X[train_index],X[test_index]
    y_train,y_test = y[train_index],y[test_index]
    all_labels.append([i[0] for i in y_test])
    tp = np.zeros(shape = (20))
    fp = np.zeros(shape = (20))
    fn = np.zeros(shape = (20))
    tn = np.zeros(shape = (20))
    mean = np.mean(X_train,axis=0)
    std = np.std(X_train,axis=0)
    X_train = (X_train - mean)/std
    X_test = (X_test - mean)/std
    predictions=[]
    for row in range(len(X_test)):
        distances=[]
        #computing the distance matrix
        for j in X_train:
            distances.append(np.sum(np.abs(X_test[row]-j)))
#       Finding 20 Nearest neighbours of the test sample.
#       list_neighbours = [i[0] for i in sorted(enumerate(distances), key=lambda x:x[1])][:20]
        list_neighbours = np.argsort(distances)[:20]
        pred = []
        for k in range(1,21):
            count_1 = len(np.where(y_train[list_neighbours[:k]] == 1)[0])
            count_0 = len(np.where(y_train[list_neighbours[:k]] == 0)[0])
            # picking the nearest neighbour's label in the case of tie.
            if(count_1==count_0):
                temp_prediction = y_train[list_neighbours[0]][0]
                pred.append(temp_prediction)
                if(temp_prediction == y_test[row][0]):
                    if(temp_prediction == 1):
                        tp[k-1]+=1
                    else:
                        tn[k-1]+=1
                else:
                    if(temp_prediction == 1):
                        fp[k-1]+=1
                    else:
                        fn[k-1]+=1
            elif(count_1>count_0):
                temp_prediction = 1
                pred.append(temp_prediction)
                if(temp_prediction == y_test[row][0]):
                    tp[k-1]+=1
                else:
                    fp[k-1]+=1
            else:
                temp_prediction = 0
                pred.append(temp_prediction)
                if(temp_prediction == y_test[row][0]):
                    tn[k-1]+=1
                else:
                    fn[k-1]+=1
        predictions.append(pred)
    all_predictions.append(predictions)
    TP.append(list(tp))
    FP.append(list(fp))
    TN.append(list(tn))
    FN.append(list(fn))

# In[142]:

TP_list = (list(np.sum(TP,axis=0)))
FP_list = (list(np.sum(FP,axis=0)))
FN_list = (list(np.sum(FN,axis=0)))
TN_list = (list(np.sum(TN,axis=0)))
prediction_accuracies_cumulative = []
for i in range(20):
    prediction_accuracies_cumulative.append((TP_list[i] + TN_list[i])/
                                            (TP_list[i] + TN_list[i] + FP_list[i] + FN_list[i]))

# In[143]:

plt.plot(range(1,21),prediction_accuracies_cumulative)
plt.scatter(range(1,21),prediction_accuracies_cumulative)
plt.title("Accuracy v/s K")
plt.xlabel("K")
plt.ylabel("Accuracy")
plt.xticks(np.arange(1,21, step=1))
plt.show()

# # Problem-3 : Gaussian Process

# In[69]:

X_train = pd.read_csv("Gaussian_process\\X_train.csv",header=None).values
y_train = pd.read_csv("Gaussian_process\\y_train.csv",header=None).values
X_test = pd.read_csv("Gaussian_process\\X_test.csv",header=None).values
y_test = pd.read_csv("Gaussian_process\\y_test.csv",header=None).values

# In[70]:

def RMSE(y_predicted,y_test):
    y_predicted = np.array(y_predicted)
    y_test = np.array(y_test)
    return np.sqrt(np.sum((y_predicted - y_test)**2)/len(y_test))

# In[71]:

class Gaussian_process:
    def __init__(self,X_train,y_train, sigma2, b):
        self.sigma2 = sigma2
        self.b = b
        self.X_train = X_train
        self.y_train = y_train

    def Kernel(self,xi,xj):
        return math.exp((-1/self.b) * np.sum((xi -xj)**2))

    def fit(self):
        K = []
        for i in self.X_train:
            temp = []
            for j in self.X_train:
                temp.append(self.Kernel(i,j))
            K.append(temp)
        self.K = np.asarray(K)

    def predict(self,x0):
        temp = []
        for j in self.X_train:
            temp.append(self.Kernel(x0,j))
        KxD = np.asarray(temp)
        sigma2I = self.sigma2 * np.identity(self.K.shape[0])
        mean = (np.matmul(np.matmul(KxD,np.linalg.inv(sigma2I + self.K)), self.y_train))[0]
        variance = self.sigma2 + self.Kernel(x0,x0) + np.matmul(np.matmul(KxD,np.linalg.inv(sigma2I + self.K)), np.transpose(KxD))

        return [mean,variance]

# In[51]:

B = [5,7,9,11,13,15]
Sigma2 = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]

# In[52]:

RMSES = []
y_test = [i[0] for i in y_test]
for b in B:
    temp = []
    for sigma2 in Sigma2:
        y_predicted = []
        process = Gaussian_process(X_train,y_train,sigma2 = sigma2, b = b)
        process.fit()
        for i in X_test:
            y_predicted.append(process.predict(i)[0])
        temp.append(RMSE(y_predicted,y_test))
    RMSES.append(temp)

# In[53]:

rmse_values = pd.DataFrame(RMSES, columns = Sigma2, index = B)

# In[55]:

rmse_values

# In[72]:

X_carweight_train = X_train[:,3]
X_carweight_test = X_test[:,3]

# In[73]:

b2 = 5
sigma22 = 2

# In[74]:

y_predicted_train = []
process = Gaussian_process(X_carweight_train,y_train,sigma2 = sigma22, b = b2)
process.fit()
for i in X_carweight_train:
    y_predicted_train.append(process.predict(i)[0])

# In[75]:

y_train = [i[0] for i in y_train]
X_carweight_train = list(X_carweight_train)
y_preds = [i for i,_ in sorted(zip(y_predicted_train,X_carweight_train), key=lambda pair: pair[1])]

# In[79]:

plt.figure(figsize=(5,5))
plt.scatter(X_carweight_train,y_train, label = "True values of y")
plt.plot(sorted(X_carweight_train),y_preds,color="red",linewidth=4.0,label = "Predicted values of y")
plt.xlabel("x[4] : Car Weight")
plt.ylabel("y : Miles Per Gallon")
plt.title("Miles Per Gallon v/s Car weight")
plt.legend()
plt.show()

# In[ ]:

Bayes Classifier and Gaussian Process/hw2_pds2136.py


import numpy as np
import matplotlib.pyplot as plt
from autologging import logged, traced

from fullwavepy.generic.parse import kw
from fullwavepy.numeric.const import *
from fullwavepy.numeric.generic import *

@traced
@logged
def derivative(y, **kwargs):
  """
  Calculate a derivative.

  Parameters
  ----------
  y : array
    A vector of numbers.
  **kwargs : keyword arguments (optional)
    Current capabilities:
    - n : int
      Order of derivative.
      Default: 1.
    - acc : int
      Order of numerical accuracy.
      Default: 2.
    - dx : float
      Grid-cell size.
      Default: 1.

  Returns
  -------
  deriv : array
    Derivative of y. It has
    the same length as y.

  Notes
  -----
  We might as well use the scipy.misc.derivative or
  numpy.gradient but an own implementation
  is always more instructive.

  Benchmarked against numpy.gradient. Same results.

  Amplitude is the same as analytical derivative,
  no normalization needed - don't forget about
  differentiation rules that are applied implicitly,
  e.g.: d(sin(wx))/dx = w cos(wx)

  """
  n = kw('n', 1, kwargs) # ORDER OF DERIVATIVE
  acc = kw('acc', 2, kwargs) # ORDER OF NUMERICAL ACCURACY
  dx = kw('dx', 1, kwargs)

  deriv = np.zeros(len(y))

  if n == 1:
    if acc == 2:
      # INTERIOR
      deriv[1:-1] = [(-0.5*y[i-1] + 0.5*y[i+1]) / dx for i in range(1, len(y)-1)]

      # LEFT EDGE - FORWARD FD
      deriv[0] = ((-3/2.)*y[0] + 2*y[1] - 1/2.*y[2]) / dx

      # RIGHT EDGE - BACKWARD FD (NOTE: OPPOSITE SIGNS)
      deriv[-1] = (3/2.*y[-1] - 2*y[-2] + 1/2.*y[-3]) / dx

    else:
      raise NotImplementedError('Order of accuracy: %s' % acc)

  else:
    raise NotImplementedError('Order of derivative: %s' % n)

  return deriv

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


import numpy as np
from numba import jit, njit
from copy import copy
from enum import Enum, unique, auto
import 靴紐

@unique
class Order(Enum):
    CLOCKWISE = auto()
    COUNTERCLOCKWISE = auto()

@jit
def ensure(xys, order=Order.COUNTERCLOCKWISE):
    """
    args
        xys, ndarray
            of shape (n_pts, 2)
        order, CLOCKWISE/COUNTERCLOCKWISE

    return
        ordered_xys, ndarray

    Note. This function only works for convex polygones in 2D space.
    """
    if order not in set(Order):
        raise TypeError("order arg should be either Order.CLOCKWISE or Order.COUNTERCLOCKWISE")

    # We construct a list of indices that corresponds to clockwise order
    n = len(xys)
    clockwise_indices = [0]
    remaining_indices = list(range(1, n))
    while len(remaining_indices) > 1:
        best_index = remaining_indices[0]
        u = xys[best_index, :] - xys[clockwise_indices[-1], :]
        for index in range(1, len(remaining_indices)):
            v = xys[remaining_indices[index], :] - xys[clockwise_indices[-1], :]
            A = np.array([u, v])
            determinant = np.linalg.det(A)
            if determinant > 0:
                best_index = remaining_indices[index]
                u = v
            elif determinant == 0:
                raise ValueError("det(A) == 0 => two edges of the polygone colinear")
        clockwise_indices.append(best_index)
        remaining_indices.remove(best_index)
    clockwise_indices.append(remaining_indices[0])
    if order == Order.CLOCKWISE:
        ordered_xys = xys[clockwise_indices]
    else:
        ordered_xys = xys[[clockwise_indices[i%n] for i in range(0, -n, -1)]]
    return ordered_xys

@jit
def area(xys):
    xs = xys[:, 0]
    ys = xys[:, 1]
    n = len(xys)
    # the following line cannot be jit-ed, but the one following it can,
    # no jit here does not give good performance, if not worsen.
    #A = sum(ys[k]*(xs[(k-1)%n] - xs[(k+1)%n])for k in range(n)) * 0.5
    A = sum([ys[k]*(xs[(k-1)%n] - xs[(k+1)%n])for k in range(n)]) * 0.5
    return A

@jit
def area_vectorize(xys):
    xs = xys[:, 0]
    ys = xys[:, 1]
    n = len(xys)
    shifted_left = xs[[(i-1) % n for i in range(n)]]
    shifted_right = xs[[(i+1) % n for i in range(n)]]
    A = np.dot(ys, shifted_left - shifted_right).astype(np.float32) * 0.5
    return A

@njit
def area_stackoverflow(xys):
    xs = xys[:, 0]
    ys = xys[:, 1]
    S1 = np.sum(xs * np.roll(ys, -1))
    S2 = np.sum(ys * np.roll(xs, -1))
    A = .5 * np.abs(S1 - S2)
    return A


# Example of an HMM with Gaussian emission in 2D
# For a matlab version, see https://github.com/probml/pmtk3/blob/master/demos/hmmLillypadDemo.m

# Author: Gerardo Durán-Martín (@gerdm)

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
import pyprobml_utils as pml

def sample_hmm(n_samples, initial_probs, mu_collection, cov_collection):
    """
    Sample from a 2-dimensional HMM

    Parameters
    ----------
    n_samples: int
        Number of samples to be generated
    initial_probs: numpy.ndarray(n_states, )
        Initial probabilities for each state
    mu_collection: np.array(n_states, dim)
        Collection of means for each state
    cov_collection: np.array(n_states, dim, dim)
        Collection of covariances for each state

    Returns
    -------
    * numpy.ndarray(n_samples, d)
        Observations
    * numpy.ndarray(n_samples, )
        Latent states of the system
    """
    samples_obs = np.zeros((n_samples, 2))
    samples_state = np.zeros((n_samples,), dtype=int)
    zi = np.random.choice(z, p=initial_probs)
    N = multivariate_normal(mu_collection[zi], cov_collection[zi])
    samples_obs[0] = N.rvs()
    samples_state[0] = zi

    for i in range(1, n_samples):
        zi = np.random.choice(z, p=A[zi])
        N = multivariate_normal(mu_collection[zi], cov_collection[zi])
        samples_obs[i] = N.rvs()
        samples_state[i] = zi
    return samples_obs, samples_state

def plot_2dhmm(samples_obs, samples_state, colors, ax, xmin, xmax, ymin, ymax):
    """
    Plot the trajectory of a 2-dimensional HMM

    Parameters
    ----------
    samples_obs: numpy.ndarray(n_samples, 2)
        Observations
    samples_state: numpy.ndarray(n_samples, )
        Latent state of the system
    colors: list(int)
        List of colors for each latent state

    Returns
    -------
    * matplotlib.axes
    * colour of each latent state
    """
    color_sample = [colors[i] for i in samples_state]
    Xgrid = np.mgrid[xmin:xmax:0.01, ymin:ymax:0.01]

    for k, (mu, S, color) in enumerate(zip(mu_collection, cov_collection, colors)):
        N = multivariate_normal(mean=mu, cov=S)
        Z = np.apply_along_axis(N.pdf, 0, Xgrid)
        ax.contour(*Xgrid, Z, levels=[1], colors=color, linewidths=3)
        ax.text(*(mu + 0.13), f"$k$={k + 1}", fontsize=13, horizontalalignment="right")
    ax.plot(*samples_obs.T, c="black", alpha=0.3, zorder=1)
    ax.scatter(*samples_obs.T, c=color_sample, s=30, zorder=2, alpha=0.8)
    return ax, color_sample

if __name__ == "__main__":
    plt.rcParams["axes.spines.right"] = False
    plt.rcParams["axes.spines.top"] = False

    initial_probs = np.array([0.3, 0.2, 0.5])

    # transition matrix
    A = np.array([
        [0.3, 0.4, 0.3],
        [0.1, 0.6, 0.3],
        [0.2, 0.3, 0.5]
    ])

    S1 = np.array([
        [1.1, 0],
        [0, 0.3]
    ])

    S2 = np.array([
        [0.3, -0.5],
        [-0.5, 1.3]
    ])

    S3 = np.array([
        [0.8, 0.4],
        [0.4, 0.5]
    ])

    cov_collection = np.array([S1, S2, S3]) / 60
    mu_collection = np.array([
        [0.3, 0.3],
        [0.8, 0.5],
        [0.3, 0.8]
    ])

    np.random.seed(314)
    n_samples = 50
    z = np.array([0, 1, 2])
    colors = ["tab:green", "tab:blue", "tab:red"]

    xmin, xmax = 0, 1
    ymin, ymax = 0, 1.2
    samples_obs, samples_state = sample_hmm(n_samples, initial_probs, mu_collection, cov_collection)

    fig, ax = plt.subplots()
    _, color_sample = plot_2dhmm(samples_obs, samples_state, colors, ax, xmin, xmax, ymin, ymax)
    pml.savefig("hmm_lillypad_2d.pdf")

    fig, ax = plt.subplots()
    ax.step(range(n_samples), samples_state, where="post", c="black", linewidth=1, alpha=0.3)
    ax.scatter(range(n_samples), samples_state, c=color_sample, zorder=3)
    pml.savefig("hmm_lillypad_step.pdf")
    plt.show()