import numpy as np
from sympy import simplify, integrate, zeros, S, Matrix, symbols, pi, cos, sin
from .funcs_aproximacion import producto_asecas

def producto_escalar_trigono(f, g, var=symbols('x'), a=-pi, b=pi, I=None, numeric=False):
    """Aplica el producto escalar <f,g> = 1/(2pi) ∫_[-pi]^[pi] f.g

    Args:
        f (funcion): f
        g (funcion): g
        var (variable): variable de integración
        a (int, optional): limite inferior de integracion. Defaults to 0.
        b (int, optional): limite superior de integracion. Defaults to 1.
        I (list, optional): Si no es None, lista de valores sobre los que hacer un sumatorio discreto. Defaults to None.
        numeric (bool, optional): si True, realiza una aproximación numérica de la integral usando un método de sympy.

    Returns:
        funcion, float: Valor del producto escalar. Se devuelve como funcion si tiene variables.
    """
    prod = producto_asecas(f, g, var, a, b, I, numeric)
    return simplify(prod / (2 * pi))

def coefs_fourier(f, var=symbols('x'), I=[0, 1], n_coefs=2):
    """Genera los coeficientes de la serie de fourier. Esta es la versión continua, donde los coeficientes se calculan usando la expresión de la integral.

    Args:
        f (funcion): Función a aproximar
        var (variable, optional): Variable de la función. Defaults to symbols('x').
        I (list, optional): Intervalo de aproximación de la función. Defaults to [0, 1].
        n_coefs (int, optional): Número de coeficientes de la serie a generar. Defaults to 2.

    Returns:
        dict_coefs: {a_0, a_1, b_1, a_2, b_2, ...}
    """
    dict_coefs = {}
    dict_coefs['a0'] = simplify(1 / pi * integrate(f, (var, I[0], I[1])))
    for i in range(1, n_coefs):
        dict_coefs[f'a{i}'] = simplify(1 / pi * integrate(f * cos(i * var), (var, I[0], I[1])))
        dict_coefs[f'b{i}'] = simplify(1 / pi * integrate(f * sin(i * var), (var, I[0], I[1])))

    return dict_coefs

def coefs_fourier_discr(f, var=symbols('x'), I=[0, 1], n_coefs=2, m=10):
    """Genera los coeficientes de la serie de fourier. Esta es la versión donde la integral se aproxima como un sumatorio discreto de m términos sobre I.

    Args:
        f (funcion): Función a aproximar
        var (variable, optional): Variable de la función. Defaults to symbols('x').
        I (list, optional): Intervalo de aproximación de la función. Defaults to [0, 1].
        n_coefs (int, optional): Número de coeficientes de la serie a generar. Defaults to 2.
        m (int, optional): Número de elementos en los que dividir I para el sumatorio.

    Returns:
        dict_coefs: {a_0, a_1, b_1, a_2, b_2, ...}
    """
    dict_coefs = {}
    lista_xk = np.linspace(I[0], I[1], 2 * m)

    dict_coefs['a0'] = np.sum([f.subs(var, xk) * cos(0 * xk) for xk in lista_xk]) / m
    for i in range(1, n_coefs):
        dict_coefs[f'a{i}'] = np.sum([f.evalf(subs={var: S(xk)}) * cos(S(i) * xk) for xk in lista_xk]) / m
        dict_coefs[f'b{i}'] = np.sum([f.evalf(subs={var: S(xk)}) * sin(S(i) * xk) for xk in lista_xk]) / m

    return dict_coefs

def serie_fourier(f, var=symbols('x'), I=[0, 1], n_coefs=3, discreto=False, m=10):
    """Genera la serie de Fourier para la función f sobre un intervalo.

    Args:
        f (funcion): Función a aproximar
        var (variable, optional): Variable de la función. Defaults to symbols('x').
        I (list, optional): Intervalo de aproximación de la función. Defaults to [0, 1].
        n_coefs (int, optional): Número de coeficientes de la serie a generar. Defaults to 2.
        discreto (bool, optional): Si True, genera una aproximación discreta de los coeficientes empleando m términos.
        m (int, optional): Número de elementos en los que dividir I para el sumatorio.

    Returns:
        funcion: Función polinómica con la serie de Fourier.
    """
    if discreto:
        dict_coefs = coefs_fourier_discr(f, var, I, n_coefs, m)
    else:
        dict_coefs = coefs_fourier(f, var, I, n_coefs)

    serie_fourier = dict_coefs['a0'] / 2
    for i in range(1, n_coefs):
        serie_fourier += dict_coefs[f'a{i}'] * cos(i * var) + dict_coefs[f'b{i}'] * sin(i * var)

    return simplify(serie_fourier)


import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import numpy as np
import random
import torch
import torch.optim as optim
import utils
import math

print('定义 adagrad')
eta = 0.4

def adagrad_2d(x1, x2, s1, s2):
    g1, g2, eps = 0.2 * x1, 4 * x2, 1e-6
    s1 += g1 ** 2
    s2 += g2 ** 2
    x1 -= eta / math.sqrt(s1 + eps) * g1
    x2 -= eta / math.sqrt(s2 + eps) * g2
    return x1, x2, s1, s2

def f_2d(x1, x2):
    return 0.1 * x1 ** 2 + 2 * x2 ** 2

print('lr= 0.4 的轨迹')
utils.show_trace_2d(f_2d, utils.train_2d(adagrad_2d))
print('lr= 2.0 的轨迹，更快逼近最优解')
eta = 2.0
utils.show_trace_2d(f_2d, utils.train_2d(adagrad_2d))

print('自行实现 Adagrad')

features, labels = utils.get_nasa_data()

def init_adagrad_states():
    s_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
    s_b = torch.zeros(1, dtype=torch.float32)
    return (s_w, s_b)

def adagrad(params, states, hyperparams):
    eps = 1e-6
    for p, s in zip(params, states):
        s.data += (p.grad.data ** 2)
        p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)

print('Adagrad 进行优化')
utils.train_opt(adagrad, init_adagrad_states(), {'lr': 0.1}, features, labels)

print('简洁实现')
utils.train_opt_pytorch(optim.Adagrad, {'lr': 0.1}, features, labels)


"""
Evaluate the integral of x * exp(-a * x) with a = 2 on [0,1].
    Determine the relative error as a function of step size N and
    find N such that the approximate integral isaccurate to
    five significant digits

Solution to Problem Set 2, Problem 1

To run:
    python trap_rule_2_1.py

"""

import numpy as np
import matplotlib.pyplot as plt

#original trapezoidal rule; loop from 1 to n - 1
def trapezoidalRule(func, a, b, n, *P):
        h = (b - a) / n
        Int_part1 = func(a, *P) + func(b, *P)
        Int_part2 = 0
        for i in range(1, n):
            x = a + i * h
            Int_part2 += func(x, *P)
        I = (h / 2) * (Int_part1 + 2 * Int_part2)
        return I
#for part 4 of problem 1: modify step d to loop from 0 to n
def trapezoidalRule_modified(func, a, b, n, *P):
        h = (b - a) / n
        Int_part1 = func(a, *P) + func(b, *P)
        Int_part2 = 0
        for i in range(0, n+1):
            x = a + i * h
            Int_part2 += func(x, *P)
        I = (h / 2) * (Int_part1 + 2 * Int_part2)
        return I

#the function to be integrated
def integrand(x,alpha):
    return x*np.exp(-alpha*x)
#the indefinite integral
def int_indef(x,alpha):
    return ((-x/alpha) - (1./alpha**2)) * np.exp(-alpha*x)

alpha = 2.0
#integral boundaries
xmax = 1.0
xmin = 0.0

#analytic result
int_analytic = int_indef(xmax,alpha)-int_indef(xmin,alpha)
#build the n vs relErr arrays
N = np.array([50, 100, 200, 400])
rel_err = np.zeros(4)
for i in range(N.shape[0]):
    int_trap = trapezoidalRule(integrand, xmin, xmax, N[i], alpha)
    rel_err[i] = np.abs(int_trap - int_analytic) / int_analytic

#fit a line to log(N), log(rel_err) to get slope/convergence rate
m, b = np.polyfit(np.log(N), np.log(rel_err), 1)
print("convergence rate regular trapezoidal rule %.3f" % m)
plt.xlabel("log(number of steps)")
plt.ylabel("log(relative Error)")
plt.yscale('log')
plt.xscale('log')
#plot relative error
plt.plot(N, rel_err, marker ='x',label='Regular Trapezoidal Rule',color ='b')
# and best fit
plt.plot(N,np.exp(np.log(N)*m+b),linestyle = "--",color ='b')

#recalcuate the n and relErr arrays, but with the modified step d
rel_err_mod = np.zeros(4)
for i in range(N.shape[0]):
    int_trap = trapezoidalRule_modified(integrand, xmin, xmax, N[i], alpha)
    rel_err_mod[i] = np.abs(int_trap - int_analytic) / int_analytic

#fit a line to log(N), log(rel_err) to get slope/convergence rate
m, b = np.polyfit(np.log(N), np.log(rel_err_mod), 1)
print("convergence rate modified trapezoidal rule %.3f" % m)

#plot results
plt.plot(N, rel_err_mod, marker ='o',label='Modified Trapezoidal Rule',color ='r')
# and best fit
plt.plot(N,np.exp(np.log(N)*m+b),linestyle = "--",color ='r')
plt.legend()
plt.show()

#find an n accurate to at least five significant digits
N_5 = 10
int_trap = trapezoidalRule(integrand, xmin, xmax, N_5, alpha)
print("finding N_5...")
while np.abs(int_trap - int_analytic) / int_analytic >= 5.e-6:
    N_5 += 1
    int_trap = trapezoidalRule(integrand, xmin, xmax, N_5, alpha)
#    print("\tN=%d\t I=%.7e\taccuracy=%.6e"%(N_5,int_trap,np.abs(int_trap - int_analytic) / int_analytic))
#print the results
print("Integration with N = %d has relative error %e\naccurate to at least 5 digits!"%(N_5,np.abs(int_trap - int_analytic) / int_analytic))

Problem Sets/Problem Set 2/Solutions/trap1D_ps2_1.py


import numpy as np

def multivariate_gaussian_pdf(X : np.array, mu : np.array, cov : np.array) -> float:
    """
    Computes the likelihood of a vector w.r.t. the multivariate gaussian PDF.

    Parameters
    ==========
    X: numpy.array.
        The input array.
    mu: numpy.array.
        The mean array.
    cov: numpy.array.
        The covariance array.
    Returns
    ===========
    p: float.
        The likelihood of X given the mean mu and covariance cov.
    """
    diff = X.reshape(-1,1) - mu.reshape(-1,1)
    exp_factor = float( - 0.5 * np.dot( np.dot ( diff.T, np.linalg.inv(cov) ), diff ) )
    norm_factor = float ( np.linalg.det(2 * np.pi * cov) ** (-0.5) )
    p = norm_factor * np.exp( exp_factor )
    return p


import numpy as np
import pandas

def normalize_features(array):
    mu = array.mean()
    array_normalized = (array-mu)/array.std()
    sigma = array.std()

    return array_normalized, mu, sigma

def compute_cost(features, values, theta):
    m = len(values)
    sum_of_square_errors = np.square(np.dot(features, theta) - values).sum()
    cost = sum_of_square_errors / (2*m)

    return cost

def gradient_descent(features, values, theta, alpha, num_iterations):
    m = len(values)
    cost_history = []

    for i in range(num_iterations):
        predicted_values = np.dot(features, theta)
        theta = theta - alpha / m * np.dot((predicted_values - values), features)

        cost = compute_cost(features, values, theta)
        cost_history.append(cost)

    print "Theta is ", theta
    return theta, pandas.Series(cost_history)

def predictions(dataframe):
    dummy_units = pandas.get_dummies(dataframe['UNIT'], prefix='unit')
    features = dataframe[['rain', 'precipi', 'Hour', 'meantempi']].join(dummy_units)
    values = dataframe[['ENTRIESn_hourly']]
    m = len(values)

    features, mu, sigma = normalize_features(features)

    features['ones'] = np.ones(m)
    features_array = np.array(features)
    values_array = np.array(values).flatten()

    alpha = 0.5
    num_iterations = 50

    #Initialize theta, perform gradient descent
    theta_gradient_descent = np.zeros(len(features.columns))
    theta_gradient_descent, cost_history = gradient_descent(features_array, values_array, theta_gradient_descent,
                                                            alpha, num_iterations)

    predictions = np.dot(features_array, theta_gradient_descent)

    return predictions

def compute_r_squared(data, predictions):
    data_mean = data.mean()
    r_squared = 1 - (
        np.sum(np.square(data - predictions)) / np.sum(np.square(data - data_mean))
    )

    return r_squared

if __name__ == '__main__':
    df = pandas.read_csv('./turnstile_data_master_with_weather.csv')
    print "Prediction are ", predictions(df)
    features['ones'] = np.ones(m)
    data = np.array(df['ENTRIESn_hourly']).flatten()

    print "R^2 results ", compute_r_squared(data, predictions)


# -*- coding: utf-8 -*-
"""
Created on Tue Oct 13 10:15:04 2020

#https://xavierbourretsicotte.github.io/Intro_optimization.html
"""

#matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('seaborn-white')
import numpy as np
from mpl_toolkits import mplot3d

def Rosenbrock(x,y):
    return 100*(y-x**2)**2 + (1-x)**2

def Grad_Rosenbrock(x,y):
    g1 = 2*x + 2 - 400*x*y+400*x**3
    g2 = 200*y-200*x**2
    return np.array([g1,g2])

