"""
# T: maturity
# n: # option periods
# N: # futures periods
# S: initial stock price
# r: continuously-compounded interest rate
# c: dividend yield
# sigma: annualized volatility
# K: strike price
# cp: +1/-1 with regards to call/put
"""

from __future__ import division
from math import exp, sqrt
import numpy as np
import math

T = 0.25
n = 15 # option periods
N = 15 # futures periods
S = 100 #initial stock price
r = 0.02 #continuously-compounded interest rate
c = 0.01 #dividend yield
sigma = 0.3 #annualized volatility
K = 110 #strike price
cp = -1 #with regards to call/put

def Parameter(T,n,sigma,r,c):

    """Parameter calculation"""

    dt = T/n
    u = exp(sigma * sqrt(dt))
    d = 1/u

    q1 = (exp((r-c)*dt)-d)/(u-d)
    q2 = 1-q1
    R = exp(r*dt)

    return (u, d, q1, q2, R)

# =============================================================================

def GenerateTree(T,n,S,sigma,r,c):
    """generate stock tree"""
    u, d, q1, q2, R = Parameter(T,n,sigma,r,c)

    stockTree = np.zeros((n+1, n+1))

    # compute the stock tree
    stockTree[0,0] = S
    for i in range(1,n+1):
        stockTree[0,i] = stockTree[0, i-1]*u
        for j in range(1,n+1):
            stockTree[j,i] = stockTree[j-1, i-1]*d

    return stockTree

# =============================================================================

def StockOptionAM(T,n,S,r,c,sigma,K,cp):
    """first return: American Stock Option Pricing"""
    """second return: when is the earliest time to exercise"""
    """Though it's never optimal to early exercise AM call"""
    """It matters for AM put"""

    u, d, q1, q2, R = Parameter(T,n,sigma,r,c)

    stockTree = GenerateTree(T,n,S,sigma,r,c)
    optionTree = np.zeros((n+1,n+1))

    # compute the option tree
    for j in range(n+1):
        optionTree[j, n] = max(0, cp * (stockTree[j, n]-K))

    flag = 0
    list = []
    for i in range(n-1,-1,-1):
        for j in range(i+1):
            optionTree[j, i] = max((q1 * optionTree[j, i+1] + q2 * optionTree[j+1, i+1])/R,
                               cp * (stockTree[j, i] - K))
            if (optionTree[j, i] - cp * (stockTree[j, i] - K)) < 1e-10:
                flag += 1
                list.append(i)

    when = n
    if(flag):  when = list[-1]

    print(optionTree, when)
    return (optionTree[0,0], when)

z = StockOptionAM(T,n,S,r,c,sigma,K,cp)

option_maturity = 10

class bs_bin_tree:

    def __init__(self,T,s0,r,sigma,c,K,n):
        self.T = T
        self.r = r
        self.c = c
        self.sigma = sigma
        self.K = K
        self.s0 = s0
        self.n = n
        self.u = math.exp(self.sigma*np.sqrt(self.T/self.n))
        self.q = (math.exp((self.r-self.c)*T/self.n)-(1/self.u))/(self.u-(1/self.u))
        self.R = math.exp(self.r*self.T/self.n)
        self.__print_param__()

    def __print_param__(self):
        print('Time',self.T)
        print('Starting Price',self.s0)
        print('r',self.r)
        print('volatility',self.sigma)
        print('dividend yield',self.c)
        print('strike',self.K)
        print('# period',self.n)

    def generate_price(self):
        arr=[[self.s0]]
        for i in range(self.n):
            arr_to_add=[]
            for j in range(len(arr[i])):
                arr_to_add.append(arr[i][j]/self.u)
                if j == (len(arr[i])-1):
                    arr_to_add.append(arr[i][j]*self.u)
            arr.append(arr_to_add)
        return arr

    def neutral_pricing(self,p1,p2):
        price = ((1-self.q)*p1 + (self.q)*p2)/self.R
        return price

    def eu_put(self):
        arr = self.generate_price()
        arr_rev = arr[::-1]
        res=[]
        for i in range(len(arr_rev)):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    a = max(self.K-arr_rev[i][j],0)
                    res_to_add.append(a)
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])
                    #a = max(arr_rev[i][j]-strike,0)
                    #a = max(a,price)
                    a = price
                    res_to_add.append(a)

            res.append(res_to_add)
        return res[::-1]

    def eu_call(self):
        arr = self.generate_price()
        arr_rev = arr[::-1]
        res=[]
        for i in range(len(arr_rev)):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    a = max(arr_rev[i][j]-self.K,0)
                    res_to_add.append(a)
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])
                    #a = max(arr_rev[i][j]-strike,0)
                    #a = max(a,price)
                    a = price
                    res_to_add.append(a)

            res.append(res_to_add)
        return res[::-1]

    def us_call(self):
        arr = self.generate_price()
        arr_rev = arr[::-1]
        res=[]
        for i in range(len(arr_rev)):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    a = max(arr_rev[i][j]-self.K,0)
                    res_to_add.append(a)
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])
                    a1 = max(arr_rev[i][j]-self.K,0)
                    a = max(a1,price)
                    res_to_add.append(a)

            res.append(res_to_add)
        return res[::-1]

    def us_call_price(self):
        return self.us_call()[0][0]

    def us_put(self):
        arr = self.generate_price()
        arr_rev = arr[::-1]
        res=[]
        for i in range(len(arr_rev)):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    a = max(self.K-arr_rev[i][j],0)
                    res_to_add.append(a)
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])
                    a1 = max(self.K - arr_rev[i][j],0)
                    a = max(a1,price)
                    res_to_add.append(a)

            res.append(res_to_add)
        return res[::-1]

    def us_put_price(self):
        return self.us_put()[0][0]

    def us_put_early_ex(self):
        early_ex = False
        early_ex_earning = 0
        early_ex_time = self.n
        arr = self.generate_price()
        arr_rev = arr[::-1]
        res=[]
        for i in range(len(arr_rev)):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    a = max(self.K-arr_rev[i][j],0)
                    res_to_add.append(a)
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])
                    a1 = max(self.K-arr_rev[i][j],0)
                    if a1 > price:
                        if early_ex_time == self.n - i:
                            early_ex_earning = max(early_ex_earning,a1)
                        else:
                            early_ex_earning = a1
                        early_ex =True
                        early_ex_time = self.n - i

                    a = max(a1,price)
                    res_to_add.append(a)

            res.append(res_to_add)
        return {early_ex_time:early_ex_earning} if early_ex == True else False

    def us_put_call_parity(self):
        LHS = self.us_put_price() + self.s0 * math.exp(-self.c * self.T)
        RHS = self.us_call_price() + self.K * math.exp(-self.r * self.T)
        print('Put Side',LHS)
        print('Call Side',RHS)
        return LHS==RHS

    def generate_future_price(self):
        arr = self.generate_price()
        arr_rev = arr[::-1]
        res=[]
        for i in range(len(arr_rev)):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    res_to_add.append(arr_rev[i][j])
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])*self.R
                    res_to_add.append(price)

            res.append(res_to_add)
        return res[::-1]

    def option_on_future(self,option_maturity):
        arr = self.generate_future_price()[0:option_maturity+1]
        arr_rev = arr[::-1]
        res=[]
        for i in range(option_maturity+1):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    a = max(arr_rev[i][j]-self.K,0)
                    res_to_add.append(a)
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])
                    a1 = max(arr_rev[i][j]-self.K,0)
                    a = max(a1,price)
                    res_to_add.append(a)

            res.append(res_to_add)
        return res[::-1]

    def option_price_on_future(self,option_maturity):
        return self.option_on_future(option_maturity)[0][0]

    def option_on_future_early_ex(self,option_maturity):
        arr = self.generate_future_price()[0:option_maturity+1]
        arr_rev = arr[::-1]
        res=[]
        early_ex = False
        early_ex_earning = 0
        early_ex_time = self.n
        for i in range(option_maturity+1):
            res_to_add = []
            for j in range(len(arr_rev[i])):
                if i == 0:
                    a = max(arr_rev[i][j]-self.K,0)
                    res_to_add.append(a)
                else:
                    price = self.neutral_pricing(res[i-1][j], res[i-1][j+1])
                    a1 = max(arr_rev[i][j]-self.K,0)
                    if a1 > price:
                        if early_ex_time == option_maturity - i:
                            early_ex_earning = max(early_ex_earning,a1)
                        else:
                            early_ex_earning = a1
                        early_ex =True
                        early_ex_time =  len(arr_rev) - i -1

                    a = max(a1,price)
                    res_to_add.append(a)

            res.append(res_to_add)
        return {early_ex_time:early_ex_earning} if early_ex == True else False

    def nCr(self,n,r):
        f = math.factorial
        return f(n) / f(r) / f(n-r)

    def chooser_option_price(self,option_expire):
        call = self.eu_call()[option_expire]
        put = self.eu_put()[option_expire]
        res=[]
        for i in range(len(call)):
            res.append(max(call[i],put[i]))
        result=0
        for j in range(0,len(res)):
            result += self.nCr(option_expire,j)* (self.q**(j)) * (1-self.q)**(option_expire-j) * res[j]
        return (result/self.R**(option_expire))

tree = bs_bin_tree(T, 100, r, sigma, c, K, n)
print(tree.us_call())
print(tree.us_call_price())
print(tree.us_put())
print(tree.us_put_price())
print(tree.us_put_early_ex())
print(tree.us_put_call_parity())
print(tree.option_on_future(option_maturity))
print(tree.option_price_on_future(option_maturity))
print(tree.option_on_future_early_ex(option_maturity))
print(tree.chooser_option_price(10))


# -*- coding: utf-8 -*-
"""
Newton fractal for the equation

    x**3 + 1

Currently, it is quite slow since it is using Python loops instead
of vectorization of the operations.

@author: Nicolás Guarín-Zapata
"""

import numpy as np
import matplotlib.pyplot as plt

def newt(x, fun, der, tol=1e-5, niter=100):
    """ Find a root using Newton Method"""
    x0 = x
    for k in range(niter):
        x = x0 - fun(x0)/der(x0)
        err = np.abs((x - x0)/x)
        if err < tol:
            break
        if k == niter - 1:
            x = 1000
        x0 = x
    return x

def img_newt(N, xran=(-3, 3), yran=(-3, 3), tol=1e-5, niter=100):
    """
    Add colors to a matrix according to the fixed point
    of the given equation.
    """
    sol = [-(np.sqrt(3.0)*1j - 1.0)/2.0,
           (np.sqrt(3.0)*1j + 1.0)/2.0,
          -1.0]
    col_newt = np.zeros((N, N, 3))
    Y, X = np.mgrid[yran[0]:yran[1]:N*1j,
                    xran[0]:xran[1]:N*1j]
    for row in range(N):
        for col in range(N):
            x = X[row, col]
            y = Y[row, col]
            xf = newt(x + y*1j, fun, der, tol=tol, niter=niter)
            if abs(xf - sol[0])<1e-6:
                col_newt[row, col, :] = colors[0]
            if abs(xf - sol[1])<1e-6:
                col_newt[row, col, :] = colors[1]
            if abs(xf - sol[2])<1e-6:
                col_newt[row, col, :] = colors[2]
            if abs(xf - 1000) < 1e-6:
                col_newt[row, col, :] = colors[3]
    return col_newt

#%% Computation
colors = [[0.0431, 0.4078, 0.6588],
          [0.1176, 0.6431, 0.2314],
          [0.6745, 0.1216, 0.2431],
          [0.2, 0.2, 0.2]]
fun = lambda x: x*x*x + 1.0
der = lambda x: 3.0*x*x
col_newt = img_newt(2000, tol=1e-10, niter=1000)

#%% Visualization
plt.figure(figsize=(4,4))
plt.imshow(col_newt, extent=(-3, 3, -3, 3), origin='lower')
plt.axis('off')
plt.savefig('newton_fractal.png', dpi=500, transparent=True,
            bbox_inches='tight', pad_inches=0)
plt.show()


# metrics

import numpy as np
import pandas as pd

# metrics

def mae(targets, predictions) -> float:
    """
    Calculo de la metrica: mean absolute error (MAE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """
    error = predictions - targets
    return round(np.abs(error).mean(), 4)

def mse(targets, predictions) -> float:
    """
    Calculo de la metrica: mean squared error (MSE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """
    error = predictions - targets
    return round((error ** 2).mean(), 4)

def rmse(targets, predictions) -> float:
    """
    Calculo de la metrica: root mean squared error (RMSE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """
    error = predictions - targets
    return round(np.sqrt((error ** 2).mean()), 4)

# b) Percentage errors

def mape(targets, predictions) -> float:
    """
    Calculo de la metrica: mean absolute percentage error (MAPE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """
    error = predictions - targets

    if any(x == 0 for x in targets):
        return np.inf
    else:
        return round(np.abs(error / targets).mean(),4)

def maape(targets, predictions) -> float:
    """
    Calculo de la metrica:  mean arctangent percentage error  (MAAPE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """

    error = predictions - targets

    if any((x, y) == (0, 0) for x, y in zip(predictions, targets)):
        return np.inf

    else:
        return round(np.arctan(np.abs(error / targets)).mean(),4)

def wmape(targets, predictions) -> float:
    """
    Calculo de la metrica:  weighted mean absolute percentage error (WMAPE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """
    error = predictions - targets
    sum_values = np.sum(targets)

    if sum_values == 0:
        return np.inf
    else:
        return round(np.abs(error).sum() / sum_values,4)

