import numpy as np

f_src=open('nodes_list.txt','r')

# imported node information
nodes=[]	# info: [{'node':'x', 'x':pos_x, 'z':pos_z}]
len_nodes=0	# amount

while(True):
  linetmp=f_src.readline()
  if(not linetmp):
    break
  line=linetmp.split('\t')
  if(len(line)!=3):
    break
  nodes.append({'node':line[0],'x':int(line[1]),'z':int(line[2])})
len_nodes=len(nodes)

f_src.close()

print("Distance table:")
# n*n distance table: length=dist_table_full[index1][index2]
dist_table_full=np.full((len_nodes, len_nodes), -1, dtype=int)
# n*(n-1)/2 E dict: {'node1':index1, 'node2':index2, 'length':len}
dist_table_dict=[]

print("#\t",end ="")
for i in range(0,len_nodes):
  if(i==len_nodes-1):
    print(nodes[i]['node'],end ="\n")
  else:
    print(nodes[i]['node'],end ="\t")

for i in range(0,len_nodes):
  print(nodes[i]['node'],end ="\t")
  for j in range(0,len_nodes):
    dist_table_full[i][j]=int(pow(pow(nodes[i]['x']-nodes[j]['x'],2)+pow(nodes[i]['z']-nodes[j]['z'],2),1/2))
    if(i < j):
      dist_table_dict.append({'node1':i, 'node2':j, 'length':dist_table_full[i][j]})
    if(j==len_nodes-1):
      print(dist_table_full[i][j],end ="\n")
    else:
      print(dist_table_full[i][j],end ="\t")

print()

## MST: Prim
# G = (V=nodes, E=dist_table_full)
prim_V_a=[]	# target nodes collection
prim_V_b=[]	# source nodes collection
prim_path=[]	# generated path: {'node1':index, 'node2':index}
sum_length=0

for i in range(0,len_nodes):
  prim_V_b.append({'index':i})

prim_V_a.append(prim_V_b.pop())	# init node

while(prim_V_b):
  node_b=prim_V_b.pop()

  connectedto_a_index=-1	# which a should b connected to
  min_length=-1	# total minimum length
  for node_a in prim_V_a:
    target_len=dist_table_full[node_a['index']][node_b['index']]
    if(target_len == -1):
      continue	# skip
    if(min_length==-1):
      min_length=target_len
      connectedto_a_index=node_a['index']
    else:
      if(min_length > target_len):
        min_length = target_len
        connectedto_a_index=node_a['index']
  if(connectedto_a_index == -1):
    print("MST failed for node %d %s : no path valid"%(node_b['index'],nodes[node_b['index']]['node']))
  else:
    sum_length=sum_length+dist_table_full[connectedto_a_index][node_b['index']]
    prim_V_a.append(node_b)
    prim_path.append({'node1':min(connectedto_a_index,node_b['index']), 'node2':max(connectedto_a_index,node_b['index'])})
# MST length table
conn_table_mst=np.full((len(prim_V_a), len(prim_V_a)), 0, dtype=int)
for gen_path in prim_path:
  conn_table_mst[gen_path['node1']][gen_path['node2']]=dist_table_full[gen_path['node1']][gen_path['node2']]
  conn_table_mst[gen_path['node2']][gen_path['node1']]=dist_table_full[gen_path['node2']][gen_path['node1']]
# print table
print("MST table:")
print("#\t",end ="")
for i in range(0,len(prim_V_a)):
  if(i==len(prim_V_a)-1):
    print(nodes[prim_V_a[i]['index']]['node'],end ="\n")
  else:
    print(nodes[prim_V_a[i]['index']]['node'],end ="\t")

for i in range(0,len(prim_V_a)):
  print(nodes[prim_V_a[i]['index']]['node'],end ="\t")
  for j in range(0,len(prim_V_a)):
    if(j==len(prim_V_a)-1):
      print(conn_table_mst[prim_V_a[i]['index']][prim_V_a[j]['index']],end ="\n")
    else:
      print(conn_table_mst[prim_V_a[i]['index']][prim_V_a[j]['index']],end ="\t")
print("Total length: %d"%sum_length)
## MST: Prim

print()

print("MST paths:")

print("From\tTo\tLength")
for path in prim_path:
  print("%s\t%s\t%d"%(nodes[path['node1']]['node'],nodes[path['node2']]['node'],conn_table_mst[path['node1']][path['node2']]))
  print("%s\t%s\t%d"%(nodes[path['node2']]['node'],nodes[path['node1']]['node'],conn_table_mst[path['node2']][path['node1']]))


# The body mass index (BMI) is a number that can be calculated for any individual by using the equation
# BMI = 703w / h**2
# where weight (w) is in pounds and height (h) is in inches.  Use this information to complete the following.

from sympy import symbols, solve, pprint, Derivative
import math

# rounding

dec_places = 0

# input variables

weight = 219
height = 6.2
target_bmi = 24.9

height = int( height ) * 12 + ( height % 1 * 10 )

w, h = symbols( 'w, h' )

fBMI = 703*w / h**2

w_bmi = fBMI.subs( { h: height } )
bmi = w_bmi.subs( { w: weight } )

print( 'raw bmi: {0}'.format( bmi ) )

target_weight = solve( w_bmi - target_bmi, w )[ 0 ]

# a
# Calculate the BMI for a person who weighs 219 pounds and is 6 prime 2 double prime tall.
print( 'A person that weighs {0}lbs and is {1}in tall has a BMI of {2}'.format( weight, height, round( bmi, dec_places ) ) )
# b
# How much weight would the person in part a have to lose to reach a BMI of 24.9?
print( 'They would need to loose {0}lbs to reach a BMI of {1}'.format( round( weight - target_weight, dec_places ), target_bmi ) )

# c
# For a 126-lb. female, what is the rate of change of BMI with respect to height?

weight = 126
dH = Derivative( fBMI.subs( { w: weight } ) ).doit()
pprint( dH )

# d
# Calculate f'( 66 ).
height = 66
rate_of_change = float( dH.subs( { h: height } ) )

print( 'rate of change for female {0}'.format( round( rate_of_change, 2 ) ) )


import numpy as np
# from scipy import stats, optimize

def PolyBaseFunction(x, dims):
    return np.array([x**d for d in range(dims+1)]).T

class LinearRegression:
    def __init__(self, y, Phi):
        self.y = np.array(y).reshape((-1, 1))
        self.Phi = np.array(Phi)

        self.N, self.D = self.Phi.shape

    def fit_OLS(self):
        self.beta_OLS \
            = np.linalg.pinv(self.Phi.T @ self.Phi) @ self.Phi.T @ self.y
        diff = self.y - self.predict_OLS(self.Phi)
        self._loss_OLS = float(diff.T @ diff)

    def predict_OLS(self, Phi):
        return Phi @ self.beta_OLS

    def fit_ML(self):
        self.fit_OLS()
        self.beta_ML = self.beta_OLS
        self.sigma2_ML = self._loss_OLS / self.N
        self._loglik_ML = -0.5*(self._loss_OLS / self.sigma2_ML
                                + self.N * np.log(2*np.pi)
                                + self.N * np.log(self.sigma2_ML))

    def predict_ML(self, Phi):
        return self.predict_OLS(Phi)

    def fit_Ridge(self, alpha):
        self.alpha_Ridge = float(alpha)
        inversed = np.linalg.pinv(self.Phi.T @ self.Phi
                                  + self.alpha_Ridge*np.eye(self.D))
        self.beta_Ridge \
            = inversed @ self.Phi.T @ self.y
        diff = self.y - self.predict_Ridge(self.Phi)
        self._loss_Ridge = float(diff.T @ diff) \
            + self.alpha_Ridge * self._penalty_Ridge(self.beta_Ridge)

    def predict_Ridge(self, Phi):
        return Phi @ self.beta_Ridge

    def _penalty_Ridge(self, beta):
        return float(beta.T @ beta)

    def fit_Bayes(self, sigma2, prior_cov):
        self.sigma2_Bayes = float(sigma2)
        self.prior_cov_Bayes = np.array(prior_cov).reshape((self.D, self.D))
        self._prior_prec_Bayes = np.linalg.pinv(self.prior_cov_Bayes)

        self._beta_prec_Bayes \
            = self._prior_prec_Bayes \
            + self.Phi.T @ self.Phi / self.sigma2_Bayes
        self.beta_cov_Bayes = np.linalg.pinv(self._beta_prec_Bayes)
        self.beta_mean_Bayes = \
            self.beta_cov_Bayes @ self.Phi.T @ self.y / self.sigma2_Bayes

    def predict_Bayes(self, Phi):
        N = len(Phi)

        mu = (Phi @ self.beta_mean_Bayes).flatten()
        sigma2 = np.empty(N)
        for n in range(N):
            sigma2[n] = 1 / self.sigma2_Bayes \
                + Phi[n].T @ self.beta_cov_Bayes @ Phi[n]

        return mu, sigma2


import numpy as np

class _Distances:

    def __init__(self):
        pass

    def euclidian(self, X_train, input_X):
        """Compute the ecuclidean distance between X_train and input_X
        """
        differences_matrix = np.tile(input_X, (X_train.shape[0], 1)) - X_train
        squared_differences_matrix = differences_matrix**2
        squared_distances_matrix = squared_differences_matrix.sum(axis=1)
        return squared_distances_matrix ** 0.5

    def manhattan(self, X_train, input_X):
        """Compute the manhattan distance between X_train and input_X
        """
        differences_matrix = np.tile(input_X, (X_train.shape[0], 1)) - X_train
        differences_matrix = np.abs(differences_matrix)
        distances_matrix = differences_matrix.sum(axis=1)
        return distances_matrix

    def canberra(self, X_train, input_X):
        """Compute the canberra distance between X_train and input_X
        """
        differences_matrix = np.tile(input_X, (X_train.shape[0], 1)) - X_train
        differences_matrix = np.abs(differences_matrix)
        summations_matrix = np.tile(input_X, (X_train.shape[0], 1)) + X_train
        summations_matrix = np.abs(summations_matrix)
        divisions_matrix = differences_matrix / summations_matrix
        distances_matrix = divisions_matrix.sum(axis=1)
        return distances_matrix

    def chebychev(self, X_train, input_X):
        """Compute the chebychev distance between X_train and input_X
        """
        distances_matrix = np.tile(input_X, (X_train.shape[0], 1)) - X_train
        distances_matrix = np.abs(distances_matrix)
        distances_matrix = distances_matrix.max(axis=1)
        return distances_matrix


import numpy
import matplotlib
from matplotlib import pyplot
from scipy.optimize import fsolve

matplotlib.rcParams.update({'font.size':18, 'figure.figsize':(10,6)})

def relax_dirichlet(p, q, f, interval, bcs, N):
    x, dx = numpy.linspace(interval[0], interval[1], N+2, retstep=True)
    x_interior = x[1:-1]
    A = numpy.zeros((N,N))
    F = numpy.zeros((N,))
    y = numpy.zeros((N+2,))
    for i in range(N):
        A[i,i] = dx**2 * q(x_interior[i]) - 2
        if i>0:
            A[i,i-1] = 1 - dx/2 * p(x_interior[i])
        if i<N-1:
            A[i,i+1] = 1 + dx/2 * p(x_interior[i])
        F[i] = dx**2 * f(x_interior[i])
    F[0] = F[0] - bcs[0] * (1 - dx/2 * p(x_interior[0]))
    F[-1] = F[-1] - bcs[-1] * (1 + dx/2 * p(x_interior[-1]))
    y_interior = numpy.linalg.solve(A, F)
    y[0] = bcs[0]
    y[1:-1] = y_interior
    y[-1] = bcs[-1]

    return x, y

def relax_blackbox(f, bcs, N):
    x, dx = numpy.linspace(0, 1, N+2, retstep=True)
    def residual(y):
        y[0] = bcs[0]
        y[-1] = bcs[-1]
        dy = (y[2:] - y[:-2]) / (2*dx)
        res = numpy.zeros_like(y)
        res[1:-1] = y[:-2] + y[2:] - 2*y[1:-1] - dx**2 * f(x[1:-1], y[1:-1], dy)
        return res
    y_initial = numpy.zeros_like(x)
    y = fsolve(residual, y_initial)
    return x, y

def relax_newton(f, dfdy, dfddy, bcs, N):
    x, dx = numpy.linspace(0, 1, N+2, retstep=True)
    y = numpy.zeros_like(x)
    y_old = numpy.ones_like(x)
    step = 0
    while numpy.linalg.norm(y-y_old) > 1e-10 and step < 100:
        y_old = y.copy()
        step = step + 1
        y[0] = bcs[0]
        y[-1] = bcs[-1]
        x_interior = x[1:-1]
        y_interior = y[1:-1]
        dy = (y[2:] - y[:-2]) / (2*dx)
        residual = y[:-2] + y[2:] - 2*y[1:-1] - dx**2 * f(x[1:-1], y[1:-1], dy)
        J = numpy.zeros((N,N))
        for i in range(N):
            J[i,i] = -2 - dx**2*dfdy(x_interior[i], y_interior[i], dy[i])
            if i>0:
                J[i,i-1] = 1 + dx/2*dfddy(x_interior[i], y_interior[i], dy[i])
            if i<N-1:
                J[i,i+1] = 1 - dx/2*dfddy(x_interior[i], y_interior[i], dy[i])
        y_new_interior = y_interior + numpy.linalg.solve(J, -residual)
        y[1:-1] = y_new_interior

    return x, y