def Hessian_Rosenbrock(x,y):
    h11 = -400*y + 1200*x**2 + 2
    h12 = -400 * x
    h21 = -400 * x
    h22 = 200
    return np.array([[h11,h12],[h21,h22]])

def Gradient_Descent(Grad,x,y, gamma = 0.00125, epsilon=0.0001, nMax = 10000 ):
    #Initialization
    i = 0
    iter_x, iter_y, iter_count = np.empty(0),np.empty(0), np.empty(0)
    error = 10
    X = np.array([x,y])

    #Looping as long as error is greater than epsilon
    while np.linalg.norm(error) > epsilon and i < nMax:
        i +=1
        iter_x = np.append(iter_x,x)
        iter_y = np.append(iter_y,y)
        iter_count = np.append(iter_count ,i)
        #print(X)

        X_prev = X
        X = X - gamma * Grad(x,y)
        error = X - X_prev
        x,y = X[0], X[1]

    print(X)
    return X, iter_x,iter_y, iter_count

root,iter_x,iter_y, iter_count = Gradient_Descent(Grad_Rosenbrock,-2,2)

#PLOTTING THE SOLUTION

x = np.linspace(-2,2,250)
y = np.linspace(-1,3,250)
X, Y = np.meshgrid(x, y)
Z = Rosenbrock(X, Y)

#Angles needed for quiver plot
anglesx = iter_x[1:] - iter_x[:-1]
anglesy = iter_y[1:] - iter_y[:-1]

#matplotlib inline
fig = plt.figure(figsize = (16,8))

#Surface plot
ax = fig.add_subplot(1, 2, 1, projection='3d')
ax.plot_surface(X,Y,Z,rstride = 5, cstride = 5, cmap = 'jet', alpha = .4, edgecolor = 'none' )
ax.plot(iter_x,iter_y, Rosenbrock(iter_x,iter_y),color = 'r', marker = '*', alpha = .4)

ax.view_init(45, 280)
ax.set_xlabel('x')
ax.set_ylabel('y')

#Contour plot
ax = fig.add_subplot(1, 2, 2)
ax.contour(X,Y,Z, 50, cmap = 'jet')
#Plotting the iterations and intermediate values
ax.scatter(iter_x,iter_y,color = 'r', marker = '*')
ax.quiver(iter_x[:-1], iter_y[:-1], anglesx, anglesy, scale_units = 'xy', angles = 'xy', scale = 1, color = 'r', alpha = .3)
ax.set_title('Gradient Descent with {} iterations'.format(len(iter_count)))

plt.show()

#Newton's Method (Multi-Dimensional)

def Newton_Raphson_Optimize(Grad, Hess, x,y, epsilon=0.000001, nMax = 200):
    #Initialization
    i = 0
    iter_x, iter_y, iter_count = np.empty(0),np.empty(0), np.empty(0)
    error = 10
    X = np.array([x,y])

    #Looping as long as error is greater than epsilon
    while np.linalg.norm(error) > epsilon and i < nMax:
        i +=1
        iter_x = np.append(iter_x,x)
        iter_y = np.append(iter_y,y)
        iter_count = np.append(iter_count ,i)
        print(X)

        X_prev = X
        X = X - np.linalg.inv(Hess(x,y)) @ Grad(x,y)
        error = X - X_prev
        x,y = X[0], X[1]

    return X, iter_x,iter_y, iter_count

root,iter_x,iter_y, iter_count = Newton_Raphson_Optimize(Grad_Rosenbrock,Hessian_Rosenbrock,-2,2)

x = np.linspace(-3,3,250)
y = np.linspace(-9,8,350)
X, Y = np.meshgrid(x, y)
Z = Rosenbrock(X, Y)

#Angles needed for quiver plot
anglesx = iter_x[1:] - iter_x[:-1]
anglesy = iter_y[1:] - iter_y[:-1]

#matplotlib inline
fig = plt.figure(figsize = (16,8))

#Surface plot
ax = fig.add_subplot(1, 2, 1, projection='3d')
ax.plot_surface(X,Y,Z,rstride = 5, cstride = 5, cmap = 'jet', alpha = .4, edgecolor = 'none' )
ax.plot(iter_x,iter_y, Rosenbrock(iter_x,iter_y),color = 'r', marker = '*', alpha = .4)

#Rotate the initialization to help viewing the graph
ax.view_init(45, 280)
ax.set_xlabel('x')
ax.set_ylabel('y')

#Contour plot
ax = fig.add_subplot(1, 2, 2)
ax.contour(X,Y,Z, 60, cmap = 'jet')
#Plotting the iterations and intermediate values
ax.scatter(iter_x,iter_y,color = 'r', marker = '*')
ax.quiver(iter_x[:-1], iter_y[:-1], anglesx, anglesy, scale_units = 'xy', angles = 'xy', scale = 1, color = 'r', alpha = .3)
ax.set_title('Newton method with {} iterations'.format(len(iter_count)))

plt.show()

week 8 tutorial, Multivariate optimization.py


import numpy as np
from math import pi as PI

def estimate_pi(sims):
    """
    takes the number of simulations as input to estimate pi
    """

    # counter to hold points lying inside the circle
    in_circle = 0

    for s in range(0,sims):

        x = np.random.rand()
        y = np.random.rand()

        if (x**2 + y**2) <= 1:
            in_circle += 1

    # The ratio of pts. inside the circle and the total pts. will be same as the ratio
    # of the area of circle to the area of the square, inside which the circle is inscribed
    # Area of circle = PI * R * R
    # Area of square = (2R) * (2R)

    pi_estimated = 4.0 * in_circle / sims

    print("Simulations ran: ", sims)
    print("Estimated pi", pi_estimated)
    print("Error", PI - pi_estimated)

pow = 0
input_sims = 100
while pow <= 8:
    estimate_pi(sims=input_sims)
    pow += 1
    input_sims *= 10


# -*- coding: utf-8 -*-
"""
@author: Alex Muirhead
"""

import numpy as np
from numpy.core.numeric import ones_like

def fact(n):
    """Calculate the value of n factorial."""
    # n! = n*(n-1)*...*2*1
    product = 1
    for i in range(1, n+1):
        product *= i
    return product

def exptaylor(n, x):
    """Taylor series expansion of e^x

    NOTE:
        This will fail with x as a np.ndarray when
        n > 21, due to a quirk with division by
        large numbers (21! = 51090942171709440000).
        Just another reason to avoid this implemenation!
    """
    output = 0
    for i in range(n):
        output += x**i / fact(i)
    return output

def better_exptaylor(n, x):
    """
    Better Taylor series expansion of e^x

    term 0: 1
    term 1: x**1 / 1! == (term 0) * x
    term 2: x**2 / 2! == (term 1) * x / 2
    term 3: x**3 / 3! == (term 2) * x / 3
    """
    term = ones_like(x)
    output = term
    for i in range(1, n):
        term   *= x / i
        output += term
    return output

def lntaylor(n, x):
    """
    Efficient Taylor series expansion of ln(x+1) at x=1

    term 0: -1 {not included}
    term 1:   (x-1)**1 / (2**1 * 1)
    term 2: - (x-1)**2 / (2**2 * 2)
    term 3:   (x-1)**3 / (2**3 * 3)
    term 4: - (x-1)**4 / (2**4 * 4)
    """
    term = -1
    output = np.log(2.)  # <- 0th term
    for i in range(1, n):
        term *= (1-x) / 2
        output += term / i
    return output

if __name__ == '__main__':

    approx = better_exptaylor(100, 2)
    exact  = np.exp(2)
    error  = np.abs(exact - approx)
    print(f'The error for 100 terms was {error:.2e}')


"""
Jacobi Iteration Method - https://en.wikipedia.org/wiki/Jacobi_method
"""
from __future__ import annotations

import numpy as np

# Method to find solution of system of linear equations
def jacobi_iteration_method(
    coefficient_matrix: np.ndarray,
    constant_matrix: np.ndarray,
    init_val: list,
    iterations: int,
) -> list[float]:
    """
    Jacobi Iteration Method:
    An iterative algorithm to determine the solutions of strictly diagonally dominant
    system of linear equations

    4x1 +  x2 +  x3 =  2
     x1 + 5x2 + 2x3 = -6
     x1 + 2x2 + 4x3 = -4

    x_init = [0.5, -0.5 , -0.5]

    Examples:

    >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]])
    >>> constant = np.array([[2], [-6], [-4]])
    >>> init_val = [0.5, -0.5, -0.5]
    >>> iterations = 3
    >>> jacobi_iteration_method(coefficient, constant, init_val, iterations)
    [0.909375, -1.14375, -0.7484375]

    >>> coefficient = np.array([[4, 1, 1], [1, 5, 2]])
    >>> constant = np.array([[2], [-6], [-4]])
    >>> init_val = [0.5, -0.5, -0.5]
    >>> iterations = 3
    >>> jacobi_iteration_method(coefficient, constant, init_val, iterations)
    Traceback (most recent call last):
    ...
    ValueError: Coefficient matrix dimensions must be nxn but received 2x3

    >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]])
    >>> constant = np.array([[2], [-6]])
    >>> init_val = [0.5, -0.5, -0.5]
    >>> iterations = 3
    >>> jacobi_iteration_method(coefficient, constant, init_val, iterations)
    Traceback (most recent call last):
    ...
    ValueError: Coefficient and constant matrices dimensions must be nxn and nx1 but
                received 3x3 and 2x1

    >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]])
    >>> constant = np.array([[2], [-6], [-4]])
    >>> init_val = [0.5, -0.5]
    >>> iterations = 3
    >>> jacobi_iteration_method(coefficient, constant, init_val, iterations)
    Traceback (most recent call last):
    ...
    ValueError: Number of initial values must be equal to number of rows in coefficient
                matrix but received 2 and 3

    >>> coefficient = np.array([[4, 1, 1], [1, 5, 2], [1, 2, 4]])
    >>> constant = np.array([[2], [-6], [-4]])
    >>> init_val = [0.5, -0.5, -0.5]
    >>> iterations = 0
    >>> jacobi_iteration_method(coefficient, constant, init_val, iterations)
    Traceback (most recent call last):
    ...
    ValueError: Iterations must be at least 1
    """

    rows1, cols1 = coefficient_matrix.shape
    rows2, cols2 = constant_matrix.shape

    if rows1 != cols1:
        raise ValueError(
            f"Coefficient matrix dimensions must be nxn but received {rows1}x{cols1}"
        )

    if cols2 != 1:
        raise ValueError(f"Constant matrix must be nx1 but received {rows2}x{cols2}")

    if rows1 != rows2:
        raise ValueError(
            f"""Coefficient and constant matrices dimensions must be nxn and nx1 but
            received {rows1}x{cols1} and {rows2}x{cols2}"""
        )

    if len(init_val) != rows1:
        raise ValueError(
            f"""Number of initial values must be equal to number of rows in coefficient
            matrix but received {len(init_val)} and {rows1}"""
        )

    if iterations <= 0:
        raise ValueError("Iterations must be at least 1")

    table = np.concatenate((coefficient_matrix, constant_matrix), axis=1)

    rows, cols = table.shape

    strictly_diagonally_dominant(table)

    # Iterates the whole matrix for given number of times
    for i in range(iterations):
        new_val = []
        for row in range(rows):
            temp = 0
            for col in range(cols):
                if col == row:
                    denom = table[row][col]
                elif col == cols - 1:
                    val = table[row][col]
                else:
                    temp += (-1) * table[row][col] * init_val[col]
            temp = (temp + val) / denom
            new_val.append(temp)
        init_val = new_val

    return [float(i) for i in new_val]

# Checks if the given matrix is strictly diagonally dominant
def strictly_diagonally_dominant(table: np.ndarray) -> bool:
    """
    >>> table = np.array([[4, 1, 1, 2], [1, 5, 2, -6], [1, 2, 4, -4]])
    >>> strictly_diagonally_dominant(table)
    True

    >>> table = np.array([[4, 1, 1, 2], [1, 5, 2, -6], [1, 2, 3, -4]])
    >>> strictly_diagonally_dominant(table)
    Traceback (most recent call last):
    ...
    ValueError: Coefficient matrix is not strictly diagonally dominant
    """

    rows, cols = table.shape

    is_diagonally_dominant = True

    for i in range(0, rows):
        sum = 0
        for j in range(0, cols - 1):
            if i == j:
                continue
            else:
                sum += table[i][j]

        if table[i][i] <= sum:
            raise ValueError("Coefficient matrix is not strictly diagonally dominant")

    return is_diagonally_dominant

# Test Cases
if __name__ == "__main__":
    import doctest

    doctest.testmod()


import numpy as np

def BFGSAlgo(f,g,xd,epsilon=1e-6):
    """
       quasi-Newton method, use BFGS algorithm
       :param f:target function, it's a method having a float parameter.
       :param g:the derivative of the target function, it's a method having a float parameter.
       :param xd: the dimension of the vector
       :param epsilon: precision
       :return: the optimization point
    """
    #x=np.random.rand(xd)
    x=np.zeros(xd)
    B=np.eye(xd)
    gk=g(x)
    if np.linalg.norm(gk)<epsilon:
        return x
    while True:
        pk = np.linalg.solve(B, -1 * gk)
        #pk=solveEquation(B,-1*gk)

        abs=np.abs(gk)
        # print("grad",np.max(abs),np.argmax(abs))
        # print("function",f(x))
        # print("w",np.max(x),np.argmax(x),np.median(x),np.mean(x))
        # print()
        stepLength=calLambdaByArmijoRule(x,f(x),gk,pk,f)
        #stepLength=0.01
        xPre=x
        gkPre=gk

        x=x+stepLength*pk
        gk=g(x)
        if np.linalg.norm(gk)<epsilon:
            return x

        #update B
        yk=gk-gkPre
        deltaK=x-xPre
        deltaMatrix=np.matmul(deltaK.reshape(-1,1),deltaK.reshape(1,-1))
        B=B+(np.matmul(yk.reshape(-1,1),yk.reshape(1,-1))/np.sum(yk*deltaK))- \
          (np.matmul(np.matmul(B,deltaMatrix),B)/np.matmul(np.matmul(deltaK.reshape(1,-1),B),deltaK.reshape(-1,1)))

