# This file implements polynomial regression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from sklearn.metrics import mean_squared_error

#Set number of samples and seed
NUM_SAMPLES = 100
np.random.seed(42)

# Our `True' function
def f(x):
    return 7 *(x**3 -1.3 *x**2+0.5*x - 0.056)

# initialize sample data
data = np.array([[x,f(x) ] for x in np.random.random(NUM_SAMPLES)])

# grid of coordinates for true function
gridx = np.linspace(0, 1, NUM_SAMPLES)
gridy = np.array([f(x) for x in gridx])

datax = data[:,0]
normaly = data[:,1]+0.3*np.random.randn(NUM_SAMPLES)

#Plot sampled data points
plt.scatter(datax, normaly )
plt.title("Scatter plot of synthetic data with normal errors")
plt.plot(gridx, gridy, label = "True function", color = 'Red')
plt.legend(loc = 2)
plt.savefig("poly_scatter_normal.png")
plt.cla()

gen_poly = True
# Run polynomial regression repeatedly for increasing degrees
if gen_poly:
    lm = LinearRegression()
    for deg in range(1, 8):
        poly = PolynomialFeatures(degree = deg)
        newdatax = poly.fit_transform(datax.reshape(NUM_SAMPLES,1))
        for i in range(1, NUM_SAMPLES+1):
            lm.fit(newdatax[:i], normaly[:i].reshape(i, 1))
            predictions = lm.predict(newdatax)
            mse = mean_squared_error(predictions, normaly.reshape(NUM_SAMPLES,1))

            #Plot everything
            plt.ylim(-0.75, 1.25)
            plt.scatter(datax, normaly)
            plt.title("Degree {} polynomial regression on {} points with normal error".format(deg, i))
            plt.plot(gridx, gridy, label = "True function", color = 'Red')
            gridpred = lm.predict(poly.fit_transform(gridx.reshape(NUM_SAMPLES, 1)))
            plt.plot(gridx.flatten(), gridpred.flatten(), label = "Polynomial regressor curve MSE = {:0.4f}".format(mse), color = 'Green')
            plt.legend(loc = 2)
            plt.savefig("polyreg_normal_{:02d}{:03d}.png".format(deg,i))
            plt.cla()

# Run degree 10 polynomial regression repeatedly using a random sample of 30 points
gen_var = True
if gen_var:
    lm = LinearRegression()
    poly = PolynomialFeatures(degree = 10)
    newdatax = poly.fit_transform(datax.reshape(NUM_SAMPLES,1))
    for i in range(30):
        samp = np.random.choice(range(NUM_SAMPLES), 30)
        lm.fit(newdatax[samp], normaly[samp].reshape(30, 1))
        predictions = lm.predict(newdatax)
        mse = mean_squared_error(predictions, normaly.reshape(NUM_SAMPLES,1))

        #Plot everything
        plt.ylim(-0.75, 1.25)
        plt.scatter(datax, normaly)
        plt.title("Degree {} polynomial regression on 30 random points with normal error".format(10))
        plt.plot(gridx, gridy, label = "True function", color = 'Red')
        gridpred = lm.predict(poly.fit_transform(gridx.reshape(NUM_SAMPLES, 1)))
        plt.plot(gridx.flatten(), gridpred.flatten(), label = "Polynomial regressor curve MSE = {:0.4f}".format(mse), color = 'Green')
        plt.legend(loc = 2)
        plt.savefig("polyreg_var_{:03d}.png".format(i))
        plt.cla()


#!/usr/bin/python

import numpy as np

# Construct an array by executing a function over each coordinate.

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

a = np.fromfunction(f, (5, 4), dtype=int)
print(a)

# anonymous functoin
b = np.fromfunction(lambda x, y: 2*x + y, (2, 2))
print(b)


#!/usr/bin/env python
"""
Written by: CAPT Stu Blair
Date: 12 Oct
Purpose: simple Fourier-Bessel series expansion to a function

This alternative implementation uses the more general jv function

"""
import numpy as np
import matplotlib.pyplot as plt
import scipy.integrate as integrate
from scipy.special import jn_zeros
from scipy.special import jv

def f_exp(x):
    return x;

def FB_exp(x,cn,ev,N):
    ret_val = 0.;
    for i in range(N):
        ret_val += cn[i]*jv(1,x*ev[i]);
    return ret_val;

# describe discrete domain
Nx = 1000;
a = 0.; b = 3.;
X = np.linspace(a,b,Nx);

# get eigenvalues
N = 35; # number of ev's.

# get N roots of J_1
k = jn_zeros(1,N);
# compute eigenvalues
ev = k/b;
# allocate an array for expansion coefficients
cn = np.zeros_like(ev);
for n in range(N):
    # compute all of the expansion coefficients
    num, err = integrate.quad(lambda x: f_exp(x)*jv(1,ev[n]*x)*x,a,b);
    denom, err = integrate.quad(lambda x: jv(1,ev[n]*x)*jv(1,ev[n]*x)*x,a,b);
    cn[n] = num/denom;

# make a convenient function capturing the FB expansion
FB = lambda x: FB_exp(x,cn,ev,N);

plt.plot(X,f_exp(X),'-b',linewidth=2.0,label='f(x)')
plt.plot(X,FB(X),'-.r',linewidth=2.0,label='FB(x)')
plt.grid(True);
plt.xlabel('X',fontsize=14,fontweight='bold');
plt.title('Fourier-Bessel Expansion',fontsize=16,fontweight='bold')
plt.legend()


#! /usr/bin/env python3
"""Functions to optimize binning for histograms"""

import numpy as np
from math import ceil

def interquartile_range(data):
    """Calculate the interquartile range of a dataset"""
    q3, q1 = np.percentile(data, [75, 25])
    iqr = q3 -q1
    return iqr

def freedman_diaconis(data):
    """Calculate the bin width for a histogram."""
    n = len(data)
    iqr = interquartile_range(data)
    width = 2 * iqr / n**(1/3)
    return width

def bin_number(data):
    """Calculate the number of bins for a histogram"""
    width = freedman_diaconis(data)
    number = (np.max(data) - np.min(data)) / width
    return ceil(number)


import numpy as np

"""
Conversion between kappa and angular radius.

Based on Equation 11 in Soiaporn et al. 2013.
"""

def get_kappa(theta_p, p=0.68):
    """
    Calculate kappa corresponding to an angular radius theta_p.

    :param theta_p: Angular radius containing probability p [deg]
    :param p: Probability contained within radius theta_p
    :return kappa: Shape parameter of the vMF distribution
    """

    theta_p = np.deg2rad(theta_p)

    return -(2 / theta_p ** 2) * np.log(1 - p)

def get_theta_p(kappa, p=0.68):
    """
    Calculate theta_p corresponding to a vMF distribution
    of width kappa and containing probability p.

    :param kappa: Shape parameter of the vMF distribution
    :param p: Probability contained in radius theta_p
    :return theta_p: Angular radius containing probability p [deg]
    """

    theta_p = np.sqrt((-2 / kappa) * np.log(1 - p))

    return np.rad2deg(theta_p)


import lagmat
import numpy as np
import sklearn.base

def val_to_cont(val: np.array) -> np.array:
    """Convert values (e.g. prices) to continous returns, log
    differences, exponential growth rates

    Parameters:
    -----------
    x : ndarray (2D)
        Time series with the oldest observation in the first row
        and the latest observation in the last row.
        Values need to be ratio-scale, i.e. x>0

    Returns:
    --------
    ret : ndarray (2D)
        continous returns, log differences, exponential growth rates.
        The first row will be nan, i.e. r[0,;]=np.nan

    Alternative:
    ------------
        def val_to_cont(val):
            y = np.empty(shape=x.shape)
            y[0, :] = np.nan
            y[1:, :] = np.log(x[1:, :] / x[:-1, :])
            return y
    """
    return np.log(val / lagmat.lagmat(val, lags=[1]))

def cont_to_val(ret: np.array, initial: float = 1.0) -> np.array:
    """Convert growth rates, log differences, continous returns
    into values or prices

    Returns:
    --------
    ret : ndarray (2D)
        continous returns, log differences, exponential growth rates.

    initial: int or list/tupel/array
        Start values to compound the growth rates from

    Returns:
    --------
    x : ndarray (2D)
        Time series with the oldest observation in the first row
        and the latest observation in the last row.
    """
    z = np.exp(ret)
    z[0, :] = initial
    return np.cumprod(z, axis=0)

class Cont(sklearn.base.BaseEstimator, sklearn.base.TransformerMixin):
    def __init__(self, initial: float = None, trimnan: bool = True):
        self.initial = initial
        self.trimnan = trimnan

    def fit(self, X: np.array, y=None):
        # store the starting values
        if self.initial is None:
            if len(X.shape) == 1:
                self.initial = X[0]
            else:
                self.initial = X[0, :]
        # done
        return self

    def transform(self, X: np.array, copy=None) -> np.array:
        if self.trimnan:
            return lagmat.chopnan(val_to_cont(X), nchop=1)
        else:
            return val_to_cont(X)

    def inverse_transform(self, Z: np.array, initial: float = None,
                          copy=None) -> np.array:
        # Use the requested initial value, or use the fitted initial values
        if initial is None:
            initial = self.initial
        # Multiply initial values repeatly with growth rates
        if self.trimnan:
            return cont_to_val(
                np.r_[np.zeros(shape=(1, Z.shape[1])), Z],
                initial=initial)
        else:
            return cont_to_val(Z, initial=initial)


# %% Import

import sympy as spy
import numpy as np
import tensor_routines.sympy_routines as ts

# %%

def test_nf():
    a = ts.t(3)
    assert spy.sqrt(ts.sp(a, a)) == ts.nf(a)

def test_rp():
    # First order
    a = ts.t(1)
    q = ts.t(2, "q")
    assert ts.rp(q, a).tomatrix() == q.tomatrix()*ts.vec(a)
    # Second order
    a = ts.t(2)
    q = ts.t(2, "q")
    temp_1 = ts.rp(q, a).tomatrix()
    a = a.tomatrix()
    q = q.tomatrix()
    temp_2 = q*a*spy.transpose(q)
    assert spy.simplify(temp_1 - temp_2) == spy.zeros(3, 3)
    # Higher order
    a = ts.t(2, "a", [2, 2, 2])
    q = ts.t(2, "q", [4, 2])
    temp_1 = ts.rp(q, a)
    d_out = q.shape[0]
    zeros = spy.MutableDenseNDimArray(
        np.zeros([d_out]*a.rank(), dtype=int)
        )
    temp_2 = spy.MutableDenseNDimArray(
        np.zeros([d_out]*a.rank(), dtype=int)
        )
    for i0 in range(d_out):
        for i1 in range(d_out):
            for i2 in range(d_out):
                    temp_2[i0, i1, i2] = ts.sp(ts.tp(q[i0], q[i1], q[i2]), a)
    assert spy.simplify(temp_1 - temp_2) == zeros

def test_lm():
    # Scalar output
    a = ts.t(3, "a", [2, 3, 4])
    b = ts.t(3, "b", [2, 3, 4])
    assert ts.lm(a, b).is_scalar
    # Tensor output
    a = ts.t(5, "a")
    b = ts.t(2, "b")
    assert ts.lm(a, b).shape == (3, 3, 3)

def test_nvn():
    a = ts.symmetrize_lr(ts.t(4))
    b = ts.symmetrize(ts.t(2, "b"))
    c = ts.lm(a, b)
    check = spy.simplify(ts.nvn(c) - ts.nvn(a)*ts.nvn(b)) == spy.zeros(6, 1)
    assert check

def test_grad():
    a = ts.t(2, "a")
    b = ts.t(2, "b")
    temp = ts.grad(ts.tp(a, b), b)
    assert temp == ts.tp(a, ts.ID_4)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Feb  9 17:53:39 2019

@author: alankar
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.special.orthogonal import p_roots #Legendre Polynomial roots
from scipy import constants

def gauss_quad(func,a,b,n,*args):#Legendre
    [x,w] = p_roots(n+1)
    I_G = 0.5*(b-a)*np.sum(w*func(0.5*(b-a)*x+0.5*(b+a),*args))
    return I_G

V = 1000*1e-6 #m^3
rho = 6.022e28 #m^-3
thetaD = 428  #K

