#!/usr/bin/env python
# coding: utf-8

import numpy as np
import random
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
from scipy.stats import t,norm,uniform
from matplotlib.offsetbox import AnchoredText
plt.style.use('ggplot')

def Markov_Monte_Carlo(x0, delta, num_samples):
    """
    Returns samples from t-distribution
    using Metropolis-Hastings algorithim
    and an uniform distribution.
    """
    X = np.zeros(num_samples)  # Samples
    X[0] = x0
    for i in range(1,num_samples):
        r = np.random.uniform(-delta,delta)
        y = X[i-1] + r
        acc_ratio = min(np.log(t.pdf(y,2))-np.log(t.pdf(X[i-1],2)), 0)  # Acceptance ratio
        u = np.random.uniform(0,1)
        if np.log(u) <= acc_ratio:
            X[i] = y
        else:
            X[i] = X[i-1]
    return X

def MCMC_Plots(x0, delta_vec, num_samples):
    half = len(delta_vec)//2
    fig1 = plt.figure(figsize=(16,6), constrained_layout=True)
    gs1 = fig1.add_gridspec(2,3)
    ax = {}
    for i, delta in enumerate(delta_vec[:half+1]):
        ax[i] = fig1.add_subplot(gs1[0, i])
        samples = Markov_Monte_Carlo(x0, delta, num_samples)
        x = np.linspace(-8,8,num_samples)
        z = t.pdf(x,2)
        ax[i].plot(x, z, label='true density', color='cornflowerblue')
        sns.kdeplot(samples, label='MCMC simulation', color='orchid', ax=ax[i])
        ax[i].legend(fontsize=8)
        at = AnchoredText('$\delta$={}'.format(delta), loc='center left', frameon=True)
        ax[i].add_artist(at)
        ax[0].set_ylabel('Density')
        ax[i].set_xlim([-8,8])
    for i, delta in enumerate(delta_vec[half+1:]):
        ax[i] = fig1.add_subplot(gs1[1, i])
        samples = Markov_Monte_Carlo(x0, delta, num_samples)
        x = np.linspace(-8,8,num_samples)
        z = t.pdf(x,2)
        ax[i].plot(x, z, label='true density', color='cornflowerblue')
        sns.kdeplot(samples, label='MCMC simulation', color='orchid', ax=ax[i])
        ax[i].legend(fontsize=8)
        at = AnchoredText('$\delta$={}'.format(delta), loc='center left', frameon=True)
        ax[i].add_artist(at)
        ax[0].set_ylabel('Density')
        ax[i].set_xlim([-8,8])

def Quotient(n, mu, sigma, c):
    return t.pdf(n,2)/(c*norm.pdf(n, mu, sigma))

def Supremum(mu, sigma):
    x = np.linspace(-10,10,100)
    q = norm.pdf(x, mu, sigma) / t.pdf(x,2)
    c = max(q)
    return c

def Rejection_Sampling1(mu, sigma, num_sim):
    """
    Returns samples of a t-distribution using
    rejection sampling method and a fixed
    prob of acceptance. Candidate density is
    a normal distribution.
    """
    c = Supremum(mu, sigma)
    sample1 = []
    for i in range(num_sim):
        u = np.random.uniform(0,1)
        n = np.random.normal(mu, sigma)
        if u <= Quotient(n, mu, sigma, c):
            sample1.append(n)
    return [c, sample1]

def Rejection_Sampling2(mu, sigma, c0, num_sim):
    """
    An alternative method. Prob of acceptance
    is updated during the simulations.
    """

    c_list = []
    sample2 = []
    c = c0
    for i in range(num_sim):
        u = np.random.uniform(0,1)
        n = np.random.normal(mu, sigma)
        if u <= Quotient(n, mu, sigma, c):
            sample2.append(n)
            c = max(c, norm.pdf(n, mu, sigma)/t.pdf(n,2))
            c_list.append(c)
    return [c_list, sample2]

def Rejection_Comparison_Plots(mu, var_vec, c0, num_sim):

     """It compares both methods for rejection sampling."""

    fig, ax = plt.subplots(len(var_vec),2, figsize=(12,10))
    x = np.linspace(-8,8,num_sim)
    for i, v in enumerate(var_vec):
        [c, sample1] = Rejection_Sampling1(mu, np.sqrt(v), num_sim)
        [c_list, sample2] = Rejection_Sampling2(mu, np.sqrt(v), c0, num_sim)
        ax[i,0].plot(x, t.pdf(x,2), label='true density', color='black')
        sns.kdeplot(sample1, label='first approach', color='cornflowerblue', ax=ax[i,0])
        sns.kdeplot(sample2, label='alternative method', color='orchid', ax=ax[i,0])
        ax[0,0].set_title('KDE plots')
        ax[i,0].legend(fontsize=10)
        ax[-1,0].set_xlabel('x', fontsize=14)
        ax[i,0].set_xlim([-8,8])
        ax[i,0].text(-6,0.2,'Candidate density:\n$\mu=0,\;\sigma^2$={}'.format(v))
        ax[i,1].axhline(y=c, label='4', color='r', linestyle='--', alpha=0.6)
        ax[i,1].plot(np.arange(len(c_list)), c_list, label='5', color='b', alpha=0.6)
        ax[0,1].set_title('Plots for $c$', fontsize=12)
        ax[i,1].legend(['$c$ in the first approach','$c$ in the second approach'], fontsize=10)
        ax[-1,1].set_xlabel('steps', fontsize=14)
        ax[i,1].set_xlim(0,50)
        plt.suptitle('Rejection sampling method with fixed $c$ and its alternative', y=1.05)
        plt.tight_layout()

def MCMC_Rejection_Plots(x0, delta, mu, v, s:list):

    """It compares Monte-Carlo Narkov chain with rejection sampling method."""

    fig, ax = plt.subplots(len(s), figsize=(10,10))
    for i, num_samples in enumerate(s):
        first_mathod = Markov_Monte_Carlo(x0, delta, num_samples)
        [c, second_method] = Rejection_Sampling1(mu, np.sqrt(v), num_samples)
        x = np.linspace(-8,8,num_samples)
        ax[i].plot(x, t.pdf(x,2), label='1', color='black')
        sns.kdeplot(first_mathod, label='1', color='cornflowerblue', ax=ax[i])
        sns.kdeplot(second_method, label='2', color='orchid', ax=ax[i])
        ax[i].legend(['true density','MCMC','rejection sampling'], fontsize=10)
        ax[i].text(-6,0.25,'number of simulations={}'.format(num_samples), fontsize=12)
        ax[i].set_ylabel('KDE', fontsize=14)
        ax[i].set_xlim([-8,8])
        plt.suptitle('Comparison between MCMC and rejection sampling,\n for MCMC:'\
                     r' $\delta=1$ and for rejection sampling: $\mu=0,\,\sigma^2=3$'\
                     ' and $c\approx1.25$', y=1.05)
        plt.tight_layout()

# An Example
x0 = 0
delta = 1
mu = 0
v = 3
s = [5000, 10000, 50000]

MCMC_Rejection_Plots(x0, delta, mu, v, s)


"""
Author : Achintya Gupta
Date Created : 22-09-2020
"""

"""
Problem Statement
------------------------------------------------
Q)  The following iterative sequence is defined for the set of positive integers:
                n → n/2 (n is even)
                n → 3n + 1 (n is odd)
    Using the rule above and starting with 13, we generate the following sequence:
    13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
    It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem),
    it is thought that all starting numbers finish at 1.
    Which starting number, under one million, produces the longest chain?
    NOTE: Once the chain starts the terms are allowed to go above one million.
"""
from utils import timing_decorator, find_factors
import numpy as np

chain_mapping={}

def get_chainlen(num):
    global chain_mapping
    chain_len=0
    chain=[num]
    while num!=1:
        if num in chain_mapping:
            return len(chain)+chain_mapping[num]-1
        if num%2==0:
            num=int(num/2)
        else:
            num=int(3*num+1)
        chain.append(num)
    chain_len = len(chain)
    for i in range(len(chain)):
        if i not in chain_mapping:
            chain_mapping[chain[i]] = len(chain[i:])
    return chain_len

@timing_decorator
def logest_collatz(N=1e6):
    num = 0
    cno = 0
    clen = 0

    for i in range(1,int(N)):
        curr_clen = get_chainlen(i)
        if curr_clen>clen:
            cno = i
            clen=curr_clen
    print(f'Starting number with longest collatz chain under < {N} is {cno}, with chain length of {clen}')

logest_collatz()


'''
logsumexp.py

Provides a numerically implementation of logsumexp function,
such that no matter what 1-dimensional input array is provided,
we return a finite floating point answer that does not overflow or underflow.

References
----------
See the math here:
https://www.cs.tufts.edu/comp/135/2020f/hw2.html#logsumexp
'''

import numpy as np

# No other imports allowed

def my_logsumexp(scores_N):
    ''' Compute logsumexp on provided array in numerically stable way.

    This function only handles 1D arrays.
    The equivalent scipy function can handle arrays of many dimensions.

    Args
    ----
    scores_N : 1D NumPy array, shape (N,)
        An array of real values

    Returns
    -------
    a : float
        Result of the logsumexp computation

    Examples
    --------
    >>> _ = np.seterr(all='raise') # Make any numerical issue raise error

    # Example 1: an array without overflow trouble, so you get the basic idea
    >>> easy_arr_N = np.asarray([0., 1., 0.1])

    # Show that your code does OK on Example 1
    >>> easy_ans = my_logsumexp(easy_arr_N)
    >>> print("%.5f" % (easy_ans))
    1.57349

    # Show that naive implementation does OK on Example 1
    >>> naive_ans = np.log(np.sum(np.exp(easy_arr_N)))
    >>> print("%.5f" % (naive_ans))
    1.57349

    # Example 2: an array where overflow would occur in bad implementation
    >>> tough_arr_N = [1000., 1001., 1002.]

    # Show that naive implementation suffers from overflow on Example 2
    >>> naive_ans = np.log(np.sum(np.exp(tough_arr_N)))
    Traceback (most recent call last):
    ...
    FloatingPointError: overflow encountered in exp

    # Show that your implementation does well on Example 2
    >>> ans_that_wont_overflow = my_logsumexp(tough_arr_N)
    >>> np.isfinite(ans_that_wont_overflow)
    True
    >>> print("%.5f" % (ans_that_wont_overflow))
    1002.40761
    '''
    scores_N = np.asarray(scores_N, dtype=np.float64)

    # TODO compute logsumexp in numerically stable way
    # See math on HW2 instructions page for the correct approach
    return 0.0


###################Tools and metrics##################

'''The functions in this python module compute
algebric metrics from adjacency matrices used in MANIA'''

import numpy as num

def AR(A):
	# Computes Assymetry ratio
	B=A-A.transpose()
	total_edges=sum(sum(A-num.diag(num.diag(A))))
	eu=float(sum(sum(abs(B))))/2
	if total_edges==0:
		return 1.0
	return eu/total_edges

def NAR(A):
	# Computes the normalized asymmetry ratio
	l1,l2=num.shape(A)
	if l1!=l2:
		print 'Error: Input matrix not square'
		return None
	l=l1
	total_edges=sum(sum(A-num.diag(num.diag(A))))
	AR_rand=1-(float(total_edges)/(l*(l-1)))
	if AR_rand==0:
		return float('inf')
	return AR(A)/AR_rand

def sim(A1,A2,mode='jac'):
	# Jaccard similarity on edges
	l1,l2=num.shape(A1)
	if l1!=l2:
		print 'Error: Input matrix not square'
		return None
	l=l1
	l1,l2=num.shape(A1)
	if l1!=l or l2!=l:
		print 'Error: Matrices must be of the same size'
		return None
	if mode=='jac':
		P=A1+A2
		P=P-num.diag(num.diag(P))
		P[P>0]=1
		S=A1*A2
		S=S-num.diag(num.diag(S))
		return float(sum(sum(S)))/sum(sum(P))
	else:
		S=A1*A2
		S=S-num.diag(num.diag(S))
		A1=A1-num.diag(num.diag(A1))
		A2=A2-num.diag(num.diag(A2))
		denom=min(sum(sum(A1)),sum(sum(A2)))
		return float(sum(sum(S)))/denom

def density(A):
	# returns the density of edges in A
	l1,l2=num.shape(A)
	if l1!=l2:
		print 'Error: Input matrix not square'
		return None
	l=l1
	total_edges=sum(sum(A-num.diag(num.diag(A))))
	return float(total_edges)/(l*(l-1))


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

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

def gauss(mu,std,n=1000):
    noise = np.random.normal(mu,std,n)
    x = np.linspace(mu-3*std,mu+3*std,n)
    y = st.norm.pdf(x,mu,std)

    #Timeseries
    plt.figure()
    plt.title(f"Gaussian Time Series   μ={mu}   σ={std}")
    plt.xlabel("Index")
    plt.ylabel("Value")
    plt.plot(range(len(noise)),noise)
    plt.savefig(f"Gaussian_Timeseries_{mu}_{std}.pdf")
    plt.savefig(f"Gaussuan_Timeseries_{mu}_{std}.png")

    #Histogram
    plt.figure()
    plt.title(f"Gaussian   μ={mu}   σ={std}   bins={int(n/30)}")
    plt.xlabel("Value")
    plt.ylabel("Probability")
    plt.hist(noise,bins=int(n/30),density=True,label='Data')
    plt.plot(x,y,color='red',label='Gaussian')
    plt.legend()
    plt.savefig(f"Gaussian_{mu}_{std}.pdf")
    plt.savefig(f"Gaussian_{mu}_{std}.png")