def calLambdaByArmijoRule(xCurr, fCurr, gCurr, pkCurr,f, c=1.e-4, v=0.5):
    """
    refer to https://www.cnblogs.com/xxhbdk/p/11785365.html
    to calculate lambda
    """
    i = 0
    alpha = v ** i
    xNext = xCurr + alpha * pkCurr
    fNext = f(xNext)

    while True:
        if fNext <= fCurr + c * alpha * np.sum(pkCurr*gCurr):
            break
        i += 1
        alpha = v ** i
        xNext = xCurr + alpha * pkCurr
        fNext = f(xNext)

    return alpha

#PALU decomposition
def PALU_Factorization(A: np.array):
    U = A.copy()
    P = np.eye(U.shape[0])
    L = np.zeros(U.shape)
    for index in range(U.shape[1]):
        maxIndex = index + np.argmax(U[index:, index])
        # exchange 2 rows
        #print(U[[index, maxIndex], :])
        P[[index, maxIndex], :] = P[[maxIndex, index], :]
        U[[index, maxIndex], :] = U[[maxIndex, index], :]
        L[[index, maxIndex], :] = L[[maxIndex, index], :]
        # eliminate non-zero elements
        for rIndex in range(index + 1, U.shape[0]):
            # try:
            #     assert U[index, index]!=0
            # except:
            #     print(index)
            #     print(U)
            multiFactor = U[rIndex, index] / U[index, index]
            U[rIndex, :] -= U[index, :] * multiFactor
            L[rIndex, index] = multiFactor

        # 给L加上对角线的1
    for i in range(U.shape[0]):
        L[i, i] = 1.0
    return P, L, U

# 使用PA=LU分解解方程
def solveEquation(A: np.array, b: np.array):
    P, L, U = PALU_Factorization(A)
    Pb = np.matmul(P, b.reshape(-1,1))
    # 此时方程为：LUx=Pb
    # 先解Lc=Pb
    c = np.zeros([L.shape[0], 1])
    for i in range(L.shape[0]):
        c[i, 0] = Pb[i, 0]
        for j in range(i):
            c[i, 0] -= L[i, j] * c[j, 0]

    # 再解Ux=c
    x = np.zeros([U.shape[0], 1])
    for i in range(U.shape[0] - 1, -1, -1):
        x[i, 0] = c[i, 0]
        for j in range(U.shape[1] - 1, i, -1):
            x[i, 0] -= U[i, j] * x[j, 0]
        x[i, 0] /= U[i, i]

    return x.reshape(-1,)

if __name__ == '__main__':
    f=lambda x:5*x[0]*x[0]+2*x[1]*x[1]+3*x[0]-10*x[1]+4
    g=lambda x:np.array([10*x[0]+3,4*x[1]-10])
    print(BFGSAlgo(f,g,2))
    #print(solveEquation(np.eye(50),np.array([i for i in range(50)])))


from itertools import ifilter
import numpy as np

def sieve(n):
    '''Recursive Sieve of Eratosthenes'''
    acc = []
    def aux(numList):
        try:
            k = numList[0]
            acc.append(k)
            aux(filter(lambda x: x % k, numList))
        except IndexError:
            pass
    aux(xrange(2,n))
    return acc

def sieve2(n):
    '''Iterative Sieve of Eratosthenes'''
    acc = []
    numList = xrange(2,n)
    while numList:
        k = numList[0]
        acc.append(k)
        numList = filter(lambda x: x % k, numList)
    return acc

def sieve3(n):
    '''Iterative Sieve of Eratosthenes using iterator'''
    acc = []
    numList = iter(xrange(2,n))
    try:
        while True:
            k = numList.next()
            acc.append(k)
            numList = ifilter(lambda x: # all(x % np.array(acc))
                reduce(lambda acc,a: acc and (x % a), acc, True)
                , numList)
    except StopIteration:
        pass
    return acc

def sieve4(n):
    '''Sieve of Eratosthenes in memory'''
    acc = []
    sieve_array = np.arange(2,n)
    next_index = 0
    for (i,k) in enumerate(sieve_array):
        if k != 0:
          sieve_array[sieve_array % k == 0] = 0
          sieve_array[i] = k # Store the prime back into the array
    return sieve_array[sieve_array != 0]

if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        n = int(sys.argv[1])
    else:
        n =100
    print 'There are {0} primes less than {1}'.format(len(sieve2(n)), n)


"""
File: yx_ODE.py
Copyright (c) 2016 Andrew Malfavon
License: MIT
Exercise C.3
Description: Solve an ODE using the Forward Euler method.
"""

import numpy as np
import sympy as sp
import matplotlib.pyplot as plt

#ode to be solved
def y_prime(y, x):
    return 1 / (2 * (y - 1))

#forward euler method based on code from book
def Forward_Euler(prime, a, b, dx, eps = 1e-3):
    n = int((b - a) / (dx))
    x = np.zeros(n + 1)
    y = np.zeros(n + 1)
    y[0] = 1 + np.sqrt(eps)
    x[0] = 0
    for i in range(n):
        x[i + 1] = x[i] + dx
        y[i + 1] = y[i] + dx * prime(y[i], x[i])
    return y, x

#solves the ode using sympy
#does not take into account initial condition
def sympy_solution():
    x = sp.Symbol('x')
    f = sp.Function('f')
    eq = 1 / (2 * (f(x) - 1))
    return sp.dsolve(sp.Eq(sp.diff(f(x)), eq))[1]#solves ode. first solution is negative so we use the second solution

#plot approximations with three step sizes and plots analytical solution
def plot(eps = 1e-3):
    x_exact = np.linspace(0, 4, 1001)
    y_exact = 1 + np.sqrt(x_exact + eps)
    x1 = Forward_Euler(y_prime, 0, 4, 1)[1]
    y1 = Forward_Euler(y_prime, 0, 4, 1)[0]
    x2 = Forward_Euler(y_prime, 0, 4, 0.25)[1]
    y2 = Forward_Euler(y_prime, 0, 4, 0.25)[0]
    x3 = Forward_Euler(y_prime, 0, 4, 0.01)[1]
    y3 = Forward_Euler(y_prime, 0, 4, 0.01)[0]
    plt.plot(x_exact, y_exact, label = 'Exact Solution')#labels are for the key in the graph
    plt.plot(x1, y1, label = 'step size = 1')
    plt.plot(x2, y2, label = 'step size = 0.25')
    plt.plot(x3, y3, label = 'step size = 0.01')
    plt.title('Approximations and exact solution of ODE')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)#creates key for graph

#test the most accurate approximation with the analytical solution
def test():
    eps = 1e-3
    exact = 1 + np.sqrt(4 + eps)
    assert (Forward_Euler(y_prime, 0, 4, 0.01)[0][-1]) - (exact) < 0.01


# Importing required libraries

import numpy as np
import scipy.stats
import math
from scipy.stats import multivariate_normal

def normal_dist(x , mean , var):

    """
    Calculates a normal distribution in a variate x,
    with mean mu (mean) and variance sigma^2 (sd)
    as a statistic distribution with probability density function.
    source: https://mathworld.wolfram.com/NormalDistribution.html

    note that variance is std^2, e.g. a distribution with variance equal to 36, has a std dev of 6 (36 = 6^2).

    sigma^2 = sd, standard deviation
    sigma = variance
    """

    prob_density = (1/((2*np.pi*var)**(1/2))) * (np.exp(-0.5*((x-mean)**2/var)))

    return prob_density

def stackoverflow_normpdf(x, mean, sd):

    """
    Another alternative from: https://stackoverflow.com/a/12413491
    This uses the formula found here: http://en.wikipedia.org/wiki/Normal_distribution#Probability_density_function
    """
    var = float(sd)**2
    denom = (2*math.pi*var)**.5
    num = math.exp(-(float(x)-float(mean))**2/(2*var))
    return num/denom

def scipy_norm_pdf(x, mean, sd):

    #Using scipy library for calculating the normal probability distribution
    # loc being the mean, varies for probability of 'yes' vs 'no'
    # scale being the sd (not variance), varies for probability of 'yes' vs 'no'
    # use cdf to indicate the value at which we would like to calculate the probability; e.g. x = 4 in this particular scenario

    prob = scipy.stats.norm(mean, sd).pdf(x)
    prob_dist = scipy.stats.norm(mean, sd)

    return prob, prob_dist

x = 4
mean_yes = 10
mean_no = 0
var = 36
sd = var**(1/2)

#using the function we have created

our_prob_dist_yes = normal_dist(x, mean_yes, var)
our_prob_dist_no = normal_dist(x, mean_no, var)

print(f"Using the function we have created, for \n X = {x} \n mean_yes = {mean_yes} \n mean_no= {mean_no} n var = {var} \n the probability for Yes is {our_prob_dist_yes} \n and the probability for No is {our_prob_dist_no}")

#using the alternative method from stackoverflow and wikipedia formulation; note you are passing on here sd and not variance (e.g. passing on sd and not var = sd^(2)
alt_prob_dist_yes = stackoverflow_normpdf(x, mean_yes, sd)
alt_prob_dist_no = stackoverflow_normpdf(x, mean_no, sd)

print(f"Using the alternative method from stackoverflow and wikipedia formulation, for \n X = {x} \n mean_yes = {mean_yes} \n mean_no = {mean_no} \n var = {var} the probability for Yes is {alt_prob_dist_yes} \n and the probability for No is {alt_prob_dist_no}")

#using scipy library to compare/validate above function
scipy_prob_yes, scipy_prob_dist_yes = scipy_norm_pdf(x, mean_yes, sd)
scipy_prob_no, scipy_prob_dist_no = scipy_norm_pdf(x, mean_no, sd)

print(f"Using scipy library to compare/validate above function, for \n X = {x} \n mean_yes = {mean_yes} \n mean_no = {mean_no} \n var = {var} the probability for Yes is {scipy_prob_yes} \n and the probability for No is {scipy_prob_no}")


import numpy as np

print('----> starts here')

#create array of ten zeros
my_array = np.zeros(10)
print(my_array)
print('---------')

#create array of ten ones
my_array = np.ones(10)
print(my_array)
print('---------')

#create array of ten fives
my_array = np.ones(10) * 5
print(my_array)
print('---------')

#create array of integers from 10 to 50
my_array = np.arange(10, 51)
print(my_array)
print('---------')

#create array of even integers from 10 to 50
my_array = np.arange(10, 51, 2)
print(my_array)
print('---------')

#create 3x3 matrix with values 0 to 8
my_matrix = np.arange(9).reshape (3,3)
print(my_matrix)
print('---------')

#create 3x3 identity matrix
my_matrix = np.eye(3)
print(my_matrix)
print('---------')

#generate random numbers between 0 and 1
my_matrix = np.random.rand(1)
print(my_matrix)
print('---------')

#generate array of random numbers sampled from a normal distribution
my_array = np.random.randn(25)
print(my_array)
print('---------')

#generate array of random numbers sampled from a normal distribution
my_array = np.arange(1,101)/100
my_matrix = my_array.reshape(10,10)
print(my_matrix)
print('---------')

#create array of linearly spaced points between 0 and 1
my_array = np.linspace(0, 1, 20)
print(my_array)
print('---------')

# *****************
#scalars = numbers
print('scalar')
s = 5
# print(type(s))
print('----------')

# vectors
print('vector')
v = np.array([5,-2,4])
# print(type(v))
print(v.shape)
print('----------')

# matrix = array containing vectors
print('matrix')
m = np.array([[5,12,6],[-3,0,14]])
# print(type(m))
print(m.shape)
print('----------')

# creating a tensor
# ------
print('source matrix m1 for tensor')
m1 = np.array([[5,10,6],[-3,1,11]])
print(m1)
print('source matrix m2 for tensor')
m2 = np.array([[9,8,7],[1,3,-5]])
print(m2)
print('tensor')
tensor = np.array([m1,m2])
print(tensor)
print(tensor.shape)
print('----------')
# manual tensor
tensor2 = np.array([[[ 5, 12,  6], [-3,  0, 14]], [[ 9,  8,  7], [ 1,  3, -5]]])

# math ops
# ------
# addition for vectors
print('addition for vectors')
v1 = np.array([1,2,3,4,5])
v2 = np.array([7,8,9,10,11])
v3 = v1 + v2
print(v3)
print(v3.shape)
print('----------')
# -- addition, matrix keeps the shape
print('addition for matrices')
m3 = m1 + m2
print(m3)
print(m3.shape)
print('----------')
# -- subtraction, matrix keeps the shape
print('subtraction for matrices')
m4 = m1 - m2
print(m4)
print(m3.shape)
print('----------')
# -- dot product, multiplication
print('dot product - vectors')
x = np.array([2,8,-4])
y = np.array([1,-7,3])
m5 = np.dot(x,y)
print(m5)
#output is always a single number
print('dot product - matrix')
z = np.array([[5,10,6],[-3,1,11]])
w = np.array([[5,10],[-3,1],[-3,1]])
m5 = np.dot(z,w)
# matrix (m * n) multiplied by matrix (n * k) = matrix (m*k)
print(m5)
print(z.shape)
print(w.shape)
print(m5.shape)
print('----------')

