from sympy import *
from sympy import simplify
from scipy.interpolate import lagrange
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#2a
def lang(x_i,y_i):
    x= symbols('x')
    if len(x_i)==len(y_i):
       yp=0
       for i in range(len(x_i)):
           p=1
           for j in range(len(x_i)):
               if j!=i:
                  p*=(x-x_i[j])/(x_i[i]-x_i[j])
           yp+=y_i[i]*p
       p_x=simplify(yp)
       fx=lambdify(x,p_x,modules=['numpy'])
       return fx
    else:
        return("len(x_i) should be equal to len(y_i)")

#2b
def in_lang(x_i,y_i):
    return lang(y_i,x_i)

#2c
def inbuilt(x_i,y_i,x1):
    poly = lagrange(x_i, y_i)
    L=poly(x1)
    return L

#Graph

def graph1(x_i,y_i,arr_x,arr_y_f,arr_y_in,pt_x,pt_y,xlab,ylab,titl):
    plt.scatter(x_i,y_i,marker='*',c="red",label="Given discrete points")
    plt.scatter(pt_x,pt_y,marker='o',c='black',label="Interpolated point")
    plt.plot(arr_x,arr_y_in,c='blue',label="Scipy's Inbuilt function",linestyle="-.")
    plt.plot(arr_x,arr_y_f,c="green",label="Interpolated langrange function")
    plt.xlabel(xlab)
    plt.ylabel(ylab)
    plt.title(titl)
    plt.grid(True)
    plt.legend()
    plt.show()

#3a
a=[0.00,0.2,0.4,0.6,0.8,1,1.2,1.4,1.6,1.8,2.0,2.2,2.4,2.6,2.8,3.0]
b=[1,0.99,0.96,0.91,0.85,0.76,0.67,0.57,0.46,0.34,0.22,0.11,0.00,-0.1,-0.18,-0.26]
g=lang(a,b)
k=in_lang(a,b)

arr_beta=np.linspace(0,3,1000)
arr_Jbeta_f=[]
arr_Jbeta_i=[]
for i in arr_beta:
    arr_Jbeta_f.append(g(i))
    arr_Jbeta_i.append(inbuilt(a,b,i))

arr_Jbeta=np.linspace(1,-0.26,1000)
arr_beta_f=[]
arr_beta_i=[]
for i in arr_Jbeta:
    arr_beta_f.append(k(i))
    arr_beta_i.append(inbuilt(b,a,i))

print("The value of bessel function for \u03B2 = 0.5 is ",g(2.3))
print("The value of \u03B2 for which the value of bessel function is 2.3 = ",k(0.5))
graph1(a,b,arr_beta,arr_Jbeta_f,arr_Jbeta_i,2.3,g(2.3),"\u03B2","J0_\u03B2","3a. (i) Bessel Function")
graph1(b,a,arr_Jbeta,arr_beta_f,arr_beta_i,0.5,k(0.5),"J0_\u03B2","\u03B2","3a. (ii) Inverse Bessel Function")

#3b
I=[2.81,3.24,3.80,4.30,4.37,5.29,6.03]
V=[0.5,1.2,2.1,2.9,3.6,4.5,5.7]
s=in_lang(I,V)
z=lang(I,V)

arr_I=np.linspace(2.81,6.03,1000)
arr_V_f=[]
arr_V_i=[]
for i in arr_I:
    arr_V_f.append(z(i))
    arr_V_i.append(inbuilt(I,V,i))

arr_V=np.linspace(0.5,5.7,1000)
arr_I_f=[]
arr_I_i=[]
for i in arr_V:
    arr_I_f.append(s(i))
    arr_I_i.append(inbuilt(V,I,i))

graph1(I,V,arr_I,arr_V_f,arr_V_i,3.79,z(3.79),"I","V","3b. (i) Photoelectric Effect")
graph1(V,I,arr_V,arr_I_f,arr_I_i,2.4,s(2.4),"V","I","3b. (ii) Inverse Photoelectric effect")
print("The value of I for V= 2.4 is ",s(2.4))

#Comparison
j=["3a (i)","3a (ii)","3b"]
d=[g(2.3),k(0.5),s(2.4)]
c=[inbuilt(a,b,2.3),inbuilt(b,a,0.5),inbuilt(V,I,2.4)]
error=np.array(d)-np.array(c)
print("# Comparison Table")
Data={"Ques":j,"Scipy":d,"My function":c,"Error":error}
print(pd.DataFrame(Data))


from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
image = np.array(Image.open('data/tiger.jpg'))

image = image / 255
row, col, _ = image.shape
print("pixels: ", row, "*", col)

fig = plt.figure(figsize=(15, 10))
a = fig.add_subplot(1, 1, 1)
imgplot = plt.imshow(image)
a.set_title('Royal Bengal Tiger, Sundarban, Bangladesh')
plt.show()

image_red = image[:, :, 0]
image_green = image[:, :, 1]
image_blue = image[:, :, 2]

original_bytes = image.nbytes
print("The space needed to store this image is: ", original_bytes/1024, " KB")

U_r, d_r, V_r = np.linalg.svd(image_red, full_matrices=True)
U_g, d_g, V_g = np.linalg.svd(image_green, full_matrices=True)
U_b, d_b, V_b = np.linalg.svd(image_blue, full_matrices=True)

bytes_to_be_stored = sum([matrix.nbytes for matrix in [U_r, d_r, V_r, U_g, d_g, V_g, U_b, d_b, V_b]])
print("The matrices that we store have total size: ", bytes_to_be_stored/1024, " KB")

k = 50

U_r_k = U_r[:, 0:k]
V_r_k = V_r[0:k, :]
U_g_k = U_g[:, 0:k]
V_g_k = V_g[0:k, :]
U_b_k = U_b[:, 0:k]
V_b_k = V_b[0:k, :]

d_r_k = d_r[0:k]
d_g_k = d_g[0:k]
d_b_k = d_b[0:k]

compressed_bytes = sum([matrix.nbytes for matrix in
                        [U_r_k, d_r_k, V_r_k, U_g_k, d_g_k, V_g_k, U_b_k, d_b_k, V_b_k]])
print("Compressed matrices have total size: ", compressed_bytes/1024, "KB")

ratio = compressed_bytes / original_bytes
print("Compression ratio between the original image size and the total size of the compressed factors is: ", ratio)

image_red_approx = np.dot(U_r_k, np.dot(np.diag(d_r_k), V_r_k))
image_green_approx = np.dot(U_g_k, np.dot(np.diag(d_g_k), V_g_k))
image_blue_approx = np.dot(U_b_k, np.dot(np.diag(d_b_k), V_b_k))

image_reconstructed = np.zeros((row, col, 3))

image_reconstructed[:, :, 0] = image_red_approx
image_reconstructed[:, :, 1] = image_green_approx
image_reconstructed[:, :, 2] = image_blue_approx

image_reconstructed[image_reconstructed < 0] = 0
image_reconstructed[image_reconstructed > 1] = 1

fig = plt.figure(figsize=(15, 10))
a = fig.add_subplot(1, 1, 1)
imgplot = plt.imshow(image_reconstructed)
a.set_title('Compressed image of the Royal Bengal Tiger, using best rank-{} approximation'.format(k))
plt.show()

k = 10

U_r_k = U_r[:, 0:k]
V_r_k = V_r[0:k, :]
U_g_k = U_g[:, 0:k]
V_g_k = V_g[0:k, :]
U_b_k = U_b[:, 0:k]
V_b_k = V_b[0:k, :]

d_r_k = d_r[0:k]
d_g_k = d_g[0:k]
d_b_k = d_b[0:k]

compressed_bytes = sum([matrix.nbytes for matrix in
                        [U_r_k, d_r_k, V_r_k, U_g_k, d_g_k, V_g_k, U_b_k, d_b_k, V_b_k]])
print("Compressed matrices have total size: ", compressed_bytes/1024, "KB")

image_red_approx = np.dot(U_r_k, np.dot(np.diag(d_r_k), V_r_k))
image_green_approx = np.dot(U_g_k, np.dot(np.diag(d_g_k), V_g_k))
image_blue_approx = np.dot(U_b_k, np.dot(np.diag(d_b_k), V_b_k))

image_reconstructed = np.zeros((row, col, 3))
image_reconstructed[:, :, 0] = image_red_approx
image_reconstructed[:, :, 1] = image_green_approx
image_reconstructed[:, :, 2] = image_blue_approx
image_reconstructed[image_reconstructed < 0] = 0
image_reconstructed[image_reconstructed > 1] = 1

fig = plt.figure(figsize=(15, 10))
a = fig.add_subplot(1, 1, 1)
imgplot = plt.imshow(image_reconstructed)
a.set_title('Compressed image of the Royal Bengal Tiger, using best rank-{} approximation'.format(k))
plt.show()

k = 200

U_r_k = U_r[:, 0:k]
V_r_k = V_r[0:k, :]
U_g_k = U_g[:, 0:k]
V_g_k = V_g[0:k, :]
U_b_k = U_b[:, 0:k]
V_b_k = V_b[0:k, :]

d_r_k = d_r[0:k]
d_g_k = d_g[0:k]
d_b_k = d_b[0:k]

image_red_approx = np.dot(U_r_k, np.dot(np.diag(d_r_k), V_r_k))
image_green_approx = np.dot(U_g_k, np.dot(np.diag(d_g_k), V_g_k))
image_blue_approx = np.dot(U_b_k, np.dot(np.diag(d_b_k), V_b_k))

image_reconstructed = np.zeros((row, col, 3))
image_reconstructed[:, :, 0] = image_red_approx
image_reconstructed[:, :, 1] = image_green_approx
image_reconstructed[:, :, 2] = image_blue_approx
image_reconstructed[image_reconstructed < 0] = 0
image_reconstructed[image_reconstructed > 1] = 1

fig = plt.figure(figsize=(15, 10))
a = fig.add_subplot(1, 1, 1)
imgplot = plt.imshow(image_reconstructed)
a.set_title('Compressed image of the Royal Bengal Tiger, using best rank-{} approximation'.format(k))
plt.show()


import collections
import math
import re
import string

import numpy as np

class Logistic_Regression():
    def __init__(self):
        self.W = None
        self.b = None

    def fit(self, x, y, batch_size=64, iteration=2000, learning_rate=1e-2):
        """
        Train this Logistic Regression classifier using mini-batch stochastic gradient descent.
        Inputs:
        - X: A numpy array of shape (N, D) containing training data; there are N
          training samples each of dimension D.
        - y: A numpy array of shape (N,) containing training labels; y[i] = c
          means that X[i] has label 0 <= c < C for C classes.
        - learning_rate: (float) learning rate for optimization.
        - iteration: (integer) number of steps to take when optimizing
        - batch_size: (integer) number of training examples to use at each step.

        Use the given learning_rate, iteration, or batch_size for this homework problem.

        Returns:
        None
        """
        dim = x.shape[1]
        num_train = x.shape[0]

        # initialize W
        if self.W is None:
            self.W = 0.001 * np.random.randn(dim, 1)
            self.b = 0

        for it in range(iteration):
            batch_ind = np.random.choice(num_train, batch_size)

            x_batch = x[batch_ind]
            y_batch = y[batch_ind]

            ############################################################
            ############################################################
            # BEGIN_YOUR_CODE
            # Calculate loss and update W, b

            z = x_batch.dot(self.W) + self.b
            y_hat = self.sigmoid(z)

            loss, grad = self.loss(x_batch, y_hat, y_batch)

            self.W = self.W - learning_rate * grad["dW"]
            self.b = self.b - learning_rate * grad["db"]

            y_pred = self.predict(x_batch)

            acc = np.mean(y_pred == y_batch)

            pass

            # END_YOUR_CODE
            ############################################################
            ############################################################

            if it % 50 == 0:
                print('iteration %d / %d: accuracy : %f: loss : %f' % (it, iteration, acc, loss))

    def predict(self, x):
        """
        Use the trained weights of this linear classifier to predict labels for
        data points.
        Inputs:

        Returns:
        - y_pred: Predicted labels for the data in X. y_pred is a 1-dimensional
          array of length N, and each element is an integer giving the predicted
          class.
        """
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate predicted y

        z = x.dot(self.W) + self.b
        y_pred = np.round(self.sigmoid(z))

        pass

        # END_YOUR_CODE
        ############################################################
        ############################################################
        return y_pred

    def loss(self, x_batch, y_pred, y_batch):
        """
        Compute the loss function and its derivative.
        Inputs:
        - X_batch: A numpy array of shape (N, D) containing a minibatch of N
          data points; each point has dimension D.
        - y_batch: A numpy array of shape (N,) containing labels for the minibatch.

        Returns: A tuple containing:
        - loss as a single float
        - gradient dictionary with two keys : 'dW' and 'db'
        """
        gradient = {'dW': None, 'db': None}
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate loss and gradient

        y_hat = y_pred
        n = y_hat.shape[0]
        loss = -np.sum(y_batch * np.log(y_hat + 1e-5)) / n

        dw = 2 * x_batch.transpose().dot(y_hat - y_batch) / n
        db = np.mean(y_hat - y_batch) / n

        gradient["dW"] = dw
        gradient["db"] = db

        pass

        # END_YOUR_CODE
        ############################################################
        ############################################################
        return loss, gradient

    def sigmoid(self, z):
        """
        Compute the sigmoid of z
        Inputs:
        z : A scalar or numpy array of any size.
        Return:
        s : sigmoid of input
        """
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate loss and update W

        z = np.clip(z, -500, 500)
        s = 1 / (1 + np.exp(-z))

        # END_YOUR_CODE
        ############################################################
        ############################################################

        return s

