# -*- coding: utf-8 -*-
"""Untitled13.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/121CoD0XoW3rQC8gJpL_9UUvDF3TjLgXZ
"""

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

"""## A. Application of Improved Multiple Linear Regression Method in Oilfield Output Forecasting.
Liang Guo, Xianghui Deng. 2009 IEEE, DOI: 10.1109/ICIII.2009.39. pp 133-136.
https://ieeexplore.ieee.org/document/5370533
This paper uses a multiple linear regression model to predict production of an oil field, the coefficients of the linear regression model are calculated using matrix operations which they call Model A, and some coefficients of the general model are discarded and now called Model B. The last model B has more predictive power because it has a lower average percentage of error.

## B. When we want to implement an algorithm or methodology published in an article, the first thing to do is repeat the ideas proposed by the authors with the same data they used to ensure that we are understanding and implementing it correctly. In this exercise we will repeat what was done by the authors of this article in order to validate and better understand what they did. That is, using the data they indicate, you must obtain the same equation (10) of multiple linear regression that they indicate on page 135. As there are really few data, the authors obtain the coefficients of the regression model using the pseudoinverse matrix, as It is explained in the article. In the article, the authors say that this equation was obtained with data from the years from 1983 to 2001, when they actually used data from 1980 to 2001, and erroneously did not publish the missing years in the table on page 135. That is, you must apply the multiple linear regression method with the data from the years 1980 to 2001 and obtain the regression equation shown on page 135. Note that for this section you should only use the data from 1980 to 2001, because they are those they use as training data.
"""

df1 = pd.read_csv('https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/data.csv',
                  sep=',',
                  names=["year", "x1", "x2", "x3", 'x4', 'x5', 'x6', 'x7', 'x8', 'y']);
df1

x= df1[df1['year']<2002]
x= x.filter(['x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8'])
x.insert(0, 'x0', 1)

y= df1[df1['year']<2002]
y= y.filter(["y"])

x

y

x_t = x.transpose()
x_t_x= x_t.dot(x)
x_t_y= x_t.dot(y)
x_t_x_inv = pd.DataFrame(np.linalg.pinv(x_t_x.values), x_t_x.columns, x_t_x.index)
x_t_x_inv_x_t = x_t_x_inv.dot(x_t)

"""![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/betas.png)"""

betas= x_t_x_inv.dot(x_t_y)

betas

"""## C. Indicate the dimensions of each of the following matrices, in the case of training data from 1980 to 2001:

![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/dim1.png)

![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/dim2.png)

## D. Calculate the value of the matrix determinant ![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/x_t_x.PNG) and the product ![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/x_t_x_inv_x_t_x.PNG) According to these two results, what can you say about the convenience of using the pseudo-inverse method to obtain the solution of this linear least squares regression problem? Do you consider it reliable? Explain your conclusions.

The determinant of matrix ![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/x_t_x.PNG) is different of null, For this reason using the pseudo-inverse is reliable, since there are no several solutions to the problem
"""

det_x_t_x = np.linalg.det(x_t_x)
det_x_t_x

x_t_x_inv_x_t_x = x_t_x_inv.dot(x_t_x)
x_t_x_inv_x_t_x

"""## E. Get the 8 Pearson correlation coefficients of the variable 𝑌 with respect to each of the 8 independent variables Xi. Use only training data, using only data from 1980 to 2001.

![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Linear%20Regression/pearson.png)
"""

#Least square method, this will return the Variance and covariance of X and Y (X,Y)= (PetalLength, PetalWidth)
def Least_square(x,y):
  mx = x.mean()
  my = y.mean()
  u=0
  d=0
  e=0
  i=0;
  while(i< len(x)):
      u += ((x[i] - mx)*(y[i] - my))
      d += ((x[i] - mx)**2)
      e += ((y[i] - my)**2)
      i +=1;

  return (u/math.sqrt((d*e))), u, d, e

p1 = Least_square(x.x1, y.y)
p2 = Least_square(x.x2, y.y)
p3 = Least_square(x.x3, y.y)
p4 = Least_square(x.x4, y.y)
p5 = Least_square(x.x5, y.y)
p6 = Least_square(x.x6, y.y)
p7 = Least_square(x.x7, y.y)
p8 = Least_square(x.x8, y.y)
print("P1: ", p1[0], "P2: ", p2[0], "P3: ", p3[0], "P4: ", p4[0],"P5: ", p5[0],"P6: ", p6[0],"P7: ", p7[0], "P8: ", p8[0])

"""## F. Get the 8 scatter plots of each of the independent variables 𝑋i, with respect to the variable 𝑌.
All the variables are highly correlated with the variable Y, The which one shows a negative correlation coincides with the negative slope that in this case is the variable X6 that also has a negative correlation coefficient, the variable that showed less correlation was the variable X3.
"""

fig = plt.figure()

fig, axes = plt.subplots(nrows=2, ncols=4, figsize=(22, 12))
plt.subplots_adjust(wspace=0.2, hspace=0.2)

axes[0, 0].scatter(x.x1, y.y, color='C0')
axes[0, 0].set_xlabel("X1")
axes[0, 0].set_ylabel("Y")

axes[0, 1].scatter(x.x2, y.y, color='C0')
axes[0, 1].set_xlabel("X2")
axes[0, 1].set_ylabel("Y")

axes[0, 2].scatter(x.x3, y.y, color='C0')
axes[0, 2].set_xlabel("X3")
axes[0, 2].set_ylabel("Y")

axes[0, 3].scatter(x.x4, y.y, color='C0')
axes[0, 3].set_xlabel("X4")
axes[0, 3].set_ylabel("Y")

axes[1, 0].scatter(x.x5, y.y, color='C0')
axes[1, 0].set_xlabel("X5")
axes[1, 0].set_ylabel("Y")

axes[1, 1].scatter(x.x6, y.y, color='C0')
axes[1, 1].set_xlabel("X6")
axes[1, 1].set_ylabel("Y")

axes[1, 2].scatter(x.x7, y.y, color='C0')
axes[1, 2].set_xlabel("X7")
axes[1, 2].set_ylabel("Y")

axes[1, 3].scatter(x.x8, y.y, color='C0')
axes[1, 3].set_xlabel("X8")
axes[1, 3].set_ylabel("Y")

plt.show()

"""## G. The authors explain on pages 135 and 136 of the article a method (screening process) to discard some variables that they do not consider relevant for the linear model found, where at the end of this process they remain only with the variables they consider more important, in This case were 𝑋1, 𝑋2, 𝑋4, 𝑋8. Assuming that as an alternative method you would have used the criterion of leaving only the variables 𝑋i that have a Pearson correlation coefficient with the variable 𝑌, greater than 0.96, what variables would have remained in your model?

# X1, X4, X7, X8

## H. Use the linear regression equation (10), called Model A in the article (the one that uses the 8 independent variables), to validate this model with the Test Data used by the authors, that is, using only the data of the years 2002 to 2006. Also calculate the relative percent errors of these results. Compare these results that you obtained with the results obtained by the authors and shown in Table III on page 136. Which year or years show a considerable discrepancy between your results and those of the article? NOTE: The percentage relative error is calculated by the authors using the following operation:![texto alternativo](https://raw.githubusercontent.com/Wittline/Machine_Learning/master/Multiple%20linear%20regression/pe.png)

## I. Repeat the previous experiment with the so-called Model B, that is, using equation 11 on page 136 of the article. Again compare your results with those shown by the authors.

In both cases Partially similar results are being obtained, the prediction for the year 2006 is different from  the author, it is higher, therefore the percentage of error is higher.
"""

def ModelA(x1,x2,x3,x4,x5,x6,x7,x8):
  return (-63.325 - (0.0683*x1) + (0.0589*x2) + (0.0058*x3) + (0.2076*x4) - (1.0603*x5) + (58.2047*x6) + (0.5673*x7) + (0.8103*x8))

def ModelB(x1,x2,x3,x4,x5,x6,x7,x8):
  return (-63.325 - (0.0683*x1) + (0.0589*x2) + (0.2076*x4) + (0.8103*x8))

def evaluate(d):
    mA = [None] * len(d)
    mB = [None] * len(d)
    eA = [None] * len(d)
    eB = [None] * len(d)
    vy = [None] * len(d)
    year = [None] * len(d)

    for i in range(len(d)):
      mA[i]= ModelA(d.iloc[i]['x1'], d.iloc[i]['x2'],d.iloc[i]['x3'],d.iloc[i]['x4'],d.iloc[i]['x5'],d.iloc[i]['x6'],d.iloc[i]['x7'],d.iloc[i]['x8']);
      mB[i]= ModelB(d.iloc[i]['x1'], d.iloc[i]['x2'],d.iloc[i]['x3'],d.iloc[i]['x4'],d.iloc[i]['x5'],d.iloc[i]['x6'],d.iloc[i]['x7'],d.iloc[i]['x8']);
      vy[i] = d.iloc[i]['y']
      eA[i] = 100*((mA[i] - d.iloc[i]['y'])/mA[i])
      eB[i] = 100*((mB[i] - d.iloc[i]['y'])/mB[i])
      year[i] =  d.iloc[i]['year']

    return year, vy, mA, eA, mB, eB

data = df1[df1['year']>=2002]
year, vy, mA, eA, mB, eB = evaluate(data)
results = pd.DataFrame(list(zip(year, vy, mA, eA, mB, eB)),
               columns =['Year', 'Output', 'Modelo A', 'Error', 'Modelo B', 'Error'])
results

"""## J. What conclusions can you get from this Task and the process of repeating the tests of an article and trying to obtain the same results as the authors?
People trust the results of a scientific researching and almost never repeat the author's experiments, in this case I found discrepancies in the values that were predicted for the year 2006, which dramatically changed the expected error, and therefore also the error average, where both models have almost the same average percentage of error in their predictions.

## K. What are your thoughts about this article published with missing data and results with clear discrepancies?
It is difficult to trust results where it is not clear how they used the dataset, sometimes the authors do not dedicate a paragraph to explain the origin and the real status of their dataset and that will always generate discrepancies in the experiments.
"""


import matplotlib.pylab as plt
from numpy import sin, arange, pi, ones, linspace, ndarray
from functools import partial

DFLT_N_SAMPLES = 21 * 2048
DFLT_SR = 44100

def mk_sine_wf(freq=5, n_samples=DFLT_N_SAMPLES, sr=DFLT_SR, phase=0, gain=1):
    """Make a sine waveform

    :param freq: Frequency (in Hz)
    :param n_samples: The number of samples of waveform you want
    :param sr: Sample rate
    :param phase: Phase (in radians)
    :param gain: (A number to multiply the base sine wave by)
    :return: Waveform. A numpy array of samples of the specified sine wave
    """
    return gain * sin(phase + arange(n_samples) * 2 * pi * freq / sr)

def freq_based_stationary_wf(freqs=(200, 400, 600, 800), weights=None,
                             n_samples: int = DFLT_N_SAMPLES, sr: int = DFLT_SR) -> ndarray:
    """
    Makes a stationary waveform by mixing a number of freqs together, possibly with different weights.

    :param freqs: List(-like) of frequencies (in Hz)
    :param weights: The weights these frequencies should have (all weights will be normalized
    :param n_samples: The number of samples of waveform you want
    :param sr: Sample rate
    :return: Waveform. A numpy array of samples of the specified sine wave
    """
    if weights is None:
        weights = ones(len(freqs))
    assert len(freqs) == len(weights)
    _mk_sine_wf = partial(mk_sine_wf, n_samples=n_samples, sr=sr)
    wf = sum(_mk_sine_wf(freq) * weights[i] for i, freq in enumerate(freqs))
    return wf / sum(weights)


import math
import random
import numpy
import pandas
from scipy.integrate import odeint

"""
This models the ODE of an unregulated genetic expression of a prokaryotic cell
k0 is the rate at which mRNA is being generated
k1 is the rate at which proteins are being
dm and dp are the degradation constants of mRNA and proteins respectively
C1 and C2 are integration constants
m0 and p0 are the mRNA and protein concentrations in M
"""

