import pandas as pd
import numpy as np
import numpy.linalg as ln
from matplotlib import pylab as plt

def out(filename, s):
	with open(filename, 'w') as f:
		f.write(s)

def mserror(y, y_pred):
	return (1 / float(y.size)) * np.sum((y - y_pred) ** 2)

def normal_equation(X, y):
	return ln.solve(X.T.dot(X), X.T.dot(y))

def linear_prediction(X, w):
	return X.dot(w)

def stochastic_gradient_step(X, y, w, train_ind, eta=0.01):
	mult = (2 * eta / float(X.shape[0])) * (y[train_ind] - X[train_ind, :].dot(w))
	w_new = np.zeros(X.shape[1])
	w_new[0] = (w[0] + mult)
	for i in xrange(1, X.shape[1]):
		w_new[i] = (w[i] + mult * X[train_ind, i])
	return w_new

def stochastic_gradient_descent(X, y, w_init, eta=1e-2, max_iter=1e4,
								min_weight_dist=1e-8, seed=42, verbose=False):
	weight_dist = np.inf
	w = w_init
	errors = []
	iter_num = 0
	np.random.seed(seed)

	while weight_dist > min_weight_dist and iter_num < max_iter:
		random_ind = np.random.randint(X.shape[0])
		w_new = stochastic_gradient_step(X, y, w, random_ind, eta)

		weight_dist = np.linalg.norm(w_new - w)
		w = w_new
		errors.append(mserror(y, linear_prediction(X, w)))
		iter_num += 1

	return w, errors

data = pd.read_csv('advertising.csv')

y = data['Sales'].values
X = data.drop('Sales', axis=1).values

means, stds = np.mean(X, axis=0), np.std(X, axis=0)
X = (X - means) / stds
X = np.hstack((np.ones((X.shape[0], 1)), X))

y_pred = np.full(y.shape, np.median(y))
err = mserror(y, y_pred)
res = '%.3f' % err
out('1_1.txt', res)

norm_eq_weights = normal_equation(X, y)
res = '%.3f' % norm_eq_weights.dot(np.mean(X, axis=0))
out('1_2.txt', res)

y_pred = linear_prediction(X, norm_eq_weights)
err = mserror(y, y_pred)
res = '%.3f' % err
out('1_3.txt', res)

stoch_grad_desc_weights, stoch_errors_by_iter = stochastic_gradient_descent(X, y, np.zeros(X.shape[1]), max_iter=1e5)
plt.plot(range(len(stoch_errors_by_iter)), stoch_errors_by_iter)
plt.xlabel('Iteration number')
plt.ylabel('MSE')
plt.show()

print stoch_grad_desc_weights
print stoch_errors_by_iter[-1]

y_pred = linear_prediction(X, stoch_grad_desc_weights)
err = mserror(y, y_pred)
res = '%.3f' % err
print res
out('1_4.txt', res)


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

#%%
u = np.linspace(-2,2,65)
v = np.linspace(-1,1,33)

#%%
X,Y = np.meshgrid(u,v)

#%%
Z = X**2 /25 + Y**2/4
# Z = np.sin(X) + np.cos(Y)

#%%
plt.pcolor(Z)
plt.colorbar()
plt.show()

#%%
plt.pcolor(X, Y, Z,  cmap='gray') # show the meshgrid to label
plt.colorbar()
plt.axis('tight')
plt.show()

#%%
a = [1,2,3,4]
b = [10,11,12]
A,B = np.meshgrid(a,b)

#%%
plt.contour(X, Y, Z, 30, cmap='autumn') # show the meshgrid to label
plt.colorbar()
plt.axis('tight')
plt.show()

#%%
plt.contourf(X, Y, Z, 30) # show the meshgrid to label
plt.colorbar()
plt.axis('tight')
plt.show()

#%%
plt.subplot(2,2,1)
plt.pcolor(Z)
plt.colorbar()

plt.subplot(2,2,2)
plt.pcolor(X, Y, Z,  cmap='gray') # show the meshgrid to label
plt.colorbar()

plt.subplot(2,2,3)
plt.contour(X, Y, Z, 30, cmap='autumn') # show the meshgrid to label
plt.colorbar()

plt.subplot(2,2,4)
plt.contourf(X, Y, Z, 30) # show the meshgrid to label
plt.colorbar()

# plt.axis('tight')
plt.tight_layout()
plt.show()

#%%


import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
from numpy import pi
plt.rcParams['lines.linewidth'] = 3
plt.rcParams['font.size'] = 30
plt.rcParams['figure.figsize'] = [30,14]
path = '/home/rosinante/PDS/PDS_UFCG/python/questao3/'
#%% Function
t = sp.symbols('t')
f = sp.cos(2*np.pi*3200*t) + 0.5*sp.cos(2*np.pi*600*t) + 0.01*sp.cos(2*np.pi*300*t)
x = sp.lambdify(t,f,'numpy')
#%% Continuous
L = 10
Fs = int(6000/L)
Ts = 1/Fs
t_sampled = np.linspace(0,1,Fs)
x_sampled = x(t_sampled)

#%% Sampled
F = 20*6400
T = 1/F
t_continuous = np.linspace(0,1,F)
x_continuous = x(t_continuous)

# %% FFT continuous
y_continuous = np.fft.fft(x_continuous)
y_continuous = np.abs(y_continuous)/np.max(np.abs(y_continuous))
nc = x_continuous.size
freq_continuous = np.fft.fftfreq(nc,d=T)#*T*2*pi

# %% FFT sampled
y_sampled = np.fft.fft(x_sampled)
y_sampled = np.abs(y_sampled)/np.max(np.abs(y_sampled))
n = x_sampled.size
freq_sampled = np.fft.fftfreq(n,d=Ts)#*Ts*2*pi

#%% plot FFT continuous
fig = plt.figure()
labels = ['-pi','-pi/2','0','pi/2','pi']
plt.plot(freq_continuous,y_continuous)
#plt.xticks(np.arange(-pi,pi+1,pi/2),labels)
plt.xlim([-3200-100,3200+100])
plt.xlabel('frequency (Hz)')
plt.title('Continuous Fourier transformed function')
fig.savefig(path+'fft_continuous.png')

#%% plot FFT sampled
fig = plt.figure()
plt.stem(freq_sampled,y_sampled)
#plt.xticks(np.arange(-pi,pi+1,pi/2),labels)
#plt.xlim([-3200-100,3200+100])
plt.grid(True)
plt.xlabel('frequency (Hz)')
plt.title('Sampled Fourier transformed function')
plt.legend(['Fs = '+str(Fs)+' Hz'])
fig.savefig(path+'fft_sampled_Fs_'+str(Fs)+'Hz.png')
#%%
fig = plt.figure()
plt.plot(freq_continuous,y_continuous,'k-')
plt.stem(freq_sampled,y_sampled,'r--')
#plt.xticks(np.arange(-pi,pi+1,pi/2),labels)
plt.xlim([-3200-100,3200+100])
plt.xlabel('frequency (Hz)')
plt.title('Continuous and Sampled Fourier transformed functions')
plt.grid(True)
plt.legend(['Continuous','Sampled - Fs = '+str(Fs)+' Hz'])
fig.savefig(path+'fft_both_Fs_'+str(Fs)+'Hz.png')


import numpy as np
import pandas as pd

############################################################################
# DO NOT MODIFY CODES ABOVE
# DO NOT CHANGE THE INPUT AND OUTPUT FORMAT
############################################################################

# Part 1.1
def mean_square_error(w, X, y):
    """
    Compute the mean square error of a model parameter w on a test set X and y.
    Inputs:
    - X: A numpy array of shape (num_samples, D) containing test features
    - y: A numpy array of shape (num_samples, ) containing test labels
    - w: a numpy array of shape (D, )
    Returns:
    - err: the mean square error
    """
    n, y_predicted = len(y), np.dot(X, w)
    err = np.sum(np.power(y - y_predicted, 2), dtype=np.float) / n
    return err

# Part 1.2
def linear_regression_noreg(X, y):
    """
    Compute the weight parameter given X and y.
    Inputs:
    - X: A numpy array of shape (num_samples, D) containing features
    - y: A numpy array of shape (num_samples, ) containing labels
    Returns:
    - w: a numpy array of shape (D, )
    """
    # w* = (X^T X)^(-1) X^T y
    w = np.dot(np.dot(np.linalg.inv(np.dot(X.T, X)), X.T), y)
    return w

# Part 1.3
def regularized_linear_regression(X, y, lambd):
    """
    Compute the weight parameter given X, y and lambda.
    Inputs:
    - X: A numpy array of shape (num_samples, D) containing features
    - y: A numpy array of shape (num_samples, ) containing labels
    - lambd: a float number specifying the regularization parameter
    Returns:
    - w: a numpy array of shape (D, )
    """
    D = np.shape(X)[1]
    I = np.eye(D, k=0)
    w = np.dot(np.dot(np.linalg.inv(np.dot(X.T, X)+lambd*I), X.T), y)
    return w

# Part 1.4
def tune_lambda(Xtrain, ytrain, Xval, yval):
    """
    Find the best lambda value.
    Inputs:
    - Xtrain: A numpy array of shape (num_training_samples, D) containing training features
    - ytrain: A numpy array of shape (num_training_samples, ) containing training labels
    - Xval: A numpy array of shape (num_val_samples, D) containing validation features
    - yval: A numpy array of shape (num_val_samples, ) containing validation labels
    Returns:
    - bestlambda: the best lambda you find among 2^{-14}, 2^{-13}, ..., 2^{-1}, 1.
    """
    lambds, best_loss, best_lambda = [pow(2.0, -x) for x in range(15)], float('inf'), None
    for lambd in lambds:
        w = regularized_linear_regression(X=Xtrain, y=ytrain, lambd=lambd)
        mse = mean_square_error(w=w, X=Xval, y=yval)
        if mse < best_loss:
            best_lambda = lambd
            best_loss = mse

    return best_lambda

# Part 1.5
def mapping_data(X, P):
    """
    Augment the data to [X, X^2, ..., X^p]
    Inputs:
    - X: A numpy array of shape (num_training_samples, D) containing training features
    - P: An integer that indicates the degree of the polynomial regression
    Returns:
    - X: The augmented dataset. You might find np.insert useful.
    """
    raw = X
    for p in range(2, P+1):
        x_poly = np.power(raw, p)
        X = np.column_stack((X, x_poly))

    return X

"""
NO MODIFICATIONS below this line.
You should only write your code in the above functions.
"""


# -*- coding: utf-8 -*-
# Created on Thu Feb 27 14:58:01 2020
# @author: arthurd

import numpy as np

def softmax(X):
    """
    Compute and return the softmax of the input.

    Parameters
    ----------
    X : numpy.ndarray
        Inputs of floats with shape [n, m]

    Returns
    -------
    numpy.ndarray
        Outputs of floats with shape [n, m]
    """

    exp = np.exp(X)
    t = X - np.log(np.sum(exp, axis=0))
    S = np.exp(t)
    return S

sigmoid = lambda X: 1.0 / (1.0 + np.exp(-X))

tanh = lambda X: np.tanh(X)

relu = lambda X: np.maximum(0, X)

leaky_relu = lambda X: np.where(X > 0, X, X * 0.01)

linear = lambda X: X


# -*- coding: utf-8 -*-
"""
@author: salimt
"""
#Problem 1: Curve Fitting
#15/15 points (graded)
#Implement the generate_models function.
#
#x and y are two lists corresponding to the x-coordinates and y-coordinates of the data samples (or data points); for example, if you have N data points, x = [x1 , x2 , ..., xN ] and y = [y1 , y2 , ..., yN ], where x_i and y_i are the x and y coordinate of the i-th data points. In this problem set, each x coordinate is an integer and corresponds to the year of a sample (e.g., 1997); each corresponding y coordinate is a float and represents the temperature observation (will be computed in multiple ways) of that year in Celsius. This representation will be used throughout the entire problem set.
#degs is a list of integers indicating the degree of each regression model that we want to create. For each model, this function should fit the data (x,y) to a polynomial curve of that degree.
#This function should return a list of models. A model is the numpy 1d array of the coefficients of the fitting polynomial curve. Each returned model should be in the same order as their corresponding integer in degs.
#Example:
#
#print(generate_models([1961, 1962, 1963],[4.4,5.5,6.6],[1, 2]))
#Should print something close to:
#
#[array([ 1.10000000e+00, -2.15270000e+03]), array([ -8.86320195e-14, 1.10000000e+00, -2.15270000e+03])]
#The above example was generating a linear and a quadratic curve on data samples (xi, yi ) = (1961, 4.4), (1962, 5.5), and (1963, 6.6). The resulting models are in the same order as specified in degs. Note that it is fine you did not get the exact number because of numerical errors.
#
#Note: If you want to use numpy arrays, you should import numpy as np and use np.METHOD_NAME in your code. Unfortunately, pylab does not work with the grader
#
## Problem 1
#
def generate_models(x, y, degs):
    """
    Generate regression models by fitting a polynomial for each degree in degs
    to points (x, y).
    Args:
        x: a list with length N, representing the x-coords of N sample points
        y: a list with length N, representing the y-coords of N sample points
        degs: a list of degrees of the fitting polynomial
    Returns:
        a list of numpy arrays, where each array is a 1-d array of coefficients
        that minimizes the squared error of the fitting polynomial
    """
    import numpy as np

    models = []
    for d in degs:
        model = np.polyfit(x, y, d)
        models.append(model)
    return models

