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()


import numpy as np
from scipy.special import erf, erfinv

# =============================================================================
# Define variables
# =============================================================================
sqrt2 = np.sqrt(2)
sqrt2pi = np.sqrt(2 * np.pi)

# =============================================================================
# Define functions
# =============================================================================
def gaussian1d_variant1(x, *p):
    """
    Create a normalised gaussian (Area == 1) from mean and variance

    :param x: [numpy array]    x axis data (array/list)
    :param p: [Tuple]         (mean , variance)

    Area of gaussian is 1  --> A = 1/(c*sqrt(2pi))

    Returns a Gaussian array one value for each x value
    """
    mu, sigma = p
    A = 1.0 / (sigma * sqrt2pi)
    return A * np.exp(-0.5 * (x - mu) ** 2 / (sigma ** 2))

def gaussian1d_variant2(x, *p):
    """
    Create a gaussian from amplitude, mean and variance

    :param x: [numpy array]    x axis data (array/list)
    :param p: [Tuple]         (a = amplitude, b = mean , c = variance)

    Returns a Gaussian array one value for each x value
    """
    a, b, c = p
    return a * np.exp(-0.5 * (x - b) ** 2 / (c ** 2))

def gaussian2d_variant1(x, y, *p):
    """
    Create a 2D gaussian from amplitude, mean x, variance x, mean y and
    variance y

    :param x: [numpy array]    x axis data (array/list)
    :param y: [numpy array]    y axis data (array/list)
    :param p: [Tuple]         (amplitude, mean x, variance x,
                               mean y, variance y)

    Returns a Gaussian array one value for each x value
    """
    A, x0, sigmax, y0, sigmay = p
    part1 = (x - x0) ** 2 / (2. * sigmax ** 2)
    part2 = (y - y0) ** 2 / (2. * sigmay ** 2)
    # return 2d gaussian
    return A * np.exp(-(part1 + part2))

def gaussian2d_variant2(x, y, *p, **kwargs):
    """
    Create a 2D gaussian from amplitude, mean x, variance x, mean y and
    variance y and a rotation angle theta (0.0 == x-axis) where theta is in
    degrees unless keyword units = radians or rad

    :param x: [numpy array]    x axis data (array/list)
    :param y: [numpy array]    y axis data (array/list)
    :param p: [Tuple]         (amplitude, mean x, variance x,
                               mean y, variance y, theta)
    :param kwargs:             keyword arguments i.e. units = "deg"
    keywords args are as follows:
        - units:               string either deg or rad

    Returns a Gaussian array one value for each x value
    """
    # extract values from p
    A, x0, sigmax, y0, sigmay, theta = p
    # sort out units
    units = kwargs.get('units', 'deg')
    if units == 'deg' or 'deg' in units:
        theta = np.deg2rad(theta)
    else:
        theta = theta
    # calculate gaussian
    a = np.cos(theta) ** 2 / (2. * sigmax ** 2) + np.sin(theta) ** 2 / (
    2. * sigmay ** 2)
    b = -np.sin(2 * theta) / (4. * sigmax ** 2) + np.sin(2 * theta) / (
    4. * sigmay ** 2)
    c = np.cos(theta) ** 2 / (2. * sigmay ** 2) + np.sin(theta) ** 2 / (
    2. * sigmax ** 2)
    part1 = a * (x - x0) ** 2
    part2 = -2 * b * (x - x0) * (y - y0)
    part3 = c * (y - y0) ** 2
    # return 2d gaussian
    return A * np.exp(-(part1 + part2 + part3))

def gaussian2d_variant3(x, y, *p):
    """
    Standard Gaussian function Creator
    :param x: [numpy array]    x axis data (array/list)
    :param y: [numpy array]    y axis data (array/list)
    :param p: [Tuple]         (amplitude, mean x, mean y, a, b, c)

    Returns a Gaussian array one value for each x value
    """
    A, x0, y0, a, b, c = p
    part1 = a * (x - x0) ** 2
    part2 = -2 * b * (x - x0) * (y - y0)
    part3 = c * (y - y0) ** 2
    # return 2d gaussian
    return A * np.exp(-(part1 + part2 + part3))

def sigma2percentile(sigma):
    """
    Percentile calculation from sigma
    i.e. 1 sigma == 0.68268949213708585 (68.27%)
    :param sigma: [float]     sigma value
    :return percentile:  [float] the percentile (i.e. between 0.00 and 1.00)
    """
    # percentile = integral of exp{-0.5x**2}
    percentile = erf(sigma / sqrt2)
    return percentile

def percentile2sigma(percentile):
    """
    Sigma calcualtion from percentile
    i.e. 0.68268949213708585 (68.27%) == 1 sigma
    :param percentile: [float]     percentile value (i.e. between 0.00 and 1.00)
    :return sigma:  [float] the sigma value (i.e. 1, 2, 3, 1.5)
    """
    # area = integral of exp{-0.5x**2}
    sigma = sqrt2 * erfinv(percentile)
    return sigma


# Find eigenvalue and eigenvectors of matrix A.
#Ax = lx
#l = lamda

# Rewrite (A - lI)x = 0
# det(A - lI) = 0

# Find lamda first.

import numpy as np
import scipy.linalg

A = np.array([[3, 1],
              [1, 3]])

(l, eigv) = np.linalg.eig(A)

# More by hand
# Another solution det(A - lI) = 0
# Lamda in diagonal.

# (3-l)² = 1
# Solve for lamda. l1 = 4 and l2 = 2

lamda1 = l[0]
lamda2 = l[1]

lamda1Vector = lamda1 * (np.identity(2))
sol1 = A - lamda1Vector

lamda2Vector = lamda2 * (np.identity(2))
sol2 = A - lamda2Vector

# We will solve a type of Bx = 0. That is solve for N(B), The Nullspace of B.

X1 = scipy.linalg.null_space(sol1)
X2 = scipy.linalg.null_space(sol2)

# Need to reshape to compare.
X1 = X1.reshape((2,))
X2 = X2.reshape((2,))

assert np.allclose(X1, eigv[:,0])
assert np.allclose(X2, eigv[:,1])

print(lamda1)


# Import
import numpy as np  # Matrix
from sklearn.datasets import make_regression  # Random dataset
import matplotlib.pyplot as plt  # Graphic

# Linear regression multiple
# https://github.com/MachineLearnia/Regression-lineaire-numpy/blob/master/R%C3%A9gression%20Lin%C3%A9aire%20Multiple.ipynb
class LrMultiple:
    # Hyper parameter
    learning_rate = 0.1
    n_iteration = 100

    # Configuration
    n_features = 2
    n_param = 2

    def run(self):
        x, y = make_regression(n_samples=100, n_features=self.n_features, noise=10)  # x -> inputs  y -> outputs
        y = y.reshape(y.shape[0], 1)

        X = np.hstack((x, np.ones((x.shape[0], 1))))  # X -> arguments input [[a, 1],[b, 1]...]
        for i in range(2, self.n_param):
            X = np.hstack((X * x, np.ones((x.shape[0], 1))))
            # print(X)

        theta = np.random.randn(X.shape[1], 1)  # Matrix with the arguments of the function

        # Learn
        theta_final, cost_history = self.gradient_descent(X, y, theta, self.learning_rate, self.n_iteration)

        predictions = self.model(X, theta_final)

        print("Stats: Linear regression multiple")  # Show Stats
        print("Iteration: " + str(self.n_iteration))
        print("Learning rate: " + str(self.learning_rate))
        print("Features: " + str(self.n_features))
        print("Parameters: " + str(self.n_param))
        print("x: " + str(x.shape))
        print("y: " + str(y.shape))
        print("X: " + str(X.shape))
        print("Theta: " + str(theta.shape))
        print("Theta final: " + str(theta_final))

        # Show coef /1
        coef = self.coef_determination(y, predictions)
        print("\nCoef: {0:9.3f}/1 ({0})".format(coef))

        # Show graphs
        for i in range(0, self.n_features):
            plt.scatter(x[:, i], y)
            plt.scatter(x[:, i], predictions, c='r')
            plt.show()

        #  Show cost evolution
        plt.plot(range(self.n_iteration), cost_history)
        plt.show()

    def model(self, X, theta):
        return X.dot(theta)

    def cost(self, X, y, theta):
        m = len(y)
        return 1 / (2 * m) * np.sum((self.model(X, theta) - y) ** 2)

    def grad(self, X, y, theta):
        m = len(y)
        return 1 / m * X.T.dot(self.model(X, theta) - y)

    def gradient_descent(self, X, y, theta, learning_rate, n_iteration):
        cost_history = np.zeros(n_iteration)
        for i in range(0, n_iteration):
            theta = theta - learning_rate * self.grad(X, y, theta)
            cost_history[i] = self.cost(X, y, theta)
        return theta, cost_history

    def coef_determination(self, y, pred):
        u = ((y - pred) ** 2).sum()
        v = ((y - y.mean()) ** 2).sum()
        return 1 - u / v


