#!/usr/bin/env python3

import tensorflow as tf
from tensorflow.python import debug as tf_debug
import numpy as np
import argparse
import os.path as path

def build_gregory_leibniz_series():
    """
    Calculate Pi using Gregory–Leibniz series
            oo
            __        n
            \     (-1)
    pi = 4  /   -------
            --   2n + 1
            n=0
    """
    with tf.name_scope('gls'):
        n = tf.Variable(0, name='n')
        pi = tf.Variable(0, name='pi', dtype=tf.float64)
        gregory_leibniz = tf.multiply(np.float64(4), tf.divide(tf.pow(-1, n), tf.add(tf.multiply(2, n), 1)))

        update_pi = tf.assign_add(pi, gregory_leibniz)

        with tf.control_dependencies([update_pi]):
            update_n = tf.assign_add(n, 1)

        tf.summary.scalar('gls_pi', update_pi)
        merged = tf.summary.merge_all()

        return 'Gregory Leibniz series', [update_pi, update_n, merged]

def build_bbp():
    """
    Calculate Pi using BBP digit extraction
            oo
            __
            \     1     /    4            2            1             1       \
    pi =    /   ----   |  --------  -  --------  -  --------  -  ---------   |
            --   16^k   \  8k + 1       8k + 4       8k + 5        8k + 6    /
            k=0
    """
    with tf.name_scope('bbp'):
        k = tf.Variable(0, name='k', dtype=tf.float64)
        pi = tf.Variable(0, name='pi', dtype=tf.float64)
        first_part = tf.divide(np.float64(4), tf.add(tf.multiply(np.float64(8), k), 1))
        second_part = tf.divide(np.float64(2), tf.add(tf.multiply(np.float64(8), k), 4))
        third_part = tf.divide(np.float64(1), tf.add(tf.multiply(np.float64(8), k), 5))
        fourth_part = tf.divide(np.float64(1), tf.add(tf.multiply(np.float64(8), k), 6))
        subtract_result = tf.subtract(tf.subtract(tf.subtract(first_part, second_part), third_part), fourth_part)
        bailey = tf.multiply(tf.pow(tf.reciprocal(np.float64(16)), k), subtract_result)

        update_pi = tf.assign_add(pi, bailey)
        with tf.control_dependencies([update_pi]):
            update_k = tf.assign_add(k, 1)
        tf.summary.scalar('bbp_pi', update_pi)
        merged = tf.summary.merge_all()

        return 'BBP digit extraction', [update_pi, update_k, merged]

def run_pi_calculation(description, graphs, iteration=15000, print_every=1000, debug=False, log_path=None):
    with tf.Session() as sess:
        train_writer = None
        if log_path:
            train_writer = tf.summary.FileWriter(path.join(log_path, description.split(' ')[0]), sess.graph)
        if debug:
            sess = tf_debug.LocalCLIDebugWrapperSession(sess)
        sess.run(tf.global_variables_initializer())
        print('Running pi approximation algorithm {}:'.format(description))
        for i in range(1, iteration+1):
            result, _, summary = sess.run(graphs)
            if train_writer:
                train_writer.add_summary(summary, i)
            if i % print_every == 0:
                print('iteration {:>10}:  Pi = {}'.format(i, result))
        print()
        if train_writer:
            train_writer.close()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--debug", help="Enables TensorFlow debugger.", action="store_true")
    parser.add_argument("-l", "--logPath", help="Saves TensorBoard logs at path.")
    args = parser.parse_args()

    gls = build_gregory_leibniz_series()
    bbp = build_bbp()

    run_pi_calculation(gls[0], gls[1], debug=args.debug, log_path=args.logPath)
    run_pi_calculation(bbp[0], bbp[1], iteration=15, print_every=1, debug=args.debug, log_path=args.logPath)


import numpy as np
import pandas as pd

def numpy_dot():
    '''
    Imagine a point system in which each country is awarded 4 points for each
    gold medal,  2 points for each silver medal, and one point for each
    bronze medal.

    Using the numpy.dot function, create a new dataframe called
    'olympic_points_df' that includes:
        a) a column called 'country_name' with the country name
        b) a column called 'points' with the total number of points the country
           earned at the Sochi olympics.

    You do not need to call the function in your code when running it in the
    browser - the grader will do that automatically when you submit or test it.
    '''

    countries = [
        'Russian Fed.', 'Norway', 'Canada', 'United States', 'Netherlands',
        'Germany', 'Switzerland', 'Belarus', 'Austria', 'France', 'Poland',
        'China', 'Korea', 'Sweden', 'Czech Republic', 'Slovenia', 'Japan',
        'Finland', 'Great Britain', 'Ukraine', 'Slovakia', 'Italy', 'Latvia',
        'Australia', 'Croatia', 'Kazakhstan'
    ]

    gold = [
        13, 11, 10, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0,
        0, 0, 0
    ]
    silver = [
        11, 5, 10, 7, 7, 6, 3, 0, 8, 4, 1, 4, 3, 7, 4, 2, 4, 3, 1, 0, 0, 2, 2,
        2, 1, 0
    ]
    bronze = [
        9, 10, 5, 12, 9, 5, 2, 1, 5, 7, 1, 2, 2, 6, 2, 4, 3, 1, 2, 1, 0, 6, 2,
        1, 0, 1
    ]

    # YOUR CODE HERE
    olympic_medal_counts = {
        'country_name':
        pd.Series(countries),
        'points':
        pd.Series(
            np.dot(np.array([gold, silver, bronze]).T, np.array([4, 2, 1])))
    }

    olympic_points_df = pd.DataFrame(olympic_medal_counts)
    return olympic_points_df

res = numpy_dot()
print res


#
#	Solved Problems in Geostatistics
#
# ------------------------------------------------
#	Script for lesson 2.3
#	"Standartization & probability intervals"
# ------------------------------------------------

from numpy import *
from scipy import *

import sys
sys.path.append(r'../shared')

from gaussian_cdf import *
from statistics import *
from gslib import *

# ---------------------------------------------------
#	Problem:
#
#	Consider 10 data that are available over 2D area of interest, that is nominally 50 x 50 distance units square. Assume, that the data is known to be a normal distribution with mean of 12.0 and a variance of 16.0
#
# ----------------------------------------------------

print "----------------------------------------------------"
print "Loading data & initializing..."

# Loading sample data from file
x = 10
y = 4
dict = load_gslib_file("samples.txt")

# Mean and variance
mean = 12.0
var = 16.0

# Standart deviation
st_dev = sqrt(var)

print "Done."
print "----------------------------------------------------"

data_points_variable = dict["Variable"]
data_points_easting = dict["Easting"]
data_points_northing = dict["Northing"]
# ----------------------------------------------------
#	(1) Calculate 95% centered probability interval for the mean, that is, the 0.025 quantile and the 0.975 quantile of the theoretical distribution for the mean.
# ----------------------------------------------------

# Probability interval
p = 0.95

# Calculating quantiles
quantiles = zeros( (2), order = 'F', dtype = float32)
# 1: 0.025 quantile
quantiles[0] = (1-p)/2
# 2: 0.975 quantile
quantiles[1] = (1+p)/2

# Retrieving probability intervals
probs = zeros( (2), order = 'F', dtype = float32)
probs[0] = inverse_normal_score(quantiles[0], mean, var)
probs[1] = inverse_normal_score(quantiles[1], mean, var)

# Printing result on screen
print "Probability interval is: [", probs[0], ",", probs[1], "]"
print "----------------------------------------------------"

# ----------------------------------------------------
#	(2) Transform the data to be standart normal and recalculate the 95% probability interval.
# ----------------------------------------------------

# Transforming data to standart normal form
for i in xrange(x):
	data_points_variable = stand(data_points_variable, mean, st_dev)

# Retrieving probability intervals of transformed data
probs_trans = zeros( (2), order = 'F', dtype = float32)
probs_trans[0] = inverse_normal_score(quantiles[0])
probs_trans[1] = inverse_normal_score(quantiles[1])

# Printing result on screen
print "Probability interval for standartized normal data is: [", probs_trans[0], ",", probs_trans[1], "]"
print "----------------------------------------------------"

# ----------------------------------------------------
#	(3) Consider now that the data are spatially dependent, and this dependece is characterized by a correlation function specified as C(h) = exp(-0.2h) where h is calculated as the distance between the samples. Note that this function applies to the standart variable, that is, C(0) = 1.0 (the variance of a standart variable). These values would have to be multiplied by 16 to establish the actual covariance values. Calculate variance of the mean in this case and calculate 95% centered probability interval.
# ----------------------------------------------------

# Function for calculating covariance with model C(h) = exp(-0.2h)
def calc_cov(h):
	cov_h = exp(-0.2*h)
	return cov_h

# Calculating covariance matrix
cov_h = zeros( (x,x), order = 'F', dtype = float32)

for i in xrange(x):
	for j in xrange(x):

		# First point coordinates
		x1 = data_points_easting[i]
		y1 = data_points_northing[i]

		# Second point coordinates
		x2 = data_points_easting[j]
		y2 = data_points_northing[j]

		# Calculating distance
		distance = calc_distance(x1,y1,x2,y2)

		# Calculating covariance
		cov_h [i,j] = calc_cov(distance)

# 1. Standartized variable

# Calculating variance of the mean
var_cov = cov_h.mean()
print "Variance of standartized data in presense of corellation is: ", var_cov

# Retrieving probability intervals for corellated data
probs_cov = zeros( (2), order = 'F', dtype = float32)
probs_cov[0] = inverse_normal_score(quantiles[0], var_cov, var)
probs_cov[1] = inverse_normal_score(quantiles[1], var_cov, var)

print "Probability interval for standartized data in presense of corellation is: [", probs_cov[0], ",", probs_cov[1], "]"
print "----------------------------------------------------"

# 2. Original variable

# Calculating variance of the mean
var_cov_orig = cov_h.mean() * var
print "Variance of data in presense of corellation is: ", var_cov_orig

# Retrieving probability intervals for corellated data
probs_cov = zeros( (2), order = 'F', dtype = float32)
probs_cov[0] = inverse_normal_score(quantiles[0], var_cov_orig, var)
probs_cov[1] = inverse_normal_score(quantiles[1], var_cov_orig, var)

print "Probability interval for data in presense of corellation is: [", probs_cov[0], ",", probs_cov[1], "]"

# Print covariance matrix
# print "Covariance matrix: \n", cov_h

src/sample-scripts/solved_problems/2.3. Standartization and Probability Intervals/problem_2.3.py


import math
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt

r = 0.08
init_s = 100
sigma_s = 0.3
T = 1
K = 100
barrier = 150
init_f = 200
sigma_f = 0.25
debt = 175
recovery = 0.25
corr = 0.2
corr_mat = np.array([[1, corr], [corr, 1]])
L = np.linalg.cholesky(corr_mat)
n_steps = 12  # monthly
dT = 1/n_steps
S = np.array([[init_s], [init_f]])
sigma = np.array([[sigma_s], [sigma_f]])

def a_path(S, r, sigma, Z, dT):
    return S*np.exp(np.cumsum((r-0.5*sigma*sigma)*dT+sigma*np.sqrt(dT)*Z, 1))

def UpOutCall(p, K, L, r, T):
    if max(p) > L:
        return 0
    else:
        return max(0, p[-1]-K)*np.exp(-r*T)

np.random.seed(10)
opt_est = [None]*50
opt_std = [None]*50
d_opt_est = [None]*50
d_opt_std = [None]*50
cva_est = [None]*50
cva_std = [None]*50
for i in range(1, 51):
    p_array = np.zeros(1000*i)
    l_array = np.zeros(1000*i)
    for j in range(1000*i):
        Z = np.matmul(L, norm.rvs(size=(2, 12)))
        p_path = a_path(S, r, sigma, Z, dT)
        s_path = p_path[0]
        f_path = p_path[1]
        p_array[j] = UpOutCall(s_path, K, barrier, r, T)
        l_array[j] = np.exp(-r*T)*(1-recovery)*(f_path[-1] < debt)*p_array[j]
    opt_est[i-1] = p_array.mean()
    opt_std[i-1] = p_array.std()/np.sqrt(1000*i)
    cva_est[i-1] = l_array.mean()
    cva_std[i-1] = l_array.std()/np.sqrt(1000*i)
    d_opt_est[i-1] = opt_est[i-1]-cva_est[i-1]
    d_opt_std[i-1] = np.std(p_array-l_array)/np.sqrt(1000*i)

plt.plot(opt_est, '.')
plt.plot(opt_est+3*np.array(opt_std))
plt.plot(opt_est-3*np.array(opt_std))
plt.ylabel('Price')
plt.xlabel('N*1000 Sample Size')
plt.title('Option Price (Default Free)')
plt.show()
plt.plot(cva_est, '.')
plt.plot(cva_est+3*np.array(cva_std))
plt.plot(cva_est-3*np.array(cva_std))
plt.ylabel('Price')
plt.xlabel('N*1000 Sample Size')
plt.title('Credit Valuation Adjustment')
plt.show()
plt.plot(d_opt_est, '.')
plt.plot(d_opt_est+3*np.array(d_opt_std))
plt.plot(d_opt_est-3*np.array(d_opt_std))
plt.ylabel('Price')
plt.xlabel('N*1000 Sample Size')
plt.title("Option Price with CVA")
plt.show()


import numpy as np
import pandas as pd

