import pandas as pd
import numpy as np

# Volatility literally refers to how "volatile" a stock is, meaning how unpredictably its price might change.
# A statistical measure of dispersion, such as standard deviation, is commonly used to measure volatility.

# In the exercise below, you're given daily prices for two sample stocks.
# Compute the standard deviations of their log returns, and return the ticker symbol for the stock that is more volatile.

def get_most_volatile(prices):
    """Return the ticker symbol for the most volatile stock.

    Parameters
    ----------
    prices : pandas.DataFrame
        a pandas.DataFrame object with columns: ['ticker', 'date', 'price']

    Returns
    -------
    ticker : string
        ticker symbol for the most volatile stock
    """
    # TODO: Fill in this function.
    curr_prices = prices.reset_index().pivot(index="date", columns="ticker", values="price")
    shifted_prices = curr_prices.shift(1)
    returns = curr_prices.applymap(np.log) - shifted_prices.applymap(np.log)
    std = returns.std()
    print(std)
    most_volatile = std.idxmax()
    print(most_volatile)
    # most_volatile = std.loc[most_volatile_value]
    # print(most_volatile)

    return most_volatile


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

import numpy as np

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

w, v = np.linalg.eig(A)
print(w)
print(v)

A = np.array([[1, 4, 6], [5, 2, 2], [-1, 6, 8]])
b = np.array([[1], [2], [3]])
x = np.linalg.solve(A, b)
print(x)


import numpy as np
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')

# Snowflake time of flight

Have you ever watched a snowflake fall and thought, "How long has that snowflake been falling?"

Here, we want to determine the time of flight for a snowflake. We'll start simple and add some complexity to make a more accurate model.

## Simplest model

The simplest assumption we can make is that the only force acting on the snowflake is gravity. This leads to one differential equation,

$-mg = m\ddot{y}$

where $m$ is the mass of the snowflake, $g=9.81~\frac{m}{s^2}$, and $\ddot{y}$ is the second derivative with respect to time for the height of the snowflake i.e. its vertical acceleration.

We integrate this equation twice to create a solution in terms of $y_0$, initial height and $\dot{y}_0$, its initial vertical velocity.

$\ddot{y} = -g$

$\frac{d\dot{y}}{dt} = -g$

$\dot{y} -\dot{y}_0 = -gt$

$\frac{dy}{dt} = \dot{y}_0 -gt$

$y-y_0 = \dot{y}_0t - \frac{gt^2}{2}$

$y(t) = y_0 +\dot{y}_0t - \frac{gt^2}{2}$

Now, we need the initial height and initial speed of the snowflake. A typical cloud might sit $\approx 1,000~m$ above the ground and let's assume the initial vertical speed is 0 m/s.

This leaves, $y_0=1000~m~and~\dot{y}_0=0~m/s$

