###APPROXMIATING LEGENDRE POLYNOMIAL EIGENVALS via MATRIX CALCULUS & GRAM-SCHMIDT

#WIKIPEDIA: "Legendre polynomials (named after Adrien-Marie Legendre, who discovered them in 1782) are a system of complete and orthogonal polynomials,
#with a vast number of mathematical properties, and numerous applications. They can be defined in many ways, and the various definitions highlight
#different aspects as well as suggest generalizations and connections to different mathematical structures and physical and numerical applications.

#Package Imports
import numpy as np
import math as m
import matplotlib.pylab as plt
%matplotlib inline

#Legendre Equation done in Python:

#Construct a matrix that approximates the deriv

#Define range and fineness of function
xmin = (-1)
xmax = (1)
nn = 300
dx = (xmax-xmin)/nn
x = np.linspace(xmin,xmax,nn,True)

#Define $d/dx$
d_x = np.zeros([nn,nn],dtype=float)
d_x[0,0]=-1.0/dx
d_x[0,1]=1.0/dx
d_x[-1,-1]=1.0/dx
d_x[-1,-2]=-1.0/dx

for i in range(1,nn-1) :
    d_x[i,i-1]=-0.5/dx
    d_x[i,i+1]=0.5/dx

#Define the function we want to differentiate
fx = np.array([x_i for x_i in x])

#Finally, we compute the derivative
dfx_dx = d_x.dot(fx)

#Verify code still correctly computes deriv of f(x) = x

#Plot, f(x) is red, f'(x) is blue
plt.figure()
plt.title("f(x), df(x)/dx vs x")
plt.ylabel('f(x),df(x)/dx')
plt.xlabel('x')
plt.plot(x,fx,'rs')
plt.plot(x,dfx_dx,'gs')

#Repeat for Various polynomials

#i) f(x) = x^2
fx = [x_i**2 for x_i in x]
dfx_dx = d_x.dot(fx)

#plt.figure()
#plt.ylabel('f(x),df(x)/dx')
#plt.xlabel('x')
#plt.plot(x,fx,'rs')
#plt.plot(x,dfx_dx,'bo')

#ii) f(x) = exp(x)
fx = [np.exp(x_i) for x_i in x]
dfx_dx = d_x.dot(fx)

#plt.figure()
#plt.ylabel('f(x),df(x)/dx')
#plt.xlabel('x')
#plt.plot(x,fx,'rs')
#plt.plot(x,dfx_dx,'bo')

#iii) f(x) = sin(2pix)
fx = np.array([np.sin(2*np.pi*x_i) for x_i in x])
dfx_dx = d_x.dot(fx)

#plt.figure()
#plt.ylabel('f(x),df(x)/dx')
#plt.xlabel('x')
#plt.plot(x,fx,'rs')
#plt.plot(x,dfx_dx,'bo')

# Write down the algebraic expression for a finite diff approx:

#Define $d/dx$
d_x = np.zeros([nn,nn],dtype=float)
d_x[0,0]=-1.0/dx
d_x[0,1]=1.0/dx
d_x[-1,-1]=1.0/dx
d_x[-1,-2]=-1.0/dx

for i in range(1,nn-1) :
    d_x[i,i-1]=-0.5/dx
    d_x[i,i+1]=0.5/dx

#Take derivative of d_x to get d2_x
d2_x = d_x.dot(d_x)

#Check that d2_x takes the 2nd derivative by plotting

#try f(x) = x^2
fx = [x_i**2 for x_i in x]
d2fx_dx = d2_x.dot(fx)
plt.figure()
plt.ylabel('f(x),d^2f(x)/dx^2')
plt.xlabel('x')
plt.xlim(-.98,.98)
plt.plot(x,fx, linewidth = 5, color = 'red')
plt.plot(x,d2fx_dx, linewidth = 5, color = 'green')

#try f(x) = x^3
fx = [x_i**3 for x_i in x]
d2fx_dx = d2_x.dot(fx)
plt.figure()
plt.ylabel('f(x),d^2f(x)/dx^2')
plt.xlabel('x')
plt.plot(x,fx, linewidth = 5, color = 'red')
plt.plot(x,d2fx_dx,linewidth =5, color = 'green' )
plt.xlim(-.98,.98)

#Therefore d2_x works as a second derivative function

#Construct matrices to represent multiplication by functions

#i) (1-x^2)
C1 = np.zeros([nn,nn],dtype = float)
for i in range(0,nn):
    C1[i,i]  = 1 - x[i]**2

#ii) (2x)
C2 = np.zeros([nn,nn], dtype = float)

for i in range(0,nn):
    C2[i,i] = -2*x[i]

#iii) Plot C1 and C2 x f(x) = 1 to check
fx = np.array([1 for x_i in x])
fx = np.zeros([nn,1],dtype = float)
for i in range(0,nn):
    fx[i,0] = 1

C1_fx = C1.dot(fx)
C2_fx = C2.dot(fx)

#Check functions by plotting

#i) (1-x^2)
plt.figure()
plt.title('(1-x^2) vs x')
plt.ylabel('(1-x^2)')
plt.xlabel('x')
plt.plot(x,C1_fx, linewidth = 5, color= 'green')

#ii) (-2x)
plt.figure()
plt.title('-2x vs x')
plt.ylabel('(-2x)')
plt.xlabel('x')
plt.plot(x,C2_fx, linewidth = 5, color = 'red')

Define a matrix L that approximates the Legendre Equation
L =  C1.dot(d2_x) + C2.dot(d_x)

#Check
print(L)
print(L.shape)

#L is a matrix, made up of d_x,d2_x,C1 and C2

#L acting on f(x) = x
fx = np.array([x_i for x_i in x])
L_fx = L.dot(fx)

#transpose of L acting on f(x) = x
fx = np.array([x_i for x_i in x])
L_trans = L.transpose()
L_trans_fx = L_trans.dot(fx)

#plotting
plt.figure()

plt.title('L_fx,L_trans_fx vs x')
plt.ylabel('L_fx')
plt.xlabel('x')
plt.xlim(-0.95,0.95)
plt.ylim(-2,2)
plt.plot(x,L_fx, color= 'green')
plt.plot(x,L_trans_fx, color= 'red')

Compare L_fx and L_trans_fx for various functs

#i) f(x) = x^3
fx = np.array([x_i**3 for x_i in x])
L_fx = L.dot(fx)

#transpose of L acting on f(x) = x^3
L_trans = L.transpose()
L_trans_fx = L_trans.dot(fx)

#plotting
#plt.figure()
#plt.xlim(-.95,.95)
#plt.ylim(-3,3)
#plt.title('L_fx,L_trans_fx vs x')
#plt.ylabel('L_fx')
#plt.xlabel('x')
#plt.plot(x,L_fx, color= 'purple')
#plt.plot(x,L_trans_fx, color= 'yellow')

#ii) f(x) = exp(x)
fx = np.array([np.exp(x_i) for x_i in x])
L_fx = L.dot(fx)

#transpose of L acting on f(x) = exp(x)
L_trans = L.transpose()
L_trans_fx = L_trans.dot(fx)

#plotting
#plt.figure()
#plt.xlim(-.95,.95)
#plt.title('L_fx,L_trans_fx vs x')
#plt.ylabel('L_fx')
#plt.xlabel('x')
#plt.ylim(-3,3)
#plt.plot(x,L_fx, color= 'purple')
#plt.plot(x,L_trans_fx, color= 'yellow')

#iii) f(x) = sin(x)
fx = np.array([np.sin(x_i) for x_i in x])
L_fx = L.dot(fx)

#transpose of L acting on f(x) = sin(x)
L_trans = L.transpose()
L_trans_fx = L_trans.dot(fx)

#plotting
#plt.figure()
#plt.xlim(-.95,.95)
#plt.ylim(-3,3)
#plt.title('L_fx,L_trans_fx vs x')
#plt.ylabel('L_fx')
#plt.xlabel('x')
#plt.plot(x,L_fx, color= 'purple')
#plt.plot(x,L_trans_fx, color= 'yellow')

#Calculating the eigenvalues of L

#import numpy's lin alg package to use for calculations
import numpy.linalg
import scipy.linalg

#i) Calculate eigenvalues of real, symmetric matrix L
e_vals, e_vecs = scipy.linalg.eigh(L)

#ii) Arranging eigenvalues in decreasing order
sorted_evals = sorted(e_vals, reverse = True)
n = np.arange(0,300,1)
n_n = (-1)*n
n_2n = (n+1)*n_n

#plotting
plt.figure()
plt.title('Lambda_n vs n and -n(n+1) vs n')
plt.ylabel('Lambda_n')
plt.xlabel('n')
plt.scatter(n,sorted_evals, color = 'red')
plt.scatter(n,n_2n, color = 'green')

#Finding the eigenvectors of L

#From above:
e_vals, e_vecs = scipy.linalg.eig(L)

#Find poistion of (almost) 0 value in list so as to find corresponding eigenvec

#Convert np array to list and then round in order to use index func to find position of "0" eigenvec"
e_val_list = e_vals.tolist()

#As index(0) doesn't work due to the value being not quite 0, the value had to be found using brute force
#print(e_val_list)
a = e_val_list.index(-7.213923634679477e-13+0j)

#Thus the eigenvec corresponding to the 0 eigenval is
P_0 = e_vecs[a]
#Checking eigenvec
L_P_0 = L.dot(P_0)

#Plotting
plt.figure()
plt.title('L_P_0 vs x ')
plt.ylabel('L_P_0')
plt.xlabel('x')
plt.plot(x,L_P_0, color= 'green')

#Other eigenvectors via Gram-Schmidt

#i) Trial function (DNS):

t_1  = np.array([x_i for x_i in x])

##ii) Putative eigenvector (DNS):|P_1>, via Gram-Schmidt

#Gram-Schmidt projecting |P_0> orthogonally onto the line spanned by t
a = np.inner(P_0,t_1)
b = np.inner(t_1,t_1)
P_1 = t_1*(a*(b))

#iii) Plotting (Submit):

#L acting on P_1
L_P1 = L.dot(P_1)

plt.figure()
plt.title('t_1,P_1 and L_P_1 vs x ')
plt.xlabel('x')
plt.plot(x,t_1, color = 'red')
plt.plot(x,P_1, color = 'blue')
plt.plot(x,L_P1, color= 'g')

#Is P_1 an eigenvector of L?

#For P_1 to be a an eigenvector of L  L|P_1> = lambda|P_1>

#Ideally should find a matrix of one constant (the eigenval)

e_val_1 = P_1/L

#Thus the corresponding e_val is 0.03006337

e_val_1 = 0.03006337

#i) x^2
t_2  = np.array([x_i**2 for x_i in x])

#Gram-Schmidt projecting |P_1> orthogonally onto the line spanned by t_2
a = np.inner(P_0,t_2)
b = np.inner(t_2,t_2)
P_2 = t_2*(a*(b))

#ii) x^3
t_3 = np.array([x_i**3 for x_i in x])

#Gram-Schmidt projecting |P_2> orthogonally onto the line spanned by t_3
a = np.inner(P_0,t_3)
b = np.inner(t_3,t_3)
P_3 = t_3*(a*(b))

#iii) x^3
t_4 = np.array([x_i**4 for x_i in x])

#Gram-Schmidt projecting |P_2> orthogonally onto the line spanned by t_3
a = np.inner(P_0,t_3)
b = np.inner(t_3,t_3)
P_4 = t_3*(a*(b))

#Plotting

#i) |P_2>
L_P_2 = L.dot(P_2)
plt.figure()
plt.title('t_2,P_2 and L_P_2 vs x ')
plt.xlabel('x')
plt.plot(x,t_2, color = 'r')
plt.plot(x,P_2, color = 'b')
plt.plot(x,L_P_2, color= 'g')

#ii) |P_3>
L_P_3 = L.dot(P_3)
plt.figure()
plt.title('t_3,P_3 and L_P_3 vs x ')
plt.xlabel('x')
plt.plot(x,t_3, color = 'r')
plt.plot(x,P_3, color = 'b')
plt.plot(x,L_P_3, color= 'g')

#ii) |P_4>
L_P_4 = L.dot(P_4)
plt.figure()
plt.title('t_4,P_4 and L_P_4 vs x ')
plt.xlabel('x')
plt.plot(x,t_4, color = 'r')
plt.plot(x,P_4, color = 'b')
plt.plot(x,L_P_4, color= 'g')

#Finding Eigenvalues

#i) |P_2>

#For P_2 to be a an eigenvector of L  L|P_2> = lambda|P_2>
#Ideally should find a matrix of one constant (the eigenval)

e_val_2 = P_2/L
#print(e_val_2)

#Thus the corresponding e_val is 0.16272140
e_val_2 = 0.16272140

#ii) |P_3>

#For P_3 to be a an eigenvector of L  L|P_3> = lambda|P_3>
#Ideally should find a matrix of one constant (the eigenval)

e_val_3 = P_3/L
#print(e_val_3)

#Thus the corresponding e_val is 0.01425235
e_val_3 = 0.01425235

#iii) |P_4>
#For P_4 to be a an eigenvector of L  L|P_4> = lambda|P_3>
#Ideally should find a matrix of one constant (the eigenval)

e_val_4 = P_4/L
#print(e_val_4)

#Thus the corresponding e_val is 0.00717384
e_val_4 = 0.00717384


#!/usr/bin/env python3

# Solution to Project Euler problem 81

import numpy as np

def read_file(filename):
    matrix = []
    with open(filename) as file:
        for line in file:
            matrix.append([int(x) for x in line.split(',')])
    return np.asarray(matrix)

def find_min_path(matrix):
    cost_matrix = np.zeros(shape=(len(matrix), len(matrix)))
    cost_matrix[0][0] = matrix[0][0]

    # Start with finding cost of first row and first column
    for i in range(1,len(matrix)):
        cost_matrix[0][i] = cost_matrix[0][i-1] + matrix[0][i]
        cost_matrix[i][0] = cost_matrix[i-1][0] + matrix[i][0]

    # Then loop rest of the two matrixes...
    for i in range(1, len(matrix)):
        for j in range(1, len(matrix)):
            cost_matrix[i][j] = min(cost_matrix[i-1][j], cost_matrix[i][j-1]) + matrix[i][j]

    return cost_matrix

