import numpy as np
from .tomohelpers import *

"""
purity(rho):

calculates the purity of rho. Defined as the trace of rho ** 2

Parameters:
----------------------
rho: np.matrix

"""

def purity(rho):
    purity = np.trace(np.matmul(rho,rho))
    return np.round(np.real(purity), 3)

"""
s_param(n, rho)

Calculate the s-parameter. Currently only available for two qubit systems.

Parameters:
----------------------

n: int
    number of qubits in the system.

rho: numpy array
    numpy array representing the density matrix of a quantum state

"""

def s_param(n, rho):

    X = np.matrix([[0,1], [1,0]])
    Z = np.matrix([[1,0], [0,-1]])

    M1 = -1/np.sqrt(2)*(Z+X)
    M2 = -1/np.sqrt(2)*(Z-X)

    s = 0

    if n == 2:
        ops = [np.kron(X, M1),np.kron(X, M2),np.kron(Z, M1),np.kron(Z, M2)]
        for i in ops:
            expval = np.trace(np.matmul(rho,i))
            s += abs(expval)

        return np.round(s, 3)

    else:
        print("S-parameter: This is only available for two-qubit systems. S-param was not calculated")

"""
concurrence(rho)

Calculate the concurrence. Currently only available for two qubit systems.

Parameters:
----------------------

rho: numpy array
    numpy array representing the density matrix of a quantum state

"""

def concurrence(rho):
    eigvals, eigvecs = np.linalg.eig(rho)
    eigvals = np.sort(eigvals)[::-1]
    con = eigvals[0]
    for i in eigvals:
        if i == eigvals[0]:
            continue
        else:
            con = con - i

    return max(0, np.round(np.real(con), 3))

"""
tangle(concurrence)

Calculate the tangle. Currently only available for two qubit systems.

Parameters:
----------------------

concurrence: num
    The value of the concurrence of the system.

"""

def tangle(concurrence):
    return concurrence ** 2

"""
stokes_params(rho, d)

Calculates the Stoke's Parameters of a given quantum state density matrix.

Parameters:
----------------------

rho: numpy array
    numpy array representing the density matrix of a quantum state

d: int
    represents the dimension of our quantum system

n: int
    number of qudits in the system

"""

def stokes_params(rho, d, n):
    stokes_params = []
    gell_manns = generate_gellman(d)
    sp_matricies = gen_sp_matricies(n, gell_manns, gell_manns)

    for i in sp_matricies:
        #print(i, np.size(i), rho, np.size(rho))
        stoke_param = np.trace(np.matmul(i, rho))
        stokes_params.append(round(stoke_param, 3))

    return stokes_params


# -*- coding: utf-8 -*-
"""GMM_EM.ipynb

Automatically generated by Colaboratory.

# Gaussian mixture model with expectation maximization algorithm

GMM with EM.

This notebook implements the following:

1) Function that avoids computing inverse of matrix when computing y=(A_inverse)x by solving system of linear equations.

2) Log sum trick to avoid underflow when multiplying small numbers.

3) pdf of the Multivariate normal distribution

4) E-step function of the EM algorithm

5) M-step function of the EM algorithm

6) Variational lower bound function

7) GMM function

8) Training function for GMM

9) Scatter plot of clusters (Plot at the bottom of this notebook shows 8 clusters from a dataset of 100 points)

# Imports
"""

# Commented out IPython magic to ensure Python compatibility.
import sys
import numpy as np
from numpy.linalg import det, solve
import matplotlib
import matplotlib.pyplot as plt
# %matplotlib inline

"""# Variables info

N: number of data (rows)

d: dimension of data

X: (N x d), data points

C: int, number of clusters

Computed from E-step:

gamma: (N x C), distribution q(T), probabilities of clusters for objects

Initial values are also subsequently computed & updated from M-step:

pi: (C), mixture component weights, initial weights of T (latent variable), sum to 1, t=1, 2 or 3.
mu: (C x d), mixture component means

sigma: (C x d x d), # mixture component covariance matrices

# Generate random data.
"""

N = 100
d = 2

X = np.random.rand(N,d)

print(X[:5])
print(X.shape)

fig, ax = plt.subplots(1,1, figsize=(15,10))
ax.set_title('Data')
ax.scatter(X[:, 0], X[:, 1], c='black', s=100)
#plt.axis('equal')
plt.show()

"""# Generate initial values"""

epsilon = 1e-10 # Use in stopping criterion as well as in preventing numerical errors.

C = 7

def rand_input(C, d):
  # https://stackoverflow.com/questions/18659858/generating-a-list-of-random-numbers-summing-to-1
  pi0 = np.random.dirichlet(np.ones(C),size=1)[0] # Generating a list of random numbers, summing to 1
  mu0 = np.random.rand(C,d)
  sigma0 = np.random.rand(C,d,d)
  return pi0, mu0, sigma0

pi0, mu0, sigma0 = rand_input(C, d)

print(pi0)
print(pi0.shape)
print(mu0)
print(mu0.shape)
print(sigma0)
print(sigma0.shape)

"""# Avoid computing inverse of matrix when computing y=(A_inverse)x."""

# Function which avoids computing inverse of matrix when computing y=(A_inverse)x by solving linear equations.
# Use in E-step.

def _A_inverse_times_X(A, X):
  # A is nxn
  # X is rxn

  Y = []
  for row_data in X:
    Y_new = np.linalg.solve(A, row_data)
    Y.append(Y_new)

  Y = np.asarray(Y)
  assert Y.shape == X.shape, "Output shape must be equal to shape of X."

  return Y

"""# Multivariate normal (Gaussian) distribution

$MVN = \frac{1}{\sqrt{(2\pi)^n|\boldsymbol\Sigma_c|}}
\exp\left(-\frac{1}{2}({x}-{\mu_c})^T{\boldsymbol\Sigma_c}^{-1}({x}-{\mu_c})\right)$

Computes pdf of Multivariate normal (Gaussian) distribution.
"""

# Alternatively, one could also use multivariate_normal.pdf from scipy.stats
# instead of this function

def __mvg(cov, X, mean):
  diff = X - mean
  Y = _A_inverse_times_X(cov, diff)
  pow_term = -0.5 * np.matmul(Y, diff.T)
  e_term = np.exp(pow_term)

  const_term = (2*np.pi)**(X.shape[1])
  det_term = np.linalg.det(cov)
  deno_term = np.sqrt(np.multiply(const_term, det_term))

  P = np.divide(e_term, deno_term)

  return P.diagonal() # returns the pdf, shape=(num_X,)

# Returns pdf for multiple components.

def _mvg(cov, X, mean):
  P = []
  for i, r in enumerate(mean):
    P.append(__mvg(cov[i], X, mean[i]))

  return P # shape=(C, num_X)

"""# Log sum trick"""

# log sum trick to prevent underflow in E-step.
# https://timvieira.github.io/blog/post/2014/02/11/exp-normalize-trick/
# https://web.archive.org/web/20150502150148/http://machineintelligence.tumblr.com/post/4998477107/the-log-sum-exp-trick
# https://www.quora.com/Why-is-e-log_-e-x-equal-to-x

def exp_normalize(x):
  b = x.max()
  y = np.exp(x - b)

  return y / y.sum()

"""# E-step

Multiply the initial weight of the class with the multivariate guassian pdf of each data from the class.
"""

def E_step(X, pi, mu, sigma):
    N = X.shape[0] # number of objects
    C = pi.shape[0] # number of clusters
    d = mu.shape[1] # dimension of each object
    gamma = np.zeros((N, C)) # distribution q(T)
    gamma = np.mat(np.zeros((N, C)))

    prob = _mvg(sigma, X, mu) # pdf of data X in each class
    prob = np.mat(prob)

    for c in range(C):
        # Instead of multiplying probabilities directly which could result in underflow,
        # we'll work in log scale.
        # pi[c] = P(T=c), prob[c, :] = P(X|T=c)
        #gamma[:, c] = np.multiply(pi[c], prob[c, :].T)
        gamma[:, c] = np.log(pi[c] + epsilon) + np.log(prob[c, :].T + epsilon)

    for i in range(N):
        # Instead of summing the denominator, we'll use the log sum trick coded in exp_normalize function.
        gamma[i, :] = exp_normalize(gamma[i, :])

    return gamma # Q(T) = P(T|X,theta), weights of each model (class) for each data in X.

"""# M-Step

Compute the following:

[Equations from wiki](https://en.wikipedia.org/wiki/Expectation%E2%80%93maximization_algorithm#E_step)

![alt text](https://wikimedia.org/api/rest_v1/media/math/render/svg/0e0327c8676ae66ec651b422a19f5ea532913c7a)

![alt text](https://wikimedia.org/api/rest_v1/media/math/render/svg/45f3e73f50d396aadc98182709eee0c0d513aa6b)

![alt text](https://wikimedia.org/api/rest_v1/media/math/render/svg/a92651be432155520db19dc0b4da807039d96eb0)
"""

def M_step(X, gamma):
    N = X.shape[0] # number of objects
    C = gamma.shape[1] # number of clusters
    d = X.shape[1] # dimension of each object

    mu = np.zeros((C, d))
    sigma = []
    pi = np.zeros(C)

    # for each model in C
    for c in range(C):
        # sum of all Q(t) of model c
        sum_Q_t = np.sum(gamma[:, c])

        # mean of model c
        mu[c, :] = np.sum(np.multiply(X, gamma[:, c]), axis=0) / sum_Q_t

        # cov of model c
        diff = X - mu[c]
        sigma.append(diff.T @ np.multiply(diff, gamma[:, c]) / sum_Q_t)

        # weight of model c
        pi[c] = sum_Q_t / N

    return pi, mu, np.asarray(sigma)

"""# Variational lower bound

Computes the scalar output of the following:

$$\sum_{i=1}^{N} \sum_{c=1}^{C} q(t_i =c) (\log \pi_c + \log(MVN)) - \sum_{i=1}^{N} \sum_{c=1}^{K} q(t_i =c) \log q(t_i =c)$$
"""

def compute_vlb(X, pi, mu, sigma, gamma):
    """
    Each input is numpy array:
    X: (N x d), data points
    gamma: (N x C), distribution q(T)
    pi: (C)
    mu: (C x d)
    sigma: (C x d x d)

    Returns value of variational lower bound
    """
    N = X.shape[0] # number of objects
    C = gamma.shape[1] # number of clusters
    d = X.shape[1] # dimension of each object

    VLB = 0.0
    for c in range(C):
      mu_c = np.expand_dims(mu[c,:], axis=0)
      sigma_c = np.expand_dims(sigma[c,:], axis=0)
      gamma_c = gamma[:,c]

      mvg = np.asarray(_mvg(sigma_c, X, mu_c)) # 1xc
      sum = np.log(pi[c] + epsilon) + np.log(mvg + epsilon) # 1xc, + 1e-30 to prevent log(0)
      prod = np.multiply(gamma_c, sum.T) # transpose sum for element wise multiplication
      prod2 = np.multiply(gamma_c, np.log(gamma_c + epsilon)) # element wise multiplication, + 1e-30 to prevent log(0)
      VLB += (prod - prod2)

    VLB = np.sum(VLB, axis=0) # sum all values for all rows

    return VLB

"""# GMM

Find the best parameters by optimizing with the following criterion:

Stopping threshold: ($|\frac{\mathcal{L}_i-\mathcal{L}_{i-1}}{\mathcal{L}_{i-1}}| \le \text{threshold}$)
"""

def GMM(X, C, d, threshold=epsilon, max_iter=1000, trial=500):
    N = X.shape[0] # number of objects
    d = X.shape[1] # dimension of each object
    best_VLB = None
    best_pi = None
    best_mu = None
    best_sigma = None

    for rs in range(trial):
        try:
            pi, mu, sigma = rand_input(C, d) # Try random initial values
            curr_LVB, prev_LVB = 0.0, 0.0
            iter = 0
            while iter < max_iter:
                #print('iter, rs', iter, rs)

                prev_LVB = curr_LVB

                gamma = E_step(X, pi, mu, sigma)
                pi, mu, sigma = M_step(X, gamma)
                curr_LVB = compute_vlb(X, pi, mu, sigma, gamma)

                #print('prev_LVB', prev_LVB)
                #print('curr_LVB', curr_LVB)

                # LVB is the variation lower bound function. It must NOT be decreasing.
                # We are trying to maximize LVB so that the gap between LVB & GMM is minimized.
                if prev_LVB != 0.0 and curr_LVB < prev_LVB:
                    print('VLB ERROR EXIT!: curr_LVB < prev_LVB')
                    sys.exit(1)

                # If numerical error in LVB, goto next trial.
                if np.isnan(curr_LVB) == True:
                    break

                if prev_LVB != 0.0 and abs((curr_LVB - prev_LVB) / (prev_LVB)) <= threshold:
                    if best_VLB == None or curr_LVB > np.float32(best_VLB):
                        best_VLB = curr_LVB
                        best_pi = pi
                        best_mu = mu
                        best_sigma = sigma
                    break # end while loop, goto for loop

                iter += 1

        except np.linalg.LinAlgError:
            print("Singular matrix not allowed.")
            pass

    return best_VLB, best_pi, best_mu, best_sigma

"""# Train"""

# Train
# If numerical errors occured, run a couple of more times.

best_VLB, best_pi, best_mu, best_sigma = GMM(X, C, d)
print('best_VLB', best_VLB)
print('best_pi', best_pi)
print('best_mu', best_mu)
print('best_sigma', best_sigma)