class Naive_Bayes():
    def fit(self, X_train, y_train):
        """
        fit with training data
        Inputs:
            - X_train: A numpy array of shape (N, D) containing training data; there are N
                training samples each of dimension D.
            - y_train: A numpy array of shape (N,) containing training labels; y[i] = c
                means that X[i] has label 0 <= c < C for C classes.

        With the input dataset, function gen_by_class will generate class-wise mean and variance to implement bayes inference.

        Returns:
        None

        """

        self.x = X_train
        self.y = y_train

        self.gen_by_class()

    def gen_by_class(self):
        """
        With the given input dataset (self.x, self.y), generate 3 dictionaries to calculate class-wise mean and variance of the data.
        - self.x_by_class : A dictionary of numpy arraies with the keys as each class label and values as data with such label.
        - self.mean_by_class : A dictionary of numpy arraies with the keys as each class label and values as mean of the data with such label.
        - self.std_by_class : A dictionary of numpy arraies with the keys as each class label and values as standard deviation of the data with such label.
        - self.y_prior : A numpy array of shape (C,) containing prior probability of each class
        """
        self.x_by_class = dict()
        self.mean_by_class = dict()
        self.std_by_class = dict()
        self.y_prior = None

        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Generate dictionaries.
        # hint : to see all unique y labels, you might use np.unique function, e.g., np.unique(self.y)

        pass

        # END_YOUR_CODE
        ############################################################
        ############################################################

    def mean(self, x):
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate mean of input x
        mean = 0
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################
        return mean

    def std(self, x):
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate standard deviation of input x, do not use np.std
        std = 0
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################
        return std

    def calc_gaussian_dist(self, x, mean, std):
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # calculate gaussian probability of input x given mean and std
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

    def predict(self, x):
        """
        Use the acquired mean and std for each class to predict class for input x.
        Inputs:

        Returns:
        - prediction: Predicted labels for the data in x. prediction is (N, C) dimensional array, for N samples and C classes.
        """

        n = len(x)
        num_class = len(np.unique(self.y))
        prediction = np.zeros((n, num_class))

        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # calculate naive bayes probability of each class of input x

        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

        return prediction

class Spam_Naive_Bayes(object):
    """Implementation of Naive Bayes for Spam detection."""
    def __init__(self):
        self.word_counts = {'spam': {}, 'ham': {}}
        self.num_messages = {'spam': 0, 'ham': 0}
        self.class_priors = {'spam': 0.0, 'ham': 0.0}

    def clean(self, s):
        translator = str.maketrans("", "", string.punctuation)
        return s.translate(translator)

    def tokenize(self, text):
        text = self.clean(text).lower()
        return re.split("\W+", text)

    def get_word_counts(self, words):
        """
        Generate a dictionary 'word_counts'
        Hint: You can use helper function self.clean and self.toeknize.
              self.tokenize(x) can generate a list of words in an email x.

        Inputs:
            -words : list of words that is used in a data sample
        Output:
            -word_counts : contains each word as a key and number of that word is used from input words.
        """
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # calculate naive bayes probability of each class of input x

        token = self.tokenize(words[0])
        word_counts = {}
        for word in token:
            if word in word_counts:
                word_counts[word] = word_counts[word] + 1
            else:
                word_counts[word] = 1

        # END_YOUR_CODE
        ############################################################
        ############################################################

        return word_counts

    def fit(self, X_train, y_train):
        """
        compute likelihood of all words given a class

        Inputs:
            -X_train : list of emails
            -y_train : list of target label (spam : 1, non-spam : 0)

        Variables:
            -self.num_messages : dictionary contains number of data that is spam or not
            -self.word_counts : dictionary counts the number of certain word in class 'spam' and 'ham'.
            -self.class_priors : dictionary of prior probability of class 'spam' and 'ham'.
        Output:
            None
        """
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # calculate naive bayes probability of each class of input x
        for x in range(len(X_train)):
            words = self.tokenize(X_train[x])
            if y_train[x] == 1:
                self.num_messages['spam'] = self.num_messages['spam'] + 1
            else:
                self.num_messages['ham'] = self.num_messages['ham'] + 1

            for word in words:
                if word not in self.word_counts['spam']:
                    self.word_counts['spam'][word] = 0
                if word not in self.word_counts['ham']:
                    self.word_counts['ham'][word] = 0
                if y_train[x] == 1:
                    self.word_counts['spam'][word] = self.word_counts['spam'][word] + 1
                else:
                    self.word_counts['ham'][word] = self.word_counts['ham'][word] + 1

        self.class_priors['spam'] = self.num_messages['spam'] / (self.num_messages['spam'] + self.num_messages['ham'])
        self.class_priors['ham'] = self.num_messages['ham'] / (self.num_messages['spam'] + self.num_messages['ham'])

        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

    def predict(self, X):
        """
        predict that input X is spam of not.
        Given a set of words {x_i}, for x_i in an email(x), if the likelihood

        p(x_0|spam) * p(x_1|spam) * ... * p(x_n|spam) * y(spam) > p(x_0|ham) * p(x_1|ham) * ... * p(x_n|ham) * y(ham),

        then, the email would be spam.

        Inputs:
            -X : list of emails

        Output:
            -result : A numpy array of shape (N,). It should tell rather a mail is spam(1) or not(0).
        """

        result = []
        for x in X:
            ############################################################
            ############################################################
            # BEGIN_YOUR_CODE
            # calculate naive bayes probability of each class of input x

            p_spam = -1
            p_ham = -1

            words = self.tokenize(x)
            for word in words:
                if p_spam == -1 and word in self.word_counts['spam']:
                    p_spam = self.word_counts['spam'][word] / self.num_messages['spam']
                    p_ham = self.word_counts['ham'][word] / self.num_messages['ham']
                elif word in self.word_counts['spam']:
                    p_spam = p_spam * (self.word_counts['spam'][word] / self.num_messages['spam'])
                    p_ham = p_ham * (self.word_counts['ham'][word] / self.num_messages['ham'])

            if p_spam > p_ham:
                result.append(1)
            else:
                result.append(0)

            pass
            # END_YOUR_CODE
            ############################################################
            ############################################################

        result = np.array(result)
        return result


import numpy as np
import math

def euclidiana(vetor): # norma-2 vetorial
    n, x = len(vetor), 0

    for i in range(n):
        x += math.fabs(vetor[i]) ** 2

    return x ** (1/2)

def manhattan(vetor): # norma-1 vetorial
    n, x = len(vetor), 0

    for i in range(n):
        x += math.fabs(vetor[i])

    return x

def p(vetor, p): #norma-p vetorial
    n, x = len(vetor), 0

    for i in range(n):
        x += math.fabs(math.pow(vetor[i], p))

    return x ** (1/p)

def infinita(vetor): # norma-infinita vetorial
    n, max = len(vetor), vetor[0]

    for i in range(1, n):
        if math.fabs(vetor[i]) > max:
            max = math.fabs(vetor[i])

    return max

def frobenius(matriz_a): # norma-2 matricial
    n, x = len(matriz_a), 0

    for i in range(n):
        for j in range(n):
            x += math.pow(math.fabs(matriz_a[i,j]), 2)

    return x ** (1/2)

def soma_coluna(matriz_a): # norma-1 matricial
    n, max, x = len(matriz_a), 0, 0

    for j in range(n):
        for i in range(n):
            x += math.fabs(matriz_a[i,j])

        if x > max:
            max = x

    return max

def soma_linha(matriz_a): # norma-infinita matricial
    n, max, x = len(matriz_a), 0, 0

    for i in range(n):
        for j in range(n):
            x += math.fabs(matriz_a[i,j])

        if x > max:
            max = x

    return max

def residual(matriz_a, vetor_b, delta_x): #norma-residual matricial
    n, k = len(matriz_a), np.linalg.cond(matriz_a)
    delta_b = np.matmul(matriz_a, delta_x)
    vetor_r = vetor_b - delta_b

    vetor_x = np.ones(n)
    vetor_x_menos_delta_x = vetor_x - delta_x
    residuo_r_b = euclidiana(vetor_r)/euclidiana(vetor_b)

    print("Vetor residual:\n", vetor_r)
    print("Resíduo da solução x:", residuo_r_b)

    if euclidiana(vetor_x_menos_delta_x)/euclidiana(vetor_x) <= k*residuo_r_b:
        print("A solução encontrada é precisa.")
    else:
        print("A solução encontrada não é precisa.")


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jul 17 16:07:48 2019

