# This program and its computation instruction was written by Andre Christoga Pramaditya (drepram.com) with the student ID (2006570006).
# Program dan instruksi komputasi ini ditulis oleh Andre Christoga Pramaditya (drepram.com) dengan Nomor Pokok Mahasiswa (2006570006).

import numpy as np
import matplotlib.pyplot as plt

import warnings
warnings.filterwarnings("ignore") # insert programmer laziness joke here

size_ten = 10
size_hundred = 100
size_thousand = 1000
size_million = 1000000

# Case 1 on the task said to retrieve n=10 random numbers from the uniform distribution [1,10]

# Remembering the set notation of [low, high), where low is included and high is excluded.
# We could check the validity of our computation with the functions that we would show below (via console logging)

case_1_histogram = plt.figure(1, figsize=(7,7))

case_1_sample_ten = np.random.uniform(1,10,10)
print("For we are supposed to retrieve random numbers from [1,10], it begs the question as to whether the intervals are actually included in our computation.\n")
print("Does the random distribution of (low=1,high=10,size=10) includes samples from numbers that is less or equal to 10?", np.all(case_1_sample_ten <= 10))
print("Does the random distribution of (low=1,high=10,size=10) includes samples from numbers that is greater or equal to 1?", np.all(case_1_sample_ten >= 1))
print("\nAs the questions above has been answered, we will proceed to show the graph of other uniform distributions with differing number of sample size.")

case_1_sample_hundred = np.random.uniform(1,10,100)
case_1_sample_thousand = np.random.uniform(1,10,1000)
case_1_sample_million = np.random.uniform(1,10,1000000)

plt.hist(case_1_sample_ten, 15, density=True)
plt.hist(case_1_sample_hundred, 15, density=True)
plt.hist(case_1_sample_thousand, 15, density=True)
plt.hist(case_1_sample_million, 15, density=True)

case_1_histogram.suptitle('Uniform Distribution Case 1 Combined Histogram', fontsize=14, fontweight='bold')
case_1_axes_title = case_1_histogram.add_subplot(111)
case_1_histogram.subplots_adjust(top=0.85)
case_1_axes_title.set_title('retrieve n=10 random numbers from the uniform distribution [1,10] \nwith n iterating from 10,100,1000,1000000')
case_1_histogram.show()

print("\n---\nNow on to the second case.\n")

case_2_histogram = plt.figure(2, figsize=(7,7))

case_2_sample_ten = np.random.uniform(25,100,10)
print("For we are supposed to retrieve random numbers from [25,100], it begs the question as to whether the intervals are actually included in our computation.\n")
print("Does the random distribution of (low=25,high=100,size=10) includes samples from numbers that is less or equal to 25?", np.all(case_2_sample_ten <= 25))
print("Does the random distribution of (low=25,high=100,size=10) includes samples from numbers that is greater or equal to 10?", np.all(case_2_sample_ten >= 10))
print("\nAs the questions above has been answered, we will proceed to show the graph of other uniform distributions with differing number of sample size.")

case_2_sample_hundred = np.random.uniform(25,100,100)
case_2_sample_thousand = np.random.uniform(25,100,1000)
case_2_sample_million = np.random.uniform(25,100,1000000)

plt.hist(case_2_sample_ten, 15, density=True)
plt.hist(case_2_sample_hundred, 15, density=True)
plt.hist(case_2_sample_thousand, 15, density=True)
plt.hist(case_2_sample_million, 15, density=True)

case_2_histogram.suptitle('Uniform Distribution Case 2 Combined Histogram', fontsize=14, fontweight='bold')
case_2_axes_title = case_2_histogram.add_subplot(111)
case_2_histogram.subplots_adjust(top=0.85)
case_2_axes_title.set_title('retrieve n=10 random numbers from the uniform distribution [25,100] \nwith n iterating from 10,100,1000,1000000')
case_2_histogram.show()

print("\n---\nNow that the combined histograms of both cases has been shown, we will show the individual graphs.\n")

case_1_each, case_1_each_axes = plt.subplots(2, 2, figsize=(10,10))
case_1_each.suptitle('Uniform Distribution Case 1 Histogram of Every Sample Size', fontsize=14, fontweight='bold')

case_1_each_axes[0, 0].hist(case_1_sample_ten, 15, density=True)
case_1_each_axes[0, 0].set_title('low=1,high=10,size=10')
case_1_each_axes[1, 0].hist(case_1_sample_hundred, 15, density=True)
case_1_each_axes[1, 0].set_title('low=1,high=10,size=100')
case_1_each_axes[0, 1].hist(case_1_sample_thousand, 15, density=True)
case_1_each_axes[0, 1].set_title('low=1,high=10,size=1000')
case_1_each_axes[1, 1].hist(case_1_sample_million, 15, density=True)
case_1_each_axes[1, 1].set_title('low=1,high=10,size=1000000')

case_2_each, case_2_each_axes = plt.subplots(2, 2, figsize=(10,10))
case_2_each.suptitle('Uniform Distribution Case 2 Histogram of Every Sample Size', fontsize=14, fontweight='bold')

case_2_each_axes[0, 0].hist(case_2_sample_ten, 15, density=True)
case_2_each_axes[0, 0].set_title('low=25,high=100,size=10')
case_2_each_axes[1, 0].hist(case_2_sample_hundred, 15, density=True)
case_2_each_axes[1, 0].set_title('low=25,high=100,size=100')
case_2_each_axes[0, 1].hist(case_2_sample_thousand, 15, density=True)
case_2_each_axes[0, 1].set_title('low=25,high=100,size=1000')
case_2_each_axes[1, 1].hist(case_2_sample_million, 15, density=True)
case_2_each_axes[1, 1].set_title('low=25,high=100,size=1000000')

plt.show()


import math as m
from scipy.misc import derivative

# Here we simply give the input needed as The Following:
# ( Equation ) First Specify if the input is an equation ( 1 ) or only a single application of the rule ( 0 )
# ( Xi ) is the initial point
# If The Equation was set to "0" you need to provide The Values First Derivative ( dr ) And Second Derivative ( Ddr ). if not selected keep 0
# If The Equation was set to "1" you need to provide The Equation in the function ( F ) if not selected keep 0
# ( n ) is the number of Iterations needed

Equation = 1

Xi = 8.6

dr = 1.2

Ddr = 5

def F(x): return -0.25*x**4+1.1*x**3-1.75*x**2+2*x

n = 5

print("\n-----------------------------------------------------------------")
print(" Iteration |   Xi    |   F(Xi)  |   F(x)\'  |  F(x)\"    |  Error "
      "\n-----------------------------------------------------------------")
Error = 100
XiPlus1 = 0
for i in range (n):
    if Equation == 1:
        print("\t{0:1d}  | {1:.5f} | {2:.5f}  | {3:.5f} | {4:.5f} |  {5:.4f}%\n"
              .format(i, Xi, F(Xi), derivative(F, Xi, dx=1e-6), derivative(F, Xi, dx=1e-6, n = 2 ), Error))
        XiPlus1 = Xi - derivative(F, Xi, dx=1e-6)/derivative(F, Xi, dx=1e-6, n = 2 )
        Error = abs(((XiPlus1 - Xi)/XiPlus1) *100)
    if Equation == 0:
        print("\t{0:1d}  | {1:.5f} |  F(Xi)  | {2:.5f} | {3:.5f} |  {4:.4f} %\n"
              .format(i, Xi, dr2,Ddr2, Error))
        XiPlus1 = Xi - (dr/Ddr)
        Error = abs(((XiPlus1 - Xi)/XiPlus1) *100)
        if i == 2 : break
    Xi = XiPlus1

# Finally displaying the solution as well as the percentge of error after each iteration


import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-1,3,100)
y=x**2-2*x+1

fig = plt.figure()
axdef = fig.add_subplot(1, 1, 1)
axdef.spines['left'].set_position('center')
axdef.spines['bottom'].set_position('zero')
axdef.spines['right'].set_color('none')
axdef.spines['top'].set_color('none')
axdef.xaxis.set_ticks_position('bottom')
axdef.yaxis.set_ticks_position('left')

plt.plot(x,y, 'r')
plt.show()

Gradf = lambda x: 2*x-2

ActualX = 3
LearningRate = 0.01
PrecisionValue = 0.000001
PreviousStepSize = 1
MaxIteration = 10000
IterationCounter = 0

while PreviousStepSize > PrecisionValue and IterationCounter < MaxIteration:
    PreviousX = ActualX
    ActualX = ActualX - LearningRate * Gradf(PreviousX)
    PreviousStepSize = abs(ActualX - PreviousX)
    IterationCounter = IterationCounter+1
    print("Number of iterations = ",IterationCounter,"\nActual value of x  is = ",ActualX)

print("X value of f(x) minimum = ", ActualX)


from sympy import ( symbols, solve, diff, integrate, exp, sqrt, lambdify, Integral, pprint )
import matplotlib.pyplot as plt
import numpy as np

# If the age of a randomly selected driver in a fatal car crash is a random variable with a probability density function given by

x = symbols( 'x' )
F = 0.01912 * exp( -0.00321 * x )

# What does the petal distribution look like?

g_xlim = [ -1, 90 ]

lam_p = lambdify( x, F, np )
x_vals = np.linspace( g_xlim[0], g_xlim[1], 1000, endpoint=True )
y_vals = lam_p( x_vals )
plt.plot( x_vals, y_vals )

x_min, x_max = 18, 79

plt.hlines( y = 0, xmin = g_xlim[0], xmax = g_xlim[1], color = 'Black', zorder = 1 )

plt.vlines( x = x_min, ymin = 0, ymax = F.subs( { x: x_min } ), color = 'Black', zorder = 1 )
plt.vlines( x = x_max, ymin = 0, ymax = F.subs( { x: x_max } ), color = 'Red', zorder = 1 )

# The probability that the driver's age is between 24 and 45 is

a, b = 24, 45
bounds = np.arange( a, b, 1/25., dtype=float)

for n in bounds:
	y = F.subs( { x: n } )
	plt.vlines( x = n, ymin = 0, ymax = y, color = 'Teal', zorder = 1, alpha = .4 )

area = integrate( F, ( x, a, b ) ).evalf()
total_area = integrate( F, ( x, x_min, x_max ) ).evalf()

area_pct = round( ( area / total_area ), 2 )

plt.title( 'Area: {0}'.format( area_pct ) )
plt.show()

# The probability that the driver's age is greater than or equal to 33

a, b = 32, x_max
bounds = np.arange( a, b, 1/25., dtype=float)

for n in bounds:
	y = F.subs( { x: n } )
	plt.vlines( x = n, ymin = 0, ymax = y, color = 'Teal', zorder = 1, alpha = .4 )

area = integrate( F, ( x, a, b ) ).evalf()
total_area = integrate( F, ( x, x_min, x_max ) ).evalf()

area_pct = round( ( area / total_area ), 2 )

plt.title( 'Area: {0}'.format( area_pct ) )
plt.show()

# The probability that the driver is less than or equal to 28.

a, b = x_min, 21
bounds = np.arange( a, b, 1/25., dtype=float)

for n in bounds:
	y = F.subs( { x: n } )
	plt.vlines( x = n, ymin = 0, ymax = y, color = 'Teal', zorder = 1, alpha = .4 )

area = integrate( F, ( x, a, b ) ).evalf()
total_area = integrate( F, ( x, x_min, x_max ) ).evalf()

area_pct = round( ( area / total_area ), 2 )

plt.title( 'Area: {0}'.format( area_pct ) )
plt.show()

# Find the cumulative distribution function for this random variable.
a, b = x_min, x
cdf = integrate( F, ( x, a, b ) )

pprint( cdf )

round( 5.95638629283489, 5 )

#  Use the answer to part D to find the probability that a randomly selected driver in a fatal crash is at most 24 years old.

ans = cdf.subs( { x: 23 } ).evalf()
round( ans, 2 )


__doc__ = """ Fits a set of 2D data points (x,y) to a circle

See https://scipy-cookbook.readthedocs.io/items/Least_Squares_Circle.html
From https://dtcenter.org/met/users/docs/write_ups/circle_fit.pdf
For more complicated shapes, see https://arxiv.org/pdf/cs/0301001.pdf
"""

import numpy as np
from scipy.optimize import least_squares
from scipy.stats import linregress

def fit_circle_to_data(in_data, verbose=False):
    """
    Wrapper around _fit_circle_impl that takes care of unwrapping shapes and so on...
    Returns
    -------

    """
    slope, intercept, r_value, p_value, std_err = linregress(in_data[0], in_data[1])

    if verbose:
        print("R-square of linear fit : {}".format(r_value ** 2))
        print("std-error of linear fit : {}".format(std_err))

    if r_value ** 2 < 0.98 and std_err > 1e-3:
        xc, yc, r, opt = circle_fit_impl(in_data)

        if verbose:
            print("Number of function evaluations : {}".format(opt.nfev))
            print("Number of Jacobian evaluations : {}".format(opt.njev))
            print("Stddev. residuals at optimal : {}".format(np.std(opt.fun)))

        return xc, yc, r
    else:
        # It's almost a straight line, so return all infinitirys
        return np.inf, np.inf, np.inf

def circle_fit_impl(x):
    """(2,n) array as input"""

    def distance_from_center(xc, yc):
        """calculate the distance of each 2D points from the center (xc, yc)"""
        return np.sqrt((x[0] - xc) ** 2 + (x[1] - yc) ** 2)

    def objective(c):
        """calculates the objective : algebraic distance between the data points
        and the mean circle centered at c=(xc, yc)

        Maps from R^2 -> R^n where n is the number of points to map
        """
        Ri = distance_from_center(*c)
        return Ri - Ri.mean()

    def jacobian_objective(c):
        """Jacobian of above objective function
        By definition from scipy J_{ij} = \\partial f_{i}/ \\partial x_{j}
        Hence J is a (n,2) matrix below

        Parameters
        ----------
        c

        Returns
        -------

        """
        xc, yc = c
        df2b_dc = np.empty((x.shape[-1], c.shape[0]))

        r_i = distance_from_center(xc, yc)
        df2b_dc[..., 0] = (xc - x[0]) / r_i  # dR/dxc
        df2b_dc[..., 1] = (yc - x[1]) / r_i  # dR/dyc
        df2b_dc -= np.mean(df2b_dc, axis=0)

        return df2b_dc

    # estimate center via mean of data
    center_estimate = np.mean(x, axis=-1)
    optimum_results = least_squares(
        objective, center_estimate, jac=jacobian_objective, method="lm"
    )

    center_optimized = optimum_results.x
    radius_samples = distance_from_center(*center_optimized)
    radius = np.mean(radius_samples)
    return (*center_optimized, radius, optimum_results)


