from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt

# Let’s run the basic SIR model

# describe the model
def deriv(y, t, N, beta, k, delta, m ):
    S, E, I, R, D = y
    dSdt = -beta  * S * I / N
    dEdt = (beta  * S * I / N - k * E)
    dIdt = delta * E - k * I
    dRdt = k * I
    dDdt = m * I
    return  dSdt, dEdt, dIdt, dRdt, dDdt

# describe the parameters
N =  1000000 # population
m = 0.02    #percentage of the contaminated that dies
delta = 0.2
P = 0.9 #the effect of restrictions in percent, 1 = no effect 0 = full effect
beta = P * 2.5
k=1/6
S0, E0, I0, R0, D0= N-1, 1, 0, 0, 0 # initial conditions: one infected, rest susceptible

t = np.linspace(0, 99, 100) # Grid of time points (in days)
y0 = S0, E0, I0, R0, D0 # Initial conditions vector

    # Integrate the SIR equations over the time grid, t.
ret = odeint(deriv, y0, t, args=(N, beta, k, delta, m, ))
S, E, I, R , D= ret.T

def plotsir(t, S, E, I, R, D):
    f, ax = plt.subplots(1,1,figsize=(10,4))
    ax.plot(t, S, 'b', alpha=0.7, linewidth=2, label='Susceptible')
    ax.plot(t, E, 'y', alpha=0.7, linewidth=2, label='Exposed')
    ax.plot(t, I, 'r', alpha=0.7, linewidth=2, label='Infected')
    ax.plot(t, R, 'g', alpha=0.7, linewidth=2, label='Recovered')
    ax.plot(t, D , 'k', alpha=0.7, linewidth=2, label='Deceased')
    ax.set_xlabel('Time (days)')

    ax.yaxis.set_tick_params(length=0)
    ax.xaxis.set_tick_params(length=0)
    ax.grid(b=True, which='major', c='w', lw=2, ls='-')
    legend = ax.legend()
    legend.get_frame().set_alpha(0.5)
    for spine in ('top', 'right', 'bottom', 'left'):
        ax.spines[spine].set_visible(False)
    #plt.savefig('C:/Users/albin/OneDrive/Dokument/GitHub/SIR_model_SEE070/SIR_model_SEE070/Lecture2dec.png')
    plt.show()
plotsir(t, S, E, I, R, D)


import pandas as pd
import numpy as np
from scipy.stats import entropy

def total_entropy(df : pd.DataFrame) -> float:
    '''Gets the entropy of the entire dataset'''
    label = df.keys()[-1]

    # Gets the total entropy of the dataframe
    ent = entropy(df[label].value_counts(),base=2)
    return ent

def attribute_entropy(df : pd.DataFrame,attribute : str) -> float:
    '''Gets the entropy of a specific attribute'''
    label = df.keys()[-1]
    variables = df[attribute].unique()

    # Gets the entropy of the attribute
    ent = np.array([-(len(df[df[attribute] == variable]) / len(df)) * entropy(df[df[attribute]==variable][label].value_counts(),base=2) for variable in variables]).sum()
    return abs(ent)

def create_tree(df : pd.DataFrame) -> dict:
    '''ID3 decision tree creating algorithm. Requires df to be a pd.DataFrame, and that the label of the data is the last column'''
    label = df.keys()[-1]
    #Get attribute with maximum information gain
    node = df.keys()[:-1][np.argmax([total_entropy(df)-attribute_entropy(df,key) for key in df.keys()[:-1]])]

    #Get distinct value of that attribute
    att_values = df[node].unique()
    # Create the tree
    tree={}
    tree[node] = {}

    for att_value in att_values:
        # Create table with specific attribute
        subtable = df[df[node] == att_value].reset_index(drop=True)
        # Get how many outcomes there are for the attribute
        table_values,counts = np.unique(subtable[label],return_counts=True)

        # If there is only one outcome
        if len(counts) == 1:
            tree[node][att_value] = table_values[0]
        # If there is more than one outcome, need to create another node
        else:
            tree[node][att_value] = create_tree(subtable)
    return tree

def is_number(s) -> bool:
    try:
        float(s)
        return True
    except ValueError:
        return False

def go(dic : dict, last_key : None, current_level : None) -> None:
    '''Prints the tree, best used with the wrapper. ONLY WORKS ON TREES WITH NUMERIC ENTRIES'''
    for key, value in dic.items():
        if is_number(key):
            for i in range(current_level - 1):
                print("| ", end="")

            print(last_key, "= ", end="")
            print(key, ": ", end="")
        else:
            if current_level > 0:
                print("")

            current_level = current_level + 1

        if isinstance(value, dict):
            go(value, key, current_level)
        else:
            print(value)

def print_tree(tree : dict) -> None:
    '''Prints the tree created with ID3 algorithm
       ONLY PRINTS PROPERLY WITH TREES WITH NUMERIC NODE ENTRIES'''
    go(tree,None,0)

def classify_series(data : pd.Series,tree : dict):
    '''Given a series, returns the tree's classification of the series
       data is a pd.Series object, and tree is a dict created with the ID3 algorithm'''
    label = tree
    while isinstance(label,dict):
        key = list(label.keys())[0]
        value = data[key]
        label = label[key][value]
    return label


"""CNOT matrix."""
import numpy as np

def cnot() -> np.ndarray:
    r"""
    Produce the CNOT matrix [WikCNOT]_.

    The CNOT matrix is defined as

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

    Examples
    ==========

    >>> from toqito.matrices import cnot
    >>> cnot()
    [[1 0 0 0]
     [0 1 0 0]
     [0 0 0 1]
     [0 0 1 0]]

    References
    ==========
    .. [WikCNOT] Wikipedia: Controlled NOT gate
        https://en.wikipedia.org/wiki/Controlled_NOT_gate

    :return: The CNOT matrix.
    """
    return np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])


import numpy as np
from scipy.constants import mu_0, pi

def MagneticDipoleFields(txLoc, obsLoc, component, dipoleMoment=1.):
    """
        Calculate the vector potential of a set of magnetic dipoles
        at given locations 'ref. <http://en.wikipedia.org/wiki/Dipole#Magnetic_vector_potential>'

        :param numpy.ndarray txLoc: Location of the transmitter(s) (x, y, z)
        :param numpy.ndarray obsLoc: Where the potentials will be calculated (x, y, z)
        :param str component: The component to calculate - 'x', 'y', or 'z'
        :param numpy.ndarray dipoleMoment: The vector dipole moment (vertical)
        :rtype: numpy.ndarray
        :return: The vector potential each dipole at each observation location
    """

    if component=='x':
        dimInd = 0
    elif component=='y':
        dimInd = 1
    elif component=='z':
        dimInd = 2
    else:
        raise ValueError('Invalid component')

    txLoc = np.atleast_2d(txLoc)
    obsLoc = np.atleast_2d(obsLoc)
    dipoleMoment = np.atleast_2d(dipoleMoment)

    nFaces = obsLoc.shape[0]
    nTx = txLoc.shape[0]

    m = np.array(dipoleMoment).repeat(nFaces, axis=0)
    B = np.empty((nFaces, nTx))
    for i in range(nTx):
        dR = obsLoc - txLoc[i, np.newaxis].repeat(nFaces, axis=0)
        r = np.sqrt((dR**2).sum(axis=1))
        if dimInd == 0:
            B[:, i] = +(mu_0/(4*pi)) /(r**3) * (3*dR[:,2]*dR[:,0]/r**2)
        elif dimInd == 1:
            B[:, i] = +(mu_0/(4*pi)) /(r**3) * (3*dR[:,2]*dR[:,1]/r**2)
        elif dimInd == 2:
            B[:, i] = +(mu_0/(4*pi)) /(r**3) * (3*dR[:,2]**2/r**2-1)
        else:
            raise Exception("Not Implemented")
    if nTx == 1:
        return B.flatten()
    return B


import pandas as pd
import numpy as np

def IS(l,u,y,q):
    """
    calculation of the Interval Score

    l:  left interval limit
    u:  right interval limit
    y:  argument of IS
    q:  quantile level corresponding to limits

    Output
    IS value in y for given l,u,q
    """
    if y<l:
        return 2*(l-y) + q*(u-l)
    elif y>u:
        return 2*(y-u) + q*(u-l)
    else:
        return q*(u-l)

def WIS_calc(ci, target):
    """
    compute WIS in the point target in for 21 quantiles q_vals(below)

    ci:     array with empirical quantiles of the forecast
    target: ground truth at which to estimate WIS

    Output:
    WIS value
    """
    q_vals = [0.01,0.025] + list(np.arange(0.05, 0.5, 0.05))
    WIS_qpart = np.sum([IS(ci[i], ci[len(ci)-i-1], target, q_vals[i]) for i in range(len(q_vals))])
    WIS = (0.5*np.abs(ci[len(q_vals)]-target) + WIS_qpart)/(2*len(q_vals)+1)
    return WIS

def compute_WIS(df, model, target_type="day"):
    """
    create a DataFrame with WIS estimates for each time point in df
    df: DataFrame with empirical quantiles for the forecast and ground truth value
    model: model name
    target_type: "cumulative" or "day" depending on the data in df

    Output:
    DataFrame with WIS for each time point
    """
    WIS_ = []
    q = [0.01,0.025] + list(np.arange(0.05,1,0.05)) +  [0.975,0.99]
    q = [str(round(x,3)) for x in q]
    if target_type=="day":
        for index, row in df[q].iterrows():
            WIS_.append(WIS_calc(row,df["target_uptodate"][index]))
    if target_type=="cumulative":
        for index, row in df[q].iterrows():
            WIS_.append(WIS_calc(row, df["target_cumul"][index]))
    df[model+"WIS"] = WIS_
    return df

def coverage_point(ci, target):
    """
    average coverage over 21 quantiles
    ci:     array with empirical quantiles of the forecast
    target: ground truth

    Output:
    coverage averaged over 21 quantiles
    """
    q_vals = [0.01,0.025] + list(np.arange(0.05, 0.5, 0.05))
    coverage = np.sum([1*((ci[i]<=target)&(ci[len(ci)-i-1]>=target)) for i in range(len(q_vals))])
    return coverage

def compute_coverage(df, model, target_type="day"):

    """
    create a DataFrame with average coverage estimates for each time point in df
    df: DataFrame with empirical quantiles for the forecast and ground truth value
    model: model name
    target_type: "cumulative" or "day" depending on the data in df

    Output:
    DataFrame with average coverage for each time point
    """
    coverage = []
    q = [0.01,0.025] + list(np.arange(0.05,1,0.05)) +  [0.975,0.99]
    q = [str(round(x,3)) for x in q]
    if target_type=="day":
        for index, row in df[q].iterrows():
            coverage.append(coverage_point(row, df["target_uptodate"][index]))
    if target_type=="cumulative":
        for index, row in df[q].iterrows():
            coverage.append(coverage_point(row, df["target_cumul"][index]))

    df[model+"_coverage"] = coverage
    return df


'''
An example of optimization in SpaceFuncs with olympic rings
Let's search 5 points (1 from each ring) that form polygon with maximum area
'''
from SpaceFuncs import *
from FuncDesigner import *
from openopt import *
from numpy import linspace

centers = [(-2, 0), (0, 0), (2, 0), (1, -1), (-1, -1)]
colors = ['b', 'k', 'r', 'g', 'y']
radius = 0.9
circles = [Circle(center, radius, edgecolor = colors[i], linewidth = 3) for i, center in enumerate(centers)]

n = len(circles)
x = oovars(n)
y = oovars(n)

Points = [Point(x[i], y[i]) for i in range(n)]
P = Polygon(*Points)
objective = P.S # area

# we will deal with disks instead of circles because handling nonlinear equalities is more difficult then inequalities
# you can switch via d = circle.disk, c = disk.circle (without brackets)
constraints = [circles[i].disk.contains(Points[i], tol = 1e-5) for i in range(n)]

X, Y = linspace(-1, 1, n), [0]*n
startPoint = {x: X, y: Y}

p = NLP(objective, startPoint, constraints = constraints)

solver = 'scipy_cobyla' # requires SciPy installed; see openopt.org for other available solvers
p.debug = 1
r = p.maximize(solver)
''' Intel Atom 1.6 GHz result:
------------------------- OpenOpt 0.39 -------------------------
solver: scipy_cobyla   problem: unnamed    type: NLP   goal: maximum
 iter   objFunVal   log10(MaxResidual/ConTol)
    0  0.000e+00                      4.22
   10  1.063e+01                      2.08
   20  1.069e+01                      0.64
   30  1.068e+01                     -5.60
   40  1.068e+01                     -8.15
istop: 1000
Solver:   Time Elapsed = 5.0 	CPU Time Elapsed = 4.99
objFunValue: 10.684673 (feasible, max(residuals/requiredTolerances) = 7.09988e-09)
'''

# some basic plotting is available in SpaceFuncs API:
for circle in circles:
    circle.plot()

# For features beyond it use matplotlib:
import pylab
xx, yy = x(r).tolist(), y(r).tolist()
xx.append(xx[0])
yy.append(yy[0])
pylab.plot(xx, yy, marker = (5, 1, 0), markersize = 15)
pylab.xlim([-3, 3])
pylab.ylim([-3, 3])
pylab.show()
# see http://openopt.org/Image:olympic.png


