import altair as alt
import numpy as np
import pandas as pd
import streamlit as st

import scipy.stats

def normal_distribution():
    param_range = st.sidebar.slider('Range', value=20.0, min_value=0.0, max_value=100.0, step=0.1, format='%.1f')
    param_mean = st.sidebar.slider('Mean', value=0.0, min_value=-30.0, max_value=30.0, step=0.1, format='%.1f')
    param_std = st.sidebar.slider('Standard deviation', value=3.0, min_value=0.1, max_value=20.0, step=0.1, format='%.1f')

    x = np.linspace(start=-param_range, stop=param_range, num=400)

    with st.expander('Plot of PDF', expanded=True):
        y = scipy.stats.norm.pdf(x, param_mean, param_std)
        pdf_data = pd.DataFrame.from_dict({
            'x': x,
            'probability density': y,
        })
        line_chart = alt.Chart(pdf_data).mark_line().encode(
            x='x',
            y='probability density',
            tooltip=['x', 'probability density'],
        )
        st.altair_chart(line_chart.interactive(), use_container_width=True)

    with st.expander('Plot of CDF', expanded=False):
        y = scipy.stats.norm.cdf(x, param_mean, param_std)
        cdf_data = pd.DataFrame.from_dict({
            'x': x,
            'cumulative density': y,
        })
        line_chart = alt.Chart(cdf_data).mark_line().encode(
            x='x',
            y='cumulative density',
            tooltip=['x', 'cumulative density'],
        )
        st.altair_chart(line_chart.interactive(), use_container_width=True)

    with st.expander('Formulae', expanded=False):
        st.markdown(formulae_text)

    with st.expander('LaTeX', expanded=False):
        st.markdown(latex_text)

formulae_text = r'''Parameters:
- mean: $\mu\in\mathbb{R}$
- standard deviation: $\sigma\in\mathbb{R}^+$
---
Support:
- $x\in\mathbb{R}$
---
Moments:
- $\mathrm{E}(X) = \mu$
- $\mathrm{Var}(X) = \sigma^2$
---
Probability density function (PDF):

$$
f(x|\mu,\sigma) = \frac{1}{\sqrt{2\pi\sigma^2}}\text{exp}\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)
$$
---
Cumulative distribution function (CDF):

$$
F(x|\mu,\sigma) = \frac{1}{2}\left[1+\text{erf}\left(\frac{x-\mu}{\sigma\sqrt{2}}\right)\right]
$$

where

$$
\text{erf}(x) = \frac{2}{\sqrt{\pi}}\int_{0}^{x} e^{-t^2}\mathrm{d}t
$$
is the error function.
'''

latex_text = r'''Moments:
```latex
\mathrm{E}(X) = \mu
```
```latex
\mathrm{Var}(X) = \sigma^2
```
---
Probability density function (PDF):
```latex
f(x|\mu,\sigma) = \frac{1}{\sqrt{2\pi\sigma^2}}\text{exp}\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)
```
---
Cumulative distribution function (CDF):
```latex
F(x|\mu,\sigma) = \frac{1}{2}\left[1+\text{erf}\left(\frac{x-\mu}{\sigma\sqrt{2}}\right)\right]
```
where
```latex
\text{erf}(x) = \frac{2}{\sqrt{\pi}}\int_{0}^{x} e^{-t^2}\mathrm{d}t
```
is the error function.
'''


# %BST_LICENCE_TEXT%

import numpy as np
from math import factorial, sqrt
from scipy.special import lpmv

def sphHarmReal( n, m, theta, phi, dtype = None ):
    """
    Real spherical harmonics function.
    """
    def trig( m, phi, dtype ):
        if m == 0:
            return np.ones( phi.shape, dtype = dtype )
        elif m < 0:
            return np.sin( phi * abs(m) ) * sqrt(2.0)
        else:
            return np.cos( phi * m ) * sqrt(2.0)

    if dtype is None:
        dtype = np.asarray(theta).dtype

    (thetaBC, phiBC) = np.broadcast_arrays( theta, phi )
    mabs = abs(m)

    fac = sqrt( ((2*n+1)*factorial(n-mabs)) /(4*np.pi*factorial(n+mabs)))
    # Apparently we need to compensate for the Condon-Shortley phase
    # Although the scipy documentation says
    # "Note that this implementation includes the Condon-Shortley phase."
    lp = fac * (-1)**mabs * lpmv(  mabs, n, np.cos(thetaBC) )
    return trig( m, phiBC, dtype ) * lp

def allSphHarmRealACN( N, theta, phi, dtype = None ):
    numCoeffs = (N+1)*(N+1)
    [thetaBC, phiBC ] = np.broadcast_arrays( theta, phi )
    argShape = thetaBC.shape
    if dtype is None:
        dtype = thetaBC.dtype
    Y = np.NaN * np.ones( (numCoeffs,)+argShape, dtype = dtype )
    for n in range(0,N+1):
        for m in range(-n,n+1):
            Y[n**2+n+m,...] = sphHarmReal( n, m, theta, phi, dtype )
    return Y


import numpy as np
from functions import get_matinc, PeriodicBoundary
from fenics import (Constant, FunctionSpace, UnitSquareMesh, UnitCubeMesh, inner,
                    MeshFunction, SubDomain, between, Measure, DirichletBC,
                    TrialFunction, TestFunction, Function, grad, assemble, solve)

# PARAMETERS
dim   = 2   # dimension (works for 2D and 3D)
N     = 5*np.ones(dim, dtype=np.int) # number of voxels (assumed equal for all directions)
phase = 10. # material contrast
order = 1   # polynomial order in FE space

# auxiliary values
prodN = np.prod(np.array(N)) # number of grid points
vec_shape=(dim,)+tuple(N) # shape of the vector for storing DOFs

# PROBLEM DEFINITION
_mat, _inc = get_matinc(dim, phase) # material coef. for matrix (mat) and inclusion (inc)
mat=Constant(_mat)
inc=Constant(_mat+_inc)

class Inclusion_2d(SubDomain): # square inclusion
    def inside(self, x, on_boundary):
        return (between(x[1], (0.2, 0.8)) and between(x[0], (0.2, 0.8)))

class Inclusion_3d(SubDomain): # cube inclusion
    def inside(self, x, on_boundary):
        return (between(x[2], (0.2, 0.8)) and between(x[1], (0.2, 0.8)) and between(x[0], (0.2, 0.8)))

E=np.zeros(dim); E[0]=1. # macroscopic value
E=Constant(E)

if dim==2:
    mesh=UnitSquareMesh(*N) # generation of mesh
    inclusion=Inclusion_2d()
    point0="near(x[0], 0) && near(x[1], 0)"
elif dim==3:
    mesh=UnitCubeMesh(*N) # generation of mesh
    inclusion=Inclusion_3d()
    point0="near(x[0], 0) && near(x[1], 0) && near(x[2], 0)"

V=FunctionSpace(mesh, "CG", order, constrained_domain=PeriodicBoundary(dim))

# setting the elements that lies in inclusion and in matrix phase
domains=MeshFunction("size_t", mesh, dim)
domains.set_all(0)
inclusion.mark(domains, 1)
dx=Measure('dx', subdomain_data=domains)

def bilf(up, vp): # bilinear form
    return inner(mat*up, vp)*dx(0)+inner(inc*up, vp)*dx(1)

bc0=DirichletBC(V, Constant(0.), point0, method='pointwise')

# SOLVER
u=TrialFunction(V)
v=TestFunction(V)
uE = Function(V)

solve(bilf(grad(u), grad(v)) == -bilf(E, grad(v)), uE, bcs=[bc0])

# POSTPROCESSING evaluation of guaranteed bound
AH11 = assemble(bilf(grad(uE)+E, grad(uE)+E))
print('homogenised component A11 = {} (FEM)'.format(AH11))

print('END')


'''
Polynomial Feature Transform

Summary
-------
Feature transform that takes a vector [x y z],
and returns an order-r transform [1 x^1 x^2 x^3 y^1 y^2 y^3 z^1 z^2 z^3]

API
---
Follows an sklearn-like "transform" interface
* __init__()
* transform
'''

import numpy as np

class PolynomialFeatureTransform:

    def __init__(self, order=1, input_dim=1, input_feature_names=None):
        ''' Create FeatureTransform that does polynomial transform of each feature up to given order
        '''
        if order < 0:
            raise ValueError("Polynomial order cannot be negative")
        if input_dim < 1:
            raise ValueError(
                "Input dimension must be an integer greater than or equal to 1")
        self.input_dim = int(input_dim)
        self.order = int(order)
        self.output_dim = self.input_dim * order + 1
        if input_feature_names is None:
            self.input_feature_names = ['x%2d' %
                                        a for a in range(self.input_dim)]
        else:
            self.input_feature_names = [str(a) for a in input_feature_names]

    def get_feature_size(self):
        return self.output_dim

    def get_feature_names(self):
        ''' Get list of string names, one for each transformed feature

        Examples
        --------
        >>> tfm2 = PolynomialFeatureTransform(order=3, input_dim=2, input_feature_names=['a', 'b'])
        >>> tfm2.get_feature_names()
        ['bias', 'a^1', 'b^1', 'a^2', 'b^2', 'a^3', 'b^3']
        '''
        feat_names = ['bias']
        for P in range(1, self.order+1):
            feat_names += map(lambda s: '%s^%d' % (s,P),
                              self.input_feature_names)
        return feat_names

    def transform(self, x_ND):
        ''' Perform feature transformation on raw input measurements

        Examples
        --------
        >>> x_ND = np.arange(0, 5)[:, np.newaxis]
        >>> x_ND
        array([[0],
               [1],
               [2],
               [3],
               [4]])
        >>> tfm0 = PolynomialFeatureTransform(order=0)
        >>> tfm0.transform(x_ND)
        array([[1],
               [1],
               [1],
               [1],
               [1]])
        >>> tfm1 = PolynomialFeatureTransform(order=1)
        >>> tfm1.transform(x_ND)
        array([[1, 0],
               [1, 1],
               [1, 2],
               [1, 3],
               [1, 4]])
        >>> tfm2 = PolynomialFeatureTransform(order=2)
        >>> tfm2.transform(x_ND)
        array([[ 1,  0,  0],
               [ 1,  1,  1],
               [ 1,  2,  4],
               [ 1,  3,  9],
               [ 1,  4, 16]])
        '''
        N, D = x_ND.shape
        if not self.input_dim == D:
            raise ValueError(
                "Mismatched input dimension. Expected %d but received %d" % (self.input_dim, D))

        phi_NM = np.zeros((N, self.output_dim), dtype=x_ND.dtype)

        phi_NM[:, 0] = 1.0
        m_dim = 1
        for P in range(1, self.order+1):
            phi_NM[:, m_dim:(m_dim + D)] = np.power(x_ND, P)
            m_dim += D
        return phi_NM


# plots.py
from __future__ import division
import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')

import numpy as np
from numpy import array as arr
from scipy.interpolate import interp1d
from scipy.linalg import solve
import matplotlib.pyplot as plt
from matplotlib.axis import Axis

from solutions import ode_fe, cheb
from tridiag import tridiag

def tridiagonal_approach():
	epsilon, alpha, beta = .02, 2., 4.
	# N = number of subintervals or finite elements

	def AnalyticSolution(x,alpha, beta,epsilon):
		out = alpha+x+(beta-alpha-1.)*(np.exp(x/epsilon) -1.)/(np.exp(1./epsilon) -1.)
		return out

	def matrix_diagonals(N):
		x = np.linspace(0,1,N+1)**(1.) # N+1 = number of grid points
		h = np.diff(x)
		b, f = np.zeros(N+1), np.zeros(N+1)
		a, c= np.zeros(N), np.zeros(N)
		b[0], b[N] = 1., 1.
		f[0], f[N] = alpha, beta
		# i = 0 to N-2
		b[1:N] = -epsilon*(1./h[0:N-1] + 1./h[1:N])
		f[1:N] = -.5*(h[0:N-1] + h[1:N])
		c[1:N] = epsilon/h[1:N] - .5
		a[0:N-1] = epsilon/h[0:N-1] + .5
		return a, b, c, f, x

	n = [2**i for i in range(4,22)]

	max_error_fe = [10]*(len(n))
	h = [1./num for num in n]
	for j in range(len(n)):
		a, b, c, f, x = matrix_diagonals(n[j])
		numerical_soln = tridiag(a,b,c,f)
		analytic_soln = AnalyticSolution(x, alpha, beta,epsilon)
		max_error_fe[j] = np.max(np.abs(numerical_soln - analytic_soln))

	n2 = [2*i for i in range(4,50)]
	max_error_cheb = [10]*len(n2)
	for j in range(len(n2)):
		N = n2[j]
		D,x = cheb(N)
		M = 4*epsilon*D.dot(D) -2.*D
		M[0] = 0.
		M[-1] = 0.
		M[0,0] = 1.
		M[-1,-1] = 1.

		F = -np.ones(len(x))
		F[0] = 4    # beta
		F[-1] = 2   # alpha

		cheb_sol = solve(M,F)
		analytic_soln = AnalyticSolution((x+1.)/2., alpha, beta,epsilon)
		max_error_cheb[j] = np.max(np.abs(cheb_sol - analytic_soln))

	plt.loglog(n2,max_error_cheb,'-b',linewidth=2.,label='Chebychev Error')

	plt.loglog(n,max_error_fe,'-k',linewidth=2.)
	plt.xlabel('$n$',fontsize=16)
	plt.ylabel('$E(n)$',fontsize=16)
	# plt.savefig("FEM_error_2nd_order.pdf")
	plt.show()
	plt.clf()

