import math
# import numpy as np

def primes_under(N):
    if N == 1:
        return []
    if N == 2:
        return [2]
    if N == 3:
        return [2,3]

    sqrtN = int(round(math.sqrt(N)))
    primes = [2,3]
    for j in range(3, N, 2):
        founddivisor = False
        for p in primes:
            if p > sqrtN:
                break
            elif j % p == 0:
                founddivisor = True
                break
        if not founddivisor:
            primes.append(j)

    return primes

def is_prime(N):
    if N < 0 or type(N) != int:
        return False
    if N == 0 or N == 1:
        return False
    if N == 2 or N == 3:
        return True

    sqrtN = int(round(math.sqrt(N)))+1
    for j in range(2,sqrtN):
        # print(j)
        if N % j == 0:
            return False
    return True

def test_primes_under():
    primes_10 = primes_under(10)
    primes_100 = primes_under(100)
    primes_1000 = primes_under(1000)
    # print(primes_10)
    u = [is_prime(x) for x in primes_10]
    print(u)
    print([(x, is_prime(x)) for x in range(100)])
    is_prime(10)
    # print(primes_100)
    # print(primes_1000)

if __name__ == "__main__":
    test_primes_under()


'''
Lorenz system.

A simplified mathematical model for atmospheric convection. The model is a system of three ordinary differential
equations now known as the Lorenz equations:

dx/dt = sigma * (y - x)
dy/dt = x * (rho - z) - y
dz/dt = xy - beta * z

The equations relate the properties of a two-dimensional fluid layer uniformly warmed from below and cooled from above.
In particular, the equations describe the rate of change of three quantities with respect to time: x is proportional to
the rate of convection, y to the horizontal temperature variation, and z to the vertical temperature variation. The
constants sigma, rho, and beta are system parameters proportional to the Prandtl number, Rayleigh number, and certain
physical dimensions of the layer itself.

From a technical standpoint, the Lorenz system is nonlinear, non-periodic, three-dimensional and deterministic.

SRC: https://en.wikipedia.org/wiki/Lorenz_system

------------------------------------------------------------------------------------------------------------------------

A chaotic map is a map (= evolution function) that exhibits some sort of chaotic behavior.

List of chaotic maps (https://en.wikipedia.org/wiki/List_of_chaotic_maps)
    Map                     Time dom    Space dom  Space dim  Params
    ----------------------------------------------------------------
    Logistic map            discrete    real               1       1
    Lotka-Volterra system   continuous  real               3       4
    Lorenz system           continuous  real               3       3

------------------------------------------------------------------------------------------------------------------------

https://physics.nyu.edu/pine/pymanual/html/chap9/chap9_scipy.html
http://sam-dolan.staff.shef.ac.uk/mas212/notebooks/ODE_Example.html
'''

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from mpl_toolkits.mplot3d import Axes3D

# Declarations and definitions:
rho = 28.0
sigma = 10.0
beta = 8.0 / 3.0

def f(state, t):
    x, y, z = state  # unpack the state vector
    return (sigma * (y - x), x * (rho - z) - y, x * y - beta * z)  # derivatives

state0 = [1.0, 1.0, 1.0]  # the initial state
# t = np.arange(0.0, 40.0, 0.01)

# Loopless:
# t = np.arange(0.0, 1.0, 0.1)
# states = odeint(f, state0, t)
# print(states)

# Loop (compatible with a PRAM simulation):
# states = []
# s = state0
# t0 = 0.0
# for t1 in t[1:]:
#     s = odeint(f, s, [t0,t1])[0]
#     t0 = t1
#     states.append(s)
#
# print(states[:10])

t0 = 0.0
t1 = 1.0
dt = 0.1

r = ode(f).set_integrator('zvode', method='bdf')
# r = ode(f).set_integrator('lsoda')
r.set_initial_value(state0, t0)
while r.successful() and r.t < t1:
    r.integrate(r.t + dt)
    print(f'{round(r.t,1)}: {r.y}')

# Visualize:
# fig = plt.figure()
# ax = fig.gca(projection='3d')
# ax.plot(states[:,0], states[:,1], states[:,2])
# plt.show()


import numpy as np
import matplotlib.pyplot as plt

def ecdf(data):
    """Compute ECDF for a one-dimensional array of measurements."""
    # Number of data points: n
    n = len(data)

    # x-data for the ECDF: x
    x = np.sort(data)

    # y-data for the ECDF: y
    y = np.arange(1, n+1) / n

    return x, y

def main():
    versi_color_petal = [4.7, 4.5, 4.9, 4.0,  4.6, 4.5, 4.7, 3.3, 4.6, 3.9, 3.5, 4.2, 4.0,  4.7, 3.6, 4.4, 4.5, 4.1,
     4.5, 3.9, 4.8, 4.0,  4.9, 4.7, 4.3, 4.4, 4.8, 5.0,  4.5, 3.5, 3.8, 3.7, 3.9, 5.1, 4.5, 4.5,
     4.7, 4.4, 4.1, 4.0,  4.4, 4.6, 4.0,  3.3, 4.2, 4.2, 4.2, 4.3, 3.0,  4.1]

    x_vers, y_vers = ecdf(versi_color_petal)
    plt.plot(x_vers,y_vers, marker='.', linestyle = 'none')
    # can multiple ecdf on the same plot and compare

    plt.xlabel('petal length')
    plt.ylabel('ECDF')

    plt.show()

main()


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

" This pyfile is used to give a numerical solution of ordinary differential equation "

import numpy as np
import matplotlib.pyplot as plt

""" Input:
Interval: [a,b]
Step: h
Differential: f(x,y)
Initial point: y(a) = y0
"""

def EulerMethod(a,b,h,f,y0):
    if b>a:
        step = int((b-a)/h+1)
        x = np.linspace(a,b,step)
        y = np.zeros_like(x)
        y[0]=y0
        for index in range(1,step):
            y[index] = y[index-1] + h*f(x[index-1],y[index-1])
        return y
    else:
        return "Error"

def I_EulerMethod(a,b,h,f,y0):
    if b>a:
        step = int((b-a)/h+1)
        x = np.linspace(a,b,step)
        y = np.zeros_like(x)
        ye = np.zeros_like(x)
        y[0] = y0
        ye[0] = y0
        for index in range(1,step):
            ye[index] = ye[index-1] + h*f(x[index-1],ye[index-1])
            y[index] = y[index-1] + 0.5*h*(f(x[index-1],y[index-1])+f(x[index],ye[index]))
        return y
    else:
        return "Error"

def Draw(a,b,h,f,y0,ft):
    step = int((b-a)/h+1)
    x = np.linspace(a,b,step)
    fig, ax = plt.subplots(dpi=120)
    ye = EulerMethod(a,b,h,f,y0)
    yie = I_EulerMethod(a,b,h,f,y0)
    yt = ft(x)
    ax.plot(x,ye,marker='o',color='r',alpha=0.5,label='Euler Method')
    ax.plot(x,yie,marker='x',color='b',alpha=0.5,label='Improved Euler Method')
    ax.plot(x,yt,marker='v',color='g',alpha=0.5,label='Explicit Function')
    ax.legend()
    fig.show()
    return 0

"""
a = 0
b = 1
h = 0.1
f = lambda x,y: y-2*x/y
y0 = 1
ft = lambda x: np.sqrt(1+2*x)

EulerMethod(a,b,h,f,y0)
I_EulerMethod(a,b,h,f,y0)
Draw(a,b,h,f,y0,ft)
"""


from scipy.fftpack import fft
import numpy as np
from fractions import gcd

"""
A3-Part-1: Minimize energy spread in DFT of sinusoids
Given a signal consisting of two sinusoids, write a function that selects the first M samples from
the signal and returns the positive half of the DFT magnitude spectrum (in dB), such that it has
only two non-zero values.

M is to be calculated as the smallest positive integer for which the positive half of the DFT magnitude
spectrum has only two non-zero values. To get the positive half of the spectrum, first compute the
M point DFT of the input signal (for this you can use the fft function of scipy.fftpack, which is
already imported in this script). Consider only the first (M/2)+1 samples of the DFT and compute the
magnitude spectrum of the positive half (in dB) as mX = 20*log10(abs(X[:M/2+1])), where X is the DFT
of the input.

The input arguments to this function are the input signal x (of length W >= M) consisting of two
sinusoids of frequency f1 and f2, the sampling frequency fs and the value of frequencies f1 and f2.
The function should return the positive half of the magnitude spectrum mX. For this question,
you can assume the input frequencies f1 and f2 to be positive integers and factors of fs, and
that M is even.

Due to the precision of the FFT computation, the zero values of the DFT are not zero but very small
values < 1e-12 (or -240 dB) in magnitude. For practical purposes, all values with absolute value less
than 1e-6 (or -120 dB) can be considered to be zero.

HINT: The DFT magnitude spectrum of a sinusoid has only one non-zero value (in the positive half of
the DFT spectrum) when its frequency coincides with one of the DFT bin frequencies. This happens when
the DFT size (M in this question) contains exactly an integer number of periods of the sinusoid.
Since the signal in this question consists of two sinusoids, this condition should hold true for each
of the sinusoids, so that the DFT magnitude spectrum has only two non-zero values, one per sinusoid.

M can be computed as the Least Common Multiple (LCM) of the sinusoid periods (in samples). The LCM of
two numbers x, y can be computed as: x*y/GCD(x,y), where GCD denotes the greatest common divisor. In
this script (see above) we have already imported fractions.gcd() function that computes the GCD.

Test case 1: For an input signal x sampled at fs = 10000 Hz that consists of sinusoids of frequency
f1 = 80 Hz and f2 = 200 Hz, you need to select M = 250 samples of the signal to meet the required
condition. In this case, output mX is 126 samples in length and has non-zero values at bin indices 2
and 5 (corresponding to the frequency values of 80 and 200 Hz, respectively). You can create a test
signal x by generating and adding two sinusoids of the given frequencies.

Test case 2: For an input signal x sampled at fs = 48000 Hz that consists of sinusoids of frequency
f1 = 300 Hz and f2 = 800 Hz, you need to select M = 480 samples of the signal to meet the required
condition. In this case, output mX is 241 samples in length and has non-zero values at bin indices 3
and 8 (corresponding to the frequency values of 300 and 800 Hz, respectively). You can create a test
signal x by generating and adding two sinusoids of the given frequencies.
"""