def mmape(targets, predictions) -> float:
    """
    Calculo de la metrica: modified mean absolute percentage error (MMAPE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """
    error = np.abs(predictions - targets)
    denom = 1 + np.abs(targets)

    return round(np.mean(error / denom),4)

def smape(targets, predictions) -> float:
    """
    Calculo de la metrica: symmetric mean absolute percentage error (SMAPE)
    :param targets: valor real
    :param predictions: valor estimado
    :return: valor de la metrica
    """
    error = predictions - targets
    sum_values = np.abs(predictions) + np.abs(targets)

    if any(x == 0 for x in sum_values):
        return np.inf

    else:
        return round(2 * np.mean(np.abs(error) / sum_values),4)

def summary_metrics(df) -> pd.DataFrame:
    """
    Aplicar las distintas metricas definidas
    :param df:  dataframe con las columnas: ['y','yhat']
    :return: dataframe con las metricas especificadas
    """
    df_result = pd.DataFrame()

    y_true = df['y']
    y_pred = df['yhat']

    df_result['mae'] = [round(mae(y_true, y_pred), 4)]
    df_result['mse'] = [round(mse(y_true, y_pred), 4)]
    df_result['rmse'] = [round(rmse(y_true, y_pred), 4)]

    df_result['mape'] = [round(mape(y_true, y_pred), 4)]
    df_result['maape'] = [round(maape(y_true, y_pred), 4)]
    df_result['wmape'] = [round(wmape(y_true, y_pred), 4)]
    df_result['mmape'] = [round(mmape(y_true, y_pred), 4)]
    df_result['smape'] = [round(smape(y_true, y_pred), 4)]

    # nombre del modelo por defecto
    #try:
    #    model = df['model'][0]
    #    df_result['model'] = [model]
    #except:
    #    df_result['model'] = ['---']

    return df_result


import numpy as np
from source.week2.ex2_1 import construct_element_table, xy
from source.week2.ex2_3 import assembly
from source.week2.ex2_4 import boundary_conditions

def u_hat(X, Y, L1, L2, x0, y0, M, A, b, test_case=None,exercise=None):
    # u_hat --> Au=b
    A, b = boundary_conditions(X, Y, L1=L1, L2=L2, x0=x0, y0=y0, M=M, A=A, b=b, test_case=test_case, exercise=exercise)
    uhat = np.linalg.solve(A, b)
    return uhat

def u(x, y, test_case=None):
    if test_case == 1:
        return x ** 3 - x ** 2 * y + y ** 2 - 1
    if test_case == 2:
        return x ** 2 * y ** 2

def construct_qt(etov_dict, VX, VY, test_case=None):
    """
    Computes points as means of each elements' vertices.
    """
    qt_dict = dict()
    for e, (v1, v2, v3) in etov_dict.items():
        if test_case == 1:
            x1, x2, x3 = VX[v1 - 1], VX[v2 - 1], VX[v3 - 1]
            y1, y2, y3 = VY[v1 - 1], VY[v2 - 1], VY[v3 - 1]

            def f(x, y):
                return -6 * x + 2 * y - 2

            q1 = f(x1, y1)
            q2 = f(x2, y2)
            q3 = f(x3, y3)

            q_avg = np.mean([q1, q2, q3])
            qt_dict[e] = q_avg
        elif test_case == 2:
            x1, x2, x3 = VX[v1 - 1], VX[v2 - 1], VX[v3 - 1]
            y1, y2, y3 = VY[v1 - 1], VY[v2 - 1], VY[v3 - 1]

            def f(x, y):
                return -2 * y ** 2 - 2 * x ** 2

            q1 = f(x1, y1)
            q2 = f(x2, y2)
            q3 = f(x3, y3)

            q_avg = np.mean([q1, q2, q3])
            qt_dict[e] = q_avg
        else:
            raise Exception("Unknown test case")
    return qt_dict

def test_case_data(nr_of_test_case):
    if nr_of_test_case == 1:
        L1 = 7.6
        L2 = 5.9
        x0 = -2.5
        y0 = -4.8
        X, Y = xy(x0=x0, y0=y0, L1=L1, L2=L2, noelms1=4, noelms2=3)
        etov_dict, M = construct_element_table(4, 3)
        lam1 = 1
        lam2 = 1
        qt = construct_qt(etov_dict, X, Y, test_case=1)
        return nr_of_test_case, X, Y, L1, L2, x0, y0, etov_dict, M, lam1, lam2, qt

    elif nr_of_test_case == 2:
        L1 = 7.6
        L2 = 5.9
        x0 = -2.5
        y0 = -4.8
        p = 3
        noelms = 2 ** p
        X, Y = xy(x0=x0, y0=y0, L1=L1, L2=L2, noelms1=noelms, noelms2=noelms)
        etov_dict, M = construct_element_table(noelms, noelms)
        lam1 = 1
        lam2 = 1
        qt = construct_qt(etov_dict, X, Y, test_case=2)
        return nr_of_test_case, X, Y, L1, L2, x0, y0, etov_dict, M, lam1, lam2, qt
    else:
        raise Exception("Unknown test case")

def max_error(u, u_hat, M):
    errors = []
    for i in range(M):
        errors.append(abs(u[i] - u_hat[i]))
    return max(errors)

def main():
    ntest_case = 1
    nr_of_test_case, X, Y, L1, L2, x0, y0, etov_dict, M, lam1, lam2, qt = test_case_data(ntest_case)

    A, b = assembly(X, Y, etov_dict, lam1=lam1, lam2=lam2, qt=qt, M=M)
    uhat = u_hat(X, Y, L1, L2, x0, y0, M, A, b, ntest_case,"2_5")
    print("test case ",ntest_case)
    print("u_hat(x,y): ", uhat)
    u1 = u(X, Y, ntest_case)
    print("u(x,y): ", u1)
    error = max_error(u1, uhat, M)
    print("Max error: ",error)

if __name__ == '__main__':
    main()


import pylab as pl
import numpy as np
from matplotlib import cm

color_map=[cm.winter,cm.autumn]

D = 2

# Different Mean Vectors
M1 = np.array([0.0, 2.0])
M2 = np.array([3.0, 1.0])
M3 = np.array([1.0, 0.0])

# Same Covariance Matrix
C = np.array([
    [1.0, 0.0],
    [0.0, 1/3]
])

X, Y = np.mgrid[-2:2:100j, -2:2:100j]
points = np.c_[X.ravel(), Y.ravel()]

invC = np.linalg.inv(C)

v = points - M1
g1 = -0.5*np.sum(np.dot(v, invC) * v, axis=1) - D*0.5*np.log(2*np.pi) - 0.5*np.log(np.linalg.det(C))
g1.shape = 100, 100

v = points - M2
g2 = -0.5*np.sum(np.dot(v, invC) * v, axis=1) - D*0.5*np.log(2*np.pi) - 0.5*np.log(np.linalg.det(C))
g2.shape = 100, 100

v = points - M3
g3 = -0.5*np.sum(np.dot(v, invC) * v, axis=1) - D*0.5*np.log(2*np.pi) - 0.5*np.log(np.linalg.det(C))
g3.shape = 100, 100

# Display the distributions plots
fig, axes = pl.subplots(1, 3, dpi=60, figsize=(15, 5))
ax1, ax2, ax3 = axes.ravel()
for ax in axes.ravel():
    ax.set_aspect("equal")

ax1.pcolormesh(X, Y, g1, cmap=color_map[0])
ax1.set_title("W1")
ax2.pcolormesh(X, Y, g2, cmap=color_map[0])
ax2.set_title("W2")
ax3.pcolormesh(X, Y, g3, cmap=color_map[0])
ax3.set_title("W3")

# Display the discriminators plots
fig2, axes2 = pl.subplots(1, 3, dpi=60, figsize=(15, 5))
ax4, ax5, ax6 = axes2.ravel()

ax4.pcolormesh(X, Y, g1 > g2, cmap=color_map[1])
ax4.set_title("W1 > W2")
ax5.pcolormesh(X, Y, g2 > g3, cmap=color_map[1])
ax5.set_title("W2 > W3")
ax6.pcolormesh(X, Y, g1 > g3, cmap=color_map[1])
ax6.set_title("W1 > W3")

pl.show()


from scipy import stats
from numpy.random import default_rng
rng = default_rng(12345)

current = rng.normal(4.0, 2.0, size=40)
process_a = rng.normal(6.2, 2.0, size=25)
process_b = rng.normal(4.5, 2.0, size=64)

significance = 0.05

F_stat, p_value = stats.f_oneway(current, process_a, process_b)

print(f"F stat: {F_stat}, p value: {p_value}")
# F stat: 9.949052026027028, p value: 9.732322721019206e-05

if p_value <= significance:
    print("Reject H0: there is a difference between means")
else:
    print("Accept H0: all means equal")
# Reject H0: there is a difference between means


from math import exp, sqrt, pi
from numpy import *
from matplotlib.pyplot import *

# Parameters
mu = 30
sigma = 10

# Plotting the PDF
def pdf(x):
    return (1 / (sigma * sqrt(2*pi)) ) * exp(-(x - mu)**2 / (2 * sigma**2))

X = arange(-100, 100, 0.1)
Y = []

for x in X:
    Y.append(pdf(x))

matplotlib.rc('xtick', labelsize=18)
matplotlib.rc('ytick', labelsize=18)
plot(X, Y, Linewidth=2, color='black')
xlabel('$X$', size=22)
ylabel('$f_X(x)$', size=22)
#show()
savefig('normal_pdf.pdf', format='pdf', bbox_inches='tight')


import os
""" First change the following directory link to where all input files do exist """
os.chdir("D:\\Book writing\\Codes\\Chapter 8")

# K-means clustering
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist, pdist

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

iris = pd.read_csv("iris.csv")
print (iris.head())

x_iris = iris.drop(['class'],axis=1)
y_iris = iris["class"]

k_means_fit = KMeans(n_clusters=3,max_iter=300)
k_means_fit.fit(x_iris)

print ("\nK-Means Clustering - Confusion Matrix\n\n",pd.crosstab(y_iris,k_means_fit.labels_,rownames = ["Actuall"],colnames = ["Predicted"]) )
print ("\nSilhouette-score: %0.3f" % silhouette_score(x_iris, k_means_fit.labels_, metric='euclidean'))

for k in range(2,10):
    k_means_fitk = KMeans(n_clusters=k,max_iter=300)
    k_means_fitk.fit(x_iris)
    print ("For K value",k,",Silhouette-score: %0.3f" % silhouette_score(x_iris, k_means_fitk.labels_, metric='euclidean'))

# Avg. within-cluster sum of squares
K = range(1,10)

KM = [KMeans(n_clusters=k).fit(x_iris) for k in K]
centroids = [k.cluster_centers_ for k in KM]

D_k = [cdist(x_iris, centrds, 'euclidean') for centrds in centroids]

cIdx = [np.argmin(D,axis=1) for D in D_k]
dist = [np.min(D,axis=1) for D in D_k]
avgWithinSS = [sum(d)/x_iris.shape[0] for d in dist]

# Total with-in sum of square
wcss = [sum(d**2) for d in dist]
tss = sum(pdist(x_iris)**2)/x_iris.shape[0]
bss = tss-wcss

# elbow curve - Avg. within-cluster sum of squares
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(K, avgWithinSS, 'b*-')
plt.grid(True)
plt.xlabel('Number of clusters')
plt.ylabel('Average within-cluster sum of squares')
#plt.title('Elbow for KMeans clustering')

# elbow curve - percentage of variance explained
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(K, bss/tss*100, 'b*-')
plt.grid(True)
plt.xlabel('Number of clusters')
plt.ylabel('Percentage of variance explained')
#plt.title('Elbow for KMeans clustering')

# Calculation of eigenvectors & eigenvalues
import numpy as np
w,v = np.linalg.eig(np.array([[ 0.91335 ,0.75969 ],[ 0.75969,0.69702]]))
print ("\nEigen Values\n",w)
print ("\nEigen Vectors\n",v)

# PCA - Principal Component Analysis
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.datasets import load_digits

digits = load_digits()
X = digits.data
y = digits.target

print (digits.data[0].reshape(8,8))

plt.matshow(digits.images[0])
plt.show()

from sklearn.preprocessing import scale
X_scale = scale(X,axis=0)

pca = PCA(n_components=2)
reduced_X = pca.fit_transform(X_scale)

zero_x, zero_y = [],[] ; one_x, one_y = [],[]
two_x,two_y = [],[]; three_x, three_y = [],[]
four_x,four_y = [],[]; five_x,five_y = [],[]
six_x,six_y = [],[]; seven_x,seven_y = [],[]
eight_x,eight_y = [],[]; nine_x,nine_y = [],[]

for i in range(len(reduced_X)):
    if y[i] == 0:
        zero_x.append(reduced_X[i][0])
        zero_y.append(reduced_X[i][1])

    elif y[i] == 1:
        one_x.append(reduced_X[i][0])
        one_y.append(reduced_X[i][1])

    elif y[i] == 2:
        two_x.append(reduced_X[i][0])
        two_y.append(reduced_X[i][1])

    elif y[i] == 3:
        three_x.append(reduced_X[i][0])
        three_y.append(reduced_X[i][1])

    elif y[i] == 4:
        four_x.append(reduced_X[i][0])
        four_y.append(reduced_X[i][1])

    elif y[i] == 5:
        five_x.append(reduced_X[i][0])
        five_y.append(reduced_X[i][1])

    elif y[i] == 6:
        six_x.append(reduced_X[i][0])
        six_y.append(reduced_X[i][1])

    elif y[i] == 7:
        seven_x.append(reduced_X[i][0])
        seven_y.append(reduced_X[i][1])

    elif y[i] == 8:
        eight_x.append(reduced_X[i][0])
        eight_y.append(reduced_X[i][1])

    elif y[i] == 9:
        nine_x.append(reduced_X[i][0])
        nine_y.append(reduced_X[i][1])