# transposing
# https://lihan.me/2018/01/numpy-reshape-and-transpose/
# ------
print('transposing')
print(m3)
m6 = m3.T
print(m6)
print('----------')
print('reshaping')
print(m3)
m7 = m3.reshape(3,2)
print(m7)
print('----------')
# *****************

#---
#use this starting matrix for indexing and selection
new_matrix = np.arange(1,26).reshape(5,5)
print(new_matrix)
print('---------')
#---

#grab a slice removing first two rows and first column
test_matrix = new_matrix[2:,1:]
print(test_matrix)
print('---------')

#grab the value 20 from the new_matrix
test_matrix = new_matrix[3,4]
print(test_matrix)
print('---------')

#grab the the second column in matrix
test_matrix = new_matrix[:3,1:2]
print(test_matrix)
print('---------')

#grab last two rows in matrix
test_matrix = new_matrix[3:, :]
print(test_matrix)
print('---------')

#sum all values in matrix
test_matrix = new_matrix.sum()
print(test_matrix)
print('---------')

#sum all columns in matrix
test_matrix = new_matrix.sum(axis=0)
print(test_matrix)
print('---------')

#st dev for all values in matrix
test_matrix = new_matrix.std()
print(test_matrix)
print('---------')
#or...
test_matrix = np.std(new_matrix)
print(test_matrix)
print('---------')

#anchor the generation of random numbers
seed = np.random.seed(77)
test_array_seed = np.random.rand(1)

print(test_array_seed)
print('---------')


from sympy import *

t, a, Q, w = symbols('t a A w', positive=True, real=True)
u = Q*exp(-a*t)*sin(w*t)
dudt = diff(u, t)
dudt
factor(dudt)
simplify(dudt)
# Alternative, manually derived expression
phi = atan(-a/w)
A = Q*sqrt(a**2 + w**2)
dudt2 = exp(-a*t)*A*cos(w*t - phi)

simplify(expand_trig(dudt2))
simplify(expand_trig(dudt2 - dudt))  # are they equal?
s = solve(dudt2, t)
s


'''
Print the series:
x + x**2 + x**3 ... + x**n
'''

from sympy import Symbol, pprint, init_printing

def print_series( n, x_value ):

	# Initialize printing system with reverse order
	init_printing( order = 'rev-lex' )

	x = Symbol( 'x' )

	series = x
	for i in range( 2, n + 1 ):
		series = series + ( x ** i ) / i
	pprint( series )

	series_value = series.subs( { x: x_value } )

	print( 'Value of the series at {0}: {1}'.format( x_value, series_value ) )

if __name__ == '__main__':
	n = input( 'Enter the number of terms you want in the series: ' )
	x_value = input( 'Enter the value of x at which you want to evaluate the series: ' )

	print_series( int( n ), float( x_value ) )


#NAME:
#  K_ARR
#PURPOSE:
#  Generate an array of wavenumber values (by default, in units of cycles per
#  sample interval), arranged corresponding to the FFT of an N-element array.
#CALLING SEQUENCE:
#  k = k_arr(N [, dx=dx] [,/radians])
#OUTPUT:
#  k = N-element array of positive and negative wavenumbers, where the maximum
#     (Nyquist) frequency has a value of pi radians per sample interval. The
#     dx and cycles keywords can be used to modify the units of k.
#INPUTS:
#  N = Number of elements. The program has been verified for both odd and
#     even N.
#OPTIONAL KEYWORD INPUTS:
#  dx = sample interval. Default=1. If included, then k will be converted into units
#     of cycles (or radians, if that keyword is set) per unit distance (or time),
#     corresponding to the units of dx.
#  radians = if set, then let k be in radians (rather than cycles).
#HISTORY:
#  2013-Jun-25 C. Kankelborg
#  2019 Jun 21  JTE Translated into Python 3

import numpy as np

def k_arr(N,
          dx = 1.0,
          radians = False):

    dk = np.power(N * dx, -1.0)  #frequency interval equals the fundamental frequency
       #of one cycle per N samples, or one cycle per total distance N*dx.

    k = dk * [ np.arange(np.floor_divide(N, 2.0) + 1), -np.arange(np.ceil(0.5 * N - 1))[::-1] + 1  ]
    #        [  positive frequencies,        negative frequencies              ]

    if radians:

        k *= 2.0 * np.pi #convert from cycles to radians.

    return k


import numpy as np
itr_limit = 20
conv_crit = 0.0000001

def stop_cond(A, b, x):

	n = len(b)
	res = (A@x - b)
	magnitude = (res@res)**0.5

	if magnitude < conv_crit:
		return True
	else:
		return False

def jacobi(A, b, N = -1, x_old = None):

	n = len(b)
	itr = 0
	converged = False
	x_new = np.empty(n)
	if x_old == None:
		x_old = np.zeros(n)

	while not converged and itr < itr_limit and itr != N:

		for i in range (n):
			dot = 0
			for j in range(n):
				if(i!=j):
					dot += A[i,j]*x_old[j]

			x_new[i] = (b[i] - dot)/A[i,i]

		itr += 1
		converged = stop_cond(A,b,x_new)
		x_old = x_new.copy()

	if not converged:
		print("Iteration limit reached without convergence.")

	return x_new

M = np.array([[4,1],[1,4]],dtype=float)
x = np.array([5,5],dtype=float)
print(jacobi(M,x))


import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
from numpy.fft import fft2, fftshift, ifft2

# %% read images
img_raw = np.asarray(Image.open('Q5_1.tif'))
row, col = img_raw.shape

# %% spatial filtering
sobel_filter = np.array([[-1, 0, 1],
                         [-2, 0, 2],
                         [-1, 0, 1]])

img_pad = np.pad(img_raw, 1)
img_spatial = np.asarray([(img_pad[i:i + 3, j:j + 3] * sobel_filter).sum()
                          for i in range(row)
                          for j in range(col)]).reshape(row, col)

plt.subplot(121)
plt.imshow(img_raw, cmap='gray')
plt.title('Raw Q5_1.tif')

plt.subplot(122)
plt.imshow(img_spatial, cmap='gray')
plt.title('Sobel filtered Q5_1.tif\n(in spatial domain)')
# plt.savefig('Q5_1_1.png')
plt.show()

# %% freq domain filtering
img_fourier = fftshift(fft2(np.pad(img_raw, ((0, row), (0, col)))))
filter_fourier = fftshift(fft2(np.pad(sobel_filter, ((0, 2 * row - 3), (0, 2 * col - 3)))))

img_view = np.log10(np.abs(img_fourier) + 1).astype(np.uint8)
filter_view = np.abs(filter_fourier).astype(np.uint8)

plt.subplot(121)
plt.imshow(img_view, cmap='gray')
plt.title('Fourier transform of Q5_1.tif\n(Shifted and log transformed)')

plt.subplot(122)
plt.imshow(filter_view, cmap='gray')
plt.title('Fourier transform of Sobel Filter\n(Shifted and log transformed)')
plt.savefig('Q5_1_2.png')
plt.show()

img_freq = np.real(ifft2(fftshift(img_fourier * filter_fourier)))[0:row, 0:col]

plt.subplot(121)
plt.imshow(img_raw, cmap='gray')
plt.title('Raw Q5_1.tif')

plt.subplot(122)
plt.imshow(img_freq, cmap='gray')
plt.title('Sobel Filtered Q5_1.tif\n(in frequency domain)')
plt.savefig('Q5_1_3.png')
plt.show()

# %% no fft shift
img_fourier_no_shift = fft2(np.pad(img_raw, ((0, row), (0, col))))
img_view_no_shift = np.log10(np.abs(img_fourier_no_shift) + 1).astype(np.uint8)
img_freq_no_shift = np.real(ifft2(img_fourier * filter_fourier))[0:row, 0:col]

plt.subplot(121)
plt.imshow(img_view_no_shift, cmap='gray')
plt.title('Fourier transform of Q5_1.tif\n(Not shifted, log transformed)')

plt.subplot(122)
plt.imshow(img_freq_no_shift, cmap='gray')
plt.title('Sobel Filtered Q5_1.tif\n(in frequency domain, not shifted)')
plt.savefig('Q5_1_4.png')
plt.show()


import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

#https://datahub.io/core/s-and-p-500
dat = pd.read_csv("c:/users/jliv/downloads/data_csv.csv")

dat['year']=dat['Date'].apply(lambda x: x[:4])
dat['year']=dat['year'].astype(int)

i = dat.index.values/max(dat.index)

print("Has the SP500 grown strictly exponentially? It can be shown that an exponential model a second exponential model on the exponent can well describe the historical growth of the sp500.")
plt.plot(dat['SP500'])
plt.title("monthly s&p 500: big exponential growth")
plt.xlabel("month since 1871")
plt.show()

print("If growth is exponential, evaluating log(sp500) can render growth linear. It is observable that the log-trend still grows more in recent history than in early history.")
plt.plot(np.log(dat['SP500']))
plt.title("monthly log s&p 500: growing faster and faster, but linear in part")
plt.xlabel("month since 1871")
plt.show()

print("Using a homomorphism on i (normalized month since 1871), functionally a**i for some a, 'a' can be chosen to map the normalized month to a new value in space a**i, such that small i are mapped to a closer proximity to each other than large i.")
a=6
plt.scatter(i,a**i)
plt.title("i (month since 1871, normalized), maps to a**i \n stretching high i and compressing low i")
plt.xlabel("i = month since 1871 normalized")
plt.ylabel("a**i")
plt.show()

print("Initial guess of a is "+str(a)+", such that plot log_a(sp500) against a**i is as linear as possible.")
plt.plot(a**i,np.log(dat['SP500'])/np.log(a))
plt.title("warping x axis: a**x; a="+str(a)+", compressing low x \n stretching high x, even more linear")
plt.xlabel("a**i")
plt.ylabel("log_a(sp500)")
plt.show()

print("With initial a = "+str(a)+", fitting linear model X=[a**i,1] to log_a(sp500) for inital guess of beta.  log_a(sp500)=b1*i'+b0 where i'=a**i")
X = np.column_stack([a**i,np.ones(len(i))])
Y=np.log(dat['SP500'])/np.log(a)
beta = np.linalg.inv(X.T@X)@(X.T@Y)

Yhat=X@beta

plt.plot(a**i,Y)
plt.plot(a**i,Yhat)
plt.title("Fitting a linear model this can be abstracted into an exponential curve \n with respect to a power-warped axis")
plt.xlabel("a**i")
plt.ylabel("log_a(sp500)")
plt.show()

print("Plotting log_a(Y) over unmorphed i scale, exponential growth is observable. Here log_5(sp500)=b1*a**i+b0")
plt.plot(i,Y)
plt.plot(i,Yhat)
plt.ylabel("i")
plt.title("This growth is well behaved with respect to a**i, and thus, i")
plt.show()

print("Plotting Y over unmorphed i scale, growth is exponential, and the exponent grows exponentially. Here sp500=a**(b1*a**i+b0). The shape is largely correct, but parameters a, b1 and b0 can be tuned with gradient descent.")
plt.plot(i,a**Y)
plt.plot(i,a**Yhat)
plt.ylabel("i")
plt.title("This growth is well behaved with respect to a**i, and thus, i")
plt.show()

print("Setting Y to be sp500, x is i (normalized month since 1870).")
print("Model is Y=a**(b1*a**i+b0), from visual analysis above, initial a guessed "+str(a)+", and model fit given a="+str(a)+" yielded beta vector "+ str(beta)+".")

print("Derivative of SSE cost function is evaluated using each of a, b1 and b0.")
print("In terms of a: (a**(b1*a**X+b0)-Y)@(a**(b1*a**X+b0-1)*(b1*a**X+b0+b1*X*np.log(a)*a**X))")
print("In terms of b1: (a**(b1*a**X+b0)-Y)@(np.log(a)*a**(b1*a**X+b0+X))")
print("In terms of b0: (a**(b1*a**X+b0)-Y)@(np.log(a)*a**(b1*a**X+b0))\n")

Y=dat['SP500']
X = i

l=.00000000001
tol=1e-8
a,b1,b0=a,beta[0],beta[1]
e1=1e5
for c in range(15000):
    err = (a**(b1*a**X+b0)-Y)

    e2=np.mean(abs(err))

    if e1-e2<tol:
        break
    else:
        e1=e2
    grad_mod_a = a**(b1*a**X+b0-1)*(b1*a**X+b0+b1*X*np.log(a)*a**X)
    grad_mod_b1=np.log(a)*a**(b1*a**X+b0+X)
    grad_mod_b0 = np.log(a)*a**(b1*a**X+b0)

    grad_a=err@grad_mod_a
    grad_b1=err@grad_mod_b1
    grad_b0=err@grad_mod_b0

    a,b1,b0=a-l*grad_a,b1-l*grad_b1,b0-l*grad_b0

print(np.mean(abs(err)))
print("After gradient descent, new a, b1 and b0 are:",a,b1,b0)
yhat=a**(b1*a**X+b0)

print("With learned a, b1 and b0, model well-defines historical growth of the market.")
plt.plot(Y)
plt.plot(yhat)
plt.show()


import numpy as np
import matplotlib.pyplot as plt