#print(generate_models([1961, 1962, 1963],[4.4,5.5,6.6],[1, 2]))
#[array([ 1.10000000e+00, -2.15270000e+03]), array([ -8.86320195e-14, 1.10000000e+00, -2.15270000e+03])]


import numpy as np
from math import factorial

def _sin(x):
    val = np.float64(0)
    terms = np.array([], dtype=np.float64)
    for n in range(41):
        term = (((-1) ** n) * (x ** (2*n+1)) / factorial(2*n + 1))
        terms = np.append(terms, term)
        val += term
    return val, terms

def debug_sin(x):
    val, terms = _sin(x)
    tmp = np.float64(0)
    for i, t in enumerate(terms):
        tmp += t
        print("for x_{0}, term is {1}, sin({2}) approximation is {3}".format(
            i, t, x, tmp))
    return val

def sin(x):
    v, _ = _sin(x)
    return v

def cos(x):
    val = np.float64(0)
    for n in range(41):
        term = (((-1) ** n) * (x ** (2*n)) / factorial(2*n))
        val += term
    return val

pi = np.float64(22./7.)
print("pi is initially {0}".format(pi))
for i in range(5):
    pi += sin(pi)
    print("improving pi iteration {0}, pi: {1}".format(i, pi))

if __name__ == "__main__":
    vals = np.array([0, pi / 4., pi / 2., pi * 3./4., pi], dtype=np.float64)
    for n in vals:
        print("sin({0}) = {1}".format(n, sin(n)))
        print("cos({0}) = {1}".format(n, cos(n)))


import numpy as np
import matplotlib.pyplot as plt
import time
import math

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

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

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

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

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

    return X.ravel()

def brev(inDat):
    N = len(inDat)
    outDat = []
    for k in range(N):
        revN = int('{:0{width}b}'.format(k, width=int(np.ceil(np.log2(N))))[::-1], 2)
        outDat.append(inDat[revN])
    return outDat

if __name__ == "__main__":
    fig, axes = plt.subplots(ncols=1, nrows=2)
    ax1, ax2 = axes.ravel()

    # generate an input signal
    Num = 1024
    fs = 1000
    t = np.linspace(0, Num / fs, Num)
    x_t = np.sin(2 * np.pi * 1 * t) + 0.5 * np.sin(2 * np.pi * 5 * t) + 0.2 * np.sin(2 * np.pi * 10 * t)

    # Generate the FFT twiddle factors

    # Compute the FFT with time measurement
    start_time = time.time()
    X_f = fft_own(x_t)
    stop_time = time.time()

    f = np.linspace(0, fs / 2, int(Num / 2))
    # Magnitude
    A_f = np.zeros(Num, dtype=float)
    for k in range(Num):
        A_f[k] = np.sqrt(np.power(X_f[k].imag, 2) + np.power(X_f[k].real, 2))

    elapsed_time = stop_time - start_time
    print("The time of execution of our DFT is: %.5f s." % elapsed_time)

    # plot input signal
    ax1.plot(t, x_t)
    ax1.margins(0)
    ax1.grid()
    # plot output signal
    ax2.plot(f, A_f[0:int(Num / 2)])
    plt.xscale("log")
    ax2.margins(0)
    ax2.grid()
    plt.show()


import numpy as np
import Matrix_2 as M2

"""
lin - Linear interpolation
Input: inp = a matrix containing all the x and y values (columns 0 and 1),
sorted in ascending order in x.
Output: array f whose elements i provide the y values for any x between x_i and
x_i+1 using a linear interpolation method.
"""

def lin(inp, stepsize = 0.01):
    x = inp[:, 0]
    y = inp[:, 1]
    f = np.array([])
    xsamp = np.array([])
    for i in np.arange(len(x)-1):
        xnew = np.arange(start = x[i], stop = x[i+1], step = stepsize)
        xsamp = np.append(xsamp, xnew)
        # Samples points at step size 0.01 between two adjacent data points
        # Note: last point of each section is not included, but it does not
        # matter because it is automatically the first point of the next,
        # EXCEPT the very last point
        for j in np.arange(len(xnew)):
            f = np.append(f, (((x[i+1] - xsamp[j + len(xsamp) - len(xnew)])*y[i]
            + (xsamp[j + len(xsamp) - len(xnew)] - x[i])*y[i+1]) / (x[i+1]-x[i])))
    xsamp = np.append(xsamp, x[-1]) # Adds in the last point because it is not
    # included in any interpolation
    f = np.append(f, y[-1])
    return xsamp, f

"""
cubspline - Cublic spline
Input: inp = a matrix containing all the x and y values (columns 0 and 1),
sorted in ascending order in x.
Output: array f whose elements i provide the y values for any x between x_i and
x_i+1 using a cubic spline method.
"""

def cubspline(inp, stepsize = 0.01, showcoeff = False, showb = False): # show the coefficient matrix
# or the array of constants
    x = inp[:, 0]
    y = inp[:, 1]
    fdashdash = np.array([0.]) # first second derivative = 0
    # Calculate the array contatining all the second derivatives of f at each
    # data point.
    coeff = np.zeros([len(x)-2, len(x)-2]) # Contains all the coefficients in
    # the fundamental equation in finding f". If we assume f"_0 = f"_n = 0
    # (natural spline), then this leaves us with n-1 unknowns in n-1 eqs.
    b = np.zeros(len(x)-2) # Contains the constant values
    for i in np.arange(len(x)-2):
        coeff[i, i] = (x[i+2] - x[i]) / 3 # Valid for all points
        if i > 0: # First point (i = 0) only has 2 coefficients as f"_0 = 0
            coeff[i, i-1] = (x[i+1] - x[i]) / 6
        if i < len(x)-3: # Last point (i = len(x) - 3) only has 2 coefficients as f"_n = 0
            coeff[i, i+1] = (x[i+2] - x[i+1]) / 6
        b[i] = (y[i+2] - y[i+1])/(x[i+2] - x[i+1]) - (y[i+1] -y[i])/(x[i+1] - x[i])
    if showcoeff == True:
        print(coeff)
    if showb == True:
        print(b)
    fdashdash = np.append(fdashdash, M2.solve(coeff, b))
    fdashdash = np.append(fdashdash, 0.) # last second derivative = 0

    # Now we solve for the cubic spline function f(x).
    f = np.array([])
    xsamp = np.array([])
    for i in np.arange(len(x)-1):
        xnew = np.arange(start = x[i], stop = x[i+1], step = stepsize)
        #xnew = np.linspace(x[i], x[i+1], 500)
        xsamp = np.append(xsamp, xnew)
        for j in np.arange(len(xnew)):
            A = (x[i+1] - xnew[j])/(x[i+1] - x[i])
            B = (xnew[j] - x[i])/(x[i+1] - x[i])
            C = 1./6.*(A**3-A)*((x[i+1] - x[i])**2)
            D = 1./6.*(B**3-B)*((x[i+1] - x[i])**2)
            f = np.append(f, A*y[i] + B*y[i+1] + C*fdashdash[i] + D*fdashdash[i+1])
    xsamp = np.append(xsamp, x[-1]) # Adds in the last point because it is not
    # included in any interpolation
    f = np.append(f, y[-1])
    return xsamp, f


#!/usr/bin/env python

"""
Machine Learning Online Class Coursera
Exercise 1: Linear regression with multiple variables
Modified with few changes,Solved the issue
Modified by github.com/utkarshmani1997
"""

# Initialization
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from ex1_utils import *

## ================ Part 1: Feature Normalization ================

print('Loading data ...','\n')

## Load Data
print('Plotting Data ...','\n')

data = pd.read_csv("ex1data2.txt",names=["size1","bedrooms","price"])
s = np.array(data.size1)
b = np.array(data.bedrooms)
p = np.array(data.price)
m = len(b) # number of training examples

# Design Matrix
s = np.vstack(s)
b = np.vstack(b)
X = np.hstack((s,b))

# Print out some data points
print('First 10 examples from the dataset: \n')
print(" size = ", s[:10],"\n"," bedrooms = ", b[:10], "\n")

input('Program paused. Press enter to continue.\n')

# Scale features to zero mean and standard deviation of 1
print('Normalizing Features ...\n')

X = featureNormalize(X)

# Add intercept term to X
X = np.hstack((np.ones_like(s),X))

## ================ Part 2: Gradient Descent ================

print('Running gradient descent ...\n')

# Choose some alpha value
alpha = 0.05
num_iters = 400

# Init Theta and Run Gradient Descent
theta = np.zeros(3)

# Multiple Dimension Gradient Descent
theta, hist = gradientDescentMulti(X, p, theta, alpha, num_iters)

# Plot the convergence graph
fig = plt.figure()
ax = plt.subplot(111)
plt.plot(np.arange(len(hist)),hist ,'-b')
plt.xlabel('Number of iterations')
plt.ylabel('Cost J')
plt.show()

# Display gradient descent's result
print('Theta computed from gradient descent: \n')
print(theta,'\n')

# Estimating the price of a house on the given size in square feet and no of bedrooms
siz=int(input("Program paused. Enter the size of house in sq-ft:"))
br=int(input("Program paused. Enter the no of bedrooms:"))

# Recall that the first column of X is all-ones. Thus, it does
# not need to be normalized.
normalized_specs = np.array([1,((siz-s.mean())/s.std()),((br-b.mean())/b.std())])
price = np.dot(normalized_specs,theta)

print('Predicted price of house with size {} and {} no of bedrooms (using gradient descent):\n '.format(siz, br),
      price)

input('Program paused. Press enter to continue.\n')

## ================ Part 3: Normal Equations ================
print('Solving with normal equations...\n')

data = pd.read_csv("ex1data2.txt",names=["sz","bed","price"])
s = np.array(data.sz)
b = np.array(data.bed)
p = np.array(data.price)
m = len(b) # number of training examples

# Design Matrix
s = np.vstack(s)
b = np.vstack(b)
X = np.hstack((s,b))

# Add intercept term to X
X = np.hstack((np.ones_like(s),X))

# Calculate the parameters from the normal equation
theta = normalEqn(X, p)

# Display normal equation's result
print('Theta computed from the normal equations: \n')
print(theta)
print('\n')

# Estimate the price of a house, by Normal equations
price = np.dot([1,siz,br],theta) # You should change this

print('Predicted price of house with size = {} and {} no of bedrooms (Normalization):\n '.format(siz, br),
      price)

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4


import numpy as np
#!----Relu activation function-----!

inputs = [1, 2, -2, 100, -130, 22, 0, 1, 4]
outputs =[]
for i in inputs:
    if i > 0:
        outputs.append(i)
    else:
        outputs.append(0)

print(outputs)

# print( np.exp(3))
# print(2.718**3)

#another way
inputs = [1, 2,-2, 100, -130,22,0,1,4]
outputs =[]
for i in inputs:
        outputs.append(max(0,i))
print(outputs)

#another way using numpy
outputs=np.maximum(0,inputs)
print(outputs)

#                 !-----soft_max activation function------!
outputs =[4.8, 1.21, 2.385]
E = 2.71828
exp_values = []
for values in outputs:
    exp_values.append(E**values)

print(exp_values)

norm_base = sum(exp_values)

norm_values = []

for n_value in exp_values:
    norm_values.append(n_value / norm_base)

print("CE", norm_values)

#Do it with numpy
outputs =([[1.8, 1.21, 3.385], [2, 1.21, 2.385],[3, 1.21, 2.385]])
exp_values = np.exp(outputs)
probabilities = exp_values / np.sum(exp_values, axis=1, keepdims=True)
print("Hello", probabilities)

np.exp(1000)  #doesn't take maximum number

# for this reason write code as

exp_values = np.exp(outputs-np.max(outputs, axis=1, keepdims=True))
print("Exponential values",exp_values)
probabilities = exp_values /np.sum(exp_values, axis =1, keepdims=True)
print("Hi", probabilities)


import numpy as np

# Write a function that takes as input a list of numbers, and returns
# the list of values given by the softmax function.
def softmax(L):
    return np.exp(L) / np.sum(np.exp(L))

res = softmax([1, 8, 16])
print(res, '\nsum =', sum(res))


import numpy as np
from grid_world import GridworldEnv

env = GridworldEnv()

def policy_eval(policy, env, discount_factor=1.0, theta=0.00001):
    """
    Evaluate a policy given an environment and a full description of the environment's dynamics.

    Args:
        policy: [S, A] shaped matrix representing the policy.
        env: OpenAI env. env.P represents the transition probabilities of the environment.
            env.P[s][a] is a list of transition tuples (prob, next_state, reward, done).
            env.nS is a number of states in the environment.
            env.nA is a number of actions in the environment.
        theta: We stop evaluation once our value function change is less than theta for all states.
        discount_factor: Gamma discount factor.

    Returns:
        Vector of length env.nS representing the value function.
    """
    # Start with a random (all 0) value function
    V = np.zeros(env.nS)
    while True:
        max_v_changed = 0
        for s in range(env.nS):
            v = 0
            for a, action_prob in enumerate(policy[s]):
                for prob, next_state, reward, done in env.P[s][a]:
                    v += action_prob * prob * \
                        (reward + discount_factor * V[next_state])

            max_v_changed = max(max_v_changed, np.abs(v - V[s]))
            V[s] = v
        # print(max_v_changed)
        if max_v_changed < theta:
            break

    return np.array(V)

