import numpy as np

def normalize_data(x):
    """Function to normalize data using standard score / z-score.
    Args:
        x            (numpy array): Matrix features of size N x D.
    Returns:
        normalized_x (numpy array): Matrix features of size N x D, with its data normalized.
    """

    # Normalization using Standard Score
    # Standardize data by substracting with mean and dividing with standard deviation
    mean         = np.mean(x, axis = 0)
    std_dev      = np.std(x, axis = 0)
    normalized_x = (x - mean) / std_dev

    return normalized_x

def clean_data(x):
    """Function to clean outliers (-999 value) in matrix features and replace them with median.
    Args:
        x (numpy array): Matrix features of size N x D.
    Returns:
        x (numpy array): Matrix features of size N x D, with outliers replaced by median of each feature.
    """

    # locate useful values and outliers (-999)
    mask     = (x != -999)
    outliers = (~mask)

    # Replacing outliers (-999) with median (per feature) of the rest of the data
    x[outliers] = (x[outliers] * 0) + np.median(x[mask], axis = 0)

    return x

def log_basis(x):
    """Function to generate logarithmic basis function for matrix features of x.
    Args:
        x              (numpy array): Matrix features of size N x D.
    Returns:
        log_features_x (numpy array): Matrix log features of size N x 2D.
    """

    # Find minimum value per feature
    min_               = np.min(x, axis=0)

    # Generate log feature of ( 1 / (1 + log(1+x-min)) )
    log_x_1            = 1 / (1 + np.log(1 + x - min_))

    # normalized log feature 1
    normalized_log_x_1 = normalize_data(log_x_1)

    # Generate log feature of ( log(1+x-min) )
    log_x_2            = np.log(1 + x - min_)

    # normalized log feature 2
    normalized_log_x_2 = normalize_data(log_x_2)

    # combine log feature 1 and log feature 2 as one single numpy array
    log_features_x = np.concatenate((normalized_log_x_1, normalized_log_x_2), 1)

    return log_features_x

def polynomial_basis(x, degree):
    """Function to generate polynomial basis function for matrix features of x.
    Args:
        x               (numpy array)        : Matrix features of size N x D.
        degree          (int, scalar)        : Degree of polynomial basis function.
    Returns:
        poly_features_x (list of numpy array): Matrices polynomial features with each size of N x ((degree-1) * D).
    """

    # locate useful values and outliers (-999)
    mask     = (x != -999)
    outliers = (~mask)

    # for power>= 2, store each polynomial basis features into list
    poly_features_x = []

    # iterate through each degree, 2 <= degree < power+1
    for power in range(2, degree + 1):
        # Generate matrix power of features
        poly_x           = np.power(x, power)

        # Find mean of non-outlier values
        mean_poly_x      = np.sum( poly_x * mask, axis=0)/ np.sum(mask, axis=0)

        # Find standard deviation of non-outlier values
        std_dev_poly_x   = np.sqrt( np.sum( ((poly_x - mean_poly_x) * mask)**2, axis=0)/np.sum(mask, axis=0))

        # Normalization using Standard Score, apply to non-outlier values only
        # Standardize data by substracting with mean and dividing with standard deviation
        poly_x           = (poly_x * mask - mean_poly_x)/std_dev_poly_x

        # Replace outliers by zero
        poly_x[outliers] = 0

        # add to list
        poly_features_x.append(poly_x)

    return poly_features_x

def cross_terms_basis(x):
    """Function to generate cross term function for matrix features of x.
    Args:
        x              (numpy array): Matrix features of size N x D.
    Returns:
        log_features_x (numpy array): Matrix cross term features of size N x (D-2)!.
    """

    # store each cross-term basis features into list
    cross_features_x = []

    # Iterate through features and create the cross-term
    for i in range(x.shape[1]):
        for j in range(i+1, x.shape[1]):
            cross_term = (x[:, i] * x[:, j])
            cross_term = normalize_data(cross_term)
            cross_features_x.append(cross_term)

    cross_features_x = np.array(cross_features_x).squeeze().T

    return cross_features_x

def sqrt_basis(x):
    """Function to generate square root basis function for matrix features of x.
    Args:
        x      (numpy array): Matrix features of size N x D.
    Returns:
        sqrt_x (numpy array): Matrix square root features of size N x D.
    """

    # Find minimum value per feature
    min_ = np.min(x, axis=0)

    # generate square root term
    sqrt_x = np.sqrt(x - min_)

    # normalize square root features using standard score
    sqrt_x = normalize_data(sqrt_x)

    return sqrt_x

def generate_features(x, degree, with_ones = True, with_log = False, with_sqrt = False, cross_terms = False):
    """Function to standardize data input and generate features.
    Args:
        x              (numpy array)            : Matrix features of size N x D.
        degree         (int, scalar)            : Degree of polynomial basis function.
        with_ones      (boolean, default true)  : Boolean value to indicate either adding feature column with ones value or not.
        with_logs      (boolean, default false) : Boolean value to indicate either generating features with log basis function or not.
        with_sqrt      (boolean, default false) : Boolean value to indicate either generating features with square root basis function or not.
        cross_terms    (boolean, default false) : Boolean value to indicate either generating features with cross terms (Xi*Xj) basis function or not.
    Returns:
        preprocessed_x (numpy array)            : Final matrix features to be processed using ML methods. Minimal size of N x D.
    """

    # Remove outliers from matrix features
    x_without_outliers = clean_data(x)

    # normalize matrix features (without outliers) using standard score (z-score)
    normalized_x       = normalize_data(x_without_outliers)

    # Form matrix features at least with normalized features
    preprocessed_x     = normalized_x

    # if generating with logarithmic basis function
    if with_log:
        # generate logarithmic basis function for matrix input x
        log_features_x = log_basis(x)

        # combine logarithmic basis features into final matrix
        preprocessed_x = np.concatenate((preprocessed_x, log_features_x), axis = 1)

    # generate polynomial basis function for matrix input x
    poly_features_x    = polynomial_basis(x, degree)

    # iterate through the polynomial features of x (through degree)
    for poly_x in poly_features_x:
        preprocessed_x = np.concatenate((preprocessed_x, poly_x), axis=1)

    # if adding feature column with ones value.
    if with_ones:
        # ---------- Add ones to the matrix --------
        tmp       = np.ones([preprocessed_x.shape[0], preprocessed_x.shape[1] + 1])
        tmp[:,1:] = preprocessed_x
        preprocessed_x   = tmp

    # if generating with cross term basis function
    if cross_terms:
        # generate cross term basis function for matrix input x
        cross_term_features = cross_terms_basis(x)

        # combine cross term basis features into final matrix
        preprocessed_x = np.concatenate((preprocessed_x, cross_term_features), axis = 1)

    # if generating with square root basis function
    if with_sqrt:
        # generate square root basis function for matrix input x
        sqrt_features = sqrt_basis(x)

        # combine square root basis features into final matrix
        preprocessed_x = np.concatenate((preprocessed_x, sqrt_features), axis = 1)

    return preprocessed_x


%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

def func(x):
    a = -2
    b = 10
    return np.exp(a*x) * np.cos(b*x)

def func_integral(x):
    a = -2
    b = 10
    c = 5
    d = 52
    return np.exp(a*x) * (c* np.sin(b*x) - np.cos(b*x)) * (1/d)

def trapezoid_core(f,x,h):
    return 0.5 * h * (f(x + h) + f(x))

def trapezoid_method(f,a,b,N):
    x = np.linspace(a,b,N)
    h = x[1] - x[0]

    fint = 0.0

    for i in range(0,len(x) - 1, 1):
        fint += trapezoid_core(f,x[i],h)

    print("Iterations = " + str(i))
    return(fint)

def simpson_core(f,x,h):
    return (h/3)* (f(x) + 4*f(x+h) + f(x+2*h))

def simpsons_method(f,a,b,N):
    x = np.linspace(a,b,N)
    h = x[1] - x[0]

    Fint = 0.0

    for i in range (0, len(x) - 2, 2):
        Fint += simpson_core(f,x[i],h)

    if (N%2 == 0):
        Fint += simpson_core(f,x[-2], 0.5 * h)

    print("Iterations = " + str(i))
    return Fint

def romberg_core(f,a,b,i):
    h = b - a
    dh = h/2.**i
    K = h/2.**(i+1)

    M = 0.0
    for j in range(2**i):
        M += f(a + 0.5*dh + j*dh)

    return(K*M)

def romberg_integration(f,a,b,tol):

    i = 0

    imax = 1000

    delta = 100.0*np.fabs(tol)

    I = np.zeros(imax,dtype=float)

    I[0] = 0.5*(b-a)*(f(a) + f(b))

    i +=1

    while(delta>tol):

        I[i] = 0.5*I[i-1] + romberg_core(f,a,b,i)

        delta = np.fabs( (I[i] - I[i-1]) / I[i])

        print(i,I[i],I[i-1],delta)

        if (delta>tol):
            i += 1

            if (i > imax):
                print('Max iterations reached.')
                raiseStopIteration('Stopping iterations after ', i)

    print("Iterations = " + str(i))
    return(I[i])

print("function = e^(-2x) * cos(10x)")
answer = func_integral(np.pi) - func_integral(0)
print("Integral = " + str(answer))
print(" ")
print('Trapezoid method')
print(trapezoid_method(func,0,np.pi,10))
print(" ")
print("Simpson's method")
print(simpsons_method(func,0,np.pi,10))
print(" ")
print("Romberg's method")
tolerance = 1e-6
RI = romberg_integration(func,0,np.pi,tolerance)
print(RI, (RI - answer)/answer, tolerance


import numpy as np
import numpy.testing as npt
from statsmodels.tools import sequences

def test_discrepancy():
    space_0 = [[0.1, 0.5], [0.2, 0.4], [0.3, 0.3], [0.4, 0.2], [0.5, 0.1]]
    space_1 = [[1, 3], [2, 6], [3, 2], [4, 5], [5, 1], [6, 4]]
    space_2 = [[1, 5], [2, 4], [3, 3], [4, 2], [5, 1], [6, 6]]

    corners = np.array([[0.5, 0.5], [6.5, 6.5]])

    npt.assert_allclose(sequences.discrepancy(space_0), 0.1353, atol=1e-4)

    # From Fang et al. Design and modeling for computer experiments, 2006
    npt.assert_allclose(sequences.discrepancy(space_1, corners), 0.0081, atol=1e-4)
    npt.assert_allclose(sequences.discrepancy(space_2, corners), 0.0105, atol=1e-4)

def test_van_der_corput():
    sample = sequences.van_der_corput(10)
    out = [0., 0.5, 0.25, 0.75, 0.125, 0.625, 0.375, 0.875, 0.0625, 0.5625]
    npt.assert_almost_equal(sample, out)

    sample = sequences.van_der_corput(5, start_index=3)
    out = [0.75, 0.125, 0.625, 0.375, 0.875]
    npt.assert_almost_equal(sample, out)

def test_primes():
    primes = sequences.primes_from_2_to(50)
    out = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
    npt.assert_allclose(primes, out)

def test_halton():
    corners = np.array([[0, 2], [10, 5]])
    sample = sequences.halton(dim=2, n_sample=5, bounds=corners)

    out = np.array([[5., 3.], [2.5, 4.], [7.5, 2.3], [1.25, 3.3], [6.25, 4.3]])
    npt.assert_almost_equal(sample, out, decimal=1)

    sample = sequences.halton(dim=2, n_sample=3, bounds=corners, start_index=2)
    out = np.array([[7.5, 2.3], [1.25, 3.3], [6.25, 4.3]])
    npt.assert_almost_equal(sample, out, decimal=1)


import numpy as np

def bisection(func, a=0, b=1, tol=1e-6, maxits=1000, which="min"):
    """Implements a basic bisection search on the function f over the interval [a,b].

    Args:
        f (func): the function to optimize.
        a (float): left end-point to optimize over.
        b (float): the right end-point to optimize over.
        tol (float): relative tolerance for when to stop iterating.
        maxits (int): maximum number of iterations.
    """
    if which == "max":
        func = lambda x: func(-x)

    if func(a)*func(b) >= 0:
        raise ValueError("Bisection failed... f(a) and f(b) must be of oposite sign!")

    if func(a) == 0:
        return a

    if func(b) == 0:
        return b

    for i in range(maxits):
        midpoint = (b+a)/2
        f_mid = func(midpoint)
        f_a = func(a)
        f_b = func(b)

        if f_a*f_mid < 0:
            b = midpoint
        elif f_b*f_mid < 0:
            a = midpoint
        elif f_mid == 0:
            return midpoint

        if np.abs((b+a)/2 - midpoint) < tol:
            return (b+a)/2
        else:
            pass

def newton(f, df, x0, epsilon, maxiter):
    x = x0
    for n in range(0, maxiter):
        f_x = f(x)
        if abs(f_x) < epsilon:
            return x
        df_x = df(x)
        if df_x == 0:
            print("Error: derivative is zero")
        x -= f_x/df_x
    return x


import numpy as np
import time
from inspect import signature

def nparams(f):
	return len(signature(f).parameters)

def sign(x):
	if x >= 0:
		return 1.0
	else:
		return -1.0

def FindMinima(fn, gradfn, eta=0.1, threshold=1e-3):
	# Determine the number of arguments to the gradient.
	n_params = len(signature(gradfn).parameters)

	# Create a randomized starting position.
	position = (np.random.rand(n_params) - 0.5) * 20
	gradient = np.ones(n_params)

	while True:
		# Calculate the gradient.
		gradient = gradfn(*position)
		#print(gradient)

		# Check for zero gradient.
		closeness = np.abs(gradient).max()
		if closeness < threshold:
			break

		# Move each coordinate based on the gradient.
		cofactor  = ((closeness**4) / (1 + closeness**4)) + (eta / 10)
		for c in range(n_params):
			position[c] -= eta * sign(gradient[c]) * cofactor
			#position[c] -= eta * gradient[c] * cofactor

	# We may have reached a minimum. Return this position.
	return position

def Minimize(fn, gradfn, std_threshold=0.01, n_best=10):
	# Determine the number of arguments to the gradient.
	n_params = len(signature(gradfn).parameters)

	positions = []
	minima    = []
	# Find enough minima to start looking for convergence.
	for i in range(n_best):
		pos = FindMinima(fn, gradfn)
		val = fn(*pos)[0]
		minima.append(val)
		positions.append(pos)

	minima.sort()
	while np.array(minima[:n_best]).std() > std_threshold:
		print(len(minima))

		pos = FindMinima(fn, gradfn)
		val = fn(*pos)[0]
		minima.append(val)
		print(val)
		positions.append(pos)
		minima.sort()

	minidx  = np.array(minima).argmin()
	minimum = minima[minidx]
	minpos  = positions[minidx]
	return minimum, minpos

def f(x, y, z):
	return [-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z))]

