import math
import numpy as np
#-------------------------------------------------------------------------
'''
    Problem 2: User-based recommender systems
    In this problem, you will implement a version of the recommender system using user-based method.
    You could test the correctness of your code by typing `nosetests test2.py` in the terminal.
'''

#--------------------------
def cosine_similarity(RA, RB):
    '''
        compute the cosine similarity between user A and user B.
        The similarity values between users are measured by observing all the items which have been rated by BOTH users.
        If an item is only rated by one user, the item will not be involved in the similarity computation.
        You need to first remove all the items that are not rated by both users from RA and RB.
        If the two users don't share any item in their ratings, return 0. as the similarity.
        Then the cosine similarity is < RA, RB> / (|RA|* |RB|).
        Here <RA, RB> denotes the dot product of the two vectors (see here https://en.wikipedia.org/wiki/Dot_product).
        |RA| denotes the L-2 norm of the vector RA (see here for example: http://mathworld.wolfram.com/L2-Norm.html).
        For more details, see here https://en.wikipedia.org/wiki/Cosine_similarity.
        Input:
            RA: the ratings of user A, a float python vector of length m (the number of movies).
                If the rating is unknown, the number is 0. For example the vector can be like [0., 0., 2.0, 3.0, 0., 5.0]
            RB: the ratings of user B, a float python vector
                If the rating is unknown, the number is 0. For example the vector can be like [0., 0., 2.0, 3.0, 0., 5.0]
        Output:
            S: the cosine similarity between users A and B, a float scalar value between -1 and 1.
        Hint: you could use math.sqrt() to compute the square root of a number
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    # assuming cosine(RA, RB) = P/(sqrt(NA)*sqrt(NB))

    # loop through two lists

        # if both users rated the item

    # if the two user share no item in their ratings

        # compute cosine similarity on the shared items

    #########################################
    return S

#--------------------------
def find_users(R, i):
    '''
        find the all users who have rated the i-th movie.
        Input:
            R: the rating matrix, a float numpy matrix of shape m by n. Here m is the number of movies, n is the number of users.
                If a rating is unknown, the number is 0.
            i: the index of the i-th movie, an integer python scalar (Note: the index starts from 0)
        Output:
            idx: the indices of the users, a python list of integer values
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return idx

#--------------------------
def user_similarity(R, j, idx):
    '''
        compute the cosine similarity between a collection of users in idx list and the j-th user.
        Input:
            R: the rating matrix, a float numpy matrix of shape m by n. Here m is the number of movies, n is the number of users.
                If a rating is unknown, the number is 0.
            j: the index of the j-th user, an integer python scalar (Note: the index starts from 0)
            idx: a list of user indices, a python list of integer values
        Output:
            sim: the similarity between any user in idx list and user j, a python list of float values. It has the same length as idx.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return sim

#--------------------------
def user_based_prediction(R, i_movie, j_user, K=5):
    '''
        Compute a prediction of the rating of the j-th user on the i-th movie using user-based approach.
        First we take all the users who have rated the i-th movie, and compute their similarities to the target user j.
        If there is no user who has rated the i-th movie, predict 3.0 as the default rating.
        From these users, we pick top K similar users.
        If there are less than K users who has rated the i-th movie, use all these users.
        We weight the user's ratings on i-th movie by the similarity between that user and the target user.
        Finally, we rescale the prediction by the sum of similarities to get a reasonable value for the predicted rating.
        Input:
            R: the rating matrix, a float numpy matrix of shape m by n. Here m is the number of movies, n is the number of users.
                If the rating is unknown, the number is 0.
            i_movie: the index of the i-th movie, an integer python scalar
            j_user: the index of the j-th user, an integer python scalar
            K: the number of similar users to compute the weighted average rating.
        Output:
            p: the predicted rating of user j on movie i, a float scalar value between 1. and 5.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    # find all other users who have rated movie i.

    # if there are less than K users who have rated the movie, change K to the number of users

    # compute the similarity between all of these users with user j

    # compute the weighted average of the top K similar users to user j

    #########################################
    return p

#--------------------------
def compute_RMSE(ratings_pred, ratings_real):
    '''
        Compute the root of mean square error of the rating prediction.
        Input:
            ratings_pred: predicted ratings, a float python list
            ratings_real: real ratings, a float python list
        Output:
            RMSE: the root of mean squared error of the predicted rating, a float scalar.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return RMSE

#--------------------------
def load_rating_matrix(filename = 'movielens_train.csv'):
    '''
        Load the rating matrix from a CSV file.  In the CSV file, each line represents (user id, movie id, rating).
        Note the ids start from 1 in this dataset.
        Input:
            filename: the file name of a CSV file, a string
        Output:
            R: the rating matrix, a float numpy array of shape m by n. Here m is the number of movies, n is the number of users.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return R

