# Reference for discrete equations:
# http://physics.bu.edu/py502/lectures3/cmotion.pdf

from numpy import zeros_like, array
from numpy import sum as npsum
from numba import njit, prange

# Constants
G     = 6.67430e-11     # Netwon's gravitational constant in SI units
m2AU  = 6.684587122e-12 # Metre to astronomical units
kg2ME = 1.6744e-25      # Kilogram to Earth masses
s2dy  = 1.157407407e-5  # Second to days (ass. 24hr/day)

convG = G * m2AU**3 / (kg2ME * s2dy**2) # G in unit [au^3 ME^-1 dy^-2]

@njit(parallel=True)
def make_step(stepSize, numBodies, bodyMasses, currentPosits, currentVels):
    """Perform a forward step using the fourth-order Runge-Kutta method
    in parallel over all bodies in the simulation using Numba.

    This function takes the current positions and velocities of a number of
    bodies and performs a single forward time step of Netwon's gravitational
    equations of motion using the 4th order Runge-Kutta method for all bodies.

    Arguments:
    stepSize      -- the size of the time step in days.
    numBodies     -- the number of bodies equal to len(bodyMasses).
    bodyMasses    -- a list of masses for each body.
    currentPosits -- a NumPy ndarray of the current positions of all bodies,
                     each element of which has in turn two dimensions: [x, y].
    currentVels   -- a NumPy ndarray of the current velocities of all bodies,
                     each element of which has in turn two dimensions: [x, y].

    Returns:
    nextPosits  -- a NumPy ndarray of the next positions of all bodies, each
                   element of which has in turn two dimensions: [x, y].
    currentVels -- a NumPy ndarray of the next velocities of all bodies, each
                   element of which has in turn two dimensions: [x, y].

    Note, the order of the position and velocities vectors for each mass in the
    arguments 'currentPosits' and 'currentVels' must be the same as the order
    of masses in 'bodyMasses'. The return arrays also have the same ordering.
    """

    nextPosit = zeros_like(currentPosits)
    nextVel   = zeros_like(currentVels)

    for i in prange(numBodies):

        iMass  = bodyMasses[i]
        iPosit = currentPosits[i]
        iVel   = currentVels[i]

        l1 = stepSize*iVel
        k1 = array([0., 0.])
        for j in prange(numBodies):
            if i == j: continue

            jMass  = bodyMasses[j]
            jPosit = currentPosits[j]

            rVec   = jPosit - iPosit
            rSumSq = npsum(rVec**2)
            k1    += stepSize * convG * jMass * (rVec/rSumSq**1.5)

        l2 = stepSize * (iVel + k1/2)
        k2 = array([0., 0.])
        for j in prange(numBodies):
            if i == j: continue

            jMass  = bodyMasses[j]
            jPosit = currentPosits[j]

            rVec   = jPosit - (iPosit + l1/2)
            rSumSq = npsum(rVec**2)
            k2    += stepSize * convG * jMass * (rVec/rSumSq**1.5)

        l3 = stepSize * (iVel + k2/2)
        k3 = array([0., 0.])
        for j in prange(numBodies):
            if i == j: continue

            jMass  = bodyMasses[j]
            jPosit = currentPosits[j]

            rVec   = jPosit - (iPosit + l2/2)
            rSumSq = npsum(rVec**2)
            k3    += stepSize * convG * jMass * (rVec/rSumSq**1.5)

        l4 = stepSize * (iVel + k3)
        k4 = array([0., 0.])
        for j in prange(numBodies):
            if i == j: continue

            jMass  = bodyMasses[j]
            jPosit = currentPosits[j]

            rVec   = jPosit - (iPosit + l3)
            rSumSq = npsum(rVec**2)
            k4    += stepSize * convG * jMass * (rVec/rSumSq**1.5)

        nextPosit[i] = iPosit + (l1 + 2*l2 + 2*l3 + l4)/6
        nextVel[i]   = iVel   + (k1 + 2*k2 + 2*k3 + k4)/6

    return nextPosit, nextVel


import numpy as np
import scipy as sp
from numpy.random import randn, rand
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import norm

def em_gauss_sim(n_sample=1000, n_component=5, iter_lim=20, seed=7):
    # set seed
    np.random.seed(seed)

    x = randn(n_sample) + (rand(n_sample) > 0.3) * 4 - 2

    L = - np.inf

    # initial values
    weights = np.ones(n_component) / n_component
    means = np.linspace(np.min(x), np.max(x), n_component)
    covs = np.ones(n_component)/10
    eta = np.zeros([n_sample, n_component])

    # main loop
    n_iter = 0
    while n_iter < iter_lim:
        # E step
        for i in range(n_sample):
            for l in range(n_component):
                eta[i][l] = (weights[l] * norm.pdf(x=x[i], loc=means[l], scale=np.sqrt(covs[l]))) / np.dot(weights, norm.pdf(x=x[i], loc=means, scale=np.sqrt(covs)))

        # M step
        means_prev = means.copy()
        weights_prev = weights.copy()
        covs_prev = covs.copy()
        for l in range(n_component):
            weights[l] = np.sum(eta[:,l]) / n_sample
            covs[l] = np.dot(eta[:,l], (x - means_prev[l])**2) / (1 * np.sum(eta[:,l]))
            means[l] = np.dot(eta[:,l], x) / np.sum(eta[:, l])

        # stop condition
        if np.sum((weights - weights_prev)**2) < 0.00001:
            break

        n_iter += 1

    # for debugging
    print(n_iter)

    sim_x = np.linspace(np.min(x), np.max(x), 1000)
    y = np.zeros_like(sim_x)
    for l in range(n_component):
        tmp = weights[l] * norm.pdf(x=sim_x, loc=means[l], scale=np.sqrt(covs[l]))
        y += tmp

    return x, sim_x, y

import numpy,from numpy,import scipy,from scipy


import numpy as np

def Sigmoid(feature,derivative=False):
  if derivative==False:
    return 1/(1+np.exp(-feature))
  else :
    return np.exp(-feature)/((1+ np.exp(-feature)))**2

def ReLu(feature,derivative=False):
  if derivative==False:
    return feature*(feature>0)
  else :
    return 1*(feature>0)

def Tanh(feature,derivative=False):
  if derivative==False:
    return np.tanh(feature)
  else :
    return 1/(np.cosh(feature))**2


import numpy as np

def feature_normalize(X):
    mu = np.mean(X, 0)
    sigma = np.std(X, 0, ddof=1)
    X_norm = (X - mu) / sigma

    return X_norm, mu, sigma


from sage.all import QQ
from sage.all import ZZ
from sage.all import matrix
from sage.all import vector

def attack(y, k, s, m, a, c):
    """
    Recovers the states associated with the outputs from a truncated linear congruential generator.
    More information: Frieze, A. et al., "Reconstructing Truncated Integer Variables Satisfying Linear Congruences"
    :param y: the sequential output values obtained from the truncated LCG (the states truncated to s most significant bits)
    :param k: the size in bits of the states
    :param s: the size in bits of the outputs
    :param m: the modulus of the LCG
    :param a: the multiplier of the LCG
    :param c: the increment of the LCG
    :return: a list containing the states associated with the provided outputs
    """
    diff_bitsize = k - s

    # Preparing for the lattice reduction.
    delta = c % m
    y = vector(ZZ, y)
    for i in range(len(y)):
        # Shift output value to the MSBs and remove the increment.
        y[i] = (y[i] << diff_bitsize) - delta
        delta = (a * delta + c) % m

    # This lattice only works for increment = 0.
    B = matrix(ZZ, len(y), len(y))
    B[0, 0] = m
    for i in range(1, len(y)):
        B[i, 0] = a ** i
        B[i, i] = -1

    B = B.LLL()

    # Finding the target value to solve the equation for the states.
    b = B * y
    for i in range(len(b)):
        b[i] = round(QQ(b[i]) / m) * m - b[i]

    # Recovering the states
    delta = c % m
    x = list(B.solve_right(b))
    for i, state in enumerate(x):
        # Adding the MSBs and the increment back again.
        x[i] = int(y[i] + state + delta)
        delta = (a * delta + c) % m

    return x


import math
import numpy as np

class ProjectionVector(object):
	"""
	Computes the projection vector from an input segment
	Input must be in the form: ((x0,y0),(x1,y1))
	"""
	def __init__(self, lineA):
		self.lineA = lineA
		self.vector = self.find_projection()
		self.slope = self.find_slope()
		self.intercept = self.find_intercept()

	def find_projection(self):
		return (self.lineA[1], (2*self.lineA[1][0]-self.lineA[0][0], 2*self.lineA[1][1]-self.lineA[0][1]))

	def find_slope(self):
		a =  self.vector[1][1]- self.vector[0][1]
		if  self.vector[1][0] !=  self.vector[0][0]: # protect from zero division error
			return (a / ( self.vector[1][0]- self.vector[0][0]))
		else:
			return a

	def find_intercept(self):
		return  self.vector[1][1] - self.slope* self.vector[1][0]

def find_angle(lineB,VectorP):
	"""
	Line A (not present in function) is the starting segment
	Line B is the following segment
	VectorP is an object of class ProjectionVector and it is derived from Line A
	"""
	vB = [(lineB[0][0]-lineB[1][0]), (lineB[0][1]-lineB[1][1])]
	vP = [(VectorP.vector[0][0]-VectorP.vector[1][0]), (VectorP.vector[0][1]-VectorP.vector[1][1])]
	dot_prod = np.dot(vB,vP)
	magA = np.sqrt(np.dot(vB, vB))
	magB = np.sqrt(np.dot(vP, vP))
	cos_ = dot_prod/(magA*magB)
	ang = np.arccos(cos_)
	ang_deg = math.degrees(ang)%360
	if lineB[1][1]<VectorP.slope*lineB[1][0] + VectorP.intercept:
	    ang_deg = -ang_deg
#	print(ang_deg)
	return -1*ang_deg

if __name__ == '__main__':
	#input lines
	lineA = ((1, 1),(1, 2))
	# lineA = np.array([[-2, 2],[-4, 1.5]])
	lineB = ((1, 2),(2, 1))

	#projected vector
	vectorP = ProjectionVector(lineA)

	a = find_angle(lineB,vectorP)

# for i in range(0,len(a), 12):
# 	vP = ProjectionVector([a[i], a[i+1]])
# 	find_angle([a[i+1], a[i+2]],vP)


import numpy as np
import math

class NaiveBayes():
    def __init__(self):
        self.partial_vector_0 = []
        self.partial_vector_1 = []
        self.prob_class_1 = 0

    def train_naive_bayes(self, matrix, categories):
        '''Assume there are only two class.
        @param: categories is a list of 1's and 0's
        '''
        self.prob_class_1 = sum(categories) / len(categories)
        norm_features_0 = np.ones(len(matrix[0]))
        norm_features_1 = np.ones(len(matrix[0]))
        cnt0 = cnt1 = len(matrix[0])
        for i in range(len(categories)):
            if categories[i] == 1:
                norm_features_1 += matrix[i]
                cnt1 += sum(matrix[i])
            else:
                norm_features_0 += matrix[i]
                cnt0 += sum(matrix[i])
        self.partial_vector_0 = np.log(norm_features_0 / cnt0)
        self.partial_vector_1 = np.log(norm_features_1 / cnt1)

    def predict(self, test_matrix):
        '''@param: test_matrix: vector of vectors
        '''
        return [1 if sum(vec * self.partial_vector_1) + math.log(self.prob_class_1) > \
            sum(vec * self.partial_vector_0) + math.log(1 - self.prob_class_1) else 0 \
                for vec in test_matrix]

if __name__ == "__main__":
    matrix = [[0, 1, 1, 0], [1, 0, 0, 0], [1, 1, 1, 0]]
    print(NaiveBayes().train_naive_bayes(matrix, [0, 0, 1]))
    # x = np.asarray([1,2,3,4])
    # print(np.log(x))
    # print(math.log(2))

    # y = np.asarray([1,2,3,4])
    # z = x + y
    # print(z)
    # print(np.asarray(x) / 3)


import numpy as np

class AffineLayer:
    """
    forward/backward propagation for Affine transformation: Y = XW + B

    In the textbook, `doutdW` and `doutdb` are fields.
    """

    def __init__(self):
        self.x = None
        self.W = None
        self.b = None

    def forward(self, x, W, b):
        """
        return y = xW + b

        x: (batch_size, n) matrix
        W: (n, m) matrix
        b: (1, m) matrix. This is NOT (batch_size, m) matrix because bias is common for all data in `x`.
        """
        self.x = x
        self.W = W
        self.b = b
        return np.dot(self.x, self.W) + self.b

    def backward(self, doutdy):
        """
        return d(out)/dx, d(out)/dW

        d(out)/dx = d(out)/dy * [W]T
        d(out)/dW = [x]T * d(out)/dy
        d(out)/db = d(out)/dy
        ([A]T means the transpose matrix of A.)

        doutdy: (batch_size, m) matrix (`batch_size` and `m` must be consistent with those of `forward`.)
        """
        doutdx = np.dot(doutdy, self.W.T)
        doutdW = np.dot(self.x.T, doutdy)
        doutdb = np.sum(doutdy, axis=0)
        return doutdx, doutdW, doutdb

