import math
import numpy as np

class VectorSimilarity():

    def __init__(self):
        print('class init')

    def Cosine(self, vec1, vec2) :
        result = self.InnerProduct(vec1,vec2) / (self.VectorSize(vec1) * self.VectorSize(vec2))
        return result

    def VectorSize(self, vec) :
        return math.sqrt(np.sum(np.power(vec, 2)))

    def InnerProduct(self, vec1, vec2) :
        return np.inner(vec1, vec2)

    def Euclidean(self, vec1, vec2) :
        return np.linalg.norm(vec1-vec2)

    def Theta(self, vec1, vec2) :
        return math.acos(self.Cosine(vec1,vec2)) + math.radians(10)

    def Triangle(self, vec1, vec2) :
        theta = math.radians(self.Theta(vec1,vec2))
        return (self.VectorSize(vec1) * self.VectorSize(vec2) * math.sin(theta)) / 2

    def Magnitude_Difference(self, vec1, vec2) :
        return abs(self.VectorSize(vec1) - self.VectorSize(vec2))

    def Sector(self, vec1, vec2) :
        ED = self.Euclidean(vec1, vec2)
        MD = self.Magnitude_Difference(vec1, vec2)
        theta = self.Theta(vec1, vec2)
        return math.pi * math.pow((ED+MD),2) * theta/360

    def TS_SS(self, vec1, vec2) :
        return self.Triangle(vec1, vec2) * self.Sector(vec1, vec2)


from __future__ import division
from operations import accuracy_score
import numpy as np

class SquareLoss():
    def __init__(self, y, y_pred):
    	self.y = y
    	self.y_pred = y_pred

    def loss(self, y, y_pred):
        return (0.5 * np.power((y_pred - y), 2))

    def gradient(self, y, y_pred):
        return (y_pred - y)

class CrossEntropy():
    def __init__(self, label, predict_proba):
    	''' label : for example in a binary setting, label = 0 for man and label = 1 for woman
    	predict_proba : the predicted probability
    	You can think of label as "y_true" and predict_proba as "y_pred" '''
    	self.label = label
    	self.predict_proba = predict_proba

    def loss(self, label, predict_proba):
        # Avoid division by zero
        predict_proba = np.clip(predict_proba, 1e-15, 1 - 1e-15)
        return (-label * np.log(predict_proba) - (1 - label) * np.log(1 - predict_proba))

    def accuracy(self, label, predict_proba)
    	return accuracy_score(np.argmax(label, axis=1), np.argmax(predict_proba, axis=1))
    	# 'label' and 'predict_proba' being of dimensions (n_samples, n_classes)
    	# we basically pick the index for which the probability is maximum in each row

    def gradient(self, label, predict_proba):
        # Avoid division by zero
        predict_proba = np.clip(predict_proba, 1e-15, 1 - 1e-15)
        return (-(label / predict_proba) + (1 - label) / (1 - predict_proba))


import unittest
import numpy as np
import paderbox.testing as tc
from paderbox.math.vector import cos_similarity

class TestCosineSimilarity(unittest.TestCase):
    def test_cosine_similarity(self):
        phi = 60 / 360 * 2 * np.pi

        W1 = np.array([4, 0, 0])
        W2 = np.array([1, 0, 0])
        W3 = np.array([0, 1, 0])
        W4 = np.array([1, 1j, 0])
        W5 = np.array([1, -1j, 0])
        W6 = np.array([np.cos(phi), np.sin(phi), 0])

        tc.assert_equal(cos_similarity(W1, W1), 1.0)
        tc.assert_equal(cos_similarity(W1, W2), 1.0)
        tc.assert_equal(cos_similarity(W1, W3), 0.0)
        tc.assert_equal(cos_similarity(W4, W5), 0.0)
        tc.assert_equal(cos_similarity(W1, W6), np.cos(phi))


from math import log
import numpy as np

class Regrezio:
    """Base class for linear regression models"""

    def __init__(self, model='lin'):
        # set the regression model
        self.model = model.lower()

    def fit(self, x, y):
        """ Fits values to linear regression model and calculates
            coefficients and intercept """
        self.y = np.array(y)
        self.x = np.array(x)

        # Calculate the lns of the values for function models
        if self.model == 'log-lin':
            self.y = np.array([[log(i[0])] for i in self.y])
        elif self.model == 'log-log':
            self.y = np.array([[log(i[0])] for i in self.y])
            self.x = np.array([[log(cell) for cell in row] for row in self.x])
        elif self.model == 'lin-log':
            self.x = np.array([[log(cell) for cell in row] for row in self.x])
        elif self.model == 'quadratic':
            self.x = np.array([[xij[0], xij[0] ** 2] for xij in self.x])
        elif self.model == 'lin' or self.model == 'lin-lin':
            pass

        # insert ones to first column
        self.x1 = np.concatenate((np.ones((len(self.x), 1)), self.x), axis=1)
        # transpose of x
        self.xt = np.transpose(self.x1)
        # X'X
        self.xtx = self.xt @ self.x1
        # X'X inversed
        self.xtx_inv = np.linalg.inv(self.xtx)

        # X'Y
        self.xty = self.xt @ self.y
        # (X'X)^-1(X'Y)
        self.coefficient_vector = self.xtx_inv @ self.xty
        # independent value in the model
        self.intercept = self.coefficient_vector[0]
        # estimated coefficients of the model
        self.coefficients = self.coefficient_vector[1:]
        # set the length of the series
        self.n = len(self.y)

    def y_func(self, xi):
        return float(self.intercept + sum([xij * ci for xij, ci in zip([xi], self.coefficients)]))

    def predict(self, x):
        """ Calculates predicted Y values and error values """
        # predicted Y values. Y^
        self.y_pred = np.array(
            [[self.y_func(xi[0])] for xi in x])
        # mean of Y's
        self.y_bar = float(sum(self.y) / self.n)

        # To calculate the determination coefficient r^2
        # error values
        self.errors = np.array(
            [[float(yi - yi_pred)] for yi, yi_pred in zip(self.y, self.y_pred)]
        )
        self.squared_errors = sum(np.array(
            [[e**2] for e in self.errors]
        ))
        self.total_variation = sum([(yi - self.y_bar)**2 for yi in self.y])
        self.score =  float(1 - self.squared_errors / self.total_variation)
        return self.y_pred


# coding=utf-8
import numpy as np
from sklearn import metrics
y_true=np.array([1.1,3.3,5.5,7.7])
y_pred=np.array([1.0,3.0,5.0,7.0])

# MSE
def MSE(true, pred):
    return np.sum((true - pred)**2)
mse=metrics.mean_squared_error(y_true,y_pred)
# RMSE
rmse=np.sqrt(mse)
# MAE
def mae(true, pred):
    return np.sum(np.abs(true - pred))
mae=metrics.mean_absolute_error(y_true,y_pred)
# MAPE
mape=np.mean(np.abs(y_pred-y_true)/y_true)*100
#SMPAE
smape=np.mean(2*np.abs(y_pred-y_true)/(np.abs(y_pred)+np.abs(y_true)))*100
# R Squared
RS=metrics.r2_score(y_true,y_pred)
# huber loss
def huber(true, pred, delta):
    loss = np.where(np.abs(true-pred) < delta , 0.5*((true-pred)**2), delta*np.abs(true - pred) - 0.5*(delta**2))
    return np.sum(loss)

y_trues = [1,1,1,1,0,0,1,1,1,0,0]
y_preds = [1,1,1,0,1,1,0,1,1,1,0]

# ACC准确率
acc=metrics.accuracy_score(y_trues,y_preds)
# Precision精准率
P=metrics.precision_score(y_trues,y_preds)
# Recall召回率
R=metrics.recall_score(y_trues,y_preds)
# F
F=metrics.fbeta_score(y_trues,y_preds,beta=1)


from typing import Union
import numpy as np

class Vector:

    def __init__(self, values: Union[list, tuple, iter]) -> None:
        self.values = tuple(values)
        self.n_dim = len(self.values)
        self.module = sum([x ** 2 for x in self.values])**(1/2)

    def pp(self, other_vector: 'Vector') -> int:
        """_summary_

        Args:
            other_vector (Vector): other vector.
        """
        try:
            if other_vector.n_dim == self.n_dim:
                return sum([i * k for i, k in zip(other_vector.values, self.values)])
                # for i, k in zip(other_vector.values, self.values):
                #     aux.append(i * k)
                # return sum(aux)
            else:
                raise ValueError(f"El vector ingresado es de dimensión "
                                 f"[{other_vector.n_dim}], se esperaba [{self.n_dim}]")
        except ValueError as ve:
            print(ve)
            return 0

    def pe(self, a: int) -> object:
        """_summary_

        Args:
            a (int):
        """
        return Vector([i * a for i in self.values])

    def angle(self, other_vector: 'Vector') -> float:
        """_summary_

        Args:
            other_vector (Vector): other vector.
        """
        return np.cos(self.pp(other_vector) / (self.module * other_vector.module))


import numpy as np

def get_distance_between_two_points(from_x=None, from_y=None, to_x=None, to_y=None):
    if (from_x is None) or (from_y is None) or (to_x is None) or (to_y is None):
        raise ValueError("Provide from_x, from_y, to_x and to_y values!")

    distance = np.sqrt(np.power(from_y - to_y, 2) + np.power(from_x - to_x, 2))
    return distance


import numpy as np

class Tensors:

    @staticmethod
    def get_first_invariant(tensor):
        result = None
        result = np.trace(tensor)

        return result

    @staticmethod
    def get_second_invariant(tensor):
        result = 0.0

        for i in range(3):
            for j in range(3):
                result += (
                    tensor[i, i]*tensor[j, j] - tensor[i, j]*tensor[j, i]
                )

        return result/2

    @staticmethod
    def get_third_invariant(tensor):
        result = 0.0
        result = np.linalg.det(tensor)

        return result

    @staticmethod
    def principal_stresses(tensor):
        coeff = [
            1,
            -Tensors.get_first_invariant(tensor),
            Tensors.get_second_invariant(tensor),
            -Tensors.get_third_invariant(tensor)
        ]

        sol = np.roots(coeff)

        return sol

    @staticmethod
    def principal_directions(tensor):
        eigenvect = np.linalg.eig(tensor)[0]

        return eigenvect


import numpy as np
import math

# 一些初值
initial1 = [0.1, 0.2, 0.9, 9.0]
initial2 = [[-0.1, 0.1], [-0.2, 0.2], [-2.0, 0.9], [0.9, 9.0]]
e = 1.0e-8

def f(x):
    return x**3/3-x

def df(x):
    return x**2-1

root = [0, 3**0.5, -3**0.5]

def Newton(x0, f, df, e):
    n = 1
    x1 = x0-f(x0)/df(x0)
    es = []
    while abs(x1-x0) >= e:
        es.append(abs(x1-x0))
        x0 = x1
        n += 1
        x1 = x0-f(x0)/df(x0)
    return x1, n, es

def Secant(x0, x1, f, e):
    n = 1
    x2 = x1-f(x1)*(x1-x0)/(f(x1)-f(x0))
    es = []
    while abs(x2-x1) >= e and abs(f(x2)) >= e:
        es.append(abs(x2-x1))
        x1, x0 = x2, x1
        n += 1
        x2 = x1-f(x1)*(x1-x0)/(f(x1)-f(x0))
    return x2, n, es

def ErrorOrder(es, order):
    return [es[i+1]/es[i]**order for i in range(len(es)-1)]

def main():
    for x in initial1:
        r, n, es = Newton(x, f, df, e)
        print("初值={{x0={:.1f}}}, 根={:.15e}, 迭代步数={:d}".format(x, r, n))
        print(ErrorOrder(es, 2))

    for x in initial2:
        r, n, es = Secant(x[0], x[1], f, e)
        print("初值={{x0={:.1f},x1={:.1f}}}, 根={:.15e}, 迭代步数={:d}".format(x[0], x[1], r, n))
        #print(ErrorOrder(es, 2))

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Mon Nov  2 09:57:31 2020