if __name__ == '__main__':

    random_policy = np.ones([env.nS, env.nA]) / env.nA
    v = policy_eval(random_policy, env)

    print("Value Function:")
    print(v)
    print("")

    print("Reshaped Grid Value Function:")
    print(v.reshape(env.shape))
    print("")

    expected_v = np.array([0, -14, -20, -22, -14, -18, -20, -20, -20, -20, -18, -14, -22, -20, -14, 0])
    np.testing.assert_array_almost_equal(v, expected_v, decimal=2)


#   # -*- coding: UTF-8 -*-
#   trial on the : Satomi machine
#   Created by Ush on 2018/3/16
#   Project name :  Class3_FindingRoots
#   Please contact CHIH, HSIN-CHING/D0631008 when expect to refer this source code.
#   NOTE : no liability on any loss nor damage by using this source code. it is your own risk.

import numpy as np
import math

def FUNC(type, x):
    F_select = 0
    if (type == 1):  # function1 is f(x)=x^2-1
        F_select = x ** 2 - 2
    elif (type == 2):
        F_select = x ** 5 + x ** 3 + 3
    elif (type == 3):
        F_select = x ** 3 - 2 * math.sin(x) - 1
    elif (type == 4):
        F_select = 0.5 * math.tan(x) - math.tanh(x)
    else:
        print("invalid function")
    return (float(F_select))

def Bisection(f, left, right, eps):
    iteration = 0
    while (math.fabs(right - left) > eps * math.fabs(right)):
        middle = (left + right) / 2
        if (np.sign(FUNC(f, middle)) == np.sign(FUNC(f, right))):
            right = middle
        else:  # root is  founded, move left = middle value
            left = middle
        iteration = iteration + 1
        # print(iteration, "\t", right, "\t", FUNC(f, right))
    return (right, iteration)

def Secant(f, left, right, eps):
    iteration = 0
    while (math.fabs(right - left) > eps * math.fabs(right)):
        middle, left = left, right
        right = right + ((right - middle) / ((FUNC(f, middle)) / (FUNC(f, right)) - 1))
        iteration = iteration + 1
        # print(iteration, left, FUNC(f, left), right, FUNC(f, right))
    return (right, iteration)

# IQI : Inverse Quadratic Interpolation
# https://en.wikipedia.org/wiki/Inverse_quadratic_interpolation
# https://nickcdryan.com/2017/09/13/root-finding-algorithms-in-python-line-search-bisection-secant-newton-raphson-boydens-inverse-quadratic-interpolation-brents/
def PolyInterp(f, p0, p1, p2):
    a, b, c = FUNC(f, p0), FUNC(f, p1), FUNC(f, p2)
    L0 = (p0 * b * c) / ((a - b) * (a - c))
    L1 = (p1 * a * c) / ((b - a) * (b - c))
    L2 = (p2 * b * a) / ((c - a) * (c - b))
    return (L0 + L1 + L2)

def IQI(f, left, middle, right, eps):
    iteration = 0
    while (math.fabs(right - middle) > eps * math.fabs(right)):
        x = PolyInterp(f, left, middle, right)
        left, middle, right = middle, right, x
        iteration = iteration + 1
        # print(iteration, right, FUNC(f, right))
    return (right, iteration)

# https://en.wikipedia.org/wiki/Brent%27s_method#Dekker.27s_method
# it is Dekker's method in 1969
# https://blogs.mathworks.com/cleve/2015/10/12/zeroin-part-1-dekkers-algorithm/
# https://cocalc.com/share/ab93d447b6728ae561bf1f9e18f0b103316d715f/Efficiency%20of%20Standard%20and%20Hybrid%20Root%20Finding%20Methods.sagews?viewer=share
def Mixed_Scant_Bisection(f, left, right, eps):
    fa = FUNC(f, left)
    fb = FUNC(f, right)
    if (np.sign(fa) == np.sign(fb)):
        print(" Interval in ", left, " and ", right, " is in the same sign signal")
    #print("initial-value from :", right, "\t", fb)
    #print("N-Type-Step\t\tbn\t\t\t\t\t\tf(bn) ")
    iteration = 1
    # left is the previous value of right and [right, middle] always contains the zero.
    middle, fc = left, fa
    while (math.fabs(right - left) > eps):
        if (np.sign(fb) == np.sign(fc)):
            middle, fc = left, fa
        if (math.fabs(fc) < math.fabs(fb)):  # Swap to insure f(b) is the smallest value so far.
            left, fa = right, fb
            right, fb = middle, fc
            middle, fc = left, fa
        mid_point = float(right + middle) / 2  # BiSection ( Step1 )
        # secant_left/secant_right is the the secant step.
        secant_left = (right - left) * fb
        if (secant_left >= 0):
            secant_right = fa - fb  # swap like BiSection
        else:
            secant_right = -1 * (fa - fb)  # swap like BiSection
            secant_left = -1 * secant_left
        left, fa = right, fb  # prepare for the next iteration process points.
        if (secant_left <= ((mid_point - right) * secant_right)):
            right = right + (secant_left / secant_right)  # Secant
            fb = FUNC(f, right)
            #print(iteration, "Secant-step", right, "\t", fb)
        else:
            right = mid_point  # BiSection
            fb = FUNC(f, right)
            #print(iteration, "Bisect-step", right, "\t", fb)
        iteration = iteration + 1
    return (right, iteration)

# https://cocalc.com/share/ab93d447b6728ae561bf1f9e18f0b103316d715f/Efficiency%20of%20Standard%20and%20Hybrid%20Root%20Finding%20Methods.sagews?viewer=share
def brentsMethod(f, a, b, accuracy):
    '''
    Code inspired by:
    https://en.wikipedia.org/wiki/Brent's_method (The pseudocode was very helpful in translating this to Python)
    http://blogs.mathworks.com/cleve/2015/10/26/zeroin-part-2-brents-version/
    Function that computes an approximate root
    for a given function using Dekker's method.
    args:
        f: a function
        a, b: an initial value, most efficient when close to the root
        iterations: number of times to run the loop
    output:
        a list that contains an approximate root s and the number
        of iterations required
    '''
    fa = FUNC(f, a)
    fb = FUNC(f, b)
    # When this is set, we use the bisection method
    bisection = True
    if fa * fb >= 0:
        raise Exception("Invalid inputs for a and b, require sign change")
    c = a
    s = 0
    d = 0
    iteration = 0
    while abs(b - a) > accuracy:
        # quadratic interpolation
        if (FUNC(f, a) != FUNC(f, c)) and (FUNC(f, b) != FUNC(f, c)):
            s = PolyInterp(f, a, b, c)      # IQI core
            #print(iteration, "IQI-step", s, "\t", FUNC(f,s))
        else:
            s = ((a * FUNC(f, b)) - (b * FUNC(f, a))) / (FUNC(f, b) - FUNC(f, a))# secant
            #print(iteration, "Secant-step", s, "\t", FUNC(f, s))
        if brentConditional(s, a, b, c, d, bisection, accuracy):
            s = (a + b) / 2.0 # bisection
            bisection = True
        else:
            bisection = False
        d = c
        c = b
        if (FUNC(f, a) * FUNC(f, s) < 0):   #step (3a)
            b = s
            fb = FUNC(f, s)
        else:
            a = s
            fa = FUNC(f, s)
        if abs(fa) < abs(fb):
            temp = a
            a = b
            b = temp
            temp2 = fa
            fa = fb
            fb = temp2
        iteration = iteration + 1
    return (s, iteration)

def brentConditional(s, a, b, c, d, mflag, accuracy):
    if ((s < (3 * a + b) * 0.25) or
            (mflag and (abs(s - b) >= (abs(b - c) * 0.5)) or
                 (not mflag and (abs(s - b) >= (abs(c - d) * 0.5)) or
                      (mflag and (abs(b - c) < accuracy)) or
                      (not mflag and (abs(c - d) < accuracy))))):
        return True
    return False

import matplotlib.pyplot as plt
from scipy import optimize

def f1(x):
    return (x ** 2 - 2)

def main():
    print
    "let's make it happen!"
    # eps function in matlab
    # Python code expression : np.spacing(1)
    eps = np.spacing(1)
    eps = 1e-6
    print("Python eps : " + str(eps))

    # formula 0
    print("\nFormula 0 : x ** 2 - 2 \t\t\t (root, iteration times)")
    ITR, Root = Bisection(1, 1, 2, eps)
    print("BiSection Result:\t\t\t\t", Bisection(1, 1, 2, eps))
    print("Secant Result:\t\t\t\t\t", Secant(1, 1, 2, eps))
    print("IQI Result:\t\t\t\t\t\t", IQI(1, 1, 1.5, 2, eps))
    print("Mixed_Scant_Bisection Result:\t", Mixed_Scant_Bisection(1, 1, 2, eps))
    print("Brent Result:\t\t\t\t\t", brentsMethod(1, 1, 2, eps))

    # formula 1
    print("\nFormula 1 : x ** 5 + x ** 3 + 3  (root, iteration times)")
    print("BiSection Result:\t\t\t\t", Bisection(2, -2, 0, eps))
    print("Secant Result:\t\t\t\t\t", Secant(2, -2, 0, eps))
    print("IQI Result:\t\t\t\t\t\t", IQI(2, -2, -1, 0, eps))
    print("Mixed_Scant_Bisection Result:\t", Mixed_Scant_Bisection(2, -2, 0, eps))
    print("Brent Result:\t\t\t\t\t", brentsMethod(2, -2, 0, eps))

    # formula 2
    print("\nFormula 2 : x ** 3 - 2 * math.sin(x) - 1\t  (root, iteration times)")
    print("BiSection Result:\t\t\t\t", Bisection(3, 0, 2, eps))
    print("Secant Result:\t\t\t\t\t", Secant(3, 0, 2, eps))
    print("IQI Result:\t\t\t\t\t\t", IQI(3, 0, 1, 2, eps))
    print("Mixed_Scant_Bisection Result:\t", Mixed_Scant_Bisection(3, 0, 2, eps))
    print("Brent Result:\t\t\t\t\t", brentsMethod(3, 0, 2, eps))

    # formula 3
    print("\nFormula 3 : 0.5 * math.tan(x) - math.tanh(x)\t  (root, iteration times)")
    A = 1e-3
    B = math.pi / 2
    print("BiSection Result:\t\t\t\t", Bisection(4, A, B, eps))
    print("Secant Result:\t\t\t\t\t", Secant(4, A, B, eps))
    print("IQI Result:\t\t\t\t\t\t", IQI(4, A, B, B, eps))
    print("Mixed_Scant_Bisection Result:\t", Mixed_Scant_Bisection(4, A, B, eps))
    print("Brent Result:\t\t\t\t\t", brentsMethod(4, A, B, eps))

if __name__ == "__main__":
    main()


import numpy as np

class Secant(object):
    def __init__(self, f=None, **extras):
        """
        f : a function f(x) that returns 'y', the function applied to it
        """
        self.f = f

    def solve(self, init_a=None, init_b=None, tol=None, max_it=50, **extras):
        """fast implementation of the secant method
        init_a : bottom bound of the initial interval
        init_b : top bound of the initial interval
        tol : maximum absolute error after which the method should stop. (e.g. 1e-15)
        max_it : maximum number of iterations after which the method should stop
        """
        xo = init_a
        x = init_b
        xn = 0

        i = 0
        while i < max_it:
            xn = x - ( self.f(x) * (x - xo) ) / ( self.f(x) - self.f(xo) )
            if abs(xn - x) < tol:
                break
            xo = x
            x = xn
            i+=1
        return x

    def vsolve(self, init_a=None, init_b=None, tol=None, max_it=50, **extras):
        """verbose implementation of the secant method
        init_a : first iteration estimate
        init_b : second iteration estimate
        tol : maximum absolute error after which the method should stop. (e.g. 1e-15)
        max_it : maximum number of iterations after which the method should stop
        """

        xo = init_a
        x = init_b
        xn = 0
        i = 0
        iteration_list = []
        dist_list = []
        estimate_list = []

        while i < max_it:
            dist = abs(x-xo)
            iteration_list.append(i)
            dist_list.append(dist)
            estimate_list.append(x)
            xn = x - ( self.f(x) * (x - xo) ) / ( self.f(x) - self.f(xo) )

            if abs(xn - x) < tol:
                break

            xo = x
            x = xn
            i+=1

        return iteration_list, estimate_list, dist_list

    def __repr__(self):
        # return "Secant "
        raise NotImplementedError

    def __str__(self):
        return "Secant method"


import numpy as np
import time
import scipy.linalg

def GENP(A, b):

    n =  len(A)
    if b.size != n:
        raise ValueError("Invalid argument", b.size, n)
    for pivot_row in xrange(n-1):
        for row in xrange(pivot_row+1, n):
            multiplier = A[row][pivot_row]/A[pivot_row][pivot_row]
            A[row][pivot_row] = multiplier
            for col in xrange(pivot_row + 1, n):
                A[row][col] = A[row][col] - multiplier*A[pivot_row][col]

            b[row] = b[row] - multiplier*b[pivot_row]
    print A
    print b
    x = np.zeros(n)
    k = n-1
    x[k] = b[k]/A[k,k]
    while k >= 0:
        x[k] = (b[k] - np.dot(A[k,k+1:],x[k+1:]))/A[k,k]
        k = k-1
    return x

