#autor: Rodolfo Quispe
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

#define the original function
def f(x, w):
  return w[0]*(x**3) + w[1]*(x**2) + w[2]*(x) + w[3]

def plot_results(x, y, w):
  fig = plt.figure(figsize=(12, 6))
  fig.add_subplot(111)

  # Plot the target t versus the input x
  plt.plot(x, y, 'o', label='data')
  # Plot the initial line
  x = list(range(-10, 10))
  plt.plot(x, [f(i,w) for i in x],'g-',label='f(x)')

  #set extra plot parameters
  plt.title('Question 6: Stochastic Gradient Descent on Tensorflow')
  plt.xlim(-6,6)
  plt.ylim(-60,60)
  plt.xlabel('x')
  plt.ylabel('f(x)', fontsize=15)
  plt.legend(loc=2)
  plt.grid()

  plt.show()
  fig.savefig("t01_6.png")

if __name__ == "__main__":

  #Model parameters
  a = tf.Variable([np.random.rand()], tf.float32)
  b = tf.Variable([np.random.rand()], tf.float32)
  c = tf.Variable([np.random.rand()], tf.float32)
  d = tf.Variable([np.random.rand()], tf.float32)

  # Our model of y = a*x^3 + b*x^2 + c*x + d
  x = tf.placeholder(tf.float32)
  cubic_model = a*x*x*x + b*x*x + c*x + d
  y = tf.placeholder(tf.float32)

  # Our error is defined as the sum of the squares
  loss = tf.reduce_sum(tf.square(cubic_model - y))

  # Defining AdamOptimizer to calulate gradient
  optimizer = tf.train.AdamOptimizer(learning_rate=0.01)
  train = optimizer.minimize(loss)

  #training data
  x_train = np.array([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])
  y_train = np.array([-53.9, -28.5, -20.7, -3.6, -9.8, 5.0,
                        4.2, 5.1, 11.4, 27.4, 44.0])

  # number of gradient descent iterations
  nb_of_iterations = 200

  # training loop
  init = tf.global_variables_initializer()
  sess = tf.Session()
  sess.run(init) # reset values to wrong

  errors = []

  #SGD optimization with random order
  order = list(range(len(x_train)))
  for i in range(nb_of_iterations):
    np.random.shuffle(order)
    for j in order:
      x_value = x_train[j]
      y_value = y_train[j]
      sess.run(train, {x:x_value, y:y_value})
    errors.append(sess.run(loss, {x:x_train, y:y_train}))

  #recover solution
  curr_a, curr_b, curr_c, curr_d, curr_loss  =  sess.run(
   [a, b, c, d, loss], {x:x_train, y:y_train})

  # show solution
  print ('iterations', nb_of_iterations)
  print ('cost', curr_loss)
  print ('best parameters', curr_a, curr_b, curr_c, curr_d)
  plot_results(x_train, y_train, np.array([curr_a, curr_b,
                                          curr_c, curr_d]))

  '''
  Extra implementaions

  #batch optimizacion
  for i in range(nb_of_iterations):
    sess.run(train, {x:x_train, y:y_train})
    errors.append(sess.run(loss, {x:x_train, y:y_train}))

  #SGD optimization
  for i in range(nb_of_iterations):
    for x_value, y_value in zip(x_train, y_train):
      sess.run(train, {x:x_value, y:y_value})
    errors.append(sess.run(loss, {x:x_train, y:y_train}))
  '''


from time import time
from typing import List

import numpy

from assignment_2_3_4.line_searches import AbstractLineSearch
from assignment_2_3_4.optimizers import AbstractOptimizer, OptimizationStep
from assignment_2_3_4.oracles import AbstractOracle

class LassoOptimizer(AbstractOptimizer):
    name: str = "lasso"

    def _optimize_oracle(
        self, oracle: AbstractOracle, start_point: numpy.ndarray, line_search: AbstractLineSearch = None
    ) -> List[OptimizationStep]:
        points = [self._aggregate_optimization_step(oracle, start_point, 0)]

        lipschitz: float = 1.0
        start_time = time()
        for _ in range(self._config.max_iter):
            for _ in range(self._config.max_iter):
                alpha = 1 / lipschitz
                prox = self._lasso_proximal(points[-1].point - alpha * points[-1].grad, alpha)
                delta = prox - points[-1].point
                delta_norm = delta @ delta
                if oracle.value(prox) <= points[-1].value + points[-1].grad @ delta + lipschitz / 2 * delta_norm:
                    break
                lipschitz *= 2

            points.append(self._aggregate_optimization_step(oracle, prox, time() - start_time))
            points[-1].stop_criterion = delta_norm / (alpha ** 2)
            if points[-1].stop_criterion <= self._config.tol:
                break
            lipschitz /= 2

        return points

    def _lasso_proximal(self, point: numpy.ndarray, alpha: float) -> numpy.ndarray:
        return numpy.sign(point) * numpy.maximum(numpy.abs(point) - alpha * self._config.lasso_lambda, 0)

    def _get_direction(self, oracle: AbstractOracle, last_point: OptimizationStep) -> numpy.ndarray:
        pass


from matplotlib import colors as mplColors
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
import numpy as np

class ShowCell:
    figure  = None # matplotlib figure
    subplot = None # subplot

    def make_spherical(self):
        self.phi   = np.linspace(0, 2*np.pi, self.resolution) # spherical coordinates
        self.theta = np.linspace(0,   np.pi, self.resolution)

    def make_points(self,position,radius):
        return np.array([
                  position[0]+radius*np.outer(np.cos(self.phi), np.sin(self.theta)),
                  position[1]+radius*np.outer(np.sin(self.phi), np.sin(self.theta)),
                  position[2]+radius*np.outer(np.ones(np.size(self.phi)), np.cos(self.theta))])

    def __init__(self,resolution = 13, alpha = 0.4):
        if resolution > 3:
            self.resolution = resolution
        else:
            self.resolution = 3

        self.make_spherical()
        self.alpha      = alpha
        # generate plot
        self.figure          = plt.figure()
        self.subplot         = self.figure.add_subplot(111, projection='3d')
        self.subplot.set_anchor((0.75,0.75))

    def add_sphere(self, position = np.zeros(3),
                         radius  = 1.0,
                         **kwargs):
        if 'color' not in kwargs:
            kwargs['color'] = mplColors.rgb2hex(0.8*np.random.rand(3))
        if 'alpha' not in kwargs:
            kwargs['alpha'] = 0.2

        self.subplot.plot_surface(*(self.make_points(position,radius)), **kwargs)

    def add_polygon(self,points,**kwargs):
        if 'color' not in kwargs:
            kwargs['color'] = mplColors.rgb2hex(0.8*np.random.rand(3))

        polygon = Poly3DCollection([points], **kwargs)
        polygon.set_color(kwargs['color'])
        self.subplot.add_collection3d(polygon)

    def set_aspect(self,aspect_data):
        scale_from_ascpect = np.repeat([[np.min(aspect_data), np.max(aspect_data)]],3,axis=0)
        self.subplot.auto_scale_xyz(*scale_from_ascpect)

    @staticmethod
    def show(name=None):
        if name is None:
            plt.show()
        else:
            plt.savefig(name)


# -*- coding: utf-8 -*-
'''
Copyright (c) 2015 by Tobias Houska

This file is part of Statistical Parameter Estimation Tool (SPOTPY).

:author: Tobias Houska

This tool holds functions for statistic analysis. It takes Python-lists and
returns the objective function value of interest.
'''

import numpy as np

def bias(evaluation,simulation):
    """
    Bias

        .. math::

         Bias=\\frac{1}{N}\\sum_{i=1}^{N}(e_{i}-s_{i})

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Bias
    :rtype: float
    """
    if len(evaluation)==len(simulation):
        bias_values=[]
        for i in range(len(evaluation)):
            if evaluation[i] == -99999:
                '''
                Cleans out No Data values
                '''
                print('Wrong Results! Clean out No Data Values')
                pass

            else:
                bias_values.append(float(simulation[i]) - float(evaluation[i]))
        bias_sum = np.sum(bias_values[0:len(bias_values)])
        bias = bias_sum/len(bias_values)
        return float(bias)

    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def nashsutcliff(evaluation,simulation):
    """
    Nash-Sutcliff model efficinecy

        .. math::

         NSE = 1-\\frac{\\sum_{i=1}^{N}(e_{i}-s_{i})^2}{\\sum_{i=1}^{N}(e_{i}-\\bar{e})^2}

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Nash-Sutcliff model efficiency
    :rtype: float

    """
    if len(evaluation)==len(simulation):
        s,e=np.array(simulation),np.array(evaluation)
        #s,e=simulation,evaluation
        mean_observed = np.mean(e)
        # compute numerator and denominator
        numerator = sum((e - s) ** 2)
        denominator = sum((e - mean_observed)**2)
        # compute coefficient
        return 1 - (numerator/denominator)

    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def lognashsutcliff(evaluation,simulation):
    """
    log Nash-Sutcliff model efficiency

        .. math::

         NSE = 1-\\frac{\\sum_{i=1}^{N}(log(e_{i})-log(s_{i}))^2}{\\sum_{i=1}^{N}(log(e_{i})-log(\\bar{e})^2}-1)*-1

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: log Nash-Sutcliff model efficiency
    :rtype: float

    """
    if len(evaluation)==len(simulation):
        return float(1 - sum((np.log(simulation)-np.log(evaluation))**2)/sum((np.log(evaluation)-np.mean(np.log(evaluation)))**2))
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def log_p(evaluation,simulation,scale=0.1):
    """
    Logarithmic probability distribution

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Logarithmic probability distribution
    :rtype: float
    """
    #from scipy import stats
    #logLik = np.mean( stats.norm.logpdf(evaluation, loc=simulation, scale=.1) )
    if len(evaluation)==len(simulation):
        y        = (np.array(evaluation)-np.array(simulation))/scale
        normpdf = -y**2 / 2 - np.log(np.sqrt(2*np.pi))
        return np.mean(normpdf)
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def correlationcoefficient(evaluation,simulation):
    """
    Correlation Coefficient

        .. math::

         r = \\frac{\\sum ^n _{i=1}(e_i - \\bar{e})(s_i - \\bar{s})}{\\sqrt{\\sum ^n _{i=1}(e_i - \\bar{e})^2} \\sqrt{\\sum ^n _{i=1}(s_i - \\bar{s})^2}}

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Corelation Coefficient
    :rtype: float
    """
    if len(evaluation)==len(simulation):
        Corelation_Coefficient = np.corrcoef(evaluation,simulation)[0,1]
        return Corelation_Coefficient
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def rsquared(evaluation,simulation):
    """
    Coefficient of Determination

        .. math::

         r^2=(\\frac{\\sum ^n _{i=1}(e_i - \\bar{e})(s_i - \\bar{s})}{\\sqrt{\\sum ^n _{i=1}(e_i - \\bar{e})^2} \\sqrt{\\sum ^n _{i=1}(s_i - \\bar{s})^2}})^2

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Coefficient of Determination
    :rtype: float
    """
    if len(evaluation)==len(simulation):
        return correlationcoefficient(evaluation,simulation)**2
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def mse(evaluation,simulation):
    """
    Mean Squared Error

        .. math::

         MSE=\\frac{1}{N}\\sum_{i=1}^{N}(e_{i}-s_{i})^2

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Mean Squared Error
    :rtype: float
    """

    if len(evaluation)==len(simulation):

        MSE_values=[]

        for i in range(len(evaluation)):
            MSE_values.append((simulation[i] - evaluation[i])**2)

        MSE_sum = np.sum(MSE_values[0:len(evaluation)])

        MSE=MSE_sum/(len(evaluation))
        return MSE
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def rmse(evaluation,simulation):
    """
    Root Mean Squared Error

        .. math::

         RMSE=\\sqrt{\\frac{1}{N}\\sum_{i=1}^{N}(e_{i}-s_{i})^2}

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Root Mean Squared Error
    :rtype: float
    """
    if len(evaluation)==len(simulation):
        return np.sqrt(mse(evaluation,simulation))
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def mae(evaluation,simulation):
    """
    Mean Absolute Error

        .. math::

         MAE=\\frac{1}{N}\\sum_{i=1}^{N}(\\left |  e_{i}-s_{i} \\right |)

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Mean Absolute Error
    :rtype: float
    """
    if len(evaluation)==len(simulation):

        MAE_values=[]

        for i in range(len(evaluation)):
            MAE_values.append(np.abs(simulation[i] - evaluation[i]))

        MAE_sum = np.sum(MAE_values[0:len(evaluation)])

        MAE = MAE_sum/(len(evaluation))

        return MAE
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def rrmse(evaluation,simulation):
    """
    Relative Root Mean Squared Error

        .. math::

         RRMSE=\\frac{\\sqrt{\\frac{1}{N}\\sum_{i=1}^{N}(e_{i}-s_{i})^2}}{\\bar{e}}

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Relative Root Mean Squared Error
    :rtype: float
    """

    if len(evaluation)==len(simulation):

        RRMSE = rmse(evaluation,simulation)/np.mean(simulation)
        return RRMSE

    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def agreementindex(evaluation,simulation):
    """
    Agreement Index (d) developed by Willmott (1981)

        .. math::

         d = 1 - \\frac{\\sum_{i=1}^{N}(e_{i} - s_{i})^2}{\\sum_{i=1}^{N}(\\left | s_{i} - \\bar{e} \\right | + \\left | e_{i} - \\bar{e} \\right |)^2}

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Agreement Index
    :rtype: float
    """
    if len(evaluation)==len(simulation):
        simulation,evaluation=np.array(simulation),np.array(evaluation)
        Agreement_index=1 -(np.sum((evaluation-simulation)**2))/(np.sum(
                (np.abs(simulation-np.mean(evaluation))+np.abs(evaluation-np.mean(evaluation)))**2))
        return Agreement_index
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def covariance(evaluation,simulation):
    """
    Covariance

        .. math::
         Covariance = \\frac{1}{N} \\sum_{i=1}^{N}((e_{i} - \\bar{e}) * (s_{i} - \\bar{s}))

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Covariance
    :rtype: float
    """
    if len(evaluation)==len(simulation):
        Covariance_values = []

        for i in range(len(evaluation)):
            Covariance_values.append((evaluation[i]-np.mean(evaluation))*(simulation[i]-np.mean(simulation)))

        Covariance = np.sum(Covariance_values)/(len(evaluation))
        return Covariance
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def decomposed_mse(evaluation,simulation):
    """
    Decomposed MSE developed by Kobayashi and Salam (2000)

        .. math ::
         dMSE = (\\frac{1}{N}\\sum_{i=1}^{N}(e_{i}-s_{i}))^2 + SDSD + LCS

         SDSD = (\\sigma(e) - \\sigma(s))^2

         LCS = 2 \\sigma(e) \\sigma(s) * (1 - \\frac{\\sum ^n _{i=1}(e_i - \\bar{e})(s_i - \\bar{s})}{\\sqrt{\\sum ^n _{i=1}(e_i - \\bar{e})^2} \\sqrt{\\sum ^n _{i=1}(s_i - \\bar{s})^2}})

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Decomposed MSE
    :rtype: float
    """

    if len(evaluation)==len(simulation):

        Decomposed_MSE = str(round((bias(evaluation,simulation))**2,2))+'(bias**2) + '+str(round((_standarddeviation(evaluation)-_standarddeviation(simulation))**2,2))+'(SDSD) + '+str(round(2*_standarddeviation(evaluation)*_standarddeviation(simulation)*(1-correlationcoefficient(evaluation,simulation)),2))+'(LCS)'

        return Decomposed_MSE
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def kge(evaluation,simulation, return_all=False):
    """
    Kling-Gupta Efficiency

    Corresponding paper:
    Gupta, Kling, Yilmaz, Martinez, 2009, Decomposition of the mean squared error and NSE performance criteria: Implications for improving hydrological modelling

    output:
        kge: Kling-Gupta Efficiency
    optional_output:
        cc: correlation
        alpha: ratio of the standard deviation
        beta: ratio of the mean
    """
    if len(evaluation)==len(simulation):
        cc    = np.corrcoef(evaluation,simulation)[0,1]
        alpha = np.std(simulation)/np.std(evaluation)
        beta  = np.sum(simulation)/np.sum(evaluation)
        kge   = 1- np.sqrt( (cc-1)**2 + (alpha-1)**2 + (beta-1)**2 )
        if return_all:
            return kge, cc, alpha, beta
        else:
            return kge
    else:
        print("Error: evaluation and simulation lists does not have the same length.")
        return np.nan