@author: Larisa
"""
import numpy as np
import sistemeEcuatiiLiniare as scl

# Lab 4
# Ex1
A = np.array([[2, -2, 0, 0],
              [2, 1, 0, 0],
              [-4, 4, 0, 0],
              [4, -1, 10 ** (-20), 10 ** (-20)]])
tol = 0
rang = scl.rang(A, tol)
print(f'Rang: {rang}')

tol = 10 ** (-20)
rang = scl.rang(A, tol)
print(f'Rang: {rang}')

tol = 10 ** (-10)
rang = scl.rang(A, tol)
print(f'Rang: {rang}\n')

# Ex3
A = np.array([[0, -3, -6, 4],
              [-1, -2, -1, 3],
              [-2, -3, 0, 3],
              [1, 4, 5, -9]])
tol = 0
rang = scl.rang(A, tol)
print(f'Rang: {rang}')

tol = 10 ** (-20)
rang = scl.rang(A, tol)
print(f'Rang: {rang}')

tol = 10 ** (-10)
rang = scl.rang(A, tol)
print(f'Rang: {rang}\n')

# Ex2
A = np.array([[0., 1., -2.],
              [1., -1., 1.],
              [1., 0., -1.]])
b = np.array([[4.], [6.], [2.]])
tol = 10 ** (-10)
scl.naturaSi(A, b, tol)

A = np.array([[1., -2., 3.],
              [-2., 3., -4.],
              [3., -4., 5.]])
b = np.array([[4.], [-5.], [6.]])
scl.naturaSi(A, b, tol)

A = np.array([[0., 1., 1.],
              [2., 1., 5.],
              [4., 2., 1.]])
b = np.array([[3.], [5.], [1.]])
tol = 10 ** (-10)
scl.naturaSi(A, b, tol)

Calcul Numeric (CN)/Laborator/Laborator 3, 4/lab4.py


# EM.py is the Expectation-Maximization algorithm, written from Andrew Ng's notes: http://cs229.stanford.edu/notes/cs229-notes7b.pdf. Also see http://cs229.stanford.edu/notes/cs229-notes8.pdf for mathematical derivations and http://cs229.stanford.edu/section/gaussians.pdf for a description of Gaussian isocontours.

import random,pickle
from copy import copy
from math import fmod

import numpy as np
import matplotlib.pyplot as plot

# tol - Stopping criteria.
# k - The number of gaussian densities.
# multinomial - The latent random variable.
# gaussians - The current estimates for the k gaussian densities.

class EM():

    def __init__(self):
        self.X = None
        self.tol = None
        self.k = None
        self.multinomial = None
        self.gaussians = None

    def set_X(self,X):
        self.X = X

    def set_tolerance(self,tol=0.1):
        self.tol = tol

    def initialize_EM(self,k):
        self.k = k

        # initialize multinomial
        self.multinomial = {}
        prob = 1.0/self.k
        for j in xrange(self.k):
            self.multinomial[j] = prob

        # initialize gaussians
        # build hypercube from min/max of dataset
        number_of_rows = self.X.shape[0]
        number_of_columns = self.X.shape[1]
        mins = [float('inf'),]*number_of_columns
        maxes = [float('-inf'),]*number_of_columns
        for i in xrange(number_of_rows):
            x = self.X[i,:]
            for j in xrange(number_of_columns):
                val = x[j]
                current_min = mins[j]
                current_max = maxes[j]

                if val < current_min:
                    mins[j] = val

                if val > current_max:
                    maxes[j] = val

        # select a random mean from the min/max hypercube. also, initialize covariance, inverse, and determinant.
        self.gaussians = {}
        for j in xrange(self.k):
            self.gaussians[j] = {}

            # mean
            mean = []
            for inx in xrange(number_of_columns):
                min_j = mins[inx]
                max_j = maxes[inx]
                rand = random.uniform(min_j,max_j)
                mean.append(rand)

            mean = np.array(mean)
            mean.resize((number_of_columns,1))
            self.gaussians[j]['mean'] = mean

            # cov
            cov = []
            for inx in xrange(number_of_columns):
                vals = [0.0,]*number_of_columns
                vals[inx] = 1.0
                cov.append(vals)
            cov = np.array(cov)
            self.gaussians[j]['cov'] = cov
            self.gaussians[j]['inv'] = np.linalg.inv(cov)
            self.gaussians[j]['det'] = np.linalg.det(cov)

    def run_EM(self):
        current_likelihood = self._calculate_likelihood()
        while True:
            weights = self._E_step()
            self._M_step(weights)

            new_likelihood = self._calculate_likelihood()
            if np.isnan(new_likelihood):
                print 'POOR INITIALIZATION. REINITIALIZING...'
                self.initialize_EM(self.k)
                current_likelihood = self._calculate_likelihood()
                continue

            diff = new_likelihood - current_likelihood
            print 'DIFF LIKELIHOOD:',diff,new_likelihood
            if diff < self.tol:
                return new_likelihood

            current_likelihood = new_likelihood

    def _E_step(self):
        weights = {}
        for j in xrange(self.k):
            weights[j] = []

        number_of_rows = self.X.shape[0]
        number_of_columns = self.X.shape[1]
        for j in weights:
            weights_j = []
            for i in xrange(number_of_rows):
                x = self.X[i]
                x.resize((number_of_columns,1))
                prob_z_given_x = self._prob_z_given_x(x,j)
                weights_j.append((prob_z_given_x,i))

            weights[j] = weights_j

        return weights

    def _M_step(self,weights):
        self._update_phi(weights)
        self._update_mean(weights)
        self._update_cov(weights)

    def _calculate_likelihood(self):
        likelihood = 0.0

        number_of_rows = self.X.shape[0]
        number_of_columns = self.X.shape[1]
        for i in xrange(number_of_rows):
            x = self.X[i]
            x.resize((number_of_columns,1))
            for j in xrange(self.k):
                prob_x_given_z = self._prob_x_given_z(x,j)
                prob_z_given_x = self._prob_z_given_x(x,j)
                prob_z = self._prob_z(j)

                likelihood = likelihood + prob_z_given_x*np.log(prob_x_given_z*prob_z/prob_z_given_x)

        return likelihood

    # use Bayes' rule to get prob_z_given_x
    def _prob_z_given_x(self,x,j):
        prob_x_given_z = self._prob_x_given_z(x,j)
        prob_z = self._prob_z(j)

        sum_probs = 0.0
        for l in xrange(self.k):
            sum_probs = sum_probs + self._prob_x_given_z(x,l)*self._prob_z(l)

        prob_z_given_x = prob_x_given_z*prob_z/sum_probs

        return prob_z_given_x

    # I had to write the gaussian pdf. I could not get the gaussians pdf in scipy to work.
    def _prob_x_given_z(self,x,j):
        gaussian = self.gaussians[j]
        mean = gaussian['mean']
        inv = gaussian['inv']
        det = gaussian['det']
        number_of_columns = self.X.shape[1]

        diff = x - mean
        quadratic = np.dot(np.dot(diff.T,inv),diff)
        e = np.exp(-0.5*quadratic[0][0])
        c = 1.0/((2*np.pi)**(number_of_columns/2.0)*det**0.5)
        prob_x_given_z = c*e

        return prob_x_given_z

    def _prob_z(self,j):
        prob_z = self.multinomial[j]

        return prob_z

    def _update_phi(self,weights):
        for j in weights:
            phi_j = 0.0
            for prob_z_given_x,_ in weights[j]:
                phi_j = phi_j + prob_z_given_x

            number_of_rows = self.X.shape[0]
            phi_j = phi_j/number_of_rows
            self.multinomial[j] = phi_j

    def _update_mean(self,weights):
        number_of_columns = self.X.shape[1]
        for j in weights:
            sum_probs = 0.0
            mean_j = np.zeros((number_of_columns,1))
            for prob_z_given_x,i in weights[j]:
                x = self.X[i]
                x.resize((number_of_columns,1))
                mean_j = mean_j + prob_z_given_x*x
                sum_probs = sum_probs + prob_z_given_x

            mean_j = mean_j/sum_probs
            self.gaussians[j]['mean'] = mean_j

    def _update_cov(self,weights):
        number_of_columns = self.X.shape[1]
        for j in weights:
            sum_probs = 0.0
            cov_j = np.zeros((number_of_columns,number_of_columns))
            mean_j = self.gaussians[j]['mean']
            for prob_z_given_x,i in weights[j]:
                x = self.X[i]
                x.resize((number_of_columns,1))
                diff = x - mean_j
                product = prob_z_given_x*diff*diff.T
                cov_j = cov_j + product
                sum_probs = sum_probs + prob_z_given_x

            cov_j = cov_j/sum_probs
            inv_j = np.linalg.inv(cov_j)
            det_j = np.linalg.det(cov_j)
            self.gaussians[j]['cov'] = cov_j
            self.gaussians[j]['inv'] = inv_j
            self.gaussians[j]['det'] = det_j

    def generate_example(self,number_of_clusters=3,sample_size_per_cluster=500,number_of_runs=3):
        # assemble data
        X = np.array([])
        X.resize((0,2))
        for inx in xrange(number_of_clusters):
            x_1 = random.sample(range(0,15),1)[0]
            x_2 = random.sample(range(0,15),1)[0]
            cov = random.uniform(-0.75,0.75)
            mean = np.array([x_1,x_2])
            cov = np.array([[1,cov],[cov,1]])
            res = np.random.multivariate_normal(mean,cov,sample_size_per_cluster)
            X = np.row_stack((X,res))

        # run EM a few times
        likelihoods = []
        for i in xrange(number_of_runs):
            print 'RUN:',i
            self.set_X(X)
            self.set_tolerance()
            self.initialize_EM(number_of_clusters)
            likelihood = self.run_EM()
            s_gaussians = pickle.dumps(self.gaussians)
            pair = (likelihood,s_gaussians)
            likelihoods.append(pair)
            print '---\n'

        # select run with highest likelihood
        likelihoods.sort(reverse=True)
        s_gaussians = likelihoods[0][1]
        gaussians = pickle.loads(s_gaussians)

        # plot the original data, X, and the estimated means from EM.
        plot.scatter(self.X[:,0],self.X[:,1],s=0.5,color='grey')
        for j in gaussians:
            mean_j = gaussians[j]['mean']
            plot.scatter(mean_j[0][0],mean_j[1][0])

        plot.show()

        # plot random samples from the densities estimated by EM and the means estimated by EM.
        colors = ['red','orange','green','purple','grey']
        X_colors = []
        X = np.array([])
        X.resize((0,2))
        for j in gaussians:
            color = colors[int(fmod(j,len(colors)))]
            mean_j = gaussians[j]['mean']
            cov_j = gaussians[j]['cov']
            res = np.random.multivariate_normal(mean_j.T[0],cov_j,sample_size_per_cluster)
            X = np.row_stack((X,res))
            for inx in xrange(sample_size_per_cluster):
                X_colors.append(color)

        X_colors = np.array(X_colors)
        X_colors.resize((number_of_clusters*sample_size_per_cluster,1))

        plot.scatter(X[:,0],X[:,1],color=X_colors[:,0],s=0.5)
        for j in gaussians:
            mean_j = gaussians[j]['mean']
            plot.scatter(mean_j[0][0],mean_j[1][0])

        plot.show()


""" Tuple representation class
"""
import numpy as np

class Tuple:
    """ (x, y, z, w)

        for w == 0, it's a vector
        for w == 1, it's a point
    """

    def __init__(self, x: float, y: float, z: float, w: float, narray=None):
        if narray is not None:
            self._t = narray
        else:
            self._t = np.array((x, y, z, w))

    @property
    def x(self):
        return self._t[0]

    @property
    def y(self):
        return self._t[1]

    @property
    def z(self):
        return self._t[2]

    @property
    def w(self):
        return self._t[3]

    def __eq__(self, other):
        return (self._t == other._t).all()

    def __add__(self, other):
        return Tuple(0, 0, 0, 0, self._t + other._t)

    def __neg__(self):
        return Tuple(0, 0, 0, 0, -self._t)

    def __mul__(self, other):
        return Tuple(0, 0, 0, 0, self._t * other)

    def __truediv__(self, other):
        return Tuple(0, 0, 0, 0, self._t / other)

class Point(Tuple):
    """ a point is a tuple with w == 1
    """

    def __init__(self, x: float, y: float, z: float):
        super().__init__(x, y, z, 1)

    def __sub__(self, other):
        """ Returns a Vector
        """
        return Tuple(0, 0, 0, 0, self._t - other._t)

class Vector(Tuple):
    """ a vector is a tuple with w == 0
    """

    def __init__(self, x: float, y: float, z: float):
        super().__init__(x, y, z, 0)
        self._magnitude = None
        self._norm = None

    @property
    def magnitude(self):
        if not self._magnitude:
            self._magnitude = np.linalg.norm(self._t)
        return self._magnitude

    @property
    def norm(self):
        if not self._norm:
            result = self._t / self.magnitude
            self._norm = Vector(result[0], result[1], result[2])
        return self._norm

    def __sub__(self, other):
        return Tuple(0, 0, 0, 0, self._t - other._t)

def make_tuple(x: float, y: float, z: float, w: float) -> Tuple:
    if w == 0:
        return Vector(x, y, z)
    elif w == 1:
        return Point(x, y, z)

    return Tuple(x, y, z, w)

def dot(a: Tuple, b: Tuple) -> float:
    return np.dot(a._t, b._t)

def cross(a: Vector, b: Vector) -> Vector:
    v = np.cross(a._t[0:3], b._t[0:3])
    print(v[0], v[1], v[2])
    return Vector(v[0], v[1], v[2])


"""
testts for measure module