if __name__=="__main__":
    def p(x):
        return numpy.ones_like(x)
    def q(x):
        return numpy.zeros_like(x)
    def f(x):
        return -numpy.ones_like(x)

    x_exact = numpy.linspace(0, 1, 1000)
    x, y = relax_dirichlet(p, q, f, [0, 1], [0, 1], 5)
    pyplot.plot(x, y, 'kx', mew=2)
    pyplot.plot(x_exact, 2*numpy.exp(1)/(numpy.exp(1)-1)*(1-numpy.exp(-x_exact))-x_exact)
    pyplot.xlabel(r"$x$")
    pyplot.show()
    pyplot.plot(x, y-(2*numpy.exp(1)/(numpy.exp(1)-1)*(1-numpy.exp(-x))-x))
    pyplot.xlabel(r"$x$")
    pyplot.show()

    def f_nonlinear(x, y, dy):
        return -1/(1+y**2)
    def dfdy_nonlinear(x, y, dy):
        return 2*y/(1+y**2)**2
    def dfddy_nonlinear(x, y, dy):
        return numpy.zeros_like(x)

    x, y = relax_blackbox(f_nonlinear, [0, 0], 50)
    pyplot.plot(x, y, 'k--')
    pyplot.show()

    x, y = relax_newton(f_nonlinear, dfdy_nonlinear, dfddy_nonlinear,
                        [0, 0], 50)
    pyplot.plot(x, y, 'k--')
    pyplot.show()


import numpy as np

class NaiveBayesClassifier():
    def __init__(self, caso = 1):
        self.mean = None
        self.variance = None
        self.probs = None
        self.ys = None
        self.caso = caso

    def train(self, X, y):
        y_unique = np.unique(y)
        mean = []
        variance = []
        probs = []
        ys = []

        for _y in y_unique:
            j = 0
            new_X = []
            for i in range(len(X)):
                if y[i] == _y:
                    new_X.append(X[i])
                    j += 1
            mean.append(np.mean(np.array(new_X).T, axis = 1))
            variance.append(np.cov(np.array(new_X).T))
            ys.append(_y)
            probs.append(j / len(y))

        self.mean = mean
        self.variance = variance
        self.probs = probs
        self.ys = ys

    def print_params(self):
        for i in range(len(self.ys)):
            print("Y {} {:.2f}%".format(self.ys[i], self.probs[i]*100.0))
            print("MÉDIA")
            print(self.mean[i])
            print("VARIANCIA")
            print(self.variance[i])
            print("\n")

    def predict(self, X):
        y = []
        for x in X:
            dj = []
            for i in range(len(self.ys)):
                if self.caso == 1:
                    t1 = np.log(self.probs[i])

                    det = np.linalg.det(self.variance[i])
                    t2 = - 1/2 * np.log(det)

                    tsub = np.subtract(x, self.mean[i])
                    t3 = np.matmul(tsub.T, np.linalg.pinv(self.variance[i]))
                    t4 = -1/2 * np.matmul(t3, tsub)

                    dj.append(t1 + t2 + t4)

                elif self.caso == 2:
                    det = np.linalg.det(self.variance[i])
                    t2 = - 1/2 * np.log(det)

                    tsub = np.subtract(x, self.mean[i])
                    t3 = np.matmul(tsub.T, np.linalg.pinv(self.variance[i]))
                    t4 = -1/2 * np.matmul(t3, tsub)

                    dj.append(t2 + t4)

                else:
                    tsub = np.subtract(x, self.mean[i])
                    t3 = np.matmul(tsub.T, np.linalg.pinv(self.variance[i]))
                    t4 = -1/2 * np.matmul(t3, tsub)

                    dj.append(t4)
            #print(dj)
            y.append(self.ys[np.argmax(dj)])

        return y

    def score(self, X, true_y):
        predict_y = self.predict(X)
        total = len(predict_y)
        certos = 0
        for i in range(total):
            if predict_y[i] == true_y[i]:
                certos += 1
        return certos/total

    def confusion_matrix(self, X, true_y):
        predict_y = self.predict(X)
        cm = np.zeros((len(self.ys), len(self.ys)), int)
        for i in range(len(predict_y)):
            #print("{} -> {}x{}".format(i, predict_y[i], true_y[i]))
            cm[predict_y[i] - 1, true_y[i] - 1] += 1
        return cm


#!/usr/bin/env python
# coding: utf-8

# # Gaussian Code Exercise
#
# Read through the code below and fill out the TODOs. You'll find a cell at the end of the Jupyter notebook containing unit tests. After you've run the code cell with the Gaussian class, you can run the final cell to check that your code functions as expected.
#
# This exercise includes a file called 'numbers.txt', which you can see if you click on the 'Jupyter' icon at the top of the workspace and then go into the folder titled 3.OOP_code_gaussian_class. The 'numbers.txt' file is read in by the read_data_file() method. There is also a solution in the 3.OOP_code_gaussian_class folder in a file called answer.py.

# In[50]:

import math
import matplotlib.pyplot as plt

class Gaussian():
    """ Gaussian distribution class for calculating and
    visualizing a Gaussian distribution.

    Attributes:
        mean (float) representing the mean value of the distribution
        stdev (float) representing the standard deviation of the distribution
        data_list (list of floats) a list of floats extracted from the data file

    """
    def __init__(self, mu = 0, sigma = 1):

        self.mean = mu
        self.stdev = sigma
        self.data = []

    def calculate_mean(self):
        print("calculate_mean()")

        """Method to calculate the mean of the data set.

        Args:
            None

        Returns:
            float: mean of the data set

        """

        #TODO: Calculate the mean of the data set. Remember that the data set is stored in self.data
        # Change the value of the mean attribute to be the mean of the data set
        # Return the mean of the data set
        ttl = 0.0
        qty = 0
        for val in self.data:
           ttl += val
           qty += 1
        #print("ttl {}".format(ttl))
        #print("qty {}".format(qty))
        if 0 < qty and 0 < ttl:
            return ttl / qty
        else:
            return 0

    def calculate_stdev(self, sample=True):
        print("calculate_stdev()")
        """Method to calculate the standard deviation of the data set.

        Args:
            sample (bool): whether the data represents a sample or population

        Returns:
            float: standard deviation of the data set

        """

        # TODO:
        #   Calculate the standard deviation of the data set
        #
        #   The sample variable determines if the data set contains a sample or a population
        #   If sample = True, this means the data is a sample.
        #   Keep the value of sample in mind for calculating the standard deviation
        #
        #   Make sure to update self.stdev and return the standard deviation as well

        # https://www.khanacademy.org/math/probability/data-distributions-a1/summarizing-spread-distributions/a/calculating-standard-deviation-step-by-step
        # Step 1: Find the mean.
        m = self.calculate_mean()
        # Step 2: For each data point, find the square of its distance to the mean.
        # Step 3: Sum the values from Step 2.
        sum_p = 0.0
        qty = 0
        for p in self.data:
            sum_p += (p - m)**2
            qty += 1
        # Step 4: Divide by the number of data points.
        print("sum_p {}".format(sum_p))
        print("qty {}".format(qty))
        val = 0
        if 0 < qty:
            val = sum_p / qty
        # Step 5: Take the square root.
            self.stdev = math.sqrt(val)
        return self.stdev

    def read_data_file(self, file_name, sample=True):

        """Method to read in data from a txt file. The txt file should have
        one number (float) per line. The numbers are stored in the data attribute.
        After reading in the file, the mean and standard deviation are calculated

        Args:
            file_name (string): name of a file to read from

        Returns:
            None

        """
        data_list = []
        # This code opens a data file and appends the data to a list called data_list
        with open(file_name) as file:
            data_list = []
            line = file.readline()
            while line:
                data_list.append(int(line))
                line = file.readline()
        file.close()

        # TODO:
        #   Update the self.data attribute with the data_list
        #   Update self.mean with the mean of the data_list.
        #       You can use the calculate_mean() method with self.calculate_mean()
        #   Update self.stdev with the standard deviation of the data_list. Use the
        #       calcaulte_stdev() method.
        self.data = []
        for val in data_list:
            #print(float(val))
            self.data.append(val)
        self.mean = self.calculate_mean()
        print("self.mean {}".format(self.mean))
        self.stdev = self.calculate_stdev(False)
        print("self.stdev {}".format(self.stdev))

    import numpy as np
    import matplotlib.mlab as mlab
    import matplotlib.pyplot as plt
    def plot_histogram(self):
        """Method to output a histogram of the instance variable data using
        matplotlib pyplot library.

        Args:
            None

        Returns:
            None
        """

        # TODO: Plot a histogram of the data_list using the matplotlib package.
        #       Be sure to label the x and y axes and also give the chart a title
        num_bins = len(self.data)
        n, bins, patches = plt.hist(self.data, num_bins, facecolor='blue', alpha=0.5)
        plt.show()

    def pdf(self, x):
        """Probability density function calculator for the gaussian distribution.

        Args:
            x (float): point for calculating the probability density function

        Returns:
            float: probability density function output
        """

        # TODO: Calculate the probability density function of the Gaussian distribution
        #       at the value x. You'll need to use self.stdev and self.mean to do the calculation
        pass

    def plot_histogram_pdf(self, n_spaces = 50):

        """Method to plot the normalized histogram of the data and a plot of the
        probability density function along the same range

        Args:
            n_spaces (int): number of data points

        Returns:
            list: x values for the pdf plot
            list: y values for the pdf plot

        """

        #TODO: Nothing to do for this method. Try it out and see how it works.

        mu = self.mean
        sigma = self.stdev

        min_range = min(self.data)
        max_range = max(self.data)

         # calculates the interval between x values
        interval = 1.0 * (max_range - min_range) / n_spaces

        x = []
        y = []

        # calculate the x values to visualize
        for i in range(n_spaces):
            tmp = min_range + interval*i
            x.append(tmp)
            y.append(self.pdf(tmp))

        # make the plots
        fig, axes = plt.subplots(2,sharex=True)
        fig.subplots_adjust(hspace=.5)
        axes[0].hist(self.data, density=True)
        axes[0].set_title('Normed Histogram of Data')
        axes[0].set_ylabel('Density')

        axes[1].plot(x, y)
        axes[1].set_title('Normal Distribution for \n Sample Mean and Sample Standard Deviation')
        axes[0].set_ylabel('Density')
        plt.show()

        return x, y

# In[51]:

# Unit tests to check your solution

import unittest

class TestGaussianClass(unittest.TestCase):
    def setUp(self):
        self.gaussian = Gaussian(25, 2)

    def test_initialization(self):
        self.assertEqual(self.gaussian.mean, 25, 'incorrect mean')
        self.assertEqual(self.gaussian.stdev, 2, 'incorrect standard deviation')

    #def test_pdf(self):
    #    self.assertEqual(round(self.gaussian.pdf(25), 5), 0.19947,\
    #     'pdf function does not give expected result')

    def test_meancalculation(self):
        self.gaussian.read_data_file('numbers.txt', True)
        self.assertEqual(self.gaussian.calculate_mean(),         sum(self.gaussian.data) / float(len(self.gaussian.data)), 'calculated mean not as expected')

    def test_stdevcalculation(self):
        self.gaussian.read_data_file('numbers.txt', True)
        self.assertEqual(round(self.gaussian.stdev, 2), 92.87, 'sample standard deviation incorrect')
        self.gaussian.read_data_file('numbers.txt', False)
        self.assertEqual(round(self.gaussian.stdev, 2), 88.55, 'population standard deviation incorrect')

tests = TestGaussianClass()

tests_loaded = unittest.TestLoader().loadTestsFromModule(tests)

unittest.TextTestRunner().run(tests_loaded)

# In[ ]:

# In[ ]:


import math
import random

from numpy.linalg import inv
from numpy.linalg import det
from scipy.stats import norm

class Point:
  def __init__(self, pos, meta = None):
    self.pos = pos
    self.meta = meta
  def __iter__(self):
    for i in self.pos:
      yield i

## Euclidean distance between two points
def eucl_distance(a, b):
  a = tuple(a)
  b = tuple(b)
  total = 0;
  for i in range(0, len(a)):
    total += (a[i] - b[i]) ** 2
  return math.sqrt(total)

## Sum of absolute difference
def sad_distance(a, b):
  a = tuple(a)
  b = tuple(b)
  total = 0;
  for i in range(0, len(a)):
    total += abs(a[i] - b[i])
  return total

## Sum of squared differences
## aka euclidean norm
def ssd_distance(a, b):
  a = tuple(a)
  b = tuple(b)
  total = 0;
  for i in range(0, len(a)):
    total += (a[i] - b[i]) ** 2
  return total

## Sum of squared differences
## aka euclidean norm
def chebyshev_distance(a, b):
  a = tuple(a)
  b = tuple(b)
  diffs = [];
  for i in range(0, len(a)):
    diffs.append(abs(a[i] - b[i]))
  return max(diffs)

def first(items):
  return items[0] if items else None

def last(items):
  return items[-1] if items else None

## Find an item nearest to the predicate
def nearest(items, predicate, as_index = False, distance_fn = eucl_distance):
  result = None
  min_dist = math.inf
  for i, item in enumerate(items):
    cur_dist = distance_fn(item, predicate)
    if (cur_dist < min_dist):
      min_dist = cur_dist
      result = i if as_index else item
  return result

def tuple_apply(fn, a, b):
  size = len(a)
  result = [None] * size
  for i in range(size):
    result[i] = fn(a[i], b[i])
  return tuple(result)

def mean(items):
  return tuple(sum(x) / len(items) for x in zip(*items))

