# linear regression for prostate cancer dataset

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn.datasets
import sklearn.cross_validation as cv
import sklearn.linear_model as lm
import scipy.io
import patsy

# Prevent numpy from printing too many digits
np.set_printoptions(precision=3)

# Prevent Pandas from printing results in html format
pd.set_option('notebook_repr_html', False)
pd.set_option('display.max_columns', 160)
pd.set_option('display.width', 1000)

# Get data
#url = 'http://statweb.stanford.edu/~tibs/ElemStatLearn/datasets/prostate.data'
url = 'https://web.stanford.edu/~hastie/ElemStatLearn/datasets/prostate.data'
df = pd.read_csv(url, sep='\t', header=0)
# skip the column of indices
df = df.drop('Unnamed: 0', axis=1)

# Convert the final column of T/F into boolean
istrain_str = df['train']
istrain = np.asarray([True if s == 'T' else False for s in istrain_str])
istest = np.logical_not(istrain)
df = df.drop('train', axis=1)

# Scale the X variables
scaler = sklearn.preprocessing.StandardScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
df_scaled['lpsa'] = df['lpsa']  # restore the unscaled y column

##############################

# Helper functions
def L2loss(yhat, ytest):
    ntest = ytest.size
    sqerr = np.power(yhat - ytest, 2)
    mse = np.mean(sqerr)
    stderr = np.std(sqerr) / np.sqrt(ntest)
    return (mse, stderr)

def trainAndTest(X, y, train_mask):
    #  partition into train/test set
    Xtrain = X[train_mask]
    ytrain = y[train_mask]
    Xtest = X[np.logical_not(train_mask)]
    ytest = y[np.logical_not(train_mask)]

    # Fit model
    linreg = lm.LinearRegression(fit_intercept = False)
    linreg.fit(Xtrain, ytrain)

    # Extract parameters
    coef = linreg.coef_
    names = X.columns.tolist()
    print([name + ':' + str(round(w,3)) for name, w in zip(names, coef)])

    # Measure train error
    yhat = linreg.predict(Xtrain)
    (mse, stderr) = L2loss(yhat, ytrain)
    print('Train mse {0:0.3f}, stderr {1:0.3f}'.format(mse, stderr))

    # Measure test error
    yhat = linreg.predict(Xtest)
    (mse, stderr) = L2loss(yhat, ytest)
    print('Test mse {0:0.3f}, stderr {1:0.3f}'.format(mse, stderr))

##############################

# use all the variables, without dummy encoding. Adds column of 1s.
#y, X = patsy.dmatrices('lpsa ~ .', df, return_type="dataframe")
#formula = 'lpsa ~ {0}'.format(' + '.join(xcols))
#y, X = patsy.dmatrices(formula, df_scaled, return_type="dataframe")
#y = np.ravel(y)

X = df_scaled.ix[:,:-1] # exclude final lpsa column
N = X.shape[0]
X.insert(0, 'intercept', np.ones(N))
y = df_scaled['lpsa'] # pandas series

'''
   intercept    lcavol   lweight       age      lbph       svi       lcp   gleason     pgg45
0          1 -1.645861 -2.016634 -1.872101 -1.030029 -0.525657 -0.867655 -1.047571 -0.868957
1          1 -1.999313 -0.725759 -0.791989 -1.030029 -0.525657 -0.867655 -1.047571 -0.868957
2          1 -1.587021 -2.200154  1.368234 -1.030029 -0.525657 -0.867655  0.344407 -0.156155
3          1 -2.178174 -0.812191 -0.791989 -1.030029 -0.525657 -0.867655 -1.047571 -0.868957
4          1 -0.510513 -0.461218 -0.251933 -1.030029 -0.525657 -0.867655 -1.047571 -0.868957
'''

print('vanilla model on scaled X')
trainAndTest(X, y, istrain)

# Matches HTF Table 3.2 book
# https://web.stanford.edu/~hastie/ElemStatLearn/
#['intercept:2.465', 'lcavol:0.676', 'lweight:0.262', 'age:-0.141', 'lbph:0.209',
#'svi:0.304', 'lcp:-0.287', 'gleason:-0.021', 'pgg45:0.266']

#Train mse 0.439, stderr 0.079
#Test mse 0.521, stderr 0.176

##############################

# Treat categorical variables properly
df_cat = df_scaled.copy()
# svi has values {0,1}
df_cat["svi"] = df["svi"].astype('category')
# gleason has  values (6,7,8,9), so we convert to factor
df_cat["gleason"] = df["gleason"].astype('category')

# Look at the reformatted data. Notice how lpsa, svi and gleason match
# original data, whereas other columns are scaled
df_cat.head()

#     lcavol   lweight       age      lbph svi       lcp gleason     pgg45      lpsa
#0 -1.645861 -2.016634 -1.872101 -1.030029   0 -0.867655       6 -0.868957 -0.430783
#1 -1.999313 -0.725759 -0.791989 -1.030029   0 -0.867655       6 -0.868957 -0.162519
#2 -1.587021 -2.200154  1.368234 -1.030029   0 -0.867655       7 -0.156155 -0.162519
#3 -2.178174 -0.812191 -0.791989 -1.030029   0 -0.867655       6 -0.868957 -0.162519
#4 -0.510513 -0.461218 -0.251933 -1.030029   0 -0.867655       6 -0.868957  0.371564

# create summary of numeric and categorical columns
df_cat.describe(include='all')

##############################

# Basic model. Adds column of 1s at start.
X = patsy.dmatrix('lcavol + lweight + age + lbph + lcp + svi + gleason + pgg45',
    df_cat, return_type="dataframe")
y = df_cat['lpsa']

X.head()

#   Intercept  svi[T.1]  gleason[T.7]  gleason[T.8]  gleason[T.9]    lcavol   lweight       age      lbph       lcp     pgg45
#0          1         0             0             0             0 -1.645861 -2.016634 -1.872101 -1.030029 -0.867655 -0.868957
#1          1         0             0             0             0 -1.999313 -0.725759 -0.791989 -1.030029 -0.867655 -0.868957
#2          1         0             1             0             0 -1.587021 -2.200154  1.368234 -1.030029 -0.867655 -0.156155
#3          1         0             0             0             0 -2.178174 -0.812191 -0.791989 -1.030029 -0.867655 -0.868957
#4          1         0             0             0             0 -0.510513 -0.461218 -0.251933 -1.030029 -0.867655 -0.868957

pd.set_option('precision', 1)
X.head()
pd.set_option('precision', 3)

#    Intercept  svi[T.1]  gleason[T.7]  gleason[T.8]  gleason[T.9]  lcavol  lweight  age  lbph  lcp  pgg45
# 0        1.0       0.0           0.0           0.0           0.0    -1.6     -2.0 -1.9  -1.0 -0.9   -0.9
# 1        1.0       0.0           0.0           0.0           0.0    -2.0     -0.7 -0.8  -1.0 -0.9   -0.9
# 2        1.0       0.0           1.0           0.0           0.0    -1.6     -2.2  1.4  -1.0 -0.9   -0.2
# 3        1.0       0.0           0.0           0.0           0.0    -2.2     -0.8 -0.8  -1.0 -0.9   -0.9
# 4        1.0       0.0           0.0           0.0           0.0    -0.5     -0.5 -0.3  -1.0 -0.9   -0.9

# print summary statistics of each feature
X.describe()

#       Intercept   svi[T.1]  gleason[T.7]  gleason[T.8]  gleason[T.9]        lcavol       lweight           age          lbph           lcp         pgg45
# count       97.0  97.000000     97.000000     97.000000     97.000000  9.700000e+01  9.700000e+01  9.700000e+01  9.700000e+01  9.700000e+01  9.700000e+01
# mean         1.0   0.216495      0.577320      0.010309      0.051546  3.204767e-17 -3.170431e-16  4.131861e-16 -2.432190e-17  3.662591e-17  5.636957e-17
# std          0.0   0.413995      0.496552      0.101535      0.222258  1.005195e+00  1.005195e+00  1.005195e+00  1.005195e+00  1.005195e+00  1.005195e+00
# min          1.0   0.000000      0.000000      0.000000      0.000000 -2.300218e+00 -2.942386e+00 -3.087227e+00 -1.030029e+00 -8.676552e-01 -8.689573e-01
# 25%          1.0   0.000000      0.000000      0.000000      0.000000 -7.139973e-01 -5.937689e-01 -5.219612e-01 -1.030029e+00 -8.676552e-01 -8.689573e-01
# 50%          1.0   0.000000      1.000000      0.000000      0.000000  8.264956e-02 -1.392703e-02  1.531086e-01  1.383966e-01 -4.450983e-01 -3.343557e-01
# 75%          1.0   0.000000      1.000000      0.000000      0.000000  6.626939e-01  5.806076e-01  5.581506e-01  1.010033e+00  9.762744e-01  5.566470e-01
# max

print('categorical')
trainAndTest(X, y, istrain)

#['Intercept:2.218', 'svi[T.1]:0.707', 'gleason[T.7]:0.183', 'gleason[T.8]:0.727', 'gleason[T.9]:-0.497',
#'lcavol:0.666', 'lweight:0.275', 'age:-0.164', 'lbph:0.19', 'lcp:-0.341', 'pgg45:0.31']

#Train mse 0.413, stderr 0.070
#Test mse 0.551, stderr 0.179

##############################

# Rename columns to make them shorter (for printing purposes)
df_cat = df_cat.rename(columns = {'gleason':'g', 'svi':'s'})

# Interaction terms.
X = patsy.dmatrix('lcavol + lweight + age + lbph + lcp + s:g + pgg45',
    df_cat, return_type="dataframe")
y = df_cat['lpsa']

X.head()

'''
   Intercept  g[T.7]  g[T.8]  g[T.9]  s[T.1]:g[6]  s[T.1]:g[7]  s[T.1]:g[8]  s[T.1]:g[9]    lcavol   lweight       age      lbph       lcp     pgg45
0          1       0       0       0            0            0            0            0 -1.645861 -2.016634 -1.872101 -1.030029 -0.867655 -0.868957
1          1       0       0       0            0            0            0            0 -1.999313 -0.725759 -0.791989 -1.030029 -0.867655 -0.868957
2          1       1       0       0            0            0            0            0 -1.587021 -2.200154  1.368234 -1.030029 -0.867655 -0.156155
3          1       0       0       0            0            0            0            0 -2.178174 -0.812191 -0.791989 -1.030029 -0.867655 -0.868957
4          1       0       0       0            0            0            0            0 -0.510513 -0.461218 -0.251933 -1.030029 -0.867655 -0.868957
'''

print('interaction ')
trainAndTest(X, y, istrain)

#['Intercept:2.223', 'gleason[T.7]:0.121', 'gleason[T.8]:0.641', 'gleason[T.9]:0.048',
#'svi[T.1]:gleason[6]:0.0', 'svi[T.1]:gleason[7]:0.849', 'svi[T.1]:gleason[8]:0.0', 'svi[T.1]:gleason[9]:-0.872',
#'lcavol:0.631', 'lweight:0.265', 'age:-0.135', 'lbph:0.243', 'lcp:-0.292', 'pgg45:0.287']

#Train mse 0.390, stderr 0.075
#Test mse 0.575, stderr 0.170


# coding: utf-8

import numpy as np
from numpy.linalg import svd
import matplotlib.pyplot as plt
import pylayers.util.geomutil as geu
from mpl_toolkits.mplot3d import Axes3D

N = 200
np.random.seed(1)
t = np.linspace(0,2*np.pi,N)
x = 10*np.cos(t)[None,:]
y = 20*np.sin(t)[None,:]
z = 30*(1-t/np.pi)[None,:]
pts1 = np.vstack((x,y,z)).T
print pts1.shape
ptm=np.mean(pts1,axis=0)
pts1 = pts1 - ptm
M = geu.MEulerAngle(0,2.4,.24)
pts2 = np.dot(pts1,M.T)

U1,S1,V1=svd(pts1)
U2,S2,V2=svd(pts2)

# In[4]:

x1_r= S1[0]*U1[:,0]
y1_r= S1[1]*U1[:,1]
z1_r= S1[2]*U1[:,2]
pts1_r= np.vstack((x1_r,y1_r,z1_r)).T
x2_r= S2[0]*U2[:,0]
y2_r= S2[1]*U2[:,1]
z2_r= S2[2]*U2[:,2]
pts2_r= np.vstack((x2_r,y2_r,z2_r)).T

# In[5]:

np.dot(M,M.T)

# In[6]:

S1

# In[7]:

S2

# In[8]:

fig1 = plt.figure()
ax1 = fig1.add_subplot(111,projection='3d')
ax1.scatter(pts1[:,0],pts1[:,1],pts1[:,2],s=20,color='g')
ax1.scatter(pts2[:,0],pts2[:,1],pts2[:,2],s=20,color='b')
ax1.scatter(pts1_r[:,0],pts1_r[:,1],pts1_r[:,2],s=20,color='r')
ax1.scatter(pts2_r[:,0],pts2_r[:,1],pts2_r[:,2],s=20,color='r')

# In[9]:

U1

# In[10]:

U2

# In[11]:

np.linalg.det(V2)

# On est bon à une symmétrie près

# In[ ]:


# Question 01, Lab 10
# AB Satyaprakash, 180123062

# imports
from math import sqrt
import numpy as np
import matplotlib.pyplot as plt

# functions

def getStockPrices(part, T, r, sigma):
    S1, S2 = np.ndarray(T+1), np.ndarray(T+1)
    S1[0], S2[0] = 100.0, 100.0
    for i in range(1, T+1):
        W = sqrt(delT)*np.random.randn()
        S1[i] = S1[i-1]*np.exp(sigma*W + (r-0.5*(sigma**2))*delT)
        if part == 'A':
            # return risk neutral and real world stock prices
            S2[i] = S2[i-1]*np.exp(sigma*(W) + (mu-0.5*(sigma**2))*delT)
        else:
            # return variance reduction stock prices
            S2[i] = S2[i-1]*np.exp(sigma*(-W) + (r-0.5*(sigma**2))*delT)
    return S1[1:], S2[1:]

