"""
Implementation of Principal component analysis (PCA)
https://en.wikipedia.org/wiki/Principal_component_analysis
"""

import numpy as np
from svd import svd

class PCA(object):
    """Principal component analysis (PCA)

    Linear dimensionality reduction using Singular Value Decomposition of the
    data to project it to a lower dimensional space.

    """
    def __init__(self, n_components=None, random_state=None, epsilon=1e-10):
        self.n_components = n_components
        self.random_state = random_state
        self.epsilon = epsilon
        self.U, self.S, self.VT = None, None, None
        self.X_mean = None
        self.explained_variance_ = None
        self.explained_variance_ratio_ = None

    def fit(self, X):
        """Fit the model to the data X.

        Parameters:
        X: original matrix for fit

        """
        if self.n_components is None:
            # set maximum number of components
            self.n_components = X.shape[1]
        # calculate mean vector
        self.X_mean = X.mean(0)
        # extract mean vector from matrix
        X_centered = X - self.X_mean
        # performe SVD
        self.U, self.S, self.VT = svd(
            X_centered, epsilon=self.epsilon, random_state=self.random_state)
        n_samples = X.shape[0]
        # calculate explained variance
        self.explained_variance_ = (self.S ** 2) / (n_samples - 1)
        self.explained_variance_ = self.explained_variance_[:self.n_components]
        # calculate explained variance ratio
        total_var = self.explained_variance_.sum()
        self.explained_variance_ratio_ = self.explained_variance_ / total_var
        self.explained_variance_ratio_ = self.explained_variance_ratio_[:self.n_components]

    def transform(self, X):
        """Apply dimensionality reduction on X.

        Parameters:
        X : matrix for projection

        Returns:

        X_new: projected matrix
        """
        X_centered = X - self.X_mean
        X_transform = np.dot(X_centered, self.VT[:self.n_components].T)
        return X_transform

    def inverse_transform(self, X):
        """Transform data back to its original space.

        Parameters:
        X : projected matrix

        Returns:
        X_original : Original matrix

        """
        X_original = np.dot(X, self.VT[:self.n_components]) + self.X_mean
        return X_original

if __name__ == "__main__":

    from sklearn.decomposition import PCA as PCA_

    data = np.array([
        [2, 5, 3],
        [1, 2, 1],
        [4, 1, 1],
        [3, 5, 2],
        [5, 3, 1],
        [4, 5, 5],
        [2, 4, 2],
        [2, 2, 5],
    ], dtype='int64')

    n_components = 2
    pca = PCA(n_components=n_components, random_state=37)
    pca.fit(data)

    pca_ = PCA_(n_components=n_components, random_state=37)
    pca_.fit(data)

    check_transform = (np.round(
        pca.transform(data) - pca_.transform(data), decimals=3) == 0).all()

    X_transform = pca.transform(data)
    check_inverse = (np.round(
        pca.inverse_transform(X_transform) - pca_.inverse_transform(X_transform),
        decimals=3) == 0).all()

    print('data:\n{}'.format(data))
    print('Data transformed:\n{}'.format(X_transform))
    print('Is transform correct: {}'.format(check_transform))
    print('Inverse transformed data:\n{}'.format(pca.inverse_transform(X_transform)))
    print('Is inverse transform correct: {}'.format(check_inverse))


import numpy as np
import numexpr as ne
from scipy.linalg.blas import dgemm, sgemm

def polynomial_kernel_matrix(P, Q, c, degree):
    """
    Calculate kernel matrix using polynomial kernel.

    k(p,q) = (p^{T}q + c)^d

    Parameters:
    -----------
    P : `numpy.ndarray`
        (nDataP, nDim) matrix of data. Each row corresponds to a data point.
    Q : `numpy.ndarray`
        (nDataQ, nDim) matrix of data. Each row corresponds to a data point.
    c : `float`
        Bias term, c >= 0.
    degree : `int`
        Degree of the polynomial kernel function.

    Returns:
    --------
    K : `numpy.ndarray`
        (nDataP,nDataQ) matrix, the polynomial kernel matrix of the P and Q data matrix.
    """
    return ne.evaluate('(c + A)**d', {
        'A' : dgemm(alpha=1.0, a=P, b=Q, trans_b=True),
        'c' : c,
        'd' : degree
    })

def gaussian_kernel_matrix(P, Q, c):
    """
    Calculate kernel matrix using gaussian kernel.

    ||p-q||^2 = ||p||^2 + ||q||^2 - 2 * p^T * q

    k(p,q) = exp(-c*||p-q||^2)
           = exp(-c*[||p||^2 + ||q||^2 - 2 * p^T * q])

    C: dgemm(alpha=1.0, a=P, b=Q, trans_b=True)  double precision
    C: sgemm(alpha=1.0, a=P, b=Q, trans_b=True)  single precision
    C: np.dot(P,Q.T)                             single precision
            P = P.astype(np.float32)
            Q = Q.astype(np.float32)

    Parameters:
    -----------
    P : `numpy.ndarray`
        (nDataP, nDim) matrix of data. Each row corresponds to a data point.
    Q : `numpy.ndarray`
        (nDataQ, nDim) matrix of data. Each row corresponds to a data point.
    c : `int`
        Width of the gaussian kernel function.

    Returns:
    --------
    K : `numpy.ndarray`
        (nDataP,nDataQ) matrix, the gaussian kernel matrix of the P and Q data matrix.
    """
    # Calculate norm
    P_norm = np.einsum('ij,ij->i',P,P)
    Q_norm = np.einsum('ij,ij->i',Q,Q)
    return ne.evaluate('exp(-gamma * (A + B - 2*C))', {
        'A' : P_norm[:,None],
        'B' : Q_norm[None,:],
        'C' : dgemm(alpha=1.0, a=P, b=Q, trans_b=True),
        'gamma' : c
    })


# --------------
# Code starts here

import numpy as np

# Code starts here

# Adjacency matrix
adj_mat = np.array([[0,0,0,0,0,0,1/3,0],
                   [1/2,0,1/2,1/3,0,0,0,0],
                   [1/2,0,0,0,0,0,0,0],
                   [0,1,0,0,0,0,0,0],
                  [0,0,1/2,1/3,0,0,1/3,0],
                   [0,0,0,1/3,1/3,0,0,1/2],
                   [0,0,0,0,1/3,0,0,1/2],
                   [0,0,0,0,1/3,1,1/3,0]])

# Compute eigenvalues and eigencevectrs
eigenvalues, eigenvectors = np.linalg.eig(adj_mat)
print('Eigen values are', eigenvalues)
print('*'*25)
#print(eigenvectors)
print('Eigen Vector are', abs(eigenvectors[:,0]))
print('*'*25)
eigen_1 = abs(eigenvectors[:,0]) / np.linalg.norm(eigenvectors[:,0],1)
print('Normalize eigen vector are', eigen_1)
print('*'*50)
page =  np.where(np.max(eigen_1) == eigen_1)[0][0] + 1
print('The most important page is', page)

# Eigen vector corresponding to 1

# most important page

# Code ends here

# --------------
# Code starts here

# Initialize stationary vector I
# Code starts here

# Initialize stationary vector I
init_I = np.array([1,0,0,0,0,0,0,0])

# Perform iterations for power method
for i in range(10):
    init_I = np.dot(adj_mat, init_I)
    init_I /= np.linalg.norm(init_I, 1)

print(init_I)
power_page = np.where(np.max(init_I) == init_I)[0][0] + 1
print(power_page)

# Code ends here

print('*'*100)
print('*'*100)
print('*'*100)
print('*'*100)
print('*'*100)

# Code ends here

# --------------
# Code starts here

# New Adjancency matrix
# New Adjancency matrix
new_adj_mat = np.array([[0,0,0,0,0,0,0,0],
                   [1/2,0,1/2,1/3,0,0,0,0],
                  [1/2,0,0,0,0,0,0,0],
                   [0,1,0,0,0,0,0,0],
                   [0,0,1/2,1/3,0,0,1/2,0],
                   [0,0,0,1/3,1/3,0,0,1/2],
                   [0,0,0,0,1/3,0,0,1/2],
                   [0,0,0,0,1/3,1,1/2,0]])

# Initialize stationary vector I
new_init_I = np.array([1,0,0,0,0,0,0,0])
#print(new_init_I)

# Perform iterations for power method
for  i in range (10):
     new_init_I = np.dot(new_adj_mat, new_init_I)
     new_init_I /= np.linalg.norm(new_init_I, 1)
print(new_init_I)

# Code ends here

# --------------
# Alpha value
alpha = 0.85

# Code starts here

# Modified adjancency matrix
G = (alpha*new_adj_mat) + (1-alpha)*(1/len(new_adj_mat))*np.ones(new_adj_mat.shape)
print(G)

# Initialize stationary vector I
final_init_I = np.array([1,0,0,0,0,0,0,0])

# Perform iterations for power method
for i in range (1000):
    final_init_I = np.dot(new_adj_mat, final_init_I)
    final_init_I /= np.linalg.norm(final_init_I, 1)
print(final_init_I)
# Code ends here


'''
grad_descent.py

Use gradient descent to find the minimum value of a
single variable function. This also checks for the existence
of a solution for the equation, f'(x)=0 and plots the intermediate
points traversed.
'''

from sympy import Derivative, Symbol, sympify, solve
import matplotlib.pyplot as plt

def grad_descent(x0, f1x, x):
    # check if f1x=0 has a solution
    if not solve(f1x):
        print('Cannot continue, solution for {0}=0 does not exist'.format(f1x))
        return None
    epsilon =  1e-6
    step_size = 1e-4
    x_old = x0
    x_new = x_old - step_size*f1x.subs({x:x_old}).evalf()

    # list to store the X values traversed
    X_traversed = []
    while abs(x_old - x_new) > epsilon:
        X_traversed.append(x_new)
        x_old = x_new
        x_new = x_old-step_size*f1x.subs({x:x_old}).evalf()

    return x_new, X_traversed

def frange(start, final, interval):

    numbers = []
    while start < final:
        numbers.append(start)
        start = start + interval

    return numbers

def create_plot(X_traversed, f, var):
    # First create the graph of the function itself
    x_val = frange(-1, 1, 0.01)
    f_val = [f.subs({var:x}) for x in x_val]
    plt.plot(x_val, f_val, 'bo')
    # calculate the function value at each of the intermediate
    # points traversed
    f_traversed = [f.subs({var:x}) for x in X_traversed]
    plt.plot(X_traversed, f_traversed, 'r.')
    plt.legend(['Function', 'Intermediate points'], loc='best')
    plt.show()

if __name__ == '__main__':

    f = input('Enter a function in one variable: ')
    var = input('Enter the variable to differentiate with respect to: ')
    var0 = float(input('Enter the initial value of the variable: '))
    try:
        f = sympify(f)
    except SympifyError:
        print('Invalid function entered')
    else:
        var = Symbol(var)
        d = Derivative(f, var).doit()
        var_min, X_traversed = grad_descent(var0, d, var)
        if var_min:
            print('{0}: {1}'.format(var.name, var_min))
            print('Minimum value: {0}'.format(f.subs({var:var_min})))
            create_plot(X_traversed, f, var)


import numpy as np
import matplotlib.pyplot as plt
from BSpline import BSpline, OpenUniformKnotVector
import math

def FindSide(P1, P2, P):
    val = (P[1] - P1[1]) * (P2[0] - P1[0]) - (P2[1] - P1[1]) * (P[0] - P1[0])

    return np.sign(val)

def LineDist(P1, P2, P):
    return abs((P[1] - P1[1]) * (P2[0] - P1[0]) - (P2[1] - P1[1]) * (P[0] - P1[0]))

def QuickHull(Points, n, P1, P2, side, Hull):
    index = -1
    max_dist = 0

    for i in range(0, n):
        temp = LineDist(P1, P2, Points[i, :])
        if(FindSide(P1, P2, Points[i, :]) == side and temp > max_dist):
            index = i
            max_dist = temp

    if(index == -1):
        Hull.append(P1)
        Hull.append(P2)
        return

    QuickHull(Points, n, Points[index, :], P1, -FindSide(Points[index, :], P1, P2), Hull)
    QuickHull(Points, n, Points[index, :], P2, -FindSide(Points[index, :], P2, P1), Hull)

def ConvexHull2D(Points):
    maxX = np.argmax(Points[:, 0])
    minX = np.argmin(Points[:, 0])
    countPoints = np.size(Points, 0)

    Hull = []

    QuickHull(Points, countPoints, Points[minX, :], Points[maxX, :], 1, Hull)
    QuickHull(Points, countPoints, Points[minX, :], Points[maxX, :], -1, Hull)

    Hull = np.asarray(Hull)

    Hull = np.unique(Hull, axis=0)

    #sort by angle so it plots nicely
    Center = np.average(Hull)

    ToCenter = Hull - Center
    Angles = np.arctan2(ToCenter[:, 0], ToCenter[:, 1])

    SortedAngles = np.argsort(Angles)

    Hull = np.array(Hull)[SortedAngles]

    #copy first element so it loops on itself in plot
    Hull = np.append(Hull, [Hull[0, :]], axis=0)

    return Hull

