from package import redact_ex

from package import solve_explicit_ode

import numpy as np

EXERCISE_01 = """\
Make a program that is able to graphically solve the equation
\u2202T/\u2202t = \u03B1 \u2202\u00B2T/\u2202x\u00B2 = 0 using the Forward in Time, Centered in Space (FTCS)
scheme with Dirichlet boundary conditions u_0 = 0 and u_L = 10.
+ Consider different initial conditions.
+ Consider a new boundary condition: u_L = sin(t/2)
+ Consider null flux boundary conditions.\
"""

redact_ex(EXERCISE_01, 1)

# computation parameters
slices = 20
itern = 1000
plot_frequency = 0.05

# differentiation parameters
deltat = 1e-3
deltax = 1e-1

# problem parameters
alpha =  1

# helper variable
s = alpha*deltat/deltax**2

# grid creation and initial conditions
tprev = np.zeros(slices+1); tpprev = np.zeros(slices+1)
tprev[0] = 0; tpprev[0] = 0
tprev[slices] = 10; tpprev[slices] = 10

initial_conditions = [tprev, tpprev]

# boundary conditions
def boundary_conditions(lap, ngr, grid):

    slices = len(grid[0])-1

    ngr[0] = 0
    ngr[slices] = 10

    egrid = [ngr] + [r for r in grid]

    return egrid

# differentiation scheme
def ftcs(ngr, grid, s = s):

    slices = len(grid[0])-1

    for vl in range(1, slices):
        ngr[vl] = \
            grid[0][vl] + s*(grid[0][vl+1] - 2*grid[0][vl] + grid[0][vl-1])

    return ngr

print("Computing...", end='\n\n')
solve_explicit_ode(ftcs, initial_conditions, boundary_conditions,
    slices, itern, plot_frequency)

otprev = otpprev = np.zeros(slices+1)
otprev[8:12] = 5; otpprev[8:12] = 5
otprev[0] = 0; otpprev[0] = 0
otprev[slices] = 10; otpprev[slices] = 10

oinitial_conditions = [otprev, otpprev]

print("+ Computing...", end='\n\n')
solve_explicit_ode(ftcs, oinitial_conditions, boundary_conditions,
    slices, itern, plot_frequency)

deltat = 1e-2
def oboundary_conditions(lap, ngr, grid, deltat = deltat):

    slices = len(grid[0])-1

    ngr[0] = 0
    ngr[slices] = 10 * abs(np.cos((lap+1)*deltat/2))

    egrid = [ngr] + [r for r in grid]

    return egrid

# need to re-create the initial_conditions arrays
tprev = np.zeros(slices+1); tpprev = np.zeros(slices+1)
tprev[0] = 0; tpprev[0] = 0
tprev[slices] = 10; tpprev[slices] = 10

initial_conditions = [tprev, tpprev]

print("+ Computing...", end='\n\n')
solve_explicit_ode(ftcs, initial_conditions, oboundary_conditions,
    slices, itern, plot_frequency)
deltat = 1e-3

def oboundary_conditions(lap, ngr, grid, deltat = deltat):

    slices = len(grid[0])-1

    ngr[0] = ngr[1]
    ngr[slices] = ngr[slices-1]

    egrid = [ngr] + [r for r in grid]

    return egrid

otprev = otpprev = np.zeros(slices+1)
otprev[8:12] = 5; otpprev[8:12] = 5
otprev[0] = 0; otpprev[0] = 0

oinitial_conditions = [otprev, otpprev]

print("+ Computing...", end='\n\n')
solve_explicit_ode(ftcs, oinitial_conditions, oboundary_conditions,
    slices, itern, plot_frequency)


# -*- coding: utf-8 -*-
"""
Created on Thu Jun 23 00:45:04 2020

@author: Bhaskar Tripathi
"""

import numpy  as np
import math

'''Beale'''
def F1(x):
    x = np.asarray_chkfinite(x)
    x1 = x[0]
    x2 = x[1]
    s = ((1.5 - x1 + x1 * x2) ** 2.0 + (2.25 - x1 + x1 * x2 ** 2.0) ** 2.0+ (2.625 - x1 + x1 * x2 ** 3.0) ** 2.0)
    return s

'''Easom'''
def F2(x):
    x = np.asarray_chkfinite(x)
    term1=-np.cos(x[0])
    term2=np.cos(x[1])
    term3=np.exp(-1*((np.float_(x[0])-np.pi)**2 + (np.float_(x[1]) - np.pi) ** 2))
    s=term1*term2*term3
    return s

'''Matyas'''
def F3(x):
    #y=0.26*(x(1)^2+x(2)^2)-0.48*x(1)*x(2);
    x = np.asarray_chkfinite(x)
    x1 = x[0]
    x2 = x[1]
    s = 0.26 * (np.power(x1,2.0) + np.power(x2,2)) - 0.48 * np.multiply(x1,x2)
    #s= 0.26*(np.sum(np.power(x1,2),np.power(x2,2)))-0.48*np.multiply(x1,x2)
    return s