def stockSimulation():
    r = 0.05
    sigma = 0.2
    T = 180
    SrnArray, SArray = [], []
    for _ in range(10):
        Srn, S = getStockPrices('A', T, r, sigma)
        SrnArray.append(Srn)
        SArray.append(S)

    for Srn in SrnArray:
        plt.plot(Srn)
    plt.xlabel('Time Steps')
    plt.ylabel('Stock price')
    plt.title('10 stock simulation paths in Risk Neutral World')
    plt.show()

    for S in SArray:
        plt.plot(S)
    plt.xlabel('Time Steps')
    plt.ylabel('Stock price')
    plt.title('10 stock simulation paths in Real World')
    plt.show()

def stockSimulationCompare():
    r = 0.05
    sigma = 0.2
    T = 180
    Srn, S = getStockPrices('A', T, r, sigma)
    plt.figure()
    plt.plot(S)
    plt.plot(Srn)
    plt.legend(['real', 'risk neutral'])
    plt.xlabel('Time Steps')
    plt.ylabel('Stock price')
    plt.title('Comparison of stock price in real and risk neutral world')
    plt.show()

def optionPrice(m, K, option, T, r, sigma):
    payoffSum1, payoffSum2 = 0, 0
    for i in range(m):
        Splus, Sminus = getStockPrices('B', T, r, sigma)
        Yplus = np.mean(Splus)
        Yminus = np.mean(Sminus)
        if option == 'Call':
            payoff1 = max(0, Yplus-K)
            payoff2 = 0.5*(max(0, Yplus-K) + max(0, Yminus-K))
        else:
            payoff1 = max(0, K-Yplus)
            payoff2 = 0.5*(max(0, K-Yplus) + max(0, K-Yminus))
        payoffSum1 = payoffSum1 + payoff1
        payoffSum2 = payoffSum2 + payoff2
    return np.exp(-r*delT*T)*(payoffSum1/m), np.exp(-r*delT*T)*(payoffSum2/m)

def optionPriceSensitivityAnalysis(option):
    T, r, sigma = 180, 0.05, 0.2
    for K in [90, 105, 110]:
        a1, a2, m = [], [], []
        for i in range(500, 3000, 500):
            without_var_red, with_var_red = optionPrice(
                i, K, option, T, r, sigma)
            a1.append(without_var_red)
            a2.append(with_var_red)
            m.append(i)

        print(option + ' Option price for K = ' + str(K) +
              ' is ' + str(np.round_(np.mean(a1), 5)))
        plt.plot(m,a1)
        plt.xlabel('# of simulations')
        plt.ylabel('Option price')
        plt.title(
            option + ' Option price sensitivity analysis with K = {}'.format(K))
        plt.show()

    T, r, sigma = 180, 0.05, 0.2
    KArr = np.arange(50, 150, 5)
    a1, a2 = [], []
    for K in KArr:
        without_var_red, with_var_red = optionPrice(
            100, K, option, T, r, sigma)
        a1.append(without_var_red)
        a2.append(with_var_red)
    plt.plot(KArr, a1, color='orange')
    plt.xlabel('Strike Price')
    plt.ylabel('Option price')
    plt.title(option + ' Option price sensitivity analysis')
    plt.show()

    K, T, r = 105, 180, 0.05
    sigmaArr = np.arange(0.02, 1.02, 0.02)
    a1, a2 = [], []
    for sigma in sigmaArr:
        without_var_red, with_var_red = optionPrice(
            100, K, option, T, r, sigma)
        a1.append(without_var_red)
        a2.append(with_var_red)
    plt.plot(sigmaArr, a1, color='lime')
    plt.xlabel('Volatility')
    plt.ylabel('Option price')
    plt.title(option + ' Option price sensitivity analysis')
    plt.show()

    K, T, sigma = 105, 180, 0.2
    rArr = np.arange(0.05, 1.05, 0.05)
    a1, a2 = [], []
    for r in rArr:
        without_var_red, with_var_red = optionPrice(
            100, K, option, T, r, sigma)
        a1.append(without_var_red)
        a2.append(with_var_red)
    plt.plot(rArr, a1, color='red')
    plt.xlabel('Risk-free rate')
    plt.ylabel('Option price')
    plt.title(option + ' Option price sensitivity analysis')
    plt.show()

    K, r, sigma = 105, 0.05, 0.2
    TArr = np.arange(30, 365, 1)
    a1, a2 = [], []
    for T in TArr:
        without_var_red, with_var_red = optionPrice(
            100, K, option, T, r, sigma)
        a1.append(without_var_red)
        a2.append(with_var_red)
    plt.plot(TArr, a1, color='blue')
    plt.xlabel('Maturity')
    plt.ylabel('Option price')
    plt.title(option + ' Option price sensitivity analysis')
    plt.show()

# program body
mu = 0.1
delT = 1.0/365
S0 = 100  # asset price at t = 0 is S(0) = 100

# 10 stock simulations in real and risk neutral worlds
stockSimulation()
stockSimulationCompare()
optionPriceSensitivityAnalysis('Call')
optionPriceSensitivityAnalysis('Put')

Semester 6/MA 374 (Financial Engg. Lab)/Lab 10/180123062_AB_q1.py


# demo to compute Madelung constant for NaCl (cubic lattice) to 15 digits, via
# periodic-BC solve approach instead of summation over lattice.
# Barnett 11/17/19

# Note on runtimes:
# If run cold from python, takes 5s CPU time due to numba jit compilation.
# If rerun it once njit done (eg within ipython): 0.2s precomp + 0.003s comp.

from numpy import eye,array,random,prod,pi
import perilap3d as l3p

L = eye(3)                            # unit cube (each row is a lattice vec)
p = l3p.lap3d3p(L)                    # make a periodizer object
p.precomp(tol=1e-12,verb=0)           # do expensive precomputation (0.2 sec)

# now we can periodize any arrangement of (zero-sum) charges & dipoles fast...
y = array([[1,1,1],[1,1,-1],[1,-1,1],[1,-1,-1],[-1,1,1],[-1,1,-1],[-1,-1,1],[-1,-1,-1]])               # the eight NaCl atoms in unit cell side-length 4
c = prod(y,1)                         # their charges alternate (-1)^{i+j+k}
y = y/4                               # rescale for side length 1
pot,grad = p.eval(y,None,None,c)      # charges (no dipoles), self only  (3 ms)

# extract Madelung const (note overall pot const is arb for periodic solve)...
v = (pot[1]-pot[0])/2                 # half the pot diff btw Na and Cl locs
v = v*4*pi                            # remove 1/4pi fac from 1/r fund sol
v = v/2                               # rescale as if NaCl on unit lattice
vexact = 1.74756459463318219          # cf Bailey et al 2006, Amer Math Monthly
print("Madelung const = %.15g (error from known: %.3g)"%(v,abs(v-vexact)))

# Note on series summation:
# The infinite sum does not converge if taken using an expanding sphere, but
# does for an expanding cube. This illustrates the subtlety of lattice sums,
# and motivates our periodic BVP approach.
# Due to some nice work in python's mpmath package by F. Johansson, nsum can
# amazingly apply series convergence acceleration methods and get 16 digits
# for the constant, in arbitrary precision arithmetic, in 10s CPU time, via:
#   from mpmath import nsum,inf
#   nsum(lambda i,j,k: (-1)**(i+j+k)/(i**2+j**2+k**2)**0.5,
#        ...     [-inf,inf], [-inf,inf], [-inf,inf], ignore=True)
# Out[13]: mpf('-1.7475645946331821')


from numpy.linalg import inv
from scipy import misc

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

#----------------------------------------------------------------------------------------#
# Training data

#x1_data = np.array([-8.0, -6.00, -7.0, -4.0, 2.0, 5.0, 1.0, 3.0, 7.0])
#x2_data = np.array([-8.0, -3.0, 2.0, 4.0, 3.0, 7.0, -1.0, -4.0, -7.0])
x1_data = np.array([1500, 1500, 1500,   2500, 2500, 2500,   500,    500, 500])
x2_data = np.array([1500, 2500, 500,    1500, 2500, 500,    1500,   2500, 500])

y_data = np.array([145,   335,  205,    158,  155,  135,    170,    135, 170])

#y_data = np.array([-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0])

sigma_n = 0.1

x1_min = min(x1_data) - 500.0
x1_max = max(x1_data) + 500.0

x2_min = min(x2_data) - 500.0
x2_max = max(x2_data) + 500.0

matplotlib_marker = []
for i in y_data:
    matplotlib_marker.append('o')
	#if i == -1.0: matplotlib_marker.append('o')
	#if i == 1.0: matplotlib_marker.append('x')

plt.xlabel('x1')
plt.ylabel('x2')
plt.xlim(x1_min,x1_max)
plt.ylim(x2_min,x2_max)
plt.title('Training Data')

plt.grid(True)

for i in range(len(x1_data)):
    print(i)
    plt.scatter(x1_data[i], x2_data[i], marker=matplotlib_marker[i])

plt.savefig('gaussian_processes_2d_training_data.png', bbox_inches='tight')
#plt.show()
plt.close()

#----------------------------------------------------------------------------------------#
# Find Gaussian processes (3) hyperparameters using gradient descent

def log_likelihood_function(gph_l1, gph_l2, gph_sigma_f):
	dim_x_data = len(x1_data)
	k = np.zeros((dim_x_data,dim_x_data))
	for i in range(dim_x_data):
		for j in range(i+1):
			dx1 = x1_data[i] - x1_data[j]
			dx2 = x2_data[i] - x2_data[j]
			coef = - 0.5 * ( dx1**2 / gph_l1**2 + dx2**2 / gph_l2**2 )
			k[i,j] = gph_sigma_f * gph_sigma_f * math.exp( coef )
			k[j,i] = k[i,j]
			if i == j:
				k[i,j] = k[i,j] + sigma_n * sigma_n
	k_inv = inv(k)
	m1 = np.dot(k_inv,y_data)
	part1 = np.dot(y_data.T,m1)
	part2 = math.log(np.linalg.det(k))
	return -0.5 * part1 - 0.5 * part2 - dim_x_data / 2.0 * math.log(2*math.pi)

def partial_derivative(func, var=0, point=[]):
    args = point[:]
    def wraps(x):
        args[var] = x
        return func(*args)
    return misc.derivative(wraps, point[var], dx = 1e-6)

alpha = 0.1 # learning rate
nb_max_iter = 100 # Nb max of iterations
eps = 0.0001 # stop condition

gph_l1 = 2.5 # start point
gph_l2 = 2.5 # start point
gph_sigma_f = 3.0 # start point

ll_value = log_likelihood_function(gph_l1, gph_l2, gph_sigma_f)

cond = eps + 10.0 # start with cond greater than eps (assumption)
nb_iter = 0
tmp_ll_value = ll_value
while cond > eps and nb_iter < nb_max_iter:

	tmp_gph_l1 = gph_l1 + \
	alpha * partial_derivative(log_likelihood_function, 0, [gph_l1, gph_l2, gph_sigma_f])

	tmp_gph_l2 = gph_l2 + \
	alpha * partial_derivative(log_likelihood_function, 1, [gph_l1, gph_l2, gph_sigma_f])

	tmp_gph_sigma_f = gph_sigma_f + \
	alpha * partial_derivative(log_likelihood_function, 2, [gph_l1, gph_l2, gph_sigma_f])

	gph_l1 = tmp_gph_l1
	gph_l2 = tmp_gph_l2
	gph_sigma_f = tmp_gph_sigma_f

	ll_value = log_likelihood_function(gph_l1, gph_l2, gph_sigma_f)

	nb_iter = nb_iter + 1

	cond = abs( tmp_ll_value - ll_value )

	tmp_ll_value = ll_value

	print 'gph_l1, gph_l2, gph_sigma_f, nb_iter, cond', \
	gph_l1, gph_l2, gph_sigma_f, nb_iter, cond

print "Hyperparameters found using gradient descent: "
print 'gph_l1, gph_l2, gph_sigma_f', gph_l1, gph_l2, gph_sigma_f

#----------------------------------------------------------------------------------------#
# Prediction 1 point

dim_x1_data = len(x1_data)
k = np.zeros((dim_x1_data,dim_x1_data))
for i in range(dim_x1_data):
	for j in range(i+1):
		dx1 = x1_data[i] - x1_data[j]
		dx2 = x2_data[i] - x2_data[j]
		coef = - 0.5 * ( dx1**2 / gph_l1**2 + dx2**2 / gph_l2**2 )
		k[i,j] = gph_sigma_f * gph_sigma_f * math.exp( coef )
		k[j,i] = k[i,j]
		if i == j:
			k[i,j] = k[i,j] + sigma_n * sigma_n

#x1_new = -7.0
#x2_new = -5.0
x1_new=700.0
x2_new=500.0
k_new = np.zeros((dim_x1_data))
for i in range(dim_x1_data):
	dx1 = x1_new - x1_data[i]
	dx2 = x2_new - x2_data[i]
	coef = - 0.5 * ( dx1**2 / gph_l1**2 + dx2**2 / gph_l2**2 )
	k_new[i] = gph_sigma_f * gph_sigma_f * math.exp( coef )

k_inv = inv(k)
m1 = np.dot(k_new,k_inv)
y_new = np.dot(m1,y_data)

print "Prediction for 1 point: "
print 'x1_new, x2_new, y_new', x1_new, x2_new, y_new

var_y = k[0,0] - k_new.dot(k_inv.dot(np.transpose(k_new)))
#print "var_y", var_y

x1 = np.append(x1_data,x1_new)
x2 = np.append(x2_data,x2_new)
y = np.append(y_data,y_new)

