import numpy as np
import matplotlib.pyplot as plt
import solver

# right hand side of linear sde model equation dX = aX dt + bX dW
# returns deterministic and stochastic part of right hand side
def linearSDE (X, t):

    # model parameters
    a = 5.0
    b = 1.0

    f_det   = np.array([a * X])
    f_stoch = np.array([b * X])

    return (f_det, f_stoch)

# returns analytic solution to linear sde dX = aX dt + bX dW
def X_analytic (t, Wt):

    # model parameters
    a  = 5.0
    b  = 1.0

    # initial state
    x0 = 1.0

    return (x0 * np.exp((a - np.power(b, 2) / 2.0) * t + b * Wt))

# returns time vector and numerical solution of sde
def numInt (x0, Ttrans, Teval, dt, sqrt_dt, Ito_dt, method):

    s       = solver.SDE_Solver()
    time, X = s.solveSDE(linearSDE, x0, Ttrans, Teval, dt, 1, method)
    X       = X[0, :]

    return (time, X)

# plots time series for a single realization
def plotSingle ():

    # initial state
    x0 = 1.0

    # parameters for numerical integration
    Ttrans  = 0.0
    Teval   = 1.0
    dt      = 1e-3
    sqrt_dt = np.sqrt(dt)                     # stdev for Wiener process
    Ito_dt  = np.sqrt(np.power(dt, 3) / 3.0)  # stdev for Itô integral
    method  = "explicitStrongOrder1"

    # generate data
    time, X = numInt(x0, Ttrans, Teval, dt, sqrt_dt, Ito_dt, method)

    # plot data
    plt.figure(figsize=(8, 6))
    plt.suptitle("linear sde: time series")
    plt.title(r"$dX_t = aX_t\,dt + bX_t\,dW_t$", fontsize=14)
    plt.plot(time, X)
    plt.xlabel(r"time $t$")
    plt.ylabel(r"$X(t)$")
    plt.grid(color="lightgray")
    plt.show()

# plots averaged time series and global error with respect to the analytic solution
def plotMulti ():

    # initial state
    x0 = 1.0

    # parameters for numerical integration
    Ttrans  = 0.0
    Teval   = 1.0
    dt      = 1e-3
    sqrt_dt = np.sqrt(dt)                     # stdev for Wiener process
    Ito_dt  = np.sqrt(np.power(dt, 3) / 3.0)  # stdev for Itô integral
    nSteps  = int((Ttrans + Teval) / dt)
    nSims   = 10

    # initialize solution arrays
    Wt      = np.zeros((nSteps, nSims))
    Ana     = np.zeros((nSteps, nSims))
    Euler   = np.zeros((nSteps, nSims))
    RuKu    = np.zeros((nSteps, nSims))
    SO1     = np.zeros((nSteps, nSims))
    SO15    = np.zeros((nSteps, nSims))

    # generate gaussian noise for analytic solution through integration
    # of Wiener increments dW
    for i in range(1, nSteps):
        Wt[i, :] = Wt[i-1, :] + np.random.normal(loc=0.0, scale=sqrt_dt, size=nSims)

    # generate data
    for i in range(nSims):

        print(i+1, " of ", nSims, " simulations")

        # generate data
        time, X_Eu   = numInt(x0, Ttrans, Teval, dt, sqrt_dt, Ito_dt, "explicitEulerMaruyama")
        time, X_RuKu = numInt(x0, Ttrans, Teval, dt, sqrt_dt, Ito_dt, "explicitRungeKutta")
        time, X_SO1  = numInt(x0, Ttrans, Teval, dt, sqrt_dt, Ito_dt, "explicitStrongOrder1")
        time, X_SO15 = numInt(x0, Ttrans, Teval, dt, sqrt_dt, Ito_dt, "explicitStrongOrder15")

        Ana[:, i]    = X_analytic(time, Wt[:, i])
        RuKu[:, i]   = X_RuKu
        Euler[:, i]  = X_Eu
        SO1[:, i]    = X_SO1
        SO15[:, i]   = X_SO15

    # calculation of averaged time series
    AnaMean   = np.mean(Ana, axis=1)
    EulerMean = np.mean(Euler, axis=1)
    RuKuMean  = np.mean(RuKu, axis=1)
    SO1Mean   = np.mean(SO1, axis=1)
    SO15Mean  = np.mean(SO15, axis=1)

    # calculation of global deviation from analytic solution
    errEuler  = np.sqrt(np.sum(np.square(AnaMean - EulerMean)))
    errRuKu   = np.sqrt(np.sum(np.square(AnaMean - RuKuMean)))
    errSO1    = np.sqrt(np.sum(np.square(AnaMean - SO1Mean)))
    errSO15   = np.sqrt(np.sum(np.square(AnaMean - SO15Mean)))

    # plot data
    plt.figure(figsize=(12, 9))
    plt.suptitle("linear sde: averaged time series for " + str(nSims) + " simulations, " + "dt = " + str(dt))
    plt.title(r"$dX_t = aX_t\,dt + bX_t\,dW_t$", fontsize=14)

    #~ for i in range(nSims):
        #~ plt.plot(time, Ana[:, i], color="blue", alpha=0.05)
        #~ plt.plot(time, Euler[:, i], color="orange", alpha=0.05)
        #~ plt.plot(time, SO1[:, i], color="green", alpha=0.05)
        #~ plt.plot(time, SO15[:, i], color="red", alpha=0.05)

    plt.plot(time, AnaMean, color="darkblue", label=r"analytic: $X_t = X_0\,\exp[(a - 0.5\,b^2)t + b\,W_t]$")
    plt.plot(time, EulerMean, color="darkorange", label="Euler-Maruyama, rms = " + str(errEuler))
    plt.plot(time, RuKuMean, color="purple", label="Runge-Kutta, rms = " + str(errRuKu))
    plt.plot(time, SO1Mean, color="darkgreen", label="strong order 1.0, rms = " + str(errSO1))
    plt.plot(time, SO15Mean, color="darkred", label="strong order 1.5, rms = " + str(errSO15))

    plt.xlabel(r"time $t$")
    plt.ylabel(r"$X(t)$")
    plt.legend(loc="upper left")
    plt.grid(color="lightgray")

    plt.show()

plotSingle()
plotMulti()


import numpy
import csv
import logging
import matplotlib.pyplot as plt
from matplotlib import style
import matplotlib.cm as cm
import random
import time
logging.basicConfig(filename="test.log", level=logging.DEBUG)
style.use('ggplot')

class kMeans:
    '''Initialize K-Means parameters @k - k,@r - number of iterations,@t - tolerance'''

    def __init__(self, k=3, r=10, t=0.0001):
        self.k = k
        self.r = r
        self.t = t
        self.rss = numpy.empty((r, 1))

    '''Cluster data into k clurster.@data - (n_rows,n_features)'''

    def clusterData(self, data):
        self.centroids = {}

        for c in range(self.k):
            randomPoint = random.randrange(1, len(data))
            self.centroids[c] = data[randomPoint]
        # end

        for r in range(self.r):
            self.clusters = {}

            for c in range(self.k):
                self.clusters[c] = []
            # end

            for d in data:
                distances = [numpy.linalg.norm(
                    d - self.centroids[clusterMean]) for clusterMean in self.centroids]
                assignment = distances.index(min(distances))
                self.clusters[assignment].append(d)
            # end
            oldCentroids = dict(self.centroids)
            for c in self.clusters:
                self.centroids[c] = numpy.average(self.clusters[c], axis=0)
            # end
                converged = True

            for centroid in self.centroids:
                old = oldCentroids[centroid]
                current = self.centroids[centroid]
                if numpy.sum((current-old)/old*100) > self.t:
                    converged = False
                # end
            if converged:
                break
            # end
            self.rss[r, 0] = sum(((x - self.centroids[c])**2).sum()
                                 for x, c in zip(data, self.clusters))
        # end
    # end

'''Starting point'''

def main():
    random.seed(time.time())
    print("Loading data set...")
    data = numpy.loadtxt('GMM_dataset.txt', dtype=float)
    km = kMeans(k=5, r=10, t=0.001)
    km.clusterData(data)
    print("K Means Centroids - ", km.centroids)
    print("K Means Sum Of Squared Error - ", km.rss)
    # Plotting starts here
    plt.subplot(121)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.title("K Means Clustering With K = "+str(km.k))
    colors = iter(cm.gist_rainbow(numpy.linspace(0, 1, len(km.clusters))))
    for centroid in km.centroids:
        plt.scatter(km.centroids[centroid][0],
                    km.centroids[centroid][1], color="k", s=100, marker="*")

    for classification in km.clusters:
        color = next(colors)
        for features in km.clusters[classification]:
            plt.scatter(features[0], features[1],
                        color=color, s=10, marker="o")
        # end
    # end
    plt.subplot(122)
    plt.plot(km.rss)
    plt.title("Sum Of Squared Error vs Iterations")
    plt.xlabel("Iterations")
    plt.ylabel("Sum Of Squared Error")
    plt.show()

if __name__ == "__main__":
    main()


import sys
import numpy as np
import math

default_precision = 0.5/100

def BER(M, gamma):
    L = np.sqrt(M)
    k = 2 * (L - 1) / (np.log2(M) * L)
    gamma = np.power(10, gamma / 10)
    arg = np.sqrt(3 * np.log2(M) * gamma / (2*M - 2))
    return k * math.erfc(arg)

def search_ber_qam(target: float, constellation_size: int, precision = default_precision):
    lb = 0
    rb = 100
    mb = lb + (rb - lb) / 2
    delta = 1
    n = 0;

    while(abs(delta) > precision):
        n += 1
        mb = lb + (rb - lb)/2
        B = BER(constellation_size, mb)
        delta = (target - B) / B if B != 0 else 1e6 # Avoid division by infinity
        delta = min(delta, 1e6)
        # print("[{}] T: {} C: {:1.2e} \t({}%) \t({:1.2f} {:1.2f} {:1.2f})".format(n, target, B, int(100*delta), lb, mb, rb))

        if(delta > 0):
            rb = mb
        else:
            lb = mb

    return mb

if __name__ == "__main__":
    if len(sys.argv) < 4:
        print("Usage:\n{} constellation_size target_ber_base target_ber_power [epsilon]".format(sys.argv[0]))
        print("\ni.e:\n{} 16 10 -6 returns the required Eb/No to achieve BER < 10^-6 on a 16 QAM system".format(sys.argv[0]))
        exit()

    M     = int( sys.argv[1] )
    base  = int( sys.argv[2] )
    power = int( sys.argv[3] )

    if( len(sys.argv) == 5 ):
        default_precision = float(sys.argv[4])

    assert( default_precision < 1.0 )
    assert( M > 1 )
    assert( pow(base, power) > 0 )
    target = pow(base, power)
    gamma = search_ber_qam(target, M)
    print("Found BER = {:1.2e} at Eb/No = {:.2f} dB".format(BER(M, gamma), gamma))


import numpy as np

def sum_of_squares(params):
    """ Implement Sum of Squared Parameters function.

    Function description: https://www.sfu.ca/~ssurjano/sumsqu.html.

    Args:
        params (pandas.DataFrame): Must have the column "value" containing
        input values for parameters. Accepts arbitrary numbers of input values.

    Returns:
        integer:  Sum of Squared Parameters function output.

    """
    params["multiplier"] = np.arange(1, len(params.index) + 1)
    return (params["multiplier"] * params["value"] ** 2).sum()

def trid(params):
    """ Implement Trid function.

    Function description: https://www.sfu.ca/~ssurjano/trid.html.

    Args:
        params (pandas.DataFrame): Must have the column "value" containing
        input values for parameters. Accepts arbitrary numbers of input values.

    Returns:
        integer:  Trid function output.

    """
    params_np = params["value"].to_numpy()
    return ((params["value"] - 1) ** 2).sum() - (
        params["value"][1:] * params_np[:-1]
    ).sum()

def rotated_hyper_ellipsoid(params):
    """ Implement Rotated Hyper Ellipsoid function.

    Function description: https://www.sfu.ca/~ssurjano/rothyp.html.

    Args:
        params (pandas.DataFrame): Must have the column "value" containing
        input values for parameters. Accepts arbitrary numbers of input values.

    Returns:
        integer: Rotated Hyper Ellipsoid function output.

    """
    val = 0
    for i in range(len(params["value"])):
        val += (params["value"][: i + 1] ** 2).sum()

    return val

def rosenbrock(params):
    """ Implement Rosenbrock function.

    Function description: https://www.sfu.ca/~ssurjano/rosen.html.

    Args:
        params (pandas.DataFrame): Must have the column "value" containing
        input values for parameters. Accepts arbitrary numbers of input values.

    Returns:
        integer:  Rosenbrock function output.

    """
    params_np = params["value"].to_numpy()
    r1 = ((params_np[1:] - params_np[:-1] ** 2) ** 2).sum() * 100
    r2 = ((params_np[:-1] - 1) ** 2).sum()
    return r1 + r2


import numpy as np
import numpy.linalg as npla
import matplotlib.pyplot as plt

def lancoiz(A, max_iter=20):
    alphas = list()
    betas = list()
    ritz_vals = list()
    qk_1 = np.zeros(A.shape[1])
    qk = np.random.random(A.shape[1])
    qk = qk / np.linalg.norm(qk, ord=2)
    Q = list()
    Q.append(qk_1)
    Q.append(qk)
    beta = 0
    k = 1
    while True:
        qk = Q[k]
        qk_1 = Q[k-1]
        uk = np.dot(A, qk)
        ak = np.dot(qk, uk)
        alphas.append(ak)
        uk -= ((beta * qk_1) + (ak * qk))
        beta = npla.norm(uk, ord=2)
        if beta == 0:
            break
        betas.append(beta)
        qk_p1 = uk / beta

        T_i = np.diag(np.array(alphas))
        T_i = T_i + np.diag(np.array(betas[:len(alphas) - 1]), -1) + np.diag(np.array(betas[:len(alphas) - 1]), +1)
        ritzs = np.linalg.eigvals(T_i)

        ritz_vals.extend([[k, ritzs[j]] for j in range(len(ritzs))])

        k+=1
        if k >= max_iter:
            break
        Q.append(qk_p1)

    H = np.diag(np.array(alphas))
    H = H + np.diag(np.array(betas[:len(alphas) - 1]), -1) + np.diag(np.array(betas[:len(alphas) - 1]), +1)
    return np.array(Q[1:]).T, H, np.array(ritz_vals)

