"""

Generate datasets of inputs/outputs for model training.

Alex Cunliffe
2020-10-18

"""

import random

import numpy as np
import torch

def projectile_dataset(n_samples, normalize=True):
    """
    Generate dataset for the projectile problem - uses actual physics to compute,
    based on a projectile's initial velocity and angle, the horizontal
    displacement, max height, and travel time of the projectile.
    Inputs:
       n_samples: int, number of samples to generate
       normalize: bool, if True, normalize the inputs and outputs by subtracting
          the mean and dividing by the standard deviation
    Outputs:
       x: torch.Tensor, velocity & angle for a set of projectiles
       y: torch.Tensor, associated horizontal displacement, max height, and
          travel time of projectiles
       normalization_params: dict; Empty if normalize is False. Otherwise contains:
          {"x_mean": np.array,
           "x_std": np.array,
           "y_mean": np.array,
           "y_std": np.array
    """
    initial_velocity = np.array(range(n_samples))
    angle = np.array(random.choices(range(90), k=n_samples))
    angle_radians = angle * np.pi / 180.0

    horizontal_velocity = initial_velocity * np.cos(angle_radians)
    vertical_velocity = initial_velocity * np.sin(angle_radians)
    time = 2 * vertical_velocity / 9.8
    horizontal_displacement = horizontal_velocity * time
    max_height = vertical_velocity * 0.5 * time + 0.5 * (-9.8) * (0.5 * time) ** 2

    x = np.vstack((initial_velocity, angle_radians)).T
    y = np.vstack((horizontal_displacement, max_height, time)).T

    if normalize:
        x_mean = np.mean(x, axis=0)
        x_std = np.std(x, axis=0)
        x_normalized = (x - x_mean) / x_std
        y_mean = np.mean(y, axis=0)
        y_std = np.std(y, axis=0)
        y_normalized = (y - y_mean) / y_std

        normalization_params = {"x_mean": x_mean,
                                "x_std": x_std,
                                "y_mean": y_mean,
                                "y_std": y_std}

        return torch.from_numpy(x_normalized).float(), \
            torch.from_numpy(y_normalized).float(), \
            normalization_params

    return torch.from_numpy(x).float(), torch.from_numpy(y).float(), {}

def random_dataset(n_samples, n_input_features, n_output_features):
    """random dataset used for testing"""
    return torch.randn(n_samples, n_input_features), torch.randn(n_samples, n_output_features)


"""
=========================================================
Demo of the histogram (hist) function with a few features
=========================================================

In addition to the basic histogram, this demo shows a few optional features:

* Setting the number of data bins.
* The ``normed`` flag, which normalizes bin heights so that the integral of
  the histogram is 1. The resulting histogram is an approximation of the
  probability density function.
* Setting the face color of the bars.
* Setting the opacity (alpha value).

Selecting different bin counts and sizes can significantly affect the shape
of a histogram. The Astropy docs have a great section_ on how to select these
parameters.

.. _section: http://docs.astropy.org/en/stable/visualization/histogram.html
"""

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

np.random.seed(19680801)

# example data
mu = 100  # mean of distribution
sigma = 15  # standard deviation of distribution
x = mu + sigma * np.random.randn(437)

num_bins = 50

fig, ax = plt.subplots()

# the histogram of the data
n, bins, patches = ax.hist(x, num_bins, density=1)

# add a 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
     np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')

# Tweak spacing to prevent clipping of ylabel
fig.tight_layout()
plt.show()

#############################################################################
#
# ------------
#
# References
# """"""""""
#
# The use of the following functions and methods is shown in this example:

matplotlib.axes.Axes.hist
matplotlib.axes.Axes.set_title
matplotlib.axes.Axes.set_xlabel
matplotlib.axes.Axes.set_ylabel


import sympy as sy
import matplotlib.pyplot as plt
import numpy as np
plt.close()
sy.init_printing()
#From  Johansson, R Numerical Python p.209 onward.
#Create symbols for the ODE
r,k,N0, t = sy.symbols('r k N_0 t' )
N = sy.symbols('N', function=True)
ode = N(t).diff(t) - (r*N(t)*(1-N(t)/k))# - N(t)**2/(1+N(t)**2))
sy.pprint(sy.Eq(ode))
#solve the ODE
ode_sol = sy.dsolve(ode, N(t))
ics = {N(0):N0}
print('+'*30)
print('Iniital conditions:')
sy.pprint(ics)
#Find the value of the integration constants
C_eq = sy.Eq(ode_sol.lhs.subs(t,0).subs(ics),ode_sol.rhs.subs(t,0))
print('+'*30)
print('Create equations at N(0))')
sy.pprint(C_eq)
print()
#Solve for C_eq
C_sol = sy.solve(C_eq,sy.Symbol('C1'))
soln = ode_sol.subs(sy.Symbol('C1'),C_sol[0])
sy.pprint(C_sol)
sy.pprint(soln)
print('+'*30)

ode_func = sy.lambdify(t, soln.rhs.subs({N0:100,r:.2,k:1000}) )
sy.pprint(ode_func(20))
# print('Compare to http://www.ugrad.math.ubc.ca/coursedoc/math100/notes/diffeqs/cool.html')
print()
print('Now use a theano function allowing broadcastable arrays')
from sympy.printing.theanocode import theano_function
theano_ode = theano_function([t],[soln.rhs.subs({N0:.15,r:.2,k:.4})],dims={t:1})
tt = np.linspace(0,80)
theano_output = theano_ode(tt)
sy.pprint(theano_output)
plt.plot(tt, theano_output)
plt.show()
#See http://matthewrocklin.com/blog/work/2013/04/05/SymPy-Theano-part-3
#for better examples on using Theano....

import numpy,import sympy,from sympy,import theano


"""
10001st prime
Problem 7
By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.

What is the 10 001st prime number?
"""

import numpy as np

n = 200000
sieve = np.array([True for _ in range(n)])
sieve[0:2] = False
upper_bound = int(np.sqrt(n))

for i in range(2, upper_bound+1):
    if sieve[i]:
        j = i * i
        counter = 0
        while j < n:
            sieve[j] = False
            j = (i * i) + (counter * i)
            counter += 1

primes = np.where(sieve == True)[0]

solution = primes[10000]
print('solution: ', solution)

"""
104743
"""


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

# Pulling data from Data sets
datafile_1 = np.genfromtxt('data_1.csv', delimiter=',')
# print(datafile_1)
datafile_2 = np.genfromtxt('data_2.csv', delimiter=',')
# print(datafile_2)

datafile_1 = datafile_1[1:]
datafile_2 = datafile_2[1:]

# Least square method for Data Set 1
# Fitting a parabola for y=Ax+B
def fit_parabola(points):
	n_points = len(points)
	# calculating matrix A and B for least squares
	A = []
	B = []
	for point in points:
		x = point[0]
		y = point[1]
		A.append([x**2,x,1])
		B.append([y])

# Array A, B
	A = np.array(A)
	B = np.array(B)
	# print(A)
	# print(B)

# Matrix multiplication for getting solution (sol) = (ATA)^-1.AT.B
	sol = np.matmul(np.transpose(A),A)
	# print(sol)
	sol = np.linalg.inv(sol)
	# print(sol)
	sol = np.matmul(sol,np.transpose(A))
	# print(sol)
	sol = np.matmul(sol,B)

	return sol

plt.scatter(datafile_1[:,0],datafile_1[:,1])
# print(datafile_1[:,0])

datafile_1_list = datafile_1.tolist()

least_square_sol = fit_parabola(datafile_1_list)
# print(least_square_sol)
a = least_square_sol[0]
b = least_square_sol[1]
c = least_square_sol[2]
x = np.linspace(0,500,1000)
# print(x)
y = a*(x**2)+b*x+c
# print(y)

#plotting the parabola
plt.plot(x,y)
plt.title('Least Square Method')
plt.show()

#RANSAC Method for Data Set 2
datafile_2_list = datafile_2.tolist()

dist_thresh = 70        # Distance threshold for line fundamental matrix, we considered this by looking at the data set
thresh_prob = 0.8       # desired probability that we get a good sample
plt.scatter(datafile_2[:,0],datafile_2[:,1])

sol_found = False

for n_interations in range(150):
	#choosing random 3 points
	random_points = []
	random_indices = random.sample(range(len(datafile_2_list)), 3)
	# printing (random_indices)
	for random_index in random_indices:
		random_points.append(datafile_2_list[random_index])

	# print(random_points)

	random_points_sol = fit_parabola(random_points)

	a = random_points_sol[0]
	b = random_points_sol[1]
	c = random_points_sol[2]

	# fit a parabola using these fit_parabola(points)

	inliers = []
	# finding distance of all the points from parbola and append to inliers
	for point in datafile_2_list:
		x_curr_point = point[0]
		y_curr_point = point[1]

		# find distsance of point from parabola
		dist = abs(y_curr_point- (a*(x_curr_point**2)+b*x_curr_point+c))
		# print(dist)
        # if distance is less than the threshold that will be an inlier
		if dist<dist_thresh:
			inliers.append([x_curr_point,y_curr_point])
    # If the probability to find the best sample is more than our considered threshold probability then we have the best solution
	if((float(len(inliers))/len(datafile_2_list))>thresh_prob):
		sol_found = True
		break

# plotting parabola
if(sol_found==True):
	inliers_arr = np.array(inliers)
	plt.scatter(inliers_arr[:,0],inliers_arr[:,1],color="r")
	least_square_sol = fit_parabola(inliers)
	# print(least_square_sol)
	a = least_square_sol[0]
	b = least_square_sol[1]
	c = least_square_sol[2]
	x = np.linspace(0,500,1000)
	y = a*(x**2)+b*x+c

	plt.title('RANSAC Method')
	plt.plot(x,y)
	plt.show()

else:
	print('I counld not find a solution')


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

def normal_dist(sigma=1, mean=0):
    vx = np.linspace(-5, 5, 1000)
    return vx, norm.pdf(vx, mean, sigma)

if __name__ == "__main__":
    if len(sys.argv) == 1:
        vx, vy = normal_dist()
        plt.plot(vx, vy)
    elif len(sys.argv) == 2:
        vx, vy = normal_dist(float(sys.argv[1]))
        plt.plot(vx, vy)
    elif len(sys.argv) == 3:
        vx, vy = normal_dist(float(sys.argv[1]), float(sys.argv[2]))
        plt.plot(vx, vy)

    plt.xlim(-5, 5)
    plt.ylim(0, None)
    plt.xlabel("x")
    plt.ylabel("Probability density")
    plt.show()


from __future__ import division
import numpy as np
import NMF_linear_solve as LS

def cubic_spline(x, v):
    """
    Natural cubic spline interpolation function
    :param x: interpolation nodes, numpy array with dim [n + 1, 1]
    :param v: interpolation values, numpy array with dim [n + 1, 1]
    :return: [a, b, c, d] cubic polynomials coefficients, 4 [n, 1] numpy arrays
    """
    n = x.shape[0] - 1
    z = np.zeros([n, 1])
    for i in range(1, n):
        z[i] = 6 * ((v[i + 1] - v[i]) / (x[i + 1] - x[i]) - (v[i] - v[i - 1]) / (x[i] - x[i - 1]))

    M = np.zeros([n, n])
    for i in range(1, n):
        M[i, i] = 2 * (x[i + 1] - x[i - 1])
    for i in range(1, n - 1):
        M[i, i + 1] = x[i + 1] - x[i]
    for i in range(2, n):
        M[i, i - 1] = x[i] - x[i - 1]
    w = np.zeros([n + 1, 1])
    w[1:n,:] = LS.linear_solve_lu_no_pivoting(M[1:n, 1:n], z[1:n])
    #print M[1:n, 1:n]

    a, b, c, d = np.zeros([n + 1,1]), np.zeros([n + 1,1]), np.zeros([n + 1,1]), np.zeros([n + 1,1])
    for i in range(1, n + 1):
        c[i] = (w[i - 1] * x[i] - w[i] * x[i - 1]) / (2 * (x[i] - x[i - 1]))
        d[i] = (w[i] - w[i - 1]) / (6 * (x[i] - x[i - 1]))
        q = np.zeros([n, 1])
        r = np.zeros([n + 1, 1])
    for i in range(1, n + 1):
        q[i - 1] = v[i - 1] - c[i] * x[i - 1] ** 2 - d[i] * x[i - 1] ** 3
        r[i] = v[i] - c[i] * x[i] ** 2 - d[i] * x[i] ** 3
    for i in range(1, n + 1):
        a[i] = (q[i - 1] * x[i] - r[i] * x[i - 1]) / (x[i] - x[i - 1])
        b[i] = (r[i] - q[i - 1]) / (x[i] - x[i - 1])

    return[a[1:n + 1], b[1:n + 1], c[1:n + 1], d[1:n + 1]]

def cubic_spline_interpolate(x_new, x, v):
    [a, b, c, d] = cubic_spline(x, v)
    n = x.shape[0] - 1
    v_new = 0
    for i in range(0, n):
        if x_new >= x[i] and x_new < x[i + 1]:
            a_v, b_v, c_v, d_v = a[i], b[i], c[i], d[i]
            v_new = a_v + b_v * x_new + c_v * x_new ** 2 + d_v * x_new ** 3
    return v_new