def _variance(evaluation):
    """
    Variance

        .. math::

         Variance = \\frac{1}{N}\\sum_{i=1}^{N}(e_{i}-\\bar{e})^2

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :return: Variance
    :rtype: float
    """
    Variance_values = []
    for i in range(len(evaluation)):
        Variance_values.append((evaluation[i]-np.mean(evaluation))**2)
    Variance = np.sum(Variance_values)/len(evaluation)
    return Variance

def _standarddeviation(evaluation):
    """
    Standard Derivation (sigma)

        .. math::
         sigma = \\sqrt{\\frac{1}{N}\\sum_{i=1}^{N}(e_{i}-\\bar{e})^2}

    :evaluation: Observed data to compared with simulation data.
    :type: list

    :simulation: simulation data to compared with evaluation data
    :type: list

    :return: Standard Derivation
    :rtype: float
    """

    return np.sqrt(_variance(evaluation))


import math
import numpy as np
from scipy.integrate import quad
import sympy as sp
from sympy import simplify, solve

def test():
    print('test')

def bisection(f, a, b, tol):
    i, x0, error = 0, 0, tol*10
    P, ERROR, I = [], [], []
    while error >= tol:
        x = (b - a)/2
        p = a + x
        P.append(p)
        if f(a)*f(p) > 0: a = p
        else: b = p
        error = x - x0
        ERROR.append(error); I.append(i)
        x0 = x
        i += 1
    return P, ERROR, I

def clamped_cubic_spline(X, f, a, b):
    def algorithm(f, m):
        n = m - 1
        Y, YP = np.zeros(m), np.zeros(m)
        YP_sym = sp.diff(f, sym_x)
        i = 0
        while i <= n:
            xi = X[i]
            Y[i] = f.evalf(subs={sym_x: xi})
            YP[i] = YP_sym.evalf(subs={sym_x: xi})
            i += 1
        # STEP 1:   build list, h_i
        H, i = np.zeros(n), 0
        while i < n:
            H[i] = X[i+1] - X[i]
            i += 1
        # STEP 2:   define alpha list endpoints
        A, AP, ALPHA = Y, YP, np.zeros(m)
        ALPHA[0] = 3*(A[1] - A[0])/H[0] - 3*AP[0]
        ALPHA[n] = 3*AP[n] - 3*(A[n] - A[n-1])/H[n-1]
        # STEP 3:   build list, alpha_i
        i = 1
        while i <= n-1:
            ALPHA[i] = 3/H[i]*(A[i+1] - A[i]) - 3/H[i-1]*(A[i] - A[i-1])
            i += 1
        # Algorithm 6.7 to solve tridiagonal
        # STEP 4:   define l, mu, and z first points
        L, MU, Z, C = np.zeros(m), np.zeros(m), np.zeros(m), np.zeros(m)
        L[0], MU[0] = 2*H[0], 0.5
        Z[0] = ALPHA[0]/L[0]
        # STEP 5:   build lists l, mu, and z
        i = 1
        while i <= n-1:
            L[i] = 2*(X[i+1] - X[i-1]) - H[i-1]*MU[i-1]
            MU[i] = H[i]/L[i]
            Z[i] = (ALPHA[i] - H[i-1]*Z[i-1])/L[i]
            i += 1
        # STEP 6:   define l, z, and c endpoints
        L[n] = H[n-1]*(2-MU[i-1])
        Z[n] = (ALPHA[n] - H[n-1]*Z[n-1])/L[n]
        C[n] = Z[n]
        # STEP 7:   build lists b, c, and d
        B, D, i, j = np.zeros(n), np.zeros(n), 1, 0
        while i <= n:
            j = n-i
            C[j] = Z[j] - MU[j]*C[j+1]
            B[j] = (A[j+1] - A[j])/H[j] - H[j]*(C[j+1] + 2*C[j])/3
            D[j] = (C[j+1] - C[j])/(3*H[j])
            i += 1
        return Y, A, B, C, D
    sym_x, m = sp.Symbol('x'), len(X)
    Y, A, B, C, D = algorithm(f(sym_x), m)
    splines_j, j = [], 0
    while j < n-1:
        xj, aj, bj, cj, dj = X[j], A[j], B[j], C[j], D[j]
        sj = aj + bj*(sym_x - xj) + cj*(sym_x - xj)**2 + dj*(sym_x - xj)**3
        splines_j.append(sj)
        j += 1
    spline = sum(Sj)
    return Y, splines_j, spline

def composite_simpson(f, a, b, h):
    n = math.ceil((b-a)/h)
    XJ1, XJ2, XJ, = [], [], []
    YJ1, YJ2, YJ, = [], [], []
    XJ.append(a); YJ.append(f(a))
    j, y1, z1 = 1, 0, 0
    while j <= (n/2)-1:
        xj = a + 2*j*h
        yj = f(xj)
        XJ1.append(xj); YJ1.append(yj)
        z1 += yj
        j += 1
    k, y2, z2 = 1, 0, 0
    while k <= n/2:
        xj = a + (2*k - 1)*h
        yj = f(xj)
        XJ2.append(xj); YJ2.append(yj)
        z2 += yj
        k += 1
    l = 0
    while l < len(XJ1):
        XJ.append(XJ2[l]); YJ.append(YJ2[l])
        XJ.append(XJ1[l]); YJ.append(YJ1[l])
        l += 1
    XJ.append(XJ2[l]); YJ.append(YJ2[l])
    XJ.append(b); YJ.append(f(b))
    F = h/3*(f(a) + 2*z1 + 4*z2 + f(b))
    return XJ, YJ, F

def composite_trapz(f, a, b, h):
    XJ, YJ = [], []
    XJ.append(a); YJ.append(f(a))
    j, n, y, z = 1, math.ceil((b-a)/h), 0, 0
    while j <= n-1:
        x_j = a + j*h
        XJ.append(x_j)
        y_j = f(x_j)
        YJ.append(y_j)
        z += y_j
        j += 1
    XJ.append(b); YJ.append(f(b))
    return XJ, YJ, h/2*(f(a) + 2*z + f(b))

def gaussian_quadrature(function, a, b):
    return integrate(function, a, b)

def integrate(function, a, b):
    F, error = quad(function, a, b)
    return F, error

def l_infinity_norm(x, x0):
    norm_i, i = np.zeros_like(x0), 0
    while i < n:
        norm_i[i] = np.abs(x[i] - x0[i])
        i += 1
    return np.amax(norm_i)

def lagrange_polynomial(X, Y):
    def term(xk, yk):
        num, den, L_k = [], [], []
        for xl in X:
            if xl != xk:
                num.append(sym_x-xl)
                den.append(xk-xl)
        L_k = (np.divide(np.prod(num), np.prod(den)))
        return L_k * yk
    def error(n, xi):
        roots, g, xi_error = [], [], []
        i = 0
        while i <= n:
            root = X[i]
            roots.append(sym_x - root)
            g = np.prod(roots)
            k = 0
            while k <= n:
                xi = sp.diff(xi, sym_x)
                k += 1
            dxi = np.abs(xi.evalf(subs={sym_x: root})/(math.factorial(k)))
            xi_error.append(np.abs(dxi))
            xi_err = np.max(xi_error)
            g_prime = sp.diff(g, sym_x)
            r = solve(g_prime)
            if i == 0:
                x = g_prime
                gx = g.evalf(subs={sym_x: x})
            if i == 1:
                x = r[0]
                gx = g.evalf(subs={sym_x: x})
            else:
                R = []
                for s in r:
                    if not isinstance(s, complex):
                        R.append(g.evalf(subs={sym_x: s}))
                gx = np.amax(np.abs(R))
            i += 1
        return np.abs(xi_err*gx)
    sym_x, yn, bound = sp.Symbol('x'), [], []
    for xk in X:
        k = X.index(xk)
        yn.append(term(xk, Y[k]))
        bound.append(error(k, sum(yn)))
    return yn, sum(yn), bound

def linear_least_squares(X_i, Y_i, n):
    def poly(X):
        terms, k = [], 0
        for x in X:
            terms.append(x*(sym_x**k))
            k += 1
        p = simplify(sum(terms))
        err, i = 0, 0
        for x_i in X_i:
            px = p.subs(sym_x, x_i)
            err += (Y_i[i] - px)**2
            i += 1
        return p, err
    m, sym_x = len(X_i), sp.Symbol('x')
    A, x = np.zeros((n+1, n+1)), np.zeros((n+1,1))
    b, i = np.zeros_like(x), 0
    while i <= n:
        j = 0
        while j <= n:
            a_ij, k = 0, 0
            while k < m:
                a_ij += (X_i[k])**(i + j)
                k += 1
            A[i][j] = a_ij
            j += 1
        b_i, k = 0, 0
        while k < m:
            b_i += Y_i[k]*(X_i[k]**(i))
            k += 1
        b[i] = b_i
        i += 1
    x = np.transpose(np.linalg.solve(A, b))
    X, terms, k = x[0], [], 0
    for x in X:
        terms.append(x*(sym_x**k))
        k += 1
    polynomial = simplify(sum(terms))
    E, i = 0, 0
    for x_i in X_i:
        E += (Y_i[i] - polynomial.subs(sym_x, x_i))**2
        i += 1
    return polynomial, E

def newton_difference(X, FX, m, n, x, state):
    def fterm(i, j):
        fij = (fxn[i][j] - fxn[i-1][j])/(fxn[i][0] - fxn[i-j][0])
        return fij
    fxn, coeff, term, poly = np.zeros((m,n)), [], [], []
    m, n = m - 1, n - 1
    fxn[:m,0], fxn[:m,1], j = X, FX, 1
    while j <= n:
        i = 1
        while i <= m:
            fk = fterm(i, j)
            fxn[i][j+1] = fk
            if state == 'forward' and i == j:
                coeff.append(fk)
            if state == 'backward' and i == m - 1:
                coeff.append(fk)
            i += 1
        j += 1
    for c in coeff:
        k = coeff.index(c)
        term.append(sym_x - X[k])
        poly.append(c*np.prod(term))
    if state == 'forward': polynomial = simplify(sum(poly) + FX[0])
    if state == 'backward': polynomial = simplify(sum(poly) + FX[m])
    px = polynomial.subs(sym_x, x)
    return polynomial, px