if __name__ == "__main__":
    #shows that the b spline curve fits in the convex hull of the control points
    Points = np.array([[1., 1.], [2., 4.], [2., 6.], [4., 3.], [6., 6.], [8., 6.]])
    countPoints = np.size(Points, 0)
    order = 3

    Knots = OpenUniformKnotVector(order, countPoints, True)
    T = np.arange(0, 1.0, 0.01)

    Spline = BSpline(Points, order, Knots, T)

    Hull = ConvexHull2D(Points)

    plt.plot(Points[:, 0], Points[:, 1])
    plt.plot(Hull[:, 0], Hull[:, 1], 'r--')
    plt.plot(Spline[:, 0], Spline[:, 1])
    plt.show()


## 2. The Empirical Probability ##

p_tail = 44 / 100
p_six = 28 / 200
p_odd = 102/ 200
print(p_tail)
print(p_six)
print(p_odd)

## 3. Probability as Relative Frequency ##

p_heads_1 = 1 - (162/300)
percentage_1 = p_heads_1 * 100

p_heads_2 = 1 - (2450/5000)
percentage_2 = p_heads_2 * 100

print(percentage_1)
print(percentage_2)

## 4. Repeating an Experiment ##

# INITIAL CODE
from numpy.random import seed, randint

seed(1)

def coin_toss():
    if randint(0,2) == 1:
        return 'HEAD'
    else:
        return 'TAIL'

probabilities = []
heads = 0

for n in range(1, 10001):
    # Uncomment above and complete code from here
    outcome = coin_toss()
    if outcome == 'HEAD':
        heads +=1
    current_probability = heads / n
    probabilities.append(current_probability)

print(probabilities[:10])
print(probabilities[-10:])

## 5. The True Probability Value ##

p_l = 87/ 200
p_l_and_c = 40 / 200
p_h = 63/200
p_no = 1 - (160/200)
print(p_l)
print(p_l_and_c)
print(p_h)
print(p_no)

## 6. The Theoretical Probability ##

p_5 = 1/6
p_ht = 1/4
p_tt = 1/4

## 7. Events vs. Outcomes ##

p_even = 3/6
p_odd_no_3 = 2/6
p_odd_greater_5 = 0/6

## 8. A Biased Die ##

p_blue = 10 /100
p_red = 90/100

5. Probability and Statistics/probability-fundamentals/Estimating Probabilities-377.py


from __future__ import print_function
from __future__ import division
from math import log
import numpy as np

def calcShannonEnt(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:
        currentLabel = featVec[-1]
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1

    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key]) / numEntries
        shannonEnt -= prob * log(prob, 2)

    return shannonEnt

def calcShannonEnt_(labels):
    numEntries = len(labels)
    labelCounts = {}

    for currentLabel in labels:
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1

    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key]) / numEntries
        shannonEnt -= prob * log(prob, 2)

    return shannonEnt

def createDataSet():
    dataSet = [[1, 1, 'yes'],
               [1, 1, 'yes'],
               [1, 0, 'no'],
               [0, 1, 'no'],
               [0, 1, 'no']]
    features = ['no surfacing', 'flippers']
    return dataSet, features

if __name__ == '__main__':
    data, features = createDataSet()
    print(calcShannonEnt(data))

    labels = ['yes', 'yes', 'no', 'no', 'no']
    print(calcShannonEnt_(labels))


import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import freqz

if __name__ == '__main__':
    fs = 5000
    Ts = 1/fs
    Ns = 1024

    # Generate the signal
    f1 = 500
    f2 = 2000
    t = np.arange(0, 1, Ts)
    x1 = np.sin(2 * np.pi * f1 * t)
    x2 = np.sin(2 * np.pi * f2 * t)
    x = x1 + x2

    x = x[:Ns]
    t = t[:Ns]

    # Plot the signal and the magnitude spectrum
    plt.figure(1)
    plt.subplot(211)
    plt.plot(t, x)
    plt.subplot(212)
    X = np.fft.fft(x)
    X = X[:int(len(X)/2)]
    f = np.linspace(0, fs/2, len(X))
    A = np.sqrt(np.imag(X)**2 + np.real(X)**2)
    A = A/len(A)
    plt.plot(f, A)

    #Design a LP FIR filter
    fc = 1000
    wc = fc*np.pi/fs
    n = np.arange(0, Ns)
    h = np.sin(wc*(n - (Ns-1)/2))/(np.pi*(n - (Ns-1)/2))
    # print(h[int((Ns-1)/2)])
    # print(wc/np.pi)
    plt.figure(2)
    plt.plot(n, h)

    #Filter the signal with convolution
    y1 = np.convolve(x,h,mode='same')
    plt.figure(1)
    plt.subplot(211)
    plt.plot(t, y1)
    plt.subplot(212)
    Y = np.fft.fft(y1)
    Y = Y[:int(len(Y) / 2)]
    f = np.linspace(0, fs / 2, len(Y))
    Ay = np.sqrt(np.imag(Y) ** 2 + np.real(Y) ** 2)
    Ay = Ay / len(Ay)
    plt.plot(f, Ay)

    # Design a window for the filter - Triangle
    tri_win = 1 - np.abs((2*n-Ns+1)/(Ns-1))
    h_win = np.multiply(h, tri_win)
    plt.figure(2)
    #plt.plot(n, tri_win)
    plt.plot(n, h_win)

    # Filter the signal with convolution
    y2 = np.convolve(x, h_win, mode='same')
    plt.figure(1)
    plt.subplot(211)
    plt.plot(t, y2)
    plt.subplot(212)
    Y = np.fft.fft(y2)
    Y = Y[:int(len(Y) / 2)]
    f = np.linspace(0, fs / 2, len(Y))
    Ay = np.sqrt(np.imag(Y) ** 2 + np.real(Y) ** 2)
    Ay = Ay / len(Ay)
    plt.plot(f, Ay)

    # check the filter response
    w1, h1 = freqz(h, worN=8000)
    f = np.linspace(0,fs/2,8000)
    w2, h2 = freqz(h_win, worN=8000)
    plt.figure(3)
    plt.plot(f,np.abs(h1))
    plt.plot(f, np.abs(h2))

    plt.show()


import numpy as np
import time

def to3d(point):
    return np.array([point[0], point[1], 1.0])

def determinent(p1, p2, p3):
    mat = np.vstack((to3d(p1), to3d(p2), to3d(p3)))
    print(mat)
    return np.linalg.det(mat)

def simple_determinent(p1, p2, p3):
    return p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) + p3[0] * (p1[1] - p2[1])

p1 = (1, 2)
p2 = (2, 3)
p3 = (3, 4)

t1 = time.time()
det = determinent(p1, p2, p3)
print('time taken = ', time.time() - t1)
epsilon = 1e-2

det = 0 if det < epsilon else det
print(det)

t1 = time.time()
det = simple_determinent(p1, p2, p3)
print('time taken = ', time.time() - t1)
epsilon = 1e-2

det = 0 if det < epsilon else det
print(det)


import numpy as np
from RRT.polygon_functions import *
INT_MAX = 10000

def is_inside_circle(conf, circle_obstacle):
    """
    Checks to see if the configuration is in collision with a circle obstacle.

    conf: The location of the point being tested represented as an np.ndarray
    of shape (2,).
    circle_obstacle: The circle obstacle being tested.

    return: True if the configuration is in collision with the circle and False if not.
    """

    # Store the points that represent the randomly chosen point conf
    x = conf[0]
    y = conf[1]

    # Store circle center (x,y) value
    center = circle_obstacle.position
    center_x = center[0]
    center_y = center[1]

    # Store circle radius
    r = circle_obstacle.size

    # Set bounds of y for circle at current point
    y_max = center_y + np.sqrt(np.abs(r**2 - (x-center_x)**2))
    y_min = center_y - np.sqrt(np.abs(r**2 - (x-center_x)**2))

    # Set bounds of x for circle at current point
    x_max = center_x + np.sqrt(np.abs(r**2 - (y-center_y)**2))
    x_min = center_x - np.sqrt(np.abs(r**2 - (y-center_y)**2))

    # Check bounds of circle
    # If point is within bounds of circle, then there is a collision and return True
    if ((y >= y_min) and (y <= y_max) and (x >= x_min) and (x <= x_max)):
        return True

    else:
        return False

def is_inside_polygon(p, points):
    """
    Function checks whether or not point 'p' is within the polygon outlined by
    the set of vertices 'points'.

    p: The location of the point being tested represented as an np.ndarray of shape (2,).

    points: Set of vertices that represents the polygon represented as a list of tuples.

    returns: Returns true if the point lies within the polygon and False otherwise.
    """

    n = len(points)

    # There must be at least 3 vertices
    # in polygon
    if n < 3:
        return False

    # Create a point for line segment
    # from p to infinite
    extreme = (INT_MAX, p[1])
    count = i = 0

    while True:
        next = (i + 1) % n

        # Check if the line segment from 'p' to
        # 'extreme' intersects with the line
        # segment from 'polygon[i]' to 'polygon[next]'
        if (line_intersect(points[i], points[next], p, extreme)):

            # If the point 'p' is collinear with line
            # segment 'i-next', then check if it lies#
            # on segment. If it lies, return true, otherwise false
            if orientation(points[i], p, points[next]) == 0:
                return on_segment(points[i], p, points[next])

            count += 1

        i = next

        if (i == 0):
            break

    # Return true if count is odd, false otherwise
    return (count % 2 == 1)


import numpy as np
from numpy import linalg as la
import matplotlib.pyplot as plt
import bisection as bis

'''Gradient descent for 2-dimensions minimization'''
class Gradient():

    def __init__(self, alpha = 0.01, precision = 10**-6, X = np.array([[0.] , [0.]])):
         self.Xs = [] # Candidates
         self.Xs.append(X) # Append first candidate (X0)
         self.alpha = alpha   # Learning rate
         self.precision = precision # Stop criteria

    '''Returns the gradient of f(x1, x2) evaluated at X'''
    def gradient(self, X):
        x1 = X[0][0]
        x2 = X[1][0]

        return np.array([[4*(x1 - 2)],
                         [4*(x2 - 3)]])

    '''Evaluate f at X'''
    def evaluate(self, X):
        return 2*(X[0] - 2)**2 + 2*(X[1] - 3)**2 # Function to be minimized

    '''Returns the point (x1, x2) which minimizes f'''
    def descent(self):
        d = - self.gradient(self.Xs[0])

        # Calculate new X and Update direction
        while la.norm(d) > self.precision:
            ialpha = bis.getAlpha(self.Xs[-1], d) # Iteration alpha from bisection
            X = self.Xs[-1] + ialpha*d
            self.Xs.append(X)
            d = - self.gradient(self.Xs[-1])
        return self.Xs[-1]

    def plot(self, title, xlabel, ylabel):
        x1s = []
        x2s = []

        for X in self.Xs:
            x1s.append(X[0][0])
            x2s.append(X[1][0])

        plt.plot(x1s, x2s, 'bo')
        plt.title(title)
        plt.xlabel(xlabel)
        plt.ylabel(ylabel)
        plt.show()


import numpy as np

class SignalGenerator:
	def __init__(self, period, signal_type="sine", amplitude=1, vertical_shift=0, phase_shift=0):
		assert signal_type.lower() in ["sine", "cos", "half-sine", "half-cos", "sawtooth"], "get unknown signal_type: '{}'".format(signal_type)

		self.period = period
		self.signal_type = signal_type.lower()
		self.amplitude = amplitude
		self.vertical_shift = vertical_shift
		self.phase_shift = phase_shift

	def _cal_x(self, t):
		"""
		Convert t into x which has range of [0, 1]
		"""
		x = np.mod(t + self.phase_shift, self.period)
		x = x / self.period
		return x

	def _cal_y(self, x):
		"""
		Calculate y from x according to signal type
		"""
		if self.signal_type == "sine":
			y = np.sin(2 * np.pi * x) * self.amplitude + self.vertical_shift
		elif self.signal_type == "cos":
			y = np.cos(2 * np.pi * x) * self.amplitude + self.vertical_shift
		elif self.signal_type == "half-sine":
			y = np.sin(2 * np.pi * (x / 2 - 0.25)) * self.amplitude + self.vertical_shift
		elif self.signal_type == "half-cos":
			y = np.cos(2 * np.pi * x / 2) * self.amplitude + self.vertical_shift
		else:
			y = x * self.amplitude + self.vertical_shift
		return y

	def __call__(self, start_t, stop_t):
		t = np.arange(start_t, stop_t)
		x = self._cal_x(t)
		y = self._cal_y(x)
		return y


import numpy as np
import random

from q1_softmax import softmax
from q2_sigmoid import sigmoid, sigmoid_grad
from q2_gradcheck import gradcheck_naive