import numpy as np

def universe_maker(setsList):
    '''
    This alorithm determines the universe of elements contain on the list of sets 'setList'.  To do that it uses a double loop that adds the elements to 'universe' set.  Since 'universe' is a set only unique elements are store.

    Arg:
        setsList: is a list of all the sets

    Out:
        universe:  is a set of all the elements

    '''
    universe = set(e for s in setsList for e in s)

    return universe

def set_cover_greedy(universe, setsList):
   '''
   This alorithm for the set cover problem uses the greedy alorithm.  The rule that follows is choosing the set that contains the largest number of uncovered elements.
   This alorithm is a ln n –approximation.

   Arg:
        universe:  is a set of all the elements
        setsList: is a list of all the sets

   Out:
        cover: is the list of sets that covers the hole universe.
        listCover: is the list that indicates what set (number) was use to forn the cover.
   '''

   if universe_maker(setsList) != universe:
        raise ValueError('There is a discrepancy between the universe input and the universe constructed from the list of sets.')

   covered = set()
   cover = []
   listCover = []
   while covered != universe:
        subSet = max(setsList, key=lambda s: len(s - covered))
        cover.append(subSet)
        listCover.append(setsList.index(subSet))
        covered |= subSet

   return cover, listCover

def set_cover_weighted_greedy(universe, setsList, weightList, normalize = True):
    '''
    This alorithm for the set cover problem uses the greedy alorithm.  The rule that follows is choosing the set that is most efficent at adding new elements.  The efficency is deffiend as the new elements to be added weighted by their cost.
    This alorithm is a ln n –approximation.

    Arg:
        universe:  is a set of all the elements
        setsList: is a list of all the sets

   Out:
        cover: is the list of sets that covers the hole universe.
        listCover: is the list that indicates what set (number) was use to forn the cover.
   '''

    if universe_maker(setsList) != universe:
        raise ValueError('There is a discrepancy between the universe input and the universe constructed from the list of sets.')

    if len(setsList) != len(weightList):
        raise ValueError('The two list have different lenghts')

    covered = set()
    cover = []
    listCover = []

    #The weights are normalized, limits numerical errors.
    if normalize == True:
        weightList = weightList/np.mean(weightList)

    while covered != universe:
        #setWeightStep is the list of cost given what is alrady present in covered. When a set does not gives value (no new items) it assings a 0.
        setWeightStep = list(map(lambda x, y: len(x -covered)/y if len(x -covered) != 0 else 0, setsList, weightList))
        #It will give the first element on the setWeightStep that is the most efficent. It is likely that more than one index will have the min same value.  In such cases a secondary cost function could be use to separate them. s
        index = setWeightStep.index(max(setWeightStep))
        cover.append(setsList[index])
        listCover.append(index)
        covered |= setsList[index]

    return cover, listCover


# 1次元ガウス分布の作図

# 利用するライブラリ
import numpy as np
from scipy.stats import norm # 1次元ガウス分布
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

#%%

### 確率密度の計算

# 平均を指定
mu = 1.0

# 標準偏差を指定
sigma = 2.5

# 確率変数の値を指定
x = 1.0

# 定義式により確率密度を計算
C = 1.0 / np.sqrt(2.0 * np.pi * sigma**2)
dens = C * np.exp(-0.5 * (x - mu)**2 / sigma**2)
print(dens)

# 対数をとった定義式により確率密度を計算
log_C = -0.5 * np.log(2.0 * np.pi) - np.log(sigma)
log_dens = log_C - 0.5 * (x - mu)**2 / sigma**2
dens = np.exp(log_dens)
print(dens, log_dens)

# ガウス分布の関数により確率密度を計算
dens = norm.pdf(x=x, loc=mu, scale=sigma)
print(dens)

# ガウス分布の対数をとった関数により確率密度を計算
log_dens = norm.logpdf(x=x, loc=mu, scale=sigma)
dens = np.exp(log_dens)
print(dens, log_dens)

#%%

### 統計量の計算

# 平均を指定
mu = 1.0

# 標準偏差を指定
sigma = 2.5

# 計算式により平均を計算
E_x = mu
print(E_x)

# 計算式により分散を計算
V_x = sigma**2
print(V_x)

# 関数により平均を計算
E_x = norm.mean(loc=mu)
print(E_x)

# 関数により分散を計算
V_x = norm.var(scale=sigma)
print(V_x)

#%%

### 分布の可視化

## 分布の計算

# 平均を指定
mu = 0.0

# 標準偏差を指定
sigma = 1.0

# 作図用のxの点を作成
x_vals = np.linspace(start=mu - sigma*4.0, stop=mu + sigma*4.0, num=250)

# ガウス分布を計算
density = norm.pdf(x=x_vals, loc=mu, scale=sigma)

#%%

## 分布の作図

# ガウス分布を作図
plt.figure(figsize=(12, 9)) # 図の設定
plt.plot(x_vals, density, color='#00A968') # 折れ線グラフ
plt.xlabel('x') # x軸ラベル
plt.ylabel('density') # y軸ラベル
plt.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル
plt.title('$\mu=' + str(mu) + ', \sigma=' + str(sigma) + '$', loc='left') # タイトル
plt.grid() # グリッド線
plt.show() # 描画

#%%

## 統計量を重ねた分布の作図

# ガウス分布を作図
plt.figure(figsize=(12, 9)) # 図の設定
plt.plot(x_vals, density, color='#00A968') # 分布
plt.vlines(x=mu, ymin=0.0, ymax=np.max(density), color='orange', linestyle='--', label='$\mu$') # 平均
plt.vlines(x=mu - sigma, ymin=0.0, ymax=np.max(density), color='orange', linestyle=':', label='$\mu \pm \\sigma$') # 平均 - 標準偏差
plt.vlines(x=mu + sigma, ymin=0.0, ymax=np.max(density), color='orange', linestyle=':') # 平均 + 標準偏差
plt.xlabel('x') # x軸ラベル
plt.ylabel('density') # y軸ラベル
plt.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル
plt.title('$\mu=' + str(mu) + ', \sigma=' + str(sigma) + '$', loc='left') # タイトル
plt.grid() # グリッド線
plt.legend() # 凡例
plt.show() # 描画

#%%

### パラメータと分布の形状の関係

## 平均の影響

# 平均として利用する値を指定
mu_vals = np.arange(start=-5.0, stop=5.0, step=0.1)
print(len(mu_vals)) # フレーム数

# 標準偏差を指定
sigma = 1.0

# 作図用のxの点を作成
x_vals = np.linspace(start=np.median(mu_vals) - sigma*4.0, stop=np.median(mu_vals) + sigma*4.0, num=250)

# y軸(確率密度)の最大値を設定
dens_max = np.max(norm.pdf(x=x_vals, loc=0.0, scale=sigma)) + 0.05

# 図を初期化
fig = plt.figure(figsize=(12, 9)) # 図の設定
fig.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル

# 作図処理を関数として定義
def update(i):
    # 前フレームのグラフを初期化
    plt.cla()

    # i番目の平均パラメータを取得
    mu = mu_vals[i]

    # ガウス分布を計算
    dens = norm.pdf(x=x_vals, loc=mu, scale=sigma)

    # ガウス分布を作図
    plt.plot(x_vals, dens, color='#00A968') # 折れ線グラフ
    plt.xlabel('x') # x軸ラベル
    plt.ylabel('density') # y軸ラベル
    plt.title('$\mu=' + str(np.round(mu, 1)) + ', \sigma=' + str(sigma) + '$', loc='left') # タイトル
    plt.grid() # グリッド線
    plt.ylim(ymin=-0.01, ymax=dens_max) # y軸の表示範囲

# gif画像を作成
anime_dens = FuncAnimation(fig, update, frames=len(mu_vals), interval=100)

# gif画像を保存
anime_dens.save('ProbabilityDistribution/Gaussian_dens_mu.gif')

#%%

## 標準偏差の影響

# 標準偏差として利用する値を指定
sigma_vals = np.arange(start=1.0, stop=10.1, step=0.1)
print(len(sigma_vals)) # フレーム数

# 平均を指定
mu = 0.0

# 作図用のxの点を作成
x_vals = np.linspace(start=mu - np.max(sigma_vals)*2.0, stop=mu + np.max(sigma_vals)*2.0, num=250)

# y軸(確率密度)の最大値を設定
dens_max = np.max(norm.pdf(x=x_vals, loc=mu, scale=np.min(sigma_vals))) + 0.05

# 図を初期化
fig = plt.figure(figsize=(12, 9)) # 図の設定
fig.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル

# 作図処理を関数として定義
def update(i):
    # 前フレームのグラフを初期化
    plt.cla()

    # i番目の標準偏差パラメータを取得
    sigma = sigma_vals[i]

    # ガウス分布を計算
    dens = norm.pdf(x=x_vals, loc=mu, scale=sigma)

    # ガウス分布を作図
    plt.plot(x_vals, dens, color='#00A968') # 折れ線グラフ
    plt.xlabel('x') # x軸ラベル
    plt.ylabel('density') # y軸ラベル
    plt.title('$\mu=' + str(mu) + ', \sigma=' + str(np.round(sigma, 1)) + '$', loc='left') # タイトル
    plt.grid() # グリッド線
    plt.ylim(ymin=-0.01, ymax=dens_max) # y軸の表示範囲

# gif画像を作成
anime_dens = FuncAnimation(fig, update, frames=len(sigma_vals), interval=100)

# gif画像を保存
anime_dens.save('ProbabilityDistribution/Gaussian_dens_sigma.gif')

#%%

### 乱数の生成

## 乱数の可視化

# 平均を指定
mu = 1.0

# 標準偏差を指定
sigma = 2.5

# データ数を指定
N = 1000

# ガウス分布に従う乱数を生成
x_n = np.random.normal(loc=mu, scale=sigma, size=N)

# 作図用のxの点を作成
x_vals = np.linspace(mu - sigma*4.0, mu + sigma*4.0, num=250)

# ガウス分布を計算
density = norm.pdf(x=x_vals, loc=mu, scale=sigma)

#%%

## 乱数の可視化

# サンプルのヒストグラムを作成
plt.figure(figsize=(12, 9)) # 図の設定
plt.hist(x=x_n, bins=50, range=(x_vals.min(), x_vals.max()), color='#00A968') # ヒストグラム
plt.xlabel('x') # x軸ラベル
plt.ylabel('frequency') # y軸ラベル
plt.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル
plt.title('$\mu=' + str(mu) + ', \sigma=' + str(sigma) + ', N=' + str(N) + '$', loc='left') # タイトル
plt.grid() # グリッド線
plt.show() # 描画

# サンプルのヒストグラムを作成
plt.figure(figsize=(12, 9)) # 図の設定
plt.hist(x=x_n, bins=50, range=(x_vals.min(), x_vals.max()), density=True, color='#00A968') # ヒストグラム
plt.plot(x_vals, density, color='green', linestyle='--') # 元の分布
plt.xlabel('x') # x軸ラベル
plt.ylabel('density') # y軸ラベル
plt.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル
plt.title('$\mu=' + str(mu) + ', \sigma=' + str(sigma) + ', N=' + str(N) + '$', loc='left') # タイトル
plt.grid() # グリッド線
plt.show() # 描画

#%%

## アニメーションによる可視化:(頻度)

# フレーム数を指定
N_frame = 100

# 図を初期化
fig = plt.figure(figsize=(12, 9)) # 図の設定
fig.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル

# y軸(頻度)の最大値を設定
freq_max = np.max(
    np.histogram(a=x_n[:N_frame], bins=30, range=(x_vals.min(), x_vals.max()))[0],
) + 1.0

# 作図処理を関数として定義
def update(n):
    # 前フレームのグラフを初期化
    plt.cla()

    # サンプルのヒストグラムを作成
    plt.hist(x=x_n[:(n+1)], bins=50, range=(x_vals.min(), x_vals.max()), color='#00A968', zorder=1) # ヒストグラム
    plt.scatter(x=x_n[n], y=0.0, color='orange', s=100, zorder=2) # サンプル
    plt.xlabel('x') # x軸ラベル
    plt.ylabel('freqency') # y軸ラベル
    plt.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル
    plt.title('$\mu=' + str(mu) + ', \sigma=' + str(sigma) +
              ', N=' + str(n + 1) + '$', loc='left') # タイトル
    plt.grid() # グリッド線
    plt.ylim(ymin=-0.5, ymax=freq_max) # y軸の表示範囲

# gif画像を作成
anime_freq = FuncAnimation(fig, update, frames=N_frame, interval=100)

# gif画像を保存
anime_freq.save('ProbabilityDistribution/Gaussian_freq.gif')

#%%

## アニメーションによる可視化:(密度)

# フレーム数を指定
N_frame = 100

# 図を初期化
fig = plt.figure(figsize=(12, 9)) # 図の設定
fig.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル

# 作図処理を関数として定義
def update(n):
    # 前フレームのグラフを初期化
    plt.cla()

    # サンプルのヒストグラムを作成
    plt.hist(x=x_n[:(n+1)], bins=50, range=(x_vals.min(), x_vals.max()), density=True, color='#00A968', zorder=1) # ヒストグラム
    plt.plot(x_vals, density, color='green', linestyle='--', zorder=2) # 元の分布
    plt.scatter(x=x_n[n], y=0.0, color='orange', s=100, zorder=3) # サンプル
    plt.xlabel('x') # x軸ラベル
    plt.ylabel('density') # y軸ラベル
    plt.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル
    plt.title('$\mu=' + str(mu) + ', \sigma=' + str(sigma) +
              ', N=' + str(n + 1) + '$', loc='left') # タイトル
    plt.grid() # グリッド線
    plt.ylim(ymin=-0.01, ymax=density.max() + 0.1) # y軸の表示範囲