matplotlib_marker = []
for i in y:
    matplotlib_marker.append('o')
	#if i == -1.0: matplotlib_marker.append('o')
	#if i == 1.0: matplotlib_marker.append('x')
	#if i != -1.0 and i != 1.0: matplotlib_marker.append('d')

plt.xlim(x1_min, x1_max)
plt.ylim(x2_min, x2_max)

plt.title('Prediction for a new point')

plt.xlabel('x1')
plt.ylabel('x2')

plt.grid(True)

print len(x1), len(x2), len(matplotlib_marker)

for i in range(len(x1)):
	plt.scatter(x1[i], x2[i], marker=matplotlib_marker[i])

plt.savefig('gaussian_processes_2d_prediction_1_point.png', bbox_inches='tight')
#plt.show()
plt.close()

#----------------------------------------------------------------------------------------#
# Inference regular gridded data

#x1 = np.arange(-10.0,10.0,0.1)
#x2 = np.arange(-10.0,10.0,0.1)
x1 = np.arange(0.0,3000.0, 10.0)
x2 = np.arange(0.0,3000.0, 10.0)

dim_x1 = x1.shape[0]
dim_x2 = x2.shape[0]
dim_x = dim_x1 * dim_x2

Z = np.zeros((dim_x))
Z_var = np.zeros((dim_x))

i1_cp = 0
i2_cp = 0
for i in range(dim_x):
	x1_new = x1[i1_cp]
	x2_new = x2[i2_cp]
	k_new = np.zeros((dim_x1_data))
	for j in range(dim_x1_data):
		dx1 = x1_new - x1_data[j]
		dx2 = x2_new - x2_data[j]
		coef = - 0.5 * ( dx1**2 / gph_l1**2 + dx2**2 / gph_l2**2 )
		k_new[j] = gph_sigma_f * gph_sigma_f * math.exp( coef )
	k_inv = inv(k)
	m1 = np.dot(k_new,k_inv)
	y_new = np.dot(m1,y_data)
	Z[i] = y_new
	Z_var[i] = k[0,0] - k_new.dot(k_inv.dot(np.transpose(k_new)))
	i2_cp = i2_cp + 1
	if i2_cp == dim_x2:
		i1_cp = i1_cp + 1
		i2_cp = 0
	if i1_cp == dim_x1:
		i1_cp = 0

Z = np.reshape(Z, (dim_x1,dim_x2))
Z_var = np.reshape(Z_var, (dim_x1,dim_x2))

print(Z.T)

plt.imshow(Z.T, interpolation='bilinear', origin='lower')
plt.colorbar()
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('Gaussian processes 2d regression')
plt.grid(True)
plt.savefig('gaussian_process_2d_gridded_data.png')
plt.close()

plt.imshow(Z_var.T, interpolation='bilinear', origin='lower')
plt.colorbar()
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('Gaussian processes 2d regression (variance)')
plt.grid(True)
plt.savefig('gaussian_process_2d_gridded_data_var.png')
plt.close()

#----------------------------------------------------------------------------------------#
# Classification

for x in np.nditer(Z, op_flags=['readwrite']):
	x[...] = 1.0 / ( 1.0 + math.exp(-x))

plt.imshow(Z.T, interpolation='bilinear', origin='lower',vmin=0,vmax=1)
plt.colorbar()
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('Gaussian processes 2d for classification')
plt.grid(True)
plt.savefig('gaussian_process_2d_gridded_data_normalized.png')
plt.close()

CS = plt.contour(x1, x2, Z.T, origin='lower')
plt.clabel(CS, inline=1, fontsize=10)
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('Gaussian processes 2d for classification (contour plot)')
plt.grid(True)
plt.savefig('gaussian_process_2d_gridded_data_normalized_contour_plot.png')
plt.close()


"""
This script calculates solutions to some of the problems from the
"Many Digits" competition:
http://www.cs.ru.nl/~milad/manydigits/problems.php

Run with:

    python manydigits.py

"""
from mpmath import *
from mpmath.libmp import to_fixed, bin_to_radix

dps = 100
mp.dps = dps + 10