def CV(T):
    N = 50
    f = lambda x:(x**4*np.exp(x))/(np.exp(x)-1)**2
    return 9*V*rho*constants.k*(T/thetaD)**3*gauss_quad(f,0,thetaD/T,N)

Temperature = np.linspace(5,500,1000)
Heat_cap = np.array([CV(T) for T in Temperature])
plt.figure(figsize=(13,10))
plt.plot(Temperature,Heat_cap)
plt.grid()
plt.title(r'Debye Heat capacity $C_V(T)$ in a solid',size=25,y=1.02)
plt.xlabel(r'$T$',size=22)
plt.ylabel(r'$C_V(T)$',size=22)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('9.png')
plt.show()


#!/usr/bin/env python3

from numpy import *
from math import sqrt

from rigid_transform_3D import rigid_transform_3D

# Test with random data

# Random rotation and translation
R = mat(random.rand(3,3))
t = mat(random.rand(3,1))

# make R a proper rotation matrix, force orthonormal
U, S, Vt = linalg.svd(R)
R = U*Vt

# remove reflection
if linalg.det(R) < 0:
   Vt[2,:] *= -1
   R = U*Vt

# number of points
n = 10

# A = mat(random.rand(3, n));
A = array(random.rand(3, n));
B = R*A + tile(t, (1, n))

# Recover R and t
ret_R, ret_t = rigid_transform_3D(A, B)

# Compare the recovered R and t with the original
B2 = (ret_R*A) + tile(ret_t, (1, n))

# Find the root mean squared error
err = B2 - B
err = multiply(err, err)
err = sum(err)
rmse = sqrt(err/n);

print("Points A")
print(A)
print("")

print("Points B")
print(B)
print("")

print("Ground truth rotation")
print(R)

print("Recovered rotation")
print(ret_R)
print("")

print("Ground truth translation")
print(t)

print("Recovered translation")
print(ret_t)
print("")

print("RMSE:", rmse)

if rmse < 1e-5:
    print("Everything looks good!\n");
else:
    print("Hmm something doesn't look right ...\n");


'''
ANKIT KHANDELWAL
15863

Exercise 12a
'''

import math

import matplotlib.pyplot as plt
import numpy as np

theta = np.linspace(0, 2 * np.pi, 5000)
x = []
y = []
for i in theta:
    x.append(2 * math.cos(i) + math.cos(2 * i))
    y.append(2 * math.sin(i) - math.sin(2 * i))
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Deltoid Curve')
plt.plot(x, y)
plt.show()

Ankit Khandelwal_HW1/Exercise 12/exercise 12a.py


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

'''
an example of a genetic algorithm optimizing the parameters of a linear equation to find the
line of best fit for given points

there are only two parameters that are being optimized, but is easily scaleable;
    slope(flaot): between -10 and 10 --- this may need to be changed if the best fit line has a more extreme slope

    y-int(flaot): between -10 and 10 --- may also need to be changed depending on the location of the data

feel free to modify/add points or number of generations evolved

ethan gilmore
10/17/19
'''

x_points = [1, 1.5, 1.9, 2.1, 2.75, 4]
y_points = [5, 4.3, 2.4, 3.7, 2.5, 1]

'''
this is the structure for defining a set of parameters, or "gene_structure"

your parameters will be represented inside a dictionary with the keys named what the values represent
the value for each pair will be a 3-tuple

if you want your value to be a
    float: (float, min, max)
        float defines the type, but replace the min and the max fo set the bounds for the variable

    int: (int, min, max)
        int defines the type, replace min and max to set bounds

    list: (list, length, type)
        a list is a little different. the first element will be list to define it as a list
        second is the length, or number of things in the list
        third is the type of variable in the list - a float or an int

        example:
            {"my_list": (list, 10, (int, 1, 10))}
            this will create a paramter called my_list which is a list of length 10 filled with ints between 1 and 10
'''
parameters = {"slope": (float, -10, 10), "y-int": (float, -10, 10)}

'''
this is the structure for a fitness function

a fitness function is what determines how good a set of parameters (reffered to as an organism) is.
your fitness function should consist of;

Args:
    parameters / genes(dict): the actual values of an organism, modeled to your predefined structure

Returns:
    (int or float): returns a value specifying how good a set of paramters performed

    keep in mind that organisms (sets of parameters) with the HIGHEST RETURN VALUE when passed through
    this function are the ones kept and chosen to reproduce
'''

def loss(parameters):
    mean_squared_error = 0
    #find the total error of the line and the points
    for i, x in enumerate(x_points):
        #find the error between the actual y and the y of the function built with slope and y-int
        error = y_points[i] - (x * parameters['slope'] + parameters['y-int'])
        #decrease the mean squared error, because i want the set of parameters with the least difference to be greater
        #i.e. those with a greater error will be have a more negative fitness score
        mean_squared_error -= error**2
    return mean_squared_error

#create a population of size 20 with the paremeters and loss function we created
pop = genetic.Population(20, parameters, loss)
[pop.evolve() for i in range(10000)]

#set the slope and y-int to the organism with the best fitness
slope = pop.organisms[-1]['slope']
y_int = pop.organisms[-1]['y-int']

#used to plot points and the corrosponding line of best fit
plt.plot(x_points, y_points, 'ro')
x = np.linspace(-5,5,100)
y = slope*x+y_int
plt.plot(x, y)
plt.show()


#!/usr/bin/env
# -*- coding: utf-8 -*-
# Copyright (C) Victor M. Mendiola Lau - All Rights Reserved
# Unauthorized copying of this file, via any medium is strictly prohibited
# Proprietary and confidential
# Written by Victor M. Mendiola Lau <ryuzakyl@gmail.com>, August 2016

from math import sqrt
import numpy as np
from scipy.stats.stats import pearsonr

def probabilistic_pearsonr(x, y):
    """Computes the pearson coefficient as a probability value.

    Args:
        x (list): The first vector.
        y (list): The second vector.

    Returns:
        float: The pearson probability value between vectors x and y.

    """

    # computing the correlation value
    value = pearsonr(x, y)[0]       # getting only the first value of the tuple

    # moving value to interval [0, 2]
    value += 1.0

    # stretching value to the interval [0, 1]
    return 0.5 * value

def dis_pearsonr(x, y):
    """Computes the pearson coefficient as a dissimilarity value.

    Args:
        x (list): The first vector.
        y (list): The second vector.

    Returns:
        float: The pearson dissimilarity value between vectors x and y.

    """

    # computing the correlation value
    value = pearsonr(x, y)[0]       # getting only the first value of the tuple

    # setting dissimilarity value to the interval [0, 2]
    return 1 - value

def dissimilarity_pcc(x, y):
    """Computes a dissimilarity value based on a Pearson Correlation Coefficient.

    Args:
        x (list): The first vector.
        y (list): The second vector.

    Returns:
        float: The pearson correlation coefficient-based dissimilarity value between vectors x and y.

    """

    # getting the length of the vectors
    x_length = len(x)
    y_length = len(y)

    # validating parameters
    if x_length != y_length:
        raise Exception('Vectors with different sizes')

    # TODO: Here it is assumed that x and y are lists. Analyze the possibility for them to be tuples or numpy arrays

    # converting x and y to numpy arrays
    x_arr = np.array(x, np.float32)
    y_arr = np.array(y, np.float32)

    # computing vectors means and centered vectors
    x_mean = np.mean(x_arr)
    x_cent = x - x_mean
    y_mean = np.mean(y_arr)
    y_cent = y - y_mean

    num = np.dot(x_cent, y_cent)
    den = sqrt(np.dot(x_cent, x_cent) * np.dot(y_cent, y_cent))

    return 1 - num/den


# evolution strategy (mu + lambda) of the ackley objective function
from numpy import asarray
from numpy import exp
from numpy import sqrt
from numpy import cos
from numpy import e
from numpy import pi
from numpy import argsort
from numpy.random import randn
from numpy.random import rand
from numpy.random import seed

# objective function
def objective(v):
	x, y = v
	return -20.0 * exp(-0.2 * sqrt(0.5 * (x**2 + y**2))) - exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) + e + 20

# check if a point is within the bounds of the search
def in_bounds(point, bounds):
	# enumerate all dimensions of the point
	for d in range(len(bounds)):
		# check if out of bounds for this dimension
		if point[d] < bounds[d, 0] or point[d] > bounds[d, 1]:
			return False
	return True

# evolution strategy (mu + lambda) algorithm
def es_plus(objective, bounds, n_iter, step_size, mu, lam):
	best, best_eval = None, 1e+10
	# calculate the number of children per parent
	n_children = int(lam / mu)
	# initial population
	population = list()
	for _ in range(lam):
		candidate = None
		while candidate is None or not in_bounds(candidate, bounds):
			candidate = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
		population.append(candidate)
	# perform the search
	for epoch in range(n_iter):
		# evaluate fitness for the population
		scores = [objective(c) for c in population]
		# rank scores in ascending order
		ranks = argsort(argsort(scores))
		# select the indexes for the top mu ranked solutions
		selected = [i for i,_ in enumerate(ranks) if ranks[i] < mu]
		# create children from parents
		children = list()
		for i in selected:
			# check if this parent is the best solution ever seen
			if scores[i] < best_eval:
				best, best_eval = population[i], scores[i]
				print('%d, Best: f(%s) = %.5f' % (epoch, best, best_eval))
			# keep the parent
			children.append(population[i])
			# create children for parent
			for _ in range(n_children):
				child = None
				while child is None or not in_bounds(child, bounds):
					child = population[i] + randn(len(bounds)) * step_size
				children.append(child)
		# replace population with children
		population = children
	return [best, best_eval]

# seed the pseudorandom number generator
seed(1)
# define range for input
bounds = asarray([[-5.0, 5.0], [-5.0, 5.0]])
# define the total iterations
n_iter = 5000
# define the maximum step size
step_size = 0.15
# number of parents selected
mu = 20
# the number of children generated by parents
lam = 100
# perform the evolution strategy (mu + lambda) search
best, score = es_plus(objective, bounds, n_iter, step_size, mu, lam)
print('Done!')
print('f(%s) = %f' % (best, score))


import numpy as np

def get_indices(N, n_batches, split_ratio):
    """Generates splits of indices from 0 to N-1 into uniformly distributed\
       batches. Each batch is defined by 3 indices [i, j, k] where\
       (j-i) = (1/split_ratio)*(k-j). The first batch starts with i = 0,\
       the last one ends with k = N - 1.
    Args:
        N (int): total counts
        n_batches (int): number of splits
        split_ratio (float): split ratio, defines position of j in [i, j, k].
    Returns:
        generator for batch indices [i, j, k]
        ---------------------------------------
        Assume inds(n)[i] = inds(n-1)[i] + a, where a = const and i = 0,1,2
        from this and the fact that K(n_batches) = N-1 we can find that a = (N - 1 - k0)/(n_batches-1)
        Next, find j0 = k0 /(split_ratio+1)
        Assuming that j1 = k0 we get that j1 = j0 + a = k0, where j0,a are already found before.
        Finally, from that equation we get k0 = ((N-1)*(split_ratio+1))/(split_ratio*n_batches+1)
    """
    #inds = np.array([0, 0, 0])
    k0 = ((N-1)*(split_ratio+1))/(split_ratio*n_batches+1)
    j0 = k0 /(split_ratio+1)
    i0 = 0
    inds = np.array([i0, j0, k0])
    a = (N - 1 - k0)/(n_batches-1)
    inds -= a
    for i in range(n_batches):
        inds += a
        yield [int(round(inds[0])),int(round(inds[1])),int(round(inds[2]))]

def main():
    for inds in get_indices(100, 5, 0.25):
        print(inds)
    # expected result:
    # [0, 44, 55]
    # [11, 55, 66]
    # [22, 66, 77]
    # [33, 77, 88]
    # [44, 88, 99]

if __name__ == "__main__":
    main()


import copy
import numpy as np

# INPUTS
iters = 10
# data = [[-1,-1, 1],[1,0,-1],[-1,1.5,1]]
# data = [[-1,-1, 1],[1,0,-1],[-1,10,1]]
# data = [[-4,2, 1],[-2,1,1],[-1,-1,-1],[2,2,-1],[1,-2,-1]]
data = [(0,0,-1), (2,0,-1), (3,0,-1), (0,2,-1), (2,2,-1), (5,1,1), (5,2,1), (2,4,1), (4,4,1), (5,5,1)]