def main():

	B = np.random.random((100, 100))
	A = B + B.T
	Q, H, rvals = lancoiz(A, max_iter=20)

	part_1 = npla.norm(np.dot(Q, Q.T) - np.diag(np.ones(100)), ord = 2)

	part_2 = npla.norm(np.dot(np.dot(Q.T, A), Q) - H, ord=2) / npla.norm(A, ord=2)

	print("Answer for part 6(a) (i):", part_1)
	print("Answer for part 6(a) (ii):", part_2)

if __name__ == '__main__':
	main()


from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from math import tan, cos, sin, pi
from scipy.integrate import odeint, simps, cumtrapz

##############
## y0 = yk
## y1 = theta
## y2 = px
## y3 = py
##############

def model(y, t):
    yk, theta, vx, vy = y

    # constants
    m = 0.206 # Mass of pendulum
    k = 10 # Stiffness of Spring
    b = 0.3 # Torsional Resistance
    R = 1.5 # Friction
    L = 0.61  # Length of Pendulum
    g = 9.81 # Gravitional acceleration
    Y = 0 # Equilibrium position

    # in between terms
    disp = (yk - Y)

    d_yk = vy + ((tan(theta) * vx))
    d_theta = vx / (L * cos(theta))
    d_vy = g + (( -R * d_yk - k * yk)/m)

    # the derivative causality is resolved here, so adding some in between
    # terms for easier debugging
    e_21 = tan(theta) * (d_vy - g) # comes from the left side of bg
    e_24 = d_theta * b  # torsional resistance
    e_22 = d_theta * tan(theta) * vx / (12 * (cos(theta)**2))
    factor = 1 / (1 + (1 / ( 12 * (cos(theta)**2))))

    d_vx = factor * (e_21 - e_22 - e_24)
    return [d_yk, d_theta, d_vx, d_vy]

time = np.linspace(0.0, 8.0, 10000)
# Initial condition parameters
# yinit = [Vertical spring displacement, pendulum angle relative to vertical, horizontal velocity, vertical]
yinit = [0, pi/4, 0, 0]
y = odeint(model, yinit, time)

# the state equations give us velocity
# integrate again to get displacement
# our variable of interest
ped_y = cumtrapz(y[:,3], time, initial=0)
ped_x = cumtrapz(y[:,2], time, initial=0)

plt.figure(1)

plt.subplot(311)
plt.plot(time, y[:,0])
plt.xlabel('t [s]')
plt.ylabel('Displacement [m]')
plt.title('Displacement of Spring in Y')
plt.grid()
plt.legend()

# plt.subplot(312)
# plt.plot(time, y[:,1])
# plt.xlabel('t [s]')
# plt.ylabel('Displacement [rad]')
# plt.title('Angle of rotation')
# plt.legend()

plt.subplot(312)
plt.plot(time, ped_x)
plt.xlabel('t [s]')
plt.ylabel('Displacement [m]')
plt.title('Displacement of Pendulum in X')
plt.grid()
plt.legend()

plt.subplot(313)
plt.plot(time, ped_y)
plt.xlabel('t [s]')
plt.ylabel('Displacement [m]')
plt.title('Displacement of Pendulum in Y')
plt.grid()
plt.legend()

plt.tight_layout()
plt.show()


import numpy as np
from numba import jit
from numba import float64
from numba import int64
"""
This module contains various implementations of ewma based on sample size
"""

@jit((float64[:], int64), nopython=False, nogil=True)
def ewma(arr_in, window):
    """Exponentialy weighted moving average specified by a decay ``window``
    to provide better adjustments for small windows via:
        y[t] = (x[t] + (1-a)*x[t-1] + (1-a)^2*x[t-2] + ... + (1-a)^n*x[t-n]) /
               (1 + (1-a) + (1-a)^2 + ... + (1-a)^n).

    :param arr_in : np.ndarray, float64. A single dimenisional numpy array
    :paran window : int64. The decay window, or 'span'
    :return: np.ndarray. The EWMA vector, same length / shape as ``arr_in``
    """
    n = arr_in.shape[0]
    ewma_arr = np.empty(n, dtype=float64)
    alpha = 2 / float(window + 1)
    w = 1
    ewma_old = arr_in[0]
    ewma_arr[0] = ewma_old
    for i in range(1, n):
        w += (1 - alpha)**i
        ewma_old = ewma_old * (1 - alpha) + arr_in[i]
        ewma_arr[i] = ewma_old / w

    return ewma_arr


import numpy as np

class LogCost():
    @staticmethod
    def get(A2, Y):
        m = Y.shape
        logProb = np.multiply(np.log(A2), Y) + np.multiply(np.log(1-A2), 1-Y)
        return - np.sum(logProb) / m

    @staticmethod
    def getDerivative(A2, Y):
        return - (Y / A2) + (1 - Y) / (1 - A2)


import numpy as np

def fact(n):
    if n<2:
        return 1
    else:
        return n*fact(n-1)

def f(N,k):
	return fact(N)/( fact(k) * fact(N-k) )

def b(t,k,N):
	return f(N,k) * (1-t)**(N-k) * t**k

def bezier(t,points):
	N = points.shape[1]-1 # colnum
	sum = np.zeros((3,1))
	for k in range(0,N+1):
		sum[:,0] += b(t,k,N)*points[:,k]
	return sum

def bezier1D(t,points):
	N = len(points)-1
	sum = 0
	for k in range(0,N+1):
		sum += b(t,k,N)*points[k]
	return sum

if __name__ == "__main__":
	points = np.array(
		[
			[ 20,  20, -20, -20],
			[ 10,   0,   0,  10],
			[  0,   0,   0,   0]
		]
	)
	print(bezier(0.783,points))


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt

def best_fit_slope(xs, ys):
    m = (     (   (mean(xs) * mean(ys)) - mean(xs*ys)   ) /
    (   (mean(xs)*mean(xs)) - mean(xs**2)   )     )

    return m

#set example data
xs = np.array([1,2,3,4,5,6], dtype = np.float64)
ys = np.array([5,4,6,5,6,7], dtype = np.float64)

m = best_fit_slope(xs, ys)
print(m)
#draw plot
plt.plot(xs,ys)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Mon Nov  1 20:27:56 2021

@author: danie
"""

import pandas as pd
import numpy as np
import matplotlib.pyplot as mpl
import scipy.optimize as sp

df = pd.read_pickle('ex1.gz')

#%% Regresión Lineal

dmc = df[(df['x'] <= 1.5)]

sxy = (dmc['x']*dmc['y']).sum()
sx = dmc['x'].sum()
sy = dmc['y'].sum()
sxx = (dmc['x']*dmc['x']).sum()

n = len(dmc['x'])
mean = (1/n)*(dmc['y'].sum())

m = (n*sxy - sx*sy)/(n*sxx - sx*sx)
b = (sy*sxx - sx*sxy)/(n*sxx - sx*sx)
ssres = ((dmc['y'] - (m*dmc['x'] + b))*(dmc['y'] - (m*dmc['x'] + b))).sum()
sstot = ((dmc['y'] - mean)*(dmc['y'] - mean)).sum()

r2_mc = 1 - (ssres/sstot)

print("\nRegresión Lineal:")
print(f"pendiente = {m}")
print(f"intercepto = {b}")
print(f"valor de r^2 = {r2_mc}")

mpl.scatter(dmc['x'],dmc['y'],color='r')
mpl.plot(dmc['x'],(m*dmc['x']+b))
mpl.xlabel("x")
mpl.ylabel("y")
mpl.title("Regresión Lineal")
mpl.show()

#%% Regresión Polinomial

#usando función de costo

def vector_ajuste_rp( datos_x,datos_y ):

    v = []

    for i in range(0,6):

        v = v + [((datos_y)*(datos_x**i)).sum()]

    return np.array(v)

def matriz_ajuste_rp( datos_x ):

    matriz = np.empty((0,6))

    for i in range(0,6):

        matriz = np.vstack((matriz,np.array([(datos_x**(0+i)).sum(),(datos_x**(1+i)).sum(),(datos_x**(2+i)).sum(),(datos_x**(3+i)).sum(),(datos_x**(4+i)).sum(),(datos_x**(5+i)).sum()])))

    return matriz

A = matriz_ajuste_rp(df['x'])
x = vector_ajuste_rp(df['x'], df['y'])

b_rp = np.matmul(np.linalg.inv(A),x)

y_vals_rp = np.zeros(len(df['x']))

for i in range(0,len(df['x'])):

    y_vals_rp[i] = b_rp[0]*1 + b_rp[1]*(df['x'][i]) + b_rp[2]*((df['x'][i])**2) + b_rp[3]*((df['x'][i])**3) + b_rp[4]*((df['x'][i])**4) + b_rp[5]*((df['x'][i])**5)

y_data_rp = pd.DataFrame(data=y_vals_rp,columns=['y'])

mean_rp = (1/len(y_data_rp))*((df['y']).sum())

ssres = ((df['y'] - (y_data_rp['y']))**2).sum()
sstot = ((df['y']-mean_rp)**2).sum()

r2_rp = 1 -(ssres/sstot)

print("\nRegresión Polinomial:")
print(f"coeffs 5to grado = \n b1: {b_rp[0]} \n b2: {b_rp[1]} \n b3: {b_rp[2]} \n b4: {b_rp[3]} \n b5: {b_rp[4]} ")
print(f"r^2 = {r2_rp}")
print(f"f(0) = {y_vals_rp[0]}")

mpl.scatter(df['x'],df['y'],color='r')
mpl.plot(df['x'],y_data_rp['y'])
mpl.xlabel("x")
mpl.ylabel("y")
mpl.title("Regresión Polinomial")
mpl.show()

#%% Regresión Polinomial exacta

#usando procedimiento datos inmobiliarios

def matriz_ajuste( datos ):

    matriz = np.empty((0,6))

    for index in datos.index:

        matriz = np.vstack((matriz,np.array([1,datos[index],datos[index]**2,datos[index]**3,datos[index]**4,datos[index]**5])))

    return matriz

y = np.matrix(df['y'].to_numpy(dtype=np.float64))

A = np.matrix(matriz_ajuste(  df['x'] ))
b = np.matmul(np.linalg.inv((np.matmul(np.transpose(A),A))),np.matmul(np.transpose(A),np.transpose(y)))

y_vals = np.zeros(len(df['x']))

for i in range(0,len(df['x'])):

    y_vals[i] = b[0]*1 + b[1]*(df['x'][i]) + b[2]*((df['x'][i])**2) + b[3]*((df['x'][i])**3) + b[4]*((df['x'][i])**4) + b[5]*((df['x'][i])**5)

y_data = pd.DataFrame(data=y_vals,columns=['y'])

mean = (1/len(y_data))*((df['y']).sum())

ssres = ((df['y'] - (y_data['y']))**2).sum()
sstot = ((df['y']-mean)**2).sum()

r2 = 1 -(ssres/sstot)

print("\nRegresión Polinomial exacta:")
print(f"f(0) = {y_vals[0]}")
print(f"r^2 = {r2}")
print(f"coeffs 5to grado = \n b1: {b[0][0]} \n b2: {b[1][0]} \n b3: {b[2][0]} \n b4: {b[3][0]} \n b5: {b[4][0]} ")

mpl.scatter(df['x'],df['y'],color='r')
mpl.plot(df['x'],y_data['y'])
mpl.xlabel("x")
mpl.ylabel("y")
mpl.title("Regresión Polinomial Exacta")
mpl.show()

#%% Ajuste ecuación no lineal

def f(parametros,x):
    return parametros[0]/(((x - parametros[1])**2) + parametros[2])**(parametros[3])

def Lfit(parametros,x,y):

    deltaY = f(parametros,x) - y
    return np.dot(deltaY,deltaY)/len(y)

res1 = sp.minimize( fun=Lfit, x0 = np.array([1,0,0,1]),args=(df['x'],df['y']), method='L-BFGS-B',tol=1e-8 )

y1_pred = f(res1.x,df['x'])

print("\nEcuación no lineal:")
print(f"f(0) = {y1_pred[0]}")
print(f"los coeficientes son: \n a: {res1.x[0]} \n b: {res1.x[1]} \n c: {res1.x[2]} \n gamma: {res1.x[3]}")

mpl.scatter(df['x'],df['y'],color='r')
mpl.plot(df['x'],y1_pred)
mpl.xlabel("x")
mpl.ylabel("y")
mpl.title("Ajuste ecuación no lineal")
mpl.show()

print("\nEntre la regresión polinomica y la exacta la diferencia es de apenas algunos decimales, ambos resultados son muy similares, tanto para el intercepto como para el valor de r2, estas tienen mejor ajuste que la regresión lineal. El intercepto de el ajuste con la ecuación no lineal tiene un valor muy cercano al de la regresión con polinomios.")


"""

Authors: Chase Coleman, Spencer Lyon, Thomas Sargent, John Stachurski