def basis_functions_plot():
	fig = plt.figure()
	ax = fig.add_subplot(111)
	# ax = fig.add_axes([-.1,1.1, -.1,1.1])

	# Basis function phi_0
	x, y = arr([0,.2,.4,1.]), arr([1.,0.,0.,0.])
	ax.plot(x,y,'-k',linewidth=2.0)
	ax.text(0.,1.02,r"$\phi_0$",fontsize=18)

	# Basis function phi_1
	x, y = arr([0,.2,.4,1.]), arr([0.,1.,0.,0.])
	ax.plot(x,y,color='g',lw=2.0)
	ax.text(0.2,1.02,r"$\phi_1$",fontsize=18)

	# Basis function phi_2
	x, y = arr([0,.2,.4,.6,1.]), arr([0.,0.,1.,0.,0.])
	ax.plot(x,y,color='r',lw=2.0)
	ax.text(0.4,1.02,r"$\phi_2$",fontsize=18)

	# Basis function phi_3
	x, y = arr([0,.4,.6,.8,1.]), arr([0.,0.,1.,0.,0.])
	ax.plot(x,y,'-m',linewidth=2.0)
	ax.text(0.6,1.02,r"$\phi_3$",fontsize=18)

	# Basis function phi_4
	x, y = arr([0,.6,.8,1.]), arr([0.,0.,1.,0.])
	ax.plot(x,y,'-b',linewidth=2.0)
	ax.text(0.8,1.02,r"$\phi_4$",fontsize=18)

	# Basis function phi_5
	x, y = arr([0,.8,1.]), arr([0.,0.,1.])
	ax.plot(x,y,'-c',linewidth=2.0)
	ax.text(1.,1.02,r"$\phi_5$",fontsize=18)

	ax.set_ylim(-.1,1.1)
	ax.set_xlim(-.1,1.1)
	plt.xticks(np.linspace(0.,1.,6),['$x_0$','$x_1$','$x_2$','$x_3$','$x_4$','$x_5$'],fontsize=18)
	# print Axis.get_majorticklabels(plt.axis)
	# plt.savefig("basis_functions.pdf")
	plt.clf()

def one_basis_function_plot():
	fig = plt.figure()
	ax = fig.add_subplot(111)
	# ax = fig.add_axes([-.1,1.1, -.1,1.1])

	# Basis function phi_3
	x, y = arr([0,.4,.6,.8,1.]), arr([0.,0.,1.,0.,0.])
	ax.plot(x,y,'-m',linewidth=2.0)
	ax.text(0.6,1.02,r"$\phi_3$",fontsize=18)

	ax.set_ylim(-.1,1.1)
	ax.set_xlim(-.1,1.1)
	# plt.xticks(np.linspace(0.,1.,6),['$x_0$','$x_1$','$x_2$','$x_3$','$x_4$','$x_5$'],fontsize=18)
	plt.xticks(arr([.4,.6,.8]),['$x_2$','$x_3$','$x_4$'],fontsize=18)
	# print Axis.get_majorticklabels(plt.axis)
	# plt.savefig("one_basis_function.pdf")

	# plt.show()
	plt.clf()

if __name__ == "__main__":
	# basis_functions_plot()
	# one_basis_function_plot()
	tridiagonal_approach()


import matplotlib.pyplot as plt
import numpy as np
import sys
#coding: utf-8

def main(argv):
	qtdValores, parametros, x, y = int(sys.argv[1]), [], [], []

	for param in sys.argv[2:]: # Transformar parametros para float
		parametros.append(float(param))

	for copy in parametros[:qtdValores]: # Copiar parametros equivalentes a X passados no CMD e adicioná-los ao vetor X
		x.append(copy)
	print(f'Valores de X: {x}')

	for copy in parametros[len(x):(2*qtdValores)]: # Copiar parametros equivalentes a Y passados no CMD e adicioná-los ao vetor Y
		y.append(copy)
	print(f'Valores de Y: {y}')

	grafico(qtdValores, x, y)

def Px(numVars, x, y, pontoXnovo):
	phi=float(0)
	for i in range(numVars):
		phi += (y[i]*Lk(i, numVars, x, pontoXnovo))
	return phi

def Lk(count, numVars, x, pontoXnovo):
	l=float(1)
	for k in range(numVars):
		if (k!=count):
			l *= ((pontoXnovo-x[k])/(x[count]-x[k]))
	return l

def grafico(numVars, x, y):
	intervaloX = []
	intervaloX = np.linspace(x[0], x[len(x)-1])
	phi=[]
	for i in range(len(intervaloX)):
		phi.append(Px(numVars, x, y, intervaloX[i]))
	plt.plot(intervaloX, phi, label='P(x)')
	plt.plot(x, y, 'o', label='(x,y)')
	plt.xlabel("x")
	plt.ylabel("y")
	plt.legend()
	plt.show()

if __name__ == '__main__':
	main(sys.argv[1:])


import numpy as np

def koch_snowflake(nsteps, initial=None):
    '''Koch snowflake based on equil. triangle with base [0, 0] -- [1, 0]'''
    assert nsteps > 0

    if initial is None:
        fractal = [np.zeros(2),
                   np.array([np.cos(np.pi/3), np.sin(np.pi/3)]),
                   np.array([1, 0])]
    else:
        assert initial.shape == (3, 2)
        fractal = [intial[0], initial[1], initial[2]]
    # Check equilateral
    _, = set(np.round(np.linalg.norm(fractal[i]-fractal[j]), 8)
             for (i, j) in ((0, 1), (1, 2), (2, 0)))

    # Rot.
    R = np.array([[np.cos(np.pi/3), -np.sin(np.pi/3)],
                  [np.sin(np.pi/3), np.cos(np.pi/3)]])

    def koch_rule(A, B, R=R):
        # 3 segments
        X, Y = A + (B-A)/3., A + 2*(B-A)/3.
        # Grow the tent
        Z = X + R.dot(Y-X)

        return [X, Z, Y]

    fractal = grow_fractal(fractal, koch_rule, nsteps)

    return fractal

def grow_fractal(fractal, rule, nsteps):
    '''Grow fractal applying the rule to consecutive pairs of points'''
    # Base case
    if nsteps == 0:
        return fractal

    l = len(fractal)
    # Preconpute new points for current pairs. Note that we assume that
    # points form a loop
    new_pts = [rule(fractal[i], fractal[(i+1)%l]) for i in range(l)]
    # Insert new points in between segments of the old fractal
    fractal = sum(([p]+pts for p, pts in zip(fractal, new_pts)), [])

    L = len(fractal)
    perimeter = sum(np.linalg.norm(fractal[i]-fractal[(i+1)%L]) for i in range(L))
    print(f'Fractal npoints change {l} to {L}. Perimeter {perimeter}')
    # Next round
    return grow_fractal(fractal, rule, nsteps-1)


import numpy as np

# 100 Years Goodwin Model (Numerical Solution)
from models.goodwin import plot_num_goodwin, eqm_goodwin_eval, phase_plot_goodwin
from models.goodwin_keen import find_eqm_keen, phase_plot_keen, plot_num_keen
from models.goodwin_symbolic import j_goodwin, eqm_goodwin, eig_goodwin, eig_goodwin_val
from models.simulation import sim_study, sim_study_plot

if __name__ == '__main__':
    J_g = j_goodwin()
    Eqm_g = eqm_goodwin()
    Eigvals_g = eig_goodwin()
    Eig_eval_g = eig_goodwin_val()

    print(f'Symbolic Jacobian Matrix for the Goodwin Mode:\n{J_g}\n')
    print(f'Symbolic Eqm. Points for the Goodwin Model:\n{Eqm_g}\n')
    print(f'Symbolic Eigenvalues for the Goodwin Model:\n{Eigvals_g}\n')
    print(f'Eigenvalues for the Goodwin Model Evaluated at Eqm.:\n{Eig_eval_g}\n')

    t = np.arange(0,120,0.01) # 100 Year Model
    ICs = [0.8,0.9]
    plot_num_goodwin(t, ICs, 'goodwin_model')

    # Long-Term Behaviour
    t = np.arange(0,1002,0.01) # 800 Year Model
    ICs = [0.8,0.9]
    plot_num_goodwin(t, ICs, 'goodwin_longterm')

    # Goodwin Phase Portrait
    eqm_values = eqm_goodwin_eval()
    # eqm = [ eqm_values[0], eqm_values[1] ] # Trivial Eqm. Point
    eqm = [ eqm_values[2], eqm_values[3] ] # Stable, Econmically Realistic Eqm. Point
    # eqm = [ eqm_values[4], eqm_values[5] ] # Stable, Econmically Unrealistic Eqm. Point
    t = np.arange(0,1000,0.01) # 100 Year Model
    ICs = [0.8,0.9]
    phase_plot_goodwin(t, ICs, eqm)

    t = np.arange(0, 302, 0.01)  # 100 Year Model
    ICs = [0.75, 0.75, 0.1]
    plot_num_keen(t, ICs, 'keen_model')

    # # Goodwin-Keen Phase Portrait
    t = np.arange(0, 302, 0.01)
    t_eqm = np.arange(0, 1000, 0.01)
    eqm = find_eqm_keen(ICs, t_eqm)

    phase_plot_keen(t, ICs, eqm)
    print(eqm)

    sims = sim_study()

    converge = 0
    diverge = 0
    total = len(sims)
    for i in range(total):
        if sims[i][2]:
            converge += 1
        else:
            diverge += 1

    print('Total Convergence Events: ', converge)
    print('Total Divergence Events: ', diverge)
    print('Total Events: ', total)

    sim_study_plot(sims)


import numpy as np

def quatMul(q0, q1):
    w0, x0, y0, z0 = q0
    w1, x1, y1, z1 = q1
    return np.array([-x1 * x0 - y1 * y0 - z1 * z0 + w1 * w0,
                      x1 * w0 + y1 * z0 - z1 * y0 + w1 * x0,
                     -x1 * z0 + y1 * w0 + z1 * x0 + w1 * y0,
                      x1 * y0 - y1 * x0 + z1 * w0 + w1 * z0], dtype=np.float64)

def quatConj(q):
    w, x, y, z = q
    return np.array([w, -x, -y, -z], dtype=np.float64)
def quatNorm2(q):
    w, x, y, z = q
    return (w**2 + x**2 + y**2 + z**2)
def quatNorm(q):
    return np.sqrt(quatNorm2(q))
def quatNormalize(q):
    w, x, y, z = q
    n = quatNorm(q)
    return np.array([w/n, x/n, y/n, z/n], dtype=np.float64)
def quatInv(q):
    return quatConj(q) / quatNorm2(q)

def quatFromAngleAxis(angle, axis):
    u,v,r = axis
    cos = np.cos(angle/2.)
    sin = np.sin(angle/2.)
    return quatNormalize(np.array([cos, sin*u, sin*v, sin*r], dtype=np.float64))
def pureQuat(vector):
    return np.concatenate([[0], vector])
def getPure(q):
    w, x, y, z = q
    return np.array([x, y, z], dtype=np.float64)

def quatTrans(q,v):
    """
    Rotation a vector using a quaternion.
    """
    return getPure(quatMul(q, quatMul(pureQuat(v), quatConj(q))))

def quat_dot_np(p, q):
    return np.sum(p*q)
def quat_dist_np(p, q):
    # See: https://math.stackexchange.com/a/90098
    # d(p,q) = 1 - <p,q>^2
    return 1 - (quat_dot_np(p, q))**2

if __name__=="__main__":
    ## Test quaternions
    q = quatFromAngleAxis(np.pi/2, [0,1,0])
    print(q)
    print("[0.70710678, 0., 0.70710678, 0.]")
    # quatMul(q, quatInv(q))
    # quatFromAngleAxis(np.pi/2, [1,0,0])
    print(getPure(quatMul(q,quatMul(pureQuat([1,2,3]), quatConj(q)))))
    print("array([-3.,  2.,  1.])")


# coding: utf-8

# In[29]:

data = loadtxt('thermocouples.dat',skiprows=1)
t = data[:,0]
T = data[:,1]
plot(t,T)
xlabel('$t$ [sec]')
ylabel(r'$T^\circ$C')
display()

# In[30]:

print 1 + 3.3*log10(len(t))
print 2*len(t)**(0.33)

# In[31]:

J = 11
n,bins,patches = hist(T,J)
x = bins[:-1]+0.5*diff(bins)[0]
plot(x,n,'r')
xlabel(r'$T^\circ$C')
ylabel('Counts')
title('11 bins')

# In[32]:

J = 19
n,bins,patches = hist(T,J)
x = bins[:-1]+0.5*diff(bins)[0]
plot(x,n,'r')
xlabel(r'$T^\circ$C')
ylabel('Counts')
title('19 bins')

# In[33]:

J = 15
n,bins,patches = hist(T,J)
x = bins[:-1]+0.5*diff(bins)[0]
plot(x,n,'r')
xlabel(r'$T^\circ$C')
ylabel('Counts')
title('15 bins')

