# integrantes: GABRIEL GOMEZ, EDUARDO DE LA HOZ, STEPHANIA DE LA HOZ

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.linalg as la
import sys
import mpl_toolkits.mplot3d as mpl
from numpy import genfromtxt
from collections import Counter
from sklearn.preprocessing import StandardScaler

def cleaner(df, k):
    return df[(abs(df[0]-np.mean(df[0])) <= k*np.std(df[0])) & (abs(df[1]-np.mean(df[1])) <= k*np.std(df[1])) & (abs(df[2]-np.mean(df[2])) <= k*np.std(df[2])) & (abs(df[3]-np.mean(df[3])) <= k*np.std(df[3]))]

df = pd.read_table('irisdata.txt', skiprows=9, header=None)
dfClean = df
cat = df.iloc[:, 4].values
df = df.drop(columns=4)
rawdata = np.array(df)
covRawData = np.cov(rawdata.T)
resultRaw = la.eig(covRawData)
eugenVector = resultRaw[1]
eugenValors = resultRaw[0].real

print('matriz de covarianza: ')
print(covRawData)
print('\n')
print('eugenvalores: ')
print(eugenValors)
print('\n')
print('eugenvectores: ')
print(eugenVector)

sumEugen = np.sum(eugenValors)
porEugen = eugenValors/sumEugen
porEugen = porEugen*100

x = np.arange(4)

fig, ax = plt.subplots()
lang = ['PC1', 'PC2', 'PC3', 'PC4']
ax.set_ylabel('n')
ax.set_title('nices')
ax.bar(lang, porEugen, edgecolor='black')
plt.show()

eugenPares = [(np.abs(eugenValors[i]), eugenVector[:, i])
              for i in range(len(eugenValors))]

zerros = np.zeros((4, 1))
matrix1D = np.hstack((eugenPares[0][1].reshape(4, 1), zerros.reshape(4, 1)))
matrix2D = np.hstack((eugenPares[0][1].reshape(
    4, 1), eugenPares[1][1].reshape(4, 1)))
matrix3D = np.hstack((eugenPares[0][1].reshape(
    4, 1), eugenPares[1][1].reshape(4, 1), eugenPares[2][1].reshape(4, 1)))

z = rawdata.dot(matrix3D)
lab = (0, 1, 2)
color = ('green', 'red', 'brown')
with plt.style.context('seaborn-whitegrid'):
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    for lab, color in zip(lab, color):
        ax.scatter(z[cat == lab, 0], z[cat == lab, 1],
                   z[cat == lab, 2], c=color, s=10, label=lab)
    plt.legend(loc=2)
    plt.show()

y = rawdata.dot(matrix2D)
lab2 = (0, 1, 2)
color2 = ('green', 'red', 'brown')
with plt.style.context('seaborn-whitegrid'):
    plt.figure(figsize=(6, 4))
    for lab2, color2, in zip(lab2, color2):
        plt.scatter(y[cat == lab2, 0], y[cat == lab2, 1], label=lab2, c=color2)
    plt.xlabel('componente 1')
    plt.ylabel('componente 2')
    plt.legend(loc='lower center')
    plt.tight_layout()
    plt.show()

v = rawdata.dot(matrix1D)
lab3 = (0, 1, 2)
color3 = ('green', 'red', 'brown')
with plt.style.context('seaborn-whitegrid'):
    plt.figure()
    for lab3, color3, in zip(lab3, color3):
        plt.scatter(v[cat == lab3, 0], v[cat == lab3, 1],
                    label=lab3, c=color3, s=10)
    plt.xlabel('componente 1')
    plt.ylabel('componente 2')
    plt.legend(loc='lower center')
    plt.tight_layout()
    plt.show()

k = 2
cleanDf = cleaner(dfClean, k)
catClean = cleanDf.iloc[:, 4].values
cleanDf = cleanDf.drop(columns=4)
cleanData = np.array(cleanDf)
covCleanData = np.cov(cleanData.T)
resultClean = la.eig(covCleanData)
eugenVectorClean = resultClean[1]
eugenValorsClean = resultClean[0].real
eugenParesClean = [(np.abs(eugenValorsClean[i]), eugenVectorClean[:, i])
                   for i in range(len(eugenValorsClean))]

zerros = np.zeros((4, 1))
matrix1DClean = np.hstack(
    (eugenParesClean[0][1].reshape(4, 1), zerros.reshape(4, 1)))

vClean = cleanData.dot(matrix1DClean)
lab4 = (0, 1, 2)
color4 = ('green', 'red', 'brown')
with plt.style.context('seaborn-whitegrid'):
    plt.figure(figsize=(6, 4))
    for lab4, color4, in zip(lab4, color4):
        plt.scatter(vClean[catClean == lab4, 0],
                    vClean[catClean == lab4, 1], label=lab4, c=color4, s=10)
    plt.xlabel('componente 1')
    plt.ylabel('componente 2')
    plt.legend(loc='lower center')
    plt.tight_layout()
    plt.show()
"""
respuesta de la pregunta n7.
7. What do you observe at the three plots? How many components are actually necessary?

En la grafica de 3d obsebamos que los datos se encuentran bastantes dispersos entre si,
observaamos que un grupo de datos  esta mas apartado de otros
en la grafica dos 2 esa separacion desaparece, al solo tener 2 ejes, se pierde esa separacion que se observamos en
la grafica en 3D, En el la grafica 1D Solo observamos una linea representando todos los conjuntos de datos, perdiendo
demasiada informacion util para poder diferenciar los datos.
consideramos que los componentes mas necesarios serian el pc1 y e pc2.

"""


# -*- coding: utf-8 -*-
# --------------------------------------------------------------------------------- #
# Software de Observaciones Sintéticas S.O.S.
# Line functions
#
# Marcial Becerril, @ 25 August 2020
# Latest Revision: 25 Aug 2020, 21.33 GMT
#
# For all kind of problems, requests of enhancements and bug reports, please
# write to me at:
#
# mbecerrilt92@gmail.com
# mbecerrilt@inaoep.mx
#
# --------------------------------------------------------------------------------- #

import numpy as np

def gaussian(x, A, mu, sigma):
    """
        Gaussian function
        Parameters
        ----------
        x : int/float/array
        A : float
            Amplitude
        mu : float
            Mean
        sigma : float
            Dispersion
        y0 : float
            Offset
        ----------
    """
    return A*np.exp(-((x-mu)**2)/(2*sigma**2))

def lorentzian(x, A, mu, w):
    """
        Gaussian function
        Parameters
        ----------
        x : int/float/array
        A : float
            Amplitude
        mu : float
            Mean
        w : float
            Width
        y0 : float
            Offset
        ----------
    """
    w = np.abs(w)
    return A*(w/(4*(x-mu)**2 + w**2))


'''
A robot is located at the top-left corner of a m x n grid
(marked 'Start' in the diagram below).

The robot can only move either down or right at any point
in time. The robot is trying to reach the bottom-right
corner of the grid (marked 'Finish' in the diagram below).

Now consider if some obstacles are added to the grids.
How many unique paths would there be?

An obstacle and empty space is marked as 1 and 0 respectively in the grid.

Note: m and n will be at most 100.

Example 1:

Input:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right
'''
import numpy as np

class Solution(object):
    def uniquePathsWithObstacles(self, obstacleGrid):
        """
        :type obstacleGrid: List[List[int]]
        :rtype: int
        """
        n = len(obstacleGrid)
        if n == 0:
            return 0
        m = len(obstacleGrid[0])
        grid = np.array([[0 for i in range(m)] for j in range(n)])
        for i in range(m):
            if obstacleGrid[0][i] == 1:
                break
            grid[0, i] = 1
        for j in range(n):
            if obstacleGrid[j][0] == 1:
                break
            grid[j, 0] = 1
        for i in range(1, m):
            for j in range(1, n):
                if obstacleGrid[j][i] == 1:
                    grid[j, i] = 0
                else:
                    grid[j, i] = grid[j-1, i] + grid[j, i-1]
        print(grid)
        return grid[n-1, m-1]

sol = Solution()
obstacleGrid = [[0,0,0],
                [0,1,0],
                [0,0,0]
                        ]
print(sol.uniquePathsWithObstacles(obstacleGrid))


import numpy as np
import math
a = [0,1,2,3,4,5,6,7,8,9]
b = [1,3,2,5,7,8,8,9,10,12]

#Changing lists to numpy arrays
x = np.array(a)
y = np.array(b)

#Finding b0 and b1
mean_x = np.mean(x)
mean_y = np.mean(y)
SS_xy = np.sum(y*x) - 10*mean_y*mean_x
SS_xx = np.sum(x*x) - 10*mean_x*mean_x

b_1 = SS_xy / SS_xx
b_0 = mean_y - b_1*mean_x
print("\nRegression Coefficients: b0 = ",b_0," b1 = ",b_1)

#Finding SSR,SST,SSE and R squared
for i in range(10):
    y_pred = b_0 + b_1*x
SSR = 0
SSE = 0
for i in range(10):
    SSR = SSR + math.pow((y_pred[i]-mean_y),2)
    SSE = SSE + math.pow((y[i]-y_pred[i]),2)
print("\nSSR = ",SSR)
print("\nSSE = ",SSE)
SST = SSR +SSE
R_squared = SSR/SST
print("\nR squared = ",R_squared)

#Gradient decent full batch

print("\nGRADEIENT DECENT")
bb0=0
bb1=0
a=0.001
n=10
y_exp=np.zeros(n)
for j in range(10000):
    for i in range(n):
        y_exp[i]=b_0+b_1*x[i]
        bb0=bb0+a*(y[i]-y_exp[i])
        bb1=bb1+a*(y[i]-y_exp[i])*x[i]
print("\nB0=",bb0)
print("B1=",bb1)

#Gradient decent Stochastic
print("\nGRADEIENT DECENT Stochastic")
bb0=0
bb1=0
a=0.001
y_exp=np.zeros(n)
b=[0,0]
se=10
while(se>1):
    for i in range(n):
        y_exp[i]=b_0+b_1*x[i]
        bb0=bb0+a*(y[i]-y_exp[i])
        bb1=bb1+a*(y[i]-y_exp[i])*x[i]
        b[0]=bb0
        b[1]=bb1
    e=y-y_exp
    se=np.sum((e**2))*(1/n)
print("\nSquared error=",se)
print("B0=",bb0)
print("B1=",bb1)


# Bai toan: Dua vao chieu cao de du doan can nang
#   f(x) = w1.x1 + w2.x2 + w3.x3 + w0
#       w1, w2, w3: const, w0: bias
#       y ~ f(x): quan he tuyen tinh --> Linear Regression --> Toi uu: {w1, w2, w3, w0}
#       y: Gia tri thuc te
#       f(x): Gia tri du doan (predict)

# CT:   w = inverse(A). b
#   Voi:    A = Tranpose(X) . X
#           b = Tranpose(X) . y
#           w = A^-1 . b
# Trong bai nay thi:    (weight) = w_1*(height) + w_0
# Ta can tim` w_1 va w_0

import numpy as np
import matplotlib.pyplot as plt

# height (cm)
X = np.array([[147, 150, 153, 158, 163, 165, 168, 170, 173, 175, 178, 180, 183]]).T

# weight (kg)
y = np.array([[ 49, 50, 51,  54, 58, 59, 60, 62, 63, 64, 66, 67, 68]]).T

# Building Xbar
one = np.ones((X.shape[0], 1))
Xbar = np.concatenate((one, X), axis = 1)
print Xbar

# Calculating weights of the fitting line
A = np.dot(Xbar.T, Xbar)
print A
b = np.dot(Xbar.T, y)
print np.linalg.pinv(A)
w = np.dot(np.linalg.pinv(A), b)

print('w = ', w)

# Preparing the fitting line
w_0 = w[0][0]
w_1 = w[1][0]
x0 = np.linspace(145, 185, 2)
y0 = w_0 + w_1*x0

# Du doan (2 du~ lieu 155, 160 ta chua dua vao mo hinh train)
y1 = w_1*155 + w_0
y2 = w_1*160 + w_0

print( u'Predict weight of person with height 155 cm: %.2f (kg), real number: 52 (kg)'  %(y1) )
print( u'Predict weight of person with height 160 cm: %.2f (kg), real number: 56 (kg)'  %(y2) )

# Drawing the fitting line
plt.plot(X.T, y.T, 'ro')     # data
plt.plot(x0, y0)               # the fitting line
plt.axis([140, 190, 45, 75])
plt.xlabel('Height (cm)')
plt.ylabel('Weight (kg)')
plt.show()