# gif画像を作成
anime_ｐrop = FuncAnimation(fig, update, frames=N_frame, interval=100)

# gif画像を保存
anime_prop.save('ProbabilityDistribution/Gaussian_prop.gif')

#%%

### 分布の生成

## パラメータの生成

# 超パラメータを指定
mu_prior = 1.0
sigma_prior = 2.5

# サンプルサイズを指定
N = 10

# 1次元ガウス分布の平均パラメータを生成
mu_n = np.random.normal(loc=mu_prior, scale=sigma_prior, size=N)

# 標準偏差パラメータを指定
sigma = 1.0

# 平均パラメータを計算
E_mu = mu_prior

# 作図用のxの点を作成
x_vals = np.linspace(E_mu - sigma*5.0, E_mu + sigma*5.0, num=250)

# 平均パラメータの期待値による1次元ガウス分布の確率密度を計算
E_dens = norm.pdf(x=x_vals, loc=E_mu, scale=sigma)

#%%

## 分布の作図

# サンプルによる分布を作図
plt.figure(figsize=(12, 9)) # 図の設定
plt.plot(x_vals, E_dens, color='blue', linestyle='--', label='$E[\mu]=' + str(E_mu) + '$') # 期待値による分布
for n in range(N):
    tmp_dens = norm.pdf(x=x_vals, loc=mu_n[n], scale=sigma)
    plt.plot(x_vals, tmp_dens, alpha=0.5, label='$\mu=' + str(np.round(mu_n[n], 2)) + '$') # サンプルによる分布
plt.xlabel('x') # x軸ラベル
plt.ylabel('density') # y軸ラベル
plt.suptitle('Gaussian Distribution', fontsize=20) # 全体のタイトル
plt.title('$\mu_{pri}=' + str(mu_prior) + ', \sigma_{pri}=' + str(sigma_prior) +
          ', \sigma=' + str(sigma) + ', N=' + str(N) + '$', loc='left') # タイトル
plt.legend() # 凡例
plt.grid() # グリッド線
plt.show() # 描画

#%%


import numpy as np
import cv2 as cv
import scipy.signal as sp
import matplotlib.pyplot as plt