def gradf(x, y, z):
	return [
		-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*np.cos(x) + (2 / 25)*x*np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z)),
		-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*np.cos(y) + (2 / 25)*y*np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z)),
		-np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*np.cos(z) + (2 / 25)*z*np.exp(-((x / 5)**2 + (y / 5)**2 + (z / 5)**2))*(np.sin(x) + np.sin(y) + np.sin(z))
	]

if __name__ == '__main__':
	start = time.time_ns()
	minimum, minpos = Minimize(f, gradf)
	stop = time.time_ns()
	print("Minimum: %f"%minimum)
	print("Pos:     %s"%str(minpos))
	print("Time:    %fs"%((stop - start) / 1e9))


import math as mt
def poisel(z,L=91.44,u=0.01,d=0.1,p=1,g=9.81,R=10):
    a=64*L*u/(p*d**2)
    return (8*R**2)/(d**2)*(-a+mt.sqrt(a**2+8*g*(z+L)))
ti=[]
t0=5 # Waktu Awal
t1=50 # Waktu Akhir
n=5000
h=(t1-t0)/n
fa=poisel(t0)
fn=poisel(t1)
## pengisian matriks
while t0<t1+h:
    ti.append(t0)
    t0=t0+h
print('nilai t=',ti)
panjang=len(ti)
####
pois=[]
for i in range (0, panjang):
    v=poisel(ti[i])
    pois.append(v)
print('Nilai hasil fungsi =',pois)
###
length=len(pois)
sigma=0
for i in range(1,length-1):
    sigma=sigma+pois[i]
print("sigma =",sigma)
## METODE TRAPEZOID
hasil=(fa+2*sigma+fn)*h/2
print("Hasil Trapezoid =", hasil)
####
sigmaGanjil=0
sigmaGenap=0
#METODE SIMPSON
for i in range(1,length-1):
    if i%2!=0 :
        sigmaGanjil=sigmaGanjil+pois[i]
    else:
        sigmaGenap=sigmaGenap+pois[i]
print("hasil sigma ganjil = ",sigmaGanjil,", hasil sigma genap =",sigmaGenap)
simpson=(fa+4*sigmaGanjil+2*sigmaGenap+fn)*h/3
print("Nilai Hasil Simpson =",simpson)

from sympy import *
z=Symbol('z')
L=91.44
u=0.01
d=0.1
p=1
g=9.81
R=10
a=64*L*u/(p*d**2)
fd=(8*R**2)/(d**2)*(-a+sqrt(a**2+8*g*(z+L)))
integral=fd.integrate(z)
print("Hasil Integral=",integral)

from scipy import integrate
hasilIntegral=integrate.quad(poisel,5,50)
print("Hasil Integral Eksak",hasilIntegral)

er_simson=abs(simpson-hasilIntegral[0])
er_trape=abs(hasil-hasilIntegral[0])
print("Hasil Eror Sympson = ", er_simson)
print("Hasil Eror Trapezoid = ", er_trape)

Chapter 9/Tugas Nomor 2 Integrasi Numerik.py


# coding:utf8
print("一.一维矩阵的加，减,平方，三角函数")
import  numpy as np
a=np.array([10,20,30,40])
b=np.arange(4)#0,1,2,3
c=b**2
d=np.sin(a)
e=np.cos(a)
f=np.tan(a)
print(a+b)
print(a-b)
print(c)
print(d)
print(e)
print(f)
print(b<3)#返回Ture或者False,bool类型的矩阵

print("二.多维矩阵的乘法")
import  numpy as np
a=np.array([[1,1],
            [0,1]])
b=np.arange(4).reshape((2,2))
c=a*b#两个同型矩阵对应元素的乘积
c_dot=np.dot(a,b)#矩阵的乘法运算
c_dot_2=a.dot(b) #矩阵ab的乘积
print(c)
print(c_dot)
print(c_dot_2)

print("三.多维矩阵行列运算")
import  numpy as np
a=np.array([[1,2,3],[2,3,4]])#shape=2x4
print(a)
print(np.sum(a)) #15
print(np.max(a)) #4
print(np.min(a)) #1
print(np.sum(a,axis=1)) #行求和[6,9]
print(np.sum(a,axis=0)) #列求和[3,5,7]
print(np.max(a,axis=0)) #列最大[2,3,4]
print(np.min(a,axis=1)) #行最小[1,2]

print("四.矩阵的运算 ")
import  numpy as np
A=np.arange(2,14).reshape(3,4)
print(A)
print(np.argmin(A))  #0
print(np.argmax(A))  #11
print(np.mean(A))  # 均值7.5
print(A.mean())    #均值 7.5
print(np.average(A))# 均值7.5
print(np.median(A))  #中位数7.5
print(np.cumsum(A)) # 累加[ 2  5  9 14 20 27 35 44 54 65 77 90]
print(np.diff(A))#后一个元素减去前一个元素
#将所有非零元素的行与列坐标分割开，重构成两个分别关于行和列的矩阵
print(np.nonzero(A))

print("五.矩阵的排序 ")
import  numpy as np
A=np.arange(14,2,-1).reshape(3,4)#从14到3，步长为-1
print(A)
print(np.sort(A))  #每一行排序
print(np.sort(A, axis=0))  #每一列排序

print("六.矩阵的转置有两种表示方法")

import  numpy as np
A=np.arange(1,10).reshape(3,3)
print(A)
print(np.transpose(A))
print(A.T)

print("七.矩阵截取clip")
import numpy as np
a=np.arange(1,13).reshape((3,4))
print(a)
print(np.clip(a,5,9))#最小5，最大9，小于5的都成了5，大于9的都成了9


import numpy as np
import matplotlib.pyplot as plt

def cost_function(X, y, theta, m):
    a = np.dot(X, theta) # h(X)
    b = a - y[:][np.newaxis].T
    return np.dot(b.T, b)[0][0] / 2 / m

def gradient_descent(X, y, iteration=1500, alpha=0.01):
    m = len(X) # num of samples
    theta = np.zeros((1, 2)).T # initiate theta = [0, 0]
    X = np.concatenate((np.ones((m, 1)), X[np.newaxis][:].T), axis=1) # add a colum of one to X

    for i in range(iteration):
        theta_temp = theta
        a = np.dot(X, theta_temp) # h(X)
        b = a - y[:][np.newaxis].T
        theta[0] = theta_temp[0] - alpha * np.dot(b.T, X[:,0]) / m
        theta[1] = theta_temp[1] - alpha * np.dot(b.T, X[:,1]) / m

    return theta

def main():
    data = np.loadtxt('./ex1/ex1/ex1data1.txt', delimiter=',', )
    X = data[:, 0]
    y = data[:, 1]

    # plot the data
    plt.scatter(X, y, marker='x', c='red')
    plt.xlabel('Population of City in 10,000s')
    plt.ylabel('Profit in $10,000s')

    theta = gradient_descent(X, y)
    print('Profile in areas of 35,000 is: %.2f' % np.dot(np.array([1, 3.5]), theta))
    print('Profile in areas of 70,000 is: %.2f' % np.dot(np.array([1, 7.0]), theta))

    # plot the prediction
    temp_x = np.linspace(5, 25, 20)
    temp_y = theta[0] + theta[1] * temp_x
    plt.plot(temp_x, temp_y)

    plt.legend(['Linear regression', 'Training data'], loc='lower right')
    plt.show()

if __name__ == '__main__':
    main()


import numpy as np

def wtd_mean(values, weights):
    '''
    Calculates the weighted mean of a set of values
    :param values: The values to take the weighted mean of
    :param weights: The weights
    :return:
    '''
    values = np.array(values)
    weights = np.array(weights)
    return np.dot(values, weights) / weights.sum()

def lin_interp(x, x1, y1, x2, y2):
    '''
    Performs linear interpolation between (x1,y1) and (x2,y2) to the point x.
    If x is not between x1 and x2, no worries, we just follow the linear map onwards.
    The map is f(x) = m (x-x1) + y1, where
    m=(y2-y1)/(x2-x1)
    However, if x1==x2, our function is the constant function
    f(x) = (y1+y2)/2
    :param x: The input value to interpolate to
    :param x1: The x coordinate of our first point
    :param y1: The y coordinate of our first point
    :param x2: The x coordinate of our second point
    :param y2: The y coordinate of our second point
    :return: The linearly interpolated value as described above
    '''
    if x1 == x2:
        return (y2+y1)/2
    m = (y2-y1)/(x2-x1)
    #print("x="+str(x)+"interp ("+str(x1)+","+str(y1)+") to ("+str(x2)+","+str(y2)+")")
    return m*(x-x1)+y1

def approx_equal(a, b, per_diff=1e-15) -> bool:
    '''
    Checks if 2 values are approximately equal, but checking
    | a - b | / (avg(|a|, |b|) < per_diff
    note: if a==b==0 then we return True, and do not divide by zero
    :param a: The first value to check
    :param b: The second value to check
    :param per_diff: The maximum percent difference to allow.
    :return: True/False
    '''
    if (a == 0) and (b == 0):
        return True
    return np.abs(b-a)/((np.abs(a)+np.abs(b))/2) <= per_diff

def wtd_median(values, weights):
    '''
    Calculates the weighted median of a set of values.
    :param values: The values to calculated the weighted median of
    :param weights: The weights of the values
    :return: The weighted median
    '''
    pairs = list(zip(values, weights))
    sorted_pairs = sorted(pairs, key=lambda x: x[0])
    #print("Sorted Pairs="+str(sorted_pairs))
    total_weight = np.sum(weights)
    half_way = total_weight/2
    if len(values) == 0:
        # Nothing to do
        return 0
    elif len(values) == 1:
        # We have only 1 value
        return values[0]
    #Now we have at least 2 values, we start with first value
    prev_area = 0
    xs_with_cumulative_area_equal = []
    for i in range(len(sorted_pairs)):
        (x, weight) = sorted_pairs[i]
        area = prev_area+weight
        #print("area="+str(area)+" prev_area="+str(prev_area)+" str")
        if approx_equal(area, half_way) and len(xs_with_cumulative_area_equal) == 0:
            # Okay we are equal
            next_x = sorted_pairs[i+1][0]
            xs_with_cumulative_area_equal.append(x+0.5)
        elif area > half_way:
            # We have gone from being below to being above, the median is here
            # Unless we have other places with area equal
            if len(xs_with_cumulative_area_equal) > 0:
                # Okay we need to average the previous equal areas
                xs_with_cumulative_area_equal.append(x-0.5)
                return np.mean(xs_with_cumulative_area_equal)
            # Alright nothing was previously equal, do this
            (prev_x, prev_weight) = sorted_pairs[i-1]
            if i!=(len(sorted_pairs)-1):
                next_x = sorted_pairs[i+1][0]
            else:
                next_x = (x - prev_x) + x
            return lin_interp(half_way, prev_area, x-0.5, area, x+0.5)
        prev_area = area
    return None


from torch import nn, tensor, max
import numpy as np

# Cross entropy example
# One hot
# 0: 1 0 0
# 1: 0 1 0
# 2: 0 0 1
Y = np.array([1, 0, 0])
Y_pred1 = np.array([0.7, 0.2, 0.1])
Y_pred2 = np.array([0.1, 0.3, 0.6])
print(f'Loss1: {np.sum(-Y * np.log(Y_pred1)):.4f}')
print(f'Loss2: {np.sum(-Y * np.log(Y_pred2)):.4f}')

# Softmax + CrossEntropy (logSoftmax + NLLLoss)
loss = nn.CrossEntropyLoss()

# target is of size nBatch
# each element in target has to have 0 <= value < nClasses (0-2)
# Input is class, not one-hot
Y = tensor([0], requires_grad=False)

# input is of size nBatch x nClasses = 1 x 4
# Y_pred are logits (not softmax)
Y_pred1 = tensor([[2.0, 1.0, 0.1]])
Y_pred2 = tensor([[0.5, 2.0, 0.3]])

l1 = loss(Y_pred1, Y)
l2 = loss(Y_pred2, Y)

print(f'PyTorch Loss1: {l1.item():.4f} \nPyTorch Loss2: {l2.item():.4f}')
print(f'Y_pred1: {max(Y_pred1.data, 1)[1].item()}')
print(f'Y_pred2: {max(Y_pred2.data, 1)[1].item()}')

# target is of size nBatch
# each element in target has to have 0 <= value < nClasses (0-2)
# Input is class, not one-hot
Y = tensor([2, 0, 1], requires_grad=False)

# input is of size nBatch x nClasses = 2 x 4
# Y_pred are logits (not softmax)
Y_pred1 = tensor([[0.1, 0.2, 0.9],
                  [1.1, 0.1, 0.2],
                  [0.2, 2.1, 0.1]])

Y_pred2 = tensor([[0.8, 0.2, 0.3],
                  [0.2, 0.3, 0.5],
                  [0.2, 0.2, 0.5]])

l1 = loss(Y_pred1, Y)
l2 = loss(Y_pred2, Y)
print(f'Batch Loss1:  {l1.item():.4f} \nBatch Loss2: {l2.data:.4f}')


