#Python code for chapter 24 DSILT: Statistics

'''
-------------------------------------------------------------------------------
------------------------------Hidden Markov Models-----------------------------
-------------------------------------------------------------------------------
'''

'''
Evaluation Problem
'''

import numpy as np
from hmmlearn import hmm
import math

states = ('healthy', 'sick')
observations = ('no-symptoms', 'cold', 'dizzy')

start_probability = {'healthy': 0.8, 'sick': 0.2}
startm = np.array([0.8, 0.2])
transition_probability = {
   'healthy' : {'healthy': 0.8, 'sick': 0.2},
   'sick' : {'healthy': 0.4, 'sick': 0.6},
   }
transm = np.array([[0.8, 0.2],
                   [0.4, 0.6]])
emission_probability = {
   'healthy' : {'no-symptoms': 0.6, 'cold': 0.3, 'dizzy': 0.1},
   'sick' : {'no-symptoms': 0.1, 'cold': 0.3, 'dizzy': 0.6},
   }
emism = np.array([[0.6, 0.3, 0.1],
                  [0.1, 0.3, 0.6]])

hmm_model = hmm.MultinomialHMM(n_components=len(states), algorithm='viterbi')
hmm_model.startprob_ = startm
hmm_model.transmat_ = transm
hmm_model.emissionprob_ = emism

#Evaluation: given a model, what is the probability of sequence y?
#Note that the score method produces the log likelihood, so to get prob, exponentiate
y = np.array([[0]])
print('Probability of first observation in a sequence being',
      observations[0], 'regardless of state is', math.exp(hmm_model.score(y)))
y = np.array([[1]])
print('Probability of first observation in a sequence being',
      observations[1], 'regardless of state is', math.exp(hmm_model.score(y)))
y = np.array([[2]])
print('Probability of first observation in a sequence being',
      observations[2], 'regardless of state is', math.exp(hmm_model.score(y)))
y = np.array([[1, 1, 0]])
print('Probability of', observations[1], observations[1], observations[0],
      'regardless of state is', math.exp(hmm_model.score(y)))
y = np.array([[0, 0, 2]])
print('Probability of', observations[0], observations[0], observations[2],
      'regardless of state is', math.exp(hmm_model.score(y)))

'''
Decoding Problem
'''

#Decoding: given a model and a sequence of observations, what is the most likely sequence of states?
#Note that the score method produces the log likelihood, so to get prob, exponentiate
y = np.array([[2]]).T
print('Given observation', observations[2],
      'the most likely hidden state is', states[hmm_model.decode(y)[1][0]],
      'with probability', math.exp(hmm_model.decode(y)[0]))
y = np.array([[0, 1, 0]]).T
print('Given observations', observations[0], observations[1], observations[0],
      'the most likely sequence of hidden states is', states[hmm_model.decode(y)[1][0]],
      states[hmm_model.decode(y)[1][1]], states[hmm_model.decode(y)[1][2]],
      'with probability', math.exp(hmm_model.decode(y)[0]))
y = np.array([[2, 1, 2]]).T
print('Given observations', observations[2], observations[1], observations[2],
      'the most likely sequence of hidden states is', states[hmm_model.decode(y)[1][0]],
      states[hmm_model.decode(y)[1][1]], states[hmm_model.decode(y)[1][2]],
      'with probability', math.exp(hmm_model.decode(y)[0]))

'''
Learning problem
'''

import datetime as dt
import numpy as np
import pandas as pd
pd.core.common.is_list_like = pd.api.types.is_list_like  #Fixes import error for datareader
import pandas_datareader.data as web
from matplotlib import cm, pyplot as plt
from matplotlib.dates import YearLocator, MonthLocator
import warnings
from hmmlearn.hmm import GaussianHMM

start_dt = dt.datetime(1993, 2, 1)
end_dt = dt.datetime(2016, 1, 1)

#d = web.DataReader('SPY', 'morningstar', start_dt, end_dt)
#d.to_csv('spy.csv')  #Write to csv so future runs don't need to call the API
d = pd.read_csv('/home/dsilt/Desktop/dsilt-stats-code/23-24 Bayesian Statistics and Markov Models/spy.csv')
d = d[['Date', 'Close']]
date_index = pd.to_datetime(d['Date'])
dts = pd.Series(d['Close'])
dts.index = date_index
log_returns = pd.Series(np.log(dts/dts.shift(1)).dropna())
#Note that log returns = percent change
#print(d.pct_change().dropna())
log_returns.plot()
plt.title('Log Returns for SPY 1993-2016')
plt.show()
dts.plot()
plt.title('Closing Price of SPY 1993-2016')
plt.show()

#Convert log returns to a numpy array
log_returns_arr = np.column_stack([log_returns]).reshape(-1, 1)

#Ignore the litany of deprecation warnings
warnings.filterwarnings("ignore")

#Create a HMM with Gaussian emissions and assume there are 2 hidden states (bear and bull markets)
hmm_model = GaussianHMM(n_components=2, covariance_type="full", n_iter=1000).fit(log_returns_arr)
hidden_states = hmm_model.predict(log_returns_arr)

#Stack plots for each of the hidden states
#This plotting code comes from hmmlearn's documentation:
#http://hmmlearn.readthedocs.io/en/latest/auto_examples/plot_hmm_stock_analysis.html
fig, axs = plt.subplots(hmm_model.n_components, sharex=True, sharey=True)
colors = cm.rainbow(np.linspace(0, 1, hmm_model.n_components))
for i, (ax, color) in enumerate(zip(axs, colors)):
    mask = hidden_states == i
    ax.plot_date(dts[1:].index[mask], d['Close'][1:][mask], ".", linestyle='none', c=color)
    ax.set_title("Hidden State #%s" % i)
    ax.xaxis.set_major_locator(YearLocator())
    ax.xaxis.set_minor_locator(MonthLocator())
    ax.grid(True)
plt.show()

'''
-------------------------------------------------------------------------------
--------------------------------Kalman Filter----------------------------------
-------------------------------------------------------------------------------
'''

'''
Kalman Filter on a 2D random walk
'''

import numpy as np
import random
import matplotlib.pyplot as plt
from pykalman import KalmanFilter

random.seed(14)

def randomWalk2d(n_steps=1000, walkType="financial"):
    x = np.zeros(n_steps)
    y = np.zeros(n_steps)
    if walkType == "random":
        for s in range(1, n_steps):
            val = random.randint(1,4)
            if val == 1:
                x[s] = x[s - 1] + 1
                y[s] = y[s - 1]
            elif val == 2:
                x[s] = x[s - 1] - 1
                y[s] = y[s - 1]
            elif val == 3:
                x[s] = x[s - 1]
                y[s] = y[s - 1] + 1
            else:
                x[s] = x[s - 1]
                y[s] = y[s - 1] - 1
    else:
        for s in range(1, n_steps):
            val = random.randint(1,3)
            if val == 1:
                x[s] = x[s - 1] + 1
                y[s] = y[s - 1]
            elif val == 2:
                x[s] = x[s - 1]
                y[s] = y[s - 1] + 1
            else:
                x[s] = x[s - 1]
                y[s] = y[s - 1] - 1
    return x,y

x, y = randomWalk2d()

#Plot random walk
plt.title("Random Walk ($n = " + str(1000) + "$ steps)")
plt.plot(x, y)
plt.show()

robotpath = np.array((x,y)).T

'''
Every state in this system consists of a coordinate pair (x,y) and nothing
else.  So the initial state is a matrix x equal to (x y) <- imagine those
vertically stacked.

X
The initial state is assumed to be drawn from a normal distribution.  We can
set its mean equal to zero, so initial state is a 1*2m matrix, where m is the
number of rows in the initial state matrix.  Since the initial state matrix
has 2 rows, the initial state mean matrix is a 1*2m matrix of where the
diagonal contains the values of the x and y coordinates (x in first row,
change in x in second row, y in third row, change in y in fourth row)

P
Since we have no idea what the covariance of each state is, it is safe to set
them all equal to 1.  The covariance matrix is an m*m matrix, where m is the
number of rows in the initial state matrix.  Since the initial state matrix
has 2 rows, the initial coveriance matrix is a 2*2 matrix of diagonal ones.

H
The observation matrix maps each state to the next one.  Since this example is
simply a random walk, the transition is the last state plus noise, so the
observation matrix is filled with ones where the value of the coordinate
changes.  So first row would be [1, 0, 0, 0] because it represents
[x_coord, change in x_coord, y_coord, change in y_coord], and the second row
would be [0, 0, 1, 0]

Q
The process errror (measurement error) is essentially 0 since there are no
measurements taken because we know the data directly.  So the observation
covariance matrix is an m*m matrix of zeros.

A
Each row and column of the transition matrix corresponds to a variable to be
updated.  So if there were 2 variables: position and velocity, then H would
be a 4*4 matrix.  The first row would be [1, 1, 0, 0] because it represents
[x_coord, change in x_coord, y_coord, change in y_coord] and
next_position = position + velocity*delta_time.  The second row would be
[0, 1, 0, 0] because next_velocity = velocity.

R
The measurement error covariance can be set to some small value.  This can be
set to any value - it should really be played with to see how it affects the
model. So the transition covariance is a m*m matrix of diagonal small values,
such as 0.2.
'''

#Parameter set up
init_state_mean = [robotpath[0, 0],
                   0,
                   robotpath[0, 1],
                   0]
init_state_cov = np.ones((4, 4))
obs_mat= [[1, 0, 0, 0],
          [0, 0, 1, 0]]
obs_cov = np.eye(2)
trans_mat = [[1, 1, 0, 0],
             [0, 1, 0, 0],
             [0, 0, 1, 1],
             [0, 0, 0, 1]]
trans_cov = np.eye(4)*1e-6

kf = KalmanFilter(n_dim_obs=2, n_dim_state=4,
                  initial_state_mean=init_state_mean,
                  initial_state_covariance=init_state_cov,
                  observation_matrices=obs_mat,
                  observation_covariance=obs_cov,
                  transition_matrices=trans_mat,
                  transition_covariance=trans_cov)

state_means, state_covs = kf.filter(robotpath)

#Smoothing with expectation maximization
kf1 = kf.em(robotpath, n_iter=5)
smoothed_state_means, smoothed_state_covariances = kf1.smooth(robotpath)

#Plot the fitted Kalman filter
plt.plot(#x, y, 'bo',
         #state_means[:, 0], state_means[:, 2], 'ro',
         x, y, 'b--',
         state_means[:, 0], state_means[:, 2], 'r--',)
plt.title('Kalman Filter Fitted to a 2D Random Walk')
plt.show()
plt.plot(#x, y, 'bo',
         #state_means[:, 0], state_means[:, 2], 'ro',
         x, y, 'b--',
         smoothed_state_means[:, 0], smoothed_state_means[:, 2], 'r--',)
plt.title('Kalman Filter Fitted to a 2D Random Walk and Smoothed with Expectation Maximization')
plt.show()

#Plot the change in each variable over time
times = range(robotpath.shape[0])
plt.plot(#times, robotpath[:, 0], 'bo',
         #times, robotpath[:, 1], 'ro',
         times, state_means[:, 0], 'b--',
         times, state_means[:, 2], 'r--',)
plt.title('Split Plot of the Change in x and y throughout the Random Walk')
plt.show()
plt.plot(#times, robotpath[:, 0], 'bo',
         #times, robotpath[:, 1], 'ro',
         times, smoothed_state_means[:, 0], 'b--',
         times, smoothed_state_means[:, 2], 'r--',)
plt.title('Split Plot of the Smoothed Change in x and y throughout the Random Walk')
plt.show()

'''
Now try deleting some points from the random walk to see how the Kalman
filter adapts to missing states
'''

#Remove observations 400-500
x_h = x.copy()
y_h = y.copy()
x_h[range(400, 500)] = np.nan
y_h[range(400, 500)] = np.nan
robotpath_obscured = np.array((x_h, y_h)).T

#Parameter set up
init_state_mean = [robotpath_obscured[0, 0],
                   0,
                   robotpath_obscured[0, 1],
                   0]
init_state_cov = np.ones((4, 4))
obs_mat= [[1, 0, 0, 0],
          [0, 0, 1, 0]]
obs_cov = np.eye(2)
delta_x = x_h[1] - x_h[0]  #If x were time, this would be more meaningful, but here it is just 1.0
#Note that this trans_mat is the same as before, but we're using delta_x to show how it would change if dt were not 1.0
trans_mat = [[1, delta_x, 0, 0],
             [0, 1, 0, 0],
             [0, 0, 1, delta_x],
             [0, 0, 0, 1]]
trans_cov = np.eye(4)*1e-6

kf = KalmanFilter(n_dim_obs=2, n_dim_state=4,
                  initial_state_mean=init_state_mean,
                  initial_state_covariance=init_state_cov,
                  observation_matrices=obs_mat,
                  observation_covariance=obs_cov,
                  transition_matrices=trans_mat,
                  transition_covariance=trans_cov)