# In[34]:

# vertical normalization
p = n/1000.
plot(x,p)
xlabel(r'$T^\circ$C')
ylabel('Relative counts')
title('Vertically normalized')

# In[35]:

# area normalization
f = p/diff(x)[0]
plot(x,f)
xlabel(r'$T^\circ$C')
ylabel('Probability Density')

# In[36]:

# horizontal normalization
z = (x - T.mean())/T.std()
plot(z,f)
xlabel(r'$x$ ')
ylabel(r'$P(x)$')
title('Final result')

# In[37]:

def gaussian(x,mu,sig):
    return 1/(sig*sqrt(2*pi))*exp(-((x-mu)**2)/(2*sig**2))

plot(z,f,z,gaussian(z,0,1),'r')
xlabel(r'$x$')
ylabel(r'$P(x)$')
title('Blue - original, red - Gaussian')

# In[38]:

import scipy.stats as st

print "T skewness = %f, kurtosis = %f" % (st.skew(T), st.kurtosis(T))
tmp = randn(1000,1)
print "Normal distribution skewness = %f, kurtosis = %f" % (st.skew(tmp), st.kurtosis(tmp))

# In[39]:

# area under the curve
print "Area under the curve = %f " % trapz(f,x)

# In[39]:


import numpy as np
import random
import cv2

random.seed(7)
np.random.seed(7)