import numpy as np
import scipy.sparse as sp
import time

def power_iteration(A, num_simulations):
	tol = 10**(-9)
	Ashape = A.get_shape()
        eigvec = np.random.rand(Ashape[0])
	eigval_old = np.dot(np.transpose(eigvec),A.dot(eigvec))/np.dot(np.transpose(eigvec),eigvec)
	for i in range(num_simulations):
        	# calculate the matrix-by-vector product Ab
        	eigvec1 = A.dot(eigvec)
        	# calculate the norm
        	eigvec1_norm = np.linalg.norm(eigvec1)
        	# re normalize the vector
        	eigvec = eigvec1 / eigvec1_norm

		eigval_new = np.dot(np.transpose(eigvec),A.dot(eigvec))/np.dot(np.transpose(eigvec),eigvec)
		if (abs(eigval_new-eigval_old)/eigval_new) < tol:
			return eigval_new
		eigval_old = eigval_new

	return eigval_new

def get_maxeigval(A):
	niter = 200000
	#maxeigval = power_iteration(A,niter)
	#UNSTABLE
	#rc = np.array([i for i in xrange(Ashape[0])])
	#Atemp = A - maxeigval*sp.coo_matrix((np.ones(Ashape[0]),(rc,rc)),shape=Ashape)
	#mineigval = power_iteration(Atemp,niter) + maxeigval

	return power_iteration(A,niter)

#stime=time.time()
#A = sp.load_npz('ham_kin.npz')
#print 'Time to load..'+str(time.time()-stime)+' seconds...'
#print 'Total time...'+str(time.time()-stime)+' seconds...'


import math
import numpy as np
import scipy.stats as st
import matplotlib.pyplot as plt

parameters = {
    'num_points': 500,
    'origin': (0, 0),
    'covariance_range': [-5, 5],
    'x_range': [-2, 2],
    'y_range': [-2, 2],
    'smoothness': 100j,
}

def generate_data():
    # generate random data
    cov = [
        np.random.uniform(parameters.get('covariance_range'), 2),
        np.random.uniform(parameters.get('covariance_range'), 2),
    ]
    data = np.random.multivariate_normal(
        parameters.get('origin'),
        cov,
        parameters.get('num_points'),
        check_valid='ignore',
    )

    # compute kernel density estimate
    XX, yy = data[:, 0], data[:, 1]
    X, y = np.mgrid[
        parameters.get('x_range')[0]:parameters.get('x_range')[1]:parameters.get('smoothness'),
        parameters.get('y_range')[0]:parameters.get('y_range')[1]:parameters.get('smoothness'),
    ]
    positions = np.vstack([X.ravel(), y.ravel()])
    kernel = st.gaussian_kde(np.vstack([XX, yy]))
    z = np.reshape(kernel(positions).T, X.shape)
    z[z==0] = 0.01

    # generate contour and get values
    (contour_X, contour_y, contour_z, x_text, y_text) = get_contour_data(X, y, z).values()

    return {
        'X': contour_X,
        'y': contour_y,
        'z': contour_z,
        'x_text': x_text,
        'y_text': y_text,
        'X_range': (X[0][0], X[-1][-1]),
        'y_range': (y[0][0], y[-1][-1]),
        'num_repeats': len(contour_X),
    }

'''
Title: get_contour_data()
Author: BR123
Date: 2016
Code version: N/A
Availability: https://docs.bokeh.org/en/latest/docs/gallery/histogram.html

The contour function below was used to plot values with matplotlib and scrape the generated plot values
'''

def get_contour_data(X, Y, Z):
    # generate matplotlib contour
    cs = plt.contour(X, Y, Z)
    X_points, y_points, z_points = [], [], np.array([])
    x_text, y_text = np.array([]), np.array([])

    # go through each contour level
    for i, level in enumerate(cs.collections):
        z = round(cs.get_array()[i], 2)

        # append points, text coordinates, text, and colour
        for path in level.get_paths():
            x, y = path.vertices[:, 0], path.vertices[:, 1]
            X_points.append(x.tolist())
            y_points.append(y.tolist())
            x_text = np.append(x_text, x[math.floor(len(x) / 2)])
            y_text = np.append(y_text, y[math.floor(len(y) / 2)])
            z_points = np.append(z_points, z)

    # create dict based on scraped data
    df = {
      'X': np.array(X_points, dtype=object),
      'y': np.array(y_points, dtype=object),
      'z': z_points,
      'x_text': x_text,
      'y_text': y_text,
    }

    return df


# Acceptance-Rejection method using Monte Carlo

import numpy as np
import matplotlib.pyplot as plt

def pdf(costh, P_mu):
    # define our probability density function
    return 0.5 * (1.0 - 1.0 / 3.0 * P_mu * costh)

N_measures=10000000
P_mu=1.0

x = np.random.uniform(-1.0, 1.0, size=N_measures)

x_axis = np.linspace(-1.0,1.0, 1000)
fmax= np.amax(pdf(x_axis,P_mu)) #find the maximum of the function

u = np.random.uniform(0, fmax, size= N_measures)

# we use a mask in order to reject the values we don't want
data_pdf = x[u < pdf(x,P_mu)]

# look out, the number of bins cannot be very large, otherwise the histogram doesn't fit the function.
plt.hist(data_pdf, bins=100, normed=True, histtype="step", color="black")
plt.plot(x_axis,pdf(x_axis,P_mu), color="orange", linewidth="1")
plt.xlabel(r'$cos \ \theta$')
plt.ylabel('f')
plt.xlim(-1.,1.)
plt.ylim(0.3,0.7)

print(len(data_pdf))
# plt.savefig(r"C:\Users\Aleix López\Desktop\acc_rej.jpg")
plt.show()

Projects/Probability and Monte Carlo Techniques/_2_acceptance_rejection_MC.py


import numpy as np

from optimizer import Optimizer

class Nesterov(Optimizer):
    """
    Accelerated gradient descent with constant learning rate.
    For details, see, e.g., http://mpawankumar.info/teaching/cdt-big-data/nesterov83.pdf

    Arguments:
        lr (float, optional): an estimate of the inverse smoothness constant
    """
    def __init__(self, lr=None, strongly_convex=False, mu=0, *args, **kwargs):
        super(Nesterov, self).__init__(*args, **kwargs)
        self.lr = lr
        if mu < 0:
            raise ValueError("Invalid mu: {}".format(mu))
        if strongly_convex and mu == 0:
            raise ValueError("""Mu must be larger than 0 for strongly_convex=True,
                             invalid value: {}""".format(mu))
        self.strongly_convex = strongly_convex
        if self.strongly_convex:
            self.mu = mu

    def step(self):
        if not self.strongly_convex:
            alpha_new = 0.5 * (1 + np.sqrt(1 + 4*self.alpha**2))
            self.momentum = (self.alpha - 1) / alpha_new
            self.alpha = alpha_new
        self.x_nest_old = self.x_nest.copy()
        self.grad = self.loss.gradient(self.x)
        self.x_nest = self.x - self.lr*self.grad
        self.x = self.x_nest + self.momentum*(self.x_nest-self.x_nest_old)

    def init_run(self, *args, **kwargs):
        super(Nesterov, self).init_run(*args, **kwargs)
        if self.lr is None:
            self.lr = 1 / self.loss.smoothness()
        self.x_nest = self.x.copy()
        if self.strongly_convex:
            kappa = (1/self.lr)/self.mu
            self.momentum = (np.sqrt(kappa)-1) / (np.sqrt(kappa)+1)
        else:
            self.alpha = 1.


import numpy as np
from scipy import linalg as la
import scipy.sparse as spar
from scipy.sparse import linalg as sparla

def problem1(l):
    '''
    print the answer to problem 1 in the Beam buckling lab.
    Inputs:
        l -- length of the beam in feet
    '''
    # initialize constants, do unit conversion
    r = 1.0
    E = 4.35*r*12**2*10**6
    L = l
    I = np.pi*r**4/4
    n = 1000
    h = L/n

    # build the tri-diagonal matrix
    diag = -2*np.ones(n)*E*I/h**2
    odiag = np.ones(n-1)*E*I/h**2
    band = np.zeros((2,n))
    band[0,1:] = -odiag
    band[1,:] = -diag

    # calculate and print smallest eigenvalue
    evals = la.eigvals_banded(band)
    print evals[0]

    # print the analytically calculated answer
    print np.pi**2*E*I/L**2

def problem2(l):
    '''
    print the solution to the second problem in Beam Buckling
    Inputs:
        l -- length of beam in feet
    '''
    # initialize constants, unit conversions
    r = 1.0
    E1 = r*12**2*10**7
    E2 = 4.35*r*12**2*10**6
    E3 = 5*r*12**2*10**5
    L = 20.0
    I = np.pi*r**4/4
    n = 100
    h = L/n

    # build the sparse matrix B
    b_diag = np.ones(n)
    b_diag[0:n/3] = E1*I/h**2
    b_diag[n/3:n/3+n/3] = E2*I/h**2
    b_diag[n/3+n/3:] = E3*I/h**2
    B = spar.spdiags(b_diag, np.array([0]), n, n, format='csc')

    # build the sparse matrix A
    diag = -2*np.ones(n)
    odiag = np.ones(n)
    A = spar.spdiags(np.vstack((-odiag, -odiag, -diag)),
                     np.array([-1,1,0]), n, n, format='csc')

    # calculate and print the smallest eigenvalue
    evals = sparla.eigs(B.dot(A), which='SM')
    print evals[0].min()


import numpy as np

class Activation:
    def func(self, x):
        raise NotImplementedError

    def dfunc(self, x):
        raise NotImplementedError

    def compile(self, comp, data):
        raise NotImplementedError

class ReLU(Activation):
    def __init__(self, leaky=0):
        self.__leaky = leaky

    def func(self, x):
        return np.maximum(x, self.__leaky * x)

    def dfunc(self, x):
        return np.where(x > 0, 1, self.__leaky)

    def compile(self, comp, data):
        if self.__leaky:
            return comp.compile(op='leaky_relu', data=data,
                                alpha=self.__leaky)
        else:
            return comp.compile(op='relu', data=data)

class Sigmoid(Activation):
    def func(self, x):
        return 1 / (1 + np.exp(-x))

    def dfunc(self, x):
        s = self.func(x)
        return s * (1 - s)

    def compile(self, comp, data):
        return comp.compile(op='sigmoid', data=data)


import parent_import
import numpy as np
import pytest
from MLibrary.utils import euclidean_distance, sigmoid, accuracy, mse

@pytest.mark.parametrize(
    'params, expected', [
        ([1, 3], 2),
        ([3, 1], 2),
        ([-3, -2], 1),
        ([-2, -3], 1),
        ([0, 0], 0)
    ]
)
def test_euclidean_distance(params, expected):
    assert euclidean_distance(params[0], params[1]) == expected

@pytest.mark.parametrize(
    'param, expected', [
        (5, 0.9933071490757153),
        (-5, 0.0066928509242848554),
        (0, 0.5)
    ]
)
def test_sigmoid(param, expected):
    assert sigmoid(param) == expected

@pytest.mark.parametrize(
    'params, expected', [
        ([[1, 1], [1, 1]], 1.0),
        ([[1, 0], [1, 1]], 0.5),
        ([[0, 0], [1, 1]], 0.0)
    ]
)
def test_accuracy(params, expected):
    assert accuracy(np.array(params[0]), np.array(params[1])) == expected

@pytest.mark.parametrize(
    'params, expected', [
        ([[0.5, 0.6, 0.7, 0.8], [0.5, 0.7, 0.6, 1.0]], 0.014999999999999993),
        ([[1, 1, 1, 1], [1, 1, 1, 1]], 0.0),
    ]
)
def test_mse(params, expected):
    assert mse(np.array(params[0]), np.array(params[1])) == expected


import numpy as np
from scipy.misc import derivative
from scipy.special import erf, softmax

class Activation:
	"""
	Class for several activation functions.
	Implemented activation functions are.

	- RELU
	- tanh (Hyperbolic Tangent)
	- Sigmoid (Logistic Function)
	- Linear
	- arctan
	- Error Function (erf)
	- Softmax
	"""

	@classmethod
	def relu(cls, x):
		return np.maximum(0, x)

	@classmethod
	def tanh(cls, x):
		return np.tanh(x)

	@classmethod
	def sigmoid(cls, x):
		return 1 / (1 + np.exp(-x))

	@classmethod
	def linear(cls, x):
		return x

	@classmethod
	def arctan(cls, x):
		return np.arctan(x)

	@classmethod
	def erf(cls, x):
		return erf(x)

	@classmethod
	def softmax(cls, x):
		return softmax(x)

class Loss:
	"""
	Class for several loss functions.
	Implemented loss functions are:

	- MSE (Mean Squared Error)
	- MAE (Mean Absolute Error)
	- Binary Cross Entropy
	"""

	@classmethod
	def mse(cls, t_p, o_p):
		m = t_p.shape[0]
		return (1/m) * (t_p - o_p)**2

	@classmethod
	def mae(cls, t_p, o_p):
		m = t_p.shape[0]
		return (1/m) * np.abs(t_p - o_p)

	@classmethod
	def binary_crossentropy(cls, t_p, o_p):
		return -(t_p*np.log(o_p) + (1-t_p)*np.log(1-o_p))