def PCA_from_SVD(A):
    """
    Función para PCA a partir de la SVD de numpy
    params: A			matriz de datos
            num_componentes 	número de componentes deseados

    return: valores_singulares	Los valores singulares de la descomposición SVD
	    componentes		Los coeficientes para calcular los componentes principales
	    Z			Los datos transformados (componentes principales)
	    varianza_explicada	La varianza explicada por cada componente principal
    """

    # Centrar los datos
    A = np.array(A) # convertir los datos a un numpy array por si vienen de un DataFrame
    A_centered = A - A.mean(axis=0)

    # Calcular SVD
    U, S, Vt = np.linalg.svd(A_centered, full_matrices=False)

    # Los valores singulares
    valores_singulares = S

    # Los componentes (coeficientes)
    componentes = ((Vt))

    # Los datos transformados (componentes principales)
    Z = A_centered@np.transpose(Vt)

    # La varianza explicada
    varianza_explicada = S**2/np.sum(S**2)

    # Calcula número de componentes de manera automatica de acuerdo a la variana explicada
    # Threshold de 60%
    n = A.shape[1] #numero de columnas
    varianza_acumulada = varianza_explicada.cumsum()
    conteo = (varianza_acumulada)  <  0.8
    num_componentes = conteo.sum() + 1

    # regresar 4 objetos
    return valores_singulares[:num_componentes], componentes[:num_componentes].T, Z[:,:num_componentes], varianza_explicada[:num_componentes]


import math
import numpy as np

def sigmoid_activation(z):
    z = np.clip(z,-60.0,60.0)
    return 1.0 / (1.0 + np.exp(-z))

def tanh_activation(z):
    z = np.clip(z,-60.0,60.0)
    return np.tanh(z)

def sin_activation(z):
    z = np.clip(z,-60.0,60.0)
    return np.sin(z)

def gauss_activation(z):
    z = np.clip(z,-60.0,60.0)
    return np.exp(-0.5 * z**2) / np.sqrt(2 * math.pi)

def relu_activation(z):
    return z if z > 0.0 else 0

def identity_activation(z):
    return z

def clamped_activation(z):
    return np.clip(z,-1.0,1.0)

def inv_activation(z):
    if z == 0:
        return 0.0

    return 1.0 / z

def log_activation(z):
    z = np.clip(z,1e-7,1.0e99)
    return np.log(z)

def exp_activation(z):
    z = np.clip(z,-60.0,60.0)
    return np.exp(z)

def abs_activation(z):
    return np.abs(z)

def hat_activation(z):
    return np.clip(1.0-np.abs(z),0.0,1.5)

def square_activation(z):
    return z ** 2

def cube_activation(z):
    return z ** 3

activations = {'sigmoid':sigmoid_activation,
               'tanh': tanh_activation,
               'sin': sin_activation,
               'gauss': gauss_activation,
               'relu': relu_activation,
               'identity': identity_activation,
               'clamped': clamped_activation,
               'inv': inv_activation,
               'log': log_activation,
               'exp': exp_activation,
               'abs': abs_activation,
               'hat': hat_activation,
               'square': square_activation,
               'cube': cube_activation}

class InvalidActivationFunction(Exception):
    pass

class ActivationFunctionSet(object):
    def __init__(self):
        self.functions = {}

    def add(self, config_name, function):
        # TODO: Verify that the given function has the correct signature.
        self.functions[config_name] = function

    def get(self, config_name):
        f = self.functions.get(config_name)
        if f is None:
            raise InvalidActivationFunction("No such function: {0!r}".format(config_name))

        return f

    def is_valid(self, config_name):
        return config_name in self.functions


import numpy as np

def Usolve(U, y):
    """Backward solve an upper triangular system Ux = y for x
    Parameters:
      U: the matrix, must be square, upper triangular, with nonzeros on the diagonal
      y: the right-hand side vector
    Output:
      x: the solution vector to U @ x == y
    """
    m, n = U.shape
    assert m == n, "matrix U must be square"
    assert np.all(np.triu(U) == U), "matrix U must be Upper triangular"
    assert np.all(np.diag(U) != 0), "matrix U must have ones on the diagonal"
    yn, = y.shape
    assert yn == n,"must be same size as U"
    # Make a copy of y that we will transform into the solution
    x = y.astype(np.float64).copy()
    # back solve
    for col in reversed(range(n)):
        x[col] = x[col]/U[col,col]
        x[:col] = x[:col] - x[col]*U[:col,col]
    return x


from util import utils
import os
import numpy as np
from mpl_toolkits.mplot3d import *
from sklearn import linear_model

F = None #num of features
N = None #num of training samples
T = None #num of test samples

def readData(filename):
    filePath = os.path.join(utils.getResourcesPath(), filename)
    f = open(filePath, 'r')
    global F, N, T
    F, N = map(int, f.readline().strip().split())

    data = []
    for _ in range(0, N):
        line = list(map(float, f.readline().strip().split()))
        data.append((line[:-1], line[-1]))

    T = int(f.readline().strip())
    test = []
    for _ in range(0, T):
        line = list(map(float, f.readline().strip().split()))
        test.append(line)
    return data, test

def gradientDescent(features, values, nIter = 500, lRate = 0.001):
    X = np.append(features, np.ones((len(features),1)), axis=1) #dummy column for intercept weight
    weights = np.random.random(X.shape[1]) #consider intercept weight
    history = []
    for _ in range(nIter):
        pred = X.dot(weights).flatten()
        for i in range(F+1):
            #errors2 = [(values[i]-np.dot(weights,X[i]))*X[i] for i in range(N)]
            errors = (values - pred)* X[:, i]
            weights[i] += lRate * errors.sum()

            #necessary 1/n factor??
        e = sum([(values[i]-np.dot(weights,X[i]))**2 for i in range(N)])
        history.append(e)
        #history.append(weights)
    return weights, history

data, test = readData('hackerrank\\housePricing.txt')
features = np.array([[row[0][x] for x in range(F)] for row in data])
values = np.array([row[1] for row in data])

weights, history = gradientDescent(features, values)

test = np.array([[row[x] for x in range(F)] for row in test])
mTest = np.append(test, np.ones((T, 1)), axis=1)
for v in mTest.dot(weights):
    print("{0:.2f}".format(float(v)))

#Using sklearn
# Create linear regression object
regr = linear_model.LinearRegression()
# Train the model using the training sets
regr.fit(features, values)
for v in test.dot(regr.coef_):
    print("{0:.2f}".format(float(v)))


import numpy as np
import matplotlib.pyplot as pl

def compute_mean(x):
    """
    computes and returns the mean vector of the input data
    """
    m = np.mean(x, axis=1)[:, np.newaxis]
    return m

def compute_covariance(X):
    """
    computes and returns the covariance matrix of the input data x
    """
    X = X.copy()
    m = compute_mean(X)
    N = X.shape[1]
    X = X - m
    S = 1.0/(N-1) * np.dot(X, X.T)
    return S

def compute_principal_components(X):
    """
    computes and returns the principal components
    """
    # compute the covariance matrix
    S = compute_covariance(X)
    # compute eigenvalues and eigenvectors
    l, p = np.linalg.eigh(S)
    # sort the eigenvectors in descending order
    li = np.argsort(l)[::-1]
    L = np.diag(l[li])
    P = p[:,li]
    return P, L

def select_principal_components(P, L, delta):
    """
    selects and returns principal components considering
    desired retained variation
    """
    l = np.diagonal(L)
    d = np.argmin(np.abs(delta - np.cumsum(l) / np.sum(l))) + 1
    Pd = P[:,0:d]
    Ld = L[0:d, 0:d]
    return Pd, Ld, d

def project_onto_eigenvector_space(X, Pd, m):
    """
    projects data matrix onto eigenvector space
    """
    W = np.dot(Pd.T, (X-m))
    return W

def reconstruct_data(Pd, W, m):
    """
    reconstructs data matrix using d-rank approximation
    """
    Xhat = m + np.dot(Pd, W)
    return Xhat

# pl.close('all')
# # generate a synthetic data set1
# x = generate_data()
# # compute the mean vector of the data set
# m = compute_mean(x)
# # compute the covariance matrix of the data set
# S = compute_covariance(x)
# # compute all principal components
# P, L = compute_principal_components(x)
# # select principal components according to desired variation
# delta = 1.0
# Pd, Ld, d = select_principal_components(P, L, delta)
# # plot data and principal components
# plot_principal_components(x, Pd, Ld, '../../slides/docs/principal_components4.png')


#pythran export arc_distance(float [], float[], float[], float[])
#runas import numpy as np; arc_distance(np.array([12.4,0.5,-5.6,12.34,9.21]),np.array([-5.6,3.4,2.3,-23.31,12.6]),np.array([3.45,1.5,55.4,567.0,43.2]),np.array([56.1,3.4,1.34,-56.9,-3.4]))
#bench import numpy.random; N=5000000; a, b, c, d = numpy.random.rand(N), numpy.random.rand(N), numpy.random.rand(N), numpy.random.rand(N); arc_distance(a, b, c, d)

import numpy as np
def arc_distance(theta_1, phi_1,
                 theta_2, phi_2):
    """
    Calculates the pairwise arc distance between all points in vector a and b.
    """
    temp = np.sin((theta_2-theta_1)/2)**2+np.cos(theta_1)*np.cos(theta_2)*np.sin((phi_2-phi_1)/2)**2
    distance_matrix = 2 * (np.arctan2(np.sqrt(temp),np.sqrt(1-temp)))
    return distance_matrix


# Plots various neural net activation functions.

import matplotlib.pyplot as plt
import numpy as np
from pyprobml_utils import save_fig

e = np.exp(1)
x = np.linspace(-4, 4, 1000)
y = e**x / (e**x + 1)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('sigmoid function')
save_fig('sigmoidPlot.pdf')
plt.show()

y = np.tanh(x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('tanh function')
save_fig('tanhPlot.pdf')
plt.show()

y = np.log(1+np.exp(x))
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('softplus function')
save_fig('softplusPlot.pdf')
plt.show()

y = 1.0*(x>0)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('Heaviside function')
save_fig('heavisidePlot.pdf')
plt.show()

y = np.maximum(0, x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('ReLU function')
save_fig('reluPlot.pdf')
plt.show()

lam = 0.5
y = np.maximum(0, x) + lam*np.minimum(0, x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('Leaky ReLU function')
save_fig('leakyReluPlot.pdf')
plt.show()

lam = 0.5
y = np.maximum(0, x) + np.minimum(0, lam*(e**x - 1))
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('ELU function')
save_fig('eluPlot.pdf')
plt.show()


import math
from scipy.stats import norm
from dateutil.parser import *
from datetime import *

def BlackSholes(currentPrice, strikePrice, volatility, rate, expiration = '12/31/2020'):
    if type(expiration) is str:
        expiration = parse(expiration)
    term = (expiration.date() - date.today()).days
    return _BlackSholes(currentPrice,strikePrice,volatility,rate, term, termUnits='days')

def _BlackSholes(currentPrice, strikePrice, volatility, rate, term, termUnits='days'):
    # this is the log of the current price / strike price term
    logStockStrike = math.log(currentPrice/strikePrice)
    # represents the sigma-squared divided by 2 term
    sigmaDiv2 = (volatility**2)/2
    # Convert days to part of years
    T = term/365 if termUnits == 'days' else term
    ert = math.exp(-rate*T)

    d1 = (logStockStrike + (rate+sigmaDiv2)*T) / (volatility*math.sqrt(T))
    d2 = d1 - volatility*math.sqrt(T)
    Nd1 = norm.cdf(d1)
    Nd2 = norm.cdf(d2)
    callPrice = currentPrice*Nd1 - strikePrice*ert*Nd2

    Nnegd1 = norm.cdf(-d1)
    Nnegd2 = norm.cdf(-d2)
    putPrice = strikePrice*ert*Nnegd2 - currentPrice*Nnegd1

    return (callPrice, putPrice)

if __name__ == "__main__":
    import numpy as np
    import pandas as pd

    df = pd.DataFrame({'vol':np.arange(15,25,0.1)})
    df['call'] = df['vol'].apply(lambda x: BlackSholes(166.36, 175, x, 0.017,'12/31/2020')[0])
    df


from sympy import symbols, hessian, Matrix
from sympy.functions import exp

def gradient(f, v): return Matrix([f]).jacobian(v)

x, b1, b2, b3, b4, b5, b6, b7, b8 = symbols('x,b1,b2,b3,b4,b5,b6,b7,b8')

print('NIST Low Difficulty')
print('\nChwirut')
print('y = exp(-b1*x)/(b2+b3*x)  +  e')

r = exp(-b1*x)/(b2+b3*x)
v = [b1, b2, b3]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))

print('\nChwirut2')
print('y = exp(-b1*x)/(b2+b3*x)  +  e')

r = exp(-b1*x)/(b2+b3*x)
v = [b1, b2, b3]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))

print('\nDanWood')
print('y  = b1*x**b2  +  e')

r = b1*x**b2
v = [b1, b2]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))

print('\nGauss1')
print('y = b1*exp( -b2*x ) + b3*exp( -(x-b4)**2 / b5**2) '
      '+ b6*exp( -(x-b7)**2 / b8**2 ) + e')

r = b1*exp(-b2*x) + b3*exp(-(x-b4)**2 / b5**2) + b6*exp(-(x-b7)**2 / b8**2)
v = [b1, b2, b3, b4, b5, b6, b7, b8]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))

print('\nGauss2')
print('y = b1*exp( -b2*x ) + b3*exp( -(x-b4)**2 / b5**2) '
      '+ b6*exp( -(x-b7)**2 / b8**2 ) + e')

r = b1*exp(-b2*x) + b3*exp(-(x-b4)**2 / b5**2) + b6*exp(-(x-b7)**2 / b8**2)
v = [b1, b2, b3, b4, b5, b6, b7, b8]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))

print('\nLanczos3')
print('y = b1*exp(-b2*x) + b3*exp(-b4*x) + b5*exp(-b6*x)  +  e')

r = b1*exp(-b2*x) + b3*exp(-b4*x) + b5*exp(-b6*x)
v = [b1, b2, b3, b4, b5, b6]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))

print('\nMisra1a')
print('y = b1*(1-exp(-b2*x))  +  e')

r = b1*(1-exp(-b2*x))
v = [b1, b2]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))

print('\nMisra1b')
print('y = b1 * (1-(1+b2*x/2)**(-2))  +  e')

r = b1 * (1-(1+b2*x/2)**(-2))
v = [b1, b2]