def get_initial_centroids(X, k):
    """
    Function picks k random data points from dataset X, recurring points are removed and replaced but new points
    so a result we have array of k unique points. Founded points can be used as intial centroids for k means algorithm
    Args:
        X (numpy.ndarray) : dataset points array, size N:D
        k (int): number of centroids

    Returns:
        (numpy.ndarray): array of k unique initial centroids, size K:D

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

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

    number_of_unique_centroids = len(unique_centroids)

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

        number_of_unique_centroids = len(unique_centroids)

    return np.array(unique_centroids)

def get_euclidean_distance(A_matrix, B_matrix):
    """
    Function computes euclidean distance between matrix A and B.
    E. g. C[2,15] is distance between point 2 from A (A[2]) matrix and point 15 from matrix B (B[15])
    Args:
        A_matrix (numpy.ndarray): Matrix size N1:D
        B_matrix (numpy.ndarray): Matrix size N2:D

    Returns:
        numpy.ndarray: Matrix size N1:N2
    """

    A_square = np.reshape(np.sum(A_matrix * A_matrix, axis=1), (A_matrix.shape[0], 1))
    B_square = np.reshape(np.sum(B_matrix * B_matrix, axis=1), (1, B_matrix.shape[0]))
    AB = A_matrix @ B_matrix.T

    C = -2 * AB + B_square + A_square

    return np.sqrt(C)

def get_clusters(X, centroids, distance_mesuring_method):
    """
    Function finds k centroids and assigns each of the N points of array X to one centroid
    Args:
        X (numpy.ndarray): array of sample points, size N:D
        centroids (numpy.ndarray): array of centroids, size K:D
        distance_mesuring_method (function): function taking 2 Matrices A (N1:D) and B (N2:D) and returning distance
        between all points from matrix A and all points from matrix B, size N1:N2

    Returns:
        dict {cluster_number: list_of_points_in_cluster}
    """

    k = centroids.shape[0]

    clusters = {}

    distance_matrix = distance_mesuring_method(X, centroids)

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

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

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

    return clusters

def has_centroids_covered(previous_centroids, new_centroids, distance_mesuring_method, movement_threshold_delta):
    """
    Function checks if any of centroids moved more then MOVEMENT_THRESHOLD_DELTA if not we assume the centroids were founded
    Args:
        previous_centroids (numpy.ndarray): array of k old centroids, size K:D
        new_centroids (numpy.ndarray): array of k new centroids, size K:D
        distance_mesuring_method (function): function taking 2 Matrices A (N1:D) and B (N2:D) and returning distance
        movement_threshold_delta (float): threshold value, if centroids move less we assume that algorithm covered

    Returns: boolean True if centroids coverd False if not

    """
    distances_between_old_and_new_centroids = distance_mesuring_method(previous_centroids, new_centroids)
    centroids_covered = np.max(distances_between_old_and_new_centroids.diagonal()) <= movement_threshold_delta

    return centroids_covered

def perform_k_means_algorithm(X, k, distance_mesuring_method, movement_threshold_delta=0):
    """
    Function performs k-means algorithm on a given dataset, finds and returns k centroids
    Args:
        X (numpy.ndarray) : dataset points array, size N:D
        distance_mesuring_method (function): function taking 2 Matrices A (N1:D) and B (N2:D) and returning distance
        between all points from matrix A and all points from matrix B, size N1:N2.
        k (int): number of centroids
        movement_threshold_delta (float): threshold value, if centroids move less we assume that algorithm covered

    Returns:
        (numpy.ndarray): array of k centroids, size K:D
    """

    new_centroids = get_initial_centroids(X=X, k=k)

    centroids_covered = False

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

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

        centroids_covered = has_centroids_covered(previous_centroids, new_centroids, distance_mesuring_method, movement_threshold_delta)

    return new_centroids

def get_reduced_colors_image(image, number_of_colors):
    """
    Function returns given image with reduced number of colors
    Args:
        image (numpy.ndarray): original opencv image, function finds its reduced colors form
        number_of_colors (integer): number of colors in reduced image

    Returns:
        (numpy.ndarray): image with reduced number of colors
    """

    h, w, d = image.shape

    X = np.reshape(image, (h * w, d))
    X = np.array(X, dtype=np.int32)

    centroids = perform_k_means_algorithm(X, k=number_of_colors, distance_mesuring_method=get_euclidean_distance, movement_threshold_delta=4)
    distance_matrix = get_euclidean_distance(X, centroids)
    closest_cluster_ids = np.argmin(distance_matrix, axis=1)

    X_reconstructed = centroids[closest_cluster_ids]
    X_reconstructed = np.array(X_reconstructed, dtype=np.uint8)
    reduced_image = np.reshape(X_reconstructed, (h, w, d))

    return reduced_image

if __name__ == '__main__':
    k_values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 32, 64, 128, 256]

    reconstrutions = []

    img = cv2.imread("image.jpg")

    for k in k_values:
        reduced_colors_image = get_reduced_colors_image(img, k)

        cv2.imwrite(f"images/k{k}.jpg", reduced_colors_image)


import numpy as np
import pandas as pd
import sys

np.random.seed(0)

#Obtain the OHE of input vector
def one_hot_encoder(Y):
	new_Y = np.zeros((Y.shape[0], np.max(Y)+1))
	new_Y[np.arange(Y.shape[0]), Y] = 1
	return new_Y

#Softmax function
def softmax(Z):
	N, M = Z.shape
	val = np.exp(Z - (np.max(Z, axis = 1).reshape(N, 1)))
	return val/(np.sum(val, axis = 1)).reshape(N, 1)

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

# Derivative of softmax already handled in backwardpropagation()
def derivative_sigmoid(A):
	return (A*(1-A))

#Initialize weights and bias for the network
def init_network(M_data, num_labels, num_hidden_layers, hidden_layer_sizes):
	W = dict()	#W[l] means the weights between layer l and layer l-1. Dimension = (#units in layer l-1 x #units in layer l)
	b = dict() #b[l] means the bias value added for each unit of layer l

	#Will use 1-based indexing for weights, since hidden-layer are also 1-based indexed
	L = num_hidden_layers

	#Will use Xavier initialization of weights
	W[1] = np.random.randn(M_data, hidden_layer_sizes[0])*np.sqrt(1.0/M_data)
	b[1] = np.zeros((1, hidden_layer_sizes[0]))

	for i in range(1, L):
		W[i+1] = np.random.randn(hidden_layer_sizes[i-1], hidden_layer_sizes[i])*np.sqrt(1.0/hidden_layer_sizes[i-1])
		b[i+1] = np.zeros((1, hidden_layer_sizes[i]))

	W[L+1] = np.random.randn(hidden_layer_sizes[L-1], num_labels)*np.sqrt(1.0/hidden_layer_sizes[L-1])	#since output layer is necessarily softmax
	b[L+1] = np.zeros((1, num_labels))

	return W, b

# Forward propagation function
def forwardpropagation(W, b, X_data, num_hidden_layers):
	A = dict()	#Output of all layers. Output of input layer = a[0]

	L = num_hidden_layers
	A[0] = X_data

	for i in range(L):
		Z = np.dot(A[i], W[i+1]) + b[i+1]
		A[i+1] = sigmoid(Z)

	Z = np.dot(A[L], W[L+1]) + b[L+1]
	A[L+1] = softmax(Z)

	return A

#Backward propagation function
def backwardpropagation(A, W, num_hidden_layers, num_examples, Y_data):
	dW = dict()	#dW[l] means dL/dW[l] corresponding to weights for that layer
	db = dict()	#db[l] means dL/db[l] corresponding to biases for that layer

	L = num_hidden_layers

	dZ = (A[L+1] - Y_data)/(num_examples)	#Handled softmax's derivative
	dW[L+1] = np.dot(A[L].T, dZ)
	db[L+1] = np.sum(dZ, axis = 0, keepdims = True)

	for i in range(L, 0, -1):
		dZ = np.dot(dZ, W[i+1].T)*derivative_sigmoid(A[i])
		dW[i] = np.dot(A[i-1].T, dZ)
		db[i] = np.sum(dZ, axis = 0, keepdims = True)

	return dW, db

#Updating the weights using the gradients
def gradientdescent(W, b, dW, db, learning_rate, num_hidden_layers):
	for i in range(num_hidden_layers+1):
		W[i+1] = W[i+1] - learning_rate*dW[i+1]
		b[i+1] = b[i+1] - learning_rate*db[i+1]

	return W, b

#Loss function used: cross entropy
def loss_function(Y_pred, Y, num_examples):
	logY_pred = np.log(Y_pred)
	loss = -(np.sum(Y*logY_pred))/num_examples
	return loss

def main():
	#Command line arguments
	train_path = sys.argv[1]
	test_path = sys.argv[2]
	out_path = sys.argv[3]
	batch_size = 100 #Final batch size
	initial_lr = 1.4	#Final initial learning rate
	hidden_layer_sizes = [500, 300]	#Final hidden layers
	num_hidden_layers = len(hidden_layer_sizes)

	#Get train data
	train_df = pd.read_csv(train_path, header = None)

	X_train = train_df.iloc[:, 1:].values
	Y_train = train_df.iloc[:, 0].values
	Y_train = one_hot_encoder(Y_train)
	num_labels = Y_train.shape[1]
	N_train, M_train = X_train.shape

	#Normalize train data
	X_train = (X_train)/np.max(X_train)	#Divide by max pixel value

	#Get test data
	test_df = pd.read_csv(test_path, header = None)
	N_test = len(test_df)
	X_test = test_df.iloc[:, 1:].values
	M_test = X_test.shape[1]

	#Normalize test data
	X_test = (X_test)/np.max(X_train)	#Divide by max pixel value

	#Initialize weights for the neural network. Note: M_train == M_test == M_data
	W, b = init_network(M_train, num_labels, num_hidden_layers, hidden_layer_sizes)

	num_epochs = 100	#Training for 100 epochs

	batch_idx = 1	#will help us track the current batch
	num_batches = 0	#number of minibatches

	#Now, construct our minibatches
	X_train_batch = dict()
	Y_train_batch = dict()

	while batch_idx != N_train:

		if(batch_idx+batch_size < N_train):
			X_train_batch[num_batches] = X_train[batch_idx:batch_idx+batch_size]
			Y_train_batch[num_batches] = Y_train[batch_idx:batch_idx+batch_size]
			batch_idx = batch_idx+batch_size
		else:
			X_train_batch[num_batches] = X_train[batch_idx:N_train]
			Y_train_batch[num_batches] = Y_train[batch_idx:N_train]
			batch_idx = N_train

		num_batches += 1

	#Below values are meant for adaptive learning rate
	cur_avg_loss = 0
	prev_avg_loss = 1000000
	adaptive_factor = 1

	cur_lr = initial_lr

	for i in range(num_epochs):	#For now, number of iterations = 1000
		for j in range(num_batches):
			A = forwardpropagation(W, b, X_train_batch[j], num_hidden_layers)
			dW, db = backwardpropagation(A, W, num_hidden_layers, X_train_batch[j].shape[0], Y_train_batch[j])

			W, b = gradientdescent(W, b, dW, db, cur_lr, num_hidden_layers)
			cur_loss = loss_function(A[num_hidden_layers+1], Y_train_batch[j], X_train_batch[j].shape[0])

			cur_avg_loss += cur_loss

		cur_avg_loss = cur_avg_loss/num_batches
		if(cur_avg_loss > prev_avg_loss):
			adaptive_factor += 1
			cur_lr = initial_lr/np.sqrt(adaptive_factor)
		print("Epoch " + str(i+1) + " done! Loss = "+str(cur_avg_loss))
		prev_avg_loss = cur_avg_loss
		cur_avg_loss = 0

	#Time to make predictions
	A = forwardpropagation(W, b, X_test, num_hidden_layers)

	Y_pred = np.argmax(A[num_hidden_layers+1], axis = 1)

	out_file = open(out_path, 'w')
	for i in range(len(Y_pred)):
		out_file.write(str(Y_pred[i])+"\n")
	out_file.close()

if __name__ == '__main__':
	main()


'''
orbital_analysis.py

Functions to help calculate/estimate orbital parameters
'''
from platon.constants import M_sun, R_sun, R_jup
import numpy as np

def find_orbital_radius(T_planet, T_star, albedo):
    '''
    Calculates the orbital radius of the planet in units of stellar radii,
    assuming a circular orbit

    Parameters
    ----------
    T_planet : float
        The isothermal temperature of the planet's atmosphere. Note that
        we are assuming this is the same as the equilibrium temperatue.
    T_star : float
        The blackbody temeprature of the star.
    albedo : float
        The Bond albedo of the planet

    Returns
    -------
    orbital_radius : float
        The orbital radius of the planet in units of stellar radii,
        assuming a circular orbit.
    '''
    return (T_star ** 2 * np.sqrt(1 - albedo))/(2 * T_planet ** 2)

def find_orbital_period(orbital_radius, M_star, R_star, unit='seconds'):
    '''
    Calculates the orbital period of a circular orbit using Kepler's 3rd law

    Parameters
    ----------
    orbital_radius : float
        The orbital radius in units of stellar radii
    M_star : float
        The mass of the star in solar masses
    R_star : float
        The radius of the star in solar radii
    unit : str, optional
        The unit to return the period in. Accepted are
        - 'seconds'/'s'
        - 'hours'/'h'
        - 'days'/'d'
        - 'years'/'y'
    Default is seconds
    '''
    a = orbital_radius * R_star * R_sun

    G = 6.674e-11

    P = np.sqrt((4 * np.pi**2 * a**3)/(G * M_star * M_sun))

    unit = unit.lower()
    if unit in ['seconds', 's']:
        return P
    if unit in ['hours', 'h']:
        return P / 3600
    if unit in ['days', 'd']:
        return P / (3600 * 24)
    if unit in ['years', 'y']:
        return P / 31536000

    else:
        raise ValueError('Unrecognised unit {}'.format(unit))

def find_transit_duration(period, Rp, Rs, inc, orbital_radius):
    '''
    Calculates the transit duration

    Parameters
    ----------
    period : float
        The period of the planet orbit
    Rp : float
        The planet radius in jupiter radii
    Rs : float
        The star radus in solar radii
    orbital_radius :
        The orbital radius of the planet in units of host radii,
        assuming a circular orbit.

    Returns
    -------
    transit_duration : float
        The transit duration in the same unit as the period
    '''
    # Put things in SI units
    Rs = Rs * R_sun
    Rp = Rp * R_jup
    a = orbital_radius * Rs

    # Impact parameter
    b = a * np.cos(inc * (np.pi/180)) / Rs

    return (period/np.pi) * np.arcsin(np.sqrt((Rp + Rs)**2 - (b*Rs)**2)/a)


# here we have word problem to be solved

# the admission fee at a small fare is a $1.5  for children in $4 for
# adults on a certain day.2200 people enter the fair and five thousand fifty dollars is collected.
# How many children and how many adults attended.

# lets solve the problem using matrices

import numpy as np

# from the given data lets say we gave x1 as the num children
# x2 as the number of adults

fair = np.array([[1,1],[1.5, 4]])
# the prices are in the fair variable

moneyCollected = np.array([[2200], [5050]])
# the money collected is in the moneyCollected array
# so lets find the number of children and adults
X = np.linalg.solve(fair,moneyCollected)

print("the number of children that were present were", X[0,0])
print("the number of adults that were present were", X[1,0])


import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize

# lets just generate some data from known beta parameters
x = np.linspace(0,10,30)
beta0 = 2.7
beta1 = 1.3
y = (beta0*x)/(beta1+x)

# determine beta0 and beta1 by minimizing the mean abs residual error

def my_func(X):
    yHat = (X[0]*x)/(X[1]+x)
    resid = yHat - y
    return np.mean(np.abs(resid))

res = optimize.fmin_bfgs(my_func, [3.0, 3.0], full_output=True)

plt.figure()
plt.plot(x,y,'o')
beta = res[0]
plt.plot(x,(beta[0]*x)/(beta[1]+x))
plt.show()


import numpy as np
from scipy.spatial.distance import cdist

class KMeans:
    def __init__(
            self,
            k: int,
            metric: str = "euclidean",
            tol: float = 1e-6,
            max_iter: int = 100):
        """
        inputs:
            k: int
                the number of centroids to use in cluster fitting
            metric: str
                the name of the distance metric to use
            tol: float
                the minimum error tolerance from previous error during optimization to quit the model fit
            max_iter: int
                the maximum number of iterations before quitting model fit
        """
        # making sure the inputted k is greater than
        assert k > 0, "K must be greater than zero!"

        # making sure the num of iterations is +
        assert max_iter >0, "Num Iterations must be grater than zero!"

        # assert tol is slightly greater than zero
        assert tol > 0, "Tol must be sliiiightly greater than zero!"

        self.k = k
        self.metric = metric
        self.tol = tol
        self.max_iter = max_iter

    def fit(self, mat: np.ndarray):
        """
        fits the kmeans algorithm onto a provided 2D matrix

        inputs:
            mat: np.ndarray
                A 2D matrix where the rows are observations and columns are features
        """

        # get total num of examples features from mat
        self.num_samples, self.num_feats = mat.shape

        # initialize random centroids from mat
        self.centroids = self._random_centroids(mat)

        # starting with huge error to then minimize
        self._err = np.inf

        # keeping track of the number of iterations
        for _ in range(self.max_iter):

            # assign rest of points to centers in self.centroids
            clusters = self._cluster_points(mat)

            # old centroids vs new assigned based on mean
            prev_centroids = self.centroids

            # update centroids
            for i in range(self.k):
                self.centroids[i] = np.mean(mat[clusters == i], axis=0)

            # calculate mse, via cdist, np.square and np.mean - yay vector ops
            mse = np.average(np.square(np.min(cdist(mat,
                                                self.centroids,
                                            metric = self.metric), axis=1)))

            # delta errors, for checking diff.
            delta_err = np.abs(self._err - mse)

            if delta_err < self.tol:
                print("Clusters found -- Convergence Achieved)")
                self._err = mse
                break

    def predict(self, mat: np.ndarray) -> np.ndarray:
        """
        predicts the cluster labels for a provided 2D matrix

        inputs:
            mat: np.ndarray
                A 2D matrix where the rows are observations and columns are features

        outputs:
            np.ndarray
                a 1D array with the cluster label for each of the observations in `mat`
        """

        # get labels from the best saved centroids
        return self._cluster_points(mat)

    def get_error(self) -> float:
        """
        returns the final squared-mean error of the fit model

        outputs:
            float
                the squared-mean error of the fit model
        """

        return self._err

    def get_centroids(self) -> np.ndarray: # new centroids?
        """
        returns the centroid locations of the fit model

        outputs:
            np.ndarray
                a `k x m` 2D matrix representing the cluster centroids of the fit model
        """

        return self.centroids

    def _random_centroids(self, mat: np.ndarray): # rename and change
        '''
        Private method to randomly generate centroids given k
        number of clusters, from the available points stored
        and available in the matrix 'mat'.
        '''

        # need a total of k (clusters) by total features centroids
        all_centroids = np.zeros((self.k, self.num_feats))

        # random selection of (k) P[oints]
        for p in range(self.k):
            # pick random point from all available
            aCentroid = mat[np.random.choice(range(self.num_samples))]
            # save it
            all_centroids[p] = aCentroid

        # return the randomly generated centroids
        return all_centroids

    def _cluster_points(self, mat: np.array):
        '''
        Private method to create clusters based on the proximity of
        each point.
        Note that proximity is calculated based on the metric
        inputed -- in this case we take advantage of cdist, as
        imported above the start of this script.

        '''
        # get all distances first with the given self.metric
        dist_points = cdist(mat, self.centroids, metric = self.metric)

        # get points that are closest to the centroids (here labels are indices)
        k_clusters_min = np.argmin(dist_points, axis=1)

        return k_clusters_min


from lazydiff.vars import Var
from lazydiff import ops
import numpy as np
import time

def MSE(X, y, m, b):
    """
    Returns Mean Squared Error where
    the predicted target is mX+b
    and y is the observed target variable
    """
    loss = Var(0)
    for vec, y_i in zip(X,y):
        loss = loss + (ops.sum(m*vec)+b-y_i)**2
    return loss/len(X)

def MSE_regularized(X, y, m, b, p = 1, C = 1):
    """
    Returns a regularized Mean Squared Error with L-p norm
    where the predicted target is mX+b,
    y is the observed target variable
    and C is the weight in L-p norm of the vector m
    """
    loss = Var(0)
    for vec,y_i in zip(X,y):
        loss = loss + (ops.sum(m*vec)+b-y_i)**2
    return loss/(2*len(X)) + C*ops.norm(m, p=p)**p

def lasso_loss(X, y, m, b, C = 1):
    """
    Returns Ridge Regression objective function
    Reference: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Lasso.html
    the predicted target is mX+b,
    y is the observed target variable
    and C is the weight in L-1 norm of the vector m
    """
    return MSE_regularized(X, y, m, b, p = 1, C = C)

def ridge_loss(X, y, m, b, C = 1):
    """
    Returns Ridge Regression objective function
    Reference: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html
    the predicted target is mX+b,
    y is the observed target variable
    and C is the weight in L-2 norm of the vector m
    """
    loss = Var(0)
    for vec, y_i in zip(X,y):
        loss = loss + (ops.sum(m*vec)+b-y_i)**2
    return loss + C*ops.norm(m,2)**2

def elastic_loss(X, y, m, b, C = 1, l1_ratio = 0.5):
    """
    Returns Elastic Net Regression objective function
    Reference: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.ElasticNet.html
    the predicted target is mX+b,
    y is the observed target variable,
    C is the weight in L-2 norm of the vector m
    and L1_ratio is the ratio of L-1 norm loss
    """
    loss = Var(0)
    for vec,y_i in zip(X,y):
        loss = loss + (ops.sum(m*vec)+b-y_i)**2
    return loss/(2*len(X)) + C*l1_ratio*ops.norm(m, p=1) + 0.5*C*(1-l1_ratio)*ops.norm(m,2)**2

def gradient_descent(X, y, loss_function, m, b, lr = 0.1, forward = True):
    """ Performs one single update step of gradient descent
        Returns the updated parameters m, b and loss
        X is the matrix of independent variables
        y is the vector of dependent variable
        m is the coefficient of the prediction mX+b
        b is the intercept/bias of the prediction
        lr is the learning rate
        forward determines whether to perform forward mode
        or reverse mode to find the gradient
    """
    loss = loss_function(X, y, m, b)
    if (forward):
        # forward mode
        m.forward()
        b.forward()
    else:
        # reverse mode
        loss.backward()
    # clear cache by reinstantiating
    m = Var(m.val-lr*loss.grad(m))
    b = Var(b.val-lr*loss.grad(b))
    return m, b, loss

def iterative_regression(X, y, m, b, loss_function, lr = 0.1,\
        epochs = 100, earlyStop = 0, forward = True, history = None):
    """
    Performs iterative regression with the given loss function
    minimizing the loss function w.r.t. the parameters

    Returns the updated parameters m, b and the minimized loss
    X, y are the dependent and independent variables
    m, b are the coefficients and intercept
    loss_function is the objective function
    lr is the learning rate of the gradient descent
    epochs is the number of updates over the entire training data set
    earlyStop is absolute tolerance to stop the iteration early
    Note that 0 means no early stopping
    forward determines whether to perform forward mode
    or reverse mode to find the gradient
    history to store old values of m, b, loss
    if provided a dictionary
    """
    canStore = isinstance(history, dict)

    if (canStore):
        history['m'] = []
        history['b'] = []
        history['loss'] = []

    loss = Var(0)
    for ep in range(epochs):
        prev = loss
        m, b, loss = gradient_descent(X, y, loss_function, m, b, lr, forward)
        if (canStore):
            # store the m, b
            # change over each epoch
            history['m'].append(m.val)
            history['b'].append(b.val)
            history['loss'].append(loss.val)

        # check if absolute tolerance meets early stopping condition
        if (abs(loss.val - prev.val) < earlyStop):
            break
    # return coefficient and intercept
    return m, b, loss


"""
DEMSLV01 Compute root of f(x)=exp(-x)-1