def richard_extrapolation(function, x0, h, order, state):
    def f(h):
        x = x0 + h
        return x, function(x)
    i, X, FX = 0, [], []
    while i < order:
        dx = h / (2**order) * (2**i)
        x_i, fx_i = f(dx)
        X.append(x_i); FX.append(fx_i)
        i += 1
    m = len(X)
    n = m + 1
    return newton_difference(X, FX, m, n, x0, state)

def single_variable_newton_raphson(f, x, p0, tol):
    i, error = 0, tol*10
    P, ERROR, I = [], [], []
    while error >= tol:
        fp0 = f(p0)
        df = sp.lambdify(x, sp.diff(f))
        dfp0 = df(p0)
        p = p0 - (fp0/dfp0)
        P.append(p)
        error = abs(p - p0)
        ERROR.append(error); I.append(i)
        p0 = p
        i += 1
    return P, ERROR, I

def vector_converge(A, x0, b, tol, type, method, w):
    def jacobi(x0):
        while i < n:
            j, y = 0, 0.
            while j < n:
                if j != i:
                    y += A[i][j]*x0[j]
                    j += 1
            xi[i] = (-y + b[i])/A[i][i]
            i += 1
        return xi
    def gauss_seidel(x0):
        while i < n:
            j, y1, y2 = 0, 0., 0.
            while j < i-1:
                y1 += A[i][j]*xi[j]
                j += 1
            j = i + 1
            while j < n:
                y2 += A[i][j]*x0[j]
                j += 1
            xi[i] = (-y1 - y2 + b[i])/A[i][i]
            i += 1
        return xi
    def successive_over_relaxation(x0):
        while i < n:
            gauss_seidel(x0)
            xi[i] = (1 - w)*x0[i] + w*gauss_seidel(x0)
            i += 1
        return xi
    n = len(x0)
    x0, b, norm, k = np.reshape(x0,(n,1)), np.reshape(b,(n,1)), tol*10, 0
    X, NORM, K = [], [], []
    X.append(x0); K.append(k)
    while norm > tol:
        xi, i = np.zeros_like(x0), 0
        if method == 'jacobi': xi = jacobi(x0)
        if method == 'gauss_seidel': xi = gauss_seidel(x0)
        if method == 'successive_over_relaxation': xi = successive_over_relaxation(x0)
        if type == 'l_infinity': norm = l_infinity_norm(xi, x0)
        if type == 'l_2': norm = l_2_norm(xi, x0)
        X.append(xi); NORM.append(norm); K.append(k)
        x0 = xi
        k += 1
    m, n = len(X[0]), len(X)
    X_mat, j = np.zeros((m,n)), 0
    while j < n:
        i = 0
        while i < m:
            X_mat[i][j] = float(X[j][i])
            i += 1
        j += 1
    return X_mat, NORM, K

import numpy,from scipy,import sympy,from sympy


import numpy as np
from matplotlib import pyplot as plt

from sspy.filters import KalmanFilter
from sspy.smoothers import KalmanSmoother

from sspy.util import model_noisy, model_noiseless, plot_estimate, rmse

## Reseed random generator
_reseed = True

# Process model
F = np.array([[1, 1, 0], [0, 1, 1], [0, 0, 1]])
# H
H = np.array([[1, 0, 0]])

# Process noise covariance
Q = np.diag(np.array([0., 0.01, 0.001]))

# Observation noise covariance
R = np.array([[400]])

# Initial state and estimation error covariance (assume Q)
x0 = np.array([[0., 0.1, 0.05]]).T
P0 = np.diag(np.array([10, 1, 0.1])) #Q.copy()

# Data dimensions
n_x = 3
n_y = 1

# Number of observations
n_t = 100
if _reseed:
    seed = np.random.randint(0,np.iinfo(np.int32).max)
    print(seed)
else:
    seed = 1892303842
np.random.seed(seed)

## Kalman filter subroutine
def run_filter(y_noisy):
    kf = KalmanFilter(x0, P0, F, Q, H, R, _verbose=False)

    x_estimate = np.zeros((n_x, n_t))
    P_estimate = np.zeros((n_x, n_x, n_t))
    y_estimate = np.zeros((n_y, n_t))
    Py_estimate = np.zeros((n_y, n_y, n_t))

    x_estimate[:,0] = x0.ravel()
    y_estimate[:,0] = (H @ x0).ravel()
    P_estimate[:,:,0] = P0
    Py_estimate[:,:,0] = H @ P0 @ H.T + R

    for i_ in range(1, n_t):
        kf.predict()
        kf.update(y_noisy[:,i_])
        x_estimate[:,i_] = kf.state['expected'].ravel()
        P_estimate[:,:,i_] = kf.state['err_cov']
        y_estimate[:,i_] = (H @ x_estimate[:,i_].reshape(n_x,1)).ravel()
        Py_estimate[:,:,i_] = H @ P_estimate[:,:,i_] @ H.T + R

    return x_estimate, P_estimate, y_estimate, Py_estimate, kf

## Kalman (Rauch-Tung-Streibel) smoother subroutine
def run_smoother(kf):
    rts = KalmanSmoother.from_filter(kf)
    states = rts.smooth()

    x_smoothed = np.zeros_like(x_true)
    P_smoothed = np.zeros((n_x, n_x, n_t))
    y_smoothed = np.zeros_like(y_true)
    Py_smoothed = np.zeros((n_y, n_y, n_t))

    x_smoothed[:,0] = x0.ravel()
    y_smoothed[:,0] = (H @ x0).ravel()
    P_smoothed[:,:,0] = P0

    for i_ in range(1, n_t):
        x_smoothed[:,i_] = states[i_]['expected'].ravel()
        P_smoothed[:,:,i_] = states[i_]['err_cov']
        y_smoothed[:,i_] = (H @ x_smoothed[:,i_].reshape(n_x,1)).ravel()
        Py_smoothed[:,:,i_] = H @ P_smoothed[:,:,i_] @ H.T + R

    return x_smoothed, P_smoothed, y_smoothed, Py_smoothed, rts

## Run Kalman prediction (n_s * n_t filter samples)
def run_predictor(y_noisy, ratio=0.8):
    pr = KalmanFilter(x0, P0, F, Q, H, R, _verbose=False)

    n_s = int(np.fix(ratio * n_t))

    x_predict = np.zeros((n_x, n_t-n_s))
    P_predict = np.zeros((n_x, n_x, n_t-n_s))
    y_predict = np.zeros((n_y, n_t-n_s))
    Py_predict = np.zeros((n_y, n_y, n_t-n_s))

    for i_ in range(1, n_s):#%n_t):
        pr.predict()
        pr.update(y_noisy[:,i_])

    for i_ in range(0,n_t-n_s):
        pr.predict()
        x_predict[:,i_]   = pr.state['expected'].ravel()
        P_predict[:,:,i_] = pr.state['err_cov']
        y_predict[:,i_] = (H @ x_predict[:,i_].reshape(n_x,1)).ravel()
        Py_predict[:,:,i_] = H @ P_predict[:,:,i_] @ H.T + R

    return x_predict, P_predict, y_predict, Py_predict

### PLOTTING
def plot_filter():
    f, ax = plt.subplots(3, 1, sharex='all')
    ax[0].set_title('Filter estimates')

    ax[0].plot(y_true.T, 'k-')
    ax[0].plot(y_noisy.T, 'b.')
    plot_estimate(np.arange(n_t), y_estimate, P_estimate[0,0,:], ax=ax[0])

    ax[0].set_xlabel('$t$')
    ax[0].set_ylabel('$x$')
    ax[0].legend(labels=['true state','noisy measurements','filtered estimate','estimate confidence (0.95)'])

    lbl = ['$\dot{x}$','$\ddot{x}$']
    for i in range(2):
        #ax[i+1].figure(figsize=(14,4))
        ax[i+1].plot(x_true[i+1,:].ravel(),'k-')

        plot_estimate(np.arange(n_t),
                      x_estimate[i+1,:], P_estimate[i+1,i+1,:],
                      ax=ax[i+1])

        ax[i+1].set_xlabel('$t$')
        ax[i+1].set_ylabel(lbl[i])
        ax[i+1].legend(labels=['true state','filtered estimate','estimate confidence (0.95)'])

def plot_smoother():
    f, ax = plt.subplots(3, 1, sharex='all')
    ax[0].set_title('Smoother estimate')
    ax[0].plot(y_true.T, 'k-')
    ax[0].plot(y_estimate.T, 'r--', lw=3)

    plot_estimate(np.arange(n_t), y_smoothed, P_smoothed[0,0,:], c='b',ax=ax[0])

    ax[0].set_xlabel('$t$')
    ax[0].set_ylabel('$x$')
    ax[0].legend(labels=['true state','filtered estimate','smoothed estimate','smoother confidence (0.95)'])

    lbl = ['$\dot{x}$','$\ddot{x}$']
    for i in range(2):
        #plt.figure(figsize=(14,4))
        ax[i+1].plot(x_true[i+1,:].ravel(),'k-')
        ax[i+1].plot(x_estimate[i+1,:].ravel(), 'r--', lw=3)

        plot_estimate(np.arange(n_t), x_smoothed[i+1,:], P_smoothed[i+1,i+1,:], c='b',ax=ax[i+1])

        ax[i+1].set_xlabel('$t$')
        ax[i+1].set_ylabel(lbl[i])
        ax[i+1].legend(labels=['true state','filtered estimate','smoothed estimate','smoother confidence (0.95)'])

def plot_prediction(r):
    plt.figure()
    plt.title('Predictor (%3.2f)' % r)

    plt.plot(y_true.T, 'k-')
    plt.plot(y_noisy[:,0:n_s+1].T, 'b.')

    plot_estimate(np.arange(n_s+1), y_estimate[:,0:n_s+1], P_estimate[0,0,0:n_s+1])

    plot_estimate(np.arange(n_s,n_t), y_predict, P_predict[0,0,:], c='g')

    plt.xlabel('$t$')
    plt.ylabel('$x$')
    plt.legend(labels=['true state', 'noisy measurements', 'filtered estimate','prediction','estimation confidence (0.95)','prediction confidence (0.95)'])

### RUN TESTS
num_test = 1000
filter_ratios = [0.9, 0.75, 0.5, 0.2, 0.]

f_rmse = np.zeros((n_x + n_y, num_test))
s_rmse = np.zeros((n_x + n_y, num_test))
p_rmse = np.zeros((n_x + n_y, num_test, len(filter_ratios)))

for k in range(num_test):
    if not k % 10:
        print(k)
    # Model systems with noise
    x_true, y_true, y_noisy = model_noisy(x0, F, Q, None, H, R, n=n_t)

    # Run filter
    x_estimate, P_estimate, y_estimate, Py_estimate, kf = run_filter(y_noisy)
    # Run smoother
    x_smoothed, P_smoothed, y_smoothed, Py_smoothed, _ = run_smoother(kf)
    # Calculate RMSE
    f_rmse[:,k] = rmse(np.vstack([x_true,y_true]),
                       np.vstack([x_estimate, y_estimate])).ravel()
    s_rmse[:,k] = rmse(np.vstack([x_true,y_true]),
                       np.vstack([x_smoothed, y_smoothed])).ravel()

    if k is num_test-1:
        plot_filter()
        plot_smoother()

    for j, r_ in enumerate(filter_ratios):
        x_predict, P_predict, y_predict, Py_predict = run_predictor(y_noisy, r_)

        n_s = int(np.fix(r_ * n_t))
        p_rmse[:,k,j] = rmse(np.vstack([x_true[:,n_s:n_t],y_true[:,n_s:n_t]]),
                             np.vstack([x_predict, y_predict])).ravel()
        if k is num_test-1 and (j is 1 or j is 2):
            plot_prediction(r_)

## Show results
f_m, f_s = np.mean(f_rmse, 1), np.std(f_rmse, 1)
s_m, s_s = np.mean(s_rmse, 1), np.std(s_rmse, 1)
p_m, p_s = np.mean(p_rmse, 1).squeeze(), np.std(p_rmse, 1).squeeze()

lbl = ['x[0]','x[1]','x[2]','y[0]']
print('        RMSE             ||   prediction ')
print('_____|_filter__|_smoother||___0.90___|___0.75___|___0.50___|___0.20___|___0.00___')
for i in range(4):
    print('%s | %6.5f | %6.5f || %8.4f | %8.4f | %8.4f | %8.4f | %8.4f' %\
            (lbl[i],f_m[i],s_m[i],p_m[i,0],p_m[i,1],p_m[i,2],p_m[i,3],p_m[i,4]))
    print(' +/- | %6.5f | %6.5f || %8.4f | %8.4f | %8.4f | %8.4f | %8.4f'  %\
                (f_s[i], s_s[i],p_s[i,0],p_s[i,1],p_s[i,2],p_s[i,3],p_s[i,4]))
    print('=================================================================================')
# Plot all
plt.show()


import numpy as np
import pandas_datareader as pdr
import datetime as dt
import pandas as pd
import matplotlib.pyplot as plt

from get_data_api_b import get_pd_histo

start = dt.datetime(2020, 9, 1)
date_in_string = start.strftime("%d %b %Y ")

list_of_tickers = ['BTCUSDT', 'ETHUSDT', 'XTZUSDT', 'SOLUSDT' ]