#--------------------------
def load_test_data(filename = 'movielens_test.csv'):
    '''
        Load the test data from a CSV file.  In the CSV file, each line represents (user id, movie id, rating).
        Note the ids in the CSV file start from 1. But the indices in u_ids and m_ids start from 0.
        Input:
            filename: the file name of a CSV file, a string
        Output:
            m_ids: the list of movie ids, an integer python list of length n. Here n is the number of lines in the test file. (Note indice should start from 0)
            u_ids: the list of user ids, an integer python list of length n.
            ratings: the list of ratings, a float python list of length n.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return m_ids, u_ids, ratings

#--------------------------
def movielens_user_based(train_file='movielens_train.csv', test_file ='movielens_test.csv', K = 5):
    '''
        Compute movie ratings in movielens dataset. Based upon the training ratings, predict all values in test pairs (movie-user pair).
        In the training file, each line represents (user id, movie id, rating).
        Note the ids start from 1 in this dataset.
        Input:
            train_file: the train file of the dataset, a string.
            test_file: the test file of the dataset, a string.
            K: the number of similar users to compute the weighted average rating.
        Output:
            RMSE: the root of mean squared error of the predicted rating, a float scalar.
    Note: this function may take 1-5 minutes to run.
    '''

    # load training set
    R = load_rating_matrix(train_file)

    # load test set
    m_ids, u_ids,ratings_real = load_test_data(test_file)

    # predict on test set
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    # compute RMSE
    RMSE = compute_RMSE(ratings_pred,ratings_real)
    return  RMSE


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

# https://www.mathportal.org/calculators/statistics-calculator/standard-deviation-calculator.php

import numpy as np

def print_this_matrix(given_matrix):
  for i in range(len(given_matrix)):
   for j in range(len(given_matrix[i])):
    print (given_matrix[i][j]," ",end="")
   print()
  print("\n")

arr_extreme = np.array([[ 4,   5,  6,   7],
                        [ 3,  -1, -2,  -3],
                        [ 8,   9,  10,  11],
                        [12,   13, 14,  15]])
print("np.ptp() -- difference between max and min")
print("np.ptp(arr_extreme)                     --->",np.ptp(arr_extreme))
print("np.ptp(arr_extreme, axis=0)--vertical   --->",np.ptp(arr_extreme, axis=0))
print("np.ptp(arr_extreme, axis=1)--horizontal --->",np.ptp(arr_extreme, axis=1))

print("np.percentile() -- percentitle of sum of given range of values")
print("np.percentile(arr_extreme)                     --->",np.percentile(arr_extreme,10))        # 10% of the total values
print("np.percentile(arr_extreme, axis=0)--vertical   --->",np.percentile(arr_extreme,10,axis=0)) # 10% of the total values
print("np.percentile(arr_extreme, axis=1)--horizontal --->",np.percentile(arr_extreme,10,axis=1)) # 10% of the total values

print("np.mean() -- mean = 50% percentile")
print("np.mean(arr_extreme)                     --->",np.mean(arr_extreme))
print("np.mean(arr_extreme, axis=0)--vertical   --->",np.mean(arr_extreme, axis=0))
print("np.mean(arr_extreme, axis=1)--horizontal --->",np.mean(arr_extreme, axis=1))

print("np.median() -- middle number or avg of the middle 2 numbers")
print("np.median(arr_extreme)                     --->",np.median(arr_extreme))
print("np.median(arr_extreme, axis=0)--vertical   --->",np.median(arr_extreme, axis=0))
print("np.median(arr_extreme, axis=1)--horizontal --->",np.median(arr_extreme, axis=1))

print("np.average(arr_extreme)                     --->",np.average(arr_extreme))
print("np.std(arr_extreme)    standard deviation   --->",np.std(arr_extreme))
print("np.var(arr_extreme)    variance             --->",np.var(arr_extreme))
print("\n")

arr_extreme = np.array([[ 4,   5,  6,   7],
                        [ 3,  -1, -2,  -3],
                        [ 8,   9,  10,  11],
                        [12,   13, 14,  15]])
print_this_matrix(arr_extreme)
print("np.amin(arr_extreme)                    --->",np.amin(arr_extreme))
print("np.amin(arr_extreme, axis=0)--vertical  --->",np.amin(arr_extreme, axis=0))
print("np.amin(arr_extreme, axis=1)--horizontal--->",np.amin(arr_extreme, axis=1))
print("just index of sorted element")
arr_extreme.flatten()
print("np.argmin(arr_extreme, axis=1)--horizontal--->",np.argmin(arr_extreme, axis=1))

from scipy import stats

arr_extreme = np.array([[ 4,   5,  6,   7],
                        [ 3,  -1, -2,  -3],
                        [ 8,   9,  10,  11],
                        [12,   13, 14,  15]])

stats.describe(arr_extreme)


import numpy as np

def riemann_sum(f, a, b, N: int, method='midpoint'):
    '''
    Compute the Riemann sum of f(x) over the interval [a,b].

    Parameters
    ----------
    f : function
        Vectorized function of one variable
    a , b : numbers
        Endpoints of the interval [a,b]
    N : integer
        Number of subintervals of equal length in the partition of [a,b]
    method : string
        Determines the kind of Riemann sum:
        right : Riemann sum using right endpoints
        left : Riemann sum using left endpoints
        midpoint (default) : Riemann sum using midpoints

    Returns
    -------
    float
        Approximation of the integral given by the Riemann sum.
    '''
    dx = (b - a)/N
    x = np.linspace(a, b, N+1)

    # left riemann sum
    if method == 'left':
        x_left = x[:-1]
        return np.sum(f(x_left)*dx)

    # right reimann sum
    elif method == 'right':
        x_right = x[1:]
        return np.sum(f(x_right)*dx)

    # midpoint reimann sum
    elif method == 'midpoint':
        x_mid = (x[:-1] + x[1:])/2
        return np.sum(f(x_mid)*dx)
    else:
        raise ValueError("Method must be 'left', 'right' or 'midpoint'.")

# example problems

# 1
f = np.sin
r_sum = riemann_sum(f, 0, np.pi/2, 100)
right_r_sum = riemann_sum(f, 0, np.pi/2, 100, 'right')
left_r_sum = riemann_sum(f, 0, np.pi/2, 100, 'left')
print(f"""Sin(x) in the interval [0,pi/2] partition with 100 sub-intervals
Left Riemann Sum: {left_r_sum}
Midpoint Riemann Sum: {r_sum}
Right Riemann Sum: {right_r_sum}""")

# 2
f = lambda x : 1 / (1 + x**2)
r_sum = riemann_sum(f, 0, 5, 10)
right_r_sum = riemann_sum(f, 0, 5, 10, 'right')
left_r_sum = riemann_sum(f, 0, 5, 10, 'left')
print(f"""
1/(1+x^2) in the interval [0,5] partition with 10 sub-intervals
Left Riemann Sum: {left_r_sum}
Midpoint Riemann Sum: {r_sum}
Right Riemann Sum: {right_r_sum}""")


import numpy as np
import math

def poisson_lambda_mle(d):
    """
    Computes the Maximum Likelihood Estimate for a given 1D training
    dataset from a Poisson distribution.

    """
    return sum(d) / len(d)

def likelihood_poisson(x, lam):
    """
    Computes the class-conditional probability for an univariate
    Poisson distribution

    """
    if x // 1 != x:
        likelihood = 0
    else:
        likelihood = math.e**(-lam) * lam**(x) / math.factorial(x)
    return likelihood

if __name__ == "__main__":

    # Plot Probability Density Function
    from matplotlib import pyplot as plt

    training_data = [0, 1, 1, 3, 1, 0, 1, 2, 1, 2, 2, 1, 2, 0, 1, 4]
    mle_poiss =  poisson_lambda_mle(training_data)
    true_param = 1.0

    x_range = np.arange(0, 5, 0.1)
    y_true = [likelihood_poisson(x, true_param) for x in x_range]
    y_mle = [likelihood_poisson(x, mle_poiss) for x in x_range]

    plt.figure(figsize=(10,8))
    plt.plot(x_range, y_true, lw=2, alpha=0.5, linestyle='--', label='true parameter ($\lambda={}$)'.format(true_param))
    plt.plot(x_range, y_mle, lw=2, alpha=0.5, label='MLE ($\lambda={}$)'.format(mle_poiss))
    plt.title('Poisson probability density function for the true and estimated parameters')
    plt.ylabel('p(x|theta)')
    plt.xlim([-1,5])
    plt.xlabel('random variable x')
    plt.legend()

    plt.show()


import numpy as np
import math
from math import sqrt
#float_percision = '{:.4f}'.format
#np.set_printoptions(formatter={'float_kind':float_percision})

def decomp_cholesky(A):

    n = len(A)
    L = np.zeros((n, n))

    for i in range(n):

        for j in range(i+1):

            L[i][j] = A[i][j]

            for k in range(j):

                L[i][j] -= L[i][k]*L[j][k]

            if(i == j):

                L[i][j] = sqrt(L[j][j])

            else:

                L[i][j] /= L[j][j]

    return L

def solve_lower(L, b):

    n = len(L)
    y = np.copy(b)

    y[0] = b[0] / L[0][0]

    for i in range(1, n):

        for j in range(i):

            y[i] -= L[i][j]*y[j]

        y[i] /= L[i][i]

    return y

def solve_upper(U, y):

    n = len(U)
    x = np.copy(y)

    for i in range(n-1, -1, -1):

        for j in range(i+1, n):

            x[i] -= U[j][i]*x[j]

        x[i] /= U[i][i]

    return x

def solve_cholesky(L,b):

    y = solve_lower(L,b)
    x = solve_upper(L,y)

    return x

if __name__ == '__main__':

	N = 1000
	print('N = ', N)

	#Filling N*N array to initialize it
	A1 = np.zeros((N,N), float)
	A2 = np.zeros((N,N), float)

	b1 = np.zeros(N, float)
	b2 = np.ones(N, float)

	#Fill arrays with the correspondant values
	np.fill_diagonal(A1, 6)
	np.fill_diagonal(A1[1:], -4)
	np.fill_diagonal(A1[:, 1:], -4)
	np.fill_diagonal(A1[2:], 1)
	np.fill_diagonal(A1[:, 2:], 1)

	np.fill_diagonal(A2, 7)
	np.fill_diagonal(A2[1:], -4)
	np.fill_diagonal(A2[:, 1:], -4)
	np.fill_diagonal(A2[2:], 1)
	np.fill_diagonal(A2[:, 2:], 1)

	b1[0] = 3
	b1[1] = -1
	b1[-2] = -1
	b1[-1] = 3

	b2[0] = 4
	b2[1] = 0
	b2[-2] = 0
	b2[-1] = 4

	L = decomp_cholesky(A1)
	x = solve_cholesky(L, b1)

	print('A1 x = b1 \n Ten median x are:')

	ml = len(x) // 2 - 5
	mu = len(x) // 2 + 5

	print(x[ml : mu])

	L = decomp_cholesky(A2)
	x = solve_cholesky(L, b2)

	print('A2 x = b2 \n Ten median x are:')

	ml = len(x) // 2 - 5
	mu = len(x) // 2 + 5

	print(x[ml : mu])


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import numpy as np
import csv
from numpy import cov
from numpy.linalg import eig
import matplotlib.pyplot as plt

def PCA(filename = "",topk = 0, graph=True):

	if len(filename) == 0:
		print("Error: File not provided!")
		return

	data = []
	label = []
	with open(filename) as datafile:
		reader = csv.reader(datafile,delimiter=",")
		next(reader)
		for row in reader:

			# data.append(list(map(float,row[2:])))
			float_row = []
			for value in row[2:]:
				try:
					value = float(value)
					float_row.append(value)

				except ValueError:
					float_row.append(0.0)
			data.append(float_row)
			label.append(row[1])

	data_mat = np.asarray(data)

	totalDims = data_mat.shape[-1]

	if topk > totalDims or topk < 0:
		print("Invalid value for topk!")
		return

	centered_mat = data_mat - np.mean(data_mat.T, axis=1)

	values, vectors = eig(cov(centered_mat.T))
	indices = np.argpartition(values, -topk)[-topk:]

	max_variance_vectors = []
	for index in indices:
		max_variance_vectors.append(vectors[index])

	max_variance_vectors = np.asarray(max_variance_vectors)
	projected_mat = max_variance_vectors.dot(centered_mat.T)

	if graph:
		fig = plt.figure()
		fig.suptitle('PCA on '+filename, fontsize=14, fontweight='bold')
		ax = fig.add_subplot(111)
		ax.set_xlabel("PC1")
		ax.set_ylabel("PC2")
		ax.scatter(projected_mat[0],projected_mat[1])
		# for i, txt in enumerate(label):
		# 	ax.annotate(txt, (projected_mat[0], projected_mat[1]))
		plt.show()

	return projected_mat.T


import numpy as np
from scipy.stats import multivariate_normal
import pdb

class MultivariateGaussianData:
    """ Class for generating data from a multivariate Gaussian distibution
    (Z, X) ~ N(mu, Sigma), where Z is considered the latent variable and X the observation,
    such that Z ~ N(0, I) and X|Z=z ~ N(Wz + b, Sigma_x)
    ------------------
    Parameters:
              dim_x : dimension of X
              dim_z : dimension of Z
    ------------------
    Methods:
            create_dataset : generate n_samples observations X ~ N(mu_x, Sigma_x)
            compute_posterior : return p(Z|X=x), mean and covariance of Z|X=x
            compute_log_likelihood : return mean(log(p(x))) across the given dataset
    """

    def __init__(self, dim_x, dim_z):
        np.random.seed(0)
        self.n_x = dim_x
        self.n_z = dim_z

        # z ~ N(0, I):
        self.sigma_z = np.eye(dim_z)
        self.mu_z = np.zeros(dim_z)

        # X|Z=z ~ N(Wz+b, Sigma_x_z)
        self.W = np.random.normal(0, 1, size=(dim_x, dim_z))
        self.b = np.random.normal(0, 1, dim_x)
        aux_sigma = np.random.normal(0, 1, size=(dim_x, dim_x))
        self.sigma_x_z = np.dot(aux_sigma, aux_sigma.T) + 0.5 * np.eye(dim_x)

        # Cholesky factor of the covariance of the observed variable X (needed for sampling)
        self.chol_sigma_x_z = np.linalg.cholesky(self.sigma_x_z)

        # X + N(b, W*W^T + Sigma_x, z)
        self.sigma_x = np.dot(self.W, self.W.T) + self.sigma_x_z

    def create_dataset(self, n_samples):
        """ Sample n_samples from X ~ N(mu_x, Sigma_x), generated by first sampling from Z
        """
        z = np.random.normal(0, 1, size=(n_samples, self.n_z))
        eps = np.random.normal(0, 1, size=(n_samples, self.n_x))
        data = np.dot(z, self.W.T) + self.b + np.dot(eps, self.chol_sigma_x_z)
        return data

    def compute_posterior(self, x, z):
        """ Compute the posterior and its parameters: p(z|x), mu_z_x, sigma_z_x
        """
        solver = np.linalg.solve(self.sigma_x, x - self.b)
        mu_z_x = self.mu_z + np.dot(self.W.T, solver)
        sigma_z_x = self.sigma_z - np.dot(self.W.T, np.dot(np.linalg.inv(self.sigma_x), self.W))
        pdf = multivariate_normal.pdf(z, mu_z_x, sigma_z_x)
        return pdf, mu_z_x, sigma_z_x

    def compute_log_likelihood(self, x):
        """ True log likelihood of the data
        """
        p_x = multivariate_normal.pdf(x, self.b, self.sigma_x)
        return np.mean(np.log(p_x))

def load(n_x, n_z, n_samples_train, n_samples_test):
    data = {}
    multivarGaussData = MultivariateGaussianData(n_x, n_z)
    generatedX = multivarGaussData.create_dataset(n_samples_train + n_samples_test)
    data["gaussian_class"] = multivarGaussData
    data["train_set_x"] = generatedX[:n_samples_train]
    data["test_set_x"] = generatedX[n_samples_train:]
    data["n_samples_train"] = n_samples_train
    data["n_samples_test"] = n_samples_test
    data["input_size"] = n_x
    data["binary"] = 0
    return data

if __name__=='__main__':
    opts = {"n_x": 10,
            "n_z": 2,
            "n_train": 500,
            "n_test" : 50,
            "n_samples": 5,
            "n_samples_is": 10,
            "n_epochs": 100,
            "period" : 10,
            "alpha": 0.9,
            "learning_rate": 0.0001}

    data = load(opts["n_x"], opts["n_z"], opts["n_train"], opts["n_test"])
    # pdb.set_trace()


import numpy as np

def sigmoid(z):
    return 1.0/(1.0 + np.exp(-z))

def sigmoid_prime(z):
    return sigmoid(z)*(1-sigmoid(z))

def tanh(z):
    return ((2 / (1 + np.exp(-2*z))) + 1)

def tanh_prime(z):
    return 1 - tanh(z)**2

def relu(z):
    def comp(x):
        if (x > 0):
            return x
        else:
            return 0
    return np.array([comp(i) for i in z])

def relu_prime(z):
    def comp(x):
        if (x <= 0):
             return 0
        elif (x > 0):
            return 1

    return np.array([comp(i) for i in z])

def leaky_relu(z):
    def comp(x):
        if (x < 0):
            return 0.1*x
        elif (x >= 0):
            return x
    return np.array([comp(i) for i in z])

def leaky_relu_prime(z):
    def comp(x):
        if (x < 0):
            return 0.1
        elif (x >= 0):
            return 1
    return np.array([comp(i) for i in z])

def elu(z):
    alpha = 1
    def comp(x):
        if (x > 0):
            return x
        elif (x <= 0):
            return alpha*(np.exp(x) - 1)
    return np.array([comp(i) for i in z])

def elu_prime(z):
    alpha = 1
    def comp(x):
        if (x > 0):
            return 1
        elif (x <= 0):
            return alpha*np.exp(x)
    return np.array([comp(i) for i in z])

def softmax(z):
    return np.exp(z)/sum(np.exp(z))

def softmax_prime(z):
    #this is the diagonal of the jacobian.
    return softmax(z)*(1-softmax(z))

def softmax_input_change(z):
    dz_da = np.zeros((z.shape[0], z.shape[0]))
    for i in range(len(z)):
        tmp = []
        if (z[i] == 0):
            for j in z:
                if (j == 0):
                    tmp.append(0)
                else:
                    tmp.append(-1)
        else:
            for j in range(len(z)):
                if (i == j):
                    tmp.append(1)
                else:
                    tmp.append(0)

        dz_da[:,i] += np.array(tmp)

    return dz_da.diagonal()

def func_dict(order):
    if (order == 0):
        dict = {
            "sigmoid": sigmoid,
            "relu": relu,
            "l_relu": leaky_relu,
            "tanh": tanh,
            "elu": elu,
        }
    elif (order == 1):
        dict = {
            "sigmoid": sigmoid_prime,
            "relu": relu_prime,
            "l_relu": leaky_relu_prime,
            "tanh": tanh_prime,
            "elu": elu_prime,
            "softmax": softmax_prime,
        }
    return dict


# -*- coding: utf-8 -*-
"""
Created on Wed Sep  5 22:11:24 2018

@author: chen jin

Non-Linear Least-Squares Fitting: solve a curve fitting problem using robust
loss function to take care of outliers in the data. Define the model function
as y = a + b * exp(c * x), where t is a predictor variable, y is an observation
and a, b, c are parameters to estimate.
Put all coefficients in beta, this could be write as:
    y = beta[0] + beta[1] * exp(beta[2] * x)

Input:
    target function: y = a + b * np.exp(x * c)
    x_train linspace generated
    y_train generated with noise and outliers
    par: parameters (a, b, c) of target function
    loss_fun: loss or residual function (simple f(x) - y used here)
    beta: initial guess of parameters (a, b, c)

Output:
    res_lsq: standard least-squares solution with beta = [*res_lsq.x]
    res_soft_l1: robust soft_11 solution with beta = [*res_soft_l1.x]
    res_log: robust cauchy solution with beta = [*res_log.x]
"""

import numpy as np
from scipy.optimize import least_squares
import matplotlib.pyplot as plt

# First, define the function which generates the data with noise and outliers,
# define the model parameters with default setting in par

def gen_data(x, par = [0.5, 2, -1], noise=0, n_outliers=0, random_state=0):

    a, b, c = par[0], par[1], par[2]
    y = a + b * np.exp(x * c)

    rnd = np.random.RandomState(random_state)
    error = noise * rnd.randn(x.size)
    outliers = rnd.randint(0, x.size, n_outliers)
    error[outliers] *= 10

    return y + error

# generate data
x_train = np.linspace(0, 10, 15)
par = [0.5, 2, -1]
y_train = gen_data(x_train, par, noise=0.1, n_outliers=3)

# Define function for computing Loss (residuals)
def loss_fun(beta, x, y):
    return beta[0] + beta[1] * np.exp(beta[2] * x) - y

# initial estimate of parameters
beta = np.array([1.0, 1.0, 0.0])

# Compute a standard least-squares solution
res_lsq = least_squares(loss_fun, beta, args=(x_train, y_train))

# compute two solutions with two different robust loss functions.
# The parameter f_scale is set to 0.1, meaning that inlier residuals
# should not significantly exceed 0.1 (the noise level used)

res_soft_l1 = least_squares(loss_fun, beta, loss='soft_l1', f_scale=0.1,
                            args=(x_train, y_train))
res_log = least_squares(loss_fun, beta, loss='cauchy', f_scale=0.1,
                        args=(x_train, y_train))

# plot all curves
x_test = np.linspace(0, 10, 15 * 10)
y_true = gen_data(x_test, par)
y_lsq = gen_data(x_test, [*res_lsq.x])
y_soft_l1 = gen_data(x_test, [*res_soft_l1.x])
y_log = gen_data(x_test, [*res_log.x])

plt.plot(x_train, y_train, 'o')
plt.plot(x_test, y_true, 'k', linewidth=2, label='true')
plt.plot(x_test, y_lsq, label='linear loss')
plt.plot(x_test, y_soft_l1, label='soft_l1 loss')
plt.plot(x_test, y_log, label='cauchy loss')
plt.xlabel("t")
plt.ylabel("y")
plt.legend()
plt.show()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Realisation of Multivariate Linear Regression powered by pure Numpy.
Inspired by Machine Learning class from Andrew Ng at Coursera.
@see https://www.coursera.org/learn/machine-learning

FAQ:
- Linear Regression model looks like: y = theta0 + theta1*x1 + theta2*x2 ... thetan*xn
- We try to find theta parameters
- We use Gradient Descent for that
- It is very handful do np.seterr(all='raise') before using this module, to stop on any errors and avoid further
calculations that already wrong
"""
import numpy as np

def compute_cost(x: np.ndarray, y: np.ndarray, theta: np.ndarray) -> float:
    """Calculate and return cost function value (float scalar), based on given training set x, expected outcomes y and
    theta

    If x.shape = (m, n), then y.shape must be (m, 1) and theta.shape must be (1, n)

    :param x: set of features
    :param y: set of training examples
    :param theta: parameters of linear regression function
    """
    m = len(y)  # number of training examples
    return np.ndarray.item(sum(np.square(x @ theta - y)) / (2 * m))

def gradient_descent(x: np.ndarray, y: np.ndarray, theta: np.ndarray, alpha: float, num_iters: int) \
        -> (np.ndarray, np.ndarray):
    """Straitforward (unoptimized) version of Gradient Descent. Applies num_iters iterations with learning rate alpha
    and returns tuple (theta, J_history) with finded theta and history of values of the cost function J_history

    If x.shape = (m, n), then y.shape must be (m, 1) and theta.shape must be (1, n)

    :param x: set of features
    :param y: set of training examples
    :param theta: parameters of linear regression function
    :param alpha: learning rate alpha (size of the one step of gradient descent)
    :param num_iters: number of iterations for gradient descent
    :return: a tuple (theta, J_historu), where theta is sought-for parameter of Linear Regression Model,
        J_history - is an array of cost function values over iterations
    """
    m = len(y)  # number of training examples
    n = x.shape[1]  # number of features
    j_history = np.zeros(shape=(num_iters, 1))
    for i in range(num_iters):
        for j in range(n):
            # np.multiply is the element-wise multiplication
            theta[j, 0] = theta[j, 0] - alpha * sum(np.multiply(x @ theta - y, x[:, j].reshape(m, 1))) / m
        j_history[i] = compute_cost(x, y, theta)
    return theta, j_history

def gd(x: np.ndarray, y: np.ndarray, theta: np.ndarray, alpha: float, num_iters: int) \
        -> (np.ndarray, np.ndarray):
    """Optimized version of gradient_descent function. Parameters and return is equal with gradient_descent()"""
    m = len(y)  # number of training examples
    n = x.shape[1]  # number of features
    alpha_m = alpha / m  # constant formula part, calculate outside of loops
    j_history = np.zeros(shape=(num_iters, 1))
    for i in range(num_iters):
        difference = x @ theta - y
        for j in range(n):
            # np.multiply is the element-wise multiplication
            theta[j, 0] = theta[j, 0] - alpha_m * sum(np.multiply(difference, x[:, j].reshape(m, 1)))
        j_history[i] = np.ndarray.item(sum(np.square(difference)) / (2 * m))  # use precalculated difference
    return theta, j_history


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

import numpy as np
import imageio
import matplotlib.pyplot as plt
import timeit

#%%
#<start:svd>
def compress_image(A,k):
    u,s,vt = np.linalg.svd(A, full_matrices=False)

    return (u[:,:k]*s[:k])@vt[:k]
#<end:svd>
#%%
#<start:approx_svd>
def col_sample_compress_image(A,k,eps):
    m,n = np.shape(A)

    column_norms = np.linalg.norm(A,axis=0)

    column_probs = column_norms**2
    column_probs /= np.sum(column_probs)

    s = int(k/eps**2)

    indices = np.random.choice(np.arange(n), size=s, p=column_probs)

    X = A[:,indices]

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

    return u[:,:k]@(u[:,:k].T@A)
#<end:approx_svd>

#%%
#<start:approx_qr>
def gaus_sample_compress_image(A,k):
    m,n = np.shape(A)

    X = A@np.random.randn(n,k)
    u,s,vt = np.linalg.svd(X, full_matrices=False)

    return u[:,:k]@(u[:,:k].T@A)
#<end:approx_qr>

#%%
A = imageio.imread('einstein.jpg')[:,:,0]
K = np.logspace(0,2.7,8,dtype='int')
for i,k in enumerate(K):
    A_k = compress_image(A,k)
    plt.imsave('img/'+str(k)+'.png', A_k[::6,::6], format='png')

    A_k_col = col_sample_compress_image(A,k,1)
    plt.imsave('img/'+str(k)+'_fast.png', A_k_col[::6,::6], format='png')

    A_k_gaus = gaus_sample_compress_image(A,k)
    plt.imsave('img/'+str(k)+'_faster.png', A_k_gaus[::6,::6], format='png')

#%%

N = 1

K = np.logspace(0,2.7,8,dtype='int')
setup = 'from __main__ import A, compress_image'
setup_col = 'from __main__ import A, col_sample_compress_image'
setup_gaus = 'from __main__ import A, gaus_sample_compress_image'

t = np.zeros(len(K))
t_col = np.zeros(len(K))
t_gaus = np.zeros(len(K))

for i,k in enumerate(K):
    t[i] = timeit.timeit('compress_image(A,'+str(k)+')', number=N, setup=setup)/N
    t_col[i] = timeit.timeit('col_sample_compress_image(A,'+str(k)+',1)', number=N, setup=setup_col)/N
    t_gaus[i] = timeit.timeit('gaus_sample_compress_image(A,'+str(k)+')', number=N, setup=setup_gaus)/N

#%%
plt.figure(figsize=(10,6))
plt.scatter(K,t,label='exact')
plt.scatter(K,t_col,label='column sampling')
plt.scatter(K,t_gaus,label='gaussian sampling')

plt.xlabel('$k$')
plt.ylabel('time (s)')
plt.legend()

plt.xscale('log')
plt.yscale('log')
plt.savefig('img/times.pdf')


"""
floor
The tool floor returns the floor of the input element-wise.
The floor of  is the largest integer  where .

import numpy

my_array = numpy.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9])
print numpy.floor(my_array)         #[ 1.  2.  3.  4.  5.  6.  7.  8.  9.]
ceil
The tool ceil returns the ceiling of the input element-wise.
The ceiling of  is the smallest integer  where .

import numpy

my_array = numpy.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9])
print numpy.ceil(my_array)          #[  2.   3.   4.   5.   6.   7.   8.   9.  10.]
rint
The rint tool rounds to the nearest integer of input element-wise.

import numpy

my_array = numpy.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9])
print numpy.rint(my_array)          #[  1.   2.   3.   4.   6.   7.   8.   9.  10.]
Task
You are given a 1-D array, . Your task is to print the ,  and  of all the elements of .

Note
In order to get the correct output format, add the line  below the numpy import.

Input Format

A single line of input containing the space separated elements of array .

Output Format

On the first line, print the  of A.
On the second line, print the  of A.
On the third line, print the  of A.

Sample Input

1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9
Sample Output

[ 1.  2.  3.  4.  5.  6.  7.  8.  9.]
[  2.   3.   4.   5.   6.   7.   8.   9.  10.]
[  1.   2.   3.   4.   6.   7.   8.   9.  10.]
"""

import numpy as np

np.set_printoptions(legacy="1.13")

arr = np.array(list(map(float, input().split())), float)
print(np.floor(arr))
print(np.ceil(arr))
print(np.rint(arr))


import numpy as np
from scipy.sparse import diags
import pandas as pd

# We consider that matrix A can be written as A=L*L^T.
# Here we define the function responsible for finding the L matrix.

def Cholesky(A):
    A=np.array(A, float)
    L=np.zeros_like(A)
    n,_=np.shape(A)

    for i in range(n):

        L[i,i]=np.sqrt(A[i,i]-L[i,i] ** 2)

        sum = L[i,i-1] * L[i-1,i-1] + L[i,i] * L[i-1,i]
        L[i,i-1] = (A[i,i-1]-sum) / L[i-1,i-1]

    return L

# Now we define a function that solves our system using firstly forward substition and then backward
# It uses the matrix given from the first function, its transpose and B from Ax=B and provides the solution x

def solver(L,U,b):
  L=np.array(L, float)
  U=np.array(U, float)
  b=np.array(b, float)

  n,_=np.shape(L)
  y=np.zeros(n)
  x=np.zeros(n)

# Forward substitution
  for i in range(n):
    sumj=0
    for j in range(i):
      sumj += L[i,j]*y[j]
    y[i]=(b[i]-sumj)/L[i,i]

# Backward substitution
  for i in range(n-1, -1, -1):
    sumj=0
    for j in range(i+1,n):
      sumj += U[i,j] * x[j]
    x[i]=(y[i]-sumj)/U[i,i]
  return x

# Finally, we constract matrix A (500x500)

a = [1+i for i in range(2, 501)]
b = [1000+i for i in range(1, 501)]
c = [1+i for i in range(2, 501)]
offset = [-1,0,1]
matrix = np.array([a, b, c])
A = diags(matrix, offset).toarray()

# And then B (500x1)

B = [1+i for i in range(1,501)]

l = Cholesky(A)
U = np.transpose(l)
x = solver(l, U, B)

# Now, we use the built-in function for Cholesky Decomposition to compare our results

from numpy.linalg import cholesky

L = cholesky(A)
U = np.transpose(L)
x = solver(L, U, B)


# IMport the necessary modules
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# In this short script, we will see how Stirling's approximation can be
# useful for approximating very large numbers. For example, computing 200! is
# very difficult because that is beyond the basic precision of a 64-bit
# computer. We can do better by converting the numbers to something called
# 'arbitrary precision', but it is often sufficient to perform approximations
# by hand.

# As a reminder, Stirling's approximation is
#
#     log(n!) = n * log(n) - n
#
# We'll show this by computing the log of the factorial for large numbers
# and show it along with the approximation.

# We'll first set upa range of n to compute.
n_array = np.arange(0, 150, 1)

# Now we'll compute the factorial and store the log.
log_factorial = []
for n in n_array:
    n_factorial = float(np.math.factorial(n))   # Converts to a float for precision
    log_factorial.append(np.log(n_factorial))

# Now we can compute our approximation.
stirlings_approx = n_array * np.log(n_array) - n_array

# Now let's plot the log factorial and our approximation together.
plt.figure()
plt.plot(n_array, log_factorial, label='factorial')
plt.plot(n_array, stirlings_approx, label='approximation')
plt.xlabel('$n$')
plt.ylabel('$n!$')
plt.legend()
plt.show()

# We see that as n becomes larger and larger, our approximation gets even
# better.


"""
  Name     : c12_10_simulate_pi.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

import scipy as sp
n=100000
x=sp.random.uniform(low=0,high=1,size=n)
y=sp.random.uniform(low=0,high=1,size=n)
dist=sp.sqrt(x**2+y**2)
in_circle=dist[dist<=1]
our_pi=len(in_circle)*4./n
print ('pi=',our_pi)
print('error (%)=', (our_pi-sp.pi)/sp.pi)


import numpy as np
import pandas as pd
import time, gc

pi = np.pi
cos = np.cos
sin = np.sin
acos = np.arccos
degrees = np.degrees
radians = np.radians

def angularDistance(row, col_names):
    '''
    Computes the angular distance (in degrees) between two points on the celestial
    sphere with a given right-ascension and declination values

    <Formula> - http://spiff.rit.edu/classes/phys373/lectures/radec/radec.html

    Parameters
    ----------
    row : pd.Dataframe - series
        Input right-ascension in (hrs) and declination in (degrees) format

    col_names: list of strings
        The names of the columns on which the function will be applied
        ### SHOULD FOLLOW THIS CONVENTION
        c1 = right-ascension_1; c2 = right-ascension_2
        c3 = declination_1; c4 = declination_2

    Returns
    -------
    y : pd.Dataframe - series
        The corresponding angular distance in degree value.
    '''
    # Unpack column names
    c1, c2, c3, c4 = col_names
    # Assert datatypes
    assert type(c1) == str and type(c2) == str and type(c3) == str and type(c4) == str, 'TypeError: input should be str'

    # Units of right-ascension is in (hours) format
    alpha1, alpha2 = radians(15*row[c1]), radians(15*row[c2])

    # Units of declination is in (degrees) format
    delta1, delta2 = radians(row[c3]), radians(row[c4])

    # Given Formula
    temp = cos(pi/2 - delta1)*cos(pi/2 - delta2) + sin(pi/2 - delta1)*sin(pi/2 - delta2)*cos(alpha1 - alpha2)

    return np.degrees(acos(temp))

def genRefCatalogue(CATALOGUE, mag_limit, no_iter = -1,  gen_csv = True):
    '''
    Generates the reference star catalogue for Geometric Voting Algorithm where each row
    of the table has two unique stars and the corresponding angular distance in degrees,
    for all pairs of stars with a specified upper magnitude limit

    Parameters
    ----------
    CATALOGUE : pd.Dataframe
        The 'master' star catalogue on which the function works

    mag_limit : floating-point number
        The upper magnitude limit of stars that are required in the reference catalogue

    no_iter : integer, default = -1
        Specifies the number of iterations, thereby allowing it to be reduced
        Default value = -1, allows for the completion of the entire catalogue

    gen_csv : boolean, default = True
          If True generates csv files of the reference catalogues

    Returns
    -------
    OB_CATALOGUE : pd.Dataframe
        The corresponding angular distance in degree value.
    '''

    # Start clock-1
    start1 = time.time()

    # Generate restricted catalogue based on upper magnitude limit
    temp0 = CATALOGUE[CATALOGUE.Mag <= mag_limit]

    # Number of rows in the resticted catalogue
    rows = temp0.shape[0]
    # Resets the index of <temp0>
    temp0.index = list(range(rows))

    # Prints total number of stars in <temp0> and the (n)C(2) - combinations
    print('Number of stars - ', rows)
    print('Number of unique combinations = ', (rows-1)*rows/2)

    # Initialize the number of iterations to take place
    no_iter = (rows-1) if no_iter == -1 else no_iter

    for i in range(no_iter):
        # Throws error if an iteration runs beyond number of available rows in <temp0>
        assert i<(rows-1), 'IndexError: iterating beyond available number of rows'

        # The final iteration is reduntant, as <temp2> will be zero rows
        '''
        if (rows-1-i)==0:
            continue
        '''

        # Generates <temp1> dataframe which has the (i - th) star of <temp0>
        # repetated (rows-1-i) times
        temp1 = pd.DataFrame(columns = ['Star_ID1','RA_1', 'Dec_1', 'Mag_1'])
        s1, ra, dec, mag = temp0.iloc[i]
        temp1.loc[0] = [s1] + [ra] + [dec] + [mag]
        temp1 = pd.concat([temp1]*(rows-1-i), ignore_index=True)

        # Generates <temp2> dataframe by copying values of <temp0> and dropping the first
        # (i + 1) number of stars
        temp2 = temp0
        temp2 = temp2.drop(list(range(i+1)), axis = 0)
        # Resets the index
        temp2.index = list(range(0, rows-1-i))

        # Concatenates <temp1> & <temp2> side-by-side such that resulting <temp3> has (8) columns altogether
        temp3 = pd.concat([temp1, temp2], axis=1)

        # Initializes <temp4> in the first iteratation
        if i == 0:
            temp4 = temp3

        # Append subsequent <temp4> with <temp3> after first iteration
        else:
            temp4 = pd.concat([temp4, temp3], axis = 0, ignore_index=True)

        # Releases memory back to OS
        if i%40 == 0:
            gc.collect()

    gc.collect()
    # Rename columns
    temp4.columns = ['Star_ID1','RA_1', 'Dec_1', 'Mag_1', 'Star_ID2', 'RA_2', 'Dec_2', 'Mag_2']

    if gen_csv == True:
        #Generates CSV of <temp4>
        temp4.to_csv('Processed_Catalogue1.csv', index = False)

    # Stop clock-1
    end1 = time.time() - start1

    # Print time taken
    print('Process 1 - ', end1)

    # Start clock-2
    start2 = time.time()

    # Initialize <OB_CATALOGUE>
    OB_CATALOGUE = temp4

    # Calculate angular distance between the two stars present in every row
    cols = ['RA_1', 'RA_2', 'Dec_1', 'Dec_2']
    OB_CATALOGUE['Ang_Distance'] = OB_CATALOGUE.apply(angularDistance, axis = 1, col_names = cols)

    if gen_csv == True:
        # Generates CSV of <OB_CATALOGUE>
        OB_CATALOGUE.to_csv('Processed_Catalogue2.csv', index = False)

    # Stop clock-2
    end2 = time.time() - start2

    # Print time taken
    print('Process 2 - ', end2)
    print('Total Process ', end1+ end2)

    return OB_CATALOGUE

def main():
    '''
    main function
    '''
    # Reads 'Master' star catalogue
    CATALOGUE = pd.read_csv(r"F:\IIT Bombay\SatLab\Star Tracker\Programs\Catalogues\Modified Star Catalogue.csv")
    # StarID: The database primary key from a larger "master database" of stars
    # Mag: The star's apparent visual magnitude
    # RA, Dec: The star's right ascension and declination, for epoch 2000.0 (Unit: RA - hrs; Dec - degrees)

    # Sorts <CATALOGUE>
    CATALOGUE.sort_values('Mag', inplace=True)

    # Run function
    REF_DF = genRefCatalogue(CATALOGUE, mag_limit=1, no_iter=-1, gen_csv=False)

    # Sort <REF_DF>
    REF_DF.sort_values('Ang_Distance', inplace=False)

    # Generates CSV of <REF_DF>
    REF_DF.to_csv('Processed_Catalogue3.csv', index = False)
    print('Done')

if __name__ == '__main__':
    main()


# A function to find out all the primes till the number n
#
# @Author : Subhendu Ranjan Mishra
#
# @Input : a positive intiger
# @Output : An array of intigers

from time import time
from numba import jit
import numpy as np

@jit
def getPrimes(n):
    q, r = divmod(n, 2)
    r = q + r
    seive = np.ones(r, dtype=bool)
    seive[0] = 0

    lim = int(n**0.5 / 2) + 1

    for i in range(1, lim + 1):
        p = 2 * i + 1
        if seive[i]:
            sp = 2 * i * (i + 1)
            seive[sp:r:p] = False

    def_prime = np.asarray([2])
    seived_primes = np.asarray(np.nonzero(seive)).flatten() * 2 + 1
    primes = np.concatenate((def_prime, seived_primes))
    return primes

@jit
def getPrimes2(n):
    arr = np.ones(n + 1, dtype=bool)
    arr[0], arr[1] = 0, 0
    for i in range(2, n + 1):
        if arr[i]:
            arr[i * i:n + 1:i] = False
    return arr.nonzero()[0]

if __name__ == '__main__':
    # Test Cases
    print("Testing the getPrimes function.")
    print("getPrimes2(18) : ", getPrimes2(18))
    print("getPrimes2(7) : ", getPrimes2(7))
    print("getPrimes2(25) : ", getPrimes2(25))
    print("getPrimes2(31) : ", getPrimes2(31))
    print()
    print("Now testing the getPrimes function for higher powers of 10")
    for power in range(1, 9):
        n = 10**power
        t0 = time()
        primes = getPrimes(n)
        print(
            "1 - Computed for {:10d} ({}th power of 10), in {:03.5f} secs. number of primes : {:,}".
            format(n, power, time() - t0, len(primes)))

        # second function
        t0 = time()
        primes2 = getPrimes2(n)
        print(
            "2 - Computed for {:10d} ({}th power of 10), in {:03.5f} secs. number of primes : {:,}".
            format(n, power, time() - t0, len(primes2)))
        # print(primes)


#IIR Analog and Digital Butterworth BPF filter design
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt
from pylab import clf
#Analog Band pass butterworth filter, order =3, fc1=100Hz, fc2=300Hz
#b = numerator coefficients, a = denominator coefficients
#w = frequency variable, h= frequency response

b,a = signal.iirfilter(3,[100,300],btype ='bandpass',analog=True, ftype ='butter')
w,h = signal.freqs(b,a,1000)

fig1 = plt.figure(1)
ax = fig1.add_subplot(1,1,1)
ax.semilogx(w,20*np.log10(np.maximum(abs(h),1e-5)))
ax.set_title('Butterworth IIR Analog BPF frequency response')
ax.set_xlabel('Frequency in Hz---->')
ax.set_ylabel('Amplitude in dB----->')
#ax.axis((10,1000,-100,10))
ax.grid(which='both',axis='both')
plt.show()

#Digital Band pass butterworth filter, order =3, fc1=100Hz, fc2=300Hz
fs =1000
sos = signal.iirfilter(3,[100/fs,300/fs],btype ='bandpass',analog=False, ftype ='butter',output='sos')
w,h = signal.sosfreqz(sos,1000)

fig2 = plt.figure(2)
clf()
ax = fig2.add_subplot(1,1,1)
ax.plot(w/np.pi,20*np.log10(np.maximum(abs(h),1e-5)))
ax.set_title('Butterworth IIR Digital BPF frequency response')
ax.set_xlabel('Normalized Digital Frequency ---->')
ax.set_ylabel('Amplitude in dB----->')
#ax.axis((10,1000,-100,10))
ax.grid(which='both',axis='both')
plt.show()

#Result
##>>> b
##array([8000000.,       0.,       0.,       0.])
##>>> a
##array([1.0e+00, 4.0e+02, 1.7e+05, 3.2e+07, 5.1e+09, 3.6e+11, 2.7e+13])
##>>> sos
##array([[ 0.01809893,  0.03619787,  0.01809893,  1.        , -1.28407904,
##         0.50952545],
##       [ 1.        ,  0.        , -1.        ,  1.        , -1.0263941 ,
##         0.65079467],
##       [ 1.        , -2.        ,  1.        ,  1.        , -1.73866033,
##         0.83854915]])


import numpy as np

def l1Norm(x):
    """
        Computes the l1 norm of x
    """
    return np.sum(np.abs(x))

def l2Norm(x):
    """
        Computes the l2 norm of x
    """
    return np.sqrt(np.sum(x**2))

def r2Compute(u, v, x):
    """
        Computes the r square score of the data
        x in the projection defined by u and v
        :param u: Vector of size x
        :param v: Vector of size x
        :param x: Matrix of size N * x
        :return r2: r square score of the N points projected in (u,v)
    """
    xProj = np.dot(x, u.T).flatten()
    yProj = np.dot(x, v.T).flatten()
    projectedPoints = np.array([xProj,yProj]).T
    SSRes = np.sum((projectedPoints[:, 1] - projectedPoints[:, 0])**2)
    SSTot = np.sum((projectedPoints[:, 1] - np.mean(projectedPoints[:, 1]))**2)

    return 1 - SSRes/(SSTot + 10**(-5)) # Add epsilon to avoid nan


"""

This program will find the closest pair of points in a set of n points.
Two algorithms will be implemented here:
    1) A brute force approach determines the distance between
       every pair of points first and then find the points
       with the closest distance.
    2) An enhanced algorithm
       - Sorts all points according to coordinates;
       - Divides all points in two halves;
       - Recursively finds the smallest distances in both subarrays;
       - Take the minimum of two smallest distances;
       - Create an array strip[] that stores all points which are at
         most d distance away from the middle line dividing the two sets;
       - Find the smallest distance in strip[];
       - Find the points with the closest distance.

 The time complexity of brute force algorithm is O(n^2).
 The time complexity of enhanced algorithm algorithm is O(n * log(n)).

 @author RabbitCaesar

"""

import numpy as np
import timeit

# Input random generator
# Use this to create different testing inputs
def input_random_generator(n):
    arrP = []
    for i in range (n):
        temp = []
        for j in range (2):
            temp.append(np.random.randint(1,50))
        arrP.append(temp)
    return arrP

# Calculate Euclidean distance between 2 points
def get_distance(point1, point2):
    return np.round(np.sqrt((point1[0] - point2[0]) ** 2
                            + (point1[1] - point2[1]) ** 2), 2)

# Brute force O(n^2)
def brute_force(arr, n):
    min_dist, pair = float('inf'), []
    # Iterate through all the possible pairs
    # Calculate distance and compare to find min
    for i in range(n - 1):
        for j in range(i + 1, len(arr)):
            dist = get_distance(arr[i], arr[j])
            if min_dist > dist:
                min_dist = dist
                pair = [arr[i], arr[j]]
    return [min_dist, pair]

# Sort points vertically or horizontally. For example, points
# (4,3), (6,2), (1,7) will be sorted to (6,2), (4,3), (1,7) vertically
def sort_points(array, index=0):
    return sorted(array, key=lambda x: x[index])

# Find the closest pair of points in the strip.
# Return the min distance between the closest pair.
def get_strip_min_dist(points, n, min_dist=float('inf')):
    # it's proven that the max iteration of this loop is 6
    for i in range(min(6, n - 1), n):
        for j in range(max(0, i - 6), i):
            dist = get_distance(points[i], points[j])
            if min_dist > dist: min_dist = dist
    return min_dist

# Recursively find the distance between the closest pair
def closest_pair_raw(points_h_sorted, points_v_sorted, n):
    # Base case: if there are only 2 or 3 points, then use brute force
    if n <= 3:
        return brute_force(points_h_sorted, n)[0]

    # Recursion part
    mid = n // 2
    # Left section
    dist_left = closest_pair_raw(points_h_sorted
                                 , points_v_sorted[:mid], mid)
    # Right section
    dist_right = closest_pair_raw(points_v_sorted
                                  , points_v_sorted[mid:], n - mid)
    min_dist = min(dist_left, dist_right)

    # Points in the strip
    strip = []
    for point in points_h_sorted:
        if abs(point[0] - points_h_sorted[mid][0]) < min_dist:
            strip.append(point)

    # Get the distance between closest pair in strip
    min_dist_strip = get_strip_min_dist(strip, len(strip), min_dist)

    return min(min_dist, min_dist_strip)

# Return final distance between closest pair
def closest_pair_of_points(points, n):
    points_h_sorted = sort_points(points, index=0)
    points_v_sorted = sort_points(points, index=1)

    return (closest_pair_raw(points_h_sorted, points_v_sorted, n))

"""
Driver executes DTM
"""
if __name__ == '__main__':

    # Manual Test case 1:
    #points = [(32, 24), (34, 70), (77, 61), (37, 99), (26, 47), (16, 53)]

    # Manual Test case 2:
    # points = [[82, 31], [67, 11], [80, 7], [25, 22], [84, 23]
    #         , [21, 2], [24, 74], [21, 53], [28, 85], [12, 7]]

    # Manual Test case 3:
    # points  =[[94, 38], [35, 48], [18, 59], [33, 95], [57, 80]
    #         , [34, 5], [71, 42], [42, 41], [75, 40], [81, 52]
    #         , [99, 21], [74, 59], [83, 70], [17, 72], [22, 50]
    #         , [51, 16], [12, 80], [57, 96], [7, 14], [5, 2]]

    # Random generator can be used to initialize input points
    points = input_random_generator(100)

    """
    Brute Force Approach O(n^2)
    Set timmer to log the runtime
    """
    start = timeit.default_timer()

    brute_force_result = brute_force(points, len(points))

    stop = timeit.default_timer()
    time_b = stop - start

    """
    Enhanced Approach O(nlog(n))
    Set timmer to log the runtime
    """
    start = timeit.default_timer()

    nlogn_result = closest_pair_of_points(points, len(points))

    stop = timeit.default_timer()
    time_e = stop - start

    """
    Display final results
    """
    comp = np.round(time_b/time_e ,2)
    pointA = str(brute_force_result[1][0])
    pointB = str(brute_force_result[1][1])

    print('\nInputs:\n' + str(points))
    print('\nClosest Pair Is: ' + pointA + ' and ' + pointB)

    print('\nBrute Force Approach O(n^2):\nMin Distance: '
          + str(brute_force_result[0]) + '\nRuntime: '
          , time_b)
    print('\nEnhanced Approach O(nlog(n)):\nMin Distabce: '
          + str(nlogn_result) + '\nRuntime: '
          , time_e)
    print('\nRuntime Comparison:\nEnhanced Approach is'
          , comp, 'times faster than Brute Force.')


# Simpson's rule
#
# M. Zingale (2013-02-13)

import math
import numpy
import sys

# function we wish to integrate
def fun(x):
    return numpy.exp(-x)

# analytic value of the integral
def I_exact(a,b):
    return -math.exp(-b) + math.exp(-a)

# do a Simpson's integration by breaking up the domain [a,b] into N
# slabs.  Note: N must be even, because we do a pair at a time
def simp(a,b,f,N):

    xedge = numpy.linspace(a,b,N+1)

    integral = 0.0

    if not N%2 == 0:
        sys.exit("ERROR: N must be even")

    delta = (xedge[1] - xedge[0])

    n = 0
    while n < N:
        integral += (1.0/3.0)*delta*(f(xedge[n]) +
                                     4.0*f(xedge[n+1]) +
                                     f(xedge[n+2]))
        n += 2

    return integral

a = 0.0
b = 1.0

N = 2
while (N <= 128):
    t = simp(a,b,fun,N)
    e = t - I_exact(a,b)
    print N, t, e

    N *= 2


import numpy as np

# 均方误差
def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)

# 交叉熵误差
# 支持单个和 batch
def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

# 假设 第三个位置是正确的
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
print('第三个位置的概率最高情况')
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
print('均方误差', mean_squared_error(np.array(y), np.array(t)))
print('交叉熵误差', cross_entropy_error(np.array(y), np.array(t)))

print('第八个位置的概率最高的情况')
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print('均方误差', mean_squared_error(np.array(y), np.array(t)))
print('交叉熵误差', cross_entropy_error(np.array(y), np.array(t)))

# 数值微分
def numerical_diff(f, x):
    h = 1e-4
    return (f(x+h) - f(x-h)) / (2 * h)

# 梯度计算
def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]
        # f(x+h) 的计算
        x[idx] = tmp_val + h
        fxh1 = f(x)
        # f(x-h) 的计算
        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        x[idx] = tmp_val

    return grad

# 梯度下降
def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad

    return x

def function_2(x):
    return x[0]**2 + x[1]**2

print('用梯度下降计算函数 function_2 的最小值')
init_x = np.array([-3.0, 4.0])
result = gradient_descent(function_2, init_x=init_x, lr=0.1, step_num=100)
print(result)


from sympy import Symbol, Derivative
from sympy.solvers import solve

x = Symbol('x')
y = Symbol('y')
h = Symbol('h')

#f = 2*x**2 - y + 3*x*y
#values = (2,3)

f = 3*x**2 - 2*y + x*y
values = (2,1)

def printline(): print('---------------------------------------------------------------------------------------')

def steep_step(f, values, x, y, h):
    (xi, yi) = values
    dfdx = Derivative(f,x).doit().evalf(subs = {x:xi, y:yi})
    dfdy = Derivative(f,y).doit().evalf(subs = {x:xi, y:yi})
    xnew = xi + dfdx*h
    ynew = yi + dfdy*h
    g = f.subs(x, xnew)
    g = g.subs(y,ynew)
    dgdh = Derivative(g,h).doit()
    H = solve(dgdh, h)[0]
    xnew = xi + dfdx*H
    ynew = yi + dfdy*H
    return (xnew, ynew)

def steep_step_verbose(f, values, x, y, h):
    (xi, yi) = values
    dfdx = Derivative(f,x).doit()
    print('df/dx: ' + str(dfdx))
    dfdx = dfdx.evalf(subs = {x:xi, y:yi})
    print('df/dx @ ' + str(values) + ': ' + str(dfdx))
    dfdy = Derivative(f,y).doit()
    print('df/dy: ' + str(dfdy))
    dfdy = dfdy.evalf(subs = {x:xi, y:yi})
    print('df/dy @ ' + str(values) + ': ' + str(dfdy))
    xnew = xi + dfdx*h
    print('x = ' + str(xnew))
    ynew = yi + dfdy*h
    print('y = ' + str(ynew))
    g = f.subs(x, xnew)
    g = g.subs(y,ynew)
    print('g(h) = ' + str(g))
    dgdh = Derivative(g,h).doit()
    print("g'(h) = " + str(dgdh))
    H = solve(dgdh, h)
    print('Solutions of h: ' + str(len(H)))
    H = H[0]
    print('h* = ' + str(H))
    xnew = xi + dfdx*H
    ynew = yi + dfdy*H
    print('(x,y) = ' + str((xnew, ynew)))
    return (xnew, ynew)

printline()
print('Iteration 1: ')
values = steep_step_verbose(f, values, x, y, h)
printline()
print('Iteration 2: ')
values = steep_step_verbose(f, values, x, y, h)
(xi, yi) = values
Y = f.evalf(subs = {x:xi, y:yi})
print('Height on the mountain: ' + str(Y))
printline()


import numpy as np

def Build(type_str="sigmoid"):
    """
    Factory method that builds the specified activation function.
    """
    if (type_str == "sigmoid"):
        return SigmoidAF()
    elif (type_str == "arctan"):
        return ArctanAF()
    else:
        raise NotImplementedError("Unsupported activation function: '%s'" % (type_str))

class ActivationFunctionBaseClass:
    """
    Base class that defines the interfaces of all activation functions.
    """
    def __init__(self):
        pass

    def GetValue(self, x):
        raise NotImplementedError("Must implement GetValue().")

    def GetDerivative(self, x):
        raise NotImplementedError("Must implement GetDerivative().")

class SigmoidAF(ActivationFunctionBaseClass):
    def __init__(self):
        pass

    def GetValue(self, x):
        return 1.0 / (1.0 + np.exp(-x))

    def GetDerivative(self, x):
        v = self.GetValue(x)
        #return np.multiply(v, (1.0 - v))
        return v * (1.0 - v)

class ArctanAF(ActivationFunctionBaseClass):
    def __init__(self):
        pass

    def GetValue(self, x):
        return np.arctan(x);

    def GetDerivative(self, x):
        return 1.0 / (1.0 + np.multiply(x, x))


import numpy as np

def median_bins(values, B):
  #main variables (returns)
  mean = np.mean(values)
  stdev = np.std(values)
  bin_counts = np.zeros((B,)) # init with zeroes - , dtype=np.int
  n_smaller = 0

  #aux variables
  width = 2*stdev / B
  minval = mean-stdev

  for value in values: # for all the values in the array
    binmax = minval

    if value < minval : #skip this value
      n_smaller += 1
    else: # fit into a bin
      for index in range(B):
        binmax += width
        if value < binmax: #values > maxval -> ignored
          bin_counts[index] += 1
          break
  return (mean, stdev, n_smaller, bin_counts)

def median_approx(values, B):
  median_bins_result = median_bins(values, B)

  #resuts of bin calculation
  mean = median_bins_result[0]
  stdev = median_bins_result[1]
  n_smaller = median_bins_result[2]
  bins = median_bins_result[3]

  #other aux calculations
  minval = mean - stdev
  target_total = (len(values)+1)/2
  width = 2*stdev / B
  running_total = n_smaller

  #print("minval:", minval, "width:", width, ", running_total/n_smaller: ", running_total)

  for index in range(len(bins)): #go over every bin
     running_total += bins[index]
     #print("idx: ", index, "rt: ", running_total, ", target_total: ", target_total)
     if running_total >= target_total:
        return minval + width/2 + index*width
  return minval + width/2 + index*width

# You can use this to test your functions.
# Any code inside this `if` statement will be ignored by the automarker.
if __name__ == '__main__':
  # Run your functions with the first example in the question.
  print(median_bins([1, 1, 3, 2, 2, 6], 3))
  print(median_approx([1, 1, 3, 2, 2, 6], 3)) #2.5

  # Run your functions with the second example in the question.
  print(median_bins([1, 5, 7, 7, 3, 6, 1, 1], 4))
  print(median_approx([1, 5, 7, 7, 3, 6, 1, 1], 4)) #4.50544503131

  #3rd case
  print(median_bins([0, 1], 5))
  print(median_approx([0, 1], 5)) #0.9

  # Pathological case
  print(median_bins([1, 1, 1000], 5))
  print(median_approx([1, 1, 1000], 5)) #-42.7464930162


def bsm_call_value(S0, K, T, r, sigma):
  #Valuation of European call option in BSM model.
  #Analytical formula.

  from math import log, sqrt, exp
  from scipy import stats
  S0 = float(S0)
  d1 = (log(S0 / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sqrt(T))
  d2 = (log(S0 / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * sqrt(T))
  value = (S0 * stats.norm.cdf(d1, 0.0, 1.0) - K * exp(-r * T) * stats.norm.cdf(d2, 0.0, 1.0))
  # stats.norm.cdf --> cumulative distribution function
  # for normal distribution
  return value

bsm_call_value(S0=10,K=10,T=3./12,r=0.01,sigma=0.1)

# Vega function
def bsm_vega(S0, K, T, r, sigma):
  # Vega of European option in BSM model.
  from math import log, sqrt
  from scipy import stats
  S0 = float(S0)
  d1 = (log(S0 / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sqrt(T))
  vega = S0 * stats.norm.cdf(d1, 0.0, 1.0) * sqrt(T)
  return vega

bsm_vega(10,10,3./12,0.01,0.1)

# Implied volatility function
def bsm_call_imp_vol(S0, K, T, r, C0, sigma_est, it=100):
  for i in range(it):
    sigma_est -= ((bsm_call_value(S0, K, T, r, sigma_est) - C0)
    / bsm_vega(S0, K, T, r, sigma_est))
  return sigma_est

bsm_call_imp_vol(S0=10,K=10,T=3./12,r=0.01,C0=0.3,sigma_est=0.05,it=1000)

import pandas as pd
import os
os.getcwd()

filename = 'vstoxx_data_31032014.h5'

with pd.HDFStore(filename,  mode='r') as h5:
  futures_data = h5.select('futures_data')

with pd.HDFStore(filename,  mode='r') as h5:
  options_data = h5.select('options_data')

#==============================================================================
# futures_data = pd.read_hdf(filename, 'futures_data')
# options_data= pd.read_hdf(filename, 'options_data')
#
# h5 = pd.HDFStore('vstoxx_data_31032014.h5', 'r')
# futures_data = h5['futures_data'] # VSTOXX futures data
# options_data = h5['options_data'] # VSTOXX call option data
#
#==============================================================================

h5.close()

options_data.info()
options_data.head()
futures_data.head()
options_data[['DATE', 'MATURITY', 'TTM', 'STRIKE', 'PRICE']].head()

options_data['IMP_VOL'] = 0.0

V0 = 17.6639
r = 0.01
tol = 0.5 # tolerance level for moneyness

for option in options_data.index:
  # iterating over all option quotes
  forward = futures_data[futures_data['MATURITY'] == \
            options_data.loc[option]['MATURITY']]['PRICE'].values[0]
  # picking the right futures value
  if (forward * (1 - tol) < options_data.loc[option]['STRIKE'] < forward * (1 + tol)):
      # only for options with moneyness within tolerance
      imp_vol = bsm_call_imp_vol(V0, # VSTOXX value
                                 options_data.loc[option]['STRIKE'],
                                 options_data.loc[option]['TTM'],
                                 r, # short rate
                                 options_data.loc[option]['PRICE'],
                                 sigma_est=2., # estimate for implied volatility
                                 it=100)
      options_data['IMP_VOL'].loc[option] = imp_vol

futures_data['MATURITY']
options_data.loc[46170]
options_data.loc[46170]['STRIKE']

maturities = sorted(set(options_data['MATURITY']))
maturities

plot_data = options_data[options_data['IMP_VOL'] > 0]

#iterates over all maturities and does the plotting
import matplotlib.pyplot as plt
%matplotlib inline

plt.figure(figsize=(16, 12))
for maturity in maturities:
  data = plot_data[options_data.MATURITY == maturity]
  # select data for this maturity
  plt.plot(data['STRIKE'], data['IMP_VOL'],label=maturity.date(), lw=1.5)
  plt.plot(data['STRIKE'], data['IMP_VOL'], 'r.')

plt.grid(True)
plt.xlabel('strike')
plt.ylabel('implied volatility of VSTOXX')
plt.legend()
plt.show()

keep = ['PRICE', 'IMP_VOL']
group_data = plot_data.groupby(['MATURITY', 'STRIKE'])[keep]
group_data

# any aggregation since one element in every group
group_data = group_data.sum()
group_data.head(20)
# check attributes and methods of unknown objects
dir(group_data)
#unique MATURITY and STRIKE
group_data.index.levels


"""
Contains various functions for computing statistics over 3D volumes
"""
import numpy as np

def Dice3d(a, b):
    """
    This will compute the Dice Similarity coefficient for two 3-dimensional volumes
    Volumes are expected to be of the same size. We are expecting binary masks -
    0's are treated as background and anything else is counted as data

    Arguments:
        a {Numpy array} -- 3D array with first volume
        b {Numpy array} -- 3D array with second volume

    Returns:
        float
    """
    if len(a.shape) != 3 or len(b.shape) != 3:
        raise Exception(f"Expecting 3 dimensional inputs, got {a.shape} and {b.shape}")

    if a.shape != b.shape:
        raise Exception(f"Expecting inputs of the same shape, got {a.shape} and {b.shape}")

    # TASK: Write implementation of Dice3D. If you completed exercises in the lessons
    # you should already have it.

    # From slides (and exericse)
    # Dice = 2 * intersection(x, y) / sum(x, y)
    # Where
    # Intersection = np.sum(a*b)
    # Note additional logic for scenario where denominator == 0

    # Convert to binary: here, we're not going to make a distinction between classes beyond "0" and "not 0"
    a, b = np.where(a > 0, 1, 0), np.where(b > 0, 1, 0)

    if (np.sum(a) + np.sum(b)) == 0:
        return -1

    return (np.sum(a * b) * 2.0) / (np.sum(a) + np.sum(b))

def Jaccard3d(a, b):
    """
    This will compute the Jaccard Similarity coefficient for two 3-dimensional volumes
    Volumes are expected to be of the same size. We are expecting binary masks -
    0's are treated as background and anything else is counted as data

    Arguments:
        a {Numpy array} -- 3D array with first volume
        b {Numpy array} -- 3D array with second volume

    Returns:
        float
    """
    if len(a.shape) != 3 or len(b.shape) != 3:
        raise Exception(f"Expecting 3 dimensional inputs, got {a.shape} and {b.shape}")

    if a.shape != b.shape:
        raise Exception(f"Expecting inputs of the same shape, got {a.shape} and {b.shape}")

    # TASK: Write implementation of Jaccard similarity coefficient. Please do not use
    # the Dice3D function from above to do the computation ;)
    # <YOUR CODE GOES HERE>

    # From slides (and exericse)
    # Jaccard = intersection(x, y) / union(x, y)
    # Where
    # Union = sum - intersection
    # TODO remove Similar logic for denominator == 0
    a, b = np.where(a > 0, 1, 0), np.where(b > 0, 1, 0)

    if ((np.sum(a) + np.sum(b)) - np.sum(a * b)) == 0:
        return -1

    return  np.sum(a * b) / ((np.sum(a) + np.sum(b)) - np.sum(a * b))


### Univariate batch (standard) gradient descent algorithm

### author: Dr. Marko Mitic
### year 2015
### contact: miticm@gmail.com

import numpy as np
import matplotlib.pyplot as plt

class BatchGD: #Main class for Univariate Batch Gradient Descent

    def __init__(self, data):
        ### data is given in two-column format, first column is input, second column is output
        self.x=data[:,0]
        self.y=data[:,1]
        self.m=len(data[:,0])

    def plotdata(self):
        ###
        plt.figure()
        plt.plot(self.x, self.y, 'ro')
        plt.show()

    def hypothesis(self, theta0, theta1):
        "Return univariate hypothesis"
        self.t0=theta0
        self.t1=theta1
        self.hy=self.t0+self.t1*self.x ##current hypothesis
        return self.hy

    def cost_function(self):
        "Return Cost function for hypothesis hy(of input x) and output y"
        self.J=(1./(2.*self.m))*sum((self.hy-self.y)**2)
        return self.J

    def gradientdescent(self,alpha):
        "Perform gradient descent algorithm"
        self.a=alpha #leaarning rate

        temptheta0=self.t0-self.a*(1./self.m)*sum((self.hy-self.y))
        temptheta1=self.t1-self.a*(1./self.m)*sum(((self.hy-self.y)*self.x))

        self.t0=temptheta0
        self.t1=temptheta1
        return self.t0, self.t1

    def plothyp(self,col,title):
        "Plot current hypothesis"
        plt.figure()
        plt.plot(self.x, self.y, 'ro')
        plt.plot(self.x, self.hy, col)
        #plt.gca().set_xlim(left=0)
        #plt.gca().set_ylim(bottom=0)
        plt.title(title)
        plt.show()

## END CLASS

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

#import data
data = np.loadtxt("data_GD.txt", comments="#", delimiter=",", unpack=False)

gd=BatchGD(data)
## visualize the data

gd.plotdata()
##starting values for hupothesis hy=theta0+theta1*x
gd.t0=1.5
gd.t1=-0.7

#test and plot the starting hypothesis
gd.hy=gd.hypothesis(gd.t0,gd.t1)
gd.plothyp('b','Initial hypothesis')

#calculate initial gradient
gd.J=gd.cost_function()

#define error tolerance:
tol=5

while (gd.J>tol): # of course, you can do this with FOR also :)
                  #for i in range (0,1000):
    gd.hy=gd.hypothesis(gd.t0,gd.t1)
    gd.J=gd.cost_function()
    [gd.t0,gd.t1]=gd.gradientdescent(0.002) ##learning rate==0.002    ## interesting results with 0.2 ;)

    print "Current cost is", gd.J
    print 'Parameters Theta0 and Theta1 are {} and {}, respectively' .format(gd.t0, gd.t1)

print 'Final parameters Theta0 and Theta1 found by GD are {} and {}, respectively' .format(gd.t0, gd.t1)
gd.plothyp('k', 'Final hypothesis')

##END


# -*- coding: utf-8 -*-
"""
Created on Fri Sep  8 17:22:01 2017

@author: Alan
"""

#Black Scholes Model
###############
S = float(input("Please enter the stock price :"))

K = float(input("Please enter the strike price :"))

r = float(input("Please enter the risk-free rate :"))

T = float(input("Please enter the maturing time in year :"))

t = float(input("Please enter the starting time in year :"))

D = float(input("Please enter the annual dividend yield :"))

sigma = float(input("Please enter the annualized volatility :"))

def BS_Call_Value():

    from math import exp,log,sqrt
    from scipy import stats

    d1 = (log(S/K ,exp(1)) + ((r - D + (0.5*(sigma**2)))*(T-t))) / (sigma*sqrt(T-t))
    print("d1 =",d1)
    d2 = d1 - (sigma*sqrt(T-t))
    print("d2 =",d2)

    Nd1 = stats.norm.cdf(d1, 0.0 ,1.0)
    print("N(d1) =",Nd1)

    Nd2 = stats.norm.cdf(d2, 0.0 ,1.0)
    print("N(d2) =",Nd2)

    call_value = float((S*exp(-D*(T-t))*Nd1)- (K*exp(-r*(T-t))*Nd2))
    return call_value

European_Call_Option = BS_Call_Value()
print("European_Call_Option =",European_Call_Option)

def BS_Put_Value():

    from math import exp,log,sqrt
    from scipy import stats

    d1 = (log(S/K ,exp(1)) + ((r - D + (0.5*(sigma**2)))*(T-t))) / (sigma*sqrt(T-t))
    print("d1 =",d1)

    d2 = d1 - (sigma*sqrt(T-t))
    print("d2 =",d2)

    N_d1 = stats.norm.cdf(-d1, 0.0 ,1.0)
    print("N(-d1) =",N_d1)

    N_d2 = stats.norm.cdf(-d2, 0.0 ,1.0)
    print("N(-d2) =",N_d2)

    Put_value = float((K*exp(-r*(T-t))*N_d2)-(S*exp(-D*(T-t))*N_d1))
    return Put_value

European_Put_Option = BS_Put_Value()
print("European_Put_Option =",European_Put_Option)

###################
#Put Call Parity in Black Scholes Model
###################
Put_Call_Parity = round(European_Call_Option - European_Put_Option,2)
print("Put Call Parity =",Put_Call_Parity)

def P_C_Parity():

     from math import exp

     PV_S = S*exp(-D*(T-t))
     print("Present Value of Stock Price =",PV_S)
     PV_K = K*exp(-r*(T-t))
     print("Present Value of Strike Price in Dollar =",PV_K)
     P_C = round(PV_S - PV_K , 2)
     return P_C

Put_Call_Parity = P_C_Parity()
print("Put Call Parity =", Put_Call_Parity)

Black Scholes Model european call and put .py


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

# In your python module cplane-np-hw.py, create a function (c, max=100) that takes a complex parameter c and an optional positive integer max, and returns a function specified by the following algorithm:
# The returned function should take one complex parameter z as an input, and return one positive integer as an output.
# If the input number z has a magnitude abs(z) larger than 2, the function should output the integer 1.
# Otherwise, set a counter n=1.
# Increment n by 1, then transform the input z according to the formula z = z**2 + c. Check the resulting magnitude abs(z): If the magnitude now exceeds 2, then return the value of n; If the magnitude does not yet exceed 2, repeat this step.
# If the positive integer max is reached before the magnitude of z exceeds 2, the preceding loop should abort and return the output integer 0.

# Create a new class JuliaPlane that subclasses your ArrayComplexPlane class. We are going to extend that class with interesting functionality:
# Change the __init__ method to accept only a single argument c, which is a complex number. Have the init call the ___init___ method of the parent class, assuming the default values xmin=ymin=-2 and xmax=ymax=2 and xlen=ylen=1000. Have the init call also immediately apply the function julia to the plane, using the input argument c used to initialize the class. (Note that the transformed plane will now be a 2D array of positive integers, and fs will now contain one function.)
# Change the refresh method to accept only a single argument c, which is a complex number. Have this function regenerate a fresh plane and empty the fs list as before, but then apply the function julia to the plane with the updated argument c analogously to the initialization. (The zoom and apply methods should work the same as before.)
# Find a way to test that the resulting output is correct. (Hint: What happens when c=0?)
# Create a new method toCSV(self, filename) that exports the transformed plane of integers to a .csv file in a sensible way. Make sure that the file stores all the needed parameters for the plane, including the value of c and the current zoom settings (xmin, etc.).
# Create a new method fromCSV(self, filename) that imports a .csv file previously exported by the class. This method should reset the plane parameters to match the settings in the file, and refresh the plane array to the values stored in the .csv file directly.

# In a module test_cplane-np-hw.py write suitable test functions to verify that you accomplished your goals correctly.
def julia(c, max = 1000):
    def func510(z):
        if abs(z) > 2:
            return 1
        n = 1
        mag = abs(z)
        while mag < 2 and n < max:
            z = z**2 + c
            mag = abs(z)
            n += 1
        if n == max:
            return 0
        else:
            return n
    return func510

class JuliaPlane(cplane_np.ArrayComplexPlane):
    def gen_plane(self,c):
        '''make it plane'''
        self.plane =  []
        #xs-----------------------------------------------------------------------------
        self.dx = (self.c[1]-self.c[0])/(self.c[2]-1)
        multi = np.array(list(range(0,self.c[2]))*self.c[-1])  #array with index + 1, ylen times
        multi = multi * self.dx #multiply by dx to get how much to add
        multi = self.c[0] + multi
        #ys-----------------------------------------------------------------------------
        self.dy = (self.c[-2]-self.c[-3])/(self.c[-1]-1)
        vec_y = np.array(list(range(0,self.c[-1])))
        vec_y = np.repeat(vec_y,self.c[2])
        vec_y =  (self.c[-3] + vec_y*self.dy)*1j

        #together------------------------------------------------------------------------
        self.plane = multi + vec_y
#         print(self.plane.reshape(self.c[2],self.c[-1]).transpose())
        return self.plane.reshape(self.c[2],self.c[-1]).transpose()
    def __init__(self,d,c = (-2,2,100,-2,2,100)):
        self.c = c
        self.fs = []
        print("init fx")
        self.plane = self.gen_plane(self.c)
#         self.plane = np.vectorize(julia(self.plane))
        f = np.vectorize(julia(c = d))
        self.plane =  f(self.plane)
#         print("------------------------------")
#         print(self.plane)
    def toCSV(self):
        print("PLA")
        print(self.plane)
        np.savetxt("510.csv",self.plane, header = 'xmax is: ' + str(self.c[0]) + 'xmin is: ' + str(self.c[1]) + 'xlen is: ' + str(self.c[2]) +
                   'ymax is: ' + str(self.c[3]) + 'ymin is: ' + str(self.c[4]) + 'ylen is: ' + str(self.c[5]))
    def fromCSV(self):
        csv = np.array(list(i  for i in (open('510.csv', 'rb'))))
        print(csv)
#         self.c = csv[1]
    def show(self):
        #need x of  reals, y of complex, but now they're all reals...
#         print(self.plane)
        plt.imshow(self.plane,  cmap = plt.cm.hot, interpolation = 'bicubic', extent = [self.c[0], self.c[1], self.c[3], self.c[4]])


import math
import numpy as np

__DEBUG__ = False

def compute_entropy(values):
    """
    Given values for a random variable, calculate its entropy.
    :param values: RV
    :return: entropy of random variable with values outcome
    """
    counts = {}
    for val in values:
        try:
            counts[val] += 1
        except KeyError:
            counts[val] = 1
    entropy = 0
    for key, value in counts.items():
        prob = counts[key] / len(values)
        entropy += -1 * prob * math.log(prob, 2)
    return entropy

def compute_expected_info(data, feature, target):
    """
    Compute expected info for a feature given the observation data
    :param data: training data
    :param feature: feature name to process
    :param target: target variable
    :return: expected info
    """
    data_size = data.shape[0]
    expected_info = 0
    for attr_value in data[feature].unique():
        values = data.loc[data[feature] == attr_value][target].values
        expected_info += compute_entropy(values) * len(values) / data_size
    return expected_info

def get_best_feature(data, target):
    """
    Get best feature that gives the purest node!
    :param data: training data
    :param target: target variable
    :return: feature name
    """
    features = list(set(data.columns) - set([target]))
    exp_info_values = []
    for feature in features:
        exp_info = compute_expected_info(data, feature, target)
        exp_info_values.append(exp_info)
        if __DEBUG__:
            print("feature: {} --> info: {}".format(feature, exp_info))
    best_fet_inx = np.argmin(np.array(exp_info_values))
    if __DEBUG__:
        print("feature: {} --> info: {}".format(features[best_fet_inx], min(exp_info_values)))
    return features[best_fet_inx]


import pandas as pd
from portfolioActualReturns import singleStockActualReturns
from marketRiskPremium import marketActualReturns
import statsmodels.api as sm

tickers = ["AAPL","GOOG","AMZN","MSFT","INTC","IBM","ORCL","CSCO","NVDA"]

def singleStockLinearRegression_Summary(ticker):
    """
    Returns a summary for the Linear Regression of a single stock, just like in excel
    """

    stockReturns = singleStockActualReturns(ticker)
    market = marketActualReturns()

    data={ticker: stockReturns, "S&P500":market}
    df = pd.DataFrame.from_dict(data)

    Y = df[ticker]
    X= df["S&P500"]

    X = sm.add_constant(X)

    model = sm.OLS(Y,X).fit() # Regression Model

    summary = model.summary()

    return summary

def singleStockLinearRegression_Stats(ticker):
    """
    Returns the regression stats as a list for a single ticker. Stats are Alpha, Beta, p-values for each and R^2
    """
    stockReturns = singleStockActualReturns(ticker)
    market = marketActualReturns()

    data={ticker: stockReturns, "S&P500":market}
    df = pd.DataFrame.from_dict(data)

    Y = df[ticker]
    X=df["S&P500"]

    X = sm.add_constant(X)

    model = sm.OLS(Y,X).fit()

    alpha_beta = dict(model.params)

    alpha_beta["Alpha"] = alpha_beta.pop("const")
    alpha_beta["Beta"] = alpha_beta.pop("S&P500")

    p_values = dict(model.pvalues)
    p_values["Alpha p-value"] = p_values.pop("const")
    p_values["Beta p-value"] = p_values.pop("S&P500")

    r_squared = model.rsquared

    return [alpha_beta,p_values,r_squared]

def singleStockLinearRegression_StatsOutput(ticker):
    """
    Displays the statistical results explained above
    """

    stockReturns = singleStockActualReturns(ticker)
    market = marketActualReturns()

    data={f"{ticker}": stockReturns, "S&P500":market}
    df = pd.DataFrame.from_dict(data)

    Y = df[f"{ticker}"]
    X=df["S&P500"]

    X = sm.add_constant(X)

    model = sm.OLS(Y,X).fit()

    alpha_beta = dict(model.params)

    alpha_beta["Alpha"] = alpha_beta.pop("const")
    alpha_beta["Beta"] = alpha_beta.pop("S&P500")

    p_values = dict(model.pvalues)
    p_values["Alpha p-value"] = p_values.pop("const")
    p_values["Beta p-value"] = p_values.pop("S&P500")

    r_squared = model.rsquared

    print(f"Linear Regression Stats for {ticker}:")
    print("============================"+"="*len(ticker)+"=")
    print(f"Alpha = {alpha_beta.get('Alpha')}")
    print(f"Alpha p-value = {p_values.get('Alpha p-value')}")
    print(f"Beta = {alpha_beta.get('Beta')}")
    print(f"Beta p-value = {p_values.get('Beta p-value')}")
    print(f"R-Squared = {r_squared}")
    print("============================"+"="*len(ticker)+"=")

if __name__=="__main__":
    singleStockLinearRegression_StatsOutput("INTC")
    #print(singleStockLinearRegression_Summary("INTC"))
    #print(singleStockLinearRegression_Stats("INTC"))


# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""
import numpy as np
import matplotlib.pyplot as plt

N = int(input("Pick a number: "))

n, a, sum1, m, b, sum2 = 1, 0, 0, N, 0, 0

while (n<=N):
    a = 1/n
    sum1+=a
    n = n+1

while (m>=1):
    b = 1/m
    sum2+=b
    m = m-1

error = (sum1 - sum2)/(np.absolute(sum1)+np.absolute(sum2))
print("For N = :",N,", the error is: ",error)

N1=1
Ns=[]
Errors=[]

while N1<=N:

    n1, a1, sum11, m1, b1, sum21 = 1, 0, 0, N1, 0, 0

    while (n1<=N1):
        a1 = 1/n1
        sum11+=a1
        n1 = n1+1
    while (m1>=1):
        b1 = 1/m1
        sum21+=b1
        m1 = m1-1

    Ns.append(N1)
    error1 = (sum11 - sum21)/(np.absolute(sum11)+np.absolute(sum21))
    Errors.append(error1)
    N1=N1+1

plt.plot(Ns,Errors,'r-')
plt.xlabel("N")
plt.ylabel("Error(x10^-16)")
plt.title("N vs. Error")
plt.show()

print("The down summation is more precise because the error of the number is proportional to the number you are summing, therefore as the number decreases, there is less error, versus starting with a small number, and adding more error for every summation.")


import numpy as np
# Q. 11. Get the common items between a and b
a = np.array([1,2,3,2,3,4,3,4,5,6])
b = np.array([7,2,10,2,7,4,9,4,9,8])
c = np.intersect1d(a,b)
# print(c)
# 12. How to remove from one array those items that exist in another?
a = np.array([1,2,3,4,5])
b = np.array([5,6,7,8,9])
c = np.setdiff1d(a,b)
# print(c)
# 13. How to get the positions where elements of two arrays match?
a = np.array([1,2,3,2,3,4,3,4,5,6])
b = np.array([7,2,10,2,7,4,9,4,9,8])
# print(np.where(a==b))
# 14. How to extract all numbers between a given range from a numpy array?
# Q. Get all items between 5 and 10 from a
a = np.array([2, 6, 1, 9, 10, 3, 27])
# method 1
print(a[(a >= 5) & (a <= 10)])
# method 2
mask = np.where((a>=5) & (a<=10))
print(a[mask])
# method 3
truthy_mask = np.logical_and(a>=5, a<=10)
index_mask = np.where(truthy_mask)
print(a[index_mask])
# 15. How to make a python function that handles scalars to work on numpy arrays?
# Q. Convert the function maxx that works on two scalars, to work on two arrays.
a = np.array([5, 7, 9, 8, 6, 4, 5])
b = np.array([6, 3, 4, 8, 9, 7, 1])
def pair_max(arr_a, arr_b):
    res = np.empty_like(arr_a)
    for i, (a, b) in enumerate(zip(arr_a, arr_b)):
        if a >= b:
            res[i] = a
        else:
            res[i] = b
    return res
print(pair_max(a, b))
# using the vectorize method
def maxx(x, y):
    """Get the maximum of two items"""
    if x >= y:
        return x
    else:
        return y
pair_max = np.vectorize(maxx, otypes=[int])
print(pair_max(a, b))
# 16. How to swap two columns in a 2d numpy array?
# Q. Swap columns 1 and 2 in the array arr.
arr = np.arange(9).reshape(3,3)
print(arr)
swapped_arr = np.empty_like(arr)
swapped_arr[:,0] = arr[:,0]
swapped_arr[:,1] = arr[:,2]
swapped_arr[:,2] = arr[:,1]
print(swapped_arr)
# better method
swapped_arr = arr[:, [0,2,1]]
print(swapped_arr)
# 17. How to swap two rows in a 2d numpy array?
# Q. Swap rows 1 and 2 in the array arr:
arr = np.arange(9).reshape(3,3)
swapped_arr = arr[[0,2,1],:]
print(swapped_arr)
# 18. How to reverse the rows of a 2D array?
# Q. Reverse the rows of a 2D array arr.
# Input
arr = np.arange(9).reshape(3,3)
print(arr)
reversed_rows_arr = arr[[2,1,0],:]
print(reversed_rows_arr)
# better method, using arr[start:stop:step]
arr = np.arange(9).reshape(3,3)
reversed_rows_arr = arr[::-1]
print(reversed_rows_arr)
# 20. How to create a 2D array containing random floats between 5 and 10?
# Q. Create a 2D array of shape 5x3 to contain random decimal numbers between 5 and 10.

arr = np.random.uniform(low=5, high=11, size=(5,3))
print(arr)
print(arr)

# 21. How to print only 3 decimal places in python numpy array?
# Q. Print or show only 3 decimal places of the numpy array rand_arr.

rand_arr = np.random.random((5,3))

# solution, use: numpy.set_printoptions(precision=None...)
# #from https://het.as.utexas.edu/HET/Software/Numpy/reference/generated/numpy.set_printoptions.html

np.set_printoptions(precision=3)
print(rand_arr)

# 22. How to pretty print a numpy array by suppressing the scientific notation (like 1e10)?
# Q. Pretty print rand_arr by suppressing the scientific notation (like 1e10)

# Create the random array
np.random.seed(100)
rand_arr = np.random.random([3,3])/1e3

# Desired Output (similar to):
#> array([[ 0.000543,  0.000278,  0.000425],
#>        [ 0.000845,  0.000005,  0.000122],
#>        [ 0.000671,  0.000826,  0.000137]])
Define branch protection rules to disable force pushing, prevent branches from being deleted, and optionally require status checks before merging. New to branch protection rules? Learn more.
np.set_printoptions(suppress=True)
print(rand_arr)

# 23. How to limit the number of items printed in output of numpy array?
# Q. Limit the number of items printed in python numpy array a to a maximum of 6 elements.

a = np.arange(15)
#> array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
# Desired Output:
#> array([ 0,  1,  2, ..., 12, 13, 14])

np.set_printoptions(threshold=6)
print(a)

# 24. How to print the full numpy array without truncating
# Q. Print the full numpy array a without truncating.

# Input:

a = np.arange(15)
np.set_printoptions(threshold=sys.maxsize)
print(a)
#> array([ 0,  1,  2, ..., 12, 13, 14])

# 25. How to import a dataset with numbers and texts keeping the text intact in python numpy?
# Q. Import the iris dataset keeping the text intact.

import requests
import io

link = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"

response = requests.get(link, verify=False)
response.raise_for_status()
data = np.load(io.BytesIO(response.content))  # Works!

# data = np.loadtxt(link)

print(data)
# Not sure if this one is working because I can't run it on ONS work machine

# LOOKS LIKE np.genfromtxt() would have been a better solution

# 26. How to extract a particular column from 1D array of tuples?
# Q. Extract the text column species from the 1D iris imported in previous question.

url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
iris_1d = np.genfromtxt(url, delimiter=',', dtype=None)

text_col = np.array([row[4] for row in iris_1d])

# 27. How to convert a 1d array of tuples to a 2d numpy array?
# Q. Convert the 1D iris to 2D array iris_2d by omitting the species text field.

# running iris_1d.dtype.names gives us the col names
# The names can be used to access items in the np.void (like a tuple)

# HACKY AS HELL solution using nested list comprehension and unpacking the list of voids
iris_2d = np.array([[*void[['f0', 'f1', 'f2', 'f3']]] for void in iris_1d])

# Given solution using .tolist() method on the np.voids

iris_2d = np.array([row.tolist()[:4] for row in iris_1d])


import numpy as np
from numpy.linalg import inv
import matplotlib.pyplot as plt

# Load data
x_train = np.load('x_train.npy')
y_train = np.load('y_train.npy')
x_test = np.load('x_test.npy')
y_test = np.load('y_test.npy')

def trace_for_all_data(x_train, y_train, vector_like=True):
    if vector_like:
        for i in range(len(x_train)):
            yield (x_train[i]).reshape(2, 1), y_train[i]
    else:
        for i in range(len(x_train)):
            yield x_train[i], y_train[i]

# 1. Compute the mean vectors mi, (i=1,2) of each 2 classes

def get_mi(x_train, y_train):
    n_1 = 0
    # sum_1 = np.zeros(2)
    # sum_2 = np.zeros(2)
    # for i, e in enumerate(y_train):
    #     print(f'dot: ({x_train[i][0]}, {x_train[i][1]}) -> {y_train[i]}')
    #     if e == 0:
    #         sum_1 += x_train[i]
    #         n_1 += 1
    #     else:
    #         sum_2 += x_train[i]
    #     print(f'update sum: s1= {sum_1}, s2= {sum_2}')
    # return sum_1/ n_1, sum_2/ (len(x_train) - n_1)

    sum_ = np.zeros((2, 2, 1))
    for data in trace_for_all_data(x_train, y_train):
        sum_[data[1]] += data[0]
        n_1 += data[1] == 0
        # print(f'data: {data}')
        # print(f'update sum: s1= {sum_[0]}, s2= {sum_[1]}')
        # print(f'n_1: {n_1}')
    return sum_[0] / n_1, sum_[1] / (len(x_train) - n_1)
    # training data:
    # mean vector of class 1: [ 1.3559426  -1.34746216]
    # mean vector of class 2: [-1.29735587  1.29096203]

m1, m2 = get_mi(x_train, y_train)
print(f"mean vector of class 1:\n{m1}\nmean vector of class 2:\n{m2}\n")
# =================================================================

# 2. Compute the Within-class scatter matrix SW

def get_SW(x_train, y_train, m=None):
    '''
    m: [ m1: 2x1 vector, m2: 2x1 vector ]
    '''
    if m is None:
        m = get_mi(x_train, y_train)

    # Si = sum( (xi - m)(xi - m)T )
    # 2x2 matrix, if xi is 2-D data
    S = np.zeros((2, 2, 2))
    for data in trace_for_all_data(x_train, y_train):
        sub = data[0] - m[data[1]]
        S[data[1]] += sub @ sub.T
        # print(f'   X: {data[0]}')
        # print(f'mean: {m[data[1]]}')
        # print(f' sub: {sub}')
        # print(f'sub @ subT: {sub@sub.T}')
        # print(f'S[{data[1]}]: {S[data[1]]}')
    return S[0] + S[1]

sw = get_SW(x_train, y_train, (m1, m2))
assert sw.shape == (2, 2)
print(f"Within-class scatter matrix SW:\n{sw}\n")
# =================================================================

# 3. Compute the Between-class scatter matrix SB

def get_SB(m1, m2):
    sub = m2 - m1
    return sub @ sub.T

sb = get_SB(m1, m2)
assert sb.shape == (2, 2)
print(f"Between-class scatter matrix SB:\n{sb}\n")
# =================================================================

# 4. Compute the Fisher’s linear discriminant

def get_W(SW, m1, m2):
    w = inv(SW) @ (m2 - m1)
    # |w| = sqrt( wTw )
    # print(f'origin w: {w}')
    w = w / (w.T @ w)**(1/2)
    # print(f' after w: {w}')
    return w

w = get_W(sw, m1, m2)
assert w.shape == (2, 1)
print(f" Fisher’s linear discriminant:\n{w}\n")
# =================================================================

# 5. Project the test data by linear discriminant
#    to get the class prediction by nearest-neighbor rule
#    and calculate the accuracy score

def predict(x_train, y_train, w, x_test):
    '''
    x_test: [
        [x0, x1],
        [x0, x1],
        ...
    ]
    y_test: [
        1, 0, 0, 1, 1, ...
    ]
    w: [
        [w0],
        [w1]
    ]
    return [
        1, 0, 0, 1, 1
    ]
    x_train 投影到 w 上的長度(正負)差異
    x_train @ w = [
        [1.342],
        [-0.023],
        ...
    ]
    '''
    x_train_project = x_train @ w
    x_test_project = x_test @ w

    check_table = np.append(x_train_project,
                            y_train.reshape((-1, 1)),
                            axis=1)
    # check table:
    # [[project val, catgory],
    #  [project val, catgory]]
    # print(check_table)

    # sort by [proj val, ]
    check_table = check_table[np.argsort(check_table[:, 0])]
    # find no. of x_test( by project val to w )
    search_result = np.searchsorted(check_table[:, 0], x_test_project)
    # [1 3 5], 2 -> place in pos "1" -> check distance to "0" and "1"
    # print(f'check:\n{check_table}')
    # print(f'resul:\n{search_result}')

    y_pred = []
    for i, e in enumerate(search_result):
        e = e[0]  # U 夠麻煩...
        # x_test[i] between check table [e-1] to [e]
        # print(f'{x_test_project[i][0]} - {check_table[e-1][0]} vs.')
        # print(f'{check_table[e][0]} - {x_test_project[i][0]}')
        if x_test_project[i][0] - check_table[e-1][0] < \
           check_table[e][0] - x_test_project[i][0]:
            # if distance to e-1 is closer than
            #    distance to e
            y_pred += [check_table[e-1][1]]
        else:
            y_pred += [check_table[e][1]]
    return np.array(y_pred)

y_pred = predict(x_train, y_train, w, x_test)

def accuracy_score(y_test, y_pred):
    '''
    y_test: [1, 0, 0, ...]
    y_pred: [1, 1, 0, ...]

    correct / all
    (y_test == y_pred) / all

    not correct: abs(y_test - y_pred)
    (all-abs(y_test-y_pred)) / all
    '''
    n = len(y_test)
    return (n-sum(abs(y_test-y_pred))) / n

acc = accuracy_score(y_test, y_pred)
print(f"Accuracy of test-set {acc}")
# =================================================================

# 6. Plot the
# 1) best projection line on the training data and show the slope
#    and intercept on the title
#    (you can choose any value of intercept for better visualization)
# 2) colorize the data with each class
# 3) project all data points on your projection line.
# Your result should look like this image

def partition(x_train, y_train):
    '''
    return [
        [x0, x1],
        [x0, x1],
        ...
        [x0, x1]
    ]
    '''
    collect_0 = []
    collect_1 = []
    for x, y in trace_for_all_data(x_train, y_train, vector_like=False):
        if y == 0:
            # plt.plot([x[0]], [x[1]], 'b.')
            collect_0 += [x]
        else:
            # plt.plot([x[0]], [x[1]], 'r.')
            collect_1 += [x]
    collect_0 = np.array(collect_0)
    collect_1 = np.array(collect_1)
    return collect_0, collect_1

def Plot_point(x_train, y_train):
    collect_0, collect_1 = partition(x_train, y_train)
    # plt.plot(collect_0[0][0], collect_0[0][1], 'b.')
    plt.plot(collect_0.T[0], collect_0.T[1], 'bo')
    plt.plot(collect_1.T[0], collect_1.T[1], 'ro')

def Plot_line(w, b, from_x, to_x):
    # y_dist / x_dist
    a = w[1][0] / w[0][0]
    # y = a x + b
    plt.title(f'ProjectionLine: w={a}, b={b}')
    plt.plot(
        [from_x, to_x],
        [a * from_x + b, a * to_x + b],
        'k-'
    )

def Plot_point_on_line(x_train, y_train, w, b, from_x, to_x):
    collect_0, collect_1 = partition(x_train, y_train)
    # x0, x1 = collect_0[0]
    # project = (x0*w[0][0] + (x1-b)*w[1][0]) * w
    # point = [project[0][0], project[1][0] + b]
    # plt.plot(*point, 'b.')

    # plt.plot([x0, point[0]], [x1, point[1]], 'b--')
    # # a = (x1-x0)*w[0] + (point[1] - point[0])*w[1]
    # print()

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

    c_0 = []
    c_1 = []
    pairs = []
    for x0, x1 in collect_0:
        # project = np.array([[x0], [x1-b]]).dot(w) * w
        project = (x0*w[0][0] + (x1-b)*w[1][0]) * w
        point = [project[0][0], project[1][0] + b]
        c_0 += [point]
        pairs += [[[x0, x1], point]]
    for x0, x1 in collect_1:
        # project = np.array([[x0], [x1-b]]).dot(w) * w
        project = (x0*w[0][0] + (x1-b)*w[1][0]) * w
        point = [project[0][0], project[1][0] + b]
        c_1 += [point]
        pairs += [[[x0, x1], point]]

    c_0 = np.array(c_0)
    c_1 = np.array(c_1)
    pairs = np.array(pairs)

    plt.plot(c_0.T[0], c_0.T[1], 'bo')
    plt.plot(c_1.T[0], c_1.T[1], 'ro')
    for p1, p2 in pairs:
        plt.plot([p1[0], p2[0]], [p1[1], p2[1]], 'c:')

def Plot_all(x_train, y_train, w, b, from_x, to_x):
    # set x, y size
    plt.figure(figsize=(8, 8), dpi=80)
    plt.xlim(-6, 4)
    plt.ylim(-5, 5)

    Plot_point(x_test, y_test)
    Plot_line(**line_information)
    Plot_point_on_line(x_test, y_test, **line_information)
    plt.show()

line_information = {
    'w': w,
    'b': -3.5,
    'from_x': -6,
    'to_x': 4
}

Plot_all(x_train, y_train, **line_information)