## TODO: support for Point class
def std(items):
  m = mean(items)
  size = len(m)
  s = [0] * size
  n = len(items)
  for x in items:
    x = tuple(x)
    for i in range(0, size):
      s[i] += (x[i] - m[i]) ** 2
  for i in range(0, size):
    s[i] = math.sqrt(s[i] / n)
  return tuple(s)

def mean_normalize(items):
  x_mean = mean(items)
  return [tuple_apply(lambda a, b: a - b, x, x_mean) for x in items]

def find_two_farthest(items):
  length = len(items)
  max_dist = 0
  result = None
  for i in range(0, length - 1):
    for j in range(i, length):
      a = items[i]
      b = items[j]
      cur_dist = distance(a, b)
      if (cur_dist > max_dist):
        max_dist = cur_dist
        result = (a, b)
  return result

## KMeans
def kmeans(points, k = 2, epsilon = 0.001, iterations = 1000, distance_fn = eucl_distance):
  ## Initialize cluster centers with a random sample
  cluster_centers = random.sample(points, k)

  ## Iterate
  for it in range(iterations):
    ## Assign points to clusters
    clusters = [[] for _ in range(k)]
    for p, point in enumerate(points):
      c = nearest(cluster_centers, point, as_index = True, distance_fn = distance_fn)
      clusters[c].append(point)

    ## Calculate new cluster centers
    cluster_centers_new = [None] * k
    for c, cluster_points in enumerate(clusters):
      cluster_centers_new[c] = mean(cluster_points)

    ## Evaluate difference between new and old cluster centers
    cluster_center_distances = [distance_fn(a, b)
      for a, b in zip(cluster_centers, cluster_centers_new)]

    print('iteration: {}, diff: {}'.format(it, cluster_center_distances))

    ## Break loop when change is smaller than epsilon
    if max(cluster_center_distances) < epsilon:
      break

    ## Replace cluster centers with newer and continue iteration
    cluster_centers = cluster_centers_new

  return cluster_centers, clusters

def gaussian_pdf(point, mu, sigma, lam):
  point = tuple(point)
  n = len(point)
  probability = lam
  for i in range(n):
    probability *= norm.pdf(point[i], mu[i], sigma[i][i])
  return probability

## Expectation Minimization
def em_gaussian(points, k = 2, epsilon = 0.001, iterations = 1000):
  params = {
    'mu': [tuple(x) for x in random.sample(points, k)],
    'sig': [[[1, 0], [0, 1]] for _ in range(k)],
    'lambda': [1/k for _ in range(k)],
  }
  count = len(points)

  ## Iterate
  for it in range(iterations):
    clusters = [[] for _ in range(k)]

    ## Expectation
    for point in points:
      max_prob = 0
      max_prob_cluster = None
      for i in range(k):
        prob = gaussian_pdf(point, params['mu'][i], params['sig'][i], params['lambda'][i])
        if prob > max_prob:
          max_prob = prob
          max_prob_cluster = i
      clusters[max_prob_cluster].append(point)

    ## Maximization
    params_new = {}
    params_new['lambda'] = [len(clusters[i]) / float(count) for i in range(k)]
    params_new['mu'] = [mean(clusters[i]) for i in range(k)]
    params_new['sig'] = []
    for i in range(k):
      cluster_std = std(clusters[i])
      cluster_std = [[cluster_std[0], 0], [0, cluster_std[1]]]
      params_new['sig'].append(cluster_std)

    print(params_new)

    ## Evaluate difference between new and old params
    diff = 0
    for i in range(k):
      diff += (params['mu'][i][0] - params_new['mu'][i][0]) ** 2
      diff += (params['mu'][i][1] - params_new['mu'][i][1]) ** 2
    diff = diff ** 0.5

    ## Break loop when change is smaller than epsilon
    if diff < epsilon:
      break

    params = params_new

    print("iteration {}, diff {}".format(it, diff))

  return [], clusters


# -*- coding: utf-8 -*-
"""
Efficient Computation of the Singular Value Decomposition

@author: Miika Ahdesmaki, Korbinian Strimmer
"""
from __future__ import print_function, division
import numpy as np

def positive_svd(m, tol):
    """svd that retains only positive singular values
    """
    u, d, v = np.linalg.svd(m)
    # determine rank of B  (= rank of m)
    if tol is None:
      tol = max(m.shape) * max(d) * np.finfo(float).eps
    Positive = d > tol
    sub_range = np.array(range(0,len(Positive)))[Positive] # subset of indices
    return (d[Positive], u[:, sub_range], v.T[:, sub_range])

def nsmall_svd(m, tol):
    B = np.matmul(m, m.T) # n by n matrix
    u, d, _ = np.linalg.svd(B) # ...whose svd is easy
    # determine rank of B  (= rank of m)
    if tol is None:
      tol = B.shape[0] * max(d) * np.finfo(float).eps
    Positive = d > tol

    # positive singular values of m
    d = np.sqrt(d[Positive])

    # corresponding orthogonal basis vectors
    u = u[:, Positive]
    v = np.matmul(np.matmul(m.T, u), np.diag(1/d) )

    return (d, u, v)

def psmall_svd(m, tol):
    B = np.matmul(m.T, m) # p by p matrix
    _, d, v = np.linalg.svd(B) # ...whose svd is easy
    # determine rank of B  (= rank of m)
    if tol is None:
      tol = B.shape[0] * max(d) * np.finfo(float).eps
    Positive = d > tol

    # positive singular values of m
    d = np.sqrt(d[Positive])

    # corresponding orthogonal basis vectors (v is different from the v returned by R by a transpose)
    v = v.T[:, Positive]
    u = np.matmul(np.matmul(m, v), np.diag(1/d) )
    return (d, u, v)

# public functions

# fast computation of svd(m)

# note that the signs of the columns vectors in u and v
# mayfast computation of svd(m) if n >> p be different from that given by svd()

# note that also only positive singular values are returned

def fast_svd(m, tol = None):
    """Fast computation of svd(m)
Note that the signs of the columns vectors in u and v
may be different from that given by svd()

Note that also only positive singular values are returned

    Parameters
    ----------
    m : array
        Matrix whose svd is sought.
    tol : float
        Singularity tolerance.

    Returns
    -------
    tuple
        Returns (d, u, v) tuple

    """
    n, p = m.shape
    EDGE_RATIO = 2 # use standard SVD if matrix almost square
    if n > EDGE_RATIO*p:
        return psmall_svd(m, tol)
    elif EDGE_RATIO*n < p:
        return nsmall_svd(m, tol)
    else: # if p and n are approximately the same
        return positive_svd(m, tol)


import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d import proj3d

np.random.seed(43)
####################### Data Generation for mapping of 3-d data into 2-d space #####################

##creating a mean vector centered at origin (0,0,0)
##cov matrix is taken as the identity as it would generate a matrix of varianvce 1 for all variables
mu_vec1 = np.array([0,0,0])
cov_mat1 = np.array([[1,0,0],[0,1,0],[0,0,1]])
class1_sample = np.random.multivariate_normal(mu_vec1, cov_mat1, 30).T
assert class1_sample.shape == (3,30), "The matrix does not have 3x30 dim"

##creating a mean vector centered at (1,1,1)
##cov matrix is taken as the identity as it would generate a matrix of varianvce 1 for all variables
mu_vec2 = np.array([1,1,1])
cov_mat2 = np.array([[1,0,0],[0,1,0],[0,0,1]])
class2_sample = np.random.multivariate_normal(mu_vec2, cov_mat2, 30).T
assert class2_sample.shape == (3,30), "The matrix does not have 3x30 dim"

####################### Plot the 3-d data for visualtization #######################################

fig = plt.figure(figsize=(8,8))
ax = fig.add_subplot(111, projection='3d')
plt.rcParams['legend.fontsize'] = 10
ax.plot(class1_sample[0,:], class1_sample[1,:], class1_sample[2,:], 'o', markersize=8, color='blue', alpha=0.5, label='class1')
ax.plot(class2_sample[0,:], class2_sample[1,:], class2_sample[2,:], '^', markersize=8, alpha=0.5, color='red', label='class2')

plt.title('Samples for class 1 and class 2')
ax.legend(loc='upper right')

plt.show()

####################### A combine all the samples of different classes #############################
##mean of the x,y and  axis values
all_samples = np.concatenate((class1_sample, class2_sample), axis=1)
assert all_samples.shape == (3,60), "The matrix has not the dimensions 3x60"

mean_x = np.mean(all_samples[0,:])
mean_y = np.mean(all_samples[1,:])
mean_z = np.mean(all_samples[2,:])

mean_vector = np.array([[mean_x],[mean_y],[mean_z]])

print('Mean Vector:\n', mean_vector)

cov_mat = np.cov([all_samples[0,:],all_samples[1,:],all_samples[2,:]])
print('Covariance Matrix:\n', cov_mat)

eig_val_cov, eig_vec_cov = np.linalg.eig(cov_mat)

print('Eigven values of covariance matrix:\n',eig_val_cov)
print('Eigven vectors of covariance matrix:\n',eig_vec_cov)

#matrix_w = np.hstack((eig_pairs[0][1].reshape(3,1), eig_pairs[1][1].reshape(3,1)))
#print('Matrix W:\n', matrix_w)

for ev in eig_vec_cov:
    np.testing.assert_array_almost_equal(1.0, np.linalg.norm(ev))

# Make a list of (eigenvalue, eigenvector) tuples
eig_pairs = [(np.abs(eig_val_cov[i]), eig_vec_cov[:,i]) for i in range(len(eig_val_cov))]

# Sort the (eigenvalue, eigenvector) tuples from high to low
eig_pairs.sort(key=lambda x: x[0], reverse=True)

print('Sorting the eigenvalues in the ascending order:')
# Visually confirm that the list is correctly sorted by decreasing eigenvalues
for i in eig_pairs:
    print(i[0])

matrix_w = np.hstack((eig_pairs[0][1].reshape(3,1), eig_pairs[1][1].reshape(3,1)))
print('Matrix W:\n', matrix_w)

transformed = matrix_w.T.dot(all_samples)
assert transformed.shape == (2,60), "The matrix is not 2x60 dimensional."

plt.plot(transformed[0,0:30], transformed[1,0:30], 'o', markersize=7, color='blue', alpha=0.5, label='class1')
plt.plot(transformed[0,30:60], transformed[1,30:60], '^', markersize=7, color='red', alpha=0.5, label='class2')
plt.xlim([-4,4])
plt.ylim([-4,4])
plt.xlabel('transformed x_values')
plt.ylabel('Transformed y_values')
plt.legend()
plt.title('Transformed samples with class labels')

plt.show()


"""
Implementation of Singular Value Decomposition (SVD)
based on this post https://jeremykun.com/2016/05/16/singular-value-decomposition-part-2-theorem-proof-algorithm/
"""

import numpy as np

def L2_norm(vec):
    """Calculate L2 norm of vertor

    returns: float
    """
    return np.sum(np.abs(vec) ** 2) ** 0.5

def random_unit_vector(size):
    """Create normalized random unit vector

    size: lenght of vector

    returns: list
    """
    # create vector based on normal distribution
    unnormalized = np.random.normal(0, 1, size=size)
    # calculate L2 norm of vector
    norm = L2_norm(unnormalized)
    # normalized vector
    return unnormalized / norm

def svd_1d(A, epsilon=1e-10):
    """The one-dimensional SVD

    A: matrix
    epsilon: tolerance

    Returns:

    VT - right singular vector
    """
    n, m = A.shape
    # create random normalized unit vector
    lastV, currentV = None, random_unit_vector(m)
    # calculate A.T * A
    # A.T * A is a rectangular matrix
    ATA = np.dot(A.T, A)

    while True:
        lastV = currentV
        currentV = np.dot(ATA, lastV)
        currentV = currentV / L2_norm(currentV)
        if abs(np.dot(currentV, lastV)) > 1 - epsilon:
            return currentV

def svd(A, epsilon=1e-10, random_state=None):
    """Compute the singular-value decomposition of a matrix.

    A: matrix
    epsilon: tolerance
    random_state: int

    Returns: U, S, VT
    """

    np.random.seed(random_state)
    # shape of matrix
    # m - number of columns in matrix
    n, m = A.shape
    svdSoFar = []

    for i in range(m):
        # loop for each column
        # create temporary matrix as copy of original
        # and substact singular values from it
        # for all previuos columns
        matrixFor1D = A.copy()
        for singular, u, v in svdSoFar[:i]:
            # calculate product of two vectors
            matrix = np.outer(u, v)
            # substact previous singular values
            matrixFor1D -= singular * matrix
        # calculate svd for remaining matrix
        v = svd_1d(matrixFor1D, epsilon=epsilon)
        # next singular vector unnormalized
        u_unnormalized = np.dot(A, v)
        # get L2 norm of vertor
        sigma = L2_norm(u_unnormalized)
        # calculate next singular value
        u = u_unnormalized / sigma
        # append
        svdSoFar.append((sigma, u, v))
    # transform it into matrices of the right shape
    singularValues, us, vs = [np.array(x) for x in zip(*svdSoFar)]

    return us.T, singularValues, vs

if __name__ == "__main__":

    data = np.array([
        [2, 5, 3],
        [1, 2, 1],
        [4, 1, 1],
        [3, 5, 2],
        [5, 3, 1],
        [4, 5, 5],
        [2, 4, 2],
        [2, 2, 5],
    ], dtype='float64')

    # calculate mean vector
    data_mean = data.mean(axis=0)
    # extract mean vector from original matrix
    data_centered = data - data_mean
    # performe SVD
    # X' = U * S * VT
    # X - n-by-m matrix
    #
    # U - n-by-n matrix left singular vectors
    # S - singular values
    # VT - m-by-m matrix right singular vectors
    U, S, VT = svd(data_centered, epsilon=1e-10, random_state=37)
    # calculate restored matrix based on SVD
    restored = np.dot(U, np.dot(np.diag(S), VT)) + data_mean
    # calculate delta between original matrix and restored
    is_equal = (np.round(data - restored) == 0).all()
    print('data:\n{}'.format(data))
    print('U:\n{}'.format(U))
    print('S:\n{}'.format(np.diag(S)))
    print('VT:\n{}'.format(VT))
    print('\nOriginal matrix and restores are equal: {}'.format(is_equal))