#This is how the KF would be applied if all points were known - if we try it, it will fail when the values disappear
#state_means_h, state_covs_h = kf.filter(robotpath_obscured)
'''
#Plot the fitted Kalman filter - this shows how the previous approach fails when points are obscured
plt.plot(#x_h, y_h, 'bo',
         #state_means_h[:, 0], state_means_h[:, 2], 'ro',
         x_h, y_h, 'b--',
         state_means_h[:, 0], state_means_h[:, 2], 'r--',)
plt.show()
'''

#Make sure the tuples have the same number of dimensions as the initial state mean and initial state cov matrix (4, and (4,4) in this case)
state_means_h = np.zeros((x_h.shape[0], 4))
state_covs_h = np.zeros((x_h.shape[0], 4, 4))

#Apply the KF iteratively to iterpolate missing states
#Note that both x and y are missing, but we are assuming x always increments by 1 (delta_x). so we really only need to interpolate y
for t in range(robotpath_obscured[:,0].shape[0]):
    if np.isnan(robotpath_obscured[t,0]):
        x_h[t] = x_h[t-1]+1  #Increments x[t] by x[t-1]+1 when x[t] is missing
        state_means_h[t], state_covs_h[t] = (
            kf.filter_update(state_means_h[t-1],
                             state_covs_h[t-1],
                             observation=x_h[t])  #Only x is available
            )
    else:
        state_means_h[t], state_covs_h[t] = (
            kf.filter_update(state_means_h[t-1],
                             state_covs_h[t-1],
                             observation=robotpath_obscured[t])  #Both x and y are available
            )

#Plot the fitted Kalman filter
plt.plot(x, y, 'b--', label="Random Walk")
plt.plot(state_means[:, 0], state_means[:, 2], 'g--', label="KF with No Missing Values")
plt.plot(state_means_h[:, 0], state_means_h[:, 2], 'r--', label="KF with Missing Values")
plt.grid()
plt.legend(loc="upper left")
plt.xlabel('x')
plt.ylabel('y')
plt.title('Random Walk with Hidden States')
plt.show()

'''
Kalman Filter on 1D data

Example of Kalman filter with 1 variable: dealing with noisy readings from a
voltmeter and estimating the true voltage.  This can be applied to any sensor,
but note that a Kalman filter may not always be the best way to determine the
true signal with noise.  Other filters could be more effective.

The class to simulate a voltmeter was taken from:
    http://greg.czerniak.info/guides/kalman1/
'''

class Voltmeter:
    def __init__(self, _truevoltage, _noiselevel):
        self.truevoltage = _truevoltage
        self.noiselevel = _noiselevel
    def GetVoltage(self):
        return self.truevoltage
    def GetVoltageWithNoise(self):
        return random.gauss(self.GetVoltage(), self.noiselevel)

voltmeter = Voltmeter(1.25, 0.25)

random.seed(14)

#Simulate voltmeter readings for 100 time steps
measuredvoltage = []
truevoltage = []
for i in range(100):
    measured = voltmeter.GetVoltageWithNoise()
    measuredvoltage.append(measured)
    truevoltage.append(voltmeter.GetVoltage())

#Parameter set up
init_state_mean = np.array((measuredvoltage[0]))
init_state_cov = np.ones((1, 1))
obs_mat= np.matrix([1])
obs_cov = np.eye(1)
trans_mat = np.matrix([1])
trans_cov = np.eye(1)*1e-6

kfv = KalmanFilter(n_dim_obs=1, n_dim_state=1,
                   initial_state_mean=init_state_mean,
                   initial_state_covariance=init_state_cov,
                   observation_matrices=obs_mat,
                   observation_covariance=obs_cov,
                   transition_matrices=trans_mat,
                   transition_covariance=trans_cov)

observations = np.array((measuredvoltage)).reshape(100,1)
state_means_v, state_covs_v = kfv.filter(observations)

#Plot the change in each variable over time
times = range(len(measuredvoltage))
plt.plot(times, measuredvoltage, 'b--', label='Measured Voltage')
plt.plot(times, truevoltage, 'g--', label='True Voltage')
plt.plot(times, state_means_v, 'r--', label='Kalman Filter')
plt.title('Noisy Voltage Measurements Smoothed by Kalman Filter')
plt.legend(loc="upper left")
plt.show()

23-24 Bayesian Statistics and Markov Models/24_markov_models.py


import math
import numpy as np

from typing import Any

def alpha(idx: int) -> float:
    """
    Calculate alpha.

    Args:
        idx: index of position

    Returns:
        value
    """

    return 1 / (2 ** 0.5) if idx == 0 else 1

def g_function(u: int, v: int, g: np.ndarray) -> float:
    """
    Apply Discrete Cosine.

    Args:
        u: index of row in image
        v: index of column in image
        g: image size 8x8

    Returns:
        value
    """

    sigma = 0
    for x in range(8):
        for y in range(8):
            sigma += g[x, y] * math.cos((2*x + 1) * u * math.pi/16)\
                             * math.cos((2*y + 1) * v * math.pi/16)
    return alpha(u) * alpha(v) * sigma / 4

def f_function(x: int, y: int, F: np.ndarray) -> float:
    """
    Apply Inverse Discrete Cosine.

    Args:
        x: index of row in image
        y: index of column in image
        F: image size 8x8 after apply DCT

    Returns:
        value
    """

    sigma = 0
    for u in range(8):
        for v in range(8):
            sigma += alpha(u) * alpha(v) \
                    * F[u, v] * math.cos((2*x + 1) * u * math.pi/16)\
                              * math.cos((2*y + 1) * v * math.pi/16)
    return sigma / 4

def apply_func(inp: np.ndarray, func: Any) -> np.ndarray:
    """
    Apply function

    Args:
        inp: input 8x8
        func: function to apply

    Returns:
        result shape 8x8
    """

    res = []

    for i in range(8):
        for j in range(8):
            res.append(func(i, j, inp))

    res = np.array(res).reshape((8, 8))
    return res

def dct_encode(inp: np.ndarray) -> np.ndarray:
    """
    Apply dct encoder

    Args:
        inp: input 8x8

    Returns:
        result
    """

    # TODO: can speed up with parallel or vectorize
    return apply_func(inp, g_function)

def dct_decode(inp: np.ndarray) -> np.ndarray:
    """
    Apply inverse dct

    Args:
        inp: input 8x8

    Returns:
        result
    """

    # TODO: can speed up with parallel or vectorize
    return apply_func(inp, f_function)


# Importar as seguintes bibliotecas
import sympy as sp
import numpy as np
# Definir a letra x como parametro
x = sp.Symbol('x')
##-------- Início dos métodos auxiliares --------##
# Método para construção do numerador
def padeExponencialNumerador( grauDoNumerador, grauDoDenominador ):
    # definir parâmetros e objetos auxiliares
    Numerador = sp.Function('Numerador')
    N = sp.Matrix(np.zeros( ( 1, grauDoNumerador + 1 ) ))
    # calcular os coeficientes
    for i in range( 0, grauDoNumerador + 1 ):
        numerador = sp.factorial( grauDoNumerador + grauDoDenominador - i ) * sp.factorial( grauDoNumerador )
        denominador = sp.factorial( grauDoNumerador + grauDoDenominador ) * sp.factorial( i ) * sp.factorial( grauDoNumerador - i )
        N[i] = (numerador / denominador ) * x**i
    # somar os monómios -> polinómio
    Numerador = sum( N )
    return ( Numerador )

# Método para construção do denominador
def padeExponencialDenominador( grauDoNumerador, grauDoDenominador ):
    # definir parâmetros e objectos auxiliares
    x = sp.Symbol('x')
    Denominador = sp.Function('Denominador')
    D = sp.Matrix(np.zeros( ( 1, grauDoDenominador + 1 ) ))
    # calcular os coeficientes
    for i in range( 0, grauDoDenominador + 1 ):
        numerador = sp.factorial( grauDoNumerador + grauDoDenominador - i ) * sp.factorial( grauDoDenominador )
        denominador = sp.factorial( grauDoNumerador + grauDoDenominador ) * sp.factorial(i) * sp.factorial( grauDoDenominador - i )
        D[i] = (numerador / denominador ) *  ( (-1)**(i) ) * x**i
    # somar os monómios -> polinómio
    Denominador = sum(D)
    return ( Denominador )
##-------- Fim dos métodos auxiliares --------##
# Método direto para construção de um aproximante de Padé da função exponencial
def padeExponencial( grauDoNumerador, grauDoDenominador):
    #Aproximante de Padé
    Numerador = padeExponencialNumerador( grauDoNumerador, grauDoDenominador)
    Denominador = padeExponencialDenominador( grauDoNumerador, grauDoDenominador)
    Pade = Numerador / Denominador
    return( Pade )


'''
Timeseries operation
Conform to HEC-convention.
'''
from scipy import stats
import numpy as np
import pandas as pd

def resample_hec_style(df, interval='D'):
    '''
    Resampling of time series in DataFrame provided for the interval (see Pandas resample for codes)
    In addition to conform to HEC-conventions the resampling is done with closed="right" and label="right"
    see pandas resample documentation to understand these arguments
    '''
    return df.resample(interval, closed='right', label='right')

def per_aver(df, interval='D'):
    '''
    PermissionErroriod averages of the time series in DataFrame provided for the interval (see Pandas resample for codes)
    In addition to conform to HEC-conventions the resampling is done with closed="right" and label="right"
    '''
    return resample_hec_style(df, interval).mean()

def per_max(df, interval='D'):
    return resample_hec_style(df, interval).max()

def per_min(df, interval='D'):
    return resample_hec_style(df, interval).min()

def mse(series1: pd.Series, series2: pd.Series):
    """Mean Squared Error (MSE)

    Args:

        series1 (Series or single value):
        series2 (Series or single value):

    Returns:

        Mean squared error
    """
    diff = series1-series2
    diff2=diff*diff
    return diff2.mean()

def rmse(series1, series2):
    return np.sqrt(mse(series1, series2))

def mean_error(series1, series2):
    return (series1-series2).mean()

def nash_sutcliffe(series1, series2):
    """https://en.wikipedia.org/wiki/Nash%E2%80%93Sutcliffe_model_efficiency_coefficient

    Args:

        series1 (Series): Can be considered as model in the URL above
        series2 (Series): Can be considered the observed in the URL above

    Returns:

        Nash Sutcliffe Efficiency
    """
    return 1-mse(series1, series2)/mse(series2, series2.mean())

def percent_bias(series1, series2):
    """Percent bias (PBIAS) measures the average tendency
    of the simulated values to be larger or smaller than their observed ones.

    https://rdrr.io/cran/hydroGOF/man/pbias.html#:~:text=Percent%20bias%20(PBIAS)%20measures%20the,values%20indicating%20accurate%20model%20simulation

    Args:

        series1 (Series): can be considered as model
        series2 (Series): can be considered as observed or target

    Returns:

        float : percent bias
    """
    return 100*((series1.sum()-series2.sum())/series2.sum())

def linregress(xseries, yseries):
    return stats.linregress(xseries, yseries)

def rsr(series1, series2):
    """
    Ratio of the RMSE to the standard deviation of the observed time series

    Args:

        series1 (Series): can be considered as model
        series2 (Series): can be considered as observed or target

    Returns:

        float : RSR
    """
    return rmse(series1, series2) / stats.tstd(series2)


import numpy as np
from math import *

n = 10**5
X = np.random.uniform(0,1,n)
Y = np.random.uniform(0,1,n)

def f(x):
    result = (1/sqrt(2*pi))*(exp(-(x**2)/2))
    return result

count =0
for i in range(n):
    if Y[i]<=f(X[i]):
        count+=1

print(count/n)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 30/06/2020 21:12
# @Author  : Ziye Yang
# @Purpose : Collections of useful algorithms

import numpy as np
import math

def calculate_return_rate(net_values):
    """

    Args:
        net_values: net values of fund as a list

    Returns: return_rate

    """
    return_rate = []
    for i in range(len(net_values) - 1):
        return_rate.append((net_values[i] - net_values[i + 1]) / net_values[i+1])
    return return_rate

def max_draw_down(net_values, time_window):
    """

    Args:
        net_values: net values of fund as a list
        time_window: how many days ago from today

    Returns: max draw down in time window

    """
    net_values = net_values[:time_window]
    return ((np.maximum.accumulate(net_values) - net_values) / np.maximum.accumulate(net_values)).max()

def sharp_ratio(net_values):
    """

    Args:
        net_values: net values of fund as a list

    Returns: daily_sharp_ratio, annual_sharp_ratio

    """
    annual_no_risk_return = 0.03
    daily_no_risk_return = (1 + annual_no_risk_return) ** (1.0 / 365) - 1
    return_rates = calculate_return_rate(net_values)
    return_rates_mean = np.mean(return_rates)
    return_rates_std = np.std(return_rates)

    daily_sharp_ratio = (return_rates_mean - daily_no_risk_return) / return_rates_std
    annual_sharp_ratio = daily_sharp_ratio * math.sqrt(len(net_values))

    return daily_sharp_ratio, annual_sharp_ratio


import matplotlib.pyplot as plt
import seaborn as sns
from data import DataCsv
import numpy as np

def mean(X: list) -> float:
    '''
    Computes the mean of a list

    Parameters
    ----------
    X : list

    Returns
    -------
    float
        Mean of X.

    '''
    return np.round(sum(X) / len(X), 4)