import numpy as np
import copy
from tqdm import trange
def burgers_timestep(x:np.ndarray,y:np.ndarray,u:np.ndarray,v:np.ndarray,nt:int,dt:float=0.001,nu:float=0.1):
    """Solving burgers equation using finite difference with euler time step (1st order approximation)

    Args:
        x (np.ndarray): domain x direction
        y (np.ndarray): domain y direction
        u (np.ndarray): matrix describing x-velocity field in i,j space
        v (np.ndarray): matrix describing y-velocity field in i,j space
        nt (int): number of time advances
        dt (float): time step to use for first order approximation
        nu (float, Optional): Viscosity. Defaults to 0.1

    Returns:
        (tuple): containing

            **u_history** (np.ndarray): history for u velocity
            **v_history** (np.ndarray): history for v velocity
    """

    dx = x[0,2]-x[0,1] # We can do it this way because x and y are initialized using linspace which guarantees constant spacing
    dy = y[1,0]-y[0,0]

    u_history = list()
    v_history = list()
    u_history.append(copy.deepcopy(u)) # set equal to initial value
    v_history.append(copy.deepcopy(v))
    for n in trange(nt):
        un = u.copy()   # previous value
        vn = v.copy()
        for i in range(1,x.shape[0]-1):
            for j in range(1,y.shape[1]-1):
                # Uses backward difference in space to solve first order derivative
                # Central differencing for second order derivative
                u[i,j] = (un[i, j] -(un[i, j] * dt / dx * (un[i, j] - un[i-1, j])) -vn[i, j] * dt / dy * (un[i, j] - un[i, j-1])) + (nu*dt/(dx**2))*(un[i+1,j]-2*un[i,j]+un[i-1,j])+(nu*dt/(dx**2))*(un[i,j-1]-2*un[i,j]+un[i,j+1])
                v[i,j] = (vn[i, j] -(un[i, j] * dt / dx * (vn[i, j] - vn[i-1, j]))-vn[i, j] * dt / dy * (vn[i, j] - vn[i, j-1])) + (nu*dt/(dx**2))*(vn[i+1,j]-2*vn[i,j]+vn[i-1,j])+(nu*dt/(dx**2))*(vn[i,j-1]-2*vn[i,j]+vn[i,j+1])

        u[:,0] = 1       # At all i values when j = 0
        u[:,-1] = 1      # At all i values when j = jmax
        u[0,:] = 1       # At all j values and i = 0
        u[-1,:] = 1     # At all j values and i = imax

        v[:,0] = 1
        v[:,-1] = 1
        v[0,:] = 1
        v[-1,:] = 1

        u_history.append(copy.deepcopy(u))
        v_history.append(copy.deepcopy(v))
    return u_history, v_history


import numpy as np
from layers.activation_layer import *
from layers.gradient_check import *

def mean_square_error_loss(y_hat, y):
    """
    MSE loss, loss=mean(y_hat-y)^2
    :param y_hat: output of the network
    :param y: input labels
    :return: MSE loss
    """
    loss = np.mean((y_hat - y) ** 2)
    num_output = y.shape[1]
    d_loss = 2 * (y_hat - y) / num_output
    return loss, d_loss

def cross_entropy_loss(y_hat, y):
    """
    Cross entropy loss, loss = -sum(yi * log(y_hat))
    :param y_hat: output of the network
    :param y: input labels (one_hot)
    :return: cross entropy loss
    """
    loss = -np.sum(y * np.log(y_hat), axis=1)
    # loss = np.mean(loss, axis=0)
    d_loss = -y / y_hat
    return loss, d_loss

def softmax_loss(x, y):
    shifted_logits = x - np.max(x, axis=1, keepdims=True)
    Z = np.sum(np.exp(shifted_logits), axis=1, keepdims=True)
    log_probs = shifted_logits - np.log(Z)
    probs = np.exp(log_probs)
    N = x.shape[0]
    loss = -np.sum(log_probs[np.arange(N), y]) / N
    dx = probs.copy()
    dx[np.arange(N), y] -= 1
    dx /= N
    return loss, dx

if __name__ == '__main__':
    np.random.seed(231)
    num_classes, num_inputs = 10, 50
    x = 0.001 * np.random.randn(num_inputs, num_classes)
    y = np.random.randint(num_classes, size=num_inputs)

    dx_num = eval_numerical_gradient(lambda x: softmax_loss(x, y)[0], x, verbose=False)
    loss, dx = softmax_loss(x, y)

    # Test softmax_loss function. Loss should be 2.3 and dx error should be 1e-8
    print('\nTesting softmax_loss:')
    print('loss: ', loss)
    print('dx error: ', rel_error(dx_num, dx))


"""
This is an example showing how to use the mgd2d solver.
A 4th order accurate solution is obtained with the 5pt stencil,
by using deferred correction.

"""
import numpy as np
import time
from mgd2d import FMG,V_cycle
#analytical solution
def Uann(x,y,n):
  return np.sin(2*n*np.pi*x)*np.sin(2*n*np.pi*y)

#RHS corresponding to above
def source(x,y,n):
  return -8 * (np.pi)**2 * n**2 * np.sin(2*n*np.pi*x) * np.sin(2*n*np.pi*y)

#input

#input
#FMG is a direct solver. tolerance and iterations are not used

#nv          = 1 # nv : Number of V-cycles within FMG. nv=1 will give solution a to within discretization error.
                 # Increase this to get a higher accuracy solution (upto roundoff limit)
                 # of the discrete problem.(residual on the fine grid =round off limit)
                 # Here I am using nv=2 for the first solve and nv=6 for the second solve.

nlevels    = 7    #total number of grid levels. 1 means no multigrid, 2 means one coarse grid. etc

# Number of points is based on the number of multigrid levels as
# N=A*2**(num_levels-1) where A is an integer >=4. Smaller A is better
# This is a cell centered discretization
NX         = 4*2**(nlevels-1)
NY         = 4*2**(nlevels-1)

#the grid has one layer of ghost cells to help apply the boundary conditions
uann=np.zeros([NX+2,NY+2])#analytical solution
u   =np.zeros([NX+2,NY+2])#approximation
f   =np.zeros([NX+2,NY+2])#RHS

#for deferred correction
uxx   = np.zeros_like(u)
corr  = np.zeros_like(u)

#calcualte the RHS and exact solution
DX=1.0/NX
DY=1.0/NY

n=1 # number of waves in the solution
xc=np.linspace(0.5*DX,1-0.5*DX,NX)
yc=np.linspace(0.5*DY,1-0.5*DY,NY)
XX,YY=np.meshgrid(xc,yc,indexing='ij')

uann[1:NX+1,1:NY+1]=Uann(XX,YY,n)
f[1:NX+1,1:NY+1]=source(XX,YY,n)

print('mgd2d.py : Two Dimensional geometric multigrid solver')
print('NX:',NX,', NY:',NY,', levels: ',nlevels)
#start solving
tb=time.time()

u,res=FMG(NX,NY,nlevels,f,2)

error=np.abs(uann[1:NX+1,1:NY+1]-u[1:NX+1,1:NY+1])
print(' 2nd Order::L_inf (true error): ',np.max(np.max(error)))
print(' Elapsed time: ',time.time()-tb,' seconds')

print('Improving approximation using deferred correction')

#deferred correction
#refer Leveque, p63
Ax=1.0/DX**2
Ay=1.0/DY**2

for i in range(1,NX+1):
  for j in range(1,NY+1):
    uxx[i,j]=(u[i+1,j]+u[i-1,j] - 2*u[i,j])/DX**2

# we should be using one-sided difference formulae for values
# near the boundary. For simplicity I am just applying the
# condition known from the analytical form for these terms.

uxx[ 0,:] = -uxx[ 1,:]
uxx[-1,:] = -uxx[-2,:]
uxx[:, 0] = -uxx[:, 1]
uxx[:,-1] = -uxx[:,-2]

f[ 0,:] = -f[ 1,:]
f[-1,:] = -f[-2,:]
f[:, 0] = -f[:, 1]
f[:,-1] = -f[:,-2]

#correction term
#  del2(f)-2*uxxyy
for i in range(1,NX+1):
  for j in range(1,NY+1):
    corr[i,j]=(Ax*(f[i+1,j]+f[i-1,j])+Ay*(f[i,j+1]+f[i,j-1])-2.0*(Ax+Ay)*f[i,j])-2*(uxx[i,j+1]+uxx[i,j-1] - 2*uxx[i,j])/DY**2

#adjust the RHS to cancel the leading order terms
for i in range(1,NX+1):
  for j in range(1,NY+1):
    f[i,j]+= 1.0/12*DX**2*(corr[i,j])

##solve once again with the new RHS
u,res=FMG(NX,NY,nlevels,f,5)

tf=time.time()
error=np.abs(uann[1:NX+1,1:NY+1]-u[1:NX+1,1:NY+1])
print(' 4nd Order::L_inf (true error): ',np.max(np.max(error)))
print('Elapsed time: ',tf-tb,' seconds')


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jun 21 14:05:55 2018

@author: top40ub
"""

"""
Navigation in 3D is a non-trivial task. Due to the anisotropic definition of spherical coordinate
systems, rotations and movement along isolines or fixed points are tricky. A way to do this a bit
easier is to use an old concept from particle physics called quaternions. To be true, their
mathematical formulation exceeds those of vectors in terms of age. They are common standard
in computer vision.
"""

import numpy as np

"""
Function name : vec_to_quat()
***Description***

Transformation of 3d vectors to 4 dimensional quaternions

***I/O***
Input parameter:
	a) np.array([x,y,z])
Output:
	a) np.array([0,x,y,z])

Inline output: Raises an Error raise Exception('Error the vector is not a 3D vector [x,y,z]') if vector is not of shape (3,)
Plot output:
Save file:
"""
def vec_to_quat(vec):
    if vec.shape[0] == 3:
        quat = np.append([0], vec)
        return quat

    else:
        raise Exception('Error the vector is not a 3D vector [x,y,z]')

"""
Function name : quat_conjug()
***Description***

Calculate the conjugate quaternion of given quaternion

***I/O***
Input parameter:
	a) np.array([w,x,y,z])
Output:
	a) np.array([w,-x,-y,-z])

Inline output: Raises an Error raise Exception('Error the quaternion is not a 4D [w,x,y,z]') if quaternion is not of shape (4,)
Plot output:
Save file:
"""
def quat_conjug(quat):
    if quat.shape[0] == 4:
        w, x, y, z = quat
        quatconjung = np.array([w, -x, -y, -z])
        return quatconjung
    else:
        raise Exception('Error the quaternion is not a 4D [w,x,y,z]')

"""
Function name : vec_normal()
***Description***

Normalisation of given vector within a certain tolerance

***I/O***
Input parameter:
	a) np.array([x,y,z])

Output:
	a) np.array([x,y,z]) with x² + y² + z² = 1

Inline output: Raises an Error Exception('Error the vector is not a 3D vector [x,y,z]') if vector is not of shape (3,)
Plot output:
Save file:
"""
def vec_normal(vec, tolerance=0.00001):
    if vec.shape[0] == 3:
    	vec_len_quad = np.sum([n * n for n in vec])
    	if abs(vec_len_quad - 1.0) > tolerance:
        	vec_len = np.sqrt(vec_len_quad)
        	vec = vec/vec_len
    	return vec
    else:
        raise Exception('Error the vector is not a 3D vector [x,y,z]')

"""
Function name : quat_mult()
***Description***

This function defins how to quaternions multiplicate. The multiplication core
can be interpreted as a kind of rotation. For deeper information read the
coresponding wikipedia page or one of the plenty graphical visualition books
about computer vision like I' ve done :)

***I/O***
Input parameter:
	a) np.array([w1,x1,y1,z1]), np,array([w2,x2,y2,z2])
Output:
	a) np.array([w_mult,x_mult,y_mult,z_mult])

Inline output: Raises an error Exception('Error one quaternion is not a 4D [w,x,y,z]') if quaternion is not of shape (4,)
Plot output:
Save file:
"""
def quat_mult(quat1, quat2):
    if quat1.shape[0] == 4 and quat2.shape[0] ==4:
        w1, x1, y1, z1 = quat1
        w2, x2, y2, z2 = quat2
        w_mult = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
        x_mult = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
        y_mult = w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2
        z_mult = w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2
        quat_mult = np.array([w_mult, x_mult, y_mult, z_mult])
        return quat_mult
    else:
        raise Exception('Error one quaternion is not a 4D [w,x,y,z]')

"""
Function name : quat_vec_mult()
***Description***

Quaternionic multiplication between quaternion and vector. The vector
is transformed. A second quaternionc multiplication with the conjugate of given
quaternion returns the three vector compoments [1:] of the operation.

***I/O***
Input parameter:
	a) np.array([x1,y1,z1]), np,array([w2,x2,y2,z2])
Output:
	a) np.array([w_mult,x_mult,y_mult,z_mult])

Inline output: Raises an error Exception('Error the quaternion is not a 4D [w,x,y,z] or the vector is not 3D [x,y,z]') if quaternion or vector are not of shape (4,) or (3,)
Plot output:
Save file:
"""
def quat_vec_mult(vec1, quat1):
    if vec1.shape[0] == 3 and quat1.shape[0] ==4:
    	quat2 = vec_to_quat(vec1)
    	return quat_mult(quat_mult(quat1, quat2), quat_conjug(quat1))[1:]
    else:
        raise Exception('Error the quaternion is not a 4D [w,x,y,z] or the vector is not 3D [x,y,z]')

"""
Function name : axisangle_to_quat()
***Description***

Calcuation to transform a unit vector or any vector that will be normalsied
into a quaternion that elements are object to a rotation around half theta.
!Theta needs to be of type degree!

***I/O***
Input parameter:
	a) np.array([x,y,z]), np.array([theta])

Output:
	a) np.array([w_a,x_a,y_a,z_a])

Inline output: Raises an error Exception('Error the vector is not 3D [x,y,z] or the angle 1D [theta]') if vector or angle are not of shape(3,) or (1,)
Plot output:
Save file:
"""
def axisangle_to_quat(vec, theta):
    if vec.shape[0] == 3 and theta.shape[0] == 1:
        vec = vec_normal(vec)
        x, y, z = vec
        theta = theta[0]/2
        theta = np.deg2rad(theta)
        w_a = np.cos(theta)
        x_a = x * np.sin(theta)
        y_a = y * np.sin(theta)
        z_a = z * np.sin(theta)
        quat_a = np.array([w_a, x_a, y_a, z_a])
        return quat_a
    else:
        raise Exception('Error the vector is not 3D [x,y,z] or the angle 1D [theta]')

"""
Function name : quat_to_axisangle()
***Description***