# Use the best values to do 1 more E-step to get gamma.
gamma = E_step(X, best_pi, best_mu, best_sigma)
labels = np.ravel(gamma.argmax(axis=1))

"""# Scatter plot"""

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

'''
# Generate colors for each class.
# only works for max of 4 classes.
def gen_col(C):
  colors =[]
  for c in range(C):
    colors.append(np.random.randint(0, 255, C) / 255)
  print(colors)
  return colors

colors = gen_col(C)

plt.scatter(X[:, 0], X[:, 1], c=labels, cmap=matplotlib.colors.ListedColormap(colors), s=30)
plt.axis('equal')
plt.show()
'''

# https://stackoverflow.com/questions/12487060/matplotlib-color-according-to-class-labels

N = C # Number of labels

# setup the plot
fig, ax = plt.subplots(1,1, figsize=(15,10))
# define the data
x = X[:, 0]
y = X[:, 1]
tag = labels

# define the colormap
cmap = plt.cm.jet
# extract all colors from the .jet map
cmaplist = [cmap(i) for i in range(cmap.N)]
# create the new map
cmap = cmap.from_list('Custom cmap', cmaplist, cmap.N)

# define the bins and normalize
bounds = np.linspace(0,N,N+1)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

# make the scatter
scat = ax.scatter(x, y, c=tag, s=np.random.randint(100,500,N), cmap=cmap, norm=norm)

# create the colorbar
cb = plt.colorbar(scat, spacing='proportional',ticks=bounds)
cb.set_label('Custom cbar')
ax.set_title('Discrete color mappings')
plt.show()


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

Copyright (c) 2022 Jongrae.K

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

import numpy as np
from numpy import linspace
from scipy.integrate import solve_ivp

init_time = 0 # [s]
final_time = 60.0 # [s]
num_data = 1000
tout = linspace(init_time, final_time, num_data)

q0 = np.array([0,0,0,1])
RelTol_cases = np.array([1e-3, 1e-6, 1e-9])
N_sim = len(RelTol_cases)

def dqdt_attitude_kinematics(time, state):
     quat = state
     w_true = np.array([0.1*np.sin(2*np.pi*0.005*time), #[rad/s]
                        0.05*np.cos(2*np.pi*0.01*time + 0.2), #[rad/s]
                        0.02]) #[rad/s]

     wx=np.array([[0,           -w_true[2],     w_true[1]],
                  [w_true[2],   0,              -w_true[0]],
                  [-w_true[1],  w_true[0],      0]])

     Omega_13 = np.hstack((-wx,np.resize(w_true,(3,1))))
     Omega_4  = np.hstack((-w_true,0))
     Omega = np.vstack((Omega_13, Omega_4))

     dqdt = 0.5*(Omega@quat)

     return dqdt

err_idx = np.zeros((num_data,N_sim))
for idx in range(N_sim):
    sol = solve_ivp(dqdt_attitude_kinematics, (init_time, final_time), q0, t_eval=tout, rtol=RelTol_cases[idx], atol=RelTol_cases[idx]*1e-3)
    qout = sol.y
    err_idx[:,idx]=np.abs(np.sum(qout**2,0)-1)

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(tout,np.log(err_idx[:,0]+1e-100),'b-',
        tout,np.log(err_idx[:,1]+1e-100),'r--',
        tout,np.log(err_idx[:,2]+1e-100),'g-.')

fig.set_figheight(6) # size in inches
fig.set_figwidth(8)  # size in inches

xtick_list = np.array([0,10,20,30,40,50,60])
ax.set_xticks(xtick_list)
ax.set_xticklabels(xtick_list,fontsize=14)

ytick_list = np.array([-35,-30,-25,-20,-15,-10,-5,0])
ax.set_yticks(ytick_list)
ax.set_yticklabels(ytick_list,fontsize=14)

ax.axis((0,60,-35,0))
ax.set_xlabel('time [s]',fontsize=14)
ax.set_ylabel(r'$\log|{\bf q}^T{\bf q}-1|$',fontsize=14)

ax.legend(('RelTol=1e-3','RelTol=1e-6','RelTol=1e-9'),fontsize=14, loc='lower right')


# -*- coding: utf-8 -*-
"""
Created on Sat Jul 13 17:54:48 2019

@author: Marcus Bonifacio

Esse script é a tarefa 8 da aula de Bases Computacionais
"""

print('1) Mostrar a classificação do IMC para as seguintes pessoas:') #enunciado
print('Pessoa a: m = 52Kg e h = 1,58m')
print('Pessoa b: m = 83Kg e h = 1,75m')
print('Pessoa c: m = 70Kg e h = 1,79m')
print('\n')

import numpy as np #importa a biblioteca numpy para o script
import matplotlib.pyplot as plt

#Pessoa a
IMCa1 = 52/(1.58**2) #cálculo para o imc da pessoa a
IMCa = np.round(IMCa1, 1) #arrendondamento do valor do imc para uma casa decimal
if IMCa < 17: #linhas que comparam o valor do imc e definem a categoria que a pessoa se enquadra
    print('Pessoa a : IMC =',IMCa,'; Muito abaixo do peso')
else:
    if IMCa >= 17 and IMCa < 18.5:
        print('Pessoa a : IMC =',IMCa,'; Abaixo do peso')
    else:
        if IMCa >= 18.5 and IMCa < 25:
            print('Pessoa a : IMC =',IMCa,'; Peso normal')
        else:
            if IMCa >= 25 and IMCa < 30:
                print('Pessoa a : IMC =',IMCa,'; Acima do peso')
            else:
                if IMCa >= 30 and IMCa < 35:
                    print('Pessoa a : IMC =',IMCa,'; Obesidade Grau I')
                else:
                    if IMCa >= 35 and IMCa < 40:
                        print('Pessoa a : IMC =',IMCa,'; Obesidade Grau II')
                    else:
                        print('Pessoa a : IMC =',IMCa,'; Obesidade Grau III')

#Pessoa b
IMCb1 = 83/(1.75**2) #cálculo para o imc da pessoa b
IMCb = np.round(IMCb1, 1) #arrendondamento do valor do imc para uma casa decimal
if IMCb < 17: #linhas que comparam o valor do imc e definem a categoria que a pessoa se enquadra
    print('Pessoa b : IMC =',IMCb,'; Muito abaixo do peso')
else:
    if IMCb >= 17 and IMCb < 18.5:
        print('Pessoa b : IMC =',IMCb,'; Abaixo do peso')
    else:
        if IMCb >= 18.5 and IMCb < 25:
            print('Pessoa b : IMC =',IMCb,'; Peso normal')
        else:
            if IMCb >= 25 and IMCb < 30:
                print('Pessoa b : IMC =',IMCb,'; Acima do peso')
            else:
                if IMCb >= 30 and IMCb < 35:
                    print('Pessoa b : IMC =',IMCb,'; Obesidade Grau I')
                else:
                    if IMCb >= 35 and IMCb < 40:
                        print('Pessoa b : IMC =',IMCb,'; Obesidade Grau II')
                    else:
                        print('Pessoa b : IMC =',IMCb,'; Obesidade Grau III')

#Pessoa c
IMCc1 = 70/(1.79**2) #cálculo para o imc da pessoa c
IMCc = np.round(IMCc1, 1) #arrendondamento do valor do imc para uma casa decimal
if IMCc < 17: #linhas que comparam o valor do imc e definem a categoria que a pessoa se enquadra
    print('Pessoa c : IMC =',IMCc,'; Muito abaixo do peso')
else:
    if IMCc >= 17 and IMCc < 18.5:
        print('Pessoa c : IMC =',IMCb,'; Abaixo do peso')
    else:
        if IMCc >= 18.5 and IMCc < 25:
            print('Pessoa c : IMC =',IMCc,'; Peso normal')
        else:
            if IMCc >= 25 and IMCc < 30:
                print('Pessoa c : IMC =',IMCc,'; Acima do peso')
            else:
                if IMCc >= 30 and IMCc < 35:
                    print('Pessoa c : IMC =',IMCc,'; Obesidade Grau I')
                else:
                    if IMCc >= 35 and IMCc < 40:
                        print('Pessoa c : IMC =',IMCc,'; Obesidade Grau II')
                    else:
                        print('Pessoa c : IMC =',IMCc,'; Obesidade Grau III')

print('\n')
print('\n')

print('2) A acidez A(x) de uma solução de hidróxido de magnésio em ácido clorídrico, sob certas condições experimentais, é dada pela equação:'

'A(x)=x³+3x²−54'
' na qual x é a concentração de íons hidrônio.'

' Pede-se:'

' 1) Gere o gráfico de A(x) em função de x para 0 ⩽x⩽8.'

' 2) Determine a concentração x do íon de hidrônio que resulta em solução saturada (i.e., com acidez nula). Acrescente uma instrução que gere um ponto vermelho no gráfico correspondente à saturação nula da solução.')
print('\n')

x1 = np.arange(0, 8.1, 0.1) #cria um vetor com os valores da concentração de hidrônio

Ax = (x1**3) + (3*(x1**2)) - 54 #definicão da acidez da solução em função da concentração

plt.figure()
plt.plot(x1, Ax, color = 'red', marker = 's', markevery = np.arange(len(x1))[Ax==0]) #configurações visuais do gráfico
plt.title('Acidez da solução de Mg(OH)\u2082 em HCl') #título do gráfico
plt.xlabel('Concentração de H\u2083O') #rótulo do eixo x
plt.ylabel('Acidez') #rótulo do eixo y
plt.show()

print('Concentração de H\u2083O quando a acidez é 0 =',x1[Ax==0])

print('\n')
print('\n')

print('3) Faça o gráfico de:'
' f(x)=|x−2|+|2x+1|−x−6'
' Faça o gráfico com x variando de -10 a 10.'

' Mostre no Console o menor valor de x tal que f(x)>0 e x>0.')

x2 = np.arange(-10, 10.1, 0.1) #cria vetor com os valores de x

y2 = (np.abs(x2-2)) + (np.abs((2*x2)+1)) - x2 - 6 #função para o vetor com valores de f(x)

plt.figure() #gráfico da função
plt.plot(x2, y2, color='green')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Gráfico de f(x)=|x−2|+|2x+1|−x−6')
plt.show()

z2 = np.logical_and(x2 > 0, y2 >= 0) #condição para criação do vetor onde f(x) e x > 0

xm2 = x2[z2] #cria o vetor com os elementos em que f(x) e x > 0

xmin = np.min(xm2) #valor mínimo do vetor acima
print('Valor mínimo de x para f(x) e x > 0 =',np.round(xmin,1))

print('\n')
print('\n')

print('3) Faça o gráfico da seguinte função:'

' f(x)=x²–sin(0.784x²)–2'
' Mostre no Console as raízes da função (i.e. os valores de x para os quais f(x)=0).')

x3 = np.round(np.arange(-5, 5.1, 0.001),3) #cria um vetor com valores de x

y3 = (x3**2)-(np.sin(0.784*(x3**2)))-2 #define os valores de f(x)

plt.figure() #gráfico da função
plt.plot(x3, y3, color='blue')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Gráfico de f(x)=x²–sin(0.784x²)–2')
plt.show()

print('Raízes da função: x =',x3[np.logical_and(y3 > -0.002, y3 < 0.002)]) #busca os valores de x quando f(x) = 0
#como não há valor exato 0 no vetor, busca-se o valor mais próximo

"""
Fim do Script
"""


"""
Discrete probability distributions.
"""
from math import ceil

import numpy as np
from scipy import stats

from .distributions import Discrete

eps = np.finfo(float).eps

class Binomial(Discrete):
    R"""
    Binomial distribution.

    The discrete probability distribution of the number of successes
    in a sequence of n independent yes/no experiments, each of which
    yields success with probability p.

    The pmf of this distribution is

    .. math:: f(x \mid n, p) = \binom{n}{x} p^x (1-p)^{n-x}

    .. plot::
        :context: close-figs

        import arviz as az
        from preliz import Binomial
        az.style.use('arviz-white')
        ns = [5, 10, 10]
        ps = [0.5, 0.5, 0.7]
        for n, p in zip(ns, ps):
            Binomial(n, p).plot_pdf()

    ========  ==========================================
    Support   :math:`x \in \{0, 1, \ldots, n\}`
    Mean      :math:`n p`
    Variance  :math:`n p (1 - p)`
    ========  ==========================================

    Parameters
    ----------
    n : int
        Number of Bernoulli trials (n >= 0).
    p : float
        Probability of success in each trial (0 < p < 1).
    """

    def __init__(self, n=None, p=None):
        super().__init__()
        self.n = n
        self.p = p
        self.name = "binomial"
        self.params = (self.n, self.p)
        self.param_names = ("n", "p")
        self.params_support = ((eps, np.inf), (eps, 1 - eps))
        self.dist = stats.binom
        self._update_rv_frozen()

    def _get_frozen(self):
        return self.dist(self.n, self.p)

    def _update(self, n, p):
        self.n = n
        self.p = p
        self.params = (n, p)
        self._update_rv_frozen()

    def fit_moments(self, mean, sigma):
        p = 1 - sigma**2 / mean
        n = int(mean / p)
        self._update(n, p)

    def fit_mle(self, sample):
        # see https://doi.org/10.1016/j.jspi.2004.02.019 for details
        x_bar = np.mean(sample)
        x_std = np.std(sample)
        x_max = np.max(sample)
        n = ceil(x_max ** (1.5) * x_std / (x_bar**0.5 * (x_max - x_bar) ** 0.5))
        p = x_bar / n
        self._update(n, p)