Compute root of f(x)=exp(-x)-1 using Newton and secant methods. Initial value generated randomly. True root is x=0.
"""
from demos.setup import np, plt, tic, toc
from numpy.linalg import norm
from compecon import NLP

''' Set up the problem '''
def f(x):
    fval = np.exp(-x) - 1
    fjac = -np.exp(-x)
    return fval, fjac

problem = NLP(f, all_x=True)

''' Randomly generate starting point '''
problem.x0 = 10 * np.random.randn(1)

''' Compute root using Newton method '''
t0 = tic()
x1 = problem.newton()
t1 = 100 * toc(t0)
n1, x_newton = problem.fnorm, problem.x_sequence

''' Compute root using Broyden method '''
t0 = tic()
x2 = problem.broyden()
t2 = 100 * toc(t0)
n2, x_broyden = problem.fnorm, problem.x_sequence

''' Print results '''
print('Hundredths of seconds required to compute root of exp(-x)-1,')
print('via Newton and Broyden methods, starting at x = %4.2f.' % problem.x0)
print('\nMethod      Time   Norm of f   Final x')
print('Newton  %8.2f    %8.0e     %5.2f' % (t1, n1, x1))
print('Broyden %8.2f    %8.0e     %5.2f' % (t2, n2, x2))

''' View current options for solver '''
print(problem.opts)

''' Describe the options '''
print(problem.opts.__doc__)

''' Plot the convergence '''
b = -abs(problem.x0)
a = -b
xx = np.linspace(a, b, 100)

fig = plt.figure()
plt.plot(xx, f(xx)[0], 'b-')
plt.plot(x_newton,f(x_newton)[0],'ro:')
plt.show()


from IPython.core.pylabtools import figsize
import numpy as np
from matplotlib import pyplot as plt
figsize(11, 9)

import scipy.stats as stats

dist = stats.beta
n_trials = [0, 1, 2, 3, 4, 5, 8, 15, 50, 500]
data = stats.bernoulli.rvs(0.5, size=n_trials[-1])
x = np.linspace(0, 1, 100)

# For the already prepared, I'm using Binomial's conj. prior.
for k, N in enumerate(n_trials):
    sx = plt.subplot(len(n_trials)/2, 2, k+1)
    plt.xlabel("$p$, probability of heads") \
        if k in [0, len(n_trials)-1] else None
    plt.setp(sx.get_yticklabels(), visible=False)
    heads = data[:N].sum()
    y = dist.pdf(x, 1 + heads, 1 + N - heads)
    plt.plot(x, y, label="observe %d tosses,\n %d heads" % (N, heads))
    plt.fill_between(x, 0, y, color="#348ABD", alpha=0.4)
    plt.vlines(0.5, 0, 4, color="k", linestyles="--", lw=1)

    leg = plt.legend()
    leg.get_frame().set_alpha(0.4)
    plt.autoscale(tight=True)

plt.suptitle("Bayesian updating of posterior probabilities",
             y=1.02,
             fontsize=14)

plt.tight_layout()

figsize(12.5, 4)

import scipy.stats as stats
a = np.arange(16)
poi = stats.poisson
lambda_ = [1.5, 4.25]
colours = ["#348ABD", "#A60628"]

plt.bar(a, poi.pmf(a, lambda_[0]), color=colours[0],
        label="$\lambda = %.1f$" % lambda_[0], alpha=0.60,
        edgecolor=colours[0], lw="3")

plt.bar(a, poi.pmf(a, lambda_[1]), color=colours[1],
        label="$\lambda = %.1f$" % lambda_[1], alpha=0.60,
        edgecolor=colours[1], lw="3")

plt.xticks(a + 0.4, a)
plt.legend()
plt.ylabel("probability of $k$")
plt.xlabel("$k$")
plt.title("Probability mass function of a Poisson random variable; differing $\lambda$ values");


"""
Helper module to provide activation to network layers.
Four types of activations with their derivates are available:

- Sigmoid
- Softmax
- Tanh
- ReLU
"""
import numpy as np

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

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

def softmax(z):
    return np.exp(z) / np.sum(np.exp(z))

def tanh(z):
    return np.tanh(z)

def tanh_prime(z):
    return 1 - tanh(z) * tanh(z)

def relu(z):
    return np.maximum(z, 0)

def relu_prime(z):
    return z > 0


"""This module implements waveforms to be attached to sources."""
from abc import ABC, abstractmethod
from typing import Union

import matplotlib.pyplot as plt
import numpy as np

class Waveform(ABC):
    """Base Waveform."""

    @abstractmethod
    def func(self):
        """Waveform function."""

        def wrap(time):
            """Wrap function."""
            return 0 * time

        return wrap

    def __call__(self, time: Union[float,
                                   np.ndarray]) -> Union[float, np.ndarray]:
        """Call a waveform function."""
        return self.func()(time)

    def plot(self, time: np.ndarray):
        """Plot waveform for a given time array."""
        y = self.func()(time)
        fig = plt.figure()
        ax = fig.add_subplot(111)
        ax.plot(time, y)
        plt.show()

class GaussianWaveform(Waveform):
    """
    Model of a gaussian waveform.

    Represent a function ``A*exp(-(t-t0)^2/tau^2)``.
    This model should be attached to a source.

    Parameters
    ----------
    t_0 : float
        Initial time.
    tau : float
        Tau parameter.
    amplitude : float
        Amplitude.
    """

    def __init__(self, t_0: float, tau: float, amplitude: float = 1):
        """Initialize waveform."""
        super().__init__()
        self.tau = tau
        self.t_0 = t_0
        self.amp = amplitude

    def func(self):
        """Return a gaussian waveform function."""

        def wrap(time: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
            return self.amp * np.exp(-(((time - self.t_0) / self.tau)**2))

        return wrap

class SineWaveform(Waveform):
    """
    Model of a sine waveform.

    Represent a function ``A*sin(2*pi*freq*t-offset)``
    This model should be attached to a source.

    Parameters
    ----------
    frequency : float
        Frequency [Hz].
    offset : float
        Phase offset.
    amplitude : float
        Amplitude.
    """

    def __init__(self,
                 frequency: float,
                 offset: float = 0,
                 amplitude: float = 1):
        """Initialize waveform."""
        super().__init__()
        self.freq = frequency
        self.offset = offset
        self.amp = amplitude

    def func(self):
        """Return a gaussian waveform function."""

        def wrap(time: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
            """Wrap function."""
            return self.amp * \
                    np.sin(2 * np.pi * self.freq * time - self.offset)

        return wrap

class StepWaveform(Waveform):
    """
    Model of a step waveform.

    Represent a function ``A*step(t-t0)``.
    This model should be attached to a source.

    Parameters
    ----------
    t_0 : float
        Initial time.
    amplitude : float
        Amplitude.
    """

    def __init__(self, t_0: float = 0, amplitude: float = 1):
        """Initialize waveform."""
        super().__init__()
        self.t_0 = t_0
        self.amp = amplitude

    def func(self):
        """Return a step waveform function."""

        def wrap(time: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
            return self.amp * np.heaviside(time - self.t_0, 1)

        return wrap


import numpy as np
import random
from sklearn.datasets.samples_generator import make_blobs
import matplotlib.pyplot as plt

def euclidean_dist(a, b):
    dist = np.linalg.norm(np.array(a) - np.array(b))
    return dist

def gaussian_kernel(euclidean_dist, bandwidth):
    kernel = (1 / (bandwidth * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((euclidean_dist / bandwidth)) ** 2)
    return kernel

class MeanShift(object):
    def __init__(self, kernel=gaussian_kernel):
        self.kernel = kernel

    def shift_point(self, point, points, kernel_bandwidth):
        shift_x = float(0)
        shift_y = float(0)
        scale_factor = float(0)
        for p_temp in points:
            # numerator
            dist = euclidean_dist(point, p_temp)
            weight = self.kernel(dist, kernel_bandwidth)
            shift_x += p_temp[0] * weight
            shift_y += p_temp[1] * weight
            # denominator
            scale_factor += weight
        shift_x = shift_x / scale_factor
        shift_y = shift_y / scale_factor
        return [shift_x, shift_y]

    def cluster_points(self, points):
        cluster_ids = []
        cluster_idx = 0
        cluster_centers = []
        for i, point in enumerate(points):
            if(len(cluster_ids) == 0):
                cluster_ids.append(cluster_idx)
                cluster_centers.append(point)
                cluster_idx += 1
            else:
                for center in cluster_centers:
                    dist = euclidean_dist(point, center)
                    if(dist < Cluster_Threshold):
                        cluster_ids.append(cluster_centers.index(center))
                if(len(cluster_ids) < i + 1):
                    cluster_ids.append(cluster_idx)
                    cluster_centers.append(point)
                    cluster_idx += 1
        return cluster_ids

    def fit(self, points, kernel_bandwidth):
        shift_points = np.array(points)
        shifting = [True] * points.shape[0]
        while True:
            max_dist = 0
            for i in range(0, len(shift_points)):
                if not shifting[i]:
                    continue
                p_shift_init = shift_points[i].copy()
                shift_points[i] = self.shift_point(shift_points[i], points, kernel_bandwidth)
                dist = euclidean_dist(shift_points[i], p_shift_init)
                max_dist = max(max_dist, dist)
                shifting[i] = dist > Stop_Threshold
            if(max_dist < Stop_Threshold):
                break
        cluster_ids = self.cluster_points(shift_points.tolist())
        return shift_points, cluster_ids

def colors(n):
  ret = []
  for i in range(n):
    ret.append((random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1)))
  return ret

def main():
    centers = [[-1, -1], [-1, 1], [1, -1], [1, 1]]
    X, _ = make_blobs(n_samples=200, centers=centers, cluster_std=0.3)
    mean_shifter = MeanShift()
    _, mean_shift_result = mean_shifter.fit(X, kernel_bandwidth=0.3)
    print('input: {}'.format(X))
    print('assined clusters: {}'.format(mean_shift_result))
    color = colors(np.unique(mean_shift_result).size)
    for i in range(len(mean_shift_result)):
        plt.scatter(X[i, 0], X[i, 1], color = color[mean_shift_result[i]])
    for i, j in centers:
        plt.scatter(i, j, s=100, c='black', marker='*')
    plt.show()

Stop_Threshold = 1e-5
Cluster_Threshold = 1e-1

if __name__ == '__main__':
    main()


"""
Summary Statistics with Numpy
"""

import numpy as np

# Define a python list
a_list = [2, 4, -1, 5.5, 3.5, -2, 5, 4, 6.5, 7.5]

# Convert the list into numpy array
an_array = np.array(a_list)

# Compute and print various statistics
print('Mean:', an_array.mean())
print('Median:', np.median(an_array))
print('Range (Max - min):', np.ptp(an_array))
print('Standard deviation:', an_array.std())
print('80th percentile:', np.percentile(an_array, 80))
print('0.2-quantile:', np.quantile(an_array, 0.2))

"""
Visualization with Matplotlib and Seaborn
"""

import matplotlib.pyplot as plt

plt.plot(an_array)
plt.show()

plt.figure(figsize=(9, 5))
plt.title('A basic plot', fontsize=18)
plt.plot(an_array, color='blue', linestyle='--',
         linewidth=4, marker='o', markersize=20)
plt.xlabel('X-axis points', fontsize=14)
plt.ylabel('Y-axis points', fontsize=14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.grid(True)
plt.show()

"""
Statsmodels
"""

import numpy as np
import statsmodels.api as sm

# Input variables
nobs = 100
X = np.random.random((nobs, 2))
X = sm.add_constant(X)

# Regression coefficients
beta = [1, .1, .5]

# Random errors
e = np.random.random(nobs)

# Output y
y = np.dot(X, beta) + e

# Fit the regression model
reg_model = sm.OLS(y, X).fit()

# Print the summary
print(reg_model.summary())


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

# importing the dataset
dataset = pd.read_csv('Ads_CTR_Optimisation.csv')

# implementing UCB
N = 10000
d = 10
ads_selected = []
number_of_selections = [0] * d
sum_of_rewards = [0] * d
total_reward = 0

for n in range(0, N):
    ad = 0
    max_upper_bound = 0
    for i in range(0, d):
        if number_of_selections[i] > 0:
            average_reward = sum_of_rewards[i] / number_of_selections[i]
            delta_i = math.sqrt(3/2 * math.log(n + 1) / number_of_selections[i])
            upper_bound = average_reward + delta_i
        else:
            upper_bound = 1e400

        if upper_bound > max_upper_bound:
            max_upper_bound = upper_bound
            ad = i
    ads_selected.append(ad)
    number_of_selections[ad] += 1
    reward = dataset.values[n, ad]
    sum_of_rewards[ad] += reward
    total_reward += reward

print(f"Total Reward: {total_reward}")


import numpy as np
from rlberry.utils.jit_setup import numba_jit

@numba_jit
def kernel_func(z, kernel_type):
    """
    Returns a kernel function to the real value z.

    Kernel types:

    "uniform"      : 1.0*(abs(z) <= 1)
    "triangular"   : max(0, 1 - abs(z))
    "gaussian"     : exp(-z^2/2)
    "epanechnikov" : max(0, 1-z^2)
    "quartic"      : (1-z^2)^2 *(abs(z) <= 1)
    "triweight"    : (1-z^2)^3 *(abs(z) <= 1)
    "tricube"      : (1-abs(z)^3)^3 *(abs(z) <= 1)
    "cosine"       : cos( z * (pi/2) ) *(abs(z) <= 1)
    "exp-n"        : exp(-abs(z)^n/2), for n integer

    Parameters
    ----------
    z : double
    kernel_type : string
    """
    if kernel_type == "uniform":
        return 1.0 * (np.abs(z) <= 1)
    elif kernel_type == "triangular":
        return (1.0 - np.abs(z)) * (np.abs(z) <= 1)
    elif kernel_type == "gaussian":
        return np.exp(-np.power(z, 2.0) / 2.0)
    elif kernel_type == "epanechnikov":
        return (1.0 - np.power(z, 2.0)) * (np.abs(z) <= 1)
    elif kernel_type == "quartic":
        return np.power((1.0 - np.power(z, 2.0)), 2.0)*(np.abs(z) <= 1)
    elif kernel_type == "triweight":
        return np.power((1.0 - np.power(z, 2.0)), 3.0)*(np.abs(z) <= 1)
    elif kernel_type == "tricube":
        return np.power((1.0 - np.power(np.abs(z), 3.0)), 3.0)*(np.abs(z) <= 1)
    elif kernel_type == "cosine":
        return np.cos(z*np.pi/2)*(np.abs(z) <= 1)
    elif "exp-" in kernel_type:
        exponent = _str_to_int(kernel_type.split("-")[1])
        return np.exp(-np.power(np.abs(z), exponent) / 2.0)
    else:
        raise NotImplementedError("Unknown kernel type.")

@numba_jit
def _str_to_int(s):
    """
    Source: https://github.com/numba/numba/issues/5650#issuecomment-623511109
    """
    final_index, result = len(s) - 1, 0
    for i, v in enumerate(s):
        result += (ord(v) - 48) * (10 ** (final_index - i))
    return result


import sys
from numpy import matrix, sqrt

def f(x, y):
    return matrix([[y - x**2, y - 2 + x**4]])

def J(x):
    return matrix([[-2*x, 1], [4*x**3, 1]])

x = matrix([[2.0, 2.0]]).T   # Starting values
f_eval = f(float(x[[0],[0]]), float(x[[1],[0]]))
error_limit = 0.001
no_iterations = 0
it_limit = 100
while sqrt(f_eval[[0],[0]]**2 + f_eval[[0],[1]]**2) > error_limit and \
                                            no_iterations < it_limit:
    try:
        x = x - J(float(x[[0],[0]])).I*f(float(x[[0],[0]]),\
                                            float(x[[1],[0]])).T
        print x, '\n'
        f_eval = f(float(x[[0],[0]]), float(x[[1],[0]]))
        no_iterations += 1
    except:
        print "Error! - Jacobian not invertible for x = ", x
        sys.exit(1)   # Abort with error


import numpy as np
import ActiveFunction as f

# 入力
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])

print(W1.shape)
print(X.shape)
print(B1.shape)

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

Z1 = f.sigmoid(A1)
print("A1", A1)
print("Z1", Z1)
# 入力層(0層)から第1層の変換が完了

W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
print(Z1.shape)
print(W2.shape)
print(B2.shape)

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

def identity_function(x):
    return x

W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])

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

Y = identity_function(A3)
print(Y)

def init_network():
    network = {};
    #2x3
    network["W1"] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    network["b1"] = np.array([0.1, 0.2, 0.3])
    #3x2
    network["W2"] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
    network["b2"] = np.array([0.1, 0.2])
    #2x2
    network["W3"] = np.array([[0.1, 0.3], [0.2, 0.4]])
    network["b3"] = np.array([0.1, 0.2])

    return network

def forward(network, x):
    W1, W2, W3 = network["W1"], network["W2"], network["W3"]
    b1, b2, b3 = network["b1"], network["b2"], network["b3"]

    a1 = np.dot(x, W1) + b1
    z1 = f.sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = f.sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = identity_function(a3)

    return y

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


"""
Created by Alex Hopkins
on 10/19/18