"""

#imports
import pytest
import molecool
import numpy as np

def test_calculate_distance():
    r1 = np.array([0,0,0])
    r2 = np.array([0,1,0])

    expected_distance = 1

    calculate_distance = molecool.calculate_distance(r1,r2)

    assert expected_distance == calculate_distance

#test for calculate_angle_function
# use points (0,0,-1), (0,0,0), (1,0,0)
#expected angle 90

def test_calculate_angle():
    r1 = np.array([1,0,0])
    r2 = np.array([0,0,0])
    r3 = np.array([0,1,0])

    expected_value = 90

    calculate_angle = molecool.calculate_angle(r1,r2,r3, degrees = True)

    assert pytest.approx(expected_value) == calculate_angle

#decorator #need to write coorodinates
@pytest.mark.parametrize("p1, p2, p3, expected_angle", [
    (np.array([np.sqrt(2)/2, np.sqrt(2)/2, 0]), np.array([0, 0, 0]), np.array([1, 0, 0]), 45),
    (np.array([0, 0, -1]), np.array([0, 1, 0]), np.array([1, 0, 0]), 60  ),
    (np.array([np.sqrt(3)/2, (1/2), 0]), np.array([0, 0, 0]), np.array([1, 0, 0]), 30),
])

def test_calculate_angle_many():
    calculate_angle = molecool.calculate_angle(p1,p2,p3, degrees = True)

    assert pytest.approx(expected_angle) == calculate_angle


import numpy as np
import unittest

# --------------------------------------------------------------------------------}
# --- Functions to reproduce Matlab's gradient, div, and curl: axis 0 and 1 inverted!
# --------------------------------------------------------------------------------{
def matlab_gradient_1d(F,*args,**kwargs):
    """ Computes gradient of a 1d vector field in python like matlab gradient function.
    call syntax:
       gradient(F) or gradient(F,dx) or gradient(F,x)
    """
    shape_in=F.shape
    G=np.gradient(F.ravel(),*args,**kwargs)
    return G.reshape(shape_in)

def matlab_gradient_2d(F,*args,**kwargs):
    """ Computes gradient of a 1d vector field in python like matlab gradient function.

    call syntax:
       gradient(F) or gradient(F,dx,dy) or gradient(F,x,y)
    """
    G=np.gradient(F.T,*args,**kwargs)
    G[0]=G[0].T
    G[1]=G[1].T
    return G

def matlab_div_2d(*args):
    """ Computes divergence of a 2D vector field in python like matlab div function

    call syntax:
       div(U,V) or div(X,Y,U,V)
    """
    if len(args)==2:
        sz = args[0].shape
        dx = np.arange(1,sz[1]+1) # NOTE: matlab, x is along the 2nd dimension..
        dy = np.arange(1,sz[0]+1)
        U=args[0]
        V=args[1]
    elif len(args)==4:
        dx = args[0][0,:];
        dy = args[1][:,0];
        U=args[2]
        V=args[3]
    else:
        raise Exception('Input 2 or 4 arguments')
    #retval  = matlab_gradient_2d (U, dx, dy)[0]
    #retval += matlab_gradient_2d(V.T, dy, dx)[0].T;
    return np.gradient(U, dx, axis=1) +  np.gradient(V,  dy, axis=0)

def matlab_curl_2d(*args):
    """ Computes curl of a D vector field in python like matlab curl function

    call syntax:
       curl(U,V) or curl(X,Y,U,V)
    """
    if len(args)==2:
        sz = args[0].shape
        dx = np.arange(1,sz[1]+1) # NOTE: matlab, x is along the 2nd dimension..
        dy = np.arange(1,sz[0]+1)
        U=args[0]
        V=args[1]
    elif len(args)==4:
        dx = args[0][0,:];
        dy = args[1][:,0];
        U=args[2]
        V=args[3]
    else:
        raise Exception('Input 2 or 4 arguments')
    dFx_dy = matlab_gradient_2d(U.T, dy, dx)[0].T
    dFy_dx = matlab_gradient_2d(V, dx, dy)[0]
    rot_z  = dFy_dx - dFx_dy
    av     = rot_z / 2
    return rot_z, av

# --------------------------------------------------------------------------------}
# --- Pythonic versions
# --------------------------------------------------------------------------------{
def div(f):
    """
    TODO
    Computes the divergence of the vector field f, corresponding to dFx/dx + dFy/dy + ...
    :param f: List of ndarrays, where every item of the list is one dimension of the vector field
    :return: Single ndarray of the same shape as each of the items in f, which corresponds to a scalar field
    """
    num_dims = len(f)
    return np.ufunc.reduce(np.add, [np.gradient(f[i], axis=i) for i in range(num_dims)])

# --------------------------------------------------------------------------------}
# --- TESTS
# --------------------------------------------------------------------------------{
class TestVectorAnalysis(unittest.TestCase):
    def test_grad1d(self):
        x  = np.array([0,1,3,4])
        x2 = x**2
        np.testing.assert_almost_equal(matlab_gradient_1d(x2),np.array([1,4.5,7.5,7]))
        np.testing.assert_almost_equal(matlab_gradient_1d(x2,2),np.array([0.5,2.25,3.75,3.5]))
        # NOTE NOTE: this test below fails due to boundary effects, to replaced by python values..
        # np.testing.assert_almost_equal(matlab_gradient_1d(x2,x),np.array([1,3,5,7]))
        np.testing.assert_almost_equal(matlab_gradient_1d(x2,x),np.array([1,2,6,7]))

        x  = np.array([[0,1,3,4]])
        x2 = x**2
        np.testing.assert_almost_equal(matlab_gradient_1d(x2),np.array([[1,4.5,7.5,7]]))
        np.testing.assert_almost_equal(matlab_gradient_1d(x2.T),np.array([[1,4.5,7.5,7]]).T)

    def test_grad2d(self):
        x = np.arange(-1,1.5,0.5)
        y = np.arange( 0,1.21,0.2)
        X,Y = np.meshgrid(x,y)
        Z = X * np.exp(-X**2 - Y**2)
        # --- Gradient, no dimensions
        px,py = matlab_gradient_2d(Z)
        px_ref =np.array([[ -0.0215210,  0.1839397],[-0.0206771,  0.1767273]])
        py_ref = np.array([ [ 0.014425,  0.015269] ,[ 0.027197,  0.028788]])
        np.testing.assert_almost_equal(px[:2,:2],px_ref,decimal=6)
        np.testing.assert_almost_equal(py[:2,:2],py_ref,decimal=6)
        # --- Gradient, uni dimensions
        px,py = matlab_gradient_2d(Z,0.5)
        px_ref = np.array([ [  -0.0430419,    0.3678794], [  -0.0413542,    0.3534547]])
        py_ref = np.array([ [   0.0288495,    0.0305372], [   0.0543933,    0.0575753]])
        np.testing.assert_almost_equal(px[:2,:2],px_ref,decimal=7)
        np.testing.assert_almost_equal(py[:2,:2],py_ref,decimal=7)
        # --- Gradient, bi dimensions
        px,py = matlab_gradient_2d(Z,0.5,0.2);
        px_ref = np.array([ [  -0.0430419,    0.3678794], [  -0.0413542,    0.3534547]])
        py_ref = np.array([ [   0.0721238,    0.0763430], [   0.1359832,    0.1439382]])
        np.testing.assert_almost_equal(px[:2,:2],px_ref,decimal=7)
        np.testing.assert_almost_equal(py[:2,:2],py_ref,decimal=7)
        px,py = matlab_gradient_2d(Z,x,y);
        np.testing.assert_almost_equal(px[:2,:2],px_ref,decimal=7)
        np.testing.assert_almost_equal(py[:2,:2],py_ref,decimal=7)

    def test_div2d(self):
        x = np.arange(-1,1.5,0.5)
        y = np.arange( 0,1.21,0.2)
        X,Y = np.meshgrid(x,y)
        U =    X * np.exp(-X**2 - Y**2)
        V = Y**2 * np.exp(-X**2 - Y**2)

        # --- Divergence, no dimensions
        div=matlab_div_2d(U,V)
        div_ref = np.array([ [  -0.0073828,    0.2138703], [   0.0044018,    0.2298194]])
        np.testing.assert_almost_equal(div[:2,:2],div_ref,decimal=7)

        # --- Divergence, dimensions
        div=matlab_div_2d(X,Y,U,V)
        div_ref = np.array([ [   0.0276490,    0.5175322], [   0.0840403,    0.6189148]])
        np.testing.assert_almost_equal(div[:2,:2],div_ref,decimal=7)

    def test_curl2d(self):
        x = np.arange(-1,1.5,0.5)
        y = np.arange( 0,1.21,0.2)
        X,Y = np.meshgrid(x,y)
        U =    X * np.exp(-X**2 - Y**2)
        V = Y**2 * np.exp(-X**2 - Y**2)
        # --- Curl, no dimensions
        curl,_=matlab_curl_2d(U,V)
        curl_ref = np.array([ [  -0.0144248,   -0.0152686], [  -0.0114043,   -0.0166409]])
        np.testing.assert_almost_equal(curl[:2,:2],curl_ref,decimal=7)

        # --- Curl, dimensions
        curl,_=matlab_curl_2d(X,Y,U,V)
        curl_ref = np.array([ [  -0.0721238,   -0.0763430], [  -0.1043984,   -0.1196448]])
        np.testing.assert_almost_equal(curl[:2,:2],curl_ref,decimal=7)

if __name__=='__main__':
    unittest.main()


"""
author: Fabian Schaipp

This is Condat's algorithm for the proximal operator of the Fused Lasso regularizer (https://lcondat.github.io/publis/Condat-fast_TV-SPL-2013.pdf)
"""

import numpy as np
from numba import jit
#from tick.prox import ProxTV

@jit(nopython = True)
def condat_method(y,lam):

    N = len(y)

    x = np.zeros(N)
    k = k0 = kplus = kminus = 0

    vmin = y[0] - lam
    vmax = y[0] + lam
    umin = lam
    umax = -lam

    while 0 < 1:

        while k == N-1:
            if umin < 0:
                x[k0:kminus +1] = vmin
                kminus += 1
                k = k0 = kminus
                umin = lam; vmin = y[k]; umax = y[k] + lam - vmax
            elif umax > 0:
                x[k0:kplus+1] = vmax
                kplus += 1
                k  = k0 = kplus
                umax = -lam; vmax = y[k]; umin = y[k] - lam - vmin
            else:
                x[k0:] = vmin + umin/(k-k0+1)
                return x

            if k == N-1:
                x[k] = vmin + umin
                return x

        if y[k+1] + umin - vmin < -lam:
            x[k0:kminus+1] = vmin
            kminus += 1
            k = kplus = k0 = kminus
            vmin = y[k]; vmax = y[k] + 2*lam
            umin = lam; umax = -lam

        elif y[k+1] + umax -vmax > lam:
            x[k0:kplus+1] = vmax
            kplus += 1
            k  = kminus = k0 = kplus
            vmin = y[k] - 2*lam; vmax = y[k]
            umin = lam; umax = -lam

        else:
            k += 1
            umin = umin + y[k] - vmin
            umax = umax + y[k] - vmax
            if umin >= lam:
                vmin += (umin-lam)/(k-k0+1); umin = lam; kminus = k
            if umax <= -lam:
                vmax += (umax+lam)/(k-k0+1); umax = -lam; kplus = k

    return

# for testing

# def objective(x, y, l1):
#     res = l1* abs(x[1:] - x[0:-1]).sum() + .5*np.linalg.norm(x-y)**2

#     return res

#l1 = .1
#
# for i in range(1000):
#     print(i)
#     y = np.random.rand(100)
#     x = condat_method(y, l1)
#     x2 = ProxTV(l1).call(y)

#     print(np.linalg.norm(x-x2))
#     print("Condat function correct: ", objective(x, y, l1) <= objective(x2, y, l1) + 1e-5)
#     assert  np.linalg.norm(x-x2) <= 1e-10


import numpy as np

def f(x):
    return (np.sin(np.sqrt(100 * x))) ** 2

def adaptive_simpson(a, b, e):
    n = 2
    h = []
    er = []
    S = []
    T = []
    i = []
    h.append((b - a) / n)
    s = (f(a) + f(b)) * (1 / 3)
    t = (2 / 3) * f(a + h[0])
    S.append(s)
    T.append(t)
    i.append(h[0] * (S[0] + (2 * T[0])))
    er.append("   ------------")
    for m in range(1, 1000, 1):
        sum2 = 0
        n = 2 * n
        h.append((b - a) / n)
        S.append(S[m - 1] + T[m - 1])
        for k in range(1, n, 2):
            sum2 = sum2 + f(a + (k * h[m]))
        T.append((2 / 3) * sum2)
        i.append(h[m] * (S[m] + (2 * T[m])))
        er.append((1 / 15) * abs(i[m] - i[m - 1]))
        if er[m] <= e:
            break
    return i, er

i1, error = adaptive_simpson(0, 1, 1e-6)
p = 1
print("n    I                           error")
for j in range(0, len(i1)):
    p = 2 * p
    print(p, " ", i1[j], "  ", error[j])

Ankit Khandelwal_HW2/exercise 3/exercise 3c.py


'''
plot the beta distribution of posterior probability
'''

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

def plot_beta(a, b):
    x = np.linspace(0, 1, 10000)
    y = st.beta.pdf(x, a, b)
    plt.plot(x, y, 'r-', lw=5, alpha=0.6, label='beta pdf')
    plt.show()
    return x, y

def interval_estimate(prob, a, b):
    print('The {} interval estimate is {}'.format(prob, st.beta.interval(prob, a, b)))

def beta_low(prob, a, b):
    print('The probability that the proportion of monitoring sites with detectable algae levels'
          ' π is smaller than {} is {}.'.format(prob, round(st.beta.cdf(prob, a, b), 3)))

def get_mean(a, b):
    print(st.beta.mean(a, b))

if __name__ == '__main__':
    # plot_beta(2, 10)
    interval_estimate(0.95, 74, 380)
    # beta_low(0.19999, 46, 239)
    # print(st.beta.mean(46, 239))
    get_mean(74, 380)


from __future__ import division
import numpy as np
import scipy as sp
from scipy.io import loadmat
from dml.tool import pca,featurenormal,projectData,recoverData,displayData
import matplotlib.pyplot as plt
data = loadmat("../data/face/ex7data1.mat")
X = data['X'].transpose()
plt.axis([0,8,0,8])

'''
	simple test for PCA
'''

X_norm,mu,sigma=featurenormal(X)
plt.plot(X[0,:],X[1,:],'o')
#print mu,sigma,'======='
U,S = pca(X_norm)
#print U,S
tt=mu+U[:,0]
tp=mu+U[:,1]

plt.plot([mu[0],tt[0]] ,[mu[1],tt[1]])
plt.plot([mu[0],tp[0]],[mu[1],tp[1]])
plt.show()
plt.axis([-3,3,-3,3])

plt.plot(X_norm[0,:],X_norm[1,:],'o')

Z=projectData(X_norm, U, 1);
X_rec=recoverData(Z, U, 1);
plt.plot(X_rec[0,:],X_rec[1,:],'o')
for i in range(X.shape[1]):
	plt.plot([X_norm[0,i],X_rec[0,i]],[X_norm[1,i],X_rec[1,i]],'r')
plt.show()

'''
   face images dimension reduction
'''
data = loadmat("../data/face/ex7faces.mat")
X = data['X'].transpose()

X_norm,mu,sigma=featurenormal(X)
fig = plt.figure()
fig.add_subplot(1,2, 1)
plt.imshow(displayData(X_norm[:,:100]) , cmap='gray')

#PCA STEPs
[U, S] = pca(X_norm);
print S
K = 100;
Z = projectData(X_norm, U, K);
X_rec  = recoverData(Z, U, K);

fig.add_subplot(1,2, 2)
plt.imshow(displayData(X_rec[:,:100]) , cmap='gray')
plt.show()


#%%
"""
Created on August 25 2021
The Heston Hull-White model used for pricing of European type of FX options using
the COS method and comparisons with the Monte Carlo simulation.