'''Powell'''
def F4(x):
    x = np.asarray_chkfinite(x)
    n = len(x)
    n4 = ((n + 3) // 4) * 4
    if n < n4:
        x = np.append( x, np.zeros( n4 - n ))
    x = x.reshape(( 4, -1 ))  # 4 rows: x[4i-3] [4i-2] [4i-1] [4i]
    term = np.empty_like( x )
    term[0] = x[0] + 10 * x[1]
    term[1] = np.sqrt(5) * (x[2] - x[3])
    term[2] = np.power((x[1] - 2 * x[2]),2)
    term[3] = np.sqrt(10) * np.power((x[0] - x[3]),2)
    return np.sum( term**2 )

''' Commenting this as it is not Powell function'''
#def F4(x):
#    o=np.max(np.abs(x))
#    return o

'''Schaffer No.1'''
def F5(x):
    x = np.asarray_chkfinite(x)
    x1 = x[0]
    x2 = x[1]
    s = 0.5 + ((np.power(np.sin(np.power(x1,2) - np.power(x2,2)),2) - 0.5))/ (1 + (0.001 * np.power(np.power(x1,2)+ np.power(x2,2),2)))
    return s

'''Schaffer No. 3'''
def F6(x):
    x = np.asarray_chkfinite(x)
    x1 = x[0]
    x2 = x[1]
    term1 = np.power(np.sin(np.cos(np.power(np.abs(x1),2) - np.power(x2,2))),2) - 0.5
    term2 = (1 + 0.001 * (np.power(x1,2) + np.power(x2,2))) **2
    s = 0.5 + (term1 / term2)
    return s

'''Schaffer No.4 '''
def F7(x):
    x = np.asarray_chkfinite(x)
    x1 = x[0]
    x2 = x[1]
    term1 = np.power(np.cos(np.sin(np.power(np.abs(x1),2) - np.power(x2,2))),2) - 0.5
    term2 = (1 + 0.001 * (np.power(x1,2) + np.power(x2,2))) **2
    s = 0.5 + term1 / term2;
    return s

'''Zakhrov'''
def F8(x):
    x = np.asarray_chkfinite(x)
    n = len(x);
    term1 = 0;
    term2 = 0;
    for i in range(0,n):
        term1 = term1 + (np.power(x[i],2))
        term2 = term2 + (0.5 * i * x[i])
    s = term1 + (np.power(term2,2)) + (np.power(term2,4))
    return s

'''Quartic'''
def F9(x):
    x = np.asarray_chkfinite(x)
    w=[i for i in range(len(x))]
    np.add(w,1)
    s = np.sum(np.multiply(w,np.power(x,4)) + np.random.uniform(0,1))
    return s

'''Schwefel 2.21 -To test'''
def F10(x):
    x = np.asarray_chkfinite(x)
    w=len(x)
    max=0.0
    for i in range(0,w):
        if abs(x[i])>max:
            max= abs(x[i])
            return max

'''Schwefel 2.22  -To test'''
def F11(x):
    x = np.asarray_chkfinite(x)
    term1 = 0.0
    term2 = 1.0
    w=len(x)
    for i in range(w):
        term1 += abs(x[i])
        term2 *= abs(x[i])
    s=term1 + term2
    return s

'''sphere'''
def F12( x ):
    s = np.asarray_chkfinite(x)
    return np.sum( s**2.0 )

'''step2'''
def F13( x ):
    x=np.asarray_chkfinite(x)
    s=np.sum(np.floor((x+.5))**2)
    return s

'''stepint'''
def F14(x):
    x=np.asarray_chkfinite(x)
    s = np.sum(np.ceil(x)) + 25
    return s

'''sumsquares'''
def F15(x):
    x=np.asarray_chkfinite(x)
    w=len(x)
    p=0
    for i in range(0,w):
        p=p+ np.multiply(i,np.power(x[i],2))
    s=p
    return s

'''ackley'''
def F16(x):
    term1 = 0.0
    term2 = 0.0
    x=np.asarray_chkfinite(x)
    for c in x:
        term1 += np.power(c,2.0)
        term2 += np.cos(2.0*np.pi*c)
    n = float(len(x))
    s= -20.0*np.exp(-0.2*np.sqrt(term1/n)) - np.exp(term2/n) + 20 + np.e
    return s

'''Bohachevsky no.2'''
def F17(x):
    x1 = x[0]
    x2 = x[1]
    s= (np.power(x1,2)) + (2*np.power(x2,2)) - (0.2* np.cos(0.3*np.pi*x1))*np.cos(4*np.pi*x2) + 0.3
    #for x1, x2 in zip(x[:-1], x[1:])),
    return s

'''Bohachevsky no.3'''
def F18(x):
    x1 = x[0]
    x2 = x[1]
    s=np.power(x1,2) + 2*np.power(x2,2) - 0.3*np.cos(3*np.pi*x1+ 4*np.pi*x2)
    return s

'''Crossintray'''
def F19(x):
    x1 = x[0]
    x2 = x[1]
#    x1=float(x1)
#    x2=float(x2)
    a=np.sqrt(np.power(x1,2)*np.power(x2,2))
    expo = np.abs(100 - (a/np.pi))
    inside = np.fabs(np.sin(x1) * np.sin(x2) * expo) + 1
    s = (-0.0001) * np.power(inside, 0.1)
    return s

'''Griewank'''
def F20(x):
    fr=4000
    x = np.asarray_chkfinite(x)
    n = len(x)
    j = np.arange( 1., n+1 )
    temp_sum = sum( np.power(x,2))
    p = np.prod(np.cos( x / np.sqrt(j) ))
    s=temp_sum/fr - p + 1
    return s

'''GoldStein-Price'''
def F21(x):
    x1 = x[0]
    x2 = x[1]
    s = (1+ (x1+x2+1)**2.0* (19- 14*x1+ 3 *x1** 2.0- 14*x2+ 6*x1*x2 + 3 *x2**2.0)) * (30+ (2*x1-3*x2)**2.0* (18-32*x1+12*x1**2.0+ 48*x2- 36 * x1*x2+ 27*x2**2.0))
    return s

'''Hartman 3'''
def F22(x):
    x = np.asarray_chkfinite(x)
    alpha = [1.0, 1.2, 3.0, 3.2]
    A = np.array([[3.0, 10.0, 30.0],
                       [0.1, 10.0, 35.0],
                       [3.0, 10.0, 30.0],
                       [0.1, 10.0, 35.0]])
    P = 0.0001 * np.array([[3689, 1170, 2673],
                                [4699, 4387, 7470],
                                [1090, 8732, 5547],
                                [381, 5743, 8828]])
    extern_sum = 0
    for i in range(4):
        intern_sum=0
        for j in range(3):
            intern_sum = intern_sum+ A[i, j]*np.power((x[j]-P[i, j]),2)
        extern_sum = extern_sum + alpha[i] * np.exp(-intern_sum)
        s=-extern_sum
    return s

'''Hartman 6'''
def F23(x):
    alpha = [1.00, 1.20, 3.00, 3.20]
    A = np.array([[10.00, 3.00, 17.00, 3.50, 1.70, 8.00],
                      [0.05, 10.00, 17.00, 0.10, 8.00, 14.00],
                      [3.00, 3.50, 1.70, 10.00, 17.00, 8.00],
                      [17.00, 8.00, 0.05, 10.00, 0.10, 14.00]])
    P = 0.0001 * np.array([[1312, 1696, 5569, 124, 8283, 5886],
                               [2329, 4135, 8307, 3736, 1004, 9991],
                               [2348, 1451, 3522, 2883, 3047, 6650],
                               [4047, 8828, 8732, 5743, 1091, 381]])
    extern_sum = 0
    for i in range(4):
        intern_sum = 0
        for j in range(6):
            intern_sum = intern_sum + A[i, j] * (x[j] - P[i, j]) ** 2
            extern_sum = extern_sum + alpha[i] * np.exp(-intern_sum)
            s=-extern_sum
            return s

'''Penalized no 1 - BUG TO BE FIXED AND TESTED'''
def F24(x):
    x = np.asarray_chkfinite(x)
    w = len(x)
#    term1=np.pi/w
#    term2 = (10*((np.sin(np.pi*(1+(x[0]+1)/4)))**2))
#    term3= sum((((x[0:w-1]+1)/4)**2))
#    term4 = (1+10*((np.sin(np.pi*(1+(x[1:w]+1)/4))))**2)
#    term5 = ((x[w-1]+1)/4)**2
#    term6 = sum(fun_penalized(x,10,100,4))
#    s= (term1)*(term2+((term3)*(term4))+(term5))+ (term6)
#    (term1)*(term2)+term3*(term4)+(term5)+term6
    #y = (pi/dim)*(10*((sin(pi*(1+(x(1)+1)/4)))^2)+sum((((x(1:dim-1)+1)./4).^2).*...
     #   (1+10.*((sin(pi.*(1+(x(2:dim)+1)./4)))).^2))+((x(dim)+1)/4)^2)+sum(Ufun(x,5,100,4));

    s = (np.pi/w)*(10*((np.sin(np.pi*(1+(x[0]+1)/4)))**2)+np.sum((((x[0:w-1]+1)/4)**2)*
                   (1+10*((np.sin(np.pi*(1+(x[1:w]+1)/4))))**2))+((x[w-1]+1)/4)*2)+sum(fun_penalized(x,5,100,4))
    return s

'''Penalized no 2'''
def F25(x):
    x = np.asarray_chkfinite(x)
    w = len(x)
    #a=10;k=100;m=4;
    #ufun = lambda x: k*((x-a)**m)*(x>a)+k*((-x-a)**m)*(x<(-a))

    s =0.1*((np.sin(3*np.pi*x[0]))**2+ sum((x[0:w-1]-1)**2 * (1+(np.sin(3*np.pi*x[1:w]))**2))+
            ((x[w-1]-1)**2)*(1+(np.sin(2*np.pi*x[w-1]))**2))+sum(fun_penalized(x,5,100,4))
    #s =0.1*((np.sin(3*np.pi*x[0]))**2+ sum((x[0:w-1]-1)**2 * (1+(np.sin(3*np.pi*x[1:w]))**2))+
     #       ((x[w-1]-1)**2)*(1+(np.sin(2*np.pi*x[w-1]))**2))+ufun(x)
    return s

def fun_penalized(x,a,k,m):
    s=k*((x-a)**m)*(x>a)+k*((-x-a)**m)*(x<(-a))
    return s

'''Perm'''
def F26(x):
    x = np.asarray_chkfinite(x)
    b = 0.5;d = len(x);s = 0;
    for ii in range(d):
        i = 0
        for jj in range(d):
            xj = x[jj]
            i = i + ((jj+1)+b) * xj**(ii+1) - (1/(jj+1))**(ii+1)
        s=s+(i)**2
    return s

'''Powersum'''
def F27(x):
    x = np.asarray_chkfinite(x)
    n = len(x)
    s = 0
    b=[8,18,44,114]
    for k in range( 1, n+1 ):
        bk = b[ min( k - 1, len(b) - 1 )]  # ?
        s += (sum( x**k ) - bk) **2  # dim 10 huge, 100 overflows
    return s

'''Shubert'''
def F28(x):
    x = np.asarray_chkfinite(x)
    x1 = x[0]
    x2 = x[1]
    s1 = 0
    s2 = 0
    for i in range(5):
        n1= (i+1)*np.cos((i+2)*x1+(i+1))
        n2= (i+1)*np.cos((i+2)*x2+(i+1))
        s1=s1+n1
        s2=s2+n2
    s = s1 * s2
    return s

'''Alpine No.1'''
def F29(x):
    x = np.asarray_chkfinite(x)
    s = sum(abs(x*np.sin(x)+0.1*x))
    return s

'''BohachevskyNo.1'''
def F30(x):
    x = np.asarray_chkfinite(x)
    x1=x[0]
    x2=x[1]
    s= (x1**2) + (2*x2**2) - (0.3 * np.cos(3*np.pi*x1)) - (0.4* np.cos(4*np.pi*x2)) + 0.7
    return s

'''Booth'''
def F31(x):
     x = np.asarray_chkfinite(x)
     s=(x[0]**2*x[1]-7)**2+(2*x[0]+x[1]-5)**2
     return s

'''Branin'''
def F32(x):
    a = 1; b =  5.1/(4*np.pi**2); c =  5/np.pi; r = 6; p =  10; t =  1/(8*np.pi);
    x = np.asarray_chkfinite(x)
    x1=x[0]
    x2=x[1]
    term1 = a * (x2 - b*x1**2 + c*x1 - r)**2
    term2 = p*(1-t)*np.cos(x1)
    s = term1 + term2 + p
    return s

'''Michalewics 2'''
def F33(x):
    n = 2; m = 10;s = 0;
    x = np.asarray_chkfinite(x)
    for i in range(n):
        s= s+ np.sin(x[i])*(np.sin((i+1)* x[i]** 2/np.pi))**(2*m)
    return -s

'''Michalewics 5'''
def F34(x):
    n = 5; m = 10;s = 0;
    x = np.asarray_chkfinite(x)
    for i in range(n):
        s= s+ np.sin(x[i])*(np.sin((i+1)* x[i]** 2/np.pi))**(2*m)
    return -s

'''Michalewics 5'''
def F35(x):
    n = 10; m = 10;s = 0;
    x = np.asarray_chkfinite(x)
    for i in range(n):
        s= s+ np.sin(x[i])*(np.sin((i+1)* x[i]** 2/np.pi))**(2*m)
    return -s

'''Rastrigin'''
def F36(x):
    x = np.asarray_chkfinite(x)
    n = len(x)
    return 10*n + sum( x**2 - 10 * np.cos( 2 * np.pi * x ))

'''INPUTS -
    clasific_regr= 1 OR 0. 1= Classification problem, 0 = Regression Problem;
    kernel - linear, polynomial or rbf kernel ;
    X_tr,X_te,y_tr,y_te = Train and Test data ;
    c_pos,gamma_pos = C and Gamma value array
   OUTPUT - Fitness
'''

'''INPUTS -
    clasific_regr= 1 OR 0. 1= Classification problem, 0 = Regression Problem;
    kernel - linear, polynomial or rbf kernel ;
    X_tr,X_te,y_tr,y_te = Train and Test data ;
    c_pos,gamma_pos = C and Gamma value array
   OUTPUT - Fitness
'''
'''def F36(clasific_regr,svm_kernel,X_tr,X_te,y_tr,y_te ,c_pos,gamma_pos):
    clasific_regr=1
    svm_kernel='rbf'
    if clasific_regr == 1:
        rbf_regressor = svm.SVR(kernel = svm_kernel, C = c_pos, gamma = gamma_pos).fit(X_tr, y_tr)
        cv_scores = cross_val_score(rbf_regressor,X_te,y_te,cv =3,scoring = 'neg_mean_squared_error') # Taking negated value of MSE
        #To minimize the error rate
        scores = cv_scores.mean()
        fitness = (1 - scores)*100
    else:
        rbf_svm = svm.SVC(kernel = svm_kernel, C = c_pos, gamma = gamma_pos).fit(X_tr, y_tr)  #svm
        cv_scores = cross_val_score(rbf_svm,X_te,y_te,cv =3,scoring = 'accuracy')
        #To minimize the error rate
        scores = cv_scores.mean()
        fitness = (1 - scores)*100
    return fitness
'''

def ProblemsDetails(a):
    param = {  "F1":["F1",-4.5,4.5,2],
         "F2":["F2",-10,10,2],
         "F3":["F3",-10,10,2],
         "F4":["F4",-4,5,24],
         "F5":["F5",-100,100,2],#To be changed - All below values
         "F6":["F6",-100,100,2],
         "F7":["F7",-100,100,2],
         "F8":["F8",-5,10,30],
         "F9":["F9",-1.28,1.28,30],
         "F10":["F10",-100,100,30],
         "F11":["F11",-10,10,30],
         "F12":["F12",-100,100,30],
         "F13":["F13",-100,100,30],
         "F14":["F14",-5.12,5.12,5],
         "F15":["F15",-10,10,30],
         "F16":["F16",-32,32,30],
         "F17":["F17",-10,10,2],
         "F18":["F18",-100,100,2],
         "F19":["F19",-10,10,2],
         "F20":["F20",-600,600,30],
         "F21":["F21",-2,2,2],
         "F22":["F22",0,1,3],
         "F23":["F23",0,1,6],
         "F24":["F24",-50,50,30],
         "F25":["F25",-50,50,30],
         "F26":["F26",-4,4,4],
         "F27":["F27",0,4,4],
         "F28":["F28",-10,10,2],
         "F29":["F29",-10,10,30],
         "F30":["F30",-100,100,2],
         "F31":["F31",-10,10,2],
         "F32":["F32",-5,5,2],
         "F33":["F33",0,np.pi,2],
         "F34":["F34",0,np.pi,5],
         "F35":["F35",0,np.pi,10],
         "F36":["F36",-5.12,5.12,30]
}
    return param.get(a, "nothing")


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

import edhec_risk_kit as erk

df_quiz_1 = pd.read_csv('course_1/Portfolios_Formed_on_ME_monthly_EW.csv',
                        header=0, index_col=0, parse_dates=True, na_values=99.99)

columns = ['Lo 20', 'Hi 20']
df_quiz_1 = df_quiz_1[columns]/100
df_quiz_1.index = pd.to_datetime(df_quiz_1.index, format="%Y%m").to_period('M')

## se calcula el retorno por mes con la productoria y luego elevando a 1/# de meses
## luego se anualiza elevando a 1/12
ret_per_month = (df_quiz_1 + 1).prod() ** (1/df_quiz_1.shape[0]) - 1
annualized_return = (ret_per_month + 1) ** (12) -1

## para calcular la volatilidad se calcula la desviación estándar de los retornos
## y se multiplica por la raíz cuadrada de un año (12)
annualized_vol = df_quiz_1.std()*np.sqrt(12)

## para calcular el retorno anualizado sobre un intervalo de tiempo aprovechamos
## la estructura del índice como una fecha en el DataFrame
df_1999_2015 = df_quiz_1['1999':'2015']
ret_per_month = (df_1999_2015 + 1).prod() ** (1/df_1995_2015.shape[0]) - 1
annualized_return = (ret_per_month + 1) ** (12) -1

annualized_vol = df_1999_2015.std()*np.sqrt(12)

## calculating monthly drawdown
drawdowns_small = erk.drawdown(df_1999_2015['Lo 20'])
drawdowns_large = erk.drawdown(df_1999_2015['Hi 20'])

## obtaining the maximum drawdown and when it happened
print(np.round(drawdowns_small.min()*-1, 4))
print(np.round(drawdowns_large.min()*-1, 4))
print(drawdowns_small.idxmin())
print(drawdowns_large.idxmin())

## cargamos los datos para los demás puntos
df_edhec = erk.get_hfi_returns()

## cálculo de la semidesviación estándar (la que aplica sólo cuando tenemos retornos negativos)
semidev = erk.semideviation(df_edhec['2009':'2018'])

## quién ha tenido la semidesviación más alta entre 2009 y 2018
print(semidev.idxmax())
## la más baja
print(semidev.idxmin())

## ahora calculamos la asimetría
skew = erk.skewness(df_edhec['2009':'2018'])
print(skew.idxmin())

# ... y la kurtosis
kurt = erk.kurtosis(df_edhec['2000':])
print(kurt.idxmax())


## 1. Equal interval scales ##

car_speeds = [10,20,30,50,20]
earthquake_intensities = [2,7,4,5,8]
mean_car_speed = sum(car_speeds)/len(car_speeds)
mean_earthquake_intensities = sum(earthquake_intensities)/len(earthquake_intensities)

## 2. Discrete and continuous scales ##

day_numbers = [1,2,3,4,5,6,7]
snail_crawl_length = [.5,2,5,10,1,.25,4]
cars_in_parking_lot = [5,6,4,2,1,7,8]

import matplotlib.pyplot as plt
plt.plot(day_numbers,snail_crawl_length)
plt.show()
plt.plot(day_numbers,cars_in_parking_lot)
plt.show()

## 3. Scale starting points ##

fahrenheit_degrees = [32, 64, 78, 102]
yearly_town_population = [100,102,103,110,105,120]
population_zero = yearly_town_population
degrees_zero = [f + 459.67 for f in fahrenheit_degrees]

## 4. Ordinal scales ##

# Results from our survey on how many cigarettes people smoke per day
survey_responses = ["none", "some", "a lot", "none", "a few", "none", "none"]
survey_scale = ["none", "a few", "some", "a lot"]
survey_numbers = [survey_scale.index(response) for response in survey_responses]
average_smoking = sum(survey_numbers) / len(survey_numbers)

## 5. Categorical scales ##

# Let's say that these lists are both columns in a matrix.  Index 0 in both is the first row, and so on.
gender = ["male", "female", "female", "male", "male", "female"]
savings = [1200, 5000, 3400, 2400, 2800, 4100]
male_count = 0
female_count = 0
m_c = 0
f_c = 0
for i in range(len(gender)):
    if gender[i] == 'male':
        male_count += savings[i]
        m_c +=1
    elif gender[i] == 'female':
        female_count += savings[i]
        f_c +=1
male_savings = male_count/m_c
female_savings = female_count/f_c

## 6. Frequency histograms ##

# Let's say that we watch cars drive by, and measure average speed in miles per hour
average_speed = [10, 20, 25, 27, 28, 22, 15, 18, 17]
import matplotlib.pyplot as plt
plt.hist(average_speed)
plt.show()

# Let's say we measure student test scores, from 0-100
student_scores = [15, 80, 95, 100, 45, 75, 65]
plt.hist(student_scores)
plt.show()

## 7. Histogram bins ##

average_speed = [10, 20, 25, 27, 28, 22, 15, 18, 17]
import matplotlib.pyplot as plt
plt.hist(average_speed, bins=6)
plt.show()

# As you can see, the values in the list are counted into the nearest bin.
# If we have less bins, each bin will have a higher count (because it's showing all of the values that fall into it)
# With more bins, the counts are less, because each bin contains less values.
plt.hist(average_speed, bins=4)
plt.show()
plt.hist(average_speed, bins=2)
plt.show()

## 8. Skew ##

# Some numpy arrays are already loaded in, and we'll make some plots with them.
# The arrays contain student test scores from an exam, on a 0-100 scale.
import matplotlib.pyplot as plt

# See how there is a long slope to the left?
# The data is concentrated in the right part of the distribution, but some people also scored poorly.
# This plot is negatively skewed.
plt.hist(test_scores_negative)
plt.show()

# This plot has a long slope to the right.
# Most students did poorly, but a few did really well.
# This is positively skewed.
plt.hist(test_scores_positive)
plt.show()

# This plot has no skew either way -- most of the values are in the center, and there is no long slope either way.
# Is is an unskewed distribution.
plt.hist(test_scores_normal)
plt.show()

# We can test how skewed a distribution is using the skew function.
# A positive value means positive skew, a negative value means negative skew, and close to zero means no skew.
from scipy.stats import skew
from scipy.stats import skew
positive_skew = skew(test_scores_positive)
negative_skew = skew(test_scores_negative)
no_skew = skew(test_scores_normal)

## 9. Kurtosis ##

import matplotlib.pyplot as plt

# This plot is short, making it platykurtic
# See how the values are distributed pretty evenly, and there isn't a huge cluster in the middle?
# Students had a wide variation in their performance
plt.hist(test_scores_platy)
plt.show()

# This plot is tall, and is leptokurtic
# Most students did very similarly to the others
plt.hist(test_scores_lepto)
plt.show()

# This plot is in between, and is mesokurtic
plt.hist(test_scores_meso)
plt.show()

# We can measure kurtosis with the kurtosis function
# Negative values indicate platykurtic distributions, positive values indicate leptokurtic distributions, and values close to 0 are mesokurtic
from scipy.stats import kurtosis
kurt_platy = kurtosis(test_scores_platy)
kurt_lepto = kurtosis(test_scores_lepto)
kurt_meso = kurtosis(test_scores_meso)

## 10. Modality ##

import matplotlib.pyplot as plt

# This plot has one mode, making it unimodal
plt.hist(test_scores_uni)
plt.show()

# This plot has two peaks, and is bimodal
# This could happen if one group of students learned the material, and one learned something else, for example.
plt.hist(test_scores_bi)
plt.show()

# More than one peak means that the plot is multimodal
# We can't easily measure the modality of a plot, like we can with kurtosis or skew.
# Often, the best way to detect multimodality is to observe the plot.
plt.hist(test_scores_multi)
plt.show()

## 11. Measures of central tendency ##

import matplotlib.pyplot as plt
# We're going to put a line over our plot that shows the mean.
# This is the same histogram we plotted for skew a few screens ago
plt.hist(test_scores_normal)
# We can use the .mean() method of a numpy array to compute the mean
mean_test_score = test_scores_normal.mean()
# The axvline function will plot a vertical line over an existing plot
plt.axvline(mean_test_score)

# Now we can show the plot and clear the figure
plt.show()

# When we plot test_scores_negative, a very negatively skewed distribution, we see that the mean is pulled to the left by the small values there.
# The mean can be changed easily by very large or very small values.
# This can make it misleading with distributions that are very skewed, when we expect the mean to be the center.
plt.hist(test_scores_negative)
plt.axvline(test_scores_negative.mean())
plt.show()

# We can do the same with the positive side
# See how the very high values pull the mean to the right more than you would expect?
plt.hist(test_scores_positive)
plt.axvline(test_scores_positive.mean())
plt.show()
mean_normal = sum(test_scores_normal)/len(test_scores_normal)
mean_negative = sum(test_scores_negative)/len(test_scores_negative)
mean_positive = sum(test_scores_positive)/len(test_scores_positive)

## 12. The median ##

# Let's plot the mean and median side by side in a negatively skewed distribution.
# Sadly, arrays don't have a nice median method, so we have to use a numpy function to compute it.
import numpy
import matplotlib.pyplot as plt

# Plot the histogram
plt.hist(test_scores_negative)
# Compute the median
median = numpy.median(test_scores_negative)

# Plot the median in blue (the color argument of "b" means blue)
plt.axvline(median, color="b")

# Plot the mean in red
plt.axvline(test_scores_negative.mean(), color="r")

# See how the median is further to the right than the mean?
# It's less sensitive to outliers, and isn't pulled to the left.
plt.show()
plt.hist(test_scores_positive)
p_median = numpy.median(test_scores_positive)
plt.axvline(p_median,color = 'b')
plt.axvline(test_scores_positive.mean(),color = 'r')
plt.show()

## 14. Cleaning missing data ##

import pandas
f = "titanic_survival.csv"
titanic_survival = pandas.read_csv(f)

# Luckily, pandas dataframes have a method that can drop rows that have missing data
# Let's look at how big the dataframe is first
print(titanic_survival.shape)

# There were 1310 passengers on the titanic, according to our data
# Now let's drop any row with missing data
# The dropna method on dataframes will do this for us
# Any row with any missing values will be removed
new_titanic_survival = titanic_survival.dropna()

# Hmm, it looks like we were too zealous with dropping rows with na values
# We now have no rows in our dataframe
# This is because some of the later columns, which aren't immediately relevant to our analysis, have a lot of missing values
print(new_titanic_survival.shape)

# We can use the subset keyword argument to the dropna method to only drop rows if there are na values in certain columns
# This will drop any row where the embarkation port (where people boarded the Titanic), or cabin number is missing
new_titanic_survival = titanic_survival.dropna(subset=["embarked", "cabin"])

# This is much better -- we have removed only the rows that we need to remove.
print(new_titanic_survival.shape)
new_titanic_survival = titanic_survival.dropna(subset=['age','sex'])
print(new_titanic_survival.shape)

## 15. Plotting age ##

# The cleaned up data has been loaded into the new_titanic_survival variable
import matplotlib.pyplot as plt
import numpy
plt.hist(new_titanic_survival['age'])
p_median = numpy.median(new_titanic_survival['age'])
plt.axvline(p_median,color = 'b')
plt.axvline(new_titanic_survival['age'].mean(),color = 'r')
plt.show()

## 16. Calculating indices for age ##

from scipy.stats import skew,kurtosis

mean_age = new_titanic_survival['age'].mean()
median_age = numpy.median(new_titanic_survival['age'])
skew_age = skew(new_titanic_survival['age'])
kurtosis_age = kurtosis(new_titanic_survival['age'])

Probability Statistics Beginner/Introduction to statistics-11.py


import numpy as np
import math as m
from prettytable import PrettyTable
import matplotlib.pyplot as mpl

# -------------------------------|ATENÇÃO|-------------------------------#
# Estou usando uma biblioteca chamada: "Prettytable", para deixar a     #
# tabela com uma aparência bonita.                                      #
#                                                                       #
# Para instalar no windows é só digitar no prompt de comando (admin):   #
# "pip3 install PTable".                                                #
# Para instalar no linux é só digitar no terminal:                      #
# "sudo pip3 install PTable".                                           #
#                                                                       #
# -----------------------------------------------------------------------#
# Para visualizar os resustados de alguma função ou método, apenas      #
# descomente a função desejada.                                         #
#                                                                       #
# E caso queira ver o grafico dessa função, descomente a função grafico #
# correspondente, e também a plot_graf no final.                        #
#                                                                       #
# -----------------------------------------------------------------------#

tabela1 = PrettyTable()
tabela1.title = "Newton"
tabela1.field_names = ["i", "x", "f(x)"]

tabela2 = PrettyTable()
tabela2.title = "Bisseccao"
tabela2.field_names = ["i", "x", "f(x)"]

tabela3 = PrettyTable()
tabela3.title = "Ponto fixo"
tabela3.field_names = ["i", "x", "f(x)"]

def f1(x):
    return m.cos(x) + 1

def d_f1(x):
    return -(m.sin(x))

def f2(x):
    return 10 + (x - 2) ** 2 - (10 * m.cos(2 * m.pi * x))

def d_f2(x):
    return 2 * x - 4 + (20 * m.pi * m.sin(2 * m.pi * x))

def f3(x):
    return x ** 3 - 3 * x ** 2 * 2 ** -x + 3 * x * 4 ** -x - 8 ** -x

def d_f3(x):
    return (
        3 * x ** 2
        - 6 * x * 2 ** -x
        - m.log(2) * 2 ** -x * -3 * x ** 2
        + 3 * 4 ** -x
        - m.log(4) * 4 ** -x * 3 * x
        + m.log(8) * 8 ** -x
    )

def f4(x):
    return m.sin(x) * m.sin((x ** 2) / m.pi)

def d_f4(x):
    return m.cos(x) * m.sin(x ** 2 / m.pi) + 2 * x / m.pi * m.cos(x ** 2 / m.pi) * m.sin(x)

def f5(x):
    return (x - 1.44) ** 5

def d_f5(x):
    return 5 * (x - 1.44) ** 4

def grafico(x, y, i):
    mpl.figure()
    mpl.subplot(211)
    mpl.plot(i, x, label="x -> f(x) = 0", color="r")
    mpl.ylabel("f(x)/x")
    mpl.legend()
    mpl.grid(True)
    mpl.subplot(212)
    mpl.plot(i, y, label="f(x) -> 0", color="b")
    mpl.xlabel("i")
    mpl.ylabel("f(x)/x")
    mpl.legend()
    mpl.grid(True)

def graficof(x, y):
    mpl.figure()
    mpl.plot(x, y, color="g")
    mpl.xlabel("x")
    mpl.ylabel("f(x)")
    mpl.grid(True)

def plot_graf():
    mpl.show()

def funcao(fx, xv, fxv, a, b):

    for i in range(a * 10, b * 10 + 1):
        j = i / 10
        xv.append(j)
        fxv.append(fx(j))

    # print(xv, fxv, sep = '\n')
    # print("")

def bissecao(a, b, fx, xv, fxv, iv):

    for i in range(1000):

        m = (a + b) / 2
        xv.append(m)
        fxv.append(fx(m))
        iv.append(i)
        tabela2.add_row([iv[i], xv[i], fxv[i]])

        if abs(fx(m)) < erro:
            print(tabela2, end="\n")
            tabela2.clear_rows()
            break

        else:
            if ((fx(m)) * (fx(b))) < 0:
                a = m
            else:
                b = m

def newton(fx, dfx, x, xv, fxv, iv):

    xv.append(x)
    fxv.append(f1(x))
    iv.append(0)
    tabela1.add_row([iv[0], xv[0], fxv[0]])

    for i in range(1, 1000):

        xv.append(xv[i - 1] - (fx(xv[i - 1]) / dfx(xv[i - 1])))
        fxv.append(fx(xv[i]))
        iv.append(i)

        tabela1.add_row([iv[i], xv[i], fxv[i]])

        if abs(fxv[i]) < erro:
            print(tabela1, end="\n")
            tabela1.clear_rows()
            break

def ponto_fixo1(x):

    xp1.append(x)
    fxp1.append(f1(x))
    ip1.append(0)

    tabela3.add_row([ip1[0], xp1[0], fxp1[0]])

    for i in range(1, 1000):

        xp1.append(xp1[i - 1] + f1(xp1[i - 1]) * 14)
        fxp1.append(f1(xp1[i]))
        ip1.append(i)

        tabela3.add_row([ip1[i], xp1[i], fxp1[i]])

        if abs(fxp1[i]) < erro:
            print(tabela3, end="\n")
            tabela3.clear_rows()
            break

def ponto_fixo2(x):

    xp2.append(x)
    fxp2.append(f2(x))
    ip2.append(0)

    tabela3.add_row([ip2[0], xp2[0], fxp2[0]])

    for i in range(1, 2000):

        xp2.append(xp2[i - 1] - f2(xp2[i - 1]) / 20)
        fxp2.append(f2(xp2[i]))
        ip2.append(i)

        tabela3.add_row([ip2[i], xp2[i], fxp2[i]])

        if abs(fxp2[i]) < erro:
            print(tabela3, end="\n")
            tabela3.clear_rows()
            break

def ponto_fixo3(x):

    xp3.append(x)
    fxp3.append(f3(x))
    ip3.append(0)

    tabela3.add_row([ip3[0], xp3[0], fxp3[0]])

    for i in range(1, 1000):

        xp3.append(xp3[i - 1] - f3(xp3[i - 1]) * 5)
        fxp3.append(f3(xp3[i]))
        ip3.append(i)

        tabela3.add_row([ip3[i], xp3[i], fxp3[i]])

        if abs(fxp3[i]) < erro:
            print(tabela3, end="\n")
            tabela3.clear_rows()
            break

def ponto_fixo4(x):

    xp4.append(x)
    fxp4.append(f4(x))
    ip4.append(0)

    tabela3.add_row([ip4[0], xp4[0], fxp4[0]])

    for i in range(1, 1000):

        xp4.append(xp4[i - 1] - f4(xp4[i - 1]) * 4 / (xp4[i - 1]))
        fxp4.append(f4(xp4[i]))
        ip4.append(i)

        tabela3.add_row([ip4[i], xp4[i], fxp4[i]])

        if abs(fxp4[i]) < erro:
            print(tabela3, end="\n")
            tabela3.clear_rows()
            break

def ponto_fixo5(x):

    xp5.append(x)
    fxp5.append(f5(x))
    ip5.append(0)

    tabela3.add_row([ip5[0], xp5[0], fxp5[0]])

    for i in range(1, 1000):

        xp5.append(xp5[i - 1] - f5(xp5[i - 1]) * 50 / xp5[i - 1] ** 2)
        fxp5.append(f5(xp5[i]))
        ip5.append(i)

        tabela3.add_row([ip5[i], xp5[i], fxp5[i]])

        if abs(fxp5[i]) < erro:
            print(tabela3, end="\n")
            tabela3.clear_rows()
            break

xp1 = []
fxp1 = []
ip1 = []
xp2 = []
fxp2 = []
ip2 = []
xp3 = []
fxp3 = []
ip3 = []
xp4 = []
fxp4 = []
ip4 = []
xp5 = []
fxp5 = []
ip5 = []
xn1 = []
fxn1 = []
in1 = []
xn2 = []
fxn2 = []
in2 = []
xn3 = []
fxn3 = []
in3 = []
xn4 = []
fxn4 = []
in4 = []
xn5 = []
fxn5 = []
in5 = []
x1 = []
fx1 = []
x2 = []
fx2 = []
x3 = []
fx3 = []
x4 = []
fx4 = []
x5 = []
fx5 = []
xb3 = []
fxb3 = []
ib3 = []
xb4 = []
fxb4 = []
ib4 = []
xb5 = []
fxb5 = []
ib5 = []
erro = 1e-6

# ----------------|Newton|----------------#
# newton(f1, d_f1, 1, xn1, fxn1, in1)
# newton(f2, d_f2, 1.6, xn2, fxn2, in2)
# newton(f3, d_f3, 4, xn3, fxn3, in3)
# newton(f4, d_f4, 1, xn4, fxn4, in4)
# newton(f5, d_f5, 1.6, xn5, fxn5, in5)

# grafico(xn1, fxn1, in1)
# grafico(xn2, fxn2, in2)
# grafico(xn3, fxn3, in3)
# grafico(xn4, fxn4, in4)
# grafico(xn5, fxn5, in5)
# ----------------------------------------#

# --------------|Ponto fixo|--------------#
# ponto_fixo1(3)
# ponto_fixo2(2.1)
# ponto_fixo3(1)
# ponto_fixo4(2)
# ponto_fixo5(2)

# grafico(xp1, fxp1, ip1)
# grafico(xp2, fxp2, ip2)
# grafico(xp3, fxp3, ip3)
# grafico(xp4, fxp4, ip4)
# grafico(xp5, fxp5, ip5)
# ----------------------------------------#

# --------------|Bissecçõo|---------------#
# bissecao(0, 3, f3, xb3, fxb3, ib3)
# bissecao(-1, 2, f4, xb4, fxb4, ib4)
# bissecao(0, 10, f5, xb5, fxb5, ib3)

# grafico(xb3, fxb3, ib3)
# grafico(xb4, fxb4, ib4)
# grafico(xb5, fxb5, ib3)
# ----------------------------------------#

# ---------------|Funções|----------------#
# funcao(f1, x1, fx1, -10, 10)
# funcao(f2, x2, fx2, -12, 16)
# funcao(f3, x3, fx3, -3, 10)
# funcao(f4, x4, fx4, -10, 10)
# funcao(f5, x5, fx5, -7, 10)

# graficof(x1, fx1)
# graficof(x2, fx2)
# graficof(x3, fx3)
# graficof(x4, fx4)
# graficof(x5, fx5)
# ----------------------------------------#

# plot_graf()


import numpy as np
from scipy.interpolate import interp1d

def inverse_transform_sampling(data, bins, nsamples):
    """
    Extract random samples from a distribution
    using inverse transform sampling. This code
    uses a Univariate Spline (without smoothing)
    to extract the inverse CDF for an arbitrary
    continuous distribution.

    Parameters
    ------------------
    data : array_like
        1D distribution to be sampled.
    bins : int or array_like
        Histogram bins for `data` (see np.histogram).
    nsamples : int
        Number of samples (>1) to be returned.

    Returns
    --------------------
    samples : np.array
        Samples according to the distribution of `data`.
        Length matches nsamples.
    """
    hist, binedges = np.histogram(data, bins=bins, density=True)
    cum_values = np.zeros_like(binedges)
    cum_values[1:] = np.cumsum(hist*(binedges[1:]-binedges[:-1]))
    inversecdf = interp1d(cum_values, binedges)
    uval = np.random.rand(nsamples)
    samples = inversecdf(uval)
    return samples

if __name__=='__main__':
    data = np.random.normal(loc=140,scale=26,size=1000)
    samples = inverse_transform_sampling(data, bins=40, nsamples=500)

    import matplotlib.pyplot as plt
    plt.figure()
    plt.hist(data, bins=40, label='Raw Data')
    plt.hist(samples, histtype='step', color='orange', linewidth=3, label='Samples')
    plt.legend(loc='best')
    plt.show()


# Author : Antoine Broyelle
# Licence : MIT
# inspired by : ENSIMAG - 4MMFDAS6 : Data mining and multivariate statistical analysis
# http://ensimag.grenoble-inp.fr/cursus-ingenieur/data-mining-and-multivariate-statistical-analysis-4mmfdas6-842620.kjsp

import numpy as np

class LinReg:
    '''Linear Regression. Least-squares optimisation'''

    def __init__(self, inputs, targets):
        '''Constructor'''
        # target is a column vector
        #if np.ndim(targets) != 2 or np.shape(targets)[1] != 1:
        #    raise Exception('[linreg][init] targets variable must be a column vector')

        self.inputs = self.__addColumn(inputs) # add bias
        self.targets = targets

        self.W = np.linalg.inv(np.dot(np.transpose(self.inputs), self.inputs))
        self.W = np.dot(np.dot(self.W, np.transpose(self.inputs)), targets)

    def __addColumn(self, inputs):
        '''Insert column with ones'''
        return np.concatenate((inputs, np.ones((np.shape(inputs)[0],1))),axis=1)

    def eval(self, inputs=None):
        if inputs is None:
            inputs = self.inputs
        else:
            inputs = self.__addColumn(inputs) # add bias
        return np.dot(inputs, self.W)

    def error(self, inputs=None, targets=None):
        '''RSS. Residual sum of squares'''
        if inputs is None or targets is None:
            inputs = self.inputs
            targets = self.targets
        else:
            inputs = self.__addColumn(inputs) # add bias

        output = np.dot(inputs, self.W)
        error = np.sum((output - targets)**2)
        return error

if __name__ == "__main__":
    inputs = np.array([[0,0], [0,1], [1,0], [1,1]])
    ORtargets = np.array([[0], [1], [1], [1]])

    linreg = LinReg(inputs, ORtargets)

    output = linreg.eval(inputs)
    print "Regression :"
    print output
    print "Error :", linreg.error()

    classification = np.where(output >= 0.5, 1, 0)
    print "Classification :"
    print classification


import time

import numpy as np
import matplotlib.pyplot as plt
from util_plot import AddPlot

is_3d = True
ax, point_dim = AddPlot(is_3d).returns

n_cluster_points = 100
cluster_shape = (n_cluster_points, point_dim)
means_K = 4
early_stop_distant = 0.01

# Randomly generate clusters using Normal Distribution (randn)
rand_points1 = 0 + 2 * np.random.randn(*cluster_shape)
rand_points2 = 10 + 3 * np.random.randn(*cluster_shape)
if is_3d:
    rand_points3 = [20, 0, 5] + 2 * np.random.randn(*cluster_shape)
    rand_points4 = [30, 20, 10] + 1.5 * np.random.randn(*cluster_shape)
else:
    rand_points3 = [20, 0] + 2 * np.random.randn(*cluster_shape)
    rand_points4 = [30, 20] + 1.5 * np.random.randn(*cluster_shape)
points_num = n_cluster_points * means_K
all_points = np.concatenate((rand_points1, rand_points2, rand_points3, rand_points4))

# We random choice centroids from points.
rand_indexes = np.random.choice(all_points.shape[0], means_K, replace=False)
centroids = all_points[rand_indexes]

distant_arr = np.zeros((points_num, means_K))
iteration = 0
# Loop until converged.
while True:
    points_per_cluster = [[] for _ in range(means_K)]

    # Get minimal distance between each centroid and each point,
    # then put in the points array per cluster.
    for point in all_points:
        distant_per_centroid = []
        # Calculate distance per point with each centroid.
        for cp in centroids:
            distant_per_centroid.append(np.linalg.norm(cp - point))
        cluster_idx = np.argmin(distant_per_centroid)
        points_per_cluster[cluster_idx].append(point)
        ax.scatter(*point, color="C%d" % cluster_idx, s=50, alpha=0.1)

    centroids_distant = 0

    # Calculate the mean of each cluster to got the new centroid of each cluster
    for i, (cluster, old_centroid) in enumerate(zip(points_per_cluster, centroids)):
        ax.scatter(*old_centroid, color="C%d" % i, marker='+', s=200)
        new_centroid = np.mean(cluster, axis=0)
        # Record distance between new and old centroid in oder to determine convergence.
        centroids_distant += np.linalg.norm(new_centroid - old_centroid)
        ax.scatter(*new_centroid, color="C%d" % i, s=200, marker="*")
        # Update centroid
        centroids[i] = new_centroid

    plt.title("iteration %s, Updated distant=%.4f" % (iteration, centroids_distant))
    plt.draw()
    plt.pause(0.001)
    iteration += 1

    # We assume converged when centroid no more updated
    if centroids_distant < early_stop_distant:
        break
    # Only clear figure on non-last figure
    ax.clear()
# Show end plot forever
plt.show()


import matplotlib.pyplot as plt
import numpy as np
import _pickle as pickle

class PolynomialRegression:

    def __init__(self, K):
        assert 1 <= K <= 10, f"K must be between 1 and 10. Got: {K}"
        self.K = K

        # Remember that we have K weights and 1 bias.
        self.parameters = np.ones((K + 1, 1), dtype=np.float)

    def predict(self, X):

        """ This method predicts the output of the given input data using the model parameters.
        Recall that a polynomial function is defined as:

        Given a single scalar input x,
        f(x) = w_0 + w_1 * x + w_2 * x^2 + ... + w_K * x^K

        Args:
        - X (ndarray (shape: (N, 1))): A N-column vector consisting N scalar input data.

        Output:
        - ndarray (shape: (N, 1)): A N-column vector consisting N scalar output data.
        """
        assert X.shape == (X.shape[0], 1)

        # ===================================================

        # Obtain the K-th degree polynomial as a Matrix with the shape (len(train_X), K) with all ones
        poly_x = np.ones((len(X), self.K))
        # Raise col to obtain a polynomial basis function in the form as seen in class
        for i in range(0, self.K):
            poly_x[:, i] = np.power(np.transpose(X), self.K - i)

        # model.fit will get the parameters needed to predict Y based off of X using matrix multiplication
        parameters = self.parameters
        # Extract the parameters except for the 1st term
        weights = np.delete(parameters, 0, axis=0)
        # First term is the bias
        bias = parameters[0][0]
        # Use vector form of equation form class to predict a value of Y based off of bias and weights
        pred_Y = np.matmul(poly_x, weights) + bias

        return pred_Y

        # ====================================================

    def fit(self, train_X, train_Y):
        """ This method fits the model parameters, given the training inputs and outputs.

        Recall that the optimal parameters are:
        parameters = (X^{T}X)^{-1}X^{T}Y

        NOTE: Do not forget that we are using polynomial basis functions!

        Args:
        - train_X (ndarray (shape: (N, 1))): A N-column vector consisting N scalar training inputs.
        - train_Y (ndarray (shape: (N, 1))): A N-column vector consisting N scalar training outputs.
        """
        assert train_X.shape == train_Y.shape and train_X.shape == (train_X.shape[0],
                                                                    1), f"input and/or output has incorrect shape (train_X: {train_X.shape}, train_Y: {train_Y.shape})."
        assert train_X.shape[
                   0] >= self.K, f"require more data points to fit a polynomial (train_X: {train_X.shape}, K: {self.K}). Do you know why?"

        # ====================================================

        # Obtain the K-th degree polynomial as a Matrix with the shape (len(train_X), K) with all ones
        poly_x = np.ones((len(train_X), self.K + 1))
        # Raise col to obtain a polynomial basis function in the form as seen in class
        for i in range(0, self.K + 1):
            poly_x[:, -1 - i] = np.power(np.transpose(train_X), self.K - i)

        # Obtain the pinv. Using pinv because if det of poly_x^t * poly_x is 0 we will get a singular matrix
        pen_B = np.linalg.pinv(poly_x)
        # Compute optimal weights using formula from class
        w = np.matmul(pen_B, train_Y)

        self.parameters = w

        return self.parameters

        # ====================================================
        assert self.parameters.shape == (self.K + 1, 1)

    def fit_with_l2_regularization(self, train_X, train_Y, l2_coef):
        """ This method fits the model parameters with L2 regularization, given the training inputs and outputs.
        Recall that the optimal parameters are:
        parameters = (X^{T}X + lambda*I)^{-1}X^{T}Y2

        NOTE: Do not forget that we are using polynomial basis functions!

        Args:
        - train_X (ndarray (shape: (N, 1))): A N-column vector consisting N scalar training inputs.
        - train_Y (ndarray (shape: (N, 1))): A N-column vector consisting N scalar training outputs.
        - l2_coef (float): The lambda term that decides how much regularization we want.
        """
        assert train_X.shape == train_Y.shape and train_X.shape == (train_X.shape[0],
        1), f"input and/or output has incorrect shape (train_X: {train_X.shape}, train_Y: {train_Y.shape})."
        # ====================================================

        # Obtain the K-th degree polynomial as a Matrix with the shape (len(train_X), K) with all ones
        poly_x = np.ones((len(train_X), self.K + 1))
        # Raise col to obtain a polynomial basis function in the form as seen in class
        for i in range(0, self.K + 1):
            poly_x[:, -1 - i] = np.power(np.transpose(train_X), self.K - i)

        # Calculate w_bar by using formula (X^{T}X + lambda*I)^{-1}X^{T}Y2
        # Get size of identity matrix required then proceed with regularization
        B_2 = np.matmul(np.transpose(poly_x), poly_x)
        Id_size = np.ma.size(B_2, 0)
        Id = np.identity(Id_size)
        # Compute the m-p pinv with regularization
        reg_B = B_2 + l2_coef * Id

        # If coeff is 0, there will be no regularization of the optimal weights

        # Apply formula from class to obtain the weight
        inv_B = np.linalg.inv(reg_B)
        p_inv = np.matmul(inv_B, np.transpose(poly_x))
        w_bar = np.matmul(p_inv, train_Y)

        # Form a vector for the parameters
        self.parameters = w_bar

        return self.parameters

         # ====================================================

        assert self.parameters.shape == (self.K + 1, 1)

    def compute_mse(self, X, observed_Y):
        """ This method computes the mean squared error.

        Args:
        - X (ndarray (shape: (N, 1))): A N-column vector consisting N scalar inputs.
        - observed_Y (ndarray (shape: (N, 1))): A N-column vector consisting N scalar observed outputs.

        Output:
        - (float): The mean squared error between the predicted Y and the observed Y.
        """
        assert X.shape == observed_Y.shape and X.shape == (
            X.shape[0], 1), f"input and/or output has incorrect shape (X: {X.shape}, observed_Y: {observed_Y.shape})."
        pred_Y = self.predict(X)
        return ((pred_Y - observed_Y) ** 2).mean()

if __name__ == "__main__":
    # You can use linear regression to check whether your implementation is correct.
    # NOTE: This is just a quick check but does not cover all cases.
    model = PolynomialRegression(K=1)
    train_X = np.expand_dims(np.arange(10), axis=1)
    train_Y = np.expand_dims(np.arange(10), axis=1)

    model.fit(train_X, train_Y)
    optimal_parameters = np.array([[0.], [1.]])
    print("Correct optimal weights: {}".format(np.allclose(model.parameters, optimal_parameters)))

    pred_Y = model.predict(train_X)
    print("Correct predictions: {}".format(np.allclose(pred_Y, train_Y)))

    # Change parameters to suboptimal for next test
    model.parameters += 1
    model.fit_with_l2_regularization(train_X, train_Y, l2_coef=0)
    print("Correct optimal weights: {}".format(np.allclose(model.parameters, optimal_parameters)))

    # Regularization pulls the weights closer to 0.
    optimal_parameters = np.array([[0.0231303], [0.99460293]])
    model.fit_with_l2_regularization(train_X, train_Y, l2_coef=0.5)
    print("Correct optimal weights: {}".format(np.allclose(model.parameters, optimal_parameters)))


import numpy as np

def uniform_cover(points, N, overlap=.5):
    minimum = points.min()
    maximum = points.max()

    interval = (maximum - minimum) / (N - (N - 1) * overlap)
    step = interval * (1 - overlap)
    intervals = []

    m = np.full(N, float(minimum))
    m += step * np.arange(N)
    M = m + interval

    return np.dstack((m, M))[0]

def balanced_cover(points, N, overlap=.5):
    sorted_points = np.sort(points)
    length = sorted_points.shape[0]
    interval = int(length / (N - (N - 1) * overlap))
    step = int(interval * overlap)
    m = np.empty(N)
    M = np.empty(N)

    for i in range(N):
        start = i * (interval - step)

        # If this is last step in the loop, include all the rest points.
        if i == N - 1:
            subset = sorted_points[start:]
        else:
            subset = sorted_points[start:start + interval]

        m[i] = subset.min()

        # Get next point of the max point in the interval.
        try:
            next_point = sorted_points[start + interval]

        except:
            next_point = 0.0

        subset_max = subset.max()

        # Add difference between max and next element for
        # include this point in the m <= points < M condition.
        # See point_in_interval function.
        M[i] = subset_max + (next_point - subset_max) / 2

    return np.dstack((m, M))[0]


#!/usr/bin/env python
#coding:utf-8
'''@author: ZKY'''

import numpy as np

class Normalization():

    def __init__(self):
        pass

    def max(self):
        return np.max(self.df)

    def min(self):
        return np.min(self.df)

    def aver(self):
        return np.average(self.df)

    def std(self):
        return np.std(self.df)

    def method_1(self, df):
        '''Normalize to [0, 1]'''
        self.df = df
        df = (df - self.min()) / (self.max() - self.min())
        return df

    def method_2(self, df):
        '''Divided by Max'''
        self.df = df
        df = df / self.max()
        return df

    def method_3(self, df):
        '''Z scores (standardize to N(0, 1)) 要求原始数据的分布可以近似为高斯分布，否则归一化效果会比较差'''
        self.df = df
        df = (df - self.min()) / self.std()
        return df

    def method_4(self, df, x1, x2):
        ''''Divide by custom'''
        cols = df.columns
        self.df = df[cols[1]]
        minium = self.min()
        self.df = df[(df[cols[0]] > x1) & (df[cols[0]] < x2)][cols[1]]
        df = (df[cols[1]] - minium) / (self.max() - minium)
        return df

    def method_5(self, df):
        '''sigmoid函数'''
        pass


'''Meeus: Astronomical Algorithms (2nd ed.), chapter 30'''

import numpy as np

def method1(e,M,eps=1e-8):
    '''1st method for solving Kepler's equation'''

    #type of output (same as input - number, list, numpy.array)
    out_type='lst'
    if isinstance(M,int) or isinstance(M,float):
        #all input args are numbers
        out_type='num'

    if isinstance(M,np.ndarray):
        #numpy.array
        out_type='np'

    if isinstance(M,list): M=np.array(M)
    if out_type=='num': M=np.array([M])

    M=np.deg2rad(M)
    E=M
    E0=E+1e3
    #i=0
    while max(abs(E-E0))>eps:
        E0=np.array(E)
        E=M+e*np.sin(E)
        #i+=1
    #print i

    E=np.rad2deg(E)
    if out_type=='num': E=E[0]
    elif out_type=='lst': E=E.tolist()
    return E

def method2(e,M,eps=1e-8):
    '''2nd method for solving Kepler's equation'''

    #type of output (same as input - number, list, numpy.array)
    out_type='lst'
    if isinstance(M,int) or isinstance(M,float):
        #all input args are numbers
        out_type='num'

    if isinstance(M,np.ndarray):
        #numpy.array
        out_type='np'

    if isinstance(M,list): M=np.array(M)
    if out_type=='num': M=np.array([M])

    M=np.deg2rad(M)
    E=M
    E0=E+1e3
    #i=0
    while max(abs(E-E0))>eps:
        E0=np.array(E)
        E=E+(M+e*np.sin(E)-E)/(1-e*np.cos(E))
        #i+=1
    #print i

    E=np.rad2deg(E)
    if out_type=='num': E=E[0]
    elif out_type=='lst': E=E.tolist()
    return E

def method3(e,M,eps=1e-8):
    '''3rd method for solving Kepler's equation'''

    #type of output (same as input - number, list, numpy.array)
    out_type='lst'
    if isinstance(M,int) or isinstance(M,float):
        #all input args are numbers
        out_type='num'

    if isinstance(M,np.ndarray):
        #numpy.array
        out_type='np'

    if isinstance(M,list): M=np.array(M)
    if out_type=='num': M=np.array([M])

    M=np.deg2rad(M)

    F=np.sign(M)
    M=np.abs(M)/(2*np.pi)
    M=(M-M//1)*2*np.pi*F
    M[np.where(M<0)]+=2*np.pi
    F=np.ones(M.shape)
    F[np.where(M>np.pi)]=-1
    M[np.where(M>np.pi)]=2*np.pi-M[np.where(M>np.pi)]

    E=np.pi/2.*np.ones(M.shape)
    D=np.pi/4.
    E0=np.ones(M.shape)
    #i=0
    while max(abs(E-E0))>eps:
        E0=np.array(E)
        E+=D*np.sign(M-(E-e*np.sin(E)))
        D/=2.
        #i+=1
    #print i

    E*=F
    E[np.where(E<0)]+=2*np.pi
    E=np.rad2deg(E)
    if out_type=='num': E=E[0]
    elif out_type=='lst': E=E.tolist()
    return E

def method4(e,M,eps=1e-8):
    '''4th method (approximation) for solving Kepler's equation'''

    #type of output (same as input - number, list, numpy.array)
    out_type='lst'
    if isinstance(M,int) or isinstance(M,float):
        #all input args are numbers
        out_type='num'

    if isinstance(M,np.ndarray):
        #numpy.array
        out_type='np'

    if isinstance(M,list): M=np.array(M)
    if out_type=='num': M=np.array([M])

    M=np.deg2rad(M)

    E=np.arctan2(np.sin(M),np.cos(M)-e)

    E[np.where(E<0)]+=2*np.pi
    E=np.rad2deg(E)
    if out_type=='num': E=E[0]
    elif out_type=='lst': E=E.tolist()
    return E


import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from statistics import mean as rata_rata
from matplotlib import style
import random
style.use('ggplot')

def create_dataset(jumlah_data,selisih,step=2,correlation=False):

    # jumlah_data = berapa banyak data yang kita ingingkan.terserah, namun semakin banyak data maka samakin lama untuk di proses
    # selisih = jarak point point dari garis regresi
    # step = nilai sebarapah jauh rata2 per titik
    # correlation = apakah garis hubungan nya postiive apa negative

    val = 1
    ys = []
    for i in range(jumlah_data):
        y = val + random.randrange(-selisih,selisih)
        ys.append(y)
        if (correlation and correlation == 'pos'):
            val+=step
        elif (correlation and correlation == 'neg'):
            val-=step

    xs = [i for i in range(len(ys))]

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

# m = nilai kemiringan
# b = nilai perpotongan antara sumbu tegak(y)

def hitung_kemiringan_dan_b(x,y):
    m = (((rata_rata(x)*rata_rata(y)) - rata_rata(x*y)) /
         ((rata_rata(x)*rata_rata(x)) - rata_rata(x*x)))

    b = rata_rata(y) - m*rata_rata(x)

    return m, b

""" menghitung jumlah square error pada y"""

def kesalahan_kuadarat(y_asli,y_line):
    return sum((y_asli-y_line)**2)

"""determinan_koefisien berfungsi untuk mengkalkulasikan seberapah akurat data yang di prediksikan semakin tinngi nilai
r nya maka semakin besar data yg di prediksi akurat"""

def determinan_koefisien(ys_orig,ys_line):
    y_rata_rata_line = [rata_rata(ys_orig) for y in ys_orig]

    SE = sum((ys_line - ys_orig) * (ys_line - ys_orig))
    SE_rata2 = sum((y_rata_rata_line - ys_orig) * (y_rata_rata_line - ys_orig))

    print(SE)
    print(SE_rata2)

    r = 1 - (SE/SE_rata2)

    return r

x,y = create_dataset(402,100,5,correlation='pos')
m,b = hitung_kemiringan_dan_b(x,y)
# y = mx+b asumsikan y itu adalah nama variabel yg dibawah
rumus_regresi_linear = [(m*x+b) for x in x]
nilai_prediksix = 10
nilai_prediksiy = (m*nilai_prediksix+b)
kuadrat_r = determinan_koefisien(y,rumus_regresi_linear)
print(kuadrat_r)

plt.scatter(x,y,color='#003F72', label = 'data')
plt.scatter(nilai_prediksix,nilai_prediksiy,color='green',label='prediksi')
plt.plot(x, rumus_regresi_linear, label = 'garis regresi')
plt.legend(loc=4)
plt.title("Regresi Linear")
plt.show()


import numpy as np

A = np.array([(2,3),(1,2)])
Y = np.array([23,14])
print(A)
print(Y)

A_inv = np.linalg.inv(A)

# menyelesaikan persamaan linear
X1 = np.dot(A_inv,Y)
print(X1)

# cara lain
X2 = np.linalg.solve(A,Y)
print(X2)


import math

import numpy as np
import numpy.linalg as npla

def rotation_matrix_to_euler(R : np.ndarray) -> np.ndarray:
    sy = math.sqrt(R[0,0] * R[0,0] +  R[1,0] * R[1,0])
    singular = sy < 1e-6
    if  not singular :
        x = math.atan2(R[2,1] , R[2,2])
        y = math.atan2(-R[2,0], sy)
        z = math.atan2(R[1,0], R[0,0])
    else :
        x = math.atan2(-R[1,2], R[1,1])
        y = math.atan2(-R[2,0], sy)
        z = 0
    return np.array([x, y, z])

def segment_length(p1 : np.ndarray, p2 : np.ndarray):
    """
        p1  (2,)
        p2  (2,)
    """
    return npla.norm(p2-p1)

def segment_to_vector(p1 : np.ndarray, p2 : np.ndarray):
    """
        p1  (2,)
        p2  (2,)
    """
    x = p2-p1
    x /= npla.norm(x)
    return x

def intersect_two_line(a1, a2, b1, b2) -> np.ndarray:
    """
    Returns the point of intersection of the lines (not segments) passing through a2,a1 and b2,b1.
    a1: [x, y] a point on the first line
    a2: [x, y] another point on the first line
    b1: [x, y] a point on the second line
    b2: [x, y] another point on the second line
    """
    s = np.vstack([a1,a2,b1,b2])        # s for stacked
    h = np.hstack((s, np.ones((4, 1)))) # h for homogeneous
    l1 = np.cross(h[0], h[1])           # get first line
    l2 = np.cross(h[2], h[3])           # get second line
    x, y, z = np.cross(l1, l2)          # point of intersection
    if z == 0:                          # lines are parallel
        return (float('inf'), float('inf'))
    return np.array( [x/z, y/z], np.float32 )

def polygon_area(poly : np.ndarray) -> float:
    """
    calculate area of n-vertices polygon with non intersecting edges

        poly   np.ndarray (n,2)
    """
    return float( np.abs(np.sum( poly[:,0] * np.roll( poly[:,1], -1  ) - poly[:,1] * np.roll( poly[:,0], -1  )  ) / 2) )


""" Helpers for working with (sparse) 2d matrices
"""
import logging
logger = logging.getLogger(__name__)

import numpy as np

import scipy.io
import scipy.sparse

from typing import List, Optional, Tuple

###
# Matrix operation helpers
###

def col_op(m, op):
    """Apply op to each column in the matrix."""
    return op(m, axis=0)

def col_sum(m):
    """Calculate the sum of each column in the matrix."""
    return  np.sum(m, axis=0)

def col_sum_mean(m:np.ndarray, return_var:bool=False) -> float:
    """ Calculate the mean of the sum of each column in the matrix.

    Optionally, the variances of the column sums can also be calculated.

    Parameters
    ----------
    m : numpy.ndarray
        The (2d) matrix

    var : bool
        Whether to calculate the variances

    Returns
    -------
    mean : float
        The mean of the column sums in the matrix

    variance : float
        If `return_var` is True, then the variance of the column sums
    """
    col_sums = col_sum(m)
    mean = np.mean(col_sums)

    ret = mean
    if return_var:
        var = np.var(col_sums)
        ret = mean, var

    return ret

def normalize_columns(matrix:np.ndarray) -> np.ndarray:
    """ Normalize the columns of the given (dense) matrix

    Parameters
    ----------
    m : numpy.ndarray
        The (2d) matrix

    Returns
    -------
    normalized_matrix : numpy.ndarray
        The matrix normalized such that all column sums are 1
    """
    col_sums = np.sum(np.abs(matrix), axis=0)
    matrix = np.divide(m, col_sums)
    return matrix

def row_op(m, op):
    """Apply op to each row in the matrix."""
    return op(m, axis=1)

def row_sum(m):
    """Calculate the sum of each row in the matrix."""
    return  np.sum(m, axis=1)

def row_sum_mean(m:np.ndarray, var:bool=False) -> float:
    """ Calculate the mean of the sum of each row in the matrix.

    Optionally, the variances of the row sums can also be calculated.

    Parameters
    ----------
    m : numpy.ndarray
        The (2d) matrix

    return_var : bool
        Whether to calculate the variances

    Returns
    -------
    mean : float
        The mean of the row sums in the matrix

    variance : float
        If `return_var` is `True`, then the variance of the row sums
    """
    row_sums = row_sum(m)
    mean = np.mean(row_sums)

    ret = mean
    if return_var:
        var = np.var(row_sums)
        ret = mean, var

    return ret

def normalize_rows(matrix:np.ndarray) -> np.ndarray:
    """ Normalize the rows of the given (dense) matrix

    Parameters
    ----------
    matrix : numpy.ndarray
        The (2d) matrix

    Returns
    -------
    normalized_matrix : numpy.ndarray
        The matrix normalized such that all row sums are 1
    """
    coef = np.abs(matrix).sum(axis=1)
    coef = coef[:,np.newaxis]
    matrix = np.divide(matrix, coef)
    return matrix

###
# Sparse matrix helpers
###
def get_dense_row(
        matrix:scipy.sparse.spmatrix,
        row:int,
        dtype=float,
        max_length:Optional[int]=None) -> np.ndarray:
    """ Extract `row` from the sparse `matrix`

    Parameters
    ----------
    matrix : scipy.sparse.spmatrix
        The sparse matrix

    row : int
        The 0-based row index

    dtype : type
        The base type of elements of `matrix`. This is used for
        the corner case where `matrix` is essentially a sparse
        column vector.

    max_length : typing.Optional[int]
        The maximum number of columns to include in the returned
        row.

    Returns
    -------
    row : numpy.ndarray
        The specified row (as a 1d numpy array)
    """
    d = matrix.getrow(row).todense()
    d = np.squeeze(np.asarray(d, dtype=dtype))

    if max_length is not None:
        d = d[:max_length]

    # make sure we do not return a scalar
    if isinstance(d, dtype):
        d = np.array([d])

    return d

def sparse_matrix_to_dense(sparse_matrix:scipy.sparse.spmatrix) -> np.ndarray:
    """ Convert `sparse_matrix` to a dense numpy array

    Parameters
    ----------
    sparse_matrix : scipy.sparse.spmatrix
        The sparse scipy matrix

    Returns
    -------
    dense_matrix: numpy.ndarray
        The dense (2d) numpy array
    """
    dense = np.array(sparse_matrix.todense())
    return dense

def sparse_matrix_to_list(sparse_matrix:scipy.sparse.spmatrix) -> List:
    """ Convert `sparse_matrix` to a list of "sparse row vectors".

    In this context, a "sparse row vector" is simply a sparse matrix
    with dimensionality (1, sparse_matrix.shape[1]).

    Parameters
    ----------
    sparse_matrix: scipy.sparse.spmatrix
        The sparse scipy matrix

    Returns
    -------
    list_of_sparse_row_vectors : typing.List[scipy.sparse.spmatrix]
        The list of sparse row vectors
    """
    list_of_sparse_row_vectors = [
        sparse_matrix[i] for i in range(sparse_matrix.shape[0])
    ]

    return list_of_sparse_row_vectors

def write_sparse_matrix(
        target:str,
        a:scipy.sparse.spmatrix,
        compress:bool=True,
        **kwargs) -> None:
    """ Write `a` to the file `target` in matrix market format

    This function is a drop-in replacement for scipy.io.mmwrite. The only
    difference is that it gzip compresses the output by default. It *does not*
    alter the file extension, which should likely end in "mtx.gz" except in
    special circumstances.

    If `compress` is `True`, then this function imports `gzip`.

    Parameters
    ----------
    target : str
        The complete path to the output file, including file extension

    a : scipy.sparse.spmatrix
        The sparse matrix

    compress : bool
        Whether to compress the output

    **kwargs : <key>=<value> pairs
        These are passed through to :func:`scipy.io.mmwrite`. Please see the
        scipy documentation for more details.

    Returns
    --------
    None, but the matrix is written to disk
    """

    if compress:
        import gzip

        with gzip.open(target, 'wb') as target_gz:
            scipy.io.mmwrite(target_gz, a, **kwargs)
    else:
        scipy.io.mmwrite(target, a, **kwargs)

###
# Other helpers
###
def matrix_multiply(
        m1:np.ndarray,
        m2:np.ndarray,
        m3:np.ndarray) -> np.ndarray:
    """ Multiply the three matrices

    This function performs the multiplications in an order such that the
    size of the intermediate matrix created by the first matrix multiplication
    is as small as possible.

    Parameters
    ----------
    m{1,2,3} : numpy.ndarray
        The (2d) matrices

    Returns
    -------
    product_matrix : numpy.ndarray
        The product of the three input matrices
    """

    # check the dimensions
    if m1.shape[1] != m2.shape[0]:
        msg = ("The inner dimension between the first and second matrices do "
            "not match. {} and {}".format(m1.shape, m2.shape))
        raise ValueError(msg)

    if m2.shape[1] != m3.shape[0]:
        msg = ("The inner dimensions between the second and third matrices do "
            "not match. {} and {}".format(m2.shape, m3.shape))
        raise ValueError(msg)

    # now, check which order to perform the multiplications
    m1_first_size = m1.shape[0] * m2.shape[1]
    m3_first_size = m2.shape[0] * m3.shape[1]

    if m1_first_size < m3_first_size:
        res = np.dot(m1, m2)
        res = np.dot(res, m3)
    else:
        res = np.dot(m2, m3)
        res = np.dot(m1, res)

    return res

def permute_matrix(
        m:np.ndarray,
        is_flat:bool=False,
        shape:Optional[Tuple[int]]=None) -> np.ndarray:
    """ Randomly permute the entries of the matrix. The matrix is first
    flattened.

    For reproducibility, the random seed of numpy should be set **before**
    calling this function.

    Parameters
    ----------
    m : numpy.ndarray
        The matrix (tensor, etc.)

    is_flat : bool
        Whether the matrix values have already been flattened. If they have
        been, then the desired shape must be passed.

    shape : typing.Optional[typing.Tuple]
        The shape of the output matrix, if `m` is already flattened

    Returns
    -------
    permuted_m: numpy.ndarray
        A copy of m (with the same shape as m) with the values randomly
        permuted.
    """

    if shape is None:
        shape = m.shape

    if not is_flat:
        m = np.reshape(m, -1)

    # shuffle the actual values
    permuted_m = np.random.permutation(m)

    # and put them back in the correct shape
    permuted_m = np.reshape(permuted_m, shape)

    return permuted_m


import random

def MCint(f, a, b, n):
    s = 0
    for i in xrange(n):
        x = random.uniform(a, b)
        s += f(x)
    I = (float(b-a)/n)*s
    return I

import numpy as np

def MCint_vec(f, a, b, n):
    x = np.random.uniform(a, b, n)
    s = np.sum(f(x))
    I = (float(b-a)/n)*s
    return I

def MCint_vec2(f, a, b, n, arraysize=1000000):
    s = 0
    # Split sum into batches of size arraysize
    # + a sum of size rest (note: n//arraysize is integer division)
    rest = n % arraysize
    batch_sizes = [arraysize]*(n//arraysize) + [rest]
    for batch_size in batch_sizes:
        x = np.random.uniform(a, b, batch_size)
        s += np.sum(f(x))
    I = (float(b-a)/n)*s
    return I

def MCint2(f, a, b, n):
    s = 0
    # Store the intermediate integral approximations in an
    # array I, where I[k-1] corresponds to k function evals.
    I = np.zeros(n)
    for k in range(1, n+1):
        x = random.uniform(a, b)
        s += f(x)
        I[k-1] = (float(b-a)/k)*s
    return I

def MCint3(f, a, b, n, N=100):
    s = 0
    # Store every N intermediate integral approximations in an
    # array I and record the corresponding k value.
    I_values = []
    k_values = []
    for k in range(1, n+1):
        x = random.uniform(a, b)
        s += f(x)
        if k % N == 0:
            I = (float(b-a)/k)*s
            I_values.append(I)
            k_values.append(k)
    return k_values, I_values

def demo():
    def f1(x):
        return 2 + 3*x

    a = 1; b = 2; n = 1000000; N = 10000
    k, I = MCint3(f1, a, b, n, N)

    from scitools.std import plot
    error = 6.5 - np.array(I)
    plot(k, error, title='Monte Carlo integration',
         xlabel='n', ylabel='error', savefig='tmp.pdf')

def test_MCint_all():
    """Test MCint, MCint_vec, MCint_vec2."""
    # Let all functions use np.random
    global random
    random = np.random
    f = np.sin
    np.random.seed(30)
    I = MCint(f, 0, 2*np.pi, n=10)
    np.random.seed(30)
    I_vec = MCint_vec(f, 0, 2*np.pi, n=10)
    np.random.seed(30)
    I_vec2 = MCint_vec2(f, 0, 2*np.pi, n=10, arraysize=4)
    tol = 1E-15
    assert abs(I - I_vec)  < tol
    assert abs(I - I_vec2) < tol

if __name__ == '__main__':
    #demo()
    test_MCint_all()


import numpy as np
from scipy.stats import multivariate_normal
from typing import Optional

class GaussianMixture:
    """
    Gaussian mixture

    N: num of data
    D: dimension of each data
    K: num of cluster

    X.shape = (N, D)
    mu.shape = (K, D)
    sigma.shape = (K, D, D)
    pi.shape = (K,)
    prob.shape = (N, K)
    resp.shape = (N, K)
    """

    def __init__(self, n_components: int, random_state: int = 42,
                 max_iter: int = 100, verbose: int = 0,
                 init_params: str = "random",
                 means_init: Optional[np.array] = None) -> None:
        np.random.seed(random_state)
        if n_components <= 0:
            raise ValueError("n_components must be larger than 0")
        self.n_components = n_components
        self.max_iter = max_iter
        self.verbose = verbose
        self.init_params = init_params
        self.mu = means_init

    def _initialize(self, X) -> None:
        """
        initialization methods
        mu -> uniform from each dim
        sigma -> identity matrix
        pi -> uniform
        """
        if self.mu is None:
            idx = np.random.choice(
                np.arange(X.shape[0]), size=self.n_components, replace=False)
            self.mu = X[idx]
        self.sigma = np.array([np.eye(X.shape[1])
                               for _ in range(self.n_components)])
        self.pi = np.ones(self.n_components) / self.n_components
        self.prob = []
        for i in range(self.n_components):
            self.prob.append(multivariate_normal.pdf(
                X, mean=self.mu[i], cov=self.sigma[i]))
        self.prob = np.asarray(self.prob).T

    def fit(self, X, y=None) -> None:
        num_data, _ = X.shape
        if num_data < self.n_components:
            raise ValueError("size of X must be smaller than n_components")

        self._initialize(X)
        for i in range(self.max_iter):
            # E-step
            self.resp = self.pi * self.prob
            self.resp /= self.resp.sum(axis=1, keepdims=True)
            # M-step
            Nk = self.resp.sum(axis=0)
            self.mu = np.dot(self.resp.T, X) / Nk[:, np.newaxis]
            self.sigma = np.empty(self.sigma.shape)
            for k in range(self.n_components):
                diff = X - self.mu[k]
                self.sigma[k] = np.dot(self.resp[:, k] * diff.T, diff) / Nk[k]
            self.pi = Nk / num_data
            if self.verbose:
                print(f"[step {i+1}] loglike =", self.loglike())

    def loglike(self):
        return np.log((self.pi * self.prob).sum(axis=1)).sum()

    def get_hard_cluster(self):
        return self.resp.argmax(axis=1)


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

# # Calculators

# **CS1302 Introduction to Computer Programming**
# ___

# Run the following to load additional tools required for this lab.
# In particular, the `math` library provides many useful mathematical functions and constants.

# In[ ]:

get_ipython().run_line_magic('reset', '-f')
from ipywidgets import interact
import matplotlib.pyplot as plt
import numpy as np
import math
from math import log, exp, sin, cos, tan, pi

# The following code is a Python one-liner that creates a calculator.
# Evaluate the cell with `Ctrl+Enter`:

# In[ ]:

print(eval(input()))

# Try some calculations below using this calculator:
# 1. $2^3$ by entering `2**3`;
# 1. $\frac23$ by entering `2/3`;
# 1. $\left\lceil\frac32\right\rceil$ by entering `3//2`;
# 1. $3\mod 2$ by entering `3%2`;
# 1. $\sqrt{2}$ by entering `2**0.5`; and
# 1. $\sin(\pi/6)$ by entering `sin(pi/6)`;

# For this lab, you will create more powerful and dedicated calculators.
# We will first show you a demo. Then, it will be your turn to create the calculators.

# ## Hypotenuse Calculator (Demo)

# ![interact](https://www.cs.cityu.edu.hk/~ccha23/cs1302/Lab2/hypotenuse-calculator.gif)

# Using the Pythagoras theorem below, we can define the following function `length_of_hypotenus` to calculate the length `c` of the hypotenuse given the lengths `a` and `b` of the other sides of a right-angled triangle:
# $$c = \sqrt{a^2 + b^2}$$

# In[ ]:

def length_of_hypotenuse(a, b):
    c = (a**2 + b**2)**(0.5)  # Pythagoras
    return c

# - You need not understand how a function is defined, but
# - you should know how to *write the formula as a Python expression*, and
# - *assign to the variable* `c` the value of the expression (Line 2).

# For example, you may be asked to write Line 2, while Line 1 and 3 are given to you:

# **Exercise** Complete the function below to return the length `c` of the hypotenuse given the lengths `a` and `b`.

# In[ ]:

def length_of_hypotenuse(a, b):
    # YOUR CODE HERE
    raise NotImplementedError()
    return c

# Note that indentation affects the execution of Python code. The assignment statement must be indented to indicate that it is part of the *body* of the function.
# (Try removing the indentation and see what happens.)

# We will use widgets (`ipywidgets`) to let user interact with the calculator more easily:

# In[ ]:

# hypotenuse calculator
@interact(a=(0, 10, 1), b=(0, 10, 1))
def calculate_hypotenuse(a=3, b=4):
    print('c: {:.2f}'.format(length_of_hypotenuse(a, b)))

# After running the above cell, you can move the sliders to change the values of `a` and `b`. The value of `c` will be updated immediately.

# - For this lab, you need not know how write widgets, but
# - you should know how to *format a floating point number* (Line 3).

# You can check your code with a few cases listed in the test cell below.

# In[ ]:

# tests
def test_length_of_hypotenuse(a, b, c):
    c_ = length_of_hypotenuse(a, b)
    correct = math.isclose(c, c_)
    if not correct:
        print(f'For a={a} and b={b}, c should be {c}, not {c_}.')
    assert correct

test_length_of_hypotenuse(3, 4, 5)
test_length_of_hypotenuse(0, 0, 0)
test_length_of_hypotenuse(4, 7, 8.06225774829855)

# ## Quadratic Equation

# ### Graphical Calculator for Parabola

# ![plot parabola](https://www.cs.cityu.edu.hk/~ccha23/cs1302/Lab2/plot_parabola.gif)

# In mathematics, the collection of points $(x,y)$ satisfying the following equation forms a *parabola*:
#
# $$
# y=ax^2+bx+c
# $$
# where $a$, $b$, and $c$ are real numbers called the *coefficients*.

# **Exercise** Given the variables `x`, `a`, `b`, and `c` store the $x$-coordinate and the coefficients $a$, $b$, and $c$ respectively, assign to `y` the corresponding $y$-coordinate for the parabola.

# In[ ]:

def get_y(x, a, b, c):
    # YOUR CODE HERE
    raise NotImplementedError()
    return y

# In[ ]:

# tests
def test_get_y(y,x,a,b,c):
    y_ = get_y(x,a,b,c)
    correct = math.isclose(y,y_)
    if not correct:
        print(f'With (x, a, b, c)={x,a,b,c}, y should be {y} not {y_}.')
    assert correct

test_get_y(0,0,0,0,0)
test_get_y(1,0,1,2,1)
test_get_y(2,0,2,1,2)

# In[ ]:

# graphical calculator for parabola
@interact(a=(-10, 10, 1), b=(-10, 10, 1), c=(-10, 10, 1))
def plot_parabola(a, b, c):
    xmin, xmax, ymin, ymax, resolution = -10, 10, -10, 10, 50
    ax = plt.gca()
    ax.set_title(r'$y=ax^2+bx+c$')
    ax.set_xlabel(r'$x$')
    ax.set_ylabel(r'$y$')
    ax.set_xlim([xmin, xmax])
    ax.set_ylim([ymin, ymax])
    ax.grid()

    x = np.linspace(xmin, xmax, resolution)
    ax.plot(x, get_y(x, a, b, c))

# ### Quadratic Equation Solver

# ![quadratic equtaion soler](https://www.cs.cityu.edu.hk/~ccha23/cs1302/Lab2/quadratic-equation-solver.gif)

# For the quadratic equation
#
# $$
# ax^2+bx+c=0,
# $$
# the *roots* (solutions for $x$) are give by
#
# $$
# \frac{-b-\sqrt{b^2-4ac}}{2a},\frac{-b+\sqrt{b^2-4ac}}{2a}.
# $$

# **Exercise** Assign to `root1` and `root2` the values of the first and second roots above respectively.

# In[ ]:

def get_roots(a, b, c):
    # YOUR CODE HERE
    raise NotImplementedError()
    return root1, root2

# In[ ]:

# tests
def test_get_roots(roots, a, b, c):
    roots_ = get_roots(a, b, c)
    correct = all([math.isclose(roots[i], roots_[i]) for i in range(2)])
    if not correct:
        print(f'With (a, b, c)={a,b,c}, roots should be {roots} not {roots_}.')
    assert correct

test_get_roots((-1.0, 0.0), 1, 1, 0)
test_get_roots((-1.0, -1.0), 1, 2, 1)
test_get_roots((-2.0, -1.0), 1, 3, 2)

# In[ ]:

# quadratic equations solver
@interact(a=(-10,10,1),b=(-10,10,1),c=(-10,10,1))
def quadratic_equation_solver(a=1,b=2,c=1):
    print('Roots: {}, {}'.format(*get_roots(a,b,c)))

# ## Number Conversion

# ### Byte-to-Decimal Calculator

# ![byte-to-decimal](https://www.cs.cityu.edu.hk/~ccha23/cs1302/Lab2/byte-to-decimal.gif)

# Denote a binary number stored as a byte ($8$-bit) as
#
# $$
# b_7\circ b_6\circ b_5\circ b_4\circ b_3\circ b_2\circ b_1\circ b_0,
# $$
# where $\circ$ concatenates $b_i$'s together into a binary string.

# The binary string can be converted to a decimal number by the formula
#
# $$
# b_7\cdot 2^7 + b_6\cdot 2^6 + b_5\cdot 2^5 + b_4\cdot 2^4 + b_3\cdot 2^3 + b_2\cdot 2^2 + b_1\cdot 2^1 + b_0\cdot 2^0.
# $$

# E.g., the binary string `'11111111'` is the largest integer represented by a byte:
#
# $$
# 2^7+2^6+2^5+2^4+2^3+2^2+2^1+2^0=255=2^8-1.
# $$

# **Exercise** Assign to `decimal` the *integer* value represented by a byte.
# The byte is a sequence of bits assigned to the variables `b7,b6,b5,b4,b3,b2,b1,b0` as *characters*, either `'0'` or `'1'`.

# In[ ]:

def byte_to_decimal(b7, b6, b5, b4, b3, b2, b1, b0):
    # YOUR CODE HERE
    raise NotImplementedError()
    return decimal

# In[ ]:

# tests
def test_byte_to_decimal(decimal, b7, b6, b5, b4, b3, b2, b1, b0):
    decimal_ = byte_to_decimal(b7, b6, b5, b4, b3, b2, b1, b0)
    correct = decimal == decimal_ and isinstance(decimal_, int)
    if not correct:
        print(
            f'{b7}{b6}{b5}{b4}{b3}{b2}{b1}{b0} should give {decimal} not {decimal_}.'
        )
    assert correct

test_byte_to_decimal(38, '0', '0', '1', '0', '0', '1', '1', '0')
test_byte_to_decimal(20, '0', '0', '0', '1', '0', '1', '0', '0')
test_byte_to_decimal(22, '0', '0', '0', '1', '0', '1', '1', '0')

# In[ ]:

# byte-to-decimal calculator
bit = ['0', '1']

@interact(b7=bit, b6=bit, b5=bit, b4=bit, b3=bit, b2=bit, b1=bit, b0=bit)
def convert_byte_to_decimal(b7, b6, b5, b4, b3, b2, b1, b0):
    print('decimal:', byte_to_decimal(b7, b6, b5, b4, b3, b2, b1, b0))

# ### Decimal-to-Byte Calculator

# ![decimal-to-byte](https://www.cs.cityu.edu.hk/~ccha23/cs1302/Lab2/decimal-to-byte.gif)

# **Exercise** Assign to `byte` a *string of 8 bits* that represents the value of `decimal`, a non-negative decimal integer from $0$ to $2^8-1=255$.
# *Hint: Use `//` and `%`.*

# In[ ]:

def decimal_to_byte(decimal):
    # YOUR CODE HERE
    raise NotImplementedError()
    return byte

# In[ ]:

# tests
def test_decimal_to_byte(byte,decimal):
    byte_ = decimal_to_byte(decimal)
    correct = byte == byte_ and isinstance(byte, str) and len(byte) == 8
    if not correct:
        print(
            f'{decimal} should be represented as the byte {byte}, not {byte_}.'
        )
    assert correct

test_decimal_to_byte('01100111', 103)
test_decimal_to_byte('00000011', 3)
test_decimal_to_byte('00011100', 28)

# In[ ]:

# decimal-to-byte calculator
@interact(decimal=(0,255,1))
def convert_decimal_to_byte(decimal=0):
    print('byte:', decimal_to_byte(decimal))


#!/usr/local/Cellar/python/2.7.6/bin/python
# -*- coding: utf-8 -*-

'''Standard python modules'''
import sys

'''For scientific computing'''
from numpy import *
import scipy

'''For plotting'''
from matplotlib import pyplot, cm
from mpl_toolkits.mplot3d import Axes3D

def featureNormalizeLoop( data ):
	"""Normalize the features using loop, this is the slower version"""
	mu 	      = []
	sigma 	  = []
	data_norm = zeros( shape(data), data.dtype )

	for col in range( 0, shape(data)[1] ):
		mu.append	( mean( data[:, col] ) )
		sigma.append( std ( data[:, col], ddof=1 ) ) # if ddof = 0 sigma will be uncorrected sample standard deviation
		data_norm[:, col] = map( lambda x: (x - mu[col]) / sigma[col], data[:, col] )

	return data_norm, array(mu), array( sigma )

def featureNormalize( data ):
	"""A vectorized version of feature normalization"""
	mu 			= mean( data, axis=0 )
	data_norm 	= data - mu
	sigma 		= std( data_norm, axis=0, ddof=1 )
	data_norm 	= data_norm / sigma
	return data_norm, mu, sigma

def gradientDescent( X, y, theta, alpha, iters, m ):
	"""Run a gradient descent"""
	grad  = copy( theta )
	max_j 		= shape(X)[1]
	J_history 	= []
	alpha_div_m = alpha / m

	for counter in range( 0, iters ):
		inner_sum 	= X.T.dot(X.dot( grad ) - y)
		grad 		= grad - alpha_div_m * inner_sum
		J_history.append( computeCost(X, y, grad, m ) )

	return J_history, grad

def computeCost( X, y, theta, m ):
	term = X.dot( theta ) - y
	return ( term.T.dot( term ) / (2 * m) )[0, 0]

def normalEquation( X, y ):
	return linalg.inv(X.T.dot( X )).dot( X.T ).dot( y )

def part3_1():
	data = genfromtxt( "/Users/saburookita/Downloads/mlclass-ex1-004/mlclass-ex1/ex1data2.txt", delimiter = ',' )
	X = data[:, 0:2]
	y = data[:, 2:3]

	X, mu, sigma = featureNormalize( X )
	print X
	print mu
	print sigma

def part3_2():
	data = genfromtxt( "/Users/saburookita/Downloads/mlclass-ex1-004/mlclass-ex1/ex1data2.txt", delimiter = ',' )
	X = data[:, 0:2]
	y = data[:, 2:3]
	m = shape( X )[0]

	X, mu, sigma = featureNormalize( X )

	X 			= c_[ ones((m, 1)), X ] # add intercept to X
	iterations 	= 400
	alphas 		= [0.01, 0.03, 0.1, 0.3, 1.0]

	# for each alpha, try to do gradient descent and plot the convergence curve
	for alpha in alphas:
		theta = zeros( (3, 1) )
		J_history, theta = gradientDescent( X, y, theta, alpha, iterations, m )

		# create an array of number of iterations
		number_of_iterations = array( [x for x in range( 1, iterations + 1 )] ).reshape( iterations, 1)

		pyplot.plot( number_of_iterations, J_history, '-b' )
		pyplot.title( "Alpha = %f" % (alpha) )
		pyplot.xlabel('Number of iterations')
		pyplot.ylabel('Cost J')
		pyplot.xlim( [0, 50] )
		pyplot.show( block=True )

		# 1650 sq feet 3 bedroom house
		test = array([1.0, 1650.0, 3.0])
		# exclude intercept units
		test[1:] = (test[1:] - mu) / sigma
		print test.dot( theta )

def part3_3():
	data = genfromtxt( "/Users/saburookita/Downloads/mlclass-ex1-004/mlclass-ex1/ex1data2.txt", delimiter = ',' )
	X = data[:, 0:2]
	y = data[:, 2:3]
	m = shape( X )[0]

	X = c_[ ones((m, 1)), X ] # add intercept to X

	theta = normalEquation( X, y )
	# 1650 sq feet 3 bedroom house
	test = array([1.0, 1650.0, 3.0])
	print test.dot( theta )

def main():
	set_printoptions(precision=6, linewidth=200)

	part3_1()
	part3_2()
	part3_3()

if __name__ == '__main__':
	main()


import sympy as sym
import scipy as sci
import numpy as np
from math import *
import sys

#===================================================================================
# Phần thuật toán chính
class newton_mat_inversion:
#{
    max_attempt = 2;

    def __init__(self, A, n, eps):    # Khởi tạo
    #{
        self.A = np.reshape(np.array(A), (n, n));
        self.n = n;
        self.eps = eps;
        self.nr_iterations = 0;
    #}
    def norm(self, __A, __norm_type = 2):   # Chuẩn ma trận
    #{
        return np.linalg.norm(__A, __norm_type);
    #}
    def __refine_initial_approx(self):  # Tìm xấp xỉ đầu:
    #{
        # Gán các biến cơ bản
        E  = np.eye(self.n);
        A  = self.A;
        X  = self.A;

        # PP xấp xỉ đầu của Pan & Reif
        t1 = self.norm(X, 1);
        t2 = self.norm(X, inf);
        X  = (X / (t1 * t2)).T;

        # Hiệu chỉnh lại giá trị q của ma trận xấp xỉ đầu
        attempt = 0;
        while(attempt <= newton_mat_inversion.max_attempt):
        #{
            X = X @ (2 * E - A @ X);
            if(self.norm(E - A @ X) < 1): attempt += 1;
            self.nr_iterations += 1 * bool(attempt == 0);
        #}

        # Trả về ma trận xấp xỉ đầu
        return X;
    #}
    def __pure_newton(self, X_0):  # Lặp Newton nguyên bản
    #{

        # Gán các biến cơ bản
        norm_X0 = self.norm(X_0); # Do X_0 không đổi nên ta đặt 1 biến làm chuẩn của X_0
        E       = np.eye(self.n);
        A       = self.A;
        eps     = self.eps;

        # Bước 2 của thuật toán
        q2k = q = self.norm(E - A @ X_0);
        X = X_0;

        # Kiểm tra điều kiện hội tụ
        if(q >= 1):
        #{
            print("Xấp xỉ đầu không thỏa mãn nên không đưa ra được ma trận nghịch đảo.");
            return np.full((self.n, self.n), float("NaN"));
        #}

        # Lặp
        while(norm_X0 * q2k >= self.eps * (1 - q)):
        #{
            self.nr_iterations += 1;
            X = X @ (2 * E - A @ X);
            q2k = q2k ** 2
        #}

        # Đưa ra ma trận cuối cùng
        print(f"Phương pháp Newton kết thúc sau {self.nr_iterations} bước lặp", file=sys.stderr);
        return X;
    #}

    def improved_newton(self):  # PP Newton cải tiến với xấp xỉ đầu
    #{
        if(np.linalg.det(self.A) == 0):
        #{
            print("A không khả nghịch nên không đưa ra được ma trận nghịch đảo");
            return np.full((self.n, self.n), float("NaN"));
        #}
        return self.__pure_newton(self.__refine_initial_approx());
    #}
    def pure_newton(self, X_0):  # PP Newton nguyên bản
    #{
        if(np.linalg.det(self.A) == 0):
        #{
            print("A không khả nghịch nên không đưa ra được ma trận nghịch đảo");
            return np.full((self.n, self.n), float("NaN"));
        #}
        return self.__pure_newton(X_0);
    #}
#}

#===================================================================================
# Chương trình ví dụ
# n = # Kích cỡ ma trận
# A = # Ma trận đầu vào
# eps = # Sai số epsilon

# In ra A
# print(A);

# A = np.reshape(np.array(A), (n, n));
# uu = newton_mat_inversion(A, n, eps);

# B1 = uu.improved_newton();   # Tiến hành thuật toán
# print(B1);                   # In ra ma trận nghịch đảo
# print(B1 @ A);               # Kiểm tra

Topic 2.5 - Matrix Inversion/Newton Method/newton.py


"""
  Name     : c14_06_binary_call.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""
import random
import scipy as sp
#
def terminalStockPrice(S, T,r,sigma):
    tao=random.gauss(0,1.0)
    terminalPrice=S * sp.exp((r - 0.5 * sigma**2)*T+sigma*sp.sqrt(T)*tao)
    return terminalPrice
#
def binaryCallPayoff(x, sT,payoff):
    if sT >= x:
        return payoff
    else:
        return 0.0
# input area
S = 40.0            # asset price
x = 40.0            #  exericse price
T = 0.5             # maturity in years
r = 0.01            # risk-free rate
sigma = 0.2         # vol of 20%
fixedPayoff = 10.0  # payoff
nSimulations =10000 # number of simulatins
#
payoffs=0.0
for i in xrange(nSimulations):
    sT = terminalStockPrice(S, T,r,sigma)
    payoffs += binaryCallPayoff(x, sT,fixedPayoff)
#
price = sp.exp(-r * T) * (payoffs / float(nSimulations))
print('Binary options call= %.8f' % price)