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

import numpy as np
import numpy.linalg

# Question 3
def conjgrad (A, b, x = None, iterations = 10**6, epsilon = 10**(-10)):
    """
        Méthode du gradient conjugué.
        -----------------------------
        Entrée:
            A matrice symétrique définie positive.
            b vecteur colonne.
            (optional) x vecteur initial de la suite.
            (optional) iterations nombre d'itérations maximales pour apporcher la solution.
            (optional) epsilon précision minimale pour approcher la solution.
        Sortie:
            x solution approchée de Ax=b.
    """
    if not x:
        x = np.matrix(np.zeros([len(A),1]))

    r = b - A*x
    p = r
    rsold = (r.T * r)[0,0]
    rsnew = epsilon**2 + 1

    i = 1
    while i < iterations and np.sqrt(rsnew) > epsilon:
        Ap = A*p
        alpha= rsold/((p.T*Ap)[0,0])
        x = x + alpha*p
       #recuperer la valeur dans la matrice de taille 1.
        r = r - alpha*Ap
        rsnew = (r.T * r)[0,0]

        #print rsnew
        p = r + rsnew / (rsold*p)
        rsold = rsnew
        i+=1
    return x

# Question 4
def conjgrad_precond (A, b, M, x = None, iterations = 10**6, epsilon = 10**(-10)):
    """
        Méthode du gradient conjugué, avec préconditioneur.
        -----------------------------
        Entrée:
            A matrice symétrique définie positive.
            b vecteur colonne.
            M matrice préconditionneuse.
            (optional) x vecteur initial de la suite.
            (optional) iterations nombre d'itérations maximales pour apporcher la solution.
            (optional) epsilon précision minimale pour approcher la solution.
        Sortie:
            x solution approchée de Ax=b.
    """
    if not x:
        x = np.matrix(np.zeros([len(A),1]))
    xold = x
    rold = b - A*x
    zold = M.I*rold
    p = zold

    rnew = [epsilon**2 + 1]
    i = 1
    while i < iterations and numpy.linalg.norm(rnew) > epsilon:
        Ap = A*p
        alphaold = ((rold.T * zold)/(p.T*Ap))[0,0]
        xnew= xold + alphaold*p
        rnew = rold - alphaold*Ap
        znew = M.I*rnew
        betaold = (znew.T*rnew)[0,0] / (zold.T*rold)[0,0]
        p = znew + betaold*p

        rold = rnew
        zold = znew
        xold = xnew
        i+=1
    return xnew


import numpy as np
np.set_printoptions(suppress=True)
import matplotlib.pyplot as plt

def gradient_descent(X, y, theta, iterations, alpha):
	m = X.shape[0]

	def compute_cost(X, y, theta):
		#easy way
		#error = 0

		#for i in range(m):
		#	hypothesis = X[i][0] * theta[0] + X[i][1] * theta[1]
		#	error += (hypothesis - y[i])**2

		#linear algebra way
		hypothesis = np.dot(X, theta) #dot product
		error = np.sum((hypothesis - y) ** 2)

		return error / (2 * m)

	cost_history = []

	for i in range(iterations):
		hypothesis = np.matmul(X, theta)

		for j in range(theta.shape[0]):
			theta[j] = theta[j] - alpha * np.sum((hypothesis - y) * X[:, j]) / m

		cost_history.append(compute_cost(X, y, theta))

	return theta, cost_history

def plot_cost_history(cost_history):
	plt.clf()
	plt.title('Cost J x iterations')

	plt.ylabel('Cost J')
	plt.xlabel('Number of iterations')

	plt.plot(cost_history, 'g.')

	plt.show()

if __name__ == '__main__':
	# Initialization
	## Import data
	data = np.loadtxt("ex1data1.txt", delimiter=",")

	## Initialize important variables
	m = data.shape[0]
	alpha = 0.01

	for iterations in [100, 1500, 3000]:
		theta = np.zeros(2)

		# Evaluation
		## Create the bias column, set to 1
		X = np.stack((np.ones(m), data[:, 0]), axis=-1)

		## Second column of data
		y = data[:, 1]

		theta, cost_history = gradient_descent(X, y, theta, iterations, alpha)

		plt.plot(data[:, 0], np.matmul(X,theta), '-', label='linear regression using %d iterations' % iterations)

		print('Theta found by gradient descent', theta);

		# Predictions
		print('For population = 35,000, we predict a profit of', np.matmul(np.array([1, 3.5]), theta) * 10000)
		print('For population = 70,000, we predict a profit of', np.matmul(np.array([1, 7]), theta) * 10000)
		print()

	#plot_cost_history(cost_history)

	# Show Results
	plt.ylabel('Profit in $10,000s')
	plt.xlabel('Population of City in $10,000s')

	plt.plot(data[:, 0], data[:, 1], 'rx', label='Training data')

	plt.legend()
	plt.show()


import numpy
import sympy
from matplotlib import pyplot
from sympy.utilities.lambdify import lambdify

# Set the font family and size to use for Matplotlib figures.
pyplot.rcParams['font.family'] = 'serif'
pyplot.rcParams['font.size'] = 16

sympy.init_printing()

x, nu, t = sympy.symbols('x nu t')
phi = (sympy.exp(-(x - 4 * t)**2 / (4 * nu * (t + 1))) +
       sympy.exp(-(x - 4 * t - 2 * numpy.pi)**2 / (4 * nu * (t + 1))))
phiprime = phi.diff(x)

u = -2 * nu * (phiprime / phi) + 4

u_lamb = lambdify((t, x, nu), u)

# Set parameters.
nx = 101  # number of spatial grid points
L = 2.0 * numpy.pi  # length of the domain
dx = L / (nx - 1)  # spatial grid size
nu = 0.07  # viscosity
nt = 100  # number of time steps to compute
sigma = 0.1  # CFL limit
dt = sigma * dx**2 / nu  # time-step size

# Discretize the domain.
x = numpy.linspace(0.0, L, num=nx)

# Set initial conditions.
t = 0.0
u0 = numpy.array([u_lamb(t, xi, nu) for xi in x])

# Integrate the Burgers' equation in time.
u = u0.copy()
for n in range(nt):
    un = u.copy()
    # Update all interior points.
    u[1:-1] = (un[1:-1] -
               un[1:-1] * dt / dx * (un[1:-1] - un[:-2]) +
               nu * dt / dx**2 * (un[2:] - 2 * un[1:-1] + un[:-2]))
    # Update boundary points.
    u[0] = (un[0] -
            un[0] * dt / dx * (un[0] - un[-1]) +
            nu * dt / dx**2 * (un[1] - 2 * un[0] + un[-1]))
    u[-1] = (un[-1] -
            un[-1] * dt / dx * (un[-1] - un[-2]) +
            nu * dt / dx**2 * (un[0] - 2 * un[-1] + un[-2]))

# Compute the analytical solution.
u_analytical = numpy.array([u_lamb(nt * dt, xi, nu) for xi in x])

# Plot the numerical solution along with the analytical solution.
pyplot.figure(figsize=(6.0, 4.0))
pyplot.xlabel('x')
pyplot.ylabel('u')
pyplot.grid()
pyplot.plot(x, u, label='Numerical',
            color='C0', linestyle='-', linewidth=2)
pyplot.plot(x, u_analytical, label='Analytical',
            color='C1', linestyle='--', linewidth=2)
pyplot.legend()
pyplot.xlim(0.0, L)
pyplot.ylim(0.0, 10.0);
pyplot.show()
pyplot.clf()


#!/usr/bin/env python
"""

"""

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

n = 100
pcoin = 0.62 # actual value of p for coin
results = st.bernoulli(pcoin).rvs(n)
h = sum(results)
print results
print("we observed %s heads out of %s"%(h,n))

## Expected distribution for fair coin
p = 0.5
rv = st.binom(n, p)
mu = rv.mean()
sd = rv.std()
print("The expected distribution for a fair coin is mu=%s, sd=%s"%(mu,sd))

## if we move into a hypothesis testing framework
# we can use the binomial test
print("binomial test - %s"%st.binom_test(h, n, p))

## normal approximation for binomal
z = (h-0.5-mu)/sd
print("normal approx for binomial - %s"%(2*(1 - st.norm.cdf(z))))

## can use simulation to test things as well
nsamples = 100000
xs = np.random.binomial(n, p, nsamples)
print("simulation p-value - %s"%(2*np.sum(xs >= h)/(xs.size + 0.0)))

## MLE
print("Maximum likelihood %s"%(np.sum(results)/float(len(results))))
bs_samples = np.random.choice(results, (nsamples, len(results)), replace=True)
bs_ps = np.mean(bs_samples, axis=1)
bs_ps.sort()
print "Bootstrap CI: (%.4f, %.4f)" % (bs_ps[int(0.025*nsamples)], bs_ps[int(0.975*nsamples)])

## The Bayesian approach directly estimates the posterior distribution
## all other point/interval statistics can be estimated from posterior

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

a, b = 10, 10
prior = st.beta(a, b)
post = st.beta(h+a, n-h+b)
ci = post.interval(0.95)
map_ =(h+a-1.0)/(n+a+b-2.0)

xs = np.linspace(0, 1, 100)
ax.plot(prior.pdf(xs), label='Prior')
ax.plot(post.pdf(xs), label='Posterior')
ax.axvline(mu, c='red', linestyle='dashed', alpha=0.4)
ax.set_xlim([0, 100])
ax.axhline(0.3, ci[0], ci[1], c='black', linewidth=2, label='95% CI');
ax.axvline(n*map_, c='blue', linestyle='dashed', alpha=0.4)
ax.legend()

plt.savefig("coin-toss.png")


import matplotlib as mpl
mpl.use('TkAgg')

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.optimize as opt
from plotData import *
import costFunction as cf
import plotDecisionBoundary as pdb
import predict as predict
from sigmoid import *

# Load data
# The first two columns contain the exam scores and the third column contains the label.
data = pd.read_csv('ex2data1.txt', sep=',', header=None, names=['Exam1', 'Exam2', 'Admitted'])

cols = data.shape[1]
X = data.iloc[:, :cols - 1]
y = data.iloc[:, cols - 1:]

# ===================== Part 1: Plotting =====================
print('Plotting Data with + indicating (y = 1) examples and o indicating (y = 0) examples.')
pos = data[data.Admitted.isin([1])]
neg = data[data.Admitted.isin([0])]

fig, ax = plt.subplots()
ax.scatter(pos['Exam1'], pos['Exam2'], c='b', marker='o', label='Admitted')
ax.scatter(neg['Exam1'], neg['Exam2'], c='r', marker='+', label='Not admitted')
plt.axis([30, 100, 30, 100])
plt.legend(loc=1)
plt.xlabel('Exam 1 score')
plt.ylabel('Exam 2 score')

input('Program paused. Press ENTER to continue')

# ===================== Part 2: Compute Cost and Gradient =====================
# In this part of the exercise, you will implement the cost and gradient
# for logistic regression. You need to complete the code in
# costFunction.py

# Setup the data array appropriately, and add ones for the intercept term
(m, n) = X.shape

# Add intercept term
X.insert(0, 'Ones', 1)

# Initialize fitting parameters
initial_theta = np.zeros((n + 1, 1))

# Compute and display initial cost and gradient
cost, grad = cf.cost_function(initial_theta, X, y)
np.set_printoptions(formatter={'float': '{: 0.4f}\n'.format})

print('Cost at initial theta (zeros): {:0.3f}'.format(cost))
print('Expected cost (approx): 0.693')
print('Gradient at initial theta (zeros): \n{}'.format(grad))
print('Expected gradients (approx): \n-0.1000\n-12.0092\n-11.2628')

# Compute and display cost and gradient with non-zero theta
test_theta = np.array([[-24], [0.2], [0.2]])
cost, grad = cf.cost_function(test_theta, X, y)

print('Cost at test theta (zeros): {}'.format(cost))
print('Expected cost (approx): 0.218')
print('Gradient at test theta: \n{}'.format(grad))
print('Expected gradients (approx): \n0.043\n2.566\n2.647')

input('Program paused. Press ENTER to continue')

# ===================== Part 3: Optimizing using fmin_bfgs =====================
# In this exercise, you will use a built-in function (opt.fmin_bfgs) to find the
# optimal parameters theta

def cost_func(t, X, y):
    t = t.reshape((len(t), 1))
    return cf.cost_function(t, X, y)[0]

def grad_func(t, X, y):
    t = t.reshape((len(t), 1))
    return cf.cost_function(t, X, y)[1]

initial_theta = np.zeros(3)
# Run fmin_bfgs to obtain the optimal theta
res = opt.minimize(fun=cost_func, x0=initial_theta, args=(X, y), method='BFGS', jac=grad_func)
theta = res.x
cost = res.fun

print('Cost at theta found by fmin: {:0.4f}'.format(cost))
print('Expected cost (approx): 0.203')
print('theta: \n{}'.format(theta))
print('Expected Theta (approx): \n-25.161\n0.206\n0.201')

# Plot boundary
pdb.plot_decision_boundary(theta, X)
plt.show()

input('Program paused. Press ENTER to continue')

# ===================== Part 4: Predict and Accuracies =====================
# After learning the parameters, you'll like to use it to predict the outcomes
# on unseen data. In this part, you will use the logistic regression model
# to predict the probability that a student with score 45 on exam 1 and
# score 85 on exam 2 will be admitted
#
# Furthermore, you will compute the training and test set accuracies of our model.
#
# Your task is to complete the code in predict.py