class NegativeBinomial(Discrete):
    R"""
    Negative binomial distribution.

    If it is parametrized in terms of n and p, the negative binomial describes
    the probability to have x failures before the n-th success, given the
    probability p of success in each trial. Its pmf is

    .. math::

        f(x \mid n, p) =
           \binom{x + n - 1}{x}
           (p)^n (1 - p)^x

    .. plot::
        :context: close-figs

        import arviz as az
        from preliz import NegativeBinomial
        az.style.use('arviz-white')
        ns = [5, 10, 10]
        ps = [0.5, 0.5, 0.7]
        for n, p in zip(ns, ps):
            NegativeBinomial(n, p).plot_pdf()

    ========  ==========================
    Support   :math:`x \in \mathbb{N}_0`
    Mean      :math:`\frac{(1-p) r}{p}`
    Variance  :math:`\frac{(1-p) r}{p^2}`
    ========  ==========================

    Parameters
    ----------
    n: float
        Number of target success trials (n > 0)
    p: float
        Probability of success in each trial (0 < p < 1).
    """

    def __init__(self, n=None, p=None):
        super().__init__()
        self.n = n
        self.p = p
        self.name = "negativebinomial"
        self.params = (self.n, self.p)
        self.param_names = ("n", "p")
        self.params_support = ((eps, np.inf), (eps, 1 - eps))
        self.dist = stats.nbinom
        self._update_rv_frozen()

    def _get_frozen(self):
        return self.dist(self.n, self.p)

    def _update(self, n, p):
        self.n = n
        self.p = p
        self.params = (self.n, p)
        self._update_rv_frozen()

    def fit_moments(self, mean, sigma):
        n = mean**2 / (sigma**2 - mean)
        p = mean / sigma**2
        self._update(n, p)

    def fit_mle(self, sample):
        raise NotImplementedError

class Poisson(Discrete):
    R"""
    Poisson log-likelihood.

    Often used to model the number of events occurring in a fixed period
    of time when the times at which events occur are independent.
    The pmf of this distribution is

    .. math:: f(x \mid \mu) = \frac{e^{-\mu}\mu^x}{x!}

    .. plot::
        :context: close-figs

        import arviz as az
        from preliz import Poisson
        az.style.use('arviz-white')
        for mu in [0.5, 3, 8]:
            Poisson(mu).plot_pdf()

    ========  ==========================
    Support   :math:`x \in \mathbb{N}_0`
    Mean      :math:`\mu`
    Variance  :math:`\mu`
    ========  ==========================

    Parameters
    ----------
    mu: float
        Expected number of occurrences during the given interval
        (mu >= 0).

    Notes
    -----
    The Poisson distribution can be derived as a limiting case of the
    binomial distribution.
    """

    def __init__(self, mu=None):
        super().__init__()
        self.mu = mu
        self.name = "poisson"
        self.params = (self.mu,)
        self.param_names = ("mu",)
        self.params_support = ((eps, np.inf),)
        self.dist = stats.poisson
        self._update_rv_frozen()

    def _get_frozen(self):
        return self.dist(self.mu)

    def _update(self, mu):
        self.mu = mu
        self.params = (self.mu,)
        self._update_rv_frozen()

    def fit_moments(self, mean, sigma=None):  # pylint: disable=unused-argument
        self._update(mean)

    def fit_mle(self, sample):
        mu = np.mean(sample)
        self._update(mu)


"""W-state."""
from typing import List

import numpy as np

from scipy import sparse

def w_state(num_qubits: int, coeff: List[int] = None) -> np.ndarray:
    r"""
    Produce a W-state [DVC00]_.

    Returns the W-state described in [DVC00]. The W-state on `num_qubits` qubits is defined by:

    .. math::
        |W \rangle = \frac{1}{\sqrt{num\_qubits}}
        \left(|100 \ldots 0 \rangle + |010 \ldots 0 \rangle + \ldots +
        |000 \ldots 1 \rangle \right).

    Examples
    ==========

    Using :code:`toqito`, we can generate the :math:`3`-qubit W-state

    .. math::
        |W_3 \rangle = \frac{1}{\sqrt{3}} \left( |100\rangle + |010 \rangle +
        |001 \rangle \right)

    as follows.

    >>> from toqito.states import w_state
    >>> w_state(3)
    [[0.    ],
     [0.5774],
     [0.5774],
     [0.    ],
     [0.5774],
     [0.    ],
     [0.    ],
     [0.    ]]

    We may also generate a generalized :math:`W`-state. For instance, here is a
    :math:`4`-dimensional :math:`W`-state

    .. math::
        \frac{1}{\sqrt{30}} \left( |1000 \rangle + 2|0100 \rangle + 3|0010
        \rangle + 4 |0001 \rangle \right).

    We can generate this state in :code:`toqito` as

    >>> from toqito.states import w_state
    >>> import numpy as np
    >>> coeffs = np.array([1, 2, 3, 4]) / np.sqrt(30)
    >>> w_state(4, coeffs)
    [[0.    ],
     [0.7303],
     [0.5477],
     [0.    ],
     [0.3651],
     [0.    ],
     [0.    ],
     [0.    ],
     [0.1826],
     [0.    ],
     [0.    ],
     [0.    ],
     [0.    ],
     [0.    ],
     [0.    ],
     [0.    ]]

    References
    ==========
    .. [DVC00] Three qubits can be entangled in two inequivalent ways.
        W. Dur, G. Vidal, and J. I. Cirac.
        E-print: arXiv:quant-ph/0005115, 2000.

    :param num_qubits: An integer representing the number of qubits.
    :param coeff: default is `[1, 1, ..., 1]/sqrt(num_qubits)`: a
                  1-by-`num_qubts` vector of coefficients.
    """
    if coeff is None:
        coeff = np.ones(num_qubits) / np.sqrt(num_qubits)

    if num_qubits < 2:
        raise ValueError("InvalidNumQubits: `num_qubits` must be at least 2.")
    if len(coeff) != num_qubits:
        raise ValueError(
            "InvalidCoeff: The variable `coeff` must be a vector "
            "of length equal to `num_qubits`."
        )

    ret_w_state = sparse.csr_matrix((2 ** num_qubits, 1)).toarray()

    for i in range(num_qubits):
        ret_w_state[2 ** i] = coeff[num_qubits - i - 1]

    return np.around(ret_w_state, 4)


import numpy as np
from scipy import linalg as LA

class LDA(object):
	def __init__(self, data_inputs, data_labels):
		self.data_inputs = np.array(data_inputs)
		self.data_labels = data_labels
		self.test_cases = self.data_inputs.shape[0]
		self.labels = np.unique(data_labels)
		self.Sw = np.zeros((self.data_inputs.shape[1], self.data_inputs.shape[1]))
		self.Sb = self.Sw.copy()

	def analyse(self):
		C = np.cov(self.data_inputs.T)

		for label in self.labels:
			indices = np.where(self.data_labels == label)
			points = self.data_inputs[indices[0]]
			classcov = np.cov(points.T)
			self.Sw += (np.float(points.shape[0])/self.test_cases) * classcov

		self.Sb = C - self.Sw
		evals, evecs = LA.eig(self.Sw, self.Sb)
		indices = np.argsort(evals)
		indices = indices[::-1]
		evals = evals[indices]
		evecs = evecs[indices]
		self.eigen_vals = evals
		self.eigen_vecs = evecs

	def reduce_dim(self, red_n, data_inputs=None):
		w = self.eigen_vecs[:,:red_n]
		if data_inputs is None:
			data_inputs = self.data_inputs
		return np.dot(data_inputs, w)

	def expand_dim(self, red_data):
		red_n = red_data.shape[1]
		return np.transpose(np.dot(self.eigen_vecs[:,:red_n], red_data.T))


from sympy import *
from sympy_recursive import Recursive

# create an index symbol
n = Symbol('n')

# create the sequence
fib = Recursive('fib')
fib.index = n

# input the first items
fib[0] = 1
fib[1] = 1

# provide a recursive formula
fib[n] = fib[n-1] + fib[n-2]

# resolve it
explicit = fib.resolve()

# test
for i in range(10):
    value = explicit.subs(n, i).simplify()
    print "fib[{}] = {}".format(i, value)


import numpy as np

# Random Number Generator - Uniform Distribution
def U():
        return np.random.rand()

# Integrated Function
def f(x):
    return np.exp(x)

if __name__ == "__main__":
    # Number of Random Numbers
    N = 1000
    # sample_mean(f)
    μf = 0
    # sample_mean(f^2)
    μf2 = 0
    # Inegral computated with Crude-Monte-Carlo Method
    I_sample = 0

    # Limits of Integration
    a, b = 0, 10
    # "Volume" of Integration
    V = (b-a)

    for i in range(N):
        xi = V * U() + a

        μf += f(xi)/N
        μf2 += f(xi)**2/N

    I_sample = V * μf

    # Variance of f(x)
    σf_sample = np.sqrt((N/(N-1)) * (μf2 - μf**2))

    # Integral Error
    δI_sample = V/np.sqrt(N)*σf_sample

    # Integral Relative Error
    η_sample = δI_sample/I_sample

    print(f'I ± δI = {I_sample:.2f} ± {δI_sample:.2f},\nη_sample = {η_sample:.3f}')


import numpy as np