This code is purely educational and comes from "Financial Engineering" course by L.A. Grzelak
The course is based on the book “Mathematical Modeling and Computation
in Finance: With Exercises and Python and MATLAB Computer Codes”,
by C.W. Oosterlee and L.A. Grzelak, World Scientific Publishing Europe Ltd, 2019.
@author: Lech A. Grzelak
"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as st
import scipy.special as sp
import scipy.integrate as integrate
import scipy.optimize as optimize
import enum

# This class defines puts and calls
class OptionType(enum.Enum):
    CALL = 1.0
    PUT = -1.0

# Black-Scholes Call option price
def BS_Call_Put_Option_Price(CP,S_0,K,sigma,tau,r):
    if K is list:
        K = np.array(K).reshape([len(K),1])
    d1    = (np.log(S_0 / K) + (r + 0.5 * np.power(sigma,2.0))
    * tau) / (sigma * np.sqrt(tau))
    d2    = d1 - sigma * np.sqrt(tau)
    if CP == OptionType.CALL:
        value = st.norm.cdf(d1) * S_0 - st.norm.cdf(d2) * K * np.exp(-r * tau)
    elif CP == OptionType.PUT:
        value = st.norm.cdf(-d2) * K * np.exp(-r * tau) - st.norm.cdf(-d1)*S_0
    return value

# Implied volatility method
def ImpliedVolatilityBlack76(CP,marketPrice,K,T,S_0):
    # To determine initial volatility we interpolate define a grid for sigma
    # and interpolate on the inverse
    sigmaGrid = np.linspace(0.0,5.0,5000)
    optPriceGrid = BS_Call_Put_Option_Price(CP,S_0,K,sigmaGrid,T,0.0)
    sigmaInitial = np.interp(marketPrice,optPriceGrid,sigmaGrid)
    print("Strike = {0}".format(K))
    print("Initial volatility = {0}".format(sigmaInitial))

    # Use determined input for the local-search (final tuning)
    func = lambda sigma: np.power(BS_Call_Put_Option_Price(CP,S_0,K,sigma,T,0.0) - marketPrice, 1.0)
    impliedVol = optimize.newton(func, sigmaInitial, tol=1e-15)
    print("Final volatility = {0}".format(impliedVol))
    if impliedVol > 2.0:
        impliedVol = 0.0
    return impliedVol

def CallPutOptionPriceCOSMthd_StochIR(cf,CP,S0,tau,K,N,L,P0T):
    # cf   - characteristic function as a functon, in the book denoted as \varphi
    # CP   - C for call and P for put
    # S0   - Initial stock price
    # tau  - time to maturity
    # K    - list of strikes
    # N    - Number of expansion terms
    # L    - size of truncation domain (typ.:L=8 or L=10)
    # P0T  - zero-coupon bond for maturity T.

    # reshape K to a column vector
    if K is not np.array:
        K = np.array(K).reshape([len(K),1])

    #assigning i=sqrt(-1)
    i = np.complex(0.0,1.0)
    x0 = np.log(S0 / K)

    # truncation domain
    a = 0.0 - L * np.sqrt(tau)
    b = 0.0 + L * np.sqrt(tau)

    # sumation from k = 0 to k=N-1
    k = np.linspace(0,N-1,N).reshape([N,1])
    u = k * np.pi / (b - a)

    # Determine coefficients for Put Prices
    H_k = CallPutCoefficients(OptionType.PUT,a,b,k)
    mat = np.exp(i * np.outer((x0 - a) , u))
    temp = cf(u) * H_k
    temp[0] = 0.5 * temp[0]
    value = K * np.real(mat.dot(temp))

    # we use call-put parity for call options
    if CP == OptionType.CALL:
        value = value + S0 - K * P0T

    return value

# Determine coefficients for Put Prices
def CallPutCoefficients(CP,a,b,k):
    if CP==OptionType.CALL:
        c = 0.0
        d = b
        coef = Chi_Psi(a,b,c,d,k)
        Chi_k = coef["chi"]
        Psi_k = coef["psi"]
        if a < b and b < 0.0:
            H_k = np.zeros([len(k),1])
        else:
            H_k      = 2.0 / (b - a) * (Chi_k - Psi_k)
    elif CP==OptionType.PUT:
        c = a
        d = 0.0
        coef = Chi_Psi(a,b,c,d,k)
        Chi_k = coef["chi"]
        Psi_k = coef["psi"]
        H_k      = 2.0 / (b - a) * (- Chi_k + Psi_k)

    return H_k

def Chi_Psi(a,b,c,d,k):
    psi = np.sin(k * np.pi * (d - a) / (b - a)) - np.sin(k * np.pi * (c - a)/(b - a))
    psi[1:] = psi[1:] * (b - a) / (k[1:] * np.pi)
    psi[0] = d - c

    chi = 1.0 / (1.0 + np.power((k * np.pi / (b - a)) , 2.0))
    expr1 = np.cos(k * np.pi * (d - a)/(b - a)) * np.exp(d)  - np.cos(k * np.pi
                  * (c - a) / (b - a)) * np.exp(c)
    expr2 = k * np.pi / (b - a) * np.sin(k * np.pi *
                        (d - a) / (b - a))   - k * np.pi / (b - a) * np.sin(k
                        * np.pi * (c - a) / (b - a)) * np.exp(c)
    chi = chi * (expr1 + expr2)

    value = {"chi":chi,"psi":psi }
    return value

def EUOptionPriceFromMCPathsGeneralizedFXFrwd(CP,S,K):
    # S is a vector of Monte Carlo samples at T
    result = np.zeros([len(K),1])
    if CP == OptionType.CALL:
        for (idx,k) in enumerate(K):
            result[idx] = np.mean(np.maximum(S-k,0.0))
    elif CP == OptionType.PUT:
        for (idx,k) in enumerate(K):
            result[idx] = np.mean(np.maximum(k-S,0.0))
    return result

def GeneratePathsHHWFXHWEuler(NoOfPaths,NoOfSteps,T,frwdFX,v0,vbar,kappa,gamma,lambdd,lambdf,etad,etaf,rhoxv,rhoxrd,rhoxrf,rhovrd,rhovrf,rhordrf):
    Wx = np.zeros([NoOfPaths, NoOfSteps+1])
    Wv = np.zeros([NoOfPaths, NoOfSteps+1])
    Wrd = np.zeros([NoOfPaths, NoOfSteps+1])
    Wrf = np.zeros([NoOfPaths, NoOfSteps+1])

    V = np.zeros([NoOfPaths, NoOfSteps+1])
    FX = np.zeros([NoOfPaths, NoOfSteps+1])
    V[:,0] = v0
    FX[:,0] = frwdFX

    dt = T / float(NoOfSteps)
    Bd = lambda t,T: 1.0/lambdd*(np.exp(-lambdd*(T-t))-1.0)
    Bf = lambda t,T: 1.0/lambdf*(np.exp(-lambdf*(T-t))-1.0)

    cov = np.array([[1.0, rhoxv,rhoxrd,rhoxrf],[rhoxv,1.0,rhovrd,rhovrf],\
                        [rhoxrd,rhovrd,1.0,rhordrf],[rhoxrf,rhovrf,rhordrf,1.0]])

    time = np.zeros([NoOfSteps+1])

    for i in range(0,NoOfSteps):
        Z = np.random.multivariate_normal([.0,.0,.0,.0],cov,NoOfPaths)
        if NoOfPaths > 1:
            Z[:,0] = (Z[:,0] - np.mean(Z[:,0])) / np.std(Z[:,0])
            Z[:,1] = (Z[:,1] - np.mean(Z[:,1])) / np.std(Z[:,1])
            Z[:,2] = (Z[:,2] - np.mean(Z[:,2])) / np.std(Z[:,2])
            Z[:,3] = (Z[:,3] - np.mean(Z[:,3])) / np.std(Z[:,3])

        Wx[:,i+1] = Wx[:,i] + np.power(dt, 0.5)*Z[:,0]
        Wv[:,i+1] = Wv[:,i] + np.power(dt, 0.5)*Z[:,1]
        Wrd[:,i+1] = Wrd[:,i] + np.power(dt, 0.5)*Z[:,2]
        Wrf[:,i+1] = Wrf[:,i] + np.power(dt, 0.5)*Z[:,3]

        # Variance process- Euler discretization
        V[:,i+1] = V[:,i] + kappa*(vbar - V[:,i])*dt \
                          + gamma*rhovrd*etad*Bd(time[i],T)*np.sqrt(V[:,i]) * dt \
                          + gamma* np.sqrt(V[:,i]) * (Wv[:,i+1]-Wv[:,i])
        V[:,i+1] = np.maximum(V[:,i+1],0.0)

        # FX process under the forward measure
        FX[:,i+1] = FX[:,i] *(1.0 + np.sqrt(V[:,i])*(Wx[:,i+1]-Wx[:,i]) \
                   -etad*Bd(time[i],T)*(Wrd[:,i+1]-Wrd[:,i])\
                   +etaf*Bf(time[i],T)*(Wrf[:,i+1]-Wrf[:,i]))
        time[i+1] = time[i] +dt

    paths = {"time":time,"FX":FX}
    return paths

# Exact expectation E(sqrt(V(t)))
def meanSqrtV_3(kappa,v0,vbar,gamma):
    delta = 4.0 *kappa*vbar/gamma/gamma
    c= lambda t: 1.0/(4.0*kappa)*gamma*gamma*(1.0-np.exp(-kappa*(t)))
    kappaBar = lambda t: 4.0*kappa*v0*np.exp(-kappa*t)/(gamma*gamma*(1.0-np.exp(-kappa*t)))
    temp1 = lambda t: np.sqrt(2.0*c(t))* sp.gamma((1.0+delta)/2.0)/sp.gamma(delta/2.0)*sp.hyp1f1(-0.5,delta/2.0,-kappaBar(t)/2.0)
    return temp1

def C_H1HW_FX(u,tau,kappa,gamma,rhoxv):
    i = np.complex(0.0,1.0)

    D1 = np.sqrt(np.power(kappa-gamma*rhoxv*i*u,2.0)+(u*u+i*u)*gamma*gamma)
    g  = (kappa-gamma*rhoxv*i*u-D1)/(kappa-gamma*rhoxv*i*u+D1)
    C  = (1.0-np.exp(-D1*tau))/(gamma*gamma*(1.0-g*np.exp(-D1*tau)))\
        *(kappa-gamma*rhoxv*i*u-D1)
    return C

def ChFH1HW_FX(u,tau,gamma,Rxv,Rxrd,Rxrf,Rrdrf,Rvrd,Rvrf,lambdd,etad,lambdf,etaf,kappa,vBar,v0):
    i  = np.complex(0.0,1.0)
    C = lambda u,tau: C_H1HW_FX(u,tau,kappa,gamma,Rxv)
    Bd = lambda t,T: 1.0/lambdd*(np.exp(-lambdd*(T-t))-1.0)
    Bf = lambda t,T: 1.0/lambdf*(np.exp(-lambdf*(T-t))-1.0)
    G = meanSqrtV_3(kappa,v0,vBar,gamma)

    zeta = lambda t: (Rxrd*etad*Bd(t,tau) - Rxrf*etaf*Bf(t,tau))*G(t) + \
                    Rrdrf*etad*etaf*Bd(t,tau)*Bf(t,tau) - 0.5*(etad**2.0*Bd(t,tau)**2.0+etaf**2.0*Bf(t,tau)**2.0)

    # Integration in the function A(u,tau)
    int1=np.zeros([len(u),1],dtype=np.complex_)
    N = 500
    z=np.linspace(0.0+1e-10,tau-1e-10,N)

    temp1 =lambda z1: kappa*vBar + Rvrd*gamma*etad*G(tau-z1)*Bd(tau-z1,tau)
    temp2 =lambda z1, u: -Rvrd*gamma*etad*G(tau-z1)*Bd(tau-z1,tau)*i*u
    temp3 =lambda z1, u:  Rvrf*gamma*etaf*G(tau-z1)*Bf(tau-z1,tau)*i*u
    f = lambda z1,u: (temp1(z1)+temp2(z1,u)+temp3(z1,u))*C(u,z1)

    value1 =integrate.trapz(np.real(f(z,u)),z).reshape(u.size,1)
    value2 =integrate.trapz(np.imag(f(z,u)),z).reshape(u.size,1)
    int1=(value1 + value2*i)

    """
    for k in range(0,len(u)):
        temp1 = kappa*vBar + Rvrd*gamma*etad*G(tau-z)*Bd(tau-z,tau)
        temp2 = -Rvrd*gamma*etad*G(tau-z)*Bd(tau-z,tau)*i*u[k]
        temp3 = Rvrf*gamma*etaf*G(tau-z)*Bf(tau-z,tau)*i*u[k]
        f = (temp1+temp2+temp3)*C(u[k],z)
        value1 =integrate.trapz(np.real(f),z)
        value2 =integrate.trapz(np.imag(f),z)
        int1[k]=(value1 + value2*i)
    """
    int2 = (u**2.0 + i*u)*integrate.trapz(zeta(tau-z),z)
    A = int1 + int2

    cf = np.exp(A + v0*C(u,tau))
    return cf