def forward_backward_prop(data, labels, params, dimensions):
    """
    Forward and backward propagation for a two-layer sigmoidal network

    Compute the forward propagation and for the cross entropy cost,
    and backward propagation for the gradients for all parameters.
    """

    ### Unpack network parameters (do not modify)
    ofs = 0
    Dx, H, Dy = (dimensions[0], dimensions[1], dimensions[2])

    W1 = np.reshape(params[ofs:ofs+ Dx * H], (Dx, H))
    ofs += Dx * H
    b1 = np.reshape(params[ofs:ofs + H], (1, H))
    ofs += H
    W2 = np.reshape(params[ofs:ofs + H * Dy], (H, Dy))
    ofs += H * Dy
    b2 = np.reshape(params[ofs:ofs + Dy], (1, Dy))

    ### YOUR CODE HERE: forward propagation
    h_per_item = sigmoid(np.dot(data, W1) + b1)
    yhat_per_item = softmax(np.dot(h_per_item, W2) + b2)
    cost = -np.sum(labels * np.log(yhat_per_item))
    ### END YOUR CODE

    ### YOUR CODE HERE: backward propagation
    grad_softmax_per_item = yhat_per_item - labels
    grad_b2 = np.sum(grad_softmax_per_item, axis=0, keepdims=True)
    grad_W2 = np.dot(h_per_item.T, grad_softmax_per_item)
    grad_sigmoid_per_item = sigmoid_grad(h_per_item)
    grad_b1_per_item = np.dot(grad_softmax_per_item, W2.T) * grad_sigmoid_per_item
    grad_b1 = np.sum(grad_b1_per_item, axis=0, keepdims=True)
    grad_W1 = np.dot(data.T, grad_b1_per_item)
    ### END YOUR CODE
    assert grad_b2.shape == b2.shape
    assert grad_W2.shape == W2.shape
    assert grad_b1.shape == b1.shape
    assert grad_W1.shape == W1.shape

    ### Stack gradients (do not modify)
    grad = np.concatenate((grad_W1.flatten(), grad_b1.flatten(),
        grad_W2.flatten(), grad_b2.flatten()))

    return cost, grad

def sanity_check():
    """
    Set up fake data and parameters for the neural network, and test using
    gradcheck.
    """
    print "Running sanity check..."

    N = 20
    dimensions = [10, 5, 10]
    data = np.random.randn(N, dimensions[0])   # each row will be a datum
    labels = np.zeros((N, dimensions[2]))
    for i in xrange(N):
        labels[i,random.randint(0,dimensions[2]-1)] = 1

    params = np.random.randn((dimensions[0] + 1) * dimensions[1] + (
        dimensions[1] + 1) * dimensions[2], )

    gradcheck_naive(lambda params: forward_backward_prop(data, labels, params,
        dimensions), params)

def your_sanity_checks():
    """
    Use this space add any additional sanity checks by running:
        python q2_neural.py
    This function will not be called by the autograder, nor will
    your additional tests be graded.
    """
    print "Running your sanity checks..."
    ### YOUR CODE HERE
    raise NotImplementedError
    ### END YOUR CODE

if __name__ == "__main__":
    sanity_check()
    your_sanity_checks()


import numpy as np

class Activation:
    """An activation function"""

    def __init__(self, name, activation, d_activation, vectorize=True):
        self.name = name
        self.activation = np.vectorize(activation) if vectorize else activation
        self.derivative = np.vectorize(d_activation) if vectorize else d_activation

    def __call__(self, inputs, D=False) -> np.array:
        return self.activation(inputs) if (not D) else self.derivative(inputs)

# Some Common Activation Functions
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))

def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

def tanh_derivative(x):
    return 1 - (tanh(x) ** 2)

# Common Activations
IDENTITY = Activation('None', lambda x: x, lambda x: 1)
RELU = Activation('ReLU',
                  lambda x: x if (x > 0) else 0,
                  lambda x: 1 if (x > 0) else 0)
SIGMOID = Activation('Sigmoid', sigmoid, sigmoid_derivative)
TANH = Activation('Hyperbolic', tanh, tanh_derivative)


#------------------------Packages------------------------#

import numpy as np
import scipy.stats as si
import matplotlib.pyplot as plt
import pymc3 as pm
### I have 252 observations for HV, so 0 to 251 ###
### I have 253 observations for IV, so 0 to 252 ###

#-------------------------Option Pricing---------------------------#

    ######## Black-Scholes Model########

''' Call Option Premium C = SN(d1) - Xe^(-rt) N(d2)
     Put Option Premium P = Xe^(-rT) N(-d2) - S0 N(-d1)'''

def BS_price(strategy, sp, x, r, sd, t):
    d1 = (np.log(sp/x) + (r + (sd**2*0.5)*t)) / sd*np.sqrt(t)
    #print("D1 = ", d1)
    d2 = d1 - sd*np.sqrt(t)
    if strategy == 'c':
        price = sp * si.norm.cdf(d1,0,1) - x * np.exp(-r*t) * si.norm.cdf(d2,0,1)
        return price
    elif strategy == 'p':
        price = x * np.exp(-r*t) * si.norm.cdf(-d2,0,1) - sp * si.norm.cdf(-d1,0,1)
        return price
    else:
        print("No Such Option Strategy; Enter 'c' for Call or 'p' for Put")
        exit()

    ######## Calculating Historical Volatility (A.K.A standard deviation of the periodic daily return) #######
    #'n' Max is 252 days#

def hist_vol(ticker, n):
    file = []
    if ticker == 'GOOG':
        file = open('/Users/TristanTaylor/Documents/Master_Project/HV/GOOG_HV.csv', 'r')
    elif ticker == 'AMZN':
        file = open('/Users/TristanTaylor/Documents/Master_Project/HV/AMZN_HV.csv', 'r')
    elif ticker == 'AAPL':
        file = open('/Users/TristanTaylor/Documents/Master_Project/HV/AAPL_HV.csv', 'r')
    else:
        print("Enter 'GOOG', 'AMZN', or 'AAPL' for ticker")
        exit()
    s = []
    for lines in file:
        s = np.append(s, float(lines))
    #print(s)
    r = []
    for i in range(n):
        r.append(np.log(s[i]/s[i+1]))
        #print(r)

    a = []
    r_mean = np.mean(r)
    #print('Mean: ', r_mean)
    diff_square = [(r[j] - r_mean) ** 2 for j in range(0, len(r))]
    #print(diff_square)
    std = np.sqrt(sum(diff_square) * (1.0 / (n)))
    hist = std * np.sqrt(n+1)
    return hist, std

####GOOG on 06/24/19: adj c.price=1115.52, strike=1115.00, 10yr. Treasury=0.02016, time=88days, target=45.30, "call"

#OTM Call & Put
####GOOG on 06/27/19: adj c.price=1076.01, strike=1077.50, 10yr. Treasury=0.02016, time=29days, target=35.55, "call"
####GOOG on 06/27/19: adj c.price=1076.01, strike=1075.00, 10yr. Treasury=0.02016, time=29days, target=33.75, "put"

####AMZN on 06/27/19: adj c.price=1904.28, strike=1905.00, 10yr. Treasury=0.02016, time=29days, target=66.00, "call"
####AMZN on 06/27/19: adj c.price=1904.28, strike=1902.50, 10yr. Treasury=0.02016, time=29days, target=61.48, "put"

####AAPL on 6/27/19: adj c.price=199.74, strike=200.00, 10yr. Treasury=0.02016, time=29days, target=5.07, "call"
####AAPL on 6/27/19: adj c.price=199.74, strike=197.50, 10yr. Treasury=0.02016, time=29days, target=4.53, "put"

#25 Delta Call & Put
####GOOG on 06/27/19: adj c.price=1076.01, strike=1095.00, 10yr. Treasury=0.02016, time=29days, target=27.05, "call"
####GOOG on 06/27/19: adj c.price=1076.01, strike=1057.50, 10yr. Treasury=0.02016, time=29days, target=24.65, "put"

####AMZN on 06/27/19: adj c.price=1904.28, strike=1922.50, 10yr. Treasury=0.02016, time=29days, target=56.98, "call"
####AMZN on 06/27/19: adj c.price=1904.28, strike=1885.00, 10yr. Treasury=0.02016, time=29days, target=53.58, "put"

####AAPL on 06/27/19: adj c.price=199.74, strike=217.50, 10yr. Treasury=0.02016, time=29days, target=0.6, "call"
####AAPL on 06/27/19: adj c.price=199.74, strike=180.00, 10yr. Treasury=0.02016, time=29days, target=0.85, "put"

    ######## Calculating Vega ########

def vega(sp, x, r, sd, t):
    d1 = (np.log(sp/x) + (r + (sd**2/2)*t)) / sd*np.sqrt(t)
    return sp * np.sqrt(t) * si.norm.pdf(d1,0,1)

    ######### Calculating Implied Volatility########

def imp_vol(target, strategy, sp, x, r, sd, t):
    precision = 0.0000001

    price = BS_price(strategy, sp, x, r, sd, t)
    veg = vega(sp, x, r, sd, t)
    diff = target - price
    new_sd = sd + diff / (veg)

    if abs(diff) > precision:
        new_sd = imp_vol(target, strategy, sp, x, r, new_sd, t)
    else:
        new_sd = sd + diff/vega(sp, x, r, new_sd, t)
        #print('New SD = ', new_sd)

    return new_sd

def iv_range_formula(sp, sd, t):
    upper = sp + sp*sd*np.sqrt(t)
    lower = sp - sp*sd*np.sqrt(t)
    return upper, lower

def iv_percent(ticker, strategy, n, sd):
    file = []
    if ticker == "GOOG":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_put_IV2.csv', 'r')
    elif ticker == "AMZN":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_put_IV2.csv', 'r')
    elif ticker == "AAPL":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_put_IV2.csv', 'r')
    else:
        exit()
    s = []
    for lines in file:
        s = np.append(s, float(lines))
    # print(s)
    r = []
    for i in range(n):
        r.append(s[i])
        # print(r)
    b = 0
    for i in range(len(r)):
        if r[i] < sd*100:
            b += 1
        else:
            pass
    p = b/252
    return p

#----------------------Bayesian Inference-------------------------#

    ######## Plotting Implied Volatility for GOOG, AAPL, & AMZN ########

def data_iv(ticker, strategy, n):
    file = []
    if ticker == "GOOG":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_put_IV2.csv', 'r')
    elif ticker == "AMZN":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_put_IV2.csv', 'r')
    elif ticker == "AAPL":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_put_IV2.csv', 'r')
    else:
        exit()
    s = []
    for lines in file:
        s = np.append(s, float(lines))
    # print(s)
    r = []
    for i in range(n):
        r.append(s[i])
        # print(r)
    fig, ax = plt.subplots(figsize=(14, 8))
    ax.plot(r, label='Implied Volatility Plot')
    ax.set(xlabel='Time (Days)', ylabel='25 Trading Days IV')
    ax.legend()
    plt.show()
    r_mean = np.mean(r)
    #print('Mean: ', r_mean)
    diff_square = [(r[j] - r_mean) ** 2 for j in range(0, len(r))]
    # print(diff_square)
    var = (np.sum(diff_square) / (n))
    # std = np.sqrt(np.sum(diff_square)/(n))
    #print('Variance: ', var)
    # print(std)
    # hist = std * np.sqrt(n+1)
    return r_mean, var

def data_iv_update(ticker, strategy, n):
    file = []
    if ticker == "GOOG":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_call_IV.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_put_IV.csv', 'r')
    elif ticker == "AMZN":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_call_IV.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_put_IV.csv', 'r')
    elif ticker == "AAPL":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_call_IV.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_put_IV.csv', 'r')
    else:
        exit()
    s = []
    for lines in file:
        s = np.append(s, float(lines))
    # print(s)
    r = []
    for i in range(n):
        r.append(s[i])
        # print(r)
    fig, ax = plt.subplots(figsize=(14, 8))
    ax.plot(r, label='Implied Volatility Plot')
    ax.set(xlabel='Time (Days)', ylabel='25 Trading Days IV')
    ax.legend()
    plt.show()
    r_mean = np.mean(r)
    # print('Mean: ', r_mean)
    diff_square = [(r[j] - r_mean) ** 2 for j in range(0, len(r))]
    # print(diff_square)
    var = (np.sum(diff_square) / (n))
    # std = np.sqrt(np.sum(diff_square)/(n))
    # print('Variance: ', var)
    # print(std)
    # hist = std * np.sqrt(n+1)
    return r_mean, var

    ######## Methods of Moments Estimation of Gamma Parameters ########

def parameters(mean, var):
    shape = mean ** 2 / var
    #print('Shape: ', shape)
    scale = var / mean
    #print('Scale: ', scale)
    return shape, scale

def parameters_inv(mean, var):
    shape = mean ** 2 / var + 2
    scale = mean * (mean ** 2 / var + 1)
    return shape, scale

    ######## QQPlot: See if Data Follows a Distribution ########

def qqplot(ticker, strategy, n, mean, var, shape, scale, inv_shape, inv_scale):
    file = []
    if ticker == "GOOG":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_put_IV2.csv', 'r')
    elif ticker == "AMZN":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_put_IV2.csv', 'r')
    elif ticker == "AAPL":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_call_IV2.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_put_IV2.csv', 'r')
    else:
        exit()
    s = []
    for lines in file:
        s = np.append(s, float(lines))
    #print(s)
    r = []
    for i in range(n):
        r.append(s[i])
    #print(r)

    fig = plt.figure()
    ax_1 = fig.add_subplot(221)
    ax_2 = fig.add_subplot(222)
    ax_3 = fig.add_subplot(223)
    res_g = si.probplot(r, dist=si.gamma, sparams=(shape, scale), plot=ax_1)
    res_n = si.probplot(r, dist=si.norm, sparams=(mean, np.sqrt(var)), plot=ax_2)
    res_i = si.probplot(r, dist=si.invgamma, sparams=(inv_shape, inv_scale), plot=ax_3)
    ax_1.set_title("Probplot on Gamma Distribution")
    ax_2.set_title("Probplot on Normal Distribution")
    ax_3.set_title("Probplot on Inverse Gamma Distribution")
    plt.show()
    return r

