from typing import Union

import numpy as np

def manhattan(
    x: Union[list, np.array], y: Union[list, np.array]
) -> Union[float, list, np.array]:
    """Calculate manhattan distance between two points.
    The distance between two points measured along axes at right angles.

    Args:
        x: Point x
        y: Point y

    Returns:

    """
    if isinstance(x, list):
        x = np.array(x)
    if isinstance(y, list):
        y = np.array(y)
    if x.shape != y.shape or x.size != y.size:
        raise ValueError(
            f"Shape or size of x and y does not matches, x shape {x.shape}, y shape {y.shape}"
        )
    return np.abs(x[0] - y[0]) + np.abs(x[1] - y[1])

def euclidean(
    x: Union[list, np.array], y: Union[list, np.array]
) -> Union[float, list, np.array]:
    """Calculate manhattan distance between two points.
    The distance between two points measured along axes at right angles.

    Args:
        x: Point x
        y: Point y

    Returns:

    """
    return np.sqrt(np.sum(np.square(x - y)))


# -*- coding: utf-8 -*-
"""Algebra_Linear_no_Python-Colab.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/18WA6PXajjsR3ipWb-9c5dfxojLcI6QMv

# **Álgebra Linear**
"""

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

v1 = [2,5]
v2 = [1,6,8]
v1 ,v2

type(v1)

v3 = np.array([8,3,9])
type(v3)

v3.shape[0]

v3.shape

v4=np.array([1.+2.j, 3.+4.j, 5, 6.j], dtype=complex)
v4

type(v4)

"""# **Lendo elementos de um array**"""

a = np.array([7,5,3,9,0,2])
a

a[0]

a[1:]

a[1:4]

a[-1]

a[-3]

a[-6]

a[-3:-1]

"""#**Plotando um vetor**"""

v = [3,5]
u = [1,2,3]

plt.plot(v)

plt.plot([0,v[0]] , [0,v[1]])

"""# **Plota um vetor 2D**"""

plt.plot([0,v[0]] , [0,v[1]])
plt.plot([8,-8] , [0,0] , 'k--')
plt.plot([0,0] , [8,-8] , 'k--')
plt.grid()
plt.axis((-8, 8, -8, 8))
plt.show()

"""# **Plota um vetor 3D**"""

fig = plt.figure()
ax = Axes3D(fig)
ax.plot([0,u[0]],[0,u[1]],[0,u[2]])
#plt.axis('equal')
ax.plot([0, 0],[0, 0],[-5, 5],'k--')
ax.plot([0, 0],[-5, 5],[0, 0],'k--')
ax.plot([-5, 5],[0, 0],[0, 0],'k--')
plt.show()

"""# **Soma de vetores**"""

v1 = np.array([2,-4])
v2 = np.array([4,6])
v3 = v1+v2
v3 = np.add(v1,v2)
print('V3 =' ,v3)
plt.plot([0,v1[0]] , [0,v1[1]] , 'r' , label = 'v1')
plt.plot([0,v2[0]] , [0,v2[1]], 'b' , label = 'v2')
plt.plot([0,v3[0]] , [0,v3[1]] , 'g' , label = 'v3')
plt.plot([8,-8] , [0,0] , 'k--')
plt.plot([0,0] , [8,-8] , 'k--')
plt.grid()
plt.axis((-8, 8, -8, 8))
plt.legend()
plt.show()

plt.plot([0,v1[0]] , [0,v1[1]] , 'r' , label = 'vetor v1')
plt.plot([0,v2[0]]+v1[0] , [0,v2[1]]+v1[1], 'b' , label = 'vetor v2')
plt.plot([0,v3[0]] , [0,v3[1]] , 'g' , label = 'vetor v3')
plt.plot([8,-8] , [0,0] , 'k--')
plt.plot([0,0] , [8,-8] , 'k--')
plt.grid()
plt.axis((-8, 8, -8, 8))
plt.legend()
plt.show()

"""# **Multiplicação de vetor por um escalar**"""

u1 = np.array([4,4])
a = .5
u2 = u1*a
plt.plot([0,u1[0]] , [0,u1[1]] , 'r' , label = 'v1')
plt.plot([0,u2[0]] , [0,u2[1]], 'b--' , label = 'v2')
plt.plot([8,-8] , [0,0] , 'k--')
plt.plot([0,0] , [8,-8] , 'k--')
plt.grid()
plt.axis((-8, 8, -8, 8))
plt.legend()
plt.show()

u1 = np.array([4,4])
a = -.3
u2 = u1*a
plt.plot([0,u1[0]] , [0,u1[1]] , 'r' , label = 'v1')
plt.plot([0,u2[0]] , [0,u2[1]], 'b' , label = 'v2')
plt.plot([8,-8] , [0,0] , 'k--')
plt.plot([0,0] , [8,-8] , 'k--')
plt.grid()
plt.axis((-8, 8, -8, 8))
plt.legend()
plt.show()

"""# **Multiplicação de vetores**"""

a1 = [2,4,6]
a2 = [3,5,1]
print(np.multiply(a1,a2))

"""# **Produto interno**"""

a1 = np.array([2,4,6])
a2 = np.array([3,5,1])

dotp = a1@a2
print(" Dot product - ",dotp)

dotp = np.dot(a1,a2)
print(" Dot product usign np.dot",dotp)

dotp = np.inner(a1,a2)
print(" Dot product usign np.inner", dotp)

dotp = sum(np.multiply(a1,a2))
print(" Dot product usign np.multiply & sum",dotp)

dotp = np.matmul(a1,a2)
print(" Dot product usign np.matmul",dotp)

dotp = 0
for i in range(len(a1)):
    dotp = dotp + a1[i]*a2[i]
print(" Dot product usign for loop" , dotp)

"""# **Tamanho de um vetor**"""

v3 = np.array([1,2,3,4,5,6,7])
length = np.sqrt(np.dot(v3,v3))
length

v3 = np.array([1,2,3,4,5,6,7])
length = np.sqrt(sum(np.multiply(v3,v3)))
length

v3 = np.array([1,2,3,4,5,6,7])
length = np.sqrt(np.matmul(v3,v3))
length

"""# **Vetor normalizado**"""

v1 = [1,1]
length_v1 = np.sqrt(np.dot(v1,v1))
norm_v1 = v1/length_v1
length_v1 , norm_v1

v1 = [1,1]
norm_v1 = v1/np.linalg.norm(v1)
norm_v1

"""# **Ângulo entre vetores**"""

#First Method
v1 = np.array([5,5])
v2 = np.array([-5,5])
ang = np.rad2deg(np.arccos( np.dot(v1,v2) / (np.linalg.norm(v1)*np.linalg.norm(v2))))
plt.plot([0,v1[0]] , [0,v1[1]] , 'r' , label = 'v1')
plt.plot([0,v2[0]]+v1[0] , [0,v2[1]]+v1[1], 'b' , label = 'v2')
plt.plot([15,-15] , [0,0] , 'k--')
plt.plot([0,0] , [15,-15] , 'k--')
plt.grid()
plt.axis((-15, 15, -15, 15))
plt.legend()
plt.title('Angulo entre vetores - %s'  %ang)
plt.show()

#Second Method
v1 = np.array([5,5])
v2 = np.array([-5,5])
lengthV1 = np.sqrt(np.dot(v1,v1))
lengthV2  = np.sqrt(np.dot(v2,v2))
ang = np.rad2deg(np.arccos( np.dot(v1,v2) / (lengthV1 * lengthV2)))
print('Angulo entre vetores - %s' %ang)

v1 = np.array([5,2,-3])
v2 = np.array([7,-1,2])
fig = plt.figure()
ax = Axes3D(fig)
ax.plot([0, v1[0]],[0, v1[1]],[0, v1[2]],'b')
ax.plot([0, v2[0]],[0, v2[1]],[0, v2[2]],'r')
ang = np.rad2deg(np.arccos( np.dot(v1,v2) / (np.linalg.norm(v1)*np.linalg.norm(v2)) ))
plt.title('Angulo entre vetores: %s degrees.' %ang)

"""# **Produtos interno e externo**"""

v1 = np.array([1,2,1])
v2 = np.array([2,1,2])
np.inner(v1,v2)

print("\n Inner Product ==>  \n", np.inner(v1,v2))
print("\n Outer Product ==>  \n", np.outer(v1,v2))

"""# **Produto vetorial**"""

v1 = np.array([7,0,0])
v2 = np.array([0,7,0])
print("\nVector Cross Product ==>  \n", np.cross(v1,v2))

"""# **Operações com matrizes**

# **Criação de matriz**
"""

A = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
A

type(A)

A.dtype