def poisson(mu,n=1000):
    noise = np.random.poisson(mu,n)
    bins = np.arange(min(noise),max(noise)+1,1)

    #Timeseries
    plt.figure()
    plt.title(f"Poisson Time Series   μ={mu}")
    plt.xlabel("Index")
    plt.ylabel("Value")
    plt.plot(range(len(noise)),noise)
    plt.savefig(f"Poisson_Timeseries_{mu}.pdf")
    plt.savefig(f"Poisson_Timeseries_{mu}.png")

    #Sharp Histogram
    x = np.arange(0,max(noise),0.01)
    y = st.poisson.pmf(x,mu)

    plt.figure()
    plt.title(f"Poisson Sharp   μ={mu}   n={1000}")
    plt.xlabel("Value")
    plt.ylabel("Probability")
    plt.hist(noise,bins=bins,align='left',density=True,label='Data')
    plt.xticks(bins)
    plt.plot(x,y,color='red',label='Poisson')
    plt.legend()
    plt.savefig(f"Poisson_Sharp_{mu}.pdf")
    plt.savefig(f"Poisson_Sharp_{mu}.png")

    #Fuzzy Histogram
    x = np.arange(0,max(noise),1)
    y = st.poisson.pmf(x,mu)

    plt.figure()
    plt.title(f"Poisson Fuzzy   μ={mu}   n={1000}")
    plt.xlabel("Value")
    plt.ylabel("Probability")
    plt.hist(noise,bins=bins,align='left',density=True,label='Data')
    plt.xticks(bins)
    plt.plot(x,y,color='red',label='Poisson')
    plt.legend()
    plt.savefig(f"Poisson_Fuzzy_{mu}.pdf")
    plt.savefig(f"Poisson_Fuzzy_{mu}.png")

#1
gauss(0,1)

#2
gauss(10.345,2.338)

#3
poisson(2)

#4
poisson(3.45)


# # https://www.geeksforgeeks.org/implement-sigmoid-function-using-numpy/
import copy
import numpy as np
np.random.seed(0)

# compute sigmoid nonlinearity
def sigmoid(x):
    output = 1/(1+np.exp(-x))
    return output

# convert output of sigmoid function to its derivative
def sigmoid_output_to_derivative(output):
    return output*(1-output)

# sigmoid_test = sigmoid([22,44])

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

# x = np.linspace(-10, 10, 100)
x =1

x = [-1,0,0.5,1]
# x = [-1.33,0.33,0.533,1.33]
# z = 1/(1 + np.exp(-x))

# for val in x:
#     print(val,sigmoid(val))

for val in x:
    print(val,sigmoid(val),sigmoid_output_to_derivative(sigmoid(val)))

# 1 0.7310585786300049
# 0.5 0.6224593312018546
# 0 0.5
# -1 0.2689414213699951

# plt.plot(x, z)
# plt.xlabel("x")
# plt.ylabel("Sigmoid(X)")

# plt.show()


import numpy as np
import scipy.optimize as op
import scipy.linalg as la
import numdifftools as nd
from matplotlib import pyplot as plt
from mpl_toolkits import mplot3d
from time import perf_counter

#returns the minimum of a multivariable function using gradient descent
def gradient_descent(f, xk, delta = 0.01, plot=False, F = None, axlim = 10):
    """
    f: multivariable function with 1 array as parameter
    xk : a vector to start descent
    delta : precision of search
    plot : option to plot the results or not
    F : the function f expressed with 2 arrays in argument (X,Y) representing the colomns xk[0] and xk[1] for ploting issues. used only if plot == True
    axlim : limit of the plot 3 axis (x,y,z)
    """
    if plot : ax = plt.axes(projection='3d')
    A = []
    t = perf_counter()
    dk = nd.Gradient(f)(xk)
    while la.norm(dk) > delta :
        if plot and len(A) < 10 : A.append(xk)
        xt = xk
        phi = lambda s : f(xk - s * dk)
        alpha = op.newton(phi, 1)
        xk -= alpha * dk
        if plot and len(A) < 10 : A.append(xk)
        dk = nd.Gradient(f)(xk)
        if la.norm(xk - xt) < delta : break
    t = perf_counter() - t
    print("execution time: ",t)
    if plot :
        for u in A:
            ax.scatter(u[0], u[1], f(u), c = 'b', s = 50)
        ax.scatter(xk[0], xk[1], f(xk), c = 'r', s = 50,label="optimum")
        x = np.arange(-axlim, axlim, axlim/100)
        y = np.arange(-axlim, axlim, axlim/100)
        X, Y = np.meshgrid(x, y)
        Z = F(X,Y)
        ax.set_xlabel('x', labelpad=20)
        ax.set_ylabel('y', labelpad=20)
        ax.set_zlabel('z', labelpad=20)
        surf = ax.plot_surface(X, Y, Z, cmap = plt.cm.cividis)
        plt.legend()
        plt.title("optimizition with Gradient Descent")
        plt.show()
    return xk

#returns the minimum of a multivariable function using conjugate gradient
def conjugate_gradient(f, x, plot=False, F = None,axlim = 10):
    """
    f: multivariable function with 1 array as parameter
    x : a vector to start descent
    plot : option to plot the results or not
    F : the function f expressed with 2 arrays in argument (X,Y) representing the colomns x[0] and x[1] for ploting issues. used only if plot == True
    axlim : limit of the plot 3 axis (x,y,z)
    """
    if plot : ax = plt.axes(projection='3d')
    A = []
    t = perf_counter()
    d = -nd.Gradient(f)(x)
    q = nd.Hessian(f)(x)
    n = len(x)
    for k in range(1, n):
        if plot and len(A) < int(n/3) : A.append(x)
        alpha = (d.T@d)/(d.T@q@d)
        x += alpha * d
        beta = (nd.Gradient(f)(x).T@q@d)/(d.T@q@d)
        d = beta * d - nd.Gradient(f)(x)
    t = perf_counter() - t
    print("execution time: ",t)
    if plot :
        for u in A:
            ax.scatter(u[0], u[1], f(u), c = 'b', s = 50)
        ax.scatter(x[0], x[1], f(x), c = 'r', s = 50,label="optimum")
        x = np.arange(-axlim, axlim, axlim/100)
        y = np.arange(-axlim, axlim, axlim/100)
        X, Y = np.meshgrid(x, y)
        Z = F(X,Y)
        ax.set_xlabel('x', labelpad=20)
        ax.set_ylabel('y', labelpad=20)
        ax.set_zlabel('z', labelpad=20)
        surf = ax.plot_surface(X, Y, Z, cmap = plt.cm.cividis)
        plt.legend()
        plt.title("optimizition with Conjugate Gradient")
        plt.show()
    return x

#verifies if a function is defined positive at a point x
def is_pos_def(f, x):
    """
    f: multivariable function with 1 array as parameter
    x : a vector where to verify if f is definite positive
    """
    m = nd.Hessian(f)(x)
    return np.all(np.linalg.eigvals(m) > 0)

#returns the minimum of a multivariable function using Newton descent
def newton_descent(f, x, delta = 0.01, plot=False, F = None, axlim = 10):
        """
    f: multivariable function with 1 array as parameter
    x : a vector to start descent
    delta : precision of search
    plot : option to plot the results or not
    F : the function f expressed with 2 arrays in argument (X,Y) representing the colomns x[0] and x[1] for ploting issues. used only if plot == True
    axlim : limit of the plot axis (x,y,z)
    """
    d = -la.inv(nd.Hessian(f)(x))@nd.Gradient(f)(x)
    m = nd.Hessian(f)(x)
    if la.det(m) == 0:
        return ValueError
    else :
        if plot : ax = plt.axes(projection='3d')
        A = []
        t = perf_counter()
        while la.norm(d) > delta:
            if plot and len(A) < 10 : A.append(x)
            phi = lambda s : f(x - s * d)
            alpha = op.newton(phi, 1)
            x -= alpha * d
            dt = nd.Hessian(f)(x)
            if is_pos_def(f, x) : d = -la.inv(nd.Hessian(f)(x))@nd.Gradient(f)(x)
            else :
                u = la.eigvals(f)(x)
                epsilon = min(u)
                n = len(x)
                d = -la.inv((epsilon * np.identity(n) + nd.Hessian(f)(x))) @ nd.Gradient(f)(x)
        t = perf_counter() - t
        print("execution time: ",t)
        if plot :
            for u in A:
                ax.scatter(u[0], u[1], f(u), c = 'b', s = 50)
            ax.scatter(x[0], x[1], f(x), c = 'r', s = 50,label="optimum")
            x = np.arange(-axlim, axlim, axlim/100)
            y = np.arange(-axlim, axlim, axlim/100)
            X, Y = np.meshgrid(x, y)
            Z = F(X,Y)
            ax.set_xlabel('x', labelpad=20)
            ax.set_ylabel('y', labelpad=20)
            ax.set_zlabel('z', labelpad=20)
            surf = ax.plot_surface(X, Y, Z, cmap = plt.cm.cividis)
            plt.legend()
            plt.title("optimizition with Newton descent")
            plt.show()
    return x


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

def loadDataset(filename, split, trainingSet=[] , testSet=[]):
    with open(filename, 'r') as csvfile:
        lines = csv.reader(csvfile)
        dataset = list(lines)
        for x in range(len(dataset)):
            for y in range(2):
                dataset[x][y] = float(dataset[x][y])
            if random.random() < split:
                trainingSet.append(dataset[x])
            else:
                testSet.append(dataset[x])

def pca(X,n):

    #1st Step : subtract mean
    avg = np.mean(X,axis=0)
    avg = np.tile(avg,(X.shape[0],1))
    X -= avg;
    #print(avg)

    #2nd Step : covariance matrix
    C = np.dot(X.transpose(),X)/(X.shape[0]-1)

    #3rd Step : Eigen Value, Eigen Vector
    eig_values,eig_vecs = np.linalg.eig(C)

    #4rd Step : Select n개의 PC
    idx = np.argsort(eig_values)[-n:][::-1]
    eig_values = eig_values[idx]
    eig_vecs = eig_vecs[:,idx]

    #print(eig_values.argsort())
    #print(eig_values.argsort()[-n:])
    #print(idx)
    #print(eig_values)
    #print(eig_vecs)

    #5th Step : new coordinate in new space
    Y = np.dot(X,eig_vecs)

    #6th Step : reconstruction
    rec=np.dot(eig_vecs,Y.transpose())
    Score=np.linalg.norm(X.transpose()-rec,axis=0)
    #print(rec)
    #print(Score)

    return (X.transpose(), rec, Score.transpose(), eig_vecs, eig_values)

def main():

	# prepare data
    trainingSet=[]
    testSet=[]
    split = 0.8
    random.seed(100)
    loadDataset('ex_pca5.csv', split, trainingSet, testSet)
    #print('Train set: ' + repr(len(trainingSet)))
    #print('Test set: ' + repr(len(testSet)))

    # n=PC개수 (Hyper parameter)
    n=1
    trainX=np.array(trainingSet)
    pca_result=pca(trainX[:,:-1].astype(np.float),n)

   # print('pca result:',pca_result)

    x=pca_result[0][0]
    y=pca_result[0][1]
    Score=pca_result[2]*100

    print('Eigen Value : ', pca_result[4])
    print('Eigen Vector : ', pca_result[3])
    print('Data X : ', np.transpose(pca_result[0])[:10])
    print('Reconstruction : ', np.transpose(pca_result[1])[:10])
    print('Novelty Score : ', np.transpose(pca_result[2])[:10])

    x_rec=pca_result[1][0]
    y_rec=pca_result[1][1]

    plt.figure(figsize=(6, 6), dpi=80)
    plt.scatter(x,y,s=Score);
    plt.scatter(x_rec,y_rec,s=20);
    plt.xticks(np.arange(-3,3,0.5))
    plt.yticks(np.arange(-3,3,0.5))

main()

03 Novelty Detection/Tutorial 09 - Distance and reconstruction-based novelty detection/PCA/PCA_ND.py


# https://projecteuler.net/problem=14
# Run with: 'python solve14.py'
# using Python 3.6.9
# by Zack Sargent

# Prompt:

# The following iterative sequence is defined for the set of positive integers:
#   n → n/2 (n is even)
#   n → 3n + 1 (n is odd)
# Using the rule above and starting with 13, we generate the following sequence:
#   13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
# It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms.
# Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.
# Which starting number, under one million, produces the longest chain?
# NOTE: Once the chain starts the terms are allowed to go above one million.

# numba is only used to improve performance.
# remove lines 20 and 22 if you don't want it.
from numba import njit

@njit
def collatz_sequence(n: int, length: int) -> int:
    length += 1
    if n == 1:
        return length
    elif n % 2 == 0:
        return collatz_sequence(n / 2, length)
    else:
        return collatz_sequence((3*n)+1, length)

max_num: int = 0
max_size: int = 0