"""
import pandas as pd
import numpy as np
from scipy.linalg import eig
import matplotlib.pyplot as plt
import quantecon as qe

# == model parameters == #

a_0 = 100
a_1 = 0.5
ρ = 0.9
σ_d = 0.05
β = 0.95
c = 2
γ = 50.0

θ = 0.002
ac = (a_0 - c) / 2.0

# == Define LQ matrices == #

R = np.array([[0.,   ac,   0.],
              [ac, -a_1,  0.5],
              [0.,  0.5,  0.]])

R = -R  # For minimization
Q = γ / 2

A = np.array([[1., 0., 0.],
              [0., 1., 0.],
              [0., 0., ρ]])
B = np.array([[0.],
              [1.],
              [0.]])
C = np.array([[0.],
              [0.],
              [σ_d]])

# -------------------------------------------------------------------------- #
#                                 Functions
# -------------------------------------------------------------------------- #

def evaluate_policy(θ, F):
    """
    Given θ (scalar, dtype=float) and policy F (array_like), returns the
    value associated with that policy under the worst case path for {w_t}, as
    well as the entropy level.
    """
    rlq = qe.robustlq.RBLQ(Q, R, A, B, C, β, θ)
    K_F, P_F, d_F, O_F, o_F = rlq.evaluate_F(F)
    x0 = np.array([[1.], [0.], [0.]])
    value = - x0.T @ P_F @ x0 - d_F
    entropy = x0.T @ O_F @ x0 + o_F
    return list(map(float, (value, entropy)))

def value_and_entropy(emax, F, bw, grid_size=1000):
    """
    Compute the value function and entropy levels for a θ path
    increasing until it reaches the specified target entropy value.

    Parameters
    ==========
    emax: scalar
        The target entropy value

    F: array_like
        The policy function to be evaluated

    bw: str
        A string specifying whether the implied shock path follows best
        or worst assumptions. The only acceptable values are 'best' and
        'worst'.

    Returns
    =======
    df: pd.DataFrame
        A pandas DataFrame containing the value function and entropy
        values up to the emax parameter. The columns are 'value' and
        'entropy'.

    """
    if bw == 'worst':
        θs = 1 / np.linspace(1e-8, 1000, grid_size)
    else:
        θs = -1 / np.linspace(1e-8, 1000, grid_size)

    df = pd.DataFrame(index=θs, columns=('value', 'entropy'))

    for θ in θs:
        df.loc[θ] = evaluate_policy(θ, F)
        if df.loc[θ, 'entropy'] >= emax:
            break

    df = df.dropna(how='any')
    return df

# -------------------------------------------------------------------------- #
#                                    Main
# -------------------------------------------------------------------------- #

# == Compute the optimal rule == #
optimal_lq = qe.lqcontrol.LQ(Q, R, A, B, C, β)
Po, Fo, do = optimal_lq.stationary_values()

# == Compute a robust rule given θ == #
baseline_robust = qe.robustlq.RBLQ(Q, R, A, B, C, β, θ)
Fb, Kb, Pb = baseline_robust.robust_rule()

# == Check the positive definiteness of worst-case covariance matrix to == #
# == ensure that θ exceeds the breakdown point == #
test_matrix = np.identity(Pb.shape[0]) - (C.T @ Pb @ C) / θ
eigenvals, eigenvecs = eig(test_matrix)
assert (eigenvals >= 0).all(), 'θ below breakdown point.'

emax = 1.6e6

optimal_best_case = value_and_entropy(emax, Fo, 'best')
robust_best_case = value_and_entropy(emax, Fb, 'best')
optimal_worst_case = value_and_entropy(emax, Fo, 'worst')
robust_worst_case = value_and_entropy(emax, Fb, 'worst')

fig, ax = plt.subplots()

ax.set_xlim(0, emax)
ax.set_ylabel("Value")
ax.set_xlabel("Entropy")
ax.grid()

for axis in 'x', 'y':
    plt.ticklabel_format(style='sci', axis=axis, scilimits=(0, 0))

plot_args = {'lw': 2, 'alpha': 0.7}

colors = 'r', 'b'

df_pairs = ((optimal_best_case, optimal_worst_case),
            (robust_best_case, robust_worst_case))

class Curve:

    def __init__(self, x, y):
        self.x, self.y = x, y

    def __call__(self, z):
        return np.interp(z, self.x, self.y)

for c, df_pair in zip(colors, df_pairs):
    curves = []
    for df in df_pair:
        # == Plot curves == #
        x, y = df['entropy'], df['value']
        x, y = (np.asarray(a, dtype='float') for a in (x, y))
        egrid = np.linspace(0, emax, 100)
        curve = Curve(x, y)
        print(ax.plot(egrid, curve(egrid), color=c, **plot_args))
        curves.append(curve)
    # == Color fill between curves == #
    ax.fill_between(egrid,
                    curves[0](egrid),
                    curves[1](egrid),
                    color=c, alpha=0.1)

plt.show()


#!/usr/bin/env python2

# Author: juscodit@gmail.com

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import math
import sys
from math import pow
from math import exp

delta = 0.025

### ==============================
### Ellipse function (f(x) = x1^2 + 50 x2^2)
### ==============================

def func(p):
  return p[0] ** 2 + 50 * p[1] ** 2

def gr(p):
  x, y = p
  dx = 2 * x
  dy = 100 * y
  return np.array([dx, dy])

def he(p):
  return np.matrix([[2, 0], [0, 100]])

# descent direction
def nddir(p):
  hessian = he(p)
  heinv = np.linalg.inv(hessian)
  return np.squeeze(np.asarray(np.negative(np.dot(heinv, gr(p)))))

# step length at point @p with respect to descent direction @dd
def alpha(p, dd):
  a, r, c1 = 1, 0.3, 1e-4

  g = gr(p)
  p1 = p + a * dd
  while func(p1) > func(p) + c1 * a * np.dot(g, dd):
    a *= r
    p1 = p + a * dd

  return a

def steepest_descent(p0):
  result = []
  result.append(p0)
  pk = p0

  i = 0
  while np.linalg.norm(gr(pk)) > 0.001:
    print "step %d [%f, %f]" % (i, pk[0], pk[1])
    i += 1
    dd = -gr(pk)
    a = alpha(pk, dd)
    pk = pk + a * dd
    result.append(pk)

  print "step %d [%f, %f]" % (i, pk[0], pk[1])

  return result

def classical_newton(p0):
  result = []
  result.append(p0)
  pk = p0

  i = 0
  while np.linalg.norm(gr(pk)) > 0.001:
    print "step %d [%f, %f]" % (i, pk[0], pk[1])
    i += 1
    dd = nddir(pk)
    # a = alpha(pk, dd)
    pk = pk + dd
    result.append(pk)

  print "step %d [%f, %f]" % (i, pk[0], pk[1])

  return result

# initial point
p0 = (-2.5, 0.05)

# get result of classical newton
nres = classical_newton(p0)

# get result of steepest descent
sres = steepest_descent(p0)

x = np.arange(-3, 3, delta)
y = np.arange(-0.7, 0.7, delta)
X, Y = np.meshgrid(x, y)
Z = X ** 2 + 50 * Y ** 2

# plot figure for classicl newton
fig = plt.figure()
plt.contour(X, Y, Z, [13, 9, 6, 4, 2, 1, 0.1])
plt.annotate("", xytext=nres[0], xy=nres[1], arrowprops=dict(arrowstyle='->', linewidth=2, color="red"))
for r in nres:
  plt.plot(*r, marker='o', color='g')

plt.text(p0[0] - 0.3, p0[1] + 0.02, "(%.1f, %.1f)" % (p0[0], p0[1]), color="purple", fontsize=10)
plt.text(-1.5, 0.1, "1 step", fontsize=20)

plt.savefig("newton.png")

# plot figure for steepest descent
fig = plt.figure()
plt.contour(X, Y, Z, [13, 9, 6, 4, 2, 1, 0.1])
for i in xrange(len(sres) - 1):
  plt.annotate("", xytext=sres[i], xy=sres[i+1], arrowprops=dict(arrowstyle="-"))
for r in sres:
  plt.plot(*r, marker='o', color='g')

plt.text(p0[0] - 0.3, p0[1] + 0.02, "(%.1f, %.1f)" % (p0[0], p0[1]), color="purple", fontsize=10)
plt.text(-1.5, 0.15, "%d step" % len(sres), fontsize=20)

plt.savefig("steepest.png")

### ==============================
### Function x^2 and 10x^2
### ==============================

def steepest_gradient_x2(x0):
  def func(x):
    return x ** 2

  def gr(x):
    return 2 * x

  def alpha(x, d):
    a, r, c1 = 1, 0.3, 1e-4
    while func(x + a * d) > func(x) + c1 * a * gr(x) * d:
      a *= r
    return a

  def ddir(x):
    return -gr(x)

  result = []
  xk = x0
  i = 1
  while np.linalg.norm(gr(xk)) > 0.001:
    print "step %d [%f] loss [%f]" % (i, xk, func(xk))
    i += 1
    result.append((xk, func(xk)))
    dd = ddir(xk)
    a = alpha(xk, dd)
    xk = xk + a * dd

  result.append((xk, func(xk)))

  return result

def steepest_gradient_10x2(x0):
  def func(x):
    return 10 * x ** 2

  def gr(x):
    return 20 * x

  def alpha(x, d):
    a, r, c1 = 1, 0.3, 1e-4
    while func(x + a * d) > func(x) + c1 * a * gr(x) * d:
      a *= r
    return a

  def ddir(x):
    return -gr(x)

  result = []
  xk = x0
  i = 1
  while np.linalg.norm(gr(xk)) > 0.001:
    print "step %d [%f] loss [%f]" % (i, xk, func(xk))
    i += 1
    result.append((xk, func(xk)))
    dd = ddir(xk)
    a = alpha(xk, dd)
    xk = xk + a * dd

  result.append((xk, func(xk)))

  return result

x = np.arange(-1.6, 1.6, delta)
y1 = x ** 2
y2 = 10 * x ** 2

# plot for steepest descent on x^2
fig = plt.figure()
plt.plot(x, y1, linewidth=2)
plt.ylim(0, 2.8)
sg_x2_res = steepest_gradient_x2(-1.5)

for i in xrange(len(sg_x2_res) - 1):
  plt.annotate("", xytext=sg_x2_res[i], xy=sg_x2_res[i+1], arrowprops=dict(arrowstyle="->"))
for r in sg_x2_res:
  plt.plot(*r, marker='o', color='g')

plt.text(-1.0, 2.5, "$f(x) = x^2$", fontsize=25)
plt.text(-1.0, 2.15, "%d step" % len(sg_x2_res), fontsize=20)
plt.savefig('steepest_x2.png')

# plot for steepest descent on 10x^2
fig = plt.figure()
plt.plot(x, y2, linewidth=2)
plt.ylim(0, 28)
sg_10x2_res = steepest_gradient_10x2(-1.5)

for i in xrange(len(sg_10x2_res) - 1):
  plt.annotate("", xytext=sg_10x2_res[i], xy=sg_10x2_res[i+1], arrowprops=dict(arrowstyle="->"))
for r in sg_10x2_res:
  plt.plot(*r, marker='o', color='g')

plt.text(-1.0, 25, "$f(x) = 10 x^2$", fontsize=25)
plt.text(-1.0, 21.5, "%d step" % len(sg_10x2_res), fontsize=20)
plt.savefig('steepest_10x2.png')

# plot for curvature axis

x = np.arange(-3, 3, delta)
y = np.arange(-3, 3, delta)
X, Y = np.meshgrid(x, y)
Z = 4 * X ** 2 + 2 * Y ** 2 - 4 * X * Y

w, v = np.linalg.eig(np.array([[8, -4], [-4, 4]]))

print w, v[0], v[1]

plt.figure(figsize=(4, 4))
plt.contour(X, Y, Z, [13, 9, 6, 4, 2, 1, 0.1])
plt.annotate("", xytext=(0, 0), xy=(v[0, 1], v[1, 1]), arrowprops=dict(arrowstyle="->", color="red", linewidth=1))
plt.annotate("", xytext=(0, 0), xy=(2 * v[0, 0], 2 * v[1, 0]), arrowprops=dict(arrowstyle="->", color="red", linewidth=1))
plt.plot(0, 0, marker='o', color='r')
plt.text(-2.5, 2.2, "$f(x) = 4x_1^2 + 2x_2^2 - 4x_1 x_2$", fontsize=13)
plt.text(0.7, 0.9, "curvature axis", color="red", fontsize=10)
plt.savefig('axis.png')

plt.show()


import numpy as np
from astropy import units as u

###################################### Q1 ###############################################

def getBr(Mb, r, theta, outputUnits=u.nT):
    Br = - 2 * (Mb/r**3) * np.cos(theta)
    if outputUnits is not None:
        Br = Br.to(outputUnits)

    return Br

def getBtheta(Mb, r, theta, outputUnits=u.nT):
    Btheta = (Mb/r**3) * np.cos(theta)
    if outputUnits is not None:
        Btheta = Btheta.to(outputUnits)

    return Btheta

###################################### Q2 ###############################################
def getV(E, m, outputUnits=u.m/u.s):
    """
    Gets velocity of a particle with KE E and mass m
    :param E:
    :param m:
    :param outputUnits:
    :return:
    """
    V = np.sqrt(2*E/m)

    if outputUnits is not None:
        V = V.to(outputUnits)

    return V

def getr_lam(m, Vperp, q, B, outputUnits=u.m):
    r_lam = (m*Vperp)/(np.abs(q)*B)
    if outputUnits is not None:
        r_lam = r_lam.to(outputUnits)

    return r_lam

def getXYZ(t, Vperp, Vpar, r_lam):
    omega = Vperp/r_lam
    theta = omega*t

    z = r_lam * np.cos(theta)
    y = r_lam * np.sin(theta)

    x = Vpar * t

    return x,y,z

###################################### Q3 ###############################################

def getRhoS(rho_p, aR, rp, outputUnits=u.Unit("kg/m**3")):
    frac = rho_p**3/aR**3
    rho_s = (16*frac*rp**3)**(1/3)
    # print(rho_s.decompose())
    if outputUnits is not None:
        rho_s = rho_s.to(outputUnits)

    return rho_s

###################################### Q3 ###############################################

def getDV(mu, anom, W, outputUnits=u.m/u.s):
    Vlow = np.sqrt(mu/(anom - 0.5*W))
    Vhigh = np.sqrt(mu/(anom + 0.5*W))
    DV = Vlow - Vhigh
    if outputUnits is not None:
        DV = DV.to(outputUnits)

    return DV

def get_tSpread(anom, DV, outputUnits=u.s):
    tSpread = (2*np.pi*anom)/DV
    if outputUnits is not None:
        tSpread = tSpread.to(outputUnits)

    return tSpread

def getAring(Rout, Rin, outputUnits=u.m**2):
    Aring = np.pi*(Rout - Rin)*(Rout + Rin)
    if outputUnits is not None:
        Aring = Aring.to(outputUnits)

    return Aring

def getAring2(anom, W, outputUnits=u.m**2):
    Rout = anom + 0.5*W
    Rin = anom - 0.5*W
    Aring = getAring(Rout, Rin, outputUnits=outputUnits)

    return Aring

def get_tDiff(A1, A2, Vv, outputUnits=u.s):
    tDiff = (abs(A2-A1))/Vv
    if outputUnits is not None:
        tDiff = tDiff.to(outputUnits)

    return tDiff


#!/usr/bin/env python
"""Monte carlo estimation of pi

Background:
https://en.wikipedia.org/wiki/Monte_Carlo_method