@author: TempestGuerra
"""

import numpy as np
from numpy import multiply as mul
from scipy import linalg as las
import math as mt
from scipy.special import roots_hermite
from scipy.special import roots_chebyt

def hefunclb(NX):
       #'''
       # Compute off-diagonals of 7.84 in Spectral Methods, Springer
       b = range(1,NX+1)
       bd = 0.5 * np.array(b)

       # Assemble the matrix
       m1 = np.diag(np.sqrt(bd), k=+1)
       m2 = np.diag(np.sqrt(bd), k=-1)
       mm = np.add(m1,m2)

       # Compute the eigenvalues of this matrix (zeros Hermite polys)
       ew = las.eigvals(mm)
       # Sort the eigenvalues in ascending order and store nodes
       xi = np.sort(np.real(ew))

       # Compute the Hermite function weights
       hf = hefuncm(NX, xi, False)
       w = 1.0 / (NX+1) * np.power(hf, -2.0)
       #'''
       '''
       xi, w = roots_hermite(NX+1)

       # Compute the Hermite function weights
       hf = hefuncm(NX, xi, False)
       w = 1.0 / (NX+1) * np.power(hf, -2.0)
       '''
       return xi, w

def hefuncm(NX, xi, fullMat):
       # Initialize constant
       cst = 1.0 / mt.pi**4;
       ND = len(xi)

       # Initialize the output matrix if needed
       if fullMat:
              HFM = np.zeros((NX+1,ND))

       # Compute the first two modes of the recursion
       wfun = np.exp(-0.5 * np.power(xi, 2.0))
       poly0 = cst * wfun;
       poly1 = cst * mt.sqrt(2.0) * (xi * wfun);

       # Put the first two functions in the matrix or return low order functions
       if fullMat:
              HFM[0,:] = poly0
              HFM[1,:] = poly1
       elif NX == 0:
              return poly0
       elif NX == 1:
              return poly1

       for nn in range(1,NX):
              polyn = mt.sqrt(2.0 / (nn+1)) * (xi * poly1)
              polyn -= mt.sqrt(nn / (nn+1)) * poly0
              poly0 = poly1;
              poly1 = polyn;
              # Put the new function in its matrix place
              if fullMat:
                     HFM[nn+1,:] = polyn
              else:
                     HFM = polyn

       return HFM.T

def cheblb(NZ):
       # Compute Chebyshev CGL nodes and weights
       ep = NZ - 1
       xc = np.array(range(NZ))
       xi = -np.cos(mt.pi / ep * xc)

       w = mt.pi / NZ * np.ones(NZ)
       w[0] *= 0.5
       w[ep] *= 0.5

       return xi, w

def chebpolym(NM, xi):
       # Compute Chebyshev pols (first kind) into a matrix transformation
       # Functions need to be arranged bottom to top!
       NX = len(xi)
       CTM = np.zeros((NX, NM+1))

       CTM[:,0] = np.ones(NX)
       CTM[:,1] = xi

       # 3 Term recursion for functions
       for ii in range(2, NM+1):
              CTM[:,ii] = 2.0 * \
              mul(xi, CTM[:,ii-1]) - \
              CTM[:,ii-2]

       return CTM


from sympy import Matrix
import numpy as np
import random

def GenerateMatrixWithRank(row, column, rank, lowerBound=-9, upperBound=9):
    """
    Generate Matrix with Rank=rank
    Res = A(row x rank) X B(rank x column)
    lowerBound: lower bound for generated entry
    upperBound: upper bound for generated entry
    """
    A = np.array([random.randint(lowerBound, upperBound) for x in range(row*rank)]).reshape(row, rank)
    B = np.array([random.randint(lowerBound, upperBound) for x in range(column*rank)]).reshape(rank, column)

    return np.matmul(A, B)

if __name__ == "__main__":
    rank = 1
    row = 5
    column = 7
    M = GenerateMatrixWithRank(row, column, rank, lowerBound=-3, upperBound=3)
    print(M)
    RREF, cols = Matrix.rref(Matrix(M))
    C = np.array(M)[:,[x for x in cols]]
    R = np.array(RREF)[[x for x in cols],:]

    print("C:")
    print(C)
    print("R:")
    print(R)
    print("C x R:")
    print(np.matmul(C, R))


import numpy as np
import matplotlib.pyplot as plt

# 计算在某一特定t下的 B_{i,k}
def getBt(controlPoints, knots, t):
	# calculate m,n,k
	m = knots.shape[0]-1
	n = controlPoints.shape[0]-1
	k = m - n - 1
	# initialize B by zeros
	B = np.zeros((k+1, m))

	# get t region
	tStart = 0
	for x in range(m+1):
		if t==1:
			tStart = m-1
		if knots[x] > t:
			tStart = x-1
			break

	# calculate B(t)
	for _k in range(k+1):
		if _k == 0:
			B[_k, tStart] = 1
		else:
			for i in range(m-_k):
				if knots[i+_k]-knots[i]== 0:
					w1 = 0
				else:
					w1 = (t-knots[i])/(knots[i+_k]-knots[i])
				if knots[i+_k+1]-knots[i+1] == 0:
					w2 = 0
				else:
					w2 = (knots[i+_k+1]-t)/(knots[i+_k+1]-knots[i+1])
				B[_k,i] = w1*B[_k-1, i] + w2*B[_k-1, i+1]
	return B

# 绘制 B_{i,k}(t)函数
def plotBt(Bt,num, i,k):
	print(k,i)
	Bt = np.array(Bt)
	tt = np.linspace(0,1,num)
	yy = [Bt[t,k,i] for t in range(num)]
	plt.plot(tt, yy)

# 根据最后一列（最高阶次）的 B(t)，即权重，乘以控制点坐标，从而求出曲线上点坐标
def getPt(Bt, controlPoints):
	Bt = np.array(Bt)
	ptArray = Bt.reshape(-1,1) * controlPoints
	pt = ptArray.sum(axis = 0)
	return pt

# 绘制出生成的样条曲线: useReg 表示是否使用曲线有效定义域[t_k, t_{m-k}]
def main1(controlPoints ,knots, useReg = False):

	m = knots.shape[0]-1
	n = controlPoints.shape[0]-1
	k = m - n - 1
	print('n:',n)
	print('m:',m)
	print('k:',k)

	for t in np.linspace(0,1,100):
		if useReg and not(t >= knots[k] and t<= knots[n+1]):
			continue
		Bt = getBt(controlPoints, knots, t)
		Pt = getPt(Bt[k, :n+1], controlPoints)

		plt.scatter(Pt[0],Pt[1],color='b')

	plt.scatter(controlPoints[:,0], controlPoints[:,1],color = 'r')
	plt.show()

# 绘制 B_{i,k} 变化图:如果不给定{i,k}则显示所有B{i,k}(t)图像
def main2(i=-1,k=-1):
	controlPoints = np.array([[50,50], [100,300], [300,100], [380,200], [400,600]])
	knots = np.array([0,1/9,2/9,3/9,4/9,5/9,6/9,7/9,8/9,1])
	m = knots.shape[0]-1
	n = controlPoints.shape[0]-1
	k = m - n - 1
	print('n:',n)
	print('m:',m)
	print('k:',k)
	B = []
	num = 100 # 离散点数目
	for t in np.linspace(0,1,num):
		Bt = getBt(controlPoints, knots, t)
		B.append(list(Bt))

	figure1 = plt.figure('B_{i,k}')
	if i==-1:
		fig = []
		for i in range(n+1):
			for k in range(k+1):
				plotBt(B,num, i,k)
				fig.append('B_{%d,%d}'%(i,k))
	else:
		plotBt(B,num, i,k)
		fig.append('B_{%d,%d}'%(i,k))
	plt.legend(fig)
	plt.show()

if __name__ == '__main__':
    controlPoints = np.array([
                [ 0.00000000e+00 , 0.00000000e+00 ], [-1.73422712e+01 , 2.15658488e-01],
                [-2.35382699e+01 , 3.13403809e-01] ,[-3.76766971e+01,  5.46545379e-01],
                [-4.78039077e+01 , 5.62960911e-01 ],[-5.98447978e+01 ,-7.11339258e-02],
                [-7.74681854e+01, -2.17359351e+00] ,[-8.87793826e+01 ,-1.75073904e+00],
                [-9.63270000e+01, -1.81000000e+00]
                            ])
    knots = np.array([0,0,0,0,1/6,1/3,1/2,2/3,5/6,1,1,1,1])

    main1(controlPoints,knots)


import numpy as np

from compute_cost import compute_cost

def gradient_descent(x, y, size, theta, alpha, iterations):
    """
        Performs gradient descent to optimize the 'theta' parameters. Updates theta for a total of
        inputted 'iterations', with a learning rate 'alpha'.

        Parameters
        ----------
        x : array_like
            Shape (m, n+1), where m is the number of examples, and n is the number of features
            including the vector of ones for the zeroth parameter.

        y : array_like
            Shape (m,), where m is the value of the function at each point.

        size : int
            Number of total training points.

        theta : array_like
            Shape (n+1, 1). Starting parameters of the regression function.

        alpha : float
            The learning rate.

        iterations : int
            The number of iterations for gradient descent.

        Returns
        -------
        theta : array_like
            Shape (n+1, 1). The optimized linear regression parameters.

        cost_history : list
            A list of the values of the cost function after each iteration.
    """

    cost_history = []
    converge = False
    for i in range(iterations):
        temp_cost = compute_cost(x, y, size, theta)
        try:
            if cost_history[-1] - temp_cost <= 0.0001:
                converge = True
        except IndexError:
            pass
        cost_history.append(temp_cost)

        delta = (1 / size) * ((np.dot(theta.T, x)) - y) * x
        delta2 = delta.sum(axis=1, keepdims=True)
        theta = (theta - (alpha * delta2))
    if converge:
        print("The function converged, use less iterations.")
    print(f"The new optimized parameters are: \n{theta}\n")
    return theta, cost_history


# Draw 100000 samples from Normal distribution with stds of interest: samples_std1, samples_std3, samples_std10
import matplotlib.pyplot as plt
import numpy as np

samples_std1 = np.random.normal(20, 1, size=100000)
samples_std3 = np.random.normal(20, 3, size=100000)
samples_std10 = np.random.normal(20, 10, size=100000)

# Make histograms
plt.hist(samples_std1, bins=100, normed = True, histtype = 'step')
plt.hist(samples_std3,bins=100, normed = True, histtype = 'step')
plt.hist(samples_std10, bins=100,normed = True, histtype = 'step')

# Make a legend, set limits and show plot
plt.legend(('std = 1', 'std = 3', 'std = 10'))
plt.ylim(-0.01, 0.42)
plt.show()


# -*-coding:utf-8 -*-
import numpy as np
from scipy import interpolate
import pylab as pl

x=np.linspace(0,10,11)
#x=[  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.]
y=np.sin(x)
xnew=np.linspace(0,10,101)
pl.plot(x,y,"ro")

for kind in ["nearest","zero","slinear","quadratic","cubic"]:#插值方式
    #"nearest","zero"为阶梯插值
    #slinear 线性插值
    #"quadratic","cubic" 为2阶、3阶B样条曲线插值
    f=interpolate.interp1d(x,y,kind=kind)
    # ‘slinear’, ‘quadratic’ and ‘cubic’ refer to a spline interpolation of first, second or third order)
    ynew=f(xnew)
    pl.plot(xnew,ynew,label=str(kind))
pl.legend(loc="lower right")
pl.show()


import numpy as np

print("")
print("Ejercicio 1.1")
A = np.array([[4, -2, 1, ], [3, 6, -4], [2, 1, 8]])
A_inversa = np.linalg.inv(A)
print("")
print("Matriz A")
print(A)
print("")
print("Matriz A^-1")
print(A_inversa)
print("")
print("Verificando A * A^-1")
print(A@A_inversa)

print("")
print("Ejercicio 1.2")
A_inversa_analitica = (1/263)*np.array([[52, 17, 2],[-32, 30, 19],[-9, -8, 30]])
print("")
print("Cifras significativas en A^{-1}")
print(np.log10(np.abs(A_inversa-A_inversa_analitica)))

print("")
print("Ejercicio 2")
b = np.array([12, -25, 32])
x_sol = np.array([+1, -2, +4])
x = np.linalg.solve(A, b)
print("")
print("numerico: {} . analitico: {}".format(x, x_sol))

b = np.array([+4, -10, +22])
x_sol = np.array([+0.312, -0.038, +2.677])
x = np.linalg.solve(A, b)
print("")
print("numerico: {} . analitico: {}".format(x, x_sol))

b = np.array([+20, -30, +40])
x_sol = np.array([+2.319, -2.965, +4.790])
x = np.linalg.solve(A, b)
print("")
print("numerico: {} . analitico: {}".format(x, x_sol))

print("")
print("Ejercicio 3")
alpha = 2.0
beta = 1.0
A = np.array([[alpha, beta], [-beta, alpha]])
values, vectors = np.linalg.eig(A)
print("")
print('lambda_1 {}. eigenvec_1 {}'.format(values[0], vectors[:,0]))
print('lambda_2 {}. eigenvec_2 {}'.format(values[1], vectors[:,1]))
print('son complejos conjugados como se esperaba')

print("Ejercicio 4")
A = np.array([[-2, +2, -3], [+2, +1, -6], [-1, -2, +0]])
values, vectors = np.linalg.eig(A)
print("")
print("Obtengo autovalores: {}. Esperaba {} {} {}".format(values, 5, -3, -3))
eigen_5 = 1/np.sqrt(6)*np.array([-1, -2, +1])
eigen_3_a = 1/np.sqrt(5)*np.array([-2, +1, 0])
eigen_3_b = 1/np.sqrt(10)*np.array([3, 0, +1])

print("")
print("Eigenvector para lambda=5: {} (que es proporcional a {})".format(vectors[:,1], eigen_5))

print("")
print("Eigenvector para lambda=3: {} ".format(vectors[:,0]))
beta_a = vectors[1,0]*np.sqrt(5) # componente y
beta_b = vectors[2,0]*np.sqrt(10) # componente z
print("Combinacion lineal de dos vectores\n\t vec_a: {} y vec_b {}".format(eigen_3_a, eigen_3_b))
print("\t vec_3 = beta_a * vec_a + beta_b * vec_b: {}".format((beta_a * eigen_3_a + beta_b * eigen_3_b)))

print("")
print("Eigenvector para lambda=3: {} ".format(vectors[:,2]))
beta_a = vectors[1,2]*np.sqrt(5) # componente y
beta_b = vectors[2,2]*np.sqrt(10) # componente z
print("Combinacion lineal de dos vectores\n\t vec_a: {} y vec_b {}".format(eigen_3_a, eigen_3_b))
print("\t vec_3 = beta_a * vec_a + beta_b * vec_b: {}".format((beta_a * eigen_3_a + beta_b * eigen_3_b)))

print("")
print("Ejercicio 5")
n = 100
A = np.zeros([n,n])
b = np.ones(n)
for i in range(1,n+1):
    b[i-1] = 1/i
    for j in range(1,n+1):
        A[i-1,j-1] = 1/(i+j-1)
x = np.linalg.solve(A,b)
print("Solucion con todos los elementos igual a cero salvo el primero:\n\t {}".format(x))


import numpy as np
import pandas as pd
import scipy.stats as sp

class BlackScholes:
    """
    Black-Scholes model for pricing European options.

    Notes
    -----
    Assumptions of the model:

    - the volatility of the underlying asset is constant over time;
    - the underlying asset price follows the log-normal distribution;
    - the underlying assset can be traded continuously;
    - no transaction costs or taxes;
    - all securities are perfectly divisible;
    - the risk-free rate is constant and the same for all maturities.

    Price of the call option was calculated based on below formula:

    .. math:: c = S_{0}N(d_{1}) - Ke^{-rT}N(d_{2})

    On the other hand, price of the put option was derived from the formula:

    .. math:: p = Ke^{-rT}N(-d_{2}) - S_{0}N(-d_{1}),

    where:

    .. math:: d_{1} = \\frac{ ln(S_{0}/K) + (r + \\sigma^{2}/2)T }{ \\sigma \\sqrt{T} } \\
        d_{2} = \\frac{ ln(S_{0}/K) + (r - \\sigma^{2}/2)T }{ \\sigma \\sqrt{T} } = d_{1} - \\sigma \\sqrt{T}

    """

    def __init__(self, S: float, K: float, r: float, q: float=0, T: float, sigma: float, type: str) -> None:
        """
        Class initializer.

        Parameters
        ----------
        S : float
            Price of the underlying.
        K : float
            Strike price.
        r : float
            Risk free rate.
        q : float
            Dividend rate.
        T : float
            Time to expiry (in years).
        sigma : float
            Underlying volatility.
        type : str
            Option type: "put" or "call".
        """
        self.S = S
        self.K = K
        self.r = r
        self.q = q
        self.T = T
        self.sigma = sigma
        self.type = type

    def d1(self) -> float:
        """
        Calculates d1 from the class description.

        Returns
        -------
        float
            d1 value
        """
        return (np.log(self.S/self.K) + (self.r + (self.sigma**2)/2)*self.T) / (self.sigma*np.sqrt(self.T))

    def d2(self) -> float:
        """
        Calculates d2 from the class description.

        Returns
        -------
        float
            d2 value
        """
        return self.d1() - self.sigma*np.sqrt(self.T)

    def price(self) -> float:
        """
        Runs Black-Scholes calculation.

        Returns
        -------
        float
            Option price
        """
        if self.type == 'call':
            return self.S*sp.norm.cdf(self.d1()) - self.K*np.exp(-self.r*self.T)*sp.norm.cdf(self.d2())

        elif self.type == 'put':
            return self.K*np.exp(-self.r*self.T)*sp.norm.cdf(-self.d2()) - self.S*sp.norm.cdf(-self.d1())

        else:
            raise ValueError("Option price type can only be call or put")


"""
Lagrange (Polynomial Interpolation)
"""

import numpy as np

def L(i, X):
    """
    This function returns l(x).

    Parameters
    ----------
        i : int
            Index of X.
        X : list
            List of x values.

    Returns
    -------
        function
            l(x)
    """
    temp = X.copy()
    xi = temp.pop(i)

    def l(x):
        return np.prod([(x - xj) / (xi - xj) for xj in temp])

    return l

def lagrange(X, Y):
    """
    Polynomial Interpolation using Lagrange method.
    X and Y are data from `(x, f(x))`.

    Parameters
    ----------
        X : list
            list of x values.
        Y : list
            list of y values.

    Returns
    -------
        function
            Lagrange Polynomial from `X` and `Y` values.
    """
    def f(x):
        return sum([yi * L(i, X)(x) for i, yi in enumerate(Y)])

    return f


""" Example showing how to compute a family of basis polynomials """

# -------------------------------------------------------------------------------------------------------------------- #
# Importing packages
# -------------------------------------------------------------------------------------------------------------------- #
import numpy as np
import nurbspy as nrb
import matplotlib.pyplot as plt

# -------------------------------------------------------------------------------------------------------------------- #
# Basis polynomials and derivatives example
# -------------------------------------------------------------------------------------------------------------------- #
# Maximum index of the basis polynomials (counting from zero)
n = 4

# Define the order of the basis polynomials
p = 3

# Define the knot vector (clamped spline)
# p+1 zeros, n-p equispaced points between 0 and 1, and p+1 ones. In total r+1 points where r=n+p+1
U = np.concatenate((np.zeros(p), np.linspace(0, 1, n - p + 2), np.ones(p)))

# Define a new u-parametrization suitable for finite differences
h = 1e-5
hh = h + h**2
Nu = 1000
u = np.linspace(0.00 + hh, 1.00 - hh, Nu)       # Make sure that the limits [0, 1] also work when making changes

# Compute the basis polynomials and derivatives
N_basis   = nrb.compute_basis_polynomials(n, p, U, u)
dN_basis  = nrb.compute_basis_polynomials_derivatives(n, p, U, u, derivative_order=1)
ddN_basis = nrb.compute_basis_polynomials_derivatives(n, p, U, u, derivative_order=2)

# -------------------------------------------------------------------------------------------------------------------- #
# Plot the basis polynomials
# -------------------------------------------------------------------------------------------------------------------- #
# Create the figure
fig = plt.figure(figsize=(15, 5))

# Plot the basis polynomials
ax1 = fig.add_subplot(131)
ax1.set_title('Zeroth derivative', fontsize=12, color='k', pad=12)
ax1.set_xlabel('$u$ parameter', fontsize=12, color='k', labelpad=12)
ax1.set_ylabel('Function value', fontsize=12, color='k', labelpad=12)
for i in range(n+1):
    line, = ax1.plot(u, N_basis[i, :])
    line.set_linewidth(1.25)
    line.set_linestyle("-")
    # line.set_color("k")
    line.set_marker(" ")
    line.set_markersize(3.5)
    line.set_markeredgewidth(1)
    line.set_markeredgecolor("k")
    line.set_markerfacecolor("w")
    line.set_label('index ' + str(i))

# Plot the first derivative
ax2 = fig.add_subplot(132)
ax2.set_title('First derivative', fontsize=12, color='k', pad=12)
ax2.set_xlabel('$u$ parameter', fontsize=12, color='k', labelpad=12)
ax2.set_ylabel('Function value', fontsize=12, color='k', labelpad=12)
for i in range(n+1):
    line, = ax2.plot(u, dN_basis[i, :])
    line.set_linewidth(1.25)
    line.set_linestyle("-")
    # line.set_color("k")
    line.set_marker(" ")
    line.set_markersize(3.5)
    line.set_markeredgewidth(1)
    line.set_markeredgecolor("k")
    line.set_markerfacecolor("w")
    line.set_label('index ' + str(i))

# Plot the second derivative
ax3 = fig.add_subplot(133)
ax3.set_title('Second derivative', fontsize=12, color='k', pad=12)
ax3.set_xlabel('$u$ parameter', fontsize=12, color='k', labelpad=12)
ax3.set_ylabel('Function value', fontsize=12, color='k', labelpad=12)
for i in range(n+1):
    line, = ax3.plot(u, ddN_basis[i, :])
    line.set_linewidth(1.25)
    line.set_linestyle("-")
    # line.set_color("k")
    line.set_marker(" ")
    line.set_markersize(3.5)
    line.set_markeredgewidth(1)
    line.set_markeredgecolor("k")
    line.set_markerfacecolor("w")
    line.set_label('index ' + str(i))

# Create legend
ax3.legend(ncol=1, loc='right', bbox_to_anchor=(1.60, 0.50), fontsize=10, edgecolor='k', framealpha=1.0)

# Adjust pad
plt.tight_layout(pad=5.0, w_pad=None, h_pad=None)

# Show the figure
plt.show()

# # -------------------------------------------------------------------------------------------------------------------- #
# # Check that the computations are correct
# # -------------------------------------------------------------------------------------------------------------------- #
# # Check that the sum of the basis polynomials is equal to one (partition of unity property)
# print('The two-norm of partition of unity error is     :  ', np.sum((np.sum(N_basis, axis=0) - 1.00) ** 2) ** (1 / 2))
#
# # Check the first derivative against a finite difference aproximation
# a = -1/2*compute_basis_polynomials(n, p, U, u - h)
# b = +1/2*compute_basis_polynomials(n, p, U, u + h)
# dN_fd = (a+b)/h
# print('The two-norm of the first derivative error is   :  ', np.sum((dN_basis-dN_fd)**2)**(1/2)/Nu)
#
# # Check the second derivative against a finite difference aproximation
# a = +1*compute_basis_polynomials(n, p, U, u - h)
# b = -2*compute_basis_polynomials(n, p, U, u)
# c = +1*compute_basis_polynomials(n, p, U, u + h)
# ddN_fd = (a+b+c)/h**2
# print('The two-norm of the second derivative error is  :  ', np.sum((ddN_basis-ddN_fd)**2)**(1/2)/Nu)


import numpy as np

def createSquare(size):
    """Creates an N x N magic square

    In a magic square, every row, column, and diagonal add up to the same number.

    @param size The size of the magic square
    @return A boolean representing if the square was created or not
    """

    # The size must be odd
    if size % 2 == 0:
        print('Size must be odd')
        return False

    # Initialize with zeros
    magic_square = np.zeros((size,size), dtype=int)

    n = 1
    i, j = 0, size//2

    while n <= size**2:
        magic_square[i, j] = n
        n += 1
        newi, newj = (i - 1) % size, (j + 1) % size
        if magic_square[newi, newj]:
            i += 1
        else:
            i, j = newi, newj

    print(magic_square)
    return True

def runTests():
    # Test cases
    assert createSquare(3) is True
    assert createSquare(5) is True
    assert createSquare(6) is False
    assert createSquare(4) is False

runTests()


#!/usr/bin/env python

"""
Usage:
python polyarea.py c 1 2 4 8 3 5
python polyarea.py f coord.txt
python polyarea.py help
"""

import sys
import numpy as np

def simpoly(x,y):
    """
    A function that calculates the area of a 2-D simple polygon (no matter concave or convex)
    Must name the vertices in sequence (i.e., clockwise or counterclockwise)
    Inputs must be float type
    Formula used: http://en.wikipedia.org/wiki/Polygon#Area_and_centroid
    Definition of "simply polygon": http://en.wikipedia.org/wiki/Simple_polygon

    Input x: x-axis coordinates of vertex array
          y: y-axis coordinates of vertex array
    Output: polygon area
    """

    ind_arr = np.arange(len(x))-1  # for indexing convenience
    s = 0
    for ii in ind_arr:
      s = s + (x[ii]*y[ii+1] - x[ii+1]*y[ii])

    return abs(s)*0.5

if __name__ == "__main__":
    usage_str = 'Usage:\npython polyarea.py c 1 2 4 8 3 5 \npython polyarea.py f coord.txt\npython polyarea.py help'

    if len(sys.argv) == 1:
        print "Error: argument input needed"
        print usage_str
        exit()

    if sys.argv[1].lower() == 'c':
        # Recognize the inputs as the coordinates
        if len(sys.argv) <= 7:
            print "Error: at least three 2-D points needed for a valid polygon"
            print "Exiting..."
            exit()
        elif np.mod(len(sys.argv[2:]), 2) != 0:
            print "Error: the number of input arguments should be even"
            print "Exiting..."
            exit()
        else:
            print "This polygon has", (len(sys.argv)-2)/2, "vertices"

        a = np.zeros(len(sys.argv)-2)  # the default a.dtype.name is float64

        ind = 0
        for coord in sys.argv[2:]:
            a[ind]=float(eval(coord))  # convert the input arguments to "float" type
            ind = ind+1

        b = a.reshape(-1, 2).copy()
        x = b[:,0].copy()   # get x coords
        y = b[:,1].copy()   # get y coords

        print "The area of this polygon is", simpoly(x,y)

    elif sys.argv[1].lower() == 'f':
        # Get the input from a file
        # in which the 1st column is x coordinates, and the 2nd column is y coordinates
        d = np.loadtxt(sys.argv[2])
        x = d[:,0]
        y = d[:,1]
        print "The area of this polygon is", simpoly(x,y)

    elif sys.argv[1].lower() == 'help':
        print usage_str
        exit()

    else:
        print "Error need input arg either c (command line) or f (file)"
        print "Exiting"
        exit()


import numpy
from matplotlib import pyplot

def bisection(f, interval, max_steps=100, tol=1e-10):

    x_lo, x_hi = interval
    x = (x_lo + x_hi)/2
    f_lo = f(x_lo)
    f_hi = f(x_hi)
    fx = f(x)
    steps = 0

    while steps < max_steps and abs(fx) > tol and (x_hi - x_lo) > tol:
        steps = steps + 1
        if fx*f_hi < 0: # Root lies in right-hand half
            x_lo = x
            f_lo = fx
        else: # Root lies in left-hand half
            x_hi = x
            f_hi = fx
        x = (x_lo + x_hi) / 2
        fx = f(x)
    print("Nsteps", steps)
    return x

if __name__=="__main__":
    def f(x):
        return numpy.exp(x) + x - 2
    def g(x):
        return numpy.sin(x**2) - 0.1*x

    interval = [0,1]
    s = bisection(f, interval)
    print("s = ", s, "f(s) = ", f(s))

    x = numpy.linspace(0, 10, 1000)
    pyplot.plot(x, g(x))
    pyplot.show()
    s = bisection(g, [1,10])
    print("s = ", s, "g(s) = ", g(s))
    s = bisection(g, [1,9])
    print("s = ", s, "g(s) = ", g(s))
    s = bisection(g, [1,8.5])
    print("s = ", s, "g(s) = ", g(s))
    s = bisection(g, [1,8])
    print("s = ", s, "g(s) = ", g(s))


from scipy import *
from scipy import interpolate
from scipy.interpolate import barycentric_interpolate as bi
import numpy as np
import pylab as pl #where we get graph function
from scipy.optimize import curve_fit
from polyFit import *
from scipy import polyfit

h=np.array([0.0,1.525,30.050,4.575,6.10,7.625,9.150])
p=np.array([1.0,0.8617,0.7385,0.6292,0.5328,0.4481,0.3741])

z=np.arange(0,9.3,0.1) #gives points to evaluate a polynomial fit going through p and h
a=bi(h,p,2) #evaluates a polynomial going through the  points p(h) gives p at h=2
print a

z=np.arange(0,9.3,0.1)
b=bi(h,p,4)
print b

z=np.arange(0,9.2,0.1)
c=bi(h,p,8)
print c

print 'part b'
f=interpolate.interp1d(h,p,kind='cubic') #creates a cubic function that fits the graph

d=f(2) #uses interpolation function
print 'Density at 2km using cubic spline interpolation'
print d

e=f(4) #uses interpolation function
print 'Density at 4km using cubic spline interpolation'
print e

f=f(8) #uses interpolation function
print 'Density at 8km using cubic spline interpolation'
print f

print 'part c- getting errors'
actualvalue=0.67
e1= (b-actualvalue)/actualvalue
print e1

actualvalue=0.67
e2= (e-actualvalue)/actualvalue
print e2

ExamPrep/04 Curve Fitting & Interpolation/rp2.py


import numpy as np

def rbf(x1, x2, gamma=1.):
    """
    rbf kernel function

    Parameters
    ------------
    x1 : numpy array, (..., n)
        the first input feature vector
    x2 : numpy array, (..., n)
        the second input feature vector
    gamma : positive double, default: 1
        gamma=0.5/sigma**2

    Returns
    ---------
    kernel : numpy array
        output kernel
    """
    return np.exp(-gamma*((x1-x2)**2).sum(axis=-1))

def linear(x1, x2):
    """
    linear kernel function

    Parameters
    ------------
    x1 : numpy array, (..., n)
        the first input feature vector
    x2 : numpy array, (..., n)
        the second input feature vector

    Returns
    ---------
    kernel : numpy array
        output kernel
    """
    return (x1*x2).sum(axis=-1)

def poly(x1, x2, degree=3, gamma=1., r=0.):
    """
    polynomial kernel function

    Parameters
    ------------
    x1 : numpy array, (..., n)
        the first input feature vector
    x2 : numpy array, (..., n)
        the second input feature vector
    degree : positive double, default: 3
        degree of the polynomial kernel function
    gamma : positive double, default: 1
        kernel coefficient
     r : positive double, default: 0
         independent term

    Returns
    ---------
    kernel : numpy array
        output kernel
    """
    return (gamma*(x1*x2).sum(axis=-1) + r)**degree


import numpy as np
import submission as sub
import numpy.linalg as la

# 1. Generate random camera matrix

K = np.array([[1,0,100], [0,1,100], [0,0,1]])
R, _,_ = la.svd(np.random.randn(3,3))
if la.det(R) < 0: R = -R
t = np.vstack((np.random.randn(2,1), 1))

P = K @ np.hstack((R, t))

# 2. Generate random 2D and 3D points

N = 100

X = np.random.randn(N,3)
x = P @ np.hstack((X, np.ones((N,1)))).T
x = x[:2,:].T / np.vstack((x[2,:], x[2,:])).T

# 3. Test parameter estimation with clean points

Pc = sub.estimate_pose(x, X)
Kc, Rc, tc = sub.estimate_params(Pc)

print('Intrinsic Error with clean 2D points:', la.norm((Kc/Kc[-1,-1])-(K/K[-1,-1])))
print('Rotation Error with clean 2D points:', la.norm(R-Rc))
print('Translation Error with clean 2D points:', la.norm(t-tc))

# 4. Test parameter estimation with noisy points

x = x + np.random.rand(x.shape[0], x.shape[1])
Pn = sub.estimate_pose(x, X)
Kn, Rn, tn = sub.estimate_params(Pn)

print('Intrinsic Error with noisy 2D points:', la.norm((Kn/Kn[-1,-1])-(K/K[-1,-1])))
print('Rotation Error with noisy 2D points:', la.norm(R-Rn))
print('Translation Error with noisy 2D points:', la.norm(t-tn))


import numpy as np

def MSE(y_true, y_pred):
    """
        Mean Squared Error

        parameters :
        -> y_true, y_pred (numpy arrays)

        returns :
        -> scalar value of the loss
    """
    return (np.square(y_true - y_pred)).mean()

def MAE(y_true, y_pred):
    """
        Mean Absolute Error

        parameters :
        -> y_true, y_pred (numpy arrays)

        returns :
        -> scalar value of the loss
    """
    return np.abs(y_true - y_pred).mean()

def CrossEntropyLoss(y_true, y_pred, epsilon=1e-10):
    """
        Cross Entropy Loss

        parameters :
        -> y_true, y_pred, epsilon

        returns :
        -> scalar value of the loss
    """
    predictions = np.clip(y_pred, epsilon, 1. - epsilon)
    N = y_pred.shape[0]
    ce_loss = -np.sum(np.sum(y_true * np.log(predictions + 1e-5)))/N
    return ce_loss


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

# define grid
p_grid = np.linspace(0, 1, num=20)

# define prior
prior = np.repeat(1, 20)

# Other priors
# prior[p_grid < 0.5] = 0
# prior = np.exp(-5 * np.abs(p_grid - 0.5))

# compute likelihood at each value in grid
likelihood = stats.binom.pmf(k=6, n=9, p=p_grid)

# compute product of likelihood and prior
unstd_posterior = likelihood * prior

# standardize the posterior, so it sums to 1
posterior = unstd_posterior / sum(unstd_posterior)

# Show plot
plt.plot(p_grid, posterior, '-o')
plt.xlabel("Probability of water")
plt.ylabel("Posterior probability")
plt.title("20 points")
plt.show()


"""