def solve():
    matrix = read_file('p081_matrix.txt')
    return int(find_min_path(matrix)[-1][-1])

if __name__ == "__main__":
    print(solve())


# used for manipulating directory paths
import os

# Scientific and vector computation for python
import numpy as np

# Plotting library
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D  # needed to plot 3-D surfaces

def computeCost(X, y, theta):
    """
    Compute cost for linear regression. Computes the cost of using theta as the
    parameter for linear regression to fit the data points in X and y.

    Parameters
    ----------
    X : array_like
        The input dataset of shape (m x n+1), where m is the number of examples,
        and n is the number of features. We assume a vector of one's already
        appended to the features so we have n+1 columns.

    y : array_like
        The values of the function at each data point. This is a vector of
        shape (m, ).

    theta : array_like
        The parameters for the regression function. This is a vector of
        shape (n+1, ).

    Returns
    -------
    J : float
        The value of the regression cost function.

    Instructions
    ------------
    Compute the cost of a particular choice of theta.
    You should set J to the cost.
    """

    # initialize some useful values
    m = y.size  # number of training examples

    # You need to return the following variables correctly
    J = 0

    # Add a column of ones to X. The numpy function stack joins arrays along a given axis.
    # The first axis (axis=0) refers to rows (training examples)
    # and second axis (axis=1) refers to columns (features).
    X = np.stack([np.ones(m), X], axis=1)

    for i in range(0, m):
        J += ((theta[0] + theta[1] * X[i][1]) - y[i]) ** 2
        # for more than one feature I´d simply implement it with a second for loop

    return J * (1/(2 * m))

if __name__ == '__main__':
    data = np.loadtxt(os.path.join("/home/thelichking/Desktop/ml-coursera-python-assignments/Exercise1/Data",
                                   "ex1data1.txt"), delimiter=',')

    data_test = [[], []]
    X, y = data[:, 0], data[:, 1]

    J = computeCost(X, y, theta=np.array([0.0, 0.0]))
    print('With theta = [0, 0] \nCost computed = %.2f' % J)
    print('Expected cost value (approximately) 32.07\n')

    # further testing of the cost function
    J = computeCost(X, y, theta=np.array([-1, 2]))
    print('With theta = [-1, 2]\nCost computed = %.2f' % J)
    print('Expected cost value (approximately) 54.24')


import numpy as np
import cv2

# Y' = 0.299 R + 0.587 G + 0.114 B
def rgb_to_gray(X_rgb):
    X_gray = []
    for sample in X_rgb:
        grayscale = np.dot(sample[...,:3], [0.299, 0.587, 0.114])
        X_gray.append(grayscale)

    # Turn into numpy ararry
    X_gray = np.asarray(X_gray)
    X_gray = np.expand_dims(X_gray, axis=3)
    return X_gray

#(pixel - 128)/ 128 is a quick way to approximately normalize the data and can be used in this project.
def naive_normalization(X):
    return (X - 128)/128

def cv2_gray_n_normalize(img):
    """
    Using the embedded function in open cv to convert rgb into gray
    Using the n-mean(n)/stdev(n)
    """
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    gray = (gray - np.mean(gray)) / (np.std(gray) + 1e-8) # normalization
    gray = np.dstack([gray]).astype(np.float32)
    return gray


"""
Module: libfmp.c6.c6s2_tempo_analysis
Author: Meinard Müller, Angel Villar-Corrales
License: The MIT license, https://opensource.org/licenses/MIT

This file is part of the FMP Notebooks (https://www.audiolabs-erlangen.de/FMP)
"""
import numpy as np
import librosa
from scipy import signal
from scipy.interpolate import interp1d
from matplotlib import pyplot as plt
from numba import jit
import IPython.display as ipd

import libfmp.b
import libfmp.c6

@jit(nopython=True)
def compute_tempogram_fourier(x, Fs, N, H, Theta=np.arange(30, 601, 1)):
    """Compute Fourier-based tempogram [FMP, Section 6.2.2]

    Notebook: C6/C6S2_TempogramFourier.ipynb

    Args:
        x (np.ndarray): Input signal
        Fs (scalar): Sampling rate
        N (int): Window length
        H (int): Hop size
        Theta (np.ndarray): Set of tempi (given in BPM) (Default value = np.arange(30, 601, 1))

    Returns:
        X (np.ndarray): Tempogram
        T_coef (np.ndarray): Time axis (seconds)
        F_coef_BPM (np.ndarray): Tempo axis (BPM)
    """
    win = np.hanning(N)
    N_left = N // 2
    L = x.shape[0]
    L_left = N_left
    L_right = N_left
    L_pad = L + L_left + L_right
    # x_pad = np.pad(x, (L_left, L_right), 'constant')  # doesn't work with jit
    x_pad = np.concatenate((np.zeros(L_left), x, np.zeros(L_right)))
    t_pad = np.arange(L_pad)
    M = int(np.floor(L_pad - N) / H) + 1
    K = len(Theta)
    X = np.zeros((K, M), dtype=np.complex_)

    for k in range(K):
        omega = (Theta[k] / 60) / Fs
        exponential = np.exp(-2 * np.pi * 1j * omega * t_pad)
        x_exp = x_pad * exponential
        for n in range(M):
            t_0 = n * H
            t_1 = t_0 + N
            X[k, n] = np.sum(win * x_exp[t_0:t_1])
        T_coef = np.arange(M) * H / Fs
        F_coef_BPM = Theta
    return X, T_coef, F_coef_BPM

def compute_sinusoid_optimal(c, tempo, n, Fs, N, H):
    """Compute windowed sinusoid with optimal phase

    Notebook: C6/C6S2_TempogramFourier.ipynb

    Args:
        c (complex): Coefficient of tempogram (c=X(k,n))
        tempo (float): Tempo parameter corresponding to c (tempo=F_coef_BPM[k])
        n (int): Frame parameter of c
        Fs (scalar): Sampling rate
        N (int): Window length
        H (int): Hop size

    Returns:
        kernel (np.ndarray): Windowed sinusoid
        t_kernel (np.ndarray): Time axis (samples) of kernel
        t_kernel_sec (np.ndarray): Time axis (seconds) of kernel
    """
    win = np.hanning(N)
    N_left = N // 2
    omega = (tempo / 60) / Fs
    t_0 = n * H
    t_1 = t_0 + N
    phase = - np.angle(c) / (2 * np.pi)
    t_kernel = np.arange(t_0, t_1)
    kernel = win * np.cos(2 * np.pi * (t_kernel*omega - phase))
    t_kernel_sec = (t_kernel - N_left) / Fs
    return kernel, t_kernel, t_kernel_sec

def plot_signal_kernel(x, t_x, kernel, t_kernel, xlim=None, figsize=(8, 2), title=None):
    """Visualize signal and local kernel

    Notebook: C6/C6S2_TempogramFourier.ipynb

    Args:
        x: Signal
        t_x: Time axis of x (given in seconds)
        kernel: Local kernel
        t_kernel: Time axis of kernel (given in seconds)
        xlim: Limits for x-axis (Default value = None)
        figsize: Figure size (Default value = (8, 2))
        title: Title of figure (Default value = None)

    Returns:
        fig: Matplotlib figure handle
    """
    if xlim is None:
        xlim = [t_x[0], t_x[-1]]
    fig = plt.figure(figsize=figsize)
    plt.plot(t_x, x, 'k')
    plt.plot(t_kernel, kernel, 'r')
    plt.title(title)
    plt.xlim(xlim)
    plt.tight_layout()
    return fig

# @jit(nopython=True)  # not possible because of np.correlate with mode='full'
def compute_autocorrelation_local(x, Fs, N, H, norm_sum=True):
    """Compute local autocorrelation [FMP, Section 6.2.3]

    Notebook: C6/C6S2_TempogramAutocorrelation.ipynb

    Args:
        x (np.ndarray): Input signal
        Fs (scalar): Sampling rate
        N (int): Window length
        H (int): Hop size
        norm_sum (bool): Normalizes by the number of summands in local autocorrelation (Default value = True)

    Returns:
        A (np.ndarray): Time-lag representation
        T_coef (np.ndarray): Time axis (seconds)
        F_coef_lag (np.ndarray): Lag axis
    """
    # L = len(x)
    L_left = round(N / 2)
    L_right = L_left
    x_pad = np.concatenate((np.zeros(L_left), x, np.zeros(L_right)))
    L_pad = len(x_pad)
    M = int(np.floor(L_pad - N) / H) + 1
    A = np.zeros((N, M))
    win = np.ones(N)
    if norm_sum is True:
        lag_summand_num = np.arange(N, 0, -1)
    for n in range(M):
        t_0 = n * H
        t_1 = t_0 + N
        x_local = win * x_pad[t_0:t_1]
        r_xx = np.correlate(x_local, x_local, mode='full')
        r_xx = r_xx[N-1:]
        if norm_sum is True:
            r_xx = r_xx / lag_summand_num
        A[:, n] = r_xx
    Fs_A = Fs / H
    T_coef = np.arange(A.shape[1]) / Fs_A
    F_coef_lag = np.arange(N) / Fs
    return A, T_coef, F_coef_lag

def plot_signal_local_lag(x, t_x, local_lag, t_local_lag, lag, xlim=None, figsize=(8, 1.5), title=''):
    """Visualize signal and local lag [FMP, Figure 6.14]

    Notebook: C6/C6S2_TempogramAutocorrelation.ipynb

    Args:
        x: Signal
        t_x: Time axis of x (given in seconds)
        local_lag: Local lag
        t_local_lag: Time axis of kernel (given in seconds)
        lag: Lag (given in seconds)
        xlim: Limits for x-axis (Default value = None)
        figsize: Figure size (Default value = (8, 1.5))
        title: Title of figure (Default value = '')

    Returns:
        fig: Matplotlib figure handle
    """
    if xlim is None:
        xlim = [t_x[0], t_x[-1]]
    fig = plt.figure(figsize=figsize)
    plt.plot(t_x, x, 'k:', linewidth=0.5)
    plt.plot(t_local_lag, local_lag, 'k', linewidth=3.0)
    plt.plot(t_local_lag+lag, local_lag, 'r', linewidth=2)
    plt.title(title)
    plt.ylim([0, 1.1 * np.max(x)])
    plt.xlim(xlim)
    plt.tight_layout()
    return fig

# @jit(nopython=True)
def compute_tempogram_autocorr(x, Fs, N, H, norm_sum=False, Theta=np.arange(30, 601)):
    """Compute autocorrelation-based tempogram

    Notebook: C6/C6S2_TempogramAutocorrelation.ipynb

    Args:
        x (np.ndarray): Input signal
        Fs (scalar): Sampling rate
        N (int): Window length
        H (int): Hop size
        norm_sum (bool): Normalizes by the number of summands in local autocorrelation (Default value = False)
        Theta (np.ndarray): Set of tempi (given in BPM) (Default value = np.arange(30, 601))

    Returns:
        tempogram (np.ndarray): Tempogram tempogram
        T_coef (np.ndarray): Time axis T_coef (seconds)
        F_coef_BPM (np.ndarray): Tempo axis F_coef_BPM (BPM)
        A_cut (np.ndarray): Time-lag representation A_cut (cut according to Theta)
        F_coef_lag_cut (np.ndarray): Lag axis F_coef_lag_cut
    """
    tempo_min = Theta[0]
    tempo_max = Theta[-1]
    lag_min = int(np.ceil(Fs * 60 / tempo_max))
    lag_max = int(np.ceil(Fs * 60 / tempo_min))
    A, T_coef, F_coef_lag = compute_autocorrelation_local(x, Fs, N, H, norm_sum=norm_sum)
    A_cut = A[lag_min:lag_max+1, :]
    F_coef_lag_cut = F_coef_lag[lag_min:lag_max+1]
    F_coef_BPM_cut = 60 / F_coef_lag_cut
    F_coef_BPM = Theta
    tempogram = interp1d(F_coef_BPM_cut, A_cut, kind='linear',
                         axis=0, fill_value='extrapolate')(F_coef_BPM)
    return tempogram, T_coef, F_coef_BPM, A_cut, F_coef_lag_cut

def compute_cyclic_tempogram(tempogram, F_coef_BPM, tempo_ref=30,
                             octave_bin=40, octave_num=4):
    """Compute cyclic tempogram

    Notebook: C6/C6S2_TempogramCyclic.ipynb

    Args:
        tempogram (np.ndarray): Input tempogram
        F_coef_BPM (np.ndarray): Tempo axis (BPM)
        tempo_ref (float): Reference tempo (BPM) (Default value = 30)
        octave_bin (int): Number of bins per tempo octave (Default value = 40)
        octave_num (int): Number of tempo octaves to be considered (Default value = 4)

    Returns:
        tempogram_cyclic (np.ndarray): Cyclic tempogram tempogram_cyclic
        F_coef_scale (np.ndarray): Tempo axis with regard to scaling parameter
        tempogram_log (np.ndarray): Tempogram with logarithmic tempo axis
        F_coef_BPM_log (np.ndarray): Logarithmic tempo axis (BPM)
    """
    F_coef_BPM_log = tempo_ref * np.power(2, np.arange(0, octave_num*octave_bin)/octave_bin)
    F_coef_scale = np.power(2, np.arange(0, octave_bin)/octave_bin)
    tempogram_log = interp1d(F_coef_BPM, tempogram, kind='linear', axis=0, fill_value='extrapolate')(F_coef_BPM_log)
    K = len(F_coef_BPM_log)
    tempogram_cyclic = np.zeros((octave_bin, tempogram.shape[1]))
    for m in np.arange(octave_bin):
        tempogram_cyclic[m, :] = np.mean(tempogram_log[m:K:octave_bin, :], axis=0)
    return tempogram_cyclic, F_coef_scale, tempogram_log, F_coef_BPM_log