# Predict probability for a student with score 45 on exam 1
# and score 85 on exam 2

prob = sigmoid(np.array([1, 45, 85]).dot(theta.reshape((len(theta), 1))))
print('For a student with scores 45 and 85, we predict an admission probability of {:0.4f}'.format(prob[0]))
print('Expected value : 0.775 +/- 0.002')

# Compute the accuracy on our training set
p = predict.predict(theta.reshape((len(theta), 1)), X)

count = 0
for i in range(m):
    if p[i] == y['Admitted'][i]:
        count += 1
print('Train accuracy: {}'.format(count / m * 100))
print('Expected accuracy (approx): 89.0')

input('ex2 Finished. Press ENTER to exit')


import numpy as np
import matplotlib.pyplot as plt
from plot_functions import plot_mutliplot_bilinear

dx = 0.1  # The resolution in x
dy = 0.1  # The resolution in y

lx = 50 # How long are kernels in the x direction
ly = 50 # The long are kernels in the y direction

# Create the positions
x = np.arange(-lx/2, lx/2, dx)
y = np.arange(-ly/2, ly/2, dy)

sigma_center = 15  # Size of the center area
sigma_surround = 7  # Size of the surround area

# This is for the two dimensional pattern
X, Y = np.meshgrid(x, y)
R = np.sqrt(X**2 + Y**2)  # Distance
center = (17.0 / sigma_center**2) * np.exp(-(R / sigma_center)**2)
surround = (16.0 / sigma_surround**2) * np.exp(-(R / sigma_surround)**2)
Z = surround - center

# Plot countour map
plt.contourf(X, Y, Z, 50, alpha=0.75, cmap=plt.cm.hot)
plt.colorbar()

C = plt.contour(X, Y, Z, 10, colors='black', linewidth=.5)
plt.clabel(C, inline=10, fontsize=10)
plt.show()

# One dimensionall side view
center = (17.0 / sigma_center**2) * np.exp(-(x / sigma_center)**2)
surround = surround = (16.0 / sigma_surround**2) * np.exp(-(x / sigma_surround)**2)
z1 = surround - center

plt.plot(x,z1)

plt.show()

##  Now we code the temporal pattern

# First the kernel size and resolution
kernel_size = 25
dt_kernel = 10
t = np.arange(0, kernel_size * dt_kernel, dt_kernel) # Time vector

## Temporal parameters
K1 = 1.05
K2 = 0.7
c1 = 0.14
c2 = 0.12
n1 = 7.0
n2 = 8.0
t1 = -6.0
t2 = -6.0
td = 6.0

p1 = K1 * ((c1*(t - t1))**n1 * np.exp(-c1*(t - t1))) / ((n1**n1) * np.exp(-n1))
p2 = K2 * ((c2*(t - t2))**n2 * np.exp(-c2*(t - t2))) / ((n2**n2) * np.exp(-n2))
p3 = p1 - p2

plt.plot(t, p3, label='temporal kernel')
plt.xlabel('time (ms)')
plt.legend()
plt.show()

## Now create the spatio-temporal filter

# Initialize and fill the spatio-temporal kernel
kernel = np.zeros((kernel_size, int(lx/dx), int(ly/dy)))

for k, p in enumerate(p3):
    kernel[k,...] = p * Z

plot_mutliplot_bilinear(25,kernel, colorbar=True, symmetric=2)
p

plt.show()


"""
Created on Thu Apr  1 03:05:09 2021
"""

from typing import List

import numpy as np

ListOfFloats = List[float, float, float]

def distance_formula(final_coordinates: ListOfFloats,
                     initial_coordinates: ListOfFloats = None,
                     deg_rad: str = 'rad') -> float:
    """
    Calculates the distance between two given points in spherical coordinate system
    Parameters
    ----------
    final_coordinates: List[float, float, float]
        Final coordinates of the point to which the distance is to be calculated.
    initial_coordinates: List[float, float, float]
        Reference coordinates of the point. The default is None.
    deg_rad: str, optional
        Whether the specified theta and phi arguments are in degree or radians.
        The default is 'rad'.

    Returns
    ----------
    float:
        Distance between two points in spherical coordinates
    """

    if initial_coordinates is None:
        initial_coordinates = [0, 0, 0]

    r1, theta1, phi1 = initial_coordinates
    r2, theta2, phi2 = final_coordinates

    theta1, phi1 = np.radians([theta1, phi1]) if deg_rad == 'deg' else theta2, phi1
    theta2, phi2 = np.radians([theta2, phi2]) if deg_rad == 'deg' else theta2, phi2

    p1 = r1**2 + r2**2

    _comp1 = np.sin(theta1) * np.sin(theta2) * np.cos(phi1 - phi2)
    _comp2 = np.cos(theta1) * np.cos(theta2)

    p2 = 2 * r1 * r2 * (_comp1 + _comp2)

    return np.sqrt(p1 - p2)


import math
import numpy as np
# Note: please don't add any new package, you should solve this problem using only the packages above.
#-------------------------------------------------------------------------
'''
    Problem 1: Linear Regression (Maximum Likelihood)
    In this problem, you will implement the linear regression method based upon maximum likelihood (least square).
    w'x + b = y
    You could test the correctness of your code by typing `nosetests -v test1.py` in the terminal.
    Note: please don't use any existing package for linear regression problem, implement your own version.
'''

#--------------------------
def compute_Phi(x,p):
    '''
        Compute the design matrix Phi of x for polynoial curve fitting problem.
        We will construct p polynoials a the p features of the data samples.
        The features of each sample, is x^0, x^1, x^2 ... x^(p-1)
        Input:
            x : a vector of samples in one dimensional space, a numpy vector of shape n by 1.
                Here n is the number of samples.
            p : the number of polynomials/features
        Output:
            Phi: the design/feature matrix of x, a numpy matrix of shape (n by p).
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return Phi

#--------------------------
def least_square(Phi, y):
    '''
        Fit a linear model on training samples. Compute the paramter w using Maximum likelihood (equal to least square).
        Input:
            Phi: the design/feature matrix of the training samples, a numpy matrix of shape n by p
                Here n is the number of training samples, p is the number of features
            y : the sample labels, a numpy vector of shape n by 1.
        Output:
            w: the weights of the linear regression model, a numpy float vector of shape p by 1.
        Hint: you could use np.linalg.inv() to compute the inverse of a matrix
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return w


import numpy as np

def arc(center, radius, start_angle, end_angle, resolution=100):
    """Draws an arc

    Args:
        center (pair): the center of the circle inferred by the arc
        radius (float): the radius of the circle inferred by the arc
        start_angle (float): the starting angle position of the arc
        end_angle (float): the ending angle position of the arc

    Returns:
        layer: A layer.
    """
    linspace = np.linspace(0., 1., resolution)
    angles = start_angle + (end_angle - start_angle) * linspace
    return (
        (np.cos(angles) * radius) + center[0],
        (np.sin(angles) * radius) + center[1],
    )

def circle(center, radius, resolution=100):
    return arc(center, radius, 0, 2*np.pi, resolution)

def line(origin, end=None, vector=None, length=None, angle=None, resolution=2):
    """Draws a line. One of these must be specified: end, vector, or (length, angle)

    Args:
        origin (pair): the origin of the line
        end (pair): optional. the coordinates of end of the line
        vector (pair): optional. the x and y lengths of the line
        length (float): optional. the length of the line
        angle (float): optional. the angle of the line

    Returns:
        layer: A layer.
    """

    if end is not None:
        return (
            np.linspace(origin[0], end[0], resolution),
            np.linspace(origin[1], end[1], resolution)
        )

    if vector is not None:
        return (
            np.linspace(origin[0], origin[0] + vector[0], resolution),
            np.linspace(origin[1], origin[1] + vector[1], resolution)
        )

    if length is not None and angle is not None:
        return (
            np.linspace(origin[0], origin[0] + length * np.cos(angle), resolution),
            np.linspace(origin[1], origin[1] + length * np.sin(angle), resolution)
        )

def hexagon(center, diameter=1.0, resolution=2):
    return ngon(6, center, diameter, resolution)

def ngon(n, origin=(0,0), diameter=1.0, resolution=2):
    segment_length = 2 * np.sin(np.pi / n)
    ngon_inner_angle = np.pi * (n - 2) / n
    ngon_outer_angle = np.pi - ngon_inner_angle

    segments = []
    for i in range(n):
        if i == 0:
            from_pt = origin
        else:
            last_segment = segments[-1]
            from_pt = (last_segment[0][-1], last_segment[1][-1])

        segment = line(from_pt, length=segment_length, angle=i*ngon_outer_angle, resolution=resolution)
        segments.append(segment)

    segments.append(([np.nan], [np.nan]))

    ngon_layer = (
        np.concatenate([s[0] for s in segments]),
        np.concatenate([s[1] for s in segments])
    )

    return ngon_layer


import numpy as np
import pandas as pd