The opposite function of axisangle_to_quat, it returns the axisangle the
corresponding unit vector of a given quaternion.

***I/O***
Input parameter:
	a) np.array([w,x,y,z])

Output:
	a) np.array([x_a,y_a,z_a]), np.arra([theta])

Inline output: Raises an error Exception('Error the quaternion is not a 4D [w,x,y,z]') if quaternion is not of shape (4,)
Plot output:
Save file:
"""
def quat_to_axisangle(quat):
   if quat.shape[0] == 4:
    	w, vec = quat[0], quat[1:]
    	theta = np.rad2deg(np.arccos(w) * 2.0)
    	return vec_normal(vec), theta

   else:
       raise Exception('Error the quaternion is not a 4D [w,x,y,z]')

"""
Function name :vec_angle_rot()
***Description***

Main function for the quaternionic rotation of 3D vectors:
A vector (vec) is rotated around a second vector a_unit with angle theta.
a_unit is a axisvector but mostly a basis vector of given cartesic coordinate
system. In case of a cartesic basis vector we have the rotation around
coordinate axsis.

***I/O***
Input parameter:
	a) np.array([x,y,z]), np.array([theta]),
        np.array(x2,y2,z2) with x2² + y2² + z2² = 1

Output:
	a) np.array([x1_rot,y1_rot,z1_rot])

Inline output: Raises an error Exception('Error one vector is not 3D [x,y,z] or the angle 1D [theta]') if vector or angle are not of shape (3,) or (1,)
Plot output:
Save file:
"""
def vec_angle_rot(vec,theta,a_unit):
    if vec.shape[0] == 3 and theta.shape[0] == 1 and vec.shape[0] == 3:
    	quat1 = vec_to_quat(vec)
    	quat_a = axisangle_to_quat(a_unit,theta)
    	v_rot = quat_mult(quat_mult(quat_a,quat1),quat_conjug(quat_a))[1:]
    	return v_rot
    else:
        raise Exception('Error one vector is not 3D [x,y,z] or the angle 1D [theta]')

if __name__=='__main__':
	pass


"""
.. module:: utils
    :synopsis: Provides routines of interest to different ML models.

.. moduleauthor:: Benardi Nunes <benardinunes@gmail.com>
"""

from numpy import zeros, copy, std, mean, float64, exp

# sigmoid gradient function
def g(x):
    """This function applies the sigmoid function on a given value.

    :param x: Input value or object containing value .
    :type x: obj

    :returns: Sigmoid function at value.
    :rtype: obj
    """
    return 1 / (1 + exp(-x))

# sigmoid gradient function
def g_grad(x):
    """This function calculates the sigmoid gradient at a given value.

    :param x: Input value or object containing value .
    :type x: obj

    :returns: Sigmoid gradient at value.
    :rtype: obj
    """
    return g(x) * (1 - g(x))

def gradient_descent(X, y, grad, initial_theta,
                     alpha, num_iters, _lambda=None):
    """This function performs parameter optimization via gradient descent.

    :param X: Features' dataset plus bias column.
    :type X: numpy.array

    :param y: Column vector of expected values.
    :type y: numpy.array

    :param grad: Routine that generates the partial derivatives given theta.
    :type grad: numpy.array

    :param initial_theta: Initial value for parameters to be optimized.
    :type initial_theta: numpy.array

    :param alpha: Learning rate or step size of the optimization.
    :type alpha: float

    :param num_iters: Number of times the optimization will be performed.
    :type num_iters: int

    :param _lambda: Weight of the penalty term.
    :type _lambda: float

    :returns: Optimized model parameters.
    :rtype: numpy.array
    """
    if _lambda is not None:
        theta = copy(initial_theta)

        for _ in range(num_iters):
            theta = theta - alpha * grad(theta, X, y, _lambda)

    else:
        theta = copy(initial_theta)
        for _ in range(num_iters):
            theta = theta - alpha * grad(theta, X, y)

    return theta

def numerical_grad(J, theta, err):
    """Numerically calculates the gradient of a given cost function.

    :param J: Function handle that computes cost given theta.
    :type J: function

    :param theta: Model parameters.
    :type theta: numpy.array

    :param err: distance between points where J is evaluated.
    :type err: float

    :returns: Computed numeric gradient.
    :rtype: numpy.array
    """
    num_grad = zeros(theta.shape, dtype=float64)
    perturb = zeros(theta.shape, dtype=float64)

    for i in range(len(theta)):
        perturb[i] = err
        loss1 = J(theta - perturb)
        loss2 = J(theta + perturb)
        num_grad[i] = (loss2 - loss1) / (2 * err)
        perturb[i] = 0

    return num_grad

def feature_normalize(X):
    """Performs Z score normalization in a numeric dataset.

    :param X: Features' dataset plus bias column.
    :type X: numpy.array

    :returns:
        - X_norm - Normalized features' dataset.
        - mu - Mean of each feature
        - sigma - Standard deviation of each feature.

    :rtype:
        - X_norm (:py:class: numpy.array)
        - mu (:py:class: numpy.array)
        - sigma (:py:class: numpy.array)
    """
    mu = mean(X)
    sigma = std(X)

    X_norm = (X - mu) / sigma

    return X_norm, mu, sigma


# Chapter 3 Decision tree learning

# Maximizing information gain - getting the most bang for the buck
''' Plot the impurity indices for the probability range [0,1] for class 1.
Note that we will also add in a scaled version of the entropy (entropy/2)
to observe that the Gini impurity is an intermediate measure  between
entropy and the classification error. '''

import matplotlib.pyplot as plt
import numpy as np

def gini(p):
    return (p) * (1 - (p)) + (1 - p) * (1 - (1-p))

def entropy(p):
    return - p*np.log2(p) - (1 - p)* np.log2((1 - p))

def error(p):
    return 1 - np.max([p, 1 - p])

x = np.arange(0.0, 1.0, 0.01)
ent = [entropy(p) if p != 0 else None for p in x]
sc_ent = [e*0.5 if e else None for e in ent]
err = [error(i) for i in x]
fig = plt.figure()
ax = plt.subplot(111)
for i, lab, ls, c, in zip([ent, sc_ent, gini(x), err], ['Entropy', 'Entropy (scaled)', 'Gini Impurity', 'Misclassification Error'],
['-', '-', '--', '-.'], ['black', 'lightgray', 'red', 'green', 'cyan']):
   line = ax.plot(x, i, label=lab, linestyle=ls, lw=2, color=c)

ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.15), ncol=3, fancybox=True, shadow=False)
ax.axhline(y=0.5, linewidth=1, color='k', linestyle='--')
ax.axhline(y=1.0, linewidth=1, color='k', linestyle='--')
plt.ylim([0, 1.1])
plt.xlabel('p(i=1)')
plt.ylabel('Impurity Index')
plt.show()

# Building a decision tree
'''
Decision trees can build complex decision boundaries by dividing the feature
space into rectangles. Using scikit-learn, we will now train a decision tree with
a maximum depth of 3 using entropy as a criterion for impurity.  Although feature
scaling may be desired for visualization purposes, note that feature scaling is
not a requirement for decision tree algorithms.
 '''
# Train a  model to classify the different flowers in our Iris dataset
from sklearn import datasets
import numpy as np

iris = datasets.load_iris()
X = iris.data[:, [2, 3]]
y = iris.target

from sklearn.cross_validation import train_test_split

# random_state : int or RandomState
# Pseudo-random number generator state used for random sampling.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt

def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):
    # setup marker generator and color map
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])
    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                           np.arange(x2_min, x2_max, resolution))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())
    # plot all samples
    X_test, y_test = X[test_idx, :], y[test_idx]
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], alpha=0.8, c=cmap(idx), marker=markers[idx], label=cl)

    # highlight test samples
    if test_idx:
        X_test, y_test = X[test_idx, :], y[test_idx]
        plt.scatter(X_test[:, 0], X_test[:, 1], c='', alpha=1.0, linewidth=1, marker='o', s=55, label='test set')

from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(criterion='entropy',max_depth=3, random_state=0)
tree.fit(X_train, y_train)
X_combined = np.vstack((X_train, X_train))
y_combined = np.hstack((y_train, y_test))
plot_decision_regions(X_combined, y_combined, classifier=tree, test_idx=range(105,150))
plt.xlabe('petal length [cm]')
plt.ylabel('petal width [cm]')
plt.legend(loc='upper left')
plt.show()

'''
A nice feature in scikit-learn is that is allows us to export the decision tree as a
.dot file after training, which we can visualize using the GraphViz program.
First, we create the .dot file via scikit-learn using the export_graphviz function
from the tree submodule, as follows:
'''
from sklearn.tree import export_graphviz
export_graphviz(tree, out_file='C:\Users\Wei\Desktop/tree.dot', feature_names=['petal length', 'petal width'])

'''
After installed GraphViz on the computer, we can convert the tree.dot file into a PNG file
by executing the following command from the command line in the location where we
saved the tree.dot file:

> dot -Tpng tree.dot -o tree.png
'''

self_practice/Chapter 3 Decision Tree.py


# -*- coding: utf-8 -*-
"""
Created on Fri Apr 23 11:49:57 2021