# SEPARATE DEPENDENT & INDEPENDENT
X = np.array( [ d[:-1] for d in data ] )
Y = np.array( [ d[-1] for d in data ] )

# INITIALIZE WEIGHTS & BIASES
theta = np.zeros(  ( len(data[0])-1 )  )
theta_0 = 0

# PERCEPTRON ALGORITHM
def perceptron(X, Y, theta, string, theta_0=0, iters=20):
    cntr = 0
    for iter_num in range(iters):
        print("ITER ", cntr)
        for i in range(len(Y)):
            theta0_before = copy.deepcopy(theta_0)
            theta_before = copy.deepcopy(theta)
            pred = (  theta.T @ X[i] + theta_0)
            loss = ( Y[i] * pred )
            if loss <= 0:
                theta = theta + Y[i] * X[i]
                theta_0 += Y[i]
            if theta0_before != theta_0:
                print(i, " :  MISTAKE!!")
            print("********* EPOCH COMPLETE ************")
        cntr += 1
    print("======================================================")
    print()
    print()
    return theta, theta_0

def d_hinge_loss(x, y, pred, theta):
    loss = max(1 - ( y * pred ), 0)
    if loss <= 0:
        d = theta
    else:
        d = -y*x - theta

    return d

def reg_perceptron(X, Y, theta, string, theta_0=0, iters=20):
    cntr = 0
    for iter_num in range(iters):
        print("ITER ", cntr)
        for i in range(len(Y)):
            pred = (  theta.T @ X[i] + theta_0)
            d = d_hinge_loss(x=X[i], y=Y[i], pred=pred, theta=theta)
            theta = theta - d
            theta_0 += Y[i]
            print("Updated: ", theta, "  |  ", theta_0)

            print("********* EPOCH COMPLETE ************")
        cntr += 1
    print("======================================================")
    print()
    print()
    return theta, theta_0

# a1, a2 = reg_perceptron(X=X, Y=Y, theta=np.zeros(  ( len(data[0])-1 )  ), string="CASE 1")
# print(a1, a2)

# a1, a2 = perceptron(X=X, Y=Y, theta=np.zeros(  ( len(data[0])-1 )  ), string="CASE 1")
# b1, b2 = perceptron(X=[X[1],X[2],X[0]], Y=[Y[1],Y[2],Y[0]], theta=np.zeros(  ( len(data[0])-1 )  ), string="CASE 2")
# c1, c2 = perceptron(X=[X[2],X[0],X[1]], Y=[Y[2],Y[0],Y[1]], theta=np.zeros(  ( len(data[0])-1 )  ), string="CASE 3")

# print(a1, a2)
# print(b1)
# print(c1)
# print(a2,b2,c2)
# print(a2)


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

# load Image
img = cv2.imread("hist2.tiff", 0)
# ----------------------------------------------------------
#Task (a)

plt.hist(img.ravel(),256,[0,256])
plt.show()
# ----------------------------------------------------------
height , width = img.shape # 500 * 500
pixelcount = height * width # 250000

# Task (b)
# frecquency
frecquency_dist = np.zeros((256))
for i in range(height):
    for j in range(width):
        frecquency_dist[img[i,j]] = frecquency_dist[img[i,j]] + 1

# probability
probability_array = np.zeros(len(frecquency_dist))
for i in range (len(probability_array)):
    probability_array[i] = frecquency_dist[i] /pixelcount

# comulative
commulative_freq = np.zeros(len(frecquency_dist))
commulative_freq[0] = probability_array[0]
for i in range(1 , len(commulative_freq)):
    commulative_freq[i] = probability_array[i]  + commulative_freq[i-1]

# multiply by L - 1 = 256 -1 = 255
xMultiplied = [np.math.floor(x * 255)  for x in commulative_freq]
print xMultiplied
# replace values
ContrastCorrected = img
for i in range(height):
    for j in range(width):
        ContrastCorrected[i , j ] = xMultiplied[img[i,j]]

#---------------- Showing New Histogram ----------------------------
# Task (c)
plt.hist(ContrastCorrected.ravel() , 255 , (0,  255))
plt.show()
# Save Image
cv2.imwrite("CorrectedImage.png" , ContrastCorrected)


# -*- coding: utf-8 -*-
"""
Created on Mon Oct  7 10:13:51 2019

@author: hamil
"""

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

#Plot the function to determine what the finction looks like to helps estimate a value
Eb = np.arange(0,10, 0.1)
y = (np.sqrt(10 - Eb)*np.tan(np.sqrt(10-Eb)) - np.sqrt(Eb))

plt.plot(Eb, y)
plt.ylim(-50,50)
plt.title("Even function with binding potential of 10")
plt.grid()
plt.show()

#Now use the Bisection method to determine where F(Eb) = 0
def funct(Eb):
    return (np.sqrt(10 - Eb)*np.tan(np.sqrt(10-Eb)) - np.sqrt(Eb))

guess = optimize.bisect(funct, 8, 9)
print("Guess for the Bisection Method:", guess)

#Now use the Newton / Raphson Method to determine where F(Eb) = 0
x0 = 8.5

newguess = optimize.newton(funct, x0)
print("Guess for the Newton/Raphson Method:", newguess)

#When looking at the two results, there are quite similar to 10**-8 decimal place.
#Then the question would be which is more accurate, both are precise to 10**-8.

#Now we can check the values returned for each method within our function

test1 = funct(guess)
test2 = funct(newguess)
print("The f(Eb) values for the Bisection method result and the Newton/Raphson method:", test1,test2)

#Based on the values determined by the two different methods, the second method, Newton/Raphson
#is more precise. This would be due to the fact that the result is two orders of magnitude smaller
#(closer to zero).

#Show that the equation is the alternative form for the original equation
y2 = ((np.sqrt(Eb)*(1/np.tan(np.sqrt(10-Eb))))-(np.sqrt(10-Eb)))
plt.plot(Eb,y)
plt.plot(Eb,y2)
plt.ylim(-50,50)
plt.title("Both functions with binding potential of 10")
plt.grid()
plt.show()
#This equation seems to be a mirror of the first equation. This equation also does not extend deeply
#into negative values. I believe that the root is equal or relatively close to being
#equal to the original equation.

def funct2(Eb):
    return ((np.sqrt(Eb)*(1/np.tan(np.sqrt(10-Eb))))-(np.sqrt(10-Eb)))

guess3 = optimize.bisect(funct2, 8,9)
guess4 = optimize.newton(funct2, x0)
print("Guess for the Bisection and Newton/Raphson methods for equation two:", guess3, guess4)

#Now to change the value of 10 to 20 and 30 within the functions
y3 = (np.sqrt(20 - Eb)*np.tan(np.sqrt(20-Eb)) - np.sqrt(Eb))
y4 = (np.sqrt(Eb)*(1/np.tan(np.sqrt(20-Eb))))-(np.sqrt(20-Eb))

plt.plot(Eb,y3)
plt.plot(Eb, y4)
plt.title("Functions with binding potential of 20")
plt.grid()
plt.show()

y5 = (np.sqrt(30 - Eb)*np.tan(np.sqrt(30-Eb)) - np.sqrt(Eb))
y6 = (np.sqrt(Eb)*(1/np.tan(np.sqrt(30-Eb))))-(np.sqrt(30-Eb))

plt.plot(Eb,y5)
plt.plot(Eb, y6)
plt.title("Functions with binding potential of 30")
plt.xlim(5,10)
plt.grid()
plt.show()

#When you change the binding potential to 20, is really shows how the mirror each other.
#Once you change the value to 30, the even function actually goes to zero and the other
#function significantly peaks at around 8.

#Now to find the roots for each new value
def funct3(Eb):
    return (np.sqrt(20 - Eb)*np.tan(np.sqrt(20-Eb)) - np.sqrt(Eb))
#redo functions then use methods to find roots to compare
def funct4(Eb):
    return (np.sqrt(30 - Eb)*np.tan(np.sqrt(30-Eb)) - np.sqrt(Eb))

x1 = 6
guess5 = optimize.bisect(funct3,5,7)
guess6 = optimize.newton(funct3, x1)
print("Guess for bisection and Newton Methods with binding potential set to 20:", guess5, guess6)

guess7 = optimize.bisect(funct4,8,15)
guess8 = optimize.newton(funct4, x0)
print("Guess for bisection and Newton Methods with binding potential set to 30:", guess5, guess6)
test3 = funct3(guess5)
test4 = funct3(guess6)
test5 = funct4(guess7)
test6 = funct4(guess8)
print("The y values for the function with binding potential of 20:", test3, test4)
print("The y values for the function with binding potential of 30:", test5, test6)

#When looking at the root values for the function with the two different binding potentials,
#it seems the binding potential at 30 is more unrealistic and the values of the root does not make
#too much sense. I would say the best binding potential would be between 10 and 20. The values for
#for the function are very close again, this time to the 10*-10. WHen looking at the values for f(Eb),
#the bisection method seems to be better for the binding potential set to 20, since it is positive.
#the function with binding potential set to 30 seems to be fairly off due to the fact that the
#f(Eb) values are both negative for both methods. With that being said over all (All functions) the Newton/Raphson
#method seems to be more precise.

#Now to try another method to find the roots and test the precision
#Lets use Ridder's method and compare to the others:
guesses = optimize.ridder(funct,8,9)
print("This is the guess for the Ridder Method:", guesses)
print("again the bisection and Newtons/Raphson:", guess, newguess)

#Now to check the precision of all three together
test9 = funct(guesses)
print("Respective precisions for Bisection, Newton/Raphson, Ridder:", test1,test2,test9)

#It seems the precision for the bisection and the ridder are on the same scale and the precision for the
#Newton/Raphson is better. This makes sense with the similarities between the bisection method and the
#ridder method. But the ridder tends to run faster than the bisection. This method uses the false position method
#a trial and error method but tends to have a better constraint on the tolarance. This method also determines that
#convergance is guarunteed such as the bisection method.


"""Poisson Distribution
=============================
Contains the concrete definition of PoissonDistribution.

Example:
    Creating the distribution::

        dist = PoissonDistribution(5)

"""

from __future__ import annotations

from gsf.core.mathematics.distributions.random_distribution import RandomDistribution
import numpy as np

class PoissonDistribution(RandomDistribution):
    """Poisson Distribution

    Draw samples from a Poisson distribution."""

    __mean: float
    """Mean of the distribution."""

    def __init__(self, mean: float):
        """Creates a poisson distribution

        Args:
            mean (float): Mean of the distribution.
        """
        self.__mean = mean

    def generate(self) -> int:
        """Generates a value following the distribution"""
        return np.random.poisson(self.__mean)

    def generate_list(self, size: int) -> np.ndarray:
        """Generates a ndarray of values following the distribution

        Args:
            size (int): Size of the list
        """
        return np.random.poisson(self.__mean, size)

    def evaluate(self) -> int:
        """Evaluates the expression"""
        return self.generate()

    def __str__(self):
        return super().__str__() + ".Poisson(" + str(self.__mean) + ")"


import numpy as np
import scipy.stats as spst

def ret2dva(r_um):
    """Converts retinal distances (um) to visual angles (deg)

    This function converts an eccentricity measurement on the retinal
    surface(in micrometers), measured from the optic axis, into degrees
    of visual angle.
    Source: Eq. A6 in Watson(2014), J Vis 14(7): 15, 1 - 17
    """
    sign = np.sign(r_um)
    r_mm = 1e-3 * np.abs(r_um)
    r_deg = 3.556 * r_mm + 0.05993 * r_mm ** 2 - 0.007358 * r_mm ** 3
    r_deg += 3.027e-4 * r_mm ** 4
    return sign * r_deg

def dva2ret(r_deg):
    """Converts visual angles (deg) into retinal distances (um)

    This function converts a retinal distancefrom the optic axis(um)
    into degrees of visual angle.
    Source: Eq. A5 in Watson(2014), J Vis 14(7): 15, 1 - 17
    """
    sign = np.sign(r_deg)
    r_deg = np.abs(r_deg)
    r_mm = 0.268 * r_deg + 3.427e-4 * r_deg ** 2 - 8.3309e-6 * r_deg ** 3
    r_um = 1e3 * r_mm
    return sign * r_um