def accuracy_score(y_true, y_pred):

    """
    Classification performance metric that computes the accuracy of y_true
    and y_pred.
    :param numpy.array y_true: array-like of shape (n_samples,) Ground truth correct labels.
    :param numpy.array y_pred: array-like of shape (n_samples,) Estimated target values.
    :returns: C (float) Accuracy score.
    """

    """
    Verifica quais são as previsões que são iguais aos valores reais
    """

    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 regression loss function.
    Parameters
    :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.
    :param bool squared: If True returns MSE, if False returns RMSE. Default=True
    :returns: loss (float) A non-negative floating point value (the best value is 0.0).
    """

    """
    Mean squared error
    """

    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.toDataFrame()

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

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


#!/usr/bin/env python

import numpy as np
import time

def eig_sorted(X):
	D,V = np.linalg.eig(X)
	lastV = None
	sort_needed = False
	for m in D:
		if m > lastV and lastV != None:
			sort_needed = True
			#print 'Sort needed : \t' , m, lastV
		lastV = m

	if sort_needed:
		idx = D.argsort()[::-1]
		D = D[idx]
		V = V[:,idx]

	return [V,D]

def multiply_until_converge(X, f, accuracy):
	for p in range(1000):
		new_f = X.dot(f)
		new_f = new_f/np.max(new_f)
		if np.linalg.norm(new_f - f) < accuracy*np.linalg.norm(f):
			#print np.linalg.norm(new_f - f)
			print 'iteration : ' , p
			break
		else:
			f = new_f

	f = f/np.linalg.norm(f)
	return f

def power_eig(B, num_eigs, accuracy=0.0001, direction='largest first'): # direction = 'largest first' or 'smallest first'
	if B.shape[0] != B.shape[1]:
		print 'Error : A must be a squared matrix'
		return

	d = B.shape[0]
	eigValues = np.array([])
	eigVects = np.empty((d, 0))

	if direction == 'smallest first':
		N = np.linalg.norm(B,1)
		X = N*np.eye(d) - B

		for e in range(num_eigs):
			f = np.random.randn(d,1)
			f = multiply_until_converge(X, f, accuracy)
			ev = f.T.dot(X).dot(f)
			eig_value = N - ev
			X = X - ev*f.dot(f.T)

			eigValues = np.append(eigValues, eig_value)
			eigVects = np.hstack((eigVects, f))
	else:
		for e in range(num_eigs):
			f = np.random.randn(d,1)
			f = multiply_until_converge(B, f, accuracy)
			eig_value = f.T.dot(B).dot(f)
			B = B - eig_value*f.dot(f.T)

			eigValues = np.append(eigValues, eig_value)
			eigVects = np.hstack((eigVects, f))

	return [eigVects, eigValues]

if __name__ == "__main__":
	np.set_printoptions(precision=4)
	np.set_printoptions(threshold=np.nan)
	np.set_printoptions(linewidth=300)

	A = np.random.randn(6,6)
	A = A.dot(A.T)
	[V,D] = eig_sorted(A)

	print 'Truth eigen decomposition : '
	print V , '\n\n'
	print D , '\n----------\n'

	print 'Finding Largest 2 eigenvalues and vectors : \n'
	[eigVects, eigValues] = power_eig(A,2, accuracy=0.00001)
	print 'Eigenvectors : \n',  eigVects , '\n\n'
	print 'Eigenvalues : \n', eigValues , '\n----------\n'

	print 'Finding smallest 2 eigenvalues and vectors : \n'
	[eigVects, eigValues] = power_eig(A,2, direction='smallest first')
	print 'Eigenvectors : \n',  eigVects , '\n\n'
	print 'Eigenvalues : \n', eigValues , '\n----------\n'

	print 'Time difference '
	A = np.random.randn(500,500)
	A = A.dot(A.T)

	start_time = time.time()
	[eigVects, eigValues] = power_eig(A,5, accuracy=0.00001)
	print("Power Method : %s seconds ---" % (time.time() - start_time))

	start_time = time.time()
	[V,D] = eig_sorted(A)
	print("Eig Method : %s seconds ---" % (time.time() - start_time))

	#import pdb; pdb.set_trace()


#!/usr/bin/python
"""
author: Bhekimpilo Ndhlela
author: 18998712
module: Applied Mathematics(Numerical Analysis) TW324
task  : computer assignment 01
since : Friday-09-02-2018
"""

#for the square root function and absolute value
from numpy import (sqrt, abs )
def question2(debug=True):
a, b, c = 1.0, -10000.0, 1.0

    xP = (-1*b + sqrt(pow(b,2) - 4*a*c))/ 2*a
    xM = (-1*b - sqrt(pow(b,2) - 4*a*c))/ 2*a

    if debug is True:
        print("x+ = " + str(format(xP, ".20f")))
        print("x- = " + str(format(xM, ".20f")))

    xM2 = c / (a * xP)
    if debug is True:
        print format(xM2, ".20f")

question2()  #call code for question 2


import numpy as np
from scipy import optimize
from matplotlib import pyplot as plt, cm, colors

def calc_R(x,y, xc, yc):
    """ calculate the distance of each 2D points from the center (xc, yc) """
    return np.sqrt((x-xc)**2 + (y-yc)**2)

def f(c, x, y):
    """ calculate the algebraic distance between the data points and the mean circle centered at c=(xc, yc) """
    Ri = calc_R(x, y, *c)
    return Ri - Ri.mean()

def leastsq_circle(x,y):
    # coordinates of the barycenter
    x_m = np.mean(x)
    y_m = np.mean(y)
    center_estimate = x_m, y_m
    center, ier = optimize.leastsq(f, center_estimate, args=(x,y))
    xc, yc = center
    Ri       = calc_R(x, y, *center)
    R        = Ri.mean()
    residu   = np.sum((Ri - R)**2)
    return xc, yc, R, residu

def plot_data_circle(x,y, xc, yc, R):
    f = plt.figure( facecolor='white')  #figsize=(7, 5.4), dpi=72,
    plt.axis('equal')

    theta_fit = np.linspace(-pi, pi, 180)

    x_fit = xc + R*np.cos(theta_fit)
    y_fit = yc + R*np.sin(theta_fit)
    plt.plot(x_fit, y_fit, 'b-' , label="fitted circle", lw=2)
    plt.plot([xc], [yc], 'bD', mec='y', mew=1)
    plt.xlabel('x')
    plt.ylabel('y')
    # plot data
    plt.plot(x, y, 'r-.', label='data', mew=1)

    plt.legend(loc='best',labelspacing=0.1 )
    plt.grid()
    plt.title('Least Squares Circle')


"""Solution for up-and-in option with antithetic Monte-Carlo"""
import numpy as np

################################################################################
# Simulation parameters
M = 10000
r = 0.02
sg = 0.3
dt = 1 / 252
T = 1
n = int(T / dt)
S0 = 1
H = 1.1
K = 0.9
################################################################################

if __name__ == '__main__':
    # Generate random numbers from a normal distribution for the MC simulation
    eps = np.random.normal(size=(n, M))

    # Initialize empty vectors
    V = np.zeros((M))
    V_ = np.zeros((M))
    W = np.zeros((M))
    # Iterate through M iterations
    for i in range(M):
        S1 = np.zeros((n))
        S2 = np.zeros((n))
        S1[0] = S0
        S2[0] = S0
        # Iterate through n - 1 time steps (first time step is known)
        for j in range(n - 1):
            # normal pricing is stored in S1
            S1[j + 1] = S1[j] * np.exp((r - 0.5 * sg ** 2) * dt \
                 + sg * eps[j, i] * np.sqrt(dt))
            # antitetic variate pricing is stored in S2
            S2[j + 1] = S2[j] * np.exp((r - 0.5 * sg ** 2) * dt \
                - sg * eps[j, i] * np.sqrt(dt))
        # Find the maximum value of the realized stock price path
        S_tmax = np.max(S1)
        S_amax = np.max(S2)

        # Test if the price path exceeded the barrier H
        # if so, the option is exercisable
        # then the pay off is max(S(T) - K, 0)
        if S_tmax > H:
            # normal pricing
            V[i] = np.exp(-r * T) * np.max(S1[-1] - K, 0)
        if S_amax > H:
            # for use in antithetic pricing
            V_[i] = np.exp(-r * T) * np.max(S2[-1] - K, 0)
        # antithetic pricing
        W[i] = 0.5 * (V[i] + V_[i])

    # Print the results
    print('Price (mean), normal pricing:', np.mean(V))
    print('Price (mean), antithetic pricing:', np.mean(W))
    print('Std, normal pricing:', np.std(V))
    print('Std, antithetic pricing:', np.std(W))

    # It can be seen that antithetic pricing has a lower standard deviation.
    # This is because of the use of antithetic variates.

    # It can be shown analytically that the variance of the antithetic pricing
    # is less than or equal to half of the variance of normal pricing.
    # This is verified below.
    print('Test if Var(W) <= 0.5 * Var(V):', np.var(W) <= 0.5 * np.var(V))


from IMLearn.learners import UnivariateGaussian, MultivariateGaussian
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
import plotly.express as px
pio.templates.default = "simple_white"

def test_univariate_gaussian():
    # Question 1 - Draw samples and print fitted model
    mu1 = 10
    sigma1 = 1
    X1 = np.random.normal(mu1, sigma1, 1000)
    UG = UnivariateGaussian()
    UG.fit(X1)
    print("expectation1: ", UG.mu_, "variance1: ", UG.var_, "\n")
    # Question 2 - Empirically showing sample mean is consistent
    arr = np.ndarray(100)
    for i, smp in enumerate(range(10, 1001, 10)):
        temp = UnivariateGaussian()
        temp.fit(X1[:smp])
        arr[i] = np.abs(temp.mu_ - mu1)
    px.scatter(x=np.array(list(range(10, 1001, 10))), y=arr, title="question 2").update_xaxes \
        (title_text="sample size").update_yaxes(title_text="|estimated - true value of expectation|").show()
    # Question 3 - Plotting Empirical PDF of fitted model
    px.scatter(x=X1, y=UG.pdf(X1), title="question 3").update_xaxes \
        (title_text="ordered sample values").update_yaxes(title_text="PDF").show()

def test_multivariate_gaussian():
    # Question 4 - Draw samples and print fitted model
    mu2 = np.array([0, 0, 4, 0])
    cov_matrix = np.array([[1, 0.2, 0, 0.5], [0.2, 2, 0, 0], [0, 0, 1, 0], [0.5, 0, 0, 1]])
    X2 = np.random.multivariate_normal(mu2, cov_matrix, 1000)
    MG = MultivariateGaussian()
    MG.fit(X2)
    print("expectation2: \n", MG.mu_, "\n", "covar matrix: \n", MG.cov_)

    # # Question 5 - Likelihood evaluation
    f1 = np.linspace(-10, 10, 200)
    f3 = np.linspace(-10, 10, 200)
    log_matrix = np.array([[MultivariateGaussian.log_likelihood(np.array([f1[i], 0, f3[j], 0]), cov_matrix, X2)
                            for j in range(200)] for i in range(200)])
    px.imshow(log_matrix, x=f1, y=f3, labels=dict(x="f1", y="f3", color="log-likelihood"), title="question 5").show()
    # Question 6 - Maximum likelihood
    max_val = np.amax(log_matrix)
    max_location = np.where(log_matrix == max_val)
    print("max log-likelihood value: ", max_val,"f1 value: ", f1[max_location[0][0]],
          "f3 value:  ", f3[max_location[1][0]])

if __name__ == '__main__':
    np.random.seed(0)
    test_univariate_gaussian()
    test_multivariate_gaussian()


###________________________ Non-Lineal-Equations ___________________________###
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize

# La ayuda de este paquete es bastante larga (puedes consultarla también en
# http://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html).
# El paquete optimize incluye multitud de métodos para optimización, ajuste de
# curvas y búsqueda de raíces. Vamos a centrarnos ahora en la búsqueda de raíces
# de funciones escalares. Para más información puedes leer
# http://pybonacci.org/2012/10/25/como-resolver-ecuaciones-algebraicas-en-python-con-scipy/

# **Nota**: La función `root` se utiliza para hallar soluciones de *sistemas* de
# ecuaciones no lineales así que obviamente también funciona para ecuaciones escalares.
# No obstante, vamos a utilizar las funciones `brentq` y `newton` para que el método
# utilizado quede más claro.

# Hay básicamente dos tipos de algoritmos para hallar raíces de ecuaciones no lineales:

# * Aquellos que operan en un intervalo $[a, b]$ tal que $f(a) \cdot f(b) < 0$.
# Más lentos, convergencia asegurada.
# * Aquellos que operan dando una condición inicial $x_0$ más o menos cerca de
# la solución. Más rápidos, convergencia condicionada.

# De los primeros vamos a usar la función `brentq` (aunque podríamos usar `bisect`)
# y de los segundos vamos a usar `newton` (que en realidad engloba los métodos
# de Newton y de la secante).

##_________________________ Example ___________________________##
# $\ln{x} = \sin{x} \Rightarrow F(x) \equiv \ln{x} - \sin{x} = 0$
# Lo primero que tengo que hacer es definir la ecuación, que matemáticamente
# será una función $F(x)$ que quiero igualar a cero.
def F(x):
    return np.log(x) - np.sin(x)
# Para hacernos una idea de las posibles soluciones siempre podemos representar
# gráficamente esa función:
x = np.linspace(0, 10, num=100)
with plt.style.context('seaborn-notebook'):
    plt.plot(x, F(x), 'k', lw=2, label="$F(x)$")
    plt.plot(x, np.log(x), label="$\log{x}$")
    plt.plot(x, np.sin(x), label="$\sin{x}$")
    plt.plot(x, np.zeros_like(x), 'k--')
    plt.legend(loc=4)
    plt.grid()
    plt.show()
# Y utilizando por ejemplo el método de Brent en el intervalo [0,3]:
# print(optimize.brentq(F, 0, 3))

##_________________________ Exercise ___________________________##
# Obtener por ambos métodos (newton y brentq) una solución a la ecuación
# tanx=x distinta de x=0. Visualizar el resultado.
## Argumentos extra ##
# Nuestras funciones siempre tienen que tomar como primer argumento la incógnita,
# el valor que la hace cero. Si queremos incluir más, tendremos que usar el argumento
# `args` de la funciones de búsqueda de raíces. Este patrón se usa también en otras
# partes de SciPy, como ya veremos.

# Vamos a resolver ahora una ecuación que depende de un parámetro:
    # $$\sqrt{x} + \log{x} = C$$.
def G(x, C):
    return C - np.sqrt(x) - np.log(x)
# **Nuestra incógnita sigue siendo $x$**, así que debe ir en primer lugar.
# El resto de parámetros van a continuación, y sus valores se especifican a la
# hora de resolver la ecuación usando `args`:
# print(optimize.newton(G, 2.0, args=(2,)))

##_________________________ Compressible Flow ___________________________##
# Esta es la relación isentrópica entre el número de Mach $M(x)$ en un conducto
# de área $A(x)$:
    # $$ \frac{A(x)}{A^*} = \frac{1}{M(x)} \left( \frac{2}{1 + \gamma} \left( 1 +
    # \frac{\gamma - 1}{2} M(x)^2 \right) \right)^{\frac{\gamma + 1}{2 (\gamma - 1)}}$$
# Para un conducto convergente:
    # $$ \frac{A(x)}{A^*} = 3 - 2 x \quad x \in [0, 1]$$
# Hallar el número de Mach en la sección 𝑥=0.9.
def A(x):
    return 3 - 2 * x
x = np.linspace(0, 1)
area = A(x)
r = np.sqrt(area / np.pi)
plt.fill_between(x, r, -r, color="#ffcc00")
plt.show()

# ¿Cuál es la función $F$ ahora? Hay dos opciones: definir una función $F_{0.9}(M)$
# que me da el número de Mach en la sección $0.9$ o una función $F(M; x)$ con la
# que puedo hallar el número de Mach en cualquier sección.

# Para resolver la ecuación utiliza el método de Brent (bisección). ¿En qué intervalo
# se encontrará la solución? ¡Si no te haces una idea es tan fácil como pintar
# la función $F$!
def F(M, x, g):
    return A(x) - (1 / M) * ((2 / (1 + g)) * (1 + (g - 1) / 2 * M ** 2)) ** ((g + 1) / (2 * (g - 1)))

# print(optimize.brentq(F, 0.01, 1, args=(0.9, 1.4)))

##_______________________________ Kepler law ________________________________##
# Representar la ecuación de Kepler
    # $$M = E - e \sin E$$

# que relaciona dos parámetros geométricos de las órbitas elípticas, la anomalía
# media $M$ y la anomalía excéntrica $E, para los siguientes valores de excentricidad:
    # * Tierra: $0.0167$
    # * Plutón: $0.249$
    # * Cometa Holmes: $0.432$
    # * 28P/Neujmin: $0.775$
    # * Cometa Halley: $0.967$

# Para ello utilizaremos el método de Newton (secante).
# 1- Define la función correspondiente a la ecuación de Kepler, que no solo es
# una ecuación implícita sino que además depende de un parámetro. ¿Cuál es la incógnita?
def Kepler(E, e, M):
    return M - E + e * np.sin(E)
# 2- Como primer paso, resuélvela para la excentricidad terrerestre y anomalía
# media $M = 0.3$. ¿Qué valor escogerías como condición inicial?
print(optimize.newton(Kepler, 0.3, args=(0.0167, 0.3)))
# 3- Como siguiente paso, crea un dominio (`linspace`) de anomalías medias entre
# $0$ y $2 \pi$ y resuelve la ecuación de Kepler con excentricidad terrestre para
# todos esos valores. Fíjate que necesitarás un array donde almacenar las soluciones.
# Representa la curva resultante.
N = 500

M = np.linspace(0, 2 * np.pi, N)
sol = np.zeros_like(M)

for ii in range(N):
    sol[ii] = optimize.newton(Kepler, sol[ii - 1], args=(0.249, M[ii]))
plt.plot(M, sol)
plt.show()

# 4- Como último paso, solo tienes que meter parte del código que ya has escrito
# en un bucle que cambie el valor de la excentricidad 5 veces.
M = np.linspace(0, 2 * np.pi, N)
sol = np.zeros_like(M)

plt.figure(figsize=(6, 6))

for ee in 0.0167, 0.249, 0.432, 0.775, 0.967:
    # Para cada valor de excentricidad sobreescribimos el array sol
    for ii in range(N):
        sol[ii] = optimize.newton(Kepler, sol[ii - 1], args=(ee, M[ii]))
    with plt.style.context('seaborn-notebook'):
        plt.plot(M, sol)
with plt.style.context('seaborn-notebook'):
    plt.xlim(0, 2 * np.pi)
    plt.ylim(0, 2 * np.pi)
    plt.xlabel("$M$", fontsize=15)
    plt.ylabel("$E$", fontsize=15)
    plt.gca().set_aspect(1)
    plt.grid(True)
    plt.legend(["Earth", "Pluto", "Comet Holmes", "28P/Neujmin", "Halley's Comet"], loc=2)
    plt.title("Kepler's equation solutions", fontsize=15)


'''
Usage
-----
Run as a script with no arguments
```
$ python run_Gibbs_correlated_2d_normal.py
```
Will run MCMC and produce plots

Purpose
-------
Sample from a 2-dim. Normal distribution using Gibbs Sampling

Target distribution:
# mean
>>> mu_D = np.asarray([-1.0, 1.0])
# covariance
>>> cov_DD = np.asarray([[2.0, 0.95], [0.95, 1.0]])

'''

import numpy as np
import scipy.stats
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')

from GibbsSampler import GibbsSampler2D

def draw_z0_given_z1(z1, u_stdnorm):
    ''' Draw sample from target conditional for z0 given z1

    Does not generate any randomness internally.
    Relies performing a transformation,
    Given a passed value "u" drawn externally from standard normal.

    Args
    ----
    z1 : scalar float
        Value of variable z1 to condition on.
    u_stdnorm : scalar float
        Value drawn from Standard Normal distribution
        Assumed: u ~ Normal(0.0, 1.0)
        Should be only source of randomness you need.

    Returns
    -------
    z0 : scalar float
        Sample from conditional p*(z0 | z1)
    '''
    # TODO Compute conditional distribution mean and covariance
    # Given known joint Gaussian over z0, z1 with mean mu_D and covar cov_DD

    # TODO Transform provided u_stdnorm value into sample from target conditional
    z0_samp = z1 * u_stdnorm # fixme
    return z0_samp

def draw_z1_given_z0(z0, u_stdnorm):
    ''' Draw sample from target conditional for z1 given z0

    Does not generate any randomness internally.
    Relies performing a transformation,
    Given a passed value "u" drawn externally from standard normal.

    Args
    ----
    z1 : scalar float
        Value of variable z0 to condition on.
    u_stdnorm : value drawn from Standard Normal distribution
        Assumed: u ~ Normal(0.0, 1.0)
        Should be only source of randomness you need.

    Returns
    -------
    z1 : scalar float
        Sample from conditional p*(z1 | z0)
    '''

    # Use Bishop PRML Equations 2.81 and 2.82 to compute conditional,
    # Given joint Gaussian over z0, z1 with mean mu_D and covar cov_DD
    mu_D = np.asarray([-1.0, 1.0])
    cov_DD = np.asarray([[2.0, 0.95], [0.95, 1.0]])
    a = 1
    b = 0
    var_10 = cov_DD[a,a] - (cov_DD[a,b] * cov_DD[b,a]) / cov_DD[b,b]
    mean_10 = mu_D[a] + cov_DD[a,b] / cov_DD[b,b] * (z0 - mu_D[b])

    # Draw from p(z1 | z0) =  Normal(mean_10, var_10)
    z1_samp = mean_10 + np.sqrt(var_10) * u_stdnorm
    return z1_samp

if __name__ == '__main__':
    ''' Main block to test Gibbs Sampler for D=2-dim. random. variable z_D

    Goal: Run separate MCMC chains from two initial values and verify that
    the sampler *converges* to the same distribution in both chains
    '''
    n_samples = 10000   # total number of iterations of MCMC
    n_keep = 5000       # number samples to keep
    random_state = 42   # seed for random number generator

    # Two initializations, labeled 'A' and 'B'
    z_initA_D = np.zeros(2)
    z_initB_D = np.asarray([1.0, -1.0])

    # No hyperparameters to tune for Gibbs
    G = 1

    # Prepare a plot to view samples from two chains (A/B) side-by-side
    _, ax_grid = plt.subplots(
        nrows=2, ncols=1, sharex=True, sharey=True,
        figsize=(2*G, 2*2))

    # Create samplers and run them for specified num iterations
    samplerA = GibbsSampler2D(draw_z0_given_z1, draw_z1_given_z0, random_state)
    z_fromA_list, samplerA_info = samplerA.draw_samples(zinit_D=z_initA_D, n_samples=n_samples)

    samplerB = GibbsSampler2D(draw_z0_given_z1, draw_z1_given_z0, random_state+1)
    z_fromB_list, samplerB_info = samplerB.draw_samples(zinit_D=z_initB_D, n_samples=n_samples)

    # Stack list of samples into a 2D array of size (S, D)
    # Keeping only the last few samples (and thus discarding burnin)
    zA_SD = np.vstack(z_fromA_list[-n_keep:])
    zB_SD = np.vstack(z_fromB_list[-n_keep:])

    # Plot samples as scatterplot
    # Use small alpha transparency value for visual debugging of rare/frequent samples
    ax_grid[0].plot(zA_SD[:,0], zA_SD[:,1], 'r.', alpha=0.05)
    ax_grid[1].plot(zB_SD[:,0], zB_SD[:,1], 'b.', alpha=0.05)
    # Mark initial points with "X"
    ax_grid[0].plot(z_fromA_list[0][0], z_fromA_list[0][1], 'rx')
    ax_grid[1].plot(z_fromB_list[0][0], z_fromB_list[0][1], 'bx')

    ##Label axes
    ax_grid[0].set_xlabel("z_0")
    ax_grid[1].set_xlabel("z_0")
    ax_grid[0].set_ylabel("z_1")
    ax_grid[1].set_ylabel("z_1")

    ##Title for plots
    ax_grid[0].set_title("Initialization A")
    ax_grid[1].set_title("Initialization B")

    # Pretty print some stats for the samples
    # To give a way to check "convergence" from the terminal's stdout
    msg_pattern = ("Gibbs from init %s | kept %d of %d samples | accept rate %.3f"
        + "\n    percentiles z0: 10th % 5.2f   50th % 5.2f   90th % 5.2f"
        + "\n    percentiles z1: 10th % 5.2f   50th % 5.2f   90th % 5.2f"
        )
    print(msg_pattern % (
        'A', n_keep, n_samples,
        samplerA_info['accept_rate_last_half'],
        *tuple(np.percentile(zA_SD[:,0:1], [10, 50, 90], axis=0)),
        *tuple(np.percentile(zA_SD[:,1:2], [10, 50, 90], axis=0)),
        ))
    print(msg_pattern % (
        'B', n_keep, n_samples,
        samplerB_info['accept_rate_last_half'],
        *tuple(np.percentile(zB_SD[:,0:1], [10, 50, 90], axis=0)),
        *tuple(np.percentile(zB_SD[:,1:2], [10, 50, 90], axis=0)),
        ))

    # Make plots pretty and standardized
    for ax in ax_grid.flatten():
        ax.set_xlim([-5, 5]);
        ax.set_ylim([-5, 5]);
        ax.set_aspect('equal', 'box');
        ax.set_xticks([-4, -2, 0, 2, 4])
        ax.set_yticks([-4, -2, 0, 2, 4])
    plt.tight_layout()
    plt.savefig("problem2_figure.pdf", bbox_to_inches='tight', pad_inches=0)
    plt.show()


# ------------------------------------------------------------------------------------------------------ #
# Author: Anna Lonergan                                                                                #
# Purpose: Script providing examples for the Newton-raphson Functions.                                   #
# ------------------------------------------------------------------------------------------------------ #

# necessary libraries:
import numpy as np
from newtonraphson import *

##################################################################
# EXAMPLE 1 -> 2 Functions                                       #
# Consider a market with nonlinear demand and supply curves.     #
# Solve the system of equations for equilibrium Price & Quantity #
# Supply: P(Qs) = 20*(1.5**Qs/100)                               #
# Demand: P(Qd) = (2000 - Qd)**0.7                               #
# The market equilibrium is:                                     #
# P = 165.55, Q = 521.26                                         #
# Now, lets test the algorithm                                   #
##################################################################

# Create system of equations
def F(X):
    # F takes one argument, X.
    # X is a tuple, list or vector containing values for the relevant system variables (P & Q)
    p = X[0]
    q = X[1]
    # System is a vector of zeros, the same length as X.
    # Each element of the vector "system" will hold a function.
    # Length of X and system *must* match, because the number of unknowns (2: P & Q) must match the number of equations (2).
    system = np.zeros(len(X))
    system[0] = 20*(1.5**(q/100)) - p
    system[1] = (2000 - q)**0.7 - p
    # This function returns a vector of 2 equations, evaluated at X.
    return system

# Set initial guess, P = 0, Q = 0
init = np.array([0,0])

# Find the Solution:
# Only required inputs are function & initial value guesses.
# Tolerance and Maximum Iterations are optional. I choose not to specify, so we'll use the NR function defaults.
solution, results = NR(F, init)

print("\nExample 1: Nonlinear Market")
print("Price in the market is %3.3f, and the Quantity Demanded is %3.3f." % (solution[0], solution[1]))
print("\n Iteration Output:")
print(results)

# Nice! The solutions from my solver match those listed above. It seems to be working in 2 dimensions.

##################################################################
# EXAMPLE 2 -> 3 Functions                                       #
# Consider a market with 3 cournot duopolists.                   #
# Each producer chooses its own quantity based on what the other #
# duopolists produce.                                            #
# Solve this system of the duopolists' reaction curves.          #
# Supply1: Q1(q2,q3) = 5 - 0.5q2 -0.3q3                          #
# Supply2: Q2(q1,q3) = 7 - 0.6q1 -0.1q3                          #
# Supply3: Q3(q1,q2) = 4 - 0.2q1 -0.4q2                          #
# The market equilibrium is:                                     #
# q1 = 1.67 , q2 = 5.87 , q3 = 1.32                              #
# Now, lets test the algorithm                                   #
##################################################################

# Create system of equations
def Duopoly(Q):
    # Duopoly takes one argument, Q.
    # Q is a tuple, list or vector containing values for the relevant system variables (Q1-Q3)
    q1 = Q[0]
    q2 = Q[1]
    q3 = Q[2]
    # System is a vector of zeros, the same length as Q.
    # Each element of the vector "system" will hold a function.
    # Length of Q and system *must* match, because the number of unknowns (3: q1, q2 & q3) must match the number of equations (3).
    system = np.zeros(len(Q))
    system[0] = 5 - (0.5*q2) - (0.3*q3) - q1
    system[1] = 7 - (0.6*q1) - (0.1*q3) - q2
    system[2] = 4 - (0.2*q1) - (0.4*q2) - q3
    # This function returns a vector of 3 equations, evaluated at Q.
    return system

# Set initial guess, all quantities equal 0.
init = np.array([0,0,0])

# Find the Solution:
# Only required inputs are function & initial value guesses.
# Tolerance and Maximum Iterations are optional. I choose not to specify, so we'll use the NR function defaults.
solution, results = NR(Duopoly, init)

print("\nExample 2: Cournot Duopolists")
print("Q1 is %3.3f, Q2 is %3.3f, and Q3 is %3.3f." % (solution[0], solution[1], solution[2]))
print("\n Iteration Output:")
print(results)

# Again, the solutions from my solver match those listed above. It seems to be working in 3 dimensions.


#!/usr.bin/env python
#D. Jones - 1/13/14
"""This code is from the IDL Astronomy Users Library"""
import numpy as np

def meanclip(image,
             clipsig=3, maxiter=5,
             converge_num=0.02, verbose=False,
             returnSubs=False):
    """Computes an iteratively sigma-clipped mean on a data set
    Clipping is done about median, but mean is returned.
    Converted from IDL to Python.

    CALLING SEQUENCE:
        mean,sigma = meanclip( data, clipsig=, maxiter=,
                               converge_num=, verbose=,
                               returnSubs=False)
        mean,sigma,subs = meanclip( data, clipsig=, maxiter=,
                                    converge_num=, verbose=,
                                    returnSubs=True)

    INPUT PARAMETERS:
         data           -  Input data, any numeric array

    OPTIONAL INPUT PARAMETERS:
         clipsig        -  Number of sigma at which to clip.  Default=3
         maxiter        -  Ceiling on number of clipping iterations.  Default=5
         converge_num   -  If the proportion of rejected pixels is less
                            than this fraction, the iterations stop.  Default=0.02, i.e.,
                            iteration stops if fewer than 2% of pixels excluded.
         verbose        -  Set this flag to get messages.
         returnSubs     -  if True, return subscript array for pixels finally used

    RETURNS:
         mean           -  N-sigma clipped mean.
         sigma          -  Standard deviation of remaining pixels.

    MODIFICATION HISTORY:
         Written by:       RSH, RITSS, 21 Oct 98
         20 Jan 99   -     Added SUBS, fixed misplaced paren on float call,
                            improved doc.  RSH
         Nov 2005    -     Added /DOUBLE keyword, check if all pixels are removed
                            by clipping W. Landsman
         Jan. 2014   -     Converted from IDL to Python by D. Jones
    """

    prf = 'MEANCLIP:  '

    #image = image.reshape(np.shape(image)[0]*np.shape(image)[1])
    subs = np.where(np.isfinite(image))[0]
    ct = len(subs)
    iter=0

    for i in range(maxiter):
        skpix = image[subs]
        iter = iter + 1
        lastct = ct
        medval = np.median(skpix)
        mom = [np.mean(skpix),np.std(skpix)]
        sig = mom[1]
        wsm = np.where(np.abs(skpix-medval) < clipsig*sig)[0]
        ct = len(wsm)
        if ct > 0: subs = subs[wsm]
        if (float(np.abs(ct-lastct))/lastct <= converge_num) or \
                (iter > maxiter) or (ct == 0):
            break
    #mom = moment(image[subs],double=double,max=2)
    mean = np.mean(image[subs])
    sigma = np.std(image[subs])

    if verbose:
        print(prf+strn(clipsig)+'-sigma clipped mean')
        print(prf+'Mean computed in ',iter,' iterations')
        print(prf+'Mean = ',mean,',  sigma = ',sigma)

    if not returnSubs:
        return(mean,sigma)
    else:
        return(mean,sigma,subs)


import math
import numpy as np

def primes_up_to(n):
    non_prime = np.zeros(n+1, dtype=bool)
    non_prime[0] = True
    non_prime[1] = True
    candidate = 2
    last_candidate = math.ceil(math.sqrt(n))
    while candidate <= last_candidate:
        while non_prime[candidate]:
            candidate += 1
        i = candidate
        while i*candidate <= n:
            non_prime[i*candidate] = True
            i += 1
        candidate += 1
    primes = []
    for i in xrange(n+1):
        if not non_prime[i]:
            primes.append(i)
    return primes


import numpy as np

def linear_solve(A, b):
  AAT = A@(A.T)
  # w = np.linalg.solve(AAT, b)
  w = np.linalg.lstsq(AAT, b)[0]
  x = (A.T)@w
  return x

class RandomSinModel:
  '''
  H(n) :  h(x) = \sum_j [b_j*sin(a_0+a_1*x+a_2*x^2+...)]
  '''

  def __init__(self, n, MAX_DEGREE=3):
      self.D = MAX_DEGREE+1
      self.n = n # No. of Features
      self.F = np.random.rand(self.D, self.n) * 10 # Feature Tranformation matrix D X n
      self.a = None

  def generate_features(self, X):
      X = X.flatten()
      arrays = [X**i for i in range(self.D)] # includes constant
      A = np.stack(arrays, axis = 1)        # N_data X D
      A = A @ self.F                        # N_data X n
      A = np.sin(A)
      return A

  def fit(self, X, Y, refit=False):
      '''
      X: (d,1)
      Y: (d,1)
      '''
      if self.a and (not refit):
         raise ValueError("Re-Fitting")
      A = self.generate_features(X)
      # self.a = linear_solve(A, Y)
      self.a = np.linalg.lstsq(A, Y)[0]
      return self

  def predict(self, X, A=None):
      if (not A):
        A = self.generate_features(X)
      return A@self.a

  def score(self, X, y):
      '''
        RMSE
      '''
      y_pred = self.predict(X=X)
      return np.linalg.norm(y-y_pred, ord=2)/np.sqrt(X.shape[0])


import numpy as np
from matplotlib import pyplot as plt

'''
Implement a function that given the domain interval, the forcing function, the number of discretization points,
the boundary conditions, returns the matrix  and the the right hand side b.
'''
def finDif(omega, f, n, bc):
    span = omega[ -1 ] - omega[ 0 ]
    delta = span / ( n - 1 )

    # A's diagonals
    diags = [
        30 * np.ones( (n,) ),
        -16 * np.ones( (n - 1,) ),
        np.ones( (n - 2,) )
    ]

    A = np.diag( diags[ 0 ], 0 ) \
        + np.diag( diags[ 1 ], -1 ) \
        + np.diag( diags[ 1 ], 1 ) \
        + np.diag( diags[ 2 ], -2 ) \
        + np.diag( diags[ 2 ], 2 )

    A /= (12 * delta**2)

    x = np.linspace( omega[ 0 ], omega[ -1 ], n )
    b = f( x )

    # boundary conditions
    A[ 0, : ] = 0
    A[ :, 0 ] = 0
    A[ 0, 0 ] = 1
    b[ 0 ] = bc[ 0 ]

    A[ -1, : ] = 0
    A[ :, -1 ] = 0
    A[ -1, -1 ] = 1
    b[ -1 ] = bc[ -1 ]
    return (A, b)

omega = [ 0, np.pi ]
f = lambda x : np.sin(x)

n = 100
bc = [ 0, 0 ]
(A, b) = finDif( omega, f, n, bc )

print(A)
print(b)

'''
Implement two functions that compute the LU and the Cholesky factorization of the system matrix A
'''
def LU(A, tol=1e-15):
    A = A.copy()
    size = len( A )

    for k in range(size - 1):
        pivot = A[ k, k ]
        if abs(pivot) < tol:
            raise RuntimeError("Null pivot")

        for j in range( k + 1, size ):
            A[ j, k ] /= pivot

        for j in range( k + 1, size ):
            A[ k + 1 : size, j ] -= A[ k + 1 : size, k ] * A[ k, j ]

    L = np.tril( A )
    for i in range( size ):
        L[ i, i ] = 1.0

    U = np.triu( A )
    return (L, U)

(L, U) = LU( A )
print(L, U)

def cholesky(A):
    A = A.copy()
    size = len( A )

    for k in range( size - 1 ):
        A[ k, k ] = np.sqrt(A[ k, k ])

        A[ k + 1 : size, k ] = A[ k + 1 : size, k ] / A[ k, k ]

        for j in range(k+1,size):
            A[ j : size, j ] = A[ j : size, j ] - A[ j : size, k ] * A[ j, k ]

    A[ -1, -1 ] = np.sqrt(A[ -1, -1 ])

    L = np.tril( A )
    Lt = L.transpose()
    return (L, Lt)

(Ht, H) = cholesky( A )
print(Ht, H)

# Implement forward and backward substitution functions to exploit the developed factorization methods to solve the
# derived linear system of equations.

def L_solve(L, rhs):
    size = len( L )
    x = np.zeros( size )

    x[ 0 ] = rhs[ 0 ] / L[ 0, 0 ]

    for i in range( 1, size ):
        x[ i ] = ( rhs[ i ] - np.dot( L[ i, 0 : i ], x[ 0 : i ] ) ) / L[ i, i ]
    return x

def U_solve(U, rhs):
    size = len( U )
    x = np.zeros( size )

    x[ -1 ] = rhs[ -1 ] / L[ -1, -1 ]

    for i in reversed( range( size - 1 ) ):
        x[ i ] = ( rhs[ i ] - np.dot( U[ i, i + 1 : size ], x[ i + 1 : size ] ) ) / U[ i, i ]
    return x

'''
Solve the derived linear system using the implemented functions and plot the computed solution:
'''

(fig, ax) = plt.subplots()

# exact solution
x = np.linspace( omega[0], omega[-1], n)
u_exact = np.sin( x )

ax.plot( x, u_exact, label = 'exact' )

# using LU factorization
w = L_solve( L, b )
u = U_solve( U, w )

ax.plot( x, u, label = 'lu' )

# using cholesky factorizations
w = L_solve( Ht, b )
u = U_solve( H, w )

ax.plot( x, u, label = 'cholesky' )

ax.legend()
ax.grid()

fig.savefig( 'plot1.svg' )

'''
Considering the new domain [0, 1] and the forcing term  with B.C. ,
on  produce a plot and a table where you show the decay of the error w.r.t. the number of grid points.
(The analytical solution for the above problems is )
'''

def compute_errors(omega, f, fex, bc, npoints):
    errors = []

    for idx in range( len(npoints) ):
        npts = npoints[ idx ]

        x = np.linspace( omega[0], omega[1], npts )
        ex = fex( x )

        ( A, b ) = finDif( omega, f, npts, bc )
        ( L, U ) = LU( A )
        w = L_solve( L, b )
        u = U_solve( U, w )

        err = sum( ( ex - u )**2 )**0.5
        errors.append( err )
    return errors

omega = [ 0, 1 ]

def func(x):
    return x * (1 - x)

def fex(x):
    return x**4/12 - x**3/6 + x/12

bc = [ 0, 0 ]

npoints = np.arange( 10, 310, 10 )

errors = compute_errors( omega, func, fex, bc, npoints )

(fig, ax) = plt.subplots()

ax.set_yscale( 'log' )
ax.plot( npoints, errors )

fig.savefig( 'plot2.svg' )

'''
Exploit the derived LU factorizations to compute the condition number of the system's matrix  using the original problem formulation.
'''
def PM(A, z0, tol=1e-12, nmax=10000):
    q = z0 / np.linalg.norm( z0, 2 )

    it = 0
    err = tol + 1

    while it < nmax and err > tol:
        z = A.dot( q )
        l = q.T.dot( z )
        err = np.linalg.norm( z - l*q, 2 )
        q = z / np.linalg.norm( z, 2 )
        it = it + 1
    return (l, q)

def IPM(A, x0, mu, eps=1.0e-12, nmax=10000):
    M = A - mu * np.eye(len(A))

    (L, U) = LU( M )
    q = x0 / np.linalg.norm( x0, 2 )

    err = eps + 1.0
    it = 0
    while err > eps and it < nmax:
        y = L_solve( L, q )
        x = U_solve( U, y )
        q = x / np.linalg.norm( x, 2 )
        z = A.dot( q )
        l = q.T.dot( z )
        err = np.linalg.norm( z - l*q, 2 )
        it = it + 1
    return (l, q)

def condNumb(A):
    z0 = np.ones( ( len(A), ))
    lmax = PM( A, z0 )[ 0 ]
    lmin = IPM( A, z0, 0.0 )[ 0 ]

    condNum = lmax / lmin
    return condNum

condNum = condNumb( A )
print( condNum )

'''
Implement a preconditioned Conjugant Gradient method to solve the original linear system of equations using an iterative method:

'''
def conjugate_gradient(A, b, P, nmax=len(A), eps=1e-10):
    x = np.zeros_like( b )
    r = b - A.dot( x )

    rho0 = 1
    p0 = np.zeros_like( b )

    err = eps + 1.0

    it = 1
    while it < nmax and err > eps:
        z = np.linalg.solve( P, r )
        rho = r.dot( z )

        if it > 1:
            beta = rho / rho0
            p = z + beta * p0

        else:
            p = z

        q = A.dot( p )
        alpha = rho / p.dot( q )

        x += p * alpha
        r -= q * alpha

        p0 = p
        rho0 = rho

        err = np.linalg.norm( r, 2 )
        it = it + 1

    print( f'iterations: {it}' )
    print( f'error: {err}' )
    return x

omega = [ 0, np.pi ]
x = np.linspace( omega[ 0 ], omega[ -1 ], n )

ex = np.sin( x )

u = conjugate_gradient( A, b, np.diag(np.diag( A ) ))

(fig, ax) = plt.subplots()

ax.plot( x, ex, label = 'exact' )
ax.plot( x, u, label = 'CG' )

ax.legend()
fig.savefig( 'plot3.svg' )

exit( 0 )

'''
Consider the following time dependent variation of the PDE starting from the orginal problem formulation:

for , with  and

Use the same finite difference scheme to derive the semi-discrete formulation and solve it using a forward Euler's method.

Plot the time dependent solution solution at , ,
'''

#TODO
'''
Given the original  system, implement an algorithm to compute the eigenvalues and eigenvectors of the matrix .
oExploit the computed LU factorization
'''

#TODO

'''
Compute the inverse of the matrix A exploiting the derived LU factorization

'''
#TODO

'''
Consider the following Cauchy problem

Implement a Backward Euler's method in a suitable function and solve the resulting non-linear equation using a Newton's method.
'''

#TODO


import numpy as np

from source.week2.ex2_1 import construct_element_table, xy

abc = np.zeros((3, 3))
delta = 0

def a(j, k, X, Y):
    return X[j] * Y[k] - X[k] * Y[j]

def b(Y, j, k):
    return Y[j] - Y[k]

def c(X, j, k):
    return X[k] - X[j]

def basfun(element_idx, X, Y, etov_dict):
    """
    :param element_idx:
    :param X:
    :param Y:
    :param etov_dict:
    :return:
    """

    # Formula 2.3

    abc = np.zeros((3, 3))
    v1, v2, v3 = etov_dict[element_idx]
    v1 -= 1
    v2 -= 1
    v3 -= 1

    abc[0][0] = X[v2] * Y[v3] - X[v3] * Y[v2]
    #   1  b
    abc[0][1] = Y[v2] - Y[v3]
    #   1  c
    abc[0][2] = X[v3] - X[v2]

    #   2  a
    abc[1][0] = X[v1] * Y[v3] - X[v3] * Y[v1]
    #   2  b
    abc[1][1] = -(Y[v1] - Y[v3])
    #   2  c
    abc[1][2] = X[v3] - X[v1]

    #   3  a
    abc[2][0] = X[v1] * Y[v2] - X[v2] * Y[v1]
    #   3  b
    abc[2][1] = Y[v1] - Y[v2]
    #   3  c
    abc[2][2] = X[v2] - X[v1]

    # Formula 2.1 TODO
    # Delta : positive due to clockwise ordering
    _delta = 0.5 * (X[v2] * Y[v3] - Y[v2] * X[v3] - (X[v1] * Y[v3] - Y[v1] * X[v3]) + X[v1] * Y[v2] - Y[v1] * X[v2])

    return abc, _delta

def main():
    """Test case:
    (x_0 , y_0 ) = (−2.5, −4.8), L_1 = 7.6, L_2 = 5.9, noelms1 = 4, noelms2 = 3
    """
    X, Y = xy(-2.5, -4.8, 7.6, 5.9, 4, 3)
    etov_dict, M = construct_element_table(4, 3)
    element_idx = 4
    test_element = basfun(element_idx, X, Y, etov_dict)
    print("delta", test_element[1])
    print("abc", test_element[0])

if __name__ == '__main__':
    main()


import timeit

import numpy as np
import numpy.linalg
from scipy.linalg import cho_factor, cho_solve

# We are trying to solve the following system
# (A.T * A) * x = A.T * b
# Where x are the polynomial coefficients and b is are the input points

# First we build A
deg = 3
x = np.arange(50 * 1.0)
A = np.vstack(tuple(x**n for n in range(deg, -1, -1))).T

# The first way to solve this is using the pseudoinverse, which can be precomputed
# x = (A.T * A)^-1 * A^T * b = PINV b
PINV = np.linalg.pinv(A)

# Another way is using the Cholesky decomposition
# We can note that at (A.T * A) is always positive definite
# By precomputing the Cholesky decomposition we can efficiently solve
# systems of the form (A.T * A) x = c
CHO = cho_factor(np.dot(A.T, A))

def model_polyfit_old(points, deg=3):
  A = np.vstack(tuple(x**n for n in range(deg, -1, -1))).T
  pinv = np.linalg.pinv(A)
  return np.dot(pinv, map(float, points))

def model_polyfit(points, deg=3):
  A = np.vander(x, deg + 1)
  pinv = np.linalg.pinv(A)
  return np.dot(pinv, map(float, points))

def model_polyfit_cho(points, deg=3):
  A = np.vander(x, deg + 1)
  cho = cho_factor(np.dot(A.T, A))
  c = np.dot(A.T, points)
  return cho_solve(cho, c, check_finite=False)

def model_polyfit_np(points, deg=3):
  return np.polyfit(x, points, deg)

def model_polyfit_lstsq(points, deg=3):
  A = np.vander(x, deg + 1)
  return np.linalg.lstsq(A, points, rcond=None)[0]

TEST_DATA = np.linspace(0, 5, num=50) + 1.

def time_pinv_old():
  model_polyfit_old(TEST_DATA)

def time_pinv():
  model_polyfit(TEST_DATA)

def time_cho():
  model_polyfit_cho(TEST_DATA)

def time_np():
  model_polyfit_np(TEST_DATA)

def time_lstsq():
  model_polyfit_lstsq(TEST_DATA)

if __name__ == "__main__":
  # Verify correct results
  pinv_old = model_polyfit_old(TEST_DATA)
  pinv = model_polyfit(TEST_DATA)
  cho = model_polyfit_cho(TEST_DATA)
  numpy = model_polyfit_np(TEST_DATA)
  lstsq = model_polyfit_lstsq(TEST_DATA)

  assert all(np.isclose(pinv, pinv_old))
  assert all(np.isclose(pinv, cho))
  assert all(np.isclose(pinv, numpy))
  assert all(np.isclose(pinv, lstsq))

  # Run benchmark
  print("Pseudo inverse (old)", timeit.timeit("time_pinv_old()", setup="from __main__ import time_pinv_old", number=10000))
  print("Pseudo inverse", timeit.timeit("time_pinv()", setup="from __main__ import time_pinv", number=10000))
  print("Cholesky", timeit.timeit("time_cho()", setup="from __main__ import time_cho", number=10000))
  print("Numpy leastsq", timeit.timeit("time_lstsq()", setup="from __main__ import time_lstsq", number=10000))
  print("Numpy polyfit", timeit.timeit("time_np()", setup="from __main__ import time_np", number=10000))


import numpy as np
import load_foil as lf
import matplotlib.pyplot as mp;

# Returns the second derivative of the interpolating function at each point where
# xa is the array containing the x coordinate of each point
# ya is the array containing the y coordinate of each point
# n is the number of points
def spline(xa, ya, n):
	u = np.zeros(n)
	y2 = np.zeros(n)

	y2[1] = 0.0
	u[1] = 0.0

	for i in range(1, n-1):
		sig = (xa[i] - xa[i-1]) / (xa[i+1] - xa[i-1])
		p = sig * y2[i-1] + 2.0
		y2[i] = (sig - 1.0) / p
		u[i] = (ya[i+1] - ya[i]) / (xa[i+1] - xa[i]) - (ya[i] - ya[i-1]) / (xa[i] - xa[i-1])
		u[i] = (6.0 * u[i] / (xa[i+1] - xa[i-1]) - sig * u[i-1]) / p

	y2[n - 1] = 0.0
	for k in range(n-2, 0, -1):
		y2[k] = y2[k] * y2[k+1] + u[k]

	return y2

# Return the value of the spline at a given x coordinate where
# xa is the array containing the x coordinate of each point
# ya is the array containing the y coordinate of each point
# y2a is the array containing the second derivaive of the function at each point
# n is the number of points
def splint(xa, ya, y2a, n, x):
	k = 0
	h = 0.0
	b = 0.0
	a = 0.0
	klo=1
	khi=n
	while khi - klo > 1:
		k = (khi + klo) // 2
		if xa[k - 1] > x:
			khi = k
		else:
			klo = k

	khi = khi - 1
	klo = klo - 1

	h = xa[khi] - xa[klo]

	if (h == 0.0):
		raise Exception("Bad xa input to routine splint")

	a = (xa[khi] - x) / h
	b = (x - xa[klo]) / h

	return a * ya[klo] + b * ya[khi] + (((a**3) - a) * y2a[klo] + ((b**3) - b) * y2a[khi]) * (h * h) / 6.0

# Return the spline function passing through each provided point where
# xa is the array containing the x coordinate of each point
# ya is the array containing the y coordinate of each point
# n is the number of points
def spline_fun(xa, ya, n):
	y2 = spline(xa, ya, n)
	return lambda x: splint(xa, ya, y2, n, x)

if __name__ == "__main__":
	(dim,ex,ey,ix,iy) = lf.load_foil("k1.dat")
	espline = spline_fun(ex, ey, int(dim[0]))
	ispline = spline_fun(ix, iy, int(dim[1]))

	r = np.arange(0, 1.00001, 0.001)

	mp.plot(r, list(map(espline, r)), linewidth = 1.0)
	mp.plot(ex, ey, marker='.', linestyle="None")
	mp.plot(r, list(map(ispline, r)), linewidth = 1.0)
	mp.plot(ix, iy, marker='.', linestyle="None")
	mp.axis('equal')
	mp.title("Cubic spline interpolation of the airfoil")
	mp.show()


import numpy as np

def comp_k_out_of_n(pf, k):
    '''
    COMPUTE PROBABILITY OF FAILURE OF THE SYSTEM RELIABILITY
    ========================================================

    The system is good if and only if at least K of its n components are good.

    => Model assumptions:
        1. Components and the system are 2-state (eg, good or bad)
        2. Component states are statiscally independent
        3. The system is good if and only if at least k of its n components
           are good

    Source: IEEE TRANSACTIONS ON RELIABILITY, VOL. R-33, NO. 4, OCTOBER 1984
            page 321, R.E. Barlow and K.D. Heidtmann

    Parameters
    ----------
    pf : array-like
        the probability of failure of each element
    k : integer
        the number of components necessary to make the system work

    Retuns
    ------
    PF_sys : float
        system probability of failure
    '''

    pf = np.array(pf)
    n = pf.size

    # in case of no element
    if n == 0:
        return np.nan

    # n = len(pf)
    # k = ncomp-1
    PF_sys = np.zeros(1)
    nk = n-k
    m = k+1
    A = np.zeros(m+1)
    A [1] = 1
    L = 1
    for j in range(1,n+1):
        h = j + 1
        Rel = 1-pf[j-1]
        if nk < j:
            L = h - nk
        if k < j:
            A[m] = A[m] + A[k]*Rel
            h = k
        for i in range(h, L-1, -1):
            A[i] = A[i] + (A[i-1]-A[i])*Rel
    PF_sys = 1-A[m]
    return PF_sys

class System_of_Subsystems:
    """
    System of Subsystems
    ====================

    Separate all systems components in k-out-of-n susbsystems according to an
    assingment vector. Subsystems are in series.

    Parameters:
    -----------
    assignments : array
        array with subsystem assignment of each component.
    k_list : array
        list of k values of each subsystem.
    """
    def __init__(self, assignments, k_dict):
        self.assignments = np.array(assignments)
        self.k_dict = k_dict

    def compute_system_pf(self, pf_list):
        """
        Compute system P_f
        ==================

        Compute system probability of failure.

        Parameters:
        -----------
        pf_list : array
            list of probabilities of failure of each component.

        Returns:
        --------
        pf_sys : float
            probability of failure of th eentire system
        """
        pf_list = np.array(pf_list)
        subsystem_pfs = []
        for zone, k in self.k_dict.items():
            zone_pfs = pf_list[self.assignments == zone]
            subsystem_pfs.append(comp_k_out_of_n(zone_pfs, k))
        return comp_k_out_of_n(subsystem_pfs, len(subsystem_pfs))


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

"""
The module provides parametric equations for well-known curves

