"""
This file is some side functions that allows for the conversion of cordinate
systems.
"""

import numpy as np

########################################################################
##########                From Cartesian to ***               ##########
########################################################################

def cartesian_to_polar_2d(x, y):
    """Convert cartesian points to polar points.

    Convert cartesian coordinate points in 2D to polar coordinate points in 2D.
    This function uses the notation convention of ISO 80000-2:2009 and its
    related successors.

    Parameters
    ----------
    x : array_like
        The x values of the points to be transformed.
    y : array_like
        The y values of the points to be transformed.

    Returns
    -------
    rho : array_like
        The rho (radial) values of the points after transformation.
    phi : array_like
        The phi (angular) values of the points after transformation.
    """

    # Basic validation
    x = np.array(x, dtype=float)
    y = np.array(y, dtype=float)

    # Convert to polar coordinates.
    rho = np.hypot(x, y)
    phi = np.arctan2(y, x)

    return rho, phi

def cartesian_to_cylindrical_3d(x, y, z):
    """Convert cartesian points to cylindrical points.

    Convert cartesian coordinate points in 3D to cylindrical coordinate points
    in 3D. This function uses the notation convention of ISO 80000-2:2009 and
    its related successors.

    Parameters
    ----------
    x : array_like
        The x values of the points to be transformed.
    y : array_like
        The y values of the points to be transformed.
    z : array_like
        The z values of the points to be transformed.

    Returns
    -------
    rho : array_like
        The rho (radial) values of the points after transformation.
    phi : array_like
        The phi (angular) values of the points after transformation.
    z : array_like
        The z (height) values of the points after transformation.
    """

    # Basic validation.
    x = np.array(x, dtype=float)
    y = np.array(y, dtype=float)
    z = np.array(z, dtype=float)

    # Convert to cylindrical coordinates.
    rho = np.hypot(x, y)
    phi = np.arctan2(y, z)
    z = z

    return rho, phi, z

def cartesian_to_spherical_3d(x, y, z):
    """Convert cartesian points to cylindrical points.

    Convert cartesian coordinate points in 3D to cylindrical coordinate points
    in 3D. This function uses the notation convention of ISO 80000-2:2009 and
    its related successors.

    Parameters
    ----------
    x : array_like
        The x values of the points to be transformed.
    y : array_like
        The y values of the points to be transformed.
    z : array_like
        The z values of the points to be transformed.

    Returns
    -------
    r : array_like
        The rho (radial) values of the points after transformation.
    theta : array_like
        The theta (azimuthal angle) values of the points after the
        transformation.
    phi : array_like
        The phi (polar angle) values of the points after the transformation.
    """

    # Basic validation.
    x = np.array(x, dtype=float)
    y = np.array(y, dtype=float)
    z = np.array(z, dtype=float)

    # Convert to spherical coordinates.
    r = np.sqrt(x**2 + y**2 + z**2)
    theta = np.arccos(z/r)
    phi = np.arctan2(y, x)

    return r, theta, phi

########################################################################
##########                  From Polar to ***                 ##########
########################################################################

def polar_to_cartesian_2d(rho, phi):
    """Convert polar points to cartesian points.

    Convert polar coordinate points in 2D to cartesian coordinate points in 2D.
    This function uses the notation convention of ISO 80000-2:2009 and its
    related successors.

    Parameters
    ----------
    rho : array_like
        The rho values of the points to be transformed.
    phi : array_like
        The phi values of the points to be transformed.

    Returns
    -------
    x : array_like
        The x values of the points after transformation.
    y : array_like
        The y values of the points after transformation.
    """

    # Basic type checking
    rho = np.array(rho, dtype=float)
    phi = np.array(phi, dtype=float)

    # Convert
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)

    return x, y

########################################################################
##########               From Cylindrical to ***              ##########
########################################################################

def cylindrical_to_cartesian_3d(rho, phi, z):
    """Convert cylindrical points to cartesian points.

    Convert cylindrical coordinate points in 3D to cartesian coordinate points
    in 3D. This function uses the notation convention of ISO 80000-2:2009 and
    its related successors.

    Parameters
    ----------
    rho : array_like
        The rho values of the points to be transformed.
    phi : array_like
        The phi values of the points to be transformed.
    z : array_like
        The z values of the points to be transformed.

    Returns
    -------
    x : array_like
        The x values of the points after transformation.
    y : array_like
        The y values of the points after transformation.
    z : array_like
        The z values of the points after transformation.
    """

    # Basic type checking
    rho = np.array(rho, dtype=float)
    phi = np.array(phi, dtype=float)
    z = np.array(z, dtype=float)

    # Convert
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)
    z = z

    return x, y, z

def cylindrical_to_spherical_3d(rho, phi, z):
    """Convert cylindrical points to spherical points.

    Convert cylindrical coordinate points in 3D to spherical coordinate points
    in 3D. This function uses the notation convention of ISO 80000-2:2009 and
    its related successors.

    Parameters
    ----------
    rho : array_like
        The rho values of the points to be transformed.
    phi : array_like
        The phi values of the points to be transformed.
    z : array_like
        The z values of the points to be transformed.

    Returns
    -------
    r : array_like
        The rho (radial) values of the points after transformation.
    theta : array_like
        The theta (azimuthal angle) values of the points after the
        transformation.
    phi : array_like
        The phi (polar angle) values of the points after the transformation.
    """

    # Basic type checking
    rho = np.array(rho, dtype=float)
    phi = np.array(phi, dtype=float)
    z = np.array(z, dtype=float)

    # Convert
    r = np.hypot(rho, z)
    theta = np.arccos(z/r)
    phi = phi

    return r, theta, phi

########################################################################
##########                From Spherical to ***               ##########
########################################################################

def spherical_to_cartesian_3d(r, theta, phi):
    """Convert spherical points to cartesian points.

    Convert spherical coordinate points in 3D to cartesian coordinate points
    in 3D. This function uses the notation convention of ISO 80000-2:2009 and
    its related successors.

    Parameters
    ----------
    r : array_like
        The r values of the points to be transformed.
    theta : array_like
        The theta values of the points to be transformed.
    phi : array_like
        The phi values of the points to be transformed.

    Returns
    -------
    x : array_like
        The x values of the points after transformation.
    y : array_like
        The y values of the points after transformation.
    z : array_like
        The z values of the points after transformation.
    """

    # Basic type checking
    r = np.array(r, dtype=float)
    theta = np.array(theta, dtype=float)
    phi = np.array(phi, dtype=float)

    # Convert
    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

def spherical_to_cylindrical_3d(r, theta, phi):
    """Convert cylindrical points to cartesian points.

    Convert cylindrical coordinate points in 3D to cartesian coordinate points
    in 3D. This function uses the notation convention of ISO 80000-2:2009 and
    its related successors.

    Parameters
    ----------
    r : array_like
        The r values of the points to be transformed.
    theta : array_like
        The theta values of the points to be transformed.
    phi : array_like
        The phi values of the points to be transformed.

    Returns
    -------
    rho : array_like
        The rho values of the points after transformation.
    phi : array_like
        The phi (angular) values of the points after transformation.
    z : array_like
        The z values of the points after transformation.
    """

    # Basic type checking
    r = np.array(r, dtype=float)
    theta = np.array(theta, dtype=float)
    phi = np.array(phi, dtype=float)

    # Convert
    rho = r * np.sin(theta)
    phi = phi
    z = r * np.cos(theta)

    return rho, phi, z


# Import required libraries:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from sklearn.metrics import r2_score

# Generating dataset:
# Y = A*sin(B(X + C)) + D
# A = Amplitude
# Period = 2*pi/B
# Period = Length of One Cycle
# C = Phase Shift (In Radian)
# D = Vertical Shift

X = np.linspace(0,1,100)             #(Start,End,Points)

# Here…
# A = 1
# B= 2*pi
# B = 2*pi/Period
# Period = 1
# C = 0
# D = 0

Y = 1*np.sin(2*np.pi*X)

# Adding some Noise :
Noise = 0.4*np.random.normal(size=100)
Y_data = Y + Noise
plt.scatter(X,Y_data,c="r")

# Calculate the value:
def calc_sine(x,a,b,c,d):
  return a * np.sin(b* ( x + np.radians(c))) + d

# Finding optimal parameters :
popt,pcov = curve_fit(calc_sine,X,Y_data)

# Plot the main data :
plt.scatter(X,Y_data)# Plot the best fit curve :
plt.plot(X,calc_sine(X,*popt),c="r")
plt.show()

# Check the accuracy :
Accuracy =r2_score(Y_data,calc_sine(X,*popt))
print (Accuracy)

# Function to calculate the value :
def calc_line(X,m,b):
  return b + X*m

# It returns optimized parametes for our function :
# popt stores optimal parameters
# pcov stores the covarience between each parameters.
popt,pcov = curve_fit(calc_line,X,Y_data)

# Plot the main data :
plt.scatter(X,Y_data)

# Plot the best fit line :
plt.plot(X,calc_line(X,*popt),c="r")
plt.show()

# Check the accuracy of model :
Accuracy =r2_score(Y_data,calc_line(X,*popt))
print ("Accuracy of Linear Model : ",Accuracy)


# Plot ridge regression applied to  1d polynomial problem
# Based on https://github.com/probml/pmtk3/blob/master/demos/polyfitRidgeLasso.m

import numpy as np
import matplotlib.pyplot as plt
from pyprobml_utils import save_fig

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error as mse

def make_1dregression_data(n=21):
    np.random.seed(0)
    xtrain = np.linspace(0.0, 20, n)
    xtest = np.arange(0.0, 20, 0.1)
    sigma2 = 4
    w = np.array([-1.5, 1/9.])
    fun = lambda x: w[0]*x + w[1]*np.square(x)
    ytrain = fun(xtrain) + np.random.normal(0, 1, xtrain.shape) * \
        np.sqrt(sigma2)
    ytest= fun(xtest) + np.random.normal(0, 1, xtest.shape) * \
        np.sqrt(sigma2)
    return xtrain, ytrain, xtest, ytest

xtrain, ytrain, xtest, ytest = make_1dregression_data(n=21)

#Rescaling data
scaler = MinMaxScaler(feature_range=(-1, 1))
Xtrain = scaler.fit_transform(xtrain.reshape(-1, 1))
Xtest = scaler.transform(xtest.reshape(-1, 1))

deg = 14
alphas = np.logspace(-10, 1.3, 10)
nalphas = len(alphas)
mse_train = np.empty(nalphas)
mse_test = np.empty(nalphas)
ytest_pred_stored = dict()
for i, alpha in enumerate(alphas):
    model = Ridge(alpha=alpha, fit_intercept=False)
    poly_features = PolynomialFeatures(degree=deg, include_bias=False)
    Xtrain_poly = poly_features.fit_transform(Xtrain)
    model.fit(Xtrain_poly, ytrain)
    ytrain_pred = model.predict(Xtrain_poly)
    Xtest_poly = poly_features.transform(Xtest)
    ytest_pred = model.predict(Xtest_poly)
    mse_train[i] = mse(ytrain_pred, ytrain)
    mse_test[i] = mse(ytest_pred, ytest)
    ytest_pred_stored[alpha] = ytest_pred

# Plot MSE vs degree
fig, ax = plt.subplots()
mask = [True]*nalphas
ax.plot(alphas[mask], mse_test[mask], color = 'r', marker = 'x',label='test')
ax.plot(alphas[mask], mse_train[mask], color='b', marker = 's', label='train')
ax.set_xscale('log')
ax.legend(loc='upper right', shadow=True)
plt.xlabel('L2 regularizer')
plt.ylabel('mse')
save_fig('polyfitVsRidge.pdf')
plt.show()

# Plot fitted functions
chosen_alphas = alphas[[0,5,8]]
for i, alpha in enumerate(chosen_alphas):
    fig, ax = plt.subplots()
    ax.scatter(xtrain, ytrain)
    ax.plot(xtest, ytest_pred_stored[alpha])
    plt.title('L2 regularizer {:0.5f}'.format(alpha))
    save_fig('polyfitRidge{}.pdf'.format(i))
    plt.show()


from scipy import stats
import numpy as np

############################
# CALCULATING CORRELATIONS #
############################

array_1 = np.array([1,2,3,4,5,6])  # Create a numpy array from a list
array_2 = array_1  # Create another array with the same values

print(stats.pearsonr(array_1, array_2))  # Calculate the correlation which will be 1 since the values are the same

#######################
# NORMAL DISTRIBUTION #
#######################
x = stats.norm.rvs(loc=0, scale=10, size=10)  # Generate 10 values randomly sampled from a normal distribution with mean 0 and standard deviation of 10

print(x)

################################
# PROBABILITY DENSITY FUNCTION #
################################
p1 = stats.norm.pdf(x=-100, loc=0, scale=10)  # Get probability of sampling a value of -100
p2 = stats.norm.pdf(x=0, loc=0, scale=10)     # Get probability of sampling a value of 0

print(p1)
print(p2)

####################################
# CUMULATIVE DISTRIBUTION FUNCTION #
####################################
p1 = stats.norm.cdf(x=0, loc=0, scale=10)  # Get probability of sampling a value less than or equal to 0

print(p1)

######################################
# CALCULATING DESCRIPTIVE STATISTICS #
######################################
print(stats.describe(stats.norm.rvs(loc=0, scale=1, size=500)))  # Calculate descriptive statistics for 500 data points sampled from normal distribution with mean 0 and standard deviation of 1


import sympy as sp

x = sp.Symbol('x')
f = (x**3)/(1+x**(1/2))
print('Integrate the following algebraic expression from 1 to 2')
sp.pprint(f)