print('Jacobian:')
print(gradient(r, v))

print('Hessian:')
print(hessian(r, v))


## Translation of mlb's barras_linear.m to Python
## cacs@thorus-scisoft.com.br

import numpy as np

## Physical data
Lb=1.0        # bar length
E  = 210e9       # Young modulus
A  = 1e-4        # cross sectional area
EA = E*A
p0 = 100         # distributed load
Pc = 200         # concentrated load at x = L, b.c. at x = L

## FEM setup: all element with the same length
Nel   = 4        # number of elements
Nvert = Nel+1    # number of nodes (1-D, linear interpolation)
he    = Lb/Nel   # element lenght
Ndofs = Nel+1    # number of d.o.f. (1 d.o.f. per node)

# Allocation of the stifness matrix
Kg = np.zeros((Ndofs,Ndofs))

# Allocation of the load vector
Fg = np.zeros(Ndofs)

# Allocation of the solution vector
ug = np.zeros(Ndofs)

# Allocation of incidence matrix
Incid = np.zeros((Nel,2), dtype=np.int)

# Auxiliary matrix for accessing submatrix (more comments below)
TIncid = np.zeros((2,1), dtype=np.int)

# Nodes coordinates
X = np.linspace(0, Lb, Nvert)

# Bar element stiffness matrix
Ke = (EA/he)*np.array([[1,-1],[-1,1]])

# Bar element load vector due to the distributed load p0
Fe = (p0*he/2)*np.array([1, 1])

# Assembly of global matrix and global load vector without b.c.'s
for e in range(0,Nel):
	Incid[e,:]=np.array([e,e+1])
	Fg[Incid[e,:]]=Fg[Incid[e,:]]+Fe
	# numpy requires a column matrix to specify submatrix columns
	TIncid[:,0]=Incid[e,:]
	# The general mechanism to access submatrices in numpy returns the
    # transpost of the submatrix. Despite all matrices being symmetric
	# I am transposing Ke to be coherent.
	Kg[Incid[e,:], TIncid] = Kg[Incid[e,:], TIncid] + np.transpose(Ke);

# Application of force b.c. at x = L
Fg[-1] = Fg[-1] + Pc

# Application of b.c. disp. condition at x = 0 (elimination of the first line
# and first column of the global system) and linear system solution
ug[1:] = np.linalg.solve( Kg[1:,1:],Fg[1:] )


import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA

def generate_cluster(n_dim, n_objects, intermix_factor, d1=-1, d2=1):
    a1 = 0.025
    a2 = 0.05

    mean = intermix_factor * (d1 + (d2 - d1) * np.random.rand(n_dim))
    #     print(mean)
    cov_diag = ((d2 - d1) * (a1 + (a2 - a1) * np.random.rand(n_dim))) ** 2
    #     print(cov_diag)
    cov = np.diag(cov_diag)
    sample = np.random.multivariate_normal(mean, cov, n_objects)

    return sample

def plot_principal_components(data, return_pca=False, plt_args={}):
    pca = PCA(n_components=2)
    data_2dim = pca.fit_transform(data)
    plt.scatter(*data_2dim.T, **plt_args)
    if return_pca:
        return pca

def get_principal_components(data, return_pca=False):
    pca = PCA(n_components=2)
    data_2dim = pca.fit_transform(data)
    if return_pca:
        return data_2dim, pca
    else:
        return data_2dim

def generate_noise(sample, percent):
    n_objects, n_dim = sample.shape
    n_noise_objects = int(n_objects * percent)
    max_ = np.max(sample, axis=0)
    min_ = np.min(sample, axis=0)
    noise = np.random.rand(n_noise_objects, n_dim) * (max_ - min_) + min_

    return noise

def generate_cluster_sizes(n_objects, n_clusters, cluster_min_size):
    n_free_objects = n_objects - n_clusters * cluster_min_size

    r1 = np.concatenate((np.zeros(1), np.random.rand(n_clusters - 1)))
    r2 = np.diff(np.sort(r1))

    n_extra_obj_in_cluster = np.round(r2 * n_free_objects)
    n_extra_obj_in_cluster = np.concatenate((np.ones(1) * (n_free_objects - n_extra_obj_in_cluster.sum()),
                                             n_extra_obj_in_cluster))

    n_obj_in_cluster = (n_extra_obj_in_cluster + cluster_min_size).astype(int)

    return n_obj_in_cluster

def generate_dataset(n_objects, n_clusters, n_dim, intermix_factor, cluster_min_size):
    # print('Generating dataset. Obj=%d, Clust=%d, Dim=%d, Intermix=%.3f, Clust_min_size=%d' % (n_objects, n_clusters,
    # n_dim, intermix_factor, cluster_min_size))

    clusters_size = generate_cluster_sizes(n_objects, n_clusters, cluster_min_size)
    # print('Cluster sizes = %s' % clusters_size)

    data = []
    labels = []

    for n, cl_size in enumerate(clusters_size):
        data.append(generate_cluster(n_dim, cl_size, intermix_factor))
        labels.append(np.full(cl_size, n))

    data = np.concatenate(tuple(data))
    labels = np.concatenate(tuple(labels))

    return data, labels


import json
import os.path
from typing import List

import math
import sympy

from utils import get_root_dir

# Reading the primes file is slower than calculating the first primes. Where this cutoff point exactly lies depends
# on many factors, among which how big the primes file is. With a file with 1,000,000 primes, this cutoff point
# lies somewhere between 1,000 and 10,000.
CUTOFF_TO_LOOKUP = 1000

PRIMES_FILE = os.path.join(get_root_dir(), 'json', 'primes.json')

def is_prime(n: int) -> bool:
    """
    Return wether the input argument is a prime number
    """

    return sympy.isprime(n)

def get_primes(n: int) -> List[int]:
    """
    Return a list of the first n primes

    The results are cached on file, so the primes are only calculated once.
    """

    if n > CUTOFF_TO_LOOKUP and os.path.exists(PRIMES_FILE):
        with open(PRIMES_FILE, 'r') as f:
            known_primes = json.loads(f.read())
        if n <= len(known_primes):
            return known_primes[:n]
    else:
        known_primes = [1, 2, 3]  # The first primes are known

    num = max(known_primes)
    while len(known_primes) < n:
        # Check if num is divisible by any prime before it
        num_is_prime = True
        for p in known_primes[1:]:  # Skip the first prime, which is 1
            # If there is no remainder dividing the number then the number is not a prime
            if num % p == 0:
                # Break to stop testing more numbers, we know it's not a prime
                num_is_prime = False
                break

        # If it is a prime, then add it to the dictionary of known primes.
        if num_is_prime:
            known_primes.append(num)

        # Don't check even numbers
        num += 2

    if n > CUTOFF_TO_LOOKUP:
        with open(PRIMES_FILE, 'w') as f:
            f.write(json.dumps(known_primes))
    return known_primes

def nth_prime(n: int) -> int:
    """
    Return the n'th prime number

    The results are cached on file, so the primes are only calculated once.
    """

    return get_primes(n)[n - 1]

def prime_factorize(n: int) -> List[int]:
    """
    Return the list of divisors for the given integer

    >>> prime_factorize(15485857 * 15485863)
    [15485857, 15485863]
    """

    result = []
    for prime, multiplicity in sympy.factorint(n).items():
        result += [prime] * multiplicity
    return result

if __name__ == '__main__':
    print(nth_prime(10000))
    print(prime_factorize(15485857 * 15485863))


from __future__ import annotations

import math

import numpy
import numpy as np
from numpy.linalg import inv, det, slogdet

class UnivariateGaussian:
    """
    Class for univariate Gaussian Distribution Estimator
    """

    def __init__(self, biased_var: bool = False) -> UnivariateGaussian:
        """
        Estimator for univariate Gaussian mean and variance parameters

        Parameters
        ----------
        biased_var : bool, default=False
            Should fitted estimator of variance be a biased or unbiased estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `UnivariateGaussian.fit` function.

        mu_: float
            Estimated expectation initialized as None. To be set in `UnivariateGaussian.fit`
            function.

        var_: float
            Estimated variance initialized as None. To be set in `UnivariateGaussian.fit`
            function.
        """
        self.biased_ = biased_var
        self.fitted_, self.mu_, self.var_ = False, None, None

    def fit(self, X: np.ndarray) -> UnivariateGaussian:
        """
        Estimate Gaussian expectation and variance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Training data

        Returns
        -------
        self : returns an instance of self.

        Notes
        -----
        Sets `self.mu_`, `self.var_` attributes according to calculated estimation (where
        estimator is either biased or unbiased). Then sets `self.fitted_` attribute to `True`
        """
        # raise NotImplementedError()

        self.mu_ = np.mean(X)
        if self.biased_:
            self.var_ = np.var(X) * (len(X) - 1) / len(X)
        else:
            self.var_ = np.var(X)

        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray) -> np.ndarray:
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, var_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")

        # raise NotImplementedError()

        pdfs = np.zeros(len(X))
        for index, x in enumerate(X):
            prefix = 1 / (2 * math.pi * self.var_) ** 0.5
            exponent = math.exp(-(((x - self.mu_) ** 2) / (2 * self.var_)))

            pdfs[index] = prefix * exponent

        return pdfs

    @staticmethod
    def log_likelihood(mu: float, sigma: float, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : float
            Expectation of Gaussian
        sigma : float
            Variance of Gaussian
        X : ndarray of shape (n_samples, )
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated
        """
        # raise NotImplementedError()

        log_pdfs = np.zeros(len(X))
        for index, x in enumerate(X):
            prefix = 1 / (2 * math.pi * sigma) ** 0.5
            exponent = math.exp(-(((x - mu) ** 2) / (2 * sigma)))

            log_pdfs[index] = np.log(prefix * exponent)

        return log_pdfs.sum()

class MultivariateGaussian:
    """
    Class for multivariate Gaussian Distribution Estimator
    """

    def __init__(self):
        """
        Initialize an instance of multivariate Gaussian estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `MultivariateGaussian.fit` function.

        mu_: ndarray of shape (n_features,)
            Estimated expectation initialized as None. To be set in `MultivariateGaussian.fit`
            function.

        cov_: ndarray of shape (n_features, n_features)
            Estimated covariance initialized as None. To be set in `MultivariateGaussian.fit`
            function.
        """
        self.mu_, self.cov_ = None, None
        self.fitted_ = False

    def fit(self, X: np.ndarray) -> MultivariateGaussian:
        """
        Estimate Gaussian expectation and covariance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Training data

        Returns
        -------
        self : returns an instance of self

        Notes
        -----
        Sets `self.mu_`, `self.cov_` attributes according to calculated estimation.
        Then sets `self.fitted_` attribute to `True`
        """
        # raise NotImplementedError()
        self.mu_ = np.mean(X, axis=0)
        self.cov_ = np.cov(np.transpose(X))

        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray):
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, cov_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")
        # raise NotImplementedError()

        pdfs = np.zeros(X.shape)
        for index, x in enumerate(X):
            two_pi_powered = (2 * math.pi) ** len(self.mu_)
            prefix = 1 / (two_pi_powered * np.linalg.det(self.cov_)) ** 0.5
            exponent = np.exp(-0.5 * ((np.transpose(x - self.mu_)).dot(np.linalg.inv(self.cov_)).dot(x - self.mu_)))

            pdfs[index] = prefix * exponent

        return pdfs

    # @staticmethod
    # def calc_pdf(mu: np.ndarray, cov: np.ndarray, X: np.ndarray, func):
    #     func_pdfs = np.zeros(X.shape)
    #     for index, x in enumerate(X):
    #         two_pi_powered = (2 * math.pi) ** len(mu)
    #         prefix = 1 / (two_pi_powered * np.linalg.det(cov)) ** 0.5
    #         exponent = np.exp(-0.5 * ((np.transpose(x - mu)).dot(np.linalg.inv(cov)).dot(x - mu)))
    #
    #         func_pdfs[index] = func(prefix * exponent)
    #
    #     return func_pdfs

    @staticmethod
    def log_likelihood(mu: np.ndarray, cov: np.ndarray, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : ndarray of shape (n_features,)
            Expectation of Gaussian
        cov : ndarray of shape (n_features, n_features)
            covariance matrix of Gaussian
        X : ndarray of shape (n_samples, n_features)
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated over all input data and under given parameters of Gaussian
        """
        # raise NotImplementedError()
        inv_cov = np.linalg.inv(cov)
        det_cov = np.linalg.det(cov)

        k = np.shape(X)[1]
        num_samples = np.shape(X)[0]

        log_likelihood_sum = 0
        for index, x in enumerate(X):
            log_likelihood_sum += (x - mu) @ inv_cov @ (x - mu).T

        return -0.5 * (num_samples * np.log(det_cov) + log_likelihood_sum + k * num_samples * np.log(2 * np.pi))
        # return IMLearn.learners.MultivariateGaussian.calc_pdf(mu, cov, X, lambda q: np.log(q)).sum()


"""
Module containing functions that replicate some useful matlab routines

Created on 8 March 2019
@authors:
    * Francesco Della Santa (FDS), Politecnico di Torino, ITALY

Updates:
dd Mon YYYY:
    * ...

"""

import numpy as np
import scipy as sp  # Still not used

def cart2pol(x, y):
    """
    Transform Cartesian to polar coordinates (look at car2pol help on matlab)
    :param x:
    :param y:
    :return theta: radians of the angle between [x,y] and x axis (anti-clockwise with x=east, y=north)
    :return rho: radius, norm of [x,y]
    """
    theta = np.arctan2(y, x)
    rho = np.hypot(x, y)
    return theta, rho

def pol2cart(theta, rho):
    """
    Transform polar to Cartesian coordinates (look at the pol2cart help on matlab)
    :param theta: radians of the angle between [x,y] and x axis (anti-clockwise with x=east, y=north)
    :param rho: radius, norm of [x,y]
    :return: x, y
    """
    x = rho * np.cos(theta)
    y = rho * np.sin(theta)
    return x, y

def cart2sph(x, y, z):
    """
    Transform Cartesian to spherical coordinates (look at the cart2sph help on matlab)
    :param x:
    :param y:
    :param z:
    :return az: radians of the angle between projection on (x,y) plane of [x,y,z] and x axis (anti-clockwise with
    respect to z)
    :return el: radians of the angle between [x,y,z] and (x,y) plane (el >/=/< 0 if z >/=/< 0). then -pi/2 <= el <= pi/2
    :return r: radius, norm of [x,y,z]
    """
    hxy = np.hypot(x, y)
    r = np.hypot(hxy, z)
    el = np.arctan2(z, hxy)
    az = np.arctan2(y, x)
    return az, el, r

def sph2cart(az, el, r):
    """
    Transform spherical to Cartesian coordinates (look at the sph2cart help on matlab)
    :param az: radians of the angle between projection on (x,y) plane of [x,y,z] and x axis (anti-clockwise with
    respect to z)
    :param el: radians of the angle between [x,y,z] and (x,y) plane (el >/=/< 0 if z >/=/< 0). then -pi/2 <= el <= pi/2
    :param r: radius, norm of [x,y,z]
    :return: x, y, z
    """
    rcos_theta = r * np.cos(el)
    x = rcos_theta * np.cos(az)
    y = rcos_theta * np.sin(az)
    z = r * np.sin(el)
    return x, y, z

def rotx(ang):
    """
    Create a rotation matrix (3 x 3 np.array) in the 3D space with respect to x axis
    :param ang: radians. If ang <(>) 0, rotation is (anti-)clockwise with respect to x.
    :return: Rx: np.array of shape (3, 3) representing the rotation matrix
    """
    Rx = np.array(
        [
            [1, 0, 0],
            [0, np.cos(ang), -np.sin(ang)],
            [0, np.sin(ang), np.cos(ang)]
        ]
    )

    return Rx

def roty(ang):
    """
    Create a rotation matrix (3 x 3 np.array) in the 3D space with respect to y axis
    :param ang: radians. If ang <(>) 0, rotation is (anti-)clockwise with respect to y.
    :return: Rx: np.array of shape (3, 3) representing the rotation matrix
    """
    Ry = np.array(
        [
            [np.cos(ang), 0, np.sin(ang)],
            [0, 1, 0],
            [-np.sin(ang), 0, np.cos(ang)]
        ]
    )

    return Ry

def rotz(ang):
    """
    Create a rotation matrix (3 x 3 np.array) in the 3D space with respect to z axis
    :param ang: radians. If ang <(>) 0, rotation is (anti-)clockwise with respect to z.
    :return: Rz: np.array of shape (3, 3) representing the rotation matrix
    """
    Rz = np.array(
        [
            [np.cos(ang), -np.sin(ang), 0],
            [np.sin(ang), np.cos(ang), 0],
            [0, 0, 1]
        ]
    )

    return Rz


import numpy as np

def sin_normal_noise(N_samples, N_true=100, std=0.3):
    """
    Returns synthetic data of a sinusoidal signal with added
    normal noise.
    Args:
        N_samples: int, number of points
        N_true: int, number of points from signal without noise
        std: float, standard deviation of the added normal noise
    Returns:
        x_true: numpy array of size (N, 1) containing x data of signal
            without noise.
        t_true: numpy array of size (N, 1) containing target data of signal
            without noise.
        x: numpy array of size (N, 1) containing x data of signal
        t: numpy array of size (N, 1) containing target data of signal
    """
    # True function
    x_true = np.linspace(0, 1, N_true)[:, np.newaxis]
    t_true = np.sin(2*np.pi*x_true)

    # Training data
    x = np.linspace(0, 1, N_samples)[:, np.newaxis]
    t = np.sin(2*np.pi*x) + np.random.normal(0, std, N_samples)[:, np.newaxis]

    return x_true, t_true, x, t

def gaussian_mixture(N_samples):
    """Sample data from a Gaussian mixture with two components of mean -2 and 2
    and unit variance.
    Args:
        N_samples: int, the number of samples
    Returns:
        X: array, shape (N_samples,), the sampled data
    """
    X = np.empty(N_samples)
    # Choose from components with equal probability
    idx = np.random.choice([True, False], N_samples)
    n_1 = np.sum(idx)
    n_2 = N_samples - n_1
    # Fill array with samples from both components
    X[idx] = np.random.normal(-2, size=n_1)
    X[np.logical_not(idx)] = np.random.normal(2, size=n_2)
    return X


import numpy as np

'''
    From the name you can guess the calculation of this distance.
    Imagine that you are driving from an intersection to another intersection in Manhattan.
    Is the driving distance a straight line between two points? Obviously not,
    unless you can cross the building. The actual driving distance is this "Manhattan distance" (L1 norm).
    This is also the source of the Manhattan distance name, which is also known as the City Block distance
'''

objA = [22, 1, 42, 10]

objB = [20, 0, 36, 8]

npA = np.array(objA)

npB = np.array(objB)

manhattan = np.sum(np.abs(npA - npB))

# manhattan = np.linalg.norm(npA - npB, ord=1)

print(manhattan)


import numpy as np
class Solution:
    def numWays(self, steps: int, arrLen: int) -> int:
        arrLen = min(arrLen, steps + 1)
        dp = np.zeros((steps, arrLen),int)#dp[i][j] how many ways ith step reaches j
        dp[0][0], dp[0][1] = 1, 1
        for i in range(1, steps):
            for j in range(arrLen):
                if j-1 >= 0 and j+1 < arrLen:
                    dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + dp[i - 1][j] + dp[i - 1][j + 1]) % (10**9 + 7)
                elif j-1 >= 0:
                    dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + dp[i - 1][j]) % (10**9 + 7)
                elif j+1 < arrLen:
                    dp[i][j] = (dp[i][j] + dp[i - 1][j] + dp[i - 1][j + 1]) % (10**9 + 7)
                else:
                    dp[i][j] = dp[i - 1][j] % (10**9 + 7)
        return dp[steps-1][0]

    ans = 0
    def numWays2(self, steps: int, arrLen: int) -> int:
        #remain0 left -1 right 1
        self.ans = 0
        self.helper(steps, arrLen,0,0)
        return self.ans

    def helper(self, steps: int, arrLen: int, currSum: int, currStep: int):
        if currStep < steps:
            for i in [-1, 0, 1]:
                if currSum+i < arrLen and currSum+i >= 0:
                    self.helper(steps, arrLen, currSum+i, currStep+1)
        elif currSum == 0:
            self.ans += 1