def minimizeEnergySpreadDFT(x, fs, f1, f2):
    """
    Inputs:
        x (numpy array) = input signal
        fs (float) = sampling frequency in Hz
        f1 (float) = frequency of the first sinusoid component in Hz
        f2 (float) = frequency of the second sinusoid component in Hz
    Output:
        The function should return
        mX (numpy array) = The positive half of the DFT spectrum (in dB) of the M sample segment of x.
                           mX is (M/2)+1 samples long (M is to be computed)
    """
    ## Your code here
    M = fs // gcd(f1,f2)
    M = int(M)
    X = fft(x[:M])
    mX = 20 * np.log10(abs(X[:M//2+1]))
    return mX


import numpy as np

def pca(X):
    """
    Run PCA on dataset X
    U,S,V = pca(X) computes eigenvectors of the covariance matrix of X
    Return eigenvectors U and the eigenvalues in S
    """
    #########################################################
    #         YOUR CODE HERE                                #
    #########################################################

    # compute the covariance of X and then use the
    # svd function to compute the eigenvectors and
    # eigenvalues of the covariance matrix

    # When computing the covariance remember to divide by
    # the number of rows in X
    m = X.shape[0]
    sigma = X.T @ X / m
    U, S, V = np.linalg.svd(sigma, full_matrices=False)
    ########################################################
    #           END YOUR CODE                              #
    ########################################################
    return U,S,V

def feature_normalize(X):
    Xnorm = (X - X.mean(axis=0))/X.std(axis=0)
    return Xnorm, X.mean(axis=0), X.std(axis=0)

def project_data(X,U,K):
    """
    project_data computes the reduced data representation when projecting only
    on to the top k eigenvectors
    Z = project_data(X, U, K) computes the projection of
    the normalized inputs X into the reduced dimensional space spanned by
    the first K columns of U. It returns the projected examples in Z.
    """
    #########################################################
    #         YOUR CODE HERE                                #
    #########################################################
    topk = U[:, :K]
    Z = X @ topk
    ########################################################
    #           END YOUR CODE                              #
    ########################################################
    return Z

def recover_data(Z,U,K):
    """
    recover_data recovers an approximation of the original data when using the
    projected principal axis U
    X_rec = recover_data(Z, U, K) recovers an approximation the
    original data Z that has been reduced to K dimensions. It returns the
    approximate reconstruction in X_rec.
    """

    #########################################################
    #         YOUR CODE HERE                                #
    #########################################################
    topk = U[:, :K]
    X_rec = Z @ topk.T
    ########################################################
    #           END YOUR CODE                              #
    ########################################################
    return X_rec


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

import sympy
import numpy as np
from math import sqrt

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

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

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

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

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

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

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


"""
Map

- Com map, fazemos o mapeamento de valores para a função.

"""

import numpy as np

area = lambda r: np.pi * (r ** 2)

def c_area(r):
    return np.pi * (r ** 2)

print(area(2))
print(area(5))

raios = list(range(1, 21))
print(raios)

# Forma utilizando map recebe(função, interavel)

areas = map(c_area, raios)
print(areas)
print(type(areas))
print(list(areas))

# Forma 3 map com lambda
print(list(map(lambda r: np.pi * (r ** 2), raios)))

# Obs.: Após a primeira utilização (loop, conversão) ele zera.


"""
Logistic Function: f(x) = 1 / (1+e ^(-x))

Created by: MJ

"""

"""
Ref Stack Overflow:

The sigmoid function is a special case of the Logistic function when L=1, k=1, x0=0.

- L is the maximum value the function can take. e−k(x−x0) is always greater or equal than 0, so the maximum point is achieved when it it 0, and is at L/1.

x0 controls where on the x axis the growth should the, because if you put x0 in the function, x0−x0 cancel out and e0=1, so you end up with f(x0)=L/2, the midpoint of the growth.

the parameter k controls how steep the change from the minimum to the maximum value is.
"""

import math
import numpy as np

def sigmoid(x,library="math"):
	if library=="math":
		return 1/(1+math.exp(-x))
	else:
		return 1/(1+np.exp(-x))

def sigmoid_derivative(x):
	return x*(1-x)

if __name__ == "__main__":
	print(sigmoid(0.75))
	print(sigmoid_derivative(0.75))


from sympy import *

import numpy as np

x = symbols('x')

"""Root approximation using Newton-Raphson Method"""

def newton(function, initial_guess, max_iter):
    xn = initial_guess

    for n in range(0, max_iter):

        fxn = function.subs(x, xn)

        if abs(fxn) < 10 ** -4:  # find the |f(x)| upto appropriate decimal digits

            print('Found solution after', n, 'iterations.')
            try:
                return xn.evalf(4)
            except AttributeError:
                return xn

        Dfxn = diff(function, x).subs(x, xn)

        if Dfxn == 0:
            print('Zero derivative. No solution found.')

            return None

        xn = xn - fxn / Dfxn

    print('Exceeded maximum iterations. No solution found.')

    return None

"""Root approximation using bisection method"""

def bisection(f, a, b, max_iter):
    if f.subs(x, a) * f.subs(x, b) >= 0:
        print("Bisection method fails.")
        return None

    a_n = a
    b_n = b

    for n in range(1, max_iter + 1):
        m_n = (a_n + b_n) / 2
        f_m_n = f.subs(x, m_n)

        if f.subs(x, a_n) * f_m_n < 0:
            a_n = a_n
            b_n = m_n

        elif f.subs(x, b_n) * f_m_n < 0:
            a_n = m_n
            b_n = b_n

        elif f_m_n == 0:
            print("Found exact solution.")
            return m_n

        else:
            print("Bisection method fails.")

            return None
    print("Found approximate solution after", max_iter, "iteration")
    return ((a_n + b_n) / 2)

        '''Root approximation using secant Method'''

def secant(f,a,b,N):
    if f(a)*f(b) >= 0:
        print("Secant method fails.")
        return None
    a_n = a
    b_n = b
    for n in range(1,N+1):
        m_n = a_n - f(a_n)*(b_n - a_n)/(f(b_n) - f(a_n))
        f_m_n = f(m_n)
        if f(a_n)*f_m_n < 0:
            a_n = a_n
            b_n = m_n
        elif f(b_n)*f_m_n < 0:
            a_n = m_n
            b_n = b_n
        elif f_m_n == 0:
            print("Found exact solution.")
            return m_n
        else:
            print("Secant method fails.")
            return None
    return a_n - f(a_n)*(b_n - a_n)/(f(b_n) - f(a_n))


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

# data
xx = [0,1,2,3,4,5,6,7,8,9]
yy = [1,3,2,5,7,8,8,9,10,12]

# mean / average
xx_mean = np.mean(xx)
yy_mean = np.mean(yy)

# total number of value
n = len(xx)

up = 0
down = 0

for i in range(n):
  up += (xx[i] - xx_mean) * (yy[i] - yy_mean)
  down += (xx[i] - xx_mean) **2

m = up/down
c = yy_mean - (m * xx_mean)

print(m, c) # now calculate (y1...yn = m(x1...xn)+c) for each value of

# calculate y1....yn
YY = []

for i in range(n):
  tmp = m * xx[i] + c
  YY.append(tmp)

# ploting regression line
plt.scatter(xx,yy)
plt.plot(xx,YY)

plt.xlabel("X coordinate")
plt.ylabel("Y coordinate")
plt.grid(True)
plt.show()


# Importing the dependancies
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Loading the data in numerical format
X = pd.read_csv('Linear Regression\python\Training Data\Linear_X_Train.csv').values
y = pd.read_csv('Linear Regression\python\Training Data\Linear_Y_Train.csv').values

# Standardising the data (We can even use min-max normalization)
u = X.mean()
std = X.std()
X = (X-u)/std

# Visualising the data
plt.style.use('fivethirtyeight') # Setting a plot style
plt.scatter(X, y)
plt.title("Hardwork vs Performance Graph")
plt.xlabel("Hardwork")
plt.ylabel("Performance")
plt.show()

# METHOD-1: Normal Equation

X_norm = np.concatenate((np.ones((X.shape[0], 1)), X), axis=1)
theta_norm = np.dot(np.linalg.inv(np.dot(X_norm.transpose(), X_norm)), np.dot(X_norm.transpose(), y))
print(f'Normal Equation method: {theta_norm}') # Parameter values

# METHOD-2: Gradient Descent

# Hypothesis function
def cost_function(temp, learning_rate):
    cost_sigma = 0
    for i in range(X_norm.shape[0]):
        cost_sigma += (np.dot(temp.transpose(), X_norm[i])-y[i])**2
    cost = learning_rate*(1/(2*X_norm.shape[0]))*cost_sigma
    return cost

# Updating the parameters
def update_theta(theta, learning_rate):
    temp = np.empty((theta.shape[0], 1))
    for i in range(theta.shape[0]):
        sigma = np.zeros(1, dtype=np.float64)
        for j in range(X_norm.shape[0]):
            sigma += (np.dot(theta.transpose(), X_norm[j])-y[j])*X_norm[j][i]
        temp[i] = (theta[i] - (learning_rate/X_norm.shape[0])*sum(sigma))
    return temp

# Iterating 1000 times (Can be reduced if the rate of change of cost is very low)
cost = [] # appending the costs for visualization
theta = np.zeros((X_norm.shape[1], 1)) # initializing the parameters
n = int(input("Enter the number of iterations: "))
for k in range(n):
    learning_rate = 0.1 # (Ideal but can be triggered)
    theta = update_theta(theta, learning_rate)
    cost.append(cost_function(theta, learning_rate))

print(f'Gradient Descent method: {theta}') # Parameter values

# Visualizing the cost function
plt.plot(np.arange(n), cost)
plt.title("Cost function vs iterations")
plt.xlabel("Iterations")
plt.ylabel("Cost")
plt.show()

# Testing the model
X_test = pd.read_csv('Linear Regression\python\Test\Linear_X_Test.csv').values # Loading data
y_test = np.dot(X_test, theta[1:].transpose()) # Computing the predictions
df = pd.DataFrame(data=y_test, columns=["y"]) # Converting to a dataframe
df.to_csv('Linear Regression\python\y_prediction.csv', index=False) # Saving the dataframe


import numpy as np

def conjgrad(A, b, x):
    """
    A function to solve [A]{x} = {b} linear equation system with the
    conjugate gradient method.
    More at: http://en.wikipedia.org/wiki/Conjugate_gradient_method
    ========== Parameters ==========
    A : matrix
        A real symmetric positive definite matrix.
    b : vector
        The right hand side (RHS) vector of the system.
    x : vector
        The starting guess for the solution.
    """
    r = b - np.dot(A, x)
    p = r
    rsold = np.dot(np.transpose(r), r)

    for i in range(len(b)):
        Ap = np.dot(A, p)
        alpha = rsold / np.dot(np.transpose(p), Ap)
        x = x + np.dot(alpha, p)
        r = r - np.dot(alpha, Ap)
        rsnew = np.dot(np.transpose(r), r)
        #print(" error: ",i,np.sqrt(rsnew) )
        if np.sqrt(rsnew) < 1e-8:
            break
        p = r + (rsnew/rsold)*p
        rsold = rsnew
        #print(" sol: ", x)
    return x

def main():

  A = np.array([[5, -2, 0], [-2, 5, 1], [0, 1, 5]])
  b = np.array([20, 10, -10])
  #print(b)
  #print(b.size)
  x = np.zeros(b.size)
  x=conjgrad(A, b, x)

  print(" The answer is:", x)

if __name__=="__main__":
  main()


def cells():
    '''
    ## Chapter 3 problems
    '''

    '''
    '''

    from sympy import *
    init_printing()

    '''
    '''

    '''
    '''

    A = Matrix([
    [3,     3],
    [2, S(3)/2]])
    A

    '''
    '''

    b = Matrix([6,5])

    '''
    '''

    AUG = A.row_join(b)
    AUG # the augmented matrix

    '''
    '''

    '''
    ### Alice
    '''

    '''
    '''

    AUGA = AUG.copy()
    AUGA[0,:] = AUGA[0,:]/3
    AUGA

    '''
    '''

    AUGA[1,:] = AUGA[1,:] - 2*AUGA[0,:]
    AUGA

    '''
    '''

    AUGA[1,:] = -2*AUGA[1,:]
    AUGA

    '''
    '''

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

    '''
    '''

    '''
    ### Bob
    '''

    '''
    '''

    AUGB = AUG.copy()
    AUGB[0,:] = AUGB[0,:] - AUGB[1,:]
    AUGB

    '''
    '''

    AUGB[1,:] = AUGB[1,:] - 2*AUGB[0,:]
    AUGB

    '''
    '''

    AUGB[1,:] = -1*S(2)/3*AUGB[1,:]
    AUGB

    '''
    '''

    AUGB[0,:] = AUGB[0,:] - S(3)/2*AUGB[1,:]
    AUGB

    '''
    '''

    '''
    '''

    '''
    ### Charlotte
    '''

    '''
    '''

    AUGC = AUG.copy()
    AUGC[0,:], AUGC[1,:] = AUGC[1,:], AUGC[0,:]
    AUGC

    '''
    '''

    AUGC[0,:] = AUGC[0,:]/2
    AUGC

    '''
    '''

    AUGC[1,:] = AUGC[1,:] - 3*AUGC[0,:]
    AUGC

    '''
    '''

    AUGC[1,:] = S(4)/3*AUGC[1,:]
    AUGC

    '''
    '''

    AUGC[0,:] = AUGC[0,:] - S(3)/4*AUGC[1,:]
    AUGC

    '''
    '''

    '''
    ### P3.3
    '''

    '''
    '''

    # define agmented matrices for three systems of eqns. with unique sol'ns
    A = Matrix([
            [ -1, -2, -2],
            [  3, 3, 0]])

    B = Matrix([
            [ 1, -1, -2,  1],
            [-2,  3,  3, -1],
            [-1,  0,  1,  2]])

    C = Matrix([
            [ 2, -2,  3, 2],
            [ 1, -2, -1, 0],
            [-2,  2,  2, 1]])

    '''
    '''

    A

    '''
    '''

    A.rref()

    '''
    '''

    B

    '''
    '''

    B.rref()

    '''
    '''

    C

    '''
    '''

    C.rref()

    '''
    '''

    '''
    ### P3.4
    '''

    '''
    '''

    # now for three systems of eqns. with infinitely many sol'ns
    D = Matrix([
            [ -1, -2, -2],
            [  3, 6,   6]])

    E = Matrix([
            [ 1, -1, -2,  1],
            [-2,  3,  3, -1],
            [-1,  2,  1,  0]])

    F = Matrix([
            [ 2, -2, 3, 2],
            [ 0,  0, 5, 3],
            [-2,  2, 2, 1]])

    '''
    '''

    '''
    ### Solving d)
    '''

    '''
    '''

    D

    '''
    '''

    D.rref()

    '''
    '''

    D[0:2,0:2].nullspace()

    '''
    '''

    # the solutions to the sytem of equations represented by D
    # is of the form    point + nullspace
    point = D.rref()[0][:,2]
    nullspace = D[0:2,0:2].nullspace()

    '''
    '''

    # the point is also called he particular solution
    point

    '''
    '''

    # if A aug matrix is [A|b], then the point satisfies A*point = b.
    print( D[0:2,0:2]*point == D[:,2] )
    D[0:2,0:2]*point

    '''
    '''

    '''
    ### Null space
    '''

    '''
    '''

    # the nullspace of A in aug. matrix [A|b] is one dimensional and spanned by
    n = nullspace[0]
    n
    # every vector n in the nullspace of A satisfies  A*n=0

    '''
    '''

    # so solution to A*x=b is any (point+s*n) where s is any real number
    # since  A*(point +s*n) = A*point + sA*n = A*point + 0 = b.
    # verify claim for 20 values of s in range -5,-4,-3,-2,-1,0,1,2,3,4,5
    for s in range(-5,6):
        print( D[0:2,0:2]*(point + s*n),
               D[0:2,0:2]*(point + s*n) == D[:,2] )

    '''
    '''

    '''
    ### Solving e)
    '''

    '''
    '''

    E

    '''
    '''

    E.rref()

    '''
    '''

    point_E = E.rref()[0][:,3]
    nullspace_E = E[0:3,0:3].nullspace()[0]
    s = symbols('s')
    point_E + s*nullspace_E

    '''
    '''

    '''
    ### Solving f)
    '''

    '''
    '''

    F

    '''
    '''

    F.rref()

    '''
    '''

    point_F = F.rref()[0][:,3]
    nullspace_F = F[0:3,0:3].nullspace()[0]
    s = symbols('s')
    point_F + s*nullspace_F

    '''
    '''

    '''
    '''


#!/usr/bin/env python

"""
Title: Finding the root nearest to x=0 for f(x) = tan(x) - 3 using the Bisection Method

Solution to Problem Set 1, Problem 3
~ Arsh R. Nadkarni

To run:
python bfind_1_3.py

This code is a quantitative way to compute the midpoint of the function range initially and replace a or b,
owing to the sign. The same is iterated until f(midpoint) is within the tolerance value (eps).
The upper limit (b), lower limit (a), and tolerance (eps) can be changed in the function arguments on line 51.

"""
# import libraries
import sys
import numpy as np

# define the function
def f(x):
    return np.tan(x) - 3
# find the root using the bisection method
def bisection(a,b,eps):
    # ensure that the a is negative and b is positive
	if f(a) > 0:
		a = b
		b = a
	elif f(a)*f(b) > 0: # check the validity of the bounds used
		print("Invalid Bounds. Root cannot be found!")
		exit()
	# define the mid-point
	mid = (a+b)/2
	# while loop to iterate the mid-point to until it is within eps
	while np.abs(f(mid)) >= eps:
		# update bounds
		if f(mid) >= 0:
			b = mid
		else:
			a = mid
		# calculate midpoint with updated bounds
		mid = (a+b)/2
	return mid

answer = bisection(-(np.pi/2),(np.pi/2),1e-3) # call the function

print("The root of f(x) = tan(x) - 3 nearest to x = 0 occurs at x =", answer) # print the answer


"""
@author: MatteoRaso
"""
from numpy import pi, sqrt
from random import uniform

def pi_estimator(iterations: int):
    """An implementation of the Monte Carlo method used to find pi.
    1. Draw a 2x2 square centred at (0,0).
    2. Inscribe a circle within the square.
    3. For each iteration, place a dot anywhere in the square.
    3.1 Record the number of dots within the circle.
    4. After all the dots are placed, divide the dots in the circle by the total.
    5. Multiply this value by 4 to get your estimate of pi.
    6. Print the estimated and numpy value of pi
    """

    circle_dots = 0

    # A local function to see if a dot lands in the circle.
    def circle(x: float, y: float):
        distance_from_centre = sqrt((x ** 2) + (y ** 2))
        # Our circle has a radius of 1, so a distance greater than 1 would land outside the circle.
        return distance_from_centre <= 1

    circle_dots = sum(
        int(circle(uniform(-1.0, 1.0), uniform(-1.0, 1.0))) for i in range(iterations)
    )

    # The proportion of guesses that landed within the circle
    proportion = circle_dots / iterations
    # The ratio of the area for circle to square is pi/4.
    pi_estimate = proportion * 4
    print("The estimated value of pi is ", pi_estimate)
    print("The numpy value of pi is ", pi)
    print("The total error is ", abs(pi - pi_estimate))

if __name__ == "__main__":
    import doctest

    doctest.testmod()


"""File for non sklearn metrics that are to be used for reference for tests"""
from typing import Optional

import numpy as np
from sklearn.metrics._regression import _check_reg_targets
from sklearn.utils.validation import check_consistent_length

def symmetric_mean_absolute_percentage_error(
    y_true: np.ndarray,
    y_pred: np.ndarray,
    sample_weight: Optional[np.ndarray] = None,
    multioutput: str = 'uniform_average'
):
    r"""Symmetric mean absolute percentage error regression loss.
    <https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error>`_ (SMAPE):

    .. math:: \text{SMAPE} = \frac{2}{n}\sum_1^n\frac{max(|   y_i - \hat{y_i} |}{| y_i | + | \hat{y_i} |, \epsilon)}

    Where :math:`y` is a tensor of target values, and :math:`\hat{y}` is a tensor of predictions.

    Parameters
    ----------
    y_true : array-like of shape (n_samples,) or (n_samples, n_outputs)
        Ground truth (correct) target values.
    y_pred : array-like of shape (n_samples,) or (n_samples, n_outputs)
        Estimated target values.
    sample_weight : array-like of shape (n_samples,), default=None
        Sample weights.
    multioutput : {'raw_values', 'uniform_average'} or array-like
        Defines aggregating of multiple output values.
        Array-like value defines weights used to average errors.
        If input is list then the shape must be (n_outputs,).
        'raw_values' :
            Returns a full set of errors in case of multioutput input.
        'uniform_average' :
            Errors of all outputs are averaged with uniform weight.
    Returns
    -------
    loss : float or ndarray of floats in the range [0, 1]
        If multioutput is 'raw_values', then symmetric mean absolute percentage error
        is returned for each output separately.
        If multioutput is 'uniform_average' or an ndarray of weights, then the
        weighted average of all output errors is returned.
        MAPE output is non-negative floating point. The best value is 0.0.
        But note the fact that bad predictions can lead to arbitarily large
        MAPE values, especially if some y_true values are very close to zero.
        Note that we return a large value instead of `inf` when y_true is zero.

    """
    _, y_true, y_pred, multioutput = _check_reg_targets(y_true, y_pred, multioutput)
    check_consistent_length(y_true, y_pred, sample_weight)
    epsilon = np.finfo(np.float64).eps
    smape = 2 * np.abs(y_pred - y_true) / np.maximum(np.abs(y_true) + np.abs(y_pred), epsilon)
    output_errors = np.average(smape, weights=sample_weight, axis=0)
    if isinstance(multioutput, str):
        if multioutput == 'raw_values':
            return output_errors
        # pass None as weights to np.average: uniform mean
        multioutput = None

    return np.average(output_errors, weights=multioutput)


import numpy as np
import matplotlib.pyplot as plt

# We Will Solve the Diff Equation: dA/dt = -λA = f(A,t), 0<=t<=8 [s]
def f(t, A):
    λ = np.log(2)/2 # Constant
    f = -λ*A
    return f

# Fourth Order Runge Kutta Method (τ: the step)
def RK4(τ):
    # Interval Edges
    a, b = 0, 8
    # Number of Repetitions
    N = int((b - a) / τ)

    w = np.zeros(N+1)
    t = np.zeros(N+1)

    # Starting Points
    w[0], t[0] = 1, 0

    for i in range(N):
        k1 = τ * f(t[i],w[i])
        k2 = τ * f(t[i]+τ/2,w[i]+k1/2)
        k3 = τ * f(t[i]+τ/2,w[i]+k2/2)
        k4 = τ * f(t[i]+τ,w[i]+k3)
        w[i+1] = w[i] + 1/6 * (k1 + 2*k2 + 2*k3 + k4)
        t[i+1] = t[i] + τ

    return w, t

# We Compute the Uncertenty
def unc_RK4(τ):
    unc = np.zeros(11)
    rk4_τ, rk4_2τ = RK4(τ)[0], RK4(τ/2)[0]

    for i in range(1,11):
        unc[i] = (rk4_τ[i]-rk4_2τ[2*i])/15

    return unc

if __name__ == '__main__':

    w_rk4, t_rk4, unc_rk4 = RK4(0.8)[0], RK4(0.8)[1], unc_RK4(0.8)

    print('\nComputed with 2^nd Order Runge Kutta Method:')
    for i in range(11):
        print(f"\tStep {i} with t_{i}={t_rk4[i]:.1f}: {w_rk4[i]:.8f} ± {unc_rk4[i]:.8f}")

    # Visualize the Results
    ## Plot the Solutions for Both Methods
    plt.plot(t_rk4, w_rk4, color='black', marker=".", linewidth=1, label='Runge Kutta 4^th Order')

    ## How Graph is Shown (legend, axis titles, graph title)
    plt.legend(loc='upper center', prop={'size': 10})
    plt.ylabel('A(t) [kBq]')
    plt.xlabel('Time [s]')
    plt.title('Solution of Differential Equation')

    ## Show the major grid lines with dark grey lines
    plt.grid(b=True, which='major', color='#666666', linestyle='--')

    ## Show the minor grid lines
    plt.minorticks_on()
    plt.grid(b=True, which='minor', color='#999999', linestyle='--', alpha=0.2)

    ## Fix Quality
    plt.tight_layout()

    ## Show the Graph
    plt.show()


# GRADED FUNCTION
import numpy as np
import numpy.linalg as la

verySmallNumber = 1e-14 # That's 1×10⁻¹⁴ = 0.00000000000001

# Our first function will perform the Gram-Schmidt procedure for 4 basis vectors.
def gsBasis4(A) :
    B = np.array(A, dtype=np.float_) # Make B as a copy of A, since we're going to alter it's values.
    # The zeroth column is easy, since it has no other vectors to make it normal to.
    # All that needs to be done is to normalise it. I.e. divide by its modulus, or norm.
    B[:, 0] = B[:, 0] / la.norm(B[:, 0])
    # For the first column, we need to subtract any overlap with our new zeroth vector.
    B[:, 1] = B[:, 1] - B[:, 1] @ B[:, 0] * B[:, 0]
    # If there's anything left after that subtraction, then B[:, 1] is linearly independant of B[:, 0]
    # If this is the case, we can normalise it. Otherwise we'll set that vector to zero.
    if la.norm(B[:, 1]) > verySmallNumber :
        B[:, 1] = B[:, 1] / la.norm(B[:, 1])
    else :
        B[:, 1] = np.zeros_like(B[:, 1])
    # Now we need to repeat the process for column 2.
    # Insert two lines of code, the first to subtract the overlap with the zeroth vector,
    # and the second to subtract the overlap with the first.
    B[:, 2] = B[:, 2] - B[:, 2] @ B[:, 0] * B[:, 0]
    B[:, 2] = B[:, 2] - B[:, 1] @ B[:, 0] * B[:, 0]
    # Again we'll need to normalise our new vector.
    # Copy and adapt the normalisation fragment from above to column 2.
    if la.norm(B[:, 2]) > verySmallNumber :
        B[:, 2] = B[:, 2] / la.norm(B[:, 2])
    else :
        B[:, 2] = np.zeros_like(B[:, 2])
    # Finally, column three:
    # Insert code to subtract the overlap with the first three vectors.
    B[:, 3] = B[:, 3] - B[:, 3] @ B[:, 0] * B[:, 0]
    B[:, 3] = B[:, 3] - B[:, 3] @ B[:, 1] * B[:, 1]
    B[:, 3] = B[:, 3] - B[:, 3] @ B[:, 2] * B[:, 2]
    # Now normalise if possible
    if la.norm(B[:, 3]) > verySmallNumber :
        B[:, 3] = B[:, 3] / la.norm(B[:, 3])
    else :
        B[:, 3] = np.zeros_like(B[:, 3])
    # Finally, we return the result:
    return B

# In the second function we will generalise the procedure.
# Previously, we could only have four vectors, and there was a lot of repeating in the code.
# We'll use a for-loop here to iterate the process for each vector.
def gsBasis(A) :
    B = np.array(A, dtype=np.float_) # Make B as a copy of A, since we're going to alter it's values.
    # Loop over all vectors, starting with zero, label them with i
    for i in range(B.shape[1]) :
        # Inside that loop, loop over all previous vectors, j, to subtract.
        for j in range(i) :
            B[:, i] = B[:, i] - B[:, i] @ B[:, j] * B[:, j]
        # Next insert code to do the normalisation test for B[:, i]
        if la.norm(B[:, i]) > verySmallNumber :
            B[:, i] = B[:, i] / la.norm(B[:, i])
        else :
            B[:, i] = np.zeros_like(B[:, i])
    # Finally, we return the result:
    return B

# This function uses the Gram-schmidt process to calculate the dimension
# spanned by a list of vectors.
# Since each vector is normalised to one, or is zero,
# the sum of all the norms will be the dimension.
def dimensions(A) :
    return np.sum(la.norm(gsBasis(A), axis=0))

#Test cases:
V = np.array([[1,0,2,6],
              [0,1,8,2],
              [2,8,3,1],
              [1,-6,2,3]], dtype=np.float_)
gsBasis4(V)

# Once you've done Gram-Schmidt once,
# doing it again should give you the same result.
U = gsBasis4(V)
gsBasis4(U)

# Try the general function.
gsBasis(V)

# See what happens for non-square matrices
A = np.array([[3,2,3],
              [2,5,-1],
              [2,4,8],
              [12,2,1]], dtype=np.float_)
gsBasis(A)

dimensions(A)

B = np.array([[6,2,1,7,5],
              [2,8,5,-4,1],
              [1,-6,3,2,8]], dtype=np.float_)
gsBasis(B)

dimensions(B)

# Now let's see what happens when we have one vector that is a linear combination of the others.
C = np.array([[1,0,2],
              [0,1,-3],
              [1,0,2]], dtype=np.float_)
gsBasis(C)

dimensions(C)


# -*- coding: utf-8 -*-
"""
Created on Thu Dec  2 17:55:16 2021

@author: Oliver
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate
"""Numerical Integration Methods"""

"""Rectangular Rule""" """INTEGRATION"""

def calculate_dx (a, b, n):
    return (b-a)/float(n)

def rect_rule (f, a, b, n):
    total = 0.0
    dx = calculate_dx(a, b, n)
    for k in range (0, n):
        total = total + f((a + (k*dx)))
    return dx*total

def f(x):
    return x**2
print(rect_rule(f, 0, 10, 100000))

"""Trapezoidal Rule"""

def trapz(f,a,b,N=50):
    x = np.linspace(a,b,N+1) # N+1 points make N subintervals
    y = f(x)
    y_right = y[1:] # right endpoints
    y_left = y[:-1] # left endpoints
    dx = (b - a)/N
    T = (dx/2) * np.sum(y_right + y_left)
    return T

def f(x):
    return np.exp(-x**2)

a=1#float(input("Please enter a value for the lower bound:\n"))
b=-1#float(input("Please enter a value for the upper bound:\n"))
n=1000#int(input("Please enter a value for the number of intervals:\n"))
print(trapz(f,a,b,n))

# Graphing the Trapezoidal Rule

x = np.linspace(-1.5,1.5,100)
y = np.exp(x**2)
plt.plot(x,y)
x0 = 0; x1 = 1;
y0 = np.exp(x0**2); y1 = np.exp(x1**2);
plt.fill_between([x0,x1],[y0,y1])
plt.xlim([-1.5,1.5]); plt.ylim([0,10]);
plt.show()
A = 0.5*(y1 + y0)*(x1 - x0)
print("Trapezoid area:", A)

"""Simpsons Rule Method"""

def simps(f,a,b,N=50):
    if N % 2 == 1:
        raise ValueError("N must be an even integer.")
    dx = (b-a)/N
    x = np.linspace(a,b,N+1)
    y = f(x)
    S = dx/3 * np.sum(y[0:-1:2] + 4*y[1::2] + y[2::2]) # x[startAt:endBefore:skip]
    return S
# https://stackoverflow.com/questions/9027862/what-does-listxy-do
f = lambda x: x**3
solution = simps(f,1,2,24)
print(solution)

"""Adaptive Simpson Algorithm"""

es = 0.0001

def simps(f,a,b,N):
    if N % 2 == 1:
        raise ValueError("N must be an even integer.")
    dx = (b-a)/N
    x = np.linspace(a,b,N+1)
    y = f(x)
    S = dx/3 * np.sum(y[0:-1:2] + 4*y[1::2] + y[2::2])
    #print(S)
    return S

def f(x):
    return x**2+4*x-12

ansol = scipy.integrate.quad(f,-10,10)

for N in range(2,10,1):
    es = 0.0001
    integral = simps(f,-10,10,N)
    et = (integral - ansol[0])/ansol[0]
    if abs(et) <= es:
        print(f'integral and error {integral, et}')
        break

# IDK TRY TOMORROW WHEN YOU ARE LESS TIRED

# "structured" adaptive version, translated from Racket
def _quad_simpsons_mem(f, a, fa, b, fb):
    """Evaluates the Simpson's Rule, also returning m and f(m) to reuse"""
    m = (a + b) / 2
    fm = f(m)
    return (m, fm, abs(b - a) / 6 * (fa + 4 * fm + fb))

def _quad_asr(f, a, fa, b, fb, eps, whole, m, fm):
    """
    Efficient recursive implementation of adaptive Simpson's rule.
    Function values at the start, middle, end of the intervals are retained.
    """
    lm, flm, left  = _quad_simpsons_mem(f, a, fa, m, fm)
    rm, frm, right = _quad_simpsons_mem(f, m, fm, b, fb)
    delta = left + right - whole
    if abs(delta) <= 15 * eps:
        return left + right + delta / 15
    return _quad_asr(f, a, fa, m, fm, eps/2, left , lm, flm) +\
           _quad_asr(f, m, fm, b, fb, eps/2, right, rm, frm)

def quad_asr(f, a, b, eps):
    """Integrate f from a to b using Adaptive Simpson's Rule with max error of eps."""
    fa, fb = f(a), f(b)
    m, fm, whole = _quad_simpsons_mem(f, a, fa, b, fb)
    return _quad_asr(f, a, fa, b, fb, eps, whole, m, fm)

from math import sin
print(quad_asr(sin, 0, 1, 1e-09))


# -*- coding: utf-8 -*-
"""
File per la soluzione di sistemi Lineari
"""
import numpy as np

def Lsolve(L,b):
    """
    Risoluzione con procedura forward di Lx=b con L triangolare inferiore
     Input: L matrice triangolare inferiore
            b termine noto
    Output: x: soluzione del sistema lineare
            flag=  0, se sono soddisfatti i test di applicabilità
                   1, se non sono soddisfatti
    """
#test dimensione
    m,n=L.shape
    flag=0;
    if n != m:
        print('errore: matrice non quadrata')
        flag=1
        x=[]
        return x, flag

     # Test singolarita'
    if np.all(np.diag(L)) != True:
         print('el. diag. nullo - matrice triangolare inferiore')
         x=[]
         flag=1
         return x, flag
    # Preallocazione vettore soluzione
    x=np.zeros((n,1))

    for i in range(n):
         s=np.dot(L[i,:i],x[:i]) #scalare=vettore riga * vettore colonna
         x[i]=(b[i]-s)/L[i,i]

    return x,flag

def Usolve(U,b):

    """
    Risoluzione con procedura backward di Rx=b con R triangolare superiore
     Input: U matrice triangolare superiore
            b termine noto
    Output: x: soluzione del sistema lineare
            flag=  0, se sono soddisfatti i test di applicabilità
                   1, se non sono soddisfatti

    """
#test dimensione
    m,n=U.shape
    flag=0;
    if n != m:
        print('errore: matrice non quadrata')
        flag=1
        x=[]
        return x, flag

     # Test singolarita'
    if np.all(np.diag(U)) != True:
         print('el. diag. nullo - matrice triangolare superiore')
         x=[]
         flag=1
         return x, flag
    # Preallocazione vettore soluzione
    x=np.zeros((n,1))

    for i in range(n-1,-1,-1):
         s=np.dot(U[i,i+1:n],x[i+1:n]) #scalare=vettore riga * vettore colonna
         x[i]=(b[i]-s)/U[i,i]

    return x,flag

def LUsolve(L,U,P,b):
     """
     Risoluzione a partire da PA =LU assegnata
     """
     Pb=np.dot(P,b)
     y,flag=Lsolve(L,Pb)
     if flag == 0:
         x,flag=Usolve(U,y)
     else:
        return [],flag

     return x,flag

def LU_nopivot(A):
    """
    % Fattorizzazione PA=LU senza pivot   versione vettorizzata
    In output:
    L matrice triangolare inferiore
    U matrice triangolare superiore
    P matrice identità
    tali che  LU=PA=A
    """
    # Test dimensione
    m,n=A.shape

    flag=0;
    if n!=m:
      print("Matrice non quadrata")
      L,U,P,flag=[],[],[],1
      return P,L,U,flag

    P=np.eye(n);
    U=A.copy();
 # Fattorizzazione
    for k in range(n-1):
       #Test pivot
          if U[k,k]==0:
            print('elemento diagonale nullo')
            L,U,P,flag=[],[],[],1
            return P,L,U,flag

  #     Eliminazione gaussiana
          U[k+1:n,k]=U[k+1:n,k]/U[k,k]                                   # Memorizza i moltiplicatori
          U[k+1:n,k+1:n]=U[k+1:n,k+1:n]-np.outer(U[k+1:n,k],U[k,k+1:n])  # Eliminazione gaussiana sulla matrice

    L=np.tril(U,-1)+np.eye(n)  # Estrae i moltiplicatori
    U=np.triu(U)           # Estrae la parte triangolare superiore + diagonale
    return P,L,U,flag

def LU_nopivotv(A):
    """
    % Fattorizzazione PA=LU senza pivot   versione vettorizzata intermedia
    In output:
    L matrice triangolare inferiore
    U matrice triangolare superiore
    P matrice identità
    tali che  LU=PA=A
    """
    # Test dimensione
    m,n=A.shape

    flag=0;
    if n!=m:
      print("Matrice non quadrata")
      L,U,P,flag=[],[],[],1
      return P,L,U,flag

    P=np.eye(n);
    U=A.copy();
 # Fattorizzazione
    for k in range(n-1):
       #Test pivot
          if U[k,k]==0:
            print('elemento diagonale nullo')
            L,U,P,flag=[],[],[],1
            return P,L,U,flag

  #     Eliminazione gaussiana
          for i in range(k+1,n):
             U[i,k]=U[i,k]/U[k,k]                                   # Memorizza i moltiplicatori
             U[i,k+1:n]=U[i,k+1:n]-U[i,k]*U[k,k+1:n]  # Eliminazione gaussiana sulla matrice

    L=np.tril(U,-1)+np.eye(n)  # Estrae i moltiplicatori
    U=np.triu(U)           # Estrae la parte triangolare superiore + diagonale
    return P,L,U,flag

def LU_nopivotb(A):
    """
    % Fattorizzazione PA=LU senza pivot  versione base
    In output:
    L matrice triangolare inferiore
    U matrice triangolare superiore
    P matrice identità
    tali che  LU=PA=A
    """
    # Test dimensione
    m,n=A.shape
    flag=0;
    if n!=m:
      print("Matrice non quadrata")
      L,U,P,flag=[],[],[],1
      return P,L,U,flag

    P=np.eye(n);
    U=A.copy();
 # Fattorizzazione
    for k in range(n-1):
         #Test pivot

          if U[k,k]==0:
            print('elemento diagonale nullo')
            L,U,P,flag=[],[],[],1
            return P,L,U,flag

  #     Eliminazione gaussiana
          for i in range(k+1,n):
                U[i,k]=U[i,k]/U[k,k]
                for j in range(k+1,n):                                 # Memorizza i moltiplicatori
                  U[i,j]=U[i,j]-U[i,k]*U[k,j]  # Eliminazione gaussiana sulla matrice

    L=np.tril(U,-1)+np.eye(n)  # Estrae i moltiplicatori
    U=np.triu(U)           # Estrae la parte triangolare superiore + diagonale
    return P,L,U,flag

def swapRows(A,k,p):
    A[[k,p],:] = A[[p,k],:]

def LU_pivot(A):
    """
    % Fattorizzazione PA=LU con pivot
    In output:
    L matrice triangolare inferiore
    U matrice triangolare superiore
    P matrice di permutazione
    tali che  PA=LU
    """
    # Test dimensione
    m,n=A.shape
    flag=0;
    if n!=m:
      print("Matrice non quadrata")
      L,U,P,flag=[],[],[],1
      return P,L,U,flag

    P=np.eye(n);
    U=A.copy();
 # Fattorizzazione
    for k in range(n-1):
       #Scambio di righe nella matrice U e corrispondente scambio nella matrice di permutazione per
       # tenere traccia degli scambi avvenuti

       #Fissata la colonna k-esima calcolo l'indice di riga p a cui appartiene l'elemento di modulo massimo a partire dalla riga k-esima
          p = np.argmax(abs(U[k:n,k])) + k
          if p != k:
              swapRows(P,k,p)
              swapRows(U,k,p)

  #     Eliminazione gaussiana
          U[k+1:n,k]=U[k+1:n,k]/U[k,k]                                   # Memorizza i moltiplicatori
          U[k+1:n,k+1:n]=U[k+1:n,k+1:n]-np.outer(U[k+1:n,k],U[k,k+1:n])  # Eliminazione gaussiana sulla matrice

    L=np.tril(U,-1)+np.eye(n)  # Estrae i moltiplicatori
    U=np.triu(U)           # Estrae la parte triangolare superiore + diagonale
    return P,L,U,flag

"""
Risolve n sistemi lineari che condividono la matrice A, ma ognuno ha come vettore b,
una colonna di A

Se B = id allora X = inversa di A
"""
def solve_nsis(A,B):
  # Test dimensione
    m,n=A.shape
    flag=0;
    if n!=m:
      print("Matrice non quadrata")

      return

    Y= np.zeros((n,n))
    X= np.zeros((n,n))
    P,L,U,flag= LU_nopivot(A)

    if flag==0:
        for i in range(n):
            y,flag=Lsolve(L,np.dot(P,B[:,i]))
            # squeezy toglie la seconda dimensione(y era una matrice nx1)
            Y[:,i]=y.squeeze(1)
            x,flag= Usolve(U,Y[:,i])
            X[:,i]=x.squeeze(1)
    else:
        print("Elemento diagonale nullo")
        X=[]
    return X


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

import numpy as np
from functools import reduce

ar = np.array

def gauss(A, b):
    n = A.shape[0]

    for k in range(n-1):
        i = slice(k+1,n)
        lk = np.divide(A[i, k] , A[k, k])
        A[i, i] = A[i,i] - np.multiply(A[k,i], lk)
        b[i] = b[i] - np.multiply(b[k], lk[:, np.newaxis])

    return A, b

# Factorización LU
# Algoritmo que reescribe la matriz A para que la parte superior almacene
# la matriz U de la descomposición LU
def LU(A):
    n = A.shape[0]
    for k in range(n-1):
        i = slice(k+1,n)
        A[i, k] = np.divide(A[i, k] , A[k, k])
        A[i,i] = A[i,i] - np.multiply(A[i, k], A[k,i])

    U = np.triu(A)
    L = np.tril(A)
    np.fill_diagonal(L, 1)
    return L, U

L, U = LU(ar([[1, 2], [3, 4]]))

np.matmul(L, U)

# Generar mil lados derechos, y calcular con eliminación gaussiana
# Generar los matrices

A = ar([[3, 0, 0, 0],
        [2, 1, 0, 0],
        [1, 5, 1, 0],
        [7, 9, 8, 4]])

b = ar([[-9, 12],
        [6,  -1],
        [2,   0],
        [5,   1]])

mil_bs = [ b for i in range(1000) ]
mil_bs = reduce(lambda a, b: np.append(a, b, axis = 1), mil_bs)

def sustitucion_adelante(A, b):
    """A matriz triangular inferior"""
    n = A.shape[0]
    x = np.zeros((n, b.shape[1]))

    x[0,:] = b[0,:] / A[0, 0]

    for i in range(1, n):
        x[i,:] = b[i,:] - np.matmul(A[i,:i], x[:i,:])
        x[i,:] = x[i,:] / A[i, i]
#
    return x

def sustitucion_atras(A, b):
    """A matriz triangular superior"""
    n = A.shape[0]
    x = np.zeros((n, b.shape[1]))

    x[-1,:] = b[-1,:] / A[-1, -1]

    for i in range(n-1, -1, -1):
        x[i,:] = b[i,:] - np.matmul(A[i,(i+1):], x[(i + 1):,:])
        x[i,:] = x[i,:] / A[i, i]
#
    return x

# Tiempo que se tarda en ejecutar eliminación gaussiana
def fun1_gauss():
    gauss(A, mil_bs)
    return

fun1_gauss()

#%timeit fun1_gauss()
#The slowest run took 10.18 times longer than the fastest. This could mean that an intermediate result is being cached.
#10000 loops, best of 3: 70.2 µs per loop

# Tiempo que se tarda en Resolver con sustitución hacia adelante y hacia atrás
def fun2_LU():
    L, U = LU(A)
    # Paso 1: Resolvemos LY = b
    y = sustitucion_adelante(L, mil_bs)
    # Paso 2: Resolvemos Ux = Y
    sustitucion_atras(U, y)
    return

fun2_LU()

#%timeit fun2_LU()
#The slowest run took 8.52 times longer than the fastest. This could mean that an intermediate result is being cached.
#10000 loops, best of 3: 164 µs per loop


import numpy as np
import matplotlib.pyplot as plt

xc = np.linspace(0, 1, 101)  # x coordinates for plotting

def f(x):
    return 1 + 2*x*(1-x)

import sympy as sym
x = sym.symbols('x')
psi_0 = 1
psi_1 = sym.sin(sym.pi*x)

half = sym.Rational(1,2)
u = 1*psi_0 + half*psi_1

# How to combine c_0*psi_0 + c_1*psi_1 to match f?
# Intuitively, c_0=c_1=1...
# Turn u to function so we can plot and compute with it
u = sym.lambdify([x], u, modules='numpy')

print('L2 error of intuitive approximation:', end=' ')
e = f(xc) - u(xc)
dx = xc[1] - xc[0]
print(np.sqrt(dx*np.sum(e**2)))

plt.plot(xc, f(xc), 'r--')
plt.plot(xc, u(xc), 'b-')
plt.legend(['exact', 'intuitive approximation'])
plt.savefig('tmp1.png'); plt.savefig('tmp1.pdf')

# Do the calculations in the least squares or project method
A = sym.zeros(2, 2)
b = sym.zeros(2, 1)
A[0,0] = sym.integrate(psi_0*psi_0, (x, 0, 1))
A[0,1] = sym.integrate(psi_0*psi_1, (x, 0, 1))
A[1,0] = A[0,1]
A[1,1] = sym.integrate(psi_1*psi_1, (x, 0, 1))
b[0] = sym.integrate(f(x)*psi_0, (x, 0, 1))
b[1] = sym.integrate(f(x)*psi_1, (x, 0, 1))
print('A:', A)
print('b:', b)
c = A.LUsolve(b)
c = [sym.simplify(c[i,0]) for i in range(c.shape[0])]
print('c:', c, [c_.evalf() for c_ in c])
u = c[0]*psi_0 + c[1]*psi_1
print('u:', u)
print(sym.latex(u))
print(sym.latex(A))
print(sym.latex(c))
print(sym.latex(b))
# Turn u to function so we can plot it
u = sym.lambdify([x], u, modules='numpy')

print('L2 error of least squares approximation:', end=' ')
e = f(xc) - u(xc)
dx = xc[1] - xc[0]
print(np.sqrt(dx*np.sum(e**2)))

plt.plot(xc, u(xc), 'k-')
plt.legend(['exact', 'guess', 'least squares approx.'],
           loc='lower center')
plt.savefig('tmp2.png'); plt.savefig('tmp2.pdf')
plt.show()


import numpy as np

# Write a function that takes as input a list of numbers, and returns
# the list of values given by the softmax function.
def softmax(L):
    expL = np.exp(L)
    return np.divide (expL, expL.sum())

18-11-22-Deep-Learning-with-PyTorch/01-Introduction to neural networks/04-Softmax/softmax.py


########################################################################################################################
# BUILDING LINEAR REGRESSION FROM SCRATCH
# ...WE ARE GOING TO GET IT WITH THE MAIN + HELPER FUNCTIONS METHOD...
# ...WE ARE GOING TO MINIMIZE THE MEAN SQUARED ERROR (ORDINARY LEAST SQUQARES): THIS IS OUR LOSS FUNCTION...
# ...WITH GRADIENT DESCENT WE MINIMIZE THE ERROR OVER DIFFERENT ITERATIONS...
########################################################################################################################

from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import random
import os

#dataset = pd.read_csv(os.getcwd() + '/Codes/linear_regression_dataset.csv', sep = ',')
#dataset = dataset[['Head_Size', 'Brain_Weight']]

def initialize_params(dimensions):

    beta_0     = 0
    beta_other = [random.random() for _ in range(dimensions)]

    return beta_0, beta_other

def compute_gradient(x, y, beta_0, beta_other, dimensions, m):

    gradient_beta_0     = 0
    gradient_beta_other = [0] * dimensions

    for i in range(m):
        y_i_hat   = sum( [x[i][j] * beta_other[j] for j in range(dimensions)] ) + beta_0
        derror_dy = 2 * (y[i] - y_i_hat)

        for j in range(dimensions):
            gradient_beta_other[j] += (derror_dy * x[i][j]) / m
        gradient_beta_0 += derror_dy / m

    return gradient_beta_0, gradient_beta_other

def update_params(beta_0, beta_other, gradient_beta_0, gradient_beta_other, learning_rate):

    beta_0 += gradient_beta_0 * learning_rate
    for i in range(len(beta_other)):
        beta_other[i] += gradient_beta_other[i] * learning_rate

    return beta_0, beta_other

def linear_regression(x, y, epochs=750, learning_rate=0.00000000045):

    n, m = len(x[0]), len(x)
    beta_0, beta_other = initialize_params(n)

    for _ in range(epochs):
        gradient_beta_0, gradient_beta_other = compute_gradient(x, y, beta_0, beta_other, n, m)
        beta_0, beta_other = update_params(beta_0, beta_other, gradient_beta_0, gradient_beta_other, learning_rate)

    return beta_0, beta_other

#coeff = linear_regression(x = dataset[['Head_Size']].values, y = dataset[['Brain_Weight']].values )
#print(coeff)

#reg = LinearRegression().fit( dataset[['Head_Size']].values, dataset[['Brain_Weight']].values )
#print(reg.coef_, reg.intercept_)

########################################################################################################################
# PLOTTING...

#plt.scatter(dataset.Head_Size, dataset.Brain_Weight, edgecolors='black', label='Data Points')
#plt.xlabel('Head Size')
#plt.ylabel('Brain Weight')

#all_x = np.linspace(dataset[['Head_Size']].min(), dataset[['Head_Size']].max(), 2000)
#plt.plot(all_x, coeff[0] + coeff[1] * all_x, color='red', lw=2, label='Gradient Regression')
#plt.plot(all_x, reg.intercept_ + reg.coef_ * all_x, color='green', lw=2, label='Sklearn Regression')

#plt.legend(loc='best')
#plt.show()


import numpy as np

def L2(v, *args):
    """Returns the L2 norm (weighted if weights specified) of a vector v.

    INPUTS
    =======
    v: list
       Components of the vector v = [v1, v2, ..., vn]
    *args: list, optional
       Weights for each component of v

    RETURNS
    ========
    the L2 norm: float
       Weighted L2 norm of the vector using the weight values specified by *args
       A value exception is raised when the vector and the weights have different length

    NOTES
    =====
    PRE:
         - v is a list of numeric type values
         - w is a list of numeric type values
    POST:
         - v and *args are not changed by this function
         - raises a ValueError exception if the vector and the weights have different length
         - returns weighted L2 norm of the vector

    EXAMPLES
    =========
    >>> L2([4, 3], [1, 1])
    5.0
    >>> L2([40, 30], [1, 1])
    50.0
    """
    s = 0.0 # Initialize sum
    if len(args) == 0: # No weight vector
        for vi in v:
            s += vi * vi
    else: # Weight vector present
        w = args[0] # Get the weight vector
        if (len(w) != len(v)): # Check lengths of lists
            raise ValueError("Length of list of weights must match length of target list.")
        for i, vi in enumerate(v):
            s += w[i] * w[i] * vi * vi
    return np.sqrt(s)


'''
For-loop method to search for the parameters
a and b of a straight line equation.
'''
#data points and constants:
x = [3,4,5,6,7,8]
y = [0,7,17,26,35,45]
n = len(x)
#initial value of summation variables:
sumx = sumxy = sumx2 = sumy = 0
for i in range(n):
    sumx += x[i]
    sumy += y[i]
    sumx2 += x[i]**2
    sumxy += x[i]*y[i]
xm = sumx/n
ym = sumy/n
#Calculates a and b:
a = (ym*sumx2 - xm*sumxy)/(sumx2 - n*xm**2)
b = (sumxy-xm*sumy)/(sumx2 - n*xm**2)
#Results:
print('The straight line equation:')
print('y = (%.3f) + (%.3f)x' % (a,b))
'''
Numpy library based method to search for parameters
a and b of a straight line equation.
'''
a = b = 0
#import numpy as np
from numpy import array, sum, mean
#arrays and constants:
x = array([3,4,5,6,7,8], float)
y = array([0,7,17,26,35,45], float)
n = len(x)
#Calculates a and b parameters:
a = (mean(y)*sum(x**2) - mean(x)*sum(x*y))/(sum(x**2)-(n*mean(x)**2))
b = (sum(x*y) - (mean(x)*sum(y)))/(sum(x**2) - (n*mean(x)**2))
#Results:
print('The straight line equation:')
print('y = (%.3f) + (%.3f)x' % (a,b))


#!/usr/bin/env python3
import scipy.integrate as spi
import numpy as np
import pylab as pl
import time

### --- creates an instance of the city and the behaviour of the disease --- ###
class city_sir_model(object):
    def __init__(self, name, population, density, latitude, longitude, end_time):
        #General info on city
        self.city_name = name
        self.city_pop = population
        self.city_dens = density
        self.city_lat = latitude
        self.city_long = longitude

        #Info on ODE's
        self.num_iterations = 0
        self.beta = .8
        self.gamma = 0.01
        self.time_step = 0.1

        #Initial and final time for simulation
        self.global_time_infected = 0
        self.end_time = end_time

        #Initial Conditions
        self.susceptible_init = 1-1e-6
        self.infected_init= 1e-6
        self.recovered_init = 0
        self.initial_conditions = (self.susceptible_init, self.infected_init, self.global_time_infected)

        self.result = []
        self.infected = False

    def infect(self, time):
        self.global_time_infected = time
        self.num_iterations = int(10*(self.end_time - self.global_time_infected))
        self.infected = True
        self.run_eqs()

    def run_eqs(self):
        t_start = self.global_time_infected;
        t_end = self.end_time
        t_inc = self.time_step
        t_range = np.arange(t_start, t_end, t_inc)
        self.result = spi.odeint(self.diff_eqs, self.initial_conditions , t_range)
        # for result in self.result:
        #     print(result)

    def diff_eqs(self,INP,t):
    	equation_list=np.zeros((3))
    	initial_conditions = INP
    	equation_list[0] = - self.beta * initial_conditions[0] * initial_conditions[1]
    	equation_list[1] = self.beta * initial_conditions[0] * initial_conditions[1] - self.gamma * initial_conditions[1]
    	equation_list[2] = self.gamma * initial_conditions[1]
    	return equation_list   # For odeint

    def plot(self):
        pl.subplot(211)
        pl.plot(self.result[:,0], '-g', label='Susceptibles')
        pl.plot(self.result[:,2], '-k', label='Recovereds')
        pl.legend(loc=0)
        pl.title('Program_2_1.py')
        pl.xlabel('Time')
        pl.ylabel('Susceptibles atime_end Recovereds')
        pl.subplot(212)
        pl.plot(self.result[:,1], '-r', label='Infectious')
        pl.xlabel('Time')
        pl.ylabel('Infectious')
        pl.show()

def main():
    sample_model = city_sir_model("ny",1000000,10000,1,1,200)
    print(sample_model.start_time)

    sample_model.run_eqs()
    print(sample_model.get_city_data())
    # print(sample_model.result)

    print(sample_model.result)
        # time.sleep(1)?
    # sample_model.plot()

    # t_start = 0.0; t_end = 70; t_inc = sample_model.time_step
    # t_range = np.arange(t_start, t_end+t_inc, t_inc)
    # print(t_range)

if(__name__ == '__main__'):
    main()


## 1. Introduction ##

mean_new = houses_per_year['Mean Price'].mean()

print(mean_new)

mean_original = houses['SalePrice'].mean()
print(mean_original)

difference =  mean_original - mean_new
print(difference)

## 2. Different Weights ##

houses_per_year['sum_per_year'] = houses_per_year['Mean Price'] * houses_per_year['Houses Sold']
all_sums_together = houses_per_year['sum_per_year'].sum()
total_n_houses = houses_per_year['Houses Sold'].sum()
weighted_mean = all_sums_together / total_n_houses

mean_original = houses['SalePrice'].mean()

difference = round(mean_original, 10) - round(weighted_mean, 10)

print(difference)

## 3. The Weighted Mean ##

def weighted_mean(distribution, weights):
    weighted_sum = []
    for mean, weight in zip(distribution, weights):
        weighted_sum.append(mean * weight)

    return sum(weighted_sum) /  sum(weights)

weighted_mean_function = weighted_mean(houses_per_year['Mean Price'],houses_per_year['Houses Sold'])
print(weighted_mean_function)

from numpy import average

weighted_mean_numpy = average(houses_per_year['Mean Price'],weights = houses_per_year['Houses Sold'])

print(weighted_mean_numpy)

equal =  round(weighted_mean_function, 10) == round(weighted_mean_numpy, 10)

## 4. The Median for Open-ended Distributions ##

distribution1 = [23, 24, 22, '20 years or lower,', 23, 42, 35]
distribution2 = [55, 38, 123, 40, 71]
distribution3 = [45, 22, 7, '5 books or lower', 32, 65, '100 books or more']

median1 = 23
median2 = 55
median3 = 32

## 5. Distributions with Even Number of Values ##

rooms =  houses['TotRms AbvGrd'].copy()
rooms = rooms.replace({'10 or more':10})
rooms = rooms.astype(int)

rooms_sorted =  rooms.sort_values()

# Find the median
middle_indices = [int((len(rooms_sorted) / 2) - 1),
                  int((len(rooms_sorted) / 2))
                 ] # len - 1 and len because Series use 0-indexing
middle_values = rooms_sorted.iloc[middle_indices] # make sure you don't use loc[]
median = middle_values.mean()

## 6. The Median as a Resistant Statistic ##

lotarea_median =  houses['Lot Area'].median()
SalePrice_median =  houses['SalePrice'].median()
Lotarea_mean = houses['Lot Area'].mean()
SalePrice_mean = houses['SalePrice'].mean()

lotarea_difference = Lotarea_mean - lotarea_median
saleprice_difference = SalePrice_mean - SalePrice_median

print(lotarea_difference)
print(saleprice_difference)

## 7. The Median for Ordinal Scales ##

mean =  houses['Overall Cond'].mean()

median =  houses['Overall Cond'].median()

houses['Overall Cond'].plot.hist()
more_representative = 'mean'

'''
The mean seems more representative and more informative because it captures the
fact that there are more houses rated above 5 than rated under 5. Because of this,
the mean is slightly shifted above 5.
'''

5. Probability and Statistics/statistics-intermediate/The Weighted Mean and the Median-306.py


import numpy as np

def RSE(pred, true):
    return np.sqrt(np.sum((true-pred)**2)) / np.sqrt(np.sum((true-true.mean())**2))

def CORR(pred, true):
    u = ((true-true.mean(0))*(pred-pred.mean(0))).sum(0)
    d = np.sqrt(((true-true.mean(0))**2*(pred-pred.mean(0))**2).sum(0))
    return (u/d).mean(-1)

def MAE(pred, true):
    return np.mean(np.abs(pred-true))

def MSE(pred, true):
    return np.mean((pred-true)**2)

def RMSE(pred, true):
    return np.sqrt(MSE(pred, true))

def MAPE(pred, true):
    return np.mean(np.abs((pred - true) / true))

def MSPE(pred, true):
    return np.mean(np.square((pred - true) / true))

def metric(pred, true):
    mae = MAE(pred, true)
    mse = MSE(pred, true)
    rmse = RMSE(pred, true)
    mape = MAPE(pred, true)
    mspe = MSPE(pred, true)

    return mae,mse,rmse,mape,mspe


import numpy as np
from numpy.linalg import inv

def covariance(X, Y, show_steps=False):
    X = np.array(X)
    Y = np.array(Y)
    show_steps

    if len(X) != len(Y):
        raise ValueError("Both lists must have same number of values")

    N = len(X)

    x_mean = np.average(X)
    y_mean = np.average(Y)

    s_xy = np.sum((X - x_mean) * (Y - y_mean)) / (N - 1)

    s_x = np.sqrt((np.sum(np.square(X)) - N * (x_mean ** 2)) / (N - 1))
    s_y = np.sqrt((np.sum(np.square(Y)) - N * (y_mean ** 2)) / (N - 1))

    r_xy = s_xy / (s_x * s_y)

    print("Covariance: ", "{:.4f}".format(s_xy))
    print("Correlation coefficient: ", "{:.4f}".format(r_xy))

def covariance_matrix(data, show_steps=False):

    data = np.transpose(np.array(data))
    f = data.shape[1]
    N = data.shape[0]
    S = (data.T @ (np.identity(N) - np.ones(N) / N) @ data) / (N - 1)

    print("Mean vector: ")
    print(np.average(data, axis=0))
    print("Covariance Matrix: ")
    print(S)

    Ds = np.identity(f)
    Ds[np.diag_indices(f)] = np.sqrt(np.diagonal(S))

    print("Ds: ")
    print(Ds)

    R = inv(Ds) @ S @ inv(Ds)

    print("Correlation Matrix: ")
    print(R)


# -*- coding: utf-8 -*-
"""
Created on Wed Apr  8 15:05:29 2020

@author: Wallace

Examples based on https://realpython.com/linear-regression-in-python/
"""
#
# Step 1: Import packages and classes
#
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
import matplotlib.pyplot as plt

#
# Step 2a: Provide data
#

# Simple - Univariate polynomia linear regression y = b0 + b1*x b2*x^2
# Vector of independent variable
x = np.array([5, 15, 25, 35, 45, 55]).reshape((-1, 1))
y = np.array([15, 11, 2, 8, 25, 32])

#
# Step 2b: Transfor data for polynomial fit of higher order terms
#
transformer = PolynomialFeatures(degree=2, include_bias=False)
# include_bias = False -> neglect intercept in the model
transformer.fit(x)
x_ = transformer.transform(x)
# fit_transform() can be used at once to fit and trasnform

#
# Step 3: Create a model and fit it
#

model = LinearRegression().fit(x_, y)

# Alternatively the model intercept should be created by using:
# x_ = PolynomialFeatures(degree=2, include_bias=True).fit_transform(x)
# model = LinearRegression(fit_intercept=False).fit(x_, y)

#
# Step 4: Get results
#

r_sq = model.score(x_, y)
print('Coefficient of determination - R^2:', r_sq)

print('Intercept - b0:', model.intercept_) # coefficient b0
print('Slope - b11 and b12:', model.coef_) # coefficient b1

#
# Step 5: Predict response
#

y_pred = model.predict(x_)  #
print('Predicted response:', y_pred, sep='\n')

#
# Step 6: Plot data
#

b0 = model.intercept_
b11 = model.coef_[0]
b12 = model.coef_[1]

line = "Regression: y ={:6.2f} +{:6.2f}x +{:6.2f}x^2 \n\nR^2 ={:6.2f}".format(b0,b11,b12,r_sq)
print(line)
fig, ax = plt.subplots()
ax.plot(x, y, linewidth=0, marker='s', label='Data points')
ax.plot(x, b0 + b11*x + b12*x**2, label=line)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend(facecolor='white')
plt.show()

fig2, ax2 = plt.subplots()
ax2.plot(y, y_pred, linewidth=0, marker='s', label = "Predicted")
ax2.plot(y, y, label=' Exact, y_pred = y')
ax2.set_xlabel('Actual Response, y')
ax2.set_ylabel('Predicted Response, y_pred')
ax2.legend(facecolor='white')
plt.show()


import numpy as np
from math import isnan, isinf
from scipy.interpolate import splev
from matplotlib import pyplot as plt

# Recursive De Boor algorithm problem.
def N(x, i, k, t):
    """ Computes the i'th basis function of order 'k'
    for the spline with knot vector 't' at the parameter value 'x'."""
    # This recursion involves a lot
    # of redundant calculation.
    # This is not the way this algorithm
    # should be implemented in real world
    # applications, but it is instructive.
    # Do k=0 case.
    if k <= 0:
        if t[i] <= x < t[i+1]:
            return 1.
        else:
            return 0.
    # Use recursion for other cases.
    else:
        # Compute left and right hand sides.
        left = (x - t[i]) / (t[i+k] - t[i])
        right = (t[i+k+1] - x) / (t[i+k+1] - t[i+1])
        # Account for nan and inf values.
        if isnan(left) or isinf(left):
            left = 0.
        if isnan(right) or isinf(right):
            right = 0.
        # Perform the recursive call.
        # It could be good to avoid calling
        # recursively for terms we already know
        # will be zero, but this matches
        # more closely with the formula
        # as it is usually written.
        return left * N(x, i, k-1, t) + right * N(x, i+1, k-1, t)

def circle_interp(m, k, res=401):
    """ Plots an interpolating spline of degree 'k'
    with parameters ranging from 0 to 'm'
    that approximates the unit circle.
    Uses scipy.integrate.splev."""
    # Make the knot vector.
    t = np.array([0]*(k) + range(m) + [m]*(k+1))
    # Preallocate the array 'c' of control points.
    c = np.empty((2, m + k + 1))
    c[:,-1] = 0.
    # Construct the circle.
    # Use n + k control points.
    theta = np.linspace(0, 2 * np.pi, m + k)
    np.cos(theta, out=c[0,:-1])
    np.sin(theta, out=c[1,:-1])
    # Generate the sample values to use for plotting.
    X = np.linspace(0, m, res)
    # Evaluate the B-spline at the given points.
    pts = splev(X, (t, c, k))
    # Plot the B-spline and its control points.
    plt.plot(pts[0], pts[1])
    plt.scatter(c[0], c[1])
    plt.show()

def my_circle_interp(m, k, res=401):
    """ Plots an interpolating spline of degree 'k'
    with parameters ranging from 0 to 'm'
    that approximates the unit circle.
    Uses the function 'N' defined above."""
    # Make the knot vector.
    t = np.array([0]*(k) + range(m) + [m]*(k+1))
    # Preallocate the array 'c' of control points.
    c = np.empty((2, m + k))
    # Construct the circle.
    # Use n + k control points.
    theta = np.linspace(0, 2 * np.pi, m + k)
    np.cos(theta, out=c[0])
    np.sin(theta, out=c[1])
    # Generate the sample vaues to use for plotting.
    # Offset just a little to not get to the end of the interval.
    # This makes the plots look identical instead of just similar.
    X = np.linspace(0, m - 1E-10, res)
    # Find the values of each basis function
    Ni = np.array([[N(x, i, k, t) for x in X] for i in xrange(m + k)])
    # Use the points to evaluate the spline, using the basis functions.
    pts = Ni.T.dot(c.T).T
    # Plot the B-spline and its control points.
    plt.plot(pts[0], pts[1])
    plt.scatter(c[0], c[1])
    plt.show()

if __name__=="__main__":
    circle_interp(20, 4)
    my_circle_interp(20, 4)


# This script will look for prime numbers

# Software
    # Mu IDE v 1.0.3

# Engineering
    # 3Blue1Brown, Grant Sanderson
    # Stack overflow, https://stackoverflow.com
    # ATS, May 2022

import numpy as np
import math
import time
import sys

def get_seriesRange():
    """Queries user for series parameters and finds associated prime numbers"""

    print("A prime number can only be factored by 1 and itself")
    print("Remember that a factor is defined as a number that divides another number and leaves no remainder \n")
    print("\nThis script will find the prime numbers within a range of numbers that you provide")

    n_min = int(input("\nPlease enter a number for the low end of your search range \n"))
    n_max = int(input("\nPlease enter a number for the high end of your search range \n"))

    result = []
    for x in range(max(n_min, 2), n_max):
        has_factor = False
        for p in range(2, int(np.sqrt(x)) + 1):
            if x % p == 0:
                has_factor = True
                break
        if not has_factor:
            result.append(x)
    #return result
    print("You are searching a range of numbers from " + str(n_min) + " to " + str(n_max))
    print("There are " + str(len(result)) + " prime numbers in your search range")
    print(result)

def rerunScript():
    """Queries the user for a rerun or stop script decision"""

    query = input("\nWould you like to run the script again? (if so, type yes) \n")
    #if query == 'yes':
    if query.lower() == 'yes':
        print("\nRunning the script again \n")
        get_seriesRange()
    else:
    #elif query.lower().startswith("n"):
        print("\nOk, see ya \n")
        #exit()
        sys.exit()
        #sys.quit()
        #quit()

if __name__ == "__main__":
    """Conditional statement for running the script"""

    start_time = time.time()
    get_seriesRange()
    end_time = time.time()
    duration = end_time - start_time
    print("\nRuntime for this script was {} units of time (milliseconds?).".format(duration))

    while True:
        start_time = time.time()
        rerunScript()
        end_time = time.time()
        duration = end_time - start_time
        print("\nRuntime for this script was {} units of time (milliseconds?).".format(duration))


from math import sqrt, pi

import numpy as np
from scipy.special import hermite
from scipy.integrate import dblquad

from ..utils import InvalidMatrix

def disentangled_gaussian_wavefcn():
    """ Return the function of normalized disentangled Gaussian systems.

    :return: function of two variables
    :rtype: function
    """
    return lambda x1, x2: np.exp(-0.5 * (x1 * x1 + x2 * x2)) / np.sqrt(np.pi)

def correlated_bipartite_gaussian_wavefcn(covmatrix):
    """ Return a normalized correlated bivariate Gaussian wavefunction.

    :param covmatrix: covariance matrix of size (2, 2)
    :return: a wavefunction of two variables
    :type covmatrix: numpy.array
    :rtype: function
    """
    if not covmatrix.shape == (2, 2):
        raise InvalidMatrix("Invalid matrix shape: "+str(covmatrix.shape)+"; desired shape: (2, 2)")
    if covmatrix[0, 1] != covmatrix[1, 0]:
        raise InvalidMatrix("Not a symmetric covariance matrix")

    norm = 2 * np.pi / np.sqrt(np.linalg.det(covmatrix))
    const = 1 / np.sqrt(norm)
    return lambda x1, x2: const * np.exp(-0.25* np.matmul(np.array([[x1, x2]]),
                                                          np.matmul(covmatrix,
                                                                    np.array([[x1], [x2]])
                                                                    )
                                                          )
                                         )

def tail_factorial(n, accumulator=1):
    """ Returns the factorial of an integer.

    The calculation is done by tail recursion.

    :param n: the integer of which the factorial is desired to return
    :param accumulator: default to be 1, for tail recursion.
    :return: factorial of `n`
    :type n: int
    :type accumulator: int
    :rtype: int
    """
    if n == 0:
        return accumulator
    else:
        return tail_factorial(n-1, accumulator * n)

# m = omega = hbar = 1
def harmonic_wavefcn(n):
    """ Return the normalized wavefunction of a harmonic oscillator, where $n$ denotes
    that it is an n-th excited state, or ground state for $n=0$.

    :param n: quantum number of the excited state
    :return: a normalized wavefunction
    :type n: int
    :rtype: function
    """
    const = 1/sqrt(2**n * tail_factorial(n)) * 1/sqrt(sqrt(pi))
    return lambda x: const * np.exp(-0.5*x*x) * hermite(n)(x)

# excited interaction states
def coupled_excited_harmonics(n):
    """ Return a bipartitite wavefunction, with ground state of center of mass,
    but excited state for the interaction.

    :param n: quantum harmonic state number for the interaction
    :return: wavefunction of two variables
    :type n: int
    :rtype: function
    """
    return lambda x1, x2: harmonic_wavefcn(0)(0.5*(x1+x2)) * harmonic_wavefcn(n)(x1-x2)

# tutorial on double integration: https://docs.scipy.org/doc/scipy/reference/tutorial/integrate.html#general-multiple-integration-dblquad-tplquad-nquad


# MSDS 400 Module 6 Practice 2

import matplotlib.pyplot as plt
from numpy import poly1d, linspace

'''
The first example shows how to generate and print a second degree
polynomial with coefficients, 1, -2 and 4.  The software is not limited
to second degree polynomials.  Higher order can be generated.  The critical
thing is to have all the coefficients in the right sequence.
'''
p = poly1d([5, -3, 2])
print('Second Degree Polynomial')
print(p)

# Now a fourth degree polynomial will be generated and printed.

q = poly1d([2, 1, 4, -2, 3])
print('\nFourth Degree Polynomial')
print(q)

# It is possible to combine p and q algebraically.

print('\nCombination')
g = p + p*q
print(g)

# Derivatives of different orders may be calculated.  This next section will
# show determination of the first and second derivatives of p.

print('\nFirst Derivative')
h = p.deriv(m=1)  # First derivative with m=1.
print(h)
print(h.roots)

print('\nSecond Derivative')
t = p.deriv(m=2)  # Second derivative with m=2.
print(t)
print(t.roots)

'''
Using t, the original function p can be restored if the missing
coefficients -2 and 4 are supplied.  Different coefficients would result
in a different function instead of the original p.
'''
print('\nIntegrated Derivative')
w = t.integ(m=2, k=[-3, 2])
print(w)
print(w.coeffs)

print('\nrandom stuff below')
new = t.integ(m=2, k=[-2, 4])
print(new)
print(new.deriv(m=1))

# Roots may also be found.  This is useful when locating the maxima, minima
# and inflection points of a function from the first and second derivatives.

print('\nRoots of polynomial')
print(w.roots)

'''
Plotting requires defining a domain for the polynomial.  The linspace
function is used to set boundaries and define the number of points
used for calculation. A new polynomial p will be defined.
'''
p = poly1d([.3333, 0, -1, 5])

# As a final example, we will find the first and second derivatives of the
# polynomial p, find the roots of the derivatives and plot the functions.

g = p.deriv(m=1)

print('\nRoots of First Derivative')
print(g.roots)

print('\nRoots of Second Derivative')
q = p.deriv(m=2)
print(q.roots)

x = linspace(-4, 4, 101)
y = p(x)
yg = g(x)  # These statements define points for plotting.
yq = q(x)
y0 = 0*x   # This statement defines the y axis for plotting.

# What is shown below is a different way to legends using a label.  Python
# will pick the colors to assign to the labels and the plotted points.

plt.plot(x, y, label='y=p(x)')
plt.plot(x, yg, label='First Derivative')
plt.plot(x, yq, label='Second Derivative')
plt.legend(loc='best')

plt.plot(x, y0)
plt.xlabel('x-axis')
plt.ylabel('y-axis')
plt.title('Plot Showing Function, First and Second Derivatives')
plt.show()

# Exercise: Refer to Lial Section 14.1 Example 2.  Duplicate the results
# showing plots of the function and derivatives.  Compare to the answer sheet.

plt.figure()
p = poly1d([3, -4, -12, 0, 2])
print('\nFourth Degree Polynomial')
print(p)
print('\nFirst Derivative')
g = p.deriv(m=1)  # First derivative with m=1.
print(g)
print('\nSecond Derivative')
q = p.deriv(m=2)  # Second derivative with m=2.
print(q)
x = linspace(-2, 3, 101)
y = p(x)
yg = g(x)  # These statements define points for plotting.
yq = q(x)
y0 = 0*x  # This statement defines the y axis for plotting.
plt.plot(x, y, label='y=p(x)')
plt.plot(x, yg, label='First Derivative')
plt.plot(x, yq, label='Second Derivative')
plt.legend(loc='best')

plt.plot(x, y0)
plt.xlabel('x-axis')
plt.ylabel('y-axis')
plt.title('Plot Showing Function, First and Second Derivatives')
plt.show()


import numpy as np
import pandas as pd
from typing import Union
def unit_vector(azi:Union[int,float]) -> np.array:
    """
    Get the unit vector2D of a given azimuth
    Input:
        azi -> (int,float) Azimuth in Degrees
    Return:
        u -> (np.ndarray) numpy array with a shape of (2,1) with the x and y components of unit vector
    """
    assert isinstance(azi,(int,float,np.ndarray))
    alpha = 90 - azi
    alpha_rad = np.deg2rad(alpha)
    x = np.cos(alpha_rad)
    y = np.sin(alpha_rad)
    p = np.array([[x,y]])
    return p

def projection_1d(x, azi, center=None):
    """
    Get the 1D projection of a series of 2D Coordinates within a given azimuth direction

    Input:
        x -> (np.ndarray) Numpy array of shape (m,2) being m the number of coordinates
        azi -> (int,float) Azimuth in Degrees
        center -(list,np.ndarray)  list or numpy array with the center
    Return:
        u -> (np.ndarray) numpy array with a shape of (m,1)
    """
    assert isinstance(x,np.ndarray) and x.shape[1] == 2
    assert isinstance(azi,(int,float,np.ndarray))
    assert isinstance(center,(list,np.ndarray, type(None)))

    if isinstance(center,type(None)):
        center = x.mean(axis=0)
    else:
        center = np.atleast_1d(center)
        assert center.shape == (2,)
    #Normalize the coordinates by substracting the average coordinates

    x = x - center

    # Get the unit vector
    u = unit_vector(azi)

    # Projection over the azimuth direction
    cv = np.squeeze(np.dot(x,u.T))

    return cv, center


import math
import numpy as np
from core import StatisticsCalculator

class CorrelationCoefficient:
    @staticmethod
    def pearson(x, y):
        n = x.__len__()
        if n != y.__len__():
            raise IndexError('The length of vector x and y are not equal.')
        mean_x = StatisticsCalculator.mean(x)
        mean_y = StatisticsCalculator.mean(y)
        numerator_sum = 0
        denominator_as_x_sum_temp = 0
        denominator_as_y_sum_temp = 0
        for i in range(0, n):
            numerator_sum += ((x[i] - mean_x) * (y[i] - mean_y))
            denominator_as_x_sum_temp += (x[i] - mean_x) ** 2
            denominator_as_y_sum_temp += (y[i] - mean_y) ** 2
        return numerator_sum / math.sqrt(denominator_as_x_sum_temp * denominator_as_y_sum_temp)

    @staticmethod
    def spearman_rank(x, y):
        n = x.__len__()
        if n != y.__len__():
            raise IndexError('The length of vector x and y are not equal.')
        op_x = np.array(x)
        op_y = np.array(y)
        sorted_x = np.sort(op_x)
        sorted_y = np.sort(op_y)
        r = np.zeros((n,), dtype=int)
        s = np.zeros((n,), dtype=int)
        for i in range(0, n):
            for j in range(0, n):
                if sorted_x[j] == op_x[i]:
                    r[i] = j
        for i in range(0, n):
            for j in range(0, n):
                if sorted_y[j] == op_y[i]:
                    s[i] = j
        d = np.zeros((n,), dtype=int)
        for i in range(0, n):
            d[i] = r[i] - s[i]
        for i in range(0, n):
            d[i] = d[i] ** 2
        return 1 - ((6 * d.sum()) / (n * ((n ** 2) - 1)))

    @staticmethod
    def partial(x, y, z, kind='pearson'):
        if kind == 'pearson':
            rXY = CorrelationCoefficient.pearson(x, y)
            rXZ = CorrelationCoefficient.pearson(x, z)
            rYZ = CorrelationCoefficient.pearson(y, z)
            rZY = CorrelationCoefficient.pearson(z, y)
            rYX = CorrelationCoefficient.pearson(y, x)
            rZX = CorrelationCoefficient.pearson(z, x)
            return {
                'XY.Z': ((rXY - (rXZ * rYZ)) / (math.sqrt((1 - rXZ ** 2) * (1 - rYZ ** 2)))),
                'XZ.Y': ((rXZ - (rXY * rZY)) / (math.sqrt((1 - rXY ** 2) * (1 - rZY ** 2)))),
                'YZ.X': ((rYZ - (rYX * rZX)) / (math.sqrt((1 - rYX ** 2) * (1 - rZX ** 2))))
            }
        if kind == 'spearman':
            rXY = CorrelationCoefficient.spearman_rank(x, y)
            rXZ = CorrelationCoefficient.spearman_rank(x, z)
            rYZ = CorrelationCoefficient.spearman_rank(y, z)
            rZY = CorrelationCoefficient.spearman_rank(z, y)
            rYX = CorrelationCoefficient.spearman_rank(y, x)
            rZX = CorrelationCoefficient.spearman_rank(z, x)
            return {
                'XY.Z': ((rXY - (rXZ * rYZ)) / (math.sqrt((1 - rXZ ** 2) * (1 - rYZ ** 2)))),
                'XZ.Y': ((rXZ - (rXY * rZY)) / (math.sqrt((1 - rXY ** 2) * (1 - rZY ** 2)))),
                'YZ.X': ((rYZ - (rYX * rZX)) / (math.sqrt((1 - rYX ** 2) * (1 - rZX ** 2))))
            }
        raise NameError("Undefined kind: " + kind)


#!/usr/bin/env python
# coding: utf-8
## Monte Carlo - Black-Scholes-Merton
# *Suggested Answers follow (usually there are multiple ways to solve a problem in Python).*
# Download the data for Microsoft (‘MSFT’) from Yahoo Finance for the period ‘2000-1-1’ until today.
# Download the data for Microsoft (‘MSFT’) from IEX for the period ‘2015-1-1’ until today.
# We have written a few lines of code that will import the documents you need and define the functions estimating d1, d2, and the Black-Scholes-Merton formula.
import numpy as np
import pandas as pd
from pandas_datareader import data as wb
from scipy.stats import norm
ticker = 'MSFT'
data = pd.DataFrame()
data[ticker] = wb.DataReader(ticker, data_source='yahoo', start='2000-1-1')['Adj Close']
# In[2]:
def d1(S, K, r, stdev, T):
    return (np.log(S / K) + (r + stdev ** 2 / 2) * T) / (stdev * np.sqrt(T))

def d2(S0, K, r, sigma, T):
    return (np.log(S / K) + (r - stdev ** 2 / 2) * T) / (stdev * np.sqrt(T))

def BSM(S, K, r, stdev, T):
        return (S * norm.cdf(d1(S, K, r, stdev, T))) - (K * np.exp(-r * T) * norm.cdf(d2(S, K, r, stdev, T)))
# Store the annual standard deviation of the log returns in a variable, called “stdev”.
# In[3]:
log_returns = np.log(1 + data.pct_change())
log_returns.tail()
# In[5]:
stdev = log_returns.std() * 250 ** 0.5
stdev
# Set the risk free rate, r, equal to 2.5% (0.025); the strike price, K, equal to 110.0; and the time horizon, T, equal to 1, respectively.
r = 0.025
K = 110.0
T = 1
# Create a variable S equal to the last adjusted closing price of Microsoft. Use the “iloc” method.
# In[7]:
S = data.iloc[-1]
S
# Call the d1 and d2 functions with the relevant arguments to obtain their values.
d1(S, K, r, stdev, T)
d2(S, K, r, stdev, T)
# Use the BSM function to estimate the price of a call option, given you know the values of S, K, r, stdev, and T.
BSM(S, K, r, stdev, T)


#! /usr/bin/env python3
# ************************************************************************** #
#                                                                            #
#                                                       :::      ::::::::    #
#    cost_function.py                                 :+:      :+:    :+:    #
#                                                   +:+ +:+         +:+      #
#    By: darodrig                                 +#+  +:+       +#+         #
#                                               +#+#+#+#+#+   +#+            #
#    Created: 2020/04/22 16:07:59 by darodrig        #+#    #+#              #
#    Updated: 2020/04/22 16:07:59 by darodrig       ###   ########.fr        #
#                                                                            #
# ************************************************************************** #

import numpy as np

def cost_elem_(theta, X, Y):
    if type(X) != np.ndarray or type(Y) != np.ndarray or \
            type(theta) != np.ndarray:
        return None
    if X.shape[1] + 1 != theta.shape[0] or X.shape[0] != Y.shape[0]:
        return None
    if X.size == 0 or Y.size == 0 or theta.size == 0:
        return None
    ones = np.ones((X.shape[0], 1))
    X = np.concatenate((ones, X.reshape(X.shape)), axis=1)
    ret = X[:, 0].reshape(ones.shape)
    for i in range(Y.size):
        ret[i] = ((X[i].dot(theta) - Y[i]) ** 2) * (1/(2 * Y.size))
    return ret

def cost_(theta, X, Y):
    return np.sum(cost_elem_(theta, X, Y))

if __name__ == "__main__":
    X1 = np.array([[0.], [1.], [2.], [3.], [4.]])
    Y1 = np.array([[2.], [7.], [12.], [17.], [22.]])
    theta1 = np.array([[2.], [4.]])

    print(cost_elem_(theta1, X1, Y1))
    print(cost_(theta1, X1, Y1))
    print()
    X2 = np.array([[0.2, 2., 20.], [0.4, 4., 40.], [0.6, 6., 60.], [0.8, 8., 80.]])
    theta2 = np.array([[0.05], [1.], [1.], [1.]])
    Y2 = np.array([[19.], [42.], [67.], [93.]])
    print(cost_elem_(theta2, X2, Y2))
    print(cost_(theta2, X2, Y2))

# Bonus: Cost functions
# https://stats.stackexchange.com/questions/154879/a-list-of-cost-functions-used-in-neural-networks-alongside-applications


#%%
# Math Modules
import numpy as np
import math
import pandas as pd
import scipy as sp
from scipy.misc import derivative
from scipy import integrate
from scipy.sparse.linalg import eigsh

# Plot Libraries
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from matplotlib import cm

# Utilities
import datetime

pi = np.pi
# %%
def f(x, y):
    return (x ** 2 * y) + (x * y ** 2)

def dbsimpson(f, limits: list, d: list):
    """Simpson's 1/3 rule for double integration

    int_{ay}^{by} int_{ax}^{bx} f(x,y) dxdy

    Args:
        f (func): two variable function, must return float or ndarray
        limits (list): limits of integration [ax, bx, ay, by]
        d (lsit): list of integral resolution [dx, dy]

    Returns:
        float: double integral of f(x,y) between the limits
    """
    ax, bx, ay, by = limits
    dx, dy = d
    nx = math.floor((bx - ax) / dx)
    ny = math.floor((by - ay) / dy)
    s = 0
    for i in range(ny + 1):  # loop of outer integral
        if i == 0 | i == ny:
            p = 1
        elif i % 2 != 0:
            p = 4
        else:
            p = 2

        for j in range(nx + 1):  # loop of inner integral
            if j == 0 | j == nx:
                q = 1
            elif j % 2 != 0:
                q = 4
            else:
                q = 2
            x = ax + j * dx
            y = ay + i * dy
            s += p * q * f(x, y)

    return dx * dy / 9 * s

dbsimpson(f, [1, 2, -1, 1], [0.01, 0.01])

# %%
def f(x, y):
    return (x ** 2 * y) + (x * y ** 2)

def dbsimpson(g: np.ndarray, dxdy: tuple = (1, 1), grid: tuple = None):
    """Simpson's 1/3 rule for double integration

    int_{ay}^{by} int_{ax}^{bx} f(x,y) dxdy
    """
    nx = g.shape[0] - 1
    ny = g.shape[1] - 1

    if grid:
        (x, y) = grid
        ax, bx = np.min(x[1]), np.max(x[1])
        ay, by = np.min(y[:, 0]), np.max(y[:, 0])
        dx = (bx - ax) / nx
        dy = (by - ay) / ny
    else:
        dx, dy = dxdy

    s = 0
    for i in range(ny + 1):  # loop of outer integral
        if i == 0 | i == ny:
            p = 1
        elif i % 2 != 0:
            p = 4
        else:
            p = 2

        for j in range(nx + 1):  # loop of inner integral
            if j == 0 | j == nx:
                q = 1
            elif j % 2 != 0:
                q = 4
            else:
                q = 2
            s += p * q * g[j, i]

    return dx * dy / 9 * s

ax, bx, ay, by = [1, 2, -1, 1]
dx, dy = [0.01, 0.01]
nx = int((bx - ax) / dx)
ny = int((by - ay) / dy)
x = np.arange(ax, bx + dx, dx)
y = np.arange(ay, by + dy, dy)
xv, yv = np.meshgrid(x, y)
g = f(xv, yv)
aa = dbsimpson(g, grid=(xv, yv))
bb = dbsimpson(g, dxdy=(dx, dy))
print(aa, bb)


#!/usr/bin/env python

import tensorflow as tf
import numpy as np

def create_data():

    trX = np.linspace(-1, 1, 101)
    trY = 2 * trX + np.random.randn(*trX.shape) * 0.33 # create a y value which is approximately linear but with some random noise
    return [trX, trY]

def inputs_placeholder():
    X = tf.placeholder("float") # create symbolic variables
    Y = tf.placeholder("float")
    return [X, Y]

def model(X, Y):
    '''
    symbolic graph and ops are defined here

    '''
    w = tf.Variable(0.0, name="weights") # create a shared variable (like theano.shared) for the weight matrix
    y_model = tf.multiply(X, w) # lr is just X*w
    cost = tf.square(Y - y_model) # use square error for cost function
    train_op = tf.train.GradientDescentOptimizer(0.01).minimize(cost) # construct an optimizer to minimize cost and fit line to my data
    return [train_op, w]

def train(sess, trX, trY, train_op, w):
    ''' train the model '''
    for i in range(100):
        for (x, y) in zip(trX, trY):
            sess.run(train_op, feed_dict={X: x, Y: y})

    print(sess.run(w))  # It should be something around 2

if __name__ == '__main__':
    ''' '''
    # 1 create data
    [trX, trY] = create_data()
    # 2. define the inputs
    [X, Y] = inputs_placeholder()
    # 3. define the model
    [train_op, w] = model(X, Y)
    # Launch the graph in a session
    with tf.Session() as sess:
        # you need to initialize variables (in this case just variable W)
        tf.global_variables_initializer().run()
        # 4. training
        train(sess, trX, trY, train_op, w)


from statistics import mean
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')

xs = [1,2,3,4,5,6]
ys = [5,4,6,5,6,7]

# plt.plot(xs,ys)
# plt.show()

# Since above are not np array they are just a python array, to change them which will give us
# more powerful way to iterate over then values in the array,

xs = np.array(xs, dtype = np.float64)  # you can also specify the data-type into your array
ys = np.array(ys, dtype = np.float64)

# first we need a function to calculate the slop as

def best_fit_slop_and_intercept(xs,ys):

    m = ( ((mean(xs)*mean(ys)) - mean(xs*ys)) /
          (mean(xs)**2-mean(xs**2)))
    b = mean(ys)-m*mean(xs)

# remember the PEMDAS the order of the operations in computing
    return m,b

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

def coefficient_of_determination(ys_orig,ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]    # to make a vector with only one value which is mean(ys_orig)
    squared_error_regr = squared_error(ys_orig,ys_line)
    squared_error_y_mean = squared_error(ys_orig, y_mean_line)
    return 1- (squared_error_regr / squared_error_y_mean)

m,b = best_fit_slop_and_intercept(xs,ys)
print(m,b)

regression_line = [(m*x)+b for x in xs]
print(regression_line)

# Here we will add the piece of code to calculate the coefficient of determination,
r_squared = coefficient_of_determination(ys, regression_line)
print(r_squared)

# this is exact identical to the following format
# for x in xs:
#     regression_line.append((m*x)+b)

# now we will plot the data and the regression line

# what if you want to predict a specific value
predict_x = 8
predict_y = (m*predict_x)+b

plt.scatter(xs,ys)
plt.scatter(predict_x,predict_y)
plt.plot(xs,regression_line)
plt.show()

Machine_Learning_Old_Files/[11] linear regression P.4.py