def GenerateStrikes(frwd,Ti):
    c_n = np.array([-1.5, -1.0, -0.5,0.0, 0.5, 1.0, 1.5])
    return frwd * np.exp(0.1 * c_n * np.sqrt(Ti))

def mainCalculation():
    CP  = OptionType.CALL
    T       = 5.0

    NoOfPaths = 1000
    NoOfSteps = (int)(T*50)

    # Settings for the COS method
    N = 500
    L = 8

    # Market Settings
    P0Td = lambda t: np.exp(-0.02*t)
    P0Tf = lambda t: np.exp(-0.05*t)
    y0      = 1.35
    frwdFX  = y0*P0Tf(T)/P0Td(T)
    kappa   = 0.5
    gamma   = 0.3
    vbar    = 0.1
    v0      = 0.1

    # HW model settings
    lambdd  = 0.01
    lambdf  = 0.05
    etad    = 0.007
    etaf    = 0.012

    # correlations
    Rxv   = -0.4
    Rxrd  = -0.15
    Rxrf  = -0.15
    Rvrd  = 0.3
    Rvrf  = 0.3
    Rrdrf = 0.25

    # Strikes
    K = GenerateStrikes(frwdFX,T)
    K = np.array(K).reshape([len(K),1])

    # number of repeated simulations for different Monte Carlo seed
    SeedV = range(0,20)
    optMCM = np.zeros([len(SeedV),len(K)])
    for (idx,seed) in enumerate(SeedV):
        print('Seed number = {0} out of {1}'.format(idx,len(SeedV)))
        np.random.seed(seed)
        paths = GeneratePathsHHWFXHWEuler(NoOfPaths,NoOfSteps,T,frwdFX,v0,vbar,kappa,gamma,lambdd,lambdf,etad,etaf,Rxv,Rxrd,Rxrf,Rvrd,Rvrf,Rrdrf)
        frwdfxT = paths["FX"]
        optMC = P0Td(T)* EUOptionPriceFromMCPathsGeneralizedFXFrwd(CP,frwdfxT[:,-1],K)
        optMCM[idx,:]= np.squeeze(optMC)

    # Average of the runs + standard deviation
    optionMC_E = np.zeros([len(K)])
    optionMC_StDev = np.zeros([len(K)])
    for (idx,k) in enumerate(K):
        optionMC_E[idx] = np.mean(optMCM[:,idx])
        optionMC_StDev[idx]  = np.std(optMCM[:,idx])

    # Value from the COS method
    cf = lambda u: ChFH1HW_FX(u,T,gamma,Rxv,Rxrd,Rxrf,Rrdrf,Rvrd,Rvrf,lambdd,etad,lambdf,etaf,kappa,vbar,v0)
    valCOS_H1HW = P0Td(T)*CallPutOptionPriceCOSMthd_StochIR(cf, CP, frwdFX, T, K, N, L,1.0)

    # checking martingale property
    EyT = P0Td(T)/P0Tf(T)*EUOptionPriceFromMCPathsGeneralizedFXFrwd(CP,frwdfxT[:,-1],[0.0])
    print("Martingale check: P_d(T)/P_f(T)*E[FX(T)] ={0:.4f} and y0 ={1}".format(EyT[0][0],y0))
    print("Maturity chosen to T={0}".format(T))
    for (idx,k) in enumerate(K):
        print("Option price for strike K={0:.4f} is equal to: COS method = {1:.4f} and MC = {2:.4f} with stdDev = {3:.4f}".format(k[0],valCOS_H1HW[idx][0],optionMC_E[idx],optionMC_StDev[idx]))

    plt.figure(1)
    plt.plot(K,optionMC_E)
    plt.plot(K,valCOS_H1HW,'--r')
    plt.grid()
    plt.legend(['Monte Carlo Option Price','COS method'])
    plt.title("Fx Option prices")

    # implied volatilities
    IVCos =np.zeros([len(K),1])
    IVMC =np.zeros([len(K),1])
    for (idx,k) in enumerate(K):
        priceCOS = valCOS_H1HW[idx]/P0Td(T)
        IVCos[idx] = ImpliedVolatilityBlack76(CP,priceCOS ,k,T,frwdFX)*100.0
        priceMC = optionMC_E[idx]/P0Td(T)
        IVMC[idx] = ImpliedVolatilityBlack76(CP,priceMC ,k,T,frwdFX)*100.0

    plt.figure(2)
    plt.plot(K,IVMC)
    plt.plot(K,IVCos,'--r')

    plt.grid()
    plt.legend(['IV-COS','IV-MC'])
    plt.title("Fx Implied volatilities")

mainCalculation()

Lecture 10-Foreign Exchange (FX) and Inflation/Materials/H1_HW_COS_vs_MC_FX.py


import numpy as np
from collections import Counter
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.stats as stats
from pandas.api.types import is_object_dtype, is_numeric_dtype
from scipy import stats

def grubbs_remove_outlier(X, alpha):
    '''
    This function runs Grubbs’ Statistical Test to detect and remove the outlier based on significance level
    and grubbs value & critical value.

    Parameters
    ----------
    X : ndarray
        A python list to be tested for outliers.

    alpha : float
        The significance level.

    Returns
    -------
    X : ndarray
        The original array with one outlier removed.
    '''

    # Calculte Grubbs Statistics Value
    std_dev = np.std(X)
    avg_X = np.mean(X)
    abs_val_minus_avg = abs(X - avg_X)
    max_of_deviations = max(abs_val_minus_avg)
    grubbs_value = max_of_deviations / std_dev

    # Find Max Index
    max_index = np.argmax(abs_val_minus_avg)
    outlier = X[max_index]

    # Calculte Grubbs Critical Value
    size = len(X)
    t_dist = stats.t.ppf(1 - alpha / (2 * size), size - 2)
    numerator = (size - 1) * np.sqrt(np.square(t_dist))
    denominator = np.sqrt(size) * np.sqrt(size - 2 + np.square(t_dist))
    grubbs_critical_value = numerator / denominator

    if grubbs_value > grubbs_critical_value:
        print('{} is an outlier. Grubbs value  > Grubbs critical: {:.4f} > {:.4f} \n'.format(
            outlier, grubbs_value, grubbs_critical_value))
        #X = np.delete(X, np.where(X == outlier) )
        X.remove(outlier)

    return X

def grubbs(X, max_outliers,  alpha=0.05, name=''):
    '''
    Grubbs’ Test is also known as the maximum normalized residual test or extreme studentized deviate test is a test used to detect outliers in a univariate data set assumed to come from a normally distributed population. This test is defined for the hypothesis:

    Ho: There are no outliers in the dataset
    Ha: There is exactly one outlier in the dataset

    Parameters
    ----------
    X : ndarray
        A python list to be tested for outliers.

    alpha : float
        The significance level.

    max_outliers: int
    The maximum  number of iteration that will be deleted by this method

    Returns
    -------
    X : ndarray
        The original array with outliers removed.

    outliers: the list of all outliers
    '''

    init_x_len = len(X)
    reach_end = False

    clean_X = X.copy().tolist()

    for i in range(max_outliers):
        clean_X = grubbs_remove_outlier(clean_X, alpha)
        if len(X) == len(clean_X):
            reach_end = True
            break

    if name != "":
        name = " in " + name + " dataset"
    else:
        name = " in the dataset "

    outlisers = []

    if (len(X)-len(clean_X) == 0):
        print("we can not reject the null hypothesis and there are no outliers " + name)

    else:
        print("we reject the null hypothesis and there are outliers "+name)
        outlisers = [value for value in X if value not in clean_X]

        if reach_end:
            print("Found all Grubbs outliers, " +
                  str(len(X)-len(clean_X)) + " outliers")
        else:
            print("Found some Grubbs outliers, " + str(len(X)-len(clean_X)
                                                       ) + " outliers. increase the max itration to find more")

    return clean_X, outlisers

def detect_outliers(X, factor=1.5):

    # list to store outlier indices
    outlier_indices = []

    # Get the 1st quartile (25%)
    Q1 = np.percentile(X, 25)

    # Get the 3rd quartile (75%)
    Q3 = np.percentile(X, 75)

    # Get the Interquartile range (IQR)
    IQR = Q3 - Q1

    # Define our outlier step
    outlier_step = 1.5 * IQR

    return X[(X < Q1 - outlier_step) | (X > Q3 + outlier_step)]

def missing_values_table(df):
    """
    Returns a pandas DataFrame containing the counts of missing values by column in the input DataFrame

    """
    mis_val = df.isnull().sum()
    mis_val_percent = 100 * df.isnull().sum() / len(df)
    mis_val_table = pd.concat([mis_val, mis_val_percent], axis=1)
    mis_val_table_ren_columns = mis_val_table.rename(
        columns={0: 'Missing Values', 1: '% of Total Values'})
    mis_val_table_ren_columns = mis_val_table_ren_columns[
        mis_val_table_ren_columns.iloc[:, 1] != -1].sort_values(
        '% of Total Values', ascending=False).round(1)
    # print("Your selected dataframe has " + str(df.shape[1]) + " column/s and " + str(df.shape[0]) + " row/s.\n"
    #       "There are " + str(mis_val_table_ren_columns.shape[0]) +
        #   " columns that have missing values.")
    return mis_val_table_ren_columns

#logic preparation
def logic_preparation(x_df, no_treatment_columns = [] ):
    """
    This function provides a recommendation on how to deal with each feauture's missing values based on the missing values' percentage.

    """
    missing = missing_values_table(x_df).iloc[:,1]
    df_missing = pd.DataFrame(columns=['name', 'missing','treatment', 'comment' ])

    df_missing["missing"] = missing.tolist()
    df_missing["name"] = missing.index.values.tolist()
    df_missing.treatment = "Treat"
    df_missing.comment = "Treat missing values based on statistical distribution (mean or median) or mode"
    df_missing.treatment.loc[df_missing["missing"] >= 70] = "Drop"
    df_missing.treatment.loc[df_missing["missing"] < 5] = "Omit"
    df_missing.treatment.loc[df_missing["missing"] == 0] = "Do Nothing"
    df_missing.comment.loc[df_missing["missing"] == 0] = "Feature has no missing values"
    df_missing.comment.loc[df_missing["missing"] >= 70] = "Drop the feature as it is missing more than 70 pecent of the values"
    df_missing.comment.loc[df_missing["missing"] < 5] = "Omit observations with this feature missing (less than 5 percent of total records)"
    df_missing.comment.loc[df_missing["missing"] == 0] = "Feature has no missing values"

    columns_to_drop = []
    columns_to_drop = df_missing.name.loc[df_missing["treatment"] == "Drop"].tolist()
    columns_to_drop = list(dict.fromkeys(columns_to_drop))

    for i in range(len(df_missing.index)):
        if (df_missing.iloc[i]["name"] in no_treatment_columns and
                df_missing.iloc[i]["treatment"] == "Treat"):
            df_missing.treatment.iloc[i] = "Omit"

    columns_to_omit_missing_values = []
    columns_to_omit_missing_values = df_missing.name.loc[df_missing["treatment"] == "Omit"].tolist()
    columns_to_omit_missing_values = list(dict.fromkeys(columns_to_omit_missing_values))

    return df_missing