def cart2pol(x, y):
    theta = np.arctan2(y, x)
    rho = np.hypot(x, y)
    return theta, rho

def pol2cart(theta, rho):
    x = rho * np.cos(theta)
    y = rho * np.sin(theta)
    return x, y

def angle_diff(angle1, angle2, lo=0, hi=2 * np.pi):
    """Returns the signed difference between two angles (rad)

    The difference is calculated as angle2 - angle1. The difference will thus
    be positive if angle2 > angle1.

    Parameters
    ----------
    angle1, angle2 : array_like
        Input arrays with circular data in the range [lo, hi]

    Returns
    -------
    The signed difference angle2 - angle1 in [lo, hi]
    """
    # https://stackoverflow.com/questions/1878907/
    #    the-smallest-difference-between-2-angles
    _, angle1 = spst.morestats._circfuncs_common(angle1, hi, lo)
    _, angle2 = spst.morestats._circfuncs_common(angle2, hi, lo)
    diff = np.arctan2(np.sin(angle2 - angle1), np.cos(angle2 - angle1))
    return diff * (hi - lo) / 2.0 / np.pi + lo

def circfve(r_true, r_pred, lo=0, hi=2 * np.pi):
    """Calculates the fraction of variance explained (FVE) for circular data

    Assumes circular data are in the range [lo, hi].
    Uses SciPy's circular stats functions.

    Parameters
    ----------
    r_true : array_like
        Circular data (ground-truth)
    r_pred : array_like
        Circular data (predicted)
    low : float or int, optional
        Low boundary for circular variance range.  Default is 0.
    high : float or int, optional
        High boundary for circular variance range.  Default is ``2*pi``.
    """
    r_true = np.asarray(r_true)
    r_pred = np.asarray(r_pred)
    r_mu_true = spst.circmean(r_true, low=lo, high=hi)
    var_err = spst.circvar(angle_diff(r_true, r_pred, lo=lo, hi=hi),
                           low=lo, high=hi)
    var_tot = spst.circvar(angle_diff(r_true, r_mu_true, lo=lo, hi=hi),
                           low=lo, high=hi)
    if np.isclose(var_err, 0) and np.isclose(var_tot, 0):
        return 1
    if np.isclose(var_tot, 0):
        return 0
    return (1 - var_err / var_tot)


# -----------------------------------------------------------------------------
# Author: Erik Rydow
# Email: erik.rydow@kcl.ac.uk
# Extended from code by Nicolas P. Rougier https://gist.github.com/rougier/e5eafc276a4e54f516ed5559df4242c0
#
# From https://en.wikipedia.org/wiki/Minkowski–Bouligand_dimension:
#
# In fractal geometry, the Minkowski–Bouligand dimension, also known as
# Minkowski dimension or box-counting dimension, is a way of determining the
# fractal dimension of a set S in a Euclidean space Rn, or more generally in a
# metric space (X, d).
# -----------------------------------------------------------------------------
import imageio
import numpy as np

def fractal_dimension(Z, threshold=0.8):
    """Returns box-counting dimension of a 2D array.

    Args:
        Z: 2D array to be analysed.
        threshold: Cutoff for converting values in Z to 1 and 0.

    Returns:
        The estimated box counting dimension.
    """

    # Only for 2d image
    assert(len(Z.shape) == 2)

    # From https://github.com/rougier/numpy-100 (#87)
    def boxcount(Z, k):
        S = np.add.reduceat(
            np.add.reduceat(Z, np.arange(0, Z.shape[0], k), axis=0),
                               np.arange(0, Z.shape[1], k), axis=1)

        # We count non-empty (0) and non-full boxes (k*k)
        return len(np.where((S > 0) & (S < k*k))[0])

    # Transform Z into a binary array
    Z = (Z < threshold)

    # Minimal dimension of image
    p = min(Z.shape)

    # Greatest power of 2 less than or equal to p
    n = 2**np.floor(np.log(p)/np.log(2))

    # Extract the exponent
    n = int(np.log(n)/np.log(2))

    # Build successive box sizes (from 2**n down to 2**1)
    sizes = 2**np.arange(n, 1, -1)

    # Actual box counting with decreasing size
    counts = []
    for size in sizes:
        counts.append(boxcount(Z, size))

    # Fit the successive log(sizes) with log (counts)
    coeffs = np.polyfit(np.log(sizes), np.log(counts), 1)
    return -coeffs[0]

I = imageio.imread("sierpinski.png", as_gray="True")/255.0    # Import the image in greyscale
print("Minkowski–Bouligand dimension (computed): ", fractal_dimension(I))
print("Haussdorf dimension (theoretical):        ", (np.log(3)/np.log(2)))


"""Module to calculate best path between multiple points, using simulated annealing algorithm

Functions:
distance -- cost function that calculates distance as the cost of a path
new_path -- path altering function that creates a new path
new_path_modified -- same as new_path but slightly modified
simulated_annealing_optimizer -- objective function that implements the simulated annealing algorithm
"""

import random
import numpy as np

def distance(coords):
    """Calculate the distance of a path between multiple points

    Arguments received:
    coords — list of coordinates, e.g. [(0,0), (10, 5), (10, 10)], representing a path

    Arguments returned:
    distance -- total distance as a float
    """
    distance = 0
    for p1, p2 in zip(coords[:-1], coords[1:]):
        distance += ((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2) ** 0.5
    return distance

def new_path(existing_path):
    """Switch two random consecutive points on a path

    Arguments received:
    existing_path -- list of coordinates representing a path

    Arguments returned:
    path -- list of coordinates representing the mutated path
    """
    path = existing_path[:]
    point = random.randint(0, len(path)-2)  # randomly choose a point between 1st and 2nd-to-last points on path
    path[point+1], path[point] = path[point], path[point+1]  # switch this point with the next point
    return path

# Our simulated annealing function generated a reasonable solution but there were signs that the approach was
# limited by our function for creating new paths. Create a new function to generate paths in a different way.
# Does this improve the performance of the simulated annealing function? Answer in workspace.py, along with timer.

def new_path_modified(existing_path):
    """Switch two random points on a path

    Arguments received:
    existing_path -- list of coordinates representing a path

    Arguments returned:
    path -- list of coordinates representing the mutated path
    """
    path = existing_path[:]
    point = random.randint(0, len(path)-1)  # randomly choose any point on path
    point2 = random.randint(0, len(path)-1)  # randomly choose any other point on path
    while point2 == point:
        point2 = random.randint(0, len(path)-1)
    temp = path[point]  # temporary placeholder for 1st point
    path[point], path[point2] = path[point2], temp  # switch 1st point with 2nd point
    return path

def simulated_annealing_optimizer(starting_path, cost_func, new_path_func, start_temp, min_temp, steps):
    """Calculate the best path between multiple points using a simulated annealing algorithm

    The simulated annealing algorithm begins with a given path and calculates its cost.
    Each iteration generates a new path with two random consecutive points switched.
    If the new path has a lower cost, it is better and will replace the current path.
    If it has a higher cost, it is a worse path and may replace the current path, as
    decided by comparing a random number with an acceptance probability that depends on
    the temperature. The temperature decreases a bit in each iteration, giving worse paths
    a lesser chance of replacing the current path. Allowing worse paths at the beginning
    helps to avoid converging to a local minimum rather than the global minimum.

    Arugments received:
    starting_path -- list of coordinates representing a path
    cost_func -- function to calculate cost of a path
    new_path_func -- function to generate a new path with two random consecutive points switched
    start_temp -- maximum temperature
    min_temp -- minimum temperature
    steps -- number of iterations

    Arguments returned:
    current_path -- list of coordinates representing the best path
    current_cost -- cost of the best path
    history -- an array of objects, each object containing information about each tested path
    """
    current_path = starting_path[:]
    current_cost = cost_func(current_path)
    temp_factor = -np.log(start_temp / min_temp)
    history = []
    for s in range(0, steps):
        temp = start_temp * np.exp(temp_factor * s / steps)
        new_path = new_path_func(current_path)
        new_cost = cost_func(new_path)
        if (new_cost < current_cost) or (random.random() <= np.exp(-(new_cost - current_cost)/temp)):
            current_path = new_path
            current_cost = new_cost
        record = {'step': s, 'temperature': temp, 'current_cost': current_cost, }
        history.append(record)
    return (current_path, current_cost, history)


# Example for Polynomial Regression
"""
Created on Wed May  9 06:59:15 2018

@author: Shivendra
This is Non Linear Model illustration code for ML training
"""

#Importing the libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# Lets read the Free fall dataset
dataset = pd.read_csv('/Users/apple/AnacondaProjects/polynomRegression/Polynomial_Regression/StoneTrajectoryData.csv')
time = dataset.iloc[:, 0:1].values
# We need to read the column no 3 which has the distance or height data
distance = dataset.iloc[:, 2].values
plt.scatter(time, distance, color="orange")

# Lets try Linear Regression to the Stone thrown up dataset
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(time, distance)

# Fitting Polynomial Regression to the dataset
#We use a tric. we transform our input x to x, x-square, x-cube....
#on these derived features we run the lienar regression
from sklearn.preprocessing import PolynomialFeatures
poly_reg = PolynomialFeatures(degree = 4)
time_poly = poly_reg.fit_transform(time)
lin_reg_2 = LinearRegression()
lin_reg_2.fit(time_poly, distance)
#pred2 = lin_reg_2.predict(poly_reg.fit_transform(time))
pred4 = lin_reg_2.predict(poly_reg.fit_transform(time))

poly_reg3 = PolynomialFeatures(degree = 12)
time_poly3 = poly_reg3.fit_transform(time)
lin_reg_3 = LinearRegression()
lin_reg_3.fit(time_poly3, distance)

pred12 = lin_reg_3.predict(poly_reg3.fit_transform(time))
print("R2 coefficient for degree 4: " , r2_score(pred4,distance))
print("R2 coefficient for degree 12: " , r2_score(pred12,distance))

# The average or bad results
# With linear Regression
plt.scatter(time, distance, color = 'green')
plt.plot(time, lin_reg.predict(time), color = 'brown')
plt.title('Linear Regression gives erroneous results here')
plt.xlabel('Time')
plt.ylabel('height(or Dsiplacement)')
plt.show()

# Visualising the Polynomial Regression results, the good one
plt.scatter(time, distance, color = 'green')
plt.plot(time, lin_reg_2.predict(poly_reg.fit_transform(time)), color = 'brown')
plt.title('Seems to work well (Polynomial Regression)')
plt.xlabel('Time')
plt.ylabel('Height')
plt.show()

#for degree 12
plt.scatter(time, distance, color = 'green')
plt.plot(time, lin_reg_3.predict(poly_reg3.fit_transform(time)), color = 'brown')
plt.title('Seems to work well (Polynomial Regression)')
plt.xlabel('Time')
plt.ylabel('Height')
plt.show()

# Predicting a new result with Linear Regression
lin_reg.predict(18)

# Predicting a new result with Polynomial Regression
lin_reg_2.predict(poly_reg.fit_transform(18))


import numpy as np
import numpy.linalg as la
from numbers import Number
from scipy import stats

def sequ(n,d,lb=0,ub=1,method=None,randomized=True, random_state = 1, rng= None,return_likelihood = False):
    if rng is None:
        rng = np.random.RandomState(seed=random_state)
    if method is None:
        seq = np.zeros((n,d))
    elif method == "uniform":
        n0 = int(np.power(n,1/d))
        assert n == n0 **d, 'this method does not apply to n = %d'%n
        tmp_list = np.linspace(0,1,n0+1)
        tmp_list = (tmp_list[:-1]+tmp_list[1:])/2
        dim_grid_list = [list(tmp_list) for i in range(d)]
        seq = np.stack(np.meshgrid(*dim_grid_list)).T.reshape(n,d)
    elif method == "random":
        seq = rng.rand(n,d)

    if randomized and method!="random":
        seq += rng.rand(n,d)
        seq = np.mod(seq,1.0)
    seq = lb+seq*(ub-lb)
    if not return_likelihood:
        return seq
    else:
        if isinstance(ub-lb,Number):
            return seq, np.ones(n)/np.power(ub-lb,d)
        else:
            return seq, np.ones(n)/np.prod(ub-lb)

def generate_multivariate_normal(Z,mu,Sigma):
    C = la.cholesky(Sigma)
    X = Z@C.T + mu
    likelihood = np.exp(-np.sum(Z*Z,axis=1)/2)/np.power(2*np.pi,len(mu)/2)/np.sqrt(la.det(Sigma))
    return X,likelihood

def seqn(n,d,mu=0,Sigma=1,method=None,randomized=True, random_state = 1,rng=None,return_likelihood = False):
    if method != "random":
        seq = sequ(n,d,0,1,method,randomized,random_state,rng,False)
        seq = stats.norm.ppf(seq)
    else:
        if rng is None:
            rng = np.random.RandomState(seed=random_state)
        seq = rng.randn(n,d)

    if isinstance(mu,Number):
        mu = mu*np.ones(d)
    if isinstance(Sigma,Number):
        Sigma = np.diag(np.ones(d)*Sigma)

    if return_likelihood:
        return generate_multivariate_normal(seq, mu,Sigma)
    else:
        return generate_multivariate_normal(seq, mu,Sigma)[0]

class SeqGenerator:
    def __init__(self, method = None, randomized=True, seed = 1, rng = None):
        self.method = method
        self.randomized = randomized
        self.seed = seed
        if rng is None:
            self.rng = np.random.RandomState(seed=random_state)
        else:
            self.rng = rng
        self.cur_state = seed
    def rand(self,n,d, lb=0,ub=1,return_likelihood = False):
        res = sequ(n,d,lb=lb,ub=ub,method=self.method,randomized=self.randomized, random_state = self.cur_state, rng= self.rng,return_likelihood = return_likelihood)
        self.cur_state += n
        return res
    def randn(self,n,d,mu=0,Sigma=1,return_likelihood = False):
        res = seqn(n,d,mu=mu,Sigma=Sigma,method=self.method,randomized=self.randomized, random_state = self.cur_state,rng=self.rng,return_likelihood = return_likelihood)
        self.cur_state += n
        return res


# Linear autoencoder (ie PCA) applied to a 3d dataset projecting to 2d
#https://github.com/ageron/handson-ml2/blob/master/17_autoencoders_and_gans.ipynb

import superimport

import numpy as np
import matplotlib.pyplot as plt
import os
figdir = "../figures"
def save_fig(fname): plt.savefig(os.path.join(figdir, fname))

import tensorflow as tf
from tensorflow import keras

from sklearn.decomposition import PCA

from mpl_toolkits import mplot3d
from mpl_toolkits.mplot3d import Axes3D

np.random.seed(4)

def generate_3d_data(m, w1=0.1, w2=0.3, noise=0.1):
    angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5
    data = np.empty((m, 3))
    data[:, 0] = np.cos(angles) + np.sin(angles)/2 + noise * np.random.randn(m) / 2
    data[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2
    data[:, 2] = data[:, 0] * w1 + data[:, 1] * w2 + noise * np.random.randn(m)
    return data

X_train = generate_3d_data(60)
X_train = X_train - X_train.mean(axis=0, keepdims=0)

np.random.seed(42)
tf.random.set_seed(42)

encoder = keras.models.Sequential([keras.layers.Dense(2, input_shape=[3])])
decoder = keras.models.Sequential([keras.layers.Dense(3, input_shape=[2])])
autoencoder = keras.models.Sequential([encoder, decoder])

autoencoder.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1.5))