"""
SiEPIC Photonics Package

Author:     Mustafa Hammood
            Mustafa@siepic.com

            https://github.com/SiEPIC-Kits/SiEPIC_Photonics_Package

Solvers and simulators: Mach-Zehnder Interferometer simulator using a simple transfer function of an MZI

Based on L. Chrostowski MATLAB implementation
"""
#%% dependent packages
import matplotlib.pyplot as plt
import numpy as np

#%% user input
# the wavelength range of interest.
wavelength_start = 1500e-9
wavelength_stop = 1600e-9
resolution = 0.001

# Effective index:
# - as a Taylor expansion around the central wavelength, lam0
lam0 = 1.55
n1=2.4
n2=-1.0
n3=0.0  # these are constants from the waveguide model.

# plot, and check if this is as expected:
L1=100
L2=110  # Units [µm, microns], variable

# Complex propagation constant
alpha = 1e-3   # propagation loss [micron^-1]; constant

#%% analysis

lambda_0 = np.linspace(wavelength_start, wavelength_stop, round((wavelength_stop-wavelength_start)*1e9/resolution))*1e6

def neff(lam):
    return n1 + n2*(lam-lam0) + n3*(lam-lam0)**2

def beta(lam):
    return	2*np.pi*neff(lam)/lam - 1j*alpha/2*np.ones(np.size(lam))

# MZI transfer function
def T_MZI(L1, L2, lam):
    return 0.25*np.abs(np.exp(-1j*beta(lam)*L1) + np.exp(-1j*beta(lam)*L2))**2

#%% application

plt.figure()
plt.plot(lambda_0, neff(lambda_0),linewidth=2.0)
plt.xlabel ('Wavelength [$\mu$m]')
plt.ylabel ('Effective index')
plt.autoscale(enable=True, axis='x', tight=True)
plt.autoscale(enable=True, axis='y', tight=True)
plt.title('Effective index model')

plt.figure()
plt.plot(lambda_0, T_MZI(L1, L2, lambda_0), linewidth=3)
plt.xlabel ('Wavelength [$\mu$m]')
plt.ylabel ('Transmission')
plt.autoscale(enable=True, axis='x', tight=True)
plt.autoscale(enable=True, axis='y', tight=True)
plt.title('MZI transfer function')

plt.figure()
T_MZI_dB = 10*np.log10(T_MZI(L1, L2, lambda_0))
plt.plot(lambda_0, T_MZI_dB, linewidth=3)
plt.xlabel ('Wavelength [$\mu$m]')
plt.ylabel ('Transmission [dB]')
plt.autoscale(enable=True, axis='x', tight=True)
plt.autoscale(enable=True, axis='y', tight=True)
plt.title ('MZI transfer function')


import numpy as np
import data_reader
import sys

def newton_interpolation(x_values:list,y_values:list,x:float):
	nor = len(x_values) #nor - number of rows

	#DT - Difference Table
	#DT[i,i] = f[x_i]
	#DT[i,j] = f[x_i,x_(i+1),...,x_j] for j > i

	DT = np.zeros((nor,nor),dtype=np.float64)

	###############[calculate the kth divided difference]###############
	for i in range(0,nor):
		DT[i,i] = y_values[i]
	for m in range(1,nor):
		for j in range(0,nor-m):
			DT[j,j+m] = (DT[j+1,j+m] - DT[j,j+m-1])/(x_values[j+m]-x_values[j])

	#print(DT) #for debugging purposes only.

	##############[evaluate the interpolating polynomial at x]##########
	p = np.zeros(nor,dtype=np.float64)
	p[0] = y_values[0]
	for k in range(1,nor):
		p[k] = p[k-1]
		g = 1
		for i in range(0,k):
			g *= (x - x_values[i])
		g *= DT[0,k]
		p[k] += g

	return p[nor-1]

if __name__ == '__main__':
	help_message = f"""
	Usage: python3.7 {sys.argv[0]} <filename> x
	* the first option is for the filename
	* the second option is the point you want to evaluate the interpolating polynomial.
	"""

	if len(sys.argv) != 3:
		print(help_message)
		exit(1)

	f = data_reader.InterpolationData(sys.argv[1])
	data = f.get_data_in_two_lists()
	x = float(sys.argv[2])

	print(newton_interpolation(data[0],data[1],x))


import numpy as np

def get_rotation_matrix(angle):

    thetha = (angle/180.) * np.pi
    rotation_matrix = np.array([
                            [np.cos(thetha), -np.sin(thetha)],
                            [np.sin(thetha), np.cos(thetha) ]
                            ])
    return rotation_matrix

def rotate_90_clockwise(vector):
    """
    vector:tuple
    """
    v = np.array(vector,dtype= np.float)

    m = get_rotation_matrix(-90)

    r = np.matmul(m,v)

    r = r.astype('int')

    return r

def rotate_90_counterclockwise(vector):
    """
    vector:tuple
    """
    v = np.array(vector)

    m = get_rotation_matrix(90)

    r = np.matmul(m,v)
    r = r.astype('int')
    return r

if __name__ == "__main__":
    l  = (-1,0)
    up  = (0,1)
    r = (1,0)
    down = (0,-1)

    r0 = rotate_90_clockwise(l)

    print("Rotate clockwise l {} must give up {}, r={} ".format(l,up, r0))

    r0 = rotate_90_clockwise(up)

    print("Rotate clockwise up {} must give r {}, r={} ".format(up,r, r0))

    r0 = rotate_90_clockwise(r)

    print("Rotate clockwise r {} must give down {}, r={} ".format(r,down, r0))

    r0 = rotate_90_clockwise(down)

    print("Rotate clockwise down {} must give l {}, r={} ".format(down,l, r0))

    print("\n\n")
    ### COUNTERCLOCKWISE

    r0 = rotate_90_counterclockwise(l)

    print("Rotate counterclockwise l {} must give down {}, r={} ".format(l,down, r0))

    r0 = rotate_90_counterclockwise(up)

    print("Rotate clockwise up {} must give l {}, r={} ".format(up,l, r0))

    r0 = rotate_90_counterclockwise(r)

    print("Rotate clockwise r {} must give up {}, r={} ".format(r,up, r0))

    r0 = rotate_90_counterclockwise(down)

    print("Rotate clockwise down {} must give r {}, r={} ".format(down,r, r0))


import numpy as np

def Gauss(n, A):
    X = np.zeros(n,dtype=float)
    L = np.arange(n,dtype=int)   #index vector
    S = np.zeros(n,dtype=float)
    for i in range(0, n):
        L[i] = i
        smax = 0
        for j in range(0, n):
            smax = max(smax, np.abs(A[i][j]))
        S[i] = smax
    for k in range(0, n-1):
        rmax = 0
        for i in range(k, n):
            r = np.abs(A[L[i]][k]/S[L[i]])
            if r > rmax:
                rmax = r
                j = i
        temp = L[j]
        L[j] = L[k]
        L[k] = temp
        for i in range(k+1, n):
            xmult = A[L[i]][k]/A[L[k]][k]
            A[L[i]][k] = xmult
            for j in range (k+1, n):
                A[L[i]][j] = A[L[i]][j] - (xmult*A[L[k]][j])
    return X,A,L

def Solve(n,A,L,B,X):
    for k in range(0, n-1):
        for i in range(k+1, n):
            B[L[i]] = B[L[i]]-(A[L[i]][k]*B[L[k]])
    X[n-1] = B[L[n-1]]/A[L[n-1]][n-1]
    for i in range(n-1, -1, -1):
        sum1 = B[L[i]]
        for j in range(i+1, n):
            sum1 = sum1-A[L[i]][j]*X[j]
        X[i] = sum1/A[L[i]][i]
    return X

def FullGauss(n,A,B):
    X,A,L = Gauss(n, A)
    X = Solve(n,A,L,B,X)
    return X

#Example
A = np.array([[3.0, 4, 3],
              [1, 5, -1],
              [6, 3, 7]])
B = np.array([10.0, 7, 15])
print(A, "\n")
print(B, "\n")

n = 3
X,A,L = Gauss(n,A)
X = Solve(n,A,L,B,X)
print("Solution ", X)
#solution [2,1,0]


import numpy as np
import math

"""
:A: square matrix (np.ndarray)
:returns: determinant of A
"""
def det(A: np.ndarray):
    if A.shape[0] < 2 or A.shape[0] != A.shape[1]:
        raise Exception('Wrong matrix dimensions for determinant')

    n = A.shape[0]

    if n == 2:
        return A[0][0]*A[1][1] - A[0][1]*A[1][0]

    a = np.copy(A)
    s = 0

    for i in range(n):
        s += (-1 if i%2!=0 else 1) * a[0][i] * det(np.delete(np.delete(a, 0, 0), i, 1))
        a = np.copy(A)

    return s

"""
:returns: matrix A with chess table rule applied
"""
def chess_table_rule(A: np.ndarray):
    if A.shape[0] < 2 or A.shape[0] != A.shape[1]:
        raise Exception('Wrong matrix dimensions for chess table rule')

    a = np.copy(A)
    n = len(A)
    c = 0

    for i in range(n):
        for j in range(n):
            if c%2!=0:
                a[i][j] *= -1
            c += 1

    return a

"""
:returns: subdeterminant of matrix A (minor matrix)
"""
def subdet(A: np.ndarray):
    if A.shape[0] < 3 or A.shape[0] != A.shape[1]:
        raise Exception('Wrong matrix dimensions for adjungate')

    n = len(A.shape)
    a = np.copy(A)
    na = []

    for i in range(n + 1):
        row = []
        for j in range(n + 1):
           row.append(det(np.delete(np.delete(a, i, 0), j, 1)))
        na.append(row)

    return np.array(na)

"""
:A: square matrix (np.ndarray)
:returns: adjungate of A
"""
def adj(A: np.ndarray):
    return chess_table_rule(subdet(A)).T

def inv(A: np.ndarray):
    d = det(A)
    if d == 0:
        raise Exception('Matrix cannot be inverted')
    return adj(A) / d

"""
Convert data from cartesian coorinates to polar coordinates.
:returns: New X and Y values
"""
def to_polar(X: np.ndarray, Y: np.ndarray):
    nX, nY = [], []

    for x, y in zip(X, Y):
        nX.append((x**2 + y**2)**0.5)
        if x != 0:
            nY.append(math.degrees(math.atan(y / x)))
        else:
            nY.append(0)

    """
    # For visual testing
    import matplotlib.pyplot as plt

    plt.scatter(X, Y, c="red")
    plt.scatter(nX, nY, c="green")
    plt.show()
    """
    return np.array(nX), np.array(nY)

def sigmoid(X):
    return 1 / (1 + np.exp(-X))

if __name__ == '__main__':
    import random

    print('Testing', __file__)

    A = np.array([
        [1, 1, 3],
        [2, 3, -4],
        [3, -2, 5],
    ])

    print(det(A))

    B = np.array([
        [1, -2, 0],
        [2, -1, 1],
        [-1, 0, -3]
    ])

    C = np.array([
        [1, 2],
        [3, 4]
    ])

    # Test data
    X = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    y = np.array([random.randint(10, 100) for _ in range(10)])

    print(chess_table_rule(C))
    print(adj(B))
    print(inv(B))

    print(X, y)
    print(to_polar(X, y))


import numpy as np
from scipy.optimize import curve_fit

###############################################################
# Standard functions
def linear_func(x, a, b):
    """a * x + b"""
    return a * x + b

def quad_func(x, a, b, c):
    """ a*x**2 + b*x + c """
    return a*x**2 + b*x + c
def quad_func2(x, a, b, c):
    """a*(x-b)**2 + c"""
    return a*(x-b)**2 + c

def power_func(x, a, b):
    """a * x ** b"""
    return a * x ** b
def power_func2(x, x0, a, b):
    """a * (x-x0) ** b"""
    return a * (x-x0) ** b

def exp(x, a, b):
    """a * np.exp(-b * x)"""
    return a * np.exp(-b * x)

def exp_func(x, a, b, x0):
    """a * np.exp(-b * (x-x0)) + c"""
    return a * np.exp(-b * (x-x0))

def gaussian(x, a, x0, sigma, b):
    """ a * np.exp(- (x - x0) ** 2. / (2. * sigma ** 2.)) + b"""
    return a * np.exp(- (x - x0) ** 2. / (2. * sigma ** 2.)) + b

def gaussian_norm(x, x0, sigma):
    """ 1. / (np.sqrt(2 * np.pi * sigma ** 2.)) * np.exp(- (x - x0) ** 2. / (2. * sigma ** 2.)) """
    return 1. / (np.sqrt(2 * np.pi * sigma ** 2.)) * np.exp(- (x - x0) ** 2. / (2. * sigma ** 2.))

def double_gaussian(x, a1, x1, sigma1, b1, a2, x2, sigma2, b2):
    """a1 * np.exp(- (x - x1) ** 2. / (2. * sigma1)) + b1 + a2 * np.exp(- (x - x2) ** 2. / (2. * sigma2)) + b2"""
    return a1 * np.exp(- (x - x1) ** 2. / (2. * sigma1)) + b1 + a2 * np.exp(- (x - x2) ** 2. / (2. * sigma2)) + b2

def lorentzian(x, x0, gamma, alpha):
    """alpha * gamma / ((x-x0)**2 + gamma ** 2)"""
    return alpha * gamma / ((x-x0)**2 + gamma ** 2)

def lorentzian_norm(x, x0, gamma):
    """1. / np.pi * gamma / ((x-x0)**2 + gamma ** 2)"""
    return 1. / np.pi * gamma / ((x-x0)**2 + gamma ** 2)

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

def fit(func, xdata, ydata, **kwargs):
    if func is None:
        popt, pcov = curve_fit(linear_func, xdata, ydata, **kwargs)
    else:
        popt, pcov = curve_fit(func, xdata, ydata, **kwargs)
    return popt, pcov


# These are the libraries will be used for this lab.

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

# Convert 2D List to 2D Tensor

twoD_list = [[11, 12, 13], [21, 22, 23], [31, 32, 33]]
twoD_tensor = torch.tensor(twoD_list)
print("The New 2D Tensor: ", twoD_tensor)