def GEPP(A, b):

    n =  len(A)
    if b.size != n:
        raise ValueError("Invalid argument", b.size, n)

    for k in xrange(n-1):
        maxindex = abs(A[k:,k]).argmax() + k
        if A[maxindex, k] == 0:
            raise ValueError("singular")
        #Swap rows
        if maxindex != k:
            A[[k,maxindex]] = A[[maxindex, k]]
            b[[k,maxindex]] = b[[maxindex, k]]
        for row in xrange(k+1, n):
            multiplier = A[row][k]/A[k][k]

            A[row][k] = multiplier
            for col in xrange(k + 1, n):
                A[row][col] = A[row][col] - multiplier*A[k][col]

            b[row] = b[row] - multiplier*b[k]
    print A
    print b
    x = np.zeros(n)
    k = n-1
    x[k] = b[k]/A[k,k]
    while k >= 0:
        x[k] = (b[k] - np.dot(A[k,k+1:],x[k+1:]))/A[k,k]
        k = k-1
    return x

if __name__ == "__main__":
    A = np.array([[1e-20,0.,1.],
                  [1,1e+20,1.],
                  [0,1,-1],
                  ])
    b =  np.array([[0.],[0.],[1.]])
    print GENP(np.copy(A), np.copy(b))
    print GEPP(A,b)
    print '\n\nresidual', scipy.linalg.norm(np.dot(A, GEPP(A,b)) - b)/scipy.linalg.norm(A)
    tic = time.clock()
    toc = time.clock()
    print "Processing time is : %f" %(toc - tic)


import numpy as np

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

def hard_sigmoid(Z):
    y = 0.2 * Z + 0.5
    return np.clip(y, 0, 1)

def relu(Z):
    return (Z > 0) * Z

def softmax(Z):
    e_x = np.exp(Z - np.max(Z))
    return e_x / e_x.sum(axis = 1)

def backward_sigmoid(a):
    return a * (1 - a)

def backward_tanh(Z):
    return 1-(np.tanh(Z))**2

def backward_relu(Z):
    mul = np.ones(Z.shape)
    return (Z > 0) * mul

def backward_softmax(t_hat, i):
    t_hat[i] = t_hat[i] - 1
    return t_hat

# based on keras drop out
def dropout(input, level):
    noise_shape = input.shape # (Tx, n_a)
    noise = np.random.choice([0,1], noise_shape, replace = True, p=[level, 1-level])
    return input * noise / (1 - level), noise / (1 - level)


import torch
import numpy as np
import matplotlib.pylab as plt

def numerical_diff(f, x):
    h = 1e-4
    return (f(x + h) - f(x)) / h

def function_1(x):
    return 0.01 * x **2 + 0.1 * x

x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x, y)
plt.show()

print(numerical_diff(function_1, 5))
print(numerical_diff(function_1, 10))
print("---------------------------------------------------")

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

# center numerical diff
def numerical_gradient(f, x):
    h = 1e-4
    grad = torch.zeros_like(x)

    for idx in range(x.size()[0]):
        tmp_val = x[idx].item()

        x[idx] = tmp_val + h
        fxh1 = f(x)

        x[idx] = tmp_val - h
        fxh2 = f(x)

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

    return grad
#
#
# print(numerical_gradient(function_2, torch.tensor([3.0, 4.0])))
# print(numerical_gradient(function_2, torch.tensor([0.0, 2.0])))
# print(numerical_gradient(function_2, torch.tensor([3.0, 0.0])))
# print("---------------------------------------------------")

def numpy_numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]

        x[idx] = tmp_val + h
        fxh1 = f(x)

        x[idx] = tmp_val - h
        fxh2 = f(x)

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

    return grad

print(numpy_numerical_gradient(function_2, np.array([3.0, 4.0])))
print(numpy_numerical_gradient(function_2, np.array([0.0, 2.0])))
print(numpy_numerical_gradient(function_2, np.array([3.0, 0.0])))

# gradient descent
def numpy_gradient_descent(f, init_x, lr=0.001, step_num=100):
    x = init_x

    for i in range(step_num):
        # grad = numerical_gradient(f, x)
        grad = numpy_numerical_gradient(f, x)
        x -= lr * grad

    return x

init_x = np.array([-3.0, 4.0])
print(numpy_gradient_descent(function_2, init_x, lr=10.0, step_num=100))

def gradient_descent(f, init_x, lr=0.001, step_num=100):
    x = init_x

    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad

    return x

init_x = torch.tensor([-3.0, 4.0], dtype=torch.float64)
print(gradient_descent(function_2, init_x, lr=0.1, step_num=100))
print("---------------------------------------------------")

print(gradient_descent(function_2, init_x, lr=10.0, step_num=100))
print(gradient_descent(function_2, init_x, lr=1e-10, step_num=100))
print("---------------------------------------------------")


import numpy as np
import math

from .utils import *

 # A lot of comments and documentation is directly copied from Raphael Vallat (https://github.com/raphaelvallat/entropy)

# Permutation Entropy
"""Permutation Entropy.

  Parameters
  ----------
  x : np.array
      One-dimensional time series of shape (n_times)
  order : int
      Order of permutation entropy. Default is 3.
  delay : int
      Time delay (lag). Default is 1.
  normalize : bool
      If True, divide by log2(order!) to normalize the entropy between 0
      and 1. Otherwise, return the permutation entropy in bit. Default is true.
  Returns
  -------
  pe : float
      Permutation Entropy.
  Notes
  -----
  The permutation entropy is a complexity measure for time-series first
  introduced by Bandt and Pompe in 2002.
  The permutation entropy of a signal :math:`x` is defined as:
  .. math:: H = -\\sum p(\\pi)\\log_2(\\pi)
  where the sum runs over all :math:`n!` permutations :math:`\\pi` of order
  :math:`n`. This is the information contained in comparing :math:`n`
  consecutive values of the time series. It is clear that
  :math:`0 ≤ H (n) ≤ \\log_2(n!)` where the lower bound is attained for an
  increasing or decreasing sequence of values, and the upper bound for a
  completely random system where all :math:`n!` possible permutations appear
  with the same probability.
  The embedded matrix :math:`Y` is created by:
  .. math::
      y(i)=[x_i,x_{i+\\text{delay}}, ...,x_{i+(\\text{order}-1) *
      \\text{delay}}]
  .. math:: Y=[y(1),y(2),...,y(N-(\\text{order}-1))*\\text{delay})]^T
  References
  ----------
  Bandt, Christoph, and Bernd Pompe. "Permutation entropy: a
  natural complexity measure for time series." Physical review letters
  88.17 (2002): 174102.
  Examples
  --------
  Permutation entropy with order 2
  >>> from OrdinalEntroPy import *
  >>> import numpy as np
  >>> x = [4, 7, 9, 10, 6, 11, 3]
  >>> # Return a value in bit between 0 and log2(factorial(order))
  >>> print(PE(x, order=2, normalize=False))
  0.9182958340544896
  Normalized permutation entropy with order 3
  >>> from OrdinalEntroPy import *
  >>> import numpy as np
  >>> x = [4, 7, 9, 10, 6, 11, 3]
  >>> # Return a value comprised between 0 and 1.
  >>> print(PE(x, order=3, normalize=True))
  0.5887621559162939
"""
def PE(values,order=3,delay=1,normalize=True):

  # get all the permuations
  str_permutations = get_str_permutation_ordinal(values,order,delay)

  # get set of indices for each unique permutation
  permutation_indexes = get_permutation_index(str_permutations)

  # get frequency of each permutation pattern
  permutation_frequency = get_permutation_frequency(permutation_indexes,len(values),order)

  # get shannon entropy of frequencies
  entropy = get_shanon_entropy(permutation_frequency)

  #Normalize
  if normalize:
    entropy = entropy/math.log2(math.factorial(order))

  return entropy

"""Dispersion Entropy.

  Parameters
  ----------
  x : np.array
      One-dimensional time series of shape (n_times)
  order : int
      Order of permutation entropy. Default is 3.
  classes : int
      Number of classes. Default is 3.
  delay : int
      Time delay (lag). Default is 1.
  normalize : bool
      If True, divide by log2(classes**order) to normalize the entropy between 0
      and 1. Otherwise, return the permutation entropy in bit. Default is true.
  Returns
  -------
  pe : float
      Permutation Entropy.
  Notes
  -----
  Dispersion Entropy (DE) was introduced in the year 2016 by Azami and Rostaghi
  to quantify the complexity of time series.
  The Dispersion entropy of a signal :math:`x` is defined as:
  .. math:: H = -\\sum p(\\pi)\\log_2(\\pi)
  where the sum runs over all :math:`classes**order` permutations :math:`\\pi` of order
  :math:`n` and consisting of classes :math:`c`. This is the information contained in comparing :math:`n`
  consecutive values of the time series. It is clear that
  :math:`0 ≤ H (n) ≤ \\log_2(classes^order)` where the lower bound is attained for an
  increasing or decreasing sequence of values, and the upper bound for a
  completely random system where all :math:`classes**order` possible dispersion patterns appear
  with the same probability.
  The embedded matrix :math:`Y` is created by:
  .. math::
      y(i)=[x_i,x_{i+\\text{delay}}, ...,x_{i+(\\text{order}-1) *
      \\text{delay}}]
  .. math:: Y=[y(1),y(2),...,y(N-(\\text{order}-1))*\\text{delay})]^T
  References
  ----------
  M. Rostaghi and H. Azami, "Dispersion Entropy: A Measure for Time-Series Analysis,"
  in IEEE Signal Processing Letters, vol. 23, no. 5, pp. 610-614, May 2016, doi: 10.1109/LSP.2016.2542881.
  Examples
  --------
  Dispersion entropy with order=3 and classes=3
  >>> from OrdinalEntroPy import *
  >>> import numpy as np
  >>> np.random.seed(1234567)
  >>> x = np.random.rand(3000)
  >>> # Return a value in bit between 0 and log2(factorial(order))
  >>> print(DE(x, order=3,classes=3,normalize=True))
  0.9830685145488814
"""

# Dispersion Entropy
def DE(values,order=3,classes=3,delay=1,normalize=True):
  # map TS to classes using cummulative distributive function
  mapped_values = get_ncdf_values(values,classes)
  # get all the permuations
  str_permutations = get_str_permutation(mapped_values,order,delay)

  # get set of indices for each unique permutation
  permutation_indexes = get_permutation_index(str_permutations)

  # get frequency of each permutation pattern
  permutation_frequency = get_permutation_frequency(permutation_indexes,len(values),order)

  # get shannon entropy of frequencies
  entropy = get_shanon_entropy(permutation_frequency)
  if normalize:
    entropy = entropy/math.log2(classes**order)

  return entropy

# Reverse Dispersion Entropy
def RDE(values,order=3,classes=3,delay=1,normalize=True):
  mapped_values = get_ncdf_values(values,classes)
  # get all the permuations
  str_permutations = get_str_permutation(mapped_values,order,delay)

  permutation_indexes = get_permutation_index(str_permutations)

  permutation_frequency = get_permutation_frequency(permutation_indexes,len(values),order)

  entropy = np.square(permutation_frequency).sum() - (1/(classes**order))
  if normalize:
    entropy = entropy/(1 - (1/(classes**order)))

  return entropy

# Reverse Permutation Entropy
def RPE(values,order=3,delay=1,normalize=True):
  str_permutations = get_str_permutation_ordinal(values,order,delay)

  permutation_indexes = get_permutation_index(str_permutations)
  #print(set(str_permutations))

  permutation_frequency = get_permutation_frequency(permutation_indexes,len(values),order)
  #print(permutation_frequency)
  entropy = np.square(permutation_frequency).sum() - (1/math.factorial(order))
  if normalize:
    entropy = entropy/(1 - (1/math.factorial(order)))

  return entropy

# Weighted Permutation Entropy
def WPE(values,order=3,delay=1,normalize=True):
  weights = get_weights(values,order,delay)

  str_permutations = get_str_permutation_ordinal(values,order,delay)

  permutation_indexes = get_permutation_index(str_permutations)

  weighted_permutations = cal_weighted_permutations(np.array(weights),permutation_indexes)

  entropy = get_shanon_entropy(weighted_permutations)

  if normalize:
    entropy = entropy/math.log2(math.factorial(order))

  return entropy

  # Reverse Weighted Permutation Entropy
def RWPE(values,order,delay=1,normalize=True):
  weights = get_weights(values,order,delay)

  str_permutations = get_str_permutation_ordinal(values,order,delay)

  permutation_indexes = get_permutation_index(str_permutations)

  weighted_permutations = cal_weighted_permutations(np.array(weights),permutation_indexes)

  entropy = np.square(weighted_permutations).sum() - (1/math.factorial(order))
  if normalize:
    entropy = entropy/(1 - (1/math.factorial(order)))

  return entropy