history = autoencoder.fit(X_train, X_train, epochs=20)

codings = encoder.predict(X_train)

X = X_train
fig = plt.figure().gca(projection='3d')
fig.scatter(X[:,0], X[:,1], X[:,2], s=50, marker='o')
save_fig("linear-autoecoder-data3d.pdf")
plt.show()

fig = plt.figure(figsize=(4,3))
plt.plot(codings[:,0], codings[:, 1], "b.")
plt.xlabel("$z_1$", fontsize=18)
plt.ylabel("$z_2$", fontsize=18, rotation=0)
plt.grid(True)
save_fig("linear-autoencoder-embedding.pdf")
plt.show()

# PCA version

pca = PCA(n_components=2)
mu = np.mean(X_train, axis=0)
Xc = X_train - mu # center the data
pca.fit(Xc)
W = pca.components_.T # D*K
Z = np.dot(Xc, W) # N * K latent scores
Xrecon = np.dot(Z, W.T) + mu # N*D

fig = plt.figure(figsize=(4,3))
plt.plot(Z[:,0], Z[:, 1], "b.")
plt.xlabel("$z_1$", fontsize=18)
plt.ylabel("$z_2$", fontsize=18, rotation=0)
plt.grid(True)
save_fig("linear-autoencoder-pca.pdf")
plt.show()


"""Swap operator."""
from typing import List, Union

import numpy as np

from toqito.matrices import iden
from toqito.perms import swap

def swap_operator(dim: Union[List[int], int], is_sparse: bool = False) -> np.ndarray:
    r"""
    Produce a unitary operator that swaps two subsystems.

    Provides the unitary operator that swaps two copies of :code:`dim`-dimensional space. If the two
    subsystems are not of the same dimension, :code:`dim` should be a 1-by-2 vector containing the
    dimension of the subsystems.

    Examples
    ==========

    The :math:`2`-dimensional swap operator is given by the following matrix

    .. math::
        X_2 =
        \begin{pmatrix}
            1 & 0 & 0 & 0 \\
            0 & 0 & 1 & 0 \\
            0 & 1 & 0 & 0 \\
            0 & 0 & 0 & 1
        \end{pmatrix}

    Using :code:`toqito` we can obtain this matrix as follows.

    >>> from toqito.perms import swap_operator
    >>> swap_operator(2)
    [[1., 0., 0., 0.],
     [0., 0., 1., 0.],
     [0., 1., 0., 0.],
     [0., 0., 0., 1.]]

    The :math:`3`-dimensional operator may be obtained using :code:`toqito` as follows.

    >>> from toqito.perms import swap_operator
    >>> swap_operator(3)
    [[1., 0., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 1., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 1., 0., 0.],
     [0., 1., 0., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 1., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 1., 0.],
     [0., 0., 1., 0., 0., 0., 0., 0., 0.],
     [0., 0., 0., 0., 0., 1., 0., 0., 0.],
     [0., 0., 0., 0., 0., 0., 0., 0., 1.]]

    :param dim: The dimensions of the subsystems.
    :param is_sparse: Sparse if :code:`True` and non-sparse if :code:`False`.
    :return: The swap operator of dimension :code:`dim`.
    """
    # Allow the user to enter a single number for dimension.
    if isinstance(dim, int):
        dim = np.array([dim, dim])

    # Swap the rows of the identity appropriately.
    return swap(iden(int(np.prod(dim)), is_sparse), [1, 2], dim, True)


# **************************************************************************** #
#                                                                              #
#                                                         :::      ::::::::    #
#    variance.py                                        :+:      :+:    :+:    #
#                                                     +:+ +:+         +:+      #
#    By: ciglesia <ciglesia@student.42.fr>          +#+  +:+       +#+         #
#                                                 +#+#+#+#+#+   +#+            #
#    Created: 2020/11/22 23:53:26 by ciglesia          #+#    #+#              #
#    Updated: 2020/11/23 19:11:18 by ciglesia         ###   ########.fr        #
#                                                                              #
# **************************************************************************** #

import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent.parent.absolute()))
import ex00.sum as s
import ex01.mean as m

def variance(x):
    """
    Computes the variance of a non-empty numpy.ndarray, using a for-loop.
    Args:
    x: has to be an numpy.ndarray, a vector.
    Returns:
    The variance as a float.
    None if x is an empty numpy.ndarray.
    Raises:
    This function should not raise any Exception.
    """
    if (not s.elements(x)):
        return (None)
    mean = m.mean(x)
    return (s.sum_(x, lambda X: (X - mean)**2) / len(x))

if __name__ == "__main__":
    import numpy as np
    print("X = np.array([0, 15, -9, 7, 12, 3, -21])\nvariance(X)")
    X = np.array([0, 15, -9, 7, 12, 3, -21])
    print(variance(X))


#!/usr/bin/env python3
import numpy as np

def dt_x(c, x):
    return c * x

def simulate_prey_using_euler_method(c, x0, t, delta_t):
    x = x0
    for _ in np.arange(0, t, delta_t):
        x += dt_x(c, x) * delta_t
    return x

def simulate_prey_using_midpoint_method(c, x0, t, delta_t):
    x = x0
    for _ in np.arange(0, t, delta_t):
        dx_dt0 = c * x
        x05 = x + 0.5 * dx_dt0 * delta_t
        dx_dt05 = c * x05
        x += dx_dt05 * delta_t
    return x

def simulate_prey_using_analytical_method(c, x0, t):
    return x0 * np.exp(c * t)

def get_prey_euler_simulation_error(c, x0, t, delta_t):
    return simulate_prey_using_euler_method(
        c, x0, t, delta_t
    ) - simulate_prey_using_analytical_method(c, x0, t)

def get_prey_midpoint_simulation_error(c, x0, t, delta_t):
    return simulate_prey_using_midpoint_method(
        c, x0, t, delta_t
    ) - simulate_prey_using_analytical_method(c, x0, t)

def print_error(delta_t):
    c = 0.03
    x0 = 1000.0
    t = 20.0

    euler_error = get_prey_euler_simulation_error(c, x0, t, delta_t)
    print(f"Euler error with delta t = {delta_t}: {euler_error}")
    midpoint_error = get_prey_midpoint_simulation_error(c, x0, t, delta_t)
    print(f"Midpoint error with delta t = {delta_t}: {midpoint_error}")

if __name__ == "__main__":
    print_error(0.05)
    print_error(0.1)
    print_error(0.2)


#!/usr/bin/python
"""
File: Polynomial_vec.py

Copyright (c) 2016 Michael Seaman

License: MIT

Creating a class for polynomials that implements standard functions that
are common for polynomials. The class's code is later vectorized
"""

import timeit
import numpy as np
from unittest import TestCase

class Polynomial:
    """
    The polynomial class stores a list of coefficient values in increasing
    order of powers of x. Polynomials can be added, multiplied, subtracted
    differentiated, and outputed as strings
    """
    def __init__(self, coefficients):
        """
        Initiializes with a (python) list of coefficients, in
        increasing order by powers of x
        """
        self.coeff = coefficients

    def __add__(self, other):
        """
        Uses vectorized adding to the arrays that are reduced to the same size
        then concatenates the left overs.
        """
        smaller_array = np.copy(self.coeff)
        larger_array = np.copy(other.coeff)

        if len(self.coeff) > len(other.coeff):
            smaller_array = np.copy(other.coeff)
            larger_array = np.copy(self.coeff)
        smaller_array += larger_array[:len(smaller_array)]
        return Polynomial(np.concatenate([smaller_array, larger_array[len(smaller_array):]]))

    def __call__(self, x):
        """
        Evaluates this polynomial at the given value of x
        """
        power_array = np.arange(len(self.coeff))
        power_array = x ** power_array
        return np.inner(self.coeff, power_array)

    def differentiate(self):
        """
        Differentiate this polynomial in-place.
        This implementation I think is a bit more elegant
        than the book's solution
        """
        coeff_factors = np.arange(len(self.coeff))
        self.coeff = (self.coeff * coeff_factors)[1:]

    def call_unvectorized(self, x):
        """
        From this point down, the methods are unvectorized

        Evaluates the polynomial at the given value of x
        """
        s = 0
        for i in xrange(len(self.coeff)):
            s += self.coeff[i]*x**i
        return s

    def add_unvectorized(self, other):
        """
        Adds two polynomials, adding the coefficients only at their respective
        powers of x. Returns a polynomial.
        """
        if len(self.coeff) > len(other.coeff):
            result_coeff = self.coeff[:]  #copy!
            for i in xrange(len(other.coeff)):
                result_coeff[i] += other.coeff[i]
        else:
            result_coeff = other.coeff[:]
            for i in xrange(len(self.coeff)):
                result_coeff[i] += self.coeff[i]
        return Polynomial(result_coeff)

    def mul_unvectorized(self, other):
        """
        Returns a list of coefficients of from multiplying two polynomials
        that is no longer than the sum of the two coefficient list
        """
        c = self.coeff
        d = other.coeff
        M = len(c) - 1
        N = len(d) - 1
        result_coeff = np.zeros(M + N + 1)
        for i in xrange(0, M+1):
            for j in xrange(0, N+1):
                result_coeff[i+j] += c[i]*d[i]
        return Polynomial(result_coeff)

    def differentiate_unvectorized(self):
        """
        Differentiate this polynomial in-place.
        """
        for i in xrange(1, len(self.coeff)):
            self.coeff[i-1] = i*self.coeff[i]
        del self.coeff[-1]

    def derivative(self):
        """
        Differentiates this polynomial by copying it and the returning the
        differentiated version
        """
        dpdx = Polynomial(self.coeff[:]) # make a copy
        dpdx.differentiate()
        return dpdx

    def __str__(self):
        s = ""
        for i in range(len(self.coeff)):
            if(self.coeff[i] != 0):
                s += " + %g*x^%d" % (self.coeff[i], i)
        s = s.replace("+ -", "- ")
        s = s.replace("x^0", "1")
        s = s.replace(" 1*", " ")
        s = s.replace("x^1 ", "x ")
        if s[0:3] == " + ":
            s = s[3:]
        if s[0:3] == " - ":
            s = "-" + s[3:]
        return s