def set_yticks_tempogram_cyclic(ax, octave_bin, F_coef_scale, num_tick=5):
    """Set yticks with regard to scaling parmater

    Notebook: C6/C6S2_TempogramCyclic.ipynb

    Args:
        ax (mpl.axes.Axes): Figure axis
        octave_bin (int): Number of bins per tempo octave
        F_coef_scale (np.ndarra): Tempo axis with regard to scaling parameter
        num_tick (int): Number of yticks (Default value = 5)
    """
    yticks = np.arange(0, octave_bin, octave_bin // num_tick)
    ax.set_yticks(yticks)
    ax.set_yticklabels(F_coef_scale[yticks].astype((np.unicode_, 4)))

@jit(nopython=True)
def compute_plp(X, Fs, L, N, H, Theta):
    """Compute windowed sinusoid with optimal phase

    Notebook: C6/C6S3_PredominantLocalPulse.ipynb

    Args:
        X (np.ndarray): Fourier-based (complex-valued) tempogram
        Fs (scalar): Sampling rate
        L (int): Length of novelty curve
        N (int): Window length
        H (int): Hop size
        Theta (np.ndarray): Set of tempi (given in BPM)

    Returns:
        nov_PLP (np.ndarray): PLP function
    """
    win = np.hanning(N)
    N_left = N // 2
    L_left = N_left
    L_right = N_left
    L_pad = L + L_left + L_right
    nov_PLP = np.zeros(L_pad)
    M = X.shape[1]
    tempogram = np.abs(X)
    for n in range(M):
        k = np.argmax(tempogram[:, n])
        tempo = Theta[k]
        omega = (tempo / 60) / Fs
        c = X[k, n]
        phase = - np.angle(c) / (2 * np.pi)
        t_0 = n * H
        t_1 = t_0 + N
        t_kernel = np.arange(t_0, t_1)
        kernel = win * np.cos(2 * np.pi * (t_kernel * omega - phase))
        nov_PLP[t_kernel] = nov_PLP[t_kernel] + kernel
    nov_PLP = nov_PLP[L_left:L_pad-L_right]
    nov_PLP[nov_PLP < 0] = 0
    return nov_PLP

def compute_plot_tempogram_plp(fn_wav, Fs=22050, N=500, H=10, Theta=np.arange(30, 601),
                               title='', figsize=(8, 4), plot_maxtempo=False):
    """Compute and plot Fourier-based tempogram and PLP function

    Notebook: C6/C6S3_PredominantLocalPulse.ipynb

    Args:
        fn_wav: Filename of audio file
        Fs: Sample rate (Default value = 22050)
        N: Window size (Default value = 500)
        H: Hop size (Default value = 10)
        Theta: Set of tempi (given in BPM) (Default value = np.arange(30, 601))
        title: Title of figure (Default value = '')
        figsize: Figure size (Default value = (8, 4))
        plot_maxtempo: Visualize tempo with greatest coefficients in tempogram (Default value = False)
    """
    x, Fs = librosa.load(fn_wav, Fs)

    nov, Fs_nov = libfmp.c6.compute_novelty_spectrum(x, Fs=Fs, N=2048, H=512, gamma=100, M=10, norm=True)
    nov, Fs_nov = libfmp.c6.resample_signal(nov, Fs_in=Fs_nov, Fs_out=100)

    L = len(nov)
    H = 10
    X, T_coef, F_coef_BPM = libfmp.c6.compute_tempogram_fourier(nov, Fs=Fs_nov, N=N, H=H, Theta=Theta)
    nov_PLP = compute_plp(X, Fs_nov, L, N, H, Theta)
    tempogram = np.abs(X)

    fig, ax = plt.subplots(2, 2, gridspec_kw={'width_ratios': [1, 0.05],
                                              'height_ratios': [2, 1]},
                           figsize=figsize)
    libfmp.b.plot_matrix(tempogram, T_coef=T_coef, F_coef=F_coef_BPM, title=title,
                         ax=[ax[0, 0], ax[0, 1]], ylabel='Tempo (BPM)', colorbar=True)
    if plot_maxtempo:
        coef_k = np.argmax(tempogram, axis=0)
        ax[0, 0].plot(T_coef, F_coef_BPM[coef_k], 'r.')

    t_nov = np.arange(nov.shape[0]) / Fs_nov
    peaks, properties = signal.find_peaks(nov_PLP, prominence=0.05)
    peaks_sec = t_nov[peaks]
    libfmp.b.plot_signal(nov_PLP, Fs_nov, color='k', ax=ax[1, 0])
    ax[1, 1].set_axis_off()
    ax[1, 0].plot(peaks_sec, nov_PLP[peaks], 'ro')
    plt.show()
    x_peaks = librosa.clicks(peaks_sec, sr=Fs, click_freq=1000, length=len(x))
    ipd.display(ipd.Audio(x + x_peaks, rate=Fs))


import numpy as np
import math
from utils import variance

x1 = np.array([3 , 13 , 19 , 24 , 29])
mean_x1 = np.mean(x1)
variance_x1 = variance(x1)
print("estimated population variance variance_x1 : " , variance_x1)

x2 = np.array([12 , 10 , 29, 33 , 38])
mean_x2 = np.mean(x2)
variance_x2 = variance(x2)
print("estimated population variance variance_x2 : " , variance_x2)

num_measurements = x1.size
covariance = np.dot(x1 - mean_x1 , x2 - mean_x2) / (num_measurements - 1)
print("covariance  : ",covariance)

correlation = covariance/(math.sqrt(variance_x1) * math.sqrt(variance_x2) )
print("correlation (1 , -1) : ",correlation)


#!/usr/bin/python3

"""functions.py: Various cost and activation functions """

import numpy as np

def cost_ce_prime(A, Y): # Cross Entropy
    return A - Y

def cost_mse(A, Y): # Mean Squared Error
    return (A - Y) / A * (1 - A)

class ActivationReLU:

    def f(self, Z):
        return Z * (Z > 0)

    def f_prime(self, Z):
        return (Z > 0).astype(float)

class ActivationSigmoid:

    def f(self, Z):
        return 1.0 / (1.0 + np.exp(-Z))

    def f_prime(self, Z):
        return self.f(Z) * (1 - self.f(Z))


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

def loaddata():
    with open("set.csv",newline='') as csvfile:
        lines=csv.reader(csvfile,delimiter=",",quoting=csv.QUOTE_NONE, quotechar='')
        for x in lines:
            templist=[]
            for i in range(len(x)-1):
                if x[i]=="Female":
                    x[i]=0
                elif x[i]=="Male":
                    x[i]=1
                templist.append(float(x[i]))
            X_list.append(templist)
            if x[len(x)-1]=="1":
                Y_list.append([0])
            else:
                Y_list.append([1])

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

def initialize(dim):
    w=np.zeros((dim,1),dtype=float)
    b=0
    return w,b

def propagate(w, b, X, Y):
    m = X.shape[1]
    A = sigmoid(np.dot(w.T,X)+b)
    cost = (-1/m)*np.sum(np.dot(Y,np.log(A).T)+np.dot((1-Y),np.log(1-A).T))
    dw = (1/m)*np.dot(X,(A-Y).T)
    db = (1/m)*np.sum(A-Y)
    cost = np.squeeze(cost)
    grads = {"dw": dw,
             "db": db}
    return grads, cost

def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):
    costs = []
    for i in range(num_iterations):
        grads, cost = propagate(w,b,X,Y)
        dw = grads["dw"]
        db = grads["db"]
        w = w-learning_rate*dw
        b = b-learning_rate*db
        if i%100==0:
            costs.append(cost)
        if print_cost and i % 100 == 0:
            print ("Cost after iteration %i: %f" %(i, cost))
    params = {"w": w,
              "b": b}
    grads = {"dw": dw,
             "db": db}
    return params, grads, costs

def predict(w, b, X):
    m = X.shape[1]
    Y_prediction = np.zeros((1,m))
    w = w.reshape(X.shape[0], 1)
    A = sigmoid(np.dot(w.T,X)+b)
    for i in range(A.shape[1]):
        if A[0][i]<=0.75:
            Y_prediction[0][i]=0
        else:
            Y_prediction[0][i]=1
    return Y_prediction

def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
    w, b = initialize(X_train.shape[0])
    parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)
    w = parameters["w"]
    b = parameters["b"]
    Y_prediction_test = predict(w,b,X_test)
    Y_prediction_train = predict(w,b,X_train)
    print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
    print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))
    d = {"costs": costs,
         "Y_prediction_test": Y_prediction_test,
         "Y_prediction_train" : Y_prediction_train,
         "w" : w,
         "b" : b,
         "learning_rate" : learning_rate,
         "num_iterations": num_iterations}
    return d

X_list=[]
Y_list=[]
loaddata()
X=np.array(X_list).T
Y=np.array(Y_list).T
Xmeans=np.mean(X,axis=1).reshape(X.shape[0],1)
Xdevs=np.std(X,axis=1).reshape(X.shape[0],1)
X=(X-Xmeans)/Xdevs
X_trainset=X[:,0:400]
Y_trainset=Y[:,0:400]
X_testset=X[:,400:]
Y_testset=Y[:,400:]
d = model(X_trainset, Y_trainset, X_testset, Y_testset, num_iterations = 2000, learning_rate = 0.005, print_cost = True)
costs = np.squeeze(d['costs'])
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('iterations (per hundreds)')
plt.title("Learning rate =" + str(d["learning_rate"]))
plt.show()


'''
Plots the Mandelbrot set.

Usage:
    mandelbrot.py [--centre=X,Y] [[--height=HEIGHT] [--width=WIDTH] | --size=WxH] [--escape-limit=LIMIT]

Options:
    -c X,Y --centre=X,Y            The centre point of the plot [default: 0,0]
    -W WIDTH --width=WIDTH         The horizontal size of the plot [default: 8]
    -H HEIGHT --height=HEIGHT      The vertical size of the plot in pixels [default: 8]
    -s WxH --size=WxH              Size of the plot (combines W and H)
    -e LIMIT --escape-limit=LIMIT  The maximum escape time for each point on the plot [default: 20]
    -h --help                      Show this help screen
'''
import matplotlib as mpl
mpl.use('Agg')
from matplotlib.pyplot import figure, imshow, savefig
from numpy import linspace, reshape, transpose, shape, zeros, array, complex256
from itertools import product
from docopt import docopt

def  mandelorder(C, limit):
    '''
    For creating a colour plot which represents the escape time of each
    point.
    '''
    Z = C
    I = zeros(shape(Z))-1
    for i in range(limit):
        Z = Z**2+C
        escaped = (abs(Z)>2) & (I==-1)
        I[escaped] = i
        Z[escaped] = 0 # prevents overflows
        C[escaped] = 0
    I[I==-1] = i # didn't escape: max value
    return I

if __name__=='__main__':
    arguments = docopt(__doc__)

    centre = tuple(float(a) for a in arguments["--centre"].split(","))
    if arguments["--size"]:
        size = tuple(float(a) for a in arguments["--size"].split("x"))
    else:
        size = (float(arguments["--width"]),
                float(arguments["--height"]))
    limit = int(arguments["--escape-limit"])

    xstart = centre[0]-size[0]/2
    xend = centre[0]+size[0]/2
    ystart = centre[1]-size[1]/2
    yend = centre[1]+size[1]/2
    X = linspace(xstart, xend, 200)
    Y = linspace(ystart, yend, 200)
    C = array([complex(x,y) for x,y in product(X, Y)], dtype=complex256)
    print("Size: {}".format(size))
    print("C: {}".format(shape(C)))
    mset = mandelorder(C, limit)
    print("Mset: {}".format(shape(mset)))
    mset2d = transpose(reshape(mset, (200, 200)))
    print("Img: {}".format(shape(mset2d)))
    extent = [xstart, xend, ystart, yend]
    figure(figsize=(16,12), dpi=200)
    imshow(mset2d, origin='lower', extent=extent, cmap="gray_r")
    savefig('mandelbrot.png')


import numpy as np

class StandardScaler:
    def __init__(self):
        self.mean_ = None
        self.std_ = None

    def fit(self, X):
        self.mean_ = np.mean(X, axis=0)
        self.std_ = np.std(X, axis=0)

    def transform(self, X):
        """
        output = (X - mean) / std

        """
        mean_X = X - self.mean_
        # std may contain zeros
        output = np.divide(
            mean_X, self.std_,
            out=np.zeros_like(mean_X), where=self.std_ != 0
        )
        return output

class LogisticRegressionAdaGrad:
    def __init__(self, num_epochs, learning_rate):
        self.num_epochs = num_epochs
        self.learning_rate = learning_rate
        self.w = None

    @staticmethod
    def sigmoid(z):
        """
        Calculates the sigmoid activation.

        """
        res = 1 / (1 + np.exp(-z))
        return res
        # write your code here

    def batch_loss(self, X, Y):
        """
        Calculates the averge loss of logistic regression over all training samples.

        """

        sig = self.sigmoid(np.dot(X, self.w))
        return (-Y @ np.log(sig) - (1 - Y) @ np.log(1 - sig)) / Y.size
        # write your code here

    def batch_gradient(self, X, Y):
        """
        Calculates the averge gradients of the weights over all training samples.

        """
        sig = self.sigmoid(np.dot(X, self.w))
        return np.dot((sig - Y), X)
        # write your code here

    def fit(self, X, Y):
        """
        Optimizes the weights using batch gradient descent with momentum (AdaGrad).
        Formula:
            m_{t+1} = m_t + square(grad_t)
            w_{t+1} = w_t - lr / (sqrt(m_{t+1}) + e) * grad_t

        where w_0 = [1, 1, ...], m_0 = [0, 0, ...], and e = 1e-8.

        """
        self.w = np.ones(X.shape[1])
        mom = np.zeros(X.shape[1])
        for i in range(self.num_epochs):
            grad = self.batch_gradient(X,Y)
            mom += np.square(grad)
            self.w -= (self.learning_rate / (np.sqrt(mom) + 1e-8)) * grad

        # write your code here

    def predict_proba(self, X):
        """
        Predicts the probabilities of the output labels.

        """
        y_prob = self.sigmoid(np.dot(X, self.w))
        return y_prob

    def predict(self, X):
        """
        Predicts the binary output labels using threshold 0.5.

        """
        y_pred = self.predict_proba(X)
        y_pred = y_pred > 0.5
        return y_pred

