"""
Filename: test_tauchen.py
Authors: Chase Coleman, John Stachurski
Date: 07/22/2014

Tests for ricatti.py file

"""
import numpy as np
from numpy.testing import assert_allclose
from quantecon.matrix_eqn import solve_discrete_riccati

def dare_test_golden_num_float():
    val = solve_discrete_riccati(1.0, 1.0, 1.0, 1.0)
    gold_ratio = (1 + np.sqrt(5)) / 2.
    assert_allclose(val, gold_ratio)

def dare_test_golden_num_2d():
    A, B, R, Q = np.eye(2), np.eye(2), np.eye(2), np.eye(2)
    gold_diag = np.eye(2) * (1 + np.sqrt(5)) / 2.
    val = solve_discrete_riccati(A, B, R, Q)
    assert_allclose(val, gold_diag)

def dare_test_tjm_1():
    A = [[0.0, 0.1, 0.0],
         [0.0, 0.0, 0.1],
         [0.0, 0.0, 0.0]]
    B = [[1.0, 0.0],
         [0.0, 0.0],
         [0.0, 1.0]]
    Q = [[10**5, 0.0, 0.0],
         [0.0, 10**3, 0.0],
         [0.0, 0.0, -10.0]]
    R = [[0.0, 0.0],
         [0.0, 1.0]]
    X = solve_discrete_riccati(A, B, Q, R)
    Y = np.diag((1e5, 1e3, 0.0))
    assert_allclose(X, Y, atol=1e-07)

def dare_test_tjm_2():
    A = [[0, -1],
         [0, 2]]
    B = [[1, 0],
         [1, 1]]
    Q = [[1, 0],
         [0, 0]]
    R = [[4, 2],
         [2, 1]]
    X = solve_discrete_riccati(A, B, Q, R)
    Y = np.zeros((2, 2))
    Y[0, 0] = 1
    assert_allclose(X, Y, atol=1e-07)

def dare_test_tjm_3():
    r = 0.5
    I = np.identity(2)
    A = [[2 + r**2, 0],
         [0,        0]]
    A = np.array(A)
    B = I
    R = [[1, r],
         [r, r*r]]
    Q = I - np.dot(A.T, A) + np.dot(A.T, np.linalg.solve(R + I, A))
    X = solve_discrete_riccati(A, B, Q, R)
    Y = np.identity(2)
    assert_allclose(X, Y, atol=1e-07)


# Check README.md for instruction to run.
# Check input_array.txt for the format in which the matrices need to be input.

import sys
import numpy as np

# Default initialization of the matrices in case the command line argument of matrix input files in not passed
def initialize_default():
	mat1 = np.array([[3,5] , [4,5]])
	mat2 = np.array([[1,2,3], [4,5,6] ])
	return mat1, mat2

mat1_list = []
mat2_list = []

# Check if a matrix input file is passed
if len(sys.argv) < 2:
	print("No input file passed, using Default Values")
	mat1, mat2 = initialize_default()

# Read the matrix input file, store the matrices as two distinct numpy 2D arrays
elif len(sys.argv) == 2:
	mat_file = sys.argv[1] # File Path
	with open(sys.argv[1], 'r') as f: # Read the file content from file path
		lines = f.readlines()

	mat1_start = lines.index('matrix1\n') + 1 # Where matrix 1 starts
	mat1_end = min(lines.index('matrix2\n'), lines.index('\n')) # Where matrix 1 ends. It is assumed that matrix 1 ends at blank line or where matrix 2 starts.
	mat2_start = lines.index('matrix2\n') + 1 # Where Matrix 2 starts. Matrix 2 is assumed to end at the end of the file.

	# Read the lines from file, split the space seprated numbers and store in list.
	for line in lines[mat1_start: mat1_end]:
		row = [float(i) for i in line.split()]
		mat1_list.append(row)

	for line in lines[mat2_start:]:
		row = [float(i) for i in line.split()]
		mat2_list.append(row)

	# Convert the list of lists to 2D numpy array
	mat1 = np.array(mat1_list)
	mat2 = np.array(mat2_list)

else:
	print("Please pass only 1 input file - Using Default Values")
	mat1, mat2 = initialize_default()

# Check multiplication compatibility of the two matrices
if mat1.shape[1] != mat2.shape[0]:
	print("Matrices are not compatible for multiplication")

# By matrix multiplication definition (C= AB, where A is (mxn) and B is a (nxm) matrix):
# c_{ij} = \Sigma_{k=1}^{n}(a_{ik}b_{kj}) where a_{ik} element of A in ith row and kth column and likewise for b_{kj}
else:
	mat3 = np.zeros((mat1.shape[0], mat2.shape[1])) # Initialize the matrix multiplication product
	for i in range(mat3.shape[0]):
		for j in range(mat3.shape[1]):
			element = 0
			for k in range(mat2.shape[0]):
				element += mat1[i][k]*mat2[k][j]

			mat3[i][j] = element

	check = np.dot(mat1, mat2) # Exact answer is found using in-built funnction in numpy
	print(mat3)
	print("Verifying Answer with numpy matrix multiplication function:", np.array_equal(check, mat3)) # Verify the answer


import os
import numpy as np

# import jax.numpy as jnp
from sklearn.decomposition import TruncatedSVD
# For efficient linear algebra
from numpy import linalg as LA
# For Timing
import time

def dmd_s(D_1,D_2,n_Modes, F_S, SAVE_T_DMD=False, FOLDER_OUT='./'):
    """
    This method computes the Dynamic Mode Decomposition (DMD).

    https://youtu.be/8fhupzhAR_M
    --------------------------------------------------------------------------------------------------------------------
    Parameters:
    ----------

    :param D_2: np.array
           Second portion of the data, i.e. D[:,1:n_t]
    :param Phi_P, Psi_P, Sigma_P: np.arrays
            POD decomposition of D1
    :param F_S: float
           Sampling frequency in Hz
    :param FOLDER_OUT: str
            Folder in which the results will be saved (if SAVE_T_DMD=True)
    :param K: np.array
            Temporal correlation matrix
    :param SAVE_T_POD: bool
            A flag deciding whether the results are saved on disk or not. If the MEMORY_SAVING feature is active, it is
            switched True by default.
    :param n_Modes: int
           number of modes that will be computed
    --------------------------------------------------------------------------------------------------------------------
    Returns:
    --------

        :return Phi_D: np.array
                DMD Psis

        :return Lambda_D: np.array
                DMD Eigenvalues (of the reduced propagator)

        :return freqs: np.array
                Frequencies (in Hz, associated to the DMD modes)

         :return a0s: np.array
                Initial Coefficients of the Modes
    """

    #TODO This currently does a POD of D_1. The memory saving feature
    # should be implemented in this step.
    svd = TruncatedSVD(n_Modes)
    print('Computing the SVD of the D1')
    start = time.time()
    X_transformed = svd.fit_transform(D_1)
    #%% Step 2: compute the SVD of D1 (randomized algorithm)
    Phi_P = X_transformed / svd.singular_values_
    Psi_P = svd.components_.T
    Sigma_P=svd.singular_values_
    end = time.time()
    print('SVD computed in '+ str(end - start)+'s')

    Sigma_inv=np.diag(1/Sigma_P); dt=1/F_S
    #%% Step 3: Compute approximated propagator
    P_A=LA.multi_dot([np.transpose(Phi_P),D_2,\
                     Psi_P,Sigma_inv])

    #%% Step 4: Compute eigenvalues of the system
    Lambda, Q = LA.eig(P_A)
    freqs=np.imag(np.log(Lambda))/(2*np.pi*dt)

    #%% Step 5: Spatial structures of the DMD
    Phi_D=LA.multi_dot([D_2,Psi_P,Sigma_inv,Q])
    #%% Step 6: Compute the initial coefficients
    #a0s=LA.lstsq(Phi_D, D_1[:,0],rcond=None)
    a0s=LA.pinv(Phi_D).dot(D_1[:,0])

    if SAVE_T_DMD:
        os.makedirs(FOLDER_OUT + "/DMD/", exist_ok=True)
        print("Saving DMD results")
        np.savez(FOLDER_OUT + '/DMD/dmd_decomposition',
                  Phi_D=Phi_D, Lambda=Lambda, freqs=freqs,a0s=a0s)

    return Phi_D, Lambda, freqs, a0s


from numpy import *
from matplotlib.pyplot import *

# Parameters
a = 3
b = 10

# Plotting the PDF
def pdf(x):
    if x >= a and x <= b:
        return 1 / (b - a)
    else:
        return 0

X = arange(0, b+3, 0.1)
Y = []

for x in X:
    Y.append(pdf(x))

matplotlib.rc('xtick', labelsize=18)
matplotlib.rc('ytick', labelsize=18)
plot(X, Y, Linewidth=2, color='black')
xlabel('$X$', size=22)
ylabel('$f_X(x)$', size=22)
#show()
savefig('uniform_pdf.pdf', format='pdf', bbox_inches='tight')

# Clear the current figure
clf()

# Plotting the CDF
def cdf(x):
    if x < a:
        return 0
    elif x >= a and x < b:
        return (x - a) / (b - a)
    elif x >= b:
        return 1

X = arange(0, b+3, 0.1)
Y = []

for x in X:
    Y.append(cdf(x))

matplotlib.rc('xtick', labelsize=18)
matplotlib.rc('ytick', labelsize=18)
plot(X, Y, Linewidth=2, color='black')
xlabel('$X$', size=22)
ylabel('$F_X(x)$', size=22)
#show()
savefig('uniform_cdf.pdf', format='pdf', bbox_inches='tight')


import numpy as np
from numpy import linalg as LA

def outerProduct(X):
    """Computer outer product and indicate which locations in matrix are undefined"""
    O=np.outer(X,X)
    N=1-np.isnan(O)
    return (O,N)

def sumWithNan(M1,M2):
    """Add two pairs of (matrix,count)"""
    (X1,N1)=M1
    (X2,N2)=M2
    N=N1+N2
    X=np.nansum(np.dstack((X1,X2)),axis=2)
    return (X,N)