data = pd.DataFrame()

for tick in list_of_tickers:
    dataiso = get_pd_histo(tick, date_in_string)
    dataiso = dataiso.set_index('Open_Time')
    dataiso = dataiso['Close']
    data[tick] = dataiso

portfolio = [.25, .15, .40, .20]

log_return  = np.sum(np.log(data/data.shift())*portfolio, axis=1)

# fig, ax = plt.subplots()
# log_return.hist(bins=50, ax=ax)
# plt.show()

return_daily = log_return.mean()
std_daily = log_return.std()

sharpe_ratio = log_return.mean()/log_return.std()

annualized_shape_ratio =  sharpe_ratio*364**.5

weight = np.random.random(4)
weight /= weight.sum()

log_return2 = np.sum(np.log(data/data.shift())*weight, axis=1)
sharpe_ratio2 = log_return2.mean()/log_return2.std()
asr2 = sharpe_ratio2*364**.5

print('portfolio')
print(portfolio)
print(annualized_shape_ratio)
print('randomly')
print(weight)
print(asr2)


A = [[2.,  1.,  4.,  1.],
     [3.,  4., -1., -1.],
     [1., -4.,  1.,  5.],
     [2., -2.,  1.,  3.]]

v = [-4., 3., 9., 7. ]

N = len(v)

# Gaussian elimination
for m in range(N):
    div = A[m][m]
    for n in range(N):
        A[m][n] /= div
    v[m] /= div
    for l in range(m+1,N):
        mult = A[l][m]
        for o in range(N):
            A[l][o] -= mult*A[m][o]
        v[l] -= mult*v[m]

# Backsubstitution
x = [0 for i in range(N)]
for m in range(N-1,-1,-1):
    x[m] = v[m]
    for l in range(m+1,N):
        x[m] -= A[m][l]*x[l]

print(x)

# Using numpy:
A = [[2.,  1.,  4.,  1.],
     [3.,  4., -1., -1.],
     [1., -4.,  1.,  5.],
     [2., -2.,  1.,  3.]]

v = [-4., 3., 9., 7. ]
import numpy
print(numpy.linalg.solve(A,v))


import matplotlib.pyplot as plt
import numpy as np

def Re_Z(f,R1,R2,C2):
    """
    Return the real part of the complex impedance of a circuit formed by a series of a resistance (R1), a capacitor (C1) and a parallel of a resistance (R2) and a capacitor (C2).
    ---------------------------------------------------------------------
    INPUT:
        f: array, N dimensional. frequency array
        R1, C1, R2, C2: real, scalar. Resistnces and capacitors.
    OUTPUT:
        Real part of the impedance
    """
    w = 2*np.pi*f
    return R1 + R2/(1+(w*C2*R2)**2)

def Im_Z(f,R1,C1,R2,C2):
    """
    Returns the imaginary part of the complex impedance of a circuit formed by a series of a resistance (R1), a capacitor (C1) and a parallel of a resistance (R2) and a capacitor (C2).
    ---------------------------------------------------------------------
    INPUT:
        f: array, N dimensional. frequency array
        R1, C1, R2, C2: real, scalar. Resistnces and capacitors.
    OUTPUT:
        Real part of the impedance
    """
    w = 2*np.pi*f
    return -1.0/(w*C1)-w*C2*R2**2/(1+(w*C2*R2)**2)

R1 = 90 # Ohm
C1 = 20e-6 # F
R2 = 7e3 # Ohm
C2 = 0.3e-6 # F

# Frequency extremes
f_min = 1 #Hz
f_max = 1e6 #Hz

# sampling frequency
sf = 1 #Hzu
# sampling points
sp = (f_max-f_min)*sf

# Frequency partition
f_part = np.geomspace(f_min,f_max,int(sp))

# Calculatin real and imaginary part of the impedance
Z_real = Re_Z(f_part,R1,R2,C2)
Z_imag = Im_Z(f_part,R1,C1,R2,C2)

# Calculating modulus and phase of the impedance
Z_modulus = np.sqrt(Z_real**2 + Z_imag**2)
Z_phase = np.arctan2(Z_imag,Z_real)

# Creating visualisation
fig, ax = plt.subplots(2, sharex = True)
ax[0].plot(f_part,20*np.log10(Z_modulus))
ax[0].set_ylabel("|Z| [dB]")
ax[1].plot(f_part,180/(np.pi)*Z_phase)
ax[1].set_ylabel("Phase [°]")
ax[1].set_xscale("log")
ax[1].set_xlabel("Frequency [Hz]")
#---------------------------------------------------------

#Johnson-Nyquist Noise formula
k_b = 1.38064852e-23 #m2 kg s-2 K-1
T = 300
S = 4*T*k_b*Z_real

# Creating visualization
fig1,ax1 = plt.subplots()
ax1.semilogx(f_part,10*np.log10(S))
ax1.set_ylabel(r"$S_{\nu}$ [dB]")
ax1.set_xlabel("Frequency [Hz]")
plt.show()


import math
import matplotlib.pyplot as plt
import numpy as np
import data_class as dc
import json

ROOT = 0.176923

def algebraic_equasion_function(x):
    """
    This function makes a calculation for an Algebraic equasion
    It calculates f(x) with the given equasion and x as a parameter
    """
    formula = x**2 + 6*x + 9
    return formula

def algebraic_equasion_defunction(x):
    """
    This function makes a calculation for an Algebraic equasion
    It calculates f'(x) with the given equasion and x as a parameter
    """
    formula = 2*x + 6
    return formula

def algebraic_u(x):
    """
    This function makes a calculation for an Algebraic equasion
    It calculates u(x) = f(x)/f'(x) with the given equasion and x as a parameter
    """
    f = algebraic_equasion_function(x)
    de_f = algebraic_equasion_defunction(x)
    u = f / de_f
    return u

def function_F_of_X(x):  # function - f(x)
    """
    This function makes a calculation for Non-Algebraic equasion
    It calculates f(x) with the given equasion and x as a parameter
    """
    solution = x ** 3 + x + 2 * math.sqrt(math.e ** x)
    return solution

def DE_function_F_of_X(x):  # function - f'(x)
    """
    This function makes a calculation for Non-Algebraic equasion
    It calculates f'(x) with the given equasion and x as a parameter
    """
    solution = 3 * x ** 2 + 1 + math.sqrt(math.e ** x)
    return solution

def solve_U_function_of_X(x):  # function - u(x)
    """
    This function makes a calculation for Non-Algebraic equasion
    It calculates u(x) = f(x)/f'(x) with the given equasion and x as a parameter
    """
    f_of_x = function_F_of_X(x)
    de_f_of_x = DE_function_F_of_X(x)
    variable_u = f_of_x / de_f_of_x
    return variable_u

json_data = []

first = True

def save_data(the_data):
    json_data.append([{"VALUES": i.VALUES, "F_X": i.F_X, "DEF_X": i.DEF_X, "P1": i.P1, "P3": i.P3, "ERROR": i.ERROR,
                       "counter": i.counter} for i in the_data])

def save_data_to_json(filename_str: str):
    with open(filename_str, "w") as file:
        json.dump(json_data, file)
    json_data.clear()

def read_data_from_json(filename_str: str):
    with open(filename_str, "r") as file:
        data = json.load(file)
    return data

def print_data(filename_str: str, title: str):
    data = read_data_from_json(filename_str)
    list_Lables = []
    NR_non_al = []
    MP_non_al = []

    for d in data[:(int(len(data) / 2))]:
        print(f"Value: {d[0]['VALUES']} Counter: {d[-1]['counter']}")
        list_Lables.append(d[0]['VALUES'])
        NR_non_al.append(d[-1]['counter'])
    print()

    for d in data[(int(len(data) / 2)):]:
        print(f"Value: {d[0]['VALUES']} Counter: {d[-1]['counter']}")
        MP_non_al.append(d[-1]['counter'])

    make_a_plot(list_Lables, NR_non_al, MP_non_al, title)

def solve_until_zero(x, counter, temp_data):  # function calculate f(x) till f(x) == 0
    the_data = temp_data
    while True:
        f_of_x = round(function_F_of_X(x), 4)  # Our f'(x) for the table!
        if first:
            new_x = x - solve_U_function_of_X(x)  # Our X new for the table!
        else:
            new_x = x - 1 / 12 * (5 * solve_U_function_of_X(x) + 7 * f_of_x / DE_function_F_of_X(
                x - 6 * solve_U_function_of_X(x) / 7))  # Our X new for the table!

        p1 = calculate_p(x, new_x, 1)  # p1
        p3 = calculate_p(x, new_x, 3)  # p3
        #  DEBUG print(f"VALUE: {x}, F_OF_X:{f_of_x}, DE_F_OF_X: {de_f_of_x}, NEW_VALUE: {new_x}")
        if counter == -1:
            counter += 1
            the_data.append(dc.data_container(x, f_of_x, DE_function_F_of_X(x), "", "", calculate_error(x), counter))
        else:
            counter += 1
            if round(f_of_x, 4) != 0:
                the_data.append(
                    dc.data_container(new_x, round(function_F_of_X(new_x), 4), DE_function_F_of_X(new_x), p1, p3,
                                      calculate_error(new_x), counter))
                # for i in range(0, the_data.counter_DC()):
                #     the_data.print(i)
                return solve_until_zero(new_x, counter, the_data)
            else:
                break

    save_data(the_data)

def calculate_error(x: float):  # function to calculate an error ERROR
    error = abs(x - ROOT)
    return error

def calculate_p(old_x, new_x: float, power: int):  # function to calculate an error P1 (Power of 1)
    p = abs(new_x - ROOT) / abs(old_x - ROOT) ** power
    return p

def call_NA(list: list):
    global first
    counter = -1
    first = True
    for element in list:
        temp_data = []
        solve_until_zero(element, counter, temp_data)
        # print('\n')
    first = not first
    for element in list:
        temp_data = []
        solve_until_zero(element, counter, temp_data)
        # print('\n')

def solve_until_zeroA(x, counter, temp_data):  # function calculate f(x) till f(x) == 0
    the_data_algebraic = temp_data
    while True:
        f_of_x = round(function_F_of_X(x), 4)  # Our f'(x) for the table!
        if first:
            new_x = x - algebraic_u(x)  # Our X new for the table!
        else:
            new_x = x - 1 / 12 * (5 * algebraic_u(x) + 7 * round(algebraic_equasion_function(x),
                                                                 4) / algebraic_equasion_defunction(
                x - 6 * algebraic_u(x) / 7))

        p1 = calculate_p(x, new_x, 1)  # p1
        p3 = calculate_p(x, new_x, 3)  # p3
        if counter == -1:
            counter += 1
            the_data_algebraic.append(
                dc.data_container(x, f_of_x, DE_function_F_of_X(x), "", "", calculate_error(x), counter))
        else:
            counter += 1
            if round(f_of_x, 4) != 0:
                the_data_algebraic.append(
                    dc.data_container(new_x, round(function_F_of_X(new_x), 4), DE_function_F_of_X(new_x), p1, p3,
                                      calculate_error(new_x), counter))
                return solve_until_zero(new_x, counter, the_data_algebraic)
            else:
                break

    save_data(the_data_algebraic)

def call_A(list: list):
    global first
    counter = -1
    for element in list:
        temp_data = []
        solve_until_zeroA(element, counter, temp_data)
        # print('\n')
    first = not first
    for element in list:
        temp_data = []
        solve_until_zeroA(element, counter, temp_data)
        # print('\n')

def make_a_plot(labels_parameter: list, NewtonMethod: list, MultiPoint: list, title: str):
    """
    This function makes a plot of the given data for comparison
    """
    x = np.arange(len(labels_parameter))
    width = 0.35

    fig, ax = plt.subplots()
    rects1 = ax.bar(x - width / 2, MultiPoint, width, label='Multi-Point Method')
    rects2 = ax.bar(x + width / 2, NewtonMethod, width, label='Newtons Method')

    ax.set_ylabel('Number of Repeats')
    ax.set_title(title)
    ax.set_xticks(x)
    ax.set_xticklabels(labels_parameter)
    ax.legend()
    ax.bar_label(rects1, padding=3)
    ax.bar_label(rects2, padding=3)
    fig.tight_layout()
    plt.show()

if __name__ == "__main__":
    labels = [-35, -25, -15, -10, -7.5, -5, 0, 5, 15, 25, 35, 50]
    print("NON ALGEBRAIC:")
    call_NA(labels)
    #print(json_data)
    save_data_to_json("data1.json")
    read_data_from_json("data1.json")
    print_data("data1.json", 'Non-Algebraic')
    print("ALGEBRAIC:")
    call_A(labels)
    #print(json_data)
    save_data_to_json("data2.json")
    read_data_from_json("data2.json")
    print_data("data2.json", 'Algebraic')


import numpy as np
import matplotlib.pyplot as plt
import random
import itertools
from scipy.io import loadmat

def initialize_centroids(X, k):
    """X: dataset, k: number of centroids"""

    number_of_samples = X.shape[0]
    sample_points_ids = random.sample(range(0, number_of_samples), k)

    centroids = [tuple(X[id]) for id in sample_points_ids]
    unique_centroids = list(set(centroids))

    number_of_unique_centroids = len(unique_centroids)

    while number_of_unique_centroids < k:
        new_sample_points_ids = random.sample(range(0, number_of_samples), k - number_of_unique_centroids)
        new_centroids = [tuple(X[id]) for id in new_sample_points_ids]
        unique_centroids = list(set(unique_centroids + new_centroids))

        number_of_unique_centroids = len(unique_centroids)

    return np.array(unique_centroids)