class NeuralNet:
	"""
	Simple feed-forward neural network model.

	Parameters
	----------
	layers : list<int> or list<tuple>
		List of layer sizes or list of tuples of layer sizes and activations.
	activations : str, function, list<str>, list<functions> or list of mixed str or functions, default=None
		Activation functions for each layer. If None, linear activation is applied.

	 Examples
	--------

	>>> my_layers = [10, 5, 1]
	>>> my_activations = ['relu', np.tanh, my_activation_func]
	>>> model = NeuralNet(my_layers, my_activations)
	>>> model.fit(X, y, **kwargs)
	>>>
	>>> # Make predicitons by using `predict` method
	>>> model.predict(X, **kwargs)
	"""

	def __init__(self, layers: list, activations=None):

		if activations is not None:
			assert len(layers) == len(activations)
		else:
			activations = ['linear' for _ in range(len(layers))]

		self.L = len(layers)

		check_all_tuple = all([isinstance(l, tuple) and len(l) == 2 for l in layers])
		if check_all_tuple:
			layers = [l[0] for l in layers]
			activations = [l[1] for l in layers]

		_activations = self._check_activations(activations)

		self.layers = layers.copy()
		self.activations = _activations

	@property
	def weights(self) -> (list, list):
		"""
		Return a tuple containing list of weights and biases
		"""
		return self.__W, self.__T

	@weights.setter
	def weights(self, val):
		raise ValueError("Weights does not support assignment.")

	def _check_activations(self, activations):
		_activations = activations.copy()
		check_type = [isinstance(a, str) or callable(a) for a in activations]

		if not check_type:
			raise ValueError("'activations' must be a list of callables or strings")

		for i, a in enumerate(_activations):
			if isinstance(a, str):
				try:
					activation = getattr(Activation, a)
				except:
					raise NotImplementedError(f"Activation function {a} is not implemented, try using a udf instead.")
				else:
					del _activations[i]
					_activations.insert(i, activation)

		return _activations

	def error(self, t_p, o_p):
		E_p = self.loss(t_p, o_p)
		return np.sum(E_p)

	def initialize_weights(self, layers: list):
		self.__W = [np.random.normal(0, 0.05, size=(layers[i], s)) for i, s in enumerate(layers[1:])]
		self.__T = [np.zeros((1, s)) for s in layers[1:]]

	@staticmethod
	def forward_pass(o_p, w, t, activation_func):
		net_p = np.matmul(o_p, w) + t
		o_p = activation_func(net_p)
		return o_p, net_p

	def forward_propagate(self, input_data):
		o_ps, net_ps = [input_data], [input_data]

		o_p = input_data

		for layer in range(self.L):
			w, t = self.__W[layer], self.__T[layer]
			o_p, net_p = self.forward_pass(o_p, w, t, activation_func=self.activations[layer])
			o_ps.append(o_p); net_ps.append(net_p)

		return o_ps, net_ps

	@staticmethod
	def backward_pass(f, net_p, delta_p, w):
		dnet_p = derivative(f, net_p)
		delta_w = np.matmul(delta_p, w.T)
		return dnet_p * delta_w

	def backward_propagate(self, o_ps, net_ps, t_p):
		m = t_p.shape[0]
		dW, dT = [], []

		# Initialize the recursion with the output layer
		do_p = derivative(lambda x: self.loss(t_p, x), o_ps[-1])
		dnet_p = derivative(self.activations[-1], net_ps[-1])
		delta_p = do_p * dnet_p

		for layer in reversed(range(self.L)):
			dw = np.matmul(o_ps[layer].T, delta_p)
			dt = np.sum(delta_p, axis=0, keepdims=True)

			if layer != 0: # No need to compute delta_0 (skipped for performance)
				w = self.__W[layer]
				delta_p = self.backward_pass(f=self.activations[layer], net_p=net_ps[layer], delta_p=delta_p, w=w)

			dW.insert(0, dw); dT.insert(0, dt)

		return dW, dT

	def update_weights(self, dW, dT, eta):
		i = 0
		while i < len(self.__W):
			self.__W[i] -= eta * dW[i]
			self.__T[i] -= eta * dT[i]

			i += 1

	def fit(self, X, y, epochs: int = 50, learning_rate: float = 0.1, loss='mse', verbose=1):
		input_size = X.shape[1]
		self.history = {}
		self.history['costs'] = []

		if callable(loss):
			self.loss = loss
		else:
			if isinstance(loss, str):
				self.loss = getattr(Loss, loss)
			else:
				raise NotImplementedError(f"Loss function {loss} is not implemented, try using a udf instead.")

		self.layers.insert(0, input_size)
		self.activations.insert(0, Activation.linear)
		self.initialize_weights(self.layers)

		y = y.reshape(len(y), 1)

		for epoch in range(epochs):
			o_ps, net_ps = self.forward_propagate(X)
			E = self.error(t_p=y, o_p=o_ps[-1])
			self.history['costs'].append(E)
			dW, dT = self.backward_propagate(o_ps, net_ps, y)
			self.update_weights(dW, dT, learning_rate)

			if verbose >= 1 and epoch % int(epochs / np.log(epochs)) == 0:
				print(f'Epoch {epoch} \t Cost: {E}')

	def predict(self, X, return_classes=False):
		o_ps, _ = self.forward_propagate(X)

		if return_classes:
			return np.vectorize(lambda x: 1 if x>0.5 else 0)(o_ps[-1])

		return o_ps[-1]


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

''' The problem is: dy/dt = f(t,y).'''

def f(t, y):
    return 0.5*y

# a,b: edges of the interval, w0:ititial value, h:step
def Euler(a, b, w0, h):
    n = ceil((b-a)/h)

    w, t = np.zeros(n+1), np.zeros(n+1)
    w[0], t[0] = w0, a

    for i in range(n):
        w[i+1] = w[i] + h*f(t[i], w[i])
        t[i+1] = t[i] + h

    return w, t

if __name__ == '__main__':

    a, b, w0, h = 0, 2, 1, 0.2

    print(Euler(a, b, w0, h)[0])

    # Visualization
    x = np.linspace(0, 2, 100)

    y = np.zeros(len(x))

    for j in range(len(x)):
        y[j] = exp(0.5*x[j])

    plt.plot(x, y, color="black", label="Solution")
    plt.plot(Euler(a, b, w0, h)[1], Euler(a, b, w0, h)[0], label="Euler")
    plt.xlabel("t")
    plt.ylabel("y")
    plt.legend()
    plt.show()


import numpy as np
import multiprocessing
from multiprocessing.dummy import freeze_support
import parmap
import matplotlib.pyplot as plt
from scipy import special

num_cores = multiprocessing.cpu_count()

"""
BER for BPSK in Rayleigh channel
y = hx + n
y: recived symbol
h: Rayleigh multipath channel, var=1
x: BPSK symbol(+1, -1)
n: Gaussian Noise

REF: http://www.dsplog.com/2008/08/10/ber-bpsk-rayleigh-channel/
"""

# number of BPSK symbol
n = 1_000_000

# SNR[dB] values
SNR = np.linspace(-3,35,100)

# generating BPSK symbol
def generate_BPSK(len_symbol):
    return np.random.randint(2, size=len_symbol) * 2 - 1

# Rayleigh Channel
def empirical_rayleigh_channel_BER(SNR, N):
    """
    SNR: Eb/No
    N: number of symbol
    return: n_error(number of errors)
    """
    x = generate_BPSK(N)
    n_error = []

    for i in range(0,len(SNR)):
        n = (1/np.sqrt(2)) * (np.random.randn(1,N) + 1j * np.random.randn(1,N)) # white gaussian noise, 0dB variance
        h = (1/np.sqrt(2)) * (np.random.randn(1,N) + 1j * np.random.randn(1,N)) # Rayleigh channel

        # Rx
        y = (h*x) + (10**(-SNR[i]/20))*n

        # equalization
        y_hat = y / h

        # recevier - hard Decision
        y_ = (np.real(y_hat) > 0).reshape((N,))
        decoding = lambda x_: 1 if x_ else -1
        decode_y = np.array([decoding(tmp_y) for tmp_y in y_])
        #print(f'decode: {decode_y}')

        # counting the errors
        n_error.append(list(decode_y != x).count(True))

    return n_error

def plot_figure(SNR, N, num_cores):
    """
    SNR: Eb/No
    N: number of symbol
    """
    #############################################################################################################
    # Multiprocessing for empirical rayleigh channel

    splited_data = np.array_split(SNR, num_cores)
    n_error = parmap.map(empirical_rayleigh_channel_BER, splited_data, N, pm_pbar=True, pm_processes=num_cores)
    n_error = np.array([item for l in n_error for item in l])

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

    empirical_BER = n_error / N
    theory_AWGN_BER = 0.5 * special.erfc(np.sqrt(10**(SNR/10)))
    SNR_ = 10**(SNR/10)
    thoery_rayleigh_BER = 0.5 * ( 1 - np.sqrt(SNR_/(SNR_ + 1)) )

    # plot
    plt.plot(SNR,theory_AWGN_BER, '-.g', label='AWGN-Theory')
    plt.plot(SNR, empirical_BER, '-XC4', label='Rayleigh-Empirical')
    plt.plot(SNR, thoery_rayleigh_BER, '-1C5', label='Ralyleigh-Thoery')
    plt.semilogy(); plt.xlim(-3,35); plt.ylim(1e-5, 0.5); plt.grid(); plt.legend()
    plt.ylabel('Bit Error Rate'); plt.xlabel('SNR[dB]')
    plt.title("BER for BPSK in Rayleigh channel")
    plt.show()

if __name__ == '__main__':
    freeze_support()
    plot_figure(SNR, n, num_cores)


import math
import pandas as pd
import numpy as np
from sklearn import preprocessing
from sklearn.linear_model import LogisticRegression
from BColors import BColors

FILE_PATH = "data/spam.csv"
THRESHOLD = 0.5
EPSILON = 1e-10

def read_file(filename):
    read_data = pd.read_csv(filename, header=None)
    return read_data

def standardize(df_to_std):
    x = df_to_std.iloc[:, 0:57].values
    y = df_to_std.iloc[:, 57]

    standard_scaler = preprocessing.StandardScaler()
    x_scaled = standard_scaler.fit_transform(x)

    df_x = pd.DataFrame(x_scaled)
    df_scaled = df_x.join(y)

    return df_scaled

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

def loss(p, y, w, lambda_reg):
    return -np.average(y * np.log(p + EPSILON) + (1 - y) * np.log(1 - p + EPSILON)) \
           + lambda_reg * np.sum(np.square(w)) / (2 * y.size)

def get_x(df):
    return df.iloc[:, 0:-1].values

def get_y(df):
    return df.iloc[:, -1].values

def predict_probability(x, b, w):
    z = np.dot(x, w) + b
    return sigmoid(z)

def train(df, iterations=500, learning_rate=10, lambda_reg=1, verbose=True):
    x = get_x(df)
    y = get_y(df)

    w_train = np.zeros(x.shape[1])
    b = 0

    for it in range(iterations):
        predictions = predict_probability(x, b, w_train)
        error = predictions - y

        gradient_b = np.average(error)
        gradient_w = np.dot(x.T, error)
        regularization = lambda_reg * w_train

        b -= learning_rate * gradient_b
        w_train -= learning_rate * (gradient_w + regularization) / y.size

        if verbose and it % (iterations/5) == 0:
            print("It. %4d\t|\tLoss: %0.4f" % (it, loss(predictions, y, w_train, lambda_reg)))
    return b, w_train

def predict(x, b, w, threshold=0.5):
    prob = predict_probability(x, b, w)
    return prob >= threshold

def accuracy(df, b, w, verbose=True):
    x = get_x(df)
    y = get_y(df)
    predictions = predict(x, b, w, threshold=THRESHOLD)

    acc = np.average(predictions == y)

    if verbose:
        print("Accuracy: %0.4f\n" % acc)
    return acc

def log_reg_sklearn(df):
    x = get_x(df)
    y = get_y(df)

    model = LogisticRegression(C=1e20)
    model.fit(x, y)

    predictions = model.predict(x)
    acc = np.average(predictions == y)
    print("sklearn: %0.4f" % acc)

    return model.coef_

def get_block_data(df, fold, tot_folds):
    fold_size = math.floor(df.shape[0] / tot_folds)

    start_index = fold_size * fold
    end_index = start_index + fold_size

    df_test = df.loc[start_index:end_index]
    df.drop(df.loc[start_index:end_index].index, inplace=True)

    return df, df_test

def shuffle(df):
    return df.sample(frac=1).reset_index(drop=True)

def cross_validation(df, l_r, l_reg, folds=10, verbose=True, sklearn=True):
    avg_acc = 0
    sk_acc = 0

    for i in range(folds):
        if verbose:
            print(BColors.OK_BLUE + "Fold number " + str(i+1) + BColors.END_C)
        tr_data, test_data = get_block_data(df.copy(), i, folds)
        b, w = train(
            tr_data,
            learning_rate=l_r,
            lambda_reg=l_reg,
            verbose=verbose
        )
        avg_acc += accuracy(test_data, b, w, verbose=verbose)

        if sklearn:
            x = get_x(tr_data)
            y = get_y(tr_data)

            model = LogisticRegression(C=1e20)
            model.fit(x, y)

            predictions = model.predict(get_x(test_data))
            sk_acc += np.average(predictions == get_y(test_data))

    avg_acc /= folds
    sk_acc /= folds

    print(BColors.OK_GREEN + "AVG acc: %0.4f" % avg_acc)
    print("AVG sklearn: %0.4f" % sk_acc)