def qqplot_update(ticker, strategy, n, mean, var, shape, scale, inv_shape, inv_scale):
    file = []
    if ticker == "GOOG":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_call_IV.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/GOOG_put_IV.csv', 'r')
    elif ticker == "AMZN":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_call_IV.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AMZN_put_IV.csv', 'r')
    elif ticker == "AAPL":
        if strategy == 'c':
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_call_IV.csv', 'r')
        else:
            file = open('/Users/TristanTaylor/Documents/Master_Project/IV/AAPL_put_IV.csv', 'r')
    else:
        exit()
    s = []
    for lines in file:
        s = np.append(s, float(lines))
    #print(s)
    r = []
    for i in range(n):
        r.append(s[i])
    #print(r)

    fig = plt.figure()
    ax_1 = fig.add_subplot(221)
    ax_2 = fig.add_subplot(222)
    ax_3 = fig.add_subplot(223)
    res_g = si.probplot(r, dist=si.gamma, sparams=(shape, scale), plot=ax_1)
    res_n = si.probplot(r, dist=si.norm, sparams=(mean, np.sqrt(var)), plot=ax_2)
    res_i = si.probplot(r, dist=si.invgamma, sparams=(inv_shape, inv_scale), plot=ax_3)
    ax_1.set_title("Probplot on Gamma Distribution")
    ax_2.set_title("Probplot on Normal Distribution")
    ax_3.set_title("Probplot on Inverse Gamma Distribution")
    plt.show()
    return r

def bayesian_modeling(mean, var, alpha, beta, inv_a, inv_b, iv):
    with pm.Model() as model:
        prior = pm.InverseGamma('bv', inv_a, inv_b)

        likelihood = pm.Gamma('like', alpha, beta, observed=iv)

    with model:
        # step = pm.Metropolis()

        v_trace = pm.sample(10000, tune=1000)
        #print(v_trace['bv'][:])
        trace = v_trace['bv'][:]
        #print(trace1)

    pm.traceplot(v_trace)
    plt.show()

    pm.autocorrplot(v_trace)
    plt.show()

    #s = pm.summary(v_trace)
    #print(s)
    return trace

def update_bayesian_modeling(mean_upd, var_upd, alpha_upd, beta_upd, inv_a_upd, inv_b_upd, iv_upd, strategy, stock_price, strike_price, risk_free, time):
    with pm.Model() as update_model:
        prior = pm.InverseGamma('bv', inv_a_upd, inv_b_upd)

        likelihood = pm.InverseGamma('like', inv_a_upd, inv_b_upd, observed=iv_upd)

    with update_model:
        # step = pm.Metropolis()

        v_trace_update = pm.sample(10000, tune=1000)
        #print(v_trace['bv'][:])
        trace_update = v_trace_update['bv'][:]
        #print(trace)

    pm.traceplot(v_trace_update)
    plt.show()

    pm.autocorrplot(v_trace_update)
    plt.show()

    pm.plot_posterior(v_trace_update[100:], color='#87ceeb', point_estimate='mean')
    plt.show()

    s = pm.summary(v_trace_update).round(2)
    print("\n Summary")
    print(s)

    a = np.random.choice(trace_update, 10000, replace=True)
    ar = []
    for i in range(9999):
        t = a[i] / 100
        ar.append(t)
    #print("Bayesian Volatility Values", ar)

    op = []
    for i in range(9999):
        temp = BS_price(strategy, stock_price, strike_price, risk_free, ar[i], time)
        op.append(temp)
    #print("Bayesian Option Prices", op)

    plt.hist(ar, bins=50)
    plt.title("Volatility")
    plt.ylabel("Frequency")
    plt.show()

    plt.hist(op, bins=50)
    plt.title("Option Price")
    plt.ylabel("Frequency")
    plt.show()
    return trace_update

#--------------------------Main Code to Run--------------------------#
def main():
    ######## Getting Input from User ########

    ticker = input("Enter stock ticker symbol: ")

    stock_price = float(input("Enter current stock price: "))

    strike_price = float(input("Enter strike price: "))

    risk_free = float(input("Enter risk-free interest rate: "))

    time = float(input("Enter days to maturity: "))
    time = time/365

    target = float(input("Enter Target Value: "))

    strategy = input("Enter Strategy: ")

    ######## Calculate Implied Volatility & Black-Scholes Option Price w/ Historical Volatility ########

    hist = hist_vol(ticker, 252)

    vol = imp_vol(target, strategy, stock_price, strike_price, risk_free, hist[0], time)

    bs = BS_price(strategy, stock_price, strike_price, risk_free, vol, time)

    percent = iv_percent(ticker, strategy, 252, vol)

    std_dev = iv_range_formula(stock_price, vol, time)

    print("\n Annual Historical Volatility, Periodic Daily Return:")
    print(hist)

    print("\n Option Price with HV:")
    print(BS_price(strategy, stock_price, strike_price, risk_free, hist[0], time))

    print("\n Implied Volatility:")
    print(vol)
    print("\n Option Price with IV:")
    print(bs)
    print("\n IV Percentile")
    print(percent)
    print("\n Expected Price Range at Maturity")
    print(std_dev)

    ######## Calculate Parameters: mean, variance, alpha, & beta ########

    mean, var = data_iv(ticker, strategy, 252)
    mean = float(mean)
    alpha, beta = parameters(mean, var)
    print("\n Gamma Parameters")
    print(alpha, beta)
    inv_a, inv_b = parameters_inv(mean, var)
    print("\n Inverse Gamma Parameters")
    print(inv_a, inv_b)
    iv = qqplot(ticker, strategy, 252, mean, var, alpha, beta, inv_a, inv_b)

    ######## Simple Bayesian Statistics (NUTS) ########

    trace = bayesian_modeling(mean, var, alpha, beta, inv_a, inv_b, iv)

    ######## Define New Parameters: mean, variance, alpha, & beta for Update ########

    mean_update, var_update = np.mean(trace), np.var(trace)
    mean_update = float(mean)
    alpha_update, beta_update = parameters(mean_update, var_update)
    print("\n Gamma Parameters")
    print(alpha_update, beta_update)
    inv_a_update, inv_b_update = parameters_inv(mean_update, var_update)
    print("\n Inverse Gamma Parameters")
    print(inv_a_update, inv_b_update)
    iv_update = qqplot_update(ticker, strategy, 252, mean_update, var_update, alpha_update, beta_update, inv_a_update, inv_b_update)
    new_mean, new_var = data_iv_update(ticker, strategy, 252)
    new_alpha, new_beta = parameters(new_mean, new_var)
    new_inv_a, new_inv_b = parameters_inv(new_mean, new_var)
    ######## Update Posterior ########

    new_trace = update_bayesian_modeling(mean_update, var_update, new_alpha, new_beta, new_inv_a, new_inv_b, iv_update, strategy, stock_price, strike_price, risk_free, time)

    ###CALL###
    # 03/20/18-03/20/19: GOOG mean=23.939, var=33.696, shape=17.008, scale=1.408, bma=23.995
    # 03/26/18-03/26/19: AMZN mean=30.104, var=72.564, shape=12.489, scale=2.410, bma=30.132
    # 03/26/18-03/26/19: AAPL mean=23.539, var=35.819, shape=15.469, scale=1.522, bma=23.463

    ###PUT###
    # 03/20/18-03/20/19: GOOG mean=26.395, var=45.190, shape=15.417, scale=1.712, bma=26.412
    # 03/26/18-03/26/19: AMZN mean=33.377, var=97.999, shape=11.367, scale=2.936, bma=33.376
    # 03/26/18-03/26/19: AAPL mean=26.570, var=57.393, shape=12.300, scale=2.160, bma=26.623

main()


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

#############################################################
# 1. Imports
#############################################################

import numpy as np
import matplotlib.pyplot as plt

from scipy.optimize import curve_fit

#############################################################
# 2. General Functions
#############################################################

def lsq(x, y):
    assert len(x) == len(y), 'Array dimensions do not match'
    n = float(len(x)) # Don't lose precision with int * float multiplication

    # compute covariance matrix and correlation coefficient of data
    cov  = np.cov(x, y)
    varx = cov[0][0]
    vary = cov[1][1]
    sxy  = cov[0][1]
    r    = sxy / (np.sqrt(vary) * np.sqrt(varx))

    # lambda expression for a line
    # dummy parameter array of [1, 1]
    f    = lambda x, *p: p[0]*x + p[1]
    pars = [1, 1]

    pvals, pcov = curve_fit(f, x, y, p0=pars)

    m, b = pvals
    sm   = np.sqrt(pcov[0, 0])
    sb   = np.sqrt(pcov[1, 1])
    sy   = np.sqrt(vary)

    # y = mx + b; r is correlation
    return m, b, sy, sm, sb, r

#############################################################
# 3. Data & Globals
#############################################################

current = np.array([5.372, 10.024, 14.975, 20.482, 24.878, 30.105]) * 1e-3 # mA
voltage = np.array([0.503, 1.043, 1.526, 2.034, 2.521, 3.018]) # V

# The multimeter tends to have a variable uncertainty, so these arrays is needed
dI      = np.array([0.001, 0.002, 0.002, 0.001, 0.001, 0.003]) * 1e-3
dV      = np.array([0.002, 0.001, 0.003, 0.001, 0.001, 0.002])

#############################################################
# 4. Lab-Specific Functions
#############################################################

def plot_line():

    # Least-squares linear regression for y = mx + b
    m, b, sy, sm, sb, r = lsq(current * 1e3, voltage) # We want to plot in mA

    # You will NEED to call this for each plot so that you don't have multiple plots
    # overlaid on each other
    plt.figure()

    # Range upon which we want to plot the line
    x = np.linspace(5, 31, 1000)
    plt.plot(x, m*x + b, 'c--')

    plt.errorbar(x=(current * 1e3), y=voltage, xerr=(dI * 1e3), yerr=dV, fmt='r.', ecolor='k', alpha=0.5)

    plt.xlabel('Current ($mA$)')
    plt.ylabel('Voltage ($V$)')

def get_resistance():
    m, b, sy, sm, sb, r = lsq(current, voltage)

    # Resistance is the slope m; its uncertainty sm is already computed by lsq()
    return (m, sm)


import numpy as np
import sys

np.seterr(all='warn')

def sisEcua(mat_A, mat_B):
    a_inv = np.linalg.inv(mat_A)
    C = a_inv.dot(mat_B.T)
    return C

def sisDeEcuaciones(yx, x_elev, grado, n_datos):
    dimension = grado+1
    total_suma = len(x_elev)
    sistema = (dimension, dimension)
    mat_A = np.zeros(sistema)
    mat_B = np.matrix(yx)
    # ubicacion de datos en la matriz
    # primera fila
    col = 1
    for i in range(0, dimension-1):
        mat_A[0][col] = x_elev[i]
        col += 1
    # el resto de la ubicciones
    col = 0
    inicio = 0
    fin = dimension
    mat_A[0][0] = n_datos
    for i in range(1, dimension):
        for j in range(inicio, fin):
            mat_A[i][col] = x_elev[j]
            col += 1
        inicio += 1
        fin += 1
        col = 0
    # Para visualizar las matrices del sistema de ecuaciones
    #print(mat_A, mat_B)
    return sisEcua(mat_A, mat_B)

def multiplicacion(eje_y, datos_elev, cant_de_datos):
    datos_yx=[]
    for k in range(0, cant_de_datos):
        yx = eje_y[k]*datos_elev[k]
        datos_yx.append(yx)
        #print(datos_yx)
    return datos_yx

def potencia(eje_x, cant_de_datos, i):
    x_elev_i = []
    suma_x = 0
    for j in range(0, cant_de_datos):
        potencia = eje_x[j]**i
        x_elev_i.append(potencia)
        suma_x = suma_x+potencia
    return suma_x, x_elev_i

def rPolinomial(eje_x, eje_y, grado_del_polinomio):
    cant_de_datos = len(eje_y)
    cant_de_val_elevados = int(grado_del_polinomio*2)
    eje_y = np.array(eje_y)
    # vectores auxiliares
    suma_val_elevados_x = []
    suma_yx = []
    suma_y = np.sum(eje_y)
    suma_yx.append(suma_y)
    for i in range(1, cant_de_val_elevados+1):
        sumatoria, datos_elev = potencia(eje_x, cant_de_datos, i)
        suma_val_elevados_x.append(sumatoria)
        if i <= grado_del_polinomio:
            m = np.array(datos_elev)
            datos_yx = m*eje_y  # aqui falla
            suma_datos_yx = np.sum(datos_yx)
            suma_yx.append(suma_datos_yx)
            # Para visualizar las multiplicaciones de y*xi, y*x**2, etc.
            # print(datos_yx)
            # print(m)
            #print(datos_yx)
            # print(suma_yx)
    coeficientes = sisDeEcuaciones(
        suma_yx, suma_val_elevados_x, grado_del_polinomio, cant_de_datos)
    # visualizacion de los resultados
    print('\n     COEFICIENTES DEL AJUSTE POLINOMIAL\n')
    ecuacion_final = 'y = '
    for k in range(0, grado_del_polinomio+1):
        a = float(coeficientes[k])
        a_redondeada = "{0:.7f}".format(a)
        print(f'             a{k} = {a_redondeada} ')
        if k == 0:
            ec = str(a_redondeada)
        elif k == 1:
            ec = ' + '+str(a_redondeada)+'*x'
        else:
            ec = ' + '+str(a_redondeada)+'*x^'+str(k)
        ecuacion_final = ecuacion_final+ec
    print('\n La ecuacion de ajuste es:\n')
    print(f'         {ecuacion_final}')

