import numpy as  np

class LinearRegression:
    def fit(self, X, y):
        X = np.concatenate((np.ones_like(X[:, 0])[:, np.newaxis], X), axis=1)
        self.w = np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y)

    def compute_frontier(self):

        def frontier(x1):
            return (1/self.w[2])*(-self.w[1] * x1 + 0.5 - self.w[0])

        return frontier

    def predict_proba(self, X):
        X = np.concatenate((np.ones_like(X[:, 0])[:, np.newaxis], X), axis=1)
        return self.w.dot(X.T)

    def predict(self, X):
        return self.predict_proba(X) > 0.5

    def compute_misclassif_error(self, X, y):
        return np.sum(self.predict(X) != y)/len(y)


import numpy as np
import matplotlib.pyplot as plt
import os

dir_path = os.path.dirname(os.path.realpath(__file__))

def log_loss(w, X, y):
    """
    Computes the log-loss function at w. The
    computation uses the data in X with
    corresponding labels in y.
    """

    L = 0 # Accumulate loss terms here.

    # Process each sample in X:
    for n in range(X.shape[0]):
        L += np.log(1 + np.exp(-y[n] * np.dot(w, X[n])))

    return L

def grad(w, X, y):
    """
    Computes the gradient of the log-loss function
    at w. The computation uses the data in X with
    corresponding labels in y.
    """

    G = 0 # Accumulate gradient here.

    # Process each sample in X:
    for n in range(X.shape[0]):

        numerator = -y[n] * X[n]
        denominator = 1 + np.exp(y[n] * np.dot(w, X[n]))

        G += numerator / denominator

    return G

if __name__ == "__main__":

    # Add your code here:

    # 1) Load X and y.
    X = np.genfromtxt(os.path.join(dir_path , "data", "log_loss_data", "X.csv"), delimiter=",")
    y = np.genfromtxt(os.path.join(dir_path , "data", "log_loss_data", "y.csv"), delimiter=",")

    # 2) Initialize w at w = np.array([1, -1])
    w = np.array([1, -1])

    # 3) Set step_size to a small positive value.
    step_size = 0.001

    # 4) Initialize empty lists for storing the path and
    # accuracies: W = []; accuracies = []
    accuracies = []
    W = []

    for iteration in range(100):

        # 5) Apply the gradient descent rule.
        w = w - step_size * grad(w, X, y)

        # 6) Print the current state.
        print ("Iteration %d: w = %s (log-loss = %.2f)" % \
              (iteration, str(w), log_loss(w, X, y)))

        # 7) Compute the accuracy (already done for you)

        # Predict class 1 probability
        y_prob = 1 / (1 + np.exp(-np.dot(X, w)))
                # Threshold at 0.5 (results are 0 and 1)
        y_pred = (y_prob > 0.5).astype(int)
                # Transform [0,1] coding to [-1,1] coding
        y_pred = 2*y_pred - 1

        accuracy = np.mean(y_pred == y)
        accuracies.append(accuracy)

        W.append(w)

    # 8) Below is a template for plotting. Feel free to
    # rewrite if you prefer different style.

    W = np.array(W)

    plt.figure(figsize = [5,5])
    plt.subplot(211)
    plt.plot(W[:,0], W[:,1], 'ro-')
    plt.xlabel('w$_0$')
    plt.ylabel('w$_1$')
    plt.title('Optimization path')

    plt.subplot(212)
    plt.plot(100.0 * np.array(accuracies), linewidth = 2)
    plt.ylabel('Accuracy / %')
    plt.xlabel('Iteration')
    plt.tight_layout()
    plt.savefig(os.path.join(dir_path, "log_loss_minimization.pdf"), bbox_inches = "tight")


import numpy as np
from .functions import hav

def polygon_excess(vertices):
    '''
    Calculate the signed area of a spherical polygon over a unit sphere.

    Usage:
    signed_area = polygon_excess(vertices)

    Inputs:
    vertices -> [float 2d array] Vertices of the spherical polygon in form of [[lat_0,lon_0],..,[lat_n,lon_n]] with unit of degrees.
    Vertices can be arranged either counterclockwise or clockwise.

    Outputs:
    signed_area -> [float] The signed area of a spherical polygon in steradians.
    For the case where the South Pole is outside the polygon, if the arrangement of the vertices is counterclockwise, the signed area should be positive, otherwise, it should be negative.
    For the case where the South Pole is inside the polygon, if the arrangement of the vertices is counterclockwise, the signed area should be negative, otherwise, it should be positive.

    Note: The spherical polygon has a latitude range of [-90°,90°] and a longitude range of [-180°,180°] or [0°,360°].
    '''
    N = len(vertices)

    sum_excess = 0

    for i in range(N-1):
        p1,p2 = np.radians(vertices[i]),np.radians(vertices[i+1])
        pdlat,pdlon = p2[0] - p1[0], p2[1] - p1[1]
        dlon = np.abs(pdlon)

        # If two adjacent vertices are close enough(coincident), do nothing.
        if dlon < 1e-6: continue

        # Calculate the area of a spherical triangle consisting of sides and north poles
        if dlon > np.pi: dlon = 2*np.pi - dlon
        if pdlon < -np.pi: p2[1] = p2[1] + 2*np.pi
        if pdlon > np.pi: p2[1] = p2[1] - 2*np.pi

        havb = hav(pdlat) + np.cos(p1[0])*np.cos(p2[0])*hav(dlon)
        b = 2*np.arcsin(np.sqrt(havb))
        a,c = np.pi/2 - p1[0], np.pi/2 - p2[0]
        s = 0.5*(a + b + c)
        t = np.tan(s/2)*np.tan((s - a)/2)*np.tan((s - b)/2)*np.tan((s - c)/2)
        excess = 4*np.arctan(np.sqrt(np.abs(t)))
        if p2[1] - p1[1] < 0: excess = -excess

        sum_excess += excess

    return sum_excess

def polygon_area(vertices):
    '''
    Calculate the area of a spherical polygon over a unit sphere.

    Usage:
    area = polygon_area(vertices)

    Inputs:
    vertices -> [float 2d array] Vertices of a spherical polygon in format of [[lat_0,lon_0],..,[lat_n,lon_n]] with unit of degrees.
    Vertices can be arranged either counterclockwise or clockwise.

    Outputs:
    area -> [float] Area of the spherical polygon in steradians. It is independent of how the vertices are arranged.

    Note: The spherical polygon has a latitude range of [-90,90] and a longitude range of [-180,180] or [0,360].
    '''
    excess = polygon_excess(vertices)
    area = np.abs(excess)

    if area > 2*np.pi: area = 4*np.pi - area

    return area


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

iteration=5

#deal with b_n
b_n=[]
for i in range(1,iteration+1):
    b_n.append((2/math.pi)*((1-(-1)**i)/i))

print(b_n)

def fourier(b_n,iteration,x):
    value=0
    for i in range(1,iteration):
        value+=b_n[i-1]*math.sin(i*x)
    return value

#visualize the fourier transform
discrete_num=2000
x=np.linspace(-math.pi+0.1,math.pi-0.1,discrete_num)
y=[]
for j in range(discrete_num):
    y.append(fourier(b_n,iteration,x[j]))

plt.plot(x,y)
plt.title("realize the fourier transformation (number of summations: %d)" %(iteration))
plt.xlabel("x")
plt.ylabel("y")
plt.text(-3,0,"f(x)=-1, for x=-π~0")
plt.text(-3,-0.5,"f(x)=1, for x=0~π")
plt.show()


import numpy as np
def DFT(x, N):
    #N = len(x)
    x = np.concatenate([x, [0]*(N-len(x))])
    n = np.arange(N)
    k = n.reshape((N,1))
    twiddler_mat = np.exp((-2j*np.pi*n*k)/N)
    #print(twiddler_mat)

    dft_X = np.dot(twiddler_mat,x)

    return dft_X
x = np.array([0,1,2,3])
N = 4
X = DFT(x,N)
print('DFT: ',X)

def IDFT(X, N):
    #N = len(X)
    #print(N)
    k = np.arange(N)
    n = k.reshape((N,1))
    twiddler_mat = np.exp((2j*np.pi*k*n)/N)
    #print(twiddler_mat)
    x = (np.dot(twiddler_mat, X))/N

    return x
N = 4
ans = IDFT(X,N)
print('IDFT: ',np.round(ans))

def FIR(x,h,N):
    dft_x = DFT(x,N)
    dft_h = DFT(h,N)
    mul = dft_x * dft_h

    idft = IDFT(mul, N)
    return np.round(idft)
x = np.array([1,2,2,1])
h = np.array([1,2,3])

print(FIR(x,h,8))

# N = 8
# dft_x = DFT(x, N)
# #print(dft_x)
# dft_h = DFT(h,N)
# #print(dft_h)
# mul = dft_x * dft_h
# #print(mul)

# idft = IDFT(mul, N)
# print(np.round(idft))


# -*- coding: utf-8 -*-
"""
Created on Sun Jun 10 23:57:45 2018
@author: Sathish Sampath(ss.sathishsampath@gmail.com)
"""

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

# Read the input file
df = pd.read_csv("PierceCricketData.csv")

x_data, y_data = (df["Chirps"].values,df["Temp"].values)
X = tf.placeholder(tf.float32, shape=(x_data.size))
Y = tf.placeholder(tf.float32,shape=(y_data.size))
m = tf.Variable(3.0)
c = tf.Variable(2.0)

# Construct a Model
Ypred = tf.add(tf.multiply(X, m), c)

# Create and Run a Session to Visualize the Predicted Line from above Graph
session = tf.Session()
session.run(tf.global_variables_initializer())
pred = session.run(Ypred, feed_dict={X:x_data})

#Initial Prediction vs Datapoints
plt.plot(x_data, pred)
plt.plot(x_data, y_data, 'ro')
plt.xlabel("Number of Chirps every 15 sec")
plt.ylabel("Temperature in Farenhiet")

#Loss Function
nf = 1e-1
loss = tf.reduce_mean(tf.squared_difference(Ypred*nf,Y*nf))

# Defining Gradient Descent Optimizer and implementing it to mimize loss
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train = optimizer.minimize(loss)

# Reinitializing the Global Variables
session.run(tf.global_variables_initializer())

convergenceTolerance = 0.0001
previous_m = np.inf
previous_c = np.inf

steps = {}
steps['m'] = []
steps['c'] = []

losses=[]

for k in range(100000):
    _, _m , _c,_l = session.run([train, m, c,loss],feed_dict={X:x_data,Y:y_data})
    steps['m'].append(_m)
    steps['c'].append(_c)
    losses.append(_l)
    if (np.abs(previous_m - _m) <= convergenceTolerance) or (np.abs(previous_c - _c) <= convergenceTolerance):

        print("Finished by Convergence Criterion")
        print(k)
        print(_l)
        break
    previous_m = _m,
    previous_c = _c,

session.close()

# Plotting the Loss against number of steps taken to converge
plt.plot(losses[:])


# Problemas propostos

No laboratório computacional, você praticará o que aprendeu. Resolva os problemas com o auxílio do Python pesquisando apenas as informações essenciais de que precisa. Não use respostas prontas.

**Problema (Índice de Massa Corpórea - IMC):** O IMC é usado para saber se um indivíduo está no peso ideal. Ele é definido pela fórmula

$$IMC = \dfrac{M}{A^2},$$

onde $M$ é a massa (considere quilogramas) do indivíduo e $A$ é a sua altura (considere metros). Um indíviduo tem *peso normal* se seu IMC estiver no intervalo faixa $18.5 \leq IMC < 25.0$.

A seleção brasileira masculina de futebol profissional convocada para a Copa do Mundo 2018 era composta de 23 jogadores, a saber: Alisson, Casemiro, Cássio, Danilo, Douglas Costa, Ederson, Fagner, Fernandinho, Filipe Luis, Firmino, Fred, Gabriel Jesus, Geromel, Marcelo, Marquinhos, Miranda, Neymar, Paulinho, Philippe Coutinho, Renato Augusto, Taison, Thiago Silva e Willian.

O arquivo `04c-copa2018.npy`, disponibilizado juntamente com este arquivo, contém uma tabela de peso, altura e idade de cada um desses atletas.

O gráfico abaixo plota o valor do IMC para cada um desses jogadores. Todos os jogadores possuem peso ideal, mas a linha tracejada divide-os em dois grupos:

- Grupo 1: aqueles na faixa ideal cujo IMC é menor do que 21.7.
- Grupo 2: aqueles na faixa ideal cujo IMC é maior do que 21.7.

