import numpy as np

from ..in_place import (
    elastic_net_,
    general_plastic_net_,
    hard_plastic_net_,
    lasso_,
    ordinary_least_squares_,
    plastic_lasso_,
    plastic_ridge_,
    ridge_,
    soft_plastic_net_,
    unified_plastic_net_,
)

def ordinary_least_squares(X, y, tol=1e-8, max_iter=1000):
    r"""
    Ordinary least squares regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N}||\vec{y}-X\vec{\beta}||_2^2

    Args:
        X (numpy.ndarray): shape (N,D) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    ordinary_least_squares_(beta, r, X, tol=tol, max_iter=max_iter)

    return beta

def ridge(X, y, lambda_total=1.0, tol=1e-8, max_iter=1000):
    r"""
    Ridge regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda \tfrac{1}{2} ||\vec{\beta}||_2^2

    Args:
        X (numpy.ndarray): shape (N,D) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    ridge_(beta, r, X, lambda_total=lambda_total, tol=tol, max_iter=max_iter)

    return beta

def lasso(X, y, lambda_total=1.0, tol=1e-8, max_iter=1000):
    r"""
    Lasso regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda ||\vec{\beta}||_1

    Args:
        X (numpy.ndarray): shape (N,D) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    lasso_(beta, r, X, lambda_total=lambda_total, tol=tol, max_iter=max_iter)

    return beta

def elastic_net(X, y, lambda_total=1.0, alpha=0.75, tol=1e-8, max_iter=1000):
    r"""
    Elastic net regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda \bigl( \alpha||\vec{\beta}||_1 + (1-\alpha) \tfrac{1}{2} ||\vec{\beta}||_2^2 \bigr)

    Args:
        X (numpy.ndarray): shape (N,D) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        alpha (float): mixing parameter between L1 and L1 penalties. must be between zero and one. :math:`\alpha=0` is pure L2 penalty, :math:`\alpha=1` is pure L1 penalty.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    elastic_net_(
        beta, r, X, lambda_total=lambda_total, alpha=alpha, tol=tol, max_iter=max_iter
    )

    return beta

def general_plastic_net(
    X, y, xi, zeta, lambda_total=1.0, alpha=0.75, tol=1e-8, max_iter=1000
):
    r"""
    General plastic net regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda \bigl( \alpha||\vec{\beta}-\vec{\xi}||_1 + (1-\alpha) \tfrac{1}{2} ||\vec{\beta}-\vec{\zeta}||_2^2 \bigr)

    Args:
        X (numpy.ndarray): shape (N,P) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        xi (numpy.ndarray): shape (P,) target for L1 penalty.
        zeta (numpy.ndarray): shape (P,) target for L2 penalty.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        alpha (float): mixing parameter between L1 and L1 penalties. must be between zero and one. :math:`\alpha=0` is pure L2 penalty, :math:`\alpha=1` is pure L1 penalty.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    general_plastic_net_(
        beta,
        r,
        X,
        xi,
        zeta,
        lambda_total=lambda_total,
        alpha=alpha,
        tol=tol,
        max_iter=max_iter,
    )

    return beta

def plastic_ridge(X, y, zeta, lambda_total=1.0, tol=1e-8, max_iter=1000):
    r"""
    Plastic ridge regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda \tfrac{1}{2} ||\vec{\beta}-\vec{\zeta}||_2^2

    Args:
        X (numpy.ndarray): shape (N,P) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        zeta (numpy.ndarray): shape (P,) target for L2 penalty.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    plastic_ridge_(
        beta, r, X, zeta, lambda_total=lambda_total, tol=tol, max_iter=max_iter
    )

    return beta

def plastic_lasso(X, y, xi, lambda_total=1.0, tol=1e-8, max_iter=1000):
    r"""
    Plastic lasso regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda ||\vec{\beta}-\vec{\xi}||_1

    Args:
        X (numpy.ndarray): shape (N,P) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        xi (numpy.ndarray): shape (P,) target for L1 penalty.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    plastic_lasso_(
        beta, r, X, xi, lambda_total=lambda_total, tol=tol, max_iter=max_iter
    )

    return beta

def hard_plastic_net(X, y, xi, lambda_total=1.0, alpha=0.75, tol=1e-8, max_iter=1000):
    r"""
    Hard plastic net regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda \bigl( \alpha||\vec{\beta}-\vec{\xi}||_1 + (1-\alpha) \tfrac{1}{2} ||\vec{\beta}||_2^2 \bigr)

    Args:
        X (numpy.ndarray): shape (N,P) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        xi (numpy.ndarray): shape (P,) target for L1 penalty.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        alpha (float): mixing parameter between L1 and L1 penalties. must be between zero and one. :math:`\alpha=0` is pure L2 penalty, :math:`\alpha=1` is pure L1 penalty.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.

    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    hard_plastic_net_(
        beta,
        r,
        X,
        xi,
        lambda_total=lambda_total,
        alpha=alpha,
        tol=tol,
        max_iter=max_iter,
    )

    return beta

def soft_plastic_net(X, y, zeta, lambda_total=1.0, alpha=0.75, tol=1e-8, max_iter=1000):
    r"""
    Soft plastic net regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda \bigl( \alpha||\vec{\beta}||_1 + (1-\alpha) \tfrac{1}{2} ||\vec{\beta}-\vec{\zeta}||_2^2 \bigr)

    Args:
        X (numpy.ndarray): shape (N,P) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        zeta (numpy.ndarray): shape (P,) target for L2 penalty.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        alpha (float): mixing parameter between L1 and L1 penalties. must be between zero and one. :math:`\alpha=0` is pure L2 penalty, :math:`\alpha=1` is pure L1 penalty.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.
    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    soft_plastic_net_(
        beta,
        r,
        X,
        zeta,
        lambda_total=lambda_total,
        alpha=alpha,
        tol=tol,
        max_iter=max_iter,
    )

    return beta

def unified_plastic_net(
    X, y, xi, lambda_total=1.0, alpha=0.75, tol=1e-8, max_iter=1000
):
    r"""
    Unified plastic net regression.  This function finds the :math:`\vec{\beta}` that minimizes

    .. math::

        \tfrac{1}{2N} ||\vec{y}-X\vec{\beta}||_2^2 + \lambda \bigl( \alpha||\vec{\beta}-\vec{\xi}||_1 + (1-\alpha) \tfrac{1}{2} ||\vec{\beta}-\vec{\xi}||_2^2 \bigr)

    Args:
        X (numpy.ndarray): shape (N,P) data matrix.
        y (numpy.ndarray): shape (N,) target vector.
        xi (numpy.ndarray): shape (P,) target for both L1 and L2 penalties.
        lambda_total (float): must be non-negative. total regularization penalty strength.
        alpha (float): mixing parameter between L1 and L1 penalties. must be between zero and one. :math:`\alpha=0` is pure L2 penalty, :math:`\alpha=1` is pure L1 penalty.
        tol (float): convergence criterion for coordinate descent. coordinate descent runs until the maximum element-wise change in **beta** is less than **tol**.
        max_iter (int): maximum number of update passes through all P elements of **beta**, in case **tol** is never met.
    Returns:
        (numpy.ndarray): shape (D,) coefficient vector.
    """

    N, D = X.shape
    beta = np.zeros(D, dtype=np.float64)
    r = y - np.dot(X, beta)

    unified_plastic_net_(
        beta,
        r,
        X,
        xi,
        lambda_total=lambda_total,
        alpha=alpha,
        tol=tol,
        max_iter=max_iter,
    )

    return beta


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

data = pd.read_csv("D:\ML exercises\CourseraML-master\CourseraML-master\ex1\data\ex1data1.txt",header = None , names = ["population" ,"profit"])
data.head()
#---------------------------------- COST FUNCTION ------------------------------------ #
def compute_cost(X,y,theta):
    inner = np.power(((X * theta.T) - y), 2)
    return np.sum(inner) / (2 * len(X))

data.insert(0,"ones",1)
cols = data.shape[1]
X = data.iloc[:,0:cols-1]
y = data.iloc[:,cols-1:cols]
theta = np.matrix(np.array([0,0]))
X = np.matrix(X.values)
y = np.matrix(y.values)

## ------------------------------- Gradient descent ---------------------------------#
def grad_des(X,y,theta,alpha,iters):
    temp = np.matrix(np.zeros(2))
    parameters = 2
    cost = np.zeros(iters)

    for i in range(iters):
        error = (X*theta.T)-y

        for j in range(parameters):
            term = np.multiply(error,X[:,j])
            temp[0,j] = theta[0,j] - ((alpha / len(X)) * np.sum(term))

        theta = temp
        #cost[i] = compute_cost(X,y,theta)

    return theta,cost

alpha = 0.01
iters = 2000

g,cost = grad_des(X,y,theta,alpha,iters)
print(g)

x = np.linspace(data.population.min(), data.population.max(), 2000)
f = g[0, 0] + (g[0, 1] * x)
print(f)
fig, ax = plt.subplots(figsize=(12,12))
ax.plot(x, f, 'r', label='Prediction')
ax.scatter(data.population, data.profit, label='Traning Data')
ax.legend(loc=2)
ax.set_xlabel('population')
ax.set_ylabel('profit')
ax.set_title('predicted profit vs. population Size')

plt.show()

#---------------------------    predict with values ------------------------#
get = int(input("please tell the value to predict"))
print(g[0,0] + g[0,1]*get,"is the approximation value")


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
np.set_printoptions(threshold=np.nan)
np.set_printoptions(precision=5)

def f(u,t):
    return u**2 - np.sin(t) - np.cos(t)**2

#<startEuler>
def fw_euler(f,u0,N,T):
    k = T/N
    u = np.zeros(N+1)
    u[0] = u0
    for n in range(N):
        u[n+1] = u[n] + k*f(u[n],k*n)
    return u
#<endEuler>

#<startRK>
def r_k(f,u0,N,T):
    k = T/N
    u = np.zeros(N+1)
    u[0] = u0
    for n in range(N):
        tn = k*n
        Y1 = u[n]
        Y2 = u[n] + k/2*f(Y1,tn)
        Y3 = u[n] + k/2*f(Y2,tn+k/2)
        Y4 = u[n] + k*f(Y3,tn+k/2)
        u[n+1] = u[n] + k/6*( f(Y1,tn) + 2*f(Y2,tn+k/2) + 2*f(Y3,tn+k/2) + f(Y4,tn+k))
    return u
#<endRK>

def make_plots():
    error_e = []
    error_rk = []
    Ns = np.array([25,50,100,200,400,800,1600])
    for N in Ns:
        T = 8
        x = np.linspace(0,T,N+1)
        u_true = np.cos(x)
        u_e = fw_euler(f,1,N,T)
        u_rk = r_k(f,1,N,T)

        error_e.append(np.linalg.norm(u_true-u_e,np.inf))
        error_rk.append(np.linalg.norm(u_true-u_rk,np.inf))

        if N==25:
            plt.figure()
            plt.plot(x,u_true,color='.5')
            plt.plot(x,u_e,color='.1',marker='o',ms=4,linestyle='None')
            plt.plot(x,u_rk,color='.1',marker='s',ms=4,linestyle='None')
            plt.savefig('img/5/N25.pdf')

    ks = 8/Ns
    coeff_e = np.polyfit(np.log10(ks), np.log10(error_e), 1)
    fit_e = np.poly1d(coeff_e)

    coeff_rk = np.polyfit(np.log10(ks), np.log10(error_rk), 1)
    fit_rk = np.poly1d(coeff_rk)

    np.savetxt('img/5/coeff_e.txt',[coeff_e[0]],fmt='%.2f',delimiter='',newline='')
    np.savetxt('img/5/coeff_rk.txt',[coeff_rk[0]],fmt='%.2f',delimiter='',newline='')

    print(coeff_e,coeff_rk)

    fig = plt.figure()
    ax = plt.gca()
    plt.plot(ks,10**(fit_e(np.log10(ks))),'.5')
    plt.plot(ks,10**(fit_rk(np.log10(ks))),'.5')
    ax.plot(ks,error_e,color='k',marker='o',ms=4,linestyle='None')
    ax.plot(ks,error_rk,color='k',marker='s',ms=4,linestyle='None')
    ax.set_yscale('log')
    ax.set_xscale('log')
    plt.savefig('img/5/error.pdf')

make_plots()


'''
Created on Jul 21, 2017

@author: arnon
'''

import numpy as np

def meanabsdev(vector, Double=None, Median=None, NaN=None):
    ''' MeanAbsDev computes the mean absolute deviation (average deviation)
    of an N-element vector.

    Args:
        vector: An N-element vector of type integer, float or double.
        Double: If set to a non-zero value, MEANABSDEV performs its
                computations in double precision arithmetic and returns
                a double precision result. If not set to a non-zero value,
                the computations and result depend upon the type of the
                input data (integer and float data return float results,
                while double data returns double results). This has no
                effect if the Median keyword is set.
        Median: If set to a non-zero value, meanabsdev will return
                the average deviation from the median, rather than
                the mean.  If Median is not set, meanabsdev will return
                the average deviation from the mean.
        NaN: If set, treat NaN data as missing.

    Returns:
        mean absolute deviation
    '''
    if Median is not None:
        if NaN is not None:
            whereNotNaN = np.where(np.isfinite(vector))
            whereNotNaN = whereNotNaN[0]
            nanCount = len(whereNotNaN)

            if nanCount > 0:
                middle = np.median(vector[whereNotNaN])
            else:
                middle = 0
                raise Exception("Trying to calculate Median but all items are NaN")
        else:
            middle = np.median(vector)
    else:
        middle = np.mean(vector,)

    return np.mean(np.abs(vector - middle))


import numpy as np
import csv

def mapFeature(X1, X2):
# MAPFEATURE Feature mapping function to polynomial features
#
#   MAPFEATURE(X1, X2) maps the two input features
#   to quadratic features used in the regularization exercise.
#
#   Returns a new feature array with more features, comprising of
#   X1, X2, X1.^2, X2.^2, X1*X2, X1*X2.^2, etc..
#
#   Inputs X1, X2 must be the same size
    degree = 6
    out = np.ones([len(X1), (degree+1)*(degree+2)/2])
    idx = 1
    for i in range(1, degree+1):
        for j in range(0, i+1):
            a1 = X1 ** (i-j)
            a2 = X2 ** j
            out[:, idx] = a1*a2
            idx += 1
    return out

if __name__ == "__main__":
    with open('ex2data2.txt', 'rb') as csvfile:
        data = np.loadtxt(csvfile, delimiter=",")
        new_data = mapFeature(data[:,0], data[:,1])
        print new_data


import numerical_differentiaition as nd
import numpy as np

def gradientDecent(x, epsilon, n) :
    '''
    경사감소법
    :param x: init weights
    :param epsilon: learning rate
    :param n: number of method running
    :return: optimal weights
    '''
    xVal=np.array(x)
    for i in range(n) :
        grad=nd.numericalGradient(sample_function, xVal)
        xVal-=epsilon*grad
    return xVal

def sample_function(x) :
    return x[0]**2 + x[1]**2

w=np.array([-3.0, 4.0])

print('n = 100, init weights = [-3, 4]')
print('learning rate : 0.1\n', gradientDecent(w, 0.1, 100))
print('learning rate : 2\n', gradientDecent(w, 2, 100))
print('learning rate : 0.001\n', gradientDecent(w, 0.001, 100))


# -*- coding: utf-8 -*-
"""
Created on Thu Nov 22 23:18:15 2018