#datos de prueba
""" x = [280, 650, 1000, 1200, 1500, 1700]
y = [32.7, 45.4, 52.15, 53.7, 52.9, 50.3]
grado = 2
rPolinomial(x, y, grado) """

""" x = [1976, 1992, 2001, 2012]
y = [205.42, 289.21, 276.45, 301.85]
grado = 2
rPolinomial(x, y, grado) """

""" x=[273,283,293,303,313,323,333,343,353,363,373]
y=[1.00738,1.00129,0.99883,0.99802,0.99804,0.99854,0.99943,1.00067,1.00229,1.00437,1.00697]
grado=4
rPolinomial(x, y, grado) """

""" x=[2,3,4,7,8,9,5,5]
y=[9,6,5,10,9,11,2,3] """

""" x = [50, 60, 70, 80,90,100]
y = [24.94,30.11,36.05,42.84,50.57,59.3]
grado=2
rPolinomial(x, y, grado) """

src/4. Ajuste de curvas/Metodos/regresion_polinomial.py


import numpy as np
import matplotlib.pylab as plt
def basic_softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

def simple_softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

x = np.array([800, 695, 690])
y1 = basic_softmax(x) # nan 출력(값을 초과함)
y2 = simple_softmax(x) # 상대적으로 작은 값, 값 간의 차이를 줄이기 위한 방법 - 각 값을 해당 array의 최대값으로 뺀 값

plt.plot(x, y1)
plt.plot(x, y2, linestyle="--")
plt.ylim(-0.1, 1.1)

print(y1)
print(y2)
print(np.sum(y1)) # 값의 합은 언제나 1이다.
print(np.sum(y2)) # 값의 합은 언제나 1이다.

plt.show()


import numpy as np
from pycsminwel import csminwel

# Test function
def rosenbrock(x):
    """
    Rosenbrock Function
    :param x: vector of legth 2
    :return: float
    """
    return (1 - x[0]) ** 2 + 105 * (x[1] - x[0] ** 2) ** 4

def drosenbrock(x):
    """
    Gradient of the Rosenbrock Function
    :param x: vector of legth 2
    :return: gradient vector
    """
    dr = np.zeros(2)
    dr[0] = 2 * (x[0] - 1) - 8 * 105 * x[0] * (x[1] - x[0] ** 2) ** 3
    dr[1] = 4 * 105 * (x[1] - x[0] ** 2) ** 3
    return dr

print(' ===== Optimization with NUMERCIAL derivatives =====')
x0 = np.array([10, -9])
h0 = np.eye(2) * 0.5
fh, xh, _, _, itct, _, _ = csminwel(rosenbrock, x0, h0)
print(f'Convergence in {itct} steps')
print(f'Minimal value is {round(fh, 4)}')
print('Optimal point is', xh)
print('\n')

print(' ===== Optimization with ANALYTICAL derivatives =====')
x0 = np.array([10, -9])
h0 = np.eye(2) * 0.5
fh, xh, _, _, itct, _, _ = csminwel(rosenbrock, x0, h0, drosenbrock)
print(f'Convergence in {itct} steps')
print(f'Minimal value is {round(fh, 4)}')
print('Optimal point is', xh)
print('\n')


"""
Normalization is used to standardize the intensity values in an image by adjusting the range of
gray level values so that they extend in a desired range of values and improve the contrast of the
image. The main goal of normalization is to reduce the variance of the gray level value along the
ridges to facilitate subsequent processing steps
"""
from math import sqrt
import numpy as np

def normalize_pixel(x, v0, v, m, m0):
    """
    From Handbook of Fingerprint Recognition pg 133
    Normalize job used by Hong, Wan and Jain(1998)
    similar to https://pdfs.semanticscholar.org/6e86/1d0b58bdf7e2e2bb0ecbf274cee6974fe13f.pdf equation 21
    :param x: pixel value
    :param v0: desired variance
    :param v: global image variance
    :param m: global image mean
    :param m0: desired mean
    :return: normilized pixel
    """
    dev_coeff = sqrt((v0 * ((x - m)**2)) / v)
    return m0 + dev_coeff if x > m else m0 - dev_coeff

def normalize(im, m0, v0):
    m = np.mean(im)
    v = np.std(im) ** 2
    (y, x) = im.shape
    normilize_image = im.copy()
    for i in range(x):
        for j in range(y):
            normilize_image[j, i] = normalize_pixel(im[j, i], v0, v, m, m0)

    return normilize_image


import numpy as np

a = input("Enter the choice of array(input a or b or c): ")
if a=='a':
  x=np.array([10,15,20,22.5,30],float)
  y=np.array([227.04,362.78,517.35,602.97,901.67],float)
elif a=='b':
  x=np.array([-1,0,1,2],float)
  y=np.array([3,-4,5,6],float)
elif a=='c':
  x=np.array([1,2,2.5,3,4,4.5,5],float)
  y=np.array([-1.5,-1,0.5,0.25,1,4.5,5],float)
else:
  print("Array Doesn't exist!")

n=len(x)
nn=80

xd=(x[n-1]-x[0])/nn

listx=[]  # creating an empty list for x values.

for j in range(nn+1):
    xn=x[0]+j*xd
    listx.append(xn)
print(listx)
p = float(input("Enter the value of the data point p= "))

f=0

for i in range(n):
     y1=1  ## important  Do not put y1 before defining the range for i,
     # otherwise while evaluating the multiplication,
     #  y1 will continue with the next iteration for i after finishing with one i
     for j in range(n):
        if i!=j:
          y1=y1*(p-x[j])/(x[i]-x[j])

     f+=y[i]*y1

# print(y1)
print("Value of the polynomial is = ", f)


import numpy as np
from figaro.exceptions import FIGAROException

def MC_integral(p, q, n_draws = 1e4, error = True):
    """
    Monte Carlo integration using FIGARO reconstructions.
        ∫p(x)q(x)dx ~ ∑p(x_i)/N with x_i ~ q(x)

    p(x) must have a pdf() method and q(x) must have a rvs() method.
    Lists of p and q are also accepted.

    Arguments:
        :list or class instance p: the probability density to evaluate. Must have a pdf() method.
        :list or class instance q: the probability density to sample from. Must have a rvs() method.
        :int n_draws:              number of MC draws
        :bool error:               whether to return the uncertainty on the integral value or not.

    Return:
        :double: integral value
        :double: uncertainty (if error = True)
    """
    # Check that both p and q are iterables or callables:
    if not ((hasattr(p, 'pdf') or np.iterable(p)) and (hasattr(q, 'rvs') or np.iterable(q))):
        raise FIGAROException("p and q must be list of callables or having pdf/rvs methods")
    # Number of p draws and methods check
    if np.iterable(p):
        if not np.alltrue([hasattr(pi, 'pdf') for pi in p]):
            raise FIGAROException("p must have pdf method")
        n_p = len(p)
        np.random.shuffle(p)
        iter_p = True
    else:
        if not hasattr(p, 'pdf'):
            raise FIGAROException("p must have pdf method")
        iter_p = False
    # Number of q draws and methods check
    if np.iterable(q):
        if not np.alltrue([hasattr(qi, 'rvs') for qi in q]):
            raise FIGAROException("q must have rvs method")
        n_q = len(q)
        np.random.shuffle(q)
        iter_q = True
    else:
        if not hasattr(q, 'rvs'):
            raise FIGAROException("q must have rvs method")
        iter_q = False

    n_draws = int(n_draws)

    # Integrals
    if iter_p and iter_q:
        shortest = np.min([n_p, n_q])
        probabilities = np.array([qi.pdf(pi.rvs(n_draws)) for pi, qi in zip(p[:shortest], q[:shortest])])
    elif iter_p and not iter_q:
        probabilities = np.array([q.pdf(pi.rvs(n_draws)) for pi in p])
    elif iter_q and not iter_p:
        samples = p.rvs(n_draws)
        probabilities = np.array([qi.pdf(samples) for qi in q])
    else:
        probabilities = np.atleast_2d(q.pdf(p.rvs(n_draws)))

    means = probabilities.mean(axis = 1)
    I = means.mean()
    if not error:
        return I
    mc_error = (probabilities.var(axis = 1)/n_draws).mean()
    figaro_error = means.var()/len(means)
    return I, np.sqrt(mc_error + figaro_error)


import numpy as np
import matplotlib.pyplot as plt
def V(x):
	return 0.5*x*x

def numerov(E,plot=0,normalize=0,returnfi=0):
	#lim=5*np.sqrt(2*E)
	xmin = -8.0
	xmax = 8.0
	ndivide = 1000

	s=(xmax-xmin)/(ndivide-1)

	G = np.zeros(ndivide,dtype=np.dtype('f16'))
	f = np.zeros(ndivide,dtype=np.dtype('f16'))
	x = np.linspace(xmin, xmax, num=ndivide,dtype=np.dtype('f16'))

	nn = 0 #number of nodes

	#assign initial values of phi
	f[0] = 0
	f[1] = 0.0001

	G[0] = 2*V(x[0]) - 2*E
	G[1] = 2*V(x[1]) - 2*E

	for i in range(2, ndivide):
		G[i] = 2*V(x[i]) - 2*E
		f[i] = (-f[i-2] + 2*f[i-1] + 5.0*G[i-1]*f[i-1]*s*s/6.0 + G[i-2]*f[i-2]*s*s/12.0)/(1-G[i]*s*s/12.0)
		if (f[i]*f[i-1]) < 0.0:
			nn+=1

	#print('E = %.10f Phimax = %.5f nn = %d'%(E,f[i],nn))
	if normalize:
		I=np.trapz(x=x,y=f**2)
		f=f/np.sqrt(I)
	if plot:
		plt.plot(x,f,label='%.10f'%E)
		#plt.savefig('%.10f.png'%E)
		#plt.clf()
	if returnfi:
		return nn,f[i]
	else:
		return nn

#uniform sampling, found limits of nn changes
Es=np.linspace(0,7,num=200,dtype=np.dtype('f16'))
NNs=[numerov(i) for i in Es]
#print(NNs)

Pairs=[]
for i in range(1,len(NNs)):
	if NNs[i]!=NNs[i-1]:
		Pairs.append((Es[i-1],Es[i],NNs[i-1],NNs[i]))

epsilon=0.0001
print('With accuracy of %.10f'%epsilon)
for a,b,nn0,nn1 in Pairs:
	#print(a,b,nn0,nn1)
	x0=a
	x1=b
	_,fxnew=numerov(x0,normalize=1,returnfi=1)
	while(abs(fxnew) > epsilon):
		xnew=0.5*(x0+x1)
		nn_new,fxnew=numerov(xnew,normalize=1,returnfi=1)
		print(xnew,nn_new,fxnew)
		if (nn_new == nn0):
			x0 = xnew
		elif (nn_new == nn1):
			x1 = xnew
	print('Found eigenenergy at %.10f, nu = %d'%(x0,nn0))
	numerov(x0,plot=1,normalize=1)

plt.legend()
plt.xlabel(r'$x_r$')
plt.title('First several wavefunctions')
plt.savefig('sum.png')


"""Variance ratio test"""

# Authors: Thierry Moudiki
#
# License: BSD 3

import numpy as np
from scipy.stats import f

# inspired from R's var.test
def var_test(x, y, ratio=1, alternative="less", level=95):

    level = level / 100

    assert alternative in (
        "twosided",
        "less",
        "greater",
    ), "must have `alternative` in ('twosided', 'less', 'greater')"

    dfx = len(x) - 1
    dfy = len(y) - 1

    assert dfx >= 1, "not enough observations for `x`"

    assert dfy >= 1, "not enough observations for `y`"

    varx = np.var(x)
    vary = np.var(y)

    estimate = varx / vary
    statistic = estimate / ratio
    parameter = [dfx, dfy]

    pval = f.cdf(statistic, dfx, dfy)

    if alternative == "twosided":
        pval = 2 * min(pval, 1 - pval)
        beta = (1 - level) / 2
        cint = [
            estimate / f.ppf(1 - beta, dfx, dfy),
            estimate / f.ppf(beta, dfx, dfy),
        ]

    if alternative == "greater":
        pval = 1 - pval
        cint = [estimate / f.ppf(level, dfx, dfy), np.infty]

    if alternative == "less":
        cint = [0, estimate / f.ppf(1 - level, dfx, dfy)]

    return {
        "statistic": statistic,
        "parameter": parameter,
        "p_value": pval,
        "conf_int": cint,
        "estimate": estimate,
        "null_value": ratio,
        "alternative": alternative,
    }


from sympy import Symbol, Matrix, Eq, MatAdd, MatMul, preview, S, zeros

def get_symbols_dict(s: str) -> dict:
    return {i: Symbol(i) for i in s.split(' ')}

def create_endos_v(endos: dict) -> Matrix:
    """
    Вектор ЭНДОГЕННЫХ переменных
    """
    return Matrix([[i] for i in endos.values()])

def create_predeters_v(pseudos: list, predeters: dict) -> Matrix:
    """
    Вектор ПРЕДОПРЕДЕЛЕННЫХ (и мнимых) переменных
    """
    return Matrix([[i] for i in [*pseudos, *predeters.values()]])