GOAL: int = 1_000_000
# divide by 1.5 to limit the range we check
for i in range(round(GOAL // 1.5), GOAL + 1):
    size = collatz_sequence(i, 0)
    if size > max_size:
        max_size = size
        max_num = i

print(max_num)
# -> 837799


## module gaussNodes
''' x,A = gaussNodes(m,tol=10e-9)
    Returns nodal abscissas {x} and weights {A} of
    Gauss-Legendre m-point quadrature.
'''
import math
import numpy as np

def gaussNodes(m,tol=10e-9):

    def legendre(t,m):
        p0 = 1.0; p1 = t
        for k in range(1,m):
            p = ((2.0*k + 1.0)*t*p1 - k*p0)/(1.0 + k )
            p0 = p1; p1 = p
        dp = m*(p0 - t*p1)/(1.0 - t**2)
        return p,dp

    A = np.zeros(m)
    x = np.zeros(m)
    nRoots = int((m + 1)/2)         # Number of non-neg. roots
    for i in range(nRoots):
        t = math.cos(math.pi*(i + 0.75)/(m + 0.5))# Approx. root
        for j in range(30):
            p,dp = legendre(t,m)    # Newton-Raphson
            dt = -p/dp; t = t + dt  # method
            if abs(dt) < tol:
                x[i] = t; x[m-i-1] = -t
                A[i] = 2.0/(1.0 - t**2)/(dp**2) # Eq.(6.25)
                A[m-i-1] = A[i]
                break
    return x,A


import numpy as np

def polykernel(x1, x2, offset=0, degree=2):
    """
    polynomial kernel of the form:
    k(x1, x2) = ((x1' * x2) + offset) ^ degree
    """
    return (np.dot(x1, x2) + offset)**degree

def rbf(x1, x2, sigma=1):
    """
    rbf kernel. sigma is the smoothing parameter
    k(x1, x2) = exp(-||x1 - x2||^2 / 2 * sigma^2)
    """
    return np.exp(-(np.linalg.norm(x1 - x2) / (2 * sigma**2)))


from time import perf_counter
from math import sqrt, ceil, log
from matplotlib import pyplot as plt
import numpy as np

#unrestricted search with fixed step
def usfs(f, a, b, p = 0.01, plot=False):
    """
    f: monovariable function
    a : start of search interval
    b : end of search interval
    p : step size
    plot : a parameter to choose either to plot or not the results
    """
    if plot :
        X = np.linspace(a,b,1000)
        plt.plot(X, f(X))
    t = perf_counter()
    iter = 0
    A = []
    while f(a) > f(a + p) and a <= b :
        iter += 1
        a += p
        if plot and len(A) < 10 : A.append(a)
    t = perf_counter() - t
    print("time execution: ",t)
    if plot :
        for e in A :
            plt.plot(e,f(e),"o")
    if a > b :
        plt.plot(a-p, f(a-p),"s",label="optimum")
        plt.show()
        return a - p
    if plot :
        plt.plot(a,f(a),"s",label="optimum")
        plt.title("optimization with fixed step search")
        plt.legend()
        plt.show()
    return a

#unrestricted search with accelerated step
def usas(f, a, b, p = 0.01, plot=False):
    """
    f: monovariable function
    a : start of search interval
    b : end of search interval
    p : step size
    plot : a parameter to choose either to plot or not the results
    """
    t = perf_counter()
    it , step = a, 0
    A = []
    while  step != p :
        if it > b : it  -= step / 2
        step , i1, i2 = p, f(it), f(it + p)
        while i1 > i2 and it < b :
            if plot and len(A) < 10 : A.append(it)
            it += step
            i1 = i2
            step *= 2
            i2 = f(it + step)
    t = perf_counter() - t
    print("time execution: ",t)
    if plot :
        plt.plot(it,i1,"s",label="optimum")
        X = np.linspace(a,b,1000)
        plt.plot(X,f(X))
        for e in A :
            plt.plot(e,f(e),"o")
        plt.legend()
        plt.title("optimization with accelerated step search")
        plt.show()
    return it

#exhaustive search
def bf(f, a, b, n = 1000, plot=False):
    """
    f: monovariable function
    a : start of search interval
    b : end of search interval
    n : number of divisions of the interval
    plot : a parameter to choose either to plot or not the results
    """
    t = perf_counter()
    p = (b - a) / (n - 1)
    it = a
    L = []
    A = []
    while a <= b :
        L.append(f(a))
        a += p
        if plot : A.append(a)
    t = perf_counter() - t
    print("time execution: ",t)
    if plot :
        plt.plot(it + L.index(min(L))*p,f(it + L.index(min(L))*p),"s",label="optimum")
        X = np.linspace(a,b,1000)
        plt.plot(X,f(X))
        for e in A :
            plt.plot(e,f(e),"o")
        plt.legend()
        plt.title("optimization with Brute Force method")
        plt.show()
    return it + L.index(min(L))*p

#binary search
def bs(f, a, b, e = 0.01, delta = 0.001, plot=False):
    """
    f: monovariable function
    a : start of search interval
    b : end of search interval
    e : search precision
    delta : a factor relied to the binary search
    plot : a parameter to choose either to plot or not the results
    """
    t = perf_counter()
    A = []
    mid = (a + b) / 2
    n = ceil(log((b - a - delta) / (2 * e * (b - a) - delta)) * (2 / log(2)))
    if n%2 : n+=1
    if plot :
        X = np.linspace(a,b,1000)
        plt.plot(X,f(X))
    for i in range(n) :
        y1, y2 = f(mid - (delta / 2)), f(mid + (delta / 2))
        if y2 > y1 : b = mid + (delta / 2)
        else : a = mid - (delta / 2)
        mid = (a + b) / 2
        if plot : A.append(mid)
    t = perf_counter() - t
    print("time execution: ",t)
    if plot :
        plt.plot(mid,f(mid),"s",label="optimum")
        for u in A :
            plt.plot(u,f(u),"o")
        plt.legend()
        plt.title("optimization with Binary Search method")
        plt.show()
    return mid

#interval halving method
def halv(f, a, b, e = 0.01, plot=False):
    """
    f: monovariable function
    a : start of search interval
    b : end of search interval
    e : search precision
    plot : a parameter to choose either to plot or not the results
    """
    t = perf_counter()
    A = [[],[],[]]
    n = ceil((2 * log(2 * e))/ log(1 / 2)) + 1
    if (n%2)==0 : n += 1
    if plot :
        X = np.linspace(a,b,1000)
        plt.plot(X,f(X))
    for i in range(n):
        x0, x1, x2 = (a+b)/2, (a+b)/3, (2*(a+b))/3
        y0, y1, y2 = f(x0), f(x1), f(x2)
        if plot :
            A[0].append(x0)
            A[1].append(x1)
            A[2].append(x2)
        if y1 < y0 < y2 : b = x0
        elif y1 > y0 > y2 :	a = x0
        else :
            a = x1
            b = x2
    t = perf_counter() - t
    print("time execution: ",t)
    if plot :
        plt.plot((a+b)/2, f((a+b)/2),"s",color="yellow",label="optimum")
        for i in range(n):
            plt.plot(A[0][i],f(A[0][i]),"o",color='red')
            plt.plot(A[1][i],f(A[1][i]),"o",color='blue')
            plt.plot(A[2][i],f(A[2][i]),"o",color='green')
        plt.legend()
        plt.title("optimization with Interval Halving method")
        plt.show()
    return (a + b) / 2

#fibonacci method
def fibo(f, a, b, n = 20, plot=False):
    """
    f: monovariable function
    a : start of search interval
    b : end of search interval
    n : number of iterations (deepth)
    plot : a parameter to choose either to plot or not the results
    """
    A = [[],[]]
    if plot :
        X = np.linspace(a,b,1000)
        plt.plot(X,f(X))
    t = perf_counter()
    f0, f1, f2 = 1, 1, 2
    for i in range(n-2) :
        f0 = f1
        f1 = f2
        f2 = f1 + f0
    c = a + (f0 / f2) * (b - a)
    d = a + (f1 / f2) * (b - a)
    y1 = f(c)
    y2 = f(d)
    for i in range(n):
        if plot and len(A[0]) < 10 :
            A[0].append(c)
            A[1].append(d)
        if y1 < y2 :
            b = d
            d = c
            c = a + (b - d)
            y2 = y1
            y1 = f(c)
        else :
            a = c
            c = d
            d = a + (b - c)
            y1 = y2
            y2 = f(d)
    t = perf_counter() - t
    print("time execution: ",t)
    if plot :
        plt.plot(c,f(c),"s",label="optimum")
        for i in range(len(A[0])):
            plt.plot(A[0][i],f(A[0][i]),"o",color='red')
            plt.plot(A[1][i],f(A[1][i]),"o",color='blue')
        plt.legend()
        plt.title("optimization with Fibonacci method")
        plt.show()
    return c

#golden ratio method
def golden(f, a, b, e = 0.01, plot=False):
    """
    f: monovariable function
    a : start of search interval
    b : end of search interval
    e : search precision
    plot : a parameter to choose either to plot or not the results
    """
    A = [[],[]]
    if plot and len(A[0]) < 10 :
        X = np.linspace(a,b,1000)
        plt.plot(X,f(X))
    t = perf_counter()
    phi = (sqrt(5) - 1) / 2
    Ln = 1
    f0, f1, f2 = 1, 1, 2
    for i in range(100) :
        f0 = f1
        f1 = f2
        f2 = f1 + f0
    c = a + (f0 / f2) * (b - a)
    d = a + (f1 / f2) * (b - a)
    y1 = f(c)
    y2 = f(d)
    while Ln > e:
        if plot :
            A[0].append(c)
            A[1].append(d)
        if y1 < y2 :
            b = d
            d = c
            c = a + (b - d)
            y2 = y1
            y1 = f(c)
        else :
            a = c
            c = d
            d = a + (b - c)
            y1 = y2
            y2 = f(d)
        Ln *= phi
    t = perf_counter() - t
    print("time execution: ",t)
    if plot :
        plt.plot(c,f(c),"s",label="optimum")
        for i in range(len(A[0])):
            plt.plot(A[0][i],f(A[0][i]),"o",color='red')
            plt.plot(A[1][i],f(A[1][i]),"o",color='blue')
        plt.title("optimization with Golden section")
        plt.legend()
        plt.show()
    return c


import numpy as np

__all__ = ['cart2pol', 'pol2cart', 'cart2sphere', 'sphere2cart']

def _fix_angle(a):
    while a > np.pi:
        a -= np.pi
    while a < -np.pi:
        a += np.pi
    return a

def cart2pol(x, y):
    r = np.sqrt(x ** 2 + y ** 2)
    ang = np.arctan2(y, x)
    return r, ang

def pol2cart(r, ang):
    x = r * np.cos(ang)
    y = r * np.sin(ang)
    return x, y

def cart2sphere(x, y, z):
    r = np.sqrt(x**2 + y**2 + z**2)
    # theta = np.arctan2(np.sqrt(x**2 + y**2), z)
    theta = np.arccos(z / r)
    phi = np.arctan2(y, x)
    return r, theta, phi

def sphere2cart(r, theta, phi):
    theta = _fix_angle(theta)
    phi = _fix_angle(phi)
    x = r * np.sin(theta) * np.cos(phi)
    y = r * np.sin(theta) * np.sin(phi)
    z = r * np.cos(theta)
    return x, y, z


# Code was created by M. Heriyanto, 2020/01/13
# https://github.com/ezygeo-ai/machine-learning-and-geophysical-inversion/blob/master/scripts/derrivatives_in_python.py

import sympy as sym
# https://scipy-lectures.org/packages/sympy.html

# === example
x = sym.Symbol('x')
y = sym.Symbol('y')

print(sym.diff(3*x**2 + 3*y, x))
print(sym.diff(3*x**2 + 3*y, y))

def func(vx, vy):
    return 3*vx**2 + 3*vy

print(sym.diff(func(x, y), x))
print(sym.diff(func(x, y), y))

# === SP case
x = sym.Symbol('x')
x0 = sym.Symbol('x0')
alpa = sym.Symbol('alpa')
h = sym.Symbol('h')
K = sym.Symbol('K')

# forward function
def func_SP(vx, vx0, va, vh, vk):
    return vk * (((vx-vx0)*sym.cos(va)-vh*sym.sin(va))/((vx-vx0)**2 + vh**2)**3/2)

# x0
print(sym.diff(func_SP(x, x0, alpa, h, K), x0))

# alpa
print(sym.diff(func_SP(x, x0, alpa, h, K), alpa))

# h
print(sym.diff(func_SP(x, x0, alpa, h, K), h))

# K
print(sym.diff(func_SP(x, x0, alpa, h, K), K))


import numpy as np
import re
import random

H_matrix = np.mat([
    [1 , 0 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 0 , 1 , 0 , 1],
    [0 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1 , 0 , 0 , 1 , 1],
    [0 , 0 , 0 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1],
    [0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1],
])

G_matrix = np.mat([
    [1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],
    [1 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],
    [0 , 1 , 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],
    [1 , 1 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0],
    [1 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0],
    [0 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 1 , 0 , 0 , 0 , 0 , 0],
    [1 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 0 , 0],
    [0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0],
    [1 , 0 , 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0],
    [0 , 1 , 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 1 , 0],
    [1 , 1 , 0 , 1 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 1]
])

def hamming_encode(data: str) -> str:
    d = []
    for x in data:
        d.append(int(x))
    res = (np.mat(d) * G_matrix) % 2
    return re.sub(r'[^0-1]', '', str(res))
    # return str(res).replace(' ', '').replace('[', '').replace(']', '')

def error_occur(data: str) -> str:
    tmp = random.randint(0, 14)
    return data[:tmp] + ('0' if data[tmp] == '1' else '1') + data[tmp + 1:]

def hamming_decode(data: str, error = False) -> str:
    if error:
        data = error_occur(data)
    d = []
    for x in data:
        d.append(int(x))
    diff = (np.mat(d) * H_matrix.T) % 2
    diff = int(re.sub(r'[^0-1]', '', str(diff)[::-1]), 2)
    if diff != 0:
        diff -= 1
        data = data[:diff] + ('0' if data[diff] == '1' else '1') + data[diff + 1:]
    return data[2] + data[4:7] + data[8:]

if __name__ == "__main__":
    file_lines = []
    f = open('lab2_data/hamming_15_11.txt')
    file_lines = f.readlines()
    f.close()
    print (len(file_lines))
    for line in file_lines:
        data = line.split(',')
        data[0], data[1] = data[0].strip(), data[1].strip()
        assert (hamming_encode(data[0]) == data[1])
        assert (hamming_decode(data[1]) == data[0])
        assert (hamming_decode(data[1], True) == data[0])


"""
Exercise 2
Implement the Gradient Descent
Your challenge is to implement a generic prgram using the gradient
descent algorithm to find the minimum value of a single-variable
function specified as input by the user. The program should also create
a graph of the function and show all the intermediate values it found
before finding the minimum.
"""

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

epsilon = 1e-6
step_size = 1e-4

def grad_descent(init_val, function, x):

    if not solve(function):
        print("Cannot continue, solution for {0}=0 does not exist".format(function))
        return None

    x_new = init_val - step_size * function.subs({x: init_val}).evalf()
    x_old = init_val

    x_traversed = []

    while abs(x_old - x_new) > epsilon:
        x_traversed.append(x_new)
        x_old = x_new
        x_new = x_old - step_size * function.subs({x: x_old}).evalf()

    return x_new, x_traversed

def frange(start, final, interval):

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

    return numbers

def create_plot(x_traversed, function, var):
    x_val = frange(-10, 10, 0.01)
    y_val = [function.subs({var: x}) for x in x_val]
    plt.plot(x_val, y_val, "black")

    y_traversed = [function.subs({var: x}) for x in x_traversed]
    plt.plot(x_traversed, y_traversed, "green")

    plt.legend(["Function", "Intermediate points"])
    plt.show()

def validate_function(function):

    try:
        return sympify(function)
    except SympifyError:
        print("Invalid function entered")
        sys.exit(1)

    return None

def main():
    function = input("Provide a function of one variable: ")
    function = validate_function(function)

    variable = input("Enter the variable to differentiate with respect to: ")
    variable = Symbol(variable)

    init_val = input("Enter the initial value of the variable: ")
    init_val = float(init_val)

    derivative = Derivative(function, variable).doit()
    var_min, x_traversed = grad_descent(init_val, derivative, variable)

    if var_min:
        print("{} : {}".format(variable.name, var_min))
        create_plot(x_traversed, function, variable)

if __name__ == "__main__":
    main()


#Author: Daniel Moraes dos Santos
import numpy as np
import algebra

def multiply_matrix(A, B):
    if A.shape[1] != B.shape[0]:
        raise NameError('deu ruim')

    C = np.zeros(shape=(A.shape[0], B.shape[1]))

    for i in range(C.shape[0]):
        for j in range(C.shape[1]):
            for k in range(B.shape[0]):
                C[i, j] += A[i, k] * B[k, j]

    return C

def transpose_matrix(A):
    t = np.zeros(shape=(A.shape[1], A.shape[0]))

    for i in range(t.shape[0]):
        for j in range(t.shape[1]):
            t[i, j] = A[j, i]

    return t

def generate_A(points):
    A = np.zeros(shape=(len(points), k + 1))

    for i, point in enumerate(points):
        for j in range(k + 1):
            A[i, j] = point[0] ** abs(k - j)

    return A

def generate_b(points):
    b = np.zeros(shape=(len(points), 1))

    for i, point in enumerate(points):
        b[i, 0] = point[1]

    return b

if __name__ == '__main__':
    points_list = [(-2., -3.), (-1., 0.), (1., 0.), (2., -3.), (2., -1.), (4., 0.), (5., 2.)]
    # points_list = [(-2., 0.), (-1., 4.), (0., 5.), (1., 4.), (2., 0.)] # ex 1
    # points_list = [(-4, 0), (3, 0), (0, 0), (-3, -576), (4, -576), (-1, 144), (2, 144)] # ex 2
    # points_list = [(0., 1.), (2., 1.), (2., 5.), (6., 2.), (6., 4.), (6., 6.)] # ex 3

    k = 3  # ax3+bx2+cx1+d=y
    # k = 4
    # k = 6
    # k = 2

    A = generate_A(points_list)
    b = generate_b(points_list)
    print A
    print b

    At = transpose_matrix(A)

    An = multiply_matrix(At, A)
    bn = multiply_matrix(At, b)
    # An = At.dot(A)
    # bn = At.dot(b)

    print An
    print bn

    solver = algebra.Algebra()

    print solver.solve_system(An, bn)

Trabalho 6 (Daniel M.)/polynomial_interpolation.py


import numpy as np
from scipy import linalg as la
from scipy import optimize as opt
from matplotlib import pyplot as plt

# Problem 1
def conjugateGradient(b,x0,Q,tol = .0001):
    """Use the Conjugate Gradient Method to find the solution to the linear
    system Qx = b.

    Parameters:
        b  ((n, ) ndarray)
        x0 ((n, ) ndarray): An initial guess for x.
        Q  ((n,n) ndarray): A positive-definite square matrix.
        tol (float)

    Returns:
        x ((n, ) ndarray): The solution to the linear systm Qx = b, according
            to the Conjugate Gradient Method.
    """
    xk = x0
    rk = Q.dot(x0)-b
    dk = -rk
    k = 0
    while la.norm(rk) > tol:
        alphak = rk.T.dot(rk)/dk.T.dot(Q.dot(dk))
        xk1 = xk + alphak*dk
        rk1 = rk + alphak*Q.dot(dk)
        betak1 = rk1.T.dot(rk1)/rk.T.dot(rk)
        dk1 = -rk1 + betak1*dk
        k = k+1
        xk = xk1
        rk = rk1
        dk = dk1
    return xk1

# Problem 2
def prob2(filename = 'linregression.txt'):
    """Use conjugateGradient() to solve the linear regression problem with
    the data from linregression.txt.
    Return the solution x*.
    """
    data = np.loadtxt(filename)
    m,n = data.shape
    b = data[:,0]
    A = np.column_stack((np.ones(m),data[:,1:]))
    x0 = np.random.random(n)
    return conjugateGradient(A.T.dot(b),x0,A.T.dot(A))

    '''Correct Answer:
    [   -3482258.6159527,   15.06187214,    -0.03581918,    -2.0202298
        -1.03322686,        -0.05110411,    1829.15145504               ]

    or

    [ -3.48225866e+06   1.50618728e+01  -3.58191800e-02  -2.02022981e+00
      -1.03322687e+00  -5.11041030e-02   1.82915148e+03 ]
    '''

# Problem 3
def prob3(filename = 'logregression.txt'):
    """Use scipy.optimize.fmin_cg() to find the maximum likelihood estimate
    for the data in logregression.txt.
    """
    def objective(b):
        return (np.log(1+np.exp(x.dot(b))) - y*(x.dot(b))).sum()

    data = np.loadtxt(filename)
    m,n = data.shape
    y = data[:,0]
    x = np.empty_like(data)
    x[:,0] = np.ones_like(data[:,0])
    x[:,1:] = data[:,1:]
    y = data[:,0]

    guess = np.ones(4)
    b = opt.fmin_cg(objective, guess)

    return b

    '''Correct Answer:
    [-0.41307717, 0.92181585, 0.21007539, -0.55791808]
    '''

if __name__ == '__main__':
    n = 10
    A = np.random.random((n,n))
    Q = A.T.dot(A)
    b = np.random.random(n)
    x0 = np.random.random(n)
    x = conjugateGradient(b, x0, Q)
    # np.set_printoptions(suppress=True)
    if not np.allclose(x, la.solve(Q,b)):
        raise ValueError("Problem 1 Failed")
    print prob2()
    print prob3()
    np.set_printoptions()
    # test()


import math
import numpy as np
from random import randint

def PrimeGen(n=10000):
    """ Returns  a list of primes < n. Used Robert William method of high speed prime generation. """
    assert n>=2
    sieve = np.ones(n/2, dtype=np.bool)
    for i in range(3,int(n**0.5)+1,2):
        if sieve[i/2]:
            sieve[i*i/2::i] = False
    return np.r_[2, 2*np.nonzero(sieve)[0][1::]+1]

def lcm(p,q):
    ''' Computes LCM between two Integers p,q'''
    return (p*q)/math.gcd(p,q)

def extended_gcd(aa, bb):
    lastremainder, remainder = abs(aa), abs(bb)
    x, lastx, y, lasty = 0, 1, 1, 0
    while remainder:
        lastremainder, (quotient, remainder) = remainder, divmod(lastremainder, remainder)
        x, lastx = lastx - quotient*x, x
        y, lasty = lasty - quotient*y, y
    return lastremainder, lastx * (-1 if aa < 0 else 1), lasty * (-1 if bb < 0 else 1)

def modinv(a, m):
    g, x, y = extended_gcd(a, m)
    if g != 1:
        raise ValueError
    return x % m

def mult_inverse(e, lamda):
    ''' Using the Euclidian extended algorithm '''
    d = 0
    x1 = 0
    x2 = 1
    y1 = 1
    temp_lamda = lamda

    while e > 0:
        temp1 = temp_lamda/e
        temp2 = temp_lamda - temp1 * e
        temp_lamda = e
        e = temp2

        x = x2- temp1* x1
        y = d - temp1 * y1

        x2 = x1
        x1 = x
        d = y1
        y1 = y

    if temp_lamda == 1:
        return d + lamda

def keyGen():
    ''' Generate  Keypair '''
    i_p=randint(0,20)
    i_q=randint(0,20)
    # Instead of Asking the user for the prime Number which in case is not feasible,
    # generate two numbers which is much highly secure as it chooses higher primes
    while i_p==i_q:
        continue
    primes=PrimeGen(100)
    p=primes[i_p]
    q=primes[i_q]
    #computing n=p*q as a part of the RSA Algorithm
    n=p*q
    #Computing lamda(n), the Carmichael's totient Function.
    # In this case, the totient function is the LCM(lamda(p),lamda(q))=lamda(p-1,q-1)
    # On the Contrary We can also apply the Euler's totient's Function phi(n)
    #  which sometimes may result larger than expected
    lamda_n=int(lcm(p-1,q-1))
    e=randint(1,lamda_n)
    #checking the Following : whether e and lamda(n) are co-prime
    while math.gcd(e,lamda_n)!=1:
        e=randint(1,lamda_n)
    #Determine the modular Multiplicative Inverse
    d=modinv(e,lamda_n)
    #return the Key Pairs
    # Public Key pair : (e,n), private key pair:(d,n)
    return ((e,n),(d,n))

def encrypt(pk,message):
    """ Perform RSA Encryption Algorithm"""
    key, n = pk
    #Convert each letter in the plaintext to numbers based on the character using a^b mod m
    cipher = [(ord(char) ** key) % n for char in message]
    #Return the array of bytes
    return cipher

def decrypt(pk,cipher):
    '''Perform RSA Decryption Algorithm '''
    key, n = pk
    #Generate the plaintext based on the ciphertext and key using a^b mod m
    message = [chr((int(char) ** key) % n) for char in cipher]
    #Return the array of bytes as a string
    return ''.join(message)


import numpy as np

# Mathematical source for both Haar and Schauder(-Faber) bases: https://en.wikipedia.org/wiki/Haar_wavelet

# Numpy implementation of Haar wavelet generation on [0,1]
def haar_wavelet(time_grid):
    index_set1 = np.where( (0   <= time_grid) & (time_grid < 0.5) )
    index_set2 = np.where( (0.5 <= time_grid) & (time_grid < 1  ) )
    wavelet = np.zeros_like( time_grid )
    wavelet[ index_set1 ] = 1
    wavelet[ index_set2 ] = -1
    return wavelet

def haar_basis_element(n, k, time_grid):
    return (2.0**(0.5*n)) * haar_wavelet( (2**n)*time_grid- k)

# Returns a total of 2**n_max basis functions corresponding to
#   the Haar wavelets (n,k) corresponding to
#   n=0, ..., n_max-1 and
#   k=0, ..., 2**n - 1
def haar_basis( n_max, time_grid):
    basis = []
    # Constant function
    basis.append( np.ones_like(time_grid) )
    # The next ones (n>0)
    for n in range(n_max):
        basis = basis + [ haar_basis_element(n,k, time_grid) for k in range(2**n)]
    return basis

# Numpy implementation of Schauder basis generation on [0,1]

# Schauder elements are integrals of the Haar elements
def schauder_basis_element(n, k, time_grid):
    haar_element =  haar_basis_element(n, k, time_grid)
    element = np.zeros_like( haar_element )
    element[1:] = (2**(1+0.5*n)) * 0.5* ( haar_element[:-1] + haar_element[1:] )* (time_grid[1:] - time_grid[:-1])
    return element.cumsum()

def schauder_basis( n_max, time_grid):
    basis = []
    # Constant function
    basis.append( np.ones_like(time_grid) )
    # Identity map
    basis.append( np.copy(time_grid) )
    # The next ones (n>0)
    for n in range(n_max):
        basis = basis + [ schauder_basis_element(n,k, time_grid) for k in range(2**n)]
    return basis


import numpy as np
#import matplotlib.pyplot as plt

def Gradient_descent(x,y):
  m = 0
  c = 0
  L = 0.0001  # The learning Rate
  epochs = 1000  #The number of iterations to perform gradient descent
  n = len(X) # Number of elements in X
  for i in range(epochs):
    Y_pred = m*X + c  # The current predicted value of Y
    D_m = (-2/n) * sum(X * (Y - Y_pred))  # Derivative wrt m
    D_c = (-2/n) * sum(Y - Y_pred)  # Derivative wrt c
    m = m - L * D_m  # Update m
    c = c - L * D_c  # Update c
  # print(m,c,i)
  # print('Independent ',X)
  # print('Dependent ',Y)
  print('predicted values ',Y_pred)

if __name__ == '__main__':
  X = np.array([1,2,4,3,5])
  Y = np.array([1,3,3,2,5])
  Gradient_descent(X,Y)

'''
After completing the gradient descent algorithms we found the optimal m & c respectively .
m = 0.7652159238030152 c = 0.21788387785300492
'''


from typing import List
import sympy as sp
from sympy.matrices.dense import MutableDenseMatrix

import logging
logging.basicConfig(level=logging.WARN)

class InverseSolver:

    def __init__(self,mat) -> None:
        self.mat: MutableDenseMatrix = mat
        self.dim = self.mat.shape[0]
        self.course: List[MutableDenseMatrix] = []
        self.is_Invertible = False if self.mat.det() == 0 else True #可逆标志

    def getCourse(self) -> List[MutableDenseMatrix]:
        if self.is_Invertible == False:
            raise Exception('矩阵不可逆')

        self.course.clear()
        mat = self.mat.copy()
        AE: MutableDenseMatrix = mat.row_join(sp.eye(self.dim)) #将原矩阵与单位矩阵连接，用Gauss-Jordan消元法求逆
        self.course.append(AE)

        #Gauss消元
        for pivot_rowInd in range(self.dim-1): #最后一行不用选主元
            pivot = AE[pivot_rowInd, pivot_rowInd]
            #选主元，如果待消元的列不为0，则选为主元，否则选最开始一行的元素为主元
            if pivot == 0:
                for rowInd in range(pivot_rowInd+1, self.dim):
                    if AE[rowInd, pivot_rowInd] != 0:
                        AE = AE.elementary_row_op(op="n<->m", row1=pivot_rowInd, row2=rowInd)
                        self.course.append(AE.copy())
                        break
            #用主元所在的行消元
            for rowInd in range(pivot_rowInd+1, self.dim):
                if AE[rowInd, pivot_rowInd] != 0:
                    k = -sp.Rational(AE[rowInd, pivot_rowInd], pivot)
                    AE = AE.elementary_row_op(op='n->n+km', k=k, row1=rowInd, row2=pivot_rowInd)
                    self.course.append(AE.copy())

        #Jordan消元
        for pivot_rowInd in range(self.dim-1, 0, -1): #第一行不用选主元
            pivot = AE[pivot_rowInd, pivot_rowInd]
            #先将主元化为1
            if pivot != 1:
                AE = AE.elementary_row_op(op='n->kn', k=sp.Rational(1, pivot), row=pivot_rowInd)
                self.course.append(AE.copy())
            #用主元所在的行消元
            for rowInd in range(pivot_rowInd-1, -1, -1):
                if AE[rowInd, pivot_rowInd] != 0:
                    k = -AE[rowInd, pivot_rowInd]
                    AE = AE.elementary_row_op(op='n->n+km', k=k, row1=rowInd, row2=pivot_rowInd)
                    self.course.append(AE.copy())
        return self.course

    def getInverse(self) -> MutableDenseMatrix:
        return self.mat.inv()

if __name__ == '__main__':
    mat = sp.Matrix([[1,0,0],[0,1,0],[0,0,3]])
    logging.info(mat)
    solver = InverseSolver(mat)
    co = solver.getInverse()
    logging.info(co)
    logging.info(mat.inv())


## 1. Individual Values ##

import pandas as pd
houses = pd.read_table('AmesHousing_1.txt')

import matplotlib.pyplot as plt

houses['SalePrice'].plot.kde(xlim = (houses['SalePrice'].min(),
                                    houses['SalePrice'].max()
                                    )
                            )

st_dev = houses['SalePrice'].std(ddof = 0)
mean = houses['SalePrice'].mean()
plt.axvline(mean, color = 'Black', label = 'Mean')
plt.axvline(mean + st_dev, color = 'Red', label = 'Standard deviation')
plt.axvline(220000, color = 'Orange', label = '220000')
plt.legend()
very_expensive = False

## 2. Number of Standard Deviations ##

distance = 220000 - houses['SalePrice'].mean()
st_devs_away = distance / houses['SalePrice'].std(ddof=0)
print(distance)
print(st_devs_away)

## 3. Z-scores ##

min_val = houses['SalePrice'].min()
mean_val = houses['SalePrice'].mean()
max_val = houses['SalePrice'].max()
def z_score(value, array, bessel = 0):
    mean = sum(array) / len(array)

    from numpy import std
    st_dev = std(array, ddof = bessel)

    distance = value - mean
    z = distance / st_dev

    return z

min_z = z_score(min_val, houses['SalePrice'])
mean_z = z_score(mean_val, houses['SalePrice'])
max_z = z_score(max_val, houses['SalePrice'])

## 4. Locating Values in Different Distributions ##

def z_score(value, array, bessel = 0):
    mean = sum(array) / len(array)

    from numpy import std
    st_dev = std(array, ddof = bessel)

    distance = value - mean
    z = distance / st_dev

    return z

north_ames = houses[houses['Neighborhood']=='NAmes']
College_creek = houses[houses['Neighborhood']=='CollgCr']
old_town = houses[houses['Neighborhood']=='OldTown']
edwards = houses[houses['Neighborhood']=='Edwards']
somerset = houses[houses['Neighborhood']=='Somerst']

north_ames_z = z_score(200000,north_ames['SalePrice'])
College_creek_z = z_score(200000, College_creek['SalePrice'])
old_town_z = z_score(200000, old_town['SalePrice'])
edwards_z = z_score(200000, edwards['SalePrice'])
somerset_z = z_score(200000, somerset['SalePrice'])
print('north_ames ' + str(north_ames_z))
print('College_creek '+ str(College_creek_z))
print('old_town ' + str(old_town_z))
print('edwards ' + str(edwards_z))
print('somerset '+ str(somerset_z))

best_investment = 'College Creek'

## 5. Transforming Distributions ##

mean = houses['SalePrice'].mean()
st_dev = houses['SalePrice'].std(ddof = 0)
houses['z_prices'] = houses['SalePrice'].apply(
    lambda x: ((x - mean) / st_dev)
    )

z_mean_price = houses['z_prices'].mean()
print(z_mean_price)
z_stdev_price = houses['z_prices'].std(ddof=0)
print(z_stdev_price)

mean = houses['Lot Area'].mean()
st_dev = houses['Lot Area'].std(ddof=0)
houses['z_area'] = houses['Lot Area'].apply(
    lambda x: ((x - mean)/st_dev)
    )
z_mean_area = houses['z_area'].mean()
z_stdev_area = houses['z_area'].std(ddof=0)
print(z_mean_area)
print(z_stdev_area)

## 6. The Standard Distribution ##

from numpy import std, mean
population = [0,8,0,8]
mean_pop = mean(population)
stdev_pop = std(population, ddof = 0)

standardized_pop = []
for value in population:
    z = (value - mean_pop) / stdev_pop
    standardized_pop.append(z)

mean_z = mean(standardized_pop)
stdev_z = std(standardized_pop, ddof = 0)

## 7. Standardizing Samples ##

from numpy import std, mean
sample = [0,8,0,8]

x_bar = mean(sample)
s = std(sample, ddof = 1)

standardized_sample = []
for value in sample:
    z = (value - x_bar) / s
    standardized_sample.append(z)
stdev_sample = std(standardized_sample, ddof = 1)

## 8. Using Standardization for Comparisons ##

mean_index1 = houses['index_1'].mean()
stdev_index1 = houses['index_1'].std(ddof = 0)
houses['z_1'] = houses['index_1'].apply(lambda x:
                                      (x - mean_index1) / stdev_index1
                                     )

mean_index2 = houses['index_2'].mean()
stdev_index2 = houses['index_2'].std(ddof = 0)
houses['z_2'] = houses['index_2'].apply(lambda x:
                                      (x - mean_index2) / stdev_index2
                                     )

print(houses[['z_1', 'z_2']].head(2))
better = 'first'

## 9. Converting Back from Z-scores ##

mean = 50
st_dev = 10
houses['transformed'] = houses['z_merged'].apply(
                                lambda z: (z * st_dev + mean)
                                )
mean_transformed = houses['transformed'].mean()
stdev_transformed = houses['transformed'].std(ddof = 0)

5. Probability and Statistics/statistics-intermediate/Z-scores-309.py


# zeros
# The zeros tool returns a new array with a given shape and type filled with 0's.

# import numpy
# print numpy.zeros((1,2))   # Default type is float
# #Output : [[ 0.  0.]]

# print numpy.zeros((1,2), dtype = numpy.int)   # Type changes to int
# #Output : [[0 0]]

# ones
# The ones tool returns a new array with a given shape and type filled with 1's.

# import numpy
# print numpy.ones((1,2))   # Default type is float
# #Output : [[ 1.  1.]]

# print numpy.ones((1,2), dtype = numpy.int) #Type changes to int

# #Output : [[1 1]]

# Task
# You are given the shape of the array in the form of space-separated integers, each integer representing the size
# of different dimensions, your task is to print an array of the given shape and integer type using the tools numpy.zeros
# and numpy.ones.

# Input Format
# A single line containing the space-separated integers.

import numpy as np

abc = list(map(int, input().split()))

print(np.array(np.zeros(abc), dtype=np.int))
print(np.array(np.ones(abc), dtype=np.int))

# Output Format
# First, print the array using the numpy.zeros tool
# and then print the array with the numpy.ones tool.

# Sample Input 0
# 3 3 3

# Sample Output 0
# [[[0 0 0]
#   [0 0 0]
#   [0 0 0]]

#  [[0 0 0]
#   [0 0 0]
#   [0 0 0]]

#  [[0 0 0]
#   [0 0 0]
#   [0 0 0]]]
# [[[1 1 1]
#   [1 1 1]
#   [1 1 1]]

#  [[1 1 1]
#   [1 1 1]
#   [1 1 1]]

#  [[1 1 1]
#   [1 1 1]
#   [1 1 1]]]

# Explanation 0
# Print the array built using numpy.zeros and numpy.ones tools
# and you get the result as shown.


import matplotlib.pyplot as plt
import numpy as np

print("Formally we say that the Taylor series of a function is its representation as a power series. Let's see what this really means.")

print("\nConsider the cosine function from trigonometry. It has a nice smooth shape but if I asked you the cosine of some random number you'd probably have only a rough idea what the answer is. Traditionally it had to be worked out geometrically.")

print("\nWe can be more clever than that. There is one that we know for sure: cos(0) = 1. We will make an approximation starting with this requirement that approx(0) = 1.")

print("\nWhat should be used to make the approximation? Polynomials are very easy to evaluate and they can take lots of different shapes.")

print("\nSo lets start with the simplest polynomial... a horizontal line. There aren't many options here.")

print("\nObviously this isn't a very good quality approximation.")

print("\nTo make a better approximation we need to fit the curve of the cosine function. Fortunately that is a well known concept in calculus: the derivate.")

print("\nSo lets check the derivative of cos(0)... its zero. Our approximation also has a derivative of zero so we're matching!")

print("\n(Of course that means our approximation isn't any better.)")

print("\nBut the derivative doesn't tell the whole story. Lets check the second derivative of cos(0). That is -1!")

print("\nSo now we need to change our polynomial to give it a second derivative that is equal to -1. In order to have a second derivative it will have to be quadratic.")

print("\nSince polynomials are so well behaved this isn't a hard requirement to match. The power rule tells us that the second derivative of a quadratic polynomial is 2b, where b is the second coefficient. To make 2b = -1 we just have to say that b = -1/2.")

print("\nWe can continue in this way as long as desired. All of the odd terms will have a derivative of zero and so don't contribute. But each even term will make the approximation better.")

x = np.linspace(-5,5,100)
y = np.cos(x)

approx1 = [1]*len(x)
approx2 = [1-(i**2)/2 for i in x]
approx3 = [1-(i**2)/2+(i**4)/24 for i in x]
approx4 = [1-(i**2)/2+(i**4)/24-(i**6)/720 for i in x]

fig = plt.figure()
ax=fig.add_axes([0,0,1,1])
ax.set_axis_off()

plt.ylim(-2,2)
plt.plot(x,y)
plt.plot(x,approx1)
plt.title("First Approximation: 1")

fig = plt.figure()
ax=fig.add_axes([0,0,1,1])
ax.set_axis_off()

plt.ylim(-2,2)
plt.plot(x,y)
plt.plot(x,approx2)
plt.title("Second Approximation: 1 - (x^2)/2")

fig = plt.figure()
ax=fig.add_axes([0,0,1,1])
ax.set_axis_off()

plt.ylim(-2,2)
plt.plot(x,y)
plt.plot(x,approx3)
plt.title("Third Approximation: 1-(x^2)/2 + (x^4)/24")

fig = plt.figure()
ax=fig.add_axes([0,0,1,1])
ax.set_axis_off()

plt.ylim(-2,2)
plt.plot(x,y)
plt.plot(x,approx4)
plt.title("Fourth Approximation: 1-(x^2)/2 + (x^4)/24 - (x^6)/720")


from os import stat
import numpy as np
import cv2 # OpenCV
import math

from numpy.linalg.linalg import qr

class Rotations:
    def __init__(self):

        return

    @staticmethod
    def rot_mat_2_quat(R):
        """
        Convert a rotation from rotation matrix to quaternion representation. Assumes that the columns of the rotation matrix are orthonomal!
        """

        i, j, k = 0, 1, 2
        if R[1,1] > R[0,1]:
            i, j, k = 1, 2, 0

        if R[2,2] > R[i,i]:
            i, j, k = 2, 0, 1

        t = R[i,i] - (R[j,j] + R[k,k]) + 1
        q = np.array([0, 0, 0, 0])
        q[0] = R[k,j] - R[j,k]
        q[i+1] = t
        q[j+1] = R[i,j] + R[j,i]
        q[k+1] = R[k,i] + R[i,k]
        q = np.multiply(q, 0.5 / math.sqrt(t))

        return q

    @staticmethod
    def quat_2_rot_mat(q):
        """
        Convert a rotation from rotation matrix to quaternion representation.
        """

        s = np.linalg.norm(q) # s = 1 if the quaternion has unit length

        R = np.zeros((3,3))
        R[0,0] = 1 - 2 * s * (q[2]**2 + q[3]**2)
        R[0,1] = 2 * s * (q[1]*q[2] - q[3]*q[0])
        R[0,2] = 2 * s * (q[1]*q[3] + q[2]*q[0])
        R[1,0] = 2 * s * (q[1]*q[2] + q[3]*q[0])
        R[1,1] = 1 - 2 * s * (q[1]**2 + q[3]**2)
        R[1,2] = 2 * s * (q[2]*q[3] - q[1]*q[0])
        R[2,0] = 2 * s * (q[1]*q[3] - q[2]*q[0])
        R[2,1] = 2 * s * (q[2]*q[3] + q[1]*q[0])
        R[2,2] = 1 - 2 * s * (q[1]**2 + q[2]**2)

        R = Rotations.orthonormal_mat(R)

        return R

    @staticmethod
    def rot_mat_2_angle_axis(rot_mat):
        theta = math.acos((np.trace(rot_mat) - 1) / 2)

        return 1 / (2 * math.sin(theta)) * np.array([rot_mat[2,1] - rot_mat[1,2], rot_mat[0,2] - rot_mat[2,0], rot_mat[1,0] - rot_mat[0,1]]).reshape((3,1))

    @staticmethod
    def orthonormal_mat(mat):
        # Perform SVD on rotation matrix to make the rows and columns orthonormal
        U, _, V_transpose = np.linalg.svd(mat, full_matrices=True)
        return np.matmul(U, V_transpose)


'''
A robot is located at the top-left corner of a m x n grid
(marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time.
The robot is trying to reach the bottom-right corner of the
grid (marked 'Finish' in the diagram below).

How many possible unique paths are there?

Note: m and n will be at most 100.

Example 1:

Input: m = 3, n = 2
Output: 3
Explanation:
From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Right -> Down
2. Right -> Down -> Right
3. Down -> Right -> Right

Example 2:

Input: m = 7, n = 3
Output: 28
'''
import numpy as np

class Solution(object):
    def uniquePaths(self, m, n):
        """
        :type m: int
        :type n: int
        :rtype: int
        """
        grid = np.array([[0 for i in range(m)] for j in range(n)])
        grid[0, :] = 1
        grid[:, 0] = 1
        for i in range(1, m):
            for j in range(1, n):
                grid[j, i] = grid[j-1, i] + grid[j, i-1]
        print(grid)
        return grid[n-1, m-1]

sol = Solution()
m, n = [7,3]
print(sol.uniquePaths(m, n))


import numpy as np

x = np.array([[1, 3, 5], [7, 9, 11]])
y = np.array([[1, 2], [3, 4], [5, 6]])

print(np.dot(x, y)) #行列積

'''
[[ 35  44]
 [ 89 116]]
'''

print(x.dot(y)) #行列積

'''
[[ 35  44]
 [ 89 116]]
'''

print(x @ y) #行列積

'''
[[ 35  44]
 [ 89 116]]
'''


import torch
import numpy as np

def decayconst(tau, dt):
    if type(tau) == torch.Tensor:
        return torch.exp(-dt/tau)
    return float(np.exp(-dt/tau))

def sigmoid_project(value, bounds, framework = torch):
    return bounds[0] + (bounds[1]-bounds[0])/(1 + framework.exp(-value))

def expfilt(target, filtered, alpha):
    return alpha*filtered + (1 - alpha)*target

# Sunflower seed pattern:
# https://stackoverflow.com/questions/28567166/uniformly-distribute-x-points-inside-a-circle
phi = (np.sqrt(5)+1)/2 # The golden ratio
def sunflower(n, alpha=1):
    '''
    Returns a sunflower seed pattern of n points. alpha>1 causes truncation of
    radii at the outer edge, making a smoother boundary.
    '''
    b = torch.tensor(np.round(alpha * np.sqrt(n)))
    k = torch.arange(1,n+1)
    r = torch.sqrt(k-1/2) / torch.sqrt(n - (b+1)/2)
    r[r>1] = 1
    theta = 2*np.pi*k / phi**2
    return r, theta

def broadcast_outer(a,b):
    assert a.dim() == b.dim() == 1
    a = a.reshape(-1,1).expand(-1,len(b))
    b = b.reshape(1,-1)
    return a,b

def polar_dist(r1, th1, r2, th2):
    r1,r2 = broadcast_outer(r1,r2)
    th1,th2 = broadcast_outer(th1,th2)
    return torch.sqrt(r1**2 + r2**2 - 2*r1*r2*torch.cos(th1-th2))

def cartesian(r, theta):
    return r*theta.cos(), r*theta.sin()


import tkinter
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm

## HELPER FUNCTIONS ##

def plot_decision_function(model, ax, sv=True):
    ## Create a grid to evaluate the model
    xx, yy = np.meshgrid(np.linspace(-4, 4, 100),
                         np.linspace(-4, 4, 100))
    ## Evaluate the model
    Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    ## Plot the margin
    ax = plt.contour(xx, yy, Z, levels=[0], linewidths=2, colors='darkred')
    ax = plt.contourf(xx, yy, Z, levels=[0, Z.max()], colors='palevioletred')

    ax = plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), 0, 6), cmap=plt.cm.PuBu)

    if sv:
        ax = plt.scatter(model.support_vectors_[:, 0],
                       model.support_vectors_[:, 1],
                       s=300, linewidth=1, facecolors='none', edgecolors='black')

