"""
This file contains the implementation of the Newton-Cotes rules
"""

import numpy as np

def rectangle(x, f):
    """
    Compute a 1D definite integral using the rectangle (midpoint) rule
    Parameters
    ----------
    f : function
        User defined function.
    x : numpy array
        Integration domain.
    Returns
    -------
    I : float
        Integration result.
    """
    a = x[0]
    b = x[1]
    ya = f((a+b)/2)
    I = (b-a) * ya
    return I

def trapz(x, f):
    """
    Compute a 1D definite integral using the trapezoidal rule
    Parameters
    ----------
    f : function
        User defined function.
    x : numpy array
        Integration domain.
    Returns
    -------
    I : float
        Integration result.
    """
    a = x[0]
    b = x[1]
    ya = f(a)
    yb = f(b)
    I = (b-a) * (ya + yb) / 2
    return I

def simpson(x, f):
    """
    Compute a 1D definite integral using Simpson's rule.
    Parameters
    ----------
    f : function
        User defined function.
    x : numpy array
        Integration domain.
    Returns
    -------
    I : float
        Integration result.
    """
    a = x[0]
    b = x[1]
    ya = f(a)
    yb = f((a+b)/2)
    yc = f(b)
    I = (b-a) * (ya + 4 * yb + yc) / 6
    return I

def simpson3_8(x, f):
    """
    Compute a 1D definite integral using the 3/8 Simpson's rule.
    Parameters
    ----------
    f : function
        User defined function.
    x : numpy array
        Integration domain.
    Returns
    -------
    I : float
        Integration result.
    """
    a = x[0]
    b = x[1]
    ya = f(a)
    yb = f((2*a+  b)/3)
    yc = f((  a+2*b)/3)
    yd = f(b)
    I = (b-a) * (ya + 3 * (yb + yc) + yd) / 8
    return I

def boole(x, f):
    """
    Compute a 1D definite integral using Boole's rule.
    Parameters
    ----------
    f : function
        User defined function.
    x : numpy array
        Integration domain.
    Returns
    -------
    I : float
        Integration result.
    """
    a = x[0]
    b = x[1]
    ya = f(a)
    yb = f((3*a+  b)/4)
    yc = f((  a+  b)/2)
    yd = f((  a+3*b)/4)
    ye = f(b)
    I = (b-a) * (7 * (ya + ye) + 32 * (yb + yd) + 12 * yc) * 2 / 45
    return I


import numpy as np
from copy import deepcopy