def computeCov(RDDin):
    """computeCov recieves as input an RDD of np arrays, all of the same length,
    and computes the covariance matrix for that set of vectors"""
    RDD=RDDin.map(lambda v:np.array(np.insert(v,0,1),dtype=np.float64)) # insert a 1 at the beginning of each vector so that the same
                                           #calculation also yields the mean vector
    OuterRDD=RDD.map(outerProduct)   # separating the map and the reduce does not matter because of Spark uses lazy execution.
    (S,N)=OuterRDD.reduce(sumWithNan)
    # Unpack result and compute the covariance matrix
    # print('RDD=',RDD.collect())
    # print('shape of S=',S.shape,'shape of N=',N.shape)
    # print('S=',S)
    # print('N=',N)
    E=S[0,1:]
    NE=np.float64(N[0,1:])
    print('shape of E=',E.shape,'shape of NE=',NE.shape)
    Mean=E/NE
    O=S[1:,1:]
    NO=np.float64(N[1:,1:])
    Cov=O/NO - np.outer(Mean,Mean)
    # Output also the diagnal which is the variance for each day
    Var=np.array([Cov[i,i] for i in range(Cov.shape[0])])
    return {'E':E,'NE':NE,'O':O,'NO':NO,'Cov':Cov,'Mean':Mean,'Var':Var}

if __name__=="__main__":
    # create synthetic data matrix with j rows and rank k

    V=2*(np.random.random([2,10])-0.5)
    data_list=[]
    for i in range(1000):
        f=2*(np.random.random(2)-0.5)
        data_list.append(np.dot(f,V))
    # compute covariance matrix
    RDD=sc.parallelize(data_list)
    OUT=computeCov(RDD)

    #find PCA decomposition
    eigval,eigvec=LA.eig(OUT['Cov'])
    print('eigval=',eigval)
    print('eigvec=',eigvec)


import numpy as np
from abc import ABC

class CartPolSwitcher(ABC):
    """
    A polar <-> Cartesian coordinates switcher.

    .. doctest::

        >>> CartPolSwitcher.cart2pol(2,2)
        (2.8284271247461903, 0.7853981633974483)
        >>> CartPolSwitcher.pol2cart(1, np.pi/4)
        (0.7071067811865476, 0.7071067811865476)
    """

    @classmethod
    def cart2pol(cls, x, y):
        rho = np.sqrt(x ** 2 + y ** 2)
        phi = np.arctan2(y, x)
        return rho, phi

    @classmethod
    def pol2cart(cls, rho, phi):
        x = rho * np.cos(phi)
        y = rho * np.sin(phi)
        return x, y

if __name__ == "__main__":
    import doctest
    doctest.testmod()


import numpy as np
def compute_euclidean_distance(point, centroid):
    return np.sqrt(np.sum((point - centroid) ** 2))

def assign_label_cluster(distance, data_point, centroids):
    index_of_minimum = min(distance, key=distance.get)
    return [index_of_minimum, data_point, centroids[index_of_minimum]]

def compute_new_centroids(cluster_label, centroids):
    return np.array(cluster_label + centroids) / 2

def iterate_k_means(data_points, centroids, total_iteration):
    label = []
    cluster_label = []
    total_points = len(data_points)
    k = len(centroids)

    for iteration in range(0, total_iteration):
        for index_point in range(0, total_points):
            distance = {}
            for index_centroid in range(0, k):
                distance[index_centroid] = compute_euclidean_distance(data_points[index_point],
                                                                      centroids[index_centroid])
            label = assign_label_cluster(distance, data_points[index_point], centroids)
            centroids[label[0]] = compute_new_centroids(label[1], centroids[label[0]])

            if iteration == (total_iteration - 1):
                cluster_label.append(label)

    return [cluster_label, centroids]

def print_label_data(result):
    print("Result of k-Means Clustering: \n")
    for data in result[0]:
        print("data point: {}".format(data[1]))
        print("cluster number: {} \n".format(data[0]))
    print("Last centroids position: \n {}".format(result[1]))

def create_centroids(x, y):
    return np.array([[x, y]])

#false is fake, true is real
def compute(x, y):
    filename = "k/data.csv"
    data_points = np.genfromtxt(filename, delimiter=",")
    centroids = create_centroids(x, y)
    total_iteration = 100

    [cluster_label, new_centroids] = iterate_k_means(data_points, centroids, total_iteration)
    return (centroids[0][0] % 2 == 0)


import os
import time as tm
import numpy as np
import matplotlib.pyplot as plt

#function to produce n normal random points in d dimensional hypersphere with radius r
def generateNormalRandomPointsInHypersphere(dimension, number_of_points, radius = 1):
    #generate n normal random d dimensional vectors
    points = np.random.normal(size = (dimension, number_of_points))
    #dividing each vector by its norm to scale them between -1 and 1
    points /= np.linalg.norm(points, axis = 0)
    #uniform random radius for making all points on the surface of balls within the unit ball equally likely
    r = np.random.uniform(low = 0.0, high = 1.0, size = number_of_points)
    r **= 1 / dimension
    result = radius * (points * r).T
    return result

#function to calculate std and norm of random generated vector in d dimensional space
def generateNormalRandomPointsInSpace(dimension, number_of_points):
    #generate n normal random d dimensional vectors
    points = np.random.normal(size = (number_of_points, dimension))
    return points

#function to calculate norms of each set
def getNorm(points: np.ndarray) -> np.array:
    norm = np.linalg.norm(points, axis = 1)
    return norm

#function to calculate innter products of each 2 vectors
def getInnerProduct(points: np.ndarray) -> dict:
    inner_prod = []
    for i in range(len(points)):
        for j in range(len(points)):
            if i != j:
                inner_prod.append(np.inner(points[i], points[j]))
    return inner_prod

#function to calculate std and norm of random generated vector in hypersphere
def interactiveMain():
    while True:
        d = int(input("Enter dimension of hypersphere: "))
        n = int(input("Enter number of random vectors to produce: "))
        points = generateNormalRandomPointsInHypersphere(d, n)
        norm = getNorm(points)
        inner_product = getInnerProduct(points)
        print("Average of norms is: " + str(np.average(norm)))
        print("Standard deviation of norms is: " + str(np.std(norm)))
        print("Average of inner products is: " + str(np.average(inner_product)))
        print("Standard deviation of inner products is: " + str(np.std(inner_product)))
        input("\n\nPress any key to restart...\n\n\n\n")
        #os.system("clear")

#plot std and norm of vectors in  unit hypersphere based on n and d
def graphMain():
    while True:
        d = int(input("Enter upper bound for dimension of hypersphere: "))
        n = int(input("Enter upper bound for number of random vectors to produce: "))
        xs = []
        ys = []
        z0s = []
        z1s = []
        z2s = []
        z3s = []
        for i in range(2, d + 1):
            for j in range(2, n + 1):
                xs.append(i)
                ys.append(j)
                points = generateNormalRandomPointsInHypersphere(i, j)
                norm = getNorm(points)
                inner_product = getInnerProduct(points)
                z0s.append(np.average(norm))
                z1s.append(np.std(norm))
                z2s.append(np.average(inner_product))
                z3s.append(np.std(inner_product))
        #####################
        fig0 = plt.figure()
        ax0 = fig0.add_subplot(111, projection='3d')
        ax0.scatter(xs,ys,z0s, c = "blue")
        ax0.set_xlabel("d")
        ax0.set_ylabel("n")
        ax0.set_zlabel("avg of norms")
        plt.show()
        #####################
        fig1 = plt.figure()
        ax1 = fig1.add_subplot(111, projection='3d')
        ax1.scatter(xs,ys,z1s, c = "green")
        ax1.set_xlabel("d")
        ax1.set_ylabel("n")
        ax1.set_zlabel("std of norms")
        plt.show()
        #####################
        fig2 = plt.figure()
        ax2 = fig2.add_subplot(111, projection='3d')
        ax2.scatter(xs,ys,z2s, c = "black")
        ax2.set_xlabel("d")
        ax2.set_ylabel("n")
        ax2.set_zlabel("avg of inner products")
        plt.show()
        #####################
        fig3 = plt.figure()
        ax3 = fig3.add_subplot(111, projection='3d')
        ax3.scatter(xs,ys,z3s, c = "red")
        ax3.set_xlabel("d")
        ax3.set_ylabel("n")
        ax3.set_zlabel("std of inner products")
        plt.show()
        input("\n\nPress any key to restart...\n\n\n\n")

def main2():
    while True:
        d = int(input("Enter dimension: "))
        n = int(input("Enter number of random vectors to produce: "))
        points = generateNormalRandomPointsInSpace(d, n)
        norm = getNorm(points)
        inner_product = getInnerProduct(points)
        print("Average of norms is: " + str(np.average(norm)))
        print("Standard deviation of norms is: " + str(np.std(norm)))
        print("Average of inner products is: " + str(np.average(inner_product)))
        print("Standard deviation of inner products is: " + str(np.std(inner_product)))
        input("\n\nPress any key to restart...\n\n\n\n")

main2()