def pr(x):
    """Return the first dps digits after the decimal point"""
    x = x._mpf_
    p = int(dps*3.33 + 10)
    t = to_fixed(x, p)
    d = bin_to_radix(t, p, 10, dps)
    s = str(d).zfill(dps)[-dps:]
    return s[:dps//2] + "\n" + s[dps//2:]

print("""
This script prints answers to a selection of the "Many Digits"
competition problems: http://www.cs.ru.nl/~milad/manydigits/problems.php

The output for each problem is the first 100 digits after the
decimal point in the result.
""")

print("C01: sin(tan(cos(1)))")
print(pr(sin(tan(cos(1)))))
print()

print("C02: sqrt(e/pi)")
print(pr(sqrt(e/pi)))
print()

print("C03: sin((e+1)^3)")
print(pr(sin((e+1)**3)))
print()

print("C04: exp(pi*sqrt(2011))")
mp.dps += 65
print(pr(exp(pi*sqrt(2011))))
mp.dps -= 65
print()

print("C05: exp(exp(exp(1/2)))")
print(pr(exp(exp(exp(0.5)))))
print()

print("C06: arctanh(1-arctanh(1-arctanh(1-arctanh(1/pi))))")
print(pr(atanh(1-atanh(1-atanh(1-atanh(1/pi))))))
print()

print("C07: pi^1000")
mp.dps += 505
print(pr(pi**1000))
mp.dps -= 505
print()

print("C08: sin(6^(6^6))")
print(pr(sin(6**(6**6))))
print()

print("C09: sin(10*arctan(tanh(pi*(2011^(1/2))/3)))")
mp.dps += 150
print(pr(sin(10*atan(tanh(pi*sqrt(2011)/3)))))
mp.dps -= 150
print()

print("C10: (7+2^(1/5)-5*(8^(1/5)))^(1/3) + 4^(1/5)-2^(1/5)")
a = mpf(1)/5
print(pr(((7 + 2**a - 5*(8**a))**(mpf(1)/3) + 4**a - 2**a)))
print()

print("C11: tan(2^(1/2))+arctanh(sin(1))")
print(pr((tan(sqrt(2)) + atanh(sin(1)))))
print()

print("C12: arcsin(1/e^2) + arcsinh(e^2)")
print(pr(asin(1/exp(2)) + asinh(exp(2))))
print()

print("C17: S= -4*Zeta(2) - 2*Zeta(3) + 4*Zeta(2)*Zeta(3) + 2*Zeta(5)")
print(pr(-4*zeta(2) - 2*zeta(3) + 4*zeta(2)*zeta(3) + 2*zeta(5)))
print()

print("C18: Catalan G = Sum{i=0}{\infty}(-1)^i/(2i+1)^2")
print(pr(catalan))
print()

print("C21: Equation exp(cos(x)) = x")
print(pr(findroot(lambda x: exp(cos(x))-x, 1)))
print()

print("C22: J = integral(sin(sin(sin(x)))), x=0..1")
print(pr(quadts(lambda x: sin(sin(sin(x))), [0, 1])))
print()


import numpy as np

# x=0,y=1; x=1,y=0
def ReadData():
    X = np.array([0,1]).reshape(1,2)
    Y = np.array([1,0]).reshape(1,2)
    return X,Y

def ForwardCalculation(w,b,x):
    z = w * x + b
    return z

def BackPropagation(x,y,z):
    dZ = z - y
    dB = dZ
    dW = dZ * x
    return dW, dB

def UpdateWeights(w, b, dW, dB, eta):
    w = w - eta*dW
    b = b - eta*dB
    return w,b

def CheckLoss(w, b, X, Y, count):
    Z = w * X + b
    LOSS = (Z - Y)**2
    loss = LOSS.sum()/count/2
    return loss

def Test(w,b):
    z1 = ForwardCalculation(w, b, 0)  # x=0, check if z==1
    z2 = ForwardCalculation(w, b, 1)  # x=1, check if z==0
    print (z1,z2)
    if np.abs(z1-1) < 0.001 and np.abs(z2-0)<0.001:
        return True
    return False

if __name__ == '__main__':
    # initialize_data
    eta = 0.1
    # set w,b=0, you can set to others values to have a try
    #w, b = np.random.random(),np.random.random()
    w, b = 0, 0
    eps = 1e-15
    max_epoch = 10000
    # calculate loss to decide the stop condition
    loss = 1
    # create mock up data
    X, Y = ReadData()
    # count of samples
    num_features = X.shape[0]
    num_example = X.shape[1]

    for epoch in range(max_epoch):
        for i in range(num_example):
            # get x and y value for one sample
            x = X[:,i]
            y = Y[:,i]
            # get z from x,y
            z = ForwardCalculation(w, b, x)
            # calculate gradient of w and b
            dW, dB = BackPropagation(x, y, z)
            # update w,b
            w, b = UpdateWeights(w, b, dW, dB, eta)
            # calculate loss for this batch
            loss = CheckLoss(w,b,X,Y,num_example)
            print(epoch,i,loss,w,b)

        if loss < eps:
            break

    print("w=%f,b=%f" %(w,b))

    print("result=", Test(w, b))


import numpy as np
import matplotlib.pyplot as plt
from tabulate import tabulate

def newton_poly(x,x_new,y):
    '''
    x: x-values
    x_new: x-value to be interpolated
    y: y-values
    '''

    n = len(y)
    coef = np.zeros([n, n])

    coef[:,0] = y

    for j in range(1,n):
        for i in range(n-j):
            coef[i][j] = \
           (coef[i+1][j-1] - coef[i][j-1]) / (x[i+j]-x[i])

    coef = coef[0,:]

    n = len(x) - 1
    p = coef[n]
    for k in range(1,n+1):
        p = coef[n-k] + (x_new -x[n-k])*p
    return p

if __name__ == "__main__":
    temperature = np.array([250,300,350,400,450,500])
    cp = np.array([1.003,1.005,1.008,1.013,1.020,1.029])
    cv = np.array([0.716,0.718,0.721,0.726,0.733,0.742])
    k = np.array([1.401,1.400,1.398,1.395,1.391,1.387])

    temperature_new = np.arange(250,501,10)
    cv_new= newton_poly(temperature,temperature_new,cv)
    cp_new = newton_poly(temperature, temperature_new, cp)
    k_new = newton_poly(temperature, temperature_new, k)

    table = np.array([temperature_new,cp_new,cv_new,k_new])

    print(tabulate(table.T, headers=["Temperature","Cp","Cv","K"]))

    fig,axs = plt.subplots(3,1,sharex=True)

    axs[0].plot(temperature, cp, 'o')
    axs[0].set_ylabel("Cp")
    axs[1].plot(temperature,cv,'o')
    axs[1].set_ylabel("Cv")
    axs[2].plot(temperature,k,'o')
    axs[2].set_ylabel("K")

    axs[0].plot(temperature_new, cp_new)
    axs[1].plot(temperature_new, cv_new)
    axs[2].plot(temperature_new, k_new)
    for i in range(3):
        axs[i].set_xlabel("Temperature")
        axs[i].legend()
        axs[i].grid()

    plt.show()


import numpy as np
import numpy.linalg as la

def angle(vector1, vector2):
    """ Returns the angle in radians between vectors 'vector1' and 'vector2', works also good for very small values  """
    cos_angle = np.dot(vector1, vector2)
    sin_angle = la.norm(np.cross(vector1, vector2))

    angle_in_radians = np.arctan2(sin_angle, cos_angle)

    return angle_in_radians


# (c) Dan La Russa & Randy Taylor
# routines for fitting histograms

import numpy as np
import scipy.special as sps

MAX_NEWTON_ITERATIONS = 1000

def gauss_fit(data, binwidth=None):
    """
    Fits a Gaussian pdf to a set of independent values (data) using
    maximum likelihood estimators. If fitting to a histogram, the
    resulting fit can be normalized if the binwidth is also supplied.
    """
    if binwidth is None:
        norm = 1
    else:
        norm = np.float(np.size(data) * binwidth)
    mean = np.mean(data)
    std = np.std(data)

    optParam = norm, mean, std

    return optParam

def gamma_fit(data, binwidth):
    """
    Fits a Gamma pdf to independent values (data) using
    maximum likelihood estimators.
    """
    if binwidth is None:
        norm = 1
    else:
        norm = np.float(np.size(data) * binwidth)

    # for large k where the Gaussian distribution is approached,
    # k = np.power(np.mean(data), 2.) / np.power(np.std(data), 2.)

    # In general, k can be approxmated to within 1.5% as
    s = np.log(np.mean(data)) - np.mean(np.log(data))

    kguess = (3. - s + np.sqrt((s-3)**2 + 24*s)) / (12*s)
    # k = kguess  # "accurate to within 1.5%" according to wikipedia.org

    # We can solve for k numerically using Newton's method.
    # Scipy.special has the digamma (psi) and its derivative (polygamma)
    # required for this.
    k = k_param(kguess, s)

    theta = np.mean(data)/k

    optParam = norm, k, theta

    return optParam

def k_param(kguess, s):
    """
    Finds the root of the maximum likelihood estimator
    for k using Newton's method. Routines for using Newton's method
    exist within the scipy package but they were not explored. This
    function is sufficiently well behaved such that we should not
    have problems solving for k, especially since we have a good
    estimate of k to use as a starting point.
    """
    k = kguess
    val = np.log(k) - sps.psi(k) - s
    counter = 0
    while np.abs(val) >= 0.0001:
        k = k - (np.log(k)-sps.psi(k)-s)/(1/k-sps.polygamma(1, k))
        val = np.log(k) - sps.psi(k) - s
        # sps.polygamma(1,k) is first derivative of sps.psi(k)

        counter += 1
        if counter > MAX_NEWTON_ITERATIONS:
            raise Exception("Max Newton's method iterations exceeded")

    return k

def gamma_pdf(x, norm, k, theta):
    """
    This method calculates the Gamma probability density function
    for a variable/array x with a given shape parameter (k) and scale
    parameter (theta). The results is normalized (multiplied by) 'norm',
    and so 'norm' should equal 1.000 unless you have a reason for it
    to be otherwise.
    """
    GammaPdf = norm * np.power(x, k-1.0) * np.exp(-x/theta) / (sps.gamma(k) * np.power(theta, k))
    return GammaPdf


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np

class DualNumber(object):
    def __init__(self,x=0.0,x_d=0.0):
        self.x = x
        self.x_d = x_d

    def __repr__(self):
        return 'DualNumber({},{})'.format(self.x,self.x_d)

    # overload operators:

    # self + other
    def __add__(self,other):
        if isinstance(other,DualNumber):
            return DualNumber(self.x + other.x, self.x_d + other.x_d)
        return DualNumber(self.x + other, self.x_d)

    # other + self
    def __radd__(self,other):
        return self.__add__(other)

    # self - other
    def __sub__(self,other):
        if isinstance(other,DualNumber):
            return DualNumber(self.x - other.x, self.x_d - other.x_d)
        return DualNumber(self.x - other, self.x_d)

    # other - self
    def __rsub__(self,other):
        if isinstance(other,DualNumber):
            return DualNumber(other.x - self.x, other.x_d - self.x_d)
        return DualNumber(other - self.x, -self.x_d)

    # self * other
    def __mul__(self,other):
        if isinstance(other,DualNumber):
            return DualNumber(self.x * other.x, self.x*other.x_d + self.x_d*other.x)
        return DualNumber(self.x * other, self.x_d * other)

    # other * self
    def __rmul__(self,other):
        return self.__mul__(other)

    # self / other
    def __truediv__(self,other):
        if isinstance(other,DualNumber):
            xsq = other.x*other.x
            return DualNumber( self.x / other.x, (self.x_d*other.x - self.x*other.x_d)/xsq)
        return DualNumber(self.x / other, self.x_d / other)

    # other / self
    def __rtruediv__(self,other):
        if isinstance(other,DualNumber):
             xsq = self.x*self.x
             return DualNumber( other.x / self.x, (other.x_d*self.x - other.x*self.x_d)/xsq)
        xsq = self.x*self.x
        return DualNumber(other/self.x , -other*self.x_d/xsq)

    # self ** other
    def __pow__(self,other):
        if isinstance(other,int) or isinstance(other,float):
            return DualNumber(self.x ** other, other * self.x**(other-1) * self.x_d )
        raise TypeError('unsupported operand type(s) for ** or pow(): \'DualNumber\' and \'{}\''.format(type(other)))

    # self > other
    def __gt__(self,other):
        if isinstance(other,DualNumber):
            return self.x > other.x
        return self.x > other

    # self < other
    def __lt__(self,other):
        if isinstance(other,DualNumber):
            return self.x < other.x
        return self.x < other

    # overload some mathematical functions

    def exp(self):
        tmp = np.exp(self.x)
        return DualNumber(tmp, tmp*self.x_d)

if __name__=='__main__':
    # example:

    def f(x):
        return 2*(x-1)**2 + 3

    x = DualNumber(4,1)
    print('f({}) = {}'.format(x,f(x)))


import matplotlib.pyplot as plt #pip install matplotlib
import math
import numpy as np #pip install numpy
import statistics

u=[] #to fill with 100000 values from linear congruence generator
def gen_lincong_foru():
    #X(i+1)=X(i)A mod M and U(i+1)=X(i+1)/M -> Linear Congruence Generator
    x=23 # x0 value
    m=4096
    a=17
    for i in range(0,100000):
        u.append(x/m)
        t=(a*x+1)%m #b=1
        x=t

x=[] #to get x from the inverse of arcsine distribution
def gen_xfromu():
    #X=1/2 - 1/2*(cos(u*pi)) -> Inverse of arcsine distribution
    for i in range(0,100000):
        t=1/2-(1/2*math.cos(u[i]*math.pi))
        x.append(t)

fx=[]
def gen_fx_fromx():
    #F(x)=(2/pi)*arcsin(sqrt(x)) -> Arcsine distribution
    for i in range(0,100000):
        t=(2*np.arcsin(math.sqrt(x[i])))/(math.pi)
        fx.append(t)

def plot_xvsu(l):
    #take 0.05 interval length and plot X vs [0,1]
    x_cor=[] #will have the midpoint of that interval
    y_cor=[] #will have the freq of x belonging to that interval
    ini=0
    freq=0
    while(ini<=1):
        x_cor.append(ini+0.05/2)
        for i in range(0,len(l)):
            if(l[i]>=ini and l[i]<=ini+0.05):
                freq=freq+1
        y_cor.append(freq)
        ini+=0.05
    s_mean='Sample mean= '+str(np.mean(l)) #sample mean
    s_var='Sample variance= '+str(statistics.variance(l)) #sample var
    plt.plot(x_cor,y_cor)
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.title(s_mean+'\n'+s_var+'\nDistribution function for '+str(len(l))+' generated values of X')
    plt.show()

def plot_xvsu_fr(l):
    #take 0.05 interval length and plot X vs [0,5]
    x_cor=[] #will have the midpoint of that interval
    y_cor=[] #will have the freq of x belonging to that interval
    ini=0
    while(ini<=1):
        freq=0
        x_cor.append(ini+0.05/2)
        for i in range(0,len(l)):
            if(l[i]>=ini and l[i]<=ini+0.05):
                freq=freq+1
        y_cor.append(freq)
        ini+=0.05
    plt.plot(x_cor,y_cor)
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.title('Frequency vs Interval for '+str(len(l))+' generated values of X')
    plt.show()

def plot_fxvsx(no_values):
    x_cor=[] #will have the value of x generated
    y_cor=[] #will have the corresponding value of fx
    for i in range(0,no_values):
        x_cor.append(x[i])
        y_cor.append(fx[i])
    plt.scatter(x_cor,y_cor)
    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.title('Distribution function f(x) '+str(no_values)+' generated values of X')
    plt.show()

gen_lincong_foru()
gen_xfromu()
gen_fx_fromx()
plot_xvsu(x[0:100])
plot_xvsu_fr(x[0:100])
plot_fxvsx(100)
plot_xvsu(x[0:1000])
plot_xvsu_fr(x[0:1000])
plot_fxvsx(1000)
plot_xvsu(x[0:10000])
plot_xvsu_fr(x[0:10000])
plot_fxvsx(10000)
plot_xvsu(x[0:100000])
plot_xvsu_fr(x[0:100000])
plot_fxvsx(100000)

Semester 5/MA 323 (Monte Carlo)/Lab Submissions/Lab 2/180123062_ABSatyaprakash_q3.py


import numpy as np

def central_difference(x, y):
    """
    Compute the derivative of y with respect to x via central difference.

    Parameters
    ----------
    x : np.ndarray
        Array of x values
    y : np.ndarray
        Array of y values

    Returns
    -------
    Tuple containing x values and corresponding y derivatives.

    """

    dydx = ((np.roll(y, -1) - np.roll(y, 1)) \
            / (np.roll(x, -1) - np.roll(x, 1)))[1:-1]

    return x[1:-1], dydx


"""
Code is from https://pvigier.github.io/2018/06/13/perlin-noise-numpy.html
(https://github.com/pvigier/perlin-numpy/blob/master/perlin_numpy/perlin2d.py)
"""
from typing import Tuple, Callable, Optional

import numpy as np

def interpolate_linear(t):
    return t

def interpolate_poly2(t):
    return t*(3. - 2. * t)

def interpolate_poly5(t):
    return t*t*t*(t*(t*6 - 15) + 10)

def generate_perlin_noise_2d(
        shape: Tuple[int, int],
        res: Tuple[int, int],
        tileable: Tuple[bool, bool] = (False, False),
        interpolant: Callable[[np.ndarray], np.ndarray] = interpolate_poly5,
        rng: Optional[np.random.Generator] = None,
        values: Optional[np.ndarray] = None,
):
    """Generate a 2D numpy array of perlin noise.
    Args:
        shape: The shape of the generated array (tuple of two ints).
            This must be a multple of res.
        res: The number of periods of noise to generate along each
            axis (tuple of two ints). Note shape must be a multiple of
            res.
        tileable: If the noise should be tileable along each axis
            (tuple of two bools). Defaults to (False, False).
        interpolant: The interpolation function, defaults to
            t*t*t*(t*(t*6 - 15) + 10).
        rng: optional numpy.random.Generator to use
        values: optional ndarray with initial random values,
            defaults to rng.random([res[0] + 1, res[1] + 1])
    Returns:
        A numpy array of shape shape with the generated noise.
    Raises:
        ValueError: If shape is not a multiple of res.
    """
    if rng is None:
        rng = np.random
    if values is None:
        values = rng.random([res[0]+1, res[1]+1])
    else:
        assert values.shape == (res[0]+1, res[1]+1), \
            f"expected {(res[0]+1, res[1]+1)}, got {values.shape}"

    delta = (res[0] / shape[0], res[1] / shape[1])
    d = (shape[0] // res[0], shape[1] // res[1])
    grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]] \
               .transpose(1, 2, 0) % 1
    # Gradients
    angles = 2*np.pi*values
    gradients = np.dstack((np.cos(angles), np.sin(angles)))
    if tileable[0]:
        gradients[-1,:] = gradients[0,:]
    if tileable[1]:
        gradients[:,-1] = gradients[:,0]
    gradients = gradients.repeat(d[0], 0).repeat(d[1], 1)
    g00 = gradients[    :-d[0],    :-d[1]]
    g10 = gradients[d[0]:     ,    :-d[1]]
    g01 = gradients[    :-d[0],d[1]:     ]
    g11 = gradients[d[0]:     ,d[1]:     ]
    # Ramps
    n00 = np.sum(np.dstack((grid[:,:,0]  , grid[:,:,1]  )) * g00, 2)
    n10 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]  )) * g10, 2)
    n01 = np.sum(np.dstack((grid[:,:,0]  , grid[:,:,1]-1)) * g01, 2)
    n11 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]-1)) * g11, 2)
    # Interpolation
    t = interpolant(grid)
    n0 = n00*(1-t[:,:,0]) + t[:,:,0]*n10
    n1 = n01*(1-t[:,:,0]) + t[:,:,0]*n11
    return np.sqrt(2)*((1-t[:,:,1])*n0 + t[:,:,1]*n1)


from json_to_dict import constants
import numpy as np
from astropy import units as u
pi = np.pi
u.sday = u.def_unit('sday', 86164.1004*u.s) #redefine sday in astropy to match the one from Wertz

def get_a0(j_k, mu=constants["muEarth"], DS=constants["DS"], return_unit=(u.km)):
    frac = (DS/(2*pi)*j_k**-1)**(2/3)
    a0 = frac * mu**(1/3)
    return a0.to(return_unit)

def get_Omegadot(a, i, e=0, J2=constants["J2"], mu=constants["muEarth"], R=constants["RE"], DS=constants["DS"], return_unit=(u.deg/u.sday)):
    k2 = (0.75 * J2 * mu**0.5 * R**2)
    #calulate a working version of Omegadot
    Omegadot_temp = -2* k2 * a**(-7/2) * np.cos(i) * (1 - e**2)**(-2)
    #convert to proper units, and take value
    Omegadot_temp = (Omegadot_temp.to(u.s**-1)).value
    #convert to deg/sday
    Omegadot = (Omegadot_temp * 180/pi * DS.value)*(u.deg/u.sday)
    return Omegadot.to(return_unit)

def get_omegadot(a, i, e=0, J2=constants["J2"], mu=constants["muEarth"], R=constants["RE"], DS=constants["DS"], return_unit=(u.deg/u.sday)):
    k2 = (0.75 * J2 * mu**0.5 * R**2)

    #calulate a working version of Omegadot
    omegadot_temp = k2 * a**(-7/2) * (5*(np.cos(i))**2 - 1)*(1-e**2)**(-2)
    #convert to proper units, and take value
    omegadot_temp = (omegadot_temp.to(u.s**-1)).value
    #convert to deg/sday
    omegadot = (omegadot_temp * 180/pi * DS.value)*(u.deg/u.sday)
    return omegadot.to(return_unit)

def get_Mdot(a, i, e=0, J2=constants["J2"], mu=constants["muEarth"], R=constants["RE"], DS=constants["DS"], return_unit=(u.deg/u.sday)):
    k2 = (0.75 * J2 * mu**0.5 * R**2)
    #calulate a working version of Omegadot
    Mdot_temp = k2 * a**(-7/2) * (3*(np.cos(i))**2 - 1)*(1-e**2)**(-3/2)
    #convert to proper units, and take value
    Mdot_temp = (Mdot_temp.to(u.s**-1)).value
    #convert to deg/sday
    Mdot = (Mdot_temp * 180/pi * DS.value)*(u.deg/u.sday)
    return Mdot.to(return_unit)

def get_n(j_k, Omegadot, omegadot=None, Mdot=None, Ldot=constants["Ldot"], return_unit=(u.deg/u.sday), approach='1'):
    if approach == '1':
        n = j_k * (Ldot - Omegadot)
    elif approach == '2':
        n = j_k*(Ldot -Omegadot) - (omegadot + Mdot)
    return n.to(return_unit)

def get_a(n, mu=constants["muEarth"], DS=constants["DS"], return_unit=(u.km)):
    #make sure everything is in units we want, and take value
    mu_val = (mu.to(u.km**3/(u.s**2))).value
    n_val = (n.to(u.deg/u.sday)).value
    DS_val = (DS.to(u.s)).value
    #perform calculation
    bottom = ((n_val * pi) / (DS_val * 180)) ** 2
    a_val = (mu_val/bottom)**(1/3)
    a = a_val*(u.km)
    return a.to(return_unit)