def mode(X: list) -> float:
    '''
    Computes the mode of a list

    Parameters
    ----------
    X : list

    Returns
    -------
    Mode of X.

    '''
    return max(set(X), key=list(X).count)

def modeWithFrequency(X: list, f: list) :
    '''
    Computes the mode of a list based of frequency values

    Parameters
    ----------
    X : list
        List of values.
    f : list
        The respective frequencies.

    Returns
    -------
    Mode of X.

    '''
    return X[f.index(max(f))]

def median(X: list) -> float:
    '''
    Computes the median of X.

    Parameters
    ----------
    X : list

    Returns
    -------
    float
        Median of X.

    '''
    X_sorted = sorted(X)
    n = len(X)
    if n % 2 != 0:
        return X_sorted[(n - 1) // 2]
    return (X_sorted[(n - 1) // 2] + X_sorted[((n - 1) // 2) + 1]) / 2

def variance(X: list) -> float:
    '''
    Computes variance of X (sample variance).

    Parameters
    ----------
    X : list

    Returns
    -------
    float
        Variance of X.

    '''
    return np.round(sum([(x - mean(X))**2 for x in X]) / (len(X) - 1), 4)

def std(X: list) -> float:
    '''
    Computes standard deviation of X (sample std).

    Parameters
    ----------
    X : list
        DESCRIPTION.

    Returns
    -------
    float
        Standard deviation of X.

    '''
    return np.round(variance(X)**0.5, 4)

def cov(X: list, Y: list) -> float:
    '''
    Computes the covariance of X and Y.

    Parameters
    ----------
    X : list

    Y : list

    Returns
    -------
    float
        Covariance of X and Y.

    '''
    mx, my = mean(X), mean(Y)
    return np.round(sum([(i - mx) * (j - my) for i, j in zip(X, Y)]) / (len(X) - 1), 4)

def corr(X: list, Y: list) -> float:
    '''
    Computes the correlation of X and Y.

    Parameters
    ----------
    X : list

    Y : list

    Returns
    -------
    float
        Correlation of X and Y.

    '''
    stdx, stdy = std(X), std(Y)
    covxy = cov(X, Y)
    return np.round(covxy / (stdx * stdy), 4)

def plotHistogram(X: DataCsv, name: str):
    '''
    Plots histogram of column "name"

    Parameters
    ----------
    X : DataCsv
        The dataset.
    name : str
        The column.

    Returns
    -------
    None.

    '''
    data = X[name]
    sorted(data)
    x1 = data[:len(data) // 2]
    x2 = data[len(data) // 2:]
    IQR = (median(x2) - median(x1))
    h = 2 * IQR / (len(data)**(1 / 3))
    n = (max(data) - min(data)) // h
    figure, axis = plt.subplots()
    axis.set_title(name)
    plt.hist(data, bins = int(n))
    plt.show()

def plotScatter(X: list, Y: list, title: str= None, labels: list= None):
    '''
    Plots scatterplot of X and Y.

    Parameters
    ----------
    X : list

    Y : list

    title : str, optional
        Title for the plot. The default is None.
    labels : list, optional
        Labels for the x and y axis. The default is None.

    Returns
    -------
    None.

    '''
    if title:
        figure, axis = plt.subplots()
        axis.set_title(title)
    if labels:
        axis.set_xlabel(labels[0])
        axis.set_ylabel(labels[1])
    plt.scatter(X, Y)
    plt.show()

def plotBox(X: list, title: str= None):
    '''
    Plots box plot of X.

    Parameters
    ----------
    X : list

    title : str, optional
        Title for the plot. The default is None.

    Returns
    -------
    None.

    '''
    if title:
        figure, axis = plt.subplots()
        axis.set_title(title)
    plt.boxplot(X)
    plt.show()

def plotBar(X: list, Y: list, title: str= None):
    '''
    Plots bar plot using X and Y.

    Parameters
    ----------
    X : list
        The classes.
    Y : list
        The heights.
    title : str, optional
        Title for the plot. The default is None.

    Returns
    -------
    None.

    '''
    if title:
        figure, axis = plt.subplots()
        axis.set_title(title)
    plt.bar(X, Y)
    plt.show()

def plotCorr(data: DataCsv):
    '''
    Plots correlation matrix for the dataset

    Parameters
    ----------
    data : DataCsv

    Returns
    -------
    None.

    '''
    corr_matrix = [[corr(data[i], data[j]) for i in data.header] for j in data.header]
    sns.heatmap(corr_matrix, annot=True, xticklabels=data.header, yticklabels=data.header)

def confidenceIntMeanZ(sample_mean: float, n: int, std: float, CL: float= 0.95) -> tuple:
    '''
    Calculates 0.9, 0.95 or 0.99 confidence interval for the sample mean.

    Parameters
    ----------
    sample_mean : float

    n : int
        Number of observations.
    std : float
        Population standard deviation.
    CL : float, optional
        Confidence level. The default is 0.95.

    Returns
    -------
    tuple
        Margin of error and interval limits.

    '''
    margin = z_score(CL) * (std / (np.sqrt(n)))
    return margin, [sample_mean - margin, sample_mean + margin]

def z_score(CL: float) -> float:
    '''

    Parameters
    ----------
    CL : float
        Confidence level.

    Raises
    ------
    Exception
        When CL has an unexpected value.

    Returns
    -------
    float
        Z score for the given CL.

    '''
    cls_ = {0.90: 1.645, 0.95: 1.96, 0.99: 2.58}
    if not CL in cls_:
        raise Exception("Use one of the following for C.L.: 0.9, 0.95, 0.99")
    return cls_[CL]

def confidenceIntProportion(p: float, n: int, CL: float) -> tuple:
    '''
    Calculates 0.9, 0.95 or 0.99 confidence interval for the sample proportion.

    Parameters
    ----------
    p : float
        Sample proportion.
    n : int
        Number of observations.
    CL : float
        Confidence level.

    Returns
    -------
    tuple
        Margin of error and interval limits.

    '''
    margin = z_score(CL) * np.sqrt((p * (1- p))/ n)
    return margin, [p - margin, p + margin]


#!/usr/bin/env python3

import numpy as np
import matplotlib.pyplot as plt
import cvxpy as cvx

#f = np.array( [[1, 4]] )
f = np.array( [[1], [4]] )
H = np.array( [[1,0],[0,4]])
A = np.array( [[-2, -4],[1, 5 ],[2, 2]] )
b = np.array( [[-35],[50],[40]] )
lb = np.array( [[0],[0]] )

x = cvx.Variable( 2,1 )
objective = cvx.Minimize( cvx.quad_form(x, H) + f.T*x )
constraints = [A*x <=b, lb <=x]

prob = cvx.Problem(objective, constraints)
result = prob.solve()

print( x.value )
print( result )


import numpy as np
from scipy.io import loadmat # for loading MATLAB files
import pandas as pd

def costfunction(Theta1, Theta2, X_with_bias, y_one_hot, lambda_param):

    # Feedforward
    a1 = X_with_bias # 5000x401

    z2 = Theta1.dot(a1.T)  # 25x401 * 401x5000 = 25x5000
    a2 = np.c_[np.ones((X_with_bias.shape[0], 1)), sigmoid(z2.T)]  # 5000x26

    z3 = Theta2.dot(a2.T)  # 10x26 * 26x5000 = 10x5000
    a3 = sigmoid(z3)  # 10x5000

    # Compute the cost
    number_of_examples = X_with_bias.shape[0]

    J = -1 * (1 / number_of_examples) * np.sum((np.log(a3.T) * (y_one_hot) + np.log(1 - a3).T * (1 - y_one_hot))) + \
        (lambda_param/(2 * number_of_examples))*(np.sum(np.square(Theta1[:,1:])) + np.sum(np.square(Theta2[:,1:])))

    # Compute the gradients
    delta3 = a3.T - y_one_hot  # 5000x10
    delta2 = Theta2[:, 1:].T.dot(delta3.T) * sigmoidGradient(z2)  # 25x10 *10x5000 * 25x5000 = 25x5000

    big_delta1 = delta2.dot(a1)  # 25x5000 * 5000x401 = 25x401
    big_delta2 = delta3.T.dot(a2)  # 10x5000 *5000x26 = 10x26

    # DON'T regularize the fist column of Theta1 and Theta2
    Theta1_ = np.c_[np.ones((Theta1.shape[0], 1)), Theta1[:, 1:]]
    Theta2_ = np.c_[np.ones((Theta2.shape[0], 1)), Theta2[:, 1:]]

    theta1_grad = # Put your code here
    theta2_grad = # Put your code here

    return J, theta1_grad, theta2_grad

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

def sigmoidGradient(z):
    return sigmoid(z) * (1 - sigmoid(z))

'''
Step 0: Do the following steps:
1. load the training dataset from 'data/digits.mat' and store as (X, y)
2. load the provided weights from 'data/weights.mat' and store as Theta1, Theta2
'''
dataset = loadmat('data/digits.mat')
print(dataset.keys())

weights = loadmat('data/weights.mat')
print(weights.keys())

X, y = dataset['X'], dataset['y']
print('X: {}'.format(X.shape))
print('y: {}'.format(y.shape))

Theta1, Theta2 = weights['Theta1'], weights['Theta2']
print('Theta1: {}'.format(Theta1.shape))
print('Theta2: {}'.format(Theta2.shape))

'''
Step 1: Augment the original matrix X with 1 (the intercept term)
'''

# Add constant for intercept
X_new = np.c_[np.ones((dataset['X'].shape[0], 1)), dataset['X']]
print('X_new (with intercept): {}'.format(X_new.shape), '\n')

'''
Step 2: Convert the original labels to one-hot vectors
'''
y_one_hot = pd.get_dummies(y.ravel()).values

'''
Step 3: Compute the cost function with lambda = 1
'''
print('The cost (without regularization) is', costfunction(Theta1, Theta2, X_new, y_one_hot, 1)[0], '\n')

'''
Step 4: Test the implemented sigmoidGradient.
For large values (both positive and negative), the gradient should be close to 0.
Furthermore, when z = 0, the gradient should be exactly 0.25.
'''
print('Compute the sigmoidGradient for -1, -0.5, 0, 0.5, 1 :', '\n', [sigmoidGradient(z) for z in [-1, -0.5, 0, 0.5, 1]])


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

## Machine Learning Online Class
#  Exercise 1: Linear regression with multiple variables

def featureNormalize(X):
    mean = np.mean(X, 0)
    sigma = np.std(X, 0)
    X_norm = (X - mean) / sigma
    return (X_norm, mean, sigma)

def computeCostMulti(X, y, theta):
    error = (X @ theta - y)
    return (error.T @ error)/ (2*y.size)

def gradientDescentMulti(X, y, theta, alpha, num_iters):
    m = y.size
    J_history = np.zeros(num_iters)
    for iter in range(num_iters):
        theta -= alpha/m * X.T @ (X @ theta - y)
        J_history[iter] = computeCostMulti(X, y, theta)
    return (theta, J_history)

def normalEqn(X,y):
    return np.linalg.pinv(X.T @ X) @ X.T @ y

# ================ Part 1: Feature Normalization ================

print('Loading data ...')
# Load Data
scriptdir = os.path.dirname(os.path.realpath(__file__))
data = np.loadtxt(scriptdir + '//ex1data2.txt', delimiter=',')
X = data[:,:-1]
y = data[:,-1]

# Print out some data points
print('First 10 examples from the dataset: X')
for i in range(10):
    print(f'x = {X[i,:]}, y = {y[i]}')
input('Program paused. Press enter to continue.\n')

# Scale features and set them to zero mean
print('Normalizing Features ...')
X, mean, sigma = featureNormalize(X)
##Add intercept term to X
X = np.c_[np.ones(y.size), X]

# ================ Part 2: Gradient Descent ================
print('Running gradient descent ...')
alpha = [0.01, 0.03, 0.10, 0.3, 1, 1.25]
for i in range(len(alpha)):
    #Choose some alpha value
    num_iters = 400

    #Init Theta and Run Gradient Descent
    theta = np.zeros(3)
    theta, J_history = gradientDescentMulti(X, y, theta, alpha[i], num_iters)

    #Plot the convergence graph
    plt.plot(J_history, lw=2, label=f'Cost J for alpha {alpha[i]}')

    #Display gradient descent's result
    print(f'Theta computed from gradient descent with alpha {alpha[i]}')
    print(f'{theta}\n')
plt.xlabel('Number of iterations')
plt.ylabel('Cost J')
plt.legend()
plt.show()

# Estimate the price of a 1650 sq-ft, 3 br house
house = [1650, 3]
house_norm = ((house - mean) / sigma)
price = np.append(1, house_norm) @ theta
print(f'Predicted price of a 1650 sq-ft, 3 br house (using gradient descent):\n ${price}')
input('Program paused. Press enter to continue.\n')

# ================ Part 3: Normal Equations ================

print('Solving with normal equations...')

X = data[:,:-1]
y = data[:,-1]

#Add intercept term to X
X = np.c_[np.ones(y.size), X]

# Calculate the parameters from the normal equation
theta = normalEqn(X, y)

# Display normal equation's result
print('Theta computed from the normal equations:')
print(theta)
input('Program paused. Press enter to continue.\n')

# Estimate the price of a 1650 sq-ft, 3 br house
house = [1650, 3]
price = np.append(1, house) @ theta
print(f'Predicted price of a 1650 sq-ft, 3 br house (using normal equations):\n ${price}')


# -*- coding: utf-8 -*-
import numpy as np
from scipy.optimize import leastsq
import pylab as pl

def func(x, p):
    """
    数据拟合所用的函数: A*sin(2*pi*k*x + theta)
    """
    A, k, theta = p
    return A*np.sin(2*np.pi*k*x+theta)

def residuals(p, y, x):
    """
    实验数据x, y和拟合函数之间的差，p为拟合需要找到的系数
    """
    return y - func(x, p)

x = np.linspace(0, -2*np.pi, 100)
A, k, theta = 10, 0.34, np.pi/6 # 真实数据的函数参数
y0 = func(x, [A, k, theta]) # 真实数据
y1 = y0 + 2 * np.random.randn(len(x)) # 加入噪声之后的实验数据

p0 = [7, 0.2, 0] # 第一次猜测的函数拟合参数

# 调用leastsq进行数据拟合
# residuals为计算误差的函数
# p0为拟合参数的初始值
# args为需要拟合的实验数据
plsq = leastsq(residuals, p0, args=(y1, x))

print u"real parameter:", [A, k, theta]
print u"fitted parameter", plsq[0] # 实验数据拟合后的参数

pl.plot(x, y0, label=u"real data")
pl.plot(x, y1, label=u"noisy data")
pl.plot(x, func(x, plsq[0]), label=u"fitted data")
pl.legend()
pl.show()


"""
This module illustrate how to find the roots of an equation using
the Newton-Rapshon and secant methods available in the Scipy lib.
"""
from scipy import optimize
from math import exp

def f(x):
    """Represents a mathematical function called f"""
    return x*(x - 2)*exp(3 - x)

def fp(x):
    """Represents the first derivative of f"""
    return -(x**2 - 4*x + 2)*exp(3 - x)

roots_secant = optimize.newton(f, 1., x1=1.5) # Using x1 = 1.5 and the secant
roots_newton = optimize.newton(f, 1., fprime=fp) # Using Newton-Rapshon method
roots_bisect = optimize.bisect(f, 1., 3) # Using bisect
roots_brent  = optimize.brentq(f, 1., 3.) # Using Brent

print(roots_secant)
print(roots_newton)
print(roots_bisect)
print(roots_brent)


from src.functions.objectivefn import ObjectiveFn
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

class HimmelbauFn(ObjectiveFn):

    def __init__(self):
        super(HimmelbauFn, self).__init__(2)
        self.domain = (-5.0, 5.0)

    def get_minima(self):
        # minima = (3.0, 2.0)             # Minima point 1
        # minima = (-2.805118, 3.131312)  # Minima point 2
        # minima = (-3.779310, -3.283186) # Minima point 3
        minima = (3.584428, -1.848126)  # Minima point 4
        return self.evaluate(minima)  # minima value is 0 at the 4 global minimas

    def evaluate(self, params):
        f = 0
        try:
            if len(params) != self.dims:
                raise Exception('Dimensions not matching params')
            X = params[0]
            Y = params[1]
            f = (X ** 2 + Y - 11) ** 2 + (X + Y ** 2 - 7) ** 2
            return f
        except Exception as error:
            print('Exception in sphere_fn.eval_fn: ', repr(error))

    def graph_fn(self):
        # X = np.linspace(-10, 10, 100)
        # Y = np.linspace(-10, 10, 100)
        X = np.linspace(self.domain[0], self.domain[1], 200)
        Y = np.linspace(self.domain[0], self.domain[1], 200)
        X, Y = np.meshgrid(X, Y)
        Z = (X ** 2 + Y - 11) ** 2 + (X + Y ** 2 - 7) ** 2

        fig = plt.figure()
        ax = fig.gca(projection='3d')
        ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.plasma, linewidth=0, antialiased=False)
        plt.savefig('himmelbau.png')

    def contour_plot(self, save_file_name, points):
        # X = np.linspace(-10, 10, 100)
        # Y = np.linspace(-10, 10, 100)
        # X = np.linspace(-5, 5, 200)
        # Y = np.linspace(-5, 5, 200)
        X = np.linspace(self.domain[0], self.domain[1], 100)
        Y = np.linspace(self.domain[0], self.domain[1], 100)
        X, Y = np.meshgrid(X, Y)
        Z = (X ** 2 + Y - 11) ** 2 + (X + Y ** 2 - 7) ** 2

        plt.contour(X, Y, Z)
        for point in points:
            plt.scatter(point[0], point[1], marker='X', color='red')
        plt.savefig(save_file_name, dpi=300, bbox_inches='tight')
        plt.close()

    def is_defined_only_for_2d(self):
        return True

    def name(self):
        return "HimmelbauFn"


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

# # Approximating functions on $R$
#
# **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: **demapp01.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
#
# This demo illustrates how to use CompEcon Toolbox routines to construct and operate with an approximant for a function defined on an interval of the real line.
#
# In particular, we construct an approximant for $f(x)=\exp(-x)$ on the interval $[-1,1]$.  The function used in this illustration posseses a closed-form, which will allow us to measure approximation error precisely. Of course, in practical applications, the function to be approximated will not possess a known closed-form.
#
# In order to carry out the exercise, one must first code the function to be approximated at arbitrary points.
# Let's begin:

# ## Initial tasks

# In[1]:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from compecon import BasisChebyshev, BasisSpline, demo

# ### Defining the functions

# Function to be approximated and derivatives

# In[2]:

def f(x): return np.exp(-x)
def d1(x): return -np.exp(-x)
def d2(x): return np.exp(-x)

# Set degree and domain of interpolation

# In[3]:

n, a, b = 10, -1, 1

# ## Chebyshev interpolation

# In this case, let us use an order 10 Chebychev approximation scheme:

# In[4]:

F = BasisChebyshev(n, a, b, f=f)

# One may now evaluate the approximant at any point x calling the basis:

# In[5]:

x = 0
F(x)

# ... one may also evaluate the approximant's first and second derivatives at x:

# In[6]:

print(f'1st derivative = {F(x, 1):.4f}, 2nd derivative = {F(x, 2):.4f}')

# ... and one may even evaluate the approximant's definite integral between the left endpoint a and x:

# In[7]:

F(x, -1)

# ###  Compare analytic and numerical computations

# In[8]:

pd.set_option('precision', 8)

pd.DataFrame({
    'Numerical': [F(x), F(x, 1), F(x,2), F(x, -1)],
    'Analytic': [f(x), d1(x), d2(x), np.exp(1)-1]},
    index = ['Function', 'First Derivative', 'Second Derivative', 'Definite Integral']
)

# ### Plots of approximation errors

# One may evaluate the accuracy of the Chebychev polynomial approximant by computing the approximation error on a highly refined grid of points:

# In[9]:

nplot = 501  # number of grid nodes
xgrid = np.linspace(a, b, nplot)    # generate refined grid for plotting

figures=[]  # to save all figures

def approx_error(true_func, appr_func, d=0, title=''):
    fig, ax = plt.subplots()
    ax.hlines(0,a,b, 'k', linestyle='--',linewidth=2)
    ax.plot(xgrid, appr_func(xgrid, d) - true_func(xgrid))
    ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
    ax.set(title=title, xlabel='x', ylabel='Error')

    figures.append(plt.gcf())

# ### Plot function approximation error

# In[10]:

approx_error(f,F,title='Chebychev Approximation Error - Function')

# The plot indicates that an order 10 Chebychev approximation scheme, produces approximation errors no bigger in magnitude than $6\times10^{-10}$. The approximation error exhibits the "Chebychev equioscillation property", oscilating relatively uniformly throughout the approximation domain.
#
# This commonly occurs when function being approximated is very smooth, as is the case here but should not be expected when the function is not smooth.  Further notice how the approximation error is exactly 0 at the approximation nodes --- which is true by contruction.

# ### Plot first derivative approximation error

# In[11]:

approx_error(d1,F,1, title='Chebychev Approximation Error - First Derivative')

# ### Plot second derivative approximation error

# In[12]:

approx_error(d2,F,2, title='Chebychev Approximation Error - Second Derivative')

# ## Cubic spline interpolation

# Let us repeat the approximation exercise, this time constructing a 21-function cubic spline approximant:

# In[13]:

n = 21                         # order of approximation
S = BasisSpline(n, a, b, f=f)  # define basis
yapp = S(xgrid)                # approximant values at grid nodes

# ### Plot function approximation error

# In[14]:

approx_error(f,S,title='Cubic Spline Approximation Error - Function')

# The plot indicates that an order 21 cubic spline approximation scheme produces approximation errors no bigger in magnitude than $1.2\times10^{-6}$, about four orders of magnitude worse than with Chebychev polynomials.

# ### Plot first derivative approximation error

# In[15]:

approx_error(d1,S,1, title='Cubic Spline Approximation Error - First Derivative')

# ### Plot second derivative approximation error

# In[16]:

approx_error(d2,S,2, title='Cubic Spline Approximation Error - Second Derivative')

# ## Linear spline interpolation

# Let us repeat the approximation exercise, this time constructing a 31-function linear spline approximant:

# In[17]:

n = 31
L = BasisSpline(n, a, b, k=1, f=f)

# ### Plot function approximation error

# In[18]:

approx_error(f,L,title='Linear Spline Approximation Error - Function')

# The plot indicates that an order 21 cubic spline approximation scheme produces approximation errors no bigger in magnitude than $1.2\times10^{-6}$, about four orders of magnitude worse than with Chebychev polynomials.

# ### Save all figures to disc

# In[19]:

#demo.savefig(figures,name="demapp01")

_build/jupyter_execute/notebooks/app/01 Approximating function on R.py


# -*- coding: utf-8 -*-
"""
chebpy.chebq
============

Chebyshev quadrature.

"""

import numpy as np
from numpy.fft import ifft

__all__ = ['cheb_quadrature_cgl',
           'clencurt_weights',
           'clencurt_weights_fft',
           'cheb_quadrature_clencurt',
          ]

def cheb_quadrature_cgl(f):
    '''
    Chebyshev-Gauss 2nd kind quadrature.
    \integrate[-1,+1] sqrt(1-x^2) g(x) dx ~ \sum_i{1}{N-1} w_i * g(x_i)
    on
        x_i = cos(i*pi/N), i = 0, 1, 2, ..., N
    with
        w_i = (pi/N) * (1 - x_i^2), i = 1, 2, ..., N-1

    Note: from numeric experiments, this method is much less accurate than
    Clenshaw-Curtis quadrature.
    '''

    N = np.size(f) - 1
    theta = np.arange(N+1) * np.pi / N
    x = np.cos(theta)
    w = np.zeros_like(x)
    w += np.pi / N
    #w = np.pi/N
    w[0] *= .5
    w[N] *= .5
    #return np.dot(w[1:N], f[1:N] * np.sqrt(1-x[1:N]**2))
    return np.dot(w, f * np.sqrt(1-x**2))
    #return np.dot(w[1:N], f[1:N])

def clencurt_weights(N):
    '''
    The clenshaw-Curtis quadrature weights are:
        w_n = (4/(r_n*N)) * \sum_{k=0,even}{N} (1/r_k * cos(pi*k*n/N) /
                (1 - k^2), n = 0, 1, 2, ..., N
    Thus, w_0 = w_N, and w_0 = 1 / (N^2 - 1) for even N, w_0 = 1 / N^2
    for odd N.

    Ref. Trefethen LN, 2000, p128.
    '''

    theta = np.arange(N+1) * np.pi / N
    x = np.cos(theta)
    w = np.zeros_like(x)
    ii = np.arange(1,N)
    v = np.ones(N-1)
    if N % 2 == 0:
        w[0] = 1. / (N**2 - 1)
        w[N] = w[0]
        for k in xrange(1,N/2):
            v -= 2. * np.cos(2*k*theta[ii]) / (4*k**2 - 1.)
        v -= np.cos(N*theta[ii]) / (N**2 - 1.)
    else:
        w[0] = 1. / N**2
        w[N] = w[0]
        for k in xrange(1,(N-1)/2+1):
            v -= 2. * np.cos(2*k*theta[ii]) / (4*k**2 - 1.)
    w[ii] = 2 * v / N
    return w

def clencurt_weights_fft(N):
    '''
    Compute the (N+1) nodes and weights for Clenshaw-Curtis quadrature
    on the interval [-1, 1]. Using FFT algorithm, the weights and nodes are
    computed in linear time.
    '''

    c = np.zeros(N+1)
    if N % 2 == 0:
        c[0:N+2:2] = 2. / (1. - np.arange(0, N+2, 2)**2)
    else:
        c[0:N:2] = 2. / (1. - np.arange(0, N, 2)**2)
    c = np.concatenate((c, np.flipud(c[1:N])))

    v = np.real(ifft(c))

    w = 2. * v[0:N+1]
    w[0] *= .5
    w[N] *= .5

    return w

def cheb_quadrature_clencurt(f, w=None):
    '''
    Integrate f in the range [-1, 1] by Clenshaw-Curtis quadrature.
    '''
    N = np.size(f) - 1
    if w is None:
        w = clencurt_weights_fft(N)
    return np.dot(w, f)


import numpy as np
import math

class Impedance:
    """
    Used in ex_2_3.
    """

    def __init__(self, R, X):
        """
        Impedance constructor.

        :param R: Resistance [Ohm].
        :param X: Reactance [Ohm].
        """
        self.R = R
        self.X = X
        self.Z = complex(R, X)  # representation as a complex number.
        self.mag = np.abs(self.Z)  # Magnitude.
        self.ang = np.angle(self.Z)  # Angle in rad.

    @staticmethod
    def from_polar(mag, ang):
        """
        Use this if you want to build an impedance from its magnitude - angle description.
        :param mag: Magnitude [Ohm].
        :param ang: Angle [rad].
        :return: An Impedance object.
        """
        return Impedance(mag * np.cos(ang), mag * np.sin(ang))

    def __add__(self, other):
        """
        Override addition.

        :param other: Another impedance.
        :return: The sum of this and the other impedance.
        """
        return Impedance(self.R + other.R, self.X + other.X)

    def __mul__(self, other):
        """
        Override multiplication

        :param other: Another impedance.
        :return: The product of this and the other impedance.
        """
        # Could multiply the Z simply, but to show the details.
        mag = self.mag * other.mag
        ang = self.ang + other.ang
        return self.from_polar(mag, ang)

    def __truediv__(self, other):
        """
        Override (true) division

        :param other: Another impedance.
        :return: The quotient of this and the other impedance.
        """
        # Could devide the Z simply, but to show the details.
        mag = self.mag / other.mag
        ang = self.ang - other.ang
        return self.from_polar(mag, ang)

    def parallel(self, other):
        """
        Compute the equivalent impedance of this impedance in parallel with another.

        :param other: Another impedance.
        :return: The equivalent impedance.
        """
        num = self * other
        den = self + other
        return num / den

    def tex_cart(self):
        """
        Print impedance in latex format, R+jX style.
        """
        print("$ %.2f+j%.2f $" % (self.R, self.X))

    def tex_polar(self):
        """
        Print impedance in latex format, polar style.
        """
        print("$ %.2f \\angle %.2f $" % (self.mag, np.rad2deg(self.ang)))

    def __str__(self):
        return '%.2f < %.2f deg' % (self.mag, self.ang*180/math.pi)


import numpy as np
''' Look up: https://docs.scipy.org/doc/numpy/reference/routines.random.html '''

def random_integer():
	np.random.seed(1337)

	d = np.random.randint(10, 20, size = (2, 3)) # integers in [low, high) as 2D array

	print(d)
	print('*' * 50)
	return d

def all_info(input_array):
	shape = input_array.shape
	print('Shape:', shape)
	print('Number of objects in the array:', input_array.size)
	print('Type:', input_array.dtype)
	print('Dimentionality:', len(shape))
	print('The array has', shape[0] , 'rows.')
	print('The array has', shape[1] , 'columns.')
	print('*' * 50)

def sum_all_elements(array):
	print('Sum of all elements: ', array.sum())
	return array.sum()

def sum_specific_dir(d, dir):
	if dir == 0:
		sum_dir = d.sum(axis = dir)
		print('Iterated over rows')
		print('Sum of each column: ', sum_dir)
	if dir == 1:
		sum_dir = d.sum(axis = dir)
		print('Iterated over columns')
		print('Sum of each row: ', sum_dir)

	return sum_dir

def find_min(array):
	minimum = array.min()
	return minimum

def find_max(array):
	maximum = array.min()
	return maximum

def find_mean(array):
	mean_value = array.mean()
	return mean_value

if __name__ == "__main__":

	print("Random integers")
	d = random_integer()
	all_info(d)
	sum_of_all = sum_all_elements(d)

	sum_column = sum_specific_dir(d, dir = 0) # Iteration over rows gives sum of for each column
	min_sum_columns = find_min(sum_column)
	print('Minimum value of the sums for columns:', min_sum_columns)

	sum_rows = sum_specific_dir(d, dir = 1) # Iteration over columns gives sum of for each rows
	max_sum_rows = find_max(sum_rows)
	print('Max value of the sums for rows:',max_sum_rows)

	print('Mean value of all values in the array:', find_mean(d))


# Copyright 2021 CR.Sparse Development Team
#
# 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
#
#     https://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.

from jax import jit, grad, lax

import jax.numpy as jnp
import cr.nimble as cnb

from .smooth import build2

def smooth_huber(tau=1.):
    r"""Huber penalty function and its gradient
    """
    tau = jnp.asarray(tau)
    tau = cnb.promote_arg_dtypes(tau)

    @jit
    def func(x):
        x = jnp.asarray(x)
        x = cnb.promote_arg_dtypes(x)
        x_mag = jnp.abs(x)

        small = x_mag <= tau
        x_small = 0.5*(x_mag**2)/tau
        x_large = x_mag - tau/2

        v = jnp.where(small, x_small, x_large)
        return sum(v)

    @jit
    def gradient(x):
        x = jnp.asarray(x)
        x = cnb.promote_arg_dtypes(x)
        g = x/jnp.maximum(tau, jnp.abs(x) )
        return g

    return build2(func, gradient)


from numpy import dot, zeros
from scipy import tril, triu, exp
from numpy.linalg import norm
from scipy.linalg import solve
from pylab import plot, clf, axis, legend, grid, loglog

#Perform newton's method with multiple equations
def newton_multi(F, J, x0, f, dfdy, tn, yn, dt, c, a, tol = 1e-8):

  #start with initial guess
  x_old = x0

  #Loop until break condition met
  while 1:
    #Find Jacobian
    M = J(dfdy, tn, yn, dt, c, a, x_old)
    #Find RHS(-F)
    RHS = dot(-1 , F(f, tn, yn, dt, c, a, x_old))

    #Solve for Sigma
    sig = solve( M, RHS )

    #Get next x iteration value
    x_new = x_old + sig

    #Get error
    res = x_new - x_old
    res_norm = norm(res)

    #if error is small enough, exit loop
    if res_norm < tol: break

    #assign next iteration value to previous value
    x_old = x_new

  #return root
  return x_new

#Perform Runge-Kutta method for an input butcher's table
def runge_kutte_allorder(a, b, c, f, dfdy, T, dt, y0, exact_sol_available=False, exact_sol=0):

  #initialize GTE
  gte = 0

  # calculate number of iterations
  n = int(T/float(dt) + 0.5)

  #Calculate order
  s = len(b)

  #initialize variables
  k_n = zeros(s)

  # Use initial condition:
  t_list = [0]
  y_list = [y0]

  # Calculate approximations for all time steps:
  for i in range(n):

    #find k values
    k_n = newton_multi(function, Jacobian, k_n, f, dfdy, t_list[i], y_list[i], dt, c, a)

    #initialize temp sum
    tempSum = 0

    #calculate weighted sum of kis
    for j in range(s):

      tempSum += b[j]*k_n[j]

    #calculate y and append
    y_n_plus_one = y_list[i] + dt * tempSum
    y_list.append(y_n_plus_one)

    #Calculate time and append
    t_n_plus_one = t_list[i] + dt
    t_list.append(t_n_plus_one)

    #Find GTE if exact solution available
    if exact_sol_available:
      err_n = abs(exact_sol(t_n_plus_one) - y_n_plus_one)
      if err_n > gte: gte = err_n

  # Return the solution and error:
  return[t_list,y_list, gte]

#generates F vector for a RK method, where Fi = ki-f(tn+ci*h,yn+sum(aij*ki)
def function( f, tn, yn, dt, c, a, k ):

  #find RK order
  s = len( c )

  #initialize function array
  result = zeros( s )

  #loop through each column of result
  for i in range( s ):

    #initialize temp sum
    tempSum = 0

    #calculate weighed sum of kjs
    for j in range( s ):

      tempSum += a[i][j]*k[j]

    #calculate each column of F
    result[i] = k[i] - f(tn + c[i]*dt, yn + dt*tempSum)

  #return F
  return result

#generates jacobian for dFj/dkis
def Jacobian( dfdy, tn, yn, dt, c, a, k ):

  #find RK order
  s = len( c )

  #initialize Jacobian
  result = zeros( [s,s] )

  #loop through each row of Jacobian(kis)
  for i in range( s ):

    #initialize temp sum
    tempSum = 0

    #calculate weighted sum of kjs
    for j in range( s ):

      tempSum += a[i][j]*k[j]

    #loop through columns of Jacobian(Fis)
    for j in range( s ):

      #calculate each element of J
      result[i][j] = kdel(i,j)-dfdy(tn + c[i]*dt, yn + dt*tempSum)*dt*a[i][j]

  #return J
  return result

#evaluates kronecker delta function for an i and j
def kdel( i, j ):

  #if i = j, result is 1
  if (i == j):
    return 1

  #if i != j, result is 0
  else:
    return 0

#Example butcher's tables
'''
a = [[0.,0.],[0.,1.]]

b = [0.,1.]

c=[0.0,0.5]
'''
'''
a = [[1.]]
b = [1.]
c = [0.]
'''
a = [[0.1666667,-0.3333333,0.1666667],[0.1666667,0.4166667,-0.0833333],[0.1666667,0.6666667,0.1666667]]
b = [0.1666667,0.6666666,0.1666667]
c = [0.,0.5,1.]

#input derivative functions and its derivative
def f(t,y):
  return y*((1/(t+1))-1)

def dfdy(t,y):
  return ((1/(t+1))-1)

#final time
T = 5.

#time step(h)
dt = 0.1

#inital condition
y0 = 1.

#set exact solution available flag to true
exact_sol_available =True

def exact_sol(t):
  return (t+1)*exp(-t)

#run RK method
[t_list,y_list,error] = runge_kutte_allorder(a, b, c, f, dfdy, T, dt, y0, exact_sol_available, exact_sol)

#plot Result
clf()
axis('equal')
label = "Runge-Kutta Results of order"
plot(t_list, y_list, "b-", label=label)
if exact_sol_available:
  label = "Exact Solution"
  plot(t_list,[exact_sol(t) for t in t_list], "r-", label = label)
grid(True)
legend()
lab.show()

#if exact solution available, generate and plot error vs timestep
if exact_sol_available:

  gte=[]

  time_steps = [0.5, 0.4, 0.3, 0.2, 0.1, 0.05, 0.025, 0.0125]
  for dt in time_steps:
    [t_list, y_list, error] = runge_kutte_allorder(a, b, c, f, dfdy, T, dt, y0, exact_sol_available, exact_sol)
    gte.append(error)

  # Plot the solution:
  clf()
  label = "GTE as a function of dt"
  axis('equal')
  loglog(time_steps, gte, "b-", label=label)
  grid(True)
  legend()
  lab.show()


#imports
import numpy as np
import matplotlib.pyplot as plt

def est_coeff(x,y):
	m=np.size(x); #number of trainingexamples

	mean_x,mean_y =  np.mean(x),np.mean(y) #mean

	ss_xy = np.sum(y*x) - m*mean_y*mean_x #cross deviation
	ss_xx = np.sum(x*x) - m*mean_x*mean_x

	b1 = ss_xy/ss_xx #regression coefficient
	b0 = mean_y - b1*mean_x

	return(b0,b1)

def plot_regressionline(x,y,b):
	plt.scatter(x, y) #original data

	y= b[0] + b[1]*x #predicted data

	plt.plot(x, y, color = "g") #regression line

	plt.xlabel('x') #labels
	plt.ylabel('y')

	plt.show()
def main():

	x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])  #training data
	y = np.array([2, 3, 5, 6, 8, 9, 12, 14, 15, 16])

	b = est_coeff(x, y) #estimating coefficients
	print("Estimated coefficients:\nb0 = {} \
		\nb1 = {}".format(b[0], b[1]))

	plot_regressionline(x, y, b) #plotting regression line

if __name__ == "__main__":
	main()


##########################################################################
## PROBABILITY DISTRIBUTIONS                                            ##
##########################################################################

#Import general packages
import math
import numpy as np
import scipy as sp
#import random as rd
import matplotlib.pyplot as plt

##import scikits.timeseries as ts
##import scikits.timeseries.lib.plotlib as tpl

#Import framework packages
##from FDC import *               #SVH-package
##from CRVC import *              #SVH-package
##from plot_functies import *     #SVH-package
##from obj_functies import *	    #SVH-package

def NormalizePDF(Input):
    '''
    Sum of all elements becomes 1
    '''
    Normed=Input/Input.sum()
    return Normed

def Normalize(Input):
    '''
    Normalization, values recalculated between 0 and 1
    '''
    Normed=(Input-min(Input))/(max(Input)-min(Input))
    return Normed

#########################################################################
# PDF   Get probability value for a certain input!                     ##
#########################################################################

def UniformDistribution(x,left=0.0,right=1.0,val=1.0):
    '''
    Probability is 1. when in the region
    '''
    if left<=x<=right:
        px=val
    else:
        px=0.0
    return px

def TriangularDistribution(x,left,mode,right):
    '''
    Calculates the "weight factor" of triangular, based on a certain inputvalue

    see numpy manual (or Beven_book: left=0, right=1)
    '''
    if mode>right:
        print 'right en mode zijn omgewisseld!!'
    if left<=x<=mode:
        px=2*(x-left)/((right-left)*(mode-left))
    elif mode<=x<=right:
        px=2*(right-x)/((right-left)*(right-mode))
    else:
        px=0.0
    return px

def TrapezoidalDistribution(x,left,mode1,mode2,right):
    '''
    Calculates the "weight factor" of trapezoidal
     based on a certain inputvalue
    '''
    if mode1>right:
        print 'right en mode1 zijn omgewisseld!!'
    if mode2>right:
        print 'right en mode2 zijn omgewisseld!!'
    if mode1>mode2:
        print 'mode1 en mode2 zijn omgewisseld!!'

    u=2/(right+mode2-mode1-left)

    if left<=x<=mode1:
        px=u*(x-left)/(mode1-left)
    elif mode1<=x<=mode2:
        px=u
    elif mode2<=x<=right:
        px=u*(right-x)/(right-mode2)
    else:
        px=0.0
    return px

def NormalDistribution(x, mu=0.0, sigma=1.0, wholePDF=True,left=None,right=None):
    if wholePDF == True:
        px=1*np.exp(- (x - mu)**2 / (2 * sigma**2)) /(sigma * np.sqrt(2 * np.pi))
    else:           #cut boundaries to use it as membership function
        if left<=x<=right:
            px=1*np.exp(- (x - mu)**2 / (2 * sigma**2)) /(sigma * np.sqrt(2 * np.pi))
        else:
            px=0.0
    return px

def LogNormalDistribution(x, mu=0.0, sigma=1.0, wholePDF=True,left=0.0,right=None):
    if wholePDF == True:
        px = np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2))/(x * sigma * np.sqrt(2 * np.pi))
    else:
        if left<=x<=right:
            px = np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2))/(x * sigma * np.sqrt(2 * np.pi))
        else:
            px=0.0
    return px