'''
Using vectorized functions

When performance is paramount, you should avoid using .apply() and .map() because those constructs perform Python for-loops over the data stored in a pandas Series or DataFrame. By using vectorized functions instead, you can loop over the data at the same speed as compiled code (C, Fortran, etc.)! NumPy, SciPy and pandas come with a variety of vectorized functions (called Universal Functions or UFuncs in NumPy).

You can even write your own vectorized functions, but for now we will focus on the ones distributed by NumPy and pandas.

In this exercise you're going to import the zscore method from scipy.stats and use it to compute the deviation in voter turnout in Pennsylvania from the mean in fractions of the standard deviation. In statistics, the z-score is the number of standard deviations by which an observation is above the mean - so if it is negative, it means the observation is below the mean.

Instead of using .apply() as you did in the earlier exercises, the zscore UFunc will take a pandas Series as input and return a NumPy array. You will then assign the values of the NumPy array to a new column in the DataFrame. You will be working with the election DataFrame - it has been pre-loaded for you.

Import zscore from scipy.stats.
Call zscore with election['turnout'] as input .
Print the output of type(turnout_zscore). This has been done for you.
Assign turnout_zscore to a new column in election as 'turnout_zscore'.
Print the output of election.head(). This has been done for you, so hit 'Submit Answer' to view the result.
'''
# Import zscore from scipy.stats
from scipy.stats import zscore

# Call zscore with election['turnout'] as input: turnout_zscore
turnout_zscore = zscore(election['turnout'])

# Print the type of turnout_zscore
print(type(turnout_zscore))

# Assign turnout_zscore to a new column: election['turnout_zscore']
election['turnout_zscore'] = turnout_zscore

# Print the output of election.head()
print(election.head())


#!/usr/bin/env python3
"""
Clustering Module
"""
import numpy as np

def pdf(X, m, S):
    """
    Calculates the probability density function of a Gaussian distribution:

    X is a numpy.ndarray of shape (n, d) containing the data points whose PDF
    should be evaluated
    m is a numpy.ndarray of shape (d,) containing the mean of the distribution
    S is a numpy.ndarray of shape (d, d) containing the covariance of the
    distribution
    You are not allowed to use any loops
    You are not allowed to use the function numpy.diag or the method
    numpy.ndarray.diagonal

    Returns: P, or None on failure
        P is a numpy.ndarray of shape (n,) containing the PDF values for each
        data point
    All values in P should have a minimum value of 1e-300
    """
    if not isinstance(X, np.ndarray) or len(X.shape) != 2:
        return None
    if not isinstance(m, np.ndarray) or len(m.shape) != 1:
        return None
    if not isinstance(S, np.ndarray) or len(S.shape) != 2:
        return None
    if X.shape[1] != m.shape[0] or X.shape[1] != S.shape[0]:
        return None
    if S.shape[0] != S.shape[1] or X.shape[1] != S.shape[1]:
        return None
    if S.shape[0] != S.shape[1]:
        return None

    n, d = X.shape
    Xmm = X - m
    Sinv = np.linalg.inv(S)

    p = 1. / (np.sqrt(((2 * np.pi)**d * np.linalg.det(S))))
    fac = np.einsum('...k,kl,...l->...', Xmm, Sinv, Xmm)
    q = np.exp(-fac / 2)
    return (np.maximum(p*q, 1e-300))


"""CrossEntropy loss implementation."""

from losses.loss import Loss
import numpy as np
import numpy.typing as npt
from activations import Activation

class CrossEntropy(Loss):
    """Implements loss for cross entropy with and without a softmax."""

    # We could create this such that it will apply softmax to the output. This is the equivalent of setting
    # from_logits=True in tensorflow. Basically we're saying in the case of logits, they aren't a probability
    # distribution.

    def __init__(self, apply_softmax=False):
        """
        Init the class.

        Parameters:
        apply_softmax: Whether the loss function should first apply Softmax to the inputs.
        """
        self.apply_softmax = apply_softmax

    def __call__(self, y_pred: npt.ArrayLike, y_true: npt.ArrayLike) -> npt.ArrayLike:
        """
        Output the loss or cost calculated using y_pred and y_true.

        Parameters:
        y_pred: The predicted output of a layer.
        y_true: The True output it should have given.

        Return:
        The loss of this particular training example.
        """
        if self.apply_softmax:
            exps = np.exp(y_pred)
            exp_sums = np.sum(exps, axis=-1)
            exp_sums = np.repeat(
                exp_sums[:, np.newaxis], exps.shape[-1], axis=1)
            y_pred = exps / exp_sums
        # losses = np.nan_to_num(-y_true * np.log(y_pred)
        #                        - (1 - y_true) * np.log(1 - y_pred))
        losses = y_true * -np.log(y_pred)
        losses = np.sum(losses, axis=-1, keepdims=False)
        return losses

    def _delta(self, z: npt.ArrayLike, y_pred: npt.ArrayLike,
               y_true: npt.ArrayLike, activation: Activation = None) -> npt.ArrayLike:
        """Find the derivative of the loss function with respect to y_true and y_pred."""
        return np.transpose(y_pred - y_true)


import numpy as np
import math

def linear(m0,m1,u,domain=None):
    """For completeness: linear interpolation between m0 and m1 at parameter u in [0,1].

    Alternatively, if `domain` != None, then this will use domain=(a,b)
    """
    if domain is not None:
        return linear(m0,m1,(u-domain[0])/(domain[1]-domain[0]))
    return (1.0-u)*m0 + u*m1

def piecewise_linear(ms,u,times=None):
    """Evaluate a piecewise linear spline at interpolation parameter u in [0,1]

    Milestones are given by the array `ms`.  `ms` is assumed to be a list of
    n Numpy arrays, or an n x d Numpy array.

    If `times` != None, this will be a list of n non-decreasing time indices.
    """
    if times is not None:
        raise NotImplementedError("Not done with timed paths")
    n = len(ms)
    s = u*n
    i = int(math.floor(s))
    u = s - i
    if i < 0: return ms[0]
    elif i+1 >= n: return ms[-1]
    return linear(ms[i],ms[i+1],u)

def hermite(m0,m1,t0,t1,u,domain=None):
    """Evaluate a cubic hermite curve at interpolation parameter u in [0,1].

    Endpoints are m0 and m1, with derivatives t0 and t1.  These are assumed to be Numpy arrays.

    Alternatively, if `domain` != None, then this will use domain=(a,b)
    as the interpolation domain
    """
    if domain is not None:
        assert isinstance(domain,(list,tuple)) and len(domain) == 2,"Need to provide a pair as a domain"
        scale = (domain[1]-domain[0])
        t = (u - domain[0])/scale
        return hermite(m0,m1,t0*scale,t1*scale,t)
    u2 = u**2
    u3 = u**3
    cm0 = 2*u3 - 3*u2 + 1
    cm1 = -2*u3 + 3*u2
    ct0 = u3 - 2*u2 + u
    ct1 = u3 - u2
    return cm0*m0 + cm1*m1 + ct0*t0 + ct1*t1

def hermite_deriv(m0,m1,t0,t1,u,domain=None):
    """Evaluate the derivative of a cubic hermite curve at interpolation parameter u in [0,1].

    Endpoints are m0 and m1, with derivatives t0 and t1.  These are assumed to be numpy arrays.

    Alternatively, if `domain` != None, then this will use domain=(a,b)
    as the interpolation domain
    """
    if domain is not None:
        assert isinstance(domain,(list,tuple)) and len(domain) == 2,"Need to provide a pair as a domain"
        scale = (domain[1]-domain[0])
        t = (u - domain[0])/scale
        return hermite_deriv(m0,m1,t0*scale,t1*scale,t)
    u2 = u**2
    cm0 = 6*u2 - 6*u
    cm1 = -6*u2 + 6*u
    ct0 = 3*u2 - 4*u + 1
    ct1 = 3*u2 - 2*u
    return cm0*m0 + cm1*m1 + ct0*t0 + ct1*t1

def hermite_spline(ms,ts,u,times=None):
    """Evaluate a cubic hermite spline at interpolation parameter u in [0,1].

    Milestones are given in `ms`, with derivatives in `ts`.  These are assumed to be
    lists of n Numpy arrays, or n x d Numpy arrays.

    If `times` != None, this will be a list of n non-decreasing time indices.
    """
    if times is not None:
        raise NotImplementedError("Not done with timed paths")

    n = len(ms)
    s = u*n
    i = int(math.floor(s))
    u = s - i
    if i < 0: return ms[0]
    elif i+1 >= n: return ms[-1]
    return hermite(ms[i],ms[i+1],ts[i],ts[i+1],u)