class UnregulatedGeneExpression:

    # t is a number
    # const is an array of numbers that represent the constants of the reactions
    def __init__(self, tmax=10, num_of_datapoints=1000, m0=0, p0=0, const=(1, 1, .1, .1)):
        self.n = num_of_datapoints
        self.tmax = tmax
        self.m0 = m0
        self.p0 = p0
        self.k0 = const[0]
        self.k1 = const[1]
        self.dm = const[2]
        self.dp = const[3]
        self.t = numpy.linspace(0, self.tmax, self.n)  # time points

    def analytical_solution(self, t):
        C1 = self.m0 - self.k0 / self.dm
        C2 = self.p0 - (self.k1 * self.k0) / (self.dm * self.dp) + C1 * self.k1 / (self.dm - self.dp)
        m_rna = C1 * math.exp(-self.dm * t) + self.k0 / self.dm
        protein = (self.k1 * self.k0) / (self.dm * self.dp) - (C1 * self.k1 * math.exp(-self.dm * t)) / (self.dm - self.dp) + C2 * math.exp(-self.dp * t)
        return m_rna, protein

    def analytical_sim(self):
        # store solutions
        m = numpy.empty_like(self.t)
        p = numpy.empty_like(self.t)

        # record initial conditions
        m[0] = self.m0
        p[0] = self.p0
        # iterate over time:
        for i in range(1, self.n):
            z = self.analytical_solution(i)

            # store solution for plotting
            m[i] = z[0]
            p[i] = z[1]

        dfp = pandas.DataFrame()
        dfp["Time"] = self.t
        dfp["Proteins"] = p

        dfm = pandas.DataFrame()
        dfm["Time"] = self.t
        dfm["mRNA"] = m
        return dfm, dfp

    # Use scipy.odeint to evaluate
    def numerical_solution(self, z, t):
        m0 = z[0]
        p0 = z[1]
        dmdt = self.k0 - self.dm * m0
        dpdt = self.k1 * m0 - self.dp * p0
        dzdt = dmdt, dpdt
        return dzdt

    def numerical_sim(self):
        # store solutions
        m = numpy.empty_like(self.t)
        p = numpy.empty_like(self.t)
        # record initial conditions
        z0 = [self.m0, self.p0]
        m[0] = self.m0
        p[0] = self.p0
        # solve ODE
        for i in range(1, self.n):
            # span for next time step
            tspan = [self.t[i - 1], self.t[i]]

            # solve for next step
            z = odeint(self.numerical_solution, z0, tspan)

            # store solution for plotting
            m[i] = z[1][0]
            p[i] = z[1][1]
            # next initial condition
            z0 = z[1]

        dfp = pandas.DataFrame()
        dfp["Time"] = self.t
        dfp["Proteins"] = p

        dfm = pandas.DataFrame()
        dfm["Time"] = self.t
        dfm["mRNA"] = m
        return dfm, dfp

class GillespieUnregulatedGeneExpression:

    # const is an array of numbers that represent the constants of the reactions
    def __init__(self, tmax=10, m0=0, p0=0, const=None, num_cells=1):
        if const is None:
            const = [1, 1, .1, .1]
        self.tmax = tmax
        self.k0 = const[0]
        self.k1 = const[1]
        self.dm = const[2]
        self.dp = const[3]
        self.Nm = m0
        self.Np = p0
        self.num_cells = num_cells

    def initial_state(self):
        return [self.Nm, self.Np]

    def update_propensities(self, Nm, Np):
        a1 = self.k0
        a2 = self.k1 * Nm
        a3 = self.dm * Nm
        a4 = self.dp * Np

        total = a1 + a2 + a3 + a4

        r1 = a1/total
        r2 = a2/total
        r3 = a3/total
        r4 = a4/total

        return [r1, r2, r3, r4, total]

    def next_reaction(self, r):
        n = random.uniform(0, 1)
        # Create an mRNA
        if 0 <= n <= r[0]:
            return [1, 0]
        # Create a protein
        elif r[0] < n <= r[1] + r[0]:
            return [0, 1]
        # Delete an mRNA
        elif r[1] + r[0] < n <= r[2] + r[1] + r[0]:
            return [-1, 0]
        # Delete a protein
        elif r[2] + r[1] + r[0] < n <= r[3] + r[2] + r[1] + r[0]:
            return [0, -1]
        # no reaction occurs
        elif r[3] + r[2] + r[1] + r[0] < n < 1:
            return [0, 0]

    def time_to_next_rxn(self, a, t):
        dt = -math.log(1 - random.uniform(0, 1))/a[4]
        t = t + dt
        return t

    def update_reaction_vector(self, current_state, update_vector):
        current_state = numpy.array(current_state)
        update_vector = numpy.array(update_vector)
        updated = current_state + update_vector
        return updated

    def run_sim(self):
        time = []
        protein = []
        mrna = []
        t = 0  # start time
        r0 = self.initial_state()
        mrna.append(r0[0])
        protein.append(r0[1])
        time.append(t)
        while t <= self.tmax:
            a = self.update_propensities(r0[0], r0[1])
            next_rxn = self.next_reaction(a)
            t = self.time_to_next_rxn(a, t)
            r = self.update_reaction_vector(r0, next_rxn)
            mrna.append(r[0])
            protein.append(r[1])
            time.append(t)
            r0 = r

        dfp = pandas.DataFrame()
        dfp["Time"] = time
        dfp["Proteins"] = protein

        dfm = pandas.DataFrame()
        dfm["Time"] = time
        dfm["mRNA"] = mrna
        return dfm, dfp

    def multiple_cells_sim(self):
        dfp_multiple = pandas.DataFrame()
        dfm_multiple = pandas.DataFrame()
        dfmt_multiple = pandas.DataFrame()
        dfpt_multiple = pandas.DataFrame()
        if self.num_cells == 1:
            return self.run_sim()
        elif self.num_cells > 1:
            for i in range(0, self.num_cells):
                mrna, prot = self.run_sim()
                dfm_multiple["Run{n}".format(n=i)] = mrna["mRNA"]
                dfp_multiple["Run{n}".format(n=i)] = prot["Proteins"]
                dfmt_multiple["mRNA_Run_time{t}".format(t=i)] = mrna["Time"]
                dfpt_multiple["prot_Run_time{t}".format(t=i)] = prot["Time"]

                dfm_multiple = dfm_multiple.dropna()
                dfp_multiple = dfp_multiple.dropna()
                dfmt_multiple = dfmt_multiple.dropna()
                dfpt_multiple = dfpt_multiple.dropna()

            # Add average col to dataframes
            dfm_multiple["Average"], dfmt_multiple["Average_Time"] = dfm_multiple.mean(axis=1), \
                                                                     dfmt_multiple.mean(axis=1)
            dfp_multiple["Average"], dfpt_multiple["Average_Time"] = dfp_multiple.mean(axis=1), \
                                                                     dfpt_multiple.mean(axis=1)

            # Join the DataFrams
            dfm_final = pandas.concat([dfm_multiple, dfmt_multiple], axis=1, sort=False)
            dfp_final = pandas.concat([dfp_multiple, dfpt_multiple], axis=1, sort=False)
            return dfm_final, dfp_final


# Karhunen-Loève Transform
# Same as pca, this code is a demo on dataset 'ColorHistogram.asc'
import numpy as np

data = np.loadtxt('ColorHistogram.asc', dtype=np.float64)
data = data[:,1:]

means = np.mean(data, axis = 0)
data -= means

data = data.T
print(means.shape)

print(data.shape)

C = (data @ data.T) / data.shape[1]

eig, P = np.linalg.eig(C)
print("eig = ", eig)
print("P = ", P)

P1 = P.T[:16, :]
print("select first 16 eig: ", eig[:16])
#print(P.T @ C @ P)
#print(P1 @ C @ P1.T)

# compress and rebuild
data1 = P1.T @ (P1 @ data)

print("original data[0] vector: ", data[0])
print("rebuild  data[0] vector: ", data1[0])


import numpy as np
from scipy.stats import chi2
from statsmodels.stats.diagnostic import acorr_ljungbox

# Helper Functions
def auto_covariance(series, h):
    n = len(series)
    x_bar = np.mean(series)
    series_centered = series - x_bar
    return np.dot(series_centered[:(n - h)], series_centered[h:]) / (n - h)

# INDEPENDENCE TESTING
def acorr_chi2_test(series, lags, alpha=0.05):
    n = len(series)
    quantile = chi2.ppf(q=1-alpha, df=lags)
    sum_of_autocov = 0

    for lag in range(1, lags + 1):
        sum_of_autocov += auto_covariance(series, lag)

    test_statistic = n * sum_of_autocov / (auto_covariance(series, 0) ** 2)

    if test_statistic > quantile:
        reject_h0 = True
    else:
        reject_h0 = False

    return quantile, test_statistic, reject_h0

def acorr_ljungbox_test(series, **kwargs):
    results = acorr_ljungbox(series, **kwargs)
    return results

# STATIONARITY TESTING

import numpy,from scipy,from statsmodels


# PREAMBLE:

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

def pearson_correlation(trajectory_data):
        """ Calculates the Pearson Correlation Matrix for node pairs
        Usage: node_correlation, node_variance, node_average = pearson_correlation(trajectory_data)
        Arguments:
        trajectory_data: multidimensional numpy array; first index (rows) correspond to timestep, second index correspond to positions of each node;

        Returns:
        node_correlation: a nNodes x nNodes square matrix (numpy array) filled with the Pearson Correlation Coefficients for all node pairs
        node_variance: one dimensional numpy array containing the variances of the data
        node_average: one dimensional numpy array containing the averages of the data

        """

        # ----------------------------------------
        # CALCULATING THE AVERAGE OF TRAJECTORY DATA
        # ----------------------------------------
        nSteps = len(trajectory_data)
        nSteps_range = range(nSteps)
        nNodes = len(trajectory_data[0])
        nNodes_range = range(nNodes)
        for ts in nSteps_range:
                # removing center of geometry translational motion
                center_of_geometry = np.mean(trajectory_data[ts])
                trajectory_data[ts] -= center_of_geometry
                # no rotations to worry about...

        node_average = np.sum(trajectory_data,axis=0)/nSteps

        # ----------------------------------------
        # PREPARE NUMPY ARRAYS
        # ----------------------------------------
        node_variance = np.zeros(nNodes,dtype=np.float64)
        node_covariance = np.zeros((nNodes,nNodes),dtype=np.float64)
        node_correlation = np.zeros((nNodes,nNodes),dtype=np.float64)

        # ----------------------------------------
        # CALCULATING PEARSON CORRELATION COEFFICIENT MATRIX
        # ----------------------------------------
        for ts in nSteps_range:
                for i in nNodes_range:
                        node_variance[i] += trajectory_data[ts,i]**2
                        for j in nNodes_range[i:]:
                                node_covariance[i,j] += trajectory_data[ts,i]*trajectory_data[ts,j]

        node_variance /= nSteps
        node_variance -= node_average**2
        node_covariance /= nSteps

        for i in nNodes_range:
                for j in nNodes_range[i:]:
                        node_covariance[i,j] -= node_average[i]*node_average[j]
                        node_correlation[i,j] = node_covariance[i,j]/np.sqrt(node_variance[i]*node_variance[j])
                        node_correlation[j,i] = node_correlation[i,j]

        # ----------------------------------------
        # OUTPUT OF AVERAGE, VARIANCE, COVARIANCE, AND CORRELATION MATRICES
        # ----------------------------------------
        np.savetxt('node_positional_average.dat',node_average)
        np.savetxt('node_positional_variance.dat',node_variance)
        np.savetxt('node_positional_covariance.dat',node_covariance)
        np.savetxt('node_positional_correlation.dat',node_correlation)

        # ----------------------------------------
        # PLOTTING VARIANCE
        # ----------------------------------------
        plt.plot(nNodes_range,node_variance,'k')
        plt.xlabel('Node Index',size=14)
        plt.ylabel(r'Node Variance ($\AA^{2})',size=14)
        plt.tight_layout()
        plt.savefig('node_positional_variance.png',dpi=600,transparent=True)
        plt.close()

        # ----------------------------------------
        # PLOTTING COVARIANCE
        # ----------------------------------------
        fig, ax = plt.subplots()
        temp = plt.pcolormesh(nNodes_range,nNodes_range,node_covariance,cmap='Blues')
        cb1 = plt.colorbar()
        cb1.set_label(r'Node-Node Covariance ($AA^{2}$)')

        xlabels = [str(int(x)) for x in temp.axes.get_xticks()[:]]
        ylabels = [str(int(y)) for y in temp.axes.get_yticks()[:]]
        temp.axes.set_xticks(temp.axes.get_xticks(minor=True)[:]+0.5,minor=True)
        temp.axes.set_xticks(temp.axes.get_xticks()[:]+0.5)
        temp.axes.set_yticks(temp.axes.get_yticks(minor=True)[:]+0.5,minor=True)
        temp.axes.set_yticks(temp.axes.get_yticks()[:]+0.5)
        temp.axes.set_xticklabels(xlabels)
        temp.axes.set_yticklabels(ylabels)

        plt.xlim((-0.5,nNodes+0.5))
        plt.ylim((-0.5,nNodes+0.5))
        plt.xlabel('Node Index',size=14)
        plt.ylabel('Node Index',size=14)
        ax.set_aspect('equal')
        plt.tight_layout()
        plt.savefig('node_positional_covariance.png',dpi=600,transparent=True)
        plt.close()

        # ----------------------------------------
        # PLOTTING CORRELATION
        # ----------------------------------------
        fig, ax = plt.subplots()
        temp = plt.pcolormesh(nNodes_range,nNodes_range,node_correlation,cmap='bwr',vmin=-1.0,vmax=1.0)
        cb1 = plt.colorbar()
        cb1.set_label('Node-Node Correlation')

        xlabels = [str(int(x)) for x in temp.axes.get_xticks()[:]]
        ylabels = [str(int(y)) for y in temp.axes.get_yticks()[:]]
        temp.axes.set_xticks(temp.axes.get_xticks(minor=True)[:]+0.5,minor=True)
        temp.axes.set_xticks(temp.axes.get_xticks()[:]+0.5)
        temp.axes.set_yticks(temp.axes.get_yticks(minor=True)[:]+0.5,minor=True)
        temp.axes.set_yticks(temp.axes.get_yticks()[:]+0.5)
        temp.axes.set_xticklabels(xlabels)
        temp.axes.set_yticklabels(ylabels)

        plt.xlim((-0.5,nNodes+0.5))
        plt.ylim((-0.5,nNodes+0.5))
        plt.xlabel('Node Index',size=14)
        plt.ylabel('Node Index',size=14)
        ax.set_aspect('equal')
        plt.tight_layout()
        plt.savefig('node_positional_correlation.png',dpi=600,transparent=True)
        plt.close()

        return node_correlation, node_variance, node_average