"""Function for computing a robust mean estimate in the presence of outliers.

This is a modified Python implementation of this file:
https://idlastro.gsfc.nasa.gov/ftp/pro/robust/resistant_mean.pro
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np

def robust_mean(y, cut):
    """Computes a robust mean estimate in the presence of outliers.

  Args:
    y: 1D numpy array. Assumed to be normally distributed with outliers.
    cut: Points more than this number of standard deviations from the median are
        ignored.

  Returns:
    mean: A robust estimate of the mean of y.
    mean_stddev: The standard deviation of the mean.
    mask: Boolean array with the same length as y. Values corresponding to
        outliers in y are False. All other values are True.
  """
    # First, make a robust estimate of the standard deviation of y, assuming y is
    # normally distributed. The conversion factor of 1.4826 takes the median
    # absolute deviation to the standard deviation of a normal distribution.
    # See, e.g. https://www.mathworks.com/help/stats/mad.html.
    absdev = np.abs(y - np.median(y))
    sigma = 1.4826 * np.median(absdev)

    # If the previous estimate of the standard deviation using the median absolute
    # deviation is zero, fall back to a robust estimate using the mean absolute
    # deviation. This estimator has a different conversion factor of 1.253.
    # See, e.g. https://www.mathworks.com/help/stats/mad.html.
    if sigma < 1.0e-24:
        sigma = 1.253 * np.mean(absdev)

    # Identify outliers using our estimate of the standard deviation of y.
    mask = absdev <= cut * sigma

    # Now, recompute the standard deviation, using the sample standard deviation
    # of non-outlier points.
    sigma = np.std(y[mask])

    # Compensate the estimate of sigma due to trimming away outliers. The
    # following formula is an approximation, see
    # http://w.astro.berkeley.edu/~johnjohn/idlprocs/robust_mean.pro.
    sc = np.max([cut, 1.0])
    if sc <= 4.5:
        sigma /= (-0.15405 + 0.90723 * sc - 0.23584 * sc ** 2 + 0.020142 * sc ** 3)

    # Identify outliers using our second estimate of the standard deviation of y.
    mask = absdev <= cut * sigma

    # Now, recompute the standard deviation, using the sample standard deviation
    # with non-outlier points.
    sigma = np.std(y[mask])

    # Compensate the estimate of sigma due to trimming away outliers.
    sc = np.max([cut, 1.0])
    if sc <= 4.5:
        sigma /= (-0.15405 + 0.90723 * sc - 0.23584 * sc ** 2 + 0.020142 * sc ** 3)

    # Final estimate is the sample mean with outliers removed.
    mean = np.mean(y[mask])
    mean_stddev = sigma / np.sqrt(len(y) - 1.0)

    return mean, mean_stddev, mask


import numpy as np

a = b = np.arange(3)
print(a)
# [0 1 2]

print(a.shape)
# (3,)

print(a * b)
# [0 1 4]

print(np.multiply(a, b))
# [0 1 4]

a_1_3 = a.reshape(1, 3)
print(a_1_3)
# [[0 1 2]]

print(a_1_3.shape)
# (1, 3)

b_3_1 = b.reshape(3, 1)
print(b_3_1)
# [[0]
#  [1]
#  [2]]

print(b_3_1.shape)
# (3, 1)

print(a_1_3 * b_3_1)
# [[0 0 0]
#  [0 1 2]
#  [0 2 4]]

print(np.multiply(a_1_3, b_3_1))
# [[0 0 0]
#  [0 1 2]
#  [0 2 4]]

print(a * b_3_1)
# [[0 0 0]
#  [0 1 2]
#  [0 2 4]]

print(np.multiply(a, b_3_1))
# [[0 0 0]
#  [0 1 2]
#  [0 2 4]]

print(a_1_3 @ b_3_1)
# [[5]]

print(np.matmul(a_1_3, b_3_1))
# [[5]]

print(np.dot(a_1_3, b_3_1))
# [[5]]

print(type(a_1_3 @ b_3_1))
# <class 'numpy.ndarray'>

print((a_1_3 @ b_3_1).shape)
# (1, 1)

print(a_1_3 @ b)
# [5]

print(np.matmul(a_1_3, b))
# [5]

print(np.dot(a_1_3, b))
# [5]

print(type(a_1_3 @ b))
# <class 'numpy.ndarray'>

print((a_1_3 @ b).shape)
# (1,)

print(a @ b)
# 5

print(np.matmul(a, b))
# 5

print(np.dot(a, b))
# 5

print(type(a @ b))
# <class 'numpy.int64'>

a = np.arange(6).reshape(2, 3)
print(a)
# [[0 1 2]
#  [3 4 5]]

b = np.arange(2).reshape(1, 2)
print(b)
# [[0 1]]

# print(a @ b)
# ValueError: shapes (2,3) and (1,2) not aligned: 3 (dim 1) != 1 (dim 0)

print(np.tile(b, (3, 1)))
# [[0 1]
#  [0 1]
#  [0 1]]

print(a @ np.tile(b, (3, 1)))
# [[ 0  3]
#  [ 0 12]]


# Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma de
# Barcelona (UAB).
#
# This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>.
#
# Modified by ERDOS team.

import numpy as np
from scipy.misc import comb

def bernstein_poly(i, n, t):
    """
     The Bernstein polynomial of n, i as a function of t
    """

    return comb(n, i) * (t**(n - i)) * (1 - t)**i

def bezier_curve(points, nTimes=1000):
    """
       Given a set of control points, return the
       bezier curve defined by the control points.

       points should be a list of lists, or list of tuples
       such as [ [1,1],
                 [2,3],
                 [4,5], ..[Xn, Yn] ]
        nTimes is the number of time steps, defaults to 1000

        See http://processingjs.nihongoresources.com/bezierinfo/
    """

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

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

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

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

    return xvals, yvals


""" Gradient Descent Model """
import numpy as np, pandas as pd, matplotlib.pyplot as plt, seaborn as sns
sns.set()

""" Functions """
def error_calc(x, y, theta):
    inner = np.power(((x * theta.T) - y), 2)
    err = np.sum(inner) /  (2 * len(x))
    return err

def gradDescent(x, y, theta, alpha, iters):
    t = np.matrix(np.zeros(theta.shape))
    paramater = int(theta.ravel().shape[1])
    cost = np.zeros(iters)

    for i in range(iters):
        error = (x * theta.T) - y
        for j in range(paramater):
            term = np.multiply(error, x[:, j])
            t[0,j] = theta[0,j] - ((alpha / len(x)) * np.sum(term))

        theta = t
        cost[i] = error_calc(x, y, theta)
    return theta, cost

""" Prepare Data """
data = pd.read_csv(r'data_2.txt', header = None, names = ['pop', 'profit'])
data.insert(0, 'theta_0', 1)
print(data.describe().round(2))

x = np.matrix(data.iloc[:,:-1].values)
y = np.matrix(data.iloc[:,-1:].values)
theta = np.matrix(np.array([0] * x.shape[1]))

""" Model """
alpha = 0.01
iters = 8888

grad_dec, cost = gradDescent(x, y, theta, alpha, iters)

print('Error before gradDescent:', error_calc(x, y, theta).round(2))
print('Error after gradDescent:', error_calc(x, y, grad_dec).round(2))
print('Gradient:', grad_dec.round(2))

""" Plot """
xp = np.linspace(data['pop'].min(), data['pop'].max(), 100)
f = grad_dec[0, 0] + (grad_dec[0, 1] * xp)

fig, ax = plt.subplots(figsize = (10,6))
ax.plot(xp, f, 'darkgray', label = 'Prediction')
ax.scatter(data['pop'], data['profit'], label = 'Traning Data')
ax.legend(loc = 2)
ax.set_xlabel('Population')
ax.set_ylabel('Profit')
ax.set_title('Predicted Profit vs. Population Size')


from sympy import Matrix, Float, Symbol
from BMSS.strike_goldd_simplified import*

###############################################################################
#Model
###############################################################################

x, h = [Symbol(x) for x in ['x', 'h']]
mu_max, synh, Kind, x_max = [Symbol(x) for x in ['mu_max', 'synh', 'Kind', 'x_max']]
Ind = Symbol('Ind')

mu   = mu_max*(1 - x/x_max)

dx  =  x*mu
dh  =  synh*Ind/(Ind+Kind) -h*mu

variables = {'x': x, 'h': h, 'mu_max': mu_max, 'synh': synh, 'Kind': Kind, 'x_max': x_max, 'Ind': Ind}

###############################################################################
#Specify Input for IA
###############################################################################
#User makes changes here
measured_states    = Matrix([x, h])
states             = Matrix([x, h])
unknown_parameters = Matrix([])
diff               = Matrix([dx, dh])
input_conditions   = {Ind: 5}
init_conditions    = {x: Float(0.1, 3), h: Float(0.1, 3)}
decomposition      = [
                      ]

known_parameters = {mu_max : 1, synh : 1, Kind : 1, x_max : 1}
diff = diff.subs(known_parameters.items())

###############################################################################
#Call Strike-Goldd
###############################################################################
def run_strike_goldd():
    start_time = time()
    x_aug_dict = strike_goldd(measured_states,
                              states,
                              unknown_parameters,
                              input_conditions,
                              diff,
                              init_conditions,
                              decomposition)
    print("Total time: ",time()-start_time)
    print(x_aug_dict)
    return x_aug_dict

if __name__ == '__main__':
    pass


import numpy as np

def sigmoid(z):
    """
    Computes the sigmoid activation function for a given numpy array.
    """
    return (1 / (1 + np.exp(-z)))

def sigmoid_p(z):
    """
    Computes the gradient of the sigmoid for a given numpy array.
    """
    return (sigmoid(z) * (1 - sigmoid(z)))

def relu(z):
    """
    Computes the relu activation function for a given numpy array.
    """
    result = np.maximum(0, z)

    return result

def relu_p(z):
    """
    Computes the gradient of the relu for a given numpy array.
    """
    result = z
    result[result <= 0] = 0
    result[result > 0] = 1

    return result

def tanh(z):
    """
    Computes the tanh activation function for a given numpy array.
    """
    return (np.tanh(z))

def tanh_p(z):
    """
    Computes the gradient of the tanh function for a given numpy array.
    """
    return (1 - np.tanh(z)**2)

def softmax(x):
    """
    Computes the softmax activation function for a given numpy array.
    """
    shiftx = x - np.max(x)
    return (np.exp(shiftx)) / np.sum(np.exp(shiftx), axis=0)

def softmax_p(z):
    """
    Computes the gradient of the softmax function for a given numpy array.
    """
    # return softmax(z) * (1 - softmax(z))
    return np.diagflat(z) - np.dot(z, z.T)


import numpy as np

x = np.array([[1],
                [2],
                [3],
                [4],
                [5]])

y = np.array([[4],
                [12],
                [28],
                [50],
                [80]])

class LinearRegression(object):

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.m = None
        self.b = None

    def finding_mandb(self):
        x1, x2  = self.x[0], self.x[-1]
        y1, y2  = self.y[0], self.y[-1]
        #Slope
        self.m = (y2-y1)/(x2-x1)
        #Bias
        #self.y0  = self.m[0]*self.x0 + (- self.m[0]*x1 + y1)
        self.b = - self.m[0]*x1 + y1
        return self.m[0], self.b[0]

    def finding_predicted(self):
        y0 = []

        for i in range(len(x)):
            y_pred = self.m[0]*self.x[i] + self.b[0]
            y0.append(y_pred)
        return y0

    def applying_lse(self):
        x_mean = np.mean(self.x)
        y_mean = np.mean(self.y)
        x_x_mean = []
        y_y_mean = []
        for i in range(len(self.x)):
            sep_mean = self.x[i] - x_mean
            x_x_mean.append(sep_mean)
            sep_mean_y = self.y[i] - y_mean
            y_y_mean.append(sep_mean_y)
        top = sum(np.array(y_y_mean)*np.array(x_x_mean))
        bot = sum(np.array(x_x_mean)**2)
        m = top/bot
        b = y_mean - m * x_mean

        y0_pre = []
        for i in range(len(x)):
            y_pred = m[0]*self.x[i] + b[0]
            y0_pre.append(y_pred)

        return y0_pre

    def mean_square_error(self, y, y_pred):
        error = (1/len(y)) * (sum(y_pred - y)**2)
        return error

obj = LinearRegression(x, y)
m, b = obj.finding_mandb()
y_pred = obj.finding_predicted()
y_pred_1 = obj.applying_lse()
print(y_pred_1)

error = obj.mean_square_error(y, y_pred_1)
print(error)

import matplotlib.pyplot as plt
plt.scatter(x, y, label="Normal values")
plt.plot(x, y)
plt.scatter(x, y_pred, label = "straight line Equation")
plt.plot(x, y_pred)
plt.scatter(x, y_pred_1, label = "Least Square Equation")
plt.plot(x, y_pred_1)
plt.legend(loc="upper left")
plt.title("Linear Regression")
plt.xlabel("X Values")
plt.ylabel("Y Values")
plt.savefig("LinearRegression.png")
plt.show()


#!/usr/bin/python
# encoding: utf8

import numpy as np
import numpy.linalg as la
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib2tikz import save as tikz_save

# load data
X = np.genfromtxt("data/data-dimred-X.csv", dtype=float, delimiter=',').T
y = np.genfromtxt("data/data-dimred-y.csv", dtype=float, delimiter=',')

# zero mean, normalization,
m0 = np.mean(X, axis=0)
# center the data
normalized = np.subtract(X, m0)

# covariance matrix. Why rowvar == normalized.T?
C = np.cov(normalized.T)

# compute eigenvectors and eigenvalues
eival, eivec = np.linalg.eigh(C)

#inds = np.argsort(eival)[::-1]
#eival = eival[inds]
#eivec = eivec[:,inds]
eivec = eivec.T[::-1]

# plots
# plot in 2D
z = np.dot(normalized, eivec[:3].T)
# for plotting
z = z.T

fig = plt.figure()
plt.scatter(z[0], z[1], c=y)
#path="latex/pca_2d.tex"
#tikz_save(path)
plt.savefig("out/03/pca_2d.png", bbox_inches="tight", pad_inches=0)
plt.show()

# plot in 2D
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.scatter3D(z[0], z[1], z[2], c=y);
#path="latex/pca_3d.tex"
#tikz_save(path)
plt.savefig("out/03/pca_3d.png", bbox_inches="tight", pad_inches=0)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Tue Nov 17 14:28:30 2015

@author: Hanna
"""
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
import scipy.optimize as op