def euclidean_distance(x,y):
    """Computes euclidean distance between matrices x and y."""

    x_sq = np.reshape(np.sum(x * x, axis=1), (x.shape[0], 1))
    y_sq = np.reshape(np.sum(y * y, axis=1), (y.shape[0]), 1)
    xy = x @ y.T

    dist = x_sq + y_sq - 2 * xy

    return np.sqrt(dist)

def assign_clusters(X, centroids):
    """ assigns the points to one of the centroids"""

    k = centroids.shape[0]

    clusters = {}

    distance_matrix = euclidean_distance(X, centroids)

    closest_cluster_ids = np.argmin(distance_matrix, axis=1)

    for i in range(k):
        clusters[i] = []

    for i, cluster_id in enumerate(closest_cluster_ids):
        clusters[cluster_id].append(X[i])

    return clusters

def stop_iteration(old_centroids, new_centroids, threshold):
    ''' stopping condition'''
    dist = euclidean_distance(old_centroids, new_centroids)
    centroids_stop= np.max(dist.diagonal()) <= threshold

    return centroids_stop

def kmeans(X, k,threshold=0):
    """Performs k-means and find k cluster centroid"""

    new_centroids = initialize_centroids(X=X, k=k)

    centroids_covered = False

    while not centroids_covered:
        previous_centroids = new_centroids
        clusters = assign_clusters(X, previous_centroids)

        new_centroids = np.array([np.mean(clusters[key], axis=0, dtype=X.dtype) for key in sorted(clusters.keys())])

        centroids_covered = stop_iteration(previous_centroids, new_centroids, threshold)

    return new_centroids

k = 8
x1 = loadmat('clustering_data2.mat')
data = x1['X']
X = data.T

centroids = kmeans(X, k, threshold=1/100)

clusters = assign_clusters(X, centroids)

marker = itertools.cycle(('+','o','^','s','*','x','p','H'))

plt.figure()
for centroid, points in clusters.items():
    points = np.array(points)
    centroid = np.mean(points, axis=0)

    plt.scatter(points[:, 0], points[:, 1], marker=next(marker), s = 50)
    plt.grid()
    plt.scatter(centroid[0], centroid[1], marker='x', color="black")

plt.show()


# Reference: https://www.sfu.ca/~ssurjano/branin.html

import pygpbo
import numpy as np
def F(p1,p2,noise=0):
  return -(np.power((p2-5.1/(4*np.power(3.14,2))*np.power(p1,2)+5/3.14*p1-6),2)+10*(1-1/(8*3.14))*np.cos(p1)+10) + noise*np.random.randn()

bounds = {'p1': (-5, 10), 'p2': (0, 15)}

optimizer = pygpbo.BayesOpt(F, bounds)
optimizer.add_custom_points([{'p1': -4, 'p2': 1}, {'p1': -3, 'p2': 5},{'p1': 9, 'p2': 10},{'p1': 4, 'p2': 14}])

optimizer.maximize(n_iter=30)
print("Optimum point and value: ", optimizer.max_sample)

# Output seen:
"""
Optimum point and value:  ({'p1': 9.474934424992359, 'p2': 2.373669396349481}, array([-0.43209275]))
"""


import matplotlib.pyplot as plt
import statsmodels.api as sm

from tsdata import generate_sample_data

sample_ts, _ = generate_sample_data()

ts_fig, ts_ax = plt.subplots(tight_layout=True)
sample_ts.plot(ax=ts_ax, label="Observed")
ts_ax.set_title("Time series data")
ts_ax.set_xlabel("Date")
ts_ax.set_ylabel("Value")

adf_results = sm.tsa.adfuller(sample_ts)
adf_pvalue = adf_results[1]
print("Augmented Dickey-Fuller test:\nP-value:", adf_pvalue)

ap_fig, (acf_ax, pacf_ax) = plt.subplots(2, 1, sharex=True, tight_layout=True)
sm.graphics.tsa.plot_acf(sample_ts, ax=acf_ax, title="Observed autocorrelation")
sm.graphics.tsa.plot_pacf(sample_ts, ax=pacf_ax, title="Observed partial autocorrelation")
pacf_ax.set_xlabel("Lags")
pacf_ax.set_ylabel("Value")
acf_ax.set_ylabel("Value")

arma_model = sm.tsa.ARMA(sample_ts, order=(1, 1))

arma_results = arma_model.fit()
print(arma_results.summary())

residuals = arma_results.resid
rap_fig, (racf_ax, rpacf_ax) = plt.subplots(2, 1, sharex=True, tight_layout=True)
sm.graphics.tsa.plot_acf(residuals, ax=racf_ax, title="Residual autocorrelation")
sm.graphics.tsa.plot_pacf(residuals, ax=rpacf_ax, title="Residual partial autocorrelation")
rpacf_ax.set_xlabel("Lags")
rpacf_ax.set_ylabel("Value")
racf_ax.set_ylabel("Value")

fitted = arma_results.fittedvalues
fitted.plot(c="r", ax=ts_ax, label="Fitted")
ts_ax.legend()

plt.show()


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

###
# Name: Alley and Royal
# Student ID: 2293544
# Email: busick@chapman.edu
# Course: PHYS220/MATH220/CPSC220 Fall 2018
# Assignment: CW07
###

import numpy as np

def s(T, t, n):

    #k values
    array = np.arange(1, n+1)

    def function(k):
        return (((1)/(2*k-1))*np.sin((2*(2*k-1)*np.pi*t)/(T)))
    summation = np.vectorize(function)
    value = (4/np.pi)*np.sum(summation(array))
    return value

def f(T, t):
    if 0 < t < (T/2):
        return 1
    elif t == 0:
        return 0
    elif -(T/2) < t < 0:
        return -1
    else:
        print("Out of range")
        return None


import numpy as np
import os

def compute_euclidean_distance(point, centroid):
    return np.sqrt(np.sum((point - centroid) ** 2))

def assign_label_cluster(distance, data_point, centroids):
    index_of_minimum = min(distance, key=distance.get)
    return [index_of_minimum, data_point, centroids[index_of_minimum]]

def compute_new_centroids(cluster_label, centroids):
    return np.array(cluster_label + centroids) / 2

def iterate_k_means(data_points, centroids, total_iteration):
    label = []
    cluster_label = []
    total_points = len(data_points)
    k = len(centroids)

    for iteration in range(0, total_iteration):
        for index_point in range(0, total_points):
            distance = {}
            for index_centroid in range(0, k):
                distance[index_centroid] = compute_euclidean_distance(data_points[index_point],
                                                                      centroids[index_centroid])
            label = assign_label_cluster(distance, data_points[index_point], centroids)
            centroids[label[0]] = compute_new_centroids(label[1], centroids[label[0]])

            if iteration == (total_iteration - 1):
                cluster_label.append(label)

    return [cluster_label, centroids]

def print_label_data(result):
    print("Result of k-Means Clustering: \n")
    for data in result[0]:
        print("data point: {}".format(data[1]))
        print("cluster number: {} \n".format(data[0]))
    print("Last centroids position: \n {}".format(result[1]))

def create_centroids():
    centroids = []

    centroids.append([-4.282 ,2.269])
    centroids.append([-4.334 ,- 2.43])
    centroids.append(  [0.044 ,- 5.791])
    return np.array(centroids)

filename = os.path.dirname(__file__) + "\data.csv"
data_points = np.genfromtxt(filename, delimiter=",")
centroids = create_centroids()
total_iteration = 130

[cluster_label, new_centroids] = iterate_k_means(data_points, centroids, total_iteration)
print_label_data([cluster_label, new_centroids])
print()


from random import *
from numpy import *

darts=input("How many darts? ")

x=random.random(darts)
y=random.random(darts)
one=ones(darts)

dist=sqrt(x*x+y*y)
print "dist ",dist
hit=(less_equal(dist,one)==True)
print "hit ",hit
hits=sort(hit)
print "hits ",hits
hitsnum=hits.searchsorted(True, side="right") - hits.searchsorted(True, side="left")
print "hitsnum ",hitsnum

pic=4.0*hitsnum/darts
print "Pi calculated as ",pic
error=100*(pic-3.1415926535897932)/(3.1415926535897932)
print "Error is ", error, "%"


"""
Script to do SVD on the covariance matrix of the MASKED voxel by time matrix.
Spits out the first k components for each subject.

Run with:
    python get_pcs_script.py

"""

from __future__ import absolute_import, division, print_function
import numpy as np
import numpy.linalg as npl
import nibabel as nib
import os
import sys
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.lines as mlines

# Relative paths to project and data.
project_path          = "../../../"
path_to_data          = project_path+"data/ds009/"
location_of_images    = project_path+"images/"
location_of_functions = project_path+"code/utils/functions/"
behav_suffix           = "/behav/task001_run001/behavdata.txt"

sys.path.append(location_of_functions)
from Image_Visualizing import make_mask

# List of subject directories.
sub_list = os.listdir(path_to_data)
sub_list = [i for i in sub_list if 'sub' in i]

# Initialize list to store principal components for each subject.
pcs = []
# Number of components to pull out.
k = 6

# Loop through all the subjects.
for name in sub_list:
    # amount of beginning TRs not standardized at 6
    behav=pd.read_table(path_to_data+name+behav_suffix,sep=" ")
    num_TR = float(behav["NumTRs"])

    # Load image data.
    img = nib.load(path_to_data+ name+ "/BOLD/task001_run001/bold.nii.gz")
    data = img.get_data().astype(float)

    # Load mask.
    mask = nib.load(path_to_data+ name+'/anatomy/inplane001_brain_mask.nii.gz')
    mask_data = mask.get_data()

    # Drop the appropriate number of volumes from the beginning.
    first_n_vols=data.shape[-1]
    num_TR_cut=int(first_n_vols-num_TR)
    data = data[...,num_TR_cut:]

    # Now fit a mask to the 3-d image for each time point.
    my_mask = np.zeros(data.shape)
    for i in range(my_mask.shape[-1]):
         my_mask[...,i] = make_mask(data[...,i], mask_data, fit=True)

    # Reshape stuff to 2-d (voxel by time) and mask the data.
    # This should cut down the number of volumes by more than 50%.
    my_mask_2d = my_mask.reshape((-1,my_mask.shape[-1]))
    data_2d = data.reshape((-1,data.shape[-1]))
    masked_data_2d = data_2d[my_mask_2d.sum(1) != 0,:]

    # Subtract means over voxels (columns).
    masked_data_2d = masked_data_2d - np.mean(masked_data_2d, 0)

    # Subtract means over time (rows)
    masked_data_2d = masked_data_2d - np.mean(masked_data_2d, axis=1)[:, None]

    # PCA analysis on MASKED data:
    # Do SVD on the time by time matrix and transform into first k principal components.
    U_masked, S_masked, VT_masked = npl.svd(masked_data_2d.T.dot(masked_data_2d))
    pcs.append(masked_data_2d.dot(U_masked[:,:k]))


"""
implement bayesian analysis of proportion/fraction/percent type parameter
like bias of coin, % of mutations etc
"""
from math import sqrt, exp, log
import numpy as np
import matplotlib.pyplot as plt
from SciInf_utilities import *
import sys
#--------------------------------------
#
print("\n implement bayesian analysis of proportion/fraction/percent type parameter")
print(" like bias of coin, % of mutations etc")
print(" work with log p \n")
if(len(sys.argv) == 3):
  n_pos = int(sys.argv[1])
  n_neg = int(sys.argv[2])
else:
  n_pos = int(input('Enter # of positive events/samples> '))
  n_neg = int(input('Enter # of negative events/samples> '))
print('\n # of positive, negative events: ',n_pos,n_neg,'\n')
# main
#
#generate pdf and cdf
#
df = 1./(NPOINT + 1)
f_axis = np.zeros(NPOINT)
log_f_pdf = np.zeros(NPOINT)
for i in range(NPOINT):
  f_axis[i] = (i+1)*df
  ff = f_axis[i]
  ffm1 = 1. - ff
  # f_pdf[i] = (ff**n_pos)*(ffm1**n_neg)
  log_f_pdf[i] = n_pos*log(ff) + n_neg*log(ffm1)
pdf_max = max(log_f_pdf)
log_f_pdf = log_f_pdf - pdf_max
f_pdf = np.exp(log_f_pdf)
f_cdf = pdf_to_cdf(f_axis,f_pdf)
write_pdf_cdf(f_axis,f_pdf,f_cdf,title='fraction pdf cdf',filename='fraction_pdf_cdf.dat')
summarize(f_axis,f_pdf,f_cdf,title='fraction parameter')
#
# plot
#
if(MAKEPLOT):
  plt.figure()
  plt.plot(f_axis,f_pdf,'g-')
  plt.plot(f_axis,f_cdf,'r-')
  plt.xlabel(' fraction (r)')
  plt.ylabel(' prob(r)')
  plt.title(' posterior pdf, cdf for fraction')
  plt.ylim((0.,1.2))
  plt.grid(True)
  plt.show()


import numpy as np

# sigmoid function
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# print function
def identity_function(x):
    return x

# 1st -> 2nd layer
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])

A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
print(Z1)