## Sigmoid activation function

import numpy as np

class Sigmoid:
    def __init__(self):
        """
        ## This function is used to calculate the sigmoid and the derivative
            of a sigmoid
        """

    def forward(self, x):
        return 1.0 / (1.0 + np.exp(-x))

    def backward(self, x):
        return x * (1.0 - x)


import scipy.stats as scs

def min_sample_size(baseline_conversion_rate, minimum_detectable_effect, significance_level=0.05, min_power=0.8, size_ratio=1., two_tailed=True):

    if minimum_detectable_effect == 0:
        raise ValueError('Minimum detectable effect must be greater than 0%')
    if significance_level == 0:
        raise ValueError('The statistical significant level must be less than 100%')
    if significance_level == 1:
        raise ValueError('The statistical significant level must be greater than 0%')
    if min_power == 1:
        raise ValueError('The power must be less than 100%')
    if min_power == 0:
        raise ValueError('The power must be strictly greater than 0%')
    if size_ratio == 0:
        raise ValueError('Size ratio must be greater than 0%')

    Z_alpha = z_value(significance_level, two_tailed=two_tailed)
    Z_power = z_value(1-min_power, two_tailed=False)
    pooled_probability = (baseline_conversion_rate+size_ratio*(baseline_conversion_rate+minimum_detectable_effect))/(1+size_ratio)
    min_N_B = (1+size_ratio)*pooled_probability*(1-pooled_probability)*(Z_alpha+Z_power)**2/minimum_detectable_effect**2
    min_N_A = min_N_B/size_ratio
    return round(min_N_A), round(min_N_B)

def z_value(significance_level, two_tailed=True):
    z_dist = scs.norm()
    if two_tailed:
        significance_level = significance_level/2
        area = 1 - significance_level
    else:
        area = 1 - significance_level
    z = z_dist.ppf(area)
    return z


"""Estimate the confidence interval of a Bernoulli dist. parameter.

It is a conservative interval i.e. it overestimates the confidence
interval bounds.

Useful to find confidence intervals for machine learning models
accuracy.
"""
import scipy.stats
import numpy as np

def bernoulli_confidence_interval(
    accuracy: float, num_inst: int, confidence: float = 0.95
):
    crit_point = scipy.stats.norm.isf(0.5 * (1.0 - confidence))

    dist = 0.5 * crit_point / np.sqrt(num_inst)

    lower_bound = max(0.0, accuracy - dist)
    upper_bound = min(1.0, accuracy + dist)

    return (lower_bound, upper_bound)

def _test():
    accuracy = 0.98
    confidence = 0.95
    num_inst = 128

    print(bernoulli_confidence_interval(accuracy, num_inst, confidence))

if __name__ == "__main__":
    _test()


import numpy as np
from numpy.testing import assert_allclose, assert_equal
import pytest

import scipy.special as sc

class TestInverseErrorFunction:
    def test_compliment(self):
        # Test erfcinv(1 - x) == erfinv(x)
        x = np.linspace(-1, 1, 101)
        assert_allclose(sc.erfcinv(1 - x), sc.erfinv(x), rtol=0, atol=1e-15)

    def test_literal_values(self):
        # calculated via https://keisan.casio.com/exec/system/1180573448
        # for y = 0, 0.1, ... , 0.9
        actual = sc.erfinv(np.linspace(0, 0.9, 10))
        expected = [
            0,
            0.08885599049425768701574,
            0.1791434546212916764928,
            0.27246271472675435562,
            0.3708071585935579290583,
            0.4769362762044698733814,
            0.5951160814499948500193,
            0.7328690779592168522188,
            0.9061938024368232200712,
            1.163087153676674086726,
        ]
        assert_allclose(actual, expected, rtol=0, atol=1e-15)

    @pytest.mark.parametrize(
        'f, x, y',
        [
            (sc.erfinv, -1, -np.inf),
            (sc.erfinv, 0, 0),
            (sc.erfinv, 1, np.inf),
            (sc.erfinv, -100, np.nan),
            (sc.erfinv, 100, np.nan),
            (sc.erfcinv, 0, np.inf),
            (sc.erfcinv, 1, -0.0),
            (sc.erfcinv, 2, -np.inf),
            (sc.erfcinv, -100, np.nan),
            (sc.erfcinv, 100, np.nan),
        ],
        ids=[
            'erfinv at lower bound',
            'erfinv at midpoint',
            'erfinv at upper bound',
            'erfinv below lower bound',
            'erfinv above upper bound',
            'erfcinv at lower bound',
            'erfcinv at midpoint',
            'erfcinv at upper bound',
            'erfcinv below lower bound',
            'erfcinv above upper bound',
        ]
    )
    def test_domain_bounds(self, f, x, y):
        assert_equal(f(x), y)


# -*- coding: utf-8 -*-

import numpy as np
from numpy import linspace, logspace, diff, zeros
from numpy import cos, sin, exp, log, pi
import matplotlib.pyplot as plt

####################################
# Implementación genérica de Euler
# f(t,x): derivada de x respecto al tiempo
# x0: condición inicial
# t0, tf: tiempo inicial y final
# h: paso de integración
####################################
def euler(f, x0, t0, tf, h):
    """
    Arguments:
        f: Function handler. Must recieve two arguments, t and x. f(t, x).
        x0: Initial value
        t0 / tf: Initial and final time to evaluate
        h: Desiered step

    Returns:
        t, x
        t: Array with the time points used for the function's calculation.
            (X axis on a plot)
            Shape: (n,)
        x: Obtained values at a given t.
            (Y axis on a plot)
            Shape: (n,1)  - One row for each element in t -
    """
    N = int((tf - t0) / h)  # número de puntos
    t = linspace(t0, tf, N + 1)
    n = x0.shape[0]  # dimensión del problema
    x = zeros((n, N + 1))
    x[:, 0] = x0
    for k in range(N):
        x[:, k + 1] = x[:, k] + h * f(t[k], x[:, k])
    return t, x

def taylor(x0, t0, tf, h, *derivatives):
    """
    Generic implementation of taylor of N order
    Arguments:
        x0: Initial value
        t0 / tf: Initial and final time to evaluate
        h: Desiered step
        f: derivatives of the function g, starting from g'
        up to the desired order

    Returns:
        t, x
        t: Array with the time points used for the function's calculation.
            (X axis on a plot)
            Shape: (n,)
        x: Obtained values at a given t.
            (Y axis on a plot)
            Shape: (n,1)  - One row for each element in t -
    """
    N = int((tf - t0) / h)  # número de puntos
    t = linspace(t0, tf, N + 1)
    n = x0.shape[0]  # dimensión del problema
    x = zeros((n, N + 1))
    x[:, 0] = x0
    for k in range(N):
        x[:, k + 1] = x[:, k]
        for index, der in enumerate(derivatives):
            i = index+1
            x[:, k + 1] = x[:, k + 1] + der(t[k], x[:, k]) * (h ** i) / np.math.factorial(i)

    return t, x

# LO SIGUIENTE ES EL EJEMPLO QUE DIÓ EN CLASE USANDO AL FUNCIÓN TAYOR
# SI CORRES LA FUNCIÓN PARA MOSTRAR EL ERROR TENES QUE CAMBIAR AHÍ LOS DATOS
# SEGUN EL GRAOD QUE USES
# SAME PLOTAYLOR

########################
# EJEMPLO
########################
R = 1e3  # Valor de la resistencia
C = 1e-6  # Valor de la capacidad
w = 2.0 * pi * 1000  # frecuencia angular de la señal de entrada
A = 1.0  # amplitud de la señal de entrada
T = 5 * 2 * pi / w  # simulo cinco ciclos

####################################
# Derivada primera de x
def dx(t, x):
    return ((A * cos(w * t) - x) / (R * C))

####################################
# Derivada segunda de x
def d2x(t, x):
    return ((-A * w * sin(w * t) - ((A * cos(w * t) - x) / (R * C))) / (R * C))

####################################
# Plot ejemplo
def plotaylor(h):
    x0 = zeros(1)
    t, xt = taylor(x0, 0, T, h, dx, d2x)
    fig, ax = plt.subplots()
    ax.plot(t, xt[0, :], label='x(t)')
    ax.legend()
    plt.title('Ejercicio')

    plt.show()

####################################
# Estimación error
def esterrorejemplo(h):

    # i es el grado del polinomio
    i = 2

    x0 = zeros(1)
    t, xt1 = taylor(x0, 0, T, h, dx, d2x)
    t, xt2 = taylor(x0, 0, T, h/2, dx, d2x)

    eet = abs(xt1[0, -1] - xt2[0, -1]) / ((2**i)-1)

    return eet

plotaylor(T / 10000)
print(esterrorejemplo(T / 10000))


import numpy as np
''' Look up: https://docs.scipy.org/doc/numpy/reference/routines.random.html '''

def generate_array():
	#x = np.random.rand(4, 5)

	x = np.array([(20, 25, 10, 23, 26, 32, 10, 5, 0),
		(0, 2, 50, 20, 0, 1, 28, 5, 0)])
	print('Given array: ')
	print(x)

	return x

def access_elements(array):
	indexes = np.array([1, 1, 2, 3])
	elements = array[indexes]
	print('Requested elements:')
	print(elements)
	return elements

def masking(array, mask_value):
	masked = array[array < mask_value]
	print('Masked')
	print(masked)

def replacing(array, mask_value, new_value):
	array[array < mask_value] = new_value
	print('Replaced:')
	print(array)

if __name__ == "__main__":
	x = generate_array()
	mean_value = x.mean()
	print("Mean:")
	print(mean_value)

	masking(x, mean_value)

	replacing(x, mean_value, 0)