zero = plt.scatter(zero_x, zero_y, c='r', marker='x',label='zero')
one = plt.scatter(one_x, one_y, c='g', marker='+')
two = plt.scatter(two_x, two_y, c='b', marker='s')

three = plt.scatter(three_x, three_y, c='m', marker='*')
four = plt.scatter(four_x, four_y, c='c', marker='h')
five = plt.scatter(five_x, five_y, c='r', marker='D')

six = plt.scatter(six_x, six_y, c='y', marker='8')
seven = plt.scatter(seven_x, seven_y, c='k', marker='*')
eight = plt.scatter(eight_x, eight_y, c='r', marker='x')

nine = plt.scatter(nine_x, nine_y, c='b', marker='D')

plt.legend((zero,one,two,three,four,five,six,seven,eight,nine),
           ('zero','one','two','three','four','five','six','seven','eight','nine'),
           scatterpoints=1,
           loc='lower left',
           ncol=3,
           fontsize=10)

plt.xlabel('PC 1')
plt.ylabel('PC 2')

plt.show()

# 3-Dimensional data
pca_3d = PCA(n_components=3)
reduced_X3D = pca_3d.fit_transform(X_scale)

print (pca_3d.explained_variance_ratio_)

zero_x, zero_y,zero_z = [],[],[] ; one_x, one_y,one_z = [],[],[]
two_x,two_y,two_z = [],[],[]; three_x, three_y,three_z = [],[],[]
four_x,four_y,four_z = [],[],[]; five_x,five_y,five_z = [],[],[]
six_x,six_y,six_z = [],[],[]; seven_x,seven_y,seven_z = [],[],[]
eight_x,eight_y,eight_z = [],[],[]; nine_x,nine_y,nine_z = [],[],[]

for i in range(len(reduced_X3D)):

    if y[i]==10:
        continue

    elif y[i] == 0:
        zero_x.append(reduced_X3D[i][0])
        zero_y.append(reduced_X3D[i][1])
        zero_z.append(reduced_X3D[i][2])

    elif y[i] == 1:
        one_x.append(reduced_X3D[i][0])
        one_y.append(reduced_X3D[i][1])
        one_z.append(reduced_X3D[i][2])

    elif y[i] == 2:
        two_x.append(reduced_X3D[i][0])
        two_y.append(reduced_X3D[i][1])
        two_z.append(reduced_X3D[i][2])

    elif y[i] == 3:
        three_x.append(reduced_X3D[i][0])
        three_y.append(reduced_X3D[i][1])
        three_z.append(reduced_X3D[i][2])

    elif y[i] == 4:
        four_x.append(reduced_X3D[i][0])
        four_y.append(reduced_X3D[i][1])
        four_z.append(reduced_X3D[i][2])

    elif y[i] == 5:
        five_x.append(reduced_X3D[i][0])
        five_y.append(reduced_X3D[i][1])
        five_z.append(reduced_X3D[i][2])

    elif y[i] == 6:
        six_x.append(reduced_X3D[i][0])
        six_y.append(reduced_X3D[i][1])
        six_z.append(reduced_X3D[i][2])

    elif y[i] == 7:
        seven_x.append(reduced_X3D[i][0])
        seven_y.append(reduced_X3D[i][1])
        seven_z.append(reduced_X3D[i][2])

    elif y[i] == 8:
        eight_x.append(reduced_X3D[i][0])
        eight_y.append(reduced_X3D[i][1])
        eight_z.append(reduced_X3D[i][2])

    elif y[i] == 9:
        nine_x.append(reduced_X3D[i][0])
        nine_y.append(reduced_X3D[i][1])
        nine_z.append(reduced_X3D[i][2])

# 3- Dimensional plot
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.scatter(zero_x, zero_y,zero_z, c='r', marker='x',label='zero')
ax.scatter(one_x, one_y,one_z, c='g', marker='+',label='one')
ax.scatter(two_x, two_y,two_z, c='b', marker='s',label='two')

ax.scatter(three_x, three_y,three_z, c='m', marker='*',label='three')
ax.scatter(four_x, four_y,four_z, c='c', marker='h',label='four')
ax.scatter(five_x, five_y,five_z, c='r', marker='D',label='five')

ax.scatter(six_x, six_y,six_z, c='y', marker='8',label='six')
ax.scatter(seven_x, seven_y,seven_z, c='k', marker='*',label='seven')
ax.scatter(eight_x, eight_y,eight_z, c='r', marker='x',label='eight')

ax.scatter(nine_x, nine_y,nine_z, c='b', marker='D',label='nine')

ax.set_xlabel('PC 1')
ax.set_ylabel('PC 2')
ax.set_zlabel('PC 3')

plt.legend(loc='upper left', numpoints=1, ncol=3, fontsize=10, bbox_to_anchor=(0, 0))

plt.show()

# Chosing number of Principal Components
max_pc = 30

pcs = []
totexp_var = []

for i in range(max_pc):
    pca = PCA(n_components=i+1)
    reduced_X = pca.fit_transform(X_scale)
    tot_var = pca.explained_variance_ratio_.sum()
    pcs.append(i+1)
    totexp_var.append(tot_var)

plt.plot(pcs,totexp_var,'r')
plt.plot(pcs,totexp_var,'bs')
plt.xlabel('No. of PCs',fontsize = 13)
plt.ylabel('Total variance explained',fontsize = 13)

plt.xticks(pcs,fontsize=13)
plt.yticks(fontsize=13)
plt.show()

# SVD
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits

digits = load_digits()
X = digits.data
y = digits.target

from sklearn.utils.extmath import randomized_svd
U,Sigma,VT = randomized_svd(X,n_components=15,n_iter=300,random_state=42)

print ("\nShape of Original Matrix:",X.shape)
print ("\nShape of Left Singular vector:",U.shape)
print ("Shape of Singular value:",Sigma.shape)
print ("Shape of Right Singular vector",VT.shape)

import pandas as pd
VT_df = pd.DataFrame(VT)

n_comps = 15
from sklearn.decomposition import TruncatedSVD
svd = TruncatedSVD(n_components=n_comps, n_iter=300, random_state=42)
reduced_X = svd.fit_transform(X)

print("\nTotal Variance explained for %d singular features are %0.3f"%(n_comps,svd.explained_variance_ratio_.sum()))

# Choosing number of Singular Values
max_singfeat = 30

singfeats = []
totexp_var = []

for i in range(max_singfeat):
    svd = TruncatedSVD(n_components=i+1, n_iter=300, random_state=42)
    reduced_X = svd.fit_transform(X)
    tot_var = svd.explained_variance_ratio_.sum()
    singfeats.append(i+1)
    totexp_var.append(tot_var)

plt.plot(singfeats,totexp_var,'r')
plt.plot(singfeats,totexp_var,'bs')
plt.xlabel('No. of Features',fontsize = 13)
plt.ylabel('Total variance explained',fontsize = 13)

#plt.xticks(singfeats,fontsize=13)
plt.yticks(fontsize=13)
plt.show()

# Deep Auto Encoders
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_digits

digits = load_digits()
X = digits.data
y = digits.target

print (X.shape)
print (y.shape)

x_vars_stdscle = StandardScaler().fit_transform(X)
print (x_vars_stdscle.shape)

from keras.layers import Input,Dense
from keras.models import Model

# 2-Dimensional Architecture

input_layer = Input(shape=(64,),name="input")

encoded = Dense(32, activation='relu',name="h1encode")(input_layer)
encoded = Dense(16, activation='relu',name="h2encode")(encoded)
encoded = Dense(2, activation='relu',name="h3latent_layer")(encoded)

decoded = Dense(16, activation='relu',name="h4decode")(encoded)
decoded = Dense(32, activation='relu',name="h5decode")(decoded)
decoded = Dense(64, activation='sigmoid',name="h6decode")(decoded)

autoencoder = Model(input_layer, decoded)
autoencoder.compile(optimizer="adam", loss="mse")

# Fitting Encoder-Decoder model
autoencoder.fit(x_vars_stdscle, x_vars_stdscle, epochs=100,batch_size=256,shuffle=True,validation_split= 0.2 )

# Extracting Encoder section of the Model for prediction of latent variables
encoder = Model(autoencoder.input,autoencoder.get_layer("h3latent_layer").output)

# Predicting latent variables with extracted Encoder model
reduced_X = encoder.predict(x_vars_stdscle)

print (reduced_X.shape)

zero_x, zero_y = [],[] ; one_x, one_y = [],[]
two_x,two_y = [],[]; three_x, three_y = [],[]
four_x,four_y = [],[]; five_x,five_y = [],[]
six_x,six_y = [],[]; seven_x,seven_y = [],[]
eight_x,eight_y = [],[]; nine_x,nine_y = [],[]

# For 2-Dimensional data
for i in range(len(reduced_X)):
    if y[i] == 0:
        zero_x.append(reduced_X[i][0])
        zero_y.append(reduced_X[i][1])

    elif y[i] == 1:
        one_x.append(reduced_X[i][0])
        one_y.append(reduced_X[i][1])

    elif y[i] == 2:
        two_x.append(reduced_X[i][0])
        two_y.append(reduced_X[i][1])

    elif y[i] == 3:
        three_x.append(reduced_X[i][0])
        three_y.append(reduced_X[i][1])

    elif y[i] == 4:
        four_x.append(reduced_X[i][0])
        four_y.append(reduced_X[i][1])

    elif y[i] == 5:
        five_x.append(reduced_X[i][0])
        five_y.append(reduced_X[i][1])

    elif y[i] == 6:
        six_x.append(reduced_X[i][0])
        six_y.append(reduced_X[i][1])

    elif y[i] == 7:
        seven_x.append(reduced_X[i][0])
        seven_y.append(reduced_X[i][1])

    elif y[i] == 8:
        eight_x.append(reduced_X[i][0])
        eight_y.append(reduced_X[i][1])

    elif y[i] == 9:
        nine_x.append(reduced_X[i][0])
        nine_y.append(reduced_X[i][1])

zero = plt.scatter(zero_x, zero_y, c='r', marker='x',label='zero')
one = plt.scatter(one_x, one_y, c='g', marker='+')
two = plt.scatter(two_x, two_y, c='b', marker='s')

three = plt.scatter(three_x, three_y, c='m', marker='*')
four = plt.scatter(four_x, four_y, c='c', marker='h')
five = plt.scatter(five_x, five_y, c='r', marker='D')

six = plt.scatter(six_x, six_y, c='y', marker='8')
seven = plt.scatter(seven_x, seven_y, c='k', marker='*')
eight = plt.scatter(eight_x, eight_y, c='r', marker='x')

nine = plt.scatter(nine_x, nine_y, c='b', marker='D')

plt.legend((zero,one,two,three,four,five,six,seven,eight,nine),
           ('zero','one','two','three','four','five','six','seven','eight','nine'),
           scatterpoints=1,
           loc='lower right',
           ncol=3,
           fontsize=10)

plt.xlabel('Latent Feature 1',fontsize = 13)
plt.ylabel('Latent Feature 2',fontsize = 13)

plt.show()

# 3-Dimensional architecture
input_layer = Input(shape=(64,),name="input")

encoded = Dense(32, activation='relu',name="h1encode")(input_layer)
encoded = Dense(16, activation='relu',name="h2encode")(encoded)
encoded = Dense(3, activation='relu',name="h3latent_layer")(encoded)

decoded = Dense(16, activation='relu',name="h4decode")(encoded)
decoded = Dense(32, activation='relu',name="h5decode")(decoded)
decoded = Dense(64, activation='sigmoid',name="h6decode")(decoded)

autoencoder = Model(input_layer, decoded)
autoencoder.compile(optimizer="adam", loss="mse")

# Fitting Encoder-Decoder model
autoencoder.fit(x_vars_stdscle, x_vars_stdscle, epochs=100,batch_size=256,shuffle=True,validation_split= 0.2 )

# Extracting Encoder section of the Model for prediction of latent variables
encoder = Model(autoencoder.input,autoencoder.get_layer("h3latent_layer").output)

# Predicting latent variables with extracted Encoder model
reduced_X3D = encoder.predict(x_vars_stdscle)

zero_x, zero_y,zero_z = [],[],[] ; one_x, one_y,one_z = [],[],[]
two_x,two_y,two_z = [],[],[]; three_x, three_y,three_z = [],[],[]
four_x,four_y,four_z = [],[],[]; five_x,five_y,five_z = [],[],[]
six_x,six_y,six_z = [],[],[]; seven_x,seven_y,seven_z = [],[],[]
eight_x,eight_y,eight_z = [],[],[]; nine_x,nine_y,nine_z = [],[],[]