def main():
    t = np.array([0, 2/12, 6/12, 1, 20/12])
    r = np.array([0.005, 0.0065, 0.0085, 0.0105, 0.012])
    t = np.row_stack(t)
    r = np.row_stack(r)
    [a, b, c, d] = cubic_spline(t, r)
    print a, b, c, d

if __name__ == "__main__":
    main()


# -*- coding:utf-8 -*-
"""
@project = 0603-1
@file = simple_linear_regression
@author = Liangjisheng
@create_time = 2018/6/3 0003 下午 17:25
"""
import numpy as np

# size of the points dataset
m = 20

# points x-coordinate and dummy value (x0, x1)
X0 = np.ones((m, 1))
X1 = np.arange(1, m + 1).reshape(m, 1)
X = np.hstack((X0, X1))

# points y-coordinate
y = np.array([
    3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12,
    11, 13, 13, 16, 17, 18, 17, 19, 21
]).reshape(m, 1)

# the learning Rate alpha
alpha = 0.01

def error_function(theta, X, y):
    """Error function J definition"""
    diff = np.dot(X, theta) - y
    return (1. / 2 * m) * np.dot(np.transpose(diff), diff)

def gradient_function(theta, X, y):
    """Gradient fo the functino J definition"""
    diff = np.dot(X, theta) - y
    return (1. / m) * np.dot(np.transpose(X), diff)

def gradient_descent(X, y, alpha):
    """Perform gradient descent"""
    theta = np.array([1, 1]).reshape(2, 1)
    gradient = gradient_function(theta, X, y)
    while not np.all(np.absolute(gradient) <= 1e-5):
        theta = theta - alpha * gradient
        gradient = gradient_function(theta, X, y)
    return theta

if __name__ == '__main__':
    optimal = gradient_descent(X, y, alpha)
    print('optimal:', optimal)
    print('error function:', error_function(optimal, X, y)[0, 0])


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import sklearn.linear_model as skl

def R2(y_data, y_model):
    return 1 - np.sum((y_data - y_model) ** 2) / np.sum((y_data - np.mean(y_data)) ** 2)
def MSE(y_data,y_model):
    n = np.size(y_model)
    return np.sum((y_data-y_model)**2)/n

# A seed just to ensure that the random numbers are the same for every run.
# Useful for eventual debugging.
np.random.seed(3155)

x = np.random.rand(100)
y = 2.0+5*x*x+0.1*np.random.randn(100)

# number of features p (here degree of polynomial
p = 3
#  The design matrix now as function of a given polynomial
X = np.zeros((len(x),p))
X[:,0] = 1.0
X[:,1] = x
X[:,2] = x*x
# We split the data in test and training data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

# matrix inversion to find beta
OLSbeta = np.linalg.inv(X_train.T @ X_train) @ X_train.T @ y_train
print(OLSbeta)
# The variance is given by the inverse of the matrix X^TX
print(np.linalg.inv(X_train.T @ X_train))

# Repeat now for Ridge regression and various values of the regularization parameter
I = np.eye(p,p)
# Decide which values of lambda to use
nlambdas = 10
MSEPredict = np.zeros(nlambdas)
MSEPredictSKL = np.zeros(nlambdas)
MSETrain = np.zeros(nlambdas)
lambdas = np.logspace(-4, 0, nlambdas)
for i in range(nlambdas):
    lmb = lambdas[i]
    Ridgebeta = np.linalg.inv(X_train.T @ X_train+lmb*I) @ X_train.T @ y_train
    print(np.linalg.inv(X_train.T @ X_train+lmb*I))


import numpy as np
import matplotlib.pyplot as plt

original_set = -2 * np.random.rand(100, 2)
second_set = 1 + 2 * np.random.rand(50, 2)
original_set[50:100, :] = second_set

from sklearn.cluster import KMeans

kmean = KMeans(n_clusters=2)

kmean.fit(original_set)

print(kmean.cluster_centers_)

print(kmean.labels_)

import matplotlib.pyplot as plt

for i in set(kmean.labels_):
    index = kmean.labels_ == i
    plt.plot(original_set[index, 0], original_set[index, 1], "o")

plt.plot(kmean.cluster_centers_[0][0], kmean.cluster_centers_[0][1], "*", c="r", ms=10)
plt.plot(kmean.cluster_centers_[1][0], kmean.cluster_centers_[1][1], "*", c="r", ms=10)
plt.savefig("kmeans1.png")
plt.show()

sample = np.array([[-1.4, -1.4]])
print(kmean.predict(sample))

another_sample = np.array([[2.5, 2.5]])
print(kmean.predict(another_sample))


import numpy as np

import matplotlib.pyplot as plt
#%matplotlib inline

#import matplotlib.image as img
#import PIL.Image as Image
from PIL import Image
import math
import cmath

import time

import csv

from numpy import binary_repr

from fractions import gcd

class DFT(object):
    """
    This class DFT implements all the procedures for transforming a given 2D digital image
    into its corresponding frequency-domain image (Forward DFT Transform)
    """

    def __init__():
        pass

    @classmethod
    def __computeConjugate(self, mat):
        """
        Computes the conjugate of a complex square-matrix.

        Parameters
        ----------
        mat : ndarray
            Input matrix of complex numbers.

        Returns
        -------
        result : ndarray
            The conjugate of the input matrix.
        """

        N = mat.shape[0]
        result = np.zeros([N, N], dtype=np.complex)

        for i in range(N):
            for j in range(N):
                result[i, j] = (mat[i, j].real) - (mat[i, j].imag*1j)

        return result

    @classmethod
    def __multiplyMatrices(self, mat1, mat2):
        """
        Computes the multiplication of two complex square matrices.

        Parameters
        ----------
        mat1 : ndarray
            First input matrix of complex numbers.
        mat2 : ndarray
            Second input matrix of complex numbers.

        Returns
        -------
        result : ndarray
            The multiplication result of the two matrices.
        """

        N = mat1.shape[0]

        result = np.zeros([N, N], np.complex)
        #For each column and row...
        for i in range(N):
            row = mat1[i, :]
            for j in range(N):
                col = mat2[j, :]
                total = 0 + 0j
                for k in range(N):
                    total += row[k]*col[k]
                result[i, j] = total

        return result

    #Compute the two separable kernels for the forward DFT.
    @classmethod
    def computeXForwardKernel(self, size):
        """
        Computes/generates the first forward kernel function.

        Parameters
        ----------
        size : int
            Size of the kernel to be generated.

        Returns
        -------
        xKernel : ndarray
            The generated kernel as a matrix.
        """

        #Initialize the kernel
        xKernel = np.zeros([size, size], dtype=np.complex)

        #Compute each value of the kernel...
        for u in range(size):
            for x in range(size):

                #Rounding it is used here for making the values integers as it will insert very small fractions.
                xKernel[u, x] = math.cos((2*math.pi*u*x)/size) - (1j*math.sin((2*math.pi*u*x)/size))

        #Return the resulting kernel
        return xKernel

    @classmethod
    def computeYForwardKernel(self, xKernel):
        """
        Computes/generates the second forward kernel function.

        Parameters
        ----------
        xKernel : ndarray
            The first forward kernel function.

        Returns
        -------
        yKernel : ndarray
            The generated kernel as a matrix.
        """
        #yKernel = np.conj(xKernel) ## In numpy package.
        N = xKernel.shape[0]

        #For each value, find the conjugate...
        yKernel = np.zeros([N, N], dtype=np.complex)
        for i in range(N):
            for j in range(N):
                yKernel[i, j] = (xKernel[i, j].real) - (xKernel[i, j].imag*1j)

        # Return the resulting kernel (Since the original kernel is symmetric, transpose is not needed)
        return yKernel

    @classmethod
    def computeCenteredImage(self, imge):
        """
        Centers a given image.

        Parameters
        ----------
        imge : ndarray
            Input array that stores the image to be centered.

        Returns
        -------
        newImge : int
            The new and centered version of the input image.
        """

        #Compute the dimensions of the image
        M, N = imge.shape
        #centeringMatrix = np.zeros([M, N], dtype=int)
        newImge = np.zeros([M, N], dtype=int)
        for x in range(M):
            for y in range(N):
                newImge[x, y] = imge[x, y] * ((-1)**(x+y))

        #newImge = imge * centeringMatrix
        return newImge

    @classmethod
    def computeForward2DDFTWithSeparability(self, imge):
        """
        Computes/generates the 2D DFT by computing the two forward kernels first (Separability).

        Parameters
        ----------
        imge : ndarray
            The input image to be transformed.

        Returns
        -------
        final2DDFT : ndarray
            The transformed image.
        """

        N = imge.shape[0]
        xKernel = DFT.computeXForwardKernel(N)
        yKernel = DFT.computeYForwardKernel(xKernel)

        #row1DDFT = (1.0/size) * np.dot(xKernel, imge)
        intermediate2DDFT = (1.0/N) * DFT.__multiplyMatrices(xKernel, imge)
        final2DDFT = (1.0/N) * DFT.__multiplyMatrices(intermediate2DDFT, yKernel)

        return final2DDFT

    @classmethod
    def __computeSinglePoint2DFT(self, imge, u, v, N):
        """
        A private method that computes a single value of the 2DDFT from a given image.

        Parameters
        ----------
        imge : ndarray
            The input image.

        u : ndarray
            The index in x-dimension.

        v : ndarray
            The index in y-dimension.

        N : int
            Size of the image.

        Returns
        -------
        result : complex number
            The computed single value of the DFT.
        """
        result = 0 + 0j
        for x in xrange(N):
            for y in xrange(N):
                result += (imge[x, y] * (math.cos((2*math.pi*(u*x + v*y))/N) -
                                         (1j*math.sin((2*math.pi*(u*x + v*y))/N))))
        return result

    @classmethod
    def computeForward2DDFTNoSeparability(self, imge):
        """
        Computes/generates the 2D DFT by computing without separating the kernels.

        Parameters
        ----------
        imge : ndarray
            The input image to be transformed.

        Returns
        -------
        final2DDFT : ndarray
            The transformed image.
        """

        # Assuming a square image
        N = imge.shape[0]
        final2DDFT = np.zeros([N, N], dtype=np.complex)
        for u in xrange(N):
            for v in xrange(N):
                #Compute the DFT value for each cells/points in the resulting transformed image.
                final2DDFT[u, v] = DFT.__computeSinglePoint2DFT(imge, u, v, N)
        return ((1.0/(N**2))*final2DDFT)

    @classmethod
    def computeInverse2DDFTWithSeparability(self, dftImge):
        """
        Computes the inverse 2D DFT by computing the two inverse kernels first (Separability).

        Parameters
        ----------
        dftImge : ndarray
            The dft transformed image as input.

        Returns
        -------
        imge : ndarray
            The resulting image in spatial domain from the inverse DFT.
        """

        N = dftImge.shape[0]

        #Here the kernels are interchanged from the forward DFT
        yKernel = DFT.computeXForwardKernel(N)
        xKernel = DFT.computeYForwardKernel(yKernel)

        intermediateImge = DFT.__multiplyMatrices(xKernel, dftImge)
        imge = DFT.__multiplyMatrices(intermediateImge, yKernel)

        #imge = np.real(imge)

        return imge

    @classmethod
    def compute2DDFTFourierSpectrum(self, dftImge):
        """
        Computes the fourier spectrum of the transformed image.

        Parameters
        ----------
        dftImge : ndarray
            The input transformed image.

        Returns
        -------
        fourierSpect : ndarray
            The computed fourier spectrum.
        """
        N = dftImge.shape[0]

        fourierSpect = np.zeros([N, N], dtype=float)
        #Calculate the magnitude of each point(complex number) in the DFT image
        for i in xrange(N):
            for j in xrange(N):
                v = dftImge[i, j]
                fourierSpect[i, j] = math.sqrt((v.real)**2 + (v.imag)**2)
        return fourierSpect

    @classmethod
    def normalize2DDFTByLog(self, dftImge):
        """
        Computes the log transformation of the transformed DFT image to make the range
        of the fourier values b/n 0 to 255

        Parameters
        ----------
        dftImge : ndarray
            The input transformed image.

        Returns
        -------
        dftNormImge : ndarray
            The normalized version of the transformed image.
        """

        #Compute the fourier spectrum of the transformed image:
        dftFourierSpect = DFT.compute2DDFTFourierSpectrum(dftImge)

        #Normalize the fourier spectrum values:
        dftNormFourierSpect = (255.0/ math.log10(255)) * np.log10(1 + (255.0/(np.max(dftFourierSpect))*dftFourierSpect))

        return dftNormFourierSpect


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
import pandas as pd
from scipy import integrate
from scipy.optimize import curve_fit

# Write with LaTeX
rc('text', usetex=True)
rc('font', family='serif')

#Read .csv file
file1 = pd.read_csv('T_Compression-1.csv', header=None)
file2 = pd.read_csv('T_Compression-2.csv', header=None)

# Temperatures
t1 = file1[16][1:]
t1 = np.array([float(i) for i in t1])
t2 = file2[16][1:]
t2 = np.array([float(i) for i in t2])

# Volumes
v1 = file1[11][1:]
v1 = np.array([float(i)*0.1 for i in v1])
v2 = file2[11][1:]
v2 = np.array([float(i)*0.1 for i in v2])

# Pressure
p1 = file1[8][1:]
p1 = np.array([float(i)*10**3 for i in p1])
p2 = file2[8][1:]
p2 = np.array([float(i)*10**3 for i in p2])