#%%
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import matplotlib.gridspec as gridspec
from scipy.stats import multivariate_normal as mn
# %%
plt.rcParams['grid.color'] = '#A8BDB7'
plt.rcParams['grid.linestyle'] = '--'
plt.rcParams['text.usetex'] = True
plt.rcParams['text.latex.preamble'] = r'\usepackage{{amsmath}}'

class UpdateFigure:
    def __init__(self, ax1, ax2, ax3):

        self.colors = dict(
            blue        = '#375492',
            green       = '#88E685',
            dark_green  = '#00683B',
            red         = '#93391E',
            pink        = '#E374B7',
            purple      = '#A268B4',
            black       = '#000000',
        )
        self.cm = plt.cm.turbo
        # ====================
        # config data
        # ====================
        self.mean = np.zeros(2)
        self.cov = np.eye(2)
        self.xx, self.yy = np.meshgrid(np.linspace(-4,4,101), np.linspace(-4,4,101))
        xysurf = mn.pdf(np.dstack((self.xx,self.yy)), self.mean, self.cov)
        self.vmin, self.vmax = 0, np.max(xysurf)

        # ====================
        # draw LaTeX formula
        # ====================
        ax1.axis('off')
        self.tex = ax1.text(0.5,0.5,
            self.gen_text(self.mean, self.cov),
            ha='center', va='center',
            color='k', fontsize=25)

        # ====================
        # plot 3d surface
        # ====================
        self.surf = ax2.plot_surface(self.xx, self.yy, xysurf, cmap=self.cm,
                        rstride=1, cstride=1, vmin=self.vmin, vmax=self.vmax)
        ax2.view_init(10, None)

        ax2.set_xlabel(r'$x$', fontsize=20)
        ax2.set_ylabel(r'$y$', fontsize=20)
        ax2.zaxis.set_rotate_label(False)
        # ax2.set_zlabel(r'$f(y)$', rotation=0, fontsize=20)
        xticks=[-4,-2,0,2,4]
        yticks=[-4,-2,0,2,4]
        zticks=[0,.1,.2]
        ax2.set_xticks(xticks)
        ax2.set_yticks(yticks)
        ax2.set_zticks(zticks)
        ax2.set_xlim(xticks[0], xticks[-1])
        ax2.set_ylim(yticks[0], yticks[-1])
        ax2.set_zlim(zticks[0], zticks[-1])
        # ax2.invert_xaxis()
        ax2.xaxis.pane.fill = False
        ax2.yaxis.pane.fill = False
        ax2.zaxis.pane.fill = False
        ax2.xaxis.pane.set_edgecolor('w')
        ax2.yaxis.pane.set_edgecolor('w')
        ax2.zaxis.pane.set_edgecolor('w')
        self.ax2 = ax2

        # ====================
        # draw 2d pcolor
        # ====================
        self.mesh = ax3.pcolormesh(self.xx, self.yy, xysurf, cmap=self.cm)
        ax3.axis('scaled')
        ax3.set_xticks([-4,-2,0,2,4])
        ax3.set_yticks([-4,-2,0,2,4])

    def set_target(self, trans_type, diff, nframe):
        self.trans_type = trans_type
        if trans_type == 'stretch':
            self.diff = (diff-np.eye(2))*1.0/(nframe-1)
        else:
            self.diff = diff*1.0/(nframe-1)

    @staticmethod
    def rot(mat, theta):
        rot_mat =  np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]])
        return rot_mat.T@mat@rot_mat

    @staticmethod
    def stretch(mat, diff):
        stretch_mat = np.sqrt(np.eye(2) + diff)
        return stretch_mat@mat@stretch_mat

    @staticmethod
    def gen_text(_mean, _cov):
        return r"$\boldsymbol{\mu}=\begin{bmatrix}%.1f\\%.1f\end{bmatrix},\boldsymbol{\Sigma}=\begin{bmatrix}%.1f & %.1f \\ %.1f & %.1f\end{bmatrix}$"%(*_mean, *_cov.flatten())

    def __call__(self, i):
        if self.trans_type == 'rotation':
            mean_ = self.mean.copy()
            cov_ = self.rot(self.cov, self.diff*i)
        elif self.trans_type == 'stretch':
            mean_ = self.mean.copy()
            cov_ = self.stretch(self.cov, self.diff*i)
        elif self.trans_type == 'translation':
            mean_ = self.mean+self.diff*i
            cov_ = self.cov.copy()
        xysurf = mn.pdf(np.dstack((self.xx,self.yy)), mean_, cov_)

        self.surf.remove()
        self.surf = self.ax2.plot_surface(self.xx, self.yy, xysurf, cmap=self.cm,
                        rstride=1, cstride=1, vmin=self.vmin, vmax=self.vmax)
        self.mesh.set_array(xysurf)
        self.tex.set_text(self.gen_text(mean_, cov_), )
        return [self.surf,]

# %%
if __name__ == '__main__':
    # %%
    fig = plt.figure(figsize=(5,10),dpi=400,)
    spec = gridspec.GridSpec(3, 1,
        left=0.10, right=0.90, top=1.00, bottom=0.05,
        hspace=0.0,
        height_ratios=[1,5,4],
        figure=fig)
    ax1 = fig.add_subplot(spec[0])
    ax2 = fig.add_subplot(spec[1], projection='3d')
    ax3 = fig.add_subplot(spec[2], )
    # create a figure updater
    nframes=100
    ud = UpdateFigure(ax1, ax2, ax3)
    ud.cov = np.array([[1,-0.8],[-0.8,1]])
    ud.set_target('stretch', np.diag([1,4]),nframes)
    # user FuncAnimation to generate frames of animation
    anim = FuncAnimation(fig, ud, frames=nframes, blit=True)
    # save animation as *.mp4
    anim.save('2d_gaussian_2.mp4', fps=20, dpi=400, codec='libx264', bitrate=-1, extra_args=['-pix_fmt', 'yuv420p'])
# %%


import matplotlib.pyplot as plt
import numpy as np

e = np.e
pi = np.pi

def VolumeNSphere(N, R = 1):
    '''Computes the volume of a N Sphere (using Stearling aproximation) in a N dimensionl space as a function od N
    (dimension) and R (radius).'''
    term1 = 1 / (np.sqrt(N * pi))
    term2 = np.power( (2 * pi * e)/N, N/2 )
    expresion = term1 * term2 * np.power(R, N)
    return expresion

x_axis = [x for x in range(1, 50)]
Radious = [(0.9, 'orange'),
           (1, 'red'),
           (1.1, 'purple'),
           #(1.2, 'green'),
           #(1.5,'indigo'),
          # (1.75, 'black')
           ]

def DimensionRadious():
    plt.figure(figsize = (20, 14))
    for Rad in Radious:
        plt.plot(x_axis,
                 [VolumeNSphere(x, R=Rad[0]) for x in x_axis],
                 'ro',
                 linestyle='-',
                 label = 'Radius: ' + str(Rad[0]),
                 c = Rad[1]
                 )

    plt.title('Evolution of the volume of the Sphere as dimension grows for different radious')
    plt.ylabel('Volume')
    plt.xlabel('Dimension')
    plt.legend()
    plt.savefig('VolumeNSphere')
    plt.show()

def Radious1():
    plt.figure(figsize=(20, 14))
    plt.plot(x_axis,
             [VolumeNSphere(N = x, R = 1) for x in x_axis],
             'ro',
             linestyle='-',
             )
    plt.title('Volume of a N-Sphere of radius 1')
    plt.xlabel('Dimension')
    plt.ylabel('Volumen')
    plt.savefig('Volume_N_Sphere_R1')
    plt.show()

DimensionRadious()

Images and auxiliar graphics/SphereVolumeExploration.py


import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hamming, triang, blackmanharris
from scipy.fftpack import fft, ifft
import math
import sys, os, functools, time

sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../software/models/'))

import stft as STFT
import sineModel as SM
import utilFunctions as UF

M = 256
N = 256
hN = N//2
hM = M//2
fs = 44100
f0 = 5000.0
A0 = 1
ph = 1.5
t = np.arange(-hM,hM)/float(fs)
x = A0 * np.cos(2*np.pi*f0*t+ph)

w = hamming(M)
xw = x*w
fftbuffer = np.zeros(N)
fftbuffer[0:M] = xw
X = fft(fftbuffer)
mX = abs(X)
pX = np.angle(X[0:hN])

powerX = sum(2*mX[0:hN]**2)/N