B = np.array([[2.7,6.03,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
B

type(B)

B.dtype

A.shape

A[0,]

A[:,0]

A[0,0]

A[0][0]

A[1:3 , 1:3]

"""# **Matriz de zeros**"""

np.zeros(36).reshape(6,6)

np.zeros((6,6))

"""# **Matriz de 1's**"""

np.ones(25).reshape(5,5)

np.ones((5,5))

"""# **Matriz de números aleatórios**"""

X = np.random.random((3,4))
X

"""# **Matriz identidade**"""

I = np.eye(7)
I

"""# **Matriz diagonal**"""

D = np.diag([1,2,3,4,5,6,7])
D

"""# **Matrizes triangulares (superior e inferior)**"""

M = np.random.randn(5,5)
U = np.triu(M)
L = np.tril(M)
print("matriz aleatória \n" , M)
print("\n")

print("matriz triangular inferior \n" , L)
print("\n")

print("matriz triangular superior \n" , U)

"""# **Concatenação de matrizes**"""

A = np.array([[1,2] , [3,4] ,[5,6]])
B = np.array([[9,2] , [3,-3]])
C = np.concatenate((A,B))
C , C.shape , type(C) , C.dtype

np.full((7,7) , 4)

M = np.array([[1,2,3],[4,-3,6],[8,7,0]])
M

M.flatten()

"""# **Soma de matrizes**"""

M = np.array([[1,2,3],[4,-3,6],[7,8,0]])
N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("\n Primeira matriz (M)  ==>  \n", M)
print("\n Segunda matriz (N)  ==>  \n", N)

C = M+N
print("\n soma (M+N)  ==>  \n", C)

# ou

C = np.add(M,N,dtype = np.float64)
print("\n soma usando np.add  ==>  \n", C)

"""# **Subtração de matrizes**"""

M = np.array([[1,2,3],[4,-3,6],[7,8,0]])
N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("\n Primeira matriz (M)  ==>  \n", M)
print("\n Segunda matriz (N)  ==>  \n", N)

C = M-N
print("\n Subtração (M-N)  ==>  \n", C)

# ou

C = np.subtract(M,N,dtype = np.float64)
print("\n Subtração usando np.subtract  ==>  \n", C)

"""# **Multiplicação de matriz por escalar**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])

C = 20

print("\n Matriz (M)  ==>  \n", M)

print("\n Multiplicação por escalar ==>  \n", C*M)

# ou

print("\n Multiplicação por escalar usando np.multiply ==>  \n", np.multiply(C,M))

"""# **Transposta de uma matriz**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])

print("\n Matriz (M)  ==>  \n", M)

print("\n Transposta de M ==>  \n", np.transpose(M))

# ou

print("\nTransposta de M ==>  \n", M.T)

"""# **Determinante de uma matriz**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])

print("\n Matriz (M)  ==>  \n", M)

print("\n Determinante de M ==>  ", np.linalg.det(M))

"""# **Posto de uma matriz**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])

print("\n Matriz (M)  ==>  \n", M)

print("\n Posto de M ==> ", np.linalg.matrix_rank(M))

"""# **Traço de uma matriz**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])

print("\n Matriz (M)  ==>  \n", M)

print("\n Traço de M ==> ", np.trace(M))

"""# **Inversa de uma matriz**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])

print("\n Matriz (M)  ==>  \n", M)

print("\n Inversa de M ==> \n", np.linalg.inv(M))

"""# **Multiplicação de matrizes (pontual)**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])
N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("\n Primeira matriz (M)  ==>  \n", M)
print("\n Segunda matriz (N)  ==>  \n", N)

print("\n Multiplication pontual de M e N  ==> \n", M*N)

# ou

print("\n Multiplication pontual de M e N  ==> \n", np.multiply(M,N))

"""# **Produto escalar matricial**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])
N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("\n Primeira matriz (M) \n", M)
print("\n Segunda matriz (N) \n", N)

print("\n Produto escalar \n", M@N)

# ou

print("\n Produto escala usando np.matmul \n", np.matmul(M,N))

# ou

print("\n Produto escala usando np.dot \n", np.dot(M,N))

"""# **"Divisão" de Matrizes**"""

M = np.array([[1,9,3],[2,-7,6],[8,7,0]])
N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("\n Primeira matriz (M) \n", M)
print("\n Segunda matriz (N) \n", N)

print("\n Divisão (M/N) \n", M/N)

# ou

print("\n Divisão (M/N) \n", np.divide(M,N))

"""# **Soma de todos elementos da matriz**"""

N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("Matriz (N) \n", N)

print ("Soma de todos elementos da matriz")
print (np.sum(N))

"""# **Adição com base na coluna**"""

N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("Matriz (N)  ==>  \n", N)

print ("Adição com base na coluna")
print (np.sum(N,axis=0))

"""# **Adição com base na linha**"""

N = np.array([[5,5,5],[6,6,6],[7,7,7]])

print("Matriz (N)  ==>  \n", N)

print ("Adição com base na linha")
print (np.sum(N,axis=1))

"""# **Produto de Kronecker de matrizes**"""

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

M2 = np.array([[10,10,10],[10,10,10]])
M2

np.kron(M1,M2)

"""# **Multiplicação matriz-vetor**"""

A = np.array([[1,2,3] ,[4,5,6]])
v = np.array([10,20,30])
print ("Multiplicação matriz-vetor \n", A*v)

"""# **Produto escalar matriz-vetor**"""

A = np.array([[1,2,3] ,[4,5,6]])
v = np.array([10,20,30])

print ("Produto escalar matriz-vetor \n" , A@v)

"""# **Potências de matriz**"""

M1 = np.array([[1,2],[5,6]])
M1

# Matriz na potencia 3

M1@M1@M1

# Matriz na potencia 3 usando np.linalg.matrix_power

np.linalg.matrix_power(M1,3)

"""# **Tensores**"""

# Criando um Tensor

T1 = np.array([
  [[1,2,3],    [4,5,6],    [7,8,9]],
  [[10,11,12], [13,14,15], [16,17,18]],
  [[19,20,21], [22,23,24], [25,26,27]],
  ])

T1

T2 = np.array([
  [[0,0,0] , [0,0,0] , [0,0,0]],
  [[2,2,2] , [2,2,2] , [2,2,2]],
  [[4,4,4] , [4,4,4] , [4,4,4]]

])

T2

"""# **Soma de tensores**"""

A = T1+T2
A

"""# **Subtração de tensores**"""

S = T1-T2
S

np.subtract(T1,T2)

"""# **Produto de tensores (baseado em elementos)**"""

P = T1*T2
P

np.multiply(T1,T2)

"""# **"Divisão" de tensores (baseado em elementos)**"""

D = T1/T2
D

np.divide(T1,T2)

"""# **Produto escalar de tensores**"""

T1

T2

np.tensordot(T1,T2)

"""# **Solução de sistemas lineares (AX=B)**"""

A = np.array([[4,-2,3] , [1,-5,6] , [-7,8,9]])
A

B = np.random.random((3,1))
B

# Primeiro metodo
X = np.dot(np.linalg.inv(A) , B)
X

# Segundo Metodo
X = np.matmul(np.linalg.inv(A) , B)
X

# Terceiro metodo
X = np.linalg.inv(A)@B
X

# Quarto metodo
X = np.linalg.solve(A,B)
X


"""Scoring module or forecasting models."""
import collections

import numpy as np
import sklearn.metrics

def mse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """Compute the MSE (Mean Squared Error)."""
    return sklearn.metrics.mean_squared_error(y_true, y_pred)

def rmse(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """Compute the RMSE (Root Mean Squared Error)."""
    return sklearn.metrics.mean_squared_error(y_true, y_pred, squared=False)

def mae(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """Compute the MAE (Mean Absolute Error)."""
    return sklearn.metrics.mean_absolute_error(y_true, y_pred)

def smape(
    arr_a: np.ndarray,
    arr_b: np.ndarray,
    percentage: bool = False,
    half_denom: bool = True,
) -> float:
    """Calculate SMAPE (Symmetric Mean Absolute Percentage Error).

    Parameters
    ----------
    arr_a, arr_b : :obj:`np.ndarray`
        Arrays to calculate the SMAPE from.

    percentage : bool, optional
        If True, multiply the result by 100 (i.e., return the
        percentage value, not the fraction).

    half_denom : bool, optional
        If True, divide the denominator by 2. If False, the
        result if bounded by [0, 100] (if ``percentage`` is
        True), or by [0, 1] otherwise.

    Returns
    -------
    float
        SMAPE estimation between the two given arrays. If
        ``percentage`` is True, then return the estimation in
        the percentage form (in [0, 100] range). Return the
        error in fraction form (in [0, 1] range) otherwise.
    """
    res = np.mean(
        np.abs(arr_a - arr_b) / (1e-9 + np.abs(arr_a) + np.abs(arr_b))
    )

    if percentage:
        res *= 100

    if not half_denom:
        res *= 2

    return res

VALID_SCORING = collections.OrderedDict(
    (
        ("mse", mse),
        ("rmse", rmse),
        ("mae", mae),
        ("smape", smape),
    )
)


import numpy as np
v = 1/np.sqrt(5)*np.array([1, 2])
v_norm = np.sqrt(sum(v**2))
# x1
u = np.array([1, 2])
pruv = (np.dot(u, v)/v_norm**2)*v
print(pruv)
# x2
u = np.array([3, 4])
pruv = (np.dot(u, v)/v_norm**2)*v
print(pruv)
# x3
u = np.array([-1, 0])
pruv = (np.dot(u, v)/v_norm**2)*v
print(pruv)

x1= np.array([1, 2])
x2 = np.array([3, 4])
x3 = np.array([-1, 0])

#covariance matrix def
#1/n*np.dot(x.T,x)


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

f1 = open('Data3-1.txt', 'r')
w1 = f1.readlines()
for line in w1:
    w1 = line.split(',')
w1 = [float(x) for x in w1]
f1.close()

f2 = open('Data3-2.txt', 'r')
w2 = f2.readlines()
for line in w2:
    w2 = line.split(',')
w2 = [float(x) for x in w2]
f2.close()

f3 = open('Data3-3.txt', 'r')
w3 = f3.readlines()
for line in w3:
    w3 = line.split(',')
w3 = [float(x) for x in w3]
f3.close()

data = {
    'Data1': w1,
    'Data2': w2,
    'Data3': w3,
    }
from itertools import combinations
from scipy.stats import ttest_ind #Built-in function for one tail
print('1st  vs 2nd       P-value')
for list1, list2 in combinations(data.keys(), 2):
    t, p = ttest_ind(data[list1], data[list2])
    print(list1, list2, p)

df = pandas.DataFrame(data)
kwargs = dict(alpha=0.8, bins=100)
plt.hist(df['Data1'], **kwargs, color='g', label='D1',density=True, histtype='bar', stacked=True)
plt.hist(df['Data2'], **kwargs, color='b', label='D2',density=True, histtype='bar', stacked=True)
plt.hist(df['Data3'], **kwargs, color='r', label='D3',density=True, histtype='bar', stacked=True)
plt.show()

#%% Tailored function Comparing two lists for two tails

def Calculate_Pvalue(w1,w2):
    w1Mean, w2Mean = np.mean(w1), np.mean(w2)
    w1Std, w2Std = np.sqrt(np.var(w1)), np.sqrt(np.var(w2))
    n = len(w2)
    Mu0 = w1Mean
    x = w2Mean
    segma = w1Std
    print('Mean 1: %f' %w1Mean,' Mean 2: %f'%w2Mean)
    print('Std  1: %f' %w1Std , '  Std  2: %f' %w2Std)

    z0 = (x-Mu0)/(segma/np.sqrt(n))
    P_value = 2*(1-norm.cdf(abs(z0)))
    C_value = norm.ppf(P_value)
    print('P-value for Data is: %f' %P_value)
    print('with Critical Value: %f' %C_value)
    if P_value <= 0.05:
        print("we reject null hypothesis")
    else:
        print("we accept null hypothesis")
    kwargs = dict(alpha=0.8, bins=50)
    plt.hist(w1, **kwargs, color='b', label='D1',density=True, histtype='bar', stacked=True)
    plt.hist(w2, **kwargs, color='r', label='D2',density=True, histtype='bar', stacked=True)
    plt.show()

#%% Try here!!

#Calculate_Pvalue(w1,w2)


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

def false_position(function, aa, bb, eps = 0.001, nombre = 'Gráfica',
    iteraciones = 30, graph = True, err_graph = False):
	a = float(aa)
	b = float(bb)
	m = 0.
	n = 0
	err = float(abs((a-b)/2))
	err_steps = [err]

	def f(x):
		return eval(function)

	domain = np.linspace(a,b,1000)
	plt.plot(domain,f(domain))
	plt.grid(True, which='both')
	plt.axhline(y=0, color='k')
	plt.axvline(x=0, color='k')
	plt.title(nombre)
	if graph:
		plt.show()
	plt.close()

	while eps < err:
		if n < iteraciones:
			try:
				m = b - (f(b) * (a - b))/(f(a) - f(b))
				n += 1
			except ZeroDivisionError:
				print('Error por división de cero: f(a) y f(b) son iguales. Pruebe'
							    + ' nuevo intervalo')
				break

			if f(m) == 0:
				print('Raíz exacta: ' + str(m))
				break

			if f(a)*f(m)<0:
				a, b = a, m
				b=m
			else:
				a, b = m, b

			err = abs((a-b)/2)
			err_steps.append(err)
			print('Nuevo valor: ' + str(m) + '. Loop Nº' + str(n) +
						    '. Error: ' + str(err))

		else:
			print('End of loop \n \n')
			break
	m = (a + b) / 2
	print('Nuevo valor: %f. Loop Nº %f. Error: %f' % (m, n, err))

	if err_graph:
		domain_err = np.arange(len(err_steps))
		plt.plot(domain_err, err_steps)
		plt.grid(True, which='both')
		plt.axhline(y=0, color='k')
		plt.axvline(x=0, color='k')
		plt.yscale('log')
		plt.show()
		plt.close()

def biparticion(function, aa, bb, eps=0.001, nombre = 'Grafica',
    iteraciones = 30, graph = True, err_graph = False):
	a = float(aa)
	b = float(bb)
	m = 0
	n = 0
	err = abs((a-b)/2)
	err_steps = [err]

	def f(x):
		return eval(function)

	domain = np.linspace(a, b, 1000)
	plt.plot(domain, f(domain))
	plt.grid(True, which='both')
	plt.axhline(y = 0, color = 'k')
	plt.axvline(x = 0, color = 'k')
	plt.title(nombre)
	if graph:
		plt.show()
	plt.close()

	while eps < err:
		if n < iteraciones:
			m = (a + b) / 2
			n += 1

			if f(m) == 0:
				print('Raíz exacta: ' + str(m))
				break

			if f(a) * f(m) < 0:
				a, b = a, m
			else:
				a, b = m, b

			err = abs((a-b)/2)
			err_steps.append(err)
			print('Nuevo valor: ' + str(m) + '. Loop Nº' + str(n) +
						    '. Error: ' + str(err))

		else:
			print('End of loop')
			break

	if err_graph:
		domain_err = np.arange(len(err_steps))
		plt.plot(domain_err, err_steps)
		plt.grid(True, which='both')
		plt.axhline(y=0, color='k')
		plt.axvline(x=0, color='k')
		plt.yscale('log')
		plt.show()
		plt.close()

# biparticion(, -2, 6, nombre = 'Ejercicio 2')
# false_position('x - 15**0.5', 3, 4, nombre = 'Ejercicio 2', eps= 0.49)

ecuacion = '-0.13952954729309466/np.sqrt(x**5) - 0.86*np.log(0.07027027027027026/x + (9.168716265367385e-6*x)/np.sqrt(x**5))'
biparticion(ecuacion, 0.2, 0.8, eps=0.00001, iteraciones=10000, err_graph=True)
false_position(ecuacion, 0.2, 0.8, eps=0.00001, iteraciones=100000, err_graph=True)


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

"""
http://www.scipy.org/Cookbook/Least_Squares_Circle
"""

from numpy import *

# Coordinates of the 2D points
x = r_[  9,  35, -13,  10,  23,   0]
y = r_[ 34,  10,   6, -14,  27, -10]

# == METHOD 1 ==
method_1 = 'algebraic'

# coordinates of the barycenter
x_m = mean(x)
y_m = mean(y)

# calculation of the reduced coordinates
u = x - x_m
v = y - y_m

# linear system defining the center in reduced coordinates (uc, vc):
#    Suu * uc +  Suv * vc = (Suuu + Suvv)/2
#    Suv * uc +  Svv * vc = (Suuv + Svvv)/2

Suv  = sum(u*v)
Suu  = sum(u**2)
Svv  = sum(v**2)
Suuv = sum(u**2 * v)
Suvv = sum(u * v**2)
Suuu = sum(u**3)
Svvv = sum(v**3)

# Solving the linear system
A = array([ [ Suu, Suv ], [Suv, Svv]])
B = array([ Suuu + Suvv, Svvv + Suuv ])/2.0
uc, vc = linalg.solve(A, B)

xc_1 = x_m + uc
yc_1 = y_m + vc

# Calculation of all distances from the center (xc_1, yc_1)
Ri_1     = sqrt((x-xc_1)**2 + (y-yc_1)**2)
R_1      = mean(Ri_1)
residu_1 = sum((Ri_1-R_1)**2)

#  == METHOD 2 ==
from scipy      import optimize

method_2 = "leastsq"

def calc_R(c):
    """ calculate the distance of each 2D points from the center c=(xc, yc) """
    return sqrt((x-c[0])**2 + (y-c[1])**2)

def calc_ecart(c):
    """ calculate the algebraic distance between the 2D points and the mean circle centered at c=(xc, yc) """
    Ri = calc_R(c)
    return Ri - Ri.mean()

center_estimate = x_m, y_m
center_2, ier = optimize.leastsq(calc_ecart, center_estimate)

xc_2, yc_2 = center_2
Ri_2       = calc_R(center_2)
R_2        = Ri_2.mean()
residu_2   = sum((Ri_2 - R_2)**2)

# == METHOD 3 ==
from scipy      import  odr

method_3 = "odr"

def calc_f(beta, x):
    """ implicit function of the circle """
    xc, yc, r = beta
    return (x[0]-xc)**2 + (x[1]-yc)**2 -r**2

def calc_estimate(data):
    """ Return a first estimation on the parameter from the data  """
    xc0, yc0 = data.x.mean(axis=1)
    r0 = sqrt((data.x[0]-xc0)**2 +(data.x[1] -yc0)**2).mean()
    return xc0, yc0, r0

# for implicit function :
#       data.x contains both coordinates of the points
#       data.y is the dimensionality of the response
lsc_data  = odr.Data(row_stack([x, y]), y=1)
lsc_model = odr.Model(calc_f, implicit=True, estimate=calc_estimate)
lsc_odr   = odr.ODR(lsc_data, lsc_model)
lsc_out   = lsc_odr.run()

xc_3, yc_3, R_3 = lsc_out.beta
Ri_3 = calc_R([xc_3, yc_3])
residu_3 = sum((Ri_3 - R_3)**2)

# Summary
fmt = '%-15s %10.5f %10.5f %10.5f %10.6f %10.6f'
print '\n%-15s %10s %10s %10s %10s %10s' % tuple('METHOD Xc Yc Rc std(Ri) residu'.split())
print '-'*(15 +5*(10+1))
print  fmt % (method_1, xc_1, yc_1, R_1, Ri_1.std(), residu_1)
print  fmt % (method_2, xc_2, yc_2, R_2, Ri_2.std(), residu_2)
print  fmt % (method_3, xc_3, yc_3, R_3, Ri_3.std(), residu_3)

# plotting functions

from matplotlib import pyplot as p, cm

f = p.figure()
p.plot(x, y, 'ro', label='data', ms=9, mec='b', mew=1)

theta_fit = linspace(-pi, pi, 180)

x_fit1 = xc_1 + R_1*cos(theta_fit)
y_fit1 = yc_1 + R_1*sin(theta_fit)
p.plot(x_fit1, y_fit1, 'b-' , label=method_1, lw=2)

x_fit2 = xc_2 + R_2*cos(theta_fit)
y_fit2 = yc_2 + R_2*sin(theta_fit)
p.plot(x_fit2, y_fit2, 'k--', label=method_2, lw=2)

x_fit3 = xc_3 + R_3*cos(theta_fit)
y_fit3 = yc_3 + R_3*sin(theta_fit)
p.plot(x_fit3, y_fit3, 'r-.', label=method_3, lw=2)

p.plot([xc_1], [yc_1], 'bD', mec='y', mew=1)
p.plot([xc_2], [yc_2], 'gD', mec='r', mew=1)
p.plot([xc_3], [yc_3], 'kD', mec='w', mew=1)

# draw
p.axis('equal')
p.xlabel('x')
p.ylabel('y')
p.legend(loc='best',labelspacing=0.1)

# plot the residu fields
nb_pts = 100

p.draw()
xmin, xmax = p.xlim()
ymin, ymax = p.ylim()

vmin = min(xmin, ymin)
vmax = max(xmax, ymax)

xg, yg = ogrid[vmin:vmax:nb_pts*1j, vmin:vmax:nb_pts*1j]
xg = xg[..., newaxis]
yg = yg[..., newaxis]

Rig    = sqrt( (xg - x)**2 + (yg - y)**2 )
Rig_m  = Rig.mean(axis=2)[..., newaxis]
residu = sum( (Rig-Rig_m)**2 ,axis=2)

lvl = exp(linspace(log(residu.min()), log(residu.max()), 15))

p.contourf(xg.flat, yg.flat, residu.T, lvl, alpha=0.75, cmap=cm.YlGnBu_r)
cbar = p.colorbar(format='%.1f')
cbar.set_label('Residu')
p.xlim(xmin=vmin, xmax=vmax)
p.ylim(ymin=vmin, ymax=vmax)

p.grid()
p.title('Leasts Squares Circle')
p.show()
# vim: set et sts=4 sw=4:


import sympy as sp
x, y, z = sp.symbols('x y z')
sp.init_printing()

# Here we simply give the input needed as The Following:
# ( Xl ) is X of The Lower Limit
# ( Xu ) Is X of The Upper Limit
# ( F ) is the equation of the function
# ( n ) is the number of Iterations needed

Xl = 1

Xu = 3

F = 2*sp.cos(x) +5*sp.sin(x) -2

n = 5

print(" ______________________________________________________________________________________\n  "
      "Iteration |     Xl   |    Xu   |   Xm   |   F(Xl)  |  F(Xu)  |   F(Xm)   |   Error"
      "\n --------------------------------------------------------------------------------------")
def Error (old, new):
    return abs((new-old)/new)*100
Xm = 1 ; error = 0 ; oldXm = 0
for i in range(n):
    Fxl = F.subs(x, Xl).evalf() ; Fxu = F.subs(x, Xu).evalf() ; Xm = Xu - (Fxu * (Xl - Xu))/(Fxl- Fxu)
    Fxm = F.subs(x, Xm).evalf() ; error = Error(oldXm, Xm); oldXm = Xm
    print("      {0:2d}    |  {1:.5f} | {2:.5f} | {3:.5f} | {4:.5f} | {5:.5f} | {6:.5f} | {7:.5f} % "
          .format(i+1, float(Xl), float(Xu), float(Xm),float(Fxl),float(Fxu), float(Fxm), float(error)))
    if Fxl*Fxm < 0 : Xu = Xm
    elif Fxl*Fxm > 0 : Xl = Xm

# In The End The Program Prints The Output and specifies what the new Xm will be as well as The Percentage of error between Iterations


## module newtonRaphson
''' root = newtonRaphson(f,df,a,b,tol=1.0e-9).
    Finds a root of f(x) = 0 by combining the Newton-Raphson
    method with bisection. The root must be bracketed in (a,b).
    Calls user-supplied functions f(x) and its derivative df(x).
'''
def newtonRaphson(f,df,a,b,tol=1.0e-9):
    import error
    from numpy import sign

    fa = f(a)
    if fa == 0.0: return a
    fb = f(b)
    if fb == 0.0: return b
    if sign(fa) == sign(fb): error.err('Root is not bracketed')
    x = 0.5*(a + b)
    for i in range(30):
        fx = f(x)
        if fx == 0.0: return x
        # Tighten the brackets on the root
        if sign(fa) != sign(fx): b = x
        else: a = x
        # Try a Newton-Raphson step
        dfx = df(x)
        # If division by zero, push x out of bounds
        try: dx = -fx/dfx
        except ZeroDivisionError: dx = b - a
        x = x + dx
        # If the result is outside the brackets, use bisection
        if (b - x)*(x - a) < 0.0:
            dx = 0.5*(b - a)
            x = a + dx
        # Check for convergence
        if abs(dx) < tol*max(abs(b),1.0): return x
    print('Too many iterations in Newton-Raphson')


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

''' Statistics exploration using SciPy'''
''' Few tests, Linear Regression, and Distributions explored here,
along with proper explanations'''

'''
1. Shapiro-Wilk test: This test is useful for testing if a data sample
has a Gaussian Distribution or not.
'''
import numpy
from scipy.stats import shapiro

samples = numpy.array([0.873, 2.817, 0.121,
                       -0.945, -0.055, -1.436,
                       0.360, -1.478, -1.637, -1.869])

# p value for shapiro test detected
value, p_value = shapiro(samples)

# Testing if p_value is significant or not
if p_value > 0.05:
    print('Likely gaussian with 95% confidence')
else:
    print('Unlikely to be Gaussain')

'''
2. Pearson Correlation Coefficient: Checks whether two data samples have
linear correlation or not.
'''
from scipy.stats import pearsonr

samples_2 = numpy.array([ 0.991, 3.150, 0.212,
                          -0.799, -0.055, -1.002,
                          0.443, -1.0, -1.44, -2.21])

#Testing if p_value is signnificant or not
corr_coef, p_value = pearsonr(samples, samples_2)

if p_value > 0.05:
    print('Likely to be linearly correleated')
else:
    print('Unlikely to be correlated')

'''
3.  Spearman rank correlation: Checks if two samples have monotonic
relationship or not'''

from scipy.stats import spearmanr

stat, p_value = spearmanr(samples, samples_2)
if p_value > 0.05:
    print('Likely to be correlated via spearman test')
else:
    print('Unlikely to be correlated')


# importing the required python libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from lifelines import NelsonAalenFitter

# reading the data into python dataframe
telco_data = pd.read_csv('telco_customer.csv')

# filtering and preparing the data to the required format
telco_data['TotalCharges']=pd.to_numeric(telco_data['TotalCharges'],errors='coerce')
telco_data['Churn']=telco_data['Churn'].apply(lambda x: 1 if x == 'Yes' else 0 )

# the duration varaiable column
T = telco_data['tenure']

# the target event(occures/not occured) column
E = telco_data['Churn']

# creating a NelsonAalenFitter object and fitting the duration and event colum data to it
naf = NelsonAalenFitter()
naf.fit(T,event_observed=E)

# printing out the NA Estimations (cummulative hazard rates)
print(naf.cumulative_hazard_.head())
print(naf.cumulative_hazard_.tail())

# utility function to plot the NA Estimator
def plot_estimator():
    naf.plot(figsize=(12,10),label="NA Estimator")
    plt.xlabel('Tenure',size=15)
    plt.ylabel('NA Estimate(Commulative Hazard Function Estimator)',size=13)
    plt.title('Plotting NA Estimate against the churning tenure of a customer',size=15)
    plt.legend(prop={'size': 16})

# utility function to smoothen the curve and compare between the plots
def plot_function(category1,category2,bandwidth,column_name):
    small_ans = (telco_data[column_name] == category1)
    naf.fit(T[small_ans], event_observed=E[small_ans], label= category1)
    ax = naf.plot_hazard(bandwidth=bandwidth,figsize=(10,8))
    naf.fit(T[~small_ans], event_observed=E[~small_ans], label= category2)
    naf.plot_hazard(ax=ax, bandwidth=bandwidth,figsize=(10,8))
    plt.legend(title = column_name,prop={'size': 12})
    plt.title('Curve Smoothening and Comparing({})'.format(column_name),size=12)

# utility function for a plot generation to explain the smoothness and bandwidth factor visually
def smooth_analysis():
    n = 10
    while(n<=100):
        naf.plot_hazard(bandwidth=n,figsize=(15,13),label=n)
        n+=10
    plt.tight_layout()
    plt.legend(title= 'Bandwidth',prop={'size': 12})

# plotting the NA Estimator
plot_estimator()

# getting a visualization of bandwidth and smoothness of the NA Estimator
smooth_analysis()

# smoothing the NA Estimator
naf.plot_hazard(bandwidth=10,figsize=(12,10),title="Smoother NA Estimator",label="NA Estimator")

# smoothing and comparing according to the column's entry
plot_function('Male','Female',3,'gender')
plot_function('Yes','No',3,'PhoneService')
plot_function('Yes','No',3,'Partner')
plot_function('Yes','No',3,'Dependents')
plot_function('Yes','No',3,'PaperlessBilling')


from sympy import FiniteSet

def probability( space, event ):
	return len( event ) / len( space )

def check_prime( number ):
	if number != 1:
		for factor in range( 2, number ):
			if number % factor == 0:
				return False
	else:
		return False
	return True

if __name__ == '__main__':

	space = FiniteSet( *range( 1, 21 ) )

	primes = []

	for num in space:
		if check_prime( num ):
			primes.append( num )

	event = FiniteSet( *primes )

	p = probability( space, event )

	print( 'Sample Space: {0}'.format( space ) )
	print( 'Event: {0}'.format( event ) )
	print( 'Probability of rolling a prime: {0:.5f}'.format( p ) )

# P( A | B )

s = FiniteSet( 1, 2, 3, 4, 5, 6 )
a = FiniteSet( 2, 3, 5 )
b = FiniteSet( 1, 3, 5 )

e = a.union( b )

probability( s, e )

# P( A & B )

s = FiniteSet( 1, 2, 3, 4, 5, 6 )
a = FiniteSet( 2, 3, 5 )
b = FiniteSet( 1, 3, 5 )

e = a.intersect( b )

probability( s, e )


import numpy as np
import matplotlib.pyplot as plot

def tma(matrix, d, shape):
    a, b, c = zip(*matrix)
    p = [-c[0] / b[0]]
    q = [d[0] / b[0]]
    x = [0] * (shape + 1)
    for i in range(1, shape):
        p.append(-c[i] / (b[i] + a[i] * p[i - 1]))
        q.append((d[i] - a[i] * q[i - 1]) / (b[i] + a[i] * p[i - 1]))
    for i in reversed(range(shape)):
        x[i] = p[i] * x[i + 1] + q[i]
    return x[:-1]

def spline(x, f):
    n = len(x)
    h = [x[i] - x[i - 1] for i in range(1, n)]
    tridiag_matrix = [[0, 2 * (h[0] + h[1]), h[1]]]
    b = [3 * ((f[2] - f[1]) / h[1] - (f[1] - f[0]) / h[0])]
    for i in range(1, n - 3):
        tridiag_row = [h[i], 2 * (h[i] + h[i + 1]), h[i + 1]]
        tridiag_matrix.append(tridiag_row)
        b.append(3 * ((f[i + 2] - f[i + 1]) / h[i + 1] - (f[i + 1] - f[i]) / h[i]))
    tridiag_matrix.append([h[-2], 2 * (h[-2] + h[-1]), 0])
    b.append(3 * ((f[-1] - f[-2]) / h[-1] - (f[-2] - f[-3]) / h[-2]))
    c = tma(tridiag_matrix, b, n - 2)
    a = []
    b = []
    d = []
    c.insert(0, 0)
    for i in range(1, n):
        a.append(f[i - 1])
        if i < n - 1:
            d.append((c[i] - c[i - 1]) / (3 * h[i - 1]))
            b.append((f[i] - f[i - 1]) / h[i - 1] -
                     h[i - 1] * (c[i] + 2 * c[i - 1]) / 3)
    b.append((f[-1] - f[-2]) / h[-1] - 2 * h[-1] * c[-1] / 3)
    d.append(-c[-1] / (3 * h[-1]))
    return a, b, c, d

def get_points():
    n = int(input())
    x = list(map(float, input().split()))
    f = list(map(float, input().split()))
    x_test = float(input())

    return x, f, x_test

def polyval(x0, x, k, coef):
    a, b, c, d = coef
    diff = x0 - x[k]

    return a[k] + b[k] * diff + c[k] * diff ** 2 + d[k] * diff ** 3

def poly(x, x_test, coef):
    k = 0
    for i, j in zip(x, x[1:]):
        if i <= x_test <= j:
            break
        k += 1

    return polyval(x_test, x, k, coef)

def main():
    x, f, x_test = get_points()
    coef = spline(x, f)

    res = poly(x, x_test, coef)
    print(f'x* = {x_test}')
    print(f'f(x*) = {res}')

    x_vals = np.linspace(x[0], x[-1])
    y = [poly(x, val, coef) for val in x_vals]

    plot.figure()
    plot.xlabel('x')
    plot.ylabel('y')
    plot.plot(x_vals, y, 'g')
    plot.grid(True)
    plot.show()

if __name__ == "__main__":
    main()


import numpy as np
import numpy.linalg as LA

class PCAnalyser():

    def __init__(self, X: np.ndarray, data_centered=False) -> None:
        if data_centered:
            self.X = X
        else:
            self.X = X - np.mean(X, axis=0)
        self.Sigma = self.X.T @ self.X
        self.eigs = np.array([])
        return

    def analyse(self, k = 10):
        if k > self.Sigma.shape[0]: return

        eig_vals, eig_vecs = LA.eigh(self.Sigma)
        idx = np.argsort(eig_vals)[::-1]
        self.eigs = eig_vals[idx]
        eig_vecs = eig_vecs[:,idx]
        PCs = eig_vecs[:,0:k]

        return self.eigs, PCs

    def calc_PVE(self, m=10, individual=False):
        m = np.clip(m, 0, len(self.eigs))
        if individual:
            return self.eigs[m] / sum(self.eigs) # PVE(m)
        return sum(self.eigs[:m+1]) / sum(self.eigs) # PVE(first m)


import numpy as np
from bisect import bisect_right

def Sk(X, Y, k):
    """
    Create Sk function

    Parameters
    ----------
        X : list
            list of x values
        Y : list
            list of y values
        k : int
            index from X and Y

    Returns
    -------
        function
            Sk from linear spline
    """

    def f(x):
        if(X[k + 1] != X[k]):
            return Y[k] * ((X[k + 1] - x)/(X[k + 1] - X[k])) + Y[k + 1] * ((x - X[k])/(X[k + 1] - X[k]))

    return f

def linear_spline(X, Y):
    """
    Polynomal Interpolation by parts using a linear spline

    Parameters
    ----------
        X : list
            list of x values
        Y : list
            list of y values

    Returns
    -------
        function
            function of interpolation by parts (using linear spline)
    """

    n = len(X)

    S = [Sk(X, Y, k) for k in range(n)]

    def f(x):
        k =  bisect_right(X, x) - 1
        if k == (n - 1):
            k -= 1
        return S[k](x)

    return f


import time
import numpy as np
np.set_printoptions(suppress=True, linewidth=np.inf, precision=12)
print("========================================================================")
print("Chương Trình Tìm nghịch đảo của ma trận bằng phương pháp viền quanh")
print("========================================================================")

#Nhập ma trận
a = np.loadtxt('test1.txt')

det = np.linalg.det(a)
if det == 0:
    print("Ma trận không khả nghịch!!!")
    quit()
n = len(a)
b = np.transpose(a)
t = b.dot(a)
print("Ma trận vừa nhập là:")
print(a)
print("========================================================================")
def bordering(a, n):# Nhap ma tran a va kich thuoc cua ma tran

    if n == 2: #Trường hợp chặn đệ quy
        inv = np.zeros([2,2]) #Khởi tạo ma trận nghịch đảo alpha^-1
        inv[0,0] = a[1,1]/(a[0,0] * a[1,1] - a[1,0]*a[0, 1])
        inv[0,1] = -a[0 ,1]/(a[0,0] * a[1,1] - a[1,0]*a[0, 1])
        inv[1,0] = -a[1,0]/(a[0,0] * a[1,1] - a[1,0]*a[0, 1])
        inv[1,1] = a[0,0]/(a[0,0] * a[1,1] - a[1,0]*a[0, 1])
        return inv
    a_11 = np.zeros([n - 1, n - 1])  # Khởi tạo các ma trận con
    a_12 = np.zeros([n - 1, 1])
    a_21 = np.zeros([1, n - 1])
    a_22 = a[n - 1, n - 1]
    for i in range(0, n - 1):
        for j in range(0, n - 1):
            a_11[i, j] = a[i, j]
    for i in range(0, n - 1):
        a_12[i, 0] = a[i, n - 1]
        a_21[0, i] = a[n - 1, i]
    if n>2: #Nếu chưa gặp trường hợp chặn
        a_111 = bordering(a_11, n-1)
        X = a_111.dot(a_12)
        Y = a_21.dot(a_111)
        theta = a_22 - Y.dot(a_12)
        x = X.dot(Y)
        for i in range(0, n - 1):
            for j in range(0, n - 1):
                a[i, j] = a_111[i, j] + x[i, j] / theta
                a[n - 1, i] = Y[0, i] / (-theta)
                a[i, n - 1] = -X[i, 0] / theta
        a[n - 1, n - 1] = 1 / theta
    return a
st = time.time()
s = bordering(t, n)
et = time.time()
print("========================================================================")
print("Ma trận nghịch đảo là:")
print(s.dot(b))
print("========================================================================")
print((et-st)*1000,'ms')
print("========================================================================")

Topic 2.5 - Matrix Inversion/Bordering/Bordering.py


import parser
from math import *
import numpy as np

class mathfunc:

    '''
    Create a mathfunc object by inputting a function of x in pythonic notation

    examples:

        2*x**2*sin(x)

        x**3+3*x**2+5

        3*pi*log(x)

    '''

    def __init__(self, equation):
        try:
            parser.expr(equation).compile()
        except:
            raise Exception('The mathematical expression could not be parsed')

        self.equation = equation

    def __str__(self):
        return self.equation

    def evaluate(self, value):

        '''
        Evaluates the mathfunc object at a given value

        returns float

        '''
        x = value
        formula = parser.expr(self.equation).compile()
        return float(eval(formula))

    def midpoint_rule(self,a,b,num_points):

        '''
        Evaluates the integral of the mathfunc object using the midpoint method

        Integration start point: a

        Integration end point: b

        Number of evenly spaced points from a to b: num_points

        returns float

        '''

        x_grid = np.linspace(a,b,num_points)
        midpoint = (x_grid[1]-x_grid[0])/2
        delta_x = x_grid[1]-x_grid[0]
        area = 0

        for i in range(0,len(x_grid)-1):
            area += (self.evaluate(x_grid[i]+midpoint))*delta_x

        return float(area)

    def trapezoidal_rule(self,a,b,num_points):

        '''
        Evaluates the integral of the mathfunc object using the trapezoid method

        Integration start point: a

        Integration end point: b

        Number of evenly spaced points from a to b: num_points

        returns float

        '''
        x_grid = np.linspace(a,b,num_points)
        delta_x = x_grid[1]-x_grid[0]
        area = 0

        for i in range(0,len(x_grid)-1):
            area += (1/2)*(self.evaluate(x_grid[i])+self.evaluate(x_grid[i+1]))*delta_x

        return float(area)

    def simpsons_rule(self,a,b,num_points):

        '''
        Evaluates the integral of the mathfunc object using Simpson's method

        Integration start point: a

        Integration end point: b

        Number of evenly spaced points from a to b: num_points

        num_points must be odd for simpsons rule

        returns float

        '''

        if num_points % 2 == 0:
            raise Exception('The number of points in the grid must be odd for Simpson\'s method')
        else:
            x_grid = np.linspace(a,b,num_points)

        delta_x = x_grid[1]-x_grid[0]
        area = delta_x*(self.evaluate(a)+self.evaluate(b))/3

        for i in range(1,len(x_grid)-1):
            if i % 2 == 1:
                area += 4*delta_x*self.evaluate(x_grid[i])/3
            elif i % 2 == 0:
                area += 2*delta_x*self.evaluate(x_grid[i])/3

        return float(area)


import numpy as np

class LogReg_binary:
    """ Binary Logistic classifier
        This class implements regularized binary logistic regression
        and ues Maximum likelihood Estimation for loss function

        Parameter
        ---------
        n_iter: int
        number of iteration Gradient Descent

        learning_rate: float
        alpha in Gradient Descent Algorithm

        reg: float
        lambda in regularization

        report: bool
        showing report of each iter in Gradient Descent

        Return
        ------
        predict values for test data

        Examples
        --------
        >>> clf = LogReg_binary()
        >>> clf.fit(x, y)
        >>> y_pred = clf.predict(x_test)
    """

    def __init__(self, n_iter=1000, learning_rate=0.00001, reg=0.0, report=True):
        self.W = None
        self.lr = learning_rate
        self.n_iter = n_iter
        self.reg = reg
        self.report = report

    @staticmethod
    def simplification(x):
        return np.concatenate((np.ones((x.shape[0], 1)), x), axis=1)

    @staticmethod
    def sigmoid(y):
        return 1 / (1 + np.exp(-y))

    @staticmethod
    def StandardScaler(x):
        return (x - np.mean(x, axis=0)) / np.std(x, axis=0)

    # Optimize Method
    @staticmethod
    def gradient_descent(x, y_pred, y):
        return x.T @ (y_pred - y)

    # Loss Function
    def MLE(self, y, y_pred, w):

        error = -np.sum((y * np.log(y_pred)) + ((1 - y) * np.log(1 - y_pred)))
        return error + (self.reg * (w.T @ w)[0][0])

    def Probability(self, x, theta):
        y_predict = x @ theta
        return self.sigmoid(y_predict)

    def fit(self, x, y):
        # PreProcessing
        x = self.simplification(self.StandardScaler(x))
        y = y.reshape(-1, 1)
        w = 0.001 * np.random.randn(x.shape[1], 1)

        report_time = self.n_iter / 10
        for i in range(self.n_iter):
            # Loss
            y_pred = self.Probability(x, w)
            error = self.MLE(y, y_pred, w)
            # Optimize Weight
            gd = self.gradient_descent(x, y_pred, y)

            w[0] = w[0] - self.lr * gd[0]
            w[1:] = w[1:] - self.lr * (gd[1:] + self.reg * w[1:])
            # Report
            if self.report and i % report_time == 0:
                print('\terror in iter {} = {}'.format(i, error))

        self.W = w

    def predict(self, x_test):
        x = self.simplification(self.StandardScaler(x_test))
        probs = self.Probability(x, self.W)

        y_pred = np.zeros(shape=(x_test.shape[0],), dtype=int)
        y_pred[np.flatnonzero(probs > 0.5)] = 1
        return y_pred

    def score(self, x, y):
        y_pred = self.predict(x)
        return np.mean(y_pred == y)


"""A module with number theory functions necessary for other functions.
"""

import random
import sympy

def mod_exp(val, exp, modulus):
    """Computes an exponent in a modulus.

    Raises val to power exp in the modulus without overflowing.

    Args:
        val (int): Value we wish to raise the power of.
        exp (int): Exponent.
        modulus (int): Modulus where computation is performed.

    Returns:
        A value raised to a power in a modulus.
    """
    return pow(int(val), int(exp), int(modulus))

def mod_inv(val, modulus):
    """Finds an inverse in a given prime modulus.

    Finds the inverse of val in the modulus.

    Args:
        val (int): Value to find the inverse of.
        modulus (int): Modulus where computation is performed.
            Note: MUST BE PRIME.

    Returns:
        The inverse of the given value in the modulus.
    """
    return mod_exp(val, modulus - 2, modulus)

def find_generator(modulus):
    """Finds a generator in the given modulus.

    Finds a generator, or primitive root, in the given prime modulus.

    Args:
        modulus (int): Modulus to find the generator in. Note: MUST
            BE PRIME.

    Returns:
        A generator, or primitive root in the given modulus.
    """
    return sympy.ntheory.primitive_root(modulus)

def root_of_unity(order, modulus):
    """Finds a root of unity in the given modulus.

    Finds a root of unity with the given order in the given prime modulus.

    Args:
        order (int): Order n of the root of unity (an nth root of unity).
        modulus (int): Modulus to find the root of unity in. Note: MUST BE
            PRIME

    Returns:
        A root of unity with the given order in the given modulus.
    """
    if ((modulus - 1) % order) != 0:
        raise ValueError('Must have order q | m - 1, where m is the modulus. \
            The values m = ' + str(modulus) + ' and q = ' + str(order) + ' do not satisfy this.')

    generator = find_generator(modulus)
    if generator is None:
        raise ValueError('No primitive root of unity mod m = ' + str(modulus))

    result = mod_exp(generator, (modulus - 1)//order, modulus)

    if result == 1:
        return root_of_unity(order, modulus)

    return result

def is_prime(number, num_trials=200):
    """Determines whether a number is prime.

    Runs the Miller-Rabin probabilistic primality test many times on the given number.

    Args:
        number (int): Number to perform primality test on.
        num_trials (int): Number of times to perform the Miller-Rabin test.

    Returns:
        True if number is prime, False otherwise.
    """
    if number < 2:
        return False
    if number != 2 and number % 2 == 0:
        return False

    # Find largest odd factor of n-1.
    exp = number - 1
    while exp % 2 == 0:
        exp //= 2

    for _ in range(num_trials):
        rand_val = int(random.SystemRandom().randrange(1, number))
        new_exp = exp
        power = pow(rand_val, new_exp, number)
        while new_exp != number - 1 and power != 1 and power != number - 1:
            power = (power * power) % number
            new_exp *= 2
        if power != number - 1 and new_exp % 2 == 0:
            return False

    return True


# coding: utf-8

# # 4-Linear Algebra

# In[1]:

from scipy import *
from scipy.linalg import *
from matplotlib.pyplot import *
get_ipython().magic('matplotlib inline')

# ## Overview
#
# ### Vectors and Matrices

# In[2]:

v = array([1.,2.,3.])

# In[3]:

# two vectors with three components
v1 = array([1., 2., 3.])
v2 = array([2, 0, 1.])

# In[4]:

2*v1 # array([2., 4., 6.])

# In[5]:

v1/2 # array([0.5, 1., 1.5])

# In[6]:

# linear combinations
3*v1 #  array([ 3.,  6.,  9.])

# In[7]:

3*v1 + 2*v2  # array([  7.,   6.,  11.])

# In[8]:

# norm
norm(v1) #  3.7416573867739413

# In[9]:

# scalar product
dot(v1, v2) # 5.

# In[10]:

v1 @ v2  #  5 ; alternative formulation

# In[11]:

# elementwise operations:
v1 * v2 # array([2., 0., 3.])

# In[12]:

v2 / v1 # array([2.,0.,.333333])

# In[13]:

v1 - v2 # array([-1.,  2.,  2.])

# In[14]:

v1 + v2 # array([ 3.,  2.,  4.])

# In[15]:

cos(v1) # cosine, elementwise: array([ 0.5403, -0.4161, -0.9899])

# In[16]:

M = array([[1.,2],[0.,1]])

# ### Indexing and Slices

# In[17]:

v = array([1.,2.,3])
M = array([[1.,2],[3.,4]])

v[0] # works as for lists

# In[18]:

v[1:] # array([2.,3.])

# In[19]:

M[0,0] # 1.

# In[20]:

M[1:] # returns the matrix array([[3.,4]])

# In[21]:

M[1] # returns the vector array([3.,4.])

# In[22]:

# access
v[0] # 1.

# In[23]:

v[0] = 10

# In[24]:

# slices
v[:2] # array([10., 2.])

# In[25]:

v[:2] = [0, 1] # now v == array([0.,1.,3.])
v

# In[26]:

v[:2] = [1,2,3] # error!

# ### Linear Algebra Operations

# In[27]:

v = array([1.,2.])
M = array([[1.,2],[3.,4]])
dot(M,v) # matrix vector multiplication; returns a vector

# In[28]:

M @ v # alternative formulation

# In[29]:

w = array([7.,0.])
dot(v,w) # scalar product; the result is a scalar

# In[30]:

v @ w # alternative formulation

# In[31]:

N = array([[3., 8.],[0., 1.]])
dot(M,N) # results in a matrix

# In[32]:

M @ N # alternative formulation

# #### Solving a Linear System

# In[33]:

import scipy.linalg as sl
A = array([[1., 2.],
           [3., 4.]])
b = array([1., 4.])
x = sl.solve(A,b)

# In[34]:

allclose(dot(A, x), b) # True

# In[35]:

allclose(A @ x, b)  # alternative formulation

# ## Mathematical Preliminaries
# ### The dot operations

# In[36]:

angle = pi/3
M = array([[cos(angle), -sin(angle)],
           [sin(angle), cos(angle)]])
v = array([1., 0.])
y = dot(M, v)
y

# ## The Array Type
# ### Array Properties

# In[37]:

A=array([[1,2,3],[3,4,6]])
A.shape  # (2, 3)

# In[38]:

A.dtype  #  dtype('int64')

# In[39]:

A.strides    # (24, 8)

# ### Creating Arrays from Lists

# In[40]:

V = array([1., 2., 1.], dtype=float)
V

# In[41]:

V = array([1., 2., 1.], dtype=complex)
V

# In[42]:

V = array([1, 2]) # [1, 2] is a list of integers
V.dtype # int

# In[43]:

V = array([1., 2]) # [1., 2] mix float/integer
V.dtype # float

# In[44]:

V = array([1. + 0j, 2.]) # mix float/complex
V.dtype # complex

# In[45]:

a = array([1, 2, 3])
a[0] = 0.5
a   # now: array([0, 2, 3])

# In[46]:

# the identity matrix in 2D
Id = array([[1., 0.], [0., 1.]])
# Python allows this:
Id = array([[1., 0.],
            [0., 1.]])
# which is more readable

# ### Vectors and Matrices

# In[47]:

v = array([1., 2., 1.])
shape(v)

# In[48]:

R = array([[1.,2.,1.]]) # notice the double brackets: this is a matrix
shape(R) # (1,3): this is a row matrix

# In[49]:

C = array([1.,2.,1.]).reshape(3,1)
shape(C) # (3,1): this is a column matrix

# ## Array Indexing
# ### Accessing Array Entries

# In[50]:

M = array([[1., 2.],[3.,4.]])
M[0,0] # first row, first column: 1.

# In[51]:

M[-1,0] # last row, first column: 3.

# In[52]:

v = array([1., 2., 3.])
v1 = v[:2] # v1 is array([1.,2.])
v1

# In[53]:

v1[0] = 0. # if v1 is changed ...
v1

# In[54]:

v #   ... v is changed too: array([0., 2., 3.])

# ### Altering an array using slices

# In[55]:

M = array([[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]])
shape(M)

# In[56]:

M[1,2] = 2.0 # scalar
M

# In[57]:

M[2,:] = [1.,2.,3.] # vector
M

# In[58]:

M[1:3,:] = array([[1.,2.,3.],[-1.,-2.,-3.]]) # matrix
M

# In[59]:

M[1:4,2:3] = array([[1.],[0.],[-1.0]]) # column matrix
M

# ## Functions to Construct Arrays

# In[60]:

identity(3)

# ## Accessing and Changing the Shape

# In[61]:

M = identity(3)
shape(M) # (3, 3)

# In[62]:

v = array([1., 2., 1., 4.])
shape(v) # (4,) <- singleton (1-tuple)

# In[63]:

M = array([[1.,2.]])
shape(M) # (1,2)

# In[64]:

M.shape # (1,2)

# In[65]:

shape(1.) # ()

# In[66]:

shape([1,2]) # (2,)

# In[67]:

shape([[1,2]]) # (1,2)

# ### Number of Dimensions

# In[68]:

A=rand(2,3)
ndim(A) # 2

# In[69]:

A.ndim

# In[70]:

T = zeros((2,2,3)) # tensor of shape (2,2,3); three dimensions
ndim(T) # 3

# In[71]:

len(shape(T)) # 3

# ### Reshape

# In[72]:

v = array([0,1,2,3,4,5])
M = v.reshape(2,3)
M

# In[73]:

shape(M)  # returns (2,3)

# In[74]:

M[0,0] = 10 # now v[0] is 10
v

# In[75]:

v = array([1,2,3,4,5,6,7,8])
M = v.reshape(2,-1)
shape(M)   # returns (2,4)

# In[76]:

M = v.reshape(-1,2)
shape(M)   # returns (4,2)

# In[77]:

M = v.reshape(3,-1) # returns error

# ### Transpose

# In[78]:

A = rand(3,4)
shape(A) # 3,4

# In[79]:

B = A.T # A transpose
shape(B) # 4,3

# In[80]:

v = array([1.,2.,3.])
v.T # exactly the same vector!

# In[81]:

v.reshape(-1,1) # column matrix containing v

# In[82]:

v.reshape(1,-1) # row matrix containing v

# # Stacking
#
# ### Stacking vectors

# In[83]:

# v is supposed to have an even length.
def symp(v):
  n = len(v) // 2 # use the integer division //
  return hstack([v[-n:], -v[:n]])
v = arange(1,5)
symp(v)

# # Functions Acting on Arrays
#
# ## Universal Functions
#
# ### Built-in Universal Functions

# In[84]:

cos(pi) # -1

# In[85]:

cos(array([[0, pi/2, pi]])) # array([[1, 0, -1]])

# In[86]:

2 * array([2, 4]) # array([4, 8])
array([1, 2]) * array([1, 8]) # array([1, 16])

# In[87]:

array([1, 2])**2 # array([1, 4])

# In[88]:

2**array([1, 2]) # array([1, 4])

# In[89]:

array([1, 2])**array([1, 2]) # array([1, 4])

# #### Create Universal Functions: `vectorize`

# In[90]:

def const(x):
    return 1
const(array([0, 2])) # returns 1 instead of array([1, 1])

# In[91]:

def heaviside(x):
    if x >= 0:
        return 1.
    else:
        return 0.
heaviside(array([-1, 2])) # error

# In[92]:

vheaviside = vectorize(heaviside)
vheaviside(array([-1,2])) # array([0,1]) as expected

# In[93]:

xvals=linspace(-1,1,100)
plot(xvals,vectorize(heaviside)(xvals))
axis([-1.5,1.5,-0.5,1.5])

# ### Array Functions: `axis` Argument

# In[94]:

A = arange(1,9).reshape((2,-1))
A

# In[95]:

sum(A) # 36

# In[96]:

sum(A, axis=0) # array([ 6,  8, 10, 12])

# In[97]:

A.sum(axis=1) # array([10, 26])

# ## Linear Algebra Methods in SciPy

# In[98]:

import scipy.linalg as sl
A = array([[1,2], [-6,4]])
[LU,piv] = sl.lu_factor(A)

# In[99]:

bi = array([1,1])
xi=sl.lu_solve((LU,piv),bi)
xi

# ### Solving a least squares problem with SVD

# In[100]:

import scipy.linalg as sl
A = array([[1, 2], [-6, 4], [0, 8]])
b = array([1,1,1])
[U1, Sigma_1, VT] = sl.svd(A, full_matrices = False, compute_uv = True)
xast = dot(VT.T, dot(U1.T, b)/ Sigma_1)
r = dot(A, xast) - b  # computes the residual
nr = sl.norm(r, 2)     # computes the Euclidean norm of the residual
nr


# -*- coding: utf-8 -*-
"""
Created on Sun Jun 25 12:05:02 2017

@author: Orlando
"""

#R Squared Theory
#coeficiente de determinacion
from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style

style.use('fivethirtyeight')

def best_fit_slope_and_intercept(xy,ys):

    m = (((mean(xs)*mean(ys))-mean(xy*ys))/
         ((mean(xs)*mean(xs))-mean(xs*xs)))

    b = mean(ys)-m*mean(xs)
    return m,b

def squared_error(ys_orig,ys_line):
    return sum((ys_line-ys_orig)**2)

def coefficient_of_determination(ys_orig,ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_regr = squared_error(ys_orig,ys_line)
    squared_error_y_mean = squared_error(ys_orig,y_mean_line)
    return 1 - (squared_error_regr/squared_error_y_mean)

xs = np.array([1,2,3,4,5,6],dtype=np.float64)
ys = np.array([5,4,6,5,6,7],dtype=np.float64)

m,b = best_fit_slope_and_intercept(xs,ys)

regression_line = [ ((m*x)+b) for x in xs]

r_squared = coefficient_of_determination(ys,regression_line)

print(r_squared)

plt.scatter(xs,ys,label='data')

plt.plot(xs, regression_line,label='regression line')
plt.legend(loc=4)
plt.show()


import numpy as np
import pandas as pd

def accuracy_score(y_true, y_pred):
    """Classification performance metric compute the accuracy of y_true and y_pred
    :param numoy.array y_true: like a shape array
    :param numoy.array y_pred: like a shape array
    :return c (float) accuracy score"""

    correct = 0
    for true, pred in zip(y_true,y_pred):
        if true == pred:
            correct += 1
    accuracy = correct/len(y_true)
    return accuracy

def mse(y_true, y_pred, squared = True):#Mean squared error
    """Mean squared errir regression loss fucntion.
    Parameters
    :param numpy.array y_true: array like of shape (n samples,)"""
    y_true = np.array(y_true)
    y_pred = np.array(y_pred)
    errors = np.average((y_true - y_pred)**2, axis=0)
    if not squared:
        errors = np.sqrt(errors)
    return np.average(errors)

def mse_prime(y_true, y_pred):
    return 2*(y_pred-y_true)/y_true.size

def cross_entropy(y_true, y_pred):
    return -(y_true * np.log(y_pred)).sum()

def cross_entropy_prime(y_true, y_pred):
    return y_pred - y_true

def r2_score(y_true, y_pred):
    """
    R^2 regression score function.
        R^2 = 1 - SS_res / SS_tot
    where SS_res is the residual sum of squares and SS_tot is the total
    sum of squares.
    :param numpy.array y_true : array-like of shape (n_samples,) Ground truth (correct) target values.
    :param numpy.array y_pred : array-like of shape (n_samples,) Estimated target values.
    :returns: score (float) R^2 score.
    """
    # Residual sum of squares.
    numerator = ((y_true - y_pred) ** 2).sum(axis=0)
    # Total sum of squares.
    denominator = ((y_true - np.average(y_true, axis=0)) ** 2).sum(axis=0)
    # R^2.
    score = 1 - numerator / denominator
    return score

class ConfusionMatrix:

    def __call__(self, true_y, pred_y):
        self.true = np.array(true_y)
        self.pred = np.array(pred_y)
        return self.to_df()

    def calc(self):
        cm = pd.crosstab(self.true, self.pred, rownames=["Actual Values"], colnames=["Predicted Values"], margins=True)
        return cm

    def to_df(self):
        return pd.DataFrame(self.calc())


import numpy as np
import matplotlib.pyplot as plt
import math
from scipy.stats import multivariate_normal
from scipy.optimize import minimize

def heatmap_gaussian(w0_mesh, w1_mesh, z, title_str= None, point=None) :
    plt.clf()
    plt.pcolormesh(w0_mesh, w1_mesh, z, cmap='jet')
    plt.colorbar()
    if(point != None):
        plt.plot(point[0], point[1],'bo')
    plt.xlabel("w_0")
    plt.ylabel("w_1")
    plt.title(title_str)
    plt.show()

def plot_samples(mean, cov, title_str= None, point=None):
    w_sample = multivariate_normal.rvs(mean, cov, 20)
    plt.clf()
    x = np.linspace(-1, 1, 50)
    for i in w_sample:
        y = i[0] + i[1]*x
        ind = np.logical_and(y<=1, y>=-1)
        plt.plot(x[ind], y[ind], 'r-')
    if(point!=None):
        plt.plot(point[0], point[1], 'bo')
    plt.xlabel("x")
    plt.ylabel("y")
    plt.title(title_str)
    plt.show()

def posterior(x, y, alpha = 2, beta = 25):
    x = np.vstack((np.ones(x.shape), x))
    s_n = np.linalg.inv(alpha + beta*np.matmul(x,np.transpose(x)))
    m_n = np.dot(s_n, beta*np.matmul(x,y.reshape(-1,1)))
    return(m_n.flatten(), s_n)

# ground truth and training data generation
np.random.seed(15)
w0_true = -0.3
w1_true = 0.5
noise =  np.random.normal(0, 0.2, 20)
x_train = np.random.uniform(-1,1,20)
y_train = (w0_true + x_train*w1_true + noise)

##########################################################################################
###################################### PART A ############################################
##########################################################################################
#parameters to plot the graph
grid_len = 1000
w0_range = [-1,1]
w1_range = [-1,1]

w0 = np.linspace(w0_range[0], w0_range[1], grid_len)
w1 = np.linspace(w1_range[0], w1_range[1], grid_len)
w0_mesh,w1_mesh=np.meshgrid(w0,w1)
W = np.transpose(np.vstack((w0_mesh.flatten(), w1_mesh.flatten())))

# plotting the prior
prior = multivariate_normal.pdf(W, [0,0], [[2,0],[0, 2]]).reshape(grid_len, grid_len)
heatmap_gaussian(w0_mesh, w1_mesh, prior, "Prior distribution")
# plotting the curves drawn from samples
plot_samples([0,0], [[2,0],[0, 2]], "w drawn from prior")

##########################################################################################
###################################### PART B ############################################
##########################################################################################
m_n, s_n = posterior(x_train[0], y_train[0])
pos_dist = multivariate_normal.pdf(W, m_n, s_n).reshape(grid_len, grid_len)
heatmap_gaussian(w0_mesh, w1_mesh, pos_dist, "Posterior distribution for {} examples".format(i), [w0_true, w1_true])
plot_samples(m_n, s_n, "w drawn from posterior for {} examples".format(i), [x_train[0], y_train[0]])

##########################################################################################
###################################### PART C ############################################
##########################################################################################
m_n, s_n = posterior(x_train[0:2], y_train[0:2])
pos_dist = multivariate_normal.pdf(W, m_n, s_n).reshape(grid_len, grid_len)
heatmap_gaussian(w0_mesh, w1_mesh, pos_dist, "Posterior distribution for {} examples".format(i), [w0_true, w1_true])
plot_samples(m_n, s_n, "w drawn from posterior for {} examples".format(i), [x_train[0:2], y_train[0:2]])

##########################################################################################
###################################### PART D ############################################
##########################################################################################
m_n, s_n = posterior(x_train[0:5], y_train[0:5])
pos_dist = multivariate_normal.pdf(W, m_n, s_n).reshape(grid_len, grid_len)
heatmap_gaussian(w0_mesh, w1_mesh, pos_dist, "Posterior distribution for {} examples".format(i), [w0_true, w1_true])
plot_samples(m_n, s_n, "w drawn from posterior for {} examples".format(i), [x_train[0:5], y_train[0:5]])

##########################################################################################
###################################### PART E ############################################
##########################################################################################
m_n, s_n = posterior(x_train, y_train)
pos_dist = multivariate_normal.pdf(W, m_n, s_n).reshape(grid_len, grid_len)
heatmap_gaussian(w0_mesh, w1_mesh, pos_dist, "Posterior distribution for {} examples".format(i), [w0_true, w1_true])
plot_samples(m_n, s_n, "w drawn from posterior for {} examples".format(i), [x_train, y_train])


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 13 20:17:53 2021

@author: Cristian Magalhães
João Marcos
Matheus Lopes
William Francisco Machado
"""

# AND

import numpy as np

def degrau(v):
    if v >=0:
        return 1
    else:
        return 0

def perceptron(x, w, b):
    v = np.dot(w, x) + b
    y = degrau(v)
    return y

def AND(x):
    w = np.array([1,1])
    bAND = -1.5
    return perceptron(x, w, bAND)

x0 = np.array([0,1])
x1 = np.array([1,1])
x2 = np.array([0,0])
x3 = np.array([1,0])

print("AND ({} + {}) = {}".format(0,1, AND(x0)))
print("AND ({} + {}) = {}".format(1,1, AND(x1)))
print("AND ({} + {}) = {}".format(0,0, AND(x2)))
print("AND ({} + {}) = {}".format(1,0, AND(x3)))

print("----------------------------------------------------------------------")

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

import numpy as np

def degrau(v):
    if v >=0:
        return 1
    else:
        return 0

def perceptron(x, w, b):
    v = np.dot(w, x) + b
    y = degrau(v)
    return y

def OR(x):
    w = np.array([1,1])
    b = -0.5
    return perceptron(x, w, b)

x0 = np.array([0,1])
x1 = np.array([1,1])
x2 = np.array([0,0])
x3 = np.array([1,0])

print("OR ({} + {}) = {}".format(0,1, OR(x0)))
print("OR ({} + {}) = {}".format(1,1, OR(x1)))
print("OR ({} + {}) = {}".format(0,0, OR(x2)))
print("OR ({} + {}) = {}".format(1,0, OR(x3)))

print("----------------------------------------------------------------------")

# NOR

import numpy as np

def degrau(v):
    if v >=0:
        return 1
    else:
        return 0

def perceptron(x, w, b):
    v = np.dot(w, x) + b
    y = degrau(v)
    return y

def NOT(x):
    wNOT = -1.0
    bNOT = 0.5
    return perceptron(x, wNOT, bNOT)

def OR(x):
    w = np.array([1,1])
    b = -0.5
    return perceptron(x, w, b)

def NOR(x):
    output_OR = OR(x)
    output_NOT = NOT(output_OR)
    return output_NOT

x0 = np.array([0,1])
x1 = np.array([1,1])
x2 = np.array([0,0])
x3 = np.array([1,0])

print("NOR ({} + {}) = {}".format(0,1, NOR(x0)))
print("NOR ({} + {}) = {}".format(1,1, NOR(x1)))
print("NOR ({} + {}) = {}".format(0,0, NOR(x2)))
print("NOR ({} + {}) = {}".format(1,0, NOR(x3)))

print("----------------------------------------------------------------------")
# NAND

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

import numpy as np

def degrau(v):
    if v >=0:
        return 1
    else:
        return 0

def perceptron(x, w, b):
    v = np.dot(w, x) + b
    y = degrau(v)
    return y

def NOT(x):
    wNOT = -1.0
    bNOT = 0.5
    return perceptron(x, wNOT, bNOT)

def AND(x):
    w = np.array([1,1])
    bAND = -1.5
    return perceptron(x, w, bAND)

def NAND(x):
    output_AND = AND(x)
    output_NOT = NOT(output_AND)
    return output_NOT

x0 = np.array([0,1])
x1 = np.array([1,1])
x2 = np.array([0,0])
x3 = np.array([1,0])

print("NAND ({} + {}) = {}".format(0,1, NAND(x0)))
print("NAND ({} + {}) = {}".format(1,1, NAND(x1)))
print("NAND ({} + {}) = {}".format(0,0, NAND(x2)))
print("NAND ({} + {}) = {}".format(1,0, NAND(x3)))


import numpy as np

def softmax_func(x):
    """
    Numerically stable softmax function. For more details
    about numerically calculations please refer:
    http://www.deeplearningbook.org/slides/04_numerical.pdf
    :param x:
    :return:
    """
    stable_values = x - np.max(x, axis=1, keepdims=True)
    return np.exp(stable_values) / np.sum(np.exp(stable_values), axis=1, keepdims=True)

def log_sum_exp(x):
    """
    log_sum_exp is a very useful function in machine learning.
    It can be seen in many places including cross-entropy error.
    However, the naive implementation is numerically unstable.
    Therefore, we use the following implementation. For more details
    please refer: http://www.deeplearningbook.org/slides/04_numerical.pdf
    :param x:
    :return:
    """
    mx = np.max(x, axis=1, keepdims=True)
    safe = x - mx
    return mx + np.log(np.sum(np.exp(safe), axis=1, keepdims=True))

# Following two methods were used in the initial version of the convolution operations.
# Later we introduced fast Cython versions of `im2col` and `col2im` implementations.
# Hence, these two methods are obsolete.
def im2col(image, filter_size=(3, 3), padding=(0, 0), stride=(1, 1)):
    M, C, h, w, = image.shape
    filter_height = filter_size[0]
    filter_width = filter_size[1]
    padding_height = padding[0]
    padding_width = padding[1]
    stride_height = stride[0]
    stride_width = stride[1]
    x_padded = np.pad(image, ((0, 0),
                              (0, 0),
                              (padding_height, padding_height),
                              (padding_width, padding_width)),
                      mode='constant')
    h_new = int((h - filter_height + 2 * padding_height) / stride_height + 1)
    w_new = int((w - filter_width + 2 * padding_width) / stride_width + 1)

    out = np.zeros((filter_width * filter_height * C, M * h_new * w_new), dtype=image.dtype)

    itr = 0
    for i in range(h_new):
        for j in range(w_new):
            for m in range(M):
                start_i = stride_height * i
                end_i = stride_height * i + filter_width
                start_j = stride_width * j
                end_j = stride_width * j + filter_height
                out[:, itr] = x_padded[m, :, start_i:end_i, start_j:end_j].ravel()
                itr += 1
    return out

def col2im(cols, x_shape, filter_size=(3, 3), padding=(0, 0), stride=(1, 1)):
    N, C, H, W = x_shape
    filter_height = filter_size[0]
    filter_width = filter_size[1]
    padding_height = padding[0]
    padding_width = padding[1]
    stride_height = stride[0]
    stride_width = stride[1]

    H_padded, W_padded = H + 2 * padding_height, W + 2 * padding_width
    x_padded = np.zeros((N, C, H_padded, W_padded), dtype=cols.dtype)

    idx = 0
    for i in range(0, H_padded - filter_height + 1, stride_height):
        for j in range(0, W_padded - filter_width + 1, stride_width):
            for m in range(N):
                col = cols[:, idx]
                col = col.reshape((C, filter_height, filter_width))
                x_padded[m, :, i:i + filter_height, j:j + filter_width] += col
                idx += 1
    if padding[0] or padding[1] > 0:
        return x_padded[:, :, padding_height:-padding_height, padding_width:-padding_width]
    else:
        return x_padded


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 25 18:16:26 2017

@author: ratnadeepb
@License: MIT
"""

'''
This module defines a single function proj(u, v). This function returns the
orthoginal projection of the vector u on the vector v.

Similar, projections in other vector spaces can be obtained by using
corresponding inner product definitions.

Please look at the InnerProductSPaces.Examples folder for more on such functions
'''

# System Imports
import numpy as np
import sys

# Local Imports
from InnerProductSpaces.dot import dot
from InnerProductSpaces.norm import norm

def proj(u, v):
    try:
        u = np.array(u)
        v = np.array(v)
    except:
        sys.exit("A list, tuple or numpy array is expected")

    return dot(u, v) * v / np.square(norm(v))

if __name__ == "__main__":
    u = [1, 2]
    v = [3, 4]
    print("The projection of u on v is: ", proj(u, v))


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

# External dependency
import numpy as np

#
# Compute the mean and the gaussian curvature of a depth map
#
# Reference :
#
#   Computation of Surface Curvature from Range Images Using Geometrically Intrinsic Weights
#   T. Kurita, P. Boulanger.
#   In MVA (pp. 389-392) 1992
#
def GetCurvaturesFromSlopes( Zx, Zy ) :
	# Compute the gradients
	Zxy, Zxx = np.gradient( Zx )
	Zyy, _ = np.gradient( Zy )
	# Mean Curvature
	H  = ( 1 + Zx ** 2 ) * Zyy + ( 1 + Zy ** 2 ) * Zxx - 2 * Zx * Zy * Zxy
	H /= ( 2 * ( 1 + Zx ** 2 + Zy ** 2 ) ) ** 1.5
	# Gaussian Curvature
	K  = Zxx * Zyy - Zxy ** 2
	K /= ( 1 + Zx ** 2 + Zy ** 2 ) ** 2
	# Return the different curvatures
	return H, K

def GetCurvatures( Z ) :
	# Compute the gradients
	Zy, Zx = np.gradient( Z )
	Zxy, Zxx = np.gradient( Zx )
	Zyy, _ = np.gradient( Zy )
	# Mean Curvature
	H  = ( 1 + Zx ** 2 ) * Zyy + ( 1 + Zy ** 2 ) * Zxx - 2 * Zx * Zy * Zxy
	H /= ( 2 * ( 1 + Zx ** 2 + Zy ** 2 ) ) ** 1.5
	# Gaussian Curvature
	K  = Zxx * Zyy - Zxy ** 2
	K /= ( 1 + Zx ** 2 + Zy ** 2 ) ** 2
	# Principle Curvatures
#	Pmin = H - np.sqrt( H ** 2 - K )
#	Pmax = H + np.sqrt( H ** 2 - K )
	# Return the different curvatures
	return H, K

#
# Compute the mean and the gaussian simplified curvature of a depth map
#
# Reference :
#
#   Simplified Gaussian and mean curvatures to range image segmentation
#   C. Zhao, D. Zhao, Y. Chen
#   In Proceedings of the 13th IEEE International Conference on Pattern Recognition (Vol. 2, pp. 427-431) 1996
#
def GetCurvaturesSimplified( Z ) :
	# Compute the gradients
	Zy, Zx = np.gradient( Z )
	Zxy, Zxx = np.gradient( Zx )
	Zyy, _ = np.gradient( Zy )
	# Simplified Mean Curvature
	H = Zxx + Zyy
	# Simplified Gaussian Curvature
	K = Zxx * Zyy - ( Zxy ** 2 )
	# Return the different curvatures
	return H, K


'''
Sampling out of the Binomial distribution
100xp

Compute the probability mass function for the number of defaults we would expect
for 100 loans as in the last section, but instead of simulating all of the Bernoulli
trials, perform the sampling using np.random.binomial(). This is identical to the
calculation you did in the last set of exercises using your custom-written
perform_bernoulli_trials() function, but far more computationally efficient. Given
this extra efficiency, we will take 10,000 samples instead of 1000. After taking the
samples, plot the CDF as last time. This CDF that you are plotting is that of the
Binomial distribution.

Note: For this exercise and all going forward, the random number generator is pre-seeded
for you (with np.random.seed(42)) to save you typing that each time.

Instructions
-Draw samples out of the Binomial distribution using np.random.binomial(). You should use
parameters n = 100 and p = 0.05, and set the size keyword argument to 10000.
-Compute the CDF using your previously-written ecdf() function.
-Plot the CDF with axis labels. The x-axis here is the number of defaults out of 100 loans,
while the y-axis is the CDF.
-Show the plot.
'''
import numpy as np
import matplotlib.pyplot as plt

def ecdf(data):
    """Compute ECDF for a one-dimensional array of measurements."""

    # Number of data points: n
    n = len(data)

    # x-data for the ECDF: x
    x = np.sort(data)

    # y-data for the ECDF: y
    y = np.arange(1, n + 1) / n

    return x, y

# Seed random number generator
np.random.seed(42)

# Take 10,000 samples out of the binomial distribution: n_defaults
n_defaults = np.random.binomial(n=100, p=0.05, size=10000)

# Compute CDF: x, y

x, y = ecdf(n_defaults)

# Plot the CDF with axis labels
_ = plt.plot(x, y, marker='.', linestyle='none')
_ = plt.xlabel('Defaults out of 100')
_ = plt.ylabel('CDF')

# Show the plot
plt.show()


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

# # Machine Learning using Logistic Regression
#
# Classifier model that estimates an applicant’s probability of admission based the scores from those two exams.

# In[1]:

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

# In[2]:

data = pd.read_csv('ex2data1.txt', names = ['score1', 'score2', 'admitted'])
data = data.astype('float128')
print('Profiling Data')
print(data.info())
print(data.head())

# profile = pandas_profiling.ProfileReport(df)
# profile.to_file(outputfile="output.html")

# In[3]:

x = np.array(data[['score1', 'score2']]) # training set
y = np.array(data['admitted']) # labels
[m, n] = np.shape(x)

# ## ==================== Part 1: Plotting ====================

# In[4]:

print('Plotting data with "x" indicating (y = 1) examples and "o" indicating (y = 0) examples.')
sns.scatterplot('score1', 'score2', hue='admitted', style='admitted', data=data)

# ## =========== Part 2: Mapping Features ============
#
# One way to fit the data better is to create more features from each data point. We will map the features into all polynomial terms of $x_1$ and $x_2$ up to the sixth power.
#
# As a result of this mapping, our vector of two features (the scores on two QA tests) has been transformed into a 28-dimensional vector. A logistic regression classifier trained on this higher-dimension feature vector will have a more complex decision boundary and will appear nonlinear when drawn in our 2-dimensional plot.
#
# While the feature mapping allows us to build a more expressive classifier, it also more susceptible to overfitting. To void that we will implement regularized logistic regression to fit the data and combat the overfitting problem.

# In[5]:

"""
Normalizes the features in X
returns a normalized version of X where the mean value of each feature is 0 and the standard deviation is 1.
This is often a good preprocessing step to do when working with learning algorithms.

First, for each feature dimension, compute the mean of the feature and subtract it from the dataset,
storing the mean value in mu. Next, compute the  standard deviation of each feature and divide
each feature by it's standard deviation, storing the standard deviation in sigma.
"""
def featureNormalize(X):
    X_norm = np.zeros(np.shape(X))
    qty_features = np.shape(X)[1]
    mu = np.zeros(qty_features)
    sigma = np.zeros(qty_features)

    for i in range(qty_features):
        mu[i] = np.mean(X[:,i])
        X_norm[:,i] = X[:,i] - mu[i]

        # by default np.std calculate the population std, here we want sample std (as done in Octave)
        sigma[i] = np.std(X_norm[:,i], ddof=1) # default: ddof=0 (N - 0) will predict 293092.21273075533
        X_norm[:,i] = X_norm[:,i] / sigma[i]
    return X_norm, mu, sigma

# In[6]:

# Tessting Feature Normalization
X_ = np.array([
    [2104, 3],
    [1600, 3],
    [2400, 3],
    [1416, 2],
    [3000, 4]
])
X_n, mu_, sigma_ = featureNormalize(X_)
print("X Norm", X_n)
print("mu", mu_) # [2104.    3.]
print("sigma", sigma_) # [635.96226303   0.70710678]

# In[7]:

# Scale features and set them to zero mean
print('Normalizing Features ...\n')
# We normalize to avoid log(0) because our sigmoid will return small values
X, mu, sigma = featureNormalize(x)

print('Normalized data')
X[:5]

# In[8]:

# Add a column of ones to X to facilitate the manipulation
X = np.column_stack((np.ones(m), X))
X[:5]

# ## ============ Part 3: Compute Cost and Gradient ============

# ### Activation Function
# We will use the sigmoid function as our activation function.
#
# $g(z) = \frac{1}{1 + e^{-z}}$
#
# When:
#
# $z = 0$ then $g = 0.5$
#
# $z \rightarrow +\infty$ then $g \rightarrow +1$
#
# $z \rightarrow -\infty$ then $g \rightarrow 0$
#

# In[9]:

def sigmoid(z):
    # return np.divide(np.float128(1.0), (np.float128(1.0) + np.exp(-z)), dtype=np.float128)
    return 1 / (1 + np.exp(-z))

# In[10]:

print(sigmoid(-5)) # ~= 0.0066929
print(sigmoid(0)) # ~= 0.5
print(sigmoid(5)) # ~= 0.99331
print(sigmoid(np.array([4, 5, 6]))) # ~= [0.98201   0.99331   0.99753]
print(sigmoid(np.array([-1, 0, 1]))) # ~= [0.26894 0.50000 0.73106]
print(sigmoid(np.array([[4, 5, 6], [-1, 0, 1]])))

# Add a column of ones to X to facilitate the manipulation.
#
# Each row is a input with the following format:
#
# $X[0] = [ x_0, x_1, x_2 ]$ where $x_0 = 1$

# ### Hypothesis Function
# Function that defines our logistic model.
#
# Definition:
#
# $h_\theta(x) = g(\theta_0 + \theta_1 * x_1 + \theta_2 * x_2)$
#
# Vectorial form:
#
# $h_\theta(x) = g(\theta^{T} * x)$
#
# where:
#
# $g$ is the sigmoid function; $x = [x_0, x_1, x_2]$; $x_0 = 1$ and $\theta = [\theta_0, \theta_1, \theta_2]$

# In[11]:

def hypothesis(X, theta):
    # z = np.array([np.dot(xi, theta) for xi in X])
    z = X.dot(theta)
    return sigmoid(z)

# ### Logistic Cost Function
# Computes the logistic cost of using theta as the parameter for logistic regression to fit the data points in X and y.
#
# Function cost:
#
# $ J(\theta) = \frac{1}{m} \sum_{i=1}^{m} [ -y^{(i)} log(h_\theta(x^{(i)}) - (1 - y^{(i)}) log(1 - h_\theta(x^{(i)})) ]$
#
# Vectorial form:
#
# $ J(\theta) = \frac{1}{m} * [-\vec{y}^{T} \cdot log(h_\theta(\vec{x})) - (1 - \vec{y})^{T} \cdot log(1 - h_\theta(\vec{x}))] $
#
# If any time we got a $log(h_\theta(x^{(i)})) = 0$ that means we need to normalize the features.

# In[12]:

"""
Inputs:
X = [
  [ x_0, x_1, ..., x_n ]
]
y = [
  [ y_0 ]
  ...
  [ y_m ]
]
theta = [ theta_0, ..., theta_n ]
"""
def computeLogisticCostIterative(X, y, theta):
    m = len(y)
    errorSum = 0 # total error
    for i in range(m):
        h = hypothesis(X[i], theta)
        errorSum = errorSum + (-y[i] * np.log(h) - (1 - y[i]) * np.log(1 - h))
    return errorSum / m

# Better way using Matrix/Vectors
def computeLogisticCostMatrix(X, y, theta):
    m = len(y)
    h = hypothesis(X, theta)
    return (1 / m) * (-y.T.dot(np.log(h)) - (1 - y).T.dot(np.log(1 - h)))

# In[13]:

print('Testing cost function')
X_ = np.array([
    [1, 8, 1, 6],
    [1, 3, 5, 7],
    [1, 4, 9, 2]
]);
y_ = np.array([1, 0, 1]);
theta_ = np.array([-2, -1, 1, 2]);
print('J ~= 4.6832 ->', computeLogisticCostIterative(X_, y_, theta_))
print('J ~= 4.6832 ->', computeLogisticCostMatrix(X_, y_, theta_))

# In[14]:

# Initialize fitting parameters
initial_theta = np.zeros([n + 1])
#initial_theta = np.array([0.1, 12.00921659, 11.26284221], dtype=np.float128)
cost = computeLogisticCostMatrix(X, y, initial_theta)
print('Cost at initial theta (zeros): %f', cost)
print('Expected cost (approx): 0.693')

# ### Running Gradient Descent
# Performs gradient descent to learn $\theta$ parameters.
#
# It return an array with $\theta$ containing the values found by taking num_iters gradient steps with learning rate alpha.
#
# Also it return an array with the history of $J(\theta)$ to be plotted.
#
# Step to update each parameter:
#
# $\theta_j := \theta_j - \alpha * \frac{\partial J}{\partial \theta_j} $
#
# Where:
#
# $\frac{\partial J}{\partial \theta_j} = \frac{1}{m} \sum_{i=1}^{m} [( h_\theta(x^{(i)}) - y^{(i)}) * x^{(i)}]$
#
# Metrix form:
#
# $ \frac{\partial J}{\partial \theta_j} = \frac{1}{m} = X^{T} ( h_\theta(x^{(i)}) - y^{(i)}) $

# In[15]:

def logisticGradientDescent(X, y, theta, alpha, num_iters):
    m = len(y)
    J_history = np.zeros(num_iters)
    for i in range(num_iters):
        h_theta = hypothesis(X, theta)
        # gradient of our cost function
        nabla = (1 / m) * X.T.dot(h_theta - y)
        # print (nabla) # first iteration: [ 0.31722, 0.87232, 1.64812, 2.23787 ]
        theta = theta - alpha * nabla

        # Save the cost J in every iteration
        J_history[i] = computeLogisticCostIterative(X, y, theta)
    return theta, J_history

# In[16]:

# ~= [-2, -1, 1, 2], [4.6832]
logisticGradientDescent(X_, y_, theta_, 0, 1)

# In[17]:

num_iters = 50; # with alpha = 0.01 we should rise the # of iterations
alphas = [0.01, 0.03, 0.1, 0.3, 1, 3, 10]
colors = ['b', 'r', 'y', 'black', 'brown', 'gray'];

# In[18]:

# To plot the J(theta) using different alphas
fig, ax = plt.subplots()
iterations = range(num_iters)

print('Running gradient descent ...\n')
for alpha, color in zip(alphas, colors):
    theta = np.zeros([n + 1]) # reset the theta to the current alpha

    theta, J_history = logisticGradientDescent(X, y, theta, alpha, num_iters)
    # print('alpha ', alpha, ' found theta ', theta)
    plt.plot(iterations, J_history, color=color, label='alpha %.2f' % alpha)

plt.legend(loc='best', ncol=2)

# In[19]:

# Display gradient descent's result
print('Theta computed from gradient descent:');
# Expected ~= [ 1.426919, 3.391232, 3.142802]
print(theta);

# In[20]:

predict = [45, 85]
# normalize and add the x_0 = 1
predict_norm = np.column_stack((np.ones(1), [((predict - mu) / sigma)]))
prob = hypothesis(predict_norm, theta)
print('For a student with scores 45 and 85, we predict an admission probability of', prob)
print('Expected value: 0.732');

# Compute accuracy on our training set
p = hypothesis(X, theta)

print('Train Accuracy: ', ((p >= 0.5) == y).sum().mean())
print('Expected accuracy (approx): 89.0\n')

# ## ==================== Part 3: Plotting Boundary Decision ====================

# In[21]:

# Calculate the boundary decision
score1_min = x[:,0].min()
score1_max = x[:,0].max()
score2_min = x[:,1].min()
score2_max = x[:,1].max()

xgrid = np.arange(score1_min, score1_max, (score1_max - score1_min) / 100)
ygrid = np.arange(score2_min, score2_max, (score2_max - score2_min) / 100)

X,Y = np.meshgrid(xgrid, ygrid) # grid of points
gridPoints = np.c_[X.ravel(), Y.ravel()]

predictions = np.zeros(len(gridPoints))
for i in range(len(gridPoints)):
    point = gridPoints[i]
    predict_norm = np.column_stack((np.ones(1), [((point - mu) / sigma)]))
    prob = hypothesis(predict_norm, theta)
    predictions[i] = 1 if prob >= 0.5 else 0

predictions = predictions.reshape(X.shape)

# In[22]:

fig, ax = plt.subplots()
# setup plot for decision boundary (0 = Refused, 1 = Admitted)
plt.contourf(X, Y, predictions, alpha=0.3)
sns.scatterplot('score1', 'score2', hue='admitted', style='admitted', style_order=[1,0], data=data)
plt.legend()
# plt.show()

# In[ ]:

Machine Learning - Andrew Ng/assignments/machine-learning-ex2/ex2_python/Machine Learning using Logistic Regression.py


import numpy as np
def swap_rows(A, a, b):
    """Rows two rows in a matrix, switch row a with row b

    args:
        A: matrix to perform row swaps on
        a: row index of matrix
        b: row index of matrix

    returns: nothing

    side effects:
    changes A to rows a and b swapped
    """
    assert (a>=0) and (b>=0)
    N = A.shape[0] #number of rows
    assert (a<N) and (b<N) #less than because 0-based indexing
    temp = A[a,:].copy()
    A[a,:] = A[b,:].copy()
    A[b,:] = temp.copy()
def BackSub(aug_matrix,x):
    """back substitute a N by N system after Gaussian elimination

    Args:
        aug_matrix: augmented matrix with zeros below the diagonal
        x: length N vector to hold solution
    Returns:
        nothing
    Side Effect:
    x now contains solution
    """
    N = x.size
    for row in range(N-1,-1,-1):
        RHS = aug_matrix[row,N]
        for column in range(row+1,N):
            RHS -= x[column]*aug_matrix[row,column]
        x[row] = RHS/aug_matrix[row,row]
    return
def GaussElimPivotSolve(A,b,LOUD=0):
    """create a Gaussian elimination with pivoting matrix for a system

    Args:
        A: N by N array
        b: array of length N
    Returns:
        solution vector in the original order
    """
    [Nrow, Ncol] = A.shape
    assert Nrow == Ncol
    N = Nrow
    #create augmented matrix
    aug_matrix = np.zeros((N,N+1))
    aug_matrix[0:N,0:N] = A
    aug_matrix[:,N] = b
    #augmented matrix is created

    #create scale factors
    s = np.zeros(N)
    count = 0
    for row in aug_matrix[:,0:N]: #don't include b
        s[count] = np.max(np.fabs(row))
        count += 1
    if LOUD:
        print("s =",s)
    if LOUD:
        print("Original Augmented Matrix is\n",aug_matrix)
    #perform elimination
    for column in range(0,N):

        #swap rows if needed
        largest_pos = np.argmax(np.fabs(aug_matrix[column:N,column]/s[column])) + column
        if (largest_pos != column):
            if (LOUD):
                print("Swapping row",column,"with row",largest_pos)
                print("Pre swap\n",aug_matrix)
            swap_rows(aug_matrix,column,largest_pos)
            #re-order s
            tmp = s[column]
            s[column] = s[largest_pos]
            s[largest_pos] = tmp
            if (LOUD):
                print("A =\n",aug_matrix)
        #finish off the row
        for row in range(column+1,N):
            mod_row = aug_matrix[row,:]
            mod_row = mod_row - mod_row[column]/aug_matrix[column,column]*aug_matrix[column,:]
            aug_matrix[row] = mod_row
    #now back solve
    x = b.copy()
    if LOUD:
        print("Final aug_matrix is\n",aug_matrix)
    BackSub(aug_matrix,x)
    return x


import jax
import jax.numpy as jnp

rng = jax.random.PRNGKey(43)

def gradient_descent(A, b, iters=1000, tol=1e-8, debug=False):

    n = A.shape[0]

    x = jax.random.uniform(rng, shape=(n, 1))
    r = b - A @ x

    i = 0

    while i < iters and jnp.linalg.norm(A @ x - b) > tol:
        if debug:
            print( f"{i} th iter, err {jnp.linalg.norm(A @ x - b)}" )

        Ar = A @ r
        tau = r.T @ r / ( r.T @ Ar )
        x += tau * r
        r-= tau * Ar

        i += 1

    return x

def positive_definite(n):
    A = jax.random.uniform(rng, shape=(n, n))
    A = A @ A.T
    return A

n = 4
A = positive_definite(n)
b = jax.random.uniform(rng, shape=(n, 1))

x = gradient_descent(A, b)

assert( jnp.linalg.norm(A @ x - b) < 1e-2 )


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

class LM:
  def __init__(self, y, X = None, verbose = False):
    ### process arguments
    self.y = y
    self.n = len(y)

    if X is None:
      X = np.ones(self.n).reshape(self.n, 1)
    self.X = X
    self.p = X.shape[1]
    self.verbose = verbose

    ### others
    self.fitted = False

    self.beta_hat = None
    self.y_hat = None
    self.residuals = None

  def fit(self):
    ### estimate model parameters
    # beta = (X'X)^(-1) X' y
    self.beta_hat = np.linalg.inv(self.X.transpose().dot(self.X)).dot(self.X.transpose().dot(self.y))

    self.y_hat = self.X.dot(self.beta_hat)
    self.residuals = self.y - self.y_hat

    self.fitted = True

  def __repr__(self):
    line_dim = ' -- ' + str(self.n) + ' observations, ' + str(self.p) + ' predictors\n'

    if self.fitted == False:
      return 'Linear Model (not fitted yet)\n' + line_dim
    else:
      line_beta = ' -- beta: ' + str(mod.beta_hat.transpose())
      return 'Liner Model (fitted)\n' + line_dim + line_beta

  def plot(self, type = 1):
    if type == 1:
      self.plotRes()

  def plotRes(self):
    fig = plt.figure()

    plt.scatter(self.y_hat, self.residuals, figure = fig)
    plt.xlabel('Fitted values')
    plt.ylabel("Residuals")
    plt.title("Linear Model")
    plt.axhline(0)

    fig.show()

#-------------
# Tests
#-------------

def sim_dat1(n = 100, beta = [3, 2, 1]):
  np.random.seed(1)

  x0 = np.ones(n).reshape((n, 1))
  x1 = np.random.normal(loc = 0, scale = 2, size = n).reshape((n, 1))
  x2 = np.matrix(np.random.normal(loc = 0, scale = 1, size = n)).reshape((n, 1))

  X = np.concatenate((x0, x1, x2), 1)

  b = np.array(beta).reshape((3, 1))
  error = np.random.normal(scale = 2, size = n).reshape((n, 1))

  y = X.dot(b) + error

  return(y, X)

def main():
  print ' * test `sim_dat1`:'
  (y, X) = sim_dat1()
  mod = LM(y, X)
  mod.fit()

if __name__ == '__main__': main()

(y, X) = sim_dat1()
mod = LM(y, X)
mod.fit()


# %%
"""In this code we wanna calculate determinant of matrix."""
import numpy as np
# %%
help(np.linalg.det)
# %%
a = np.arange(1, 5).reshape((2, 2))
a
# %%
np.linalg.det(a)
# %%
round(np.linalg.det(a))
# %%
b = np.arange(1, 10).reshape((3, 3))
b
# %%
np.linalg.det(b)
# %%
round(np.linalg.det(b))


import numpy as np

check = "rmse"
#check = "pearson"

def correl(X,Y):
    N, = X.shape

    if N < 9:
        print "not enough points. {} datapoints given. at least 9 is required".format(n)
        return

    r = np.corrcoef(X,Y)[0][1]
    r_sig = 1.96/np.sqrt(n-2+1.96**2)
    F_plus = 0.5*np.log((1+r)/(1-r))+r_sig
    F_minus = 0.5*np.log((1+r)/(1-r))-r_sig
    le = r - (np.exp(2*F_minus)-1)/(np.exp(2*F_minus)+1)
    ue = (np.exp(2*F_plus)-1)/(np.exp(2*F_plus)+1) - r

    return r, le, ue

def rmse(X, Y):
    """
    Root-Mean-Square Error

    Lower Error = RMSE \left( 1- \sqrt{ 1- \frac{1.96\sqrt{2}}{\sqrt{N-1}} }  \right )
    Upper Error = RMSE \left(    \sqrt{ 1+ \frac{1.96\sqrt{2}}{\sqrt{N-1}} } - 1 \right )

    This only works for N >= 8.6832, otherwise the lower error will be
    imaginary.

    Parameters:
    X -- One dimensional Numpy array of floats
    Y -- One dimensional Numpy array of floats

    Returns:
    rmse -- Root-mean-square error between X and Y
    le -- Lower error on the RMSE value
    ue -- Upper error on the RMSE value
    """

    N, = X.shape

    if N < 9:
        print "Not enough points. {} datapoints given. At least 9 is required".format(N)
        return

    diff = X - Y
    diff = diff**2
    rmse = np.sqrt(diff.mean())

    le = rmse * (1.0 - np.sqrt(1-1.96*np.sqrt(2.0)/np.sqrt(N-1)))
    ue = rmse * (np.sqrt(1 + 1.96*np.sqrt(2.0)/np.sqrt(N-1))-1)

    return rmse, le, ue

def mae(X, Y):
    """
    Mean Absolute Error (MAE)

    Lower Error =  MAE_X \left( 1- \sqrt{ 1- \frac{1.96\sqrt{2}}{\sqrt{N-1}} }  \right )
    Upper Error =  MAE_X \left(  \sqrt{ 1+ \frac{1.96\sqrt{2}}{\sqrt{N-1}} }-1  \right )

    Parameters:
    X -- One dimensional Numpy array of floats
    Y -- One dimensional Numpy array of floats

    Returns:
    mae -- Mean-absolute error between X and Y
    le -- Lower error on the MAE value
    ue -- Upper error on the MAE value
    """

    N, = X.shape

    mae = np.abs(X - Y)
    mae = mae.mean()

    le =  mae * (1 - np.sqrt(1 - 1.96*np.sqrt(2)/np.sqrt(N-1) ) )
    ue =  mae * (    np.sqrt(1 + 1.96*np.sqrt(2)/np.sqrt(N-1) ) -1 )

    return mae, le, ue

def me(X, Y):
    """
    mean error (ME)

    L_X = U_X =  \frac{1.96 s_N}{\sqrt{N}}
    where sN is the standard population deviation (e.g. STDEVP in Excel).

    Parameters:
    X -- One dimensional Numpy array of floats
    Y -- One dimensional Numpy array of floats

    Returns:
    mae -- Mean error between X and Y
    e   -- Upper and Lower error on the ME
    """

    N, = X.shape

    error = X - Y
    me = error.mean()

    s_N = stdevp(error, me, N)
    e = 1.96*s_N/np.sqrt(N)

    return me, e

def stdevp(X, X_hat, N):
    """
    Parameters:
    X -- One dimensional Numpy array of floats
    X_hat -- Float
    N -- Integer

    Returns:

    Calculates standard deviation based on the entire population given as
    arguments. The standard deviation is a measure of how widely values are
    dispersed from the average value (the mean).
    """
    return np.sqrt(np.sum((X-X_hat)**2)/N)

if __name__ == '__main__':

    import sys
    import numpy as np

    import matplotlib.pyplot as plt

    if len(sys.argv) < 2:
        exit("usage: python example.py example_input.csv")

    filename = sys.argv[1]
    f = open(filename, "r")
    data = np.genfromtxt(f, delimiter=',', names=True)
    f.close()

    try:
        ref = data['REF']
    except:
        ref = data['\xef\xbb\xbfREF']
    n = len(ref)

    methods = data.dtype.names
    methods = methods[1:]
    nm = len(methods)

    rmse_list = []
    rmse_lower = []
    rmse_upper = []

    mae_list = []
    mae_lower = []
    mae_upper = []

    me_list = []
    me_lower = []
    me_upper = []

    r_list = []
    r_lower = []
    r_upper = []

    for method in methods:
        mdata = data[method]

        # RMSE
        mrmse, mle, mue = rmse(mdata, ref)
        rmse_list.append(mrmse)
        rmse_lower.append(mle)
        rmse_upper.append(mue)

        # MAD
        mmae, maele, maeue = mae(mdata, ref)
        mae_list.append(mmae)
        mae_lower.append(maele)
        mae_upper.append(maeue)

        # ME
        mme, mmee = me(mdata, ref)
        me_list.append(mme)
        me_lower.append(mmee)
        me_upper.append(mmee)

        # r
        r, rle, rue = correl(mdata, ref)
        r_list.append(r)
        r_lower.append(rle)
        r_upper.append(rue)

    print "Method_A   Method_B      RMSE_A   RMSE_B   RMSE_A-RMSE_B  Comp Err  same?"
    ps = "{:10s} "*2 +  "{:8.3f} "*2 + "{:8.3f}" + "{:15.3f}" + "     {:}"

    if check == "pearson":
        measure = r_list
        upper_error = r_upper
        lower_error = r_lower
    else:
        measure = rmse_list
        upper_error = rmse_upper
        lower_error = rmse_lower
        # measure = mae_list
        # upper_error = mae_upper
        # lower_error = mae_lower

    for i in xrange(nm):
        for j in xrange(i+1, nm):

            m_i = methods[i]
            m_j = methods[j]

            rmse_i = measure[i]
            rmse_j = measure[j]

            r_ij = np.corrcoef(data[m_i], data[m_j])[0][1]

            if rmse_i > rmse_j:
                lower = lower_error[i]
                upper = upper_error[j]
            else:
                lower = lower_error[j]
                upper = upper_error[i]

            comp_error = np.sqrt(upper**2 + lower**2 - 2.0*r_ij*upper*lower)
            significance = abs(rmse_i - rmse_j) < comp_error

            print ps.format(m_i, m_j, rmse_i, rmse_j, rmse_i-rmse_j, comp_error, significance)

    print
    print "\\begin{table}[]"
    print "\centering"
    print "\caption{}"
    print "\label{}"
    print "\\begin{tabular}{l" + nm*"c" + "}"
    print "\midrule"
    print "& " + " & ".join(methods) + "\\\\"
    print "\midrule"
#   for i in xrange(nm-1):
#       print '%.1f $\pm$ %.1f/%.1f &'%(rmse_list[i],lower_error[i],rmse_upper[i]),
#   print '%.1f $\pm$ %.1f/%.1f'%(rmse_list[-1],lower_error[-1],rmse_upper[-1])
    print "RMSE & " + " & ".join(format(x, "3.1f") for x in rmse_list) + "\\\\"

    temp_list = [ i+"/"+j for i,j in zip([format(x, "3.1f") for x in rmse_upper],[format(x, "3.1f") for x in rmse_lower])]
    print "95 \% conf & $\pm$ " + " & $\pm$ ".join(temp_list) + "\\\\"

    temp_list = [ i+" $\pm$ "+j for i,j in zip([format(x, "3.1f") for x in me_list],[format(x, "3.1f") for x in me_upper])]
    print "ME & " + " & ".join(temp_list) + "\\\\"

    print "$r$ & " + " & ".join(format(x, "3.2f") for x in r_list) + "\\\\"

    temp_list = [ i+"/"+j for i,j in zip([format(x, "3.2f") for x in r_upper],[format(x, "3.2f") for x in r_lower])]
    print "95 \% conf & $\pm$ " + " & $\pm$ ".join(temp_list) + "\\\\"

    print "\midrule"
    print "\end{tabular}"
    print "\end{table}"

    # Create x-axis
    x = range(len(methods))

    # Errorbar (upper and lower)
    asymmetric_error = [rmse_lower, rmse_upper]

    # Add errorbar for RMSE
    plt.errorbar(x, rmse_list, yerr=asymmetric_error, fmt='o')

    # change x-axis to method names and rotate the ticks 30 degrees
    plt.xticks(x, methods, rotation=30)

    # Pad margins so that markers don't get clipped by the axes
    plt.margins(0.2)

    # Tweak spacing to prevent clipping of tick-labels
    plt.subplots_adjust(bottom=0.15)

    # Add grid to plot
    plt.grid(True)

    # Set plot title
    plt.title('Root-mean-sqaure error')

    # Save plot to PNG format
    plt.savefig('example_rmsd.png')

    # Clear figure
    plt.clf()

    # MAE plot
    asymmetric_error = [mae_lower, mae_upper]
    plt.errorbar(x, mae_list, yerr=asymmetric_error, fmt='o')
    plt.xticks(x, methods, rotation=30)
    plt.margins(0.2)
    plt.subplots_adjust(bottom=0.15)
    plt.grid(True)
    plt.title('Mean Absolute Error')
    plt.savefig('example_mae.png')

    # Clear figure
    plt.clf()

    # ME plot
    asymmetric_error = [me_lower, me_upper]
    plt.errorbar(x, me_list, yerr=asymmetric_error, fmt='o')
    plt.xticks(x, methods, rotation=30)
    plt.margins(0.2)
    plt.subplots_adjust(bottom=0.15)
    plt.grid(True)
    plt.title('Mean Error')
    plt.savefig('example_me.png')


import math
import numpy as np

def simple_euclidean(instance1, instance2, dimension):
    distance = 0
    for dim in range(dimension):
        distance += pow((instance1[dim] - instance2[dim]), 2)
    return math.sqrt(distance)

def euclidean(instance1, instance2):
    """Vectorized implementation of Euclidean distance"""
    delta = instance1 - instance2
    return np.sqrt(np.sum(delta * delta))

def weighted_euclidean(instance1, instance2, weights):
    delta = instance1 - instance2
    return np.sqrt((np.sum(weights * (delta * delta))))


from matplotlib import pyplot as plt
from math import sin, cos, radians
import numpy as np

sup_2 = '\N{SUPERSCRIPT TWO}'

def force(theta):
    m = 50
    g = 9.8
    f_press = 350
    return 0.2 * m * g * sin(radians(theta)) * sin(radians(theta)) + f_press * cos(radians(90 - theta))

x = np.linspace(45, 90, 100)
y = []
for i in x:
    y.append(force(i))

plt.title('By pedaling on the starting block with same force\n'
          'how much forward force (parallel to the ground) I can get\n'
          'by setting starting block to different angles?', fontsize=14)
plt.xlabel('degree of starting block (degree)', fontsize=14)
plt.ylabel('parallel force pointing forward\n(Newton)', fontsize=14)
plt.plot(x, y, label=f'F = 20%mg sin{sup_2}\u03b8 + Fp cos(90 - \u03b8)', color='k')

dx = [45, 60, 70, 80, 90]
dy = []
for i in dx:
    di = force(i)
    dy.append(di)
    if i == 45:
        plt.annotate(s=f'({i}, {round(di)})', xy=[i + 6, di + 20], fontsize=14)
    elif i != 90 and i != 80:
        plt.annotate(s=f'({i}, {round(di)})', xy=[i + 2, di], fontsize=14)
    elif i == 80:
        plt.annotate(s=f'({i}, {round(di)})', xy=[i - 8, di + 4], fontsize=14)
    else:
        plt.annotate(s=f'({i}, {round(di)})', xy=[i - 7, di - 14], fontsize=14)

plt.scatter(dx, dy, color='k', linewidths=3)
plt.legend(fontsize=14, loc='lower right')
plt.tight_layout()
plt.show()