##########################################################################
##RANDOM SAMPLING                                                       ##
##########################################################################

#Uniform
def randomUniform(left=0.0,right=1.0,rnsize=None):
    '''
    link to uniform sampling of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    left: float
        lower value
    right: float
        upper value
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.uniform
    '''
    rn=np.random.uniform(left,right,rnsize)
    return rn

#triangular
def randomTriangular(left=0.0, mode=None, right=1.0, rnsize=None):
    '''
    link to triangular sampling of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    left: float
        lower value
    mode: float
        value between left and right, highest probability
    right: float
        upper value
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.triangular
    '''

    if mode==None:
        print 'Triangular needs mode-value'
    rn=np.random.triangular(left, mode, right, rnsize)
    return rn

#trapezoidal
def randomTrapezoidal(left=0.0,mode1=None,mode2=None,right=1.0,rnsize=None):
    '''
    random sampling from trapezoidal function

    Parameters
    ----------
    left: float
        lower value
    mode1: float
        value between left and right, highest probability left side
    mode2: float
        value between left and right, highest probability right side
    right: float
        upper value
    rnsize: int
        number of samples

    '''
    if mode1==None:
        print 'Triangular needs 2 mode-values'
    if mode1==None:
        print 'Triangular needs 2 mode-values'

    rn=np.zeros(rnsize)
    for i in range(np.size(rn)):
        y = np.random.uniform(0.0,1.0,1)
        h=2/(right+mode2-mode1-left)

        a=left
        b=right
        c=mode1
        d=mode2

        if 0.0<=y<=(h*(c-a)/2):
            rn[i]=a+np.sqrt(2*(c-a)/h)*np.sqrt(y)
        elif (h*(c-a)/2)<=y<=(1-h*(b-d)/2):
            rn[i]=(a+c)/2 + y/h
        elif (1-h*(b-d)/2)<=y<=1.0:
            rn[i]=b-np.sqrt(2*(b-d)/h)*np.sqrt(1-y)
        else:
            print 'not in correct range'
    return rn