mask = np.zeros(N//2)
mask[int(N*f0/fs-2*N/float(M)):int(N*f0/fs+3*N/float(M))] = 1.0
mY = mask*mX[0:hN]
powerY = sum(2*mY[0:hN]**2)/N

Y = np.zeros(N, dtype = complex)
Y[:hN] = mY * np.exp(1j*pX)
Y[hN+1:] = mY[:0:-1] * np.exp(-1j*pX[:0:-1])

y = ifft(Y)
SNR1 = -10*np.log10((powerX-powerY)/(powerX))

freqaxis = fs*np.arange(0,N/2)/float(N)
taxis = np.arange(N)/float(fs)

plt.figure(1, figsize=(9, 6))
plt.subplot(3,2,1)
plt.plot(20*np.log10(mY[:hN])-max(20*np.log10(mY[:hN])), 'r', lw=1.5)
plt.title ('mX, mY (main lobe); Hamming')
plt.plot(20*np.log10(mX[:hN])-max(20*np.log10(mX[:hN])), 'r', lw=1.5, alpha=.2)
plt.axis([0,hN,-120,0])

plt.subplot(3,2,3)
plt.plot(y[0:M], 'b', lw=1.5)
plt.axis([0,M,-1,1])
plt.title ('y (synthesis of main lobe)')

plt.subplot(3,2,5)
yerror = xw - y
plt.plot(yerror, 'k', lw=1.5)
plt.axis([0,M,-.003,.003])
plt.title ("error function: x-y; SNR = ${%d}$ dB" %(SNR1))

w = blackmanharris(M)
xw = x*w
fftbuffer = np.zeros(N)
fftbuffer[0:M] = xw
X = fft(fftbuffer)
mX = abs(X)
pX = np.angle(X[0:hN])

powerX = sum(2*mX[0:hN]**2)/N

mask = np.zeros(N//2)
mask[int(N*f0/fs-4*N/float(M)):int(N*f0/fs+5*N/float(M))] = 1.0
mY = mask*mX[0:hN]
powerY = sum(2*mY[0:hN]**2)/N

Y = np.zeros(N, dtype = complex)
Y[:hN] = mY * np.exp(1j*pX)
Y[hN+1:] = mY[:0:-1] * np.exp(-1j*pX[:0:-1])

y = ifft(Y)
SNR2 = -10*np.log10((powerX-powerY)/(powerX))

plt.subplot(3,2,2)
plt.plot(20*np.log10(mY[:hN])-max(20*np.log10(mY[:hN])), 'r', lw=1.5)
plt.title ('mX, mY (main lobe); Blackman Harris')
plt.plot(20*np.log10(mX[:hN])-max(20*np.log10(mX[:hN])), 'r', lw=1.5, alpha=.2)
plt.axis([0,hN,-120,0])

plt.subplot(3,2,4)
plt.plot(y[0:M], 'b', lw=1.5)
plt.axis([0,M,-1,1])
plt.title ('y (synthesis of main lobe)')

plt.subplot(3,2,6)
yerror2 = xw - y
plt.plot(yerror2, 'k', lw=1.5)
plt.axis([0,M,-.003,.003])
plt.title ("error function: x-y; SNR = ${%d}$ dB" %(SNR2))

plt.tight_layout()
plt.savefig('spec-sine-synthesis-lobe.png')
plt.show()


"""
Good to use when the number of features are less than 1000.
Extremely efficient than gradient decent when above condition is satisfied
"""

import numpy as np

np.random.seed(111)

'''
The data is generated adding noise to the values from  y = 1 + 0.5x1 + 3x2 -2x3 equation
The expectation of the auto encoder is to get the values  w0, w1, w2 and w3  closer to 1, 0.5, 3 and -2  respectively
'''

'''generate random x1, x2, x3 values'''
x1 = np.random.random((1, 50))[0]
x2 = np.random.random((1, 50))[0]
x3 = np.random.random((1, 50))[0]

'''define x0 as 1. To be multiplied with w0 values'''
x0 = np.ones((1, 50))[0]

'''create X_train which is has x1, x2, x3 as columns and 50 rows'''
X = np.c_[x0, x1, x2, x3]

'''get the reference y value'''
y_reference = 1 + 0.5*x1 + 3*x2 -2*x3

'''add noise to the reference y value'''
y = y_reference + np.sqrt(0.01) * np.random.random((1, 50))[0]

'''Get X transpose'''
X_t = X.transpose()

'''Normal Equation'''
W = np.linalg.pinv(X_t.dot(X)).dot(X_t).dot(y)

print('\nNormal Equation optimization completed')
print('W Expected : [1, 0.5, 3, -2]' + '  Learned : ' + str(W))


import numpy as np
from math import *

def f_of_x(func_str, endpoints):
    ''' User-entered Function

    Takes a mathematical expression as string and computes its result

    Args:
        func_str (string)     : the function to be analized [f(x)]
        endpoints (float list): coordinates of the partitions

    Returns:
        A new list of endpoints
    '''

    #result = [ (pow(1+(cos(i)), 1./3)) for i in endpoints]
    result = [ eval(func_str) for x in endpoints]

    return result

def mid_rule(interval, delta_x, f_of_x, func_str):
    ''' Midpoint Rule of Integration

    Approximates the area between a given curve f(x) and the line x=0 within a closed interval [a,b] using the Midpiont Rule

    Args:
        interval (int list): [a,b]
        my_func (function) : the function to integrate
        delta_x (float)    : length of partitions

    Returns:
        Approximate are under f(x) within the interval [a,b]

    '''
    endpoints = [ (.5* ( (i-delta_x) + i)) for i in np.arange(interval[0]+delta_x, interval[1]+delta_x, delta_x)]

    endpoints = f_of_x(func_str, endpoints)
    result = np.sum(endpoints)
    result *= delta_x

    return result

def trap_rule(interval, delta_x, endpoints):
    ''' Trapezoidal Rule of Integration

    Approximates the area between a given curve f(x) and the line x=0 within a closed interval [a,b] using the Trapezoidal Rule

    Args:
        interval  (int list): [a,b]
        my_func   (function): the function to integrate
        delta_x   (float): length of partitions
        endpoints (float list): enpoints of the partitions

    Returns:
        Approximate are under f(x) within the interval [a,b]

    '''

    # the first and last term stay unchanged. This is why the for loop only goes through indices 1 to (and including) len(endpoints)-2
    for i in range(1,len(endpoints)-1):
        endpoints[i] = endpoints[i]*2

    #endpoints = [endpoints[i]*2 for i in range(1,len(endpoints)-1)]

    result = np.sum(endpoints)
    result *= (delta_x/2)

    return result

def simpsons_rule(interval, delta_x, endpoints):
    ''' Simpson's Rule of Integration

    Approximates the area between a given curve f(x) and the line x=0 within a closed interval [a,b] using Simpson's Rule

    Args:
        interval  (int list): [a,b]
        my_func   (function): the function to integrate
        delta_x   (float): length of partitions
        endpoints (float list): enpoints of the partitions

    Returns:
        Approximate are under f(x) within the interval [a,b]

    '''

    # the first and last terms stay unchanged. This is why the for loop only goes through indices 1 to endpoints-2
    for i in range(1,len(endpoints)-1):

        # if my index is odd, multiply by four, otherwise multiply by two.
        if (i%2)==1:
            endpoints[i] = endpoints[i]*4
        else:
            endpoints[i] = endpoints[i]*2

    result = sum(endpoints)
    result *= (delta_x/3)

    return result

def compute(n, interval, func_str):
    ''' Main function

    Controls the flow of information on the file; helps minimize redundant functionalities.

    Args:
        n (int)            : number of partitions
        interval (int list): [a,b]
        func_str (str)     : string version of the formula entered by the user.

    Returns:
        The area under the cruve and within the interval using all three methods of approximate integration
    '''

    delta_x = (interval[1] - interval[0])/n

    # Bescause two different rules use the same set on endpoints, delegate this computation, so it only happens once.
    endpts = [ i for i in np.arange(interval[0], interval[1] + delta_x, delta_x)]

    #enpoints_max = fofx2(my_func, endpts)
    endpts = f_of_x(func_str, endpts)
    endpts_copy = endpts.copy()

    by_midpoint = round(mid_rule(interval, delta_x, f_of_x, func_str), 6)
    by_trapezoidal = round(trap_rule(interval, delta_x, endpts), 6)
    by_simpsons = round(simpsons_rule(interval, delta_x, endpts_copy), 6)

    return by_midpoint, by_trapezoidal, by_simpsons


import numpy as np
import timeit

MATRIX = np.array([[1, 1],
                   [1, 0]])

def log_fibonacci(num):
    result = np.copy(MATRIX)

    # We create a stack, floor dividing by 2 each time.
    # The length of the stack determines how many matrix multiplications to do
    stack = [num]
    while stack[len(stack) - 1] != 1:
        last_ele = stack[len(stack) - 1]
        stack.append(last_ele // 2 )

    # The last element, which will be one, does not provide any useful info.
    stack.pop()

    while len(stack) != 0:
        result = np.matmul(result, result)

        # If smallest integer is odd, then we multiply by MATRIX once more
        # to match the necessary power on result
        if stack.pop() % 2 == 1:
        # Do one final multiplication in this cycle if we have an odd number
            result = np.matmul(result, MATRIX)

    return result[0][1]

def linear_fibonacci(num):
    # The matrix above to the nth power will have the nth fibonacci
    # number in its second and third entries.
    result = np.copy(MATRIX)

    for _ in range(num - 1):
        result = np.matmul(result, MATRIX)

    # Could've used: 'return result[1][0]'
    return result[0][1]

def main():
    print('Input a number to find that numbered entry in the Fibonacci sequence!')
    num = int(input())
    fib = linear_fibonacci(num)
    print(f'Fibonacci number {num} is {fib}')
    log_fig = log_fibonacci(num)
    print(f'Log(n) time fibonacci number {num} is {log_fig}')

if __name__ == '__main__':
    main()


import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate

from functions import *

##### Plot basic spline ######
x = np.array([0, 0.1, 0.2, 0.5, 0.7, 1.0])

spline = cubic_spline( x, x**3, 0, 3 )

x = np.linspace(0,1,50)
y = x**3 + 0.1

yhat = spline(x)

plt.plot(x, y, label='x^3 + 0.1')
plt.plot(x, yhat, label='Spline')

plt.legend()
plt.gca().set_title('Spline approximation of cubic')
plt.savefig('spline_vs_cubic.png')
plt.close()

##### Plot periodic spline ######
f = lambda x: np.sin(2*np.pi * (x + 0.2))
interval = [0,1]

d = 0.01

# Evaluation mesh
x = np.linspace(0, 1, 10)

# Spline construction
spline = periodic_cubic_spline( x, f(x) )
# Ensure periodic
ps = lambda x: spline( x % (interval[1] - interval[0]) )
# Create this sd_ps thingy
sd_ps = lambda x: d**-2 * (ps(x-d) - 2*ps(x) + ps(x+d))

x = np.linspace( 0, 1, 100)

fig, axs = plt.subplots(2, figsize=(7,5))

axs[0].plot( x, ps(x), label='ps')
axs[1].plot( x, sd_ps(x), label='sd_ps')

for ax in axs:
    ax.legend()
axs[0].set_title('Periodic spline PS vs SD_PS')
plt.savefig('sd_vs_ps.png')
plt.close()

####### Plot Error metrics on spline ########

# Our functions
fs = [ np.sin, lambda x: x**0.5,]
f_names = ['sin(x)', 'sqrt(x)']

# Function domains
f_intervals = [[0,np.pi/2.0], [1,4]]

# Function endpoint derivatives
f_derivs = [[1, 0], [0.5, 1]]

# Subintervals for sampling
n_intervals = [4, 8, 16, 32]

fig, axs = plt.subplots(2, figsize=(7,5), sharex=True)

for i, f, derivs, interval in zip(range(len(fs)), fs, f_derivs, f_intervals):

    abs_error = []

    for n in n_intervals:

        # Create mesh
        x_spline = np.linspace(interval[0], interval[1], n)

        # Spline construction
        spline = cubic_spline(
            x_spline,
            f(x_spline),
            derivs[0],
            derivs[1]
        )

        # Spline error sample
        # We evaluate on 10x as many points as the construction mesh
        x_sample = np.linspace(interval[0], interval[1], 10*n)

        abs_error.append(
            # Take the highest value
            np.max(
                # of the absolute error
                np.abs(spline(x_sample) - f(x_sample))
            )
        )

    # Plot that error
    axs[i].plot(
        np.log10(n_intervals),
        np.log10(abs_error),
        label=f_names[i],
    )
    # Make sure name is present
    axs[i].legend()

axs[0].set_title('Log(number of intervals) vs Log(maximum error)')
fig.tight_layout()
plt.savefig('spline_error.png')


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

# using scipy.integrate.ode
def ode_sol(n0, decay_const, t_final, n_t_steps):
    intermediate_points = n_t_steps
    t3 = np.linspace(0,t_final, intermediate_points)
    n3 = np.zeros(t3.shape, float)
    def f(t, y, decay_const):
        return  - decay_const * y
    solver = ode(f).set_integrator('dopri5') # runge-kutta of order (4)5
    y0 = n0
    t0 = 0
    solver.set_initial_value(y0, t0)
    solver.set_f_params(decay_const)
    k=1
    n3[0] = n0
    while solver.successful() and solver.t < t_final:
        n3[k] = solver.integrate(t3[k])[0]
        k += 1  # k = k + 1
    n3r = n3 / n0
    return n3, n3r, t3

# Analytical solution
na, nar, ta = analytical_solution(n0=10000, decay_const=1.54e-1, t_final=20, n_t_steps=10)
# Euler method
ne, ner, te = euler_method(n0=10000, decay_const=1.54e-1, t_final=20, n_t_steps=10)
nuler_rel_error = 100*(ne-na)/na
# runge-kutta of order (4)5
n_ode, n_oder, tode = ode_sol(n0=10000, decay_const=1.54e-1, t_final=20, n_t_steps=10)
ode_rel_error = 100*(n_ode - na) / na

# Make the plot
fig = plt.figure(figsize=(8,5))
ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(ta, nar, linestyle="-", linewidth=2, label='Analytical Solution', c='#ff464a')
ax1.plot(te, ner, linestyle="--", linewidth=2, label='Euler method', c='#4881e9')
ax1.plot(tode, n_oder, linestyle="--", linewidth=2, label='Runge-Kutta of order (4)5', c='#342a77')
ax1.set_ylabel('Relative Number of $^{238}$U atoms')
ax1.set_xlabel('Time in bilion years')
ax1.legend()

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(te, euler_rel_error, linestyle="-", linewidth=2, c='#4881e9', label='Euler method')
ax2.plot(tode, ode_rel_error, linestyle="-", linewidth=2, c='#342a77', label='Runge-Kutta of order (4)5')
ax2.set_ylabel('Relative Error, in %')
ax2.set_xlabel('Time in bilion years')
ax2.legend()

fig.tight_layout()


#!/usr/bin/env python
import sys

##########################################################################
## Can you prove that De Morgan's laws work in Python?
##########################################################################

a = set(["A","B","C","D"])
b = set(["C","D","E","F"])
sample_space = set(["A","B","C","D","E","F","G"])

## The complement of the union of two sets is the same as the intersection of their complements
part1 = sample_space.difference(a.union(b))
part2 = sample_space.difference(b).intersection(sample_space.difference(a))
print(part1==part2)

## The complement of the intersection of two sets is the same as the union of their complements
part1 = sample_space.difference(b).union(sample_space.difference(a))
part2 = sample_space.difference(a.intersection(b))
print(part1==part2)

## A = (X < 90) and B  is between 90 and 95

# The union or  (A \cap C) = (250 \leq chol \leq 280)

# `P(250 \leq chol \leq 299) + P(chol \geq 300) = 0.2 + 0.1 = 0.3`. chol \leq 280)

##########################################################################
## combinations and permutations
##########################################################################
from math import factorial
from itertools import combinations,permutations
from scipy.misc import comb

## We have sampler plates that hold 4 beers.  How many different ways can we combine these beers?
lefthand_beers = ["Milk Stout", "Good Juju", "Fade to Black", "Polestar Pilsner"]
lefthand_beers += ["Black Jack Porter", "Wake Up Dead Imperial Stout","Warrior IPA"]
n = len(lefthand_beers)
k = 4

def comb(n, k):
    return factorial(n) / (factorial(k) * factorial(n - k))

print("There are %s combinations"%comb(n,k))

## Print a list of these pairs so we can identify the bad ones?
for c in combinations(lefthand_beers,4):
    print(c)

## on a team of 12 baseball players how many batting orders?
def permu(n,k):
    return factorial(n) / factorial(n - k)

print(permu(12,9))

##########################################################################
## probability
##########################################################################

## probability of a queen
p_queen = 1.0/52 + 1.0/52 + 1.0/52 + 1.0/52
p_queen_or_spade = 4.0/52 + 13/52 - 1.0/52

## conditional probability problem
#print("prob of tails: %s"%1 - ((1./3 + (1./3 * 1./4)) / (1. / 2)))
print("prob of heads: %s"%((1./3 + (1./3 * 1./4)) / (1. / 2)))

import random
import pandas as pd

coins = ['HH', 'HT', 'TT']
results = []
for i in range(10):
    coin = random.choice(coins)
    results.append([random.choice(coin) for j in [1,2]])

print(results)

df = pd.DataFrame(results, columns=['first', 'second']) == 'H'
#print df
df.groupby('first').mean()

sys.exit()


#########################
##                     ##
## Irving Gomez Mendez ##
##     May 09, 2021    ##
##                     ##
#########################
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

import sympy as sym
from sympy.functions import exp
from scipy.optimize import least_squares
from scipy.stats import norm, t

alpha = 0.05

dat = pd.read_csv('ChickWeight.csv')
dat = dat.drop(dat.columns[0], axis=1)
dat['Diet'] = dat['Diet'].astype('category')

plt.figure(figsize=(10,7.5))
sns.scatterplot(x=dat['Time'], y=dat['weight'], hue=dat['Diet'], s=50)

plt.figure(figsize=(10,7.5))
sns.lineplot(
    x=dat['Time'], y=dat['weight'],
    hue=dat['Diet'], style=dat['Diet']
)
## Let's select just the diet=3
dat_example = dat.loc[dat['Chick']==3, ['weight','Time']].reset_index(drop=True)

plt.figure(figsize=(10,7.5))
sns.lineplot(x=dat_example['Time'], y=dat_example['weight'])
sns.scatterplot(x=dat_example['Time'], y=dat_example['weight'])
# We are going to model using a logistic growth
M, r, b, t = sym.symbols('M r b t')
f = M/(1+exp(-r*(t-b)))

sym.diff(f, M)
sym.diff(f, r)
sym.diff(f, b)

def f(t, M, r, b):
    return(M/(1+np.exp(-r*(t-b))))

def F_1(t,M,r,b):
    return(1/(1+np.exp(-r*(t-b))))

def F_2(t,M,r,b):
    return((M*(t-b)*np.exp(-r*(t-b)))/(1+np.exp(-r*(t-b)))**2)

def F_3(t,M,r,b):
    return((-M*r*np.exp(-r*(t-b)))/(1+np.exp(-r*(t-b)))**2)

y = dat_example['weight']
X = dat_example['Time']
n = len(y)
p = 3

# Initialize
M0 = y.max()
r0 = 0.1
b0 = 15

tt = [M0,r0,b0]

tolm   = 1e-6       # tolerance (minimum norm of the difference of the betas)
iterm  = 100        # maximum number of iterations
tolera = 1          # initialize tolera
itera  = 0          # initialize ittera
histo  = tt          # initialize beta upgrade

while((tolera > tolm) and (itera < iterm)):
    F_matrix = np.vstack([
        F_1(dat_example['Time'], tt[0], tt[1], tt[2]),
        F_2(dat_example['Time'], tt[0], tt[1], tt[2]),
        F_3(dat_example['Time'], tt[0], tt[1], tt[2])
    ])
    F_matrix = F_matrix.T
    y_hat = f(dat_example['Time'], tt[0], tt[1], tt[2])
    delta = np.linalg.solve(F_matrix.T @ F_matrix, F_matrix.T @ (y-y_hat))
    tt = tt + delta
    tolera = np.sqrt(sum(delta**2))
    histo  = np.vstack([histo, tt])
    itera  = itera+1

histo

## Using scipy least squares
def logistic_gowth(theta, t):
    return theta[0] / (1 + np.exp(- theta[1] * (t - theta[2])))

def fun(theta):
    return logistic_gowth(theta, X) - y

theta0 = [M0,r0,b0]
log_growth = least_squares(fun, theta0)
log_growth.x

F_matrix

log_growth.jac

# Getting confidence intervals
hat_sigma2 = sum((y-y_hat)**2/(n-p))
var_params = np.diag(hat_sigma2 * np.linalg.inv(F_matrix.T @ F_matrix))
var_params

# Let's get the significance of the estimators
se_params = np.sqrt(var_params)
z_score = tt/se_params
p_value = 1-norm.cdf(np.abs(z_score))

z_score
p_value

# predictions
y_hat = logistic_gowth(theta=log_growth.x, t=X)

plt.figure(figsize=(10,7.5))
plt.plot(X, y, 'o-', label='data')
plt.plot(X, y_hat, 'o-', label='estimated')
plt.xlabel('time')
plt.ylabel('weight')
plt.title("Weight of a chicken over time")
plt.legend(loc='upper left')

low_pred = y_hat - np.sqrt(hat_sigma2) * t.ppf(1-alpha/2, n-p)
upp_pred = y_hat + np.sqrt(hat_sigma2) * t.ppf(1-alpha/2, n-p)

plt.figure(figsize=(10,7.5))
plt.fill_between(X, low_pred, upp_pred, facecolor='green', alpha=0.5, label='Prediction interval')
plt.plot(X, y, 'o-', label='data')
plt.plot(X, y_hat, 'o-', label='estimated')
plt.xlabel('time')
plt.ylabel('weight')
plt.title("Weight of a chicken over time")
plt.legend(loc='upper left')

###

import numpy,from scipy,import sympy,from sympy


# -*- coding: utf-8 -*-
"""
Created on Wed Jun  7 14:17:38 2017

@author: picku
"""

import numpy as np
from scipy import linalg
from collections import OrderedDict
import matplotlib.pyplot as plt
from matplotlib import style
style.use('ggplot')

class PolynomialRegression(object):
        """PolynomialRegression

        Parameters
        ------------
        x_pts : 1-d numpy array, shape = [n_samples,]
        y_pts : 1-d numpy array, shape = [n_samples,]

        Attributes
        ------------
        theta : 1-d numpy array, shape = [polynomial order + 1,]
            Ceofficients of fitted polynomial, with theta[0] corresponding
            to the intercept term

        method : str , values = 'normal_equation' | 'gradient_descent'
            Method used for finding optimal values of theta

        If gradient descent method is chosen:

            costs : 1-d numpy array,
                Cost function values for every iteration of gradient descent

            numIters: int
                Number of iterations of gradient descent to be performed

        References
        ------------
        https://en.wikipedia.org/wiki/Polynomial_regression
        """

    def __init__(self, x, y):

        self.x = x
        self.y = y

    def standardize(self,data):
        """ Peform feature scaling
        Parameters:
        ------------
        data : numpy-array, shape = [n_samples,]

        Returns:
        ---------
        Standardized data
        """

        return (data - np.mean(data))/(np.max(data) - np.min(data))

    def hypothesis(self, theta, x):
        """ Compute hypothesis, h, where
        h(x) = theta_0*(x_1**0) + theta_1*(x_1**1) + ...+ theta_n*(x_1 ** n)

        Parameters:
        ------------
        theta : numpy-array, shape = [polynomial order + 1,]
        x : numpy-array, shape = [n_samples,]

        Returns:
        ---------
        h(x) given theta values and the training data

        """
        h = theta[0]
        for i in np.arange(1, len(theta)):
            h += theta[i]*x ** i
        return h

    def computeCost(self, x, y, theta):
        """ Compute value of cost function J

        Parameters:
        ------------
        x : numpy array, shape = [n_samples,]
        y : numpy array, shape = [n_samples,]

        Returns:
        ---------
        Value of cost function J at value theta given the training data

        """
        m = len(y)
        h = self.hypothesis(theta, x)
        errors = h-y

        return (1/(2*m))*np.sum(errors**2)

    def fit(self, method = 'normal_equation', order = 1, tol = 10**-3, numIters = 20, learningRate = 0.01):

        """Fit theta to the training data

        Parameters
        -----------
        method: string, values = 'normal_equation' | 'gradient_descent'
             Indicates method for which polynomial regression will be performed

        order: int, optional
             Order of polynomial fit. Defaults to 1 (linear fit)

        numIters: int, optional
             Number of iterations of gradient descent to be performed

        learningRate: float, optional

        tol : float, optional
            Value indicating the cost value (J(theta)) at which
            gradient descent should terminated. Defaults to 10 ** -3

        Returns:
        -----------
        self : object

        """

        if method == 'normal_equation':
            d = {}
            d['x' + str(0)] = np.ones([1,len(x_pts)])[0]
            for i in np.arange(1, order+1):
                d['x' + str(i)] = self.x ** (i)

            d = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
            X = np.column_stack(d.values())

            theta = np.matmul(np.matmul(linalg.pinv(np.matmul(np.transpose(X),X)), np.transpose(X)), self.y)

        elif method == 'gradient_descent':

            d = {}
            d['x' + str(0)] = np.ones([1,len(x_pts)])[0]
            for i in np.arange(1, order+1):
                d['x' + str(i)] = self.standardize(self.x ** (i))

            d = OrderedDict(sorted(d.items(), key=lambda t: t[0]))
            X = np.column_stack(d.values())

            m = len(self.x)
            theta = np.zeros(order + 1)
            costs = []
            for i in range(numIters):

                h = self.hypothesis(theta, self.x)
                errors = h-self.y
                theta += -learningRate * (1/m)*np.dot(errors, X)
                cost = self.computeCost(self.x, self.y, theta)
                costs.append(cost)
                #tolerance check
                if cost < tol:
                    break

            self.costs = costs
            self.numIters = numIters

        self.method = method
        self.theta = theta

        return self

    def plot_predictedPolyLine(self):
        """Plot predicted polynomial line using values of theta found
        using normal equation or gradient descent method

        Returns
        -----------
        matploblib figure
        """
        plt.figure()
        plt.scatter(self.x, self.y, s = 30, c = 'b')
        line = self.theta[0] #y-intercept
        label_holder = []
        label_holder.append('%.*f' % (2, self.theta[0]))
        for i in np.arange(1, len(self.theta)):
            line += self.theta[i] * self.x ** i
            label_holder.append(' + ' +'%.*f' % (2, self.theta[i]) + r'$x^' + str(i) + '$')

        plt.plot(self.x, line, label = ''.join(label_holder))
        plt.title('Polynomial Fit: Order ' + str(len(self.theta)-1))
        plt.xlabel('x')
        plt.ylabel('y')
        plt.legend(loc = 'best')

    def plotCost(self):
        """Plot number of gradient descent iterations verus cost function, J,
        values at values of theta

        Returns
        -----------
        matploblib figure
        """
        if self.method == 'gradient_descent':
            plt.figure()
            plt.plot(np.arange(1, self.numIters+1), self.costs, label = r'$J(\theta)$')
            plt.xlabel('Iterations')
            plt.ylabel(r'$J(\theta)$')
            plt.title('Cost vs Iterations of Gradient Descent')
            plt.legend(loc = 'best')
        else:
            print('plotCost method can only be called when using gradient descent method')


import math

# Taylor expansion at n=1 2x
# Taylor expansion at n=3 2x - 4(x**3)/3
# Taylor expansion at n=5 2x - 4(x**3)/3 + 4(x**5)/15

import sympy as sy
import numpy as np
from sympy.functions import sin, cos
import matplotlib.pyplot as plt
from sympy.parsing.sympy_parser import *

plt.style.use("ggplot")

# Define the variable and the function to approximate
x = sy.Symbol('x')
f = sin(2 * x)

# Factorial function
def factorial(n):
    if n <= 0:
        return 1
    else:
        return n * factorial(n - 1)

# Taylor approximation at x0 of the function 'function'
def taylor(function, x0, n):
    i = 0
    p = 0
    while i <= n:
        p = p + (function.diff(x, i).subs(x, x0)) / (factorial(i)) * (x - x0) ** i
        i += 1
    return p

# Plot results
def plot():
    x_lims = [0, math.pi]
    x1 = np.linspace(x_lims[0], x_lims[1], 800)
    y1 = []
    # Approximate up until 5 starting from 1 and using steps of 2
    lastFunc = 0
    for j in range(1, 6, 2):
        func = taylor(f, 0, j)
        lastFunc = func
        print('Taylor expansion at n=' + str(j), func)
        for k in x1:
            y1.append(func.subs(x, k))
        plt.plot(x1, y1, label='order ' + str(j))
        y1 = []
    # Plot the function to approximate (sine, in this case)
    expr = parse_expr(str(lastFunc))
    realValue = np.sin(math.pi / 2)  # sin(2x) => x=pi/2
    taylorValue = expr.subs(x, math.pi / 4)

    print('\n\nTaylor expansion sin of 2x=', lastFunc)
    print('Taylor expansion result sin of 2x ~=', taylorValue)
    print('Real value sin of 2x ~=', realValue)

    absoluteError = abs(realValue - taylorValue)
    realtiveError = absoluteError / realValue
    percentageError = realtiveError * 100

    print('\n\nAbsolute Error: ', absoluteError)
    print('Realtive Error: ', realtiveError)
    print('Percentage Error: "%"', percentageError)

    plt.plot(x1, np.sin(x1), label='sin of 2x')
    plt.xlim(x_lims)
    plt.ylim([-math.pi, math.pi])
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend()
    plt.grid(True)
    plt.title('Taylor series approximation')
    plt.show()

plot()


import scipy as sp
import numpy as np
import scipy.linalg as la
import numpy.linalg as la
import matplotlib.pyplot as plt
'''
A = sp.array([[2,1],[4,2.01]])
la.norm(A)*la.norm(la.pinv(A))
np.linalg.cond(A)
'''
#problem 2

def hilbertCond(n):
	'''
	I'm just going to construct teh Hilbert Matrx and calculate the
	condition from there. If there's a better way, I don't know it.
	'''
	def hilbert(n):
		return sp.array([[1./(x+y-1) for x in range(1,n+1)] for y in range(1,n+1)])
	return np.linalg.cond (hilbert(n))
'''
I don't understand the growth curve. When I plot n in ranges like 1 to 21 and 1 to 100 I get strange peaks, but the condition is still obviously growing rapidly. When I plot from 1 to 10 though, I get no peaks. Is this what I'm supposed to get?
'''
if __name__ == "__main__":
	N = sp.arange(1,21)
	plt.plot(N,sp.vectorize(hilbertCond)(N))
	plt.show()


import numpy as np
import scipy.linalg as la

#######################################################################
# These functions are intended to test the accuracy of the various    #
# scipy linear algebra functions.                                     #
#                                                                     #
#       Colin McKinley | September 2018                               #
#######################################################################

#Multiply random matrix by its inverse and calculate norm of residual with
#identity matrix.
def myinvcheck(n):
    A = np.random.normal(0, 1, (n, n))
    B = np.linalg.inv(A)
    combined = np.matmul(A,B)
    I = np.eye(n)
    residual = combined - I
    return np.linalg.norm(residual,2)

#Create a random nxn matrix A, random nx1 b, and solve Ax=b. Return norm of
#residual
def mysolvecheck(n):
    A = np.random.normal(0, 1, (n, n))
    b = np.random.normal(0, 1, (n, 1))
    x = np.linalg.solve(A, b)
    return np.linalg.norm(np.matmul(A, x) - b, 2)

#PLU decomposition to solve Ax=b and compare with other solvers
def pluDecomp(A,b):
    #Inputs:
    #A, matrix
    #b, right hand vector
    #Outputs:
    #x1, PLU Solution
    #r1, norm of residual x1
    #x2, solution using linalg.solve
    #r2 norm of residual x2

    (P, L, U) = la.lu(A)
    D = np.matmul(P, b)
    Y = la.solve(L, D)
    x1 = la.solve(U, Y)
    r1 = la.norm(np.matmul(A, x1) - b, 2)
    x2 = la.solve(A, b)
    r2 = la.norm(np.matmul(A, x2) - b, 2)
    print "\nr1:", r1
    print "x1: \n", x1
    print "\nr2:", r2
    print "x2:\n", x2

if __name__ == "__main__":
    n = 7
    print("\nInverse Check: ")
    print(myinvcheck(n))
    print("\nla.solve check: ")
    print(myinvcheck(n))
    print("\nPLU Vs. la.solve:")
    print "="*20
    A = np.random.normal(0, 1, (n, n))
    b = np.random.normal(0, 1, (n, 1))
    pluDecomp(A, b)

    A = np.array([[5, 7],
                  [9, 3]])
    b = np.array([[16],
                  [9]])
    print("\nTesting PLU again")
    print "="*20
    pluDecomp(A, b)


'''
Computing the covariance
100xp
The covariance may be computed using the Numpy function np.cov(). For example,
we have two sets of data x and y, np.cov(x, y) returns a 2D array where entries
[0,1] and [1,0] are the covariances. Entry [0,0] is the variance of the data in x,
and entry [1,1] is the variance of the data in y. This 2D output array is called
the covariance matrix, since it organizes the self- and covariance.

To remind you how the I. versicolor petal length and width are related, we include
the scatter plot you generated in a previous exercise.

Instructions
-Use np.cov() to compute the covariance matrix for the petal length
(versicolor_petal_length) and width (versicolor_petal_width) of I. versicolor.
-Print the covariance matrix.
-Extract the covariance from entry [0,1] of the covariance matrix. Note that by symmetry,
entry [1,0] is the same as entry [0,1].
-Print the covariance.
'''
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

versicolor_petal_length = np.array([4.7,  4.5,  4.9,  4.,  4.6,  4.5,  4.7,  3.3,  4.6,  3.9,  3.5,
                                    4.2,  4.,  4.7,  3.6,  4.4,  4.5,  4.1,  4.5,  3.9,  4.8,  4.,
                                    4.9,  4.7,  4.3,  4.4,  4.8,  5.,  4.5,  3.5,  3.8,  3.7,  3.9,
                                    5.1,  4.5,  4.5,  4.7,  4.4,  4.1,  4.,  4.4,  4.6,  4.,  3.3,
                                    4.2,  4.2,  4.2,  4.3,  3.,  4.1])

versicolor_petal_width = np.array([1.4,  1.5,  1.5,  1.3,  1.5,  1.3,  1.6,  1.,  1.3,  1.4,  1.,
                                   1.5,  1.,  1.4,  1.3,  1.4,  1.5,  1.,  1.5,  1.1,  1.8,  1.3,
                                   1.5,  1.2,  1.3,  1.4,  1.4,  1.7,  1.5,  1.,  1.1,  1.,  1.2,
                                   1.6,  1.5,  1.6,  1.5,  1.3,  1.3,  1.3,  1.2,  1.4,  1.2,  1.,
                                   1.3,  1.2,  1.3,  1.3,  1.1,  1.3])

# Compute the covariance matrix: covariance_matrix
covariance_matrix = np.cov(versicolor_petal_length, versicolor_petal_width)

# Print covariance matrix
print(covariance_matrix)

# Extract covariance of length and width of petals: petal_cov
petal_cov = covariance_matrix[0, 1]

# Print the length/width covariance
print(petal_cov)


# -*- coding: utf-8 -*-
"""Copy of 01-NumPy Arrays.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1QdhliV0NkU0TrIPepfCXMXb0ekYXwv0s

#Introduction to NumPy

![alt text](https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/NumPy_logo.svg/1200px-NumPy_logo.svg.png)

NumPy (or Numpy) is a Linear Algebra Library for Python. NumPy is a Python package. It stands for 'Numerical Python'. It is a library consisting of multidimensional array objects and a collection of routines for processing of array.

Numpy is also incredibly fast, as it has bindings to C libraries.

## **About iPython Notebooks**

iPython Notebooks are interactive coding environments embedded in a webpage. You will be using iPython notebooks in this class. You only need to write code between the ### START CODE HERE ### and ### END CODE HERE ### comments. After writing your code, you can run the cell by either pressing "SHIFT"+"ENTER" or by clicking on "Run Cell" (denoted by a play symbol) in the left bar of the cell.

**In this notebook you will learn -**
* Numpy arrays
* Numpy Selecting and Indexing
* Numpy Operations

## Importing NumPy

To import NumPy in Colaboratory under the name **np** type the following:
"""

import numpy as np

"""Numpy has many built-in functions and capabilities. We won't cover them all but instead we will focus on some of the most important aspects of Numpy: vectors, arrays, matrices, and number generation. Let's start with arrays.

# Numpy Arrays

NumPy arrays are the main way we will use Numpy throughout the course. Numpy arrays essentially come in two flavors: vectors and matrices. Vectors are strictly 1-d arrays and matrices are 2-d.

**Note:** A matrix can still have only one row or one column.

## Creating NumPy Arrays

### From a Python List

We can create an array by directly converting a list or list of lists:
"""

my_list = [1,2,3]
my_list

np.array(my_list)

my_matrix = [[1,2,3],[4,5,6],[7,8,9]]      # list of lists
my_matrix

np.array(my_matrix)      # 3x3 matrix

"""**Exercise 1.1:**

Create a list named **odd_list** containing all odd numbers from 0 to 10 and print it.
"""

### START CODE HERE ### (2 lines of code)

odd_list=[1,3,5,7,9]
odd_list
### END CODE HERE ###

"""**Expected Output:**

[1, 3, 5, 7, 9]

**Exercise 1.2:**

Create a 2x2 matrix of all even numbers from 1 to 9.
"""

### START CODE HERE ###

m=([2,4],[6,8])                   #create list of 2 lists
m                                 #print the list
np.array(m)                       #convert the list into array

### END CODE HERE ###

"""**Expected Output:**

array[[2, 4],

     [6,8]]

#Built-in Methods

There are lots of built-in ways to generate Arrays.

### arange

Return evenly spaced values within a given interval.
"""

np.arange(0,10)     #creates an array from 0 to 9

"""**Note:**
The first parameter is the starting value and second parameter (excluding) is the ending value.
"""

np.arange(0,11,2)

"""**Note:**
The third parameter is the step value.

**Exercise 1.3:**

Create an array of odd numbers between 30 to 40 using **arange** function.
"""

### START CODE HERE ###
np.arange(31,40,2)

### END CODE HERE ###

"""**Expected Output:**

[31, 33, 35, 37, 39]

### zeros and ones

Generate arrays of zeros or ones
"""

np.zeros(3)      #creates an array of 3 zeros

np.zeros((5,5))      #creates a 5x5 matrix of zeros

np.ones(3)      #creates an array of 3 ones

np.ones((3,3))      #creates a 5x5 matrix of ones

"""### Linspace

 Returns number spaces evenly w.r.t interval. Similiar to arange but instead of step it uses sample number.

numpy.linspace(start, stop, num = 50, endpoint = True, retstep = False, dtype = None,axis = 0)

 Parameters :

* start  : [optional] start of interval range. By default start = 0

* stop   : end of interval range, unless endpoint is set to False.In that case, the sequence consists of all but the last.

* num    : [int, optional] No. of samples to generate.Default is 50. Must be non-negative.

* endpoint   :  [bool, optional] If True, stop is the last sample. Otherwise, it is not included. Default is True.

* restep :  [bool, optional] If True, return (samples, step,)where step is the spacing between samples. By deflut restep = False.

* dtype  : [dtype, optional] The type of the output array. If dtype is not given, infer the data type from the other input arguments.

*axis  :  [int, optional] The axis in the result to store the samples. Relevant only if start or stop are array-like. By default (0), the samples will be along a new axis inserted at the beginning. Use -1 to get an axis at the end.
"""

np.linspace(0,10,5)                 #5 intervals generated between 0 and 10

np.linspace(0,10,50)                #50 intervals generated between 0 and 10

"""### eye

Creates an identity matrix
"""

np.eye(4)

"""###Random

Numpy also has lots of ways to create random number arrays:

### rand
Create an array of the given shape and populate it with
random samples from a uniform distribution
over ``[0, 1]``.
"""

np.random.rand(2)

np.random.rand(5,5)

"""### randn

Return a sample (or samples) from the "standard normal" distribution. Unlike rand which is uniform:
"""

np.random.randn(2)

np.random.randn(5,5)

"""### randint
Return random integers from `low` (inclusive) to `high` (exclusive).
"""

np.random.randint(1,100)

np.random.randint(1,100,10)      #print 10 values between 1 to 100

"""**Note:**
The third parameter indicates the number of values to be printed.

# Array Attributes and Methods

Let's discuss some useful attributes and methods or an array:
"""

arr = np.arange(25)
ranarr = np.random.randint(0,50,10)

arr

ranarr

"""### Reshape
Returns an array containing the same data with a new shape.
"""

arr.reshape(5,5)

"""### max, min, argmax, argmin

These are useful methods for finding max or min values. Or to find their index locations using argmin or argmax
"""

ranarr

ranarr.max()      #prints the maximum value in the array

ranarr.argmax()      #prints the index of the max value

ranarr.min()      #prints the minimum value in the array

ranarr.argmin()      #prints the index of the max value

"""### Shape

Shape is an attribute that arrays have (not a method). It gives you the shape if the array(M x N).
"""

# Vector
arr.shape

# Notice the two sets of brackets
arr.reshape(1,25)

arr.reshape(1,25).shape

arr.reshape(25,1)

arr.reshape(25,1).shape

"""### dtype

You can also grab the data type of the object in the array:
"""

arr.dtype

"""**Exercise 1.4:**

Create a random array of 15 numbers from 10 to 40. Reshape the same array to 3x5 matrix. Also print the min and max numbers from that array list.
"""

### START CODE HERE ###
arr = np.random.randint(10,40,15)      #create random array
arr                                    #print the array
arr.reshape(3,5)                       #reshape the array

a = arr.min()                          #get the minimum value
a

b = arr.max()                         #get the maximum value
b
### END CODE HERE ###

"""# Great Job!"""


import numpy as np

def MAE(X, T):
    """Mean Absolute Error

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return np.sum(np.absolute(X-T))/X.shape[0]

def MSE(X, T):
    """Mean Square Error

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return np.sum((X-T)**2)/X.shape[0]

def RMSE(X, T):
    """Root Mean Square Error

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return np.sqrt(np.sum((X-T)**2)/X.shape[0])

def CEL(X, T):
    """Cross Entropy Loss
    X: output of softmax function
    T: training data in the form of one-hot vector

    ## Output
        Loss of the whole mini-batch (one value will be returned)
    """
    return -np.sum(T*np.log(np.absolute(X+10e-7)))/X.shape[0]