if __name__ == '__main__':
    affine_layer = AffineLayer()

    # non-batch
    # Be aware of '[[1, 2, 0]]' not '[1, 2, 0]'.
    # np.array([1,2,3]).T = np.array([1,2,3]), not np.array([[1],[2],[3]]).
    x = np.array([[1, 2, 0]])  # (1, 3) matrix
    W = np.array([[1, 0], [0, 1], [0, 0]])  # (3, 2) matrix
    b = np.array([[1, 1]])  # (1, 2) matrix
    y = affine_layer.forward(x, W, b)
    print('y = {0}'.format(y))
    doutdy = np.array([[1, 0]])  # (1, 2) matrix
    doutdx, doutdW, doutdb = affine_layer.backward(doutdy)
    print('doutdx = {0}, doutdW = {1}, doutdb = {2}'.format(
        doutdx, doutdW, doutdb))

    # batch
    x = np.array([[1, 2, 0], [0, 1, 2]])  # (2, 3) matrix
    W = np.array([[1, 0], [0, 1], [0, 0]])  # (3, 2) matrix
    b = np.array([[1, 1]])  # (1, 2) matrix
    y = affine_layer.forward(x, W, b)
    print('y = {0}'.format(y))
    doutdy = np.array([[1, 0], [0, 1]])  # (2, 2) matrix
    doutdx, doutdW, doutdb = affine_layer.backward(doutdy)
    print('doutdx = {0}, doutdW = {1}, doutdb = {2}'.format(
        doutdx, doutdW, doutdb))


from math import factorial

import matplotlib.pyplot as plt
import numpy as np

from numerical_analysis.dependencies import GeometricalPlace
from numerical_analysis.dependencies import Polynomial
from numerical_analysis.root_finding import newton_raphson

class Bezier(GeometricalPlace):

    def __init__(self, control_points: np.array, datatype=np.float):
        super().__init__()
        self.datatype = datatype
        self.cp = control_points
        self.n = len(self.cp) - 1
        self.m = self.matrix(self.n, self.datatype)
        self.c = self.polynomial_coefficients()
        self.polynomials = {"x": Polynomial(self.c[0]),
                            "y": Polynomial(self.c[1]),
                            "dx/dt": Polynomial(self.c[0]).derivative(),
                            "dy/dt": Polynomial(self.c[1]).derivative(),
                            "p": [Polynomial(self.m[i]) for i in range(self.n + 1)]}

    def polynomial_coefficients(self):
        c = np.empty([2, self.n + 1], dtype=self.datatype)
        for k in range(2):
            c[k] = np.matmul(np.transpose(self.m), self.cp[:, k])
        return c

    def y_x(self, x, error=1e-14):
        t0 = newton_raphson(lambda t: self.x_t(t) - x, self.dx_dt, 0.5, error)
        return self.y_t(t0)

    def x_t(self, t):
        return self.polynomials["x"].value(t)

    def y_t(self, t):
        return self.polynomials["y"].value(t)

    def dx_dt(self, t):
        return self.polynomials["dx/dt"].value(t)

    def dy_dt(self, t):
        return self.polynomials["dy/dt"].value(t)

    def p_i(self, i, t):
        return self.polynomials["p"][i].value(t)

    def graph_cp(self):
        return [[self.cp[j, i] for j in range(len(self.cp))] for i in range(len(self.cp[0]))]

    def plot(self, dt):
        graph = self.graph(dt)
        # noinspection PyUnresolvedReferences
        plt.plot(graph[0], graph[1], self.graph_cp()[0], self.graph_cp()[1])
        plt.show()

    def update(self):
        self.c = self.polynomial_coefficients()
        self.polynomials = {"x": Polynomial(self.c[0]),
                            "y": Polynomial(self.c[1]),
                            "dx/dt": Polynomial(self.c[0]).derivative(),
                            "dy/dt": Polynomial(self.c[1]).derivative(),
                            "p": [Polynomial(self.m[i]) for i in range(self.n + 1)]}

    def modify_control_point(self, i, new_control_point):
        self.cp[i][0] = new_control_point[0]
        self.cp[i][1] = new_control_point[1]
        self.update()

    def modify_control_point_coordinate(self, i, k, new_control_point_coordinate):
        self.cp[i, k] = new_control_point_coordinate
        self.update()

    def refresh_control_points(self, control_points):
        self.cp = control_points
        self.update()

    def append_control_point(self, new_control_point):
        self.cp = np.append(self.cp, [new_control_point], axis=0)
        self.update()

    def change_datatype(self, datatype):
        self.datatype = datatype
        self.cp = self.cp.astype(datatype)
        self.m = self.m.astype(datatype)
        self.c = self.c.astype(datatype)

    @classmethod
    def matrix(cls, n, datatype=np.int):
        return np.array([[((-1) ** (j - i) * factorial(n)) /
                          (factorial(i) * factorial(j - i) * factorial(n - j))
                          if j >= i else 0. for j in range(n + 1)] for i in range(n + 1)], dtype=datatype)


import numpy

class DiagGaussianKernel(object):
    def __init__(self, sigma=1.0):
        """
        Initialise object with given value of sigma >= 0
        :param sigma: kernel width parameter.
        :type sigma: :class:`float`
        """
        self.sigma = sigma

    def __call__(self, X1, X2):
        # if X1.shape[1] != X2.shape[1]:
        # raise ValueError("Invalid matrix dimentions: " + str(X1.shape) + " " + str(X2.shape))

        K = numpy.exp(- numpy.sum((X1 - X2) ** 2, 1) / (2 * self.sigma ** 2))
        K = numpy.array(K, ndmin=2).T
        return K

class GaussianKernel(object):
    """
    A class to find gaussian kernel evaluations k(x, y) = exp (-||x - y||^2/2 sigma^2)
    """

    def __init__(self, sigma=1.0):
        """
        Initialise object with given value of sigma >= 0
        :param sigma: kernel width parameter.
        :type sigma: :class:`float`
        """
        self.sigma = sigma

    def __call__(self, X1, X2):
        """
        Find kernel evaluation between two matrices X1 and X2 whose rows are
        examples and have an identical number of columns.
        :param X1: First set of examples.
        :type X1: :class:`numpy.ndarray`
        :param X2: Second set of examples.
        :type X2: :class:`numpy.ndarray`
        """

        if X1.shape[1] != X2.shape[1]:
            raise ValueError("Invalid matrix dimentions: " + str(X1.shape) + " " + str(X2.shape))

        j1 = numpy.ones((X1.shape[0], 1))
        j2 = numpy.ones((X2.shape[0], 1))

        diagK1 = numpy.sum(X1 ** 2, 1)
        diagK2 = numpy.sum(X2 ** 2, 1)

        X1X2 = numpy.dot(X1, X2.T)

        Q = (2 * X1X2 - numpy.outer(diagK1, j2) - numpy.outer(j1, diagK2)) / (2 * self.sigma ** 2)

        return numpy.exp(Q)

    def __str__(self):
        return "GaussianKernel: sigma = " + str(self.sigma)

class PolyKernel(object):
    """
    A class to find linear kernel evaluations k(x, y) = <x, y>
    """

    def __init__(self, c, p):
        """
        Intialise class.
        """
        self.c = c
        self.p = p

    def __call__(self, X1, X2):
        """
        Find kernel evaluation between two matrices X1 and X2 whose rows are
        examples and have an identical number of columns.
        :param X1: First set of examples.
        :type X1: :class:`numpy.ndarray`
        :param X2: Second set of examples.
        :type X2: :class:`numpy.ndarray`
        """

        if X1.shape[1] != X2.shape[1]:
            raise ValueError("Invalid matrix dimentions: " + str(X1.shape) + " " + str(X2.shape))

        return (numpy.dot(X1, X2.T) + self.c) ** self.p

class LinearKernel(PolyKernel):
    def __init__(self):
        super(LinearKernel, self).__init__(0, 1)


import numpy as np

def l2_regularization(W, reg_strength):
    '''
    Computes L2 regularization loss on weights and its gradient
    Arguments:
      W, np array - weights
      reg_strength - float value
    Returns:
      loss, single value - l2 regularization loss
      gradient, np.array same shape as W - gradient of weight by l2 loss
    '''
    loss = reg_strength * np.sum(W**2)
    grad = 2*reg_strength*W

    return loss, grad

def softmax_with_cross_entropy(predictions, target_index):
    '''
    Computes softmax and cross-entropy loss for model predictions,
    including the gradient
    Arguments:
      predictions, np array, shape is either (N) or (N, batch_size) -
        classifier output
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)
    Returns:
      loss, single value - cross-entropy loss
      dprediction, np array same shape as predictions - gradient of predictions by loss value
    '''
    if not isinstance(target_index, np.ndarray):
        target_index = np.array([[target_index]])
    target_index.shape = (-1,)

    f_num = predictions.shape[-1]
    batch_size = target_index.shape[0]

    # Make correct shape
    pred_copy = predictions.copy().reshape(batch_size, f_num)
    pred_copy -= np.amax(pred_copy, axis=1).reshape(batch_size, 1)

    # Compute softmax
    e = np.exp(pred_copy)
    probs = e / np.sum(e, axis=1).reshape(batch_size, 1)

    # Compute loss
    s = np.arange(batch_size) # make array of [0, 1, 2, ..., (batch_size-1)]
    target_probs = probs[s, target_index] # using multiple array indexing

    loss = -np.average(np.log(target_probs))

    # Compute gradient
    dprediction = np.copy(probs)
    dprediction[s, target_index] -= 1
    dprediction /= batch_size # normalize gradient

    dprediction.shape = predictions.shape

    return loss, dprediction

class Param:
    """
    Trainable parameter of the model
    Captures both parameter value and the gradient
    """

    def __init__(self, value):
        self.value = value
        self.grad = np.zeros_like(value)

class ReLULayer:
    def __init__(self):
        pass

    def forward(self, X):
        self.X = X # Will be used in the back pass
        return np.maximum(0, X)

    def backward(self, d_out):
        """
        Backward pass

        Arguments:
        d_out, np array (batch_size, num_features) - gradient
           of loss function with respect to output

        Returns:
        d_result: np array (batch_size, num_features) - gradient
          with respect to input
        """
        d_input = np.ones(self.X.shape)
        d_input[np.where(self.X < 0)] = 0
        d_result = d_out * d_input
        return d_result

    def params(self):
        # Doesn't have any parameters
        return {}

class FullyConnectedLayer:
    def __init__(self, n_input, n_output):
        self.W = Param(0.001 * np.random.randn(n_input, n_output))
        self.B = Param(0.001 * np.random.randn(1, n_output))
        self.X = None

    def forward(self, X):
        self.X = X
        result = X @ (self.W.value) + self.B.value
        return result

    def backward(self, d_out):
        """
        Backward pass
        Computes gradient with respect to input and
        accumulates gradients within self.W and self.B

        Arguments:
        d_out, np array (batch_size, n_output) - gradient
           of loss function with respect to output

        Returns:
        d_result: np array (batch_size, n_input) - gradient
          with respect to input
        """
        d_result = d_out @ (self.W.value).T
        self.W.grad = self.X.T @ d_out
        self.B.grad = np.sum(d_out, axis=0, keepdims=True)
        return d_result

    def params(self):
        return {'W': self.W, 'B': self.B}


# -*- coding: utf-8 -*-
"""
Created on Sat Apr 18 13:03:24 2020

@author: Mohammad Asif Zaman

Code for testng differentiation matrix operators

Solves 𝛁²u(x,y) = g(x,y)

May 2, 20200: v1:
    - Dirichlet boundary condition implementation

May 5, 20200: v1_1:
    - Fixed a bug regarding the right-hand function
    - Figure size and font size adjusted

"""

from __future__ import print_function

import time
import math
import numpy as np
import pylab as py
import scipy.sparse as sp                 # import sparse matrix library
from scipy.sparse.linalg import spsolve

py.rcParams.update({'font.size': 20})

# import the file where the differentiation matrix operators are defined
from diff_matrices import Diff_mat_1D, Diff_mat_2D

# Defining custom plotting functions
def my_contourf(x,y,F,ttl):
    cnt = py.contourf(x,y,F,41,cmap = 'inferno')
    py.colorbar()
    py.xlabel('x'); py.ylabel('y'); py.title(ttl)
    return 0

def my_scatter(x,y,clr,ttl):
    py.plot(x,y,'.',markersize=2,color=clr)
    py.xlabel('x'); py.ylabel('y'); py.title(ttl)
    return 0

# Dirichlet boundary conditions
uL = 0.5
uR = 1.2
uT = 1.5
uB = 0

# Define independent variables
Nx = 50                         # No. of grid points along x direction
Ny = 80                         # No. of grid points along y direction
x = np.linspace(-6,6,Nx)        # x variables in 1D
y = np.linspace(-3,3,Ny)        # y variable in 1D

dx = x[1] - x[0]                # grid spacing along x direction
dy = y[1] - y[0]                # grid spacing along y direction