# Try tensor_obj.ndimension(), tensor_obj.shape, tensor_obj.size()

print("The dimension of twoD_tensor: ", twoD_tensor.ndimension())
print("The shape of twoD_tensor: ", twoD_tensor.shape)
print("The shape of twoD_tensor: ", twoD_tensor.size())
print("The number of elements in twoD_tensor: ", twoD_tensor.numel())

# Convert tensor to numpy array; Convert numpy array to tensor

twoD_numpy = twoD_tensor.numpy()
print("Tensor -> Numpy Array:")
print("The numpy array after converting: ", twoD_numpy)
print("Type after converting: ", twoD_numpy.dtype)

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

new_twoD_tensor = torch.from_numpy(twoD_numpy)
print("Numpy Array -> Tensor:")
print("The tensor after converting:", new_twoD_tensor)
print("Type after converting: ", new_twoD_tensor.dtype)

# Try to convert the Panda Dataframe to tensor

df = pd.DataFrame({'a':[11,21,31],'b':[12,22,312]})

print("Pandas Dataframe to numpy: ", df.values)
print("Type BEFORE converting: ", df.values.dtype)

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

new_tensor = torch.from_numpy(df.values)
print("Tensor AFTER converting: ", new_tensor)
print("Type AFTER converting: ", new_tensor.dtype)

# Practice: try to convert Pandas Series to tensor

df = pd.DataFrame({'A':[11, 33, 22],'B':[3, 3, 2]})

# Use tensor_obj[row, column] and tensor_obj[row][column] to access certain position

tensor_example = torch.tensor([[11, 12, 13], [21, 22, 23], [31, 32, 33]])
print("What is the value on 2nd-row 3rd-column? ", tensor_example[1, 2])
print("What is the value on 2nd-row 3rd-column? ", tensor_example[1][2])

tensor_example[0][0]

# Use tensor_obj[begin_row_number: end_row_number, begin_column_number: end_column number]
# and tensor_obj[row][begin_column_number: end_column number] to do the slicing

tensor_example = torch.tensor([[11, 12, 13], [21, 22, 23], [31, 32, 33]])
print("What is the value on 1st-row first two columns? ", tensor_example[0, 0:2])
print("What is the value on 1st-row first two columns? ", tensor_example[0][0:2])

# Give an idea on tensor_obj[number: number][number]

tensor_example = torch.tensor([[11, 12, 13], [21, 22, 23], [31, 32, 33]])
sliced_tensor_example = tensor_example[1:3]
print("1. Slicing step on tensor_example: ")
print("Result after tensor_example[1:3]: ", sliced_tensor_example)
print("Dimension after tensor_example[1:3]: ", sliced_tensor_example.ndimension())
print("================================================")
print("2. Pick an index on sliced_tensor_example: ")
print("Result after sliced_tensor_example[1]: ", sliced_tensor_example[1])
print("Dimension after sliced_tensor_example[1]: ", sliced_tensor_example[1].ndimension())
print("================================================")
print("3. Combine these step together:")
print("Result: ", tensor_example[1:3][1])
print("Dimension: ", tensor_example[1:3][1].ndimension())

# Use tensor_obj[begin_row_number: end_row_number, begin_column_number: end_column number]

tensor_example = torch.tensor([[11, 12, 13], [21, 22, 23], [31, 32, 33]])
print("What is the value on 3rd-column last two rows? ", tensor_example[1:3, 2])

# Practice: Use slice and index to change the values on the matrix tensor_ques.

tensor_ques = torch.tensor([[11, 12, 13], [21, 22, 23], [31, 32, 33]])

# Calculate [[1, 0], [0, 1]] + [[2, 1], [1, 2]]

X = torch.tensor([[1, 0],[0, 1]])
Y = torch.tensor([[2, 1],[1, 2]])
X_plus_Y = X + Y
print("The result of X + Y: ", X_plus_Y)

# Calculate 2 * [[2, 1], [1, 2]]

Y = torch.tensor([[2, 1], [1, 2]])
two_Y = 2 * Y
print("The result of 2Y: ", two_Y)

# Calculate [[1, 0], [0, 1]] * [[2, 1], [1, 2]]

X = torch.tensor([[1, 0], [0, 1]])
Y = torch.tensor([[2, 1], [1, 2]])
X_times_Y = X * Y
print("The result of X * Y: ", X_times_Y)

# Calculate [[0, 1, 1], [1, 0, 1]] * [[1, 1], [1, 1], [-1, 1]]

A = torch.tensor([[0, 1, 1], [1, 0, 1]])
B = torch.tensor([[1, 1], [1, 1], [-1, 1]])
A_times_B = torch.mm(A,B)
print("The result of A * B: ", A_times_B)


import numpy as np

from typing import Union, List

def complex_sinusoid(w: float, t: Union[np.ndarray, float]) -> np.ndarray:
    """ This function evaluates a complex sinusoid.

    Parameters
    ----------

    w : float
        The angular frequency.

    t : np.ndarray
        The instances to evaluated at.

    Returns
    -------

    np.ndarray
        A complex sinusoid.

    """

    return np.exp(1j * w * t)

def steering_vector(theta: float, n_sensors: int, d: float = 0.5) -> np.ndarray:
    """ This function returns a steering vector pointing at direction `theta`.

    Parameters
    ----------

    theta : float
        The direction of arrival in degrees

    n_sensors : int
        The number of recievers

    d : float
        Antenna spacing in number of wavelengths

    Returns
    -------

    a : np.ndarray
        The steering vector

    """

    s = d * np.arange(n_sensors)
    w = 2 * np.pi * np.sin(np.pi * theta / 180)
    a = np.exp(-1j * w * s)
    return a

def steering_matrix(thetas: Union[np.ndarray, List], n_sensors: int, d: float = 0.5) -> np.ndarray:
    """ This function returns the steering matrix of an array.

    Parameters
    ----------
    thetas : Union[np.ndarray, List]
        A list with the direction of arrivals.

    n_sensors : int
        Then number of sensors

    d : float
        The array spacing in wavelengths.

    Returns
    -------
    np.ndarray
        The steering matrix of shape (n_sensors, n_doas).

    """

    return np.array([steering_vector(theta, n_sensors, d) for theta in thetas]).T

def single_radar_target(theta: float, n_sensors: int, s: np.ndarray = None, d: float=0.5, rnd_phase: bool=True) -> np.ndarray:
    """ This function measures a single incoming radar target

    Parameters
    ----------
    s : np.ndarray
        The source signal

    theta : float
        The direction of arrival

    n_sensors : int
        The number of sensors

    d : float
        The element spacing in number of wavelengths

    rnd_phase : bool
        If true, adds a random phase shift to the signal at each snapshot

    Returns
    -------
    np.ndarray
        The measured source

    """

    if s is None:
        s = np.random.randn(32).astype(np.complex128)

    if rnd_phase:
        s *= np.exp(1j * 2 * np.pi * np.random.rand(1))

    a = steering_vector(theta, n_sensors, d=d)
    return np.outer(a, s)

def wideband_steering_vector(theta1, theta2, n_sensors, d=0.5):
    s = d * np.arange(n_sensors)

    wa = 2 * np.pi * np.sin(np.pi * theta1 / 180)
    wb = 2 * np.pi * np.sin(np.pi * theta2 / 180)

    a = (np.exp(1j * wb * s) - np.exp(-1j * wa * s)) / (1j * 2 * np.pi * s)
    return a


import numpy as np
import matplotlib.pyplot as mpl
from scipy.optimize import fmin_powell as fmin
from scipy.odr import *
from scipy.stats import chi2

t 		= np.asfarray([ 352 , 701 , 1048 , 1398 , 1751 , 2099 , 2446 , 2805 ])

t_err	= np.asfarray([ 5 , 9 , 9 , 9 , 9 , 15 , 15 , 15 ])

dT 		= np.asfarray([ 10.0 , 19.7 , 30.2 , 40.4 , 49.9 , 60.5 , 70.4 , 80.0 ])

dT_err	= np.asfarray([ 0.1 , 0.2 , 0.2 , 0.2 , 0.3 , 0.3 , 0.4 , 0.4 ])

U		= 12
I		= 10
m		= 1000.0

RCFbound	= 2.0/(np.sum(np.power(m/(t_err*U*I),2.0)*(np.power(dT,2.0)+np.power(dT_err,2.0))))

def t_value_odr(C,delta_T):
	U		= 12
	I		= 10
	m		= 1000.0
	return (m*delta_T*C[0])/(U*I)
model	= Model(t_value_odr)
data	= RealData(dT, t, sx=dT_err, sy=t_err)

odrfit	= ODR(data, model, beta0=[1.0])

odrfit.set_job(fit_type=2)
output1	= odrfit.run()
C_val1	= output1.beta[0]
chisq1	= output1.sum_square
pval1	= chi2.cdf(chisq1,1)
C_err1	= output1.sd_beta[0]

odrfit.set_job(fit_type=0)
output2	= odrfit.run()
C_val2	= output2.beta[0]
chisq2	= output2.sum_square
pval2	= chi2.cdf(chisq2,1)
C_err2	= output2.sd_beta[0]

# I wrote the commented sections of code to make sure the odrfit matches with least squares code written by me. It does.
"""
def t_value(C,delta_T):
	U		= 12
	I		= 10
	m		= 1000.0
	return (m*delta_T*C)/(U*I)

def chisquare(t_obs, sigma_t, C, delta_T):
	t_exp	= t_value(C,delta_T)
	return	np.sum(np.power((t_obs-t_exp)/sigma_t, 2.0))
C_val_old, chisq_old = fmin(lambda x: chisquare(t, t_err, x, dT), np.asfarray([1.0]), full_output=True, disp=False)[0:2]
C_err_old = np.abs(C_val_old - fmin(lambda x: np.power(chisquare(t, t_err, x, dT)-chisq_old-1,2.0), np.asfarray([C_val_old]),disp=False))

print 'Difference between manual least squares minimization and ODR-packages:',C_val_old-C_val1
"""

print 'Least squares with t-errors only:'
print 'C:',C_val1, ' sigma_C:', C_err1,' Chi2:', chisq1, ' P-value:',pval1
print 'Least squares with both errors:'
print 'C:',C_val2, ' sigma_C:', C_err2,' Chi2:', chisq2, ' P-value:',pval2
print 'RFC-bound for std:',np.sqrt(RCFbound)

Statistical Methods 2014 - Home exam/Code/exam_p04.py


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

# Name: Baylee Mumma, Christine Outlaw
# Email: mumma103@mail.chapman.edu
# Course: PHYS220/MATH220/CPSC220 Fall 2017
# Assignment: CLASSWORK 12

import numpy as np
import matplotlib.pyplot as plt

def y_dot(t,y,x,nu,F):
    """y_dot(t,y,x,nu,F)
    The differential equation given for the dot derivative of y.
    """
    return -(nu*y)+x-(x**3)+(F*np.cos(t))

def x_dot(y):
    """x_dot(y)
    The differential equation given for the dot derivative of x.
    """
    return y

def rk4(a, b, x0, y0, nu=0, F=0, xdot = x_dot, ydot = y_dot):
    """rk(a, b, x0, y0, nu=0, F=0, xdot = x_dot, ydot = y_dot)

    Args:
        a  (float) : Lower bound, t = a*2*pi
        b  (float) : Upper bound, t = b*2*pi
        x0 (float) : Initial position of ball
        y0 (float) : Initial velocity of ball
        nu (float) : Constant damping coefficient
        F  (float) : Constant force amplitude coefficient
        xdot (function) : Part of the differential equation
        ydot (function) : Part of the differential equation

    Returns:
        t (array) : Array over the time interval with equal dt = .001
        x (array) : Array containing the position of the ball at each time in the time array
        y (array) : Array containing the velocity of the ball at each time in the time array
    """
    dt = 0.001
    start = 2*a*np.pi
    end = 2*b*np.pi
    n = int(np.ceil((end-start)/dt))
    t = np.linspace(start,end,n)
    x = np.zeros(n)
    y = np.zeros(n)
    x_dot_vec = np.zeros(n)
    y_dot_vec = np.zeros(n)
    x[0] = x0
    y[0] = y0
    for k in range(n):
        x_dot_vec[k] = x_dot(y[k])
        y_dot_vec[k] = ydot(t[k],y[k],x[k],nu,F)
        if k == n-1:
            break
        else:
            k1y = dt*ydot(t[k],y[k],x[k],nu,F)
            k2y = dt*ydot((t[k]+dt/2),(y[k]+k1y/2),x[k],nu,F)
            k3y = dt*ydot((t[k]+dt/2),(y[k]+k2y/2),x[k],nu,F)
            k4y = dt*ydot((t[k]+dt),(y[k]+k3y),x[k],nu,F)
            rky = (k1y+(2*k2y)+(2*k3y)+k4y)/6
            y[k+1] = y[k]+rky
            k1x = dt*xdot(y[k])
            k2x = dt*xdot(y[k]+k1x/2)
            k3x = dt*xdot(y[k]+k2x/2)
            k4x = dt*xdot(y[k]+k3x)
            rkx = (k1x+(2*k2x)+(2*k3x)+k4x)/6
            x[k+1] = x[k]+rkx
    return (t,x,y)

def plot(t,x,title=""):
    """plot(x,t,title='')
    Generate a plot of a time-valued function.

    Args:
        t (array) : Array of time domain
        x (array) : Array of values representing position
        title (string) : Title of the plot

    Returns:
        A plot of a function with respect to time.
        """
    plt.plot(t,x)
    plt.xlabel('t')
    plt.ylabel('x')
    plt.title(title, fontsize = 20)
    plt.show()

def parametric(x,y,title = ''):
    """parametric(x,y,title = '')
    Generate a parametric plot of velocity with respect to position.

    Args:
        x (array) : Postion of ball with respect to time
        y (array) : Velocity of ball with respect to time
        title (array) : Title of plot

    Returns:
        A parametric plot.
        """
    plt.plot(x,y)
    plt.xlabel('x')
    plt.ylabel('$\dot{x}$')
    plt.title(title, fontsize = 20)
    plt.show()