# Reverse weighted Dispersion Entropy
def RWDE(values,order,classes,delay=1,normalize=True):
  # find variance of each permutation
  weights = get_weights(values,order,delay)
  # convert values with NCDF and assign them class
  mapped_values = get_ncdf_values(values,classes)
  # get all the permuations
  str_permutations = get_str_permutation(mapped_values,order,delay)

  #find the indices of a permutation
  permutation_indexes = get_permutation_index(str_permutations)

  # get the weight for each permutation
  weighted_permutations = cal_weighted_permutations(np.array(weights),permutation_indexes)

  entropy = np.square(weighted_permutations).sum() - (1/(classes**order))
  # calculate final RWDE entropy
  if normalize:
    entropy = entropy/(1 - (1/(classes**order)))

  return entropy


import numpy as np
from scipy import linalg
from numpy import dot

def nmf(X, latent_features, max_iter=100, error_limit=1e-6, fit_error_limit=1e-6):
	"""
	Decompose X to A*Y
	"""
	eps = 1e-5
	print 'Starting NMF decomposition with {} latent features and {} iterations.'.format(latent_features, max_iter)
	#X = X.toarray()  # I am passing in a scipy sparse matrix

	# mask
	mask = np.sign(X)
	rows, columns = X.shape
	A = np.random.rand(rows, latent_features)
	A = np.maximum(A, eps)

	Y = linalg.lstsq(A, X)[0]
	Y = np.maximum(Y, eps)

	masked_X = mask * X
	X_est_prev = dot(A, Y)
	for i in range(1, max_iter + 1):
		top = dot(masked_X, Y.T)
		bottom = (dot((mask * dot(A, Y)), Y.T)) + eps
		A *= top / bottom

		A = np.maximum(A, eps)
		top = dot(A.T, masked_X)
		bottom = dot(A.T, mask * dot(A, Y)) + eps
		Y *= top / bottom
		Y = np.maximum(Y, eps)
		if i % 5 == 0 or i == 1 or i == max_iter:
			print 'Iteration {}:'.format(i),
			X_est = dot(A, Y)
			err = mask * (X_est_prev - X_est)
			fit_residual = np.sqrt(np.sum(err ** 2))
			X_est_prev = X_est
			curRes = linalg.norm(mask * (X - X_est), ord='fro')
			print 'fit residual', np.round(fit_residual, 4),
			print 'total residual', np.round(curRes, 4)
			if curRes < error_limit or fit_residual < fit_error_limit:
				break
	return A, Y

if __name__ == "__main__":
    R = [
         [5,3,0,1],
         [4,0,0,1],
         [1,1,0,5],
         [1,0,0,4],
         [0,1,5,4],
        ]

    R = np.array(R)
    print R

    nP, nQ = nmf(R, 2)
    print np.dot(nP, nQ)
	# To restore R, execute numpy.dot(np, nQ.T)


#coding:utf8
import numpy as np

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

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

    Return:
    s -- sigmoid(x)
    """
    s = 1 / (1 + np.exp(-x))
    return 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.
    """
    s = sigmoid(x)
    ds = s * (1 - s)
    return ds

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

    Returns:
    v -- a vector of shape (length*height*depth, 1)
    """
    v = image.reshape((image.shape[0] * image.shape[1] * image.shape[2]), 1)

    return v

# GRADED FUNCTION: normalizeRows

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.
    """
    # Compute x_norm as the norm 2 of x. Use np.linalg.norm(..., ord = 2, axis = ..., keepdims = True)
    # keepdims is keep the np.dims, in this case, [2, 3] will become [2, 1], but not [2, ]
    x_norm = np.linalg.norm(x, ord=2, axis=1, keepdims=True)
    # x_norm = np.linalg.norm(x, ord = 2, axis = 1)
    # print(x_norm.shape)
    # Divide x by its norm.
    x = x / x_norm
    return x

# GRADED FUNCTION: softmax

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 (n, m).

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

    Returns:
    s -- A numpy matrix equal to the softmax of x, of shape (n,m)
    """
    # 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
    # print("x_exp: {}, x_sum: {}".format(x_exp.shape, x_sum.shape))

    return s

# The loss is used to evaluate the performance of your model. The bigger your loss is,
# the more different your predictions ($ \hat{y} $) are from the true values ($y$). In deep learning,
# you use optimization algorithms like Gradient Descent to train your model and to minimize the cost.
# GRADED FUNCTION: L1
# L1正则
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(np.abs(yhat - y), axis=0)
    ### END CODE HERE ###

    return loss

# GRADED FUNCTION: L2

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

    Returns:
    loss -- the value of the L2 loss function defined above
    """
    loss = np.dot(abs(y - yhat), abs(y - yhat).T)
    # loss = np.square(np.linalg.norm(abs(y - yhat), ord = 2, axis = 0))
    return loss


import math
from scipy.special import erf
from numpy import poly1d
from numpy import pi, sin, linspace
from numpy import exp, cos

# scipy erfc does not support complex numbers but erf does
def erfc(z):
        if z == complex(0,0):
          return 1.0
        else:
          return 1.0-erf(z)

def analytic_solution_simple(t, x, a_0, L, g, eta): # time, x_value, initial maximum perturbation (at the very left), wavelength, gravity, viscosity
        k= 2*pi/L # wave number 2pi/wavelength
        gamma=2*eta*k**2
        a=a_0*cos(pi*x)
        return a*exp(-gamma*t)

# Formulas were compared with the same formula hacked into matlab
# based on
# Motion of two superposed viscous fluids
# A Prosperetti - Physics of Fluids, 1981
# doi:10.1063/1.863522
# assumes that fluid in the tank has same viscosity as fluid above
def analytic_solution(t, x, a_0, L, g, eta): # time, x_value, initial maximum perturbation (at the very left), wavelength, gravity, viscosity
  debug=False
  eta=eta/2.0
  k= 2.0*pi/L # wave number 2pi/wavelength
  omega_0sq = g*k  # inviscid natural frequency
  a=a_0*cos(pi*x)

  p1 = poly1d([1.0,0.0,2*k**2*eta,4*k**3*eta**(1.5),eta**2*k**4+omega_0sq],r=0)

  p1roots = p1.r
  z1=p1roots[0]
  z2=p1roots[1]
  z3=p1roots[2]
  z4=p1roots[3]
  Z1=(z2-z1)*(z3-z1)*(z4-z1)
  Z2=(z1-z2)*(z3-z2)*(z4-z2)
  Z3=(z1-z3)*(z2-z3)*(z4-z3)
  Z4=(z1-z4)*(z2-z4)*(z3-z4)

  if debug:
                print 'Calculate analytic solution:'
                print 't=', t
                print 'a=', a
                print 'k=', k
                print 'g=', g
                print 'eta=', eta
                print 'omega_0sq=', omega_0sq

  t0=4*eta**2*k**4/(8*eta**2*k**4+omega_0sq)*a*erfc((eta*k**2*t)**0.5)
  t1=z1/Z1*omega_0sq*a/(z1**2-eta*k**2)*exp((z1**2-eta*k**2)*t)*erfc(z1*t**0.5)
  t2=z2/Z2*omega_0sq*a/(z2**2-eta*k**2)*exp((z2**2-eta*k**2)*t)*erfc(z2*t**0.5)
  t3=z3/Z3*omega_0sq*a/(z3**2-eta*k**2)*exp((z3**2-eta*k**2)*t)*erfc(z3*t**0.5)
  t4=z4/Z4*omega_0sq*a/(z4**2-eta*k**2)*exp((z4**2-eta*k**2)*t)*erfc(z4*t**0.5)

  a=t0+t1+t2+t3+t4

  if debug:
                print 'a(t)=', a.real
  if (a.imag>0.000001):
                print 'Warning: Imaginary part of a(t) is not zero!'
  return a.real


import numpy as np

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

def predict(X, w):
    linear_combination = np.matmul(X, w)
    return sigmoid(linear_combination)

def classify(X, w):
    return np.round(predict(X, w))

def loss (X, Y, w):
    predictions = predict(X, w)
    first_term = Y * np.log(predictions)
    second_term = (1 - Y) * np.log(1 - predictions)
    return (-1) * np.average(first_term + second_term)

def gradient(X, Y, w):
    predictions = predict(X, w)
    return np.matmul(X.T, (predictions - Y)) / (2 * X.shape[0])

def train(X, Y, iterations, learning_rate):
    w = np.zeros((X.shape[1], 1))
    for i in range(iterations):
        print("Iterations: %4d => Loss: %.20f" % (i, loss(X, Y, w)))
        w -= gradient(X, Y, w) * learning_rate
    return w

def test(X, Y, w):
    total_examples = X.shape[0]
    correct_results = np.sum(classify(X, w) == Y)
    success_percent = correct_results * 100 / total_examples

    print("\nSuccess: %d/%d (%.2f%%)" % (correct_results, total_examples, success_percent))

if __name__ == "__main__":
    X1, X2, X3, Y = np.loadtxt("examples.txt", skiprows=1, unpack=True)
    X = np.column_stack((np.ones(X1.size), X1, X2, X3))
    Y = Y.reshape(-1, 1)
    iterations = 10000
    learning_rate = 0.001
    w = train(X, Y, iterations, learning_rate)

    test(X, Y, w)

    input("Press <Enter>")


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat May  2 16:13:25 2020