X,Y = np.meshgrid(x,y)          # 2D meshgrid

# 1D indexing
Xu = X.ravel()                  # Unravel 2D meshgrid to 1D array
Yu = Y.ravel()

# Source function (right hand side vector)
g = np.zeros(Nx*Ny)

# Loading finite difference matrix operators

Dx_2d, Dy_2d, D2x_2d, D2y_2d = Diff_mat_2D(Nx,Ny)   # Calling 2D matrix operators from funciton

# Boundary indices
start_time = time.time()
ind_unravel_L = np.squeeze(np.where(Xu==x[0]))          # Left boundary
ind_unravel_R = np.squeeze(np.where(Xu==x[Nx-1]))       # Right boundary
ind_unravel_B = np.squeeze(np.where(Yu==y[0]))          # Bottom boundary
ind_unravel_T = np.squeeze(np.where(Yu==y[Ny-1]))       # Top boundary

ind_boundary_unravel = np.squeeze(np.where((Xu==x[0]) | (Xu==x[Nx-1]) | (Yu==y[0]) | (Yu==y[Ny-1])))  # All boundary
ind_boundary = np.where((X==x[0]) | (X==x[Nx-1]) | (Y==y[0]) | (Y==y[Ny-1]))    # All boundary
print("Boundary search time = %1.6s" % (time.time()-start_time))

# Plot solution domain (with boundary)

py.close('all')
my_scatter(X,Y,'b','Solution grid')
my_scatter(X[ind_boundary], Y[ind_boundary],'r','Solutiohn grid with boundary')

# Construction of the system matrix
start_time = time.time()
I_sp = sp.eye(Nx*Ny).tocsr()
L_sys = D2x_2d/dx**2 + D2y_2d/dy**2     # system matrix without boundary conditions

L_sys[ind_boundary_unravel,:] = I_sp[ind_boundary_unravel,:]

# Construction of right hand vector (function of x and y)
b = g
b[ind_unravel_L] = uL
b[ind_unravel_R] = uR
b[ind_unravel_T] = uT
b[ind_unravel_B] = uB

print("System matrix and right hand vector computation time = %1.6s" % (time.time()-start_time))

# solve
start_time = time.time()
u = spsolve(L_sys,b).reshape(Ny,Nx)
print("spsolve() time = %1.6s" % (time.time()-start_time))

# Plot solution
py.figure(figsize = (14,7))
my_contourf(x,y,u,r'$\nabla^2 u = 0$')


# -*- coding: utf-8 -*-
"""
ELS 2-asset by Monte Carlo simulation

@author: Minhyun Yoo
"""
import time
import numpy as np

from math import exp, sqrt, ceil

S0_1 = 100.0; # underlying price 1
S0_2 = 100.0; # underlying price 2
T = 3.0; # maturity
r1 = 0.03; # riskless interest rate 1
r2 = 0.03; # riskless interest rate 2
discr = 0.03;
sig1 = 0.3; # volatility 1
sig2 = 0.3; # volatility 2
rho = 0.5;
ns = 10000;  # # of simulations
dateConv = 360; # 1 year
nStep = int(dateConv * T); # # of time steps

Kib = 60.0;
cpy = 0.03;
dummy = cpy*T;
face = 10000.0;
B = [90.0, 90.0, 85.0, 85.0, 80.0, 80.0]; # early redemption barrier
numExercise = len(B);
obsDate = np.array([ceil(nStep / numExercise), ceil(2.0*nStep/numExercise), ceil(3.0*nStep/numExercise),
           ceil(4.0*nStep / numExercise), ceil(5.0*nStep / numExercise), ceil(nStep)]);
payment = face*(1.0+np.array([cpy*0.5, cpy*1.0, cpy*1.5, cpy*2.0, cpy*2.5, cpy*3.0]));

# functions call
dt = T / nStep;
t0 = time.clock();
sumPayoff = 0.0;
idx = np.zeros(numExercise);

drift1 = (r1 - 0.5*sig1**2)*dt;
drift2 = (r2 - 0.5*sig2**2)*dt;
sigsqdt1 = sig1*sqrt(dt);
sigsqdt2 = sig2*sqrt(dt);

corr = [[1.0, rho],[rho, 1.0]];
M = np.matrix(np.linalg.cholesky(corr));

for i in xrange(ns):
    s1 = S0_1; s2 = S0_2;
    kievent = False; tag = False;
    cnt1 = 0; cnt2 = 0;
    payoff = 0.0; idx[:] = 0.0;

    z = M * np.matrix(np.random.normal(size = [2, nStep]));

    for j in xrange(nStep):
        s1 = s1 * exp(drift1 + sigsqdt1*z[0, j]);
        s2 = s2 * exp(drift2 + sigsqdt2*z[1, j]);

        wp = min(s1, s2);

        kievent = True if wp < Kib else kievent;

        if ((j+1) == obsDate[cnt1]):
            idx[cnt1] = wp;
            cnt1 += 1;

    for k in xrange(numExercise):
        if (idx[k] >= B[k]):
            payoff = payment[k];
            tag = True;
            cnt2 = k;
            break;

    if (tag == False):
        payoff = 100.0 * wp;
        if (kievent == False):
            if (wp >= Kib):
                payoff = face * (1.0 + dummy);
    sumPayoff += payoff * exp(-discr * obsDate[cnt2] / dateConv);

els1 = sumPayoff / ns;
t1 = time.clock();
del z;

print 'Monte Carlo Call Price : %.5f' % els1
print 'CPU time in Python(sec) : %.4f' % (t1-t0)


from sklearn.datasets import load_breast_cancer
import pandas as pd
import numpy as np
import random

class NearestNeighbors:
    def __init__(self, data, k=1, measure='e'):
        self.x = []
        self.y = []
        self.data = data
        validMeasure = ['e', 'm', 'manhattan', 'euclid']
        if measure in validMeasure:
            self.measure = measure
        else:
            raise IOError(f'use one of the valid measure: {validMeasure}')
        self.initialize()
        self.LengthOfLabels = self.findLengthOfLabels()
        self.k = k

    def initialize(self):
        for i in self.data:
            self.x.append(i[:-1])
            self.y.append(i[-1])
        return [self.x, self.y]

    @staticmethod
    def euclid(dataSetSample, NewSample):
        return np.sqrt(np.sum((np.array(dataSetSample) - np.array(NewSample)) ** 2))

    @staticmethod
    def manhattan(dataSetSample, NewSample):
        return np.sum(abs(np.array(dataSetSample) - np.array(NewSample)))

    def findLengthOfLabels(self):
        temp = np.unique(self.y)
        return {'groupName': list(temp), 'length': len(temp)}

    def analyzeResult(self, predictResult):
        result = sorted(predictResult)[:self.k]
        countGroup = list(np.zeros(self.LengthOfLabels['length']))
        for item in result[:self.k]:
            countGroup[self.LengthOfLabels['groupName'].index(item[1])] = +1
        return countGroup.index(max(countGroup))

    def predict(self, newX):
        result = []
        cal = 0
        for x, y in zip(self.x, self.y):
            if self.measure in ['e', 'euclid']:
                cal = self.euclid(x, newX)
            if self.measure in ['m', 'manhattan']:
                cal = self.manhattan(x, newX)
            result.append([cal, y])
        return self.analyzeResult(result)

if __name__ == '__main__':
    '''
    Test Algorithm In Cancer DataSet From Sklearn
    '''

    data = load_breast_cancer()
    DataSetData = data['data']
    DataSetLabel = data['target']

    DataSetData = pd.DataFrame(DataSetData)
    DataSetLabel = pd.DataFrame(DataSetLabel)

    allDataSet = pd.concat([DataSetData, DataSetLabel], axis=1).astype(float).values.tolist()
    random.shuffle(allDataSet)

    percentSlice = round(len(allDataSet) * 20 / 100)

    train, test = allDataSet[:percentSlice], allDataSet[percentSlice:]

    N = NearestNeighbors(train, k=3, measure='m')

    total = 0
    correct = 0
    for item in test:
        pr = N.predict(item[:-1])
        if pr == item[-1]:
            correct += 1
        total += 1
    print('accuracy:', correct / total)


"""ir.py: Script that compares various Interest Rate term structure models.

A common model used in the financial industry for modelling the short rate (think overnight rate, but actually an infinitesimally short amount of time) is the Vasicek model. #
Although it is unlikely to perfectly fit the yield curve, it has some nice properties that make it a good model to work with.
The dynamics of the Vasicek model are describe below.

In this model, the parameters  are constants, and the random motion is generated by the Q measure Brownian motion .
An important property of the Vasicek model is that the interest rate is mean reverting to , and the tendency to revert is controlled by .
Also, this process is a diffusion process, hence Markovian, which will lead to some nice closed form formulas.
Finally, the future value of the interest rate is normally distributed with the distribution .

"""

__author__ = "Alexander Baker"
__copyright__ = "Copyright 2012, The Hatch Project"
__credits__ = ["Alexander Baker"]
__license__ = "GPL"
__version__ = "1.0.1"
__maintainer__ = "Alexander Baker"
__email__ = "baker.alexander@gmail.com"
__status__ = "Production"

import numpy as np
from numpy.random import standard_normal
from numpy import array, zeros, sqrt, shape

from pylab import plot, title, xlabel, ylabel, show, figure, grid

r0 = 0.5 # current UK funding rate
theta = 1.00 # 1 % long term interest rate
k = 0.3
beta = 0.03

## simulate short rate paths
n = 10000    # MC simulation trials
T = 24.    # total time
m = 100.   # subintervals
dt = T/m  # difference in time each subinterval

r = np.zeros(shape=(n, m), dtype=float) # matrix to hold short rate paths

fig1 = figure(1)
fig1.clf()
ax1a = fig1.add_subplot(121)

for j in np.arange(0,n): # number of MC simulations
	r[j,0] = r0
	for i in np.arange(1,m): #trials per simulation
		r[j,i] = r[j,i-1] + k*(theta-r[j,i-1])*dt + beta*sqrt(dt)*standard_normal();
		#print j,i, r[j,i]
        ax1a.plot(np.arange(0, T, dt), r[j])

## plot paths
t = np.arange(0, T, dt)
rT_expected = theta + (r0-theta)*pow(np.e,-k*t)
rT_stdev = sqrt( pow(beta,2)/(2*k)*(1-pow(np.e,-2*k*t)))
print 'expected', rT_expected, 'std', rT_stdev

plot(np.arange(0, T, dt), rT_expected, '-or')
plot(np.arange(0, T, dt), rT_expected+2*rT_stdev, '-ob')
plot(np.arange(0, T, dt), rT_expected-2*rT_stdev, '-ob')

print shape(t), shape(r)

title('Simulations %d Steps %d r0 %.2f alpha %.2f beta %.2f sigma %.2f' % (int(n), int(m), r0, k, theta, beta))
xlabel('steps')
ylabel('short term interest rate')
grid()

stock_ending_price = r[:,-1]
print 'average ending rate ', np.mean(stock_ending_price)

ax2a = fig1.add_subplot(122)
ax2a.hist(stock_ending_price, 100)
ax2a.grid()

show()

#rT.stdev <- sqrt( beta^2/(2*k)*(1-exp(-2*k*t)))
#matplot(t, r[,1:10], type="l", lty=1, main="Short Rate Paths", ylab="rt")
#abline(h=theta, col="red", lty=2)
#lines(t, rT.expected, lty=2)
#lines(t, rT.expected + 2*rT.stdev, lty=2)
#lines(t, rT.expected - 2*rT.stdev, lty=2)
#points(0,r0)


__author__ = 'mike_bowles'
from urllib.request import urlopen
import sys
import numpy as np

# read data from uci data repository
target_url = ("https://archive.ics.uci.edu/ml/machine-learning-"
              "databases/undocumented/connectionist-bench/sonar/sonar.all-data")
# data = urllib2.urlopen(target_url)
data = urlopen(target_url)

# arrange data to list for labels and list of lists for attributes
xList = []
labels = []

for line in data:
    # split on comma
    row = line.decode('UTF-8').strip().split(",")
    xList.append(row)
nrow = len(xList)
ncol = len(xList[1])

type = [0] * 3
colCounts = []

# generate summary statistics for column 3 (e.g.)
col = 3
colData = []
for row in xList:
    colData.append(float(row[col]))

colArray = np.array(colData)
colMean = np.mean(colArray)
colsd = np.std(colArray)

sys.stdout.write("Mean = " + '\t' + str(colMean) + '\t\t' +
                 "Standard Deviation = " + '\t' + str(colsd) + "\n")

# calculate quantile boundaries
ntitles = 4

percentBdry = []

for i in range(ntitles + 1):
    percentBdry.append(np.percentile(colArray, i * (100) / ntitles))

sys.stdout.write("\nBoundaries for 4 Equal Percentiles \n ")
print(percentBdry)
sys.stdout.write("\n")

# run again with 10 equal intervals
ntitles = 10

percentBdry = []

for i in range(ntitles + 1):
    percentBdry.append(np.percentile(colArray, i * (100) / ntitles))