https://en.wikipedia.org/wiki/Companion_matrix
http://et.engr.iupui.edu/~skoskie/ECE602/LNotes/A_Companion_Matrix.pdf

"""

import numpy as np
from sympy import *
from sympy.abc import *
from sympy.solvers import solve

"""

A companion matrix is of the form
C(p) = [0 0 ... 0 -a0   ]
       [1 0 ... 0 -a1   ]
       [0 1 ... 0 -a2   ]
       [    ...         ]
       [0 0 ... 1 -a[n-1]]

where the coefficients a0 .. a[n-1] are from a monic polynomial
c0 + c1*t + c2*t^2 + ... c[n-1]*t^(n-1) + t^n

"""

def gen_companion_matrix(n):
    A = [[0 for i in range(n)] for j in range(n)]
    for i in range(n-1):
        A[i+1][i] = 1
    for i in range(n):
        A[i][n-1] = -1 * Symbol('a{}'.format(i))
    return Matrix(A)

"""

det(lambda*I - A) or det(lambda*I - transpose(A))
gives rise to the monic polynomial
c0 + c1*x + c2*x^2 ... c[n-1]*x^(n-1) + x^n
where lambda gets substituted to be x

"""

def test_monic_polynomial_property(n):
    l = Symbol('x')
    A = gen_companion_matrix(n)
    I = Matrix(np.identity(n))
    p = expand(simplify(det(l*I - A)))
    q = expand(simplify(det(l*I - A.T)))
    print("Order {}".format(n))
    pprint(p)
    pprint(q)
    print()

def test_power(n, m):
    l = Symbol('x')
    A = gen_companion_matrix(n)
    I = Matrix(np.identity(n))
    p = expand(simplify(det(l*I - A)))
    for i in range(m):
        print("Power {}x{} {}".format(n, n, i))
        pprint(A**i)

"""