def plot_new_observations(X_train, X_new, anomaly, ax):
    _ = plt.scatter(X_train[:,0], X_train[:,1],
                    axes=ax, color='w', s=40, edgecolors='k',
                    label='Training data')

    _ = plt.scatter(X_new[:,0], X_new[:,1], axes=ax,
                    color='violet', s=40, edgecolors='k',
                    label='New regular observations')

    _ = plt.scatter(anomaly[:,0], anomaly[:,1], axes=ax,
                    color='gold', s=40, edgecolors='k',
                    label='New abnormal observations')

    _ = ax.legend()
    _ = ax.set_xlim([-4, 4])
    _ = ax.set_ylim([-4, 4])

n_points = 200

## generate a cluster of points for the training
np.random.seed(42)
X_train = 0.5 * np.random.randn(n_points, 2)

## Plot
fig, ax = plt.subplots(figsize=(12,8))
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax, color='w', s=40, edgecolors='k')
_ = ax.set_xlim([-4, 4])
_ = ax.set_ylim([-4, 4])

model = svm.OneClassSVM(nu=0.05, kernel="rbf", gamma=0.7)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))

## Reduce nu, i.e. weight more the slack variables
model = svm.OneClassSVM(nu=0.005, kernel="rbf", gamma=0.7)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_2 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_2, n_points))
# Training error = 5/200