def eval_df(df_o,
            normal_test_alpha = 0.05,
            grubbs_alpha = 0.05,
            grubbs_max_outliers = 20,
            iqr_factor = 2.5 ):
    df = df_o.copy()

    output = {}

    output['name']          = []
    output['count']         = []
    output['missing']       = []
    output['missing_prc']   = []
    output['types']         = []
    output['q1']            = []
    output['q2']            = []
    output['q3']            = []
    output['is_normal']     = []
    output['unique']        = []
    output['top']           = []
    output['freq']          = []
    output['min']           = []
    output['max']           = []
    output['mean']          = []
    output['sd']            = []
    output['variance']      = []
    output['skewness']      = []
    output['kurtosis']      = []
    output['iqrs']          = []
    output['has_outliers']  = []
    outliers   = {}

    def get_stat(name):
        """

        This function outputs feature statistics such as IQR, min, max, mean, variance, skewness, kurtosis, etc..

        """
        if(name in output['name']):
            return

        mis_val         = df.isnull().sum()
        mis_val_percent = 100 * df.isnull().sum() / len(df)

        df_describe = df.describe(include='all', datetime_is_numeric=True)

        output['name'].append(name)
        output['count'].append(df_describe[name]['count'])
        output['missing'].append( mis_val[name])
        output['missing_prc'].append(mis_val_percent[name])

        if is_numeric_dtype(df[name]):
            output['types'].append('Numerical')

            num_item_describe  =stats.describe(df[name])
            output['q1'].append(df_describe[name]['25%'])
            output['q3'].append(df_describe[name]['75%'])

            iqr = df_describe[name]['75%'] - df_describe[name]['25%']

            _, p = stats.normaltest(df[name])

            is_normal = True
            if (p < normal_test_alpha):
                is_normal = False
                output['is_normal'].append("Non-normal")
            else:
                is_normal = True
                output['is_normal'].append("Normal")

            output['unique'].append(np.nan)
            output['top'].append(np.nan)
            output['freq'].append(np.nan)
            output['min'].append(df_describe[name]['min'])
            output['max'].append(df_describe[name]['max'])
            output['mean'].append(df_describe[name]['mean'])
            output['sd'].append(df_describe[name]['std'])
            output['q2'].append(df_describe[name]['50%'])
            output['variance'].append(num_item_describe.variance)
            output['skewness'].append(num_item_describe.skewness)
            output['kurtosis'].append(num_item_describe.kurtosis)
            output['iqrs'].append(iqr)
            #to skip outlier detection in binary vars or very small range features
            if(df_describe[name]['max'] -  df_describe[name]['min'] <= 1):
                pass

            elif (is_normal):
                _ , grubbs_outliers = grubbs( df[name].dropna() , grubbs_max_outliers, grubbs_alpha,name)

                if len(grubbs_outliers) > 0:
                    outliers[name]  = grubbs_outliers
            else:
                iqr_outliers =  detect_outliers(df[name],iqr_factor)

                if len(iqr_outliers) > 0:
                    outliers[name]  = iqr_outliers

            if name in outliers:
                output['has_outliers'].append('True')
            else:
                output['has_outliers'].append('False')

        else:
            output['types'].append('Categorical')
            output['unique'].append(df_describe[name]['unique'])
            output['top'].append(df_describe[name]['top'])
            output['freq'].append(df_describe[name]['freq'])
            output['min'].append(np.nan)
            output['max'].append(np.nan)
            output['mean'].append(np.nan)
            output['sd'].append(np.nan)
            output['variance'].append(np.nan)
            output['skewness'].append(np.nan)
            output['kurtosis'].append(np.nan)
            output['q1'].append(np.nan)
            output['q2'].append(np.nan)
            output['q3'].append(np.nan)
            output['iqrs'].append(np.nan)
            output['is_normal'].append(np.nan)
            output['has_outliers'].append(np.nan)

    vfunc = np.vectorize(get_stat)
    vfunc(df.columns)

    descriptive_statistics = pd.DataFrame({'Name'     : output['name'],
                                           'Type'     : output['types'] ,
                                           'Count'    : output['count'],
                                           'Unique'   : output['unique'],
                                           'Top'      : output['top'],
                                           'Freq'     : output['freq'] ,
                                           'Min'      : output['min'],
                                           'Max'      : output['max'],
                                           'Mean'     : output['mean'],
                                           'SD'       : output['sd'],
                                           'Variance' : output['variance'] ,
                                           'Skewness' : output['skewness'],
                                           'Kurtosis' : output['kurtosis'],
                                           '25%'      : output['q1'],
                                           '50%'      : output['q2'],
                                           '75%'      : output['q3'] ,
                                           'IQR'      : output['iqrs'] ,
                                           '# Missing Values' : output['missing'],
                                           '% Missing Values' : output['missing_prc'],
                                           'Is Normal': output['is_normal'],
                                           'Has Outliers':output['has_outliers']
                                           })

    return descriptive_statistics , logic_preparation(df) , outliers

def clean_data(df_train, df_test):
    """
    This function is used to handle outliers and missing data, for outliers, it will clean the outliers in the training and testing datasets
    based on the output of eval_df function. For missing values, the function is also used for imputation of missing values by Mean/Median/Mode.

    """

    df_clean = df_train.copy()
    descriptive_statistics, df_treatment_logic, outliers = eval_df(df_clean)

    df_test_clean = df_test.copy()
    descriptive_statistics_test, df_treatment_logic_test, outliers_test = eval_df(
        df_test_clean.copy())

    print("Number of rows before handling the outliers values in training " +
          str(df_clean.shape[0])+"\n")
    print("Number of rows before handling the outliers values in testing " +
          str(df_test_clean.shape[0])+"\n")

    # clean all outliers in training data set
    for item in df_clean.columns:
        if item in outliers:
            df_clean = df_clean.loc[~df_clean[item].isin(outliers[item])]

    # clean all outliers in test data set
    for item in df_test_clean.columns:
        if item in outliers_test:
            df_test_clean = df_test_clean.loc[~df_test_clean[item].isin(
                outliers_test[item])]

    print("Number of rows after handling outliers (before handling missing data) in training " +
          str(df_clean.shape[0])+"\n")
    print("Number of rows after handling outliers (before handling missing data) in testing " +
          str(df_test_clean.shape[0])+"\n")

    for column in df_clean.columns:
        if(df_treatment_logic.loc[df_treatment_logic.name == column, :].treatment.tolist()[0] == "Drop"):
            df_clean = df_clean.drop(column, axis=1)
            df_test_clean = df_test_clean.drop(column, axis=1)
        elif(df_treatment_logic.loc[df_treatment_logic.name == column, :].treatment.tolist()[0] == "Omit"):
            df_clean = df_clean.dropna(subset=[column])
        elif(df_treatment_logic_test.loc[df_treatment_logic_test.name == column, :].treatment.tolist()[0] == "Omit"):
            df_test_clean = df_test_clean.dropna(subset=[column])

    # Outliers Treatment

    # impute based if the data is normal or not
    for item in df_clean.columns:
        row = descriptive_statistics[descriptive_statistics['Name'] == item]
        is_norm = str(row['Is Normal'].to_string(index=False))

        if (is_norm == 'Normal'):
            df_clean[item].fillna((df_clean[item].mean()), inplace=True)
            df_test_clean[item].fillna((df_clean[item].mean()), inplace=True)

        elif (is_norm == 'Non-normal'):
            df_clean[item].fillna((df_clean[item].median()), inplace=True)
            df_test_clean[item].fillna((df_clean[item].median()), inplace=True)
        else:
            df_clean[item].fillna((df_clean[item].mode()[0]), inplace=True)
            df_test_clean[item].fillna((df_clean[item].mode()[0]), inplace=True)

    print("Number of rows after handling the missing values in training " +
          str(df_clean.shape[0])+"\n")
    print("Number of rows after handling the missing values in testing " +
          str(df_test_clean.shape[0])+"\n")

    return df_clean, df_test_clean


import unittest

import numpy as np
import sympy as sp

from utils.jacobians import JacobianCalculator

class TestJacobianCalculatorModule(unittest.TestCase):
    def testEqualityOfMethods(self):
        jc = JacobianCalculator("RzTx", [1, 0], simplify=False)
        J_num = jc.calculate_numeric()
        J_skew = jc.calculate_scew()
        Res = np.array(sp.simplify(J_skew - J_num), dtype=np.float)

        self.assertTrue(np.allclose(Res, np.zeros((6, 1))))

        jc = JacobianCalculator("RzTxRzTx", [1, 0, 1, 0], simplify=False)
        J_num = jc.calculate_numeric()
        J_skew = jc.calculate_scew()
        Res = np.array(sp.simplify(J_skew - J_num), dtype=np.float)

        self.assertTrue(np.allclose(Res, np.zeros((6, 2))))

        jc = JacobianCalculator("RzTxRzTxTyRz", [1, 1, 1, 1, 0, 1])
        J_num = jc.calculate_numeric()
        J_skew = jc.calculate_scew()
        Res = np.array(sp.simplify(J_skew - J_num), dtype=np.float)

        self.assertTrue(np.allclose(Res, np.zeros((6, 5))))

    def testWithTransforms(self):
        T_base = sp.Matrix([
            [1, 0, 0, 3],
            [0, 1, 0, 6],
            [0, 0, 1, -9],
            [0, 0, 0, 1]
        ])

        T_tool = sp.Matrix([
            [0, 1, 0, 2],
            [1, 0, 0, -4],
            [0, 0, 1, 6],
            [0, 0, 0, 1]
        ])

        jc = JacobianCalculator("RzTxRzTx",
                                [1, 0, 1, 0],
                                T_base=T_base,
                                T_tool=T_tool,
                                simplify=False)
        J_num = jc.calculate_numeric()
        J_skew = jc.calculate_scew()

        Res = np.array(sp.simplify(J_skew - J_num), dtype=np.float)

        self.assertTrue(np.allclose(Res, np.zeros((6, 2))))

    def testNumericValues(self):
        jc = JacobianCalculator("RzTxRzTx",
                                [1, 0, 1, 0],
                                simplify=False)

        values = (0.0, 1.0, 0.0, 2.0)
        J = jc.calculate_numerically(values)
        J = np.array(J, dtype=np.float)

        RealJ = np.array([[0.0, 0.0],
                          [3.0, 2.0],
                          [0.0, 0.0],
                          [0.0, 0.0],
                          [0.0, 0.0],
                          [1.0, 1.0]])

        self.assertTrue(np.allclose(J, RealJ))


# Calculating strength of interaction between variables using sklearn_gbmi

#https://github.com/ralphhaygood/sklearn-gbmi/blob/master/example.ipynb

import numpy as np

import pandas as pd

from sklearn.ensemble import GradientBoostingRegressor

from sklearn_gbmi import *

DATA_COUNT = 10000

RANDOM_SEED = 137

TRAIN_FRACTION = 0.9

np.random.seed(RANDOM_SEED)

xs = pd.DataFrame(np.random.uniform(size = (DATA_COUNT, 3)))
xs.columns = ['x0', 'x1', 'x2']

y = pd.DataFrame(xs.x0*xs.x1 + xs.x2 + pd.Series(0.1*np.random.randn(DATA_COUNT)))
y.columns = ['y']

train_ilocs = range(int(TRAIN_FRACTION*DATA_COUNT))

test_ilocs = range(int(TRAIN_FRACTION*DATA_COUNT), DATA_COUNT)

gbr_1 = GradientBoostingRegressor(random_state = RANDOM_SEED)

gbr_1.fit(xs.iloc[train_ilocs], y.y.iloc[train_ilocs])

h_all_pairs(gbr_1, xs.iloc[train_ilocs])

#function to return dataframe with specified no of top interacting features
def get_interactions(model, df, top_N=10):
    d = h_all_pairs(model, df)
    df = pd.DataFrame(columns=['interaction','h'])
    for key in d.keys():
        cols = str(key[0])+"-"+str(key[1])
        df = (

            df.append({'interaction': cols,
                       'h':d[key]},
                        ignore_index=True)

              )

    return df.sort_values(by='h', ascending=False).nlargest(top_N, 'h').set_index('interaction')

get_interactions(gbr_1,xs.iloc[train_ilocs] )


# ## The `numpy` library
#
# `numpy` is a Python library designed to manipulate multi-dimensional arrays.
# It is often used together with the `scipy` library, containing tools for
# linear algebra, integration, optimisation, signal analysis and more.
#
# We present here very simple Numpy functionalities. For more details, refer to
# the official documentation:
# https://docs.scipy.org/doc/numpy/reference/

# %%
import numpy as np

# ## Types of embedded arrays
#
# A Numpy array can be made of an iterable structure (table, tuple, list). Numpy
# becomes powerful because all elements in the array have the same type
# (called `dtype`)
# %%
# Types in a Python array
table = [2, 7.3, 4]
type(table), [(value, type(value)) for value in table]

# %%
# Types in a Numpy array
np_table = np.array(table)
(type(np_table), np_table.dtype, [(value, type(value)) for value in np_table])

# ## Performance
#
# Python is commonly blamed for being slow at runtime. This comes for many
# factors, in particular the flexibility of the language, the numerous checks
# made at runtime (Python cannot assume anything about your data) and dynamic
# typing. Now with Numpy, we know with the `dtype`, the types of all elements in
# the array: arithmetic operations can be processed and accelerated in C.
# %%
table = [i for i in range(1, 10000000)]
np_array = np.array(table)

# %%
%%timeit
# Note: the timeit operator is specific to IPython and Jupyter. It is not
# valid Python syntax but will be interpreted correctly in the console if
# put at the very beginning of a cell.
double = [x * 2 for x in table]

# %%
%%timeit
np_double = 2 * np_array

# ## Views on Numpy array
#
# It is possible to work on views of a previously built array. We use the word
# "view" because modifying data in the "view" also modifies data in the original
# array.
# %%
np_array = np.array([[i + 2 * j for i in range(5)] for j in range(4)])
np_array

# %%
sub = np_array[0:2, 2:4]
sub

# %%
sub = np_array[:3, 2:]
sub

# %%
# Modifying sub values also modifies the original array
sub[:] = 0
np_array

# ## Matrix operations
#
# %%
a = np.array([[4, 6, 7, 6]])
b = np.array([[i + j for i in range(4)] for j in range(4)])

print(f"{a}\n with dimensions {a.shape}")
print(f"{b}\n with dimensions {b.shape}")

# %%
# Dot product (or matrix product)
np.dot(a, b)

# %%
# Term by term addition, multiplication, etc.
2 * a

# %%
a + a

# %%
a * a

# %%
# Dimensions are broadcast when needed:
# (details in https://www.xoolive.org/python/numpy/)

a * b

# This is NOT a dot product!!

# %%
import numpy.linalg  # noqa: E402

a = np.array([[abs(i - j) for i in range(5)] for j in range(5)])
inv_a = numpy.linalg.inv(a)

a * inv_a

# %%
inv_a @ a  # @ operator (called `matmul`), equivalent to np.dot(inv_a, a)

# ## Further references
#
# - Numpy tutorial:
#   https://github.com/rougier/numpy-tutorial
# - From Python to Numpy:
#   https://www.labri.fr/perso/nrougier/from-python-to-numpy/
# - 100 Numpy exercices:
#   https://github.com/rougier/numpy-100/
# - The dedicated chapter in https://www.xoolive.org/python/numpy/


import numpy as np