def deriv_fitting_matrix(degree, t_end=1.0):
  """
    Returns A s.t. that the vector x that satisfies

    Ax = b

    contains polynomial coefficients

      p(t) = x_1 + x_2 t + x_3 t^2 ...

    so that

    p(0) = b_1
    p^(i)(0) = b_{i+1}
    p(t_end) = b_{degree / 2}
    p^(i)(t_end) = b_{degree / 2 + i + 1}

    i.e. the first degree / 2 derivatives of p at 0 match the first degree / 2
         entries of b and the first degree / 2 derivatives of p at 1, match the last
         degree / 2 entries of b
  """

  assert degree % 2 == 0

  A = np.zeros((degree, degree))

  ts = t_end ** np.array(range(degree))

  constant_term = 1
  poly = np.ones(degree)
  for i in range(degree // 2):
    A[i, i] = constant_term
    A[degree // 2 + i, :] = np.hstack((np.zeros(i), poly * ts[:degree - i]))
    poly = np.polynomial.polynomial.polyder(poly)
    constant_term *= (i + 1)

  return A

if __name__ == "__main__":
  for i in range(1, 5):
    mat = deriv_fitting_matrix(2 * i)
    print(mat)
    print(np.linalg.inv(mat))


import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as st
import seaborn as sns

sns.set()

def p(x):
    return st.norm.pdf(x, loc=30, scale=10) + st.norm.pdf(x, loc=80, scale=20)

def q(x):
    return st.norm.pdf(x, loc=50, scale=30)

x = np.arange(-50, 151)
k = max(p(x) / q(x))

def rejection_sampling(iter=1000):
    samples = []

    for i in range(iter):
        z = np.random.normal(50, 30)
        u = np.random.uniform(0, k * q(z))

        if u <= p(z):
            samples.append(z)

    return np.array(samples)

if __name__ == "__main__":
    plt.plot(x, p(x))
    plt.plot(x, k * q(x))
    plt.show()

    s = rejection_sampling(iter=100000)
    sns.distplot(s)


import numpy as maths

def lin_reg(*series):
	'''Fits the data (x, y) to the equation y = ax + b and returns (a, b)'''
	if(len(series)) == 1:
		y = series[0]
		x = [index for index in range(len(y))]
	else:
		x, y = series

	x_ = maths.sum(x) / len(x)
	y_ = maths.sum(y) / len(y)
	xy_ = maths.dot(x - x_, y - y_)
	xx_ = maths.dot(x - x_, x - x_)

	a = xy_ / xx_
	b = y_ - (a * x_)
	return(a, b)

def exp_reg(y):
	'''Fits the data (y) to the equation y = b.(a^x) and returns (a, b)'''
	y = [y_ for y_ in y if y_ > 0]			#Clean non-zero values.
	if(len(y) == 0):		#If all entries in y are 0, then the required curve is the x-axis.
		return(0, 0)

	if(len(y) == 1):		#If there's only one entry in y, then the required curve is a line parallel to the x-axis: y = y[0]
		return(y[0], y[0])

	else:
		a, b = maths.exp(lin_reg(maths.log(y)))		#Fit the series to a linear model using a logarithmic scale.
		#y = b.(a^x)		=>		log(y) = x.log(a) + log(b)
		return(a, b)

def exp_predict(x, a, b):
	'''Returns a prediction for an exponenial series characterised by (a, b).'''
	return((a ** x) * b)


import numpy as np
from tqdm.auto import tqdm
from scipy.sparse import coo_matrix
from ortools.linear_solver import pywraplp

__all__ = ["linprog"]

def linprog(f, A, b):
    """
    Solve the following linear programming problem
            maximize_x (f.T).dot(x)
            subject to A.dot(x) <= b
    where   A is a sparse matrix (coo_matrix)
            f is column vector of cost function associated with variable
            b is column vector
    """

    # flatten the variable
    f = f.ravel()
    b = b.ravel()

    solver = pywraplp.Solver('SolveReviewerAssignment',
                             pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)

    infinity = solver.Infinity()
    n, m = A.shape
    x = [[]] * m
    c = [0] * n

    print("Setting up variables...")
    for j in tqdm(range(m)):
        x[j] = solver.NumVar(-infinity, infinity, 'x_%u' % j)

    # state objective function
    print("Setting up objective function...")
    objective = solver.Objective()
    for j in tqdm(range(m)):
        objective.SetCoefficient(x[j], f[j])
    objective.SetMaximization()

    # state the constraints
    print("Setting up constraints...")
    for i in tqdm(range(n)):
        c[i] = solver.Constraint(-infinity, int(b[i]))
        for j in A.col[A.row == i]:
            c[i].SetCoefficient(x[j], A.data[np.logical_and(A.row == i, A.col == j)][0])

    result_status = solver.Solve()
    if result_status != 0:
        print("The final solution might not converged")

    x_sol = np.array([x_tmp.SolutionValue() for x_tmp in x])

    return {'x': x_sol, 'status': result_status}

def test_example():
    """
    Solves example problem from http://www.vitutor.com/alg/linear_programming/example_programming.html
        f(x,y)= 50x + 40y
    subject to:
        2x+3y <= 1500
        2x + y <= 1000
        x >= 0 -> -x <= 0
        y >= 0 -> -y <= 0
    """
    f = np.array([50, 40], dtype=np.float)
    A = np.array([[ 2, 3],
                  [ 2, 1],
                  [-1, 0],
                  [ 0, -1]], dtype=np.float)
    C = np.array([1500, 1000, 0, 0])
    x_sol = linprog(f, coo_matrix(A), C)
    print('Example Problem:')
    print('maximize_x\t 50x + 40y')
    print('s.t.\t\t 2x+3y <= 1500, 2x + y <= 1000, x >= 0, y >= 0')
    print('Solution: (x, y) = ', x_sol)

if __name__ == '__main__':
    test_example()


"""
Convergence comparison of different adaptive filtering algorithms (with
different step sizes) in white Gaussian noise.
"""

import numpy as np
import matplotlib.pyplot as plt
import adaptfilt as adf

# Generating inpud and desired signal
N = 3000
coeffs = np.concatenate(([-4, 3.2], np.zeros(20), [0.7], np.zeros(33), [-0.1]))
u = np.random.randn(N)
d = np.convolve(u, coeffs)

# Perform filtering
M = 60  # No. of taps to estimate
mu1 = 0.0008  # Step size 1 in LMS
mu2 = 0.0004  # Step size 1 in LMS
beta1 = 0.08  # Step size 2 in NLMS and AP
beta2 = 0.04  # Step size 2 in NLMS and AP
K = 3  # Projection order 1 in AP

# LMS
y_lms1, e_lms1, w_lms1 = adf.lms(u, d, M, mu1, returnCoeffs=True)
y_lms2, e_lms2, w_lms2 = adf.lms(u, d, M, mu2, returnCoeffs=True)
mswe_lms1 = adf.mswe(w_lms1, coeffs)
mswe_lms2 = adf.mswe(w_lms2, coeffs)

# NLMS
y_nlms1, e_nlms1, w_nlms1 = adf.nlms(u, d, M, beta1, returnCoeffs=True)
y_nlms2, e_nlms2, w_nlms2 = adf.nlms(u, d, M, beta2, returnCoeffs=True)
mswe_nlms1 = adf.mswe(w_nlms1, coeffs)
mswe_nlms2 = adf.mswe(w_nlms2, coeffs)

# AP
y_ap1, e_ap1, w_ap1 = adf.ap(u, d, M, beta1, K, returnCoeffs=True)
y_ap2, e_ap2, w_ap2 = adf.ap(u, d, M, beta2, K, returnCoeffs=True)
mswe_ap1 = adf.mswe(w_ap1, coeffs)
mswe_ap2 = adf.mswe(w_ap2, coeffs)

# Plot results
plt.figure()
plt.title('Convergence comparison of different adaptive filtering algorithms')
plt.plot(mswe_lms1, 'b', label='LMS with stepsize=%.4f' % mu1)
plt.plot(mswe_lms2, 'b--', label='LMS with stepsize=%.4f' % mu2)
plt.plot(mswe_nlms1, 'g', label='NLMS with stepsize=%.2f' % beta1)
plt.plot(mswe_nlms2, 'g--', label='NLMS with stepsize=%.2f' % beta2)
plt.plot(mswe_ap1, 'r', label='AP with stepsize=%.2f' % beta1)
plt.plot(mswe_ap2, 'r--', label='AP with stepsize=%.2f' % beta2)
plt.legend()
plt.grid()
plt.xlabel('Iterations')
plt.ylabel('Mean-squared weight error')
plt.show()


import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns
import matplotlib.ticker as ticker

# based on
# https://www.wired.com/story/how-fast-does-a-virus-spread/

#########

def InfectionCurve(t, Ninit, loc, scale):

    N = Ninit
    ic = []
    t_old = t[0]

    for it in t:

      t_new = it
      dt = t_new - t_old

      r = np.random.normal(loc=loc, scale=scale, size=None)
      N = N + r * N * dt

      ic.append(N)

      t_old = t_new

      print([r, t_new, dt, N])

    return(ic)

def plt_IC(t, ic, fplot):

    fig, ax = plt.subplots(figsize=(10, 10))

    sns.lineplot(x=t, y=ic, sort=False, linewidth=2)
    sns.scatterplot(t, ic)

    plt.suptitle("Infection curve", fontsize=16, fontweight='bold', color='white')
    plt.xticks(rotation=0)
    plt.xlabel('Time [days]')
    plt.ylabel('Number infected')

    fig.savefig(fplot)
    plt.close()

def plt_IC_n(t, ics, fplot):

    fig, ax = plt.subplots(figsize=(10, 10))

    ax.set(yscale="log")
    ax.yaxis.set_major_formatter(\
            ticker.FuncFormatter(lambda y, _: '{:g}'.format(y)))

    for ic in ics:
        sns.lineplot(x=t, y=ic, sort=False, linewidth=2)
        sns.scatterplot(t, ic)

    plt.suptitle("Infection curve", fontsize=16, fontweight='bold', color='white')
    plt.xticks(rotation=0)
    plt.xlabel('Time [days]')
    plt.ylabel('Number infected')

    fig.savefig(fplot)
    plt.close()

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

fplot = '../plt/InfectionCurve.png'

# EXPERIMENTO 1
# Como afecta el error en el cálculo de R en la proyección de casos

start = 0.
stop = 40.
num = 41
dt = (stop-start)/num
t = np.linspace(start=start, stop=stop, num=num)

ics = []
for i in range(20):
    ic = InfectionCurve(t, 1., 0.2, 0.05)
    ics.append(ic)

fplot = '../plt/InfectionCurve_sd.png'
plt_IC_n(t, ics, fplot)

# EXPERIMENTO 2
# Como afecta R en el avance de los contagios

R = np.linspace(start=0.15, stop=0.3, num=10)
ics = []
for r in R:
    ic = InfectionCurve(t, 1., r, 0.0)
    ics.append(ic)

fplot = '../plt/InfectionCurve_R.png'
plt_IC_n(t, ics, fplot)

# keep track of:
#
# - numero real de contagiados
# - numero de casos confirmados
# - numero de casos recuerados
# - numero de fallecimientos
# - numero de pacientes leves (en la casa)
# - numero de pacientes moderados (internados, no UTI)
# - numero depacientes graves (UTI)

# infected
# confirmed
# recovered
#
# inf_dead
# inf_uti
# inf_bed
# inf_home
#

def InfectionCurve_full(t, Ninit, loc, scale):

    N = Ninit
    ic = []
    t_old = t[0]

    for it in t:

      t_new = it
      dt = t_new - t_old

      r = np.random.normal(loc=loc, scale=scale, size=None)
      N = N + r * N * dt

      ic.append(N)

      t_old = t_new

      print([r, t_new, dt, N])

    return(ic)


import numpy as np

def reshape_features(X):
    return X.reshape(X.shape[0],-1).T

def initialize_with_zeros(dim):
    w = np.zeros((dim,1))
    b = 0
    return w,b

def compute_loss(Yhat, Y):
    return -(Y * np.log(Yhat) +( (1-Y) * np.log((1-Yhat))))

def compute_cost(Yhat, Y, m):
    loss = compute_loss(Yhat,Y)
    return np.sum(loss) / m

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

def propagate(w, b, X,Y):
    # 1. Forward Propagation
    m = X.shape[1]
    A = sigmoid(w.T @ X)
    cost = compute_cost(A,Y,m)

    # 2. Backward Propagation
    dw = (X @ (A-Y).T)/m
    db = np.sum(A-Y)/m
    cost = np.squeeze(cost)
    grads = {
        'dw' : dw,
        'db' : db
    }

    return grads, cost

def optimize(w, b, X, Y, n_iterations, learning_rate, print_cost=False):
    """
    This function optimizes w and b by running a gradient descent algorithm

    Arguments:
    w -- weights, a numpy array of size (num_px * num_px * 3, 1)
    b -- bias, a scalar
    X -- data of shape (num_px * num_px * 3, number of examples)
    Y -- true "label" vector (containing 0 if non-cat, 1 if cat), of shape (1, number of examples)
    n_iterations -- number of iterations of the optimization loop
    learning_rate -- learning rate of the gradient descent update rule
    print_cost -- True to print the loss every 100 steps

    Returns:
    params -- dictionary containing the weights w and bias b
    grads -- dictionary containing the gradients of the weights and bias with respect to the cost function
    costs -- list of all the costs computed during the optimization, this will be used to plot the learning curve.

    Tips:
    You basically need to write down two steps and iterate through them:
        1) Calculate the cost and the gradient for the current parameters. Use propagate().
        2) Update the parameters using gradient descent rule for w and b.
    """
    costs = []

    for i in range(n_iterations):
        grads, cost = propagate(w,b,X,Y)
        dw = grads['dw']
        db =  grads['db']
        w = w - learning_rate * dw
        b = b - learning_rate * db

        if i % 100 == 0:
            costs.append(cost)

        if print_cost and i % 100 == 0:
            print (f'Cost after iteration {i}: {cost}')

    params = {
        'w' : w,
        'b': b
    }
    grads = {
        'dw': dw,
        'db': db
    }

    return params, grads, costs

def predict(w, b, X):
    '''
    Predict whether the label is 0 or 1 using learned logistic regression parameters (w, b)

    Arguments:
    w -- weights, a numpy array of size (num_px * num_px * 3, 1)
    b -- bias, a scalar
    X -- data of size (num_px * num_px * 3, number of examples)

    Returns:
    Y_prediction -- a numpy array (vector) containing all predictions (0/1) for the examples in X
    '''
    m = X.shape[1]
    Y_prediction = np.zeros((1,m))
    w = w.reshape(X.shape[0], 1)

    # Compute vector "A" predicting the probabilities of a cat being present in the picture
    A = sigmoid(np.dot(w.T,X)+b)

    for i in range(A.shape[1]):
        # Convert probabilities A[0,i] to actual predictions p[0,i]
        Y_prediction[0, i] = 1 if A[0, i] > 0.5 else 0
    assert(Y_prediction.shape == (1, m))
    return Y_prediction

def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
    # initialize parameters with zeros (≈ 1 line of code)
    w,b = initialize_with_zeros(X_train.shape[0])

     # Gradient descent
    parameters, grads, costs = optimize(w,b,X_train, Y_train, num_iterations,learning_rate,print_cost)

    # Retrieve parameters w and b from dictionary "parameters"
    w = parameters["w"]
    b = parameters["b"]

    # Predict test/train set examples (≈ 2 lines of code)
    Y_prediction_test = predict(w, b, X_test)
    Y_prediction_train = predict(w, b, X_train)

    # Print train/test Errors
    train_acc = 100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100
    test_acc = 100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100
    # print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
    # print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))

    d = {"costs": costs,
         "Y_prediction_test": Y_prediction_test,
         "Y_prediction_train" : Y_prediction_train,
         "w" : w,
         "b" : b,
         "learning_rate" : learning_rate,
         "num_iterations": num_iterations,
         "train_acc": train_acc,
         "test_acc": test_acc
         }

    return d


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

def plot_damped_oscillator(m=1.0, k=1.0, b=1.0):
    """
    m = 1.0 # mass [kg]
    k = 1.0 # spring constant [N/m]
    b = 1.0 # friction constant [Ns/m = kgm/s^2*s/m = kg/s]
    """

    two_gamma = b/m
    omega_squared = k/m

    gamma = two_gamma/2
    omega = np.sqrt(omega_squared)

    if gamma == 0.0:
        osc_type = "non damped oscillator"
    elif gamma < omega:
        osc_type = "underdamped"
    elif gamma > omega:
        osc_type = "overdamped"
    else:
        osc_type = "Critically damped"

    def rhs(y, t):
        x, v = y[0], y[1]
        a = -two_gamma*v - omega_squared*x
        y_prime = np.array([v, a])
        return y_prime

    x0 = 10.0 # [m]
    v0 = 0.0 # [m/s]
    y0 = np.array([x0, v0])

    T = 20.0 # [s]
    steps = 1000
    ts = np.linspace(0, T, steps)

    ys = odeint(rhs, y0, ts)
    xs, vs = ys[:,0], ys[:,1]

    plt.subplot(2,1,1)
    plt.title(r"%s $\gamma = %f $, $\omega = %f $" % \
            (osc_type, gamma, omega))
    plt.plot(ts, xs, color="black")
    plt.xlabel("t [s]")
    plt.ylabel("x [m]")
    plt.subplot(2,1,2)
    plt.plot(ts, vs, color="black")
    plt.xlabel("t [s]")
    plt.ylabel("v [m/s]")
    plt.show()

#plot_damped_oscillator()
#plot_damped_oscillator(b=10.0)
# Critically damped
# k = b^2/(4m)
plot_damped_oscillator(k=1/4.)


# coding=utf-8
# Copyright 2022 The Google Research Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Library of computing associated Legendre function of the first kind."""

import math

from jax import lax
import jax.numpy as jnp
import numpy as np

def gen_normalized_legendre(l_max,
                            x):
  r"""Computes the normalized associated Legendre functions (ALFs).

  The ALFs of the first kind are used in spherical harmonics. The spherical
  harmonic of degree `l` and order `m` can be written as
  `Y_l^m(θ, φ) = N_l^m * P_l^m(cos(θ)) * exp(i m φ)`, where `N_l^m` is the
  normalization factor and θ and φ are the colatitude and longitude,
  repectively. `N_l^m` is chosen in the way that the spherical harmonics form
  a set of orthonormal basis function of L^2(S^2). For the computational
  efficiency of spherical harmonics transform, the normalization factor is
  embedded into the computation of the ALFs. In addition, normalizing `P_l^m`
  avoids overflow/underflow and achieves better numerical stability. Three
  recurrence relations are used in the computation. Note that the factor of
  \sqrt(1 / (4 𝛑)) is used in the formulation.

  Args:
    l_max: The maximum degree of the associated Legendre function. Both the
      degrees and orders are `[0, 1, 2, ..., l_max]`.
    x: A vector of type `float32`, `float64` containing the sampled points in
      spherical coordinates, at which the ALFs are computed; `x` is essentially
      `cos(θ)`.

  Returns:
    The 3D array of shape `(l_max + 1, l_max + 1, len(x))` containing the
    normalized values of the ALFs at `x`.
  """
  dtype = lax.dtype(x)
  if dtype not in (jnp.float32, jnp.float64):
    raise TypeError(
        'x.dtype={} is not supported, see docstring for supported types.'
        .format(dtype))

  if x.ndim != 1:
    raise ValueError('x must be a 1D array.')

  p = np.zeros((l_max + 1, l_max + 1, x.shape[0]))

  # The initial value p(0,0).
  initial_value = 0.5 / np.sqrt(math.pi)
  p[0, 0] = initial_value

  # Compute the diagonal entries p(l,l) with recurrence.
  y = np.sqrt(1.0 - x * x)
  for l in range(1, l_max + 1):
    a = -1.0 * np.sqrt(1.0 + 0.5 / l)
    p[l, l] = a * y * p[l - 1, l - 1]

  # Compute the off-diagonal entries with recurrence.
  for l in range(l_max):
    b = np.sqrt(2.0 * l + 3.0)
    p[l + 1, l] = b * x * p[l, l]

  # Compute the remaining entries with recurrence.
  for m in range(l_max + 1):
    for l in range(m + 2, l_max + 1):
      c0 = l * l
      c1 = m * m
      c2 = 2.0 * l
      c3 = (l - 1.0) * (l - 1.0)
      d0 = np.sqrt((4.0 * c0 - 1.0) / (c0 - c1))
      d1 = np.sqrt(((c2 + 1.0) * (c3 - c1)) / ((c2 - 3.0) * (c0 - c1)))
      p[l, m] = d0 * x * p[l - 1, m] - d1 * p[l - 2, m]

  return jnp.asarray(p)


from math import cos
from numpy.lib.shape_base import tile
import torch
import torch.nn as nn
import numpy as np
import plotly.graph_objects as go

def plot_ackley(axis_npoints = 1000):
    coords = torch.linspace(-40, 40, axis_npoints)
    grid = torch.cartesian_prod(coords, coords)
    cost = Ackley()
    z = cost(grid)
    fig = go.Figure(data=[go.Surface(z=z.reshape(axis_npoints, axis_npoints), x=coords.numpy(), y=coords.numpy())])
    fig.update_layout(title="Ackley function a=20, b=0.2, c=2pi", autosize=False,
                    width=1000, height=1000,
                    margin=dict(l=65, r=50, b=65, t=90))
    fig.show()

def plot_rastrigin(axis_npoints = 1000):
    coords = torch.linspace(-5.12, 5.12, axis_npoints)
    grid = torch.cartesian_prod(coords, coords)
    cost = Rastrigin()
    z = cost(grid)
    fig = go.Figure(data=[go.Surface(z=z.reshape(axis_npoints, axis_npoints), x=coords.numpy(), y=coords.numpy())])
    fig.update_layout(title="Rastrigin function A=10", autosize=False,
                    width=1000, height=1000,
                    margin=dict(l=65, r=50, b=65, t=90))
    fig.show()

def plot_griewank(axis_npoints = 1000):
    coords = torch.linspace(-5, 5, axis_npoints)
    grid = torch.cartesian_prod(coords, coords)
    cost = Griewank()
    z = cost(grid)
    fig = go.Figure(data=[go.Surface(z=z.reshape(axis_npoints, axis_npoints), x=coords.numpy(), y=coords.numpy())])
    fig.update_layout(title="Griewank function", autosize=False,
                    width=1000, height=1000,
                    margin=dict(l=65, r=50, b=65, t=90))
    fig.show()

def plot_zakharov(axis_npoints = 1000):
    coords = torch.linspace(-5, 10, axis_npoints)
    grid = torch.cartesian_prod(coords, coords)
    cost = Zakharov()
    z = cost(grid)
    fig = go.Figure(data=[go.Surface(z=z.reshape(axis_npoints, axis_npoints), x=coords.numpy(), y=coords.numpy())])
    fig.update_layout(title="Zakharov function", autosize=False,
                    width=1000, height=1000,
                    margin=dict(l=65, r=50, b=65, t=90))
    fig.show()

class Ackley:
    def __init__(self, a=20, b=0.2, c=2*np.pi):
        self.a = a
        self.b = b
        self.c = c

    def __call__(self, x):
        _, d = x.shape
        f = -self.a * torch.exp(-self.b * torch.linalg.norm(x, ord=2, dim=1) / np.sqrt(d)) - torch.exp(torch.sum(torch.cos(self.c * x), dim=1) / d) + self.a + np.exp(1.0)
        return f

class Rastrigin:
    def __init__(self, A=10):
        self.A = A

    def __call__(self, x):
        _, d = x.shape
        return self.A * d + torch.sum(torch.square(x) - 10 * torch.cos(2 * np.pi * x), dim=1)

class Griewank:
    def __call__(self, x):
        n, d = x.shape
        denom = torch.repeat_interleave(torch.arange(1, d+1).view(1, -1), repeats=n, dim=0)
        denom = torch.sqrt(denom.float())
        return torch.sum(x / 4000, dim=1) - torch.prod(torch.cos(x / denom), dim=1) + 1

class Zakharov:
    def __call__(self, x):
        n, d = x.shape
        multiplier = torch.repeat_interleave(torch.arange(1, d+1).view(1, -1), repeats=n, dim=0)
        square = torch.square(torch.sum(0.5 * multiplier * x, dim=1))
        return torch.sum(torch.square(x), dim=1) + square + torch.square(square)


import gym
import numpy as np
import math as math

# Init environment
env = gym.make("FrozenLake-v0")
# you can set it to deterministic with:
# env = gym.make("FrozenLake-v0", is_slippery=False)

# If you want to try larger maps you can do this using:
#random_map = gym.envs.toy_text.frozen_lake.generate_random_map(size=5, p=0.8)
#env = gym.make("FrozenLake-v0", desc=random_map)

# Init some useful variables:
n_states = env.observation_space.n
n_actions = env.action_space.n

def value_iteration():
    V_states = np.zeros(n_states)  # init values as zero
    theta = 1e-8
    gamma = 0.8
    # TODO: implement the value iteration algorithm and return the policy
    # Hint: env.P[state][action] gives you tuples (p, n_state, r, is_terminal), which tell you the probability p that you end up in the next state n_state and receive reward r
    nsteps = 0
    while True:
        delta = 0
        for s in range(n_states):
            v = V_states[s]
            V_states[s] = max([math.fsum([p * (r + gamma * V_states[n_state]) for p, n_state, r, is_terminal in env.P[s][a]]) for a in range(n_actions)])
            delta = max(delta, abs(v - V_states[s]))
        nsteps += 1
        if delta < theta:
            break
    print("Number of steps: {}".format(nsteps))
    return V_states

def make_policy(value):
    policy = np.zeros(n_states)  # init values as zero
    theta = 1e-8
    gamma = 0.8

    for s in range(n_states):
        policy[s] = np.argmax([math.fsum([p * (r + gamma * value[n_state]) for p, n_state, r, is_terminal in env.P[s][a]]) for a in range(n_actions)])

    return policy

def main():
    # print the environment
    print("current environment: ")
    env.render()
    print("")

    # run the value iteration
    value = value_iteration()
    print("Computed value:")
    print(value)
    policy = make_policy(value)
    print("Computed policy:")
    print(policy)

    # This code can be used to "rollout" a policy in the environment:
    """print ("rollout policy:")
    maxiter = 100
    state = env.reset()
    for i in range(maxiter):
        new_state, reward, done, info = env.step(policy[state])
        env.render()
        state=new_state
        if done:
            print ("Finished episode")
            break"""

if __name__ == "__main__":
    main()


"""Functions for working with lines in spectrum"""

import numpy as np
from scipy.optimize import curve_fit

def gauss_function(x, a, x0, sigma):
    """
    Gaussian
    _________
    Arguments:

    x (float or float array) - argument
    a (float) - amplitude
    x0 (float) - mathematical expectation
    sigma (float) - standard deviation
    __________________________________
    Returns:

    Gauss function by formula
    a * np.exp(-(x - x0)**2 / (2 * sigma**2))
    """

    return a * np.exp(-(x - x0) ** 2 / (2 * sigma ** 2))

def calc_cont(x, y, wln, deg):
    """
    Trying to find continuum in current spectrum
    ____________________________________________
    Arguments:

    points (float x float array) - selected points
    deg - polynomial degree
    _____________________
    Returns:

    Continuum curve
    """

    cont_coef = np.polyfit(x, y, deg)
    cont = np.poly1d(cont_coef)(wln)

    return cont

def calc_masc(wln_sel, flx_sel):
    """
    Function for calculation of mass center of selected area
    ________________________________________________________
    Arguments:

    wln_sel (float array) - wavelength in selected area
    flx_sel (float array) - flux in selected area
    _____________________________________________
    Returns:

    Mass center of selected area
    """

    mx = np.sum(wln_sel * flx_sel)
    m = np.sum(flx_sel)
    mc = mx / m

    return mc

def calc_int(wln_sel, flx_sel, cnt_sel):
    """
    Function for calculation area between spectrum and continuum
    in selected area
    ________________
    Arguments:

    wln_sel (float array) - wavelength in selected area
    flx_sel (float array) - flux in selected area
    cnt_sel (float array) - continuum curve in selected area
    ________________________________________________________
    Returns:

    Intensity (area between spectrum and continuum)
    """

    intensity = np.trapz(flx_sel - cnt_sel, wln_sel)

    return intensity

def fit_gauss(wln_sel, flx_sel, cnt_lvl):
    """
    Function to fitting gaussian in selected area
    _____________________________________________
    Arguments:

    wln_sel (float array) - wavelength in selected area
    flx_sel (float array) - flux in selected area
    cnt_lvl (float array) - median continuum level in
                            selected area
    _________
    Returns:

    Coefficients of gauss curve
    """

    # Initial approximation
    amp = np.max(flx_sel) - np.min(flx_sel)
    mean = calc_masc(wln_sel, flx_sel)
    sigma = np.std(flx_sel)

    popt, pcov = curve_fit(
        gauss_function,
        wln_sel,
        flx_sel - cnt_lvl,
        p0=[amp, mean, sigma]
    )

    return popt


import numpy as np
from scipy.stats import norm
import xlwings as xw

# https://en.wikipedia.org/wiki/Greeks_(finance)
@xw.func
def bsm(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16, CP='call'):
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = (S * np.exp(-q * T) * norm.cdf(d1, 0.0, 1.0) - K * np.exp(-r * T) * norm.cdf(d2, 0.0, 1.0))
    if CP.lower() == 'put':
        result = (K * np.exp(-r * T) * norm.cdf(-d2, 0.0, 1.0) - S * np.exp(-q * T) * norm.cdf(-d1, 0.0, 1.0))

    return result

@xw.func
def bsm_delta(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16, CP='call'):
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = np.exp(-q * T) * norm.cdf(d1, 0.0, 1.0)
    if CP.lower() == 'put':
        result = - np.exp(-q * T) * norm.cdf(-d1, 0.0, 1.0)

    return result

@xw.func
def bsm_vega(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16):
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = S * np.exp(-q * T) * norm.pdf(d1, 0.0, 1.0) * np.sqrt(T)
    # result2 = K * np.exp(-r * T) *  norm.pdf(d2, 0.0, 1.0) * np.sqrt(T)
    # assert result == result2, 'vega failed'
    return result

@xw.func
def bsm_theta(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16, CP='call'):
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = - np.exp(-q * T) * S * norm.pdf(d1, 0.0, 1.0) * sigma / 2 / np.sqrt(T) \
                 - r * K * np.exp(-r * T) * norm.cdf(d2, 0.0, 1.0) \
                 + q * S * np.exp(-q * T) * norm.cdf(d1, 0.0, 1.0)
    if CP.lower() == 'put':
        result = - np.exp(-q * T) * S * norm.pdf(-d1, 0.0, 1.0) * sigma / 2 / np.sqrt(T) \
                 + r * K * np.exp(-r * T) * norm.cdf(-d2, 0.0, 1.0) \
                 - q * S * np.exp(-q * T) * norm.cdf(-d1, 0.0, 1.0)

    return result

@xw.func
def bsm_rho(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16, CP='call'):
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = K * T * np.exp(-r*T) * norm.cdf(d2, 0.0, 1.0)
    if CP.lower() == 'put':
        result = -K * T * np.exp(-r*T) * norm.cdf(-d2, 0.0, 1.0)

    return result

@xw.func
def bsm_gamma(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16):
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = np.exp(-q*T) * norm.pdf(d1) / S / sigma / np.sqrt(T)
    # result = K * np.exp(-r*T) * np.pdf(d2) / S / S / sigma / np.sqrt(T)
    return result

@xw.func
def bsm_vanna(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16):
    """d^2V/dS/dsigma"""
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = -np.exp(-q*T) * norm.pdf(d1) * d2 / sigma
    return result

@xw.func
def bsm_volga(S, K, T = 1.0, r = 0.0, q = 0.0, sigma = 0.16):
    """d^2V/dsigma^2"""
    d1 = (np.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - q - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = S*np.exp(-q*T) * norm.pdf(d1) * np.sqrt(T) * d1 * d2 / sigma
    return result

@xw.func
def black76(F, K, T, r, sigma, CP='call'):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = np.exp(-r*T)*( F*norm.cdf(d1) - K*norm.cdf(d2) )
    else:
        result = np.exp(-r*T)*( K*norm.cdf(-d2) - F*norm.cdf(-d1) )

    return result

@xw.func
def black76_delta(F, K, T, r, sigma, CP='call'):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = np.exp(-r*T)*norm.cdf(d1)
    else:
        result = -np.exp(-r*T)*norm.cdf(-d1)

    return result

@xw.func
def black76_vega(F, K, T, r, sigma):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = F * np.exp(-r * T) * norm.pdf(d1) * np.sqrt(T)
    # result = K * np.exp(-r*T) * norm.pdf(d2)*np.sqrt(T)

    return result

@xw.func
def black76_theta(F, K, T, r, sigma, CP='call'):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = -F*np.exp(-r*T)*norm.pdf(d1)*sigma/2/np.sqrt(T) \
                 - r*K*np.exp(-r*T)*norm.cdf(d2) \
                 + r*F*np.exp(-r*T)*norm.cdf(d1)
    else:
        result = -F * np.exp(-r * T) * norm.pdf(-d1) * sigma / 2 / np.sqrt(T) \
                 + r * K * np.exp(-r * T) * norm.cdf(-d2) \
                 - r * F * np.exp(-r * T) * norm.cdf(-d1)

    return result

@xw.func
def black76_rho(F, K, T, r, sigma, CP='call'):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = 0.0
    if CP.lower() == 'call':
        result = K*T*np.exp(-r*T)*norm.cdf(d2)
    else:
        result = -K*T*np.exp(-r*T)*norm.cdf(-d2)
    return result

@xw.func
def black76_gamma(F, K, T, r, sigma):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = np.exp(-r*T)*norm.pdf(d1)/F/sigma/np.sqrt(T)
    # result = k*np.exp(-r*T)*norm.pdf(d2)/F/F/sigma/np.sqrt(T)

    return result

@xw.func
def black76_vanna(F, K, T, r, sigma):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = -np.exp(-r*T)*norm.pdf(d1)*d2/sigma

    return result

@xw.func
def black76_volga(F, K, T, r, sigma):
    d1 = (np.log(F / K) + (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(F / K) - (0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    result = F*np.exp(-r*T)*norm.pdf(d1)*np.sqrt(T)*d1*d2/sigma

    return result


from math import sin, cos, sqrt, atan2, radians
import numpy as np

# approx. radius of earth
R = 6373.0

#computes distance in km between to given points with lat and long
def computeDistance(p1,p2):

#adds a distance column to a table

    lat1 = radians(p1[0])
    lon1 = radians(p1[1])
    lat2 = radians(p2[0])
    lon2 = radians(p2[1])

    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
    c = 2 * atan2(sqrt(a), sqrt(1 - a))

    distance = R * c

    return distance

def getDistances(table):
    result = []

    curOwl = "" # current Owl. Changes when reaching a new tag ID
    p1 = []
    p2 = []

    for f in table:

        if curOwl == "" :
            curOwl = f[0]
            p1 = [f[2], f[3]]
            distanceFeature = np.array(np.append(f,  0 ))
            result.append(distanceFeature)

        else:
            if curOwl == f[0]:

                p2 = [f[2], f[3]]

                distance = computeDistance(p1, p2)

                distanceFeature = np.array(np.append(f, distance))
                p1 = p2
                result.append(distanceFeature)

            else:
                curOwl = ""
                distanceFeature = np.array(np.append(f, 0))
                result.append(distanceFeature)

    return result


import numpy as np
import pandas as pd
import math

def get_kuramoto_params(x):
    """
    Calculates the two Kuramoto parameters: r for measuring synchrony and ψ for the average phase.

    Arguments:
    x = 1D numpy array or pandas series, shape (n) where n is number of timepoints, expects unit of time to be hours

    Returns:
    mag = the magnitude of synchrony (r) between the set of oscillators, between [0-1]
    ang = the average phase (ψ) of the set of oscillators

    If you use this code, consider citing https://www.biorxiv.org/content/10.1101/2022.01.11.475783v1
    """
    if isinstance(x, (pd.Series, pd.DataFrame)):
        x = x.values
    if x.ndim > 1:
        print("Input data must be 1 dimensional")
        return np.nan, np.nan
    # Convert from hours to radians
    x = x * np.pi / 12
    scale_factor = 1/x.shape[0]
    cosine_term, sine_term = 0, 0
    for i in range(x.shape[0]):
        cosine_term += np.cos(x[i])
        sine_term += np.sin(x[i])
    # Get magnitude of combined vector through square root of sum of squares
    mag = scale_factor * np.sqrt(cosine_term**2 + sine_term**2)
    # Calculate angle of combined vector, set to be from positive real axis
    ang = math.atan2(cosine_term, sine_term)
    ang += 2*np.pi
    ang = ang % (2*np.pi)
    return mag, ang


from scipy.special import sph_harm, assoc_laguerre
import math
FACTORIAL_LIMIT = 170  # the limit over which factorial cannot be converted to float

def Y(l, m, theta, phi):
    '''
    Spherical harmonics in the convention of physics.

    :param l: orbital quantum number.
    :param m: magnetic quantum number.
    :param theta: azimuthal angle (angle with z axis).
    :param phi: polar angle (angle with x axis in the xy plane).

    :return: the spherical harmonics Y_m^l(theta, phi).
    '''

    if l < 0:
        raise ValueError(f'l must be larger than 0. Found: l = {l}')
    if m < -l or m > l:
        raise ValueError(f'm must be between -l and l. Found: m = {m}, l = {l}')

    return sph_harm(m, l, phi, theta)

def R(n: int, l: int, r: float, a: float = 1) -> float:
    '''
    Radial wave function of the hydrogen atom :math:R_{n \ell}(r)

    :param n: principal quantum number.
    :param l: orbital quantum number.
    :param r: dimensionless radius.
    :param a: bohr radius.

    :return: normalized radial wave function at r.
    '''
    n = int(n)

    if n < 1:
        raise ValueError(f'n must be greater than 0. Found: n = {n}')
    if l < 0 or l >= n:
        raise ValueError(
            f'l must be greater than or equal to 0 and less than n. Found: n = {n}, l = {l}.'
        )

    norm = math.sqrt((2 / (n * a)) ** 3)

    if n - l - 1 <= FACTORIAL_LIMIT:
        norm *= math.sqrt(math.factorial((n - l - 1)) / (2 * n) / math.factorial(n + l))
    else:
        norm /= math.sqrt((2 * n) * prod(n-l, n+l))

    norm *= math.exp(- r / (n * a))
    norm *= (2*r / (n * a)) ** l

    rad_func = assoc_laguerre(x=2*r/(n*a), n=n-l-1, k=2*l+1)

    return norm * rad_func

def psi(
    n: int, l: int, m: int,
    r: float, theta: float, phi: float,
    a: float = 1
) -> float:
    '''
    Normalized wave function of the hydrogen atom.

    :param n: principal quantum number.
    :param l: orbital quantum number.
    :param m: magnetic quantum number.
    :param r: radius.
    :param theta: azimuthal angle [0, pi].
    :param phi: polar angle [0, 2pi].
    :param a: Bohr radius.

    :return: normalized wave function psi_{n, l, m} at r, theta, phi.
    '''

    n = int(n)

    if n < 1:
        raise ValueError(f'n must be greater than 0. Found: n = {n}')
    if l < 0 or l >= n:
        raise ValueError(f'l must be between 0 and n - 1. Found: l = {l}, n = {n}')
    if m < -l or m > l:
        raise ValueError(f'm must be between -l and l. Found: m = {m}, l = {l}')

    return R(n, l, r, a) * Y(l, m, theta, phi)

def prod(start, end):
    p = 1
    for i in range(start, end+1):
        p *= i
    return p


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 27 19:05:27 2017

@author: sitibanc
"""
import math
from scipy.stats import norm
import numpy as np

# Black-Sholes定價模型

def blscall(s, l, t, r, sigma):
    d1 = math.log(s/l) + ( r + 0.5 * sigma ** 2) * t / sigma * math.sqrt(t)
    #d2 = math.log(s/l) + ( r - 0.5 * sigma ** 2) * t / sigma * math.sqrt(t)
    d2 = d1 - sigma * math.sqrt(t)
    return s * norm.cdf(d1) - l * math.exp(-r * t) * norm.cdf(d2)

s = 50.0
l = 40.0
t = 2.0
r = 0.08
sigma = 0.2

print('Black-Scholes Call price: ', blscall(s, l, t, r, sigma))
print('Parity: ', blscall(s, l, t, r, sigma) + l * math.exp(-r * t) - s)

# 差分代替偏微分

d1 = math.log(s/l) + ( r + 0.5 * sigma ** 2) * t / sigma * math.sqrt(t)
print('偏微分：', norm.cdf(d1))
print('差分：', (blscall(s + 0.01, l, t, r, sigma) - blscall(s - 0.01, l, t, r, sigma)) / 0.02)

# Monte-Carlo Method

n = 100
dt = t / n

p = np.zeros([10000, n+1])
for i in range(10000):
    p[i,0] = s
    for j in range(n):
        p[i,j+1] = p[i,j] * math.exp((r - 0.5 * sigma ** 2) * dt + np.random.normal(0, 1, 1) * sigma * math.sqrt(dt))

# 以Mote-Carlo算出的結果回推目前call option的合理價格
c = 0
for i in range(10000):
    if p[i, 100] > l:
        c += (p[i, 100] - l) / 10000
print('Monte-Carlo: ', c * math.exp(-r * t))


import numpy as np
from ._base import Activation

class SigmoidActivation(Activation):
    """Sigmoid activation layer."""

    def forward(self, in_arr: np.ndarray) -> np.ndarray:
        out = 1.0 / (1.0 + np.exp(-in_arr))
        return out

    def backward(self, out: np.ndarray) -> np.ndarray:
        grad = out * (1 - out)
        return grad

class ReLUActivation(Activation):
    """ReLU activation layer."""

    def forward(self, in_arr: np.ndarray) -> np.ndarray:
        out = np.max(in_arr, 0)
        return out

    def backward(self, out: np.ndarray) -> np.ndarray:
        grad = np.zeros_like(out)
        grad[out > 0] = 1
        return grad

class TanhActivation(Activation):
    """Tanh activation layer."""

    def forward(self, in_arr: np.ndarray) -> np.ndarray:
        out = np.tanh(in_arr)
        return out

    def backward(self, out: np.ndarray) -> np.ndarray:
        grad = 1.0 - np.power(out, 2)
        return grad


import numpy as np
import matplotlib.pyplot as plt
from sklearn import linear_model
from sklearn.svm import SVR

plt.close('all')
# create dataset for a semi-circle
N=1000
x=np.linspace(-1,1,N)
ymodel=np.sqrt(1-x**2)
ymodel=np.sin(2*np.pi*x)
y=ymodel+np.random.normal(0,0.001,N)
noise=np.zeros(N)
noise[10:N-10:20]=1;
y[noise==1]=5

### Linear regrassion fiting
model=50
X=np.zeros((N,model))
for i in range(1,model):
    X[:,i-1]=pow(x,i)

modelLinear = linear_model.LinearRegression()
modelLinear.fit(X,y)
coefsLinear=modelLinear.coef_

### L2 regularization regression
n_alphas = 200
alphas = np.logspace(5, -5, n_alphas)

modelL2 = linear_model.Ridge(fit_intercept=False)

coefsL2 = np.zeros((n_alphas,model))
errorL2 = np.zeros(n_alphas)
modelDeviationL2=np.zeros(n_alphas)
for a,k in zip(alphas,range(0,n_alphas)):
    modelL2.set_params(alpha=a)
    modelL2.fit(X, y)
    coefsL2[k,:]=modelL2.coef_
    yest=modelL2.predict(X)
    errorL2[k]=np.mean((y-yest)**2)
    modelDeviationL2[k]=np.mean(((ymodel-yest)**2))

## L1 regularization regression
modelL1 = linear_model.Lasso(fit_intercept=False)

coefsL1 = np.zeros((n_alphas,model))
errorL1 = np.zeros(n_alphas)
modelDeviationL1=np.zeros(n_alphas)

for a,k in zip(alphas,range(0,n_alphas)):
    modelL1.set_params(alpha=a)
    modelL1.fit(X, y)
    coefsL1[k,:]=modelL1.coef_
    yest=modelL1.predict(X)
    errorL1[k]=np.mean((y-yest)**2)
    modelDeviationL1[k]=np.mean(((ymodel-yest)**2))

## Plot L1 and L2 weights for different alphas
fig,subplot=plt.subplots(2,1,sharex=True)

ax =subplot[0]

ax.plot(alphas, coefsL2)
ax.set_xscale('log')
ax.set_title('L2 weights as a function of the regularization')
ax.set_ylabel('weights')

ax=subplot[1]

ax.plot(alphas, coefsL1,'--')
ax.set_xscale('log')
ax.set_xlabel('alpha')
ax.set_ylabel('weights')

ax.set_title('L1 weights as a function of the regularization')
plt.axis('tight')
plt.show()

## Plot L1 and L2 estimation error for different alphas
plt.figure()
plt.plot(alphas,errorL1,color='r',label='L1 deviation from data')
plt.plot(alphas,errorL2,color='b', label='L2 deviation from data')
plt.plot(alphas,modelDeviationL1,color='r',ls='--',label='L1 deviation from model')
plt.plot(alphas,modelDeviationL2,color='g',ls='--', label='L2 deviation from model')
plt.xscale('log')
plt.xlabel('Alphas')
plt.ylabel('Error')
plt.title('L1 and L2 estimation error for different alphas')
plt.legend()
plt.show()

## Plot Number of zeros weights of L1 and L2 regularization
plt.figure()
L1zeros=np.sum(abs(coefsL1)<1e-15,axis=1)
L2zeros=np.sum(abs(coefsL2)<1e-15,axis=1)
plt.plot(alphas,L1zeros,'r',alphas,L2zeros,'b')
plt.xscale('log')
plt.xlabel('Alphas')
plt.ylabel('Number of zeros')
plt.title('Plot number of zeros weights of L1 and L2 regularization')
plt.show()

## Estimate using all three models
yestL0=modelLinear.predict(X)
yestL1=modelL1.predict(X)
yestL2=modelL2.predict(X)
plt.figure()
plt.scatter(x,y,alpha=0.2,color='darkorange',label='data')
plt.plot(x,ymodel,color='k',label='Model')
plt.plot(x,yestL0, color='r',ls='--',label='Linear regression')
plt.plot(x,yestL1,color='g',ls='--',label='L1 regularization')
plt.plot(x,yestL2,color='b',ls='--',label='L2 regularization')
plt.xlabel('data')
plt.ylabel('target')
plt.title('Estimation using linear Regression')
plt.legend()
plt.show()

print("\nError from data L0: ",(np.sqrt(np.mean((y-yestL0)**2))))
print("Error from data L1: ",(np.sqrt(np.mean((y-yestL1)**2))))
print("Error from data L2: ",(np.sqrt(np.mean((y-yestL2)**2))))

print("\nError from model L0: ",(np.sqrt(np.mean((ymodel-yestL0)**2))))
print("Error from model L1: ",(np.sqrt(np.mean((ymodel-yestL1)**2))))
print("Error from model L2: ",(np.sqrt(np.mean((ymodel-yestL2)**2))))

## Estimation using SVM regression
plt.figure()
svr_rbf = SVR(kernel='rbf', C=1e3, gamma=0.1)
svr_lin = SVR(kernel='linear', C=1e3)
svr_poly = SVR(kernel='poly', C=1e3, degree=2)
y_rbf = svr_rbf.fit(X, y).predict(X)
y_lin = svr_lin.fit(X, y).predict(X)
y_poly = svr_poly.fit(X, y).predict(X)

print("\nError from data RBF: ",(np.sqrt(np.mean((y-y_rbf)**2))))
print("Error from data Linear: ",(np.sqrt(np.mean((y-y_lin)**2))))
print("Error from data Poly: ",(np.sqrt(np.mean((y-y_poly)**2))))

print("\nError from model RBF: ",(np.sqrt(np.mean((ymodel-y_rbf)**2))))
print("Error from model Linear: ",(np.sqrt(np.mean((ymodel-y_lin)**2))))
print("Error from model Poly: ",(np.sqrt(np.mean((ymodel-y_poly)**2))))

lw = 2
plt.scatter(x, y, color='darkorange', label='data')
plt.hold('on')
plt.plot(x, y_rbf, color='navy', lw=lw, label='RBF model')
plt.plot(x, y_lin, color='c', lw=lw, label='Linear model')
plt.plot(x, y_poly, color='cornflowerblue', lw=lw, label='Polynomial model')
plt.xlabel('data')
plt.ylabel('target')
plt.title('Estimation using Support Vector Regression')
plt.legend()
plt.show()


# Simulate Matern hard-core point processes (Type I/II) on a rectangle.
# Author: H. Paul Keeler, 2019.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts
# For more details, see the post:
# hpaulkeeler.com/simulating-matern-hard-core-point-processes/

import numpy as np;  # NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt  # For plotting

plt.close('all');  # close all figures

numbSim=10**3; #number of simulations

# Simulation window parameters
xMin = -.5;
xMax = .5;
yMin = -.5;
yMax = .5;

#Parameters for the parent and daughter point processes
lambdaPoisson=50;#density of underlying Poisson point process
radiusCore=0.1;#radius of hard core

#Extended simulation windows parameters
rExt=radiusCore; #extension parameter -- use core radius
xMinExt=xMin-rExt;
xMaxExt=xMax+rExt;
yMinExt=yMin-rExt;
yMaxExt=yMax+rExt;
#rectangle dimensions
xDeltaExt=xMaxExt-xMinExt;
yDeltaExt=yMaxExt-yMinExt;
areaTotalExt=xDeltaExt*yDeltaExt; #area of extended rectangle

###START Simulations START####
#initialize arrays for collecting statistics
numbPointsAll=np.zeros(numbSim); #number of Poisson points
numbPointsAll_I=np.zeros(numbSim); #number of Matern I points
numbPointsAll_II=np.zeros(numbSim); #number of Matern II points
#loop through for each simulation
for ss in range(numbSim):

    #Simulate Poisson point process for the parents
    numbPointsExt= np.random.poisson(areaTotalExt*lambdaPoisson);#Poisson number
    #x and y coordinates of Poisson points for the parent
    xxPoissonExt=xMinExt+xDeltaExt*np.random.rand(numbPointsExt);
    yyPoissonExt=yMinExt+yDeltaExt*np.random.rand(numbPointsExt);

    #thin points if outside the simulation window
    booleWindow=((xxPoissonExt>=xMin)&(xxPoissonExt<=xMax)&(yyPoissonExt>=yMin)&(yyPoissonExt<=yMax));
    indexWindow=np.arange(numbPointsExt)[booleWindow];
    #retain points inside simulation window
    xxPoisson=xxPoissonExt[booleWindow];
    yyPoisson=yyPoissonExt[booleWindow];

    numbPoints=len(xxPoisson); #number of Poisson points in window
    #create random marks for ages
    markAge=np.random.rand(numbPointsExt);

    ###START Removing/thinning points START###
    booleRemoveI=np.zeros(numbPoints, dtype=bool);#Index for removing points -- Matern I
    booleKeepII=np.zeros(numbPoints,dtype=bool);#Index for keeping points -- Matern II
    for ii in range(numbPoints):
        distTemp=np.hypot(xxPoisson[ii]-xxPoissonExt,yyPoisson[ii]-yyPoissonExt);  #distances to other points
        booleInDisk=(distTemp<radiusCore)&(distTemp>0); #check if inside disk

        #Matern I
        booleRemoveI[ii]=any(booleInDisk);

        #Matern II
        #keep the younger points
        if len(markAge[booleInDisk])==0:
            booleKeepII[ii]=True;
            #Note: if markAge(booleInDisk) is empty, keepBooleII[ii]=True.
        else:
            booleKeepII[ii]=all(markAge[indexWindow[ii]]<markAge[booleInDisk]);

    ###END Removing/thinning points END###

    #Remove/keep points to generate Matern hard-core processes
    #Matérn I
    booleKeepI=~(booleRemoveI);
    xxMaternI=xxPoisson[booleKeepI];
    yyMaternI=yyPoisson[booleKeepI];

    #Matérn II
    xxMaternII=xxPoisson[booleKeepII];
    yyMaternII=yyPoisson[booleKeepII];

    #Update statistics
    numbPointsAll[ss]=numbPoints;
    numbPointsAll_I[ss]=len(xxMaternI);
    numbPointsAll_II[ss]=len(xxMaternII);

###END Simulations END####

##Plotting
markerSize=12; #marker size for the Poisson points
plt.plot(xxPoisson,yyPoisson, 'ko',markerfacecolor="None",markersize=markerSize);
plt.plot(xxMaternI,yyMaternI, 'rx',markersize=markerSize/2);
plt.plot(xxMaternI,yyMaternI, 'b+',markersize=markerSize);
plt.legend(('Underlying Poisson','Matern I','Matern II'));

#Compare statistics
diskArea=np.pi*radiusCore**2; #area of disk
areaWindow=(xMax-xMin)*(yMax-yMin); #area of simulation window

#underlying Poisson point process
lambdaExact=lambdaPoisson
print("lambdaExact = ",lambdaExact)
lambdaEmp=np.mean(numbPointsAll/areaWindow)
print("lambdaEmp = ",lambdaEmp)

#Matern I
lambdaExactI=lambdaPoisson*np.exp(-lambdaPoisson*diskArea) #exact
print("lambdaExactI = ",lambdaExactI)
lambdaEmpI=np.mean(numbPointsAll_I/areaWindow) #empirical
print("lambdaEmpI = ",lambdaEmpI)

#Matern II
lambdaExactII=1/(diskArea)*(1-np.exp(-lambdaPoisson*diskArea))
print("lambdaExactII = ",lambdaExactII)
lambdaEmpII=np.mean(numbPointsAll_II/areaWindow)
print("lambdaEmpII = ",lambdaEmpII)


import numpy as np

# Bellman-Ford algorithm
def bellman_ford1(graph, start):
    # distances and parents init
    distance = {}
    parent = {}
    for node in graph:
        distance[node] = 'inf'
        parent[node] = 'nil'

    distance[start] = 0

    for i in range(len(graph) - 1):
        changes = False
        for v in graph:
            if not distance[v] == 'inf':
                for n in graph[v]:
                    if distance[n] == 'inf' or distance[n] > distance[v] + graph[v][n]:
                        changes = True
                        distance[n] = distance[v] + graph[v][n]
                        parent[n] = v

        if not changes:
            break

    return distance, parent

def bellman_ford2(graph, start):
    # distances and parents init
    distance = {}
    parent = {}
    for node in graph:
        distance[node] = 'inf'
        parent[node] = 'nil'

    distance[start] = 0

    c = set()
    c.add(start)

    while c:
        changed = set()
        for v in graph:
            if v in c or v in changed:
                if v in changed:
                    changed.remove(v)
                for n in graph[v]:
                    if distance[n] == 'inf' or distance[n] > distance[v] + graph[v][n]:
                        distance[n] = distance[v] + graph[v][n]
                        parent[n] = v
                        changed.add(n)

        c = set(changed)

    return distance, parent

def bellman_ford3(graph, start):
    # distances and parents init
    distance = {}
    parent = {}
    for node in graph:
        distance[node] = 'inf'
        parent[node] = 'nil'

    distance[start] = 0

    order = list(graph)
    order = list(np.random.permutation(order))

    index = order.index(start)
    temp = order[index]
    order[index] = order[0]
    order[0] = temp

    r_order = list(order)
    r_order.reverse()

    graph_plus = dict()
    graph_minus = dict()

    for v in graph:
        neighbours_p = dict()
        for e in graph[v]:
            if order.index(e) > order.index(v):
                neighbours_p[e] = graph[v][e]
        graph_plus[v] = neighbours_p

    for v in graph:
        neighbours_m = dict()
        for e in graph[v]:
            if order.index(e) < order.index(v):
                neighbours_m[e] = graph[v][e]
        graph_minus[v] = neighbours_m

    c = set()
    c.add(start)

    while c:
        changed = set()
        for v in order:
            if v in c or v in changed:
                for n in graph_plus[v]:
                    if distance[n] == 'inf' or distance[n] > distance[v] + graph_plus[v][n]:
                        distance[n] = distance[v] + graph_plus[v][n]
                        parent[n] = v
                        changed.add(n)

        for v in r_order:
            if v in c or v in changed:
                for n in graph_minus[v]:
                    if distance[n] == 'inf' or distance[n] > distance[v] + graph_minus[v][n]:
                        distance[n] = distance[v] + graph_minus[v][n]
                        parent[n] = v
                        changed.add(n)

        c = set(changed)

    return distance, parent


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

# # Simple nonlinear complementarity problem
#
# **Randall Romero Aguilar, PhD**
#
# This demo is based on the original Matlab demo accompanying the  Computational Economics and Finance 2001 textbook by Mario Miranda and Paul Fackler.
#
# Original (Matlab) CompEcon file: **demslv13.m**
#
# Running this file requires the Python version of CompEcon. This can be installed with pip by running
#
#     !pip install compecon --upgrade
#
# Last updated: 2021-Oct-01
#

# ## About
#
# The problem is to solve
#
# \begin{equation}
# f(x, y) = \begin{bmatrix}1+xy -2x^3-x\\ 2x^2-y\end{bmatrix}
# \end{equation}
#
# subject to $0 \leq x, y \leq 1$

# In[1]:

from compecon import MCP, jacobian
import numpy as np

x0 = [0.5, 0.5]

# ### Solving the problem without the Jacobian
# To solve this problem we create a **MCP** object using a lambda function.

# In[2]:

def func(z):
    x, y = z
    return np.array([1 + x*y - 2*x**3 - x, 2*x**2 - y])

F = MCP(func, [0, 0], [1,1])

# Solve for initial guess $x_0 = [0.5, 0.5]$

# In[3]:

x = F.zero(x0, transform='minmax', show=True)
#x = F.zero(x0, transform='ssmooth', show=True) # FIXME: generates error
print(f'Solution is {x=}.')

# ### Solving the problem with the Jacobian

# In[4]:

def func2(z):
    x, y = z
    f = [1 + x*y - 2*x**3 - x, 2*x**2 - y]
    J = [[y-6*x**2-1, x],[4*x, -1]]
    return np.array(f), np.array(J)

F2 = MCP(func2, [0, 0], [1,1])

# In[5]:

x = F2.zero(x0, transform='minmax', show=True)
print(f'Solution is {x=}.')

_build/jupyter_execute/notebooks/slv/13 Simple nonlinear complementarity problem.py


import numpy as np

a = np.arange(15).reshape(3, 5)

# print np array
print a
# print shape of the array
print a.shape
# print #of dimensions - rank
print a.ndim
# print data type
print a.dtype.name
# print item size
print a.itemsize
# print actual size (how many elements)
print a.size
# print array type
print type(a)

# Data type of array is choose automatically when different type of data given
a = np.array([2, 3, 4])
print a.dtype

b = np.array([1.2, 3.5, 5.1])
print b.dtype

# sequences of sequences into two-dimensional arrays
b = np.array([(1.5, 2, 3), (4, 5, 6)])
print b

# Data type can be also define at the initiation time
c = np.array([[1, 2], [3, 4]], dtype=complex)
print c.dtype.name

# can create array with zeros , ones , and randoms (data type is float 64)
np.zeros((3, 4))

np.ones((2, 3, 4), dtype=np.int16)  # dtype can also be specified

np.empty((2, 3))  # uninitialized, output may vary

# can create sequence of numbers (start , end , increment )
np.arange(10, 30, 5)

# 9 numbers from 0 to 2
np.linspace(0, 2, 9)

# array types
a = np.arange(6)  # 1d array
print(a)

b = np.arange(12).reshape(4, 3)  # 2d array
print(b)

c = np.arange(24).reshape(2, 3, 4)  # 3d array
print(c)

# if array is too large to display it will skip showing middle content
print(np.arange(10000).reshape(100, 100))
# If need to see all
# np.set_printoptions(threshold='nan')

# basic arithmetic operation will return a new array
a = np.array([20, 30, 40, 50])
b = np.arange(4)
print b

c = a - b
print c

print b ** 2

print 10 * np.sin(a)

print a < 35

# * will works on element wise if need to get matrix product use dot

A = np.array([[1, 1],
              [0, 1]])
B = np.array([[2, 0],
              [3, 4]])
print A * B  # element wise product

print A.dot(B)  # matrix product

print np.dot(A, B)  # another matrix product


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

def ramuh(x,y,choices,plotTypes):
    """This is the user interface
    'x' is your list of x-axis variables
    'y' is your list of y-axis variables
    'choices' is an integer of how many top choices you want to view.
    'plotType' controls how the trendlines are shown in the end.
        0: all on the same plot
        1: all on different plots in the same window
        2: all on different windows that pop up all at once
        3: all on different windows that pop up one by one.

    Example Input: ramuh(np.array([0.5,0.9,1.7,2.4]),np.array([8.7,9.3,10.6,12.1]),2,0)
    """

#[lineList,r2Max,nList,recomendation]
    print('Welcome to ramuh. I will be your guide for our journey in curve fitting.')
    print('I see you have given me some inputs. Let me check that they are indeed numpy arrays.')
    result = check(x,y,choices,plotTypes)
    print('Looks good. You asked for the best {0:2d} trendlines for this data. I would be happy to oblige.\n'.format(choices))
    answer = optimizer(x,y,choices)
    #i = 0
    #print('\nlineList',answer[0][i])
    #print('\nr2Max',answer[1][i])
    #print('\nnList',answer[2][i])
    #print('\nrecomendation',answer[3][i])
    #print('')
    for i in range(choices):
        print('Choice number {0:2d} comes from a '.format(i+1)+answer[2][i]+' trendline')
        print('The r2 value for it is, {0:1.5f}, which is '.format(answer[1][i])+answer[3][i])
        print('This trendline is: '+answer[0][i],'\n')
    print("Before I go, I'll let you check over the trendlines I choose. Here is a plot that contains them all.")
    showplots(x,y,answer,choices,plotTypes)

#The Black Box
def check(x,y,choices,plotTypes):
    """This checks that the inputs will work.

    Example Input: optimizer(np.array([0.5,0.9,1.7,2.4]),np.array([8.7,9.3,10.6,12.1]),2,0)
    """

    if len(x) > len(y):
        print('Your x-list is larger than your y-list.')
    elif len(y) > len(x):
        print('Your y-list is larger than your x-list.')

    if type(x) != np.ndarray:
        print('Your x-list is not an np.array')
    if type(y) != np.ndarray:
        print('Your y-list is not an np.array')
    #Check for multi-dimensionsal
    #Check that there are not too many choices

    #Have it return wether or not it is good. If not, the whole thing stops.

def showplots(xlist,ylist,answer,choices,plotType):
    """This creates a dynamic plot(s) for the trendlines.

    Example Input: optimizer(np.array([0.5,0.9,1.7,2.4]),np.array([8.7,9.3,10.6,12.1]),2,0)
    """

    h = 50 #step scalar
    x = np.arange(xlist.min(),xlist.max(),(xlist.max()-xlist.min())/h)
    if plotType == 0:
        plt.figure(1)
        plt.plot(xlist,ylist,'k-')
        for j in range(choices):
            if j<6:
                if j%2 == 0:
                    if j == 0:
                        lnStl = 'Purple'
                    elif j == 2:
                        lnStl = 'Orange'
                    elif j == 4:
                        lnStl = 'g-'
                else:
                    if j == 1:
                        lnStl = 'r-'
                    elif j == 3:
                        lnStl = 'y-'
                    elif j == 5:
                        lnStl = 'b-'
            else:
                lnStl = 'w--'
            plt.plot(x,eval(answer[0][j].strip('y=')),lnStl)
        plt.show()

def optimizer(x,y,choices):
    """This runs through the various trendlines and evaluates them.
    myList = [linear,power_law,logrtithmic,poly2,poly3,poly4]

    Example Input: optimizer(np.array([0.5,0.9,1.7,2.4]),np.array([8.7,9.3,10.6,12.1]),2)
    """

    myList,solvedMax,r2Max,nList,recomendation,lineList = [],[],[],[],[],[]
    myList.append(trendline(x,y,'linear'))
    myList.append(trendline(x,y,'power_law'))
    myList.append(trendline(x,y,'exponential'))
    myList.append(trendline(x,y,'logrithmic'))
    myList.append(trendline(x,y,3))
    myList.append(trendline(x,y,4))
    myList.append(trendline(x,y,5))
    myList = np.transpose(myList)[:]
    for i in range(choices):
        n = myList[1].argmax()
        solvedMax.append(myList[0][i].tolist())
        r2Max.append(myList[1][i])
        if n == 0:
            nList.append('linear')
            lineList.append('y='+str(solvedMax[0][0][0])+'+'+str(solvedMax[0][1][0])+'*x')
        elif n == 1:
            nList.append('power law')
            lineList.append('y='+str(solvedMax[0][0][0])+'*x**('+str(solvedMax[0][1][0]))
        elif n == 2:
            nList.append('exponential')
            lineList.append('y='+str(solvedMax[0][0][0])+'*np.exp('+str(solvedMax[0][1][0])+'*x)')
        elif n == 3:
            nList.append('logrithmic')
            lineList.append('y='+str(solvedMax[0][0][0])+'+'+str(solvedMax[0][1][0])+'*np.log(x)')
        else:
            if n == 4:
                nList.append('2nd order polynomial')
                lineList.append('y='+str(solvedMax[0][0][0])+'+'+str(solvedMax[0][1][0])+'*x'+str(solvedMax[0][2][0])+'*x**2')
            elif n == 5:
                nList.append('3rd order polynomial')
                lineList.append('y='+str(solvedMax[0][0][0])+'+'+str(solvedMax[0][1][0])+'*x'+str(solvedMax[0][2][0])+'*x**2'+str(solvedMax[0][3][0])+'*x**3')
            else:
                nList.append(str(n-1)+'th order polynomial')
                temp = 'y='+str(solvedMax[0][0][0]+'+'+str(solvedMax[0][1][0])+'*x')
                for i in range(n-1):
                    temp += '+'+str(solvedMax[0][i+2][0])+'*x**'+str(i+2)
                lineList.append(tempStr)

        if r2Max[i] >= 0.999:
            recomendation.append('extremely good. I recommend using it.')
        elif r2Max[i] >= 0.99:
            recomendation.append('very good. I recommend using it.')
        elif r2Max[i] >= 0.98:
            recomendation.append('great. I recommend using it.')
        elif r2Max[i] >= 0.95:
            recomendation.append('good. You could use it.')
        elif r2Max[i] >= 0.90:
            recomendation.append('alright. You could still use it.')
        elif r2Max[i] >= 0.80:
            recomendation.append('not that good. You should re-take your data so I can provide you with a better trendline.')
        elif r2Max[i] >= 0.50:
            recomendation.append('bad. You should re-take your data so I can provide you with a useable trendline.')
        else:
            recomendation.append("horrible. I'm sorry it did not work out.")

        myList[1][n] = 0

    return [lineList,r2Max,nList,recomendation]

def trendline(x,y,eqnType):
    """This creates various types of trendlines from lists of data in the form of numpy arrays.

    Example Input: trendline(np.array([0.5,0.9,1.7,2.4]),np.array([8.7,9.3,10.6,12.1]),'linear')
    """
    if type(eqnType)==str:
        n = len(x)
        if eqnType == 'linear':
            sumx = np.sum(x)
            sumy = np.sum(y)
            sumxy = np.sum(x*y)
            sumx2 = np.sum(x**2)
            sumy2 = np.sum(y**2)

        elif eqnType == 'power_law':
            sumx = np.sum(np.log(x))
            sumy = np.sum(np.log(y))
            sumxy = np.sum(np.log(x)*np.log(y))
            sumx2 = np.sum(np.log(x)**2)
            sumy2 = np.sum(np.log(y)**2)

        elif eqnType == 'exponential':
            sumx = np.sum(x)
            sumy = np.sum(np.log(y))
            sumxy = np.sum(x*np.log(y))
            sumx2 = np.sum(x**2)
            sumy2 = np.sum(np.log(y)**2)

        elif eqnType == 'logrithmic':
            sumx = np.sum(np.log(x))
            sumy = np.sum(y)
            sumxy = np.sum(x*y)
            sumx2 = np.sum(x**2)
            sumy2 = np.sum(y**2)

        A = np.array([[n,sumx],[sumx,sumx2]])
        b = np.array([[sumy],[sumxy]])
        solved = np.linalg.solve(A,b)
        aAns = solved[0,0]
        #print(aAns)
        bAns = solved[1,0]
        #print(bAns)
        r2 = (aAns*sumy+bAns*sumxy-1/n*sumy**2)/(sumy2-1/n*sumy**2)

    else:
        #The eqnType is the order that the polynomial is.
        sumxList,sumyList,b = [],[],[]
        n = eqnType
        A = [[n]]
        for i in range(eqnType*2): #Create a list that ranges from x^1 to x^n
            sumxList.append(np.sum(x**(i+1)))
            sumyList.append(np.sum(x**(i)*y))

        for i in range(eqnType-1): #Initialize the A
            A.append([sumxList[i]])

        for j in range(eqnType):#Set up the A and b
            for i in range(eqnType-1):
                A[j].append(sumxList[i+j])
            b.append(sumyList[j])

        A = np.array(A)
        b = np.array(b).transpose()

        #Solve it
        solved = np.linalg.solve(A,b)

        for i in range(eqnType):
            r2 = solved[i]*sumyList[i]
        r2 = (r2-solved[0]**2/n)/(np.sum(y**2)-1/n*sumyList[0]**2)
    #return [0,1]
    return [solved,r2]