class PermutationHandler():
    def __init__(self, n, k=None, random_seed=0): #n choose k
        if k is None: k = n
        self.n = n
        self.k = k
        self.n_perms = np.math.factorial(n)//np.math.factorial(n-k)
        self.random_seed = random_seed
        self.rng = np.random.RandomState(seed=random_seed)

    def generate_random_permutation(self):
        n, k = self.n, self.k
        item = []
        a = list(range(n))
        for i in range(k):
            entry_idx = self.rng.randint(n-i)
            item.append(a[entry_idx])
            del a[entry_idx]
        return tuple(item)

    def get_generator(self, count=None):
        if count is None:
            for idx in range(self.n_perms):
                yield self.index_to_permutation(idx)
        else:
            for idx in range(count):
                yield self.generate_random_permutation()

    def index_to_permutation(self, idx):
        assert idx < self.n_perms, "Index must be less than {}".format(self.n_perms)
        n_perms = self.n_perms
        n, k = self.n, self.k
        a = list(range(n))
        item = []
        for i in range(k):
            stride = n_perms // (n-i)
            entry_index = int(idx // stride)
            idx = idx - entry_index * stride
            item.append(a[entry_index])
            del a[entry_index]
            n_perms = n_perms//(n-i)
        return tuple(item)

    def permutation_to_index(self, item):
        n_perms = self.n_perms
        n, k = self.n, self.k
        a = list(range(n))
        idx = 0
        for i in range(len(item)):
            idx = idx + n_perms//(n-i) * a.index(item[i])
            a.remove(item[i])
            n_perms = n_perms//(n-i)
        return int(idx)

    def mutate(self, item, pos=None):
        item = list(item)
        n, k = self.n, self.k
        if pos is None: pos = self.rng.randint(k)
        perturbation = self.rng.randint(n-1) + 1
        existing_val = item[pos]
        new_val = (existing_val + perturbation) % n
        try:
            new_val_idx = item.index(new_val)
        except:
            new_val_idx = None
        if new_val_idx is not None:
            item[new_val_idx] = existing_val
        item[pos] = new_val
        return tuple(item)

    def crossover(self, item1, item2):
        item1, item2 = list(item1), list(item2)
        n, k = self.n, self.k
        pos = 1 + self.rng.randint(k-1)
        citem1 = item1[:pos] + [entry for entry in item2 if entry not in item1[:pos]][-k+pos:]
        citem2 = item2[:pos] + [entry for entry in item1 if entry not in item2[:pos]][-k+pos:]
        citem3 = [entry for entry in item2 if entry not in item1[pos:]][:pos] + item1[pos:]
        citem4 = [entry for entry in item1 if entry not in item2[pos:]][:pos] + item2[pos:]
        return tuple(citem1), tuple(citem2), tuple(citem3), tuple(citem4)


# Multivariate Linear Regression
# Includes data normalization

import numpy as np
from sklearn import datasets, linear_model

def partial_derivative(theta, trainX, trainY, trainSize):
    return trainX.T.dot((trainX.dot(theta) - trainY)) / trainSize

def normalize(data):
    mean = np.mean(data, axis=0)
    stdDeviation = np.std(data, axis=0)
    return (data - mean) / stdDeviation

def fit(trainX, trainY, learningRate=0.1, threashold=1E-5):
    trainSize, featureSize = trainX.shape
    theta = np.array(np.ones((featureSize, 1)), dtype=float) # Initialize theta
    derivative = partial_derivative(theta, trainX, trainY, trainSize)
    while abs(derivative).max() > threashold: # While gradient doesn't converge
        theta -= learningRate * derivative # Perform gradient descent
        derivative = partial_derivative(theta, trainX, trainY, trainSize) # Update partial derivative
    return theta

def standard_fit(trainX, trainY):
    trainX = trainX[:, 1:] # Remove the extra bias term
    model = linear_model.LinearRegression()
    model.fit(trainX, trainY)
    return np.append(model.intercept_, model.coef_)[np.newaxis].T # Returns the hypothesis in a column vector

def accuracy(theta, testX, testY):
    # Returns the R Squared value of the model
    explained = testX.dot(theta)
    mean = np.sum(testY) / len(testY)
    totSumSquares = np.sum((testY - mean) ** 2)
    resSumSquares = np.sum((testY - explained) ** 2)
    accuracy = 1 - resSumSquares / totSumSquares
    return accuracy

dataset = datasets.load_diabetes()
data, target = dataset.data, dataset.target
dataSize = data.shape[0] # Number of rows (entries)
data = normalize(data) # Normalize the data
data = np.hstack((np.ones((dataSize, 1)), data)) # Add an extra bias term
target = target[np.newaxis].T # Transpose target vector into column vector
separation = int(dataSize * 0.8) # Percentage of training data set and testing data set
trainX, trainY = data[:separation, :], target[:separation]
testX, testY = data[separation:, :], target[separation:]

hypothesis = standard_fit(trainX, trainY)
print('Standard Fitted Model: {}'.format(hypothesis.T))
acc = accuracy(hypothesis, testX, testY)
print('Standard Fitted Accuracy: {:.4f}\n'.format(acc))

hypothesis = fit(trainX, trainY, 0.1, 1E-6)
print('Fitted Model: {}'.format(hypothesis.T))
acc = accuracy(hypothesis, testX, testY)
print('Fitted Accuracy: {:.4f}'.format(acc))


'''
    Problem 69 : Totient maximum
'''
from time import time
from numba import jit
import numpy as np

@jit
def getPrimes(n):
    q, r = divmod(n, 2)
    r = q + r
    seive = np.ones(r)
    seive[0] = 0

    lim = int(n**0.5 / 2) + 1

    for i in range(1, lim + 1):
        p = 2 * i + 1
        if seive[i]:
            sp = 2 * i * (i + 1)
            while (sp < r):
                seive[sp] = 0
                sp = sp + p

    def_prime = np.asarray([2])
    seived_primes = np.asarray(np.nonzero(seive)).flatten() * 2 + 1
    primes = np.concatenate((def_prime, seived_primes))
    return primes

@jit
def get_phi(n):
    if n in primes_list:
        return n - 1

    res = n
    for p in primes_list:
        r = n % p
        if r == 0:
            while r == 0:
                n = n // p
                r = n % p

            res = res // p
            res = res * (p - 1)
        elif p * p > n:
            break

    if n > 1:
        res = res // n
        res = res * (n - 1)

    return res

if __name__ == "__main__":
    t0 = time()
    N = 1000000
    primes_list = getPrimes(int(N**0.5))
    print("Primes computed in {:.3f} secs".format(time() - t0))
    a, r = 0, 0
    for i in range(2, N + 1):
        phi = get_phi(i)
        # print(i," ", phi)
        r1 = i / phi
        if r1 > r:
            a, r = i, r1

    print("Result : ", a, " , ratio :", r)
    print("Done in {:.3f} secs".format(time() - t0))


"""
The purpose of this module is to perform a single Euler step on a step
function with the WENO method.

The initial conditions are

    q(t=0, x) = 1_{ x > 0 }

That is, q = 1 if x > 0, and q = 0 otherwise.

Required Modules:
-----------------

    * sympy - symbolic toolbox for Python.
"""

from weno_diff import central_diff1, central_diff2, ConstructIntegratedF, LinearReconstruct

import sympy

# Quick accessors

dx = sympy.Symbol("dx", real=True )    # spatial resolution
dt = sympy.Symbol("dt", real=True )    # time step
nu = sympy.Symbol("nu", real=True )    # CFL number
u  = sympy.Symbol("u" , real=True )    # advection speed

dt = nu*dx/u

qim5 = 0
qim4 = 0
qim3 = 0
qim2 = 0
qim1 = 0
qi   = 1
qip1 = 1
qip2 = 1
qip3 = 1
qip4 = 1

# Expansion of f:
#
#    F \approx f_i - (u*dt)/2 u_{i,x} + (u*dt)^2/3 u_{i,xx}
#
Fim3 = sympy.simplify( ConstructIntegratedF( [ qim5, qim4, qim3, qim2, qim1 ], u, dx, dt ) )
Fim2 = sympy.simplify( ConstructIntegratedF( [ qim4, qim3, qim2, qim1, qi   ], u, dx, dt ) )
Fim1 = sympy.simplify( ConstructIntegratedF( [ qim3, qim2, qim1, qi  , qip1 ], u, dx, dt ) )
Fi   = sympy.simplify( ConstructIntegratedF( [ qim2, qim1, qi  , qip1, qip2 ], u, dx, dt ) )
Fip1 = sympy.simplify( ConstructIntegratedF( [ qim1, qi  , qip1, qip2, qip3 ], u, dx, dt ) )
Fip2 = sympy.simplify( ConstructIntegratedF( [ qi  , qip1, qip2, qip3, qip4 ], u, dx, dt ) )

Fimh = sympy.simplify( LinearReconstruct( [Fim3, Fim2, Fim1, Fi, Fip1] ) )
Fiph = sympy.simplify( LinearReconstruct( [Fim2, Fim1, Fi, Fip1, Fip2] ) )

# Update is q_i^{n+1} = q_i^n - dt ( Fiph - Fim2 ) / dx
stability_polynomial = sympy.collect( sympy.expand(1 - dt*(Fiph - Fimh)/dx), nu )

print('Stability polynomial for Taylor PIF-WENO is ')
sympy.pretty_print( stability_polynomial )
print('Stability polynomial for Taylor PIF-WENO is ')
print( stability_polynomial )

# Try this with a simpler scheme that has a known solution:
#forward_euler = sympy.collect( 1 - (u*dt)*( qi - qim1 ) / dx, exp(-I*k*dx) )
#print('Stability polynomial for Forward Euler is ')
#sympy.pretty_print( forward_euler )


import numpy as np
#import time

A = np.matrix('1 2 -2 1; 2 5 -2 3; -2 -2 5 3; 1 3 3 2')
b = np.matrix('4; 7; -1; 0')

def lu_decomposition(A):
    # if A.shape[0]!=A.shape[1]: implement exception

    dimension = A.shape[0]

    L = np.zeros(shape=(dimension,dimension))
    U = np.zeros(shape=(dimension,dimension))

    for i in range(dimension):
        L[i,i] = 1.0

    for i in range(dimension):
        for j in range(dimension):
            sum = 0.0

            if j >= i:
                for k in range(i+1):
                    sum += L[i,k] * U[k,j]

                U[i,j] = A[i,j] - sum
            else:
                for k in range(j+1):
                    sum += L[i,k] * U[k,j]

                L[i,j] = (A[i,j] - sum)/U[j,j]

    return L,U

def solveReverse(L, b):
    dimension = L.shape[0]

    y = np.zeros(shape = (dimension,1))

    for i in range(dimension):
        sum = 0

        for j in range(dimension):
            if j<i:
                sum += L[i][j]*y[j]

        y[i] = b[i] - sum

    return y

def solveNormal(U,y):
    dimension = U.shape[0]

    x = np.zeros(shape = (dimension,1))

    for i in range(dimension-1,-1,-1): #range([start], stop[, step])
        sum = 0

        for j in range(0,dimension):
            if not(j == i):
                sum += U[i][j] * x[j]

        x[i] = (y[i] - sum)/ U[i][i]

    return x

#a=time.clock()

l,u = lu_decomposition(A)
y = solveReverse(l,b)
x = solveNormal(u,y)

#b = time.clock()

print "matriz L \n",l
print "matriz U \n",u
print "L * U \n",np.dot(l,u)
print "\nvetor y \n",y
print "\nvetor x \n",x

#print 'delta', b-a


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

random.seed(2)
f = lambda x: x**2
a = 0.0

b = 3.0
NumSteps = 1000000
XIntegral=[]
YIntegral=[]
XRectangle=[]
YRectangle=[]

ymin = f(a)
ymax = ymin
for i in range(NumSteps):
    x = a + (b - a) * float(i) / NumSteps
    y = f(x)
    if y < ymin: ymin = y
    if y > ymax: ymax = y

A = (b - a) * (ymax - ymin)
N = 1000000
M = 0
for k in range(N):
    x = a + (b - a) * random.random()
    y = ymin + (ymax - ymin) * random.random()
    if y <= f(x):
            M += 1
            XIntegral.append(x)
            YIntegral.append(y)
    else:
            XRectangle.append(x)
            YRectangle.append(y)
NumericalIntegral = M / N * A
print ("Numerical integration = " + str(NumericalIntegral))

XLin=np.linspace(a,b)
YLin=[]
for x in XLin:
    YLin.append(f(x))

plt.axis   ([0, b, 0, f(b)])
plt.plot   (XLin,YLin, color="red" , linewidth="4")
plt.scatter(XIntegral, YIntegral, color="blue", marker   =".")
plt.scatter(XRectangle, YRectangle, color="yellow", marker   =".")
plt.title  ("Numerical Integration using Monte Carlo method")
plt.show()


# Implementation of cross-entropy error function using numpy.
# Compare the random weight results with the closed form solution (0,4,4).

# Code Flow:
    # 1. Import all relevant libraries.
    # 2. Generate sample data from std. normal distribution.
    # 3. Create random weights for plotting.
    # 4. Define sigmoid function.
    # 5. Feedforward calculation.
    # 6. Cross-entropy error function.
    # 7. Print cross-entropy error from random weights.
    # 8. Closed-form solution.
    # 9. Print cross-entropy error from closed form solution.

# 1. Import all relevante libraries:
import numpy as np

# 2. Create input data:
N = 100 # No. of samples
D = 2 # No. of features

# Random data generation:
X = np.random.randn(N,D)

# Center the first 50 points at (-2,-2)
X[:50,:] = X[:50,:] - 2*np.ones((50,D))

# Center the last 50 points at (2, 2)
X[50:,:] = X[50:,:] + 2*np.ones((50,D))

# Labels: first 50 are 0, last 50 are 1
T = np.array([0]*50 + [1]*50) # Random labels

# Bias term: Add a column of ones
ones = np.ones((N, 1))
Xb = np.concatenate((ones, X), axis=1)

# 3. Create random weights:
w = np.random.randn(D + 1)

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

# 5. Feedforward:
z = Xb.dot(w)
Y = sigmoid(z)

# 6. Cross-entropy error function:
# Section 3B from jupyter notebook
def cross_entropy(T, Y):
    E = 0
    for i in range(len(T)):
        if T[i] == 1:
            E -= np.log(Y[i])
        else:
            E -= np.log(1 - Y[i])
    return E

# 7. Print cross-entropy error from random weights:
print(cross_entropy(T, Y))

# 8. Closed-form solution:
# Section 3A from jupyter notebook
w = np.array([0, 4, 4])

# calculate the model output
z = Xb.dot(w)
Y = sigmoid(z)

# 9. Print cross-entropy error from closed form solution:
print(cross_entropy(T, Y))
print('As we expect the error is much lower with our closed form solution!')

2.Logistic Regression/1.Code - Using Theory/1.Cross_Entropy_Function.py


import torch
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader,random_split,Subset
from sklearn.model_selection import LeaveOneOut,KFold
from pathlib import Path
from dataset import data_generator,RegressionDataset
from opt import parse_args

opt=parse_args()

# Using RMSE loss as loss function
def RMSE_loss(yhat,y):
    mse=((yhat - y) ** 2).sum() / yhat.data.nelement()
    return torch.sqrt(mse)

# Perform Linear Regression AX=b
def linear_regression(x,weight):
    outputs=[]
    for i in x:
        out=i.view([1,len(i)]).mm(weight)
        outputs.append(out.numpy())
    return outputs

# Get the best weight using QR Decomposition
"""def get_best_weight(x,y,_lambda=0):
    Q,R=torch.qr(x)
    pseudo_inv=R.pinverse(_lambda).mm(Q.t())
    weight=torch.mm(pseudo_inv,y)
    return weight"""

# Get the best weight by the formulat mention in the course
def get_best_weight(x,y,_lambda=0):
    x_t=x.t()

    if _lambda==0:
        pseudo_inv=torch.inverse(x_t.mm(x))
    else:   # if regularization is needed
        mat=x_t.mm(x)
        I=torch.eye(mat.size(0))
        pseudo_inv=torch.inverse(mat+_lambda*I)

    weight=torch.mm(pseudo_inv.mm(x_t),y)
    return weight

# get polynomial features when polynomial regression with degree>1
def get_poly_features(x,degree):
    poly_x=[]
    for each_x in x:
        poly_term=[each_x**n for n in range(1,degree+1)]
        poly_x.append(poly_term)
    return poly_x

# draw the fitting plot
def draw_fitting_plot(train_set,range,w_1,deg_1,legend,title,fname,w_2=None,deg_2=None,w_3=None,deg_3=None,w_4=None,deg_4=None):
    train_X=[]
    train_Y=[]

    for i,(inputs,labels) in enumerate(train_set):
        train_X.append(inputs)
        train_Y.append(labels)

    train_X=np.asarray(train_X)
    train_Y=np.asarray(train_Y)
    plt.scatter(train_X,train_Y,facecolors='none',edgecolors='b')

    X=np.linspace(range[0],range[1],100)

    deg_1_X=get_poly_features(X,deg_1)
    deg_1_X=np.hstack((deg_1_X,np.ones((len(deg_1_X),1))))
    deg_1_X=torch.FloatTensor(deg_1_X)
    w_1=torch.FloatTensor(w_1)
    deg_1_Y=linear_regression(deg_1_X,w_1)
    deg_1_Y=torch.FloatTensor(deg_1_Y)
    deg_1_Y=torch.flatten(deg_1_Y)
    plt.plot(X,deg_1_Y,'r',linewidth=0.5)

    if w_2 is not None:
        deg_2_X=get_poly_features(X,deg_2)
        deg_2_X=np.hstack((deg_2_X,np.ones((len(deg_2_X),1))))
        deg_2_X=torch.FloatTensor(deg_2_X)
        w_2=torch.FloatTensor(w_2)
        deg_2_Y=linear_regression(deg_2_X,w_2)
        deg_2_Y=torch.FloatTensor(deg_2_Y)
        deg_2_Y=torch.flatten(deg_2_Y)
        plt.plot(X,deg_2_Y,'g',linewidth=0.5)

    if w_3 is not None:
        deg_3_X=get_poly_features(X,deg_3)
        deg_3_X=np.hstack((deg_3_X,np.ones((len(deg_3_X),1))))
        deg_3_X=torch.FloatTensor(deg_3_X)
        w_3=torch.FloatTensor(w_3)
        deg_3_Y=linear_regression(deg_3_X,w_3)
        deg_3_Y=torch.FloatTensor(deg_3_Y)
        deg_3_Y=torch.flatten(deg_3_Y)
        plt.plot(X,deg_3_Y,'deepskyblue',linewidth=0.5)

    if w_4 is not None:
        deg_4_X=get_poly_features(X,deg_4)
        deg_4_X=np.hstack((deg_4_X,np.ones((len(deg_4_X),1))))
        deg_4_X=torch.FloatTensor(deg_4_X)
        w_4=torch.FloatTensor(w_4)
        deg_4_Y=linear_regression(deg_4_X,w_4)
        deg_4_Y=torch.FloatTensor(deg_4_Y)
        deg_4_Y=torch.flatten(deg_4_Y)
        plt.plot(X,deg_4_Y,'darkviolet',linewidth=0.5)

    plt.legend(legend,fontsize=16)
    plt.xlabel('x',fontsize=14)
    plt.ylabel('y',fontsize=14)
    plt.title(title,fontsize=14)
    plt.savefig(fname)
    plt.clf()

# Testing
def test(test_set,degree,weight):
    test_X=[]
    test_Y=[]

    for i,(inputs,labels) in enumerate(test_set):
        test_X.append(inputs)
        test_Y.append(labels)

    test_X=get_poly_features(test_X,degree)
    test_X=np.hstack((test_X,np.ones((len(test_X),1))))
    test_X=torch.FloatTensor(test_X)
    test_Y=torch.FloatTensor(test_Y)
    test_Y=test_Y.view([test_Y.size(0),1])

    weight=torch.FloatTensor(weight)

    outputs=linear_regression(test_X,weight)
    outputs=torch.FloatTensor(outputs)
    outputs=outputs.view([outputs.size(0),1])

    loss=RMSE_loss(outputs,test_Y)
    avg_test_loss=loss/test_X.size(0)

    print(f'testing_loss: {avg_test_loss:.4f}\n')

# Training when the cross validation is five folds
def kf_train(dataset,degree=1,_lambda=0):
    kf=KFold(n_splits=5,random_state=None,shuffle=False)

    total_training_loss=0.0
    total_valid_loss=0.0
    total_weight=np.empty((degree+1,1))

    for train_idx,valid_idx in kf.split(dataset):   # Split the data to train and valid
        train_set=Subset(dataset,train_idx)
        valid_set=Subset(dataset,valid_idx)

        train_X=[]
        train_Y=[]

        ##########          training          ##########
        for i,(inputs,labels) in enumerate(train_set):
            train_X.append(inputs)
            train_Y.append(labels)

        train_X=get_poly_features(train_X,degree)
        train_X=np.hstack((train_X,np.ones((len(train_X),1))))
        train_X=torch.FloatTensor(train_X)
        train_Y=torch.FloatTensor(train_Y)
        train_Y=train_Y.view([train_Y.size(0),1])

        weight=get_best_weight(train_X,train_Y,_lambda)
        total_weight+=weight.numpy()

        outputs=linear_regression(train_X,weight)
        outputs=torch.FloatTensor(outputs)

        loss=RMSE_loss(outputs,train_Y)
        total_training_loss+=loss/train_X.size(0)

        valid_X=[]
        valid_Y=[]

        ##########          validation          ##########
        for i,(inputs,labels) in enumerate(valid_set):
            valid_X.append(inputs)
            valid_Y.append(labels)

        valid_X=get_poly_features(valid_X,degree)
        valid_X=np.hstack((valid_X,np.ones((len(valid_X),1))))
        valid_X=torch.FloatTensor(valid_X)
        valid_Y=torch.FloatTensor(valid_Y)
        valid_Y=valid_Y.view([valid_Y.size(0),1])

        outputs=linear_regression(valid_X,weight)
        outputs=torch.FloatTensor(outputs)

        loss=RMSE_loss(outputs,valid_Y)
        total_valid_loss+=loss/valid_X.size(0)

    best_weight=total_weight/dataset.__len__()
    avg_training_loss=total_training_loss/dataset.__len__()
    avg_valid_loss=total_valid_loss/dataset.__len__()

    print(f'training_loss: {avg_training_loss:.4f}\tvalid_loss: {avg_valid_loss:.4f}')

    return best_weight

# Training when the cross validation is leave one out
def loo_train(dataset,degree=1,_lambda=0):
    loo=LeaveOneOut()

    total_training_loss=0.0
    total_valid_loss=0.0
    total_weight=np.zeros((degree+1,1))

    for train_idx,valid_idx in loo.split(dataset):  # split the data to train and valid
        train_set=Subset(dataset,train_idx)
        valid_set=Subset(dataset,valid_idx)

        train_X=[]
        train_Y=[]

        ##########          training          ##########
        for i,(inputs,labels) in enumerate(train_set):
            train_X.append(inputs)
            train_Y.append(labels)

        train_X=get_poly_features(train_X,degree)
        train_X=np.hstack((train_X,np.ones((len(train_X),1))))
        train_X=torch.FloatTensor(train_X)
        train_Y=torch.FloatTensor(train_Y)
        train_Y=train_Y.view([train_Y.size(0),1])

        weight=get_best_weight(train_X,train_Y,_lambda)
        total_weight+=weight.numpy()

        outputs=linear_regression(train_X,weight)
        outputs=torch.FloatTensor(outputs)
        outputs=outputs.view([outputs.size(0),1])

        loss=RMSE_loss(outputs,train_Y)
        total_training_loss+=loss/train_X.size(0)

        valid_X=[]
        valid_Y=[]

        ##########          validation          ##########
        for i,(inputs,labels) in enumerate(valid_set):
            valid_X.append(inputs)
            valid_Y.append(labels)

        valid_X=get_poly_features(valid_X,degree)
        valid_X=np.hstack((valid_X,np.ones((len(valid_X),1))))
        valid_X=torch.FloatTensor(valid_X)
        valid_Y=torch.FloatTensor(valid_Y)
        valid_Y=valid_Y.view([valid_Y.size(0),1])

        outputs=linear_regression(valid_X,weight)
        outputs=torch.FloatTensor(outputs)
        outputs=outputs.view([outputs.size(0),1])

        loss=RMSE_loss(outputs,valid_Y)
        total_valid_loss+=loss

    best_weight=total_weight/dataset.__len__()
    avg_training_loss=total_training_loss/dataset.__len__()
    avg_valid_loss=total_valid_loss/dataset.__len__()

    print(f'training_loss: {avg_training_loss:.4f}\tvalid_loss: {avg_valid_loss:.4f}')
    return best_weight

def main():

    ##########          Linear Regression with degree=1,5,10,14          ##########
    if Path('./data/data.csv').is_file()==False:
        data_generator('linear','./data/data.csv',20)

    data_set=RegressionDataset('./data/data.csv')
    total_sz=data_set.__len__()
    test_sz=int(0.25*total_sz)
    train_set,test_set=random_split(data_set,[total_sz-test_sz,test_sz])

    origin_train_set=train_set
    origin_test_set=test_set

    print("Regression Linear Leave One Out Degree 1 20 Data Points")
    weight_loo_deg1=loo_train(train_set,1)
    test(test_set,1,weight_loo_deg1)

    print("Regression Linear Five Fold Degree 1 20 Data Points")
    weight_kf_deg1=kf_train(train_set,1)
    test(test_set,1,weight_kf_deg1)

    print("Regression Linear Leave One Out Degree 5 20 Data Points")
    weight_loo_deg5=loo_train(train_set,5)
    test(test_set,5,weight_loo_deg5)

    print("Regression Linear Five Fold Degree 5 20 Data Points")
    weight_kf_deg5=kf_train(train_set,5)
    test(test_set,5,weight_kf_deg5)

    print("Regression Linear Leave One Out Degree 10 20 Data Points")
    weight_loo_deg10=loo_train(train_set,10)
    test(test_set,10,weight_loo_deg10)

    print("Regression Linear Five Fold Degree 10 20 Data Points")
    weight_kf_deg10=kf_train(train_set,10)
    test(test_set,10,weight_kf_deg10)

    print("Regression Linear Leave One Out Degree 14 20 Data Points")
    weight_loo_deg14=loo_train(train_set,14)
    test(test_set,14,weight_loo_deg14)

    print("Regression Linear Five Fold Degree 14 20 Data Points")
    weight_kf_deg14=kf_train(train_set,14)
    test(test_set,14,weight_kf_deg14)

    draw_fitting_plot(train_set,[-3,3],weight_loo_deg1,1,['degree=1','degree=5','degree=10','degree=14'],'Linear Data Live One Out Curve','./figure/linear-loo.jpg',weight_loo_deg5,5,weight_loo_deg10,10,weight_loo_deg14,14)
    draw_fitting_plot(train_set,[-3,3],weight_kf_deg1,1,['degree=1','degree=5','degree=10','degree=14'],'Linear Data Five Folds Curve','./figure/linear-kf.jpg',weight_kf_deg5,5,weight_kf_deg10,10,weight_kf_deg14,14)

    ##########          Linear Regression on Sine Curve Data with degree=5,10,14          ##########
    if Path('./data/sin_data.csv').is_file()==False:
        data_generator('sin','./data/sin_data.csv',20)

    data_set=RegressionDataset('./data/sin_data.csv')
    total_sz=data_set.__len__()
    test_sz=int(0.25*total_sz)
    train_set,test_set=random_split(data_set,[total_sz-test_sz,test_sz])

    print('Regression Sine Leave One Out Degree 5 20 Data Points')
    weight_loo_deg5=loo_train(train_set,5)
    test(test_set,5,weight_loo_deg5)

    print('Regression Sine Five Fold Degree 5 20 Data Points')
    weight_kf_deg5=kf_train(train_set,5)
    test(test_set,5,weight_kf_deg5)

    print('Regression Sine Leave One Out Degree 10 20 Data Points')
    weight_loo_deg10=loo_train(train_set,10)
    test(test_set,10,weight_loo_deg10)

    print('Regression Sine Five Fold Degree 10 20 Data Points')
    weight_kf_deg10=kf_train(train_set,10)
    test(test_set,10,weight_kf_deg10)

    print('Regression Sine Leave One Out Degree 14 20 Data Points')
    weight_loo_deg14=loo_train(train_set,14)
    test(test_set,14,weight_loo_deg14)

    print('Regression Sine Five Fold Degree 14 20 Data Points')
    weight_kf_deg14=kf_train(train_set,14)
    test(test_set,14,weight_kf_deg14)

    draw_fitting_plot(train_set,[0,1],weight_loo_deg5,5,['degree=5','degree=10','degree=14'],'Sine Data Live One Out Curve','./figure/sine-loo.jpg',weight_loo_deg10,10,weight_loo_deg14,14)
    draw_fitting_plot(train_set,[0,1],weight_kf_deg5,5,['degree=5','degree=10','degree=14'],'Sine Data Five Folds Curve','./figure/sine-kf.jpg',weight_kf_deg10,10,weight_kf_deg14,14)

    ##########          Linear Regression on Different Training Data Size          ##########
    if Path('./data/data_320.csv').is_file()==False:
        data_generator('linear','./data/data_320.csv',320)

    data_set=RegressionDataset('./data/data_320.csv')
    total_sz=data_set.__len__()
    delete_sz=260
    use_set,delete_set=random_split(data_set,[total_sz-delete_sz,delete_sz])

    total_sz=use_set.__len__()
    test_sz=int(0.25*total_sz)
    train_set,test_set=random_split(use_set,[total_sz-test_sz,test_sz])

    print('Regression Linear Leave One Out Degree 14 60 Data Points')
    weight_loo_data60=loo_train(train_set,14)
    test(test_set,14,weight_loo_data60)

    print('Regression Linear Five Fold Degree 14 60 Data Points')
    weight_kf_data60=kf_train(train_set,14)
    test(test_set,14,weight_kf_data60)

    total_sz=data_set.__len__()
    delete_sz=160
    use_set,delete_set=random_split(data_set,[total_sz-delete_sz,delete_sz])

    total_sz=use_set.__len__()
    test_sz=int(0.25*total_sz)
    train_set,test_set=random_split(use_set,[total_sz-test_sz,test_sz])

    print('Regression Linear Leave One Out Degree 14 160 Data Points')
    weight_loo_data160=loo_train(train_set,14)
    test(test_set,14,weight_loo_data160)

    print('Regression Linear Five Fold Degree 14 160 Data Points')
    weight_kf_data160=kf_train(train_set,14)
    test(test_set,14,weight_kf_data160)

    total_sz=data_set.__len__()
    delete_sz=0
    use_set,delete_set=random_split(data_set,[total_sz-delete_sz,delete_sz])

    total_sz=use_set.__len__()
    test_sz=int(0.25*total_sz)
    train_set,test_set=random_split(use_set,[total_sz-test_sz,test_sz])

    print('Regression Linear Leave One Out Degree 14 320 Data Points')
    weight_loo_data320=loo_train(train_set,14)
    test(test_set,14,weight_loo_data320)

    print('Regression Linear Five Fold Degree 14 320 Data Points')
    weight_kf_data320=kf_train(train_set,14)
    test(test_set,14,weight_kf_data320)

    draw_fitting_plot(origin_train_set,[-3,3],weight_loo_data60,14,['m=60','m=160','m=320'],'Linear Data Different m Live One Out Curve','./figure/data-m-loo.jpg',weight_loo_data160,14,weight_loo_data320,14)
    draw_fitting_plot(origin_train_set,[-3,3],weight_kf_data60,14,['m=60','m=160','m=320'],'Linear Data Different m Five Folds Curve','./figure/data-m-kf.jpg',weight_kf_data160,14,weight_kf_data320,14)

    ##########          Linear Regression with Regularization Term lambda          ##########
    print('Regularization 0.001/m Linear Five Fold Degree 14 20 Data Points')
    _lambda=0.001/20
    weight_kf_0001l=kf_train(origin_train_set,14,_lambda)
    test(origin_test_set,14,weight_kf_0001l)

    print('Regularization 1/m Linear Five Fold Degree 14 20 Data Points')
    _lambda=float(1)/20
    weight_kf_1l=kf_train(origin_train_set,14,_lambda)
    test(origin_test_set,14,weight_kf_1l)

    print('Regularization 1000/m Linear Five Fold Degree 14 20 Data Points')
    _lambda=float(1000)/20
    weight_kf_1000l=kf_train(origin_train_set,14,_lambda)
    test(origin_test_set,14,weight_kf_1000l)

    draw_fitting_plot(origin_train_set,[-3,3],weight_kf_0001l,14,['0.001/m','1/m','1000/m'],'Linear Data Five Fold with Regularization Curve','./figure/regularization-kf.jpg',weight_kf_1l,14,weight_kf_1000l,14)

if __name__=='__main__':
    main()


import numpy as np

print("\nMétodo de Newton-Raphson")

# Definindo a função
def f(x):
    return (x*np.log10(x)) - 1

# Definindo a derivada
def der(x):
    dx = 1e-10
    return (f(x+dx) - f(x)) / dx

# Implementação do método numérico
def newraph(tol, x):
    x_0 = x
    dif = 1
    iter = 0
    while dif > tol:
        x_1 = x_0 - (f(x_0)/der(x_0))
        dif = abs(x_1 - x_0)
        x_0 = x_1
        iter += 1
    print("\nRaiz:", x_1)
    print("Quantidade de iterações:", iter)
    print("x_1 - x_0 = ", dif, "\n")

# Atribuição dos parâmetros
tol =  1e-4
a_inicial = 2
b_inicial = 3
x_inicial = 3

newraph(tol, x_inicial)


""" Orbital Mechanics for Engineering Students Example 1.16
Question:
Expand the function sin(t+h) in a Taylor series around t=1.
Plot the Taylor series of orders 1 to 4 and compare them with
sin(1+h) for -2<h<2.
Written by: J.X.J. Bannwarth
"""
import matplotlib.pyplot as plt
from sympy import symbols, diff, sin, cos
from math import factorial
from numpy import array, linspace, dot, zeros
import numpy as np

# Title
print("Orbital Mechanics for Engineering Students Example 1.16")

# Equation to approximate
t = symbols('t')
g = sin(t)

# Linearisation point
tLin = 1.

# Step size
hs = linspace(-2.,2.,50)
gTrue = np.sin(tLin + hs)

# Find the taylor coefficients
orders = range(5)
taylorCoefsEqn = [diff(g, t, order)/factorial(order) for order in orders]
taylorCoefs = array([coef.evalf(subs={t:tLin}) for coef in taylorCoefsEqn])

# Calculate the terms of the Taylor series polynomials
termApprox = zeros((hs.shape[0],len(orders)))
for idx, h in enumerate(hs):
    powers = array([h**order for order in orders])
    termApprox[idx,:] = powers * taylorCoefs

# Calculate the sum of the terms
gApprox = zeros((hs.shape[0],len(orders)))
gApprox[:,0] = termApprox[:,0]
for idx in range(1,gApprox.shape[1]):
    gApprox[:,idx] = termApprox[:,0:idx+1].sum(axis=1)

# Plot the results
plt.figure()
plt.grid()
plt.plot(hs, gTrue, label=f"sin(1+h)")
for idx, order in enumerate(orders):
    plt.plot(hs, gApprox[:,idx], label=f"$p_{order}$")
plt.xlabel("$h$ (-)")
plt.ylabel("Value (-)")
plt.title("Example 1.16 Plots of zeroth- to fourth-order\nTaylor series expansions of sin(1-h)")
plt.legend()
plt.show()


# -*- coding: utf-8 -*-
"""
In the introduction of his MOOC "SMAC" (Statistical Mechanics: Algorithms and
Computations - https://www.coursera.org/learn/statistical-mechanics), Werner
Krauth propose a simple method to compute pi using a direct sampling
Monte Carlo simulation. A program is proposed in Python, in a version which
allows to do many runs of the function direct_pi(N). The code is written in a
style close to pseudocode used for algorithms, or classical coding style used
in C, Fortran,...

It is possible to write the function in a more "pythonic" way, or to use the
numpy numerical library, to improve compactness and efficiency.

Function direct_pi_DV(N) use pure python with list comprehension to eliminate
the for loop. The sum is directly made on the boolean comparison results to count
the number of true trials.

Function direct_pi_DV_np(N) use the numpy library to vectorize the loop, directly
square values and sum the array elements over the smaller axis. Again the sum
is directly made on the boolean comparisons.

Finally, in order to compare efficiency, the execution times of the three
versions have been measured using the timeit library.

Here is value obtain for a sample run :
direct_pi       : 3.5209695600005944 s
direct_pi_DV    : 4.000994963998892 s
direct_pi_Dv_np : 0.19237353700009407 s

The use ot the numpy library clearly improve the computer speed performance by
a factor about 20.
"""
import random, timeit
import numpy as np

def direct_pi(N):
    n_hits = 0
    for i in range(N):
        x, y = random.uniform(-1.0, 1.0), random.uniform(-1.0, 1.0)
        if x ** 2 + y ** 2 < 1.0:
            n_hits += 1
    return n_hits

def direct_pi_DV(N):
    return sum((random.uniform(-1,1)**2 + random.uniform(-1,1)**2) < 1 for i in range(N))

def direct_pi_DV_np(N):
    return np.sum((np.random.uniform(-1,1,(N,2))**2).sum(1)<1)

n_runs = 1000
n_trials = 4000

# running :
for run in range(n_runs):
    print(run, 4.0 * direct_pi(n_trials) / n_trials)

for run in range(n_runs):
    print(run, 4.0 * direct_pi_DV(n_trials) / n_trials)

for run in range(n_runs):
    print(run, 4.0 * direct_pi_DV_np(n_trials) / n_trials)

# timing three versions :
print(timeit.timeit('direct_pi('+str(n_trials)+')', "from __main__ import direct_pi", number=n_runs))
print(timeit.timeit('direct_pi_DV('+str(n_trials)+')', "from __main__ import direct_pi_DV", number=n_runs))
print(timeit.timeit('direct_pi_DV_np('+str(n_trials)+')', "from __main__ import direct_pi_DV_np", number=n_runs))


# -*- coding: utf-8 -*-
"""
Created on Fri Aug 30 15:36:39 2019

@author: Simón Cometto
"""

import numpy as np

def biseccion(f, a, b, tolerancia=False, iteraciones=False):
    ''' Encuentra el cero de f entre a y b mediante el método bisección
        Parámetros:
            ->  f: una función que, dado x, devuelve el valor de f(x)
            ->  a y b: números reales iniciales para el método
            ->  tolerancia: el menor intérvalo entre el cual se encuentra el cero
            ->  iteraciones: la cantidad de iteraciones a realizar en el algoritmo'''

    #Si a la función no se le pasa un parámetro para detener las iteraciones devuelve False
    if not (tolerancia or iteraciones):
        return False

    if (f(a)*f(b))<0: #Hay un cero en el medio
        #El algoritmo se ejecuta hasta llegar a un intervalo de tolerancia menor o igual a "tolerancia"
        if(tolerancia):
            #Mientras el intérvalo es mayor a la tolerancia
            while(abs(a-b)>tolerancia):
                #Encuentro el punto medio entre a y b
                c =  a + (abs(b-a)*0.5)

                #Si f(a) y f(c) tienen signo distinto, entonces hubo un cruce por cero entre a y b
                if(f(a)*f(c)<0):
                    b = c
                #Sino el cero se encuentra entre b y c
                else:
                    a = c
            return c
        elif iteraciones:
            for i in range(iteraciones):
                c = a + (abs(b - a) * 0.5)

                # Si f(a) y f(c) tienen signo distinto, entonces hubo un cruce por cero entre a y b
                if (f(a) * f(c) < 0):
                    b = c
                # Sino el cero se encuentra entre b y c
                else:
                    a = c
            return c
    else:    #No se sabe si hay cero en el medio
        pass
        #Ver que devolver en caso de que no haya un cero en el medio, o como implementarlo

def punto_fijo(g, x_i, n):
    ''' Encuentra el cero de f a partir de g.
        Parámetros:
            ->  g: surge de despejar x de f
            ->  x_i: número real inicial para el método
            ->  n: la cantidad de iteraciones'''
    for i in range(n):
        x_i = g(x_i)
    return x_i

if __name__ == "__main__":
    f = lambda x: 1*(x**2) - x - 2

    a = float(input("Ingrese a: "))
    b = float(input("Ingrese b: "))
    i = int(input("Ingrese la cantidad de iteraciones: "))

    cero_b = biseccion(f, a, b, iteraciones=i)
    g = lambda x: (x + 2) ** 0.5
    cero_pf = punto_fijo(g, b, i)
    print("Resultado método de bisección:  ", cero_b)
    print("Resultado método de punto fijo: ", cero_pf)


import numpy as np
import matplotlib.pyplot as plt

from ivp_solvers import IVP, IVPSolver

class PredatorPrey1(IVP):
    """
    Predator-prey taken from example 16.8 (pg. 496) of "A First Course in Numerical Methods" by Ascher and Greif
    """
    tf = 100
    y0 = np.array([80., 30.])
    yf = np.array([94.04588719, 38.11498521])  # For running tests

    def dy_dt(self, _, y):
        return np.array([
            0.25 * y[0] - 0.01 * y[0] * y[1],
            -y[1] + 0.01 * y[0] * y[1]
        ])

def plot_example(t, y, show=True):
    plt.plot(t, y)

    if show:
        plt.show()

def run_example(problem: IVP, solver: IVPSolver, plot=True, show=True):
    with Timer():
        t, y = solver.solve_problem(problem)

    if plot:
        plot_example(t, y, show=show)

if __name__ == '__main__':
    from runge_kutta.explicit import Midpoint
    from ivp_solvers import CustomFixedStepRKSolver, FixedEuler, FixedRK2, FixedRK4, FixedThreeEigthsRK4
    from shared_utils.tests import PerfTimerNS as Timer

    run_example(PredatorPrey1(), FixedEuler(step_size=0.01), show=False)
    run_example(PredatorPrey1(), CustomFixedStepRKSolver(0.1, Midpoint()), show=False)
    run_example(PredatorPrey1(), FixedRK2(step_size=0.1), show=False)
    run_example(PredatorPrey1(), FixedRK4(step_size=0.1), show=False)
    run_example(PredatorPrey1(), FixedThreeEigthsRK4(step_size=0.1), show=True)

    _t, _y = FixedThreeEigthsRK4(step_size=0.01).solve_problem(PredatorPrey1())

    plt.show()


import matplotlib.pyplot as plt
import numpy as np

x = np.array(list(range(0, 11)))
y = np.array(list(map(lambda n: n * n, x)))

def compute_cost_function(m, t0, t1, x, y):
    return 1 / 2 / m * sum([(t0 + t1 * np.asarray([x[i]]) - y[i])**2 for i in range(m)])

def gradient_descent(x, y, alpha=0.01, ep=0.0001, max_iter=1500):
    converged = False
    iter = 0
    m = x.shape[0]    # number of samples

    # initial theta
    t0 = 0
    t1 = 0

    # total error, J(theta)
    J = compute_cost_function(m, t0, t1, x, y)
    # print('J=', J);
    # Iterate Loop
    num_iter = 0
    while not converged:
        # for each training sample, compute the gradient (d/d_theta j(theta))
        grad0 = 1.0 / m * sum([(t0 + t1 * np.asarray([x[i]]) - y[i]) for i in range(m)])
        grad1 = 1.0 / m * sum([(t0 + t1 * np.asarray([x[i]]) - y[i]) * np.asarray([x[i]]) for i in range(m)])

        # update the theta_temp
        temp0 = t0 - alpha * grad0
        temp1 = t1 - alpha * grad1

        # update theta
        t0 = temp0
        t1 = temp1

        # mean squared error
        e = compute_cost_function(m, t0, t1, x, y)
        # print ('J = ', e)
        J = e    # update error
        iter += 1    # update iter

        if iter == max_iter:
            print('Max interactions exceeded!')
            converged = True

    return t0, t1

def graph_points(data_x, data_y):
    for x, y in zip(data_x, data_y):
        plt.scatter(x, y, c='r')

def graph_line(data_x, m, b):
    line = [(m * x) + b for x in data_x]
    plt.plot(data_x, line, c='b')

def squared_error(y_points, regression_line):
    return sum((regression_line - y_points)**2)

def coeff_of_determination(y_points, regression_line):
    y_mean_line = [np.mean(y_points) for y in regression_line]
    squared_error_regr = squared_error(y_points, regression_line)
    squared_error_y_mean = squared_error(y_points, y_mean_line)
    return 1 - (squared_error_regr / squared_error_y_mean)

graph_points(x, y)
b, m = gradient_descent(x, y)
print(m, b)
graph_line(x, m, b)
print(coeff_of_determination(y, m * x + b))
plt.show()


import numpy as np
# import scipy as sp
# import sklearn
# import matplotlib.pyplot as plt

class Lab1(object):
    def solver(self, A, b):
        return np.dot(np.linalg.inv(A), b)

    def fitting(self, x, y):
        xmatr = np.column_stack((x, np.ones(x.shape)))
        coeff = np.dot(np.linalg.pinv(xmatr), y)
        return coeff

    def naive5(self, X, A, Y):
        # Calculate the matrix with $(i,j$)-th entry as  $\mathbf{x}_i^\top A \mathbf{y}_j$ by looping over the rows of $X,Y$.
        qf = np.zeros((X.shape[0], Y.shape[0]))
        for i in range(X.shape[0]):
            for j in range(Y.shape[0]):
                qf[i, j] = np.dot(np.dot(X[i], A), Y[j])
        return qf

    def matrix5(self, X, A, Y):
        # Repeat part (a), but using only matrix operations (no loops!).
        return np.dot(np.dot(X, A), Y.T)

    def naive6(self, X, A):
        # Calculate a vector with $i$-th component $\mathbf{x}_i^\top A \mathbf{x}_i$ by looping over the rows of $X$.
        qf = np.zeros(X.shape[0])
        for i in range(X.shape[0]):
            qf[i] = np.dot(np.dot(X[i], A), X[i])
        return qf

    def matrix6(self, X, A):
        # Repeat part (a) using matrix operations (no loops!).
        return np.sum(np.dot(X, A)*X, axis=1)


import math
import numpy as np

def read_file(name):
    # Open file
    file = open(name, 'r')

    # Return content
    return file.read()

def exercise_1_generator(size, alphabet, probability):
    # Result
    results = ""

    # Loop to generate size-long text
    for _ in range(size):
        results += np.random.choice(alphabet, p=probability)

    # Return prepared text
    return results

def entropy(dictionary):
    # Entropy sum
    entropy_result = 0.0

    # Loop to analyze
    for key, value in dictionary.items():
        entropy_result += value * math.log(value, 2)

    # Return result
    return -entropy_result

def analyze_content(content):
    # Letters dictionary
    letters = {}

    # Letters counter to prepare probability of single letter
    counter = 0

    # Loop to analyze content
    for _, letter in enumerate(content):
        cardinality = letters.get(letter, 0)
        letters.update({letter: cardinality + 1})
        counter += 1

    # Loop to change cardinality to probability
    for letter in letters:
        letters.update({letter: letters.get(letter) / counter})

    # Return letters dictionary
    return letters

def analyze_characters(content, row, separator=''):
    # Results - dictionary with letters
    letters = {}

    # Last characters
    last_characters = []

    # Counter
    counter = 0

    # Loop to iterate
    for char in content:
        if len(last_characters) == row:
            # Fetch dictionary
            selected_dictionary = letters.get(separator.join(last_characters), {})

            # Update total counter
            total = selected_dictionary.get('--TOTAL--', 0)
            selected_dictionary.update({'--TOTAL--': total + 1})

            # Update counter this letter
            cardinality = selected_dictionary.get(char, 0)
            selected_dictionary.update({char: cardinality + 1})

            # Update selected dictionary
            letters.update({separator.join(last_characters): selected_dictionary})

            # Update counter
            counter += 1

        # Append char to list
        last_characters.append(char)

        # Check length - if greater than row -> delete first char (FIFO)
        if len(last_characters) > row:
            del (last_characters[0])

    # Return letters dictionary with counter
    return letters, counter

def cardinality_to_probability(dictionary, counter):
    key_dictionary = {}

    # Loop to change cardinality to probability
    for key, value_dict in dictionary.items():
        # Fetch cardinality from special key
        cardinality = value_dict.pop('--TOTAL--')

        # Loop to iterate - values in dictionary
        for value_key, value in value_dict.items():
            # Set value
            value_dict.update({value_key: value / cardinality})

        # Probability of key
        key_dictionary[key] = cardinality / counter

        # Update dictionary
        dictionary.update({key: value_dict})

    # Return updated dictionary
    return dictionary, key_dictionary

def conditional_entropy(dictionary, keys):
    # Entropy
    entropy_value = 0.0

    # Sum over keys - get all values
    for key, probability_x in keys.items():
        for value in dictionary.get(key).values():
            entropy_value += probability_x * value * math.log(value, 2)

    # Return calculated entropy
    return -entropy_value

def main():
    #
    # Exercise 1 - Random, all letters with probability 1/37
    #

    letters = list('qwertyuiopasdfghjklzxcvbnm0123456789 ')
    probability = [1/37 for _ in letters]
    size = 10_000
    text = exercise_1_generator(size=size, alphabet=letters, probability=probability)
    letters_dictionary = analyze_content(text)
    entropy_result = entropy(letters_dictionary)

    print('Exercise 1 - Raw, random text')
    print('\tEntropy =>', entropy_result)

    #
    # Exercise 1 - Analyze content from sample Wiki
    #

    file_name = '../Exercise_1/norm_wiki_sample.txt'
    content = read_file(file_name)
    size = 10_000
    letters_dictionary = analyze_content(content)
    text = exercise_1_generator(size=size, alphabet=list(letters_dictionary.keys()), probability=list(letters_dictionary.values()))
    letters_dictionary = analyze_content(text)
    entropy_result = entropy(letters_dictionary)

    print('Exercise 1 - Based on text')
    print('\tEntropy =>', entropy_result)

    #
    # Exercise 2
    #

    files = ['norm_wiki_en.txt', 'norm_wiki_la.txt', 'sample0.txt', 'sample1.txt', 'sample2.txt', 'sample3.txt', 'sample4.txt', 'sample5.txt']
    rows = [1, 2, 3, 4, 5]

    for file_name in files:
        print('File ' + str(file_name))
        content_char = read_file(file_name)
        content_words = content_char.split()

        print('\tContent [characters] =>', entropy(analyze_content(content_char)))
        print('\tContent [words] =>', entropy(analyze_content(content_words)))

        for row in rows:
            words, counter = analyze_characters(content_char, row, separator='')
            dictionary, keys = cardinality_to_probability(words, counter)
            print('\t- chars @ ' + str(row) + ' =>', conditional_entropy(dictionary, keys))

            words, counter = analyze_characters(content_words, row, separator=' ')
            dictionary, keys = cardinality_to_probability(words, counter)
            print('\t- words @ ' + str(row) + ' =>', conditional_entropy(dictionary, keys))

        print('-------------------')

if __name__ == '__main__':
    main()


from lam.eigen.eigenvalue import *
import json
from lam.readtext.readtext import readtext
import sympy as sp
import sympy.core.numbers as nu
import numpy as np
class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        elif isinstance(obj, np.floating):
            return float(obj)
        elif isinstance(obj, np.ndarray):
            return obj.tolist()
        elif isinstance(obj,sp.core.numbers.Integer):
            return str(obj)
        else:
            return str(obj)
def getlambdamat(mat: sp.MutableDenseMatrix):#得到矩阵λE-A
    lambdamatrix = -mat
    lambdamatrix1 = sp.eye(mat.shape[0])
    x = sp.symbols("lambda")
    lambdamatrix1 = lambdamatrix1 * x
    lambdamatrix = lambdamatrix + lambdamatrix1
    return lambdamatrix
def getlambdamatvalue(mat: sp.MutableDenseMatrix):#得到列表[λ1E-A,λ2E-A,.....λiE-A]
    lambdamatrix = -mat
    lambdamatrix1 = sp.eye(mat.shape[0])
    eigenvalues=list(mat.eigenvals().keys())
    list0=[]
    for i in range(len(eigenvalues)):
        lambdamatrix2=lambdamatrix1*eigenvalues[i]
        lambdamatrix3 = lambdamatrix + lambdamatrix2
        list0.append(lambdamatrix3)
    return list0
def slveigengetcharpoly(a:str):#特征多项式
    eigenSolver = EigenSolver(readtext(a))
    p = eigenSolver.getCharpoly()
    p=sp.latex(p)
    json_str = json.dumps(p)
    return json_str
def slveigenvalue(a:str):#特征值
    eigenSolver = EigenSolver(readtext(a))
    p = eigenSolver.getEigenvalues()
    p = sp.latex(p)
    json_str = json.dumps(p)
    return json_str
def slveigenvectors(a:str):#特征向量
    eigenSolver = EigenSolver(readtext(a))
    eigenvectors = eigenSolver.getEigenvectors()
    eigenvectors_0 = []
    for i in range(len(eigenvectors)):
        p = []
        for j in range(len(eigenvectors[i][2])):
            p.append(sp.latex(eigenvectors[i][2][j]))
        m = [sp.latex(eigenvectors[i][0]), sp.latex(eigenvectors[i][1]), p]
        eigenvectors_0.append(m)
    eigenvectors=eigenvectors_0
    return eigenvectors
def slveigenCourse(a:str):#（特征值求解过程）
    eigenSolver=EigenSolver(readtext(a))
    p=eigenSolver.get_course()
    matrix=p['matrix']
    lambdamat = getlambdamat(matrix)
    lambdamatvalue = getlambdamatvalue(matrix)
    eigenvectors=p['eigenvectors']
    charpoly=p['charpoly']
    matrix=sp.latex(matrix)
    charpoly=sp.latex(charpoly)
    eigenvectors_0=[]
    for i in range(len(eigenvectors)):
        q = []
        for j in range(len(eigenvectors[i][2])):
            q.append(sp.latex(eigenvectors[i][2][j]))
        m = (sp.latex(eigenvectors[i][0]), sp.latex(eigenvectors[i][1]), q)
        eigenvectors_0.append(m)
    p['matrix']=matrix
    p['eigenvectors']=eigenvectors_0
    p['charpoly']=charpoly
    print(p['charpoly'])
    lambdamat = sp.latex(lambdamat)
    for i in range(len(lambdamatvalue)):
        lambdamatvalue[i] = sp.latex(lambdamatvalue[i])
    p.update({'lambdamat': lambdamat})
    p.update({'lambdamatvalue':lambdamatvalue})
    return p

if __name__ == '__main__':
    slveigenCourse("[[3,2,4],[2,0,2],[4,2,3]]")


import numpy as np
import matplotlib.pyplot as plt

def lag(x, y, num):
    """
    'num' is a set of given points, use x and y to figure out lagrange
    polynomial and calculate all the corresponding values for num

    Implement as you wish but your 'total' numpy array
    has to return all the results
    """

    assert(len(x)==len(y))
    total  = np.zeros(len(num))

    #place your code here!!!!!!!!!!!!!!!!!!!!!!!!!
    for i in range(len(num)):
        total[i] = lagPol(num[i], x, y)

    return total

def lagPol(xi, x, y):
    res = 0
    for i in range(len(x)):
        res += y[i]*lagCoeff(xi, i, x)
    return res

def lagCoeff(xi, i, x):
    res = 1
    for j in range(len(x)):
        if(j != i):
            res *= (xi-x[j])/(x[i]-x[j])
    return res

data_x = np.array([-3.,-2.,-1.,0.,1.,3.,4.])
data_y = np.array([-60.,-80.,6.,1.,45.,30.,16.])
# data_x = np.array([10.,15.,20.,22.5])
# data_y = np.array([227.4,362.78,517.35,602.97])

#generating 50 points from -3 to 4 in order to create a smooth line
X = np.linspace(-3, 4, 50, endpoint=True)
F = lag(data_x, data_y, X)
print(F)
plt.plot(X,F)
plt.plot(data_x, data_y, 'ro')
plt.show()


from numpy import array, sqrt, zeros, set_printoptions

def cholesky(A,b):
    # Lower triangular matrix
    el = zeros((3,3))
    el[0,0] = sqrt(A[0,0])
    el[1,0] = A[1,0] / el[0,0]
    el[1,1] = sqrt(A[1,1] - el[1,0]**2)
    el[2,0] = A[2,0] / el[0,0]
    el[2,1] = (A[2,1] - el[2,0] * el[1,0])/el[1,1]
    el[2,2] = sqrt(A[2,2] - el[2,0]**2 - el[2,1]**2)
    # Forward substitution for y
    y = zeros((3))
    y[0] = b[0] / el[0,0]
    y[1] = (b[1] - el[1,0] * y[0]) / el[1,1]
    y[2] = (b[2] - el[2,0] * y[0] - el[2,1] * y[1]) / el[2,2]
    # Backward substitution for x
    x = zeros((3))
    elT = el.T
    x[2] = y[2] / elT[2,2]
    x[1] = (y[1] - elT[1,2] * x[2]) / elT[1,1]
    x[0] = (y[0] - elT[0,1] * x[1] - elT[0,2] * x[2]) / elT[0,0]
    return el, y, x

# Input parameters to choesky(A,b) function
A = array([[5,-1,1],[-1,3,-1],[1,-1,4]])
b = array([6,2,11])
el, y, xc = cholesky(A,b)

set_printoptions(precision = 4)
print("L = ", el)
print("y = ", y)
print("x1 = %8.4f " % xc[0])
print("x2 = %8.4f " % xc[1])
print("x3 = %8.4f " % xc[2])


import numpy as np
import time

#----- normal distribution

def model_prob(_x, _theta):
    #-- parameters
    _mu = _theta[0]
    _sigma_sq = _theta[1]
    #-- probability
    _p = np.exp(-(_x - _mu) ** 2 / (2 * _sigma_sq)) / np.sqrt(2 * np.pi * _sigma_sq)
    #print(_p)
    return _p

def max_likelihood_est(_x):
    _mu = np.mean(_x)
    _sigma_sq = np.var(_x)
    _theta = [_mu, _sigma_sq]
    #print(_theta)
    return _theta

#-----

def likelihood(_x, _theta):
    _l = np.sum(np.log(model_prob(_x, _theta)))
    #print(_l)
    return _l

def bootstrap_sample(_x):
    _n = _x.size
    _ids = np.random.randint(0, _n, _n)
    #print(_ids)
    _x_ast = _x[_ids]
    #print(_x_ast)
    return _x_ast

def EIC_bias(_x, _B):
    _D_ast = np.zeros(_B)
    for i in range(_B):
        _x_ast = bootstrap_sample(_x)
        _theta_ast = max_likelihood_est(_x_ast)
        _D_ast[i] = likelihood(_x_ast, _theta_ast) - likelihood(_x, _theta_ast)
        #print(_D_ast[i])
    #print(_D_ast)

    _b_b = np.mean(_D_ast)
    #print(_b_b)
    return _b_b

def EIC_bias2(_x, _B):
    _D_ast = np.zeros(_B)
    for i in range(_B):
        _x_ast = bootstrap_sample(_x)
        _theta_ast = max_likelihood_est(_x_ast)
        _eic = EIC_bias(_x_ast, _B)
        _D_ast[i] = likelihood(_x_ast, _theta_ast) - _eic - likelihood(_x, _theta_ast)
    _b_2nd = np.mean(_D_ast)
    return _b_2nd

#-----

if __name__ == '__main__':

    T = 100 #10000

    n = 25 #25, 100, 400
    B = 100 #1000

    t_eic = np.zeros(T)
    t_eic_2nd = np.zeros(T)

    prv_ut = time.time()
    for t in range(T):
        ut = time.time()
        if ut - prv_ut > 5.0:
            prv_ut = ut
            print("---", t)

        #-- samples from true distribution
        x = np.random.normal(0.0, 1.0, n)
        #print(x)

        t_eic[t] = EIC_bias(x, B)
        t_eic_2nd[t] = EIC_bias2(x, B) + t_eic[t]
        #print(t_eic[t])
        #print(t_eic_2nd[t])

    print("mean EIC bias:", np.mean(t_eic))
    print("mean EIC_2nd bias:", np.mean(t_eic_2nd))


import numpy as np

l1 = np.array([4, 1, 9, 3])
l2 = np.array([1, 6, 3, 0])

#-------------------------------------------------------------

# Rounding Decimals
# There are primarily five ways of rounding off decimals in NumPy:

# # truncation
# arr = np.trunc([76.6754, -13.435])
# print(arr)

# # fix
# arr = np.fix([76.6754, -13.435])
# print(arr)

# # rounding
# arr = np.around(76.6754)
# print(arr)

# arr = np.around(76.6754, 2)
# print(arr)

# # floor
# arr = np.floor([76.265, -13.435])
# print(arr)

# # ceil
# arr = np.ceil([76.265, -13.435])
# print(arr)

#-------------------------------------------------------------
# LOGS

arr = np.arange(1, 10)
print(arr)
print(np.log2(arr))
print(np.log10(arr))
print(np.log(arr))  # log to the base e
#-------------------------------------------------------------

# Addition is done between two arguments whereas summation happens over n elements.
# Summations
a1 = np.sum([l1,l2])
print(a1)

# NumPy will sum the numbers in each array.
a2 = np.sum([l1,l2], axis = 1)
print(a2)

# cummulative summation
a3 = np.cumsum(l1)
print(a3)

# Products
a1 = np.prod([l1,l2])
print(a1)

# NumPy will return the product of each array.
a2 = np.prod([l1,l2], axis = 1)
print(a2)

# cummulative product
a3 = np.cumprod(l1)
print(a3)

#Difference

# subtracting two successive elements.
a1 = np.diff(l1)
print(a1)

# Compute discrete difference of the following array twice
a2 = np.diff(l1, n=2)
print(a2)

# Set Operations

a1 = np.array([2,3,5,2,1,2,3,1,5,5,3])

b = np.unique(a1)
print(b)

a2 = np.union1d(l1,l2)
print(a2)

a3 = np.intersect1d(l1,l2)
print(a3)

a4= np.setdiff1d(l1,l2)
a5= np.setdiff1d(l2,l1)
print(a5)
print(a4)

# LCM
a1 = np.lcm.reduce(l1)
print(a1)
arr = np.array([[3, 6, 9], [2,3,5]])
a2 = np.lcm.reduce(arr)
print(a2)

# GCD
a1 = np.gcd.reduce(l1)
print(a1)
arr = np.array([[3, 6, 9], [2,3,5]])
a2 = np.gcd.reduce(arr)
print(a2)


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

style.use('ggplot')

# Algoritmo para calcular regresion lineal
###########################
#     _   _   __
#     x . y - xy
# m = ------------------
#             __
#      _ 2     2
#     (x)   - x
###########################
#     _   __
# b = y - mx
#
###########################
#                 ^
#  2           SE y
# r  = 1 - --------------
#                 _
#              SE y
###########################

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

def create_dataset(hm, variance, step=2, correlation=False):
    val = 1
    ys = []
    for i in range(hm):
        y = val + random.randrange(-variance, variance)
        ys.append(y)
        if correlation and correlation == 'pos':
            val += step
        elif correlation and correlation == 'neg':
            val -= step
    xs = [i for i in range(len(ys))]

    return np.array(xs, dtype=np.float64), np.array(ys, dtype=np.float64)

def squared_error(ys_orig, ys_line):
    return sum((ys_line - ys_orig)**2)

def coeffiecient_of_determination(ys_orig, ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_regr = squared_error(ys_orig, ys_line)
    squared_error_y_meean = squared_error(ys_orig, y_mean_line)
    return 1 - (squared_error_regr / squared_error_y_meean)

def best_fit_slope_and_intercept(xy, ys):
    m = (((mean(xs) * mean(ys)) - mean(xs * ys)) /
         ((mean(xs) * mean(xs)) - mean(xs * xs)))
    b = mean(ys) - (m * mean(xs))

    return m, b

xs, ys = create_dataset(40, 10, 2, correlation=False)

m, b = best_fit_slope_and_intercept(xs, ys)

regression_line = [(m * x) + b for x in xs]

predict_x = 8
predict_y = (m * predict_x) + b

r_squared = coeffiecient_of_determination(ys, regression_line)
print(r_squared)

plt.scatter(xs, ys)
plt.plot(xs, regression_line)
plt.scatter(predict_x, predict_y, s=100, color='g')
plt.show()
# plt.scatter(xs, ys)
# plt.show()


import numpy as np
import matplotlib.pyplot as plt
from numpy.random import randn
import seaborn as sns
sns.set()

from linear_classification.basic_logistic_unit import sigmoid
from e_commerce_example.logistic_training import cross_entropy

def generate_donut_data(number_of_points, dimensions, inner_radius, outer_radius):
    # The distance from origin = R + random normal
    # The theta angle =  Uniformly distributed from (0, 2*pi)

    R1 = np.random.randn(number_of_points//2) + inner_radius
    R2 = np.random.randn(number_of_points//2) + outer_radius

    theta1 = 2 * np.pi * np.random.random(number_of_points//2)
    theta2 = 2 * np.pi * np.random.random(number_of_points//2)

    X_inner = np.concatenate( [ [R1 * np.cos(theta1)], [R1 * np.sin(theta1)] ] ).T
    X_outer = np.concatenate( [ [R2 * np.cos(theta2)], [R2 * np.sin(theta2)] ] ).T

    X = np.concatenate([ X_inner, X_outer ])
    y = np.array([0]*(number_of_points//2) + [1]*(number_of_points//2))

    plt.figure()
    plt.scatter(X[:,0], X[:,1], c=y)
    plt.show()

    return X, y

def gradient_descent_l2_donut_problem(X, y, learning_rate=0.0001, epochs=5000, l2_norm=0.1):
    w = np.random.randn(X.shape[1])
    z = X.dot(w)
    y_hat = sigmoid(z)

    errors = []
    for epoch in range(epochs):
        error = cross_entropy(y, y_hat)
        errors.append(error)
        if epoch % 100 == 0:
            print("Epoch {}:{}".format(epoch, error))
        w += learning_rate * ( X.T.dot(y - y_hat) - l2_norm * w )

        y_hat = sigmoid(X.dot(w))

    plt.figure()
    plt.plot(errors)
    plt.title("Cross-entropy Loss/Iteration")
    plt.show()

    print("Final w:", w)
    print("Final classification rate:", 1 - np.abs(y - np.round(y_hat)).sum() / X.shape[0])

    return w

if __name__ == '__main__':
    number_of_points = 1000
    dimensions = 2
    inner_radius = 5
    outer_radius = 10
    learning_rate=0.0001
    epochs=5000
    l2_norm = 0.1

    X, y = generate_donut_data(number_of_points, dimensions, inner_radius, outer_radius)

    # Generate the ones + an additional column of r = sqrt(x^2 + y^2)
    # This is the radious of a point, and makes data points linearly separable

    ones = np.ones((number_of_points, 1))
    radiuses = np.sqrt( (X*X).sum(axis=1) ).reshape(-1,1)
    X = np.concatenate((ones, radiuses, X), axis=1)

    _ = gradient_descent_l2_donut_problem(X, y, learning_rate, epochs, l2_norm)

    # Classification does not appear to depend on the X and y term, but on the bias and the summed radiuses


import numpy as np

def swap_row(mat,r1,r2):
	temp = mat[r1]
	mat[r1] = mat[r2]
	mat[r2] = temp

def normalize(mat,i):
	for j in range(i+1,len(mat)):
		fact = (mat[j][i]/mat[i][i])
		for k in range(0,len(mat)+1):
			mat[j][k] -= fact*mat[i][k]

def fwd_elimination(mat):
	n = len(mat)
	for i in range(n-1):
		if mat[i][i] == 0:
			swap_row(mat,i,i+1)
		normalize(mat,i)
		m = np.array(mat)
		m = m[:,:n]
		if (np.linalg.det(m) == 0):
			print ("Singular Matrix!! No Unique solution Exists!!")
			return False
	return True

def bwd_elimination(mat):
	n = len(mat)
	x = [0]*n

	for i in range(n-1,-1,-1):
		x[i] = mat[i][n]

		for j in range(i+1,n):
			x[i] -= mat[i][j]*x[j]

		x[i] = x[i]/mat[i][i]

	print("Solutions are: \n\n")
	print("y(0.25): ",x[0],"\n\n")
	print("y(0.5): ",x[1],"\n\n")
	print("y(0.75): ",x[2],"\n\n")

def func(x):
	return np.exp(x**2)

def main():
	mat = []
	y0 = 0; y4 = 0;
	h = 0.25;
	x = [0.25,0.5,0.75]
	f = []
	for i in range(3):
		temp = func(x[i])*(h**2)
		f.append(temp)

	f[0] -= y0;
	f[2] -= y4;

	mat = [[-2,1,0,f[0]],[1,-2,1,f[1]],[0,1,-2,f[2]]]

	if(fwd_elimination(mat) == True):
		bwd_elimination(mat)

if __name__ == '__main__':
	main()


# -*- coding: utf-8 -*-
"""
Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers.

For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.

Evaluate the sum of all the amicable numbers under 10000.

Solution commment:
I evaluate d(n) by first prime factorizing n, taking all the possible partitions of
the primes and summing the product of them. Hoping this is faster for large n because
prime factorization only needs to look at sqrt(n) numbers. Potential bottleneck is
the partitioning of the primes, a recursive method which is probably not very fast for
large numbers of primes. Find solution for 10 000 in under 2 sec, 100 000 in under 30 sec.
"""
from numpy import prod
from itertools import groupby

def partitions(arr):
    """Return a list of all the partitions of arr"""
    if len(arr) == 1:
        return [arr]
    without = partitions(arr[:-1])
    incl = [part + [arr[-1]] for part in without]
    return without + incl + [[arr[-1]]]

def prodComplex(listoflists):
    return [prod(arr) for arr in listoflists]

def primes(n):
    primefac = []
    d = 2
    while d*d <= n:
        while n % d == 0:
            primefac.append(d)
            n /= d
        d += 1
    if n > 1:
        primefac.append(n)
    return primefac

def unduplicate(listoflists):
    listoflists.sort()
    return list(k for k, _ in groupby(listoflists))

def d(n):
    if n <= 1:
        return 0
    primefac = primes(n)
    primeparts = unduplicate(partitions(primefac))
    primeprod = prodComplex(primeparts)
    return sum(primeprod) - n + 1

s = 0
seen = set()
num = 0
while num < 10000:
    num += 1
    if num in seen:
        continue

    amicable = d(num)
    if d(amicable) == num and num != amicable:
        print "Found pair:", num, amicable
        seen.add(num)
        seen.add(amicable)
        s += amicable + num
print 'sum of amicable numbers =', s


#########################################################################################
# This is a class that collects data science "distances methods".
# Distances measure similarities between features (both numeric and categorical).
# Distances are used in a lot of fields: in Machine Learning inside methods (such as the
# k-Means or the cosine similarity used inside the colloborative filtering tecnique).
#########################################################################################

import numpy as np

class Distances(object):

	def __init__(self):
		super().__init__()

	def Euclidean(self, points_1:list, points_2:list):

		'''
		Euclidean distance measures distance between points in a linear space. When the space we
		are considering is high dimensional, it suffers from the curse of dimensionality.

		param points_1: first point in form of [float, float]
		param points_2: second point in form of [float, float]
		'''

		assert len(points_1) == len(points_2)

		return ( sum( (x - y) ** 2 for x, y in zip(points_1, points_2) ) ) ** 0.5

	def Manhattan(self, points_1:list, points_2:list):

		'''
		Manhattan distance refers to the distance between two vectors if they could only move
		by following right angles. It takes its name from New York Manhattan hood where streets
		are forming almost always squre root angles against each other.

		points_1: first point in form of list
		points_2: second point in form of list
		'''

		assert len(points_1) == len(points_2)

		return sum( abs(x - y) for x, y in zip(points_1, points_2) )

	def Minkowski(self, p_order:float, x:list, y:list):

		'''
		Minkowski distance refers to the distance a metric used in Normed vector space
		(n-dimensional real space), which means that it can be used in a space where distances
		can be represented as a vector that has a length.
		When p_order is equal to 1, this distance is the Mahnattan one.
		When p_order is equal to 2, this distance is the Euclidean one.

		param x: first point in form of [float, float]
		param y: second point in form of [float, float]
		param p_order: order of distance magnitude
		'''

		assert len(x) == len(y)

		return ( sum( [abs(i - j) ** p_order for i, j in zip(x, y)] ) ) ** (1 / p_order)

	def Hamming(self, x:str, y:str):

		'''
		Hamming distance (used for categorical features such as strings) measures the distance in terms of different
		values between two vectors. It is tipically used to measure the difference between two strings.

		param x: first object (i.e. "Andrea")
		param y: second point object (i.e. "Annrea")
		'''

		assert len(x) == len(y)

		distance = 0
		length   = len(x)

		for i in range(length):
			if x[i] != y[i]:
				distance +=1

		return (length, distance)

	def CosineSimilarity(self, x:list, y:list):

		'''
		Cosine similarity measure the distance between two vectors getting the cosine among them.
		If the cosine is close to one, these two vectors are equal. If the cosine is almost equal
		to minus one, these two vectors are dissimilar.
		in order to be compared the vector must have same length.

		param x: is it the first vector in form [1,2,4,5]
		param y: is it the first vector in form [1,2,4,7]
		'''

		dot    = np.dot( x, y)
		norm_x = ( sum( [ k**2 for k in x ] ) ) ** 0.5
		norm_y = ( sum( [ k**2 for k in y ] ) ) ** 0.5

		return dot / (norm_x * norm_y)


'''
Simulate MA(1) Time Series

You will simulate and plot a few MA(1) time series, each with a different parameter, θ
θ
, using the arima_process module in statsmodels, just as you did in the last chapter for AR(1) models. You will look at an MA(1) model with a large positive θ
θ
 and a large negative θ
θ
.

As in the last chapter, when inputting the coefficients, you must include the zero-lag coefficient of 1, but unlike the last chapter on AR models, the sign of the MA coefficients is what we would expect. For example, for an MA(1) process with θ=−0.9
θ
=
−
0.9
, the array representing the MA parameters would be ma = np.array([1, -0.9])

INSTRUCTIONS
100XP
Import the class ArmaProcess in the arima_process module.
Plot the simulated MA(1) processes
Let ma1 represent an array of the AR parameters [1, θ
θ
] as explained above. The AR parmater array will contain just the lag-zero coeffienct of one.
With parameters ar1 and ma1, create an instance of the class ArmaProcess(ar,ma) called MA_object1.
Simulate 1000 data points from the object you just created, MA_object1, using the method .generate_sample(). Plot the simulated data in a subplot.
Repeat for the other MA parameter.
'''
# import the module for simulating data
from statsmodels.tsa.arima_process import ArmaProcess

# Plot 1: MA parameter = -0.9
plt.subplot(2,1,1)
ar1 = np.array([1])
ma1 = np.array([1, -0.9])
MA_object1 = ArmaProcess(ar1, ma1)
simulated_data_1 = MA_object1.generate_sample(nsample=1000)
plt.plot(simulated_data_1)

# Plot 2: MA parameter = +0.9
plt.subplot(2,1,2)
ar2 = np.array([1])
ma2 = np.array([1, 0.9])
MA_object2 = ArmaProcess(ar2, ma2)
simulated_data_2 = MA_object2.generate_sample(nsample=1000)
plt.plot(simulated_data_2)

plt.show()


def make_design_matrix(X, M):
    """ Return a design matrix.

    Args:
        X -- N -- float: Inputs for training or testing.
        M -- 1 -- int: # of features to use.

    Returns:
        Phi -- N x M -- float: Design matrix.
    """
    assert M > 0 and type(M) == int, "M should be a positive integer."

    from numpy import zeros

    N = len(X)
    Phi = zeros((N, M))
    for col in range(M):
        Phi[:, col] = X ** col

    return Phi

def CFS_LR(Phi, y_train):
    """ Return weights of linear regression using closed form solution.

    Args:
        Phi -- N x M -- float: Design matrix.
        y_train -- N -- float: Labels for training.

    Returns:
        w -- M -- float: Updated weights.
    """
    from numpy.linalg import pinv

    # Calculate closed form solution.
    w = pinv(Phi.T @ Phi) @ (Phi.T @ y_train)

    return w

def make_R(query, x_train, tau):
    """ Return R for locally weighted linear regression.

    Args:
        query -- 1 -- float: Input that I want to focus on.
        x_train -- N -- float:
            Entire train input to determine the local weight of
            each train input with respect to the current query.
        tau -- 1 -- float: Bandwidth parameter.

    Returns:
        R -- -- float: Local weights for weighted linear regression.
    """
    from numpy import zeros, exp, diag
    R = zeros((len(x_train), len(x_train)))

    # Calculate each element of R using give formula and vectorize them.
    R = [exp(- (query - x_train[idx]) ** 2 / (2 * tau ** 2)) for idx in range(len(x_train))]

    # Make a diagonal matrix using the vector.
    R = diag(R)

    return R

def CFS_LW(Phi, R, y_train):
    """ Return weights of locally weighted linear regression
        using closed form solution.

    Args:
        Phi -- N x M -- float: Design matrix.
        R: -- N x N -- float: Local weights for a given query.
        y_train -- N -- float: Labels for training.

    Returns:
        w -- M -- float: Updated weights.
    """
    from numpy.linalg import pinv

    # Calculated the closed form solution for locally weighted LR.
    w = pinv(Phi.T @ R @ Phi) @ (Phi.T @ (R @ y_train))

    return w

def main():
    """ Run linear regression with various local weight parameters.
    """
    from numpy import load, linspace
    from matplotlib.pyplot import subplots, savefig, show

    # load data.
    x_train = load("data/q2x.npy")
    y_train = load("data/q2y.npy")

    # Run unweighted linear regression.
    # Find closed form solution for unweighted linear regression.
    Phi = make_design_matrix(x_train, 2)
    w = CFS_LR(Phi, y_train)
    y_result = w @ Phi.T

    # Plot the data and the line, and save as file.
    fig, ax = subplots()
    ax.title.set_text("Linear Regression without Local Weighting\ny = {:.3f} + {:.3f}x".format(w[0], w[1]))
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.scatter(x_train, y_train, label="Data", color="black")
    ax.plot(x_train, y_result, label="Unweighted LR Line")
    ax.legend()
    savefig("q2-d-i.png")

    # Run locally weighted linear regression with tau = 0.8.
    # Prepare queries.
    xmin, xmax = min(x_train), max(x_train)
    xran = xmax - xmin
    queries = linspace(xmin, xmax, num=50)

    # Iterate over each query.
    y_result = []
    for query in queries:
        # Find closed for solution for locally weighted linear regression.
        R = make_R(query, x_train, 0.8)
        w = CFS_LW(Phi, R, y_train)
        y_result.append(w[0] + w[1] * query)

    # Plot the curve and save as file.
    ax.title.set_text("Local Weighting Linear Regression")
    ax.plot(queries, y_result, label="Locally Weighted LR Curve, tau=0.8")
    ax.legend()
    savefig("q2-d-ii.png")

    # Run locally weighted linear regression using various taus.
    # Nest another iteration over four tau = 0.1, 0.3, 2, 10.
    taus = [0.1, 0.3, 2, 10]
    for tau in taus:
        y_result = []
        for query in queries:
            # Find closed for solution for locally weighted linear regression.
            R = make_R(query, x_train, tau)
            w = CFS_LW(Phi, R, y_train)
            y_result.append(w[0] + w[1] * query)

        # Plot the curve and save as file.
        ax.plot(queries, y_result, label="Locally Weighted LR Curve, tau={}".format(tau))
    ax.title.set_text("Local Weighted Linear Regression")
    ax.legend()
    savefig("q2-d-iii.png")
    show()

if __name__ == "__main__":
    main()


"""
Example: Cafe Java Demand Distribution and Process Generator.

This example develops a discrete process generator for coffee demand data
observed in Cafe Java.

@author: Paul T. Grogan <pgrogan@stevens.edu>
"""

# import the python3 behavior for importing, division, and printing in python2
from __future__ import absolute_import, division, print_function

# import the numpy package and refer to it as `np`
# see http://docs.scipy.org/doc/numpy/reference/ for documentation
import numpy as np

# import the matplotlib pyplot package and refer to it as `plt`
# see http://matplotlib.org/api/pyplot_api.html for documentation
import matplotlib.pyplot as plt

# create a numpy array with the demand sizes
demands = np.array([0, 1, 2, 3])
labels = ["None", "Small", "Medium", "Large"]
print("demands = {}".format(demands))

# create a numpy array with the observed frequencies
frequency = np.array([8, 10, 22, 10])

# the probability mass function is the number of demands (frequency) divided
# by the total number of demands (sum of all frequency)
pmf = frequency/np.sum(frequency)
print("pmf = {}".format(pmf))

# create a new figure for a bar plot using the demand indices, pmf values,
# desired bar width, and color
plt.figure()
bar_width = 0.5
plt.bar(demands, pmf, bar_width, align='center', color='b')
plt.ylabel('p(x)')
plt.ylim([0,1])
plt.xlabel('Coffee Demand (x)')
plt.title('PMF for Cafe Java Demand')
plt.xticks(demands, labels)

# the cumulative distribution function is the cumulative sum of the PDF
cdf = np.cumsum(pmf)
print("cdf = {}".format(cdf))

# create a new figure for a step plot using the demand indices, cdf values,
# desired format (solid red line, -r), and option for post-steps
plt.figure()
plt.step(demands, cdf, '-r', where='post')
plt.ylabel('F(x)')
plt.ylim([0,1])
plt.xlabel('Coffee Demand (x)')
plt.title('CDF for Cafe Java Demand')
plt.xticks(demands, labels)

# define a function to generate demands following the inverse transform method
def generate_demand_ivt():
    """Generates a demand following the inverse transform method.

    Returns:
        demand (int): the size of coffee demanded
    """
    r = np.random.rand()

    # check the first cdf entry (index 0)
    if r <= cdf[0]:
        return demands[0]
    # check the second cdf entry (index 1)
    elif r <= cdf[1]:
        return demands[1]
    # check the third cdf entry (index 2)
    elif r <= cdf[2]:
        return demands[2]
    # otherwise no need to check, it's the final CDF entry (index 3)
    else:
        return demands[3]

    """
    note: the code above could be replaced with the following for
    loop which iterates over each array index i in demands

    for i in range(len(demands)):
        if r <= cdf[i]:
            return demands[i]
    """

# define number of samples
num_samples = 1000

# fill the samples arrays with samples from the generators
samples_ivt = [generate_demand_ivt() for i in range(num_samples)]

# count the number of each demand: use a generator expression to count the
# number of samples which match each demand level i
counts_ivt = np.array([sum(samples_ivt==i) for i in demands])
frequency_ivt = counts_ivt/np.sum(counts_ivt)

# create a new figure recreating the PMF bar plot
plt.figure()
bar_width = 0.3
plt.bar(demands, pmf, bar_width, align='center', color='k', label='Observed')
# also display the generated frequency (offset bars by bar_width)
plt.bar(demands+bar_width, frequency_ivt,
        bar_width, color='b', label='Generated (IVT)')
plt.ylabel('p(x)')
plt.ylim([0,1])
plt.xlabel('Coffee Demand (x)')
plt.title('Cafe Java Demand Process Generator Results (n={})'.format(num_samples))
plt.xticks(demands + bar_width, labels)
plt.legend()


import math

import numpy as np
import pandas as pd
from pandas import Timestamp
from scipy import interpolate

__all__ = [
    "compute_days_between",
    "treasury_bill_price",
    "bond_equivalent_yield",
    "discount_factor_from",
    "spot_rate_from",
    "forward_rate_from",
]

def compute_days_between(start_date, end_date):
    """Computes number of days between dates"""
    time_delta = Timestamp(end_date) - Timestamp(start_date)
    return time_delta.days

def treasury_bill_price(discount_yield, days_to_maturity):
    """Computes price ot treasury bill"""
    return 100 * (1 - days_to_maturity / 360 * discount_yield)

def bond_equivalent_yield(discount_yield, days_to_maturity):
    """Computes bond equivalent yield from treasury bill discount yield"""
    return 365 * discount_yield / (360 - discount_yield * days_to_maturity)

def is_valid_freq(freq):
    return math.isfinite(freq) and isinstance(freq, int) and freq > 0

def discount_factor_from(spot_rate, term, freq=math.inf):
    """Computes discount factor from spot rate

    The  discount factor from a spot rate given term and compounding frequency.

    Parameters
    ----------
    spot_rate : float
        The spot rate
    term : float
        The term or time to maturity
    freq : int or math.inf, optional
        The compounding frequency, default is math.inf which results in continuous compounding rates.

    Returns
    -------
    float
        The discount factor determined the spot rate.

    Raises
    ------
    ValueError
        If freq is not math.inf of positive int

    """
    if freq == math.inf:
        return math.exp(-spot_rate * term)
    elif is_valid_freq(freq):
        return math.pow(1 + spot_rate / freq, -freq * term)
    else:
        raise ValueError("Freq must be math.inf or positive int")

def spot_rate_from(discount_factor, term, freq=math.inf):
    """Computes spot rate from discount factor

    The spot rate from the discount factor given term and compounding frequency.

    Parameters
    ----------
    discount_factor : float or pandas.Series
        The discount factor to determine the spot rate.
    term : float
        The term or time to maturity
    freq : int or math.inf, optional
        The compounding frequency, default is math.inf which results in continuous compounding rates.

    Returns
    -------
    float or pandas.Series
        The spot rate

    Raises
    ------
    ValueError
        If freq is not math.inf of positive int

    """
    if freq == math.inf:
        package = math
        if isinstance(discount_factor, pd.Series):
            package = np
        return -1 / term * package.log(discount_factor)
    elif is_valid_freq(freq):
        return freq * (math.pow(discount_factor, -1 / (freq * term)) - 1)
    else:
        raise ValueError("Freq must be math.inf or positive int")

def forward_rate_from(rate_1, term_1, rate_2, term_2, freq=math.inf):
    """Computes forward rate from pair of spot rates

    The forward rate between pair of spot rates given terms and compounding frequency.

    Parameters
    ----------
    rate_1 : float
       The first spot rate
    term_1 : float
        The first term or time to maturity
    rate_2 : float
       The second spot rate
    term_2 : float
        The second term or time to maturity
    freq : int or math.inf, optional
        The compounding frequency, default is math.inf which results in continuous compounding rates.

    Returns
    -------
    float, float
        The term and rate of the forward

    Raises
    ------
    ValueError
        If freq is not math.inf of positive int

    """
    term = term_2 - term_1
    if freq == math.inf:
        return term, (rate_2 * term_2 - rate_1 * term_1) / term
    elif is_valid_freq(freq):
        df_1 = discount_factor_from(rate_1, term_1, freq)
        df_2 = discount_factor_from(rate_2, term_2, freq)
        forward_df = df_2 / df_1
        return term, spot_rate_from(discount_factor=forward_df, term=term, freq=freq)
    else:
        raise ValueError("Freq must be math.inf or positive int")

def interp_rates(rates, maturities=None, column="Rate"):
    assert "Maturity" in rates.columns
    assert column in rates.columns
    sorted_rates = rates.sort_values("Maturity")

    interpolator = interpolate.Akima1DInterpolator(
        sorted_rates["Maturity"], sorted_rates[column]
    )
    if maturities is None:
        maturities = np.arange(0.25, 7.25, 0.25)
    interpolated_rates = interpolator(maturities)

    index = pd.Series(data=maturities, name="Maturity")
    interpolated_rates = pd.Series(
        data=interpolated_rates, name=f"Interpolated {column}", index=index
    )
    interpolated_rates = pd.DataFrame(interpolated_rates).reset_index()

    return interpolated_rates

def add_libor_curve(rates, first_swap_maturity, delta=0.25):
    assert "Maturity" in rates.columns
    assert "Interpolated Rate" in rates.columns
    zeros = np.zeros((len(rates),))

    idx = rates.index < first_swap_maturity
    zeros[idx] = 1 / (
        1 + rates.loc[idx, "Maturity"] * rates.loc[idx, "Interpolated Rate"]
    )
    for i in range(sum(idx), len(zeros)):
        rate = rates["Interpolated Rate"][i]
        zeros[i] = (1 - rate * delta * np.sum(zeros[:i])) / (1 + rate * delta)

    rates["Zero"] = zeros
    rates["Spot Rate"] = -1 / rates["Maturity"] * np.log(zeros)

    return rates

def add_short_rates(rates, time_step):
    assert "Maturity" in rates.columns
    assert "Zero" in rates.columns
    rates["Short Rate"] = 1 / time_step * (1 / rates["Zero"] - 1)

def add_forward_discounts(rates, start_maturity=1):
    assert "Maturity" in rates.columns
    assert "Zero" in rates.columns
    rates["Forward Discount"] = rates["Zero"] / rates["Zero"].shift(start_maturity)
    return rates

def add_forward_rates(rates):
    assert "Maturity" in rates.columns
    assert "Forward Discount" in rates.columns
    rates["Forward Rate"] = (1 / rates["Forward Discount"] - 1) / rates[
        "Maturity"
    ].diff()
    return rates

def add_forward_swap_discounts(rates, start_period=1):
    assert "Maturity" in rates.columns
    assert "Zero" in rates.columns
    rates["Forward Swap Discount"] = (
        rates["Zero"] / rates["Zero"].values[start_period - 1]
    )
    rates.loc[rates.index < start_period, "Forward Swap Discount"] = np.nan
    return rates

def add_swap_rates(rates):
    assert "Zero" in rates.columns
    assert "Maturity" in rates.columns
    rates["Swap Rate"] = (
        (1 - rates["Zero"]) / rates["Zero"].cumsum() / rates["Maturity"].diff()
    )
    return rates


# Question 5 Lab 03
# AB Satyaprakash (180123062)

# imports ----------------------------------------------------------------------
import sympy as sp
# ------------------------------------------------------------------------------
# (i) To show that both P(x) and Q(x) interpolate the data:
# f(−2) = −1, f(−1) = 3, f(0) = 1, f(1) = −1, f(2) = 3
# Given:
# P(x) = 3 − 2(x + 1) + 0(x + 1)(x) + (x + 1)(x)(x − 1)
# Q(x) = −1 + 4(x + 2) − 3(x + 2)(x + 1) + (x + 2)(x + 1)(x)
x = sp.Symbol('x')
px = 3 - 2*(x+1) + 0*(x+1)*(x) + (x+1)*(x)*(x-1)
qx = -1 + 4*(x+2) - 3*(x+2)*(x+1) + (x+2)*(x+1)*(x)

X = [-2, -1, 0, 1, 2]
F = [-1, 3, 1, -1, 3]
ok = True  # will change this to false if either P or Q does not interpolate

for i in range(len(X)):
    pval = px.subs(x, X[i])
    print('The value of P({}) = {}'.format(X[i], pval))
    if pval != F[i]:
        ok = False
print('\n')
for i in range(len(X)):
    qval = qx.subs(x, X[i])
    print('The value of Q({}) = {}'.format(X[i], qval))
    if qval != F[i]:
        ok = False

if ok == True:
    print("\nThus, both cubic polynomails P(x) and Q(x) interpolate the given data")

# (ii) Why does part (i) not violate the uniqueness property of interpolating polynomials
px = sp.expand(px)
qx = sp.expand(qx)
print('Simplifying P(x) we get', px)
print('Simplifying Q(x) we get', qx)
print('Since we can clearly see that P(x) = Q(x), this ensures that the uniqueness property of interpolating polynmials is not violated')

# Question 5 ends --------------------------------------------------------------

Semester 6/MA 322 (Scientific Computing Theory)/Labs/Lab 3/Code/q5.py


#%%
"""
Created on July 05  2021
Incentive function as a function of a swap rate or the differential w.r.t. "old" mortgage rate

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 and Emanuele Cassamassima
"""
import numpy as np
import matplotlib.pyplot as plt

def Annuity(rate,notional,periods,CPR):
    # it returns a matrix M such that
    # M = [t  notional(t)  prepayment(t)  notional_quote(t)  interest_quote(t)  installment(t)]
    # WARNING! here "rate" and "periods" are quite general, the choice of getting year/month/day.. steps, depends on the rate
    # that the function receives. So, it is necessary to pass the correct rate to the function
    M = np.zeros((periods + 1,6))
    M[:,0] = np.arange(periods + 1) # we define the times
    M[0,1] = notional
    for t in range(1,periods + 1):
        # we are computing the installment at time t knowing the oustanding at time (t-1)
        remaining_periods = periods - (t - 1)

        # Installment, C(t_i)
        M[t,5] = rate * M[t-1,1]/(1 - 1/(1 + rate)**remaining_periods)

        # Interest rate payment, I(t_i) = r * N(t_{i})
        M[t,4] = rate * M[t-1,1]

        # Notional payment, Q(t_i) = C(t_i) - I(t_i)
        M[t,3] = M[t,5] - M[t,4]

        # Prepayment, P(t_i)= Lambda * (N(t_i) -Q(t_i))
        M[t,2] = CPR * (M[t-1,1] - M[t,3])

        # notional, N(t_{i+1}) = N(t_{i}) - lambda * (Q(t_{i} + P(t_i)))
        M[t,1] = M[t-1,1] - M[t,3] - M[t,2]
    return M

def mainCode():

    IncentiveFunction = lambda x : 0.04 + 0.1/(1 + np.exp(115 * (0.02-x)))

    oldRate = 0.05
    newRate = np.linspace(-0.05,0.15,25)

    epsilon = oldRate-newRate
    incentive = IncentiveFunction(epsilon)

    plt.figure(1)
    plt.plot(newRate,incentive)
    plt.xlabel('S(t)')
    plt.ylabel('Incentive')
    plt.grid()

    plt.figure(2)
    plt.plot(epsilon,incentive)
    plt.xlabel('epsilon= K - S(t)')
    plt.ylabel('Incentive')
    plt.grid()

    return 0.0

mainCode()

Lecture 08-Mortgages and Prepayments/Materials/Incentives.py


import numpy as np

def mean_square_error(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate MSE loss

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    MSE of given predictions
    """

    val = 0
    diffArr = y_true - y_pred
    for i in diffArr:
        val += np.power(i, 2)

    return val * (1 / diffArr.size)

def misclassification_error(y_true: np.ndarray, y_pred: np.ndarray,
                            normalize: bool = True) -> float:
    """
    Calculate misclassification loss

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values
    normalize: bool, default = True
        Normalize by number of samples or not

    Returns
    -------
    Misclassification of given predictions
    """
    counter = 0
    for i, j in zip(y_pred, y_true):
        if i != j:
            counter += 1
    if normalize: return counter / y_true.size
    return counter

def accuracy(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate accuracy of given predictions

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    Accuracy of given predictions
    """
    counter = 0
    for i, j in zip(y_pred, y_true):
        if i == j:
            counter += 1

    return counter / y_true.size

def cross_entropy(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate the cross entropy of given predictions

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    Cross entropy of given predictions
    """
    raise NotImplementedError()


import numpy as np

def hilb(n, m=0):
    """
    hilb   Hilbert matrix.
       hilb(n,m) is the n-by-m matrix with elements 1/(i+j-1).
       it is a famous example of a badly conditioned matrix.
       cond(hilb(n)) grows like exp(3.5*n).
       hilb(n) is symmetric positive definite, totally positive, and a
       Hankel matrix.

       References:
       M.-D. Choi, Tricks or treats with the Hilbert matrix, Amer. Math.
           Monthly, 90 (1983), pp. 301-312.
       N.J. Higham, Accuracy and Stability of Numerical Algorithms,
           Society for Industrial and Applied Mathematics, Philadelphia, PA,
           USA, 2002; sec. 28.1.
       M. Newman and J. Todd, The evaluation of matrix inversion
           programs, J. Soc. Indust. Appl. Math., 6 (1958), pp. 466-476.
       D.E. Knuth, The Art of Computer Programming,
           Volume 1, Fundamental Algorithms, second edition, Addison-Wesley,
           Reading, Massachusetts, 1973, p. 37.

       NOTE added in porting.  We do not use the function cauchy here to
       generate the Hilbert matrix.  That is done so we can unit test the
       the functions against each other.  Also, the function has been
       generalized to take by row and column sizes.  If only a row size
       is given, we assume a square matrix is desired.
    """
    if n < 1 or m < 0:
        raise ValueError("Matrix size must be one or greater")
    elif n == 1 and (m == 0 or m == 1):
        return np.array([[1]])
    elif m == 0:
        m = n

    return 1. / (np.arange(1, n + 1) + np.arange(0, m)[:, np.newaxis])


#
# Source Code  from https://people.duke.edu/~ccc14/sta-663/EMAlgorithm.html
# And here https://mk-minchul.github.io/EM/
#
import numpy as np

# This is our observed data
# Coin flips by two people, each 10 times, the first number is the number heads and scond is the number of tails
ys = np.array([(9,1), (8,2), (5,5), (4,6), (7,3)])

thetas = np.array([[0.6, 0.4], [0.5, 0.5]])  # initialize theta_1, 2
# Thetas can be the probabilities for the first and second persons for example.

pis =np.array([0.5, 0.5])  # prob of choosing coin 1 or coin2 is the same.

tolerance = 0.01
max_iter = 100

loglike_old = 0
for i in range(max_iter):
    E_c1 = []
    E_c2 = []
    EcY_1 = []
    EcY_2 = []

    loglike_new = 0
    # E-step: calculate probability distributions over possible completions
    for i in range(len(ys)):
        # multinomial (binomial) log likelihood
        log_k1 = np.sum([ys[i]*np.log(thetas[0])])  #  \log [\theta_k^{y_{oi}} (1-\theta_k)^{n - y_{oi}} ]
        log_k2 = np.sum([ys[i]*np.log(thetas[1])])  #  \log [\theta_k^{y_{oi}} (1-\theta_k)^{n - y_{oi}} ]

        # Getting the expectation of c_ik
        denom = np.exp(log_k1) * pis[0] + np.exp(log_k2) * pis[1]
        E_ci1 = np.exp(log_k1) * pis[0] / denom
        E_ci2 = np.exp(log_k2) * pis[1] / denom

        # update complete log likelihood
        # we need it only to check if it converged.
        # we dont need it for updating theta.
        loglike_new += E_ci1 * log_k1 + E_ci2 * log_k2
        E_c1.append(E_ci1)
        E_c2.append(E_ci2)

    # M-step: update values for parameters given current distribution
    for i in range(len(ys)):
        EcY_1.append(E_c1[i] * ys[i] )  # this is a scalar times a vector.
        EcY_2.append(E_c2[i] * ys[i] )

    thetas[0] = np.sum(EcY_1, 0)/np.sum(EcY_1)
    thetas[1] = np.sum(EcY_2, 0)/np.sum(EcY_2)

    print ("Iteration: %d" % (i+1))
    print ("theta_A = [%.2f, %.2f], theta_B = [%.2f, %.2f] , difference in loglike = %.2f" % (thetas[0,0], thetas[0,1], thetas[1,0], thetas[1,1], loglike_new - loglike_old))

    if np.abs(loglike_new - loglike_old) < tolerance:
        break

    loglike_old = loglike_new


#!/usr/bin/env python

''' Método de Newton: encontrar o mínimo dessa função '''

from sympy import lambdify, diff, cos, sin, exp
from sympy.plotting import plot, plot3d
from sympy.abc import x

from Error import *
from Log import *

MAX = 50
PATH = 'log/newton/'
TOLERANCE = 0.00000001 # 10**(-8)

def newton(fn, cx, tol, nmax) :
    l = Log()
    e = Error()
    previous = 0
    function = fn

    dx = fn.diff(x)
    dx = lambdify(x, dx)
    fn = lambdify(x, fn)

    for n in range(nmax) :
        if (dx(cx) == 0) :
            return "Error 25: derivative less than zero"
            breakpoint
        cx = cx - (fn(cx) / dx(cx) )
        e.absolute(cx, previous)
        e.relative(cx, previous)
        l.append([cx, e._absolute, e._relative, fn(cx), dx(cx)])
        if (e._absolute < tol) :
            l.set_header(['x', 'absolute_error', 'relative_error', 'function', 'derivative'])
            l.list2file((PATH+str(function)))
            return cx
            breakpoint
        previous = cx
    return False

def run_test(function, a, TOLERANCE, MAX):
    m = newton(function, a, TOLERANCE, MAX)
    print('f(x) =',f, '-> newton =', m)
    return m

if __name__ == "__main__":
    ''' Tests '''
    a = 0.5
    fx = [(cos(x) - x**3), ((3*(x**3))-2), (6*(x**2) - 400),
          (x**2 - 4*x -5), (x ** 2 - 10), (x**2 - 612),
          (x**3 - 1),(x ** 3 - 2 * x - 5), ((x**3)-(2*x)+2),
          (x**3-6*x**2+4*x+2), (x**3 - x**2 - 1),
          (x**6 - x - 1)]

    for f in fx:
        r = run_test(f, a, TOLERANCE, MAX)
        # graph = plot(f, show=False)
        # graph.save('view/function/'+str(f))

        # graph = plot3d(f, show=False)
        # graph.save('view/function/3D/'+str(f))


# coding: utf-8

# ## Matrix Object in Numpy
# Numpy, short for Numerical Python, is the fundamental package required for hight performance scientific computing and its best library to learn and apply on data science career.
#
# This is just little illustration.
#
# <img style="float: left;" src="https://www.safaribooksonline.com/library/view/python-for-data/9781449323592/httpatomoreillycomsourceoreillyimages1346880.png" width=400 height=200>

# In[1]:

import numpy as np

# In[2]:

a = np.array([[1,2,4],
              [2,5,3],
              [7,8,9]])
A = np.mat(a)
A

# In[3]:

A = np.mat('1,2,4;2,5,3;7,8,9')
A

# In[5]:

a = np.array([[ 1, 2],
              [ 3, 4]])
b = np.array([[10,20],
              [30,40]])

np.bmat('a,b;b,a')

# In[10]:

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

# In[11]:

A * x

# In[12]:

print(A * A.I)

# In[13]:

print(A ** 3)


from numpy import array
from numpy.linalg import det

def poly_fit(x,y):
    n = len(x)
    x1sm = sum(x)
    y1sm = sum(y)
    x2sm = sum(x**2)
    x3sm = sum(x**3)
    x4sm = sum(x**4)
    xysm = sum(x*y)
    x2ysm = sum((x**2)*y)
    # Cramer's rule
    eq = array([[x2sm,x3sm,x4sm],[x1sm,x2sm,x3sm],[n,x1sm,x2sm]])
    rig = array([x2ysm, xysm, y1sm])
    detabc = det(eq)
    eqc = array([[x2ysm,x3sm,x4sm],[xysm,x2sm,x3sm],[y1sm,x1sm,x2sm]])
    eqb = array([[x2sm,x2ysm,x4sm],[x1sm,xysm,x3sm],[n,y1sm,x2sm]])
    eqa = array([[x2sm,x3sm,x2ysm],[x1sm,x2sm,xysm],[n,x1sm,y1sm]])
    detc = det(eqc)
    detb = det(eqb)
    deta = det(eqa)
    a = deta/detabc
    b = detb/detabc
    c = detc/detabc
    return a,b,c

x = array([0, 1, 2, 4])
y = array([2, 3, 9, 15])
a,b,c = poly_fit(x,y)

print("a = %8.4f" % a)
print("b = %8.4f" % b)
print("b = %8.4f" % c)
print("y = %8.4f x^2 + %8.4f x + %8.4f" % (a, b, c))