# 2nd -> 3rd layer
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])

A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
print(Z2)

# 3rd -> return layer
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])

A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)
print(Y)

# multi layer neural network
def init_network():
    network = {}

    network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])

    network['B1'] = np.array([0.1, 0.2, 0.3])
    network['B2'] = np.array([0.1, 0.2])
    network['B3'] = np.array([0.1, 0.2])

    return network

def foward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    B1, B2, B3 = network['B1'], network['B2'], network['B3']

    A1 = np.dot(x, W1) + B1
    Z1 = sigmoid(A1)
    A2 = np.dot(Z1, W2) + B2
    Z2 = sigmoid(A2)
    A3 = np.dot(Z2, W3) + B3
    Y = identity_function(A3)

    return Y

network = init_network()
x = np.array([1.0, 0.5])
y = foward(network, x)
print(y)


import numpy as np
import matplotlib.pyplot as plt
import naive_taylor_diagram.taylor_diagram as td

# -- synthetic data -- #
phase = np.pi/10  # phase difference between reference and synt_mes
magnt = 1.5  # magnitude factor
x = np.arange(0,1,0.001)
reference = np.sin(x*np.pi*10)  # 'reference'
synt_mesr = magnt*np.sin(x*np.pi*10+np.pi/10)  # 'measurement'

# -- taylor diagram -- #
fig = plt.figure(figsize=[10.2, 3.83])
ax1 = fig.add_subplot(121, projection='polar')
ax2 = fig.add_subplot(122)
ax1 = td.taylor(fig, ax=ax1)

# -- plot lines of skill or rmse -- #
ax1.contour(*td.skill_willmott_space(), linewidths=0.5, colors='0.5')
# ax1.contour(*td.rmse_space(), linewidths=0.5, colors='0.5')

# -- convert from cartesian to polar coordinates --
theta_corr = np.arccos(np.corrcoef(reference, synt_mesr)[0,1])
std0 = np.std(reference)
std1 = np.std(synt_mesr)

# -- divide by the radius standard deviation --
radius_stdn = std1/std0
ax1.scatter(theta_corr, radius_stdn)  # plot

# -- plot time series -- #
ax2.plot(x, reference, label='reference signal')
ax2.plot(x, synt_mesr, label='a signal')
ax2.legend()

plt.tight_layout()


import os
import glob
import torch
import pickle
from os import listdir
from os.path import isfile, join
import numpy as np
import librosa
import librosa.display
import matplotlib.pyplot as plt
from scipy.fftpack import dct
from torch.utils.data import random_split, Dataset, DataLoader

use_cuda = torch.cuda.is_available()
device = torch.device("cuda:0" if use_cuda else "cpu")

def display_spectrogram(spectrogram):
    librosa.display.specshow(spectrogram.transpose(), hop_length=220.5,y_axis='mel', fmax=8000, x_axis='s')
    #getting 7 second in time axis, it should be 3, why???
    plt.title('Mel Spectrogram')
    plt.colorbar(format='%+2.0f dB')
    plt.show()

def logmel_filterbanks(filename,pre_emphasis=0.97,frame_size = 0.025,frame_stride = 0.01,nfilt=40,normalize=True):
    target_len = 66150

    signal, sample_rate = librosa.load(filename)

    while(signal.shape[0] < target_len):
        signal = np.append(signal, signal[:target_len - signal.shape[0]])

    #Pre-Emphasis step
    emphasized_signal = np.empty(shape=len(signal)+1)
    emphasized_signal = np.append(signal[0], signal[1:] - pre_emphasis * signal[:-1])

    #Framing

    frame_length, frame_step = frame_size * sample_rate, frame_stride * sample_rate  # Convert from seconds to samples
    signal_length = len(emphasized_signal)
    frame_length = int(round(frame_length))
    frame_step = int(round(frame_step))
    num_frames = int(np.ceil(float(np.abs(signal_length - frame_length)) / frame_step)) + 1  # Make sure that we have at least 1 frame

    pad_signal_length = num_frames * frame_step + frame_length
    z = np.zeros((pad_signal_length - signal_length))
    pad_signal = np.append(emphasized_signal, z) # Pad Signal to make sure that all frames have equal number of samples without truncating any samples from the original signal

    indices = np.tile(np.arange(0, frame_length), (num_frames, 1)) + np.tile(np.arange(0, num_frames * frame_step, frame_step), (frame_length, 1)).T
    frames = pad_signal[indices.astype(np.int32, copy=False)]

    #Hamming-Window
    frames *= np.hamming(frame_length)

    #FFT
    NFFT = 512
    mag_frames = np.absolute(np.fft.rfft(frames, NFFT))  # Magnitude of the FFT
    pow_frames = ((1.0 / NFFT) * ((mag_frames) ** 2))

    #Filter-Bank
    low_freq_mel = 0
    high_freq_mel = (2595 * np.log10(1 + (sample_rate / 2) / 700))  # Convert Hz to Mel
    mel_points = np.linspace(low_freq_mel, high_freq_mel, nfilt + 2)  # Equally spaced in Mel scale
    hz_points = (700 * (10**(mel_points / 2595) - 1))  # Convert Mel to Hz
    bin = np.floor((NFFT + 1) * hz_points / sample_rate)

    fbank = np.zeros((nfilt, int(np.floor(NFFT / 2 + 1))))
    for m in range(1, nfilt + 1):
        f_m_minus = int(bin[m - 1])   # left
        f_m = int(bin[m])             # center
        f_m_plus = int(bin[m + 1])    # right

        for k in range(f_m_minus, f_m):
            fbank[m - 1, k] = (k - bin[m - 1]) / (bin[m] - bin[m - 1])
        for k in range(f_m, f_m_plus):
            fbank[m - 1, k] = (bin[m + 1] - k) / (bin[m + 1] - bin[m])
    filter_banks = np.dot(pow_frames, fbank.T)
    filter_banks = np.where(filter_banks == 0, np.finfo(float).eps, filter_banks)  # Numerical Stability
    filter_banks = 20 * np.log10(filter_banks)  # dB

    if normalize==True:
        #filter_banks = (filter_banks - filter_banks.mean()) / (filter_banks.max() - filter_banks.min())
        normed_filter_banks = (filter_banks - filter_banks.mean(axis=0)) / filter_banks.std(axis=0)
        return normed_filter_banks

    return filter_banks

def mfcc(filter_banks,num_ceps=13):
    return dct(filter_banks, type=2, axis=1, norm='ortho')[:, 1 : (num_ceps + 1)]

if __name__=='__main__':

    dataset_dir = '/home/bbekci/datasets/vctk/wav48_silence_trimmed'
    data = []
    c2i, i2c = {}, {}
    for indx, cla in enumerate(os.listdir(dataset_dir)):
                main_path = dataset_dir + '/' + cla + '/*.flac'
                for file_path in glob.glob(main_path):
                    data.append((file_path, cla))
                c2i[cla] = indx
                i2c[indx] = cla

    with open('preprocessed_vctk.pkl', 'wb') as pickle_file:
        result=[]
        for i in range(0,len(data)):
                sample = []
                sound_path, class_name = data[i]
                sound_data = logmel_filterbanks(sound_path)
                label = c2i[class_name]

                sample = [label, sound_data]

                result.append((sample))

        pickle.dump(result, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
        f.close()

    class PreprocessedDataset(Dataset):
        def __init__(self, file_dir):
            self.file_dir = file_dir
            self.lst = 0
            with open(file_dir, 'rb') as pickle_load:
                self.lst = pickle.load(pickle_load)

        def __len__(self):
            return len(self.lst)

        def n_class(self):
            return self.lst[-1][0]

        def __getitem__(self, idx):
            if torch.is_tensor(idx):
                idx = idx.tolist()

            sound_data = self.lst[idx][1]
            label = self.lst[idx][0]

            sample = (sound_data, label)

            return sample

    dataset_dir = '/home/bbekci/inzpeech/preprocessed_vctk.pkl'
    offset_dict = {}
    max_epochs = 25
    batch_size = 256

    sound_data = PreprocessedDataset(file_dir=dataset_dir)

    n_classes = sound_data.n_class()

    train_data, test_data = random_split(sound_data,
                                         [int(len(sound_data) * 0.8),
                                          len(sound_data) - int(len(sound_data) * 0.8)]
                                         )

    train_dataset_loader = torch.utils.data.DataLoader(train_data,
                                                       batch_size=batch_size,
                                                       shuffle=True,
                                                       num_workers=4)

    test_dataset_loader = torch.utils.data.DataLoader(test_data,
                                                      batch_size=batch_size,
                                                      shuffle=True,
                                                      num_workers=4)


import numpy as np
from sklearn.decomposition import PCA

def pca(X: np.ndarray, n_components: int = 2, svd_solver: str = 'auto') -> np.ndarray:
    """
    Apply PCA dimensionality reduction and return the projection of X on
    the first `n_components` principal components.

    Parameters
    ----------
    X
        Batch of instances.
    n_components
        Number of principal component projections to return.
    svd_solver
        Solver used for SVD. Options are ‘auto’, ‘full’, ‘arpack’ or ‘randomized’.

    Returns
    -------
    Projection of X on first `n_components` principcal components.
    """
    X = X.reshape(X.shape[0], -1)
    pca = PCA(n_components=n_components, svd_solver=svd_solver)
    pca.fit(X)
    X_pca = pca.transform(X)
    return X_pca


# # Quaternion class implementation for python.
#
# Offers native integration of operators between quaternions generated and
# numpy data structures.
#
# Toby James 2018

import numpy as np
class Quaternion():
    """
    Quaternion class implemented to allow manipulation of quaternions with
    each other, matrices (including, where appropriate, vectors) and scalars.

    Declare a quaternion of the form q = w + xi + yj +zk by

    >> q = Quaternion(w,x,y,z)

    Addition, subtraction, multiplication and division are all supported natively
    between quaternions and where appropriate with other data types.

    Transformation of quaternions is also supported:

    Normalised unit quaternion:     >> q_u = q.unit()
    Conjugate of q:                 >> q_c = q.conjugate()
    Reciprocal or inverse of q:     >> q_r = q.reciprocal() or >> q_i = q.inverse(). They are equivalent.

    Tolerance can be applied to the unit quaternion function to determine the
    unit quaternion to a desired accuracy.

    >> q_u = q.unit(tolerance=0.0001)

    """

    def __init__(self, w, x, y, z):
        self.w = w
        self.x = x
        self.y = y
        self.z = z
        self.magnitude = np.sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)

    def __repr__(self):             #Appropriate representation
        return "Quaternion(%r + %r i + %r j + %r k)" % (self.w, self.x, self.y, self.z)

    def unit(self, tolerance=0):#Creates the equivalent unit quaternion
    #By default, produces unit quaternion with an arbitrary tolerance ie magnitude will not perfectly be 1 - but should be sufficiently close
    #if tolerance is given, recursively divides by magnitude until the new magnitude is sufficiently close to 1 as desired
        if not tolerance:
            return Quaternion(self.w/self.magnitude,
                              self.x/self.magnitude,
                              self.y/self.magnitude,
                              self.z/self.magnitude)
        else:
            new_q = self.unit()     #scarily close to recursion
            while abs(1 - new_q.magnitude) > tolerance:
                new_q = new_q.unit()
            return new_q

    def conjugate(self):            #Create the quaternion conjugate
        return Quaternion(self.w,
                          -self.x,
                          -self.y,
                          -self.z)

    def reciprocal(self):           #Create the reciprocal
        return (self.conjugate()/(self.magnitude**2))

    def inverse(self):              #The same
        return self.reciprocal()

    def __add__(self,other):        #Addition of quaternions
        if isinstance(other, Quaternion):
            return Quaternion(self.w+other.w,self.x+other.x, self.y+other.y, self.z+other.z)
        else:
            raise TypeError("Unable to broadcast together types Quaternion and %r." %type(other))

    def __sub__(self,other):        #Subtraction of quaternions
        if isinstance(other, Quaternion):
            return Quaternion(self.w-other.w,self.x-other.x, self.y-other.y, self.z-other.z)
        else:
            raise TypeError("Unable to broadcast together types Quaternion and %r." %type(other))

    # For multiplication, python 3.5+ supports @ as the matrix multiplication operator.
    # Improvements in readability may be gained by replacing .dot functions with @.

    # It is not (currently) in __future__, so from __future__ import does not allow for
    # use with python versions < 3.5.

    # No non-stylistic improvements, it breaks backwards compatibility and more than 2
    # matrices are never multiplied here therefore it is probably not currently worth
    # implementing.

    def __mul__(self, other):
        # Allow for right multiplication by scalars, matrices and quaternions
        if isinstance(other, Quaternion):
            x = self.x * other.w + self.y * other.z - self.z * other.y + self.w * other.x
            y = -self.x *other.z + self.y * other.w + self.z * other.x + self.w * other.y
            z = self.x * other.y - self.y * other.x + self.z * other.w + self.w * other.z
            w = -self.x *other.x - self.y * other.y - self.z * other.z + self.w * other.w
            return Quaternion(w,x,y,z)
        elif isinstance(other, np.ndarray):
            if other.shape[0] == 4:
                return np.array([self.w,self.x,self.y,self.z]).dot(other)
            else:
                raise ValueError("Operand with shape (%r,%r) could not be broadcast with a quaternion." % other.shape)
        elif isinstance(other, (int, float)):
            return Quaternion(self.w*other,
                              self.x*other,
                              self.y*other,
                              self.z*other)
        else:
            raise TypeError("Multiplication of quaternion with %r is not supported." % type(other))

    def __rmul__(self,other):
        # Allow for left multiplication by matrices and scalars (quaternion right multiplication handled above)
        if isinstance(other, np.ndarray):
            if other.shape[1] == 4:
                return other.dot(np.array([self.w,self.x,self.y,self.z]))
            else:
                raise ValueError("Operand with shape (%r,%r) could not be broadcast with a quaternion." % other.shape)
        elif isinstance(other, (int, float)):
            return Quaternion(self.w*other,
                              self.x*other,
                              self.y*other,
                              self.z*other)
        else:
            raise TypeError("Multiplication of quaternion with %r is not supported." %type(other))

    def __truediv__(self, other):
        # Allow for division by scalars and quaternions: division by other types is undefined
        if isinstance(other, Quaternion):
            mag_2 = other.magnitude**2
            w = (other.w*self.w + other.x*self.x + other.y*self.y + other.z*self.z)/mag_2
            x = (other.w*self.x - other.x*self.w - other.y*self.z + other.z*self.y)/mag_2
            y = (other.w*self.y + other.x*self.z - other.y*self.w - other.z*self.x)/mag_2
            z = (other.w*self.z - other.x*self.y + other.y*self.x - other.z*self.w)/mag_2
            return Quaternion(w,x,y,z)
        elif isinstance(other, (int, float)):
            return Quaternion(self.w/other,
                              self.x/other,
                              self.y/other,
                              self.z/other)
        else:

            raise TypeError("Division of quaternion by given types is not supported.")

    def to_vector(self):
        return np.array([self.x, self.y, self.z])

    def basis(self):
        a_11 = 1 - 2*((self.y)**2 + (self.z)**2)
        a_12 = 2*(self.x*self.y + self.z*self.w)
        a_13 = 2*(self.x*self.z-self.y*self.w)
        a_21 = 2*(self.x*self.y - self.z*self.w)
        a_22 = 1 - 2*((self.x)**2 + (self.z)**2)
        a_23 = 2*(self.y*self.z + self.x*self.w)
        a_31 = 2*(self.x*self.z + self.y*self.w)
        a_32 = 2*(self.y*self.z - self.x*self.w)
        a_33 =1 - 2*((self.x)**2 + (self.y)**2)

        A = np.array([a_11, a_12, a_13, a_21, a_22, a_23, a_31, a_32, a_33])

        return A.reshape(3,3)

    __array_priority__ = 10000  # big number so numpy respects left matrix multiplication with quaternions