lim_inf=1
lim_sup=2
# Numerical integration by Simpson 1/3 method
print('\nSimpson 1/3\n')
n = 2   # For Simpson 1/3 n=2
h = (lim_sup-lim_inf)/n
x0 = lim_inf
f0 = f.subs(x,x0)
x1 = x0+h
f1 = f.subs(x,x1)
x2 = x1+h
f2 = f.subs(x,x2)
print('h = ',h,'\nx0 = ',x0,'\nf0 = ',f0,'\nx1 = ',x1,'\nf1 = ',f1,'\nx2 = ',x2,'\nf2 = ',f2)
I = (h/3)*(f0+4*f1+f2)
print('Result = ',I)

# Numerical integration by Simpson 3/8 method
print('\nSimpson 3/8\n')
n = 3   # For Simpson 3/8 n=3
h = (lim_sup-lim_inf)/n
x0 = lim_inf
f0 = f.subs(x,x0)
x1 = x0+h
f1 = f.subs(x,x1)
x2 = x1+h
f2 = f.subs(x,x2)
x3 = x2+h
f3 = f.subs(x,x3)
print('h = ',h,'\nx0 = ',x0,'\nf0 = ',f0,'\nx1 = ',x1,'\nf1 = ',f1,'\nx2 = ',x2,'\nf2 = ',f2,'\nx3 = ',x3,'\nf3 = ',f3)
I = (3*h/8)*(f0+3*f1+3*f2+f3)
print('Result = ',I)


#Python3 Steven
import numpy as np
import matplotlib.pyplot as plt

def plotPowerFuc():
    x = np.linspace(-4,4,100)
    e = range(-1,4)
    for i in e:
        y = x**i
        plt.plot(x,y,label='y=x**%s'%i,linewidth=2)

    plt.xlim(x[0],x[-1])
    plt.ylim(-20,20)
    plt.legend()
    plt.show()

def plotLine(n=80):
    for k in range(n):
        #plt.plot([0, np.cos(2*np.pi*k/n)], [0, np.sin(2*np.pi*k/n)])
        plt.plot([0, np.cos(2*np.pi*k/n)], [0, np.sin(2*np.pi*k/n)],color=[k/n,k/n,k/n])
        #plt.plot([0,k*np.cos(2*np.pi*k/n)], [0, k*np.sin(2*np.pi*k/n)],color=[k/n,k/n,k/n])
        #plt.plot([0,k*np.cos(2*np.pi*k/n)], [0, k*np.sin(2*np.pi*k/n)])

    plt.axis('square')
    plt.show()

def complexRoot(n=2):
    #Z**n=1
    for k in range(n):
        yield np.exp(2*np.pi*1j*k/n)

def plotCompexRoot(n=50):
    for k in range(n):
        #z = np.exp(2*np.pi*1j*k/n)
        z = k*np.exp(2*np.pi*1j*k/n)
        #plt.plot([0,np.real(z)], [0,np.imag(z)])
        plt.plot([0,np.real(z)], [0,np.imag(z)],color=[0,0,0])

    plt.axis('square')
    plt.show()

def plotTrigonometry():
    t = np.linspace(0,8*np.pi,1000)
    r1 = np.random.rand()
    r2 = np.random.rand()

    x = np.cos(r1*t)
    y = np.cos(r2*t)
    plt.plot(x,y,'k')
    plt.title('r1=%s,r2=%s'%(np.round(r1,2),np.round(r2,2)))
    plt.axis('square')
    plt.show()

def derivative(f,x,h=0.0001): #slop
    return (f(x+h)-f(x))/h

def func(x):
    return x**8
    #return x**3

def plotTangent():
    x = np.linspace(-1,1,50)
    xT = np.linspace(-1,1,100)
    bound=[-2,2]
    for i in xT:
        y=func(i)
        slope = derivative(func,i)
        b = y-slope*i
        plt.plot([bound[0],bound[1]],[slope*bound[0]+b,slope*bound[1]+b],color=[abs(i)/3,abs(i)/2,abs(i)/3])
    #plt.axis('square')
    plt.axis('off')
    plt.plot(x,func(x))
    plt.xlim(x[0],x[-1])
    plt.ylim(-3,3)
    plt.show()

def main():
    #plotPowerFuc()
    #plotLine()
    #plotCompexRoot()
    #plotTrigonometry()
    plotTangent()

if __name__=='__main__':
    main()


import numpy as np

"""This program aim to:
    1) on the fly construct a list of prime number
    2) strip out all the prime factor from target """

input_num = 600851475143
#input_num = 13195

#INT
primes = np.empty(1000,dtype=int)
counters = np.empty(1000,dtype=int)
target = input_num
factors = []
n = 0

print("TARGET = ", target)

#manual check 2
while(target % 2 == 0):
    target //= 2
    factors.append(2)

print("Try dividing by: 2, remainder =",target)

# Loop through all the odd numbers to find primes
# until all the factors are found
i = 3
while i*i <= target:
    for j in range(n):
        counters[j] += 2
        if(counters[j] >= primes[j]):
            counters[j] -= primes[j]

# if none of the counters are zero, it is a prime
    if not any(counters[:n] == 0):
        primes[n] = i
        counters[n] = 0
        n += 1

# try strip factors from target using the newly found prime
        while(target % i == 0):
            target //= i
            factors.append(i)

        print("Try dividing by:",i, ", remainder =",target)

    i += 2

# OUTPUT
if(target == 1):
# write a pythonic statement in case my TA complains I am writing C
    print(input_num, "=", " x ".join([str(x) for x in factors]))
    print("LARGEST PRIME FACTOR = ", primes[n-1])
else:
    factors.append(target)
    print(input_num, "=", " x ".join([str(x) for x in factors]))
    print("LARGEST PRIME FACTOR = ", target)


import numpy as np
from numpy.lib.function_base import append

# 传入源数据，源数据是n×m的数据矩阵，n是事务个数，m是事务特征个数
# k是降维后的维度，当k=-1是默认值,k=其它值时代表它传入了这个参数，否则k值由程序决定
# 返回降维后的数据，它是n×k的矩阵
def PCA(src_data,k=-1):
    # 求均值
    avg = np.array([np.mean(src_data[:, i]) for i in range(len(src_data[0]))])
    src_sub=src_data-avg
    print(1)
    # 求协方差矩阵
    C=np.dot(np.transpose(src_sub),src_sub)
    print(2)
    # C=np.divide(C,len(src_data))
    # 求特征值，特征向量
    val, vec = np.linalg.eig(C)
    print(3)
    # 将特征值和特征向量组合
    characteristic=[]
    for i in range(len(val)):
        characteristic.append([val[i],vec[:,i]])
    print(4)
    # 然后排序
    characteristic.sort(key=lambda characteristic: characteristic[0], reverse=True)
    print(5)
    # 当k=-1时，意味着我们要自己决定k
    if k==-1:
        # stop为阈值
        stop=0.95
        sum=0
        for i in range(len(characteristic)):
            sum=sum+characteristic[i][0]
        stop=stop*sum
        for i in range(len(characteristic)-1,-1,-1):
            if sum-characteristic[i][0]>=stop:
                sum=sum-characteristic[i][0]
            else:
                break
        k=i+1

    # 获得前k个特征向量，组成矩阵
    vec=[]
    for i in range(k):
        vec.append(characteristic[i][1])

    # 与减去平均值的矩阵相乘，获得降维后的数据
    data=np.dot(src_sub,np.transpose(vec))
    return data


import numpy as np
from findiff import FinDiff
from scipy.special import factorial

def taylor_approximate(pars, x0s, derivs, order=None):
    '''
    Computes the nth order Taylor series approximation to a function given
    - derivs: a list of n derivatives
    - x0s: the point about which we are expanding
    - pars: the coordinates at which we want to expand the function
    If order is specified it will go to that order instead of using the full list of derivatives.
    '''
    Nparams = len(x0s)
    output_shape = derivs[0].shape

    if order is None:
        order = len(derivs) - 1
        print("Taking the order %d Taylor series."%(order))

    Fapprox = np.zeros(output_shape)
    diffs =  [ (pars[ii] - x0s[ii]) for ii in range(Nparams) ]

    for oo in range(order+1):
        if oo == 0:
            Fapprox += derivs[oo]
        else:
            param_inds = np.meshgrid( * (np.arange(Nparams),)*oo, indexing='ij')
            PInds = [ind.flatten() for ind in param_inds]

            term = 0

            for iis in zip(*PInds):
                term += derivs[oo][iis] * np.prod([ diffs[ii] for ii in iis ])

            Fapprox += 1/factorial(oo) * term

    return Fapprox

def compute_derivatives(Fs, dxs, center_ii, order):
    '''
    Computes all the partial derivatives up to order 'order' given a function on a grid Fs
    The grid separation is given by dxs, and the derivatives are computed at the grid point 'center_ii.'

    Assumes that Fs is gridded in the standard matrix way (i.e. indexing = 'ij' in numpy) as
    opposed to Cartesian indexing that one uses for plotting.
    '''

    # assume the structure of the input is
    # [Npoints,]*Nparams + output_shape, where Nparams is also the length of dxs

    Nparams = len(dxs)
    output_shape = Fs.shape[len(dxs):]

    derivs = []

    for oo in range(order+1):
        if oo == 0:
            derivs += [Fs[center_ii]]
        else:
            dnFs = np.zeros( (Nparams,)*oo + output_shape)

            # Want to get a list of all the possible d/dx_i dx_j dx_k ...
            param_inds = np.meshgrid( * (np.arange(Nparams),)*oo, indexing='ij')
            PInds = [ind.flatten() for ind in param_inds]

            for iis in zip(*PInds):
                # build a string of (xk, dxk, 1) for taking the d/dxk derivative in sequence
                deriv_tuple = []
                for ii in iis:
                    deriv_tuple += [(ii, dxs[ii],1),]

                dndx = FinDiff(*deriv_tuple)

                dnFs[iis] += dndx(Fs)[center_ii]

            derivs += [dnFs]

    return derivs


import numpy as np
from ._Regularization import _L1Reg,_L2Reg

def MeanSquareCost(h,y,ThetaW,L1=0.0,L2=0.0):
	m = y.shape[0]
	#diff = (h-y)**2
	#print(h.dtype,y.dtype,diff.dtype,h.min(),h.max(),y.min(),y.max(),diff.min(),diff.max(),np.sum(diff))
	#J = np.sum((h - y)**2)/(2*m)
	J = np.mean((h - y)**2)/2

	if L1 > 0.0:
		L1Reg = _L1Reg(ThetaW,L1,m)
	else:
		L1Reg = 0.0

	if L2 > 0.0:
		L2Reg = _L2Reg(ThetaW,L2,m)
	else:
		L2Reg = 0.0

	J = J + L1Reg + L2Reg
	return J

def MeanSquareDelta(h,y,InvAFgrad):

	return (h - y)*InvAFgrad(h)


#!/usr/bin/env python
#   Author: Christopher Bull.
#   Affiliation:  British Antarctic Survey
#                 Cambridge, UK
#   Contact: chbull@bas.ac.uk
#   www:     christopherbull.com.au
#   Date created: Sun, 13 Mar 2022 14:29:36
#   Machine created on: SB2Vbox
"""
Step 1: 1-D Linear Convection
"""
from cb2logger import *
import numpy                       #here we load numpy
from matplotlib import pyplot      #here we load matplotlib
import matplotlib.pyplot as plt
import time, sys                   #and load some utilities

if __name__ == "__main__":
    LogStart('',fout=False)
    #Now let's define a few variables; we want to define an evenly spaced grid of points within a spatial domain that is 2 units of length wide, i.e., 𝑥𝑖∈(0,2). We'll define a variable nx, which will be the number of grid points we want and dx will be the distance between any pair of adjacent grid points.

    nx = 41  # try changing this number from 41 to 81 and Run All ... what happens?
    #CB: seems like '81' is the necessary amount of resolution required to not difuse the bump, 61 does better but not as good as 81

    dx = 2 / (nx-1)
    nt = 25    #nt is the number of timesteps we want to calculate
    dt = .025  #dt is the amount of time each timestep covers (delta t)
    c = 1      #assume wavespeed of c = 1

    #We also need to set up our initial conditions. The initial velocity 𝑢0 is given as 𝑢=2 in the interval 0.5≤𝑥≤1 and 𝑢=1 everywhere else in (0,2) (i.e., a hat function).
    u = numpy.ones(nx)      #numpy function ones()
    u[int(.5 / dx):int(1 / dx + 1)] = 2  #setting u = 2 between 0.5 and 1 as per our I.C.s

    pyplot.plot(numpy.linspace(0, 2, nx), u)
    #Why doesn't the hat function have perfectly straight sides? Think for a bit.

    #cb: b/c nx has a small number of points (goes to vertical as nx--> \infinity)
    #plt.show()

    un = numpy.ones(nx) #initialize a temporary array

    for n in range(nt):  #loop for values of n from 0 to nt, so it will run nt times
        un = u.copy() ##copy the existing values of u into un
        for i in range(1, nx): ## you can try commenting this line and...
        #for i in range(nx): ## ... uncommenting this line and see what happens!
            u[i] = un[i] - c * dt / dx * (un[i] - un[i-1])

    pyplot.plot(numpy.linspace(0, 2, nx), u)
    plt.show()

    lg.info('')
    localtime = time.asctime( time.localtime(time.time()) )
    lg.info("Local current time : "+ str(localtime))
    lg.info('SCRIPT ended')


import numpy as np
import matplotlib.pyplot as plt

def P_cumulativa(pi):
  ''' Calcula a CDF F(x) de uma PMF P(x) '''
  pif = [pi[0]]
  for i in range(1, len(pi)):
    pif.append( pif[-1] + pi[i])
  return pif

def escolhe_estado(S, fpi):
  ''' Dada uma Cadeia de Markov com vetor de estados S e CDF F(i), escolhe aleatoriamente o próximo estado '''
  r = np.random.rand(1)[0]
  for i in range(len(S)):
    if fpi[i] >= r:
      return S[i]