for i in range(len(reduced_X3D)):

    if y[i]==10:
        continue

    elif y[i] == 0:
        zero_x.append(reduced_X3D[i][0])
        zero_y.append(reduced_X3D[i][1])
        zero_z.append(reduced_X3D[i][2])

    elif y[i] == 1:
        one_x.append(reduced_X3D[i][0])
        one_y.append(reduced_X3D[i][1])
        one_z.append(reduced_X3D[i][2])

    elif y[i] == 2:
        two_x.append(reduced_X3D[i][0])
        two_y.append(reduced_X3D[i][1])
        two_z.append(reduced_X3D[i][2])

    elif y[i] == 3:
        three_x.append(reduced_X3D[i][0])
        three_y.append(reduced_X3D[i][1])
        three_z.append(reduced_X3D[i][2])

    elif y[i] == 4:
        four_x.append(reduced_X3D[i][0])
        four_y.append(reduced_X3D[i][1])
        four_z.append(reduced_X3D[i][2])

    elif y[i] == 5:
        five_x.append(reduced_X3D[i][0])
        five_y.append(reduced_X3D[i][1])
        five_z.append(reduced_X3D[i][2])

    elif y[i] == 6:
        six_x.append(reduced_X3D[i][0])
        six_y.append(reduced_X3D[i][1])
        six_z.append(reduced_X3D[i][2])

    elif y[i] == 7:
        seven_x.append(reduced_X3D[i][0])
        seven_y.append(reduced_X3D[i][1])
        seven_z.append(reduced_X3D[i][2])

    elif y[i] == 8:
        eight_x.append(reduced_X3D[i][0])
        eight_y.append(reduced_X3D[i][1])
        eight_z.append(reduced_X3D[i][2])

    elif y[i] == 9:
        nine_x.append(reduced_X3D[i][0])
        nine_y.append(reduced_X3D[i][1])
        nine_z.append(reduced_X3D[i][2])

# 3- Dimensional plot
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.scatter(zero_x, zero_y,zero_z, c='r', marker='x',label='zero')
ax.scatter(one_x, one_y,one_z, c='g', marker='+',label='one')
ax.scatter(two_x, two_y,two_z, c='b', marker='s',label='two')

ax.scatter(three_x, three_y,three_z, c='m', marker='*',label='three')
ax.scatter(four_x, four_y,four_z, c='c', marker='h',label='four')
ax.scatter(five_x, five_y,five_z, c='r', marker='D',label='five')

ax.scatter(six_x, six_y,six_z, c='y', marker='8',label='six')
ax.scatter(seven_x, seven_y,seven_z, c='k', marker='*',label='seven')
ax.scatter(eight_x, eight_y,eight_z, c='r', marker='x',label='eight')

ax.scatter(nine_x, nine_y,nine_z, c='b', marker='D',label='nine')

ax.set_xlabel('Latent Feature 1',fontsize = 13)
ax.set_ylabel('Latent Feature 2',fontsize = 13)
ax.set_zlabel('Latent Feature 3',fontsize = 13)

ax.set_xlim3d(0,60)

plt.legend(loc='upper left', numpoints=1, ncol=3, fontsize=10, bbox_to_anchor=(0, 0))

plt.show()

ax.set_xlim3d(left = 0,right = 30)
ax.set_ylim3d(left = 0,right = 30)
ax.set_zlim3d(left = 0,right = 30)


import numpy as np
import matplotlib.pyplot as plt

def test_equation(x, l):
    return -l * x

def two_step_method(xk, xkminus1, l, tau):
    return xkminus1 - 2 * tau * l * xk

def solution(t, l, x0):
    return x0*np.exp(-l*t)

lamb = 1.0
x0 = 1.0
tmin = 0.0
tmax = 1.0

grid = np.linspace(tmin, tmax, 1000)
y_soln = solution(grid, lamb, x0)
plt.plot(grid, y_soln, label='Solution', linewidth=3.5)

ns = [10, 100, 1000]
for n in ns:
    grid = np.linspace(tmin, tmax, n)
    print(len(grid))
    tau = 1/n
    x1 = x0 - tau * lamb * x0
    y_twostep = [x0, x1]
    for tk in grid[2:]:
        y_twostep.append(
            two_step_method(y_twostep[-1], y_twostep[-2], lamb, tau)
        )
    plt.plot(grid, y_twostep, label=f'Two step n={n}')
plt.legend()
plt.savefig(f'compare.png')


################################################################################
#                                                                              #
#	UJJWAL KHANDELWAL                                                      #
#	CSO (CUCKOO SEARCH OPTIMIZATION)                                       #
#	PYTHON 3.7.10                                                          #
#                                                                              #
################################################################################

#######################   IMPORT DEPENDENCIES   ################################

import numpy as np

######################## FITNESS FUNCTION 1 ######################################

def fitness_1(X):

    '''

    X: POSITION (EITHER CURRENT, LOCAL BEST OR GLOBAL BEST) OF SIZE (n,)

    2-DIMENSIONAL VECTORS (X = (x,y))

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

    HIMMELBLAU'S FUNCTION

    MINIMIZE f(x) = (x^2 + y - 11)^2 + (x + y^2 - 7)^2

    OPTIMUM SOLUTION IS x* = 3 AND y* = 2

    REPLACE 'f' BELOW WITH THIS TO TEST EXAMPLE-1

    f = (x**2 + y - 11)**2 + (x + y**2 - 7)**2

    '''
    X = np.array(X).reshape(-1,1)
    x, y = X[0][0], X[1][0]
    f = (x**2 + y - 11)**2 + (x + y**2 - 7)**2
    return f

######################## FITNESS FUNCTION 2 ######################################

def fitness_2(X):

    '''

    X: POSITION (EITHER CURRENT, LOCAL BEST OR GLOBAL BEST) OF SIZE (n,)

    2-DIMENSIONAL VECTORS (X = (x,y))

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

    BOOTH'S FUNCTION

    MINIMIZE f(x) = (x + 2y - 7)^2 + (2x + y - 5)^2

    OPTIMUM SOLUTION IS x* = 1 AND y* = 3

    REPLACE 'f' BELOW WITH THIS TO TEST EXAMPLE-2

    f = (x + 2*y - 7)**2 + (2*x + y - 5)**2

    '''
    X = np.array(X).reshape(-1,1)
    x, y = X[0][0], X[1][0]
    f = (x + 2*y - 7)**2 + (2*x + y - 5)**2
    return f

######################## FITNESS FUNCTION 3 ######################################

def fitness_3(X):

    '''

    X: POSITION (EITHER CURRENT, LOCAL BEST OR GLOBAL BEST) OF SIZE (n,)

    2-DIMENSIONAL VECTORS (X = (x,y))

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

    BEALE'S FUNCTION

    MINIMIZE f(x) = (1.5 - x - xy)^2 + (2.25 - x + xy^2)^2 + (2.625 - x + xy^3)^2

    OPTIMUM SOLUTION IS x* = 3 AND y* = 0.5

    REPLACE 'f' BELOW WITH THIS TO TEST EXAMPLE-3

    f = (1.5 - x + x*y)**2 + (2.25 - x + x*(y**2))**2 + (2.625 - x + x*(y**3))**2

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

    '''
    X = np.array(X).reshape(-1,1)
    x, y = X[0][0], X[1][0]
    f = (1.5 - x + x*y)**2 + (2.25 - x + x*(y**2))**2 + (2.625 - x + x*(y**3))**2
    return f

def fitness_4(X):

    '''

    X: POSITION (EITHER CURRENT, LOCAL BEST OR GLOBAL BEST) OF SIZE (n,)

    2-DIMENSIONAL VECTORS (X = (x,y))

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

    MAXIMIZE f(x) = 2xy + 2x - x^2 - 2y^2

    OPTIMUM SOLUTION IS x* = 2 AND y* = 1

    REPLACE 'f' BELOW WITH THIS TO TEST fitness_4

    f = 2*x*y + 2*x - x**2 - 2*(y**2)

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

    '''
    X = np.array(X).reshape(-1,1)
    x, y = X[0][0], X[1][0]
    f = 2*x*y + 2*x - x**2 - 2*(y**2)
    return f

def fitness_5(X):

    '''

    X: POSITION (EITHER CURRENT, LOCAL BEST OR GLOBAL BEST) OF SIZE (n,)

    2-DIMENSIONAL VECTORS (X = (x,y))

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

    BIVARIATE MICHAELWICZ FUNCTION

    MINIMIZE f(x) = -sin(x)*(sin(x^2/π)^2m) - sin(y)*(sin(2y^2/π)^2m)

    OPTIMUM SOLUTION IS x* = 2.20319, AND y* = 1.57049

    REPLACE 'f' BELOW WITH THIS TO TEST EXAMPLE-5

    f = -np.sin(x)*(np.sin(x*x/np.pi)**(2*m))-np.sin(y)*(np.sin(2*y*y/np.pi)**(2*m))

    '''
    X = np.array(X).reshape(-1,1)
    m = 10
    x, y = X[0][0], X[1][0]
    f = -np.sin(x)*(np.sin(x*x/np.pi)**(2*m))-np.sin(y)*(np.sin(2*y*y/np.pi)**(2*m))
    return f


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

def normalization_constants(X):
    """
    Calculates the normalization (standardization) constants of a matrix

    X is the numpy.ndarray of shape (m, nx) to normalize
        m is the number of data points
        nx is the number of features

    Returns: the mean and standard deviation of each feature, respectively
    """
    mean = np.sum(X, axis=0) / X.shape[0]
    vrnc = np.sum((X - mean) ** 2, axis=0) / X.shape[0]
    stdv = np.sqrt(vrnc)
    return mean, stdv


# -*- coding: utf-8 -*-
" We solve the numerical integral in this pyfile. Newton-Cotes integral rule, Composite Newton-Cotes integral rule are mentioned "

import numpy as np

""" Input data Instance:
data = np.array([[0,3],
                 [1,5],
                 [2,6],
                 [3,8]])

"""

def trapezium(data,a,b):
    " Integral from ath data to bth data "
    #dim = b-a
    x = data[a-1,0]
    y = data[b-1,0]
    fx = data[a-1,1]
    fy = data[b-1,1]
    return (y-x)/2*(fx+fy)

# trapezium(data,1,2)

def sinpson(data,a,b):
    " Integral from ath data to bth data "
    dim = b-a
    if dim%2 != 0:
        return "Error"
    else:
        x = data[a-1,0]
        #xy = data[(b+a)/2-1,0]
        y = data[b-1,0]
        fx = data[a-1,0]
        fy = data[b-1,0]
        fxy = data[(b+a)/2-1,0]
        return (y-x)/6*(fx+4*fxy+fy)

"""
data = np.array([[0,1],
                 [0.125,0.997398],
                 [0.25,0.989688],
                 [0.375,0.976727],
                 [0.5,0.958851],
                 [0.625,0.936156],
                 [0.75,0.908858],
                 [0.875,0.877193],
                 [1,0.841471]])
"""

def com_trapezium(data):
    dim = data.shape[0]
    a = data[0,0]
    b = data[dim-1,0]
    h = (b-a)/(dim-1)
    result = data[0,1]+data[dim-1,1]
    for index in range(1,dim-1):
        result = result + 2*data[index,1]
    return result*h/2

def com_sinpson(data):
    dim = data.shape[0]
    a = data[0,0]
    b = data[dim-1,0]
    h = (b-a)/(dim-1)*2
    result = data[0,1]+data[dim-1,1]
    #times = (dim-1)/2
    for index in range(1,dim-1):
        if index%2 != 0:
            result = result + 4*data[index,1]
        else:
            result = result + 2*data[index,1]
    return result*h/6

#com_trapezium(data)
#com_sinpson(data)


import matplotlib.pyplot as plt
import numpy as np
import sympy as sp
from sympy.abc import x

def get_integrated_tanh_sympy():
    return sp.integrate(sp.tanh(x), x)

def get_integrated_sigmoid_sympy():
    sigmoid = get_sigmoid_function_sympy()
    return sp.integrate(sigmoid, x)

def get_sigmoid_function_sympy():
    sigmoid = 1 / (1 + sp.exp(-x))
    return sigmoid

def proc_sympy_function(x_array, expression, label, lib='numpy'):
    # Numeric Computation, Sympy documentation, 2016, [Online] Available
    # http://docs.sympy.org/dev/modules/numeric-computation.html
    f = sp.lambdify(x, expression, lib)
    y_f = f(x_array)
    plt.plot(x_array, y_f, label=label)

def step(z):
    """
    return an array of same size as z.
    For elements of z larger than zero, return one.
    Otherwise zero.

    :type z: Any
    :param z: input. preferrably a ndarray
    :return:
    """
    result = np.zeros_like(z)
    result[z > 0] = 1.0
    return result

def ReLU(x_array):
    result = np.zeros_like(x_array)
    result[0 < x_array] = x_array[0 < x_array]
    return result

def main(min_x=-8, max_x=8):
    min_x, max_x = sorted([min_x, max_x])
    x_array = np.linspace(min_x, max_x, 101)

    x_array_step = get_x_step_array(min_x, max_x)

    symbols_to_be_processed_list = [
        {'label': 'sigmoid', 'f': get_sigmoid_function_sympy()},
        {'label': 'integrated sigmoid', 'f': get_integrated_sigmoid_sympy()},
    ]

    for fmt in ('pdf', 'png'):
        plt.clf()

        for d in symbols_to_be_processed_list:
            proc_sympy_function(x_array, d['f'], d['label'])

        y_ReLU = ReLU(x_array)
        plt.plot(x_array, y_ReLU, label='ReLU')

        y_step = step(x_array_step)
        plt.plot(x_array_step, y_step, label='step')

        plt.grid(True)
        plt.legend(loc=0)
        plt.axis('equal')
        plt.savefig('activation.%s' % fmt)