import numpy as np

def EaseInSine(t):
    return 1-np.sin((1-t)*np.pi/2)

def EaseOutSine(t):
    return np.sin(t*np.pi/2)

def EaseInOutSine(t):
    return 0.5-0.5*np.sin((1-t*2)*np.pi/2)

def EaseInQuad(t):
    return t**2

def EaseOutQuad(t):
    return 1-EaseInQuad(1-t)

def EaseInOutQuad(t):
    return (t<0.5) * 0.5 * EaseInQuad(t * 2) + (t>=0.5) * 0.5 * (EaseOutQuad((t-0.5)*2) + 1)

def EaseInCubic(t):
    return t**3

def EaseOutCubic(t):
    return 1-EaseInCubic(1-t)

def EaseInOutCubic(t):
    return (t<0.5) * 0.5 * EaseInCubic(t * 2) + (t>=0.5) * 0.5 * (EaseOutCubic((t-0.5)*2) + 1)

def EaseInQuart(t):
    return t**4

def EaseOutQuart(t):
    return 1-EaseInQuart(1-t)

def EaseInOutQuart(t):
    return (t<0.5) * 0.5 * EaseInQuart(t * 2) + (t>=0.5) * 0.5 * (EaseOutQuart((t-0.5)*2) + 1)

def EaseInQuint(t):
    return t**5

def EaseOutQuint(t):
    return 1-EaseInQuint(1-t)

def EaseInOutQuint(t):
    return (t<0.5) * 0.5 * EaseInQuint(t * 2) + (t>=0.5) * 0.5 * (EaseOutQuint((t-0.5)*2) + 1)

def EaseInExpo(t):
    return (2**(t*10)-1)/1023

def EaseOutExpo(t):
    return 1-EaseInExpo(1-t)

def EaseInOutExpo(t):
    return (t<0.5) * 0.5 * EaseInExpo(t * 2) + (t>=0.5) * 0.5 * (EaseOutExpo((t-0.5)*2) + 1)

def EaseInCirc(t):
    return 1-np.sqrt(1-t**2)

def EaseOutCirc(t):
    return 1-EaseInCirc(1-t)

def EaseInOutCirc(t):
    return (t<0.5) * 0.5 * EaseInCirc((t<0.5) * t * 2) + (t>=0.5) * 0.5 * (EaseOutCirc((t>=0.5) * (t-0.5)*2) + 1)

def EaseInBack(t):
    return t**2 * (np.e*t-np.e+1)

def EaseOutBack(t):
    return 1-EaseInBack(1-t)

def EaseInOutBack(t):
    return (t<0.5) * 0.5 * EaseInBack(t * 2) + (t>=0.5) * 0.5 * (EaseOutBack((t-0.5)*2) + 1)

def EaseInElastic(t):
    return EaseInExpo(t) * np.sin(t * np.pi * 6.5) / np.sin(np.pi * 6.5)

def EaseOutElastic(t):
    return 1-EaseInElastic(1-t)

def EaseInOutElastic(t):
    v1 = (t<0.46503) * 0.5 * EaseInElastic(t * 2)
    v2 = (t>0.53497) * 0.5 * (EaseOutElastic((t-0.5)*2) + 1)
    # 确保一阶导数连续
    v3 = ((0.46503<=t) & (t<=0.53497)) * (13.047404544869156 * t - 6.023702272434578)
    return v1+v2+v3

def EaseInBounce(t):
    def _poly(_t):
        return 1-((_t-0.5)*2)**2
    v1 = (t<0.125) * _poly(t/0.125) * 0.015625
    v2 = ((0.125<=t) & (t<0.375)) * _poly((t-0.125)/0.25) * 0.0625
    v3 = ((0.375<=t) & (t<0.750)) * _poly((t-0.375)/0.375) * 0.25
    v4 = (0.750<=t) * _poly((t-0.75)/0.5) * 1
    return v1+v2+v3+v4

def EaseOutBounce(t):
    return 1-EaseInBounce(1-t)

def EaseInOutBounce(t):
    return (t<0.5) * 0.5 * EaseInBounce(t * 2) + (t>=0.5) * 0.5 * (EaseOutBounce((t-0.5)*2) + 1)


import numpy as np

'''
Dynamic Time Warping

A dynamic programming approach to measuring the distance between two
temporal sequences. Ideal for sequences that may vary in speed or in phase.
'''

def distance(x, y):
    ''' Generic euclidean distance function '''
    return np.sqrt(np.sum((x - y)**2))

def dtw(a, b):
    '''
    Returns the DTW distance between sequences `a` and `b`.

    Parameters:
    -----------
    a : numpy array
    b : numpy array

    Returns:
    --------
    dist : float
    The distance between sequences `a` and `b`
    '''
    # Ensure inputs are np arrays
    a = np.asarray(a)
    b = np.asarray(b)

    # dtw_table[i,j] is the dtw distance up to and including a[i-1] and b[j-1]
    dtw_table = np.empty((a.size+1, b.size+1))
    # Initialize first row and column
    dtw_table[:, 0] = np.inf
    dtw_table[0, :] = np.inf
    dtw_table[0, 0] = 0.

    for i in xrange(1, a.size+1):
        for j in xrange(1, b.size+1):
            cost = distance(a[i-1], b[j-1])
            dtw_table[i, j] = cost + min(dtw_table[i-1, j],
                                         dtw_table[i, j-1],
                                         dtw_table[i-1, j-1])

    return dtw_table[a.size, b.size], dtw_table

def display_grid(grid):
    import matplotlib.pyplot as plt
    fig = plt.figure()
    ax = fig.add_subplot(111)
    im = ax.imshow(grid, interpolation='None')
    for (j, i), label in np.ndenumerate(grid):
        ax.text(i, j, label, ha='center', va='center')
    plt.show()

def main():
    np.set_printoptions(suppress=True, precision=1)
    a = np.array([0, 5, 8, 9, 3, 0])
    b = np.array([5, 8, 9, 3])

    distance, table = dtw(a, b)
    print distance
    display_grid(table)

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Wed Jul  4 20:08:46 2018