# Logarithmic Axis
logp1 = np.log(p1)
logp2 = np.log(p2)

logv1 = np.log(v1)
logv2 = np.log(v2)

# Fitting
# The function we use to fit our data on
def func(x, a, b):
    return (a * x) + b

# popt1 = (a1,b1)
popt1, pcov1 = curve_fit(func, logv1, logp1)
# err1 = (δa1,δb1)
err1 = np.sqrt(np.diag(pcov1))

# popt2 = (a,b)
popt2, pcov2 = curve_fit(func, logv2, logp2)
# err2 = (δa2,δb2)
err2 = np.sqrt(np.diag(pcov2))

# Integrals (=work done)
I1 = integrate.trapz(p1, v1)
I2 = integrate.trapz(p2, v2)

# Plots
fig, ax = plt.subplots(2,2)

p1 = p1*0.001
v1 = v1*10**4
p2 = p2*0.001
v2 = v2*10**4

ax[0,0].scatter(logv1, logp1, color='black', marker=".")
ax[0,0].plot(logv1, func(logv1, *popt1), color = 'darkorange', label="$y=\\alpha_1 x + \\beta_1$")
ax[0,0].set_ylabel("$\\log P_1$")
ax[0,0].set_xlabel("$\\log V_1$")
ax[0,0].legend()
ax[0][0].set_title("First Compression")
ax[0,0].grid(b=True, which='major', color='#666666', linestyle='--')
ax[0,0].minorticks_on()
ax[0,0].grid(b=True, which='minor', color='#999999', linestyle='--', alpha=0.2)

ax[1,0].plot(v1, p1, color='seagreen')
ax[1,0].set_ylim((0,130))
ax[1,0].set_ylabel("$P_1$ $(\\mathrm{kPa})$")
ax[1,0].set_xlabel("$V_1$ $(10^{-4}\,\\mathrm{m}^3)$")
ax[1,0].set_title("First Compression")
ax[1,0].fill_between(v1, p1, color='seagreen', label="$W_1$", alpha=0.6)
ax[1,0].legend()
ax[1,0].grid(b=True, which='major', color='#666666', linestyle='--')
ax[1,0].minorticks_on()
ax[1,0].grid(b=True, which='minor', color='#999999', linestyle='--', alpha=0.2)

ax[0,1].scatter(logv2, logp2, color='black', marker=".")
ax[0,1].plot(logv2, func(logv2, *popt2), color = 'darkorange', label="$y=\\alpha_2 x + \\beta_2$")
ax[0,1].set_ylabel("$\\log P_2$")
ax[0,1].set_xlabel("$\\log V_2$")
ax[0,1].legend()
ax[0,1].set_title("Second Compression")
ax[0,1].grid(b=True, which='major', color='#666666', linestyle='--')
ax[0,1].minorticks_on()
ax[0,1].grid(b=True, which='minor', color='#999999', linestyle='--', alpha=0.2)

ax[1,1].plot(v2, p2, color='seagreen')
ax[1,1].set_ylim((0,130))
ax[1,1].set_ylabel("$P_2$ $(\\mathrm{kPa})$")
ax[1,1].set_xlabel("$V_2$ $(10^{-4}\,\\mathrm{m}^3)$")
ax[1,1].set_title("Second Compression")
ax[1,1].fill_between(v2, p2, color='seagreen', label="$W_2$", alpha=0.6)
ax[1,1].legend(loc="upper right")
ax[1,1].grid(b=True, which='major', color='#666666', linestyle='--')
ax[1,1].minorticks_on()
ax[1,1].grid(b=True, which='minor', color='#999999', linestyle='--', alpha=0.2)

fig.tight_layout()

plt.show()

# Print lines' slopes and constant terms
print(f"a_1 ± δa_1 = {popt1[0]} ± {err1[0]}, b_1 ± δb_1 = {popt1[1]} ± {err1[1]} \n")
print(f"a_2 ± δa_2 = {popt2[0]} ± {err2[0]}, b_2 ± δb_2 = {popt2[1]} ± {err2[1]} \n")
# Print mean temperature (=const)
print(f"T_1 = {np.mean(t1)} K, T_2 = {np.mean(t2)} K \n")
# Print work done
print(f"I_1 = {I1} J, I_2 = {I2} J")


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

def sigmoid(x):
    a = []
    for item in x:
        a.append(1 / (1 + math.exp(-item)))
    return a

x = np.arange(-10., 10., 0.2)
sig = sigmoid(x)
plt.plot(x, sig)
plt.show()


'''
    regress_linear - example of using SciPy stats linear regression technique
'''
from scipy import *
from pylab import *
from scipy import stats

n = 50
t = linspace(-5, 5, n)

a = 0.8; b = -4
x = polyval([a,b],t)
xn = x + randn(n)

(a_s, b_s, r_val, p_val, stderr) = stats.linregress(t, xn)
print('parameters: a=%.2f b=%.2f \nregression: a=%.2f b=%.2f, std error= %.3f' % (
        a,b,a_s,b_s,stderr))
print "R-squared", r_val**2
print "p-value", p_val


import numpy as np

class LogisticRegression:
    """
    Classification using logistic regression
    """

    def __init__(self,table,reg=False,lamda=0,degree = 1):
        """Initializes Class for Logistic Regression

        Parameters
        ----------
        table : ndarray(n-rows,m-features + 1)
            Numerical training data, last column as training labels
        reg : Boolean
            Set True to enable regularization, false by default
        degree: int
            Degree of polynomial to fit to the data. Default is 1.

        """
        # Regularization Parameters
        self.reg=reg
        self.lamda=lamda
        # training data
        self.degree=degree
        self.table = table
        # num of rows in the training data
        self.num_training = np.shape(table)[0]
        # map the features according to the degree of the fit
        self.X = self.map_features()
        self.num_features = np.shape(self.X)[1]
        # extract last column from training data table
        self.y = table[:,-1]
        # craete an array of parameters initialzing them to 1
        self.theta = np.zeros(self.num_features)

    def map_features(self):
        """
        Generates polynomial features based on the degree
        """
        X = self.table[:,0]
        Y = self.table[:,1]
        # First column is ones for calculation of intercept
        features = np.ones(self.num_training)

        for i in range(1,self.degree+1):
                for j in range(0,i+1):
                    col = np.power(X,i-j)*np.power(Y,j)
                    features = np.column_stack((features,col))
        return features

    @staticmethod
    def sigmoid(val):
        """Computes sigmoid function of input value

        Parameters
        ----------
        val : float
              Value of which sigmoid is to calculate
        Returns
        -------
        val : float
            Sigmoid value of the parameter

        """
        return float(1) / (1 + np.exp(-val))

    def compute_cost(self):
        """Computes cost based on the current values of the parameters

        Returns
        -------
        cost : float
            Cost of the selection of current set of parameters

        """
        hypothesis = LogisticRegression.sigmoid(np.dot(self.X, self.theta))
        #new ndarray to prevent intercept from theta array to be changed
        theta=np.delete(self.theta,0)
        #regularization term
        reg = (self.lamda/2*self.num_training)*np.sum(np.power(theta,2))
        cost = -(np.sum(self.y * np.log(hypothesis) + (1 - self.y) * (np.log(1 - hypothesis)))) / self.num_training
        #if regularization is true, add regularization term and return cost
        if self.reg:
            return cost + reg
        return cost

    def gradient_descent(self, num_iters=1000, alpha=0.01):
        """Runs the gradient descent algorithm

        Parameters
        ----------
        num_iters : int
            The maximum number of iterations allowed to run before the algorithm terminates
        alpha : float
            The learning rate for the algorithm

        Returns
        -------
        self.theta: ndarray(self.features)
            Array of parameters after running the algorithm
        """
        old_cost=0
        for i in range(0, num_iters):
            hypothesis = LogisticRegression.sigmoid(np.dot(self.X, self.theta))
            loss = hypothesis - self.y
            cost = self.compute_cost()
            #if the cost is equal for two iterations, break from the loop
            if cost==old_cost:
                break
            old_cost = cost
            #print "Iteration: %d Cost: %f" % (i, cost)
            gradient = np.dot(self.X.T, loss) / self.num_training
            #regularization term
            reg = (self.lamda/self.num_training)*self.theta
            if self.reg:
                self.theta = self.theta - alpha * (gradient+reg)
            else:
                self.theta = self.theta - alpha * gradient
        return self.theta

    def predict(self, data, prob=False):
        """Computes the logistic probability of being a positive example

        Parameters
        ----------
        data : ndarray (n-rows,n-features)
            Test data to score using the current weights
        prob : Boolean
            If set to true, probability will be returned, else binary classification
        Returns
        -------
        0 or 1: int
            0 if probablity is less than 0.5, else 1
        """
        data = np.column_stack((np.ones(data.shape[0]), data))

        hypothesis = LogisticRegression.sigmoid(np.dot(data, self.theta))
        if not prob:
            return np.where(hypothesis >= .5, 1, 0)
        return hypothesis

    def accuracy(self):
        """Calculates percentage of correct predictions by the model on training data

        Returns
        -------
        accuracy : float
            Percentage of correct predictions on the features of training data
        """
        #delete extra ones column that was added
        x = np.delete(self.X, 0, 1)
        predicted = self.predict(x)
        match = float(np.sum(self.y == predicted))
        return (match / self.num_training) * 100

    def plot_data(self):
        """Plot the training data in X array
        """
        from matplotlib import pyplot as plt
        plt.scatter(self.X[:, 1], self.X[:, 2], s=40, c=self.y, cmap=plt.cm.Spectral)
        plt.show()

    def plot_fit(self):
        """Plot the training data in X array along with decision boundary
        """
        from matplotlib import pyplot as plt
        x1 = np.linspace(self.table.min(), self.table.max(), 100)
        #reverse self.theta as it requires coeffs from highest degree to constant term
        x2 = np.polyval(np.poly1d(self.theta[::-1]),x1)
        plt.plot(x1, x2, color='r', label='decision boundary');
        plt.scatter(self.X[:, 1], self.X[:, 2], s=40, c=self.y, cmap=plt.cm.Spectral)
        plt.legend()
        plt.show()


"""
  Name     : c14_07_mvn_multiVariateNormalGraph.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""
import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#
# input area
n   = 60                      # number of intervals
x   = np.linspace(-3, 3, n)   # x dimention
y   = np.linspace(-3, 4, n)   # y dimention
x,y = np.meshgrid(x, y)       # grid
#
# Mean vector and covariance matrix
mu = np.array([0., 1.])
cov= np.array([[ 1. , -0.5], [-0.5,  1.5]])
#
# combine x and y into a single 3-dimensional array
pos = np.empty(x.shape + (2,))
pos[:, :, 0] = x
pos[:, :, 1] = y
#
def multiNormal(pos, mu, cov):
    n = mu.shape[0]
    Sigma_det = np.linalg.det(cov)
    Sigma_inv = np.linalg.inv(cov)
    n2 = np.sqrt((2*np.pi)**n * Sigma_det)
    fac = np.einsum('...k,kl,...l->...', pos-mu, Sigma_inv, pos-mu)
    return np.exp(-fac/2)/n2
#
z    = multiNormal(pos, mu, cov)
fig  = plt.figure()
ax   = fig.gca(projection='3d')
ax.plot_surface(x, y, z, rstride=3, cstride=3,linewidth=1, antialiased=True,cmap=cm.viridis)
cset = ax.contourf(x, y, z, zdir='z', offset=-0.15, cmap=cm.viridis)
ax.set_zlim(-0.15,0.2)
ax.set_zticks(np.linspace(0,0.2,5))
ax.view_init(27, -21)
plt.title("Bivariate normal distribtuion")
plt.ylabel("y values ")
plt.xlabel("x values")
plt.show()


"""
conceptualQ3.py