def calc_M(A: Matrix, B: Matrix) -> Matrix:  # noqa
    """
    M = -A ** (-1) * B
    """
    return -A.inv() * B

def get_reduced_form(X: Matrix, Y: Matrix, A: Matrix, M: Matrix, U: Matrix) -> Eq:  # noqa
    if zeros(*U.shape) == U:
        res = MatMul(M, X)
    else:
        res = MatAdd(MatMul(M, X), MatMul(A.inv(), U))
    return Eq(Y, res, evaluate=False)

def save_components(path: str, **kwargs):
    for k, v in kwargs.items():
        eq = Eq(S(k), v, evaluate=False)
        preview(eq, output='png', viewer='file', filename=f'{path}/{k}.png', euler=False)

def save_reduced_matrix(reduced_form, path: str, dvioptions=None):
    preview(
        reduced_form,
        output='png',
        viewer='file',
        filename=f'{path}/0_reduced_form_matrix.png',
        euler=False,
        dvioptions=dvioptions
    )

def save_reduced_system(reduced_form, path: str, dvioptions=None):
    preview(
        reduced_form,
        output='png',
        viewer='file',
        filename=f'{path}/0_reduced_form_system.png',
        euler=False,
        dvioptions=dvioptions
    )


r"""
Linear Regression
=================

This example shows how to use the :py:class:`pylops.LinearRegression` operator
to perform *Linear regression analysis*.

In short, linear regression is the problem of finding the best fitting
coefficients, namely intercept :math:`\mathbf{x_0}` and gradient
:math:`\mathbf{x_1}`, for this equation:

    .. math::
        y_i = x_0 + x_1 t_i   \qquad \forall i=1,2,...,N

As we can express this problem in a matrix form:

    .. math::
        \mathbf{y}=  \mathbf{A} \mathbf{x}

our solution can be obtained by solving the following optimization problem:

    .. math::
        J= ||\mathbf{y} - \mathbf{A} \mathbf{x}||_2

See documentation of :py:class:`pylops.LinearRegression` for more detailed
definition of the forward problem.
"""
import numpy as np
import matplotlib.pyplot as plt

import pylops

plt.close('all')
np.random.seed(10)

###############################################################################
# Define the input parameters: number of samples along the t-axis (``N``),
# linear regression coefficients (``x``), and standard deviation of noise
# to be added to data (``sigma``).
N = 30
x = np.array([1., 2.])
sigma = 1

###############################################################################
# Let's create the time axis and initialize the
# :py:class:`pylops.LinearRegression` operator
t = np.arange(N, dtype='float64')
LRop = pylops.LinearRegression(t, dtype='float64')

###############################################################################
# We can then apply the operator in forward mode to compute our data points
# along the x-axis (``y``). We will also generate some random gaussian noise
# and create a noisy version of the data (``yn``).
y = LRop*x
yn = y + np.random.normal(0, sigma, N)

###############################################################################
# We are now ready to solve our problem. As we are using an operator from the
# :py:class:`pylops.LinearOperator` family, we can simply use ``/``,
# which in this case will solve the system by means of an iterative solver
# (i.e., :py:func:`scipy.sparse.linalg.lsqr`).
xest = LRop / y
xnest = LRop / yn

###############################################################################
# Let's plot the best fitting line for the case of noise free and noisy data
plt.figure(figsize=(5, 7))
plt.plot(np.array([t.min(), t.max()]),
         np.array([t.min(), t.max()]) * x[1] + x[0], 'k', lw=4,
         label=r'true: $x_0$ = %.2f, $x_1$ = %.2f' % (x[0], x[1]))
plt.plot(np.array([t.min(), t.max()]),
         np.array([t.min(), t.max()]) * xest[1] + xest[0], '--r', lw=4,
         label=r'est noise-free: $x_0$ = %.2f, $x_1$ = %.2f'
         %(xest[0], xest[1]))
plt.plot(np.array([t.min(), t.max()]),
         np.array([t.min(), t.max()]) * xnest[1] + xnest[0], '--g', lw=4,
         label=r'est noisy: $x_0$ = %.2f, $x_1$ = %.2f'
         %(xnest[0], xnest[1]))
plt.scatter(t, y, c='r', s=70)
plt.scatter(t, yn, c='g', s=70)
plt.legend()

###############################################################################
# Once that we have estimated the best fitting coefficients :math:`\mathbf{x}`
# we can now use them to compute the *y values* for a different set of values
# along the *t-axis*.
t1 = np.linspace(-N, N, 2*N, dtype='float64')
y1 = LRop.apply(t1, xest)

plt.figure(figsize=(5, 7))
plt.plot(t, y, 'k', label='Original axis')
plt.plot(t1, y1, 'r', label='New axis')
plt.scatter(t, y, c='k', s=70)
plt.scatter(t1, y1, c='r', s=40)
plt.legend()

###############################################################################
# We consider now the case where some of the observations have large errors.
# Such elements are generally referred to as *outliers* and can affect the
# quality of the least-squares solution if not treated with care. In this
# example we will see how using a L1 solver such as
# :py:func:`pylops.optimization.sparsity.IRLS` can drammatically improve the
# quality of the estimation of intercept and gradient.

# Add outliers
yn[1] += 40
yn[N-2] -= 20

# IRLS
nouter = 20
epsR = 1e-2
epsI = 0
tolIRLS = 1e-2

xnest = LRop / yn
xirls, nouter, xirls_hist, rw_hist = \
    pylops.optimization.sparsity.IRLS(LRop, yn, nouter, threshR=False,
                                      epsR=epsR, epsI=epsI,
                                      tolIRLS=tolIRLS, returnhistory=True)
print('IRLS converged at %d iterations...' % nouter)

plt.figure(figsize=(5, 7))
plt.plot(np.array([t.min(), t.max()]),
         np.array([t.min(), t.max()]) * x[1] + x[0], 'k', lw=4,
         label=r'true: $x_0$ = %.2f, $x_1$ = %.2f' % (x[0], x[1]))
plt.plot(np.array([t.min(), t.max()]),
         np.array([t.min(), t.max()]) * xnest[1] + xnest[0], '--r', lw=4,
         label=r'L2: $x_0$ = %.2f, $x_1$ = %.2f' %
         (xnest[0], xnest[1]))
plt.plot(np.array([t.min(), t.max()]),
         np.array([t.min(), t.max()]) * xirls[1] + xirls[0], '--g', lw=4,
         label=r'L1 - IRSL: $x_0$ = %.2f, $x_1$ = %.2f' %
         (xirls[0], xirls[1]))
plt.scatter(t, y, c='r', s=70)
plt.scatter(t, yn, c='g', s=70)
plt.legend()

###############################################################################
# Let's finally take a look at the convergence of IRLS. First we visualize
# the evolution of intercept and gradient

fig, axs = plt.subplots(2, 1, figsize=(8, 10))
fig.suptitle('IRLS evolution', fontsize=14,
             fontweight='bold', y=0.95)
axs[0].plot(xirls_hist[:, 0], xirls_hist[:, 1], '.-k', lw=2, ms=20)
axs[0].scatter(x[0], x[1], c='r', s=70)
axs[0].set_title('Intercept and gradient')
axs[0].grid()
for iiter in range(nouter):
    axs[1].semilogy(rw_hist[iiter],
                    color=(iiter/nouter, iiter/nouter, iiter/nouter),
                    label='iter%d' %iiter)
axs[1].set_title('Weights')
axs[1].legend(loc=5, fontsize='small')
plt.tight_layout()
plt.subplots_adjust(top=0.8)


from sys import argv
from matplotlib.pylab import figure, plot, show, subplot, title, xlabel
from scipy import array, linspace, ndarray
from scipy.interpolate import CubicSpline
from sympy import sympify, SympifyError
from integral_equation import integral_equation
from utils import print_error

def get_limits():
    try:
        a, b = map(float, input('Insert the limits with the format "a, b": ').split(','))
    except ValueError:
        print_error('Error: Incorrect format, the limits should be a numbers and the separator should be a comma ",".')
    return a, b

def get_tabular_form_input():
    try:
        n = int(input('Insert the number of points of the function: '))
    except ValueError:
        print_error('Error: The number of points should be a integer.')
    print('Insert the points with the format "x, y":')
    x_points, y_points = [], []
    for _ in range(n):
        try:
            x, y = map(float, input().split(','))
        except ValueError:
            print_error('Error: Incorrect format, the points should be a numbers and the separator should be a comma ",".')
        x_points.append(x)
        y_points.append(y)
    return x_points, y_points

def get_tabular_form():
    x_points, y_points = get_tabular_form_input()
    function = CubicSpline(x_points, y_points)
    return function

def get_analytical_form(dim2 = False):
    try:
        function = sympify(input('Insert the analytical form of the function: '))
    except SympifyError:
        print_error('Error: It is not a recognizable function')
    if dim2:
        return lambda x, y: function.evalf(subs={'x': x, 'y': y})
    return lambda x: function.evalf(subs={'x': x})

def sample1():
    number_sample = 1
    print(f'Sample {number_sample}:')
    K = lambda x, y: sympify('1 + x * y').evalf(subs={'x': x, 'y': y})
    f = lambda x: sympify('1').evalf(subs={'x': x})
    a = -1
    b = 1
    print(f'K(x, y) = {K}')
    print(f'f(x) = {f}')
    print(f'Interval = [{a}, {b}]')
    solve1, cond1 = integral_equation(K, f, a, b, use_python_libs=True)
    solve2, cond2 = integral_equation(K, f, a, b, use_python_libs=False)
    x = linspace(a, b)
    y1 = solve1(x)
    y2 = solve2(x)
    figure(f'Sample {number_sample}')
    subplot(1, 2, 1)
    plot(x, y1)
    title(f'Sample {number_sample}: Use Python Libraries')
    xlabel(f'Matrix condition: {cond1}')
    subplot(1, 2, 2)
    plot(x, y2)
    title(f'Sample {number_sample}: NO use Python Libraries')
    xlabel(f'Matrix condition: {cond2}')
    show()

def sample2():
    number_sample = 2
    print(f'Sample {number_sample}:')
    K = lambda x, y: sympify('1 + x * y').evalf(subs={'x': x, 'y': y})
    f = lambda x: sympify('2 * x / 3').evalf(subs={'x': x})
    a = -1
    b = 1
    print(f'K(x, y) = {K}')
    print(f'f(x) = {f}')
    print(f'Interval = [{a}, {b}]')
    solve1, cond1 = integral_equation(K, f, a, b, use_python_libs=True)
    solve2, cond2 = integral_equation(K, f, a, b, use_python_libs=False)
    x = linspace(a, b)
    y1 = solve1(x)
    y2 = solve2(x)
    figure(f'Sample {number_sample}')
    subplot(1, 2, 1)
    plot(x, y1)
    title(f'Sample {number_sample}: Use Python Libraries')
    xlabel(f'Matrix condition: {cond1}')
    subplot(1, 2, 2)
    plot(x, y2)
    title(f'Sample {number_sample}: NO use Python Libraries')
    xlabel(f'Matrix condition: {cond2}')
    show()

if __name__ == "__main__":
    try:
        if len(argv) > 1 and argv[1] == 'samples':
            sample1()
            sample2()
            exit()
        option = int(input('Insert 1 if use Python libraries or 2 if no: '))
        if option != 1 and option != 2:
            print_error('Error: The number of option should be 1 or 2.')
        use_python_libs = option == 1
        option = int(input('Insert 1 if the function K is in the form of a table or 2 if it is in analytical form: '))
        if option != 1 and option != 2:
            print_error('Error: The number of option should be 1 or 2.')
        K = get_tabular_form() if option == 1 else get_analytical_form(True)
        option = int(input('Insert 1 if the function f is in the form of a table or 2 if it is in analytical form: '))
        if option != 1 and option != 2:
            print_error('Error: The number of option should be 1 or 2.')
        f = get_tabular_form() if option == 1 else get_analytical_form()
        a, b = get_limits()
        solve, cond = integral_equation(K, f, a, b, use_python_libs=use_python_libs)
        x = linspace(a, b)
        y = solve(x)
        plot(x, y)
        title(f'Matrix condition: {cond}')
        show()
    except ValueError:
        print_error('Error: The number of option should be a integer.')


from sympy import symbols, poly, zeros, simplify
from anmi.T2 import factorizacion_QR

def matriz_krylov(A, x, n_iters=None):
    """Genera una matriz de krylov dada una matriz A y un vector x. Cada columna de la matriz es la iteración i de A^i*x.

    Args:
        A (matriz): Matriz de aplicación
        x (vector): Vector base
        n_iters (int, optional): Número de iteraciones. Por defecto es el número de filas de A + 1 (garantiza que la matriz de krylov tiene una combinación lineal).

    Returns:
        m_krylov: Matriz con las aplicaciones de krylov por columna.
    """
    if n_iters is None:
        n_iters = A.shape[0] + 1

    m_krylov = zeros(A.shape[0], n_iters)
    m_krylov[:, 0] = x

    for i in range(1, n_iters):
        m_krylov[:, i] = A * m_krylov[:, i - 1]

    return simplify(m_krylov)