The inverse of a companion matrix is in the form of
C(p)^-1 = [-a1/a0     1 0 ... 0
           -a2/a0     0 1 ... 0
           -a3/a0     0 0 ... 0
            ...
           -a[n-1]/a0 0 0 ... 1
           -1/a0]     0 0 ... 0]
"""
def test_inverse(n):
    l = Symbol('x')
    A = gen_companion_matrix(n)
    pprint(A)
    pprint(A.inv())
    pprint(A.T)
    pprint(A.T.inv())

init_printing()
print("Monic Polynomial Property")
for i in range(10):
    test_monic_polynomial_property(i)
print()

print("Power")
for i in range(10):
    test_power(i, 5)
print()

print("Inverse")
for i in range(10):
    test_inverse(i)


# normalize.py

import numpy as np
from math import sqrt
from sklearn.preprocessing import minmax_scale

def normalize_l2 (x):
    """ Rerturns a normalized copy of
    an array x. Uses L2 vector norm
    for normalization.
    """
    l2 = sqrt(sum(i**2 for i in x))
    return [i/l2 for i in x]

def de_normalize_l2 (x_norm, x):
    """ Transforms a normalized vector
    back to its un-normalized form.
    """
    l2 = sqrt(sum(i**2 for i in x))
    return [i*l2 for i in x_norm]

def max_scaling(x):
    """ Scaling the values of an array x
    with respect to the maximum value of x.
    """
    return [i/max(x) for i in x]

def normalize_minmax(x, min_r, max_r):
    """ Normalizing and scaling given data
    in an array x to the range of min_r
    to max_r.
    """
    x_s = [(i - min(x))/(max(x) - min(x)) for i in x]
    return [i * (max_r - min_r) + min_r for i in x_s]

def de_normalize_minmax(x_scale, x, min_r, max_r):
    """ Transforms a min-max normalized and
    scaled vector back to its un-normalized
    form.
    """
    x_t = [((i - min_r)/(max_r - min_r)) for i in x_scale]
    x_inv = [(i*(max(x) - min(x)) + min(x)) for i in x_t]
    return x_inv

# Test data
x = [2000, 2001, 2003]

# Exampels:
# L2 normalization
print(normalize_l2(x))
print(de_normalize_l2(normalize_l2(x), x))

# Max scaling
print(max_scaling(x))

# Min-Max scaling
print(normalize_minmax(x, 0, 10))
print(de_normalize_minmax(normalize_minmax(x, 0, 10), x, 0, 10))

# sklearn data scaling
print(minmax_scale(x, feature_range=(0, 1), axis=0, copy=False))


import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as si
plt.style.use('seaborn-whitegrid')

def f(x) :

    s = 1/(1+25*np.power(x,2))

    return s

def Lagrance(n) :

    x_nodes=np.linspace(-1,1,n)
    y_val=f(x_nodes)
    polynomial = si.lagrange(x_nodes, y_val)

    return polynomial,x_nodes,y_val

#----------- Main Programme --------------------#

n=10
x=np.linspace(-1,1,100)
polynomial,x_nodes,y_val = Lagrance(n)

plt.plot(x,f(x),'lime')
y=polynomial(x)
plt.plot(x_nodes,y_val,'+')
plt.plot(x,y,'green')
plt.xlabel('x')
plt.ylabel('y')
plt.xlim(-1,1)
plt.legend(['f(x)','Points','Lagrance'])
plt.show()


import numpy as np
import  matplotlib.pyplot as plt

def factorial(n):
    if n == 0:
        return 1
    else:
        fat = 1
        for i in range(1, n+1):
            fat = fat *i

        return  fat

#print(factorial(5))

def cos(x,n):
    resul  = 0
    for i in range(0,n+1):
        resul += (-1)**i * x**(2*i)/factorial(2*i)
    return resul

"""
x = int(raw_input("The value of x:"))
n = int(raw_input("The value of n:"))
print(cos(x,n))
"""

Npoints = 100
dtheta = (2.0*np.pi -0.0)/Npoints
Nterm = 10

theta = []
ctheta = []

for i in range(0, Npoints):
    theta.append(i*dtheta)
    ctheta.append(cos(i*dtheta, Nterm ))

fig = plt.figure()
ax= plt.axes()
ax.plot(theta,ctheta, '.')
ax.plot(theta,np.cos(theta))
ax.grid()
ax.set_xlabel(r"$\theta$", fontsize=14)
plt.show()
#plt.savefig('cos.png')


# -*- coding: utf-8 -*-
"""
This program models the LEGO "Doodler" Google it, and/or see:
    https://www.us.lego.com/en-us/mindstorms/community/robot?projectid=73d4591f-e964-4533-85fe-b608d3eb6a83
Briefly, a pen is at the end of a zig-zag expanding structure,
the left and right end points of the structure are connected to
to gears so they each end point moves in a circle.

Example: shown here is strucutre with n_zigzag = 3
  (three lengths from end point to tip; the Lego project has n_zig = 7);
  the length of each section in the diagram is L_zig = 3 (three "/"s)

       tip where the pen is
       /\
      /  \
    ./    \.
     \    /
      \  /
       \/
       /\
      /  \
     /    \
     L    R
   end points