import numpy as np

# 1 - Create your first array with the elements [1,22.4,5,35,4,6.7,3,8,40] and print it. Experiment what the
# following functions do: ndim, shape, size and dtype.
my_array = np.array([1, 22.4, 5, 35, 4, 6.7, 3, 8, 40])
print(my_array)
print(f'my_array.ndim is {my_array.ndim}')
print(f'my_array.shape is {my_array.shape}')
print(f'my_array.size is {my_array.size}')
print(f'my_array.dtype is {my_array.dtype}')

# 2 - Create your first matrix with the elements [['a', 'b'],['c', 'd'],[3, 3]] and print it. Experiment what the
# following functions do: ndim, shape, size and dtype
my_matrix = np.array([['a', 'b'], ['c', 'd'], [3, 3]])
print(my_matrix)
print(f'my_matrix.ndim is {my_matrix.ndim}')
print(f'my_matrix.shape is {my_matrix.shape}')
print(f'my_matrix.size is {my_matrix.size}')
print(f'my_matrix.dtype is {my_matrix.dtype}')

# 3 - Create numpy 1 dimension array using each of the functions arange and rand
range_array = np.arange(0, 10)
print(f'range_array is {range_array}')

random_array = np.random.rand(10)
print(f'random_array is {random_array}')

# 4 - Create numpy 2 dimensions matrix using each of the functions zeros and rand
zeros_matrix = np.zeros((5, 5))
print(f'range_array is {zeros_matrix}')

random_matrix = np.random.rand(4, 4)
print(f'random_array is {random_matrix}')

# 5 - Create an array containing 20 times the value 7. Reshape it to a 4 x 5 Matrix
result_array = np.ones(20) * 7
reshaped_array = result_array.reshape(4,5)
print(reshaped_array)

# 6 - Create a 6 x 6 matrix with all numbers up to 36, then print:
matrix_to_slice = np.arange(1, 37).reshape(6, 6)
print(matrix_to_slice)
print(f'only the first element on it: {matrix_to_slice[0,0]}')
print(f'only the last 2 rows for it: {matrix_to_slice[4:6,:]}')
print(f'only the two mid columns and 2 mid rows for it: {matrix_to_slice[2:4,2:4]}')
print(f'the sum of values for each column: {matrix_to_slice.sum(axis=0)}')


# -*- coding: utf-8 -*-
"""
Created on Sat Jan  6 19:51:06 2018

@author: Alankar
"""
import numpy as np
import matplotlib.pyplot as plt

def BE(A,Z):
    a1, a2, a3, a4 = 15.67, 17.23, 0.75, 93.2
    a5 = 0
    if not(A%2==0):
        a5 = 0
    elif(A%2==0 and Z%2==0):
        a5 = 12
    elif(A%2==0 and not(Z%2==0)):
        a5 = -12
    return a1*A - a2*A**(2/3) - a3*(Z**2/A**(1/3)) - a4*((A-2*Z)**2/A) + a5/A**(1/2)

print('-----------------------------------------------------------')
print('Demo Output: \nBinding Energy of a nucleus with Z = %d and A = %d is %.2f MeV'%(28,58,BE(58,28)))
print('Binding Energy per nucleon: %.2f MeV'%(BE(58,28)/58))
print('-----------------------------------------------------------')

Z = float(input('Enter Atomic number of nucleus: '))
A = float(input('Enter Mass number of nucleus: '))
B = BE(A,Z)
print('\nBinding Energy is %.2f MeV'%B)
print('Binding Energy per nucleon: %.2f MeV'%(BE(A,Z)/A))

def stableA(Z):
    B = BE(Z,Z)/Z
    A_st = Z
    for A in range(Z+1,3*Z+1):
        B_tmp = BE(A,Z)/A
        if (B_tmp>B):
            B = B_tmp
            A_st = A
    return (A_st,B)

NuProf = np.zeros((100,2),dtype=np.float32)
for Z in range(0,100):
    st = stableA(Z+1)
    NuProf[Z,0] = st[0]
    NuProf[Z,1] = st[1]
    print('Most stable nucleus with Z = %d has A = %d'%((Z+1),NuProf[Z,0]))

print('\n\nMax Binding Energy per nucleon is at Z = ',(np.argmax(NuProf[:,1])+1))

plt.plot(np.arange(1,101,1),NuProf[:,1])
plt.xlabel('Atomic Number Z')
plt.ylabel('Binding Energy per nucleon (MeV)')
plt.savefig('Binding Energy Curve.png')
plt.show()


import numpy as py
np.random.see(42)

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

def sigmoid_prime(x):
	return sigmoid(x) * (1-sigmoid(x))

def prediction(X, W, b):
	prediction_ = sigmoid(np.matul(X,W)+b)
	return prediction_

def error_vector(y, y_hat):
	errors = [-y[i]*np.log(y_hat[i]) - (1-y[i]) * np.log(1-y_hat[i]) for i in range(len(y))]
	return errors

def error(y, y_hat):
	ev = error_vector(y, y_hat)
	return sum(ev)/len(ev)

def dErrors(X, y, y_hat):
	DErrorsDx1 = [-X[i][0]*(y[i]-y_hat[i]) for i in range(len(y))]
	DErrorsDx2 = [-X[i][1]*(y[i]-y_hat[i]) for i in range(len(y))]
	DErrprsDb =[-(y[i]-y_hat[i]) for i in range(len(y))]
	return DErrorsDx1, DErrorsDx2, DErrorsDb

def gradientDescentStep(X, y, W, b, learn_rate =0.01)
	y_hat = prediction(X, W, b)
	errors = error_vector(y, y_hat)
	derivErrors = dErrors(X, y, y_hat)
	W[0] -=sum(derivErrors[0])*learn_rate
	W[1] -=sum(derivErrors[1])*learn_rate
	b -=sum(derivErrors[2])*learn_rate
	return W, b, sum(errors)

def trainLR(X, y, learn_rate=0.01, num_epochs =100):
	x_min, x_max = min(X.T[0]), max(X.T[0])
	y_min, y_max = min(X.T[1]), max(X.T[1])
	W = np.array(np.random.rand(2,1))*2 -1
	b = np.random.rand(1)[0]*2 -1
	boundary_lines =[]
	errors =[]
	for i in range(num_epochs):
		W, b, error = gradientDescentStep(X, y, W, b, learn_rate)
		boundary_lines.append((-W[0]/W[1], -b/W[1]))
		errors.append(error)
	return boundary_lines, erros

lesson 4 Intro to Neural Networks/logistic_regression.py


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

trial = 100000

init = 0.0
states = [init]

for i in range(trial):
    state = states[-1]
    # sample from uniform(state-1, state+1)
    y = random.uniform(state - 1.0, state + 1.0)
    # accept the proposal with probability min(1.0, e^(-0.5*(y^2 - s^2)))
    p = min(1.0, math.exp(-0.5 * (y**2 - state**2)))
    new = np.random.choice([state, y], p=[1-p, p])

    states.append(new)

plt.hist(states, 50, density=True)
plt.title('simulation results of standard normal distribution')
plt.savefig('standard_normal_distribution.png', dpi=300)
plt.show()

means, variances = [], []
step = trial // 1000
states = np.array(states)
for i in range(1000):
    end = step * (i+1)
    mean = np.mean(states[:end])
    variance = np.var(states[:end])
    means.append(mean)
    variances.append(variance)
means = np.array(means)
variances = np.array(variances)

plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.plot(range(1000), np.abs(0.0-means))
plt.title('convergence rate of mean')
plt.xlabel("iteration (* %d)"%(step))
plt.ylabel("approximation error")

plt.subplot(122)
plt.plot(range(1000), np.abs(1.0-variances))
plt.title("convergence rate of variance")
plt.xlabel("iteration (* %d)"%(step))

plt.ylabel("approximation error")
plt.savefig("standard_normal_distribution_convergence_rate.png", dpi=300)
plt.show()


#reason for going to robust regression- RANSAC
#http://digitalfirst.bfwpub.com/stats_applet/stats_applet_5_correg.html

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import RANSACRegressor

#Read raw csv file without headers and comma delimiter
df=pd.read_csv('housing.data',delim_whitespace=True,header=None)

#give names for each column
col_name=['CRIM','ZIN','INDUS','CHAS','NOX','RM','AGE','DIS','RAD','TAX','PIRATIO','B','LSTAT','MEDV']
df.columns=col_name

#Select the input and output datas
X=df['RM'].values.reshape(-1,1)
y=df['MEDV'].values

#fitting the data with RANdom SAmple Consensus (RANSAC) Algorithm
ransac=RANSACRegressor()
ransac.fit(X,y)

#saperating inler and outlier points
inlier_mask=ransac.inlier_mask_
outlier_mask=np.logical_not(inlier_mask)

#predicting the values with (3,4,5,6,7,8,9)
line_X=np.arange(3,10,1)
line_y_ransac=ransac.predict(line_X.reshape(-1,1))

#plot the figure
sns.set(style='darkgrid',context='notebook')
plt.figure(figsize=(12,10));
plt.scatter(X[inlier_mask],y[inlier_mask],c='blue',marker='o',label='Inliers')
plt.scatter(X[outlier_mask],y[outlier_mask],c='brown',marker='s',label='Outliers')
plt.plot(line_X,line_y_ransac,color='red')
plt.xlabel('RM')
plt.ylabel('MEDV')
plt.legend(loc='upper left')
plt.show()

#check and compare the coef and intercept with linear reg
print(ransac.estimator_.coef_,ransac.estimator_.intercept_)


import numpy as np
class Solution:
    def largestRectangleArea(self, heights: [int]) -> int:
        if len(heights) == 0:return 0
        max_stack = []
        maxs = heights[0]
        for i in range(len(heights)):
            if len(max_stack) == 0 or max_stack[-1][0] <= heights[i]:
                max_stack.append([heights[i], i])
            else:
                top = max_stack[-1][1]
                while len(max_stack) > 0 and max_stack[-1][0] > heights[i]:
                    last = max_stack.pop()
                    if len(max_stack) > 0:
                        maxs = max(maxs, last[0] * (top - max_stack[-1][1]))
                    else:
                        maxs = max(maxs, last[0] * (top + 1))
                max_stack.append([heights[i], i])

        top = max_stack[-1][1]
        while len(max_stack) > 0:
            last = max_stack.pop()
            if len(max_stack) > 0:
                maxs = max(maxs, last[0] * (top - max_stack[-1][1]))
            else:
                maxs = max(maxs, last[0] * (top + 1))

        return maxs

    def largestRectangleArea2(self, heights: [int]) -> int:
        hlen = len(heights)
        #dp[i][j] the min number in range [i, j]
        dp = np.zeros((hlen, hlen), int)
        for i in range(hlen):
            for j in range(i, hlen):
                if i == j:
                    dp[i][j] = heights[j]
                else:
                    dp[i][j] = min(dp[i][j-1], heights[j])

        maxS = 0
        for i in range(hlen):
            for j in range(i, hlen):
                maxS = max(maxS, dp[i][j]*(j-i+1))

        return int(maxS)

sol = Solution()

heights = [0, 9]

heights = [5,4,1,2]
heights = [2,1,5,6,2,3]

heights = [1, 1]
heights = [2, 1, 2]
heights = [4,2,0,3,2,5]
heights = [12,11,10,9,8,7,6,5,4,3,2,1]
res = sol.largestRectangleArea(heights)
print(res)
res = sol.largestRectangleArea2(heights)
print(res)

d :dict


import unittest
from pkg import Linear_Algebra
import numpy as np

class TestLU(unittest.TestCase):
    def setUp(self):
        self.U_answer = np.around(np.array([[2,1,0],[0,3/2,1],[0,0,4/3]], dtype=float), decimals=2).tolist()
        self.L_answer = np.around(np.array([[1,0,0],[1/2,1,0],[0,2/3,1]], dtype=float), decimals=2).tolist()

    def test_perm(self):
        answer = np.array([[0,1,0], [1,0,0], [0,0,1]], dtype=float).tolist()
        result = Linear_Algebra.make_perm_mx(3, 0, 1).tolist()
        self.assertEqual(result, answer)

    def test_LU(self):
        L_result, U_result = np.around(Linear_Algebra.LU(np.array([[2,1,0],[1,2,1],[0,1,2]], dtype=float)), decimals=2).tolist()
        self.assertEqual(U_result, self.U_answer)
        self.assertEqual(L_result, self.L_answer)