#Normal
def randomNormal(mu=0.0, sigma=1.0, rnsize=None):
    '''
    link to sampling of normal distribution of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    mu: float
        mean value
    sigma: float
        Standard deviation (spread or 'width') of the distribution
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.normal
    '''

    rn=np.random.normal(mu, sigma, rnsize)
    return rn

#lognormal
def randomLogNormal(mu=0.0, sigma=1.0, rnsize=None):
    '''
    link to sampling of lognormal distribution of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    mu: float
        Mean value of the underlying normal distribution
    sigma: float
        Standard deviation of the underlying normal distribution
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.lognormal
    '''

    rn=np.random.lognormal(mu, sigma, rnsize)
    return rn

#distribution selector
def DistSelector(args,distname='randomUniform'):
    if distname =='randomUniform':
        return randomUniform(left=args[0],right=args[1],rnsize=args[2])
    elif distname =='randomTriangular':
        return randomTriangular(left=args[0], mode=args[1], right=args[2], rnsize=args[3])
    elif distname =='randomTrapezoidal':
        return randomTrapezoidal(left=args[0],mode1=args[1],mode2=args[2],right=args[3],rnsize=args[4])
    elif distname =='randomNormal':
        return randomNormal(mu=args[0], sigma=args[1], rnsize=args[2])
    elif distname =='randomLogNormal':
        return randomLogNormal(mu=args[0], sigma=args[0], rnsize=args[0])
    else:
        raise Exception('Wrong ditribution name; choose from: randomUniform, randomTriangular, randomTrapezoidal, randomNormal, randomLogNormal')