The end points are attached a distance "radius" from the centers of
their gears, and the two gears centers are separated by 2*x_center distance.
"""

# use math functions from np
import numpy as np
# and plotting from matplotlib
import matplotlib.pyplot as plt

# parameters to set and adjust (see description above)
# ============================
# The mechanical setup:
n_zigzag = 7     # Lego online version has 7
L_zig = 7.0      # Lego design has spacong of 7 holes
x_center = 5.0   # Lego design has x_center = 5
radius = 1.58    # Lego design has radius = 1.58 = sqrt(1.5^2+0.5^2)
# The number of rotations for each gear before stopping,
# and the starting location (in rotations):
rot_l = 49
phi_l = -0.05
rot_r = -25
phi_r = -0.05
# FYI, the Lego page uses (effectively, note sign change too):
#    rot_l,'r = -49 & -50  (green in movie)
#             =  49 & -25  (blue in movie)
#    and Lego phi's look to be atan of 1/3 ~ 0.05 revs.
# ============================

def xys_of_rotations(rot_l, rot_r):
    """
    Calculate the location of the pen tip based on the
    amount of rotation of the two gears.
    """
    # convert rotations to radians (angle)
    theta_l = 2.0 * np.pi * rot_r
    theta_r = 2.0 * np.pi * rot_l
    # locations of the ends of the zig-zag from the angles,
    # note that they rotate in different directions
    # *** simple sin and cos use ***
    xl = -1.0 * (x_center + radius * np.cos(theta_l))
    yl = radius * np.sin(theta_l)
    xr = x_center + radius * np.cos(theta_r)
    yr = radius * np.sin(theta_r)
    # d is the half distance between the end points
    # *** distance formula ***
    d = np.sqrt((xr - xl)**2 + (yr - yl)**2) / 2.0
    # calculate the location of the tip of the zig-zag,
    # start at the midpoint of the two endpoints
    # *** midpoint formula ***
    xtip = (xl + xr) / 2.0
    ytip = (yl + yr) / 2.0
    # h is the extension distance of the zig-zag,
    # the "height" or distance of the tip from the midpoint
    # *** Pythagorean theorem ***
    h = n_zigzag * np.sqrt(L_zig**2 - d**2)
    # this distance is tilted from the vertical
    # by a tilt angle which has trig ratios:
    # *** simple trig, or similar triangles ***
    sintilt = 0.5 * (yr - yl) / d
    costilt = 0.5 * (xr - xl) / d
    # add the x and y components of the tilted height to the midpoint;
    # the x component of h is -h*sin(tilt)
    xtip = xtip - h * sintilt
    # the y component of h is hcos(tilt)\
    ytip = ytip + h * costilt
    # return the locations of the end points and writing tip
    # return (xl, yl, xr, yr, xtip, ytip)
    # or just the writing tip
    return (xtip, ytip)

# Setup a list of points to fill
xs = []
ys = []

# setup an array of times, from 0 to 1
Tmax = 1.0
ts = np.linspace(0.0, Tmax, num=2000)

# go through the times...
for t in ts:
    # evaluate the function for raotations at time t
    (xtip, ytip) = xys_of_rotations(rot_l * t + phi_l,
                                    rot_r * t + phi_r)
    xs.append(xtip)
    ys.append(ytip)

# Close the previous plot
plt.close()

# open a plot
plt.figure(1, [12, 12], frameon=False)

plt.xlabel('x')
plt.ylabel('y')
plt.title('Doodler Output  [ rot_l = ' +
          str(rot_l) + ',  rot_r = ' + str(rot_r) + ' ]')

# set color to match Lego demo movie
if rot_r == -25 and rot_l == 49:
    plt.plot(xs, ys, '-b')
elif rot_r == -50 and rot_l == -49:
    plt.plot(xs, ys, '-g')
# or use red for other custom values
else:
    plt.plot(xs, ys, '-r')

plt.show()


#
# This program is distributed without any warranty and it
# can be freely redistributed for research, classes or private studies,
# since the copyright notices are not removed.
#
# This file contains a function to calculate matriz convolution
#
# Jadson Santos - jadsonjs@gmail.com
#
# http://www.scipy-lectures.org/advanced/image_processing/
#
# to run this exemple install pyhton modules:
#
# python3 -m pip install SciPy
# python3 -m pip install numpy
# python3 -m pip install matplotlib
#

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from scipy import misc
from scipy import ndimage
from scipy import signal

#7x7
A = np.array(  [
               [8, 5, 8, 1, 6, 8, 7],
               [9, 9, 2, 8, 2, 7, 8],
               [2, 9, 4, 9, 7, 3, 2],
               [9, 2, 9, 7, 1, 9, 5],
               [6, 9, 8, 7, 3, 1, 5],
               [1, 9, 9, 7, 1, 4, 6],
               [3, 5, 6, 4, 1, 4, 7] ] )
#3x3
B = np.array([ [3, 2, 2],
               [1, 1, 3],
               [3, 1, 2]
               ])

print("--------------- Full --------------------")
con1 = signal.convolve(A, B, mode='full', method='direct')
print(" ")
print(con1)

con2 = signal.convolve(B, A, mode='full', method='direct')
print(" ")
print(con2)

print("---------------Same-----------------")

con3 = signal.convolve(A, B, mode='same', method='direct')
print(" ")
print(con3)

con4 = signal.convolve(B, A, mode='same', method='direct')
print(" ")
print(con4)

print("----------------My--------------------")

# calculate the convolution of a square matriz with hte mode "same"
def myconv2D( matrix, mask ):
    matrixLen = len(matrix)
    maskLen = len(mask)
    border  = int(maskLen / 2);

    # the result matrix will be a bigger in the full model
    tempMatrix = np.zeros( (matrixLen+(border*2), matrixLen+(border*2)), dtype=int  )
    convMatrix = np.zeros( (matrixLen, matrixLen), dtype=int  )

    # copy data of original matrix to convMatrix
    for i in range( matrixLen ):
        for j in range( matrixLen ):
            tempMatrix[i+1][j+1] = matrix[i][j]

    convMatrixLen = len(convMatrix)

    # the shift of the mask over the convMatrix
    # the size of original matriz
    #maxShift = matrixLen
    shiftX = 0
    shiftY = 0

    for shiftI in range( matrixLen ):
        for shiftJ in range( matrixLen ):

            for i in range( maskLen ):
                for j in range( maskLen ):
                    convMatrix[shiftI][shiftJ] = convMatrix[shiftI][shiftJ] + ( tempMatrix[i+shiftI][j+shiftJ] * mask[i][j])

    print(" ")
    print(convMatrix)

myconv2D(A, B)
myconv2D(B, A)


import numpy as np
called = 0

def z_o_knapsack(W, wt, val, n):
    global called
    called += 1

    if n == -1 or W == 0: # base case, 0 items or 0 weight left in knapsack
        return 0

    if W < wt[n]:
        return z_o_knapsack(W, wt, val, n - 1) # if nth item too heavy, cannot be included

    else:
        return max(z_o_knapsack(W, wt, val, n - 1), # value with nth item excluded
                    val[n] + z_o_knapsack(W - wt[n], wt, val, n - 1) # value with nth item included
                )

def z_o_knapsack_memoized(W, wt, val, n, memo=None):
    global called
    called += 1

    if memo == None:
        memo = np.zeros((n+1, W+1))

    if n == -1 or W == 0: # base case, 0 items or 0 weight left in knapsack
        return 0

    if memo[n, W] != 0:
        return memo[n, W]

    elif W < wt[n]:
        mem_val = z_o_knapsack(W, wt, val, n - 1) if memo[n-1, W] == 0 else memo[n-1, W] # if nth item too heavy, cannot be included
        memo[n,W] = mem_val
        return mem_val

    else:
        mem_val = max(z_o_knapsack(W, wt, val, n - 1) if memo[n-1, W] == 0 else memo[n-1, W] == 0, # value with nth item excluded
                    val[n] + z_o_knapsack(W - wt[n], wt, val, n - 1)
                        if memo[n-1, W - wt[n]] == 0 else memo[n-1, W - wt[n]] # value with nth item included
                )
        memo[n, W] = mem_val
        return mem_val

def z_o_knapsack_btm_up(W, wt, val, n):
    global called
    memo = np.zeros((n + 1, W + 1))

    for i in range(1, n + 1):
        for w in range(1, W + 1):
            called += 1
            if i == 0 or w == 0:
                memo[i, w] = 0

            elif wt[i - 1] > w:
                memo[i, w] = memo[i - 1, w]
            else:
                memo[i, w] = max(memo[i - 1, w],
                    val[i - 1] + memo[i - 1, w - wt[i - 1]]
                    )

    print(memo)
    return memo[n, W]

if __name__ == '__main__':
    val = [1,4,5,7]
    wt = [1,3,4,5]
    W = 7

    #print(z_o_knapsack_memoized(W, wt, val, len(val) - 1))
    #print(z_o_knapsack(W, wt, val, len(val) - 1))
    print(z_o_knapsack_btm_up(W, wt, val, len(val)))
    print(called)


# Dimentionality Reduction

## PCA

### Maximum variance formulation

consider a dataset $\{x_{i}\}$ where $i=1,...,n$ and $x_{i} \in \mathbb{R}^{d}$.

our goal is to project the data onto a space having dimensionality $k < d$ while maximizing the variance of the projected data.

to begin with, consider the projection onto a one-dimensional space$(k=1)$.

we can define the direction of this space by a vector $u_{1} \in \mathbb{R}^{d}$, we can choose $u_{1}$ to be a unit vector so that $u_{1}^{T}u_{1} = 1$.

each data point $x_{i}$ is then projected onto a scalar value $u_{1}^{T}x_{i}$, then mean of the projected data:

$$\frac{1}{n}\sum_{i=1}^{n}u_{1}^{T}x_{i} = u_{1}^{T}\overline{x}$$

the variance of the projected data:

$$\frac{1}{n}\sum_{i=1}^{n}(u_{1}^{T}x_{i} - u_{1}^{T}\overline{x})^{2} = \frac{1}{n}\sum_{i=1}^{n}u_{1}^{T}(x_{i} - \overline{x})(x_{i} - \overline{x})^{T}u_{1} = u_{1}^{T}Su_{1}$$

where

$$S = \frac{1}{n}\sum_{i=1}^{n}(x_{i} - \overline{x})(x_{i} - \overline{x})^{T}$$

now we can formalize our problem as:

$$\underset{u_{1}}{min}\ -u_{1}^{T}Su_{1}$$
$$s.t\quad u_{1}^{T}u_{1} = 1$$

the lagrangian of this optimization problem:

$$L(u_{1}, \lambda_{1}) = -u_{1}^{T}Su_{1} + \lambda_{1}(u_{1}^{T}u_{1} - 1)$$

the primal:

$$\underset{u_{1}}{min}\ \underset{\lambda_{1}}{max}\ L(u_{1}, \lambda_{1})$$

primal satisfy the KKT conditions, so equivalent to dual:

$$\underset{\lambda_{1}}{max}\ \underset{u_{1}}{min}\ L(u_{1}, \lambda_{1})$$

setting the derivative with respect to $u_{1}$ equal to zero, we have:

$$Su_{1} = \lambda_{1}{u_{1}}$$

which say that $u_{1}$ must be a eigenvector of $S$, if we left-multiply by $u_{1}^{T}$ and make use of $u_{1}^{T}u_{1} = 1$, we get:

$$u_{1}^{T}Su_{1} = \lambda_{1}$$

and so the variance will be a maximum when we set $u_{1}$ equal to the eigenvector having the largest eigenvalue $\lambda_{1}$. this eigenvector is known as the first principal component.

we can define the additional principal components in an increamental fashion by choosing each new direction to be that which maximizes the projected variance amongst all possible directions orthogonal to those already considered.

second principal component:

$$\underset{u_{2}}{min}\ -u_{2}^{T}Su_{2}$$
$$s.t\quad u_{2}^{T}u_{2} = 1, u_{1}^{T}u_{2} = 0$$

like before, using lagrangian we derive:

$$Su_{2} = \lambda_{2}{u_{2}} + \phi{u_{1}}$$

left multiply by $u_{1}^{T}$:

$$u_{1}^{T}Su_{2} = \lambda_{2}u_{1}^{T}{u_{2}} + \phi{u_{1}^{T}}{u_{1}}$$

analyzing each component:

$$u_{1}^{T}Su_{2} = u_{2}^{T}Su_{1} = u_{2}^{T}\lambda_{1}u_{1} = \lambda{u_{1}^{T}u_{2}} = 0$$
$$u_{1}^{T}{u_{2}} = 0$$
$${u_{1}^{T}}{u_{1}} = 1$$

we get:

$$\phi = 0$$

back to zero derivative we have:

$$Su_{2} = \lambda_{2}{u_{2}}$$
$$u_{2}^{T}Su_{2} = \lambda_{2}$$

so $\lambda_{2}$ is the second largest eigenvalue of $S$.

by induction, we can show that $i$-th principal component is the $i$-th largest eigenvector of $S$.

### properties of non-negative definite symmetric real matrix

$$S = \frac{1}{n}\sum_{i=1}^{n}(x_{i} - \overline{x})(x_{i} - \overline{x})^{T}$$

is of that kind.

### Minimum-error formulation

a complete orthonormal basis vectors $u_{i}$ in $\mathbb{R}^{d}$:

$$u_{i}^{T}u_{j} = \delta_{ij}$$

$x_{k}$ coordinate with respect to $u_{i}$ is $x_{k}^{T}u_{i}$, so:

$$x_{k} = \sum_{i=1}^{d}(x_{k}^{T}u_{i})u_{i}$$

$x_{k}$ can be approximated by the $m$-dimensional subspace representation plus a constant:

$$\tilde{x}_{k} = \sum_{i=1}^{m}z_{ki}u_{i} + \sum_{i=m+1}^{d}b_{i}u_{i}$$

where $z_{ki}$ depend on the particular data point, whereas ${b_{i}}$ are constants that are the same for all data points.

our goal is to minimize:

$$J = \frac{1}{n}\sum_{k=1}^{d}\left \| x_{k} - \tilde{x}_{k} \right \|^{2} $$

setting the derivative with respect to $z_{ni}$ to zero, and making use of the orthonormality conditions, we obtain:

$$z_{ni} = x_{n}^{T}u_{i}$$

similarly, we obtain:

$$b_{i} = \overline{x}^{T}u_{i}$$

substitude for $z_{ni}$ and $b_{i}$, we obtain:

$$x_{k} - \tilde{x}_{k} = \sum_{i=m+1}^{d}((x_{k} - \overline{x}_{k})^{T}u_{i})u_{i}$$

finally our goal is to minimize:

$$J = \frac{1}{n}\sum_{k=1}^{n}\sum_{i=m+1}^{d}(x_{k} - \overline{x}_{k})^{2} = \sum_{i=m+1}^{d}u_{i}^{T}Su_{i}$$

this is similar to the maximum variance formulation in the opposite direction.

### manual data

"""construct dataset"""
import numpy as np

m = 100
w1, w2 = 0.1, 0.3
noise = 0.2

X = np.empty((m, 3))
X[:, :2] = np.random.multivariate_normal([0, 0], [[2, 1], [1, 5]], m)
X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)

from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X2D = pca.fit_transform(X)

X2D[: 5]

"""sklearn actually uses SVD"""
X_centered = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(X_centered)

X2D_SVD = X_centered.dot(Vt.T[:, :2])
X2D_SVD[: 5]

### mnist data

from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split

mnist = fetch_openml('mnist_784', version=1, as_frame=False)
mnist.target = mnist.target.astype(np.uint8)

X = mnist["data"]
y = mnist["target"]

X_train, X_test, y_train, y_test = train_test_split(X, y)

"""
set n_components (0.0, 1.0), indicating the ratio of variance you wish to preserve
use inverse_transform trying to inverse
"""
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)
X_mnist = pca.inverse_transform(X_reduced)

pca.explained_variance_ratio_[: 10]

"""IncrementalPCA"""
from sklearn.decomposition import IncrementalPCA

n_batches = 100
inc_pca = IncrementalPCA(n_components=154)
for X_batch in np.array_split(X_train, n_batches):
    print(".", end="")
    inc_pca.partial_fit(X_batch)

X_reduced = inc_pca.transform(X_train)

### swiss roll

from sklearn.datasets import make_swiss_roll

X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)

"""kernel pca"""
from sklearn.decomposition import KernelPCA

rbf_pca = KernelPCA(n_components=2, kernel="rbf", gamma=0.04)
X_reduced = rbf_pca.fit_transform(X)

## Locally Linear Embedding(LLE)

LLE works by first measuring how each training instance linearly relates to it's colsest neighbors (c.n)

then looking for a low-dimensional representation of the training set where these local relationships are best preserved.

LLE step one: linearly modeling local relationships:

$$
\begin{equation}
\begin{split}
\hat{W} &= \underset{W}{argmin}\sum_{i=1}^{m}\left(x^{(i)} - \sum_{j=1}^{m}w_{ij}x^{(j)}\right)^{2}\\
\mbox{s.t }\ &1.w_{ij} = 0 \mbox{ if } x^{(j)} \mbox{ is not one of c.n of } x^{(i)} \\
&2.\sum_{i=1}^{m}w_{ij} = 1 \mbox{ for all }j
\end{split}
\end{equation}
$$

LLE second step doing the reverse: keeping the weights fixed and finding the optimal position of the instances' image in low-dimensional space, suppose $x^{(i)}$'s low-dimensional image is $z^{(i)}$.

$$
\begin{equation}
\begin{split}
\hat{Z} &= \underset{Z}{argmin}\sum_{i=1}^{m}\left(z^{(i)} - \sum_{j=1}^{m}w_{ij}z^{(j)}\right)^{2}\\
\mbox{s.t }\ &1.\sum_{i=1}^{m}z^{(i)} = 0\\
&2.\sum_{i=1}^{m}(z^{(i)}z^{(i)})^{T} = mI_{d}
\end{split}
\end{equation}
$$

from sklearn.manifold import LocallyLinearEmbedding

lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10)
X_reduced = lle.fit_transform(X)

## Exercise

from sklearn.ensemble import RandomForestClassifier
from sklearn.base import clone

rnd_clf = RandomForestClassifier(n_estimators=1000, max_depth=4, n_jobs=-1, random_state=42)
low_clf = clone(rnd_clf)

from datetime import datetime
from sklearn.metrics import accuracy_score

pre_time = datetime.now()
rnd_clf.fit(X_train, y_train)
print((datetime.now() - pre_time).seconds)
accuracy_score(y_test, rnd_clf.predict(X_test))

pca = PCA(n_components=0.95)
X_train_reduced = pca.fit_transform(X_train)
X_test_reduced = pca.transform(X_test)

pre_time = datetime.now()
low_clf.fit(X_train_reduced, y_train)
print((datetime.now() - pre_time).seconds)
accuracy_score(y_test, low_clf.predict(X_test_reduced))

np.random.seed(42)

m = 10000
idx = np.random.permutation(60000)[:m]

X = mnist['data'][idx]
y = mnist['target'][idx]

from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, random_state=42)
X_reduced = tsne.fit_transform(X)

import matplotlib.pyplot as plt

plt.figure(figsize=(13,10))
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=y, cmap="jet")
plt.axis('off')
plt.colorbar()
plt.show()


import numpy as np
import matplotlib.pyplot as plt

"""Rienmann Sum"""

def lowersum(f, a, b, n):
    x = symbols('x')
    area = 0
    h = (b - a) / n
    t = a
    for i in range(n):
        l = min(f.subs(x, t), f.subs(x, t + h))
        area += l * h
        t = t + h
    return area

def uppersum(f, a, b, n):
    x = symbols('x')
    area = 0
    h = (b - a) / n
    t = a
    for i in range(n):
        l = max(f.subs(x, t), f.subs(x, t + h))
        area += l * h
        t = t + h
    return area

def Rienmannsum(f, a, b):
    e = 1
    n = int(input("Initial number of divisions you want: "))
    while e > 10 ** -4:
        L = lowersum(f, a, b, n)
        U = uppersum(f, a, b, n)
        e = U - L
        n *= 10
    return (U + L) / 2

'''Simpsons Method of numerical integration'''

def simps(f,a,b,N=50):
    '''
    f : function
        Vectorized function of a single variable
    a , b : numbers
        Interval of integration [a,b]
    N : (even) integer
        Number of subintervals of [a,b]
    '''
    if N % 2 == 1:
        raise ValueError("N must be an even integer.")
    dx = (b-a)/N
    x = np.linspace(a,b,N+1)
    y = f(x)
    S = dx/3 * np.sum(y[0:-1:2] + 4*y[1::2] + y[2::2])
    return S

'''Trapezoid Rule for numerical integration'''

def trapz(f,a,b,N=50):
    '''
    f : function
        Vectorized function of a single variable
    a , b : numbers
        Interval of integration [a,b]
    N : integer
        Number of subintervals of [a,b]
    '''
    x = np.linspace(a,b,N+1)
    y = f(x)
    y_right = y[1:] # Right endpoints
    y_left = y[:-1] # Left endpoints
    dx = (b - a)/N
    T = (dx/2) * np.sum(y_right + y_left)
    return T


import numpy as np
#from matplotlib import pyplot as plt

h_0 = 5.32 # height of pivot point

x_0 = 7.744 #x position of servo 1
y_0 = 7.744 #y posiiton of servo 2

l_1 = 2 # length of servo arm
l_2 = 3.398 # length of linkage

h_1_0 = 2.26 # height of servo axis
h_2_0 = 2.26

# compute maximum tilt
h_1 = h_1_0 + l_1 + l_2
dh = h_1 - h_0
p_max = np.arctan(dh/x_0)

h_1 = h_1_0 - l_1 + l_2
dh = h_1 - h_0
p_min = np.arctan(dh/x_0)

def tilt2servo(phi, rad=True):
    if not rad:
        phi = np.deg2rad(phi)

    if phi > p_max:
        phi = p_max
    elif phi < p_min:
        phi = p_min

    k = h_0 + x_0 * np.tan(phi) - h_1_0
    t = np.arcsin((l_1**2-l_2**2+k**2)/(2*k*l_1))

    if not rad:
        t = np.rad2deg(t)
    return t

if __name__ == "__main__":
    print tilt2servo(-15.0, rad=False)

    #print np.rad2deg(p_min) # max theoretical tilt
    #print np.rad2deg(p_max) # max theoretical tilt

    #p = np.linspace(-p_min,p_max) # desired tilt angle
    #t = tilt2servo(p) # desired servo angle
    #plt.plot(p,t)
    #plt.show()


import scipy.stats

def get_z_critical(conf_level: float) -> float:
    """Gets z critical , which is z value corresponding to a specific
    confidence level (for a two sided test)

    Args:
        conf_level (float): confidence level

    Returns:
        float: z critical value
    """
    return scipy.stats.norm.ppf(1-(1-conf_level)/2)

def get_t_critical(conf_level: float, df: int) -> float:
    """Gets t critical , which is t value corresponding to a specific
    confidence level (for a two sided test)

    Args:
        conf_level (float): confidence level
        df (int): degrees of freedom

    Returns:
        float: t critical value
    """
    return scipy.stats.t.ppf(1-(1-conf_level)/2, df)

def get_t_pvalue(t_value: float,
                 df: int,
                 alternative: str = "two-sided") -> float:
    """Get corresponding p value for t value depending on test type

    Args:
        t_value (float): t value
        df (int): degrees of freedom
        alternative (str, optional): two-sided/larger/smaller.
                                     Defaults to "two-sided".

    Raises:
        ValueError: when the test type is invalid

    Returns:
        float: p value
    """

    if alternative == "two-sided":
        p_value = scipy.stats.t.sf(abs(t_value), df=df)*2
    elif alternative == "larger":
        p_value = scipy.stats.t.sf(t_value, df=df)
    elif alternative == "smaller":
        p_value = scipy.stats.t.cdf(t_value, df=df)
    else:
        raise ValueError("invalid alternative")
    return p_value

def get_norm_pvalue(z_value: float,
                    alternative: str = "two-sided") -> float:
    """Get corresponding p value for z value depending on test type

    Args:
        z_value (float): z value
        alternative (str, optional): two-sided/larger/smaller.
                                     Defaults to "two-sided".

    Raises:
        ValueError: when the test type is invalid

    Returns:
        float: p value
    """

    if alternative == "two-sided":
        p_value = scipy.stats.norm.sf(abs(z_value))*2
    elif alternative == "larger":
        p_value = scipy.stats.norm.sf(z_value)
    elif alternative == "smaller":
        p_value = scipy.stats.norm.cdf(z_value)
    else:
        raise ValueError("invalid alternative")
    return p_value

def get_f_pvalue(f_value: float, dfg: int, dfe: int):
    return scipy.stats.f.sf(f_value, dfg, dfe)


import numpy as np
import matplotlib.pyplot as plt
from plot_utils import plot_with_residual

# this is based on https://www.youtube.com/watch?v=sDv4f4s2SB8

# data set
Weight = np.array([0.5 , 2.3 , 2.9]) # x
Height = np.array([1.4 , 1.9 , 3.2]) # y , _observed_height

# Predicted Height = intercept + slope * Weight --> h
def predicted_height(intercept , slope):
    return intercept + slope * Weight

def plot_dataset():
    plt.plot(Weight, Height,'o')
    plt.grid()
    plt.title('data set')
    plt.xlabel("Weight")
    plt.ylabel("Height")
    plt.show()

# _predicted_height is h , _observed_height is y
def sum_of_square_residual(_predicted_height , _observed_height):
    residual = _observed_height - _predicted_height
    return np.dot(residual,residual) # sum the square

def show_ssr_graph_per_intercept(slope):
    vec_intercept = np.arange(0,2,0.1)
    vec_ssr = []
    for intercept in vec_intercept:
        _predicted_height = predicted_height(intercept,slope)
        _observed_height = Height
        ssr = sum_of_square_residual(_predicted_height,_observed_height)
        vec_ssr.append(ssr)

    index_min_ssr = np.argmin(vec_ssr)
    ssr_min = vec_ssr[index_min_ssr]
    intercept_min = vec_intercept[index_min_ssr]
    plt.plot(vec_intercept,vec_ssr,'o',intercept_min,ssr_min,'ro')
    plt.title('cost ssr vs intercept for slope = {}'.format(slope))
    plt.xlabel("intercept")
    plt.ylabel("ssr - sum of squre residual")
    plt.grid()
    plt.show()

def compute_one_ssr(slope):
    intercept = 0 # initial guess
    _predicted_height = predicted_height(intercept,slope)
    _observed_height = Height
    ssr = sum_of_square_residual(_predicted_height,_observed_height)
    print('ssr : ' , ssr)

# ssr is the sum over (y[i])-h[i]))^2
# d_ssr / d_intercept is equal by the chain rule to (d_ssr / d_h) * (d_h / d_intercept)
# d_ssr / d_h -> sum over 2(y[i])-h[i]))*(-1)
# d_h / d_intercept = d(intercept + slope * weight) / d_intercept --> 1
# so  d_ssr / d_intercept is equal to the sum over 2(y[i])-h[i]))*(-1)  but h = intercept + slope * weight so
# d_ssr / d_intercept is equal to the sum over 2 ( y[i] - (intercept + slope * weight[i]))*(-1) or equivalently
# d_ssr / d_intercept is equal to the sum over 2 ( observed_height[i] - (intercept + slope * weight[i]))(-1)
def d_ssr_to_d_intercept(slope,intercept):
    _observed_height = Height
    _predicted_height = intercept + slope * Weight
    error = _observed_height - _predicted_height
    return 2 * np.sum(error) *(-1)

# ssr is the sum over (y[i])-h[i]))^2
# d_ssr / d_slope is equal by the chain rule to (d_ssr / d_h) * (d_h / d_slope)
# d_ssr / d_h -> sum over 2(y[i])-h[i]))*(-1)
# d_h / d_slope = d(intercept + slope * weight) / d_slope --> weight
# so  d_ssr / d_intercept is equal to the sum over 2(y[i])-h[i]))*(-1)*weight[i]  but h = intercept + slope * weight so
# d_ssr / d_intercept is equal to the sum over 2 ( y[i] - (intercept + slope * weight[i]))*(-1)*weight[i] or equivalently
# d_ssr / d_intercept is equal to the sum over 2 ( observed_height[i] - (intercept + slope * weight[i]))(-1)*weight[i]
def d_ssr_to_d_slope(slope,intercept):
     _observed_height = Height
     _predicted_height = intercept + slope * Weight
     error = _observed_height - _predicted_height
     # np.dot : mutliply element by element and than sum
     return 2 * np.dot(error , Weight) *(-1)

def print_d_ssr_to_d_intercept(slope):
    # derivative become smaller
    print("d_ssr/d_intercept @ intercept = 0 : ",d_ssr_to_d_intercept(slope,0))
    print("d_ssr/d_intercept @ intercept = 0.5 : ",d_ssr_to_d_intercept(slope,0.5))
    print("d_ssr/d_intercept @ intercept = 0.8 : ",d_ssr_to_d_intercept(slope,0.8))
    print("d_ssr/d_intercept @ intercept = 0.9 : ",d_ssr_to_d_intercept(slope,0.9))
    print("d_ssr/d_intercept @ intercept = 0.95 : ",d_ssr_to_d_intercept(slope,0.95))
    print("d_ssr/d_intercept @ intercept = 1.05 : ",d_ssr_to_d_intercept(slope,1.05))

def gradient_descent_constant_slope(slope):
    intercept = 0
    learning_rate = 0.1
    step_size = 1 # just a value to enter the loop
    min_step_size = 0.001
    iteration = 0
    ssr_vec = []
    intercept_vec = []

    while abs(step_size) > min_step_size:
        # ------------ this is the gradient descent engine
        step_size = d_ssr_to_d_intercept(slope,intercept) * learning_rate
        intercept = intercept - step_size # i did not see a proof for this

        # ------------ below this is relevant to plot
        intercept_vec.append(intercept)
        _predicted_height = predicted_height(intercept,slope)
        _observed_height = Height
        iteration += 1
        ssr = sum_of_square_residual(_predicted_height , _observed_height)
        ssr_vec.append(ssr)

        plot1(intercept_vec,ssr_vec,intercept,slope,ssr , iteration,step_size)

def plot1(intercept_vec,ssr_vec,intercept,slope,ssr , iteration,step_size):
    fig, axs = plt.subplots(2)
    fig.suptitle('Part 1')
    plot_with_residual(axs[0],Weight,Height,intercept,slope)
    axs[0].grid()
    axs[0].set_title('data set vs intercpt  + slope * weight and residual\nssr : {:.2f} , intercept : {:.2f} , iteration : {} , step : {:.4f}'.format(ssr , intercept , iteration,step_size))
    axs[0].set_xlabel("Weight")
    axs[0].set_ylabel("Height")
    axs[1].plot(intercept_vec,ssr_vec,'o',intercept,ssr,'ro')
    axs[1].set_title('gradient descent convergence , learn intercept . step size become smaller')
    axs[1].set_xlabel("intercept")
    axs[1].set_ylabel("cost function - ssr")
    axs[1].grid()
    plt.tight_layout()
    plt.show()

def gradient_descent():
    intercept = 0
    slope = 1
    learning_rate = 0.01 # using 0.1 will not due
    step_size = 1 # just a value to enter the loop
    min_step_size = 0.001
    iteration = 0
    ssr_vec = []

    while step_size > min_step_size:
        # ------------ this is the gradient descent engine
        step_size_intercept = d_ssr_to_d_intercept(slope,intercept) * learning_rate
        step_size_slope = d_ssr_to_d_slope(slope,intercept) * learning_rate
        intercept = intercept - step_size_intercept # i did not see a proof for this
        slope = slope - step_size_slope
        step_size = max(abs(step_size_intercept) , abs(step_size_slope))

        # ------------ below this is relevant to plot
        _predicted_height = predicted_height(intercept,slope)
        _observed_height = Height
        iteration += 1
        ssr = sum_of_square_residual(_predicted_height , _observed_height)
        ssr_vec.append(ssr)
        print("intercept : {} , slope : {} , step_size : {} , iteration : {}".format(intercept,slope,step_size,iteration))

    plot2(intercept,slope,step_size,iteration,ssr,ssr_vec)

def plot2(intercept,slope,step_size,iteration,ssr,ssr_vec):
    fig, axs = plt.subplots(2)
    plot_with_residual(axs[0],Weight,Height,intercept,slope)
    axs[0].grid()
    fig.suptitle('Part 2')
    axs[0].set_title('data set vs intercpt  + slope * weight and residual\nssr : {:.2f} , intercept : {:.2f} ,slope : {:.2f} , iteration : {} , step : {:.4f}'.format(ssr , intercept,slope , iteration,step_size))
    axs[0].set_xlabel("Weight")
    axs[0].set_ylabel("Height")
    axs[1].plot(ssr_vec)
    axs[1].set_title('gradient descent convergence , learn intercept and slope')
    axs[1].set_xlabel('iteration')
    axs[1].set_ylabel('cost function - ssr')
    axs[1].grid()
    plt.tight_layout()
    plt.show()

# part 1 assume slope is 0.64 , compute intercept
def part1_learn_slope_is_constant():
    slope = 0.64
    compute_one_ssr(slope)
    show_ssr_graph_per_intercept(slope)
    print_d_ssr_to_d_intercept(slope)
    gradient_descent_constant_slope(0.64)

def part2_learn():
    gradient_descent()

# main
plot_dataset()
part1_learn_slope_is_constant()
part2_learn()


import numpy as np

def discount_rate(rate, periods):
    return 1.0 / np.power(1.0 + rate, periods)

def yield_rate(discount, periods):
    return np.power(1.0 / discount, 1.0 / periods) - 1.0

def interpolate_rate(**kwargs) -> float:
    first_period: int = kwargs.get('first_period')
    next_period: int = kwargs.get('next_period')
    extrapolate_period: int = kwargs.get('extrapolate_period')
    first_rate: float = kwargs.get('first_rate')
    next_rate: float = kwargs.get('next_rate')

    assert next_period > extrapolate_period > first_period

    if first_rate == next_rate:
        return first_rate

    first_discount = discount_rate(first_rate, first_period)
    next_discount = discount_rate(next_rate, next_period)
    assert 0.0 < next_discount < first_discount <= 1.0

    second_discount = next_discount / first_discount
    second_periods = next_period - first_period

    extrapolated_discount = first_discount * np.power(second_discount, (extrapolate_period - first_period) / second_periods)
    yr = yield_rate(extrapolated_discount, extrapolate_period)

    assert min(first_rate, next_rate) <= yr <= max(first_rate, next_rate)
    return yr


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Nov  3 21:06:23 2018

@author: Thiago Almeida
"""