"""
A2-Part-1: Generate a sinusoid

Write a function to generate a real sinusoid (use np.cos()) given its amplitude A, frequency f (Hz), initial phase phi (radians),
sampling rate fs (Hz) and duration t (seconds).

All the input arguments to this function (A, f, phi, fs and t) are real numbers such that A, t and fs are
positive, and fs > 2*f to avoid aliasing. The function should return a numpy array x of the generated
sinusoid.

EXAMPLE: If you run your code using A=1.0, f = 10.0, phi = 1.0, fs = 50.0 and t = 0.1, the output numpy
array should be: array([ 0.54030231, -0.63332387, -0.93171798,  0.05749049,  0.96724906])
"""
def genSine(A, f, phi, fs, t):
    """
    Inputs:
        A (float) =  amplitude of the sinusoid
        f (float) = frequency of the sinusoid in Hz
        phi (float) = initial phase of the sinusoid in radians
        fs (float) = sampling frequency of the sinusoid in Hz
        t (float) =  duration of the sinusoid (is second)
    Output:
        The function should return a numpy array
        x (numpy array) = The generated sinusoid (use np.cos())
    """
    Xs = []
    Tn = np.arange(0.0, t, 1.0/fs)
    lenTn = int(t*fs)
    for i in range(lenTn):
        Xs.append(A*np.cos(2*np.pi*f*Tn[i] + phi))
    x = np.array(Xs)
    return(x)

sinA = genSine(0.5, 2.0, 0.0, 1000.0, 2.0)
sinB = genSine(0.5, 2.0, 0.0, 1000.0, 2.0)
sinSum = sinA + sinB
plt.plot(sinA)
plt.plot(sinB)
plt.plot(sinSum)


import random
import time
import numpy as np

# Check if a number is a power of 2 using bit manipulations
def pow_of_2_check(n: int) -> bool:
    assert n > 0, f"The number for power-of-2-check should be a POSITIVE integer. Here n = {n}."
    return n & (n - 1) == 0  # &: bit-wise and

# O(n**log_2^7) sub-cubic time complexity
def strassen_mat_mult(m1: np.ndarray, m2: np.ndarray) -> np.ndarray:
    assert pow_of_2_check(m1.shape[0]) and m1.shape[0] == m1.shape[1] == m2.shape[0] == m2.shape[1], \
        f"Both mat should be of shape 2^n*2^n with the same n. Here the shapes are {m1.shape}, {m2.shape}."

    n = m1.shape[0]
    if n <= 2:  # Base case
        return m1 @ m2
    else:
        # Split m1 and m2 evenly into 4 sub-matrices each
        half_n = n//2
        a, b, c, d = m1[:half_n, :half_n], m1[:half_n, half_n:], m1[half_n:, :half_n], m1[half_n:, half_n:]
        e, f, g, h = m2[:half_n, :half_n], m2[:half_n, half_n:], m2[half_n:, :half_n], m2[half_n:, half_n:]
        # Compute the 7 products
        p1 = strassen_mat_mult(a, f-h)
        p2 = strassen_mat_mult(a+b, h)
        p3 = strassen_mat_mult(c+d, e)
        p4 = strassen_mat_mult(d, g-e)
        p5 = strassen_mat_mult(a+d, e+h)
        p6 = strassen_mat_mult(b-d, g+h)
        p7 = strassen_mat_mult(a-c, e+f)
        # Combine the results
        output_1 = p5 + p4 - p2 + p6
        output_2 = p1 + p2
        output_3 = p3 + p4
        output_4 = p1 + p5 - p3 - p7
    return np.concatenate((np.concatenate((output_1, output_2), axis=1), np.concatenate((output_3, output_4), axis=1)),
                          axis=0)

if __name__ == '__main__':
    # Test function "pow_of_2_check"
    test = 1483948
    print(f"The number is a power of 2: {pow_of_2_check(test)}")
    test = 2 ** 10
    print(f"The number is a power of 2: {pow_of_2_check(test)}")

    # Test Strassen matrix multiplication algorithm
    mat_size = 2**6
    mat_1 = random.randint(1, 100)*np.random.rand(mat_size, mat_size)
    mat_2 = random.randint(1, 100)*np.random.rand(mat_size, mat_size)
    ans = strassen_mat_mult(mat_1, mat_2)
    # print(f"Product = {ans}")
    print(f"Normalized Fresenius norm of (ans - np mat mult result): {np.linalg.norm(ans - mat_1@mat_2)/mat_size}")

    repetition = int(1e2)
    start = time.time()
    for _ in range(repetition):
        strassen_mat_mult(mat_1, mat_2)
    print(f"Strassen matrix multiplication average time = {(time.time() - start)/repetition}s")


# 2020.01.19
# Problem Statement:
# https://leetcode.com/problems/distinct-subsequences/
# My answer exceeded the time limit, referred to a dp solution here:
# https://leetcode.com/problems/distinct-subsequences/discuss/37327/Easy-to-understand-DP-in-Java
import numpy as np

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        if len(s) < len(t): return 0
        if len(s) == len(t): return int(s==t)

        # if take this char, have numDistinct(s_without_this_char, t_without_this_char) ways
        # if not take this char, have numDistinct(s_without_this_char, t) ways

        self.dp_structure = np.zeros((len(s)+1, len(t)+1))
        return int(self.numDistinctHelper(s, t))

    def numDistinctHelper(self, s, t):
        for i in range(0, len(s)):
            self.dp_structure[i][0] = 1

        for i in range(0, len(s)):
            for j in range(0, len(t)):
                if s[i] != t[j]:
                    self.dp_structure[i+1][j+1] = self.dp_structure[i][j+1]
                else:
                    self.dp_structure[i+1][j+1] = self.dp_structure[i][j+1] + \
                                              self.dp_structure[i][j]
        return self.dp_structure[len(s)][len(t)]

# personal solution (out of time limit), actually not sure what caused the time difference
class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        if len(s) < len(t): return 0
        if len(s) == len(t): return int(s==t)

        # if take this char, have numDistinct(s_without_this_char, t_without_this_char)
        # if not take this char, have numDistinct(s_without_this_char, t)

        self.dp_structure = np.zeros((len(s)+1, len(t)+1))
        return int(self.numDistinctHelper(s, t))

    def numDistinctHelper(self, s, t):
        if len(t) == 0:
            self.dp_structure[len(s)][len(t)] = 1
            return 1
        if len(s) < len(t):
            self.dp_structure[len(s)][len(t)] = 0
            return 0
        if len(s) == len(t):
            self.dp_structure[len(s)][len(t)] = (s==t)
            return s==t

        if self.dp_structure[len(s)][len(t)] != 0: return self.dp_structure[len(s)][len(t)]

        if s[0] != t[0]:
            self.dp_structure[len(s)][len(t)] = self.numDistinctHelper(s[1:], t)
            return self.dp_structure[len(s)][len(t)]
        else:
            self.dp_structure[len(s)][len(t)] = self.numDistinctHelper(s[1:], t[1:]) \
                                              + self.numDistinctHelper(s[1:], t)
            return self.dp_structure[len(s)][len(t)]


from math import sqrt, pi, exp
import numpy as np

def distance(x1, y1, x2, y2):
    """
    Distance between two points (x1, y1), (x2, y2).
    """
    return sqrt((x1-x2)**2 + (y1-y2)**2)

def array_dist(p1, p2):
    """
    Distance between two points represented by arrays.
    """
    return distance(p1[0], p1[1], p2[0], p2[1])

def direction(p1, p2):
    """
    Direction between two points p1 and p2.
    """
    return (p2-p1)/distance(p1[0], p1[1], p2[0], p2[1])

def theta(v1, v2):
    """
    Computes angle between two vectors
    """
    val = v1.dot(v2)/(np.linalg.norm(v1)*np.linalg.norm(v2))
    if val > 1:
        print(val, v1, v2)
        return np.arccos(1)
    elif val < -1:
        print(val, v1, v2)
        return np.arccos(-1)
    return np.arccos(val)

def proj(v1, v2):
    """
    Computes projection of v1 onto v2
    """
    return np.dot(v1, v2)/np.linalg.norm(v2)

def t1_force(t):
    M = 0.01
    N = 15
    s1 = 2
    s2 = 0.3
    sig_square = s1**2 + s2**2

    s = s1**2 + s2**2
    A = -(M * N) / sqrt(2*pi*s)
    return A * exp(-(t**2) / (2*s)) * (-t/s)
    #return (M*N*t/(ss*sqrt(2*pi*ss))) * exp(-(t**2)/(2*ss))
    #return ((M*N)/(sqrt(2*pi*sig_square))) * ((2*t*exp((-t**2)/(2*sig_square)))/(2*sig_square))

def t2_force(p, orig):
    return 0.005*array_dist(p, orig)

def ccw(A, B, C):
    """
    Check if a point C is counter-clockwise to AB.
    """
    return (C[1] - A[1])*(B[0]-A[0]) > (B[1]-A[1])*(C[0]-A[0])

def is_intersect(A, B, C, D):
    """
    Check if two line segments AB and CD intersect.
    """
    return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)

def line_line_segment_intersect(p, d, p1, p2):
    """
    Check if a line defined by point p and direction d intersects
    a segment p1p2.
    """

    extend1 = [p[0] + 500000*d[0], p[1] + 500000*d[1]]
    extend2 = [p[0] - 500000*d[0], p[1] - 500000*d[1]]

    return is_intersect(p, extend1, p1, p2) or is_intersect(p, extend2, p1, p2)

def minDistance(A, B, E) :
    """
    Minimum distance from a point E to line segment AB. Adapted from GeeksforGeeks.
    """

    # vector AB
    AB = [None, None]
    AB[0] = B[0] - A[0]
    AB[1] = B[1] - A[1]

    # vector BP
    BE = [None, None]
    BE[0] = E[0] - B[0]
    BE[1] = E[1] - B[1]

    # vector AP
    AE = [None, None]
    AE[0] = E[0] - A[0]
    AE[1] = E[1] - A[1]

    # Variables to store dot product

    # Calculating the dot product
    AB_BE = AB[0] * BE[0] + AB[1] * BE[1]
    AB_AE = AB[0] * AE[0] + AB[1] * AE[1]

    # Minimum distance from
    # point E to the line segment
    reqAns = 0

    # Case 1
    if (AB_BE > 0) :

        # Finding the magnitude
        y = E[1] - B[1]
        x = E[0] - B[0]
        reqAns = sqrt(x * x + y * y)

    # Case 2
    elif (AB_AE < 0) :
        y = E[1] - A[1]
        x = E[0] - A[0]
        reqAns = sqrt(x * x + y * y)

    # Case 3
    else:

        # Finding the perpendicular distance
        x1 = AB[0]
        y1 = AB[1]
        x2 = AE[0]
        y2 = AE[1]
        mod = sqrt(x1 * x1 + y1 * y1)
        reqAns = abs(x1 * y2 - y1 * x2) / mod

    return reqAns


#!/usr/bin/env python
# coding: utf-8
# ## Monte Carlo - Euler Discretization - Part II
# *Suggested Answers follow (usually there are multiple ways to solve a problem in Python).*
# *Euler Discretization – continued.*
# In[1]:
import numpy as np
import pandas as pd
from pandas_datareader import data as web
from scipy.stats import norm
import matplotlib.pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
ticker = 'MSFT'
data = pd.DataFrame()
data[ticker] = web.DataReader(ticker, data_source='yahoo', start='2000-1-1')['Adj Close']
log_returns = np.log(1 + data.pct_change())
stdev = log_returns.std() * 250 ** 0.5
stdev = stdev.values
r = 0.025
T = 1.0
t_intervals = 250
delta_t = T / t_intervals
iterations = 10000
Z = np.random.standard_normal((t_intervals + 1, iterations))
S = np.zeros_like(Z)
S0 = data.iloc[-1]
S[0] = S0
for t in range(1, t_intervals + 1):
    S[t] = S[t-1] * np.exp((r - 0.5 * stdev ** 2) * delta_t + stdev * delta_t ** 0.5 * Z[t])
plt.figure(figsize=(10, 6))
plt.plot(S[:, :10]);
# ******
# Use numpy.maximum to create a vector with as many elements as there are columns in the S matrix.
# In[2]:
p = np.maximum(S[-1] - 110, 0)
# In[3]:
p
# In[4]:
p.shape
# Use the following formula to forecast the price of a stock option.
# $$
# C = \frac{exp(-r \cdot T) \cdot \sum{p_i}}{iterations}
# $$
# In[5]:
np.sum(p)
# In[6]:
C = np.exp(-r * T) * np.sum(p) / iterations
C
# Because this pricing model is based on random iterations, you will obtain a different result every time you re-run the code in this document. Expand the “Kernel” list from the Jupyter menu and click on “Restart and run all”/”Restart & run all cells” to verify this is true.


#!usr/bin/python
"""
author  : Bhekimpilo Ndhlela
author  : 18998712
module  : Applied Mathematics(Numerical Methods) TW324
task    : computer assignment 04 question 4
since   : Friday-23-03-2018
"""
def trapezium(exact_I, H, x0=0., debug=True):
    apprx_I = array([h/2. * (exp(x0) + exp(h)) for h in H])
    return array([abs(apI - exI) for apI, exI in zip(apprx_I, exact_I)])

def midpoint(exact_I, H, x0=0., debug=True):
    W = array([x0 + (h / 2.0) for h in H])
    apprx_I = array([h * exp(w) for h, w in zip(H, W)])
    return array([abs(apI - exI) for apI, exI in zip(apprx_I, exact_I)])

def simpson(exact_I, H, x0=0., debug=True):
    apprx_I = array([h/6.*(exp(x0)+(4.*exp(h/2.))+exp(h)) for h in H])
    return array([abs(apI - exI) for apI, exI in zip(apprx_I, exact_I)])

def debug(abs_err_s, abs_err_m, abs_err_t, debug=True):
    if debug is True:
        print "DEBUG MODE: [ON] [Question 4 Simpson's method]"
        print "SIMPSONS METHOD\t\tMIDPOINT METHOD\t\tTRAPEZIUM METHOD"
        for s, m, t in zip(abs_err_s, abs_err_m, abs_err_t):
            print "{:.20f}".format(s),"{:.20f}".format(m),"{:.20f}".format(t)
    else:
        print "DEBUG MODE: [OFF] [Question 4 Simpson's method]"