class TestDet(unittest.TestCase):
    def setUp(self):
        self.input_mx = np.array([[2,-1,0,0],[-1,2,-1,0],[0,-1,2,-1],[0,0,-1,2]], dtype=float)
    def test_find_det(self):
        result = np.around(Linear_Algebra.find_det(A = self.input_mx), decimals=2).tolist()
        answer = np.around(5, decimals=2).tolist()
        self.assertEqual(result, answer)

if __name__ == '__main__':
    unittest.main()


import torch
import numpy as np
from torch.autograd import Variable

def compute_return(rewards, gamma=1.0):
    discounts = gamma ** np.arange(rewards.shape[0])
    return np.sum(discounts * rewards)

def make_tensor(array, dtype='float32'):
    return torch.from_numpy(array.astype(dtype))

def make_variable(array, dtype='float32'):
    return Variable(make_tensor(array, dtype))

def normalize(array):
    return (array - np.mean(array)) / (np.std(array) + np.finfo(np.float32).eps)


import numpy as np

#parameters
N = 100; a = 0.5; b = -10; sigma = 10
#Noise
sigma = np.random.normal(0, sigma, N)
# X
X = np.arange(0, N)
# y
y = a * X + b + sigma
# Trick
X = np.stack([np.ones(len(X)), X], axis = 1)


# -*- coding: utf-8 -*-
# @Time    : 7/31/2018 7:29 PM
# @Author  : DonaldY

# 生成 10 * 10的希尔伯特矩阵
from scipy.linalg import hilbert

x = hilbert(10)
x

# 希尔伯特转置矩阵于原矩阵相乘
import numpy as np

np.matrix(x).T * np.matrix(x)


# -*- coding: utf-8 -*-
"""
Created on Thu Mar 23 14:43:36 2017

@author: ADB3HAMADL
"""

'''
===================================
Case Study 3 - Wine Classification
===================================
'''

#In this case study, we will analyze a dataset consisting of an assortment of
#wines classified as "high quality" and "low quality" and will use the k-Nearest
#Neighbors classifier to determine whether or not other information about the wine
#helps us correctly predict whether a new wine will be of high quality.

#Exercise 1
#----------

#TODO: Read in the data as a pandas dataframe using pd.read_csv.
#The data can be found at https://s3.amazonaws.com/demo-datasets/wine.csv.

import pandas as pd
import numpy as np

data = pd.read_csv('https://s3.amazonaws.com/demo-datasets/wine.csv') #read directly from the link

print data.head(5)
print np.shape(data) #(6497, 15)

#------------------------------------------------------------------------------

#Exercise 2
#----------

#TODO: The dataset remains stored as data. Two columns in data are is_red and color,
#which are redundant. Drop color from the dataset, and save the new dataset as
#numeric_data.

numeric_data = data.drop('color', axis=1)
print (numeric_data.head(5))
#------------------------------------------------------------------------------

#Exercise 3
#----------

#TODO: To ensure that each variable contributes equally to the kNN classifier,
#we need to standardize the data. First, from each variable in numeric_data,
#subtract its mean. Second, for each variable in numeric_data, divide by its
#standard deviation. Store your standardized result as numeric_data.

numeric_data = (numeric_data - np.mean(numeric_data)) / np.std(numeric_data)

print(numeric_data.head(5))

#TODO: Principal component analysis is a way to take a linear snapshot of the data
#from several different angles, with each snapshot ordered by how well it
#aligns with variation in the data. The sklearn.decomposition module contains
#the PCA class, which determines the most informative principal components of
#the data (a matrix with columns corresponding to the principal components).
#Use pca.fit(numeric_data).transform(numeric_data) to extract the first two
#principal components from the data. Store this as principal_components.

import sklearn.decomposition
pca = sklearn.decomposition.PCA(n_components=2)
principal_components = pca.fit(numeric_data).transform(numeric_data)

#------------------------------------------------------------------------------

#Exercise 4
#----------

#TODO: The first two principal components can be accessed using principal_components[:,0]
#and principal_components[:,1]. Store these as x and y respectively, and plot the
#first two principal components. The high and low quality wines will be colored
#using red and blue. How well are the two groups of wines separated by the first
#two principal components?

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib.backends.backend_pdf import PdfPages
observation_colormap = ListedColormap(['red', 'blue'])
x = principal_components[:,0]
y = principal_components[:,1]
plt.plot(principal_components[:,0], principal_components[:,1])
plt.title("Principal Components of Wine")
plt.scatter(x, y, alpha = 0.2,
    c = data['high_quality'], cmap = observation_colormap, edgecolors = 'none')
plt.xlim(-8, 8); plt.ylim(-8, 8)
plt.xlabel("Principal Component 1"); plt.ylabel("Principal Component 2")
plt.show()

#------------------------------------------------------------------------------

#Exercise 5
#----------

#TODO: We are now ready to fit the wine data to our kNN classifier.
#Create a function accuracy(predictions, outcomes) that takes two lists of the
#same size as arguments and returns a single number, which is the percentage of
#elements that are equal for the two lists.

def accuracy(predictions, outcomes):
    for i in predictions:
        for j in outcomes:
            if i==j:
                Percentage = 100*np.mean(predictions == outcomes)
    return Percentage

#TODO: Use accuracy to compare the percentage of similar elements in
#x = np.array([1,2,3]) and y = np.array([1,2,4]).

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

percentage = accuracy(x, y)

#TODO: Print your answer.

print percentage

#------------------------------------------------------------------------------

#Exercise 6
#----------

#TODO: The dataset remains stored as data. Because most wines in the dataset are
#classified as low quality, one very simple classification rule is to predict
#that all wines are of low quality. Use the accuracy function (preloaded into
#memory as defined in Exercise 5) to calculate how many wines in the dataset
#are of low quality. Accomplish this by calling accuracy with 0 as the first
#argument, and data["high_quality"] as the second argument.

number_of_low_quality = accuracy(0, data["high_quality"])

#TODO: Print your result.

print number_of_low_quality

#------------------------------------------------------------------------------

#Exercise 7
#----------

#TODO: Use knn.predict(numeric_data) to predict which wines are high and low quality
#and store the result as library_predictions.

from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors = 5)
knn.fit(numeric_data, data['high_quality'])
library_predictions = knn.predict(numeric_data)

#TODO: Use accuracy to find the accuracy of your predictions, using library_predictions
#as the first argument and data["high_quality"] as the second argument.

predictions_accuracy = accuracy(library_predictions, data["high_quality"])

#TODO: Print your answer. Is this prediction better than the simple classifier
#in Exercise 6?

print (predictions_accuracy) # Yes, this is better!

#------------------------------------------------------------------------------

#Exercise 8
#----------

#TODO: Unlike the scikit-learn function, our homemade kNN classifier does not take
#any shortcuts in calculating which neighbors are closest to each observation,
#so it is likely too slow to carry out on the whole dataset. To circumvent this,
#fix the random generator using random.seed(123), and select 10 rows from the
#dataset using random.sample(range(n_nrows), 10). Store this selection as selection.

import random

n_rows = data.shape[0]

random.seed(123)
selection = random.sample(range(n_rows), 10)

#------------------------------------------------------------------------------

#Exercise 9
#----------

#TODO: The sample of 10 row indices are stored as selection from the previous exercise.
#For each predictor p in predictors[selection],
#use knn_predict(p, predictors[training_indices,:], outcomes, k=5) to predict
#the quality of each wine in the prediction set, and store these predictions
#as a np.array called my_predictions. Note that knn_predict is already defined
#as in the Case 3 videos.

predictors = np.array(numeric_data)
training_indices = [i for i in range(len(predictors)) if i not in selection]
outcomes = np.array(data["high_quality"])

my_predictions = [knn_predict(p, predictors[training_indices,:], outcomes, k=5) for p in predictors[selection]]

#TODO: Using the accuracy function, compare these results to the selected rows from
#the high_quality variable in data using my_predictions as the first argument
#and data.high_quality[selection] as the second argument. Store these results
#as percentage.

percentage = accuracy(my_predictions, data.high_quality[selection] )

#TODO: Print your answer.

print(percentage) #Our accuracy is comparable to the library's function!

#------------------------------------------------------------------------------


import sympy
import csv
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from sympy.plotting.plot import List2DSeries
from timeit import default_timer as timer
import random

matplotlib.style.use('bmh')
#seaborn-whitegrid
#fivethirtyeight

def Lagrange (Lx, Ly):
    X = sympy.symbols('X')
    if  len(Lx) != len(Ly):
        print ("Error data set")
        return 1
    y = 0
    for i in range(len(Lx)):
        t = 1
        for j in range(len(Lx)):
            if j != i:
                t *= ((X - Lx[j]) / (Lx[i] - Lx[j]))
        y += t * Ly[i]
    return y

GINI_KNOWN = 0.293 # here is Croatia's gini in 2018
Lx = []
Ly = []
yPos = []

Ly.append(0.0)
with open('input_hrv.csv', 'r') as file:
    reader = csv.reader(file)
    for row in reader:
        r = 0
        for value in row:
            r += 1
            try:
                value = float(value)
                if r % 2 == 0:
                    Ly.append(value)
            except ValueError as e:
                continue
GDP = Ly[-1]
del Ly[-1]
Ly.sort()
for i in range(len(Ly)):
    Lx.append(i * 10.0)

print("---------- Dependencies ----------")
print('matplotlib: {}'.format(matplotlib.__version__))
print('sympy: {}'.format(sympy.__version__))
print('numpy: {}'.format(np.__version__))
print("----------------------------------")

print("----------- INPUT DATA -----------")
print("X Data: ", Lx)
print("Y Data: ", Ly)
print("Total Income: ", GDP)
print("----------------------------------")

print("----------- SET POINTS -----------")
for i in range(len(Lx)):
    yValue = (Ly[i] * 1.0) / (GDP * 1.0) * 100.0
    if i != 0:
        yValue += yPos[i - 1]
    print("X: ", Lx[i], "Y: ", yValue)
    yPos.append(yValue)
print("----------------------------------")

print("----------- INPUT DATA ------------")
print("X Vector: ", Lx)
print("Y Vector: ", yPos)
print("-----------------------------------")

X = sympy.symbols('X')
xLimit = 100

startL = timer()
MyLagrange = Lagrange(Lx, yPos)
lorenz = sympy.simplify(MyLagrange)
equality = X
areaEquality = sympy.integrate(equality, (sympy.Symbol('X'), 0, xLimit))
areaGINI = sympy.integrate(equality - lorenz, (sympy.Symbol('X'), 0, xLimit))
lagGINI = areaGINI / areaEquality
endL = timer()

timeLagrange = endL - startL
lagAccuracy = abs(100.0 - (abs(GINI_KNOWN - lagGINI) / GINI_KNOWN) * 100.0)

print("------------ LAGRANGE ------------")
print("Lagrange Polynomial:", lorenz)
print("Object Type:", type(lorenz))
print("Enclosed Area:", areaGINI)
print("Equality Area:", areaEquality)
print("GINI Coefficient:", lagGINI)
print("Time:", timeLagrange, "seconds")
print("Accuracy: {:0.2f} %".format(lagAccuracy))
print("----------------------------------")

print("+++++++++++++ INTEGRATION ++++++++++++")
delta = 0.005
acc = 0.0
accArea = 0.0
eqArea = 0.0

startR = timer()
while acc <= xLimit:
    acc += delta
    eval = acc - (delta / 2.0)
    heightEq = equality.evalf(subs={X:eval})
    heightLorenz = lorenz.evalf(subs={X:eval})
    eqArea += delta * (heightEq)
    accArea += delta * (heightEq - heightLorenz)
endR = timer()
timeRiemann = endR - startR
rieGINI = accArea / eqArea
rieAccuracy = abs(100.0 - (abs(GINI_KNOWN - rieGINI) / GINI_KNOWN) * 100.0)