@author: tyrion
"""

import numpy as np

def vectors_to_polar(vectors, deg = False):
    '''
    Transform a set of vectors to polar coordinates

    Arguments
    ----------
    vectors : ndarray (n, 3)
        Vector data
    deg : bool, optional, default False
        If True, returns output polar angles in degrees. If False, returns angles in radian.
    Returns
    ----------
    theta : ndarray (size, )
        Polar angle
    phi : ndarray (size, )
        Azimuthal angle
    '''

    phi = np.arctan2(vectors[:, 1],vectors[:, 0])
    theta = np.arccos(vectors[:, 2])

    if deg == True:

        phi = np.rad2deg(phi)
        theta = np.rad2deg(theta)

    return theta, phi

def polar_to_vectors(theta, phi, deg = False):
    '''
    Transform a set of polar coordinates to vectors

    Arguments
    ----------
    theta : ndarray (n, )
        Polar angle
    phi : ndarray (n, )
        Azimuthal angle
    deg : bool, optional, default False
        If True, assumes that input is in degrees. If False, assumes that input is in radian.
    Returns
    ----------
    vectors : ndarray (n, 3)
        Vector data
    '''

    if deg == True:

        phi = np.deg2rad(phi)
        theta = np.deg2rad(theta)

    sintheta = np.sin(theta)

    vecs = np.empty((theta.shape[0],3))
    vecs[:, 0] = sintheta*np.cos(phi)
    vecs[:, 1] = sintheta*np.sin(phi)
    vecs[:, 2] = np.cos(theta)

    return vecs

def vectors_to_geographical(vectors, deg = False):
    '''
    Transform a set of vectors to geographical coordinates

    Arguments
    ----------
    vectors : ndarray (n, 3)
        Vector data
    deg : bool, optional, default False
        If True, returns output geographical angles in degrees. If False, returns angles in radian.
    Returns
    ----------
    latitude : ndarray (n, )
        Geographical latitude
    longitude : ndarray (n, )
        Geographical longitude
    '''

    longitude = np.arctan2(vectors[:, 1],vectors[:, 0])
    latitude = np.arcsin(vectors[:, 2])

    if deg == True:

        latitude = np.rad2deg(latitude)
        longitude = np.rad2deg(longitude)

    return latitude, longitude

def geographical_to_vectors(latitude, longitude, deg = False):
    '''
    Transform a set of geographical coordinates to vectors

    Arguments
    ----------
    latitude : ndarray (n, )
        Polar angle
    longitude : ndarray (n, )
        Azimuthal angle
    deg : bool, optional, default False
        If True, assumes that input is in degrees. If False, assumes that input is in radian.
    Returns
    ----------
    vectors : ndarray (n, 3)
        Vector data
    '''

    if deg == True:

        latitude = np.deg2rad(latitude)
        longitude = np.deg2rad(longitude)

    vectors = np.empty((latitude.shape[0],3))

    coslat = np.cos(latitude)

    vectors[:, 0] = coslat * np.cos(longitude)
    vectors[:, 1] = coslat * np.sin(longitude)
    vectors[:, 2] = np.sin(latitude)

    return vectors


from typing import Callable
import numpy as np

def simpsonsRule(f: Callable, N: float, start: float=-1e6,
                 stop: float=1e6) -> float:
    """Function to approximate the numeric integral of a function, f, using
    Simpson's rule.

    Arguments:
        f {Callable} -- Function for which the integral is to be estimated.
        N {float} -- Number of nodes to consider.

    Keyword Arguments:
        start {float} -- Starting point (default: {-1e6}).
        stop {float} -- Stopping point (default: {1e6}).

    Returns:
        float -- Approximation of the area under the function.
    """

    # Building values for approximation, and getting step size
    x, h = np.linspace(start=start, stop=stop, num=N, retstep=True)

    # Computing midpoints
    x_mid = np.array([(x[i - 1] + x[i]) / 2 for i in range(1, N)])

    # Estimating using Simpson's rule
    area = np.sum(2 * f(x)) - (f(start) + f(stop)) + (4 * np.sum(f(x_mid)))

    # Scaling area
    area *= (h / 6)

    return area


import numpy as np

'''

Cross-Entropy is an error function that we want to minimize.
To calculate Cross-Entropy(CE):

CE = negative summation of the logarithms of the probabilities whenever there is or there's none
simply
 = - summ [(y_i)ln(p_i) + (1-y_i)ln(1-p_i)]
where:
y is either 1 if there is or 0 if there's none
p is the probability that there is

'''

def cross_entropy(Y, P):
    result = []
    for i in range(0,len(Y)):
        result.append(-1*(Y[i]*np.log(P[i]) + (1-Y[i])*np.log(1-P[i])))
    CE = np.sum(result)
    return CE

'''
Udacity's solution which is cleaner

import numpy as np

def cross_entropy(Y, P):
    Y = np.float_(Y)
    P = np.float_(P)
    return -np.sum(Y * np.log(P) + (1 - Y) * np.log(1 - P))
'''

Lesson 3: Introduction to Neural Networks/3 Cross Entropy.py


from math import copysign, isnan
import numpy as np

test = False

with open(f"../input/{'test_' if test else ''}day7.txt") as f:
    # :pinchers:
    crabs = np.array([int(x) for x in f.read().strip().split(",")])

def sim_p1(h):
    # d/dh | x - h | = sgn(h - x)
    return np.sum(np.abs(crabs - h)), np.sum(np.sign(h - crabs))

def sim_p2(h):
    # let k = |x_i - h|
    # cost = k * (k + 1) / 2
    # so d/dh = sgn(h - x) / 2 + (h - x)

    d   = np.abs(crabs - h)
    err = np.sum((d * (d + 1)) / 2)
    dh  = np.sum(.5 * np.sign(h - crabs) + (h - crabs))

    return err, dh

def solve(sim):
    # very random
    sol = 5
    alpha = .00005

    for i in range(100000):
        err, dh = sim(sol)

        if abs(dh) < 1e-4:
            break

        sol -= dh * alpha

    err, _ = sim(round(sol))
    print(f"sol: {sol} -> {round(sol)}, err: {err}")

if __name__ == "__main__":
    solve(sim_p1)
    solve(sim_p2)


# Q.2 How do the values of options at time t = 0 compare for various values of M? Compute and plot graphs (of the
# initial option prices) varying M in steps of 1 and in steps of 5. What do you observe about the convergence of
# option prices?

# Pandas : pip install pandas
# Matplotlib: pip install matplotlib
# Numpy: pip install numpy
# Ipython: pip install ipython

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

# Given data
S0=100
K=105
T=5
r=0.05
sig=0.3

# Function to get Option Price for a given M
def getOptionPrice(M):
    dt = T/M
    u = math.exp(sig*math.sqrt(dt)+(r-sig*sig/2)*dt)
    d = math.exp(-sig*math.sqrt(dt)+(r-sig*sig/2)*dt)
    p = (math.exp(r*dt)-d)/(u-d)

    # Check if No Arbitrage Principle has got violated
    if p < 0 or p > 1:
        print("No Arbitrage Principle has been Violated")
        return '-','-'

    callList = [0]*(M+1)
    putList = [0]*(M+1)

    for i in range(M+1):
        callList[i] = max(S0*(u**i)*(d**(M-i)) - K, 0)
        putList[i] = max(0, K - S0*(u**i)*(d**(M-i)))

    for i in range(M):
        for j in range(M-i):
            callList[j] = ((1-p)*callList[j] + p*callList[j+1])*math.exp(-r*T/M)
            putList[j] = ((1-p)*putList[j] + p*putList[j+1])*math.exp(-r*T/M)
    return callList[0], putList[0]

# Lists to store the option prices
callPrices = []
putPrices = []
M=0
# Compute initial option prices in steps of 1
while M < 400:
    M += 1
    call, put = getOptionPrice(M)
    callPrices.append(call)
    putPrices.append(put)
MList = np.linspace(1, 400, 400)

plt.plot(MList, callPrices)
plt.xlabel('Value of M')
plt.ylabel('Call Option Price')
plt.title('Varying Price of Call Option with Value of M (Step Size 1)')
plt.show()

plt.plot(MList, putPrices)
plt.xlabel('Value of M')
plt.ylabel('Price of Put Option')
plt.title('Varying Price of Put Option with Value of M (Step Size 1)')
plt.show()

# Lists to store the option prices
callPrices = []
putPrices = []

# Compute initial option prices in steps of 5
M=0
while M < 400:
    M += 5
    call, put = getOptionPrice(M)
    callPrices.append(call)
    putPrices.append(put)
MList = np.linspace(1, 400, 80)

plt.plot(MList, callPrices)
plt.xlabel('Value of M')
plt.ylabel('Call Option Price')
plt.title('Varying Call Option Price with Value of M (Step Size 5)')
plt.show()

plt.plot(MList, putPrices)
plt.xlabel('Value of M')
plt.ylabel('Price of Put Option')
plt.title('Varying Put Option Price with Value of M (Step Size 5)')
plt.show()

Semester 6/MA 374 (Financial Engg. Lab)/Lab 1/180123062_ABSatyaprakash_q2 1.py


import numpy as np
import math
from scipy import stats

x = np.array([14.4,7.2,27.5,53.8,38.0,15.9,4.9])
y = np.array([54,64,44,32,37,68,62])

n = x.size

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

d = rank_x - rank_y
d_square = np.square(d)
sum_d_square = np.sum(d_square)

print("d : ",d)
print("d_square : ",d_square)

# if there are no duplicates
coeff_numerator = 6*sum_d_square

# if there are duplicates
coeff_numerator_dup = 6*(sum_d_square+((6+24)/12))

coeff_denominator = n*(math.pow(n,2)-1)
coeff = 1-(coeff_numerator/coeff_denominator)

print("rank co-relation : ",coeff)


# Databricks notebook source
# MAGIC %md
# MAGIC ScaDaMaLe Course [site](https://lamastex.github.io/scalable-data-science/sds/3/x/) and [book](https://lamastex.github.io/ScaDaMaLe/index.html)
# MAGIC
# MAGIC This is a 2019-2021 augmentation and update of [Adam Breindel](https://www.linkedin.com/in/adbreind)'s initial notebooks.
# MAGIC
# MAGIC _Thanks to [Christian von Koch](https://www.linkedin.com/in/christianvonkoch/) and [William Anzén](https://www.linkedin.com/in/william-anz%C3%A9n-b52003199/) for their contributions towards making these materials Spark 3.0.1 and Python 3+ compliant._

# COMMAND ----------

# MAGIC %md
# MAGIC #### We can also implement the model with mini-batches -- this will let us see matrix ops in action:
# MAGIC
# MAGIC (N.b., feed_dict is intended for small data / experimentation. For more info on ingesting data at scale, see https://www.tensorflow.org/api_guides/python/reading_data)

# COMMAND ----------

# we know these params, but we're making TF learn them

REAL_SLOPE_X1 = 2 # slope along axis 1 (x-axis)
REAL_SLOPE_X2 = 3 # slope along axis 2 (y-axis)
REAL_INTERCEPT = 5 # intercept along axis 3 (z-axis), think of (x,y,z) axes in the usual way

# COMMAND ----------

import numpy as np
# GENERATE a batch of true data, with a little Gaussian noise added

def make_mini_batch(size=10):
  X = np.random.rand(size, 2) #
  Y = np.matmul(X, [REAL_SLOPE_X1, REAL_SLOPE_X2]) + REAL_INTERCEPT + 0.2 * np.random.randn(size)
  return X.reshape(size,2), Y.reshape(size,1)

# COMMAND ----------

# MAGIC %md
# MAGIC To digest what's going on inside the function above, let's take it step by step.

# COMMAND ----------

 Xex = np.random.rand(10, 2) # Xex is simulating PRNGs from independent Uniform [0,1] RVs
 Xex # visualize these as 10 orddered pairs of points in the x-y plane that makes up our x-axis and y-axis (or x1 and x2 axes)

# COMMAND ----------

Yex = np.matmul(Xex, [REAL_SLOPE_X1, REAL_SLOPE_X2]) #+ REAL_INTERCEPT #+ 0.2 * np.random.randn(10)
Yex

# COMMAND ----------

# MAGIC %md
# MAGIC The first entry in Yex is obtained as follows (change the numbers in the produc below if you reevaluated the cells above) and geometrically it is the location in z-axis of the plane with slopes given by REAL_SLOPE_X1 in the x-axis and REAL_SLOPE_X2 in the y-aixs with intercept 0 at the point in the x-y or x1-x2 plane given by (0.68729439,  0.58462379).

# COMMAND ----------

0.21757443*REAL_SLOPE_X1 +  0.01815727*REAL_SLOPE_X2

# COMMAND ----------

# MAGIC %md
# MAGIC The next steps are adding an intercept term to translate the plane in the z-axis and then a scaled (the multiplication by 0.2 here) gaussian noise from independetly drawn pseudo-random samples from the standard normal or Normal(0,1) random variable via `np.random.randn(size)`.

# COMMAND ----------

Yex = np.matmul(Xex, [REAL_SLOPE_X1, REAL_SLOPE_X2]) + REAL_INTERCEPT # + 0.2 * np.random.randn(10)
Yex

# COMMAND ----------

Yex = np.matmul(Xex, [REAL_SLOPE_X1, REAL_SLOPE_X2])  + REAL_INTERCEPT + 0.2 * np.random.randn(10)
Yex # note how each entry in Yex is jiggled independently a bit by 0.2 * np.random.randn()

# COMMAND ----------

# MAGIC %md
# MAGIC Thus we can now fully appreciate what is going on in `make_mini_batch`. This is meant to substitute for pulling random sub-samples of batches of the real data during stochastic gradient descent.

# COMMAND ----------

make_mini_batch() # our mini-batch of Xx and Ys

# COMMAND ----------

import tensorflow as tf

batch = 10 # size of batch

tf.reset_default_graph() # this is important to do before you do something new in TF

# we will work with single floating point precision and this is specified in the tf.float32 type argument to each tf object/method
x = tf.placeholder(tf.float32, shape=(batch, 2)) # placeholder node for the pairs of x variables (predictors) in batches of size batch
x_aug = tf.concat( (x, tf.ones((batch, 1))), 1 ) # x_aug is a concatenation of a vector of 1`s along the first dimension

y = tf.placeholder(tf.float32, shape=(batch, 1)) # placeholder node for the univariate response y with batch many rows and 1 column
model_params = tf.get_variable("model_params", [3,1]) # these are the x1 slope, x2 slope and the intercept (3 rows and 1 column)
y_model = tf.matmul(x_aug, model_params) # our two-factor regression model is defined by this matrix multiplication
# note that the noise is formally part of the model and what we are actually modeling is the mean response...

error = tf.reduce_sum(tf.square(y - y_model))/batch # this is mean square error where the sum is computed by a reduce call on addition

train_op = tf.train.GradientDescentOptimizer(0.02).minimize(error) # learning rate is set to 0.02

init = tf.global_variables_initializer() # our way into running the TF session

errors = [] # list to track errors over iterations

with tf.Session() as session:
    session.run(init)
    for i in range(1000):
      x_data, y_data = make_mini_batch(batch) # simulate the mini-batch of data x1,x2 and response y with noise
      _, error_val = session.run([train_op, error], feed_dict={x: x_data, y: y_data})
      errors.append(error_val)

    out = session.run(model_params)
    print(out)

# COMMAND ----------

REAL_SLOPE_X1, REAL_SLOPE_X2, REAL_INTERCEPT # compare with rue parameter values - it's not too far from the estimates

# COMMAND ----------

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
fig.set_size_inches((4,3))
plt.plot(errors)
display(fig)

# COMMAND ----------


# -*- coding: utf-8 -*-
"""
Computes the Legendre-Gauss-Lobatto nodes, weights and the LGL Vandermonde
matrix. The LGL nodes are the zeros of (1-x**2)*P'_N(x). Useful for numerical
integration and spectral methods.

Reference on LGL nodes and weights:
  C. Canuto, M. Y. Hussaini, A. Quarteroni, T. A. Tang, "Spectral Methods
  in Fluid Dynamics," Section 2.3. Springer-Verlag 1987

Originally implemented in MATLAB by Greg von Winckel
http://www.mathworks.com/matlabcentral/fileexchange/4775-legende-gauss-lobatto-nodes-and-weights