class test_Polynomial(TestCase):
    """
    This class contains the test on the Polynomial class
    """
    def test_vectorized_call(self):
        """
        Tests that when x = 1, the output is the list of sum of the polynomial list,
        that the output is the first coeff when x = 0, and tests the basic case of
        5 + 0(4)^1 + 2(4)^2 - 3(4)^3 = -155
        """
        testPoly = Polynomial(np.array([5,0,2,-3]))
        assert(testPoly(1) == np.sum(testPoly.coeff))
        assert(testPoly(0) == testPoly.coeff[0])
        assert(testPoly(4) == -155)

    def test_vectorized_add(self):
        """
        Tests the vectorized add on a basic case and a case
        where the one array is empty
        """
        poly1 = Polynomial(np.arange(5))
        poly2 = Polynomial(np.ones(10))
        empty_poly = Polynomial(np.zeros(0))
        sum_of_1_and_2 = Polynomial(np.concatenate([np.arange(1,6), np.ones(5)]))
        assert( np.array_equal(sum_of_1_and_2.coeff, (poly1 + poly2).coeff))

    def test_vectorized_differentiate(self):
        """
        Tests the vectorized differentiation on a simple test case:
        f(x) = 4 + 2x - 5x^2 + 13x^3
        f'(x) = 2 - 10x + 39x^2
        """
        poly1 = Polynomial([4,2,-5,13])
        poly1.differentiate()
        poly1_prime = Polynomial([2,-10,39])
        assert(np.array_equal(poly1.coeff, poly1_prime.coeff))

def time_method(name_of_method_with_args, nRepeats):
    return timeit.timeit('poly1.' + name_of_method_with_args, setup='from Polynomial_vec import Polynomial;x = 50 ; poly1 = Polynomial([7,4,-2,0,0,0,4,1,-1,0,2]); poly2 = Polynomial([0,4, 8,8,5, 0, -2,-3])', number = nRepeats)


"""The Gamma distribution."""
from equadratures.distributions.template import Distribution
import numpy as np
from scipy.stats import gamma
RECURRENCE_PDF_SAMPLES = 8000
class Gamma(Distribution):
    """
    The class defines a Gamma object. It is the child of Distribution.

    :param double shape:
		Shape parameter of the gamma distribution.
    :param double scale:
		Scale parameter of the gamma distribution.
    """
    def __init__(self, shape=None, scale=None):
        if shape is None:
            self.shape = 1.0
        else:
            self.shape = shape
        if scale is None:
            self.scale = 1.0
        else:
            self.scale = scale

        self.bounds = np.array([0.0, np.inf])
        if self.shape < 0 or self.scale < 0:
            raise ValueError('Invalid parameters in Gamma distribution. Shape and Scale should be positive.')
        self.parent = gamma(a=self.shape, scale=self.scale)
        self.mean, self.variance, self.skewness, self.kurtosis = self.parent.stats(moments='mvsk')
        self.x_range_for_pdf = np.linspace(0, self.scale*10, RECURRENCE_PDF_SAMPLES)

    def get_description(self):
        """
        A description of the gamma distribution.

        :param Gamma self:
            An instance of the Gamma class.
        :return:
            A string describing the gamma distribution.
        """
        text = "is a gamma distribution with a shape parameter of "+str(self.shape)+", and a scale parameter of "+str(self.scale)+"."
        return text

    def get_pdf(self, points=None):
        """
        A gamma probability density function.

        :param Gamma self:
            An instance of the Gamma class.
        :param matrix points:
            Matrix of points for defining the probability density function.
        :return:
            An array of N equidistant values over the support of the distribution.
        :return:
            Probability density values along the support of the Gamma distribution.
        """
        if points is not None:
            return self.parent.pdf(points)
        else:
            raise ValueError( 'Please digit an input for getPDF method')

    def get_cdf(self, points=None):
        """
        A gamma cumulative density function.

        :param Gamma self:
            An instance of the Gamma class.
        :param matrix points:
            Matrix of points for defining the gamma cumulative density function.
        :return:
            An array of N equidistant values over the support of the gamma distribution.
        :return:
            Cumulative density values along the support of the gamma distribution.
        """
        if points is not None:
            return self.parent.cdf(points)
        else:
            raise ValueError( 'Please digit an input for getCDF method')
    def get_icdf(self, xx):
        """
        A gamma inverse cumulative density function.

        :param gamma self:
            An instance of Gamma class.
        :param xx:
            An array of points at which the inverse of cumulative density function needs to be evaluated.
        :return:
            Inverse cumulative density function values of the Gamma distribution.
        """
        return self.parent.ppf(xx)
    def get_samples(self, m=None):
        """
         Generates samples from the Gamma distribution.

         :param Gamma self:
             An instance of the Gamma class.
         :param integer m:
             Number of random samples. If no value is provided, a default of     5e5 is assumed.
         :return:
             A N-by-1 vector that contains the samples.
        """
        if m is not None:
           number = m
        else:
           number = 500000
        return self.parent.rvs(size = number)


# Class A is wTx >0
# Class B is wTx <=0

import numpy
import matplotlib.pyplot as plt

samplesize = 50

# Generating All random data for class A and Class B
xclassA = numpy.random.uniform(-1,1, size=samplesize)
yclassA = numpy.random.uniform(-1,1, size=samplesize)
xclassB = numpy.random.uniform(-1,1, size=samplesize)
yclassB = numpy.random.uniform(-1,1, size=samplesize)
ones = numpy.ones(samplesize)
xyclassA = numpy.c_[xclassA, yclassA,ones]
xyclassB = numpy.c_[xclassB, yclassB,ones]

# Creating w list
wList = [[1, 1 ,0], [-1,-1,0],[0,0.5,0],[1,-1,5],[1.0,1.0,0.3]]

# Calculating the accuracy
overallAccuracy = []
for w in wList :
    correct = [0 ,0]
    for cord in xyclassA:
        if(numpy.dot(w,cord)>0):
            correct[0] = correct[0] + 1
    correct[0]= correct[0]*100/samplesize
    for cord in xyclassB:
        if(numpy.dot(w,cord)<=0):
            correct[1] = correct[1] + 1
    correct[1]= correct[1]*100/samplesize
    overallAccuracy.append(correct)

print(overallAccuracy)
x = numpy.linspace(-2,2,1000)
plt.subplot(2, 2, 1)
plt.scatter(xclassA, yclassA, label= "classA", color= "green", s=10)
plt.scatter(xclassB, yclassB, label= "classB", color= "blue", s=10)
plt.plot(x,-x,linestyle='solid',color = "black")
plt.title('for W = [1, 1, 0]')
plt.legend()

plt.subplot(2, 2, 2)
plt.scatter(xclassA, yclassA, label= "classA", color= "green", s=10)
plt.scatter(xclassB, yclassB, label= "classB", color= "blue", s=10)
plt.plot(x,x+5,linestyle='solid',color = "black")
plt.title('for W = [1, -1, 5]')
plt.legend()

plt.subplot(2, 2, 3)
plt.scatter(xclassA, yclassA, label= "classA", color= "green", s=10)
plt.scatter(xclassB, yclassB, label= "classB", color= "blue", s=10)
plt.plot(x,-x-0.3,linestyle='solid',color = "black")
plt.title('for W = [1.0, 1.0, 0.3]')

plt.xlabel('x - axis')
plt.ylabel('y - axis')
plt.show()


from IMLearn.learners import UnivariateGaussian, MultivariateGaussian
import numpy as np
import plotly.graph_objects as go
import matplotlib.pyplot as plt

def test_univariate_gaussian():
    # Question 1 - Draw samples and print fitted model
    mu = 10
    var = 1
    ndarr = np.random.normal(mu, var, 1000)
    uvg = UnivariateGaussian()
    uvg.fit(ndarr.transpose())
    print(uvg.mu_, uvg.var_)

    # Question 2 - Empirically showing sample mean is consistent
    y_arr = np.zeros(100,)
    arr_x = np.arange(10, 1001, 10)
    sum = 0
    for i in range(0, 1000, 1):
        sum += ndarr[i]
        if i % 10 == 0 and i != 0:
            tmp = int(i / 10)
            y_arr[tmp] = abs((sum / i) - mu)
    plt.scatter(x=arr_x, y=y_arr)
    plt.title("Fit graph")
    plt.xlabel("sample size")
    plt.ylabel("distance between estimated nad true value")
    plt.show()

    # Question 3 - Plotting Empirical PDF of fitted model
    pdf = uvg.pdf(ndarr)
    plt.scatter(x=ndarr, y=pdf)
    plt.title("Empirical PDF of fitted model")
    plt.xlabel("sample value")
    plt.ylabel("sample pdf")
    plt.show()

def test_multivariate_gaussian():
    # Question 4 - Draw samples and print fitted model
    mu = [0, 0, 4, 0]
    var = [[1, 0.2, 0, 0.5], [0.2, 2, 0, 0], [0, 0, 1, 0], [0.5, 0, 0, 1]]
    ndarr = np.random.multivariate_normal(mu, var, 1000)
    mvg = MultivariateGaussian()
    mvg.fit(ndarr)
    print(mvg.mu_)
    print(mvg.cov_)

    # Question 5 - Likelihood evaluation
    f = np.linspace(-10, 10, 200)
    xv, yv = np.meshgrid(f, f)
    heatMap = np.ndarray((200, 200))
    maxval = None

    for ind, i in enumerate(f):
        for jnd, j in enumerate(f):
            curr_mu = np.array([i, 0, j, 0])
            heat_val = mvg.log_likelihood(curr_mu, var, ndarr)
            if maxval is None:
                maxval = heat_val
            if heat_val > maxval:
                maxval = heat_val
                maxf1 = ind
                maxf2 = jnd
            heatMap[ind][jnd] = int(heat_val)
    fig, ax = plt.subplots()

    c = ax.pcolormesh(xv, yv, heatMap, cmap='RdBu',
                      vmin=np.min(heatMap),
                      vmax=np.max(heatMap))
    ax.set_title('log likelihood heatmap')
    ax.axis([xv.min(), xv.max(), yv.min(), yv.max()])
    fig.colorbar(c, ax=ax)

    plt.show()

    # Question 6 - Maximum likelihood
    #round to 3 decimal points and then print (maxf1,maxf2)
    print("max values: " + str(maxval) + " " + str(round(f[maxf1], 3)) + " " + str(round(f[maxf2], 3)))

if __name__ == '__main__':
    np.random.seed(0)
    test_univariate_gaussian()
    test_multivariate_gaussian()


import numpy as np
from ..utils import check_features, check_target
from .linear_regression import LinearRegression