if __name__ == '__main__':
    df = read_file(FILE_PATH)
    df = standardize(df)

    df.insert(0, 'A', pd.Series(np.ones(df.shape[0]), index=df.index))
    df = shuffle(df)

    cross_val = True

    if cross_val:

        cross_validation(
            df,
            l_r=0.1,
            l_reg=0.01
        )

        # for rate in [0.1, 0.05, 0.01, 0.005, 0.001]:
        #     for reg in [0.1, 0.05, 0.01, 0.005, 0.001]:
        #         print(str(rate), str(reg))
        #         cross_validation(
        #             df_shuffled,
        #             verbose=False,
        #             l_r=rate,
        #             l_reg=reg,
        #             sklearn=False
        #         )
    else:
        bias, weights = train(df)

        accuracy(df, bias, weights)

        w2 = log_reg_sklearn(df)


import matplotlib.pyplot as plt
from sympy import *
x = Symbol('x')

f=sin(2*x)/(x)

def minGradientSearch(f,cur_x):
	gamma = 0.01
	precision = 0.00001
	previous_step_size = 1
	max_iters = 10000
	iters = 0

	df = diff(f,x)
	error = []

	while (previous_step_size > precision) & (iters < max_iters):
	    prev_x = cur_x

	    # df/dx
	    cur_x -= gamma * df.evalf(subs={x:prev_x})

	    previous_step_size = abs(cur_x - prev_x)
	    iters+=1
	    error.append(previous_step_size)

	print( df.evalf(subs={x:cur_x}) )

	return cur_x,error

cur_x,error = minGradientSearch(f,.1)

print("Local minimum: " + str (cur_x) )

plt.plot(error)
plt.show()


##########################################################
# to turn off warnings                                   #
import warnings                                          #
warnings.filterwarnings('ignore')                        #
                                                         #
import os                                                #
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'                 #
                                                         #
import tensorflow.python.util.deprecation as deprecation #
deprecation._PRINT_DEPRECATION_WARNINGS = False          #
                                                         #
##########################################################

import numpy as np
import tensorflow as tf

#############
# example 1 #
x = tf.constant(3.0)

with tf.GradientTape() as tape:
    tape.watch(x) # If you want to differentiate about a constant tensor,
                  # you should use taple.watch() function
                  # to change the constant tensor like a variable tensor.
    y = (2 * x * x) + (3 * x) + 1

result = tape.gradient(y, x) # = dy/dx
print(result.numpy()) # 15.0

#############
# example 2 #
x = tf.Variable(3.0)

with tf.GradientTape() as tape:
    y = x * x

result = tape.gradient(y, x)
print(result.numpy()) # 6.0

#############
# example 3 #
def diff_func(x):
    x = tf.Variable(x)
    with tf.GradientTape() as tape:
        if x <= 0:
            y = 2 * x * x
        else:
            y = 3 * x * x
    result = tape.gradient(y, x)
    return result.numpy()

print(diff_func(-3.0)) # -12.0
print(diff_func( 0.0)) #   0.0
print(diff_func( 3.0)) #  18.0

#############
# example 4 #
x = tf.Variable(2.0)
y = tf.Variable(4.0)

with tf.GradientTape() as tape:
    f = 2 * x + 3 * y

result = tape.gradient(f, [x, y])
print(result[0].numpy(), result[1].numpy()) # 2.0 3.0


#!/usr/bin/env python3

# Fast Fourier Transform snippets
#
# Documentation:
# - Numpy implementation: http://docs.scipy.org/doc/numpy/reference/routines.fft.html
# - Scipy implementation: http://docs.scipy.org/doc/scipy/reference/fftpack.html

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

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 8))

# MAKE DATA ##########################################################

pattern = np.zeros((4, 4))
pattern[1:3,1:3] = 1

signal = np.tile(pattern, (2, 2))

ax1.imshow(signal, interpolation='nearest', cmap=cm.gray)
ax1.set_title("Original image")

# FOURIER TRANSFORM WITH NUMPY #######################################

# Do the fourier transform #############

transformed_signal = np.fft.fft2(signal)

ax2.imshow(abs(transformed_signal), interpolation='nearest', cmap=cm.gray)
ax2.set_title("Fourier coefficients before filtering")

#shifted_transformed_signal = np.fft.fftshift(transformed_signal)
#shifted_filtered_signal = np.fft.ifftshift(transformed_signal)

# Filter ###############################

max_value = np.max(abs(transformed_signal))
filtered_transformed_signal = transformed_signal * (abs(transformed_signal) > max_value*0.5)

ax3.imshow(abs(filtered_transformed_signal), interpolation='nearest', cmap=cm.gray)
ax3.set_title("Fourier coefficients after filtering")

# Do the reverse transform #############

filtered_signal = np.fft.ifft2(filtered_transformed_signal)

ax4.imshow(abs(filtered_signal), interpolation='nearest', cmap=cm.gray)
ax4.set_title("Filtered image")

# SAVE FILES ######################

#plt.savefig("fft2_basic_test.png")
#plt.savefig("fft2_basic_test.svg")
plt.savefig("fft2_basic_test.pdf")

# PLOT ############################

plt.show()


# -*- coding: utf8 -*-

import math
import pandas as pd
import numpy as np
import scipy.stats
from collections import defaultdict

def shannon_entropy(labels, base=2):
    """ Computes entropy of label distribution. """
    n_labels = len(labels)
    if n_labels <= 1:
        return 0

    value, counts = np.unique(labels, return_counts=True)
    probs = counts / n_labels
    n_classes = np.count_nonzero(probs)

    if n_classes <= 1:
        return 0

    ent = 0.

    # Compute entropy
    base = e if base is None else base
    for i in probs:
        ent -= i * math.log(i, base)

    return ent

# Input a pandas series
def scipy_entropy(data, base=2):
    # Calculate the probabilities.
    p_data = data.value_counts() / len(data)
    # Input probabilities to get the entropy.
    return scipy.stats.entropy(p_data, base=base)

def binary_entropy(nr_labels, nr_target_labels, base=2):
    p = nr_target_labels / nr_labels
    if p == 1.0 or p == 0.0:
        return 0.0
    return -p * math.log(p, base) - (1 - p) * math.log(1 - p, base)

def lzw_encode(symbols):
    """Compress a string to a list of output symbols."""

    # Build the dictionary.
    dictionary = {c:i for i, c in enumerate(set(symbols))}
    dict_size = len(dictionary)

    w = ""
    result = []
    seq_sizes = []
    for c in symbols:
        wc = w + c
        if wc in dictionary:
            w = wc
        else:
            result.append(dictionary[w])
            seq_sizes.append(len(w))
            # Add wc to the dictionary.
            dictionary[wc] = dict_size
            dict_size += 1
            w = c

    # Output the code for w.
    if w:
        seq_sizes.append(len(w))
        result.append(dictionary[w])

    return result, seq_sizes

def lzw_entropy(labels):
    _, seq_sizes = lzw_encode(labels)
    return (1 / (sum(seq_sizes) / len(seq_sizes))) * math.log(len(seq_sizes))

def compute_pi_fano(S, N):
    if S == 0.0 or N <= 1:
        return 1.0
    for p in np.arange(0.001, 1.000, 0.001):
        h = -p * math.log2(p) - (1 - p) * math.log2(1 - p)
        pi_fano = h + (1 - p) * math.log2(N - 1) - S
        if pi_fano <= 0.001:
            return p
    return 0.0


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import math

from glmatrix import *

import numpy as np

print("#########################################")

#np.set_printoptions(precision=3)
np.set_printoptions(formatter={'float': '{: 8.3f}'.format})
#np.set_printoptions(suppress=True)

location_v = vec3_create([5.0, 6.0, 7.0])
location_m = gl_mat4_from_translation(location_v)

print("Location Matrix")

print("")

#print(location_m)
transform_array = np.array(location_m, np.float32)
print(transform_array)

print("")

print("#########################################")

deg = -10
rad = (deg * math.pi / 180)
q_rot = gl_quat_from_x_rotation(rad)
rotation_m = mat4_create(None)
gl_mat4_from_quat(q_rot, rotation_m)

print("Rotation Matrix - X")

print("")
transform_array = np.array(q_rot, np.float32)
print(transform_array)

transform_array = np.array(rotation_m, np.float32)
print(transform_array)

print("")

print("#########################################")

deg = -10
rad = (deg * math.pi / 180)
q_rot = gl_quat_from_y_rotation(rad)
rotation_m = mat4_create(None)
gl_mat4_from_quat(q_rot, rotation_m)

print("Rotation Matrix - Y")

print("")
transform_array = np.array(q_rot, np.float32)
print(transform_array)

transform_array = np.array(rotation_m, np.float32)
print(transform_array)

print("")

print("#########################################")

deg = -10
rad = (deg * math.pi / 180)
q_rot = gl_quat_from_z_rotation(rad)
rotation_m = mat4_create(None)
gl_mat4_from_quat(q_rot, rotation_m)

print("Rotation Matrix - Z")

print("")
transform_array = np.array(q_rot, np.float32)
print(transform_array)

transform_array = np.array(rotation_m, np.float32)
print(transform_array)

print("")

print("#########################################")

displacement_v = vec3_create([10.0, 0.0, 0])
displacement_m = gl_mat4_from_translation(displacement_v)

print("Translate Matrix")

print("")

#print(displacement_m)
transform_array = np.array(displacement_m, np.float32)
print(transform_array)

print("")

print("#########################################")

print("Translate and Rotate")

ms = matstack()

print("")

print("")

ms.loadMatrix(location_m)

mvMatrix_tmp = mat4_create(None)
ms.multMatrix(displacement_m)
ms.getMatrix(mvMatrix_tmp)
transform_array = np.array(mvMatrix_tmp, np.float32)
print(transform_array)

print("")

mvMatrix = mat4_create(None)
ms.multMatrix(rotation_m)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("")

print("#########################################")

print("Rotate and Translate")

ms = matstack()

print("")

transform_array = np.array(rotation_m, np.float32)
print(transform_array)

print("")

transform_array = np.array(location_m, np.float32)
print(transform_array)

print("")

ms.loadMatrix(location_m)

mvMatrix_tmp = mat4_create(None)
ms.multMatrix(rotation_m)
ms.getMatrix(mvMatrix_tmp)
transform_array = np.array(mvMatrix_tmp, np.float32)
print(transform_array)

print("")

mvMatrix = mat4_create(None)
ms.multMatrix(displacement_m)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("")

print("#########################################")

print("#########################################")

print("Push / Pop version")

print("")

ms = matstack()

print("Initialise")

ms.loadMatrix(location_m)
mvMatrix = mat4_create(None)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Push")

mvMatrix = mat4_create(None)
ms.pushMatrix()
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Translate")

mvMatrix = mat4_create(None)
ms.multMatrix(displacement_m)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Pop")

mvMatrix = mat4_create(None)
ms.popMatrix()
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("#########################################")

print("Push")

mvMatrix = mat4_create(None)
ms.pushMatrix()
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Translate")

mvMatrix = mat4_create(None)
ms.multMatrix(displacement_m)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Rotate")

mvMatrix = mat4_create(None)
ms.multMatrix(rotation_m)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Pop")

mvMatrix = mat4_create(None)
ms.popMatrix()
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("#########################################")

print("Push")

mvMatrix = mat4_create(None)
ms.pushMatrix()
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Rotate")

mvMatrix = mat4_create(None)
ms.multMatrix(rotation_m)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Translate")

mvMatrix = mat4_create(None)
ms.multMatrix(displacement_m)
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)

print("Pop")

mvMatrix = mat4_create(None)
ms.popMatrix()
ms.getMatrix(mvMatrix)
transform_array = np.array(mvMatrix, np.float32)
print(transform_array)


import numpy as np
# http://iamtrask.github.io/2015/07/27/python-network-part2/
# compute sigmoid nonlinearity
def sigmoid(x):
    output = 1/(1+np.exp(-x))
    return output

# convert output of sigmoid function to its derivative
def sigmoid_output_to_derivative(output):
    return output*(1-output)

# input dataset
X = np.array([  [0,1],
                [0,1],
                [1,0],
                [1,0] ])

# output dataset
y = np.array([[0,0,1,1]]).T

# seed random numbers to make calculation
# deterministic (just a good practice)
np.random.seed(1)

# initialize weights randomly with mean 0
synapse_0 = 2*np.random.random((2,1)) - 1

for iter in xrange(10000): # even just 100 is pretty good...

    # forward propagation
    layer_0 = X
    layer_1 = sigmoid(np.dot(layer_0,synapse_0))

    # how much did we miss?
    layer_1_error = layer_1 - y

    # multiply how much we missed by the
    # slope of the sigmoid at the values in l1
    layer_1_delta = layer_1_error * sigmoid_output_to_derivative(layer_1)
    synapse_0_derivative = np.dot(layer_0.T,layer_1_delta)

    # update weights
    synapse_0 -= synapse_0_derivative