##########################################################################
##INVERSE DISTRIBUTIONS (from uniform to other distributions sampling)  ##
##########################################################################

def stnorm2norm(stn,mu,sigma):
    return sigma*stn + mu

def ltqnorm(p):
    """
    Modified from the author's original perl code (original comments follow below)
    by dfield@yahoo-inc.com.  May 3, 2004.

    Lower tail quantile for standard normal distribution function.

    This function returns an approximation of the inverse cumulative
    standard normal distribution function.  I.e., given P, it returns
    an approximation to the X satisfying P = Pr{Z <= X} where Z is a
    random variable from the standard normal distribution.

    The algorithm uses a minimax approximation by rational functions
    and the result has a relative error whose absolute value is less
    than 1.15e-9.

    Author:      Peter John Acklam
    Time-stamp:  2000-07-19 18:26:14
    E-mail:      pjacklam@online.no
    WWW URL:     http://home.online.no/~pjacklam
    """

    if p <= 0 or p >= 1:
        # The original perl code exits here, we'll throw an exception instead
        raise ValueError( "Argument to ltqnorm %f must be in open interval (0,1)" % p )

    # Coefficients in rational approximations.
    a = (-3.969683028665376e+01,  2.209460984245205e+02, \
         -2.759285104469687e+02,  1.383577518672690e+02, \
         -3.066479806614716e+01,  2.506628277459239e+00)
    b = (-5.447609879822406e+01,  1.615858368580409e+02, \
         -1.556989798598866e+02,  6.680131188771972e+01, \
         -1.328068155288572e+01 )
    c = (-7.784894002430293e-03, -3.223964580411365e-01, \
         -2.400758277161838e+00, -2.549732539343734e+00, \
          4.374664141464968e+00,  2.938163982698783e+00)
    d = ( 7.784695709041462e-03,  3.224671290700398e-01, \
          2.445134137142996e+00,  3.754408661907416e+00)

    # Define break-points.
    plow  = 0.02425
    phigh = 1 - plow

    # Rational approximation for lower region:
    if p < plow:
       q  = math.sqrt(-2*math.log(p))
       return (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / \
               ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)

    # Rational approximation for upper region:
    if phigh < p:
       q  = math.sqrt(-2*math.log(1-p))
       return -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / \
                ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)

    # Rational approximation for central region:
    q = p - 0.5
    r = q*q
    return (((((a[0]*r+a[1])*r+a[2])*r+a[3])*r+a[4])*r+a[5])*q / \
           (((((b[0]*r+b[1])*r+b[2])*r+b[3])*r+b[4])*r+1)

def ltqnormarr(parr,mu=0.0,sigma=1.):
    stnorm=np.array([ltqnorm(p) for p in parr])
    print type(stnorm)
    return stnorm2norm(stnorm,mu,sigma)

##########################################################################
##TESTFIGUREN ETC                                                       ##
##########################################################################

#TEST
##import re
##plt.figure()
##trg='UniformDistribution'
##x=np.arange(-3.0,3.0,0.01)
##y=np.zeros(np.size(x))
##for i in range(np.size(x)):
##    #y[i]= TriangularDistribution(x[i],0.0,0.4,1.0)
##    #y[i]= UniformDistribution(x[i])
##    y[i]= eval(trg+'(x[i])')
##    #y[i]= NormalDistribution(x[i],wholePDF=True)
##    #y[i]= LogNormalDistribution(x[i],mu=0.5, sigma=1.0, wholePDF=False,right=2.8)
##ynorm=Normalize(y)
##plt.plot(x,ynorm,'ro')
##plt.show()
##plt.plot(x,y,'ro')
###plt.savefig('uniformmake.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')
##plt.savefig('lognormalmake.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')

##plt.figure()
##x=np.arange(-1.0,1.0,0.01)
##y=np.zeros(np.size(x))
##for i in range(np.size(x)):
##    y[i]= TriangularDistribution(x[i],-1.0,0.0,1.0)
##    #y[i]= UniformDistribution(x[i])
##    #y[i]= NormalDistribution(x[i],wholePDF=True)
##    #y[i]= LogNormalDistribution(x[i],mu=0.0, sigma=1.0, wholePDF=False,right=2.8)
##ynorm=Normalize(y)
###plt.plot(x,ynorm,'ro')
###y2=randomLogNormal(mu=0.0, sigma=1.0, rnsize=100000)
##y2=randomTriangular(left=-1.0, mode=0.0, right=1.0, rnsize=10000)
##plt.hist(y2, bins=200,normed=True, cumulative=False,color='b', edgecolor='b')
##plt.plot(x,y,'ro')
###plt.axis([-3, 3, 0, 1])
##plt.show()

##plt.figure()
##s = randomUniform(0.0,1.0,100000)
##count, bins, ignored = plt.hist(s, 200, normed=True)
##plt.plot(bins, np.ones_like(bins), linewidth=2, color='r')
##
##testnr=[1000000,100000,10000,1000,100,10]
##npt=[1,2,3,4,5,6]
##colors=['k','b','y','m','c','g']
##
##plt.figure()
##tg=0
##for i in testnr:
##    plt.subplot(3,2,npt[tg])
##    y=randomtrapezoidal(0.0,0.2,0.5,1.0,i)
##    h = plt.hist(y, bins=200,normed=True, cumulative=False,color=colors[tg], edgecolor=colors[tg])
##    plt.title(str(i)+ ' samples')
##    setp(gca(), xticklabels=[])
##    setp(gca(), yticklabels=[])
##    x=np.arange(0.0,1.0,0.0001)
##    y=np.zeros(np.size(x))
##    for i in range(np.size(x)):
##        y[i]= TrapezoidalDistribution(x[i],0.0,0.2,0.5,1.0)
##    plt.plot(x,y,'r')
##    setp(gca(), xticklabels=[])
##    setp(gca(), yticklabels=[])
##
##    tg=tg+1
##
##plt.savefig('nrMCTrap.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')
##plt.figure()
##tg=0
##for i in testnr:
##    plt.subplot(3,2,npt[tg])
##    s = np.random.uniform(0.0,1.0,i)
##    count, bins, ignored = plt.hist(s, 200, normed=True, color=colors[tg], edgecolor=colors[tg])
##    plt.plot(bins, np.ones_like(bins), linewidth=2)
##    plt.title(str(i)+ ' samples')
##    setp(gca(), xticklabels=[])
##    setp(gca(), yticklabels=[])
##    tg=tg+1
##
##plt.savefig('nrMCUnif.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')


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

Author: Chaofeng Wang

Logistic regression

Implemented gradient ascent by batch and mini batch version

very sensitive to outliers