def do_a_iterations(j_k, i, e=0, da_precision=5, return_its=False, approach='1'):
    a0 = get_a0(j_k)
    iterations=0
    running=True
    while running:
        #calc the components
        Omegadot = get_Omegadot(a0,i)
        if approach=='1':
            n = get_n(j_k, Omegadot, approach=approach)
        elif approach=='2':
            omegadot = get_omegadot(a0,i)
            Mdot = get_Mdot(a0,i)
            n = get_n(j_k, Omegadot, omegadot=omegadot, Mdot=Mdot, approach=approach)
        a_new = get_a(n)
        #find difference
        da = a_new - a0

        a0 = a_new
        iterations += 1
        #set convergence condition as the precision level given
        da_rounded = round(da.value, da_precision)
        if da_rounded == 0:
            running = False

    if return_its: return a_new, iterations
    else: return a_new


import random

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
from numpy.linalg import inv

np.random.seed(0)

plt.style.use('seaborn')
error = 0.10 #10 percent

minx, maxx = -2, 2
miny, maxy = -3, 3
# stn_locs = [[-2,3,0],[1,3,0],[-2,-1,0],[0,-3,0],[2,-2,0]]
numstations = 30
stn_locs=[]
xvals = minx+(maxx-minx)*np.random.rand(numstations)
yvals = miny+(maxy-miny)*np.random.rand(numstations)

for num in range(numstations):
    stn_locs.append([xvals[num],yvals[num],0])

eq_loc = [2,2,-2]
vel = 6 #kmps
origintime = 0

def calc_arrival_time(eq_loc, stnx, stny, stnz, vel, origintime):
    eqx, eqy, eqz = eq_loc
    dist = np.sqrt((eqx - stnx)**2 + (eqy - stny)**2 + (eqz - stnz)**2)
    arr = dist/vel + origintime
    return arr

def G_matrix_cols(eq_loc, stnx, stny, stnz, vel, origintime):
    eqx, eqy, eqz = eq_loc
    G_col1 = (eqx-stnx)/vel * ((eqx - stnx)**2 + (eqy - stny)**2 + (eqz - stnz)**2)**(-1/2)
    G_col2 = (eqy-stny)/vel * ((eqx - stnx)**2 + (eqy - stny)**2 + (eqz - stnz)**2)**(-1/2)
    G_col3 = (eqz-stnz)/vel * ((eqx - stnx)**2 + (eqy - stny)**2 + (eqz - stnz)**2)**(-1/2)
    G_col4 = -1/vel**2 * ((eqx - stnx)**2 + (eqy - stny)**2 + (eqz - stnz)**2)**(1/2)
    G_col5 = 1
    return [G_col1,G_col2,G_col3,G_col4,G_col5]

eq_initial_guess = [eq_loc[0]+error*eq_loc[0],eq_loc[1]+error*eq_loc[1],eq_loc[2]+error*eq_loc[2]]
m_intial = [eq_initial_guess[0],eq_initial_guess[1],eq_initial_guess[2],vel+error*vel,origintime+error*origintime]

G_matrix = []
d_obs = []
d_pre = []
noise_level_data = 0.1
# noise_level_data = 0.001
for stnx, stny, stnz in stn_locs:
    # print("{:.2f} {:.2f} {:d}".format(stnx, stny, stnz))
    arr = calc_arrival_time(eq_loc, stnx, stny, stnz, vel, origintime)
    G_matrix.append(G_matrix_cols(eq_initial_guess, stnx, stny, stnz, m_intial[3], m_intial[4]))
    sign = np.random.choice([-1,1])
    d_obs.append(arr+sign*noise_level_data*arr)
    print("Arrival time at ({},{},{}) is {}".format(stnx, stny, stnz, arr+sign*noise_level_data*arr))
    # print("{:.2f}".format(arr+0.05*arr))
    d_pre.append(calc_arrival_time(eq_initial_guess, stnx, stny, stnz, m_intial[3], m_intial[4]))

d_obs = np.array(d_obs)
d_pre = np.array(d_pre)
G_matrix = np.array(G_matrix)

delm = inv(np.transpose(G_matrix).dot(G_matrix)).dot(np.transpose(G_matrix)).dot(d_obs-d_pre)
inv_model = m_intial+delm

print("Actual model EQ loc: ({:.2f},{:.2f},{:.2f}), vel: {:.2f} and origin time: {:.2f}".format(eq_loc[0],eq_loc[1],eq_loc[2],vel,origintime))
print("Initial model EQ loc: ({:.2f},{:.2f},{:.2f}), vel: {:.2f} and origin time: {:.2f}".format(m_intial[0],m_intial[1],m_intial[2],m_intial[3],m_intial[4]))
print("Inverted model EQ loc: ({:.2f},{:.2f},{:.2f}), vel: {:.2f} and origin time: {:.2f}".format(inv_model[0],inv_model[1],inv_model[2],inv_model[3],inv_model[4]))

squared_error = np.sum((inv_model-m_intial)**2)

## to create the surface
X = np.linspace(-3, 3, 200)
Y = np.linspace(-3, 3, 200)
X, Y = np.meshgrid(X, Y)
Z = (X**2 + Y**2)*0

fig = plt.figure()
ax = plt.axes(projection='3d')
# plot stations
ax.scatter([x[0] for x in stn_locs],[x[1] for x in stn_locs],[x[2] for x in stn_locs],c='b',marker='^',s=50)
# plot surface
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet,linewidth=0, antialiased=False,alpha=0.1)
# plot actual EQ
ax.scatter(eq_loc[0],eq_loc[1],eq_loc[2],c='r',marker='*',s=100,label='Actual EQ location')
ax.scatter(eq_initial_guess[0],eq_initial_guess[1],eq_initial_guess[2],c='gray',marker='*',s=100,label='Initial Guess EQ location')
ax.scatter(inv_model[0],inv_model[1],inv_model[2],c='k',marker='*',s=100,label='Inverted EQ location')
plt.title("Inverted model EQ loc: ({:.2f},{:.2f},{:.2f}),\nvel: {:.2f} and origin time: {:.2f}\nsq_error: {:.2f}".format(inv_model[0],inv_model[1],inv_model[2],inv_model[3],inv_model[4],squared_error),fontsize=8)
ax.set_xlim([-3,3])
ax.set_ylim([-4,4])
ax.set_zlim(-3,0.1)
plt.legend()

plt.savefig('Earthquake_loc_{:.0f}stn_{:.0f}percent_error.png'.format(numstations,error*100),bbox_inches='tight',dpi=300)
# plt.savefig('Earthquake_loc_5stn_30percent_error.png',bbox_inches='tight',dpi=300)
plt.close('all')


import numpy as np
from tqdm import tqdm
import math

# calculating simple interest
def simple_interest(p,r,t):
    print('Principal Amount :',p)
    print('Rate of interest :',r)
    print('Time period :',t)
    si = (p*t*r)/100
    a = p+si
    print('final amount :',a)
    print('simple interest',si)

#calculating compound interest
def compound_interest(p,r,t):
    print('Principal Amount :',p)
    print('Rate of interest :',r)
    print('Time period :',t)
    a = p*((1+r/100)**t)
    ci = a-p
    print("final amount :",a)
    print("compound interest :",ci)

# purchasing power (Inflation)
def pur_power(p,r,t):
   print('Principal Amount :',p)
   print('Rate of interest :',r)
   print('Time period :',t)
   a = p*((100/(100+r))**t)
   print('after ',t,' years, the final amount is ',a)

# compound annual growth rate
def CAGR(p,a,t):
   print('Initial amount :',p)
   print('Final amount :',a)
   print('Time period :',t)
   t_inv = 1/t
   cagr_rate = (((a/p)**t_inv)-1)*100
   print('compound annual growth rate :',cagr_rate)

# monthly EMI calculation
def EMI(p,r,n):
    print("Total Loan Amount: ",p)
    print("Rate of Interest: ",r)
    print("Number of installments: ",n)
    r_mon = r/(12*100)
    emi = p*r_mon*((1+r_mon)**n)/((1+r_mon)**n -1)
    print("EMI for ",n," months :",emi)

# double time calculation
def inv_double(r):
    print("rate of interest :",r)
    t= math.log(2)/math.log(1+(r/100))
    print("time taken in years to double money at ",r," percent PA : ",t)

# weighted average
ratio=[0.20, 0.25, 0.35,0.10, 0.10]
rates=[7.5, 8.5, 8, 5, 6]
def weighted_average(ratio,rates):
    wa=0
    for i in range(len(ratio)):
        wa= wa+ ratio[i]*rates[i]
    print("Weighted Average returns: ",wa)

python projects/basic financial  functions/main.py


# -*- coding: utf-8 -*-
"""
Created on Tue Sep  4 20:53:17 2018

@author: chen jin

Introduction: simple implementation of normal equation solving linear model
    i.e. y = theta0 + theta1* x1 + ...thetan * Xn = Theta * X
    According to normal equation: Theta = inv(X.T * X) * X.T * y

    Args:
        X, y reading input from txt file
        estimated Theta: theta_best

    Returns:
        estimated model with theta_best
"""

# Modules
import numpy as np

# Loading data set
y, X = np.loadtxt('data\cricket_chirps_versus_temperature.txt', delimiter=',', unpack=True)

def normalEquation(X, y):
    # number of examples n
    n = len(y)
    theta = []

    # add bias to X
    X_b = np.c_[np.ones((n, 1)), X]

    # Normal Equation:
    # theta = inv(X^T * X) * X^T * y
    # Calculating theta
    theta = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
    return theta

theta_best = normalEquation(X, y)
print('estimated model: y = %.3f + %.3f * x1' % (theta_best[0], theta_best[1]))


import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')
from matplotlib import pyplot as plt

import numpy as np
import scipy as sp
from scipy import misc
import pyfftw
from helperCode import hamming

def FFT2(A):
    B = pyfftw.interfaces.scipy_fftpack.fft(A, axis=0)
    return pyfftw.interfaces.scipy_fftpack.fft(B, axis=1)

def melScale(f):
    return 2595*np.log10(1+f/700)

def powerCepstrum():
    '''
    Generate plots of the steps in creating the power Cepstrum.
    '''
    d = np.linspace(0,4*np.pi, 200)
    s = np.sin(d)+.1*np.sin(12*d)+.1*np.cos(12*d)**2 + .1*np.sin(18*d)**2
    ax = plt.subplot(221)
    ax.plot(s)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('Original Signal')
    ax = plt.subplot(222)
    fs = pyfftw.interfaces.scipy_fftpack.fft(s)
    ax.plot(fs[:len(fs)/2])
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('Fourier Transform')
    fs[np.abs(fs)<1e-100] = 1e-100
    lfs = np.log(np.abs(fs)**2)
    ax = plt.subplot(223)
    ax.plot(lfs[:len(lfs)/2])
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('Squared Log')
    pc = pyfftw.interfaces.scipy_fftpack.ifft(lfs)
    ax = plt.subplot(224)
    ax.plot(pc[1:len(pc)/2])
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('Power Cepstrum')
    plt.savefig('PowerCepstrum.pdf')
    plt.clf()

def hammingWindow():
    '''
    Generate plot of a Hamming window and windowed signal.
    '''
    d = np.linspace(0,4*np.pi, 200)
    h = 200*hamming(200)
    sig = 100*np.sin(3*d)
    windowed = sig*h/200
    ax = plt.subplot(131, aspect = 'equal')
    ax.plot(h)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('Hamming Window Function')
    ax = plt.subplot(132, aspect = 'equal')
    ax.plot(sig)
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_autoscale_on(True)
    plt.xlabel('Original Signal')
    ax = plt.subplot(133, aspect = 'equal')
    ax.plot(windowed)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('Windowed Signal')
    plt.savefig('Hamming.pdf')
    plt.clf()

def plotMelScale():
    '''
    Generate plot of the mel scale and a traditional mel filterbank.
    '''
    dom = np.linspace(0,10000, 1000)
    ms = melScale(dom)
    plt.subplot(211)
    plt.subplots_adjust(hspace=.5)
    plt.plot(dom, ms, 'm', lw = 5)
    plt.title('Mel Scale vs. Hz Scale')
    plt.xlim([0,10000])
    plt.ylim([0,4000])
    plt.grid(b=True, which='major', color='b', linestyle='--')
    plt.tick_params(labelsize=8)
    plt.xlabel('Herz scale')
    plt.ylabel('Mel scale')
    bins = 13
    hz_max = 8000
    mel_max = 2595*np.log10(1+hz_max/700.)
    mel_bins = np.linspace(0, mel_max, bins+2)
    hz_bins = 700*(10**(mel_bins/2595.)-1)
    l1 = np.zeros(hz_bins.shape)
    l2 = np.zeros(hz_bins.shape)
    l1[1::2] = 1
    l2[2:-1:2] = 1
    plt.subplot(212)
    plt.plot(hz_bins, l1, 'm', hz_bins, l2, 'm')
    plt.title('Mel Filterbank')
    plt.xlim([0,8000])
    plt.tick_params(labelsize=8)
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Magnitude')
    plt.savefig('melScale.pdf')
    plt.clf()

def plotFFT():
    '''
    Generate plots of the 2D FFT of the Lena image and another image.
    '''
    L = misc.lena()
    E = plt.imread('ecoli.jpg')[:512, :512]
    FL = FFT2(L)
    FE = FFT2(E)
    FLs =  pyfftw.interfaces.scipy_fftpack.fftshift(FL)
    FEs =  pyfftw.interfaces.scipy_fftpack.fftshift(FE)
    mag1 = np.abs(FLs)
    mag1 *= 1000/mag1.max()
    mag1[mag1 > 1] = 1
    mag2 = np.abs(FEs)
    mag2 *= 1000/mag2.max()
    mag2[mag2 > 1] = 1
    ax = plt.subplot(221)
    ax.imshow(L, plt.cm.Greys_r)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('Lena Image')
    ax = plt.subplot(222)
    ax.imshow(E, plt.cm.Greys_r)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('E. Coli Image')
    ax = plt.subplot(223)
    ax.imshow(mag1, plt.cm.Blues)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('FT of Lena')
    ax = plt.subplot(224)
    ax.imshow(mag2, plt.cm.Blues)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.xlabel('FT of E. Coli')
    plt.savefig('2dfft.pdf')
    plt.clf()