print "Output After Training:"
print layer_1


# implementation of seidel method

import numpy as np
import math_utils

enable_logs = False

def solve_linear(A, b, precision):
	nrows, ncolumns =  A.shape
	assert nrows == ncolumns

	x = np.zeros(ncolumns)
	while math_utils.vec_norm_1(np.matmul(A, x) - b) > precision:
		for i in range(ncolumns):
			sum1 = sum(A[i][j] * x[j] for j in range(i))
			sum2 = sum(A[i][j] * x[j] for j in range(i + 1, ncolumns))
			x[i] = (b[i] - sum1 - sum2) / A[i][i]
	return x


import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt

def model(x,t,a):
    dxdt = -a * x
    return dxdt

x0 = 100

t = np.linspace(0,100)

a = 0.1
x1 = odeint(model,x0,t,args=(a,))
a = 0.2
x2 = odeint(model,x0,t,args=(a,))
a = 0.5
x3 = odeint(model,x0,t,args=(a,))

plt.plot(t,x1,'r-',linewidth = 2,label = 'a = 0.1')
plt.plot(t,x2,'b--',linewidth = 2,label = 'a = 0.2')
plt.plot(t,x3,'g:',linewidth = 2,label = 'a = 0.5')

plt.xlabel('time')
plt.ylabel('x(t)')
plt.legend()
#plt.show()

plt.savefig('bibunn.png')


"""Plot a reaction-diffusion system to generate Turing Patterns

This system described by a PDE called the FitzHugh–Nagumo equation.
It is evaluated here using finite difference methods taken from:
https://ipython-books.github.io/124-simulating-a-partial-differential
-equation-reaction-diffusion-systems-and-turing-patterns/
"""

import numpy as np
import matplotlib.pyplot as plt
from finite_difference import laplacian, show_patterns
from gif_creator import create_gif

a = 2.8e-4
b = 5e-3
tau = .1
k = -.005

size = 100  # size of the 2D grid
dx = 2. / size  # space step

T = 10.0  # total time
dt = .001  # time step
n = int(T / dt)  # number of iterations
plot_num = 10

U = np.random.rand(size, size)
V = np.random.rand(size, size)

# U = np.ones((size, size)) + 0.01 * np.random.rand(size, size)
# V = np.ones((size, size)) + 0.01 * np.random.rand(size, size)

step_plot = n // plot_num
plot_count = 0
# Simulate the PDE with the finite difference method.
for i in range(n):
    # Compute the Laplacian of u and v.
    deltaU = laplacian(U, dx)
    deltaV = laplacian(V, dx)
    # Take the values of u and v inside the grid.
    Uc = U[1:-1, 1:-1]
    Vc = V[1:-1, 1:-1]
    # Update the variables.
    U[1:-1, 1:-1], V[1:-1, 1:-1] = \
        Uc + dt * (a * deltaU + Uc - Uc**3 - Vc + k),\
        Vc + dt * (b * deltaV + Uc - Vc) / tau
    # Neumann/zero flux conditions: derivatives at the edges
    # are null.
    for Z in (U, V):
        Z[0, :] = Z[1, :]
        Z[-1, :] = Z[-2, :]
        Z[:, 0] = Z[:, 1]
        Z[:, -1] = Z[:, -2]

    if i % step_plot == 0 and i < plot_num * step_plot:
        plot_count += 1
        plt.imshow(U, cmap=plt.cm.viridis,
              interpolation='bilinear',
              extent=[-1, 1, -1, 1])
        plt.title(f'$t={i * dt:.2f}$')
        plt.savefig(f"Images/Spatial_ODE/Turing_Evolution_Const2_{plot_count:04d}.png")
        #plt.show()

#create_gif("Images/Spatial_ODE/Turing_Evolution_Const2")

fig, ax = plt.subplots(1, 1, figsize=(8, 8))
show_patterns(U, ax=ax)
plt.title(f'$t={i * dt:.2f}$')
#plt.savefig("Images/Turing_Final_State.png")
#plt.show()


#! /usr/bin/env python
import unittest
import numpy as np
# Tested package
import baldor as br

class TestModule(unittest.TestCase):
  def test_unit(self):
    v = np.random.sample(3)
    u = br.vector.unit(v)
    np.testing.assert_allclose(np.linalg.norm(u), 1, atol=1e-8)

  def test_norm(self):
    v = np.random.sample(3)
    np.testing.assert_allclose(br.vector.norm(v), np.linalg.norm(v), atol=1e-8)

  def test_perpendicular(self):
    u = np.random.sample(3)
    v = br.vector.perpendicular(u)
    np.testing.assert_allclose(np.dot(u,v), 0, atol=1e-8)
    # Test zero vector
    def zero_vector_test():
      br.vector.perpendicular(np.zeros(3))
    self.assertRaises(ValueError, zero_vector_test)
    # Test Z_AXIS case
    u1 = br.Z_AXIS
    v1 = br.vector.perpendicular(u1)
    np.testing.assert_allclose(np.dot(u1,v1), 0, atol=1e-8)

  def test_skew(self):
    vector = np.random.sample(3)
    R = br.vector.skew(vector)
    np.testing.assert_allclose(-R, R.T)

  def test_transform_between_vectors(self):
    u0 = br.vector.unit( np.random.sample(3) )
    q = br.quaternion.random()
    T0 = br.quaternion.to_transform(q)
    v0 = br.vector.unit( np.dot(T0, np.hstack((u0, 1)))[:3] )
    T1 = br.vector.transform_between_vectors(u0, v0)
    v1 = br.vector.unit( np.dot(T1, np.hstack((u0, 1)))[:3] )
    np.testing.assert_allclose(v0, v1, atol=1e-8)
    # Test same vector
    T2 = br.vector.transform_between_vectors(u0, u0)
    np.testing.assert_allclose(T2, np.eye(4), atol=1e-8)


import numpy as np

def print_all():

    print("------------------------------------------------------")
    print("[START] - ELEMENT WISE OPERATIONS BETWEEN TWO MATRICES")
    print("------------------------------------------------------")

    # Create matrices
    x = np.array([[10, 20], [100, -10]])
    y = np.array([[5, 10], [-50, 10]])

    print(f"Matrices: \n-------\n  x = \n{x} \n\n y = \n{y}\n\n-------\n")

    print(f"Addition of x + y: \n-------\nnp.add(x,y):\n{np.add(x,y)}\n\n-------\n")

    print(f"Subtraction of x - y: \n-------\nnp.subtract(x,y):\n{np.subtract(x,y)}\n\n-------\n")

    print(f"Multiplication of x * y: \n-------\nnp.multiply(x,y):\n{np.multiply(x,y)}\n\n-------\n")

    print(f"Division of x / y: \n-------\nnp.divide(x,y):\n{np.divide(x,y)}\n\n-------\n")

    print("-------------------------------------------------------")
    print("[FINISH] - ELEMENT WISE OPERATIONS BETWEEN TWO MATRICES")
    print("-------------------------------------------------------")


import numpy as np
import csv

def smooth_noise_using_pca(noise_data, model_data, mean, numOfComponent=2):
    #noiseless_covmat = np.dot(noiseless - mean_noiseless, (noiseless - mean_noiseless).T)/noiseless.shape[1]
    covmat = np.dot(model_data - mean, (model_data - mean).T) / (model_data.shape[1])
    #np.cov(noiseless-mean_noiseless)
    eig_value, eig_vector = np.linalg.eig(covmat)
    #re-sort the eigenvalues & eigenvectors in descending order.
    reorder = np.argsort(eig_value)[::-1]
    eig_value = eig_value[reorder]
    eig_vector = eig_vector[:, reorder]
    # rotated using eigenvectors
    r = np.dot(eig_vector.T, (noise_data - mean))
    #r_covmat = np.cov(r)
    # filter out unselected components
    p = np.zeros(r.shape)
    p[:numOfComponent, :] = r[:numOfComponent, :]
    # undo rotation and translation to restore the data
    restored_x = np.dot(eig_vector, p) + mean
    return restored_x

# import the data from the csv.
data = {}
files_names = ["iris.csv", "dataI.csv", "dataII.csv", "dataIII.csv", "dataIV.csv", "dataV.csv"]
for name in files_names:
    with open('./homework3/hw3-data/'+name, newline='') as f:
        reader = csv.reader(f, delimiter=',', quoting=csv.QUOTE_NONNUMERIC)
        data[name] = []
        for row in reader:
            data[name].append(row)
        data[name].pop(0)

noiseless = np.array(data["iris.csv"]).T
mean_noiseless = np.mean(noiseless, axis=1, keepdims=True)

#noisy_set = np.array(data["dataI.csv"]).T
#noisy_set = np.array(data["iris.csv"]).T
#smooth_noise_using_pca(noisy_set, mean_noiseless, numOfComponent=2)

# print out the Mean square errors between noisy and noiseless data set into mse_1.csv
fobj = open('./homework3/minyuan3-numbers.csv', 'a+')
fobj.write('0N,1N,2N,3N,4N,0c,1c,2c,3c,4c\n')

for name in files_names[1:]:
    data_set = np.array(data[name]).T
    mean_noisy = np.mean(data_set, axis=1, keepdims=True)
    n = []
    c = []
    for numberOfComp in range(5):
        # calculate MSE using the mean and covariance matrix of the noiseless set
        restored_set = smooth_noise_using_pca(data_set, noiseless, mean_noiseless, numberOfComp)
        mean_square_error = np.sum((restored_set - noiseless)**2)/noiseless.shape[1]
        n.append(mean_square_error)
        # calculate MSE using the mean and covariance matrix of the noisy set
        restored_set = smooth_noise_using_pca(data_set, data_set, mean_noisy, numberOfComp)
        mean_square_error = np.sum((restored_set - noiseless)**2)/noiseless.shape[1]
        c.append(mean_square_error)
    fobj.write(','.join(np.array(n).astype(str)) + "," + ','.join(np.array(c).astype(str))+'\n')
    #fobj.write((','.join(['%.3f']*len(n))) % tuple(n) + "," + (','.join(['%.3f']*len(n))) % tuple(c) + '\n')

fobj.close()
# print out the reconstructed dataI.csv using 2 principal components.
fobj = open('./homework3/minyuan3-recon.csv', 'a+')
fobj.write('Sepal.Length, Sepal.Width, Petal.Length, Petal.Width\n')
dataI_set = np.array(data['dataI.csv']).T
mean_dataI = np.mean(dataI_set, axis=1, keepdims=True)
reconstructed_dataI = smooth_noise_using_pca(dataI_set, dataI_set, mean_dataI, 2).T
for row in reconstructed_dataI:
    fobj.write(', '.join(row.astype(str))+'\n')
fobj.close()

mean_square_error = np.sum((reconstructed_dataI.T - dataI_set)**2)/reconstructed_dataI.shape[0]
#mean_square_error_against_noiseless = np.sum((reconstructed_dataI.T - noiseless)**2)/noiseless.shape[1]


import numpy as np
from scipy.io import loadmat
import matplotlib.pyplot as pyplot
from scipy.stats import multivariate_normal

##################### LOADING DATA #####################
#Load from matlab
annots = loadmat("dataset1_G_noisy.mat")

#Training sets
trn_x = annots["trn_x"]
trn_x_class = annots["trn_x_class"]
trn_y = annots["trn_y"]
trn_y_class = annots["trn_y_class"]

#Testing sets
tst_xy = annots["tst_xy"]
tst_xy_class = annots["tst_xy_class"]
tst_xy_126 = annots["tst_xy_126"]
tst_xy_126_class = annots["tst_xy_126_class"]

#Normality tests and it is normal
pyplot.hist(trn_x[:,0])
pyplot.hist(trn_y[:,0])
pyplot.show()

##################### EXERCISE A #####################
#Both are normal so estimate priors
n = trn_x_class.size + trn_y_class.size
priorx = trn_x_class.size / n
priory = trn_y_class.size / n

#Calc means
mx = trn_x.mean(0)
my = trn_y.mean(0)

#Estimate covariance matrix and transpose to keep rank at 2
cx = np.cov(trn_x.T)
cy = np.cov(trn_y.T)

#Calc multivariate norm
mnx = multivariate_normal(mx, cx)
mny = multivariate_normal(my, cy)

#Calculate posteriori
postx = priorx * mnx.pdf(tst_xy)
posty = priory * mny.pdf(tst_xy)

#Classify predictions
p = np.where(postx - posty >= 0, 1, 2) # if its lower than 0 value is set 1 and 2 otherwise
s = tst_xy_class.squeeze() #throws error if value is larger than 1 so keeps only below 1
a = np.sum(p == s) / s.size
print("Acuracy of a: {:.8f}".format(a))

##################### EXERCISE B #####################
#Priors not required since its uniform

#Calc posteriori
postx = mnx.pdf(tst_xy_126)
posty = mny.pdf(tst_xy_126)

#Classify predictions
p = np.where(postx - posty >= 0, 1, 2)
s = tst_xy_126_class.squeeze()
a = np.sum(p == s) / s.size
print("Acuracy of b: {:.8f}".format(a))