@author: ahmed
"""

from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style

style.use('fivethirtyeight')

xs = np.array([1,2,3,4,5,6], dtype=np.float64)
ys = np.array([5,4,6,5,6,7], dtype=np.float64)

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

def squared_error(ys_orig, ys_line):
    return sum((ys_line-ys_orig)**2)

def coefficient_of_determination(ys_orig, ys_line):
    y_mean_line =[mean(ys_orig) for y in ys_orig]
    print('y_mean_line', y_mean_line)

    squared_error_regr = squared_error(ys_orig, ys_line)
    print('squared_error_regr', squared_error_regr)

    squared_error_y_mean = squared_error(ys_orig, y_mean_line)

    return 1 - (squared_error_regr / squared_error_y_mean)

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

predict_x = 7
predict_y = m*predict_x + b

r_squared =coefficient_of_determination(ys, regression_line)
print(r_squared)

plt.scatter(xs,ys)
plt.scatter(predict_x,predict_y, color='r')
plt.plot(xs,regression_line)
plt.show()

Machine learning/sentdex course/Machine learning/Regression/ML8-slopecalculation.py


"""ЛР 1.5, Ларькин Владимир, М8О-303Б-18"""

import numpy as np
import fire  # CLI
import matplotlib.pyplot as plt

from utilities import parse_matrix  # парсинг матрицы из файла

def householder(a, sz, k):
    v = np.zeros(sz)
    v[k] = a[k] + np.sign(a[k]) * np.linalg.norm(a[k:])
    for i in range(k + 1, sz):
        v[i] = a[i]
    v = v[:, np.newaxis]
    H = np.eye(sz) - (2 / (v.T @ v)) * (v @ v.T)
    return H

def get_QR(A):
    sz = len(A)
    Q = np.identity(sz)
    A_i = np.copy(A)

    for i in range(sz - 1):
        col = A_i[:, i]
        H = householder(col, len(A_i), i)
        Q = Q @ H
        A_i = H @ A_i

    return Q, A_i

def get_roots(A, i):
    sz = A.shape[0]
    a11 = A[i, i]
    a12 = A[i, i + 1] if i + 1 < sz else 0
    a21 = A[i + 1, i] if i + 1 < sz else 0
    a22 = A[i + 1, i + 1] if i + 1 < sz else 0
    return np.roots((1, -a11 - a22, a11 * a22 - a12 * a21))

def finish_iter_for_complex(A, eps, i):
    Q, R = get_QR(A)
    A_next = R @ Q
    lambda1 = get_roots(A, i)
    lambda2 = get_roots(A_next, i)
    return True if abs(lambda1[0] - lambda2[0]) <= eps and abs(lambda1[1] - lambda2[1]) <= eps else False

def get_eigenvalue(A, eps, i):
    A_i = np.copy(A)
    while True:
        Q, R = get_QR(A_i)
        A_i = R @ Q
        a = np.copy(A_i)
        if np.linalg.norm(a[i + 1:, i]) <= eps:
            res = (a[i][i], False, A_i)
            break
        elif np.linalg.norm(a[i + 2:, i]) <= eps and finish_iter_for_complex(A_i, eps, i):
            res = (get_roots(A_i, i), True, A_i)
            break
    return res

def QR_method(A, eps):
    res = []
    i = 0
    A_i = np.copy(A)
    while i < A.shape[0]:
        eigenval = get_eigenvalue(A_i, eps, i)
        if eigenval[1]:
            res += [*eigenval[0]]
            i += 2
        else:
            res.append(eigenval[0])
            i += 1
        A_i = eigenval[2]
    return np.array(res), i

def main(src, test=False, eps=0.01):
    """Нахождение собственных значений матрицы методом QR-разложения

    :param src: путь к текстовому файлу с матрицей
    :param test: флаг, запускающий тестирование
    :param eps: точность вычисления
    """

    np.random.seed(42)

    # чтение файла
    with open(src, "r") as file:
        s = file.readlines()

    matrix = parse_matrix(s)

    print(f"Матрица:\n{matrix}")
    print(f"\neps={eps}\n")

    tmp, count_iter = QR_method(matrix, eps)
    print("QR_method:\n", tmp)

    print("\nnp.linalg.eig:\n", np.linalg.eig(matrix)[0])

if __name__ == "__main__":
    fire.Fire(main)


# A manufacturer purchases a part for use at both of its plantsdashone at Roseville, California, the other at Akron, Ohio.
# The part is available in limited quantities from two suppliers.

# Each supplier has 85 units available.

# The Roseville plant needs 50 units, and the Akron plant requires 85 units.

# The first supplier charges $50 per unit delivered to Roseville and $80 per unit delivered to Akron.

# Corresponding costs from the second supplier are $90 and $110.

# The manufacturer wants to order a total of 85 units from the first, less expensive supplier, with the remaining 50 units to come from the second supplier.
# If the company spends $10,800 to purchase the required number of units for the two plants, find the number of units that should be sent from each supplier to each plant.

# Write a linear system of equations.

# The number of units sent from the first supplier to Roseville is w,
# the number of units sent from the first supplier to Akron is x,
# the number of units sent from the second supplier to Roseville is y,
# and the number of units sent from the second supplier to Akron is z.

# Choose the correct answer below.

from sympy import symbols, solve

w,x,y,z = symbols( 'w,x,y,z' )

# w + y = 50
expr1 = w + y - 50

# w + x = 85
expr2 = w + x - 85

# w + x + y + z = 135
expr3 = w + x + y + z - 135

# 50*w + 80*x + 90*y + 110*z
expr4 = 50*w + 80*x + 90*y + 110*z - 10800

solve( ( expr1, expr2, expr3, expr4 ), dict = True )


''' mbinary
#########################################################################
# File : numerical_integration.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-10-02  21:14
# Description:
#########################################################################
'''

#########################################################################
# File : numerical integration.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-05-11  08:58
# Description:
#       numerical intergration: using Newton-Cotes integration,  and Simpson
#       数值积分, 使用 牛顿-科特斯积分, 辛普森
#########################################################################

import numpy as np
def trapezoidal(a,b,h,fs):
    '''梯形积分公式'''
    xs = [i for i in np.arange(a,b+h,h)]
    print(xs)
    ret = h*(sum(fs)-fs[0]/2 - fs[-1]/2)
    print(ret)
    return ret

def simpson(a,b,h,fs):
    '''辛普森积分公式'''
    xs = [i for i in np.arange(a,b+h,h)]
    print(xs)
    ret = h/3*(4* sum(fs[1::2])+ 2*sum(fs[2:-1:2]) + fs[0]+fs[-1])
    print(ret)
    return ret

def romberg(a,b,f,epcilon):
    '''romberg(龙贝格) 数值积分'''
    h = b-a
    lst1=[h*(f(a)+f(b))/2]
    print(lst1)
    delta = epcilon
    k=1
    while delta >= epcilon:
        h/=2
        k+=1
        lst2=[]
        lst2.append((lst1[0]+h*2*sum(f(a+(2*i-1)*h) for i in range(1,2**(k-2)+1)))/2)
        for j in range(0,k-1):
            lst2.append(lst2[j]+(lst2[j]-lst1[j])/(4**(j+1)-1))
        delta = abs(lst2[-1]-lst1[-1])
        lst1=lst2
        print(lst1)

if __name__=='__main__':
    a,b,h = 0.6,1.8,0.2
    fs=[5.7,4.6,3.5,3.7,4.9,5.2,5.5]
    trapezoidal(a,b,h,fs)
    simpson(a,b,h,fs)
    romberg(1,2,lambda x:sin(x**4),1e-4)


import numpy as np

class Sigmoid():
    """
    Sigmoid activation function
    """
    def __call__(self, X: np.ndarray) -> np.ndarray:
        """
        Performs the sigmoid function on a given vector

        Parameters
        ----------
        X : array_like, shape {n_samples, n_features}
            Input vector

        Returns
        -------
        array_like, shape {n_samples, n_features}
            Input vector transformed
        """
        return 1 / (1 + np.exp(-X))

class Softmax():
    """
    Softmax activation function
    """
    def __call__(self, scores: np.ndarray) -> np.ndarray:
        """
        Performs the softmax function on a given vector

        Parameters
        ----------
        X : array_like, shape {n_samples, n_classes}
            Scores vector

        Returns
        -------
        array_like, shape {n_samples, n_classes}
            Scores vector transformed
        """
        exps = np.exp(scores)
        exps_sum = np.sum(exps, axis=1, keepdims=True)

        return exps / exps_sum


# Exercise 4.3.1
# Mortality rates in the middle ages

from scipy import optimize
from math import log
from math import exp

# Ages at time of death (given in lecture notes)
ages = [47, 67, 38, 45, 48, 48, 51, 76, 32, 43, 42, 26, 25, 40, 53, 64, 49, 42,
        57, 30, 1, 1, 6, 9, 12, 14, 18]

# Model ansatz
# mu(x) = Probability to die between age x and x+1,
#         conditional on being alive at age x.
def mu(x, a, b, c):
    return(exp(a + b * x + c * x ** 2))

# Negative log-likelihood
# Probability of dying at age x is the product of the probabilities of not dying
# during years before x, and the probability of dying between age x and x+1.
def negll(abc, ages):
    a, b, c = abc
    ll = 0
    for age in ages:
        for x in range(age):
            # not dying before age of death
            ll += log(1 - mu(x, a, b, c))
        # dying at age of death
        ll += a + b * age + c * age ** 2  # same as log(mu(x, a, b, c))
    return(-ll)

# Solving for three parameters in model ansatz
# Initial values are the solution given in lecture notes
print(optimize.minimize(negll, (-4.36, 1.01e-2, 4.08e-4), args=(ages,)))
# This gives [a, b, c] = [-4.48664309e+00, 1.52875034e-02, 3.38010681e-04],
# different from the given solution.


import numpy as cp

class Softmax():

    '''
    The softmax function is a generalization of the logistic function to multiple dimensions.
    It is used in multinomial logistic regression and is often used as the last activation
    function of a neural network to normalize the output of a network to a probability
    distribution over predicted output classes.

    The softmax function takes as input a vector z of K real numbers, and normalizes it into
    a probability distribution consisting of K probabilities proportional to the exponentials
    of the input numbers.

    The Softmax function is defined as follows:

        softmax(x) =  ___exp(xi)____ where exp is exponential function
                           ∑ exp(x)

    Parameters
    ----------
    x : cp.array
        Array of ouputs of deeplearning layer.

    Returns
    -------
    cp.array
        Array after Softmax function applied .

    '''

    def __call__(self, x: cp.array) -> cp.array:

        e_x = cp.exp(x)
        return e_x / cp.sum(e_x, axis=-1, keepdims=True)

    def gradient(self, x: cp.array) -> cp.array:
        '''

        Parameters
        ----------
        x : cp.array
             Array of ouputs of deeplearning layer..

        Returns
        -------
        cp.array
            Array after derivatives of Softmax applied .

        '''

        p = self.__call__(x)
        return p * (1 - p)

tutorial/7. Deep Learning/Activation Function/Softmax.py


import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns

"""
Some notations:
    d = number of features including W_0
    n = number of observations