if __name__ == "__main__":
    powerCepstrum()
    hammingWindow()
    plotFFT()
    plotMelScale()


'''
Compare the ACF for Several AR Time Series

The autocorrelation function decays exponentially for an AR time series at a rate of the AR parameter. For example, if the AR parameter, ϕ=+0.9
ϕ
=
+
0.9
, the first-lag autocorrelation will be 0.9, the second-lag will be (0.9)2=0.81
(
0.9
)
2
=
0.81
, the third-lag will be (0.9)3=0.729
(
0.9
)
3
=
0.729
, etc. A smaller AR parameter will have a steeper decay, and for a negative AR parameter, say -0.9, the decay will flip signs, so the first-lag autocorrelation will be -0.9, the second-lag will be (−0.9)2=0.81
(
−
0.9
)
2
=
0.81
, the third-lag will be (−0.9)3=−0.729
(
−
0.9
)
3
=
−
0.729
, etc.

The object simulated_data_1 is the simulated time series with an AR parameter of +0.9, simulated_data_2 is for an AR paramter of -0.9, and simulated_data_3 is for an AR parameter of 0.3

INSTRUCTIONS
100XP
Compute the autocorrelation function for each of the three simulated datasets using the plot_acf function with 20 lags (and supress the confidence intervals by setting alpha=1).
'''
# Import the plot_acf module from statsmodels
from statsmodels.graphics.tsaplots import plot_acf

# Plot 1: AR parameter = +0.9
plot_acf(simulated_data_1, alpha=1, lags=20)
plt.show()

# Plot 2: AR parameter = -0.9
plot_acf(simulated_data_2, alpha=1, lags=20)
plt.show()

# Plot 3: AR parameter = +0.3
plot_acf(simulated_data_3, alpha=1, lags=20)
plt.show()


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

from matplotlib import gridspec
from scipy.optimize import curve_fit
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import explained_variance_score
from sklearn.metrics import r2_score

# --------------------------------------------------------------------------- Approximations

def g(x):
    """``Benchmark function.``

    Args:
        x (int, float): Input.

    Returns:
        float: Output. :math:`sin(\\frac{1}{2}x) - 2 cos(2x)`
    """
    return np.sin(0.5 * x) - 2 * np.cos(2 * x)

def five_interp(x, a0, a1, a2, a3, a4):
    """``Approximation degree = 5``
    """
    return a0 + a1 * x + a2 * (x ** 2) + a3 * (x ** 3) + a4 * (x ** 4)

def six_interp(x, a0, a1, a2, a3, a4, a5):
    """``Approximation degree = 6``
    """
    return a0 + a1 * x + a2 * (x ** 2) + a3 * (x ** 3) + a4 * (x ** 4) + a5 * (x ** 5)

def seven_interp(x, a0, a1, a2, a3, a4, a5, a6):
    """``Approximation degree = 7``
    """
    return (
            a0
            + a1 * x
            + a2 * (x ** 2)
            + a3 * (x ** 3)
            + a4 * (x ** 4)
            + a5 * (x ** 5)
            + a6 * (x ** 6)
    )

def eight_interp(x, a0, a1, a2, a3, a4, a5, a6, a7):
    """``Approximation degree = 8``
    """
    return (
            a0
            + a1 * x
            + a2 * (x ** 2)
            + a3 * (x ** 3)
            + a4 * (x ** 4)
            + a5 * (x ** 5)
            + a6 * (x ** 6)
            + a7 * (x ** 7)
    )

def nine_interp(x, a0, a1, a2, a3, a4, a5, a6, a7, a8):
    """``Approximation degree = 9``
    """
    return (
            a0
            + a1 * x
            + a2 * (x ** 2)
            + a3 * (x ** 3)
            + a4 * (x ** 4)
            + a5 * (x ** 5)
            + a6 * (x ** 6)
            + a7 * (x ** 7)
            + a8 * (x ** 8)
    )

def ten_interp(x, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9):
    """``Approximation degree = 10``
    """
    return (
            a0
            + a1 * x
            + a2 * (x ** 2)
            + a3 * (x ** 3)
            + a4 * (x ** 4)
            + a5 * (x ** 5)
            + a6 * (x ** 6)
            + a7 * (x ** 7)
            + a8 * (x ** 8)
            + a9 * (x ** 9)
    )

# --------------------------------------------------------------------------- 3.2.1 Benchmark Exercise: Naive Approximation SciPy

class FCMethod:
    """This object uses the scipy ``curve fit`` method to naively approximate a specific
    function. It plots the interpolation and the real function for different nodes and
    tables the approximation accuracy.

    Args:
        a (int): Lower bound of interval.
        b (int): Upper bound of interval.
        n (int): Number of interpolation nodes.
        func (function): Benchmark function.
        degree (int): Degree of approximation.
    """

    def __init__(self, a, b, n, func, degree):
        """Constructor method. It uses the exact same arguments as the :class:`PMethod`
        constructor method. This could be achieved by inheritance as well.
        """
        self.a = a
        self.b = b
        self.n = n
        self.func = func
        self.degree = degree

    def check_degree(self, increase):
        """Increases approximation degree if degree lies between 5 and 10.

        Args:
            increase (bool): Increases approximation degree by one unit if True.
        """
        if increase == True and self.degree < 10:
            self.degree += 1
        elif self.degree < 5 or self.degree > 10:
            print("Degree must be between 5 and 10!")

    def choose_approx(self):
        """Chooses approximation function by its degree.
        """
        if self.degree == 5:
            self.approx = five_interp
        elif self.degree == 6:
            self.approx = six_interp
        elif self.degree == 7:
            self.approx = seven_interp
        elif self.degree == 8:
            self.approx = eight_interp
        elif self.degree == 9:
            self.approx = nine_interp
        elif self.degree == 10:
            self.approx = ten_interp

    def fit_curve(self):
        """Implementation of scipy curve fit method. Uses least square method to find
        optimal weight. This yield to the interpolation.
        """
        self.xa = np.linspace(self.a, self.b, self.n)
        self.xb = np.linspace(self.a, self.b, 3 * self.n)
        self.xc = np.linspace(self.a, self.b, 9 * self.n)

        self.popta = curve_fit(self.approx, self.xa, self.func(self.xa))[0]
        self.poptb = curve_fit(self.approx, self.xb, self.func(self.xb))[0]
        self.poptc = curve_fit(self.approx, self.xc, self.func(self.xc))[0]

    def plot_naive_interp(self, N, fs, number_1, number_2):
        """Plots true function and approximation as well as approximation error.

        Args:
            N (int): Number of evaluation nodes.
            fs (tuple): Figuresize
            number_1 (int, float): Number of first figure.
            number_2 (int, float: Number of second figure.
        """
        self.x = np.linspace(self.a, self.b, N)
        fig = plt.figure(figsize=fs)
        gs = gridspec.GridSpec(2, 1, height_ratios=[1.8, 1])
        ax0 = plt.subplot(gs[0])
        ax0.plot(self.x, self.func(self.x), label="Real Function")
        ax0.plot(
            self.x,
            self.approx(self.x, *self.popta),
            label=str(self.n) + " Nodes Approximation",
        )
        ax0.plot(
            self.x,
            self.approx(self.x, *self.poptb),
            label=str(3 * self.n) + " Nodes Approximation",
        )
        ax0.plot(
            self.x,
            self.approx(self.x, *self.poptc),
            label=str(9 * self.n) + " Nodes Approximation",
        )
        ax0.set_title(
            f"Figure {number_1}: Naive Approximation Output "
            + str(self.degree)
            + " Degree"
        )
        plt.grid()
        plt.legend(
            title="Naive Approximation for different Nodes",
            bbox_to_anchor=(1.04, 0.5),
            loc="center left",
            shadow=True,
            fancybox=True,
            borderaxespad=0,
            title_fontsize=12,
        )

        plt.setp(ax0.get_xticklabels(), visible=False)

        ax1 = plt.subplot(gs[1], sharex=ax0)
        ax1.plot(
            self.x,
            self.approx(self.x, *self.popta) - self.func(self.x),
            label=str(self.n) + " Nodes Error",
        )
        ax1.plot(
            self.x,
            self.approx(self.x, *self.poptb) - self.func(self.x),
            label=str(3 * self.n) + " Nodes Error",
        )
        ax1.plot(
            self.x,
            self.approx(self.x, *self.poptc) - self.func(self.x),
            label=str(9 * self.n) + " Nodes Error",
        )
        ax1.set_title(
            f"Figure {number_2}: Naive Approximation Error "
            + str(self.degree)
            + " Degree"
        )
        plt.subplots_adjust(hspace=0.0)
        plt.grid()
        plt.legend(
            title="Error for different Nodes",
            bbox_to_anchor=(1.04, 0.5),
            loc="center left",
            shadow=True,
            fancybox=True,
            borderaxespad=0,
            title_fontsize=12,
        )

        plt.tight_layout()
        plt.show()

    def table_error(self, number):
        """Returns approximation accuracy.

        Args:
            number (int): Number of table.

        Returns:
            pd.DataFrame: Approximation accuracy.
        """
        mae = mean_absolute_error(self.approx(self.x, *self.popta), self.func(self.x))
        maea = mean_absolute_error(self.approx(self.x, *self.poptb), self.func(self.x))
        maeb = mean_absolute_error(self.approx(self.x, *self.poptc), self.func(self.x))

        mse = mean_squared_error(self.approx(self.x, *self.popta), self.func(self.x))
        msea = mean_squared_error(self.approx(self.x, *self.poptb), self.func(self.x))
        mseb = mean_squared_error(self.approx(self.x, *self.poptc), self.func(self.x))

        ev = explained_variance_score(
            self.approx(self.x, *self.popta), self.func(self.x)
        )
        eva = explained_variance_score(
            self.approx(self.x, *self.poptb), self.func(self.x)
        )
        evb = explained_variance_score(
            self.approx(self.x, *self.poptc), self.func(self.x)
        )

        r2 = r2_score(self.approx(self.x, *self.popta), self.func(self.x))
        r2a = r2_score(self.approx(self.x, *self.poptb), self.func(self.x))
        r2b = r2_score(self.approx(self.x, *self.poptc), self.func(self.x))

        df = pd.DataFrame(
            [mae, mse, ev, r2],
            index=[
                "Mean Squared Error",
                "Mean Absolute Error",
                "Explained Variance",
                "$R^2$ Score",
            ],
            columns=[str(self.n) + " Nodes"],
        )
        dfa = pd.DataFrame(
            [maea, msea, eva, r2a],
            index=[
                "Mean Squared Error",
                "Mean Absolute Error",
                "Explained Variance",
                "$R^2$ Score",
            ],
            columns=[str(3 * self.n) + " Nodes"],
        )
        dfb = pd.DataFrame(
            [maeb, mseb, evb, r2b],
            index=[
                "Mean Squared Error",
                "Mean Absolute Error",
                "Explained Variance",
                "$R^2$ Score",
            ],
            columns=[str(9 * self.n) + " Nodes"],
        )

        rslt = pd.concat([df, dfa, dfb], axis=1).style.set_caption(
            f"Table {number}: Accuracy of Naive Approximation for "
            + str(self.degree)
            + " Degrees"
        )
        return rslt


#!/usr/local/bin/python3

"""
	This is written by Zhiyang Ong to test concepts with enumerating
		a list using the range() function.
"""

print("======================================")
print("=	Start Here	=")

#	============================================================
"""
	Beginning from this portion, we examine list generation to create
		an ascending list of powers of two (or any other base number).
"""
print("")
print("+++	Create an ascending list of powers of two.")

for i in range(10):
	print("The index is:",i,"=")

print("======================================")
for i in [8,16,32,64,128,256,512,1024]:
	print("Current power of 2 is:",i,"=")

print("======================================")

"""
	Reference:
	+ Matt Gilson, Answer to "How to return a list of numbers of the
		power of 2?", Stack Exchange Inc., New York, NY, June 23, 2016.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions
			at: https://stackoverflow.com/a/13354695/1531728 and
			https://stackoverflow.com/questions/13354685/how-to-return-a-list-of-numbers-of-the-power-of-2/13354695#13354695;
			March 3, 2020 was the last accessed date.
	+ karakfa, Answer to "List comprehension of powers of 2 in Python
		fails with numpy array," Stack Exchange Inc., New York, NY,
		February 17, 2020.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/48843178/1531728 and
			https://stackoverflow.com/questions/48843102/list-comprehension-of-powers-of-2-in-python-fails-with-numpy-array/48843178#48843178;
			March 3, 2020 was the last accessed date.
	+ endolith, Answer to "For loop iterate over powers of 2,"
		Stack Exchange Inc., New York, NY, June 23, 2016.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions
			at: https://stackoverflow.com/a/37980195/1531728 and
			https://stackoverflow.com/questions/31872713/for-loop-iterate-over-powers-of-2/37980195#37980195;
			March 3, 2020 was the last accessed date.
	+ Jorick Spitzen, Answer to "Raising elements of a list to a power,"
		Stack Exchange Inc., New York, NY, May 19, 2015.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/30323523/1531728 and
			https://stackoverflow.com/questions/30323439/raising-elements-of-a-list-to-a-power/30323523#30323523;
			March 3, 2020 was the last accessed date.
	+ \cite[from Section 6 "Expressions", and subsection 6.5 "The power operator"]{DrakeJr2016a}
		- Reference for the "**" power operator.
		- That is, x**y = $x^{y}$ in LaTeX.
"""

powers_of_two = [ 2**power for power in range(3,10+1)]

for i in powers_of_two:
	print("> Currently enumerated power of 2 is:",i,"=")

print("- - - - - - - - - - - - - - - - - - -")

for i in [ 2**power for power in range(3,10+1)]:
	print("= Currently enumerated power of 2 is:",i,"=")