"""

import random
import math
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import time
import sys
import getopt

__author__ = "Michael Wittmann and Maximilian Speicher"
__copyright__ = "Copyright 2020, Michael Wittmann and Maximilian Speicher"

__license__ = "MIT"
__version__ = "1.0.0"
__maintainer__ = "Michael Wittmann"
__email__ = "michael.wittmann@tum.de"
__status__ = "Example"

class MonteCarloPi():

    def __init__(self, iterations: int = 10000, random_seed: int = None, output_dir:Path = 'output') -> None:
        """
        New instance of MonteCarloPI
        :param iterations: number of samples
        :param random_seed: random seed to be used for sampling
        :param output_dir: output directory for generated plots
        """
        self.random_seed = random_seed
        self.iterations = iterations
        self.points_inside = []
        self.points_outside = []
        self.pi_estimate = None
        self.output_dir = output_dir

        if not output_dir.exists():
            output_dir.mkdir(parents=True, exist_ok=True)

    def _f_circle(self, x, radius=1.0):
        """
        Circle funcion $y=sqrt(r^2-x^2)
        :param x: x-value
        :param radius: circle radius
        :return: y-value
        """
        y = np.sqrt(radius ** 2 - x ** 2)
        return y

    def estimate_pi(self) -> float:
        """
        Estimate pi, with monte carlo method.
        https://en.wikipedia.org/wiki/Monte_Carlo_method
        :return: Estimate of pi
        """
        self.points_inside = []
        self.points_outside = []
        random.seed(self.random_seed)

        for i in range(0, self.iterations):
            point = (random.random(), random.random())
            if (math.sqrt(point[0] ** 2 + point[1] ** 2)) < 1.0:
                self.points_inside.append(point)
            else:
                self.points_outside.append(point)

        self.pi_estimate = (float(len(self.points_inside)) / float(self.iterations)) * 4.0
        return self.pi_estimate

    def plot(self, path=None):
        """
        Plot img of monte-carlo estimation
        :param path: output directory, uses cwd if None
        """
        x_circle = np.linspace(0, 1, 200)
        fig, ax = plt.subplots(1, 1, figsize=(5, 5), dpi=300)
        ax.scatter(*zip(*self.points_outside[:10000]), color='r', alpha=0.5,
                   label=f'Points outside: $n={len(self.points_outside)}$')
        ax.scatter(*zip(*self.points_inside[:10000]), color='b', alpha=0.5,
                   label=f'Points inside: $n={len(self.points_inside)}$')
        ax.plot(x_circle, self._f_circle(x_circle), color='black', linestyle='--', linewidth=2, label='circle')
        ax.set_title(f'Estimate of $\pi={self.pi_estimate:.10f}$\nIterations $n={self.iterations}$')
        ax.set_xlim(0, 1)
        ax.set_ylim(0, 1)
        ax.legend(loc='lower right')

        figure_path = self.output_dir.joinpath(f'pi_estmimate_n{self.iterations}_at{time.time_ns()}.png')
        plt.savefig(figure_path, dpi=300)

def main(argv):
    output_folder:Path = Path('img')
    iterations: int = 100000
    random_seed:int = 1

    try:
        opts, args = getopt.getopt(argv, 'ho:i:r:',['output_dir=', '--iterations', '--random_seed'])
    except getopt.GetoptError:
        print('monte_carlo_pi.py - o <output_folder> -i <iterations> -r <random_seed>')
        sys.exit(2)

    for opt, arg in opts:
        if opt == '-h':
            print('monte_carlo_pi.py - o <output_folder> -i <iterations> -r <random_seed>')

        if opt in ('-o', '--output_dir'):
            output_folder = Path(arg)

        if opt in ('-i', '--iterations'):
            iterations = int(arg)

        if opt in ('-r', '--random_seed'):
            random_seed = int(arg)

    print(f'Starting Simulation: iteations={iterations}, random_seed={random_seed}')
    tic = time.time()
    simulation = MonteCarloPi(iterations=iterations, random_seed=random_seed, output_dir = output_folder)
    pi_hat = simulation.estimate_pi()
    print(f'Result: pi_hat = {pi_hat:.10f}')
    toc = time.time()
    print('Generating plot...')
    simulation.plot()
    print(f'Simulation finished! ({toc-tic:.5} ms) ')

if __name__ == '__main__':
    main(sys.argv[1:])


import numpy as np
from sklearn.neighbors import kneighbors_graph
from scipy.spatial.distance import cdist

def rbf_kernel(data, sigma=1):
    n = data.shape[0]
    kernel_matrix = np.zeros((n,n))

    for i in range(n):
        x1 = data[i]
        for j in range(n):
            x2 = data[j]
            kernel_matrix[i][j] = np.exp( (-1)*( (x1[0]-x2[0])**2 + (x1[1]-x2[1])**2 ) / (2*sigma**2) )

    return kernel_matrix

def validation_method(sim_matrix, clustering_labels):

    n = sim_matrix.shape[0]
    validation_score = 0

    for i in range(n): # iterate through the whole dataset
        sum_numerator = 0
        sum_denominator = 0
        for j in range(n):
            if (i != j): # to make sure not the same data point
                c_ij = 1 if clustering_labels[i] == clustering_labels[j] else 0
                similarity_value = sim_matrix[i][j]

                sum_numerator += c_ij * similarity_value
                sum_denominator += similarity_value

        validation_score += sum_numerator / sum_denominator

    validation_score = validation_score / n

    return validation_score


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

df = pd.read_csv('plot_data.csv')
sorted_df = df.sort_values(by=['arr_size'], ascending=True)

x = np.linspace(30, 1000, 100)
y = [i*math.log(i, math.e) for i in x]

fig, axs = plt.subplots(figsize=(10, 8))
axs.plot(sorted_df.arr_size, sorted_df.n_comps, linewidth=3)
axs.plot(x, y, linewidth=3)
axs.set_yticks([0, 1000, 2000, 3000, 4000, 5000, 6000, 7000])
axs.set_xticks([30, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000])
axs.set_yticklabels(['0', '1000', '2000', '3000', '4000', '5000', '6000', '7000'])
axs.set_xticklabels(['30', '100', '200', '300', '400', '500', '600', '700', '800', '900', '1000'])
axs.grid()
plt.legend(['No. of comparisons in Quick Sort', 'nlogn(base=e)'], loc='lower right')
plt.xlabel('Size of array(n)', labelpad=10)
plt.ylabel('No. of comparisons', labelpad=10)
plt.title('Randomised Quick Sort Graph compared with Graph of nlogn')
# plt.title('Randomised Quick Sort Comparisons Graph')

plt.savefig('plot_compare_nlogn.png', dpi=275, bbox_inches='tight') # To save the plot
# plt.savefig('plot.png', dpi=275, bbox_inches='tight') # To save the plot
plt.show()


import numpy as np
from scipy.io import mmread

def is_pos_def(x):
    """check if a matrix is symmetric positive definite"""
    return np.all(np.linalg.eigvals(x) > 0)

def check_symmetric(x, tol=1e-8):
    return np.all(np.abs(x-x.T) < tol)

A = mmread('/home/anaxsouza/Documents/GitHub/coc757_Trabalho_05/bcsstk16.mtx').todense()
b = np.zeros((A.shape[0], 1))
x = np.ones((A.shape[0], 1))
i_max = 20000
epsilon = np.finfo(float).eps
delta_old = 0.0

i = 0
r = b - A @ x
d = r
delta_new = r.T @ r
delta_0 = delta_new

#if (is_pos_def(A) == False) | (check_symmetric(A) == False):
    #raise ValueError('Matrix A needs to be symmetric positive definite (SPD)')
while (i < i_max) and (delta_new > (epsilon ** 2)*delta_0):
    q = A @ d
    alpha = delta_new/(d.T @ q)
    alpha = alpha.item()
    x = x + alpha * d
    if (i % 50 == 0):
        print(i)
        r = b - A @ x
    else:
        r = r - alpha * q
    delta_old = delta_new
    delta_new = r.T @ r
    beta = delta_new/delta_old
    beta = beta.item()
    d = r + beta * d
    i += 1
print('\ni:\n',i)
print('\nx:')
print(x)
print(x.shape)


import numpy as np
from numba import jit
from numba import float64
from numba import int64

@jit((float64[:], int64), nopython=True, nogil=True)
def _ewma(arr_in, window):
    r"""Exponentialy weighted moving average specified by a decay ``window``
    to provide better adjustments for small windows via:

        y[t] = (x[t] + (1-a)*x[t-1] + (1-a)^2*x[t-2] + ... + (1-a)^n*x[t-n]) /
               (1 + (1-a) + (1-a)^2 + ... + (1-a)^n).

    Parameters
    ----------
    arr_in : np.ndarray, float64
        A single dimenisional numpy array
    window : int64
        The decay window, or 'span'

    Returns
    -------
    np.ndarray
        The EWMA vector, same length / shape as ``arr_in``

    Examples
    --------
    >>> import pandas as pd
    >>> a = np.arange(5, dtype=float)
    >>> exp = pd.DataFrame(a).ewm(span=10, adjust=True).mean()
    >>> np.array_equal(_ewma_infinite_hist(a, 10), exp.values.ravel())
    True
    """
    n = arr_in.shape[0]
    ewma = np.empty(n, dtype=float64)
    alpha = 2 / float(window + 1)
    w = 1
    ewma_old = arr_in[0]
    ewma[0] = ewma_old
    for i in range(1, n):
        w += (1-alpha)**i
        ewma_old = ewma_old*(1-alpha) + arr_in[i]
        ewma[i] = ewma_old / w
    return ewma


from numpy import unique
from scipy.stats import entropy as scipy_entropy

def shannon_entropy(image, base=2):
    """Calculate the Shannon entropy of an image.

    The Shannon entropy is defined as S = -sum(pk * log(pk)),
    where pk are frequency/probability of pixels of value k.

    Parameters
    ----------
    image : (N, M) ndarray
        Grayscale input image.
    base : float, optional
        The logarithmic base to use.

    Returns
    -------
    entropy : float

    Notes
    -----
    The returned value is measured in bits or shannon (Sh) for base=2, natural
    unit (nat) for base=np.e and hartley (Hart) for base=10.

    References
    ----------
    .. [1] `https://en.wikipedia.org/wiki/Entropy_(information_theory) <https://en.wikipedia.org/wiki/Entropy_(information_theory)>`_
    .. [2] https://en.wiktionary.org/wiki/Shannon_entropy

    Examples
    --------
    >>> from skimage import data
    >>> from skimage.measure import shannon_entropy
    >>> shannon_entropy(data.camera())
    7.231695011055706
    """

    _, counts = unique(image, return_counts=True)
    return scipy_entropy(counts, base=base)


#########################
##                     ##
## Irving Gomez Mendez ##
##    March 03, 2021   ##
##                     ##
#########################

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import t

# Data
P = np.array([21, 19, 20, 15, 18, 18, 7, 16, 14, 9, 11, 13 ,10 ,10 ,10])
Q = np.array([3, 3, 7, 6, 10, 15, 16, 13, 9, 15, 9, 15, 12, 18, 21])
C = 6
U = Q*(P-C)
n = len(Q)
p = 3

# confidence level
alpha = 0.05

# We see graphically our assumptions
plt.figure(figsize=(7.5,7.5))
plt.scatter(P, Q, label='Original data')

plt.figure(figsize=(7.5,7.5))
plt.scatter(P, U, label='Original data')

# Construct the design matrix
XX = np.vstack([np.ones(n), P, P**2]).T

# This would be the design matrix for Q
XX_for_Q = np.vstack([np.ones(n), P]).T

V  = np.diag((P-C)**2)
V_inv = np.linalg.inv(V)

W = XX.T @ V_inv @ XX
W_inv = np.linalg.inv(W)
W_inv_for_Q = np.linalg.inv(XX_for_Q.T @ XX_for_Q)

# Get the beta estimators
hat_beta = W_inv @ XX.T @ V_inv @ U
hat_beta_0, hat_beta_1, hat_beta_2 = hat_beta

# Get the alpha estimators
hat_alpha_0 = -hat_beta_0/C
hat_alpha_1 = hat_beta_2
hat_alpha   = np.array([hat_alpha_0,hat_alpha_1])

# See graphically the estimation
x0 = np.linspace(6, 27, 50)
plt.figure(figsize=(10,5))
plt.scatter(P, U, label='Original data')
plt.plot(x0, hat_beta_0+hat_beta_1*x0+hat_beta_2*x0**2, 'r', label='Expected Gain')
plt.legend(loc='upper left')

plt.figure(figsize=(10,5))
plt.scatter(P, Q,label='Original data')
plt.plot(x0, hat_alpha_0+hat_alpha_1*x0, 'r', label='Expected Sale')
plt.legend(loc='upper right')

# Get sigma^2 estimator
SSE = (U-XX @ hat_beta).T @ V_inv @ (U-XX @ hat_beta)
hat_sigma_2 = 1/(n-p)*SSE

# get P_star, U_star and Q_star estimators
hat_P_star = -hat_beta_1/(2*hat_beta_2)
hat_X_star = np.array([1,hat_P_star,hat_P_star**2]).reshape(3,1)
hat_H_star = np.array([1,hat_P_star]).reshape(2,1)
hat_U_star = hat_X_star.T @ hat_beta
hat_Q_star = hat_H_star.T @ hat_alpha

# get confidence interval for P_star
delta_hat_P_star  = np.array([0, -1/(2*hat_beta_2), hat_beta_1/(2*hat_beta_2**2)]).reshape(3,1)
aux_t_conf_P_star = np.sqrt(hat_sigma_2 * delta_hat_P_star.T @ W_inv @ delta_hat_P_star)
aux_t_conf_P_star = aux_t_conf_P_star[0][0]
hat_P_star_low    = hat_P_star - t.ppf(1-alpha/2,n-p)*aux_t_conf_P_star
hat_P_star_upp    = hat_P_star + t.ppf(1-alpha/2,n-p)*aux_t_conf_P_star

# Create a vector for P_star
P0 = np.linspace(hat_P_star_low, hat_P_star_upp, 20)
X0 = np.vstack([np.ones(len(P0)),P0,P0**2]).T
H0 = np.vstack([np.ones(len(P0)),P0]).T
U0 = X0 @ hat_beta
Q0 = H0 @ hat_alpha

# get confidence interval for U_star
aux_t_conf_U_star   = np.sqrt(hat_sigma_2 * hat_X_star.T @ W_inv @ hat_X_star)
aux_t_conf_U_star   = aux_t_conf_U_star[0][0]
hat_U_star_low_conf = hat_U_star - t.ppf(1-alpha/2,n-p)*aux_t_conf_U_star
hat_U_star_upp_conf = hat_U_star + t.ppf(1-alpha/2,n-p)*aux_t_conf_U_star

aux_t_conf_U_star_0   = np.sqrt(hat_sigma_2 * np.diag(X0 @ W_inv @ X0.T))
hat_U_star_0_low_conf = U0 - t.ppf(1-alpha/2,n-p)*aux_t_conf_U_star_0
hat_U_star_0_upp_conf = U0 + t.ppf(1-alpha/2,n-p)*aux_t_conf_U_star_0

# get prediction interval for U_star
aux_t_pred_U_star   = np.sqrt(hat_sigma_2 * ((hat_P_star-C)**2+hat_X_star.T @ W_inv @ hat_X_star))
aux_t_pred_U_star   = aux_t_pred_U_star[0][0]
hat_U_star_low_pred = hat_U_star - t.ppf(1-alpha/2,n-p)*aux_t_pred_U_star
hat_U_star_upp_pred = hat_U_star + t.ppf(1-alpha/2,n-p)*aux_t_pred_U_star

aux_t_pred_U_star_0   = np.sqrt(hat_sigma_2 * ((P0-C)**2+np.diag(X0 @ W_inv @ X0.T)))
hat_U_star_0_low_pred = U0 - t.ppf(1-alpha/2,n-p)*aux_t_pred_U_star_0
hat_U_star_0_upp_pred = U0 + t.ppf(1-alpha/2,n-p)*aux_t_pred_U_star_0

# get confidence interval for Q_star
aux_t_conf_Q_star   = np.sqrt(hat_sigma_2 * hat_H_star.T @ W_inv_for_Q @ hat_H_star)
aux_t_conf_Q_star   = aux_t_conf_Q_star[0][0]
hat_Q_star_low_conf = hat_Q_star - t.ppf(1-alpha/2,n-p)*aux_t_conf_Q_star
hat_Q_star_upp_conf = hat_Q_star + t.ppf(1-alpha/2,n-p)*aux_t_conf_Q_star

aux_t_conf_Q_star_0   = np.sqrt(hat_sigma_2 * np.diag(H0 @ W_inv_for_Q @ H0.T))
hat_Q_star_0_low_conf = Q0 - t.ppf(1-alpha/2,n-p)*aux_t_conf_Q_star_0
hat_Q_star_0_upp_conf = Q0 + t.ppf(1-alpha/2,n-p)*aux_t_conf_Q_star_0

# get prediction interval for Q_star
aux_t_pred_Q_star   = np.sqrt(hat_sigma_2 * (1+hat_H_star.T @ W_inv_for_Q @ hat_H_star))
aux_t_pred_Q_star   = aux_t_pred_Q_star[0][0]
hat_Q_star_low_pred = hat_Q_star - t.ppf(1-alpha/2,n-p)*aux_t_pred_Q_star
hat_Q_star_upp_pred = hat_Q_star + t.ppf(1-alpha/2,n-p)*aux_t_pred_Q_star

aux_t_pred_Q_star_0   = np.sqrt(hat_sigma_2 * (1+np.diag(H0 @ W_inv_for_Q @ H0.T)))
hat_Q_star_0_low_pred = Q0 - t.ppf(1-alpha/2,n-p)*aux_t_pred_Q_star_0
hat_Q_star_0_upp_pred = Q0 + t.ppf(1-alpha/2,n-p)*aux_t_pred_Q_star_0

# See graphically the estimation
plt.figure(figsize=(15,7.5))
plt.plot(x0, hat_beta_0+hat_beta_1*x0+hat_beta_2*x0**2, 'r', label='Expected Gain')
plt.fill_between(P0, hat_U_star_0_low_pred, hat_U_star_0_upp_pred, facecolor='green', alpha=0.5, label='Prediction interval for U_star')
plt.fill_between(P0, hat_U_star_0_low_conf, hat_U_star_0_upp_conf, facecolor='yellow', alpha=0.5, label='Confidence interval for U_star')
plt.plot([hat_P_star,hat_P_star],[hat_U_star_low_pred,hat_U_star_upp_pred], 'brown', label='Prediction interval for the best guess')
plt.plot([hat_P_star,hat_P_star],[hat_U_star_low_conf,hat_U_star_upp_conf], 'black', label='Confidence interval for the best guess')
plt.scatter(P, U, label='Original data')
plt.legend(loc='upper left')

plt.figure(figsize=(15,7.5))
plt.plot(x0, hat_alpha_0+hat_alpha_1*x0, 'r', label='Expected Sale')
plt.fill_between(P0, hat_Q_star_0_low_pred, hat_Q_star_0_upp_pred, facecolor='green', alpha=0.5, label='Prediction interval for Q_star')
plt.fill_between(P0, hat_Q_star_0_low_conf, hat_Q_star_0_upp_conf, facecolor='yellow', alpha=0.5, label='Confidence interval for Q_star')
plt.plot([hat_P_star,hat_P_star],[hat_Q_star_low_pred,hat_Q_star_upp_pred], 'brown', label='Prediction interval for the best guess')
plt.plot([hat_P_star,hat_P_star],[hat_Q_star_low_conf,hat_Q_star_upp_conf], 'black', label='Confidence interval for the best guess')
plt.scatter(P, Q, label='Original data')
plt.legend(loc='upper left')


'''
Linear Regression using Normal Equation
'''

import numpy as np

X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100,1)

# Adding 1 to each instance of X
X_b = np.c_[np.ones((100,1)), X]
# Calculating the value of Theta that minimizes the cost function
theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
print('Theta: ', theta_best)
# Making predictions using theta
X_new = np.array([[0], [2]])
X_new_b = np.c_[np.ones((2,1)), X_new]
y_predict = X_new_b.dot(theta_best)
print('y predicted: ', y_predict)

import matplotlib
import matplotlib.pyplot as plt

ax = plt.axes()
ax.plot(X, y, 'b.')
ax.plot(X_new, y_predict, 'r')
plt.show()

# Using Scikit-Learn
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X, y)
print('Scikit-Learn theta: ', lin_reg.intercept_, lin_reg.coef_)
print('Scikit-Learn y predicted: ', lin_reg.predict(X_new))


import numpy as np

def _manhattan_distance(x, y):
    '''
    x: numpy array
    y: numpy array
    RETURNS: int/float
    Compute the manhattan distance between two points x and y.
    '''
    return np.sum(np.abs(y - x))

def _find_closest_center(x, cluster_centers):
    '''
    x: 1d numpy array (shape is (p,))
    y: 2d numpy array (shape is (n, p))
    RETURNS: int
    Using manhattan distance, find the index of the center from cluster_centers
    that x is closest to.
    '''
    distances = np.sum(np.abs(cluster_centers - x), axis=1)
    return np.argmin(distances)

def _find_cost(cluster, center):
    '''
    cluster: list of 1d numpy arrays
    center: 1d numpy array
    Using manhattan distance, find the total cost of using center as the center
    '''
    return sum(_manhattan_distance(x, center) for x in cluster)

def _find_total_cost(clusters, cluster_centers):
    '''
    clusters: list of list of 1d numpy arrays
    cluster_centers: 2d numpy array
    RETURNS: int/float
    Calculate the total cost of the chosen clusters and cluster_centers.
    '''
    return sum(
        _find_cost(clusters[i], cluster_centers[i]) \
        for i in xrange(len(clusters))
    )

def _find_medoid(cluster):
    '''
    cluster: list of 1d numpy arrays
    RETURNS: 1d numpy array
    Using manhattan distance, find the datapoint from the cluster that is the
    medoid.
    '''
    min_cost = None
    medoid = None
    for y in cluster:
        cost = _find_cost(cluster, y)
        if min_cost is None or cost < min_cost:
            min_cost = cost
            medoid = y
    return medoid

class kMedoids(object):
    '''
    An implementation of k-medoids algorithm using manhattan distance.
    '''
    def __init__(self, n_clusters, max_iter=300, initial_centers=None):
        '''
        n_clusters: int
        max_iter: int
        initial_centers: list of ints
        RETURNS: None
        Intialize kMedoids algorithm with n_clusters as the number of clusters
        and max_iter the maximum number of iterations.
        initial_centers is a list of the indices of the initial cluster centers.
        If value is None, initial cluster centers are chosen randomly.
        '''
        self.cluster_centers = None
        self.n_clusters = n_clusters
        self.max_iter = max_iter
        self.initial_centers = initial_centers

    def fit(self, X):
        '''
        X: 2d numpy array
        RETURNS: None
        Build the clusters for k-medoids with data X.
        '''
        if self.initial_centers is None:
            center_indicies = np.random.choice(
                range(X.shape[0]),
                self.n_clusters,
                replace=False
            )
        else:
            center_indicies = self.initial_centers
        self.cluster_centers = X[center_indicies]
        cost = None
        for j in xrange(self.max_iter):
            self.clusters = [[] for _ in xrange(self.n_clusters)]
            for x in X:
                center = _find_closest_center(x, self.cluster_centers)
                self.clusters[center].append(x)
            for index, cluster in enumerate(self.clusters):
                self.cluster_centers[index] = _find_medoid(cluster)
            new_cost = _find_total_cost(self.clusters, self.cluster_centers)
            if cost is None or new_cost < cost:
                cost = new_cost
            else:
                break

    def predict(self, X):
        '''
        X: 2d numpy array
        RETURNS: 1d numpy array
        Give the predicted cluster for each datapoint in X
        '''
        y = np.zeros(X.shape[0])
        for i, x in enumerate(X):
            y[i] = _find_closest_center(x, self.cluster_centers)
        return y


import numpy as np

import common.utils as utils

def _gini(values):
    """Measure of entropy among a set of values."""
    bins = np.bincount(values)
    return 1 - np.sum(np.square(bins / np.sum(bins)))

def _gain(parent, children, impurity_func):
    """Measure of information gain between two successive levels of the decition tree."""
    weights = map(len, children)
    scores = map(impurity_func, children)
    weighted_scores = [w*s for w, s in zip(weights, scores)]
    return impurity_func(parent) - sum(weighted_scores) / len(parent)

def decision_tree(X, y, attrs=None):
    """Builds a traversable decision tree.

    Args:
        X (ndarray[m, n]):          ground features
        y (ndarray[m, 1]):          ground labels
        attrs (tuple, optional):    tuple of attributes to branch on (defaults to all)

    Returns:
        root_attr (int):            index of root attribute
        mapping (dict):             decision tree
    """
    # First run
    if attrs is None:
        attrs = set(range(X.shape[1]))
        return decision_tree(X, y, attrs=attrs)

    # Base case: If all samples belong to the same class
    if np.unique(y).size == 1:
        return y[0]

    # Find best split attribute within current subtree
    best_gain = 0
    best_candidate = None
    for attr in attrs:
        # Format: 'group_i' -> [(X0, y0), (X1, y1), ... (Xi, yi)]
        groups = utils.groupby(zip(X, y), key=lambda tup: tup[0][attr])

        # Evaluate candidate
        attr_values, buckets = groups.keys(), groups.values()
        transpose_buckets = [list(zip(*b)) for b in buckets]
        x_buckets, y_buckets = list(zip(*transpose_buckets))
        attr_gain = _gain(y, y_buckets, impurity_func=_gini)

        # Compare candidate and update split attributre
        if attr_gain > best_gain:
            best_gain = attr_gain
            best_candidate = (attr, attr_values, x_buckets, y_buckets)

    # Generate tree
    mapping = {}
    best_attr, attr_values, x_buckets, y_buckets = best_candidate
    for value, next_x, next_y in zip(attr_values, x_buckets, y_buckets):
        next_x = np.array(next_x)
        next_attr = attrs - {best_attr}
        mapping[value] = decision_tree(next_x, next_y, attrs=next_attr)
    return (best_attr, mapping)

def traverse(instance, decision_tree):
    """Traverses the input decision tree to yield a prediction."""
    root_attr, mapping = decision_tree
    output = mapping[instance[root_attr]]
    if isinstance(output, np.int64):
        return output
    return traverse(instance, output)


import numpy as np

def cart2pol(x, y):
    rho = np.sqrt(x**2 + y**2)
    phi = np.arctan2(y, x)
    return rho, phi

def pol2cart(rho, phi):
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)
    return x, y

def cart2pol2(x, y):
    rho, phi = cart2pol(y, x)
    deg = phi * 180. / np.pi
    inds = deg < 0
    deg[inds] = 360. + deg[inds]
    return rho, deg

def pol2cart2(rho, deg):
    x, y = pol2cart(rho, float(deg)/180.*np.pi)
    return y, x


import numpy as np

def normalize(v):
    return v / np.linalg.norm(v)

def normalize_col(A):
    for i in range(A.shape[1]):
        A[:, i] = A[:,i] / np.linalg.norm(A[:,i])
    return A

def disturbation(A,disturb):
    _,m=A.shape
    U, D, V = np.linalg.svd(A)
    #print("U: {}, D: {}, V: {}".format(
    #    U.shape, D.shape, V.shape
    #))
    Atemp = U[:, :m].dot(np.diag(D + disturb)).dot(V)
    for i in range(m):
        Atemp[:, i] = normalize(Atemp[:,i])
    _, D, _ = np.linalg.svd(Atemp)

    return np.linalg.norm(D-np.ones(m))

def creating_nonQO(A, eps_A, seed=None):
    np.random.seed(seed)
    U,D,V = np.linalg.svd(A)
    m = A.shape[1]
    tol = 0.001
    I = np.ones(m)
    pert = np.random.normal(size = m)
    l = np.linalg.norm(pert)
    l_low = 0
    max_iterations = 100
    k = 0
    while True:
        xi = (l - l_low)/2 + l_low
        res = disturbation(A, pert*xi) - eps_A
        if np.abs(res) < tol or k > max_iterations:
            break
        elif res > 0:
            l = xi
        else:
            l_low = xi
        k = k+1
    Anew = U[:,:m].dot(np.diag(D+pert*xi)).dot(V)
    for i in range(m):
        Anew[:,i] = normalize(Anew[:,i])
    return Anew


from abc import ABC, abstractmethod
from typing import Optional
from copy import copy

import numpy as np

from shared_utils.typing import ArrayFunction

# TODO: Look into ``numjac'' algorithm - ``The MATLAB ODE Suite Shampine''

class Jacobian(ABC):
    def __init__(self, func: ArrayFunction, arg_idx: int = 0):
        self.func = func
        self.arg_idx = arg_idx

    @abstractmethod
    def __call__(self, *args):
        pass

class ForwardDiffJac(Jacobian):
    def __init__(self, func: ArrayFunction, arg_idx: int = 0, step_size: float = 1e-6):
        super().__init__(func, arg_idx=arg_idx)
        self.step_size: float = step_size

    def __call__(self, *args, pre_calc_f: Optional[np.array] = None):
        if pre_calc_f is None:
            f_0 = self.func(*args)
        else:
            f_0 = pre_calc_f

        args_step = list(args)
        col_set = []
        for idx, _ in enumerate(args[self.arg_idx]):
            args_step[self.arg_idx] = copy(args[self.arg_idx])
            args_step[self.arg_idx][idx] += self.step_size
            col_set.append((self.func(*args_step) - f_0) / self.step_size)

        return np.vstack(col_set).T

class CentralDiffJac(Jacobian):
    def __init__(self, func: ArrayFunction, arg_idx: int = 0, step_size: float = 1e-6):
        super().__init__(func, arg_idx=arg_idx)
        self.step_size: float = step_size

    def __call__(self, *args):

        args_step_p, args_step_m = list(args), list(args)
        col_set = []
        for idx, _ in enumerate(args[self.arg_idx]):
            args_step_p[self.arg_idx], args_step_m[self.arg_idx] = copy(args[self.arg_idx]), copy(args[self.arg_idx])
            args_step_p[self.arg_idx][idx] += self.step_size / 2
            args_step_m[self.arg_idx][idx] -= self.step_size / 2

            col_set.append((self.func(*args_step_p) - self.func(*args_step_m)) / self.step_size)

        return np.vstack(col_set).T

def saddle(y):
    return np.array([y[0]**2 - y[1], -y[1]**2 + y[0]])

def multi_saddle(x, y):
    return np.array([x[0] * y[0]**2 - y[1], -x[1] * y[1]**2 + y[0]])

if __name__ == '__main__':
    jac = CentralDiffJac(saddle)
    print(jac(np.array([10., 10.])))

    jac2 = CentralDiffJac(multi_saddle, arg_idx=1)
    print(jac2(np.array([1., 1.]), np.array([10., 10.])))


import numpy as np
a = np.array([1,2,3,4,5,6]) #one dimensional array
b = np.array([[1,2],[3,4],[5,6]]) #2 dimensional array
#printing the arrays
print(a)
print(b)

#MATRIX:

# BI-DIMENSIONAL
c = np.array([[1,2,3],[4,5,6]])
print(c)

#shape of matrix
shape_matrix = c.shape
print(shape_matrix)

#dimension of matrix
dimension_mat = c.ndim
print(dimension_mat)

#re-shaping the arrays
#(we know that we have declared "a" as a linear array ,so now we'll try to reshape this array and turn it into a 2x3 matrix)

a2 = a.reshape(2,3)
print(a2)

a3 = a.reshape(3,2)
print(a3)

#3 dimensional array
e = np.array([1,2,3,4,5,6,7,8,9,0,12,69])
a4 = e.reshape(2,3,2)
print(a4)
f = a4.ndim
print(f) #printing the dimension

#everything in one line:
g = np.array([1,2,3,4,5,6]).reshape(2,3).ndim #in this line we are declaring the array and reshaping it and checking the dimensions altogether
print(g)

#resizing the array

#(now we know that we reshaped the array a quite a few times...so lets see what happened to the array a itself)
print(a)
##as we can see in the output the array a is still a linear array ...that means while we were reshaping the array it just reshapes and used accd to that following command but the array itself doesnt change at all
##to change the shape of the array we need to use resize()

a.resize(3,2)
print(a) ## as we can see the array itself got changed

#data type of an array:
print(a.dtype) #here we get int32 it means all the elements of the array is integer

flt = np.array([1,2,3,4.5,6,7,8]).dtype
print(flt)#float64 stands for any elemnts..atleast one element in the array is a float number

compx = np.array([1,2,3,4+7j,5,6]).dtype
print(compx) #o/p is complex128 means atleast one value in the array is complex number

##we can also change datatype of an array
flt2 = np.array([1,2,3,4.5,6,7,8],dtype = int)
print(flt2) #as we're changing the dat type from float to inyteger the value after point is removed automatixally
flt3 = np.array([1,2,3,4.5,6,7,8],dtype = complex)
print(flt3) #every elemnt is turned in complex manner

#special arrays
##1 : ZERO ARRAYS
zero= np.zeros(6) #the number written in the bracket is the no. of elements that is the number of zeros in the array
print(zero)

zero2= np.zeros((2,3)) #declaring the dimension of zero array
print(zero2)

##2 one array
one = np.ones(4)
print(one)

one2 = np.ones((2,3))
print(one2)

##3 CONSTANT ARRAYS
const = np.full((6,2),69)
print(const)

##4 identity matrix
iden = np.eye(6)
print(iden)

##MATRIX WITH RANDOM NUMBERS

rndm_mat = np.random.random((3,3))
print(rndm_mat)

#printing simple series as arrays with arrange()
arrng = np.arange(1,69,2.3) #arange(starting,ending,difference)
print(arrng)

arrng2 = np.arange(1,12,3).reshape(2,2)
print(arrng2)

#to create equidistant points using linspace()
lnspce = np.linspace(0.1,0.9,4) #(starting point,ending point,number of equidistant points)
print(lnspce)


#-------------------------------------------------
# Python code for Bisection Method to
# determine the roots of the given equation
# provided a guess within which the roots must lie.
#-------------------------------------------------

#import the necessary libraries
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import spline

#Define the function prefixes using NumPy
sin = np.sin
cos = np.cos
tan = np.tan
pi  = np.pi
exp = np.exp
ln  = np.log
log = np.log10

fx = input("Enter the function whose roots you wish to determine :")      #take function as input from the user
f  = lambda x: eval(fx)                                                 # using lambda for defining function

#Enter the initial approximation and Error as Input from User
xl  = float((input("Enter the first value of guess interval :")))
xu  = float((input("Enter the end value of guess interval :")))
tol = float((input("Enter the allowed error (for eg. 0.1 or 0.01 or 0.001, etc.):")))
N   = int(input("Enter the no. of iterations you want to run:"))

# Define the actual algorithm as a function
def bisect(f, xl, xu, tol, N):
	if f(xl)*f(xu) > 0:	# This adds interaction with the user, we can however modify it to get the interval by running a for loop.
		print("Guesses are incorrect! Enter the new guesses")
		xl = float((input("Enter the first value of guess interval again :")))
		xu = float((input("Enter the end value of guess interval again:")))
		lastFuncVal = f(xl)
	else:
		for i in range(2, N):
			xr = (xl + xu)/2
			if f(xr) == 0 or (xu - xl)/2.0 < tol:
				return xr
			elif f(xr)*f(xl) > 0:
				xl = xr
			else:
				xu = xr
			lastFuncVal = f(xr)
		return "Method failed after {} iterations".format(N)

Ans = bisect(f, xl, xu, tol, N)			# define the output of the function as a variable 'Ans'

print ("Bisection method soln: x = ", Ans) # Print the solution

#Code to plot the curve
x = np.linspace(-10,10,10)
xnew = np.linspace(x.min(), x.max(), 300)       # Interpolates extra values to make smooth curve
f_smooth = spline(x, f(x), xnew) 				# Use the spline tool from SciPy to get smoother curves

#Plot the curves
fig = plt.figure()
plt.plot(xnew, f_smooth, '-b', label = 'function')
plt.plot(Ans, 0, 'ro', label = 'The root of the function')
plt.xlabel('x')
plt.ylabel('fx')
plt.savefig('bisection_exp.png')				# Export the image to the folder where you run the python3 command
plt.legend()

#Show the curve
plt.show()


import numpy as np

class Point:
    def __init__(self, x, y):
        self.__x = x
        self.__y = y

    def __repr__(self):
        return f"""x: {self.__x}, y: {self.__y}"""

    @property
    def y(self):
        return self.__y

    @property
    def x(self):
        return self.__x

def bisection_search(
    math_function, endpoint_min,
    endpoint_max, tolerance=1e-4, max_iteration=1000
):
    """We expect `math_function` to have its minima between `endpoint_min` and `endpoint_max`.

    Parameters
    ----------
    math_function: a function that takes in a float value with signature math_function(x)
    endpoint_min: float
    endpoint_max: float

    Returns
    -------
    Point: Point at the minima

    Raises
    ------
    AssertionError: If endpoint_min > endpoint_max
    AssertionError: If sign(math_function(endpoint_min)) == sign(math_function(endpoint_max))

    """
    assert endpoint_min < endpoint_max, "Input range not ordered."
    # The following assertion can be added for functions that are linear. Imagine a parabola, this assumption might break.
    # assert np.sign(math_function(endpoint_min)) != np.sign(math_function(endpoint_max))
    lower_x = endpoint_min
    higher_x = endpoint_max

    previous_val_at_x = np.inf
    current_iteration = 0
    while current_iteration < max_iteration:
        current_x = (higher_x + lower_x) / 2.0
        current_y_at_x = math_function(current_x)

        if (
            np.isclose(current_y_at_x, previous_val_at_x, atol=tolerance) or
            np.isclose(current_y_at_x, 0, atol=tolerance) or
            np.isclose(current_x / 2, higher_x, tolerance)
        ):
            break

        # If the sign at the lower point and the mid point are the same,
        # we make the lower bound go to the mid-point.
        if np.sign(current_y_at_x) == np.sign(function(lower_x)):
            lower_x = current_x
        else:
            # If not, we have crossed the zero, hence retain the lower point and update the higher.
            higher_x = current_x
        current_iteration += 1

    return Point(current_x, current_y_at_x)

if __name__ == "__main__":
    # The following line's y = 0 is at x=-1.5
    function = lambda x: 2 * x + 3
    assert np.isclose((bisection_search(math_function=function, endpoint_min=-20, endpoint_max=20)).x, -1.5, atol=1e-3)

    # Minima should be at 2
    function = lambda x: x**2 - 4
    print((bisection_search(math_function=function, endpoint_min=-20, endpoint_max=20)).x)
    assert np.isclose((bisection_search(math_function=function, endpoint_min=-20, endpoint_max=20)).x, -2, atol=1e-3)


import matplotlib.pyplot as plt
import numpy as np
from numpy import arange, zeros, log, sqrt

from scipy.stats import t

plt.style.use('seaborn')

from MaxLikelihoodFPLocDispT import MaxLikelihoodFPLocDispT

def CalibDegOfFreedomMLFP(x,FP,maxdf,stepdf):
    # Student t model
    # MLFP for mu and sigma on a grid of degrees of freedom (df) the best fit
    # corresponds to df which gives rise to the highest (log)likelihood L)
    #  INPUTS
    # x       :[vector](1 x t_end) empirical realizations
    # FP      :[vector](1 x t_end) flexible probabilities associated with vector x
    # maxdf   :[scalar] maximum value for nu to be checked
    # stepdf  :[scalar] step between consecutive values of nu to be checked
    #  OPS
    # mu      :[scalar] estimated location parameter
    # sig2    :[scalar] estimated dispersion parameter
    # nu      :[scalar] best degrees of freedom nu

    ## Code
    df=arange(1,maxdf+stepdf,stepdf)
    Tol=10**(-6)
    l_=len(df)

    Mu = zeros((l_,1))
    Sigma2 = zeros((l_,1))
    L = zeros((l_,1))
    for i in range(l_):
        Mu[i],Sigma2[i],_ = MaxLikelihoodFPLocDispT(x,FP,df[i],Tol,1)
        L[i]=FP@log(t.pdf((x - Mu[i]) / sqrt(Sigma2[i]), df[i]) / sqrt(Sigma2[i])).T

    imax= np.argmax(L)

    mu = Mu[imax]
    sig2 = Sigma2[imax]
    nu = df[imax]

    return mu, sig2, nu


import numpy as np

def positional_encoding(X, L):
    """
    The positional encoding as defined in the NeRF paper https://arxiv.org/pdf/2003.08934.pdf
      gamma(p) = (sin(2**0*pi*p), cos(2**0*pi*p), ..., sin(2**(L-1)*pi*p), cos(2**(L-1)*pi*p))
    Wherein p represents an arbitrary batched set of vectors computed by normalizing X between
    between -1 and 1
    """
    p = 2.*(X - X.min(-2)) / (X.max(-2) - X.min(-2)) - 1.
    L = np.arange(L, dtype=X.dtype)
    f = np.pi*2**L
    fp = (f[...,None,:]*p[...,:,None]).reshape(p.shape[:-1] + (-1,))
    return np.concatenate((
        np.cos(fp),
        np.sin(fp),
    ), axis=-1)


from model import graph_generator
from scipy.stats import norm
import math

class BlackScholes:
    __oneDay = 1/365
    # *********************** D1 & D2 **********************
    @classmethod
    def __d1(cls,S,K,R,T,Vol):
        """Class method so that the method has the access to the other methods inside the class without being forced
            to call BlackScholes.My_function() but with cls.My_function()"""
        return (math.log(S/K)+(R+(Vol**2)/2)*T)/(Vol*math.sqrt(T))

    @classmethod
    def __d2(cls,S,K,R,T,Vol):
        return cls.__d1(S,K,R,T,Vol)-Vol*math.sqrt(T)

    # *********************** PRICE FORMULAS **********************
    @classmethod
    def call_price(cls,S,K,R,T,Vol):
        """Return the price of a vanilla call"""
        price = S*norm.cdf(cls.__d1(S,K,R,T,Vol))-K*math.exp(-R*T)*norm.cdf(cls.__d2(S,K,R,T,Vol))
        return round(price,3)

    @classmethod
    def call_spread_price(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.call_price(S,K1,R,T,Vol)-cls.call_price(S,K2,R,T,Vol)
        return -cls.call_price(S, K1, R, T, Vol) + cls.call_price(S, K2, R, T, Vol)

    @classmethod
    def put_spread_price(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return -cls.put_price(S,K2,R,T,Vol)+cls.put_price(S,K1,R,T,Vol)
        return cls.put_price(S,K2,R,T,Vol)-cls.put_price(S,K1,R,T,Vol)

    @classmethod
    def put_price(cls,S,K,R,T,Vol):
        """Return the price of a vanilla put"""
        price = -S*norm.cdf(-cls.__d1(S,K,R,T,Vol))+K*math.exp(-R*T)*norm.cdf(-cls.__d2(S,K,R,T,Vol))
        return round(price,3)

    @classmethod
    def call_digital_price(cls,S,K,R,T,Vol):
        return round(math.exp(-R*T)*norm.cdf(cls.__d2(S,K,R,T,Vol)),3)

    @classmethod
    def put_digital_price(cls,S,K,R,T,Vol):
        return round(math.exp(-R*T)*norm.cdf(-cls.__d2(S,K,R,T,Vol)),3)

    @classmethod
    def straddle_price(cls,S,K,R,T,Vol):
        return cls.call_price(S,K,R,T,Vol)+cls.put_price(S,K,R,T,Vol)

    @classmethod
    def strangle_price(cls,S,K1,K2,R,T,Vol):
        return cls.put_price(S,K1,R,T,Vol)+cls.call_price(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_price(cls,S,K1,K2,R,T,Vol):
        return -cls.put_price(S,K1,R,T,Vol)+cls.call_price(S,K2,R,T,Vol)

    @classmethod
    def calendar_price(cls,S,K,R,T1,T2,Vol):
        return -cls.call_price(S,K,R,T1,Vol)+cls.call_price(S,K,R,T2,Vol)

    # *********************** DELTA FORMULAS **********************
    @classmethod
    def call_delta(cls,S,K,R,T,Vol):
        delta = norm.cdf(cls.__d1(S,K,R,T,Vol))
        return round(delta,3)

    @classmethod
    def call_spread_delta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.call_delta(S,K1,R,T,Vol) - cls.call_delta(S,K2,R,T,Vol)
        return -cls.call_delta(S,K1,R,T,Vol) + cls.call_delta(S,K2,R,T,Vol)

    @classmethod
    def put_delta(cls,S,K,R,T,Vol):
        delta = norm.cdf(cls.__d1(S,K,R,T,Vol))-1
        return round(delta,3)

    @classmethod
    def put_spread_delta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return - cls.put_delta(S,K2,R,T,Vol) + cls.put_delta(S,K1,R,T,Vol)
        return cls.put_delta(S, K2, R, T, Vol) - cls.put_delta(S, K1, R, T, Vol)

    @classmethod
    def call_digital_delta(cls,S,K,R,T,Vol):
        return round((norm.pdf(cls.__d2(S,K,R,T,Vol))/(S*Vol*math.sqrt(T)))*math.exp(-R*T),3)

    @classmethod
    def put_digital_delta(cls,S,K,R,T,Vol):
        return round(-cls.call_digital_delta(S,K,R,T,Vol),3)

    @classmethod
    def straddle_delta(cls,S,K,R,T,Vol):
        return cls.call_delta(S,K,R,T,Vol)+cls.put_delta(S,K,R,T,Vol)

    @classmethod
    def strangle_delta(cls,S,K1,K2,R,T,Vol):
        return cls.put_delta(S,K1,R,T,Vol)+cls.call_delta(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_delta(cls,S,K1,K2,R,T,Vol):
        return -cls.put_delta(S, K1, R, T, Vol) + cls.call_delta(S, K2, R, T, Vol)

    @classmethod
    def calendar_delta(cls,S,K,R,T1,T2,Vol):
        return -cls.call_delta(S,K,R,T1,Vol)+cls.call_delta(S,K,R,T2,Vol)

    # *********************** GAMMA FORMULAS **********************
    @classmethod
    def gamma(cls,S,K,R,T,Vol):
        gamma = (1/(S*Vol*math.sqrt(T)))*norm.pdf(cls.__d1(S,K,R,T,Vol))
        return round(gamma,3)

    @classmethod
    def call_spread_gamma(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.gamma(S,K1,R,T,Vol)-cls.gamma(S,K2,R,T,Vol)
        return -cls.gamma(S,K1,R,T,Vol)+cls.gamma(S,K2,R,T,Vol)

    @classmethod
    def put_spread_gamma(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return - cls.gamma(S,K2,R,T,Vol) + cls.gamma(S,K1,R,T,Vol)
        return cls.gamma(S,K2,R,T,Vol) - cls.gamma(S,K1,R,T,Vol)

    @classmethod
    def call_digital_gamma(cls,S,K,R,T,Vol):
        return round((math.exp(-R*T)*norm.pdf(cls.__d2(S,K,R,T,Vol))*cls.__d1(S,K,R,T,Vol))/(S**2*Vol**2*T),3)

    @classmethod
    def put_digital_gamma(cls,S,K,R,T,Vol):
        return round(-cls.call_digital_gamma(S,K,R,T,Vol),3)

    @classmethod
    def straddle_gamma(cls,S,K,R,T,Vol):
        return 2*cls.gamma(S,K,R,T,Vol)

    @classmethod
    def strangle_gamma(cls,S,K1,K2,R,T,Vol):
        return cls.gamma(S,K1,R,T,Vol)+cls.gamma(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_gamma(cls,S,K1,K2,R,T,Vol):
        return -cls.gamma(S, K1, R, T, Vol) + cls.gamma(S, K2, R, T, Vol)

    @classmethod
    def calendar_gamma(cls, S, K, R, T1, T2, Vol):
        return -cls.gamma(S, K, R, T1, Vol) + cls.gamma(S, K, R, T2, Vol)

    # *********************** VEGA FORMULAS **********************
    @classmethod
    def vega(cls,S,K,R,T,Vol):
        vega = S*math.sqrt(Vol)*norm.pdf(cls.__d1(S,K,R,T,Vol))
        return round(vega,3)

    @classmethod
    def call_spread_vega(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.vega(S,K1,R,T,Vol) - cls.vega(S,K2,R,T,Vol)
        return - cls.vega(S,K1,R,T,Vol) + cls.vega(S,K2,R,T,Vol)

    @classmethod
    def call_digital_vega(cls,S,K,R,T,Vol):
        return round(-math.exp(-R*T)*cls.__d1(S,K,R,T,Vol)*norm.pdf(cls.__d2(S,K,R,T,Vol))/Vol,3)

    @classmethod
    def put_spread_vega(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return - cls.vega(S,K2,R,T,Vol) + cls.vega(S,K1,R,T,Vol)
        return cls.vega(S,K2,R,T,Vol) - cls.vega(S,K1,R,T,Vol)

    @classmethod
    def put_digital_vega(cls,S,K,R,T,Vol):
        return round(-cls.call_digital_vega(S,K,R,T,Vol),3)

    @classmethod
    def straddle_vega(cls,S,K,R,T,Vol):
        return 2*cls.vega(S,K,R,T,Vol)

    @classmethod
    def strangle_vega(cls,S,K1,K2,R,T,Vol):
        return cls.vega(S,K1,R,T,Vol)+cls.vega(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_vega(cls, S, K1, K2, R, T, Vol):
        return -cls.vega(S, K1, R, T, Vol) + cls.vega(S, K2, R, T, Vol)

    @classmethod
    def calendar_vega(cls, S, K, R, T1, T2, Vol):
        return -cls.vega(S, K, R, T1, Vol) + cls.vega(S, K, R, T2, Vol)

    # *********************** THETA FORMULAS **********************
    @classmethod
    def call_theta(cls,S,K,R,T,Vol):
        d2 = cls.__d2(S,K,R,T,Vol)
        theta = -(K*Vol*norm.pdf(d2)*math.exp(-R*T))/(2*math.sqrt(T)) - R*K*norm.cdf(d2)*math.exp(-R*T)
        return round(theta,3)

    @classmethod
    def call_spread_theta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return cls.call_theta(S,K1,R,T,Vol) - cls.call_theta(S,K2,R,T,Vol)
        return - cls.call_theta(S,K1,R,T,Vol) + cls.call_theta(S,K2,R,T,Vol)

    @classmethod
    def call_digital_theta(cls,S,K,R,T,Vol):
        disc_factor = math.exp(-R*T)
        d1 = cls.__d1(S,K,R,T,Vol)
        d2 = cls.__d2(S,K,R,T,Vol)
        return round(R*disc_factor*norm.cdf(d2)+disc_factor*norm.pdf(d2)*((d1/(2*T)) - (R/(Vol*math.sqrt(T)))),3)

    @classmethod
    def put_theta(cls,S,K,R,T,Vol):
        d1 = cls.__d1(S, K, R, T, Vol)
        d2 = cls.__d2(S, K, R, T, Vol)
        theta = R*K*math.exp(-R*T)*norm.cdf(-d2) - S*norm.pdf(d1)*Vol/(2*math.sqrt(T))
        return round(theta,3)

    @classmethod
    def put_spread_theta(cls,S,K1,K2,R,T,Vol,isbull):
        if isbull:
            return -cls.put_theta(S,K2,R,T,Vol) + cls.put_theta(S,K1,R,T,Vol)
        return cls.put_theta(S,K2,R,T,Vol) - cls.put_theta(S,K1,R,T,Vol)

    @classmethod
    def put_digital_theta(cls,S,K,R,T,Vol):
        return round(-R*math.exp(-R*T) - cls.call_digital_theta(S,K,R,T,Vol),3)

    @classmethod
    def straddle_theta(cls,S,K,R,T,Vol):
        return cls.call_theta(S,K,R,T,Vol)+cls.put_theta(S,K,R,T,Vol)

    @classmethod
    def strangle_theta(cls,S,K1,K2,R,T,Vol):
        return cls.put_theta(S,K1,R,T,Vol)+cls.call_theta(S,K2,R,T,Vol)

    @classmethod
    def risk_rev_theta(cls, S, K1, K2, R, T, Vol):
        return -cls.put_theta(S, K1, R, T, Vol) + cls.call_theta(S, K2, R, T, Vol)

    @classmethod
    def calendar_theta(cls, S, K, R, T1, T2, Vol):
        return -cls.call_theta(S, K, R, T1, Vol) + cls.call_theta(S, K, R, T2, Vol)

    # *********************** PAYOFF GRAPH CALLERS **********************
    @classmethod
    def payoff_lists(cls, S, K, R, T, Vol, call_price, put_price):
        """Return a tuple : (list of strikes, list of Y curve , list of title)"""
        return graph_generator.Plotter.get_payoff_lists((cls.call_price, cls.put_price)
                                                        , K, R, T, Vol,cls.__oneDay,
                                                        ["Call", "Put"],
                                                        (call_price, put_price))

    @classmethod
    def payoff_call_spread(cls,K1,K2,R,T,Vol,bull_price,bear_price):
        return graph_generator.Plotter.get_payoff_strategy((cls.call_spread_price,cls.call_spread_price,cls.call_spread_price,cls.call_spread_price),
                                                                  ((K1,K2,R,T,Vol,True),(K1,K2,R,cls.__oneDay,Vol,True),(K1,K2,R,T,Vol,False),(K1,K2,R,cls.__oneDay,Vol,False)),
                                                                  ["Bull Sp.","Bear Sp."],
                                                                  (bull_price,bull_price, bear_price,bear_price),
                                                                  (K1+K2)/2)

    @classmethod
    def payoff_put_spread(cls, K1, K2, R, T, Vol, bull_price, bear_price):
        return graph_generator.Plotter.get_payoff_strategy((cls.put_spread_price, cls.put_spread_price,cls.put_spread_price, cls.put_spread_price),
                                                                  ((K1, K2, R, T, Vol, True),(K1, K2, R, cls.__oneDay, Vol, True),
                                                                   (K1, K2, R, T, Vol, False),(K1, K2, R, cls.__oneDay, Vol, False)),
                                                                  ["Bull Spread","Bear Spread"],
                                                                 (bull_price,bull_price, bear_price,bear_price),
                                                                  (K1 + K2) / 2)

    @classmethod
    def payoff_digital_graph(cls, S, K, R, T, Vol, call_price, put_price):
        return graph_generator.Plotter.get_payoff_lists((cls.call_digital_price, cls.put_digital_price)
                                                         , K, R, T, Vol,cls.__oneDay,
                                                         ["Digital Call", "Digital Put"],
                                                         (call_price, put_price))

    @classmethod
    def straddle_payoff_graph(cls,S,K,R,T,Vol,straddle_price):
        return graph_generator.Plotter.get_payoff_lists((cls.straddle_price,)
                                                         , K, R, T, Vol,cls.__oneDay,
                                                         ["Straddle"],
                                                         (straddle_price,))

    @classmethod
    def strangle_payoff_graph(cls,S,K1,K2,R,T,Vol,price):
        return graph_generator.Plotter.get_payoff_strategy([cls.strangle_price,cls.strangle_price]
                                                         , [[K1,K2, R, T, Vol],[K1,K2, R, cls.__oneDay, Vol]],
                                                         ["Strangle"],
                                                         [price,price],(K1+K2)/2)

    @classmethod
    def risk_rev_payoff_graph(cls, S, K1, K2, R, T, Vol,price):
         return graph_generator.Plotter.get_payoff_strategy([cls.risk_rev_price,cls.risk_rev_price]
                                                                  , [[K1, K2, R, T, Vol],[K1, K2, R, cls.__oneDay, Vol]],
                                                                  ["Risk Reversal"],
                                                                  [price,price], (K1 + K2) / 2)

    @classmethod
    def calendar_payoff_graph(cls, S, K, R, T1, T2, Vol,price):
        return graph_generator.Plotter.get_payoff_strategy([cls.calendar_price,cls.calendar_price], [[K, R, T1, T2, Vol],[K, R, cls.__oneDay,  T2-T1+cls.__oneDay, Vol]],
                                                           ["Calendar Spread"],
                                                           [price,price], K)

    # *********************** GREEKS GRAPH CALLERS **********************
    @classmethod
    def greeks_graph(cls,S,K,R,T,Vol):
        list_greeks = (cls.gamma,cls.gamma,cls.call_delta,cls.put_delta,cls.call_theta,cls.put_theta,cls.vega,cls.vega)
        list_title = ["Call Gamma","Put Gamma","Call Delta","Put Delta","Call Theta","Put Theta","Call Vega","Put Vega"]
        return graph_generator.Plotter.get_graph_greeks(list_greeks,K,R,T,Vol,cls.__oneDay,list_title)

    @classmethod
    def greeks_digital_graph(cls,S,K,R,T,Vol):
        list_greeks = (
        cls.call_digital_gamma, cls.put_digital_gamma, cls.call_digital_delta, cls.put_digital_delta, cls.call_digital_theta, cls.put_digital_theta, cls.call_digital_vega, cls.put_digital_vega)
        list_title = [
        "Digital Call Gamma", "Digital Put Gamma", "Digital Call Delta", "Digital Put Delta", "Digital Call Theta", "Digital Put Theta","Digital Call Vega", "Digital Put Vega"]
        return graph_generator.Plotter.get_graph_greeks(list_greeks, K, R, T, Vol,cls.__oneDay, list_title)

    @classmethod
    def call_spread_greeks_graph(cls,K1,K2,R,T,Vol):
        list_greeks = (
        cls.call_spread_gamma, cls.call_spread_gamma, cls.call_spread_delta, cls.call_spread_delta, cls.call_spread_theta, cls.call_spread_theta, cls.call_spread_vega, cls.call_spread_vega)
        list_title = [
        "Bull Gamma", "Bear Gamma", "Bull Delta", "Bear Delta", "Bull Theta", "Bear Theta" , "Bull Vega", "Bear Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks(list_greeks, ((K1,K2,R,T,Vol,True),(K1,K2,R,cls.__oneDay,Vol,True),(K1,K2,R,T,Vol,False),(K1,K2,R,cls.__oneDay,Vol,False)), list_title,(K1+K2)/2)

    @classmethod
    def put_spread_greeks_graph(cls,K1,K2,R,T,Vol):
        list_greeks = (
            cls.put_spread_delta, cls.put_spread_delta, cls.put_spread_gamma, cls.put_spread_gamma,
            cls.put_spread_vega, cls.put_spread_vega, cls.put_spread_theta, cls.put_spread_theta)
        list_title = [
            "Bull Gamma", "Bear Gamma", "Bull Delta", "Bear Delta", "Bull Theta",
            "Bear Theta" ,"Bull Vega", "Bear Vega"]

        return graph_generator.Plotter.get_graph_strategy_greeks(list_greeks, (
        (K1, K2, R, T, Vol, True),(K1, K2, R, cls.__oneDay, Vol, True), (K1, K2, R, T, Vol, False),(K1, K2, R, cls.__oneDay, Vol, False)), list_title, (K1 + K2) / 2)

    @classmethod
    def straddle_greeks_graph(cls, S, K, R, T, Vol):
        list_greeks = (cls.straddle_gamma, cls.straddle_delta, cls.straddle_theta, cls.straddle_vega)
        list_title = ["Gamma","Delta", "Theta", "Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K, R, T, Vol),(K, R, cls.__oneDay, Vol)), list_title, K)

    @classmethod
    def strangle_greeks_graph(cls, S, K1, K2, R, T, Vol):
        list_greeks = (cls.strangle_gamma,cls.strangle_delta, cls.strangle_theta, cls.strangle_vega)
        list_title = ["Gamma", "Delta", "Theta", "Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K1, K2, R, T, Vol),(K1, K2, R, cls.__oneDay, Vol)), list_title,
                                                                          (K1 + K2) / 2)

    @classmethod
    def risk_rev_greeks_graph(cls, S, K1, K2, R, T, Vol):
        list_greeks = (cls.risk_rev_gamma,cls.risk_rev_delta, cls.risk_rev_theta,cls.risk_rev_vega)
        list_title = ["Gamma", "Delta","Theta","Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K1, K2, R, T, Vol),(K1, K2, R, cls.__oneDay, Vol)), list_title,
                                                                          (K1 + K2) / 2)

    @classmethod
    def calendar_greeks_graph(cls, S, K, R, T1, T2, Vol):
        list_greeks = (cls.calendar_gamma, cls.calendar_delta, cls.calendar_theta, cls.calendar_vega)
        list_title = ["Gamma", "Delta", "Theta", "Vega"]
        return graph_generator.Plotter.get_graph_strategy_greeks_one_leg(list_greeks, ((K, R, T1, T2, Vol),(K, R, cls.__oneDay, T2-T1+cls.__oneDay, Vol)), list_title,K)


import numpy as np
from math import cos, sin, atan2, pi
import matplotlib.pyplot as plt

def circular_mean(X, I):
    """Compute the circular mean of the given set of values X in I.

    Args:
        X (list[float]): The set of values.
        I (tuple[float,float]): The range these values are in.

    Returns:
        float: The circular mean.
    """
    assert I[0] < I[1]
    lenI = I[1] - I[0]
    pulse = 2 * pi / lenI
    sum_cos = 0.
    sum_sin = 0.
    for x in X:
        sum_cos += cos(pulse * x)
        sum_sin += sin(pulse * x)
    return atan2(sum_sin, sum_cos) / pulse

if __name__ == "__main__":
    vs = np.array([np.array([-5, 4]), np.array([4, -5])])
    mean_v = np.array([circular_mean(vs[:, 0], (-5, 5)),
                       circular_mean(vs[:, 1], (-5, 5))])
    print(f"vs: {vs}")
    print(f"mean: {mean_v}")

    for v in vs:
        plt.plot(v[0], v[1], '.k')
    plt.plot(mean_v[0], mean_v[1], '.g')
    plt.show()


import scipy as sc
import numpy as np
from matplotlib import pyplot as plt

# https://en.wikipedia.org/wiki/Gaussian_function
def Gaussian(x,a,b,c):
    up = ((x-b)**2.0)/(2.0*c**2.0)
    return a*np.exp(-up)

# https://en.wikipedia.org/wiki/Relativistic_Breit-Wigner_distribution
def BreitWigner(e,a,m,g):
    gamma = np.sqrt(m**2.0*(m**2.0 + g**2.0))
    k = (2.0*np.sqrt(2.0)*m*g*gamma)/(np.pi*np.sqrt(m**2.0 + gamma))
    lower = (e**2.0 - m**2.0)**2.0 + m**2.0*g**2.0
    return a*k/lower

def exponential(x,a,b,c):
    return a*np.exp(-b*x) + c

def linear(x,m,b):
    return m*x + b

def poly2(x, a,b,c):
    return a + b*(x) + c*(x**2.0)

def poly3(x, a,b,c,d):
    return a + b*(x) + c*(x**2.0) + d*(x**3.0)

def poly4(x, a,b,c,d,e):
    return a + b*(x) + c*(x**2.0) + d*(x**3.0) + e*(x**4.0)

class FittingResult(object):
    def __init__(self,func,xdata,popt,pcov,label="Result"):
        self.func = func
        self.xdata = xdata
        self.popt = popt
        self.pcov = pcov
        self.label = label
        self.errs = np.sqrt(np.diag(self.pcov))

    def __repr__(self):
        return "Parameters:\n{}".format(
            "\n".join(["\tx{} = {:.3f} +- {:.3f}".format(i,x,self.errs[i]) for i,x in enumerate(self.popt)])
        )

    def _repr_html_(self):
        return "
Parameters:

{}
".format(
            "\n".join(["
x{} = {:.3f} +- {:.3f}
".format(i,x,self.errs[i]) for i,x in enumerate(self.popt)])
        )

    def plot(self,show=False,error=False):
        if show:
            plt.figure()
        ydata = [self.func(x, *self.popt) for x in self.xdata]
        plt.plot(self.xdata,ydata,label=self.label+".Fit")
        plt.legend()
        if error:
            peydata = [self.func(x, *(o+e for o,e in zip(self.popt,self.errs))) for x in self.xdata]
            neydata = [self.func(x, *(o-e for o,e in zip(self.popt,self.errs))) for x in self.xdata]
            plt.fill_between(self.xdata,peydata,neydata,alpha=0.6)
        if show:
            plt.show()


import numpy as np
import math as m
# matrix construction ########################################################

def identity():
  return np.array([[1., 0., 0., 0.],
	           [0., 1., 0., 0.],
	           [0., 0., 1., 0.],
	           [0., 0., 0., 1.]])

def scale(sx=1., sy=1., sz=1.):
  return np.array([[sx, 0., 0., 0.],
	           [0., sy, 0., 0.],
	           [0., 0., sz, 0.],
	           [0., 0., 0., 1.]])

def translate(tx=0., ty=0., tz=0.):
  return np.array([[1., 0., 0., tx],
	           [0., 1., 0., ty],
	           [0., 0., 1., tz],
	           [0., 0., 0., 1.]])

# rotation with angle a in radians about axis (x,y,z)
def rotate(a=0., x=0., y=0., z=1.):
  s, c = m.sin(a), m.cos(a)
  h = m.sqrt(x*x + y*y + z*z)
  x, y, z = x/h, y/h, z/h
  sx, sy, sz = s*x, s*y, s*z
  oc = 1.-c
  return np.array([[oc*x*x+c,  oc*x*y-sz, oc*x*z+sy, 0.],
	           [oc*x*y+sz, oc*y*y+c,  oc*y*z-sx, 0.],
	           [oc*x*z-sy, oc*y*z+sx, oc*z*z+c,  0.],
	           [0.,        0.,        0.,        1.]])
# orthogonal projection
def ortho(l, r, b, t, n, f):
  w, h, d = r-l, t-b, f-n
  return np.array([[2./w, 0.,    0.,   -(r+l)/w],
	           [0.,   2./h,  0.,   -(t+b)/h],
	           [0.,   0.,   -2./d, -(f+n)/d],
	           [0.,   0.,    0.,    1.     ]])

# perspective projection, map frustum to unit cube
def frustum(l, r, b, t, n, f):
  w, h, d = r-l, t-b, f-n
  return np.array([[2.*n/w, 0.,      (r+l)/w,  0.      ],
	           [0.,     2.*n/h,  (t+b)/h,  0.      ],
	           [0.,     0.,     -(f+n)/d, -2.*f*n/d],
	           [0.,     0.,     -1.,       0.      ]])

# manipulation ###############################################################
def matrix(n, m, f=lambda i, j: 0.):
  I, J = range(n), range(m)
  return np.array([[f(i, j) for j in J] for i in I])

def size(A):
  return A.shape

def transpose(A):
  return np.transpose(A)

# opengl format
def column_major(A):
  return [float(a) for line in transpose(A) for a in line]

def exclude(A, i, j):
  return [R[:j]+R[j+1:] for R in A[:i]+A[i+1:]]

def top_left(A):
  n, m = size(A)
  return exclude(A, n-1, m-1)

# sum ########################################################################
def add(A, B):
  n, p = size(A)
  q, m = size(B)
  assert (n, p) == (q, m)
  return matrix(n, m, lambda i, j: A[i][j]+B[i][j])

def sub(A, B):
  n, p = size(A)
  q, m = size(B)
  assert (n, p) == (q, m)
  return matrix(n, m, lambda i, j: A[i][j]-B[i][j])
# product ####################################################################
def scalar(s, A):
  n, m = size(A)
  return matrix(n, m, lambda i, j: s*A[i][j])
def mul(A, B):
  return np.dot(A,B)
def product(A, *Bs):
  for B in Bs:
    A = np.dot(A,B)
  return A
def vapply(A,v):
  return np.dot(A,v)
# inverse ####################################################################
def det(A):
  return np.linalg.det(A)
def inverse(A):
  return np.linalg.inv(A)
# vector #####################################################################

# construction from list or tuples
def point(tpl):
  return np.array(tpl)
# vector from p1 to p0
def vector(p0, p1):
  return p0-p1

# homogenous vector from point p with weight w
def hom(p,w):
  return np.array([c*w for c in p] + [w]) if w else np.array([c for c in p] + [w])
# projection of homogenous vector
def proj(h):
  return np.array([h[i]/h[-1] if h[-1] else h[i] for i in range(0,len(h)-1)])
def cut(h):
  return np.array([h[i] for i in range(0,len(h)-1)])

# vector from coords
def coords(*args,dtype=float):
  return np.array(args,dtype=dtype)

# dot product of u and v: norm(u)*norm(v)*cos(angle between u and v)
def dot(u, v):
  return np.dot(u,v)
# squared norm of v
def norm2(v):
  return dot(v, v)
# norm of v, length of v
def norm(v):
  return m.sqrt(norm2(v))
# return unit vector in direction of v and length of v
def unit_length(v):
  n = norm(v)
  return point(v)/n, n
# return unit vector in direction of v
def unit(v):
  n = norm(v)
  #import pdb ; pdb.set_trace()
  return v/n
# square matrix filled with v in each row
def vmatrix(v):
  return np.array([[vi] for vi in v])

# parallel part of x to y: the projection of x onto y
def para(x,y):
  return dot(x,y)/norm2(y) * y
# perpendicular part of x to y:
# x == para(x,y)+perp(x,y), dot(para(x,y),perp(x,y)) == 0
def perp(x,y):
  return x - para(x,y)
# reflection of x about y
def refl(x,y):
  return -x + 2*para(x,y)

# perpendicular vector to 2d vector x, counterclockwise
def perp2ccw(x):
  return coords(-x[1],x[0])

def perp2cw(x):
  return coords(x[1],-x[0])


"""
polynomial_dissimilarity.py
--------------