class Logistic_Regression:
    def __init__(self):
        self.w = []

    # sigmoid function
    def sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    # logistic function
    def hx(self, w, X):
        weights_num = X.shape[1] + 1
        z_arrays = [1] * weights_num
        z_arrays[0] = w[0]
        for i in range(weights_num - 1):
            z_arrays[i + 1] = w[i + 1] * np.array(X[:, i])
        z = np.array(sum(z_arrays[j] for j in range(weights_num)))
        return self.sigmoid(z)

    # cost function - binary cross entropy
    def cost(self, w, X, Y):
        y_pred = self.hx(w, X)
        return -1 * sum(Y * np.log(y_pred) + (1 - Y) * np.log(1 - y_pred))

    # gradient descent
    def grad_descent(self, w, X, Y):
        y_pred = self.hx(w, X)
        weights_num = X.shape[1] + 1
        grad = [0] * weights_num
        grad[0] = -1 * sum(Y * (1 - y_pred) - (1 - Y) * y_pred)
        for i in range(weights_num - 1):
            grad[i + 1] = -1 * sum(Y * (1 - y_pred) * X[:, i] - (1 - Y) * y_pred * X[:, i])
        return grad

    def fit(self, X, Y, w, lr, max_iter):
        weights_num = X.shape[1] + 1
        iter = 0
        while iter < max_iter:
            w_prev = w.copy()
            grad = self.grad_descent(w_prev, X, Y)
            for index in range(weights_num):
                w[index] = w[index] - lr * grad[index]
            iter += 1
        self.w = w.copy()

    def predict(self, X):
        predictions = self.hx(self.w, X)
        return np.around(predictions).astype(int)


from scipy.cluster.vq import kmeans, vq
import matplotlib.pyplot as plt
import seaborn as sns

cluster_centers, _ = kmeans(df[["scaled_x" , "scaled_y"]], 3)

df["cluster_labels"] , _ = vq(df[["scaled_x" , "scaled_y"]] , cluster_centers)

sns.scatterplot(x="scaled_x" , y="scaled_y" , hue="cluster_labels" , data=df)
plt.show()

# how many clusters???
# creating an elbow plot

distortions = []

num_clusters = range(2,7)

for i in num_clusters:
    centroids, distortion = kmeans(df[["scaled_x" , "scaled_y"]], i)
    distortions.append(distortion)

elbow_plot_data = pd.DataFrame({'num_clusters': num_clusters,
                                'distortions': distortions})

sns.lineplot(x="num_clusters" , y="distortions" , data=elbow_plot_data)

plt.show()

# uniform clustering patterns

# Generate cluster centers
cluster_centers, distortion = kmeans(mouse[["x_scaled" , "y_scaled"]], 3)

# Assign cluster labels
mouse['cluster_labels'], distortion_list = vq(mouse[["x_scaled","y_scaled"]], cluster_centers)

# Plot clusters
sns.scatterplot(x='x_scaled', y='y_scaled',
                hue='cluster_labels', data = mouse)
plt.show()


#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Distributed under terms of the MIT license.
# Copyright 2021 Chao Pan.

import numpy as np

# short for norm
def norm(x, axis=None):
    return np.linalg.norm(x, axis=axis)

# distance in poincare disk
def poincare_dist(u, v, eps=1e-5):
    d = 1 + 2 * norm(u-v)**2 / ((1 - norm(u)**2) * (1 - norm(v)**2) + eps)
    return np.arccosh(d)

# compute symmetric poincare distance matrix
def poincare_distances(embedding):
    n = embedding.shape[0]
    dist_matrix = np.zeros((n, n))
    for i in range(n):
        for j in range(i+1, n):
            dist_matrix[i][j] = poincare_dist(embedding[i], embedding[j])
    return dist_matrix

# convert array from poincare disk to hyperboloid
def poincare_pts_to_hyperboloid(Y, eps=0, metric='minkowski'):
    X = Y.T
    d, n = np.shape(X)
    Z = np.zeros((d+1, n))
    if metric == "minkowski":
        for i in range(n):
            Z[0, i] = (1 + np.sum(X[:, i]**2)) / (1 - np.sum(X[:, i]**2) + eps)
            Z[1:, i] = 2 * X[:, i] / (1 - np.sum(X[:, i]**2) + eps)
    elif metric == "lorentz":
        for i in range(n):
            Z[d, i] = (1 + np.sum(X[:, i]**2)) / (1 - np.sum(X[:, i]**2) + eps)
            Z[0:d, i] = 2 * X[:, i] / (1 - np.sum(X[:, i]**2) + eps)
    else:
        raise NotImplementedError
    return Z.T

# define hyperboloid bilinear form
def hyperboloid_dot(u, v):
    return np.dot(u[:-1], v[:-1]) - u[-1]*v[-1]

# define alternate minkowski/hyperboloid bilinear form
def minkowski_dot(u, v):
    return u[0]*v[0] - np.dot(u[1:], v[1:])

# hyperboloid distance function
def hyperboloid_dist(u, v, eps=1e-6, metric='lorentz'):
    if metric == 'minkowski':
        dist = np.arccosh(-1*minkowski_dot(u, v))
    else:
        dist = np.arccosh(-1*hyperboloid_dot(u, v))
    if np.isnan(dist):
        # print('Hyperboloid dist returned nan value')
        return eps
    else:
        return dist

# compute symmetric hyperboloid distance matrix
def hyperboloid_distances(embedding):
    n = embedding.shape[0]
    dist_matrix = np.zeros((n, n))
    for i in range(n):
        for j in range(i+1, n):
            dist_matrix[i][j] = hyperboloid_dist(embedding[i], embedding[j])
    return dist_matrix

# project within disk
def proj(theta, eps=0.1):
    if norm(theta) >= 1:
        theta = theta/norm(theta) - eps
    return theta

# helper function to generate samples
def generate_data(n, radius=0.7, hyperboloid=False):
    theta = np.random.uniform(0, 2*np.pi, n)
    u = np.random.uniform(0, radius, n)
    r = np.sqrt(u)
    x = r * np.cos(theta)
    y = r * np.sin(theta)
    init_data = np.hstack((x.reshape(-1, 1), y.reshape(-1, 1)))
    if hyperboloid:
        return poincare_pts_to_hyperboloid(init_data)
    else:
        return init_data


import numpy as np
import random
import math

def ackley_function(transposed_decimal_pop_input):
    """Ackley Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:

        ackley = 0
        sum_1 = 0
        sum_2 = 0
        for xi in individual:
            sum_1 = sum_1 + xi**2
            sum_2 = sum_2 + math.cos(2*math.pi*xi)
        term_1 = -20*math.exp(-0.2*(math.sqrt(1/len(individual)*sum_1)))
        term_2 = - math.exp((1/len(individual))*sum_2)
        ackley = term_1 + term_2 + 20 + math.exp(1)
        y.append(ackley)
    return y

def bohachevsky_function(transposed_decimal_pop_input):
    """Bohachevsky Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:

        bohachevsky = 0
        term_1 = 0
        term_2 = 0

        for idx, xi in enumerate(individual):
            if idx == (len(individual)-1):
                break
            term_1 = (xi**2 + 2*((individual[idx + 1])**2)-0.3*math.cos(3*math.pi*xi))
            term_2 = (-0.4*math.cos(4*math.pi*individual[idx + 1]) + 0.7)
            bohachevsky = bohachevsky + (term_1 + term_2)

        y.append(bohachevsky)
    return y

def de_jong_step_a_function(transposed_decimal_pop_input):
    """De Jong Step - A Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:

        de_jong_step = 0

        for xi in individual:
            de_jong_step = de_jong_step + abs(round(xi))  # (abs) was used to adjust the function for minimization
        y.append(de_jong_step)
    return y

def de_jong_step_b_function(transposed_decimal_pop_input):
    """De Jong Step - B Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:

        de_jong_step_func_4 = 0
        term_1 = 0
        term_2 = random.gauss(0, 1)

        for idx, xi in enumerate(individual):
            term_1 = term_1 + idx*(xi**4)
        de_jong_step_func_4 = term_1 + term_2
        y.append(de_jong_step_func_4)
    return y

def griewangk_function(transposed_decimal_pop_input):
    """Griewangk Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:

        griewangk = 0
        term_1 = 0
        term_2 = 1

        for idx, xi in enumerate(individual):
            term_1 = term_1 + (xi**2)/4000
            term_2 = term_2 * math.cos(xi/(math.sqrt(idx + 1)))
        griewangk = 1 + term_1 - term_2
        y.append(griewangk)
    return y

def rastrigin_function(transposed_decimal_pop_input):
    """Rastrigin Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:
        rastrigin = 0
        term_1 = 10*len(individual)
        term_2 = 0

        for xi in individual:
            term_2 = term_2 + (xi**2)-10*(math.cos(2*xi*(math.pi)))
        rastrigin = term_1 + term_2
        y.append(rastrigin)
    return y

def rosenbrock_function(transposed_decimal_pop_input):
    """Rosenbrock Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:
        rosenbrock = 0
        for idx, xi in enumerate(individual):
            if idx == (len(individual)-1):
                break
            rosenbrock = rosenbrock + (100*(((xi**2)-individual[idx+1])**2)+((1-xi)**2))
        y.append(rosenbrock)
    return y

def schaffer_function(transposed_decimal_pop_input):
    """Schaffer Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:

        shaffer = 0
        parentheses = 0

        for xi in individual:
            parentheses = parentheses + xi**2
        term_1 = parentheses**0.25
        term_2 = (((math.sin(50*(parentheses**0.1)))**2)+1)
        schaffer = (term_1 * term_2)
        y.append(schaffer)
    return y

def schwefel_a_function(transposed_decimal_pop_input):
    """Schwefel - A Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:
        schwefel = 0
        for xi in individual:
            schwefel = schwefel + ((xi + 0.5)**2)
        y.append(schwefel)
    return y

def schwefel_b_function(transposed_decimal_pop_input):
    """Schwefel - B Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:

        schwefel = 0

        for idx, xi in enumerate(individual):
            schwefel = schwefel + (-xi*(math.sin(math.sqrt(abs(xi)))))
#        y.append(schwefel/len(individual)) # The division is not part of the function. However, it might be helpful for some. It enables the same result, independent of the number of inputs.
        y.append(schwefel)
    return y

def schwefels_pro_function(transposed_decimal_pop_input):
    """Schwefel's Pro Test Function"""

    y = []
    schwefels = 0
    for individual in transposed_decimal_pop_input:
        schwefels = 0
        term_2 = 0
        for idx, xi in enumerate(individual):
            j = 0
            term_1 = 0
            while j <= idx:
                term_1 = term_1 + individual[j]
                j = j + 1
            term_2 = term_1**2
            schwefels = schwefels + term_2
        y.append(schwefels)
    return y