def scatter(x,y,n,title = ''):
    """scatter(x,y,n,title = '')
    Generate a Poincare section of the parametric curve.

   Args:
        x (array) : Position with respect to time
        y (array) : Velocity with respect to time
        n (int)   : Number of points in scatter plot
        title (string) : Title of plot

   Returns:
       A scatter plot with points at every integer multiple of 2*pi*n for n[0,50].
       """
    ax = plt.subplot
    plt.title(title, fontsize = 20)
    plt.xlabel('x')
    plt.ylabel('$\dot{x}$')
    for k in range(n):
        plt.scatter(x[6*k],y[6*k])
    plt.show()


import sympy as sp
x, y, z = sp.symbols('x y z')
sp.init_printing()

# Here we simply give the input needed as The Following:
# ( X0 ) & ( X1 ) & ( X2 )
# ( Fx ) is the equation of the function
# ( n ) is the number of Iterations needed

x0 = 4.5

x1 = 5.5

x2 = 5

Fx = x**3 - 13*x - 12

n = 3

print("____________________________________________________________________________________________________________\n  "
      "Iteration |    X3    |    H0   |    H1    |    D0    |    D1   |    A    |    B    |    C    |   Error  "
      "\n------------------------------------------------------------------------------------------------------------")
for i in range(n):
    # Finding F(x0) & F(x1) & F(x2)
    fx0 = Fx.subs(x, x0).evalf(); fx1 = Fx.subs(x, x1).evalf(); fx2 = Fx.subs(x, x2).evalf();
    # Finding H 0 & 1
    H0 = x1 - x0; H1 = x2 - x1;
    # Finding Delta 0 & 1
    d0 = (fx1 - fx0)/(x1 - x0); d1 = (fx2 - fx1)/(x2 - x1);
    # Finding A & B & C
    A = (d1 - d0)/(H1 + H0); B = A*H1 + d1; C = fx2;
    # To Calculate X3 we have two solutions one is when delta is positive and the other is when its negative
    DeltaPos = (B + sp.sqrt(B**2 - (4*A*C)))
    DeltaNeg = (B - sp.sqrt(B**2 - (4*A*C)))

    if (abs(DeltaPos) > abs(DeltaNeg)): x3 = x2 + ((- 2*C)/DeltaPos)
    else: x3 = x2 + ((- 2*C)/DeltaNeg)
    Error = abs(((x3 - x2)/x3)*100)
    print("     {0:2d}     |  {1:.5f} | {2:.5f} | {3:.5f} | {4:.5f} | {5:.4f} | {6:.4f} | {7:.4f} | {8:.4f} | {9:.6f} \n"
      .format(i+1, x3, H0, H1, d0, d1, A, B, C, Error))
    # Changing Variables for The Next Iteration
    x0 = x1; x1 = x2; x2 = x3;

# Finally displaying the output with the value of every rule that was needed in order to get the final answer and the percentage of error after each iteration


from sklearn import datasets
from sklearn import svm
from sklearn.model_selection import cross_validate
import numpy as np
import matplotlib.pyplot as plt

def plot_svc(model):
    """Plot the decision function for a 2D SVC"""
    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    # create grid to evaluate model
    x = np.linspace(xlim[0], xlim[1], 30)
    y = np.linspace(ylim[0], ylim[1], 30)
    Y, X = np.meshgrid(y, x)
    xy = np.vstack([X.ravel(), Y.ravel()]).T
    P = model.decision_function(xy).reshape(X.shape)

    # plot decision boundary and margins
    ax.contour(X, Y, P, colors='k',
               levels=[-1, 0, 1], alpha=0.5,
               linestyles=['--', '-', '--'])

    # plot support vectors
    ax.scatter(model.support_vectors_[:, 0],
               model.support_vectors_[:, 1],
               s=300, linewidth=1, facecolors='none');
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)

def kfold_svm(X, y, number_of_folds=5, kernel='rbf', C=1, gamma='auto', degree=5, coef0=0.0):
    clf = svm.SVC(kernel=kernel, C=C, gamma=gamma, degree=degree, coef0=coef0)
    cross_validate_result = cross_validate(clf, X, y, cv=number_of_folds, scoring='accuracy',
                                           return_estimator=True, return_train_score=True)
    clfs = cross_validate_result['estimator']
    train_scores = cross_validate_result['train_score']
    test_scores = cross_validate_result['test_score']

    print("train_scores: " )
    print(train_scores)
    print()
    print("test_scores: " )
    print(test_scores)
    best_one_index = np.argmax(test_scores)

    return clfs[best_one_index], test_scores[best_one_index]

def test_case_evaluator(test_case):
    if test_case == 0:
        x, y = datasets.make_circles(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=42, factor=0.2)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=1, gamma=1)
    elif test_case == 1:
        x, y = datasets.make_circles(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=42, factor=0.2)
        clf, best_score = kfold_svm(x, y, kernel='poly', C=1, degree=5, coef0=0.5)
    elif test_case == 2:
        x, y = datasets.make_circles(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=42, factor=0.2)
        clf, best_score = kfold_svm(x, y, kernel='linear', C=1)  # which doesn't work fine

    elif test_case == 10:
        x, y = datasets.make_circles(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=30, factor=0.8)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=1, gamma=1)
    elif test_case == 11:
        x, y = datasets.make_circles(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=30, factor=0.8)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=1, gamma=1000)  # overfit
    elif test_case == 12:
        x, y = datasets.make_circles(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=30, factor=0.8)
        clf, best_score = kfold_svm(x, y, kernel='linear', C=1)  # very bad
    elif test_case == 13:
        x, y = datasets.make_circles(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=30, factor=0.8)
        clf, best_score = kfold_svm(x, y, kernel='poly', C=1, degree=5, coef0=0.5)

    elif test_case == 20:
        x, y = datasets.make_moons(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=32)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=1, gamma=1)
    elif test_case == 21:
        x, y = datasets.make_moons(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=32)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=1, gamma=1000)  # very overfit
    elif test_case == 22:
        x, y = datasets.make_moons(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=32)
        clf, best_score = kfold_svm(x, y, kernel='poly', C=1, degree=3, coef0=0.5)
    elif test_case == 23:
        x, y = datasets.make_moons(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=32)
        clf, best_score = kfold_svm(x, y, kernel='linear', C=1)
    elif test_case == 24:
        x, y = datasets.make_moons(n_samples=NUMBER_OF_SAMPLES, noise=0.1, random_state=32)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=0.0001, gamma=1)  # very unsensitive

    elif test_case == 30:
        x, y = datasets.make_blobs(n_samples=NUMBER_OF_SAMPLES, centers=2, cluster_std=1, random_state=150)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=1, gamma=1)
    elif test_case == 31:
        x, y = datasets.make_blobs(n_samples=NUMBER_OF_SAMPLES, centers=2, cluster_std=1, random_state=150)
        clf, best_score = kfold_svm(x, y, kernel='poly', C=1, degree=3, coef0=0.5)
    elif test_case == 32:
        x, y = datasets.make_blobs(n_samples=NUMBER_OF_SAMPLES, centers=2, cluster_std=1, random_state=150)
        clf, best_score = kfold_svm(x, y, kernel='linear', C=1)

    elif test_case == 40:
        x, y = datasets.make_blobs(n_samples=NUMBER_OF_SAMPLES, centers=2, cluster_std=2, random_state=175)
        clf, best_score = kfold_svm(x, y, kernel='rbf', C=1, gamma=1)  # a liitle overfit
    elif test_case == 41:
        x, y = datasets.make_blobs(n_samples=NUMBER_OF_SAMPLES, centers=2, cluster_std=2, random_state=175)
        clf, best_score = kfold_svm(x, y, kernel='poly', C=1, degree=3, coef0=0.5)
    elif test_case == 42:
        x, y = datasets.make_blobs(n_samples=NUMBER_OF_SAMPLES, centers=2, cluster_std=2, random_state=175)
        clf, best_score = kfold_svm(x, y, kernel='linear', C=1)
    return x, y, clf, best_score

NUMBER_OF_SAMPLES = 2000

print("Choose a test case: (input number) \n")
print("0. two circles with rbf kernel")
print("1. two circles with poly degree 5 kernel")
print("2. two circles with linear kernel which doesn't work good")
print("10. two more difficult circles with rbf kernel")
print("11. two more difficult circles with rbf kernel that overfits")
print("12. two more difficult circles with linear kernel whcih doesn't work good")
print("13. two more difficult circles with poly degree 5 kernel whcih doesn't work good")
print("20. moon (crescent) with rbf kernel")
print("21. moon (crescent) with rbf kernel with very overfit")
print("22. moon (crescent) with poly degree 3 kernel")
print("23. moon (crescent) with linear kernel which is not very good")
print("24. moon (crescent) with rbf kernel with low sensitivity")
print("30. two blobs with low std and rbf kernel")
print("31. two blobs with low std and poly degree 3 kernel")
print("32. two blobs with low std and linear kernel")
print("40. two blobs with more std and rbf kernel")
print("41. two blobs with more std and poly degree 3 kernel")
print("42. two blobs with more std and linear kernel")

test_case = int(input("input the test case you want: "))

x, y, clf, best_score = test_case_evaluator(test_case)

plt.scatter(x[:, 0], x[:, 1], c=y, s=50, cmap='autumn')
plot_svc(clf)
plt.show()
plt.clf()
print("best score: "+ str(best_score))


# Decision Tree

given dataset $D=\left\{(x^{(i)},y^{(i)})\right\}$

decision tree is trying to pick $(feature, value)$ that partition the dataset to subsets

after that partition, elements in each subsets is similar in total, i.e we gain certainty.

continue the process, until we get subset that is very pure or partition too many times.

we thus create a tree called the decision tree.

when predicting, find leaf subset of that sample, then use typical value of leaf as prediction.

## information gain

we use entropy to measure the uncertainty of data.

for classfication problem, assume $y_{(i)} \in \left\{1,...,k\right\}$, we have the entropy of dataset $D$:

$$H(D) = E(-log\ p_{i}) = -\sum_{i=1}^{k}p_{i}log\ p_{i}$$

$p_{i}$ is the frequency of i-th class, it defines the uncertainty of $D$.

suppose we partition $D$ according to feature $A$ into $D_{1},...,D_{n}$, we have:

$$H(D|A)=\sum_{i=1}^{n}\frac{\#D_{i}}{\#D}H(D_{i})$$

that is: the uncertainty of $D$ after knowing $A$.

information gain is uncertainty loss:

$$g(D,A) = H(D) - H(D|A)$$

decision tree ID3 choose feature $A$ that maximize $g(D,A)$ until:

1. subset is empty
2. information gain $g(D,A)\le\epsilon$

## information gain ratio

if use information gain, we prefer feature $A$ such that $\#A$ is large.

more precisely, we prefer features that is uncertain

$$H_{A}(D) =-\sum_{i=1}^{n}\frac{\#D_{i}}{\#D}log\ \frac{\#D_{i}}{\#D}$$

defines that uncertainty, it is the entropy of viewing category of $A$ as labels.

to fix that problem, we define the information gain ratio:

$$g_{R}(D,A)=\frac{g(D,A)}{H_{A}(D)}=\frac{H(D)-H(D|A)}{H_{A}(D)}$$

algorithm that uses $g_{R}(D,A)$ is C4.5

## pruning

we need to pruning the decision tree $\Rightarrow $ lower model's complexity $\Rightarrow $ mitigate overfit

suppose now we have a decision tree $T$, use $\left | T \right | $ to denote the number of leaves of $T$, and these leaves are $T_{1},...,T_{\left | T \right | }$.

then entropy of leaf $t$: $H(T_{t})$

total entropy of these leaves:

$$C(T) = \sum_{t=1}^{\left | T \right |} \left | T_{t} \right |H(T_{t})$$

we want these minimize this entropy, and at the same time limit model's complexity, give rise to the loss function:

$$C_{\alpha}(T) = C(T) + \alpha\left | T \right |$$

in practice, pruning is from leaves to root.

if pruning a node result in a decrease of the loss function, the operate this pruning.

## CART-classification and regression tree

CART can solve both the classification and regression problem.

CART simply uses different strategies for them.

for regression problem, we try to find feature $j$ and cutting point $s$ that minimize the square error:

$$\underset{j,s}{min}\left[\underset{c_{1}}{min}\sum_{x_{i} \in R_{1}(j, s)}(y_{i} - c_{1})^{2} + \underset{c_{2}}{min}\sum_{x_{i} \in R_{2}(j, s)}(y_{i} - c_{2})^{2}\right]$$

rather than optimizing information gain or information gain ratio.

CART optimize Gini-index when facing a classification problem:

$$Gini(D) = E(1 - p_{i}) = \sum_{i=1}^{k}p_{i}(1 - p_{i})$$

here, rather than self-information $-log\ p_{i}$ uses in entropy, we use $1 - p_{i}$ to indicate the information of event with probability $p_{i}$.

## Practice using sklearn

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier

iris = load_iris()
X = iris.data[:, 2:]  # petal length and width
y = iris.target

tree_clf = DecisionTreeClassifier(max_depth=2)
tree_clf.fit(X, y)

"""visualize using graphviz, need 1.pip install graphviz, 2.brew install graphviz"""
from graphviz import Source
from sklearn.tree import export_graphviz

export_graphviz(tree_clf,
                out_file="iris_tree.dot",
                feature_names=iris.feature_names[2:],
                class_names=iris.target_names,
                rounded=True,
                filled=True
               )

Source.from_file("iris_tree.dot")

tree_clf.predict_proba([[5, 1.5]]), tree_clf.predict([[5, 1.5]])

"""criterion can switch from gini to entropy"""
entropy_tree_clf = DecisionTreeClassifier(criterion="entropy", max_depth=3)

"""hyper-parameters for regularization"""
regularized_tree_clf = DecisionTreeClassifier(max_depth=5,  # maximum depth of that tree
                                              max_leaf_nodes=20,  # maximum number of leaf nodes
                                              max_features=8,  # maximum number of features when splitting each node
                                              min_samples_split=10,  # min number of samples of a node before it can split
                                              min_samples_leaf=4,  # min number of samples of a leaf node
                                              min_weight_fraction_leaf=0.01  # same as min_samples_leaf, but by weight frac
                                             )

"""CART(sklearn uses) can also regression"""
from sklearn.tree import DecisionTreeRegressor

tree_reg = DecisionTreeRegressor(max_depth=3)

### moon dataset

"""make moon dataset"""
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split

X, y = make_moons(n_samples=10000, noise=0.4)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

"""grid search"""
from sklearn.model_selection import GridSearchCV

param_grid = [{"max_leaf_nodes": [2, 5, 10, 20], "min_samples_split": [3, 4]}]

tree_clf = DecisionTreeClassifier()
grid_search = GridSearchCV(tree_clf, param_grid, cv=3, verbose=3)
grid_search.fit(X_train, y_train)

"""using best estimator to predict"""
from sklearn.metrics import accuracy_score

y_predict = grid_search.predict(X_test)
accuracy_score(y_true=y_test, y_pred=y_predict)

### using multiple trees

"""generate 1000 subsets, each 100 instances"""
from sklearn.model_selection import ShuffleSplit

n_trees = 1000
n_instances = 100

mini_sets = []

rs = ShuffleSplit(n_splits=n_trees, test_size=len(X_train) - n_instances, random_state=42)
# make train_size = len(X_train) - (len(X_train) - n_instances) = n_instances

for mini_train_index, mini_test_index in rs.split(X_train):
    X_mini_train = X_train[mini_train_index]
    y_mini_train = y_train[mini_train_index]
    mini_sets.append((X_mini_train, y_mini_train))

"""train each subset on grid_search.best_estimator_"""
from sklearn.base import clone
import numpy as np

forest = [clone(grid_search.best_estimator_) for _ in range(n_trees)]

accuracy_scores = []

for tree, (X_mini_train, y_mini_train) in zip(forest, mini_sets):
    tree.fit(X_mini_train, y_mini_train)

    y_pred = tree.predict(X_test)
    accuracy_scores.append(accuracy_score(y_test, y_pred))

np.mean(accuracy_scores)

"""save all predictions"""
Y_pred = np.empty([n_trees, len(X_test)], dtype=np.uint8)

for tree_index, tree in enumerate(forest):
    Y_pred[tree_index] = tree.predict(X_test)

"""use majority vote, improve performance"""
from scipy.stats import mode

y_pred_majority_votes, n_votes = mode(Y_pred, axis=0)
accuracy_score(y_test, y_pred_majority_votes.reshape(-1))


#
#	Solved Problems in Geostatistics
#
# ------------------------------------------------
#	This file includes some general statistics
#	functions, which are needed for the problems
# ------------------------------------------------
#
#

from numpy import *

# Standartize the array
def stand(z, mean, st_dev):
	z = (z - mean) / st_dev
	return z

# Calculate weighted variance
def w_var(weights, data, w_mean):
	var = 0.0
	weights = norm(weights)
	for i in xrange(len(data)):
		var = var + weights[i] * (data[i]-w_mean)**2
	var = sqrt(var/weights.sum())
	return var

# Calculate weighted mean
def w_mean(weights, data):
	z_1 = 0.0
	z = 0.0
	for i in xrange(len(data)):
		z = z + weights[i]*data[i]
		z_1 = z_1 + weights[i]
	z = z/z_1
	return z

# Calculate standard deviation (sqrt[var])
def calc_quadr_var(array,mean):
	var = 0.0
	for i in xrange(len(array)):
		var = var + ((array[i]-mean)**2)/len(array)
	var = sqrt(var)
	return var

# Calculate mean
def calc_mean_array(array):
	size = len(array)
	sum = array.sum() / size
	return sum

# Normalize array
def norm(array):
	sum = array.sum()
	for i in xrange(len(array)):
		array[i] = array[i]/sum
	return array

# Calculate corellation coefficient
def corr_coef(x,y):
	coef = 0.0
	mean_x = x.mean()
	var_x = calc_quadr_var(x,mean_x)
	mean_y = y.mean()
	var_y = calc_quadr_var(y,mean_y)
	var = var_x * var_y
	for i in xrange(len(x)):
		coef = coef + x[i]*y[i]
	coef = (coef - len(x)*mean_x*mean_y) / ((len(x)-1)* var)
	return coef

# Make random array from existing with same values
def rand_arrays(array1, array2, n):
	array1_rand = zeros( (n), dtype = float)
	array2_rand = zeros( (n), dtype = float)
	for i in xrange(n):
		value = random.randint(len(array1))
		array1_rand[i] = array1[value]
		array2_rand[i] = array2[value]
	return [array1_rand, array2_rand]

# Calculate weighted covariance
def calc_cov(x, y, w):
	cov = 0.0
	weigths = 0.0
	for i in xrange(len(x)):
		cov = cov + w[i] * (x[i] - w_mean(w,x)) * (y[i] - w_mean(w,y))
		weigths = weigths + w[i]
	cov = cov / weigths
	return cov

# Calculate weighted correlation coefficient
def w_corr_coef(x, y, w):
	w_coef = calc_cov(x, y, w) / sqrt(calc_cov(x, x, w) * calc_cov(y, y, w))
	return w_coef

# Calculate distance between points
def calc_distance(x1,y1,x2,y2):
	h = sqrt((x2-x1)**2+(y2-y1)**2)
	return h

def calc_distance_3d(x1, y1, z1, x2, y2, z2):
	h = sqrt((x2 - x1)**2 + (y2 - y1)**2 + (z1 - z2)**2)
	return h

from numpy import mean, std, sum, sqrt, sort, corrcoef, tanh, arctanh
from numpy.random import randint

def bootstrap_correlation(x,y):
	idx = randint(len(x),size=(1000,len(x)))
	bx = x[idx]
	by = y[idx]
	mx = mean(bx,1)
	my = mean(by,1)
	sx = std(bx,1)
	sy = std(by,1)
	r = sort(sum( (bx - mx.repeat(len(x),0).reshape(bx.shape)) *
	(by - my.repeat(len(y),0).reshape(by.shape)), 1)
	/ ((len(x)-1)*sx*sy))
#bootstrap confidence interval (NB! biased)
	conf_interval = (r[25],r[975])
#bootstrap standard error using Fisher's z-transform (NB! biased)
	std_err = tanh(std(arctanh(r))*(len(r)/(len(r)-1.0)))
	return (std_err, conf_interval)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug  1 16:27:03 2018

@author: shivap
"""

import numpy as np

# =============================================================================
# Rosenbrock Function
# https://www.sfu.ca/~ssurjano/rosen.html
# =============================================================================
def myrosenbrock(X):
    X=np.asarray(X)
    X = X.reshape((-1,2))
    if len(X.shape)==1:# one observation
        x1=X[0]
        x2=X[1]
    else:# multiple observations
        x1=X[:,0]
        x2=X[:,1]
    fx = 100*(x2-x1**2)**2 + (x1-1)**2
    return fx.reshape(-1,1)

# =============================================================================
#  Six-hump Camel Function
#  https://www.sfu.ca/~ssurjano/camel6.html
# =============================================================================
def mysixhumpcamp(X):
    X=np.asarray(X)
    X = np.reshape(X,(-1,2))
    if len(X.shape)==1:
        x1=X[0]
        x2=X[1]
    else:
        x1=X[:,0]
        x2=X[:,1]
    term1 = (4-2.1*x1**2+(x1**4)/3) * x1**2
    term2 = x1*x2
    term3 = (-4+4*x2**2) * x2**2
    fval = term1 + term2 + term3
    return fval.reshape(-1,1)

# =============================================================================
# Beale function
# https://www.sfu.ca/~ssurjano/beale.html
# =============================================================================
def mybeale(X):
    X=np.asarray(X)
    X = X.reshape((-1,2))
    if len(X.shape)==1:
        x1=X[0]
        x2=X[1]
    else:
        x1=X[:,0]
        x2=X[:,1]
    fval = (1.5-x1+x1*x2)**2+(2.25-x1+x1*x2**2)**2+(2.625-x1+x1*x2**3)**2
    return fval.reshape(-1,1)

def func(ht,X):
    # ht is a categorical index
    # X is a continuous variable

    if ht==0: #rosenbrock
        return myrosenbrock(X)
    elif ht==1: # six hump
        return mysixhumpcamp(X)
    elif ht==2: # beale
        return mybeale(X)


import numpy as np

def inverse_truncated_modal(M, num_modes, svd=None):
	'''Invert `M` with `num_modes` modes.

	Modes are given by a singular value decomposition of the input matrix.
	If `svd` is given, it is used instead. Modes are ordered in descending
	order by their singular values.

	Parameters
	----------
	M : ndarray
		The matrix to invert. This matrix must be two-dimensional.
	num_modes : integer
		The number of modes to use for the inversion. Higher-order modes
		are ignored.
	svd : SVD object
		A precalculated singular value decomposition object. This will
		be used if supplied, to avoid recalculation of an SVD.

	Returns
	-------
	ndarray
		The inverted matrix.
	'''
	if svd is None:
		from .singular_value_decomposition import SVD
		svd = SVD(M, num_modes)

	U, S, Vt = svd.svd
	return (Vt.T / S).dot(U.T)

def inverse_truncated(M, rcond=1e-15, svd=None):
	'''Invert `M` truncating the number of modes.

	Modes are given by a singular value decomposition of the input matrix.
	If `svd` is given, it is used instead. Modes are ordered in descending
	order by their singular values. All modes with a singular value lower than
	`rcond` times the maximum singular value, will be ignored.

	Parameters
	----------
	M : ndarray
		The matrix to invert. This matrix must be two-dimensional.
	rcond : scalar
		The relative condition number of the highest-order mode that must
		be used for inversion.
	svd : SVD object
		A precalculated singular value decomposition object. This will
		be used if supplied, to avoid recalculation of an SVD.

	Returns
	-------
	ndarray
		The inverted matrix.
	'''
	if svd is None:
		from .singular_value_decomposition import SVD
		svd = SVD(M)

	U, S, Vt = svd.svd
	S_inv = np.array([1/s if abs(s) > (rcond * S.max()) else 0 for s in S])

	return (Vt.T * S_inv).dot(U.T)

def inverse_tikhonov(M, rcond=1e-15, svd=None):
	'''Invert `M` using Tikhonov regularization.

	Tikhonov regularization will be an identity matrix, with strength
	`rcond` * the maximum singular value of the matrix.

	Parameters
	----------
	M : ndarray
		The matrix to invert. This matrix must be two-dimensional.
	rcond : scalar
		The relative strength of the regularization. An identity matrix
		will be used for the regularization.
	svd : SVD object
		A precalculated singular value decomposition object. This will
		be used if supplied, to avoid recalculation of an SVD.

	Returns
	-------
	ndarray
		The inverted matrix.
	'''
	if svd is None:
		from .singular_value_decomposition import SVD
		svd = SVD(M)

	U, S, Vt = svd.svd
	S_inv = S / (S**2 + (rcond * S.max())**2)

	return (Vt.T * S_inv).dot(U.T)


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

"""
二维离散傅里叶(Fourier)变换
"""

from .fourier import fft
from .fourier import ifft
import numpy as np

def dft2d(fxy:np.matrix):
    """二维离散傅里叶变换

    :Parameters:
        - fxy: 二维离散序列

    :Returns: 二维频谱序列
    """
    M,N = fxy.shape
    m = np.arange(M).reshape(M, 1)
    n = np.arange(N).reshape(N, 1)
    wm = np.exp((-2j * np.pi / M) * np.dot(m, m.T))
    wn = np.exp((-2j * np.pi / N) * np.dot(n, n.T))
    fuv = np.dot(np.dot(wm, fxy), wn)   # wm和wn为对称矩阵，不用提心转置问题
    return fuv

def idft2d(fuv:np.matrix):
    """二维离散傅里叶逆变换

    :Parameters:
        - fuv: 二维频谱序列

    :Returns: 二维离散序列
    """
    M,N = fuv.shape
    m = np.arange(M).reshape(M, 1)
    n = np.arange(N).reshape(N, 1)
    wm = np.exp((2j * np.pi / M) * np.dot(m, m.T))
    wn = np.exp((2j * np.pi / N) * np.dot(n, n.T))
    fxy = np.dot(np.dot(wm, fuv), wn) / (M * N)  # wm和wn为对称矩阵，不用提心转置问题
    return fxy

def fft2d_shift(img:np.matrix):
    """平移二维序列

    :Parameters:
        - img: 二维序列

    :Returns: 平移后的序列
    """
    M,N = img.shape
    s = np.zeros((M, N), dtype=img.dtype)
    mu = int(np.ceil(M / 2))
    md = int(np.trunc(M / 2))
    nu = int(np.ceil(N / 2))
    nd = int(np.trunc(N / 2))
    s[:md, :nd] = img[mu:, nu:]
    s[:md, nd:] = img[mu:, :nu]
    s[md:, :nd] = img[:mu, nu:]
    s[md:, nd:] = img[:mu, :nu]
    return s

def fft2d(fxy:np.matrix):
    """二维离散傅里叶变换

    基于一维fft计算太慢，可以考虑使用原位计算优化。
    """
    # raise Exception('Not implementation')
    M,N = fxy.shape
    fuv = np.zeros((M, N), dtype=np.complex)
    for k in np.arange(M):
        fuv[k, :] = fft(fxy[k, :])
    for k in np.arange(N):
        fuv[:, k] = fft(fuv[:, k])
    return fuv

def ifft2d(fuv:np.matrix):
    """二维离散傅里叶逆变换
    """
    # raise Exception('Not implementation')
    M,N = fuv.shape
    fxy = np.zeros((M, N), dtype=np.complex)
    for k in np.arange(M):
        fxy[k, :] = ifft(fuv[k, :])
    for k in np.arange(N):
        fxy[:, k] = ifft(fxy[:, k])
    return fxy


"""
Questão 2 do laboratorio 7: Interpolação por MMQ pela seria de Fourier(exponencial)
"""
import numpy as np
from math import pi, sin
import matplotlib.pyplot as plt

def sistemaAumentado(x, y, dim):
    m = len(x)
    A = np.empty((dim, dim))
    b = np.empty((dim))
    soma = []
    for i in range(0, dim + 2):
        aux = 0
        for k in range(0, m):
            aux = aux + x[k] ** i
        soma.append(aux)

    for i in range(0, dim):
        for j in range(i, dim):
            A[i, j] = soma[i + j]
            if (i != j):
                A[j, i] = A[i, j]

    b = []
    for i in range(0, dim):
        aux = 0
        for k in range(0, m):
            aux = aux + y[k] * (x[k] ** (i))
        b.append(aux)

    return A, b

T = 2*pi
Im = 1
x = np.linspace(-T/2, T/2, 30)
y = np.array([])
for i in x:
    if (-T/2) <= i < 0:
        y = np.append(y, 0)
    else:
        y = np.append(y, Im*sin((2*pi/T)*i))
n_x = np.arange(x[0], x[-1], 0.001)
A, b = sistemaAumentado(x, y, 3)
aprox = np.linalg.solve(A, b)
aprox = aprox[::-1]
p = np.poly1d(aprox)
plt.plot(x, y, 'ro', label='Pontos marcados')
plt.plot(n_x, p(n_x), 'r-', label='MMQ')
plt.title('MMQ')
plt.xlabel('X')
plt.ylabel('Y')
plt.style.use('ggplot')
plt.tight_layout()
plt.legend(loc='best')
plt.grid()
plt.show()


""" Orbital Mechanics for Engineering Students Problem 1.2
Question:
Given four vectors A, B, C, and D and the identities from Problem 1.1, show
    (A x B) . (C x D) = (A . C)(B . D) - (A . D)(B. C)
Written by: J.X.J. Bannwarth
"""
from sympy.vector import CoordSys3D
from sympy.vector import Vector
from sympy import symbols
from sympy import simplify

# Title
print("Orbital Mechanics for Engineering Students Problem 1.2")

# Coordinate system
N = CoordSys3D('N')
i, j, k = N.base_vectors()

# Define vectors
ax, ay, az = symbols("ax ay az")
bx, by, bz = symbols("bx by bz")
cx, cy, cz = symbols("cx cy cz")
dx, dy, dz = symbols("dx dy dz")
A = ax * i + ay * j + az * k
B = bx * i + by * j + bz * k
C = cx * i + cy * j + cz * k
D = dx * i + dy * j + dz * k

# Analytical
print("(1) Answer")
print("(A x B) . (C x D)")
print("A . [B x (C x D)] - interchangeability")
print("A . [C(B . D) - D(B . C)] - bac-cab")
print("(A . C)(B . D) - (A . D)(B . C) - expand")

# Check the equations work out
print("\n(2) Check")
LHS = simplify((A.cross(B)).dot(C.cross(D)))
RHS = simplify(A.dot(C) * B.dot(D) - A.dot(D) * B.dot(C))
print("(A x B) . (C x D) =", LHS)
print("(A . C)(B . D) - (A . D)(B . C) =", RHS)
if simplify(LHS - RHS) == 0:
    print("(A x B) . (C x D) == (A . C)(B . D) - (A . D)(B. C)")


'''

Betweenness centrality of a node v is the sum of the fraction of all-pairs shortest paths that pass through v.

'''
import networkx as nx

def calculate(network):
    try:
        n = nx.betweenness_centrality(network)
    except:
        return 0

    if len(n.values()) == 0:
        return 0
    else:
        return round(sum(n.values())/len(n.values()), 7)


# Assignemnt on 2 sample T test

import numpy as np

"""
1.Two sets of ten students selected at random from a college were taken:One set was given memory test as they were and the other was given the memory test after two weeks of training and the scores are given below:

Set A	10	8	7	9	8	10	9	6	7	8
Set B	12	8	8	10	8	11	9	8	9	9

Do you think there is a significant effect due to training ?
"""
print("Assignment 1")

sample1=np.array([10,8,7,9,8,10,9,6,7,8])
sample2=np.array([12,8,8,10,8,11,9,8,9,9])
#print(len(sample1),len(sample2))

from scipy.stats import ttest_ind

tstatistics,pvalue=ttest_ind(sample1,sample2)
print("T Statistics:",tstatistics)
print("pvalue:",pvalue)

if pvalue<0.05:
    print("Reject the Null Hypothesis")
    print("Significant Effect due to Training")
else:
    print("Fails to Reject the Null Hypothesis")
    print("No Significant Effect due to Training")
print("\n")

"""
2.A group of 5 patients treated with medicine A weigh 42,39,48,60 and 41 kgs; a second group of 7 patients from the same hospital treated with medicine B weigh 38,42,56,64,68,69 and 62 kgs. Do you agree with the claim that medicine B increases weight significantly.
"""
print("Assignment 2")

sample1=np.array([42,39,48,60,41])
sample2=np.array([8,42,56,64,68,69,62])

tstatistics,pvalue=ttest_ind(sample1,sample2)
print("T Statistics:",tstatistics)
print("pvalue:",pvalue)

if pvalue<0.05:
    print("Reject the Null Hypothesis")
    print("Medicine B increase weight significantly")
else:
    print("Fails to Reject the Null Hypothesis")
    print("Medicine B does not increase weight significantly")
print("\n")

"""
3.Samples of two types of electric bulbs were tested for length of life and the following data were obtained

	Type I	Type II
No of Samples	8	7
Mean of the Samples (in hrs.)	1134	1024
SD of the samples (in hrs.)	35	40

Test at 5 percent level, whether the difference in sample means is significant
"""
print("Assignment 3")
ns1=8
ns2=7
means1=1134
means2=1024
sds1=35
sds2=40

from scipy.stats import ttest_ind_from_stats

tstatistics,pvalue=ttest_ind_from_stats(\
    mean1=means1,std1=sds1,nobs1=ns1,\
    mean2=means2,std2=sds1,nobs2=ns2)
print("T Statistics:",tstatistics)
print("pvalue:",pvalue)

if pvalue<0.05:
    print("Reject the Null Hypothesis")
    print("Two types of bulbs differ significantly with respect to theri mean life")
else:
    print("Fails to Reject the Null Hypothesis")
    print("Two types of bulbs differ significantly with respect to theri mean life")
print("\n")


import numpy as np
import matplotlib.pyplot as plt
"""
Y = ((Y_(i+1) - Y_i)/(X_(i+1) - X_i))*(X - X_i) +Y_i

Programación

1. Cargar dato:
X = []
Y = []

2. Def Interpolación Lineal: (X[],Y[]):
for(i=0, len(X[])-1):
    if(x >= xi and x =< X_(i+1) ):
        calcule m, b, y
        return y
"""

datos = np.loadtxt("datos.dat")
x_list = []
y_list = []

for i in range(0,datos.shape[0]):
    x_list.append(datos[i][0])
    y_list.append(datos[i][1])

def intPLineal(x_list, y_list,x):
    for i in range(0, len(x_list)):
        if(x >= x_list[i] and x <= x_list[i+1]):
            x_prev = x_list[i] #X_i
            y_prev  = y_list[i] #  Y_i
            x_next = x_list[i+1] #X_i+1
            y_next = y_list[i+1]#Y_i+1

            y = (y_next - y_prev)/(x_next - x_prev)*(x - x_prev) + y_prev
            return y

x_interpol = np.linspace(min(x_list), max(x_list),100)
y_interpol = []

for i in range(0,len(x_interpol)):
    y_interpol.append(intPLineal(x_list,y_list,x_interpol[i]))

fig = plt.figure()
ax = plt.axes()
ax.plot(x_list, y_list,'*',markersize='10', label='Datos Discretos')
ax.plot(x_interpol, y_interpol,'.', label='Puntos interpolados')
ax.plot(x_interpol, np.sin(x_interpol), label='sin(x)')
ax.legend()
plt.show()
#print(intPLineal(x_list,y_list,0.5))


"""
Module for calculating and caching Chebyshev polynomials, as well as
  some helper functions regarding polynomial manipulation.
"""

from sympy import Poly
from sympy.abc import x

"""
Caching for the computed Chebyshev polynomials.
"""
computed_polynomials = [Poly(1, x), Poly(x, x)]

def get_nth_chebyshev_polynomial(polynomial_degree: int) -> Poly:
    """
    Calculates the nth Chebyshev polynomial using the recursive relation:

        T_n = 2x * T_{n - 1} - T_{n - 2}

    Caches the result in the computed_polynomials list.

    :param polynomial_degree: degree of the Chebyshev polynomial
    :return: the nth Chebyshev polynomial
    :rtype: Poly
    """
    if (len(computed_polynomials) - 1) >= polynomial_degree:
        return computed_polynomials[polynomial_degree]

    # T_n = 2x * T_{n - 1} - T_{n - 2}
    T_n_1 = get_nth_chebyshev_polynomial(polynomial_degree - 1)
    T_n_2 = get_nth_chebyshev_polynomial(polynomial_degree - 2)

    T_n = 2 * x * T_n_1 - T_n_2

    computed_polynomials.append(T_n)

    return T_n

def normalise_polynomial(polynomial: Poly) -> Poly:
    """
    Normalises polynomial by dividing it by its leading coefficient.

    :param polynomial: polynomial to normalise
    :return: normalised polynomial
    :rtype: Poly
    """

    return polynomial / polynomial.LC()

def get_normalised_nth_chebyshev_polynomial(polynomial_degree: int) -> Poly:
    """
    A helper function to return the normalised nth Chebyshev polynomial.

    :param polynomial_degree: degree of the Chebyshev polynomial
    :return: normalised Chebyshev polynomial
    :rtype: Poly
    """

    return normalise_polynomial(get_nth_chebyshev_polynomial(polynomial_degree))

def lower_degree_to(polynomial: Poly,
                    max_polynomial_degree: int) -> Poly:
    """
    Lowers the degree of the polynomial by using Chebyshev polynomials.

    :param polynomial: polynomial to lower the degree of
    :param max_polynomial_degree: maximum polynomial degree to lower to
    :return: polynomial with the degree less than or equal to `max_polynomial_degree`
    :rtype: Poly
    """

    while polynomial.degree() > max_polynomial_degree:
        normalised_chebyshev_polynomial = get_normalised_nth_chebyshev_polynomial(polynomial.degree())

        polynomial -= normalised_chebyshev_polynomial * polynomial.LC()

    return polynomial


import numpy as np

def linear_interpolation(x_data, y_data, point):
    if len(x_data) != len(y_data):
        raise Exception("X and Y vectors must have equal number of elements.")

    if x_data.size < 2:
        raise Exception("X and Y vectors have to contain at least 2 elements.")

    def _interpolate(x1, x2, y1, y2, point):
        return ((y2 - y1) * point + x2 * y1 - x1 * y2) / (x2 - x1)

    for index, x in np.ndenumerate(x_data[:-1]):
        i = index[0]
        if point >= x and point <= x_data[i + 1]:
            x1 = x
            x2 = x_data[i + 1]
            y1 = y_data[i]
            y2 = y_data[i + 1]
            return _interpolate(x1, x2, y1, y2, point)


"""Lower-Upper (LU) Decomposition."""

# lower–upper (LU) decomposition - https://en.wikipedia.org/wiki/LU_decomposition
import numpy

def LUDecompose(table):
    # Table that contains our data
    # Table has to be a square array so we need to check first
    rows, columns = numpy.shape(table)
    L = numpy.zeros((rows, columns))
    U = numpy.zeros((rows, columns))
    if rows != columns:
        return []
    for i in range(columns):
        for j in range(i - 1):
            sum = 0
            for k in range(j - 1):
                sum += L[i][k] * U[k][j]
            L[i][j] = (table[i][j] - sum) / U[j][j]
        L[i][i] = 1
        for j in range(i - 1, columns):
            sum1 = 0
            for k in range(i - 1):
                sum1 += L[i][k] * U[k][j]
            U[i][j] = table[i][j] - sum1
    return L, U

if __name__ == "__main__":
    matrix = numpy.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]])
    L, U = LUDecompose(matrix)
    print(L)
    print(U)


# Finding the minimum for the function:
# f(x,y) = -log(1-x-y)-log(x)-log(y)
# Domain x + y < 1, x > 0, y > 0
# Using Gradient Descent

# Import Required Libraries
import numpy
import matplotlib.pyplot as plt

# Functions
# Initial Guess
def Initalisation():
    x = numpy.random.uniform(0, 0.5)
    y = numpy.random.uniform(0, 0.5)
    return numpy.array([[x, y]])

# Calculates Gradient of the given Function
def Gradient(x, y):
    g1 = (-2 * x - y + 1) / (x ** 2 + y * x - x)
    g2 = (-x - 2 * y + 1) / (x * y + y ** 2 - 1)
    return numpy.array([g1, g2])

# Evaluates the value of the function at the given point
def Eval_Func(x, y):
    return -numpy.log(1-x-y)-numpy.log(x)-numpy.log(y)

# Update the points based on the Gradient Descent Algorithm
def Update_Weights(x, y, eta):
    return numpy.array([x, y]) - eta*Gradient(x, y)

# Actual Loop where Gradient Descent Algo runs until optimal point is reached
def GD(max_iter, tol, eta):
    iterations = 0
    F = numpy.array([])  # Stores the values of the function
    Epoch = numpy.array([])
    while iterations < max_iter:
        if iterations == 0:
            # Generate Initial Guess and Book Keeping
            if numpy.DataSource().exists('InitialGuess.txt'):
                W = numpy.loadtxt('InitialGuess.txt')  # Load the Initial Weights
            else:
                W = Initalisation()
                numpy.savetxt('InitialGuess.txt', W)  # Generate the Weights and save them
            W = numpy.reshape(W, (1, 2))
            f_temp = Eval_Func(W[-1, 0], W[-1, 1])
            F = numpy.concatenate((F, [f_temp]), axis=0)
            Epoch = numpy.concatenate((Epoch, [iterations]), axis=0)
            print('No. of Iterations: ', iterations, ' Points: ', W[-1], ' Function Value: ', F[-1], '\n')
            iterations += 1
        else:
            # Run The Gradient Descent Algorithm
            w_temp = Update_Weights(W[-1, 0], W[-1, 1], eta)
            f_temp = Eval_Func(W[-1, 0], W[-1, 1])
            # Book Keeping
            W = numpy.concatenate((W, [w_temp]), axis=0)
            F = numpy.concatenate((F, [f_temp]), axis=0)
            Epoch = numpy.concatenate((Epoch, [iterations]), axis=0)
            print('No. of Iterations: ', iterations, ' Points: ', W[-1], ' Function Value: ', F[-1], '\n')
            # Check for Close Weights
            if (W[-1] - W[-2]).all() < tol:
                print('Optimal Value Reached')
                break
            else:
                iterations += 1
    return Epoch, W, F

# Parameters
max_iter = 100000 # Maximum Iterations to reach the optimal value
tol = 1.0e-6 # Tolerance
eta = 0.05 # Learning Rate

# Main Loop
Epoch, W, F = GD(max_iter, tol, eta)

# Plot the results
# Plot 1
fig, ax1 = plt.subplots()
ax1.plot(W[:, 0], W[:, 1], 'rx:', label='Points After Every Iterations')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Trajectory Followed by the Points at each Iteration')
plt.legend()
# Plot 2
fig, ax2 = plt.subplots()
ax2.plot(Epoch, F, 'b', label='Energy')
plt.xlabel('Epoch')
plt.ylabel('Function Value')
plt.title('Value of the Function -log(1-x-y)-log(x)-log(y) at each iteration')
fig.text(0.39, 0.03, r'Learning Rate $\eta = 0.05$', ha='center')
plt.legend()
plt.tight_layout()
plt.show()


import numpy as np

def logLoss(yTrue, yPred):
    return - np.sum(((yTrue * np.log(yPred + 1e-10)) + ((1 - yTrue) * np.log(1 - yPred + 1e-10)))) / len(yTrue)

def accuracy(yTrue, yPred):
    return np.sum(yTrue == yPred) / yTrue.size

def meanSquaredError(yTrue, yPred):
    return np.sum((yTrue - yPred) ** 2) / yTrue.size

def meanAbsoluteError(yTrue, yPred):
    return np.sum(np.abs(yTrue - yPred)) / yTrue.size

def precision(yTrue, yPred):
    # todo: create precision function
    return None

def recall(yTrue, yPred):
    # todo: create recall function
    return None

if __name__ == '__main__':
    testingPredictions = np.array([2.5, 0.0, 2, 8])
    testingTruth = np.array([3, -0.5, 2, 7])

    print(meanAbsoluteError(testingTruth, testingPredictions))


import numpy as np

def sigmoid(vector: np.array) -> np.array:
    """fungsi sigmoid digunakan untuk mendapatkan output berupa non-linear

    Args:
        vector (np.array): Nilai input berupa vektor

    Returns:
        np.array: Nilai sigmoid dari vektor yang diinputkan

    >>> sigmoid(np.array([-1.0, 1.0, 2.0]))
    array([0.26894142, 0.73105858, 0.88079708])
    >>> sigmoid(np.array([0.0]))
    array([0.5])
    """
    return 1 / (1 + np.exp(-vector))


from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from rich import print
import numpy as np
import matplotlib.pyplot as plt

plt.style.use("ggplot")

digits = datasets.load_digits()

print(digits.DESCR)
print(digits.keys())

# Display digit 1010
plt.imshow(digits.images[1010], cmap=plt.cm.gray_r, interpolation="nearest")
plt.show()  # corresponds to a 5

X = digits.data
y = digits.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

knn = KNeighborsClassifier(n_neighbors=7)
knn.fit(X_train, y_train)

print(knn.score(X_test, y_test))

#### comparing classifiers ####
neighbors = np.arange(1, 9)
train_accuracy = np.empty(len(neighbors))
test_accuracy = np.empty(len(neighbors))

for i, k in enumerate(neighbors):
    knn = KNeighborsClassifier(n_neighbors=k)
    knn.fit(X_train, y_train)
    train_accuracy[i] = knn.score(X_train, y_train)
    test_accuracy[i] = knn.score(X_test, y_test)

# Generate plot
plt.title("k-NN: Varying Number of Neighbors")
plt.plot(neighbors, test_accuracy, label="Testing Accuracy")
plt.plot(neighbors, train_accuracy, label="Training Accuracy")
plt.legend()
plt.xlabel("Number of Neighbors")
plt.ylabel("Accuracy")
plt.show()

print("1 shows overfit, 8 shows underfit. 3 & 5 perform best")


import time
import numpy as np

class Knapsack:
    # A class to solve Knapsack problems presented in Coursera, Discrete Optimisation, week 2

    def __init__(self, values, weights):
        self.diagnose = False
        self.v = values
        self.w = weights
        self.method_names = {1: "DP",
                             2: "DP (Branch & Bound)",
                             3: "DP (Branch & Bound, fractional item)"
                             }

    def solve(self, capacity, method):
        self.count = 0
        self.currentBest = 0
        self.K = capacity
        self.method = method
        start_time = time.time()

        if method == 1:
            self.value = self.DP(self.K, self.v.size, self.v, self.w)
        elif method == 2:
            self.value = self.DP_BB1(self.K, self.v.size, self.v, self.w)

        self.time_taken = (time.time() * 1000 - start_time * 1000)

    def print(self):
        print("Capacity of this knapsack problem:", self.K)
        print("Optimal value:", self.value)
        print("Method used is:", self.method_names[method])
        print("Time taken: %s ms" % round(self.time_taken, 0))
        print("The function has been called: %i times." % self.count)

    def DP(self, k, j, v, w):
        self.count += 1
        if self.diagnose:
            print(f"count={self.count} | k={k} | j={j} | v={v} | w={w}")
        if j == 0:
            return 0
        elif w[j - 1] <= k:
            return max(self.DP(k, j - 1, v, w), v[j - 1] + self.DP(k - w[j - 1], j - 1, v, w))
        else:
            return self.DP(k, j - 1, v, w)

    def DP_BB1(self, k, j, v, w):
        self.count += 1
        if self.diagnose:
            print(f"count={self.count} | k={k} | j={j} | v={v} | w={w}")

        if j == 0 or v.dot(w) < self.currentBest:
            return 0
        elif w[j - 1] <= k:
            value = max(self.DP(k, j - 1, v, w), v[j - 1] + self.DP(k - w[j - 1], j - 1, v, w))
            if value > self.currentBest:
                self.currentBest = value
            return value
        else:
            return self.DP(k, j - 1, v, w)

v1 = np.array([16, 19, 23, 28, 18, 24, 5, 15, 17, 37, 21, 14, 9, 24, 11, 58, 28, 22, 18,
               13, 33, 16, 19, 23, 28, 18, 24, 5, 15, 17, 37, 21, 14, 9, 24, 11, 58, 28, 22, 18, 13, 33,
               16, 19, 23, 28, 18, 24, 5, 15, 17, 37, 21, 13, 33, 16, 19, 23, 28, 18, 24, 5, 15, 17])

w2 = np.array([2, 3, 4, 5, 3, 4, 1, 2, 2, 6, 4, 1, 1, 4, 1, 7, 5, 4, 3, 2, 6, 2, 3, 4, 5, 3, 4, 1, 2, 2, 6,
               4, 1, 1, 4, 1, 7, 5, 4, 3, 2, 6, 2, 3, 4, 5, 3, 4, 1, 2, 2, 6, 4,
               1, 1, 4, 1, 7, 5, 4, 3, 2, 6, 2, 3, 4, 5, 3, 4, 1, 2, 2])

v2 = np.array([45, 48, 35])
w2 = np.array([5, 8, 3])

knapsack1 = Knapsack(v2, w2)
knapsack1.diagnose = False

K = 10
method = 2
knapsack1.solve(K, method)
knapsack1.print()


from __future__ import division
from sympy import sin, cos, Symbol, lambdify, Number, Expr, S
from common import function
from math import sqrt, pi
import numpy as np

SQRT_PI = sqrt(pi)
SQRT_2PI = sqrt(2*pi)

def fourier_basis(N, symbol='x'):
    '''
    Consider a Fourier series

    \sum k\in[0, N]a_k cos(k x)/sqrt(pi) + \sum k\in[1, N]b_k sin(kx)/sqrt(pi).

    Here these 2*N + 1 basis functions are returned. Basis functions are
    normalized to have the L^2 norm over [0, 2*pi] equal to 1. The N can be
    understood as the highest wavenumber.
    '''
    x = Symbol(symbol)
    return [S(1)/SQRT_2PI] +\
           [cos(k*x)/SQRT_PI for k in range(1, N+1)] +\
           [-sin(k*x)/SQRT_PI for k in range(1, N+1)]

def fourier_function(F):
    '''
    Return a linear combination of len(F_vec) Fourier basis functions with
    coefficients given by F_vec.
    '''
    if len(F.shape) == 1:
        basis = fourier_basis(F.shape[0]//2, 'x')
        return function(basis, F)
    else:
        raise NotImplementedError('2d, 3d not implemented yet.')

def fourier_eval(N, f):
    '''
    Let N the highest wavenumber to be considered in the Fourier series for f.
    In order to get Fourier coefficients of f with some accuracy the function
    must be avaluated in a least 2*N points.
    '''
    n_points = 2*N
    points = np.linspace(0, 2*pi, n_points, endpoint=False)

    if isinstance(f, (Number, int, float)):
        f_values = float(f)*np.ones(len(points))
    # Symbolic functions
    elif isinstance(f, Expr):
        x = Symbol('x')
        assert x in f.atoms() or isinstance(f, Number)
        f = lambdify(x, f, 'numpy')
        # Lambdify makes it fast if we feed as arrays the x y z comps
        # of points
        f_values = f(points)
    # Python functions/lambdas
    else:
        # For (lambda)function I can only check the argcount
        assert f.func_code.co_argcount == 1
        f_values = np.array([f(*(p.tolist())) for p in points])

    return f_values

def fft(f_vec):
    '''
    If f was sapled in 2*N points we can construct a series with highest
    frequency N that has 2*N+1 terms. To get its coefficients we take a
    DFFT of f_vec this is N+1 complex values. The real part is proportional to
    the N+1 coeffs for cosines, while the imaginary part is related to the N
    coefficients of sines.
    '''
    F_vec = np.fft.rfft(f_vec)
    # These are the coefficient values
    n_points = len(f_vec)
    F_vec[0] *= SQRT_2PI/n_points
    F_vec[1:] *= 2*SQRT_PI/n_points
    F_vec = np.r_[F_vec.real, F_vec.imag[1:]]

    return F_vec

def ifft(F_vec):
    '''
    To get values of f in control points back, the coefficients must be rescaled
    and collapsed to complex vector.
    '''
    n_points = len(F_vec) - 1
    F_vec = F_vec.copy()
    F_vec[0] /= SQRT_2PI/n_points
    F_vec[1:] /= 2*SQRT_PI/n_points
    end_real = n_points/2 + 1
    F_vec = F_vec[:end_real] + 1j*np.r_[0, F_vec[end_real:]]
    f_vec = np.fft.irfft(F_vec)

    return f_vec

def mass_matrix(N):
    '''
    2N+1 x 2N+1 matrix with entries corresponding to \int_{0}^{2pi} u v where
    u, v are from fourier basis N is a diagonal matrix. Return the diagonal
    '''
    # This is here just for completeness
    return np.ones(2*N + 1)

def stiffness_matrix(N):
    '''
    2N+1 x 2N+1 matrix with entries corresponding to \int_{0}^{2pi} -u'' v where
    u, v are from fourier basis N is a diagonal matrix. Return the diagonal.
    '''
    # This is here just
    k_ = np.array([k**2 for k in range(1, N+1)])
    return np.r_[0, k_, k_]

# -----------------------------------------------------------------------------

if __name__ == '__main__':
    from sympy.mpmath import quad
    from sympy import lambdify
    from random import random

    N = 15
    tol = 1E-13

    x = Symbol('x')
    f = sum(random()*cos(k*x) + random()*sin(k*x) for k in range(N))

    # Computing fourier series coefficients by FFT
    f_vec = fourier_eval(N, f)
    F_vec = fft(f_vec)

    # Compare with analytical coefficients values
    basis = fourier_basis(N)
    for i, v in enumerate(basis):
         assert abs(quad(lambdify(x, v*f), [0, 2*np.pi]) - F_vec[i]) < 1E-14

    # Comming back
    f_vec_ = ifft(F_vec)
    assert np.allclose(f_vec, f_vec_)

    N = 6
    basis = fourier_basis(N)

    # Check mass matrix
    M = np.diag(mass_matrix(N))
    M_ = np.zeros_like(M, dtype='double')
    for i, v in enumerate(basis):
        M_[i, i] = quad(lambdify(x, v**2), [0, 2*pi])
        for j, u in enumerate(basis[i+1:], i+1):
            M_[i, j] = quad(lambdify(x, v*u), [0, 2*pi])
            M_[j, i] = M_[i, j]
    assert np.allclose(M, M_)

    # Check stiffness matrix
    A = np.diag(stiffness_matrix(N))
    A_ = np.zeros_like(A, dtype='double')
    for i, v in enumerate(basis):
        A_[i, i] = quad(lambdify(x, -v*v.diff(x, 2)), [0, 2*pi])
        for j, u in enumerate(basis[i+1:], i+1):
            A_[i, j] = quad(lambdify(x, -v*u.diff(x, 2)), [0, 2*pi])
            A_[j, i] = A_[i, j]
    assert np.allclose(A, A_)