def plot_abs_errs(abs_err_t, abs_err_m, abs_err_s):
    #loglog plot to display the error as function of the step size
    plt.title("|xc-x| of: The Midpoint, Simpson & Trapezium Methods against h")
    plt.ylabel("Midpoint vs Simpson vs Trapezium")
    plt.xlabel("h")
    plt.yscale('log'); plt.xscale('log')
    plt.plot([1., .1, .01], abs_err_t, "k-", label="Trapezium")
    plt.plot([1., .1, .01], abs_err_m, "r-", label="Midpoint")
    plt.plot([1., .1, .01], abs_err_s, "g-", label="Simpson")
    plt.legend(bbox_to_anchor=(.65, .9))
    plt.show()

if __name__ == "__main__":
    from numpy import (exp, abs, array)
    import matplotlib.pyplot as plt

    H         = array([1., .1, .01])
    exact_I   = array([exp(h) - 1 for h in H])

    abs_err_t = trapezium(exact_I, H)
    abs_err_m = midpoint(exact_I, H)
    abs_err_s = simpson(exact_I, H)

    debug(abs_err_s, abs_err_m, abs_err_t)
    plot_abs_errs(abs_err_t, abs_err_m, abs_err_s)
else:
    from sys import exit
    exit("USAGE: python q5.py")


import numpy as np
import matplotlib.pyplot as plt

# Give the parameters.
H0 = 7.16e-11 # [yr^-1]
omega_0 = 1

# Define a and time-derivative of a.
a = lambda t: ((3/2)*H0*t)**(2/3)
a_dot = lambda t: H0*((3/2)*H0*t)**(-1/3)

# Define the function to be solved.
def ode(t, y):

    """
    y ... A vector function containing D and dD/dt
    t ... time to integrate over

    """

    # Define the initial conditions. Will assign values later.
    D , dD_dt = y[0], y[1]

    d2D_dt2 = -2*(a_dot(t)/a(t))*dD_dt + (3/2)*omega_0*(H0**2)*(D/(a(t)**3))

    return np.array([dD_dt, d2D_dt2])

# define the solver method.
def rk4(ode,y,t,h):

    """
    ode ... ordinary differential equation to solve
    y   ... A vector fucntion containing D and dD/dt
    t   ... time to integrate over
    h   ... integration step

    Method using here is the classic Runge-Kutta (4-th order), acquired L10.

    """

    k1 = h * ode(t, y)
    k2 = h * ode(t+h/2., y+k1/2.)
    k3 = h * ode(t+h/2., y+k2/2.)
    k4 = h * ode(t+h, y+k3)

    return k1/6. + k2/3. + k3/3. + k4/6.

# Define the ode solver.
def ode_solver(init, lower, upper, N):

    # Define the integration step.
    h = (upper-lower)/N

    # Define initial conditions and assign values.
    y, t_values, D_values = init, [], []

    # Solve the ode using rk4.
    for i in np.arange(lower, upper, h):
        t_values.append(i)
        D_values.append(y[0])
        y += rk4(ode,y,i,h)

    return t_values, D_values

# Define the time range and number of data points.
t_init, t_final, t_step = 1., 1000., 10
N = (t_final-t_init)*t_step

# Plot given the initial conditions.
plt.figure()

case1, case2, case3 = [3,2], [10,-10], [5,0]
all_cases = [[case1,'case1'], [case2,'case2'], [case3,'case3']]

for i in range(len(all_cases)):
    t_values, D_values = ode_solver(all_cases[i][0], t_init, t_final, N)
    plt.loglog(t_values, D_values, label=all_cases[i][1])

plt.xlabel('t', fontsize=14)
plt.ylabel('D(t)', fontsize=14)
plt.legend()

plt.savefig('./plots/handin2_p3.png')
plt.close()


# svd_image_compression.py
"""Volume 1A: SVD and Image Compression.
<Name>
<Class>
<Date>
"""

from scipy import linalg as la
import numpy as np
from matplotlib import pyplot as plt

# Problem 1
def truncated_svd(A,k=None):
    """Computes the truncated SVD of A. If r is None or equals the number
        of nonzero singular values, it is the compact SVD.
    Parameters:
        A: the matrix
        k: the number of singular values to use
    Returns:
        U - the matrix U in the SVD
        s - the diagonals of Sigma in the SVD
        Vh - the matrix V^H in the SVD
    """
    raise NotImplementedError("truncated_svd incomplete")

# Problem 2
def visualize_svd():
    """Plot each transformation associated with the SVD of A."""
    raise NotImplementedError("visualize_svd incomplete")

# Problem 3
def svd_approx(A, k):
    """Returns best rank k approximation to A with respect to the induced 2-norm.

    Inputs:
    A - np.ndarray of size mxn
    k - rank

    Return:
    Ahat - the best rank k approximation
    """
    raise NotImplementedError("svd_approx incomplete")

# Problem 4
def lowest_rank_approx(A,e):
    """Returns the lowest rank approximation of A with error less than e
    with respect to the induced 2-norm.

    Inputs:
    A - np.ndarray of size mxn
    e - error

    Return:
    Ahat - the lowest rank approximation of A with error less than e.
    """
    raise NotImplementedError("lowest_rank_approx incomplete")

# Problem 5
def compress_image(filename,k):
    """Plot the original image found at 'filename' and the rank k approximation
    of the image found at 'filename.'

    filename - jpg image file path
    k - rank
    """
    raise NotImplementedError("compress_image incomplete")


import math
import numpy as np

def evenParity(n):
	"""Determines if n is an even parity.

	Calculates the number of 1 bits in the binary representation of the given
	integer, n. An even parity is true if the number of 1 bits in the
	representation is even.

	Args:
		n: integer to check for even parity.

	Returns:
		A 0 if n is an even parity, else a 1.
	"""
	binary =  "{0:b}".format(n)
	return binary.count('1') % 2

def oddParity(n):
	"""Determines if n is an odd parity.

	Calculates the number of 1 bits in the binary representation of the given
	integer, n. If the number is odd,  outputs a 0. If the number is even,
	outputs a 1.

	Args:
		n: integer to check for odd parity.

	Returns:
		A 0 if n is an odd parity, else a 1.
	"""
	binary = "{0:b}".format(n)
	return (binary.count('1') + 1) % 2

def adder(n):
	"""Adds 42 to a given number.

	Args:
		n: integer to add to.

	Returns:
		The value of n added by 42.
	"""
	return n + 42

def addThem(n, m):
	"""Calculates the sum of n and m.

	Args:
		n: an integer to add.
		m: an integer to add.
	Returns:
		The sum of n and m.
	"""
	return n + m

def multiply(n, m):
	"""Returns n multiplied by m.

	Args:
		n: integer to be multiplied by
		m: integer to be multiplied by

	Returns:
		The product of n and m.
	"""
	return n * m

def multiplyAndAdd(n, m):
	"""Adds 5 to n, adds 7 to m, and returns the product of the two.

	f(n, m) = (n + 5) * (m + 7)

	Args:
		n: integer as an input to the function
		m: integer as an input to the function

	Returns:
		The output of the function given n and m.
	"""
	return (n + 5) * (m + 7)

def makePalindrome(s):
	"""Makes a palindrome from a given string.

	Reverses and appends s to the end of s. This guarantees the return
	statement is a palindrome.

	Args:
		s: a string

	Returns:
		A palindrome formed by appending s reversed to the end of s.
	"""
	return s + s[::-1]

def isPalindrome(s):
	"""Validates whether the input is a palindrome.

	Args:
		s: a string

	Returns:
		True if the input is a valid palindrome, else False.
	"""
	return all(s[i] == s[-i - 1] for i in range(len(s) >> 1))

def sine(x):
	"""Returns the sin of x radians.

	Args:
		x: number of radians

	Returns:
		The sine of x radians.
	"""
	return math.sin(x)

def expansionTerms(x):
	output = np.zeros(9, dtype='float32')
	n = 1
	for i in range(9):
		if i % 2 == 0:
			output[i] = float((x ** n) / math.factorial(n))
		else:
			output[i] = float(-(x ** n) / math.factorial(n))
		n += 2
	return output

def fib(n):
	"""Approximates the nth Fibonacci number.

	Since this uses Binet's formula, it is only exactly accurate from 1 to 70.
	Past n values of 70, it is only an approximation.

	Binet's formula is defined as:
	F(n) = ((Phi ^ n) - ((- Phi) ^ -n)) / sqrt(5)
			 = (((1 + sqrt(5)) / 2) ^ n) - ((1 - sqrt(5)) / 2) ^ n)) / sqrt(5))

	Args:
		n: integer for the nth number in the Fibonacci sequence

	Returns:
		The output of Binet's forumula with input n, an approximation of the nth
		number in the Fibonacci sequence.
	"""
	return round(((((1 + math.sqrt(5)) / 2) ** n)
				- (((1 - math.sqrt(5)) / 2) ** n))
				/ math.sqrt(5))

def testFibAccuracy():
	"""Calculates when the function fib(n) becomes inaccurate.

	Exactly up to 70.
	71 - 72 are off by 1.
	73 and on are off by more than 1.

	Returns:
		fn: the last Fibonacci number found by using the recursive function.
		count: the number of Fibonacci numbers calculated.
		fib(count): an approximation for the last Fibonacci number.
	"""
	fn = f1 = f2 = 1
	count = 2
	while (fn == fib(count)) | (fn == fib(count) + 1) | (fn == fib(count) - 1):
		fn = f1 + f2
		f2, f1 = f1, fn
		count+=1
	return fn, count, fib(count)

def determinant(m):
	"""Calculates the determinant of a matrix.

	Args:
		m: array of arrays that represents a matrix.

	Returns:
		The determinant of a matrix.
	"""
	return np.linalg.det(m)

def main():
	print("evenParity(2) ->", evenParity(2))
	print("evenParity(3) ->", evenParity(3))
	print("evenParity(10) ->", evenParity(10))
	print("oddParity(2) ->", oddParity(2))
	print("oddParity(3) ->", oddParity(3))
	print("oddParity(10) ->", oddParity(10))
	print("oddParity(1243) + evenParity(1243) == 1 ->",
		   oddParity(1243) + evenParity(1243) == 1)
	print("adder(1) ->", adder(1))
	print("adder(5) ->", adder(5))
	print("multiply(3, 7) ->", multiply(3, 7))
	print("multiplyAndAdd(3, 7) ->", multiplyAndAdd(3, 7))
	print("isPalindrome('abc') ->", isPalindrome('abc'))
	print("isPalindrome('yootnooy') ->", isPalindrome('yootnooy'))
	print("makePalindrome('This is the final countdown!') ->",
		makePalindrome('This is the final countdown!'))
	print("isPalindrome(makePalindrome('This is the final countdown!')) ->",
		isPalindrome(makePalindrome('This is the final countdown!')))
	print("sine(math.pi) ->", sine(math.pi))
	print("sine(math.pi / 2) ->", sine(math.pi / 2))
	print("fib(2) ->", fib(2))
	print("fib(3) ->", fib(3))
	print("fib(4) ->", fib(4))
	print("fib(5) ->", fib(5))
	print("fib(6) ->", fib(6))
	fn, count, approx = testFibAccuracy()
	print("Actual", count, "number in fib sequence =", fn,
		"Approximation =", approx)

if __name__ == "__main__":
    main()


"""
This script plots the analytic solution to the differential equation in
exercise 2.2.3 of the book `Nonlinear Dynamics and Chaos` by Steven H. Strogatz.
"""
import matplotlib.pyplot as plt
import numpy as np

def get_velocities(times, x0: float):
    # Fix the integration constant using the initial condition
    c = 1.0 / (x0 ** 2) - 1.0
    if x0 < 0.0:
        return -1.0 / np.sqrt(1.0 + c * np.exp(-2.0 * times))
    else:
        return +1.0 / np.sqrt(1.0 + c * np.exp(-2.0 * times))

if __name__ == "__main__":
    # The set of initial conditions that we use.
    initial_conditions = [2.0, 0.7, 0.5, 0.3, -0.3, -0.5, -0.7, -2.0]

    # The time steps we use
    time_steps = np.linspace(0, 3, 100)

    fig, ax = plt.subplots()

    plt.title(r"$\dot{x}=x-x^3$")
    plt.xlabel("t")
    plt.ylabel("x")
    for counter, ic in enumerate(initial_conditions):
        velocities = get_velocities(time_steps, ic)
        ax.plot(time_steps, velocities, color=f"C{counter}")

    # Add the fixed points
    plt.plot(time_steps, np.zeros(len(time_steps)), color="gray", linestyle="-.")
    plt.plot(time_steps, np.ones(len(time_steps)), color="gray", linestyle="--")
    plt.plot(time_steps, -np.ones(len(time_steps)), color="gray", linestyle="--")

    # Draw the plot
    # plt.savefig('ex_2_2_3.pdf')
    plt.show()


import numpy as np
import skimage
import utils
import pathlib
import matplotlib.pyplot as plt
import functools as ft

'''
[1.0 points] Implement a function in task2a.py/task2a.ipynb that implements Otsu’s algorithm for thresholding, and returns a single threshold value.
Segment the images thumbprint.png and polymercell.png, and include the results in your report.
'''
def sum_to_threshhold(arr: np.ndarray, threshold: int):
    sum = 0
    for i, el in enumerate(arr):
        if(i < threshold):
            sum+=el

    return sum

def mean_to_threshhold(arr: np.ndarray, threshold: int, less_than: bool):
    numerator_sum = 0
    divisor_sum = 0

    if(less_than):
        for i, el in enumerate(arr):
            if(i < threshold):
                numerator_sum += i * el
                divisor_sum += el
    else:
        for i, el in enumerate(arr):
            if(i >= threshold):
                numerator_sum += i * el
                divisor_sum += el

    return numerator_sum/divisor_sum