"""
As we can see from the picture above, by reducing $\nu$ we are increasing the weight of the slack variables (because $C \sim 1/\nu$). This leads to a reduction of the error but increase the risk of overfitting.
"""

new_observation = 25
new_anomaly = 10

## Generate new observation from the same distribution
np.random.seed(42)
X_new = 0.5 * np.random.randn(new_observation, 2)

## Generate outliers
anomaly = np.random.uniform(low=-3, high=3, size=(new_anomaly, 2))

## Plot
fig, ax = plt.subplots(figsize=(12,8))
plot_new_observations(X_train, X_new, anomaly, ax)

y_new = model.predict(X_new)
y_anomaly = model.predict(anomaly)

err_new = y_new[y_new == -1].size
err_anomaly = y_anomaly[y_anomaly == -1].size

print("Fraction of new regular observations misclassified = {}/{}".format(err_new, new_observation))
print("Fraction of new abnormal observations correctly classified = {}/{}".format(err_anomaly, new_anomaly))

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=False)
plot_new_observations(X_train, X_new, anomaly, ax)
# Fraction of new regular observations misclassified = 0/25
# Fraction of new abnormal observations correctly classified = 10/10

# Let's try to make the problem a little bit more complex, i.e. use two clusters as positive examples

## generate two cluster for the training
np.random.seed(42)
X_train1 = 0.5 * np.random.randn(n_points//2, 2)+1.5
X_train2 = 0.5 * np.random.randn(n_points//2, 2)-1.5

X_train = np.r_[X_train1, X_train2]

## Plot
fig, ax = plt.subplots(figsize=(12,8))
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax, color='w', s=40, edgecolors='k')
_ = ax.set_xlim([-4, 4])
_ = ax.set_ylim([-4, 4])

model = svm.OneClassSVM(nu=0.06, kernel="rbf", gamma=0.5)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))