sys.stdout.write("Boundaries for 10 Equal Percentiles \n")
print(percentBdry)
sys.stdout.write(" \n")

# The last column contains categorical variables

col = 60
colData = []
for row in xList:
    colData.append(row[col])

unique = set(colData)
sys.stdout.write("Unique Label Values \n")
print(unique)

# count up the number of elements having each Values

catDict = dict(zip(list(unique), range(len(unique))))

catCount = [0] * 2

for elt in colData:
    catCount[catDict[elt]] += 1
sys.stdout.write("\n Counts for Each Values of categorical Label \n")
print(list(unique))
print(catCount)


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

import numpy as np
from numpy import linalg
from sympy import Matrix

def common(A):
    vecs = np.linalg.eig(A)
    for vec in vecs[1]:
        print(vec / np.linalg.norm(vec))
    print(vecs[0])
    print("-"*20)

def problem15():
    A = np.array([[-2, 2, 2, 2],
                [ 2, -6, 0, 0],
                [ 2, 0, -9, 3],
                [ 2, 0, 3, -9]])
    common(A)

def problem16():
    A = np.array([[ 4, 1.5, 0, -2],
                [ 1.5, 4, 2, 0],
                [ 0, 2, 4, 1.5],
                [ -2, 0, 1.5, 4]])
    common(A)

def problem17():
    A = np.array([[ 1, 4.5, 0, -6],
                [ 4.5, 1, 6, 0],
                [ 0, 6, 1, 4.5],
                [ -6, 0, 4.5, 1]])
    common(A)

def problem18():
    A = np.array([[ 11, -6, -6, -6],
                [ -6, -1, 0, 0],
                [ -6, 0, 0, -1],
                [ -6, 0, -1, 0]])
    common(A)

if __name__ == '__main__':
    problem15()
    problem16()
    problem17()
    problem18()


import numpy as np
import pandas as pd

def test_ks2(data1, data2):
    from scipy.stats import ks_2samp
    stat, p = ks_2samp(data1, data2)
    print('stat=%.3f, p=%.3f' % (stat, p))
    if p > 0.05:
        print('Probably the same distribution')
    else:
        print('Probably different distributions')

def test_uniform_num(data):
    from scipy.stats import uniform, ks_2samp
    dismin=np.amin(data)
    dismax=np.amax(data)
    T=uniform(dismin,dismax-dismin).rvs(data.shape[0])
    stat, p = ks_2samp(data, T)
    print('stat=%.3f, p=%.3f' % (stat, p))
    if p > 0.05:
        print('Probably is Uniform')
    else:
        print('Probably is not Uniform')