Plot the classification error, Gini index, and cross-entropy for a two-class
problem as p_m1 goes from 0 to 1
"""
import math
import numpy as np
import matplotlib.pyplot as plt
def classificationError(p_m1):
    """
    This is defined as E 1 - max_k{p_mk}
    """
    p_m2 = 1 - p_m1
    E = 1 - max(p_m1, p_m2)  # You could do E=min(p_m1, p_m2) instead
    return E

def giniIndex(p_m1):
    """
    G = sum_k { p_mk(1-p_mk }
    """
    G = p_m1*(1-p_m1)*2
    return G

def crossEntropy(p_m1):
    """
    D = - sum_k { p_mk*log(p_mk) }
    """
    p_m2 = 1 - p_m1
    D = - p_m1*math.log(p_m1) - p_m2*math.log(p_m2)
    return D

classificationErrors = []
ginis = []
crossEntropies = []
p_m1s = np.linspace(0.001,0.999)
for p_m1 in p_m1s:
    print(p_m1)
    classificationErrors.append(classificationError(p_m1))
    ginis.append(giniIndex(p_m1))
    crossEntropies.append(crossEntropy(p_m1))

plt.scatter(p_m1s, classificationErrors)
plt.scatter(p_m1s, ginis)
plt.scatter(p_m1s, crossEntropies)
plt.show()


from __future__ import division
import scipy as sp
import scipy.linalg as la

def hqr(A):
	"""Finds the QR decomposition of A using Householder reflectors.
	input: 	A, mxn array with m>=n
	output: Q, orthogonal mxm array
	        R, upper triangular mxn array
	        s.t QR = A
	"""
	R = A.copy()
	m,n = R.shape
	Q = sp.eye(m,m)
	for k in sp.arange(n-1):
		v = R[k:m,k].copy()
		v[0] += sp.sign(v[0])*la.norm(v)
		v = v/la.norm(v)
		v = v.reshape(m-k,1)
		P = sp.eye(m,m)
		P[k:m,k:m] -= 2*sp.dot(v,v.T)
		Q = sp.dot(P,Q)
		R = sp.dot(P,R)
	return Q.T,R

def hess(A):
	"""Computes the upper Hessenberg form of A using Householder reflectors.
	input:  A, mxn array
	output: Q, orthogonal mxm array
			H, upper Hessenberg
			s.t. QHQ' = A
	"""
	H = A.copy()
	m,n = H.shape
	Q = sp.eye(m,m)
	for k in sp.arange(n-2):
		v = H[k+1:m,k].copy()
		v[0] += sp.sign(v[0])*la.norm(v)
		v = v/la.norm(v)
		v = v.reshape(m-k-1,1)
		P = sp.eye(m,m)
		P[k+1:m,k+1:m] -= 2*sp.dot(v,v.T)
		Q = sp.dot(P,Q)
		H = sp.dot(P,sp.dot(H,P.T))
	return Q.T,H

def gqr(A):
	"""Finds the QR decomposition of A using Givens rotations.
	input: 	A, mxn array with m>=n
	output: Q, orthogonal mxm array
	        R, upper triangular mxn array
	        s.t QR = A
	"""
	def rotate(i,k,B):
	# create the Givens rotation matrix G to zero out the i,k entry of B
		c,s,r = solve(B[k,k],B[i,k])
		r = sp.sqrt(B[k,k]**2 + B[i,k]**2)
		c = B[k,k]/r
		s = -B[i,k]/r
		G = sp.eye(m)
		G[i,i] = c
		G[k,k] = c
		G[k,i] = -s
		G[i,k] = s
		return G

	B = A.copy()
	m,n = B.shape
	G = sp.eye(m)
	#cycle through each nonzero subdiagonal element of B, and rotate it to zero
	for k in sp.arange(n-1):
		for i in sp.arange(k+1,m):
			if B[i,k] is not 0:
				H = rotate(i,k,B)
				B = sp.dot(H,B)
				G = sp.dot(H,G)
	return G.T, B


import matplotlib.pylab as plt
import numpy as np
from matplotlib import cm
import scipy.optimize

def steepest_descent(x, f, grad_f, hessian_f):
    return -grad_f(x)

def newton(x, f, grad_f, hessian_f):
    A = hessian_f(x)
    return -np.linalg.solve(A, grad_f(x))

def backtracking(f, grad_f, x, p):
    alpha = 1.0
    c = 1e-4
    rho = 0.5
    f_x = f(x)
    c_grad_f_x_p = c*grad_f(x).dot(p)
    while f(x + alpha*p) > f_x + alpha * c_grad_f_x_p:
        alpha = rho * alpha
    return alpha

def scipy_line_search(f, grad_f, x, p):
    return scipy.optimize.line_search(f, grad_f, x, p)[0]

def minimise(f, grad_f, hessian_f, x0, tol=1e-5, max_iter=1000, search_direction=steepest_descent, line_search=backtracking):
    x = np.copy(x0)
    alpha = 1.0
    p = np.zeros_like(x)
    p[0] = 1.
    i = 0
    array_x = np.empty((max_iter+1, len(x)), dtype=float)
    array_alpha = np.empty(max_iter, dtype=float)
    array_x[0, :] = x
    while np.linalg.norm(alpha * p) > tol and i < max_iter:
        p = search_direction(x, f, grad_f, hessian_f)
        alpha = line_search(f, grad_f, x, p)
        x += alpha * p
        array_x[i+1, :] = x
        array_alpha[i] = alpha
        i += 1
    return array_x[:i,:], array_alpha[:i]

def rosenbrock(x, y):
    return (1 - x)**2 + (y - x**2)**2

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

def grad_rosenbrock_2d(x):
    return np.array([
        4*x[0] * (x[0]**2 - x[1]) + 2*x[0] - 2,
        -2*x[0]**2 + 2*x[1]
    ])

def hessian_rosenbrock_2d(x):
    return np.array([
        [12 * x[0]**2 - 4*x[1] + 2, -4*x[0]],
        [-4*x[0], 2],
    ])

# verify grad and hessian using finite differences
h = 1e-5
x0 = np.array([0., 0.])
x0_x = np.array([h, 0.])
x0_y = np.array([0., h])
fd_grad_rosenbrock = np.array([
    (rosenbrock_2d(x0_x) - rosenbrock_2d(x0)) / h,
    (rosenbrock_2d(x0_y) - rosenbrock_2d(x0)) / h,
])
np.testing.assert_almost_equal(fd_grad_rosenbrock, grad_rosenbrock_2d(x0), decimal=4)
fd_hessian_rosenbrock = np.vstack((
    (grad_rosenbrock_2d(x0_x) - grad_rosenbrock_2d(x0)) / h,
    (grad_rosenbrock_2d(x0_y) - grad_rosenbrock_2d(x0)) / h,
))
np.testing.assert_almost_equal(fd_hessian_rosenbrock, hessian_rosenbrock_2d(x0),
                               decimal=4)

# plot rosenbrock surface
x = np.linspace(-1.5, 2.0, 100)
y = np.linspace(0.2, 3.0, 100)
X, Y = np.meshgrid(x, y)
Z = np.vectorize(rosenbrock)(X, Y)

fig = plt.figure()
ax = fig.gca(projection='3d')
fig2 = plt.figure()
ax2 = fig2.add_subplot(1,1,1)

surf = ax.plot_surface(X, Y, Z, alpha=0.5, cmap=cm.coolwarm)

# pick two initial starting points
x01 = np.array([1.2, 1.2])
x02 = np.array([-1.2, 1])

# run all methods and plot results
for x0 in [x01, x02]:
    for p_method in [steepest_descent, newton]:
        for ls_method in [backtracking, scipy_line_search]:
            label = '{}-{}'.format(p_method.__name__, ls_method.__name__)
            pos, alpha = \
                minimise(rosenbrock_2d, grad_rosenbrock_2d, hessian_rosenbrock_2d, x0,
                         search_direction=p_method, line_search=ls_method)
            print('method', label, 'finished in', len(alpha), 'iterations')
            xs = pos[:, 0]
            ys = pos[:, 1]
            ax.plot(xs, ys, rosenbrock(xs, ys), label=label, linewidth=2)
            ax2.plot(alpha, label=label)
ax2.legend()
plt.show()


"""Helper functions for linear algebra."""
import numpy as np

def modified_gram_schmidt(v):
    """Orthonormalise vectors using the modified Gram--Schmidt process.

    Parameters
    ----------
    v : (n, m) ndarray
        horizontally stacked vectors of dimension n. It is assumed that m <= n.

    Returns
    -------
    (n, m) ndarray
        orthonormalised vectors such that span(w) = span(v)

    """
    w = np.empty(v.shape)
    for k in range(v.shape[1]):
        w[:, k] = v[:, k]
        for j in range(k):
            w[:, k] -= np.dot(w[:, j], v[:, k]) * w[:, j]
        w[:, k] /= np.linalg.norm(w[:, k])
    return w

def projector_onto_kernel(a):
    """Compute the orthogonal projection matrix P onto the kernel of A.

    This function uses an SVD-based method described in Golub.

    If all rows of A are linearly independent,
    you can probably use a simpler/faster method.

    Parameters
    ----------
    a : (n, m) ndarray
        input matrix A. It is assumed that n <= m

    Returns
    -------
    (m, m) ndarray
        orthogonal projector onto the kernel of A

    """
    r = np.linalg.matrix_rank(a)
    _, _, v = np.linalg.svd(a)
    v_tilde = v.T[:, r:]
    return v_tilde @ v_tilde.T

def online_variance(iterator, item_shape):
    """Compute the elementwise variance in one pass using an online algo.

    See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance

    Parameters
    ----------
    iterator : iterator
        an iterator yielding ndarrays of the same shape

    item_shape : tuple of ints
        a shape specification as a tuple

    Returns
    -------
    ndarray
        elementwise variance of the items in the iterator

    """
    n = 0
    mean = np.zeros(item_shape)
    m2 = np.zeros(item_shape)
    for item in iterator:
        n += 1
        delta = item - mean
        mean += delta/n
        delta2 = item - mean
        m2 += delta*delta2

    if n < 2:
        return float('nan')
    else:
        return m2 / n

def log_rel_error(testee, reference):
    """Compute the log relative error of a vector to some reference.

    Be careful if reference is near zero!

    Parameters
    ----------
    testee : (n,) ndarray
        an inexact result

    reference : (n,) ndarray
        an exact reference

    Returns
    -------
    float
        log relative error, i.e. log10(||testee - reference|| / ||reference||)

    """
    res = np.log10(np.linalg.norm(testee - reference))
    res -= np.log10(np.linalg.norm(reference))
    return res


# encoding='utf-8'

'''
/**
 * This is the solution of No. 72 problem in the LeetCode,
 * the website of the problem is as follow:
 * https://leetcode-cn.com/problems/edit-distance/
 *

 * The description of problem is as follow:
 * ==========================================================================================================
 * 给定两个单词 word1 和 word2，计算出将 word1 转换成 word2 所使用的最少操作数 。
 *

 * 你可以对一个单词进行如下三种操作：
 *

 * 插入一个字符
 * 删除一个字符
 * 替换一个字符
 * 示例 1:
 *

 * 输入: word1 = "horse", word2 = "ros"
 * 输出: 3
 * 解释:
 * horse -> rorse (将 'h' 替换为 'r')
 * rorse -> rose (删除 'r')
 * rose -> ros (删除 'e')
 * 示例 2:
 *

 * 输入: word1 = "intention", word2 = "execution"
 * 输出: 5
 * 解释:
 * intention -> inention (删除 't')
 * inention -> enention (将 'i' 替换为 'e')
 * enention -> exention (将 'n' 替换为 'x')
 * exention -> exection (将 'n' 替换为 'c')
 * exection -> execution (插入 'u')
 *

 * 来源：力扣（LeetCode）
 * * ==========================================================================================================
 * @author zhangyu (zhangyuyu417@gmail.com)
 */
'''
import numpy as np

class Solution:
    def min_distance(self, word1: str, word2: str) -> int:
        '''
            得到最小步数
        Args:
            word1: 字符串1
            word2:字符串2
        Returns:
            最小步数
        '''
        m = len(word1)
        n = len(word2)
        if m * n == 0:
            return n + m
        d = np.zeros((m + 1, n + 1), dtype=np.int)
        for i in range(m + 1):
            d[i][0] = i
        for j in range(n + 1):
            d[0][j] = j
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                left = d[i - 1][j] + 1
                down = d[i][j - 1] + 1
                left_down = d[i - 1][j - 1]
                if word1[i - 1] != word2[j - 1]:
                    left_down += 1
                d[i][j] = min(left, min(down, left_down))
        return d[n][m]

if __name__ == '__main__':
    word1 = "horse"
    word2 = "ros"
    solution = Solution()
    result = solution.min_distance(word1, word2)
    print(result)
    assert result == 3


"""
In this script, I write useful method for Skill Set Challenge Hudson & Thames
"""

import numpy as np

def compute_log_returns(prices):
    """
    Compute log returns for each ticker.

    Parameters
    ----------
    prices : DataFrame
        Prices for each ticker and date

    Returns
    -------
    log_returns : DataFrame
        Log returns for each ticker and date
    """

    log_returns = np.log(prices/prices.shift(1))[1:]

    return log_returns

def convert_to_distance_matrix(correlation_matrix):
    """
    Compute pair-wise distances using correlation coefficents

    References:
    ----------
        - Hierarchical Structure in Financial Markets
        (https://www.researchgate.net/publication/46462363_Hierarchical_Structure_in_Financial_Markets)

    Parameters
    ----------
    correlation_matrix : DataFrame
        correlation for every pairs of stocks.

    Returns
    -------
    distance_matrix : DataFrame
        distance matrix for every pairs of stocks.
    """

    distance_matrix = np.sqrt(2*(1-correlation_matrix))

    return distance_matrix

def get_condensed_distance_matrix(matrix):
    """
    Basically, This returns upper triangular part of square matrix(without diagonals)
    So, we can use this method to get condensed distance matrix.

    Parameters
    ----------
    matrix: numpy.ndarray
        It should be square matrix

    Returns
    -------
    condensed_distance: numpy.ndarray
        condensed distance matrix which 1-d array. If matrix has NxN shape, The lenght
        of return should be N*(N-1)/2

    """
    x_indices, y_indices = np.triu_indices_from(matrix, k=1)
    condensed_distance = matrix[x_indices.tolist(), y_indices.tolist()]
    return condensed_distance


import numpy as np
from matplotlib import pyplot as plt

def softplus(x, beta=1):
    return (1 / beta) * np.log(1 + np.exp(beta * x))

def save_relu_and_softplus_plot(figure_path: str):
    x = np.linspace(-4, 4, 100)

    # the function, which is y = e^x here
    y_relu = np.maximum(0, x)
    y_softplus = np.log(1 + np.exp(x))
    y_softplus_beta_2 = softplus(x, beta=2)
    y_softplus_beta_5 = softplus(x, beta=5)

    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.spines["left"].set_position("center")
    ax.spines["bottom"].set_position("zero")
    ax.spines["right"].set_color("none")
    ax.spines["top"].set_color("none")
    ax.xaxis.set_ticks_position("bottom")
    ax.yaxis.set_ticks_position("left")

    # plot the function
    plt.plot(x, y_relu, "r", label="ReLU(x)")
    plt.plot(x, y_softplus, "b", label=r"Softplus(x), $\beta=1$")
    plt.plot(x, y_softplus_beta_2, "orange", label=r"Softplus(x), $\beta=2$")
    plt.plot(x, y_softplus_beta_5, "g", label=r"Softplus(x), $\beta=5$")
    plt.legend(loc="upper left")

    # show the plot
    plt.savefig(
        figure_path,
        transparent=True,
    )
    plt.show()

if __name__ == "__main__":
    fig_path = "/home/steffi/dev/master_thesis/images/relu_softplus_plot.png"
    save_relu_and_softplus_plot(fig_path)


# -*- coding: utf-8 -*-
"""
Created on Fri Apr 13 23:04:11 2018

@author: Marcus
"""

import numpy as np
import time

def my_ifft(x):
    N = len(x)
    y = np.zeros(N, dtype=np.complex)
    if (N is 1):
        return x
    else:
        Wn = np.exp(1j * 2 * np.pi / N)
        W = 1
        x_even = x[0::2]
        x_odd = x[1::2]
        y_even = my_ifft(x_even)
        y_odd = my_ifft(x_odd)
        for m in range(N//2):
            y[m] = y_even[m] + (W * y_odd[m])
            y[m + N//2] = y_even[m] - (W * y_odd[m])
            W *= Wn
        return y

def my_ifft_adjusted(x):
    return my_ifft(x) / len(x)

length = 2**14
samples = np.random.normal(0, 1, length) + 1j*np.random.normal(0, 1, length)

a0 = time.clock()
my_result = my_ifft_adjusted(samples)
a1 = time.clock()
da = a1 - a0

b0 = time.clock()
np_result = np.fft.ifft(samples)
b1 = time.clock()
db = b1 - b0

print("My IFFT took:", da, "seconds")
print("NP IFFT took:", db, "seconds")
print("NP IFFT is", da/db, "times faster")
print(np.allclose(my_result, np_result))


import numpy
import scipy.spatial as spatial
import pylab

# Calcul le cout du clustering courant
def compute_cost(X, C, assign):
    n = len(X)
    d = len(X[0])
    acc = 0.
    for i in range(n):
        acc += numpy.linalg.norm(X[i] - C[assign[i]]) ** 2
    return acc

# Assigne à chaque point de X le n° du point de C dont il est le plus proche
def ppcentroide(X, C):
    mat_dist = spatial.distance.cdist(X, C, "euclidean")
    return numpy.argmin(mat_dist, axis=1)

# Algorithme kmeans
def kmeans(X, k, n_iter):
    n = len(X)
    d = len(X[0])

    cost = numpy.inf

    A = [i for i in range(n)]
    B = []
    for i in range(k):
        B.append(A.pop(numpy.random.randint(0,n-i)))
    C = [X[i] for i in B]

    for iter in range(n_iter):
        assign = ppcentroide(X, C)
        for i_k in range(k):
            cluster = [X[i] for i in range(n) if assign[i] == i_k]
            C[i_k] = numpy.mean(cluster, axis = 0)
        cost = compute_cost(X, C, assign)
        print(iter, cost)
    assign = ppcentroide(X, C)
    return C, assign, cost

# Zone d'interface

# Zone de tests

###
def read_data(file_name, X):
    f = open(file_name, 'r')
    data = f.readlines()
    for line in data:
        X.append((map(float, filter(lambda x: x <> '\n', line.split("\t")))))
    f.close()

def write_cluster(file_name, assign, n_cluster, n_iter, s):
    n = len(assign)
    f = open(file_name, 'w')
    f.write("n_cluster = " + str(n_cluster) + "\n")
    f.write("n_iter = " + str(n_iter) + "\n")
    f.write("n_data = " + str(n) + "\n")
    f.write("seed = " + str(s) + "\n")
    for i in range(n):
        f.write(str(assign[i]) + "\n")
    f.close()
###

filename = 'cinq_classes1'

X = []
read_data('./data/' + filename + '/' + filename + '.txt',X)

s = 2574927556214
n = len(X)
k = 5
n_iter = 30

numpy.random.seed(s)
C, assign, cost = kmeans(X, k, n_iter)

pylab.figure()
colors = "rbgkmc"
for i_k in range(k):
    pylab.plot([X[i][0] for i in range(n) if assign[i] == i_k],
               [X[i][1] for i in range(n) if assign[i] == i_k],
               colors[i_k] + ".")
    pylab.plot(C[i_k][0], C[i_k][1], colors[i_k] + "o")
pylab.show()

write_cluster('./data/' + filename + '/kmeans.txt', assign, k, n_iter, s)


import scipy.stats
import numpy as np

import chi_squared_goodness_of_fit

def chi_squared_homogeneity(*args, statistic: str = "likelihood_ratio"):
    """Chi-squared test for homogeneity.

    Checks whether all data groups follows the same data distribution.

    Assumptions:
        None.

    Test statistic:
        The same statistics from the Chi-squared test for goodness of
        fit. Check `chi_squared_goodness_of_fit.py` documentation for
        more specific info.

    Null distribution: Y ~ Chi^2((group_num - 1) * (bins_per_group - 1))
    Where:
        Y can be either G or X^{2} test statistic (again, check Chi-
        squared goodness of fit documentation for more info.);
        Chi^2 is the Chi^2 distribution.

    H0: all groups follows the same data distribution.
    HA: At least one group does not follow the same data distribution
        as the remaining groups.
    """
    assert len(set(map(len, args))) == 1

    args = np.asfarray(args)

    num_groups, bins_per_group = args.shape

    group_size = np.sum(args, axis=1)
    null_dist_pmf = np.sum(args, axis=0) / np.sum(args)

    expected_counts = np.outer(group_size, null_dist_pmf)

    ddof = (
        num_groups + bins_per_group - 1
    )  # df = (num_groups - 1) * (bins_per_group - 1)

    return chi_squared_goodness_of_fit.chi_squared_goodness_of_fit(
        data_counts=args.ravel(),
        expected_counts=expected_counts.ravel(),
        ddof=ddof,
        statistic=statistic,
    )

def _test():
    n = 500
    p = 0.7
    group_num = 4
    x = np.arange(n * p - 5, n * p + 6)

    expected_counts = n * scipy.stats.binom(n, p).pmf(x)
    group_counts = [
        np.ceil(np.maximum(0.0, expected_counts + np.random.randint(-20, 20)))
        for _ in range(group_num)
    ]
    group_counts[0][1] *= 4
    group_counts[1][2] *= 1.2

    print(expected_counts)

    res = chi_squared_homogeneity(*group_counts, statistic="pearson_chi_square")
    res_scipy = scipy.stats.chi2_contingency(
        np.asfarray(group_counts), correction=False
    )[:2]
    print(res, res_scipy)
    assert np.allclose(res, res_scipy)

    res = chi_squared_homogeneity(*group_counts, statistic="likelihood_ratio")
    print(res)
    assert np.allclose(
        res,
        scipy.stats.chi2_contingency(
            np.asfarray(group_counts), correction=False, lambda_="log-likelihood"
        )[:2],
    )

if __name__ == "__main__":
    _test()


import numpy as np

def compute_assign_prob_TS(alpha_1, beta_1, alpha_2, beta_2, num_sims):
    """
    Return assign prob for action 1 and action 2
    """
    action_1_count = 0
    action_2_count = 0
    for i in range(num_sims):
        draw_1 = np.random.beta(alpha_1, beta_1)
        draw_2 = np.random.beta(alpha_2, beta_2)
        if draw_1 > draw_2:
            action_1_count +=1
        else:
            action_2_count +=1

    return action_1_count/num_sims, action_2_count/num_sims #arm 1 prob, arm2 prob

def compute_assign_prob_TS_2(alphas: list, betas: list, num_sims: int, versions: list):
    """
    Return assign probability for all versions
    """
    draw = np.random.beta(alphas, betas, (num_sims, len(alphas)))
    arms = list(np.argmax(draw, axis=1))
    return dict({'probability_{}'.format(versions[i]): arms.count(i) / num_sims for i in range(len(alphas))})


from abc import ABC
from math import pi

import numpy as np
import statsmodels.stats.weightstats as ws
import statsmodels.tsa.stattools as ts
from scipy.stats import ttest_ind

class HypothesisTest(ABC):
    """
    Abstract base class for hypothesis tests.
    """
    alpha = 0.05

    def run(self, name: str, **kwargs):
        """Run test

        Args:
            name (str): Name of test to be run

        Returns:
            (None):
        """
        self.name = name

        test = getattr(self, name)

        self.stat, self.pval = test(**kwargs)
        self.accept_h0 = (self.pval > self.alpha)

class TwoSampleTest(HypothesisTest):
    def __init__(self, control: np.array, treatment: np.array):
        self.control = control
        self.treatment = treatment

    def ind_t(self, **kwargs):
        """Calculate the T-test for the means of two independent samples of scores.
        The null-hypothesis (H0) is: mean(control) <= mean(treatment)
        The alternative hypothesis (H1) is: mean(control) > mean(treatment)
        This test provides the probability to see the sample's test statistic or
        something that is even less in line with the null hypothesis (H0)
        under the H0: P(T<=0|H0), where T is the Welch's_t-test statistic [4, 2].

        Necessary Assumptions [1]:
            * 1. The means of the two populations being compared should follow
                normal distributions. Under weak assumptions, this follows in large
                samples from the central limit theorem, even when the distribution
                of observations in each group is non-normal.[18]

            * 2. If using Student's original definition of the t-test, the two
                populations being compared should have the same variance
                (testable using F-test, Levene's test, Bartlett's test, or the
                Brown–Forsythe test; or assessable graphically using a Q–Q plot).
                If the sample sizes in the two groups being compared are equal,
                Student's original t-test is highly robust to the presence of unequal
                variances. Welch's t-test is insensitive to equality of the
                variances regardless of whether the sample sizes are similar.

            * 3. The data used to carry out the test should either be sampled
                independently from the two populations being compared or be fully
                paired. This is in general not testable from the data, but if the
                data are known to be dependent (e.g. paired by test design), a
                dependent test has to be applied. For partially paired data, the
                classical independent t-tests may give invalid results as the test
                statistic might not follow a t distribution, while the dependent
                t-test is sub-optimal as it discards the unpaired data

            Most two-sample t-tests are robust to all but large deviations from the assumptions

        Args:
            control (Iterable): Iterable containing average to be tested
            treatment (Iterable): Iterable containing average to be tested
            pval_threshold (float, optional): p-value threshold. Defaults to 0.05.

        Returns:
            (float, float): Test statistic and p-value

        Example:
            >>> np.random.seed(1) # seed the random number generator
            >>> control = 5 * np.random.randn(100) + 50
            >>> treatment = 5 * np.random.randn(100) + 51
            >>> test = TwoSampleTest(control, treatment)
            >>> test.run("ind_t")
            >>> test.name
            'ind_t'
            >>> test.stat
            -2.2620139704259556
            >>> test.pval
            0.024782819014639627
            >>> test.accept_h0
            False

        References:
            [1] https://en.wikipedia.org/wiki/Student%27s_t-test
            [2] https://stats.stackexchange.com/questions/491294/can-you-multiply-p-values-if-you-perform-the-same-test-multiple-times
            [3] https://stackoverflow.com/questions/15984221/how-to-perform-two-sample-one-tailed-t-test-with-numpy-scipy
            [4] https://en.wikipedia.org/wiki/Welch%27s_t-test
            [5] https://machinelearningmastery.com/how-to-code-the-students-t-test-from-scratch-in-python/
        """
        return ttest_ind(self.control, self.treatment, **kwargs)

    def ind_z(self, **kwargs):
        """Do a independent z-test to verify if the difference of a mean between treatment and control
        is statistically significant. The need for independent test is because the variance of control
        and treatment are not necessarily equal.

        Necessary Assumptions [3]:
            * Nuisance parameters should be known, or estimated with high accuracy
            (an example of a nuisance parameter would be the standard deviation in a one-sample location test).
            Z-tests focus on a single parameter, and treat all other unknown parameters as being fixed at their
            true values. In practice, due to Slutsky's theorem, "plugging in" consistent estimates of
            nuisance parameters can be justified. However if the sample size is not large enough for these
            estimates to be reasonably accurate, the Z-test may not perform well.

            * The test statistic should follow a normal distribution. Generally, one appeals to the central limit
            theorem to justify assuming that a test statistic varies normally. There is a great deal of
            statistical research on the question of when a test statistic varies approximately normally.
            If the variation of the test statistic is strongly non-normal, a Z-test should not be used.

        Args:
            control (Iterable): Iterable containing average to be tested
            treatment (Iterable): Iterable containing average to be tested
            pval_threshold (float, optional): p-value threshold. Defaults to 0.05.

        Returns:
            (float, float): Test statistic and p-value

        Example:
            >>> np.random.seed(1) # seed the random number generator
            >>> control = 5 * np.random.randn(100) + 50
            >>> treatment = 5 * np.random.randn(100) + 51
            >>> test = TwoSampleTest(control, treatment)
            >>> test.run("ind_z")
            >>> test.name
            'ind_z'
            >>> test.stat
            -2.2620139704259556
            >>> test.pval
            0.02369654016419
            >>> test.accept_h0
            False

        References:
            [1] https://stats.stackexchange.com/questions/124096/two-samples-z-test-in-python
            [2] https://stackoverflow.com/questions/65235896/statsmodel-z-test-not-working-as-intended-statsmodels-stats-weightstats-compare
            [3] https://en.wikipedia.org/wiki/Z-test
            [4] https://stats.stackexchange.com/questions/491294/can-you-multiply-p-values-if-you-perform-the-same-test-multiple-times
        """
        col1 = ws.DescrStatsW(self.control)
        col2 = ws.DescrStatsW(self.treatment)

        cm_obj = ws.CompareMeans(col1, col2)

        return cm_obj.ztest_ind(**kwargs)

class SingleSampleTest(HypothesisTest):
    def __init__(self, data: np.array):
        self.data = data

    def stationarity(self, **kwargs):
        """[summary]

        Returns:
            [type]: [description]

        Example:
            >>> x = np.arange(365)
            >>> data = np.sin(pi*x/60)
            >>> test = SingleSampleTest(data)
            >>> test.run("stationarity")
            >>> test.name
            'stationarity'
            >>> test.stat
            -7612364038365.818
            >>> test.pval
            0.0
            >>> test.accept_h0
            False
        """

        stat, pval, _, _, critical_vals, _ = ts.adfuller(self.data, **kwargs)

        return stat, pval

import numpy,from scipy,import statsmodels


import numpy as np
from Project_Clean_data import raw
from Project_Clean_data import header
from matplotlib.pyplot import figure, plot, title, xlabel, ylabel, show, legend, hold, subplot, xticks, yticks
from scipy.linalg import svd
import matplotlib.pyplot as plt

classDict = {'Negative': 0, 'Positive': 1 }
classNames = classDict.keys()
Dx = list(header).index('Dx')
y = raw[:, Dx]
C = len(classNames)

raw = np.delete(raw,list(header).index('Dx'), 1)
header = np.delete(header,list(header).index('Dx'), 0)

X = raw

N = raw.shape[0]

# Subtract mean value from data
Y = (X - np.ones((N,1))*X.mean(0))

# PCA by computing SVD of Y
U,S,V = svd(Y,full_matrices=False)

V = V.T

# Project the centered data onto principal component space
Z = np.dot(Y, V)
# Z = Y * V

# Number of PCAS to plot
k = 4

print(V.shape)
print(len(header))
print(V[:,1].T)
for i in range(k):
    plt.plot(range(len(V[:, i])), V[:, i], '--')
    print('The min of PCA{} corresponds to the attribute {}'.format(i + 1, header[np.argmin(V[:, i])]))
    print('The max of PCA{} corresponds to the attribute {}'.format(i + 1, header[np.argmax(V[:, i])]))
plt.title('Amount of Variation per Principal Component')
legend(['PCA1', 'PCA2', 'PCA3', 'PCA4'])
xlabel('Attributes')
ylabel('Amount of variation')
plt.show()


"""
Compute PCA on a dataset of multivariate time series represented as 3-dimensional tensor
and reduces the size along the third dimension:
    [N, T, V] --> [N, T, D]
with D <= V.
The input dataset must be a 3-dimensional tensor with shapes
- N: number of observations
- T: number of time steps in the time series
- V: number of variables in the time series

"""

import numpy as np
import numpy.linalg as linalg

class tensorPCA:

    def __init__(self, n_components):
        self.n_components=n_components
        self.first_eigs = None

    def fit(self, X): # X has shape [N,T,V]

        if len(X.shape) is not 3:
            raise RuntimeError('Input must be a 3d tensor')

        Xt = np.swapaxes(X,1,2)  # [N,T,V] --> [N,V,T]
        Xm = np.expand_dims(np.mean(X, axis=0), axis=0) # mean sample
        Xmt = np.swapaxes(Xm,1,2)

        C = np.tensordot(X-Xm,Xt-Xmt,axes=([1,0],[2,0])) / (X.shape[0]-1) # covariance of 0-mode slices

        # sort eigenvalues of covariance matrix
        eigenValues, eigenVectors = linalg.eig(C)
        idx = eigenValues.argsort()[::-1]
        eigenVectors = eigenVectors[:,idx]

        self.first_eigs = eigenVectors[:,:self.n_components]

    def transform(self, X):
        return np.einsum('klj,ji->kli',X,self.first_eigs)

    def fit_transform(self, X):
        self.fit(X)
        return self.transform(X)


from math import sqrt, exp, log, pi
from scipy.stats import norm
import datetime
import numpy as np

#  Option parameters
sigma = 0.15
S = 586.08    # current stock price
K = 585.0   # strike price
r = 0.01   # FED Fund's Rate  .25
t = (datetime.date(2014,10,18) - datetime.date(2014,9,8)).days / 365. / 365.0   # in years
c0 = 1.50   # call price

def d(sigma, S, K, r, t):
    d1 = 1 / (sigma * sqrt(t)) * (log(S/K) + (r + sigma**2/2) * t)
    d2 = d1 - sigma * sqrt(t)
    return d1, d2

def call_price(sigma, S, K, r, t, d1, d2):
    C = norm.cdf(d1) * S - norm.cdf(d2) * K * exp(-r * t)
    return C

# print(d(0.15, S, K, r, t))
# d1, d2 = (d(0.15, S, K, r, t))
# print(call_price(sigma, S, K, r, t, d1, d2))

#  Tolerances
tol = 1e-3
epsilon = 1

count = 0
max_iteration = 1000

vol = 0.50  # initial guess to get things started

while epsilon > tol:
    count += 1
    # print(count)
    if count >= max_iteration:
        print('Breaking on count')
        break;

    orig_vol = vol

    d1, d2 = d(vol, S, K, r, t)
    function_value = call_price(vol, S, K, r, t, d1, d2) - c0

    vega = S * norm.pdf(d1) * sqrt(t)   #  vega == the derivative
    vol = -function_value / vega + vol

    epsilon = abs((vol - orig_vol) / orig_vol)  # epsilon ==

print('sigma = ', vol)
print(count, 'iterations')

# PUTS
def f(x):
    return np.exp(x) - 5 * x

def fddx(x):
    return np.exp(x) - 5

# def find_vol(target_value, call_put, S, K, T, r):
#     MAX_ITERATIONS = 250
#     PRECISION = 1.0e-3

#     sigma = 0.5
#     for i in range(0, MAX_ITERATIONS):
#         price = bs_price(call_put, S, K, T, r, sigma)
#         vega = bs_vega(call_put, S, K, T, r, sigma)

#         price = price
#         diff = target_value - price  # our root

#         print(i, sigma, diff)

#         if (abs(diff) < PRECISION):
#             return sigma
#         sigma = sigma + diff/vega # f(x) / f'(x)

#     # value wasn't found, return best guess so far
#     return sigma

# n = norm.pdf
# N = norm.cdf

# def bs_price(cp_flag,S,K,T,r,v,q=0.0):
#     d1 = (log(S/K)+(r+v*v/2.)*T)/(v*sqrt(T))
#     d2 = d1-v*sqrt(T)
#     if cp_flag == 'c':
#         price = S*exp(-q*T)*N(d1)-K*exp(-r*T)*N(d2)
#     else:
#         price = K*exp(-r*T)*N(-d2)-S*exp(-q*T)*N(-d1)
#     return price

# def bs_vega(cp_flag,S,K,T,r,v,q=0.0):
#     d1 = (log(S/K)+(r+v*v/2.)*T)/(v*sqrt(T))
#     return S * sqrt(T)*n(d1)

# V_market = 17.5
# K = 585
# t = (datetime.date(2014,10,18) - datetime.date(2014,9,8)).days / 365.
# S = 586.08
# r = 0.0002
# c0 = 'c' # call option

# implied_vol = find_vol(V_market, c0, S, K, t, r)

# print('Implied vol: %.2f%%' % (implied_vol * 100))

# print('Market price = %.2f' % V_market)
# print('Model price = %.2f' % bs_price(c0, S, K, t, r, implied_vol))


#--------------------------------
#SIMPLE UNIVARIABLE REGRESSION EXAMPLE
#--------------------------------

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

#------------------------
#GENERATE TOY DATA
#------------------------

#GROUND TRUTH FUNCTION
def G(x):
	GT=2
	if(GT==1):
		out=400000.0*np.exp(-((x-0.0)/2.0)**2.0)
		out=out+150000.0*np.exp(-((x-15.0)/1.0)**2.0)
		out=out+100000
	if(GT==2):
		out=400000.0*np.exp(-((x+2.0)/4.0)**4.0)
		out=out+250000.0*np.exp(-((x-5.0)/1.0)**4.0)
		out=out+150000.0*np.exp(-((x-15.0)/1.0)**4.0)
		out=out+100000
	return out

#NOISY DATA
N=100; xmin=-10; xmax=10
x = np.linspace(xmin,xmax,N)
y = G(x)  #PRISTINE DATA
noise=np.random.normal(loc=0.0, scale=0.05*(max(y)-min(y)),size=len(x))
# noise=0.15*(max(y)-min(y))*np.random.uniform(-1,1,size=len(x))
yn = y+ noise

#GROUND TRUTH DATA
xe = np.linspace(xmin,2*xmax,int(2*N))
ye = G(xe)

#NORMALIZE
xe=(xe-np.mean(x))/np.std(x)
ye=(ye-np.mean(y))/np.std(y)
yn=(yn-np.mean(y))/np.std(y)

x=(x-np.mean(x))/np.std(x)
y=(y-np.mean(y))/np.std(y)

#------------------------
#FIT MODEL
#------------------------

##FITTING MODEL
def model(x,p1,p2,p3,p4,p5,p6,p7):
    return p1*np.exp(-((x-p2)/p3)**2.0)+p4*np.exp(-((x-p5)/p6)**2.0)+p7
po=[0.1,0.1,0.1,0.1,0.1,0.1,0.1]
popt, pcov = curve_fit(model, x, yn,po) #,[0.1,0.1,0.1])

#------------------------
#PLOT
#------------------------

fig, ax = plt.subplots()
ax.plot(x, yn, 'o', label='Data')
ax.plot(xe, ye, '-', label='Ground-Truth')
ax.plot(xe, model(xe, *popt), 'r-', label="Model")

ax.legend()
FS=18   #FONT SIZE
plt.xlabel('Distance (miles)', fontsize=FS)
plt.ylabel('House Price ($) ', fontsize=FS)

plt.show()


import math

import numpy as np
import pandas as pd

from .time_series import TimeSeries

def affine(
    start,
    end,
    freq,
    start_y,
    end_y,
):
    index = pd.date_range(start=start, end=end, freq=freq)
    return TimeSeries(index=index, y=np.linspace(start_y, end_y, len(index)))

def constant(
    start,
    end,
    freq,
    value,
):
    return affine(start, end, freq, value, value)

def cosine(
    start,
    end,
    freq,
    amp=1,
    period=1,
):
    index = pd.date_range(start=start, end=end, freq=freq)
    return TimeSeries(
        index=index,
        y=amp * np.cos(np.linspace(0, math.tau * period, num=len(index))),
    )

def sine(
    start,
    end,
    freq,
    period=1,
):
    index = pd.date_range(start=start, end=end, freq=freq)
    return TimeSeries(
        index=index,
        y=np.sin(np.linspace(0, math.tau * period, num=len(index))),
    )


import numpy as np
import abc

class BaseFilter(abc.ABC):

    @abc.abstractmethod
    def predict_and_update(self, new_observation):
        pass

    def filter_list(self, array):
        """Filters a list of values using the current filter"""

        filtered_values = [self.predict_and_update(
            observation) for observation in array]

        return filtered_values

class KalmanFilter(BaseFilter):
    def __init__(self, A=1, B=1,C=1, Q=4, R=0.008) -> None:
        """

        First Order Kalman Filter Implementation
        Parameters:
        previous_mean: previous mean state
        previous_var : previous variance state
        A: state vector
        B: control vector
        C: control vector
        Q: measurement noise
        R: process noise """

        self.previous_mean = None
        self.previous_var = None
        self.A = A
        self.B = B
        self.C = C
        self.Q = Q
        self.R = R

        np.random.seed(3) # to ensure constant results for evaluation

    def predict_and_update(self, new_observation):
        """
        Prediction and update in Kalman filter

        Parameters:
        new_observation: current observation

        Returns:
        new_mean: mean state prediction
        new_var: variance state prediction
        """

        if self.previous_mean is None:
            self.previous_mean = ( 1/ self.C) * new_observation
            self.previous_var = (1/ self.C) * self.Q * (1/self.C)
            return new_observation
        else:
            x_mean = self.A * self.previous_mean + self.B * np.random.normal(0, self.R, 1)
            P_mean = (self.A * self.previous_var * self.A )+ self.R

            #kalman gain
            K = P_mean * self.C * (1 / ((self.C * P_mean * self.C) + self.Q))

            new_mean = x_mean + K * (new_observation - (self.C * x_mean))
            new_var = P_mean - (K * self.C * P_mean)

            self.previous_mean = new_mean
            self.previous_var = new_var

            return new_mean[0]

class BasicFilter(BaseFilter):

    def __init__(self) -> None:
        self.previous_observation = None

    def predict_and_update(self, new_observation):
        if self.previous_observation is None:
            self.previous_observation = new_observation
            return new_observation

        predicted_observation = new_observation * \
            0.1 + self.previous_observation * 0.9
        self.previous_observation = predicted_observation
        return predicted_observation

class MovingMeanFilter(BaseFilter):
    def __init__(self) -> None:
        self.n = 10
        self.buffer = []

    def predict_and_update(self, new_observation):
        self.buffer.append(new_observation)

        prediction = np.mean(self.buffer)

        if len(self.buffer) >= self.n:
            self.buffer.pop(0)

        return prediction

class MovingMedianFilter(BaseFilter):
    def __init__(self) -> None:
        self.n = 10
        self.buffer = []

    def predict_and_update(self, new_observation):
        self.buffer.append(new_observation)

        prediction = np.median(self.buffer)

        if len(self.buffer) >= self.n:
            self.buffer.pop(0)

        return prediction


# Assignment on Binomial Distribution

import numpy as np
from scipy.stats import binom

'''
1.For a Binomial Distribution parameter n=5 and p=0.3. Find the probabilities of getting
a)At least 3 successes
b)At most 3 successes
c)Exactly 3 failures
'''
print("Assignment 1")
print("At least 3 successes:",1-binom.cdf(k=2,n=5,p=0.3))
print("At most 3 successes:",binom.cdf(k=3,n=5,p=0.3))
print("Excatly 3 failures",binom.pmf(k=5-3,n=5,p=0.3))
print("\n")

'''
2.If on an average one vessel in every ten is wrecked, find the probability that out of five vessels expected to arrive, four at least will arrive safely
'''
print("Assignment 2")

print("Probabilty of atleast 4/5 arrive safely",
        binom.pmf(k=4,n=5,p=9/10)+binom.pmf(k=5,n=5,p=9/10))
print("\n")

'''
3.Five coins are tossed 3,200 times.
a)Find the Frequencies of the distribution of heads and tabulate the results
b)Calculate the mean number of success and standard deviations
'''

print("Assignment 3")
print("Frequency Distribution of Heads")
for k in np.arange(5+1):
    print("Frequency of {} Heads is {}"\
    .format(k,3_200*binom.pmf(k=k,n=5,p=1/2)))
print("Mean of Success",binom.mean(n=5,p=1/2))
print("Standard Deviation of Success",binom.std(n=5,p=1/2))


from statistics import mean
import numpy as np
import random
import matplotlib.pyplot as plt

def create_dataset(hm, variance, step = 2, correlation = False):
    val = 1
    ys = []

    for i in range(hm):
        y = val + random.randrange(-variance, variance)
        ys.append(y)

        if correlation and correlation == 'pos':
            val += step
        elif correlation and correlation == 'neg':
            val -= step
    xs = [i for i in range(len(ys))]

    return np.array(xs, dtype = np.float64), np.array(ys, dtype = np.float64)

def best_fit_slope_and_intercept(xs, ys):
    m = (   (   (mean(xs) * mean(ys)) - mean(xs*ys)   ) /
        (       (mean(xs)*mean(xs)) - mean(xs**2)     )     )
    b = mean(ys) - m * mean(xs)
    return m, b

def squared_error(ys_orig, ys_line):
    return sum( (ys_line - ys_orig) ** 2 )

def coefficient_of_determination(ys_orig, ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_regr = squared_error(ys_orig, ys_line)
    squared_error_y_mean = squared_error(ys_orig, y_mean_line)
    return 1 - (squared_error_regr / squared_error_y_mean)

plt.style.use('ggplot') #set plot style

#set example data
    #xs = np.array([1,2,3,4,5,6], dtype = np.float64)
    #ys = np.array([5,4,6,5,6,7], dtype = np.float64)
xs, ys = create_dataset(40, 20, 2, correlation = 'pos')

#calcucale reg line and slope
m, b = best_fit_slope_and_intercept(xs, ys)
regression_line = [(m * x) + b for x in xs]

#predict
predict_x = 8
predict_y = (m * predict_x) + b

#coef
r_squared = coefficient_of_determination(ys, regression_line)

print("m = ", m)
print("Coef:", r_squared)
#draw plot
plt.scatter(xs, ys) #display x and y data
plt.scatter(predict_x, predict_y, s = 50, color = 'g') #display the prediction
plt.plot(xs, regression_line) #display reg line
plt.show()


from typing import Union

import numpy as np
import talib
from jesse.indicators.ma import ma
from jesse.indicators.mean_ad import mean_ad
from jesse.indicators.median_ad import median_ad

from jesse.helpers import get_candle_source
from jesse.helpers import slice_candles

def zscore(candles: np.ndarray, period: int = 14, matype: int = 0, nbdev: float = 1, devtype: int = 0, source_type: str = "close",
           sequential: bool = False) -> Union[float, np.ndarray]:
    """
    zScore

    :param candles: np.ndarray
    :param period: int - default: 14
    :param matype: int - default: 0
    :param nbdev: float - default: 1
    :param source_type: str - default: "close"
    :param sequential: bool - default=False

    :return: float | np.ndarray
    """
    candles = slice_candles(candles, sequential)

    source = get_candle_source(candles, source_type=source_type)
    means = ma(source, period=period, matype=matype, sequential=True)

    if devtype == 0:
       sigmas = talib.STDDEV(source, period) * nbdev
    elif devtype == 1:
       sigmas = mean_ad(source, period, sequential=True) * nbdev
    elif devtype == 2:
       sigmas = median_ad(source, period, sequential=True) * nbdev

    zScores = (source - means) / sigmas

    return zScores if sequential else zScores[-1]


import numpy as np
import sys

class Solve:
	def __init__(self, coefficient_matrix, rhs_vector):
		'''
			Do the initialization of coef. matrix, rhs-vector,
			solution-matrix and transformation matrix
		'''
		self.coefficient_matrix = coefficient_matrix
		self.rhs_vector = rhs_vector
		self.degree = rhs_vector.shape[0]
		self.solution_matrix = np.zeros((self.degree,1))
		self.transformation_matrix = np.eye(self.degree)

	def solve(self):
		'''
			Solve the linear system here by elimination
		'''

		for j in range(self.degree):
			pivot = self.coefficient_matrix[j][j]

			# In case pivot is equal to 0, exchange the rows
			if pivot == 0:
				for k in range(j+1, self.degree):
					if self.coefficient_matrix[k][j] != 0:
						transformation_matrix_t = np.eye(self.degree)
						status = True
						temp_1 = np.copy(transformation_matrix_t[k])
						temp_2 = np.copy(transformation_matrix_t[j])
						transformation_matrix_t[j] = temp_1
						transformation_matrix_t[k] = temp_2
						self.coefficient_matrix = np.matmul(transformation_matrix_t, self.coefficient_matrix)
						self.transformation_matrix = np.matmul(transformation_matrix_t, self.transformation_matrix)
						break

			pivot = self.coefficient_matrix[j][j]

			'''
				If we can't get pivot non-zero number by exchanging the rows, the system
				does not have unique finite solution
			'''
			if pivot == 0:
				print("The system of equations does not have unique and finite solution.")
				sys.exit()

			# Transforms coefficient matrix to upper triangular matrix by doing Gaussian elimination
			# Transformation matrix stores the net transformation the coefficient matrix goes through
			for i in range(j+1,self.degree):
				if self.coefficient_matrix[i][j] == 0:
					continue
				transformation_matrix_t = np.eye(self.degree)
				transformation_matrix_t[i][j] = - self.coefficient_matrix[i][j]/pivot
				self.transformation_matrix = np.matmul(transformation_matrix_t,self.transformation_matrix)
				self.coefficient_matrix = np.matmul(transformation_matrix_t, self.coefficient_matrix)

		# Transforming rhs_vector with transformation_matrix
		self.rhs_vector = np.matmul(self.transformation_matrix, self.rhs_vector)

		# Solving for the last variable by doing pivot division
		self.solution_matrix[-1][0] = self.rhs_vector[-1][0]/self.coefficient_matrix[-1][-1]

		# Solving for the rest of the variables
		for i in range(2,self.solution_matrix.shape[0]+1):
			temp = np.copy(self.coefficient_matrix[-i])
			temp[-i] = 0
			temp_product = np.dot(temp, self.solution_matrix[:,0])
			self.solution_matrix[-i][0] = (self.rhs_vector[-i][0]-temp_product)/self.coefficient_matrix[-i][-i]

def main():
	solve = Solve(np.array([[4,2,0],[2,1,0],[5,3,9]]), np.array([[1],[3],[7]]))
	solve.solve()
	print(solve.solution_matrix)

if __name__ == "__main__":
	main()


import numpy as np

def getEulerAngles(R):
    """
    Obtain the Euler angles corresponding to the rotation matrix R
    IMPORTANT: This function assumes all the angles are within the range -90 to 90 degrees!!
    """
    rx = -np.arcsin(R[1, 2])
    ry = np.arctan2(R[0, 2], R[2, 2])
    rz = np.arctan2(R[1, 0], R[1, 1])

    return rz, rx, ry

def vectorToYawElevation(vector, unit='deg'):
    """ Convert a vector to two angle representation by building the rotation matrix where the input vector is z axis.
        The input vector become the z axis, x axis is kept in the XZ plan of the coordinate system, on the left of the
        z axis. The y axis is deduced from the two firsts.
        The output angles are:
            - the first angle is the euler's yaw, called "yaw" or "phi" (rotation around y);
            - the second is the inverse euler's pitch, called "elevation" or "theta" (rotation around x). """
    vector = np.resize(vector, (1, 3))[0]

    R = [0.0, 0.0, 0.0]
    R[2] = vector / np.linalg.norm(vector)
    R[0] = np.cross([0.0, 1.0, 0.0], R[2])
    R[1] = np.cross(R[2], R[0])

    roll, pitch, yaw = getEulerAngles(np.array(R).T)
    if unit == 'deg':
        yaw = yaw / np.pi*180
        elevation = -pitch / np.pi*180
    elif unit == 'rad':
        elevation = -pitch
    else:
        raise Exception('Unknow angle unit ' + unit)

    return [yaw, elevation]

def yawElevationToVector(yawElevation, unit='deg'):
    """ Transforms an angular representation of gaze (phi, theta) into a vectorial one (x,y,z).
        WARNING: angles outer than [-90, 90] will give wrong vectors """
    # TODO: generalize with matrix rotation
    yawElevation = map(float, yawElevation)
    if unit == 'deg':
        yaw, elevation = yawElevation[0]/180*np.pi, yawElevation[1]/180*np.pi
        if np.abs(yaw) >= 90 or np.abs(elevation) >= 90:
            raise Exception('Can not handle angles outer [-90, 90]')
    elif unit == 'rad':
        yaw, elevation = yawElevation
        if np.abs(yaw) >= np.pi/2 or np.abs(elevation) >= np.pi/2:
            raise Exception('Can not handle angles outer [-90, 90]')
    else:
        raise Exception('Unknow angle unit ' + unit)

    y = np.sin(elevation)

    x = np.sqrt((np.cos(elevation)**2 * np.tan(yaw)**2) / (1 + np.tan(yaw)**2))
    if yaw < 0:  # The signed of x is taken from the sign of the phi
        x = -x

    if x**2 + y**2 >= 1.0:
        z = 0.0
    else:
        z = np.sqrt(1 - x**2 - y**2)
    if np.isnan(z):
        print '[yawElevationToVector]', np.array(yawElevation).flatten()*180/np.pi, [x, y, z]

    return np.resize([x, y, z], (3, 1))

def angleBetweenVectors_deg(x, y):
    """ Angle in degree between x and y vectors """
    x = np.resize(x.copy(), (3, 1))
    y = np.resize(y.copy(), (3, 1))
    angle = np.arccos(np.dot(x.T, y) / (np.linalg.norm(x) * np.linalg.norm(y))) / np.pi * 180
    return np.linalg.norm(angle)


import numpy as np
from math import exp

def barnes(x):

    a1 = 75.196
    a3 = 0.12694
    a5 = 1.0345e-5
    a7 = 0.030234
    a9 = 3.5256e-5
    a11 = 0.25645
    a13 = 1.3514e-5
    a15 = -5.2375e-6
    a17 = 7.0e-10
    a19 = -1.6638e-6
    a21 = 0.0005
    a2 = -3.8112
    a4 = -2.0567e-3
    a6 = -6.8306
    a8 = -1.28134e-3
    a10 = -2.266e-7
    a12 = -3.4604e-3
    a14 = -28.106
    a16 = -6.3e-8
    a18 = 3.4054e-4
    a20 = -2.8673

    x1 = x[0]
    x2 = x[1]
    y1 = x1*x2
    y2 = y1*x1
    y3 = x2**2
    y4 = x1**2

    # --- function value ---

    f = a1 + a2*x1 + a3*y4 + a4*y4*x1 + a5*y4**2 + \
        a6*x2 + a7*y1 + a8*x1*y1 + a9*y1*y4 + a10*y2*y4 + \
        a11*y3 + a12*x2*y3 + a13*y3**2 + a14/(x2+1) + \
        a15*y3*y4 + a16*y1*y4*x2 + a17*y1*y3*y4 + a18*x1*y3 + \
        a19*y1*y3 + a20*exp(a21*y1)

    # --- constraints ---

    c = np.zeros(3)
    c[0] = 1 - y1/700.0
    c[1] = y4/25.0**2 - x2/5.0
    c[2] = (x1/500.0- 0.11) - (x2/50.0-1)**2

    # --- derivatives of f ---

    dy1 = x2
    dy2 = y1 + x1*dy1
    dy4 = 2*x1
    dfdx1 = a2 + a3*dy4 + a4*y4 + a4*x1*dy4 + a5*2*y4*dy4 + \
        a7*dy1 + a8*y1 + a8*x1*dy1 + a9*y1*dy4 + a9*y4*dy1 + a10*y2*dy4 + a10*y4*dy2 + \
        a15*y3*dy4 + a16*x2*y1*dy4 + a16*x2*y4*dy1 + a17*y3*y1*dy4 + a17*y3*y4*dy1 + a18*y3 + \
        a19*y3*dy1 + a20*exp(a21*y1)*a21*dy1

    dy1 = x1
    dy2 = x1*dy1
    dy3 = 2*x2
    dfdx2 = a6 + a7*dy1 + a8*x1*dy1 + a9*y4*dy1 + a10*y4*dy2 + \
        a11*dy3 + a12*x2*dy3 + a12*y3 + a13*2*y3*dy3 + a14*-1/(x2+1)**2 + \
        a15*y4*dy3 + a16*y4*y1 + a16*y4*x2*dy1 + a17*y4*y1*dy3 + a17*y4*y3*dy1 + a18*x1*dy3 + \
        a19*y3*dy1 + a19*y1*dy3 + a20*exp(a21*y1)*a21*dy1

    dfdx = np.array([dfdx1, dfdx2])

    # --- derivatives of c ---

    dcdx = np.zeros((3, 2))
    dcdx[0, 0] = -x2/700.0
    dcdx[0, 1] = -x1/700.0
    dcdx[1, 0] = 2*x1/25**2
    dcdx[1, 1] = -1.0/5
    dcdx[2, 0] = 1.0/500
    dcdx[2, 1] = -2*(x2/50.0-1)/50.0

    dcdx = np.transpose(dcdx)  # matlab format

    return f/30.0, c, dfdx/30.0, dcdx

if __name__ == '__main__':

    from opt import fmincon

    # --- name of function to optimize ----
    function = 'example.barnes'
    providegradients = True

    # -------- starting point and bounds --------------
    x0 = np.array([10.0, 10.0])
    ub = np.array([65.0, 70.0])
    lb = np.array([0.0, 0.0])

    # ---- set options ----
    options = {'Algorithm': 'active-set', 'AlwaysHonorConstraints': 'bounds',
        'display': 'iter-detailed', 'MaxIter': 1000, 'MaxFunEvals': 10000,
        'TolCon': 1e-6, 'TolFun': 1e-6, 'Diagnostics': 'on'}

    # --- load fmincon and run ----
    xopt, fopt, exitflag, output = fmincon(function, x0, lb, ub, options,
        providegradients=providegradients)

    # --- print results
    print xopt
    print fopt
    print exitflag
    # print output


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# downloading dataset
#https: // s3 - api.us - geo.objectstorage.softlayer.net / cf - courses - data / CognitiveClass / ML0101ENv3 / labs / china_gdp.csv

df = pd.read_csv("china_gdp.csv")
df.head(10)

plt.figure(figsize=(8,5))
x_data, y_data = (df["Year"].values, df["Value"].values)
X = np.arange(-5.0, 5.0, 0.1)
Y = 1.0 / (1.0 + np.exp(-X))

def sigmoid(x, Beta_1, Beta_2):
    y = 1 / (1 + np.exp(-Beta_1 * (x - Beta_2)))
    return y

beta_1 = 0.10
beta_2 = 1990.0

#logistic function
Y_pred = sigmoid(x_data, beta_1 , beta_2)

#plot initial prediction against datapoints

xdata =x_data/max(x_data)
ydata =y_data/max(y_data)

from scipy.optimize import curve_fit
popt, pcov = curve_fit(sigmoid, xdata, ydata)
#print the final parameters
print(" beta_1 = %f, beta_2 = %f" % (popt[0], popt[1]))

x = np.linspace(1960, 2015, 55)
x = x/max(x)
plt.figure(figsize=(8,5))
y = sigmoid(x, *popt)
plt.plot(xdata, ydata, 'ro', label='data')
plt.plot(x,y, linewidth=3.0, label='fit')
plt.legend(loc='best')
plt.ylabel('GDP')
plt.xlabel('Year')
plt.show()

msk = np.random.rand(len(df)) < 0.8
train_x = xdata[msk]
test_x = xdata[~msk]
train_y = ydata[msk]
test_y = ydata[~msk]

popt, pcov = curve_fit(sigmoid, train_x, train_y)

y_hat = sigmoid(test_x, *popt)

print("Mean absolute error: %.2f" % np.mean(np.absolute(y_hat - test_y)))
print("Residual sum of squares (MSE): %.2f" % np.mean((y_hat - test_y) ** 2))
from sklearn.metrics import r2_score
print("R2-score: %.2f" % r2_score(y_hat , test_y) )


# -*- coding: utf-8 -*-
"""
1D Wasserstein barycenter demo

@author: rflamary
"""

import numpy as np
import matplotlib.pylab as pl
import ot
from mpl_toolkits.mplot3d import Axes3D #necessary for 3d plot even if not used
from matplotlib.collections import PolyCollection

#%% parameters

n=100 # nb bins

# bin positions
x=np.arange(n,dtype=np.float64)

# Gaussian distributions
a1=ot.datasets.get_1D_gauss(n,m=20,s=5) # m= mean, s= std
a2=ot.datasets.get_1D_gauss(n,m=60,s=8)

# creating matrix A containing all distributions
A=np.vstack((a1,a2)).T
nbd=A.shape[1]

# loss matrix + normalization
M=ot.utils.dist0(n)
M/=M.max()

#%% plot the distributions

pl.figure(1)
for i in range(nbd):
    pl.plot(x,A[:,i])
pl.title('Distributions')

#%% barycenter computation

alpha=0.2 # 0<=alpha<=1
weights=np.array([1-alpha,alpha])

# l2bary
bary_l2=A.dot(weights)

# wasserstein
reg=1e-3
bary_wass=ot.bregman.barycenter(A,M,reg,weights)

pl.figure(2)
pl.clf()
pl.subplot(2,1,1)
for i in range(nbd):
    pl.plot(x,A[:,i])
pl.title('Distributions')

pl.subplot(2,1,2)
pl.plot(x,bary_l2,'r',label='l2')
pl.plot(x,bary_wass,'g',label='Wasserstein')
pl.legend()
pl.title('Barycenters')

#%% barycenter interpolation

nbalpha=11
alphalist=np.linspace(0,1,nbalpha)

B_l2=np.zeros((n,nbalpha))

B_wass=np.copy(B_l2)

for i in range(0,nbalpha):
    alpha=alphalist[i]
    weights=np.array([1-alpha,alpha])
    B_l2[:,i]=A.dot(weights)
    B_wass[:,i]=ot.bregman.barycenter(A,M,reg,weights)

#%% plot interpolation

pl.figure(3,(10,5))

#pl.subplot(1,2,1)
cmap=pl.cm.get_cmap('viridis')
verts = []
zs = alphalist
for i,z in enumerate(zs):
    ys = B_l2[:,i]
    verts.append(list(zip(x, ys)))

ax = pl.gcf().gca(projection='3d')

poly = PolyCollection(verts,facecolors=[cmap(a) for a in alphalist])
poly.set_alpha(0.7)
ax.add_collection3d(poly, zs=zs, zdir='y')

ax.set_xlabel('x')
ax.set_xlim3d(0, n)
ax.set_ylabel('$\\alpha$')
ax.set_ylim3d(0,1)
ax.set_zlabel('')
ax.set_zlim3d(0, B_l2.max()*1.01)
pl.title('Barycenter interpolation with l2')

pl.show()

pl.figure(4,(10,5))

#pl.subplot(1,2,1)
cmap=pl.cm.get_cmap('viridis')
verts = []
zs = alphalist
for i,z in enumerate(zs):
    ys = B_wass[:,i]
    verts.append(list(zip(x, ys)))

ax = pl.gcf().gca(projection='3d')

poly = PolyCollection(verts,facecolors=[cmap(a) for a in alphalist])
poly.set_alpha(0.7)
ax.add_collection3d(poly, zs=zs, zdir='y')

ax.set_xlabel('x')
ax.set_xlim3d(0, n)
ax.set_ylabel('$\\alpha$')
ax.set_ylim3d(0,1)
ax.set_zlabel('')
ax.set_zlim3d(0, B_l2.max()*1.01)
pl.title('Barycenter interpolation with Wasserstein')

pl.show()


# coding: utf-8

import numpy as np

# y - activation of the unit given input z
# dy_dz - derivative of the activation y of the unit with respect to the input z

class Sigmoid(object):

    @staticmethod
    def y(z):
        return 1 / (1 + np.exp(-z))

    @staticmethod
    def dy_dz(y):
        return y * (1. - y)

class Softmax(object):

    @staticmethod
    def y(z):
        # Subtract maximum for numerical stability. (does not affect outputs)
        y = np.exp(z - z.max(axis=1)[:,np.newaxis])
        y /= y.sum(axis=1)[:,np.newaxis]
        return y

class Linear(object):

    @staticmethod
    def y(z):
        return z

    @staticmethod
    def dy_dz(y):
        return 1.

class RectifiedLinear(object):

    @staticmethod
    def y(z):
        return np.maximum(z, 0.)

    @staticmethod
    def dy_dz(y):
        return y > 0.

class Tanh(object):

    @staticmethod
    def y(z):
        return np.tanh(z)

    @staticmethod
    def dy_dz(y):
        return 1. - y**2


""" Basic operations on numpy arrays (addition, etc.) are elementwise

This works on arrays of the same size. Nevertheless, It’s also possible to do operations on arrays of different sizes if NumPy can transform these arrays so that they all have the same size: this conversion is called broadcasting. """

import numpy as np

a = np.tile(np.arange(0, 40, 10), (3,1))
""" array([[ 0, 10, 20, 30],
       [ 0, 10, 20, 30],
       [ 0, 10, 20, 30]]) """

a=a.T
""" array([[ 0,  0,  0],
       [10, 10, 10],
       [20, 20, 20],
       [30, 30, 30]]) """

c = np.tile(np.arange(0, 40, 10), (3,2))
""" array([[ 0, 10, 20, 30,  0, 10, 20, 30],
       [ 0, 10, 20, 30,  0, 10, 20, 30],
       [ 0, 10, 20, 30,  0, 10, 20, 30]]) """

b = np.array([0, 1, 2])
# array([0, 1, 2])

a + b # b is replicated as many required for a element wise addition
""" array([[ 0,  1,  2],
       [10, 11, 12],
       [20, 21, 22],
       [30, 31, 32]]) """

# ---------------------------

one_dim_array = np.arange(0, 40, 10)
# array([ 0, 10, 20, 30])
one_dim_array.shape
# (4,)

# convert one_dim_array to two_dim_array

two_dim_array = one_dim_array[:, np.newaxis]  # adds new axis -> 2D array
""" array([[ 0],
       [10],
       [20],
       [30]]) """

two_dim_array.shape
# (4, 1)


#!/usr/bin/env python

"""
A convergence test of the multigrid solver.  We solve Laplace's equation.  Here, we
solve

u_xx = sin(x)
u = 0 on the boundary [0,1]

The analytic solution is u(x) = -sin(x) + x sin(1)

we run at a variety of resolutions and compare to the analytic solution.

"""
#from io import *
import numpy as np
import multigrid
import matplotlib.pyplot as plt

def true(x):
    # the analytic solution
    return -np.sin(x) + x*np.sin(1.0)

def error(myg, r):
    # L2 norm of elements in r, multiplied by dx to normalize
    return np.sqrt(myg.dx*np.sum((r[myg.ilo:myg.ihi+1]**2)))

def f(x):
    # the righthand side
    return np.sin(x)

def mgsolve(nx):

    # create the multigrid object
    a = multigrid.CellCenterMG1d(nx, xl_BC_type="dirichlet", xr_BC_type="dirichlet",
                                 verbose=0)

    # initialize the solution to 0
    a.init_zeros()

    # initialize the RHS using the function f
    a.init_RHS(f(a.x))

    # solve to a relative tolerance of 1.e-11
    a.solve(rtol=1.e-11)

    # get the solution
    v = a.get_solution()

    # compute the error from the analytic solution
    return error(a.soln_grid, v - true(a.x))

N = [16, 32, 64, 128, 256, 512]
err = []

for nx in N:
    err.append(mgsolve(nx))

N = np.array(N, dtype=np.float64)
err = np.array(err)

plt.scatter(N, err, color="r")
plt.plot(N, err[len(N)-1]*(N[len(N)-1]/N)**2, color="k", label=r"$\mathcal{O}(\Delta x^2)$")

print(N, err[0]*(N[0]/N)**2)

ax = plt.gca()
ax.set_xscale('log')
ax.set_yscale('log')

plt.ylim(1.e-8, 1.e-3)

plt.xlabel("N")
plt.ylabel("L2 norm of absolute error")

plt.legend(frameon=False)

plt.savefig("mg-converge.png")