[[Fonte: ESPN]](https://www.espn.com.br/artigo/_/id/4310177/selecao-brasileira-veja-o-censo-completo-dos-jogadores-que-vao-a-copa)

Carregue as informações do arquivo e reproduza o gráfico da figura anterior ou crie um similar estilizando-o do seu modo que contenha:

- a linha tracejada intermediária;
- marcadores coloridos de modo distinto;
- eixo com os nomes dos jogadores;
- linhas de grade;

**Obs.:** Use a função do *numpy* `load('...')`, com a opção `allow_pickle=True`.

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

lu = np.load('04c-copa2018.npy',allow_pickle=True)
jogador = lu[:,0]
massa = lu[:,1]
altura = lu[:,2]
idade = lu[:,3]

imc = massa/altura**2 # IMC

g1 = np.where(imc < 21.7,True,False) # grupo 1
g2 = g1 == False # grupo 2

# PLOT
plt.figure(figsize=(16,4))
plt.plot(jogador,imc,lw=0)
plt.plot(jogador[g1],imc[g1],'vr',alpha=0.5)
plt.plot(jogador[g2],imc[g2],'^b',alpha=0.5)
plt.plot(jogador,np.ones(np.shape(imc))*np.mean(imc),'--',lw=1,c=[0.5,0.5,0.5])
plt.xticks(rotation=45);
plt.ylabel('IMC',fontsize=14)
plt.xticks(fontsize=10)
plt.yticks(fontsize=14)
plt.title('IMC - Jogadores da Seleção Brasileira (Copa 2018)',c='#407294',fontsize=16)
plt.grid(axis='x',alpha=0.25)
#plt.savefig('IMC.png')

**Problema (Taxa Metabólica Basal - TMB)**: A TMB é a quantidade mínima de energia que o ser humano, em repouso, precisa para sobreviver. A *Equação de Mifflin - St. Jeor* para calcular a TMB em kcal/dia (quilocalorias por dia) de pessoas do sexo masculino é dada por:

$$TMB = 10M + 6.25A + 5I + 5,$$

onde $M$ é a massa do indivíduo, $A$ sua altura e $I$ sua idade.

[[Fonte: Wiikipedia]](https://en.wikipedia.org/wiki/Basal_metabolic_rate)

Usando a equação de Mifflin - St. Jeor:

- calcule a energia necessária total para a manutenção vital de todos os jogadores da seleção de 2018 durante um ano inteiro, isto é, a TMB anual. Considere 1 ano = 365 dias.
- calcule a TMB anual do time inteiro para o quinquenio 2020 - 2024. Assuma o início do período em 1 de janeiro de 2020 e que nenhuma alteração na escalação do time ocorrerá no período.
- plote um gráfico em que o eixo `x` deve corresponder ao quinquênio e o eixo `y` ao total da TMB para todos os jogadores em cada ano. Use marcadores do tipo `'o'`.
- qual é o valor total em kcal no final de 2024?

**Obs:** Note que a idade dos jogadores se altera com o tempo. Isto é, após o quinquênio estarão 5 anos mais velhos e a TMB para cada um será diferente a cada ano.

# Eq. Mifflin - St. Jeor
mifflin = lambda M,A,I: 10*M + 6.25*A + 5*I + 5 # kcal / dia

TMB_jogadores = mifflin(massa,altura,idade)

# energia necessária nos próximos 5 anos

TMB = {}
for i in range(5):
    TMB[2020+i] = np.sum(365*mifflin(massa,altura,idade+i)) # kcal necessárias por ano para o time

#for i in TMB.keys():
#    plt.semilogy(i,TMB[i],'ro-')
#plt.xlabel('Quinquenio 2020-2024',fontsize=14);
#plt.ylabel('kcal',fontsize=14);
#plt.xticks(np.arange(2020,2025));
#plt.title('Demanda calórica total da seleção brasileira - TMB');

**Problema (Movimento de bola de futebol):** O movimento executado por uma bola de futebol ao ser chutada a partir do campo por um jogador é similar ao movimento parabólico de um projétil. A velocidade da bola $V_b$ pode ser calculada pela expressão:

$$V_b = V_p\left( \dfrac{M_p}{M_p + M_b} \right)(1 + e),$$

onde $V_p$ é a velocidade da perna do chutador, $M_p$ é a massa da perna do chutador, $M_b$ é a massa da bola e $e$ é o *coeficiente de restituição* da bola.

O alcance $a$ é a medida horizontal máxima que a bola atinge a partir do ponto de lançamento de acordo com um certo ângulo em que é lançada. Como conhecemos da Física Básica, a fórmula para o alcance é dada por:

$$a = \dfrac{V_b^2\textrm{sen}(2\alpha)}{g}$$

Diante disso, considere os seguintes dados:

- A massa da bola de futebol profissional é de 400 gramas e seu coeficiente de restituição é 0.7.
- A massa da perna de um jogador equivale a 10% de sua massa.
- A velocidade da perna de um jogador é de 20 m/s.
- A constante gravitacional equivale a 9.8 m/s2.

Assuma que um campo de futebol profissional "padrão FIFA" possui área de 100 x 68 2. Além disso, defina um *Whole-Field Kicker* (WFK) o jogador que, chutando uma bola a um ângulo de 45 graus, consegue transportá-la de gol a gol, ou seja de uma linha de fundo a outra, e como *Not Whole-Field Kicker* (not WFK) aquele que não consegue realizar esta proeza.

Usando os dados disponíveis na tabela dos jogadores da seleção de 2018:

- determine os jogadores que são "WFK" e os "not WFK", bem como a quantidade de jogadores em cada classe.
- determine quem é o WFK do time que chuta mais longe.
- determine qual é o menor alcance entre os chutes.
- plote um gráfico do tipo jogador x alcance, colorindo os "not WFK" com marcadores com borda vermelha e cor de face branca.

[[Fonte: Physics of Kicking a Soccer Ball]](http://www.mathematicshed.com/uploads/1/2/5/7/12572836/physicsofkickingsoccerball.pdf)

# vp: velocidade da perna do jogador (fixa: 20 m/s)
# mp: massa da perna do jogador (10% da massa do jogador)
# e: coeficiente de restituição da bola (média da bola oficial: 0.7)
# mb: massa da bola de futebol oficial (mb = 0.4 kg)
# alpha: angulo de lançamento da bola (chute)
# g: constante gravitacional (9.8 m/s2)

# velocidade da bola em m/s
vb = lambda vp,mp,mb,e: vp*(mp/(mp + mb))*(1 + e)

# formula do alcance: movimento projetil
alcance = lambda vb, alpha, g: vb**2*np.sin(2*alpha)/g

# Considere o campo FIFA: 100 m x 68 m

e = 0.7
vp = 20.0
mb = 0.4
g = 9.8
alpha = 45

MP = 0.2*massa # massas perna
VB = vb(vp,MP,mb,e) # vels bola
ALC = alcance(VB,alpha,g)

# quais jogadores conseguem um 'chute de campo inteiro'
kicker = ALC >= 100

plt.figure(figsize=(16,4))
plt.plot(jogador,ALC,'ro',mfc='w')
plt.plot(jogador[kicker],ALC[kicker],'o')
plt.xticks(rotation=45);
plt.ylabel('metros',fontsize=14);
plt.legend(['not WFK','WFK']); # Whole-Field Kicker
plt.grid(axis='y');

# maior, menor alcance
maior = np.max(ALC)
menor = np.min(ALC)
maior,menor
print(jogador[kicker])


'''
    The Fixed Point Iteration
    Purpose:
        Computes approximate solution of f(x) = 0 by solving the fixed point
        problem x = g(x)
    Input:
        g: function handle
        x0: initial guess
        tol: tolerance error
        iters: iteration number
        max_iters: maximum iteration number
    Output:
        Approximate solution

Last edited by Xiaozhou Li, September 15, 2018
'''

import numpy as np

def fixed_iters(g, x0, iters):
    x = np.zeros(iters+1)
    x[0] = x0
    for i in range(iters):
        x[i+1] = g(x[i])
    return x

def fixed_tol(g, x0, tol, max_iters=100):
    x_old = x0
    x_new = g(x_old)
    iters = 1
    while (np.abs(x_new - x_old) > tol):
        x_old = x_new
        x_new = g(x_old)
        iters += 1
        if (iters > 100):
            print ("Maximum iteration number achieved!")
            return
    return x_new

#########################################################
# Example
def g(x):
    return np.cos(x)

print (fixed_tol(g,0.5,1.e-12))
print (fixed_iters(g,0.5,20))


import numpy as np

import sys
sys.path.append("../../IoTPy/core")
sys.path.append("../../IoTPy/agent_types")
sys.path.append("../../IoTPy/helper_functions")

# stream is in IoTPy/IoTPy/core
from stream import Stream, StreamArray
# op is in IoTPy/IoTPy/agent_types
from op import map_window
from basics import fmap_w
# recent_values is in IoTPy/IoTPy/helper_functions
from recent_values import recent_values

def window_dot_product(in_stream, out_stream, multiplicand_vector, step_size=1):
    """
    Parameters
    ----------
    in_stream: Stream
       input stream of agent
    out_stream: Stream
       output stream of agent
    multiplicand_vector: list or NumPy array
       length of multiplicand_vector must be strictly positive
       The dot product is applied between each sliding window
       and the multiplicand_vector
    step_size: int
       Must be positive
       The amount by which the sliding window moves on each step.

    Operation
    ---------
    Creates an agent which carries out the dot product of the
    multiplicand_vector and each sliding window.

    This example uses the wrapper map_window.

    """
    def f(window, multiplicand_vector):
        return np.dot(window, multiplicand_vector)

    map_window(
        f, in_stream, out_stream,
        window_size=len(multiplicand_vector), step_size=1,
        multiplicand_vector=multiplicand_vector)

def win_dot(in_stream, multiplicand_vector):
    """
    Same as the previous example except that this version
    uses the functional decorator @fmap_w instead of the
    wrapper.

    """
    @fmap_w
    def f(window, multiplicand_vector=multiplicand_vector):
        return np.dot(window, multiplicand_vector)

    return f(in_stream, window_size=len(multiplicand_vector), step_size=1,
      multiplicand_vector=multiplicand_vector)

#----------------------------------------------------------------
# TESTS
#----------------------------------------------------------------
def test_1():
    x = Stream('x')
    y = Stream('y')
    window_dot_product(x, y, multiplicand_vector=[2, 100])
    x.extend(np.arange(8))
    Stream.scheduler.step()
    assert recent_values(y) == [100, 202, 304, 406, 508, 610, 712]
    # y[n] = x[n]*2 + x[n+1]*100, for n = 0, 1, ..., 6
    # so, y[n] = 2*n + 100*(n+1)
    # Note that the length of recent_values(y) is 7 rather than 8
    # because the window size is 2 (i.e. the size of the multiplicand
    # vector).

def test_2():
    x = Stream('x')
    z = win_dot(x, multiplicand_vector=[2, 100])
    x.extend(np.arange(8))
    Stream.scheduler.step()
    assert recent_values(z) == [100, 202, 304, 406, 508, 610, 712]

if __name__ == '__main__':
    test_1()
    test_2()


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

'''To understand data distribution completely and properly we measures such as:
1 - measures of central tendency (mean, median, mode)
2 - measures of dispersion (standard deviation, variance)
3 - measures to describe shape of a distribution

Such measures are part of the descriptive statistics scope, which allows to summarize data
in order to get further insights and help with the decision of what comes next: inferential stats or complex algorithms...
'''

# Importing Libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Reading csv file
df = pd.read_csv('stock_price.csv')

# K Stock Descriptive Stats
kelloggs = df['K']

mean = kelloggs.mean()
median = kelloggs.median()
mode = kelloggs.mode()
print('\n')
print('Descriptive Statistics for Kellogs Stock \n')
print('Mean: ',mean,'\nMedian: ',median,'\nMode: ',mode[0])

plt.figure(figsize=(10,5))
plt.hist(kelloggs,bins=10,color='grey')
plt.axvline(mean,color='red',label='Mean')
plt.axvline(median,color='yellow',label='Median')
plt.axvline(mode[0],color='green',label='Mode')
plt.title('Frequency Distribution of Closing Stock Price for Kellogs')
plt.xlabel('Kellogs - Stock Price')
plt.ylabel('Frequency')
plt.legend()
plt.show()

# XOM Stock Descriptive Stats
exxon = df['XOM']

mean = exxon.mean()
median = exxon.median()
mode = exxon.mode()
print('\n')
print('Descriptive Statistics for Exxon Mobil Stock \n')
print('Mean: ',mean,'\nMedian: ',median,'\nMode: ',mode[0])

plt.figure(figsize=(10,5))
plt.hist(kelloggs,bins=10,color='grey')
plt.axvline(mean,color='red',label='Mean')
plt.axvline(median,color='yellow',label='Median')
plt.axvline(mode[0],color='green',label='Mode')
plt.title('Frequency Distribution of Closing Stock Price for Exxon Mobil')
plt.xlabel('Exxon Mobil - Stock Price')
plt.ylabel('Frequency')
plt.legend()
plt.show()

# The 'describe' method generates descriptive stats that summarize measures of central tendency, dispersion and shape of data
pd.set_option('display.width', 100)
pd.set_option('precision', 3)
summary = df.describe()
print('\n')
print('Descriptive Stats summary for K & XOM Stocks \n \n'+ str(summary))

'''              K       XOM
count  2518.000  2518.000
mean     52.809    71.473
std      12.862    11.723
min      27.741    45.231
25%      41.938    61.082
50%      53.135    74.555
75%      62.901    80.740
max      83.924    92.545

The descriptive summary above suggests:

Given the average, XOM stock has performed 35% better over time than the K stock.
The stock prices did not have a high volatility in performance, as per comparison of std values.
25%(Lower Quartile) of the stock prices have values <= 41.938 - K & 61.082 - XOM
50% (Median) of the stock prices have values <= 53.135 - K & 74.555 - XOM
75% (Upper Quartile) of the stock prices have values <= 62.901 - K & 80.740 - XOM

'''
# Calculate the percentage increase given mean values for K & XOM
#xom = float(71.473)
#k = float(52.809)
#percentage_change = (xom - k) / k * 100
#print('\n')
#print('Percentage increase over the last 10 years given mean value: ' + str(percentage_change))


# This file has been written to explore the accuracy of finite
#    difference approaches to 1D diffusion equations

# To ensure a fair comparison the function should be calculated at a fixed time T
from parabolic_pde_solver import pde_solve
import numpy as np
import matplotlib.pyplot as plt
from math import pi

def accuracy_explorer(method, mt_a, mx, L, T, u_I,
        heat_source=lambda x, t: [0], f_kappa=lambda x: 1):
    """
    This function can be used to test the accuracy of the:
        Forward Euler
        Backward Euler
        Crank-Nicholson

    On the heat equation

    The error measure used for the comparison is the L2 Norm
        link: https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm

    The function calculates the Error vs a variety of stepsizes and can visualise
    the result

    USAGE: accuracy_explorer(method, mt_a, mx_a, L, T, u_I,
         heat_source=lambda x, t: [0], f_kappa=lambda x: 1)

    INPUTS:
        method:(String) Method to be used to solve the equation
            CN => Crank-Nicholson
            FE => Forward Euler
            BE => Backward Euler
        mt_a: (Array) A range of the number sample points in [0,T] over which to test the solver
        mx: (Int) The number of sample points over [0, L]
        L: (Float) Length of the bar
        T: (T) Float The time period over which to test the function
        u_I: A function describing the initial heat distribution

    **Optional**(Default)
        heat_source=(lambda x, t : [0]) an internal heat source applied to the bar
            as a function of position and time
        f_kappa: (lambda x: 1) the diffusion coefficient as a function of x in [0:L]
    """
    L2_norm_vec = []
    delta_T = []
    delta_X = []
    print('\nmethod=',method)
    # Loop over mt array and calculate trucation error
    for mt in mt_a:
        print('deltaT=', T/mt)
        print('deltax=', L/mx)
        # Calculate 1st approx
        [u_h, u_x, u_t] = pde_solve(L, T, u_I, mx, int(mt), logger=True, method=method,
                heat_source=heat_source, f_kappa=f_kappa)
        #calculate 2nd approx
        [u_h2, u_hx, u_ht] = pde_solve(L, T, u_I, mx, int(mt/2), logger=True, method=method,
                heat_source=heat_source, f_kappa=f_kappa)
        # Calculate E
        L2_norm  = np.linalg.norm(np.subtract(u_h2, u_h)/mx)
        print('Abs err=',L2_norm)
        L2_norm_vec.append(L2_norm)
        delta_T.append(T/mt)

    return [L2_norm_vec, delta_T]

if __name__ == "__main__":
    # Explore accuracy with change of mt  of the separate methods on heat equation
    L=100   # length of spatial domain
    T=0.5       # total time to solve for
    # set numerical parameters
    mx = 1e3    # number of gridpoints in space
    mt_a = np.logspace(1, 4, num=10)   # number of gridpoints in time
    # define initial params
    def u_I(x):
        # initial temperature distribution
        y = np.sin(pi*x/L)
        return y
    f_kappa= lambda x : 1
    [E_BE, h_BE]=accuracy_explorer('BE', mt_a, mx, L, T, u_I, f_kappa=f_kappa)
    [E_CN, h_CN]=accuracy_explorer('CN', mt_a, mx, L, T, u_I, f_kappa=f_kappa)
    # Plot log of E vs h
    # plot the final result and exact solution
    grad_BE = E_BE[1]-E_BE[0]/h_BE[1]-h_BE[0]
    grad_CN = E_CN[1]-E_CN[0]/h_CN[1]-h_CN[0]
    print("Grad BE={}".format(grad_BE))
    print("Grad CN={}".format(grad_CN))
    print("Grad diff={}".format(grad_CN/grad_BE))
    plt.plot(h_BE, E_BE, 'g--', label='BE')
    plt.plot(h_CN, E_CN, 'b:', label='CN')
    plt.xlabel('dT')
    plt.ylabel('E (T=0.5)')
    plt.legend(loc='upper right')
    plt.yscale('log')
    plt.xscale('log')
    plt.title('Wave Equation Error Truncation Analysis')
    plt.show()

    # Explore accuracy with change of mt of the separate methods on the heat equation
    L=100        # length of spatial domain
    T=0.7       # total time to solve for
    # set numerical parameters
    mx = 1e3     # number of gridpoints in space
    mt_a = np.logspace(1, 4, num=10)   # number of gridpoints in time
    # define initial params
    def u_I(x):
        # initial temperature distribution
        y = np.sin(2*pi*x/L)
        return y
    grad_BE = E_BE[3]-E_BE[0]/h_BE[3]-h_BE[0]
    grad_CN = E_CN[3]-E_CN[0]/h_CN[3]-h_CN[0]
    print("Grad BE={}".format(grad_BE))
    print("Grad CN={}".format(grad_CN))
    print("Grad diff={}".format(grad_CN/grad_BE))
    [E_BE, h_BE]=accuracy_explorer('BE', mt_a, mx, L, T, u_I)
    [E_CN, h_CN]=accuracy_explorer('CN', mt_a, mx, L, T, u_I)
    # Plot log of E vs h
    # plot the final result and exact solution
    plt.plot(h_BE, E_BE, 'g--', label='BE')
    plt.plot(h_CN, E_CN, 'b:', label='CN')
    plt.xlabel('dT')
    plt.ylabel('E (T=0.5)')
    plt.legend(loc='upper right')
    plt.yscale('log')
    plt.xscale('log')
    plt.title('Wave Equation Error Truncation Analysis')
    plt.show()


# Finding roots of the polynomial using bisection and newtons's method
import numpy
import matplotlib

Nsteps = 50 ## we will run a fixed number of steps instead of testing for an error tolerance (step 2)
a = -10
b = 10

def f(x):
    return x**3 -9*x**2 +11*x-11

def bisection(a,b,Nsteps):
    x = zeros(Nsteps)
    for j in arange(Nsteps):
        c = (a + b)/2.
        x[j] = c
        if f(c)*f(b) < 0:
            a = c
        else:
            b = c
        if f(c) == 0.: ## this is for the unlikely event that we find the exact root
            break
    return c

def fprime(x):
    return 3*x**2 -18*x +11

def newton(Nsteps,x0 = bisection(a,b,Nsteps)):
    x = zeros(Nsteps)
    x[0] = x0
    for j in arange(Nsteps-1):
        x[j+1] = x[j] - f(x[j])/fprime(x[j])
        if abs(x[j+1] - x[j]) < 1e-6:
            print("Root of the polynomial :",x[j+1])
            break

newton(Nsteps)


# Program 21a: The Hodgkin-Huxley Equations.
# See Figures 21.2 and 21.3.

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint

# Constants
C_m = 1.0  # uF/cm^2
g_Na = 120.0  # mS/cm^2
g_K = 36.0
g_L = 0.3
V_Na = 50.0  # mV
V_K = -77.0
V_L = -54.402

# See equations (21.4)
def alpha_m(V): return 0.1 * (V + 40.0) / (1.0 - np.exp(-0.1 * (V + 40.0)))
def beta_m(V):  return 4.0 * np.exp(-0.0556 * (V + 65.0))
def alpha_h(V): return 0.07 * np.exp(-0.05 * (V + 65.0))
def beta_h(V):  return 1.0 / (1.0 + np.exp(-0.1 * (V + 35.0)))
def alpha_n(V): return 0.01 * (V + 55.0) / (1.0 - np.exp(-0.1 * (V + 55.0)))
def beta_n(V):  return 0.125 * np.exp(-0.0125 * (V + 65))

# See equation (21.2)
def I_Na(V,m,h): return g_Na * m**3 * h * (V - V_Na)
def I_K(V, n): return g_K * n**4 * (V - V_K)
def I_L(V): return g_L * (V - V_L)

# Input current
def Input_current(t): return 10 * (t > 100) - 10 * (t > 200) + 25 * (t > 300)

t = np.arange(0.0, 400.0, 0.1)

# Set up the ODEs, see equations (21.3)
def hodgkin_huxley(X, t):
    V, m, h, n = X
    dVdt = (Input_current(t) - I_Na(V, m, h) - I_K(V, n) - I_L(V)) / C_m
    dmdt = alpha_m(V) * (1.0 - m) - beta_m(V) * m
    dhdt = alpha_h(V) * (1.0 - h) - beta_h(V) * h
    dndt = alpha_n(V) * (1.0 - n) - beta_n(V) * n
    return (dVdt, dmdt, dhdt, dndt)

y0 = [-65, 0.05, 0.6, 0.32]
X = odeint(hodgkin_huxley, y0, t)
V = X[:, 0]
m = X[:, 1]
h = X[:, 2]
n = X[:, 3]
ina = I_Na(V, m, h)
ik = I_K(V, n)
il = I_L(V)

plt.subplots_adjust(hspace = 1)
plt.figure(1)

plt.subplot(5, 1, 1)
plt.title('Hodgkin-Huxley Neuron')
plt.plot(t, V, 'b')
plt.ylabel('V (mV)')

plt.subplot(5, 1, 2)
plt.plot(t, m, 'k')
plt.ylabel('m(V)')

plt.subplot(5, 1, 3)
plt.plot(t, h, 'r')
plt.ylim(0, 1)
plt.ylabel('h(V)')

plt.subplot(5, 1, 4)
plt.plot(t, n, 'g')
plt.ylim(0, 1)
plt.ylabel('n(V)')

plt.subplot(5, 1, 5)
plt.plot(t, Input_current(t), 'm')
plt.ylabel('Input current')
plt.xlabel('Time (ms)')
plt.ylim(-1, 31)

plt.show()


import numpy as np
np.set_printoptions(precision=3)
given_matrix = np.array([[11,     7,      3,      7],
                         [7,      10,    -1,     4],
                         [3,      -1,     16,     -7],
                         [7,      4,      -7,     15]])
eps = 0.01

def find_max_own_value(matrix):
    current_ly = 0
    prev_ly = -1

    x = np.array([1, 1, 1, 1])

    while abs(current_ly - prev_ly) > eps:
        prev_ly = current_ly
        e = np.divide(x, np.linalg.norm(x, ord=np.inf))
        # print("e = ", e)
        x = np.dot(matrix, e)
        # print("x = ", x)
        current_ly = np.dot(x, e) / np.dot(e, e)
        # print("current_ly = ", current_ly)

    return current_ly

ly_a_max = find_max_own_value(given_matrix)
b = np.dot(ly_a_max, np.eye(4)) - given_matrix
ly_b_max = find_max_own_value(b)
ly_a_min = ly_a_max - ly_b_max

print("max = ", ly_a_max)
print("min = ", ly_a_min)


from math import e
from numbers import Number
from typing import List, Union

from numpy import exp, max, maximum, ndarray, ones, sum, zeros

from nujo.autodiff.function import Function
from nujo.autodiff.tensor import Tensor

__all__ = [
    '_BinaryStep',
    '_Sigmoid',
    '_TanH',
    '_ReLU',
    '_LeakyReLU',
    '_Swish',
    '_Softmax',
]

# ====================================================================================================
# Built-in Neural Network Activation Functions
#  - efficient implementation of various neural activation functions
# ====================================================================================================

class _BinaryStep(Function):
    def __init__(self,
                 input: Union[Tensor, ndarray, List[Number], Number],
                 threshold=0.5):

        super(_BinaryStep, self).__init__(input)
        self.threshold = threshold

    def forward(self) -> ndarray:
        output = zeros(self.children[0].shape)
        output[self.children[0].value > self.threshold] = 1
        return output

    def backward(self, idx: int, accum_grad: Function.T) -> Function.T:
        return accum_grad * zeros(self.children[0].shape)

# ====================================================================================================

class _Sigmoid(Function):
    def __init__(self, input: Union[Tensor, ndarray, List[Number], Number]):
        super(_Sigmoid, self).__init__(input)
        self._output: ndarray = None  # Used to compute the derivative

    def forward(self) -> ndarray:
        self._output = 1 / (1 + exp(-self.children[0].value))
        return self._output

    def backward(self, idx: int, accum_grad: Function.T) -> Function.T:
        return accum_grad * self._output * (1 - self._output)

# ====================================================================================================

class _TanH(Function):
    def __init__(self, input: Union[Tensor, ndarray, List[Number], Number]):
        super(_TanH, self).__init__(input)
        self._output: ndarray = None  # Used to compute the derivative

    def forward(self) -> ndarray:
        ''' (2 / (1 + e ^ -2x)) - 1 is equivalent to
        (e ^ x - e ^ -x) / (e ^ x + e ^ -x) it is just a
        more optimal way to compute the TanH function.

        '''

        self._output = (2 / (1 + exp(-2 * self.children[0].value))) - 1
        return self._output

    def backward(self, idx: int, accum_grad: Function.T) -> Function.T:
        return accum_grad * (1 - self._output**2)

# ====================================================================================================

class _ReLU(Function):
    def __init__(self, input: Union[Tensor, ndarray, List[Number], Number]):
        super(_ReLU, self).__init__(input)

    def forward(self) -> ndarray:
        return self.children[0].value * (self.children[0].value > 0)

    def backward(self, idx: int, accum_grad: Function.T) -> Function.T:
        return accum_grad * ones(
            self.children[0].shape) * (self.children[0].value > 0)

# ====================================================================================================

class _LeakyReLU(Function):
    def __init__(self,
                 input: Union[Tensor, ndarray, List[Number], Number],
                 eps=0.1):

        super(_LeakyReLU, self).__init__(input)
        self.eps = eps

    def forward(self) -> ndarray:
        return maximum(self.eps * self.children[0].value,
                       self.children[0].value)

    def backward(self, idx: int, accum_grad: Function.T) -> Function.T:
        dinput = ones(self.children[0].shape)
        dinput[self.children[0].value < 0] = self.eps
        return accum_grad * dinput

# ====================================================================================================

class _Swish(Function):
    ''' More info here: https://arxiv.org/abs/1710.05941
    '''
    def __init__(self,
                 input: Union[Tensor, ndarray, List[Number], Number],
                 beta=1):

        super(_Swish, self).__init__(input)
        self.beta = beta

        # Reuse the sigmoid activation function
        self._sigmoid = _Sigmoid(beta * input.value)
        self._output: ndarray = None  # Used to compute the derivative

    def forward(self) -> ndarray:
        self._output = self.children[0].value * self._sigmoid.forward()
        return self._output

    def backward(self, idx: int, accum_grad: Function.T) -> Function.T:
        return accum_grad * (self._output + self._sigmoid._output *
                             (1 - self._output))

# ====================================================================================================

class _Softmax(Function):
    ''' More info here:
    https://aimatters.wordpress.com/2019/06/17/the-softmax-function-derivative/

    '''
    def __init__(self,
                 input: Union[Tensor, ndarray, List[Number], Number],
                 dim=0,
                 base: float = e):

        super(_Softmax, self).__init__(input)

        self.dim = dim
        self.base = base

        self._output: ndarray = None  # Used to compute the derivative

    def forward(self) -> ndarray:
        # The max element of the input vector will be
        # substracted from the inputs for numerical stability.
        # This will not change the relative output of the softmax.

        exps = self.base**(
            self.children[0].value -
            max(self.children[0].value, axis=self.dim, keepdims=True))

        sums = sum(exps, axis=self.dim, keepdims=True)

        self._output = exps / sums
        return self._output

    def backward(self, idx: int, accum_grad: Function.T) -> Function.T:
        return accum_grad * self._output * (1 - self._output)

# ====================================================================================================


import numpy as np

from .abstract_class import FeatureScalingAbstractClass

class StandardScore(FeatureScalingAbstractClass):

    __slots__ = ["_mean", "_std"]

    def __init__(self):
        self._mean = None
        self._std = None

    def __str__(self):
        return "Standard Score Method"

    def __repr__(self):
        return f"{self.__class__.__name__}()"

    def _set_scaling_attributes(self, feature) -> None:
        feature = np.array(feature)
        self._mean = feature.mean()
        self._std = feature.std()

    def _scaling_function(self, xi):
        return (xi - self._mean) / self._std

    def _unscaling_function(self, xi):
        return (self._std * xi) + self._mean


# import packages: numpy, math (you might need pi for gaussian functions)
import numpy as np
import math
import matplotlib.pyplot as plt
from scipy.signal import convolve2d as conv2

"""
Gaussian function taking as argument the standard deviation sigma
The filter should be defined for all integer values x in the range [-3sigma,3sigma]
The function should return the Gaussian values Gx computed at the indexes x
"""
def gauss(sigma):
    sig2=sigma**2

    x=list(map(round,np.arange(-3*(sig2), 3*(sig2)+1))) #define the range

    Gx= np.array([1/(sigma*np.sqrt(2*np.pi))*np.exp(-0.5*((el*el)/sig2)) for el in x]) #find gaussian values

    return Gx, x

"""
Implement a 2D Gaussian filter, leveraging the previous gauss.
Implement the filter from scratch or leverage the convolve2D method (scipy.signal)
Leverage the separability of Gaussian filtering
Input: image, sigma (standard deviation)
Output: smoothed image
"""
def gaussianfilter(img, sigma):

    [Gx, pos] = gauss(sigma) #get gaussian kernel

    smooth_img=np.apply_along_axis(func1d=lambda x: np.convolve(x, Gx, "same"),axis=0, arr=img) #apply to all rows
    smooth_img=np.apply_along_axis(func1d=lambda x: np.convolve(x, Gx, "same"),axis=1, arr=smooth_img) #apply to all columns

    return smooth_img

"""
Gaussian derivative function taking as argument the standard deviation sigma
The filter should be defined for all integer values x in the range [-3sigma,3sigma]
The function should return the Gaussian derivative values Dx computed at the indexes x
"""
def gaussdx(sigma):

    sig2=sigma**2
    sig3=sigma**3

    x=list(map(round,np.arange(-3*(sig2), 3*(sig2)+1))) #define the range

    Dx= np.array([-el/(sig3*np.sqrt(2*np.pi))* (np.exp(-((el*el)/(2*sig2)))) for el in x]) #find derivative gaussian values

    return Dx, x

def gaussderiv(img, sigma):

    [Dx, pos] = gaussdx(sigma) #get gaussian kernel

    imgDx=np.apply_along_axis(func1d=lambda x: np.convolve(x, Dx, "same"),axis=0, arr=img) #apply to all rows
    imgDy=np.apply_along_axis(func1d=lambda x: np.convolve(x, Dx, "same"),axis=1, arr=img) #apply to all columns

    return imgDx, imgDy


# Least squares interpolation test

from math import sin
from random import uniform

from least_squares import least_squares
import matplotlib.pyplot as plt
import numpy as np

# Interpolation interval
xmin = -2*np.pi
xmax =  2*np.pi
# Function
f = lambda x: sin(x)
# f = lambda x: 4*x
# Number of samples
s = 50
# Interpolating polynomial degree
d = 4

# Sample points
x = np.linspace(xmin, xmax, s)
y = np.asarray([f(xi) + uniform(-0.7, 0.7) for xi in x ])
# Query points
X = np.linspace(xmin, xmax, 100)
# Evaluation
Y = least_squares(x, y, d, X)

# Plot results
# Plot the result
plt.scatter(x, y, linewidths=0.2, marker='o', color='orange')
plt.plot(X, Y, linewidth=1)
plt.legend(['deg={}'.format(d)])
plt.show()


# source: https://stackoverflow.com/questions/494594

from math import sqrt

def fib_v1(n):
    if   n == 0:return 0
    elif n == 1: return 1
    return fib_v1(n-1)+fib_v1(n-2)

def fib_v2(n):
    return int(((1+sqrt(5))**n-(1-sqrt(5))**n)/(2**n*sqrt(5)))

fibs = fib_v1, fib_v2 #, fib_v3
nums = range(5)

res = [ [ f(n) for n in nums ] for f in fibs ]

# from pprint import *
# pprint(res)

import numpy as np

print(np.array(res).T)


# Extended Euclidean algorithm

import numpy as np

def Euclide(a, b):
    '''
    ax + by = GCD(a, b)
    :return: GCD(a, b), x, y
    '''
    u = np.array([1, 0, a])
    v = np.array([0, 1, b])
    while(v[2]!= 0):
        q = u[2] // v[2]
        u, v = v, u - q * v
    return u[2], u[0], u[1]

def moduloInverse(a, mod):
    return Euclide(a, mod)[1] % mod


import numpy as np
import matplotlib.pyplot as plt
import pyprobml_utils as pml

x = np.linspace(0,1,10000)

H = lambda x: -(x*np.log2(x) + (1-x) * np.log2(1-x))

plt.plot(x, H(x), '-b', lw=3)
plt.xlim((-0.01, 1.01))
plt.ylim((0, 1.01))

plt.xlabel("p(X = 1)")
plt.ylabel("H(X)")

ticks = [0, 0.5, 1]
plt.xticks(ticks)
plt.yticks(ticks)

pml.savefig("bernoulliEntropy.pdf")
plt.show()


#!/usr/bin/env python3

from math import exp
import numpy as np
import matplotlib.pyplot as plt
import logging

from ..utils.modelargs import parse_args

logging.basicConfig(level = logging.INFO)
logger = logging.getLogger(__name__)

# Susceptible -> Infected -> Susceptible model
class SIS:

    def __init__(self, N, lam, mu, gamma, I0, tau):
        logger.info('Intializing SIS model ...')
        self.N = N # Total population (assumed constant)
        self.lam = lam # Daily contact rate (for adequate contact)
        self.mu = mu # Proportionality constant of daily deaths (in each class)
        self.gamma = gamma # Proportionality constant of daily recovery
        self.I0 = I0 # Initial infected population (number, not fraction)
        self.S0 = N - I0 # Initial susceptible population (number, not fraction)
        self.tau = tau # Time window in days over which to model
        self.sigma = lam * 1.0 / (gamma + mu) # contact number

    # Closed form function : Infected population (fraction)
    def i(self, t):
        lam = self.lam
        gamma = self.gamma
        mu = self.mu
        sigma = self.sigma
        I0 = self.I0

        if sigma == 1:
            return 1.0 / (lam*t + 1.0/I0)
        else:
            num = exp((gamma + mu)*(sigma -1 )*t)
            den = (exp((gamma + mu)*(sigma -1 )*t)) * (sigma * 1.0 /
                                                (sigma - 1)) + 1.0 / I0

            return num * 1.0 / den

    # Infected population (absolute number)
    def iabs(self, t):
        return self.i(t) * self.N

    # Closed form function : Susceptibile poputation (fraction)
    def s(self, t):
        return 1.0 - self.i(t)

    # Susceptible population (absolute number)
    def sabs(self, t):
        return self.s(t) * self.N

    # Plot the S and I curves
    def plot(self, absolute=False):
        logger.info('Plotting SIS model ...')
        T = np.linspace(0, self.tau, self.tau)
        N = self.N

        if absolute:
            I = [self.iabs(tim) for tim in T]
            S = [self.sabs(tim) for tim in T]
        else:
            I = [self.i(tim) for tim in T]
            S = [self.s(tim) for tim in T]

        Iarr = np.array(I)
        Sarr = np.array(S)

        fig = plt.figure()
        ax = fig.add_subplot(111)

        if absolute:
            slab = 'Susceptible Population'
            ilab = 'Infected Population'
        else:
            slab = 'Susceptible Fraction'
            ilab = 'Infected Fraction'

        ax.plot(T, Sarr, label=slab)
        ax.plot(T, Iarr, label=ilab)

        ax.set_xlabel('Days')
        if absolute:
            ax.set_ylabel('Number of People')
            ax.set_ylim(0, N+100)
        else:
            ax.set_ylabel('Fraction of Population')
            ax.set_ylim(0, 1.1)

        legend = ax.legend()

        plt.show()

def usage():
    usagestr0 = './sis.py N=<N> I0=<I0> lambda=<lambda> mu=<mu> gamma=<gamma> tau=<tau> --abs, where: \n'
    usagestr1 = 'N = total population, assumed constant \n'
    usagestr2 = 'I0 = initial number of infected people \n'
    usagestr3 = 'lambda = daily contact rate \n'
    usagestr4 = 'mu = proportionality constant of daily deaths \n'
    usagestr5 = 'gamma = proportionality constant of daily recovery \n'
    usagestr6 = 'tau = duration in days over which the model should be constructed. \n'
    usagestr7 = '--abs (optional) means a plot with absolute numbers (instead of fraction of population) is needed. \n'
    usagestr8 = 'Note: order of arguments passsed is not important. Any other argument will be ignored. \n'
    usagestr9 = '\nExample: ./sis.py N=1000 lambda=0.05 mu=0.15 gamma=0.1 I0=1 tau=30 \n'

    print(usagestr0 + usagestr1 + usagestr2 + usagestr3 + usagestr4 +
            usagestr5 + usagestr6 + usagestr7 + usagestr8 + usagestr9)

if __name__ == "__main__":

    res = parse_args()
    if res is None:
        logger.error('No argument passed.')
        usage()
        quit()

    if res == 'usage':
        usage()
        quit()

    if isinstance(res, dict):
        miss = []
        if 'N' in res:
            N = res['N']
        else:
            miss.append('N')

        if 'lambda' in res:
            lam = res['lambda']
        else:
            miss.append('lambda')

        if 'mu' in res:
            mu = res['mu']
        else:
            miss.append('mu')

        if 'gamma' in res:
            gamma = res['gamma']
        else:
            miss.append('gamma')

        if 'I0' in res:
            I0 = res['I0']
        else:
            miss.append('I0')

        if 'tau' in res:
            tau = res['tau']
        else:
            miss.append('tau')

        plotabs = False
        if '--abs' in res:
            plotabs = True

        if len(miss) > 0:
            logger.error('Some required arguments are missing: ' + ','.join(miss))
            usage()
            quit()

    model = SIS(N=N, lam=lam, mu=mu, gamma=gamma, I0=I0, tau=tau)

    if plotabs:
        model.plot(absolute=True)
    else:
        model.plot()


"""
9.4-2. The Wirehouse Lumber Company will soon begin logging eight groves of trees in the same general area. Therefore,
it must develop a system of dirt roads that makes each grove accessible from every other grove. The distance (in
miles) between every pair of groves is as follows:

                grove_1   grove_2     grove_3     grove_4     grove_5     grove_6     grove_7     grove_8
    grove_1     --        1.3         2.1         0.9         0.7         1.8         2.0         1.5
    grove_2     1.3       --          0.9         1.8         1.2         2.6         2.3         1.1
    grove_3     2.1       0.9         --          2.6         1.7         2.5         1.9         1.0
    grove_4     0.9       1.8         2.6         --          0.7         1.6         1.5         0.9
    grove_5     0.7       1.2         1.7         0.7         --          0.9         1.1         0.8
    grove_6     1.8       2.6         2.5         1.6         0.9         --          0.6         1.0
    grove_7     2.0       2.3         1.9         1.5         1.1         0.6         --          0.5
    grove_8     1.5       1.1         1.0         0.9         0.8         1.0         0.5         --

Management now wishes to determine between which pairs of groves the roads should be constructed to connect all
groves with a minimum total length of road.<br>

(a) Describe how this problem fits the network description of the minimum spanning tree problem.
(b) Use the algorithm described in Sec. 9.4 to solve the problem.

Copyright @author: S. Lei
"""

def create_neighbor_dict(unconnected_nodes, connected_nodes):
    neighbor_dict = {}
    for i in connected_nodes:
        for j in unconnected_nodes:
            try:
                distance = distance_dict[i, j]
            except:
                pass
            neighbor_dict.update({(i, j): distance})
    return neighbor_dict

def find_nearest_neighbor_edge(unconnected_nodes, connected_nodes):
    neighbor_dict = create_neighbor_dict(unconnected_nodes, connected_nodes)
    min_key = min(neighbor_dict, key=neighbor_dict.get)
    return min_key

# ********************************************************************************
# main
# ********************************************************************************

import pandas as pd
import matplotlib.pyplot as plt
import networkx as nx
from helper_functions.util import export_soln_to_csv

if __name__ == '__main__':

    # model name
    m = '9.4-2_v1'

    # read in data

    df = pd.read_csv('data_arc.csv')
    df = df[df['distance'] != 0]
    nodes = set(df['u'])
    arcs = list((t[1], t[2]) for t in df.itertuples())
    distance_dict = dict([((t.u, t.v), t.distance) for t in df.itertuples()])

    # initialize data
    unconnected_nodes = list(nodes)
    connected_nodes = []
    distance_lst = []
    edges = []
    total_distance = 0

    # initialize data
    unconnected_nodes = list(nodes)
    connected_nodes = []
    distance_lst = []
    total_distance = 0
    edges = []

    # set a starting node
    j = unconnected_nodes[0]
    connected_nodes.append(j)
    unconnected_nodes.remove(j)

    # find MSP
    while len(unconnected_nodes) != 0:
        # find the minimum distance for nodes from nodes that are connected to nodes that are not
        edge_soln = find_nearest_neighbor_edge(unconnected_nodes, connected_nodes)
        edge_distance = distance_dict[edge_soln]
        j = edge_soln[1]

        # update trackers
        distance_lst.append(edge_distance)
        total_distance = total_distance + edge_distance
        edges.append(edge_soln)
        unconnected_nodes.remove(j)
        connected_nodes.append(j)

    # print results
    print('total_distance: ', round(total_distance), 4)
    print('edges: ', edges)

    # create a Graph object
    G = nx.Graph()

    for x in arcs:
        i = x[0]
        j = x[1]
        weight = distance_dict[i, j]
        G.add_edge(i, j, weight=weight)

    # plot results
    pos = nx.spring_layout(G)
    nx.draw_networkx_nodes(G, pos, node_size=400)
    nx.draw_networkx_edges(G, pos, edgelist=edges, width=1)
    nx.draw_networkx_labels(G, pos, font_size=15, font_family='sans-serif')

    plt.axis('off')
    plt.show()

    # export solution to csv
    node_1 = [i[0] for i in edges]
    node_2 = [i[1] for i in edges]

    output_df = pd.DataFrame(
        {'node_1': node_1,
         'node_2': node_2,
         'distance': distance_lst
         })

    export_soln_to_csv(output_df, m)

9.4 (The Minimum Spanning Tree Problem)/prob_2/mod_v1.py


#!/usr/bin/env python3

# see py/1D/study/siaconv.sh  to generate input data  siaconv.txt

import numpy as np
import matplotlib.pyplot as plt
from writeout import writeout

INFILE = 'convergence.txt'
MARKER = ['o','s','D']
MARKERSIZE = [10.0,9.0,8.0]
MARKERFACE = ['w','w','k']
NAME = ['$|s-s_{exact}|_1$','$|s-s_{exact}|_2$','$|H^r-H_{exact}^r|_p$']

print('reading %s ...' % INFILE)
v = np.array(np.loadtxt(INFILE))
#print(v)

N = 13  # number of different resolutions
assert len(v[:,0]) == N

# columns:  J m err2 errrp
L = 1800.0e3  # m
m = v[:,1]
h = L / (m+1)
h /= 1000.0   # show in km
err1 = v[:,2]
err2 = v[:,3]
errrp = v[:,4]

# numerical error versus h
plt.figure(figsize=(7,6))
q1 = np.polyfit(np.log(h),np.log(err1),1)
q2 = np.polyfit(np.log(h),np.log(err2),1)
qrp = np.polyfit(np.log(h),np.log(errrp),1)
print('|s-sexact|_1 = O(h^%.2f), |s-sexact|_2 = O(h^%.2f), |H^r - Hexact^r|_p = O(h^%.2f)' \
      % (q1[0],q2[0],qrp[0]))
plt.loglog(h,err1,
           'k'+MARKER[0],ms=MARKERSIZE[0],mfc=MARKERFACE[0],
           label=NAME[0] + '$=O(h^{%.2f})$' % q1[0])
plt.loglog(h,np.exp(q1[0]*np.log(h)+q1[1]),'k--')
plt.loglog(h,err2,
           'k'+MARKER[1],ms=MARKERSIZE[1],mfc=MARKERFACE[1],
           label=NAME[1] + '$=O(h^{%.2f})$' % q2[0])
plt.loglog(h,np.exp(q2[0]*np.log(h)+q2[1]),'k--')
plt.loglog(h,errrp,
           'k'+MARKER[2],ms=MARKERSIZE[2],mfc=MARKERFACE[2],
           label=NAME[2] + '$=O(h^{%.2f})$' % qrp[0])
plt.loglog(h,np.exp(qrp[0]*np.log(h)+qrp[1]),'k--')
plt.grid(True)
plt.axis([1.0e-2,1.0e3,1.0e2,1.0e12])
plt.xlabel('$h$  (km)',fontsize=18.0)
plt.ylabel('numerical error',fontsize=18.0)
plt.xticks(fontsize=14.0)
plt.yticks(fontsize=14.0)
plt.minorticks_off()
plt.legend(loc='upper left',fontsize=14.0)
writeout('convergence.pdf')


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

# In[1]:

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

sns.set()

# In[2]:

df = pd.read_csv("TSLA.csv")
df.head()

# In[3]:

returns = df.Close.pct_change()
volatility = returns.std()

# In[4]:

number_simulation = 100
predict_day = 30
results = pd.DataFrame()

for i in range(number_simulation):
    prices = []
    prices.append(df.Close.iloc[-1])
    for d in range(predict_day):
        prices.append(prices[d] * (1 + np.random.normal(0, volatility)))
    results[i] = pd.Series(prices).values

# In[5]:

plt.figure(figsize=(10, 5))
plt.plot(results)
plt.ylabel("Value")
plt.xlabel("Simulated days")
plt.show()

# In[6]:

raveled = results.values.ravel()
raveled.sort()
cp_raveled = raveled.copy()

plt.figure(figsize=(17, 5))
plt.subplot(1, 3, 1)
sns.distplot(raveled, norm_hist=True)
plt.xlabel("Close")
plt.ylabel("probability")
plt.title("$\mu$ = %.2f, $\sigma$ = %.2f" % (raveled.mean(), raveled.std()))
plt.subplot(1, 3, 2)
sns.distplot(df.Close, norm_hist=True)
plt.title("$\mu$ = %.2f, $\sigma$ = %.2f" % (df.Close.mean(), df.Close.std()))
plt.subplot(1, 3, 3)
sns.distplot(raveled, norm_hist=True, label="monte carlo samples")
sns.distplot(df.Close, norm_hist=True, label="real samples")
plt.legend()
plt.show()

# In[7]:

returns_volume = df.Volume.pct_change()
std = returns_volume.std()
variance = std ** 2

gaussian_2d = (1 / (2 * np.pi * variance)) * np.exp(
    -1 * ((returns_volume ** 2 + returns ** 2) / (2 * variance))
).std()

print(volatility, gaussian_2d)

# In[8]:

for i in range(number_simulation):
    prices = []
    prices.append(df.Close.iloc[-1])
    for d in range(predict_day):
        prices.append(prices[d] * (1 + np.random.normal(0, gaussian_2d)))
    results[i] = pd.Series(prices).values

# In[9]:

plt.figure(figsize=(10, 5))
plt.plot(results)
plt.ylabel("Value")
plt.xlabel("Simulated days")
plt.show()

# In[10]:

raveled = results.values.ravel()
raveled.sort()

plt.figure(figsize=(17, 5))
plt.subplot(1, 3, 1)
sns.distplot(raveled, norm_hist=True)
plt.xlabel("Close")
plt.ylabel("probability")
plt.title("$\mu$ = %.2f, $\sigma$ = %.2f" % (raveled.mean(), raveled.std()))
plt.subplot(1, 3, 2)
sns.distplot(df.Close, norm_hist=True)
plt.title("$\mu$ = %.2f, $\sigma$ = %.2f" % (df.Close.mean(), df.Close.std()))
plt.subplot(1, 3, 3)
sns.distplot(raveled, norm_hist=True, label="monte carlo samples")
sns.distplot(df.Close, norm_hist=True, label="real samples")
plt.legend()
plt.show()

# In[11]:

def pct_change(x, period=1):
    x = np.array(x)
    return (x[period:] - x[:-period]) / x[:-period]

# In[12]:

results = pd.DataFrame()

for i in range(number_simulation):
    prices = df.Close.values[-predict_day:].tolist()
    volatility = pct_change(prices[-predict_day:]).std()
    for d in range(predict_day):
        prices.append(prices[-1] * (1 + np.random.normal(0, volatility)))
        volatility = pct_change(prices[-predict_day:]).std()
    results[i] = pd.Series(prices[-predict_day:]).values

# In[13]:

plt.figure(figsize=(10, 5))
plt.plot(results)
plt.ylabel("Value")
plt.xlabel("Simulated days")
plt.show()

# In[14]:

raveled = results.values.ravel()
raveled.sort()

plt.figure(figsize=(17, 5))
plt.subplot(1, 3, 1)
sns.distplot(raveled, norm_hist=True, label="monte carlo samples")
sns.distplot(cp_raveled, norm_hist=True, label="constant volatility samples")
plt.legend()
plt.xlabel("Close")
plt.ylabel("probability")
plt.title("$\mu$ = %.2f, $\sigma$ = %.2f" % (raveled.mean(), raveled.std()))
plt.subplot(1, 3, 2)
sns.distplot(df.Close, norm_hist=True)
plt.title("$\mu$ = %.2f, $\sigma$ = %.2f" % (df.Close.mean(), df.Close.std()))
plt.subplot(1, 3, 3)
sns.distplot(raveled, norm_hist=True, label="monte carlo samples")
sns.distplot(df.Close, norm_hist=True, label="real samples")
sns.distplot(cp_raveled, norm_hist=True, label="constant volatility samples")
plt.legend()
plt.show()

# In[ ]:


#!/usr/bin/env python
"""
Functions for solving a 1D diffusion equations of simplest types
(constant coefficient, no source term):

      u_t = a*u_xx on (0,L)

with boundary conditions u=0 on x=0,L, for t in (0,T].
Initial condition: u(x,0)=I(x).

The following naming convention of variables are used.

===== ==========================================================
Name  Description
===== ==========================================================
Nx    The total number of mesh cells; mesh points are numbered
      from 0 to Nx.
F     The dimensionless number a*dt/dx**2, which implicitly
      specifies the time step.
T     The stop time for the simulation.
I     Initial condition (Python function of x).
a     Variable coefficient (constant).
L     Length of the domain ([0,L]).
x     Mesh points in space.
t     Mesh points in time.
n     Index counter in time.
u     Unknown at current/new time level.
u_n   u at the previous time level.
dx    Constant mesh spacing in x.
dt    Constant mesh spacing in t.
===== ==========================================================

user_action is a function of (u, x, t, n), u[i] is the solution at
spatial mesh point x[i] at time t[n], where the calling code
can add visualization, error computations, data analysis,
store solutions, etc.
"""
from scipy.sparse import spdiags
from scipy.sparse.linalg import spsolve, use_solver

def solver_FE_simple(I, a, L, Nx, F, T):
    """
    Simplest expression of the computational algorithm
    using the Forward Euler method and explicit Python loops.
    For this method F <= 0.5 for stability.
    """
    x = linspace(0, L, Nx+1)   # mesh points in space
    dx = x[1] - x[0]
    dt = F*dx**2/a
    Nt = int(round(T/float(dt)))
    t = linspace(0, T, Nt+1)   # mesh points in time
    u   = zeros(Nx+1)
    u_n = zeros(Nx+1)

    # Set initial condition u(x,0) = I(x)
    for i in range(0, Nx+1):
        u_n[i] = I(x[i])

    for n in range(0, Nt):
        # Compute u at inner mesh points
        for i in range(1, Nx):
            u[i] = u_n[i] + F*(u_n[i-1] - 2*u_n[i] + u_n[i+1])

        # Insert boundary conditions
        u[0] = 0;  u[Nx] = 0

        # Switch variables before next step
        u_n, u = u, u_n
    return u

def solver_FE(I, a, L, Nx, F, T,
              user_action=None, version='scalar'):
    """
    Vectorized implementation of solver_FE_simple.
    """
    import time
    t0 = time.clock()

    x = linspace(0, L, Nx+1)   # mesh points in space
    dx = x[1] - x[0]
    dt = F*dx**2/a
    Nt = int(round(T/float(dt)))
    t = linspace(0, T, Nt+1)   # mesh points in time

    u   = zeros(Nx+1)   # solution array
    u_n = zeros(Nx+1)   # solution at t-dt
    u_2 = zeros(Nx+1)   # solution at t-2*dt

    # Set initial condition
    for i in range(0,Nx+1):
        u_n[i] = I(x[i])

    if user_action is not None:
        user_action(u_n, x, t, 0)

    for n in range(0, Nt):
        # Update all inner points
        if version == 'scalar':
            for i in range(1, Nx):
                u[i] = u_n[i] + F*(u_n[i-1] - 2*u_n[i] + u_n[i+1])

        elif version == 'vectorized':
            u[1:Nx] = u_n[1:Nx] +  \
                      F*(u_n[0:Nx-1] - 2*u_n[1:Nx] + u_n[2:Nx+1])
        else:
            raise ValueError('version=%s' % version)

        # Insert boundary conditions
        u[0] = 0;  u[Nx] = 0
        if user_action is not None:
            user_action(u, x, t, n+1)

        # Switch variables before next step
        #u_n[:] = u  # slow
        u_n, u = u, u_n

    t1 = time.clock()
    return u, x, t, t1-t0

def solver_BE_simple(I, a, L, Nx, F, T):
    """
    Simplest expression of the computational algorithm
    for the Backward Euler method, using explicit Python loops
    and a dense matrix format for the coefficient matrix.
    """
    x = linspace(0, L, Nx+1)   # mesh points in space
    dx = x[1] - x[0]
    dt = F*dx**2/a
    Nt = int(round(T/float(dt)))
    t = linspace(0, T, Nt+1)   # mesh points in time
    u   = zeros(Nx+1)
    u_n = zeros(Nx+1)

    # Data structures for the linear system
    A = zeros((Nx+1, Nx+1))
    b = zeros(Nx+1)

    for i in range(1, Nx):
        A[i,i-1] = -F
        A[i,i+1] = -F
        A[i,i] = 1 + 2*F
    A[0,0] = A[Nx,Nx] = 1

    # Set initial condition u(x,0) = I(x)
    for i in range(0, Nx+1):
        u_n[i] = I(x[i])

    for n in range(0, Nt):
        # Compute b and solve linear system
        for i in range(1, Nx):
            b[i] = -u_n[i]
        b[0] = b[Nx] = 0
        u[:] = linalg.solve(A, b)

        # Switch variables before next step
        u_n, u = u, u_n
    return u

def solver_BE(I, a, L, Nx, F, T, user_action=None):
    """
    Vectorized implementation of solver_BE_simple using also
    a sparse (tridiagonal) matrix for efficiency.
    """
    import time
    t0 = time.clock()

    x = linspace(0, L, Nx+1)   # mesh points in space
    dx = x[1] - x[0]
    dt = F*dx**2/a
    Nt = int(round(T/float(dt)))
    t = linspace(0, T, Nt+1)   # mesh points in time

    u   = zeros(Nx+1)   # solution array at t[n+1]
    u_n = zeros(Nx+1)   # solution at t[n]

    # Representation of sparse matrix and right-hand side
    diagonal = zeros(Nx+1)
    lower    = zeros(Nx+1)
    upper    = zeros(Nx+1)
    b        = zeros(Nx+1)
    # "Active" values: diagonal[:], upper[1:], lower[:-1]

    # Precompute sparse matrix
    diagonal[:] = 1 + 2*F
    lower[:] = -F  #1
    upper[:] = -F  #1
    # Insert boundary conditions
    diagonal[0] = 1
    diagonal[Nx] = 1
    # Remove unused/inactive values
    upper[0:2] = 0
    lower[-2:] = 0

    diags = [0, -1, 1]
    A = spdiags([diagonal, lower, upper], diags, Nx+1, Nx+1)
    print(A.todense())

    # Set initial condition
    for i in range(0,Nx+1):
        u_n[i] = I(x[i])

    if user_action is not None:
        user_action(u_n, x, t, 0)

    for n in range(0, Nt):
        b = u_n
        b[0] = b[-1] = 0.0  # boundary conditions
        u[:] = spsolve(A, b)

        if user_action is not None:
            user_action(u, x, t, n+1)

        # Switch variables before next step
        u_n, u = u, u_n

    t1 = time.clock()
    return u, x, t, t1-t0

def solver_theta(I, a, L, Nx, F, T, theta=0.5, u_L=0, u_R=0,
                 user_action=None):
    """
    Full solver for the model problem using the theta-rule
    difference approximation in time (no restriction on F,
    i.e., the time step when theta >= 0.5).
    Vectorized implementation and sparse (tridiagonal)
    coefficient matrix.
    """
    import time
    t0 = time.clock()

    x = linspace(0, L, Nx+1)   # mesh points in space
    dx = x[1] - x[0]
    dt = F*dx**2/a
    Nt = int(round(T/float(dt)))
    t = linspace(0, T, Nt+1)   # mesh points in time

    u   = zeros(Nx+1)   # solution array at t[n+1]
    u_n = zeros(Nx+1)   # solution at t[n]

    # Representation of sparse matrix and right-hand side
    diagonal = zeros(Nx+1)
    lower    = zeros(Nx+1)
    upper    = zeros(Nx+1)
    b        = zeros(Nx+1)
    # "Active" values: diagonal[:], upper[1:], lower[:-1]

    # Precompute sparse matrix (scipy format)
    diagonal[:] = 1 + 2*Fl
    lower[:] = -Fl  #1
    upper[:] = -Fl  #1
    # Insert boundary conditions
    diagonal[0] = 1
    diagonal[Nx] = 1
    # Remove unused/inactive values
    upper[0:2] = 0
    lower[-2:] = 0

    diags = [0, -1, 1]
    A = spdiags([diagonal, lower, upper], diags, Nx+1, Nx+1)
    #print A.todense()

    # Set initial condition
    for i in range(0,Nx+1):
        u_n[i] = I(x[i])

    if user_action is not None:
        user_action(u_n, x, t, 0)

    # Time loop
    for n in range(0, Nt):
        b[1:-1] = u_n[1:-1] + Fr*(u_n[:-2] - 2*u_n[1:-1] + u_n[2:])
        b[0] = u_L; b[-1] = u_R  # boundary conditions
        u[:] = spsolve(A, b)

        if user_action is not None:
            user_action(u, x, t, n+1)

        # Switch variables before next step
        u_n, u = u, u_n

    t1 = time.clock()
    return u, x, t, t1-t0

def viz(I, a, L, Nx, F, T, umin, umax,
        scheme='FE', animate=True):

    def plot_u(u, x, t, n):
        plot(x, u, 'r-', axis=[0, L, umin, umax], title='t=%f' % t[n])
        if t[n] == 0:
            time.sleep(2)
        else:
            time.sleep(0.2)

    user_action = plot_u if animate else lambda u,x,t,n: None

    u, x, t, cpu = eval('solver_'+scheme)\
                   (I, a, L, Nx, F, T,
                    user_action=user_action)
    return u, cpu

def plug(scheme='FE', F=0.5, Nx=50):
    L = 1.
    a = 1
    T = 0.1

    def I(x):
        """Plug profile as initial condition."""
        if abs(x-L/2.0) > 0.1:
            return 0
        else:
            return 1

    u, cpu = viz(I, a, L, Nx, F, T,
                 umin=-0.1, umax=1.1,
                 scheme=scheme, animate=True)
    print('CPU time:', cpu)

    """
    if not allclose(solutions[0], solutions[-1],
                    atol=1.0E-10, rtol=1.0E-12):
        print 'error in computations'
    else:
        print 'correct solution'
    """

def expsin(scheme='FE', F=0.5, m=3):
    L = 10.0
    a = 1
    T = 1.2

    def exact(x, t):
        return exp(-m**2*pi**2*a/L**2*t)*sin(m*pi/L*x)

    def I(x):
        return exact(x, 0)

    Nx = 80
    viz(I, a, L, Nx, F, T, -1, 1, scheme=scheme, animate=True)

    # Convergence study
    def action(u, x, t, n):
        e = abs(u - exact(x, t[n])).max()
        errors.append(e)

    errors = []
    Nx_values = [10, 20, 40, 80, 160]
    for Nx in Nx_values:
        eval('solver_'+scheme)(I, a, L, Nx, F, T, user_action=action)
        dt = F*(L/Nx)**2/a
        print(dt, errors[-1])

if __name__ == '__main__':
    import sys
    import time
    if len(sys.argv) < 2:
        print("""Usage %s function arg1 arg2 arg3 ...""" % sys.argv[0])
        sys.exit(0)
    cmd = '%s(%s)' % (sys.argv[1], ', '.join(sys.argv[2:]))
    print(cmd)
    eval(cmd)


"""Example implementation of the Coordinate Descent algorithm in Python

- The minimum value of F(x) is computed iteratively by minimizing individual coordinates in a cyclic manner
- On each iteration the active coordinate is updated as follows

  X_k+1 = X_k - alpha * [del_F(xk)] * e

  Where
  * alpha : step length,
  * e : the active co-ordinate vector(required to make vector addition to Xk possible),
  * del_F(xk) : component of the gradient corresponding to the active coordinate
"""

import logging
import functools
import numpy as np
import math

logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)

__author__ = "@NdamuleloNemakh"
__version__ = 0.01

class CoordinateDescent:
    def __init__(self, max_iterations=500, no_of_dimensions=2, step_length=0.1):
        self.Dimensions = no_of_dimensions
        self.StepLength = step_length
        self.MaxIterations = max_iterations
        self.CurrentIteration = 0
        self.Minimiser: np.ndarray = np.array([0, 0])

    def __preview_config(self):
        configs = f'''
                   Configuration Preview
                   ========================
                   n              = {self.Dimensions}
                   Step Length    = {self.StepLength}
                   Max Iterations = {self.MaxIterations}
                   '''
        log.debug(configs)
        return configs

    @property
    def _current_coordinate_idx(self):
        # Choose next cordinate to optimize, in a CYCLIC manner
        next_idx = lambda i: i % 2 + 1
        return next_idx(self.CurrentIteration)

    @property
    def _ith_coordinate_vector(self):
        v = np.zeros(self.Dimensions)
        active_idx = self._current_coordinate_idx - 1
        v[active_idx] = 1
        return v

    @staticmethod
    def __current_gradient(xk):
        #  Example: F(X1, X2) = X1 - X2 + 2X1**2 + 2X1X2 + X2**2
        x1 = xk[0]
        x2 = xk[1]
        del_fx1 = 1 + 4 * x1 + 2 * x2
        del_fx2 = 2 * x1 + 2 * x2 - 1
        del_f = np.array([del_fx1, del_fx2])
        return del_f

    @staticmethod
    def f(xk, decimal_places=3):
        x1 = xk[0]
        x2 = xk[1]
        fn_value = x1 - x2 + 2 * math.pow(x1, 2) + 2 * x1 * x2 * math.pow(x2, 2)
        return round(fn_value, decimal_places)

    @property
    def __current_gradient_value(self):
        del_f = self.__current_gradient(self.Minimiser)
        sqaured_sum = functools.reduce(lambda a, b: a ** 2 + b ** 2, del_f)
        return math.sqrt(sqaured_sum)

    @property
    def __current_coordinate_gradient(self):
        del_f = self.__current_gradient(self.Minimiser)
        return del_f[self._current_coordinate_idx - 1]

    def __find_next_candidate(self):
        """Compute the next potential minimiser point, i.e. Xk+1"""
        xk = self.Minimiser
        del_f = self.__current_coordinate_gradient
        xk_plus_1 = xk - self.StepLength * del_f * self._ith_coordinate_vector
        log.debug(f"X**{self.CurrentIteration} = {xk} - {self.StepLength} * {del_f} * {self._ith_coordinate_vector}")
        return xk_plus_1

    def execute(self):
        log.info('Coordinate descent iteration started')
        self.__preview_config()
        for k in range(self.MaxIterations):
            log.debug(f"-----------Iteration {k}--------------")
            self.CurrentIteration = k
            self.Minimiser = self.__find_next_candidate()
            log.debug(f"------Minimiser = {self.Minimiser}. Gradient = {self.__current_gradient_value}--------\n")
        log.info(f"------Minimiser = {self.Minimiser}. Gradient = {self.__current_gradient_value}--------")
        log.info(f'Coordinate descent completed successfully. Total Iterations={self.CurrentIteration}')
        return self.Minimiser, self.f(self.Minimiser), round(self.__current_gradient_value, 3)


#! /usr/bin/env python
#
import math
import numpy as np
import random

def sphere_fibonacci_grid_points ( ng ):

  # phi = ( 1.0 + np.sqrt ( 5.0 ) ) / 2.0

  # theta = np.zeros ( ng )
  # sphi = np.zeros ( ng )
  # cphi = np.zeros ( ng )

  # for i in range ( 0, ng ):
  #   i2 = 2 * i - ( ng - 1 )
  #   theta[i] = 2.0 * np.pi * float ( i2 ) / phi
  #   sphi[i] = float ( i2 ) / float ( ng )
  #   cphi[i] = np.sqrt ( float ( ng + i2 ) * float ( ng - i2 ) ) / float ( ng )

  # xg = np.zeros ( ( ng, 3 ) )

  # for i in range ( 0, ng ) :
  #   xg[i,0] = cphi[i] * np.sin ( theta[i] )
  #   xg[i,1] = cphi[i] * np.cos ( theta[i] )
  #   xg[i,2] = sphi[i]

  # return xg

  rnd = 1.
  samples  = ng
  randomize = False
  if randomize:
      rnd = random.random() * samples

  points = []
  offset = 2./samples
  increment = math.pi * (3. - math.sqrt(5.));

  for i in range(samples):
      y = ((i * offset) - 1) + (offset / 2);
      r = math.sqrt(1 - pow(y,2))

      phi = ((i + rnd) % samples) * increment

      x = math.cos(phi) * r
      z = math.sin(phi) * r

      points.append([x,y,z])

  return np.array(points)

def sphere_fibonacci_grid_points_with_sym_metric (ng, half_whole):

  if (half_whole == 1):
    rnd = 1.
    samples  = ng
    randomize = False
    if randomize:
        rnd = random.random() * samples

    points = []
    offset = 2./samples
    increment = math.pi * (3. - math.sqrt(5.));

    for i in range(samples):
        y = ((i * offset) - 1) + (offset / 2);
        r = math.sqrt(1 - pow(y,2))

        phi = ((i + rnd) % samples) * increment

        x = math.cos(phi) * r
        z = math.sin(phi) * r

        points.append([x,y,z])

    return np.array(points)
  else: # half_whole == 0
    rnd = 1.
    samples  = ng
    randomize = False
    if randomize:
        rnd = random.random() * samples

    points = []
    offset = 2./samples
    # offset = 1./samples
    increment = math.pi * (3. - math.sqrt(5.));

    # for i in range(math.ceil(samples/2)):
    for i in range(round(samples/2)):
        y = ((i * offset) - 1) + (offset / 2);
        r = math.sqrt(1 - pow(y,2))

        phi = ((i + rnd) % samples) * increment

        x = math.cos(phi) * r
        z = math.sin(phi) * r

        points.append([x,y,z])

    return np.array(points)

def sphere_fibonacci_grid_points_test ( ):

#*****************************************************************************80
#
#% SPHERE_FIBONACCI_GRID_POINTS_TEST tests SPHERE_FIBONACCI_GRID_POINTS.
#
#  Licensing:
#
#    This code is distributed under the GNU LGPL license.
#
#  Modified:
#
#    16 May 2015
#
#  Author:
#
#    John Burkardt
#
  import platform
  from r8mat_print_some import r8mat_print_some
  from r8mat_write import r8mat_write

  print ( '' )
  print ( 'SPHERE_FIBONACCI_GRID_POINTS_TEST' )
  print ( '  Python version: %s' % ( platform.python_version ( ) ) )
  print ( '  SPHERE_FIBONACCI_GRID_POINTS computes points on a sphere' )
  print ( '  that lie on a Fibonacci spiral.' )

  ng = 1000
  print ( '' )
  print ( '  Number of points NG = %d' % ( ng ) )

  xg = sphere_fibonacci_grid_points ( ng )

  r8mat_print_some ( ng, 3, xg, 0, 0, 19, 2, '  Part of the grid array:' )
#
#  Write the nodes to a file.
#
  filename = 'sphere_fibonacci_grid_points.xyz'

  r8mat_write ( filename, ng, 3, xg )
#
#  Terminate.
#
  print ( '' )
  print ( 'SPHERE_FIBONACCI_GRID_POINTS_TEST:' )
  print ( '  Normal end of execution.' )
  return

if ( __name__ == '__main__' ):
  from timestamp import timestamp
  timestamp ( )
  sphere_fibonacci_grid_points_test ( )
  timestamp ( )


from sympy import *
from sympy import symbols
from sympy.matrices.matrices import ShapeError
import py

def test_multiplication():
    a=Matrix((
        (1, 2),
        (3, 1),
        (0, 6),
        ))

    b = Matrix ((
        (1, 2),
        (3, 0),
        ))

    c= a*b
    assert c[0,0]==7
    assert c[0,1]==2
    assert c[1,0]==6
    assert c[1,1]==6
    assert c[2,0]==18
    assert c[2,1]==0

    x = Symbol("x")

    c = b * Symbol("x")
    assert isinstance(c,Matrix)
    assert c[0,0] == x
    assert c[0,1] == 2*x
    assert c[1,0] == 3*x
    assert c[1,1] == 0

    c2 = x * b
    assert c == c2

    c = 5 * b
    assert isinstance(c,Matrix)
    assert c[0,0] == 5
    assert c[0,1] == 2*5
    assert c[1,0] == 3*5
    assert c[1,1] == 0

def test_power():
    A = Matrix([[2,3],[4,5]])
    assert (A**5)[:] == [6140, 8097, 10796, 14237]
    A = Matrix([[2, 1, 3],[4,2, 4], [6,12, 1]])
    assert (A**3)[:] == [290, 262, 251, 448, 440, 368, 702, 954, 433]
    assert A**0 == eye(3)
    assert A**1 == A
    assert (Matrix([[2]]) ** 100)[0,0] == 2**100
    assert eye(2)**10000000 == eye(2)

def test_creation():
    x = Symbol("x")
    a = Matrix([x, 0], [0, 0])
    m = a
    assert m.cols == m.lines
    assert m.cols == 2
    assert m[:] == [x,0,0,0]
    b = Matrix(2,2, [x, 0, 0, 0])
    m = b
    assert m.cols == m.lines
    assert m.cols == 2
    assert m[:] == [x,0,0,0]

    assert a == b

def test_determinant():
    x, y, z = Symbol('x'), Symbol('y'), Symbol('z')

    assert Matrix([ [1] ]).det() == 1

    assert Matrix(( (-3,  2),
                    ( 8, -5) )).det() == -1

    assert Matrix(( (x,   1),
                    (y, 2*y) )).det() == 2*x*y-y

    assert Matrix(( (1, 1, 1),
                    (1, 2, 3),
                    (1, 3, 6) )).det() == 1

    assert Matrix(( ( 3, -2,  0, 5),
                    (-2,  1, -2, 2),
                    ( 0, -2,  5, 0),
                    ( 5,  0,  3, 4) )).det() == -289

    assert Matrix(( ( 1,  2,  3,  4),
                    ( 5,  6,  7,  8),
                    ( 9, 10, 11, 12),
                    (13, 14, 15, 16) )).det() == 0

    assert Matrix(( (3, 2, 0, 0, 0),
                    (0, 3, 2, 0, 0),
                    (0, 0, 3, 2, 0),
                    (0, 0, 0, 3, 2),
                    (2, 0, 0, 0, 3) )).det() == 275

    assert Matrix(( (1, 0,  1,  2, 12),
                    (2, 0,  1,  1,  4),
                    (2, 1,  1, -1,  3),
                    (3, 2, -1,  1,  8),
                    (1, 1,  1,  0,  6) )).det() == -55

    assert Matrix(( (-5,  2,  3,  4,  5),
                    ( 1, -4,  3,  4,  5),
                    ( 1,  2, -3,  4,  5),
                    ( 1,  2,  3, -2,  5),
                    ( 1,  2,  3,  4, -1) )).det() == 11664

    assert Matrix(( ( 2,  7, -1, 3, 2),
                    ( 0,  0,  1, 0, 1),
                    (-2,  0,  7, 0, 2),
                    (-3, -2,  4, 5, 3),
                    ( 1,  0,  0, 0, 1) )).det() == 123

    assert Matrix([ [x,y,z],[1,0,0],[y,z,x]] ).det() == z**2 - x*y

def test_submatrix():
    m0 = eye(4)
    assert m0[0:3, 0:3] == eye(3)
    assert m0[2:4, 0:2] == zero(2)

    m1 = Matrix(3,3, lambda i,j: i+j)
    assert m1[0,:] == Matrix(1,3,(0,1,2))
    assert m1[1:3, 1] == Matrix(2,1,(2,3))

    m2 = Matrix([0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15])
    assert m2[:,-1] == Matrix(4,1,[3,7,11,15])
    assert m2[-2:,:] == Matrix([[8,9,10,11],[12,13,14,15]])

def test_submatrix_assignment():
    m = zero(4)
    m[2:4, 2:4] = eye(2)
    assert m == Matrix((0,0,0,0),
                        (0,0,0,0),
                        (0,0,1,0),
                        (0,0,0,1))
    m[0:2, 0:2] = eye(2)
    assert m == eye(4)
    m[:,0] = Matrix(4,1,(1,2,3,4))
    assert m == Matrix((1,0,0,0),
                        (2,1,0,0),
                        (3,0,1,0),
                        (4,0,0,1))
    m[:,:] = zero(4)
    assert m == zero(4)
    m[:,:] = ((1,2,3,4),(5,6,7,8),(9, 10, 11, 12),(13,14,15,16))
    assert m == Matrix(((1,2,3,4),
                        (5,6,7,8),
                        (9, 10, 11, 12),
                        (13,14,15,16)))
    m[0:2, 0] = [0,0]
    assert m == Matrix(((0,2,3,4),
                        (0,6,7,8),
                        (9, 10, 11, 12),
                        (13,14,15,16)))

def test_reshape():
    m0 = eye(3)
    assert m0.reshape(1,9) == Matrix(1,9,(1,0,0,0,1,0,0,0,1))
    m1 = Matrix(3,4, lambda i,j: i+j)
    assert m1.reshape(4,3) == Matrix((0,1,2), (3,1,2), (3,4,2), (3,4,5))
    assert m1.reshape(2,6) == Matrix((0,1,2,3,1,2), (3,4,2,3,4,5))

def test_applyfunc():
    m0 = eye(3)
    assert m0.applyfunc(lambda x:2*x) == eye(3)*2
    assert m0.applyfunc(lambda x: 0 ) == zero(3)

def test_random():
    M = randMatrix(3,3)
    M = randMatrix(3,3,seed=3)
    M = randMatrix(3,4,0,150)

def test_LUdecomp():
    testmat = Matrix([[0,2,5,3],
                      [3,3,7,4],
                      [8,4,0,2],
                      [-2,6,3,4]])
    L,U,p = testmat.LUdecomposition()
    assert L.is_lower()
    assert U.is_upper()
    assert (L*U).permuteBkwd(p)-testmat == zero(4)

    testmat = Matrix([[6,-2,7,4],
                      [0,3,6,7],
                      [1,-2,7,4],
                      [-9,2,6,3]])
    L,U,p = testmat.LUdecomposition()
    assert L.is_lower()
    assert U.is_upper()
    assert (L*U).permuteBkwd(p)-testmat == zero(4)

    x, y, z = Symbol('x'), Symbol('y'), Symbol('z')
    M = Matrix((1, x, 1), (2, y, 0), (y, 0, z))
    L, U, p = M.LUdecomposition()
    assert L.is_lower()
    assert U.is_upper()
    assert (L*U).permuteBkwd(p)-M == zero(3)

    # test FF LUdecomp
    M = Matrix([[1, 3, 3],
                [3, 2, 6],
                [3, 2, 2]])
    P, L, Dee, U = M.LUdecompositionFF()
    assert P*M == L*Dee.inv()*U

    M = Matrix([[1, 2, 3, 4],
                 [3, -1, 2, 3],
                 [3, 1, 3, -2],
                 [6, -1, 0, 2]])
    P, L, Dee, U = M.LUdecompositionFF()
    assert P*M == L*Dee.inv()*U

def test_LUsolve():
    A = Matrix([[2,3,5],
                [3,6,2],
                [8,3,6]])
    x = Matrix(3,1,[3,7,5])
    b = A*x
    soln = A.LUsolve(b)
    assert soln == x
    A = Matrix([[0,-1,2],
                [5,10,7],
                [8,3,4]])
    x = Matrix(3,1,[-1,2,5])
    b = A*x
    soln = A.LUsolve(b)
    assert soln == x

def test_inverse():
    A = eye(4)
    assert A.inv() == eye(4)
    A = Matrix([[2,3,5],
                [3,6,2],
                [8,3,6]])
    Ainv = A.inv()
    assert A*Ainv == eye(3)
    Ainv2 = A.inv("LU")
    assert Ainv == Ainv2

def test_util():
    v1 = Matrix(1,3,[1,2,3])
    v2 = Matrix(1,3,[3,4,5])
    assert v1.cross(v2) == Matrix(1,3,[-2,4,-2])
    assert v1.norm() == sqrt(14)
    # cofactor
    assert eye(3) == eye(3).cofactorMatrix()
    test = Matrix([[1,3,2],[2,6,3],[2,3,6]])
    assert test.cofactorMatrix() == Matrix([[27,-6,-6],[-12,2,3],[-3,1,0]])
    test = Matrix([[1,2,3],[4,5,6],[7,8,9]])
    assert test.cofactorMatrix() == Matrix([[-3,6,-3],[6,-12,6],[-3,6,-3]])

def test_jacobian_hessian():
    x = Symbol('x')
    y = Symbol('y')
    L = Matrix(1,2,[x**2*y, 2*y**2 + x*y])
    syms = [x,y]
    assert L.jacobian(syms) == Matrix([[2*x*y, x**2],[y, 4*y+x]])

    L = Matrix(1,2,[x, x**2*y**3])
    assert L.jacobian(syms) == Matrix([[1, 0], [2*x*y**3, x**2*3*y**2]])

    f = x**2*y
    syms = [x,y]
    assert hessian(f, syms) == Matrix([[2*y, 2*x], [2*x, 0]])

    f = x**2*y**3
    assert hessian(f, syms) == Matrix([[2*y**3, 6*x*y**2],[6*x*y**2, 6*x**2*y]])

def test_QR():
    A = Matrix([[1,2],[2,3]])
    Q, S = A.QRdecomposition()
    R = Rational
    assert Q == Matrix([[5**R(-1,2), (R(2)/5)*(R(1)/5)**R(-1,2)], [2*5**R(-1,2), (-R(1)/5)*(R(1)/5)**R(-1,2)]])
    assert S == Matrix([[5**R(1,2), 8*5**R(-1,2)], [0, (R(1)/5)**R(1,2)]])
    assert Q*S == A
    assert Q.T * Q == eye(2)

    A = Matrix([[1,1,1],[1,1,3],[2,3,4]])
    Q, R = A.QRdecomposition()
    assert Q*Q.T == eye(Q.lines)
    assert R.is_upper()
    assert A == Q*R

def test_nullspace():
    # first test reduced row-ech form
    R = Rational

    M = Matrix([[5,7,2,1],
               [1,6,2,-1]])
    out, tmp = M.rref()
    assert out == Matrix([[1,0,-R(2)/23,R(13)/23],
                              [0,1,R(8)/23, R(-6)/23]])

    M = Matrix([[1,3,0,2,6,3,1],
                [-2,-6,0,-2,-8,3,1],
                [3,9,0,0,6,6,2],
                [-1,-3,0,1,0,9,3]])
    out, tmp = M.rref()
    assert out == Matrix([[1,3,0,0,2,0,0],
                               [0,0,0,1,2,0,0],
                               [0,0,0,0,0,1,R(1)/3],
                               [0,0,0,0,0,0,0]])
    # now check the vectors
    basis = M.nullspace()
    assert basis[0] == Matrix([-3,1,0,0,0,0,0])
    assert basis[1] == Matrix([0,0,1,0,0,0,0])
    assert basis[2] == Matrix([-2,0,0,-2,1,0,0])
    assert basis[3] == Matrix([0,0,0,0,0,R(-1)/3, 1])

def test_wronskian():
    x = Symbol('x')
    assert wronskian([cos(x), sin(x)], x) == cos(x)**2 + sin(x)**2
    assert wronskian([exp(x), exp(2*x)], x) == exp(3*x)
    assert wronskian([exp(x), x], x) == exp(x) - x*exp(x)
    assert wronskian([1, x, x**2], x) == 2
    assert wronskian([exp(x), cos(x), x**3], x).expand() == \
        -6*exp(x)*sin(x)*x + 6*cos(x)*exp(x)*x**2 - 6*exp(x)*cos(x)*x - \
        exp(x)*cos(x)*x**3 + exp(x)*sin(x)*x**3

def test_eigen():
    # test charpoly
    x = Symbol('x')
    y = Symbol('y')
    eye3 = eye(3)
    assert eye3.charpoly(x) == ((1-x)**3).expand()
    assert eye3.charpoly(y) == ((1-y)**3).expand()
    # test values
    M = Matrix([(0,1,-1),
                (1,1,0),
                (-1,0,1) ])
    vals = M.eigenvals()
    vals.sort()
    assert vals == [[-1,1],[1,1],[2,1]]
    # test vectors
    R = Rational
    M = Matrix([ [1,0,0],
                 [0,1,0],
                 [0,0,1]])
    assert M.eigenvects() == [[1, 3, [Matrix([1,0,0]), Matrix([0,1,0]), Matrix([0,0,1])]]]
    M = Matrix([ [5,0,2],
                 [3,2,0],
                 [0,0,1]])
    assert M.eigenvects() == [[1, 1, [Matrix([R(-1)/2,R(3)/2,1])]],
                              [2, 1, [Matrix([0,1,0])]],
                              [5, 1, [Matrix([1,1,0])]]]

def test_sparse_matrix():
    return
    def eye(n):
        tmp = SMatrix(n,n,lambda i,j:0)
        for i in range(tmp.lines):
            tmp[i,i] = 1
        return tmp
    def zero(n):
        return SMatrix(n,n,lambda i,j:0)

    # test_multiplication
    a=SMatrix((
        (1, 2),
        (3, 1),
        (0, 6),
        ))

    b = SMatrix ((
        (1, 2),
        (3, 0),
        ))

    c= a*b
    assert c[0,0]==7
    assert c[0,1]==2
    assert c[1,0]==6
    assert c[1,1]==6
    assert c[2,0]==18
    assert c[2,1]==0

    x = Symbol("x")

    c = b * Symbol("x")
    assert isinstance(c,SMatrix)
    assert c[0,0] == x
    assert c[0,1] == 2*x
    assert c[1,0] == 3*x
    assert c[1,1] == 0

    c = 5 * b
    assert isinstance(c,SMatrix)
    assert c[0,0] == 5
    assert c[0,1] == 2*5
    assert c[1,0] == 3*5
    assert c[1,1] == 0

    #test_power
    A = SMatrix([[2,3],[4,5]])
    assert (A**5)[:] == [6140, 8097, 10796, 14237]
    A = SMatrix([[2, 1, 3],[4,2, 4], [6,12, 1]])
    assert (A**3)[:] == [290, 262, 251, 448, 440, 368, 702, 954, 433]

    # test_creation
    x = Symbol("x")
    a = SMatrix([x, 0], [0, 0])
    m = a
    assert m.cols == m.lines
    assert m.cols == 2
    assert m[:] == [x,0,0,0]
    b = SMatrix(2,2, [x, 0, 0, 0])
    m = b
    assert m.cols == m.lines
    assert m.cols == 2
    assert m[:] == [x,0,0,0]

    assert a == b

    # test_determinant
    x, y = Symbol('x'), Symbol('y')

    assert SMatrix([ [1] ]).det() == 1

    assert SMatrix(( (-3,  2),
                    ( 8, -5) )).det() == -1

    assert SMatrix(( (x,   1),
                    (y, 2*y) )).det() == 2*x*y-y

    assert SMatrix(( (1, 1, 1),
                    (1, 2, 3),
                    (1, 3, 6) )).det() == 1

    assert SMatrix(( ( 3, -2,  0, 5),
                    (-2,  1, -2, 2),
                    ( 0, -2,  5, 0),
                    ( 5,  0,  3, 4) )).det() == -289

    assert SMatrix(( ( 1,  2,  3,  4),
                    ( 5,  6,  7,  8),
                    ( 9, 10, 11, 12),
                    (13, 14, 15, 16) )).det() == 0

    assert SMatrix(( (3, 2, 0, 0, 0),
                    (0, 3, 2, 0, 0),
                    (0, 0, 3, 2, 0),
                    (0, 0, 0, 3, 2),
                    (2, 0, 0, 0, 3) )).det() == 275

    assert SMatrix(( (1, 0,  1,  2, 12),
                    (2, 0,  1,  1,  4),
                    (2, 1,  1, -1,  3),
                    (3, 2, -1,  1,  8),
                    (1, 1,  1,  0,  6) )).det() == -55

    assert SMatrix(( (-5,  2,  3,  4,  5),
                    ( 1, -4,  3,  4,  5),
                    ( 1,  2, -3,  4,  5),
                    ( 1,  2,  3, -2,  5),
                    ( 1,  2,  3,  4, -1) )).det() == 11664

    assert SMatrix(( ( 2,  7, -1, 3, 2),
                    ( 0,  0,  1, 0, 1),
                    (-2,  0,  7, 0, 2),
                    (-3, -2,  4, 5, 3),
                    ( 1,  0,  0, 0, 1) )).det() == 123

    # test_submatrix
    m0 = eye(4)
    assert m0[0:3, 0:3] == eye(3)
    assert m0[2:4, 0:2] == zero(2)

    m1 = SMatrix(3,3, lambda i,j: i+j)
    assert m1[0,:] == SMatrix(1,3,(0,1,2))
    assert m1[1:3, 1] == SMatrix(2,1,(2,3))

    m2 = SMatrix([0,1,2,3],[4,5,6,7],[8,9,10,11],[12,13,14,15])
    assert m2[:,-1] == SMatrix(4,1,[3,7,11,15])
    assert m2[-2:,:] == SMatrix([[8,9,10,11],[12,13,14,15]])

    # test_submatrix_assignment
    m = zero(4)
    m[2:4, 2:4] = eye(2)
    assert m == SMatrix((0,0,0,0),
                        (0,0,0,0),
                        (0,0,1,0),
                        (0,0,0,1))
    m[0:2, 0:2] = eye(2)
    assert m == eye(4)
    m[:,0] = SMatrix(4,1,(1,2,3,4))
    assert m == SMatrix((1,0,0,0),
                        (2,1,0,0),
                        (3,0,1,0),
                        (4,0,0,1))
    m[:,:] = zero(4)
    assert m == zero(4)
    m[:,:] = ((1,2,3,4),(5,6,7,8),(9, 10, 11, 12),(13,14,15,16))
    assert m == SMatrix(((1,2,3,4),
                        (5,6,7,8),
                        (9, 10, 11, 12),
                        (13,14,15,16)))
    m[0:2, 0] = [0,0]
    assert m == SMatrix(((0,2,3,4),
                        (0,6,7,8),
                        (9, 10, 11, 12),
                        (13,14,15,16)))

    # test_reshape
    m0 = eye(3)
    assert m0.reshape(1,9) == SMatrix(1,9,(1,0,0,0,1,0,0,0,1))
    m1 = SMatrix(3,4, lambda i,j: i+j)
    assert m1.reshape(4,3) == SMatrix((0,1,2), (3,1,2), (3,4,2), (3,4,5))
    assert m1.reshape(2,6) == SMatrix((0,1,2,3,1,2), (3,4,2,3,4,5))

    # test_applyfunc
    m0 = eye(3)
    assert m0.applyfunc(lambda x:2*x) == eye(3)*2
    assert m0.applyfunc(lambda x: 0 ) == zero(3)

    # test_LUdecomp
    testmat = SMatrix([[0,2,5,3],
                      [3,3,7,4],
                      [8,4,0,2],
                      [-2,6,3,4]])
    L,U,p = testmat.LUdecomposition()
    assert L.is_lower()
    assert U.is_upper()
    assert (L*U).permuteBkwd(p)-testmat == zero(4)

    testmat = SMatrix([[6,-2,7,4],
                      [0,3,6,7],
                      [1,-2,7,4],
                      [-9,2,6,3]])
    L,U,p = testmat.LUdecomposition()
    assert L.is_lower()
    assert U.is_upper()
    assert (L*U).permuteBkwd(p)-testmat == zero(4)

    x, y, z = Symbol('x'), Symbol('y'), Symbol('z')
    M = Matrix((1, x, 1), (2, y, 0), (y, 0, z))
    L, U, p = M.LUdecomposition()
    assert L.is_lower()
    assert U.is_upper()
    assert (L*U).permuteBkwd(p)-M == zero(3)

    # test_LUsolve
    A = SMatrix([[2,3,5],
                [3,6,2],
                [8,3,6]])
    x = SMatrix(3,1,[3,7,5])
    b = A*x
    soln = A.LUsolve(b)
    assert soln == x
    A = SMatrix([[0,-1,2],
                [5,10,7],
                [8,3,4]])
    x = SMatrix(3,1,[-1,2,5])
    b = A*x
    soln = A.LUsolve(b)
    assert soln == x

    # test_inverse
    A = eye(4)
    assert A.inv() == eye(4)
    A = SMatrix([[2,3,5],
                [3,6,2],
                [8,3,6]])
    Ainv = A.inv()
    assert A*Ainv == eye(3)
    Ainv2 = A.inv("LU")
    assert Ainv == Ainv2

    # test_cross
    v1 = Matrix(1,3,[1,2,3])
    v2 = Matrix(1,3,[3,4,5])
    assert v1.cross(v2) == Matrix(1,3,[-2,4,-2])
    assert v1.norm(v1) == 14

    # test_cofactor
    assert eye(3) == eye(3).cofactorMatrix()
    test = SMatrix([[1,3,2],[2,6,3],[2,3,6]])
    assert test.cofactorMatrix() == SMatrix([[27,-6,-6],[-12,2,3],[-3,1,0]])
    test = SMatrix([[1,2,3],[4,5,6],[7,8,9]])
    assert test.cofactorMatrix() == SMatrix([[-3,6,-3],[6,-12,6],[-3,6,-3]])

    # test_jacobian
    x = Symbol('x')
    y = Symbol('y')
    L = SMatrix(1,2,[x**2*y, 2*y**2 + x*y])
    syms = [x,y]
    assert L.jacobian(syms) == Matrix([[2*x*y, x**2],[y, 4*y+x]])

    L = SMatrix(1,2,[x, x**2*y**3])
    assert L.jacobian(syms) == SMatrix([[1, 0], [2*x*y**3, x**2*3*y**2]])

    # test_QR
    A = Matrix([[1,2],[2,3]])
    Q, S = A.QRdecomposition()
    R = Rational
    assert Q == Matrix([[5**R(-1,2), (R(2)/5)*(R(1)/5)**R(-1,2)], [2*5**R(-1,2), (-R(1)/5)*(R(1)/5)**R(-1,2)]])
    assert S == Matrix([[5**R(1,2), 8*5**R(-1,2)], [0, (R(1)/5)**R(1,2)]])
    assert Q*S == A
    assert Q.T * Q == eye(2)

    # test nullspace
    # first test reduced row-ech form
    R = Rational

    M = Matrix([[5,7,2,1],
               [1,6,2,-1]])
    out, tmp = M.rref()
    assert out == Matrix([[1,0,-R(2)/23,R(13)/23],
                              [0,1,R(8)/23, R(-6)/23]])

    M = Matrix([[1,3,0,2,6,3,1],
                [-2,-6,0,-2,-8,3,1],
                [3,9,0,0,6,6,2],
                [-1,-3,0,1,0,9,3]])
    out, tmp = M.rref()
    assert out == Matrix([[1,3,0,0,2,0,0],
                               [0,0,0,1,2,0,0],
                               [0,0,0,0,0,1,R(1)/3],
                               [0,0,0,0,0,0,0]])
    # now check the vectors
    basis = M.nullspace()
    assert basis[0] == Matrix([[-3,1,0,0,0,0,0]])
    assert basis[1] == Matrix([[0,0,1,0,0,0,0]])
    assert basis[2] == Matrix([[-2,0,0,-2,1,0,0]])
    assert basis[3] == Matrix([[0,0,0,0,0,R(-1)/3, 1]])

    # test eigen
    x = Symbol('x')
    y = Symbol('y')
    eye3 = eye(3)
    assert eye3.charpoly(x) == (1-x)**3
    assert eye3.charpoly(y) == (1-y)**3
    # test values
    M = Matrix([(0,1,-1),
                (1,1,0),
                (-1,0,1) ])
    vals = M.eigenvals()
    vals.sort()
    assert vals == [-1, 1, 2]

    R = Rational
    M = Matrix([ [1,0,0],
                 [0,1,0],
                 [0,0,1]])
    assert M.eigenvects() == [[1, 3, [Matrix(1,3,[1,0,0]), Matrix(1,3,[0,1,0]), Matrix(1,3,[0,0,1])]]]
    M = Matrix([ [5,0,2],
                 [3,2,0],
                 [0,0,1]])
    assert M.eigenvects() == [[1, 1, [Matrix(1,3,[R(-1)/2,R(3)/2,1])]],
                              [2, 1, [Matrix(1,3,[0,1,0])]],
                              [5, 1, [Matrix(1,3,[1,1,0])]]]

def test_subs():
    x = Symbol('x')
    assert Matrix([[1,x],[x,4]]).subs(x, 5) == Matrix([[1,5],[5,4]])

def test_conjugate():
    M = Matrix([ [0,I,5],
                 [1,2,0]])

    assert M.T == Matrix([ [0,1],
                           [I,2],
                           [5,0]])

    assert M.C == Matrix([ [0,-I,5],
                           [1,2,0]])

    assert M.H == M.T.C
    assert M.H == Matrix([ [0,1],
                           [-I,2],
                           [5,0]])

def test_conj_dirac():
    py.test.raises(ShapeError, "eye(3).D")

    M = Matrix([ [1,I,I,I],
                 [0,1,I,I],
                 [0,0,1,I],
                 [0,0,0,1] ])

    assert M.D == Matrix([
                 [1,0,0,0],
                 [-I,1,0,0],
                 [-I,-I,-1,0],
                 [-I,-I,I,-1] ])

def test_trace():
    M = Matrix([[1,0,0],
                [0,5,0],
                [0,0,8]])
    assert M.trace() == 14

def test_shape():
    x, y = symbols("xy")
    M = Matrix([[x,0,0],
                [0,y,0]])
    assert M.shape == (2, 3)

def test_col_row():
    x, y = symbols("xy")
    M = Matrix([[x,0,0],
                [0,y,0]])
    M.row(1,lambda x,i: x+i+1)
    assert M == Matrix([[x,0,0],
                        [1,y+2,3]])
    M.col(0,lambda x,i: x+y**i)
    assert M == Matrix([[x+1,0,0],
                        [1+y,y+2,3]])


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

import numpy as np
from scipy.stats import t

def grubbs_outlier_test(y_i, alpha=0.95):
    """
    Perform Grubbs' outlier test.

    ARGUMENTS
    y_i (list or numpy array) - dataset
    alpha (float) - significance cutoff for test

    RETURNS
    G_i (list) - Grubbs G statistic for each member of the dataset
    Gtest (float) - rejection cutoff; hypothesis that no outliers exist if G_i.max() > Gtest
    no_outliers (bool) - boolean indicating whether there are no outliers at specified significance level
    index (int) - integer index of outlier with maximum G_i
    """
    s = y_i.std()
    G_i = np.abs(y_i - y_i.mean()) / s
    N = y_i.size
    # Upper critical value of the t-distribution with N − 2 degrees of freedom and a significance level of α/(2N)
    tcrit = t.isf(1 - alpha/(2*N), N-2)
    Gtest = (N-1)/np.sqrt(N) * np.sqrt(t**2 / (N-2+tcrit**2))
    G = G_i.max()
    index = G_i.argmax()
    no_outliers = (G > Gtest)
    return [G_i, Gtest, no_outliers, index]

def tau_outlier_test(x, scale=1.0, alpha=0.05):
    dof = x.size
    t_half_alpha = t.ppf(1 - alpha/2, dof - 1)
    tau_half_alpha = t_half_alpha * np.sqrt(dof)
    tau_half_alpha /= np.sqrt(dof - 1 + t_half_alpha**2)
    return np.abs(x)/scale <= tau_half_alpha

def rms(x):
    """Return root mean square"""
    return np.sqrt(np.mean(x**2))


# --------------
# Importing header files
import numpy as np
import warnings

warnings.filterwarnings('ignore')

#New record
new_record=[[50,  9,  4,  1,  0,  0, 40,  0]]

#Reading file
data = np.genfromtxt(path, delimiter=",", skip_header=1)

#Code starts here
print("\nData: \n\n", data)

print("\nType of data: \n\n", type(data))
census=np.concatenate((data,new_record),axis=0)

age=census[:,0]
print(age)
max_age=age.max()
print(max_age)

min_age=age.min()
print(min_age)

age_mean=age.mean()

age_mean=np.round(age_mean, decimals=2)
print(age_mean)

age_std=age.std()

age_std=np.round(age_std,decimals=2)
print(age_std)

race=census[:,2]
print(race)

race_0=census[census[:,2]==0]

race_1=census[census[:,2]==1]
race_2=census[census[:,2]==2]
race_3=census[census[:,2]==3]
race_4=census[census[:,2]==4]
len_0=len(race_1)
print(len_0)
len_1=len(race_1)
len_2=len(race_2)
len_3=len(race_3)
len_4=len(race_4)
print(len_1)
print(len_2)
print(len_3)
print(len_4)
minimum=min(len_0,len_1,len_2,len_3,len_4)
if   len(race_0)==minimum:
     minority_race=0
elif   len(race_1)==minimum:
     minority_race=1
elif   len(race_2)==minimum:
     minority_race=2
elif   len(race_3)==minimum:
     minority_race=3
elif   len(race_4)==minimum:
     minority_race=4
print(minority_race)

citizens=census[:,0]
print(citizens)
senior_citizens=census[census[:,0]>60]
# print(senior_citizens)
working_hours_sum=senior_citizens.sum(axis=0)[6]
print(working_hours_sum)
senior_citizens_len=len(senior_citizens)

avg_working_hours=working_hours_sum/senior_citizens_len
np.round(avg_working_hours,decimals=2)
print(avg_working_hours)

num=census[:,1]
high=census[census[:,1]>10]
low=census[census[:,1]<=10]

avg_pay_high=high.mean(axis=0)[7]

# avg_pay_high=np.round(age_pay_high, decimals=2)

# age_mean=age.mean()

# age_mean=np.round(age_mean, decimals=2)
avg_pay_low=low.mean(axis=0)[7]

print(avg_pay_high)
print(avg_pay_low)