def sucesion_krylov(A, x):
    """
    La sucesión de krylov viene dada como la expresión (ejemplo para 3x3)
    p(l) = -l³ + a2 * l² + a1 * l + a0
    donde a = [a0, a1, a2] y es la solución del sistema de ecuaciones
    [x.T, Ax.T, Ax².T] * a.T = Ax³.T

    Args:
        A (matriz): Matriz para aplicar Krylov
        x (vector): Vector base

    Returns:
        poli_krylov (poly): Polinomio para la matriz de Krylov.
        a (vector): vector de coeficientes.
    """
    m_krylov = matriz_krylov(A, x)

    a = (m_krylov[:, : A.shape[0]] ** (-1)) * (
        ((-1) ** (A.shape[0] + 1)) * m_krylov[:, -1]
    )
    a = simplify(a)

    lambda_ = symbols("lambda")
    poli_krylov = poly(((-1) ** A.shape[0]) * (lambda_ ** A.shape[0]) + a[0])

    for i in range(1, A.shape[0]):
        poli_krylov += poly(a[i] * (lambda_ ** i))

    return poli_krylov, a

def potencia_iterada(A, x, n_iters, devolver_ultimo=True):
    """Aplica el método de la potencia iterada para calcular el mayor autovalor de la matriz, usando el método de Krylov.

    Args:
        A (matriz): Matriz aplicación
        x (vector): Vector base para el método. Si el vector es autovector dará fallo.
        n_iters (int): Número de iteraciones
        devolver_ultimo (bool, optional): Si True, devuelve el vector de la última iteración. Si False, devuelve todas las iteraciones.

    Returns:
        m_cocientes (matriz, vector): matriz/vector con el número de filas igual al de A, con los cocientes. Los números deberían tender al mayor autovalor de A.
    """

    m_krylov = matriz_krylov(A, x, n_iters=n_iters)

    if devolver_ultimo:
        m_cocientes = zeros(m_krylov.shape[0], 1)
        for row in range(m_cocientes.shape[0]):
            m_cocientes[row, 0] = (
                m_krylov[row, m_krylov.shape[1] - 1]
                / m_krylov[row, m_krylov.shape[1] - 2]
            )

    else:
        m_cocientes = zeros(m_krylov.shape[0], m_krylov.shape[1] - 1)
        for col in range(m_cocientes.shape[1]):
            for row in range(
                m_cocientes.shape[0]
            ):  # No hay element-wise division en sympy
                m_cocientes[row, col] = m_krylov[row, col + 1] / m_krylov[row, col]

    m_cocientes = simplify(m_cocientes)
    return m_cocientes

def metodo_autovals_QR(A, n_iters=3, verbose=True):
    """Aplica el método QR para el cálculo de autovalores de una matriz.

    Args:
        A (matriz): Matriz para el metodo
        n_iters (int, optional): Número de iteraciones. Defaults to 3.
        verbose (bool, optional): Imprime información intermedia.

    Returns:
        dict: "A": lista de valores de A = R*Q en cada iteración, "R" Y "Q": matrices Q y R del método.
    """
    list_A, list_Q, list_R = [], [], []

    # La lista de A tiene los A^{(2)} hasta A^{(k+1)}, porque A^{(1)} = A, y ya lo conocemos
    for i in range(n_iters):
        dict_QR = factorizacion_QR(A, verbose=verbose)
        Q, R = simplify(dict_QR["Q"]), simplify(dict_QR["R"])
        list_Q.append(Q)
        list_R.append(R)

        A = simplify(R * Q).copy()
        list_A.append(A)

    return {"A": list_A, "Q": list_Q, "R": list_R}


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time     : 2018/10/23 14:02
# @Author   : Iydon
# @File     : 5.3.4.py

import numpy as np

def linspace(start, end, num=None, step=None):
    """
    Args:
        num:  numpy.linspace(start, end, num)
        step: start:step:end
    """
    if num:
        sepera = (end-start) / (num-1)
        return [start+i*sepera for i in range(num)]
    elif step:
        length = int((end-start)/step)
        return [start+i*step for i in range(length+1)]
    else:
        return [start, end]

def taylor_method(dy0, start, end, step, **args):
    """
    Approximate the solution of the initial-value
    problem.
    ----------------------------
    Args:
        args: "d%dy", like "d1y", "d2y".
    """
    xs = linspace(start, end, step=step)
    ys = [dy0 for i in xs]
    for i in range(len(xs)-1):
        tmp,pro = 0,1
        for j in range(len(args)):
            tmp += pro * args["d%dy"%(j+1)](xs[i],ys[i])
            pro *= step / (j+2)
        ys[i+1] = ys[i] + step*tmp
    return ys

def modified_euler_method(dy, dy0, start, end, step):
    """
    Approximate the solution of the initial-value
    problem.
    | y' = f(t,y), a<=t<=b
    | y(a) = /alpha
    ----------------------------
    Args:
        dy:    y'.
        dy0:   /alpha.
        start: a.
        end:   b.
        step:  h.
    """
    xs = linspace(start, end, step=step)
    ys = [dy0 for i in xs]
    for i in range(len(xs)-1):
        tmp = ys[i] + step*dy(xs[i],ys[i])
        ys[i+1] = ys[i] + step*(dy(xs[i],ys[i])+dy(xs[i+1],tmp))/2
    return ys

def runge_kutta(dy, dy0, start, end, step, order:int=2):
    """
    Runge-Kutta Methods of Order Two.
    """
    if order not in [1,2,3,4]:
        raise ValueError
    xs = linspace(start, end, step=step)
    ys = [dy0 for i in xs]
    for i in range(len(xs)-1):
        k1 = step * dy(xs[i],ys[i])
        if order<2:
            ys[i+1] = ys[i] + k1
        elif order<3:
            k2 = step * dy(xs[i]+step/2,ys[i]+k1/2)
            ys[i+1] = ys[i] + k2
        elif order<4:
            k2 = step * dy(xs[i]+step/2,ys[i]+k1/2)
            k3 = step * dy(xs[i]+step,ys[i]-k1+2*k2)
            ys[i+1] = ys[i] + (k1+4*k2+k3)/6
        elif order<5:
            k2 = step * dy(xs[i]+step/2,ys[i]+k1/2)
            k3 = step * dy(xs[i]+step/2,ys[i]+k2/2)
            k4 = step * dy(xs[i]+step,ys[i]+k3)
            ys[i+1] = ys[i] + (k1+2*k2+2*k3+k4)/6
    return ys

d1y = lambda t,y: t*np.exp(3*t)-2*y
d2y = lambda t,y: (t+1)*np.exp(3*t)-4*y
result = taylor_method(0, 0, 1, 0.1, d1y=d1y, d2y=d2y)
print(result)

print("*"*ord("*"))

y = lambda t: t*np.exp(3*t)/5 - np.exp(3*t)/25 + np.exp(-2*t)/25
true_val = [y(i) for i in [0, 0.5, 1]]
print(true_val)
true_val = np.array(true_val)

dy = lambda t,y: t*np.exp(3*t) - 2*y
result = modified_euler_method(dy, 0, 0, 1, 0.5)
print(result, sum(abs(true_val-result)))

result = runge_kutta(dy, 0, 0, 1, 0.5, 2)
print(result, sum(abs(true_val-result)))

result = runge_kutta(dy, 0, 0, 1, 0.5, 4)
print(result, sum(abs(true_val-result)))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec  2 14:29:37 2020

Copyright 2020 by Hadrien Montanelli.
"""
# %% Imports.

# Standard library imports:
import numpy as np

# Chebpy imports:
from chebpy.cheb import chebpts, quadwts

# %% Integrate f(x) = exp(x) on [-1,1].

# Function:
f = lambda x: np.exp(x)

# Grid and weights:
n = 12
x = chebpts(n)
w = quadwts(n)

# Compute integral with quadrature:
I = w @ f(x)

# Compute error:
Iex = np.exp(1) - np.exp(-1)
error = np.abs(I - Iex)
print(f'Error: {error:.2e}')

# %% Integrate f(x) = exp(x) on [0,2].

# Function:
f = lambda x: np.exp(x)

# Grid and weights:
n = 12
x = chebpts(n, [0, 2])
w = quadwts(n, [0, 2])

# Compute integral with quadrature:
I = w @ f(x)

# Compute error:
Iex = np.exp(2) - np.exp(0)
error = np.abs(I - Iex)
print(f'Error: {error:.2e}')


import math
import numpy as np

def get_basis_dual(a1, a2):
    '''
    For the lattice
    :param a1, a2: basis vectors
    :return: dual basis vectors; https://en.wikipedia.org/wiki/Reciprocal_lattice
    '''
    R = np.array([[0, 1], [-1, 0]])  # rotation by 90deg
    b1 = 2 * np.pi * (R @ a2) / (a1 @ (R @ a2))
    b2 = 2 * np.pi * (R @ a1) / (a2 @ (R @ a1))
    return b1, b2

def mod2pi(x):
    '''
    fmod(x,y) is not equivalent to (x % y): https://docs.python.org/3/library/math.html and
    is preferred when working with floats
    :return: a value in interval from 0 to 2pi
    '''
    x = math.fmod(x, 2 * np.pi)
    if x < 0:
        x += 2 * np.pi
    return x


<<<<<<< HEAD
import numpy as np

def cross_entropy_error(y,t):
    if y.ndim==1:
        t = t.reshape(1,t.size)
        y = y.reshape(1,y.size)
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size),t]+1e-7))/batch_size

def sum_squares_error(y,t):
    return 0.5*np.sum((y-t)**2)

def numerical_diff(f,x):
    h = 1e-4
    return (f(x+h)-f(x-h))/(2*h)

def func_1(x):
    return 0.01*x**2 + 0.1*x

def numerical_gradient(f,x):
    h = 1e-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val+h
        fxh1 = f(x)

        x[idx] = tmp_val -h
        fxh2 = f(x)

        grad[idx]= (fxh1-fxh2) / (2*h)
        x[idx] = tmp_val
    return grad

def gradient_descent(f,init_x, lr = 0.01, step_num = 100):
    x = init_x

    for i in range(step_num):
        grad = numerical_gradient(f,x)
        x -=lr*grad
    return x
=======
import numpy as np

def cross_entropy_error(y,t):
    if y.ndim==1:
        t = t.reshape(1,t.size)
        y = y.reshape(1,y.size)
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size),t]+1e-7))/batch_size

def sum_squares_error(y,t):
    return 0.5*np.sum((y-t)**2)

def numerical_diff(f,x):
    h = 1e-4
    return (f(x+h)-f(x-h))/(2*h)

def func_1(x):
    return 0.01*x**2 + 0.1*x

def numerical_gradient(f,x):
    h = 1e-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val+h
        fxh1 = f(x)

        x[idx] = tmp_val -h
        fxh2 = f(x)

        grad[idx]= (fxh1-fxh2) / (2*h)
        x[idx] = tmp_val
    return grad

def gradient_descent(f,init_x, lr = 0.01, step_num = 100):
    x = init_x

    for i in range(step_num):
        grad = numerical_gradient(f,x)
        x -=lr*grad
    return x
>>>>>>> e8dc90c85b9519d31ca84678bd32c29f74538635


#-*- coding:utf-8 -*-
'''
Normal Distribution, also called Gaussian Distribution
'''

import numpy as np
import matplotlib.pyplot as plt

def simple_plot():
    x = np.linspace(0, 10, 10000)
    y = np.random.normal(0, x)
    z = np.cos(x**2)

    plt.figure(figsize = (8, 4))
    plt.plot(x, y, label = "sin(x)", color = "red", linewidth = 2)
    plt.plot(x, z, "b--", label = "cos(x^2)")
    plt.xlabel("Time(s)")
    plt.ylabel("Volt")
    plt.title("PyPlot First Example")
    plt.ylim(-1.2, 1.2)
    plt.legend()
    plt.show()

def normal_plot(mu = 0, sigma = 1, num = 1000):
    y = np.random.normal(mu, sigma, num)

    plt.figure(figsize = (8, 4))
    plt.plot(y, label = "norm", color = "red", linewidth = 2)
    plt.xlabel("X")
    plt.ylabel("Distribution")
    plt.title("Normal-Distribution")
    plt.legend()
    plt.show()

def normal_distribution(mu = 0, sigma = 1, start = None, end = None, num = 1000):
    if start and end:
        x = np.linspace(start, end, num)
    else:
        x = np.linspace(mu - 5 * sigma, mu + 5 * sigma, num)
    # gaussian density function
    y = np.e ** ((x - mu)**2 / (-2 * sigma ** 2)) / np.sqrt(2 * np.pi * sigma)

    plt.figure(figsize = (8, 4))
    plt.plot(x, y, color = "red", linewidth = 2)
    plt.xlabel("X")
    plt.ylabel("density")
    plt.title("Normal-Distribution")
    plt.show()

#simple_plot()
#normal_plot()
normal_distribution()


#######################################################    De la clase S3C1 #############################################################################
import numpy as np
import matplotlib.pylab as plt

# Escriba aca su funcion GENERAL de eliminacion Gaussiana para resolver el sistema Ax=B.

def GJ(A,B):
    NA=len(A)
    for c in range(NA):
        for r in range(NA):
            if(r==c):
                B[r]=B[r]/A[r,c]
                A[r,:]=A[r,:]/A[r,c]
            if(r>c):
                B[r]=B[r]-(B[c]*A[r,c])
                A[r,:]=A[r,:]-(A[c,:]*A[r,c])

    for c in range(NA):
        for r in range(NA):
            if(r<c):
                B[r]=B[r]-(B[c]*A[r,c])
                A[r,:]=A[r,:]-(A[c,:]*A[r,c])
    return B

########################################################################################################################################################
#######################################################   S3C2 minimos cuadrados     ###################################################################
########################################################################################################################################################
# Ejercicio: Minimos cuadrados (https://github.com/ComputoCienciasUniandes/MetodosComputacionales/blob/master/secciones/05.SistemasEcuaciones/lin_algebra.pdf)

# Almacene los datos de parabolico.dat
datos=np.genfromtxt('parabolico.dat')

# Grafique los datos. Guarde la grafica sin mostrarla en plotdatos.pdf
plt.figure()
plt.scatter(datos[:,0],datos[:,1],s=3,label='Datos')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Datos')
plt.legend()
plt.grid()
plt.savefig('plotdatos.pdf')
plt.close()

# teniendo en cuenta que el modelo que usted queiere ajustar a los datos es de la forma: xt=m1+m2*t+m3*0.5*t*t+ruido
# use los datos t y xt, implemente un esquema de minimos cuadrados para encontrar los mejores valores de los parametros m1, m2, m3. (ver explicación en el tablero)
t=datos[:,0]
xt=datos[:,1]
unos=np.ones(len(t))

AT=np.matrix([unos,t,(t**2)/2])   # La matriz creada de por sí es la Matriz transpuesta
A=AT.transpose()                # Se transpone la transpuesta para crear la matriz normal de tamaño N x 3
B=np.copy(xt)

Anew=np.dot(AT,A)
Bnew=np.dot(AT,B)
Bnew=Bnew.transpose()           #se voltea para que quede una matriz 3x1 y pueda entrar en la eliminación gaussiana

Bsol=GJ(Anew,Bnew)

sol=np.dot(A,Bsol)

# Grafique los datos y el ajuste con los parametros obtenidos. Guarde la grafica sin mostrarla en plotAjustedatos.pdf

plt.figure()
plt.scatter(datos[:,0],datos[:,1],s=3,c='b',label='Datos')
plt.plot(t,sol,c='r',label='Función')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Función Ajustada')
plt.legend()
plt.grid()
plt.savefig('plotAjustados.pdf')
plt.close()
########################################################################################################################################################
#######################################################   S3C3 Preparacion PCA       ###################################################################
########################################################################################################################################################
## Ejercicio casa:
# Los arrays `u` y `v` representan dos series en funcion del tiempo `t`.
# 1) Grafique las dos series de datos en una misma imagen y guarde dicha grafica sn mostrarla en 'serie.pdf'
# 2) Calcule la covarianza entre `u` y `v` e imprima su valor.
# 3) Calcule la varianza de `u` e imprima su valor.
# 4) Imprima un mensaje donde explique que puede inferir del valor de covarianza obtenido.
import numpy as np
t = np.array([0.,0.1,0.2,0.3,0.4,0.5,0.6, 0.8, 0.9])
u = np.array([-12.,-45.,-6.,-78.,-34.,-22.,10.,-31.,27.])
v = np.array([3.,11.,1.3,37.,11.,6.,-23.,7.,7.])

# si alcanzan:
# 5) Calcule la matriz de covarianza de los datos anteriores.
# Lectura preparatorias de PCA: https://github.com/ComputoCienciasUniandes/MetodosComputacionales/blob/master/secciones/05.SistemasEcuaciones/lin_algebra.pdf
# https://towardsdatascience.com/a-one-stop-shop-for-principal-component-analysis-5582fb7e0a9c


"""
Sample from posterior distribution using Markov Chain Monte Carlo.