import numpy as np
from metodos_numericos.LU import LU
from metodos_numericos.Gauss import Gauss
from Utils import Utils

class MinimosQuadrados():

    #constroi o polinomio para interpolacao
    def executar(self, x, y, n):

        n_col = len(x)
        n_linhas = n+1
        tam2 = n+1

        #ja preenche a primeira linha com 1
        M1 = np.ones((n_linhas,n_col), dtype=np.float128)

        for k in range (1,n_linhas):
            for j in range (0,n_col):
                M1[k][j] = x[j]**k

        #print("M1", M1)

        A = np.zeros((tam2,tam2), dtype=np.float128)
        B = np.zeros((tam2,), dtype=np.float128)

        #cria matriz
        for i in range (0,tam2):
            for j in range (0,tam2):
                a = np.array(M1[i], dtype=np.float128, copy=True)
                b = np.array(M1[j], dtype=np.float128, copy=True)

                A[i][j] = np.dot(a, b)

        #cria vetor fonte
        for i in range (0,tam2):
            b = np.array(M1[i], dtype=np.float128, copy=True)
            B[i] = np.dot(y, b)

        print("A", A)
        print("B", B)

        #Utils().obtemInfoMatriz(A)

        #calcula coeficientes
        X = LU().executar(A, B)[0]
        #X = Gauss().executarComPivoteamento(A, B)[0]
        #X = Gauss().executar(A, B)[0]
        return X

    def interpolaCoeficientes(self, c, n, xk):

        soma = 0
        for i in range (0,n+1):
            soma += c[i] * (xk ** i)

        #if(np.isnan(soma)):
            #print("resultado da interpolacao do valor "+repr(xk)+" foi igual a NaN")
            #soma = 0

        return soma

    def calculaResiduo(self, y, x, n, c):

        tam = len(y)

        soma1 = 0
        soma2 = 0
        soma3 = 0
        for k in range (0, tam):
            soma1 += (y[k] - self.interpolaCoeficientes(c, n, x[k]))**2
            soma2 += y[k]**2
            soma3 += y[k]

        r2 = 1 - (soma1 / (soma2 - (1/tam)*soma3**2))

        if(r2 < 0):
            print("N="+repr(n)+", r^2 = " + repr(r2) + " e nao tem raiz real")
            return 0
        else:
            return np.sqrt(r2)