def plotData(data):
    pos = data[data[:,2] == 1]
    neg = data[data[:,2] == 0]

    plt.figure()
    plt.scatter(pos[:,0], pos[:,1], c='k', marker='+', s=40, linewidths=2)
    plt.scatter(neg[:,0], neg[:,1], c='y', marker='o', s=40, linewidths=1)
    plt.legend(['Admitted', 'Not admitted'], loc='upper right', prop={'size':10}, scatterpoints=1)
    plt.xlabel("Exam 1 score")
    plt.ylabel("Exam 2 score")
    plt.xlim([28, 102])
    plt.ylim([28, 102])

def plotDecisionBoundary(data,X,theta):
    plotData(data)
    #decision boundary is a line where hypothesis=0.5
    #i.e. sigmoid(theta0+theta1*x1+theta2*x2)=0.5
    #i.e. theta0+theta1*x1+theta2*x2=0
    #x1 -- exam1, need only two points since decision bdry is a line:
    plt.plot_x = np.array([min(X[:,1]),max(X[:,1])])
    #x2 -- exam2:
    plt.plot_y = (-theta[0]-theta[1]*plt.plot_x)/theta[2]
    plt.plot(plt.plot_x, plt.plot_y,'b-',linewidth=1)
    plt.savefig("LogisticRegFit.pdf")

def sigmoid(z):
    #works elementwise for any array z

    return sp.special.expit(z)  # expit(x) = 1/(1+exp(-x)) elementwise for array x

def hypothesis(theta,X):
    #works for any # of features

    return sigmoid(np.dot(X,theta))

def computeCost(theta,X,y):  # J(theta)
    #works for any # of features
    m = len(y)
    h = hypothesis(theta,X)
    cost = (-np.dot(y.T,sp.log(h))-np.dot((1.0-y).T,sp.log(1.0-h)))/m
    #check for nans: can occur since have log
    #if skip this, precision loss occurs
    if np.isnan(cost):
        return np.inf

    return cost

def computeCostDeriv(theta,X,y):  # partial derivatives of J(theta)
    #works for any # of features
    n = np.shape(X)[1]  # # of features including itercept; in main n is the # of original features
    m = np.shape(X)[0]
    #minimize routine changes dimensions of theta from matrix to vector,
    #i.e from (3,1) to (3,) in this case
    #so need to reshape theta to original matrix dimensions
    #since in computeCostDeriv routine it is important that theta and thus hypothesis is matrix
    #if theta is a vector dot product in hypothesis works anyway but resulting hypothesis is a vector
    # then h-y has wrong dimension, (100,100) in this case: h is (100,), y is (100,1)
    #does not matter for computeCost routine
    theta = theta.reshape(n,1)
    h = hypothesis(theta,X)

    return (np.dot(X.T,h-y)/m).flatten()  # minimize needs vector of gradients not matrix

def predict(theta,X):
    if hypothesis(theta,X) >= 0.5:
        prediction = 1
    else:
        prediction = 0

    return prediction

if __name__ == '__main__':

    data = np.genfromtxt("ex2data1.txt", delimiter=',')
    m = np.shape(data)[0]  # number of examples in training set
    n = np.shape(data)[1]-1  # number of features
    X, y = data[:,:n], data[:,n:]

    plotData(data)
    plt.savefig("scatterData.pdf")

    #test sigmoid
    #print(sigmoid(-100))#should be close to 0
    #print(sigmoid(100))#should be close to 1
    #print(sigmoid(0))#should equal 0.5

    X = np.hstack((np.ones((m,1)),X))  # add intercept column
    theta = np.zeros((n+1,1))  # initial guess for theta

    #test cost function, should be 0.693
    print("cost function for initial theta = ",computeCost(theta,X,y),"should be 0.693")  # get [[ 0.69314718]]

    #find minimum of cost function using BFGS
    result = op.minimize(computeCost, theta, args=(X,y), method='BFGS', jac=computeCostDeriv, options={'disp': True})
    theta = result.x  # note, result.x is a vector so below theta is a vector eg when passed to plotDecisionBoundary
    cost = result.fun
    print("optimal theta =",theta)  # get [-25.16133446   0.20623173   0.20147159]
    print("optimal cost funstion =",cost,"should be 0.203")  # get 0.20349770158945538

    #plot decision bdry
    plotDecisionBoundary(data,X,theta)

    #make a prediction
    #do not need to reshape theta to matrix form theta.reshape(n+1,1)
    test_student = [[1,45,85]]  # try also [[1,45,85],[1,40,50],[1,70,90]]
    print("if scores are 45 for exam1 and 85 for exam2, the admission probability is")
    print(hypothesis(theta,test_student),"should be 0.776")  # get [ 0.77629051]

    plt.show()


# -*- coding: utf-8 -*-
"""
Created on Sat Apr 21 21:52:03 2018

@author: Marcus
"""

import numpy as np
import pylab as plt

#FFT Leakage Script
#If m is not an integer, then leakage will be observed

complex_in = True
db_magnitude = False
plot = True

N = 64
fs = 0.4
ts = 1/fs
A0 = 1

n = np.linspace(0, N - 1, N)
#n = np.arange(0, N * ts, ts) #<- This is an equivalent way to generate t

m = 31
f = m * fs / N
w = 2 * np.pi * f

if(complex_in):
    x = np.cos(w * n * ts) + 1j*np.sin(w * n * ts)
else:
    x = np.sin(w * n * ts)

x *= A0

f_out = np.abs(np.fft.fft(x))
frequencies = np.fft.fftfreq(N, d=ts)

if(db_magnitude):
    f_out = 20 * np.log10(f_out)

if(plot):
    plt.stem(f_out, markerfmt='b.')
    plt.title("FFT Output")
    plt.xlabel("Frequency index")
    plt.ylabel("Magnitude (dB)")

    plt.show()

    print(max(f_out))


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

from sympy import *
x, y, z = symbols('x y z')
init_printing(use_unicode=True)
print(diff(cos(x), x))
print(diff(exp(x**2), x))
print(diff(x**4, x, x, x))
print(diff(x**4, x, 3))
expr = exp(x*y*z)
print(diff(expr, x, y, y, z, z, z, z))
print(diff(expr, x, y, 2, z, 4))
print(diff(expr, x, y, y, z, 4))
print(expr.diff(x, y, y, z, 4))

print(expr.diff(x, y, y, z, 4))
deriv = Derivative(expr, x, y, y, z, 4)
print(deriv)
print(deriv.doit())

m, n, a, b = symbols('m n a b')
expr = (a*x + b)**m
print(expr.diff((x, n)))

print(integrate(cos(x), x))
print(sin(x))
print(integrate(exp(-x), (x, 0, oo)))
print(integrate(exp(-x**2 - y**2), (x, -oo, oo), (y, -oo, oo)))
expr = integrate(x**x, x)
print(expr)
expr = Integral(log(x)**2, x)
print(expr)
print(expr.doit())

integ = Integral((x**4 + x**2*exp(x) - x**2 - 2*x*exp(x) - 2*x -
    exp(x))*exp(x)/((x - 1)**2*(x + 1)**2*(exp(x) + 1)), x)
print(integ)
print(integ.doit())
integ = Integral(sin(x**2), x)
print(integ)
print(integ.doit())
integ = Integral(x**y*exp(-x), (x, 0, oo))
print(integ)
print(integ.doit())

print(limit(sin(x)/x, x, 0))

expr = x**2/exp(x)
print(expr.subs(x, oo))
print(limit(expr, x, oo))

expr = Limit((cos(x) - 1)/x, x, 0)
print(expr)
print(expr.doit())
print(limit(1/x, x, 0, '+'))
print(limit(1/x, x, 0, '-'))

expr = exp(sin(x))
print(expr.series(x, 0, 4))
print(x + x**3 + x**6 + O(x**4))
print(x*O(1))
print(expr.series(x, 0, 4).removeO())
print(exp(x - 6).series(x, x0=6))

f, g = symbols('f g', cls=Function)
print(differentiate_finite(f(x)*g(x)))

f = Function('f')
dfdx = f(x).diff(x)
print(dfdx.as_finite_difference())

f = Function('f')
d2fdx2 = f(x).diff(x, 2)
h = Symbol('h')
print(d2fdx2.as_finite_difference([-3*h,-h,2*h]))
print(finite_diff_weights(2, [-3, -1, 2], 0)[-1][-1])

x_list = [-3, 1, 2]
y_list = symbols('a b c')
print(apply_finite_diff(1, x_list, y_list, 0))


import numpy as np
import matplotlib.pyplot as plt

#%%

def function(x):
    return (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+x[7]+x[8]+x[9])**2

#%%
def Monte_Int(function, N):
    points_x = np.random.uniform(0,1,size=(10,N))
    points_y = np.random.uniform(0,100,size=N)

    N_adentro = 0
    N_box = 0
    for i in range(N):
        if(points_y[i] <= function(points_x[:,i])):
            N_adentro += 1
        else:
            N_box += 1

    return N_adentro*100/(N_adentro + N_box)

#%%

intentos = 1000
N = 1000
valores = np.zeros(intentos)
for i in range(intentos):
    valores[i] = Monte_Int(function,N)

integral = np.mean(valores)

print('El valor de la integral es: ', integral)

#%%

valorinshis = np.zeros(10000)
for i in range(10000):
    prueba_x = np.random.uniform(0,1,size=(10,N))
    valorinshis[i] = np.mean(function(prueba_x))
mi_val = np.mean(valorinshis)
mi_val

#%%
def f(x):
    return np.exp(-x*x-x)
def fun_vec(x):
    norm = np.sqrt(np.sum(x*x))
    return np.exp(-norm*norm)

def sampler(dist, N,dim, sigma_delta):

    lista = np.zeros((dim,N))
    propuesta = np.zeros(dim)
    for i in range(1,N):
        for j in range(dim):
            propuesta[j]  = lista[j,i-1] + sigma_delta * (np.random.random()-0.5)
        r = min(1,dist(propuesta)/dist(lista[:,i-1]))
        alpha = np.random.random()
        if(alpha<r):
            lista[:,i] = propuesta
        else:
            lista[:,i] = lista[:,i-1]
    return lista

#%%
x = np.linspace(-10,10)

numbers = sampler(f,10**5,1,1)

np.shape(numbers)

_ = plt.hist(Number_gen(f,10**5,1,1)[0], density='True', bins=x)
plt.plot(x,f(x))

#%%
x = np.linspace(-10,10,1000)
y = np.linspace(-10,10,1000)

xx, yy = np.meshgrid(x,y)

z = np.exp(-xx**2 - yy**2)
np.shape(z)

exp_2d_nums = Number_gen(fun_vec,10**5,2,1)

np.shape(exp_2d_nums)

_ = plt.hist2d(exp_2d_nums[0], exp_2d_nums[1], bins=100)

#%%
plt.pcolormesh(xx,yy,z)
plt.xlim(-3,3)
plt.ylim(-3,3)

#%%

I_MC = 2.27588*np.mean(np.cos(Number_gen(f,10**5,1,1)[0]))
I_MC
V_2 = np.mean(fun_vec(Number_gen(fun_vec,10**5,2,1)))

ver = fun_vec(Number_gen(fun_vec,10**5,2,1))
np.shape(ver)


import numpy as np
import matplotlib.pyplot as plt

def f(x):
    """Compute quadratic function."""
    return x ** 2

def compute_c(a, b):
    """Compute value c for golden section search."""
    c = b + ((a - b) / 1.618)
    return c, f(c)

def compute_d(a, b):
    """Compute value d for golden section search."""
    d = a + ((b - a) / 1.618)
    return d, f(d)