def sphere_function(transposed_decimal_pop_input):
    """Sphere Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:
        y.append(sum(np.square(individual)))
    return y

def untitled_function(transposed_decimal_pop_input):
    """Untitled Test Function"""

    y = []
    for individual in transposed_decimal_pop_input:
        untitled = 0
        parentheses = 0
        term_1 = 0
        term_2 = 1

        for xi in individual:
            parentheses = parentheses + xi**2
            term_2 = term_2*(math.cos(20*math.pi*xi))
        term_1 = parentheses/2
        untitled = term_1 - term_2 + 2
        y.append(untitled)
    return y


import numpy as np

# Generates a 2D bit array of dimensions `h` by `w`, with a
# disk of ones with radius `r`, centered at point (x, y).
# All other points in the array are zero valued.
def disk(r, x, y, h, w):
    ind = np.indices((h, w))
    return r ** 2.0 >= (ind[0] - y) ** 2.0 + (ind[1] - x) ** 2.0

with open("input.txt", "r") as f:
    h, w, r = map(int, f.readline().split())
    # Convert x to 1 and everything else (.) to 0.
    grid = np.array([map(lambda c: 1 if c == 'x' else 0, f.readline()[:-1]) for j in range(h)])

max = 0
for i in range(h):
    for j in range(w):
        sum = (grid * disk(r, j, i, h, w)).sum()
        if sum >= max:
            max = sum
            x, y = j, i

print(x, y)


import numpy as np
import pandas as pd
import pickle as pkl
import math
from random import randint
import matplotlib.pyplot as plt
import sys
import os
from PIL import Image

__set_file = "images.csv"

__training_percent = 75

__params = ["image_name"]

__output = ["Output"]

__neural_network_model = "neural_network_model.pickle"

__images_folder = "handwritten_digit_corpus"

__number_mapping = {
	0: [1,0,0,0,0,0,0,0,0,0],
	1: [0,1,0,0,0,0,0,0,0,0],
	2: [0,0,1,0,0,0,0,0,0,0],
	3: [0,0,0,1,0,0,0,0,0,0],
	4: [0,0,0,0,1,0,0,0,0,0],
	5: [0,0,0,0,0,1,0,0,0,0],
	6: [0,0,0,0,0,0,1,0,0,0],
	7: [0,0,0,0,0,0,0,1,0,0],
	8: [0,0,0,0,0,0,0,0,1,0],
	9: [0,0,0,0,0,0,0,0,0,1]
}

__neural_network = {
	"layer_1": {
		"number_of_neurons": 16,
		"number_of_inputs": 784
	},
	"layer_2":{
		"number_of_neurons": 12,
		"number_of_inputs": 16
	},
	"layer_3":{
		"number_of_neurons": 10,
		"number_of_inputs": 12
	},
	"layers_count": 3
}

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

def sigmoid_diff(input_vec):
	return sigmoid(input_vec) * sigmoid(1 - input_vec)

def summation(weights, input_vec, bias_vec):
	intermediate_vec = np.matmul(weights, input_vec) + bias_vec
	return intermediate_vec

def initialize_weights(number_of_inputs, neurons_count):
	return np.random.randn(neurons_count, number_of_inputs)

def initialize_bias(neurons_count):
	return np.random.randn(neurons_count, 1)

def get_last_index(length_of_df):
	return math.floor(length_of_df * (__training_percent/100))

def calculate_error(actual_output, desired_output, last_neuron_layer_count):
	diff_error = actual_output - desired_output
	squared_error = diff_error ** 2
	squared_sum_error = np.sum(squared_error)
	avg_squared_sum_error = squared_sum_error / last_neuron_layer_count
	return avg_squared_sum_error

def error_derivative(actual_output, desired_output, last_neuron_layer_count):
	return ((actual_output - desired_output)/last_neuron_layer_count)

def store_model(model_perceptron):
	with open(__neural_network_model, 'wb') as handle:
		pkl.dump(model_perceptron, handle, protocol=pkl.HIGHEST_PROTOCOL)

def load_model():
	with open(__neural_network_model, 'rb') as handle:
		model_perceptron = pkl.load(handle)
	return model_perceptron

def get_data_set(set_type="train"):
	dataframe = pd.read_csv(__set_file)

	dataframe["Output"] = dataframe[["label"]].apply(lambda x: np.array([__number_mapping.get(x.to_frame().T.reset_index().label[0])]).T, axis=1)
	# dataframe = dataframe.sample(frac=1).reset_index(drop=True)

	__last_idx = get_last_index(len(dataframe))

	if set_type == "train":
		chuncked_dataframe = dataframe[:__last_idx]
	elif set_type == "test":
		chuncked_dataframe = dataframe[__last_idx:]

	return chuncked_dataframe[__params].as_matrix(), chuncked_dataframe[__output].as_matrix()

def confusion_matrix(actual_output, desired_output):
	y_actu = pd.Series(actual_output, name='Actual')
	y_pred = pd.Series(desired_output, name='Predicted')
	df_confusion = pd.crosstab(y_actu, y_pred)

	return df_confusion

def get_output_value(output_vector):
	return (output_vector.index(max(output_vector)) + 1)

def get_input_image_as_vec(image_file_name):
	file_path = os.path.join(__images_folder, image_file_name)
	image_buffer = Image.open(file_path).convert('L')
	image_array = np.array(image_buffer)
	flattened_array = image_array.ravel()
	return flattened_array

def train_nn():
	input_params, desired_output = get_data_set("train")
	forward_neuron_layer_order = list(range(1, __neural_network.get("layers_count") + 1))
	backward_neuron_layer_order = forward_neurons_order[::-1]

	# INITIALIZATION OF WEIGHTS AND BIAS
	# IT IS REQUIRED BEFORE TRAINING OF NEURAL NETWORK
	for each_layer in forward_neuron_layer_order:
		layer_name = "_".join(["layer", each_layer])
		__neural_network[layer_name]["weights"] = initialize_weights(__neural_network.get(layer_name).get("number_of_inputs"),
									 								 __neural_network.get(layer_name).get("number_of_neurons"))
		__neural_network[layer_name]["bias"] = initialize_bias(__neural_network.get(layer_name).get("number_of_neurons"))

	# TODO:
	# STOCHASTIC GRADIENT DESCENT WOULD BE USED,
	# BECAUSE DATASET IS TOO LARGE FOR NORMAL TRAINING
	# THUS IT NEEDS TO BE LEARNT FIRST
	__learning_rate = 0.2

	last_layer = "_".join(["layer", model_neural_network.get("layers_count")])

	__n_times = 5
# 	__batch_size = 100
	__total_dataset_size = input_params.shape[0]
	__iteration_times = __n_times * math.ceil(__total_dataset_size)

	__start_idx = 0
	__end_idx = __total_dataset_size

	__errors = []

	for each_time in range(__iteration_times):
		# UNCOMMENT THIS OUT FOR BATCH SIZED GRADIENT DESCENT
		# for each_time in range(__batch_size):
		# FEEDFORWARD NETWORK
		random_index = randint(__start_idx, __end_idx)
		input_vec = np.array([get_input_image_as_vec(input_params[random_index][0])])
		desired_output_vec = np.array([desired_output[random_index][0]])

		# FEEDING FORWARD STARTS HERE
		# FOR EACH NEURON LAYERS
		__net_output = []
		__net_input = []

		# FEED FORWARDING
		for each_layer in forward_neuron_layer_order:
			layer_name = "_".join(["layer", each_layer])
			# INPUT FOR EACH LAYER IN INDEXING ORDER
			__net_input.append(input_vec)
			net_output = summation(
				__neural_network[layer_name]["weights"],
				input_vec.T,
				__neural_network[layer_name]["bias"]
			)
			# OUTPUT FOR EACH LAYER IN INDEXING ORDER
			__net_output.append(net_output)
			synaptic_output = sigmoid(net_output)

			# ASSIGNING SYNAPTIC OUTPUT TO INPUT
			input_vec = synaptic_output

		# BACKWARD PROPAGATION
		delta_in_weights = []
		delta_in_bias = []

		for each_layer in backward_neuron_layer_order:
			layer_name = "_".join(["layer", each_layer])
			if layer_name == last_layer:
				error_to_backpropagate = error_derivative(synaptic_output, desired_output_vec,
					__neural_network.get(layer_name).get("number_of_neurons"))
			else:
				prior_layer = "_".join(["layer", each_layer + 1])
				error_to_backpropagate = np.matmul(__neural_network[prior_layer]["weights"],
					error_to_backpropagate.T)
			# MINIMIZING ERROR
			derivative_of_sig = sigmoid_diff(__net_output[each_layer])
			input_vec = __net_input[each_layer]

			error_to_backpropagate = np.multiply(error_to_backpropagate, derivative_of_sig)
			weights_delta = np.matmul(input_vec.T, error_to_backpropagate.T).T
			bais_delta = error_to_backpropagate

			delta_in_weights.append(weights_delta)
			delta_in_bias.append(bais_delta)

		# UPDATE WEIGHTS AFTER
		# EACH ITERATION
		for each_layer in backward_neuron_layer_order:
			layer_name = "_".join(["layer", each_layer])
			__neural_network[layer_name]["weights"] -= __learning_rate * delta_in_weights[each_layer]
			__neural_network[layer_name]["bias"] -= __learning_rate * delta_in_bias[each_layer]

		calculated_error = calculate_error(synaptic_output, desired_output_vec, __neural_network.get(last_layer).get("number_of_neurons"))
		__errors.append(calculated_error)

	plt.plot(__errors)
	plt.ylabel('Error plotting')
	plt.show()

	return __neural_network

def test_nn(model_neural_network):
	__error_threshold = 0.01

	test_params, desired_output = get_data_set("test")

	test_dataset_size = len(test_params)

	calculated_output_arr = []
	desired_output_arr = []
	correct_classification = 0

	last_layer = "_".join(["layer", model_neural_network.get("layers_count")])

	forward_neuron_layer_order = list(range(1, model_neural_network.get("layers_count") + 1))

	for idx in range(test_dataset_size):
		# FEED FORWARDING
		desired_output_vec = np.array([desired_output[idx][0]])
		input_vec = np.array([get_input_image_as_vec(input_params[random_index][0])])

		for each_layer in forward_neuron_layer_order:
			layer_name = "_".join(["layer", each_layer])
			# INPUT FOR EACH LAYER IN INDEXING ORDER
			__net_input.append(input_vec)
			net_output = summation(
				model_neural_network[layer_name]["weights"],
				input_vec.T,
				model_neural_network[layer_name]["bias"]
			)
			# OUTPUT FOR EACH LAYER IN INDEXING ORDER
			__net_output.append(net_output)
			synaptic_output = sigmoid(net_output)

			# ASSIGNING SYNAPTIC OUTPUT TO INPUT
			input_vec = synaptic_output

		calculated_error = calculate_error(synaptic_output, desired_output_vec, model_neural_network.get(last_layer).get("number_of_neurons"))

		calculated_output = get_output_value(desired_output_vec)
		desired_output = get_output_value(synaptic_output)

		if calculated_output == desired_output:
			correct_classification += 1

		calculated_output_arr.append(get_output_value(calculated_output))
		desired_output_arr.append(get_output_value(desired_output))

	accuracy = (correct_classification / test_dataset_size) * 100.0
	print("Model accuracy: ",accuracy)

	return confusion_matrix(calculated_output_arr, desired_output_arr)

if __name__ == "__main__":
	try:
		command = sys.argv[1]
	except Exception as ex:
		command = "empty"

	if command == "train":
		model = train_nn()
		store_model(model)
	elif command == "test":
		model = load_model()
		result = test_nn(model)
		print("-- Confusion matrix --")
		print(result)
	else:
		print("Please provide command")
		print("Example: python perceptron.py train")
		print("Example: python perceptron.py test")


"""
Displaying the contours of a function
======================================

An example showing how to display the contours of a function with
matplotlib.
"""

import numpy as np
import matplotlib.pyplot as plt

def f(x,y):
    return (1 - x / 2 + x**5 + y**3) * np.exp(-x**2 -y**2)

n = 256
x = np.linspace(-3, 3, n)
y = np.linspace(-3, 3, n)
X,Y = np.meshgrid(x, y)

plt.axes([0.025, 0.025, 0.95, 0.95])

plt.contourf(X, Y, f(X, Y), 8, alpha=.75, cmap=plt.cm.hot)
C = plt.contour(X, Y, f(X, Y), 8, colors='black', linewidth=.5)
plt.clabel(C, inline=1, fontsize=10)

plt.xticks([])
plt.yticks([])
plt.show()


import scipy.stats as st
import scipy.optimize
import warnings
import numpy as np

def log_like_iid_gamma(params, n):
    """Log likelihood for i.i.d. Gamma measurements, parametrized
    by x, a"""

    beta, alpha = params
    if n.any() <= 0:
        return -np.inf
    if beta <= 0:
        return -np.inf
    if alpha<=0:
        return -np.inf

    return st.gamma.logpdf(n , alpha, loc=0, scale=1/beta).sum()

#Code based on Bois (2020)

def mle_iid_gamma(n):
    """Perform maximum likelihood estimates for parameters for i.i.d.
   Gamma measurements, parametrized by x, a (cov matrix)"""
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")

        res = scipy.optimize.minimize(
            fun=lambda params, n: -log_like_iid_gamma(params, n),
            x0=np.array([2.00, 0.005]),
            args=(n,),
            method='Powell'
        )

    if res.success:
        return res.x
    else:
        raise RuntimeError('Convergence failed with message', res.message)

def log_like_iid_bespoke(params, n):
    beta, dbeta = params
    if n.any() <= 0:
        return -np.inf
    if beta <= 0:
        return -np.inf
    if dbeta <= 0:
        return -np.inf

    return np.sum(np.log(beta)+np.log(beta+dbeta)-np.log(dbeta)-beta*n+np.log(1-np.exp(-dbeta*n)))

def mle_iid_bespoke(n):
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")

        res = scipy.optimize.minimize(
            fun=lambda params, n: -log_like_iid_bespoke(params, n),
            x0=np.array([1.00, 0.5]),
            args=(n,),
            method='Powell'
        )

    if res.success:
        return res.x
    else:
        raise RuntimeError('Convergence failed with message', res.message)


from vector_calculus.containers import *
from sympy import symbols, Expr, S
from linalg import tr, dot

# These are cannonical variables of cartesian coordinate system
xyz = symbols('x, y, z')

def div(u):
    '''Divergence of vector --> scalar. Divergence of tensor --> vector.'''
    # Vector
    if isinstance(u, Vector):
        return sum((Dx(ui, vari) for ui, vari in zip(u, xyz)), S(0))
    # Tensor, recurse rows
    elif isinstance(u, Tensor):
        return Vector([div(ui) for ui in u])
    else:
        raise TypeError('Only divergence of vector or tensor allowed.')

def grad(u, dim=None):
    '''Gradient of vector --> tensor. Gradient of scalar --> vector.'''
    # Scalar
    if isinstance(u, Expr):
        # Infer dim from arguments of u
        if dim is None:
            # If there is no z dependence this is most likely a 2d scalar
            if xyz[2] not in u.atoms():
                dim = 2
            else:
                dim = 3

        return Vector([Dx(u, var) for var in xyz[:dim]])
    # Vector, recurse rows
    elif isinstance(u, Vector):
        dim = len(u)
        return Tensor([grad(ui, dim) for ui in u])
    else:
        raise ValueError('Only gradient of scalar or vector allowed.')

def curl(u):
    '''Curl of 3d vector --> vector. Curl 2d vecror --> scalar.'''
    assert isinstance(u, Vector), 'Need vector for curl'
    if len(u) == 3:
        return -Vector([Dx(u[1], xyz[2]) - Dx(u[2], xyz[1]),
                        Dx(u[2], xyz[0]) - Dx(u[0], xyz[2]),
                        Dx(u[0], xyz[1]) - Dx(u[1], xyz[0])])
    else:
        R = Tensor([[0, 1], [-1, 0]])
        return div(dot(R, u))

def rot(u, orientation='+'):
    '''Rotation of 2d scalar --> 2d vector. Default is counter-clockwise rot.'''
    assert isinstance(u, Expr), 'Can only take rot of scalar'
    assert xyz[2] not in u.atoms(), 'Scalar must be function of x, y only'

    R = Tensor([[0, -1], [1, 0]])
    R = R if orientation == '+' else -R
    return dot(R, grad(u))

def Dx(u, var):
    '''Partial derivative of u w.r.t to var. Shape is maintained.'''
    if isinstance(u, (int, float)):
        u = S(u)
    # Scalar
    if isinstance(u, Expr):
        return u.diff(var, 1)
    # Vector
    elif isinstance(u, Vector):
        return Vector([Dx(ui, var) for ui in u])
    # Tensor
    elif isinstance(u, Tensor):
        return Tensor([Dx(ui, var) for ui in u])
    else:
        raise TypeError('Cannot take derivatieve of type %s' % type(u))