"""

import numpy as np
from scipy.special import fresnel

from skcurve import Curve

def arc(t_start: float = 0.0,
        t_stop: float = np.pi * 2,
        p_count: int = 49,
        r: float = 1.0,
        c: float = 0.0) -> Curve:
    r"""Produces arc or full circle curve

    Produces arc using the following parametric equations:

    .. math::

        x = cos(\theta) \dot r + c
        y = sin(\theta) \dot r + c

    By default computes full circle.

    Parameters
    ----------
    t_start : float
        Start theta
    t_stop : float
        Stop theta
    p_count : int
        The number of points
    r : float
        Circle radius
    c : float
        Circle center

    Returns
    -------
    curve : Curve
        Acr curve

    """

    theta = np.linspace(t_start, t_stop, p_count)

    x = np.cos(theta) * r + c
    y = np.sin(theta) * r + c

    return Curve([x, y], tdata=theta)

def lemniscate_of_bernoulli(t_start: float = 0.0,
                            t_stop: float = np.pi*2,
                            p_count: int = 101,
                            c: float = 1.0) -> Curve:
    """Produces Lemniscate of Bernoulli curve

    Parameters
    ----------
    t_start
    t_stop
    p_count
    c

    Returns
    -------

    """

    theta = np.linspace(t_start, t_stop, p_count)

    c_sq2 = c * np.sqrt(2)
    cos_t = np.cos(theta)
    sin_t = np.sin(theta)
    denominator = sin_t ** 2 + 1

    x = (c_sq2 * cos_t) / denominator
    y = (c_sq2 * cos_t * sin_t) / denominator

    return Curve([x, y], tdata=theta)

def archimedean_spiral(t_start: float = 0.0,
                       t_stop: float = 5 * np.pi,
                       p_count: int = 200,
                       a: float = 1.5,
                       b: float = -2.4) -> Curve:
    """Produces Archimedean spiral curve

    Parameters
    ----------
    t_start
    t_stop
    p_count
    a
    b

    Returns
    -------

    """

    theta = np.linspace(t_start, t_stop, p_count)
    x = (a + b * theta) * np.cos(theta)
    y = (a + b * theta) * np.sin(theta)

    return Curve([x, y], tdata=theta)

def euler_spiral(t_start: float = -3 * np.pi / 2,
                 t_stop: float = 3 * np.pi / 2,
                 p_count: int = 1000) -> Curve:
    """Produces Euler spiral curve

    Parameters
    ----------
    t_start
    t_stop
    p_count

    Returns
    -------

    """

    t = np.linspace(t_start, t_stop, p_count)
    ssa, csa = fresnel(t)

    return Curve([csa, ssa], tdata=t)

def lissajous(t_start: float = 0.0,
              t_stop: float = 2*np.pi,
              p_count: int = 101,
              a_ampl: float = 1.0,
              b_ampl: float = 1.0,
              a: float = 3.0,
              b: float = 2.0,
              d: float = 0.0,) -> Curve:
    """

    Parameters
    ----------
    t_start
    t_stop
    p_count
    a_ampl
    b_ampl
    a
    b
    d

    Returns
    -------

    """

    theta = np.linspace(t_start, t_stop, p_count)

    x = a_ampl * np.sin(a * theta + d)
    y = b_ampl * np.sin(b * theta)

    return Curve([x, y], tdata=theta)

def helix(t_start: float = -3 * np.pi,
          t_stop: float = 3 * np.pi,
          p_count: int = 100,
          a: float = 1.0,
          b: float = 1.0) -> Curve:
    """Produces 3-d helix curve

    Parameters
    ----------
    t_start : float
    t_stop : float
    p_count : int
    a : float
    b : float

    Returns
    -------

    """

    theta = np.linspace(t_start, t_stop, p_count)
    x = np.sin(theta) * a
    y = np.cos(theta) * a
    z = theta * b

    return Curve([x, y, z], tdata=theta)

def irregular_helix(t_start: float = -4 * np.pi,
                    t_stop: float = 4 * np.pi,
                    z_start: float = -2.0,
                    z_stop: float = 2.0,
                    p_count: int = 100) -> Curve:
    """Produces 3-d irregular helix curve

    Parameters
    ----------
    t_start
    t_stop
    z_start
    z_stop
    p_count

    Returns
    -------

    """

    theta = np.linspace(t_start, t_stop, p_count)
    z = np.linspace(z_start, z_stop, p_count)
    r = z ** 2 + 1
    x = r * np.sin(theta)
    y = r * np.cos(theta)

    return Curve([x, y, z], tdata=theta)


import math
from typing import Tuple, Union, Any

import numpy as np
from numpy import dot

def unit_vector(vec: tuple):
    """

    :param vec:
    :return: unit vector
    """
    return vec / np.linalg.norm(vec)

def angle_between_vector(v1: tuple, v2: tuple) -> float:
    """
    two vectors have either the same direction -  https://stackoverflow.com/a/13849249/71522
    :param v1:
    :param v2:
    :return:
    """
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))

def dot_between_vector(v1: tuple, v2: tuple) -> Any:
    """
    two vectors have either the same direction -  https://stackoverflow.com/a/13849249/71522
    :param v1:
    :param v2:
    :return:
    """
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    return np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)

def get_point_after_certain_distance(
    start: tuple, end: tuple, d: float, dt: float
) -> Tuple[float, float]:
    """
    https://math.stackexchange.com/questions/175896/finding-a-point-along-a-line-a-certain-distance-away-from-another-point

    :param start:
    :param end:
    :param d:
    :param dt:
    :return:
    """
    if d == 0:
        return start
    t = dt / d
    new_x = ((1 - t) * start[0]) + (t * end[0])
    new_y = ((1 - t) * start[1]) + (t * end[1])
    return new_x, new_y

def get_midpoint(start: tuple, end: tuple) -> Tuple[float, float]:
    """
    https://www.mathsisfun.com/algebra/line-midpoint.html

    :param start:
    :param end:
    :return:
    """
    return ((start[0] + end[0]) / 2), ((start[1] + end[1]) / 2)

def get_end_coordinate(
    start: tuple, angle_in_degree: float, distance: float
) -> Tuple[float, float]:
    """
    # https://math.stackexchange.com/questions/39390/determining-end-coordinates-of-line-with-the-specified-length-and-angle

    :param start:
    :param angle_in_degree:
    :param distance:
    :return:
    """
    x2 = start[0] + (distance * math.cos(angle_in_degree))
    y2 = start[1] + (distance * math.sin(angle_in_degree))
    return x2, y2

def get_center_of_mass(x, y) -> Tuple[Union[Any, float], Union[Any, float]]:
    """
    https://math.stackexchange.com/questions/24485/find-the-average-of-a-collection-of-points-in-2d-space

    :param x:
    :param y:
    :return:
    """
    return np.mean(x), np.mean(y)

def get_perpendicular_point(
    start: tuple, end: tuple, offset=10
) -> Tuple[Tuple[float, float], Tuple[float, float]]:
    """
    https://stackoverflow.com/questions/133897/how-do-you-find-a-point-at-a-given-perpendicular-distance-from-a-line?rq=1

    :param start:
    :param end:
    :param offset:
    :return:
    """
    x1, y1 = start[0], start[1]
    x2, y2 = end[0], end[1]

    dx = x1 - x2
    dy = y1 - y2

    dist = math.sqrt((dx * dx) + (dy * dy))

    dx /= dist
    dy /= dist

    x3 = x1 + (offset * dy)
    y3 = y1 - (offset * dx)

    x4 = x1 - (offset * dy)
    y4 = y1 + (offset * dx)

    return (x3, y3), (x4, y4)

def vector(p1, p2) -> Tuple[float, float]:
    """

    :param p1:
    :param p2:
    :return:
    """
    return p1[0] - p2[0], p1[1] - p2[1]

def cosine_similarity(p1, p2, p3):
    """

    :param p1:
    :param p2:
    :param p3:
    :return:
    """
    a = vector((p1[0], p1[1]), (p2[0], p2[1]))
    b = vector((p1[0], p1[1]), (p3[0], p3[1]))
    return dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def diagonal_distance(p1: tuple, p2: tuple, d1: float, d2: float) -> float:
    """

    :param p1:
    :param p2:
    :param d1:
    :param d2:
    :return:
    """
    dx = abs(p1[0] - p2[0])
    dy = abs(p1[1] - p2[1])

    return d1 * (dx + dy) + (d2 - 2 * d1) * min(dx, dy)

def euclidean_distance(p1: tuple, p2: tuple) -> float:
    """

    :param p1:
    :param p2:
    :return:
    """
    return math.hypot(p2[0] - p1[0], p2[1] - p1[1])

def manhattan_distance(p1: tuple, p2: tuple) -> float:
    """

    :param p1:
    :param p2:
    :return:
    """
    return abs(p2[0] - p1[0]) + abs(p2[1] - p1[1])

def compute_center_of_mass(input_array, axis: int = 1):
    """
    Compute center of mass

    :param axis:
    :param input_array: (dim x N)
    :return:
    """
    return np.mean(input_array, axis=axis)

def new_mass(mean, input_array):
    """
    Subtract the corresponding center of mass from every point

    new_mass = (dim x N) - (dim x 1)

    :param mean:
    :param input_array:
    :return:
    """

    return input_array - mean[:, np.newaxis]

def decompose_matrix(matrix):
    """

    :param matrix:
    :return:
    """

    return np.linalg.svd(matrix)


'''
Método de Butcher
y algunos implemetados a partir de él:
RK4: runge-kuta clásico
RK2, con alfa beta parámetros
'''

import sympy as sp
import numpy as np

def succ_butcher(F,x,y,a,b,h):
    '''
    f derivada
    y(x) = y que es el valor inicial
    h = variación
    a,b son coeficiente del arreglo de butcher
    | c_1 | a11 a12 ...a1n
    | c_2 | a21 a22 ...a2n
             ...
    | c_n | an1 an2 ...ann
    ------------------------
          | b1   b2 ... bn

    y representa

    x_{n+1} = x_n + h \sum_{j=1}^n b_j K_j(t_n, x_n)
    k_i(t,x) = f(t+c_i h, x+ h \sum_{j=1}^n a_{ij} k_j(t,x))

    '''
    n=len(b)
    c = [ sum(a[i]) for i in range(n)]
    K = sp.symbols('k0:'+str(n))

    def fk(f,i,t,x,h):
        '''Representa K_i(t,x) = f(t+c_ih, sum a K_j) )
        '''
        return f( t+c[i]*h, x+h*sum(
        [ a[i][j]*K[j] for j in range(n)]) )

    l=[ - K[i] + fk(F,i,x,y,h)    for i in range(n)]

    sk =sp.solve(l, K)

    def solk(K, sk):
        '''Solve puede que no nos devuelva todas las soluciones si son 0'''
        l = [j for j in range(n)]
        for i in range(n):
            if K[i] in sk:
                l[i] = sk[K[i]]
            else:
                l[i] =0
        return l

    sk =solk(K,sk)

    return y + h*sum([ b[i]*sk[i] for i in range(n)] )

def complete_butcher(F, x0, y0,xfinal, N, a, b):
    '''
    F función
    x,y condiciones iniciales y(x_0) = y_0
    xfinal = valor final hasta el que calcular
    N número de intervalos
    '''
    t,z = sp.symbols('t z')
    X = np.linspace(x0,xfinal,N+1)
    Y = [y0]
    h = (xfinal - x0)/N

    # Calculamos la aproximación
    rk= succ_butcher(F,t,z,a,b,h)

    for n in range(N):
        Y.append(rk.subs({t:X[n],z:Y[n]}) )
    return X,Y

def succ_RK4(f,x, y, h):
    n=4
    a = [[0,0,0,0],
         [1/2,0,0,0],
         [0,1/2,0,0],
         [0,0,1,0]
        ]

    b = [1/6,1/3,1/3,1/6]

    return succ_butcher(f,x,y,a,b,h)

def succ_RK2(f,x,y,h, alpha=1, beta=1/2):
    '''
    Recordemos que es óptimo en alpha*beta = 1/2
    - Si alpha = 1, beta = 1/2: método Punto medio
    - Si alpha 1/2, beta = 1: Heun (trapecio)
    '''
    n=2
    a = [
        [0,0],
        [beta, 0]
    ]
    b = [1-alpha, alpha]

    return succ_butcher(f,x,y,a,b,h)

def complete_RK2(F, x0, y0, xfinal, N, alpha=1, beta=1/2):
    n=2
    a = [
        [0,0],
        [beta, 0]
    ]
    b = [1-alpha, alpha]
    return complete_butcher(F, x0, y0, xfinal, N, a, b)

def complete_RK4(F, x0, y0, xfinal, N):
    '''
    F función
    x,y condiciones iniciales y(x_0) = y_0
    xfinal = valor final hasta el que calcular
    N número de intervalos
    '''
    t,z = sp.symbols('t z')
    X = np.linspace(x0,xfinal,N+1)
    Y = [y0]
    h = (xfinal - x0)/N

    # Calculamos la aproximación
    rk4= succ_RK4(F,t,z,h)

    for n in range(N):
        Y.append(rk4.subs({t:X[n],z:Y[n]}) )
    return X,Y


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

"""
Problem Statement
------------------------------------------------
Q) Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
    If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers.
    For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284.
                 the proper divisors of 284 are 1, 2, 4, 71 and 142 ______________________________ so d(284) = 220.
    Evaluate the sum of all the amicable numbers under 10000.