##################### EXERCISE C #####################
##Setting new priors
priorx = 0.9
priory = 0.1

#Calc posteriori
postx = priorx * mnx.pdf(tst_xy_126)
posty = priory * mny.pdf(tst_xy_126)

#Classify predictions
p = np.where(postx - posty >= 0, 1, 2)
s = tst_xy_126_class.squeeze()
a = np.sum(p == s) / s.size
print("Acuracy of c: {:.8f}".format(a))


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from py_expression_eval import Parser
import math
parser = Parser()

class Int_F(object):
    """
    Integrate a function f(x) from a to b
    """

    def __init__(self, f_expr):
        self.f_raw = parser.parse(f_expr)
        self. ans = None
        self.int_pts = []

    @property
    def sol(self):
        return self.ans

    def f(self, x):
        return self.f_raw.evaluate({'x': x})

    def trapeziod(self, interval, n):
        """
        order: 1
        coeffs h/2 * [1 1]
        """
        a,b = interval
        h = (b-a)/(n-1)
        x = np.linspace(a,b,n)

        self.ans = (h/2)*sum([self.f(x[i]) + self.f(x[i+1]) for i in range(0,n-1,1)])

    def simpsons(self, interval,n):
        """
        order: 2
        coeffs h/3 * [1 4 1]
        """
        if (n-1)%2 != 0:
            raise Exception('Number of points must be of the form 2k+1')

        a,b = interval
        h = (b-a)/(n-1)
        x = np.linspace(a,b,n)

        self.ans = (h/3)*sum([self.f(x[i]) + 4*self.f(x[i+1]) + self.f(x[i+2]) for i in range(0,n-2,2)])

    def simpsons_3_8(self, interval, n):
        """
        order: 3
        coeffs 3h/8 * [1 3 3 1]
        """
        if (n-1)%3 != 0:
            raise Exception('Number of points must be of the form 3k+1')

        a,b = interval
        h = (b-a)/(n-1)
        x = np.linspace(a,b,n)

        self.ans = (3*h/8)*sum([self.f(x[i]) + 3*self.f(x[i+1]) + 3*self.f(x[i+2]) + self.f(x[i+3]) for i in range(0,n-3,3)])

    def gauss_legendre(self, interval, n):
        """
        gauss-legendre method
        """
        legendre_poly = np.polynomial.legendre.Legendre([0]*n + [1])
        legendre_poly_diff = legendre_poly.deriv()
        roots = legendre_poly.roots()
        weight = [ 2/((1-x**2)*legendre_poly_diff(x)**2) for x in roots]

        a,b = interval
        self.ans = ((b-a)/2) * sum([weight[i]*self.f(((b-a)/2)*x + (b+a)/2) for i,x in enumerate(roots)])
        return [((b-a)/2)*x + (b+a)/2 for x in roots]

    def integrate(self, from_, to_, num_pts, method):
        """Wrapper method to integrate the function"""
        range = (from_,to_)
        n = num_pts
        assert (n >= 2)

        self.range_ = (from_, to_)

        if method.lower() == 'trap':
            self.int_pts = np.linspace(from_, to_, num_pts)
            self.trapeziod(range,n)
        elif method.lower() == 'simp':
            self.int_pts = np.linspace(from_, to_, num_pts)
            self.simpsons(range,n)
        elif method.lower() == 'simp_3/8':
            self.int_pts = np.linspace(from_, to_, num_pts)
            self.simpsons_3_8(range,n)
        elif method.lower() == 'gauss_legendre':
            self.int_pts =  self.gauss_legendre(range,n)
        else:
            raise Exception(f'method `{method}` not implemented')

    def table(self, num_pts=500):
        xs = np.linspace(*self.range_, num_pts)
        ys = [self.f(x) for x in xs]
        return xs, ys

    def visualize(self, num_pts=500):
        xs, ys = self.table(num_pts)

        plt.plot(xs, ys, c='b', zorder=2)
        plt.plot(xs, [0 for x in xs], c='k', zorder=1)
        for pt in self.int_pts:
            minpt = min(0, self.f(pt))
            maxpt = max(0,self.f(pt))
            plt.vlines( self.int_pts,
                        [min(0,self.f(x)) for x in self.int_pts],
                        [max(0,self.f(x)) for x in self.int_pts],
                        colors=['k' for _ in self.int_pts],
                        zorder=3)
        plt.show()


import unittest

from scipy.stats import ttest_ind

from utils import prepare_data

class TestExercise1_06(unittest.TestCase):
    def setUp(self):
        self.data = prepare_data()

    def test_means(self):
        dows = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
        target_means = [9.248, 7.981, 7.147, 4.424, 5.125]
        target_std = [15.973, 18.027, 13.268, 4.266, 7.911]
        for dow, trg_mean, trg_std in zip(dows, target_means, target_std):
            mask = self.data["Day of the week"] == dow
            hours = self.data["Absenteeism time in hours"][mask]
            mean = hours.mean()
            stddev = hours.std()

            self.assertAlmostEqual(mean, trg_mean, places=2)
            self.assertAlmostEqual(stddev, trg_std, places=2)

    def test_average_duration(self):
        thursday_mask = self.data["Day of the week"] == "Thursday"
        july_mask = self.data["Month of absence"] == "July"

        thursday_data = self.data["Absenteeism time in hours"] \
            [thursday_mask]
        no_thursday_data = self.data["Absenteeism time in hours"] \
            [~thursday_mask]
        july_data = self.data["Absenteeism time in hours"][july_mask]
        no_july_data = self.data["Absenteeism time in hours"][~july_mask]

        thursday_res = ttest_ind(thursday_data, no_thursday_data)
        july_res = ttest_ind(july_data, no_july_data)

        self.assertAlmostEqual(thursday_res[0], -2.307, places=2)
        self.assertAlmostEqual(thursday_res[1], 0.021, places=2)
        self.assertAlmostEqual(july_res[0], 2.605, places=2)
        self.assertAlmostEqual(july_res[1], 0.009, places=2)


import pandas as pd
import numpy as np

from types import SimpleNamespace

class LDAClassifier:
    def __init__(self):
        self._coef = []

    def fit(self, x, y):
        mu1 = np.mean(x[y == 1], axis=0)
        mu2 = np.mean(x[y == 0], axis=0)
        mu_diff = mu1 - mu2
        s1 = np.cov(x[y == 1].T)
        s2 = np.cov(x[y == 0].T)
        s_w = s1 + s2
        self._coef = np.matmul(np.linalg.inv(s_w), mu_diff)
        return self

    def predict(self, x):
        return np.dot(x, self._coef) >= 0

    def score(self, x, y_true):
        return 1 - np.sum((self.predict(x) - y_true) ** 2) / x.shape[0]

def load_data(train_file, test_file):
    train_df = pd.read_csv(train_file)
    test_df = pd.read_csv(test_file)

    x_train = train_df.iloc[:, :-1]
    y_train = train_df.iloc[:, -1]

    x_test = test_df.iloc[:, :-1]
    y_test = test_df.iloc[:, -1]

    return {
        "x_train": x_train.values,
        "y_train": y_train.values,
        "x_test": x_test.values,
        "y_test": y_test.values
    }

def __main__():
    data = SimpleNamespace(**load_data("train.csv", "test.csv"))
    clf = LDAClassifier()
    clf.fit(data.x_train, data.y_train)
    print("TRAIN ACCURACY =", clf.score(data.x_train, data.y_train))
    # train_pred = clf.predict(data.x_train)
    # print(train_pred)
    print("TEST ACCURACY =", clf.score(data.x_test, data.y_test))
    # test_pred = clf.predict(data.x_test)
    # print(test_pred)

__main__()


import numpy as np
from numba import jit
from math import factorial, log
from sklearn.neighbors import KDTree
from scipy.signal import periodogram, welch

from .utils import _embed

all = ['perm_entropy', 'spectral_entropy', 'svd_entropy', 'app_entropy',
       'sample_entropy']

def perm_entropy(x, order=3, delay=1, normalize=False):
    """Permutation Entropy.

    Parameters
    ----------
    x : list or np.array
        One-dimensional time series of shape (n_times)
    order : int
        Order of permutation entropy
    delay : int
        Time delay
    normalize : bool
        If True, divide by log2(order!) to normalize the entropy between 0
        and 1. Otherwise, return the permutation entropy in bit.

    Returns
    -------
    pe : float
        Permutation Entropy

    Notes
    -----
    The permutation entropy is a complexity measure for time-series first
    introduced by Bandt and Pompe in 2002 [1]_.

    The permutation entropy of a signal :math:`x` is defined as:

    .. math:: H = -\\sum p(\\pi)log_2(\\pi)

    where the sum runs over all :math:`n!` permutations :math:`\\pi` of order
    :math:`n`. This is the information contained in comparing :math:`n`
    consecutive values of the time series. It is clear that
    :math:`0 ≤ H (n) ≤ log_2(n!)` where the lower bound is attained for an
    increasing or decreasing sequence of values, and the upper bound for a
    completely random system where all :math:`n!` possible permutations appear
    with the same probability.

    The embedded matrix :math:`Y` is created by:

    .. math:: y(i)=[x_i,x_{i+delay}, ...,x_{i+(order-1) * delay}]

    .. math:: Y=[y(1),y(2),...,y(N-(order-1))*delay)]^T

    References
    ----------
    .. [1] Bandt, Christoph, and Bernd Pompe. "Permutation entropy: a
           natural complexity measure for time series." Physical review letters
           88.17 (2002): 174102.

    Examples
    --------
    1. Permutation entropy with order 2

        >>> from entropy import perm_entropy
        >>> x = [4, 7, 9, 10, 6, 11, 3]
        >>> # Return a value in bit between 0 and log2(factorial(order))
        >>> print(perm_entropy(x, order=2))
            0.918
    2. Normalized permutation entropy with order 3

        >>> from entropy import perm_entropy
        >>> x = [4, 7, 9, 10, 6, 11, 3]
        >>> # Return a value comprised between 0 and 1.
        >>> print(perm_entropy(x, order=3, normalize=True))
            0.589
    """
    x = np.array(x)
    ran_order = range(order)
    hashmult = np.power(order, ran_order)
    # Embed x and sort the order of permutations
    sorted_idx = _embed(x, order=order, delay=delay).argsort(kind='quicksort')
    # Associate unique integer to each permutations
    hashval = (np.multiply(sorted_idx, hashmult)).sum(1)
    # Return the counts
    _, c = np.unique(hashval, return_counts=True)
    # Use np.true_divide for Python 2 compatibility
    p = np.true_divide(c, c.sum())
    pe = -np.multiply(p, np.log2(p)).sum()
    if normalize:
        pe /= np.log2(factorial(order))
    return pe

def spectral_entropy(x, sf, method='fft', nperseg=None, normalize=False):
    """Spectral Entropy.

    Parameters
    ----------
    x : list or np.array
        One-dimensional time series of shape (n_times)
    sf : float
        Sampling frequency
    method : str
        Spectral estimation method ::

        'fft' : Fourier Transform (via scipy.signal.periodogram)
        'welch' : Welch periodogram (via scipy.signal.welch)

    nperseg : str or int
        Length of each FFT segment for Welch method.
        If None, uses scipy default of 256 samples.
    normalize : bool
        If True, divide by log2(psd.size) to normalize the spectral entropy
        between 0 and 1. Otherwise, return the spectral entropy in bit.

    Returns
    -------
    se : float
        Spectral Entropy

    Notes
    -----
    Spectral Entropy is defined to be the Shannon Entropy of the Power
    Spectral Density (PSD) of the data:

    .. math:: H(x, sf) =  -\\sum_{f=0}^{f_s/2} PSD(f) log_2[PSD(f)]

    Where :math:`PSD` is the normalised PSD, and :math:`f_s` is the sampling
    frequency.

    References
    ----------
    .. [1] Inouye, T. et al. (1991). Quantification of EEG irregularity by
       use of the entropy of the power spectrum. Electroencephalography
       and clinical neurophysiology, 79(3), 204-210.

    Examples
    --------
    1. Spectral entropy of a pure sine using FFT

        >>> from entropy import spectral_entropy
        >>> import numpy as np
        >>> sf, f, dur = 100, 1, 4
        >>> N = sf * duration # Total number of discrete samples
        >>> t = np.arange(N) / sf # Time vector
        >>> x = np.sin(2 * np.pi * f * t)
        >>> print(np.round(spectral_entropy(x, sf, method='fft'), 2)
            0.0

    2. Spectral entropy of a random signal using Welch's method

        >>> from entropy import spectral_entropy
        >>> import numpy as np
        >>> np.random.seed(42)
        >>> x = np.random.rand(3000)
        >>> print(spectral_entropy(x, sf=100, method='welch'))
            9.939

    3. Normalized spectral entropy

        >>> print(spectral_entropy(x, sf=100, method='welch', normalize=True))
            0.995
    """
    x = np.array(x)
    # Compute and normalize power spectrum
    if method == 'fft':
        _, psd = periodogram(x, sf)
    elif method == 'welch':
        _, psd = welch(x, sf, nperseg=nperseg)
    psd_norm = np.divide(psd, psd.sum())
    se = -np.multiply(psd_norm, np.log2(psd_norm)).sum()
    if normalize:
        se /= np.log2(psd_norm.size)
    return se