solution = Solution()
res = solution.numWays(430, 148488)
print(res)

#res = solution.numWays2(27,7)
#print(res)


# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.13.6
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# %% [markdown]
# ---
# #  [Pyomo](http://www.pyomo.org/)
#
# ### A Python-based, open-source optimization modeling language
#
# ---
#
# + [Pyomo documentation](https://pyomo.readthedocs.io/en/stable/)
# + Pyomo is a modeling language, not a solver. It interfaces to solvers such as [glpk](https://www.gnu.org/software/glpk/)
#
# ## Standard import

# %%
import pyomo.environ as pyo
import pandas as pd
import numpy as np

# %% [markdown]
# # [The Diet Problem](https://notebook.community/Pyomo/PyomoGallery/diet/DietProblem)
#
# The goal of the Diet Problem is to select foods that satisfy daily nutritional requirements at minimum cost.
#
#
#
# ### Sets
#
# | Symbol | Definition |
# | :----- | ---------------: |
# |  $F$   | set of foods     |
# |  $N$   | set of nutrients |
#
# ### Parameters
#
# | Symbol | Definition                                              | Size               |
# | :----- | :------------------------------------------------------ | :---------------- |
# | $$c_i$$  | cost per serving of food $i$ | $$\forall i \in F$$ |
# | $$a_{ij}$$   |  amount of nutrient $j$ in food $i$ | $$\forall i \in F, \forall j \in N$$ |
# | $$Nmin_j$$  | minimum level of nutrient $j$  |  $$\forall j \in N$$   |
# | $$Nmax_j$$  | maximum level of nutrient $j$  | $$\forall j \in N$$ |
# | $$V_i$$  | the volume per serving of food $i$  | $$\forall i \in F$$   |
# | $$Vmax$$  |  maximum volume of food consumed |  |
#
#
# ### Variables
#
# | Symbol | Definition |
# | :----- | ---------------: |
# |$$x_i$$ | number of servings of food $i$ to consume |
#
# ### Objective
#
# + Minimize the total cost of the food
#  $$\min \sum_{i \in F} c_i x_i$$
#
# ### Constraints
#
# + Limit nutrient consumption for each nutrient $j \in N$.
#  $$Nmin_j \leq \sum_{i \in F} a_{ij} x_i \leq Nmax_j\hspace{3em}\forall j \in N$$
#
# + Limit the volume of food consumed
#  $$\sum_{i \in F} V_i x_i \leq Vmax$$
#
# + Consumption lower bound
#  $$x_i \geq 0\hspace{3em}\forall i \in F$$

# %% [markdown]
# ## Pyomo Modeling Components
#
# + Sets
# + Parameters
# + Variables
# + Objectives
# + Constraints
# + Expressions
# + Suffixes
#
#
#

# %% [markdown]
# ### 0. Load the data

# %%
Vmax = 75.0

# %%
Bounds = pd.DataFrame([['Cal',2000, np.inf],
        ['Carbo'   ,  350  ,  375],
        ['Protein' ,   55 , np.inf],
        ['VitA'    ,  100 , np.inf],
        ['VitC'    ,  100 , np.inf],
        ['Calc'    ,  100  , np.inf],
        ['Iron'    ,  100 , np.inf]],
       columns=['nutrient','min','max'] )
Bounds = Bounds.set_index('nutrient')
Bounds

# %%
Foods = pd.DataFrame([[  "Cheeseburger",1.84 ,  4.0,510,     34,     28 ,    15 ,     6 ,   30 ,   20  ],
        [  "Ham Sandwich", 2.19 ,  7.5 ,370,     35,     24 ,    15 ,    10 ,   20 ,   20 ],
        [  "Hamburger",1.84 ,  3.5 ,500,     42,     25 ,     6 ,     2 ,   25 ,   20 ],
        [  "Fish Sandwich" , 1.44 ,  5.0 ,370,     38,     14 ,     2 ,     0 ,   15 ,   10 ],
        [  "Chicken Sandwich", 2.29,7.3,400,     42,     31 ,     8 ,    15 ,   15 ,    8],
        [  "Fries"  , .77 ,  2.6 ,220,     26,      3 ,     0 ,    15 ,    0 ,    2 ],
        [  "Sausage Biscuit", 1.29 ,  4.1 ,345,     27,     15 ,     4 ,     0 ,   20 ,   15 ],
        [  "Lowfat Milk" , .60,8.0 ,110,     12,      9 ,    10 ,     4 ,   30 ,    0],
        [  "Orange Juice", .72 , 12.0 ,80 ,    20 ,     1  ,    2  ,  120  ,   2  ,   2 ]],
                 columns = ['food','c','V','Cal' , 'Carbo','Protein' ,  'VitA'  , 'VitC' , 'Calc' , 'Iron'])

Foods.set_index('food' ,inplace=True)
Foods

# %% [markdown]
# ### 1. Create a model object.

# %%
model = pyo.ConcreteModel()

# %% [markdown]
# ### 2. Define sets

# %%
# foods set
model.F = pyo.Set(initialize = Foods.index)

# nutrients set
model.N = pyo.Set(initialize = Bounds.index)

# %% [markdown]
# ### 3. Define parameters

# %%
model.c       = pyo.Param(model.F,initialize=Foods['c'].to_dict())
model.V       = pyo.Param(model.F,initialize=Foods['V'].to_dict())
model.Cal     = pyo.Param(model.F,initialize=Foods['Cal'].to_dict())
model.Carbo   = pyo.Param(model.F,initialize=Foods['Carbo'].to_dict())
model.Protein = pyo.Param(model.F,initialize=Foods['Protein'].to_dict())
model.VitA    = pyo.Param(model.F,initialize=Foods['VitA'].to_dict())
model.VitC    = pyo.Param(model.F,initialize=Foods['VitC'].to_dict())
model.Calc    = pyo.Param(model.F,initialize=Foods['Calc'].to_dict())
model.Iron    = pyo.Param(model.F,initialize=Foods['Iron'].to_dict())

# %% [markdown]
# ### 4. Define the decision variables

# %%
model.x = pyo.Var(model.F, within=pyo.NonNegativeIntegers)

# %% [markdown]
# ### 5. Define the objective function

# %%
obj = sum(model.c[food]*model.x[food] for food in model.F)
model.cost = pyo.Objective(rule=obj, sense = pyo.minimize)

# %% [markdown]
# ### 6. Define the constraints

# %% [markdown]
# #### Limit nutrient consumption for each nutrient

# %%
def nutrient_rule(model, nutrient):
    value = sum(Foods.loc[food,nutrient]*model.x[food] for food in model.F)
    return pyo.inequality(Bounds.loc[nutrient,'min'], value, Bounds.loc[nutrient,'max'])

model.bounds = pyo.Constraint(model.N, rule=nutrient_rule)

# %% [markdown]
# #### sdasdfasdf

# %%
expr = sum(Foods.loc[food,'V']*model.x[food] for food in model.F) <= Vmax
model.volume = pyo.Constraint(rule=expr)

# %% [markdown]
# ## 7. Request the dual, ie Lagrange multiplliers (optional)

# %%
model.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT)

# %% [markdown]
# ## Check the model

# %%
model.pprint()

# %% [markdown]
# ## Solve
#
# Cnnect to a solver with `SolverFactory`. Then run the solver with the `solve` method. This returns an object with information about the solution process. The optimal values of the decision variables are written to the `model` object.

# %%
solver = pyo.SolverFactory('ipopt')  # can also use `glpk`

results = solver.solve(model)

print("The solver returned a status of: " + str(results.solver.status))