def simular_cadeia_markov(S, pi, P, n, m):
  '''
  Realiza m simulações numéricas com n instâncias de uma Cadeia de Markov com estados S,
  probabilidades iniciais pi e matriz de transição P
  '''
  processo = np.zeros((m,n))
  for j in range(m):
    pi_t = pi
    pif = P_cumulativa(pi_t)
    processo[j, 0] = escolhe_estado(S, pif)
    for i in range(1, n):
      p = P[ int(processo[j, i - 1]) , : ]
      pf = P_cumulativa(p)
      processo[j, i] = escolhe_estado(S, pf)
  return processo

def mat_pot(mat, n):
  ''' Função atalho para a exponenciação de matrizes '''
  return np.linalg.matrix_power(mat, n)

def e_regular(mat, n):
  ''' Indica se a matriz mat elevada à potência n é regular, isto é, todos os seus valores são maiores que zero. '''
  return np.all(mat_pot(mat,n) > 0)

def transicao(pi, P, n):
  ''' Calcula analiticamente o vetor de estados após n transições de uma Cadeia de Markov com matriz de transição P '''
  return pi.dot(mat_pot(P, n))

def simular_convergencia(S, pi, P, n, nomes=None):
  '''
  Avalia numericamente e visualmente a convergência para n passos de uma Cadeia de Markov com estados S,
  probabilidades iniciais pi e matriz de transição P
  '''
  pit = pi
  ns = pi.shape[0]
  pis = np.zeros((n,ns))
  for i in range(n):
    pit = pit.dot(P)
    pis[i,:] = pit
  for i in range(ns):
    plt.plot(pis[:,i], label="{}".format(i if nomes is None else nomes[i]))
  plt.legend()
  plt.tight_layout()

def dist_estacionaria(S, P):
  '''
  Calcula analiticamente a distribuição estacionária de uma Cadeia de Markov com estados S,
  e matriz de transição P
  '''
  m = len(S)
  A = np.append(P.T - np.identity(m), np.ones((1,m)),axis=0)
  b = np.zeros(m+1)
  b[-1] = 1
  b = b.T
  return np.linalg.solve(A.T.dot(A), A.T.dot(b))


# Dado uma lista com n elementos, escreva um programa em Python que retorne as seguintes medidas:
# A média aritmética e geométrica;
# Moda;
# Mediana.

import numpy #importação para o cálculo do produto dos números da lista
from math import prod #importação para o cálculo do produto dos números da lista
import statistics #importação para a moda
import math #importação para efetuar alguns cálculos matemáticos (fórmulas)

lista_elementos = [1,5,6,2,5,9,10,15,2,5] #lista aleatória

#len() é a fórmula para contar a quantidade de elementos que tem na lista

# Média aritimética: a soma dos elementos da lista dividido pela quantidade de elementos que a lista tem
media_arit = sum(lista_elementos)/len(lista_elementos) #sun() é a fórmula de soma
print("A média aritmética é ", media_arit)

# Média geométrica: é a raíz dos N elementos da multiplicação dos elementos.ATENÇÃO para o 1/len pois é raíz
media_geo = numpy.prod(lista_elementos)**(1/len(lista_elementos))
print("A média geométrica é ", media_geo)
media_goe2 = prod(lista_elementos)**(1/len(lista_elementos))
print("A média geométrica de outro jeito é: ",media_goe2)

# Para calcular a mediana precisamos organizar a lista, e para facilitar eu criei um valor com a quantidade de elementos da lista para facilitar nos cálculos, pra não ficar um fórmula dentro de outra.

lista_ordenada = sorted(lista_elementos) #função para ordenar os elementos da lista de forma crescente
print(lista_ordenada)

elementos = len(lista_elementos)
print("Quantidade de elementos na lista: ", elementos)

meio=int(elementos/2) #função para verificar qual a posição do meio na lista (é posição, não é o número)
print("Posição do elemento central: ", meio+1) #como começa com índice zero, considerei o + 1 porque iniciamos a contagem em 1, 2, 3... e o python inicia a contagem do index em 0, 1, 2...

# Medida: SE a quantidade de elementos é par, a mediana é a soma do valor antecessor ao meio e o valor do meio, divido por 2. SE a quantidade de elementos é ímpar, a mediana é o valor central.
if (len(lista_ordenada)%2)==0: #verificar se o resto da divisão por 2 é zero, se for é par.
    mediana = ((lista_ordenada[meio-1])+lista_ordenada[meio])/2
    print("A mediana é: ", mediana)
else:
    mediana = lista_ordenada[meio] #função que acessa o valor do meio da lista
    print("A mediana é: ", mediana)
# Volte ao Ex05 para ver maiores explicações sobre essa questão do posicionamento.

# Moda: valor que aparece mais vezes na lista
# não consegui fazer sem usar fórumla!!!
# SE a quantidade de elementos da fórmula multimodal for apenas 1, significa que não é multimodal, é apenas 1 número com maior frequência. Caso contrário, significa que os valores são multimodais.
if (len(statistics.multimode(lista_ordenada))==1):
    moda = statistics.mode(lista_ordenada) #para o caso de ser somente uma moda
    print("A moda é: ",moda)
else:
    moda2 = statistics.multimode(lista_ordenada) #para o caso de ser multimodal
    print("A moda multimodal é: ", moda2)
#Fazer com a lista original gera resultados diferentes, pois ela vai considerar o primeiro valor que se repete da lista, por exemplo, vai retornar [5,2] na multimodal. E não [2,5] na lista ordenada.

#Statistics.multimode e statistics.mode são fórmulas de moda e moda multimodal.

# Prof indicou esse site: https://www.geeksforgeeks.org/finding-mean-median-mode-in-python-without-libraries/ para o cálculo da moda sem usar uma fórmula já definida.

# OBS: se você conseguir entender, por gentileza, me explique!!!!

#Segue o código que tem no link:

# Python program to print
# mode of elements
from collections import Counter

# list of elements to calculate mode
n_num = [1, 2, 3, 4, 5, 2]
n = len(n_num)

data = Counter(n_num)
get_mode = dict(data)
mode = [k for k, v in get_mode.items() if v == max(list(data.values()))]

if len(mode) == n:
    get_mode = "No mode found"
else:
    get_mode = "Mode is / are: " + ', '.join(map(str, mode))

print(get_mode)


# -*- coding: utf-8 -*-
"""
SYS-611: Example Markov Process Generator

@author: Paul T. Grogan, pgrogan@stevens.edu
"""

# import the python3 behavior for importing, division, and printing in python2
from __future__ import absolute_import, division, print_function

# import the numpy package and refer to it as `np`
# see http://docs.scipy.org/doc/numpy/reference/ for documentation
import numpy as np
# import the matplotlib.pyplot package and refer to it as `plt`
import matplotlib.pyplot as plt

# define the state transition function
def next_state(q):
    r = np.random.rand()
    # if it is a clear day
    if q == 0:
        if r < 186/250:
            # another clear day
            return 0
        elif r < (186+47)/250:
            # a rainy day next
            return 1
        else:
            # a snowy day next
            return 2
    # if it is a rainy day
    elif q == 1:
        if r < 47/89:
            # a clear day next
            return 0
        elif r < (47+40)/89:
            # another rainy day
            return 1
        else:
            # a snowy day next
            return 2
    # if it is a snowy day
    else:
        if r < 16/25:
            # a clear day next
            return 0
        elif r < (16+3)/25:
            # a rainy day next
            return 1
        else:
            # another snowy day
            return 2

# define the number of samples and create a state trajectory
num_samples = 100
np.random.seed(0)
q = np.zeros(num_samples)
# perform all the state transitions
for t in range(num_samples - 1):
    q[t+1] = next_state(q[t])

# create a plot of the state trajectory
plt.figure()
plt.step(range(num_samples), q, '-r')
plt.xlabel('Time ($t$)')
plt.ylabel('State ($q$)')

# estimate the stationary distribution from the samples
pi = np.zeros(3)
for i in range(3):
    pi[i] = np.sum(q==i)/num_samples

print('estimated stationary distribution (solved using simulation):')
print(' P(q=0) = {:.3f} (clear day)'.format(pi[0]))
print(' P(q=1) = {:.3f} (rainy day)'.format(pi[1]))
print(' P(q=2) = {:.3f} (snowy day)'.format(pi[2]))

#%% steady-state analysis

# formal state transition matrix
P = [[186/250, 47/250, 17/250],
     [47/89, 40/89, 2/89],
     [16/25, 3/25, 6/25]]

# compute the eigenvalues and eigenvectors of the transpose of P
w,v = np.linalg.eig(np.transpose(P))

# the stationary distribution is the normalized eigenvector
# corresponding to the eigenvalue of 1
pi_exact = v[:,0]/np.sum(v[:,0])

print('exact stationary distribution (solved using eigenvectors):')
print(' P(q=0) = {:.3f} (clear day)'.format(pi_exact[0]))
print(' P(q=1) = {:.3f} (rainy day)'.format(pi_exact[1]))
print(' P(q=2) = {:.3f} (snowy day)'.format(pi_exact[2]))


# The prime factors of 13195 are 5, 7, 13 and 29.
# What is the largest prime factor of the number 600851475143 ?

import numpy as np

def solve():

    number = 600851475143

    max_factor = int(np.sqrt(number))
    print("Max_factor", max_factor)

    # Looking for prime numbers

    numbers = [i for i in range(1, max_factor+1)]
    is_prime = [True for i in range(max_factor)]
#    print(numbers)
#    print(is_prime)

    current_prime_index = 1
    prime_index_condition = current_prime_index < len(is_prime)
    while prime_index_condition:
        print("Current prime number:", current_prime_index+1)
        # We bar the multiples
        is_prime_traversal = 2 * current_prime_index + 1
        condition = is_prime_traversal < len(is_prime)
        while condition:
            is_prime[is_prime_traversal] = False
            is_prime_traversal += current_prime_index + 1
            condition = is_prime_traversal < len(is_prime)
#        print(numbers)
#        print(is_prime)
        # Next prime
        next_prime_index = current_prime_index + 1
        condition = next_prime_index < len(is_prime)
        condition = condition and (is_prime[next_prime_index] == False)
        while condition:
            next_prime_index = next_prime_index + 1
            condition = (next_prime_index < len(is_prime))

            condition = condition and (is_prime[next_prime_index] == False)
        current_prime_index = next_prime_index
        prime_index_condition = current_prime_index < len(is_prime)

    prime_numbers = [n for n, p in zip(numbers, is_prime) if p]

    for factor in reversed(prime_numbers):
        if number%factor == 0:
            return factor

    return

print(solve())


import numpy as np

"""
A2-Part-1: Generate a sinusoid

Write a function to generate a real sinusoid (use np.cos()) given its amplitude A, frequency f (Hz), initial phase phi (radians),
sampling rate fs (Hz) and duration t (seconds).

All the input arguments to this function (A, f, phi, fs and t) are real numbers such that A, t and fs are
positive, and fs > 2*f to avoid aliasing. The function should return a numpy array x of the generated
sinusoid.

EXAMPLE: If you run your code using A=1.0, f = 10.0, phi = 1.0, fs = 50.0 and t = 0.1, the output numpy
array should be: array([ 0.54030231, -0.63332387, -0.93171798,  0.05749049,  0.96724906])
"""
def genSine(A, f, phi, fs, t):
    """
    Inputs:
        A (float) =  amplitude of the sinusoid
        f (float) = frequency of the sinusoid in Hz
        phi (float) = initial phase of the sinusoid in radians
        fs (float) = sampling frequency of the sinusoid in Hz
        t (float) =  duration of the sinusoid (is second)
    Output:
        The function should return a numpy array
        x (numpy array) = The generated sinusoid (use np.cos())
    """
    ## Your code here
    N = fs * t
    time_domain = np.arange(0, N)
    print(time_domain)
    x = A * np.cos(2 * np.pi * f * time_domain / fs + phi)
    return(x)


# coding: utf-8
Возьмем как пример распределение Лапласса
# In[1]:

import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stat
get_ipython().run_line_magic('matplotlib', 'inline')

# In[2]:

a = 1.0
b = 0.0
ld = stat.laplace(loc=b, scale=a)
x = np.linspace(-4, 4, 1000)
plt.plot(x, stat.laplace.pdf(x))

# In[3]:

sample = ld.rvs(size=1000)
plt.hist(sample, normed=True, bins=30)
plt.ylabel('number of samples')
plt.xlabel('$x$')

# In[4]:

sizes = [5, 10, 30, 45]
samples = []
for i in sizes:
    samples.append(stat.laplace.rvs(size=[1000, i]))

# In[5]:

means_of_sample = []
for sample in samples:
    means = []
    for i in sample:
        means.append(np.mean(i))
    means_of_sample.append(means)

# In[6]:

mean = b
dispersion = 2 / ((2 * stat.laplace.pdf(mean)) ** 2)
print("mean: " + str(mean)
      + "\ndispersion: " + str(dispersion))

# In[7]:

norm_rv = stat.norm(mean, np.sqrt(dispersion / sizes[0]))
x = np.linspace(-4, 4, 1000)
pdf = norm_rv.pdf(x)
plt.plot(x, pdf)

plt.hist(means_of_sample[0], normed=True)
plt.ylabel('number of samples')
plt.xlabel('n = 5')

# In[11]:

norm_rv = stat.norm(mean, np.sqrt(dispersion / sizes[1]))
x = np.linspace(-4, 4, 1000)
pdf = norm_rv.pdf(x)
plt.plot(x, pdf)

plt.hist(means_of_sample[1], normed=True)
plt.ylabel('number of samples')
plt.xlabel('n = 30')

# In[12]:

norm_rv = stat.norm(mean, np.sqrt(dispersion / sizes[2]))
x = np.linspace(-4, 4, 1000)
pdf = norm_rv.pdf(x)
plt.plot(x, pdf)