def otsu_thresholding(im: np.ndarray) -> int:
    """
        Otsu's thresholding algorithm that segments an image into 1 or 0 (True or False)
        The function takes in a grayscale image and outputs a boolean image

        args:
            im: np.ndarray of shape (H, W) in the range [0, 255] (dtype=np.uint8)
        return:
            (int) the computed thresholding value
    """
    assert im.dtype == np.uint8
    # START YOUR CODE HERE ### (You can change anything inside this block)
    # You can also define other helper functions

    # Compute normalized histogram
    intensity_count = np.zeros(256)
    for i in range(len(im)):
        for j in range(len(im[i])):
            intensity_count[im[i][j]] += 1

    #test = [9,6,4,5,8,4]
    num_pixels = im.shape[0]*im.shape[1]
    best_between_class_variance = -100
    best_threshold = -1
    # Check for each threshold if it produces a better between class variance
    for threshold in range(1,256):
        background_percentage = sum_to_threshhold(intensity_count, threshold)/num_pixels
        foreground_percentage = 1-background_percentage

        background_mean = mean_to_threshhold(intensity_count, threshold, True)
        foreground_mean = mean_to_threshhold(intensity_count, threshold, False)

        between_class_variance = background_percentage*foreground_percentage*(background_mean-foreground_mean)**2
        if(best_between_class_variance < between_class_variance):
            best_between_class_variance = between_class_variance
            best_threshold = threshold

    threshold = best_threshold
    '''
    plt.imshow(im, cmap="gray")
    plt.show()
    '''
    # Plot intensity distribution
    intensity_levels = list(range(0,256))
    plt.bar(intensity_levels, intensity_count)
    plt.show()

    return threshold
    ### END YOUR CODE HERE ###

if __name__ == "__main__":
    # DO NOT CHANGE
    impaths_to_segment = [
        pathlib.Path("thumbprint.png"),
        pathlib.Path("polymercell.png"),
        pathlib.Path("defective-weld.png")
    ]
    for impath in impaths_to_segment:
        im = utils.read_image(impath)
        threshold = otsu_thresholding(im)
        print("Found optimal threshold:", threshold)

        # Segment the image by threshold
        segmented_image = (im >= threshold)
        assert im.shape == segmented_image.shape, "Expected image shape ({}) to be same as thresholded image shape ({})".format(
            im.shape, segmented_image.shape)
        assert segmented_image.dtype == np.bool, "Expected thresholded image dtype to be np.bool. Was: {}".format(
            segmented_image.dtype)

        segmented_image = utils.to_uint8(segmented_image)

        save_path = "{}-segmented.png".format(impath.stem)
        utils.save_im(save_path, segmented_image)


# -*- coding: utf-8 -*-
"""
Created on Sat Mar 23 22:36:14 2019

INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DO PÁRA - IFPA ANANINDEUA

@author:
        Prof. Dr. Denis C. L. Costa

        Discentes:
            Heictor Alves de Oliveira Costa
            Lucas Pompeu Neves

Grupo de Pesquisa:
                 Gradiente de Modelagem Matemática e
                 Simulação Computacional - GM²SC

Assunto:
        Derivadas Parciais

Nome do sript: derivadas_parciais

Disponível em:
    https://github.com/GM2SC/DEVELOPMENT-OF-MATHEMATICAL-METHODS-IN-
    COMPUTATIONAL-ENVIRONMENT/blob/master/SINEPEM_2019/derivadas_parciais.py

"""

# Bibliotecas

# Cálculo Diferencial e Integral: sympy
import sympy as sy

# Variáveis simbólicas
x,y = sy.symbols('x,y')

print('')

# Função de várias Variáveis: f(x,y)
def f(x,y):
    return 3*x**2*y**3

# (f(x,y), x, 1) --> (Função, variável, ordem da derivada)
# Derivada em função de x: dfx(x,y)
def dfx(x,y):
    return sy.diff(f(x,y), x, 1)
# Derivada em função de y: dfy(x,y)
def dfy(x,y):
    return sy.diff(f(x,y), y, 1)
print('')
print('=======================================================')
print('Função Analisada: f(x,y) =', f(x,y))

print('Derivada Parcial em função de x: dfx(x,y) =', dfx(x,y))

print('Derivada Parcial em função de y: dfy(x,y) =', dfy(x,y))

print('=======================================================')
print('')
# Valor Numérico das Derivadas Parciais
print('Valor Numérico da Derivada Parcial dfx')
x1 = 2; y1 = -1
print('nos pontos x1 e y1 -->', (x1,y1))

VN_dfx= dfx(x,y).subs(x,x1).subs(y,y1)
print('VN_dfx =', VN_dfx)

print('')
print('Valor Numérico da Derivada Parcial dfy')
x2 = 1; y2 = 3
print('nos pontos x2 e y2 -->', (x2,y2))
VN_dfy= dfy(x,y).subs(x,x2).subs(y,y2)
print('VN_dfy =', VN_dfy)

print('')
print('---> Fim do Programa derivadas_parciais <---')


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

from scipy.stats import spearmanr

x = np.arange(2, 20, 0.05)
y1 = np.log(x)
y2 = np.square(x)
y3 = np.exp(x)
y4 = np.sqrt(x)
y5 = np.exp(x) + (np.exp(x)/1.25)*np.sin(x*(2*np.pi))

ydf1 = pd.DataFrame({"x":x, "y":y1, "hue":["Log: Coerrelation = %.4f, SpearmanR = %.4f" % (np.corrcoef(x, y1)[0][1], spearmanr(x, y1)[0])]*len(x)})
ydf2 = pd.DataFrame({"x":x, "y":y2, "hue":["Square: Coerrelation = %.4f, SpearmanR = %.4f" % (np.corrcoef(x, y2)[0][1], spearmanr(x, y2)[0])]*len(x)})
ydf3 = pd.DataFrame({"x":x, "y":y3, "hue":["e^x: Coerrelation = %.4f, SpearmanR = %.4f" % (np.corrcoef(x, y3)[0][1], spearmanr(x, y3)[0])]*len(x)})
ydf4 = pd.DataFrame({"x":x, "y":y4, "hue":["Sqrt: Coerrelation = %.4f, SpearmanR = %.4f" % (np.corrcoef(x, y4)[0][1], spearmanr(x, y4)[0])]*len(x)})
ydf5 = pd.DataFrame({"x":x, "y":y5, "hue":["e^x + sine: Coerrelation = %.4f, SpearmanR = %.4f" % (np.corrcoef(x, y5)[0][1], spearmanr(x, y5)[0])]*len(x)})

df = pd.concat((ydf1, ydf2, ydf3, ydf4, ydf5))

plt.figure(figsize=(8,8))
sns.lineplot(x="x",y="y",hue="hue", data=df)
plt.title(" With Pearson Correlation ")
plt.semilogy()
plt.show()

x = list(sorted(np.random.randn(200)))
y = list(sorted(np.exp(np.random.randn(200)*0.5 + 2 + np.random.randn(200)*0.5)))

plt.figure(figsize=(8,8))
sns.lineplot(x=x,y=y,)
plt.title("SpearmanR = %.4f , Pearson Correlation = %.4f" % (spearmanr(x,y)[0], np.corrcoef(x, y)[0][1]))
plt.show()


import numpy
from matplotlib import pyplot

def forward_difference(f, x0, h):
    return (f(x0+h) - f(x0)) / h
def backward_difference(f, x0, h):
    return (f(x0) - f(x0-h)) / h
def central_difference(f, x0, h):
    return (f(x0+h) - f(x0-h)) / (2*h)

def euler(f, x_end, y0, N):
    x, dx = numpy.linspace(0, x_end, N+1, retstep=True)
    y = numpy.zeros((len(y0),N+1))
    y[:,0] = y0
    for n in range(N):
        y[:,n+1] = y[:,n] + dx * f(x[n], y[:,n])
    return x, dx, y

if __name__=="__main__":
    h = 0.5
    print("Forward difference, h=",h, "y'=",
          forward_difference(numpy.exp, 0, h))
    print("Backward difference, h=",h, "y'=",
          backward_difference(numpy.exp, 0, h))
    print("Central difference, h=",h, "y'=",
          central_difference(numpy.exp, 0, h))
    h = 0.05
    print("Forward difference, h=",h, "y'=",
          forward_difference(numpy.exp, 0, h))
    print("Backward difference, h=",h, "y'=",
          backward_difference(numpy.exp, 0, h))
    print("Central difference, h=",h, "y'=",
          central_difference(numpy.exp, 0, h))
    h_all = 0.5/2**numpy.arange(1,10)
    errors_forward = numpy.zeros_like(h_all)
    errors_backward = numpy.zeros_like(h_all)
    errors_central = numpy.zeros_like(h_all)
    for i, h in enumerate(h_all):
        errors_forward[i] = abs(1 - forward_difference(numpy.exp, 0, h))
        errors_backward[i] = abs(1 - backward_difference(numpy.exp, 0, h))
        errors_central[i] = abs(1 - central_difference(numpy.exp, 0, h))
    pyplot.figure(figsize=(12,6))
    pyplot.loglog(h_all, errors_forward, 'kx', label="Forward")
    pyplot.loglog(h_all, errors_backward, 'bo', label="Backward")
    pyplot.loglog(h_all, errors_central, 'r^', label="Central")
    pyplot.loglog(h_all, h_all/h_all[0]*errors_forward[0], 'b-',
                  label=r"$\propto h$")
    pyplot.loglog(h_all, (h_all/h_all[0])**2*errors_central[0], 'g-',
                  label=r"$\propto h^2$")
    pyplot.xlabel(r"$h$")
    pyplot.ylabel("Error")
    pyplot.legend(loc="upper left")
    pyplot.show()

    def f_sin(x, y):
        return -numpy.sin(x)
    print("Euler's Method")
    x, dx, y = euler(f_sin, 0.5, [1], 5)
    print("dx=", dx, "y(0.5)=", y[0,-1])
    x, dx, y = euler(f_sin, 0.5, [1], 50)
    print("dx=", dx, "y(0.5)=", y[0,-1])
    Npoints = 5*2**numpy.arange(1,10)
    dx_all = 0.5/Npoints
    errors = numpy.zeros_like(dx_all)
    for i, N in enumerate(Npoints):
        x, dx, y = euler(f_sin, 0.5, [1], N)
        errors[i] = abs(y[0,-1] - numpy.cos(0.5))
        dx_all[i] = dx
    pyplot.figure(figsize=(12,6))
    pyplot.loglog(dx_all, errors, 'kx')
    pyplot.loglog(dx_all, errors[0]*(dx_all/dx_all[0])**1, 'b-',
                  label=r"$\propto \Delta x$")
    pyplot.legend(loc='upper left')
    pyplot.xlabel(r"$\Delta x$")
    pyplot.ylabel("Error")
    pyplot.show()

    def f_circle(x, y):
        dydx = numpy.zeros_like(y)
        dydx[0] = -y[1]
        dydx[1] = y[0]
        return dydx
    y0 = numpy.array([1, 0])
    x, dx, y = euler(f_circle, 50, y0, 500)
    pyplot.figure(figsize=(8,8))
    pyplot.plot(y[0,:], y[1,:])
    pyplot.show()
    x, dx, y = euler(f_circle, 50, y0, 5000)
    pyplot.figure(figsize=(8,8))
    pyplot.plot(y[0,:], y[1,:])
    pyplot.show()


import os
import time
import json
import math
import pandas as pd
from scipy import stats
from prettytable import PrettyTable

os.chdir('..')
path = os.getcwd()

def simpleLinear(filename):

    with open(filename, newline='') as csvFile:
        start = time.time()
        df = pd.read_csv(csvFile)
        elapsed = time.time() - start
        print("Read Time ->", round(elapsed, 4), "seconds.")
        start = time.time()
        x = df['X']
        y = df['Y']
        avg_x = x.mean()
        avg_y = y.mean()
        diff_x = []
        diff_y = []
        for index, row in df.iterrows():
            diff_x.append(row['X'] - avg_x)
            diff_y.append(row['Y'] - avg_y)
        b1_numerator = sum([i * j for i, j in zip(diff_x, diff_y)])
        b1_denominator = sum([i ** 2 for i in diff_x])
        b1 = b1_numerator/b1_denominator
        b0 = avg_y - (b1 * avg_x)
        yhat = []
        residuals = []
        for index, row in df.iterrows():
            yhat.append(b0 + b1*row['X'])
            residuals.append(row['Y'] - (b0 + b1*row['X']))
        tss = sum([i ** 2 for i in diff_y])
        rss = sum([i ** 2 for i in residuals])
        rse = math.sqrt(rss/(df.shape[0] - 2))
        stdErrB0 = math.sqrt((rse ** 2) * (1/df.shape[0] + (avg_x ** 2)/b1_denominator))
        stdErrB1 = math.sqrt((rse ** 2)/b1_denominator)
        b0CI = [round(b0 - 2*stdErrB0, 4), round(b0 + 2*stdErrB0, 4)]
        b1CI = [round(b1 - 2*stdErrB1, 4), round(b1 + 2*stdErrB1, 4)]
        b0Tstat = b0/stdErrB0
        b1Tstat = b1/stdErrB1
        b0pval = stats.t.sf(abs(b0Tstat), df.shape[0] - 2)*2
        b1pval = stats.t.sf(abs(b1Tstat), df.shape[0] - 2)*2
        Rsquared = 1 - rss/tss
        adjRsquared = 1 - ((1 - Rsquared) * (df.shape[0] - 1))/(df.shape[0] - 2)
        fStat = (tss - rss)/(rss/(df.shape[0] - 2))
        elapsed = time.time() - start
        results = {
            "intercept" : {
                "b0" : round(b0, 4),
                "std_error" : round(stdErrB0, 4),
                "confidence_interval" : b0CI,
                "t_value" : round(b0Tstat, 4),
                "p_value" : round(b0pval, 6)
                },
            "slope" : {
                "b1" : round(b1, 4),
                "std_error" : round(stdErrB1, 4),
                "confidence_interval" : b1CI,
                "t_value" : round(b1Tstat, 4),
                "p_value" : round(b1pval, 6)
                },
            "equation" : "y = " + str(round(b0, 4)) + " + " + str(round(b1, 4)) + " * x",
            "rss" : round(rss, 4),
            "tss" : round(tss, 4),
            "rse" : round(rse, 4),
            "R_squared" : round(Rsquared, 4),
            "adjRsquared" : round(adjRsquared, 4),
            "f_statistic": round(fStat, 2),
            "degrees_of_freedom" : df.shape[0] - 2,
            "predicted_y" : yhat,
            "residuals" : residuals
            }
        print("Computation Time ->", round(elapsed, 4), "seconds.")
        df['yhat'] = pd.Series(yhat).values
        df['residuals'] = pd.Series(residuals).values
        t = PrettyTable(['X', 'Y', 'Yhat', 'residuals'])
        for index, row in df.iterrows():
            t.add_row(row)
        print(t)
        return results