new_observation = 50
new_anomaly = 20

## Generate new observation from the same distribution
np.random.seed(42)
X_new_1 = 0.5 * np.random.randn(new_observation//2, 2)+1.5
X_new_2 = 0.5 * np.random.randn(new_observation//2, 2)-1.5
X_new = np.r_[X_new_1, X_new_2]

## Generate outliers
anomaly = np.random.uniform(low=-4, high=4, size=(new_anomaly, 2))

## Plot
fig, ax = plt.subplots(figsize=(12,8))
plot_new_observations(X_train, X_new, anomaly, ax)

y_new = model.predict(X_new)
y_anomaly = model.predict(anomaly)

err_new = y_new[y_new == -1].size
err_anomaly = y_anomaly[y_anomaly == -1].size

print("Fraction of new regular observations misclassified = {}/{}".format(err_new, new_observation))
print("Fraction of new abnormal observations correctly classified = {}/{}".format(err_anomaly, new_anomaly))

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=False)
plot_new_observations(X_train, X_new, anomaly, ax)
# Fraction of new regular observations misclassified = 2/50
# Fraction of new abnormal observations correctly classified = 18/20

# We can try to move the cluster closer

np.random.seed(42)
X_train1 = 0.5 * np.random.randn(n_points//2, 2)+0.9
X_train2 = 0.5 * np.random.randn(n_points//2, 2)-0.9

X_train = np.r_[X_train1, X_train2]

## Plot
fig, ax = plt.subplots(figsize=(12,8))
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax, color='w', s=40, edgecolors='k')
_ = ax.set_xlim([-4, 4])
_ = ax.set_ylim([-4, 4])

model = svm.OneClassSVM(nu=0.05, kernel="rbf", gamma=0.1)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))

# If we want to try to separate the two cluster we need to increase the value of gamma and $\nu$ but this increase the errors a lot.

model = svm.OneClassSVM(nu=0.3, kernel="rbf", gamma=1)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))


from sympy import *
from datetime import datetime

if __name__ == "__main__":
    """使用SymPy进行方程求解"""
    x = Symbol('x')
    # str = '100 * (1 + x)**3 - 200'
    str = '5000 * (1 + x)**1 + 5000*(1 + x)**1.6 + 5000*(1 + x)**1.2 - 15000'
    print(solve(str, x))
    # print(solve(str, x)[0].evalf())
    # print(solve(str, x))
    # print(solve(2 ** x - 4, x))


# https://docs.sympy.org/latest/tutorial/basic_operations.html

from sympy import *
x, y, z = symbols("x y z")

expr = cos(x) + 1
print(expr)
print(expr.subs(x, y))

print(expr.subs(x, 0))

expr = x**y
print(expr)
expr = expr.subs(y, x**y)
print(expr)
expr = expr.subs(y, x**x)
print(expr)

expr = sin(2*x) + cos(2*x)
print(expr)
print(type(expr))
print(expand_trig(expr))
print(expr.subs(sin(2*x), 2*sin(x)*cos(x)))

# "SymPy expressions are immutable"

expr = cos(x)
print(expr)
print(expr.subs(x, 0))
print(expr)
print(x)

expr = x**3 + 4*x*y - z
print(expr)
print(expr.subs([(x, 2), (y, 4), (z, 0)]))

expr = x**4 - 4*x**3 + 4*x**2 - 2*x + 3
print(expr)
replacements = [(x**i, y**i) for i in range(5) if i % 2 == 0]
print(replacements)
print(expr.subs(replacements))

# why not just do a simple loop?

replacements=[]

for i in range(5):
    if i % 2 == 0:
        replacements.append((x**i, y**i))

print(replacements)
print(expr.subs(replacements))

str_expr = "x**2 + 3*x - 1/2"
print(type(str_expr))
print(str_expr)
expr = sympify(str_expr)
print(type(expr))
print(expr)
print(expr.subs(x, 2))