def compute_graphs(it_num, a, b, c, d, test_num, f):
    """
    Display various graphs illustrating the functionality of golden section
    search.
    """
    plt.clf()
    x = np.linspace(-2, 1, test_num)
    plt.ylim(0, 4)
    plt.xlim(-2, 1)
    y = f(x)
    plt.plot(x, y, color='black')
    plt.scatter(c, fc, color='green', marker='o', s=80)
    plt.scatter(d, fd, color='green', marker='o', s=80)
    if it_num == 0:
        lower = 0.05
        upper = -0.5
    elif it_num == 1:
        lower = 0.05
        upper = -0.5
    elif it_num == 2:
        lower = 0.05
        upper = -0.5
    elif it_num == 3:
        lower = 0.04
        upper = -0.5
    plt.annotate('$b$', (0, 0), (b - lower, upper), size=20)
    plt.annotate('$a$', (0, 0), (a - lower, upper), size=20)
    plt.annotate('$x_1$', (0, 0), (c - lower, upper), size=20)
    plt.annotate('$x_2$', (0, 0), (d - lower, upper), size=20)
    plt.vlines(b, 0, 4, color='black')
    plt.vlines(a, 0, 4, color='black')
    plt.vlines(c, 0, 4, color='black')
    plt.vlines(d, 0, 4, color='black')
    if fc > fd:
        plt.axvspan(a, c, color='r', alpha=0.5, lw=0)
    elif fc <= fd:
        plt.axvspan(d, b, color='r', alpha=0.5, lw=0)
    plt.xticks([])
    plt.yticks([])
    plt.savefig('golden_section_ex_%s.png' % (it_num))

if __name__ == "__main__":
    num = 4
    test_num = 100
    b = 1
    a = -2
    c, fc = compute_c(a, b)
    d, fd = compute_d(a, b)
    for it_num in range(num):
        compute_graphs(it_num, a, b, c, d, test_num, f)
        if fc < fd:
            b = d
            d = c
            fd = fc
            c, fc = compute_c(a, b)
        else:
            a = c
            c = d
            fc = fd
            d, fd = compute_d(a, b)


"""
Author : Achintya Gupta
Date Created : 22-09-2020
"""

"""
Problem Statement
------------------------------------------------
Q)  2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
    What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
"""
from utils import timing_decorator, find_factors
import numpy as np

@timing_decorator
def find_min_prod(N=20):
    D = {}
    for i in range(1, N+1):
        _factors = find_factors(i)
        fact, cnts = np.unique(_factors, return_counts=True)
        for fact, cnts in zip(fact, cnts):
            if fact in D:
                D[fact] = max(cnts, D[fact])
            else:
                D[fact] = cnts
    num = np.prod([k**v for k,v in D.items()])
    print(f'Minimum number divisible by all numbers between 1 <-> {N} : {num}')

find_min_prod(20)


import os
import numpy as np
import matplotlib.pyplot as plt
from tkinter import PhotoImage
from PIL import Image
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation

# Pelican Robot Parameters
L1 = L2 = 0.26 # Meters
I1 = 0.1213 # Kg m²
I2 = 0.01616 # Kg m²
LC1 = 0.0983 # meters
LC2 = 0.0229 # meters
M1 = 6.5225 # Kg
M2 = 2.0458 # Kg
GR = 9.80665 # m / s²

def direct_k(q1, q2):
    """
    Direct Kinematic of pelican robot:
        Obtain x-y-cordinates of each link with direct angles

    Parameters
    ----------
    q1 : Direct angle of link_1
    q2 : Direct angle of link_2

    Returns
    -------
    link_1 : x-y-cordinates
    link_2 : x-y-cordinates
    fnl_elmnt : x-y-cordinates of final element of robot
    """

    link_1 = [L1 * np.sin(q1), - L1 * np.cos(q1)]
    link_2 = [L2 * np.sin(q1 + q2), - L2 * np.cos(q1 + q2)]
    fnl_elmnt = [link_1[0] + link_2[0], link_1[1] + link_2[1]]

    return link_1, link_2, fnl_elmnt

def inverse_k(px, py):
    """
    Inverse kinematic of pelican robot:
        Obtain angles for each link to desired position

    Parameters
    ----------
    px : Value of x-position desired
    py : Value of y-position desired

    Returns
    -------
    q1 : Angle in radians of link 1 for the desired position
    q2 : Angle in radians of link 2 for the desired position
    """

    k = (px ** 2 + py ** 2 - L1 ** 2 - L2 ** 2) / (2 * L1 * L2)
    q2 = np.arctan2(np.sqrt(1 - (k ** 2)), k)
    q1 = np.arctan2(px, - py) - np.arctan2((L2 * np.sin(q2)), (L1 + L2 * np.cos(q2)))

    return [q1, q2]

class dynamic_model:
    """
    Dynamic model of pelican robot
    ------------------------------
    tau = M(q)\Ddot{q} + C(q, \dot{q})\dot{q} + G(q)
        M(q) := Inertial Matrix
        C(q, \dot{q}) := Centrifugal and Coriolis Forces Matrix
        G(q) := Gravitational Torques Vector
    """

    def M(q2):
        """
        Inertial Matrix

        Parameters
        ----------
        q2 : Angle of link 2
        """

        m_11 = M1 * LC1 ** 2 + M2 * (L1 ** 2 + LC2 ** 2 + 2 * L1 * LC2 * np.cos(q2)) + I1 + I2
        m_12 = M2 * (LC2 ** 2 + L1 * LC2 * np.cos(q2)) + I2
        m_21 = M2 * (LC2 ** 2 + L1 * LC2 * np.cos(q2)) + I2
        m_22 = M2 * LC2 ** 2 + I2

        return [[m_11, m_12], [m_21, m_22]]

    def C(q2, v1, v2):
        """
        Centrifugal and Coriolis Forces Matrix

        Parameters
        ----------
        q2 : Angle of link 2
        v1 : Velocity of link 1
        v2 : Velocity of link 2
        """

        c_11 = - ((M2 * L1 * LC2 * np.sin(q2)) * v2)
        c_12 = - ((M2 * L1 * LC2 * np.sin(q2)) * (v1 + v2))
        c_21 =   ((M2 * L1 * LC2 * np.sin(q2)) * v2)

        return [[c_11, c_12], [c_21, 0.0]]

    def G(q1, q2):
        """
        Gravitational Torques Vector

        Parameters
        ----------
        q1 : Angle of link 1
        q2 : Angle of link 2
        """

        g_11 = (M1 * LC1 + M2 * L1) * GR * np.sin(q1) + M2 * LC2 * GR * np.sin(q1 + q2)
        g_21 = M2 * LC2 * GR * np.sin(q1 + q2)
        return [g_11, g_21]

class binsrch:
    """
    Binary search

    ...

    Attribute
    ---------
    list : Values contained in a list

    Methods
    -------
    def max():
        Maximum list value by binary search

    def min():
        Minimum list value by binary search
    """

    def __init__(self, list):
        """
        Constructor
        """
        self.lst = list
        self.n = len(list) // 2

    def max(self):
        """
        Maximum list value by binary search
        """
        return max(binsrch.__max_value(self.lst[:self.n + 1]),
                   binsrch.__max_value(self.lst[-(self.n + 1):]))

    def min(self):
        """
        Minimum list value by binary search
        """
        return min(binsrch.__min_value(self.lst[:self.n + 1]),
                   binsrch.__min_value(self.lst[-(self.n + 1):]))

    def __max_value(list):
        """
        Recursive maximum value
        """
        if len(list) == 1:
            return list[0]
        else:
            mx = binsrch.__max_value(list[1:])
            return mx if mx > list[0] else list[0]

    def __min_value(list):
        """
        Recursive minimum value
        """
        if len(list) == 1:
            return list[0]
        else:
            mn = binsrch.__min_value(list[1:])
            return mn if mn < list[0] else list[0]

def plot_link(p_i, p_f, *args, **kwargs):
    """
    Function to graph a link in R²

    Parameters
    -----------
    p_i : list [x_i, y_i]
        Initial point of link in R²

    p_f : list [x_f, y_f]
        Final point of link in R²
    """

    plt.plot([p_i[0], p_i[0] + p_f[0]], [p_i[1], p_i[1] + p_f[1]], *args, **kwargs)
    # If the color is not specified, the np.scatter decorator will default to itself.
    if len(args) == 0:
        plt.scatter(p_i[0], p_i[1])
    # Otherwise the color is used regardless of the line style. Since the line
    # style does not attribute to np.sactter.
    else:
        color = ''.join([args[0][i] for i in range(len(args[0]))
                         if args[0][i] not in list(plt.Line2D.lineStyles) + ['.']])
        plt.scatter(p_i[0], p_i[1], facecolor=color)

class realtime:
    """
    Generate animation of the trajectory and how it was reducing the qt error
    until reaching the desired position.

    ...

    Methods
    -------
    def show(ts, qs, qt, dp):
        Once each of the necessary values for the animation are obtained, it is
        shown in the established time interval as it was changing.
    """

    def __init__(self):
        """
        Constructor

        In the constructor the shape of the figure is initialized and designed
        for animation.
        """

        # Number of subplots to animate
        self.fig, (self.robot, self.qt) = plt.subplots(2, 1)
        # Change window title
        # self.fig.canvas.set_window_title('Pelican Robot: Simulation')
        # Change icon window
        if os.path.exists('../pelcnrbt/images'):
            plc_anim_w = plt.get_current_fig_manager()
            img = PhotoImage(file='images/pelican-robot-icon.png')
            plc_anim_w.window.tk.call('wm', 'iconphoto', plc_anim_w.window._w, img)

        # Limits and appearance of the pelican robot animation
        self.robot.set_xlim((-0.6, 0.6))
        self.robot.set_ylim((-0.6, 0.6))
        self.robot.set_xticks(np.arange(-0.6, 0.6, 0.1))
        self.robot.set_yticks(np.arange(-0.6, 0.6, 0.1))
        self.robot.grid(which='both')
        self.robot.set_xticklabels([])
        self.robot.set_yticklabels([])
        self.robot.set_aspect('equal', adjustable='box')

        # Some elements for the animation of the error qt
        self.qt.set_title("Graph of how the error ($ \\tilde{q} $) was reduced.")
        self.qt.set_xlabel("$ t(s) $", fontsize='large')
        self.qt.set_ylabel("$ \\tilde{q} $", rotation='horizontal', fontsize='large')
        self.qt.grid('on', linestyle='--')

    def show(self, ts, qs, qt, dp, ctrl_type):
        """
        Function to generate the animation using the parameters necessary for
        the animation.

        Parameters
        ----------
        ts : List with the integration step intervals for the set time range.
        qs : List with the values of the angles of each link of each iteration
             until reaching the desired position.
        qt : List with the values of how the error qt was changing in each
             iteration until reaching the desired position.
        dp : Desired position in the form [Px, Py] to draw the point the robot
             should reach in the animation.
        ctrl_type : str
            PD := Proportional control plus velocity feedback and Proportional
                  Derivative(PD) control
            PD-GC := PD control with gravity compensation
            PD-dGC := PD control with desired gravity compensation
        """

        print('Realtime animation for desired position : [{}, {}], Start!'.format(dp[0], dp[1]))

        # Get error for each link
        q1e = [qt[n][0] for n in range(len(qt))]
        q2e = [qt[n][1] for n in range(len(qt))]
        # Get limits of qt plot
        self.qt.set_xlim((binsrch(ts).min(), binsrch(ts).max()))
        qtmin = min(binsrch(q1e).min(), binsrch(q2e).min())
        qtmax = max(binsrch(q1e).max(), binsrch(q2e).max())
        self.qt.set_ylim((qtmin, qtmax))
        # Text for formulas in qt plot
        if ctrl_type == 'PD':
            str_controller = r'$ \tau = K_{p} \tilde{q} - K_{v} \dot{q} $'
        elif ctrl_type == 'PD-GC':
            str_controller = r'$ \tau = K_{p} \tilde{q} - K_{v} \dot{q} + g(q) $'
        elif ctrl_type == 'PD-dGC':
            str_controller = r'$ \tau = K_{p} \tilde{q} - K_{v} \dot{q} + g(q_d) $'

        str_qt = r'$ \tilde{q} := q_d - q(t) $'
        self.qt.text((max(ts) / 2), (qtmax), 'Controller equation',
                     horizontalalignment='center', verticalalignment='top')
        self.qt.text((max(ts) / 2), (qtmax - 200 * 0.001), str_controller,
                     horizontalalignment='center', verticalalignment='top')
        self.qt.text((max(ts) / 2), (qtmax - 500 * 0.001), str_qt, horizontalalignment='center',
                     verticalalignment='top')
        # Some elements for the animation of the pelican robot
        self.robot.set_title(r"Pelican Robot: Trajectory to [{}, {}]".format(dp[0], dp[1]))
        self.robot.scatter(dp[0], dp[1], facecolor='k')
        # For pelican robot animation
        link_1, = self.robot.plot([], [], 'r', lw=2)
        link_2, = self.robot.plot([], [], 'b', lw=2)
        # For qt animation
        q1_error, = self.qt.plot([], [], 'r', lw=2, label="$ \\tilde{q_{1}} $")
        q2_error, = self.qt.plot([], [], 'b', lw=2, label="$ \\tilde{q_{2}} $")
        self.qt.legend(loc='upper right')

        def __animate(i):
            """
            Generate animation
            """

            if i == len(qs) - 1:
                print('Realtime animation donde!, Closed window.')
                plt.close('all')

                return link_1, link_2, q1_error, q2_error,

            else:
                # print(f'frame: {i}') # Debug: May be useful to stop
                link_1.set_data([0.0, direct_k(qs[i][0], qs[i][1])[0][0]],
                                [0.0, direct_k(qs[i][0], qs[i][1])[0][1]])

                link_2.set_data([direct_k(qs[i][0], qs[i][1])[0][0],
                                 direct_k(qs[i][0], qs[i][1])[2][0]],
                                [direct_k(qs[i][0], qs[i][1])[0][1],
                                 direct_k(qs[i][0], qs[i][1])[2][1]])

                q1_error.set_data(ts[:i - len(ts)], q1e[:i - len(q1e)])
                q2_error.set_data(ts[:i - len(ts)], q2e[:i - len(q2e)])

                return link_1, link_2, q1_error, q2_error,

        ani = FuncAnimation(self.fig, __animate, interval=1, blit=True, frames=len(qs),
                            repeat=False)

        self.fig.tight_layout()
        plt.show()