#countingWithNestedForLoops.py
import numpy as np

#Example 1
# recall how to count using a for loop:
max_count = 100
for i in range(100):
    print(i)

#Example 2a
# Counting with nested for loops offers an opportunity to think about
# 1. The structure of a number system of different bases
# 2. The structure of multidimensional arrays

# consider counting to 100 in base 10:

count_list = []
base10_count_list = []

# try changing the base to see how the count changes
# the program will count to base ** 2 - 1
# provides interpretable results work for all bases up to 10
base = 3
for i in range(base):
    for j in range(base):
        val = int(str(i) + str(j))
        count_list.append(val)
        #produce equivalent list in base10
        base10_val = i * base + j
        base10_count_list.append(base10_val)
print("\ncount in base", base)
print(count_list)
print("\ncount in base 10:")
print(base10_count_list)

#Example 2b
#imagine that you wanted to count to base ** 3 - 1
count_list = []
base10_count_list = []
# try changing the base to see how the count changes
# the program will count to base ** 3 - 1
# provides interpretable results work for all bases up to 10
for i in range(base):
    for j in range(base):
        for k in range(base):
            val = int(str(i) + str(j) + str(k))
            count_list.append(val)
            #produce equivalent list in base10
            base10_val = i * base ** 2 + j * base + k
            base10_count_list.append(base10_val)

print("\ncount in base", base)
print(count_list)
print("\ncount in base 10:")
print(base10_count_list)

#Example 3
# We can use the same logic to fill an n X n array with values:
n = base
array = np.zeros((n,n))
for i in range(n):
    for j in range(n):
        array[i][j] = i * n + j
print(array)

In Class Projects/In Class Examples Fall 2019/Section 5/countingWithNestedForLoops.py


import numpy as np

def random_graph(node_count, prob):
    """
    Create a random adjacency matrix of a graph

    Parameters
    ----------
    node_count: int
        Number of nodes in the graph
    prob: float
        Probability of presence an edge between two nodes

    Returns
    -------
    adj_mat: numpy.ndarray
        A numpy array of size (node_count, node_count) with elements in {0, 1}
    """
    return np.random.binomial(1, prob, size=(node_count, node_count)).astype(
        np.float64)

def random_choice(arr):
    """
    Based on the suggestion by Radim Rehurek in this tweet:
    https://twitter.com/RadimRehurek/status/928671225861296128

    Results of comparison between this implementation and
    numpy.random.choice:
    ```
    >>>> lst = range(100000)
    >>>> timeit lst[np.searchsorted(uniform.cumsum(), np.random.random())]
    213 µs ± 1.32 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

    >>>> timeit np.random.choice(lst)
    8.2 ms ± 70.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    ```

    Parameters
    ----------
    arr: list
        A 1-D list or array from which to select an element randomly

    Returns
    -------
    One element selected randomly from arr, with uniform probability
    """
    assert type(arr) is list or type(arr) is np.ndarray, 'List not provided!'
    uniform = np.ones((len(arr), )) / len(arr)
    return arr[np.searchsorted(uniform.cumsum(), np.random.random())]

def adj_mat_to_list(adj_mat):
    """
    Converts an adjacency matrix to an adjacency list.

    Parameters
    ----------
    adj_mat: numpy.ndarray
        Square adjacency matrix of a graph

    Returns
    -------
    adj_list: list
        The adjacency list of the given matrix
    """
    assert adj_mat.ndim == 2, 'Adjacency matrix should be of rank 2.'
    assert adj_mat.shape[0] == adj_mat.shape[1], 'Adjacency matrix' \
        ' should be square.'
    assert np.all(adj_mat >= 0), 'All elements of the adjaceny matrix ' \
        'should be nonnegative.'
    adj_list = []
    for i in range(adj_mat.shape[0]):
        adj_list.append([])
        for j in range(adj_mat.shape[0]):
            if adj_mat[i, j] > 0:
                adj_list[-1].append(j)

    return adj_list

def adj_list_to_mat(adj_list):
    """
    Converts an adjacency list to an adjacency matrix.

    Parameters
    ----------
    adj_list: list
        Adjacency list of a graph

    Returns
    -------
    adj_mat: numpy.ndarray
        Square adjacency matrix of a graph
    """
    assert type(adj_list) == list, 'Adjacency list should be provided'

    adj_mat = np.zeros((len(adj_list), len(adj_list)), dtype=np.float64)
    for i in range(len(adj_list)):
        for j in adj_list[i]:
            adj_mat[i, j] = 1.0

    return adj_mat