"""
  Name     : c6_20_myPackage.py
  Book     : Hands-on Data Science with Anaconda )
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan and James Yan
  Date     : 3/1/2018
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

def bs_call(S,X,T,rf,sigma):
    """
       Objective: Black-Schole-Merton option model
       Format   : bs_call(S,X,T,r,sigma)
               S: current stock price
               X: exercise price
               T: maturity date in years
              rf: risk-free rate (continusouly compounded)
           sigma: volatiity of underlying security
       Example 1:
         >>>bs_call(40,40,1,0.1,0.2)
         5.3078706338643578
    """
    from scipy import log,exp,sqrt,stats
    d1=(log(S/X)+(rf+sigma*sigma/2.)*T)/(sigma*sqrt(T))
    d2 = d1-sigma*sqrt(T)
    return S*stats.norm.cdf(d1)-X*exp(-rf*T)*stats.norm.cdf(d2)

def delta_call(S,X,T,rf,sigma):
    """
       Objective: detal based on the Black-Schole-Merton option model
       Format   : delta_call(S,X,T,r,sigma)
               S: current stock price
               X: exercise price
               T: maturity date in years
              rf: risk-free rate (continusouly compounded)
           sigma: volatiity of underlying security
       Example 1:
         >>>from p4f import *
         >>> round(delta_call(40,40,1,0.1,0.2),4)
        0.7257

    """
    from scipy import log,exp,sqrt,stats
    d1=(log(S/X)+(rf+sigma*sigma/2.)*T)/(sigma*sqrt(T))
    return(stats.norm.cdf(d1))

def EAR_f(APR,m):
    return (1+APR/m)**m-1

def durationBond(rate,couponRate,nper,freq=1):
    """Objective : estimte the durtion for a given bond
       rate      : discount rate
       couponRate: coupon rate
         nper    : number of years

       Example 1: >>> discountRate=0.1
                  >>> couponRate=0.04
                  >>> n=4
                  >>>durationBond(rate,couponRate,n)
                      3.5616941835365492

       Example #2>>> durationBond(0.1,0.04,4)
                     3.7465335177625576
    """
    import scipy as sp
    d=0
    B=0
    n=0
    for i in sp.arange(nper):
        n=i+1
        B0=sp.pv(rate,n,0,-couponRate)
        d+=n*B0
        B+=B0
    #
    d+=n*sp.pv(rate,n,0,-1)
    B+=sp.pv(rate,n,0,-1)
    d=d/B
    return(d)

def pvValueNperiodModel(r,longTermGrowthRate,dividendNplus1):
    """Objective: estimate stock price based on an n-period model
                         r: discount rate
         LongTermGrowhRate: long term dividend growth rate
         dividendsNpus1   : a dividend vector n + 1

         PV          = d1/(1+R) + d2/(1+R)**2 + .... + dn/(1+R)**n + sellingPrice/(1+R)**n
         sellingPrice= d(n+1)/(r-g)
             where g is long term growth rate

    Example #1: >>> r=0.182
                >>> g=0.03
                >>> d=[1.8,2.07,2.277,2.48193,2.68,2.7877]
                >>> pvValueNperiodModel(r,g,d)
                   17.472364312825711
    """
    import scipy as sp
    d=dividendNplus1
    n=len(d)-1
    g=longTermGrowthRate
    pv=sp.npv(r,d[:-1])*(1+r)
    sellingPrice=d[n]/(r-g)
    pv+=sp.pv(r,n,0,-sellingPrice)
    return pv

def delta_put(S,X,T,rf,sigma):
    """
       Objective: detal based on the Black-Schole-Merton option model
       Format   : delta_put(S,X,T,r,sigma)
               S: current stock price
               X: exercise price
               T: maturity date in years
              rf: risk-free rate (continusouly compounded)
           sigma: volatiity of underlying security
       Example 1:

    """
    from scipy import log,exp,sqrt,stats
    d1=(log(S/X)+(rf+sigma*sigma/2.)*T)/(sigma*sqrt(T))
    return(stats.norm.cdf(d1)-1)

def bs_put(S,X,T,rf,sigma):
    """
       Objective: Black-Schole-Merton option model
       Format   : bs_call(S,X,T,r,sigma)
               S: current stock price
               X: exercise price
               T: maturity date in years
              rf: risk-free rate (continusouly compounded)
           sigma: volatiity of underlying security
       Example 1:
       >>> put=bs_put(40,40,0.5,0.05,0.2)
       >>> round(put,2)
       1.77
    """
    from scipy import log,exp,sqrt,stats
    d1=(log(S/X)+(rf+sigma*sigma/2.)*T)/(sigma*sqrt(T))
    d2 = d1-sigma*sqrt(T)
    return X*exp(-rf*T)*stats.norm.cdf(-d2)-S*stats.norm.cdf(-d1)

def binomial_grid(n):
    import networkx as nx
    import matplotlib.pyplot as plt
    G=nx.Graph()
    for i in range(0,n+1):
        for j in range(1,i+2):
            if i<n:
                G.add_edge((i,j),(i+1,j))
                G.add_edge((i,j),(i+1,j+1))
    posG={}    #dictionary with nodes position
    for node in G.nodes():
        posG[node]=(node[0],n+2+node[0]-2*node[1])
    nx.draw(G,pos=posG)

#from math import sqrt, log, pi,exp
#import re
#--------------------------------------------------------#
#--- Cumulative normal distribution        --------------#
#--------------------------------------------------------#
def CND(X):
    """ Cumulative standard normal distribution
            CND(x): x is a scale
            e.g.,
            >>> CND(0)
            0.5000000005248086
    """
    (a1,a2,a3,a4,a5)=(0.31938153,-0.356563782,1.781477937,-1.821255978,1.330274429)
    L = abs(X)
    K = 1.0 / (1.0 + 0.2316419 * L)
    w = 1.0 - 1.0 / sqrt(2*pi)*exp(-L*L/2.) * (a1*K + a2*K*K + a3*pow(K,3) +
    a4*pow(K,4) + a5*pow(K,5))
    if X<0:
        w = 1.0-w
    return w

#--------------------------------------------------------#
#--- Black-Scholes-Merton option model     --------------#
#--------------------------------------------------------#
def bs_call_old(S,X,T,r,v):
    """
       Objective: Black-Schole-Merton option model
         >>>bs_call(40,40,1,0.1,0.2)
         5.307874373215604
    """
    d1 = (log(S/X)+(r+v*v/2.)*T)/(v*sqrt(T))
    d2 = d1-v*sqrt(T)
    return S*CND(d1)-X*exp(-r*T)*CND(d2)

#--------------------------------------------------------#
#--- Present value of a perpetuity due     --------------#
#--------------------------------------------------------#
def pv_perpetuity_due(c,r):
    """Objective: estimate present value of a perpetuity due
                  i.e., 1st cash flow occurs today
          c: period payment
          r: discount rate
          e.g.,
          >>>pv_perpetuity_due(30000,0.08)
          405000.0
    """
    return(c/r*(1+r))

#--------------------------------------------------------#
#--------------------------------------------------------#
#--------------------------------------------------------#
def payback_(investment, cashflows):
    """Objective: estimate payback period: number of years requried to
                  recover the initial investment
       e.g.,
       >>>invest=200
       >>>future_cashflows=[60.0, 60.0, 70.0, 90.0]
       >>>payback_(invest,future_cashflows)
       or
       >>>payback_(200.0, [60.0, 60.0, 70.0, 90.0])
       3.1111111111111112
    """
    total, years, cumulative = 0.0, 0, []
    if not cashflows or (sum(cashflows) < investment):
        raise Exception("insufficient cashflows")
    for cashflow in cashflows:
        total += cashflow
        if total < investment:
            years += 1
        cumulative.append(total)
    A = years
    B = investment - cumulative[years-1]
    C = cumulative[years] - cumulative[years-1]
    return A + (B/C)

#--------------------------------------------------------#
#--------------------------------------------------------#
#--------------------------------------------------------#
def payback_period(cashflows):
    """Objective: estimate payback period, number of years needed to
                 recover the initial investment
       e.g.,
       >>>x=[-200.0, 60.0, 60.0, 70.0, 90.0]
       >>>payback_period(x)
       3.1111111111111112
    """
    investment, cashflows = cashflows[0], cashflows[1:]
    if investment < 0 : investment = -investment
    return payback_(investment, cashflows)

#--------------------------------------------------------#
#--------------------------------------------------------#
#--------------------------------------------------------#
def npv_f(rate, cashflows):
    """Objective  : estimate NPV value
         rate     : discount rate
         cashflows: cashflows
         e.g.
         >>>npv_f(0.1, [-100.0, 60.0, 60.0, 60.0])
         49.211119459053322
    """
    total = 0.0
    for i, cashflow in enumerate(cashflows):
        total += cashflow / (1 + rate)**i
    return total

#--------------------------------------------------------#
#--- IRR (Internal Rate of Return) ----------------------#
#--------------------------------------------------------#
def IRR_f(cashflows, iterations=100):
    """Objective: estimte IRR (Internal Rate of Return)
         e.g.,
         >>>x=[-100.0, 60.0, 60.0, 60.0]
         >>>IRR_f(x)
         0.36309653947517645
    """
    rate = 1.0
    investment = cashflows[0]
    for i in range(1, iterations+1):
        rate *= (1 - npv_f(rate, cashflows) / investment)
    return rate

#--------------------------------------------------------#
#----PV_f function --------------------------------------#
#--------------------------------------------------------#
def pv_f(fv,r,n):
    """Objective: estimate present value
              fv: fture value
              r : discount period rate
              n : number of periods
        formula : fv/(1+r)**n
           e.g.,
           >>>pv_f(100,0.1,1)
           90.9090909090909
           >>>pv_f(r=0.1,fv=100,n=1)
           90.9090909090909
           >>>pv_f(n=1,fv=100,r=0.1)
           90.9090909090909
    """
    return fv/(1+r)**n

#--------------------------------------------------------#
#--------------------------------------------------------#
#--------------------------------------------------------#
def bond_price(c,r,n,face_value):
  """Objective   : estimte price of a bond
       c         : coupon payment
       r         : discount rate
       n         : number of period
       face_value: principal
       formula   : c/r*(1-1/(1+r)**n) + face_value/(1+r)**n
       e.g.,
       >>>bond_price(10,0.08,10,1000)
       530.2943020740986
  """
  return  c/r*(1-1/(1+r)**n) + face_value/(1+r)**n

#----------------------------------------------------------#
#----Mimic PV() function in Excel -------------------------#
#----------------------------------------------------------#
def pv_excel(rate,nper,pmt=0,fv=0,type=0):
    """Objective: mimic Excel pv() function
           rate : period rate
           nper : number of periods
           pmt  : payment per period
           fv   : future value
           type : type=0, payments occur at the end of each period
                  type=1, payments occur at the begining of each perod
                  default value is type=0
           e.g.,
           >>>pv_excel(0.1,1,100)
           90.90909090909093
           >>>pv_excel(0.1,1,100,100)
           181.81818181818184
           >>>pv_excel(0.1,1,100,100,1)
           200.00000000000003
    """
    pv=pv_f(fv,rate,nper) +pv_annuity(nper,pmt,rate)
    if(type==1): pv=pv*(1+rate)
    return pv

#--------------------------------------------------------#
#--- Present value of a perpetuity         --------------#
#--------------------------------------------------------#
def pv_perpetuity(c,r):
    """
  Objective : estimate present value of a perpetuity
     c      : period payment
     r      : discount rate
  formula   : c/r
      e.g.,
      >>>pv_perpetuity(100,0.08)
      1250.0
    """
    return c/r

#--------------------------------------------------------#
#--- Present value of a growing perpetuity --------------#
#--------------------------------------------------------#
def pv_grow_perpetuity(c,r,q):
    """Objective : estimate present value of a growthing perpetuity
              r  : discount rate
              q  : growth rate of perpetuity
              c  : period payment
        formula  : c/(r-g)
        e.g.,
         >>>pv_grow_perpetuity(30000,0.08,0.04)
         750000.0
    """
    return(c/(r-q))

#--------------------------------------------------------#
#--- Present value of an annuity ------------------------#
#--------------------------------------------------------#
def pv_annuity(n,c,r):
    """Objective : estimate present value of an annuity
            n    : number of payments
            c    : payment amount
            r    : discount
       formula   : c/r*(1-1/(1+r)**n)
       e.g.,
       >>>pv_annuity(29,1,0.08)
       11.158406010577684
    """
    return c/r*(1-1/(1+r)**n)

#--------------------------------------------------------#
#--- Present value of an annuity with 1st payment        #
# -----------n-period from today ------------------------#
#--------------------------------------------------------#
def pv_annuity_k_period_from_today(n,c,r,k_period_from_today=0):
    """
  Objective            : estimate present value of an annuity
         n             : number of payments
         c             : payment amount
         r             : discount
    k_period_from_today: 1st payment is k periods from today
                         default is zero
         formula       : pv_annuity(n,c,r)/(1+r)**k_period_from_today
         e.g.,
         >>> pv_annuity_k_period_from_today(29,1,0.08,1)
         10.331857417201558
         >>> pv_annuity_k_period_from_today(29,1,0.08)
         11.158406010577684
    """
    return pv_annuity(n,c,r)/(1+r)**k_period_from_today

#--------------------------------------------------------#
#--- Estimate the number of periods for an annuity ------#
#--------------------------------------------------------#
def n_annuity(r,pv,pmt,fv):
    """
  Objective: esimate the number of period
        r  : discount rate
        fv : future value
        pv : present value
        pmt: payment per period
    formula: log((fv*r+pmt)/(pv*r+pmt))/log(1+r)
        e.g.,
        >>> n_annuity(0.0725,10050,5000,60000)
        6.999345696211027
    """
    return log((fv*r+pmt)/(pv*r+pmt))/log(1+r)

#--------------------------------------------------------#
#---fv_f  future value function -------------------------#
#--------------------------------------------------------#
def fv_f(pv,r,n):
    """
      Objective : estimate furuew value
             pv : present value
             r  : discount period rate
             n  : number of periods
       formula  : pv*(1+r)**n
        e.g.,
        >>>fv_f(100,0.1,1)
        110.00000000000001
    """
    return pv*(1+r)**n

#--------------------------------------------------------#
#--- Present value of an annuity ------------------------#
#--------------------------------------------------------#
def fv_annuity(n,c,r):
    """
     Objective: estimate future value of an annuity
       n      : number of payments
       c      : payment amount
       r      : discount
     formula  : c/r*((1+r)**n-1)
       e.g.,
       >>>fv_annuity(2,100,0.1)
       210.0000000000002
    """
    return c/r*((1+r)**n-1)

#--------------------------------------------------------#
#-- Present value of a growing annuity ------------------#
#--------------------------------------------------------#
def pv_growing_annuity(n,c,r,g):
    """
     Objective: estimate present value of a growting annuity
         c    : payment
         n    : number of payments
         r    : discount rate
         g    : growth rate  (g<r)
      formula : c/(r-g)*(1-((1+g)/(1+r))**n)
         e.g.
        >>>pv_growing_annuity(30,10000,0.1,0.05)
        150463.14700582038
    """
    tt=((1+g)/(1+r))**n
    return c/(r-g)*(1-tt)

#----------------------------------------------------------#
#--mean function  -----------------------------------------#
#----------------------------------------------------------#
def mean(numberList):
    """
     Objective: estimate mean of an input variable
     e.g.,
     >>>x=(1,2,3)
     >>>mean(x)
     2.0
     >>>x=[1,1,2]
     >>>mean(x)
     1.3333333333333333
    """
    if len(numberList) == 0:
        return float('nan')
    floatNums = [float(x) for x in numberList]
    return sum(floatNums) / len(numberList)

#----------------------------------------------------------#
#--sign function  -----------------------------------------#
#----------------------------------------------------------#
def sign(x):
    """
       Objective: take the sign of x
       e.g.,
          >>>sign(-1.2)
          -1
          >>>sign(5)
          1
    """
    if x>0: return 1
    elif x<0: return -1
    else: return 0

#----------------------------------------------------------#
#--IRR estimation -----------------------------------------#
#----------------------------------------------------------#
def IRRs_f(cash_flows):
    """
     Objective: find mutiple IRRs
       e.g,
        >>>x=[55,-50,-50,-50,100]
        >>>IRRs_f(x)
        [0.072, 0.337]
    """
    n=1000
    r=range(1,n)
    n_cash_flow=len(cash_flows)
    epsilon=abs(mean(cash_flows)*0.01)
    irr=[-99.00]
    j=1
    npv=[]
    for i in r: npv.append(0)
   #print("len(r)", len(r))
    #print("len(npv)",len(npv))
    lag_sign=sign(npv_f(float(r[0]*1.0/n*1.0),cash_flows))
    for i in range(1,n-1):
                  #print("r[i]",r[i])
                  interest=float(r[i]*1.0/n*1.0)
                  npv[i]=npv_f(interest,cash_flows)
                  s=sign(npv[i])
                  if s*lag_sign<0:
                      lag_sign=s
                      if j==1:
                          irr=[interest]
                          j=2
                      else:
                          irr.append(interest)
    return irr

#--------------------------------------------------------#
#--- Estimate Payment of an annuity ---------------------#
#--------------------------------------------------------#
def PMT(n,r,pv,fv):
    """
    Objective: estimate period payment (like Excel function)
       n    : number of periods
       r    : discount rate
       pv   : present value
       fv   : period payment
       e.g.,
         >>>PMT(10,0.08,100000,0)
         14902.948869707534
    """
    return (pv-fv/(1+r)**n)*r/(1-1/(1+r)**n)

#--------------------------------------------------------#
#--- Estimte the number of periods for an annuity -------#
#       Excel function                                   #
#--------------------------------------------------------#
def NPER(r,pv,pmt,fv):
    """
    Objective: esimate the number of periods
       r  : discount rate
       fv : future value
       pv : present value
       pmt: payment per period
       e.g.,
         >>>NPER(0.0725,10050,5000,60000)
         6.999345696211027
    """
    return log((fv*r+pmt)/(pv*r+pmt))/log(1+r)

#----------------------------------------------------------#
#----------------------------------------------------------#
#----------------------------------------------------------#
def r_continuous(r,m):
    """
      Objective : estimate a coutinusly compounded interest rate
              r : an annual rate (Annual Percentage Rate)
              m : compounding frequency per year
      formula   : m*log(1+r/m)
    """
    return  m*log(1+r/m)

#----------------------------------------------------------#
#----------------------------------------------------------#
#----------------------------------------------------------#
def Rc_f(r,m):
    """
     Objective: estimate a continously compounded rate
        r : norminal rate
        m : number of times compounded each year
        e.g.,
         >>>Rc_f(0.1,2)
         0.09758032833886408
    """
    return m*log(1+r/m)

#----------------------------------------------------------#
#----------------------------------------------------------#
#----------------------------------------------------------#
def Rm_f(Rc,m):
    """
      Get norminal rate for a given continuous compounded rate
       Rc : continuous compounded rate
       m  : number of desired times compounded each year
            e.g., Rm_f(0.08,4)= 0.08080536
    """
    return m*(exp(Rc/m)-1)


import numpy as np
import matplotlib.pyplot as plt
import scipy
import wavio

# def blueNoise(t, fs, fc, bd=32):
#    M = len(t)
#    x = np.random.uniform(-2 ** bd, 2 ** bd - 1, M)
#    X = np.fft.fft(x)
#    hp = fc / fs * M
#    Y = [X[i] if hp < i < M - hp else 0 for i in range(M)]
#    np.real(np.fft.ifft(Y))

def lp_filter(x, fs, fc, M=None, plot=False):
    if M is None:
        M = x.size
    Xf = scipy.fft.fft(x, n=M)
    Af = abs(Xf)

    # Convert cutoff frequencies into points on spectrum
    lp = fc / fs * M
    Y = [0 if lp < i < M - lp else Xf[i] for i in range(M)]

    y = scipy.fft.ifft(Y, n=M)

    if plot:

        plt.figure()
        plt.subplot(211)
        plt.plot(x)
        plt.plot(y)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.subplot(212)
        plt.stem(Af[:len(Af)//2]*2.0/len(Af))
        plt.axis([0, fs / 2, None, None])
        plt.axvspan(0, lp, facecolor='g', alpha=0.5)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.show()

    return y

def hp_filter(x, fs, fc, M=None, plot=False):
    if M is None:
        M = x.size
    Xf = scipy.fft.fft(x, n=M)
    Af = abs(Xf)

    # Convert cutoff frequencies into points on spectrum
    hp = fc / fs * M
    Y = [Xf[i] if hp < i < M - hp else 0 for i in range(M)]

    y = scipy.fft.ifft(Y, n=M)

    if plot:

        plt.figure()
        plt.subplot(211)
        plt.plot(x)
        plt.plot(y)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.subplot(212)
        plt.stem(Af[:len(Af)//2]*2.0/len(Af))
        plt.axis([0, fs / 2, None, None])
        plt.axvspan(hp, fs/2, facecolor='g', alpha=0.5)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.show()

    return y

def bp_filter(x, fs, fl, fh, M=None, plot=False):
    if M is None:
        M = x.size
    Xf = scipy.fft.fft(x, n=M)
    Af = abs(Xf)

    # Convert cutoff frequencies into points on spectrum
    hp = fh / fs * M
    lp = fl / fs * M
    Y = [Xf[i] if ((lp <= i <= hp) or (M-hp <= i <= M-lp) ) else 0 for i in range(M)]

    y = scipy.fft.ifft(Y, n=M)

    if plot:

        plt.figure()
        plt.subplot(211)
        plt.plot(x)
        plt.plot(y)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.subplot(212)
        plt.stem(Af[:len(Af)//2]*2.0/len(Af))
        plt.axis([0, fs / 2, None, None])
        plt.axvspan(lp, hp, facecolor='g', alpha=0.5)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.show()

    return y

def bs_filter(x, fs, fl, fh, M=None, plot=False):
    if M is None:
        M = x.size
    Xf = scipy.fft.fft(x, n=M)
    Af = abs(Xf)

    # Convert cutoff frequencies into points on spectrum
    hp = fh / fs * M
    lp = fl / fs * M
    Y = [0 if ((lp <= i <= hp) or (M-hp <= i <= M-lp) ) else Xf[i] for i in range(M)]

    y = scipy.fft.ifft(Y, n=M)

    if plot:

        plt.figure()
        plt.subplot(211)
        plt.plot(x)
        plt.plot(y)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.subplot(212)
        plt.stem(Af[:len(Af)//2]*2.0/len(Af))
        plt.axis([0, fs / 2, None, None])
        plt.axvspan(0, lp, facecolor='g', alpha=0.5)
        plt.axvspan(hp, fs / 2, facecolor='g', alpha=0.5)
        plt.grid(which='both')
        plt.minorticks_on()
        plt.show()

    return y

if __name__ == "__main__":
    fs = 1000
    t = np.arange(0, 1, 1 / fs)
    Sines = [np.sin(2*np.pi*t*n)*(1-(n/500.0)) for n in [225.0, 187.5, 125.0, 62.5, 30.0, 7.5, 6.25]]
    x = np.sum(Sines, axis=0)

    #lp_filter(x, fs, 10.0, plot=True)
    #hp_filter(x, fs, 200.0, plot=True)
    #bp_filter(x, fs, 45.0, 80.0, plot=True)
    y1 = bs_filter(x, fs, 45.0, 200.0, plot=True)

    plt.figure()
    plt.magnitude_spectrum(y1, Fs=fs)
    plt.show()


# TODO: Add import statements
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
import numpy as np

# Assign the data to predictor and outcome variables
# TODO: Load the data
train_data = pd.read_csv('data.csv')
X = train_data[['Var_X']]
y = train_data[['Var_Y']]

# Create polynomial features
# TODO: Create a PolynomialFeatures object, then fit and transform the
# predictor feature
poly_feat = PolynomialFeatures(degree=4)
X_poly = poly_feat.fit_transform(X)
pred_data = np.arange(-3, 3, .4)
pred_x = pd.DataFrame(data=pred_data)

# Make and fit the polynomial regression model
# TODO: Create a LinearRegression object and fit it to the polynomial predictor
# features
poly_model = LinearRegression()
poly_model.fit(X_poly, y)
y_pred = poly_model.predict(poly_feat.transform(pred_x))
plt.scatter(X, y)
plt.plot(pred_data, y_pred)
plt.show()

# Once you've completed all of the steps, select Test Run to see your model
# predictions against the data, or select Submit Answer to check if the degree
# of the polynomial features is the same as ours!


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
ch3Python.py. Evaluating the fit with the coefficient of variantion.
This is demonstration code, so it is transparent but inefficient.
"""
import numpy as np