"""

def prepare_X(X:np.ndarray, degree:int = 1)-> np.ndarray:
    '''
    Expands X as per degrees and appends a column of ones in the begining
    Input:
        X: (n*1) Input matrix
        degress: expanding X to number of degrees
    Returns:
        X_new : (n * d) matrix
    '''
    assert X.ndim == 2

    n = X.shape[0]

    X_new = X.copy()

    if degree>1:
        for d in range(2,degree+1):
            X_new = np.hstack((X_new, X**d))

    # append column of ones'
    X_new = np.hstack((np.ones([n,1]), X_new))

    return X_new

def normaliz_data(X):
    '''
    Z- normalized data and array of means and sds to normalize validation data
    Input:
        X: (n*d) matrix
    Returns:
        X_norm : n*d , z-normalized data
        mean = np array of means of all columns
        std = np array of std of all columns
    '''
    mean = X.mean(axis=0)
    std = X.std(axis=0)
    mean[0] = 0 # the first columns is column of ones.
    std[0] = 1 # the first columns is column of ones.

    X_norm = (X - mean)/ std

    return X_norm, mean, std

def w_closedForm(X,y):
    '''
    Finds the optimal w using closed form solution

    Input:
        X = 2D array, N*(D+1) data matrix
        y = 1D array, N lenghth vector of y values
    Output:
        w = 1D array, (D+1) lengthe vector
    '''

    w = np.dot(np.linalg.pinv(X),y)

    return w

def give_squared_loss_grad(X, y, w, overPred_penalty=1, underPred_penalty=1):
    '''
    Gives squared loss and grandient given X, w and other parameters
    Input:
        X = 2D array; n*d input matrix
        y = 1D array; (n,) output array
        w = 1D array; (), weights array
        overPred_penalty = [-Inf, Inf] penulty for over prediction
        underPred_penalty = [-Inf, Inf] penulty for under prediction
    Returns:
        loss: float = squared loss
        grad: (d*1) array of gradients
    '''

    n = X.shape[0]

    # errors
    e = np.dot(X,w) - y

    # Penalty for Over/ Under Prediction
    penulty_vect = (e>0.).astype(float)
    penulty_vect[penulty_vect==1] = overPred_penalty
    penulty_vect[penulty_vect==0] = underPred_penalty

    # Asymmetric Loss
    asym_e = np.multiply(penulty_vect, e)

    # Normalised Squared Loss
    loss = np.dot(np.transpose(asym_e), asym_e) /(2*n)

    # Gradient
    grad = (np.dot(X.T, asym_e)) / n

    return loss, grad

def GradDescent_LinReg(X, y, overPred_penalty=1, underPred_penalty=1, lr=0.1 , maxIt = 10000, verbose=False):

    '''
    Finds the optimal w using Gradient Descent method
    Input:
        X = 2D array; n*d input matrix
        y = 1D array; (n,) output array
        w = 1D array; (), weights array
        overPred_penalty = [-Inf, Inf] penulty for over prediction
        underPred_penalty = [-Inf, Inf] penulty for under prediction
        lr = learing rate
        maxIt = Maximum Iterations
    Returns:
        w  = (d*1) array of weights
    '''
    n,d = X.shape

    if verbose:
        itr_data = []

    # initialize W randomly
    w = np.random.rand(d,1)

    for i in range(maxIt):
        loss, grad = give_squared_loss_grad(X, y, w, overPred_penalty, underPred_penalty)

        if verbose:
            itr_data.append(loss[0][0])
        w = w - (lr*grad)

    if verbose:
        return itr_data, w

    return w

def find_best_model_plot_results(X_train, y_train, X_val, y_val,
                            method:str, overPred_penalty=1, underPred_penalty=1, lr=0.1 , maxIt = 10000 ):

    # checking for degrees till 5
    degrees = [i for i in range(1,6)]

    # storing the best model

    min_loss = np.Inf
    best_model = {}
    all_model = {}

    # for plotting
    fig = matplotlib.pyplot.gcf()
    fig.set_size_inches(11, 7)
    plt.plot(X_train, y_train, 'k.')

    x_axis_data = np.linspace(min(X_train)-0.1, max(X_train)+-.1, num=100)

    # below loop: for each polynomial degree finds the w*, finds the val loss and plots the fitted curve
    for d in degrees:
        X = prepare_X(X_train, degree=d)
        X_norm, X_mean, X_std = normaliz_data(X)
        if method == 'ClosedForm':
            w = w_closedForm(X_norm, y_train)
        if method == "GradientDescent":
            w = GradDescent_LinReg(X_norm, y_train, overPred_penalty, underPred_penalty, lr, maxIt)
        train_loss = give_squared_loss_grad(X_norm, y_train, w)[0]

        # for validation loss
        X_val_prep = prepare_X(X_val, degree=d)
        X_val_norm = (X_val_prep - X_mean) / X_std
        val_loss = give_squared_loss_grad(X_val_norm, y_val, w)[0]

        all_model['degree:'+str(d)] = {'train_loss':train_loss, 'val_loss':val_loss, 'w':w}

        if val_loss < min_loss:
            min_loss = val_loss
            best_model['degree'] = d
            best_model['w'] = w
            best_model['train_loss'] = train_loss
            best_model['val_loss'] = val_loss

            if method == "GradientDescent":
                best_model['OverPred Penalty'] = overPred_penalty
                best_model['UnderPred Penalty'] = underPred_penalty
                best_model['learning_rate'] = lr
                best_model['maxIt'] : maxIt

        # to plot the line
        X_graph = ((prepare_X(x_axis_data, degree=d))-X_mean) / X_std

        y_axis_data = np.dot(X_graph,w)

        color = {1:'b', 2:'g', 3:'r', 4:'y', 5:'c'}[d]
        plt.plot(x_axis_data, y_axis_data, color, label = "deg:"+str(d))

    plt.legend()

    return best_model


#inverse an matrix
import numpy as np
A = np.mat("2 4 6;4 2 6;10 -4 18")
print "A\n", A
inverse = np.linalg.inv(A)
print "inverse of A\n", inverse
print "Check\n", A * inverse
print "Error\n", A * inverse - np.eye(3)

#solve linear system with numpy
import numpy as np
A = np.mat("1 -2 1;0 2 -8;-4 5 9")
print "A\n", A
b = np.array([0, 8, -9])
print "b\n", b
x = np.linalg.solve(A, b)
print "Solution", x
print "Check\n", np.dot(A , x)

#find eigenvalue and eigenvectors
import numpy as np
A = np.mat("3 -2;1 0")
print "A\n", A
print "Eigenvalues", np.linalg.eigvals(A)
eigenvalues, eigenvectors = np.linalg.eig(A)
print "First tuple of eig", eigenvalues
print "Second tuple of eig\n", eigenvectors
for i in range(len(eigenvalues)):
    print "Left", np.dot(A, eigenvectors[:,i])
    print "Right", eigenvalues[i] * eigenvectors[:,i]
    print


import numpy as np
import matplotlib.pyplot as plt

class PolyFit:
    def __init__(self, degree=1):
        self.degree = degree
        self.betas = np.ones(degree+1)

    def fit(self, x, y):
        """
        Entradas:
        x : array unidimensional de valores de x.
        y : array unidimensiondal de valores de y.

        Este codigo resuelve el siguiente sistema de ecuaciones
        S * beta = Y, beta = S^{-1} * Y,
        donde S es una matriz que tiene como vectotes a columnas de potencias de X.
        """
        m = self.degree
    # matriz que guarda los vectores columna de potencias de x
        S = np.zeros([len(x), m+1])
        for i in range(m+1):
            S[:,i] = x**i

    # Calculo la inversa de S.
        S_inv = np.linalg.pinv(S)

    # calculo beta
        self.betas = np.dot(S_inv, y)

    def predict(self, x):
        """
        Entradas:
        x : array de entrada.

        Salida:
        y : array de salida
        """
        y = np.zeros(len(x))
        for i in range(len(self.betas)):
            y +=  self.betas[i] * x**i
        return y

    def score(self, x, y):
        """
        Calcula el root mean squared error

        Entradas:
        x : array de entrada de valores de x.
        y : array unidimensiondal de valores de y.
        """
        y_predict  = self.predict(x)
        return np.sqrt(np.mean((y_predict - y)**2))

# cargo los datos para manipular
data = np.loadtxt("numeros_20.txt")

# divido en training y test
x_train = data[:10,0]
x_test = data[10:,0]
y_train = data[:10,1]
y_test = data[10:,1]

# distintos valores de m para probar
m_values = [0,1,3,9]

plt.figure()

for i,m in enumerate(m_values):
    modelo = PolyFit(degree=m)
    modelo.fit(x_train, y_train)

    plt.subplot(2,2,i+1)
    #grafica del modelo
    x_model = np.linspace(x_train.min(), x_train.max(), 100)
    plt.plot(x_model, modelo.predict(x_model))
    #grafica de los puntos
    plt.scatter(x_train, y_train)
    plt.xlabel("X")
    plt.ylabel("Y")
    plt.title("M={}".format(m))

plt.subplots_adjust(hspace=0.5)
plt.savefig("polinomios.png", bbox_inches='tight')

#segunda figura con los errores
error_train = []
error_test = []

for m in range(10):
    modelo = PolyFit(degree=m)
    modelo.fit(x_train, y_train)
    error_train.append(modelo.score(x_train, y_train))
    error_test.append(modelo.score(x_test, y_test))

plt.figure()
plt.plot(np.array(range(10)), error_train, label='training')
plt.plot(np.array(range(10)), error_test, label='test')
plt.semilogy()
plt.legend()
plt.xlabel("M")
plt.ylabel("$E_{RMS}$")
plt.savefig("train_test_error.png")


''' mbinary
#########################################################################
# File : iteration.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-10-02  21:14
# Description:
#########################################################################
'''

import sympy
import numpy as np
from math import sqrt

def newton(y:sympy.core,x0:float,epsilon:float=0.00001,maxtime:int=50) ->(list,list):
    '''
        newton 's iteration method for finding a zeropoint of a func
        y is the func, x0 is the init x val: int float epsilon is the accurrency
    '''
    if epsilon <0:epsilon = -epsilon
    ct =0
    t =  y.free_symbols
    varsymbol = 'x' if len(t)==0 else t.pop()
    x0= float(x0)
    y_diff = y.diff()
    li = [x0]
    vals = []
    while 1:
        val = y.subs(varsymbol,x0)
        vals.append(val)
        x = x0- val/y_diff.subs(varsymbol,x0)
        li.append(x)
        ct+=1
        if ct>maxtime:
            print("after iteration for {} times, I still havn't reach the accurrency.\
                    Maybe this function havsn't zeropoint\n".format(ct))
            return li ,val
        if abs(x-x0)<epsilon:return li,vals
        x0 = x

def secant(y:sympy.core,x0:float,x1:float,epsilon:float =0.00001,maxtime:int=50) ->(list,list):
    '''
        弦截法, 使用newton 差商计算,每次只需计算一次f(x)
        secant method for finding a zeropoint of a func
        y is the func , x0 is the init x val,     epsilon is the accurrency
    '''
    if epsilon <0:epsilon = -epsilon
    ct =0
    x0,x1 = float(x0),float(x1)
    li = [x0,x1]
    t =  y.free_symbols
    varsymbol = 'x' if len(t)==0 else t.pop()
    last = y.subs(varsymbol,x0)
    vals = [last]
    while 1:
        cur = y.subs(varsymbol,x1)
        vals.append(cur)
        x = x1-cur*(x1-x0)/(cur-last)
        x0 ,x1= x1,x
        last = cur
        li.append(x)
        ct+=1
        if ct>maxtime:
            print("after iteration for {} times, I still havn't reach the accurrency.\
                    Maybe this function havsn't zeropoint\n".format(ct))
            return li,vals
        if abs(x0-x1)<epsilon:return li,vals
        x0 = x

def solveNonlinearEquations(funcs:[sympy.core],init_dic:dict,epsilon:float=0.001,maxtime:int=50)->dict:
    '''solve  nonlinear equations:'''
    li = list(init_dic.keys())
    delta = {i:0 for i in li}
    ct = 0
    while 1:
        ys = np.array([f.subs(init_dic) for f in funcs],dtype = 'float')
        mat = np.matrix([[i.diff(x).subs(init_dic) for x in li] for i in funcs ],dtype = 'float')
        delt = np.linalg.solve(mat,-ys)
        for i,j in enumerate(delt):
            init_dic[li[i]] +=j
            delta[li[i]] = j
        if ct>maxtime:
            print("after iteration for {} times, I still havn't reach the accurrency.\
                    Maybe this function havsn't zeropoint\n".format(ct))
            return init_dic
        if sqrt(sum(i**2 for i in delta.values()))<epsilon:return init_dic

if __name__ =='__main__':
    x,y,z = sympy.symbols('x y z')

    res,res2= newton(x**5-9,2,0.01)
    print(res,res2)

    res,res2 = secant (x**3-3*x-2,1,3,1e-3)
    print(res,res2)

    funcs=[x**2+y**2-1,x**3-y]
    init = {x:0.8,y:0.6}
    res_dic = solveNonlinearEquations(funcs,init,0.001)
    print(res_dic)


import numpy as np

from numericalmethods.exceptions import InadequateArgsCombination

def newton_horner(x, x_points: np.ndarray, y_points: np.ndarray = None, coeffs: np.ndarray = None) -> np.ndarray:
    """ Evaluates the polynomial returned by Horner's algorithm.

    The user must give `y_points` or `coeffs`. If `coeffs` is not given this method will compute them with `x_points` and `y_points`.

    Args:
        x(float): The point where to evaluate the polynomial.
        x(np.ndarray): `x` coordinates of the points.
        y(np.ndarray): `y` coordinates of the points. Defaults to None.
        coeffs(np.ndarray): coefficients of the polynomial. Defaults to None.

    Raises:
        InadequateArgsCombination: If the combination  of arguments is not valid.
        ValueError: If `x` and `y` are not of the same length.

    Returns:
        float: the polynomial evaluated at x.

    """
    if y_points is None and coeffs is None:
        raise InadequateArgsCombination('Cannot evaluate Newton\'s polynomial with the combination of arguments given. Check the valid combinations.')

    if y_points is not None:
        if len(x_points) != len(y_points):
            raise ValueError('`x_points` and `y_points` must be the same length.')

    coeffs_ = coeffs if coeffs is not None else horner_algorithm(x_points, y_points)

    N = len(x_points) - 1
    polynom = coeffs_[N]

    for k in range(1, N+1):
        polynom = coeffs_[N-k] + (x - x_points[N-k])*polynom

    return polynom

def horner_algorithm(x: np.ndarray, y: np.ndarray) -> np.ndarray:
    """ Computes Newton interpolation polynomial by Horner's algorithm for some given coordinates.

    `x` and `y` must have the same length.

    Args:
        x(np.ndarray): x coordinates of the points.
        y(np.ndarray): y coordinates of the points.

    Raises:
        ValueError: If `x` and `y` are not of the same length.

    Returns:
        np.ndarray: Polynomial coefficients of the Newton interpolation.

    """
    if len(x) != len(y):
        raise ValueError('x and y must be the same length.')

    LEN = len(y)
    matrix = np.zeros((LEN, LEN))

    for j in range(LEN):
        if j == 0:
            matrix[:, 1] = y

        for i in range(LEN-j):
            matrix[i, j] = (matrix[i+1, j] - matrix[i, j]) / (x[i+1] - x[i])

    return matrix[0]


import numpy as np
import math

def f(mu, sigma2, x):
    return 1/np.sqrt(2 * np.pi * sigma2) * np.exp(-0.5 * np.square(x-mu) / sigma2)

print(f(10., 4., 8.))
print(f(10., 4., 10.)) # f(x) is max when mu and x are same, maximizing the gaussian


#!/usr/bin/env python3
import math
import copy
from decimal import *
from itertools import product
import numpy as np

# CHANGEME:
TP=150
TN=300
FP=50
FN=0

tot = TP+TN+FP+FN

print(f"N={tot}")

probtab = np.array([[TN/tot,FN/tot],[FP/tot,TP/tot]]).astype(Decimal)

print("The marginal probabilities of all the values of 𝑋")

row_labels = ["N", "A"];
px = np.sum(probtab, axis=0)
py = np.sum(probtab, axis=1)
print("     N    A     P(y)")
for row_label, row in zip(row_labels, probtab):
  print('%s  [%s]' % (row_label, ' '.join('%04s' % i for i in row)), end=" ")
  print(f"- {np.sum(row, axis=0):.2f}")

print("p(x) " + ' '.join(f"{i:.2f}" for i in px))

print("- " * 10)

# sum of all columns and rows must be one
assert sum(px) == 1
assert sum(py) == 1

print("Entropy of the input")
print("𝐻(𝑋)=−∑𝑃(𝑥)log2𝑃(𝑥)")
hx = -sum([i * math.log2(i) for i in px])
print("-(", end="")
for i in px:
  print(f"{i:.3f}*log2({i:.3f})", end=" + ")
print(f") = {hx:.3f}")
print("- " * 10)

print("The joint probabilities:")
print("P(X|Y)=𝑃(𝑥,𝑦)/𝑃(𝑦)")
for k, i in zip(py, probtab):
  for j in i:
    if j:
      print(f"({j}/{k:.3f})={(j/k):.2f}")
print("- " * 10)

print("The conditional entropy 𝐻(𝑋|𝑌)")
print("𝐻(𝑋|𝑌)=−∑y∑x𝑃(𝑥,𝑦)log2𝑃(𝑥|𝑦)")
hxy = 0
print("-(", end="")
for k, i in zip(py, probtab):
  for j in i:
    if j:
      hxy += (j * math.log2(j / k))
      print(f"{j}*log2({(j/k):.2f})", end=" + ")
hxy = -hxy
print(f") = {hxy:.3f} ")
print("- " * 10)

print("The intrusion detection capability:")
print("C_I_D=(𝐻(𝑋)−𝐻(𝑋|𝑌)) / 𝐻(𝑋)")
cid = ((hx - hxy)/hx)
print(f"({hx:.3f} - {hxy:.3f})/{hx:.3f} = {cid:.3f}")

print("\n##### Abstract IDS models ####\n")
px_ol = copy.copy(px)
probtab = np.array([[TN/tot,0],[FP/tot,FN/tot],[0,TP/tot]]).astype(Decimal)
print("The marginal probabilities of all the values of 𝑋")
row_labels = ["N", "U", "A"];
px = np.sum(probtab, axis=0)
pz = np.sum(probtab, axis=1)
print("     N    A     P(z)")
for row_label, row in zip(row_labels, probtab):
  print('%s  [%s]' % (row_label, ' '.join('%04s' % i for i in row)), end=" ")
  print(f"- {np.sum(row, axis=0):.2f}")

print("p(x) " + ' '.join(f"{i:.2f}" for i in px))
print("- " * 10)

# sum of all columns and rows must be one
assert sum(px) == 1
assert sum(pz) == 1

if set(px_ol) == set(px):
  print("Entropy of the input is still the same as above")
  print(f"recall, H(𝑋)=−∑𝑃(𝑥)log2𝑃(𝑥) = {hx:.3f}")
  print("- " * 10)
else:
  print("Entropy of the input")
  print("𝐻(𝑋)=−∑𝑃(𝑥)log2𝑃(𝑥)")
  hx = -sum([i * math.log2(i) for i in px])
  print("-(", end="")
  for i in px:
    print(f"{i:.3f}*log2({i:.3f})", end=" + ")
  print(f") = {hx:.3f}")
  print("- " * 10)

print("The joint probabilities:")
print("𝑃(𝑥,𝑦)/𝑃(𝑦)=P(X|Y)")
for k, i in zip(pz, probtab):
  for j in i:
    if j:
      print(f"({j}/{k:.3f})={(j/k):.2f}")
print("- " * 10)

print("The conditional entropy 𝐻(𝑋|𝑌)")
print("𝐻(𝑋|𝑌)=−∑y∑x𝑃(𝑥,𝑦)log2𝑃(𝑥|𝑦)")
hxy = 0
print("-(", end="")
for k, i in zip(pz, probtab):
  for j in i:
    if j:
      hxy += (j * math.log2(j / k))
      print(f"{j}*log2({(j/k):.2f})", end=" + ")
hxy = -hxy
print(f") = {hxy:.3f} ")
print("- " * 10)

print("The feature representation capability:")
print("C_r=(𝐻(𝑋)−𝐻(𝑋|𝑌)) / 𝐻(𝑋)")

cr = ((hx - hxy)/hx)
print(f"({hx:.3f} - {hxy:.3f})/{hx:.3f} = {cr:.3f}")

print("- " * 10)

print("The classification information loss:")
print(f"L_c = C_r - C_I_D = {cr:.3f} - {cid:.3f} = {(cr-cid):.3f}")


import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons

# initial center location
x_cent = 0
y_cent = 0

# definition of the circle parameters
center = np.array([x_cent,y_cent])
radius1 = np.sqrt((center[0]-1)**2+(center[1]-0)**2)
radius2 = np.sqrt((center[0]+1)**2+(center[1]-0)**2)

# calculation of the circle coordinates
angle = np.linspace(0,2*np.pi,720)
chi1 = center[0] + radius1*np.cos(angle)
eta1 = center[1] + radius1*np.sin(angle)
chi2 = center[0] + radius2*np.cos(angle)
eta2 = center[1] + radius2*np.sin(angle)

# calculations of the Joukowsky transform
x1 = ((chi1)*(chi1**2+eta1**2+1))/(chi1**2+eta1**2)
y1 = ((eta1)*(chi1**2+eta1**2-1))/(chi1**2+eta1**2)
x2 = ((chi2)*(chi2**2+eta2**2+1))/(chi2**2+eta2**2)
y2 = ((eta2)*(chi2**2+eta2**2-1))/(chi2**2+eta2**2)

# initial figure definition
fig, ax = plt.subplots(figsize=(6,6))
plt.subplots_adjust(bottom=0.25)

# zeta plane
plt.subplot(1, 2, 1)
l, = plt.plot(chi1,eta1,'g',label='Circle1')
m, = plt.plot(chi2,eta2,'b',label='Circle2')
p, = plt.plot([center[0],center[0]],[center[1],center[1]],'w',marker='x',mec='k',markersize=10,label='Center')
plt.scatter([-1,1],[0,0],c=['r','r'],s=100,marker='h',label='Reference Points')
plt.axis('equal')
plt.xlim([-3,3])
plt.grid(True)
plt.xlabel(r"$\chi$",size=14)
plt.ylabel(r"$\eta$",size=14)
plt.legend(loc='lower center', bbox_to_anchor=(0.12, 0.1))

# z plane: airfoil 1
plt.subplot(2, 2, 2)
plt.axis('equal')
n, = plt.plot(x1,y1,'g',label='Transform1')

# z plane: airfoil 2
plt.subplot(2, 2, 4)
plt.axis('equal')
o, = plt.plot(x2,y2,'b',label='Transform2')

# current value of the sliders
x0 = 0
y0 = 0

# position of the sliders
axx = plt.axes([0.18, 0.15, 0.65, 0.025], facecolor='white')
axy = plt.axes([0.18, 0.1, 0.65, 0.025], facecolor='white')

# slider assignation
sx = Slider(axx, r"$\mu_x$", -1, 1, valinit=x0)
sy = Slider(axy, r"$\mu_y$", -1, 1, valinit=y0)

# updating the figure
def update(val):
	x_cent = sx.val
	y_cent = sy.val

	# redefinition of the circle parameters
	center = np.array([x_cent,y_cent])
	radius1 = np.sqrt((center[0]-1)**2+(center[1]-0)**2)
	radius2 = np.sqrt((center[0]+1)**2+(center[1]-0)**2)

	# calculate again the circle coordinates
	angle = np.linspace(0,2*np.pi,720)
	chi1 = center[0] + radius1*np.cos(angle)
	eta1 = center[1] + radius1*np.sin(angle)
	chi2 = center[0] + radius2*np.cos(angle)
	eta2 = center[1] + radius2*np.sin(angle)

	# calculate again Joukowsky transform
	x1 = ((chi1)*(chi1**2+eta1**2+1))/(chi1**2+eta1**2)
	y1 = ((eta1)*(chi1**2+eta1**2-1))/(chi1**2+eta1**2)
	x2 = ((chi2)*(chi2**2+eta2**2+1))/(chi2**2+eta2**2)
	y2 = ((eta2)*(chi2**2+eta2**2-1))/(chi2**2+eta2**2)

	# update with circle 1
	l.set_xdata(chi1)
	l.set_ydata(eta1)

	# update with circle 2
	m.set_xdata(chi2)
	m.set_ydata(eta2)

	# update with airfoil 1
	n.set_xdata(x1)
	n.set_ydata(y1)

	# update with airfoil 2
	o.set_xdata(x2)
	o.set_ydata(y2)

	# update the value of the center of the circles
	p.set_xdata([x_cent,x_cent])
	p.set_ydata([y_cent,y_cent])

	# draw the selected updates
	fig.canvas.draw_idle()

# call the sliders
sx.on_changed(update)
sy.on_changed(update)

# show the figure
plt.show()


from math import cos, sin, pi
import numpy as np
from numpy.linalg import inv

def sph_to_cart(epsilon, alpha, r):
    """
    Transform sensor readings to Cartesian coordinates in the sensor
    frame. The values of epsilon and alpha are given in radians, while
    r is in metres. Epsilon is the elevation angle and alpha is the
    azimuth angle (i.e., in the x,y plane).
    """

    p = np.zeros(3)  # Position vector

    # Your code here
    p[0] = r * cos(alpha) * cos(epsilon)
    p[1] = r * sin(alpha) * cos(epsilon)
    p[2] = r * sin(epsilon)

    return p

def estimate_params(P):
    """
    Estimate parameters from sensor readings in the Cartesian frame.
    Each row in the P matrix contains a single 3D point measurement;
    the matrix P has size n x 3 (for n points). The format is:

    P = [[x1, y1, z1],
         [x2, x2, z2], ...]

    where all coordinate values are in metres. Three parameters are
    required to fit the plane, a, b, and c, according to the equation

    z = a + bx + cy

    The function should retrn the parameters as a NumPy array of size
    three, in the order [a, b, c].
    """

    param_est = np.zeros(3)
    A = np.ones_like(P)
    b = np.zeros((P.shape[0], 1))

      # Your code here
    for i, meas in enumerate(P):
      A[i, 1], A[i, 2], b[i] = meas

    params = inv(A.T @ A) @ A.T @ b
    param_est[0] = params[0, 0]
    param_est[1] = params[1, 0]
    param_est[2] = params[2, 0]

    return param_est

meas = np.array([[pi/3, 0, 5],
                 [pi/4, pi/4, 7],
                 [pi/6, pi/2, 4],
                 [pi/5, 3*pi/4, 6],
                 [pi/8, pi, 3]])
P = np.array([sph_to_cart(*row) for row in meas])
print(estimate_params(P))

Course 2 - State Estimation and Localization/Module 4: LIDAR Sensing/m4q2.py


#Natural Computing with Python
#Chapter 1 - Neural Networks
#Perceptron logic gates

import numpy as np

#Define the AND logic gate
def And(x1, x2):
    x = np.array([1, x1, x2])
    w = np.array([-1.5, 1, 1])
    y = np.sum(w*x)
    if y <= 0:
        return 0
    else:
        return 1

#Define the OR logic gate
def Or(x1, x2):
    x = np.array([1, x1, x2])
    w = np.array([-0.5, 1, 1])
    y = np.sum(w*x)
    if y <= 0:
        return 0
    else:
        return 1

#Define the NAND logic gate
def Nand(x1, x2):
    x = np.array([1, x1, x2])
    w = np.array([1.5, -1, -1])
    y = np.sum(w*x)
    if y <= 0:
        return 0
    else:
        return 1

#MAIN function
if __name__ == '__main__':

    #input array
    input = [(0, 0), (1, 0), (0, 1), (1, 1)]

    #start evaluation
    print("AND")
    for x in input:
        y = And(x[0], x[1])
        print(str(x) + " -> " + str(y))

    print("OR")
    for x in input:
        y = Or(x[0], x[1])
        print(str(x) + " -> " + str(y))

    print("NAND")
    for x in input:
        y = Nand(x[0], x[1])
        print(str(x) + " -> " + str(y))


import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as st

data = np.loadtxt("dataset.txt")
"""
def get_variance(x):
    mean = np.mean(x)
    sum = 0
    for i in x:
        sum += (i-mean)**2
    return sum / (len(x)-1)