expr = sqrt(8)
print(expr)
print(expr.evalf())

# substitute variable for number and
# evaluate to float

expr = cos(2*x)
print(expr)
print(expr.evalf(subs={x: 2.4}))

import numpy
a = numpy.arange(10)
expr = sin(x)
print(expr)
f = lambdify(x, expr, "numpy")
print(f(a))
print(type(f))

f = lambdify(x, expr, "math")
print(f(0.1))

def mysin(x):
    """
    My sine. Note that this is only accurate for small x.
    """
    return x

f = lambdify(x, expr, {"sin":mysin})
print(f(0.1))


import numpy as np

def euclidean_distance(a, b):
    """
    Calculate the Euclidean distance of two vectors
    """
    distance = 0
    for x, y in zip(a, b):
        distance += (x - y) ** 2

    return sqrt(distance)

def snake_distance(a, b):
    """
    Calculate the Manhattan distance of two vectors.
    """
    distance = 0
    for x, y in zip(a, b):
        distance += (x - y)

    return distance

def chebyshev_distance(a, b):
    """
    Calculate the Chebyshev distance of two vectors.
    """
    distances = []
    for x, y in zip(a, b):
        distances.append(abs(x - y))
    distance = max(distances)

    return distance

def entropy(feature_data):
    """
    Example playTennis entropy(["yes","yes","yes","yes","yes","yes","yes","yes","yes","no","no","no","no","no"])
    Calculates the entropy for a feature data.
    """
    if len(feature_data) == 0:
        return 1.02
    data_and_uniqe = np.unique(feature_data, return_inverse=True)
    pi = np.bincount(data_and_uniqe[1]).astype(np.float64)
    return np.sum(- pi/len(feature_data) * np.log2(pi/len(feature_data)))

def information_gain(sample, labels):
    """
    sample = [31, 34, 32, 20, 11, 10, 8, 23, 7, 21, 23]
    labels = [0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1]
    # we will sort the first array, and also the second one
    # labels = [1, 1, 1, 1, 1, | 0, 0, | 1, | 0, 0, 0]
    # we calculate the information gain from each split and we return the
    # higher value
    """

    global_entropy = entropy(labels)
    cardinality = float(sample.shape[0])

    indexes = np.argsort(sample)
    flag = labels[indexes][0]

    splits_indexes = {}

    for pos, element in enumerate(labels[indexes]):

        # adjacent examples with different classes, for example: 1.0 and 0.0
        if flag != element:
            mean = (sample[indexes][pos] - sample[indexes][pos-1])/2.0
            split = sample[indexes][pos - 1] + mean
            splits_indexes[pos] = str(split)
            flag = element

    # get best split
    best_gain = ('0', 0.0)

    for index in splits_indexes:

        # < less-than split
        # index is the same as cardinality of subsets
        total =+ (index/cardinality) * entropy(labels[indexes][:index])
        # >= more-equal-than split
        total += ((cardinality - index)/cardinality) * entropy(labels[indexes][index:])

        gain = global_entropy - total

        if gain > best_gain[1]:
            best_gain = (splits_indexes[index], gain)

    # a tuple of the split value and the gain
    return best_gain


# Monte Carlo Valuation of a European Option in a Black-Scholes World
# With implementation of Delta-based control variate method
# by Michal Lyskawinski
# 10/31/2016

from math import *
import numpy as np
import random
from scipy.stats import norm

def CBS(S, K, T, r, sigma,t, option):

    t2t = T-t # time to maturity
    # Calculations for the solution to BSM equation
    dplus = (1 / (sigma * sqrt(t2t))) * ((log(S / K)) + (r + ((sigma ** 2) / 2)) * t2t)
    dminus = (1 / (sigma * sqrt(t2t))) * ((log(S / K)) + (r - (sigma ** 2) / 2) * t2t)

    # Calculating price of Call and Put
    if option == 'Call':
        return S * norm.cdf(dplus) - K * exp(-r * t2t) * norm.cdf(dminus)
    elif option == 'Put':
        return K * exp(-r * t2t) * norm.cdf(-dminus) - S * norm.cdf(-dplus)

# Initialize parameters
S = 100
r = 0.06
sig = 0.2
T = 1
K = 100
N = 10
M = 100
div = 0.03  # In percentage
option = 'Call'

# Precompute constants
dt = T/N
nu = r - div - 0.5*(sig**2)
nudt = nu*dt
sigsdt = sig*sqrt(dt)
erddt = exp((r-div)*dt)

beta1 = -1

sum_CT = 0
sum_CT2 = 0

for j in range(1,M): # For each simulation

    St = S
    cv = 0

    for i in range(1,N): # For each time step
        t = (i-1)*dt
        delta = CBS(St,K,T,r,sig,t,option)
        eps = np.random.normal(0, 1)
        Stn = St*exp(nudt+sigsdt*eps)
        cv1 = cv + delta*(Stn-St*erddt)
        St = Stn

    if option == 'Call':
        CT = max(0, St - K) + beta1*cv1
        sum_CT = sum_CT + CT
        sum_CT2 = sum_CT2 + CT*CT
    elif option == 'Put':
        CT = max(0, K - St) + beta1*cv1
        sum_CT = sum_CT + CT
        sum_CT2 = sum_CT2 + CT * CT
    else:
        break

Value = sum_CT/M*exp(-r*T)
SD = sqrt((sum_CT2 - sum_CT*sum_CT/M)*exp(-2*r*T)/(M-1))
SE = SD/sqrt(M)

print('The Value of European',option,'Option is',Value)
print('The Standard Deviation of this Option is',SD)
print('The Standard Error in this case is',SE)


import numpy as np
import scipy.optimize as op

def Sigmoid(z):
    return 1/(1 + np.exp(-z))

def Gradient(theta,x,y):
    m , n = x.shape
    theta = theta.reshape((n,1))
    y = y.reshape((m,1))
    sigmoid_x_theta = Sigmoid(x.dot(theta))
    grad = ((x.T).dot(sigmoid_x_theta-y))/m
    return grad.flatten()

def CostFunc(theta,x,y):
    m,n = x.shape;
    theta = theta.reshape((n,1))
    y = y.reshape((m,1))
    term1 = np.log(Sigmoid(x.dot(theta)))
    term2 = np.log(1-Sigmoid(x.dot(theta)))
    term1 = term1.reshape((m,1))
    term2 = term2.reshape((m,1))
    term = y * term1 + (1 - y) * term2
    J = -((np.sum(term))/m)
    return J

'''
# intialize X and y
X = np.array([[1,2,3],[1,3,4]])
y = np.array([[1],[0]])

m , n = X.shape
initial_theta = np.zeros(len(n))
Result = op.minimize(fun = CostFunc,
                                x0 = initial_theta),
                                args = (X, y),
                                method = 'TNC',
                                jac = Gradient)
optimal_theta = Result.x
'''

def optimize(X, y, theta):
    Result = op.minimize(fun = CostFunc, x0 = theta, args=(X, y), method='TNC', jac= Gradient, options={'maxiter': 400})
    return Result.x


import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm, lognorm

colors = ['#342a77', '#ff464a', '#4881e9']
normal_mu = [0,0.5,1]
normal_sigma = [0.5,0.4,0.3]
x = np.arange(0.001, 7, .001) # for the log-normal PDF
x1 = np.arange(-2.5, 2.5, .001)  # for the normal PDF

fig, (ax1, ax2) = plt.subplots(nrows = 2, ncols = 1, figsize = (8,9))

for mu_n, sigma_n, color in zip(normal_mu, normal_sigma, colors):
     lognorm_pdf = lognorm.pdf(x, s=sigma_n, scale=np.exp(mu_n))
     r = lognorm.rvs(s=sigma_n, scale=np.exp(mu_n), size=15000)
     ax1.plot(x, lognorm_pdf, color=color, label=r"$\mu_n$ = " + str(mu_n) + r" - $\sigma_n$ = " + str(sigma_n))
     ax1.hist(r, bins='auto', density=True, color=color, edgecolor='#000000', alpha=0.5)
     logr= np.log(r)
     normal_pdf = norm.pdf(x1, loc= mu_n, scale = sigma_n)
     ax2.plot(x1, normal_pdf, color=color, label=r"$\mu_n$ = " + str(mu_n) + r" - $\sigma_n$ = " + str(sigma_n))
     ax2.hist(logr, bins='auto', density=True, color=color, edgecolor='#000000', alpha=0.5)
     my_mu = logr.mean()
     ax2.axvline(x=my_mu, color=color, linestyle="--", label=r"calculated $\mu_n$ = " + str(round(my_mu,3)))
     my_sigma = logr.std()
     print("Expected mean: " + str(mu_n) + " - Calculated mean: " + str(round(my_mu,3)))
     print("Expected std.dev.: " + str(sigma_n) + " - Calculated std.dev.: " + str(round(my_sigma,3)))

ax1.legend(title="log-normal distributions")
ax1.set_xlabel('x')
ax1.set_ylabel('Probability Density')
ax2.legend(title="normal distributions")
ax2.set_xlabel('ln(x)')
ax2.set_ylabel('Probability Density')

fig.tight_layout()


import sys

print(sys.version)

# We import NumPy into Python
import numpy as np

# We create a 1D ndarray that contains only integers
x = np.array([1, 2, 3, 4, 5])

# Let's print the ndarray we just created using the print() command
print('x = ', x)
print(type(x))

abc = np.array(['a','b','c','d','e','f','g','h','i','j'])
print("x: ", x)
print("shape: ", x.shape)
print("size: ", x.size)
print("type: ", x.dtype)

x = np.zeros((5,2))
print("x: ", x)
print("shape: ", x.shape)
print("size: ", x.size)
print("type: ", x.dtype)

# Using the Built-in functions you learned about in the
# previous lesson, create a 4 x 4 ndarray that only
# contains consecutive even numbers from 2 to 32 (inclusive)

x = np.arange(2,33,2).reshape((4,4))
print(x)

# Create a 5 x 5 ndarray with consecutive integers from 1 to 25 (inclusive).
# Afterwards use Boolean indexing to pick out only the odd numbers in the array

# Create a 5 x 5 ndarray with consecutive integers from 1 to 25 (inclusive).
X = np.arange(25).reshape((5,5))

print(X)
# Use Boolean indexing to pick out only the odd numbers in the array
Y = X[X % 2 != 0]
print(Y)

import numpy as np

# Use Broadcasting to create a 4 x 4 ndarray that has its first
# column full of 1s, its second column full of 2s, its third
# column full of 3s, etc..

# Do not change the name of this array.
# Please don't print anything from your code! The TEST RUN button below will print your array.

X = np.zeros((4,4)) + np.arange(1,5)
print(X)


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

def gaussian(x, mu, sig , plot_pref=False):
    '''
    A gaussian function (normalized similarly to scipy's function)
    RH 2021

    Args:
        x (np.ndarray): 1-D array of the x-axis of the kernel
        mu (float): center position on x-axis
        sig (float): standard deviation (sigma) of gaussian
        plot_pref (boolean): True/False or 1/0. Whether you'd like the kernel plotted

    Returns:
        gaus (np.ndarray): gaussian function (normalized) of x
        params_gaus (dict): dictionary containing the input params
    '''

    gaus = 1/(np.sqrt(2*np.pi)*sig)*np.exp((-((x-mu)/sig) **2)/2)

    if plot_pref:
        plt.figure()
        plt.plot(x , gaus)
        plt.xlabel('x')
        plt.title(f'$\mu$={mu}, $\sigma$={sig}')

    params_gaus = {
        "x": x,
        "mu": mu,
        "sig": sig,
    }

    return gaus , params_gaus

def generalised_logistic_function(x, a=0, k=1, b=1, v=1, q=1, c=1):
    '''
    Generalized logistic function
    See: https://en.wikipedia.org/wiki/Generalised_logistic_function
     for parameters and details
    RH 2021

    Args:
        a: the lower asymptote
        k: the upper asymptote when C=1
        b: the growth rate
        v: > 0, affects near which asymptote maximum growth occurs
        q: is related to the value Y (0). Center positions
        c: typically takes a value of 1

    Returns:
        output:
            Logistic function
     '''
    return a + (k-a) / (c + q*np.exp(-b*x))**(1/v)

def bounded_exponential(x, bounds=[1/10,10], base=2):
    """
    Bounded exponential function
    Computes an exponential function where when
     x is 0, the output is bounds[0], and when
     x is 1, the output is bounds[1]. The relative
     probability of outputting bounds[0[ over bounds[1]
     is base.
    Useful for randomly sampling over large ranges of
     values with an exponential resolution.
    RH 2021

    Args:
        x (float or np.ndarray):
            Float or 1-D array of the x-axis
        bounds (list):
            List of two floats, the lower and upper
             bounds
        base (float):
            The relative probability of outputting
             bounds[0] over bounds[1]

    Returns:
        output (float or np.ndarray):
            The bounded exponential output
    """

    range_additive = bounds[1] - bounds[0]

    return (((base**x - 1)/(base-1)) * range_additive) + bounds[0]

def polar2real(mag, angle):
    """
    Converts a polar coordinates to real coordinates
    RH 2021

    Args:
        mag (float or np.ndarray or torch.Tensor):
            Magnitude of the polar coordinates
        angle (float or np.ndarray or torch.Tensor):
            Angle of the polar coordinates

    Returns:
        output (float or np.ndarray or torch.Tensor):
    """
    if type(mag) is torch.Tensor:
        exp = torch.exp
    else:
        exp = np.exp
    return mag * exp(1j*angle)