x = [1.00,1.25,1.50,1.75,2.00,2.25,2.50,2.75,3.00,3.25,3.50,3.75,4.00]
y = [3.34,4.97,4.15,5.40,5.21,4.56,3.69,5.86,4.58,6.94,5.57,5.62,6.87]

# Convert data to vectors.
x = np.array(x)
y = np.array(y)
n = len(y)

# Find zero mean versions of x and y.
xzm = x-np.mean(x)
yzm = y-np.mean(y)
# Covariance between x and y.
covxy = np.sum(xzm * yzm)/n
# Variance of x.
varx = np.sum(xzm * xzm)/n
# Variance of y.
vary = np.sum(yzm * yzm)/n

# Find slope b1=0.764
b1 = covxy/varx

# Find intercept b0=3.225
b0 = np.mean(y) - b1*np.mean(x)

yhat = b1*x + b0 # 0.511
varyhat = np.var(yhat)

# Find coefficient of determination r2.
r2 = covxy * covxy / (varx * vary)
# OR equivalently ...
r2 = varyhat / vary # 0.466

print("Variance of x = %0.3f" %varx) # 0.875
print("Variance of y = %0.3f" %vary) # 1.095
print("Covariance of x and y = %0.3f" %covxy) # 0.669
print("Variance of yhat = %0.3f" %varyhat) # 0.511
print("Coefficient of variation = %0.3f" %r2) # 0.466
# END OF FILE.