plt.hist(means_of_sample[2], normed=True)
plt.ylabel('number of samples')
plt.xlabel('n = 50')

# In[13]:

norm_rv = stat.norm(mean, np.sqrt(dispersion / sizes[3]))
x = np.linspace(-4, 4, 1000)
pdf = norm_rv.pdf(x)
plt.plot(x, pdf)

plt.hist(means_of_sample[3], normed=True)
plt.ylabel('number of samples')
plt.xlabel('n = 100')

Таким образом, чем больше объем выборки, тем больше гистограмма похожа на нормальное распределение


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Feb  7 21:17:50 2018

@author: Daniel Yang (daniel.yj.yang@gmail.com)
"""

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

# Normal Distribution
mu = 0 # mean
sigma = 1 # standard deviation
x = np.arange(-5,5,0.1)

y = stats.norm.pdf(x, mu, sigma)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.plot(x, y)
plt.title('Normal: mu=%.1f, sigma=%.1f'%(mu, sigma))
plt.xlabel('x')
plt.ylabel('Probability density')
plt.ylim(-0.05, 0.45)
plt.show()

y = stats.norm.cdf(x, mu, sigma)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.plot(x, y)
plt.title('Normal: mu=%.1f, sigma=%.1f'%(mu, sigma))
plt.xlabel('x')
plt.ylabel('Cumulative distribution')
plt.ylim(-0.1, 1.1)
plt.show()

# Bivariate normal distribution
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
#from mpl_toolkits.mplot3d import Axes3D

#Parameters to set
mu_x = 0
variance_x = 1

mu_y = 0
variance_y = 1

#Create grid and multivariate normal
x = np.linspace(-4,4,500)
y = np.linspace(-4,4,500)
X, Y = np.meshgrid(x,y)
pos = np.empty(X.shape + (2,))
pos[:, :, 0] = X; pos[:, :, 1] = Y
rv = multivariate_normal([mu_x, mu_y], [[variance_x, 0], [0, variance_y]])

#Make a 3D plot
fig = plt.figure(num=None, figsize=(8, 6), dpi=300, facecolor='w', edgecolor='k')
ax = fig.gca(projection='3d')
ax.plot_wireframe(X, Y, rv.pdf(pos),rcount=25, ccount=25)
#ax.plot_surface(X, Y, rv.pdf(pos),cmap='viridis',linewidth=0)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()

#Make a 3D plot
fig = plt.figure(num=None, figsize=(8, 6), dpi=300, facecolor='w', edgecolor='k')
ax = fig.gca(projection='3d')
ax.plot_wireframe(X, Y, rv.cdf(pos),rcount=25, ccount=25)
#ax.plot_surface(X, Y, rv.pdf(pos),cmap='viridis',linewidth=0)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()

# Student's t-distribution
mu = 0 # mean
sigma = 1 # standard deviation
df = 1
x = np.arange(-5,5,0.1)

y = stats.t.pdf(x, df, mu, sigma)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.plot(x, y)
plt.title('Student\' t-distribution: df=%.0f, mu=%.1f, sigma=%.1f'%(df, mu, sigma))
plt.xlabel('x')
plt.ylabel('Probability density')
plt.ylim(-0.05, 0.45)
plt.show()

y = stats.t.cdf(x, df, mu, sigma)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.plot(x, y)
plt.title('Student\' t-distribution: df=%.0f, mu=%.1f, sigma=%.1f'%(df, mu, sigma))
plt.xlabel('x')
plt.ylabel('Cumulative distribution')
plt.ylim(-0.1, 1.1)
plt.show()

# Poisson Distribution
# For discrete outcomes (e.g., the number of customers visiting your lane in a supermarket for check out during 4:30-4:50), with known expected average outcome
# Watch this for the Wal-Mart example (https://www.youtube.com/watch?v=8px7xuk_7OU)
n = np.arange(970, 1030)  # the number of possible occurrences of interest, say the number of visitors that might show up during 10-10:30pm on a Tuesday night
lambda_coef = 1000   # long-run average, say, the expected number of customers in Amazon website between 10:00-10:30pm on a Tuesday night is 1000
y = stats.poisson.pmf(n, lambda_coef)  # Probability Mass Function is the probability density function for discrete outcome
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.plot(n, y, 'o-')
plt.show()
# Question: What is the probability that exactly 7 customers enter your lane between 4:30-4:45?
print("The probability that exactly 7 customers enter my lane between 4:30-4:45 is {0:.2f}%".format(100*stats.poisson.pmf(7, 10)))

# Bernoulli Distribution
k = np.array([0,1]) # 0 = tail, 1 = head
p = 0.7 # probability of the head
y = stats.bernoulli.pmf(k, p)
fig = plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
ax = fig.add_axes([0,0,1,1])
ax.bar(['0','1'], y)
plt.xlabel('k')
plt.ylim(0,1)
plt.show()

y = stats.bernoulli.cdf(k, p)
fig = plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
ax = fig.add_axes([0,0,1,1])
ax.bar(['0','1'], y)
plt.xlabel('k')
plt.ylim(0,1)
plt.show()

# Binomial Distribution
n = 10 # number of coins tossed
k = np.arange(0,n+1) # number of heads
p = 0.5 # probability of the head
y = stats.binom.pmf(k, n, p)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.bar(k,y)
plt.xlabel('k')
plt.plot(k, y, 'o-r')
plt.show()

y = stats.binom.cdf(k, n, p)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.bar(k,y)
plt.xlabel('k')
plt.plot(k, y, 'o-r')
plt.show()

# Multinomial Distribution
n = 20 # 10 # 3 # 2 # number of trials (k-sided dice rolled)
p = [1/6, 1/6, 1/6, 1/6, 1/6, 1/6] # probability of the each side of a 6-sided dice, should sum to 1

rv = stats.multinomial(n, p) # A multinomial random variable

x_samples = rv.rvs(size = 1000000, random_state=12345) # draw random samples.
x_samples = np.unique(x_samples, axis = 0)
# each sample consists of number of outcomes for event-1, event-2, ..., event-k, should num to n
# for example, if n = 9, then x = [2, 1, 2, 1, 2, 1] means the each of the odd-number sides (1,3,5) appearing 2 times and each of the even-number sides (2,4,6) appearing 1 time

x_samples.shape # the # of rows is the distinct possible outcomes of rolling the dice n times

y = rv.pmf(x = x_samples)

# Number of occurrences for each side
# [≥1, ≥1, ≥1, ≥1, ≥1, ≥1]
x_samples_subset = x_samples[np.where(
    (x_samples[:, 0] >= 1) &
    (x_samples[:, 1] >= 1) &
    (x_samples[:, 2] >= 1) &
    (x_samples[:, 3] >= 1) &
    (x_samples[:, 4] >= 1) &
    (x_samples[:, 5] >= 1))]

x_samples_subset.shape

y = rv.pmf(x = x_samples_subset)
sum(y)

# Investment example
n = 100
p = [75/100, 20/100, 5/100]
rv = stats.multinomial(n, p) # A multinomial random variable
x_samples = rv.rvs(size = 10000000, random_state=12345) # draw random samples.
x_samples = np.unique(x_samples, axis = 0)
x_samples.shape # the # of rows is the distinct possible outcomes of rolling the dice n times
# Number of occurrences for each side
# [≥1, ≥1, ≥1, ≥1, ≥1, ≥1]
x_samples_subset = x_samples[np.where(
    (x_samples[:, 0] >= 70) &
    (x_samples[:, 1] <= 25) &
    (x_samples[:, 2] <= 5))]

x_samples_subset.shape

y = rv.pmf(x = x_samples_subset)
sum(y)

# Geometric Distribution
k = np.arange(1,11) # number of heads
p = 0.5 # probability of the head
y = stats.geom.pmf(k, p)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.bar(k,y)
plt.xlabel('k')
plt.plot(k, y, 'o-r')
plt.show()

y = stats.geom.cdf(k, p)
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.bar(k,y)
plt.xlabel('k')
plt.plot(k, y, 'o-r')
plt.show()

# Logistic distribution
# https://docs.scipy.org/doc/numpy/reference/generated/numpy.random.logistic.html
loc, scale = 10, 1
s = np.random.logistic(loc, scale, 100000)
count, bins, ignored = plt.hist(s, bins=50)

def logistic_pdf(x, loc, scale):
    return exp((loc-x)/scale)/(scale*(1+exp((loc-x)/scale))**2)

def logistic_cdf(x, loc, scale):
    return 1/(1+(exp((loc-x)/scale)))

plt.plot(bins, logistic_pdf(bins, loc, scale)*count.max()/logistic_pdf(bins, loc, scale).max())
plt.show()

plt.plot(bins, logistic_cdf(bins, loc, scale)*count.max()/logistic_cdf(bins, loc, scale).max())
plt.show()

# Logistic distribution
loc, scale = 10, 1
x = np.arange(-5,25,0.1)
y = stats.logistic.pdf(x, loc, scale) # pdf
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.plot(x, y)
plt.show()

y = stats.logistic.cdf(x, loc, scale) # cdf
plt.figure(num=None, figsize=(8, 6), dpi=80, facecolor='w', edgecolor='k')
plt.plot(x, y)
plt.show()

# Chi-square distribution
# https://machinelearningmastery.com/statistical-data-distributions/
# plot the chi-squared pdf
from numpy import arange
from matplotlib import pyplot
from scipy.stats import chi2
# define the distribution parameters
sample_space = arange(0, 10, 0.01)
dof = 1 # 3
# calculate the pdf
pdf = chi2.pdf(sample_space, dof)
# plot
pyplot.plot(sample_space, pdf)
pyplot.show()
# calculate the cdf
cdf = chi2.cdf(sample_space, dof)
# plot
pyplot.plot(sample_space, cdf)
pyplot.show()


import pandas as pd
import numpy as np

# pd.set_option('display.max_columns', None)
# pd.set_option('display.max_colwidth', None)

##############################################
# Model

# points = [(np.array([2]),4), (np.array([4]),2)]
# d = 1

# Generate data
iterationCount = 2000
true_w = np.array([1,2,3,4,5,]) # Reverse-engineer to get to this vector
d = len(true_w)
dfColNames = [f"w{s+1}" for s in range(d)]
dfColNames.append('F(w)')
points = []
for i in range(iterationCount):
    x = np.random.randn(d)
    y = true_w.dot(x) + np.random.randn()
    points.append((x,y))

def F(w):
    return sum((w.dot(x) - y)**2 for x, y in points) / len(points)

def dF(w):
    return sum(2*(w.dot(x) - y) * x for x, y in points) / len(points)

##############################################
# Algorithm

def gradientDescent(F, dF, d):
    w = np.zeros(d)
    eta = 0.01

    lst = []
    for t in range(iterationCount):
        l1 = []
        value = F(w)
        gradient = dF(w)
        w = w - eta * gradient
        l1.extend(w)
        l1.append(value)
        lst.append(l1)
    df = pd.DataFrame(lst, columns = dfColNames)
    df['Iteration'] = df.index
    return df

result = gradientDescent(F, dF, d)

# print(result)


#Gauss-Seidel iterations
import numpy as np
import numpy.linalg as la
#
x = 0.0
y = 0.0
ni = 8
#
A = np.array([ [3.0, 2.0],
               [1.0, 4.0] ])
b = np.array([7.0, 9.0])
#
# does not converge for this:
#A = np.array([ [1.0, 2.0],
#               [3.0, 4.0] ])
#b = np.array([5.0, 11.0])
#
D = np.diag(np.diag(A))
L = np.tril(A) - D
U = np.triu(A) - D
A2 = np.matmul(la.inv(L+D), U)
normA2 = la.norm(A2)
print('||A2||=', normA2)
#
print('A=')
print(A)
print('L=')
print(L)
print('U=')
print(U)
print('D=')
print(D)
#
for i in range(ni):
    x = (b[0] - A[0,1]*y)/A[0,0]
    y = (b[1] - A[1,0]*x)/A[1,1]
    print('i=%i, x=%.2f, y=%.2f'%(i, x, y))
#
# this does converge, but slower...
#for i in range(ni):
#    x = (b[0] - A[0,1]*y0)/A[0,0]
#    y = (b[1] - A[1,0]*x0)/A[1,1]
#    print('i=%i, x=%.2f, y=%.2f'%(i, x, y))
#    x0 = x
#    y0 = y
print('done!')


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

from fits import CubicFit, CubicEval, PolynomialFit, PolynomialEval

Fits = [(lambda x : np.exp(-0.5*x*x), r'$\exp{-\frac{1}{2}x^2}$'),
        (np.sin, r'$\sin x$')]

def PlotCubic(x,y, xr, yr):
    plt.plot(x,y, 'ro' , label="points")
    plt.plot(xr, yr, label="fit")
    plt.title('Cubic Spline')
    plt.xlabel("x")
    plt.ylabel("f(x)")
    plt.legend()
    plt.show()

def CubicRun(x, y):
    xr = np.arange(x[0],x[-1]+0.1,0.1)
    c , cn = CubicFit(x,y)
    yr = np.array([CubicEval(x, c, xr[i]) for i in range(len(xr))])
    PlotCubic(x, y, xr, yr)

def CubicTest(FX):
    fX, fStr = FX[0], FX[1]
    x = np.array([i/6 + i/12 for i in range(-12,12)])
    CubicRun(x, fX(x))

def SplineTest(x, y):
    CubicRun(np.array(x), np.array(y))

def PolynomialTest(FX):
    fX, fStr = FX[0], FX[1]

    xp = np.arange(-2,2,0.01)
    yp = fX(xp)

    x = np.arange(-2,2,0.45)
    y = fX(x)
    p, cond = PolynomialFit(x,y)
    yhat = np.array([PolynomialEval(p, xp[i]) for i in range(0,len(xp))])

    plt.plot(xp, yp, label=fStr)
    plt.plot(xp, yhat,label=r'$fit$')
    plt.legend()
    plt.xlabel(r'$x$')
    plt.ylabel(r'$f(x)$')
    plt.title(fStr)
    plt.show()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Various Mathematical Fits')
    parser.add_argument('-f','--fit', choices=['poly', 'cubic', 'spline'])
    parser.add_argument('-i','--ix', type=int, default=0)
    parser.add_argument('-x','--x', nargs='+', default=[-12,-6.1,-3, -0.5, 1.1, 4, 8,12])
    parser.add_argument('-y','--y', nargs='+', default=[3,-14, 10, 0, 16,-2, 12, -2])
    args = parser.parse_args()
    ix = args.ix

    if args.fit == 'poly':
        PolynomialTest(Fits[ix])
    elif args.fit == 'cubic':
        CubicTest(Fits[ix])
    elif args.fit == 'spline':
        x, y  = [float(x) for x in args.x], [float(y) for y in args.y]
        asset len(x) == len(y)
        SplineTest(x, y)


# coding: utf-8

# # Modelado epidemilogico del coronavirus
#
# ### Creditos al libro "Learning Scientific Programming with Python is published by Cambridge University Press (ISBN: 9781107428225)."
#
#
# https://scipython.com/book/chapter-8-scipy/additional-examples/the-sir-epidemic-model/
#

# ### Tambien agradecimientos a : Análisis del Covid-19 por medio de un modelo SEIR
# https://institucional.us.es/blogimus/2020/03/covid-19-analisis-por-medio-de-un-modelo-seir/
#

# The SIR epidemic model
# A simple mathematical description of the spread of a disease in a population is the so-called SIR model, which divides the (fixed) population of N individuals into three "compartments" which may vary as a function of time, t:
#
# S(t) are those susceptible but not yet infected with the disease;
#
# I(t) is the number of infectious individuals;
#
# R(t) are those individuals who have recovered from the disease and now have immunity to it.
#
# The SIR model describes the change in the population of each of these compartments in terms of two parameters, β and γ. β describes the effective contact rate of the disease: an infected individual comes into contact with βN other individuals per unit time (of which the fraction that are susceptible to contracting the disease is S/N). γ is the mean recovery rate: that is, 1/γ is the mean period of time during which an infected individual can pass it on.
#
# The differential equations describing this model were first derived by Kermack and McKendrick [Proc. R. Soc. A, 115, 772 (1927)]:
#
# Variables :
#
# #### β beta Contact rate
#
# #### γ gamma, mean recovery rate,
#
# #### S(t) are those susceptible but not yet infected with the disease;
#
# #### I(t) is the number of infectious individuals;
#
# #### R(t) are those individuals who have recovered and now have immunity to it.
#
# dS/dt=−βSI/N,
#
# dI/dt=βSI/N − γI,
#
# dR/dt=γI.
#
# The following Python code integrates these equations for a disease characterised by parameters β=0.2, 1/γ=10days in a population of N=1000 (perhaps 'flu in a school). The model is started with a single infected individual on day 0: I(0)=1. The plotted curves of S(t), I(t) and R(t) are styled to look a bit nicer than Matplotlib's defaults.

# In[25]:

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

# Total population, N.
N = 47500000
# Initial number of infected and recovered individuals, I0 and R0.
I0, R0 = 1, 0
# Everyone else, S0, is susceptible to infection initially.
S0 = N - I0 - R0
# Contact rate, beta, and mean recovery rate, gamma, (in 1/days).
beta, gamma = 0.2, 1./20
# A grid of time points (in days)
t = np.linspace(0, 160, 160)

# The SIR model differential equations.
def deriv(y, t, N, beta, gamma):
    S, I, R = y
    dSdt = -beta * S * I / N
    dIdt = beta * S * I / N - gamma * I
    dRdt = gamma * I
    return dSdt, dIdt, dRdt

# Initial conditions vector
y0 = S0, I0, R0
# Integrate the SIR equations over the time grid, t.
ret = odeint(deriv, y0, t, args=(N, beta, gamma))
S, I, R = ret.T

# Plot the data on three separate curves for S(t), I(t) and R(t)
fig = plt.figure(facecolor='w')
ax = fig.add_subplot(111,  axisbelow=True)
ax.plot(t, S/1000, 'b', alpha=0.5, lw=2, label='Susceptible')
ax.plot(t, I/1000, 'r', alpha=0.5, lw=2, label='Infected')
ax.plot(t, R/1000, 'g', alpha=0.5, lw=2, label='Recovered with immunity')
ax.set_xlabel('Time /days')
ax.set_ylabel('Number (1000s)')
#ax.set_ylim(0,1.2)
ax.yaxis.set_tick_params(length=0)
ax.xaxis.set_tick_params(length=0)
ax.grid(b=True, which='major', c='w', lw=2, ls='-')
legend = ax.legend()
legend.get_frame().set_alpha(0.5)
for spine in ('top', 'right', 'bottom', 'left'):
    ax.spines[spine].set_visible(False)
plt.show()


import math
import numpy
from bisect import bisect_left

def primes_up_to(upto=1000000):
    primes=numpy.arange(3,upto+1,2)
    isprime=numpy.ones((upto-1)/2,dtype=bool)
    for factor in primes[:int(math.sqrt(upto))]:
        if isprime[(factor-2)/2]: isprime[(factor*3-2)/2::factor]=0
    return numpy.insert(primes[isprime],0,2)

def primesfrom2to(n):
    """ Input n>=6, Returns a array of primes, 2 <= p < n """
    sieve = numpy.ones(n//3 + (n%6==2), dtype=numpy.bool)
    for i in range(1,int(n**0.5)//3+1):
        if sieve[i]:
            k=3*i+1|1
            sieve[       k*k//3     ::2*k] = False
            sieve[k*(k-2*(i&1)+4)//3::2*k] = False
    return numpy.r_[2,3,((3*numpy.nonzero(sieve)[0][1:]+1)|1)]

def primes_starting_at(start=1000000, upto=2000000):
    assert(start < upto)
    p = primesfrom2to(upto)
    return p[bisect_left(p, start):]

if __name__ == "__main__":
    #print(primes_starting_at(1000000, 1100000))
    print(primesfrom2to(2000000))


from math import ceil, sqrt

from scipy.special import binom
import scipy.sparse as sp

def veclen_to_matsize(L):
  n = (sqrt(1+8*L) - 1)/2
  assert n == int(n)
  return int(n)

def matsize_to_veclen(n):
  return n*(n+1)//2

def k_to_ij(k, L):
  '''
  Given a gram matrix Q represented by a vector
    V = [Q_00, ... Q_0n, Q_11, ..., Q_1n, Q22, ..., ] of length L,
    for given k, compute i,j s.t. Q_ij = V[k]
  '''
  if (k >= L):
    raise IndexError("Index out of range")
  # inverse formula for arithmetic series
  n = (sqrt(1+8*L) - 1)/2
  # get first index
  i = int(ceil( (2*n+1)/2 - sqrt( ((2*n+1)/2)**2 -2 * (k+1)  ) ) - 1)
  # second index
  k1 = (2*n+1-i)*i/2 - 1
  j = int(i + k - k1 - 1)
  return i,j

def ij_to_k(i,j,L):
  '''
  Given a symmetric matrix Q represented by a vector
  V = [Q_00, ... Q_0n, Q_11, ..., Q_1n] of length L,
  for given i,j , compute k s.t. Q_ij = V(k)
  '''
  n = (sqrt(1+8*L) - 1)/2
  i_at1 = min(i,j)+1
  j_at1 = max(j,i)+1
  k_at1 = int((n + n-i_at1)*(i_at1-1)/2 + j_at1)
  return k_at1 - 1

def vec_to_mat(vec):
  '''convert vector representation of gram matrix to gram matrix'''
  L = len(vec)
  n = int((sqrt(1+8*L) - 1)/2)
  return [[vec[ij_to_k(i,j,L)] for i in range(n) ] for j in range(n)]

def mat_to_vec(mat):
  '''retrieve vector representation of gram matrix'''
  n = mat.shape[0]
  L = int(n*(n+1)/2)
  ret = [0. for i in range(L)]
  for k in range(L):
    i,j = k_to_ij(k, L)
    ret[k] = (mat[i,j] + mat[j,i]) / 2
  return ret

def multinomial(params):
  if len(params) == 1:
    return 1
  return binom(sum(params), params[-1]) * multinomial(params[:-1])

def double_factorial(n):
  ret = 1
  for i in range(n, 0, -2):
    ret *= i
  return ret

def speye(n, pos=0, tot=None):
  '''
  return a sparse identity matrix [ 0  I  0]
  with size n x tot and where the identity matrix
  starts at pos
  '''
  if tot is None:
    tot = n
  return sp.coo_matrix( ([1.] * n, (range(n), range(pos, pos+n))), (n,tot) )

def spzeros(n, m):
  return sp.coo_matrix( (n,m) )


"""
Checks if a system of forces is in static equilibrium.
"""
from __future__ import annotations

from numpy import array, cos, cross, ndarray, radians, sin

def polar_force(
    magnitude: float, angle: float, radian_mode: bool = False
) -> list[float]:
    """
    Resolves force along rectangular components.
    (force, angle) => (force_x, force_y)
    >>> import math
    >>> force = polar_force(10, 45)
    >>> math.isclose(force[0], 7.071067811865477)
    True
    >>> math.isclose(force[1], 7.0710678118654755)
    True
    >>> polar_force(10, 3.14, radian_mode=True)
    [-9.999987317275396, 0.01592652916486828]
    """
    if radian_mode:
        return [magnitude * cos(angle), magnitude * sin(angle)]
    return [magnitude * cos(radians(angle)), magnitude * sin(radians(angle))]

def in_static_equilibrium(
    forces: ndarray, location: ndarray, eps: float = 10**-1
) -> bool:
    """
    Check if a system is in equilibrium.
    It takes two numpy.array objects.
    forces ==>  [
                        [force1_x, force1_y],
                        [force2_x, force2_y],
                        ....]
    location ==>  [
                        [x1, y1],
                        [x2, y2],
                        ....]
    >>> force = array([[1, 1], [-1, 2]])
    >>> location = array([[1, 0], [10, 0]])
    >>> in_static_equilibrium(force, location)
    False
    """
    # summation of moments is zero
    moments: ndarray = cross(location, forces)
    sum_moments: float = sum(moments)
    return abs(sum_moments) < eps

if __name__ == "__main__":
    # Test to check if it works
    forces = array(
        [
            polar_force(718.4, 180 - 30),
            polar_force(879.54, 45),
            polar_force(100, -90),
        ]
    )

    location = array([[0, 0], [0, 0], [0, 0]])

    assert in_static_equilibrium(forces, location)

    # Problem 1 in image_data/2D_problems.jpg
    forces = array(
        [
            polar_force(30 * 9.81, 15),
            polar_force(215, 180 - 45),
            polar_force(264, 90 - 30),
        ]
    )

    location = array([[0, 0], [0, 0], [0, 0]])

    assert in_static_equilibrium(forces, location)

    # Problem in image_data/2D_problems_1.jpg
    forces = array([[0, -2000], [0, -1200], [0, 15600], [0, -12400]])

    location = array([[0, 0], [6, 0], [10, 0], [12, 0]])

    assert in_static_equilibrium(forces, location)

    import doctest

    doctest.testmod()


import numpy as np

arr = np.arange(1, 11)
print(arr)

# 1.
print(np.log2(arr))

# 2.
print(np.log10(arr))

# 3.
print(np.log(arr))

# 4.
from math import log
log = np.frompyfunc(log, 2, 1)

print(log(arr, 5))


import numpy as np
import matplotlib.pyplot as plt
from scipy import linalg as la

def PCA(dat, center=False, percentage=0.8):
    M, N = dat.shape
    if center:
        mu = np.mean(dat,0)
        dat -= mu

    U, L, Vh = la.svd(dat, full_matrices=False)

    V = Vh.T.conjugate()
    SIGMA = np.diag(L)
    X = U.dot(SIGMA)
    Lam = L**2

    normalized_eigenvalues = Lam/Lam.sum(dtype=float)
    csum = [normalized_eigenvalues[:i+1].sum() for i in xrange(N)]
    n_components = [x < percentage for x in csum].index(False) + 1

    return (normalized_eigenvalues,
            V[:,0:n_components],
            SIGMA[0:n_components,0:n_components],
            X[:,0:n_components])

def scree(normalized_eigenvalues):
    fig = plt.figure()
    plt.plot(normalized_eigenvalues,'b-', normalized_eigenvalues, 'bo')
    plt.xlabel("Principal Components")
    plt.ylabel("Percentage of Variance")
    return fig


import numpy as np
'''
ベクトル[の|][演算|計算]を[する|行う]
行列[の|][演算|計算]を[する|行う]
numpyを[使う|入れる|インポートする]
'''

np.array(l)
'''
@type(l,リスト)から配列を[作る|作成する|作成]
'''
np.array(t)
'''
@type(t,タプル)から配列を作る
'''

a.shape()
'''
@type(a,配列)の[形状|形]を調べる
'''

a.dtype()
'''
@type(a,配列)の[データ型|型]を調べる
'''

np.ndim(x)
'''
@type(x,配列)の[次元数|次元の数]を調べる
'''

np.arange(10)
'''
0から9までの配列を作る
'''

x.reshape(3,3)
'''
@type(x,配列)を3×3の多次元配列に変形する
'''

np.concatenate([a,b])
'''
@type(a,配列)と@type(b,配列)を[列方向|縦方向]に連結する
'''

np.concatenate([a,b], axis=0)
'''
@type(a,配列)と@type(b,配列)を[列方向|縦方向]に連結する
'''

np.concatenate([a,b], axis=1)
'''
@type(a,配列)と@type(b,配列)を[行方向|横方向]に連結する
'''

np.sum(x)
'''
@type(x,配列)の[合計値|合計]を調べる
'''

np.sum(x, axis=0)
'''
@type(x,配列)の列ごとの[合計値|合計]を調べる
'''

np.sum(x, axis=1)
'''
@type(x,配列)の行ごとの[合計値|合計]を調べる
'''

np.mean(x)
'''
@type(x,配列)の[平均値|平均]を調べる
'''

np.mean(x, axis=0)
'''
@type(x,配列)の列ごとの[平均値|平均]を調べる
'''

np.mean(x, axis=1)
'''
@type(x,配列)の行ごとの[平均値|平均]を調べる
'''

np.min(x)
'''
@type(x,配列)の[最小値|最小]を調べる
'''

np.min(x, axis=0)
'''
@type(x,配列)の列ごとの[最小値|最小]を調べる
'''

np.min(x, axis=1)
'''
@type(x,配列)の行ごとの[最小値|最小]を調べる
'''

np.max(x)
'''
@type(x,配列)の[最大値|最大]を調べる
'''

np.max(x, axis=0)
'''
@type(x,配列)の列ごとの[最大値|最大]を調べる
'''

np.max(x, axis=1)
'''
@type(x,配列)の行ごとの[最大値|最大]を調べる
'''

np.std(x)
'''
@type(x,配列)の標準偏差を調べる
'''

np.std(x, axis=0)
'''
@type(x,配列)の列ごとの標準偏差を調べる
'''

np.std(x, axis=1)
'''
@type(x,配列)の行ごとの標準偏差を調べる
'''

np.var(x)
'''
@type(x,配列)の分散を調べる
'''

np.var(x, axis=0)
'''
@type(x,配列)の列ごとの分散を調べる
'''

np.var(x, axis=1)
'''
@type(x,配列)の行ごとの分散を調べる
'''

np.eye(3)
'''
3×3の単位行列を作る
'''

np.identity(3)
'''
3×3の単位行列を作る
'''

np.empty(5)
'''
要素数5の[空配列|空の配列]を作る
'''

np.empty((2, 3))
'''
2×3の[空配列|空の配列]を作る
'''

np.empty_like(x)
'''
@type(x,配列)と同じ大きさの[空配列|空の配列]を作る
'''

np.gcd(a,b)
'''
@type(a,配列)と@type(b,配列)の要素ごとの最大公約数を調べる
'''

np.lcm(a,b)
'''
@type(a,配列)と@type(b,配列)の要素ごとの最小公倍数を調べる
'''

np.unique(x)
'''
@type(x,配列)から重複を除いた配列を作る
@type(x,配列)のユニークな要素を調べる
'''

u, counts = np.unique(a, return_counts=True)
'''
@type(x,配列)のユニークな要素とその個数を調べる
'''

u, indices = np.unique(x, return_index=True)
'''
@type(x,配列)のユニークな要素とその位置を調べる
'''

np.cumsum(x)
'''
@type(x,配列)の累積和を調べる
'''

np.cumprod(x)
'''
@type(x,配列)の累積積を調べる
'''

x.flatten()
'''
@type(x,配列)を[一次元にする|一次元化|平坦化]
'''


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

import numpy as np

def up(volatility, duration) :
    u = np.exp(volatility * np.sqrt(duration))
    return u

def down(volatility, duration) :
    d = np.exp(-volatility * np.sqrt(duration))
    return d

def probability_u(volatility, duration, riskfree) :
    u = up(volatility, duration)
    d = down(volatility, duration)
    probability_u = (np.exp(riskfree * duration) - d) / (u - d)
    return probability_u

def probability_d(volatility, duration, riskfree) :
    u = up(volatility, duration)
    d = down(volatility, duration)
    probability_d = (np.exp(-riskfree * duration) - d) / (u - d)
    return probability_d

def price(option, spot, riskfree, dividend, volatility, steps=4) :
    dt = option.days_to_expiry() / (365 * steps)

    u = up(volatility, dt)
    d = down(volatility, dt)

    p = probability_u(volatility, dt, riskfree)
    q = probability_d(volatility, dt, riskfree)

    ul_price = np.zeros([steps + 1, steps + 1])
    ul_price[0, 0] = spot
    for i in range(1, steps + 1) :
        ul_price[i, 0] = ul_price[i - 1, 0] * u
        for j in range(1, i + 1) :
            ul_price[i, j] = ul_price[i - 1, j - 1] * d

    option_price = np.zeros([steps + 1, steps + 1])
    for j in range(steps + 1) :
        if option.type == "call" :
            option_price[steps, j] = max(0, ul_price[steps, j] - option.strike)
        elif option.type == "put" :
            option_price[steps, j] = max(0, option.strike - ul_price[steps, j])

    for i in range(steps)[::-1] :
        for j in range(i + 1) :
            if option.style == "EU" :
                option_price[i, j] = np.exp(-riskfree * dt) * (p * option_price[i + 1, j] + q * option_price[i + 1, j + 1])
            elif option.style == "US" :
                if option.type == "call" :
                    option_price[i, j] = max(ul_price[i, j] - option.strike, np.exp(-riskfree * dt) * (p * option_price[i + 1, j] + q * option_price[i + 1, j + 1]))
                elif option.type == "put" :
                    option_price[i, j] = max(option.strike - ul_price[i, j], np.exp(-riskfree * dt) * (p * option_price[i + 1, j] + q * option_price[i + 1, j + 1]))

    return option_price[0, 0]

def main() :
    pass

if __name__ == "__main__" :
    main()


'''
Starting in the top left corner of a 2×2 grid, and only being able to move to
the right and down, there are exactly 6 routes to the bottom right corner.

How many such routes are there through a 20×20 grid?
'''

import numpy as np
from itertools import product

# brute force
def lattice_paths_brute_force(n):

    paths = [p for p in product([(0,1),(1,0)], repeat=2*n) if any(np.sum(p, axis=0)==[n,n])]

    return len(paths)

lattice_paths_brute_force(3)

from math import factorial

def lattice_paths(n):
    # this can be thought of like pascal's triangle.
    # trying to get middle number of row 2n

    return factorial(n*2) / (factorial(n*2 - n) * factorial(n))

lattice_paths(20)


# IMPORTS
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import matplotlib.style as style
from IPython.core.display import HTML

# PLOTTING CONFIG
%matplotlib inline
style.use('fivethirtyeight')
plt.rcParams["figure.figsize"] = (14, 7)
HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: center;
}
</style>
""")
plt.figure(dpi=100)