class RidgeRegression(LinearRegression):
    """Linear regression with l2 regularization.

    Attributes:
        learning_rate: Rate of update for gradient descent at
        each iteration. Not used for other solving methods (float).
        num_iterations: Number of iterations for gradient descent (int).
        save_cost: Whether to save or not the cost of each iteration.
        Only used for gradient descent (boolean).
        grad_descent_cost: List with the cost for each iteration when
        using gradient descent (list of floats).
        parameters: nd array with the model parameters (theta).
    """

    def __init__(self, regularization=0.1, solving_method='normal_equation',
                 learning_rate=0.01, num_iter=1000, save_cost=False):
        self.regularization = regularization
        self.solving_method = solving_method
        self.learning_rate = learning_rate
        self.num_iterations = num_iter
        self.save_cost = save_cost

        solvers = ['normal_equation', 'gradient_descent']
        if self.solving_method not in solvers:
            raise ValueError("solving_method must be one of: ", solvers)

    def fit(self, x, y):
        """Fit a ridge regression model to features x and target y.

        Arguments:
            x: Features - nd array with shape (m_samples, n_features).
            If you have a pandas Dataframe use df.values.
            y: Target values. Can be a one or two dimensional numpy
            array with shape (m_samples,) or (m_samples, 1)
        """
        check_features(x)
        check_target(y)
        x = np.insert(x, 0, 1, axis=1)  # add bias
        y = y.reshape(-1, 1) if y.ndim == 1 else y

        if self.solving_method == 'normal_equation':
            self.normal_equation(x, y)
        else:
            self.parameters = np.zeros(shape=(x.shape[1], 1))
            self.batch_gradient_descent(x, y)
        return self

    def normal_equation(self, x, y):
        """Normal equation for ridge: inv(X'*X + lambda*L) * X' * Y."""
        # L is the identity matrix with shape (n+1) and first cell zero.
        L = np.identity(x.shape[1])
        L[0][0] = 0
        inverse = np.linalg.pinv(x.T @ x + self.regularization*L)
        self.parameters = inverse @ x.T @ y

    def hypothesis(self, x):
        """Hypothesis for linear regression (ax + b)."""
        return x @ self.parameters

    def gradient_function(self, x, y, predictions=None):
        """Compute the gradient with the current parameters."""
        if predictions is None:
            predictions = self.hypothesis(x)
        penalty = self.regularization * np.sum(self.parameters[1:])
        return x.T @ (predictions - y) + penalty

    def cost_function(self, x, y, predictions=None):
        """Return the cost for the current parameters using MSE."""
        if predictions is None:
            predictions = self.hypothesis(x)
        squared_error = np.sum(np.square(predictions - y))
        penalty = self.regularization * np.sum(np.square(self.parameters))
        return (squared_error + penalty) / (2 * x.shape[0])


import pickle
import re
import pandas as pd
import numpy as np
import math
from statsmodels.tsa.stattools import adfuller, kpss
from arch.unitroot import PhillipsPerron

"""
Refs:
- https://www.machinelearningplus.com/time-series/time-series-analysis-python/
- https://machinelearningmastery.com/time-series-data-stationary-python/
- https://www.machinelearningplus.com/time-series/arima-model-time-series-forecasting-python/
"""

class TimeSeriesAnalysis:
    def __init__(self, series: pd.Series):
        self.original_series = series
        self.series = series

    def interpolate_ts(self, method: str):
        pass

    def test_stationarity(self, test: str, threshold=0.05, **kwargs):
        """
        Test for the stationarity of a given series around a deterministic trend.
        Interesting ref: https://stats.stackexchange.com/questions/88407/adf-test-pp-test-kpss-test-which-test-to-prefer

        :param test:    unit root test. The first three all start with the null of a unit root and have an
                        alternative of a stationary process. The last one, KPSS, has a null of a stationary process
                        with an alternative of a unit root.
                        - 'adf' for Augmented Dickey Fuller test,
                        - 'dfgls' for Elliott, Rothenberg and Stock’s GLS version of the Dickey-Fuller test
                        - 'pp' for Phillips–Perron test
                        - 'kpss' for Kwiatkowski–Phillips–Schmidt–Shin test,

        :param threshold:   confidence level for p_value. The p-value is the probability score based on which you can
                            decide whether to reject the null hypothesis or not.
                            If the p-value is less than a predefined alpha level (typically 0.05), we reject the null hypothesis.
        :param **kwargs:    Any additional arguments for the test in question i.e.
        :return: True if p-value below threshold (for DF and ADF) or above threshold (for KPSS), otherwise False.
        """
        if re.search('adf', test, re.IGNORECASE):
            test_statistic, p_value, n_lags_used, _, critical_values, _ = adfuller(self.series, **kwargs)
            return p_value < threshold
        elif re.search('kpss', test, re.IGNORECASE):
            test_statistic, p_value, n_lags_used, critical_values = kpss(self.series, **kwargs)
            return p_value > threshold  # for KPSS, series is NOT stationary if < threshold
        elif re.search('pp', test, re.IGNORECASE):
            result = PhillipsPerron(self.series)
            test_statistic, p_value, n_lags_used, critical_values = result.stat, result.pvalue, result.lags, result.critical_values
            return p_value < threshold
        else:
            raise Exception("Invalid `test`")

    def stationarize_ts(self, transform_fun, test_tup, n_thresh=10):
        """
        Making a time series stationary means to not make it a function of time, that is its mean and variance
        do not change over time.
        :param transform_fun: function to apply to time series.
            -   For **differentiation**, use `np.diff`. Preferably use `lambda x: np.diff(x, prepend=x[0])`
                to avoid losing data, since diff removes first element when differentiating,
                and need to prepend each time the first value.
            - For **log**, use `np.log`. To use different log bases, so use `lambda x: math.log(x, base)`
            -
        :param test_tup: tuple representing stationarity test and threshold
        :param n_thresh: max number of times to apply function before stopping
        :return:
        """

        def recursive_handler(n, *args):
            stationary = self.test_stationarity(test=test_tup[0], threshold=test_tup[1])
            if stationary or n == n_thresh:
                return args[0], n
            return recursive_handler(n + 1, transform_fun(*args))

        fun_applied_series, n = recursive_handler(0, self.series)
        print(f'function was applied {n} times.')
        self.series = pd.Series(index=self.series.index, data=fun_applied_series)
        return self.series, n

    def decompose_ts(self):
        pass

    def detrend_ts(self, method):
        """
        Remove the trend component from a time series.

        :param method:
            - Subtract the mean
            - Subtract the line of best fit from the time series, obtained from a linear regression model with
            the time steps as the predictor. For more complex trends, you may want to use quadratic terms (x^2) in the model.
            - Subtract the trend component obtained from time series decomposition we saw earlier.
            - Apply a filter like Baxter-King filter(statsmodels.tsa.filters.bkfilter) or the
            Hodrick-Prescott Filter (statsmodels.tsa.filters.hpfilter) to remove the moving average trend lines or the cyclical components.
        :return:
        """
        pass

if __name__ == '__main__':
    # with open('temp_prices.pkl', 'wb') as handle:
    #     data = YahooFinance(ticker='AAPL', period='YTD').convert_format('pandas')['Close']
    #     print(data)
    #     pickle.dump(data, handle, protocol=pickle.HIGHEST_PROTOCOL)

    with open('temp_prices.pkl', 'rb') as handle:
        data = pickle.load(handle)
        ts = TimeSeriesAnalysis(series=data)
        # print(ts.test_stationarity(test='adf'))
        print(ts.stationarize_ts(transform_fun=lambda x: np.diff(x, prepend=x[0]), test_tup=('pp', 0.05)))


import numpy as np

def quantile(x, q, weights=None):
    """
    Compute sample quantiles with support for weighted samples.
    Note
    ----
    When ``weights`` is ``None``, this method simply calls numpy's percentile
    function with the values of ``q`` multiplied by 100.
    Parameters
    ----------
    x : array_like[nsamples,]
       The samples.
    q : array_like[nquantiles,]
       The list of quantiles to compute. These should all be in the range
       ``[0, 1]``.
    weights : Optional[array_like[nsamples,]]
        An optional weight corresponding to each sample. These
    Returns
    -------
    quantiles : array_like[nquantiles,]
        The sample quantiles computed at ``q``.
    Raises
    ------
    ValueError
        For invalid quantiles; ``q`` not in ``[0, 1]`` or dimension mismatch
        between ``x`` and ``weights``.
    """
    x = np.atleast_1d(x)
    q = np.atleast_1d(q)

    if np.any(q < 0.0) or np.any(q > 1.0):
        raise ValueError("Quantiles must be between 0 and 1")

    if weights is None:
        return np.percentile(x, list(100.0 * q))
    else:
        weights = np.atleast_1d(weights)
        if len(x) != len(weights):
            raise ValueError("Dimension mismatch: len(weights) != len(x)")
        idx = np.argsort(x)
        sw = weights[idx]
        cdf = np.cumsum(sw)[:-1]
        cdf /= cdf[-1]
        cdf = np.append(0, cdf)
        return np.interp(q, cdf, x[idx]).tolist()


from sklearn.ensemble import RandomForestRegressor
import numpy as np  # linear algebra
import pandas as pd  # data processing, CSV file I/O (e.g. pd.read_csv)
import re
import matplotlib.pyplot as plt
from scipy.stats import chi2_contingency

from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.feature_selection import mutual_info_regression
import matplotlib.pyplot as plt  # some plotting!
from scipy import stats
from subprocess import check_output
from scipy.stats import norm
import plotly.figure_factory as ff

def check_data_norm(target_vector, apply_log=False, both=True):
    log_data = np.array([0, 0.001])
    plot_name = "plot"
    if both:
        log_data = np.log(target_vector)
    elif apply_log:
        target_vector = np.log(target_vector)
        plot_name = "log"

    m = target_vector.mean()
    s = target_vector.std()
    gaussian_data = np.random.normal(m, s, 10000)

    if both:
        m_log = target_vector.mean()
        s_log = target_vector.std()
        gaussian_data_log = np.random.normal(m_log, s_log, 10000)

    if both:
        fig = ff.create_distplot(
            [target_vector, log_data, gaussian_data, gaussian_data_log],
            group_labels=["plot", "log", "gaussian", "gaussian log"],
            curve_type="kde")
    else:
        fig = ff.create_distplot(
            [target_vector, gaussian_data],
            group_labels=[plot_name, "gaussian"],
            curve_type="kde")
    fig.update_layout(showlegend=True)
    fig.show()

    plt_fig = plt.figure()
    plt_fig.set_size_inches(10, 5)

    ax1 = plt.subplot(121)
    ax2 = plt.subplot(122)

    res = stats.probplot(target_vector, plot=ax1)
    ax1.set_title("Probability Plot", fontsize=14)
    ax1.set_ylabel("Sample Quantiles", fontsize=12)
    ax1.set_xlabel("Theoretical Quantiles", fontsize=12)

    if both:
        res = stats.probplot(log_data, plot=ax2)
        ax2.set_title("Probability Plot of log", fontsize=14)
        ax2.set_ylabel("Sample Quantiles", fontsize=12)
        ax2.set_xlabel("Theoretical Quantiles", fontsize=12)
        ax2.legend()

    plt.show()

def plot_feature_importance(X, y):
    mir_result = mutual_info_regression(X, y)  # mutual information regression feature ordering
    feature_scores = []
    for i in np.arange(len(X.columns)):
        feature_scores.append([X.columns[i], mir_result[i]])
    sorted_scores = sorted(np.array(feature_scores), key=lambda s: float(s[1]), reverse=True)
    print(np.array(sorted_scores))
    # for i, col in enumerate(sorted_scores):
    #     print(f"{i}: ", X.columns[int(col[0])])
    fig = plt.figure(figsize=(13, 6))
    ax = fig.add_subplot(111)
    ind = np.arange(len(X.columns))
    plt.bar([col[0] for col in sorted_scores], [float(i) for i in np.array(sorted_scores)[:, 1]])
    ax.axes.set_xticks(X.columns)
    plt.title('Feature Importances (Mutual Information Regression)')
    plt.ylabel('Importance')
    plt.show()


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