def svd_entropy(x, order=3, delay=1, normalize=False):
    """Singular Value Decomposition entropy.

    Parameters
    ----------
    x : list or np.array
        One-dimensional time series of shape (n_times)
    order : int
        Order of permutation entropy
    delay : int
        Time delay
    normalize : bool
        If True, divide by log2(order!) to normalize the entropy between 0
        and 1. Otherwise, return the permutation entropy in bit.

    Returns
    -------
    svd_e : float
        SVD Entropy

    Notes
    -----
    SVD entropy is an indicator of the number of eigenvectors that are needed
    for an adequate explanation of the data set. In other words, it measures
    the dimensionality of the data.

    The SVD entropy of a signal :math:`x` is defined as:

    .. math::
        H = -\\sum_{i=1}^{M} \\overline{\\sigma}_i log_2(\\overline{\\sigma}_i)

    where :math:`M` is the number of singular values of the embedded matrix
    :math:`Y` and :math:`\\sigma_1, \\sigma_2, ..., \\sigma_M` are the
    normalized singular values of :math:`Y`.

    The embedded matrix :math:`Y` is created by:

    .. math:: y(i)=[x_i,x_{i+delay}, ...,x_{i+(order-1) * delay}]

    .. math:: Y=[y(1),y(2),...,y(N-(order-1))*delay)]^T

    Examples
    --------
    1. SVD entropy with order 2

        >>> from entropy import svd_entropy
        >>> x = [4, 7, 9, 10, 6, 11, 3]
        >>> # Return a value in bit between 0 and log2(factorial(order))
        >>> print(svd_entropy(x, order=2))
            0.762

    2. Normalized SVD entropy with order 3

        >>> from entropy import svd_entropy
        >>> x = [4, 7, 9, 10, 6, 11, 3]
        >>> # Return a value comprised between 0 and 1.
        >>> print(svd_entropy(x, order=3, normalize=True))
            0.687
    """
    x = np.array(x)
    mat = _embed(x, order=order, delay=delay)
    W = np.linalg.svd(mat, compute_uv=False)
    # Normalize the singular values
    W /= sum(W)
    svd_e = -np.multiply(W, np.log2(W)).sum()
    if normalize:
        svd_e /= np.log2(order)
    return svd_e

def _app_samp_entropy(x, order, metric='chebyshev', approximate=True):
    """Utility function for `app_entropy`` and `sample_entropy`.
    """
    _all_metrics = KDTree.valid_metrics
    if metric not in _all_metrics:
        raise ValueError('The given metric (%s) is not valid. The valid '
                         'metric names are: %s' % (metric, _all_metrics))
    phi = np.zeros(2)
    r = 0.2 * np.std(x, axis=-1, ddof=1)

    # compute phi(order, r)
    _emb_data1 = _embed(x, order, 1)
    if approximate:
        emb_data1 = _emb_data1
    else:
        emb_data1 = _emb_data1[:-1]
    count1 = KDTree(emb_data1, metric=metric).query_radius(emb_data1, r,
                                                           count_only=True
                                                           ).astype(np.float64)
    # compute phi(order + 1, r)
    emb_data2 = _embed(x, order + 1, 1)
    count2 = KDTree(emb_data2, metric=metric).query_radius(emb_data2, r,
                                                           count_only=True
                                                           ).astype(np.float64)
    if approximate:
        phi[0] = np.mean(np.log(count1 / emb_data1.shape[0]))
        phi[1] = np.mean(np.log(count2 / emb_data2.shape[0]))
    else:
        phi[0] = np.mean((count1 - 1) / (emb_data1.shape[0] - 1))
        phi[1] = np.mean((count2 - 1) / (emb_data2.shape[0] - 1))
    return phi

@jit('f8(f8[:], i4, f8)', nopython=True)
def _numba_sampen(x, mm=2, r=0.2):
    """
    Fast evaluation of the sample entropy using Numba.
    """
    n = x.size
    n1 = n - 1
    mm += 1
    mm_dbld = 2 * mm

    # Define threshold
    r *= x.std()

    # initialize the lists
    run = [0] * n
    run1 = run[:]
    r1 = [0] * (n * mm_dbld)
    a = [0] * mm
    b = a[:]
    p = a[:]

    for i in range(n1):
        nj = n1 - i

        for jj in range(nj):
            j = jj + i + 1
            if abs(x[j] - x[i]) < r:
                run[jj] = run1[jj] + 1
                m1 = mm if mm < run[jj] else run[jj]
                for m in range(m1):
                    a[m] += 1
                    if j < n1:
                        b[m] += 1
            else:
                run[jj] = 0
        for j in range(mm_dbld):
            run1[j] = run[j]
            r1[i + n * j] = run[j]
        if nj > mm_dbld - 1:
            for j in range(mm_dbld, nj):
                run1[j] = run[j]

    m = mm - 1

    while m > 0:
        b[m] = b[m - 1]
        m -= 1

    b[0] = n * n1 / 2
    a = np.array([float(aa) for aa in a])
    b = np.array([float(bb) for bb in b])
    p = np.true_divide(a, b)
    return -log(p[-1])

def app_entropy(x, order=2, metric='chebyshev'):
    """Approximate Entropy.

    Parameters
    ----------
    x : list or np.array
        One-dimensional time series of shape (n_times)
    order : int (default: 2)
        Embedding dimension.
    metric : str (default: chebyshev)
        Name of the metric function used with
        :class:`~sklearn.neighbors.KDTree`. The list of available
        metric functions is given by: ``KDTree.valid_metrics``.

    Returns
    -------
    ae : float
        Approximate Entropy.

    Notes
    -----
    Original code from the mne-features package.

    Approximate entropy is a technique used to quantify the amount of
    regularity and the unpredictability of fluctuations over time-series data.

    Smaller values indicates that the data is more regular and predictable.

    The value of :math:`r` is set to :math:`0.2 * \\text{std}(x)`.

    Code adapted from the mne-features package by Jean-Baptiste Schiratti
    and Alexandre Gramfort.

    References
    ----------
    .. [1] Richman, J. S. et al. (2000). Physiological time-series analysis
           using approximate entropy and sample entropy. American Journal of
           Physiology-Heart and Circulatory Physiology, 278(6), H2039-H2049.

    1. Approximate entropy with order 2.

        >>> from entropy import app_entropy
        >>> import numpy as np
        >>> np.random.seed(1234567)
        >>> x = np.random.rand(3000)
        >>> print(app_entropy(x, order=2))
            2.075
    """
    phi = _app_samp_entropy(x, order=order, metric=metric, approximate=True)
    return np.subtract(phi[0], phi[1])

def sample_entropy(x, order=2, metric='chebyshev'):
    """Sample Entropy.

    Parameters
    ----------
    x : list or np.array
        One-dimensional time series of shape (n_times)
    order : int (default: 2)
        Embedding dimension.
    metric : str (default: chebyshev)
        Name of the metric function used with KDTree. The list of available
        metric functions is given by: `KDTree.valid_metrics`.

    Returns
    -------
    se : float
        Sample Entropy.

    Notes
    -----
    Sample entropy is a modification of approximate entropy, used for assessing
    the complexity of physiological time-series signals. It has two advantages
    over approximate entropy: data length independence and a relatively
    trouble-free implementation. Large values indicate high complexity whereas
    smaller values characterize more self-similar and regular signals.

    Sample entropy of a signal :math:`x` is defined as:

    .. math:: H(x, m, r) = -log\\frac{C(m + 1, r)}{C(m, r)}

    where :math:`m` is the embedding dimension (= order), :math:`r` is
    the radius of the neighbourhood (default = :math:`0.2 * \\text{std}(x)`),
    :math:`C(m + 1, r)` is the number of embedded vectors of length
    :math:`m + 1` having a Chebyshev distance inferior to :math:`r` and
    :math:`C(m, r)` is the number of embedded vectors of length
    :math:`m` having a Chebyshev distance inferior to :math:`r`.

    Note that if metric == 'chebyshev' and x.size < 5000 points, then the
    sample entropy is computed using a fast custom Numba script. For other
    metric types or longer time-series, the sample entropy is computed using
    a code from the mne-features package by Jean-Baptiste Schiratti
    and Alexandre Gramfort (requires sklearn).

    References
    ----------
    .. [1] Richman, J. S. et al. (2000). Physiological time-series analysis
           using approximate entropy and sample entropy. American Journal of
           Physiology-Heart and Circulatory Physiology, 278(6), H2039-H2049.

    Examples
    --------
    1. Sample entropy with order 2.

        >>> from entropy import sample_entropy
        >>> import numpy as np
        >>> np.random.seed(1234567)
        >>> x = np.random.rand(3000)
        >>> print(sample_entropy(x, order=2))
            2.192

    2. Sample entropy with order 3 using the Euclidean distance.

        >>> from entropy import sample_entropy
        >>> import numpy as np
        >>> np.random.seed(1234567)
        >>> x = np.random.rand(3000)
        >>> print(sample_entropy(x, order=3, metric='euclidean'))
            2.725
    """
    x = np.asarray(x, dtype=np.float64)
    if metric == 'chebyshev' and x.size < 5000:
        return _numba_sampen(x, mm=order, r=0.2)
    else:
        phi = _app_samp_entropy(x, order=order, metric=metric,
                                approximate=False)
        return -np.log(np.divide(phi[1], phi[0]))


# Bench mark function 8
# Generalized Schwefel's Function No.2.26
# HW dimension: 30
# Min = -418.982887272433799807913601398 * n
# Range [-500,500]
# Reference: https://www.al-roomi.org/benchmarks/unconstrained/n-dimensions/176-generalized-schwefel-s-problem-2-26

import math
import numpy as np

name = "F8"
l_bound = -500
u_bound = 500
dim = 30
opt = -418.982887272433799807913601398 * dim

def func(X):
    sin_term = np.sin(np.absolute(X)**0.5)
    result = np.sum(-1*np.multiply(X, sin_term))
    return result

if __name__ == '__main__':
    X = np.arange(30)
    X = X * 0.3
    print(X)
    result = func(X)
    print(result)


from scipy.special import gamma, gammaln
import operator
import math
from logsumexp import logsumexp
from factorialln import factorialln

def product(l):
    return reduce(operator.mul, l)

def beta(alphas):
    """Multivariate beta function"""
    return math.exp(sum(map(gammaln, alphas)) - gammaln(sum(alphas)))

def dirichlet(probs, obs, s=1):
    """Dirichlet probability density for a given set probabilities and prior
    observation counts.  s serves as a concentration parameter between 0 and 1,
    with smaller s yielding progressively more diffuse probability density
    distributions."""
    alphas = [(a + 1) * float(s) for a in obs]
    return 1 / beta(alphas) * product([math.pow(x, a - 1) for x, a in zip(probs, alphas)])

def dirichlet_maximum_likelihood_ratio(probs, obs, s=1):
    """Ratio between the dirichlet for the specific probs and obs and the
    maximum likelihood value for the dirichlet over the given probs (this can
    be determined by partitioning the observations according to the
    probabilities)"""
    maximum_likelihood = dirichlet(probs, [float(sum(obs)) / len(obs) for o in obs], s)
    return dirichlet(probs, obs, s) / float(maximum_likelihood)

def multinomial(probs, obs):
    return math.factorial(sum(obs)) / product(map(math.factorial, obs)) * product([math.pow(p, x) for p,x in zip(probs, obs)])

def multinomialln(probs, obs):
    return factorialln(sum(obs)) - sum(map(factorialln, obs)) + sum([math.log(math.pow(p, x)) for p,x in zip(probs, obs)])

def multinomial_coefficientln(n, counts):
    return factorialln(n) - sum(map(factorialln, counts))

def multinomial_coefficient(n, counts):
    return math.exp(multinomial_coefficientln(n, counts))

def multinomial_dirichlet(probs, obs): return multinomial(probs, obs) * dirichlet(probs, obs)

# NOTE:
# I started exploring the multinomial_maximum_likelihood_ratio to see if the
# same maximim likelihood estimation approach could be applied to multinomials.
# It *can't* for the obvious reason that you can't have non-integral
# observation counts while the dirichlet distribution is defined across
# non-integral alphas!  I see no clean way to resolve this using the
# multinomial distribution and now have a better understanding of the use and
# abuse of the dirichlet distribution as a conjugate prior for multinomial
# posteriors.
def multinomial_maximum_likelihood_ratio(probs, obs):
    maximum_likelihood = multinomial(probs, [float(sum(obs)) / len(obs) for o in obs])
    return multinomial(probs, obs) / float(maximum_likelihood)

def binomial(successes, trials, prob):
    return math.factorial(trials) / (math.factorial(successes) * math.factorial(trials - successes)) * math.pow(prob, successes) * math.pow(1 - prob, trials - successes)


import math
import numpy as np

def rotate_vector(vector, angle):
    angle = math.radians(angle)
    rotated_vector = [
            vector[0]*math.cos(angle) - vector[1]*math.sin(angle),
            vector[0]*math.sin(angle) + vector[1]*math.cos(angle),
        ]
    return rotated_vector

# funcion para crear circulos con canvas de Tkinter
def create_circle(x, y, r, canvasName, color): #center coordinates, radius
    x0 = x - r
    y0 = y - r
    x1 = x + r
    y1 = y + r
    return canvasName.create_oval(x0, y0, x1, y1, outline=color)