# PDF MU = 0
plt.plot(np.linspace(-4, 4, 100),
         stats.norm.pdf(np.linspace(-4, 4, 100)),
        )
plt.fill_between(np.linspace(-4, 4, 100),
                 stats.norm.pdf(np.linspace(-4, 4, 100)),
                 alpha=.15,
                )

# PDF MU = 2
plt.plot(np.linspace(-4, 4, 100),
         stats.norm.pdf(np.linspace(-4, 4, 100), loc=2),
        )
plt.fill_between(np.linspace(-4, 4, 100),
                 stats.norm.pdf(np.linspace(-4, 4, 100),loc=2),
                 alpha=.15,
                )

# PDF MU = -2
plt.plot(np.linspace(-4, 4, 100),
         stats.norm.pdf(np.linspace(-4, 4, 100), loc=-2),
        )
plt.fill_between(np.linspace(-4, 4, 100),
                 stats.norm.pdf(np.linspace(-4, 4, 100),loc=-2),
                 alpha=.15,
                )

# LEGEND
plt.text(x=-1, y=.35, s="$ \mu = 0$", rotation=65, alpha=.75, weight="bold", color="#008fd5")
plt.text(x=1, y=.35, s="$ \mu = 2$", rotation=65, alpha=.75, weight="bold", color="#fc4f30")
plt.text(x=-3, y=.35, s="$ \mu = -2$", rotation=65, alpha=.75, weight="bold", color="#e5ae38")