@author: Tian
"""

import numpy as np

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

def predict(X, w):
    return sigmoid(np.dot(X,w))

__classify=np.vectorize(lambda pred: 1 if pred>=0.5 else 0)

def classify(pred):
    return __classify(pred)

def loss(X, y, w):
    pred=predict(X,w)
    class1_cost = y*np.log(pred)
    class2_cost = (1-y)*np.log(1-pred)
    cost = -class1_cost.sum() - class2_cost.sum()
    return cost/len(y)

def gradient(X, y, w):
    n = X.shape[0] if X.ndim != 1 else X.size
    predictions = predict(X, w)
    gradient = np.dot(X.T, predictions - y)
    return gradient / n

def train(X, y, w, lrate, iters=1):
    if y.ndim == 1:
        y.resize([len(y),1])
    for _ in range(iters):
        grad=gradient(X, y, w)
        w -= grad*lrate
    return w, grad

def trainBatch(X, y, w, lrate, pf, pl):
    return train(X[pf:pl,:], y[pf:pl], w, lrate, 1)

def accuracy(predicted_labels, actual_labels):
    diff = predicted_labels - actual_labels
    return 1.0 - (float(np.count_nonzero(diff)) / len(diff))

def dis_gradient(Xs,ys,w,lid,idx,cnt):
    return gradient(Xs[lid][idx:idx+cnt,:], ys[lid][idx:idx+cnt], w)


import math as mt
def f(x):
    return x**(2)*mt.cos(x**(2))
xi=[]
x0=1.5
x1=2.5
h=0.1
n=(x1-x0)/h
fa=f(x0)
fn=f(x1)

while x0<x1+h:
    xi.append(x0)
    x0=x0+h

print("x=",xi)
length=len(xi)

fxi=[]
for i in range(0,length):
    fx=f(xi[i])
    fxi.append(fx)
print("Nilai hasil fungsi =",fxi)

sigma=0
for i in range(1,length-1):
    sigma=sigma+fxi[i]
print("sigma =",sigma)
## METODE TRAPEZOID
hasil=(fa+2*sigma+fn)*h/2
print("Hasil Trapezoid =", hasil)

sigmaGanjil=0
sigmaGenap=0
#METODE SIMPSON
for i in range(1,length-1):
    if i%2!=0 :
        sigmaGanjil=sigmaGanjil+fxi[i]
    else:
        sigmaGenap=sigmaGenap+fxi[i]
print("hasil sigma ganjil = ",sigmaGanjil,", hasil sigma genap =",sigmaGenap)
simpson=(fa+4*sigmaGanjil+2*sigmaGenap+fn)*h/3
print("Nilai Hasil Simpson =",simpson)

from sympy import *
x=Symbol('x')
fd=x**(2)*cos(x**(2))
integral=fd.integrate(x)
print("Hasil Integral=",integral)

from scipy import integrate
hasilIntegral=integrate.quad(f,1.5,2.5)
print("Hasil Integral Eksak",hasilIntegral)

er_simson=abs(simpson-hasilIntegral[0])
er_trape=abs(hasil-hasilIntegral[0])
print("Hasil Eror Sympson = ", er_simson)
print("Hasil Eror Trapezoid = ", er_trape)


import numpy as np
import pandas as pd
from typing import Tuple

def two_p_diff_ci(
    series_a_r_count: int, series_b_r_count: int, total_count: int
) -> Tuple[float, ...]:
    """Takes series_a row count, series_b row count and total count of rows
    in a data set and returns difference in two proportions together with
    lower and upper bounds of 95 % confidence interval."""

    first_p = series_a_r_count / total_count
    second_p = series_b_r_count / total_count
    first_p_se = np.sqrt(first_p * (1 - first_p) / total_count)
    second_p_se = np.sqrt(second_p * (1 - second_p) / total_count)
    se_diff = np.sqrt(first_p_se ** 2 + second_p_se ** 2)
    diff_in_p = first_p - second_p
    lcb = diff_in_p - 1.96 * se_diff
    ucb = diff_in_p + 1.96 * se_diff

    return diff_in_p, lcb, ucb

def diff_in_two_means_unpooled_ci(
    series_a: pd.Series, series_b: pd.Series
) -> Tuple[float, ...]:
    """Takes series_a and series_b as an input and returns two series
    mean difference, lower and upper bounds of 95 % confidence interval"""

    s1_sem = series_a.std() / np.sqrt(series_a.count())
    s2_sem = series_b.std() / np.sqrt(series_b.count())
    sem_diff = np.sqrt(s1_sem ** 2 + s2_sem ** 2)
    mean_diff = series_a.mean() - series_b.mean()
    lcb = mean_diff - 2 * sem_diff
    ucb = mean_diff + 2 * sem_diff

    return mean_diff, lcb, ucb

def sampling_mean_diff_ci(
    series_a: pd.Series,
    series_b: pd.Series,
    number_of_samples: int,
    alpha: float = 0.95,
) -> Tuple[np.ndarray, float]:
    """Takes two series, number of samples and alpha parameter and returns
    actual mean difference, array od sampled mean differences, lower and upper bounds
    of confidence interval controled by alpha parameter"""

    actual_mean_diff = series_a.mean() - series_b.mean()
    mean_diff = np.zeros((number_of_samples,))
    for i in range(number_of_samples):
        mean1 = np.random.choice(series_a, series_a.size).mean()
        mean2 = np.random.choice(series_b, series_b.size).mean()
        mean_diff[i] = mean1 - mean2
    quant = (1 - alpha) / 2
    lcb = np.quantile(mean_diff, quant)
    ucb = np.quantile(mean_diff, 1 - quant)

    return actual_mean_diff, mean_diff, lcb, ucb

def sampling_mean(
    series_a: pd.Series, series_b: pd.Series, sample_size: int, number_of_samples: int
) -> Tuple[np.ndarray]:
    """Samples number_of_samples from two series with with given sample_size
    and returns numpy arrays with means"""

    s1 = np.empty(sample_size)
    s2 = np.empty(sample_size)
    for i in range(number_of_samples):
        s1[i] = series_a.sample(sample_size, replace=True).mean()
        s2[i] = series_b.sample(sample_size, replace=True).mean()

    return s1, s2

def perm_test(series_a: pd.Series, series_b: pd.Series) -> float:
    """Makes a permutation test from provided input of two series
    and returns mean difference of permutation result"""

    s1 = series_a.reset_index(drop=True)
    s2 = series_b.reset_index(drop=True)
    data = pd.concat([s1, s2], ignore_index=True)
    total_n = s1.shape[0] + s2.shape[0]
    index2 = set(np.random.choice(range(total_n), s2.shape[0], replace=False))
    index1 = set(range(total_n)) - index2

    return data.iloc[list(index1)].mean() - data.iloc[list(index2)].mean()


#import packages
import numpy as np
import matplotlib.pyplot as plt
import random

def dataprocessing():
    #This function Takes in data and normalizes it for easier distance calculation
    #parameters: None
    #return: fdata - the normalized data
    #It returns the normalized data
    f = open("data.txt", "r+", encoding ="utf-8")
    raw = f.read().split()
    f.close()
    data = np.array(raw)
    a1 =[]
    a2 =[]
    na1 = []
    na2 = []
    dataset = []
    #transform the data to float value
    for i in range (len(data)):
        k = data[i].split(",")
        a1.append(float(k[0]))
        a2.append(float(k[1]))
    #normalize the data
    for i in range (len(data)):
        na1.append(round((a1[i]-min(a1))/(max(a1) - min(a1)),3))
        na2.append(round((a2[i]-min(a2))/(max(a2) - min(a2)),3))
    #turn data into an array dataset
    for i in range (len(data)):
        d =[]
        d.append(na1[i])
        d.append(na2[i])
        dataset.append(d)
    fdata = np.array(dataset)
    #return the normalized data
    return fdata

def kmeans(data,k):
    #This function is a implementation of The K-means algorithm
    #Prameters: data - the data set and k - number of clusters
    #Return:
    # - centers: array of final centers
    # - count: number of iterations
    # - cluster:labels (the assignment of points to centers)
    # - errorvalL: final error value
    #I implement this code based on the textbook code
    errorval = 0
    # Find the minimum and maximum values for each feature
    minima = data.min(axis=0)
    maxima = data.max(axis=0)
    # Pick the centre locations randomly
    centers = np.random.rand(k,2)*(maxima-minima)+minima
    oldCenters = np.random.rand(k,2)*(maxima-minima)+minima
    nData = len(data)
    count = 0
    #loop until the centers stop moving
    while np.sum(np.sum(oldCenters-centers))!= 0:

        oldCenters = centers.copy()
        count += 1

        # Compute distances
        distances = np.ones((1,nData))*np.sum((data-centers[0,:])**2,axis=1)
        for j in range(k-1):
            distances = np.append(distances,np.ones((1,nData))*np.sum((data-centers[j+1,:])**2,axis=1),axis=0)
        # Identify the closest cluster
        cls = distances.argmin(axis=0)
        cluster = np.transpose(cls*np.ones((1,nData)))

        # Update the cluster centres
        for j in range(k):
            thisCluster = np.where(cluster==j,1,0)
            if sum(thisCluster)>0:
                centers[j,:] = np.sum(data*thisCluster,axis=0)/np.sum(thisCluster)
    #calculate the error value based on the center and it corresponding clusters
    for i in range (k):
        cluster1 = np.where(cluster==i,1,0)
        errorval += np.sum((((data*cluster1)[~np.all((data*cluster1)==0,axis=1)])-centers[i,:])**2)
    return centers, count, cluster, errorval

def main():
    #The main function
    errorlist=[]
    #k value
    k = 4
    #get the data
    data = dataprocessing()
    #get the returns value from the kmeans
    centers, iterations, indices, errorval = kmeans(data,k)
    #output
    print('My optimal value for k is this situation is 4')
    print('{} centers are: '.format(k), centers)
    print('The number of iterations is:', iterations)
    print('The final error value for k={} is'.format(k), errorval)
    print('The list labels (The center index of each point in the dataset, ranging from 0-3) is the following: \n', indices.tolist())
    #get the data for k from 1 to 10
    for i in range (1,11):
        c, it, ind, error = kmeans(data,i)
        kk = [i,error]
        errorlist.append(kk)
    errorlist[3][1] = errorval
    #put the data to an array for plotting
    errorlist = np.array(errorlist)
    return centers, data, errorlist

#output, print out the desired e centers, the number of iterations
#to convergence, the final error (sum of squared distances from points to centers), and
#labels (the assignment of points to centers).
centers, data, err = main()

plt.scatter(np.array(centers)[:, 0], np.array(centers)[:, 1], color='red')
plt.scatter(data[:, 0], data[:, 1], alpha=0.1, color ='black')
plt.title('A graph of the original data and the centers ')
plt.show()

#error value of k clusters with k from 1 to 10
print(err)

#graph of error values of k clusters with k from 1 to 10
plt.plot(err[:, 0], err[:, 1], color='red')
plt.title('A graph of error vs k')
plt.show()


#import libraries required
import numpy as np
import random

import matplotlib.pyplot as plt
import seaborn as sns

from scipy.stats import norm
from scipy.stats import poisson
from scipy.stats import binom

def sample_distribution_func(samples, distribution, mean=0, sd=1, mu=3, n=10, p=0.5):
    """This function will draw random numbers from a given distribution (Normal, Poisson, Binomial).

        It takes one argument for the number of samples and a second argument which specifies the
        distribution (Normal, Poisson or Binomial).

        The function can also handle additional parameters depending on the distribution chosen"""

    f_sampled_distribution = np.zeros(shape=[0, 1])

    if distribution == "Normal":
        f_sampled_distribution = norm.rvs(size=samples, loc=mean, scale=sd)
        print (samples, "samples of a", distribution, "distribution with parameter values of mean =", mean, "and sd =", sd)
        return f_sampled_distribution
    elif distribution == "Poisson":
        f_sampled_distribution = poisson.rvs(size=samples, mu=mu)
        print (samples, "samples of a", distribution, "distribution with a parameter value of mu =", mu)
        return f_sampled_distribution
    elif distribution == "Binomial":
        f_sampled_distribution = binom.rvs(size=samples, n=n, p=p)
        print (samples, "samples of a", distribution, "distribution with parameter values of n =", n, "and p =", p)
        return f_sampled_distribution
    else:
        print('ERROR!:', distribution, "distribution is not defined within function")
        return f_sampled_distribution


from math import *
import sympy as sp
from scipy.misc import derivative
from prettytable import PrettyTable

def funcion(expr, x_eval, y_eval):
    x = x_eval
    y = y_eval
    funcion = eval(expr)
    return funcion

def derivar(ecuacion):

    x = sp.Symbol('x')
    y = sp.Symbol('y')
    derv_x = str(sp.Derivative(ecuacion, x, evaluate=True))
    derv_y = str(sp.Derivative(ecuacion, y, evaluate=True))

    return derv_x, derv_y

def mTaylor(ecuacion, x0, x1, y0, n):
    n = int(n)
    h = (x1-x0)/n
    x = x0
    y = y0
    dfdx, dfdy = derivar(ecuacion)
    dfxy_literal = dfdx+'+'+dfdy+'*('+ecuacion+')'
    # constructor tabla
    enc_y = f'y{x1}'
    tabla = PrettyTable(["Iteracion", "xi", "yi", enc_y])
    tabla.title = "METODO DE TAYLOR DE SEGUNDO ORDEN "
    for i in range(0, n):
        fxy = funcion(ecuacion, x, y)
        dfxy = funcion(dfxy_literal, x, y)
        y_cal = y+h*fxy+((h**2)/2)*dfxy
        if i < 10:
            tabla.add_row(
                [i+1, "{0:.4f}".format(x), "{0:.6f}".format(y), "{0:.6f}".format(y_cal)])
        x = x+h
        y = y_cal
        # print(y_cal)
    # print(fxy,dfxy)
    print("\n")
    print(tabla)
    print(f'             y({x1}) = {"{0:.8f}".format(y_cal)}')

# datos de ejemplo
""" x0 = 0
x1 = 1
y = 2
ecuacion = 'x-y'
n = 100
mTaylor(ecuacion, x0, x1, y, n) """

""" x0 = 0
x1 = 1
y = 1
ecuacion = 'cos(x*y)'
n = 5
mTaylor(ecuacion, x0, x1, y, n) """

""" x0 = 0
x1 = 1
y = 1
ecuacion = 'x+y'
n = 10
mTaylor(ecuacion, x0, x1, y, n) """

src/6. Ecuaciones diferenciales ordinarias/Metodos/Taylor.py


from sys import argv
from math import sqrt, trunc
from numpy import zeros, sum, ndarray
import time

from numba import jit, float32, boolean, int32, float64, int64

compile_start = time.time()

@jit([boolean(int32)])
def check_prime(num):
  has_divisor = False

  if(num ==2):
    return True
  #Check 2 specially
  if num%2 == 0:
  # Don't need to check any further - return right now
    return False
  for i in range(3, trunc(sqrt(num))+1, 2):
    if num%i == 0:
      has_divisor = True
      # Don't need to check any further - at least one divisor exists
      break
  #Either we found a divisor, or there are none
  return not has_divisor

compile_end = time.time()
print("Compilation of check_prime took {} seconds".format(compile_end-compile_start))

def check_prime_nj(num):
  has_divisor = False

  if(num ==2):
    return True
  #Check 2 specially
  if num%2 == 0:
  # Don't need to check any further - return right now
    return False
  for i in range(3, trunc(sqrt(num))+1, 2):
    if num%i == 0:
      has_divisor = True
      # Don't need to check any further - at least one divisor exists
      break
  #Either we found a divisor, or there are none
  return not has_divisor

compile_start = time.time()

@jit((int64, int64, float64[:]))
def main_loop(lower, length, flags):
  for i in range(1, length):
    flags[i] = check_prime(lower+i)

compile_end = time.time()
print("Compilation of main_loop took {} seconds".format(compile_end-compile_start))

def main_loop_nj(lower, length, flags):
  for i in range(1, length):
    flags[i] = check_prime_nj(lower+i)

def main(lower, upper, do_jit=True):

  length = upper - lower
  flags = zeros(length)
  start_time = time.time()
  if(do_jit):
    main_loop(lower, length, flags)
  else:
    main_loop_nj(lower, length, flags)
  end_time = time.time()
  print("Found ", int(sum(flags)), " primes in", end_time-start_time, ' s')

if __name__ == "__main__":

  try:
    lower = int(input('Enter lower bnd: '))
  except:
    print("I didn't understand. I'll try 100000")
    lower = 100000
  try:
    upper = int(input('Enter upper bnd: '))
  except:
    print("I didn't understand. I'll try 200000")
    upper = 200000

  main(lower, upper)


from math import hypot
import numpy as np
import json
import sys

def givens_rotation(A):
    """Perform QR decomposition of matrix A using Givens rotation."""
    (num_rows, num_cols) = np.shape(A)

    # Initialize orthogonal matrix Q and upper triangular matrix R.
    Q = np.identity(num_rows)
    R = np.copy(A)

    # Iterate over lower triangular matrix.
    (rows, cols) = np.tril_indices(num_rows, -1, num_cols)
    for (row, col) in zip(rows, cols):

        # Compute Givens rotation matrix and
        # zero-out lower triangular matrix entries.
        if R[row, col] != 0:
            (c, s) = _givens_rotation_matrix_entries(R[col, col], R[row, col])

            G = np.identity(num_rows)
            G[[col, row], [col, row]] = c
            G[row, col] = s
            G[col, row] = -s

            R = np.dot(G, R)
            Q = np.dot(Q, G.T)

    return (Q, R)

def _givens_rotation_matrix_entries(a, b):
    """Compute matrix entries for Givens rotation."""
    r = hypot(a, b)
    c = a/r
    s = -b/r

    return (c, s)

n = int(sys.argv[1])
A = np.random.rand(n, n);
Q,R = givens_rotation(A)
result = {
    "A": A.tolist(),
    "R": R.tolist(),
    "Q": Q.tolist()
}
# print(result)
print(json.dumps(result))
sys.stdout.flush()


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

inputfile = "../Datasets/S1MME_week43.csv"
df = pd.read_csv(inputfile)
sig=df.S1_mode_combined_attach_request_times_SEQ

N=24 #hours
sample=4 #sampled period number

dataframe = pd.Series(sig)
ts=pd.DataFrame(dataframe.values)
rows=int(len(ts)/N)
data = ts.values.reshape(rows,N)

#####################################################################
## estimación por valor medio de cada hora
#####################################################################

betas=data.mean(axis=0)
plt.plot(betas)
plt.plot(data[sample,:])
plt.plot(data[sample,:]-betas)
plt.legend(['betas','data','data-betas'])
plt.show()

#####################################################################
## repetir para todo el largo de la serie
#####################################################################

est=np.tile(betas,rows)

plt.plot(sig)
plt.plot(est)
plt.plot(sig-est)
plt.legend(['sig','est','sig-est'])
plt.show()

#####################################################################
## ejemplo diferenciando
#####################################################################

dataset=sig
interval=1
diff = list()
for i in range(interval, len(dataset)):
	value = dataset[i] - dataset[i - interval]
	diff.append(value)

plt.plot(sig)
plt.plot(diff)
plt.legend(['sig','diff'])
plt.show()

#repito la estimacion pero para la diferenciada
N=24 #hours
dataframe = pd.Series(pd.concat([pd.Series(diff[0]),pd.Series(diff)], axis=0))
ts=pd.DataFrame(dataframe.values)
rows=int(len(ts)/N)
data = ts.values.reshape(rows,N)

betas=data.mean(axis=0)
plt.plot(betas)
plt.plot(data[sample,:])
plt.plot(data[sample,:]-betas)
plt.legend(['betas','data','data-betas'])
plt.show()

#####################################################################
## repetir para todo el largo de la serie
#####################################################################

est=np.tile(betas,rows)

plt.plot(sig)
plt.plot(est)
plt.plot(sig-est)
plt.legend(['sig','est','sig-est'])
plt.show()


import numpy as np
import torch
import torch.nn as nn

"""
The modules/function here implement custom versions of layer normalization in PyTorch.
In contrast to more advanced implementations no use of a running mean/variance is made.
You should fill in code into indicated sections.
"""

######################################################################################
# Code for Question 3.1
######################################################################################

class CustomLayerNormAutograd(nn.Module):
    """
    This nn.module implements a custom version of the layer norm operation for MLPs.
    The operations called in self.forward track the history if the input tensors have the
    flag requires_grad set to True. The backward pass does not need to be implemented, it
    is dealt with by the automatic differentiation provided by PyTorch.
    """

    def __init__(self, n_neurons, eps=1e-5):
        """
        Initializes CustomLayerNormAutograd object.

        Args:
          n_neurons: int specifying the number of neurons
          eps: small float to be added to the variance for stability

        TODO:
          Save parameters for the number of neurons and eps.
          Initialize parameters gamma and beta via nn.Parameter
        """
        super(CustomLayerNormAutograd, self).__init__()

        ########################
        # PUT YOUR CODE HERE  #
        #######################

        self.n_neurons = n_neurons
        self.eps = eps

        self.gamma = nn.Parameter(torch.ones(n_neurons))
        self.beta = nn.Parameter(torch.zeros(n_neurons))

        ########################
        # END OF YOUR CODE    #
        #######################

    def forward(self, input):
        """
        Compute the layer normalization

        Args:
          input: input tensor of shape (n_batch, n_neurons)
        Returns:
          out: layer-normalized tensor

        TODO:
          Check for the correctness of the shape of the input tensor.
          Implement layer normalization forward pass as given in the assignment.
          For the case that you make use of torch.var be aware that the flag unbiased=False should be set.
        """

        ########################
        # PUT YOUR CODE HERE  #
        #######################
        batch_size, num_neurons = input.shape

        assert self.n_neurons == num_neurons, "{} channels are needed but there are only {}".format(self.n_neurons, num_neurons)

        # compute mean
        mu = torch.mean(input, dim=1, keepdim=True)

        # compute var
        var = torch.var(input, dim=1, unbiased=False, keepdim=True)
        var = (var+self.eps)**(-1./2.)

        # normalize
        X_norm = input - mu
        X_norm = X_norm * var

        # scale and shift
        gamma_matrix = self.gamma.expand(batch_size, -1)
        beta_matrix = self.beta.expand(batch_size, -1)

        out = gamma_matrix * X_norm + beta_matrix

        ########################
        # END OF YOUR CODE    #
        #######################

        return out

######################################################################################
# Code for Question 3.2 b)
######################################################################################

class CustomLayerNormManualFunction(torch.autograd.Function):
    """
    This torch.autograd.Function implements a functional custom version of the layer norm operation for MLPs.
    Using torch.autograd.Function allows you to write a custom backward function.
    The function will be called from the nn.Module CustomLayerNormManualModule
    Inside forward the tensors are (automatically) not recorded for automatic differentiation since the backward
    pass is done via the backward method.
    The forward pass is not called directly but via the apply() method. This makes sure that the context objects
    are dealt with correctly. Example:
      my_bn_fct = CustomLayerNormManualFunction()
      normalized = fct.apply(input, gamma, beta, eps)
    """

    @staticmethod
    def forward(ctx, input, gamma, beta, eps=1e-5):
        """
        Compute the layer normalization

        Args:
          ctx: context object handling storing and retrival of tensors and constants and specifying
               whether tensors need gradients in backward pass
          input: input tensor of shape (n_batch, n_neurons)
          gamma: variance scaling tensor, applied per neuron, shpae (n_neurons)
          beta: mean bias tensor, applied per neuron, shpae (n_neurons)
          eps: small float added to the variance for stability
        Returns:
          out: layer-normalized tensor

        TODO:
          Implement the forward pass of layer normalization
          Store constant non-tensor objects via ctx.constant=myconstant
          Store tensors which you need in the backward pass via ctx.save_for_backward(tensor1, tensor2, ...)
          Intermediate results can be decided to be either recomputed in the backward pass or to be stored
          for the backward pass. Do not store tensors which are unnecessary for the backward pass to save memory!
          For the case that you make use of torch.var be aware that the flag unbiased=False should be set.
        """

        ########################
        # PUT YOUR CODE HERE  #
        #######################

        batch_size, num_neurons = input.shape

        # compute mean
        mu = torch.mean(input, dim=1, keepdim=True)

        # compute variance
        var = torch.var(input, dim=1, unbiased=False, keepdim=True)
        var = (var+eps)**(-1./2.)

        # normalize
        X_norm = input - mu
        X_norm = X_norm * var

        # scale and shift
        gamma_matrix = gamma.expand(batch_size, -1)
        beta_matrix = beta.expand(batch_size, -1)

        out = gamma_matrix * X_norm + beta_matrix

        ctx.save_for_backward(input, gamma, beta)

        ctx.variance = var
        ctx.mu = mu
        ctx.X_norm = X_norm

        ########################
        # END OF YOUR CODE    #
        #######################

        return out

    @staticmethod
    def backward(ctx, grad_output):
        """
        Compute backward pass of the layer normalization.

        Args:
          ctx: context object handling storing and retrival of tensors and constants and specifying
               whether tensors need gradients in backward pass
        Returns:
          out: tuple containing gradients for all input arguments

        TODO:
          Retrieve saved tensors and constants via ctx.saved_tensors and ctx.constant
          Compute gradients for inputs where ctx.needs_input_grad[idx] is True. Set gradients for other
          inputs to None. This should be decided dynamically.
        """

        ########################
        # PUT YOUR CODE HERE  #
        #######################
        X, gamma, beta = ctx.saved_tensors
        grad_input = grad_gamma = grad_beta = None

        batch_size, num_neurons = grad_output.shape

        if ctx.needs_input_grad[0]:

          first = grad_output * torch.mm(ctx.variance, gamma[:, None].T)

          second = torch.sum(first, axis=1, keepdim=True).expand(-1, num_neurons) / num_neurons

          var_cubed = ctx.variance**3
          x_minus_mu = X - ctx.mu.expand(-1, num_neurons)

          third_prod = grad_output * torch.mm(var_cubed, gamma[:, None].T) * x_minus_mu
          third_sum = torch.sum(third_prod, axis=1, keepdim=True).expand(-1, num_neurons) / num_neurons
          third = (x_minus_mu * third_sum)

          grad_input = first - second - third

        if ctx.needs_input_grad[1]:
          grad_gamma = torch.sum(grad_output * ctx.X_norm, dim=0)

        if ctx.needs_input_grad[2]:
          grad_beta = torch.sum(grad_output, dim=0)

        ########################
        # END OF YOUR CODE    #
        #######################

        # return gradients of the three tensor inputs and None for the constant eps
        return grad_input, grad_gamma, grad_beta, None

######################################################################################
# Code for Question 3.2 c)
######################################################################################

class CustomLayerNormManualModule(nn.Module):
    """
    This nn.module implements a custom version of the layer norm operation for MLPs.
    In self.forward the functional version CustomLayerNormManualFunction.forward is called.
    The automatic differentiation of PyTorch calls the backward method of this function in the backward pass.
    """

    def __init__(self, n_neurons, eps=1e-5):
        """
        Initializes CustomLayerNormManualModule object.

        Args:
          n_neurons: int specifying the number of neurons
          eps: small float to be added to the variance for stability

        TODO:
          Save parameters for the number of neurons and eps.
          Initialize parameters gamma and beta via nn.Parameter
        """
        super(CustomLayerNormManualModule, self).__init__()

        ########################
        # PUT YOUR CODE HERE  #
        #######################

        self.n_neurons = n_neurons
        self.eps = eps

        self.gamma = nn.Parameter(torch.ones(n_neurons))
        self.beta = nn.Parameter(torch.zeros(n_neurons))
        ########################
        # END OF YOUR CODE    #
        #######################

    def forward(self, input):
        """
        Compute the layer normalization via CustomLayerNormManualFunction

        Args:
          input: input tensor of shape (n_batch, n_neurons)
        Returns:
          out: layer-normalized tensor

        TODO:
          Check for the correctness of the shape of the input tensor.
          Instantiate a CustomLayerNormManualFunction.
          Call it via its .apply() method.
        """

        ########################
        # PUT YOUR CODE HERE  #
        #######################
        batch_size, num_neurons = input.shape

        assert self.n_neurons == num_neurons, "{} channels are needed but there are only {}".format(self.n_neurons, num_neurons)

        custom_layer_norm = CustomLayerNormManualFunction()
        out = custom_layer_norm.apply(input, self.gamma, self.beta, self.eps)

        ########################
        # END OF YOUR CODE    #
        #######################

        return out

if __name__ == '__main__':
    # create test batch
    n_batch = 8
    n_neurons = 128
    # create random tensor with variance 2 and mean 3
    x = 2 * torch.randn(n_batch, n_neurons, requires_grad=True) + 10
    print('Input data:\n\tmeans={}\n\tvars={}'.format(x.mean(dim=1).data, x.var(dim=1).data))

    # test CustomLayerNormAutograd
    print('3.1) Test automatic differentation version')
    bn_auto = CustomLayerNormAutograd(n_neurons)
    y_auto = bn_auto(x)
    print('\tmeans={}\n\tvars={}'.format(y_auto.mean(dim=1).data, y_auto.var(dim=1).data))

    # test CustomLayerNormManualFunction
    # this is recommended to be done in double precision
    print('3.2 b) Test functional version')
    input = x.double()
    gamma = torch.sqrt(10 * torch.arange(n_neurons, dtype=torch.float64, requires_grad=True))
    beta = 100 * torch.arange(n_neurons, dtype=torch.float64, requires_grad=True)
    bn_manual_fct = CustomLayerNormManualFunction(n_neurons)
    y_manual_fct = bn_manual_fct.apply(input, gamma, beta)
    print('\tmeans={}\n\tvars={}'.format(y_manual_fct.mean(dim=1).data, y_manual_fct.var(dim=1).data))
    # gradient check
    grad_correct = torch.autograd.gradcheck(bn_manual_fct.apply, (input, gamma, beta))
    if grad_correct:
        print('\tgradient check successful')
    else:
        raise ValueError('gradient check failed')

    # test CustomLayerNormManualModule
    print('3.2 c) Test module of functional version')
    bn_manual_mod = CustomLayerNormManualModule(n_neurons)
    y_manual_mod = bn_manual_mod(x)
    print('\tmeans={}\n\tvars={}'.format(y_manual_mod.mean(dim=1).data, y_manual_mod.var(dim=1).data))


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

import numpy as np
import pandas as pd

from functools import partial
from math import sqrt
from multiprocessing import Pool
from typing import Callable, Optional

AVAILABLE_METRICS = ['mse', 'rmse', 'mape', 'smape', 'mase', 'rmsse',
                     'mini_owa', 'pinball_loss']

######################################################################
# METRICS
######################################################################

def mse(y: np.array, y_hat:np.array) -> float:
    """Calculates Mean Squared Error.

    MSE measures the prediction accuracy of a
    forecasting method by calculating the squared deviation
    of the prediction and the true value at a given time and
    averages these devations over the length of the series.

    Parameters
    ----------
    y: numpy array
        actual test values
    y_hat: numpy array
        predicted values

    Returns
    -------
    scalar:
        MSE
    """
    mse = np.mean(np.square(y - y_hat))

    return mse

def rmse(y: np.array, y_hat:np.array) -> float:
    """Calculates Root Mean Squared Error.

    RMSE measures the prediction accuracy of a
    forecasting method by calculating the squared deviation
    of the prediction and the true value at a given time and
    averages these devations over the length of the series.
    Finally the RMSE will be in the same scale
    as the original time series so its comparison with other
    series is possible only if they share a common scale.

    Parameters
    ----------
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values

    Returns
    -------
    scalar: RMSE
    """
    rmse = sqrt(np.mean(np.square(y - y_hat)))

    return rmse

def mape(y: np.array, y_hat:np.array) -> float:
    """Calculates Mean Absolute Percentage Error.

    MAPE measures the relative prediction accuracy of a
    forecasting method by calculating the percentual deviation
    of the prediction and the true value at a given time and
    averages these devations over the length of the series.

    Parameters
    ----------
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values

    Returns
    -------
    scalar: MAPE
    """
    mape = np.mean(np.abs(y - y_hat) / np.abs(y))
    mape = 100 * mape

    return mape

def smape(y: np.array, y_hat:np.array) -> float:
    """Calculates Symmetric Mean Absolute Percentage Error.

    SMAPE measures the relative prediction accuracy of a
    forecasting method by calculating the relative deviation
    of the prediction and the true value scaled by the sum of the
    absolute values for the prediction and true value at a
    given time, then averages these devations over the length
    of the series. This allows the SMAPE to have bounds between
    0% and 200% which is desireble compared to normal MAPE that
    may be undetermined.

    Parameters
    ----------
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values

    Returns
    -------
    scalar: SMAPE
    """
    scale = np.abs(y) + np.abs(y_hat)
    scale[scale == 0] = 1e-3
    smape = np.mean(np.abs(y - y_hat) / scale)
    smape = 200 * smape

    return smape

def mase(y: np.array, y_hat: np.array,
         y_train: np.array, seasonality: int = 1) -> float:
    """Calculates the M4 Mean Absolute Scaled Error.

    MASE measures the relative prediction accuracy of a
    forecasting method by comparinng the mean absolute errors
    of the prediction and the true value against the mean
    absolute errors of the seasonal naive model.

    Parameters
    ----------
    y: numpy array
      actual test values
    y_hat: numpy array
      predicted values
    y_train: numpy array
      actual train values for Naive1 predictions
    seasonality: int
      main frequency of the time series
      Hourly 24,  Daily 7, Weekly 52,
      Monthly 12, Quarterly 4, Yearly 1

    Returns
    -------
    scalar: MASE
    """
    scale = np.mean(abs(y_train[seasonality:] - y_train[:-seasonality]))
    mase = np.mean(abs(y - y_hat)) / scale
    mase = 100 * mase

    return mase

def rmsse(y: np.array, y_hat: np.array,
         y_train: np.array, seasonality: int = 1) -> float:
    """Calculates the M5 Root Mean Squared Scaled Error.

    Parameters
    ----------
    y: numpy array
      actual test values
    y_hat: numpy array of len h (forecasting horizon)
      predicted values
    y_train: numpy array
       actual train values
    seasonality: int
      main frequency of the time series
      Hourly 24,  Daily 7, Weekly 52,
      Monthly 12, Quarterly 4, Yearly 1

    Returns
    -------
    scalar: RMSSE
    """
    scale = np.mean(np.square(y_train[seasonality:] - y_train[:-seasonality]))
    rmsse = sqrt(mse(y, y_hat) / scale)
    rmsse = 100 * rmsse

    return rmsse

def mini_owa(y: np.array, y_hat: np.array,
             y_train: np.array,
             seasonality: int,
             y_bench: np.array):
    """Calculates the Overall Weighted Average for a single series.

    MASE, sMAPE for Naive2 and current model
    then calculatess Overall Weighted Average.

    Parameters
    ----------
    y: numpy array
        actual test values
    y_hat: numpy array of len h (forecasting horizon)
        predicted values
    y_train: numpy array
        insample values of the series for scale
    seasonality: int
        main frequency of the time series
        Hourly 24,  Daily 7, Weekly 52,
        Monthly 12, Quarterly 4, Yearly 1
    y_bench: numpy array of len h (forecasting horizon)
        predicted values of the benchmark model

    Returns
    -------
    return: mini_OWA
    """
    mase_y = mase(y, y_hat, y_train, seasonality)
    mase_bench = mase(y, y_bench, y_train, seasonality)

    smape_y = smape(y, y_hat)
    smape_bench = smape(y, y_bench)

    mini_owa = ((mase_y/mase_bench) + (smape_y/smape_bench))/2

    return mini_owa

def pinball_loss(y: np.array, y_hat: np.array, tau: int = 0.5):
    """Calculates the Pinball Loss.

    The Pinball loss measures the deviation of a quantile forecast.
    By weighting the absolute deviation in a non symmetric way, the
    loss pays more attention to under or over estimation.
    A common value for tau is 0.5 for the deviation from the median.

    Parameters
    ----------
    y: numpy array
      actual test values
    y_hat: numpy array of len h (forecasting horizon)
      predicted values
    tau: float
      Fixes the quantile against which the predictions are compared.

    Returns
    -------
    return: pinball_loss
    """
    delta_y = y - y_hat
    pinball = np.maximum(tau * delta_y, (tau-1) * delta_y)
    pinball = pinball.mean()

    return pinball

######################################################################
#  PANEL EVALUATION
######################################################################

def _evaluate_ts(uid, y_test, y_hat,
                 y_train, metric,
                 seasonality, y_bench, metric_name):
    y_test_uid = y_test.loc[uid].y.values
    y_hat_uid = y_hat.loc[uid].y_hat.values

    if metric_name in ['mase', 'rmsse']:
        y_train_uid = y_train.loc[uid].y.values
        evaluation_uid = metric(y=y_test_uid, y_hat=y_hat_uid,
                                y_train=y_train_uid,
                                seasonality=seasonality)
    elif metric_name in ['mini_owa']:
        y_train_uid = y_train.loc[uid].y.values
        y_bench_uid = y_bench.loc[uid].y_hat.values
        evaluation_uid = metric(y=y_test_uid, y_hat=y_hat_uid,
                                y_train=y_train_uid,
                                seasonality=seasonality,
                                y_bench=y_bench_uid)

    else:
         evaluation_uid = metric(y=y_test_uid, y_hat=y_hat_uid)

    return uid, evaluation_uid

def evaluate_panel(y_test: pd.DataFrame,
                   y_hat: pd.DataFrame,
                   y_train: pd.DataFrame,
                   metric: Callable,
                   seasonality: Optional[int] = None,
                   y_bench: Optional[pd.DataFrame] = None,
                   threads: Optional[int] = None):
    """Calculates a specific metric for y and y_hat (and y_train, if needed).

    Parameters
    ----------
    y_test: pandas df
        df with columns ['unique_id', 'ds', 'y']
    y_hat: pandas df
        df with columns ['unique_id', 'ds', 'y_hat']
    y_train: pandas df
        df with columns ['unique_id', 'ds', 'y'] (train)
        This is used in the scaled metrics ('mase', 'rmsse').
    metric: callable
        loss function
    seasonality: int
        Main frequency of the time series.
        Used in ('mase', 'rmsse').
        Commonly used seasonalities:
            Hourly: 24,
            Daily: 7,
            Weekly: 52,
            Monthly: 12,
            Quarterly: 4,
            Yearly: 1.
    y_bench: pandas df
        df with columns ['unique_id', 'ds', 'y_hat']
        predicted values of the benchmark model
        This is used in 'mini_owa'.
    threads: int
        Number of threads to use. Use None (default) for parallel processing.

    Returns
    ------
    pandas dataframe:
        loss ofr each unique_id in the panel data
    """
    metric_name = metric.__code__.co_name
    uids = y_test['unique_id'].unique()
    y_hat_uids = y_hat['unique_id'].unique()

    assert len(y_test)==len(y_hat), "not same length"
    assert all(uids == y_hat_uids), "not same u_ids"

    y_test = y_test.set_index(['unique_id', 'ds'])
    y_hat = y_hat.set_index(['unique_id', 'ds'])

    if metric_name in ['mase', 'rmsse']:
        y_train = y_train.set_index(['unique_id', 'ds'])

    elif metric_name in ['mini_owa']:
        y_train = y_train.set_index(['unique_id', 'ds'])
        y_bench = y_bench.set_index(['unique_id', 'ds'])

    partial_evaluation = partial(_evaluate_ts, y_test=y_test, y_hat=y_hat,
                                 y_train=y_train, metric=metric,
                                 seasonality=seasonality,
                                 y_bench=y_bench,
                                 metric_name=metric_name)

    with Pool(threads) as pool:
        evaluations = pool.map(partial_evaluation, uids)

    evaluations = pd.DataFrame(evaluations, columns=['unique_id', 'error'])

    return evaluations


from matplotlib.image import imread
import numpy as np
import matplotlib.pyplot as plt
import os
import pywt

plt.rcParams['figure.figsize'] = [16, 16]
plt.rcParams.update({'font.size': 18})

A = imread('headshot.jpg')
B = np.mean(A, -1)                      # convert RGB to grayscale

# Wavelet Compression
n = 4
w = 'db1'                               # daubechies wavelet
coeffs = pywt.wavedec2(B, wavelet=w, level=n)
coeff_arr, coeff_slices = pywt.coeffs_to_array(coeffs)
Csort = np.sort(np.abs(coeff_arr.reshape(-1)))

for keep in (0.1, 0.05, 0.01, 0.005):
    thresh = Csort[int(np.floor((1-keep)*len(Csort)))]
    ind = np.abs(coeff_arr) > thresh
    Cfilt = coeff_arr * ind                                         # threshold small indices

    coeffs_filt = pywt.array_to_coeffs(Cfilt, coeff_slices, output_format='wavedec2')

    # Plot reconstruction
    Arecon = pywt.waverec2(coeffs_filt, wavelet=w)
    plt.figure()
    plt.imshow(Arecon.astype('uint8'), cmap='gray_r')
    plt.axis('off')
    plt.rcParams['figure.figsize'] = [8, 8]
    plt.title('keep = ' + str(keep))
    plt.show()

""" Can code FFT2 vs Wavelet2 and compare to performance -- homework
    can compute the norm of the error to compare performance """


# -*- coding: utf-8 -*-
"""
Created on Sun Nov  3 16:49:40 2019