print("------------ RIEMANN ------------")
print("Enclosed Area:", accArea)
print("Equality Area:", eqArea)
print("GINI Coefficient:", rieGINI)
print("Time:", timeRiemann, "seconds")
print("Accuracy: {:0.2f} %".format(rieAccuracy))
print("----------------------------------")

tests = 50000
inside = 0.0
areaDomain = 100 * 100
insidePoints = []
outsidePoints = []

# For Monte Carlo visualization
'''
xx = np.linspace(0, 100, 1000)
yy = sympy.lambdify(X, [equality, lorenz])(xx)
plt.plot(xx, np.transpose(yy))
'''

startMC = timer()
for i in range(tests):
    xCoord = random.uniform(0, 100)
    yCoord = random.uniform(0, 100)

    if yCoord <= equality.evalf(subs={X:xCoord}) and yCoord >= lorenz.evalf(subs={X:xCoord}):
        inside += 1.0
        point = [xCoord, yCoord]
        insidePoints.append(point)
        #plt.plot(point[0], point[1], color='red', marker='o')
    '''
    # For Monte Carlo visualization
    else:
        point = [xCoord, yCoord]
        outsidePoints.append(point)
        plt.plot(point[0], point[1], color='blue', marker='o')

    if i == 10 or i == 50 or i == 100 or i == 500 or i == 1000 or i == 5000:
        plt.title('T = {}'.format(i))
        plt.savefig("T_{}.png".format(i),bbox_inches='tight')
    '''
endMC = timer()

timeMC = endMC - startMC
MonteCarloArea = (inside / tests) * areaDomain
mcGINI = MonteCarloArea / areaEquality
mcAccuracy = abs(100.0 - (abs(GINI_KNOWN - mcGINI) / GINI_KNOWN) * 100.0)
print("------------ MONTE CARLO ------------")
print("Enclosed Area:", MonteCarloArea)
print("GINI Coefficient:", mcGINI)
print("Time:", timeMC, "seconds")
print("Accuracy: {:0.2f} %".format(mcAccuracy))
print("------------------------------------")

# Plots
p1 = sympy.plotting.plot(lorenz, equality, (X, 0, xLimit), ylim=[0,100],show=False)
p1.title = "Lagrange Interpolation for GINI Approximation"
p1[0].line_color='r'

p2 = sympy.plotting.plot(lorenz, equality, (X, 0, xLimit), ylim=[0,100],show=False)
p2.title = "Lagrange Interpolation vs. Data Set Comparison"
p2.append(List2DSeries(Lx, yPos))
p2[2].line_color=(0.5, 0.5, 0.5)
p2[0].line_color='r'

# Time Performance visualization
finalTime = [timeLagrange, timeMC, timeRiemann]
labels = ["Python", "Monte Carlo", "Riemann"]
index = np.arange(len(labels))
viridis = matplotlib.cm.get_cmap('viridis', 5)
bars = plt.bar(index, finalTime, color=viridis.colors[2])
plt.xlabel('Method for Polynomial Integration', fontsize=13)
plt.ylabel('Time (s)', fontsize=13)
plt.xticks(index, labels, fontsize=8, rotation=0)
plt.title('Time Performance Comparison')

'''
# For Riemann visualization
xx = np.linspace(0, 100, 1000)
yy = sympy.lambdify(X, [equality, lorenz])(xx)
plt.plot(xx, np.transpose(yy))

f = sympy.lambdify(X, lorenz)
a = 0; b = 100; N = 10
n = 10
x = np.linspace(a,b,N+1)
y = f(x)
x_mid = (x[:-1] + x[1:])/2 # midpoints
y_mid = f(x_mid)
plt.bar(x_mid,y_mid,width=(b-a)/N,alpha=0.2,edgecolor='b')
#plt.title('Midpoint Riemann Sum, N = {}'.format(N))
'''

p1.show()
p2.show()
plt.show()


import unittest
import numpy as np
import math
import warnings

import ps5 as ps5