from typing import Dict
import random
from numba import jit, njit, prange, stencil
import numpy as np
from playground import ColorizedLogger, timeit
import math

class NumbaPlay:
    logger: ColorizedLogger
    conf: Dict

    def __init__(self, conf):
        self.logger = ColorizedLogger(f'NumbaPlay', 'blue')
        self.logger.info(f"Initialized NumbaPlay..")
        self.conf = conf

    @staticmethod
    @jit(nopython=True)
    def pythagorean_theorem(x: int, y: int):
        return math.sqrt(x ** 2 + y ** 2)

    @staticmethod
    def pythagorus(x, y):
        return math.sqrt(x ** 2 + y ** 2)

    def pythagorean_test(self):
        self.logger.info(f"Starting pythagorean tests for x={self.conf['x']}, y={self.conf['x']}..")
        with timeit(custom_print='Numba pythagorean took {duration:.5f} sec(s)'):
            self.pythagorean_theorem(self.conf['x'], self.conf['y'])
        with timeit(custom_print='No Numba pythagorean took {duration:.5f} sec(s)'):
            self.pythagorus(self.conf['x'], self.conf['y'])

    @staticmethod
    @njit
    def monte_carlo_pi(nsamples):
        acc = 0
        for i in range(nsamples):
            x = random.random()
            y = random.random()
            if (x ** 2 + y ** 2) < 1.0:
                acc += 1
        return 4.0 * acc / nsamples

    def monte_carlo_pi_test(self):
        self.logger.info(f"Starting monte_carlo_pi tests for nsamples={self.conf['nsamples']}..")
        with timeit(custom_print='Numba monte_carlo_pi took {duration:.5f} sec(s)'):
            self.monte_carlo_pi(self.conf['nsamples'])

    @staticmethod
    @njit(parallel=True)
    def prange(A):
        s = 0
        # Without "parallel=True" in the jit-decorator
        # the prange statement is equivalent to range
        for i in prange(A.shape[0]):
            s += A[i]
        return s

    def prange_test(self):
        self.logger.info(f"Starting prange tests for A={self.conf['A']}..")
        with timeit(custom_print='Numba prange took {duration:.5f} sec(s)'):
            self.prange(np.arange(self.conf['A']))

    @staticmethod
    @jit(nopython=True, parallel=True)
    def logistic_regression(X, Y, w, iterations):
        for i in range(iterations):
            w -= np.dot(((1.0 /
                          (1.0 + np.exp(-Y * np.dot(X, w)))
                          - 1.0) * Y), X)
        return w

    def logistic_regression_test(self):
        self.logger.info(f"Starting logistic_regression tests for "
                         f"X={self.conf['x1']}, Y={self.conf['x2']}, "
                         f"w={self.conf['w']}, iterations={self.conf['iterations']}..")
        with timeit(custom_print='Numba logistic_regression took {duration:.5f} sec(s)'):
            self.logistic_regression(np.random.rand(self.conf['x1'], self.conf['x2']),
                                     np.random.rand(self.conf['x1']),
                                     np.zeros([self.conf['x2']]),
                                     self.conf['iterations'])


from matplotlib import pyplot as plt
import scipy
from scipy.integrate import quad
import scipy.special as special
import numpy as np
from dolo.numeric.processes_iid import *

σ = 0.1
μ = 0.2
N = 10

x=2

assert(x==2)

## Polynomial
def f(x):
    return x**2

## Discretize Normal distribution with GH and EP methods

distNorm = UNormal(mu=μ, sigma=σ)
disNorm_gh = distNorm.discretize()
disNorm_ep = distNorm.discretize(N=N, method='equiprobable')

disNorm_gh.n_inodes(0) # number of nodes
# disNorm_gh.inode(0,4) # 4th node
# disNorm_gh.iweight(0,4) # weight associated to 4th node

########################################################################
############################# Plots ####################################
########################################################################
## Plot Equiprobable
weights_ep, nodes_ep = np.array( [*zip(*[*disNorm_ep.iteritems(0)])] )
plt.plot(nodes_ep, nodes_ep*0, '.')
xl = plt.xlim()
xvec = np.linspace(xl[0], xl[1], 100)
pdf = scipy.stats.norm.pdf(xvec, loc=μ, scale=σ)
plt.plot(xvec, pdf)
plt.grid()

## Plot Gauss Hermite nodes
weights_gh, nodes_gh = np.array( [*zip(*[*disNorm_gh.iteritems(0)])] )
plt.plot(nodes_gh, nodes_gh*0, '.')
xl = plt.xlim()
xvec = np.linspace(xl[0], xl[1], 100)
pdf = scipy.stats.norm.pdf(xvec, loc=μ, scale=σ)
plt.plot(xvec, pdf)
plt.grid()

## Plot Random draws
M=1000
s_MC = np.random.normal(μ, σ, M)

count, bins, ignored = plt.hist(s_MC, 30, density=True)
plt.plot(bins, 1/(σ * np.sqrt(2 * np.pi)) * np.exp( - (bins - μ)**2 / (2 * σ **2) ), linewidth=2, color='r')
plt.show()

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

## Compute expectation of the normally distributed variable

expval_normal = quad(lambda x: f(x)/np.sqrt(2*np.pi*σ**2)*np.exp(-(x-μ)**2/(2*σ**2)), -np.inf,np.inf)[0]

## Compute the mean of random draws
expval_MC = np.array([f(s_MC[j]) for j in range(0,M)]).sum() / M

## Compute ∑(f(ϵ)*w_ϵ) for each discretization method

expval_gh = np.array([f(disNorm_gh.inode(0,j))*disNorm_gh.iweight(0,j) for j in range(disNorm_gh.n_inodes(0))]).sum()
expval_ep = np.array([f(disNorm_ep.inode(0,j))*disNorm_ep.iweight(0,j) for j in range(disNorm_ep.n_inodes(0))]).sum()

## Compare

expval_normal
expval_gh
expval_ep
expval_MC

assert(abs(expval_gh-expval_normal)<0.1)
assert(abs(expval_ep-expval_normal)<0.1)

##################################################################
############################# UNIFORM ###########################
##################################################################

a = -1
b = 1 #### CANNOT PUT 0
distUni = Uniform(a, b)
disUni = distUni.discretize(N=10)
### Here there is an issue with the uniform

## Random draws
M=1000
s_MC = np.random.uniform(a, b, M)

count, bins, ignored = plt.hist(s_MC, 10, density=True)
plt.plot(bins, np.ones_like(bins)*(1/(b-a)), linewidth=2, color='r')
plt.show()

## Plot Equiprobable

weights_uni, nodes_uni = np.array( [*zip(*[*disUni.iteritems(0)])] )
plt.plot(nodes_uni, nodes_uni*0, '.')
xl = plt.xlim(a,b)
xvec = np.linspace(xl[0], xl[1], 100)
pdf = scipy.stats.uniform.pdf(xvec,a,b-a)
plt.plot(xvec, pdf)
plt.grid()

## Compute the mean of random draws
expval_MC = np.array([f(s_MC[j]) for j in range(0,M)]).sum() / M
expval_MC

# Compute ∑(f(ϵ)*w_ϵ) for each discretization method
expval_ep = np.array([f(disUni.inode(0,j))*disUni.iweight(0,j) for j in range(disUni.n_inodes(0))]).sum()

expval_ep
expval_MC

##################################################################
############################# LOGNORMAL ###########################
##################################################################

σ = 0.1
μ = 0.3

logn = LogNormal(μ=μ, σ=σ)
logn.μ
logn.σ

distLog = LogNormal(μ, σ)
disLog = distLog.discretize(N=10)
### Here there is an issue with the uniform

## Random draws
M=1000
s_MC = np.random.lognormal(μ, σ, M)

count, bins, ignored = plt.hist(s_MC, 10, density=True)
x = np.linspace(min(bins), max(bins), 10000)
pdf = (np.exp(-(np.log(x) - μ)**2 / (2 * σ**2))  / (x * σ * np.sqrt(2 * np.pi)))
plt.plot(x, pdf, linewidth=2, color='r')
plt.axis('tight')
plt.show()

## Plot Equiprobable

weights_Log, nodes_Log = np.array( [*zip(*[*disLog.iteritems(0)])] )
plt.plot(nodes_Log, nodes_Log*0, '.')
xl = plt.xlim()
xvec = np.linspace(xl[0], xl[1], 100)
xvec
pdf = scipy.stats.lognorm.pdf(xvec,s=σ, loc=μ)
plt.plot(xvec, pdf)
pdf2 = (np.exp(-(np.log(x) - μ)**2 / (2 * σ**2))  / (x * σ * np.sqrt(2 * np.pi)))
plt.plot(x, pdf2)
plt.grid()

xvec = np.linspace(0,1,100)
ppf0 = scipy.stats.lognorm.ppf(xvec,s=σ, loc=μ, scale=np.exp(μ) )
ppf1 = distLog.ppf(xvec)

plt.plot(xvec, ppf0)
plt.plot(xvec, ppf1)

## Compute the mean of random draws
expval_MC = np.array([f(s_MC[j]) for j in range(0,M)]).sum() / M

# Compute ∑(f(ϵ)*w_ϵ) for each discretization method
expval_ep = np.array([f(disLog.inode(0,j))*disLog.iweight(0,j) for j in range(disLog.n_inodes(0))]).sum()

expval_ep
expval_MC

##################################################################
############################# BETA ###########################
##################################################################

α = 2
β = 5

distbeta = Beta(α, β)
disbeta = distbeta.discretize(N=10)
### Here there is an issue with the uniform

## Random draws
M=1000
s_MC = np.random.beta(α, β, M)

count, bins, ignored = plt.hist(s_MC, 10, density=True)
x = np.linspace(min(bins), max(bins), 10000)
pdf = scipy.stats.beta.pdf(x,α, β)
plt.plot(x, pdf, linewidth=2, color='r')
plt.axis('tight')
plt.show()

## Plot Equiprobable
weights_beta, nodes_beta = np.array( [*zip(*[*disbeta.iteritems(0)])] )
plt.plot(nodes_beta, nodes_beta*0, '.')
xl = plt.xlim()
xvec = np.linspace(xl[0], xl[1], 100)
pdf = scipy.stats.beta.pdf(xvec,α, β)
plt.plot(xvec, pdf)
plt.grid()

## Compute the mean of random draws
expval_MC = np.array([f(s_MC[j]) for j in range(0,M)]).sum() / M

# Compute ∑(f(ϵ)*w_ϵ) for each discretization method
expval_ep = np.array([f(disbeta.inode(0,j))*disbeta.iweight(0,j) for j in range(disbeta.n_inodes(0))]).sum()

expval_ep
expval_MC

#################################################################################
############################ SOME EXTRA STUFF  ##################################
#################################################################################
#################################################################################

logn = LogNormal(μ=μ, σ=σ)
logn.μ
logn.σ

dp = logn.discretize(N=10)
dp2 = logn.discretize(N=10, method='equiprobable')

nodes = np.array([x for (w,x) in dp.iteritems(0)])

plt.plot(nodes, nodes*0,'.')
plt.xlim(-1,2)
plt.grid()

res_gh = n.discretize(10)
res_ep = n.discretize(10, method='equiprobable')

for (w,x) in res_ep.iteritems(0):
    print(w,x)

# neglect integration nodes whose probability is smaller than 1e-5
for (w,x) in res_gh.iteritems(0,eps=1e-5):
    print(w,x)

def f(x):
    return x**2

val = quad(lambda u: f(u)/np.sqrt(2*np.pi*σ**2)*np.exp(-(u-μ)**2/(2*σ**2)), -5, 5)

v0 = sum([f(x)*w for (w,x) in res_gh.iteritems(0)])
v1 = sum([f(x)*w for (w,x) in res_ep.iteritems(0)])

print(v0, v1, val)

sim = norm.simulate(10000,2)

sim.mean()
sim.std()

dis = n.discretize(N=50, method='equiprobable')

weights, nodes = np.array( [*zip(*[*dis.iteritems(0)])] )

plt.plot(nodes, nodes*0, '.')
xl = plt.xlim()

xvec = np.linspace(xl[0], xl[1], 100)
pdf = scipy.stats.norm.pdf(xvec)
plt.plot(xvec, pdf)
plt.grid()

from scipy.stats import beta
import matplotlib.pyplot as plt
import numpy as np
a = 2
b = 2
x = np.arange (-50, 50, 0.1)
y = beta.pdf(x,a,b, scale=100, loc=-50)
plt.plot(x,y)

from scipy.stats import beta
import matplotlib.pyplot as plt
import numpy as np
a =  2
b = 4
x = np.linspace(-50, 50, 100)
y1 = beta.pdf(x,2, 2, scale=100, loc=-50)
y2 = beta.pdf(x,3, 3, scale=100, loc=-50)
y3 = beta.pdf(x, 4,4, scale=100, loc=-50)

plt.plot(x, y1, "-", x, y2, "r--", x, y3, "g--")

###########################
logn = LogNormal(μ=μ, σ=σ)
logn.μ
logn.σ