@author: ATTUIL RUBEN
@studentid:3672225
@email:ruben-attuil@outlook.fr
"""

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

N = 12
k = 1
m = 1

# Création de la matrice d'inertie M
M = (2*np.eye(N) - np.eye(N, k=-1) - np.eye(N, k=1))*k/m
print("Matrix M is :")
print(M)

#Affichage des valeurs et vecteurs propres

e_val,e_vec=la.eig(M)
print("Eigenvalues are :")
print(e_val)

print("Eigenvectors are :")
print(e_vec)

time_sample = np.linspace(0,40,100)
W=np.zeros(len(e_val))#creation d'un tableau qui va contenir les pulsations propres
F=np.zeros(len(e_val))#creation d'un tableau qui va contenir les fréquences propres associées
for i in range(len(e_val)):
    w=np.sqrt(e_val[i])
    W[i]=w #remplissage du tableau des pulsations propres

for i in range(3):
    #E represente le déplacement de chaque masse ici 3 masses étudiées
    E1=e_vec[0,i]*np.cos(w*time_sample)
    E2=e_vec[1,i]*np.cos(w*time_sample)
    E3=e_vec[2,i]*np.cos(w*time_sample)
    # x représente la position de chaquze masse ici 3 masses étudiées
    x1=1+E1
    x2=2*(1+E2)
    x3=3*(1+E3)
    #tracé des graphes représentant la position de chaque masse en fonction du temps
    plt.figure()
    plt.plot(time_sample,x1, c='b', label='x1')
    plt.plot(time_sample,x2, c='r', label='x2')
    plt.plot(time_sample,x3, c='g', label='x3')

    plt.title("mode normal"+ str(i+1))
    plt.xlabel("t")
    plt.ylabel("mouvement de chaque masse")
    plt.legend(loc='upper left')

    plt.show()
    plt.savefig('./mode_'+str(i+1)+'.png')

for i in range (len(W)):
    F[i]=W[i]/(2*np.pi)#remplissage du tableau contenant les fréquences propres
mode=[0,1,2,3,4,5,6,7,8,9,10,11]
#Tracé du graphe répresentant les fréquences naturelles en fonction du numero de leur mode
plt.figure()
plt.scatter(mode,F)
plt.title("Natural fresquencies versus mode number")
plt.xlabel("mode number")
plt.ylabel("F")
plt.show()

print("naturals frequencies")
print(F)


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

def run_SEIR_ODE_model(
        N: 'population size',
        E0: 'init. exposed population',
        I0: 'init. infected population',
        R0: 'init. removed population',
        beta: 'infection probability',
        gamma: 'removal probability',
        alpha_inv: 'incubation period',
        t_max: 'numer of days to run'
    ) -> pd.DataFrame:

    S0 = N - I0 - R0 - E0
    alpha = 1/alpha_inv

    # A grid of time points (in days)
    t = range(t_max)

    # The SEIR model differential equations.
    def deriv(y, t, N, beta, gamma, alpha):
        S, E, I, R = y
        dSdt = -beta * S * I / N
        dEdt = -dSdt - alpha*E
        dIdt = alpha*E - gamma*I
        dRdt = gamma * I
        return dSdt, dEdt, dIdt, dRdt

    # Initial conditions vector
    y0 = S0, E0, I0, R0

    # Integrate the SIR equations over the time grid, t.
    ret = odeint(deriv, y0, t, args=(N, beta, gamma, alpha))
    S, E, I, R = ret.T

    return pd.DataFrame({'S': S, 'E': E, 'I': I, 'R': R}, index=t)

if __name__ == '__main__':
    N = 13_000_000
    E0, I0, R0 = 0, 152, 1
    beta, gamma, alpha_inv = 1.75, 0.5, 5
    t_max = 60
    results = run_SEIR_ODE_model(N, E0, I0, R0, beta, gamma, alpha_inv, t_max)

    # plot
    plt.style.use('ggplot')
    (results
     # .div(1_000_000)
     [['E', 'I']]
     .plot(figsize=(8,6), fontsize=20, logy=True))
    params_title = (
        f'SEIR($\gamma$={gamma}, $\\beta$={beta}, $\\alpha$={1/alpha_inv}, $N$={N}, '
        f'$E_0$={E0}, $I_0$={I0}, $R_0$={R0})'
    )
    plt.title(f'Number of People Affected with model:\n' + params_title,
              fontsize=20)
    plt.legend(['Exposed', 'Infected'], fontsize=20)
    plt.xlabel('Days', fontsize=20)
    plt.ylabel('People', fontsize=20)
    plt.show()


# support/Loss_n_ActivationFunction.py

import numpy as np

# Activation Function

def tanh(x):
    return np.tanh(x);

def tanhDerivative(x):
    t = np.tanh(x)
    return 1-np.power(t,2);

def sigmoid(x):
    # prevent overflow
    x = np.clip(x, -600, 600 )
    return 1.0 / (1 + np.exp(-x))

def sigmoidDerivative(x):
    S = sigmoid(x)
    return S * (1 - S)

def softmax(x):
    x = np.clip(x, -600, 600 )
    return np.exp(x) / np.sum(np.exp(x), axis=1).reshape(-1,1)

def softmaxDerivative(x):
    S = softmax(x)
    return S * (1 - S)

def relu(x):
    return (x > 0) * x # for x>0 = x, for x <= 0 = 0

def reluDerivative(x):
    return (x > 0) * 1 # for x>0 = 1, for x <= 0 = 0

# Loss Function

def mse(y, yEst):
    return np.mean(np.power(y-yEst, 2));

def mseDerivative(y, yEst):
    m = y.shape[0]
    return 2*(yEst-y)/m;

def crossEntropy(y, yEst):
    eps = 1e-5
    return  - np.sum(y*np.log(yEst+eps), axis =1)

def crossEntropyDerivative(y, yEst):
    m = y.shape[0]
    c = np.sum(yEst - y, axis = 0)
    return c/m


import numpy as np

#########################

# Legendre Polynomial

def legendre_polynomials(S, k):

    u0 = np.ones(S.shape)
    x1 = S
    x2 = (3*S**2 -1 )/2
    x3 = (5*S**3-3*S)/2
    x4 = (35*S**4-30*S**2 +3)/8
    x5 = (63*S**5 - 70*S**3 + 15*S)/8

    X = [np.stack([u0,x1,x2], axis=1),
         np.stack([u0, x1, x2, x3], axis= 1),
         np.stack([u0, x1, x2, x3, x4], axis=1)]

    return X[k-2]

# the case for one price, startin from left to right
def laguerre_polynomials_ind(S, k):

    u0 = np.ones(S.shape)
    x1 = 1 - S
    x2 = 1 - 2*S + S**2/2
    x3 = 1 - 3*S + 3*S**2/2 - S**3/6
    x4 = 1 - 4*S + 3*S**2 - 2*S**3/3 + S**4/24

    X  = [np.stack([u0, x1, x2]),
          np.stack([u0, x1, x2, x3]),
          np.stack([u0, x1, x2, x3, x4])]

    return X[k-2]

def laguerre_polynomials(S, k):

    #  the first k terms of Laguerre Polynomials (k<=4)
#    x1 = np.exp(-S/2)
#    x2 = np.exp(-S/2) * (1 - S)
#    x3 = np.exp(-S/2) * (1 - 2*S + S**2/2)
#    x4 = np.exp(-S/2) * (1 - 3*S + 3* S**2/2 - S**3/6)

    u0 = np.ones(S.shape)
    x1 = 1 - S
    x2 = 1 - 2*S + S**2/2
    x3 = 1 - 3*S + 3*S**2/2 - S**3/6
    x4 = 1 - 4*S + 3*S**2 - 2*S**3/3 + S**4/24

    X  = [np.stack([u0, x1, x2], axis = 1),
          np.stack([u0, x1, x2, x3], axis = 1),
          np.stack([u0, x1, x2, x3, x4], axis = 1)]

    return X[k-2]


## I'll be following these two tutorials
# http://apmonitor.com/me575/index.php/Main/SimulatedAnnealing
# https://perso.crans.org/besson/publis/notebooks/Simulated_annealing_in_Python.html
# Here some important points
    # If the change in energy is negative, the energy state of the new
    # configuration is lower and the new configuration is accepted.
    # I'll be using the Boltzman factor unless Jeremy tells me something else.
    # If the change in energy is positive, the new configuration has a
    # higher energy state; however, it may still be accepted according
    #to the Boltzmann probability factor
    # The Boltzmann probability is compared to a random number drawn from a
    # uniform distribution between 0 and 1; if the random number is smaller
    # than the Boltzmann probability, the configuration is acceptedself.
    # This allows the algorithm to escape local minima.
import numpy as np
from math import sin, pi, log, exp
import matplotlib
import matplotlib.pyplot as plt
## energy: in my case it will be the original function with a minus
# just to convert the maxima into a minima (cp & pt from lm00 )

def energy(x,nn=1):
    n = nn
    # I add the minus sign to cenvert the problem in a minimization
    # problem, to feel familiar with a sytem looking for the minimum energy.
    f = (16*x[0]*(1-x[0])*x[1]*(1-x[1])*sin(n*pi*x[0])*sin(n*pi*x[1]))**2
    return -f

def acceptance_p(dE,T, dE_avg):
    B_factor = exp(-dE / (dE_avg * T) )
    return B_factor

def annealing(x_start=[0.,0.],n=100,m=20,nn=1):
    #print(x_start)
    # number of accepted solutions
    n_acc = 0.
    # Probability of acceptin worst solution at the beginning and in the end
    p_0 = 0.7
    p_n = 0.001
    # Initial and final Temperature
    T_0 = -1./log(p_0)
    T_n = -1./log(p_n)
    # Temperature reduction for each step in the walk
    T_frac = (T_n/T_0)**(1.0/(n-1.0))
    ## Numpy array to store the values for latter plotting results
    x = np.zeros((n+1,2))
    x[0] = x_start
    # Updating value during iterations
    x_i  = [0,0]
    n_aa = n_acc + 1 # I automatically accept the starting point
    # Current optimum point
    x_c = x_start # Again the starting point is initially accepted
    energy_c = energy(x_i,nn) # Again... that's why I use _i
    energies = np.zeros(n+1)
    energies[0] = energy_c # Again... that's why I use _i
    # Current temperature
    T = T_0
    # k_b equivalent
    dE_avg = 0.0
    for i in range(n):
        for j in range(m):
            # Generating new point
            x_i[0] = x_c[0] + np.random.random() - 0.5
            x_i[1] = x_c[1] + np.random.random() - 0.5
            aux = np.array(x_i)
            # Making sure the new point is in the unit sqare with np.clip()
            x_i[0] = np.clip(aux,0.,1.)[0]
            x_i[1] = np.clip(aux,0.,1.)[1]
            # Change in energy
            dE = abs(energy(x_i,nn)-energy_c)
            if  energy(x_i,nn) < energy_c:
                x_c[0], x_c[1] = x_i[0], x_i[1]
                energy_c = energy(x_i,nn)
                n_acc = n_acc + 1.
                dE_avg = (dE_avg*(n_acc-1) + dE)/n_acc
            else:
                if (i==0) and (j==0):
                    dE_avg = dE
                p = acceptance_p(dE,T,dE_avg)
                if np.random.random() < p:
                    x_c[0], x_c[1] = x_i[0], x_i[1]
                    energy_c = energy(x_i,nn)
                    n_acc = n_acc + 1.
                    dE_avg = (dE_avg*(n_acc-1) + dE)/n_acc
        # Best values after a thermalization
        x[i+1][0]     = x_c[0]
        x[i+1][1]     = x_c[1]
        energies[i+1] = energy_c
        # Reducing the temperature
        T = T_frac * T
    return energies,x

def annealing_plot(points, energies, x_s, nn,chain):
    #opt = 0.5*np.ones(energies.size)
    plt.figure()
    plt.suptitle('x and y starting at ' + x_s + \
    ' for n = ' + str(nn))
    plt.subplot(121)
    plt.ylim(-0.1,1.1)
    plt.hlines(0.5,0,energies.size)
    plt.xlabel('chain length')
    plt.ylabel('x')
    plt.plot(points[:,0],'r.')
    plt.xticks(np.arange(0,110,20))
    #plt.plot(points[:,0],opt, 'r')
    #plt.title("x")
    plt.subplot(122)
    plt.ylim(-0.1,1.1)
    plt.hlines(0.5,0,energies.size)
    plt.ylabel('y')
    plt.xlabel('chain length')
    plt.plot(points[:,1],'b.')
    plt.xticks(np.arange(0,120,20))
    #plt.plot(points[:,1],opt, 'b')
    #plt.title("y")
    plt.savefig('xy_evolution_n_'+ str(nn) + '_chain_' + str(chain) + '.png')
    plt.close()

    plt.figure()
    plt.title("Energy for n = " + str(nn) + ' starting at ' + x_s)
    plt.ylim(-0.1,1.1)
    plt.hlines(1.,0,energies.size)
    plt.xlabel('chain length')
    plt.ylabel('f(x,y)')
    plt.plot(-1.*energies,'b.')
    plt.xticks(np.arange(0,110,10))
    plt.savefig('energy_n_' + str(nn)+ '_chain_' + str(chain) +'.png')
    plt.close()

## Gelman-Runin Statistics

class GR:
    def __init__(self,data):
        self.data = data
        self.length = data.shape[1]

    def mean_var(self):
        # Saving the mean and variance for each chain
        # Using only the last third of data: the idea is to use only data that is 'burned'
        idx = self.data.shape[0]*2//3
        data = self.data[idx:]
        means = np.mean(data, axis=0)
        variances = np.var(data, axis=0)
        return means, variances

    def GelmanRubin(self):
        means, variances = self.mean_var()
        N = self.length
        M = means.shape[0]
        ovl_mean = np.mean(means)
        # Between-chains variances
        B = (means - ovl_mean)**2
        B = (N/(M-1))*B.sum()
        # Between-chains variances
        W = (1/M)*variances.sum()
        # Pooled variance
        V = ((N-1)/N)*W + ((M+1)/(M*N))*B
        # Potential Scale Reduction Factor
        PSRF = V/W
        return PSRF,B, W

# def mean_var(data):
#     # Saving the mean and variance for each chain
#     # Using only the last third of data: the idea is to use only data that is 'burned'
#     idx = data.shape[0]*2//3
#     data = data[idx:]
#     means = np.mean(data, axis=0)
#     variances = np.var(data, axis=0)
#     return means, variances
#
# def GelmanRubin(means, variances,length):
#     N = length
#     M = means.shape[0]
#     ovl_mean = np.mean(means)
#     # Between-chains variances
#     B = (means - ovl_mean)**2
#     B = (N/(M-1))*B.sum()
#     # Between-chains variances
#     W = (1/M)*variances.sum()
#     # Pooled variance
#     V = ((N-1)/N)*W + ((M+1)/(M*N))*B
#     # Potential Scale Reduction Factor
#     PSRF = V/W
#     return PSRF,B, W


import math
from scipy.optimize import fsolve

class Compression():

	def __init__(self, M,theta,gamma=1.4):
		self.M = M
		self.theta = math.radians(theta)
		self.gamma = gamma

	def getWaveAngle(self, M):
		'''
		Returns wave angle (beta).
		Solves equation 9.23 from the book for beta.
		'''
		gamma = self.gamma
		theta = self.theta
		func = lambda beta: ((2*(1/math.tan(beta)) * (M**2 * math.sin(beta)**2 -1))/\
							(M**2*(gamma+math.cos(2*beta)) + 2)) - math.tan(theta)
		beta = fsolve(func, .707)
		beta = min(beta) # weak solution
		return beta

	def getMachNormal(self, M):
		beta = self.getWaveAngle(M)
		M_normal = M*math.sin(beta)
		return M_normal

	def getMach2(self,M,theta,gamma):
		'''
		Returns Mach number after Oblique shock.
		M 		= the initial Mach number before the wedge.
		theta	= the wedge angle from horizontal.
		gamma 	= the ratio of specific heats. (1.4 @ air at sea level conditions)
		'''
		M_normal = self.getMachNormal(M)
		M_normal2 = math.sqrt(\
			(1+((gamma-1)/2)*M_normal**2)/\
			(gamma*M_normal**2 - (gamma-1)/2))

		beta = self.getWaveAngle(M)
		M_2 = M_normal2/(math.sin(beta - theta))
		return M_2

	def getPropertyRatios(self,M,gamma):
		'''
		Returns ratios for density (rho), pressure, and temperature
		as tuple in the respective order.

		Ratios are equivalent to:

		 (final property value)
		-------------------------
		 (initial property value)
		'''
		M_normal = self.getMachNormal(M)

		rho_ratio = (gamma+1)*M_normal**2/\
					(2+(gamma-1)*M_normal**2)
		pressure_ratio = 1 + (2*gamma*(M_normal**2 - 1)/\
						(gamma+1))
		temp_ratio = pressure_ratio/rho_ratio

		return(rho_ratio,pressure_ratio,temp_ratio)

	def getValues(self):
		M_2 = self.getMach2(self.M,self.theta,self.gamma)
		rho_ratio,pressure_ratio,temp_ratio = self.getPropertyRatios(self.M,self.gamma)
		return (M_2,rho_ratio,pressure_ratio,temp_ratio)

class Expansion():

	def __init__(self,M,theta,gamma = 1.4):
		self.M = M
		self.theta = math.radians(theta)
		self.gamma = gamma

	def getPrantlMeyer(self,M,gamma):
		vM = math.sqrt((gamma+1)/(gamma-1))*\
			math.atan(math.sqrt((gamma-1)*(M**2 - 1)/(gamma+1))) \
			- math.atan(math.sqrt(M**2 - 1))
		return vM

	def getInversePrantlMeyer(self,vM):
		'''
		Returns Mach number from from Prantl-Meyer function output v(M).
		Uses the Hall approximation for the inverse of the Prantl-Meyer function:
		http://www.pdas.com/pm.pdf

		The maximum error of Hall approximation for gamma = 1.4 is less than 1%.
		'''
		# Approximation coefficients for gamma  = 1.4:
		A,B,C,D,E = (1.3604,0.0962,-0.5127,-0.6722,-0.3278)
		# Input angle (vM) over the maximum turning angle.
		x = (vM / (math.pi * 0.5 * (math.sqrt(6) - 1)))**(2/3)
		# Hall approximation:
		M = (1 + A*x + B*x**2 + C*x**3) /\
			(1 + D*x + E*x**2)
		return M

	def getMach2(self,M,theta,gamma):
		vM1 = self.getPrantlMeyer(M,gamma)
		vM2 = theta + vM1
		M2 = self.getInversePrantlMeyer(vM2)
		return M2

	def getPropertyRatios(self,M2,M1,gamma):
		'''
		Returns ratios for density (rho), pressure, and temperature
		as tuple in the respective order.

		Ratios are equivalent to:

		 (final property value)
		-------------------------
		 (initial property value)
		'''
		temp_ratio = (1+((gamma-1)/2)*M1**2)/\
					(1+((gamma-1)/2)*M2**2)
		pressure_ratio = ((1+((gamma-1)/2)*M1**2)/\
					(1+((gamma-1)/2)*M2**2))**(gamma/(gamma-1))
		rho_ratio = pressure_ratio/temp_ratio
		return(rho_ratio,pressure_ratio,temp_ratio)

	def getValues(self):
		M_2 = self.getMach2(self.M,self.theta,self.gamma)
		rho_ratio,pressure_ratio,temp_ratio \
            = self.getPropertyRatios(M_2,self.M,self.gamma)
		return (M_2,rho_ratio,pressure_ratio,temp_ratio)

def main():

	# Test compression, should be M2 = 1.21,p2 = 2.82, T2 = 399.7
	M1,p1,T1 = (2.0,1.0,288.0) #Mach number, atm, K
	theta = 20 # degrees
	M2,rho_ratio,pressure_ratio,temp_ratio = Compression(M1,theta).getValues()
	p2 = p1*pressure_ratio
	T2 = T1*temp_ratio

	print('Compression:')
	print(M2,p2,T2)

	# Test expansion, should be M2 = 2.0, p2 = 0.469, T2 = 232
	M1,p1,T1 = (1.5,1.0,288.0) #Mach number, atm, K
	theta = 15 # degrees
	M2,rho_ratio,pressure_ratio,temp_ratio = Expansion(M1,theta).getValues()
	p2 = p1*pressure_ratio
	T2 = T1*temp_ratio

	print('Expansion:')
	print(M2,p2,T2)

if __name__ == '__main__':
	main()


import numpy as np

# ---------------------------------------
# General helper methods such as easily generate a vector or identity matrix

# Get dimension of vector or matrix (assuming matrix is square)
def np_dim(np_obj):
    return np_obj.shape[0]

# Easy syntax for generating 2D or 3D vectors
def vect(x=0, y=0, z=None, scale=1):
    if z is None:
        return np.array((x, y)) * scale
    return np.array((x, y, z)) * scale

# Find length of vector
# Default power = 2 has 'unit circle' as an actual circle, e.g. Euclidean geometry / Pythagoras' theorem
# Other powers give different shapes of unit circle,
# e.g. power = 0.5 gives a 4 pointed star, 1 gives a diamond, 4 for squircle, 10 is nearly a square
def vect_len(vect, power=2):
    return np.sum(abs(np.float_power(abs(vect), power))) ** (1 / power)

# Find a metric for matrix using length of transformation of x-coord (1, 0)
# Currently 2D only
def metric_matrix_x_coord(mx):
    return (mx[0][0] ** 2 + mx[1][0] ** 2) ** 0.5

# Find a metric for matrix using square root of sum of squares of matrix entries
# Identity matrix in 2D or 3D has metric 1
def metric_matrix_rms(mx):
    return np.sum(mx * mx * (1 / np_dim(mx))) ** 0.5

# Construct metric for matrix using absolute values of the eigenvalues
# Identity matrix in 2D or 3D has metric 1
# Metric using minimum
def metric_matrix_min_eig_val(mx):
    return min(abs(np.linalg.eig(mx)[0]))

# Metric using maximum
def metric_matrix_max_eig_val(mx):
    return max(abs(np.linalg.eig(mx)[0]))

# Metric using ratio of max/min
def metric_matrix_ratio_eig_val(mx):
    result = abs(np.linalg.eig(mx)[0])  # Save the intermediate calculation
    return max(result) / min(result)

# Angle calculator for matrices in O(2) (symmetries of a 2D circle)
# Find angle of vect(1, 0) under transformation by mx
# Returns value between -90 and 270
def mx_angle(mx):
    x = mx[0][0]
    y = mx[0][1]
    deg_from_sign = 180 if x < 0 else 0
    deg_from_angle = 90 if y == 0 else np.degrees(np.arctan(x / y))
    return deg_from_sign + deg_from_angle

# Easy syntax for generating matrix identity
# Defaults to 2D identity matrix
def mx_id(dim=2):
    return np.identity(dim)

# Scale matrix, which is mx_id * scale
# Defaults to 2D identity matrix
def mx_scale(scale=1, dim=2):
    return mx_id(dim) * scale

# Diagonal matrix in 2D or 3D
# Defaults to 2D identity matrix
def mx_diag(x=1, y=1, z=None):
    if z is None:
        return np.diag((x, y))
    return np.diag((x, y, z))

# ---------------------------------------
# Generate rotation matrices in 2D or 3D

# 2D clockwise rotation matrix in XY plane, angle in degrees, +1° rotates Y towards X, with optional reflection
def mx_rotd(angle=0, scale=1, reflect=False):
    angle_in_radians = np.radians(angle)
    c, s = np.cos(angle_in_radians), np.sin(angle_in_radians)
    # TODO: document which plane this is reflecting in
    r = 1
    if reflect:
        r = -1
    return np.array(((c, s), (-s * r, c * r))) * scale

# 3D clockwise rotation matrix in XY plane, angle in degrees, +1° rotates Y towards X
def mx_rotd_XY(angle=0, scale=1):
    angle_in_radians = np.radians(angle)
    c, s = np.cos(angle_in_radians), np.sin(angle_in_radians)
    return np.array(((c, s, 0), (-s, c, 0), (0, 0, 1))) * scale

# 3D clockwise rotation matrix in XZ plane, angle in degrees, +1° rotates Z towards X
def mx_rotd_XZ(angle=0, scale=1):
    angle_in_radians = np.radians(angle)
    c, s = np.cos(angle_in_radians), np.sin(angle_in_radians)
    return np.array(((c, 0, s), (0, 1, 0), (-s, 0, c))) * scale

# 3D clockwise rotation matrix in YZ plane, angle in degrees, +1° rotates Z towards Y
def mx_rotd_YZ(angle=0, scale=1):
    angle_in_radians = np.radians(angle)
    c, s = np.cos(angle_in_radians), np.sin(angle_in_radians)
    return np.array(((1, 0, 0), (0, c, s), (0, -s, c))) * scale

# Utility method to do an XY rotation, then a XZ rotation, then an optional scale
# Applying a matrix transformation is _pre_multiplication, so XZ goes first.
# Scaling is commutative, so can go anywhere.
# Angles are in degrees
def mx_rotd_3D(ang_xy=0, ang_xz=0, scale=1):
    return mx_rotd_XZ(ang_xz) @ mx_rotd_XY(ang_xy, scale)

# ---------------------------------------
# Generate reflection matrices in 2D or 3D

# Reflection matrix in x-axis
def mx_refl_X(dim=2, scale=1):
    if dim == 3:
        return np.array(((-1, 0, 0), (0, 1, 0), (0, 0, 1))) * scale
    return np.array(((-1, 0), (0, 1))) * scale

# Reflection matrix in y-axis
def mx_refl_Y(dim=2, scale=1):
    if dim == 3:
        return np.array(((1, 0, 0), (0, -1, 0), (0, 0, 1))) * scale
    return np.array(((1, 0), (0, -1))) * scale

# Reflection matrix in z-axis
def mx_refl_Z(scale=1):
    return np.array(((1, 0, 0), (0, 1, 0), (0, 0, -1))) * scale

# ---------------------------------------
# Discrete symmetry groups

# Dihedral group of regular polygon 2, 3, 4, 5, 6...
# (rectangle, triangle, square, pentagon, hexagon...)
# where bottom edge is horizontal (so horizontal reflection)
# dh(5, 1) for identity
# dh(5, 2) for 72 degree rotation anticlockwise
# dh(5, 6) for horizontal reflection
# dh(8, 6) for 225 (45 * 5) degree rotation anticlockwise
def mx_dh(sides=3, num=1, scale=1):
    if not isinstance(sides, int):
        raise TypeError("Number of sides must be an integer")
    if sides < 2:
        raise TypeError("Number of sides must be at least 2")
    if not isinstance(num, int):
        raise TypeError("Transformation number must be an integer")
    if num < 1:
        raise TypeError("Transformation number must be at least 1")
    if num > sides * 2:
        raise TypeError(
            f"Transformation number must be at most {sides * 2}")
    rotation_num = (num - 1) % sides  # 0, 1, 2, ..., sides - 1
    reflection_num = ((num - 1) - rotation_num) / sides  # 0, 1
    rotation_mx = mx_rotd((-360 / sides) * rotation_num)
    refl_x_mx = np.array((((-1) ** reflection_num, 0), (0, 1)))
    result = rotation_mx @ refl_x_mx
    return result * scale

# Square transformations 1 to 8
# sq(1) for identity
# sq(2) for 90 degree rotation anticlockwise
# sq(5) for *vertical* reflection (should have been horizontal... but already published on Nifty Ink)
def mx_sq(num=1, scale=1):
    if not isinstance(num, int):
        raise TypeError("Argument is not integer")
    if num < 1:
        raise TypeError("Argument must be at least 1")
    if num > 8:
        raise TypeError("Argument must be at most 8")
    rotation_num = (num - 1) % 4  # 0, 1, 2, 3
    reflection_num = ((num - 1) - rotation_num) / 4  # 0, 1
    rotation_mx = mx_rotd((-90) * rotation_num)
    refl_y_mx = np.array(((1, 0), (0, (-1) ** reflection_num)))
    result = rotation_mx @ refl_y_mx
    return result * scale


import numpy as np

def cartesian2spherical(cartesian):
    """Convert cartesian to spherical coordinates

    Args:
        cartesian (ndarray): Vector (x, y, z)

    Returns:
        spherical (ndarray): Vector (distance, azimuth, elevation)
    """
    cartesian = np.array(cartesian).squeeze()
    x, y, z = cartesian
    distance = np.linalg.norm(cartesian)
    azimuth = np.arccos(z / distance)
    elevation = np.arctan2(y, x)  # Use arctan2 instead of arctan to get proper sign!
    return np.array([distance, azimuth, elevation])

def spherical2cartesian(spherical):
    """Convert spherical to cartesian coordinates

    Args:
        spherical (ndarray): Vector (distance, azimuth, elevation)

    Returns:
        cartesian (ndarray): Vector (x, y, z)
    """
    spherical = np.array(spherical).squeeze()
    distance, azimuth, elevation = spherical
    x = distance * np.sin(azimuth) * np.cos(elevation)
    y = distance * np.sin(azimuth) * np.sin(elevation)
    z = distance * np.cos(azimuth)
    return np.array([x, y, z])

def cartesian2polar(cartesian):
    """Converts cartesian to polar coordinates

    Args:
        cartesian (ndarray): Vector (x, y)

    Returns:
        polar (ndarray): Vector (range, azimuth)
    """
    cartesian = np.array(cartesian).squeeze()
    x, y = cartesian
    r = np.linalg.norm([x, y])
    azimuth = np.arctan2(y, x)
    return np.array([r, azimuth])

def polar2cartesian(polar):
    """Converts polar to cartesian coordinates

    Args:
        polar (ndarray): Vector (range, azimuth)

    Returns:
        cartesian (ndarray): Vector (x, y)
    """
    polar = np.array(polar).squeeze()
    r, azimuth = polar
    x = r * np.cos(azimuth)
    y = r * np.sin(azimuth)
    return np.array([x, y])

def random_uniform_within_circle():
    """Sample random point within a unit circle

    Returns:
        sample (ndarray): Vector (x, y)

    """
    rho = np.sqrt(np.random.uniform(0, 1))
    phi = np.random.uniform(0, 2 * np.pi)
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)
    return np.array([x, y])


import numpy as np

# Gradient descent based linear regression from scratch
def compute_prediction(X, weights):
    """ Compute the prediction y_hat based on current weights
    Args:
        X (numpy.ndarray)
        weights (numpy.ndarray)
    Returns:
        numpy.ndarray, y_hat of X under weights
    """
    predictions = np.dot(X, weights)
    return predictions

def update_weights_gd(X_train, y_train, weights, learning_rate):
    """ Update weights by one step
    Args:
        X_train, y_train (numpy.ndarray, training data set)
        weights (numpy.ndarray)
        learning_rate (float)
    Returns:
        numpy.ndarray, updated weights
    """
    predictions = compute_prediction(X_train, weights)
    weights_delta = np.dot(X_train.T, y_train - predictions)
    m = y_train.shape[0]
    weights += learning_rate / float(m) * weights_delta
    return weights

def compute_cost(X, y, weights):
    """ Compute the cost J(w)
    Args:
        X, y (numpy.ndarray, data set)
        weights (numpy.ndarray)
    Returns:
        float
    """
    predictions = compute_prediction(X, weights)
    cost = np.mean((predictions - y) ** 2 / 2.0)
    return cost

def train_linear_regression(X_train, y_train, max_iter, learning_rate, fit_intercept=False):
    """ Train a linear regression model with gradient descent
    Args:
        X_train, y_train (numpy.ndarray, training data set)
        max_iter (int, number of iterations)
        learning_rate (float)
        fit_intercept (bool, with an intercept w0 or not)
    Returns:
        numpy.ndarray, learned weights
    """
    if fit_intercept:
        intercept = np.ones((X_train.shape[0], 1))
        X_train = np.hstack((intercept, X_train))
    weights = np.zeros(X_train.shape[1])
    for iteration in range(max_iter):
        weights = update_weights_gd(X_train, y_train, weights, learning_rate)
        # Check the cost for every 100 (for example) iterations
        if iteration % 100 == 0:
            print(compute_cost(X_train, y_train, weights))
    return weights

def predict(X, weights):
    if X.shape[1] == weights.shape[0] - 1:
        intercept = np.ones((X.shape[0], 1))
        X = np.hstack((intercept, X))
    return compute_prediction(X, weights)

# A small example
X_train = np.array([[6], [2], [3], [4], [1], [5], [2], [6], [4], [7]])

y_train = np.array([5.5, 1.6, 2.2, 3.7, 0.8, 5.2, 1.5, 5.3, 4.4, 6.8])

weights = train_linear_regression(X_train, y_train, max_iter=100, learning_rate=0.01, fit_intercept=True)

X_test = np.array([[1.3], [3.5], [5.2], [2.8]])

predictions = predict(X_test, weights)

import matplotlib.pyplot as plt
plt.scatter(X_train[:, 0], y_train, marker='o', c='b')
plt.scatter(X_test[:, 0], predictions, marker='*', c='k')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

# The diabetes example
from sklearn import datasets
diabetes = datasets.load_diabetes()
print(diabetes.data.shape)

num_test = 30    # the last 30 samples as testing set
X_train = diabetes.data[:-num_test, :]
y_train = diabetes.target[:-num_test]

weights = train_linear_regression(X_train, y_train, max_iter=5000, learning_rate=1, fit_intercept=True)

X_test = diabetes.data[-num_test:, :]
y_test = diabetes.target[-num_test:]

predictions = predict(X_test, weights)

print(predictions)
print(y_test)

# Directly use SGDRegressor from scikit-learn
from sklearn.linear_model import SGDRegressor
regressor = SGDRegressor(loss='squared_loss', penalty='l2', alpha=0.0001, learning_rate='constant', eta0=0.01, n_iter=1000)
regressor.fit(X_train, y_train)
predictions = regressor.predict(X_test)
print(predictions)
print(regressor.score(X_test, y_test))

# Measuring model performance after hyperparameter tuning with grid search
diabetes = datasets.load_diabetes()
num_test = 30    # the last 30 samples as testing set
X_train = diabetes.data[:-num_test, :]
y_train = diabetes.target[:-num_test]
X_test = diabetes.data[-num_test:, :]
y_test = diabetes.target[-num_test:]

param_grid = {
    "alpha": [1e-07, 1e-06, 1e-05],
    "penalty": [None, "l2"],
    "eta0": [0.001, 0.005, 0.01],
    "n_iter": [300, 1000, 3000]
}

from sklearn.model_selection import GridSearchCV
regressor = SGDRegressor(loss='squared_loss', learning_rate='constant')
grid_search = GridSearchCV(regressor, param_grid, cv=3)
grid_search.fit(X_train, y_train)

print(grid_search.best_params_)
regressor_best = grid_search.best_estimator_
# regressor_best.score(X_test, y_test)

predictions = regressor_best.predict(X_test)

from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
mean_squared_error(y_test, predictions)
mean_absolute_error(y_test, predictions)
r2_score(y_test, predictions)


import numpy as np

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

def softmax(z):
    e = np.exp(z - np.amax(z, axis=1, keepdims=True))
    return e / np.sum(e, axis=1, keepdims=True)

def relu(x):
    return np.maximum(x, 0)

def leakyrelu(x, a=0.01):
    return np.maximum(x * a, x)


import numpy as np
from scipy.linalg import solve
def gaussseidel(A, B):
    row, col = np.shape(A)
    if row == col:
        n = 10000
        x = B/(np.diagonal(A))
        inbuilt = solve(A,B)
        for i in range(1, n):
            x_new = np.zeros_like(x)
            print(x)
            for i in range(row):
                x_new[i] = (B[i] - np.dot(A[i, :i], x_new[:i]) - np.dot(A[i, i + 1 :], x[i + 1 :])) / A[i, i]
            if np.allclose(x, x_new, rtol=1e-8):
                break
            else:
                x = x_new
        print("Solution: ",x)
        error = np.dot(A, x) - B
        print("Inbuilt Solution",solve(A,B))
        print("Error: ",error)
    else:
        print("Matrix is not square matrix")

if __name__ == "__main__":
    A = np.array([[8, 3, -3], [-2, -8, 5], [3, 5, 10]])
    # initialize the RHS vector
    B = np.array([14,5,-8])
    # Find diagonal coefficients
    diag = np.diag(np.abs(A))
    # Find row sum without diagonal
    off_diag = np.sum(np.abs(A), axis=1) - diag
    if np.all(diag > off_diag):
        print('matrix is diagonally dominant')
    else:
        print('NOT diagonally dominant')
    gaussseidel(A,B)


'''
Statistical Inference Through Python Codes
'''
import os
os.chdir("MyPath/Python/Chapter_05")
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import binom,poisson,norm,t
import scipy.stats as st

# Binomial Likelihood
n = 10; x = 7
p_seq = np.arange(0,1.01,0.01)
lik_p = binom.pmf(x,n,p_seq)
plt.subplot(1,3,1)
plt.plot(p_seq,lik_p)
plt.xlabel("p")
plt.title("Binomial Likelihood Function")
# Poisson Likelihood
xp = [1,2,2,1,0,2,3,1,2,4]
lam_seq = np.arange(0,5.1,0.1)
plt.subplot(1,3,2)
plt.plot(lam_seq,poisson.pmf(sum(xp),len(xp)*lam_seq))
plt.xlabel('$\lambda$')
plt.title("Poisson Likelihood Function")
# Normal likelihood
n = 25
nx = np.random.normal(10,2,n)
mu_seq = np.arange(9,11.05,0.05)
plt.subplot(1,3,3)
plt.plot(mu_seq,norm(np.mean(nx),mu_seq,2))
plt.xlabel('$\mu$')
plt.title("Normal Likelihood Function")
plt.show()

# Confidence Intervals

def binom_CI(x,n,alpha):
	phat = x/n
	ll=phat-norm.ppf(1-alpha/2)*np.sqrt(phat*(1-phat)/n)
	ul=phat+norm.ppf(1-alpha/2)*np.sqrt(phat*(1-phat)/n)
	print("Binomial Lower Confidence Limit: ",max(0,ll))
	print("Binomial Upper Confidence Limit: ",min(1,ul))
binom_CI(x=7,n=10,alpha=0.01)

def normal_CI_ksd(x,sigma,alpha):
	xbar = np.mean(x)
	n = len(x)
	ll = xbar - norm.ppf(1-alpha/2)*sigma/np.sqrt(n)
	ul = xbar + norm.ppf(1-alpha/2)*sigma/np.sqrt(n)
	print("Normal Lower Confidence Limit: ",ll)
	print("Normal Upper Confidence Limit: ",ul)
n=25
nx = np.random.normal(10,2,n)
normal_CI_ksd(nx,2,0.05)

def normal_CI_uksd(x,alpha):
	xbar = np.mean(x); n = len(x); xsd = np.std(x)*np.sqrt(n/(n-1))
	ll = xbar - t.ppf(1-alpha/2,n-1)*xsd/np.sqrt(n)
	ul = xbar + t.ppf(1-alpha/2,n-1)*xsd/np.sqrt(n)
	print("Normal Lower Confidence Limit: ",ll)
	print("Normal Upper Confidence Limit: ",ul)
nx = np.random.normal(10,2,n)
normal_CI_uksd(nx,0.05)

'''
Hypothesis Testing in Python
'''
# Binomial Test
n_lcd = 893; x_lcd = 39; p_lcd = 0.04
from scipy.stats import binom_test
binom_test(x=x_lcd,n=n_lcd,p=p_lcd)
# Proportion Test
Male_Admit = [512, 353, 120, 138,  53,  22]
Male_Applied = [825, 560, 325, 417, 191, 373]
Female_Prop = np.array([0.824, 0.680, 0.341, 0.349, 0.239, 0.070])
from statsmodels.stats.proportion import proportions_chisquare
proportions_chisquare(count = Male_Admit, nobs=Male_Applied,value=Female_Prop)

# Normal test: One sample Z-test
def z_test(x,mu,sd,alternative):
	import scipy
	xmean = x.mean(); n = len(x)
	ztest = np.sqrt(n)*(xmean-mu)/sd
	if alternative=="less":
		pvalue = scipy.stats.norm.cdf(ztest)
	if alternative=="greater":
		pvalue = 1-scipy.stats.norm.cdf(ztest)
	if alternative=="two.sided":
		pvalue = 2*(1-scipy.stats.norm.cdf(ztest))
	print("The Z-test Value is :",ztest)
	print("The p-value is :",pvalue)

pH_Data = np.array([8.30,8.42,8.44,8.32,8.43,8.41,8.42,8.46,8.37,8.42])
z_test(x=pH_Data,mu=8.4,sd=0.05,alternative="less")
LCD_Data = np.array([13.37, 10.96, 12.06, 13.82, 12.96, 10.47,10.55,
16.28, 12.94, 11.43, 14.51, 12.63, 13.50, 11.50, 12.87])
z_test(x=LCD_Data,mu =12, sd =2,alternative="greater")
peanuts = np.array([8.08, 7.71, 7.89, 7.72, 8.00, 7.90, 7.77, 7.81,
8.33, 7.67, 7.79, 7.79, 7.94, 7.84, 8.17, 7.87])
z_test(x=peanuts,mu=8,sd=0.03,alternative="two.sided")

# Normal test: Two sample Z-test
def z_test_two_samples(x1,x2,s1,s2,alternative,delta):
	import scipy
	x1mean = x1.mean(); n1 = len(x1)
	x2mean = x2.mean(); n2 = len(x2)
	pooled_var = np.sqrt(s1**2/n1+s2**2/n2)
	ztest = (x1mean-x2mean-delta)/pooled_var
	if alternative=="less":
		pvalue = scipy.stats.norm.cdf(ztest)
	if alternative=="greater":
		pvalue = 1-scipy.stats.norm.cdf(ztest)
	if alternative=="two.sided":
		pvalue = 2*(1-scipy.stats.norm.cdf(ztest))
	print("The Z-test Value is :",ztest)
	print("The p-value is :",pvalue)
	print("The pooled variance is:",pooled_var)

pH_Data = np.array([8.30, 8.42, 8.44, 8.32, 8.43, 8.41, 8.42,  8.46, 8.37, 8.42])
pH_New = np.array([8.78, 8.85, 8.74, 8.83, 8.82, 8.79, 8.82, 8.74, 8.84, 8.78, 8.75, 8.81])
z_test_two_samples(x1=pH_Data,x2=pH_New,s1=0.05,s2=0.05,alternative="less",delta=0)

length_M1 = np.array([122.4, 123.12, 122.51, 123.12, 122.55,
               121.76, 122.31, 123.2, 122.48, 121.96])
length_M2 = np.array([122.36, 121.88, 122.2, 122.88, 123.43,
               122.4, 122.12, 121.78, 122.85, 123.04])
z_test_two_samples(x1=length_M1,x2=length_M2,s1=0.5,s2=0.5,alternative="two.sided",delta=0)

import numpy,import scipy,from scipy,from statsmodels