def accuracy_score(pred, y):
    return np.mean(pred == y)

def main():
    n, m, d = input().split()
    d = int(d)
    X_train = []
    X_test = []
    y_train = []
    y_test = []
    for _ in range(int(n)):
        split_line = input().split()
        X = split_line[:d]
        y = split_line[-1]
        X_train.append([int(x) for x in X])
        y_train.append(int(y))
    for _ in range(int(m)):
        split_line = input().split()
        X = split_line[:d]
        y = split_line[-1]
        X_test.append([int(x) for x in X])
        y_test.append(int(y))
    X_train = np.array(X_train)
    X_test = np.array(X_test)
    y_train = np.array(y_train)
    y_test = np.array(y_test)

    scaler = StandardScaler()
    scaler.fit(X_train)  # Don't cheat - fit only on training data
    X_train = scaler.transform(X_train)
    X_test = scaler.transform(X_test)

    clf = LogisticRegressionAdaGrad(learning_rate=0.01, num_epochs=5000)
    clf.fit(X_train, y_train)
    y_pred = clf.predict(X_test)

    print(round(clf.batch_loss(X_train, y_train), 6))
    print(accuracy_score(y_test, y_pred))

if __name__ == '__main__':
    main()


import math
import numpy as np

def calc_mean(data):
    mean = [0, 0]
    for x in data:
        mean[0] += x[0]
        mean[1] += x[1]
    mean[0] /= len(data)
    mean[1] /= len(data)
    return mean

def feature_transform(l, r, fixed_scale=False):
    '''
        Calculate the feature transform from l to r.

        Returns the calculated:
        * scale
        * rotation
        * transition in x
        * transition in y
        * center of transformation
    '''
    if len(r) != len(l):
        print("Invalid dataset, l and r must have the same size!")
        return None, None, None, None, None
    l_mean = np.array(calc_mean(l))
    r_mean = np.array(calc_mean(r))

    cs = 0
    ss = 0
    rr = 0
    ll = 0
    for i in range(len(l)):
        l_i = np.array(l[i]) - l_mean
        r_i = np.array(r[i]) - r_mean

        cs += r_i[0] * l_i[0] + r_i[1] * l_i[1]
        ss += -r_i[0] * l_i[1] + r_i[1] * l_i[0]
        rr += r_i[0] * r_i[0] + r_i[1] * r_i[1]
        ll += l_i[0] * l_i[0] + l_i[1] * l_i[1]

    scale = 1

    if not fixed_scale:
        scale = math.sqrt(rr/ll)

    denom = math.sqrt(cs * cs + ss * ss)
    if abs(denom) < 0.00001:
        print("Numerical instability, denominator is: " + str(denom))
        return 1, 0, 0, 0, l_mean
    c = cs / denom
    s = ss / denom
    theta = math.atan2(s, c)
    tx = r_mean[0] - scale * (c * l_mean[0] - s * l_mean[1])
    ty = r_mean[1] - scale * (s * l_mean[0] + c * l_mean[1])

    return scale, theta, tx, ty, l_mean


import numpy as np

# Root mean squared error
def rmse( y_true, y_pred ):
  rmse = np.sqrt(np.sum(np.square( y_pred - y_true )) / np.size(y_true))
  return rmse

# Mean absolute percentage error
def mape( y_true, y_pred ):
  mape = np.sum(np.abs(( y_pred - y_true ) / y_true))
  mape = mape * 100 / np.size(y_true)
  return mape

# Mean absolute error
def mae( y_true, y_pred ):
  mae = np.sum(np.abs( y_true - y_pred ))
  mae = mae / np.size(y_true)
  return mae

# Pearson's coefficient
def pearson( y_true, y_pred ):
  N = np.size(y_true)
  num = np.sum(y_true * y_pred) * N - np.sum(y_true) * np.sum(y_pred)
  den = (N * np.sum(np.square(y_true))) - np.square(np.sum(y_true))
  den = den * ((N * np.sum(np.square(y_pred))) - np.square(np.sum(y_pred)))
  den = np.sqrt(den)
  if den == 0:
      return 0
  else :
      return (num/den)

# R2 coefficient
def coeff_r2( y_true, y_pred ):
  mu = np.mean(y_true)
  ss_res = np.sum(np.square(y_true - y_pred))
  ss_tot = np.sum(np.square(y_true - mu))
  r2 = 1 - (ss_res / ss_tot)
  return r2


import numpy as np

class TriDiag:
    def __init__(self, a, d, b, n=None):
        if n is None:
            n = len(d)

        self.n = n
        self.a = np.asarray(a)
        self.b = np.asarray(b)
        self.d = np.asarray(d)

    def mult(self, x):
        '''Multiplies tridiagonal matrix by some vector of length n
        '''
        x = np.asarray(x)

        return (self.d * x) + \
            np.concatenate([self.a * x[1:],[0]]) + \
            np.concatenate([[0], self.b * x[:-1]])

    def solve(self, y, verbose=True):
        '''Finds solution to equation Ax=y for x
        verbose: print arrays after solving
        All changes to internal arrays will be reset after solving
        '''
        y = np.asarray(y)

        # Create checkpoint, so that internal arrays can be manipulated
        checkPoint = (self.a, self.b, self.d)

        if verbose:
            print("Matrix before solve:")
            for label, array in [('a',self.a), ('d',self.d), ('b',self.b)]:
                print('{} array:'.format(label), array)
                print()

        # Forward solve
        for i in range(self.n):
            y[i] = y[i] / self.d[i]
            if i < self.n-1:
                self.a[i] = self.a[i] / self.d[i]
                self.d[i+1] = self.d[i+1] - self.b[i] * self.a[i]
                y[i+1] = y[i+1] - self.b[i] * y[i]

        # Backward solve
        k = self.n-1
        for j in range(k):
            i = k-j-1
            y[i] = y[i] - self.a[i] * y[i+1]

        if verbose:
            print("Matrix after solve:")
            for label, array in [('a',self.a), ('d',self.d), ('b',self.b)]:
                print('{} array:'.format(label), array)
                print()

        # Restore the checkpoint
        self.a, self.b, self.d = checkPoint

        return y

# Generate matrix according to specifications in description
def matGen (n):
    d = np.ones(n)*4 + 0.1 * np.arange(n)
    a = np.ones(n-1) + np.arange(n-1)**2 * 0.01
    b = 0.99 * np.ones(n-1) - 0.03 * np.arange(1,n)
    return d,a,b

if __name__ == '__main__':

    # Test this size matrix
    k = 5

    # Generate matrix accordingly
    # We never actually use the full MxM matrix,
    # we just generate the diagonals
    d,a,b = matGen(k)

    # Our wrapper for matrix
    t = TriDiag(a,d,b)

    # Generate arbitrary x
    x = np.arange(k)

    # Pass X through matrix
    y = t.mult(x)

    # Try to solve for the X that generated Ax=y
    print("SOLVING MATRIX")
    print(''.join(['#']*40))
    xp = t.solve(y, verbose=True)

    print(''.join(['#']*40))
    print('Original X:', x)
    print('Solution X:', xp)
    print('Norm of difference:', np.linalg.norm(x-xp))

    # Try to solve for the X that generated Ax=y
    print("SOLVING MATRIX")
    print(''.join(['#']*40))
    xp = t.solve(y, verbose=True)

    print(''.join(['#']*40))
    print('Original X:', x)
    print('Solution X:', xp)
    print('Norm of difference:', np.linalg.norm(x-xp))


#!/usr/bin/env python
"""
A program to demonstrate instability of timestepping methods#
when the timestep is inappropriately choosen.################
"""

from math import exp
import matplotlib.pyplot as plt
import numpy

#Differential equation: y'(t)=-l*y(t) y(t_0)=y_0
#Initial Condition, y(t_0)=1 where t_0=0

# Definition of the Grid
h = 0.1					# Time step size
t0 = 0					# Initial value
tmax = 4					# Value to be computed y(tmax)
Npoints = int((tmax-t0)/h)	# Number of points in the Grid

t = [t0]

# Initial data
l = 0.1
y0 = 1			# Initial condition y(t0)=y0
y_be = [y0]		# Variables holding the value given by the Backward Euler Iteration
y_fe = [y0]		# Variables holding the value given by the Forward Euler Iteration
y_imr = [y0]		# Variables holding the value given by the Midpoint Rule Iteration

for i in xrange(Npoints):
    y_fe.append(y_be[-1]*(1-l*h))
    y_be.append(y_fe[-1]/(1+l*h))
    y_imr.append(y_imr[-1]*(2-l*h)/(2+l*h))
    t.append(t[-1]+h)

print
print "Exact Value:          y(%d)=%f" % (tmax, exp(-4))
print "Backward Euler Value: y(%d)=%f" % (tmax, y_be[-1])
print "Forward Euler Value:  y(%d)=%f" % (tmax, y_fe[-1])
print "Midpoint Rule Value:  y(%d)=%f" % (tmax, y_imr[-1])

# Exact Solution
tt=numpy.arange(0,tmax,0.001)
exact = numpy.exp(-l*tt)

# Plot
plt.figure()
plt.plot(tt,exact,'r-',t,y_fe,'b:',t,y_be,'g--',t,y_imr,'k-.');
plt.xlabel('time')
plt.ylabel('y')
plt.legend(('Exact','Forward Euler','Backward Euler',
		'Implicit Midpoint Rule'))
plt.show()


# Chapter 8

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt

## Review