def real2polar(x):
    """
    Converts a real coordinates to polar coordinates
    RH 2021

    Args:
        x (float or np.ndarray or torch.Tensor):
            Real coordinates

    Returns:
        Magnitude (float or np.ndarray or torch.Tensor):
            Magnitude of the polar coordinates
        Angle (float or np.ndarray or torch.Tensor):
            Angle of the polar coordinates
    """
    if type(x) is torch.Tensor:
        abs, angle = torch.abs, torch.angle
    else:
        abs, angle = np.abs, np.angle
    return abs(x), angle(x)


#Modified Euler / Heuns method:
#Runge Kutta Second (2) Order Method
import numpy as np

def dy(ynew,xnew,y,x,h):
    dyvalue = x**2 + y**2
    return dyvalue
 #Note: change the derivative function based on question!!!!!!  Example: y-x

y0 = 2.3  #float(input"what is the y(0)?")

h = 0.1  #float(input"h?")

x_final = 1.2 #float(input"x_final")

#initiating input variables
x = 1
y = y0
# remember to change yn+1 and xn+1 values if you already know them!!!
ynew = 0
xnew = 0
i = 0

#####################################################
iterations = x_final/h

while x <= x_final:
    k1 = dy(ynew,xnew,y,x,h)
    k2 = dy(ynew,xnew,y+k1*h,x+h,h)
    xnew = x + h
    ynew = y + (h/2)*(k1+k2)
    print("iteration:        ____            ")
    print(i)
    print("\n")
    print("x = ")
    print(xnew)
    print("\n")
    print("y = ")
    print(ynew)
    x = xnew
    y = ynew
    i+=1


"""数学分析"""

import math

try:
    import sympy
    import sympy.abc
    from sympy import oo  # oo是无穷大
    # from sympy.abc import x
except ImportError as e:
    print(e)

# 另一种实现方法
# def fourier_odd(fx, x_tuple=(sympy.abc.x, 0, sympy.pi), T=0, silent=False):
#     x = x_tuple[0]
#     a = x_tuple[1]
#     b = x_tuple[2]

def fourier_odd(fx, x=sympy.abc.x, a=0, b=sympy.pi, T=0, silent=False):
    """
    计算奇延拓Fourier级数的Fourier系数,[0,b]区间,T周期(默认2b)
    """
    from sympy import pi
    n = sympy.Symbol('n', integer=True, positive=True)  # 定义符号n 为正整数

    if T == 0:
        T = 2 * b

    a0 = 0
    an = 0
    bn = sympy.simplify(sympy.integrate(fx * sympy.sin(2 * pi * n * x / T), (x, a, b)) * 4 / T)

    if not silent:
        print(f"{a0/2=}")
        print(f"{an=}")
        print(f"{bn=}")
    return [a0, an, bn]

def fourier_even(fx, x=sympy.abc.x, a=0, b=sympy.pi, T=0, silent=False):
    """计算偶延拓Fourier级数的Fourier系数,[0,b]区间,T周期(默认2b)"""
    from sympy import pi
    n = sympy.Symbol('n', integer=True, positive=True)  # 定义符号n 为正整数

    if T == 0:
        T = 2 * b

    a0 = sympy.simplify(sympy.integrate(fx, (x, a, b)) * 4 / T)
    an = sympy.simplify(sympy.integrate(fx * sympy.cos(2 * pi * n * x / T), (x, a, b)) * 4 / T)
    bn = 0

    if not silent:
        print(f"{a0/2=}")
        print(f"{an=}")
        print(f"{bn=}")
    return [a0, an, bn]

def fourier_series(fx, x=sympy.abc.x, a=-sympy.pi, b=sympy.pi, T=0, silent=False):
    """
    计算Fourier级数的Fourier系数,[a,b]区间,T周期(默认b-a)

    如果不加参数x=sympy.abc.x(即x=sympy.symbols('x')，没有限定条件)，则fx中其他的x会因与此函数中的x不是同一个x对象而导致计算错误

    注:sympy.fourier_series(1+x)或sympy.fourier_series(1+x, (x,-pi,pi))会得到
    FourierSeries(x + 1, (x, -pi, pi), (1, SeqFormula(Piecewise((2*sin(_n*pi)/_n, (_n > -oo) & (_n < oo) & Ne(_n, 0)), (2*pi, True))*cos(_n*x)/pi, (_n, 1, oo)), SeqFormula(Piecewise((-2*pi*cos(_n*pi)/_n + 2*sin(_n*pi)/_n**2, (_n > -oo) & (_n < oo) & Ne(_n, 0)), (0, True))*sin(_n*x)/pi, (_n, 1, oo))))
    这一串看不懂的东西，不如我这个好用

    """
    from sympy import pi
    n = sympy.Symbol('n', integer=True, positive=True)  # 定义符号n 为正整数

    if T == 0:
        T = b - a

    a0 = sympy.simplify(sympy.integrate(fx, (x, a, b)) * 2 / T)
    an = sympy.simplify(sympy.integrate(fx * sympy.cos(2 * pi * n * x / T), (x, a, b)) * 2 / T)
    bn = sympy.simplify(sympy.integrate(fx * sympy.sin(2 * pi * n * x / T), (x, a, b)) * 2 / T)

    if not silent:
        print(f"{a0/2=}")
        print(f"{an=}")
        print(f"{bn=}")
    return [a0, an, bn]

def generalized_fourier(fx, phi_nx, phi_0x=0, x=sympy.abc.x, a=-sympy.pi, b=sympy.pi, silent=False):
    """广义Fourier级数,phi_0x=0时不单独计算a0"""

    a0 = sympy.integrate(fx * phi_0x, (x, a, b))
    a0 = sympy.simplify(a0)

    an = sympy.integrate(fx * phi_nx, (x, a, b))
    an = sympy.simplify(an)

    if not silent:
        print(f"{a0=}")
        print(f"{an=}")
    return [a0, an]

def fourier_transform(fx, x=sympy.abc.x, silent=False):
    r"""Fourier变换

    .. math:: F(k) = \int_{-\infty}^\infty f(x) e^{- i x k} \mathrm{d} x.
    """
    k = sympy.symbols('k', real=True)
    # 无用 fx.subs(x, x1)  # 将fx中的 默认x 替换成 x1(实数x),若fx原来就是实数x也不会报错
    Fk = sympy.integrate(fx * sympy.exp(-sympy.I * k * x), (x, -oo, oo))
    if not silent:
        print(Fk)
    return Fk

def fourier_transform_inverse(Fk, k=sympy.abc.k, silent=False):
    r"""Fourier变换的逆变换

        .. math:: F(k) = \int_{-\infty}^\infty f(x) e^{- i x k} \mathrm{d} x.
        """
    x = sympy.symbols('x', real=True)
    fx = sympy.integrate(Fk * sympy.exp(sympy.I * k * x), (k, -oo, oo)) / (2 * sympy.pi)
    if not silent:
        print(fx)
    return fx

def dot_product2(fx, gx, x=sympy.abc.x, a=-1, b=1, silent=True):
    """积分形式的内积"""
    from sympy import integrate
    o1 = integrate(fx * gx, (x, a, b))
    if not silent:
        print(f"fx*gx={o1}")
    return o1

def schmidt_orthogonalization(fix: list, n, e: list, x=sympy.abc.x, a=-1, b=1):
    """施密特正交化,输出为列表,n=len(fix),手动选择积分区间[a,b]"""
    from sympy import sqrt
    # dot_product=dot_product2 #另一种实现方法,手动选择内积

    if n == 1:
        e[0] = fix[0] / sqrt(dot_product2(fix[0], fix[0], x, a, b))
    else:
        schmidt_orthogonalization(fix, n - 1, e, x, a, b)
        e[n - 1] = fix[n - 1]
        for j in range(0, n - 1):
            e[n - 1] -= dot_product2(fix[n - 1], e[j], x, a, b) * e[j]
        e[n - 1] /= sqrt(dot_product2(e[n - 1], e[n - 1], x, a, b))

def schmidt(fix: list, n, e: list, dot_product=dot_product2, x=sympy.abc.x):
    """施密特正交化,输出在e列表,n=len(fix),手动选择内积dot_product"""
    from sympy import sqrt

    if n == 1:
        e[0] = fix[0] / sqrt(dot_product(fix[0], fix[0], x))
    else:
        schmidt(fix, n - 1, e, dot_product, x)
        e[n - 1] = fix[n - 1]
        for j in range(0, n - 1):
            e[n - 1] -= dot_product(fix[n - 1], e[j], x) * e[j]
        e[n - 1] /= sqrt(dot_product(e[n - 1], e[n - 1], x))

def schmidt_orthogonalization_list(fix: list, n, x=sympy.abc.x, a=-1, b=1, silent=True):
    """施密特正交化,输出为列表,n=len(fix),手动选择积分区间[a,b]"""
    e = []
    for i in range(0, n):
        e.append(None)
    schmidt_orthogonalization(fix, n, e, x, a, b)
    if not silent:
        print(e)
    return e

def schmidt_list(fix: list, n, dot_product=dot_product2, x=sympy.abc.x, silent=True):
    """施密特正交化,输出为列表,n=len(fix),手动选择内积dot_product"""
    e = []
    for i in range(0, n):
        e.append(None)
    schmidt(fix, n, e, dot_product, x)
    if not silent:
        print(e)
    return e

def convolution1(ft, gx_t, silent=True):
    """卷积,核心算法"""
    from sympy import integrate
    t = sympy.symbols('t', real=True)
    o1 = integrate(ft * gx_t, (t, -oo, oo))
    if not silent:
        print(f"(f*g)(x)={o1}")
    return o1

def convolution(fx: str, gx: str):
    """卷积"""
    t = sympy.symbols('t', real=True)
    ft1 = fx.replace('x', 't')
    gx_t1 = gx.replace('x', '(x-t)')
    exec("ft2=" + ft1)
    exec("gx_t2=" + gx_t1)
    o1 = [None]  # 必须用引用类型(比如列表),否则报错
    exec("o1[0]=convolution1(ft2, gx_t2)")
    return o1[0]

def legrendre(n, x=sympy.abc.x):
    """勒让德多项式"""
    pnx = 1 / (2 ** n * sympy.factorial(n)) * sympy.diff((x * x - 1) ** n, x, n)
    # print(pnx)
    return pnx

def legrendre_list(n, x=sympy.abc.x):
    """勒让德多项式表"""
    l1 = []
    for i in range(0, n):
        l1.append(legrendre(i, x))
    print(l1)  # [1, x, (3*x**2 - 1)/2, x*(5*x**2 - 3)/2, (8*x**4 + 24*x**2*(x**2 - 1) + 3*(x**2 - 1)**2)/8]
    return l1

def laplace_transform(ft, t=sympy.abc.t, silent=False):
    r"""Laplace变换

    .. math:: F(p) = \int_{0}^\infty f(x) e^{-pt} \mathrm{d} t.
    """
    p = sympy.symbols('p')
    Fp = sympy.integrate(ft * sympy.exp(-p * t), (t, 0, oo))
    if not silent:
        print(Fp)
    return Fp

if __name__ == '__main__':
    x,t = sympy.symbols("x,t")  # sympy.symbols("x")等价于sympy.abc.x
    fourier_even(1 + x, x)
    fourier_odd(1 + x, x)
    fourier_series(1 + x, x, 0, sympy.pi)
    fourier_transform(sympy.sin(x), x)
    laplace_transform(1)
    laplace_transform(t ** 2, t)
    # 输出结果中Piecewise表示分段函数,Ne(n,0)表示n!=0,True表示除去前面(Ne(n,0))的情况(即n==0)
    # 已修复n的类型,不会输出上述结果了


''' Good students always try to solve exercise on their own first and then look at the ready made solution
    I know you are an awesome student !! :)
    Hence you will look into this code only after you have done your due diligence.
    If you are not an awesome student who is full of laziness then only you will come here
    without writing single line of code on your own. In that case anyways you are going to
    face my anger with fire and fury !!!
'''

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
import math

def predict_using_sklean():
    df = pd.read_csv("test_scores.csv")
    r = LinearRegression()
    r.fit(df[['math']],df.cs)
    return r.coef_, r.intercept_

def gradient_descent(x,y):
    m_curr = 0
    b_curr = 0
    iterations = 1000000
    n = len(x)
    learning_rate = 0.0002

    cost_previous = 0

    for i in range(iterations):
        y_predicted = m_curr * x + b_curr
        cost = (1/n)*sum([value**2 for value in (y-y_predicted)])
        md = -(2/n)*sum(x*(y-y_predicted))
        bd = -(2/n)*sum(y-y_predicted)
        m_curr = m_curr - learning_rate * md
        b_curr = b_curr - learning_rate * bd
        if math.isclose(cost, cost_previous, rel_tol=1e-20):
            break
        cost_previous = cost
        print ("m {}, b {}, cost {}, iteration {}".format(m_curr,b_curr,cost, i))

    return m_curr, b_curr

if __name__ == "__main__":
    df = pd.read_csv("test_scores.csv")
    x = np.array(df.math)
    y = np.array(df.cs)

    m, b = gradient_descent(x,y)
    print("Using gradient descent function: Coef {} Intercept {}".format(m, b))

    m_sklearn, b_sklearn = predict_using_sklean()
    print("Using sklearn: Coef {} Intercept {}".format(m_sklearn,b_sklearn))