@author: Nicolas Guarin-Zapata
"""
from __future__ import division, print_function
from numpy import pi, sin, zeros, amax, abs, square, array, linspace
from numpy.linalg import norm
import numpy as np

def gauss_lobatto(N, tol=1e-15):
    """
    Use Chebyshev nodes as first guess.

    Compute P_(N) using the recursion relation
    Compute its first and second derivatives and
    update x using the Newton-Raphson method.

    Chebyshev nodes are computed using the symmetric
    formula that involves sine as presented in:

      Nick Trefethen. Aproximation Theory and
      Aproximation Practice, Chapter 2, exercise 2.

    """
    x = sin(linspace(-pi/2, pi/2, N))
    P = zeros((N, N))  # Vandermonde Matrix
    x_old = 2
    while amax(abs(x - x_old)) > tol:
        x_old = x
        P[:, 0] = 1
        P[:, 1] = x
        for k in range(2, N):
            P[:, k] = ((2 * k - 1) * x * P[:, k - 1] -
                       (k - 1) * P[:, k - 2]) / k
        x = x_old - (x * P[:, N - 1] - P[:, N - 2]) / (N * P[:, N - 1])

    w = 2.0 / ((N - 1) * N * square(P[:, N - 1]))

    return array(x), array(w)

if __name__ == "__main__":
    x, _ = gauss_lobatto(5)
    x_exact = array([-1, -np.sqrt(3/7), 0, np.sqrt(3/7), 1])
    print("Relative error: {:g}".format(norm(x - x_exact)/norm(x_exact)))


# TODO: Create a synthetic dataset - how would the data need to look like if we were able to fit the sine wave on them?

# You want to reverse engineer data which would fit onto your 'ideal' curve:
# Eq: y = amp * np.sin (2* np.pi / 24 * x + shift_h) + shift_v
# Without shift_h:  2.63 * np.sin (2 * np.pi / 24 * x) + 17.18

import numpy as np
import matplotlib.pyplot as plt

def sine_function(x, amp, per, shift_h, shift_v):
    return amp * np.sin(2*np.pi/per * x + shift_h) + shift_v

amp, per, shift_h, shift_v = 2.63, 24.0, 0, 17.18

repeats = 4
x_sine = np.linspace(0, repeats * 24 + 1, 100)
y_sine = sine_function(x_sine, amp, per, shift_h, shift_v)

# Now do the reverse engineering for 5-generational families with phases ranging between 0-24:
def SynthesizeData(phase):
    """ Phase ranges from 0 to 24 with increments of 0.2. """

    x_list = [phase]
    y_list = []

    while len(x_list) < 5 or len(y_list) < 5:
        x = x_list[-1]
        y = sine_function(x=x, amp=amp, per=per, shift_h=shift_h, shift_v=shift_v)
        x_list.append(y+x)
        y_list.append(y)
    x_list = x_list[:-1]

    return x_list, y_list

phase_range = np.linspace(0, 24, 120)
print (phase_range)

x_data_0 = []
y_data_0 = []
for phase in list(phase_range.tolist()):
    x_list, y_list = SynthesizeData(phase=phase)
    plt.scatter(x=x_list, y=y_list, color="purple", alpha=0.2, zorder=2) # label="Fake Data Point",
    y_data_0.append(y_list)
    x_data_0.append([value - x_list[0] for value in x_list])

# Now plot the thing:
plt.plot(x_sine, y_sine, color="turquoise", label="Ideal Sine Wave", zorder=1)
plt.axhline(y=shift_v, color="grey", linestyle="dashed", zorder=0)
plt.axvline(x=24.0, color="gold", linestyle="dashed", zorder=0)
plt.axvline(x=48.0, color="gold", linestyle="dashed", zorder=0)
plt.axvline(x=72.0, color="gold", linestyle="dashed", zorder=0)
plt.xticks(np.arange(0, repeats * 24 + 1, 4))
plt.xlabel("Oscillation Period [hours]")
plt.ylabel("Cell Cycle Duration [hours]")
plt.title("Ideal Sine Wave")
plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.15), ncol=1)
plt.savefig("/Users/kristinaulicna/Documents/Rotation_2/Sine_Wave/Summary_All_Cells/Ideal_Wave.png", bbox_inches="tight")
plt.show()
plt.close()

# Now plot the non-phased data:
for x, y in zip(x_data_0, y_data_0):
    plt.scatter(x=x, y=y)

plt.xlabel("Oscillation Period [hours]")
plt.ylabel("Cell Cycle Duration [hours]")
plt.title("Relationships between generations in an ideal family\n"
          "Equation: {} * np.sin (2 * np.pi / {} * x + {}) + {}"
          .format(amp, per, shift_h, shift_v))
plt.savefig("/Users/kristinaulicna/Documents/Rotation_2/Sine_Wave/Summary_All_Cells/Ideal_Wave_Relationships.png",
            bbox_inches="tight")
plt.show()
plt.close()


#Determining a linear trend line for Bitcoin
import numpy as np
from scipy.linalg import lstsq

year = np.array([2010.91666666666, 2011.41666666666, 2011.91666666666,
             2012.41666666666, 2012.91666666666, 2013.41666666666,
             2013.91666666666, 2014.41666666666, 2014.91666666666,
             2015.41666666666, 2015.91666666666, 2016.41666666666,
             2016.91666666666, 2017.41666666666])
btc_price = np.array([0.23, 9.57, 3.06, 5.27, 12.56, 129.3, 946.92, 629.02,
	              378.64, 223.31, 362.73, 536.42, 753.25, 2452.18])

M = year[:, np.newaxis]**[0, 1]
model, _, _, _ = lstsq(M,btc_price)
print "Intercept =", model[0]
print "year coefficient =", model[1]


"""

Common Algorithms

@author: Naimish Agarwal
"""

import math
import fundamental_data_structures as fds
from numba import jit

@jit
def gcd(p, q):

    """
    Find Greatest Common Factor of integers p, q

    """
    if q == 0:
        return p
    r = p % q
    return gcd(q, r)

@jit
def is_prime(n):
    """

    Returns whether n is a prime number or not

    """

    if n < 2:
        return False

    for i in xrange(2, int(math.sqrt(n))):
        if n % i == 0:
            return False

    return True

def evaluate_arithmetic_expression(expression):
    """

    Evaluate an arithmetic expression using Dijkstra's two stack algorithm.
    The function assumes that each subexpression is enclosed in parenthesis.

    e.g. ( 1 + ( 2 * ( 3 / 7 ) ) )

    It is also assumed that at least one operator and two operands are present.

    e.g. 1 + 2 (valid)

    e.g. 2 (invalid)

    Parameters:
    -----------

    expression: arithmetic expression which can comprise the following:
        (, ), +, -, *, /, numbers

    all separated by whitespace.

    Returns:
    --------

    Value of expression after evaluation

    """

    expression = "( " + expression + " )"
    tokens = expression.split()
    operator_stack = fds.LinkedListStack()
    operand_stack = fds.LinkedListStack()

    for token in tokens:
        if token == "(":
            pass
        elif token in ["+", "-", "*", "/"]:
            operator = token
            operator_stack.push(operator)
        elif token == ")":
            operator = operator_stack.pop()
            num2 = operand_stack.pop()
            num1 = operand_stack.pop()

            print num1, operator, num2
            if operator == "+":
                operand = num1 + num2
            elif operator == "-":
                operand = num1 - num2
            elif operator == "*":
                operand = num1 * num2
            elif operator == "/":
                operand = num1 / num2
            operand_stack.push(operand)
        else:
            operand = float(token)
            operand_stack.push(operand)

    return operand_stack.pop()


# This program was originally authored by Delaney Granizo and Andrei Kirilenko
as a part of the Master of Finance curriculum at MIT Sloan.

"""
In this notebook, we mainly explores the statistical method of computing maximum likelyhood function
for common distributions. A subsequent financial application will be examined through fitting the normal
distributions to asset returns using MLE.
"""

# Basic Imports
import math
import matplotlib.pyplot as plt
import numpy as np
import scipy
import scipy.stats

"""
== Normal Distribution ==

We first explores some basic sampling from a normal distribution. Given the data available, a
function can be defined so that it will compute the MLE for the \mu and \sigma parameters of the
normal distribution.