import pandas as pd
import statsmodels.tsa.stattools as stattools
import numpy as np

def find_optimum_lags(df: pd.DataFrame) -> int:
  """Submit a dataframe with two columns - this will find the offset needed to aligned them using crosscorrelation"""
  df_ = df.copy().dropna(how='any')
  optimum_lags = np.argmax(stattools.ccf(df_.iloc[:,0].values,df_.iloc[:,1].values))
  print(f"Optimum offset (lag) between {df_.iloc[:,0].name} and {df_.iloc[:,1].name}: {optimum_lags}")
  return optimum_lags

def min_max_scale_df(df: pd.DataFrame) -> pd.DataFrame:
    """
    Scales the data frame values between 0 and 1 across the columns allowing for easier comparison of line shape on plots
    :param df: data frame to be scaled
    :return: scaled dataframe
    """
    return df.div(df.max(),axis=1)


import numpy as np
from scipy import stats

array_x=list(map(int,list(input().split(' '))))

def mode_manual(l):
    """
    l: List of integers
    return mode of list l. if there is more than one mode, then pick lowest
    """
    dict_mode = {}
    for n in l:
        i = l.count(n)
        if i not in dict_mode.keys():
            dict_mode[i] = [n]
        else:
            dict_mode[i].append(n)
    dict_mode = {k: sorted(set(v)) for k,v in dict_mode.items()}
    dict_mode = dict(sorted(dict_mode.items(),key=lambda kv: kv[0], reverse=True))
    keys = list(dict_mode.keys())
    return (dict_mode[keys[0]][0], keys[0])

def median_manual(l):
    """
    l: List of integers
    return median of list l
    """
    n = len(l)
    l = sorted(l)
    if n%2 == 1: return l[int((n-1)/2)]
    return (l[int(n/2)-1] + l[int(n/2)]) / 2

# Manual
mean, median, mode = np.mean(array_x), median_manual(array_x), mode_manual(array_x)
print(f"{mean:.1f}")
print(f"{median:.1f}")
print(f"{mode}")

# Library
mean, median, mode = np.mean(array_x), np.median(array_x), stats.mode(array_x)
print(f"{mean:.1f}")
print(f"{median:.1f}")
print(f"{mode.mode, mode.count}")


import numpy as np

# Linear Regressions Exercises

def empirical_risk(theta, X, Y, loss):
    loss_sum = 0
    for i, feature_vector in enumerate(X):
        loss_sum += loss(Y[i] - np.dot(np.transpose(theta), feature_vector))
    return loss_sum/X.shape[0]

def hinge_loss(z):
    if z < 1:
        return 1 - z
    return 0

def lms_loss(z):
    return z**2/2

if __name__ == "__main__":
    # Defines the Feature Matrix and its Labels
    X = np.array([[1, 0, 1],
                  [1, 1, 1],
                  [1, 1, -1],
                  [-1, 1, 1]])

    Y = np.array([2,
                  2.7,
                  -0.7,
                  2])

    # Define the Parameters
    theta = np.array([0, 1, 2])

    # Calculate the Risk
    R_hinge = empirical_risk(theta, X, Y, hinge_loss)
    R_lms = empirical_risk(theta, X, Y, lms_loss)

    print('Hinge: {}'.format(R_hinge))
    print('LMS: {}'.format(R_lms))


import numpy as np
import matplotlib.pyplot as plt

DISPLAY_DATA = False

"""
Bikin data dummy dengan persamaan: y = 4 + 3x + Gaussian_noise
"""
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

if DISPLAY_DATA is True:
  plt.figure(0)
  plt.scatter(X,y)
  plt.show()

"""
Solusi menggunakan persamaan normal.
Menggunakan fungsi inv() dari Linear Algebra module-nya Numpy ( np.linalg ) untuk menghitung inverse dari matrix,
dan fungsi dot() untuk perkalian matrix.
"""
X_b = np.c_[np.ones((100, 1)), X] # dibuat x0 = 1 supaya theta_0 tidak sama dengan 0
theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)

print("Estimasi normal: ",theta_best[0], theta_best[1])

"""
Sekarang kita plot model-nya dengan dua end-point [0] dan [2]
"""
X_points = np.array([[0], [2]])
X_points_b = np.c_[np.ones((2, 1)), X_points] # dibuat x0 = 1 supaya theta_0 tidak 0
y_points = X_points_b.dot(theta_best)
#print(y_points)

"""
Solusi kedua menggunakan scikit-learn
"""
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X, y)
print("Estimasi scikit: ", lin_reg.intercept_, lin_reg.coef_)
y_scikit = lin_reg.predict(X_points)

plt.figure(1)
plt.plot(X,y,"g.")
plt.plot(X_points, y_points, "r-")
plt.plot(X_points, y_scikit, "b-")
plt.axis([0,2,0,15])
plt.legend(["Data", "Model Normal", "Model Scikit"])
plt.title("Dengan Solusi Normal")
plt.show()


import numpy as np
from typing import List, Tuple
import torch
import pylab as plt