def get_x_step_array(min_x, max_x):
    if 0 > min_x * max_x:
        # if min_x and max_x have different signs
        x_array_step = np.array([min_x, 0.0, 1e-10, max_x])
    elif 0 < min_x * max_x:
        # if min_x and max_x have same signs
        x_array_step = np.array([min_x, max_x])
    elif 0 == min_x:
        x_array_step = np.array([min_x, 1e-10, max_x])
    elif 0 == max_x:
        x_array_step = np.array([min_x, 0, 1e-10])
    else:
        raise ValueError('Unable to decide x array')

    return x_array_step

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt

# generate 2D mesh grid
nx, ny = (100, 100)

x = np.linspace(0, 10, nx)
y = np.linspace(0, 10, ny)

xv, yv = np.meshgrid(x, y)

# define function to plot
def f(x, y):
    return x * (y ** 2)

# calculate Z value for each X,Y point
z = f(xv, yv)

"""
Now that we have our mesh grid and have calculated f(x,y) for all points on the mesh
grid,we can visualize the results! The z points on the graph will be represented using
a colormap.
"""

# Make a Color plot to display the data
plt.figure(figsize=(14, 12))
plt.pcolor(xv, yv, z)
plt.title('2D Color Plot of f(x,y)=xy^2')
plt.colorbar()
# plt.show()

# generate 2D mesh grid for Gradient
nx, ny = (10, 10)
x = np.linspace(0, 10, nx)
y = np.linspace(0, 10, ny)
xg, yg = np.meshgrid(x, y)

# calculate the gradient of f(x,y)
# Note: numpy returns answer in rows (y), columns (x) format
# Gy, Gx = np.gradient(f(xg, yg))

# calculate the gradient by formula for f(x, y) = x*y^2

def ddx(x, y):
    return y ** 2

def ddy(x, y):
    return 2 * x * y

Gx = ddx(xg, yg)
Gy = ddy(xg, yg)

# Make a Color plot to display the data
fig = plt.figure(figsize=(14, 12))
plt.pcolor(xv, yv, z)
plt.colorbar()
plt.quiver(xg, yg, Gx, Gy, scale=1000, color='w')
plt.title('Gradient of f(x,y) = xy^2')
plt.savefig('gradient.png')
plt.close(fig)


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

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import datasets

def hypo(X, theta):
    return theta * X[:,0]

def costFunc(X, y, theta, h=hypo):
    m = np.shape(X)[0]
    diff = h(X, theta) - y
    cost_list = np.power(diff, 2)
    return np.sum(cost_list) / (2 * float(m))

# gradient descent
def gradientDescent(X, y, alpa=0.1, h=hypo):
    m = np.shape(X)[0]
    theta = 0.0
    thetaArr = []
    maxCycles = 1000
    for k in range(maxCycles):
        theta = theta - alpa * ( np.sum((h(X, theta) - y) * X[:, 0]) / float(m) )
        thetaArr.append(theta)
    return theta, thetaArr

def showData(X, y):
    plt.title('x-y data')
    plt.scatter(X[:, 0], y)
    plt.show()

def showTheta_Cost(theta_list, cFunc=costFunc):
    cost_list = []
    for theta in theta_list:
        cost = cFunc(X, y, theta, h=hypo)
        cost_list.append(cost)
    plt.title('theta - cost')
    plt.scatter(theta_list, cost_list)
    plt.show()

# generate data
np.random.seed(314)
X, y = datasets.make_regression(n_samples=100, n_features=1, noise=10)
showData(X, y)

# plot the cost with different theta (model parameter)
cost_list = []
theta_list = np.arange(0, 50, 1)
for theta in theta_list:
    cost = costFunc(X, y, theta, h=hypo)
    cost_list.append(cost)
showTheta_Cost(theta_list, cFunc=costFunc)

# optimize cost-function by using gradient descent algorithm
theta, thetaArr = gradientDescent(X, y)
showTheta_Cost(thetaArr, cFunc=costFunc)

# plot the final result (best model paramter)
plt.title('best regression model')
plt.scatter(X[:, 0], y, c='b')
plt.plot(X[:, 0], hypo(X, theta), 'r')


from numpy import linspace, sin, cos, pi, absolute, real, imag, arctan, arctan2
from numpy.fft import fft, fftfreq, fftshift
import matplotlib.pyplot as plt

def dft(x):
    N = x.size
    n = np.arange(N)
    k = n.reshape((N, 1))
    e = np.exp(-2j * np.pi * k * n / N)
    return np.dot(e, x)

##
## Exercício 1
##
def exercicio1():

	# Sampling rate
	fs = 32  # Hz

	# Time is from 0 to 1 seconds, but leave off the endpoint, so
	# that 1.0 seconds is the first sample of the *next* chunk
	length = 1  # second
	N = fs * length
	t = linspace(0, length, num=N, endpoint=False)

	# Generate a sinusoid at frequency f
	f1 = 8  # Hz
	a = 3.0 * sin(2 * pi * f1 * t)

	# Plot signal, showing how endpoints wrap from one chunk to the next
	plt.subplot(3, 1, 1)
	plt.plot(t, a, '.-')
	plt.plot(t, a, 'r.')  # first sample of next chunk
	plt.margins(0.1, 0.1)
	plt.xlabel('Time [s]')

	## DFT
	ffta = fft(a)

	## normalized amplitude
	ampl = 2 * 1/N * abs(ffta)

	## real and imaginary part
	re = real(ffta)
	im = imag(ffta)

	# FFT frequency bins
	freqs = fftfreq(N, 1/fs)

	# Plot shifted data on a shifted axis
	plt.subplot(3, 1, 2)
	plt.stem(fftshift(freqs), fftshift(ampl))
	plt.margins(0.1, 0.1)
	plt.xlabel('Frequency [Hz]')

	plt.subplot(3, 1, 3)
	plt.stem(fftshift(freqs), fftshift(arctan2(im, re)*180/pi))
	plt.margins(0.1, 0.1)
	plt.xlabel('Frequency [Hz]')
	plt.tight_layout()
	plt.show()

##
## Exercício 2
##
def exercicio2():

	# Sampling rate
	fs = 64  # Hz

	# Time is from 0 to 1 seconds, but leave off the endpoint, so
	# that 1.0 seconds is the first sample of the *next* chunk
	length = 1  # second
	N = fs * length
	t = linspace(0, length, num=N, endpoint=False)

	# Generate a sinusoids
	f1 = 8   # Hz
	f2 = 12  # Hz
	f3 = 20  # Hz
	a = 3.0 * sin(2 * pi * f1 * t) + 1.5 * cos(2 * pi * f2 * t) + 0.5 * sin(2 * pi * f3 * t)

	# Plot signal, showing how endpoints wrap from one chunk to the next
	plt.subplot(3, 1, 1)
	plt.plot(t, a, '.-')
	plt.plot(t, a, 'r.')  # first sample of next chunk
	plt.margins(0.1, 0.1)
	plt.xlabel('Time [s]')

	## DFT
	ffta = fft(a)

	## normalized amplitude
	ampl = 2 * 1/N * abs(ffta)

	## real and imaginary part
	re = real(ffta)
	im = imag(ffta)

	# FFT frequency bins
	freqs = fftfreq(N, 1/fs)

	# Plot shifted data on a shifted axis
	plt.subplot(3, 1, 2)
	plt.stem(fftshift(freqs), fftshift(ampl))
	plt.margins(0.1, 0.1)
	plt.xlabel('Frequency [Hz]')

	plt.subplot(3, 1, 3)
	plt.stem(fftshift(freqs), fftshift(arctan2(im, re)*180/pi))
	plt.margins(0.1, 0.1)
	plt.xlabel('Frequency [Hz]')
	plt.tight_layout()
	plt.show()


"""
Script to create metaballs structures encoded as arrays.
Metaballs structures used for background generation.
"""

import numpy as np

def norm(x,y,p):
    """
    Normalise x and y. Raises x and y to power p sum them and then take
    root to the power 1/p.
    Since this value can be used as divisor, a small constant is added
    to ensure it is never zero
    :param x: value to be normalized
    :param y: value to be normalized
    :param p: normalization factor
    :return: sum of normalized values raised to 1/p
    """
    #the constant factor is to ensure that norm is never 0 as it is used
    # as divisor
    n = np.power(np.power(np.abs(x),p) + np.power(np.abs(y),p), 1./p)+1e-08
    return n

class ball:
    """
    Class representing a 2D ball, with a center, radius and normalization
    factor. Provides function to calculate inverse distance to a point
    relative to the radius.
    :param radius: radius of the ball
    :param x0: x coordinate of the center
    :param y0: y coordinate of the center
    :param norm: normalisation factor
    """
    def __init__(self, radius, x0, y0, norm):
        self.radius = radius
        self.x0 = x0
        self.y0 = y0
        self.norm = norm

    def inverse_distance(self, x, y):
        """
        Calcutes the inverse distance from a point to the sphere center
        :param x: x coordinate of the point
        :param y: y coordinate of the point
        :return: inverse distance to the point scaled by the radius
        """
        return self.radius/norm(x-self.x0, y-self.y0, self.norm)

def sum_inverse_distance(x,y,balls):
    """
    For a given ball and array of points calculates the inverse distance
    of each point to each ball and sums values for each point together. This
    produce an array of the same size as the input x,y arrays
    :param x: array of x coordinates of individual points
    :param y: array of y coordinates of individual points
    :balls: array of ball class instances
    :return f: array of sums of inverse distances of each point to all balls
        respectively (e.g. f[0] is sum of all inverse distances of
        point x[0],y[0])
    """
    if(x.shape!=y.shape):
        raise IndexError("The x and y parameters don't have the same shape")

    f = np.zeros(x.shape)
    for ball in balls:
        f += ball.inverse_distance(x,y)
    return f

def metaball(rows, cols, balls, thres):
    """
    For a 2D array of given number of rows and collumns, it calculates
    the inverse distance to all balls. Creates a boolean array of same size
    where True, if the points sum of inverse distances is bigger than given
    treshold.
    :param rows: int number of rows
    :param cols: int number of collumns
    :param balls: array of ball class instances
    :param thres: threshold for the inverse distance
    :return filled: boolean array of the specified size with True for entries
        which coordinate representation inverse distance was larger than
        treshold
    """
    # create arrays of X,Y coordinates
    Y = np.linspace(0,rows-1,rows)
    Y = np.reshape(Y,(rows,1))
    Ystack = []
    for i in range(cols):
        Ystack.append(Y)
    Y = np.hstack(Ystack)

    X = np.linspace(0,cols-1,cols)
    Xstack = []
    for i in range(rows):
        Xstack.append(X)
    X = np.vstack(Xstack)

    # compute the sum_inverse_distance over the coordinates, using numpy matrix operations
    f = sum_inverse_distance(X,Y,balls)
    filled = f > thres
    return filled

def random_metaball(row, col, n_balls, size):
    """
    Returns a metaball images with specified size (row, col), number of
    balls present. The amount of metaball features present can be controlled
    through the n_balls and size (threshold for pixel being included in the
    metaball)
    :param row: number of rows of returned image
    :param col: number of collumns of returned image
    :param n_balls: number of balls to generate
    :param size: treshold for point being included in a metaball
    :return metaball: returns a row*col array image of metaball features
    """
    centre_x = np.random.randint(0,col)
    centre_y = np.random.randint(0,row)
    sigma_x = np.round(col/10.0)
    sigma_y = np.round(row/10.0)
    min_ball_radius = col/(15*n_balls)
    max_ball_radius = col/(10*n_balls)

    # generate balls
    Balls = []
    for i in range(n_balls):
        x = np.round(np.random.normal(centre_x, sigma_x))
        y = np.round(np.random.normal(centre_y, sigma_y))
        x = min(max(x,0),col-1)
        y = min(max(y,0),row-1)
        radius = np.random.uniform(min_ball_radius, max_ball_radius)

        norm = np.random.randint(1,3)

        Balls.append(ball(radius, x, y, norm))

    return metaball(row, col, Balls, size)


import numpy as np
from scipy.integrate import quad
from scipy.special import gamma as Gamma

def integrand(phi, l, i):
    """Integrand in the q^l_i expression."""
    sin = np.sin(phi)
    cos = np.cos(phi)
    return (
        np.sign(sin) ** i * (1 - cos) ** (l - i / 2) * (1 + cos) ** (i / 2)
    ) / (2 * np.pi)

def qli_numerical(l, i):
    """Integration of q^l_i by quadrature."""
    return quad(integrand, -np.pi, np.pi, args=(l, i))[0]

def qli_closed_form(l, i):
    """Closed form solution forq^l_i."""
    return (
        2 ** l
        / np.pi
        * Gamma(0.5 * (1 + i))
        * Gamma(l + 0.5 * (1 - i))
        / Gamma(l + 1)
    )

def test_qli(lmax=10):
    """
    Show that our closed form expression for the integral is correct.
    """
    for l in range(lmax + 1):
        for i in range(0, 2 * l + 1, 2):
            assert np.allclose(qli_closed_form(l, i), qli_numerical(l, i))


import numpy as np

def qeq(a, b, c):
    """
    Solve quadratic equation.
    """
    d = np.sqrt(b**2 - 4 * a * c)
    return ((-b + d) / (2 * a)), ((-b - d)/ (2 * a))