def getExtremePoints(data, typeOfInflexion = None, maxPoints = None):
    """
    This method returns the indeces where there is a change in the trend of the input series.
    typeOfInflexion = None returns all inflexion points, max only maximum values and min
    only min,
    """
    a = np.diff(data)
    asign = np.sign(a)
    signchange = ((np.roll(asign, 1) - asign) != 0).astype(int)
    idx = np.where(signchange ==1)[0]

    if typeOfInflexion == 'max' and data[idx[0]] < data[idx[1]]:
        idx = idx[1:][::2]

    elif typeOfInflexion == 'min' and data[idx[0]] > data[idx[1]]:
        idx = idx[1:][::2]
    elif typeOfInflexion is not None:
        idx = idx[::2]

    # sort ids by min value
    if 0 in idx:
        idx = np.delete(idx, 0)
    if (len(data)-1) in idx:
        idx = np.delete(idx, len(data)-1)
    idx = idx[np.argsort(data[idx])]
    # If we have maxpoints we want to make sure the timeseries has a cutpoint
    # in each segment, not all on a small interval
    if maxPoints is not None:
        idx= idx[:maxPoints]
        if len(idx) < maxPoints:
            return (np.arange(maxPoints) + 1) * (len(data)//(maxPoints + 1))

    return idx

## Get pdf estimated with KDE for 1D data
def get_pdf_1d(v:np.array)->(np.array, np.array, np.array):
    """
    Get pdf estimated with KDE for 1D data.
    v - 1D data array.
    return - (x, pdf, local maximum)
    """
    # get x values
    x = np.linspace(v.min(),v.max(), v.shape[0])
    # get kde kernel
    from scipy import stats
    kernel = stats.gaussian_kde(v)
    # get local maximum
    idx = getExtremePoints(kernel(x), typeOfInflexion='max')
    # return
    return x, kernel(x), idx

## Plot pdf estimated with KDE for 1D data
def plot_pdf_1d(v:np.array):
    # get pdf
    x, pdf_kde, local_max = get_pdf_1d(v)
    # plot
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots(figsize= (14, 6))
    ax.plot(x, pdf_kde, alpha = 0.8, label = f'Statsmodels CV maximum likelihood')
    for i in local_max:
        ax.scatter(x[i], pdf_kde[i], s= 40, c = 'red')
    for i in local_max:
        ax.annotate(f' Max = {round(x[i], 2)}', (x[i], pdf_kde[i]))
    plt.show()

def test_dip(data, alpha = 0.05, verbose = True)->bool:
    import unidip.dip as dip
    # sort data
    data = np.msort(data)
    # test
    stat, p, _ = dip.diptst(data)
    # display
    if verbose:
        print('stat=%.3f, p=%.3f' % (stat, p))
    if p > 0.05:
        print('Probably unimodal')
    else:
        print('Probably not unimodal.')

## Estimate 1d clusters with KDE
def get_1d_clusters_kde(v:np.array, verbose = True)->np.array:
    """
    Estimate 1d clusters with KDE.
    v -- data array to be used.
    verbose -- display extra information (default, True)
    result -- array of estimated labels.
    """

    # get pdf and local maximuns
    pdf_kde, x, local_max = get_pdf_1d(v)
    # display results
    num_modals = len(local_max)
    # display
    if verbose:
        print(f'number of modals = {num_modals}')
    # reshape data
    v = v.reshape(-1, 1)
    # KDE algorithm
    from sklearn.mixture import GaussianMixture
    gmm = GaussianMixture(num_modals, covariance_type='full', random_state=0).fit(v)
    # get cluster means
    cluster_means = [cm for cm in gmm.means_]
    # display
    if verbose:
        print(f'cluster means: {cluster_means}')
    # get labels
    labels = gmm.predict(v)
    # return
    return labels

## Plot 1d clusters
def plot_1d_clusters_kde(v, labels):
    """
    Plot 1d clusters.
    v -- original data.
    labels -- its respective clusters.
    """
    # data and labels to df
    result = pd.DataFrame({'data':v.ravel(), 'cluster': labels})
    # get unique values of clusters
    clusters = sorted(list(result.cluster.unique()))
    # plot
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    colors = ['green', 'blue', 'red', 'yellow', 'orange', 'grey']
    for cluster, color in zip(clusters, colors[:len(clusters)]):
        temp = result[result.cluster == cluster]
        iv = temp.data.values
        ax.hist(iv, color = color, alpha = 0.5)
        del iv
    ax.set_title(f'number of clusters = {len(clusters)}')
    plt.show()


"""pebbles.py

    functions used to approximate pi with the Montecarlo method.

    Circumference center is at (0,0) and has a radius of 1. The square has a side of 2 and also centered in (0,0)
"""

import random
# import math
from matplotlib import pyplot as plt
import numpy as np

# Avoid automatic display of the figures
plt.ioff()

def pointIsInside(x,y):
    """pointIsInside

    Arguments:
    x,y -- x and y coordinates of the point.

    returns true if it is inside of the Circumference.
    """
    return x**2 + y**2 <= 1.

class MontecarloApprox:
    """MontecarloApprox class

    Description: class that implements an approximation to the number
    pi using the Montecarlo method.

    Attributes of the instances:
    - self.points -- a list of the pebbles positions. Each point is a tuple in the
        (x_coordinate,y_coordinate) format
    - self.pi_values -- a list containing the value of pi after every pebble thrown
    - self.points_inside -- the number of pebbles inside of the circumference
    - self.points_total -- the total number of pebbles
    """
    def __init__(self):
        self.points = []
        self.pi_values = []
        self.points_inside = 0
        self.points_total = 0

    def __str__(self):
        return '{:.8f}'.format(self.returnBestPiValue())

    def returnBestPiValue(self):
        if self.pi_values:
            return self.pi_values[-1]
        else:
            return 0.

    def updatePi(self):
        self.pi_values.append(4 * self.points_inside / self.points_total)

    def addNewPoint(self,n=1):
        """addNewPoint

        Description: adds n points to the board.

        Arguments:
        n -- number of new pebbles (defaults to 1)
        """
        for i in range(n):
            x = 2*random.random() - 1.
            y = 2*random.random() - 1.
            self.points.append( (x,y) )
            self.points_total += 1
            if pointIsInside(x, y):
                self.points_inside += 1
            self.updatePi()

def plotTheMonteCarloExperiment(points):
    """plots the positions of the pebbles in the Montecarlo experiment

    Arguments:
    points -- the list of the points of the positions
    """

    # Create a figure
    fig,ax = plt.subplots(constrained_layout=True,figsize=(8,8))

    #
    ax.set_facecolor('lightgreen')

    # Deactivate axis ticks
    ax.tick_params(
        axis='both',       # changes apply to the x,y-axis
        which='both',      # both major and minor ticks are affected
        bottom=False,      # ticks along the bottom edge are off
        top=False,         # ticks along the top edge are off
        right = False,
        left = False,
        labelbottom=False,
        labelleft=False) # labels along the bottom edge are off

    ax.set_aspect(1.)
    ax.set_xlim(-1.01,1.01)
    ax.set_ylim(-1.01,1.01)

    rho_circle = np.linspace(0, 2 * np.pi,100)
    r_circle = np.ones(np.shape(rho_circle))

    x_circle = r_circle * np.cos(rho_circle)
    y_circle = r_circle * np.sin(rho_circle)

    ax.fill(x_circle,y_circle,facecolor='lightblue',edgecolor='blue',zorder=1,linewidth=3)

    # First the points have to be unpacked in a x and y values

    if points:
        x, y = zip(*points)
        ax.scatter(x,y,alpha=0.75,s=50.,c='black',zorder=2)

    return fig


# Exercise 4
# In a popular board game, the players roll two dice at every turn. They want to test the fairness of the dice, so
# they note the number of occurrences of each possible result, from 2 to 12. The collected data are as follows
# values      2  3  4  5  6  7  8  9 10 11 12
# occurrences 1  4  2  7 10  9  9 14  7  5  3
# 1. Find the probability mass function of the distribution
# 2. Run a chi-squared test on this data to check if it is in accordance with the discrete triangular distribution
# that characterizes a 2-dice roll: ...
# Discuss the result of the test.

# import
import numpy as np
from matplotlib import pyplot as plt
from numpy import mean, min, max, median, quantile
from scipy.stats import binom, norm, t as student, expon, poisson, chi2
from math import sqrt, pow, floor, ceil, exp

# define corrected functions for std and variance
def variance(values):
	return np.var(values, ddof=1)

def std(values):
	return np.std(values, ddof=1)

def triangular(k):
	if k >= 2 and k <= 7:
		return 1 / 36 * (k - 1)
	else:
		return 1 / 36 * (13 - k)

data = np.array([1, 4, 2, 7, 10, 9, 9, 14, 7, 5, 3])
X = range(2, 13)  # possible values
N = sum(data)  # number of trials
Y = data / N  # sample probabilities
plt.plot(X, Y, 'o--', color='k')  # bar, lines o cosa?
# plt.show()
Y = [triangular(k) for k in X]
plt.plot(X, Y, 'o--', color='r')
plt.show()

K = len(X)
T = 0
for i in range(K):
	k = X[i]
	T += (data[i] - triangular(k) * N)**2 / (N * triangular(k))

print(f"T = {T: .3f}")
print(f"Prob t>=T (p-value) = {1 - chi2.cdf(T, K-1): .4f}")


# Python program to calculate Prime numbers and subsequently Prime Factors

"""
Usage: Prime-numbers.py

Generates a sequence of Prime numbers up to the given number (n)
and also generates Prime factors for any user given number

At prompt, enter only numbers

Options
-------

-h or help		Displays this message

"""

from sys import argv, exit
import numpy as np

def prime(num):
    primeNumbers = []
    for i in np.arange(num+1):
        count_zero = 0
        if i >= 2:
            value = i
            for j in np.arange(1, value+1):
                rem = value % j
                if rem == 0:
                    count_zero = count_zero + 1
                    if count_zero >= 3:
                        break
        if count_zero == 2:
            primeNumbers.append(i)

    return primeNumbers

def factor(facNum):
    primeFactors = []
    for pN in primeNumbersArray:
        while True:
            if (facNum % pN) == 0:
                facValue = facNum / pN
                primeFactors.append(pN)
                facNum = facValue
            else:
                break

    return primeFactors

if len(argv) > 1:
    print(__doc__)
    exit(0)

while True:
    number = raw_input("Enter the number: ")
    try:
        n = int(number)
        break
    except:
        print "Error: Enter only numbers"
        continue

primeNumbers = prime(n)
print "List of Prime Numbers up to %d:" % (n)
print primeNumbers
primeNumbersArray = np.array(primeNumbers)

while True:
    pFNum = raw_input("\nNow enter any other number to obtain the Prime Factors: ")
    try:
        pFNumber = int(pFNum)
        break
    except:
        print "Error: Enter only numbers"

if pFNumber in primeNumbersArray:
    print "Its a Prime!"
else:
    primeFactors = factor(pFNumber)
    if len(primeFactors) < 2:
        print ("Your Prime Numbers list is too short to obtain the prime factors",
"Regenerate the Prime Numbers list")
    else:
        print "The Prime Factors for %d are:" %(pFNumber)
        print primeFactors


from sklearn import datasets
import numpy as np
import collections
from sklearn.naive_bayes import GaussianNB
import ml_helpers

def compute_gaussian_probability(val, mean, var):
	coeff = 1/(np.sqrt(2*np.pi) * np.sqrt(var))
	exponent = -1 * (((val - mean) ** 2) / (2 * var))
	return coeff * np.exp(exponent)

# Import the Iris flower dataset
iris = datasets.load_iris()
train_data = np.array(iris.data)
train_labels = np.array(iris.target)
num_features = train_data.data.shape[1]

# Get the class probabilities
unique_labels, class_probs = np.unique(train_labels, return_counts=True)

# Get the mean and variance of the features for ALL of the classes
mean_features, var_features = ml_helpers.compute_mean_and_var(train_data)

# Get the mean and variance of the features for EACH of the classes
class_mean_features = np.zeros((len(unique_labels), num_features))
class_var_features = np.zeros((len(unique_labels), num_features))
count = 0
for curr_label in unique_labels:
	class_data_indices = np.where(train_labels == curr_label)[0]

	temp_data = train_data[class_data_indices]
	class_mean_features[count, :], class_var_features[count, :] = ml_helpers.compute_mean_and_var(temp_data)
	count = count + 1

# ***************************************************************
# Apply the Naive Bayes Classifier MANUALLY
# ***************************************************************
predicted_classes = np.zeros(len(train_labels))
for sample_index, sample in enumerate(train_data):

	sample_probs = np.zeros(len(unique_labels))
	for curr_label_index, curr_label in enumerate(unique_labels):
		liklihood = np.prod(compute_gaussian_probability(sample, class_mean_features[curr_label_index], class_var_features[curr_label_index]))
		class_prior_prob = class_probs[curr_label_index]
		predictor_prior_prob = np.prod(compute_gaussian_probability(sample, mean_features[curr_label_index], var_features[curr_label_index]))
		sample_probs[curr_label_index] = (liklihood * class_prior_prob) / predictor_prior_prob

	predicted_classes[sample_index] = np.argmax(sample_probs)

# ***************************************************************
# Apply the Naive Bayes Classifier USING SCIKIT LEARN
# ***************************************************************
gnb = GaussianNB()
y_pred = gnb.fit(iris.data, iris.target).predict(iris.data)
print("Scikit Learn Classification Accuracy =", (iris.target == y_pred).sum()/ iris.data.shape[0])

# Compute the classification accuracy
Accuracy = 0
for label_index, curr_label in enumerate(train_labels):
	if curr_label == predicted_classes[label_index]:
		Accuracy = Accuracy + 1

Accuracy = Accuracy / len(train_labels)
print("Manual Classification Accuracy =", Accuracy)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 18 19:47:01 2017

este scrip suelto es para testear el fiteo de la hiperparabola con una parabola
perfectamente conocida aun poniendole ruido

@author: sebalander
"""
# %% imports
import numpy as np
from dev import multipolyfit as mpf
import matplotlib.pyplot as plt

# %% useful funcs
def coefs2mats(coefs, n=8):
    const = coefs[0]
    jac = coefs[1:n+1]
    hes = np.zeros((n,n))
    hes[np.triu_indices(n)] = hes.T[np.triu_indices(n)] = coefs[n+1:]

    hes[np.diag_indices(n)] *= 2
    return const, jac, hes

# invento ejemplo para testear el fiteo de hiperparabola

# vert = np.random.rand(8) # considero centrado en el origen, no pierdo nada
jac = np.random.rand(8) * 2 -1
hes = np.random.rand(8,8) * 2 - 1
hes = (hes + hes.T) / 2 # lo hago simetrico

deltaX = np.random.rand(10000, 8)
#y = deltaX.dot(jac)
#y += (deltaX.reshape((-1,8,1,1))
#      * hes.reshape((1,8,8,1))
#      * deltaX.T.reshape((1,1,8,-1))).sum(axis=(1,2)).diagonal() / 2

y = np.array([jac.dot(x) + x.dot(hes).dot(x) / 2 for x in deltaX])

# %% hago el fiteo
ruidoY = np.random.randn(y.shape[0]) * 0.1

coefs, exps = mpf.multipolyfit(deltaX, y + ruidoY, 2, powers_out=True)

constRan, jacRan, hesRan = coefs2mats(coefs)

testRan = [constRan + jacRan.dot(x) + x.dot(hesRan).dot(x) / 2 for x in deltaX]

plt.figure()
plt.plot(y, testRan, 'x')
emin, emax = [np.min(y), np.max(y)]
plt.plot([emin, emax],[emin, emax])

plt.figure()
plt.plot(hes[np.tril_indices_from(hes)], hesRan[np.tril_indices_from(hesRan)], '+')

plt.figure()
plt.plot(y, y+ruidoY, 'x')

# %%

hUli = np.array([[2,-10],[-10,2]])
xx = np.random.rand(1000, 2) * 2 - 1

yy = [x.dot(hUli).dot(x) / 2 for x in xx]

coefs, exps = mpf.multipolyfit(xx, yy, 2, powers_out=True)

constRan, jacRan, hesRan = coefs2mats(coefs,n=2)


# Created by Lena
from statsmodels.tsa.ar_model import AR
from pandas.plotting import autocorrelation_plot
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.graphics.tsaplots import plot_pacf
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')

PATH = "plots/lena/"

df = pd.read_csv("data/^GSPC.csv", index_col=0, parse_dates=True)
df.head(10)

# checking for Autocorrelation
# Autocorrelation plots
autocorrelation_plot(df)
plt.savefig(PATH + "Autocorrelation plot")
plt.show()

# Autocorrelation Function (ACF) and Partial Autocorrelation Function (PACF) plots
plot_acf(df["Adj Close"], lags=50) # lag 50 days
plt.savefig(PATH + "Autocorrelation Function plot")
plt.show()

plot_pacf(df["Adj Close"], lags=50)
plt.savefig(PATH + "Partial Autocorrelation Function plot")
plt.show()

# If the time series is stationary, the ACF/PACF plots will show a quick drop-off in correlation after a
# small amount of lag between points.
# This data is non-stationary as a high number of previous observations are correlated with future values.

### Autoregressive model ###

# train / test split
# test - predictions of last 5 years of the Adj. Close price
test_length = 1265
X = df["Adj Close"].values
train, test = X[1:len(X)-test_length], X[len(X)-test_length:]

# Fit AR model
model = AR(train)
model_fit = model.fit(maxlag=19, ic="bic")
# bic method determined best value for maxlag is 19

# make predictions for the last 5 years
train_predictions = model_fit.predict(start=19, end=len(train))
predictions = model_fit.predict(start=len(train)-1, end=len(train)+len(test)-1) # 22109 to 23374

# plot AR model train results
plt.figure(figsize = (15,10))
plt.plot(train, label = "Train Adjusted close price")
plt.plot(train_predictions, color='red', label = "AR(p) model predictions")
plt.title("Autoregressive model AR(p) - Train data")
plt.legend()
plt.savefig(PATH + "AR_Train")
plt.show()

# plot AR model prediction results
plt.figure(figsize = (15,10))
plt.plot(test, label = "Test Adjusted close price")
plt.plot(predictions, color='red', label = "AR(p) model predictions")
plt.title("Autoregressive model AR(p) - Test data")
plt.legend()
plt.savefig(PATH + "AR_Test")
plt.show()

# Train and test prediction results show that model is highly overfitted.
# The model can't generalise well and performs poorly on unseen test data - resulting in linear fit.


"""Print an input number into a series."""

from sympy import Symbol, pprint, init_printing

def print_series(n, x_value):
    """Print the series.

    x + x**2 + X**3 +... + x**n
        _       _           _
        2       3           n
    """
    init_printing(order='rev-lex')
    x = Symbol('x')
    series = x
    for i in range(2, n + 1):
        series = series + (x**i) / i
    pprint(series)

    series_value = series.subs({x:x_value})
    print('Value of the series at {0}: {1}'.format(x_value, series_value))

if __name__ == '__main__':
    n = input('Enter the number of terms you want in the series: ')
    x_value = input('Enter the value of x at which you want to evaluate the series: ')
    print_series(int(n), float(x_value))


import unittest

import numpy as np
import pandas as pd

from pyalink.alink import *

class TestSpecialModels(unittest.TestCase):

    def test_wrap_model(self):
        source = CsvSourceBatchOp() \
            .setSchemaStr(
            "sepal_length double, sepal_width double, petal_length double, petal_width double, category string") \
            .setFilePath("https://alink-test-data.oss-cn-hangzhou.aliyuncs.com/iris.csv")
        stage1 = QuantileDiscretizer().setNumBuckets(2).setSelectedCols(["sepal_length"])
        model1 = stage1.fit(source)
        output1 = model1.transform(source)
        print(model1)
        print(dir(model1))
        pass

    def test_glr(self):
        data = np.array([
            [1, 5, 118, 69, 1.0, 2.0],
            [2, 10, 58, 35, 1.0, 2.0],
            [3, 15, 42, 26, 1.0, 2.0],
            [4, 20, 35, 21, 1.0, 2.0],
            [5, 30, 27, 18, 1.0, 2.0],
            [6, 40, 25, 16, 1.0, 2.0],
            [7, 60, 21, 13, 1.0, 2.0],
            [8, 80, 19, 12, 1.0, 2.0],
            [9, 100, 18, 12, 1.0, 2.0]
        ])
        df = pd.DataFrame({
            "id": data[:, 0],
            "u": data[:, 1],
            "lot1": data[:, 2],
            "lot2": data[:, 3],
            "offset": data[:, 4],
            "weights": data[:, 5],
        }).astype({
            "id": np.int64,
            "u": np.int64,
            "lot1": np.int64,
            "lot2": np.int64,
            "offset": np.float64,
            "weights": np.float64,
        })
        print(df)
        print(df.dtypes)

        source = dataframeToOperator(df,
                                     schemaStr="id int, u double, lot1 double, lot2 double, offset double, weights double",
                                     op_type="batch")

        featureColNames = ["lot1", "lot2"]
        labelColName = "u"

        glm = GeneralizedLinearRegression() \
            .setFamily("gamma") \
            .setLink("Log") \
            .setRegParam(0.3) \
            .setFitIntercept(False) \
            .setMaxIter(10) \
            .setOffsetCol("offset") \
            .setWeightCol("weights") \
            .setFitIntercept(False) \
            .setFeatureCols(featureColNames) \
            .setLabelCol(labelColName) \
            .setPredictionCol("pred")

        model = glm.fit(source)
        print(type(model))
        print(dir(model))
        print(model.transform(source).collectToDataframe())


#! /usr/bin/env python

#All of these operations can be found in Kathrin Schacke's "On the Kronecker Product" written on August 1, 2013. This paper provides a review of the properties of the kronecker product, as well its history and applications. This code will implement many of the properties given in Section 2 : The Kronecker Product
#All sections are split off by KRON# which is specified in the previous paper.
#In general there are different levels of restrictions to operations being done, but they are satisfied in general by randomly generated square matrices. Some properties require specific properties of the matrix to be held, but that will be done on a case by case basis.
import unittest
from scipy.stats import ortho_group
import numpy as np

import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from functools import reduce
print("FILE IS", __file__)
from src.operations import *

class TestOperations(unittest.TestCase):
#____________________HELPER FUNCTIONS________________
    # add global stuff here
    def setUp(self):
        self.p = 3

    def generateMatrices(self,n,p):
        if n < 0:
            assert "[TestOperations - generateMatrices] Can't generate negative amount of matrices"
        if p <= 0:
            assert "[TestOperations - generateMatrices] Can't generate matrix with p less than or equal to 0"

        r_As = [np.random.rand(p,p) for i in range(n)]
        As = [m/m.sum(axis=1)[:,None] for m in r_As] # normalize each row
        x = np.random.rand(p**n)
        return As, x

#_________________KRON 1 ____________________
#Apply a constant to a matrix. When applying a constant to a kron product it doesn't matter where its applied, as long as its applied once.
#(cA) kron B == A kron (cB) == c(A kron B)
    def test_applyConstant(self):
        As, x = self.generateMatrices(1,self.p)
        constant = 3.14159
        results_func = applyConstant(As[0], constant)
        results_truth = As[0] * constant
        if(np.allclose(results_func,results_truth)):
            return
        else:
            self.fail("Results not equal")
    def test_KRON1(self):
        As, x = self.generateMatrices(2,self.p)
        constant = 3.14159
        results1 = reduce(np.kron, [applyConstant(As[0],constant), As[1]])
        results2 = reduce(np.kron, [As[0], applyConstant(As[1],constant)])
        results3 = applyConstant(reduce(np.kron, As), constant)
        if(np.allclose(results1, results2, results3)):
            return
        else:
            self.fail("Results not equal")
#________________KRON 2______________________
#Taking the transpose of the kron prod is the same thing as taking the kron prod after
#(A kron B).T = A.T kron B.T
    def test_KRON2(self):
        As, x = self.generateMatrices(2,2)
        results1 = transpose(reduce(np.kron, As))
        results2 = reduce(np.kron,transpose(As))
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
#________________KRON 3____________________-
#Taking the complex conjugate before carrying out the Kron product yields the same results as doing so afterwards
#(A kron B)* = A* kron B*
    def test_KRON3(self):
        As, x = self.generateMatrices(2,self.p)
        results1 = complexConjugate(reduce(np.kron, As))
        results2 = reduce(np.kron,complexConjugate(As))
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
#_______________KRON 4_________________________
#The kron product is associative
#(A kron B) kron C = A kron (B kron C)
    def test_KRON4(self):
        As, x = self.generateMatrices(3,self.p)
        results1 = reduce(np.kron,[reduce(np.kron,[As[0],As[1]]),As[2]])
        results2 = reduce(np.kron,[As[0], reduce(np.kron,[As[1],As[2]])])
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
#______________KRON 5_________________________
#The kron product is right-distrutive
#(A+B) kron C = A kron C + B kron C
    def test_KRON5(self):
        [A,B,C], x = self.generateMatrices(3,self.p)
        results1 = reduce(np.kron,[(A+B),C])
        results2 = reduce(np.kron,[A,C]) + reduce(np.kron,[B,C])
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
#______________KRON 6________________________
#The Kron product is left-distributive
#A kron (B+C) = A kron B + A kron C
    def test_KRON6(self):
        [A,B,C], x = self.generateMatrices(3,self.p)
        results1 = reduce(np.kron,[A,(B+C)])
        results2 = reduce(np.kron,[A,B]) + reduce(np.kron,[A,C])
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
#______________KRON 7_______________________
#The product of two kron products yields another kron product
#(A kron B)(C kron D) = AC kron BD
    def test_KRON7(self):
        [A,B,C,D], x = self.generateMatrices(4,self.p)
        results1 = (reduce(np.kron,[A,B]))*(reduce(np.kron,[C,D]))
        results2 = reduce(np.kron,[(A*C),(B*D)])
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
#_____________KRON 8 ____________________
#The trace of the kron product of two matrices is the product of the races of the matrices
#trace(A kron B) = trace(B kron A) = trace(A)*trace(B)
    def test_KRON8(self):
        [A,B], x = self.generateMatrices(2,self.p)
        results1 = trace(reduce(np.kron,[A,B]))
        results2 = trace([A,B])
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
#_____________KRON 9 _________________________
#det(A kron B) = det(B kron A) = (det(A))^n*(det(B))^m
    def test_KRON9(self):
        [A,B], x = self.generateMatrices(2,self.p)
        results1 = det(reduce(np.kron,[A,B]))
        results2 = det([A,B])
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")

#_____________KRON 10 ___________________________
#If A is mxm and B is nxn and are non singular then
#(A kron B) ^-1 = A^-1 kron B^-1
#This also applies for pinv
    def test_KRON10(self):
        A = ortho_group.rvs(dim=self.p)
        B = ortho_group.rvs(dim=self.p)
        results1 = invert(reduce(np.kron,[A,B]))
        results2 = reduce(np.kron,invert([A,B]))
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
    #Test invert function when a singular matrix is given
    def test_KRON10_pinv(self):
        A = ortho_group.rvs(dim=self.p)
        B = ortho_group.rvs(dim=self.p)
        A[1,:] = A[0,:]
        B[1,:] = B[0,:]
        results1 = invert(reduce(np.kron,[A,B]))
        results2 = reduce(np.kron,invert([A,B]))
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")
    #Test psued-invert function when a singular matrix is given
    def test_KRON10_pinv_function(self):
        A = ortho_group.rvs(dim=self.p)
        B = ortho_group.rvs(dim=self.p)
        A[1,:] = A[0,:]
        B[1,:] = B[0,:]
        results1 = pinvert(reduce(np.kron,[A,B]))
        results2 = reduce(np.kron,pinvert([A,B]))
        if(np.allclose(results1,results2)):
            return
        else:
            self.fail("Results not equal")


import numpy as np
import matplotlib.pyplot as plt

x=np.array([1.0,2.0,3.0,4.0,2.0,1.0])
k = 20
y = np.zeros(20)

y[0] = x[0]
y[1] = -0.5*y[0]+x[1]

for n in range(2,k-1):
	if n < 6:
		y[n] = -0.5*y[n-1]+x[n]+x[n-2]
	elif n > 5 and n < 8:
		y[n] = -0.5*y[n-1]+x[n-2]
	else:
		y[n] = -0.5*y[n-1]
print(y)

#subplots
plt.subplot(2, 1, 1)
plt.stem(range(0,6),x)
plt.title('Digital Filter Input-Output')
plt.ylabel('$x(n)$')
plt.grid()# minor

plt.subplot(2, 1, 2)
plt.stem(range(0,k),y)
plt.xlabel('$n$')
plt.ylabel('$y(n)$')
plt.grid()# minor

plt.savefig('xnyn.png')


# Copyright 2021 Peizhi Yan. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
from __future__ import absolute_import

import numpy as np

def sin(x):
    """sin"""
    return np.sin(x)

def d_sin(x):
    """derivative of sin"""
    return np.cos(x)

def sigmoid(x):
    """sigmoid function"""
    # sigmoid(x) = 1 / (1 + e^(-x))
    return 1. / (1. + np.e**(-x))

def d_sigmoid(x):
    """derivative of sigmoid function"""
    return sigmoid(x) * (1. - sigmoid(x))

def relu(x):
    """ReLU"""
    # relu(x) = max(0, x)
    return x * (x > 0)

def d_relu(x):
    """derivative of ReLU"""
    return 1. * (x > 0)

def linear(x):
    """linear function"""
    return x

def d_linear(x):
    """derivative of linear function"""
    return 1. * (x * 0 == 0)


#!/usr/bin/env python3
#
# Demo to show second-order collocation method for BVP
#     u'' + (1/(2+x)) u' + (11x/(2+x)) u = (-e^x (12x^3 + 7x^2 + 1))/(2+x), -1<x<1
#     u(-1) = u(1) = 0
# using various uniform mesh sizes.
#
# Daniel R. Reynolds
# SMU Mathematics
# Math 4315

# imports
import numpy
import matplotlib.pyplot as plt
from differentiation_matrix import *

### utility functions ###

def enforce_boundary(A,rhs):
    """
    Utility routine to enforce the homogeneous Dirichlet boundary conditions on
    the linear system encoded in the matrix A and right-hand side vector r.
    This routine assumes that the inputs include placeholder rows for these
    conditions.
    """
    A[0,:] = 0*A[0,:]
    A[-1,:] = 0*A[-1,:]
    A[0,0] = numpy.max(numpy.abs(numpy.diag(A)))
    A[-1,-1] = numpy.max(numpy.abs(numpy.diag(A)))
    rhs[0] = 0
    rhs[-1] = 0
    return [A, rhs]

### main script ###

# set numbers of intervals for tests
nvals = [10, 20, 40, 80, 160, 320]

# setup problem, analytical solution, etc
a = -1.0
b = 1.0
def p(x):
    return (1.0/(2+x))
def q(x):
    return (11.0*x/(2+x))
def r(x):
    return (-numpy.exp(x)*(12*x**3 + 7*x**2 + 1)/(2+x))
def utrue(x):
    return (numpy.exp(x)*(1-x**2))

# initialize plots
plt.figure(1)
x = numpy.linspace(a,b,1000)
plt.plot(x, utrue(x), label='$\hat{u}(x)$')

# initialize 'current' mesh size and error norm
h_cur = 1.0
e_cur = 1.0

# loop over partition sizes
for n in nvals:

    # get partition and differentiation matrices
    t, Dx  = differentiation_matrix(n, a, b, 2, 1)
    t, Dxx = differentiation_matrix(n, a, b, 2, 2)

    # create additional diagonal matrices
    P = numpy.diag(p(t))
    Q = numpy.diag(q(t))
    rhs = r(t)
    A, rhs = enforce_boundary(Dxx + P@Dx + Q, rhs)

    # solve linear system, compute error and h
    u = numpy.linalg.solve(A, rhs)
    e = numpy.abs(u-utrue(t))
    e_old = e_cur
    e_cur = numpy.linalg.norm(e,numpy.Inf)
    h_old = h_cur
    h_cur = numpy.max(t[1:]-t[:-1])

    # update plots
    plt.figure(1)
    plt.plot(t, u, label=('$u_{%i}(x)$' % (n)))
    plt.figure(2)
    plt.semilogy(t, e, label=('$|\hat{u}-u_{%i}|$' % (n)))

    # output current error norm and estimated convergence rate
    if (n > nvals[0]):
        print('n = %3i,  h = %.2e, ||error|| = %.2e,  conv. rate = %.2f' %
              (n, h_cur, e_cur, numpy.log(e_cur/e_old)/numpy.log(h_cur/h_old)))
    else:
        print('n = %3i,  h = %.2e, ||error|| = %.2e' % (n, h_cur, e_cur))

# finalize plots
plt.figure(1)
plt.xlabel('$x$')
plt.ylabel('$u(x)$')
plt.legend()
plt.title('Second-order FD Approximations')

plt.figure(2)
plt.xlabel('$x$')
plt.ylabel('$|\hat{u}(x) - u(x)|$')
plt.legend()
plt.title('Second-order FD Approximation Error')

plt.show()

# end of script


# This module is dedicated to summarizing single channel signal data and extracting features from it.

import numpy as np

def normalise_signal(signal):
	"""Normalise the signal to zero mean, unit variance"""
	signal_mean = np.mean(signal)
	signal_stdev = np.std(signal)
	signal_normed = [((x-signal_mean)/signal_stdev) for x in signal]
	return(signal_normed)

def mean(signal):
	""" The mean of the raw signal"""
	return(np.mean(signal))

def std_dev(signal):
	"""The standard deviation of the raw signal"""
	return(np.std(signal))

def first_differences(signal):
	"""The mean of the absolute values of the first differences of the raw signal"""
	# build the first differences list
	first_diff = []
	for i in range(0,len(signal)-1):
		first_diff.append(abs(signal[i+1]-signal[i]))

	fd_sum = sum(first_diff)
	delta = float(fd_sum)/(len(signal)-1)
	return(delta)

def norm_first_differences(signal):
	"""The mean of the absolute values of the first differences of the normalized signal"""
	signal_normed = normalise_signal(signal)
	first_diff = []
	for i in range(0,len(signal_normed)-1):
		first_diff.append(abs(signal_normed[i+1]-signal_normed[i]))

	fd_sum = sum(first_diff)
	delta = float(fd_sum)/(len(signal_normed)-1)
	return(delta)

def second_differences(signal):
	"""The mean of the absolute values of the second differences of the raw signal"""
	sec_diff = []
	for i in range(0,len(signal)-2):
		sec_diff.append(abs(signal[i+2]-signal[i]))

	fd_sum = sum(sec_diff)
	delta = float(fd_sum)/(len(signal)-2)
	return(delta)

def norm_second_differences(signal):
	"""The mean of the absolute values of the second differences of the normalized signal"""
	signal_normed = normalise_signal(signal)
	sec_diff = []
	for i in range(0,len(signal_normed)-2):
		sec_diff.append(abs(signal_normed[i+2]-signal_normed[i]))

	fd_sum = sum(sec_diff)
	delta = float(fd_sum)/(len(signal_normed)-2)
	return(delta)

def package_deal_signal(signal):
	"""Returns the package of normalised mean, stdev, normalized first and second differences"""
	normedSig = normalise_signal(signal)
	m = mean(normedSig)
	s = std_dev(normedSig)
	nfd = norm_first_differences(signal)
	nsd = norm_second_differences(signal)

	return m,s,nfd,nsd

def package_deal_signal_mm(signal):
	"""Returns the package of normalised mean, stdev, normalized first and second differences"""
	normedSig = normalise_signal(signal)
	m = mean(normedSig)
	s = std_dev(normedSig)
	mini = min(normedSig)
	maxi = max(normedSig)
	nfd = norm_first_differences(signal)
	nsd = norm_second_differences(signal)

	return m,s,nfd,nsd, mini, maxi

def data_binarizer(ratings, threshold):
	"""binarizes the data below and above the threshold"""
	binarized = []
	for rating in ratings:
		if rating <= threshold:
			binarized.append(0)
		else:
			binarized.append(1)
	return binarized


"""
A small collection of well-known functions for testing or benchmarking
"""
import numpy as np
import torch as to

import pyrado

def rosenbrock(x: [to.Tensor, np.ndarray]) -> (to.Tensor, np.ndarray):
    """
    The Rosenbrock function
    (consistent with https://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html)
    :param x: multi-dim column vector, or array thereof
    :return: value of the rosenbrock function at the input point, or array thereof
    """
    if isinstance(x, to.Tensor):
        return to.sum(100.*to.pow(x[1:] - to.pow(x[:-1], 2), 2) + to.pow((1. - x[:-1]), 2), dim=0)
    elif isinstance(x, np.ndarray):
        return np.sum(100.*np.power(x[1:] - np.power(x[:-1], 2), 2) + np.power((1. - x[:-1]), 2), axis=0)
    else:
        raise pyrado.TypeErr(given=x, expected_type=[np.ndarray, to.Tensor])

def noisy_nonlin_fcn(x: [to.Tensor, np.ndarray], f: float = 1., noise_std: float = 0.) -> [to.Tensor, np.ndarray]:
    """
    A 1-dim function (sinus superposed with polynomial), representing the black box function in Bayesian optimization

    :param x: function argument
    :param noise_std: scale of the additive noise sampled from a standard normal distribution
    :param f: frequency of the sinus wave [Hz]
    :return: function value
    """
    if isinstance(x, to.Tensor):
        return -to.sin(2*np.pi*f*x) - to.pow(x, 2) + 0.7*x + noise_std*to.randn_like(x)
    elif isinstance(x, np.ndarray):
        return -np.sin(2*np.pi*f*x) - np.power(x, 2) + 0.7*x + noise_std*np.random.randn(*x.shape)
    else:
        raise pyrado.TypeErr(given=x, expected_type=[np.ndarray, to.Tensor])


import copy
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.mixture import GaussianMixture
from sklearn.cluster import KMeans

# Importing the dataset
data = pd.read_csv("ex.csv")
print("Input Data and Shape:")
print(data.head(3))
print("Shape:", data.shape)

# Getting the values and plotting it
f1 = data['V1'].values
f2 = data['V2'].values
X = np.array(list(zip(f1, f2)))
print('Graph for whole dataset')
plt.scatter(f1, f2, c='black', s=50)
plt.show()

##########################################
kmeans = KMeans(2, random_state=0)
labels = kmeans.fit(X).predict(X)
centroids = kmeans.cluster_centers_
print("Labels KMeans:", labels)

print('Graph using Kmeans Algorithm')
# plot all points, color the labels
plt.scatter(X[:, 0], X[:, 1], c=labels, s=50)
# mark centroids
plt.scatter(centroids[:, 0], centroids[:, 1], marker='*', s=200, c='black')
plt.show()

# gmm demo
gmm = GaussianMixture(n_components=2)
labels = gmm.fit(X).predict(X)
print("\nLabels EM:", labels)
print('Graph using EM Algorithm')
plt.scatter(X[:, 0], X[:, 1], c=labels, s=40)
plt.show()


import numpy as np
from dietbox.abtest.stats_util import cal_p_value
from dietbox.abtest.stats import ABTestRatios, ABTestRatiosNaive, ABTestSeries

def test_ab_tests():

    one_ab_test = {
        "A_converted": 43,
        "A_total": 17207,
        "B_converted": 68,
        "B_total": 17198,
    }

    print(cal_p_value(one_ab_test))

    dd = ABTestRatios(one_ab_test)
    ab_naive = ABTestRatiosNaive(one_ab_test)

    print("Ratios:\n", dd.report())

    print("Naive Method:\n", ab_naive.report())

    print(dd.data)
    print(cal_p_value(dd.data))

    another_ab_test = {
        "A_series": np.random.choice([1, 0], size=(1000,), p=[0.07, 1 - 0.07]),
        "B_series": np.random.choice([1, 0], size=(1100,), p=[0.1, 1 - 0.1]),
    }

    mw = ABTestSeries(another_ab_test)

    print(mw.report(with_data=False))


# Function for the Method of least squares
# by Justinas Smertinas
# Edited by Ali Rassolie
# Edited again by Justinas Smertinas

#What to import from numpy.
from numpy import array, transpose, dot, linalg, power

#We want to find the best fitting polynomial of any order (that you choose)
#between a set of point in the XY plane.
#For this we employ a Method of Least Squares.
#The following function returns a list of coefficients for a polynomial
#of order chosen by the user.
#Exanple of the return [a, b, c, d] where a is coefficient of the largest
#degree term (in this case 3).

#This will give you whatever order polynomial you want, babe.
class Algo:
    def __init__(self):
        pass

    def leastSquares(self, x=None, y=None, order=1):
        #We note the length of the input list so that we do not have to compute it again
        lengthOfList = len(x)
        #We set up 3 list that later will be turned into numpy
        # A transposed

        At = []

        #We create A transposed list by appending list of X raised
        #to powers of descending order.
        for i in reversed(range(order+1)):
            At.append(power(x, i))
        #We turn At into a numpy array
        #And create its transversed version.
        At = array(At)
        A = At.transpose()

        #We solve a systen <At*A>=<At*Y>
        solutionArray = linalg.solve(dot(At, A), dot(At, y))
        #However we obtain solution in form of an Array and since I dislike it's format
        #I turn it into a simple list with the folowing for loop.
        solution = []
        for i in range(len(solutionArray)):
            solution.append(solutionArray[i])
        return solution

    #Returns a list of Y approximations from known X and Y for wanted x.
    #We need a list of known X and Y values to find polynomial approximations
    #and we also need a list of X values, for which we want approximated values.
    def leastSquaresApproximationResults(self, xKnown = None, xWanted = None, yKnown=None, order = 1):
        #We calculate the polynomial coefficients
        coefficients = self.leastSquares(xKnown, yKnown, order)
        #We reverse the list for later convenience when calculating approximated Y values
        coefficients.reverse()
        #We create a list that we will alter return
        yApproxList = []
        #We iterate through the list of wanted X Values
        for i in xWanted:
            #We calculate approximation of Y values for wanted X and append it to the list
            yApprox = 0
            for j in range(len(coefficients)):
                yApprox += coefficients[j]*(i**j)
            yApproxList.append(yApprox)
        #We return the list of approximated Y values
        return yApproxList


#!/usr/bin/env python3

import numpy as np
viterbi = __import__('4-viterbi').viterbi

if __name__ == '__main__':
    np.random.seed(1)
    Emission = np.array([[0.90, 0.10, 0.00, 0.00, 0.00, 0.00],
                         [0.40, 0.50, 0.10, 0.00, 0.00, 0.00],
                         [0.00, 0.25, 0.50, 0.25, 0.00, 0.00],
                         [0.00, 0.00, 0.05, 0.70, 0.15, 0.10],
                         [0.00, 0.00, 0.00, 0.20, 0.50, 0.30]])
    Transition = np.array([[0.60, 0.39, 0.01, 0.00, 0.00],
                           [0.20, 0.50, 0.30, 0.00, 0.00],
                           [0.01, 0.24, 0.50, 0.24, 0.01],
                           [0.00, 0.00, 0.15, 0.70, 0.15],
                           [0.00, 0.00, 0.01, 0.39, 0.60]])
    Initial = np.array([0.05, 0.20, 0.50, 0.20, 0.05])
    Hidden = [np.random.choice(5, p=Initial)]
    for _ in range(364):
        Hidden.append(np.random.choice(5, p=Transition[Hidden[-1]]))
    Hidden = np.array(Hidden)
    Observations = []
    for s in Hidden:
        Observations.append(np.random.choice(6, p=Emission[s]))
    Observations = np.array(Observations)
    path, P = viterbi(Observations, Emission, Transition, Initial.reshape((-1, 1)))
    print(P)
    print(path)


#!/usr/bin/env python3
#
# Script to compare polynomial interpolants using various choices of node types.
#
# Daniel R. Reynolds
# SMU Mathematics
# Math 4315

# imports
import numpy
import matplotlib.pyplot as plt
from Lagrange import Lagrange

# set numbers of nodes for tests
nvals = [5, 10, 20, 40]

# set function and interval that we will interpolate
def f(x):
    return 1/(1+x**2)
a = -5
b = 5

# set evaluation points to compare interpolants
x = numpy.linspace(a, b, 2001)

# initialize plots
plt.figure(1)    # uniform nodes
plt.plot(x, f(x), label='$f(x)$')
plt.figure(3)    # random nodes
plt.plot(x, f(x), label='$f(x)$')
plt.figure(5)    # Chebyshev nodes of first kind
plt.plot(x, f(x), label='$f(x)$')
plt.figure(7)    # Chebyshev nodes of second kind
plt.plot(x, f(x), label='$f(x)$')

# loop over node numbers
for n in nvals:

   print("Testing with n =", n)

   # uniformly-spaced nodes
   plt.figure(1)
   t = numpy.linspace(a,b,n+1)
   p = Lagrange(t, f(t), x)
   e = numpy.abs(f(x)-p)
   plt.plot(x, p, label=('$p_{%i}(x)$, error = %.2e' % (n,numpy.linalg.norm(e,numpy.inf))))
   plt.figure(2)
   plt.semilogy(x, e, label=('$|f-p_{%i}|$' % (n)))

   # random nodes
   plt.figure(3)
   t = a + (b-a)*numpy.random.rand(n+1)
   p = Lagrange(t, f(t), x)
   e = numpy.abs(f(x)-p)
   plt.plot(x, p, label=('$p_{%i}(x)$, error = %.2e' % (n,numpy.linalg.norm(e,numpy.inf))))
   plt.figure(4)
   plt.semilogy(x, e, label=('$|f-p_{%i}|$' % (n)))

   # Chebyshev nodes of the first kind
   plt.figure(5)
   t = (a+b)/2 + (b-a)/2*numpy.cos((2*numpy.linspace(0,n,n+1)+1)/(2*n+2)*numpy.pi)
   p = Lagrange(t, f(t), x)
   e = numpy.abs(f(x)-p)
   plt.plot(x, p, label=('$p_{%i}(x)$, error = %.2e' % (n,numpy.linalg.norm(e,numpy.inf))))
   plt.figure(6)
   plt.semilogy(x, e, label=('$|f-p_{%i}|$' % (n)))

   # Chebyshev nodes of the second kind
   plt.figure(7)
   t = (a+b)/2 - (b-a)/2*numpy.cos(numpy.linspace(0,n,n+1)*numpy.pi/n)
   p = Lagrange(t, f(t), x)
   e = numpy.abs(f(x)-p)
   plt.plot(x, p, label=('$p_{%i}(x)$, error = %.2e' % (n,numpy.linalg.norm(e,numpy.inf))))
   plt.figure(8)
   plt.semilogy(x, e, label=('$|f-p_{%i}|$' % (n)))

# finalize plots
plt.figure(1)
plt.xlabel('$x$')
plt.ylabel('$f(x)$, $p(x)$')
plt.legend()
plt.title('Uniformly-spaced nodes')

plt.figure(2)
plt.xlabel('$x$')
plt.ylabel('$|f(x) - p(x)|$')
plt.legend()
plt.title('Uniformly-spaced node error')

plt.figure(3)
plt.xlabel('$x$')
plt.ylabel('$f(x)$, $p(x)$')
plt.legend()
plt.title('Random nodes')

plt.figure(4)
plt.xlabel('$x$')
plt.ylabel('$|f(x) - p(x)|$')
plt.legend()
plt.title('Random node error')

plt.figure(5)
plt.xlabel('$x$')
plt.ylabel('$f(x)$, $p(x)$')
plt.legend()
plt.title('Chebyshev nodes of first kind')

plt.figure(6)
plt.xlabel('$x$')
plt.ylabel('$|f(x) - p(x)|$')
plt.legend()
plt.title('Chebyshev node of first kind error')

plt.figure(7)
plt.xlabel('$x$')
plt.ylabel('$f(x)$, $p(x)$')
plt.legend()
plt.title('Chebyshev nodes of second kind')

plt.figure(8)
plt.xlabel('$x$')
plt.ylabel('$|f(x) - p(x)|$')
plt.legend()
plt.title('Chebyshev node of second kind error')

plt.show()

# end of script


#==================================================================================================
#                     Copyright (C) 2016 Olivier Mallet - All Rights Reserved
#==================================================================================================

import numpy as np
import pandas as pd

import CyURT as urt

if __name__ == "__main__":

    y = pd.read_csv('../data/y.csv', sep=',', header=None)
    x = pd.read_csv('../data/x.csv', sep=',', header=None)

    yd = np.asarray(y).reshape(y.size)
    yf = yd.astype(np.float32)
    xd = np.asarray(x, order='F')
    xf = xd.astype(np.float32)

    # running OLS regression as in ./examples/example1.cpp using double precision type
    fit = urt.OLS_d(yd, xd, True)
    fit.show()

    # running OLS regression as in ./examples/example1.cpp using single precision type
    fit = urt.OLS_f(yf, xf, True)
    fit.show()

    # running first ADF test as in ./examples/example2.cpp using double precision type
    test = urt.ADF_d(yd, lags=10, trend='ct')
    test.show()

    # running second ADF test as in ./examples/example2.cpp using double precision type
    test.method = 'AIC'
    test.bootstrap = True
    test.niter = 10000
    test.show()


import math

DISTANCE_TO_PIVOT = 1
DISTANCE_TO_SL = 6

# Constants for gate
GATE_RADIUS, MIN_ELEV = (1804.238, 7.987)

def plot_sample(ax, sample,
                DISTANCE_TO_SL=DISTANCE_TO_SL,
                DISTANCE_TO_PIVOT=DISTANCE_TO_PIVOT,
               GATE_RADIUS=GATE_RADIUS,
               MIN_ELEV=MIN_ELEV):
    """# Plotting Water Surface profile"""

    # Horizontal distance from gate hinge to gate tip
    L = math.cos(math.radians(sample.G1_ANGLE))*(GATE_RADIUS/1000)

    # Shift datum for levels
    datum = MIN_ELEV

    g1_el = sample.G1_ELEVATION - MIN_ELEV
    usl = sample.USL_VAL-MIN_ELEV
    d = sample.G1_ELEVATION - MIN_ELEV + sample.Cc*sample.hu
    dsl = sample.DSL_VAL-MIN_ELEV
    sl = sample.SL_DEPTH_COR-MIN_ELEV

    # Plot Gate Angle
    gate_x = [DISTANCE_TO_PIVOT, DISTANCE_TO_PIVOT+L]
    gate_y = [0, g1_el]

    ax.plot(gate_x, gate_y, color='k', label='Gate')

    ws_x = [0.5, L+DISTANCE_TO_PIVOT, (GATE_RADIUS/1000)+DISTANCE_TO_PIVOT+0.5, DISTANCE_TO_SL]
    ws_y = [usl, d, dsl, sl]

    ax.plot(ws_x, ws_y, marker='o', label='WS', color='blue')

    #ax.legend()

    # add dashed lines for each level/ elevation
    names = ['USL Level', 'Depth at Gate Tip', 'Gate DSL Level', 'Pool DSL Level']
    buffer = 0.02
    for i in range(0, len(names)):

        #ax.plot([0, ws_x[i]], [ws_y[i], ws_y[i]], ls='--', color='grey')

        ax.text(ws_x[i]+buffer, ws_y[i]+buffer, names[i])

    ax.plot(DISTANCE_TO_SL+1, 0)

    ax.set(xlabel='Distance [m]', ylabel='Height Above Gate Hinge [m]')
    return ax
# plto curved line through points
#from scipy.interpolate import make_interp_spline, BSpline

# 300 represents number of points to make between T.min and T.max
#xnew = np.linspace(min(ws_x), max(ws_x), 300)

#spl = make_interp_spline(ws_x, ws_y, k=2)  # type: BSpline
#ws_smooth = spl(xnew)

#plt.plot(xnew, ws_smooth)

# plt.plot(0, sample.USL_VAL - min_elev, label='USL', marker='o')
# plt.plot(L,
#          sample.G1_ELEVATION - min_elev + sample.Cc*sample.hu, label='h' , marker='o')

# plt.plot((gate_radius/1000), sample.DSL_VAL - min_elev, label='DSL', marker='o')

# plt.plot(5, sample.SL_DEPTH_COR - min_elev, label='SL', marker='o')

# plt.legend()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 27 13:20:01 2020

@author: Oscar
"""
import numpy as np

def straighLine(x, m, n):
    y = m * x + n
    return y

def polFunc(x, a, b, c, d):
    y = (a * x**3) + (b * x**2) + c * x + d
    return y

def sinFunc(x, m, n, a, w, ph):
    y = m * x + a * (np.sin(w * x + ph)) + n
    return y

def genData(typeData,X,slope,axisY,a,b,c,d,amplitude,angular,phase):
    func = {
        'straighLine': straighLine(X, slope, axisY),
        'polinomial': polFunc(X, a, b, c, d),
        'senoidal': sinFunc(X, slope, axisY, amplitude, angular, phase)
    }
    return func[typeData]


# -*- coding: utf-8 -*-
"""golden section search

This module provides a Numba JIT compilled golden section search optimizer for a custom objective.
"""

import math
import numpy as np
from numba import njit

@njit
def optimizer(obj,a,b,args=(),tol=1e-6):
    """ golden section search optimizer

    Args:

        obj (callable): 1d function to optimize over
        a (double): minimum of starting bracket
        b (double): maximum of starting bracket
        args (tuple): additional arguments to the objective function
        tol (double,optional): tolerance

    Returns:

        (float): optimization result

    """

    inv_phi = (np.sqrt(5) - 1) / 2 # 1/phi
    inv_phi_sq = (3 - np.sqrt(5)) / 2 # 1/phi^2

    # a. distance
    dist = b - a
    if dist <= tol:
        return (a+b)/2

    # b. number of iterations
    n = int(np.ceil(np.log(tol/dist)/np.log(inv_phi)))

    # c. potential new mid-points
    c = a + inv_phi_sq * dist
    d = a + inv_phi * dist
    yc = obj(c,*args)
    yd = obj(d,*args)

    # d. loop
    for _ in range(n-1):
        if yc < yd:
            b = d
            d = c
            yd = yc
            dist = inv_phi*dist
            c = a + inv_phi_sq * dist
            yc = obj(c,*args)
        else:
            a = c
            c = d
            yc = yd
            dist = inv_phi*dist
            d = a + inv_phi * dist
            yd = obj(d,*args)

    # e. return
    if yc < yd:
        return (a+d)/2
    else:
        return (c+b)/2


from pylab import *
from scipy import stats

K = 10
N = 10000
# Each arm is a Beta distribution
mu = linspace(0.1, 0.9, K)
alpha = ones(N)
beta = (1 - mu) / mu

def pull_arm(i):
	return stats.beta(alpha[i], beta[i]).rvs()

def epsilon_greedy(epsilon):
	mu_h = zeros(K) 	# Empirical mean
	n = zeros(K)		# Number of times an arm is pulled
	G = zeros(N)		# Gain at round i
	II = zeros(N)		# Choice of arm at round i
	for i in xrange(N):
		I = argmax(mu_h)
		# Explore a new arm with probability epsilon
		if rand() < epsilon:
			a = arange(K)
			a[I], a[-1] = a[-1], a[I]
			I = a[:-1][randint(0, K-1)]
		g = pull_arm(I)
		G[i] = g
		II[i] = I
		# Update empirical mean
		mu_h[I] = (mu_h[I] * n[I] + g) / (n[I] + 1)
		n[I] += 1
	return G, II

def UCB1():
	mu_h = zeros(K) 	# Empirical mean
	ucb = ones(K)		# Upper Confidence Bounds
	n = zeros(K)		# Number of times an arm is pulled
	G = zeros(N)		# Gain at round i
	II = zeros(N)		# Choice of arm at round i
	for i in xrange(N):
		# Pull the arm with the highest bound
		I = argmax(ucb)
		g = pull_arm(I)
		G[i] = g
		II[i] = I
		# Update the bound
		n[I] += 1
		mu_h[I] = (mu_h[I] * n[I] + g) / (n[I] + 1)
		ucb[I] = mu_h[I] + sqrt((2*log(i+1)) / n[I])
	return G, II

def thompson_sampling():
	mu_h = zeros(K) 	# Empirical mean
	ucb = ones(K)		# Upper Confidence Bounds
	n = zeros(K)		# Number of times an arm is pulled
	S = zeros(K)		# Number of success
	F = zeros(K)		# Number of failure
	G = zeros(N)		# Gain at round i
	II = zeros(N)		# Choice of arm at round i
	for i in xrange(N):
		theta = zeros(K)
		for j in xrange(K):
			theta[j] = stats.beta(S[j]+1, F[j]+1).rvs()
		I = argmax(theta)
		g = pull_arm(I)
		G[i] = g
		II[i] = I
		# Update the posterior using a Benourlli trial
		if rand() < g:
			S[I] += 1
		else:
			F[I] += 1
	return G, II

def exp3(gamma):
	w = ones(K) 		# Arm weights
	n = zeros(K)		# Number of times an arm is pulled
	G = zeros(N)		# Gain at round i
	II = zeros(N)		# Choice of arm at round i
	for i in xrange(N):
		# Pull the arm with highest P
		p = (1 - gamma) * w / sum(w) + gamma / K
		I = choice(K, p=p)
		g = pull_arm(I)
		G[i] = g
		II[i] = I
		# Update the weight
		w[I] = w[I] * exp(gamma * g / p[I] / K)
	return G, II

def random_bandit():
	n = zeros(K)		# Number of times an arm is pulled
	G = zeros(N)		# Gain at round i
	II = zeros(N)		# Choice of arm at round i
	for i in xrange(N):
		# Pull the arm with highest P
		I = randint(0, K)
		g = pull_arm(I)
		G[i] = g
		II[i] = I
	return G, II

def main():
	figure()
	title('Average Gain Overtime')
	# e-greedy
	for e in [0.05, 0.1]:
		G, II = epsilon_greedy(e)
		# Could also add plot of % of optimal actions
		plot(cumsum(G) / arange(1, N+1), label='EG w/ e = %.3f'%(e))
	# UCB
	G, II = UCB1()
	plot(cumsum(G) / arange(1, N+1), label='UCB1')
	# Thompson sampling
	G, II = thompson_sampling()
	plot(cumsum(G) / arange(1, N+1), label='TS')
	# Exp3
	for gamma in [0.2]:
		G, II = exp3(gamma)
		plot(cumsum(G) / arange(1, N+1), label='EXP3 w/ r = %.3f'%(gamma))
	# Random bandit
	G, II = random_bandit()
	plot(cumsum(G) / arange(1, N+1), '--', label='Random')
	legend()
	xlabel('Round')
	ylabel('Gain')
	show()

if __name__ == '__main__':
	main()


# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets # load iris dataset

class LogisticRegression:

    def __init__(self, learning_rate=0.01, epochs=200):
        self.learning_rate = learning_rate
        self.epochs = epochs

    def sigmoid(self, z):
        """
        Compute value of sigmoid function "g", where:
        g(z) = 1 / (1 + e^-z)
        """
        g = 1 / (1 + np.exp(-z))
        return g

    def hypothesis(self, X, theta):
        """
       Compute hypothesis "h" where: h(x) = g(z) with:
            z = X.T * theta
            g, a sigmoid function

		Parameters
    	------------
    	X: numpy array
			Features vector.
    	theta: numpy array
			Parameters vector.

		Returns
    	------------
		numpy ndarray
		"""
        z = X.dot(theta)
        h = self.sigmoid(z)
        return h

    def compute_cost(self, h, y):
        """
		Compute value of cost function "J", where:
		J(theta) = -np.average(y * np.log(h) + (1 - y) * np.log(1 - h))

		Parameters
    	------------
    	h: numpy array
			Predictions vector.
		y: numpy array
			Target vector.

		Returns
    	------------
		Value of cost function J
		"""
        m = len(y)
        J = -(1/m) * np.sum(y * np.log(h) + (1 - y) * np.log(1 - h))
        return J

    def add_intercept(self, X):
        """
        Add intercept to feature vector.
        """
        return np.c_[np.ones((m, 1)), X]

    def fit(self, X, y):
        """
		Fit theta to the training set.

		Parameters
    	------------
    	epochs: int
			Number of iterations.
    	learning_rate: float
			Rate at which theta is updated

		Returns
    	------------
		numpy ndarray
		"""
        X_b = self.add_intercept(X)
        costs = []
        self.theta = np.random.rand(X_b.shape[1])
        for epoch in range(self.epochs):
            h = self.hypothesis(X_b, self.theta)
            error = h - y
            gradients = 1/m * X_b.T.dot((error))
            self.theta = self.theta - self.learning_rate * gradients
            cost = self.compute_cost(h, y)
            costs.append(cost)

        self.costs = costs
        self.epochs = epochs

        return self.theta

    def predict(self, X):
        """
        """
        X_b = self.add_intercept(X)
        self.predictions = self.hypothesis(X_b, self.theta)
        return self.predictions

    def plot_cost(self):
        """Plot iterations versus cost function.

        Returns
        -----------
        matploblib figure
        """
        plt.figure()
        plt.plot(np.arange(1, self.epochs+1), self.costs)
        plt.xlabel("Iterations")
        plt.ylabel("Cost")
        plt.title("Cost function minimization")
        plt.show()

    def plot_model(self, X, y):
        """Plot fitted model.

        Returns
        -----------
        matploblib figure
        """
        plt.figure()
        x_values = [np.min(x[:, 0]), np.max(x[:, 1] + 3)]
        y_values = -(self.theta[0] + np.dot(self.theta[1], x_values)) / self.theta[2]
        plt.scatter(x[:,0], x[:,1], c=y)
        plt.plot(x_values, y_values, label='Decision Boundary')
        plt.title("Fitted model")
        plt.show()

if __name__ == "__main__":
    iris = datasets.load_iris()
    x = iris.data[:, :2] # select only the first 2 features
    y = (iris.target != 0) * 1 # make dataset into binary classification problem
    model = LogisticRegression(learning_rate=0.1, epochs=1000)
    model.fit(x, y)

    print("theta_hat", model.theta)

    model.plot_cost()
    model.plot_model(x, y)