# TICKS
plt.tick_params(axis = 'both', which = 'major', labelsize = 18)
plt.axhline(y = 0, color = 'black', linewidth = 1.3, alpha = .7)

# TITLE, SUBTITLE & FOOTER
plt.text(x = -5, y = 0.51, s = "Normal Distribution - $ \mu $",
               fontsize = 26, weight = 'bold', alpha = .75)
plt.text(x = -5, y = 0.45,
         s = 'Depicted below are three normally distributed random variables with varying $ \mu $. As one can easily\nsee the parameter $\mu$ shifts the distribution along the x-axis.',
         fontsize = 19, alpha = .85)
plt.text(x = -5,y = -0.075,
         s = '   ©Joshua Görner                                                                                github.com/jgoerner   ',
         fontsize = 14, color = '#f0f0f0', backgroundcolor = 'grey');


#!/usr/bin/python3
import numpy as np

def log(x_value):
  x = x_value-1
  x2 = x*x
  a0 = -np.log(2); a1 = 2;a2 = -1;a3 = 2/3.;a4 = -0.5;
  T0 = 1; T1 = x; T2 = 2*x2 - 1; T3 = 4*x2*x - 3*x; T4 = 8*x2*x2 - 8*x2 + 1;
  return a0*T0 + a1*T1 + a2*T2 + a3*T3 + a4*T4

def log2(x_value):
  if x_value < 2:
    t = x_value-1;
    t2 = t*t
    t4 = t2*t2
    t6 = t4*t2
    return  t - 0.5*t2 + 1./3*t2*t - 0.25*t4 + 0.2*t4*t - 1./6*t6
  elif x_value < 6:
    t = x_value-3
    t2 = t*t
    t4 = t2*t2
    t6 = t4*t2
    return  np.log(3) + 1./3*t - 1./18*t2 + 1./81*t2*t - 1/324.*t4 + 1./1215*t4*t - 1./4374*t6
  else:
    t = x_value-9
    t2 = t*t
    t4 = t2*t2
    t6 = t4*t2
    return  np.log(9) + 1./9*t - 1./162*t2 + 1./2187*t2*t - 1/26244.*t4 + 1./295245*t4*t - 1./3188646*t6

import matplotlib.pyplot as plt
fig = plt.figure()
x_list = np.linspace(0.2, 20, 200)

plt.plot(x_list, np.log(x_list), label="exact")
#plt.plot(x_list, [log(x) for x in x_list], label="Chebyshev")
plt.plot(x_list, [log2(x) for x in x_list], label="Taylor")
plt.legend()

# define global plotting parameters
plt.rcParams.update({'font.size': 16})
plt.rcParams['lines.linewidth'] = 3

fig = plt.figure()
plt.plot(x_list, [(np.log(x)-log2(x))/np.log(x) for x in x_list])
plt.grid()
plt.savefig("apxlog.pdf")
plt.show()

max_rel_error = 0
location = 0
for x in np.linspace(0.2,20,200):
  rel_error =  (np.log(x)-log2(x))/np.log(x)
  print("{}, {}, {}, error: {}".format(x, np.log(x), log(x), rel_error))
  old_rel_error = max_rel_error
  max_rel_error = max(max_rel_error, rel_error)
  if old_rel_error != max_rel_error:
    location = x

print("maximum relative error: {} at {}".format(max_rel_error, location))

plt.show()


from abc import ABC,  abstractmethod
from math import sqrt, exp
import numpy as np
def unison_shuffled_copies(a, b, random = None):
	if random is None:
		random = np.random
	assert len(a) == len(b)
	p = random.permutation(len(a))
	return a[p], b[p]

class BaseActivation(ABC):
	"""Base class for all activation functions"""
	@property
	@abstractmethod
	def name(self):
		raise NotImplementedError

	@abstractmethod
	def function(self, x):
		pass

	@abstractmethod
	def derivative(self, x):
		pass

class Identity(BaseActivation):
	""" Identity activation for testing"""
	name = "identity"

	def function(self, x):
		return x

	def derivative(self, x):
		return 1

class Sigmoid(BaseActivation):
	""" Identity activation for testing"""
	name = "sigmoid"

	def function(self, x):
		return 1 / (1 + exp(-x))

	def derivative(self, x):
		return self.function(x)*(1-self.function(x))

def softmax(X, theta = 1.0, axis = None):
	"""
	Compute the softmax of each element along an axis of X.

	Parameters
	----------
	X: ND-Array. Probably should be floats.
	theta (optional): float parameter, used as a multiplier
		prior to exponentiation. Default = 1.0
	axis (optional): axis to compute values along. Default is the
		first non-singleton axis.

	Returns an array the same size as X. The result will sum to 1
	along the specified axis.
	"""

	# make X at least 2d
	y = np.atleast_2d(X)

	# find axis
	if axis is None:
		axis = next(j[0] for j in enumerate(y.shape) if j[1] > 1)

	# multiply y against the theta parameter,
	y = y * float(theta)

	# subtract the max for numerical stability
	y = y - np.expand_dims(np.max(y, axis = axis), axis)

	# exponentiate y
	y = np.exp(y)

	# take the sum along the specified axis
	ax_sum = np.expand_dims(np.sum(y, axis = axis), axis)

	# finally: divide elementwise
	p = y / ax_sum

	# flatten if X was 1D
	if len(X.shape) == 1: p = p.flatten()

	return p


# Do not import any additional 3rd party external libraries as they will not
# be available to AutoLab and are not needed (or allowed)

import numpy as np
import os

class Activation(object):
    """
    Interface for activation functions (non-linearities).

    In all implementations, the state attribute must contain the result,
    i.e. the output of forward (it will be tested).
    """

    # No additional work is needed for this class, as it acts like an
    # abstract base class for the others

    # Note that these activation functions are scalar operations. I.e, they
    # shouldn't change the shape of the input.

    def __init__(self):
        self.state = None

    def __call__(self, x):
        return self.forward(x)

    def forward(self, x):
        raise NotImplemented

    def derivative(self):
        raise NotImplemented