print("- - - - - - - - - - - - - - - - - - -")

"""
	This solution is acceptable, since I can customize this in a
		function call using variables.
"""
start_point = 3
end_point = 10
for i in [ 2**power for power in range(start_point,end_point+1)]:
	print("using, variables, currently enumerated power of 2 is:",i,"=")

print("- - - - - - - - - - - - - - - - - - -")

"""
	TO-DO!!! TO BE COMPLETED!!!
	+ Implement and test this in the miscellaneous module of the
		utilities package of my boilerplate code.

	This solution is acceptable, since I can customize this in a
		function call using variables.

	The left shift operation should be faster than the pow() function
		to determine the value of 2^n, for "n" ranges from "start_point"
		to "end_point".

	Reference for using the left shift operation instead of the pow()
		function to determine the value of 2^n, for "n" ranges from
		start_point to end_point:
	+ Robert Olveira, Answer to "How to return a list of numbers of the
		power of 2?", Stack Exchange Inc., New York, NY, August 28, 2017.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/41462973/1531728 and
			https://stackoverflow.com/questions/13354685/how-to-return-a-list-of-numbers-of-the-power-of-2/41462973#41462973;
			March 3, 2020 was the last accessed date.
"""

for i in [1 << i for i in range(start_point, end_point+1)]:
	print("Shifted 2's power of i is:",i,"=")

print("======================================")

"""
	Reference:
	endolith, Answer to "For loop iterate over powers of 2,"
		Stack Exchange Inc., New York, NY, June 23, 2016.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/37980195/1531728 and
			https://stackoverflow.com/questions/31872713/for-loop-iterate-over-powers-of-2/37980195#37980195;
			March 3, 2020 was the last accessed date.
"""

from itertools import count, takewhile
for x in takewhile(lambda x: x <= 1024, (2**p for p in count(1))):
    print("x via functional programming is:",x,"=")

print("======================================")

"""
	Reference:
	+ AndrewSmiley, Answer to "For loop iterate over powers of 2," Stack Exchange Inc., New York, NY, June 20, 2016.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions at: https://stackoverflow.com/a/37930871/1531728 and https://stackoverflow.com/questions/31872713/for-loop-iterate-over-powers-of-2/37930871#37930871;
			March 3, 2020 was the last accessed date.
	+ \cite[from section "Numeric and Mathematical Modules", subsection
		"math — Mathematical functions"]{DrakeJr2016b}
		- Available online from Python: The Python Standard Library:
			Numeric and Mathematical Modules: math — Mathematical functions at:
			https://docs.python.org/3/library/math.html#math.pow; March 2, 2020 was the last accessed date.
		- Also, see:
			* https://www.tutorialspoint.com/python/number_pow.htm
			* https://www.w3schools.com/python/ref_func_pow.asp
			* https://www.geeksforgeeks.org/pow-in-python/
"""

from math import log
from math import pow
import sys
#for i in map(lambda v : pow(2,v), range(0,int(log(sys.maxint, 2)))):
for i in map(lambda v : pow(2,v), range(0,int(log(sys.maxsize, 2)))):
	print("Power of two till log(sys.maxint, 2) is:",i,"=")

print("======================================")

"""
	Reference:
	Robert Calhoun, Answer to "For loop iterate over powers of 2,"
		Stack Exchange Inc., New York, NY, July 8, 2017.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/44988925/1531728 and
			https://stackoverflow.com/questions/30323439/raising-elements-of-a-list-to-a-power/44988925#44988925;
			March 3, 2020 was the last accessed date.
"""

numbers=[1,2,3,4]
powers_of_two=list(map(lambda x:pow(2,x),numbers))
print("powers_of_two are:",powers_of_two,"=")

print("- - - - - - - - - - - - - - - - - - -")

"""
	Reference:
	+ \cite{ParewaLabsStaff20XYd}
		- Parewa Labs staff, "Python Program To Display Powers of 2 Using
			Anonymous Function," from Programiz - Learn to Code for Free:
			Learn Python Programming: Python Examples, Parewa Labs Pvt. Ltd.,
			Kupondole, Lalitpur District, Province No. 3, Nepal, no date.
			Available online from Programiz - Learn to Code for Free:
				Learn Python Programming: Python Examples at:
				https://www.programiz.com/python-programming/examples/power-anonymous;
				March 3, 2020 was the last accessed date.
"""

"""
	range(10+1)
	=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
	=> [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
"""
powers_of_two=list(map(lambda x:pow(2,x),range(10+1)))
print("> powers_of_two are:",powers_of_two,"=")
"""
	range(1,10+1)
	=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
	=> [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
"""
powers_of_two=list(map(lambda x:pow(2,x),range(1,10+1)))
print(">> powers_of_two are:",powers_of_two,"=")

print("======================================")

"""
	Reference:
	+ Tommy Carstensen, "Raising elements of a list to a power",
		Stack Exchange Inc., New York, NY, May 23, 2017.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions at: https://stackoverflow.com/a/30324778/1531728 and https://stackoverflow.com/questions/30323439/raising-elements-of-a-list-to-a-power/30324778#30324778;
			March 3, 2020 was the last accessed date.
"""

import functools
bases = numbers = [1,2,3]
power = exponent = 3
cubed = list(map(functools.partial(pow, exponent), numbers))
print("cubed is:",cubed,"=")

print("======================================")

"""
	\cite["numpy.power", from section Routines, subsection (and subsubsection) Mathematical functions: Arithmetic operations]{TheSciPyCommunity2019c}
"""

import numpy as np
x1 = range(1,10+1)
x2 = np.power(2, x1)
print("x2 is:",x2,"=")
for index in x2:
	print("array element is:",index,"=")

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

"""
	Beginning from this portion, we examine list generation to create
		a list of ascending floating-point numbers with fixed increments.
"""

print("======================================")

print("")
print("+++	Create a list of ascending floating-point numbers with fixed increments.")

"""
	References for using the np.linspace() and np.arange() functions
		to generate a list of ascending floating-point numbers with
		fixed increments.

	Andrew Jaffe and Peter Mortensen and Cesar and
		"user2357112 supports Monica" and Brian Burns, Answer to
		"How to use a decimal range() step value?", Stack Exchange Inc.,
		New York, NY, February 10, 2020.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions at:
			https://stackoverflow.com/a/477635/1531728 and
			https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value/477635#477635;
			March 4, 2020 was the last accessed date.
"""
import numpy as np

a = np.linspace(0.0,1.0,11)
print("np.linspace(0,1,11) is:",a,"=")
a = np.linspace(4.0,5.0,10,endpoint=False)
print("np.linspace(0,1,10,endpoint=False) - without endpoints - is:",a,"=")
print("- - - - - - - - - - - - - - - - - - -")
a = np.arange(3.5, 5.5, 0.25)
print("np.arange(3.5, 5.5, 0.25) is:",a,"=")
a = np.arange(3.5, 6.0, 0.25)
print("np.arange(3.5, 6.0, 0.25) is:",a,"=")
a = np.arange(3.5, 6.0, 0.25, list)
print("np.arange(3.5, 6.0, 0.25, list) is:",a,"=")
"""
	The following does not work:
[a] = np.arange(3.5, 6.0, 0.25)
print("[np.arange(3.5, 6.0, 0.25)] is:",a,"=")
"""

"""
	This returns a list containing the array() method call, instead of
		a NumPy array that can be transformed into a list.
	Hence, do not use this method.
"""
a = [np.arange(3.5, 6.0, 0.25)]
print("[np.arange(3.5, 6.0, 0.25)] is:",a,"=")

"""
	Convert the NumPy array into a (Python) list.
	This makes the NumPy.arrange() solution acceptable.

	Reference:
	+ user3654478, Answer to "How to use a decimal range() step value?,"
		Stack Exchange Inc., New York, NY, February 12, 2016.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions
			at: https://stackoverflow.com/a/35362489/1531728 and
			https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value/35362489#35362489;
			March 4, 2020 was the last accessed date.
"""
a = np.arange(3.5, 6.0, 0.25).tolist()
print("np.arange(3.5, 6.0, 0.25).tolist() is:",a,"=")
start_point = 4.5
end_point = 7.0
increments = 0.25
a = np.arange(start_point, end_point+increments, increments).tolist()
print("np.arange(start_point, end_point, increments).tolist() is:",a,"=")

print("======================================")

"""
	Reference:
	+ user25148, Answer to "How to use a decimal range() step value?,"
		Stack Exchange Inc., New York, NY, January 25, 2009.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions
		at: https://stackoverflow.com/a/477513/1531728 and
		https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value/477513#477513;
		March 4, 2020 was the last accessed date.
"""

"""
	Use 0.25 as the scaling factor, since range(0, 10) produces a list
		of integers.
"""
a = [3.5+0.25*x for x in range(0, 10)]
print("[3.5+0.25*x for x in range(0, 10)] is:",a,"=")
#a = [start_point+increments*x for x in range(0, 10)]
#print("[start_point+increments*x for x in range(0, 10)] is:",a,"=")

print("- - - - - - - - - - - - - - - - - - -")

"""
	TO-DO!!! TO BE COMPLETED!!!
	Implement this and test it.

	This solution is acceptable, since I can customize this in a
		function call using variables.
	It is easier to handle floating-point increments that use a
		multiplicative scale factor.
"""
bias = 4.5
scaling_factor = 0.25
start_point = 0
end_point = 10
a = [bias+scaling_factor*x for x in range(start_point, end_point+1)]
print("[bias+scaling_factor*x for x in range(start_point, end_point+1)] is:",a,"=")

print("======================================")

"""
	This solution is acceptable, since I can customize this in a
		function call using variables.
	Modified to handle floating-point increments that use a reciprocal
		scale factor or multiplicative inverse .

	cmsjr and Peter Mortensen, Answer to "How to use a decimal range()
		step value?," Stack Exchange Inc., New York, NY, February 1, 2015.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/477506/1531728 and
			https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value/477506#477506;
			March 4, 2020 was the last accessed date.
"""
for i in range(0, 100, 10):
	print("for range(0, 100, 10), i/100.0 is:",i/100.0,"=")
print("- - - - - - - - - - - - - - - - - - -")
for i in range(0, 20, 2):
	print("for range(0, 20, 2), i/10.0 is:",i/10.0,"=")
print("- - - - - - - - - - - - - - - - - - -")
start_point = 0
end_point = 30
increments = 3
bias = 9
scale_factor = 10.0
a = [bias+i/scale_factor for i in range(start_point, end_point+1, increments)]
print("A list for bias+i/scale_factor is:",a,"=")

print("======================================")

"""
	Reference:
	+ Eric Myers, Answer to "How to use a decimal range() step value?",
		Stack Exchange Inc., New York, NY, February 16, 2017.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/42283283/1531728 and
			https://stackoverflow.com/questions/477486/how-to-use-a-decimal-range-step-value/42283283#42283283;
			March 4, 2020 was the last accessed date.
"""

dt = 0.2
#xdt = 12.5
t_max = 14
def xdt(n):
	return dt*float(n)
tlist  = map(xdt, range(int(t_max/dt)+1))
"""
	Reference to convert a map object to a list object in Python:
	+ Triptych, Answer to "TITLE," Stack Exchange Inc., New York, NY, MONTH DAY, YEAR.
	Available online from Stack Exchange Inc.: Stack Overflow: Questions at: URL; March 4, 2020 was the last accessed date.
"""
tlist_list = list(tlist)
print("tlist_list is:",tlist_list,"=")
print("- - - - - - - - - - - - - - - - - - -")
a = [dt*round(i/dt) for i in tlist_list]
print("dt*round(i/dt) for i in tlist is:",a,"=")
# The following does not work.
#tlist_list = [*tlist]
"""
	References to convert a map object to a list in Python:
	+ Israel Unterman, Answer to "Getting a map() to return a list in
		Python 3.x", Stack Exchange Inc., New York, NY, October 28, 2018.
		Available online from Stack Exchange Inc.: Stack Overflow:
			Questions at: https://stackoverflow.com/a/38702484/1531728 and
			https://stackoverflow.com/questions/1303347/getting-a-map-to-return-a-list-in-python-3-x/38702484#38702484;
			March 4, 2020 was the last accessed date.
	+ Joshua Landau, Neil Girdhar, and Thomas Wouters, "Additional Unpacking Generalizations" as PEP 448,
		from Python: Python Developer's Guide: PEP 0 -- Index of Python
		Enhancement Proposals (PEPs), June 29, 2013.
		Available online from Python: Python Developer's Guide:
			PEP 0 -- Index of Python Enhancement Proposals (PEPs) as
			PEP 448 at: https://www.python.org/dev/peps/pep-0448/;
			March 4, 2020 was the last accessed date.
		- Elvis Pranskevichus and Yury Selivanov, "What’s New In Python 3.5",
			from Python: Python 3.8.2 documentation: What’s New in Python,
			Python Software Foundation, Wilmington, DE, September 13, 2015.
			Also available from Python: Python 3.8.2 documentation:
				What’s New in Python: What’s New In Python 3.5 at:
				https://docs.python.org/3/whatsnew/3.5.html#whatsnew-pep-448;
				March 4, 2020 was the last accessed date.
"""
print("- - - - - - - - - - - - - - - - - - -")
tlist_list = [*map(xdt, range(int(t_max/dt)+1))]
print("tlist_list via [*tlist] is:",tlist_list,"=")

print("======================================")

"""
	Solution requires downloading the source code as a Python script
		from the following reference.

	Reference:
	+ Andrew Barnert, "Equally-spaced Numbers (linspace) (Python Recipe)"
		from ActiveState: ActiveState Code: Recipes,
		ActiveState Software Inc., Vancouver, British Columbia, Canada,
		January 12, 2015.
		Available online from at: https://www.python.org/dev/peps/pep-0448/;
			March 4, 2020 was the last accessed date.
"""