"""
import numpy as np

class Logi_regression:

    def LoR_BGA(self,x,y,alpha):

        xsize = x.shape

        error = np.matlib.ones((xsize[0],1)) # initialize error vector

        coef = np.matlib.zeros((xsize[1]+1,1)) # initialize coefficients

        X = np.hstack((x,np.ones((xsize[0],1)))) # adding intercept

        maxCount = 2000

        count = 0

        while np.linalg.norm(error) > 0.001 and count < maxCount:

                count = count + 1

                error = y - (1 / (1 + np.exp(-X * coef)))

                coef = coef + alpha * X.T  * error

        return coef

    def LoR_SGA(self,x,y,alpha):
        xsize = x.shape

        error = np.matlib.ones((xsize[1],1)) # initialize error vector

        coef = np.matlib.zeros((xsize[1]+1,1)) # initialize coefficients

        X = np.hstack((x,np.ones((xsize[0],1)))) # adding intercept

        maxCount = 2000

        count = 0

        B = 2 # mini-batch size

        index_total = range(xsize[0])

        while np.linalg.norm(error) > 0.001 and count < maxCount:

                index = np.random.choice(index_total,B,replace = False) # random choose index

                y_batch = y[index,:]

                X_batch = X[index,:]

                count = count + 1

                error = y_batch - (1 / (1 + np.exp(-X_batch * coef)))

                coef = coef + alpha * X_batch.T  * error

        return coef

if __name__ == "__main__":

    a = Logi_regression()
    ### input data as x,y
    x = np.matrix(x)
    y = np.matrix(y)

    x = x.T # column vector
    y = y.T #column vector

    alpha = 0.001 # learning rate

    coef_BGA = a.LoR_BGA(x,y,alpha)
    coef_SGA = a.LoR_SGA(x,y,alpha)

    xsize = x.shape
    X = np.hstack((x,np.ones((xsize[0],1))))

    r_BGA_err =  y- (1 / (1 + np.exp(-X * coef_BGA)))
    r_SGA_err =  y- (1 / (1 + np.exp(-X * coef_SGA)))


import numpy as np

class Linear:
    def __init__(self, X, y):
        self.X = np.concatenate((np.ones(y.shape), X), axis = 1)
        self.y = y
        self.preds = np.zeros(y.shape)
        self.n = y.shape[0]

        self.loss = None
        self.lr = 0.001 # funny, it the lr is too big I don't get convergence
        self.beta = np.random.uniform(low=-1.0, high=1.0, size= (self.X.shape[1], 1))
        self.grad = None

    def forward(self):
            self.preds = np.dot(self.X, self.beta)

    def backward(self):
        self.grad =2*  np.mean((self.preds - self.y)* self.X,
        axis=0, keepdims=True ).T

        self.beta = self.beta -  self.lr * self.grad

    def calculate_loss(self, y, preds):
        return np.mean( (y - preds)**2 )

    def fit(self, iters):
        for _ in range(iters):
            self.forward()
            self.backward()
            self.loss = self.calculate_loss(self.y, self.preds)

    def fit_exact(self):
        self.beta_exact = np.linalg.solve(np.dot(self.X.T,self.X),
        np.dot(self.X.T,self.y))

    def pred(self, X):
        return np.dot(X, self.beta)

    def pred_exact(self, X):
        return np.dot(X, self.beta_exact)

if __name__ == '__main__':
    from data_prep import Data
    data= Data()

    data.load_data('./data/USA_Housing.csv', 'Price')

    data.normalize(norm_target = True)
    X_train, X_test, y_train, y_test =  data.split_data(data.X_norm, data.y_norm)

    linear= Linear(X_train, y_train)
    linear.fit(iters=10000)
    linear.fit_exact()

    print('Beta from gradient descent:\n{}'.format(linear.beta))
    print('Beta from matrix inversion:\n{}'.format(linear.beta_exact))

    print('Training MSE (Gradient descent):{}'.format(linear.loss))

    # Need to make uniform convention for bias matrix
    X_test = np.concatenate((np.ones(y_test.shape), X_test), axis = 1)

    print('Test MSE (Gradient descent):{}'.format(
    linear.calculate_loss(y_test, linear.pred(X_test))
    ))
    print('Test MSE (Matrix inversion):{}'.format(
    linear.calculate_loss(y_test, linear.pred_exact(X_test))
    ))


import numpy as np
import sympy as sp

def jacobianMatrix(func1, func2, points):
	res = np.zeros((2,2))
	x = sp.Symbol('x')
	y = sp.Symbol('y')

	dx11 = sp.diff(func1,x)
	dx12 = sp.diff(func1,y)
	dx21 = sp.diff(func1,x)
	dx22 = sp.diff(func2,y)

	jx11 = lambda x, y : eval(str(dx11))
	jx12 = lambda x, y : eval(str(dx12))
	jx21 = lambda x, y : eval(str(dx21))
	jx22 = lambda x, y : eval(str(dx22))

	res[0,0] = jx11(points[0], points[1])
	res[0,1] = jx12(points[0], points[1])
	res[1,0] = jx21(points[0], points[1])
	res[1,1] = jx22(points[0], points[1])

	return res;

def fxMatrix(func1, func2, points):
	res = np.zeros((2,1))

	fx1 = lambda x, y : eval(str(func1))
	fx2 = lambda x, y : eval(str(func2))

	res[0,0] = fx1(points[0], points[1])
	res[1,0] = fx2(points[0], points[1])

	return res

def calculaNewton(request):
	graph  = []
    xl     = float(request.POST.get('xl'))       # Limite inferior
    xu     = float(request.POST.get('xu'))       # Limite superior
    TOL    = float(request.POST.get('tol'))      # Tolerância do cáculo
    N      = float(request.POST.get('maxi'))     # Maximo de iterações
    f1     = request.POST.get('func1')		     # Função a ser utilizada nos calculos
    f2     = request.POST.get('func2')		     # Função a ser utilizada nos calculos

	x = np.array([xl,xu])

	k=0
	# iteracoes
	while (k < N):
	   k += 1
	   #iteracao Newton
	   resultJacobian = jacobianMatrix(f1, f2, x)
	   resultFx = fxMatrix(f1, f2, x)

	   delta = -np.linalg.inv(resultJacobian).dot(resultFx)

	   x[0] = x[0] + delta[0];
	   x[1] = x[1] + delta[1];
	   graph.append([x[0], x[1]])

	   # #criterio de parada
	   if (np.linalg.norm(delta,np.inf) < TOL):
	       break

	return HttpResponse(json.dumps({ 'points': graph }), content_type="application/json")


# https://stephens999.github.io/fiveMinuteStats/likelihood_function.html
import numpy as np
import matplotlib.pyplot as plt

q = np.linspace(0,1, 100)

def likelihood_fn(x):
    return np.power(x, 30) * np.power((1-x), 70)

l_val = likelihood_fn(q)
# print(q)
# print(l_val)
max_index = np.argmax(l_val)

textstr = '\n'.join(('L_max: {0:.3e}'.format(l_val[max_index]),
                     'q: {0:.4f}'.format(q[max_index])))
print(textstr)

if True:
    props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
    plt.plot(q, l_val)
    plt.axvline(x=q[max_index], color='r', ls='--')
    plt.text(q[max_index]+0.05, l_val[max_index], textstr, fontsize=12, verticalalignment='top', bbox=props)
    plt.xlabel('q (probability)')
    plt.ylabel('likelihood')
    plt.show()

# normalization
l_val_normalized = l_val / l_val[max_index]
textstr += '\nnorm: {} (of course!)'.format(l_val_normalized[max_index])

if True:
    plt.plot(q, l_val_normalized)
    plt.axvline(x=q[max_index], color='r', ls='--')
    plt.text(q[max_index]+0.05, l_val_normalized[max_index], textstr, fontsize=12, verticalalignment='top', bbox=props)
    plt.xlabel('q (probability)')
    plt.ylabel('L(q)/L(q_hat)')
    plt.show()

def log_likelihood_fn(x):
    return 30*np.log(x) + 70 * np.log(1-x)

log_l_val = log_likelihood_fn(q)
print(log_l_val)
# print(np.argmax(log_l_val), log_l_val[np.argmax(log_l_val)])
log_l_val_normalized = log_l_val - log_likelihood_fn(0.3)
if True:
    plt.plot(q, log_l_val_normalized)
    plt.xlabel('q')
    plt.ylabel('log L(q) / log L(q_hat)')
    plt.ylim(-10,0)
    plt.show()


'''
@author: Hsinglu Liu
@version: 1.0
@Date: 2020.8.5
'''

from gurobipy import *
import pandas as pd
import numpy as np
from pandas import Series, DataFrame
import math

Arcs = {'1,2': [15, 15]    # cost flow
        ,'1,4': [25, 25]
        ,'1,3': [45, 45]
        ,'2,5': [30, 60]
        ,'2,4': [2, 2]
        ,'5,7': [2, 2]
        ,'4,7': [50, 100]
        ,'4,3': [2, 2]
        ,'3,6': [25, 50]
        ,'6,7': [1, 1]
       }
Arcs

Nodes = [1, 2, 3, 4, 5, 6, 7]

commodity = [[1, 7, 25],  # s_i, d_i, demand
             [2, 6, 2]
]

model = Model('MultiCommodity')

# add variables
X = {}
for key in Arcs.keys():
    for k in range(len(commodity)):
        key_x = key + ',' + str(k)
        X[key_x] = model.addVar(lb=0
                                ,ub=Arcs[key][1]
                                ,vtype=GRB.CONTINUOUS
                                ,name= 'x_' + key_x
                               )
# add objective function
obj = LinExpr(0)
for key in Arcs.keys():
    for k in range(len(commodity)):
        key_x = key + ',' + str(k)
        obj.addTerms(Arcs[key][0], X[key_x])
model.setObjective(obj, GRB.MINIMIZE)

# constraints 1
for k in range(len(commodity)):
    for i in Nodes:
        lhs = LinExpr(0)
        for key_x in X.keys():
#             nodes = key_x.split(',')
            if(i == (int)(key_x.split(',')[0]) and k == (int)(key_x.split(',')[2])):
                lhs.addTerms(1, X[key_x])
            if(i == (int)(key_x.split(',')[1]) and k == (int)(key_x.split(',')[2])):
                lhs.addTerms(-1, X[key_x])
        if(i == commodity[k][0]):
            model.addConstr(lhs == commodity[k][2], name='org_, ' + str(i) + '_' + str(k))
        elif(i == commodity[k][1]):
            model.addConstr(lhs == -commodity[k][2], name='des_, ' + str(i) + '_' + str(k))
        else:
            model.addConstr(lhs == 0, name='inter_, ' + str(i) + '_' + str(k))

# constraints 2
for key in Arcs.keys():
    lhs = LinExpr(0)
    for k in range(len(commodity)):
        key_x = key + ',' + str(k)
        lhs.addTerms(1, X[key_x])
    model.addConstr(lhs <= Arcs[key][1], name = 'capacity_, ' + key)

model.write('Multicommodity_model.lp')
model.optimize()

for var in model.getVars():
    if(var.x > 0):
        print(var.varName, '\t', var.x)
dual = model.getAttr("Pi", model.getConstrs())


import numpy as np

class KittiProjector:
    """
    class to project kitti velodyne points
    """

    ## Spherical Projection ###############################################################

    @staticmethod
    def cartesian_to_spherical(x: np.array, y: np.array, z: np.array) -> [np.array, np.array, np.array]:
        """
        function transforms cartesian coordinates into spherical ones

        Args:
            x: x axis values
            y: y axis values
            z: z axis values

        returns:
            spherical coordinate of every point in degrees

        """

        depth = np.sqrt(x ** 2 + y ** 2 + z ** 2)

        azimuth = np.arctan2(y, x)
        elevation = np.arctan2(z, depth)

        # transforms to degrees
        azimuth = np.rad2deg(azimuth)
        elevation = np.rad2deg(elevation)

        return azimuth, elevation, depth

    @staticmethod
    def get_pgm_index(
                    lidar_data: np.array,
                    pgm_height: int = 64,
                    pgm_width: int = 512,
                    vertical_field_view: tuple = (-25, 3),
                    horizontal_field_view: tuple = (-90, 90),
                    invert_z_axis=True) -> np.array:
        """
        function get polar grid map coordinates for every point in lidar scan
        Args:
            lidar_data: numpy array of lidar scan
            pgm_height: height of polar grid map
            pgm_width: width of polar grid map
            vertical_field_view:  tuple
                vertical field of view of lidar, angle of highest layer, angle of lowest layer
                for sick: (-1.5,1.5)
                for velodyne 64: (-25, 3)
            horizontal_field_view: horizontal field of view of layer, angles of extrem laser beans
                for sick (-42.5, 42.5)
                for velodyne 64: (-180, 180), for kitti (-90, 90)
            invert_z_axis: boolean, flag wether to invert z axis

        return:
        pgm_azimuth_idx, pgm_elevation_idx, depth: angles for every lidar point and its depth
        """

        # extract coordinate values
        x, y, z = lidar_data[:, 0], lidar_data[:, 1], lidar_data[:, 2]

        if invert_z_axis:
            z = -1 * z

        # get spherical coordinates of points
        azimuth, elevation, depth = KittiProjector.cartesian_to_spherical(x, y, z)

        vertical_step = (abs(vertical_field_view[0]) + abs(vertical_field_view[1])) / (pgm_height-1)
        horizontal_step = (abs(horizontal_field_view[0]) + abs(horizontal_field_view[1])) / (pgm_width-1)

        vertical_range = np.arange(vertical_field_view[0], vertical_field_view[1], vertical_step)
        horizontal_range = np.arange(horizontal_field_view[0], horizontal_field_view[1], horizontal_step)

        elevation_diff = np.abs(elevation.reshape(-1, 1) - vertical_range.reshape(1, -1))
        azimuth_diff = np.abs(azimuth.reshape(-1, 1) - horizontal_range.reshape(1, -1))

        pgm_azimuth_idx = np.argmin(azimuth_diff, axis=1).squeeze()
        pgm_elevation_idx = np.argmin(elevation_diff, axis=1).squeeze()

        pgm_azimuth_angles = horizontal_range[pgm_azimuth_idx]
        pgm_elevation_angles = vertical_range[pgm_elevation_idx]

        return depth, pgm_azimuth_angles, pgm_elevation_angles

   ## project points to image coordinates #############################################################

    @staticmethod
    def project_velo2cam(lidar_data: np.array, p_rect_02_matrix: np.array, r_rect_00_matrix: np.array, tr_velo_matrix: np.array) -> np.array:
        """
        function project lidar points into image
        :param
            lidar_data: lidar points to be projected, shape: number_of_points,3 (-1,x,y,z)
            p_rect_02_matrix: projection matrix
            r_rect_00_matrix: rectifying rotation matrix of cam 0 in homogeneous space
            tr_velo_matrix:   translation rotation matrix of cam 0 in homogeneous space
        :return:
            projection_points_normalized: projected points in pixel coordinates: u,v
        """

        # transform lidar points to homogeneous space (x,y,z,1)
        velo_points_homogenous = np.concatenate([lidar_data, np.ones((lidar_data.shape[0], 1))], axis=1)

        # calculate projection matrix:
        projection_matrix = np.dot(p_rect_02_matrix, np.dot(r_rect_00_matrix, tr_velo_matrix))

        # project points [ x,y,z,1] --> [x,y,s]
        projection_points = np.dot(projection_matrix, velo_points_homogenous.T)

        # normalize projected points [x,y,s] --> [x/s,y/s] = [u,v]
        projection_points_normalized = projection_points[:2, :] / projection_points[2, :]

        return projection_points_normalized.T


# -*- coding: utf-8 -*-
""" spfs/utils/test/test_normalization """

import unittest

import numpy as np

from spfs.utils.normalization import Normalization

class TestNormalization(unittest.TestCase):

    def setUp(self):
        self.vector = np.arange(4)

    def test_lp(self):
        for p in range(3):
            Normalization.lp(self.vector, p)

        with self.assertRaises(AssertionError):
            Normalization.lp(self.vector, 3)

    def test_l0(self):
        self.assertTrue(
            np.array_equal(
                Normalization.l0(self.vector),
                self.vector/np.linalg.norm(self.vector, 0)
            )
        )

    def test_l1(self):
        self.assertTrue(
            np.array_equal(
                Normalization.l1(self.vector),
                self.vector/np.linalg.norm(self.vector, 1)
            )
        )

    def test_l2(self):
        self.assertTrue(
            np.array_equal(
                Normalization.l2(self.vector),
                self.vector/np.linalg.norm(self.vector, 2)
            )
        )

    def test_standardization(self):
        dev = np.std(self.vector)
        self.assertTrue(
            np.array_equal(
                Normalization.standardization(self.vector),
                (self.vector-np.mean(self.vector))/dev
            )
        )


"""Kernel function