print(get_variance([1,2,5,3,4,5,2,4]))
"""

variance = np.var(data,ddof=1)  #ddof = delta degree of freedom
print("Variance")
print(variance)
print()
print()
#STANDARD DEVIATION - square root of variance

std = np.std(data)
print("Standard dev\t\t Variance")
print(std,"\t",std**2)
print()
print()

#GAUSSIAN APPROXIMATION

xs = np.linspace(data.min(),data.max(),100)
ys = st.norm.pdf(xs,loc=np.mean(data),scale=std)

plt.hist(data,bins=50,density=True,histtype="step",label="Data")
plt.plot(xs,ys,label="Normal approximation")
plt.legend()
plt.ylabel("Probability")
plt.show()
print()
print()
#With variance and std we can recontruct the data almost

#Skewness - 3rd Moment
#1st - zero, 2nd - Variance, 3rd - Skewness
#cubic distance from mean
"""
def get_skew(xs):
    mean = np.mean(xs)
    var = np.var(xs)
    sum = 0
    for x in xs:
        sum += (x-mean)**3
    return (sum / (len(xs))) / (var ** 1.5)
print(get_skew([1,10,4,3]))
"""

skewness = st.skew(data)
print("Skewness")
print(skewness)

#update Gaussian approximation to include skewness
xs = np.linspace(data.min(),data.max(),100)
ys = st.norm.pdf(xs,loc=np.mean(data),scale=std)
ys1 = st.skewnorm.pdf(xs,skewness,loc=np.mean(data),scale=std)
plt.hist(data,bins=50,density=True,histtype="step",label="Data")
plt.plot(xs,ys,label="Normal approximation")
plt.plot(xs,ys1,label="Skewnormal approximation")
plt.legend()
plt.ylabel("Probability")
plt.show()
print()
print()

#not a good approx because mean is changed, so change and fit the data

xs = np.linspace(data.min(),data.max(),100)
ys = st.norm.pdf(xs,loc=np.mean(data),scale=std)
ps = st.skewnorm.fit(data) #fitting the data (mean and std dev. by scipy)
ys1 = st.skewnorm.pdf(xs,*ps) #passing the fitted data

plt.hist(data,bins=50,density=True,histtype="step",label="Data")
plt.plot(xs,ys,label="Normal approximation")
plt.plot(xs,ys1,label="Skewnormal approximation")
plt.legend()
plt.ylabel("Probability")
plt.show()
print()
print()

#KURTOSIS - same to skewness just the difference of mean to 4th power
"""
def get_k(xs):
    mean = np.mean(xs)
    var = np.var(xs)
    sum = 0
    for x in xs:
        sum += (x-mean)**4
    return (sum / (len(xs))) / (var ** 2)