# %% [markdown]
# Use the `pyo.value` method to extract the results from the model object.

# %% [markdown]
# ### Extract solution values

# %% [markdown]
# #### Optimal cost

# %%
pyo.value(model.cost)

# %% [markdown]
# #### Print optimal decision variables

# %%
{food:pyo.value(model.x[food]) for food in model.F}

# %% [markdown]
# #### Lagrange multipliers

# %%
for c in model.component_objects(pyo.Constraint):
    print ("\n   Constraint",c)
    for index in c:
        value = round(model.dual[c[index]],5)
        print ("\t{:.5s}\t{:.5f}".format(index if index!=None else "None",value))


import sympy as sym
import numpy as np

# The code in vib_undamped_verify_mms.py is here generalized
# to treat the model m*u'' + f(u') + c*u = F(t), where the
# damping term f(u') = 0, b*u' or b*V*abs(V).

def ode_source_term(u, damping):
    """Return the terms in the ODE that the source term
    must balance, here m*u'' + f(u') + c*u.
    u is a symbolic Python function of t."""
    if damping == 'zero':
        return m*sym.diff(u(t), t, t) + c*u(t)
    elif damping == 'linear':
        return m*sym.diff(u(t), t, t) + \
               b*sym.diff(u(t), t) + c*u(t)
    else:  # damping is nonlinear
        return m*sym.diff(u(t), t, t) + \
               b*sym.diff(u(t), t)*abs(sym.diff(u(t), t)) + c*u(t)

def residual_discrete_eq(u, damping):
    """Return the residual of the discrete eq. with u inserted."""
    if damping == 'zero':
        R = m*DtDt(u, dt) + c*u(t) - F
    elif damping == 'linear':
        R = m*DtDt(u, dt) + b*D2t(u, dt) + c*u(t) - F
    else:   # damping is nonlinear
        R = m*DtDt(u, dt) + b*Dt_p_half(u, dt)*\
            abs(Dt_m_half(u, dt)) + c*u(t) - F
    return sym.simplify(R)

def residual_discrete_eq_step1(u, damping):
    """Return the residual of the discrete eq. at the first
    step with u inserted."""
    half = sym.Rational(1,2)
    if damping == 'zero':
        R = u(t+dt) - u(t) - dt*V - \
            half*dt**2*(F.subs(t, 0)/m) + half*dt**2*(c/m)*I
    elif damping == 'linear':
        R = u(t+dt) - (I + dt*V + \
            half*(dt**2/m)*(-b*V - c*I + F.subs(t, 0)))
    else:   # damping is nonlinear
        R = u(t+dt) - (I + dt*V + \
            half*(dt**2/m)*(-b*V*abs(V) - c*I + F.subs(t, 0)))
    R = R.subs(t, 0)  # t=0 in the rhs of the first step eq.
    return sym.simplify(R)

def DtDt(u, dt):
    """Return 2nd-order finite difference for u_tt.
    u is a symbolic Python function of t.
    """
    return (u(t+dt) - 2*u(t) + u(t-dt))/dt**2

def D2t(u, dt):
    """Return 2nd-order finite difference for u_t.
    u is a symbolic Python function of t.
    """
    return (u(t+dt) - u(t-dt))/(2.0*dt)

def Dt_p_half(u, dt):
    """Return 2nd-order finite difference for u_t, sampled at n+1/2,
    i.e, n pluss one half... u is a symbolic Python function of t.
    """
    return (u(t+dt) - u(t))/dt

def Dt_m_half(u, dt):
    """Return 2nd-order finite difference for u_t, sampled at n-1/2,
    i.e, n minus one half.... u is a symbolic Python function of t.
    """
    return (u(t) - u(t-dt))/dt

def main(u, damping):
    """
    Given some chosen solution u (as a function of t, implemented
    as a Python function), use the method of manufactured solutions
    to compute the source term f, and check if u also solves
    the discrete equations.
    """
    print '=== Testing exact solution: %s ===' % u(t)
    print "Initial conditions u(0)=%s, u'(0)=%s:" % \
          (u(t).subs(t, 0), sym.diff(u(t), t).subs(t, 0))

    # Method of manufactured solution requires fitting F
    global F  # source term in the ODE
    F = sym.simplify(ode_source_term(u, damping))

    # Residual in discrete equations (should be 0)
    print 'residual step1:', residual_discrete_eq_step1(u, damping)
    print 'residual:', residual_discrete_eq(u, damping)

def linear(damping):
    def u_e(t):
        """Return chosen linear exact solution."""
        # General linear function u_e = c*t + d
        # Initial conditions u(0)=I, u'(0)=V require c=V, d=I
        return V*t + I

    main(u_e, damping)

def quadratic(damping):
    # Extend with quadratic functions
    q = sym.Symbol('q')  # arbitrary constant in quadratic term

    def u_e(t):
        return q*t**2 + V*t + I

    main(u_e, damping)

def cubic(damping):
    r, q = sym.symbols('r q')

    main(lambda t: r*t**3 + q*t**2 + V*t + I, damping)

def solver(I, V, F, b, c, m, dt, T, damping):
    """
    Solve m*u'' + f(u') + c*u = F for t in (0,T], u(0)=I and u'(0)=V,
    by a central finite difference method with time step dt.
    F(t) is a callable Python function.
    """
    dt = float(dt)
    Nt = int(round(T/dt))
    u = np.zeros(Nt+1)
    t = np.linspace(0, Nt*dt, Nt+1)

    if damping == 'zero':
        u[0] = I
        u[1] = u[0] - 0.5*dt**2*(c/m)*u[0] + \
               0.5*dt**2*F(t[0])/m + dt*V
        for n in range(1, Nt):
            u[n+1] = 2*u[n] - u[n-1] - \
                     dt**2*(c/m)*u[n] + dt**2*F(t[n])/m
    elif damping == 'linear':
        u[0] = I
        u[1] = u[0] + dt*V + \
               0.5*(dt**2/m)*(-b*V - c*u[0] + F(t[0]))
        for n in range(1, Nt):
            u[n+1] = (2*m*u[n] + (b*dt/2.-m)*u[n-1] + \
                     dt**2*(F(t[n])-c*u[n]))/(m+b*dt/2.)
    else:    # damping is quadratic
        u[0] = I
        u[1] = u[0] + dt*V + \
               0.5*(dt**2/m)*(-b*V*abs(V) - c*u[0] + F(t[0]))
        for n in range(1, Nt):
            u[n+1] = 1./(m+b*abs(u[n]-u[n-1])) * \
                     (2*m*u[n] - m*u[n-1] + b*u[n]*\
                     abs(u[n]-u[n-1])+dt**2*(F(t[n])-c*u[n]))
    return u, t

def test_quadratic_exact_solution(damping):
    # Transform global symbolic variables to functions and numbers
    # for numerical computations

    global p, V, I, b, c, m
    p, V, I, b, c, m = 2.3, 0.9, 1.2, 2.1, 1.6, 1.3 # i.e., as numbers
    global F, t
    u_e = lambda t: p*t**2 + V*t + I
    F = ode_source_term(u_e, damping) # fit source term
    F = sym.lambdify(t, F)            # ...numerical Python function

    from math import pi, sqrt
    dt = 2*pi/sqrt(c/m)/10   # 10 steps per period 2*pi/w, w=sqrt(c/m)
    u, t = solver(I=I, V=V, F=F, b=b, c=c, m=m, dt=dt,
                  T=(2*pi/sqrt(c/m))*2, damping=damping)
    u_e = u_e(t)
    error = np.abs(u - u_e).max()
    tol = 1E-12
    assert error < tol
    print 'Error in computing a quadratic solution:', error

if __name__ == '__main__':
    damping = ['zero', 'linear', 'quadratic']
    for e in damping:
        V, t, I, dt, m, b, c = sym.symbols('V t I dt m b c')  # global
        F = None  # global variable for the source term in the ODE
        print '---------------------------------------Damping:', e
        linear(e)  	# linear solution used for MMS
        quadratic(e)   	# quadratic solution for MMS
        cubic(e)       	# ... and cubic
        test_quadratic_exact_solution(e)


from __future__ import division

import numpy as np
from scipy.interpolate import interp1d
from scipy.sparse.linalg import spsolve
from scipy.sparse import spdiags, coo_matrix
from matplotlib import pyplot as plt

def ode_fe(func,c=-1.,d=0.,a=0.,b=1.,alpha=2.,beta=4.,x=np.linspace(0.,1.,5+1)):
	# A Simple Finite Element Scheme to solve BVP's of the form
	# u''(x) + c*u'(x) + d*u(x) = f(x), x \in [a,b]
	# u(a) = alpha
	# u(b) = beta
	# Dirichlet boundary conditions
	#
	# U_0 = alpha, U_1, U_2, ..., U_m, U_{m+1} = beta
	# We use m+1 subintervals, giving m algebraic equations

	N = len(x)
	# Number of finite elements is N-1
	# Number of basis functions is N

	rows, columns, data = np.zeros(3*(N-2)+2), np.zeros(3*(N-2)+2), np.zeros(3*(N-2)+2)
	for j in range(0,N-2):
		rows[3*j:3*(j+1)] = np.array([j+1,j+1,j+1])
		columns[j*3:(j+1)*3] = np.array(range(0,3))+j
	rows[-2:]= np.array([0,N-1])
	columns[-2:]= np.array([0,N-1])

	data[-1], data[-2] = 1,1
	for i in range(1,N-1):
		data[3*(i-1)+2] = 1./(x[i+1]-x[i]) + c*1./2. + d*(x[i+1]-x[i])/6. # i, i+1 location
		data[3*(i-1)+1] = -( 1./(x[i+1]-x[i]) + 1./(x[i]-x[i-1])
												) + c*0. + d*(x[i+1]-x[i-1])/3. # i, i location
		data[3*(i-1)] = 1./(x[i]-x[i-1]) - c*1./2. + d*(x[i]-x[i-1])/6. # i, i-1 location
	A = coo_matrix((data, (rows,columns)), shape=(N,N))

	B = np.zeros(N)
	B[0], B[-1] = alpha, beta
	for j in range(1,N-1):
		B[j]= ( (x[j]-x[j-1])*func((x[j]+x[j-1])/2.)*(1./2.) +
				(x[j+1]-x[j])*func((x[j+1]+x[j])/2.)*(1./2.)   )

	solution = spsolve(A.asformat('csr'),B)
	return x, solution

def Example3(N,epsilon):
	x, y = ode_fe(func=lambda x:-1./epsilon,c=-1./epsilon,x=np.linspace(0,1,N))
	plt.plot(x,y,'-ro')
	plt.axis([0.,1.1,.8,3.2])
	plt.show()
	return x, y

def NonlinearGrid():
	def asol(x,epsilon):
		A = []
		for item in x:
			A.append(alpha+item + (beta-alpha-1.)*(np.exp(item/epsilon) -1.)/(np.exp(1./epsilon) -1.)	)
		return x, np.array(A)

	epsilon = .01
	N=15
	X = np.linspace(0,1,N)
	x1, y1 = ode_fe(func=lambda x:-1./epsilon,c=-1./epsilon,x=X)
	x2, y2 = ode_fe(func=lambda x:-1./epsilon,c=-1./epsilon,x=X**(1./8.))
	alpha, beta = 2.,4.
	# Analytic solution
	Z =asol(np.linspace(0,1,500),epsilon)

	plt.plot(Z[0],Z[1],'-k',label='Solution',linewidth=3)
	plt.plot(x1,y1,'--bo',label='Evenly spaced grid points',linewidth=1.5)
	# plt.plot(Z[0],Z[1],'-k',mfc="None",label='Solution',linewidth=2)
	plt.plot(x2,y2,'--ro',label='Clustered grid points',linewidth=1.5)
	plt.axis([0.,1.1,1.8,4.2])
	plt.legend(loc='best')
	# plt.savefig('FEM_compare_methods.pdf')
	plt.show()
	plt.clf()

	X = np.linspace(0,1,500)
	plt.plot(X,abs(Z[1]-interp1d(x2,y2)(X)),'-r')
	plt.plot( X,abs( Z[1]-interp1d(x1,y1)(X) ), '-b' )

	# print "Max Error = ", np.max(np.abs(Z[1]-interp1d(x2,y2)(X) ))
	plt.show()

def cheb(N):
	def p(j1):
		if (j1==0 or j1 == N): return 2.
		else: return 1.

	x = np.cos(np.pi*np.arange(N+1)/N)
	D = np.zeros((N+1,N+1))
	# j represents column index
	for j in range(0,N+1):
		for i in range(0,j)+range(j+1,N+1):
			D[i,j] = ((-1.)**(i+j))*p(i)/( p(j)*(x[i]- x[j]) )

	# Values on the main diagonal
	for j in xrange(1,N):
		D[j,j] = -x[j]/(2.*(1-x[j]**2.))
	D[0,0] = (1.+2.*N**2.)/6.
	D[N,N] = -(1.+2.*N**2.)/6.
	return D,x

def barycentric_interpolation_Chebychev(u,x,N,xx):
	uu = np.zeros(xx.shape)
	a = np.ones(N+1); a[1::2] = -1
	for x0 in range(len(xx)):
		flag = False
		for item1,item2 in zip(x,u):
			if abs(xx[x0] - item1)<1e-15:
					flag = True
					uu[x0] = item2
		if flag == False:
			b = xx[x0] - x
			b[0] *= 2.
			b[-1] *= 2.
			uu[x0] = np.sum((a*u)/b)/np.sum(a/b)
	return uu