Summary:

    Begin with parameter guess θ.

    1. Generate proposal θ′ with distribution Q(θ′ | θ)
    2. Accept this proposal with probability A(θ′, θ)
    3. Record current parameter

    Records are samples from posterior.
"""

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

def proposal(x, sigma=0.5):
    """
    Propose new parameters.
    """

    x_next = stats.norm(x, sigma).rvs(size=x.shape)

    return x_next

def log_likelihood(data, mu, sigma=1):
    """
    Calculate the log likelihood of some data assuming a normal distribution.
    """

    log_prob = np.log(stats.norm(mu, sigma).pdf(data)).sum()

    return log_prob

def log_prior(parameter, mu=0, sigma=1):
    """
    Calculate the log probability of some parameter assuming a normal prior distribution on that parameter.
    """

    log_probs = np.log(stats.norm(mu, sigma).pdf(parameter))

    return log_probs

def metropolis_hastings(data):
    """
    Calculate the posterior distribution for parameters given some data.
    """

    x_current = proposal(np.ones(1))
    ll_current = log_likelihood(data, x_current)
    log_prior_current = log_prior(x_current)

    posteriors = [x_current]

    for i in range(1100):

        x_proposal = proposal(x_current)
        ll_proposal = log_likelihood(data, x_proposal)
        log_prior_proposal = log_prior(x_proposal)

        accept = np.log(np.random.rand()) < ll_proposal + log_prior_proposal - ll_current - log_prior_current

        if accept:
            x_current, ll_current = x_proposal, ll_proposal

        posteriors.append(x_current)

    return posteriors

if __name__ == '__main__':

    np.random.seed(0)

    data = np.random.randn(1000) + 0.5

    print(data.mean(), data.std())

    posteriors = metropolis_hastings(data)

    plt.hist([x[0] for x in posteriors[100:]])
    plt.show()


import numpy as np
from sigmoid import sigmoid_
from vec_log_gradient import vec_log_gradient_
# Test n.1
x = np.array([1, 4.2])
# x[0] represent the intercept
y_true = 1
theta = np.array([0.5, -0.5])
y_pred = sigmoid_(np.dot(x, theta))
print(vec_log_gradient_(x, y_pred, y_true))
# [0.83201839 3.49447722]# Test n.2x = np.array([1, -0.5, 2.3, -1.5, 3.2]) # x[0] represent the intercepty_true = 0theta = np.array([0.5, -0.5, 1.2, -1.2, 2.3])y_pred = sigmoid_(np.dot(x, theta))print(vec_log_gradient_(x, y_true, y_pred))# [ 0.99999686 -0.49999843 2.29999277 -1.49999528 3.19998994]# Test n.3x_new = np.arange(2, 14).reshape((3, 4))x_new = np.insert(x_new, 0, 1, axis=1)# first column of x_new are now intercept values initialized to 1y_true = np.array([1, 0, 1])theta = np.array([0.5, -0.5, 1.2, -1.2, 2.3])y_pred = sigmoid_(np.dot(x_new, theta))print(vec_log_gradient_(x_new, y_true, y_pred))# [0.99994451 5.99988885 6.99983336 7.99977787 8.99972238]
x = np.array([1, -0.5, 2.3, -1.5, 3.2])
 # x[0] represent the intercept
y_true = 0
theta = np.array([0.5, -0.5, 1.2, -1.2, 2.3])
y_pred = sigmoid_(np.dot(x, theta))
print(vec_log_gradient_(x, y_true, y_pred))
# [ 0.99999686 -0.49999843 2.29999277 -1.49999528 3.19998994]# Test n.3x_new = np.arange(2, 14).reshape((3, 4))x_new = np.insert(x_new, 0, 1, axis=1)# first column of x_new are now intercept values initialized to 1y_true = np.array([1, 0, 1])theta = np.array([0.5, -0.5, 1.2, -1.2, 2.3])y_pred = sigmoid_(np.dot(x_new, theta))print(vec_log_gradient_(x_new, y_true, y_pred))# [0.99994451 5.99988885 6.99983336 7.99977787 8.99972238]

x_new = np.arange(2, 14).reshape((3, 4))
x_new = np.insert(x_new, 0, 1, axis=1)
# first column of x_new are now intercept values initialized to 1
y_true = np.array([1, 0, 1])
theta = np.array([0.5, -0.5, 1.2, -1.2, 2.3])
y_pred = sigmoid_(np.dot(x_new, theta))
print(vec_log_gradient_(x_new, y_true, y_pred))
# [0.99994451 5.99988885 6.99983336 7.99977787


# x-axis represents the hours of the day
# y-axis represents the speed

# scatter plot

import numpy as np
import matplotlib.pyplot as plt

x = np.array([1,2,3,5,6,7,8,9,10,12,13,14,15,16,18,19,21,22])
y = np.array([100,90,80,60,60,55,60,65,70,70,75,76,78,79,90,99,99,100])

plt.scatter(x, y)
plt.show()

# linear-regression
from scipy import stats

slope, intercept, r, p, std_err = stats.linregress(x, y)

def myfunc(x):
	return slope * x + intercept

mymodel = list(map(myfunc, x))

plt.scatter(x, y)
plt.plot(x, mymodel)
plt.show()

# polynomial_regression

# polynomial model
mymodel = np.poly1d(np.polyfit(x, y, 3))

# start at position 1 and end at position 22
myline = np.linspace(1, 22, 100)

plt.scatter(x, y)
plt.plot(myline, mymodel(myline))

plt.show()

# r-squared

# the relationship to predict

# pip install numpy scipy scikit-learn
from sklearn.metrics import r2_score

mymodel = np.poly1d(np.polyfit(x, y, 3))

print(r2_score(y, mymodel(x)))

# predict the car passing at 17 P.M\

# mymodel = np.poly1d(np.polyfit(x, y, 3))

speed = mymodel(17)
print(speed)

# bad fit
x = np.array([89,43,36,36,95,10,66,34,38,20,26,29,48,64,6,5,36,66,72,40])
y = np.array([21,46,3,35,67,95,53,72,58,10,26,34,90,33,38,20,56,2,47,15])

mymodel = np.poly1d(np.polyfit(x, y, 3))

myline = np.linspace(2, 95, 100)

plt.scatter(x, y)
plt.plot(myline, mymodel(myline))
plt.show()

# r-squared
# from sklearn.metrics import r2_score

print(r2_score(y, mymodel(x)))


import numpy as np
import matplotlib.pyplot as plt

n = 10
domain = np.linspace(0,1,n+1)
h = 1/n
# Build L
L = np.diag((n-1)*[2],0)
L += np.diag((n-2)*[-1],1)
L += np.diag((n-2)*[-1],-1)
L = L/(h**2)

# Printign for Sanitycheck
print(L[0,:])
print(L[1,:])
print(L[-1,:])

# Show the matrix structure
plt.spy(L, marker="o", color='r')
plt.show()

#Calculate the eingenvalues
numerical_eigenvalues = [x for x in np.linalg.eigh(L)[0]]
analyticl_eigenvalues_dc = [(2/h)**2*np.sin(np.pi*i/2/n)**2 for i in range(1,n)]
analyticl_eigenvalues_op = [(np.pi * i)**2 for i in range(1,n)]

#show eigenvalues
plt.scatter([x.real for x in numerical_eigenvalues], [x.imag for x in numerical_eigenvalues], label="numerical discrete")
plt.scatter(analyticl_eigenvalues_dc, (n-1)*[0], label="analytical discrete", marker='x')
plt.scatter(analyticl_eigenvalues_op, (n-1)*[0], label="analytical operator")
plt.grid()
plt.legend()
plt.xlabel("Re")
plt.ylabel("Im")
print(analyticl_eigenvalues_dc)
print(analyticl_eigenvalues_op)
plt.show()

# Plotting the eigenvectors
for i in range(n-1):
    l = ([np.sin((i+1)*np.pi*j) for j in domain])
    plt.plot(domain,l, "+", markersize=20)

# Plotting the eigenfunctions
domain_f = np.linspace(0,1,10*n)
for i in range(1,n):
    print(i)
    plt.plot(domain_f, [np.sin(np.pi*i*x) for x in domain_f])
plt.show()

# Plot the 10th eigenvalue
plt.plot(domain_f, [np.sin(np.pi*10*x) for x in domain_f])
plt.plot(domain, [np.sin(np.pi*10*x) for x in domain], "+", markersize=20)
plt.show()


import numpy as np
from numpy.typing import ArrayLike

# There also is
# <https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.logsumexp.html>,
# but implementation is easy enough
def _logsumexp(x: ArrayLike):
    c = np.max(x)
    return c + np.log(np.sum(np.exp(x - c)))

def mean(x: ArrayLike, p: float = 1) -> np.ndarray:
    """Generalized mean.

    See <https://github.com/numpy/numpy/issues/19341> for the numpy issue.
    """
    x = np.asarray(x)

    n = len(x)
    if p == 1:
        return np.mean(x)
    if p == -np.inf:
        return np.min(np.abs(x))
    elif p == 0:
        # first compute the root, then the product, to avoid numerical difficulties with
        # too small values of prod(x)
        if np.any(x < 0.0):
            raise ValueError("p=0 only works with nonnegative x.")
        return np.prod(np.power(x, 1 / n))
        # alternative:
        # return np.exp(np.mean(np.log(x)))
    elif p == np.inf:
        return np.max(np.abs(x))

    if np.all(x > 0.0):
        # logsumexp trick to avoid overflow for large p
        # only works for positive x though
        return np.exp((_logsumexp(p * np.log(x)) - np.log(n)) / p)

    if not isinstance(p, (int, np.integer)):
        raise ValueError(f"Non-integer p (={p}) only work with nonnegative x.")

    return (np.sum(x**p) / n) ** (1.0 / p)