"""
print(linspace(0, 10, 5))
# linspace(0, 10, 5)
print(list(linspace(0, 10, 5)))
# [0.0, 2.5, 5.0, 7.5, 10]
"""

#	======================================
#print("======================================")

"""
	Added round() function to make the minor incremental floating-point
		errors negligible.
		This has no effect, and I am commenting out this option/modification.

	Reference:
	+ Santoso Wijaya ("Santa"), Answer to "How do get more control over
		loop increments in Python?", Stack Exchange Inc., New York, NY,
		February 8, 2011.
		Available online from Stack Exchange Inc.: Stack Overflow: Questions
			at: https://stackoverflow.com/a/4930482/1531728 and
			https://stackoverflow.com/questions/4930404/how-do-get-more-control-over-loop-increments-in-python/4930482#4930482;
			March 4, 2020 was the last accessed date.
"""
#for i in [6+round(float(j)) / 100 for j in range(0, 100, 5)]:
for i in [6+float(j) / 100 for j in range(0, 100, 5)]:
	print("i is:",i,"=")

print("======================================")

"""
	Alternatives:
	+ https://pypi.org/project/Franges/0.1.0/
	+ https://stackoverflow.com/questions/33257635/python-round-to-nearest-0-25

	References for Python rounding errors. such as 0.250000000002
		approximately equals 0.25 (or 0.250).
	+ https://mail.python.org/pipermail/python-list/2001-December/115350.html
	+ https://mail.python.org/pipermail/python-list/2001-December/071437.html
"""

print("=	End Here	=")


import numpy as np
import numpy.linalg as la

def conjugate_gradients(A, b, x0, tol = 0.0001, nmax = 100):
    """
    Uses the conjugate gradient algorithm to solve the system Ax = b.

    :param A: symmetric, positive definite matrix
    :type A: 2d numpy array or scipy sparse matrix
    :param b: right-hand side of the system
    :type b: 1d numpy array
    :param x0: starting point of the iteration
    :type x0: 1d numpy array
    :param tol: tolerance, iteration terminates when the norm of the residual has been reduced by the tolerance
    :type tol: float
    :param nmax: maximum number of iterations
    :type nmax: int
    :raises TypeError: the matrix A is not square
    :raises TypeError: the dimensions of A & b don't match
    :raises TypeError: the dimensions of A & x0 don't match
    :raises Exception: the iteration has failed to converge in nmax iterations
    :return: solution of the equation, list of the norm of the residual after each iteration
    :rtype: tuple of 1d numpy array and a list of floats
    """

    # check dimensions are compatible
    N = A.shape[0]
    if A.shape[1] != N:
        raise TypeError('The matrix provided is not square.')
    if b.shape[0] != N:
        raise TypeError('The dimensions of the right hand side do not match the dimensions of the matrix provided.')
    if x0.shape[0] != N:
        raise TypeError('The dimensions of the starting point do not match the dimensions of the matrix provided.')

    x = np.copy(x0)     # solution
    r = b - A@x         # residual
    res = [la.norm(r)]  # stores the norm of the residuals at each step
    p = r               # initial search direction

    n = 0               # number of iterations passed
    while res[-1] > tol * res[0]:
        alpha = np.dot(r,r)/np.dot(p,A@p)   # step length
        x += alpha * p                      # update x
        r_new = r - alpha * A@p             # new residual, store in new variable, because old r is needed to calculate beta
        beta = np.dot(r_new,r_new)/np.dot(r,r)
        r = r_new                           # update residual
        res.append(la.norm(r))
        p = r + beta * p                    # new search direction

        n+=1                                # update number of iterations
        if n > nmax:
            raise Exception('The iteration has failed to converge within nmax(=' + str(nmax) + ') iterations.')

    return x, res


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

# In[1]:

import numpy as np
import pandas as pd

# In[2]:

#import data
data = pd.read_csv("D:\dataset_FLD.csv")

# In[3]:

positive = data.loc[data['y']==1]

# In[4]:

negative = data.loc[data['y']==0]

# In[5]:

positive = positive.iloc[:,0:3].values
negative = negative.iloc[:,0:3].values

# In[6]:

#calculate means in original space
M1 = np.mean(positive, axis = 0)
M2 = np.mean(negative, axis = 0)

# In[7]:

print("M1 =",M1)
print("M2 =",M2)

# In[8]:

#Calculating Sw
res1 = np.zeros([3,3])
for i in range(len(positive)):
    pos = positive[i]-M1
    pos.shape = (1,3)
    posT = np.transpose(pos)
    ans1 = np.dot(posT,pos)
    res1 = np.add(res1,ans1)

# In[9]:

res2 = np.zeros([3,3])
for i in range(len(negative)):
    neg = negative[i]-M2
    neg.shape = (1,3)
    negT = np.transpose(neg)
    ans2 = np.dot(negT,neg)
    res2 = np.add(res2,ans2)

# In[10]:

res1 = res1/len(positive)
res2 = res2/len(negative)

# In[11]:

Sw = res1+res2
print("Sw is\n",Sw)

# In[12]:

#Calculating Sw inverse
sw_inv = np.linalg.inv(Sw)
sw_inv

# In[13]:

#Finding the vector w and normalising it
w = np.dot(sw_inv,np.transpose(M2-M1))
print("w is",w)

# In[14]:

import math
mag = math.sqrt(w[0]*w[0]+w[1]*w[1]+w[2]*w[2])

# In[15]:

w = w/mag
print("normalised w is",w)

# In[16]:

#Finding projections of positive and negative points on unit vector w
positive_projections = np.zeros([len(positive)])
for i in range(len(positive)):
    positive_projections[i] = np.dot(w,np.transpose(positive[i]))

# In[17]:

negative_projections = np.zeros([len(negative)])
for i in range(len(negative)):
    negative_projections[i] = np.dot(w,np.transpose(negative[i]))

# In[18]:

#Plotting reduced clusters of positive and negative data sets
import matplotlib.pyplot as plt
plt.axes((-2.5,-0.05,3,1))
plt.plot(positive_projections,np.zeros(len(positive_projections)),'r+')
plt.plot(negative_projections,np.zeros(len(negative_projections)),'b-')
plt.show()

# In[19]:

#Fitting the reduced clusters into Gauss Normal Distributions
mu_p = np.mean(positive_projections)
std_p = np.std(positive_projections)
mu_n = np.mean(negative_projections)
std_n = np.std(negative_projections)

# In[20]:

#Sorting projection data to plot the normal distributions
positive_projections.sort()
negative_projections.sort()

# In[21]:

print("pos_nd_mean =",mu_p,"and pos_nd_std =",std_p)
print("neg_nd_mean =",mu_n,"and neg_nd_std =",std_n)

# In[22]:

import matplotlib.pyplot as plt

# In[23]:

#Solving the quadratic equations of ND1 and ND2 to find the intersection points
def findThreshold(mu1,mu2,std1,std2):
    p = 1/(2*std1**2) - 1/(2*std2**2)
    q = mu2/(std2**2) - mu1/(std1**2)
    r = mu1**2 /(2*std1**2) - mu2**2 / (2*std2**2) - np.log(std2/std1)
    return np.roots([p,q,r])

# In[24]:

#Finding the threshold point which should be between the means of ND1 and ND2
intersection = findThreshold(mu_p,mu_n,std_p,std_n)
threshold = 0
for i in range(len(intersection)):
    if (mu_p < intersection[i]) and (mu_n > intersection[i]):
        threshold = intersection[i]
        break

print("Threshold is",threshold)

# In[25]:

#Plotting the normal distributions and the discriminant line in 1D
from scipy.stats import norm
y1 = norm.pdf(positive_projections,mu_p,std_p)
plt.plot(positive_projections,y1,color='red')
y2 = norm.pdf(negative_projections,mu_n,std_n)
plt.plot(negative_projections,y2,color='blue')
plt.plot(threshold,0,marker = 'o',markersize=3,color='green')
plt.axvline(x=threshold, color='green')

# In[26]:

#Calculating accuracy
correct_pclass = 0
for i in range(len(positive)):
    if np.dot(w,np.transpose(positive[i])) < threshold:
        correct_pclass += 1

correct_nclass = 0
for i in range(len(negative)):
    if np.dot(w,np.transpose(negative[i])) > threshold:
        correct_nclass += 1
accuracy = (correct_pclass + correct_nclass)/(len(positive) + len(negative))
print("Accuracy is",accuracy)

# In[27]:

#Plotting higher dimensional data and discriminant in oriignal space
Xp = positive[:,0]
Yp = positive[:,1]
Zp = positive[:,2]

# In[28]:

Xn = negative[:,0]
Yn = negative[:,1]
Zn = negative[:,2]

# In[29]:

import matplotlib.pyplot as mpp
mpp.rcParams['figure.figsize'] = (8,8)
mpp.rcParams['figure.dpi'] = 100

# In[30]:

fig = plt.figure()
ax = fig.add_subplot(111,projection = '3d')
ax.scatter(Xp,Yp,Zp,c = 'r',marker='.')
ax.scatter(Xn,Yn,Zn,c = 'b',marker='.')
a = w[0]
b = w[1]
c = w[2]
xx = np.linspace(-10,10,100)
yy = np.linspace(-10,10,100)
xx, yy = np.meshgrid(xx, yy)
z = (intersection[1] - a*xx - b*yy)/c
ax.plot_surface(xx, yy, z, alpha=0.4,color = 'green')

ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_zlabel('z-axis')
plt.show()

# In[31]:

#Printing Final results
print("Unit vector w :",w)
print("Threshold in 1D =",threshold)
print("Accuracy :",accuracy)

Fisher's Linear Discriminant/Fisher's Linear Discriminant.py


import tqdm

import numpy as onp
import jax.numpy as np

from tfc import mtfc
from tfc.utils import LS, egrad, MakePlot

# Constants and switches:
x0 = np.array([0.,0.])
xf = np.array([1.,1.])

n = 20
nTest = 100

xTFC = False # Set to True to use X-TFC rather than TFC
usePlotly = True # Set to True to use plotly rather than matplotlib

# Real analytical solution:
real = lambda x,y: 1./np.pi**2*np.sin(np.pi*x)*np.sin(np.pi*y)

# MC constants:
if xTFC:
    m = 335
else:
    m = 26

# Create the TFC Class:
N = [n,n]
if xTFC:
    nC = -1
    myTfc = mtfc(N,nC,m,dim=2,basis='ELMTanh',x0=x0,xf=xf)
else:
    nC = [4,4]
    myTfc = mtfc(N,nC,m,dim=2,basis='CP',x0=x0,xf=xf)
x = myTfc.x

# Get the basis functions
H = myTfc.H
Hxx = myTfc.Hx2

# Create the TFC constrained expression
u1 = lambda xi,*x: np.dot(H(*x),xi)-\
        (1.-x[0])*np.dot(H(np.zeros_like(x[0]),x[1]),xi)-\
        x[0]*np.dot(H(np.ones_like(x[0]),x[1]),xi)-\
        (-x[0]**3+3*x[0]**2-2.*x[0])/6.*np.dot(Hxx(np.zeros_like(x[0]),x[1]),xi)-\
        (x[0]**3-x[0])/6.*np.dot(Hxx(np.ones_like(x[0]),x[1]),xi)
u1yy = egrad(egrad(u1,2),2)
u = lambda xi,*x: u1(xi,*x)-\
        (1.-x[1])*u1(xi,x[0],np.zeros_like(x[1]))-\
        x[1]*u1(xi,x[0],np.ones_like(x[1]))-\
        (-x[1]**3+3*x[1]**2-2.*x[1])/6.*u1yy(xi,x[0],np.zeros_like(x[1]))-\
        (x[1]**3-x[1])/6.*u1yy(xi,x[0],np.ones_like(x[1]))

# Create the residual
u4x = egrad(egrad(egrad(egrad(u,1),1),1),1)
u4y = egrad(egrad(egrad(egrad(u,2),2),2),2)
u2x2y = egrad(egrad(egrad(egrad(u,1),1),2),2)
L = lambda xi: u4x(xi,*x)+u4y(xi,*x)+2.*u2x2y(xi,*x)-4.*np.pi**2*np.sin(np.pi*x[0])*np.sin(np.pi*x[1])

# Solve the problem
xi = np.zeros(H(*x).shape[1])
if xTFC:
    xi,time = LS(xi,L,method='lstsq',timer=True)
else:
    xi,time = LS(xi,L,timer=True)

# Calculate error at the test points:
dark = np.meshgrid(np.linspace(0,1,nTest),np.linspace(0,1,nTest))
xTest = tuple([j.flatten() for j in dark])
err = np.abs(u(xi,*xTest)-real(*xTest))
print("Time: "+str(time))
print("Max Error: "+str(np.max(err)))
print("Mean Error: "+str(np.mean(err)))

# Plot the analytical solution
if usePlotly:
    from matplotlib import cm
    from tfc.utils.PlotlyMakePlot import MakePlot

    p = MakePlot(r'x',r'y',zlabs=r'u(x,y)')
    p.Surface(x=xTest[0].reshape((nTest,nTest)),
              y=xTest[1].reshape((nTest,nTest)),
              z=real(*xTest).reshape((nTest,nTest)),
              showscale=False)
    p.view(azimuth=-135,elevation=20)
    p.fig['layout']['scene']['aspectmode']='cube'
    p.show()

else:
    from tfc.utils import MakePlot

    p = MakePlot(r'$x$',r'$y$',zlabs=r'$u(x,y)$')
    p.ax[0].plot_surface(xTest[0].reshape((nTest,nTest)),xTest[1].reshape((nTest,nTest)),real(*xTest).reshape((nTest,nTest)),cmap=cm.gist_rainbow)

    p.ax[0].tick_params(axis='z', which='major', pad=10)
    p.ax[0].xaxis.labelpad = 20
    p.ax[0].yaxis.labelpad = 20
    p.ax[0].zaxis.labelpad = 20

    p.FullScreen()
    p.show()