class Identity(Activation):
    """
    Identity function (already implemented).
    """

    # This class is a gimme as it is already implemented for you as an example

    def __init__(self):
        super(Identity, self).__init__()

    def forward(self, x):
        self.state = x
        return x

    def derivative(self):
        return 1.0

class Sigmoid(Activation):
    """
    Sigmoid non-linearity
    """

    # Remember do not change the function signatures as those are needed
    # to stay the same for AutoLab.

    def __init__(self):
        super(Sigmoid, self).__init__()

    def forward(self, x):
        # DONE:
        # Might we need to store something before returning?
        # self.state = ???
        # Hint: You can use np.exp() function
        # return self.state
        self.state = x
        self.state[x>=0] = 1.0 / (1.0 + np.exp(- self.state[x>=0]))
        self.state[x<0] = np.exp(self.state[x<0]) / ( np.exp(self.state[x<0]) + 1.0)
        return self.state

    def derivative(self):
        # DONE:
        # Maybe something we need later in here...
        # return ???
        # Maybe something we need later in here...
        return self.state * (1.0 - self.state)

class Tanh(Activation):
    """
    Tanh non-linearity
    """
    def __init__(self):
        super(Tanh, self).__init__()

    def forward(self, x):
        # DONE:
        # self.state = ???
        # Hint: You can use np.exp() function
        # return self.state
        self.state = (1 - np.exp(-2 * x)) / (1 + np.exp(-2 * x))
        return self.state

    def derivative(self):
        # DONE:
        # return ???
        return (1 - self.state**2)

class ReLU(Activation):
    """
    ReLU non-linearity
    """
    def __init__(self):
        super(ReLU, self).__init__()

    def forward(self, x):
        # DONE:
        # self.state = ???
        # return self.state
        self.state = np.maximum(x, 0)
        return self.state

    def derivative(self):
        # DONE
        # return ???
        tmp = self.state
        tmp[tmp > 0] = 1
        return tmp


#Purpose: To perform the fourier transform
#import necessary packages
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image, ImageFilter, ImageEnhance

#Function 1: Performs the fourier transform with steps
#to get Magnitude_Spectrum
#Steps: fourier transform, fourier shift, gets the magnitude spectrum
#Inputs: image
#Outputs: magntidue spectrum array
def Magnitude_Spectrum(image):
    """Peforms a fourier transform and returns the m_spec"""
    fshift = fourier_fshift(image)
    m_spec= np.log(np.abs(fshift))
    return m_spec

#Function 2: Performs the fourier transform to get fshift
#Steps: fourier transform, fourier shift, gets the magnitude Spectrum
#Inputs: image
#Outputs: fshift
def fourier_fshift(image):
    """peforms the fourier transform and returns the fshift"""
    f = np.fft.fft2(image)
    fshift= np.fft.fftshift(f)
    return fshift

#Function 3: Plotting the magnitude spectrum made my fourier transform
#Steps: plots the Magnitude Spectrum
#Input: the magnitude spectrum Output
#Output: image
# def Plot_M_Spec(m_spec):
#     plt.show(m_spec)
#     plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
#     plt.show()
#     return

#Function 4: Performs an inverse fourier transform
#Steps: unshifts the image, inverse fourier, normalizes the image
#Inputs: f_shift
#Outputs: image array of inverse fouriered image
def inverse_fourier(f_shift):
    image_revert = np.fft.ifftshift(f_shift)
    image = np.fft.ifft2(image_revert)
    image = np.abs(image)
    return image


import numpy as np
import scipy as sp

import matplotlib.pyplot as plt

from scipy.stats import beta as beta_dist
beta_pdf = beta_dist.pdf

prior_alpha = 25
prior_beta = 75

args = (prior_alpha, prior_beta)
prior_over_33, err = sp.integrate.quad(beta_pdf, 0.33, 1, args=args)
print("Prior probability", prior_over_33)
# 0.037830787030165056

observed_successes = 122
observed_failures = 257

posterior_alpha = prior_alpha + observed_successes
posterior_beta = prior_beta + observed_failures

args = (posterior_alpha, posterior_beta)
posterior_over_33, err2 = sp.integrate.quad(beta_pdf, 0.33, 1, args=args)
print("Posterior probability", posterior_over_33)
# 0.13686193416281017

p = np.linspace(0, 1, 500)
prior_dist = beta_pdf(p, prior_alpha, prior_beta)
posterior_dist = beta_pdf(p, posterior_alpha, posterior_beta)

fig, ax = plt.subplots()
ax.plot(p, prior_dist, "k--", label="Prior")
ax.plot(p, posterior_dist, "k", label="Posterior")
ax.legend()
ax.set_xlabel("Success rate")
ax.set_ylabel("Density")
ax.set_title("Prior and posterior distributions for success rate")

plt.show()


"""
Implements inequality and segregation measures such as Gini, Lorenz Curve

"""

import numpy as np
from numba import njit, prange

@njit
def lorenz_curve(y):
    """
    Calculates the Lorenz Curve, a graphical representation of the distribution of income
    or wealth.

    It returns the cumulative share of people (x-axis) and the cumulative share of income earned

    Parameters
    ----------
    y : array_like(float or int, ndim=1)
        Array of income/wealth for each individual. Unordered or ordered is fine.

    Returns
    -------
    cum_people : array_like(float, ndim=1)
        Cumulative share of people for each person index (i/n)
    cum_income : array_like(float, ndim=1)
        Cumulative share of income for each person index

    References
    ----------
    .. [1] https://en.wikipedia.org/wiki/Lorenz_curve

    Examples
    --------
    >>> a_val, n = 3, 10_000
    >>> y = np.random.pareto(a_val, size=n)
    >>> f_vals, l_vals = lorenz(y)

    """

    n = len(y)
    y = np.sort(y)
    s = np.zeros(n + 1)
    s[1:] = np.cumsum(y)
    cum_people = np.zeros(n + 1)
    cum_income = np.zeros(n + 1)
    for i in range(1, n + 1):
        cum_people[i] = i / n
        cum_income[i] = s[i] / s[n]
    return cum_people, cum_income

@njit(parallel=True)
def gini_coefficient(y):
    r"""
    Implements the Gini inequality index

    Parameters
    -----------
    y : array_like(float)
        Array of income/wealth for each individual. Ordered or unordered is fine

    Returns
    -------
    Gini index: float
        The gini index describing the inequality of the array of income/wealth

    References
    ----------

    https://en.wikipedia.org/wiki/Gini_coefficient
    """
    n = len(y)
    i_sum = np.zeros(n)
    for i in prange(n):
        for j in range(n):
            i_sum[i] += abs(y[i] - y[j])
    return np.sum(i_sum) / (2 * n * np.sum(y))

def shorrocks_index(A):
    r"""
    Implements Shorrocks mobility index

    Parameters
    -----------
    A : array_like(float)
        Square matrix with transition probabilities (mobility matrix) of
        dimension m

    Returns
    --------
    Shorrocks index: float
        The Shorrocks mobility index calculated as

        .. math::

            s(A) = \frac{m - \sum_j a_{jj} }{m - 1} \in (0, 1)

        An index equal to 0 indicates complete immobility.

    References
    -----------
    .. [1] Wealth distribution and social mobility in the US: A quantitative approach
       (Benhabib, Bisin, Luo, 2017).
       https://www.econ.nyu.edu/user/bisina/RevisionAugust.pdf
    """

    A = np.asarray(A)  # Convert to array if not already
    m, n = A.shape

    if m != n:
        raise ValueError('A must be a square matrix')

    diag_sum = np.diag(A).sum()

    return (m - diag_sum) / (m - 1)


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

def init_board(N):
	random.seed(100)
	X = np.array([(random.uniform(-1, 1), random.uniform(-1, 1)) for i in range(N)])
	random.seed(None)
	return X

def cluster_points(X, mu):
    clusters  = {}
    for x in X:
        bestmukey = min([(i[0], np.linalg.norm(x-mu[i[0]])) \
                    for i in enumerate(mu)], key=lambda t:t[1])[0]
        try:
            clusters[bestmukey].append(x)
        except KeyError:
            clusters[bestmukey] = [x]
    return clusters

def reevaluate_centers(mu, clusters):
    newmu = []
    keys = sorted(clusters.keys())
    for k in keys:
        newmu.append(np.mean(clusters[k], axis = 0))
    return newmu

def has_converged(mu, oldmu):
    return (set([tuple(a) for a in mu]) == set([tuple(a) for a in oldmu]))

def find_centers(X, K):
    # Initialize to K random centers
    random.seed(None)
    oldmu = random.sample(X, K)
    mu = random.sample(X, K)
    while not has_converged(mu, oldmu):
        oldmu = mu
        # Assign all points in X to clusters
        clusters = cluster_points(X, mu)
        # Reevaluate centers
        mu = reevaluate_centers(oldmu, clusters)
    return(mu, clusters)

def change_coords(array):
    return list(map(list, zip(*array)))

def parse_output(data):
    clusters = data[1]
    points1 = change_coords(clusters[0])
    plt.plot(points1[0], points1[1], 'ro')
    points2 = change_coords(clusters[1])
    plt.plot(points2[0], points2[1], 'g^')
    points3 = change_coords(clusters[2])
    plt.plot(points3[0], points3[1], 'ys')
    centroids = change_coords(data[0])
    plt.plot(centroids[0], centroids[1], 'kx')
    plt.axis([-1.0, 1, -1.0, 1])
    plt.show()

data = init_board(15)
# data = np.array([[0.560, 0.256], [-0.560, 0.206], [0.012, -0.789], [0.011, 0.785], [0.126, 0.692],
# 				[-0.211, 0.785], [-0.011, 0.585], [-0.911, -0.785], [-0.811, -0.678], [-0.7511, -0.9],
# 				[-0.891, -0.555], [0.896, -0.675], [0.786, -0.775], [0.681, -0.599], [0.596, -0.888]])
print(data)
print(type(data))
out = find_centers(list(data), 3)
parse_output(out)


# demonstrasi darii implementasi fungsi softmax

# Ini adalah fungsi yang mengambil vektor dari K
# bilangan real sebagai input, dan menormalkan
# ke dalam distribusi probabilitas yang terdiri
# dari K probabilitas proporsional
# ke eksponensial dari angka input.
# Setelah softmax, elemen dari
# vektor selalu berjumlah 1.

# referensi
# https://en.wikipedia.org/wiki/Softmax_function

import numpy as np

def softmax(vector):
    """
    Menerapkan fungsi softmax
    Parameter:
        vector (np.array,list,tuple): Array berbentuk numpy (1,n)
        terdiri dari nilai-nilai nyata atau daftar serupa,tuple
    Pengembalian:
        softmax_vec (np.array):
        Input array numpy setelah diterapkan
        softmax.
    Vektor softmax menambahkan hingga satu.
    Kita perlu membatasi untuk
    presisi

    >>> vec = np.array([5, 5])
    >>> softmax(vec)
    array([0.5, 0.5])

    >>> softmax([0])
    array([1.])
    """
    exponent_vector = np.exp(vector)

    # Jumlahkan semua eksponensialnya
    sum_exponent = np.sum(exponent_vector)

    # bagi setiap eksponen dengan jumlah
    # semua eksponen
    softmax_vector = exponent_vector / sum_exponent

    return softmax_vector

if __name__ == "__main__":
    import doctest

    doctest.testmod(verbose=True)


import numpy as np
from scipy import linalg as sla
a=np.array([[16,-1,1,2],
            [2,12,1,-1],
            [1,3,-24,2],
            [4,-2,1,20]],dtype='f')
#initial value s.t. np.linang.norm(x) is 1
x=np.array([0.5,0.5,0.5,0.5]).T

epsilon=0.0001
maxiteration=1000

for iteration in range(maxiteration):
    x_new =a@x
    eigen=x_new.dot(x)
    if(np.linalg.norm(x_new-eigen*x)<epsilon):
        break
    #normalize
    x_new/=np.linalg.norm(x_new)
    #update x
    x=x_new
else:
    print("fail to calc eigen value")
    exit(1)

print("num of iteration is %d"%iteration)
print("one of the eigen value of matrix a is %f"%eigen)
print("its eigen vector is \n{}".format(x))
print("compare SciPy result...")
print(sla.eig(a))


#! /usr/bin/env python
#
#  Various series for circumference derived by the kerala school
#
import numpy as np
from fractions import Fraction

# The vyAse vAridhinihate series
def vyase(v, n=10, samskara=3, calc_type="fraction"):
    '''
    Calculates the paridhi (circumference) from the vyAsa (diameter)

    Uses the famous Madhava series

    व्यासे वारिधिनिहते रूपहृते व्याससागराभिहते ।
    त्रिशरादिविषमसंख्याभक्तमृणं स्वं पृथक्क्रमात् कुर्यात् ॥

    Inputs:
                   v: diameter
                   n: number of terms
      samskara: None (default) or
                      k for kth order end-correction
                      where k in (0, 1, 2, 3)
           calc_type: "fraction" (default): uses fractions
                      "float": uses numpy float128
    Returns:
                   p: circumference
    '''
    assert ((calc_type == "fraction") or (calc_type == "float")), \
        f"Unknown calc_type {calc_type}: pass 'float' or 'fraction'"
    if calc_type == "fraction":
        t = Fraction(4*v, 1) # First term
    else:
        t = np.float128(4*v)  # First term
    # Initialize the series to first term
    s = t
    sg = -1   # Sign of next term
    # C = 4v - 4v/3 + 4v/5 - 4v/7 ...
    for i in range(2, n+1):   # Ends after the nth term
        s += sg*t/(2*i - 1)    # 4v/(2n-1) is the last term
        sg *= -1
    # Add end term
    # Last divisor
    if samskara == 0:
        # Zeroth order correction
        # a_p = 4v/2p, where 4v/p is the last term
        p = 2*n - 1
        s += sg*t/(2*p)
    elif samskara == 1:
        # First order
        # a_p = 4v/(2p+2) = 4v/4n
        s += sg*t/(4*n)
    elif samskara == 2:
        # Second Order
        # a_p = 4v/((2p+2) + 4/(2p+2))
        #     = 4v.n/(4n^2 + 1)
        s += sg*t*n/(4*n**2 + 1)
    elif samskara == 3:
        # Third Order
        # a_p = 4v/((2p+2) + 4/((2p+2) + 16/(2p+2)))
        #     = 4v.(n^2 + 1)/(4n^3+5n)
        s += sg*t*(n**2 + 1)/(4*n**3 + 5*n)
    return s