Model for Electricity and Magnetism.

In this module there are functions that calculate the basic questions of physics 2.
"""
from models.em import em_plotting as emplt
import numpy as np

def coulomb_force(q1, q2, r):
    """
    Calculates the force between two point charges.

    Applying coulombs law of F = k * q_1 * q_2 / r ** 2 to find the force of attraction/repulsion between two charges
    based on their distance from each other.

    :param q1: Scalar: Charge given in Coulombs
    :param q2: Scalar: Charge given in Coulombs
    :param r: Scalar: Distance between two charges given in meters

    :return: Scalar: Force between the two charges, given as Newtons
    """

    k = 8.987e9     # N * m ** 2 / C ** 2

    force = k * (q1 * q2) / r ** 2
    return force

def electric_field(q, r, x, y):
    """
    Return the electric field vector E=(Ex,Ey) due to charge q at r.

    :param q: Scalar: Charge given in Coulombs
    :param r: Tuple: Vector x, y components, distance between two charges given in meters
    :param x: Mesh: X values of a grid
    :param y: Mesh: Y values of a grid

    :return: Vector: Force between the two charges, given as Newtons
    """

    return q * (x - r[0]) / np.hypot(x - r[0], y - r[1]) ** 3, q * (y - r[1]) / np.hypot(x - r[0], y - r[1]) ** 3

def n_electric_field(nq):

    return emplt.e_field_plotter(nq)


import numpy as np
import numpy.linalg as la
from io import StringIO

class PageRank:

    def __init__(self):

        self.L = np.array([
            [0, 1 / 2, 1 / 3, 0, 0, 0],
            [1 / 3, 0, 0, 0, 1 / 2, 0],
            [1 / 3, 1 / 2, 0, 1, 0, 1 / 2],
            [1 / 3, 0, 1 / 3, 0, 1 / 2, 1 / 2],
            [0, 0, 0, 0, 0, 0],
            [0, 0, 1 / 3, 0, 0, 0]
        ])
        self.L2 = np.array([
    [0,   1/2, 1/3, 0, 0,   0,     0 ],
    [1/3, 0,   0,   0, 1/2, 0,     0 ],
    [1/3, 1/2, 0,   1, 0,   1 / 3, 0 ],
    [1/3, 0,   1/3, 0, 1/2, 1 / 3, 0 ],
    [0,   0,   0,   0, 0,   0,     0 ],
    [0,   0,   1/3, 0, 0,   0,     0 ],
    [0,   0,   0,   0, 0,   1 / 3, 1 ]
])

    def web_matrix(self, L):
        """method return the probability matrix in a nice formated way"""
        s = StringIO()
        np.savetxt(s, L, fmt="%.3f")
        return s.getvalue()

    def web_eingen(self, L):
        """method returns eingen vetctor in a nice formated way"""
        e_vals, e_vecs = la.eig(L)
        vec = np.transpose(e_vecs)[0]
        vec = vec * (100 / sum(vec))
        s = StringIO()
        np.savetxt(s, vec.real, fmt="%.3f")
        return s.getvalue()

    def web_eingen_iteration(self, L):
        """Method returns the eingen vectors using iteration"""
        r = (100 * np.ones(len(L))) / len(L)
        r = L @ r
        s_0 = StringIO()
        np.savetxt(s_0, r, fmt="%.3f")
        s_11 = StringIO()
        r_11 = r
        for _ in range(10):
            r_11 = L @ r_11
        np.savetxt(s_11, r_11, fmt="%.3f")
        s_con = StringIO()
        r_prev = r_11
        r_next = L @ r_prev
        while True:
            r_prev = L @ r_next
            r_next = L @ r_prev
            if la.norm(r_prev - r_next) <= 0.01:
                break

        np.savetxt(s_con, r_next, fmt="%.3f")
        return (s_0.getvalue(), s_11.getvalue(),s_con.getvalue())

    def web_probability_matrix(self, L, d):
        """Method takes a matrix as an imput and
        returns it multiplied by a dumping factor and normalized """
        J = np.ones(len(L))
        M = d * L + ((1 - d) / len(L)) * J
        return M

    def print_I_II(self):
        """Method for printing the output in stage I and stage II"""
        matrix = self.web_matrix(self.L)
        print(matrix)
        #comment this out if you are using it in stage II
        eingen = self.web_eingen(self.L)
        print(eingen)
        #comment this out if you are using it in stage I
        eingen_iter = self.web_eingen_iteration(self.L)
        print(*eingen_iter, sep="\n")

    def print_III(self):
        """method for printing the output in stage III"""
        matrix = self.web_matrix(self.L2)
        print(matrix)
        M = self.web_probability_matrix(self.L2, 0.5)
        *other, r_next = self.web_eingen_iteration(self.L2)
        print(r_next)

        *other, r_next = self.web_eingen_iteration(M)

        print(r_next)

    def print_IV(self):
        """method for printing the output in stage IV"""
        n, d = input("Enter the number of rows in your matrix and the dumping factor separated by space:\n").split()
        print("Enter yout matrix: ")
        L = np.array([[float(x) for x in input().split()] for _ in range(int(n))])
        M = self.web_probability_matrix(L, float(d))

        *other, r_next = self.web_eingen_iteration(M)

        print(r_next)

    def print_V(self):
        """method for printing the output in stage V"""
        n = int(input("Enter the number of websites in your micro-internet:\n"))
        site_list = [x for x in input().split()]
        print("""Enter the n lines of your probability matrix