class TestPS5(unittest.TestCase):

    def test_01_linear_regression(self):
        #simple y = x case
        x = np.array([1,2,3,4,5,6,7,8,9,10])
        y = np.array([1,2,3,4,5,6,7,8,9,10])
        (m, b) = ps5.linear_regression(x, y)
        self.assertEqual(m, 1, "Calculated slope is incorrect")
        self.assertEqual(b, 0, "Calculated intercept is incorrect")

        #y = 5x - 3 case
        x = np.array([-5, -3, -1, 1, 2, 6, 10])
        y = np.array([-28, -18, -8, 2, 7, 27, 47])
        (m, b) = ps5.linear_regression(x, y)
        self.assertEqual(m, 5, "Calculated slope is incorrect")
        self.assertEqual(b, -3, "Calculated intercept is incorrect")

        #not exact line case - also tests for float handling
        x = np.array([0,1,2,3,4,5,6,7,8,9,10,11])
        y = np.array([-10,2,1,5,12,13,15,14,19,25,30,29])
        (m, b) = ps5.linear_regression(x, y)
        self.assertTrue(math.isclose(3.241, m, rel_tol=1e-3), "Calculated slope is incorrect")
        self.assertTrue(math.isclose(-4.910, b, rel_tol=1e-3), "Calculated intercept is incorrect")

    def test_02_squared_error(self):
        #easy case
        x = np.array([1,2,3,4,5,6,7,8,9,10])
        y = np.array([1,2,3,4,5,6,7,8,9,10])
        m = 1
        b = 0
        sqe = ps5.squared_error(x, y, m ,b)
        self.assertEqual(sqe, 0, "Squared Error incorrect")

        #interesting case
        x = np.array([0,1,2,3,4,5,6,7,8,9,10,11])
        y = np.array([-10,2,1,5,12,13,15,14,19,25,30,29])
        m = 3
        b = -5
        sqe = ps5.squared_error(x, y, m ,b)
        self.assertEqual(sqe, 119, "Squared Error incorrect")

    def test_03_generate_models(self):
        degs_msg = "make_models should return one model for each given degree"
        list_type_msg = "make_models should return a list of models"
        array_type_msg = "each model returned by make_models should be of type np.array"
        coefficient_mismatch = "coefficients of returned model are not as expected"

        # simple y = x case.
        x = np.array(range(50))
        y = np.array(range(50))
        degrees = [1]
        models = ps5.generate_models(x, y, degrees)

        self.assertEqual(len(models), len(degrees), degs_msg)
        self.assertIsInstance(models, list, list_type_msg)
        self.assertIsInstance(models[0], np.ndarray, array_type_msg)
        self.assertListEqual(list(models[0]), list(np.polyfit(x, y, 1)), coefficient_mismatch)

        # two models for y = 2x case
        y = np.array(range(0,100,2))
        degrees = [1, 2]
        models = ps5.generate_models(x, y, degrees)
        self.assertEqual(len(models), len(degrees), degs_msg)
        self.assertIsInstance(models, list, list_type_msg)
        for m in models:
            self.assertIsInstance(m, np.ndarray, array_type_msg)
        for i in range(2):
            self.assertListEqual(list(models[i]), list(np.polyfit(x,y, degrees[i])), coefficient_mismatch)

        # three models
        degrees = [1,2,20]
        models = ps5.generate_models(x, y, degrees)
        self.assertEqual(len(models), len(degrees), degs_msg)
        self.assertIsInstance(models, list, list_type_msg)
        for m in models:
            self.assertIsInstance(m, np.ndarray, array_type_msg)
        for i in range(3):
            self.assertListEqual(list(models[i]), list(np.polyfit(x,y, degrees[i])), coefficient_mismatch)

    def test_04_get_yearly_averages(self):
        # test for just one city
        climate = ps5.Dataset('data.csv')
        test_years = np.array(range(2008, 2017))
        result = climate.get_yearly_averages(['SEATTLE'], test_years)
        correct = [10.798633879781422, 11.218767123287673, 11.514383561643836, 10.586849315068493,
                    11.283196721311475, 12.106438356164384, 12.82917808219178, 13.13178082191781, 12.500546448087432]
        self.assertTrue(len(correct) == len(result), "Expected length %s, was length %s" % (len(correct), len(result)))
        for index in range(len(correct)):
            good_enough = math.isclose(correct[index], result[index])
            self.assertTrue(good_enough, "City averages do not match expected results.\nExpected Results:" + str(correct) + "\nYour Results:" + str(result))

        # national avg check (all cities)
        result = climate.get_yearly_averages(ps5.CITIES, test_years)
        correct = [16.130025884383087, 16.116286950252345, 16.334981975486663, 16.46957462148522,
                    17.173878343399483, 16.25620043258832, 16.47222062004326, 17.17817591925018, 17.198259994247916]
        self.assertTrue(len(correct) == len(result), "Expected length %s, was length %s" % (len(correct), len(result)))

        for index in range(len(correct)):
            good_enough = math.isclose(correct[index], result[index])
            self.assertTrue(good_enough, "City averages do not match expected results.\nExpected Results:" + str(correct) + "\nYour Results:" + str(result))

        # two-city check
        result = climate.get_yearly_averages(['TAMPA', 'DALLAS'], test_years)
        correct = [21.76502732240437, 21.245616438356162, 20.80404109589041, 22.039109589041097,
                    22.272062841530055, 21.3113698630137, 20.88123287671233, 22.077945205479455, 22.181557377049177]
        self.assertTrue(len(correct) == len(result), "Expected length %s, was length %s" % (len(correct), len(result)))

        for index in range(len(correct)):
            good_enough = math.isclose(correct[index], result[index])
            self.assertTrue(good_enough, "City averages do not match expected results.\nExpected Results:" + str(correct) + "\nYour Results:" + str(result))

    def test_05_evaluate_models(self):
        x = np.array(range(50))
        y = np.array(range(0,100,2))
        degrees = [1, 2]
        models = ps5.generate_models(x, y, degrees)
        r2 = ps5.evaluate_models(x, y, models, False)
        correct_r2 = [1,1]
        self.assertEqual(len(r2), len(correct_r2), "Returned incorrect r^2 values")
        for index in range(len(correct_r2)):
            good_enough = math.isclose(correct_r2[index], r2[index])
            self.assertTrue(good_enough, "Returned incorrect r^2 values")

    def test_06_find_extreme_trend(self):
        # Test 1: Existing positive and negative slope intervals on city data
        temp = ps5.Dataset('data.csv')
        test_years = np.array(range(1961, 2016))
        yearly_temps = temp.get_yearly_averages(['PORTLAND'], test_years)

        result_neg = ps5.find_extreme_trend(test_years, yearly_temps, 20, False)
        correct_start = 31
        correct_end = 51
        correct_slope = -0.032388717977292064
        self.assertIsNotNone(result_neg, "Returned None, but valid interval exists.")
        self.assertEqual(correct_start, result_neg[0], "Start year incorrect")
        self.assertEqual(correct_end, result_neg[1], "End year incorrect")
        self.assertTrue(abs(correct_slope-result_neg[2]) <= 1e-8, "Incorrect slope")

        result_pos = ps5.find_extreme_trend(test_years, yearly_temps, 20, True)
        correct_start = 15
        correct_end = 35
        correct_slope = 0.05643539541645478
        self.assertIsNotNone(result_pos, "Returned None, but valid interval exists.")
        self.assertEqual(correct_start, result_pos[0], "Start year incorrect")
        self.assertEqual(correct_end, result_pos[1], "End year incorrect")
        self.assertTrue(abs(correct_slope-result_pos[2]) <= 1e-8, "Incorrect slope")

        # Test 2: y = 2x
        x = np.array(range(50))
        y = np.array(range(0,100,2))
        result_pos = ps5.find_extreme_trend(x, y, len(x)//2, True)
        result_neg = ps5.find_extreme_trend(x, y, len(x)//2, False)
        pos_correct_start = 0
        pos_correct_end = len(x)//2
        pos_correct_slope = 2

        self.assertIsNone(result_neg, "Returned an interval, but should be None.")

        self.assertIsNotNone(result_pos, "Returned None, but valid interval exists.")
        self.assertEqual(pos_correct_start, result_pos[0], "Start year incorrect")
        self.assertEqual(pos_correct_end, result_pos[1], "End year incorrect")
        self.assertTrue(abs(pos_correct_slope-result_pos[2]) <= 1e-8, "Incorrect slope")

        # # Test 3: y = -2x
        x = np.array(range(50))
        y = np.array(range(100,0,-2))
        result_pos = ps5.find_extreme_trend(x, y, len(x)//2, True)
        result_neg = ps5.find_extreme_trend(x, y, len(x)//2, False)
        neg_correct_start = 0
        neg_correct_end = len(x)//2
        neg_correct_slope = -2

        self.assertIsNone(result_pos, "Returned an interval, but should be None.")

        self.assertIsNotNone(result_neg, "Returned None, but valid interval exists.")
        self.assertEqual(neg_correct_start, result_neg[0], "Start year incorrect")
        self.assertEqual(neg_correct_end, result_neg[1], "End year incorrect")
        self.assertTrue(abs(neg_correct_slope-result_neg[2]) <= 1e-8, "Incorrect slope")

        # Test 4: y = (x-10)^2
        x = np.array(range(21))
        y = (x-10)**2
        result_pos = ps5.find_extreme_trend(x, y, 5, True)
        result_neg = ps5.find_extreme_trend(x, y, 5, False)

        pos_correct_start = 16
        pos_correct_end = 21
        pos_correct_slope = 16.0

        neg_correct_start = 0
        neg_correct_end = 5
        neg_correct_slope = -16.0

        self.assertEqual(pos_correct_start, result_pos[0], "Start year incorrect")
        self.assertEqual(pos_correct_end, result_pos[1], "End year incorrect")
        self.assertTrue(abs(pos_correct_slope-result_pos[2]) <= 1e-8, "Incorrect slope")

        self.assertEqual(neg_correct_start, result_neg[0], "Start year incorrect")
        self.assertEqual(neg_correct_end, result_neg[1], "End year incorrect")
        self.assertTrue(abs(neg_correct_slope-result_neg[2]) <= 1e-8, "Incorrect slope")

    def test_07_find_all_extreme_trends(self):
        temp = ps5.Dataset('data.csv')
        test_years = np.array(range(1961, 1971))
        yearly_temps = temp.get_yearly_averages(['PORTLAND'], test_years)

        result = ps5.find_all_extreme_trends(test_years, yearly_temps)
        correct = [(3, 5, 2.0206939142151246), (3, 6, 0.9937031214911256), (3, 7, 0.6971533797439919),
                   (2, 7, 0.44123377498315597), (2, 8, 0.25528513682589676), (2, 9, 0.16264380353533894),
                   (2, 10, 0.145466766652766), (1, 10, 0.09771779324799829), (0, 10, 0.040402949322555)]
        self.assertIsNotNone(result, "Returned None, but valid interval exists.")
        self.assertEqual(len(result),len(test_years) - 1, "Returned list has incorrect number of elements")
        for index in range(len(correct)):
            self.assertTrue(len(result[index]) == 3, "One or more returned tuples have incorrect number of elements.")
            self.assertEqual(correct[index][:-1], result[index][:-1], "Extreme trend range does not match expected results")
            self.assertTrue(abs(correct[index][-1] - result[index][-1]) <= 1e-8, "Extreme trends do not match expected results")

        # Test 2: y = x
        x = np.array(range(6))
        y = np.array(range(0,6))
        correct = [(0, 2, 1.0), (0, 3, 1.0), (0, 4, 1.0), (0, 5, 1.0), (0,6,1.0)]
        result = ps5.find_all_extreme_trends(x, y)
        self.assertIsNotNone(result, "Returned None, but valid interval exists.")
        self.assertEqual(len(result),len(x) - 1, "Returned list has incorrect number of elements")
        for index in range(len(correct)):
            self.assertTrue(len(result[index]) == 3, "One or more returned tuples have incorrect number of elements.")
            self.assertEqual(correct[index][:-1], result[index][:-1], "Extreme trend range does not match expected results")
            self.assertTrue(abs(correct[index][-1] - result[index][-1]) <= 1e-8, "Extreme trends do not match expected results")

        # Test 3: y = -2x
        x = np.array(range(8))
        y = np.array(range(16,0,-2))
        correct = [(0, 2, -2.0), (0, 3, -2.0), (0, 4, -2.0), (0, 5, -2.0), (0, 6, -2.0), (0, 7, -2.0), (0,8,-2.0)]
        result = ps5.find_all_extreme_trends(x, y)
        self.assertIsNotNone(result, "Returned None, but valid interval exists.")
        self.assertEqual(len(result),len(x) - 1, "Returned list has incorrect number of elements")
        for index in range(len(correct)):
            self.assertTrue(len(result[index]) == 3, "One or more returned tuples have incorrect number of elements.")
            self.assertEqual(correct[index][:-1], result[index][:-1], "Extreme trend range does not match expected results")
            self.assertTrue(abs(correct[index][-1] - result[index][-1]) <= 1e-8, "Extreme trends do not match expected results")

        # Test 4: len(x) < 2
        x = np.array(range(1))
        y = np.array(range(1))
        correct = []
        result = ps5.find_all_extreme_trends(x,y)
        self.assertEqual(correct, result, "Incorrect result, expected empty list")

        # Test 5: y = (x-10)^2
        x = np.array(range(21))
        y = (x-10)**2

        result = ps5.find_all_extreme_trends(x,y)

        correct = [(0, 2, -19.0), (0, 3, -18.0), (0, 4, -17.0), (0, 5, -16.0), (0, 6, -15.0),
         (0, 7, -14.0), (0, 8, -13.0), (0, 9, -12.0), (0, 10, -11.0), (0, 11, -10.0),
         (0, 12, -9.000000000000002), (0, 13, -8.0), (0, 14, -7.0), (0, 15, -6.0),
         (0, 16, -5.0), (0, 17, -4.0), (0, 18, -2.9999999999999996), (0, 19, -2.0),
         (0, 20, -1.0), (0, 21, None)]

        self.assertIsNotNone(result, "Returned None, but valid interval exists.")
        self.assertEqual(len(result),len(x) - 1, "Returned list has incorrect number of elements")
        for index in range(len(correct)-1):
            self.assertTrue(len(result[index]) == 3, "One or more returned tuples have incorrect number of elements.")
            self.assertEqual(correct[index][:-1], result[index][:-1], "Extreme trend range does not match expected results")
            self.assertTrue(abs(correct[index][-1] - result[index][-1]) <= 1e-8, "Extreme trends do not match expected results")

        self.assertEqual(correct[-1][:-1], result[-1][:-1], "Extreme trend range does not match expected results")
        self.assertIsNone(correct[-1][-1], "Extreme trends do not match expected results, None expected for final value")

    def test_08_rmse(self):
        y = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        estimate = [1, 4, 9, 16, 25, 36, 49, 64, 81]
        result = ps5.rmse(np.array(y), np.array(estimate))
        correct = 35.8515457593
        self.assertTrue(math.isclose(result, correct), "RMSE value incorrect")

        y = [1, 1, 1, 1, 1, 1, 1, 1, 1]
        estimate = [1, 4, 9, 16, 25, 36, 49, 64, 81]
        result = ps5.rmse(np.array(y), np.array(estimate))
        correct = 40.513372278
        self.assertTrue(math.isclose(result, correct), "RMSE value incorrect")

    def test_09_evaluate_models_testing(self):
        x = np.array(range(50))
        y = np.array(range(0,100,2))
        degrees = [1, 2]
        models = ps5.generate_models(x, y, degrees)
        rmse = ps5.evaluate_models_testing(x, y, models, False)
        correct_rmse = [0,0]
        self.assertEqual(len(rmse), len(correct_rmse), "RMSE values did not match number of models")
        for index in range(len(correct_rmse)):
            self.assertTrue(math.isclose(correct_rmse[index], rmse[index], abs_tol=1e-8), "Returned incorrect RMSE values")

if __name__ == '__main__':
    # Run the tests and print verbose output to stderr.
    warnings.simplefilter('ignore', np.RankWarning)
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(TestPS5))
    unittest.TextTestRunner(verbosity=2).run(suite)


import numpy as np

def l2_distance(p, q):

    return np.linalg.norm(p - q)

def l2_normalize(v, axis=-1):

    norm = np.linalg.norm(v, axis=axis, keepdims=True)

    return np.divide(v, norm, where=norm!=0)


from sympy.abc import r
from sympy import *

"""
N-dimensional volume of a sphere derivation:

ND cube volume = r^n
ND sphere volume = C*r^n
where C is a proportionality constant, we need to derive
the equation for C.

Let V_n(r) be a function that returns the volume of a sphere in ND

The surface is then
dV_n(r)/dr = n * C_n * r^(n-1)

hence the volume of the elements are
dV_n(r)/dr * dr = n * C_n * r^(n-1) * dr

Set r^2 to be t and we can see the following relation to the gamma function

Cn = pi^(n/2) / Gamma(0.5*n + 1)

Use recurrence relation and get
C(n) = 2pi/n * C(n-2)

as n -> infinity, C(n) -> 0, this means that the volume of a sphere approaches 0
as the dimension increases, this tells you that the all the volume exists on its surface
"""

pi = Symbol('pi')

def C(n):
    if n <= 0:
        return 0

    # we can think of a 1d sphere as a line, so its area is the positive and the negative part
    # of the line, hence why it is 2
    if n == 1:
        return 2

    if n == 2:
        return pi

    return 2*pi/n * C(n-2)

def V(n):
    return simplify(r**n * C(n))

for i in range(1, 101):
    print(i, V(i))


import numpy as np
import matplotlib.pyplot as plt

## softmax: 0.1 600
## perceptron: 0.05 550

def readData(csvname):
    data = np.loadtxt(csvname, delimiter=',')
    x = data[:-1, :]
    y = data[-1:, :]

    return x, y

def softmaxCostFunc(x, y, w):
    cost = np.sum(np.log(1 + np.exp(-y*np.transpose(np.dot(np.transpose(x), w)))))
    return cost / float(np.size(y))

def gradientDescentOneStepForSoftmax(x, y, w, alpha=0.1):
    total = np.zeros([9,1])
    for i in range(np.size(y)):
        power = np.exp(-y[:,i] * np.dot(x[:,i], w))
        term = power / (1 + power)
        total += term * y[:,i] * x[:,[i]]

    w = w + alpha * (1/np.size(y)) * total
    return w

def perceptronCostFunc(x, y, w):
    cost = 0
    a = (-y*np.transpose(np.dot(np.transpose(x), w)))[0]
    for i in range(len(a)):
        cost += a[i] if (a[i] > 0) else 0
    return cost / float(np.size(y))

def gradientDescentOneStepForPerceptron(x, y, w, alpha=0.05):
    total = np.zeros([9,1])
    for i in range(np.size(y)):
        term = -y[:,i] * np.dot(x[:,[i]].T, w)
        total += 0 if term <= 0 else -y[:,i] * x[:,[i]]

    w = w - alpha * (1/np.size(y)) * total
    return w

if  __name__  ==  "__main__":
    csvname = 'breast_cancer_data.csv'
    x, y = readData(csvname)
    w = np.ones([x.shape[0] + 1, 1])
    x = np.insert(x, 0, values=np.ones([1, x.shape[1]]), axis=0)

    xSoftList = [0]
    ySoftList = [softmaxCostFunc(x, y, w)]
    for i in range(600):
        w = gradientDescentOneStepForSoftmax(x, y, w)
        xSoftList.append(i+1)
        ySoftList.append(softmaxCostFunc(x, y, w))

    yPredic = np.transpose(np.dot(np.transpose(x), w))
    wrong = 0
    for i in range(np.size(yPredic)):
        if ((yPredic[0][i] > 0) != (y[0][i] > 0)):
            wrong += 1
    print("Softmax Wrong Prediction: ", wrong)

    w = np.ones([x.shape[0], 1])
    xPerceptronList = [0]
    yPerceptronList = [perceptronCostFunc(x, y, w)]
    for i in range(550):
        w = gradientDescentOneStepForPerceptron(x, y, w)
        xPerceptronList.append(i+1)
        yPerceptronList.append(perceptronCostFunc(x, y, w))

    plt.plot(xSoftList, ySoftList, label="Softmax Cost Function",color="#F08080")
    plt.plot(xPerceptronList, yPerceptronList, label="Perceptro Cost Function")
    plt.legend(loc="upper right")
    plt.show()
    plt.close()

    yPredic = np.transpose(np.dot(np.transpose(x), w))
    wrong = 0
    for i in range(np.size(yPredic)):
        if ((yPredic[0][i] > 0) != (y[0][i] > 0)):
            wrong += 1
    print("Perceptron Wrong Prediction: ", wrong)


import numpy as np

def mobius_add(u, v):
    norms_u = np.sum(np.power(u, 2), -1, keepdims=True)
    norms_v = np.sum(np.power(v, 2), -1, keepdims=True)
    inner_product = np.sum(u * v, -1, keepdims=True)
    denominator = 1 + 2 * inner_product + norms_u * norms_v
    numerator = (1 + 2 * inner_product + norms_v) * u + (1 - norms_u) * v
    results = numerator / denominator
    return results

def compute_hyperbolic_distances(vectors_u, vectors_v):
    """
    Compute poincare distances between input vectors.
    Modified based on gensim code.
    vectors_u: (batch_size, dim)
    vectors_v: (batch_size, dim)
    """
    euclidean_dists = np.linalg.norm(vectors_u - vectors_v, axis=1)  # (batch_size, )
    norms_u = np.linalg.norm(vectors_u, axis=1)  # (batch_size, )
    norms_v = np.linalg.norm(vectors_v, axis=1)  # (batch_size, )
    alpha = 1 - norms_u ** 2  # (batch_size, )
    beta = 1 - norms_v ** 2  # (batch_size, )
    gamma = 1 + 2 * ((euclidean_dists ** 2) / (alpha * beta))  # (batch_size, )
    poincare_dists = np.arccosh(gamma + 1e-10)  # (batch_size, )
    return poincare_dists  # (batch_size, )
    # distance = 2 * np.arctanh(np.linalg.norm(mobius_add(vectors_u, vectors_v), axis=1))
    # return distance

def compute_hyperbolic_similarity(embeds1, embeds2):
    embeds1 = embeds1.cpu()
    embeds2 = embeds2.cpu()
    x1, y1 = embeds1.shape  # <class 'numpy.ndarray'>
    x2, y2 = embeds2.shape
    assert y1 == y2
    dist_vec_list = list()
    for i in range(x1):
        embed1 = embeds1[i, :]  # <class 'numpy.ndarray'> (y1,)
        embed1 = np.reshape(embed1, (1, y1))  # (1, y1)
        embed1 = np.repeat(embed1, x2, axis=0)  # (x2, y1)
        dist_vec = compute_hyperbolic_distances(embed1, embeds2)
        dist_vec_list.append(dist_vec)
    dis_mat = np.row_stack(dist_vec_list)  # (x1, x2)
    # return (-dis_mat)
    # return np.exp(-dis_mat)  # sim mat
    return normalization(-dis_mat)

def normalization(data):
    _range = np.max(data) - np.min(data)
    return (data - np.min(data)) / _range


import numpy as np

def euclidean_distance(v1, v2, axis=0):
    return np.sqrt(euclidean_squared_distance(v1, v2, axis))

def euclidean_squared_distance(v1, v2, axis=0):
    return np.sum((v1 - v2) ** 2, axis=axis)

def manhattan_distance(v1, v2, axis=0):
    return np.sum(np.abs(v1 - v2), axis=axis)


import numpy as np
import numpy.testing as npt

from numpy import (cos, exp, log, sin, sqrt)

from ..dopri5timeintegrator import Dopri5TimeIntegrator

class TestDopri5TimeIntegrator:
    def test__exponential_growth__works_ok(self):
        rhs = lambda y: y
        dt = 0.1
        tfinal = 1.0

        t = 0.0

        times = []
        solutions = []

        sut = Dopri5TimeIntegrator(rhs)
        sut.set_initial_value(1.0)

        t_step = 0
        while t < tfinal:
            t_step += 1
            t = t_step * dt

            sut.integrate(t)

            times.append(sut.t)
            solutions.append(sut.y[0])

        npt.assert_allclose(solutions, exp(times), rtol=1e-6)

    def test__exponential_decay__converges_to_3rd_order(self):
        tfinal = 5.0
        y0 = 1.0

        rhs_func = lambda y: -y

        powers = np.array(range(2, 11))
        numpoints = 2**powers
        errors = []

        for nums in numpoints:
            times, dt = np.linspace(0.0, tfinal, num=nums, retstep=True)
            times = times[1:]

            solutions = []

            # Use very coarse tolerance to measure convergence order.
            sut = Dopri5TimeIntegrator(rhs_func, rtol=1, atol=1)
            sut.set_initial_value(y0)

            for t in times:
                sut.integrate(t)
                solutions.append(sut.y[0])

            exact = exp(-times)
            errors.append(sqrt(dt) * np.linalg.norm(solutions - exact, 2))

        errors = np.array(errors)
        observed_orders = log(errors[0:-1]/errors[1:]) / log(2)

        expected_order = 4.0
        npt.assert_(np.all(observed_orders >= expected_order))

    def test__simple_oscillator__works_ok(self):
        k = 4.0
        m = 1.0
        omega = sqrt(k / m)

        y0, ydot0 = 1.0, 0.1
        ic = (y0, ydot0)

        tfinal = 1.0 + 0.09
        t = 0.0
        dt = 0.01

        rhs = lambda y: np.array([y[1], -omega**2 * y[0]])

        times = [t]
        solutions = [y0]

        sut = Dopri5TimeIntegrator(rhs)
        sut.set_initial_value(ic)

        t_step = 0
        while t < tfinal:
            t_step += 1
            t = t_step * dt

            sut.integrate(t)

            times.append(t)
            solutions.append(sut.y[0])

        times = np.array(times)

        c1, c2 = y0, ydot0/omega
        exact_solutions = c1 * cos(omega * times) + c2 * sin(omega * times)

        npt.assert_allclose(solutions, exact_solutions, rtol=1e-16, atol=1e-12)


"""
Created on Apr 14 23:59:42 2022
"""
from typing import Union

import numpy as np

np_arr = np.ndarray

FloatStr = Union[float, str]
FloatStrArr = Union[float, str, np_arr]

def change_instance(in_obj: FloatStr, in_type: str = 'dms') -> float:
    """
    Check if given input is DMS/HMS string and convert it to float.

    Parameters
    ----------
    in_obj : FloatStr
        Input object as HMS/DMS.
    in_type : str, optional
        Type of conversion. The default is 'dms'.

    Returns
    -------
    float
        DESCRIPTION.

    """
    if isinstance(in_obj, str):
        out = dms2dd(in_obj) if in_type == 'dms' else hms2dd(in_obj)
    else:
        out = in_obj

    return out

def altitude2zenith(altitude: FloatStrArr, deg_rad: bool = True) -> float:
    """
    Convert the given altitude to its complementary zenith angle.

    Parameters
    ----------
    altitude : FloatStrArr
        Altitude of the given celestial object.
    deg_rad : bool, optional
        Whether the given altitude measurement is in degrees or radians. Default is True.

    Returns
    -------
    float
        Complementary zenith angle for the corresponding altitude angle.

    """

    altitude = change_instance(altitude, 'dms')

    return 90 - altitude if deg_rad else np.pi / 2 - altitude

def zenith2altitude(zenith_angle: FloatStrArr, deg_rad: bool = True) -> float:
    """
    Convert the given zenith angle to its complementary altitude angle.

    Parameters
    ----------
    zenith_angle : FloatStrArr
        Zenith angle of the given celestial object.
    deg_rad : bool, optional
        Whether the given zenith angle measurement is in degrees or radians.
        The default is True.

    Returns
    -------
    float :
        Complementary altitude angle for the corresponding zenith angle.

    """

    zenith_angle = dms2dd(zenith_angle) if type(zenith_angle) == str else zenith_angle

    return 90 - zenith_angle if deg_rad else np.pi / 2 - zenith_angle

def dms2dd(dms: str) -> float:
    """
    Convert given degree minute second to degree decimal format.

    Parameters
    ----------
    dms : str
        String representing the degree-minute-second value.

    Returns
    -------
    float
        Degree decimal equivalent of the DMS input.

    Notes
    -------
        List conversion is possible

    """

    deg, minute, sec = [float(j) for j in dms.split(':')]

    # check for negative degree value
    if deg < 0:
        minute, sec = float(f'-{minute}'), float(f'-{sec}')

    return deg + minute / 60 + sec / 3600

def dd2dms(degree_decimal: float) -> str:
    """
    Convert given degree decimal format to degree minute seconds.

    Parameters
    ----------
    degree_decimal : float
        Degree decimal value.

    Returns
    -------
    str
        DMS equivalent of the input degree decimal value.

    """

    # get the truncated value
    _d = np.trunc(degree_decimal)

    # get the residual
    _deg_residual = abs(degree_decimal - _d)

    # get minutes
    __min = _deg_residual * 60
    _m = np.trunc(__min)

    # get the residual
    _min_residual = abs(__min - _m)

    # get seconds
    _s = round(_min_residual * 60, 4)

    _m, _s = [_m + 1, '00'] if _s == 60 else [_m, _s]

    if int(_s) == _s:
        _s = int(_s)

    return f'{int(_d)}:{int(_m)}:{_s}'

def hms2dd(hms: str) -> float:
    """
    Convert a given hour-minute-second string to degree decimal.

    Parameters
    ----------
    hms : str
        String representing the hour-minute-second value.

    Returns
    -------
    float
        Degree decimal value of the corresponding HMS input value.

    """

    hour, minute, sec = [float(i) for i in hms.split(':')]

    if hour < 0:
        print('RA value cannot be negative, assuming positive.')
        hour = -hour

    return hour * 15 + (minute / 4.) + (sec / 240.)

def dd2hms(degree_decimal: float) -> str:
    """
    Convert degree decimal to its corresponding HMS notation.

    Parameters
    ----------
    degree_decimal : float
        Degree decimal value for the position of the object.

    Returns
    -------
    str
        Corresponding HMS value for the input DD value.

    """

    if degree_decimal < 0:
        print('dd for HMS conversion cannot be negative, assuming positive.')
        _dd = -degree_decimal / 15
    else:
        _dd = degree_decimal / 15

    # get the truncated value
    _d = np.trunc(_dd)

    # get the residual
    _deg_residual = abs(_dd - _d)

    # get minutes
    __min = _deg_residual * 60
    _m = np.trunc(__min)

    # get the residual
    _min_residual = abs(__min - _m)

    # get seconds
    _s = round(_min_residual * 60, 4)

    _m, _s = [_m + 1, '00'] if _s == 60 else [_m, _s]

    if int(_s) == _s:
        _s = int(_s)

    return f'{int(_d)}:{int(_m)}:{_s}'

def RA2HA(right_ascension: FloatStr, local_time: FloatStr) -> str:
    """
    Converts right ascension to its corresponding hour angle value depending upon the
    given local time.

    Parameters
    ----------
    right_ascension : FloatStr
        Right ascension value for the celestial object. It can either be a string with
        HH:MM:SS format or a float number representing the right ascension value.
    local_time : FloatStr
        Local time for the observer.

    Returns
    -------
    str
        Hour angle value of the object in the sky according to observer's local time.

    """

    _ra = change_instance(right_ascension, 'hms')
    _lt = change_instance(local_time, 'hms')

    if _ra > _lt:
        _lt += 360

    return dd2hms(_lt - _ra)

def HA2RA(hour_angle: FloatStrArr, local_time: FloatStr) -> str:
    """
    Converts hour angle to its corresponding right ascension value depending upon the
    given local time.

    Parameters
    ----------
    hour_angle : FloatStr
        Hour angle value for the celestial object.
    local_time : FloatStr
        Local time for the observer.

    Returns
    -------
    str
        Right ascension value of the object in the sky according to observer's local time.

    Notes
    -------
        The hour_angle and local_time parameters can either be a string with HH:MM:SS
        format or a float number representing the degree decimal representation of
        their values.
    """

    _ha = change_instance(hour_angle, 'hms')
    _lt = change_instance(local_time, 'hms')

    if _ha > _lt:
        _lt += 360

    return dd2hms(_lt - _ha)