class Riemann_Sum_Approximation():
    """
    This object approximates the area of a quarter circle by a finite (=n) sum.
    Furthermore, it provides a method to plot what is going on.

    """

    def __init__(self, n=100):

        self.n = n

    def approximate_pi(self):
        """
        Function to approximate the integral of a quarter of the unit circle.

        """
        # divide in n sectors
        d = 1/self.n

        # function to approximate pi by summing the areas of the bars
        pi_app = 4*d*sum(math.sqrt(1 - x**2) for x in np.linspace(0,1,self.n + 1))

        # calcute the error
        error = round(100*abs(pi_app - np.pi)/np.pi,6)

        return 'Approximation of pi: {}, error: {}%'.format(pi_app, error)

    def bar_plot(self):
        """
        Function to plot a quarter of the unit circle and the approximation
        with n intervals.

        """

        d = 1/self.n

        seq = np.linspace(0, 0.5*np.pi, 25)

        u = np.cos(seq)
        v = np.sin(seq)

        fig, ax = plt.subplots(1)

        # for loop to create the bar plots
        for x in np.linspace(0,1,self.n + 1):
            ax.bar(x, math.sqrt(1 - x**2),
                   width=d,
                   alpha=0.2,
                   align='edge',
                   edgecolor='b',
                   color='g')

        ax.plot(u, v)

        ax.set_aspect(1)

        plt.xlim(-0.25,1.25)
        plt.ylim(-0.25,1.25)

        plt.grid(linestyle='--')

        plt.title('plot unit circle for pi approximation', fontsize=8)

        plt.show()


import math

import numpy as np

def vector_length(vector):
    return np.linalg.norm(vector)

def vectors_angle(v1, v2):
    return math.acos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))

def vectors_differ(v1, v2):
    return len(v1) != len(v2) or any([v1[i] != v2[i] for i in range(len(v1))])

def radians_to_degrees(radians):
    return 180.0 / math.pi * radians

def normalize_vector(vector):
    length = vector_length(vector)
    return tuple(float(x) / length for x in vector)

def points_to_vector(p1, p2):
    return p1[0] - p2[0], p1[1] - p2[1]

def sum_vectors(v1, v2):
    return tuple(v1[i] + v2[i] for i in range(len(v1)))


import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
import sys

def main():

    N = 50000
    uniform = np.random.randint(0,2**30,size=N)

    #Uniform
    a = 0;
    b = 1000;
    uniform_translated = uniform/2**30*b + a
    plt.hist(uniform_translated, bins=30, color = 'green', density=True, edgecolor='black', alpha = 0.4)
    plt.xlabel('Value')
    plt.ylabel('PDF')
    plt.show()
    print(" Mean is %.2f = (b-a)/2" % np.mean(uniform_translated))
    print()

    #Normal
    mu = 100000
    sigma = 100
    normal_distribution = np.zeros(N)
    for i in range(N):
        nones = bin(uniform[i]).count("1")
        std_ones = np.sqrt(30/4)
        mean_ones = 15
        normal_distribution[i] = nones*sigma/std_ones - mean_ones*sigma/std_ones + mu

    plt.hist(normal_distribution, bins=21, color = 'orange', density=True, edgecolor='black', alpha = 0.4)
    plt.xlabel('Value')
    plt.ylabel('PDF')
    plt.show()
    print(" Mean is %.2f (mu)" % np.mean(normal_distribution))
    print(" std is %.2f (sigma)" % np.std(normal_distribution))
    print()

    #Exponential
    rate = 1e-4
    exp_distribution = (-np.log2(uniform)+30)/rate*np.log(2)
    plt.hist(exp_distribution, bins=30, color = 'red', density=True, edgecolor='black', alpha = 0.4)
    plt.xlabel('Value')
    plt.ylabel('PDF')
    plt.show()
    print(" Mean is %.2f (1/lambda)" % np.mean(exp_distribution))
    print()

    #Gamma
    k = 10
    gamma_distribution = np.zeros(N)
    for i in range(N):
        value = 0
        for ki in range(k):
            value += exp_distribution[np.random.randint(0,N)]
        gamma_distribution[i] = value
    plt.hist(gamma_distribution, bins=30, color = 'blue', density=True, edgecolor='black', alpha = 0.4)
    plt.xlabel('Value')
    plt.ylabel('PDF')
    plt.show()
    print(" Mean is %.2f (k/lambda)" % np.mean(gamma_distribution))
    print()

    #arbitrary(quantile), beta distribution as example
    """
    We can apply the the quantile function (inverse CDF) of a probability
    distribution to the uniformly distributed value in order to get a
    variable with the specified distribution.
    """

    Nprecision = 100
    prob = np.arange(1,Nprecision)/Nprecision
    quantiles = (stats.beta.ppf(prob,0.5,0.5)*1e5).astype(int) #generating quantile function for Beta distribution
    print(list(quantiles))

    uniform = np.random.randint(0,Nprecision-1,size=N)
    beta_distribution = np.zeros(N)
    for i in range(N):
        beta_distribution[i] = quantiles[uniform[i]]

    plt.hist(beta_distribution, bins=20, color = 'purple', density=True, edgecolor='black', alpha = 0.4)
    plt.xlabel('Value')
    plt.ylabel('PDF')
    plt.show()

if __name__ == '__main__':
    plt.rcParams.update({'font.size': 16})
    main()

Arbitrarily distributed on-chain random variables/Plot_distributions.py


import numpy as np
import random
import matplotlib.pyplot as plt
try:
    from scipy.special import comb
except:
    from scipy.misc import comb

def bernstein_poly(i, n, t):
    """
     The Bernstein polynomial of n, i as a function of t
    """
    return comb(n, i) * ( t**(n-i) ) * (1 - t)**i

def bezier_curve(points, nTimes=1000):
    """
       Given a set of control points, return the
       bezier curve defined by the control points.
       Control points should be a list of lists, or list of tuples
       such as [ [1,1],
                 [2,3],
                 [4,5], ..[Xn, Yn] ]
        nTimes is the number of time steps, defaults to 1000
        See http://processingjs.nihongoresources.com/bezierinfo/
    """

    nPoints = len(points)
    xPoints = np.array([p[0] for p in points])
    yPoints = np.array([p[1] for p in points])

    t = np.linspace(0.0, 1.0, nTimes)

    polynomial_array = np.array([bernstein_poly(i, nPoints-1, t) for i in range(0, nPoints)])

    xvals = np.dot(xPoints, polynomial_array)
    yvals = np.dot(yPoints, polynomial_array)

    return xvals, yvals

def nonlinear_transformation(x, prob=0.5):
    if random.random() >= prob:
        return x
    points = [[0, 0], [random.random(), random.random()], [random.random(), random.random()], [1, 1]]
    xvals, yvals = bezier_curve(points, nTimes=100000)
    if random.random() < 0.5:
        # Half change to get flip
        xvals = np.sort(xvals)
    else:
        xvals, yvals = np.sort(xvals), np.sort(yvals)
    nonlinear_x = np.interp(x, xvals, yvals)
    return nonlinear_x


from pyomo.environ import *

# Creates two vectors that represent the number of A and B ordered machines respectively per week.
# eg. consA[2] is the number of A ordered machines ordered for the second week.
consA = [0, 55, 55, 44, 0, 45, 45, 36, 35, 35]
consB = [0, 38, 38, 30, 0, 48, 48, 58, 57, 58]

week = range(1, 10)
weekZ = range(0, 10)

# Creates linear model.
mdl = ConcreteModel()

# Creates model's constraint list.
mdl.conList = ConstraintList()

# Adds two variable vectors each representing the production of A or B machines per week.
mdl.prodA = Var(week, domain=NonNegativeIntegers)
mdl.prodB = Var(week, domain=NonNegativeIntegers)

# Adds a binary variable vector that represents the state of the production line per week.
# 0->A machines, 1->B machines
mdl.deci = Var(weekZ, domain=Binary)
# Initializes state of production line's to A machines.
mdl.conList.add(expr = mdl.deci[0] == 0)

# Adds a binary variable vector that represents a change of the state of the production line's per week.
# 0->no-change, 1->change eg. chan[2] = 0 means that the state during the second week is the same as last week's.
mdl.chan = Var(week, domain=Binary)

# Adds two variable vectors that represent the stock of A or B machines respectively per week.
mdl.storA = Var(weekZ, domain=NonNegativeIntegers)
mdl.storB = Var(weekZ, domain=NonNegativeIntegers)
# Initializes stock of A machines to 125 and B machines to 143.
mdl.conList.add(expr = mdl.storA[0] == 125)
mdl.conList.add(expr = mdl.storB[0] == 143)

# Constraint setup loop.
for i in week:
    # Constraints the production of A machines per week to 100.
    mdl.conList.add(expr = mdl.prodA[i] <= 100)

    # Constraints the production of B machines per week to 80.
    mdl.conList.add(expr = mdl.prodB[i] <=  80)

    # Constraints the stock of A machines to be more than 80% of next week's order.
    mdl.conList.add(expr = mdl.storA[i-1] >= 0.8*consA[i])

    # Constraints the stock of B machines to be more than 80% of next week's order.
    mdl.conList.add(expr = mdl.storB[i-1] >= 0.8*consB[i])

    # Constraints the production to be able to fulfill current week's order of A machines.
    # Not needed since stock can't be negative.
    #mdl.conList.add(expr = mdl.prodA[i] + mdl.storA[i-1] >= consA[i])

    # Constraints the production to be able to fulfill current week's order of B machines.
    # Not needed since stock can't be negative.
    #mdl.conList.add(expr = mdl.prodB[i] + mdl.storB[i-1] >= consB[i])

    # Defines that next week's stock of A machines is equal to:
    # last week's stock + current week's produced machines - current week's ordered machines.
    mdl.conList.add(expr = mdl.storA[i] == mdl.storA[i-1] + mdl.prodA[i] - consA[i])

    # Defines that next week's stock of B machines is equal to:
    # last week's stock + current week's produced machines - current week's ordered machines.
    mdl.conList.add(expr = mdl.storB[i] == mdl.storB[i-1] + mdl.prodB[i] - consB[i])

    # Constraints the state of the production line to be either A or B using the big-M method.
    mdl.conList.add(expr = mdl.prodA[i] <= 1000*(1-mdl.deci[i]))
    mdl.conList.add(expr = mdl.prodB[i] <= 1000*mdl.deci[i])

    # Sets the value of the variable mdl.chan.
    mdl.conList.add(expr = mdl.deci[i] - mdl.deci[i-1] <= mdl.chan[i])
    mdl.conList.add(expr = mdl.deci[i-1] - mdl.deci[i] <= mdl.chan[i])

# Creates the model's cost function.
mdl.totalCost = Objective(expr =
    sum(mdl.prodA[i]*225 + mdl.prodB[i]*310 for i in week) +
    sum(mdl.storA[i]*0.00375*225 + mdl.storB[i]*0.195/52*310 for i in week) +
    500*sum (mdl.chan[i] for i in week),
    sense = minimize)

# Solves model with glpk solver.
SolverFactory('glpk').solve(mdl).write()

# Prints cost.
print("Cost = ", mdl.totalCost())

# Prints week number.
print("week#:", end = " ")
for i in weekZ:
    print("{:6}".format(weekZ[i]), end = " ")
print()

# Prints A machines order.
print("consA:", end = " ")
print("{:6}".format(''), end = " ")
for i in week:
    print("{:6}".format(consA[i]), end = " ")
print()

# Prints B machines order.
print("consB:", end = " ")
print("{:6}".format(''), end = " ")
for i in week:
    print("{:6}".format(consB[i]), end = " ")
print()

# Prints A machines production.
print("prodA:", end = " ")
print("{:6}".format(''), end = " ")
for i in week:
    print("{:6}".format(int(mdl.prodA[i].value)), end = " ")
print()

# Prints B machines production.
print("prodB:", end = " ")
print("{:6}".format(''), end = " ")
for i in week:
    print("{:6}".format(int(mdl.prodB[i].value)), end = " ")
print()

# Prints A machines stock.
print("storA:", end = " ")
for i in weekZ:
    print("{:6}".format(int(mdl.storA[i].value)), end = " ")
print()

# Prints B machines stock.
print("storB:", end = " ")
for i in weekZ:
    print("{:6}".format(int(mdl.storB[i].value)), end = " ")
print()

# Prints state of the production line. 0->A machines, 1->B machines
print("deci: ", end = " ")
for i in weekZ:
    print("{:6}".format(int(mdl.deci[i].value)), end = " ")
print()

# Prints change of the state of the production line. 0->no-change, 1->change
print("chan: ", end = " ")
print("{:6}".format(''), end = " ")
for i in week:
    print("{:6}".format(int(mdl.chan[i].value)), end = " ")
print()