dp = logn.discretize(N=10)
dp2 = logn.discretize(N=10, method='equiprobable')

nodes = np.array([x for (w,x) in dp.iteritems(0)])

plt.plot(nodes, nodes*0,'.')
plt.xlim(-1,2)
plt.grid()

res_gh = n.discretize(10)
res_ep = n.discretize(10, method='equiprobable')

for (w,x) in res_ep.iteritems(0):
    print(w,x)

# neglect integration nodes whose probability is smaller than 1e-5
for (w,x) in res_gh.iteritems(0,eps=1e-5):
    print(w,x)

def f(x):
    return x**2

val = quad(lambda u: f(u)/np.sqrt(2*np.pi*σ**2)*np.exp(-(u-μ)**2/(2*σ**2)), -5, 5)

v0 = sum([f(x)*w for (w,x) in res_gh.iteritems(0)])
v1 = sum([f(x)*w for (w,x) in res_ep.iteritems(0)])

print(v0, v1, val)

sim = norm.simulate(10000,2)

sim.mean()
sim.std()

dis = n.discretize(N=50, method='equiprobable')

weights, nodes = np.array( [*zip(*[*dis.iteritems(0)])] )

plt.plot(nodes, nodes*0, '.')
xl = plt.xlim()

xvec = np.linspace(xl[0], xl[1], 100)
pdf = scipy.stats.norm.pdf(xvec)
plt.plot(xvec, pdf)
plt.grid()


import numpy as np

def transposeMatrix(m):
    return map(list,zip(*m))

def getMatrixMinor(m,i,j):
    rows = len(m) - 1
    ret = [[0 for u in range(rows)] for v in range(rows)]

    for r in range(len(m)):
        for c in range(len(m)):
            if r != i and c != j:
                rr = (r > i) * -1 + r
                cc = (c > j) * -1 + c
                ret[rr][cc] = m[r][c]
    return ret

def getMatrixDeternminant(m):
    #base case for 2x2 matrix
    if len(m) == 2:
        return m[0][0]*m[1][1] * 1.0 -m[0][1]*m[1][0] * 1.0

    determinant = 0
    for c in range(len(m)):
        determinant += ((-1)**c)*m[0][c]*getMatrixDeternminant(getMatrixMinor(m,0,c))
    return determinant

def getMatrixInverse(m):
    determinant = getMatrixDeternminant(m)
    #special case for 2x2 matrix:
    if len(m) == 2:
        return [[m[1][1] * 1.0 /determinant, -1.0 * m[0][1] / determinant],
                [-1*m[1][0] * 1.0 /determinant, m[0][0] * 1.0 / determinant]]

    #find matrix of cofactors
    cofactors = []
    for r in range(len(m)):
        cofactorRow = []
        for c in range(len(m)):
            minor = getMatrixMinor(m,r,c)
            cofactorRow.append(((-1)**(r+c)) * getMatrixDeternminant(minor))
        cofactors.append(cofactorRow)
    cofactors = transposeMatrix(cofactors)
    for r in range(len(cofactors)):
        for c in range(len(cofactors)):
            cofactors[r][c] = cofactors[r][c] * 1.0 / determinant
    return cofactors

def gauss_jordan(m, eps = 1.0/(10**10)):
    """Puts given matrix (2D array) into the Reduced Row Echelon Form.
     Returns True if successful, False if 'm' is singular.
    NOTE: make sure all the matrix items support fractions! Int matrix will NOT work!
     Written by Jarno Elonen in April 2005, released into Public Domain"""

    (h, w) = (len(m), len(m[0]))
    maxrow = [0]
    for y in range(0,h):
        maxrow[0] = y
        for y2 in range(y+1, h):    # Find max pivot
            b = int(abs(m[y2][y]) > abs(m[maxrow[0]][y]))
            maxrow[0] = maxrow[0] * (1 - b) + y2 * b
        (m[y], m[maxrow[0]]) = (m[maxrow[0]], m[y])
        # if abs(m[y][y]) <= eps:     # Singular?
        #     return False
        for y2 in range(y+1, h):    # Eliminate column y
            c = m[y2][y] / m[y][y]
            for x in range(y, w):
                m[y2][x] -= m[y][x] * c
    for y in range(h-1, 0-1, -1): # Backsubstitute
        c  = m[y][y]
        for y2 in range(0,y):
            for x in range(w-1, y-1, -1):
                m[y2][x] -=  m[y][x] * m[y2][y] / c
        m[y][y] /= c
        for x in range(h, w):       # Normalize row y
            m[y][x] /= c
    return True

def gj_inv(M):
    """
    return the inv of the matrix M
    """
    # clone the matrix and append the identity matrix
    # [int(i==j) for j in range_M] is nothing but the i(th row of the identity matrix

    m2 = [row[:]+[int(i==j) for j in range(len(M) )] for i,row in enumerate(M) ]
    # extract the appended matrix (kind of m2[m:,...]
    return [row[len(M[0]):] for row in m2] if gauss_jordan(m2) else None

import math
def cholesky(A):
    """Performs a Cholesky decomposition of A, which must
    be a symmetric and positive definite matrix. The function
    returns the lower variant triangular matrix, L."""
    n = len(A)

    # Create zero matrix for L
    L = [[0.0] * n for i in xrange(n)]

    # Perform the Cholesky decomposition
    for i in xrange(n):
        for k in xrange(i+1):
            tmp_sum = sum(L[i][j] * L[k][j] for j in xrange(k))

            if (i == k): # Diagonal elements
                # LaTeX: l_{kk} = \sqrt{ a_{kk} - \sum^{k-1}_{j=1} l^2_{kj}}
                L[i][k] = math.sqrt(abs(A[i][i] - tmp_sum))
            else:
                # LaTeX: l_{ik} = \frac{1}{l_{kk}} \left( a_{ik} - \sum^{k-1}_{j=1} l_{ij} l_{kj} \right)
                L[i][k] = (1.0 / L[k][k] * (A[i][k] - tmp_sum))
    return L

def cholesky_inv(A):
    L = cholesky(A)

    L_inv = [[0 for r in range(len(L))] for c in range(len(L))]
    n = len(L)
    for k in range(n):
        L_inv[k][k] = 1.0 / L[k][k]
        for i in range(k, n):
            v = 0
            for j in range(k, i-1):
                v += L[i][j] * L_inv[j][k]

            L[i][k] = -1 * v / L[i][i]

    L_inv_T = transposeMatrix(L_inv)
    return np.matmul(np.array(L_inv_T), np.array(L_inv))

def cond_assign(b, x, y):
    return b * x + (1 - b) * y

def gj_inv_alternate(A):
    n = len(A)
    X = [[A[j][i] for i in range(n)] for j in range(n)]
    I = [[int(i==j) for i in range(n)] for j in range(n)]

    for j in range(n):
        for i in range(j, n):
            b = int(X[i][j] != 0)
            for k in range(0, n):
                s = X[j][k]
                X[j][k] = cond_assign(b, X[i][k], X[j][k])
                X[i][k] = cond_assign(b, s, X[i][k])

                s = I[j][k]
                I[j][k] = cond_assign(b, I[i][k], I[j][k])
                I[i][k] = cond_assign(b, s, I[i][k])

            t = cond_assign(b, 1.0 / X[j][j], 1.0)
            for k in range(n):
                X[j][k] = t * X[j][k]
                I[j][k] = t * I[j][k]

            for L in range(j):
                t = -1 * X[L][j]
                for k in range(n):
                    X[L][k] = cond_assign(b, X[L][k] + t * X[j][k], X[L][k])
                    I[L][k] = cond_assign(b, I[L][k] + t * I[j][k], I[L][k])

            for L in range(j+1, n):
                t = -1 * X[L][j]
                for k in range(n):
                    X[L][k] = cond_assign(b, X[L][k] + t * X[j][k], X[L][k])
                    I[L][k] = cond_assign(b, I[L][k] + t * I[j][k], I[L][k])
    return I

X = [[1.0, 2.0, 3.0], [2.0, 6.0, 1.0], [4.0, 5.0, 6.0]]
X_inv = gj_inv_alternate(X)
X_inv_np = np.linalg.inv(np.array(X))

print np.array(X)
print np.array(X_inv)
print X_inv_np
print
print np.matmul(np.array(X), np.array(X_inv))
print np.matmul(np.array(X), X_inv_np)


# bfgs algorithm local optimization of a convex function
from scipy.optimize import minimize
from numpy.random import rand

# objective function
def objective(x):
	return x[0]**2.0 + x[1]**2.0

# derivative of the objective function
def derivative(x):
	return [x[0] * 2, x[1] * 2]

# define range for input
r_min, r_max = -5.0, 5.0
# define the starting point as a random sample from the domain
pt = r_min + rand(2) * (r_max - r_min)
# perform the bfgs algorithm search
result = minimize(objective, pt, method='BFGS', jac=derivative)
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))


# -*- coding: utf-8 -*-
"""
Created on Sun May  9 09:27:12 2021

@author: Raza_Jutt
"""

import random

rand_x = []
rand_y = []
rand_z = []
for i in range(0,1000000):
    rand_x.append(random.uniform(0.00,1.00))
    rand_y.append(random.uniform(0.00,1.00))
    rand_z.append(random.uniform(0.00,1.00))

count = 0
for i in range(0,1000000):
    x=rand_x[i]
    y=rand_y[i]
    z=rand_z[i]
    if (pow(x,2)+pow(y,2)+pow(z,2)) <= 1.00:
        count = count + 1

print(count/1000000)

x = []
y = []
z = []
for i in range(0,101):
    for j in range(0,101):
        for k in range(0,101):
            if (pow(i/100,2)+pow(j/100,2)+pow(k/100,2)) <= 1.00:
                x.append(i/100)
                y.append(j/100)
                z.append(k/100)

# importing mplot3d toolkits, numpy and matplotlib
import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()

# syntax for 3-D projection
ax = plt.axes(projection ='3d')

# defining all 3 axes
x = np.array(x)
y = np.array(y)
z = np.array(z)

# plotting
ax.plot3D(x, y, z, 'blue')
ax.set_title('Sphare 3D 0 to 1 ')
plt.show()


import numpy as np

def muskingum(in_flow:np.ndarray, del_t:float, k:float, x:float) -> np.ndarray:

	'''
	Returns routed flow using Muskingum routing method
	(Lumped channel routing)

	Units:
	------
	in_flow  - m^3/s
	del_t    - hr
	x        - hr

	'''

	# calculate timesetp
	n_step = in_flow.shape[0]

	# create a zero array of out_flow
	out_flow = np.zeros(n_step)

	C0 = (-k*x+0.5*del_t) / (k-k*x+0.5*del_t)
	C1 = ( k*x+0.5*del_t) / (k-k*x+0.5*del_t)
	C2 = 1- (C0+C1)

	# initial condition
	out_flow[0] = in_flow[0]

	for t in np.arange(1,n_step):

		out_flow[t] = C0*in_flow[t] + C1*in_flow[t-1] + C2*out_flow[t-1]

	return out_flow


import numpy as np
import skfuzzy as fuzz
import matplotlib.pyplot as mpl
# Step 1 : Fuzzify Input / Output
#Input  Variables have the same domain/ Universe functions
service = np.arange(0,11,1)
food    = np.arange(0,11,1)

# Output Variables Domain
tip = np.arange(0,101,1)

# Input Membership Functions
# Service
ser_p = fuzz.gaussmf(service ,0,1.5)
ser_g = fuzz.gaussmf(service ,5,1.5)
ser_e = fuzz.gaussmf(service ,10,1.5)

# Food
foo_r = fuzz.trapmf(food , [0, 0, 1, 3])
foo_d = fuzz.gaussmf(food, 10,1.5)

# Output tip
tip_ch  = fuzz.trimf(tip, [0, 15, 30])
tip_ave = fuzz.trimf(tip, [25, 45, 70])
tip_gen = fuzz.trimf(tip, [60, 80, 100])
print 'tip_cheap', tip_ch
print 'tip_average', tip_ave
print 'tip_generous', tip_gen

# Here I'll use your example service == 2 and food == 4
# These return a single value, which is combined using the rules in Step 3
# and operates on tip membership functions in Step 4
food_1 = fuzz.interp_membership(food, foo_r, 4.)
food_3 = fuzz.interp_membership(food, foo_r, 4.)
service_1 = fuzz.interp_membership(service, ser_p, 2)
service_2 = fuzz.interp_membership(service, ser_g, 2)
service_3 = fuzz.interp_membership(service, ser_e, 2)
print 'food_1', food_1
print 'food_3', food_3
print 'service_1', service_1
print 'service_2', service_2
print 'service_3', service_3

# First rule is OR - this is a max operator
rule1 = np.fmax(food_1, service_1)  # Doable with inbuilt Python max(), but np.fmax is more general
rule2 = service_2  # No combination, this is just passed
rule3 = np.fmax(food_3, service_3)
print 'rule1', rule1
print 'rule2', rule2
print 'rule3', rule3

# Product is simple multiplication of weight w/fuzzy membership function
# min is np.fmin(weight, membership)
# Here I use product because that appears to be your preference
imp1 = rule1 * tip_ch
imp2 = rule2 * tip_ave
imp3 = rule3 * tip_gen

print 'imp1', imp1
print 'imp2', imp2
print 'imp3', imp3

# Aggregate
aggregate_membership = np.fmax(imp1, np.fmax(imp2, imp3))
mpl.plot(aggregate_membership)
mpl.show()

# Defuzzify
tip = fuzz.defuzz(tip, aggregate_membership, 'centroid')
print tip # correct answer is
print np.allclose(tip, 19.4, atol=0.05)