class ODESolverSpectral:
	def __init__(self,N,order,lift,ode,dim=1):
		# N grid points, N-2 interior grid points,
		# and N-1 subintervals, so self.N-1 will index
		# the Nth grid point using Python indexing.
		# The interior grid points will be indexed by
		# 1:self.N-1
		self.N = N
		self.order = order
		self.lift = lift
		# ode must be a function with five arguments,
		# u,x, y= list of differentiation matrices,
		# g = lifting function, and lmbda = parameter.
		self.ode = ode
		self.dim = dim
		self.guess = np.zeros(self.dim*(self.N-2))

	def derivs(self):
		(D,x) = cheb(self.N-1)
		self.cheb_points = x
		self.operators = [[]]
		operator = D
		# print operator[1:self.N-1,1:self.N-1].shape
		# print self.cheb_points[1:self.N-1].shape
		for j in range(1,self.order+1):
			self.operators.append(operator[1:self.N-1,1:self.N-1])
			operator = operator.dot(D)
		# A_, D_, x_ = np.dot(D,D)[1:N-1,1:N-1], D[1:N-1,1:N-1], x[1:N-1]

	def ode_solve(self,output_grid):
		self.ode_func = (lambda u,x_=self.cheb_points[1:self.N-1],
										y=self.operators,G=self.lift: self.ode(u,x_,y,G)
										)
		sol = root(self.ode_func, self.guess )
		# self.sol = np.zeros((self.dim, len(self.cheb_points)))
		self.sol = np.zeros(self.cheb_points.shape)
		self.sol[1:self.N-1] = sol.x
		# print sol.x.shape, self.sol.shape
		# for j in range(self.dim):
		#	print sol.x[0:self.N-2].shape
				# print self.dim, j
				# self.sol[j:1:self.N-1] = sol.x[(j)*(self.N-2):(j+1)*(self.N-2)]
		output_values = barycentric_interpolation_Chebychev(self.sol,
																self.cheb_points,self.N-1,output_grid)
		return output_values

	def new_guess(self,new_guess):
		self.guess = new_guess

# Example3(N=100,epsilon=.02)
# NonlinearGrid()


import numpy as np
import numpy.linalg as ln
from tabulate import tabulate
import matplotlib.pyplot as plt

def makePlot(fun,c,trueData):
    x = np.linspace(-5,5)
    f = fun(x)
    # np.polyval
    # cF = lambda a: np.sum([np.power(a,i) * c[i] for i in range(len(c))])
    cF = np.polyval(np.flip(c),x)
    # print(x)
    plt.title("f_theta VS. interpolating polynomial")
    plt.xlabel("x-axis")
    plt.ylabel("y-axis")
    plt.plot(x,f,'r',label="Original Function")
    plt.plot(x,cF,'b',label="Poly Function using Coeffiecients")
    plt.legend(loc="upper left")
    for i in trueData:
        plt.plot(i[0],i[1], 'bo')
    plt.show()

def main(theta):
    print("Running with theta value of: {}".format(theta))
    #Part 1
    f = lambda x : 1/(1+np.exp(-theta*x))
    tData = np.linspace(-5,5,num=7)
    trueTable = [[t,f(t)] for t in tData]
    header = [["x","f(x)"]]
    print("Part 1: Input and Output of Function")
    print(tabulate(header+ trueTable,headers="firstrow",tablefmt='grid') + "\n")

    #Part 2
    a = [[np.power(trueTable[i][0],t) for t in range(len(trueTable))] for i in range(len(trueTable))]
    b = [[t[1]]for t in trueTable]
    c = ln.solve(a,b)
    print("Part 2: Coeffiecients for Polynomial")
    header = [["x^n","C"]]
    print(tabulate(header + [[i,c[i]]for i in range(len(c))],headers="firstrow",tablefmt='grid') + "\n")
    # makePlot(f,c,trueTable)
    #TODO Make Table

    #Part 3
    nX = np.linspace(-5,5,num=101)
    nY = [[f(t)]for t in nX]
    print("The Mean for Part 3: {}".format(np.average(nY)))
    print("The Standard Deviation for Part 3: {}".format(np.std(nY)))

    #Part 4
    err = 0
    nCF = [np.polyval(np.flip(c),i) for i in nX]
    # cF = lambda a: np.polyval(np.flip(c),a)
    for i in range(len(nX)):
        temp = np.abs(nY[i][0] - nCF[i][0])/np.abs(nY[i][0])
        if temp > err:
            err = temp
    print("Maximum Error: {}".format(err))

main(1)
# print("\n\n\n")
#Part 5
main(10)


# Authored by Johan Blomberg and Gustav Grännsjö, 2020

"""
A collection of functions for generating specific envelope shapes.
"""

import numpy as np
from scipy.stats import norm

def sin2(nr_samples):
    x = np.linspace(0.0, 1.0, nr_samples)
    return np.sin(np.pi * x)**2

def sin_p(p, nr_samples):
    x = np.linspace(0.0, 1.0, nr_samples)
    return np.sin(np.pi * x)**p

def sinc(lim, nr_samples):
    x = np.linspace(-lim, lim, nr_samples)
    return np.sinc(x)