![Cloud heights and precipitation](https://upload.wikimedia.org/wikipedia/commons/5/57/Cloud_types_en.svg)

t = np.linspace(0, np.sqrt(1000/9.81*2), 1000)
y = 1000 - 9.81*t**2/2
plt.plot(t,y)
plt.xlabel('time (s)')
plt.ylabel('height (m)')

t[-1]

## Our solution - constant acceleration

According to your calculations, the snowflake will start at 1,000-m altitude and reach ground level at almost 14.3 seconds.

> __Note:__ What's wrong with the height curve here?

## A little problem - speed of snowflake

The graph of height-vs-time keeps getting steeper. The steeper the graph, the faster the snowflake. How fast is your snowflake traveling when it hits the ground?

dy = -9.81*t

plt.plot(t, dy)
plt.xlabel('time (s)')
plt.ylabel('vertical speed (m/s)')

According to your calculations, the snowflake is traveling at 140 m/s when it strikes the ground. This is >300 mph (or >500 km/h). Whoah...

If you caught this snowflake on your tongue it would feel like catching an icy [BB gun pellet](https://en.wikipedia.org/wiki/BB_gun#Safety), ouch!

You are missing a key force in the free body diagram that _slows_ down the snowflake, [_air resistance_ or drag](https://www.grc.nasa.gov/www/k-12/VirtualAero/BottleRocket/airplane/falling.html).

![Air drag free body diagram](https://www.grc.nasa.gov/www/k-12/VirtualAero/BottleRocket/airplane/Images/falling.gif)

## Improved model with air resistance

Adding drag to the free body diagram, now you have a new model.

$m\ddot{y} = -mg + C_d \frac{r\dot{y}^2}{2}A$

where $C_d$ is the [unitless drag coefficient](https://en.wikipedia.org/wiki/Drag_coefficient), $r=1.025~kg/m^3$ is the [density of air](https://www.macinstruments.com/blog/what-is-the-density-of-air-at-stp/),  $m=3~mg$ is the [mass of a snowflake](https://hypertextbook.com/facts/2001/JudyMoy.shtml), and $A=\piD^2/4$ is the area of the snowflake of [diameter](https://gpm.nasa.gov/sites/default/files/document_files/parsivel_Tokay_c3vp_agu.pdf) $D=6~mm$.

> __Note:__ The force of drag always opposes the velocity of the snowflake. Keep in mind if the snowflake moves upward, the force reverses direction.

Now, integrating  the equation can be a bit involved, but using v(t=0)=0, there results

$\frac{dv}{dt} = -g +\frac{C_d rA}{2}v^2$

$v(t) = -\sqrt{\frac{mg}{C_d rA}}\tanh\frac{g C_d rA}{m}t$

m = 3e-6 # mg
Cd = 0.5 # no units
r = 1.025 # kg/m/m/m
g = 9.81 #m/s/s
D = 6e-3 # mm - mm
A = np.pi*D**2/4
v = -np.sqrt(2*m*g/Cd/r/A)*np.tanh(g*Cd*r*A/m*t)
plt.plot(t, v)
plt.xlim(0,0.1)
plt.xlabel('time (s)')
plt.ylabel('vertical speed (m/s)')

### Make a comparison to previous model

In the constant acceleration model, the snowflake reached the ground in 14 seconds. In the improved air resistance model, you find that the snowflake only accelerates for 0.5 seconds. After that, it floats at a constant velocity until impact. This means, you can approximate that the snowflake travels at a constant velocity equal to its terminal velocity, $v_{term}$, as such

$\frac{dv}{dt} = 0 = -g +\frac{C_d rA}{2m}v_{term}^2$

$v_{term} = -\sqrt{\frac{2mg}{C_d rA}}$

vterm = np.sqrt(2*m*g/Cd/r/A)
t_improved = np.linspace(0,1000/vterm)
y_improved = 1000 - vterm*t_improved
plt.plot(t,y, label = 'constant acceleration')
plt.plot(t_improved,y_improved, label='constant velocity')
print('total flight time is {}'.format(t_improved[-1]))
plt.xlabel('time (s)')
plt.ylabel('height (m)')

## Wrapping up

The first model you created assumed constant acceleration, but after accounting for drag you found out that a snowflake reaches a terminal velocity in less than 0.5 seconds. The more accurate model to calculate time of flight was actually a constant velocity model.

You found that the snowflake drifts slowly to the surface over the course of 496 seconds (or 8 minutes). Gently landing at 2 m/s.


###
# Introduction to Data Science Homework Assignment # 1
# Student: Alan Fernandez, aefernandez@wpi.edu
# Date: 08/29/18
# Course: DS501, Introduction to Data Science (Grad Level)
# Worcester Polytechnic Institute (WPI), Worcester, MA
###

import numpy as np

from problem3 import random_walk

#-------------------------------------------------------------------------
'''
    Problem 4: Solving sink-node problem in PageRank
    In this problem, we implement the pagerank algorithm which can solve the sink node problem.
    You could test the correctness of your code by typing `nosetests test4.py` in the terminal.
'''

#--------------------------
def compute_S(A):
    '''
        compute the transition matrix S from addjacency matrix A, which solves sink node problem by filling the all-zero columns in A.
        S[j][i] represents the probability of moving from node i to node j.
        If node i is a sink node, S[j][i] = 1/n.
        Input:
                A: adjacency matrix, a (n by n) numpy matrix of binary values. If there is a link from node i to node j, A[j][i] =1. Otherwise A[j][i]=0 if there is no link.
        Output:
                S: transition matrix, a (n by n) numpy matrix of float values.  S[j][i] represents the probability of moving from node i to node j.
    The values in each column of matrix S should sum to 1.
    '''
#########################################
## INSERT YOUR CODE HERE
    #print(A)
    # sum of each column of A
    column_sum = A.sum(axis = 0)

    # create a diagonal matrix
    sum_vector = column_sum.getA1()
    D = np.diag(np.where(sum_vector == 0, 1, sum_vector))
    #print(D)
    # normalize each column of A

    # Invert the diagonal matrix to divide the adjacency matrix by the sum of the columns.
    D = np.linalg.inv(D)

    # Multiply the matrices to execute the division. This returns the transition matrix.
    S = A * D

    # Determine the number of elements in each column
    n = S.shape[0]

    # Iterate through each column, determine if its a sink node, and replace with all equal probabilities.
    for index, column in enumerate(S):
        if ~S[:,index].any(axis=0).any():
            S[:,index] = 1/n

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

#--------------------------
def pagerank_v2(A):
    '''
        A simplified version of PageRank algorithm, which solves the sink node problem.
        Given an adjacency matrix A, compute the pagerank score of all the nodes in the network.
        Input:
                A: adjacency matrix, a numpy matrix of binary values. If there is a link from node i to node j, A[j][i] =1. Otherwise A[j][i]=0 if there is no link.
        Output:
                x: the ranking scores, a numpy vector of float values, such as np.array([[.3], [.5], [.7]])
    '''

    # Initialize the score vector with all one values
    num_nodes, _ = A.shape
    x_0 =  np.asmatrix(np.ones((num_nodes,1)))

    # compute the transition matrix from adjacency matrix
    S = compute_S(A)
    # random walk
    x, n_steps = random_walk(S,x_0)

    return x


# -*- coding: utf-8 -*-
"""
Created on Sat Apr 11 19:55:21 2020

@author: nmazzilli24
"""

import numpy as np
import math

'''
1 - Building basic functions with numpy
Numpy is the main package for scientific computing in Python. It is maintained by a large community (www.numpy.org). In this exercise you will learn several key numpy functions such as np.exp, np.log, and np.reshape. You will need to know how to use these functions for future assignments.

1.1 - sigmoid function, np.exp()
Before using np.exp(), you will use math.exp() to implement the sigmoid function. You will then see why np.exp() is preferable to math.exp().

Exercise: Build a function that returns the sigmoid of a real number x. Use math.exp(x) for the exponential function.

Reminder:  sigmoid(x)=11+e−xsigmoid(x)=11+e−x  is sometimes also known as the logistic function. It is a non-linear function used not only in Machine Learning (Logistic Regression), but also in Deep Learning.
'''

def basic_sigmoid(x):
    """
    Compute sigmoid of x.

    Arguments:
    x -- A scalar

    Return:
    s -- sigmoid(x)
    """

    ### START CODE HERE ### (≈ 1 line of code)
    s = 1/(1+math.exp(-x))
    ### END CODE HERE ###

    return s

'''
Any time you need more info on a numpy function, we encourage you to look at the official documentation.

You can also create a new cell in the notebook and write np.exp? (for example) to get quick access to the documentation.

Exercise: Implement the sigmoid function using numpy.

Instructions: x could now be either a real number, a vector, or a matrix. The data structures we use in numpy to represent these shapes (vectors, matrices...) are called numpy arrays. You don't need to know more for now.

'''

def sigmoid(x):
    """
    Compute the sigmoid of x

    Arguments:
    x -- A scalar or numpy array of any size

    Return:
    s -- sigmoid(x)
    """

    ### START CODE HERE ### (≈ 1 line of code)
    s = 1/(1+np.exp(-x))
    ### END CODE HERE ###

    return s

'''
1.2 - Sigmoid gradient
As you've seen in lecture, you will need to compute gradients to optimize loss functions using backpropagation. Let's code your first gradient function.

Exercise: Implement the function sigmoid_grad() to compute the gradient of the sigmoid function with respect to its input x. The formula is:
sigmoid_derivative(x)=σ′(x)=σ(x)(1−σ(x))(2)
(2)sigmoid_derivative(x)=σ′(x)=σ(x)(1−σ(x))

You often code this function in two steps:

Set s to be the sigmoid of x. You might find your sigmoid(x) function useful.
Compute  σ′(x)=s(1−s)σ′(x)=s(1−s)
'''

def sigmoid_derivative(x):
    """
    Compute the gradient (also called the slope or derivative) of the sigmoid function with respect to its input x.
    You can store the output of the sigmoid function into variables and then use it to calculate the gradient.

    Arguments:
    x -- A scalar or numpy array

    Return:
    ds -- Your computed gradient.
    """

    ### START CODE HERE ### (≈ 2 lines of code)
    s = sigmoid(x)
    ds = s*(1-s)
    ### END CODE HERE ###

    return ds
'''
1.3 - Reshaping arrays
Two common numpy functions used in deep learning are np.shape and np.reshape().

X.shape is used to get the shape (dimension) of a matrix/vector X.
X.reshape(...) is used to reshape X into some other dimension.
For example, in computer science, an image is represented by a 3D array of shape  (length,height,depth=3)(length,height,depth=3) . However, when you read an image as the input of an algorithm you convert it to a vector of shape  (length∗height∗3,1)(length∗height∗3,1) . In other words, you "unroll", or reshape, the 3D array into a 1D vector.

Exercise: Implement image2vector() that takes an input of shape (length, height, 3) and returns a vector of shape (length*height*3, 1). For example, if you would like to reshape an array v of shape (a, b, c) into a vector of shape (a*b,c) you would do:

v = v.reshape((v.shape[0]*v.shape[1], v.shape[2])) # v.shape[0] = a ; v.shape[1] = b ; v.shape[2] = c
Please don't hardcode the dimensions of image as a constant. Instead look up the quantities you need with image.shape[0], etc.

'''

def image2vector(image):
    """
    Argument:
    image -- a numpy array of shape (length, height, depth)

    Returns:
    v -- a vector of shape (length*height*depth, 1)
    """

    ### START CODE HERE ### (≈ 1 line of code)
    v = image.reshape(image.shape[0]*image.shape[1]*image.shape[2],1)
    ### END CODE HERE ###

    return v

'''
1.4 - Normalizing rows
Another common technique we use in Machine Learning and Deep Learning is to normalize our data. It often leads to a better performance because gradient descent converges faster after normalization. Here, by normalization we mean changing x to  x∥x∥x∥x∥  (dividing each row vector of x by its norm).

For example, if
x=[023644](3)
(3)x=[034264]

then
∥x∥=np.linalg.norm(x,axis=1,keepdims=True)=[556⎯⎯⎯⎯√](4)
(4)∥x∥=np.linalg.norm(x,axis=1,keepdims=True)=[556]

and
x_normalized=x∥x∥=0256√35656√45456√(5)
(5)x_normalized=x∥x∥=[03545256656456]

Note that you can divide matrices of different sizes and it works fine: this is called broadcasting and you're going to learn about it in part 5.

Exercise: Implement normalizeRows() to normalize the rows of a matrix. After applying this function to an input matrix x, each row of x should be a vector of unit length (meaning length 1).

'''

def normalizeRows(x):
    """
    Implement a function that normalizes each row of the matrix x (to have unit length).

    Argument:
    x -- A numpy matrix of shape (n, m)

    Returns:
    x -- The normalized (by row) numpy matrix. You are allowed to modify x.
    """

    ### START CODE HERE ### (≈ 2 lines of code)
    # Compute x_norm as the norm 2 of x. Use np.linalg.norm(..., ord = 2, axis = ..., keepdims = True)
    x_norm = np.linalg.norm(x,axis =1, keepdims = True)

    # Divide x by its norm.
    x = x/x_norm
    ### END CODE HERE ###

    return x

'''
1.5 - Broadcasting and the softmax function¶
A very important concept to understand in numpy is "broadcasting". It is very useful for performing mathematical operations between arrays of different shapes. For the full details on broadcasting, you can read the official broadcasting documentation.

Exercise: Implement a softmax function using numpy. You can think of softmax as a normalizing function used when your algorithm needs to classify two or more classes. You will learn more about softmax in the second course of this specialization.

'''

def softmax(x):
    """Calculates the softmax for each row of the input x.

    Your code should work for a row vector and also for matrices of shape (m,n).

    Argument:
    x -- A numpy matrix of shape (m,n)

    Returns:
    s -- A numpy matrix equal to the softmax of x, of shape (m,n)
    """

    ### START CODE HERE ### (≈ 3 lines of code)
    # Apply exp() element-wise to x. Use np.exp(...).
    x_exp = np.exp(x)

    # Create a vector x_sum that sums each row of x_exp. Use np.sum(..., axis = 1, keepdims = True).
    x_sum = np.sum(x_exp,axis =1, keepdims = True)

    # Compute softmax(x) by dividing x_exp by x_sum. It should automatically use numpy broadcasting.
    s = x_exp/x_sum

    ### END CODE HERE ###

    return s

'''
2.1 Implement the L1 and L2 loss functions
Exercise: Implement the numpy vectorized version of the L1 loss. You may find the function abs(x) (absolute value of x) useful.

Reminder:

The loss is used to evaluate the performance of your model. The bigger your loss is, the more different your predictions (ŷ y^) are from the true values (yy). In deep learning, you use optimization algorithms like Gradient Descent to train your model and to minimize the cost.
L1 loss is defined as:

'''

def L1(yhat, y):
    """
    Arguments:
    yhat -- vector of size m (predicted labels)
    y -- vector of size m (true labels)

    Returns:
    loss -- the value of the L1 loss function defined above
    """

    ### START CODE HERE ### (≈ 1 line of code)
    loss = np.sum(abs(y-yhat))
    ### END CODE HERE ###

    return loss

#Testing functions
def main():
    print("Testing basic_sigmoid function")
    test_sig = basic_sigmoid(3)
    sig_sol = 0.952
    tol = 0.001
    assert(abs(test_sig-sig_sol) < tol)
    print("Passed the basic_sigmoid function!")
    print()

    #Testing the sigmoid with np apis
    print("Testing sigmoid function")
    x = np.array([1, 2, 3])
    test_sig_np = sigmoid(x)
    sig_np_sol = [ 0.73105858, 0.88079708, 0.95257413]

    #https://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html
    assert(np.allclose(test_sig_np,sig_np_sol, tol))
    print("Passed the basic_sigmoid function!")
    print()

    #Sigmoid Derivative Test
    print("Testing sigmoid_derivative function")
    x = np.array([1, 2, 3])
    test_sig_deriv = sigmoid_derivative(x)
    test_sig_deriv_sol = [ 0.19661193, 0.10499359, 0.04517666]
    assert(np.allclose(test_sig_deriv,test_sig_deriv_sol, tol))
    print("Passed the sigmoid_derivative function!")
    print()

    #Normalize Row Test
    print("Testing normalizeRows function")
    x = np.array([
    [0, 3, 4],
    [1, 6, 4]])
    norm_row = normalizeRows(x)
    norm_row_sol = [[ 0., 0.6, 0.8 ], [ 0.13736056, 0.82416338, 0.54944226]]
    assert(np.allclose(norm_row,norm_row_sol, tol))
    print("Passed the normalizeRows function!")
    print()

    #Testing Softmax Funciton
    print("Testing softmax function")
    x = np.array([
    [9, 2, 5, 0, 0],
    [7, 5, 0, 0 ,0]])
    softmax_res = softmax(x)
    softmax_sol = [[ 9.80897665e-01, 8.94462891e-04, 1.79657674e-02, 1.21052389e-04, 1.21052389e-04], [ 8.78679856e-01, 1.18916387e-01, 8.01252314e-04, 8.01252314e-04, 8.01252314e-04]]
    np.testing.assert_allclose(softmax_res,softmax_sol,rtol=1e-5, atol=0)
    print("Passed the softmax function!")
    print()

    #Testing L1 Function
    print("Testing L1 function")
    yhat = np.array([.9, 0.2, 0.1, .4, .9])
    y = np.array([1, 0, 0, 1, 1])
    L1_test = L1(yhat,y)
    L1_sol = 1.1
    assert(abs(L1_test-L1_sol) < tol)
    print("Passed the L1 function!")
    print()

    ''' Framework for Computational Time
    import time

    x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
    x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]

    ### CLASSIC DOT PRODUCT OF VECTORS IMPLEMENTATION ###
    tic = time.process_time()
    dot = 0
    for i in range(len(x1)):
        dot+= x1[i]*x2[i]
    toc = time.process_time()
    print ("dot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

    ### CLASSIC OUTER PRODUCT IMPLEMENTATION ###
    tic = time.process_time()
    outer = np.zeros((len(x1),len(x2))) # we create a len(x1)*len(x2) matrix with only zeros
    for i in range(len(x1)):
        for j in range(len(x2)):
            outer[i,j] = x1[i]*x2[j]
    toc = time.process_time()
    print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

    ### CLASSIC ELEMENTWISE IMPLEMENTATION ###
    tic = time.process_time()
    mul = np.zeros(len(x1))
    for i in range(len(x1)):
        mul[i] = x1[i]*x2[i]
    toc = time.process_time()
    print ("elementwise multiplication = " + str(mul) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

    ### CLASSIC GENERAL DOT PRODUCT IMPLEMENTATION ###
    W = np.random.rand(3,len(x1)) # Random 3*len(x1) numpy array
    tic = time.process_time()
    gdot = np.zeros(W.shape[0])
    for i in range(W.shape[0]):
        for j in range(len(x1)):
            gdot[i] += W[i,j]*x1[j]
    toc = time.process_time()
    print ("gdot = " + str(gdot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
'''

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
import cv2
from scipy.stats import special_ortho_group as sog
##########################################################
dim = 20
N = 1000

alpha_vectors = np.zeros((N, dim))

for i in range(N):
    alpha_vectors[i] = np.random.normal(0, i + 1, dim)

V = sog.rvs(dim)
alpha_v = np.matmul(alpha_vectors, V)
#print(alpha_v)
##########################################################
pca = PCA()
pca.fit(alpha_v)
#print(pca.components_)
#print(pca.explained_variance_)
##########################################################
pca = PCA(n_components = 3)
pca.fit(alpha_v)
print(str(100 * np.sum(pca.explained_variance_ratio_)) + " percent of data is preserved in 3 dimensions!")
min_dim = 0
pca = PCA(n_components = 8)
pca.fit(alpha_v)
for i in range(1, dim):
    pca = PCA(n_components = i)
    pca.fit(alpha_v)
    if (np.sum(pca.explained_variance_ratio_) >= 0.9):
        min_dim = i
        break
print("Almost " + str(100 * np.sum(pca.explained_variance_ratio_)) + " percent of data is preserved in at least " + str(min_dim) + " dimensions!")

##########################################################
##########################################################
image1 = cv2.imread("mona.jpg")
image = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
dim = image.shape
#print('Image shape =', dim)
##########################################################
#plt.imshow(image)
#plt.show()
##########################################################
R = image[:, :, 0]
G = image[:, :, 1]
B = image[:, :, 2]
# print(R.shape)
# print(G.shape)
# print(B.shape)
##########################################################
k = 10
rpca = PCA(n_components = k)
gpca = PCA(n_components = k)
bpca = PCA(n_components = k)

rpca.fit(R)
gpca.fit(G)
bpca.fit(B)

print("First " + str(k) + "components of Red Matrix have " + str(100 * np.sum(rpca.explained_variance_ratio_)) + " percent of data.")
print("First " + str(k) + "components of Green Matrix have " + str(100 * np.sum(gpca.explained_variance_ratio_)) + " percent of data.")
print("First " + str(k) + "components of Blue Matrix have " + str(100 * np.sum(bpca.explained_variance_ratio_)) + " percent of data.")

# plt.bar([i for i in range(k)], rpca.explained_variance_ratio_, color ='red', width = 0.4)
# plt.xlabel("Red Components")
# plt.ylabel("Variance %")
# plt.show()

# plt.bar([i for i in range(k)], gpca.explained_variance_ratio_, color ='green', width = 0.4)
# plt.xlabel("Green Components")
# plt.ylabel("Variance %")
# plt.show()

# plt.bar([i for i in range(k)], bpca.explained_variance_ratio_, color ='blue', width = 0.4)
# plt.xlabel("Blue Components")
# plt.ylabel("Variance %")
# plt.show()
# ##########################################################
Transform_R = rpca.transform(R)
Transform_B = gpca.transform(G)
Transform_G = bpca.transform(B)
Reduced_R = rpca.inverse_transform(Transform_R)
Reduced_G = gpca.inverse_transform(Transform_G)
Reduced_B = bpca.inverse_transform(Transform_B)

print('Transform Matrix Shape = ', Transform_R.shape)
print('Inverse Transform Matrix Shape = ', Reduced_R.shape)

# ##########################################################
Reduced_R = Reduced_R.reshape((dim[0], dim[1], 1))
Reduced_G = Reduced_G.reshape((dim[0], dim[1], 1))
Reduced_B = Reduced_B.reshape((dim[0], dim[1], 1))

reduced_image = np.dstack((Reduced_R, Reduced_G, Reduced_B))
final_image = reduced_image.astype(int)
print('final_image shape = ', final_image.shape)
plt.imshow(final_image)
plt.show()
##########################################################
##########################################################
##########################################################
##########################################################
##########################################################
k = 5
rpca = PCA(n_components = k)
gpca = PCA(n_components = k)
bpca = PCA(n_components = k)

rpca.fit(R)
gpca.fit(G)
bpca.fit(B)

##########################################################
Transform_R = rpca.transform(R)
Transform_B = gpca.transform(G)
Transform_G = bpca.transform(B)
Reduced_R = rpca.inverse_transform(Transform_R)
Reduced_G = gpca.inverse_transform(Transform_G)
Reduced_B = bpca.inverse_transform(Transform_B)

print('Transform Matrix Shape = ', Transform_R.shape)
print('Inverse Transform Matrix Shape = ', Reduced_R.shape)

##########################################################
Reduced_R = Reduced_R.reshape((dim[0], dim[1], 1))
Reduced_G = Reduced_G.reshape((dim[0], dim[1], 1))
Reduced_B = Reduced_B.reshape((dim[0], dim[1], 1))

reduced_image = np.dstack((Reduced_R, Reduced_G, Reduced_B))
final_image = reduced_image.astype(int)
print('final_image shape = ', final_image.shape)
plt.imshow(final_image)
plt.show()
print(np.sum(rpca.explained_variance_ratio_))


"""
This code implements a perceptron algorithm (PLA).
This is the version of the file that runs in Python 3.6:
- it does not plot
- it had to be adjusted to pandas 0.23.0 because Vocareum uses python 3.6

First, we visualise the dataset which contains 2 features. We can see that the dataset can be clearly separated by drawing a straight line between them. The goal is to write an algorithm that finds that line and classifies all of these data points correctly.

The output file (e.g. 'output1_f.csv') contains the values of w1, w2 and b which define the 'threshold' line. The last row will be the most accurate one. Each time it goes through each of the examples in 'input1.csv', it adds a new line to the output file containing a comma-separated list of the weights w_1, w_2, and b (bias) in that order.

Upon convergence, the program stops, and the final values of w_1, w_2, and b are printed to the output file (output1.csv). This defines the decision boundary that your PLA has computed for the given dataset.

Note: When implementing your PLA, in case of tie (sum of w_jx_ij = 0), please follow the lecture note and classify the datapoint as -1.

Ensure this file can be executed as:
$ python3 problem1.py input1.csv output1.csv

The code includes plotting functions. However those are disabled when executing the code from the command line in the format specified immediately above.

"""

# builtin modules
import os
import psutil
import requests
import sys

# 3rd party modules
import pandas as pd
import numpy as np
#import plotly.graph_objects as go

def get_data(source_file):

    # Define input and output filepaths
    input_path = os.path.join(os.getcwd(), source_file)

    # Read input data
    df = pd.read_csv(input_path)

    return df

def perceptron_classify(df, n:int = 200):
    """
    1. set b = w = 0
    2. for N iterations, or until weights do not change
        (a) for each training example xᵏ with label yᵏ
            i. if yᵏ — f(xᵏ) = 0, continue
            ii. else, update wᵢ, △wᵢ = (yᵏ — f(xᵏ)) xᵢ
    """

    # transform the dataframe to an array
    data = np.asmatrix(df, dtype = 'float64')

    # get the first two columns as pairs of values
    features = data[:, :-1]
    # get the last column
    labels = data[:, -1]

    # assign zero weight as a starting point to features and labels
    w = np.zeros(shape=(1, features.shape[1]+1)) #e.g. array([0., 0., 0.])
    w_ = np.empty(shape=[0,3]) # declare w_ as an empty matrix of same shape as w

    for iteration in range(0,n):
        for x, label in zip(features, labels):
            x = np.insert(x, 0, 1) # add a column of 1s to represent w0
            f = np.dot(w, x.transpose()) # a scalar
            #print(f)
            if f * label <= 0:
                w += (x * label.item(0,0)).tolist() # because label comes from being a matrix (matrix([[1.]])) and needs to be converted to scalar
            else:
                iteration = n

        w_ = np.vstack((w_, w))

    return w_

def write_csv(filename, weights):
    # write the outputs csv file
    filepath = os.path.join(os.getcwd(), filename)
    dataframe = pd.DataFrame(data=weights, columns=('b','w1','w2'))
    # reorder the columns in the dataframe in accordance with assignment
    order = [1,2,0] # setting column's order, 'b' goes as first column followed by weights
    dataframe = dataframe[[dataframe.columns[i] for i in order]]
    dataframe.to_csv(filepath, index = False, header = False)
    return print("New Outputs file saved to: <<", filename, ">>", sep='', end='\n')

def main():

    #take string of input data csv file
    in_data = str(sys.argv[1])

    #take string of input data csv file
    out_data = str(sys.argv[2])

    if in_data and out_data:
        #add functions execute here
        df = get_data(in_data)
        w_ = perceptron_classify(df)
        if w_.size:
            write_csv(out_data, w_)
            print("Plot and output csv files are ready !")
    else:
        print("Enter valid command arguments !")

if __name__ == '__main__':
    main()


import matplotlib.pyplot as plt
import numpy as np
import os
os.chdir('C:/Users/DELL/Desktop/Quant_macro')
import useful_functions as uf

f = lambda x: (x + np.abs(x))/2
x_bar = 2
start = 0
end_ = 6
num_g = 100

Tay1 = lambda x: uf.Taylor_any_f(x, x_bar, 1, f)
Tay2 = lambda x: uf.Taylor_any_f(x, x_bar, 2, f)
Tay5 = lambda x: uf.Taylor_any_f(x, x_bar, 5, f)
Tay20 = lambda x: uf.Taylor_any_f(x, x_bar, 20, f)

x_eval = np.linspace(start, end_, num_g)
x_eval = x_eval[1:-1]

Rg = [f(x_eval[i]) for i in range(len(x_eval))]
Tay20_o = [Tay20(x_eval[i]) for i in range(len(x_eval))]
Tay5_o = [Tay5(x_eval[i]) for i in range(len(x_eval))]
Tay2_o = [Tay2(x_eval[i]) for i in range(len(x_eval))]
Tay1_o = [Tay1(x_eval[i]) for i in range(len(x_eval))]

f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2)
f.set_figheight(10)
f.set_figwidth(10)
ax1.plot(x_eval, Tay1_o, color = 'r', label = 'Taylor 1')
ax1.legend(loc='upper left')
ax1.set_xlabel('x')
ax1.set_ylabel('f(x)')
ax1.set_title('Expansion of Order 1')
ax2.plot(x_eval, Tay2_o, color = 'b', label = 'Taylor 2')
ax2.legend(loc='upper left')
ax2.set_xlabel('x')
ax2.set_ylabel('f(x)')
ax2.set_title('Expansion of Order 2')
ax3.plot(x_eval, Tay5_o, color = 'g', label = 'Taylor 5')
ax3.legend(loc='upper left')
ax3.set_xlabel('x')
ax3.set_ylabel('f(x)')
ax3.set_title('Expansion of Order 5')
ax4.plot(x_eval, Tay20_o, color = 'k', label = 'Taylor 20')
ax4.legend(loc='upper left')
ax4.set_xlabel('x')
ax4.set_ylabel('f(x)')
ax4.set_title('Expansion of Order 20')

f1, ax5 = plt.subplots(1,1)
f1.set_figheight(5)
f1.set_figwidth(10)
ax5.set_yscale('linear')
ax5.plot(x_eval, Tay1_o, color = 'r', label = 'Taylor 1')
ax5.plot(x_eval, Tay2_o, color = 'b', label = 'Taylor 2')
ax5.plot(x_eval, Tay5_o, color = 'g', label = 'Taylor 5')
ax5.plot(x_eval, Tay20_o, color = 'k', label = 'Taylor 20')
ax5.plot(x_eval, Rg, color = 'gold', label = 'True function')

ax5.legend(loc='upper left')
ax5.set_xlabel('x')
ax5.set_ylabel('f(x)')
ax5.set_title('The Taylor Expansions')


import math
import random
import string
import warnings
import pyprimes
import sympy
import os.path
import sys

import DS
import Tx

##############
# Test 1: Check your public parameters with this routine
def checkDSparams(q, p, g):
    check = 0
    warnings.simplefilter('ignore')
    #check = pyprimes.isprime(q)   # not fast enough
    check = sympy.isprime(q)
    warnings.simplefilter('default')
    if check == False: return -1

    warnings.simplefilter('ignore')
    #check = pyprimes.isprime(p)    # not fast enough
    check = sympy.isprime(p)
    warnings.simplefilter('default')
    if check == False: return -2

    if((p-1)%q != 0): return -3

    k = (p-1)//q
    x = pow(g, k, p)
    if (x==1): return -4
    y = pow(g,q,p)
    if (y!=1): return -4

    if p.bit_length() != 2048: return -5

    if q.bit_length() != 224: return -6
    return 0

# Test 2: Chect your public-private key pair
def CheckKeys(q, p, g, alpha, beta):
    if beta == pow(g, alpha, p): return 0
    else: return -1

# Test 3: Check signature for a random message
def CheckSignature(q, p, g, alpha, beta):
    message = DS.random_string(random.randint(32, 512)).encode('UTF-8') # generate a random string for message and convert it to bytes
    (s, r) = DS.SignGen(message, q, p, g, alpha)
    return DS.SignVer(message, s, r, q, p, g, beta)

# Test 4: Verifying the signatures in "TestSet.txt"
def CheckTestSignatures():
    f = open("TestSet.txt", "r")
    q = int(f.readline())
    p = int(f.readline())
    g = int(f.readline())
    beta = int(f.readline())
    for i in range(0,10):
        message = f.readline().rstrip("\n")
        s = int(f.readline())
        r = int(f.readline())
        ReturnCode = DS.SignVer(message.encode('UTF-8'), s, r, q, p, g, beta)
        if ReturnCode != 0:
            f.close()
            return -1
    f.close()
    return 0

# Test 5: generating a single random transaction, sign and verify it
def CheckTransaction(q, p, g):
    tx = Tx.gen_random_tx(q, p, g)  # generate a random transaction
    transaction = tx.split('\n')    # Split it line by line

    payer_pk = int(transaction[2][25:])  # payer's publick key
    sign_s  = int(transaction[5][15:])   # signature for the transaction (part s)
    sign_r = int(transaction[6][15:])    # signature for the transaction (part r)

    message2sign = '\n'.join(transaction[0:5])+'\n'

    if DS.SignVer(message2sign.encode('utf-8'), sign_s, sign_r, q, p, g, payer_pk)==0:
        return 0
    else:
        return -1

# Test 6: Verifying the signatures of transactions in "transactions.txt"
def CheckBlockofTransactions():
    f = open("transactions.txt", "r")
    tx_block = f.readlines()
    f.close()
    if len(tx_block)%7 != 0:
        print("Incorrect file format")
    else:
        tx_block_count = len(tx_block)//7
        result = [0]*tx_block_count
        for i in range(0, tx_block_count):
            pk = int(tx_block[i*7+2][24:])
            s = int(tx_block[i*7+5][15:])
            r = int(tx_block[i*7+6][15:])
            tx = "".join(tx_block[i*7: i*7+5])
            result[i] = DS.SignVer(tx.encode('UTF-8'), s, r, q, p, g, pk)
    return result

##### This part executes
# Generate or read the public parameters
# You need to have a routine named GenerateOrRead that reads q, p, g from "pubparams.txt" if exists
# Otherwise, it should generate public parameters and write them to "pubparams.txt"
(q, p, g) = DS.GenerateOrRead("pubparams.txt")
# DS.GenerateTestSignatures(q, p, g)  # Generate sample signatures (Students do not uncomment this)

# Testing part
# Test 1: Test public parameters
ReturnCode = checkDSparams(q, p, g)
if ReturnCode == 0: print("Public parameters: Passed!")
elif ReturnCode == -1: print("q is not prime"); sys.exit()
elif ReturnCode == -2: print("p is not prime"); sys.exit()
elif ReturnCode == -3: print("q does not divide p"); sys.exit()
elif ReturnCode == -4: print("g is not a generator"); sys.exit()
elif ReturnCode == -5: print("p is not 2048 bit"); sys.exit()
elif ReturnCode == -6: print("q is not 224 bit"); sys.exit()

# Test 2: Check a public-private key pair
(alpha, beta) = DS.KeyGen(q, p, g) # generate key pair
ReturnCode = CheckKeys(q, p, g, alpha, beta)
if ReturnCode == 0: print("Public/private key pair: Passed!")
else: print("Public/private key pair: Failed!"); sys.exit()

# Test 3: Check the signature generation-verification for a randomly generated message
ReturnCode = CheckSignature(q, p, g, alpha, beta)
if ReturnCode == 0: print("Signature generation: Passed!")
else: print("Signature generation: Failed!"); sys.exit()

# Test 4: Verifying the signatures in "TestSet.txt"
if (CheckTestSignatures() == 0): print("Sample signatures test: Passed!")
else: print("Sample signatures test: Failed!"); sys.exit()

# Test 5: generating a single random transaction, sign and verify it
if (CheckTransaction(q, p, g) == 0): print("Transaction signature verifies: Passed!")
else: print("Transaction signature DOES NOT verify: Failed!"); sys.exit()

# Test 6: Verifying the signatures of transactions in "transactions.txt"
result = CheckBlockofTransactions()
if -1 not in result: print("All transactions verified successfully: Passed!")
else:
    for i in range(len(result)):
        if result[i] == -1:
            print("Failure: tsransaction", i+1, "does not verify: Failed!")
    sys.exit()


#!/usr/bin/env python3
import numpy as np
from numpy.linalg import inv
from scipy.spatial import distance
from math import sin
from math import cos
from numba import jit

@jit(forceobj=True)
def vector_angle(u,v):
    num = np.arccos( np.dot(u ,v) )
    den = np.power(np.dot(v,v),1/2) * np.power(np.dot(u,u),1/2 )
    angle = num / den
    if np.isnan(angle).any(0) == True:
        angle = 0
    return angle

@jit(forceobj=True)
def Rz(theta_z):
    a = theta_z
    T = np.array([[cos(a),-sin(a), 0,0 ],[sin(a),cos(a),0,0],[0,0,1,0],[0,0,0,1]],dtype = np.float32)
    return T

@jit(forceobj=True)
def Ry(theta_y):
    a = theta_y
    T =  np.array([[cos(a),0, sin(a),0 ],[0,1,0,0],[-sin(a),0,cos(a),0],[0,0,0,1]],dtype = np.float32)
    return T

@jit(forceobj=True)
def Rx(theta_x):
    a = theta_x
    T =  np.array([[1,0, 0,0 ],[0,cos(a),-sin(a),0],[0,sin(a),cos(a),0],[0,0,0,1]],dtype = np.float32)
    return T

@jit(forceobj=True)
def T_tr(dx,dy,dz):
    T =  np.array([[1 ,0 ,0 ,dx],[0,1,0,dy],[0,0,1,dz],[0,0,0,1]],dtype = np.float32)
    return T

@jit(forceobj=True)
def Tx(theta_x,dx,dy,dz):
    a = theta_x
    T =  np.array([[1,0, 0,dx ],[0,cos(a),-sin(a),dy],[0,sin(a),cos(a),dz],[0,0,0,1]],dtype = np.float32)
    return T

@jit(forceobj=True)
def Ty(theta_y,dx,dy,dz):
    a = theta_y
    T =  np.array([[cos(a),0, sin(a),dx ],[0,1,0,dy],[-sin(a),0,cos(a),dz],[0,0,0,1]],dtype = np.float32)
    return T

@jit(forceobj=True)
def Tz(theta_z,dx,dy,dz):
    a = theta_z
    T = np.array(   [   [cos(a),     -sin(a),    0,     dx],
                        [sin(a),      cos(a),    0,     dy],
                        [0     ,           0,    1,     dz],
                        [0     ,           0,    0,     1 ]
                        ] ,
                        dtype = np.float32 )
    return T

@jit
def solve_rr(x,y,L12,L23,elbow):
    num = np.power( L12, 2 ) + np.power( L23 , 2 ) - np.power( x , 2 ) - np.power( y , 2 )
    den = 2 * L12 * L23
    arg = num / den
    if elbow == "down":
        q2 = np.pi - np.arccos(arg)
    elif elbow == "up" :
        q2 = - np.arccos(- arg)
    else:
        print("wrong format,insert 1:up,or 2:down")
    q1 = np.arctan2(y,x) - np.arctan2((L23 * np.sin(q2)) , (L12 + L23 * np.cos(q2)))

    return q1,q2

@jit(forceobj=True)
def euler_angles(R):
    phi = np.arctan2(R[1,2] , R[0,2])
    theta = np.arctan2( np.power(np.power(R[0,2], 2) + np.power(R[1,2] ,2) ,1/2), R[2,2] )
    psi = np.arctan2( - R[2,1], R[2,1] )
    angles = np.asarray( [phi, theta, psi] )
    return angles

@jit(forceobj=True)
def mdot(*args):
    T = np.eye( args[0].shape[0] )
    for arg in args:
        T = np.dot( T , arg )
    return T

def T_DH(d_n,theta_n,r_n,alpha_n):
    ctheta = np.cos( theta_n )
    calpha = np.cos( alpha_n )
    stheta = np.sin( theta_n )
    salpha = np.sin( alpha_n )

    T = np.asarray  ( [ [ctheta ,   -stheta*calpha  , stheta*salpha     , r_n * ctheta  ] ,
                        [stheta ,   ctheta*calpha   , -ctheta*salpha    , r_n*stheta    ] ,
                        [0      ,   salpha          , calpha            , d_n           ] ,
                        [0      ,   0               , 0                 ,1              ] ]
                    )

    return T

import numpy,from numpy,from scipy,from numba


import numpy as np

def skew(a):
    """Returns skew symmetric matrix, given a 3-vector"""
    return np.array([
        [    0, -a[2],  a[1]],
        [ a[2],     0, -a[0]],
        [-a[1],  a[0],     0]
        ])

def rot1(t):
    return np.array([
        [1, 0, 0],
        [0, np.cos(t), -np.sin(t)],
        [0, np.sin(t),  np.cos(t)]
        ])

def rot2(t):
    return np.array([
        [np.cos(t), 0, np.sin(t)],
        [0, 1, 0],
        [-np.sin(t), 0, np.cos(t)]
        ])

def rot3(t):
    return np.array([
        [np.cos(t), -np.sin(t), 0],
        [np.sin(t), np.cos(t), 0],
        [0, 0, 1]
        ])

def rot2D(t):
    return np.array([
        [np.cos(t), -np.sin(t)],
        [np.sin(t),  np.cos(t)]
        ])

def euler2rot(phi, theta, psi):
    return rot3(psi) @ rot2(theta) @ rot1(phi)

def rot2euler(R):
    """Compute Euler angles from rotation matrix.
    yaw, pitch, roll: 3, 2, 1 rot sequence
    Note frame relationship: e^b = e^v R^{vb}
    """
    psi = np.arctan2(R[1, 0], R[0, 0]) # yaw angle
    theta = np.arcsin(-R[2, 0])        # pitch angle
    phi = np.arctan2(R[2, 1], R[2, 2]) # roll angle
    return (phi, theta, psi)

def conj(q):
    return np.concatenate([[q[0]], -q[1:]])

def prod(p, q):
    """Compute product of two quaternions"""
    p0 = p[0]; p = p[1:4]
    q0 = q[0]; q = q[1:4]
    pq0 = p0*q0 - np.dot(p, q)
    pq = p0*q + p*q0 + np.cross(p,q)
    return np.concatenate([[pq0], pq])

def euler2quat(phi, theta, psi):
    psi2 = psi/2
    theta2 = theta/2
    phi2 = phi/2
    return np.array([
        np.sin(phi2)*np.sin(psi2)*np.sin(theta2) + np.cos(phi2)*np.cos(psi2)*np.cos(theta2),
        np.sin(phi2)*np.cos(psi2)*np.cos(theta2) - np.sin(psi2)*np.sin(theta2)*np.cos(phi2),
        np.sin(phi2)*np.sin(psi2)*np.cos(theta2) + np.sin(theta2)*np.cos(phi2)*np.cos(psi2),
        -np.sin(phi2)*np.sin(theta2)*np.cos(psi2) + np.sin(psi2)*np.cos(phi2)*np.cos(theta2)
        ])

def quat2rot(q):
    """Compute rotation matrix from quaternion.
    quaternion must be provided in form [q0, q]
    """
    q = q.flatten()
    q0 = q[0]
    q = q[1:]
    return (q0**2 - np.dot(q, q))*np.eye(3) + 2*np.outer(q,q) + 2*q0*skew(q)

def quat2euler(q):
    R = quat2rot(q)
    return rot2euler(R)

if __name__ == "__main__":
    # Tests

    # Check quat_prod
    p = np.array([3, 1, -2, 1])
    q = np.array([2, -1, 2, 3])
    pq = np.array([8, -9, -2, 11])
    print('pq_hand - pq = ', np.linalg.norm(prod(p,q)-pq))

    # Check conversions rot <-> quat
    phi = 2*np.pi * np.random.random_sample()
    theta = 2*np.pi * np.random.random_sample()
    psi = 2*np.pi * np.random.random_sample()
    R = euler2rot(phi=phi, theta=theta, psi=psi)
    q = euler2quat(phi=phi, theta=theta, psi=psi)
    R_ = quat2rot(q)
    print('R - R_ = ',np.linalg.norm(R-R_))

    # Check defs w and qdot
    w = np.random.rand(4); w[0]=0
    qdot = 0.5 * prod(q, w)
    qc = conj(q)
    w_ = 2 * prod(qc, qdot)
    print('w - w_ = ', np.linalg.norm(w-w_))

    phi_, theta_, psi_ = quat2euler(q)
    print("phi - phi_ = ", phi - phi_)
    print("theta - theta_ = ", theta - theta_)
    print("psi - psi_ = ", psi - psi_)


from sympy import *
from difference_quotient import difference_quotient
import numpy as np

def spline3_interpolate(points: list, simplify_result=True):
    """三弯矩法插值

    三弯矩法进行三次样条插值。使用自由边界条件。

    Args:
        points: list, [(x1, y1), (x2, y2), ..., (xn, yn)]
        simplify_result: bool, 化简最终结果, default True

    Returns:
        L: sympy Piecewise object of Symbol('x'), 插值多项式 $L(x)$
    """
    # 排序给定的点
    ps = sorted(points, key=lambda p: p[0])
    n = len(points)

    # points to a function
    _f_dict = dict(ps)

    def f(x):
        return _f_dict[x]

    # $h_k = x_{k+1} - x_k$
    def h(k):
        return ps[k+1][0] - ps[k][0]

    hks = [h(0)]

    # 用方程 D * M = d 解出 M

    D = np.zeros((n, n))
    d = np.zeros(n)

    for k in range(1, n-1):
        # $h_k$, $h_{k-1}$
        hks.append(h(k))
        hk, hks1 = hks[k], hks[k-1]

        # $\mu_k$ -> mu, $\lambda_k$ -> ld
        _fra = hks1 + hk
        mu = hks1 / _fra
        ld = hk / _fra

        # $\mu_kM_{k-1}+2M_k+\lambda_kM_{k+1} = d_k$
        D[k, k-1] = mu
        D[k, k] = 2
        D[k, k+1] = ld
        d[k] = 6 * difference_quotient(f, [ps[k-1][0], ps[k][0], ps[k+1][0]])

    # 边界条件
    # Natural Boundary
    D[0, 0] = 1
    D[n-1, n-1] = 1

    d[0] = 0
    d[n-1] = 0

    # 解出 M
    M = np.linalg.solve(D, d)

    # 插值函数
    piecewises = []
    for k in range(n-1):
        s = M[k] * (ps[k+1][0] - _x) ** 3 / (6 * hks[k])
        s += M[k+1] * (_x - ps[k][0]) ** 3 / (6 * hks[k])
        s += (ps[k][1] - M[k] * hks[k]**2 / 6) * (ps[k+1][0] - _x) / hks[k]
        s += (ps[k+1][1] - M[k+1] * hks[k]**2 / 6) * (_x - ps[k][0]) / hks[k]
        if simplify_result:
            s = simplify(s)
        piecewises.append((s, And(_x >= ps[k][0], _x <= ps[k+1][0])))

    return Piecewise(*piecewises)


import numpy as np
# Setting the random seed, feel free to change it and see different solutions.
np.random.seed(42)

def sigmoid(x):
    return 1/(1+np.exp(-x))
def sigmoid_prime(x):
    return sigmoid(x)*(1-sigmoid(x))
def prediction(X, W, b):
    return sigmoid(np.matmul(X,W)+b)
def error_vector(y, y_hat):
    return [-y[i]*np.log(y_hat[i]) - (1-y[i])*np.log(1-y_hat[i]) for i in range(len(y))]
def error(y, y_hat):
    ev = error_vector(y, y_hat)
    return sum(ev)/len(ev)

def dErrors(X, y, y_hat):
    dErrs = []
    yDiff = [(y_hat[i] - y[i]) for i in range(len(y))]
    # With respect to all wi
    for j in range(len(X[0])):
        dErrs.append([X[i][j] * yDiff[i]  for i in range(len(y))])
    # with respect to b
    dErrs.append(yDiff)
    return dErrs

def gradientDescentStep(X, y, W, b, learn_rate = 0.01):
    y_hat = prediction(X,W,b)
    e = sum(error_vector(y, y_hat))
    gradient = dErrors(X, y, y_hat)
    W = [W[i]-sum(gradient[i]) * learn_rate for i in range(len(W))]
    b -= sum(gradient[2])*learn_rate
    return W, b, e

# This function runs the perceptron algorithm repeatedly on the dataset,
# and returns a few of the boundary lines obtained in the iterations,
# for plotting purposes.
# Feel free to play with the learning rate and the num_epochs,
# and see your results plotted below.
def trainLR(X, y, learn_rate = 0.01, num_epochs = 100):
    x_min, x_max = min(X.T[0]), max(X.T[0])
    y_min, y_max = min(X.T[1]), max(X.T[1])
    # Initialize the weights randomly
    W = np.array(np.random.rand(2,1))*2 -1
    b = np.random.rand(1)[0]*2 - 1
    # These are the solution lines that get plotted below.
    boundary_lines = []
    errors = []
    for i in range(num_epochs):
        # In each epoch, we apply the gradient descent step.
        W, b, error = gradientDescentStep(X, y, W, b, learn_rate)
        boundary_lines.append((-W[0]/W[1], -b/W[1]))
        errors.append(error)
    return boundary_lines, errors


"""
Authors: Luiz Gustavo Mugnaini Anselmo (nUSP: 11809746)
         Victor Manuel Dias Saliba     (nUSP: 11807702)
         Luan Marc Suquet Camargo      (nUSP: 11809090)

Computacao III (CCM): Ep 3 QR factorization
"""
import math
import numpy as np

class RealSpace:
    """Real Space methods"""

    @classmethod
    def inner_product(cls, v: np.ndarray, u: np.ndarray) -> float:
        """Standard real dot product `v` * `u`"""
        if v.size != u.size:
            raise Exception("Vectors have different length")
        return sum(x * y for x, y in zip(v, u))

    @classmethod
    def norm(cls, v: np.ndarray) -> float:
        """Norm of given vector `v`"""
        return math.sqrt(cls.inner_product(v, v))

    @classmethod
    def distance(cls, v: np.ndarray, u: np.ndarray) -> float:
        """Distance between vectors `v` and `u`"""
        return math.sqrt(cls.inner_product(v, u))

    @classmethod
    def proj(cls, v: np.ndarray, u: np.ndarray) -> np.ndarray:
        """Projection of `v` in `u`"""
        return cls.inner_product(u, v) / cls.inner_product(u, u) * u

    @classmethod
    def gram_schmidt(cls, set: list[np.ndarray]) -> list[np.ndarray]:
        """Gram-Schmidt algorithm.
        Takes a list of vectors (`set`) and applies the method of Gram-Schmidt,
        orthonormalizing `set`.
        """
        # Orthogonalization algorithm
        ortho: list[np.ndarray] = [np.copy(set[0]) / cls.norm(set[0])]
        for j in range(1, len(set)):
            q = np.copy(set[j])
            for i in range(j):
                r = cls.inner_product(set[j], ortho[i])
                q -= r * ortho[i]

            # Check if the list is LI and, if so, append the normalized vector
            norm = cls.norm(q)
            if norm == 0:
                raise Exception("The list of vectors is linearly dependent.")
            ortho.append(q / norm)

        return ortho


import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs

class KMeans:

    def __init__(self , k , nbr_iterations = 500):
        self.k = k
        self.nbr_iterations = nbr_iterations
        self.clusters = [[] for i in range(self.k)]
        self.centers = []

    def fit(self , x):
        self.x = x
        self.nbr_samples , self.nbr_features = x.shape

        #initialize The Centers Randomly
        centers_indexes = np.random.choice(self.nbr_samples , self.k , replace = False)
        self.centers = [self.x[index] for index in centers_indexes]

        for i in range(self.nbr_iterations):

            #Assign Samples to the clossest Center
            self.clusters = self.CreateClusters(self.centers)

            oldCenters = self.centers
            self.centers = self.UpdateCenters(self.clusters)

            #Check if The Stoping Criteria is True
            if self.StopingCriteria(oldCenters , self.centers):
                break

    def CreateClusters(self , centers):
        CurrentClusters = [[] for i in range(self.k)]

        for index , sample in enumerate(self.x):
            clossestCenter = self.clossest_center(sample , centers)
            CurrentClusters[clossestCenter].append(index)
        return CurrentClusters

    def clossest_center(self , sample , centers):
        distances = [self.euclideanDistance(sample, center) for center in centers]
        return np.argmin(distances)

    def UpdateCenters(self , clusters):
        newCenters = np.zeros((self.k , self.nbr_features))
        for index , cluster in enumerate(clusters):
            center = np.mean(self.x[cluster] , axis = 0)
            newCenters[index] = center
        return newCenters

    def StopingCriteria(self , oldCenters , newCenters):
        distances = [self.euclideanDistance(oldCenters[i], newCenters[i]) for i in range(self.k)]
        return sum(distances) == 0

    def getClusters(self):
        labels = np.empty(self.nbr_samples)

        for index , cluster in enumerate(self.clusters):
            for sampleIndex in cluster:
                labels[sampleIndex] = index
        return labels

    def displayTheResult(self):

        fig, ax = plt.subplots(figsize=(12, 8))
        #display The Clusters
        for _ , index in enumerate(self.clusters):
            points = self.x[index]
            ax.scatter(points[:,0] , points[:,1])
        #display The Centers
        for center in self.centers:
            ax.scatter(center[0] , center[1], marker="x", color="black", linewidth=5)
        plt.title("KMeans")
        plt.show()

    def euclideanDistance(self , x1 , x2):
        return np.sqrt(np.sum( (x1 - x2)**2 ))

#Test K-Means
def accuracy(y_true , y_pred):
    return np.sum(y_true == y_pred) / len(y_true)

x , y = make_blobs(n_samples=100 , n_features=2 , centers=3 , random_state=0)

nbr_classes = len(np.unique(y))
K_means = KMeans(k = nbr_classes)
K_means.fit(x)
y_pred = K_means.getClusters()
K_means.displayTheResult()
print("KMeans Accuracy : ",accuracy(y, y_pred))


import numpy as np
import matplotlib.pylab as plt

# sigmoid function used broadcast for numpy array.
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

a = np.array([-1.0, 1.0, 2.0])

print(sigmoid(a))

# 이와 같은 시그모이드 함수를 그래프로 나타내 그 변화를 살펴보자.

x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # y축의 범위 지정
plt.show()


# To add a new cell, type '#%%'
# To add a new markdown cell, type '#%% [markdown]'
#%%
from IPython import get_ipython

#%%
import numpy as np

#%%
list1 = [1, 3, 4, 5]
a = np.array(list1)
a

#%%
type(a)

#%%
a.dtype

#%%
a.size

#%%
a.ndim

#%%
a.shape

#%%
b = np.array([23, 7.4, 11, 98, 54, 23.67])
type(b)

#%%
b.ndim

#%%
b.size

#%%
b.shape

#%% [markdown]
# ## Indexing and Slicing

#%%
c = np.array([22, 56, 98, 54, 21])
c

#%%
#assign 56 to 34
c[1] = 34
c

#%%
# slicing
d = c[1:3]
d

#%%
d[0:2] = 66, 77
d

#%% [markdown]
# ## Basic operation
#%% [markdown]
# ### Vector Addition and subtraction

#%%
v1 = np.array([0, 1])
v2 = np.array([1, 0])
v1 + v2

#%%
## anothr way
u = np.array([1, 0])
v = np.array([0, 1])
z = []

for n,m in zip(u, v):
    z.append(n+m)
z

#%% [markdown]
# ### Array multiplication with scaler

#%%
y = np.array([1, 2])
z = 2*y
z

#%%
## another way
zz = []
for n in y:
    zz.append(2*n)
zz

#%% [markdown]
# ### Product of two vectors

#%%
tt1 = np.array([1, 2])
tt2 = np.array([3, 4])

tt = tt1 * tt2
tt

#%%
## another way
tt3 = []
for n,m in zip(tt1, tt2):
    tt3.append(n*m)
tt3

#%% [markdown]
# ### Dot product

#%%
ff1 = np.array([1, 2])
ff2 = np.array([3, 4])

result = np.dot(ff1, ff2)
result

#%%
## another way
result2 = []
for n,m in zip(ff1, ff2):
    result2.append(np.dot(n, m))
result2

#%% [markdown]
# ### Adding constant to numpy array
#%% [markdown]
#

#%%
gg = np.array([2, 3, 4, -9])
res1 = gg + 1
res1

#%% [markdown]
# ## Universal Function

#%%
fg = np.array([3, 5, 7, 9])

#%%
fg.mean()

#%%
fg.max()

#%%
fg.min()

#%%
## pi value
np.pi

#%%
# np.sin(x)
#
#

#%%
# starting value = -2 and ending value= 2 total values 5
np.linspace(-2, 2, num=5)

#%%
np.linspace(-2, 2, num=9)

#%%
## plotting mathamatical function
x = np.linspace(0, 2*np.pi, 50)
x

#%%
y = np.sin(x)
y

#%%
import matplotlib.pyplot as plt

#%%
get_ipython().run_line_magic('matplotlib', 'inline')
plt.plot(x, y)
#first input x is horizontal axis
#second element y is vertical axis

#%% [markdown]
# # Two Dimenstional Array

#%%
a = np.array([[1, 2, 3], [11, 12, 13], [21, 22, 23], [31, 32, 33]])

#%%
a.ndim

#%%
a.shape

#%%
a.size

#%%
# to access first row 3rd element
a[0][2]

#%%
# alternate
a[0, 2]

#%%
## slicing in numpy array
a[0, 0:2]

#%%
## adding as matrix
as1 = np.array([
    [1, 2, 3, 4],
    [2, 4, 6, 8],
    [1, 3, 5, 7]
])
as2 = np.array([
    [1, 2, 3, 4],
    [2, 4, 6, 8],
    [1, 3, 5, 7]
])

#%%
as1 + as2

#%%
as1.ndim

#%%
as1

#%%
as1 * 2

#%%
# matrix multiplication is littile bit complex
# matrix A rows must b equal to matrix B column
# each element of A is multiply with all element of B column

#%%
dfA = np.array([[1, 2, 3], [2, 0, 1]])
dfB = np.array([[1, 2], [0, 1], [3, 4]])

#%%
dfZ = np.dot(dfA, dfB)

#%%
dfZ

#%%
X=np.array([[1,0],[0,1]])
Y=np.array([[2,2],[2,2]])
Z=np.dot(X,Y)
Z

#%%


from __future__ import division
import numpy
import scipy.integrate
import matplotlib.pyplot as pyplot

USER = 'Maciej Grabias'
USER_ID = 'njgh39'

r = 0.15 # radius of the cannonball in meters
rho_iron =  7874.00 # density of iron in kg m-3
g =  9.81 # acceleration due to gravity in ms-2
kappa =  0.47 # drag coefficient of a sphere
rho_air =  1.23 # density of air in kg m-3
t1 =  25.00 # end time for our ODE integration in s
v0 = 125.00 # launch speed in m s-1
n_panels =  400 # the number of panels to use

# cross section area & mass of cannonball determined using above

area = numpy.pi* r**2
mass = rho_iron * (4/3) * numpy.pi * r**3

# define the function
def f((x, y, vx, vy),t):

    # gravitational forces
    Fx_grav = 0
    Fy_grav = - mass * g

    # drag forces
    Fx_drag = - kappa * rho_air * area * v0 * vx
    Fy_drag = - kappa * rho_air * area * v0 * vy

    d_x = vx # dx/dt
    d_y = vy # dy/dt
    d_vx =  Fx_drag / mass # dvx/dt
    d_vy = (Fy_drag + Fy_grav) / mass # dvy/dt

    return numpy.array((d_x, d_y, d_vx, d_vy))

def solve_euler(state, t1, n_panel):

    history = numpy.zeros((n_panels, len(state)))
    dt = t1 / n_panels

    # integrate with Euler method
    for i in range(n_panels):
        history[i] = state
        state = state + f(state, dt * i) * dt
    return history

# define timebase
timebase = numpy.arange(0, t1, t1 / n_panels)

def trim_trajectory(values):

    # process trajectory to terminate when below y = 0
    for i in range(len(values) - 1):
        x0, y0, vx0, vy0 = values[i]
        x1, y1, vx1, vy1 = values[i + 1]
        if y0 < 0: return values[:i]
    return values

proj_range = []
thetas = range(5,90,5)

pyplot.subplot(211)

for theta in thetas:

    vx, vy = numpy.cos(numpy.deg2rad(theta)) * v0, numpy.sin(numpy.deg2rad(theta)) * v0
    initial_conditions = (0, 0, vx, vy)

    values_scipy = scipy.integrate.odeint(f, initial_conditions, timebase)
    values_euler = solve_euler(initial_conditions, t1, n_panels)
    values_scipy = trim_trajectory(values_scipy)
    values_euler = trim_trajectory(values_euler)

    # calculate range

    x_first, y_first, vx_first, vy_first = values_scipy[0]
    x_final, y_final, vx_final, vy_final = values_scipy[-1]

    rnge = x_final - x_first

    proj_range.append(rnge)

    # trajectory for scipy integrate
    x_scipy = values_scipy[:, 0]
    y_scipy = values_scipy[:, 1]

    # trajectory for Euler integrate
    x_euler = values_euler[:,0]
    y_euler = values_euler[:,1]

    # plot trajectories
    pyplot.plot(x_scipy, y_scipy, color = 'grey', label = "Odeint")
    pyplot.plot(x_euler, y_euler, color = 'blue', linestyle = '--', label = "Euler")
    pyplot.title("Trajectories for Euler & Odeint Methods")
    pyplot.xlabel("X coordinate")
    pyplot.ylabel("Y coordinate")

pyplot.legend(("Odeint", "Euler"))

# plot range VS launch angle
pyplot.subplot(212)
pyplot.plot(thetas, proj_range, color = 'red')
pyplot.title("Range VS Launch Angle")
pyplot.xlabel("Launch Angle, deg")
pyplot.ylabel("Range, m")

pyplot.show()

ANSWER1 = """ The angle from the horizontal for maximum range under these
conditions is 40 degrees."""
ANSWER2 = """ The angle decreases with increasing air density """


# solutions.py
"""Volume 1, Lab 16: Importance Sampling and Monte Carlo Simulations.
Solutions file. Written by Tanner Christensen, Winter 2016.
"""

from __future__ import division
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats

def prob1(n):
    """Approximate the probability that a random draw from the standard
    normal distribution will be greater than 3."""
    h = lambda x : x > 3
    X = np.random.randn(n)
    return 1/n * np.sum(h(X))

def prob2():
    """Answer the following question using importance sampling:
            A tech support hotline receives an average of 2 calls per
            minute. What is the probability that they will have to wait
            at least 10 minutes to receive 9 calls?
    Returns:
        IS (array) - an array of estimates using
            [5000, 10000, 15000, ..., 500000] as number of
            sample points."""
    h = lambda y : y > 10
    f = lambda y : stats.gamma(a=9,scale=0.5).pdf(y)
    g = lambda y : stats.norm(loc=12,scale=2).pdf(y)
    num_samples = np.arange(5000,505000,5000)
    IS = []
    for n in num_samples:
        Y = np.random.normal(12,2,n)
        approx = 1./n*np.sum(h(Y)*f(Y)/g(Y))
        IS.append(approx)
    IS = np.array(IS)
    return IS

def prob3():
    """Plot the errors of Monte Carlo Simulation vs Importance Sampling
    for the prob2()."""
    h = lambda x : x > 10
    MC_estimates = []
    for N in xrange(5000,505000,5000):
        X = np.random.gamma(9,scale=0.5,size=N)
        MC = 1./N*np.sum(h(X))
        MC_estimates.append(MC)
    MC_estimates = np.array(MC_estimates)

    IS_estimates = prob2()

    actual = 1 - stats.gamma(a=9,scale=0.5).cdf(10)

    MC_errors = np.abs(MC_estimates - actual)
    IS_errors = np.abs(IS_estimates - actual)

    x = np.arange(5000,505000,5000)
    plt.plot(x, MC_errors, color='r', label="Monte Carlo")
    plt.plot(x, IS_errors, color='b', label="Importance Sampling")
    plt.legend()
    plt.show()

def prob4():
    """Approximate the probability that a random draw from the
    multivariate standard normal distribution will be less than -1 in
    the x-direction and greater than 1 in the y-direction."""
    h = lambda y : y[0] < -1 and y[1] > 1
    f = lambda y : stats.multivariate_normal(np.zeros(2), np.eye(2)).pdf(y)
    g = lambda y : stats.multivariate_normal(np.array([-1,1]), np.eye(2)).pdf(y)

    n = 10**4
    Y = np.random.multivariate_normal(np.array([-1,1]), np.eye(2), size=n)
    hh = np.apply_along_axis(h, 1, Y)
    ff = np.apply_along_axis(f, 1, Y)
    gg = np.apply_along_axis(g, 1, Y)
    approx = 1./n*np.sum(hh*ff/gg)

    return approx

if __name__ == "__main__":
    import numpy as np
    print prob4()

    n = 10**6
    h = lambda y : y[0] < -1 and y[1] > 1
    X = np.random.multivariate_normal(np.zeros(2),np.eye(2),n)
    print 1/n * np.sum(np.apply_along_axis(h, 1, X))


# -*- coding: utf-8 -*-
import numpy as np

"""
Funcție care caută intervalele pe care funcția are o soluție.
f(a) * f(b) < 0 -> EXISTENȚA
"""
def cauta_intervale(f, a, b, n):
    """
    Parameters
    ----------
    f : funcția asociată ecuației f(x)=0.
    a : capătul din stânga interval.
    b : capătul din dreapta interval.
    n : nr de subintervale în care împărțim intervalul global (a, b).

    Returns
    -------
    Matricea 'intervale' cu 2 linii; prima linie -> capăt st interval curent
    si a doua linie -> capat dr
    si un nr de coloane = nr radacini
    """

    x = np.linspace(a, b, n+1)   #returnează n+1 numere, situate la distanțe egale, din cadrul intervalului [a, b]
    for i in range(len(x)):   #range: for i = 0, len(x); i++
        if(f(x[i]) == 0):     #capetele intervalelor mele nu au voie să fie 0; tb să avem soluțiile în intervale, nu la capete
            print("Schimba nr de Intervale")
            exit(0)

    matrice = np.zeros((2, 1000))  #returnează un nou vector plin de 0; pt că am (2, 1000) -> matrice cu 2 rânduri și 1000 coloane
    z = 0
    for i in range(n):
        if f(x[i]) * f(x[i+1]) < 0:  #existență soluție
            matrice[0][z] = x[i]
            matrice[1][z] = x[i + 1]
            z += 1

    matrice_finala = matrice[:, 0:z]   #iei ambele 2 linii și doar coloanele de la 0 la z (numărat mai sus)
    return matrice_finala

"""
    Funcție care implementează algoritmul metodei bisecției.
"""
def bisectie(f, xmin, xmax, eps):
    """
    Parameters
    ----------
    f : f(x) = 0.
    xmin, xmas: capete intervale.
    eps : toleranța / eroarea (epsilon).

    Returns
    -------
    Soluția aproximativă, dar și numărul de iterații N necesar pt a obține soluția cu eroarea eps.
    """

    c = (xmin + xmax) / 2
    N = np.floor(np.log2((xmax-xmin)/eps))  #floor: cel mai mare int, dar mai mic decât val. mea
    for i in range(int(N)):
        if f(c) == 0:
            break                           #am gasit soluția
        elif f(xmin) * f(c) < 0:
            xmax = c
        elif f(xmin) * f(c) > 0:
            xmin = c

        c = (xmin + xmax) / 2
    return c, N

"""
    Funcție care implementează algoritmul metodei bisecției.
    ---> Pt Lab2.Ex4.
"""
def bisectie2(f, xmin, xmax, eps):

    x_old = (xmin + xmax) / 2
    k = 1
    while True:
        if f(x_old) == 0:
            x_new = x_old
            break
        elif f(xmin) * f(x_old) < 0:
            xmax = x_old
        elif f(xmin) * f(x_old) > 0:
            xmin = x_old

        x_new = (xmin + xmax) / 2
        k += 1
        if abs(x_new - x_old) / abs(x_old) < eps:
            break
        x_old = x_new

    return x_new, k

"""
    Metoda Newton-Raphson
"""
def NewtonRap(f, df, x0, eps):
    """
    Parameters
    ----------
    f : functia pt care cautam solutia f(x) = 0.
    df : derivata functiei.
    x0 : valoare de pornire.
    eps : epsilon / toleranta.

    Returns
    -------
    solutia (xk), nr de iteratii (N).
    """

    x_old = x0
    N = 1
    while True:
        #Calculăm noua iteratie
        x_new = x_old - (f(x_old) / df(x_old))
        N += 1
        if(abs(x_new - x_old) / abs(x_old) < eps):
            break
        x_old = x_new

    return x_new, N

Calcul Numeric (CN)/Laborator/Laborator 1, 2/metode_numerice_ecuatii_algebrice.py


import matplotlib.pyplot as plt
import numpy as np
from scipy.fftpack import fft, ifft

N = 256
M = 63
f0 = 1000
fs = 10000
A0 = .8
hN = N//2
hM = (M+1)//2
fftbuffer = np.zeros(N)
X1 = np.zeros(N, dtype='complex')
X2 = np.zeros(N, dtype='complex')

x = A0 * np.cos(2*np.pi*f0/fs*np.arange(-hM+1,hM))

plt.figure(1, figsize=(9.5, 7))
w = np.hanning(M)
plt.subplot(2,3,1)
plt.title('w (hanning window)')
plt.plot(np.arange(-hM+1, hM), w, 'b', lw=1.5)
plt.axis([-hM+1, hM, 0, 1])

fftbuffer[:hM] = w[hM-1:]
fftbuffer[N-hM+1:] = w[:hM-1]
X = fft(fftbuffer)
X1[:hN] = X[hN:]
X1[N-hN:] = X[:hN]
mX = 20*np.log10(abs(X1))

plt.subplot(2,3,2)
plt.title('mW')
plt.plot(np.arange(-hN, hN), mX, 'r', lw=1.5)
plt.axis([-hN,hN,-40,max(mX)])

pX = np.angle(X1)
plt.subplot(2,3,3)
plt.title('pW')
plt.plot(np.arange(-hN, hN), np.unwrap(pX), 'c', lw=1.5)
plt.axis([-hN,hN,min(np.unwrap(pX)),max(np.unwrap(pX))])

plt.subplot(2,3,4)
plt.title('xw (windowed sinewave)')
xw = x*w
plt.plot(np.arange(-hM+1, hM), xw, 'b', lw=1.5)
plt.axis([-hM+1, hM, -1, 1])

fftbuffer = np.zeros(N)
fftbuffer[0:hM] = xw[hM-1:]
fftbuffer[N-hM+1:] = xw[:hM-1]
X = fft(fftbuffer)
X2[:hN] = X[hN:]
X2[N-hN:] = X[:hN]
mX2 = 20*np.log10(abs(X2))

plt.subplot(2,3,5)
plt.title('mXW')
plt.plot(np.arange(-hN, hN), mX2, 'r', lw=1.5)
plt.axis([-hN,hN,-40,max(mX)])

pX = np.angle(X2)
plt.subplot(2,3,6)
plt.title('pXW')
plt.plot(np.arange(-hN, hN), np.unwrap(pX), 'c', lw=1.5)
plt.axis([-hN,hN,min(np.unwrap(pX)),max(np.unwrap(pX))])

plt.tight_layout()
plt.savefig('sine-spectrum.png')
plt.show()


def newton_raph(fx, theta_0, thresh, x_data):
    '''
        Function to implement the Newton Raphson method
    '''

    #get the first and second derivative
    d1 = sp.diff(fx, theta)
    d2 = sp.diff(d1, theta)

    print('\nLatex Equations')
    print('Original Function:  ', sp.latex(fx))
    print('First Derivative:   ', sp.latex(d1))
    print('Second Derivative:  ',sp.latex(d2))

    #initialize the first derivative
    d_sub = d1.subs(theta, theta_0)
    d1_lam = sp.lambdify( x, d_sub, "numpy")
    d_1 = sum((d1_lam(x_data)))

    #Iterate using the Newton Raphson method while theta is < thresh
    n_iterations = 0    #Number of iterations to complete

    while abs(d_1) > thresh:
        n_iterations +=1
        d1_sub = d1.subs(theta, theta_0)
        d1_lam = sp.lambdify( x, d1_sub, "numpy")
        d_1 = np.sum((d1_lam(x_data)))

        d2_sub = d2.subs(theta, theta_0)
        d2_lam = sp.lambdify( x, d2_sub, "numpy")
        d_2 = np.sum((d2_lam(x_data)))

        #assign new value to theta for next iteration
        theta_0 = theta_0 - d_1/d_2

    theta_hat = theta_0
    print('\nNumber of Iterations: ', n_iterations)
    print('theta hat: ', theta_hat)
    return theta_hat

if __name__ == "__main__":
    import sympy as sp
    from sympy.abc import i
    import numpy as np
    import pandas as pd

    #Sample 1
    #create synthetic data
    n=10
    j=np.arange(1,n+1,1)
    x_data = -3.1 + 6*j/n

    #declare the sympy function
    theta = sp.Symbol('theta')
    x = sp.Symbol('x')

    fx = 1551*theta - sp.Sum( (sp.exp(theta*x) ),(i, 0, 9))
    theta_init = 3    #initial guess of theta
    thresh = 1e-10    #threshold for theta

    #Sample 2 (work in progress)
# =============================================================================
#     data = pd.read_csv(r'data/test_data.csv')
#     x_data = data['x']
#     y_data = data['y']
#
#     theta = sp.Symbol('theta')
#     x = sp.Symbol('x')
#     y = sp.symbol('y')
#
#     fx = 1/2 * sp.sum( (y-sp.exp(theta*x))**2),(i, 0, len(x_data-1)))
# =============================================================================

    #run function
    newton_raph(fx=fx, theta_0=theta_init, thresh=thresh, x_data = x_data)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jul  2 13:54:12 2020

Function for time series novelty score calculation.
"""

import numpy as np

def _create_kernel(edge, sigma=1.0, mu=0.0):
    """
    Create a (2*edge+1) x (2*edge+1) gaussian kernel.

    Parameters
    ----------
    edge : int, optional
        Gaussian kernel window length / 2.
    sigma : float, optional
        Variance for the gaussian kernel construction. The default is 1.0.
    mu : float, optional
        Mean for the gaussian kernel construction. The default is 0.0.

    Returns
    -------
    kernel : numpy ndarray
        2D gaussian convolution kernel.

    """
    assert isinstance(edge,int), "Edge is not an integer."
    assert edge > 0, "Edge should be positive, non zero integer."

    grid = np.linspace(-1, 1, 2*edge + 1)
    x,y = np.meshgrid(grid,grid)
    d = np.sqrt(x**2 + y**2)
    gaussian_mat = np.exp(-((d - mu)**2 / (2.0 * sigma**2)))

    kernel_grid = np.sign(np.linspace(-edge, edge, 2*edge +1))
    signs = np.outer(kernel_grid, kernel_grid)
    signed_gaussian = signs * gaussian_mat
    kernel = signed_gaussian / np.sum(np.abs(signed_gaussian))

    return kernel

def compute_novelty(simmat, edge = 7, sigma=1.0, mu = 0.0):
    """
    Compute novelty score using the self similarity matrix and gaussian
    checkerboard convolution kernel, calculating the convolution along the
    self similarity matrix diagonal.

    Parameters
    ----------
    simmat : numpy ndarray
        N x N self similarity matrix.
    edge : float, optional
        Gaussian kernel window length / 2. The default is 7.
    sigma : float, optional
        Variance for the gaussian kernel construction. The default is 1.0.
    mu : float, optional
        Mean for the gaussian kernel construction. The default is 0.0.

    Returns
    -------
    nov : numpy ndarray
        1D novelty score vector.
    kernel : numpy ndarray
        2D gaussian convolution kernel.

    """

    assert isinstance(simmat,np.ndarray), "Self similarity matrix is not a numpy array."
    assert np.ndim(simmat) == 2, "Self similarity matrix is not 2-dimensional."
    assert simmat.shape[0] == simmat.shape[1], "Self similarity matrix is not square."
    assert 2*edge + 1 <= simmat.shape[0], "Kernel size is larger than the self similarity matrix."

    kernel = _create_kernel(edge, sigma, mu)

    N = simmat.shape[0]
    M = 2*edge + 1

    novelty = np.zeros(N)

    simmat_padded  = np.pad(simmat,edge,mode='constant')

    for i in range(N):
        novelty[i] = np.sum(simmat_padded[i:i+M, i:i+M] * kernel)

    return novelty, kernel


import numpy as np
from numpy import exp, sqrt, cos, pi, sin

from base_function import BaseFunction

class Levi(BaseFunction):
    target_E = 0.
    xmin = np.array([-10.,-10.])
    xmax = np.array([10.,10.])

    def getEnergy(self, coords):
        x, y = coords
        E = sin(3.*pi*x)**2 + (x-1.)**2 * (1. + sin(3*pi*y)**2) \
            + (y-1.)**2 * (1. + sin(2*pi*y)**2)
        return E

    def getEnergyGradient(self, coords):
        x, y = coords
        E = self.getEnergy(coords)

        dEdx = 2.*3.*pi* cos(3.*pi*x) * sin(3.*pi*x) + 2.*(x-1.) * (1. + sin(3*pi*y)**2)

        dEdy = (x-1.)**2 * 2.*3.*pi* cos(3.*pi*y) * sin(3.*pi*y) + 2. *  (y-1.) * (1. + sin(2*pi*y)**2) \
            + (y-1.)**2 * 2.*2.*pi * cos(2.*pi*y) * sin(2.*pi*y)

        return E, np.array([dEdx, dEdy])

if __name__ == "__main__":
    f = Levi()
    f.test_potential(f.get_random_configuration())

    from base_function import makeplot2d
    makeplot2d(f)


import numpy as np
import numpy.linalg as LA

def proj(v1,v2):
  '''
  Function to compute projection of v2 on v1
  '''
  alpha = np.dot(v1,v2)/np.dot(v1,v1)                                                  # proj(a,b) = aTb/aTa
  return alpha

def Gram_Schmid(M):
  '''
  Given any (m x n) matrix,M this function computers corresponding orthonormal matrix B such that,
  1. BTB = I
  2. C(M) = C(B) i.e Columnspace will be same
  '''
  row,col = M.shape
  Q = []                                                                               # List to append all orthogonal column vector later to be normalized
  for c in range(col):
    column_vector = M[:,c]
    if c == 0:                                                                         # First column vector is itself taken to be orthogonal hence append
      Q.append(column_vector)
    else:
      for ortho_col_vec in Q:                                                          # For all previous orthogonal column vectors do operation on current column vector
        temp_col_vec = column_vector.copy()
        column_vector -= np.multiply(proj(ortho_col_vec,temp_col_vec),ortho_col_vec)   # B = B - proj(A,B)*A for all A in Q
      if (LA.norm(column_vector)!=0):                                                  # If column vector is linearly independent then add to Q
        Q.append(column_vector)
  # Orthonormalizartion of Q
  B = []                                                                               # Output Orthonormalized Matrix
  for col_vec in Q:
    B.append(col_vec/LA.norm(col_vec))
  return np.array(B).T

A = np.array([[1,1],
              [1,0],
              [1,2]],dtype=np.float32)

B = Gram_Schmid(A)
print(B)
print(np.matmul(B.T,B))


import numpy as np

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

a, b, c = np.split(x, (3,6))

print("a = ",a)
print("b = ",b)
print("c = ",c)

##

k = np.arange(16).reshape(4,4)

print("k = ",k)

upperar, lowerar = np.vsplit(k, [2]) #vsplit stands for vertical split

print("upperar = ",upperar)
print("lowerar = ",lowerar)

upperar2, lowerar2 = np.hsplit(k, [2]) #hsplit stands for horizontal split

print("upperar2 = ",upperar2)
print("lowerar2 = ",lowerar2)

##

l = np.random.normal(10, 2, (3,3))

print("l = ",l)

print(np.sort(l, axis=0)) #sorted column-wise
print(np.sort(l, axis=1)) #sorted row-wise

##

t = np.arange(10, 20)

print("t = ",t)
print("t[0::2] = ",t[0::2])
print("t[1::3] = ",t[1::3])

##

d = np.random.randint(-1, 10, (5,5))

print("d = ", d)

print(d[:, 0]) #all row are selected, whereas only the first column is selected
print(d[:, 3]) #same here, just another column is selected
print(d[2, :])
print(d[0:3, 0:2])

##

f = np.random.randint(0, 10, size= (5, 5))

print("f = ",f)

copy = f[0:1, 0:3].copy()
#copy function provides to not changing the original values of the main array (which is f here)
print("copy = ",copy)
print("f = ",f)

##

#fancy index

g = np.arange(15, 0, -2)
print("g = ", g)

bring = [2, 5, 3]
print("g[bring] = ",g[bring])

##

h = np.arange(16).reshape(4,4)
print("h = ", h)

row = np.array([2, 1])
column = np.array([2, 2])

print("h[row, column] =", h[row, column])
print("h[2, [1, 2]] =", h[2, [1, 2]])
print("h[0:, h[1, 2]] =" , h[0:, [1, 2]])

##

j = np.arange(10)
print(j)

print(j<6)
print(j[j!=4])

##

# 4*x1 + x2 = 15
# 6*x1 + 2*x2 = 8   solve the two equations

q = np.array([[4, 1], [6, 2]])
w = np.array([15, 8])

print(np.linalg.solve(q, w))


import matplotlib.pyplot as plt
import numpy as np
import scipy.io as scio

import estimateGaussian as eg
import multivariateGaussian as mvg
import visualizeFit as vf
import selectThreshold as st

plt.ion()
# np.set_printoptions(formatter={'float': '{: 0.6f}'.format})

# ===================== Part 1: Load Example Dataset =====================
# We start this exercise by using a small dataset that is easy to visualize.
#
# Our example case consists of two network server statistics across
# several machines: the latency and throughput of each machine.
# This exercise will help us find possibly faulty (or very fast) machines
#

print('Visualizing example dataset for outlier detection.')

#  The following command loads the dataset. You should now have the
#  variables X, Xval, yval in your environment.
data = scio.loadmat('ex8data1.mat')
X = data['X']
Xval = data['Xval']
yval = data['yval'].flatten()

# Visualize the example dataset
plt.figure()
plt.scatter(X[:, 0], X[:, 1], c='b', marker='x', s=15, linewidth=1)
plt.axis([0, 30, 0, 30])
plt.xlabel('Latency (ms)')
plt.ylabel('Throughput (mb/s')

input('Program paused. Press ENTER to continue')

# ===================== Part 2: Estimate the dataset statistics =====================
# For this exercise, we assume a Gaussian distribution for the dataset.
#
# We first estimate the parameters of our assumed Gaussian distribution,
# then compute the probabilities for each of the points and then visualize
# both the overall distribution and where each of the points falls in
# terms of that distribution
#
print('Visualizing Gaussian fit.')

# Estimate mu and sigma2
mu, sigma2 = eg.estimate_gaussian(X)

# Returns the density of the multivariate normal at each data point(row) of X
p = mvg.multivariate_gaussian(X, mu, sigma2)

# Visualize the fit
vf.visualize_fit(X, mu, sigma2)
plt.xlabel('Latency (ms)')
plt.ylabel('Throughput (mb/s')

input('Program paused. Press ENTER to continue')

# ===================== Part 3: Find Outliers =====================
# Now you will find a good epsilon threshold using a cross-validation set
# probabilities given the estimated Gaussian distribution
#
pval = mvg.multivariate_gaussian(Xval, mu, sigma2)

epsilon, f1 = st.select_threshold(yval, pval)
print('Best epsilon found using cross-validation: {:0.4e}'.format(epsilon))
print('Best F1 on Cross Validation Set: {:0.6f}'.format(f1))
print('(you should see a value epsilon of about 8.99e-05 and F1 of about 0.875)')

# Find outliers in the training set and plot
outliers = np.where(p < epsilon)
plt.scatter(X[outliers, 0], X[outliers, 1], marker='o', facecolors='none', edgecolors='r')

input('Program paused. Press ENTER to continue')

# ===================== Part 4: Multidimensional Outliers =====================
# We will now use the code from the previous part and apply it to a
# harder problem in which more features describe each datapoint and only
# some features indicate whether a point is an outlier.
#

# Loads the second dataset.
data = scio.loadmat('ex8data2.mat')
X = data['X']
Xval = data['Xval']
yval = data['yval'].flatten()

# Apply the same steps to the larger dataset
mu, sigma2 = eg.estimate_gaussian(X)

# Training set
p = mvg.multivariate_gaussian(X, mu, sigma2)

# Cross Validation set
pval = mvg.multivariate_gaussian(Xval, mu, sigma2)

# Find the best threshold
epsilon, f1 = st.select_threshold(yval, pval)

print('Best epsilon found using cross-validation: {:0.4e}'.format(epsilon))
print('Best F1 on Cross Validation Set: {:0.6f}'.format(f1))
print('# Outliers found: {}'.format(np.sum(np.less(p, epsilon))))
print('(you should see a value epsilon of about 1.38e-18, F1 of about 0.615, and 117 outliers)')

input('ex8 Finished. Press ENTER to exit')


'''
Computing percentiles
100xp
In this exercise, you will compute the percentiles of petal length of Iris versicolor.

Instructions
-Create percentiles, a NumPy array of percentiles you want to compute. These are the 2.5th,
25th, 50th, 75th, and 97.5th. You can do so by creating a list containing these ints/floats
and convert the list to a NumPy array using np.array(). For example, np.array([30, 50]) would
create an array consisting of the 30th and 50th percentiles.
-Use np.percentile() to compute the percentiles of the petal lengths from the Iris versicolor
samples. The variable versicolor_petal_length is in your namespace.
-Print the percentiles.
'''

import numpy as np
import seaborn as sns

versicolor_petal_length = np.array([4.7,  4.5,  4.9,  4.,  4.6,  4.5,  4.7,  3.3,  4.6,  3.9,  3.5,
                                    4.2,  4.,  4.7,  3.6,  4.4,  4.5,  4.1,  4.5,  3.9,  4.8,  4.,
                                    4.9,  4.7,  4.3,  4.4,  4.8,  5.,  4.5,  3.5,  3.8,  3.7,  3.9,
                                    5.1,  4.5,  4.5,  4.7,  4.4,  4.1,  4.,  4.4,  4.6,  4.,  3.3,
                                    4.2,  4.2,  4.2,  4.3,  3.,  4.1])

# Specify array of percentiles: percentiles
percentiles = np.array([2.5, 25, 50, 75, 97.5])

# Compute percentiles: ptiles_vers
ptiles_vers = np.percentile(versicolor_petal_length, percentiles)

# Print the result
print(ptiles_vers)


# MIT License
#
# Copyright (c) 2021 Playtika Ltd.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import numpy as np

def cohens_d(mu_1, mu_2, std):
    """

    Compute the standardized effect size as difference between the two means divided by the standard deviation.

    Parameters
    ----------
    mu_1 : float
        Mean of the first sample.
    mu_2 : float
        Mean of the second sample.
    std : float > 0
        Pooled standard deviation. It assumes that the variance of each population is the same.

    Returns
    -------
    effect_size : float
        Effect size as cohen's d coefficient
    """

    return (mu_1 - mu_2) / std

def cohens_h(p1, p2):
    """

    Compute the effect size as measure of distance between two proportions or probabilities. It is the difference
    between their arcsine transformations

    Parameters
    ----------
    p1 : float in interval (0,1)
        Proportion or probability of the first sample.
    p2 : float in interval (0,1)
        Proportion or probability of the second sample.

    Returns
    -------
    effect_size : float
        Effect size as cohen's h coefficient
    """

    return abs(2 * np.arcsin(np.sqrt(p1)) - 2 * np.arcsin(np.sqrt(p2)))

def pooled_std(sample1, sample2):
    """

    Compute pooled standard deviation between two samples.

    Parameters
    ----------
    sample1 : array_like
        Observation of first sample
    sample2 : array_like
        Observation of second sample

    Returns
    -------
    pooled_std : float > 0
        p-value for the test
    """
    # Compute the size of samples
    n1, n2 = len(sample1), len(sample2)

    # Compute the variance of the samples
    std1, std2 = np.var(sample1, ddof=1), np.var(sample2, ddof=1)

    # Compute the pooled standard deviation
    return np.sqrt(((n1 - 1) * std1 + (n2 - 1) * std2) / (n1 + n2 - 2))


import numpy as np
import csv

# =============================================================================
# Models
# =============================================================================

def least_squares_GD(y, tx, initial_w,
                     max_iters, gamma, verbose=False):
    """Least squares with MSE loss and Gradient Descent."""
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = mse_grad(y, tx, w)
        loss = mse(y, tx, w)
        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}".format(
                    bi=n_iter, ti=max_iters - 1, l=loss))

    return ws[-1], losses[-1]

def least_squares_SGD(y, tx, initial_w,
                      max_iters, gamma, verbose=False):
    """Least squares with MSE loss and Stochasitc Gradient Descent."""
    ws = [initial_w]
    losses = []
    w = initial_w
    for n_iter in range(max_iters):
        for minibatch_y, minibatch_tx in batch_iter(y, tx, batch_size=1):
            # Compute gradient and loss
            g = mse_grad(minibatch_y, minibatch_tx, w)
            loss = mse(minibatch_y, minibatch_tx, w)
            # Update the weights
            w = w - gamma * g
            w = w.ravel()
            # store w and loss
            ws.append(w)
            losses.append(loss)
            if verbose:
                print("Stochastic gradient descent({bi}/{ti}): loss={l}".format(
                    bi=n_iter, ti=max_iters - 1, l=loss))

    return ws[-1], mse(y, tx, ws[-1])

def least_squares(y, tx):
    """Linear regression fit using normal equations."""
    a = tx.T @ tx
    b = tx.T @ y
    w = np.linalg.solve(a, b)
    loss = mse(y, tx, w)
    return w, loss

def ridge_regression(y, tx, lambda_):
    """ Ridge regression fit using normal equations """
    a = (tx.T @ tx) + lambda_*2*tx.shape[0] * np.eye(tx.shape[1])
    b = tx.T @ y
    w = np.linalg.solve(a, b)
    return w, mse(y, tx, w)

def logistic_regression(y, tx, initial_w, max_iters,
                        gamma, verbose=False):
    """Logistic regression with log loss and Gradient Descent."""
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = logistic_grad(y, tx, w)
        loss = logistic_error(y, tx, w)
        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}".format(
                    bi=n_iter, ti=max_iters - 1, l=loss))

    return ws[-1], losses[-1]

def reg_logistic_regression(y, tx, lambda_, reg, initial_w,
                            max_iters, gamma, verbose=False,
                            early_stopping=True, tol = 0.0001,
                            patience = 5):
    """Regularized logistic regression with log loss and Gradient Descent with early stopping"""
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = reg_logistic_grad(y, tx, w, lambda_, reg)
        loss = reg_logistic_error(y, tx, w, lambda_, reg)

        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}".format(
                    bi=n_iter, ti=max_iters - 1, l=loss))

        # Early stopping
        if (early_stopping) and (n_iter > patience):
            # Check if loss has improved by tol in last patience iters
            l_pat = reg_logistic_error(y, tx, ws[-patience], lambda_, reg)
            l_1 = reg_logistic_error(y, tx, ws[-1], lambda_, reg)
            if ((l_pat - l_1) < tol):
                print(f"Stopped after {n_iter} it.")
                break

    return ws[-1], losses[-1]

# =============================================================================
# Cost functions
# =============================================================================

def mse(y, tx, w):
    """Mean squared error loss function."""
    e = y - tx @ w
    return (1/(2*tx.shape[0])) * np.sum(e**2)

def logistic_error(y, tx, w):
    """Log loss function."""
    a = sigmoid(tx @ w)
    loss = - (1 / tx.shape[0]) * np.sum((y * np.log(a)) + ((1 - y) * np.log(1 - a)))
    return loss

def reg_logistic_error(y, tx, w, lambda_, reg):
    """Log loss function with regularization term."""
    assert (reg==1 or reg==2), "reg needs to be 1 or 2"
    loss = logistic_error(y, tx, w) + lambda_ * (np.linalg.norm(w, reg) ** reg)
    return loss

# =============================================================================
# Gradients
# =============================================================================

def mse_grad(y, tx, w):
    """Compute gradient for MSE loss."""
    e = y - tx @ w
    return (-1/tx.shape[0]) * tx.T @ e

def logistic_grad(y, tx, w):
    """Compute gradient for log loss."""
    e = sigmoid(tx @ w) - y
    return (1/tx.shape[0]) * tx.T @ e

def reg_logistic_grad(y, tx, w, lambda_, reg):
    """Compute gradient for log loss with regularization."""
    assert (reg==1 or reg==2), "reg needs to be 1 or 2"
    if (reg==1):
        # L1 regularization
        return logistic_grad(y, tx, w) + lambda_ * np.sign(w)
    else:
        # L2 regularization
        return logistic_grad(y, tx, w) + 2 * lambda_ * w

# =============================================================================
# Activation functions
# =============================================================================

def sigmoid(x):
    """Compute sigmoid function."""
    epsilon = 1E-12
    a = 1 / (1 + np.exp(-x))
    a = np.where(np.isclose(a, 0.0), epsilon, a)
    a = np.where(np.isclose(a, 1.0), (1-epsilon), a)
    return a

# =============================================================================
# Helpers
# =============================================================================

def batch_iter(y, tx, batch_size, num_batches=1, shuffle=True):
    # Please note this code was provided to us during the lab sessions.
    """
    Generate a minibatch iterator for a dataset.
    Takes as input two iterables (here the output desired values 'y' and the input data 'tx')
    Outputs an iterator which gives mini-batches of `batch_size` matching elements from `y` and `tx`.
    Data can be randomly shuffled to avoid ordering in the original data messing with the randomness of the minibatches.
    Example of use :
    for minibatch_y, minibatch_tx in batch_iter(y, tx, 32):
        <DO-SOMETHING>
    """
    data_size = len(y)

    if shuffle:
        shuffle_indices = np.random.permutation(np.arange(data_size))
        shuffled_y = y[shuffle_indices]
        shuffled_tx = tx[shuffle_indices]
    else:
        shuffled_y = y
        shuffled_tx = tx
    for batch_num in range(num_batches):
        start_index = batch_num * batch_size
        end_index = min((batch_num + 1) * batch_size, data_size)
        if start_index != end_index:
            yield shuffled_y[start_index:end_index], shuffled_tx[start_index:end_index]

def import_data(path="data/"):
    """
    Import csv files and return array of X,y and vector of the column names.
    """
    train = np.loadtxt(
        f"{path}train.csv",
        delimiter = ",",
        skiprows=0,
        dtype=str
    )

    test = np.loadtxt(
        f"{path}test.csv",
        delimiter = ",",
        skiprows=0,
        dtype=str
    )

    col_names = train[0,:]

    # Remove column names
    train = np.delete(train, obj=0, axis=0)
    test = np.delete(test, obj=0, axis=0)

    # Map 0 & 1 to label
    label_idx = np.where(col_names == "Prediction")[0][0]
    train[:,label_idx] = np.where(train[:,label_idx]=="s", 1, 0)

    test[:,label_idx] = 0

    # Replace -999 with nan
    train = train.astype(np.float32)
    train[train == -999] = np.nan

    test = test.astype(np.float32)
    test[test == -999] = np.nan
    return train, test, col_names

def create_csv_submission(ids, y_pred, name):
    # Please note this code was provided to us during the lab sessions.
    """
    Creates an output file in csv format for submission to AIcrowd
    Arguments: ids (event ids associated with each prediction)
               y_pred (predicted class labels)
               name (string name of .csv output file to be created)
    """
    with open(name, 'w') as csvfile:
        fieldnames = ['Id', 'Prediction']
        writer = csv.DictWriter(csvfile, delimiter=",", fieldnames=fieldnames)
        writer.writeheader()
        for r1, r2 in zip(ids, y_pred):
            writer.writerow({'Id':int(r1),'Prediction':int(r2)})

def standardize_numpy(x, mean=None, std=None):
    """Standardize the original data set. Works on numpy arrays."""
    if mean is None: mean = x.mean(axis=0, keepdims=True)
    x = x - mean
    if std is None: std = x.std(axis=0, keepdims=True)
    x = x / std
    return x, mean, std

# =============================================================================
# Prepare features
# =============================================================================

def split_X_y(train, test, cols):
    """Create tx matrix for train & test + y vector for train."""
    idx_id = np.where(cols=="Id")[0][0]
    idx_pred = np.where(cols=="Prediction")[0][0]

    tx_train = np.delete(train, [idx_id, idx_pred], axis=1)
    y_train = train[:,idx_pred].copy()
    tx_test = np.delete(test, [idx_id, idx_pred], axis=1)

    return tx_train, y_train, tx_test

def build_poly(x, degree):
    """Polynomial basis functions for each column of x, for j=1 up to j=degree, and single constant term."""
    if (degree < 0): raise ValueError("degree must be positive")

    phi = np.empty((x.shape[0], x.shape[1]*degree+1))

    # Constant term
    phi[:,-1] = 1

    # Higher order terms
    for j in range(x.shape[1]):
        phi[:,j*degree] = x[:,j]
        for d in range(1,degree):
            col = j*degree+d
            phi[:,col] = phi[:,col-1] * x[:,j]

    return phi

def prepare_features(tx_nan, degree, mean_nan=None, mean=None, std=None):
    """Clean and prepare for learning.  Mean imputing, missing value indicator, standardize."""
    # Get column means, if necessary
    if mean_nan is None: mean_nan = np.nanmean(tx_nan,axis=0)

    # Replace NaNs
    tx_val = np.where(np.isnan(tx_nan), mean_nan, tx_nan)

    # Polynomial features
    tx = build_poly(tx_val, degree)
    const_col = tx.shape[1]-1

    # Add NaN indicator columns
    nan_cols = np.flatnonzero(np.any(np.isnan(tx_nan), axis=0))

    ind_cols = np.empty((tx_nan.shape[0], nan_cols.shape[0]))
    ind_cols = np.where(np.isnan(tx_nan[:,nan_cols]), 1, 0)

    tx = np.c_[tx, ind_cols]

    # Standardize
    tx, mean, std = standardize_numpy(tx, mean, std)
    tx[:,const_col] = 1.0

    return tx, mean, std, mean_nan, nan_cols

# =============================================================================
# Performance metrics
# =============================================================================

def logistic_prediction(tx, w):
    """Make a prediction with logistic regression model."""
    return np.rint(sigmoid(tx @ w))

def regression_prediction(tx, w):
    """Make a prediction with linear regression model."""
    return tx @ w

def f1_score(y_targ, y_pred):
    """Compute the F1 score of a prediction."""
    mask_targ = (y_targ == 1)
    mask_pred = (y_pred == 1)

    # Total positives
    total_pred = np.count_nonzero(mask_pred)
    total_targ = np.count_nonzero(mask_targ)

    # True positives
    true_pos = np.count_nonzero(mask_pred[mask_targ])

    if (true_pos == 0) or (total_pred == 0) or (total_targ == 0):
        return 0.0

    precision = true_pos / total_pred
    recall = true_pos / total_targ

    # Compute F1 score
    score = 2*(precision*recall)/(precision+recall)

    return score

def accuracy(y_targ, y_pred):
    """Compute the accuracy of a prediction"""
    total_wrong = np.count_nonzero(y_targ-y_pred)
    return 1.0 - (total_wrong / len(y_pred))


import numpy as np
import scipy.signal as sig
import math
import cv2
from advanced_fields.computer_vision.utils import scale_0_255

img = np.array([[0, 90, 0],
                [105, 0, 55],
                [0, 40, 0]])

# img = cv2.imread('../../../datasets/per_field/cv/color_lady.jpg', cv2.IMREAD_GRAYSCALE)

# Note:
# Convolution reverses the direction of one of the functions it works on.
# one function is parameterized with τ and the other with -τ.
#   reference: https://en.wikipedia.org/wiki/Convolution#Definition
# the desired kernel must be flipped (K[::-1,::-1])) the in both axes to get the expected result.
# how it's done:
# desired_kernel = [[0, 1, 2],
#                   [3, 4, 5],
#                   [6, 7, 8]]
# used_kernel = np.array(desired_kernel)[::-1,::-1]
# used_kernel = np.flip(desired_kernel, axis=(0,1))

kernel_x = np.array([[-1, 0, 1]])[::-1, ::-1]
# kernel_x = np.flip([[-1, 0, 1]], axis=1)
kernel_y = np.array([[1], [0], [-1]])[::-1, ::-1]
# kernel_y = np.flip([[1], [0], [-1]], axis=0)

GV_x = sig.convolve2d(img, kernel_x, mode='valid')[1, 0]       # returns [[0], [-50], [0]]
GV_y = sig.convolve2d(img, kernel_y, mode='valid')[0, 1]       # returns [[0, 50, 0]]

# Gradient Vector:
GV = np.array([[GV_x], [GV_y]])
print(f'Gradient Vector: \n {GV}')

# Gradient Vector's Magnitude:
# GV_M = math.sqrt(GV_x ** 2 + GV_y ** 2)
GV_M = np.sqrt(GV_x ** 2 + GV_y ** 2)
print(f"Gradient Vector's Magnitude: {GV_M}")

# Gradient Vector's Direction (\ angle):
# GV_theta = math.degrees(math.atan(GV_y / GV_x))
# GV_theta = np.arctan(GV_y / GV_x) * 180 / np.pi
GV_theta = np.degrees(np.arctan(GV_y / GV_x))
print(f"Gradient Vector's Direction: {GV_theta}°")

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

# cv2 implementation
img = cv2.imread('../../../datasets/per_field/cv/color_man_2004.jpg')
# img = np.float32(img) / 255.0  # scaling

GV_x_sobel = scale_0_255(cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=1))  # cv2.CV_8U, ksize=5
GV_y_sobel = scale_0_255(cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=1))  # cv2.CV_8U, ksize=5

GV_M, GV_theta = cv2.cartToPolar(GV_x_sobel, GV_y_sobel, angleInDegrees=True)