In [the previous notebook](https://colab.research.google.com/github/AllenDowney/BiteSizeBayes/blob/master/07_euro.ipynb), we used data from a coin-spinning experiment to estimate the probability that a given coin lands on heads.

We tried three different priors: uniform distributions with 101 and 201 values, and a triangle distribution.  The results are similar, which indicates that we have enough data to "swamp the priors".

And we summarized the posterior distributions three ways, computing the value with Maximum Aposteori Probability (MAP), the posterior mean, and a credible interval.

We'll come back to the Euro problem in another notebook, but first we'll work on the World Cup problem.

## The World Cup problem

In the 2018 FIFA World Cup final, France defeated Croatia 4 goals to 2.  Based on this outcome:

1. How confident should we be that France is the better team?

2. If the same teams played again, what is the chance Croatia would win?

To answer these questions, we have to make some modeling decisions.

* First, I'll assume that for any team against any other team there is some unknown goal-scoring rate, measured in goals per game, which I'll denote `λ`.

* Second, I'll assume that a goal is equally likely during any minute of a game.  So, in a 90 minute game, the probability of scoring during any minute is `λ/90`.

* Third, I'll assume that a team never scores twice during the same minute.

Of course, none of these assumptions is absolutely true in the real world, but I think they are reasonable simplifications.

As George Box said, "[All models are wrong; some are useful.](https://en.wikipedia.org/wiki/All_models_are_wrong)"

## Goal-scoring

Given these assumptions, we can simulate the goal-scoring process by tossing 90 imaginary coins, one for each minute of the game, with the same probability of yielding a goal.  Here's a function that does it:

def flip(n, p):
    """Flips n coins and returns the number of heads.

    n: number of coins
    p: probability of heads
    """
    return np.sum(np.random.random(n) < p)

As an example, suppose we know the goal scoring rate is 1.4; we can compute `p` like this:

λ = 1.4
n = 90
p = λ / n

And call `flip` like this:

flip(n, p)

Each time you call flip, you get a different number of goals.  Here's a list of 10 simulated scores:

[flip(n, p) for i in range(10)]

If we simulate many games, we expect the mean to be near 1.4.

sample = [flip(n, p) for i in range(10000)]

np.mean(sample)

This sample contains scores from many simulated games.  The following function computes the number of times each score occurs and stores the result in a Series.

def pmf_from_seq(seq):
    """Make a PMF from a sequence of values.

    seq: sequence

    returns: Series representing a PMF
    """
    pmf = pd.Series(seq).value_counts(sort=False).sort_index()
    pmf /= pmf.sum()
    return pmf

We can use `pmf_from_seq` to compute the PMF of goals.

pmf_goals = pmf_from_seq(sample)
pmf_goals

The PMF represents the distribution of goals scored, that is, each possible outcome and its probability.

In this example, the PMF indicates that the probability of scoring 0 goals is about 27%, and the probability of scoring 1 goal is about 35%.

Here's what the whole distribution looks like:

pmf_goals.plot.bar(color='C1', alpha=0.5)

plt.xlabel('Number of goals')
plt.ylabel('Probability')
plt.title('Distribution of goals scored');

In summary, if we know the goal-scoring rate, `λ`, we can use `flip` to simulate games and estimate the distribution of goals scored.

## The Poisson distribution

The distribution in the previous section is well approximated by the [Poisson distribution](https://en.wikipedia.org/wiki/Poisson_distribution).  If we know that the goal-scoring rate is $λ$, the probability of scoring $k$ goals is

$f(k; \lambda) = \lambda^k exp(-\lambda) ~/~ k!$

SciPy provides a function that computes this expression; we can use it like this:

from scipy.stats import poisson

xs = pmf_goals.index
ys = poisson.pmf(xs, λ)
ys

The following figure compares these values with the results we got by simulation.

pmf_goals.plot.bar(color='C1', alpha=0.5, label='sample')
plt.plot(xs, ys, '_', color='C0', markersize=20,
         label='Poisson distribution')

plt.xlabel('Number of goals')
plt.ylabel('Probability')
plt.title('Distribution of goals scored')
plt.legend();

The results from the Poisson distribution are consistent with the results from simulation.

In summary, given `λ`, we can compute the distribution of goals scored, either by simulation or using a Poisson distribution.

Now let's turn it around: given the number of goals scored in a game, how can we estimate `λ`?

First, we need a prior distribution for `λ`, which represents the range of possible values and their probabilities before we see the score.

Then we use the data to update the distribution.

The result is a posterior distribution that represents our estimate of `λ` after seeing the score.

## The prior

If you have ever seen a soccer game, you have some information about `λ`.  In most games, teams score a few goals each.  In rare cases, a team might score more than 5 goals, but they almost never score more than 10.

Using [data from previous World Cups](https://www.statista.com/statistics/269031/goals-scored-per-game-at-the-fifa-world-cup-since-1930/) I estimate that each team scores about 1.4 goals per game, on average.  So I'll set the mean of `λ` to be 1.4.

For a good team against a bad one, we expect `λ` to be higher; for a bad team against a good one, we expect it to be lower.

To model the distribution of goal-scoring rates, I will use a [gamma distribution](https://en.wikipedia.org/wiki/Gamma_distribution), which I chose because:

1. The goal scoring rate is a continuous quantity that cannot be less than 0, and the gamma distribution is appropriate for this kind of quantity.

2. The gamma distribution has only one parameter, `α`, which is the mean.  So it's easy to construct a gamma distribution with the mean we want.

3. As we'll see, the shape of the Gamma distribution is a reasonable choice, given what we know about soccer.

We can use `scipy.stats.gamma` to compute the values of the gamma distribution, like this:

from scipy.stats import gamma

α = 1.4
qs = np.linspace(0, 10, 101)
ps = gamma.pdf(qs, α)

The parameter, `α`, is the mean of the distribution.

`qs` is an array of possible values for `λ`, from 0 to 10.

`ps` is an array of probabilities, one for each value of `λ`.

Now we can use `qs` and `ps` to make a `Pmf` that represents the prior distribution.

prior = pd.Series(ps, index=qs)
prior /= prior.sum()

I normalized the distribution so the probabilities add up to 1.

We can use the following function, from the previous notebook, to compute the prior mean.

def pmf_mean(pmf):
    """Compute the mean of a PMF.

    pmf: Series representing a PMF

    return: float
    """
    return np.sum(pmf.index * pmf)

pmf_mean(prior)

The prior mean is a little higher than 1.4; that's because we are approximating a continuous distribution with an array of discrete values.

But since the choice of prior is partly arbitrary, we don't have to be too precise.

Here's what it looks like.

prior.plot(label='prior', color='gray')

plt.xlabel('Goal scoring rate')
plt.ylabel('Probability')
plt.title('Prior distribution')
plt.legend();

This distribution represents our prior knowledge about goal scoring: `λ` is usually less than 2, occasionally as high as 6, and seldom higher than that.  And the mean is about 1.4.

As usual, reasonable people could disagree about the details of the prior.  As an exercise, you'll have a chance to try a different prior and see how much effect it has on the results.

But first, it's time to do an update.

## The update

Suppose you are given the goal-scoring rate, $λ$, and asked to compute the probability of scoring a number of goals, $k$.  That is precisely the question we answered by computing the Poisson distribution:

$ f(k; λ) = \lambda^k \exp(-\lambda) ~/~ k! $

For example, if $λ$ is 1.4, the probability of scoring 4 goals in a game is:

k = 4
poisson.pmf(4, λ)

Now suppose we are have an array of possible values for λ, like this:

λs = prior.index

We can compute the likelihood of the data for each hypothetical value of λ, like this:

k = 4
likelihood = poisson.pmf(k, λs)

And that's all we need to do the update.

Here's the function we used in the previous notebook; it takes a prior distribution and an array of likelihoods, does a Bayesian update, and returns the total probability of the data.

def bayes_update(pmf, likelihood):
    """Do a Bayesian update.

    pmf: Series that represents the prior
    likelihood: sequence of likelihoods

    returns: float probability of the data
    """
    pmf *= likelihood
    prob_data = pmf.sum()
    pmf /= prob_data
    return prob_data

In the example, France scored 4 goals, so I'll make a copy of the prior and update it with the likelihoods we just computed.

france = prior.copy()
bayes_update(france, likelihood)

Under the prior distribution, the probability of scoring 4 goals was about 5%.

Here's what the posterior distribution looks like:

prior.plot(label='prior', color='gray')
france.plot(label='France posterior', color='C0')

plt.xlabel('Goal scoring rate')
plt.ylabel('Probability')
plt.title('Posterior distribution for France')
plt.legend();

The data, `k=4`, makes us think higher values of `λ` are more likely and lower values are less likely.  So the posterior distribution is shifted to the right.

Remember that the prior mean was about 1.4; here's the posterior mean:

pmf_mean(prior), pmf_mean(france)

The posterior mean is near 2.7, which is between the prior (1.4) and the data (4).

Having seen 4 goals, we think France's goal-scoring rate is higher than 1.4, but we have not seen enough data to go all the way to 4.

**Exercise:** Here's a prior distribution for Croatia:

croatia = prior.copy()

Update `croatia` with the observed number of goals, `k=2`.  Plot the results and compute the posterior mean.

# Solution goes here

# Solution goes here

# Solution goes here

## Summary

In this notebook, I introduced the World Cup problem, which involves estimating the goal-scoring rate of a soccer team, based on the outcome of a single game.

To solve this problem, I introduced the gamma distribution, which we used to generate a prior, and the Poisson distribution, which we used to compute the likelihood of the data.

We used the prior and the likelihood to do a Bayesian update, which computes posterior distributions that estimate the goal-scoring rate for each team.

[In the next notebook](https://colab.research.google.com/github/AllenDowney/BiteSizeBayes/blob/master/09_predict.ipynb) we will use those distributions to answer the questions we started with:

1. What is the probability that the winning team is actually the better team?

2. What is their probability of winning a rematch?

**Exercise:**  I chose to represent the prior distribution with a gamma distribution because it has some desirable properties, but it is not the only choice.

Another possibility is a normal distribution with the same mean, 1.4 goals per game, and a standard deviation we can choose.

You can evaluate the PDF of the normal distribution like this:

from scipy.stats import norm

mean = 1.4
std = 1.4
qs = np.linspace(0, 10, 101)
ps = norm.pdf(qs, mean, std)

Use these values to construct a normal prior, then update it with France's score, `k=4`.  Plot the posterior distributions based on the gamma and uniform priors.  How different are they?  What effect does the choice of `std`?

# Solution goes here

# Solution goes here

# Solution goes here

# Solution goes here


########################################################################################################################
# LOGISTIC REGRESSION IS A BINARY CLASSIFICATION MODEL SOLVER !
# ...IT IS USED TO PREDICT A BINARY OUTCOME FROM A LINEAR COMBINATION OF VARIABLES (CLASS 0 AND CLASS 1)...
# FIRST STEP: GET THE PROBABILITY OF CLASSIFYING CLASS 1
# SECOND STEP: PREDICT THE CLASS BASED ON PROBABILITY AND THRESHOLDS
# IN ORDER TO GET "BETAS" FOR THE LINEAR COMBINATION WE ARE GOING TO USE THE MAXIMUM LIKELIHOOD FUNCTION:
# ---> WE NEED TO ESTIMATE BETAS TO MAXIMIZE THE LIKELIHOOD...
#
# THE LIKELIHOOD FUNCTION IS THE ONE THAT FOLLOWS:
# LOG(L(BETA)) = LOG( PRODUTTORIA_I_M( P(Xi)**Yi * (1 - P(Xi))**(1 - Yi) ) ) =
#              = SUM_I_M( Yi * LOG(P(Xi)) + (1 - Yi) * LOG( 1- P(Xi) ) )  <---- LOG LIKELIHOOD FUNCTION !
########################################################################################################################

from sklearn.linear_model import LogisticRegression
from tqdm import tqdm
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import random
import os

#dataset = pd.read_csv(os.getcwd() + '/Codes/logistic_regression_dataset.csv', sep=',')

def initalize_params(n):
        beta_0     = 0
        beta_other = [random.random() for _ in range(n)]
        return beta_0, beta_other

def logistic_function_np(point, beta_0, beta_other):

    return 1 / 1 + np.exp( -(beta_0 + point.dot(beta_other)) )

def compute_gradients(x, y, beta_0, beta_other, m, n):

    gradient_beta_0     = 0
    gradient_beta_other = [0] * n

    for i, point in enumerate(x):
        pred = logistic_function_np(point, beta_0, beta_other)
        for j, feature in enumerate(point):
            gradient_beta_other[j] += ((pred - y[i]) * feature) / m # THIS IS THE GRADIENT UPDATES !!!
        gradient_beta_0 += (pred - y[i]) / m

    return gradient_beta_0, gradient_beta_other

def update_params(beta_0, beta_other, gradient_beta_0, gradient_beta_other, learning_rate):

    beta_0 -= gradient_beta_0 * learning_rate
    for i in range(len(beta_other)):
        beta_other[i] -= gradient_beta_other[i] * learning_rate

    return beta_0, beta_other

def logistic_regression(x, y, iterations=250, learning_rate=0.000000001):

    m, n = len(x), len(x[0])
    beta_0, beta_other = initalize_params(n)

    for _ in tqdm(range(iterations)): # Iterations are called epochs...
        gradient_beta_0, gradient_beta_other = compute_gradients(x, y, beta_0, beta_other, m, n)
        beta_0, beta_other = update_params(beta_0, beta_other, gradient_beta_0, gradient_beta_other, learning_rate)

    return beta_0, beta_other

#x      = dataset.drop('target', axis = 1).values
#y      = dataset[['target']].values
#coeff_ = logistic_regression(x, y)
#print(coeff_)

#clf = LogisticRegression(max_iter=1000).fit(x, y)
#print(clf.coef_)


import numpy as np
from pandas import DataFrame

def mean(x:np.ndarray) -> np.ndarray:
	"""Calculates the mean of the given array along the axis 0 of the array
	(i.e. column-wise)

	Parameters:
		x [np.ndarray]: np.ndarray containing the differents features along axis 1.
	Return:
		mean [np.ndarray]: array containing the mean of each feature (column)
	"""
	mean = np.nansum(x, axis=0, keepdims=True) / x.shape[0]
	return mean

def std(x:np.ndarray, mean:np.ndarray) -> np.ndarray:
	"""Calculates the standard deviation of each features.

	Parameters:
		x [np.ndarray]: np.ndarray containing the differents features along axis 1.
		mean [np.ndarray]: numpy array containing the mean of each features.
	Return:
		std [np.ndarray]: array containing the standard deviation of each feature (column)
	"""
	m = x.shape[0]
	std = np.sqrt(np.nansum(np.square(x - mean), axis=0, keepdims=True) / m)
	return std

def min(x:np.ndarray) -> np.ndarray:
	""" Extracts the minimum of each features (column of x)
	Parameters:
		x [np.ndarray]: np.ndarray containing the differents features along axis 1.
	Return:
		v_min [np.ndarray]: array (shape[1, m]) containing the minimum of each features.
	"""
	v_min = x[0]
	for j in range(x.shape[1]):
		for i in range(1, x.shape[0]):
			if v_min[j] > x[i,j]:
				v_min[j] = 0
				v_min[j] += x[i,j]
	return v_min.reshape(1,-1)

def max(x:np.ndarray) -> np.ndarray:
	""" Extracts the aximum of each features (column of x)
	Parameters:
		x [np.ndarray]: np.ndarray containing the differents features along axis 1.
	Return:
		v_max [np.ndarray]: array (shape[1, m]) containing the maximum of each features.
	"""
	v_max = x[0]
	for j in range(x.shape[1]):
		for i in range(1, x.shape[0]):
			if v_max[j] < x[i,j]:
				v_max[j] = 0
				v_max[j] += x[i,j]
	return v_max.reshape(1,-1)

def percentile(x:np.array, p:int) -> np.ndarray:
	""" Extracts the percentile p of each features (column of x)
	Parameters:
		x [np.ndarray]: np.ndarray containing the differents features along axis 1.
	Return:
		v_percent [np.ndarray]: array (shape[1, m]) containing the p percentile of each features.
	"""
	index = int(np.round(0.01 * p * x.shape[0]))
	percentiles =np.sort(x, axis = 0)[index]
	return percentiles.reshape(1,-1)

def standardization(df:DataFrame):
	""" Standardized the array according to the formula:
		[x - mean(x)] / [2 * std(x)]
		Only the pandas Series with dtype as numpy.float32 are transformed.
	Parameters:
		* x [pandas.DataFrame]: dataframe to standardized.
		* mean [np.ndarray / float]: vector of mean values (each component is
			the mean of the corresponding columns in the dataframe)
		* std [np.ndarray / float]: vector of std values (each component is	the
			standard deviation of the corresponding columns in the dataframe)
	"""
	if df.empty:
		str_expt = "Exception: dataframe is empty."
		raise Exception(str_expt)
	col_types = df.dtypes.values
	if all([dtype != np.float32 for dtype in col_types]):
		str_expt = "Exception: all series in dataframe are not np.float32 dtype."
		raise Exception(str_expt)

	col_names = df.dtypes.index.values
	if not all([ctype == np.float32 for ctype in col_types]):
		str_warning = "Warning: At least one Serie in the DataFrame is not a of type np.float32.\n"
		str_warning += "Series which are not of dtype np.float32 will be ignored."
		print(str_warning)

	col_keep = [name for name, ctype in zip(col_names, col_types) if ctype == np.float32]
	v_mean = mean(df[col_keep].values)
	v_std = std(df[col_keep].values, v_mean)
	df.loc[:, col_keep] -= v_mean
	df.loc[:,col_keep] /= (2 * v_std)


import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from celluloid import Camera

# Does all the derv functions and returns the derviative for the ODE solver
# The parameters are passed in through the ODE solver
def derv_combiner(initial_conditions, timegrid, total_pop, beta, gamma):
    sus, infected = initial_conditions[0:2]

    # rate of transmission * susceptible * infected rate
    dSdt = -beta * sus * (infected / total_pop)
    # postive derv_sus * (gamma * infected)
    dIdt = (beta * sus * (infected / total_pop)) - (gamma * infected)
    # positive derv_infected
    dRdt = gamma * infected

    return dSdt, dIdt, dRdt

def plot_sir(time_grid, sus, infected, recovered):
    fig = plt.figure()
    camera = Camera(fig)
    ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
    x_data, sus_data, infected_data, recovered_data = [], [], [], []
    for i in range(len(time_grid)):
        x_data.append(time_grid[i])
        sus_data.append(sus[i])
        infected_data.append(infected[i])
        recovered_data.append(recovered[i])
        ax.plot(x_data, sus_data, "b", alpha=0.5, lw=2, label="Susceptible")
        ax.plot(x_data, infected_data, "r", alpha=0.5, lw=2, label="Infected")
        ax.plot(x_data, recovered_data, "gray", alpha=0.5, lw=2, label="Recovered")
        camera.snap()

    ax.set_xlabel("days")
    ax.set_ylabel("Number of People")

    anim = camera.animate(interval=1)

    # Can change to your desired file location
    f = r"c://Users/briar/Desktop/animation.gif"

    anim.save(f)
    plt.show()

if __name__ == "__main__":
    # Params
    total_pop = 100000
    days = 200
    avg_contacts = 5
    prob_infect = 0.05
    recover_time = 10

    # Rate of transmission
    beta = avg_contacts * prob_infect
    # Rate of recovery
    gamma = 1 / recover_time

    # Each derv is a function of time
    time_grid = np.linspace(0, days, days)

    initial_infect = 100
    initial_recover = 0
    initial_sus = total_pop - (initial_infect + initial_recover)
    # Initial Conditions Tuple
    initial_conditions = (initial_sus, initial_infect, initial_recover)

    # Integrating each derivative over the timegrid
    results = odeint(
        derv_combiner, initial_conditions, time_grid, args=(total_pop, beta, gamma)
    )

    # .T turns the array into 3 rows and packages it to each variable
    # Should be 200 values for each variable, if you set days = 200
    sus, infected, recovered = results.T

    plot_sir(time_grid, sus, infected, recovered)


#!/usr/bin/env python3
import sys
import math
import numpy as np
from functools import reduce

# =*=*=*=* FUNCTION DEFINITIONS *=*=*=*=*=*=*=*=*=*=*=*
# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

def euler_rotation_matrix(theta_z=0, theta_y=0, theta_x=0):
  '''
  Generates a matrix for euler angle rotation:
  * First around z, then around y and finally around x axes.
  * Inputs angles are in radians.
  * Returns a (3,3) matrix.
  * Multiplication with vector shape (3,1) or (3,n)
  * The direction of rotation is given by the right-hand rule
  >> M  = euler_rotation_matrix(alpha, beta, gamma)
  >> v  = np.random.random( (3,1) )
  >> vt = np.dot(M, v)
  '''
  T = []
  if( theta_z ):
      cz = math.cos(theta_z)
      sz = math.sin(theta_z)
      T.append(np.array([[cz, -sz, 0.], [sz, cz, 0.], [0., 0., 1.]]))
  if( theta_y ):
      cy = math.cos(theta_y)
      sy = math.sin(theta_y)
      T.append(np.array([[cy, 0., sy], [0., 1., 0.], [-sy, 0., cy]]))
  if( theta_x ):
      cx = math.cos(theta_x)
      sx = math.sin(theta_x)
      T.append(np.array([[1., 0., 0.], [0., cx, -sx], [0., sx, cx]]))
  if T:
      return reduce(np.dot, T[::-1])
  return np.eye(3)

# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

def rotation_by_euler_angles( v , ea ):
  '''
  v: input array whose shape should be (3,n)
  ea: euler angles, whose shape should be (3,), that is, len(ea) == 3.
  '''
  debug = True

  if( len(ea) != 3 ):
    sys.exit(' Error! Wrong length of euler angles data. Exiting ...')
  if( np.shape(v)[0] != 3 ):
    sys.exit(' Error! Vector array is not of shape (3,n), but {}. Exiting ...'.format(np.shape(v)))

  # Obtain the transformation matrix T and perform matrix vector product.
  Tx = euler_rotation_matrix( ea[0], ea[1], ea[2] )
  if(debug): print(' T = {} '.format(Tx))

  return np.dot(Tx,v)

# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

def rotateRasterCoordNE( Rne , theta ):
  T = np.array([ [np.cos(theta), np.sin(theta)] , [-np.sin(theta), np.cos(theta)] ] )
  return np.matmul( T, Rne )

# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*


from math import sqrt

import numpy as np
import pandas as pd

pd.set_option('display.float_format', lambda x: '%.5f' % x)  # pandas
pd.set_option('display.max_columns', 100)
pd.set_option('display.max_rows', 100)
pd.set_option('display.width', 600)

import statsmodels.formula.api as smf

sim_data = r'https://storage.googleapis.com/applied-economics/simulated_data.csv'

df_sim_data = pd.read_csv(sim_data, header=0, index_col=0, parse_dates=True).reset_index()

df_sim_data_est = df_sim_data[102:301]
df_sim_data_fore = df_sim_data[302:401]

"""

# Y, X, and scatter plot
aes(x, y)

ggplot(sim.data$est, aes(x = 102:301, y = y)) + geom_line() +  theme_bw() + xlab('') + ylab('') + ggtitle('Y')
ggplot(sim.data$est, aes(x = 102:301, y = x)) + geom_line() +  theme_bw() + xlab('') + ylab('') + ggtitle('X')
ggplot(sim.data$est, aes(x = x, y = y)) + geom_point() +  theme_bw() + xlab('X') + ylab('Y')
"""

df_sim_data[['x', 'y']].plot.scatter(x='x', y='y')

df_sim_data[['x']].plot()
df_sim_data[['y']].plot()

## OLS
"""
ols.fit <- lm(y ~ x, data = sim.data$est)
print(xtable(ols.fit), floating = F) # LaTeX output
"""

ols_fit = smf.ols(formula='y ~ x', data=df_sim_data_est).fit()
print(ols_fit.summary())

# Forecasts
"""
yhat <- list()
yhat$y <- predict(ols.fit, newdata = sim.data$fore)
yhat$se <- sqrt(sum(ols.fit$residuals^2) / 198)
yhat$y.up <- yhat$y + 1.96 * yhat$se
yhat$y.low <- - 1.96 * yhat$se
"""

yhat = pd.DataFrame()
yhat['y_hat'] = ols_fit.predict(df_sim_data_fore)
yhat['se'] = sqrt(np.sum(ols_fit.resid ** 2) / 198)
yhat['up'] = yhat['y_hat'] + 1.96 * yhat['se']
yhat['low'] = yhat['y_hat'] - 1.96 * yhat['se']

yhat.plot()
# Plot - yhat1 / yhat1_up / yhat1_low
"""
yhat$y.rec <- yhat$y.recse <- rep(0, 100)

for (i in 1:100) {
  ols.rec <- lm(y ~ x, data = sim.data$full[102:(300 + i)])
  yhat$y.rec[i] <- predict(ols.rec, newdata = sim.data$full[301 + i])
  yhat$y.recse[i] <- sqrt(sum(ols.rec$residuals^2) / (197 + i))
}
"""

y_plot = pd.concat([df_sim_data_fore[['y']], yhat[['y_hat']]], axis=1)
y_plot.plot()

## Recursive
df_rec = pd.DataFrame(index=range(0, 400), columns=['y_rec', 'y_recse'])

for i in range(1, 100):
    ols_rec = smf.ols(formula='y ~ x', data=df_sim_data[101:(300 + i)]).fit()
    df_rec['y_rec'][i + 300] = round(float(ols_rec.predict(df_sim_data[300 + i:(301 + i)])), 6)
    df_rec['y_recse'][i + 300] = sqrt(np.sum(ols_rec.resid ** 2) / 197 + i)

# Plot - actual & recursive forecasts
df_plot = pd.concat([df_sim_data, df_rec], axis=1)
df_plot[275:399][['y', 'y_rec']].plot()
print(ols_rec.summary())
# define likelihood function

"""

Element 	        Description
Dep. Variable 	    Which variable is the response in the model
Model 	            What model you are using in the fit
Method 	            How the parameters of the model were calculated
No. Observations 	The number of observations (examples)
DF Residuals 	    Degrees of freedom of the residuals. Number of observations - number of parameters
DF Model 	        Number of parameters in the model (not including the constant term if present)

Element 	        Description
R-squared 	        The coefficient of determination. A statistical measure of how well the regression line approximates the real data points
Adj. R-squared 	    The above value adjusted based on the number of observations and the degrees-of-freedom of the residuals
F-statistic 	    A measure how significant the fit is. The mean squared error of the model divided by the mean squared error of the residuals
Prob (F-statistic) 	The probability that you would get the above statistic, given the null hypothesis that they are unrelated
Log-likelihood 	    The log of the likelihood function.
AIC 	            The Akaike Information Criterion. Adjusts the log-likelihood based on the number of observations and the complexity of the model.
BIC 	            The Bayesian Information Criterion. Similar to the AIC, but has a higher penalty for models with more parameters.

Description         The name of the term in the model
coef 	            The estimated value of the coefficient
std err 	        The basic standard error of the estimate of the coefficient. More sophisticated errors are also available.
t 	                The t-statistic value. This is a measure of how statistically significant the coefficient is.
P > |t| 	        P-value that the null-hypothesis that the coefficient = 0 is true. If it is less than the confidence level, often 0.05,
                    it indicates that there is a statistically significant relationship between the term and the response. [95.0% Conf. Interval]
                    The lower and upper values of the 95% confidence interval

Element 	        Description
Skewness 	        A measure of the symmetry of the data about the mean. Normally-distributed errors should be symmetrically distributed about the mean (equal amounts above and below the line).
Kurtosis 	        A measure of the shape of the distribution. Compares the amount of data close to the mean with those far away from the mean (in the tails).
Omnibus 	        D'Angostino's test. It provides a combined statistical test for the presence of skewness and kurtosis.
Prob(Omnibus) 	    The above statistic turned into a probability
Jarque-Bera 	    A different test of the skewness and kurtosis
Prob (JB) 	        The above statistic turned into a probability
Durbin-Watson 	    A test for the presence of autocorrelation (that the errors are not independent.) Often important in time-series analysis
Cond. No 	        A test for multicollinearity (if in a fit with multiple parameters, the parameters are related with each other).

"""

### 1.12.1 Forecasting Euro Area GDP ###

ex2_regress_gdp = r'https://storage.googleapis.com/applied-economics/ex2_regress_gdp.csv'
df_eu_gdp_full = pd.read_csv(ex2_regress_gdp, header=0, index_col=0, parse_dates=True).reset_index()

## Full sample - 1996Q1 to 2013Q2

gdp_formula = ['y ~ ipr + su + pr + sr',
               'y ~ ipr + su + sr',
               'y ~ ipr + su',
               'y ~ ipr + pr + sr']

fit = {}
df_fit = pd.DataFrame(index=range(0, 400), columns=['y_rec', 'y_recse'])

for i, model in enumerate(gdp_formula):
    print(model)
    fit[model] = smf.ols(formula=model, data=df_eu_gdp_full).fit()
    print(fit[model].summary())

## Estimation sample - 1996Q1 to 2006Q4
"""
eu.gdp$est <- eu.gdp$full[1:44]
eu.gdp$fore <- eu.gdp$full[45:70]

gdp.est <- list()
for (model in 1:4) {
  gdp.est[[model]] <- lm(gdp.formula[model], data = eu.gdp$est)
  summary(gdp.est[[model]])
}
## Static and recursive forecasts
gdp.fore <- list()
gdp.rec <- list()
for (model in 1:4) {
  gdp.fore[[model]] <- predict(gdp.est[[model]], newdata = eu.gdp$fore)

  gdp.rec[[model]] <- rep(0, 26)
  for (i in 1:26) {
    print(eu.gdp$full[44 + i])
    ols.rec <- lm(gdp.formula[model], data = eu.gdp$full[1:(43 + i)])
    gdp.rec[[model]][i] <- predict(ols.rec, newdata = eu.gdp$full[44 + i])
  }
}

"""
## Estimation sample - 1996Q1 to 2006Q4

df_eu_gdp_est = df_eu_gdp_full[0:44]
df_eu_gdp_fore = df_eu_gdp_full[44:70]

gdp_est = {}

for i, model in enumerate(gdp_formula):
    gdp_est[model] = smf.ols(formula=model, data=df_eu_gdp_est).fit()
    print(gdp_est[model].summary())

"""
## Static and recursive forecasts
gdp.fore < - list()
gdp.rec < - list()
for (model in 1:4) {
    gdp.fore[[model]] < - predict(gdp.est[[model]], newdata=eu.gdp$fore)

gdp.rec[[model]] < - rep(0, 26)
for (i in 1:26) {
    print(eu.gdp$full[44 + i])
ols.rec < - lm(gdp.formula[model], data=eu.gdp$full[1:(43 + i)])
gdp.rec[[model]][i] < - predict(ols.rec, newdata=eu.gdp$full[44 + i])
}
}

"""

## Static and recursive forecasts

gdp_fore = {}
gdp_rec = {}
df_gdp_rec = pd.DataFrame(index=df_eu_gdp_fore.date, columns=['{}'.format(f) for f in gdp_formula])

for i, model in enumerate(gdp_formula):
    gdp_fore[model] = gdp_est[model].predict(df_eu_gdp_fore)

    gdp_rec[model] = [0] * 26
    for i in range(0, 26):
        ols_rec = smf.ols(formula=model, data=df_eu_gdp_full[0: (44 + i)]).fit()
        df_gdp_rec['{}'.format(model)][df_eu_gdp_full.loc[[44 + i]].date] = float(ols_rec.predict(df_eu_gdp_full.loc[[44 + i]]))

# Plots - actual & forecasts
df_eu_gdp_plot = pd.concat([df_eu_gdp_full.set_index('date'), df_gdp_rec], axis=1)
pred_columns = ['y'] + ['{}'.format(f) for f in gdp_formula]
df_eu_gdp_plot[df_eu_gdp_fore.date.min(): df_eu_gdp_fore.date.max()][pred_columns].plot()

"""
# RMSE & MAE
gdp.rec$Y <- cbind(gdp.rec[[1]], gdp.rec[[2]], gdp.rec[[3]], gdp.rec[[4]])
RMSE <- sqrt(colSums((gdp.rec$Y - eu.gdp$fore[, y])^2) / 26)
MAE <- colSums(abs(gdp.rec$Y - eu.gdp$fore[, y])) / 26
error.mat <- rbind(RMSE, MAE)
"""

# RMSE & MAE
df_RMSE = df_gdp_rec.apply(lambda x: sqrt(((x - df_eu_gdp_fore['y'].values) ** 2).sum() / 26))
df_MSE = df_gdp_rec.apply(lambda x: (x - df_eu_gdp_fore['y'].values).abs().sum() / 26)

df_error = pd.concat([df_RMSE, df_MSE], axis=1)
df_error.columns = ['RMSE', 'MSE']
df_error

### 1.12.2 Forecating US GDP ###
"""
us.gdp <- list()
us.gdp$full <- fread('ex2_regress_gdp_us.csv')
us.gdp$full[, date := as.Date(date, format = '%m/%d/%Y')]
"""

ex2_regress_gdp_us = r'https://storage.googleapis.com/applied-economics/ex2_regress_gdp_us.csv'
df_us_gdp_full = pd.read_csv(ex2_regress_gdp_us, header=0, index_col=0, parse_dates=True).reset_index()

gdp_vars = ['y', 'ipr', 'sr', 'su', 'pr']

df_us_gdp_full[['date'] + gdp_vars].set_index('date').plot()

us_gdp_formula =   ['y ~ ipr + su + pr + sr','y ~ ipr + su + sr','y ~ ipr + su','y ~ ipr + pr + sr']
# Summary

gdp_fit = {}
gdp_rec = {}
df_us_gdp_rec = pd.DataFrame(index=df_us_gdp_full.date, columns=['{}'.format(f) for f in us_gdp_formula])

for i, model in enumerate(us_gdp_formula):
    gdp_fit[model] = smf.ols(formula=model, data=df_us_gdp_full).fit()
    print(gdp_fit[model].summary())

## Estimation sample - 1985Q1 to 2006Q4

df_us_gdp_est = df_us_gdp_full[0:88]
df_us_gdp_fore = df_us_gdp_full[88:116]

us_gdp_est = {}

for i, model in enumerate(us_gdp_formula):
    us_gdp_est[model] = smf.ols(formula=model, data=df_us_gdp_est).fit()
    print(us_gdp_est[model].summary())

## Static and recursive forecasts

us_gdp_fore = {}
us_gdp_rec = {}
df_us_gdp_rec = pd.DataFrame(index=df_us_gdp_fore.date, columns=['{}'.format(f) for f in us_gdp_formula])

for i, model in enumerate(us_gdp_formula):
    us_gdp_fore[model] = us_gdp_est[model].predict(df_us_gdp_fore)
    us_gdp_rec[model] = [0] * 28
    for i in range(0, 28):
        ols_rec = smf.ols(formula=model, data=df_us_gdp_full[0: (88 + i)]).fit()
        df_us_gdp_rec['{}'.format(model)][df_us_gdp_full.loc[[88 + i]].date] = float(ols_rec.predict(df_us_gdp_full.loc[[88 + i]]))

# Plots - actual & forecasts
df_us_gdp_plot = pd.concat([df_us_gdp_full.set_index('date'), df_us_gdp_rec], axis=1)
pred_columns = ['y'] + ['{}'.format(f) for f in us_gdp_formula]
df_us_gdp_plot[df_us_gdp_fore.date.min(): df_us_gdp_fore.date.max()][pred_columns].plot()

### 1.13.1 Forecasting default risk ###
"""
(OAS) Bank of America Merrill Lynch US High Yield Master II Option-Adjusted Spread, denoted OAS, monthly
(VIX) the Chicago Board Options Exchange (CBOE) Volatility Index, denoted VIX
(SENT) Surveys of Consumers, University of Michigan, consumer sentiment index, denoted SENT
(PMI the ISM Manufacturing: purcahsing managers index, PMI
(sp500) the monthly returns, in percentage points, of the S&P 500 Index

"""
ex3_regress_oas = r'https://storage.googleapis.com/applied-economics/ex3_regress_oas.csv'
df_oas = pd.read_csv(ex3_regress_oas, header=0, index_col=0, parse_dates=True).reset_index()
df_oas.set_index("Date").plot()

"""
           Date  OAS   VIX  SENT  PMI       sp500
  1: 1998-01-01 2.87 21.47 106.6 53.8  1.01501768
  2: 1998-02-01 2.83 18.55 110.4 52.9  7.04491930
"""

# shift OAS
df_oas[['OAS']] = df_oas[['OAS']].shift(-1)
df_oas.dropna(inplace=True)

"""
          Date      OAS      VIX      SENT      PMI     sp500
0   1998-01-01  2.87000 21.47000 106.60000 53.80000   1.01502
1   1998-02-01  2.83000 18.55000 110.40000 52.90000   7.04492
"""

yield_formulas = ['OAS ~ VIX', 'OAS ~ SENT', 'OAS ~ PMI', 'OAS ~ sp500', 'OAS ~ VIX + SENT + PMI + sp500']

yield_fit = {}
df_yield = pd.DataFrame(index=df_oas.Date, columns=['{}'.format(f) for f in yield_formulas])

for i, model in enumerate(yield_formulas):
    yield_fit[model] = smf.ols(formula=model, data=df_oas).fit()
    print(yield_fit[model].summary())


import sympy as sp

from .spec import are_different_length
from .error.linear_algebra import InconsistentDataSetError

def dot_product(vec1, vec2, evaluate: bool = True):
    """
    calculate dot product of two list
    containing sympy numbers and symbols without evaluation.

    Args:
        vec1 (list of int or float, or sympy Mul): 1D-list.
            The list length must be equal to it of vec2.
        vec2 (list of int or float, or sympy Mul): 1D-list.
            The list length must be equal to it of vec1.
        evaluate (bool, optional): flag to evaluate the result.
            Defaults to True.

    Raises:
        InconsistentDataSetError: if two lists are inconsistent.

    Returns:
        sympy Expr: dot product of the passed two lists.
    """
    if are_different_length(vec1, vec2):
        raise InconsistentDataSetError(vec1, vec2)
        # raise error if
        # two lists are inconsistent.

    if evaluate:
        eq = sum([vec1[i] * vec2[i] for i in range(len(vec1))])
        # when evaluate = True, a result obtained using sp.Mul and sp.Add
        # with evaluate=True is the same as it
        # obtained using `*` operation and the sum function.
    else:
        begin_ = len(vec2) - 1  # exclude first term
        end_ = -1  # to generate numbers up to 0 using range()
        step_ = -1
        eq = sp.Mul(vec1[begin_], vec2[begin_], evaluate=evaluate)
        for i in range(begin_ + step_, end_, step_):
            eq = sp.Add(
                eq, sp.Mul(vec1[i], vec2[i], evaluate=evaluate), evaluate=evaluate
            )
        # there is no way to accumulate a list of sympy symbols
        # without evaluation.
        # So for-loop and sympy.Add and .Mul are used.
        # The for-loop is downstepped
        # so that the result are sorted when it is printed.
        # The result of the accumulation, `((-a*f_{-1} +b*f_{0}) + a*f_{1})`,
        # is printed as `a*f_{1} + b*f_{0} - a*f_{-1}`.
        # downstepping is used for sorting.

    return eq

def div(numer, denom):
    """
    calculate division of two sympy Expr.
    The result is always evaluated.

    Args:
        numer (sympy Expr): Algebraic expression to be devided
        denom (sympy Expr): Algebraic expression to divide.
            The Expr must be a single term.

    Returns:
        sympy Expr: result of division eq/denom.
    """
    return sp.Mul(numer, 1 / denom)
    # calculate Mul with evaluate=True,
    # because the result with evaluate=False will be like
    # `(1/(12*h))*(f_{-2} - 8*f_{-1} + 0*f_{0} + 8*f_{1} - f_{2})`,
    # unlike the result we want as follows:
    # `(f_{-2} - 8*f_{-1} + 0*f_{0} + 8*f_{1} - f_{2})/(12*h)`


import os as _os
_on_rtd = _os.environ.get('READTHEDOCS', None) == 'True'
if not _on_rtd:
    import numpy as _np

def frexp10(x):
    """
    Finds the mantissa and exponent of a number :math:`x` such that :math:`x = m 10^e`.

    Parameters
    ----------

    x : float
        Number :math:`x` such that :math:`x = m 10^e`.

    Returns
    -------

    mantissa : float
        Number :math:`m` such that :math:`x = m 10^e`.
    exponent : float
        Number :math:`e` such that :math:`x = m 10^e`.
    """
    expon = _np.int(_np.floor(_np.log10(_np.abs(x))))
    mant = x/_np.power(10, expon)
    return (mant, expon)


""" Class defintion for Wiener's attack """

import re
import math
from sympy.solvers import solve
from sympy.core.numbers import Integer
from sympy import Symbol
from RSAExploits.exploits.exploit import Exploit
from RSAExploits.util import compute_priv_rsa_exp

class Wiener(Exploit):
    """ Class providing a run interface to Wiener's attack """

    def run(self, rsadata_list, info_dict = None):
        """ Attempts to recover private exponent using Wiener's attack

            This attack works when the private key d is < N ^ 0.25.

            Args:
                rsadata_list: A list of RSA_Data objects on which to attempt
                              Wiener's attack
                info_dict: Not currently used

            Side Effects:
                If a private key is recovered, the corresponding field in the
                RSA_Data object will be set

            Return:
                True if at least one private key is recovered
        """

        print ("Wiener: Running Attack...")
        success = False

        for rsadata in rsadata_list:
            roots = self.wiener_attack(rsadata.get_n(), rsadata.get_e())

            # If wiener's attack was successful for the current rsaData object
            if roots != None:
                rsadata.set_d(compute_priv_rsa_exp(rsadata.get_e(), roots))
                success = True

        if success:
            print("Wiener: Success, roots found, assigned in rsadata.")
            return True
        else:
            print("Wiener: Failure, roots not found.")
            return False

    @staticmethod
    def conFraction(fraction):
        (a,b) = fraction
        v=[]
        v.append(0)
        while not a == 1:
            (x,y) = fraction
            try:
                r =  y / x
                a = y % x
            except ZeroDivisionError as details:
                print ("Exception occured:",details)
                print("Continuing to next RSA data...")
                return None
            b=x
            fraction = (a,b)
            v.append(r)
        v.append(b)
        return v

    @staticmethod
    def makeIndexedConvergent(sequence, index):
        (a,b)=(1,sequence[index])
        while index>0:
            index-=1
            (a,b)=(b,sequence[index]*b+a)
        return (b,a)

    @staticmethod
    def makeConvergents(sequence):
        r=[]
        for i in xrange(0,len(sequence)):
            r.append(Wiener.makeIndexedConvergent(sequence,i))
        return r

    @staticmethod
    def wiener_attack(N,e):
        v = Wiener.conFraction((e,N))
        if v is None:
            return None
        conv = Wiener.makeConvergents(v)
        for frac in conv:
            (k,d)=frac
            if k == 0:
                continue
            phiN=((e*d)-1)/k
            if ((pow(N-phiN+1, 2) - 4*N) <= 0 ): # (b^2 -4ac) <= 0
                continue

            # solving equation (a**2 + b*x + c)
            x = Symbol('x', integer=True)
            roots = []
            roots = solve(x**2 -(N-phiN+1)*x + N, x, rational=False)

            if len(roots) == 2:
                if type(roots[0]) is Integer and type(roots[0]) is Integer:

                    if(roots[0]*roots[1] == N):
                        return roots


'''
    Author: Mahnoor Anjum
    Description:
        Intersection Test

    Reference:
        www.geeksforgeeks.org

    For a given point triplet (A, B, C),
    we can determine the orientation by
    inspecting at the sign of the cross product of AB × BC

    If AB × BC > 0, counterclockwise.
    If AB × BC < 0, clockwise.
    Otherwise, if AB × BC = 0, collinear.

'''

import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import numpy as np
import cv2

def orientation(a,b,c):

    val = (float(b[1] - a[1]) * (c[0] - b[0])) - \
          (float(b[0] - a[0]) * (c[1] - b[1]))
    if (val > 0):
        return 1 #anticlockwise
    elif (val < 0):
        return 2 #clockwise
    else:
        return 0

def onSegment(a, b, p):
    if ( (b[0] <= max(a[0], p[0])) and (b[0] >= min(a[0], p[0])) and
           (b[1] <= max(a[1], p[1])) and (b[1] >= min(a[1], p[1]))):
        return True
    return False

def check_intersect(a,b,c,d):

    # a b c, a b d, c d a, c d b
    o1 = orientation(a, b, c)
    o2 = orientation(a, b, d)
    o3 = orientation(c, d, a)
    o4 = orientation(c, d, b)

    # General case
    if ((o1 != o2) and (o3 != o4)):
        return 1

    # Special Cases

    # a , b and c are colinear and c lies on segment ab
    if ((o1 == 0) and onSegment(a, c, b)):
        return 1

    # a , b and d are colinear and d lies on segment ab
    if ((o2 == 0) and onSegment(a, d, b)):
        return 1

    # c , d and a are colinear and a lies on segment cd
    if ((o3 == 0) and onSegment(c, a, d)):
        return 1

    # c , d and b are colinear and b lies on segment cd
    if ((o4 == 0) and onSegment(c, b, d)):
        return 1

    # If none of the cases
    return 0

mat = mpimg.imread('data/img.png')

fig, ax = plt.subplots(1,1,figsize=(5,5))
ax.imshow(mat)
x = fig.ginput(4)
plt.close('all')

points = []
for i in x:
    points.append(i)

# x, y = [], []
# for i in points:
#     x.append(i[0])
#     y.append(i[1])

intersect = check_intersect(points[0], points[1],\
                            points[2], points[3])


from sympy import *
from rodrigues_R_utils import *

px, py, pz = symbols('px py pz')
sx, sy, sz = symbols('sx sy sz')
tie_px, tie_py, tie_pz = symbols('tie_px tie_py tie_pz');
cols, rows = symbols('cols rows');
pi = symbols('pi')
u_kp, v_kp = symbols('u_kp v_kp')

position_symbols = [px, py, pz]
rodrigues_symbols = [sx, sy, sz]
tie_point_symbols = [tie_px, tie_py, tie_pz]
all_symbols = position_symbols + rodrigues_symbols + tie_point_symbols

RT_wc = matrix44FromRodrigues(px, py, pz, sx, sy, sz)
r=RT_wc[:-1,:-1]
t=Matrix([px, py, pz]).vec()

pos_w=Matrix([tie_px, tie_py, tie_pz]).vec()
bearing = r * pos_w + t;
norm = sqrt(bearing[0]*bearing[0] + bearing[1]*bearing[1] + bearing[2]*bearing[2])
bearing=bearing/norm
latitude=-asin(bearing[1])
longitude=atan2(bearing[0], bearing[2])

u=cols*(0.5 + longitude / (2.0 * pi))
v=rows*(0.5 - latitude/pi)
u_delta = u_kp - u;
v_delta = v_kp - v;

obs_eq = Matrix([u_delta, v_delta]).vec()
obs_eq_jacobian = obs_eq.jacobian(all_symbols)

print(obs_eq)
print(obs_eq_jacobian)

with open("equirectangular_camera_colinearity_rodrigues_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void observation_equation_equrectangular_camera_colinearity_rodrigues_wc(Eigen::Matrix<double, 2, 1> &delta, double rows, double cols, double pi, double px, double py, double pz, double sx, double sy, double sz, double tie_px, double tie_py, double tie_pz, double u_kp, double v_kp)\n")
    f_cpp.write("{")
    f_cpp.write("delta.coeffRef(0,0) = %s;\n"%(ccode(obs_eq[0,0])))
    f_cpp.write("delta.coeffRef(1,0) = %s;\n"%(ccode(obs_eq[1,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void observation_equation_equrectangular_camera_colinearity_rodrigues_wc_jacobian(Eigen::Matrix<double, 2, 9, Eigen::RowMajor> &j, double rows, double cols, double pi, double px, double py, double pz, double sx, double sy, double sz, double tie_px, double tie_py, double tie_pz, double u_kp, double v_kp)\n")
    f_cpp.write("{")
    for i in range (2):
        for j in range (9):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(obs_eq_jacobian[i,j])))
    f_cpp.write("}")


import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt
import streamlit as st
import seaborn as sns
import streamlit.components.v1 as components

def blackScholes(S, K, r, T, sigma, type="c"):
    "Calculate Black Scholes option price for a call/put"
    d1 = (np.log(S/K) + (r + sigma**2/2)* T)/(sigma*np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    try:
        if type == "c":
            price = S * norm.cdf(d1, 0, 1) - K * np.exp(-r * T) * norm.cdf(d2, 0, 1)
        elif type == "p":
            price = K * np.exp(-r * T) * norm.cdf(-d2, 0, 1) - S * norm.cdf(-d1, 0, 1)

        return price
    except:
        st.sidebar.error("Please confirm all option parameters!")

def optionDelta (S, K, r, T, sigma, type="c"):
    "Calculates option delta"
    d1 = (np.log(S/K) + (r + sigma**2/2)* T)/(sigma*np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    try:
        if type == "c":
            delta = norm.cdf(d1, 0, 1)
        elif type == "p":
            delta = -norm.cdf(-d1, 0, 1)

        return delta
    except:
        st.sidebar.error("Please confirm all option parameters!")

def optionGamma (S, K, r, T, sigma):
    "Calculates option gamma"
    d1 = (np.log(S/K) + (r + sigma**2/2)* T)/(sigma*np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    try:
        gamma = norm.pdf(d1, 0, 1)/ (S * sigma * np.sqrt(T))
        return gamma
    except:
        st.sidebar.error("Please confirm all option parameters!")

def optionTheta(S, K, r, T, sigma, type="c"):
    "Calculates option theta"
    d1 = (np.log(S/K) + (r + sigma**2/2)* T)/(sigma*np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    try:
        if type == "c":
            theta = - ((S * norm.pdf(d1, 0, 1) * sigma) / (2 * np.sqrt(T))) - r * K * np.exp(-r*T) * norm.cdf(d2, 0, 1)

        elif type == "p":
            theta = - ((S * norm.pdf(d1, 0, 1) * sigma) / (2 * np.sqrt(T))) + r * K * np.exp(-r*T) * norm.cdf(-d2, 0, 1)
        return theta/365
    except:
        st.sidebar.error("Please confirm all option parameters!")

def optionVega (S, K, r, T, sigma):
    "Calculates option vega"
    d1 = (np.log(S/K) + (r + sigma**2/2)* T)/(sigma*np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    try:
        vega = S * np.sqrt(T) * norm.pdf(d1, 0, 1) * 0.01
        return vega
    except:
        st.sidebar.error("Please confirm all option parameters!")

def optionRho(S, K, r, T, sigma, type="c"):
    "Calculates option rho"
    d1 = (np.log(S/K) + (r + sigma**2/2)* T)/(sigma*np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)

    try:
        if type == "c":
            rho = 0.01 * K * T * np.exp(-r*T) * norm.cdf(d2, 0, 1)
        elif type == "p":
            rho = 0.01 * -K * T * np.exp(-r*T) * norm.cdf(-d2, 0, 1)
        return rho
    except:
        st.sidebar.error("Please confirm all option parameters!")

st.set_page_config(page_title="Black-Scholes Model")

sidebar_title = st.sidebar.header("Black-Scholes Parameters")
space = st.sidebar.header("")
r = st.sidebar.number_input("Risk-Free Rate", min_value=0.000, max_value=1.000, step=0.001, value=0.030)
S = st.sidebar.number_input("Underlying Asset Price", min_value=1.00, step=0.10, value=30.00)
K = st.sidebar.number_input("Strike Price", min_value=1.00, step=0.10, value=50.00)
days_to_expiry = st.sidebar.number_input("Time to Expiry Date (in days)", min_value=1, step=1, value=250)
sigma = st.sidebar.number_input("Volatility", min_value=0.000, max_value=1.000, step=0.01, value=0.30)
type_input = st.sidebar.selectbox("Option Type",["Call", "Put"])

type=""
if type_input=="Call":
    type = "c"
elif type_input=="Put":
    type = "p"

T = days_to_expiry/365

spot_prices = [i for i in range(0, int(S)+50 + 1)]

prices = [blackScholes(i, K, r, T, sigma, type) for i in spot_prices]
deltas = [optionDelta(i, K, r, T, sigma, type) for i in spot_prices]
gammas = [optionGamma(i, K, r, T, sigma) for i in spot_prices]
thetas = [optionTheta(i, K, r, T, sigma, type) for i in spot_prices]
vegas = [optionVega(i, K, r, T, sigma) for i in spot_prices]
rhos = [optionRho(i, K, r, T, sigma, type) for i in spot_prices]

sns.set_style("whitegrid")

fig1, ax1 = plt.subplots()
sns.lineplot(spot_prices, prices)
ax1.set_ylabel('Option Price')
ax1.set_xlabel("Underlying Asset Price")
ax1.set_title("Option Price")

fig2, ax2 = plt.subplots()
sns.lineplot(spot_prices, deltas)
ax2.set_ylabel('Delta')
ax2.set_xlabel("Underlying Asset Price")
ax2.set_title("Delta")

fig3, ax3 = plt.subplots()
sns.lineplot(spot_prices, gammas)
ax3.set_ylabel('Gamma')
ax3.set_xlabel("Underlying Asset Price")
ax3.set_title("Gamma")

fig4, ax4 = plt.subplots()
sns.lineplot(spot_prices, thetas)
ax4.set_ylabel('Theta')
ax4.set_xlabel("Underlying Asset Price")
ax4.set_title("Theta")

fig5, ax5 = plt.subplots()
sns.lineplot(spot_prices, vegas)
ax5.set_ylabel('Vega')
ax5.set_xlabel("Underlying Asset Price")
ax5.set_title("Vega")

fig6, ax6 = plt.subplots()
sns.lineplot(spot_prices, rhos)
ax6.set_ylabel('Rho')
ax6.set_xlabel("Underlying Asset Price")
ax6.set_title("Rho")

fig1.tight_layout()
fig2.tight_layout()
fig3.tight_layout()
fig4.tight_layout()
fig5.tight_layout()
fig6.tight_layout()

st.markdown("
Black-Scholes Option Price Calculator
", unsafe_allow_html=True)
st.markdown("
Made by Tiago Moreira
", unsafe_allow_html=True)
st.header("")
st.markdown("
See project's description and assumptions here: https://github.com/TFSM00/Black-Scholes-Calculator
", unsafe_allow_html=True)
st.markdown("
See all my other projects here: https://github.com/TFSM00
", unsafe_allow_html=True)
st.header("")
st.markdown("
Option Prices and Greeks
", unsafe_allow_html=True)
st.header("")
col1, col2, col3, col4, col5 = st.columns(5)
col2.metric("Call Price", str(round(blackScholes(S, K, r, T, sigma,type="c"), 3)))
col4.metric("Put Price", str(round(blackScholes(S, K, r, T, sigma,type="p"), 3)))

bcol1, bcol2, bcol3, bcol4, bcol5 = st.columns(5)
bcol1.metric("Delta", str(round(blackScholes(S, K, r, T, sigma,type="c"), 3)))
bcol2.metric("Gamma", str(round(optionGamma(S, K, r, T, sigma), 3)))
bcol3.metric("Theta", str(round(optionTheta(S, K, r, T, sigma,type="c"), 3)))
bcol4.metric("Vega", str(round(optionVega(S, K, r, T, sigma), 3)))
bcol5.metric("Rho", str(round(optionRho(S, K, r, T, sigma,type="c"), 3)))

st.header("")
st.markdown("
Visualization of the Greeks
", unsafe_allow_html=True)
st.header("")
st.pyplot(fig1)
st.pyplot(fig2)
st.pyplot(fig3)
st.pyplot(fig4)
st.pyplot(fig5)
st.pyplot(fig6)


# Bimodal distribution (mixture of two 1d Gaussians)
# Based on https://github.com/probml/pmtk3/blob/master/demos/bimodalDemo.m

import superimport

import numpy as np
import matplotlib.pyplot as plt
import pyprobml_utils as pml

from scipy.stats import norm

# Define two normal distrubutions and their corresponding weights.
mu = [0, 2]
sigma = [1, 0.05]
n = [norm(loc=mu[i], scale=sigma[i]) for i in range(2)]
w = [0.5, 0.5]

# Define a set of x points for graphing.
xs = np.linspace(-2, 2*mu[1], 600)

# Combine the two distributions by their weights, evaluated at the x points.
p = sum(w[i] * n[i].pdf(xs) for i in range(2))

# Calculate the mean of the final distribution.
mean_p = np.mean(xs * p)

# Plot the final distribution and its mean.
linewidth = 3
plt.figure()
plt.plot(xs, p, 'black', linewidth=linewidth)
plt.vlines(mean_p, ymin=0, ymax=max(p), color='red', linewidth=linewidth)
pml.savefig('bimodalSpike.pdf')
plt.show()

# Another example, with two modes
mu = [0, 2]
sigma = [0.5, 0.5]
n = [norm(loc=mu[i], scale=sigma[i]) for i in range(2)]
w = [0.5, 0.5]
xs = np.linspace(-2, 2*mu[1], 600)
p = sum(w[i] * n[i].pdf(xs) for i in range(2))

plt.figure()
linewidth = 3
plt.plot(xs, p, 'black', linewidth=linewidth)
pml.savefig('bimodalDistribution.pdf')
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Fri Mar  1 17:41:58 2019

@author: Ham

HackerRanch Challenge: Sum and Prod

sum

The sum tool returns the sum of array elements over a given axis.

import numpy

my_array = numpy.array([ [1, 2], [3, 4] ])

print numpy.sum(my_array, axis = 0)         #Output : [4 6]
print numpy.sum(my_array, axis = 1)         #Output : [3 7]
print numpy.sum(my_array, axis = None)      #Output : 10
print numpy.sum(my_array)                   #Output : 10
By default, the axis value is None.
Therefore, it performs a sum over all the dimensions of the input array.

prod

The prod tool returns the product of array elements over a given axis.

import numpy

my_array = numpy.array([ [1, 2], [3, 4] ])

print numpy.prod(my_array, axis = 0)            #Output : [3 8]
print numpy.prod(my_array, axis = 1)            #Output : [ 2 12]
print numpy.prod(my_array, axis = None)         #Output : 24
print numpy.prod(my_array)                      #Output : 24
By default, the axis value is None.
Therefore, it performs the product over all the dimensions of the input array.

Task

You are given a 2-D array with dimensions NxM.
Your task is to perform the sum tool over axis 0
and then find the product of that result.

Input Format

The first line of input contains space separated values of N and M.
The next N lines contains M space separated integers.

Output Format

Compute the sum along axis 0.
Then, print the product of that sum.

Sample Input

2 2
1 2
3 4

Sample Output

24

Explanation

The sum along axis 0 = [4 6]
The product of this sum = 24

"""

import numpy

# ignoring 2nd int of 1st line, M
a = [list(map(int, input().strip().split()))
     for _ in range(int(input().strip().split()[0]))]
print(numpy.product(numpy.sum(a, axis=0)))