class PolicyEvaluation:

    def __init__(self, grid, policy, actions, small_change, gamma) -> None:
        self.grid = grid
        self.policy = policy
        self.actions = actions
        self.rows, self.cols = self.grid.grid_board.shape
        self.value_function = np.zeros((self.rows,self.cols))
        self.SMALL_CHANGE = small_change
        self.gamma = gamma
        self.actions.set_actions()

    def iterative_policy_evaluation(self):
        # First we do the policy evaluation for random policy.
        # For Random policy all states are equally likely so the probability would be
        # 1/ (No. of states in that policy)

        states = list(range(self.rows * self.cols))

        self.value_function = np.zeros((self.rows * self.cols))
        while True:
            # we iterate through all the states, for each state select action and sum there values.
            # this summation of values for each action multiplied by probability is known as expectation of that state
            # This expectation is then assigned to that particular state.
            # This process is continued untile we find very small change between two iterations for particular state.
            max_expected_value_change = 0
            for state in states:
                old_value_function = self.value_function[state]
                actions = self.actions.grid_actions[state]
                expected_value_sum = 0
                for action in actions:
                    expected_value_sum = expected_value_sum + (1/len(actions) * (self.actions.get_action_reward(state,action) + (self.gamma * self.value_function[self.actions.get_next_state_number(state,action)])))
                self.value_function[state] = expected_value_sum
                max_expected_value_change = max(max_expected_value_change, np.abs(expected_value_sum - old_value_function))
            if max_expected_value_change <= self.SMALL_CHANGE:
                break

        self.grid.print_values(self.value_function)

    def set_deterministic_policy(self):
        #Policy :
        # if we start from Start state we reach goal else we reach negative goal
        self.actions.grid_actions= self.policy.set_deterministic_policy(self.actions.grid_actions)

    def iterative_policy_evaluation_deterministic(self):
        self.gamma = 0.9
        self.set_deterministic_policy()
        self.grid.print_policy(self.actions.grid_actions)
        # Policy evaluation for deterministic policy
        # Probability for any action will be 1 as there is only one action
        states = list(range(self.rows * self.cols))

        self.value_function = np.zeros((self.rows * self.cols))
        while True:
            # holds the maximum change
            max_expected_value_change = 0
            for state in states:
                old_value_function = self.value_function[state]
                actions = self.actions.grid_actions[state]
                for action in actions:
                    # Not taking sum because there will be only one action
                    # multiplying by 1 because there will be only one action and probability of that action will be 1
                    self.value_function[state] =  (1 * (self.actions.get_action_reward(state,action)) + (self.gamma * self.value_function[self.actions.get_next_state_number(state,action)]))
                max_expected_value_change = max(max_expected_value_change, np.abs(self.value_function[state] - old_value_function))
            if max_expected_value_change < self.SMALL_CHANGE:
                break

        self.grid.print_values(self.value_function)


import matplotlib.pyplot as plt
import numpy as np
np.random.seed(0)

data = np.random.randn(10_000)
#plt.hist(data, bins=30, alpha=.5, histtype="stepfilled", color="steelblue")
#plt.show()

counts, bin_edges = np.histogram(data, bins=5)
print(counts)
print(bin_edges)

x1 = np.random.normal(0, 0.8, 1000)
x2 = np.random.normal(-2, 1, 1000)
x3 = np.random.normal(3, 2, 1000)

kwargs = dict(
    histtype='stepfilled',
    alpha=0.3,
    bins=40
)

#plt.hist(x1, **kwargs)
#plt.hist(x2, **kwargs)
#plt.hist(x3, **kwargs)
#plt.show()

mean = [0, 0]
cov = [[1, 1], [1, 2]]
x, y = np.random.multivariate_normal(mean=mean, cov=cov, size=10000).T
plt.hist2d(x, y, bins=30, cmap="Blues")
plt.colorbar()
plt.show()


import numpy as np
import scipy as scp
def pca(X, frac, mask = None):
	"""
	function [y,n_dim,vecs,vals,mu] = pca(x,frac,mask)

	Principal Components Analysis
	 transforms input data x using PCA, reduces dimensionality
	 to capture fraction of magnitude of eigenvalues

	inputs:
	 x - input data M dimensions by N samples
	 frac - [0-1] fractional amount of total eigenvalue magnitude to retain, 1 = no dimensionality reduction
	 mask - binary mask of samples to include in covariance computation (use to mask off invalid pixels)
	         leave off the argument or use [] for no mask

	outputs:
	 y - PCA transformed dimensionality reduced data
	 n_dim - number of dimensions in the output data
	 vecs - full set of eigenvectors of covariance matrix (column vectors)
	 vals - eigenvalues of covariance matrix
	 mu - mean of input data, subtracted before rotating

	8/7/2012 - Taylor C. Glenn - tcg@cise.ufl.edu
	11/2018 - Python Implementation by Yutai Zhou
	"""
	n_dim, n_sample = X.shape
	if n_dim > 210:
		input('That is a lot of dimensions. It may crash. Press ctrl + c to stop execution, or enter to continue')

	mask = np.ones(n_sample, dtype=bool) if mask is None else mask
	mu = np.mean(X[:, mask == 1], 1)
	sigma = np.cov(X[:, mask == 1].T,  rowvar=False)

	z = X - mu[:, np.newaxis]
	U, S, V = scp.linalg.svd(sigma, lapack_driver = 'gesdd')

	evecs = U

	mag = np.sum(S)
	ind = np.where(np.cumsum(S) / mag >= frac)[0][0]
	n_dim = ind
	y = evecs[:, :ind + 1].T @ z # d x 72 * 72 x 4488
	return y, n_dim, evecs, S, mu


'''
The evaluation metric is weighted correlation as opposed to a weighted mean of correlation.

The metric is defined as follows, where 'a', 'b' and 'weights' are vectors of the same length.

'a' and 'b' are the expected and predicted targets, and ' weights' include the weight of each row,
            determined by its asset.
'''

import numpy as np

def weighted_correlation(a, b, weights):

  w = np.ravel(weights)
  a = np.ravel(a)
  b = np.ravel(b)

  sum_w = np.sum(w)
  mean_a = np.sum(a * w) / sum_w
  mean_b = np.sum(b * w) / sum_w
  var_a = np.sum(w * np.square(a - mean_a)) / sum_w
  var_b = np.sum(w * np.square(b - mean_b)) / sum_w

  cov = np.sum((a * b * w)) / np.sum(w) - mean_a * mean_b
  corr = cov / np.sqrt(var_a * var_b)

  return corr


"""
Nonlinearities and their derivatives

Each function returns the value and derivative of a nonlinearity. Given :math:`y = f(x)`, the function returns
:math:`y` and :math:`dy/dx`
"""
import numpy as np

def exp(x):
    """Exponential function"""

    # compute the exponential
    y = np.exp(x)

    # compute the first and second derivatives
    dydx = y
    dy2dx2 = y

    return y, dydx, dy2dx2

def softrect(x):
    """ Soft rectifying function

    .. math::
        y = \log(1+e^x)
    """

    # compute the soft rectifying nonlinearity
    x_exp = np.exp(x)
    y = np.log1p(x_exp)

    # compute the derivative
    dydx = x_exp / (1 + x_exp)

    # compute the second derivative
    dy2dx2 = x_exp / (1 + x_exp)**2

    return y, dydx, dy2dx2


# This program calculate moment of inertia of
# some 2D systems about origin and z axis
import numpy as np
import matplotlib.pyplot as plt

def disc():
    # generares disc (radius 1) as x, y pairs
    # returns : numpy arrays x and y
    x = np.linspace(-1, 1, 2000)
    y = np.linspace(-1, 1, 2000)

    u,v = np.meshgrid(x, y)

    cx = u[u**2+v**2<=1]
    cy = v[u**2+v**2<=1]

    return cx, cy

def disc_with_hole():
    # generares disc (radius 1) as x, y pairs
    # with a hole of radius 1/2
    # returns : numpy arrays x and y
    x = np.linspace(-1, 1, 2000)
    y = np.linspace(-1, 1, 2000)

    u,v = np.meshgrid(x, y)

    mask = np.logical_and(u**2+v**2<=1, (u-0.5)**2+v**2>=0.25)
    cx = u[mask]
    cy = v[mask]

    return cx, cy

# ############################################
# CALCULATION
x, y = disc_with_hole()
N = len(x)   # number of particles
M = 3.0/4.0  # mass
m = M / N   # masss of each particle
I = (m*(x**2+y**2)).sum()  # Moment of Inertia
print("M.I = ", round(I, 3))
print("k   = ", round(np.sqrt(I/M), 3))

# x, y = disc_with_hole()
# print(x.shape, y.shape)
fig, ax = plt.subplots()
ax.set_aspect("equal")
plt.scatter(x, y, s=0.5)
plt.show()


import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import scipy.optimize

Vdc = 3.293551538837322212
res = 3.3 / 1023
inputFile = 'DischargeProcessData.txt'

# define fitting function
def DischargeProcess(t, t0, tau):
    return Vdc * (np.exp(-(t-t0)/tau))

# read the experimental data from file
t, v = np.loadtxt(inputFile, delimiter = ' ', unpack = True)

# assign the experimental uncertainty on Voltage values
dv = np.ones(v.size) * res

# initial guesses for fitting parameters
t0_guess, tau_guess = 20, 1

# fitting procedure
nlfit, nlpcov = \
       scipy.optimize.curve_fit(DischargeProcess, t, v, \
                                p0 = [t0_guess, tau_guess], sigma = dv,\
                                bounds = (0, 100))

# obtaining parameters from the best fit procedure
t0, tau = nlfit

# obtaining uncertainties associated with fitting parameters
dt0, dtau = \
     [np.sqrt(nlpcov[j, j]) for j in range(nlfit.size)]

# create fitting function from fitted parameters
t_fit = np.linspace(t.min(), t.max(), 128)
v_fit = DischargeProcess(t_fit, t0, tau)

# residuals and reduced chi squared
resids = v - DischargeProcess(t, t0, tau)
redchisqr = ((resids/dv)**2).sum()/float(t.size-2)
ndf = t.size - 2
# where 2 is the number of free parameters

# create figure window to plot data
fig = plt.figure(1, figsize = (8,8))
gs = gridspec.GridSpec(2, 1, height_ratios = [6, 2])

# plotting data and fit
ax1 = fig.add_subplot(gs[0])
ax1.plot(t_fit, v_fit)
ax1.errorbar(t, v, yerr = dv, fmt = 'or', ecolor = 'black', markersize = 2)
ax1.set_xlabel(' time (s)')
ax1.set_ylabel('voltage $V_C$ (V)')
ax1.text(0.6, 0.80, r'$\tau$ = {0:6.4f}$\pm${1:0.4f}'.format(tau, dtau), transform = ax1.transAxes, fontsize = 14)
ax1.text(0.6, 0.70, r'$t_0$= {0:5.4f}$\pm${1:0.4f}'.format(t0, dt0), transform = ax1.transAxes, fontsize = 14)
ax1.text(0.6, 0.60, r'$\chi_r^2$ = {0:0.1f}, ndf = {1}'.format(redchisqr, ndf), transform = ax1.transAxes, fontsize = 14)