def triangle(nr_samples):
    if nr_samples % 2 == 0:
        t1 = np.linspace(0.0, 1.0, nr_samples // 2)
        t2 = np.linspace(1.0, 0.0, nr_samples // 2)
        return np.concatenate((t1, t2))
    else:
        t1 = np.linspace(0.0, 1.0, nr_samples // 2, endpoint=False)
        t2 = np.flip(t1)
        return np.concatenate((t1, [1], t2))

def cool(nr_samples):
    x = np.linspace(0.0, 1.0, nr_samples)
    s = np.sin(4 * np.pi * x)
    t = triangle(nr_samples)
    return t * s

def gaussian(nr_samples, trunc):
    x = np.linspace(-trunc, trunc, nr_samples)
    y = norm.pdf(x, 0, 1)
    # Normalise
    return y / y.max()


"""
@file:          chap6_supplementary.py
@description:   answers to problems in exercises chapter 6 supplementary exercises
                (Linear Algebra and its applications, David C. Lay 3ed)
@author:        Hiu-kong Dan
@date:          June 10, 2021
"""

import numpy as np
from numpy import linalg

def calculate_relative_change(A, b, db):
    """
    @description:
        calculate Ax = b, and A*dx = db  (delta x, delta b respectively)
    @params:
        A: nxn matrix
        b: right side of Ax = b
        db: change in b
    @return:
        tuple(x, dx)
    """
    x = np.matmul(np.linalg.inv(A), b)
    dx = np.matmul(np.linalg.inv(A), db)
    return (x, dx)

def compare_relative_change(A, b, db):
    """
    @description:
        calculate the relative change in b and relative change in x
        then determine if norm(dx)/norm(x) <= cond(A) * (norm(db)/norm(b))
    @param:
        A: nxn matrix
        b: right side of Ax = b
        db: change in b
    @return:
        none
    """
    x, dx = calculate_relative_change(A, b, db)
    right_side = np.linalg.cond(A) * (np.linalg.norm(db) / np.linalg.norm(b))
    left_side = np.linalg.norm(dx) / np.linalg.norm(x)
    print("left side: ", left_side, "\nright side: ", right_side)
    print("left side <= right side: ", left_side<=right_side)

def problem(title, A, b, db):
    print("problem ", title, ":")
    print("cond: ", np.linalg.cond(A))
    compare_relative_change(A, b, db)
    print("-"*20)

def problem17():
    A = np.array([[4.5,3.1],[1.6,1.1]])
    b = np.array([19.249,6.843])
    db = np.array([.001,-.003])
    problem("17", A, b, db)

def problem18():
    A = np.array([[4.5, 3.1],[1.6,1.1]])
    b = np.array([.500,-1.407])
    db = np.array([.001,-.003])
    problem("18", A, b, db)

def problem19():
    A = np.array([[7.,-6,-4,1],
                  [-5,1,0,-2],
                  [10,11,7,-3],
                  [19,9,7,1]])
    b = np.array([.100,2.888,-1.404,1.462])
    db = 10**-4 * np.array([.49, -1.28, 5.78,8.04])
    problem("19", A, b, db)

def problem20():
    A = np.array([[7.,-6,-4,1],
                  [-5,1,0,-2],
                  [10,11,7,-3],
                  [19,9,7,1]])
    b = np.array([4.230,-11.043,49.991,69.536])
    db = 10**-4 * np.array([.27,7.76,-3.77,3.93])
    problem("20", A, b, db)

if __name__ == "__main__":
    problem17()
    problem18()
    problem19()
    problem20()


import numpy as np

def feature_scaling(X, a=0.0, b=1.0):
    Xmax = np.max(X, axis=0)
    Xmin = np.min(X, axis=0)

    return (b - a) * ((X - Xmin) / (Xmax - Xmin)) - a

def feature_normalization(X):
    mean = np.mean(X, axis=0)
    std = np.std(X, axis=0)

    return (X - mean) / std


"""
Module for calculations with Rb-Sr and Sm-Nd isotopic systems
"""
from numpy import exp
import pandas as pd

def epsNd(Ndrat,Smrat=0,age=0):
    """
    Calculate epsilon Nd using CHUR values from Bouvier et al., 2008

    Parameters:
        Ndrat: Measured 143Nd/144Nd ratio
        Smrat: Measured 147Sm/144Nd ratio
        age: Age of sample in Ma for initial calculation

    Returns:
        eNd: epsilon Nd for present-day 143Nd/144Nd
        Ndi: initial 143Nd/144Nd ratio
        eNdi: epsilon Nd for initial 143Nd/144Nd ratio
    """
    lambdaSm = 6.54e-12 # Sm-147 decay constant
    years = 10**6 # Converter for Ma to years
    time = age*years # Years for initial calc

    Ndi = Ndrat-(Smrat*(exp(lambdaSm*time)-1)) # Calculate initial 143Nd/144Nd

    CHUR143 = 0.512630 #CHUR 143Nd/144Nd from Bouvier et al., 2008
    CHUR147 = 0.1960 # CHUR 147Sm/44Nd from Bouvier et al., 2008
    CHUR143i = CHUR143-CHUR147*(exp(lambdaSm*time)-1) # Calculate CHUR for age
    eNd = ((Ndrat/CHUR143)-1)*10**4 # Calculate EpsNd
    eNdi = ((Ndi/CHUR143i)-1)*10**4 # Calculate EpsNdi
    # If age a single integer, create iterable list.
    if isinstance(age,int):
        age = pd.Series(age)

    if 0 not in age.values:
        return (Ndi,eNdi)
    elif 0 in age.values:
        return (eNd)
    else:
        print('Mix of zero and non-zero ages')

def Srinit(Srrat,Rbrat,age):
    """
    Calculate initial 87Sr/86Sr.

    Paramters:
        Srrat: Measured 87Sr/86Sr ratio
        Rbrat: Measured 87Rb/86Sr ratio
        age: Age of sample in Ma

    Returns:
        Sri: initial 87Sr/86Sr ratio
    """

    lambdaRb = 1.39e-11 # Rb-87 decay constant
    years = 10**6 # Converter for Ma to years
    time = age*years # Years for initial calc

    Sri = Srrat-(Rbrat*(exp(lambdaRb*time)-1)) # Calculate initial 87Sr/86Sr
    return (Sri)

def RbSr_rat(Rb,Sr,Srrat):
    """
    Calculate 87Rb/86Sr from Rb and Sr concentrations

    Paramters:
        Rb: Rb concentration (ppm)
        Sr: Sr concentration (ppm)
        Srrat: 87Sr/86Sr ratio

    Returns:
        rbsr8786: Calculated 87Rb/86Sr ratio

    """
    # Fundamental Rb and Sr isotopic parameters - from CIAAW
    Rb85_mass = 84.91178974 # Da
    Rb87_mass = 86.90918053 # Da

    Rb85_abund = 0.7217
    Rb87_abund = 0.2783

    Sr84_mass = 83.913419
    Sr86_mass = 85.90926073
    Sr87_mass = 86.90887750
    Sr88_mass = 87.90561226

    # Sr abundances vary - only used for calculation of ratios that
    # don't vary
    Sr84_abund = 0.0056
    Sr86_abund = 0.0986
    Sr87_abund = 0.0700 # Not used
    Sr88_abund = 0.8258

    Sr_8886 = Sr88_abund/Sr86_abund # 88Sr/86Sr ratio - doesn't vary
    Sr_8486 = Sr84_abund/Sr86_abund # 84Sr/86Sr ratio - doesn't vary

    # Calculate true abundances
    Sr86_abund_calc = Srrat/(Srrat+Sr_8886+Sr_8486+1)
    Sr84_abund_calc = Sr86_abund_calc*Sr_8486
    Sr88_abund_calc = Sr86_abund_calc*Sr_8886
    Sr87_abund_calc = Sr86_abund_calc*Srrat

    # Total Mass for Rb and Sr
    Rb_mass = Rb85_mass*Rb85_abund + Rb87_mass*Rb87_abund

    Sr_mass = (
        Sr84_mass*Sr84_abund_calc + Sr86_mass*Sr86_abund_calc
        + Sr87_mass*Sr87_abund_calc + Sr88_mass*Sr88_abund_calc
        )

    # 87Rb and 86Sr
    Rb87 = Rb*Rb87_abund/Rb_mass # Get mol of Rb87
    Sr86 = Sr*Sr86_abund_calc/Sr_mass # Get mol of Sr86
    rbsr8786 = Rb87/Sr86
    check = (Rb/Sr)*(2.69295 + 0.28304*Srrat)
    print('Check: ',check)

    return(rbsr8786)

def SmNd_rat(Sm,Nd,Ndrat):
    """
    Calculate 147Sm/144Nd using reported Sm, Nd, and 143Nd/144Nd

    Parameters:
        Sm: Sm concentration (ppm)
        Nd: Nd concentration (ppm)
        Ndrat: 143Nd/144Nd ratio

    Returns:
        smnd147/144: Calculated 147Sm/144Nd ratio
    """

    # Sm and Nd isotopic parameters - from CIAAW
    Sm144_mass = 143.91201
    Sm147_mass = 146.91490
    Sm148_mass = 147.91483
    Sm149_mass = 148.917191
    Sm150_mass = 149.917282
    Sm152_mass = 151.919739
    Sm154_mass = 153.92222

    Sm144_abund = 0.0308
    Sm147_abund = 0.1500
    Sm148_abund = 0.1125
    Sm149_abund = 0.1382
    Sm150_abund = 0.0737
    Sm152_abund = 0.2674
    Sm154_abund = 0.2274

    Nd142_mass = 141.90773
    Nd143_mass = 142.90982
    Nd144_mass = 143.91009
    Nd145_mass = 144.91258
    Nd146_mass = 145.91312
    Nd148_mass = 147.91690
    Nd150_mass = 149.920902

    # Nd abundances vary

    # Non-varying Nd ratios (from Faure et al., 2005)
    Nd_146144 = 0.7219 # Hamilton et al., 1983
    Nd_142144 = 1.141827
    Nd_145144 = 0.348417
    Nd_148144 = 0.241578
    Nd_150144 = 0.236418

    # Calculate Nd abundances
    Nd144_abund = Ndrat/(Ndrat + 1 + Nd_146144 + Nd_142144 + Nd_145144
                         + Nd_148144 + Nd_150144)
    Nd142_abund = Nd_142144*Nd144_abund
    Nd143_abund = Ndrat*Nd144_abund
    Nd145_abund = Nd_145144*Nd144_abund
    Nd146_abund = Nd_146144*Nd144_abund
    Nd148_abund = Nd_148144*Nd144_abund
    Nd150_abund = Nd_150144*Nd144_abund

    # Total mass for Sm and Nd
    Sm_mass = (
        Sm144_mass*Sm144_abund + Sm147_mass*Sm147_abund + Sm148_mass*
        Sm148_abund + Sm149_mass*Sm149_abund + Sm150_mass*Sm150_abund +
        Sm152_mass*Sm152_abund + Sm154_mass*Sm154_abund
        )

    Nd_mass = (
        Nd142_mass*Nd142_abund + Nd143_mass*Nd143_abund +
        Nd144_mass*Nd144_abund + Nd145_mass*Nd145_abund +
        Nd146_mass*Nd146_abund + Nd148_mass*Nd148_abund +
        Nd150_mass*Nd150_abund
        )

    # 147Sm and 143Nd
    Sm147 = Sm*Sm147_abund/Sm_mass
    Nd144 = Nd*Nd144_abund/Nd_mass

    smnd147144 = Sm147/Nd144

    check1 = (Sm/Nd) * (0.53149+0.14252*Ndrat)
    check2 = (Sm/Nd) * 0.602

    print('Check1: ',check1)
    print('Check2: ',check2)

    return(smnd147144)


import numpy as np

from pykitml import _functions

eg_ws = np.array([[0.1, -0.2, 0.3], [-0.4, 0.5, -0.6]])

# =============================
# = Test activation functions =
# =============================

def test_sigmoid():
    expected_output = np.array([[0.52497919, 0.450166  , 0.57444252],
       [0.40131234, 0.62245933, 0.35434369]])

    assert np.allclose(_functions.sigmoid(eg_ws), expected_output)

def test_tanh():
    expected_output = np.array([[ 0.09966799, -0.19737532,  0.29131261],
       [-0.37994896,  0.46211716, -0.53704957]])

    assert np.allclose(_functions.tanh(eg_ws), expected_output)

def test_leakyrelu():
    expected_output = np.array([[ 0.1  , -0.002,  0.3  ],
       [-0.004,  0.5  , -0.006]])

    assert np.allclose(_functions.leakyrelu(eg_ws), expected_output)

def test_relu():
   expected_output = np.array([[0.1, 0, 0.3], [0, 0.5, 0]])

   assert np.allclose(_functions.relu(eg_ws), expected_output)

def test_softmax():
    expected_output = np.array([[0.33758454, 0.25008878, 0.41232669],
       [0.23373585, 0.57489742, 0.19136673]])

    assert np.allclose(_functions.softmax(eg_ws), expected_output)

# ===========================================
# = Test derivative of activation functions =
# ===========================================

def test_sigmoid_prime():
    activ = _functions.sigmoid(eg_ws)

    expected_output = np.array([[0.24937604, 0.24751657, 0.24445831],
       [0.24026075, 0.23500371, 0.22878424]])

    assert np.allclose(_functions.sigmoid_prime(eg_ws, activ), expected_output)

def test_tanh_prime():
    activ = _functions.tanh(eg_ws)

    expected_output = np.array([[0.99006629, 0.96104298, 0.91513696],
       [0.85563879, 0.78644773, 0.71157776]])

    assert np.allclose(_functions.tanh_prime(eg_ws, activ), expected_output)

def test_leakyrelu_prime():
    activ = _functions.leakyrelu(eg_ws)

    expected_output = np.array([[1.  , 0.01, 1.  ],
       [0.01, 1.  , 0.01]])

    assert np.allclose(_functions.leakyrelu_prime(eg_ws, activ), expected_output)

def test_relu_prime():
    activ = _functions.relu(eg_ws)

    expected_output = np.array([[1, 0, 1], [0, 1, 0]])

    assert np.allclose(_functions.relu_prime(eg_ws, activ), expected_output)

def test_softmax_prime():
    activ = _functions.leakyrelu(eg_ws)

    expected_output = np.array([[ 0.09    , -0.002004,  0.21    ],
       [-0.004016,  0.25    , -0.006036]])

    assert np.allclose(_functions.softmax_prime(eg_ws, activ), expected_output)

# =======================
# = Test cost functions =
# =======================

def test_mse():
   eg_output = np.array([0.1, 0.4, -0.1, 0.3])
   eg_target = np.array([0.2, 0.3, -0.5, 0.2])
   expected_output = np.array([0.005, 0.005, 0.08 , 0.005])

   assert np.allclose(_functions.mse(eg_output, eg_target), expected_output)

def test_cross_entropy():
   eg_output = np.array([0.3, 0.1, 0.9, 0.7])
   eg_target = np.array([1, 0, 1, 1])
   expected_output = np.array([1.2039728 , 0.10536052, 0.10536052, 0.35667494])

   assert np.allclose(_functions.cross_entropy(eg_output, eg_target), expected_output)

# =====================================
# = Test derivative of cost functions =
# =====================================

def test_mse_prime():
   eg_output = np.array([0.1, 0.4, -0.1, 0.3])
   eg_target = np.array([0.2, 0.3, -0.5, 0.2])
   expected_output = np.array([-0.1, 0.1, 0.4, 0.1])

   assert np.allclose(_functions.mse_prime(eg_output, eg_target), expected_output)

def test_cross_entropy_prime():
   eg_output = np.array([0.3, 0.1, 0.9, 0.7])
   eg_target = np.array([1, 0, 1, 1])
   expected_output = np.array([-3.33333333,  1.11111111, -1.11111111, -1.42857143])

   assert np.allclose(_functions.cross_entropy_prime(eg_output, eg_target), expected_output)


# -*- coding: utf-8 -*-
"""
Notes and tries on Chaper 03 (Think Stats 2, Allen B. Downey)

Self-study on statistics using pyhton
@author: Github: @rafaelmm82
"""

import thinkstats2
import thinkplot
import nsfg
import math
import first
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

pmf= thinkstats2.Pmf([1, 2, 2, 3, 5])
pmf

pmf.Prob(2)

pmf[2]

pmf.Incr(2, 0.2)
pmf.Prob(2)

pmf.Mult(2, 0.5)
pmf

pmf.Total()

pmf.Normalize()
pmf.Total()
pmf

live, firsts, others = first.MakeFrames()

first_pmf = thinkstats2.Pmf(firsts.prglngth, label='first')
other_pmf = thinkstats2.Pmf(others.prglngth, label='other')

width = 0.45
thinkplot.PrePlot(2, cols=2)
thinkplot.Hist(first_pmf, align='right', width=width)
thinkplot.Hist(other_pmf, align='left', width=width)
thinkplot.Config(xlabel='weeks',
                 ylabel='probability',
                 axis=[27, 46, 0, 0.6])
thinkplot.PrePlot(2)
thinkplot.SubPlot(2)
thinkplot.Pmfs([first_pmf, other_pmf])
thinkplot.Show(xlabel='weeks',
               axis=[27, 46, 0, 0.6])

weeks = range(35, 46)
diffs = []
for week in weeks:
    p1 = first_pmf.Prob(week)
    p2 = other_pmf.Prob(week)
    diff = 100 * (p1 - p2)
    diffs.append(diff)

thinkplot.Bar(weeks, diffs)

d = {7: 8, 12: 8, 17: 14, 22: 4,
     27:6, 32: 12, 37: 8, 42: 3,47: 2}

pmf = thinkstats2.Pmf(d, label='actual')
print('mean', pmf.Mean())

def BiasPmf(pmf, label=''):
    new_pmf = pmf.Copy(label=label)

    for x, p in pmf.Items():
        new_pmf.Mult(x, x)

    new_pmf.Normalize()
    return new_pmf

biased_pmf = BiasPmf(pmf, label='observed')
thinkplot.PrePlot(2)
thinkplot.Pmfs([pmf, biased_pmf])
thinkplot.Show(xlabel='class size', ylabel='PMF')

def UnbiasPmf(pmf, label):
    new_pmf = pmf.Copy(label=label)

    for x, p in pmf.Items():
        new_pmf.Mult(x, 1.0/x)

    new_pmf.Normalize()
    return new_pmf

unbiased_pmf = UnbiasPmf(pmf, label='better_values')
thinkplot.PrePlot(2)
thinkplot.Pmfs([pmf, unbiased_pmf])
thinkplot.Show(xlabel='class size', ylabel='PMF')

array = np.random.randn(4, 2)
df = pd.DataFrame(array)
df

columns = ['A', 'B']

df = pd.DataFrame(array, columns=columns)
df

index = ['a', 'b', 'c', 'd']
df = pd.DataFrame(array, columns=columns, index=index)
df

df.loc['a']

df.iloc[0]

indices = ['a', 'c']
df.loc[indices]

df['a':'c']

df[0:2]


"""
Linear equation solver
* A is a square matrix and b is a vector.
* The solution x is returned in vector b
*
* Reference:
* 	Golub and Van Loan, "Matrix Computations", 1999, Ch 3

void linsolve(REAL **A, REAL *b, int N){

  int i,j,k;
  REAL sumi;

  // Algorithm to find LU decomp - See page 99
  for(k=0;k<N-1;k++){
    for(i=k+1;i<N;i++){
      A[i][k] = A[i][k]/A[k][k];
      for(j=k+1;j<N;j++){
	A[i][j] = A[i][j] - A[k][j]*A[i][k];
      }
    }
  }

  // Solve L.y=b via forward substitution (Alg 3.1.1);
  b[0] = b[0];
  for(i=1;i<N;i++){
    sumi=0.0;
    for(j=0;j<i;j++){
      sumi = sumi + A[i][j]*b[j];
    }
    b[i]=b[i]-sumi;
  }

  //Solve U.x=y via backward substitution (Alg 3.1.2)

  b[N-1] = b[N-1]/A[N-1][N-1];
  for(i=N-2;i>=0;i--){
    sumi=0.0;
    for(j=i+1;j<N;j++){
      sumi = sumi + A[i][j]*b[j];
    }
    b[i] = (b[i] - sumi)/A[i][i];
  }
} // End of linsolve
"""

import numpy as np
from numba import jit

#void linsolve(REAL **A, REAL *b, int N){
@jit(nopython=False, error_model='numpy')
def linsolve(A, b, N):
    #// Algorithm to find LU decomp - See page 99
    for k in range(0,N-1):
        for i in range(k+1,N):
            if A[k,k]!=0.:
                A[i,k] = A[i,k]/A[k,k]

            for j in range(k+1,N):
                A[i,j] = A[i,j] - A[k,j]*A[i,k]

    # // Solve L.y=b via forward substitution (Alg 3.1.1);
    b[0] = b[0]
    for i in range(1,N):
        sumi = 0.
        for j in range(0,i):
            sumi = sumi + A[i,j]*b[j]

        b[i]=b[i]-sumi

    #//Solve U.x=y via backward substitution (Alg 4.1.2)
    if A[N-1,N-1] != 0.:
        b[N-1] = b[N-1]/A[N-1,N-1]

    for i in range(N-2,-1,-1):
        sumi = 0.
        for j in range(i+1,N):
            sumi = sumi + A[i,j]*b[j]

        if A[i,i] != 0.:
            b[i] = (b[i] - sumi)/A[i,i]

    return b

#def linsolve(A, b, N):
#    #// Algorithm to find LU decomp - See page 99
#    for k in range(0,N-1):
#        for i in range(k+1,N):
#            A[i,k] = A[i,k]/A[k,k]
#            for j in range(k+1,N):
#                A[i,j] = A[i,j] - A[k,j]*A[i,k]
#
#    # // Solve L.y=b via forward substitution (Alg 3.1.1);
#    b[0] = b[0];
#    for i in range(N):
#        sumi = 0.
#        for j in range(0,i):
#            sumi = sumi + A[i,j]*b[j]
#
#        b[i]=b[i]-sumi
#
#    #//Solve U.x=y via backward substitution (Alg 3.1.2)
#
#    b[N-1] = b[N-1]/A[N-1,N-1]
#    for i in range(N-2,0):
#        sumi = 0.
#        for j in range(i+1,N):
#            sumi = sumi + A[i,j]*b[j]
#        b[i] = (b[i] - sumi)/A[i,i]
#
#    return b


'''
ANKIT KHANDELWAL
15863
Exercise 4 Square Wave
'''

from math import floor

import matplotlib.pyplot as plt
import numpy as np

def f(t):
    if floor(2 * t) % 2 == 0:
        return 1
    else:
        return -1

xv = np.linspace(0, 1, 1000)
values = []
for i in range(1000):
    values.append(f(xv[i]))

plt.plot(xv, values, label='Square Wave')
v_fft = np.fft.rfft(values)
v_fft[10:] = 0
v_inverse = np.fft.irfft(v_fft)
plt.plot(xv, v_inverse, label='Smoothing using first 10 coefficients')
plt.legend(loc='lower left', framealpha=0)
plt.show()

'''
As the function must be periodic, it needs to deviate to acheive periodicity.
This results in large fluctuations at end points.
Also, because we are only using 10 modes, we get wiggles in the result.
We need to use more modes to get better result here.
This is because FT is by definition using sines and cosines.
And we are approximating a straight line with these.
'''

Ankit Khandelwal_HW4/Exercise 4/exercise 4 square wave.py


import numpy as np
from math import sqrt

def multiply_matrix(A, B):
    if A.shape[1] != B.shape[0]:
        raise NameError('deu ruim')

    C = np.zeros(shape=(A.shape[0], B.shape[1]))

    for i in range(C.shape[0]):
        for j in range(C.shape[1]):
            for k in range(B.shape[0]):
                C[i, j] += A[i, k] * B[k, j]

    return C

def transpose_matrix(A):
    t = np.zeros(shape=(A.shape[1], A.shape[0]))

    for i in range(t.shape[0]):
        for j in range(t.shape[1]):
            t[i, j] = A[j, i]

    return t

def sumDiagonalMatrix(A):
    sum = 0

    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            if i > j:
                sum += A[i, j]

    return sum

def obtain(A, q, p):
    UL = np.identity(A.shape[0])

    UL[q, q] = UL[p, p] = A[p, p] / sqrt(A[p, p] ** 2 + A[q, p] ** 2)
    UL[p, q] = A[q, p] / sqrt(A[p, p] ** 2 + A[q, p] ** 2)
    UL[q, p] = -A[q, p] / sqrt(A[p, p] ** 2 + A[q, p] ** 2)

    return UL

def decompor(A):
    Q = np.identity(A.shape[0])
    R = np.identity(A.shape[0])

    UK = np.identity(A.shape[0])
    UL = np.identity(A.shape[0])

    for i in range(1, A.shape[0]):
        for j in range(0, i):
            UL = obtain(A, i, j)
            Q = multiply_matrix(Q, transpose_matrix(UL))
            UK = multiply_matrix(UL, UK)

    R = multiply_matrix(UK, A)

    return Q, R

if __name__ == '__main__':
    A = np.matrix('5 2 1; 2 3 1; 1 1 1')
    matrixList = []
    cont = 0

    e = 0.001

    print "Matriz A antes: \n",A

    while sumDiagonalMatrix(A) > e:
        Q, R = decompor(A)

        A = multiply_matrix(R, Q)
        matrixList.append(A)
        cont += 1

    # print matrixList,"\n"
    np.set_printoptions(precision=3, suppress=True)
    print "Matriz A depois: \n",(A)
    print "Auto Valores: \n","{",A[0, 0],",",A[1, 1],",",A[2,2],"}"
    print "Numero de iteracoes: ",cont


import numpy as np

def evaluate(X,type):

    if X.ndim == 1:
        X = np.array([X])
    nsample = np.size(X, 0)
    y = np.zeros(shape=[nsample, 1])
    if type.lower() == "branin":
        for ii in range(0, nsample):
            y[ii, 0] = branin(X[ii, :])
    elif type.lower() == "styblinski":
        for ii in range(0, nsample):
            y[ii, 0] = styb(X[ii, :])
    elif type.lower() == "sasena":
        y = sasena(X)
    elif type.lower() == "griewank":
        y = griewank(X)
    elif type.lower() == "schaffer":
        y = schaffer(X)
    elif type.lower() == "schaffer1":
        y = schaffer1(X)
    elif type.lower() == "fonseca":
        y = fonseca(X)
    elif type.lower() == "ishigami":
        y = ishigami(X)
    else:
        raise NameError("Test case unavailable!")
    return y

# 2D Cases
def styb(x):
    d = len(x)
    sum = 0
    for ii in range (0,d):
        xi = x[ii]
        new = xi**4 - 16*xi**2 + 5*xi
        sum = sum + new
    y = sum/2
    return y

def branin (x):
    a = 5.1/(4 * (np.pi)**2 )
    b = 5/ np.pi
    c = (1-(1/(8 * np.pi)))

    f = (x[1] - a*x[0]**2 + b*x[0] - 6)**2 + 10*(c* np.cos(x[0]) +1)
    return f

def sasena (x):
    """
    SASENA function (two variables).

    Input
      X - (nsamp x nvar) matrix of experimental design.

    Output
      Y - (nsamp x 1) vector of responses.
    """
    Y = np.zeros(shape=[np.size(x,0),1])
    for ii in range(0,np.size(x,0)):
        xtemp = x[ii,:]
        x1 = xtemp[0]
        x2 = xtemp[1]

        Y[ii,0] = 2 + 0.01*(x2 - x1**2)**2 + (1-x1)**2 + 2*(2-x2)**2 + 7 * np.sin(0.5*x1) * np.sin(0.7*x1*x2)
    return Y

def griewank (x):
    d = np.size(x,1)
    nn = np.size(x,0)
    Y = np.zeros(shape=[nn,1])

    for ii in range(0,nn):
        sum = 0
        prod = 1
        for jj in range(0,d):
            xi = x[ii,jj]
            sum = sum + xi**2 / 4000
            prod = prod * np.cos(xi/np.sqrt(jj+1))
        Y[ii,0] = sum - prod + 1

    return Y

#1D Cases
def case10(x):
    #bound [0,10]
    f = -x * np.sin(x)
    return f

#Multi-objective cases
def schaffer (x):
    """
    Generalized Schaffer problem
    Reference: "Emmerich, M. T., & Deutz, A. H. (2007, March). Test problems
    based on LamÃ© superspheres. In International Conference on Evolutionary
    Multi-Criterion Optimization (pp. 922-936). Springer, Berlin, Heidelberg."

    Inputs:
      X:  Vector of decision variables
      r:  Describes the shape of the Pareto front

    Output:
      fitness:    fitness function value

    Written by Kaifeng Yang, 20/1/2016
    """
    r = 1
    a = 1/(2*r)
    m = np.size(x,0)
    n = np.size(x,1)
    fitness = np.zeros(shape=[m,2])
    for i in range(0,m):
        fitness[i,0] = (1/(n**a))*np.sum(x[i,:]**2)**a
        fitness[i,1] = (1/(n**a))*np.sum((1-x[i,:])**2)**a
    if m == 1:
        fitness = fitness[0,:]
    return fitness

def fonseca (x):
    b = np.size(x[:,0])
    n = 2
    m = np.size(x, 0)
    one = np.ones(shape=[b,1])
    sum1 = np.zeros(shape=[b,1]);f1 = np.zeros(shape=[b,1])
    sum2 = np.zeros(shape=[b,1]);f2 = np.zeros(shape=[b,1])
    c  = np.ones(shape=[n])*(1/np.sqrt(n))
    for jj in range (0,b):
        sum1[jj,0] = -1*np.sum((x[jj,:]-(c))**2)
        sum2[jj,0] = -1*np.sum((x[jj,:]+(c))**2)
        f1[jj,0] = one[jj,0] - np.exp(sum1[jj,0])
        f2[jj,0] = one[jj,0] - np.exp(sum2[jj,0])
    f = np.hstack((f1,f2))
    if m == 1:
        f = f[0,:]
    return f

def schaffer1 (x):
    b = np.size(x[:,0])
    m = np.size(x, 0)
    f1 = np.zeros(shape=[b, 1])
    f2 = np.zeros(shape=[b, 1])
    for jj in range (0,b):
        f1[jj,0] = x[jj,0]**2
        f2[jj,0] = (x[jj,0]-2)**2
    f = np.hstack((f1, f2))
    if m == 1:
        f = f[0, :]
    return f

def ishigami(x):
    x1 = x[:, 0]
    x2 = x[:, 1]
    x3 = x[:, 2]
    a = 7
    b = 0.1
    f = np.sin(x1) + a * (np.sin(x2))**2 + b * (np.sin(x1) * x3**4)
    return f.reshape(-1,1)


# -*- coding: utf-8 -*-
"""
Created on Sun Nov 26 10:25:49 2017

@author: ASUS
"""
import numpy as np
import matplotlib.pyplot as plt
import os

# Where to save the figures
PROJECT_ROOT_DIR = "."

# In[]
def save_fig(fig_id, tight_layout=True):
    path = os.path.join(PROJECT_ROOT_DIR, "images", fig_id + ".png")
    print("Saving figure", fig_id)
    if tight_layout:
        plt.tight_layout()
    plt.savefig(path, format='png', dpi=300)

# In[]

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

def relu(z):
    return np.maximum(0, z)

def derivative(f, z, eps=0.000001):
    return (f(z + eps) - f(z - eps))/(2 * eps)

# In[]
z = np.linspace(-5, 5, 200)

plt.figure(figsize=(11,4))

plt.subplot(121)
plt.plot(z, np.sign(z), "r-", linewidth=2, label="Step")
plt.plot(z, logit(z), "g--", linewidth=2, label="Logit")
plt.plot(z, np.tanh(z), "b-", linewidth=2, label="Tanh")
plt.plot(z, relu(z), "m-.", linewidth=2, label="ReLU")
plt.grid(True)
plt.legend(loc="center right", fontsize=14)
plt.title("Activation functions", fontsize=14)
plt.axis([-5, 5, -1.2, 1.2])

plt.subplot(122)
plt.plot(z, derivative(np.sign, z), "r-", linewidth=2, label="Step")
plt.plot(0, 0, "ro", markersize=5)
plt.plot(0, 0, "rx", markersize=10)
plt.plot(z, derivative(logit, z), "g--", linewidth=2, label="Logit")
plt.plot(z, derivative(np.tanh, z), "b-", linewidth=2, label="Tanh")
plt.plot(z, derivative(relu, z), "m-.", linewidth=2, label="ReLU")
plt.grid(True)
#plt.legend(loc="center right", fontsize=14)
plt.title("Derivatives", fontsize=14)
plt.axis([-5, 5, -0.2, 1.2])

save_fig("activation_functions_plot")
plt.show()

# In[]
def heaviside(z):
    return (z >= 0).astype(z.dtype)

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

def mlp_xor(x1, x2, activation=heaviside):
    return activation(-activation(x1 + x2 - 1.5) + activation(x1 + x2 - 0.5) - 0.5)

# In[]
x1s = np.linspace(-0.2, 1.2, 100)
x2s = np.linspace(-0.2, 1.2, 100)
x1, x2 = np.meshgrid(x1s, x2s)

z1 = mlp_xor(x1, x2, activation=heaviside)
z2 = mlp_xor(x1, x2, activation=sigmoid)

plt.figure(figsize=(10,4))

plt.subplot(121)
plt.contourf(x1, x2, z1)
plt.plot([0, 1], [0, 1], "gs", markersize=20)
plt.plot([0, 1], [1, 0], "y^", markersize=20)
plt.title("Activation function: heaviside", fontsize=14)
plt.grid(True)

plt.subplot(122)
plt.contourf(x1, x2, z2)
plt.plot([0, 1], [0, 1], "gs", markersize=20)
plt.plot([0, 1], [1, 0], "y^", markersize=20)
plt.title("Activation function: sigmoid", fontsize=14)
plt.grid(True)


import math

import numpy as np

import pymit

LOG_BASE = 2

def H(X, bins):
    """
    Calculates the entropy of X.
    If X is already discretised, set \ref bins to the amount of bins, aka states of X.
    If X is not discretised, \ref bins will be used, to diskretise X into  \ref bins states

    @param X np vector
    @param bins If X is already diskretised, amount of bins of X. If X is not discretised, amount of bins to diskretise X into
    @return Returns the entropy of X
    """
    p_x, _ = pymit._lib.histogram(X, bins=bins)
    p_x = p_x / len(X)

    H_ = 0
    for p in p_x:
        if p > 0:
            H_ -= p * math.log(p)
    H_ = H_ / math.log(bins)
    return H_

def H_cond(X, Y, bins):
    """
    Calculates the conditional entropy of X depending on Y.
    If X and Y are already discretised, set \ref bins to the amount of bins, aka states of X and Y.
    If X and Y are not discretised, \ref bins will be used, to diskretise X and Y into \ref bins states

    @param X np vector depending on \ref Y
    @param Y np vector
    @param bins If X and Y are already diskretised specify the amount of bins of X and Y. If X and Y are not discretised specifies the amount of bins to diskretise X into
                bins can be spcified as tuple, e.g. bins = (bins_x, bins_y), to diskretise X and Y independently, or as a single value to diskretise X and Y similar, e.g. bins = amount_of_bins
    @return Returns the conditional entropy
    """
    if type(bins) == list:
        ybins = bins[1]
        xbins = bins[0]
        base = LOG_BASE
    else:
        ybins = bins
        xbins = bins
        base = bins

    p_xy, _, _ = pymit._lib.histogram2d(X, Y, bins=[xbins, ybins])
    p_xy = p_xy / len(X)
    p_y, _ = np.histogram(Y, bins=ybins)
    p_y = p_y / len(Y)

    H = 0
    for i in range(ybins):
        for j in range(xbins):
            if p_xy[j, i] > 0:
                p_cond = p_xy[j, i] / p_y[i]
                H -= p_y[i] * p_cond * math.log(p_cond)
    H = H / math.log(base)
    return H


# -*- coding: utf-8 -*-

import numpy as np
x = np.array([3, 0, -4])
n1 = np.linalg.norm(x, ord = 1)  # ord = 1表示向量的1-范数
n2 = np.linalg.norm(x, ord = 2)  # ord = 2表示向量的2-范数，系统默认
n3 = np.linalg.norm(x, ord = np.inf)  # ord = np.inf表示向量的∞-范数
n4 = np.linalg.norm(x, ord = 0)  # ord = 0表示向量的0-范数
n5 = np.linalg.norm(x, ord = -np.inf)  # ord = -np.inf表示向量的-∞-范数
print('norm_1   ', n1)
print('norm_2   ', n2)
print('norm_∞   ', n3)
print('norm_0   ', n4)
print('norm_-∞   ', n5)