def qeq2(a, b, c):
    """
    Solve quadratic equation with less error.
    """
    d = np.sqrt(b**2 - 4 * a * c)
    alpha = (-b - np.sign(b) * d) / (2 * a)
    beta = c / (a * alpha)
    return alpha, beta


import math
import numpy as np

def dotproduct(v1, v2):
	return sum((a*b) for a, b in zip(v1, v2))

def length(v):
	return math.sqrt(dotproduct(v, v))

def getRotateAngle(v1, v2):
	return math.acos(np.clip(dotproduct(v1, v2) / (length(v1) * length(v2)), -1, 1))

def distanceEuclidean(a, b):
	return np.linalg.norm(sum(x1 - x2 for x1, x2 in zip(a, b)))

def mean(numbers):
	return float(sum(numbers)) / max(len(numbers), 1)

def RMSE(v1, v2):
	return mean([distanceEuclidean(a, b) for a, b in zip(v1, v2)])

def rotateCore(point, rad):
	x = math.cos(rad)*point[0] - math.sin(rad)*point[1]
	y = math.sin(rad)*point[0] + math.cos(rad)*point[1]
	return [x, y]

def rotatePoint(point, rad):
	return rotateCore(point, rad)

def rotatePointArray(points, rad):
	# print (points)
	points = [rotateCore(p, rad) for p in points]
	return points

def minIndex(data):
	pairs = [(i, v) for i, v in enumerate(data)]
	return min(pairs, key = lambda p : p[1])[0]

def getBestMatchAngle(ref, points):
	rads = [getRotateAngle(ref[0], points[i]) for i in range(len(points))]
	rads = rads + [-rad for rad in rads]
	errors = [RMSE(ref, rotatePointArray(points, rad)) for rad in rads]
	return rads[minIndex(errors)]

def testDriver():
	angles = [math.pi * (0.2 + i / 12.0)  for i in range(12)]
	points = [[math.cos(angle), math.sin(angle)] for angle in angles]
	ref = rotatePointArray(points, math.pi * 0.35)

	rad = getBestMatchAngle(ref, points)
	print (rad)

if __name__ == '__main__':
	testDriver()


import numpy as np
import matplotlib.pyplot as plt
# %matplotlib inline
import pymesh
#https://pymesh.readthedocs.io/en/latest/basic.html
import time
import multiprocessing
import numexpr as ne
import meshplot
# for display of meshes
#https://skoch9.github.io/meshplot/tutorial/

#Mesh Methods

# compute the volume of the tetrahedron formed from face with index iface
# and the origin
def vol_i(mesh,iface):
    f = mesh.faces
    v = mesh.vertices
    iv1 = f[iface,0]  # indexes of the 3 vertices
    iv2 = f[iface,1]
    iv3 = f[iface,2]
    #print(iv1,iv2,iv3)
    v1 = v[iv1]  # the 3 vertices
    v2 = v[iv2]
    v3 = v[iv3]
    #print(v1,v2,v3)
    mat = np.array([v1,v2,v3])
    # the volume is equal to 1/6 determinant of the matrix formed with the three vertices
    # https://en.wikipedia.org/wiki/Tetrahedron
    #print(mat)
    vol = np.linalg.det(mat)/6.0  # compute determinant
    return vol

# compute the volume of the mesh by looping over all tetrahedrons formed from the faces
# we assume that the body is convex
def volume_mesh(mesh):
    f = mesh.faces
    nf = len(f)
    vol = 0.0
    for iface in range(nf):
        vol += vol_i(mesh,iface)
    return vol

# if vol equ radius is 1  the volume should be equal to 4*np.pi/3 which is 4.1888

# tests
#vi = vol_i(squannit,1)
#print(vi)
#vtot = volume_mesh(squannit)
#print(vtot)

# correct all the radii so that the volume becomes that of a sphere with radius 1
# return a new mesh
def cor_volume(mesh):
    vol = volume_mesh(mesh)
    print('Volume {:.4f}'.format(vol))
    rad = pow(vol*3/(4*np.pi),1.0/3.0)
    print('radius of vol equ sphere {:.4f}'.format(rad))
    f = mesh.faces
    v = np.copy(mesh.vertices)
    v /= rad
    newmesh = pymesh.form_mesh(v, f)
    newmesh.add_attribute("face_area")
    newmesh.add_attribute("face_normal")
    newmesh.add_attribute("face_centroid")
    vol = volume_mesh(newmesh)
    print('new Volume {:.3f}'.format(vol))
    return newmesh

def nf_mesh(mesh):
    f = mesh.faces
    print('number of faces ',len(f))

def get_nf_mesh(mesh):
    f = mesh.faces
    return len(f)


# EMAHelperFunctions.py

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

## Libraries

import matplotlib.pyplot as plt
import numpy as np

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

## Exponential Moving Average (EMA) Functions

# Calculate the alpha value for a desired period.
def calculateAlpha(ema_period):
    alpha = 2.0 / (ema_period + 1)
    return alpha

# Returns the denominator
def getDenominator(number_of_terms):
    # bottom = 1 + (1-a) + (1-a)^2 + (1-a)^3 + ...
    a = calculateAlpha(number_of_terms)
    i = 0
    total = 0
    while i < number_of_terms:
        term = (1-a)**i
        total = total + term
        i = i + 1
    return total

# Returns the numerator
def getNumerator(price_data, price_data_index, number_of_terms):
    # top = p1 + (1-a)*p2 + (1-a)^2*p3 + (1-a)^3*p4 + ...
    a = calculateAlpha(number_of_terms)
    i = 0
    total = 0
    while i < number_of_terms:
        price = price_data[price_data_index - i]
        cof = (1-a)**i
        term = price * cof
        total = total + term
        i = i + 1
    return total

# Returns a single Exponential Moving Average value.
def getEMA(price_data, price_data_index, number_of_terms):
    if (number_of_terms - price_data_index) > 1:
        # There are too many terms for the given index.
        return 0
    else:
        top = getNumerator(price_data, price_data_index, number_of_terms)
        bottom = getDenominator(number_of_terms)
        EMA = np.array([top / bottom])
        return EMA

# Returns a list of all EMA values.
def getEMAdataset(price_data, number_of_terms):
    ema_data = np.zeros(np.size(price_data))
    i = 0
    while i < np.size(price_data):
        datum = getEMA(price_data, i, number_of_terms)
        ema_data[i] = datum
        i = i + 1
    return ema_data

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

## Plotting Function

# Plots 3 lines: raw data, EMA(period_1), EMA(period_2)
def calculateAndPlotEMA(data, ema_period_1, ema_period_2):
    ema_1 = getEMAdataset(data, ema_period_1)
    ema_2 = getEMAdataset(data, ema_period_2)
    x = np.arange(len(data))
    plt.plot(x, data)
    plt.plot(x, ema_1)
    plt.plot(x, ema_2)
    ema_legend_text_1 = "EMA(" + str(ema_period_1) + ")"
    ema_legend_text_2 = "EMA(" + str(ema_period_2) + ")"
    plt.legend(['Value', ema_legend_text_1, ema_legend_text_2])
    plt.title("Exponential Moving Averages")
    plt.grid(b=True, which='major', color='gray', linestyle=':')
    plt.show()

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

## Sine Wave Function

# Generates a sine wave.
def generateSineWave(period, amplitude, sigma, end):
    # Equations
    alpha = amplitude / 2.0
    beta = 2.0 * np.pi / period
    frequency = 1.0 / period
    x = np.arange(end + 1)

    # Formula
    y = alpha * np.sin(beta * x) + sigma
    return y


import sys
sys.path.append("..")
import argparse

import numpy as np
from robopy import *
from numpy.linalg import inv
from packages.rotations import *

def rotation_calc(alpha, beta, gama):
  rot_alpha = rotationz(alpha)
  rot_beta = rotationy(beta)
  rot_gama = rotationx(gama)
  return np.dot(np.dot(rot_alpha, rot_beta), rot_gama)

def reverse_euler_calc(r):
  beta = arcsin(-r[2, 0])
  alpha = arccos(r[0,0]/cos(beta))
  gama = arccos(r[2,2]/cos(beta))
  return alpha, beta, gama

def main():
  parser = argparse.ArgumentParser(description='Exercise 2A algorithms')

  parser.add_argument('-n', dest='exercise_num', default='a',
                      type=str, nargs='?',
                      help='Exercise Number')

  args = parser.parse_args()

  assert args.exercise_num in ['a', 'b', 'c', 'd'], 'exercise number is not valid'
  if args.exercise_num == 'a':
    print('solution i: ', rotation_calc(10, 20, 30))
    print('solution ii: ', rotation_calc(30, 90, -55))
    st = np.allclose(inv(rotation_calc(10, 20, 30)),rotation_calc(10, 20, 30).T)
    print('Are Rba == Rab-1 == Rab.T : ', str(st))

  elif args.exercise_num == 'b':
    print('10_20_30(alpha,beta,gama) angle rotated then reversed: ', reverse_euler_calc(rotation_calc(10, 20, 30)))
    print('30_90_-55(alpha,beta,gama) angle rotated then reversed: ', reverse_euler_calc(rotation_calc(30, 90, -55)))
  elif args.exercise_num == 'c':
    p = [1, 0, 1]
    print('P_A', np.dot(rotationy(20), p))
  elif args.exercise_num == 'd':
    print('Is my implemented rotationx calculation correct: ',
          str(np.allclose(rotationx(10), rotx(10, "deg"))))
    print('Is my implemented rotationy calculation correct: ',
          str(np.allclose(rotationy(20), roty(20, "deg"))))
    print('Is my implemented rotationz calculation correct: ',
          str(np.allclose(rotationz(30), rotz(30, "deg"))))
    print('Is my implemented homogeneouse transformation matrix calculation correct: ',
          str(np.allclose(rotation_calc(10, 20, 30), rpy2tr([30, 20, 10], unit='deg')[:3,:3])))
    print('Is my implemented reverse euler matrix calculation correct: ',
          str(np.allclose(reverse_euler_calc(rotation_calc(10, 20, 30)), tr2rpy(rotation_calc(10, 20, 30), 'deg')[0, ::-1])))

if __name__ == '__main__':
  main()


import numpy as np
import warnings
warnings.filterwarnings("ignore")
import sys
from PIL import Image, ImageDraw, ImageFilter

def koch_line(start, end, factor):
    """
    Segments a line to Koch line, creating fractals.

    :param tuple start:  (x, y) coordinates of the starting point
    :param tuple end: (x, y) coordinates of the end point
    :param float factor: the multiple of sixty degrees to rotate
    :returns tuple: tuple of all points of segmentation
    """

    # coordinates of the start
    x1, y1 = start[0], start[1]

    # coordinates of the end
    x2, y2 = end[0], end[1]

    # the length of the line
    l = np.sqrt((x2 - x1 )**2 + (y2 - y1 )**2)

    # first point: same as the start
    a = (x1, y1)

    # second point: one third in each direction from the first point
    b = (x1 + (x2 - x1 ) /3., y1 + (y2 - y1 ) /3.)

    # third point: rotation for multiple of 60 degrees
    c = (b[0] + l/ 3. * np.cos(factor * np.pi / 3.), b[1] + l / 3. * np.sin(factor * np.pi / 3.))

    # fourth point: two thirds in each direction from the first point
    d = (x1 + 2. * (x2 - x1) / 3., y1 + 2. * (y2 - y1) / 3.)

    # the last point
    e = end

    return {'a': a, 'b': b, 'c': c, 'd': d, 'e': e, 'factor': factor}

def koch_snowflake(degree, s=5.0):
    """Generates all lines for a Koch Snowflake with a given degree.

    :param int degree: how deep to go in the branching process
    :param float s: the length of the initial equilateral triangle
    :returns list: list of all lines that form the snowflake
    """
    # all lines of the snowflake
    lines = []

    # we rotate in multiples of 60 degrees
    sixty_degrees = np.pi / 3.

    # vertices of the initial equilateral triangle
    A = (0., 0.)
    B = (s, 0.)
    C = (s * np.cos(sixty_degrees), s * np.sin(sixty_degrees))

    # set the initial lines
    if degree == 0:
        lines.append(koch_line(A, B, 0))
        lines.append(koch_line(B, C, 2))
        lines.append(koch_line(C, A, 4))
    else:
        lines.append(koch_line(A, B, 5))
        lines.append(koch_line(B, C, 1))
        lines.append(koch_line(C, A, 3))

    for i in range(1, degree):
        # every lines produce 4 more lines
        for _ in range( 3 * 4**(i - 1)):
            line = lines.pop(0)
            factor = line['factor']

            lines.append(koch_line(line['a'], line['b'], factor % 6))  # a to b
            lines.append(koch_line(line['b'], line['c'], (factor - 1) % 6))  # b to c
            lines.append(koch_line(line['c'], line['d'], (factor + 1) % 6))  # d to c
            lines.append(koch_line(line['d'], line['e'], factor % 6))  # d to e

    return lines

def image_from_koch(degree, flake_size=5, image_size=(256, 256), blur_radius=3):
    base = Image.new("L", image_size)
    lines = koch_snowflake(degree, flake_size)
    x, y = [], []
    for l in lines:
        x.extend([l['a'][0], l['b'][0], l['c'][0], l['d'][0], l['e'][0]])
        y.extend([l['a'][1], l['b'][1], l['c'][1], l['d'][1], l['e'][1]])
    x = np.array(x) + image_size[1] / 2 - flake_size / 2
    y = np.array(y) + image_size[0] / 2 - flake_size / np.sqrt(3) / 2
    draw = ImageDraw.Draw(base)
    draw.polygon(list(zip(x, y)), fill=256)
    return np.asarray(base.filter(ImageFilter.BoxBlur(blur_radius))) / 256