def gauss(dim, sigma):
    # Takes the dimensions of the desired gauss filter and the sigma and returns the filter
    # Dim must be odd
    # Filter has been scaled to add to 1 to preserve image brightness
    ret = np.zeros(shape=(dim, dim))
    start = -(dim // 2)
    for i in range(0, dim):
        for j in range(0, dim):
            ret[i][j] = (1 / (2 * np.pi * (np.square(sigma)))) * (
                np.power(np.e, (-(np.square(start + i) + np.square(start + j)) / (2 * np.square(sigma)))))
    return ret * 1/(np.sum(ret))

def laplace_gauss(dim, sigma):
    # Takes the dimensions of the desired Laplacian of Gaussian filter and returns the filter
    # Dim must be odd
    # Source of LoG function: http://fourier.eng.hmc.edu/e161/lectures/gradient/node8.html
    ret = np.zeros(shape=(dim, dim))
    start = -(dim // 2)
    for i in range(0, dim):
        for j in range(0, dim):
            ret[i][j] = (np.square(start + i) + np.square(start + j) - 2*np.square(sigma))/(np.power(sigma, 4)) * \
                        (np.power(np.e, (-(np.square(start + i) + np.square(start + j))/(2*np.square(sigma)))))
    return ret

def detect_zero(img):
    # Takes in the Laplacian filtered image and detects the zero crossings
    # Algorithm sources:
    # https://stackoverflow.com/questions/22050199/python-implementation-of-the-laplacian-of-gaussian-edge-detection
    # https://theailearner.com/tag/zero-crossings/
    # Code was written by me after looking at these algorithms, and my algorithm uses parts of both
    thresh = np.absolute(img).mean()
    height = len(img)
    width = len(img[0])
    ret = np.zeros(shape=(height, width))
    for i in range(1, height-1):
        for j in range(1, width-1):
            patch = img[i-1:i+2, j-1:j+2]
            minp = patch.min()
            maxp = patch.max()
            p = img[i][j]
            if p < 0 < maxp and maxp - minp > thresh:
                ret[i][j] = np.abs(p) + maxp if np.abs(p) + maxp > thresh else 0
            elif minp < 0 < p and maxp - minp > thresh:
                ret[i][j] = p + np.abs(minp) if p + np.abs(minp) > thresh else 0
    return ret.astype(np.uint8)

def plot_array(kernel, title):
    # Plot a 2D kernel as a colour map
    # Source of function:
    # https://matplotlib.org/3.1.1/gallery/images_contours_and_fields/image_annotated_heatmap.html
    fig, ax = plt.subplots()
    im = ax.imshow(kernel)

    for i in range(len(kernel)):
        for j in range(len(kernel)):
            text = ax.text(j, i, round(kernel[i, j], 2),
                           ha="center", va="center", color="w")
    ax.set_title(title)
    fig.tight_layout()
    plt.show()

def convolve(img, kernel):
    # Convolve and image with the given kernel
    return sp.convolve2d(img.astype(float), kernel)

def convolve_display(img, kernel, title):
    # Perform the convolution and display the image
    cv.imshow(title, convolve(img, kernel).astype(np.uint8))

if __name__ == '__main__':
    paolina = cv.cvtColor(cv.imread('images/Paolina.jpg'), cv.COLOR_BGR2GRAY)
    dog = cv.cvtColor(cv.imread('images/dog.png'), cv.COLOR_BGR2GRAY)
    # Image source for dog picture:
    # https://www.google.com/url?sa=i&url=http%3A%2F%2Fpets.university%2Fi-love-dogs%2F&psig=AOvVaw2-Xgkpnat2Z2c3wkaX4pNv&ust=1590948869508000&source=images&cd=vfe&ved=0CAIQjRxqFwoTCKikmMeY3OkCFQAAAAAdAAAAABAW

    # Question 4
    # convolve_display(paolina, gauss(5, 1), "Gaussian sigma=1")
    # convolve_display(paolina, gauss(11, 2), "Gaussian sigma=2")
    plot_array(gauss(5, 1), "Gaussian 5x5, sigma=1")
    plot_array(gauss(11, 2), "Gaussian 11x11, sigma=2")

    # Question 5
    log1 = laplace_gauss(7, 1)
    log2 = laplace_gauss(13, 1.5)
    log3 = laplace_gauss(21, 3)
    plot_array(log1, "Laplacian of Gaussian 7x7, sigma=1")
    plot_array(log2, "Laplacian of Gaussian 13x13, sigma=1.5")

    # Question 6
    paolina_log1 = convolve(paolina, log1)
    paolina_log3 = convolve(paolina, log3)
    dog_log1 = convolve(dog, log1)
    dog_log3 = convolve(dog, log3)

    # Question 7
    t = detect_zero(paolina_log1)
    cv.imshow("Paolina", paolina)
    cv.imshow("dog", dog)
    cv.imshow("Paolina with sigma=1", detect_zero(paolina_log1))
    cv.imshow("Paolina with sigma=3", detect_zero(paolina_log3))
    cv.imshow("Dog with sigma=1", detect_zero(dog_log1))
    cv.imshow("Dog with sigma=3", detect_zero(dog_log3))

    k = cv.waitKey(0)


from numpy.core.numeric import Inf
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from sklearn.cluster import DBSCAN

X = [2, 8, 0, 7, 6]
Y = [0, 4, 6, 2, 1]
labels = ["x1", "x2", "x3", "x4", "x5"]
hdata = pd.DataFrame({"X": X, "Y": Y}, index=labels)

plt.scatter(hdata.X, hdata.Y)
for i in range(len(hdata.index)):
    plt.text(hdata.loc[labels[i], "X"], hdata.loc[labels[i], "Y"], '%s' % (str(labels[i])), size=15, zorder=1)
plt.show()

clustering = AgglomerativeClustering(n_clusters=None, linkage="single", distance_threshold=0).fit(hdata)
linkage_matrix = np.column_stack([clustering.children_, clustering.distances_, np.ones(len(hdata.index)-1)]).astype(float)
dendrogram(linkage_matrix, labels=labels)
plt.show()

clustering = AgglomerativeClustering(n_clusters=None, linkage="complete", distance_threshold=0).fit(hdata)
linkage_matrix = np.column_stack([clustering.children_, clustering.distances_, np.ones(len(hdata.index)-1)]).astype(float)
dendrogram(linkage_matrix, labels=labels)
plt.show()

clustering = AgglomerativeClustering(n_clusters=2, linkage="complete").fit(hdata)
plt.scatter(hdata.X, hdata.Y, c=clustering.labels_, cmap="bwr")
for i in range(len(hdata.index)):
    plt.text(hdata.loc[labels[i], "X"], hdata.loc[labels[i], "Y"], '%s' % (str(labels[i])), size=15, zorder=1)
plt.show()

europeData = pd.read_csv("./europe.txt")

scaler = StandardScaler()
scaler = scaler.fit(europeData)
europe = pd.DataFrame(scaler.transform(europeData), columns=europeData.columns, index=europeData.index)

clustering = AgglomerativeClustering(n_clusters=None, linkage="complete", distance_threshold=0).fit(europe)
linkage_matrix = np.column_stack([clustering.children_, clustering.distances_, np.ones(len(europe.index)-1)]).astype(float)
dendrogram(linkage_matrix, labels=europe.index)
plt.show()

slc = []
for i in range(2, 21):
    clustering = AgglomerativeClustering(n_clusters=i, linkage="complete").fit(europe)
    slc.append(silhouette_score(europe, clustering.labels_))

plt.plot(range(2, 21), slc)
plt.xticks(range(2, 21), range(2, 21))
plt.show()

clustering = AgglomerativeClustering(n_clusters=7, linkage="complete").fit(europe)

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.scatter(europeData.GDP, europeData.Inflation, europeData.Unemployment, c=clustering.labels_, cmap="bwr")
for i in range(len(europeData.index)):
    ax.text(europeData.loc[europeData.index[i], "GDP"], europeData.loc[europeData.index[i], "Inflation"], europeData.loc[europeData.index[i], "Unemployment"], '%s' % (str(europeData.index[i])), size=10, zorder=1)
ax.set_xlabel('GDP')
ax.set_ylabel('Inflation')
ax.set_zlabel('Unemployment')
plt.show()

print(silhouette_score(europe, clustering.labels_))

X = [2, 2, 8, 5, 7, 6, 1, 4]
Y = [10, 5, 4, 8, 5, 4, 2, 9]
labels = ["x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"]
ddata = pd.DataFrame({"X": X, "Y": Y}, index=labels)

plt.scatter(ddata.X, ddata.Y)
for i in range(len(ddata.index)):
    plt.text(ddata.loc[labels[i], "X"], ddata.loc[labels[i], "Y"], '%s' % (str(labels[i])), size=15, zorder=1)
plt.show()

clustering = DBSCAN(eps=2, min_samples=2).fit(ddata)

clusters = clustering.labels_
plt.scatter(ddata.X, ddata.Y, c=clusters, cmap="spring")
for i in range(len(ddata.index)):
    plt.text(ddata.loc[labels[i], "X"], ddata.loc[labels[i], "Y"], '%s' % (str(labels[i])), size=15, zorder=1)
plt.title("DBSCAN(eps=2, minPts=2)")
plt.show()

clustering = DBSCAN(eps=3.5, min_samples=2).fit(ddata)
clusters = clustering.labels_
plt.scatter(ddata.X, ddata.Y, c=clusters, cmap="spring")
for i in range(len(ddata.index)):
    plt.text(ddata.loc[labels[i], "X"], ddata.loc[labels[i], "Y"], '%s' % (str(labels[i])), size=15, zorder=1)
plt.title("DBSCAN(eps=3.5, minPts=2)")
plt.show()

mdata = pd.read_csv("./mdata.txt")

# plt.scatter(mdata.X, mdata.Y, marker="o")
# plt.show()

from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=2).fit(mdata)
plt.scatter(mdata.X, mdata.Y, c=kmeans.labels_)
plt.show()

from sklearn.neighbors import NearestNeighbors
nbrs = NearestNeighbors(n_neighbors=10).fit(mdata)
distances, indices = nbrs.kneighbors(mdata)
distanceDec = sorted(distances[:, 9])
plt.plot(distanceDec)
plt.ylabel("10-NN Distance")
plt.xlabel("Points sorted by distance")
plt.show()

clustering = DBSCAN(eps=0.4, min_samples=10).fit(mdata)
plt.scatter(mdata.X, mdata.Y, c=clustering.labels_)
plt.show()

Lab/8. Hierarchical Clustering - DBSCAN/8. Hierarchical Clustering - DBSCAN.py


from matplotlib import pylab
import pylab as plt
import numpy as np
import os,sys

"""
A function used for neural network activation
in artifacial intelligence, machine learning
"""

def sigmoid(x):
    #f(x)=1/(1+e^(-x))
    return (1 / (1 + np.exp(-x)))

def sigmoidplot():
    x = plt.linspace(-10,10,5)
    y = plt.linspace(-10,10,10)
    z = plt.linspace(-10,10,100)

    plt.plot(x, sigmoid(x), 'r', label='linspace(-10,10,5)')
    plt.plot(y, sigmoid(y), 'b', label='linspace(-10,10,10)')
    plt.plot(z, sigmoid(z), 'y', label='linspace(-10,10,100)')

    plt.title('Sigmoid Function')
    plt.suptitle('Math')
    plt.grid()
    plt.legend(loc='lower right')
    plt.text(-9,0.8, r'$\sigma(x)=\frac{1}{1+e^{-x}}$', fontsize=15)
    plt.gca().xaxis.set_major_locator(plt.MultipleLocator(2))
    plt.gca().yaxis.set_major_locator(plt.MultipleLocator(0.1))
    plt.xlabel('X Axis')
    plt.ylabel('Y Axis')

    plt.show()

def main():
    if "-a" in sys.argv[1:]:
        for i in range(-5,5):
           print(sigmoid(i))
    elif "-p" in sys.argv[1:]:
        sigmoidplot()
    else:
        for i in range(-5,5):
           print(sigmoid(i))
        sigmoidplot()

if __name__=="__main__":
    main()


"""
https://projecteuler.net/problem=5
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.

What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
"""
from numpy import prod

from Common.Logger import get_logger, init_logger
from Common.Numbers import prime_factorization
from Common.Utilities import performance_run

PERFORMANCE_RUNS = 100_000
UPPER_BOUND = 20

def fastest(ceiling: int = UPPER_BOUND) -> int:
    """
    This algorithm finds the prime factorization of every number between 1 and ceiling, maintains the greatest counts of
    each prime factor found, and then multiplies them out at the end
    :param ceiling: The upper bound (inclusive) of the factor for which to find the evenly-divided quotient
    :return: The quotient evenly-divisible by every number from 1 to ceiling
    """
    return prime_tally(ceiling)

def prime_tally(ceiling: int = UPPER_BOUND) -> int:
    """
    This algorithm finds the prime factorization of every number between 1 and ceiling, maintains the greatest counts of
    each prime factor found, and then multiplies them out at the end
    --> benchmark: 104 ms/run
    :param ceiling: The upper bound (inclusive) of the factor for which to find the evenly-divided quotient
    :return: The quotient evenly-divisible by every number from 1 to ceiling
    """
    factorization_merge = []

    for number in range(1, ceiling+1):
        number_factorization = prime_factorization(number)
        for factor in set(number_factorization):
            factor_count_difference = number_factorization.count(factor) - factorization_merge.count(factor)
            if factor_count_difference > 0:
                factorization_merge += [factor] * factor_count_difference

    return prod(factorization_merge)

if __name__ == "__main__":
    # Log stuff
    init_logger()
    logger = get_logger()

    # Performance run
    # performance_run(prime_tally, iterations=PERFORMANCE_RUNS)()
    print(fastest(UPPER_BOUND))


import numpy as np

def vecNum(vec):
    return int("".join(str(int(n)) for n in vec), base=2)

def printVec(vec, width):
    return "0b{:0{}b},".format(vecNum(vec), width)

def printBinary(mat, width):
    for vec in mat:
        print(printVec(vec, width))

def genToParityCheck(genParity):
    return np.hstack((genParity.transpose(), np.eye(genParity.shape[1])))

def genToAltParityCheck(genParity):
    return np.hstack((np.eye(genParity.shape[0]), genParity))

# This was pieced together from the P25 and DMR standards, as well as appendix Q of the
# IRIG standard.
parity = np.array([
    [1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1],
    [0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1],
    [1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0],
    [0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0],
    [1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1],
    [0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1],
    [0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1],
    [1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0],
    [1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1],
    [1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0],
    [1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1],
])
gen = np.hstack((np.eye(12), parity))

print("core transpose:")
printBinary(parity.transpose(), 12)

print("core:")
printBinary(parity, 12)

parityCheck = genToParityCheck(parity)
print("parity check:")
printBinary(parityCheck, 24)

parityCheck = genToAltParityCheck(parity)
print("alt parity check:")
printBinary(parityCheck, 24)

# Verify self-dual property.
for r in range(0, 12):
    for q in range(r, 12):
        dot = (gen[r] @ gen[q]) % 2
        assert dot == 0.0


# Importing the libraries
import numpy as np
import pandas as pd
from pandas_datareader import data as web
import matplotlib.pyplot as plt
import seaborn as sns
import scipy
from scipy.stats import norm
%matplotlib notebook

# Pulling the data for AAPL (I have used a CSV file and data from 01/01/2021 - 08/27/2021)
df = pd.read_csv("C:/Users/vgupt/Desktop/Website Project Folder/MCS/AAPL.csv")['Adj Close']
df

#Ploting the prices on a graph
plt.xlabel("Date")
plt.ylabel("Price")
df.plot(figsize=(15,6))

# Calulating the logarithmic returns returns of AAPL stock
log_return = np.log(1 + df.pct_change())
log_return

# Computing the drift
u = log_return.mean()
v = log_return.var()

drift = u - (0.5*v)
drift

# Computing the variance and Daily Returns
stdev = log_return.std()
days = 50  #We are predicting AAPL's stock prices 50 days into the future
num_simulations = 1000
Z = norm.ppf(np.random.rand(days,num_simulations))
Z

daily_returns = np.exp(drift + stdev*Z)

plt.plot(daily_returns)

# Calulating the stock price for every trial
price_paths = np.zeros_like(daily_returns)
price_paths[0] = df.iloc[-1]
for t in range(1, days):
    price_paths[t] = price_paths[t-1]*daily_returns[t]

price_paths

plt.plot(price_paths)

# Graph link
![image](https://user-images.githubusercontent.com/83883988/131385949-df5dba5a-b21f-467a-a6a6-5bc9b3d3cba9.png)


# -*- coding: utf-8 -*-
"""
Created on Mon Nov 20 23:06:49 2017

@author: Grant

An implementation of the Thomas algorithm in Python, using just-in-time
compiling from numba for additional speed
"""

import numpy as np
from numba import njit, f8

def solve(A, d):
    '''Helper function for Thomas algorith. Breaks matrix into tridiagonal
    elements for easier processing by algorithm. '''

    # pass numba float64 dtype np.arrays to the solve function - need to
    # perform this step to allow for nopython execution of thomas algorithm
    # which yields maximum speed
    a = f8(np.diagonal(A, offset=0))
    b = f8(np.diagonal(A, offset=1))
    c = f8(np.diagonal(A, offset=-1))
    dfloat = f8(d)

    D = np.diag(a, 0) + np.diag(b, 1) + np.diag(c, -1) #create test matrix

    # test if D is 'close enough' to A - if not that means A was not
    # tridiagonal and the function raises an exception
    if not np.allclose(A, D):
        raise Exception('The given A is not tridiagonal')

    # pass to thomas algorithm solver
    x = solve_body(a, b, c, dfloat)

    return x

# chose to use njit decorator to force nopython implementation and
# get faster speed. Downside is I lose flexibility in input of solver, must
# wrap in another function which will format data correctly
@njit
def solve_body(a, b, c, d):
    ''' Thomas algorithm to solve a tridiagonal system of equations

    INPUTS
    ========
    a: numpy array
        the diagonal entries
    b: numpy array
        the superdiagonal entries
    c: numpy array
        the subdiagonal entries
    d: numpy array
        the right-hand side of the system of equations

    RETURNS
    ========
    The solution for the given tri-diagonal system of equations.
    '''

    n = len(a) # determine number of equations in system

    #initialize
    alpha = np.zeros(n)
    beta = np.zeros(n)
    alpha[0] = a[0]
    beta[0] = d[0]

    # first (forward) loop to zero c[i]'s
    for i in range(1, n, 1):
        # in python, c's index is from 0 to n-2, not 1 to n-1, have to subtract 1
        alpha[i] = a[i] - (b[i-1] * c[i-1]) / alpha[i-1]
        beta[i] = d[i] - (beta[i-1] * c[i-1]) / alpha[i-1]

    #initialize and set last step
    x = np.zeros(n)
    x[n-1] = beta[n-1] / alpha[n-1]

    # second (backwards) loop to find solutions
    for j in range(n-2, -1, -1): #indices are weird, want to step from n-2 to 0
        x[j] = (beta[j] - b[j-1] * x[j+1]) / alpha[j]

    return x


import numpy

def functional_iteration(f, x0, max_steps=100, tol=1e-10):
    N = len(x0)
    x = numpy.zeros((max_steps+1,N))
    x[0,:] = x0
    step = 0
    g = lambda x : x - f(x)
    while numpy.linalg.norm(f(x[step])) > tol and step < max_steps:
        step = step + 1
        x[step,:] = g(x[step-1,:])
    return x[:step+1,:]

def newton(f, df, x0, max_steps=100, tol=1e-10):
    N = len(x0)
    x = numpy.zeros((max_steps+1,N))
    x[0,:] = x0
    step = 0
    while numpy.linalg.norm(f(x[step])) > tol and step < max_steps:
        step = step + 1
        fx = f(x[step-1, :])
        J = df(x[step-1, :])
        c = numpy.linalg.solve(J, -fx)
        x[step,:] = x[step-1, :] + c
    return x[:step+1,:]

if __name__=="__main__":
    def f(x):
        return numpy.array([  x[0]**2 +    x[1]**2 - 1,
                            5*x[0]**2 + 21*x[1]**2 - 9])
    def df(x):
        return numpy.array([[ 2*x[0],  2*x[1]],
                            [10*x[0], 42*x[1]]])

    print("Exact solution is", numpy.array([numpy.sqrt(3)/2, 1/2]))
    x = functional_iteration(f, numpy.array([1, 1]))
    print("Functional iteration", x[-1, :])
    x = newton(f, df, numpy.array([1, 1]))
    print("Newton", x[-1, :], "iterations", len(x))


"""FFT Scratch
    Reference.
        Kor, proof of formula,
          https://ghebook.blogspot.com/2020/09/dft-discrete-fourier-transform.html
        Eng, Cooley and Tukey,
          http://jakevdp.github.io/blog/2013/08/28/understanding-the-fft/
"""
import time
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, dct, ifft, idct

"""Base on formula"""

def fft_scratch(f, M=0):
    """fft, exactly dft
        fft formula
          F(w) = integ_{-inf}^inf f(t) * exp(-j * w * t) dt
        dft formula
          F_k = sigma_{m=0}^{M-1} f_m * exp(-j * 2 * pi * m * k / M)
    """
    if M == 0:
        M = len(f)
    fft_result = np.zeros(M, dtype=np.complex128)
    for k in range(len(fft_result)):
        sumation_f = 0
        for m in range(len(f)):
            sumation_f += f[m] * np.exp(-1j * 2 * np.pi * m * k / M)
        fft_result[k] = sumation_f

    return fft_result

def ifft_scratch(f, M=0):
    """fft, exactly dft
        ifft formula
          f(t) = (1/2*pi)*integ_{-inf}^inf F(w) * exp(j * w * t) dw
        idft formula
          f_k  = (1/M)   *sigma_{m=0}^{M-1} F_m * exp(j * 2 * pi * m * k / M)
    """
    if M == 0:
        M = len(f)
    ifft_result = np.zeros(M, dtype=np.complex128)
    for k in range(len(ifft_result)):
        sumation_f = 0
        for m in range(len(f)):
            sumation_f += f[m] * np.exp(1j * 2 * np.pi * m * k / M)
        ifft_result[k] = sumation_f / M

    return ifft_result

def dct_scratch(f, M=0):
    """
        DCT-1, T = 2M-2
        Fk  = sigma_{m=0}^{2M-3}*f_m*e^{-j*pi*k/(M-1)*m}
            = f_0 + (-1)^k*f_{M-1} + 2*sigma_{m=1}^{M-2}*f_m*cos(pi*k*(m-1)/(M-1)*m)

        DCT-1, Xk = Fk/2

        DCT-2, T = 4M
        F_k = 2*sigma_{m=0}^{M-1}*f_{2m+1}*cos(pi*k/2M*m)
    """
    raise NotImplementedError

def idct_scratch(f, M=0):
    """
        iDCT-1
        x_m = 1/(M-1) * [X_0 + (-1)^m*X_{M-1} + 2*sigma_{m=1}^{M-2}*X_m*cos(pi*m*(m-1)/(M-1)*m)] ]
    """
    raise NotImplementedError

"""Base. Cooley and Tukey """

def DFT_slow(x):
    """Compute the discrete Fourier Transform of the 1D array x"""
    x = np.asarray(x, dtype=float)
    N = x.shape[0]
    n = np.arange(N)
    k = n.reshape((N, 1))
    M = np.exp(-2j * np.pi * k * n / N)
    return np.dot(M, x)

def FFT(x):
    """A recursive implementation of the 1D Cooley-Tukey FFT
        Big O: O(NlogN)
    """

    x = np.asarray(x, dtype=float)
    N = x.shape[0]

    if N % 2 > 0:
        raise ValueError("size of x must be a power of 2")
    elif N <= 32:  # this cutoff should be optimized
        return DFT_slow(x)
    else:
        X_even = FFT(x[::2])
        X_odd = FFT(x[1::2])
        factor = np.exp(-2j * np.pi * np.arange(N) / N)
        return np.concatenate(
            [X_even + factor[: N // 2] * X_odd, X_even + factor[N // 2 :] * X_odd]
        )

def FFT_vectorized(x):
    """A vectorized, non-recursive version of the Cooley-Tukey FFT"""
    x = np.asarray(x, dtype=float)
    N = x.shape[0]

    if np.log2(N) % 1 > 0:
        raise ValueError("size of x must be a power of 2")

    # N_min here is equivalent to the stopping condition above,
    # and should be a power of 2
    N_min = min(N, 32)

    # Perform an O[N^2] DFT on all length-N_min sub-problems at once
    n = np.arange(N_min)
    k = n[:, None]
    M = np.exp(-2j * np.pi * n * k / N_min)
    X = np.dot(M, x.reshape((N_min, -1)))

    # build-up each level of the recursive calculation all at once
    while X.shape[0] < N:
        X_even = X[:, : X.shape[1] // 2]
        X_odd = X[:, X.shape[1] // 2 :]
        factor = np.exp(-1j * np.pi * np.arange(X.shape[0]) / X.shape[0])[:, None]
        X = np.vstack([X_even + factor * X_odd, X_even - factor * X_odd])

    return X.ravel()

def hfft_scratch(f, M=0):
    raise NotImplementedError

def ihfft_scratch(f, M=0):
    raise NotImplementedError

def rfft_scratch(f, M=0):
    raise NotImplementedError

def irfft_scratch(f, M=0):
    raise NotImplementedError

if __name__ == "__main__":
    print("Checking function operation...")
    print("-" * 40)

    x = np.random.random(1024)
    print(np.allclose(fft_scratch(x), np.fft.fft(x)))
    print(np.allclose(DFT_slow(x), np.fft.fft(x)))
    print(np.allclose(fft(x), np.fft.fft(x)))
    print(np.allclose(FFT(x), np.fft.fft(x)))
    print(np.allclose(FFT_vectorized(x), np.fft.fft(x)))

    print("-" * 40)

    x = np.random.random(1024)
    curr_time = time.perf_counter()

    fft_scratch(x)
    print("fft_scratch:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    DFT_slow(x)
    print("DFT_slow:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    FFT(x)
    print("FFT:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    FFT_vectorized(x)
    print("FFT_vectorized:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    fft(x)
    print("scipy fft:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    np.fft.fft(x)
    print("numpy fft:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    np.fft.rfft(x)
    print("numpy rfft:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    np.fft.hfft(x)
    print("numpy hfft:", time.perf_counter() - curr_time)

    print("-" * 40)

    x = np.random.random(1024 * 16)
    curr_time = time.perf_counter()

    FFT(x)
    print("FFT:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    FFT_vectorized(x)
    print("FFT_vectorized:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    fft(x)
    print("scipy fft:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    np.fft.fft(x)
    print("numpy fft:", time.perf_counter() - curr_time)
    curr_time = time.perf_counter()

    # In jupyter notebook
    # %timeit DFT_slow(x)
    # %timeit FFT(x)
    # %timeit np.fft.fft(x)

    print("Check function operation in frequency domain...")
    fs = 1000
    total_time = 0.1
    frequency = 100
    sample_list = np.arange(0, total_time * fs)
    a_0 = np.sin(2 * np.pi * frequency * sample_list / fs)
    a_1 = np.sin(2 * np.pi * 2 * frequency * sample_list / fs)
    a = (a_0 + a_1) / 2
    n = len(a)
    bins = np.arange(0, n) * fs / n

    a_fft_scratch = fft_scratch(a)
    a_fft_numpy = np.fft.fft(a)
    a_fft_scipy = fft(a)

    a_ifft_scratch = ifft_scratch(a_fft_scratch)
    a_ifft_numpy = np.fft.ifft(a_fft_numpy)
    a_ifft_scipy = ifft(a_fft_scipy)

    a_dct_scipy = dct(a)
    a_idct_scipy = idct(a_dct_scipy)

    # plt.plot(bins, np.abs(a_fft_numpy/n), "o")
    # plt.plot(bins, np.abs(a_fft_scipy/n), "x")
    # try:
    #     plt.plot(bins, np.abs(a_fft_scratch/n), ".", color="r")
    # except ValueError:
    #     pass
    # plt.xticks(np.arange(0, 1100, 100))
    # plt.yticks(np.arange(-1, 1.2, 0.2))
    # plt.grid()
    # plt.show()


# This code shows how a linear regression analysis can be applied to a 2-dimensional data
# Implementation here is based on the theory described in the jupyter notebook.

# Code Flow:
    # 1. Import all relevant libraries.
    # 2. Generate sample data & save it as a csv file (Stored as a csv file just to use pandas).
    # 3. Load the dataset using pandas (X - inputs/feature, Y - output/target).
    # 4. Plot the generated data understand the trend.
    # 5. Calculate weights (parameters - a & b) using the equation from the theory lecture.
    # 6. Calculate Yhat from the weights above. Yhat = a*X + b.
    # 7. Calculate R-squared using the equation from the theory lecture to validate the model.

# 1.Imports:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd

# 2.Generate sample data:
N = 100
w = np.array([2, 3])
with open('data_2d.csv', 'w') as f:
    X = np.random.uniform(low=0, high=100, size=(N,2))
    Y = np.dot(X, w) + 1 + np.random.normal(scale=5, size=N)
    for i in range(N):
        f.write("%s,%s,%s\n" % (X[i,0], X[i,1], Y[i]))

# 3.Load the data:
df = pd.read_csv('data_2d.csv',header = None)
df['ones'] = np.ones(len(X))
X = df[[0,1,'ones']].as_matrix()
Y = df[2].values

# 4.Plot the data:
fig = plt.figure(1)
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X[:,0], X[:,1], Y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()

# 5.Model: Y = a*X + B
# Apply the equations from the jupyter notebook to calculate a & b:
# Denominator is same for both a & b
w = np.linalg.solve(np.dot(X.T,X),np.dot(X.T,Y))

# 6.Predict Y:
Yhat = np.dot(X,w)

# 7.R-squared:
d1 = Y - Yhat
d2 = Y - Y.mean()
r2 = 1 - d1.dot(d1)/d2.dot(d2)

print('the r-squared is {}'.format(r2))

1.Linear Regression/1.Code - Using Theory/1.2D - Regression.py


# -*- coding: utf-8 -*-
"""
Created on Wed Mar 27 15:10:11 2019

@author: UO270318
"""

################### DERIVADAS #########################

import numpy as np
import matplotlib.pyplot as plt

a=0
b=1
h=0.1
h2=0.01
f= lambda x: np.exp(x)
df= lambda x: np.exp(x)
xp=np.arange(a,b+h,h)
num_puntos=1/h +1
### DERIVADA PROGRESIVA ###
def df_p(f,x0,h):
    return (f(x0+h)-f(x0))/h

### DERIVADA REGRESIVA ###
def df_r(f,x0,h):
    return (f(x0)-f(x0-h))/h

### DERIVADA NUMERICA CENTRADA ###
def df_c(f,x0,h):
    return ((df_p(f,x0,h)+df_r(f,x0,h))/2)

###################### EJERCICIO 1 #######################

### CON H=01 ###
plt.plot(xp[1:-1],df(xp[1:-1]),label='derivada exacta') #evaluar de 0.1 a 0.9
plt.plot(xp[1:-1],df_p(f,xp[1:-1],h),label='derivada progresiva')
plt.plot(xp[1:-1],df_r(f,xp[1:-1],h),label='derivada regresiva')
plt.plot(xp[1:-1],df_c(f,xp[1:-1],h),label='derivada centrada')
plt.legend()
plt.title('Derivada con h=0.1')
plt.show()

errorP=abs(df(xp[1:-1])-df_p(f,xp[1:-1],h))
errorR=abs(df(xp[1:-1])-df_r(f,xp[1:-1],h))
errorC=abs(df(xp[1:-1])-df_c(f,xp[1:-1],h))

plt.plot(xp[1:-1],errorP,label='derivada progresiva')
plt.plot(xp[1:-1],errorR,label='derivada regresiva')
plt.plot(xp[1:-1],errorC,label='derivada centrada')
plt.legend()
plt.title('Error con h=0.1')
plt.show()

### CON H=0.01 ###
plt.plot(xp[1:-1],df(xp[1:-1]),label='derivada exacta') #evaluar de 0.1 a 0.9
plt.plot(xp[1:-1],df_p(f,xp[1:-1],h2),label='derivada progresiva')
plt.plot(xp[1:-1],df_r(f,xp[1:-1],h2),label='derivada regresiva')
plt.plot(xp[1:-1],df_c(f,xp[1:-1],h2),label='derivada centrada')
plt.legend()
plt.title('Derivada con h=0.01')
plt.show()

errorP2=abs(df(xp[1:-1])-df_p(f,xp[1:-1],h2))
errorR2=abs(df(xp[1:-1])-df_r(f,xp[1:-1],h2))
errorC2=abs(df(xp[1:-1])-df_c(f,xp[1:-1],h2))

plt.plot(xp[1:-1],errorP2,label='derivada progresiva')
plt.plot(xp[1:-1],errorR2,label='derivada regresiva')
plt.plot(xp[1:-1],errorC2,label='derivada centrada')
plt.legend()
plt.title('Error con h=0.01')
plt.show()

### ERROR RELATIVO ###
errorRelP= np.linalg.norm(df(xp[1:-1])-df_p(f,xp[1:-1],h))/np.linalg.norm(df(xp[1:-1]))
errorRelR= np.linalg.norm(df(xp[1:-1])-df_r(f,xp[1:-1],h))/np.linalg.norm(df(xp[1:-1]))
errorRelC= np.linalg.norm(df(xp[1:-1])-df_c(f,xp[1:-1],h))/np.linalg.norm(df(xp[1:-1]))

print('relativa progresiva',errorRelP)
print('relativa regresiva',errorRelR)
print('relativa centrada',errorRelC)

###################### EJERCICIO 2 #######################

def dfOrden2_p(f,x0,h):
    return (-3*f(x0)+4*f(x0+h)-f(x0+2*h))/(2*h)

def dfOrden2_r(f,x2,h):
    return (f(x2-2*h)-4*f(x2-h)+3*f(x2))/(2*h)

f= lambda x: 1/x
df= lambda x: -1/x**2
a=0.2
b=1.2
h=0.01
xp=np.arange(a,b+h,h)
dfa=np.zeros(len(xp))
dfa[0]=df_p(f,xp[0],h)
dfa[-1]=df_r(f,xp[-1],h)
dfa[1:-1]=df_c(f,xp[1:-1],h)
plt.plot(xp,dfa,label='derivada aproximada')
plt.legend()
plt.show()

dfb=np.zeros(len(xp))
dfb[0]=dfOrden2_p(f,xp[0],h)
dfb[-1]=dfOrden2_r(f,xp[-1],h)
dfb[1:-1]=df_c(f,xp[1:-1],h)
plt.plot(xp,dfa,label='derivada aproximada')
plt.legend()
plt.show()

Ea= np.linalg.norm(df(xp)-dfa)/np.linalg.norm(df(xp))
Eb= np.linalg.norm(df(xp)-dfb)/np.linalg.norm(df(xp))

print('relativa global con procedimiento a',Ea)
print('relativa',Eb)

###################### EJERCICIO 3 #######################
def D2f(f,x1,h):
    return (f(x1-h)-2*f(x1)+f(x1+h))/(h**2)

f=lambda x: np.sin(2*np.pi*x)
d2f=lambda x: -4*np.pi**2*np.sin(2*np.pi*x)
a=0;b=1;h=0.01
xp=np.arange(a,b+h,h)
d2_f=D2f(f,xp[1:-1],h)
plt.plot(xp[1:-1],d2f(xp[1:-1]),label='derivada 2exacta')
plt.plot(xp[1:-1],d2_f,label='derivada 2 numerica')
plt.legend()
plt.show()
Ec= np.linalg.norm(d2f(xp[1:-1])-d2_f)/np.linalg.norm(d2f(xp[1:-1]))
print('Error relativo global de D2f es',Ec)


import numpy as np

def load_linear_data(point_count=100, min_=0., max_=10., w=0.5493, b=1.1973, random_state=None, scale=1.0, loc=0.0):
    """load linear data

    Args:
        point_count: a integer number, default = 100.
        min_: bottom range of x data, a float number, default = 0.0.
        max_: top range of x data, a float number, default = 10.0.
        w: the coef of linear, a float number, default = 0.5493.
        b: the intercept of linear, a float number, default = 1.1973.
        random_state: random seed, a int number, default = None.
        scale: noise's scale. A float number, default = 1.0.
        loc: noise's loc. A float number, default = 0.0

    Returns:
        A tuple. (x, y). the shape of x is (point_count, 1), the shape
            of y is (point_count, ).

    Raises:
        AssertionError: random_state is not a integer.
    """
    if random_state is not None:
        assert isinstance(random_state, int)
        np.random.seed(random_state)

    x = np.random.uniform(min_, max_, point_count)
    noise = np.random.normal(scale=scale, loc=loc, size=[point_count])
    y = w * x + b + noise
    return x.reshape([-1, 1]), y

def load_data_from_func(func=lambda X_data: 2.1084 * np.square(X_data) - 0.1932 * X_data + 10.813,
                        x_min=0, x_max=10, n_samples=500, loc=0, scale=1, random_state=None):
    """load point data from a function

    Args:
        func: Function for creating data.A Function object, default = lambda X_data:
            2.1084 *  np.square(X_data) - 0.1932 * X_data + 10.813.
        x_min: min value of x. A number, x_min must be less ther x_max, default = 0.
        x_max: max value of x. A number, x_max must be greater ther x_min, default = 0.
        n_samples: sample count. A int number, default = 500.
        loc: loc of noise's destribution. A float number, default = 0.
        scale: scale of noise's destribution. A float number, default = 1.
        random_state: random seed. A positive int number, default = None.

    Returns:
        A tuple of x and y. x's shape is (n_samples, 1), y's shape is (n_samples, )
    """
    if random_state is not None and isinstance(random_state, int):
        np.random.seed(random_state)
    x = np.random.uniform(x_min, x_max, n_samples)
    y = func(x)
    noise = np.random.normal(loc=loc, scale=scale, size=n_samples)
    y += noise
    return x.reshape([-1, 1]), y


#importing libraries
import numpy as np
from matplotlib import pyplot as plt
import math
from mpl_toolkits.mplot3d import Axes3D

def ackley_function(x1,x2):
  #returns the point value of the given coordinate
  part_1 = -0.2*math.sqrt(0.5*(x1*x1 + x2*x2))
  part_2 = 0.5*(math.cos(2*math.pi*x1) + math.cos(2*math.pi*x2))
  value = math.exp(1) + 20 -20*math.exp(part_1) - math.exp(part_2)
  #returning the value
  return value

def ackley_function_range(x_range_array):
  #returns an array of values for the given x range of values
  value = np.empty([len(x_range_array[0])])
  for i in range(len(x_range_array[0])):

    #returns the point value of the given coordinate
    part_1 = -0.2*math.sqrt(0.5*(x_range_array[0][i]*x_range_array[0][i] + x_range_array[1][i]*x_range_array[1][i]))
    part_2 = 0.5*(math.cos(2*math.pi*x_range_array[0][i]) + math.cos(2*math.pi*x_range_array[1][i]))

    value_point = math.exp(1) + 20 -20*math.exp(part_1) - math.exp(part_2)
    value[i] = value_point
  #returning the value array
  return value

def plot_ackley_general():
  #this function will plot a general ackley function just to view it.
  limit = 1000 #number of points
  #common lower and upper limits for both x1 and x2 are used
  lower_limit = -5
  upper_limit = 5
  #generating x1 and x2 values
  x1_range = [np.random.uniform(lower_limit,upper_limit) for x in range(limit)]
  x2_range = [np.random.uniform(lower_limit,upper_limit) for x in range(limit)]
  #This would be the input for the Function
  x_range_array = [x1_range,x2_range]
  #generate the z range
  z_range = ackley_function_range(x_range_array)
  #plotting the function
  fig = plt.figure()
  ax = fig.gca(projection='3d')
  ax.scatter(x1_range, x2_range, z_range, label='Ackley Function')

  def plot_ackley(x1_range,x2_range):
  #This would be the input for the Function
  x_range_array = [x1_range,x2_range]
  #generate the z range
  z_range = ackley_function_range(x_range_array)
  #plotting the function
  fig = plt.figure()
  ax = fig.gca(projection='3d')
  ax.scatter(x1_range, x2_range, z_range, label='Ackley Function')


import math
import pandas as pd
import numpy as np

"""
REFERENCE:
http://www.statskingdom.com/doc_linear_regression.html#multi
"""

# ========================================================
# HELPER FUNCTIONS
# ========================================================
def compute_r(x, y):
    """
    Computes the Coefficient of Co-relation using Pearson's method
    """
    n = len(x)
    numerator = n * ((x * y).sum()) - (x.sum() * y.sum())
    denominator = (n * (x ** 2).sum()) - x.sum() ** 2
    denominator *= (n * (y ** 2).sum()) - y.sum() ** 2
    denominator = math.sqrt(denominator)
    return numerator / denominator

def form_equation(b):
    b_ = np.reshape(b, (b.shape[0],))
    y = 'Y = ' + str(round(b_[0], 3))
    for i, bi in zip(range(len(b_[1:])), b_[1:]):
        if bi > 0:
            sign = ' +'
        else:
            sign = ' '
        y += sign + str(round(bi, 3)) + ' X' + str(i)
    return y

def compute_equation(x, y):
    y = np.reshape(y, (y.shape[0], 1))

    # Formula to be used
    # B = (X'*X)^-1*X'*Y
    # So we do this step by step

    # 1. X_T = X Transpose
    x_t = np.matrix.transpose(x)

    # 2. (X'*X)^-1
    x_t_x = np.linalg.inv(np.dot(x_t, x))

    # 3. X_T_Y
    h = np.dot(x_t_x, x_t)

    # 4. B computation
    b = np.dot(h, y)

    # 5. y^ is predicted y values
    y_dash = np.dot(x, b)

    # 6. ERROR
    squared_error = ((y - y_dash) ** 2).sum()
    print('SSE :', squared_error)

    # B contains the equation
    print(form_equation(b))

# ==============================================
# EXECUTION
# ==============================================

# Load the data
data = pd.read_csv('data.csv', dtype=float)

# Metas
columns = data.columns[:-1]
y_label = data.columns[-1]

# Split
Y = data.iloc[:, -1].to_numpy()

# Compute Co-relation and Greater than threshold
threshold = 0.7
X_matrix = []
for x_label in columns:
    # Obtain best Attributes
    X = data[x_label]
    r = compute_r(X, Y)
    if r >= threshold:
        X_matrix.append(X.to_numpy())

# Convert to numpy array
X_matrix = np.insert(X_matrix, 0, np.ones((1, len(X_matrix[0]))), axis=0)
X_matrix = np.array(X_matrix, dtype=np.float)
X_matrix = np.matrix.transpose(X_matrix)

# X_matrix = np.array([[1, 1, 1], [1, 2, 2], [1, 3, 3], [1, 4, 1], [1, 5, 2], [1, 6, 3]])
# Y = np.array([2.1, 3.9, 6.3, 4.95, 7.1, 8.5])

compute_equation(X_matrix, Y)


import numpy as np
import math
from scipy.stats import norm
#
# CONSTRUCT MODELS (PDF)
# =======================
#
# read files
data = np.genfromtxt('Antofagasta.csv', delimiter=',')
t2m=data[1::,2] # skip first line, read column 2 (2m air temperature)
nt=len(t2m)
ny=nt//12       # rounds off to full year, floor division
#
# calculate monsthly statistics
t2m_mean=[0.0]*12
t2m_stdv=[0.0]*12
x=[0.0]*ny
#
# get means and standard deviations
for m in range(12):        # loop through months
    # construct mean and stdv for temperatures
    for y in range(ny):
        x[y]=t2m[(y*12)+m] # pass month-specific temperature to x vector
    t2m_mean[m]=np.mean(x)
    t2m_stdv[m]=np.std(x)
#
# print summary
print(' '.join('{:0.2f}'.format(i) for i in t2m_mean))
print(' '.join('{:0.2f}'.format(i) for i in t2m_stdv))
#
#
# HYPOTHESES TST
# ==============
# NOTE: here we use the normal distribution. t may be more fitting given the relatively small sample size.
#
# H0: the mean is still the one caulculated from observations
# HA: the mean is different from the one calculated from observations
# We test this by assuming H0 is correct and calculating the probability of getting a signal (temperature difference) at least as extreme as the observed.
#
# this is the data we were given (temperatures are converted to Kelvin like in dataset)
tm_jan=23.6+273.15    # predicted january temperatures
tm_jun=17.2+273.15    # predicted june temperatures
df=30                 # degrees of freedom (years in simulation)
#
# we calculate the standard error (standard deviation of sampling distribution)
serr_jan=t2m_stdv[0]/(float(df))**0.5    # standard error january
serr_jun=t2m_stdv[5]/(float(df))**0.5    # standard error june
#
# calculate the z-score/look at how far away we are from the mean (expressed in standard deviations of the sampling distribution)
z_jan=tm_jan-t2m_mean[0]/serr_jan
z_jun=tm_jun-t2m_mean[5]/serr_jun
#
# calculate p-values (2-tailed test)
p_jan=2*(1-(norm.cdf(z_jan, 0, 1)))
p_jun=2*(1-(norm.cdf(z_jun, 0, 1)))
#
print ("P(Jan signal or more extreme | H0) :",p_jan)
print ("P(Jun signal or more extreme | H0) :",p_jun)


import numpy as np

def gen_el_ma(cur, i):
    n = len(cur)
    if i == n:
        return np.identity(n)
    else:
        new = np.identity(n)
        for j in range(i, n):
            num = cur[j][i - 1]
            pivot = cur[i - 1][i - 1]
            k = -num / pivot
            new[j][i - 1] = k
        cur = new.dot(cur)
        return (gen_el_ma(cur, i + 1)).dot(new)

def solve_sim(arr):
    n = len(arr)
    ans = []
    print(arr)
    for i in range(n - 1, -1, -1):
        total = arr[i][-1]
        target = i
        cur = target + 1
        for j in range(len(ans)):
            total -= ans[j] * arr[i][cur]
            cur += 1
        ans.insert(0, total / arr[i][target])
    return ans

def main():
    # ans is x=2, y=3
    a = np.array([
      [1, 3, 5, 31],
      [2, 4, 6, 40],
      [4, 1, 7, 39]])
    # N = int(input())
    # a = [list(map(int, input().split())) for i in range(N)]

    el_ma = gen_el_ma(a, 1)
    print(el_ma)
    arr = el_ma.dot(a)
    result = solve_sim(arr)
    print(result)

if __name__ == '__main__':
    main()

Semester 1/MA1101R Linear Algebra/simultaneous_equation_solver.py


from sympy import ( symbols, solve, diff, integrate, exp, sqrt, lambdify, pprint )
import matplotlib.pyplot as plt
import numpy as np

# The length of a petal on a certain flower varies from 2.25 cm to 6.25 cm and has a probability density function defined by:

x = symbols( 'x' )
F = 1 / ( 2 * sqrt( x ) )

# What does the petal distribution look like?

g_xlim = [ 1, 7 ]
g_ylim = [ -5, 10 ]

lam_p = lambdify( x, F, np )
x_vals = np.linspace( g_xlim[0], g_xlim[1], 1000, endpoint=True )
y_vals = lam_p( x_vals )
plt.plot( x_vals, y_vals )

x_min, x_max = 2.25, 6.25

plt.vlines( x = x_min, ymin = 0, ymax = F.subs( { x: x_min } ), color = 'Black', zorder = 1 )
plt.vlines( x = x_max, ymin = 0, ymax = F.subs( { x: x_max } ), color = 'Black', zorder = 1 )

# Find the probabilities that the length of a randomly selected petal will be as follows.

# A
# The probability that the length of a randomly selected petal is between 2.5 cm and 2.7 cm is

a, b = 2.5, 2.7
bounds = np.arange( a, b, 1/50., dtype=float)

for n in bounds:
	y = F.subs( { x: n } )
	plt.vlines( x = n, ymin = 0, ymax = y, color = 'Teal', zorder = 1, alpha = .4 )

area = integrate( F, ( x, a, b ) ).evalf()
total_area = integrate( F, ( x, 2.25, 6.25 ) ).evalf()

plt.show()

area_pct = round( ( area / total_area ), 4 )

# B
# Greater than or equal to 2.7 cm

a, b = 2.7, x_max
bounds = np.arange( a, b, 1/50., dtype=float)

for n in bounds:
	y = F.subs( { x: n } )
	plt.vlines( x = n, ymin = 0, ymax = y, color = 'Teal', zorder = 1, alpha = .4 )

area = integrate( F, ( x, a, b ) ).evalf()
total_area = integrate( F, ( x, 2.25, 6.25 ) ).evalf()

plt.show()

area_pct = round( ( area / total_area ), 4 )

# C
# Less than or equal to 2.5 cm

a, b = x_min, 2.5
bounds = np.arange( a, b, 1/50., dtype=float)

for n in bounds:
	y = F.subs( { x: n } )
	plt.vlines( x = n, ymin = 0, ymax = y, color = 'Teal', zorder = 1, alpha = .4 )

area = integrate( F, ( x, a, b ) ).evalf()
total_area = integrate( F, ( x, 2.25, 6.25 ) ).evalf()

plt.show()

area_pct = round( ( area / total_area ), 4 )


import numpy as np

class PCA:

    def __init__(self, n_components):
        self.n_components = n_components
        self._center = None
        self._cov_mat = None
        self._eigen_values = None
        self._eigen_vectors = None
        self.explained_variance_ratio = None

    def fit(self, X):
        '''
        Build the subspace on which to project the data. It is generated by the eigen vectors corresponding to the k highest
        eigen values:
            1- center X
            2- compute covariance matrix
            3- compute k eigen vectors V={v_i}_k corresponding to the k larger eigenvalues \lamb={\lamb_i}_k
            4- project onto span(V)

        As the covariance matrix of X is symmetric, its eigen vectors are orthogonal, and as such the covariance of the projection of X in the subspace
        generated by its eigen vectors is a diagonal matrix where entries are its eigen values. The explained variance ratio per principal direction is
        simply the corresponding eigen value divided by the sum of all the eigen values (which is the total variance).
        '''
        self._center = X.mean(axis=0)
        self._cov_mat = np.cov(X - self._center, rowvar=False)

        lambs, vs = np.linalg.eigh(self._cov_mat)
        lambs, vs = lambs[::-1], np.flip(vs, axis=1)

        self._eigen_values, self._eigen_vectors = lambs[:self.n_components], vs[:, :self.n_components]
        self.explained_variance_ratio = self._eigen_values / lambs.sum()

    def transform(self, X):
        return (X - self._center) @ self._eigen_vectors

    def fit_transform(self, X):
        self.fit(X)
        return self.transform(X)


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

# In[1]:

# Image compression by SVD
# 2017,2018 Tsuyoshi Okubo
# 2019 modified by Tsuyoshi Okubo
# 2020 modified by TO

# By using the low rank approximation through SVD, perform data compression of a gray scale image.
#
# You can change sample image by modifying file open "sample.jpg".
#
# Also, you can set the rank of approximation by varying "chi".
#
# Let's see, how the image changes when you change the rank.

# In[2]:

## import libraries
from PIL import Image ## Python Imaging Library
import numpy as np ## numpy
#get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt

# In[3]:

## Rank for low rank approximation
chi = 10

# In[4]:

img = Image.open("./sample.jpg") ## load image
img_gray = img.convert("L") ## convert to grayscale
#img_gray.show(title="Original") ## show image in external window
img_gray.save("./gray.png") ## save grayscale image
#img_gray.save("./gray.jpg") ## save grayscale image in jpg

# In[5]:

array = np.array(img_gray,dtype=float) ## convert to ndarray
print("Array shape:" +repr(array.shape)) ## print array shape

# In[6]:

u,s,vt = np.linalg.svd(array,full_matrices=False) ## svd

# In[7]:

#truncation
u = u[:,:chi]
vt = vt[:chi,:]
st = s[:chi]

# In[8]:

array_truncated = np.dot(np.dot(u,np.diag(st)),vt) ## make truncated array
normalized_distance = np.sqrt(np.sum((array-array_truncated)**2))/np.sqrt(np.sum(array**2))
print("Low rank approximation with chi=" +repr(chi))
print("Normalized distance:" +repr(normalized_distance)) ## print normalized distance

# In[9]:

img_gray_truncated = Image.fromarray(np.uint8(np.clip(array_truncated,0,255))) ## convert to grayscale image

# In[10]:

#img_gray_truncated.show(title="Truncated") ## show image in external window
img_gray_truncated.save("./gray_truncated.png") ## save compressed image
#img_gray_truncated.save("./gray_truncated.jpg") ## save compressed image in jpg

# In[11]:

plt.figure(figsize=(array.shape[1]*0.01,array.shape[0]*0.01))
plt.axis("off")
plt.title("Original")
plt.imshow(img_gray,cmap='gray')

plt.figure(figsize=(array_truncated.shape[1]*0.01,array_truncated.shape[0]*0.01))
plt.axis("off")
plt.title("Compressed")
plt.imshow(img_gray_truncated,cmap='gray')

# In[12]:

## normalization of singular values
s = s/np.sqrt(np.sum(s**2))

output_sv = len(s) ## number of singular values to output
plt.figure()
plt.title("Singular Value Spectrum of the image")
plt.plot(np.arange(output_sv),s[:output_sv],"o")
plt.vlines([chi],0,1,  "red", linestyles='dashed') ## position of chi
plt.xlabel("Index")
plt.ylabel("sigma")
plt.yscale("log")
plt.show()


# Import Packages
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from src.utilities import Utils

# Variable Declaration

# Code
class UnivariateLR:
    def __init__(self, data, label_index=-1):
        self.data = data
        self.label_index = label_index
        self.m, self.X, self.y, self.theta = self.get_X_y(data)
        self.validate_costs = []

    def visualize_dataset(self):
        print("Plotting data...")
        ax = sns.scatterplot(x=self.data.columns[0], y=self.data.columns[self.label_index],
                             data=self.data)
        ax.set_title("{} vs {} chart".format(self.data.columns[0], self.data.columns[1]))
        plt.show()

    def get_X_y(self, data):
        print("Splitting dependent and independent columns...")
        m = data.shape[0]
        X = np.append(np.ones((m, 1)), data[data.columns[0]].values.reshape(m, 1), axis=1)
        y = data[data.columns[self.label_index]].values.reshape(m, 1)
        theta = np.zeros((data.shape[1], 1))
        return m, X, y, theta

    def compute_cost(self):
        y_pred = self.X.dot(self.theta)
        error = np.sum(np.square(y_pred - self.y)) * (0.5 / self.m)
        return error

    def gradient_descent(self, alpha, iterations):
        print("Starting Gradient Descent...")
        for i in range(1,iterations+1):
            y_pred = self.X.dot(self.theta)
            error_gradient = np.dot(self.X.transpose(), y_pred - self.y)
            self.theta = self.theta - ((alpha / self.m) * error_gradient)
            self.validate_costs.append(self.compute_cost())
            if i % 100==0:
                print("Cost value after {} iterations is: {}".format(i, self.validate_costs[-1]))

    def visualize_cost_function(self):
        print("Plotting Cost function to validate the modelling process...")
        assert len(self.validate_costs) > 0, "Costs not yet computed."
        plt.plot(self.validate_costs)
        plt.xlabel("Iterations")
        plt.ylabel("$Cost - J(\Theta)$")
        plt.title("Cost function VS Iterations")
        plt.show()

    def plot_regression_fit(self):
        print("Plotting regression fit over data...")
        t = np.squeeze(self.theta)
        sns.scatterplot(x=self.data.columns[0], y=self.data.columns[self.label_index],
                             data=self.data)
        x_value = [x for x in range(int(min(self.data[self.data.columns[0]])), int(max(self.data[self.data.columns[0]])))]
        y_value = [x*t[1]+t[0] for x in x_value]

        sns.lineplot(x_value, y_value)
        plt.xlabel(self.data.columns[0])
        plt.ylabel(self.data.columns[self.label_index])
        plt.show()

    def predict(self, x):
        return np.dot(self.theta.transpose(), x)

    def pipeline(self, alpha, iterations):
        self.visualize_dataset()
        self.gradient_descent(alpha=alpha, iterations=iterations)
        self.visualize_cost_function()
        self.plot_regression_fit()


import numpy as np
import math
import matplotlib.pyplot as plt
## E-M Coin Toss Example as given in the EM tutorial paper by Do and Batzoglou* ##
def get_binomial_log_likelihood(obs,probs):
    """ Return the (log)likelihood of obs, given the probs"""
    # Binomial Distribution Log PDF
    # ln (pdf)      = Binomial Coeff * product of probabilities
    # ln[f(x|n, p)] =   comb(N,k)    * num_heads*ln(pH) + (N-num_heads) * ln(1-pH)
    N = sum(obs) #number of trials
    k = obs[0] # number of heads
    binomial_coeff = math.factorial(N) / (math.factorial(N-k) * math.factorial(k))
    prod_probs = obs[0]*math.log(probs[0]) + obs[1]*math.log(1-probs[0])
    log_lik = binomial_coeff + prod_probs
    return log_lik
# 1st:  Coin B, {HTTTHHTHTH}, 5H,5T
# 2nd:  Coin A, {HHHHTHHHHH}, 9H,1T
# 3rd:  Coin A, {HTHHHHHTHH}, 8H,2T
# 4th:  Coin B, {HTHTTTHHTT}, 4H,6T
# 5th:  Coin A, {THHHTHHHTH}, 7H,3T
# so, from MLE: pA(heads) = 0.80 and pB(heads)=0.45
# represent the experiments
head_counts = np.array([5,9,8,4,7])
tail_counts = 10-head_counts
# initialise the pA(heads) and pB(heads)
# pA_heads = np.zeros(100); pA_heads[0] = 0.10
# pB_heads = np.zeros(100); pB_heads[0] = 0.50

pA_heads = [0.3]
pB_heads = [0.48]
# E-M begins!
delta = 10e-15
j = 0 # iteration counter
improvement = float('inf')
while (improvement>delta):
    expectation_A = np.zeros((len(head_counts),2), dtype=float)
    expectation_B = np.zeros((len(head_counts),2), dtype=float)
    i = 0
    for h, t in zip(head_counts,tail_counts):
        e = [h, t] # i'th experiment
          # loglikelihood of e given coin A:
        ll_A = get_binomial_log_likelihood(e,np.array([pA_heads[j],1-pA_heads[j]]))
          # loglikelihood of e given coin B
        ll_B = get_binomial_log_likelihood(e,np.array([pB_heads[j],1-pB_heads[j]]))
# corresponding weight of A proportional to likelihood of A
        weightA = math.exp(ll_A) / ( math.exp(ll_A) + math.exp(ll_B) )
# corresponding weight of B proportional to likelihood of B
        weightB = math.exp(ll_B) / ( math.exp(ll_A) + math.exp(ll_B) )
        expectation_A[i,:] = np.dot(weightA, e)
        expectation_B[i,:] = np.dot(weightB, e)
        i += 1
    # pA_heads[j+1] = sum(expectation_A)[0] / sum(sum(expectation_A))
    # pB_heads[j+1] = sum(expectation_B)[0] / sum(sum(expectation_B))
    pA_heads += [sum(expectation_A)[0] / sum(sum(expectation_A))]
    pB_heads += [sum(expectation_B)[0] / sum(sum(expectation_B))]
    improvement = ( max( abs(np.array([pA_heads[j+1],pB_heads[j+1]]) - np.array([pA_heads[j],pB_heads[j]]) )) )
    j = j+1
plt.figure();
plt.plot(np.arange(0,j+1),pA_heads, 'ro--')
plt.plot(np.arange(0,j+1),pB_heads, 'bo-')
plt.show()

print(f'pA is {pA_heads[-1]}')
print(f'pB is {pB_heads[-1]}')


# Week 02 Assignment: Fitting and Plotting
# Fit a linear curve to the data
# Fit a cubic curve using the SciPy library
# Find the area underneath the cubic curve over the domain of the data using the tools in SciPy
# Plot the data, the linear fit, and the cubic fit in Matplotlib. Make sure to give the plot a title and an x and y label. Save this figure and include it in your pull request!
# Put the area of the curve on the plot somewhere as text using Matplotlib
# Use the Bayesian information criterion to justify which model (linear or cubic) is preferable. Include this justification in your journal entry for the week.

# import packages
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import linregress
from scipy.integrate import simps
from matplotlib.patches import Polygon

# input data
x = np.array([ 1.,1.5,2.,2.5,3.,3.5,4.,4.5,5.,5.5,6.,6.5,7.,7.5,8.,8.5,9.,9.5,10.])
y = np.array([3.43,4.94,6.45,9.22,6.32,6.11,4.63,8.95,7.8,8.35,11.45,14.71,11.97,12.46,17.42,17.0,15.45,19.15,20.86])

# 1 Fit a linear curve to the data
slope, intercept, r_value, p_value, std_err = linregress(x,y)

# 2 Fit a cubic curve using the SciPy library
coefficients = np.polyfit(x,y,3)
p = np.poly1d(coefficients)

# 3 Find the area underneath the cubic curve over the domain of the data using the tools in SciPy
t = np.linspace(min(x),max(x),num=100)
area = simps(p(t),t)
print("area =",area)

# 4 Plot the data, the linear fit, and the cubic fit in Matplotlib
plt.xlabel('x')
plt.ylabel('y')
plt.title('Curve Fitting');
# plot data
plt.scatter(x,y,c='k',label='Data')
# plot linear fit
plt.plot(x,x*slope+intercept,'--r',label='Linear Fit')
# plot cubic fit
plt.plot(t,p(t),'-b',label='Cubic Fit')
plt.legend()
# save figure
plt.savefig('YWang_02.png')

# 5 Put the area of the curve on the plot somewhere as text using Matplotlib
fig, ax = plt.subplots()
plt.xlabel('x')
plt.ylabel('y')
plt.title('Curve Fitting');
# plot data
plt.scatter(x,y,c='k',label='Data')
# plot linear fit
plt.plot(x,x*slope+intercept,'--r',label='Linear Fit')
# plot cubic fit
plt.plot(t,p(t),'-b',label='Cubic Fit')
plt.legend()
# plot area under cubic curve
verts = [(min(x), 0)] + list(zip(t, p(t))) + [(max(x), 0)]
poly = Polygon(verts,facecolor='0.9',edgecolor='0.5',alpha=0.5)
ax.add_patch(poly)
# insert text
plt.text(0.5 * (min(x) + max(x)), 5, r"Shaded Area",
         horizontalalignment='left', fontsize=20)
# save figure
plt.savefig('YWang_02.png')

# 6 Use the Bayesian information criterion to justify which model (linear or cubic) is preferable

# define function for Bayesian information criterion
def BIC(y, yhat, k, weight = 1):
    err = y - yhat
    sigma = np.std(np.real(err))
    n = len(y)
    B = n*np.log(sigma**2) + weight*k*np.log(n)
    return B

# calculate BIC for linear fit
BIC_Linear = BIC(y,x*slope+intercept,1)
# calculate BIC for cubic fit
BIC_Cubic = BIC(y,p(x),3)


'''
Linear Algebra used for machine learning.
'''
import math
from typing import List, Tuple, Callable
import sympy

#----------------
# Vector Algebra |
#----------------

# Vectors store a single row of a matrix.
Vector = List[float]

# Vectorial Addition.
def add(v: Vector, w: Vector) -> Vector:
    '''
    with:
        v = (v_1, v_2, ..., v_x)
        w = (w_1, w_2, ..., w_x)

    v + w = (v_1 + w_1, v_2 + w_2, ..., v_x + w_x)
    as:
        add(v, w)
    '''
    assert len(v) == len(w), f"Dimension of vectors do not match: dim(v)={len(v)} != dim(w)={len(w)}."
    return [v_x + w_x for v_x, w_x in zip(v, w)]

# Vectorial Subtraction.
def subtract(v: Vector, w: Vector) -> Vector:
    '''
    with:
        v = (v_1, v_2, ..., v_x)
        w = (w_1, w_2, ..., w_x)

    v - w = (v_1 - w_1, v_2 - w_2, ..., v_x - w_x)
    as:
        subtract(v, w)
    '''
    assert len(v) == len(w), f"Dimension of vectors do not match: |v|={len(v)} != |w|={len(w)}."
    return [v_x - w_x for v_x, w_x in zip(v, w)]

# Multiple vector addition.
def vector_sum(vectors: List[Vector]) -> Vector:
    '''
    for every vector v_x having same dimensions:
        v_1 + v_2 + ... + v_x =
        (v_1x + v_2x + ... v_xx, v_1y + v_2y + ... v_xy, v_1z + v_2z + ... v_xz)
    '''
    assert vectors, "No vectors provided!"
    n_vector_dim = len(vectors[0])
    assert all(len(v) == n_vector_dim for v in vectors), "dimensions do not match!"

    return [sum(vector[x] for vector in vectors)
            for x in range(n_vector_dim)]

# Scalar Product.
def scalar_multiply(c: float, v: Vector) -> Vector:
    return [c * v_n for v_n in v]

# Component-Wise vectorial mean.
def vector_mean(vectors: List[Vector]) -> Vector:
    assert vectors, "No vectors provided!"
    n_vector_dim = len(vectors[0])
    assert all(len(v) == n_vector_dim for v in vectors), "dimensions do not match!"
    return scalar_multiply(1/len(vectors), vector_sum(vectors))

# dot product.
def dot(v: Vector, w: Vector) -> float:
    assert len(v) == len(w), "vector sizes are different!"
    return sum(v_n * w_n for v_n, w_n in zip(v, w))

# Summation of squares.
def sum_of_squares(v: Vector) -> float:
    return dot(v, v)

# Vectorial Magnitude.
def magnitude(v: Vector) -> float:
    return math.sqrt(sum_of_squares(v))

# Vectorial distance.
def distance(v: Vector, w: Vector) -> float:
    return magnitude(subtract(v, w))

# unit vector
def unit_vector(v: Vector) -> Vector:
    return [v[v_i]/magnitude(v) for v_i in range(len(v))]

# ----------------
# Matrix Algebra |
# ----------------

# Note: Matrix = List[Vector]
Matrix = List[List[float]]

# Matrix shape
def shape(A: Matrix) -> Tuple[int, int]:
    return len(A), len(A[0])

# Matrix row extraction
def get_row(A: Matrix, i: int) -> Vector:
    return A[i]

# Matrix column extraction
def get_column(A: Matrix, j: int) -> Vector:
    return [column[j] for column in A]

# Matrix generation with specified dimensions
def make_matrix(num_rows: int,
                num_cols: int,
                entry_fn: Callable[[int, int], float]) -> Matrix:
    return [[entry_fn(row, col) for col in range(num_cols)] for row in range(num_rows)]

# Identity Matrix generator.
def identity_matrix(n: int) -> Matrix:
    def identity_matrix_base_func(row, col):
        if row == col:
            return 1
        else:
            return 0
    return make_matrix(n, n, identity_matrix_base_func)

# Matrix scalar product
def matrix_scalar_multiply(c: float, A: Matrix) -> Matrix:
    return [scalar_multiply(c, row) for row in A]

def transpose(A: Matrix) -> Matrix:
    return [[row[col] for row in A] for col in range(len(A[0]))]

# Matrix cross-product
def matrix_dot(A: Matrix, B: Matrix) -> Matrix:
    assert shape(A)[1] == shape(B)[0], "Inner dimensions of matrices do not match!"

    return [[dot(row, column) for column in transpose(B)] for row in A]


import numpy as np
import numpy.linalg as LA
import matplotlib.pyplot as plt
from matplotlib import rc
plt.ion()

if __name__ == "__main__":
	a = 1
	b = 2
	c = 0
	d = 2
	A = np.array([[a,b],[c,d]])
	u,s,v = LA.svd(A)

	v_i = v.T

	# Plot
	origin = [0], [0]

	# V
	plt.subplot(121)
	plt.quiver(*origin,v_i[0,:], v_i[1,:], angles='xy', scale_units='xy', color=['r','b'],scale=1)
	# unit circle
	theta = np.arange(0.0,360.0,1.0)*np.pi/180.
	x = np.cos(theta)
	y = np.sin(theta)
	plt.plot(x,y)
	plt.axis('equal')
	plt.axis([-s[0]-0.1, s[0]+0.1, -s[0]-0.1, s[0]+0.1])

	plt.title('v')
	# U
	plt.subplot(122)
	plt.quiver(*origin,[i*x for i,x in zip(s,u[0,:])], [i*y for i,y in zip(s,u[1,:])], angles='xy', scale_units='xy', color=['r','b'],scale=1)
	theta = np.arange(0.0,360.0,1.0)*np.pi/180.
	phi = np.arccos(np.dot(u[:,0],[1,0]))
	x = s[0]*np.cos(theta)
	y = s[1]*np.sin(theta)
	R = np.array([[np.cos(phi), -np.sin(phi)],
				  [np.sin(phi),  np.cos(phi)],])
	x,y = np.dot(R,np.array([x,y]))
	plt.plot(x,y)
	plt.title('Av=u')
	plt.axis('equal')
	plt.axis([-s[0]-0.1, s[0]+0.1, -s[0]-0.1, s[0]+0.1])

	"""
	plt.figure(1)

	ax1.arrow(0,0,v_i[0,0],v_i[1,0], head_width=0.05, head_length=0.1, fc='k', ec='k')
	ax1.arrow(0,0,v_i[0,1],v_i[1,1], head_width=0.05, head_length=0.1, fc='k', ec='k')
	ax1.set_title('V')
	ax1.arrow(0,0,s[0]*u[0,0],s[0]*u[1,0], head_width=0.05, head_length=0.1, fc='k', ec='k')
	ax1.arrow(0,0,s[1]*u[0,1],s[1]*u[1,1], head_width=0.05, head_length=0.1, fc='k', ec='k')
	ax2.set_title('U')
	"""


from AutoDiff.ForwardAD import Var, MultiFunc
import numpy as np
import math

def Newton(func, guess, tol = 10**(-8), max_iter = 2000):
    '''
        Returns the values of the roots of function func
        The optimization problem is solved with the method solver and accuracy specified by tolerance

        INPUTS
        =======
        func : callable (function)
               - takes in a list of variables
               - returns a single variable

        guess : list of real numbers, corresponding to the initial guess for roots of the function func
        tol : the tolerance threshold of errors, floating point number, by default equal to 10e-8
        max_iter : integer corresponding to the maximum number of iterations  for the algorithm
                   by default equal to 2000

        RETURNS
        =======
        xnext: the root of func when the tolerance tol is reached,
        or the maximum iteration number, max_iter, is reached.

        EXAMPLES
        =======
        # >>> f = lambda x: x**2-1
        # >>> guess = 2
        # >>> root = Newton(f, guess)
          1.0
    '''
    xcur = Var(guess)
    fxcur = func(xcur)
    xnext = xcur - Var(fxcur.get_value()/fxcur.get_der()[0])
    err = abs(xnext.get_value()-xcur.get_value())
    i = 0
    while err > tol:
        xcur = xnext
        fxcur = func(xcur)
        xnext = xcur - Var(fxcur.get_value()/fxcur.get_der()[0])
        err = abs(xnext.get_value()-xcur.get_value())
        i += 1
        if i > max_iter:
            print('The execution goes beyond the maximum number of iterations, \
                  so it is stopped and the most current result is printed')
            break
    return xnext.get_value()


"""
This script demonstrates the implementation of the GeLU function.

GELUs full form is GAUSSIAN ERROR LINEAR UNIT

GELU is a smooth approximation to the rectifier. It has a non-monotonic “bump” when x < 0.
The function takes a vector of K real numbers as input.
We define Gaussian Error Linear Unit (GELU) as-
Gelu(x) = xP(X <= x) = xΦ(x)
We can approximate the GELU with
0.5x(1 + tanh[root(2/π)(x + 0.044715x^3)])

Script inspired from its corresponding Wikipedia article
https://en.wikipedia.org/wiki/Rectifier_(neural_networks)
"""

import numpy as np

def gelu(vector: np.array) -> np.array:
    """
    Implements the relu function

    Parameters:
        vector (np.array,list,tuple): A  numpy array of shape (1,n)
        consisting of real values or a similar list,tuple

    Returns:
        gelu_vec : The input numpy array, after applying
        gelu.

    >>> vec = np.array([-1, 0, 5])
    >>> relu(vec)
    array([0, 0, 5])
    """

    return  np.dot(np.dot(0.5,vector),(1 + np.tanh((np.sqrt(2/np.math.pi))*(vector + 0.044715*np.power(vector, 3)))))

if __name__ == "__main__":
    print(np.array(gelu([-1, 0, 5])))  # --> 4.841191761428657


"""
Source: https://github.com/shakedzy/dython
"""

import math
import scipy.stats as ss
from collections import Counter

def conditional_entropy(x, y):
    """
    Calculates the conditional entropy of x given y: S(x|y)
    Wikipedia: https://en.wikipedia.org/wiki/Conditional_entropy
    :param x: list / NumPy ndarray / Pandas Series
        A sequence of measurements
    :param y: list / NumPy ndarray / Pandas Series
        A sequence of measurements
    :return: float
    """
    # entropy of x given y
    y_counter = Counter(y)
    xy_counter = Counter(list(zip(x,y)))
    total_occurrences = sum(y_counter.values())
    entropy = 0.0
    for xy in xy_counter.keys():
        p_xy = xy_counter[xy] / total_occurrences
        p_y = y_counter[xy[1]] / total_occurrences
        entropy += p_xy * math.log(p_y/p_xy)
    return entropy

def theils_u(x, y):
    """
    Calculates Theil's U statistic (Uncertainty coefficient) for categorical-categorical association.
    This is the uncertainty of x given y: value is on the range of [0,1] - where 0 means y provides no information about
    x, and 1 means y provides full information about x.
    This is an asymmetric coefficient: U(x,y) != U(y,x)
    Wikipedia: https://en.wikipedia.org/wiki/Uncertainty_coefficient
    :param x: list / NumPy ndarray / Pandas Series
        A sequence of categorical measurements
    :param y: list / NumPy ndarray / Pandas Series
        A sequence of categorical measurements
    :return: float
        in the range of [0,1]
    """
    s_xy = conditional_entropy(x,y)
    x_counter = Counter(x)
    total_occurrences = sum(x_counter.values())
    p_x = list(map(lambda n: n/total_occurrences, x_counter.values()))
    s_x = ss.entropy(p_x)
    if s_x == 0:
        return 1
    else:
        return (s_x - s_xy) / s_x


#Basic models or functions in form of f(x,...)

import numpy as np
from scipy.stats import norm
from scipy.stats import cauchy as sci_cauchy
from scipy.stats import pearson3 as sci_pearson3
from scipy.special import erf as sci_erf
from scipy.special import expit, logit

from ._helpers import funcArgsNr

#polynomial functions: constant, linear, quadratic, cubic
def constant(x, a):
	y = np.full(x.size, a)
	return y

def linear(x, a, b):
	y = a * x + b
	return y

def quadratic(x, a, b, c):
	y = a * x**2 + b * x + c
	return y

def cubic(x, a, b, c, d):
	y = a * x**3 + b * x**2 + c * x + d
	return y

#Gaussian functions
def gaussian(x, a, b, c):
	'''General Gaussian function
	Parameters:
		x: independent variable
		a, b, c: parameters for function
	returns:
		y: dependent variable
	'''
	y = a*np.exp(-np.power(x-b,2)/(2*np.power(c,2)))
	#y = a * c * np.sqrt(2*np.pi) * norm.pdf(x, b, c) #equivalent to above
	return y

def erf(x, a, b, c):
	'''General Gaussian error function (erf), the general cumulative distribution function (CDF) of Gaussian or normal distribution.
	Parameters:
		x: independent variable
		a, b, c: parameters for function
	returns:
		y: dependent variable
	'''
	y = a * sci_erf((x-b)/c)
	return y

#Cauchy-Lorentz function
def cauchy(x, a, b, c):
	'''General Cauchy function, the probability density function (PDF) of Cauchy or Lorentz distribution.
	Parameters:
		x: independent variable
		a, b, c: parameters for function
	returns:
		y: dependent variable
	'''
	y = a * sci_cauchy.pdf(x, loc=b, scale=c)
	return y

#Pearson
def pearson3(x, a, b, c, d):
	'''General Pearson Type 3 function, the probability density function (PDF) of Pearson type III distribution.
	https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.pearson3.html
	Parameters:
		x: independent variable
		a, b, c, d: parameters for function
	returns:
		y: dependent variable
	'''
	y = a * sci_pearson3.pdf(x, skew=b, loc=c, scale=d)
	return y

#exponential
def exponential(x, a, b):
	'''General exponential function
	Parameters:
		x: independent variable
		a, b: parameters for function
	returns:
		y: dependent variable
	'''
	#y = a*np.exp(b*x)	#natural exponential function
	y = a * b ** x	#equivalent ?
	return y

def logarithm(x, a, b):
	'''General logarithm function, inverse function to exponentiation -- y = a * log_b (x)
	'''
	if b ==1: b = b + 0.001
	y = a * np.log(x) / np.log(b)
	return y

def logistic(x, a, b, c):
	'''General logistic function, the common S-shaped curve.
	https://en.wikipedia.org/wiki/Logistic_function
	'''
	y = a * expit(b*x + c)
	return y

def reciprocal(x, a, b):
	'''Deprecated. It's a special of general power-law function. General reciprocal function

	Note that `np.reciprocal` doesn't work with integers.
	'''
	y = 1 /(a * x + b)
	# y = np.reciprocal(a * x + b)
	return y

#power-law
def power_law(x, a, b):
	'''General power-law function

	Note that `np.power` doesn't work with a negative integer power.
	Parameters:
		x: independent variable
		a, b, c: parameters for function
	returns:
		y: dependent variable
	'''
	y = a * np.power(x, float(b))
	#y = np.power(a * x + b, float(c))
	#y = a*np.power(x + b,float(c))
	return y

__custom = '''
def %s(x, %s):
	return %s
'''

#metadata of custom basic models/functions
basicModels = [
	{'model':constant,'name':'constant','n_para':funcArgsNr(constant)-1},
	{'model':linear,'name':'linear','n_para':funcArgsNr(linear)-1},
	{'model':quadratic,'name':'quadratic','n_para':funcArgsNr(quadratic)-1},
	{'model':cubic,'name':'cubic','n_para':funcArgsNr(cubic)-1},
	{'model':gaussian,'name':'gaussian','n_para':funcArgsNr(gaussian)-1},
	{'model':erf,'name':'erf','n_para':funcArgsNr(erf)-1},
	{'model':cauchy,'name':'cauchy','n_para':funcArgsNr(cauchy)-1},
	{'model':pearson3,'name':'pearson3','n_para':funcArgsNr(pearson3)-1},
	{'model':exponential,'name':'exponential','n_para':funcArgsNr(exponential)-1},
	{'model':logarithm,'name':'logarithm','n_para':funcArgsNr(logarithm)-1},
	{'model':logistic,'name':'logistic','n_para':funcArgsNr(logistic)-1},
	{'model':power_law,'name':'power_law','n_para':funcArgsNr(power_law)-1},
	{'model':reciprocal,'name':'reciprocal','n_para':funcArgsNr(reciprocal)-1},
	]
#list of models' name
basicModels_nameList = [model['name'] for model in basicModels]
basicModels_nonp_nameList = [model['name'] for model in basicModels[4:]]	#non-polynomial
#models=['constant', 'linear', 'quadratic', 'cubic', 'gaussian', 'erf', 'cauchy', 'exponential', 'logarithm', 'logistic', 'reciprocal', 'power_law', 'pearson3']
#models=['constant', 'linear', 'quadratic', 'cubic', 'gaussian', 'erf', 'cauchy', 'exponential', 'logarithm', 'logistic', 'power_law', 'pearson3']


'''
Can't Forecast White Noise

A white noise time series is simply a sequence of uncorrelated random variables that are identically distributed. Stock returns are often modelled as white noise. Unfortunately, for white noise, we cannot forecast future observations based on the past - autocorrelations at all lags are zero.

You will generate a white noise series and plot the autocorrelation function to show that it is zero for all lags. You can use np.random.normal() to generate random returns. For a Gaussian white noise process, the mean and standard deviation describe the entire process.

Plot this white noise series to see what it looks like, and then plot the autocorrelation function.

INSTRUCTIONS
100XP
Generate 1000 random normal returns using np.random.normal() with mean 2% (0.02) and standard deviation 5% (0.05), where the argument for the mean is loc and the argument for the standard deviation is scale.
Plot the time series.
Verify the mean and standard deviation of returns using np.mean() and np.std().
Plot the autocorrelation function using plot_acf with lags=20.
'''
# Import the plot_acf module from statsmodels
from statsmodels.graphics.tsaplots import plot_acf

# Simulate wite noise returns
returns = np.random.normal(loc=0.02, scale=0.05, size=1000)

# Print out the mean and standard deviation of returns
mean = np.mean(returns)
std = np.std(returns)
print("The mean is %5.3f and the standard deviation is %5.3f" %(mean,std))

# Plot returns series
plt.plot(returns)
plt.show()

# Plot autocorrelation function of white noise returns
plot_acf(returns, lags=20)
plt.show()