# plotting residuals
ax2 = fig.add_subplot(gs[1])
ax2.errorbar(t, resids, yerr = dv, ecolor = 'black', fmt ='ro', markersize = 2)
ax2.axhline(color = 'gray', zorder = -1)
ax2.set_xlabel('time (s)')
ax2.set_ylabel('residuals (V)')
plt.savefig('DischargingDataAndFit.pdf')
plt.show()


from typing import Union, Optional

import numpy as np

def gmean(
    a: np.ndarray,
    axis: Optional[int] = None,
    eps: Optional[float] = 1e-6):
    """Computes the geometric mean along the specified axis.
    Returns the geometric average of the array elements.
    That is:  n-th root of (x1 * x2 * ... * xn)

    Args:
        a: Input array
        axis: Axis along which the geometric mean is computed.
            If None, compute over the whole array `a`.
        eps: Small value for stability

    Returns:
        gmean: The geometric mean of `a`

    Examples:
    >>> gmean(np.array([1, 4]))
    2.0
    >>> gmean(np.array([1, 2, 3, 4, 5, 6, 7]))
    3.3800151591412964
    >>> import hdrpy
    >>> hdr = hdrpy.io.read("./data/CandleGlass.exr")
    >>> gmean(hdrpy.image.get_luminance(hdr))
    0.00040137774000534386
    """
    a_c = a.copy()
    if eps is not None:
        a_c[a_c <= 0] = eps
        a_c[np.isnan(a_c)] = eps
    log_a = np.log(a_c)
    return np.exp(log_a.mean(axis=axis))

def min_max_normalization(
    a: np.ndarray,
    min_: Optional[Union[float, int]] = None,
    max_: Optional[Union[float, int]] = None) -> np.ndarray:
    """Normalizes the given array `a`
    so that its range is in [0, 1]

    Args:
        a: Input array
        min: Minimum possible value of the array
        max: Minimum possible value of the array
    Returns:
        array in [0, 1] range

    Examples:
    >>> a = min_max_normalization(np.random.randn(100))
    >>> np.all(a <= 1)
    True
    >>> np.all(a >= 0)
    True
    """
    if min_ is None:
        min_ = float(np.amin(a))

    if max_ is None:
        max_ = float(np.amax(a))

    return (a - min_) / (max_ - min_)

if __name__ == "__main__":
    import doctest
    doctest.testmod()


# coding: utf-8

import numpy as np

def jacobi_method(A, b, eps=1e-5, max_steps=100, debug=False):
    """
    Solves a linear system Ax = b for x using the iterative jacobi method,
    to a requested error eps with at most max_steps steps.
    Turn on debug prints using debug=True.
    Returns the solution x. Raises a ValueError if the iteration doesn't converge.
    """

    cols = np.arange(A.shape[0])[None,:].repeat(A.shape[1], 0)
    rows = np.arange(A.shape[1])[:,None].repeat(A.shape[0], 1)

    L = (rows > cols) * A
    U = (rows < cols) * A
    D = np.diag(A)

    x = np.zeros_like(b)

    for i in range(max_steps):
        x =  (1.0 / D) * (b - (L + U) @ x)
        err = np.linalg.norm(A @ x - b)
        if debug:
            print("error:", err, "\nx:\n", x)
        if err <= eps:
            return x

    raise ValueError("Failed to converge.\neps = {}, max_steps = {}\nA:\n{}\nb:\n{}\nlast x:\n{}".format(eps, max_steps, A, b, x))

if __name__ == "__main__":

    A = np.array([[10., -1., 2., 0.],
                  [-1., 11., -1., 3.],
                  [2., -1., 10., -1.],
                  [0.0, 3., -1., 8.]])
    b = np.array([6., 25., -11., 15.])

    jacobi_x = jacobi_method(A, b, eps=0)
    numpy_x = np.linalg.solve(A, b)

    print("A:\n", A, "\nb:\n", b, "\nx using jacobi:\n", jacobi_x, "\nerror:", np.linalg.norm(jacobi_x - numpy_x))


import numpy as np
import random as rd
import matplotlib.pyplot as pl

# Load text file
file = open("list_of_engwords.txt", "r")
words = file.read().split('\n')

file.close()

# Task 1

# Manually add the two one-letter words
words.append("a")
words.append("i")

# Define information gained for each letter revealed as log_2 (N_b - N_m),
# where N_b and N_m are the number of words remaining before and after
# revealing the letter, given that all words are equally probable a priori.

def info_gain(word_pick):
    word_pick_list = list(word_pick)

    # Stores remaining words
    rem_words = words.copy()
    #rem_words.remove(word_pick)
    #print("Remaining num of words: ", len(rem_words))

    info_total = 0 # bits
    info_total_all = [0]

    for i in range(len(word_pick_list)):
        #print("Letter {0}: ".format(i+1), word_pick_list[i])

        no_rem_words_bef = len(rem_words)

        # Find remaning number of words after revealing letter i+1
        rem_word_ind = 0
        while rem_word_ind < len(rem_words):
            if i < len(list(rem_words[rem_word_ind])):
                # So that the word is longer than the one picked
                if list(rem_words[rem_word_ind])[i] == word_pick_list[i]:
                    # Same letter, remain
                    rem_word_ind += 1
                    continue
                else:
                    rem_words.remove(rem_words[rem_word_ind])
            else:
                rem_words.remove(rem_words[rem_word_ind])

        #print("Remaining num of words: ", len(rem_words))

        # Calculate info gained
        info_total += np.log2(no_rem_words_bef / len(rem_words))
        info_total_all.append(info_total)
        #print("Info gained from revealing letter {0}: ".format(i+1),
        #      np.log2(no_rem_words_bef / len(rem_words)), " bits")
        #print("Total amount of info gained after letter {0}: ".format(i+1),
        #      info_total, " bits")
        #print("Amount of info not revealed after letter {0}: ".format(i+1),
        #      np.log2(len(words)) - info_total, " bits")
    return info_total_all

# Part (d): Try wombat
#info_gain("wombat")

# Pick a word
#pick = rd.randint(0, len(words)-1)
#word_pick_rd = "versifying" # words[pick]
#info_gain(word_pick_rd)

# Part (e): Plot cumulative info gained
# Gather all words with 15 letters
fifteen_let = []
for word in words:
    if len(list(word)) == 15:
        fifteen_let.append(word)

pl.figure()
for wd in fifteen_let:
    cum_info = info_gain(wd)
    pl.plot(np.arange(0, 16), cum_info)

# Line of info gained revealing the word altogether, as comparison
pl.plot([0, 15.5], [np.log2(len(words)), np.log2(len(words))], "k--")
pl.xlabel("Letter revealed")
pl.ylabel("Cumulative information gained / bits")
pl.xticks(np.arange(0, 16))
pl.xlim(left=0)
pl.ylim(bottom=0)
pl.grid()


"""
Activation Functions
--------------------
"""

from abc import ABC, abstractmethod
import numpy as np

class Activator(ABC):
    """Activator interface (d = True for differentiation/backprop"""

    @abstractmethod
    def activation(self, x: np.ndarray, d: bool = False) -> np.ndarray:
        raise NotImplementedError("No activation method implemented.")

class Relu(Activator):
    """Compute rectified Linear Unit (relu) values for each sets of scores in x"""

    def activation(self, x: np.ndarray, d: bool = False) -> np.ndarray:
        if d:  # differentiation for backprop
            relu_x = np.heaviside(x, 1)
        else:
            relu_x = x
            relu_x[x < 0] = 0
        return relu_x

class Tanh(Activator):
    """Compute hyperbolic tan (tanh) values for each sets of scores in x"""

    def activation(self, x: np.ndarray, d: bool = False) -> np.ndarray:
        tanh_x = (2 / (1 + np.exp(-2 * x))) - 1
        if d:  # differentiation for backprop
            tanh_x = 1 - tanh_x ** 2
        return tanh_x

class Sigmoid(Activator):
    """Compute sigmoid values for each sets of scores in x"""

    def activation(self, x: np.ndarray, d: bool = False) -> np.ndarray:
        sigmoid_x = 1 / (1 + np.exp(-x))
        if d == True:
            sigmoid_x = sigmoid_x * (1 - sigmoid_x)
        return sigmoid_x

class Softmax(Activator):
    """Output function - Compute softmax values for each sets of scores in x"""

    def activation(self, x: np.ndarray, d: bool = False) -> np.ndarray:
        if d == True:
            raise NotImplementedError("Used for output layer only")
        sigmoid_x = np.exp(x)
        return sigmoid_x / sigmoid_x.sum()


import numpy as np

def calculate_distance(rA, rB):
    """
    calculate the distance between two points

    Parameters
    ----------
    rA, rB : np ndarray
        The coordinates of each point.

    Returns
    -------
    distance : float
        the distance of bla

    Examples
    --------
    >>> r1=np.array([0, 0, 0])
    >>> r2=np.array([3.0, 0, 0])
    >>> calculate_distance(r1, r2)
    3.0
    """

    # This function calculates the distance between two points given as numpy arrays.
    distance_vec=(rA-rB)
    dist=np.linalg.norm(distance_vec)
    return dist

def calculate_angle(rA, rB, rC, degrees=False):
    # Calculate the angle between three points. Answer is given in radians by default, but can be given in degrees
    # by setting degrees=True
    dist_AB = rB - rA
    dist_BC = rB - rC
    theta=np.arccos(np.dot(dist_AB, dist_BC)/(np.linalg.norm(dist_AB)*np.linalg.norm(dist_BC)))

    if degrees:
        return np.degrees(theta)
    else:
        return theta


# Here we will compute the Weighted Sum at Each Node (z) and Node Activation (a-sigmoid function)
# But will define small_network from Lab1_Artificial_Neural_Networks_Networks

import numpy as np