@author: tapiw
"""

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

# =========================
#Importing the dataset
dataset = pd.read_csv('Salary_Data.csv')

# See what the data looks like
# dataset.plot()

X = dataset.iloc[:, :-1].values
y = dataset.iloc[:, 1].values

# =========================
#Splitting the data set into training set and test set
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 1/3, random_state = 0)

# =========================
# Fit the data to a linear model

from sklearn.linear_model import LinearRegression

regressor = LinearRegression()
regressor.fit(X_train, y_train)

# =========================
# Predict the Test results

# Vector of predictions using the training model
y_pred = regressor.predict(X_test)

# =========================
# Visualize the training set results
plt.scatter(X_train, y_train, color = 'red')
plt.plot(X_train, regressor.predict(X_train), color='blue' )
plt.title('Salary vs Experience (Training set)')
plt.xlabel('Years of Experience')
plt.ylabel('Salary')
plt.show()

# =========================
# Visualize the test set results
plt.scatter(X_test, y_test, color = 'red')
# plt.plot(X_test, y_pred, color='blue' ) -> Model the same whether we use test or train set
plt.plot(X_train, regressor.predict(X_train), color='blue' )
plt.title('Salary vs Experience (Test set)')
plt.xlabel('Years of Experience')
plt.ylabel('Salary')
plt.show()


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from numpy.random import default_rng
rng = default_rng(12345)
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, roc_curve

df = pd.DataFrame({
    "var1": np.concatenate([rng.normal(3.0, 1.5, size=50), rng.normal(-4.0, 2.0, size=50)]),
    "var2": rng.uniform(size=100),
    "var3": np.concatenate([rng.normal(-2.0, 2.0, size=50), rng.normal(1.5, 0.8, size=50)])
})

score = 4.0 + df["var1"] - df["var3"]
Y = score >= 0

fig1, ax1 = plt.subplots()
ax1.plot(df.loc[Y, "var1"], df.loc[Y, "var3"], "bo", label="True data")
ax1.plot(df.loc[~Y, "var1"], df.loc[~Y, "var3"], "rx", label="False data")
ax1.legend()
ax1.set_xlabel("var1")
ax1.set_ylabel("var3")
ax1.set_title("Scatter plot of var3 against var1")

plt.show()

model = LogisticRegression()
model.fit(df, Y)

test_df = pd.DataFrame({
    "var1": np.concatenate([rng.normal(3.0, 1.5, size=25), rng.normal(-4.0, 2.0, size=25)]),
    "var2": rng.uniform(size=50),
    "var3": np.concatenate([rng.normal(-2.0, 2.0, size=25), rng.normal(1.5, 0.8, size=25)])
})

test_scores = 4.0 + test_df["var1"] - test_df["var3"]
test_Y = test_scores >= 0

test_predicts = model.predict(test_df)

plt.show()

print(classification_report(test_Y, test_predicts))


import numpy as np
from copy import deepcopy
import pandas as pd

def MP(ft, D, tol=1e-4):
    """
    This is an implementation of matching pursuit (MP) algorithm
    Input: Signal: f(t), dictionary D with normalized columns g_i
    Output: List if coefficients a_n and indices for corresponding atoms gamma_n
    Initialization:
        R1 = f(t)
        n = 1
    Repeat:
        Find g_{gamma_n} with maximum inner product <R_n, g_{gamma_n}>
        a_n = <R_n, g_{gamma_n}>
        R_{n + 1} = R_n - a_n * g_{gamma_n}
        n = n + 1
    Util stop condition (e.g., ||R_n|| < threshold)
    return
    """
    R = deepcopy(ft)
    a = []
    gamma = []
    D = D / np.linalg.norm(D, axis=0)

    while np.dot(R, R) > tol:
        inner_prod = np.dot(R, D)
        gammai = np.argmax(np.abs(inner_prod))
        ai = inner_prod[gammai]
        R -= ai * D[:, gammai]
        a.append(ai)
        gamma.append(gammai)

    a = np.asarray(a)
    gamma = np.asarray(gamma)

    return a, gamma

if __name__ == "__main__":
    v = np.random.random(8)
    print('The original: ', v)
    # D = np.random.random((8, 12))
    D = np.random.randn(8, 12)
    a, gamma = MP(v, D)

    # print(a, gamma)
    df = pd.DataFrame([a, gamma]).T
    res = df.groupby(1).sum()
    D /= np.linalg.norm(D, axis=0)
    vn = np.sum(a * D[:, gamma], axis=1)
    print('The approximation: ', vn)
    # vnn = np.sum(res.values.T * D[:, res.index.values.astype(int)], axis=1)
    # print('The approximation: ', vnn)


import numpy as np
from scipy.special import wofz

def gaussian(x, x0, sigma):
    return np.exp(-np.power(x - x0, 2.) / (2 * np.power(sigma, 2.)))

def lorentzian(x, x0, gamma):
    return 1 / (1 + np.power((x - x0) / gamma, 2))

def voigt(x, x0, sigma, gamma):
    if sigma == 0:
        return lorentzian(x, x0, gamma)
    if gamma == 0:
        return gaussian(x, x0, sigma)
    return np.real(wofz(((x - x0) + 1j * gamma) / sigma / np.sqrt(2)))

def voigt_fwhm(g_width, l_width):
    """
    Accurate to 0.0003% for a pure Lorentzian profile, precise for a pure Gaussian.

    Source: https://en.wikipedia.org/wiki/Voigt_profile.
    """
    return 0.5346 * l_width + np.sqrt(0.2166 * l_width**2 + g_width**2)


import numpy as np

def mse(true, predict):
    """Mean Square Error"""
    return np.mean((true-predict)**2)

def rmse(true, predict):
    """Root Mean Square Error"""
    return np.sqrt(mse(true, predict))

def mape(true, predict):
    """Mean Absolute Percentage Error"""
    return np.mean(np.abs(true - predict) / (1 + np.abs(true))) * 100.0


import numpy as np
import matplotlib

matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

np.random.seed(1)

def generate_data(sample_size):
    a = np.linspace(0, 4 * np.pi, num=sample_size // 2)
    x = np.concatenate(
        [np.stack([a * np.cos(a), a * np.sin(a)], axis=1),
         np.stack([(a + np.pi) * np.cos(a), (a + np.pi) * np.sin(a)], axis=1)])
    x += np.random.random(size=x.shape)
    y = np.concatenate([np.ones(sample_size // 2), -np.ones(sample_size // 2)])
    return x, y

def build_design_mat(x1, x2, bandwidth):
    return np.exp(
        -np.sum((x1[:, None] - x2[None]) ** 2, axis=-1) / (2 * bandwidth ** 2))

def optimize_param(design_mat, y, regularizer, lr): #lr: learning rate
# implement here
    theta = np.random.rand(len(y))
    while(1):
        f = design_mat.dot(theta)
        delf = np.array(np.zeros(len(y)))
        for i in range(len(f)) :
            delfi = np.array([])
            fi = f[i]
            yi = y[i]
            for j in range(len(f)) :
                if(1 - yi*fi > 0) :
                    delfi = np.append(delfi,- yi * design_mat[i][j])
                else :
                    delfi = np.append(delfi, 0)
            delf = delf + delfi
        if (np.linalg.norm(delfi, ord=2) < 0.00001) :
            break
        print("a")
        theta = theta - lr * (regularizer * delf + 2 * design_mat.dot(theta))
    return theta

def visualize(theta, x, y, grid_size=100, x_min=-16, x_max=16):
    grid = np.linspace(x_min, x_max, grid_size)
    X, Y = np.meshgrid(grid, grid)
    mesh_grid = np.stack([np.ravel(X), np.ravel(Y)], axis=1)
    design_mat = build_design_mat(x, mesh_grid, bandwidth=1.)
    plt.clf()
    plt.figure(figsize=(6, 6))
    plt.xlim(x_min, x_max)
    plt.ylim(x_min, x_max)
    plt.contourf(X, Y, np.reshape(np.sign(design_mat.T.dot(theta)),
                                  (grid_size, grid_size)),
                 alpha=.4, cmap=plt.cm.coolwarm)
    plt.scatter(x[y == 1][:, 0], x[y == 1][:, 1], marker='$O$', c='red')
    plt.scatter(x[y == -1][:, 0], x[y == -1][:, 1], marker='x', c='blue')
    plt.savefig('ML1-homework2.png')

#x, y = generate_data(sample_size=200)
x, y = generate_data(sample_size=200)
design_mat = build_design_mat(x, x, bandwidth=1.)
theta = optimize_param(design_mat, y, regularizer=1, lr=0.0001)
#theta = optimize_param(design_mat, y, regularizer=1, lr=0.01)

visualize(theta, x, y)


from app.utils.methods import BaseMethod
import numpy as np

class SimpleLU(BaseMethod):
    def __init__(self, A, b, n):
        self.A = np.array(A).reshape(n, n)
        self.b = np.array(b).reshape(len(b), 1)
        self.n = n

    def proggresive_subst(self, M):
        n = len(M)
        x = np.zeros([n, 1])

        x[0] = M[0, n] / M[0, 0]
        array = [[1]]
        for i in range(1, n):
            aux = np.concatenate((array, np.transpose(x[0:i])), axis=1)
            array_aux = [M[i - 1, n]]
            aux_ = np.concatenate((array_aux, -M[i, 0:i]), axis=0)
            x[i] = np.dot(aux, aux_) / M[i, i]
        return x

    def back_subst(self, M):
        n = len(M)
        x = np.ones([n, 1])
        for i in range(n - 1, -1, -1):
            value = 0
            for j in range(i + 1, n):
                value += M[i, j] * x[j]
            x[i] = (M[i, n] - value) / M[i, i]
        return x

    def run(self):
        det = np.linalg.det(self.A)
        if det == 0:
            return {"Error": "Matrix must be nonsingular."}
        self.n = np.size(self.A, 1)
        L = np.eye(self.n)
        U = np.zeros((self.n, self.n))
        M = self.A
        result = {"iterations": [{"stage": 0, "M": list(map(lambda x: list(x), M))}]}
        for i in range(0, self.n - 1):
            for j in range(i + 1, self.n):
                if M[j, i] != 0:
                    L[j, i] = np.divide(M[j, i], M[i, i])
                    M[j, i:self.n] = np.subtract(M[j, i:self.n], (np.divide(M[j, i], M[i, i])) * M[i, i:self.n])
                U[i, i:self.n] = M[i, i:self.n]
                U[i + 1, i + 1:self.n] = M[i + 1, i + 1:self.n]
                result['iterations'].append({
                    "stage": i + 1,
                    "M": list(map(lambda x: list(x), M)),
                    "L": list(map(lambda x: list(x), L)),
                    "U": list(map(lambda x: list(x), U))
                })
        MLB = np.concatenate((L, self.b), axis=1)
        z = self.proggresive_subst(MLB)
        MUZ = np.concatenate((U, z), axis=1)
        result['result'] = list(map(lambda x: x[0], self.back_subst(MUZ)))
        return result


import numpy as np

x = np.array([[1,2],[3,4]], dtype=np.float64)
y = np.array([[5,6],[7,8]], dtype=np.float64)

# [[ 6.0  8.0]
#  [10.0 12.0]]
print x + y

# [[-4.0 -4.0]
#  [-4.0 -4.0]]
print x - y

# [[ 5.0 12.0]
#  [21.0 32.0]]
print x * y

# [[ 0.2         0.33333333]
#  [ 0.42857143  0.5       ]]
print x / y

# [[ 1.          1.41421356]
#  [ 1.73205081  2.        ]]
print np.sqrt(x)


"""
Simple Metropolis Hastings sampler taken from Hogg and Foreman-MacKey(2018):

  From Problem 2:
  Sample in a single parameter x and give the sampler
  as its density function p(x) a Gaussian density with
  mean 2 and variance 2. Make the proposal distribution
  q (x'∣x ) a Gaussian pdf for x′ with mean x and
  variance 1. Initialize the sampler with x = 0 and run
  the sampler for more than 10000 steps. Plot the results
  as a histogram with the true density overplotted sensibly.

"""

import probayes as pb
import numpy as np
import scipy.stats
from pylab import *; ion()

n_steps = 12288

def q(**kwds):
  x, xprime = kwds['x'], kwds["x'"]
  return scipy.stats.norm.pdf(xprime, loc=x, scale=1.)

x = pb.RV('x', vtype=float, vset=(-np.inf, np.inf))
process = pb.SP(x)
process.set_prob(scipy.stats.norm.pdf, loc=2, scale=np.sqrt(2),
                 order={'x': 0})
process.set_tran(q)
lambda_delta = lambda : process.Delta(x=scipy.stats.norm.rvs(loc=0., scale=1.))
process.set_delta(lambda_delta)
process.set_scores('hastings')
process.set_update('metropolis')
sampler = process.sampler({'x': 0.}, stop=n_steps)
samples = [sample for sample in sampler]
summary = process(samples)
xvals = summary.v['x']

figure()
xbins = np.linspace(np.min(xvals), np.max(xvals), 128)
xhist, _ = np.histogram(xvals, xbins)
xprop = xhist / (n_steps * (xbins[1]-xbins[0]))
step(xbins[:-1], xprop, 'b')
norm_x = scipy.stats.norm.pdf(xbins, loc=2, scale=np.sqrt(2))
plot(xbins, norm_x, 'r')


#taken from https://machinelearningmastery.com/statistical-hypothesis-tests-in-python-cheat-sheet/
#This section lists statistical tests that you can use to compare data samples.
import numpy as np

from scipy.stats import ttest_ind
def student(data1, data2):
    """Tests whether the means of two independent samples are significantly different.
    Assumptions
    -----------
    * Observations in each sample are independent and identically distributed (iid).
    * Observations in each sample are normally distributed.
    * Observations in each sample have the same variance.
    Interpretation
    --------------
    * H0: the means of the samples are equal.
    * H1: the means of the samples are unequal.

    Parameters
    ----------
    data1, data2: array_like
        The arrays must have the same shape.

    Returns
    -------
    stat : float or array
        The calculated t-statistic.
    p : float or array
        The two-tailed p-value.
    """
    stat, p = ttest_ind(data1, data2)
    return stat, p

from scipy.stats import ttest_rel
def paired_student(data1, data2):
    """Tests whether the means of two paired samples are significantly different.
    Assumptions
    -----------
    * Observations in each sample are independent and identically distributed (iid).
    * Observations in each sample are normally distributed.
    * Observations in each sample have the same variance.
    * Observations across each sample are paired.
    Interpretation
    --------------
    * H0: the means of the samples are equal.
    * H1: the means of the samples are unequal.

    Parameters
    ----------
    data1, data2: array_like
        The arrays must have the same shape.

    Returns
    -------
    stat : float or array
        t-statistic
    p : float or array
        two-tailed p-value
    """
    stat, p = ttest_rel(data1, data2)
    return stat, p

from scipy.stats import f_oneway
def ANOVA(data1, data2):
    """Analysis of Variance Test (ANOVA)
    Tests whether the means of two or more independent samples are significantly different.
    Assumptions
    -----------
    * Observations in each sample are independent and identically distributed (iid).
    * Observations in each sample are normally distributed.
    * Observations in each sample have the same variance.
    Interpretation
    --------------
    * H0: the means of the samples are equal.
    * H1: one or more of the means of the samples are unequal.

    Parameters
    ----------
    data1, data2: array_like
        The sample measurements for each group (must have the same shape).

    Returns
    -------
    stat : float
        The computed F-value of the test.
    p : float
        The associated p-value from the F-distribution.
    """
    stat, p = f_oneway(data1, data2)#, ...)
    return stat, p


'''
Author: Edward J. C. Ashenbert
'''
import numpy as np
import math
from scipy import linalg
from string import Template

class AlgebraEngine:
    def __init__(self):
        pass

    @staticmethod
    def transpose(input_matrix):
        try:
            assert (isinstance(input_matrix, np.ndarray))
            return input_matrix.transpose()
        except:
            print("Input is not a numpy array!")

    @staticmethod
    def get_singular_value(input_matrix):
        try:
            assert (isinstance(input_matrix, np.ndarray))
            singular_values = []
            if input_matrix.shape[0] == input_matrix.shape[1]:
                for eigen_value in np.linalg.eigvals(input_matrix):
                    if eigen_value != 0:
                        singular_values.append(math.sqrt(eigen_value))
            else:
                for eigen_value in np.linalg.eigvals(input_matrix.dot(input_matrix.transpose())):
                    if eigen_value != 0:
                        singular_values.append(math.sqrt(eigen_value))
            return singular_values
        except:
            print("Input is not a numpy array!")

    @staticmethod
    def solve_ricatti(A, B, Q, R):
        return linalg.solve_discrete_are(A, B, Q, R)

    @staticmethod
    def solve_lyapunov_equation(A, Q):
        return linalg.solve_lyapunov(A, Q)

    @staticmethod
    def solve_sylvester_equation(A, B, C):
        '''
        Bartels–Stewart algorithm
        :param A:
        :param Q:
        :return:
        '''
        return linalg.solve_sylvester(A, B, C)

    @staticmethod
    def kronecker_product(A, B):
        return np.kron(A,B)

    @staticmethod
    def schur_decomposition(A):
        # return
        return np.kron(A, B)

    def __repr__(self):

        print("This engine is to implement transparently algebra notation require for control theory")

if __name__ == "__main__":
    al = AlgebraEngine()

    a = np.zeros((2, 3))
    a[0][0] = 1
    a[0][1] = 2
    a[0][2] = 3
    a[1][0] = 4
    a[1][1] = 5
    a[1][2] = 6

    print(AlgebraEngine.get_singular_value(a))

    a = np.array([[0, 1], [0, -1]])
    b = np.array([[1, 0], [2, 1]])
    print(AlgebraEngine.kronecker_product(a,b))
    q = np.array([[-4, -4], [-4, 7]])
    r = np.array([[9, 3], [3, 1]])

    print(AlgebraEngine.solve_ricatti(a, b, q, r))
import control

control.care()

# print(a.shape)
# print(AlgebraEngine.transpose(a))
# print(type(a))