"""

True_Mean = 80
True_Std = 10
X = np.random.normal(True_Mean, True_Std, 1000)

def normal_mu_MLE(X):
  """Return the MLE given mu in a normal distribution.
  """
  n_obs = len(X)
  sum_obs = sum(x)
  return 1.0/n_obs*sum_obs

def normal_sigma_MLE(X):
  """Return
  """
  n_obs = len(X)
  mu = normal_mu_MLE(X)
  sum_sqd = sum(np.power((X - mu), 2)) # sum up the squared differences.
  sigma_sq = 1.0/ n_obs * sum_sqd
  return math.sqrt(sigma_sq)

pdf = scipy.stats.norm.pdf
x = np.linespace(0, 80, 80)
plt.hist(x, bins=x, normed='true')
plt.plot(pdf(x, loc=mu, scale=std))
plt.xlabel('Value')
plt.ylabel('Observed Frequency')
plt.legend(['Fitted Distribution PDF', 'Observed Data', ])

# Exponential Distribution
TRUE_LAMDA = 5
X = np.random.exponetial(TRUE_LAMDA, 1000)

def exp_lamda_MLE(X):
  T = len(X)
  s = sum(X)
  return s/T

pdf = scipy.stats.exon.pdf
x = range(0, 80)
plt.hist(X, bins=x, normed='true')
plt.plot(pdf(x, scale=1))
plt.xlabel('Value')
plt.ylabel('Observed Frequency')
plt.legend(['Fitted Distribution PDF', 'Observed Data', ])

prices = get_pricing('AAPL', fields='price', start_date='2018-01-01', end_date='2019-12-31')
absolute_returns = np.diff(prices)
returns = absolute_returns / prices[:-1]

# Using the scipy's fit function to get the mu and sigma
mu, std = scipy.stats.norm.fit(returns)
pdf = scipy.stats.norm.pdf
x = np.linspace(-1, 1, num= 100)
h = plt.hist(returns, bins=x, normed='true')
l = plt.plot(x, pdf(X, loc=mu, scale=std))

# Note that fitting this sample to normal would not work if they do not follow a normal in the
# first place. Such, Jarque-Bera normality test can be carried out. The hypothesis of normal
# distribution will be rejected if the p-value is under a threshold c.


import numpy as np
import sys

sys.path.append("..")
from fd_partial_derivative import fd_partial_derivative

# Choose grid size
gs = np.array([19,15])
h = 1.0/(gs-1)

# Build a grid
x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]))
V = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

# Build staggered grid in x direction
x, y = np.meshgrid(np.linspace(0,1,gs[0]-1),np.linspace(0,1,gs[1]))
Vx = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)
# Build staggered grid in y direction
x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]-1))
Vy = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

# Build partial derivative matrices
Dx = fd_partial_derivative(gs=gs,h=h,direction=0)
Dy = fd_partial_derivative(gs=gs,h=h,direction=1)

# all rows must sum up to zero (i.e. a constant function has zero derivative)
assert((np.isclose(Dx.sum(axis=1),np.zeros((Dx.shape[0],1)))).all())
assert((np.isclose(Dy.sum(axis=1),np.zeros((Dy.shape[0],1)))).all())

# Build linear function
f = 2*V[:,0] + 5*V[:,1]
computed_derivative_x = Dx*f
computed_derivative_y = Dy*f
# Derivatives must be 2.0 and 5.0, respectively
assert((np.isclose(computed_derivative_x,2.0*np.ones((computed_derivative_x.shape[0])))).all())
assert((np.isclose(computed_derivative_y,5.0*np.ones((computed_derivative_y.shape[0])))).all())

# Convergence test
linf_norm_x = 100.0
linf_norm_y = 100.0
print("This experiment should print a set of decreasing values, converging")
print("towards zero and decreasing roughly by half in each iteration")
for power in range(3,13,1):
    gs = np.array([2**power,2**power - 2])
    h = 1.0/(gs-1)
    # Build a grid
    x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]))
    V = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

    # Build staggered grid in x direction
    x, y = np.meshgrid(np.linspace(0,1,gs[0]-1),np.linspace(0,1,gs[1]))
    Vx = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)
    # Build staggered grid in y direction
    x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]-1))
    Vy = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

    # Build partial derivative matrices
    Dx = fd_partial_derivative(gs=gs,h=h,direction=0)
    Dy = fd_partial_derivative(gs=gs,h=h,direction=1)
    # Build non-linear function
    f = np.cos(V[:,0]) + np.sin(V[:,1])
    # Derivatives on staggered grids
    fx = -np.sin(Vx[:,0])
    fy =  np.cos(Vy[:,1])
    # Computed derivatives using our matrices
    computed_derivative_x = Dx*f
    computed_derivative_y = Dy*f
    # Print L infinity norm of difference
    # Make sure norm is decreasing
    assert(linf_norm_x>np.max(np.abs(computed_derivative_x - fx)))
    assert(linf_norm_y>np.max(np.abs(computed_derivative_y - fy)))
    linf_norm_x = np.max(np.abs(computed_derivative_x - fx))
    linf_norm_y = np.max(np.abs(computed_derivative_y - fy))
    # Print L infinity norm of difference
    print(np.array([np.max(np.abs(computed_derivative_x - fx)),np.max(np.abs(computed_derivative_y - fy))]))

print("Unit test passed, all asserts passed")


import numpy as np
import matplotlib.pyplot as plt

#coiefficient calculation
def regress(x, x_s, t_s, M, N,lamda = 0):
    order_list =  np.arange((M + 1))
    order_list =  order_list[ :, np.newaxis]
    exponent = np.tile(order_list,[1,N])
    h = np.power(x_s,exponent)
    a =  np.matmul(h, np.transpose(h)) + lamda*np.eye(M+1)
    b =  np.matmul(h, t_s)
    w = np.linalg.solve(a, b) #calculate the coefficent

    exponent2 = np.tile(order_list,[1,200])
    h2 = np.power(x,exponent2)
    p = np.matmul(w, h2)
    return p

##task 1
x =  np.linspace(0, 1, 200)
t =  np.sin(np.pi*x*2)

N = 10
sigma = 0.2;
x_10 = np.linspace(0, 1, N)
t_10 = np.sin(np.pi*x_10*2) + np.random.normal(0,sigma,N)

plt.figure(1)
plt.plot(x, t, 'g',  x_10, t_10, 'bo',linewidth = 2)
plt.ylabel('t',rotation='horizontal')
plt.xlabel('x')
plt.savefig('1.png', dpi=300)

##task 2
M = 3
p3_10 =  regress(x, x_10, t_10, M, N)

M = 9
p9_10 =  regress(x, x_10, t_10, M, N)

plt.figure(2)
plt.plot(x, t, 'g',  x_10, t_10, 'bo', x, p3_10, 'r', linewidth = 2)
plt.ylabel('t',rotation='horizontal')
plt.xlabel('x')
plt.text(0.7, 0.7,'M=3', fontsize=16)
plt.savefig('2.png', dpi=300)

plt.figure(3)
plt.plot(x, t, 'g',  x_10, t_10, 'bo', x, p9_10, 'r', linewidth = 2)
plt.ylabel('t',rotation='horizontal')
plt.xlabel('x')
plt.text(0.7, 0.7,'M=9', fontsize=16)
plt.savefig('3.png', dpi=300)

##task3
N = 15
x_15 =  np.linspace(0, 1,  N)
t_15 = np.sin(np.pi*x_15*2) + np.random.normal(0,sigma, N)

M = 9
p9_15 =  regress(x, x_15, t_15, M, N)

N = 100
x_100 =  np.linspace(0, 1,  N)
t_100 = np.sin(np.pi*x_100*2) + np.random.normal(0,sigma, N)

M = 9
p9_100 =  regress(x, x_100, t_100, M, N)

plt.figure(4)
plt.plot(x, t, 'g',  x_15, t_15, 'bo', x, p9_15, 'r', linewidth = 2)
plt.ylabel('t',rotation='horizontal')
plt.xlabel('x')
plt.text(0.7, 0.7,'N=15', fontsize=16)
plt.savefig('4.png', dpi=300)

plt.figure(5)
plt.plot(x, t, 'g',  x_100, t_100, 'bo', x, p9_100, 'r', linewidth = 2)
plt.ylabel('t',rotation='horizontal')
plt.xlabel('x')
plt.text(0.7, 0.7,'N=100', fontsize=16)
plt.savefig('5.png', dpi=300)

##task4
N = 10
M = 9
p9_10 =  regress(x, x_10, t_10, M, N, np.exp(-18))

plt.figure(6)
plt.plot(x, t, 'g',  x_10, t_10, 'bo', x, p9_10, 'r', linewidth = 2)
plt.ylabel('t',rotation='horizontal')
plt.xlabel('x')
plt.text(0.7, 0.7,'ln$\lambda$ = -18', fontsize=16)
plt.savefig('6.png', dpi=300)
plt.show()


import math
import numpy as np

### Note ###

# z is weighted input

### Functions ###

def linear(z,m):
	return m*z

def elu(z,alpha):
	return z if z >= 0 else alpha*(e^z -1)

def leakyrelu(z, alpha):
	return max(alpha * z, z)

def relu(z):
  return max(0, z)

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

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

### Derivatives ###

def linear_prime(z,m):
	return m

def elu_prime(z,alpha):
	return 1 if z > 0 else alpha*np.exp(z)

def leakyrelu_prime(z, alpha):
	return 1 if z > 0 else alpha

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

def relu_prime(z):
  return 1 if z > 0 else 0

def tanh_prime(z):
	return 1 - np.power(tanh(z), 2)


import numpy as np
from matplotlib import pyplot as plt
import time

def bezier_curve_3(xpoints, ypoints, nTimes=1000):
    ret_x = []
    ret_y = []
    for i in np.arange(0, nTimes + 1):
        t = i / nTimes
        x = (1 - t) ** 3 * xpoints[0] + 3.0 * t * (1 - t) ** 2 * xpoints[1] + 3.0 * (t) ** 2 * (1 - t) * xpoints[
            2] + t ** 3 * xpoints[3]
        ret_x.append(x)
        y = (1 - t) ** 3 * ypoints[0] + 3.0 * t * (1 - t) ** 2 * ypoints[1] + 3.0 * (t) ** 2 * (1 - t) * ypoints[
            2] + t ** 3 * ypoints[3]
        ret_y.append(y)
    return ret_x, ret_y

def temp_curve_3(x, y, nTimes=1000):
    dx_0 = (x[1] - x[0]) * 2 / nTimes
    dy_0 = (y[1] - y[0]) * 2 / nTimes

    tdx = (x[2] - 2 * x[1] + x[0]) * 2 / nTimes ** 2
    tdy = (y[2] - 2 * y[1] + y[0]) * 2 / nTimes ** 2

    tddx = (x[3] - 3 * (x[2] - x[1]) - x[0]) * 2 / nTimes ** 3
    tddy = (y[3] - 3 * (y[2] - y[1]) - y[0]) * 2 / nTimes ** 3

    M_0 = x[0]
    N_0 = y[0]

    TM = (x[1] - x[0]) / nTimes  # TMS
    TN = (y[1] - y[0]) / nTimes  # TNS

    ret_x = []
    ret_y = []
    ret_x.append(M_0)
    ret_y.append(N_0)
    C = nTimes

    atdx = tdx / 2
    atdy = tdy / 2

    atddx = tddx / 2
    atddy = tddy / 2
    for i in np.arange(1.0, C+1):
        M_0 += dx_0 + atdx
        N_0 += dy_0 + atdy

        atdx += tdx
        atdy += tdy

        TM += tdx + atddx
        TN += tdy + atddy

        atddx += tddx
        atddy += tddy

        M = M_0 + (i * (TM))
        N = N_0 + (i * (TN))

        ret_x.append(M)
        ret_y.append(N)
    return ret_x, ret_y

xpoints = [-6.0000000001, -4.00000000011, -0.000000000111, -0.0000000001111]
ypoints = [-5.0000000001, 3.00000000011, 0.000000000111, -4.0000000001111]

plt.rcParams["figure.figsize"] = (10, 10)
plt.axis([-8, 2, 2, -8])

plt.plot(xpoints, ypoints, "b")

loop_times = 1000
proc_times = 1000

start = time.time()
for i in np.arange(loop_times):
    X, Y = bezier_curve_3(xpoints, ypoints, nTimes=proc_times)

end = time.time()
plt.plot(X, Y, "k")
print("Bezier_3 time : ", end - start, "(start : ", start, " end : ", end)

start = time.time()
for i in np.arange(loop_times):
    X, Y = temp_curve_3(xpoints, ypoints, nTimes=proc_times)

end = time.time()
plt.plot(X, Y, "m")
print("temp_3 time : ", end - start, "(start : ", start, " end : ", end)

plt.show()


"""
A perfect number is a number for which the sum of its proper divisors is exactly equal to the number.
For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28,
which means that 28 is a perfect number.

A number n is called deficient if the sum of its proper divisors is less than n and it is called abundant
if this sum exceeds n.

As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum
of two abundant numbers is 24.
By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two
abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that
the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.

Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.
"""

import numpy as np

# all numbers over this can be written as sums of abundant numbers
limit = 28123

def d(n):
    divisors = set()
    divisors.add(1)

    less_half = np.math.floor(n / 2)

    for i in range(2, less_half + 1):

        if n % i == 0:
            divisors.add(i)

    return sum(divisors)

def all_abundant():
    abundant = list()

    for i in range(12, limit + 1):
        if d(i) > i:
            abundant.append(i)

    return abundant

abundant_numbers = all_abundant()

# Sieve like algorithm to zero out all numbers that can be written as the sum of abundant numbers
# then take the sum of the remaining
rang = [x for x in range(0, limit)]

for i in abundant_numbers:
    for j in abundant_numbers:
        if (i + j) < limit:
            # print(i+j)
            rang[i + j] = 0
        else:
            break

print(f'The sum of positive integers that cannot be written as the sum of two abundant numbers is: {sum(rang)}')

Solutions/Problem 23 - Non-abundant sums.py


import numpy
import scipy.io
'''
This code was originally in Matlab
function generatePoissonTrains
%% author: Paula Kuokkanen
%% 5. Mai 2011

traslated to Python: Nikolay Chenkov
June 2013

homogeneous Poisson process

A homogeneous Poisson process generates events (spikes) with a constant
probability or rate r(t) = r. Spikes are independent. Thus, at each
time point t, the probability P of observing a spike within
a sufficiently small temporal window from t to t + delta_t is
P= r*delta_t.

There are two simple ways of implementing a homogeneous Poisson process:
1.    Progress through time in small steps of size delta_t,
      draw a random number x (from a uniform distribution
      between 0 and 1, Matlab function 'rand'
      at each time step. If x < P, generate a spike.

2.    Generate interspike intervals from an exponential probability
      density. When x is uniformly distributed between 0 and 1,
      its negative logarithm is exponentially distributed. Thus, we
      can generate spike times t(i) iteratively from the
      formula t(i+1) = t(i) - log(x)/r.

Using implementation #2, generate 1000 spikes by a homogeneous
Poisson process with a rate of r=100 (Hz)
'''

n = 1000   # desired number of spikes
rate = 100  # rate in spikes per second
randNumbers = numpy.random.rand(n)  # n random numbers, uniform distribution
ISIs = -numpy.log(randNumbers)/rate   # ISIs in seconds
ISIs = ISIs*1000   # ISIs in milliseconds

# compute spike times from ISIs
SpikeTimes = numpy.cumsum(ISIs)

'''
# inhomogeneous Poisson process

 An inhomogeneous Poisson process is characterized by a time-dependent
 rate r(t). There are several ways of generating spikes.
 Two basic ways are:
 1.    Progress through time in small intervals of width delta_t.
       For sufficiently small delta_t, the probability P(i) of
       observing a spike in interval i from t(i) = i*delta_t to
       t_(i) + delta_t is then given by
           P(i) = integral_t(i)^[t_(i)+delta_t)] r(tau) d tau .
       Then, draw a random number x(i) from a uniform distribution
       for each time interval. If x(i) < P(i)$, generate a spike.

 2.    `Thinning' the spike train of a homogeneous Poisson process:
       First, a spike sequence is generated with a homogeneous
       Poisson process at rate r_max = max[r(t)]. The spike
       sequence is then thinned by generating a random number
       x(i) for each spike and removing the spike at time
       t(i) from the train if r(t(i))/r_max < x(i).

 We use now method 2, thinning. Our probability distribution r is
     r(t) = A* sin(2*pi*f*t) + r_0
 with A = 50 Hz, r_0 = 100 Hz and f = 10 Hz. We generate 1000 spikes.

 First, start with the homogeneous, but with more spikes so that you can
 thin out later on
'''

n = 10000   # desired number of spikes
maxRate = 150  # rate in spikes per second
randNumbers2 = numpy.random.rand(n)  # n random numbers, uniform distribution
ISIs2 = -numpy.log(randNumbers2)/maxRate   # ISIs in seconds
ISIs2 = ISIs2*1000   # ISIs in milliseconds

# compute spike times from ISIs
SpikeTimes2 = numpy.cumsum(ISIs2)

# thin the spike train

randNumbers = numpy.random.rand(n)
rates = 50.*numpy.sin(SpikeTimes2*2*numpy.pi/100) + 100
Ps = rates/maxRate
SpikeTimes_inh = SpikeTimes2[randNumbers>Ps]
n = 1000
SpikeTimes_inh = SpikeTimes_inh[0:n]     # take just 1000 spikes

'''
Poisson process with absolute refractory period

Real neurons are in a refractory state immediately after a spike.
Thus, during a certain 'refractory period' after a spike, it is
impossible or much harder to evoke the next spike.
When the effect of refractoriness is taken into account, spikes
can no longer considered to be independent because the probability
of observing the next spike then also depends on the time that has
passed since the last spike.

In point process models, an approximation of refractoriness is to
set the probability of generating a spike to 0 for a certain absolute
refractory period tr after a spike, and then to r afterwards.

We simulate a homogeneous Poisson process and add an absolute refractory
period of t_r = 5 ms. We use different 'driving' rates
r = 10, 50, 100, 200, 500, and 1000Hz.
We simulate 1000 spikes for each rate r.

Adding an absolute refractory period corresponds to simply adding a
fixed value to each interspike interval of a 'normal'
homogeneous Poisson process.
'''

rates_ref = [10, 50, 100, 200, 500, 1000]  # "driving" rates in spikes per second
n = 1000   # desired number of spikes
ISIs_ref = numpy.zeros((n,len(rates_ref)))
randNumbers3 = numpy.random.rand(n)  #n random numbers, uniform distribution
t_r = 5    # absolute refractory period in ms

for i,r in enumerate(rates_ref):
    ISIs_ref[:,i] = -numpy.log(randNumbers3)/r*1000 + t_r  #ISIs in milliseconds

# compute spike times from ISIs
SpikeTimes_ref = numpy.zeros((n,len(rates_ref)))
for i,r in enumerate(rates_ref):
    SpikeTimes_ref[:,i] = numpy.cumsum(ISIs_ref[:,i])

scipy.io.savemat('PoissonSpikeTrains.mat',
                    {'SpikeTimes_hom': SpikeTimes,
                    'SpikeTimes_inh': SpikeTimes_inh,
                    'SpikeTimes_ref': SpikeTimes_ref,
                    'rates_ref': rates_ref})