print(get_k([1,10,4,3]))
"""

kurtosis = st.kurtosis(data,fisher=False)
print("Kurtosis")
print(kurtosis)
print()
print()
#you won't get same results here from the function and st.kurtosis(), you have
#to pass fisher=False which is normalisation of kurtosis

#Problem while approximation is that data is Bi-Modal distribution and no
#moment will take this into account

#PERCENTILE

ps = np.linspace(0,100,10)
x_per = np.percentile(data,ps)

xs = np.sort(data)
ys = np.linspace(0 ,1 ,len(data))

plt.plot(xs, ys * 100, label="ECDF")
plt.plot(x_per,ps,label="Percentile",marker=".",ms=10)
plt.legend()
plt.ylabel("Percentile")
plt.show()
# Green - Emperical CDF
# Yellow - 10 percentile for numpy to calculate
# Difference in tails in curve because the data is linearly sampled.
# Data seems to be Gaussian because of it's tails.
# Loss of data at tails can be seen
ps = 100 * st.norm.cdf(np.linspace(-3,3,30))
x_per = np.percentile(data,ps)

xs = np.sort(data)
ys = np.linspace(0 ,1 ,len(data))

plt.plot(xs, ys * 100, label="ECDF")
plt.plot(x_per,ps,label="Percentile",marker=".",ms=10)
plt.legend()
plt.ylabel("Percentile")
plt.show()
#not much effort wasted at the tail part but to take tail to account,
#add an insert value to ps
ps = 100 * st.norm.cdf(np.linspace(-3,3,30))
ps = np.insert(ps,0,0)
ps = np.insert(ps,-1,100)
x_per = np.percentile(data,ps)

xs = np.sort(data)
ys = np.linspace(0 ,1 ,len(data))

plt.plot(xs, ys * 100, label="ECDF")
plt.plot(x_per,ps,label="Percentile",marker=".",ms=10)
plt.legend()
plt.ylabel("Percentile")
plt.show()
#Tails covered in percentile


import numpy as np

def print_all():

    print("------------------------------------------")
    print("[START] - DOT PRODUCT BETWEEN TWO MATRICES")
    print("------------------------------------------")

    # Create matrices
    x = np.array([[10, 20], [100, -10]])
    y = np.array([[5, 10], [-50, 10]])

    print("\nPerforming Matrix Multiplication (dot product) on matrices\n-------------\n")

    print(f"Matrices: \n-------\n  x = \n{x} \n\n y = \n{y}\n\n-------\n")

    print("Recall that ELEMENT WISE multiplication resulted in:\n-------\n")

    print(f"Multiplication of x * y: \n-------\nnp.multiply(x,y):\n{np.multiply(x,y)}\n\n-------\n\n")

    print("Whereas the dot product results in...\n")

    print(f"The dot product of x and y:\n-------\nnp.dot(x,y):\n{np.dot(x,y)}\n\n-------\n")

    print("-------------------------------------------")
    print("[FINISH] - DOT PRODUCT BETWEEN TWO MATRICES")
    print("-------------------------------------------")


# Trust region of f at x_k using a quadratic model:
# model_k(p) = f(x_k) + p*f.grad(x) + .5*p*B_k*p
# B_k = f.hessian(x_k)

from core.polynomial import *
from core.matrix import *

def make_quadratic_model(f, x, p):
    """
    p is vector of variables, e.g. (p1, p2)
    x is a vector point, e.g. (1, 2)
    f is a Polynomial
    """
    p = [Polynomial(variable) for variable in p]
    b = f.hessian(*x)
    model = f(*x) + vector_times_vector(p, f.grad(*x)) + \
            vector_times_vector(vector_times_matrix(constant_times_vector(.5, p), b), p)
    return model

def make_full_step_p(f, x):
    """
    unconstrained minimum of the quadratic model
    p = -B_k^(-1)*f.grad(x)
    """
    inverse_b = get_matrix_inverse(f.hessian(*x))
    negative_grad = constant_times_vector(-1, f.grad(*x))
    p = matrix_times_vector(inverse_b, negative_grad)
    return tuple(map(float, p))

def make_steepest_descent_direction(f, x):
    """
    unconstrained minimum of the quadratic model along the steepest descent direction
    """
    b = f.hessian(*x)
    f_grad = f.grad(*x)
    numerator = vector_times_vector(constant_times_vector(-1, f_grad), f_grad)
    denominator = vector_times_vector(vector_times_matrix(f_grad, b), f_grad)
    p = constant_times_vector(numerator / denominator, f_grad)
    return tuple(map(float, p))

def trust_region_subproblem(f, x, delta):
    """
    Dogleg method
    1. calculate full step, p_fs
    2. if full step is within trust region, return full step
    3. calculate steepest descent step, p_u
    4. if steepest descent step is outside trust region,
        return p_u such that ||t*p_u||^2 = delta^2, t in [0, 1]
        t = delta / norm(p)
    5. else return point that solves ||p_u + (t-1)(p_fs - p_u)||^2 = delta^2 for t in interval [1, 2]
    """
    p_fs = make_full_step_p(f, x)
    norm = euclidean_norm
    if norm(p_fs) <= delta:
        step = p_fs
        # print('2. step = ', step)
        return vector_plus_vector(x, step)
    p_u = make_steepest_descent_direction(f, x)
    if norm(p_u) > delta:
        step = constant_times_vector(delta / norm(p_u), p_u)
        # print('4. step = ', step)
        return vector_plus_vector(x, step)
    p_fs_minus_p_u = vector_plus_vector(p_fs, constant_times_vector(-1, p_u))
    quadratic_equation = norm_squared(
        vector_plus_vector(p_u, constant_times_vector(Polynomial('t'), p_fs_minus_p_u))) - delta ** 2
    t_minus_one = max(quadratic_equation.solve())
    step = vector_plus_vector(p_u, constant_times_vector(t_minus_one, p_fs_minus_p_u))
    # print('5. step = ', step)
    return vector_plus_vector(x, step)

def euclidean_norm(vector):
    return (sum([component ** 2 for component in vector])) ** .5

def norm_squared(vector):
    res = 0
    for component in vector:
        res += component ** 2
    return res

def main():
    p = ('p1', 'p2')
    f = Polynomial('10(x2-x1^2)^2+(1-x1)^2')
    x = (0, -1)
    delta = 2
    print(make_quadratic_model(f, x, p))

    # print(trust_region_subproblem(f, x, delta))

    def polynomial_to_numpy(polynomial, *variables):
        """
        input polynomial, a Polynomial class
        and variables, numpy variables
        converts Polynomial class to a numpy function
        """
        z = 0
        # for each term add consant*variable[0]^power[0]*variable[1]^power[1]...
        for term in polynomial.term_matrix[1:]:
            product = term[0]
            for i, variable in enumerate(variables):
                product *= variable ** term[i + 1]
            z += product
        return z

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib.cm as cm
    x_scale = .5
    y_scale = 1.25
    x1 = np.linspace(-x_scale, x_scale, 100)
    x2 = np.linspace(-y_scale, y_scale, 100)
    x1, x2 = np.meshgrid(x1, x2)
    z = polynomial_to_numpy(f, x1, x2)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    cpf = ax.contourf(x1, x2, z, 20, cmap='RdGy')
    # Set the colours of the contours and labels so they're white where the
    # contour fill is dark (Z < 0) and black where it's light (Z >= 0)
    colours = ['w' if level < 0 else 'k' for level in cpf.levels]
    cp = ax.contour(x1, x2, z, 20, colors=colours)
    ax.clabel(cp, fontsize=10, colors=colours)
    ax.set_title('Quadratic Model Contour Lines')
    plot_points = [x]
    for i in range(1, 15):
        new_delta = delta / i
        plot_points.append(trust_region_subproblem(f, x, new_delta))
    for point in plot_points:
        print(point)
    xs = [x[0] for x in plot_points]
    ys = [x[1] for x in plot_points]
    plt.scatter(xs, ys)
    plt.show()

if __name__ == '__main__':
    main()


import numpy as np

"""
Dot product
The dot product multiplies two vectors and results in a scalar. This is also
why it is called the scalar product.
- The dot product is the sum of the products of the corresponding elements.
- When we have a dot product we always multiply a row vector by a column vector
"""

# Scalar multiplication

s1 = np.array(3)
s2 = np.array(6)

s3 = np.dot(s1, s2)

# Prints 18

print(s3)

"""
Vector multiplication
Vectors must be the same length for multiplication

There are two types of products that can be produced from vector multiplication
1. Dot product also known as inner product (also known as scalar product)
2. Tensor product also known as outer product
"""

v1 = np.array([2, 8, -4])
v2 = np.array([1, -7, 3])

# In numpy you can use the dot method to multiply vectors
# This calculation is: [2 * 1 + 8 * (-7) + (-4) x 3]
s = np.dot(v1, v2)

# This results in the scalar product of [-66]
print(s)

# Multiplying a scalar times a vector results in a vector
# where each number in a vector is multiplied by that scalar

s4 = np.array(4)
v3 = np.array([1, 2, 3])

# Here the operation would be [4 * 1, 4 * 2, 4 * 3] resulting in the vector: [4, 8, 12]
v4 = np.dot(s4, v3)
print(v4)

# The same happens when multiplying a scalar times a matrix

m1 = np.array([[1, 2, 3], [3, 2, 1]])
m2 = np.dot(2, m1)

# This results in the matrix: [[2, 4, 6], [6, 4, 2]]
print(m2)

"""
Multiply Matrix's
 - We can only multiply an m x n matrix with an n x k matrix
 For example, we can multiply a 2 x 3 matrix with a 3 x 1 matrix
 If we wanted to multiply a 5 x 7 matrix, compatible matrix's are any 7 * n matrix
"""

# The first matrix is 2 x 3
m1 = np.array([[5, 12, 6], [-3, 0, 14]])

# The second matrix is 3 x 2
m2 = np.array([[2, -1], [8, 0], [3, 0]])

"""
When multiplying the common property, here 3, will disappear and the
resulting matrix is a 2 x 2

Using the dot product we always multiply a row vector by a column vector and so the above two
matrix's will be multiplied as follows:

the first row we have is 5, 12, 6 from the first matrix and from the second matrix
we have the column 2, 8, 3 and with this we get:
[5, 12, 6] * [2, 8, 3] = 124
or
5 * 2 + 12 * 8 + 6 * 3 = 124

Still using the first row: [5, 12, 6] Next we take the second column of the second matrix (-1, 0, 0) and do:
[5, 12, 6] * [-1, 0, 0] = -5
or
(5 * -1) + (12 * 0) + (6 * 0) = -5

124 and -5 represent the first row in our 2 x 2 matrix. Next we take the second row
of our first matrix and do the same multiplication:

[-3, 0, 14] * [2, 8, 3] = 36
and
[-3, 0, 14] * [-1, 0, 0] = 3

This results in a matrix equal to [[124, -5], [36, 3]]
"""
m3 = np.dot(m1, m2)
print(m3)


# Batch Learning

# This file Solves the Linear Regression Problem using
# A - Closed Form Solution which uses Pseudo Inverse W = pseudoInverse(X)*Y
# B - Gradient Descent Method
# Linear Regression problem: Find W which minimizes (Y - WX)^2
# Data required for Linear Regression is generated from different file (GenerateData.py)

# Import Required Libraries
import numpy
import matplotlib.pyplot as plt

# Functions
# Calculates Gradient of the given Function
def Gradient(w0, w1, x, y):
    g1 = (2 * w0) + (2 * w1 * x) - (2 * y)
    g2 = (2 * w0 * x) + (2 * w1 * x**2) - (2 * y * x)
    return numpy.array([g1, g2])

# Update the points based on the Gradient Descent Algorithm
def Update_Weights(x, y, w0, w1, eta):
    row, col = x.shape
    sum = numpy.array([0, 0])
    for i in range(0, row):
        sum = sum + Gradient(w0, w1, X[i, 1], Y[i])
    return numpy.array([w0, w1]) - eta*sum

# Actual Loop where Gradient Descent Algo runs until optimal point is reached
def GD(X, Y, max_iter, tol, eta):
    iterations = 0
    Epoch = numpy.array([])
    W_Initial_Guess = numpy.loadtxt('W.txt')
    while iterations <= max_iter:
        if iterations == 0:
            w_temp = W_Initial_Guess
            W_GD = numpy.array([W_Initial_Guess])
            w_temp = Update_Weights(X, Y, w_temp[0], w_temp[1], eta)
            # Book Keeping
            W_GD = numpy.concatenate((W_GD, [w_temp]), axis=0)
            Epoch = numpy.concatenate((Epoch, [iterations]), axis=0)
            print('No. of Iterations: ', iterations, ' Linear Least Square Fit: ', W_GD[-1], '\n')
            iterations += 1
        else:
            # Run The Gradient Descent Algorithm
            w_temp = Update_Weights(X, Y, w_temp[0], w_temp[1], eta)
            # Book Keeping
            W_GD = numpy.concatenate((W_GD, [w_temp]), axis=0)
            Epoch = numpy.concatenate((Epoch, [iterations]), axis=0)
            print('No. of Iterations: ', iterations, ' Linear Least Square Fit: ', W_GD[-1], '\n')
            # Check for Close Weights
            if (W_GD[-1] - W_GD[-2]).all() < tol:
                print('Optimal Value Reached')
                break
            else:
                iterations += 1
    return Epoch, W_GD

# Function for plotting
def grapher(W):
    x = numpy.array([0, 51])
    y = numpy.array([W[0], W[0] + 51 * W[1]])
    return x, y

# Parameters
# Shape of matrix X = Nx2
# Shape of matrix Y = Nx1
# Shape of Weight matrix = 2x1
X = numpy.loadtxt('X.txt')
Y = numpy.loadtxt('Y.txt')
max_iter = 100000 # Maximum Iterations to reach the optimal value
tol = 1.0e-6 # Tolerance
eta = 0.00002 # Learning Rate

# Solution for the Linear Regression Problem using Closed Form Solution
# From the way I have chosen matrices the
# Equation will be: Y - X * W = 0
# Hence closed form solution will be W = pseudoInverse(X)*Y
# Pseudo Inverse also known as Penrose Inverse
W_Closed_Form_Solution = numpy.dot(numpy.linalg.pinv(X), Y)
numpy.savetxt('ClosedFormSolution.txt', W_Closed_Form_Solution)

# Solution for the Linear Regression Problem using Gradient Descent Method
if numpy.DataSource().exists('BatchLearningGDSolution.txt'):
    W_GD = numpy.loadtxt('BatchLearningGDSolution.txt')  # Load the Initial Weights
else:
    Epoch, W_GD1 = GD(X, Y, max_iter, tol, eta)
    W_GD = W_GD1[-1]
    numpy.savetxt('BatchLearningGDSolution.txt', W_GD1[-1])  # Generate the Weights and save them

# Final Optimal Point
print(W_GD)
print(W_Closed_Form_Solution)

# Plot the results
# Plot 1
fig, ax1 = plt.subplots()
temp_x, temp_y = grapher(W_Closed_Form_Solution)
ax1.plot(temp_x, temp_y, 'r--')
ax1.plot(X[:, 1], Y)
plt.title('Closed Form Solution for Linear Least Square Fit')
plt.xlabel('X')
plt.ylabel('Y')
# Plot 2
fig, ax2 = plt.subplots()
temp_x, temp_y = grapher(W_GD)
ax2.plot(temp_x, temp_y, 'r--')
ax2.plot(X[:, 1], Y)
plt.title('Batch Gradient Descent for Linear Least Square Fit')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()


# Copyright 2018 - Jonathan Alcantara e Osmar Fernandes

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np
from math import pow
from math import sqrt

def integrate(points, a, b, type):
  if type == "gauss":
    L = b-a
    if points > 10:
            print("Numero de pontos de integração indisponivel.")

    if points == 2:
        peso = np.array([1, 1])
        vectorx = np.array([-0.577, 0.577])
    elif points == 3:
        peso = np.array([0.889, 0.556, 0.556])
        vectorx = np.array([0, -0,775, 0.775])
    elif points == 4:
        peso = np.array([0.652, 0.652, 0.348, 0.348])
        vectorx = np.array([-0.340, 0.340, -0.861, 0.861])
    elif points == 5:
        peso = np.array([0.569, 0.477, 0.477, 0.237, 0.237])
        vectorx = np.array([0, -0.538, 0.538, -0.906, 0.906])
    elif points == 6:
        peso = np.array([0.361, 0.361, 0.468, 0.468, 0.171, 0.171])
        vectorx = np.array([0.661, -0.661, -0.239, 0.239, -0.932, 0.932])
    elif points == 7:
        peso = np.array([0.462, 0.382, 0.382, 0.280, 0.280, 0.129, 0.129])
        vectorx = np.array([0, 0.406, -0.406, -0.742, 0.742, -0.949, 0.949])
    elif points == 8:
        peso = np.array([0.363, 0.363, 0.314, 0.314, 0.222, 0.222, 0.101, 0.101])
        vectorx = np.array([-0.183, 0.183, -0.526, 0.526, -0.797, 0.797, -0.960, 0.960])
    elif points == 9:
        peso = np.array([0.330, 0.181, 0.181, 0.081, 0.081, 0.312, 0.312, 0.261, 0.261])
        vectorx = np.array([0, -0.836, 0.836, -0.968, 0.968, -0.324, 0.324, -0.613, 0.613])
    elif points == 10:
        peso = np.array([0.296, 0.296, 0.269, 0.269, 0.219, 0.219, 0.149, 0.149, 0.067, 0.067])
        vectorx = np.array([-0.149, 0.149, -0.433, 0.433, -0.679, 0.679, -0.865, 0.865, -0.974, 0.974])

    function = np.zeros((points, 1))
    for i in range(points):
        function[i] = f((a + b + L*vectorx[i])/2)
    return (np.dot(function.T, peso)*(b-a))[0]/2
  else:
    x = np.zeros(points)

    if points == 1:
      x[0] = (a+b)/2
    else:
      delta = (b-a)/(points - 1)
      for i in range(points):
        x[i] = a + i*delta

      vandermonde = np.zeros((points, points))
      function = np.zeros((points, 1))
      matrixB = np.zeros((points, 1))

      for i in range(points):
        vandermonde[i] = np.power(x, i)
        matrixB[i] = ((b**(i+1)) - (a**(i+1))) / (i+1)
        function[i] = f(x[i])

      pesos = np.linalg.solve(vandermonde, matrixB)

      return np.dot(function.T, pesos)[0,0]

def f(x):
  return (2 + x + 2*pow(x,2))

result = integrate(2, 1, 3, "gauss")
print(result)


"""
Created on Wed Apr 22 15:53:00 2015