"""
from utils import timing_decorator, find_divisors
import numpy as np

@timing_decorator
def sum_amicablepair(N=10000):
    amicable_nos = []
    for i in range(N):
        divisor_sum1 = sum([k for k in list(find_divisors(i)) if k!= i])
        divisor_sum2 = sum([k for k in list(find_divisors(divisor_sum1)) if k!= divisor_sum1])
        if i == divisor_sum2 and divisor_sum1 != divisor_sum2:
            print(i, divisor_sum1, divisor_sum2)
            amicable_nos.append(i)
    print(f'Sum of the all Amicable Pair below {N} = ', sum(amicable_nos))

sum_amicablepair()


import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns

from scipy.stats import poisson

# For reproducibility
np.random.seed(1000)

if __name__ == '__main__':
    # Create the initial observation set
    obs = np.array([7, 11, 9, 9, 8, 11, 9, 9, 8, 7, 11, 8, 9, 9, 11, 7, 10, 9, 10, 9, 7, 8, 9, 10, 13])
    mu = np.mean(obs)

    print('mu = {}'.format(mu))

    # Show the distribution
    sns.set(style="white", palette="muted", color_codes=True)
    fig, ax = plt.subplots(figsize=(14, 7), frameon=False)

    sns.distplot(obs, kde=True, color="b", ax=ax)
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    plt.show()

    # Print some probabilities
    print('P(more than 8 trains) = {}'.format(poisson.sf(8, mu)))
    print('P(more than 9 trains) = {}'.format(poisson.sf(9, mu)))
    print('P(more than 10 trains) = {}'.format(poisson.sf(10, mu)))
    print('P(more than 11 trains) = {}'.format(poisson.sf(11, mu)))

    # Add new observations
    new_obs = np.array([13, 14, 11, 10, 11, 13, 13, 9, 11, 14, 12, 11, 12,
                        14, 8, 13, 10, 14, 12, 13, 10, 9, 14, 13, 11, 14, 13, 14])

    obs = np.concatenate([obs, new_obs])
    mu = np.mean(obs)

    print('mu = {}'.format(mu))

    # Repeat the analysis of the same probabilities
    print('P(more than 8 trains) = {}'.format(poisson.sf(8, mu)))
    print('P(more than 9 trains) = {}'.format(poisson.sf(9, mu)))
    print('P(more than 10 trains) = {}'.format(poisson.sf(10, mu)))
    print('P(more than 11 trains) = {}'.format(poisson.sf(11, mu)))

    # Generate 2000 samples from the Poisson process
    syn = poisson.rvs(mu, size=2000)

    # Plot the complete distribution
    fig, ax = plt.subplots(figsize=(14, 7), frameon=False)

    sns.distplot(syn, kde=True, color="b", ax=ax)
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    plt.show()


import numpy as np
from scipy.special import loggamma
import warnings

class UnivariateGaussianVB(object):

    def __init__(self, prior_a=1.0, prior_b=1.0, prior_mu = 1.0,
        kappa=1.0, tol=1e-5, max_iter=20):
        """ use factored prior: p(mu,lambda) = p(mu|lambda)p(lambda)
            Gamma prior distribition for precision:
            p(lambda) = Gamma(lambda|prior_a,prior_b)
            Gaussian prior distribution for mean:
            p(mu|ldambda) = Normal(mu|prior_mu,(kappa*lambda)^-1)
        """
        self.prior_a = prior_a
        self.prior_b = prior_b
        self.prior_mu = prior_mu
        self.kappa = kappa

        self.post_a = prior_a
        self.post_b = prior_b
        self.post_mu = prior_mu
        self.post_mu_precision = kappa

        self.tol = tol
        self.max_iter = max_iter

        self._check_parameters()

    def _check_parameters(self):
        if self.prior_a <= 0 or self.prior_b <= 0:
            raise ValueError("prior_a and prior_be must be a positive number.")
        if self.kappa < 0:
            raise ValueError("kappa must be a non-negative number.")

    def _check_X(self,X):
        X = np.squeeze(X)
        if len(X.shape) != 1:
            raise ValueError("X should be 1-d array.")
        return X

    def _compute_lower_bound(self):
        return (-1/2*np.log(self.post_mu_precision) + loggamma(
            self.post_a)-self.post_a*np.log(self.post_b))

    def fit(self, X):
        X = self._check_X(X)
        N = X.size
        xbar = np.mean(X)
        x_square = np.sum(X**2)
        x_sum = np.sum(X)

        self.lower_bound_ = -np.infty
        self.converged_ = False
        lower_bounds = []

        for iter in range(1,self.max_iter+1):
            prev_lower_bound = self.lower_bound_

            # update q(mu)
            E_lambda = self.post_a / self.post_b
            self.post_mu = ((self.kappa * self.prior_mu +  N*xbar)
                / (self.kappa + N))
            self.post_mu_precision = (self.kappa + N) * E_lambda

            # update q(lambda)
            E_mu = self.post_mu
            E_mu_square = 1 / self.post_mu_precision + self.post_mu ** 2
            self.post_a = self.prior_a + (N+1)/2
            self.post_b = self.prior_b
            self.post_b += 1/2*(x_square + N*E_mu_square - 2*E_mu*x_sum)
            self.post_b += self.kappa * (E_mu_square + self.prior_mu**2 -
                2*E_mu*self.prior_mu)

            self.lower_bound_ = self._compute_lower_bound()
            lower_bounds.append(self.lower_bound_)

            change = self.lower_bound_ - prev_lower_bound
            if abs(change) < self.tol:
                self.converged_ = True
                break

        self.best_iter_ = iter
        self.lower_bounds_ = np.array(lower_bounds)

        if not self.converged_:
            warnings.warn("Model not converged.")


import numpy as np

# for linear regression the formula will be for forward
# f = w * X

# tranning data
x = np.array([1,2,3,4],dtype=np.float32)
# eg: f = 2 * x So,since our formula is 2X so ...
y = np.array([2,4,6,8],dtype=np.float32)
w = 0.0

# model prediction
def forward(x):
    return w*x

# loss = mean
def loss(y,y_predicted):
    return ((y_predicted-y)**2).mean()

# gradient manual
# mean = 1/n*(w*x-y)**2
# dj/dw = 1/n*(2*x(w*x-y))
def gradient(x,y,y_predicted):
    return np.dot(2*x,y_predicted-y).mean()

print(f"Prediction before tranning: f(5) = {forward(5)}")

# tranning
learning_rate = 0.01
no_iteration = 20

for epoch in range(no_iteration):
    # prediction = forword pass
    y_pred = forward(x)
    # loss
    ls = loss(y,y_pred)
    # gradients
    dw = gradient(x,y,y_pred)
    # update weights
    w -= learning_rate*dw

    print(f"epoch :{epoch+1} ,weights :{w} ,loss :{ls}")

print(f"Prediction after tranning: f(5) = {forward(5)}")


import matplotlib.pyplot as plt
import numpy as np
import imageio
from _utils import *

def DFT2D(x, shift=True):
    '''
    Discrete space fourier transform
    x: Input matrix
    '''
    pi2 = 2*np.pi
    N1, N2 = x.shape
    X = np.zeros((N1, N2), dtype=np.complex64)
    n1, n2 = np.mgrid[0:N1, 0:N2]

    for w1 in range(N1):
        for w2 in range(N2):
            j2pi = np.zeros((N1, N2), dtype=np.complex64)
            j2pi.imag = pi2*(w1*n1/N1 + w2*n2/N2)
            X[w1, w2] = np.sum(x*np.exp(-j2pi))
    if shift:
        X = np.roll(X, N1//2, axis=0)
        X = np.roll(X, N2//2, axis=1)
    return X

def iDFT2D(X, shift=True):
    '''
    Inverse discrete space fourier transform
    X: Complex matrix
    '''
    pi2 = 2*np.pi
    N1, N2 = X.shape
    x = np.zeros((N1, N2))
    k1, k2 = np.mgrid[0:N1, 0:N2]
    if shift:
        X = np.roll(X, -N1//2, axis=0)
        X = np.roll(X, -N2//2, axis=1)
    for n1 in range(N1):
        for n2 in range(N2):
            j2pi = np.zeros((N1, N2), dtype=np.complex64)
            j2pi.imag = pi2*(n1*k1/N1 + n2*k2/N2)
            x[n1, n2] = abs(np.sum(X*np.exp(j2pi)))
    return 1/(N1*N2)*x

if __name__ == "__main__":
    image = imageio.imread('./sample/cameraman.png')
    s = 4
    image = image[::s, ::s] / 255
    N1, N2 = image.shape
    IMAGE = DFT2D(image)
    xX = np.array([image, np.log10(1 + abs(IMAGE))])
    panel(xX, [2, 1], text_color='green',
          texts=['Input image', 'Spectrum'])

    image_ = iDFT2D(IMAGE)
    Xx_ = np.array([np.log10(1 + abs(IMAGE)), image_])
    panel(Xx_, [2, 1], text_color='green',
          texts=['Spectrum', 'Reconstructed image'])


import numpy as np
from matplotlib import pyplot as plt

x0 = 0
y0 = 2
xf = 1
n = 11
deltax = (xf-x0)/(n-1)
x = np.linspace(x0,xf,n)
def f(x,y):
	return y-x

y = np.zeros([n])
y[0] = y0
py = np.zeros([n])
py[0] = None

for i in range(1,n):
	py[i] = deltax*f(x[i-1],y[i-1]) + y[i-1]
	y[i] = deltax/2*( f(x[i],py[i]) + f(x[i-1],y[i-1]) ) + y[i-1]
print("x_n\t   py_n\t           y_n")
for i in range(n):
	print (x[i],"\t",format(py[i],'6f'),"\t",format(y[i],'6f'))

plt.plot(x,y,'o')
plt.xlabel("Value of x")
plt.ylabel("Value of y")
plt.title("Approximation Solution with Modified Euler's Method")
plt.show()

#table 19-1


import math
# import numpy as np

def primes_under(N):
    if N == 1:
        return []
    if N == 2:
        return [2]
    if N == 3:
        return [2,3]

    sqrtN = int(round(math.sqrt(N)))
    primes = [2,3]
    for j in range(3, N, 2):
        founddivisor = False
        for p in primes:
            if p > sqrtN:
                break
            elif j % p == 0:
                founddivisor = True
                break
        if not founddivisor:
            primes.append(j)

    return primes

def is_prime(N):
    if N < 0 or type(N) != int:
        return False
    if N == 0 or N == 1:
        return False
    if N == 2 or N == 3:
        return True

    sqrtN = int(round(math.sqrt(N)))+1
    for j in range(2,sqrtN):
        # print(j)
        if N % j == 0:
            return False
    return True

def test_primes_under():
    primes_10 = primes_under(10)
    primes_100 = primes_under(100)
    primes_1000 = primes_under(1000)
    # print(primes_10)
    u = [is_prime(x) for x in primes_10]
    print(u)
    print([(x, is_prime(x)) for x in range(100)])
    is_prime(10)
    # print(primes_100)
    # print(primes_1000)

if __name__ == "__main__":
    test_primes_under()


'''
Lorenz system.

A simplified mathematical model for atmospheric convection. The model is a system of three ordinary differential
equations now known as the Lorenz equations:

dx/dt = sigma * (y - x)
dy/dt = x * (rho - z) - y
dz/dt = xy - beta * z

The equations relate the properties of a two-dimensional fluid layer uniformly warmed from below and cooled from above.
In particular, the equations describe the rate of change of three quantities with respect to time: x is proportional to
the rate of convection, y to the horizontal temperature variation, and z to the vertical temperature variation. The
constants sigma, rho, and beta are system parameters proportional to the Prandtl number, Rayleigh number, and certain
physical dimensions of the layer itself.

From a technical standpoint, the Lorenz system is nonlinear, non-periodic, three-dimensional and deterministic.

SRC: https://en.wikipedia.org/wiki/Lorenz_system

------------------------------------------------------------------------------------------------------------------------

A chaotic map is a map (= evolution function) that exhibits some sort of chaotic behavior.

List of chaotic maps (https://en.wikipedia.org/wiki/List_of_chaotic_maps)
    Map                     Time dom    Space dom  Space dim  Params
    ----------------------------------------------------------------
    Logistic map            discrete    real               1       1
    Lotka-Volterra system   continuous  real               3       4
    Lorenz system           continuous  real               3       3

------------------------------------------------------------------------------------------------------------------------

https://physics.nyu.edu/pine/pymanual/html/chap9/chap9_scipy.html
http://sam-dolan.staff.shef.ac.uk/mas212/notebooks/ODE_Example.html
'''

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

# Declarations and definitions:
rho = 28.0
sigma = 10.0
beta = 8.0 / 3.0

def f(state, t):
    x, y, z = state  # unpack the state vector
    return (sigma * (y - x), x * (rho - z) - y, x * y - beta * z)  # derivatives

state0 = [1.0, 1.0, 1.0]  # the initial state
# t = np.arange(0.0, 40.0, 0.01)

# Loopless:
# t = np.arange(0.0, 1.0, 0.1)
# states = odeint(f, state0, t)
# print(states)

# Loop (compatible with a PRAM simulation):
# states = []
# s = state0
# t0 = 0.0
# for t1 in t[1:]:
#     s = odeint(f, s, [t0,t1])[0]
#     t0 = t1
#     states.append(s)
#
# print(states[:10])

t0 = 0.0
t1 = 1.0
dt = 0.1

r = ode(f).set_integrator('zvode', method='bdf')
# r = ode(f).set_integrator('lsoda')
r.set_initial_value(state0, t0)
while r.successful() and r.t < t1:
    r.integrate(r.t + dt)
    print(f'{round(r.t,1)}: {r.y}')

# Visualize:
# fig = plt.figure()
# ax = fig.gca(projection='3d')
# ax.plot(states[:,0], states[:,1], states[:,2])
# plt.show()


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

def main():
    versi_color_petal = [4.7, 4.5, 4.9, 4.0,  4.6, 4.5, 4.7, 3.3, 4.6, 3.9, 3.5, 4.2, 4.0,  4.7, 3.6, 4.4, 4.5, 4.1,
     4.5, 3.9, 4.8, 4.0,  4.9, 4.7, 4.3, 4.4, 4.8, 5.0,  4.5, 3.5, 3.8, 3.7, 3.9, 5.1, 4.5, 4.5,
     4.7, 4.4, 4.1, 4.0,  4.4, 4.6, 4.0,  3.3, 4.2, 4.2, 4.2, 4.3, 3.0,  4.1]

    x_vers, y_vers = ecdf(versi_color_petal)
    plt.plot(x_vers,y_vers, marker='.', linestyle = 'none')
    # can multiple ecdf on the same plot and compare

    plt.xlabel('petal length')
    plt.ylabel('ECDF')

    plt.show()

main()


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

" This pyfile is used to give a numerical solution of ordinary differential equation "

import numpy as np
import matplotlib.pyplot as plt

""" Input:
Interval: [a,b]
Step: h
Differential: f(x,y)
Initial point: y(a) = y0
"""

def EulerMethod(a,b,h,f,y0):
    if b>a:
        step = int((b-a)/h+1)
        x = np.linspace(a,b,step)
        y = np.zeros_like(x)
        y[0]=y0
        for index in range(1,step):
            y[index] = y[index-1] + h*f(x[index-1],y[index-1])
        return y
    else:
        return "Error"

def I_EulerMethod(a,b,h,f,y0):
    if b>a:
        step = int((b-a)/h+1)
        x = np.linspace(a,b,step)
        y = np.zeros_like(x)
        ye = np.zeros_like(x)
        y[0] = y0
        ye[0] = y0
        for index in range(1,step):
            ye[index] = ye[index-1] + h*f(x[index-1],ye[index-1])
            y[index] = y[index-1] + 0.5*h*(f(x[index-1],y[index-1])+f(x[index],ye[index]))
        return y
    else:
        return "Error"

def Draw(a,b,h,f,y0,ft):
    step = int((b-a)/h+1)
    x = np.linspace(a,b,step)
    fig, ax = plt.subplots(dpi=120)
    ye = EulerMethod(a,b,h,f,y0)
    yie = I_EulerMethod(a,b,h,f,y0)
    yt = ft(x)
    ax.plot(x,ye,marker='o',color='r',alpha=0.5,label='Euler Method')
    ax.plot(x,yie,marker='x',color='b',alpha=0.5,label='Improved Euler Method')
    ax.plot(x,yt,marker='v',color='g',alpha=0.5,label='Explicit Function')
    ax.legend()
    fig.show()
    return 0

"""
a = 0
b = 1
h = 0.1
f = lambda x,y: y-2*x/y
y0 = 1
ft = lambda x: np.sqrt(1+2*x)

EulerMethod(a,b,h,f,y0)
I_EulerMethod(a,b,h,f,y0)
Draw(a,b,h,f,y0,ft)
"""