From
----
Donnat, Claire, and Susan Holmes. "Tracking
network dynamics: A survey of distances
and similarity metrics." arXiv
preprint arXiv:1801.07351 (2018).

author: Jessica T. Davis
email:
Submitted as part of the 2019 NetSI Collabathon.

"""
import numpy as np
import networkx as nx
import scipy.sparse as ss
from .base import BaseDistance

class PolynomialDissimilarity(BaseDistance):
    def dist(self, G1, G2,k=5,alpha=1):
        """
        The function calculates the distance between two graphs by
        comparing the polynomials of the eigenvalue decomposition of
        their adjacency matrices, where $A^k$ is the adjacency matrix
        raised to the kth power and the the elements correspond to
        the number of paths of length k between nodes i and j.

        Params
        ------

        G1, G2 (nx.Graph): two networkx graphs to be compared.
        k (float): maximum degree of the polynomial
        alpha (float): weighting factor

        Returns
        -------
        dist (float): Polynomial Dissimilarity
        """
        A1 = nx.to_numpy_array(G1)
        A2 = nx.to_numpy_array(G2)

        P_A1 = calculate_polynomial(A1,k,alpha)
        P_A2 = calculate_polynomial(A2,k,alpha)

        dist = np.linalg.norm(P_A1-P_A2,ord='fro')/A1.shape[0]**2

    return dist

def calculate_polynomial(A,k,alpha):
    eig_vals,Q = np.linalg.eig(A)

    n = A.shape[0]
    W = np.diag(sum(list(map(lambda kp:eig_vals**kp/(n-1)**(alpha*kp-1), range(1,k+1)))))

    P_A  = np.dot(np.dot(Q,W),Q.T)

return P_A

import numpy,import scipy,import networkx