Each line must contain n elements separated by space""")
        L = np.array([[float(x) for x in input().split()] for _ in range(n)])
        querry = input("""Enter your searching string:\n""")
        M = self.web_probability_matrix(L, 0.5)
        *other, r_next = self.web_eingen_iteration(M)
        site_ranks = [float(x) for x in r_next.split("\n") if x != '']
        ranks = dict(zip(site_list, site_ranks))
        output = []
        if querry in ranks:
            output.append(querry)
            del(ranks[querry])
        ranks  = dict(sorted(ranks.items(), key=lambda item: item[1], reverse=True))

        rev_ranks = {}
        for key, value in ranks.items():
            rev_ranks.setdefault(value, []).append(key)
        for key, value in rev_ranks.items():
            if len(value) > 1:
                output.extend(sorted(value, reverse=True))
            else:
                output.append(*value)
        if len(output) > 5:
            print()
            print(*output[:5], sep="\n")
        else:
            print()
            print(*output, sep="\n")

page_rank = PageRank()
message = int(input("Please enter the number of your stage:\n"))

if message == 1 or message == 2:
    page_rank.print_I_II()
elif message == 3:
     page_rank.print_III()
elif message == 4:
    page_rank.print_IV()
elif message == 5:
    page_rank.print_V()
else:
    print("Please enter an integer from 1 to 5 inclusive!")
    message


# https://jakevdp.github.io/PythonDataScienceHandbook/04.12-three-dimensional-plotting.html
#!/usr/bin/env python3
from re import X
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np

# Taubin
def Taubin_heart_3d(x,y,z):
   return (x**2+(9/4)*y**2+z**2-1)**3-x**2*z**3-(9/80)*y**2*z**3
# Kuska
def Kuska_heart_3d(x,y,z):
   return ((2*(x**2) + y**2 + z**2 - 1)**3 - ((x**2)*(z**3))/10 - ((y**2)*(z**3)))
# Nordstrand
def Nordstrand_heart_3d(x,y,z):
   return ( (32/9)*(x**2) + 2*(y**2) + z**2 - 1 )**3 - (8/45)*(x**2)*(z**3) - (y**2)*(z**3)

def plot_implicit(fn, fig, sub_plot, color, resolution, slices, elevation, azimuth, bbox=(-1.5, 1.5)):
    ''' create a plot of an implicit function
    fn  ...implicit function (plot where fn==0)
    bbox ..the x,y,and z limits of plotted interval'''
    xmin, xmax, ymin, ymax, zmin, zmax = bbox*3
    #fig = plt.figure()
    ax = fig.add_subplot(sub_plot, projection='3d')
    A = np.linspace(xmin, xmax, resolution) # resolution of the contour
    B = np.linspace(xmin, xmax, slices) # number of slices
    A1, A2 = np.meshgrid(A, A) # grid on which the contour is plotted

    for z in B: # plot contours in the XY plane
        X, Y = A1, A2
        Z = fn(X, Y, z)
        cset = ax.contour(X, Y, Z+z, [z], zdir='z', colors=(color,))
        # [z] defines the only level to plot
        # for this contour for this value of z

    for y in B:  # plot contours in the XZ plane
        X, Z = A1, A2
        Y = fn(X, y, Z)
        cset = ax.contour(X, Y+y, Z, [y], zdir='y', colors=(color,))

    for x in B: # plot contours in the YZ plane
        Y, Z = A1, A2
        X = fn(x, Y, Z)
        cset = ax.contour(X+x, Y, Z, [x], zdir='x',colors=(color,))

    # must set plot limits because the contour will likely extend
    # way beyond the displayed level.  Otherwise matplotlib extends the plot limits
    # to encompass all values in the contour.
    ax.set_zlim3d(zmin, zmax)
    ax.set_xlim3d(xmin, xmax)
    ax.set_ylim3d(ymin, ymax)
    # elevation of 60 degrees (that is, 60 degrees above the x-y plane) and an
    # azimuth of 35 degrees (that is, rotated 35 degrees counter-clockwise about the z-axis)
    ax.view_init(elevation, azimuth)
    #plt.show()

fig = plt.figure(figsize=[12.8, 9.6])
#fig, ax = plt.subplots(2,2)
fig.suptitle('Plotting Heart Surface on a 3-Dimentional Axes', fontsize=26, color='dimgray')
fig.text(0.35, 0.05, '#30DayChartChallenge - 2021/04/30 | uncertainties | 3-dimensional', style = 'italic', fontsize = 10, color = "dimgray")
fig.text(0.35, 0.03, 'Plotting 3 variable implicit equations', style = 'italic', fontsize = 10, color = "dimgray")
fig.text(0.35, 0.01, 'twitter.com/vivekparasharr | github.com/vivekparasharr | vivekparasharr.medium.com', style = 'italic', fontsize = 10, color = "dimgray")

fig.text(0.15, 0.87, "Taubin's heart surface equation (filled)", style = 'normal', fontsize = 16, color = "dimgray")
fig.text(0.15, 0.84, "(x^2+(9/4)*y^2+z^2-1)^3-x^2*z^3-(9/80)*y^2*z^3", style = 'normal', fontsize = 12, color = "slategray")
plot_implicit(Taubin_heart_3d, fig, 221, 'crimson', 100, 40, 20, 70) # Taubin
fig.text(0.55, 0.87, "Taubin's heart surface equation (mesh)", style = 'normal', fontsize = 16, color = "dimgray")
fig.text(0.55, 0.84, "(x^2+(9/4)*y^2+z^2-1)^3-x^2*z^3-(9/80)*y^2*z^3", style = 'normal', fontsize = 12, color = "slategray")
plot_implicit(Taubin_heart_3d, fig, 222, 'red', 50, 20, 20, 70)
fig.text(0.15, 0.47, "Kuska's heart surface equation", style = 'normal', fontsize = 16, color = "dimgray")
fig.text(0.15, 0.44, "((2*(x^2)+y^2+z^2-1)^3-((x^2)*(z^3))/10-((y^2)*(z^3)))", style = 'normal', fontsize = 12, color = "slategray")
plot_implicit(Kuska_heart_3d, fig, 223, 'darkorchid', 50, 20, 20, 20)
fig.text(0.55, 0.47, "Nordstrand's heart surface equation", style = 'normal', fontsize = 16, color = "dimgray")
fig.text(0.55, 0.44, "((32/9)*(x^2)+2*(y^2)+z^2-1)^3-(8/45)*(x^2)*(z^3)-(y^2)*(z^3)", style = 'normal', fontsize = 12, color = "slategray")
plot_implicit(Nordstrand_heart_3d, fig, 224, 'fuchsia', 50, 20, 20, 15)
plt.savefig('Charts/2021-04-30.png', dpi=300, facecolor='w')
plt.show()


# PageRank - Random Walk with Restart

import numpy as np

# Iteration Time for Random Walk
# Default - Iteration Time is Set to 50
ITERATION_TIME = 50
# Iteration Delta for RWR
# Default - Iteration Delta is Set to (1.0e-10) / (1.0 * 10 ** -10)
ITERATION_DELTA = 1.0e-10

# RWR PageRank Function
# Parameters List
# @Param seeds - Seed set, List of Seeds' Position (Col Num in Transition Matrix, start from 0)
# @Param trans_2d_list - Transition Matrix in the form of 2D List
# @Param alpha - Probability of Random Walk to Neighbors, Between 0 and 1
def rwr_page_rank(seeds, trans_2d_list, alpha=0.9):
    # Row Number and Col Number in Transition Matrix
    row_num = len(trans_2d_list)

    # RWR PR Result, N * 1 Matrix, Initialized with ALL 0
    pr_result = []
    for i in range(0, row_num):
        pr_result.append([0.0])
    pr_result_vector = np.matrix(pr_result)

    # RWR for Each Seed
    for s in seeds:
        # Restart Vector For Each Seed(N * 1)
        # Values at Corresponding Position = 1/NumOfSeeds
        # Otherwise = 0
        restart_list = []
        for i in range(0, row_num):
            if i == s:
                restart_list.append([1])
            else:
                restart_list.append([0])

        # PR vector (N * 1)
        # Assumption - Initial PR Vector is the Restart Vector
        # Transform to Lists to Matrix
        restart_vector = np.matrix(restart_list)
        pr_vector = np.matrix(restart_list)
        trans_matrix = np.matrix(trans_2d_list)

        # Calculate the PR Vector with Formula Iteratively
        # pr_new = alpha * TransitionMatrix * pr_old + (1 - alpha) * SeedVector
        for i in range(0, ITERATION_TIME):
            # Set the Previous PR Vector
            prev_vector = pr_vector
            # Calculate New PR Vector
            pr_vector = alpha * trans_matrix * prev_vector + (1 - alpha) * restart_vector

            # Optional - Compare the Difference Between Previous and New Vectors
            # Get the absolute value of All raw difference
            pr_diff = (np.absolute(prev_vector - pr_vector)).tolist()
            # If the difference in each dimension is less than ITERATION_DELTA, break
            count = 0  # Counter for values that satisfy (diff < DELTA)
            for j in range(0, row_num):
                if pr_diff[j][0] < ITERATION_DELTA:
                    count += 1
            if count == row_num:
                break
            # Compare ITERATION DELTA End

        # Sum PR Vector for Each Seed
        pr_result_vector += pr_vector
    # RWR End

    pr_result = pr_result_vector.tolist()
    # Return the Final PageRank Score Vector
    return pr_result
# ----- End of Random Walk with Restart PageRank -----


import superimport

import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import cholesky
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import PolynomialFeatures

np.random.seed(12)

def gaussSample(mu, sigma, n):
    A = cholesky(sigma)
    Z = np.random.normal(loc=0, scale=1, size=(len(mu), n))
    return np.dot(A, Z).T + mu

def sqDistance(p, q):
    pSOS = np.sum(p ** 2, axis=1)
    qSOS = np.sum(q ** 2, axis=1)
    pSOS = np.repeat(pSOS[..., np.newaxis], len(qSOS), axis=1)
    dist = pSOS + qSOS - 2 * np.dot(p, q.T)
    return dist

def kernelRbfSigma(X1, X2, sigma):
    Z = 1.0 / np.sqrt(2 * np.pi * sigma ** 2)
    S = sqDistance(X1, X2)
    K = Z * np.exp(-1 / (2 * sigma ** 2) * S)
    return K

def createXORdata(doplot=False):
    off1 = gaussSample([1, 1], 0.5 * np.eye(2), 20)
    off2 = gaussSample([5, 5], 0.5 * np.eye(2), 20)
    on1 = gaussSample([1, 5], 0.5 * np.eye(2), 20)
    on2 = gaussSample([5, 1], 0.5 * np.eye(2), 20)
    X = np.concatenate([off1, off2, on1, on2], axis=0)
    y = np.concatenate([np.zeros((len(off1) + len(off2))), np.ones((len(on1) + len(on2)))], axis=0)

    if doplot:
        plt.plot(X[y == 0, 0], X[y == 0, 1], 'ob', MarkerSize=8)
        plt.plot(X[y == 1, 0], X[y == 1, 1], '+r', MarkerSize=8)
        plt.show()
    return X, y

def addOnes(X):
    X = np.concatenate((np.ones((len(X), 1)), X), axis=1)
    return X

def degexpand(x, deg, addO=0):
    n, m = x.shape

    temp = x
    for i in range(1, deg):
        xx = np.concatenate((x, temp ** (i + 1)), axis=1)
        x = xx

    if addO:
        xx = addOnes(xx)

    return xx

def rescaleData(x):
    minVal = -1
    maxVal = 1

    minx = np.min(X, axis=0)
    rangex = np.max(X, axis=0) - np.min(X, axis=0)
    y = (x - minx) / rangex

    y = y * (maxVal - minVal)
    y = y + minVal
    return y

def rbf_prototype(X1, protoTypesStnd, rbfScale):
    X1 = (X1 - X1.mean(axis=0)) / X1.std(axis=0)
    X1 = kernelRbfSigma(X1, protoTypesStnd, rbfScale)
    X1 = addOnes(X1)
    return X1

def poly_data(X1, deg):
    X1 = rescaleData(X1)
    X1 = degexpand(X1, deg)
    X1 = addOnes(X1)
    return X1

X, y = createXORdata(False)
tol = 1e-2

model = LogisticRegression(tol=tol)
model.fit(X, y)
ypred = model.predict(X)
errorRate = np.mean(ypred != y)

clf = LogisticRegression()
clf.fit(X, y)

fig, ax1 = plt.subplots(1, 1, figsize=(9, 6))
# Retrieve the model parameters.
b = clf.intercept_[0]
w1, w2 = clf.coef_.T
# Calculate the intercept and gradient of the decision boundary.
c = -b / w2
m = -w1 / w2

x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
xd = np.array([x_min, x_max])
yd = m * xd + c
ax1.plot(xd, yd, 'k', lw=1, ls='--')
h = .02  # step size in the mesh
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
X_plot_raw = np.c_[xx.ravel(), yy.ravel()]

Z = clf.predict(X_plot_raw)

# Put the result into a color plot
Z = Z.reshape(xx.shape)

ax1.pcolormesh(xx, yy, Z, cmap='Set3')

# Plot also the training points
ax1.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')
ax1.set_xlim(xx.min(), xx.max())
ax1.set_ylim(yy.min(), yy.max())
ax1.set_ylabel(r'$x_2$')
ax1.set_xlabel(r'$x_1$')
ax1.set_title('Simple Logistic regression')
fig.savefig('../figures/logregXorLinear.png')

rbfScale = 1
polydeg = 2
protoTypes = np.array([[1,1], [1,5], [5,1], [5,5]])

protoTypesStnd = (protoTypes - protoTypes.mean(axis=0)) / protoTypes.std(axis=0)

X_rbf = rbf_prototype(X, protoTypesStnd, rbfScale)
lr = LogisticRegression(tol=tol)
lr.fit(X_rbf, y)

x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
h = .02  # step size in the mesh
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
X_plot_raw = np.c_[xx.ravel(), yy.ravel()]
X_plot = rbf_prototype(X_plot_raw, protoTypesStnd, rbfScale)
Z = lr.predict(X_plot)

# Put the result into a color plot
Z = Z.reshape(xx.shape)

fig, ax2 = plt.subplots(1, 1, figsize=(9, 6))
ax2.pcolormesh(xx, yy, Z, cmap='Set3')

# Plot also the training points
ax2.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')
ax2.scatter(protoTypes[:, 0], protoTypes[:, 1], marker='*', c='r', s=90)
ax2.set_xlabel(r'$x_1$')
ax2.set_ylabel(r'$x_2$')

ax2.set_xlim(xx.min(), xx.max())
ax2.set_ylim(yy.min(), yy.max())
ax2.set_title('rbf prototypes')
fig.savefig('../figures/logregXorRbfProto.png')

X_poly = poly_data(X, 10)
lr = LogisticRegression(tol=tol)
lr.fit(X_poly, y)

x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
h = .02  # step size in the mesh
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
X_plot_raw = np.c_[xx.ravel(), yy.ravel()]
X_plot = poly_data(X_plot_raw, 10)
Z = lr.predict(X_plot)

# Put the result into a color plot
Z = Z.reshape(xx.shape)
fig, ax3 = plt.subplots(1, 1, figsize=(9, 6))
ax3.pcolormesh(xx, yy, Z, cmap='Set3')

# Plot also the training points
ax3.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')
ax3.set_xlabel(r'$x_1$')
ax3.set_ylabel(r'$x_2$')
ax3.set_title('poly10')
ax3.set_xlim(xx.min(), xx.max())
ax3.set_ylim(yy.min(), yy.max())
fig.savefig('../figures/logregXorPoly.png')


import logging
import os
import sys

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

import distromax

plt.rcParams["text.usetex"] = True
plt.rcParams["font.family"] = "serif"
plt.rcParams["font.size"] = 22

""""
Example 1: $\chi^2$ distribution

$\chi^2$ distributions fall under the domain of attraction of the Gumbel distribution
and appear very frequently in gravitational wave data analysis.

We generate a dataset of $\chi^2$ samples and apply `distromax` to estimate
the probability distribution of the loudest candidate.