Charging and discharging curves for passive membrane patch
R Rao 2007

translated to Python by rkp 2015
"""

import numpy as np
import matplotlib.pyplot as plt

# input current
I = 10 # nA

# capacitance and leak resistance

# Questions 2:
# Change the values for the membrane's resistance and capacitance (R and C),
# and find out how this influences the response of the membrane. Does it reach
# a stable value more quickly or more slowly after multiplying R by 5?
#R = 100 * 5 # M ohms

# Original
R = 100 # M ohms

# Question 3:
# Does it reach a stable value more quickly or more slowly after dividing C by 10?
C = 0.1 / 10.# nF

# Original
#C = 0.1 # nF

# Question 4:
# Does it reach a stable value more quickly or more slowly after multiplying R
# by 10 AND dividing C by 10?
C = 0.1 / 10.
R = 100 * 10.

tau = R*C # = 0.1*100 nF-Mohms = 100*100 pF Mohms = 10 ms
print('C = %.3f nF' % C)
print('R = %.3f M ohms' % R)
print('tau = %.3f ms' % tau)
print('(Theoretical)')

# membrane potential equation dV/dt = - V/RC + I/C

# QUESTIONS:
# What if the current were not turned off? What would the steady state voltage
# of the membrane be?
# Use the values given in the script to compute your answer (C = 0.1 nF,
# R = 100 MΩ, I = 10 nA). You should give your answer in mV. Do not include
# units in your answer.
#tstop = 15000 # ms

# Original
tstop = 150 # ms

V_inf = I*R # peak V (in mV)
tau = 0 # experimental (ms)

h = 0.2 # ms (step size)

V = 0 # mV
V_trace = [V] # mV

for t in np.arange(h, tstop, h):

   # Euler method: V(t+h) = V(t) + h*dV/dt
   V = V +h*(- (V/(R*C)) + (I/C))

   # Verify membrane time constant
   if (not tau and (V > 0.6321*V_inf)):
     tau = t
     print('tau = %.3f ms' % tau)
     print('(Experimental)')

   # Stop current injection
   if t >= 0.6*tstop:
     I = 0

   V_trace += [V]

# Why draw this every 10th time?
#if t % 10 == 0:
plt.plot(np.arange(0,t+h, h), V_trace, color='r')
plt.xlim(0, tstop)
plt.ylim(0, V_inf)
plt.xlabel('Time[ms]')
plt.ylabel('Voltage[mV]')
plt.draw()

plt.show()


import numpy
import math
from sympy import *

def nCr(n, r):
    return math.factorial(n)//(math.factorial(r) * math.factorial(n - r))

#Returns coefficient of x^0, x^1, x^2, x^4, ... as an matrix
def binomialequation(constant,power):
    coeffs = []
    for i in range(0, power + 1):
        coeff = nCr(power, power - i) * math.pow(constant, power - i)
        coeffs.append(coeff)
    return coeffs

def expression_solver(expression, debugging = False):
    x = Symbol('x')
    converted = sympify(expression)
    if debugging:
        print("Converted is " + str(converted))
    solutions = solve(converted)
    count = 1
    for value in solutions:
        try:
            print("Solution " + str(count) + ": " + str(round(float(value),3)))
            count += 1
        except:
            continue
    if(count == 1):
        print("No real solutions")

def equation_creater(coeffs):
    result = str(int(coeffs[0])) + " + "
    for index in range(1, len(coeffs)):
        result += str(int(coeffs[index])) + "*x^" + str(index) + " + "
    result = result[:-3]
    print(result)
    return result

def subtract(leftarr, rightarr):
    updated = [0.0] * max(len(leftarr), len(rightarr))
    for index in range(len(updated)):
        if(index < len(leftarr)):
            updated[index] += leftarr[index]
        if(index < len(rightarr)):
            updated[index] -= rightarr[index]
    return updated

if __name__ == '__main__':
    leftarr = [0.0, 1.0]
    rightarr = [1.0, 0.0, -4.0]
    #print(subtract(leftarr, rightarr))
    #expression_solver(equation_creater(rightarr))
    expression_solver('2*x - ((-1 + x) + 4)')


# Exercise 2
# Load the data from the CSV file data_ex2.csv. These are samples from three different, independent Gaussian
# distributions, all mixed together.
# 1. Implement the Expectation-Maximization algorithm to fit a mixture of three Gaussian distributions to the data.
#    Try both with and without the prior update step. Discuss the results.
# 2. Give the parameters of the distributions thus found, and plot the corresponding PDFs on top of the
#    empirical PDFs of the data (e.g., the histogram).
# import and solve data
from pandas import read_csv
import numpy as np
from matplotlib import pyplot as plt
from numpy import mean, min, max, median, quantile
from scipy.stats import binom, norm, t as student, expon
from math import sqrt, pow, floor, ceil, exp

np.set_printoptions(precision=3)

# define corrected functions for std and variance
def variance(values):
	return np.var(values, ddof=1)

def std(values):
	return np.std(values, ddof=1)

data = 'HW2/data'
data_ex2 = read_csv(f'{data}\\data_ex2.csv', header=None)
values = data_ex2.to_numpy()[:, 0]

def expectation_maximization_normal(values, curve_n, max_iter=10000, curve_prob=None, prior=True):
	n = len(values)
	# use the same std for all of them
	deviations = np.array([std(values)] * curve_n)
	# for 3 curves, use 1/4, 2/4, 3/4 quantiles as means
	means = np.array(quantile(values, np.arange(1, curve_n + 1, 1) / (curve_n + 1)))
	if curve_prob == None:
		curve_prob = [1 / curve_n] * curve_n
	curve_prob = np.array(curve_prob)

	values_repeated = np.array([values] * curve_n)

	iteration = 0
	prev_total = sum(means + deviations + curve_prob)
	while iteration < max_iter:
		pdfs_per_curve = norm.pdf(values_repeated.T, means, deviations)
		denoms = np.sum(pdfs_per_curve * curve_prob, axis=1)
		b = (pdfs_per_curve * curve_prob).T / denoms
		sum_b = np.sum(b, axis=1)
		means = np.sum(b * values, axis=1) / sum_b
		variances = np.sum(b.T * ((values_repeated.T - means)**2), axis=0) / sum_b
		deviations = np.sqrt(variances)
		if prior:
			curve_prob = sum_b / n

		iteration += 1
		new_total = sum(means + deviations + curve_prob)
		# stop when total difference is no longer changing
		if abs(new_total / prev_total - 1) <= 0.0001:
			break
		prev_total = new_total

	print(f"done {iteration} iterations")
	return np.array([means, deviations, curve_prob]).T

print("prior true")
curves = expectation_maximization_normal(values, 3, max_iter=10000, prior=True)
print(curves)
plt.hist(values, bins=50, density=True, color='y', linewidth=0.1, edgecolor='b')
X_prob = np.arange(min(values), max(values), 1 / 100)
for c in curves:
	plt.scatter(X_prob, c[2] * norm.pdf(X_prob, c[0], c[1]), s=1, zorder=2)
plt.show()

print("prior false")
curves = expectation_maximization_normal(values, 3, max_iter=10000, prior=False)
print(curves)
plt.hist(values, bins=50, density=True, color='y', linewidth=0.1, edgecolor='b')
X_prob = np.arange(min(values), max(values), 1 / 100)
for c in curves:
	plt.scatter(X_prob, c[2] * norm.pdf(X_prob, c[0], c[1]), s=1, zorder=2)
plt.show()

off_start = [0.8, 0.1, 0.1]
print(f"prior true, starting from probabilities: {off_start}")
curves = expectation_maximization_normal(values, 3, max_iter=10000, prior=True, curve_prob=off_start)
print(curves)
plt.hist(values, bins=50, density=True, color='y', linewidth=0.1, edgecolor='b')
X_prob = np.arange(min(values), max(values), 1 / 100)
for c in curves:
	plt.scatter(X_prob, c[2] * norm.pdf(X_prob, c[0], c[1]), s=1, zorder=2)
plt.show()

print(f"prior false, starting from probabilities: {off_start}")
curves = expectation_maximization_normal(values, 3, max_iter=10000, prior=False, curve_prob=off_start)
print(curves)
plt.hist(values, bins=50, density=True, color='y', linewidth=0.1, edgecolor='b')
X_prob = np.arange(min(values), max(values), 1 / 100)
for c in curves:
	plt.scatter(X_prob, c[2] * norm.pdf(X_prob, c[0], c[1]), s=1, zorder=2)
plt.show()


#steven 01/03/2020
#calculate pi using random method. (Monte Carlo method)
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate

def randomSeries(N):#generate series number between 0~1
    return np.random.rand(N)

def distance(a, b):#euclidean distance from point(a,b) to point(0,0)"""
    return np.sqrt(a**2 + b**2)

def circleFun(x):
    return np.sqrt(1-x**2)

def plotXY(x,y):
    plt.figure(num='Calculate Pi')
    ax = plt.gca()
    ax.set_xlabel('x')
    ax.set_ylabel('y')

    for a,b in zip(x,y):
        if distance(a, b) > 1:
            ax.scatter(a, b, c='r', s=5, alpha=0.5)
        else:
            ax.scatter(a, b, c='b', s=5, alpha=0.5)

    plt.show()

def menteCarloMethod(N=10000 ,plot=True):
    x = randomSeries(N)
    y = randomSeries(N)

    res = np.where(distance(x,y) > 1, 0, 1)
    #print(res)

    pi = np.sum(res)/len(res)*4.0
    #pi = np.mean(res == 1)*4.0
    print('when N=',N,'pi=',pi)

    if plot:
        plotXY(x,y)

def IntegralCalculatePi(N=10000):
    """calculate area form x = 0 to 1
    divide 0~1 to N shares,  erevy part considered as a rectangle.
    """
    s = 0
    for i in range(N):
        x = 1/N
        y = circleFun(i/N)
        s += x*y

    pi = s*4
    print('when N=',N,'s = ',s,'pi = ',pi)
    fillColor()

def fillColor():
    x = np.linspace(0, 1, 100)
    y1 = np.zeros(len(x))
    y2 = circleFun(x)

    plt.plot(x,y1,c='b',alpha=0.5)
    plt.plot(x,y2,c='b',alpha=0.5)

    plt.fill_between(x,y1,y2,where=x<=1,facecolor='green')
    #plt.axes(aspect='equal')#.set_aspect('equal')
    plt.axes().set_aspect('equal')
    #plt.grid(True)
    plt.show()

def IntegralAccPi():
    s = integrate.quad(circleFun, 0, 1)[0]
    print('s = ',s,'pi = ',4*s)

def main():
    #menteCarloMethod(plot=False)
    #IntegralCalculatePi()
    IntegralAccPi()

if __name__ == "__main__":
    main()