def plot_koch():
    import matplotlib.pyplot as plt

    max_degree = 10
    fig, axs = plt.subplots(1, max_degree, figsize=(5*max_degree, 5), subplot_kw={'aspect': 'equal'})
    colors = ['#661D98', '#2CBDFE', '#47DBCD', '#F5B14C', '#960019', '#661D98', '#2CBDFE', '#47DBCD', '#F5B14C', '#960019']

    for d in range(max_degree):
        lines = koch_snowflake(degree=d)

        # extract the line coordinates
        x, y = [], []
        for l in lines:
            x.extend([l['a'][0], l['b'][0], l['c'][0], l['d'][0], l['e'][0]])
            y.extend([l['a'][1], l['b'][1], l['c'][1], l['d'][1], l['e'][1]])

        # remove all ticks and axes
        axs[d].set_xticks([], [])
        axs[d].set_yticks([], [])
        axs[d].spines['right'].set_visible(False)
        axs[d].spines['left'].set_visible(False)
        axs[d].spines['top'].set_visible(False)
        axs[d].spines['bottom'].set_visible(False)

        # fill the polygons
        axs[d].fill(x, y, facecolor=colors[d], edgecolor='black', lw=1)

    plt.show()

    plt.gcf().text(0.1, 0.15, 'by Vladimir Ilievski', fontsize=28, fontfamily='Verdana')
    plt.savefig('snowflakes_0_to_4.png', dpi=300, bbox_inches='tight')

if __name__ == '__main__':
    data = image_from_koch(4, 100)
    import matplotlib.pyplot as plt
    max_degree = 10
    fig, axs = plt.subplots(1, 1, subplot_kw={'aspect': 'equal'})
    plt.imshow(data)

    fig.show()
    pass


import pylab
import numpy
import math

# simple example of removing a frequency component from a
# two-frequency sine wave signal

# Since our input data is real, the negative frequency components
# don't include any new information, and are not interesting to us.
# The rfft routines understand this, and rfft takes n real points and
# returns n/2+1 complex output points.  The corresponding inverse
# knows this, and acts accordingly.
#
# these are the routines we want for real valued data

# note that the scipy version of rfft returns that data differently

# M. Zingale (2013-02-28)

def twoFreqSine(npts):

    # a pure sine with no phase shift will result in pure imaginary
    # signal
    f_0 = 0.2
    f_1 = 0.5

    xmax = 10.0/f_0

    # we call with endpoint=False -- if we include the endpoint, then for
    # a periodic function, the first and last point are identical -- this
    # shows up as a signal in the FFT.
    xx = numpy.linspace(0.0, xmax, npts, endpoint=False)

    # input frequency
    f_0 = 0.2

    f = 0.5*(numpy.sin(2.0*math.pi*f_0*xx) + numpy.sin(2.0*math.pi*f_1*xx))

    return xx, f

npts = 256

xx, f = twoFreqSine(npts)

# Forward transform: f(x) -> F(k)

# normalization factor: the 2 here comes from the fact that we neglect
# the negative portion of frequency space because our input function
# is real
norm = 2.0/npts
fk = norm*numpy.fft.rfft(f)

ofk_r = fk.real.copy()
ofk_i = fk.imag.copy()

# the fftfreq returns the postive and negative (and 0) frequencies
# the newer versions of numpy (>=1.8) have an rfftfreq() function
# that really does what we want.
k = numpy.fft.fftfreq(len(xx))[range(0,npts/2+1)]

# the last element is negative, because of the symmetry, but should
# be positive (see http://docs.scipy.org/doc/numpy-dev/reference/generated/numpy.fft.rfftfreq.html)
k[-1] *= -1

# since we don't include the endpoint in xx, to normalize things, we need
# max(xx) + dx to get the true range
kfreq = k*npts/(max(xx) + xx[1])

# filter all frequencies > 0.4
fk[kfreq > 0.4] = 0.0

# element 0 of fk is the DC component
fk_r = fk.real
fk_i = fk.imag

# Inverse transform: F(k) -> f(x)
fkinv = numpy.fft.irfft(fk/norm)

# PLOT
pylab.rc("font", size=9)

pylab.subplot(411)

pylab.plot(xx, f)
pylab.xlabel("x")
pylab.ylabel("f(x)")

pylab.subplot(412)

pylab.plot(kfreq, ofk_r)
pylab.plot(kfreq, ofk_i, ls=":")
pylab.xlabel(r"$\nu_k$")
pylab.ylabel("original F(k)")

#pylab.xlim(0,1)

pylab.subplot(413)

pylab.plot(kfreq, fk_r)
pylab.plot(kfreq, fk_i, ls=":")
pylab.xlabel(r"$\nu_k$")
pylab.ylabel("filtered F(k)")

pylab.subplot(414)

pylab.plot(xx, fkinv.real)
pylab.xlabel("x")
pylab.ylabel(r"inverse filtered F(k)")

pylab.tight_layout()

f = pylab.gcf()
f.set_size_inches(9.0,8.0)

pylab.savefig("simple-filter.png", bbox_inches='tight', pad_inches=0.33)


# %% [markdown]
# # Bayesian Linear Regression
# In this post I talk about reformulating linear regression in a Bayesian framework.
# This gives us the notion of epistemic uncertainty which allows us to generate probabilistic model predictions.
# I formulate a model class which can perform linear regression via Bayes rule updates.
# We show the results are the same as from the `statsmodels` library.
# I will also show some of the benefits of the sequential bayesian approach.
# %%
import pandas as pd
import numpy as np
import statsmodels.api as sm
import time
from scipy.stats import norm
import matplotlib.pyplot as plt

plt.style.use("seaborn-whitegrid")
# %% [markdown]
# ## Generate some dummy data
# We generate some ideal data for a linear regression model so that we know the true coefficients for this example.
# %%
n = 10000
m = 2
np.random.seed(3)

noise_std = 2

x = np.random.uniform(-1, 1, size=(n, m))
x = np.hstack([np.ones(shape=(n, 1)), x])
w = np.random.uniform(-1, 1, size=(m + 1, 1))
y_true = x @ w
y = y_true + np.random.normal(loc=0, scale=noise_std, size=(n, 1))

print("True coefficients:\n", w)
# %% [markdown]
# ## Fit a linear regression with `statsmodels`
# The implementation of linear regression in `statsmodels` returns the standard error
# of the fitted coefficients.
# %%
results = sm.OLS(y, x).fit()
print(results.summary())

# extract coefficient distributions
w_sm_mu = results.params
w_sm_std = np.sqrt(np.diag(results.normalized_cov_params))
# %% [markdown]
# If we received extra data we would have no choice but to retrain the model from scratch.
# In the following we train the model with an increasing fraction of the data and time each instance.
# %%
idx = np.floor(np.linspace(0, x.shape[0], num=50))[1:]
time_iter = []

params_mu = []
params_std = []
for end_idx in idx:
    t0 = time.process_time()
    results = sm.OLS(y[: int(end_idx)], x[: int(end_idx)]).fit()
    time_iter.append(time.process_time() - t0)

    params_mu.append(results.params)
    params_std.append(np.sqrt(np.diag(results.normalized_cov_params)))

params_mu = pd.DataFrame(params_mu, index=idx)
params_std = pd.DataFrame(params_std, index=idx, columns=params_mu.columns)
# %% [markdown]
# As expected, we can see that training the model with increasing lengths of data causes an increasing in fitting time.
# %%
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(idx, time_iter, label="statsmodels")
ax.set_ylabel("Time taken (s)")
ax.set_xlabel("No. training rows")
plt.legend()
plt.show()
# %% [markdown]
# We can also show that with increasing data our estimates of the coefficients become more confident
# as the standard error shrinks.
# This is a measure of our epistemic (model) uncertainty.
# %%
fig, ax = plt.subplots(ncols=2, figsize=(10, 6))
params_mu.plot(ax=ax[0])
params_std.plot(ax=ax[1])
ax[0].set_xlabel("No. training rows")
ax[0].set_ylabel("Coefficient means")
ax[1].set_ylabel("Coefficient standard dev.")
plt.show()
# %% [markdown]
# ## Sequential bayesian regression
# Another way of approaching this problem is with sequential Bayesian regression.
# This method follows Bayes theorem, where we have a prior distribution or estimates of our regression coefficients.
# We then update those prior distribution with data we observe to get a posterior distribution.
# We can then consider those posterior distributions as our new prior distributions and repeat the process.
#
# Linear regression produces a multivariate normal distribution over the resulting coefficient estimates.
# The conjugate prior to this is also a multivariate normal distribution prior.
# As such we can formulate an analytical expression for the Bayes rule posterior update.
#
# The update rules I used were taken from:
# [https://cedar.buffalo.edu/~srihari/CSE574/Chap3/3.4-BayesianRegression.pdf](https://cedar.buffalo.edu/~srihari/CSE574/Chap3/3.4-BayesianRegression.pdf)
#
# ### Model definition
# We can build this as a class withi a similar API to sklearn models:
# %%
class BayesLinearRegressor:
    def __init__(
        self, number_of_features, mean=None, cov=None, alpha=1e6, beta=1
    ):
        # prior distribution on weights
        if mean is None:
            self.mean = np.array([[0] * (number_of_features)], dtype=np.float).T

        if cov is None:
            self.cov = alpha * np.identity(number_of_features)
            self.cov_inv = np.linalg.inv(self.cov)

        self.beta = beta  # process noise

    def fit(self, x, y):
        return self.update(x, y)

    def update(self, x, y):
        """
        Perform a bayesian update step
        """
        if len(x.shape) == 1:
            x = x[np.newaxis, :]
        if len(y.shape) == 1:
            y = y[:, np.newaxis]

        # update state of covariance and means
        cov_n_inv = self.cov_inv + self.beta * x.T @ x
        cov_n = np.linalg.inv(cov_n_inv)
        mean_n = cov_n @ (self.cov_inv @ self.mean + self.beta * x.T @ y)

        self.cov_inv = cov_n_inv
        self.cov = cov_n
        self.mean = mean_n

    def predict(self, x):
        mean = x @ self.mean
        scale = np.sqrt(np.sum(x @ self.cov @ x.T, axis=1))
        return mean, scale

    @property
    def coef_(self):
        return self.mean

    @property
    def scale_(self):
        return np.sqrt(np.diag(self.cov))

# %% [markdown]
# We can train the model as follows.
# We use `numpy` testing to confirm the coefficient we get are equal to those of `statsmodels`.
# %%
bayes_linear_regression = BayesLinearRegressor(x.shape[1])
bayes_linear_regression.fit(x, y)
np.testing.assert_array_almost_equal(
    bayes_linear_regression.coef_, params_mu.tail(1).transpose().to_numpy()
)
np.testing.assert_array_almost_equal(
    bayes_linear_regression.scale_, params_std.tail(1).to_numpy().flatten()
)
# %% [markdown]
# ### Sequential training
# We can now train the model with chunks of data.
# We will time the models fitting and track the coefficients with extra data.
# %%
bayes_linear_regression = BayesLinearRegressor(x.shape[1])

time_iter_seq = []
params_mu_seq = []
params_std_seq = []
for i, end_idx in enumerate(idx):
    t0 = time.process_time()
    if i > 0:
        start_idx = int(idx[i - 1])
    else:
        start_idx = 0
    bayes_linear_regression.update(
        x[start_idx : int(end_idx)],
        y[start_idx : int(end_idx)],
    )
    time_iter_seq.append(time.process_time() - t0)

    params_mu_seq.append(bayes_linear_regression.coef_.flatten())
    params_std_seq.append(bayes_linear_regression.scale_)

params_mu_seq = pd.DataFrame(
    params_mu_seq, index=idx, columns=params_mu.columns
)
params_std_seq = pd.DataFrame(
    params_std_seq, index=idx, columns=params_mu.columns
)
# %% [markdown]
# The time taken to train with each chunk is approximately the same.
# As such, we can fit an initial model and continue to update it as an alternative to fitting a single large model.
# %%
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(idx, time_iter, label="statsmodels")
ax.plot(idx, time_iter_seq, label="sequential")
# ax.plot(idx, np.cumsum(time_iter_seq), label="cumulative_sequential")
ax.set_ylabel("Time taken (s)")
ax.set_xlabel("No. training rows")
plt.legend()
plt.show()
# %% [markdown]
# The model coefficients behave as before.
# E.g. we can similarly show that the standard deviation of the coefficients reduces with data length.
# %%
fig, ax = plt.subplots(ncols=2, figsize=(10, 6))
params_mu_seq.plot(ax=ax[0])
params_std_seq.plot(ax=ax[1])
ax[0].set_xlabel("No. training rows")
ax[0].set_ylabel("Coefficient means")
ax[1].set_ylabel("Coefficient standard dev.")
plt.show()
# %% [markdown]
# ### Posteriors vs Priors
# One of the benefits of using bayesian linear regression is the ability to apply prior distributions
# on the model coefficients.
# To demonstrate this we use a prior with a much smaller variance, as such it is no longer uninformed.
# We fit the model and plot the pdf of the prior and posterior.
# The posteriors evidently converge to the true coefficients and have a tight distribution.
# %%
bayes_linear_regression = BayesLinearRegressor(x.shape[1], alpha=0.5)
prior_mu = bayes_linear_regression.coef_
prior_std = bayes_linear_regression.scale_
bayes_linear_regression.fit(x, y)