def intialize_network(n,n_hid,m,n_out):
    n_nodes_p=n
    network={}
    for layer in range(n_hid+1):
            if layer == n_hid:
                layer_name='output' # to define last layer as output layer
                n_nodes=n_out
            else:
                layer_name ='layer_{}'.format(layer+1) # to name each layer as layer 1, layer 2....
                n_nodes=m[layer]
            network[layer_name]={}
            for node in range(n_nodes):
                 node_name='node_{}'.format(node+1) # to define nodes for each layer
                 network[layer_name][node_name]={
                     'weights': np.around(np.random.uniform(size=n_nodes_p),decimals=2),
                     'bias': np.around(np.random.uniform(size=1),decimals=2) # define weights and bias for each node in each layer
                     }
            n_nodes_p=n_nodes
    return network

ns=5 #inputs
n_hids=3 #hidden layers
ms=[3,2,3] #nodes in each hidden layer
n_outs=1 #nodes in output layer

small_network=intialize_network(ns,n_hids,ms,n_outs)

## Computing weighted sum at each Node - we need inputs ,weights, biases

def compute_weighted_sum(inputs,weights,bias):
    return np.sum(inputs*weights)+bias

# Example print the weighted sum at layer 1 node 1 (first node at first hidden layer)

np.random.seed(12)
inputs = np.around(np.random.uniform(size=5), decimals=2) # random inputs

node_weights=small_network['layer_1']['node_1']['weights']
node_bias=small_network['layer_1']['node_1']['bias']

weighted_sum=compute_weighted_sum(inputs,node_weights,node_bias)

with open ("Lab1_ANN_Compute.txt",'a') as f:
    print('Inputs are: \n',inputs,file=f)
    print('The weighted sum at the first node in the hidden layer is {}'.format(np.around(weighted_sum[0], decimals=4)),file=f)

## Node activation (Sigmoid function)

def node_activation(weighted_sum):
    return 1.0/(1.0+np.exp(-1*weighted_sum))

# Example print the node activation at layer 1 node 1 (first node at first hidden layer)

node_a_output=node_activation(compute_weighted_sum(inputs,node_weights,node_bias))
with open ("Lab1_ANN_Compute.txt",'a') as f:
    print('The output at the first node in the hidden layer is {}'.format(np.around(node_a_output[0], decimals=4)),file=f)

## We define forward_propagate function to combine both calculations for outputs of all nodes of every hidden layer

def forward_propagate(network,inputs):
    layer_inputs=list(inputs)

    for layer in network:
        layer_data=network[layer]
        layer_outputs=[]

        for layer_node in layer_data:
            node_data=layer_data[layer_node]

            node_output = node_activation(compute_weighted_sum(layer_inputs, node_data['weights'], node_data['bias']))
            layer_outputs.append(np.around(node_output[0], decimals=4))

        if layer !='output':
            with open ("Lab1_ANN_Compute.txt",'a') as f:
                print('The outputs of the nodes in hidden layer number {} is {}'.format(layer.split('_')[1], layer_outputs),file=f)

        layer_inputs = layer_outputs

    network_predictions=layer_outputs
    return network_predictions

# Exercise - Print the predictions using forward_propagate function

predictions = forward_propagate(small_network, inputs)
with open ("Lab1_ANN_Compute.txt",'a') as f:
    print('The predicted value by the network for the given input is {}'.format(np.around(predictions[0], decimals=4)),file=f)

Introduction to Deep Learning & Neural Networks with Keras/Module1/Lab1_Artificial_Neural_Networks_Calculation.py


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jun  1 23:14:54 2019

@author: fietekrutein

A script for solving a QCQP, using the log-barrier interior point method with backtracking line search
"""

import numpy as np
import cvxpy as cp
import math

# functions as per example problem

def obj(x,Q,p,r): # original objective
    return ((0.5*np.dot(np.dot(x,Q),x.T) + np.dot(p,x.T) + r)[0][0])

def grad(x,Q,p): # original gradient
    return (np.dot(x,Q).T + p)

def hess(Q): # original hessian
    return (Q)

def logbar(x,Q,p,r): # log barrier objective
    return (eta * obj(x,Q,p,r) - np.log(x[0][0] - x[0][1] + 2)
            - np.log(-x[0][0]-3*x[0][1]+5) - np.log(-x[0][0]**2 - x[0][1]**2 + 2*x[0][1] + 1))

def gradg3(x): # third gradient
    return (2*x - np.array([[0,2]]).T)

# Interior point method

def InteriorPoint(x_init, Q, p, r, alpha, beta, eta, nu, l, tol):
    # fixed constraint gradients
    gradg1 = np.array([[-1,1]])
    gradg2 = np.array([[1,3]])
    hessg3 = np.array([[2,0],
                       [0,2]])
    lIter = 0
    xCurrent = x_init
    while True:
        lIter += 1
        stop = 0
        nIter = 0

        while (stop == 0):
            nIter +=1
            g1 = -xCurrent[0][0] + xCurrent[0][1] - 2 # constraint 1
            g2 = xCurrent[0][0] + 3*xCurrent[0][1] - 5 #constraint 2
            g3 = xCurrent[0][0]**2 + xCurrent[0][1]**2 - 2*xCurrent[0][1] - 1 #constraint 3
            # gradient and hessian of P3(eta)
            gradient = eta * grad(xCurrent,Q,p.T) + (1/-g1)*gradg1.T + (1/-g2)*gradg2.T + (1/-g3)*gradg3(xCurrent.T)
            hessian = eta * hess(Q) + (1/(-g1)**2)*np.matmul(gradg1.T, gradg1)
            + (1/(-g2)**2)*np.dot(gradg2.T, gradg2)
            + (1/(-g3)**2)*np.dot(gradg3(xCurrent).T, gradg3(xCurrent))
            + (1/-g3)*hessg3

            t = 1
            d = np.dot(np.linalg.inv(hessian),gradient)
            xNext = xCurrent - t*d.T

            # check whether log goes to infinity at evaluated t
            while math.isnan(logbar(xNext,Q,p,r)):
                t = 0.98 * t
                xNext = xCurrent - t*d.T
            # implement backtracking line search
            while logbar(xNext,Q,p,r) > (logbar(xCurrent,Q,p,r) + alpha*t*(np.dot(gradient.T,d).item(0))):
                t = beta * t
                xNext = xCurrent - t*d.T
            print("IP Iteration {}, Newton Iteration {}: {}".format(lIter, nIter, xNext))

            if np.linalg.norm((xCurrent - xNext),2) <= tol:
                stop = 1
                xStar = xNext
                break
            else:
                xCurrent = xNext

        xCurrent = xStar
        if (l/eta <= tol):
            print("Optimal value found at IP iteration {}: {}".format(lIter, xStar))
            break
        else:
            eta = nu*eta

    return(xCurrent)

# initialize problem parameters
alpha = 0.01
beta = 0.7
eta = 1
nu = 2
l = 3
x_init = np.array([[1,1]])
Q = np.array([[1,0],[0,1]])
p = np.array([[-2,-2]])
r = 0

# execute

InteriorPoint(x_init, Q, p, r, alpha, beta, eta, nu, l, tol=10e-5)

# cvxpy script for comparison

# Define and solve
n = 2
x = cp.Variable(n)
objective = cp.Minimize(0.5*cp.quad_form(x, Q) + p*x + r)
constraints = [-x[0]+ x[1] <= 2,
               x[0] + 3*x[1] <= 5,
               x[0]**2 + x[1]**2 -2*x[1] <= 1]
prob = cp.Problem(objective, constraints)
prob.solve()

result = prob.solve()
# The optimal value for x is stored in `x.value`.
print("The optimum is reached at: ", result)
print("The optimal values for x are: ", x.value)


import numpy as np
import matplotlib.pyplot as plt

def interp(ni, nj, N):
    '''
    Returns an interpolation function from bin ni to bin nj (been N the total number of bins) in polar coordinates
    See https://calculus7.org/2013/04/20/peanut-allergy-and-polar-interpolation/
    '''
    n1 = min(ni, nj)
    n2 = max(ni, nj)
    Nm = int(N/2-0.0001)
    if (n2-n1 < N/2):
        r = ((Nm+1-n2+n1)/(Nm+1))**(-2)
        delta = np.pi/N*(n2-n1)
        theta0 = 2*n1*np.pi/N+delta
    elif (n2-n1 > N/2):
        r = ((Nm+1+n2-n1-N)/(Nm+1))**(-2)
        delta = np.pi/N*(N-n2+n1)
        theta0 = 2*n1*np.pi/N - delta
    else:
        r = 1e10 #yeah, I don't know why this works :O
        delta = np.pi/N*(n2-n1)
        theta0 = 2*n1*np.pi/N+delta

    return lambda x: (r + (1-r)*np.sin(x-theta0)**2/np.sin(delta)**2)**(-0.5)

def cov2corr(cov):
    '''
    Computes the correltion matrix from the covariance matrix
    '''
    return np.diag((np.diag(cov))**(-0.5)) @ cov @ np.diag((np.diag(cov))**(-0.5))

def circlecorr(cov, R=10, maxlw=5, labels=None):
    '''
    Circular plot for the correlation matrix.
    The bins represent the variance of each variable,
    and the lines the correlation coefficient:
    the width of the line corresponds to the absolute value of the correlation coefficient.
    Blue lines indicate negative correlation,
    Red lines indicate positive correlation.

    Function arguments:
    - cov: Covariance matrix.
    - R: Radius of the circle that contains the base of the bins.
    - maxlw: Linewidth of the lines of correlation = +-1
    - labels: list of labels for each bin
    '''
    N = cov.shape[0]
    corr = cov2corr(cov)

    theta = np.linspace(0, 2*np.pi, N, endpoint=False)
    width = 2*np.pi/(N*1.2)
    ax = plt.subplot(111, polar= True)
    ax.set_xticks(np.linspace(0, 2*np.pi, 6, endpoint=False))
    if labels is not None:
        ax.set_xticklabels(labels)
    ax.set_yticklabels([])
    bars = ax.bar(theta, np.diag(cov), width=width, bottom=R)
    for i in range(N):
        for j in range(i+1, N):
            if (j-i <= N/2):
                lin =np.linspace( 2*i*np.pi/N, 2*j*np.pi/N, 100 )
            else:
                lin =np.linspace( 2*j*np.pi/N, 2*(i+N)*np.pi/N, 100 )
            int1 = interp(i,j,N)
            if corr[i,j] > 0:
                c = 'red'
            else:
                c = 'blue'
            plt.plot(lin, R*int1(lin), c=c, lw=maxlw*abs(corr[i,j]))