LinearKernel, GaussianKernel, SigmoidKerne, RBFKernel, ExponentialKernel, GramMatrix are implemented

"""

import numpy as np
from abc import ABC,abstractclassmethod

class BaseKernel(ABC):
    def __init__(self):
        pass

    @abstractclassmethod
    def __call__(self,x,y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y)

        """
        pass

class LinearKernel(BaseKernel):
    """Linear Kernel

    Linear Kernel : k(x,y) = x^Ty

    """
    def __init__(self):
        super(LinearKernel,self).__init__()

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = x^Ty

        """
        return np.dot(x,y)

class GaussianKernel(BaseKernel):
    """Gaussian Kernel

    Gaussian Kernel : k(x,y) = exp(-|x-y|^2/2sigma^2)

    """
    def __init__(self,sigma=0.1):
        """

        Args:
            sigma (float) : parameter for kernel function

        """
        super(GaussianKernel,self).__init__()
        self.sigma = sigma

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = exp(-|x-y|^2/2sigma^2)

        """
        return np.exp(-np.dot(x-y,x-y)/(2*self.sigma**2))

class SigmoidKernel(BaseKernel):
    """Sigmoid Kernel

    Sigmoid Kernel: k(x,y) = tanh(ax^Ty + b)

    """
    def __init__(self,a=1,b=0):
        """

        Args:
            a,b (float) : parameter for kernel function

        """
        super(SigmoidKernel,self).__init__()
        self.a = a
        self.b = b

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = tanh(ax^Ty + b)

        """
        return np.tanh(self.a*np.dot(x,y) + self.b)

class RBFKernel(BaseKernel):
    """RBF Kernel

    RBF Kernel : k(x,y) = h(|x - y|)

    """
    def __init__(self,h):
        """

        Args:
            h (function) : k(x,y) = h(|x-y|)

        """
        super(RBFKernel,self).__init__()
        self.h = h

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = h(|x - y|)

        """
        return self.h(np.dot(x-y,x-y)**0.5)

class ExponentialKernel(BaseKernel):
    """Exponential Kernel

    Exponential Kernel : k(x,y) = exp(-theta|x - y|)

    """
    def __init__(self,theta=1):
        """

        Args:
            theta (float) : parameter for kernel function

        """
        super(ExponentialKernel,self).__init__()
        self.theta = theta

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = exp(-theta|x - y|)

        """
        return np.exp(-self.theta*np.dot(x-y,x-y)**0.5)

class GramMatrix():
    """Gram Matrix

    For making gram matrix.
    kernel type is
        LinearKernel
        GaussianKernel
        SigmoidKernel
        RBFKernel
        ExponentialKernel

    Attributes:
        kernel_func (function) : kernel_function

    """
    def __init__(self,kernel="Linear",sigma=0.1,a=1.0,b=0.0,h=None,theta=1.0):
        """

        Args:
            kernel (string) : kernel type (default "Linear"). you can choose "Linear","Gaussian","Sigmoid","RBF","Exponential"
            sigma (float) : for "Gaussian" kernel
            a,b (float) : for "Sigmoid" kernel
            h (function) : for "RBF" kernel
            theta (float) : for "Exponential" kernel

        """
        self.kernel_func = None
        if kernel == "Linear":
            self.kernel_func = LinearKernel()
        elif kernel == "Gaussian":
            self.kernel_func = GaussianKernel(sigma=sigma)
        elif kernel == "Sigmoid":
            self.kernel_func = SigmoidKernel(a=a,b=b)
        elif kernel == "RBF":
            if h is None:
                raise ValueError("if kernel is 'RBF', h must not be None.")
            self.kernel_func = RBFKernel(h=h)
        elif kernel == "Exponential":
            self.kernel_func = ExponentialKernel(theta=theta)
        else:
            raise ValueError(f"kernel '{kernel}' is inappropriate")

    def __call__(self,X):
        """

        Args:
            X (2-D array) : shape = (N_samples,N_dims)

        Returns:
            2-D array: Gram matrix, shape = (N_samples,N_samples)

        Note:
            time complexity is O(N_samples^2*N_dims)

        """
        N = X.shape[0]
        G = np.zeros((N,N))
        for i in range(N):
            G[i] = np.array([self.kernel_func(X[i],X[j]) for j in range(N)])
        return G


from sklearn import datasets
import numpy as np
import seaborn as sns
from sklearn.metrics import f1_score
from sklearn.preprocessing import normalize
from sklearn.utils import shuffle

class BinaryLogisticRegression:
  def __init__(self, lr=0.01, max_iter=1000, fit_intercept=True):
    self.lr = lr
    self.w = [] # needs input data to define size
    self.max_iter = max_iter
    self.threshold = 0.5
    self.fit_intercept = fit_intercept

  def fit(self, X, y):
    if self.fit_intercept:
      self._add_intercept(X)

    self.w = np.random.uniform(0, 1, X.shape[1])

    for _ in range(self.max_iter):
      h = np.dot(X, self.w)
      z = self._sigmoid(h)

      gradients = (np.dot(X.T, z - y)) / X.shape[0]

      self.w -= self.lr * gradients

  def _add_intercept(self, X):
    intercept = np.ones((X.shape[0], 1))
    X = np.concatenate((X, intercept), axis=1)

  def predict_prob(self, X):
    if self.fit_intercept:
      self._add_intercept(X)
    return self._sigmoid(np.dot(X, self.w))

  def predict(self, X):
    return self.predict_prob(X) >= self.threshold

  def _sigmoid(self, h):
    return 1 / (1 + np.exp(-h))

dataset = datasets.load_iris()
X = dataset.data[:,:2]
y = (dataset.target != 0) * 1

X = normalize(X)

X, y = shuffle(X, y)

blg = BinaryLogisticRegression(max_iter=30000, lr=0.1)
c = 0
blg.fit(X, y)
y_pred = blg.predict(X)
f1 = f1_score(y, y_pred)
print(f'w: {blg.w}, f1_score: {f1}')

X.shape

np.random.uniform(0, 1, 10)

h = X.dot(w)
h.shape

h[:10]

z = 1 / (1 + np.exp(h))
z.shape

z[:10]

dJdw = (X.T.dot(z - y)) / M_feats
print(dJdw.shape, '\n', dJdw[:10])


import numpy as np

def COD(x, y, theta):
    mean_of_ys = np.sum(y) / len(y)
    y_variation = np.sum(np.square(y - mean_of_ys))

    test_hypothesis = np.dot(x, theta)
    sq_error = np.sum(np.square(test_hypothesis - y))
    r2 = 1 - (sq_error / y_variation)
    return r2

def adj_r2(num_of_params, num_of_feats, r2):
    adj_r2 = 1 - (1 - r2) * (num_of_feats - 1) / \
        (num_of_feats - num_of_params - 1)
    return adj_r2

def ZNorm(x):
    mi = np.mean(x, axis=0)
    sigma = np.std(x, axis=0)
    x_norm = (x - mi) / sigma
    return x_norm, mi, sigma

def computeCost(y, hypothesis):
    train_len = len(y)
    J = 1 / (2 * train_len) * np.sum(np.square(hypothesis - y))
    return J

def gradientDescent(x_train, x_test, y_train, y_test, theta, alpha, num_iters, alpha_increase):
    train_len = len(y_train)
    test_lenn = len(y_test)
    history_train = np.zeros(num_iters)
    history_test = np.zeros(num_iters)

    for i in range(num_iters):
        # train
        hypothesis = np.dot(x_train, theta)
        history_train[i] = computeCost(y_train, hypothesis)
        theta -= alpha * (1 / train_len) * np.dot(x_train.T, (hypothesis - y_train))
        # test
        hypothesis = np.dot(x_test, theta)
        history_test[i] = computeCost(y_test, hypothesis)

        if i % alpha_increase == 0 and i != 0:
            alpha *= .1
            alpha_increase = i * 10

    return theta, history_train, history_test


import numpy as np

a = np.array([3, 4])
np.linalg.norm(a)

b = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])
c = np.array([1, 0, 1])

# 矩阵和向量之间的乘法
np.dot(b, c)            		# array([ 4, 10, 16])
np.dot(c, b.T)          		# array([ 4, 10, 16])

np.trace(b)             		# 求矩阵的迹，15
np.linalg.det(b)        		# 求矩阵的行列式值，0
np.linalg.matrix_rank(b)	# 求矩阵的秩，2，不满秩，因为行与行之间等差

d = np.array([
    [2, 1],
    [1, 2]
])

'''
对正定矩阵求本征值和本征向量
本征值为u，array([ 3.,  1.])
本征向量构成的二维array为v，
array([[ 0.70710678, -0.70710678],
       [ 0.70710678,  0.70710678]])
是沿着45°方向
eig()是一般情况的本征值分解，对于更常见的对称实数矩阵，
eigh()更快且更稳定，不过输出的值的顺序和eig()是相反的
'''
u, v = np.linalg.eig(d)

# Cholesky分解并重建
l = np.linalg.cholesky(d)

'''
array([[ 2.,  1.],
       [ 1.,  2.]])
'''
np.dot(l, l.T)

e = np.array([
    [1, 2],
    [3, 4]
])

# 对不镇定矩阵，进行SVD分解并重建
U, s, V = np.linalg.svd(e)

S = np.array([
    [s[0], 0],
    [0, s[1]]
])

'''
array([[ 1.,  2.],
       [ 3.,  4.]])
'''
np.dot(U, np.dot(S, V))


import numpy as np

def MSE(x, x_hat):
    mse = np.sum((x - x_hat)**2) / len(x)
    return mse

def MRAE(x, x_hat):
    # Mean relative absolute error
    mrae = np.sum(np.abs((x - x_hat) / x)) / len(x)
    return mrae

def GFC(x, x_hat):
    # Goodness of fit coefficient
    x_normed = x / np.sqrt(np.sum(x**2))
    x_hat_normed = x_hat / np.sqrt(np.sum(x_hat**2))
    gfc = np.dot(x_normed, x_hat_normed)
    return gfc

def PSNR(v_max, x, x_hat):
    return 20 * np.log10(v_max / MRAE(x, x_hat))

if __name__ == '__main__':
    x = np.array([-1, 2, 3, 4, 5, 6, 7, 8])
    x_hat = np.array([-1.1, 2, 3.1, 4, 4.5, 7, 7.1, 8])
    print(MRAE(x, x_hat), MRAE(x * 10, x_hat * 10))
    print(GFC(x, x_hat), GFC(x * 10, x_hat * 10))