def norm_max(x):
    return x / x.max()

x_range = np.linspace(-3, 3, num=1000)
fig, ax = plt.subplots(nrows=m + 1, figsize=(10, 6))
for idx in range(m + 1):
    ax[idx].plot(
        x_range,
        norm_max(norm.pdf(x_range, loc=prior_mu[idx], scale=prior_std[idx])),
    )
    ax[idx].plot(
        x_range,
        norm_max(
            norm.pdf(
                x_range,
                loc=params_mu_seq.iloc[-1, idx],
                scale=params_std_seq.iloc[-1, idx],
            )
        ),
    )
    ax[idx].set_ylabel(f"$~P(z_{idx})$")
# %% [markdown]
# ### Prediction uncertainty
# The distribution of our coefficients gives us a distribution for our model predictions as well.
#
# The predict method of the `BayesLinearRegressor` class returns a standard deviation for each point.
# We can then plot a few of those points with this confidence shaded.
# This only represent epistemic uncertainty - i.e. uncertainty from our model coefficients,
# not uncertainty from the data generating process.
# %%
bayes_linear_regression = BayesLinearRegressor(x.shape[1])
bayes_linear_regression.fit(x, y)
pred_mu, pred_scale = bayes_linear_regression.predict(x[:100])

sort_id = np.argsort(pred_mu.flatten())

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(pred_mu[sort_id].flatten(), ".")

ax.fill_between(
    np.arange(100),
    pred_mu[sort_id].flatten() - pred_scale[sort_id],
    pred_mu[sort_id].flatten() + pred_scale[sort_id],
    alpha=0.3,
)
# %% [markdown]
# ### Adaptive learning
# By updating sequential with data as it appears we can learn from data that may not be stationary.
# A sequential model can do some form of tracking if the coefficients need to change with time.
#
# More on this to come...

import numpy,from scipy,import statsmodels


# -*- coding: utf-8 -*-
"""
Created on Mon Oct 18 08:28:29 2021

@author: Ezra
"""

import numpy as np
import matplotlib.pyplot as plt

def f(x):
    #return x*x*np.log(x)
    return np.sin(x)

def trap(a,b,n):
    '''
    Trapazoidal method of integration

    Parameters
    ----------
    a : TYPE
        DESCRIPTION.
    b : TYPE
        DESCRIPTION.
    n : TYPE
        DESCRIPTION.

    Returns
    -------
    xi : TYPE
        DESCRIPTION.

    '''
    h = (b-a)/n
    xi0=f(a) + f(b)
    for i in range(1,n):
        x = a + i*h
        xi0 = xi0 + 2*f(x)
    xi = (h*xi0)/2
    return xi

def simp(a,b,n):
    '''
    simpsons method

    Parameters
    ----------
    a : left limit of integration
    b : right limit of integration
    n : number of subdivision for composite (even).

    Returns
    -------
    None.

    '''
    h = (b-a)/float(n)
    xi0 = f(a) + f(b)
    xi1 = 0
    xi2 = 0
    for i in range(1,n):
        x=a+i*h
        if i%2==0:
            xi2=xi2+f(x)
        else:
            xi1=xi1+f(x)
    xi=h*(xi0+2*xi2+4*xi1)/3
    return xi

def romT4(a,b,n):
    return((4*trap(a,b,n)-trap(a,b,n))/3)

def romT6(a,b,n):
    return((16*romT4(a,b,2*n)-romT4(a,b,n))/15)

def mid(a,b,n):
    h=(b-a)/(n+2)
    xi0=0
    for i in range(int(n//2)+1):
        x=a+(2*i+1)*h
        xi0=xi0+f(x)
    xi=2*h*xi0
    return xi

a=0
b=2
soln=np.cos(a)-np.cos(b)

def err(x,y):
    z = abs(x-y)/y
    return z

est1 = trap(0,2,10)
est2 = simp(0,2,10)
est3 = mid(0,2,10)
est4 = romT4(0,2,10)
est5 = romT6(0,2,10)
print(est1)
print(est2)
print(est3)
print(est4)
print(est5)

trapEst = []
midEst = []
simEst = []
rombT4Est = []
rombT6Est = []

n=[2,10,40,50,60,100,200]

for i in range(len(n)):
    trapEst.append(err(trap(a,b,n[i]),soln))
    midEst.append(err(mid(a,b,n[i]),soln))
    simEst.append(err(simp(a,b,n[i]),soln))
    rombT4Est.append(err(romT4(a,b,n[i]),soln))
    rombT6Est.append(err(romT6(a,b,n[i]),soln))

# print((f(1.5)+f(1))/2*.5)

plt.figure(0)
plt.semilogy(n,trapEst,label = 'Trapezoid')
plt.semilogy(n,midEst,label = 'Midpoint')
plt.semilogy(n,simEst,label = 'Simpson')
plt.semilogy(n,rombT4Est,label = 'Romber Trap 4')
plt.semilogy(n,rombT6Est,label= 'Romber Trap 6')


'''
Blum Blum Shub (B.B.S.) is a pseudorandom number generator proposed in 1986 by Lenore Blum,
Manuel Blum and Michael Shub[1] that is derived from Michael O. Rabin's one-way function.
Pseudorandom number generator (source: https://en.wikipedia.org/wiki/Blum_Blum_Shub)
'''
from sympy import isprime
import random

def BBSgenerator(nbits=8):
    '''
    BBS also called CSPRG (source: https://blog.csdn.net/android_jiangjun/article/details/80648184).
    @param nbits: number of bits of the number. e.g., 8-bits: 0~255
    '''

    def generatePQ(start=None):
        '''
        Get the big prime number p and q.
        In BBS algorithm, p ≡ q ≡ 3(mod 4)
        @param start: start of finding big prime,
                    default 1,000,000 (1 million)
        '''
        x = 1000000 if start is None else start
        p = q = None
        while (True):
            if isprime(x) and (x % 4) == 3:
                if p is None:
                    p = x
                elif q is None:
                    q = x
                else:
                    break
            x += 1
        return p, q

    # initialize p and q
    p, q = generatePQ()
    n = p * q
    s = p
    while (s % p) == 0 or (s % q) == 0:
        s = random.SystemRandom().randint(p+q, p+p+q+q)
    x = (s * s) % n
    num = 0
    for i in range(nbits):
        x = (x*x) % n
        num = num * 2 + (x % 2)
    return num

if __name__ == "__main__":
    print("Testing BBS generator...")
    # test: generate 10 random integers
    for i in range(10):
        print(BBSgenerator(16))


import numpy as np
from numba import jit

@jit(nopython=True, nogil=True, cache=True)
def soft_thresh(lam, x):  # pragma: no cover
    r"""

    soft_thresh(lam, x)

    Soft thresholding operator.  Takes a numpy ndarray of floats and returns and element-wise soft-thesholded version.

    Element-wise, the soft-thresholding operator :math:`S_\lambda(x)` is given by:

    .. math::

        S_\lambda(x) =
            \begin{cases}
                x + \lambda, & \text{if} \quad x < -\lambda \\
                0          , & \text{if} \quad -\lambda < x < \lambda \\
                x - \lambda, & \text{if} \quad x > \lambda
            \end{cases}

    where :math:`\lambda` is a scalar tresholding parameter.

    Args:
        lam (float): threshold value
        x (numpy.ndarray): array of floats to be soft thresholded

    Returns:
        numpy.ndarray: soft thresholded array of floats
    """
    if lam == 0.0:
        return x
    else:
        return np.sign(x) * np.maximum(np.abs(x) - lam, 0)


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

import sympy as sp
from numpy import exp
from trapezoidal import trapezoidal
from midpoint import midpoint
from simpsons import simpson
from gaussian import gauss
from scipy.integrate import fixed_quad

def f(y):
    return exp(-y**2)

a = 0
b = 2
print('    n        midpoint          trapezoidal          simpson          gaussian')
for i in range(1, 16):
    n = 2**i
    m = midpoint(f, a, b, n)
    t = trapezoidal(f, a, b, n)
    s = simpson(f, a, b, n)
    #g = gauss(f, a, b, n)
    g = fixed_quad(f,a,b,n=n)[0]
    print('%7d %.16f %.16f %.16f %.16f' % (n, m, t, s, g))

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

a = 2;  b = 6;
n = 100

numerical_trap = trapezoidal(g, a, b, n)
numerical_mid  = midpoint(g, a, b, n)

# Compute exact integral by sympy
x = sp.symbols('x')
F = sp.integrate(g(x))
exact = F.subs(x, b) - F.subs(x, a)
exact = exact.evalf()

error_trap = abs(numerical_trap - exact)
error_mid  = abs(numerical_mid - exact)

print('For n = %d, we get:' % (n))
print('Numerical trapezoid: %g , Error: %g' % \
                                 (numerical_trap,error_trap))
print('Numerical midpoint: %g , Error: %g' % \
                                 (numerical_mid,error_mid))


import numpy as np
import numpy.linalg as LA

def ssh_hamiltonian(k, t1, t2, real_basis=True):
    """ Returns the SSH Hamiltonian at k with hopping amplitudes t1 and t2.

    Args:
        k (float): point in momentum space.
        t1 (float): hopping parameter.
        t2 (float): hopping paramter.
        real_basis (bool): if True, returns Hamiltonian in real basis. Else, use complex basis.

    Returns:
        np.array: the SSH Hamiltonian.
    """
    if real_basis:
        hamiltonian = np.zeros((2, 2), dtype=np.double)
        hamiltonian[0, 0] = t2 * np.sin(k)
        hamiltonian[1, 1] = -t2 * np.sin(k)
        hamiltonian[0, 1] = t1 + t2 * np.cos(k)
        hamiltonian[1, 0] = t1 + t2 * np.cos(k)
        return hamiltonian
    else:
        j = complex(0, 1)
        hamiltonian = np.zeros((2, 2), dtype=np.cdouble)
        hamiltonian[0, 1] = t1 + t2 * np.cos(k) - j * t2 * np.sin(k)
        hamiltonian[1, 0] = t1 + t2 * np.cos(k) + j * t2 * np.sin(k)
        return hamiltonian

def get_state(hamiltonian, idx_list):
    """Return energy eigenstates of hamiltonian for band index in the idx_list

    Args:
        Hamiltonian (np.array): the Hamiltonian matrix
        idx_list (list): list of band indices, the lowest energy band being 0

    Returns:
        np.array: energy eigenstates (column vectors) arragned according to idx_list
    """
    eigval, eigvec = LA.eigh(hamiltonian)
    return eigvec[:, idx_list]

def distance(state1, state2):
    """Return the quantum distance between state1 and state2.

    Args:
        state1 (np.array): a quantum state
        state2 (np.array): a quantum state
    Returns:
        float: quantum distance between state1 and state2, sqrt(1-abs(<state1|state2>)**2)
    """
    return np.sqrt(abs(1.0 - abs(state1.conjugate().transpose() @ state2).item()**2))

def get_state_list(t1, t2, idx_list, mesh_size):
    """ Returns list of states
    Args:
        t1, t2 (float): parameters in the Hamiltonian
        idx_list (list): list of indices for which we create the state list
        mesh_size: number of k points (uniform mesh)

    Returns:
        list: list of states  (in idx_list) for each k point
    """
    state_list = []
    k_mesh = np.linspace(0, 2. * np.pi, num=mesh_size, endpoint=False)
    for k in k_mesh:
        state_list.append(get_state(ssh_hamiltonian(k, t1, t2), idx_list))
    return state_list


'''
Find the minimum aggregate area for a square and a circle formed by cutting a wire in two pieces.
'''

import math
from sympy import Derivative, symbols, solve, sympify
from collections import namedtuple

Pieces = namedtuple( 'Pieces', [ 'circumference', 'side'] )

def cut_wire( l ):
	# Side, Circumference
	s, c = symbols( 's, c' )
	# V = s^2 ( square ) + pi * ( c / 2pi )^2
	A = ( s / 4 ) ** 2 + c**2 / ( 4 * math.pi )

	# c + s = l
	L = c + s - l

	# Solve for c in terms of s
	sC = solve( L, s )[ 0 ]

	# A1 = area in terms of a single variable
	A1 = A.subs( s, sC )

	# Now single variable, take The derivitave with respect to c.
	dA = Derivative( A1, c ).doit()
	slns = solve( dA )

	min_c = slns[ 0 ]
	min_s = l - min_c

	return Pieces( circumference = min_c, side = min_s )

def area_sqare( s ):
	return s**2

def area_circle( c ):
	return c**2 / ( 4 * math.pi )

if __name__ == '__main__':

	try:
		l = float( input( 'Enter the length of the wire to cut: ') )
	except ValueError:
		print( 'Error parsing values' )
	else:
		print( 'wire length: {0}\n'.format( l ) )

		pieces = cut_wire( l )
		area_circle = area_circle( pieces.circumference )
		area_sqare = area_sqare( pieces.side )

		print( 'Mimimum Area: {0}'.format( area_circle + area_sqare ) )
		print( 'Circle Area: {0}'.format( area_circle ) )
		print( 'Square Area: {0}'.format( area_sqare ) )