def main():
    filename = path + '\\data\\autoInsur.csv'
    print("+----------+")
    print("Model Output")
    print("+----------+")
    results = simpleLinear(filename)
    with open(path + '\\src\\simpleLinearModel.json', 'w') as outfile:
        json.dump(results, outfile)
    print("+-----------------+")
    print("Model File Written.")
    print("+-----------------+")

if __name__ == "__main__":
    main()


import numpy as np
import matplotlib.pyplot as plt

class InterpolatingFunction(object):
    def __init__(self):
        print('initializing interpolating function class')
        plt.rcParams["figure.figsize"] = [20, 7]

    def calculateNodes(self,nNodes,plot=False):
        """Calculates Chebyshev points of the second kind (aka Gauss-Lobatto grid points)

        Parameters:
        nNodes (int): number of grid points

        Returns:
        ndarray: array containing all grind points
        """
        j = np.arange(nNodes)
        nodes = -np.cos(j*np.pi/(nNodes-1))

        if plot:
            plt.grid(True)
            plt.plot(nodes,np.zeros(nNodes),'x')
            plt.show()

        return(nodes)

    def calculateWeights(self,nodes,plot=False):
        """Calculates according weights for barycentric interpolation if provided
        nodes are Chebyshev points of second kind

        Parameters:
        nodes (ndarray): nodes

        Returns:
        ndarray: weights
        """

        weights = np.ones(len(nodes))*np.power((-1),np.arange(len(nodes)))
        weights[0]/=2
        weights[-1]/=2

        if plot:
            plt.grid(True)
            plt.plot(nodes,weights,'x')
            plt.show()

        return(weights)

    def calculateBasisFunctions(self,nodes,weights,n,plot=False):
        """Calculates the basis functions l_j(s) for n equidistant points s

        Parameters:
        nodes (ndarray[nNodes]): Gaus-Lobatto nodes
        weights (ndarray[nNodes]): barycentric weights for Gaus-Lobatto nodes

        Returns:
        ndarray[nNodes,n] : values of basis functions on n equidistant points s
        """
        l = np.empty((len(nodes),n))
        s = np.linspace(-1,1,n)

        # [sum_{k=0}^{n} w_k/(s-s_k)]^-1
        temp2 = 1/np.sum(np.repeat(np.reshape(weights,[len(weights),1]),len(s),axis=1)/
                         (np.repeat(np.reshape(s,[1,len(s)]),len(nodes),axis=0)-
                          np.repeat(np.reshape(nodes,[len(nodes),1]),len(s),axis=1)),axis=0)
        for j in range(len(nodes)):
            # w_j/(s-s_j):
            temp1 = weights[j]/(s-nodes[j])
            l[j,:]=temp1*temp2
            if plot:
                plt.plot(s,l[j,:])
        if plot:
            plt.plot(nodes,np.zeros(len(nodes))+1,'x')
            plt.grid(True)
            #plt.yscale("Log")
            plt.show()
        return l

    def interpolateFunction(self,values,basis,plot=False,nodes=None):
        """Calculates a Lagrange interpolation function for given values at the nodes
        with the given basis functions

        Parameters:
        values (ndarray[nNodes]): values at sampling points
        basis (ndarray[nNodes,n]): the basis functions as calculatet with
        corresponding functiong

        Returns:
        ndarray[n] : the interpolation function at n equidistant points
        """
        nNodes = len(values)
        nPoints = len(basis[0])
        p = np.sum(np.repeat(np.reshape(values,(nNodes,1)),nPoints,axis=1)*basis,axis=0)
        if plot:
            plt.plot(np.linspace(-1,1,1000),p)
            plt.plot(nodes,values,'x')
            plt.grid(True)
            plt.show()
        return(p)

    def derivative(self,values,matrix):
        """Calculates the m-th derivateive of a function at barycentric nodes
        given by parameter values at barycentric nodes by means of a differentiation matrix

        Parameters:
        values (ndarray[nNodes]): values at sampling points
        matrix (ndarray[nNodes,nNodes]): m-th differentiation matrix

        Returns:
        ndarray[nNoes] : values of the derivative function at the sampling points
        """

        #p'(s_i)=Sum_{j=0}^n l'_j(s_i)f_j
        p = np.dot(matrix,values)
        return p

    def differentiationMatrix(self,m,nodes,weights,plot=False):
        """Calculates the m-th differentiation matrix for a function given at barycentric nodes

        Parameters:
        m (int): m-th derivative of the given function
        nodes (ndarray[nNodes]): Gaus-Lobatto nodes
        weights (ndarray[nNodes]): barycentric weights for Gaus-Lobatto nodes

        Returns:
        ndarray[nNoes,nNodes] : m-th differentiation matrix
        """

        nNodes=len(nodes)
        #calculate some matrices frequenty used in the following calculation
        temp1=np.repeat(np.reshape(weights,(nNodes,1)),nNodes,axis=1)
        temp2=np.transpose(temp1)
        tempW = temp2/temp1 #w_j / w_i
        temp3=np.repeat(np.reshape(nodes,(nNodes,1)),nNodes,axis=1)
        temp4=np.transpose(temp3)
        tempS = temp3-temp4 #s_i - s_j

        #calculate matrix of first derivative
        temp5=tempW/tempS
        temp5[temp5==np.inf]=0
        temp6=-np.sum(temp5,axis=1)
        row,col = np.diag_indices(temp5.shape[0])
        temp5[row,col] = temp6

        if m==1:
            D = temp5
        elif m>1:
            for i in range(2,m+1):
                temp7 = np.repeat(np.reshape(np.diag(temp5),(nNodes,1)),nNodes,axis=1)
                temp5 = np.nan_to_num(i/tempS*(tempW*temp7-temp5))
                temp6=-np.sum(temp5,axis=1)
                row,col = np.diag_indices(temp5.shape[0])
                temp5[row,col] = temp6
            D = temp5
        else:
            raise Exception('degree of derivative not supported')

        if plot:
            plt.matshow(D)
            plt.colorbar()
            plt.show()

        return D


# Decorrelating your data and dimension reduction
# Dimension reduction summarizes a dataset using its common occuring patterns. In this chapter, you'll learn about the most fundamental of dimension reduction techniques, "Principal Component Analysis" ("PCA"). PCA is often used before supervised learning to improve model performance and generalization. It can also be useful for unsupervised learning. For example, you'll employ a variant of PCA will allow you to cluster Wikipedia articles by their content!

# Correlated data in nature
# You are given an array grains giving the width and length of samples of grain. You suspect that width and length will be correlated. To confirm this, make a scatter plot of width vs length and measure their Pearson correlation.

# Perform the necessary imports
import matplotlib.pyplot as plt
from scipy.stats import pearsonr

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

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

# Scatter plot width vs length
plt.scatter(width, length)
plt.axis('equal')
plt.show()

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

# Display the correlation
print(correlation)

# Decorrelating the grain measurements with PCA
# You observed in the previous exercise that the width and length measurements of the grain are correlated. Now, you'll use PCA to decorrelate these measurements, then plot the decorrelated points and measure their Pearson correlation.

# Import PCA
from sklearn.decomposition import PCA

# Create PCA instance: model
model = PCA()

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

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

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

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

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

# Display the correlation
print(correlation)

# The first principal component
# The first principal component of the data is the direction in which the data varies the most. In this exercise, your job is to use PCA to find the first principal component of the length and width measurements of the grain samples, and represent it as an arrow on the scatter plot.

# The array grains gives the length and width of the grain samples. PyPlot (plt) and PCA have already been imported for you.

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

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

# Fit model to points
model.fit(grains)

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

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

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

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

# Variance of the PCA features
# The fish dataset is 6-dimensional. But what is its intrinsic dimension? Make a plot of the variances of the PCA features to find out. As before, samples is a 2D array, where each row represents a fish. You'll need to standardize the features first.

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

# Create scaler: scaler
scaler = StandardScaler()

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

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

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

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

# Dimension reduction of the fish measurements
# In a previous exercise, you saw that 2 was a reasonable choice for the "intrinsic dimension" of the fish measurements. Now use PCA for dimensionality reduction of the fish measurements, retaining only the 2 most important components.

# The fish measurements have already been scaled for you, and are available as scaled_samples.

# Import PCA
from sklearn.decomposition import PCA

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

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

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

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

# A tf-idf word-frequency array
# In this exercise, you'll create a tf-idf word frequency array for a toy collection of documents. For this, use the TfidfVectorizer from sklearn. It transforms a list of documents into a word frequency array, which it outputs as a csr_matrix. It has fit() and transform() methods like other sklearn objects.

# You are given a list documents of toy documents about pets. Its contents have been printed in the IPython Shell.

# Import TfidfVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer

# Create a TfidfVectorizer: tfidf
tfidf = TfidfVectorizer()

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

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

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

# Print words
print(words)

# Clustering Wikipedia part I
# You saw in the video that TruncatedSVD is able to perform PCA on sparse arrays in csr_matrix format, such as word-frequency arrays. Combine your knowledge of TruncatedSVD and k-means to cluster some popular pages from Wikipedia. In this exercise, build the pipeline. In the next exercise, you'll apply it to the word-frequency array of some Wikipedia articles.

# Create a Pipeline object consisting of a TruncatedSVD followed by KMeans. (This time, we've precomputed the word-frequency matrix for you, so there's no need for a TfidfVectorizer).

# The Wikipedia dataset you will be working with was obtained from here.

# Perform the necessary imports
from sklearn.decomposition import TruncatedSVD
from sklearn.cluster import KMeans
from sklearn.pipeline import make_pipeline

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

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

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

# Clustering Wikipedia part II
# It is now time to put your pipeline from the previous exercise to work! You are given an array articles of tf-idf word-frequencies of some popular Wikipedia articles, and a list titles of their titles. Use your pipeline to cluster the Wikipedia articles.

# A solution to the previous exercise has been pre-loaded for you, so a Pipeline pipeline chaining TruncatedSVD with KMeans is available.

# Import pandas
import pandas as pd

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

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

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

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

Unsupervised Learning in Python/Decorrelating_your_data_and_dimension_reduction.py


import numpy as np

def sigmoid(x: np.ndarray) -> np.ndarray: # x:input / return output of sigmoid func
  """
  ref:
    http://www.kamishima.net/mlmpyja/lr/sigmoid.html
  """
  sigmoid_range = 34.538776394910684
  x = np.clip(x, -sigmoid_range, sigmoid_range)

  return 1.0 / (1.0 + np.exp(-x))

def softmax(x: np.ndarray) -> np.ndarray: # x:input / return output of softmax func
  """
  ref:
    https://qiita.com/shohei-ojs/items/d66783bcead3eb7efd82
    "axis = -1, keepdims = True" is 2dim and 1dim to share process
  """
  c = np.max(x, axis=-1, keepdims=True)
  exp_x = np.exp(x - c) # for overflow
  sum_exp_x = np.sum(exp_x, axis=-1, keepdims=True)
  y = exp_x / sum_exp_x

  return y

def cross_entropy_error(y: np.ndarray, t: np.ndarray) -> float: # y:output of NN, t:correct label / return value of cross entropy error
  if y.ndim == 1:
    t = t.reshape(1, t.size)
    y = y.reshape(1, y.size)

  delta = 1e-7 # to prevent log(0)
  err = -np.sum(t * np.log(y + delta))
  batch_size = y.shape[0] # to normalize
  return err / batch_size

def sigmoid_grad(x: np.ndarray) -> np.ndarray: # fast ver / x:input / return output of sigmoid func
  return (1.0 - sigmoid(x)) * sigmoid(x)


import numpy as np

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

def tanh(z):
    return np.tanh(z)

def sin(z):
    return np.sin(z)

def relu(z):
    return np.maximum(0.001, z)

def softmax(Z):
    return np.exp(Z) / np.sum(np.exp(Z))

def der_sigmoid(z):
    #return sigmoid(z)*(1 - sigmoid(z))
    return  (1.0 / (1 + np.exp(-(z))))*(1 -  1.0 / (1 + np.exp(-(z))))

def der_tanh(z):
    return 1 - np.tanh(z) ** 2

def der_relu(z):
    return (z>0)*1 + (z<0)*0.001