The resulting batchmax distribution is compared to the theoretical distribution.
"""

basename = os.path.basename(sys.argv[0])[:-3]
outdir = os.path.join(sys.path[0], basename)
os.makedirs(outdir, exist_ok=True)

logging.info(f"Running example {basename}")
logging.info(f"Output will be saved into {outdir}")

# Create samples
dofs = 4
total_points = 2000000
data  = stats.chi2(df=dofs).rvs(total_points)

# Apply distromax
num_batches = 10000
batch_size = total_points // num_batches
bmg = distromax.BatchMaxGumbel(data, batch_size=batch_size)
bmg.max_propagation(num_batches=num_batches)

# Compute theoretical parameters of the batchmax distribution
th_loc, th_scale = distromax.analytical.AnalyticalGammaToGumbel(dofs=dofs).get_gumbel_loc_scale(batch_size)

logging.info("Data successfully generated, starting to plot results")

# Plot samples and 95% credible region of the loudest candidate
fig, ax = plt.subplots(figsize=(16, 10))
ax.set(xlabel="Sample index", ylabel="$\chi^2_{4}$-distributed detection statistic")
ax.grid()

ax.plot(data, 'o', rasterized=True, color="slateblue",
        alpha=0.4, label=f"{total_points:.2g} Background samples",
        markerfacecolor="none")

loudest_mean = bmg.propagated_gumbel.mean()
loudest_credible_interval = bmg.propagated_gumbel.interval(0.95)
ax.axhline(loudest_mean, color="red", ls="-", label="Expected loudest candidate")
ax.axhspan(*loudest_credible_interval, color="red", alpha=0.3, label=r"95\% Credibility")

ax.legend(loc="lower right")

fig.savefig(os.path.join(outdir, "SamplesAndExpectedMax.pdf"), bbox_inches="tight")
logging.info("Plot of samples and expected maxima: Success!")

# Plot batchmax samples and compare the obtained Gumbel distribution to the theoretical one
fig, ax = plt.subplots(figsize=(16, 10))
ax.grid()
ax.set(xlabel="$\chi^2_{4}$-distributed detection statistic",
       ylabel="PDF", yscale="linear")

ax.hist(bmg.samples, density=True, histtype="step", ls="--",
        bins="auto", color="blue", label=f"{total_points:.2g} Background samples");

ax.hist(bmg.batch_max, density=True, histtype="step", bins="auto", ls="-",
        color="blue", label=f"batchmax samples - {batch_size} samples per batch")

x = np.linspace(0.9 * bmg.x.min(), 1.1 * bmg.x.max(), 1000)
ax.plot(x, bmg.gumbel.pdf(x), color="red",
        label="batchmax Gumbel fit ({:.2f} {:.2f})".format(*bmg.gumbel.args))
ax.plot(x, stats.gumbel_r(th_loc, th_scale).pdf(x), color="black", ls="--",
        label="Theoretical Gumbel ({:.2f} {:.2f})".format(th_loc, th_scale))

ax.legend()

fig.savefig(os.path.join(outdir, "BatchmaxAndTheoretical.pdf"), bbox_inches="tight")
logging.info("Plot of batchmax distribution and comparison to theoretical: Success!")

logging.info(f"Location relative error: {100 * (bmg.gumbel.args[0]/th_loc - 1):.2f} %")
logging.info(f"Scale relative error: {100 * (bmg.gumbel.args[1]/th_scale - 1):.2f} %")


import numpy as np

class CostModel:
    def cost(outputs, y):
        # outputs: (num_outputs, num_examples)
        # cost: Scalar
        raise NotImplementedError('Method must be implemented by child class')

    def d_cost_d_outputs(outputs, y):
        # outputs: (num_outputs, num_examples)
        # d_cost_d_outputs: (num_outputs, num_examples)
        raise NotImplementedError('Method must be implemented by child class')

class CrossEntropyBinaryClassificationCostModel(CostModel):
    def cost(
        self,
        outputs,
        y
    ):
        # outputs: (1, num_examples)
        # y: (1, num_examples)
        # cost: Scalar

        # outputs: For this cost function, outputs[0, i] is interpreted as the
        # probability that the ith example is of class 1 (as opposed to class 0)

        # y: For this cost function, y[0, i] is either 0
        # (first class) or 1 (second class)

        num_examples = y.shape[1]
        return np.squeeze(-(1/num_examples)*np.sum(
            np.add(
                np.multiply(
                    y,
                    np.log(outputs)
                ),
                np.multiply(
                    np.subtract(1.0, y),
                    np.log(np.subtract(1.0, outputs))
                )
            ),
            axis=1
        ))

    def d_cost_d_outputs(
        self,
        outputs,
        y
    ):
        # outputs: (num_outputs, num_examples)
        # y: (num_classes, num_examples)
        # d_cost_d_outputs: (num_outputs, num_examples)

        # outputs: For this cost function, outputs[0, i] is interpreted as the
        # probability that the ith example is of class 1 (as opposed to class 0)

        # y: For this cost function, y[0, i] is either 0
        # (first class) or 1 (second class)

        num_examples = y.shape[1]
        return -(1/num_examples)*np.divide(
            np.subtract(
                y,
                outputs
            ),
            np.multiply(
                outputs,
                np.subtract(1, outputs)
            )
        )


import numpy as np
from scipy.ndimage import convolve, maximum_filter

def gauss2d(sigma, fsize):
    """ Create a 2D Gaussian filter
    Args:
        sigma: width of the Gaussian filter
        fsize: (w, h) dimensions of the filter
    Returns:
        *normalized* Gaussian filter as (h, w) np.array
    """
    m, n = fsize
    x = np.arange(-m / 2 + 0.5, m / 2)
    y = np.arange(-n / 2 + 0.5, n / 2)
    xx, yy = np.meshgrid(x, y, sparse=True)
    g = np.exp(-(xx ** 2 + yy ** 2) / (2 * sigma ** 2))
    return g / np.sum(g)

def derivative_filters():
    """ Create derivative filters for x and y direction
    Returns:
        fx: derivative filter in x direction
        fy: derivative filter in y direction
    """
    fx = np.array([[0.5, 0, -0.5]])
    fy = fx.transpose()
    return fx, fy

def compute_hessian(img, gauss, fx, fy):
    """ Compute elements of the Hessian matrix
    Args:
        img:
        gauss: Gaussian filter
        fx: derivative filter in x direction
        fy: derivative filter in y direction
    Returns:
        I_xx: (h, w) np.array of 2nd derivatives in x direction
        I_yy: (h, w) np.array of 2nd derivatives in y direction
        I_xy: (h, w) np.array of 2nd derivatives in x-y direction
    """

    #
    # You code here
    #
    img_filtered = convolve(img, gauss,mode='mirror')
    I_x=convolve(img_filtered, fx,mode='mirror')
    I_y= convolve(img_filtered, fy, mode='mirror')
    I_xx = convolve(I_x, fx, mode='mirror')
    I_yy = convolve(I_y, fy, mode='mirror')
    I_xy=convolve(I_x, fy, mode='mirror')
    return I_xx,I_yy,I_xy

def compute_criterion(I_xx, I_yy, I_xy, sigma):
    """ Compute criterion function
    Args:
        I_xx: (h, w) np.array of 2nd derivatives in x direction
        I_yy: (h, w) np.array of 2nd derivatives in y direction
        I_xy: (h, w) np.array of 2nd derivatives in x-y direction
        sigma: scaling factor
    Returns:
        criterion: (h, w) np.array of scaled determinant of Hessian matrix
    """

    #
    # You code here
    #
    # criterion=np.power(sigma,4)*(I_xx@I_yy-I_xy@I_xy)
    #
    # H1=np.hstack((I_xx,I_xy))
    # H2=np.hstack((I_xy,I_yy))
    # H=np.vstack((H1,H2))
    # det_H=np.linalg.det(H)
    # criterion = np.power(sigma, 4) * det_H

    criterion = np.power(sigma, 4) * (np.multiply(I_xx,I_yy) - np.multiply(I_xy, I_xy))
    return criterion

def nonmaxsuppression(criterion, threshold):
    """ Apply non-maximum suppression to criterion values
        and return Hessian interest points
        Args:
            criterion: (h, w) np.array of criterion function values
            threshold: criterion threshold
        Returns:
            rows: (n,) np.array with y-positions of interest points
            cols: (n,) np.array with x-positions of interest points
    """

    #
    # You code here
    #
    criterion_filtered = maximum_filter(criterion,size=5,mode='mirror')
    size = np.shape(criterion_filtered)
    with np.nditer(criterion_filtered, op_flags=['readwrite'], flags=['multi_index']) as it:
        for x in it:
            index=it.multi_index
            if (index[0]>(size[0]-1-5))or(index[0]<5)or(index[1]>(size[1]-1-5))or(index[1]<5):
                x[...]=0

    rows, cols = np.nonzero(criterion_filtered > threshold)

    return rows, cols


# -*- coding: utf-8 -*-
"""
=== DFT_plot_NxN.py ===================================================

Show DFT base functions

TODO: layout improvement

(c) https://github.com/unpingco/Python-for-Signal-Processing/blob/master/Fourier_Transform.ipynb
===========================================================================
"""
from __future__ import division, print_function, unicode_literals

import numpy as np
import numpy.random as rnd
from numpy import (pi, log10, sqrt, exp, sin, cos, tan, angle, arange,
                    linspace, array, zeros, ones)
from numpy.fft import fft, ifft, fftshift, ifftshift, fftfreq
import scipy.signal as sig
import scipy.interpolate as intp

import matplotlib.pyplot as plt
from matplotlib.pyplot import (figure, plot, stem, grid, xlabel, ylabel,
    subplot, title, clf, xlim, ylim)

from matplotlib.patches import FancyArrow
import matplotlib.gridspec as gridspec

def dftmatrix(Nfft=32,N=None):
    'construct DFT matrix'
    k= np.arange(Nfft)
    if N is None: N = Nfft
    n = arange(N)
    U = np.matrix(exp(1j* 2*pi/Nfft *k*n[:,None])) # use numpy broadcasting to create matrix
    return U/sqrt(Nfft)

Nfft=16
v = ones((16,1))
U = dftmatrix(Nfft=Nfft,N=16)

# ---
# hardcoded constants to format complicated figure

gs = gridspec.GridSpec(8,12)
gs.update( wspace=1, left=0.01)

fig =figure(figsize=(10,5))
ax0 = subplot(gs[:,:3])
fig.add_subplot(ax0)

ax0.set_aspect(1)
a=2*pi/Nfft*arange(Nfft)

colors = ['k','b','r','m','g','Brown','DarkBlue','Tomato','Violet', 'Tan','Salmon','Pink',
          'SaddleBrown', 'SpringGreen', 'RosyBrown','Silver',]
for j,i in enumerate(a):
  ax0.add_patch( FancyArrow(0,0,cos(i),sin(i),width=0.02,
                            length_includes_head=True,edgecolor=colors[j]))

ax0.text(1,0.1,'0',fontsize=16)
ax0.text(0.1,1,r'$\frac{\pi}{2}$',fontsize=22)
ax0.text(-1,0.1,r'$\pi$',fontsize=18)
ax0.text(0.1,-1.2,r'$\frac{3\pi}{2}$',fontsize=22)
ax0.axis(array([-1,1,-1,1])*1.45)
ax0.set_title('Radial Frequency')
ax0.set_xlabel('Real')
ax0.set_ylabel('Imaginary')

# plots in the middle column
for i in range(8):
  ax=subplot(gs[i,4:8])
  ax.set_xticks([]);  ax.set_yticks([])
  ax.set_ylabel(r'$\Omega_{%d}=%d\times\frac{2\pi}{16}$'%(i,i),fontsize=16,
                rotation='horizontal')
  ax.plot(U.real[:,i],'-o',color=colors[i])
  ax.plot(U.imag[:,i],'--o',color=colors[i],alpha=0.2)
  ax.axis(ymax=4/Nfft*1.1,ymin=-4/Nfft*1.1)
ax.set_xticks(arange(16))
ax.set_xlabel('n')

# plots in the far right column
for i in range(8):
  ax=subplot(gs[i,8:])
  ax.set_xticks([]);  ax.set_yticks([])
  ax.set_ylabel(r'$\Omega_{%d}=%d\times\frac{2\pi}{16}$'%(i+8,i+8),fontsize=16,
                rotation='horizontal')
  ax.plot(U.real[:,i+8],'-o',color=colors[i+8])
  ax.plot(U.imag[:,i+8],'--o',color=colors[i+8],alpha=0.2)
  ax.axis(ymax=4/Nfft*1.1,ymin=-4/Nfft*1.1)
  ax.yaxis.set_label_position('right')
ax.set_xticks(arange(16))
ax.set_xlabel('n')

plt.show()


# William Nguyen
# email: williamvnguyen2@gmail.com

import numpy as np
import hashlib

class CountMinSketch:
    """
    The count–min sketch (CM sketch)
    is a probabilistic data structure that serves as
    a frequency table of events in a stream of data.
    It uses hash functions to map events to frequencies,
    but unlike a hash table uses only sub-linear space, at
    the expense of overcounting some events due to collisions.
    - increment(x): to update the count of x
    - count(x): to get an approximate frequency of x in the stream
    - clear(): clears the CMS
    """

    def __init__(self, hashes=5, buckets=256, seed = 1, is_conservative = True):
        """
        Constructor for CMS.
        :param hashes: the number of hash functions
        :param buckets: the number of buckets
        :param seed: a seed for our hash function
        :param is_conservative: whether to use conservative increment
        """
        self.buckets = buckets
        self.hashes = hashes
        self.seed = seed
        self.is_conservative = is_conservative
        # the table of counts, dimensions: self.hashes by self.buckets
        self.table = np.zeros((self.hashes, self.buckets))
        # md5 hash function
        self.md5 = hashlib.md5

    def hash(self, x, i):
        """
        Returns the hash of x using hash function i.
        Note: our hash function i, is the ith byte of
        the md5 hash of str(x) + str(self.seed).
        :param x: element to be hashed
        :param i: the hash function to be used
        :return: the hash of x using hash function i
        """
        concat = str(x) + str(self.seed)
        concat_bytes = self.md5(concat.encode('utf-8')).digest()
        return concat_bytes[i]

    def increment(self, x):
        """
        Increments the frequency for the element x.
        :param x: element to be incremented
        """
        if self.is_conservative:
            # update the counters that have the minimum counts,
            # if ties, update all ties
            min_freq = self.count(x)
            for i in range(self.hashes):
                if self.table[i][self.hash(x, i)] == min_freq:
                    self.table[i][self.hash(x, i)] += 1
        else:
            for i in range(self.hashes):
                self.table[i][self.hash(x, i)] += 1

    def count(self, x):
        """
        Returns the approximate frequency of the element x.
        :param x: element in which we want a frequency count of
        :return: count of x
        """
        min_count = float('inf')
        for i in range(self.hashes):
            min_count = min(min_count, self.table[i][self.hash(x, i)])
        return min_count

    def clear(self):
        """
        Resets the CMS to default state.
        """
        self.table = np.zeros((self.hashes, self.buckets))