# The vyAsavargAdravihatAt series
def vyasavargad(v, n=10, calc_type="fraction"):
    '''
    Calculates the paridhi (circumference) from the vyAsa (diameter)

    Uses the vyAsavargAdravihatAt series
    vyāsavargād ravihatāt padam syāt prathamam phalam |
    tadāditastrisamkhyāptam phalam syāduttarottaram ||
    rupādyayugmasamkhyābhirhr.tes.ves.u yathākramam |
    vis.amānāmyutestyaktvā samam hi paridhirbhavet ||

    Inputs:
                   v: diameter
                   n: number of terms
           calc_type: "fraction" (default): uses fractions
                      "float": uses numpy float128
    Returns:
                   p: circumference
    '''
    assert ((calc_type == "fraction") or (calc_type == "float")), \
        f"Unknown calc_type {calc_type}: pass 'float' or 'fraction'"
    if calc_type == "fraction":
        # Fixme implement Aryabhata square root algo
        t = Fraction(np.sqrt(12 * v**2)) # First term
    else:
        t = np.sqrt(np.float128(12 * v**2))  # First term
    p = t
    s = -1   # Sign of next term
    # C = sqrt(12d^2) (1 - 1/3.3 + 1/(3^2.5)+1/(3^3.7) ...)
    for i in range(1,n):
        if calc_type == "fraction":
            p += Fraction(s*t, (3**i*(2*i+1)))
        else:
            p += s*t/np.float64(3**i*(2*i+1))

        s = s*-1
    return p

def samapanchahatayoh(v, n=10, calc_type="fraction"):
    '''
    Calculates the paridhi (circumference) from the vyAsa (diameter) using
the samapanchahatayoH series

    समपञ्चाहतयो या रुपाद्ययुजां चतुर्घ्नमूलयुताः
    ताभिः षोडशगुणितात् पृथगाहृतेषु विषमयुतेः
    समफलयुतिमपहाय स्यादिष्टव्याससंभवः परिधिः

    Inputs:
                   v: diameter
                   n: number of terms
           calc_type: "fraction" (default): uses fractions
                      "float": uses numpy float128
    Returns:
                   p: circumference
    '''
    assert ((calc_type == "fraction") or (calc_type == "float")), \
        f"Unknown calc_type {calc_type}: pass 'float' or 'fraction'"
    sg = 1   # Sign of next term
    t = 16*v
    s = 0
    # C = 16d(1/1^5+4.1 - 1/3^5+4*3 + 1/5^5+4*5 ...)
    for i in range(0,n):
        o = 2*i+1
        if calc_type == "fraction":
            s += Fraction(sg*t, (o**5+4*o))
        else:
            s += sg*t/np.float64(o**5+4*o)

        sg = sg*-1
    return s

def vyasad(v, n=10, calc_type="fraction"):
    '''
    Calculates the paridhi (circumference) from the vyAsa (diameter) using
the vyAsAd vAridhinihatAt series

    व्यासाद् वारिधिनिहतात् पृथगाप्तं त्र्याद्ययुग्विमूलघनैः
    त्रिघ्नव्यासे स्वमृणं क्रमशः कृत्वा परिधिरानेयः ||
    C = 3D + 4D(1 /(3^3-3) - 1/(5^3-5) + 1/(7^3-7) ….)

    Inputs:
                   v: diameter
                   n: number of terms
           calc_type: "fraction" (default): uses fractions
                      "float": uses numpy float128
    Returns:
                   p: circumference
    '''
    assert ((calc_type == "fraction") or (calc_type == "float")), \
        f"Unknown calc_type {calc_type}: pass 'float' or 'fraction'"
    sg = 1   # Sign of next term
    t = 4*v
    s = 3*v
    # C = 3D + 4D(1 /(3^3-3) - 1/(5^3-5) + 1/(7^3-7) ….)
    for i in range(1,n):
        o = 2*i+1
        if calc_type == "fraction":
            s += Fraction(sg*t, (o**3-o))
        else:
            s += sg*t/np.float64(o**3-o)
        sg = sg*-1
    return s

__all__ = ["vyase", "vyasavargad", "samapanchahatayoh", "vyasad"]


'''

Find the k post offices located closest to you, given your location and a list of locations of all post offices available.
Locations are given in 2D coordinates in [X, Y], where X and Y are integers.
Euclidean distance is applied to find the distance between you and a post office.
Assume your location is [m, n] and the location of a post office is [p, q], the Euclidean distance between the office and
you is SquareRoot((m - p) * (m - p) + (n - q) * (n - q)).
K is a positive integer much smaller than the given number of post offices. from aonecode.com

e.g.
Input
you: [0, 0]
post_offices: [[-16, 5], [-1, 2], [4, 3], [10, -2], [0, 3], [-5, -9]]
k = 3

Output from aonecode.com
[[-1, 2], [0, 3], [4, 3]]
'''

# [m, n] - subject's location

import numpy as np

origin = [0,0]

dist_dict = {}

def get_k_nearest_post_offices(post_ofcs, k):
    for ind, val in enumerate(post_ofcs):
        eucl_dist = np.sqrt((origin[0] - val[0]) * (origin[0] - val[0]) + (origin[1] - val[1]) * (origin[1] - val[1]))
        dist_dict[eucl_dist] = val

    #print(dist_dict)
    return sorted(dist_dict)[:k]

k_nearest_po_keys = get_k_nearest_post_offices([[-16, 5], [-1, 2], [4, 3], [10, -2], [0, 3], [-5, -9]], 3)

for key in k_nearest_po_keys:
    print(dist_dict[key])

# to preserve duplicate keys if needed
# from collections import defaultdict
#
# d = defaultdict(list)
#
#
# def get_k_nearest_post_offices(post_ofcs, k):
#     for ind, val in enumerate(post_ofcs):
#         eucl_dist = np.sqrt((origin[0] - val[0]) * (origin[0] - val[0]) + (origin[1] - val[1]) * (origin[1] - val[1]))
#         d[eucl_dist].append(val)
#     return sorted(dist_dict)[:k]
#
# k_nearest_po_keys = get_k_nearest_post_offices([[-16, 5], [-1, 2], [4, 3], [10, -2], [0, 3], [-5, -9]], 3)
#
# for key in k_nearest_po_keys:
#     print(dist_dict[key])


import numpy as np
import math

def pendulum_derivatives(theta, omega, g=9.8, l=1):
    """
    \dot{\theta} = \omega
    \dot{\omega} = -\frac{g \sin\theta}{l}
    :param theta: angel of the pendulum
    :param omega: angular velocity of the pendulum
    :param g: gravitational acceleration
    :param l: length of the pendulum
    :return: derivative of angel, derivative of angular velocity
    """
    d_theta = omega
    d_omega = - np.sin(theta) * g / l
    return d_theta, d_omega

def simulate(sample_num, time_scale=0.1, simulate_length=100,
             theta_std=np.pi, omega_std=1,
             seed=623, noise_std=0.5):
    """
    Simulate single pendulum.
    :param sample_num:
    :param time_scale:
    :param simulate_length:
    :param theta_std:
    :param omega_std:
    :param seed:
    :param noise_std:
    :return: simulated data
    """
    np.random.seed(seed)
    batch_size = math.ceil(sample_num / simulate_length)

    data = np.zeros([simulate_length * batch_size, 4])
    theta = np.random.randn(batch_size, 1) * theta_std
    omega = np.random.randn(batch_size, 1) * omega_std

    for i in range(simulate_length):
        d_theta, d_omega = pendulum_derivatives(theta, omega)
        data[i * batch_size: (i + 1) * batch_size] = np.hstack((theta, omega, d_theta, d_omega))
        theta += d_theta * time_scale
        omega += d_omega * time_scale

    return data[:sample_num]

if __name__ == '__main__':
    data = simulate(3010)
    print(data[-1])
    print(data.shape)


import numpy as np
import matplotlib.pyplot as plt
import time
plt.rcParams['figure.figsize'] = [12, 12]
plt.rcParams.update({'font.size': 18})

n = 20
k = np.arange(1,n+1)
x = np.cos((k-1)*np.pi/(n-1))
#Input function
f=np.sin(np.pi*x)
#Analytically obtain the Derivative
df=np.pi*np.cos(np.pi*x)
df2=-np.pi**2*np.sin(np.pi*x)
# -- Differentiation via chebfft based on
# -- chebdifft.m matlab version
def chebdifft(f,M):
	N=len(f)

	a = np.flipud(f[1:N-1])
	a = np.concatenate((f,a))
	a0 = np.fft.fft(a)

	ones = np.ones(N-2)
	a = np.concatenate(([0.5],ones,[0.5] ))
	a0 = a0[0:N]*a/(N-1)  #a0 contains Chebyshev coefficients of f
	#print(a0)

	# Recursion formula for computing coefficients of ell'th derivative
	a = np.zeros((N,M+1),dtype="complex_")
	a[:,0] = a0
	for ell in np.arange(1,M+1):
		a[N-ell-1,ell]=2*(N-ell)*a[N-ell,ell-1];
		for k in np.arange(N-ell-2,0,-1):

			a[k,ell]=a[k+2,ell]+2*(k+1)*a[k+1,ell-1]
		a[0,ell]=a[1,ell-1]+a[2,ell]/2

	# Transform back to nphysical space
	b1 = [2*a[0,M]]
	b2 = a[1:N-1,M]
	b3 = [2*a[N-1,M]]
	b4 = np.flipud(b2)
	back = np.concatenate((b1,b2,b3,b4))
	Dmf = 0.5*np.fft.fft(back)
	# Real data in, real derivative out
	Dmf = Dmf[0:N]
	return np.real(Dmf)

t0 = time.time()
dfFFT = chebdifft(f,1)
dfFFT2 = chebdifft(f,2)
t1 = time.time()

print("Total time: {:}".format(t1-t0))
##Plot results
plt.plot(x, df.real, color='k', LineWidth=2, label='True Derivative')
# plt.plot(x, dfFD.real, '--', color='b', LineWidth=1.5, label='Finite Difference')
plt.plot(x, dfFFT.real, '--', color='c', LineWidth=1.5, label='Spectral Derivative')
plt.xlabel('X values')
plt.ylabel('Y values')
plt.legend()
plt.show()

##Plot results
plt.plot(x, df2.real, color='k', LineWidth=2, label='True Derivative')
# plt.plot(x, dfFD.real, '--', color='b', LineWidth=1.5, label='Finite Difference')
plt.plot(x, dfFFT2.real, '--', color='c', LineWidth=1.5, label='2nd Spectral Derivative')
plt.xlabel('X values')
plt.ylabel('Y values')
plt.legend()
plt.ylim(-np.pi**2,+np.pi**2)
plt.show()


import numpy as np

class LinearRegression():
    def __init__(self):
        self.learning_rate = 0.01
        self.total_iterations = 1000
    def yhat(self, X, w):
        '''
        predicted y value
        '''
        # making sure the dimensions are correct
        # predictions: yhat = w1*x1 + w2*x2  + ... - want a scalar value for each input
        # m - # training examples, n - # features, w - nx1, y - mx1, X - n x m, add intercept b so that X: n x m + 1
        # m x n * n x 1 = m x 1, want output to be 1 x m
        # 1 x n * n x m - w^T*X
        return np.dot(w.T, X)

    def loss(self, yhat, y):
        '''
        compute the loss - MSE
        '''
        L = 1/self.m * np.sum(np.power(yhat - y, 2)) # divide by number of examples, sum elementwise square of differences of the yhat - predicted and y - ground truth
        return L

    def gradient_descent(self, w, X, y, yhat):
        '''
        update the weigths based on the loss
        '''
        # again make sure the dimensions are correct
        # L = (yhat - y)  # loss : 1 x m, want the output to be n x 1 - multiply  n x m by (1 x m)^T = n x 1

        dLdw = 2/self.m * np.dot(X, (yhat - y).T) # gradient of L (loss) w.r.t. w (weights) , y = x * w + b, L = 1/m sum(yhat - y)^2 = 1/m sum(yhat - x*w+b)^2, dLdw = 2* 1/m *  x*w

        w = w - self.learning_rate * dLdw # update step, dLdw needs to be same dimension as w - nx1

        return w

    def main(self, X, y):
        '''
        will call the above functions for a number of iterations
        '''
        # add intercept (bias)
        x1 = np.ones((1, X.shape[1]))
        X = np.append(X, x1, axis=0) # add to features

        self.m = X.shape[1]
        self.n = X.shape[0]

        w = np.zeros((self.n, 1))

        # iterate
        for it in range(self.total_iterations + 1):
            yhat = self.yhat(X, w)
            loss = self.loss(yhat, y)

            # PRINT
            if it % 200 == 0:
                print(f'Cost at iteration: {it} is {loss}')

            w = self.gradient_descent(w, X, y, yhat)

        return w

if __name__ == '__main__':
    X = np.random.rand(1, 500)
    y = 3 * X + np.random.rand(1, 500) * 0.1 # add some small random Gaussian noise
    regression = LinearRegression()
    w = regression.main(X, y)