def coords_within_circle(pos_cir, pos_obj, r):
    # euclidean distance (x1-x)² + (y1-y)² <= r²
    x1 = pos_obj[0]
    y1 = pos_obj[1]
    x = pos_cir[0]
    y = pos_cir[1]
    return (x1-x)**2 + (y1-y)**2 <= r**2

def coords_within_arc(pos_cir, pos_obj, r, anglemax, anglemin):
    inside = False
    if coords_within_circle(pos_cir, pos_obj, r):
        angle = angle_btw_2_points(pos_cir, pos_obj)
        if angle <= anglemax and angle >= anglemin:
            inside = True

    return inside

def angle_btw_2_points(pointA, pointB):
    a = np.array(pointA)
    b = np.array(pointB)
    angle = calculate_rotation_angle_from_vector_to_vector(a,b)
    if angle > 180:
        angle -= 360

    return angle

def unit_vector(vector):
    """ Returns the unit vector of the vector.  """
    all_zero = True
    i = 0
    while i < len(vector) and all_zero:
        if vector[i] != 0:
            all_zero = False

        i += 1

    if all_zero:
        return [0, 0]

    return vector / np.linalg.norm(vector)

def calculate_rotation_angle_from_vector_to_vector(a,b):
    """ return rotation angle from vector a to vector b, in degrees.
    Args:
        a : np.array vector. format (x,y)
        b : np.array vector. format (x,y)
    Returns:
        angle [float]: degrees. 0~360
    """
    unit_vector_1 = unit_vector(a)
    unit_vector_2 = unit_vector(b)
    dot_product = np.dot(unit_vector_1, unit_vector_2)
    angle = np.arccos(dot_product)
    angle = angle/ np.pi * 180
    c = np.cross(b,a)
    if c>0:
        angle +=180

    return angle


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

def twospirals(n_points, noise = 1):
    """
     Returns the two spirals dataset.
    """
    n = np.sqrt(np.random.rand(n_points,1)) * 780 * (2*np.pi)/360
    d1x = -np.cos(n)*n + np.random.rand(n_points,1) * noise
    d1y = np.sin(n)*n + np.random.rand(n_points,1) * noise
    return (np.vstack((np.hstack((d1x,d1y)),np.hstack((-d1x,-d1y)))),
            np.hstack((np.zeros(n_points),np.ones(n_points))))

X, y_ = twospirals(2000)

#plt.plot(X[y_==0,0], X[y_==0,1], '.', color='green')
#plt.plot(X[y_==1,0], X[y_==1,1], '.', color='red')
#plt.show()

y =[]
for i in range(len(y_)):
    y.append(int(y_[i]))
y = np.eye(2)[y]


#########################
##                     ##
## Irving Gomez Mendez ##
##  February 27, 2021  ##
##                     ##
#########################

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import t, multivariate_normal
import random

random.seed(111)
mu = [0,0]
Sigma = [[2,0.7],[0.7,2]]
points = multivariate_normal.rvs(mu,Sigma,200)

# Mahalanobis distance
D_M = np.sqrt(np.diag((points-mu) @ np.linalg.inv(Sigma) @ (points-mu).T))

plt.figure(figsize=(7.5,7.5))
plt.scatter(points[:,0], points[:,1], label='Original data', c=D_M)
plt.colorbar()


import numpy as np

class FourierService:
    # channel (gray,red,green,blue) RGB
    def convertImageColor(self, image=None, channel="GRAY"):
        w, h, c = image.shape
        if c == 1:
            return Exception("Image null or image already gray")
        elif channel == "GRAY":
            image = (np.dot(image[...,:3], [0.2989, 0.5870, 0.1140])).astype(int)
        elif channel == "RED":
            image = image[:, :, 0]
        elif channel == "GREEN":
            image = image[:, :, 1]
        elif channel == "BLUE":
            image = image[:, :, 2]

        return image

    def ImageToFFT(self, image):
        return np.fft.fft2(image)

    def fftToImage(self, array):
        return np.fft.ifft2(array)

    def centerSpectrum(self, array=None):
        return np.fft.fftshift(array)

    def inverseSpectrum(self, array=None):
        return np.fft.ifftshift(array)

    def toImage(self, array):
        return np.log(1 + np.abs(array))


import numpy as np
import phd_toolbox.constants as cst

# ORBIT EFFECT
def orbit_shift(I_L, r_w, betx, bety, qx, qy, phi_w, Brho, mu0=cst.mu0_H_m):
    '''
    This functions computes the orbit shift induced by a wire in both planes.
    Taken from https://indico.cern.ch/event/456856/contributions/1968793/attachments/1196177/1740660/BBLR_LYON_2015.pdf

    Args:
        I_L: float or array, current in the wire [A.m]
        r_w: float or array, beam-wire distance, regardless the plane [m]
        phi_w: float or array, angle between the beam and the wires, zero being horizontal [rad]
        Brho: float, magnetic rigidity
        mu0: vacuum permeability [H/m]

    Returns:
        [dx,dy]: lst of dim 2 (for x and y). Each element can be either a float or an array depending on the input.
    '''
    theta_x = mu0*I_L*np.cos(phi_w)/2/np.pi/Brho/r_w
    theta_y = mu0*I_L*np.sin(phi_w)/2/np.pi/Brho/r_w
    dx = betx*theta_x/2/np.sin(np.pi*qx)*np.cos(-np.pi*qx)
    dy = bety*theta_y/2/np.sin(np.pi*qy)*np.cos(-np.pi*qy)
    return [dx,dy]

# TUNE EFFECT
def tune_shift(I_L, r_w, betx, bety, phi_w, Brho, mu0=cst.mu0_H_m):
    '''
    This functions computes the tune shift induced by a wire in both planes.
    Taken from https://indico.cern.ch/event/456856/contributions/1968793/attachments/1196177/1740660/BBLR_LYON_2015.pdf

    Args:
        I_L: float or array, current in the wire [A.m]
        r_w: float or array, beam-wire distance, regardless the plane [m]
        phi_w: float or array, angle between the beam and the wires, zero being horizontal [rad]
        betx,bety: floats, beta functions at the wire location
        Brho: float, magnetic rigidity
        mu0: vacuum permeability [H/m]

    Returns:
        [dqx,dqy]: lst of dim 2 (for x and y). Each element can be either a float or an array depending on the input.
    '''
    dqx = -mu0*I_L*betx*np.cos(2*phi_w)/8/np.pi/np.pi/Brho/r_w/r_w
    dqy = mu0*I_L*bety*np.cos(2*phi_w)/8/np.pi/np.pi/Brho/r_w/r_w
    return [dqx,dqy]


# Exercise 5: Box-and-Whisker plot

# Generate a list of normally distributed numbers
import numpy as np
y = np.random.normal(loc=0, scale=0.1, size=100) # 100 numbers with mean of 0 and standard deviation of 0.1

# Generate boxplot
import matplotlib.pyplot as plt # import matplotlib
plt.boxplot(y) # create boxplot of y
plt.show() # print plot

# Calculate shapiro wilk p-value
from scipy.stats import shapiro
shap_w, shap_p = shapiro(y)
print(shap_p)

# Get outliers
# convert to z-scores
from scipy.stats import zscore
y_z_scores = zscore(y) # convert y into z scores

# get the number of scores with absolute value of 3 or more
total_outliers = 0
for i in range(len(y_z_scores)):
    if abs(y_z_scores[i]) >= 3:
        total_outliers += 1
print(total_outliers)

# set up some logic for the title
if shap_p > 0.05:
    title = 'Normally distributed with {} outlier(s).'.format(total_outliers)
else:
    title = 'Not normally distributed with {} outlier(s).'.format(total_outliers)
print(title)

# Generate boxplot with programmatic tile
import matplotlib.pyplot as plt # import matplotlib
plt.boxplot(y) # generate boxplot
plt.title(title) # programmatic title
plt.show() # print plot


# %%
import numpy as np

# %%
v1 = np.array([3, 2, 4])
v2 = np.array([8, 9, 7])

# %%
v1

# %%
type(v1)

# %%
v1.dtype

# %%
v3 = np.array([1.0, 2.0, 3.0])

# %%
v3.dtype

# %%
v1 + v2

# %%
lst1 = [3, 2, 4]
lst2 = [8, 9, 7]

# %%
def vector_sum(v1, v2):
    assert len(v1) == len(v2)
    result = [0] * len(v1)
    for i in range(len(v1)):
        result[i] = v1[i] + v2[i]
    return result

# %%
vector_sum(lst1, lst2)

# %%
# %%timeit
vector_sum(lst1, lst2)

# %%
# %%timeit
v1 = v2

# %%
v1 * v2

# %%
v1.dot(v2)

# %%
v1.sum()

# %%
v1.mean()

# %%
v1.std()

# %%
v1.max()

# %%
v1.min()

# %%
v1

# %%
v1.argmax()

# %%
v1.argmin()

# %%
m1 = np.array([[1, 2, 3], [4, 5, 6]])

# %%
m1

# %%
m2 = np.array([[1, 0], [0, 1], [2, 3]])

# %%
m2

# %%
m1 + m1

# %%
m2 + m2

# %%
m1 + m2

# %%
v1.shape

# %%
m1.shape

# %%
m2.shape

# %%
m1

# %%
m1.T

# %%
m2

# %%
m2.T

# %%
m1.T + m2

# %%
m1 + m2.T

# %%
m1 * m2

# %%
m1.T * m2

# %%
m1.dot(m2)

# %%
m1 @ m2

# %%
v1 @ v2

# %%


# -*- coding: utf-8 -*-
"""
The Monty Hall Problem Simulated

Created on Mon May 25 20:30:43 2020
@author: Gurpinder
"""
import random
import matplotlib.pyplot as plt
import numpy as np

def generate_doors(n):
    """
    Returns: a list of n random doors, one with a car behind it all others with Goats.
    """
    Doors = ["G" for i in range(n)]
    # choose a random place to put car behind
    Doors[random.randint(0, n-1)] = "C"
    return Doors

def play(Doors, switcher):
    """
    Plays one turn of the Monty Hall Game.
    Doors = list of Doors, one with car, all other with goats. (rhymes lol.)
    switcher = True/Fasle, tells if the player will make a switch or not.
    """

    choice = random.randint(0, len(Doors)-1) #player chooses 1 door
    #player chooses a door with no car behind
    if Doors[choice] != "C":
        #Monty keeps the car door closed, opens the rest
        closed_door = Doors.index("C")

    #if player chooses the door that contains car
    if Doors[choice] == "C":
        #Monty keeps one door closed, opens the rest
        while True:
            closed_door = random.randint(0, len(Doors)-1)
            if closed_door != choice:
                break

    if switcher: #if player wants to switch it can do so here.
            choice = closed_door #Player switches to the door that monty kept CLOSED.

    return Doors[choice] == "C" #Return if the player choice door had a car behind it

def simulation(numTrials, numDoors = 3, toprint = False):
    """
    numTrials: number of times the game will be played.
    numDoors = number of doors in the game.

    """
    results_switch =  [] #storing the results of player that makes a switch
    results_not_switch = [] #same for the player that doesnot make a switch

    for i in range(numTrials):
        #generate new door set every Trial
        Doors = generate_doors(numDoors)

        results_switch.append(play(Doors, True)) #Switcher plays the game
        results_not_switch.append(play(Doors, False)) #non switcher playes the game

    #printing the results
    if toprint:
        print("\nPrinting the simulation results:")
        print("For {} Doors, Iterations {}".format(numDoors, numTrials))
        print("The person who switched choice won {}% of times.".format((results_switch.count(True)/numTrials)*100))
        print("The person who didn't switch choice won {}% of times.".format((results_not_switch.count(True)/numTrials)*100))

    return (results_switch.count(True)/numTrials, results_not_switch.count(True)/numTrials)

def door_number_exp(numDoors, numTrials = 100, numRepitions = 20):
    """
    Does the number of doors affect the wining ratio - does it faour either player?
    Answer: Yes, the player who switches gets the benefit.

    Inputs: numDoors - list of number of doors to experiment on
            numTrials - number of times to play the game
            numRepitions - number of times each door number is played 100 times and average is plotted.
    """
    #storing the averages for each door.
    r_s = []
    r_ns = []
    for num in numDoors:
        #tempory lists to store numRepitions number of numTrials Trials.
        sc = []
        nsc = []
        for i in range(numRepitions):
            Switcher, nonSwitcher = simulation(numTrials, num)
            sc.append(Switcher)
            nsc.append(nonSwitcher)

        #append the means into the lists for plotting
        r_s.append(np.array(sc).mean())
        r_ns.append(np.array(nsc).mean())

    #plotting code
    plt.figure(figsize = (7,7))
    plt.plot(numDoors, r_s, label = "Switcher")
    plt.plot(numDoors, r_ns, label= "Non-Switcher")
    plt.title("Simulating Monty hall problem over {} doors.".format(len(numDoors)))
    plt.xlabel("Number of Doors")
    plt.ylabel("Proportion of games won")
    plt.legend(loc="best")
    plt.show()

#Running the simulation
door_number_exp(np.arange(3, 103, dtype=int))