#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Ejemplo: Ecuacion de Poisson 1D pero resuelto con FEniCS

  -d2u/dx2 = 1   0 < x < 1
    u(0) = 0 y u(1) = 0 en los bordes.

"""

from __future__ import print_function
from fenics import *
# Defino la malla
nx = 5 #numero de intervalos
minx, maxx = 0.0, 1.0
mesh = IntervalMesh(nx, minx, maxx)#malla en 1D
V = FunctionSpace(mesh, 'P',1)#Lagrange Finite Element

# Defino las condiciones de borde
def borde_D(x, on_boundary): #retorna un boolean
    tol = 1.E-14
    return on_boundary and near(x[0], 1., tol)

def borde_I(x, on_boundary):
    tol = 1.E-14
    return on_boundary and near(x[0], 0., tol)

bc_der = DirichletBC(V, Constant(0.0), borde_D)
bc_iz = DirichletBC(V, Constant(0.0), borde_I)

bc = [bc_iz, bc_der]

# Comienzo la formulacion variacional
u = TrialFunction(V)
v = TestFunction(V)
f = Constant(1.0)
#Definicion abstracta
a = dot(grad(u), grad(v))*dx #o inner
L = f*v*dx

# Resuelvo
u = Function(V)
solve(a == L, u, bc)

print('Tipo de variable:',type(u))

import matplotlib.pyplot as plt

#Extraigo los datos de la solucion u.
uh = u.compute_vertex_values(mesh)

print('Cantidad de celdas:',nx)
print('Cantidad de vertices:',len(uh))

fig, axs = plt.subplots(1,1)

import numpy as np

xu = np.linspace(0.0, 1.0, len(uh),endpoint = True)

axs.plot(xu,uh,'ro',markersize=10)

#Comparo con solucion exacta
xe = np.arange(0.0,1.0,0.001)
ue = -0.5*xe*(xe-1.)
axs.plot(xe,ue,'b')

##Tambien se puede calcular en los mismos puntos que uh (para calcular errores)

class Resultado(UserExpression):
    def eval(self, values, x):
        values[0] = -0.5*x[0]*(x[0]-1.0)

u_D = Resultado(degree=1)
u_De = u_D.compute_vertex_values(mesh)

##error_max = np.max(np.abs(u_De - uh))

# Calcula el error en la norma L2
error_L2 = errornorm(u_D, u, 'L2')

##print('Error maximo:',error_max)
print('Error en L2:',error_L2)

axs.plot(xu,u_De,'.b',markersize=10)
plt.title('Soluciones comparadas')

plt.show()


import numpy as np
import pascal

def jacobi_iter(x_0, epsilon, max_iter):  # 2a
    A = np.matrix([[1.0, 1.0/2, 1.0/3], [1.0/2, 1.0, 1.0/4], [1.0/3, 1.0/4, 1.0]])
    b = np.array([.1, .1, .1]).reshape(3, 1)
    iteration = 0
    error = 1000
    x_n = x_0
    while iteration < max_iter:
        d = np.diag(1 / np.diag(A))
        alu = A - np.diag(np.diag(A))
        x_1 = pascal.mult(d, b) + pascal.mult(pascal.mult(d, -alu), x_n)
        error = pascal.norm_inf(x_1 - x_n)
        x_n = x_1
        if epsilon > error:
            return x_n, iteration
        iteration += 1
    else:
        return None, None

def gs_iter(x_0, epsilon, max_iter):  # 2a
    A = np.matrix([[1.0, 1.0/2, 1.0/3], [1.0/2, 1.0, 1.0/4], [1.0/3, 1.0/4, 1.0]])
    B = np.array([.1, .1, .1]).reshape(3, 1)
    S_inv = np.zeros((3, 3))
    a, b, c, d, e, f = A[0, 0], A[1, 0], A[1, 1], A[2, 0], A[2, 1], A[2, 2]
    S_inv[0, 0] = 1.0/a
    S_inv[1, 0] = -b/(a*c)
    S_inv[1, 1] = 1.0/c
    S_inv[2, 0] = (-c*d+b*e)/(a*c*f)
    S_inv[2, 1] = -e/(c*f)
    S_inv[2, 2] = 1.0/f

    S = np.tril(A)
    U = A - S

    iteration = 0
    error = 1000
    x_n = x_0
    while iteration < max_iter:
        x_1 = pascal.mult(pascal.mult(S_inv, -U), x_n)
        x_1 += pascal.mult(S_inv, B)
        error = pascal.norm_inf(x_1 - x_n)
        x_n = x_1
        if epsilon > error:
            return x_n, iteration
        iteration += 1
    else:
        return None, None

def rand_vec():
    return np.random.rand(3, 1) * 2 - 1

def generate_data():  # 2b
    jacobi_array = []
    gs_array = []

    for i in range(100):
        x0 = rand_vec()
        xn, iterations = jacobi_iter(x0, .00005, 100)
        jacobi_array.append((x0, xn, iterations))

    for i in range(100):
        x0 = rand_vec()
        xn, iterations = gs_iter(x0, .00005, 100)
        gs_array.append((x0, xn, iterations))

    return jacobi_array, gs_array


# Matrices and Matrix Operations
#----------------------------------
#
# This function introduces various ways to create
# matrices and how to use them in Tensorflow

import numpy as np
import tensorflow as tf
from tensorflow.python.framework import ops
ops.reset_default_graph()

# Declaring matrices
sess = tf.Session()

# Declaring matrices

# Identity matrix
identity_matrix = tf.diag([1.0,1.0,1.0])
print('identity_matrix:')
print(sess.run(identity_matrix))

# 2x3 random norm matrix
A = tf.truncated_normal([2,3])
print 'A:'
print(sess.run(A))

# 2x3 constant matrix
B = tf.fill([2,3], 5.0)
print 'B:'
print(sess.run(B))

# 3x2 random uniform matrix
C = tf.random_uniform([3,2])
print 'C:'
print(sess.run(C))
print 'Rerun C:'
print(sess.run(C)) # Note that we are reinitializing, hence the new random variabels

# Create matrix from np array
D = tf.convert_to_tensor(np.array([[1., 2., 3.], [-3., -7., -1.], [0., 5., -2.]]))
print 'D:'
print(sess.run(D))

# Matrix addition/subtraction
print 'A+B:'
print(sess.run(A+B))
print 'A-B:'
print(sess.run(B-B))

# Matrix Multiplication
print(sess.run(tf.matmul(B, identity_matrix)))

# Matrix Transpose
print(sess.run(tf.transpose(C))) # Again, new random variables

# Matrix Determinant
print(sess.run(tf.matrix_determinant(D)))

# Matrix Inverse
print(sess.run(tf.matrix_inverse(D)))

# Cholesky Decomposition
print(sess.run(tf.cholesky(identity_matrix)))

# Eigenvalues and Eigenvectors
print 'self_adjoint_eig:'
print(sess.run(tf.self_adjoint_eig(D)))


import numpy as np

def properties(A):
    (n, m) = A.shape

    dimensins = A.ndim
    symmetric = n == m and np.all(A == A.T)
    skew_symmetric = n == m and np.all(A == -A.T)
    invertable = n == m and np.linalg.det(A) != 0
    orthogonal = np.allclose(A.dot(A.T), np.eye(np.min(A.shape)), atol=1e-6)
    return f"dimensions: {dimensins}\nsymmetry: {symmetric}\nskew symmetry: {skew_symmetric}\ninveratibility: {invertable}\northogonal: {orthogonal}"

# A = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
# A = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 0]]) # skew symmetric matrix
# A = np.array([[1, 2, 3], [2, 1, 0], [3, 0, 1]]) # symmetric matrix
A = np.array([[3, 4], [-4, 3]]) * 1/5 # orthogonal matrix
print(properties(A))


import numpy as np
import matplotlib.pyplot as plt
import time
import sys

def darts(N):
    x = np.random.rand(N)
    y = np.random.rand(N)
    return x,y

def estimate_pi(N):
    x,y = darts(N)
    r = np.sqrt((x - 0.5)**2 + (y - 0.5)**2)
    r_bool = (r <= 0.5)
    pi_estimate = (np.sum(r_bool) / N) * 4
    return pi_estimate

def plot_circle(xcenter,ycenter,radius):
    theta = np.linspace(0, 2 * np.pi, 1000)
    xpoints = radius* np.cos(theta) + xcenter
    ypoints = radius * np.sin(theta) + ycenter
    plt.plot(xpoints,ypoints, 'k')

def timer(function, argument):
    start_time = time.time()
    function(argument)
    end_time = time.time()
    return end_time - start_time

def plot_darts(N):
    x,y = darts(N)
    plt.figure()
    r = np.sqrt((x - 0.5) ** 2 + (y - 0.5) ** 2)
    plt.scatter(x[r < 0.5], y[r < 0.5], s=5, c='r')
    plt.scatter(x[r > 0.5], y[r > 0.5], s=5, c='b')
    plot_circle(0.5, 0.5, 0.5)
    plt.show()

# Plots darts for N = 1000
plot_darts(10000)

# Times and calculates values of pi for several values of N.
N = 100000 * np.arange(1,11)
times = []
estimate_precision = []

for n in N:
    times.append(timer(estimate_pi, n))
    estimate_precision.append((abs(np.pi - estimate_pi(n)) / np.pi) *100)

# Plot of Execution Time
plt.figure()
plt.scatter(N,times)
plt.xlabel("Number of Darts")
plt.ylabel("Execution Time(s)")
plt.show()

# Plot of Percent Error
plt.figure()
plt.scatter(N,estimate_precision)
plt.xlabel("Number of Darts")
plt.ylabel("Percent Error")
plt.show()

# Histogram of estimates and some statistics
N = 1000

estimates = []
repeats = 100

for i in range(100):
    estimates.append(estimate_pi(N))

plt.figure()
plt.hist(estimates)
print("Mean: " + str(np.mean(estimates)))
print("Standard Deviation: " + str(np.std(estimates)))
plt.show()


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

def func(exp):
	"""
	Function to convert the expression to the Pythonic format to make mathematical calculations.

	Parameters:
	exp:	input expression by the user to be lambdified
	"""

	x = sp.symbols('x')
	return sp.utilities.lambdify(x, exp, "math")

def diffy(exp):
	"""
	Function to find the differential of an expression.

	Parameters:
	exp:	input expression whose differential is calculated
	"""

	x = sp.symbols('x')
	return sp.diff(exp, x)

def newton_raphson(expr, guess, tol, roots):
	"""
	Function to find the root of an expression using Newton-Raphson method.

	Parameters:
	expr:		input expression for which the root is calculated
	guess:		initial guess as input by the user
	tol:		maximum permissible error between the calculated root and the true root
	roots:		an array to store the calculated roots through the iterations to be plotted later
	"""

	differ = diffy(expr)
	diff_math = func(differ)
	function = func(expr)

	x_new = guess - function(guess)/diff_math(guess)
	roots.append(x_new)

	if abs(x_new - guess) < tol:

		return roots

	else:

		return newton_raphson(expr, x_new, tol, roots)

def plot_func(expr, roots, guess):
	"""
	Function to plot the expression, the initial guess and all the calculated roots while highlighting the final correct root.

	Parameters:
	expr:		the expression to be plotted
	roots:		array of roots calculated by Newton-Raphson method
	guess:		initial guess input by the user
	"""

	function = func(expr)

	# Plotting the function by creating an array of Xs and Ys
	x = np.linspace(np.floor(roots[-1]-5), np.ceil(roots[-1]+5), 50)
	y = []

	for i in x:
		y.append(function(i))

	# An array of zeros the same length as the number of roots in arrays, so as to plot the roots
	roots_y = np.zeros(len(roots))

	fig = plt.figure(figsize=(8,7))

	ax1 = fig.add_axes([0.05, 0.05, 0.9, 0.9])

	ax1.plot(x, y, label="Function: %s"% expr)
	ax1.axhline(0, color='red', ls='--', alpha=0.5)
	ax1.scatter(roots[:-1], roots_y[:-1], color='black', s=10, alpha=0.8, edgecolor='black', label="Roots")
	ax1.scatter(guess, 0, color='red', s=15, label="Initial Guess: %s"% str(guess))
	ax1.scatter(roots[-1], 0, color="green", s=15, label="Final Root: %s"% str(round(roots[-1], 3)))

	ax1.set_title("Finding Roots: Newton Raphson Method")
	ax1.legend()

	plt.show()

# Sample input:

# expr = "x - tan(x)"
# init_guess = 4.6
# error = 0.0001

points = []

expr = input("Enter a continuous function in x: ")
init_guess = float(input("Enter an initial guess for the root: "))
error = float(input("Enter the maximum permissible error of the root: "))

answers = newton_raphson(expr, init_guess, error, points)

plot_func(expr, answers, init_guess)

print(f"The root of {expr} by Newton-Raphson method: {round(answers[-1], 3)}")

Day 3 - Newton Raphson/newton_raphson.py


import numpy as np
import sys
import json

def column_convertor(x):
    """
    Converts 1d array to column vector
    """
    x.shape = (1, x.shape[0])
    return x

def get_norm(x):
    """
    Returns Norm of vector x
    """
    return np.sqrt(np.sum(np.square(x)))

def hh_reflection(v):
    """
    Returns Householder matrix for vector v
    """
    size_of_v = v.shape[1]
    e1 = np.zeros_like(v)
    e1[0, 0] = 1
    vector = get_norm(v) * e1
    if v[0, 0] < 0:
        vector = - vector
    u = (v + vector).astype(np.float32)
    H = np.identity(size_of_v) - ((2 * np.matmul(np.transpose(u), u)
                                   ) / np.matmul(u, np.transpose(u)))
    return H

def qr_decomposition(q, r, iter, n):
    """
    Return Q and R matrices for iter number of iterations.
    """
    v = column_convertor(r[iter:, iter])
    Hbar = hh_reflection(v)
    H = np.identity(n)
    H[iter:, iter:] = Hbar
    r = np.matmul(H, r)
    q = np.matmul(q, H)
    return q, r

# HANDLING THE EXECUTION

n = int(sys.argv[1])
m = int(sys.argv[2])

A = np.random.rand(n, m)
Q = np.identity(n)
R = A.astype(np.float32)
for i in range(min(n, m)):
    # For each i, H matrix is calculated for (i+1)th row
    Q, R = qr_decomposition(Q, R, i, n)
min_dim = min(m, n)
R = np.around(R, decimals=6)
R = R[:min_dim, :min_dim]
Q = np.around(Q, decimals=6)

result = {
    "n": n,
    "m": m,
    "R": R.tolist(),
    "Q": Q.tolist(),
    "A": A.tolist()
}
print(json.dumps(result))
sys.stdout.flush()


# encoding: utf-8
# Powernoise.py
# Adapted by Henrique Castilho 2019-04-14
# From the original:
########################################################
#       Arbitrary Spectral Slope Noise Generation      #
#               with MATLAB Implementation             #
#                                                      #
# From Little, 1992. Version by R. Rosa   08/08/14     #
########################################################

import numpy as np
import math

def powernoise(beta, N, *varargin):
# Generate samples of power law noise. The power spectrum
# of the signal scales as f^(-beta).
#
# Usage:
#  x = powernoise(beta, N)
#  x = powernoise(beta, N, 'option1', 'option2', ...)
#
# Inputs:
#  beta  - power law scaling exponent
# For instance:
# white noise   -> beta = 0;
# pink noise    -> beta = -1;
# red noise     -> beta = -2;
#
#  N     - number of samples to generate
#
# Output:
#  x     - N x 1 vector of power law samples
#
# With no option strings specified, the power spectrum is
# deterministic, and the phases are uniformly distributed in the range
# -pi to +pi. The power law extends all the way down to 0Hz (DC)
# component. By specifying the 'randpower' option string however, the
# power spectrum will be stochastic with Chi-square distribution. The
# 'normalize' option string forces scaling of the output to the range
# [-1, 1], consequently the power law will not necessarily extend
# right down to 0Hz.
#
# (cc) Max Little, 2008. This software is licensed under the
# Attribution-Share Alike 2.5 Generic Creative Commons license:
# http://creativecommons.org/licenses/by-sa/2.5/
# If you use this work, please cite:
# Little MA et al. (2007), "Exploiting nonlinear recurrence and fractal
# scaling properties for voice disorder detection", Biomed Eng Online, 6:23
#
# As of 20080323 markup
# If you use this work, consider saying hi on comp.dsp
# Dale B. Dalrymple

    opt_randpow = False
    opt_normal = False

    for arg in varargin:
        if arg == 'normalize':
            opt_normal = True
        if arg == 'randpower':
            opt_randpow = True

    N2 = int(N / 2) - 1
    f = np.arange(2, (N2 + 1) + 1, 1)
    A2 = 1.0 / (f ** (beta / 2.0))

    if not opt_randpow:
        p2 = (np.random.uniform(0, 1, N2) - 0.5) * 2 * math.pi
        d2 = A2 * np.exp(1j * p2)
    else:
        # 20080323
        p2 = np.random.rand(N2) + 1j * np.random.rand(N2)
        d2 = A2 * p2

    d = np.concatenate(([1], d2, [1.0/((N2 + 2.0) ** beta)], np.flipud(np.conjugate(d2))))
    x = np.real(np.fft.ifft(d))

    if opt_normal:
        x = ((x - min(x)) / (max(x) - min(x)) - 0.5) * 2

    return x


import numpy as np

def euclidean_distance(x, y):
    """
    Parameters:
    ----------
    x: m-D array-like
        a cluster points, e.g. array([[x1,y1],...,[xn,yn]])
    y: 1-D array-like
        one point, e.g. array([x1,y1]).
    Returns:
    ----------
    dist: 1-D array-like
        the euclidean distance from each one in x to y,
        e.g. array([d1,...,dn])
    """
    return minkowski_distance(x, y)

def minkowski_distance(x, y, p=2):
    """
    Parameters:
    ----------
    x: m-D array-like
        a cluster points, e.g. array([[x1,y1],...,[xn,yn]])
    y: 1-D array-like
        one point, e.g. array([x1,y1]).
    p: int or np.inf
        subscrpit of the L-p norm, p >= 1.
    Returns:
    ----------
    dist: 1-D array-like
        the minkowski distance from each one in x to y,
        e.g. array([d1,...,dn])
    """
    if p < 1:
        raise Exception("p is too less than 1!")
    if p < np.inf:
        return (np.sum(np.abs(x - y) ** p, -1)) ** (1/p)
    return np.max(np.abs(x - y), -1)

def manhattan_distance(x, y):
    """
    Parameters:
    ----------
    x: m-D array-like
        a cluster points, e.g. array([[x1,y1],...,[xn,yn]])
    y: 1-D array-like
        one point, e.g. array([x1,y1]).
    Returns:
    ----------
    dist: 1-D array-like
        the manhattan distance from each one in x to y,
        e.g. array([d1,...,dn])
    """
    return minkowski_distance(x, y, p=1)

def chebyshev_distance(x, y):
    """
    Parameters:
    ----------
    x: m-D array-like
        a cluster points, e.g. array([[x1,y1],...,[xn,yn]])
    y: 1-D array-like
        one point, e.g. array([x1,y1]).
    Returns:
    ----------
    dist: 1-D array-like
        the chebyshev distance from each one in x to y,
        e.g. array([d1,...,dn])
    """
    return minkowski_distance(x, y, p=np.inf)

if __name__ == "__main__":
    x = np.array([
        [5, 2],
        [2, 2],
        [6, 1]
    ])
    y = np.array([1, 1])
    print(euclidean_distance(x, y))
    print(minkowski_distance(x, y, 2))
    print(manhattan_distance(x, y))
    print(minkowski_distance(x, y, 1))
    print(chebyshev_distance(x, y))
    print(minkowski_distance(x, y, np.inf))


import numpy as np

# part (a)

# from lecture notes (week 6)
# G = g(h1)+E(h1) = g(h1)+ch1^p
# G = g(h2)+E(h2) = g(h2)+ch2^p

# >> Eliminate c and solve for G

# G = [ (h1/h2)^p * g(h2) - g(h1) ] / [ (h1/h2)^p - 1 ]

# >> Assume h2 = h1/2

# G = [ 2^p * g(h1/2) - g(h1) ] / [ 2^p - 1 ]

# >> Use Taylor expansion

# f(x+h) = f(x) + hf'(x) + (h^2/2!)f''(x) + (h^3/3!)f'''(x) + (h^4/4!)fiv(x) + (h^5/5!)fv(x) + ...

# f(x-h) = f(x) - hf'(x) + (h^2/2!)f''(x) - (h^3/3!)f'''(x) + (h^4/4!)fiv(x) - (h^5/5!)fv(x) + ...

# >> Sum an d subtract the two expansions

# f(x+h)+f(x-h) = 2f(x) + (h^2)f''(x) + (h^4/12)fiv(x)

# f(x+h)-f(x-h) = 2hf'(x) + (h^3/3)f'''(x) + (h^5/60)fv(x)

# >> Rearrange for f'(x). The higher orders can be considered errors and not part of the actual solution.
# Rearranging is only possible using f(x+h)-f(x-h) because f'(x) is cancelled out in f(x+h)+f(x-h)

# f(x+h)-f(x-h) - (h^3/3)f'''(x) - (h^5/60)fv(x) = 2hf'(x)

# [ f(x+h)-f(x-h) ] / 2h = centered approximation

# [ (h^3/3)f'''(x) - (h^5/60)fv(x) ] / 2h = error associated with centered approximation

# Refer to equation G(h)=g(h)+E(h) where E(h) is the error

# [ f(x+h)-f(x-h) ] / 2h = g(h)

# [ f(x+{h/2})-f(x-{h/2}) ] / h = g(h/2)

# Substitute g(h) and g(h/2) into our equation for G

# G = [ 2^p * ( [ f(x+{h1/2})-f(x-{h1/2}) ] / h ) - ([ f(x+h)-f(x-h) ] / 2h ]) ] / [ 2^p - 1 ]

# part (b)

x=1.0
h=0.5
p=2.0 # this is the order of the error

def f(x): # equation given in Q
    return x+(np.exp(x))

def dfdx(p, x, h): # central difference estimation of a differential
    return (p(x+h/2)-p(x-h/2))/h

g1 = dfdx(f, 1.0, 0.5) # plugging in constants given
g2 = dfdx(f, 1.0, 0.25)

# say h = h1 --> h2 = h1/2

G = (((2**p)*g2)-g1)/((2**p)-1)

# the error would be the difference between the derivative of f and the estimations g1 and G

def fi(x):
    return 1+(np.exp(x))

trueValue=fi(1)

err1=G-trueValue
err2=g1-trueValue

print "The Richardson extrapolation equals",round(G,3),"when x is equal to ",round(x,4)," and h is equal to",h,"with an error of",round(err1,8)
print "The central difference extrapolation equals",round(g1,3),"when x is equal to ",x," and h is equal to",h,"with an error of",round(err2,3)
print "The central difference and Richardson techniques give values that are within each others' error ranges, which is expected."

ExamPrep/Shit Comp/SciComp Exam 2016/Assignments 15-16/A2Q3 central difference error differentials richardson extrapolation.py


#！/usr/bin/env python
#  -*- coding:utf-8 -*-
#  author:dabai time:2019/1/9

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

def runge_kutta(y, x, dx, f):
    """ y is the initial value for y
        x is the initial value for x
        dx is the time step in x
        f is derivative of function y(t)
    """
    k1 = dx * f(y, x)
    k2 = dx * f(y + 0.5 * k1, x + 0.5 * dx)
    k3 = dx * f(y + 0.5 * k2, x + 0.5 * dx)
    k4 = dx * f(y + k3, x + dx)
    return y + (k1 + 2 * k2 + 2 * k3 + k4) / 6.

if __name__=='__main__':
    t = 0.
    y = 1.
    dt = .1
    ys, ts = [], []

def func(y, t):
    return t * math.sqrt(y)

while t <= 10:
    y = runge_kutta(y, t, dt, func)
    t += dt
    ys.append(y)
    ts.append(t)

# exact = [(t ** 2 + 4) ** 2 / 16. for t in ts]
#plt.plot(ts, ys, label='runge_kutta')
#plt.plot(ts, exact, label='exact')
#plt.legend()
#plt.show()
# error = np.array(exact) - np.array(ys)
# print("max error {:.5f}".format(max(error)))

from scipy.integrate import odeint

YS=odeint(func,y0=1, t=np.arange(0,10.1,0.1))

plt.plot(ts, ys, label='runge_kutta')
plt.plot(ts, YS, label='odeint')
plt.legend()
plt.show()


import numpy as np
import math
from functools import reduce

def raiser(omega, power):
   if power == 0:
       return (1, 0)
   omegas = [omega] * power
   return reduce(lambda a, b: (a[0] * b[0], a[1]+b[1]), omegas)

def multiplyer(omega, nz):
   return (omega[0]*nz[0], omega[1]+nz[1])

def add(pn1, pn2):
   c1 = to_complex(pn1)
   c2 = to_complex(pn2)
   z = (c1[0] + c2[0], c1[1] + c2[1])
   return to_polar(z)

def subtract(pn1, pn2):
   c1 = to_complex(pn1)
   c2 = to_complex(pn2)
   z = (c1[0] - c2[0], c1[1] - c2[1])
   return to_polar(z)

def to_polar(n):
   r = math.sqrt(n[0]**2 + n[1]**2)
   if n[0] == 0:
       if n[1] < 0:
           return (r, -math.pi/2)
       else:
           return (r, math.pi/2)
   theta = math.atan2(n[1], n[0])
   return (r, theta)

def to_complex(pn1):
   return (pn1[0]*math.cos(pn1[1]), pn1[0]*math.sin(pn1[1]))

# O(nlogn) time to get points from coefficients or the other way
def fft(coefficients, omega, n, rev=False):
   if omega is None:
       omega = (1, 2*math.pi/n)
       if rev :
           omega = raiser((1, 2*math.pi/n), n-1)
   if n == 1:
       return coefficients
   c_e = []
   c_o = []
   for i, c in enumerate(coefficients):
       if i % 2 == 0:
           c_e.append(c)
       else:
           c_o.append(c)
   odds = fft(c_o, raiser(omega, 2), n //2, rev)
   evens = fft(c_e, raiser(omega, 2), n //2, rev)
   ans = [1]*n
   for j in range(n//2):
       if j != 0:
           z = multiplyer(raiser(omega, j), odds[j])
       else:
           z = odds[j]
       ans[j] = add(evens[j], z)
       ans[n//2+j] = subtract(evens[j],  z)
   return ans

 # O(nlogn) time to multiply polynomials
def polynomial_multiplication(n1, n2):
   pts_1 = fft(n1, None, len(n1), False)
   pts_2 = fft(n2, None, len(n2), False)
   pts = []
   for i, p in enumerate(pts_1):
       pts.append((p[0] * pts_2[i][0], p[1] + pts_2[i][1]))

   res = fft(pts, None, len(pts), True)
   return res


import numpy as np

def cosine_distance(a, b):
    return 1 - np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))


# Create a version of fig 4.19 from "Hands-on ML with Scikit-Learn"
# by Aurelien Geron
# Based on his original code here:
# https://nbviewer.jupyter.org/github/ageron/handson-ml2/blob/master/04_training_linear_models.ipynb

#%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

# plot range for theta1 and theta2
t1a, t1b, t2a, t2b = -1, 3, -1.5, 1.5

# create a grid of parameter combinations
t1s = np.linspace(t1a, t1b, 500)
t2s = np.linspace(t2a, t2b, 500)
t1, t2 = np.meshgrid(t1s, t2s)
params = np.c_[t1.ravel(), t2.ravel()]

# create some data
np.random.seed(1)
N = 10
D = 2
X = np.random.randn(N, D)
y = np.c_[2 * X[:, 0] + 0.5 * X[:, 1]]

# compute MSE for all possible parameters
J = (1/N * np.sum((params.dot(X.T) - y.T)**2, axis=1)).reshape(t1.shape)

# Compute norm of parameter values
N1 = np.linalg.norm(params, ord=1, axis=1).reshape(t1.shape)
N2 = np.linalg.norm(params, ord=2, axis=1).reshape(t1.shape)

# Initial value for gradient descent
t_init = np.array([[0.25], [-1]])

# batch gradient descent
def bgd_path(theta, X, y, l1, l2, include_mse = 1, eta = 0.1, n_iterations = 50):
    path = [theta]
    for iteration in range(n_iterations):
        gradients = include_mse * 2/len(X) * X.T.dot(X.dot(theta) - y) + \
            l1 * np.sign(theta) + 2 * l2 * theta
        theta = theta - eta * gradients
        path.append(theta)
    return np.array(path)

def do_plot(l1=0, l2=0, ttl=""):
    JR = J + l1*N1 + l2*N2**2
    N = l1*N1 + l2*N2**2
    tr_min_idx = np.unravel_index(np.argmin(JR), JR.shape)
    t1r_min, t2r_min = t1[tr_min_idx], t2[tr_min_idx]

    levelsJR=(np.exp(np.linspace(0, 1, 20)) - 1) * (np.max(JR) - np.min(JR)) + np.min(JR)
    levelsN=np.linspace(0, np.max(N), 10)
    path_JR = bgd_path(t_init, X, y, l1=l1, l2=l2)

    plt.figure(figsize=(6, 4))
    plt.grid(True)
    plt.axhline(y=0, color='k')
    plt.axvline(x=0, color='k')
    plt.contourf(t1, t2, JR, levels=levelsJR, alpha=0.9)
    if l1>0 or l2>0:
        plt.contour(t1, t2, N, levels=levelsN)
    plt.plot(path_JR[:, 0], path_JR[:, 1], "w-o")
    plt.plot(t1r_min, t2r_min, "rs")
    plt.axis([t1a, t1b, t2a, t2b])
    plt.xlabel(r"$\theta_1$", fontsize=20)
    plt.ylabel(r"$\theta_2$", fontsize=20, rotation=0)
    plt.title(ttl, fontsize=16)

do_plot(l1=0, l2=0, ttl="OLS")
do_plot(l1=0.5, l2=0, ttl="lasso")
do_plot(l1=0, l2=0.1, ttl="ridge")


from __future__ import division
import numpy as np

import matplotlib.pyplot as plt

# Hypothesis

def Hypothesis(X,theta):
    a=np.dot(X,theta)
    sigmoid=1/(1+np.exp(-a))
    return sigmoid

# cost function
def costFunction(hypothesis,Y,num_trainning):

    cost_result = -(1/num_trainning)*(np.dot(Y.T,np.log(hypothesis))+np.dot((1-Y).T,np.log(1-hypothesis)))
    return cost_result

def gradientDescent(num_iterations,alpha,num_trainning,X,Y,theta):

    Jvalues=np.zeros((1,num_iterations))

    for i in range(num_iterations):

        result1=np.dot(X.T,np.subtract(Hypothesis(X,theta),Y))*(alpha/num_trainning)
        theta=theta-result1

        Jvalues[0,i]=costFunction(Hypothesis(X,theta),Y,num_trainning)

    return theta,Jvalues

def predict(theta, X):
    probability = Hypothesis(X,theta)
    return [1 if x >= 0.5 else 0 for x in probability]

#----------------------------------------- Code Start here ------------------------------------------------
# load data from file. here data consist of two features and the label.
data=np.genfromtxt("/directory/to/dataset",
                  delimiter=",")

# this for controlling the packing process of data to get x and y.
features_numbers=data.shape[1]-1
label=data.shape[1]-1 # just for clarity

# getting x and y , setting the algorithm values.
x=data[:,0:features_numbers]
y=data[:,label]

y=np.reshape(data[:,label],(y.shape[0],1))  # just for reshape the y to be (n,1) instead of (n,).
m=y.size  # number of trainning set.
theta=np.zeros((x.shape[1],1))
alpha=0.01 # learning rate
iterations=10000

#normalize x

mean_vector=np.zeros((x.shape[1],1))
std_vector=np.zeros((x.shape[1],1))

#normalize x
mean_vector=np.zeros((x.shape[1],1))
std_vector=np.zeros((x.shape[1],1))

for i in range(features_numbers):

    mean_vector[i,0]=np.mean(x[:,i])
    mean_vector[i,0]=np.mean(x[:,i])

    std_vector[i,0]=np.std(x[:,i])
    std_vector[i,0]=np.std(x[:,i])

x=np.divide(np.subtract(x,mean_vector.T),std_vector.T)

# --------------------------------------- Cost function --------------------------------------------------
cost=costFunction(Hypothesis(x,theta),y,m)
# you can print the cost value befire gradient descent works
print cost

new_theta,Jvalues=gradientDescent(num_iterations=iterations,alpha=alpha,num_trainning=m,X=x,Y=y,theta=theta)

# print gradient descent after working
print costFunction(Hypothesis(x,new_theta),y,m)

# calculating model accuracy
predictions = predict(new_theta, x)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
print 'accuracy = {0}%'.format(accuracy)


from sklearn.preprocessing import Normalizer
from pandas import read_csv
from numpy import set_printoptions
import numpy as np

# -- Normalize using sklearn--L1 norm -- #
print('\nNormalize using sklearn--L1 norm')
filename = 'pima-indians-diabetes.data.csv'
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']
data_frame = read_csv(filename, names=names)
array = data_frame.values

# Separate array into input and output components
X = array[:, 0:8]
Y = array[:, 8]

# L1 norm
scaler = Normalizer(norm='l1').fit(X)
normalizedX = scaler.transform(X)

# Summarize transformed data
set_printoptions(precision=3)
print(normalizedX[0:5, :])

# -- Normalize from scratch--L1 norm -- #
# L1 norm
print('\nNormalize from scratch--L1 norm')
norms = np.abs(X).sum(axis=1)

X_normalized = X / norms[:, np.newaxis]

print(X_normalized[0:5, :])

# -- Normalize using sklearn--L2 norm -- #
# L2 norm
print('\nNormalize using sklearn--L2 norm')
scaler = Normalizer(norm='l2').fit(X)
normalizedX = scaler.transform(X)

# Summarize transformed data
set_printoptions(precision=3)
print(normalizedX[0:5, :])

# -- Normalize from scratch--L2 norm -- #
# L2 norm
print('\nNormalize from scratch--L2 norm')
norms = np.einsum('ij,ij->i', X, X)
np.sqrt(norms, norms)
X_normalized = X / norms[:, np.newaxis]
print(X_normalized[0:5, :])


#------------------------------------------------#
# Gauss Elimination Linear System Numeric Solver #
#                   10/05/21                     #
#------------------------------------------------#

#Auxilary
import numpy as np
def rowChange(checkMat, line1, line2):
    line1Aux = line1
    while (line1Aux < checkMat.shape[0]) and (checkMat[line1Aux, line2]) == 0:
        line1Aux += 1
    #If entire row is 0s
    if line1Aux == checkMat.shape[0]:
        return None
    #Swap Row
    checkMat[[line1, line1Aux]] = checkMat[[line1Aux, line1]]
    return checkMat

#Main
def gaussElim(aMat_in, bMat_in):
    import numpy as np
    i = 0 #RowNum
    j = 0 #ColNum
    mult = 0 #Multiplier array
    pivot = 0 #Line pivot

    #Setup
    aMat = aMat_in
    bMat = bMat_in.reshape(aMat.shape[0],1)
    augMat = np.hstack((aMat, bMat))
    m = augMat.shape[0]
    k = augMat.shape[1]
    #Gauss algorithm
    while (i < m) and (j < k):
        #print("Step n°" + str(i+1) + ":" + "\n" + str(augMat))
        #Check consistency
        if np.linalg.det(augMat[:, :-1]) == 0:
            print("Matrix is not consistent / System is undetermined")
            break
        #Search for pivot / Swap rows if pivot == 0 / Pivotal condensation
        if (augMat[i,j] == 0):
            augMat = rowChange(augMat, i, j)
        pivot = augMat[i,j]
        #Define multiplier
        for i_m in range(i+1, m):
            mult = augMat[i_m,j]/pivot
            for j_m in range(j+1, k):
                augMat[i_m,j_m] = augMat[i_m,j_m] - mult*augMat[i,j_m]
            #Store multiplier in empty spaces
            augMat[i_m,j] = mult
        #Iterate
        i += 1
        j += 1

    #Separating augmented matrix for convenience
    newBMat = augMat[:, k-1]
    newAMat = augMat[:, 0:k-1]

    #Find results in triangular Matrix
    results = [0]*m
    results[m-1] = (newBMat[m-1]/newAMat[m-1, m-1])
    for i in reversed(range(0, m-1)):
        sumMat = 0
        for j in range(i+1, m):
            sumMat = sumMat + (newAMat[i,j]*results[j])
        results[i] = (newBMat[i] - sumMat)/newAMat[i,i]
    return results

#Implementation Example

#Test
aMat_in = np.array([[1,0,0,0],[1,0.5,pow(0.5,2),pow(0.5,3)],[1,0.75,pow(0.75,2),pow(0.75,3)],[1,1,1,1]])
bMat_in = np.array([0,0.479,0.682,0.841])
results = gaussElim(aMat_in,bMat_in)
print(results)

#Refinement step
resultsRef = results
refError = 5
while refError > 1e-5:
    rMat =  np.longdouble(np.subtract(bMat_in,np.matmul(aMat_in, resultsRef)))
    corrMat = gaussElim(aMat_in, rMat)
    oldResultsRef = resultsRef
    resultsRef = np.add(resultsRef,corrMat)
    refError = (abs(np.subtract(oldResultsRef,resultsRef))).max()
finalResult = resultsRef
print(finalResult)


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])
for i in range(0,4):
	py[i] = None

for i in range(1,4):
	k1 = deltax*f(x[i-1],y0)
	k2 = deltax*f(x[i-1]+deltax/2,y0+k1/2)
	k3 = deltax*f(x[i-1]+deltax/2,y0+k2/2)
	k4 = deltax*f(x[i-1]+deltax,y0+k3)
	y[i] =  y0 + (k1 + 2*k2 + 2*k3 + k4)/6
	y0 = y[i]

for i in range(4,n):
	py[i] = deltax/24*(55*f(x[i-1],y[i-1]) - 59*f(x[i-2],y[i-2]) + 37*f(x[i-3],y[i-3]) - 9*f(x[i-4],y[i-4]) )  + y[i-1]
	y[i] = deltax/24*( 9*f(x[i],py[i]) + 19*f(x[i-1],y[i-1]) - 5*f(x[i-2],y[i-2]) + f(x[i-3],y[i-3]) ) + 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 Adams-Bashforth-Moulton Method")
plt.show()

#table 19-6


# -*- coding: utf-8 -*-
"""
Created on Thu Sep 20 10:17:31 2018

@author: Thiago, Renan
"""

import matplotlib.lines as mlines
import matplotlib.pyplot as plt
import numpy as np
import math as m

### Funcao pra achar a raiz ###

def F(x):
    return m.exp(x) * (x - 1.0) - m.exp(-x) * (x + 1.0)

### Derivada da funcao

def dF(x):
    return m.exp(-x) * (x * m.exp(-2.0 * x) + x)

### Função de ponto fixo

def pF(x):
    return ((x+1.0)/m.exp(2.0 * x)) + 1.0

def insert_dash(string, index):
    return string[:index] + '|' + string[index:]

### Intervalo e chute inicial ###

a = np.dtype('f8')
b = np.dtype('f8')
xi_s = np.dtype('f8')
xi_n = np.dtype('f8')
E = np.dtype('f8')

a = 0.0
b = 2.0
xi = 1.5 #chute inicial
x0 = 1.0 #chute inicial (secante)
ordem = 8
E = 10**-ordem

### Metodos

def bisseccao(_a, _b, Er):

    _xa = _b
    _x = _b
    iteracoes = 0
    lista_iteracoes = []

    while(iteracoes < 10000):

        #acha meio
        e = _b - _a
        mx = e / 2.0
        _x = mx + _a

        #determina valores
        fa = F(_a)
        fx = F(_x)

        #se a raiz estiver entre um dos intervalos formados, ajusta intervalo (_a, _b)
        if((fa >= 0 and fx < 0) or (fa < 0 and fx >= 0)):
            #intervalo a-x
            _b = _x
        else:
            #intervalo x-b
            _a = _x

        lista_iteracoes.append([iteracoes, _x, e])

        if(abs((_x - _xa) / _x) < Er):
            break

        _xa = _x
        iteracoes += 1

    #atingiu condicao de parada
    return [_x, iteracoes, lista_iteracoes]

def falsaPosicao(_a,_b, Er):

    xa = _b
    x = _b
    iteracoes = 0
    lista_iteracoes = []

    #Checa a condição de existencia
    if (F(_a) * F(_b) < 0):

        while(iteracoes < 10000):

            x = (_a * F(_b) - _b * F(_a)) / (F(_b) - F(_a))

            if(F(_a) * F(x) < 0):
                _b = x
            else:
                _a = x

            lista_iteracoes.append([iteracoes, x, abs((x - xa) / x)])

            if(abs((x - xa) / x) < Er):
                break

            xa = x
            iteracoes += 1

    return [x, iteracoes, lista_iteracoes]

def pontoFixo(xi, Er):
    _x = xi
    x0 = xi
    iteracoes = 0
    lista_iteracoes = []
    while(iteracoes < 10000):
        _x = pF(_x)
        lista_iteracoes.append([iteracoes, _x, abs((_x - x0) / _x)])

        if(abs((_x - x0) / _x) < Er):
            break;

        x0 = _x
        iteracoes += 1

    return [_x, iteracoes, lista_iteracoes]

def secante(x0, x1, Er):
    iteracoes = 1
    x2 = np.dtype('f8')
    x2 = 0.0
    lista_iteracoes = []

    lista_iteracoes.append([0, x1, abs((x1 - x0) / x1) ])

    while(iteracoes < 10000):
        f0 = F(x0)
        f1 = F(x1)
        x2 = x2 - ((f1 * (x1 - x0)) / (f1 - f0))

        lista_iteracoes.append([iteracoes, x2, abs((x2 - x1) / x2) ])

        if(abs((x2 - x1) / x2) < Er):
            break

        x0 = x1
        x1 = x2
        iteracoes += 1

    return [x2, iteracoes, lista_iteracoes]

def newton(xi, Er):
    iteracoes = 0
    lista_iteracoes = []
    x0 = xi

    while(iteracoes < 10000):
        x1 = x0 - (F(x0) / dF(x0))

        lista_iteracoes.append([iteracoes, x1, abs((x1 - x0) / x1)])

        if(abs((x1 - x0) / x1) < Er):
            break

        x0 = x1
        iteracoes += 1

    return [x1, iteracoes, lista_iteracoes]

### Implementacao dos metodos ###

### Bisseccao
resultado_bisseccao = bisseccao(a, b, E)
raiz_funcao_bis = resultado_bisseccao[0]
num_passos_bis = resultado_bisseccao[1]
string_bis = repr(raiz_funcao_bis)

print("\nBisseccao:\n")
print("Raiz:"+insert_dash(string_bis, ordem + string_bis.find('.') + 1)+"")
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_bis)+"")

### Falsa Posicao
resultado_falsa_pos = falsaPosicao(a, b, E)
raiz_funcao_fpos = resultado_falsa_pos[0]
num_passos_fpos = resultado_falsa_pos[1]
string_fpos = repr(raiz_funcao_fpos)

print("\nFalsa Posicao:\n")
print("Raiz:"+insert_dash(string_fpos, ordem + string_fpos.find('.') + 1)+"")
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_fpos)+"")

### Ponto Fixo
resultado_ponto_fixo = pontoFixo(xi, E)
raiz_funcao_pfixo = resultado_ponto_fixo[0]
num_passos_pfixo = resultado_ponto_fixo[1]
string_pfixo = repr(raiz_funcao_pfixo)

print("\nPonto Fixo:\n")
print("Chute inicial:"+str(xi)+"")
print("Raiz:"+insert_dash(string_pfixo, ordem + string_pfixo.find('.') + 1)+"")
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_pfixo)+"")

### Newton
resultado_newton = pontoFixo(xi, E)
raiz_funcao_newton = resultado_newton[0]
num_passos_newton = resultado_newton[1]
string_newton = repr(raiz_funcao_newton)

print("\nNewton:\n")
print("Chute inicial:"+str(xi)+"")
print("Raiz:"+insert_dash(string_newton, ordem + string_newton.find('.') + 1)+"")
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_newton)+"")

### Secante
resultado_secante = secante(x0, xi, E)
raiz_funcao_secante = resultado_secante[0]
num_passos_secante = resultado_secante[1]
string_secante = repr(raiz_funcao_secante)

print("\nSecante:\n")
print("Chute inicial 0: "+str(x0)+"")
print("Chute inicial 1:"+str(xi)+"")
print("Raiz:"+insert_dash(string_secante, ordem + string_secante.find('.') + 1)+"")
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_secante)+"")

### Geração de gráficos

def geraGrafico(nome, lista):
    lista0 = []
    lista1 = []
    lista2 = []

    for item in lista:
        lista0.append(item[0])
        lista1.append(item[1])
        lista2.append(item[2])

    #plota grafico
    plt.plot(
        lista0, lista1, 'b--',
        lista0, lista2, 'r--'
    )

    #legendas do grafico
    se_line = mlines.Line2D([], [], color='blue', marker='', markersize=0, label=u'Solução')
    ee_line = mlines.Line2D([], [], color='red', marker='', markersize=0, label=u'Erro')

    plt.legend(handles=[se_line, ee_line])
    plt.title(nome)
    plt.show()

#geraGrafico(u"Bissecçao", resultado_bisseccao[2])
#geraGrafico(u"Falsa Posicao", resultado_falsa_pos[2])
#geraGrafico(u"Ponto Fixo", resultado_ponto_fixo[2])
#geraGrafico(u"Newton", resultado_newton[2])
geraGrafico(u"Secante", resultado_secante[2])


import time

import numpy as np
from scipy import misc

from hermite import hermite_polynomial
from lagrange import lagrange_polynomial
from power_series import power_serie

f = np.sin
a = -5
b = 5
n_ = int(input("input n ? (polynomial's degree will be 2n+1)"))
startLT = time.time()
n = 2 * n_ + 2
racines = [(a + b) / 2 + (a - b) / 2 * np.cos((2 * k + 1) * (np.pi) / (2 * n)) for k in range(0, n)]
ycoords = [f(x) for x in racines]
L = lagrange_polynomial(racines, ycoords)
endLT = time.time()
lengthLT = endLT - startLT
startLH = time.time()
n_ = n_ + 1
ycoords = [f(x) for x in racines]
dycoords = [misc.derivative(f, x) for x in racines]
L = hermite_polynomial(racines, ycoords, dycoords)
endLH = time.time()
lengthLH = endLH - startLH
startSP = time.time()
res = []
xcoords = [a + k * (b - a) / n for k in range(n + 1)]
ycoords = [f(k) for k in xcoords]
dycoords = [misc.derivative(f, k) for k in xcoords]
for k in range(n):
    a0 = ycoords[k + 1] / (xcoords[k + 1] - xcoords[k])
    a1 = ycoords[k] / (xcoords[k] - xcoords[k + 1])
    a2 = (dycoords[k] - a0 - a1) / ((xcoords[k] - xcoords[k + 1]) ** 2)
    a3 = (dycoords[k + 1] - a0 - a1) / ((xcoords[k + 1] - xcoords[k]) ** 2)
    nu0 = -a0 * xcoords[k] - a1 * xcoords[k + 1] - a2 * xcoords[k] * xcoords[k + 1] ** 2 - a3 * xcoords[k] ** 2 * \
          xcoords[k + 1]
    nu1 = a0 + a1 + a2 * (xcoords[k + 1] ** 2 + 2 * xcoords[k + 1] * xcoords[k]) + a3 * (
            xcoords[k] ** 2 + 2 * xcoords[k] * xcoords[k + 1])
    nu2 = -a2 * (xcoords[k] + 2 * xcoords[k + 1]) - a3 * (xcoords[k + 1] + 2 * xcoords[k])
    nu3 = a2 + a3
    P = [nu0, nu1, nu2, nu3]
    res.append(P)
endSP = time.time()
lengthSP = endSP - startSP
startSE = time.time()
L = power_serie(f, n)
endSE = time.time()
lengthSE = endSE - startSE
print("-> Lagrange's length:", lengthLH)
print("-> L'Hermite's length:", lengthLT)
print("-> Cubic splines's length:", lengthSP)
print("-> Power series's length", lengthSE)


import numpy as np
from helpers import sigmoid

### Loss computing functions

# Mean Square Error
def compute_mse(e):
    return 1/2*np.mean(e**2)

# Root Mean Square Error
def compute_rmse(e):
    return np.sqrt(2 * compute_mse(e))

# Mean Absolute Error
def compute_mae(e):
    return np.mean(np.abs(e))

# Computing loss for logistic regression
def compute_loss_log_reg(y, tx, w):
    return -(y.T.dot(np.log(sigmoid(tx.dot(w)))) + (1 - y).T.dot(np.log(1 - sigmoid(tx.dot(w)))))

# Computing loss with a selected cost function
def compute_loss(y, tx, w, func="mse"):
    # Computing error
    error = y - tx.dot(w)

    # Using Mean square error
    if func == "mse":
        return compute_mse(error)

    # Using Mean absolute error
    elif func == "mae":
        return compute_mae(error)

    # Using Root mean square error
    elif func == "rmse":
        return compute_rmse(error)


import math
import numpy as np
import numpy.linalg as la

a = np.array([1, 2, 3])

# L¹, taxicab norm, manhattan norm
# ‖v‖₁ = |a₁| + |a₂| + |a₃|
b = la.norm(a, 1)

print(b)
print(b == a[0] + a[1] + a[2])

# L², Euclidean norm
# L²(v) = ‖v‖₂
c = la.norm(a)
print(c)
print(c == la.norm(a, 2))
print(c == math.sqrt(
    math.pow(a[0], 2)
    + math.pow(a[1], 2)
    + math.pow(a[2], 2)
))

# Max Norm
d = la.norm(a, math.inf)
print(d)
print(d == max(a))


'''
Lab 10: Leontieff Input-Output Models
Solutions File
'''

import numpy as np
from scipy import linalg as la

def geomSeries(C, d, m):
    '''
    Calculate the geometric series of C up to the m power, multiplied by d.
    Inputs:
        C -- a (n,n) shape array
        d -- a length n array
        m -- a nonnegative integer
    Return:
        The value (\sum_{i=0}^m C^i)d
    '''
    powC = C.copy()
    sumC = np.eye(C.shape[0]) + powC
    for k in xrange(m):
        powC = C.dot(powC)
        sumC += powC
    return np.dot(sumC, d)

def construction():
    '''
    Calculate the cost vector for producing an additional 50% of construction.
    Calculate an approximation by using geomSeries for m=5.
    Then calculate the exact answer.
    Return both answers, with the approximation first.
    '''
    IO = np. array ([[250. , 150. , 30. , 600.] , [25. , 25. , 20. , 280.] ,[50. , 20. , 5., 120.]])
    IOCoeff = IO [: ,:3] / IO [: ,3]
    d = np.array([0., 0., 60.0]) # a 50% increase over a baseline 120 means 60 additional units

    return geomSeries(IOCoeff, d, 5), np.dot(la.inv(np.eye(IOCoeff.shape[0])-IOCoeff), d)

def demand():
    '''
    Calculate and return the demand vector for the three product economy.
    '''
    IO = np. array ([[250. , 150. , 30. , 600.] , [25. , 25. , 20., 280.] ,[50. , 20. , 5., 120.]])
    IOCoeff = IO [: ,:3] / IO [: ,3]
    X = np.array([600,280,120]) # this is the total output, as given in the lab

    return X - np.dot(IOCoeff,X)

def cityOutput():
    '''
    Calculate and return the required output vector for the city, given the
    demand vector.
    '''
    C = np.array([[.2,.3,.3],[.1,.2,.3],[.2,.2,.2]]) # IO coefficients
    D = np.array([100000,100000,40000]) # demand vector
    return np.dot(la.inv(np.eye(3)-C),D) # from the formula in the lab

def getIOCoeffs():
    '''
    Import the data from the csv file as described in the problem statement.
    Calculate and return the IO coefficient matrix as well as the demand vector.
    Return both of these values, the matrix first, and then the vector.
    '''
    data = np.genfromtxt("io2002table.txt", delimiter='\t', skiprows=1,
                         usecols=np.arange(1,52), missing_values = [''],
                         filling_values={i:0 for i in xrange(1,52)})
    iocoeff = data[:,:-1] / data[:,-1]
    X = data[:,-1] # this is the last column, which corresponds to total output
    D = X - np.dot(iocoeff, X) # this is our demand vector
    return iocoeff, D

def washingtonOutput(iocoeffs, demand):
    '''
    Calculate the output vector corresponding to an increase of 10% in the demand
    for construction.
    Inputs:
        iocoeffs -- the input-output coefficient matrix for the Washington economy, as
                    calculated in the getIOCoeffs function.
        demand -- the demand vector for the Washington economy, as calculated in the
                  getIOCoeffs function.
    Return:
        The output vector corresponding to an increase of 10% in the construction demand.
    '''
    # now increase the construction demand by 10% (entry 8)
    demand[8] += demand[8]*.10
    # now calculate the new output vector corresponding to this demand:
    return la.solve(np.eye(iocoeffs.shape[0]) - iocoeffs, demand)


import sample_images
import random
import display_network
import numpy as np

##================================================================
## Step 0a: Load data
#  Here we provide the code to load natural image data into x.
#  x will be a 144 * 10000 matrix, where the kth column x(:, k) corresponds to
#  the raw image data from the kth 12x12 image patch sampled.
#  You do not need to change the code below.

patches = sample_images.sample_images_raw()
num_samples = patches.shape[1]
random_sel = random.sample(range(num_samples), 400)
display_network.display_network(patches[:, random_sel], 'raw_pca.png')

##================================================================
## Step 0b: Zero-mean the data (by row)
#  You can make use of the mean and repmat/bsxfun functions.

# patches = patches - patches.mean(axis=0)
patch_mean = patches.mean(axis=1)
patches = patches - np.tile(patch_mean, (patches.shape[1], 1)).transpose()

##================================================================
## Step 1a: Implement PCA to obtain xRot
#  Implement PCA to obtain xRot, the matrix in which the data is expressed
#  with respect to the eigenbasis of sigma, which is the matrix U.

sigma = patches.dot(patches.transpose()) / patches.shape[1]
(u, s, v) = np.linalg.svd(sigma)

patches_rot = u.transpose().dot(patches)

##================================================================
## Step 2: Find k, the number of components to retain
#  Write code to determine k, the number of components to retain in order
#  to retain at least 99% of the variance.

k = 0
for k in range(s.shape[0]):
    if s[0:k].sum() / s.sum() >= 0.99:
        break
print('Optimal k to retain 99% variance is:', k)

##================================================================
## Step 3: Implement PCA with dimension reduction
#  Now that you have found k, you can reduce the dimension of the data by
#  discarding the remaining dimensions. In this way, you can represent the
#  data in k dimensions instead of the original 144, which will save you
#  computational time when running learning algorithms on the reduced
#  representation.
#
#  Following the dimension reduction, invert the PCA transformation to produce
#  the matrix xHat, the dimension-reduced data with respect to the original basis.
#  Visualise the data and compare it to the raw data. You will observe that
#  there is little loss due to throwing away the principal components that
#  correspond to dimensions with low variation.

patches_tilde = u[:, 0:k].transpose().dot(patches)
patches_hat = u.dot(np.resize(patches_tilde, patches.shape))

display_network.display_network(patches_hat[:, random_sel], 'pca_tilde.png')
display_network.display_network(patches[:, random_sel], 'pca.png')

##================================================================
## Step 4a: Implement PCA with whitening and regularisation
#  Implement PCA with whitening and regularisation to produce the matrix
#  xPCAWhite.

epsilon = 0.1
patches_pcawhite = np.diag(1 / (s + epsilon)).dot(patches_rot)

##================================================================
## Step 5: Implement ZCA whitening
#  Now implement ZCA whitening to produce the matrix xZCAWhite.
#  Visualise the data and compare it to the raw data. You should observe
#  that whitening results in, among other things, enhanced edges.

patches_zcawhite = u.dot(patches_pcawhite)
display_network.display_network(patches_zcawhite[:, random_sel], 'pca_zcawhite.png')


# Practicing Dynamic Programming (DP) with the log cutting problem given on the practice midterm.
import numpy as np

################################################################################
# Slow Algorithm
################################################################################

def cut_log(d, j, k):
    if j+1 == k:
        return 0
    c = float('Inf')
    for i in range(j+1, k):
        c = min(c, d[k] + cut_log(d, j, i) + cut_log(d - d[i], i, k))
    return c

################################################################################
# Top Down Algorithm
################################################################################

def memoized_cut_log(d):
    k = len(d)
    j = 0
    r = np.ones([k, k])*np.inf
    v = memoized_cut_log_aux(d, j, k-1, r)
    return v

def memoized_cut_log_aux(d, j, k, r):
    if r[j, k] < np.inf:
        return r[j, k]
    if j+1 == k:
        r[j, k] = 0
    else:
        c = float('Inf')
        for i in range(j+1, k):
            c = min(c, d[k] + memoized_cut_log_aux(d, j, i, r) + memoized_cut_log_aux(d - d[i], i, k, r))
        r[j, k] = c
    return r[j, k]

################################################################################
# Bottom Up Algorithm
################################################################################

# def bottom_up_cut_log(d):
#     k = len(d)
#     r = np.zeros([k, k])
#     for i in range(2, k):
#         c = np.inf
#         for j in range(1, i):
#             c = min(c, d[i] + r[j, i-1] + r[j-1, i])
#         r[j, i] = c
#     print(r)
#     return r[0, k-1]

################################################################################
# Main
################################################################################
if __name__ == '__main__':
    dist = np.array([0, 3, 8, 10])
    print("min cost (slow) = $" + str(cut_log(dist, 0, 3)))
    print("min cost (top down) = $" + str(memoized_cut_log(dist)))
    # print("min cost (slow) = $" + str(bottom_up_cut_log(dist)))


import numpy as np

EPS = np.finfo(np.float32).eps

def log_loss(
        y_hat: np.ndarray,
        y: np.ndarray
) -> float:
    """
    Log-loss cost function.

    :param y_hat: Predicted value (scaled [0, 1])
    :param y: Label value (i.e., ground-truth)
    :return: loss value
    """
    is_one = y * np.log(y_hat)
    is_zero = (1.0 - y) * np.log(1.0 - y_hat)
    loss = -(is_one + is_zero).mean()
    return loss

def mean_squared_error(
        y_hat: np.ndarray,
        y: np.ndarray
) -> float:
    """
    Mean-squared-error cost function.

    :param y_hat: Predicted value (scaled [0, 1])
    :param y: Label value (i.e., ground-truth)
    :return: loss value
    """
    error = y - y_hat
    squared_error = error ** 2
    mse = squared_error.mean()
    return mse


# # Simple ReLU
# inputs = [0, 2, -1, 3.3, -2.7, 1.1, 2.2, -100]

# output = []
# for i in inputs:
#     # if i > 0:
#     #     output.append(i)
#     # else:
#     #     output.append(0)
#     output.append(max(0, i))

# print(output)

# # In NumPY
# import numpy as np

# inputs = [0, 2, -1, 3.3, -2.7, 1.1, 2.2, -100]
# output = np.maximum(0, inputs)
# print(output)

# # Softmax
# # Values from the previous output when we described
# # what a neural network is
# layer_outputs = [4.8, 1.21, 2.385]

# # e - mathematical constant, we use E here to match a common coding
# # style where constants are uppercased
# E = 2.71828182846  # you can also use math.e

# # For each value in a vector, calculate the exponential value
# exp_values = []
# for output in layer_outputs:
#     exp_values.append(E ** output)  # ** - power operator in Python
# print("exponentiated values:")
# print(exp_values)

# # Now normalize values
# norm_base = sum(exp_values)  # We sum all values
# norm_values = []
# for value in exp_values:
#     norm_values.append(value / norm_base)
# print("Normalized exponentiated values:")
# print(norm_values)

# print("Sum of normalized values:", sum(norm_values))

# # And in NumPy
# import numpy as np

# # Values from the earlier previous when we described
# # what a neural network is

# layer_outputs = [4.8, 1.21, 2.385]

# # For each value in a vector, calculate the exponential value
# exp_values = np.exp(layer_outputs)
# print("exponentiated values:")
# print(exp_values)

# # Now normalize values
# norm_values = exp_values / np.sum(exp_values)
# print("normalized exponentiated values:")
# print(norm_values)
# print("sum of normalized values:", np.sum(norm_values))

# Axis and keepdims
import numpy as np

layer_outputs = np.array([[4.8, 1.21, 2.385], [8.9, -1.81, 0.2], [1.41, 1.051, 0.026]])

print("Sum without axis")
print(np.sum(layer_outputs))

print("This will be identical to the above since default is None:")
print(np.sum(layer_outputs, axis=None))

print("Another way to think of it w/ a matrix == axis 0: columns:")
print(np.sum(layer_outputs, axis=0))

print("But we want to sum the rows instead, like this w/ raw py:")
for i in layer_outputs:
    print(sum(i))

print("So we can sum axis 1, but note the current shape:")
print(np.sum(layer_outputs, axis=1))

print("Sum axis 1, but keep the same dimensions as input:")
print(np.sum(layer_outputs, axis=1, keepdims=True))


"""
https://projecteuler.net/problem=3
The prime factors of 13195 are 5, 7, 13 and 29.

What is the largest prime factor of the number 600851475143 ?
"""

from numpy import sqrt

from Common.Logger import get_logger, init_logger
from Common.Primes import sieve_of_atkin
from Common.Utilities import performance_run

NUMBER = 600_851_475_143
PERFORMANCE_RUNS = 1000

def fastest(number: int = NUMBER) -> int:
    """
    This algorithm counts up from 1 to sqrt(number), factorizing the number along the way
    :param number: The number of which this function will find the greatest prime factor
    :return:
    """
    return reduction(number)

def reduction(number: int = NUMBER) -> int:
    """
    This algorithm counts up from 1 to sqrt(number), factorizing the number along the way
    --> benchmark: 56672789 ns/run (over 1000 runs)
    --> benchmark: 59520400 ns/run (over 1 run)
    :param number: The number which this function will find the greatest prime factor
    :return: The greatest prime factor
    """
    largest_prime = 1
    for i in range(2, int(sqrt(number) + 1)):
        while number % i == 0:
            largest_prime = i
            number = number/i
    return largest_prime if largest_prime != 1 else number

def cached_sieve(number: int = NUMBER) -> int:
    """
    This algorithm utilizes the Sieve of Atkin (the ~fastest~ prime sieve on the market) to generate a full list of
    primes up to sqrt(number). It then works its way backward in the list to see what the first prime factor is. The
    Primes library utilizes functools.lru_caching to avoid having to repeat calculations at runtime, so that skews the
    performance of this algorithm. Checking only once takes ~50x longer than each additional check.
    For fastest(), we'll select the method of reduction due to this behavior, as I don't really care to more-permanently
    cache a long list of primes. That said, long live the cache.
    --> benchmark:   9038529 ns/run (over 1000 runs)
    --> benchmark: 463035900 ns/run (over 1 run)
    :param number: The number which this function will find the greatest prime factor
    :return: The greatest prime factor
    """
    primes = sieve_of_atkin(int(sqrt(number)))
    for i in range(1, len(primes)):
        if number % primes[-i] == 0:
            return primes[-i]
    return number

if __name__ == "__main__":
    # Log stuff
    init_logger()
    logger = get_logger()

    # Performance run for the reductive "bottoms-up" solution and cached sieve "top-down" solution
    # performance_run(reduction, iterations=PERFORMANCE_RUNS)()
    # performance_run(cached_sieve, iterations=PERFORMANCE_RUNS)()
    print(fastest(NUMBER))


# Consider a child waiting at a street corner for a gap in traffic that is large enough so that he can safely cross the street.
# A mathematical model for traffic shows that if the expected waiting time for the child is to be at most 1 minute, then the maximum traffic flow, in cars per hour, is given by
# f(x) = 29,403( 2.335 - log( x ) ) / x, where x is the width of the street in feet.

from sympy import *

init_printing()

def disp_fun( f ):
	pprint( '\n{0}\n\n'.format( pretty( f ) ) )

x = symbols( 'x' )

fX = 29403 * ( 2.335 - log( x, 10 ) )  / x
dX = diff( fX, x )

simplify( dX )

# Find the maximum traffic flow and the rate of change of the maximum traffic flow with respect to street width for the street width of 35 feet.
# (Do not round until the final answer. Then round to the nearest integer as needed.)
round( fX.subs( { x: 35 } ).evalf(), 0 )

# The rate of change of the maximum traffic flow is about X vehicles per hour per foot.
# (Do not round until the final answer. Then round to the nearest tenth as needed.)
round( dX.subs( { x: 35 } ).evalf(), 1 )

# Find the maximum traffic flow and the rate of change of the maximum traffic flow with respect to street width for the street width of 54 feet.
# (Do not round until the final answer. Then round to the nearest integer as needed.)
round( fX.subs( { x: 54 } ).evalf(), 0 )

# The rate of change of the maximum traffic flow is about X vehicles per hour per foot.
# (Do not round until the final answer. Then round to the nearest tenth as needed.)
round( dX.subs( { x: 54 } ).evalf(), 1 )


import numpy as np
import scipy

def variance(sample):
    """
    Calculate variance of a sample. After learn.co
    """
    sample_mean = np.mean(sample)
    return sum([(i - sample_mean)**2 for i in sample])

def sample_variance(sample1, sample2):
    """
    Calculate sample variance. After learn.co
    """
    n_1, n_2 = len(sample1), len(sample2)
    var_1, var_2 = variance(sample1), variance(sample2)
    return (var_1 + var_2)/((n_1 + n_2)-2)

def twosample_tstatistic(sample1, sample2):
    exp_mean, sample2_mean = np.mean(sample1), np.mean(sample2)
    samp_var = sample_variance(sample1, sample2)
    n_e, n_c = len(sample1), len(sample2)
    num = exp_mean - sample2_mean
    denom = np.sqrt(samp_var * ((1/n_e)+(1/n_c)))
    return num / denom

def one_sample_ttest(sample, popmean, alpha):
    """Calculate t-value and p-value and return each"""

    # Population
    mu = popmean

    # Sample mean (x̄) using NumPy mean()
    sample_mean = np.mean(sample)

    # Sample Stadard Deviation (sigma) using Numpy
    sample_std = np.std(sample, ddof=1)

    # Degrees of freedom
    degrees_freedom = len(sample) - 1

    #Calculate the critical t-value
    t_crit = scipy.stats.t.ppf(1-alpha, df=degrees_freedom)

    #Calculate the t-value and p-value
    t_val, p_val = scipy.stats.ttest_1samp(a=sample, popmean=mu)

    #return results
    #if t-value is greater than t-critical than you can reject the null hypothesis
    #if p-value is less than alpha than you can reject the null hypothesis
    if t_val > t_crit and p_val < alpha:
        print("Null Hypothesis rejected. ", "t-value: ", t_val, "p-value: ", p_val)
    else:
        print("Null Hypothesis true. ", "t-value: ", t_val, "p-value: ", p_val)
    return t_val, p_val

def cohen_d(group1, group2):
    """
    For two groups' samples, calculate Cohen's D
    group1: Series or NumPy array
    group2: Series or NumPy array
    After Learn.co
    """
    # returns a floating point number

    diff = group1.mean() - group2.mean()

    n1, n2 = len(group1), len(group2)
    var1 = group1.var()
    var2 = group2.var()

    # Calculate the pooled threshold as shown earlier
    pooled_var = (n1 * var1 + n2 * var2) / (n1 + n2)

    # Calculate Cohen's d statistic
    d = diff / np.sqrt(pooled_var)

    return d


from numpy import *

def f(u,beta,gamma):

    dSdt = - beta * u[0] * u[1]
    dRdt =                        gamma * u[1]
    dIdt =   -dSdt - dRdt

    return array([dSdt,dIdt,dRdt])

def check(Ustart):
    if type(Ustart) == list :
        return array(Ustart)
    else :
        return Ustart

# -------------------------------------------------------------------------
#
# -2- Schema de d'Euler explicite d'ordre 1
#

def epidemicEuler(Xstart,Xend,Ustart,n,beta,gamma):
    X, h = linspace(Xstart,Xend,n+1, retstep = True)
    Ustart = check(Ustart)

    U = zeros((len(X), 3))
    U[0] = Ustart

    for i in range(len(X) -1):
        U[i+1] = U[i] + h*f(U[i],beta,gamma)

    return X,U

# -------------------------------------------------------------------------
#
# -2- Schema de Taylor classique d'ordre 4
#

def epidemicTaylor(Xstart,Xend,Ustart,n,beta,gamma):
    X,h = linspace(Xstart,Xend,n+1, retstep = True)
    Ustart = check(Ustart)

    U = zeros((len(X), 3))
    U[0] = Ustart

    multiplicator = array([h, (h**2)/2, (h**3)/6, (h**4)/24])
    for i in range(len(X)-1) :

        s0, i0, r0 = U[i]
        s1, i1, r1 = f(U[i], beta,gamma)
        s2, r2 = -beta*(s1*i0 + i1*s0), gamma*i1
        i2 = -(s2 + r2)
        s3, r3 = -beta*(s2*i0 + 2*s1*i1 + i2*s0), gamma*i2
        i3 = -(s3+r3)
        s4, r4 = -beta*(s3*i0 + 3*s2*i1 + 3*s1*i2 + s0*i3), gamma*i3
        i4 = -(s4 + r4)

        U[i+1] = U[i] + (multiplicator @ array([[s1,i1,r1],[s2,i2,r2],[s3,i3,r3],[s4,i4,r4]]))

    return X,U

# -------------------------------------------------------------------------
#
# -3- Schema de Runge-Kutta d'ordre 4
#

def epidemicRungeKutta(Xstart,Xend,Ustart,n,beta,gamma):
    X,h = linspace(Xstart,Xend,n+1, retstep = True)
    Ustart = check(Ustart)

    U = zeros((len(X), 3))
    U[0] = Ustart

    for i in range(len(X)-1):
        K1 = f(U[i], beta, gamma)
        K2 = f(U[i] + (h/2)*K1,beta,gamma)
        K3 = f(U[i] + (h/2)*K2,beta,gamma)
        K4 = f(U[i] + h*K3,beta,gamma)

        U[i+1] = U[i] + (h/6)*(K1 + 2*K2 + 2*K3 + K4)

    return X,U


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

#
# define a wavelength going from 0 to 2pi
#
thetime=np.arange(0.,2*pi,0.05)
thewave=thetime/(2.*pi)
#
# phase shifts
#
thirty=30.*pi/180.
sixty=2.*thirty
ninety=3.*thirty
onetwenty=2.*sixty
oneeighty=3.*sixty

fig1,axis1=plt.subplots(1,1)
#
# add phase shifts and plot cosine
#
axis1.plot(thewave,np.cos(thetime),'b-',label='0')
axis1.plot(thewave,np.cos(thetime + thirty),'c-',label='30')
axis1.plot(thewave,np.cos(thetime + sixty),'g-',label='60')
axis1.plot(thewave,np.cos(thetime + ninety),'k-',label='90')
axis1.plot(thewave,np.cos(thetime + onetwenty),'m-',label='120')
axis1.plot(thewave,np.cos(thetime + oneeighty),'r-',label='180')
axis1.set_xlabel('horizontal position (in wavelengths)')
axis1.set_ylabel('amplitude')
axis1.set_title('cosine waves for 5 phase shifts')
axis1.legend(loc='best')
fig1.savefig('cosine_plot.png')
#
# add phase shifts and plot sines
#
fig2,axis2=plt.subplots(1,1)
axis2.plot(thewave,np.cos(thetime),'b-',label='0')
axis2.plot(thewave,np.cos(thetime + thirty),'c-',label='30')
axis2.plot(thewave,np.cos(thetime + sixty),'g-',label='60')
axis2.plot(thewave,np.cos(thetime + ninety),'k-',label='90')
axis2.plot(thewave,np.cos(thetime + onetwenty),'m-',label='120')
axis2.plot(thewave,np.cos(thetime + oneeighty),'r-',label='180')
axis2.set_xlabel('horizontal position (in wavelengths)')
axis2.set_ylabel('amplitude')
axis2.set_title('sine waves for 5 phase shifts')
axis2.legend(loc='best')
fig2.savefig('sine_plot.png')

#
# make a reflection at 0.75 wavelengths
#
fig3,axis3=plt.subplots(1,1)
line1=axis3.plot(thewave,np.cos(thetime),'b-')
newX=thetime
#zero out the inital wave so it looks like a reflection
newX[newX > 0.75*2.*pi]=np.nan
#
#  add a pi phase shift
#
newX = newX + pi
line2=axis3.plot(thewave,np.cos(newX),'r-')
axis3.set_xlabel('horizontal position (in wavelengths)')
axis3.set_ylabel('amplitude at receiver')
axis3.set_title('phase shift for a reflection occuring at 0.75 wavelengths')
axis3.legend((line1[0],line2[0]),('first pulse reflected at 1 wavelength','second pulse reflected at 3/4 wavelength'))
fig3.savefig('reflection_plot.png')

plt.show()


"""
Implementation of the steepest gradient descent and Newton
method.
"""
from __future__ import annotations

import numpy as np

from numpy import Inf
from numpy.linalg import inv

# Himmelblau's function
f = lambda x: (x[0]**2+x[1]-11)**2 + (x[0]+x[1]**2-7)**2
# Gradient
df_dx = lambda x: 2*(x[0]**2+x[1]-11)*2*x[0] + 2*(x[0]+x[1]**2-7)
df_dy = lambda x: 2*(x[0]**2+x[1]-11) + 2*(x[0]+x[1]**2-7)*2*x[1]
# Hessian
d2f_dx2 = lambda x: 8*x[0]**2 + 4*(x[0]**2+x[1]-11) + 2
d2f_dxy = lambda x: 4*x[0] + 4*x[1]
d2f_dyx = lambda x: 4*x[0] + 4*x[1]
d2f_dy2 = lambda x: 2 + 8*x[1]**2 + 4*(x[0]+x[1]**2-7)

def himmelblau(params: list[float]) -> float:
    return (params[0]**2+params[1]-11)**2 + (params[0]+params[1]**2-7)**2

def gradient_q(params: list[float]):
    return np.array([df_dx(params), df_dy(params)])

def hessian_q(params: list[float]):
    return np.array(
            [[d2f_dx2(params), d2f_dxy(params)],
            [d2f_dyx(params), d2f_dy2(params)]]
            )

def newton_search_direction(x: np.ndarray) -> np.ndarray:
    return np.dot(inv(hessian_q(x)), gradient_q(x))

def gradient_search_direction(x: np.ndarray) -> np.ndarray:
    return gradient_q(x)

def find_minimum(
    starting_pos: list[float],
    beta: float,
    max_iters: float,
    d: function,
    epsilon: float=10e-12
    ) -> list[float]:

    x = np.array(starting_pos, dtype='double')
    step = len(starting_pos)*[Inf]
    iter = 0
    visited_points = [x.copy()]
    while iter < max_iters and not np.allclose(step, np.zeros(len(step)), atol=epsilon):
        step = beta*d(x)
        x -= step
        # print(f"Iter: {iter}, Step: {step}, x = {x}")
        iter += 1
        visited_points.append(x.copy())
    return x, visited_points

def main() -> int:
    X = [0., 0]
    beta = 0.001
    max_iters = 10000
    print('Gradient: Minimum found at ', find_minimum(X, beta, max_iters, gradient_search_direction)[0])
    print('Newton: Minimum found at ', find_minimum(X, beta, max_iters, newton_search_direction)[0])
    return 0

if __name__ == "__main__":
    raise SystemExit(main())


######
###BACKGROUND
#Below Section: Imports necessary functions
import numpy as np
import matplotlib.pyplot as graph
import random as rand
import time as watch
pi = np.pi

######
###SIMULATIONS

#FUNCTION: ishit
#PURPOSE: This function is meant to test whether or not a given 2d point is within the unit circle.
#INPUTS: x = x-axis location, y = y-axis location
#OUTPUT: Boolean
def ishit(x, y):
	#Below Section: Calculates (x, y) vector distance from origin using Pythagorean theorem
	hyplength = np.sqrt(x**2 + y**2)

	#Below Section: Returns whether or not length within unit circle or not
	if hyplength <= 1:
		return True
	else:
		return False

#FUNCTION: simdarts
#PURPOSE: This function is meant to simulate a dart game, with the goal of hitting the inner circle.
#INPUTS: Number of times to 'throw' a dart
#OUTPUTS: Arrays of x and y locations where the dart falls each time; estimated pi value
def simdarts(num):
	#Below Section: Simulates given number of dart throws
	num = int(num)
	xlocs = np.zeros(num) #X-axis locations
	ylocs = np.zeros(num) #Y-axis locations

	#Below generates the random numbers in [0, 1)
	for a in range(0, num):
		xlocs[a] = rand.random()
		ylocs[a] = rand.random()

	#Below Section: Estimates the value of pi using the results
	count = 0
	for b in range(0, num):
		if ishit(x=xlocs[b], y=ylocs[b]):
			count = count + 1

	#Below Section: Returns the calculated values
	#NOTE: Multiplies proportion by four to account for percentage of total circular area
	return {'xs':xlocs, 'ys':ylocs, 'estpi':(count/1.0/num)*4}


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Fri Dec 29 13:46:07 2017

@author: thieunv
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
np.random.seed(11)

means = [[2, 2], [8, 3], [3, 6]]
cov = [[1, 0], [0, 1]]
N = 500
X0 = np.random.multivariate_normal(means[0], cov, N)
X1 = np.random.multivariate_normal(means[1], cov, N)
X2 = np.random.multivariate_normal(means[2], cov, N)

X = np.concatenate((X0, X1, X2), axis = 0)
K = 3

original_label = np.asarray([0]*N + [1]*N + [2]*N).T

def kmeans_display(X, label):
    K = np.amax(label) + 1
    X0 = X[label == 0, :]
    X1 = X[label == 1, :]
    X2 = X[label == 2, :]

    plt.plot(X0[:, 0], X0[:, 1], 'b^', markersize = 4, alpha = .8)
    plt.plot(X1[:, 0], X1[:, 1], 'go', markersize = 4, alpha = .8)
    plt.plot(X2[:, 0], X2[:, 1], 'rs', markersize = 4, alpha = .8)

    plt.axis('equal')
    plt.plot()
    plt.show()

kmeans_display(X, original_label)

from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=3, random_state=0).fit(X)

print 'Centers found by scikit-learn:'
print kmeans.cluster_centers_
#pred_label = kmeans.predict(X)
#kmeans_display(X, pred_label)


# Linear regression on one-dimensional data using a closed form solution.
# Please make sure matplotlib is included in the conda_dependencies.yml file.

import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plot
fig = plot.figure()

# load data
X = []
Y = []
for line in open('data.csv'):
    x, y = line.split(',')
    X.append(float(x))
    Y.append(float(y))

# turn them into numpy arrays so we can apply matrix operations
X = np.array(X)
Y = np.array(Y)

# this is the common denominator
denominator = X.dot(X) - X.mean() * X.sum()

# value of a
a = (X.dot(Y) - Y.mean() * X.sum()) / denominator

# value of b
b = (Y.mean() * X.dot(X) - X.mean() * X.dot(Y)) / denominator

# Yhat is simply aX + b
Yhat = a * X + b
print ("Coefficient: {0}, intercept: {1}".format(a, b))

# Plot the data and the fitted line, then save it into a png file in the output directory.
ax = fig.gca()
ax.scatter(X, Y)
ax.plot(X, Yhat, color='magenta')
fig.savefig('./outputs/lin.png')

### compute r-squared ###
# residual error of the prediction
d1 = Y - Yhat
# intrinsic error to mean
d2 = Y - Y.mean()

# if r2 is 1 (i.e., d1 is 0), this is a perfect model with no errors.
# if r2 is 0 (i.e., d1 is the same as d2), this is a useless model as it is just the same as predicting mean.
# if r2 is less than 0 (i.e., d1 is larger that d2), you are doing worse than predicting mean!!
r2 = 1 - d1.dot(d1) / d2.dot(d2)
print ("R-squared: {}.".format(r2))


#!env python
# Fast Hadamard Transform
import numpy as np
import scipy.misc
import scipy.ndimage
import matplotlib
import matplotlib.pyplot as plt

def _power_of_two(sz):
    n = 0
    while 2**n < sz:
        n += 1
    return n

def fhtpad(x):
    u'''pad 1-d array x to the minimal power-of-two'''
    x = x.ravel()
    n = _power_of_two(x.size)
    if x.size < 2**n:
        tmp = np.zeros(2**n)
        tmp[:x.size] = x
        x = tmp
    return x

def fht(x, unitary=False):
    u'''Fast Hadamard Transform of 2^n size array x.'''
    x = x.ravel()
    n = _power_of_two(x.size)
    assert x.size == 2**n
    t0, t1 = np.array(x), np.zeros_like(x)
    for step in [2**(n - i - 1) for i in range(n)]:
        skip = step*2
        for j in range(0, t0.size, skip):
            t1[j       :j + step] = t0[j:j + step] + t0[j + step:j + skip]
            t1[j + step:j + skip] = t0[j:j + step] - t0[j + step:j + skip]
        t0, t1 = t1, t0
    if unitary:
        return t0/(np.sqrt(2.0)**n)
    else:
        return t0

def ifht(x, unitary=False):
    u'''Inverse Fast Hadamard Transform of 2^n size array x.'''
    if unitary:
        # in unitary mode, fht(x) == ift(x)
        return fht(x, True)
    else:
        # in non-unitary mode, ifht(fht(x)) == x but not fht(x) != ift(x).
        n = _power_of_two(x.size)
        return fht(x)/2.0**n

def fht2(x, unitary=False):
    u'''2D Fast Hadamard Transform'''
    x = np.array([fht(row, unitary) for row in x])
    x = np.array([fht(col, unitary) for col in x.T])
    return x

def ifht2(x, unitary=False):
    u'''2D Inverse Fast Hadamard Transform'''
    x = np.array([ifht(row, unitary) for row in x])
    x = np.array([ifht(col, unitary) for col in x.T])
    return x

if __name__=='__main__':

    # non-unitary
    arr = np.array([1, 0, 1, 0, 0, 1, 1], np.float32) # size=7
    arr_ht = fht(fhtpad(arr))
    arr_ht_ht = fht(arr_ht)
    arr_ht_iht = ifht(arr_ht)
    print 'Non-unitary..'
    print 'x         :', arr
    print 'HT(x)     :', arr_ht
    print 'HT(HT(x)) :', arr_ht_ht
    print 'IHT(HT(x)):', arr_ht_iht
    assert np.allclose(fhtpad(arr), arr_ht_iht)

    # unitary
    arr = np.array([1, 0, 1, 0, 0, 1, 1, 0], np.float32) # size=8
    arr_ht = fht(arr, True)
    arr_ht_ht = fht(arr_ht, True)
    arr_ht_iht = ifht(arr_ht, True)
    print 'Unitary..'
    print 'x         :', arr
    print 'HT(x)     :', arr_ht
    print 'HT(HT(x)) :', arr_ht_ht
    print 'IHT(HT(x)):', arr_ht_iht
    assert np.allclose(arr, arr_ht_iht)

    # 2D
    img = scipy.misc.lena()
    img = scipy.ndimage.zoom(img, 1.0/8)
    matplotlib.rc('font', size=9)
    fig, axs = plt.subplots(4, 3, figsize=(12, 9))

    img_ht = fht2(img)
    img_ht_iht = ifht2(img_ht)
    assert np.allclose(img, img_ht_iht)

    ax = axs[0, 0]; ax.imshow(img, cmap='gray'); ax.set_title('org')
    ax = axs[0, 1]; ax.imshow(img_ht); ax.set_title('HT(img)')
    ax = axs[0, 2]; ax.imshow(img_ht_iht, cmap='gray'); ax.set_title('IHT(HT(img))')
    ax = axs[1, 0]; ax.hist(img.ravel(), bins=50, edgecolor='none'); ax.set_title('org')
    ax = axs[1, 1]; ax.hist(img_ht.ravel(), bins=50, edgecolor='none'); ax.set_title('HT(img)')
    fig.delaxes(axs[1, 2])

    img_u_ht = fht2(img, True)
    img_u_ht_iht = ifht2(img_u_ht, True)
    assert np.allclose(img, img_u_ht_iht)

    ax = axs[2, 0]; ax.imshow(img, cmap='gray'); ax.set_title('org')
    ax = axs[2, 1]; ax.imshow(img_u_ht); ax.set_title('uHT(img)')
    ax = axs[2, 2]; ax.imshow(img_u_ht_iht, cmap='gray'); ax.set_title('uIHT(uHT(img))')
    ax = axs[3, 0]; ax.hist(img.ravel(), bins=50, edgecolor='none'); ax.set_title('org')
    ax = axs[3, 1]; ax.hist(img_u_ht.ravel(), bins=50, edgecolor='none'); ax.set_title('uHT(img)')
    fig.delaxes(axs[3, 2])

    fig.subplots_adjust(hspace=0.4)
    fig.suptitle('Fast Hadamard Transform of 2D image', fontsize=12)
    plt.show()


import numpy as np

def doolittle(A, n, b):
    U = np.zeros((n,n))
    L = np.eye(n, dtype=float)
    diag=1
    Z = np.zeros((n,1), float)
    X = np.zeros((n,1), float)

#    L,U = inicializa(n,0)
    for k in range(n):
        suma1 = 0
        for p in range(0,k):
            suma1 += L[k][p]*U[p][k]
        U[k][k] = A[k][k]-suma1
        for i in range(k+1,n):
            suma2 = 0
            for p in range(k):
                suma2 += L[i][p]*U[p][k]
            L[i][k] = (A[i][k]-suma2)/float(U[k][k])
        for j in range(k+1,n):
            suma3 = 0
            for p in range(k):
                suma3 += L[k][p]*U[p][j]
            U[k][j]= (A[k][j]-suma3)/float(L[k][k])
        #imprimir L  U y k etapa
        print("Etapa: ", k)
        print("L: ")
        print(L)
        print("U: ")
        print(U)

    Lb = np.concatenate([L, b], axis=1)
    for i in range(0,n):
        diag = diag*U[i][i]
    if(diag != 0):
        for i in range(0, n):
            suma = 0
            for k in range(0, n):
                suma = suma+Lb[i][k]*Z[k][0]
            Z[i][0] = (Lb[i][n] - suma)/Lb[i][i]
        Uz = np.concatenate([U, Z], axis = 1)
        for i in range(n-1,-1, -1):
            suma = 0
            for k in range(0, n):
                suma = suma+Uz[i][k]*X[k][0]
            X[i][0] = (Uz[i][n] - suma)/Uz[i][i]
    else:
        print("El sistema no tiene solucion o tiene infinitas soluciones pues det = 0")
    print("FInal -------------------------------------------")
    print("L:")
    print(L)
    print("=================================================")
    print("U:")
    print(U)
    print("X: ")
    print(X)
    print("Z: ")
    print(Z)

    return L,U

a = [[4, -1, 0, 3],
     [1, 15.5, 3, 8],
     [0, -1.3, -4, 1.1],
     [14, 5, -2, 30]]
n = len(a)
b = np.array([[1],[1],[1],[1]])
print(n)
doolittle(a, n, b)


# coding: utf-8

# # 6. Regression (Linear and Nonlinear)
#
#

# In[2]:

import numpy as np
from matplotlib import pyplot as plt
get_ipython().magic('matplotlib inline')

# ## Linear regression with scipy.stats.linregress
# Check the documentation
# http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.linregress.html
#
# `scipy.stats.linregress(x, y=None)`
#
# Calculate a linear least-squares regression for two sets of measurements.
#
# Parameters:
# * x, y : array_like
# Two sets of measurements. Both arrays should have the same length. If only x is given (and y=None), then it must be a two-dimensional array where one dimension has length 2. The two sets of measurements are then found by splitting the array along the length-2 dimension.
#
# Returns:
#
# * slope : float
#   slope of the regression line
# * intercept : float
#   intercept of the regression line
# * rvalue : float
#   correlation coefficient
# * pvalue : float
#   two-sided p-value for a hypothesis test whose null hypothesis is that the slope is zero.
# * stderr : float
#   Standard error of the estimate

# In[4]:

import scipy.stats
x_data = np.array([0.5, 1.0, 2.0, 3.0,  5.0, 7.5])
y_data = np.array([1.2, 2.8, 5.2, 7.2, 10.6, 20.0])
slope, intercept, rvalue, pvalue, stderr = scipy.stats.linregress(x_data, y_data)
print(slope, intercept, rvalue, pvalue, stderr)
print("Slope: {}".format(slope))
print("Intercept: {}".format(intercept))
print("Coefficient of determination (r squared): {}".format(rvalue*rvalue))
print("p-value (probability that the slope is zero): {}".format(pvalue))
print("Standard error in slope: {}".format(stderr))

plt.plot(x_data, y_data, 'o')
plt.plot(x_data, x_data*slope+intercept)
plt.show()

# Notice that the documentation has a "see also" section that points to optimize.curve_fit. Let's check that out next

# ## Nonlinear regression with scipy.optimize.curve_fit
#
# `scipy.optimize.curve_fit(f, xdata, ydata, p0=None, sigma=None, absolute_sigma=False, check_finite=True, bounds=(-inf, inf), method=None, **kwargs)`
#
# This is quite a powerful function, giving access to several methods, bounds on the parameters, rescaling options, etc., so it's worth reading the documentation:
# http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html
#
# However, for a simple case it's quite easy to use.
# We define a function `f(x)` such that `ydata = f(xdata, *params) + eps` and it will optimize the parameters `params` to minimize the square of the errors `eps`, i.e. make the function `f` fit the data (`xdata, ydata`).
#

# For the simplest example, we can use it to fit a linear function like we did above. (Though if we *did* want to fit a linear funciton, we'd probably use the method above).

# In[5]:

import scipy.optimize
def linear_function(x, slope, intercept):
    return x*slope + intercept

x_data = np.array([0.5, 1.0, 2.0, 3.0,  5.0, 7.5])
y_data = np.array([1.2, 2.8, 5.2, 7.2, 10.6, 20.0])

optimal_parameters, covariance = scipy.optimize.curve_fit(linear_function, x_data, y_data)
# That's it! In this exampe we didn't even need to give it a starting guess!
print("Slope: {}".format(optimal_parameters[0]))
print("Intercept: {}".format(optimal_parameters[1]))

# Use the covariance matrix to find the standard errors.
# The diagonals give the variance, so...
parameter_errors = np.sqrt(np.diag(covariance))
print("Slope: {} +/- {} (1 st. dev.)".format(optimal_parameters[0],parameter_errors[0]))
print("Intercept: {} +/- {} (1 st. dev.)".format(optimal_parameters[1],parameter_errors[1]))

plt.plot(x_data, y_data, 'o')
plt.plot(x_data, linear_function(x_data, *optimal_parameters))
plt.show()

# Now we will try a more complicated function with three parameters, $a, b, c$:
#
# $$y = f(x) = \frac{a x^2}{(1 + b x + c x^2)}$$
#
# I generated some pretend data by using the function with $(a,b,c) = (20, 0.5, 20)$ then adding some random noise and tweaking one of the points slightly. We'll see if we can retrieve the paramaters by fitting the function to my pretend data.
#

# In[7]:

def complicated_function(x, a, b, c):
    "A more complicated function with 3 parameters"
    return a*x*x / (1 + b*x + c*x*x)

# These data were generated by adding random noise to the original function like this:
#x_data = np.linspace(0,1,11)
#y_data = complicated_function(x,20,0.5,20)*(1+np.random.normal(scale=0.05,size=x.size))+np.random.normal(scale=0.05,size=x.size)
# (then manually tweaked a bit)
x_data = np.array([ 0. ,  0.1,  0.2,  0.3,  0.4,  0.5,  0.6,  0.7,  0.8,  0.9,  1. ])
y_data = np.array([ 0.01717205,  0.1005844 ,  0.43536816,  0.59862244,  0.76359374,
        0.77385322,  0.89904593,  0.86883376,  0.87039673,  .95,
        0.95870025])

# Fit the parameters to the imperfect x_data and y_data
optimal_parameters, covariance = scipy.optimize.curve_fit(complicated_function,
                                                          x_data,
                                                          y_data)
def report(optimal_parameters, covariance):
    "Make this a function so we can reuse it in cells below"
    parameter_errors = np.sqrt(np.diag(covariance))
    for i in range(len(optimal_parameters)):
        print("Parameter {}: {} +/- {} (1 st. dev.)".format(i,
                                                            optimal_parameters[i],
                                                            parameter_errors[i]))

    # Plot the data
    plt.plot(x_data, y_data, 'o', label='data')

    # Make a new x array with 50 points for smoother lines
    x_many_points = np.linspace(x_data.min(),x_data.max(),50)
    # Plot the fitted curve
    plt.plot(x_many_points, complicated_function(x_many_points, *optimal_parameters), label='fitted')
    # Plot the original curve used to generate the data in the first place
    plt.plot(x_many_points, complicated_function(x_many_points,20,0.5,20), ':',label='original')
    # Add the legend, in the "best" location to avoid hiding the data
    plt.legend(loc='best')
    plt.show()

report(optimal_parameters, covariance)

# If we want to enforce all the parameters are positive, we can supply a `bounds` parameter to the `scipy.optimize.curve_fit` function. We use `numpy.inf` for $\infty$.

# In[9]:

# Set the bounds on all the parameters to be 0 and infinity.
optimal_parameters, covariance = scipy.optimize.curve_fit(complicated_function,
                                                          x_data,
                                                          y_data,
                                                         bounds = (0, np.inf)) # BOUNDS!
report(optimal_parameters, covariance)

# We notice the uncertainty on parameter 2 is bigger than parameter 2, i.e. it's possible, given the data, that it is zero. This suggests we can simplify our model and still fit the given data.
#
# One option would just be to enforce the $b$ parameter to be zero (or very close to zero) by specifying different bounds for each parameter:

# In[10]:

# Set the lower and upper bounds on each parameter separately
bounds = ([0,0,0], [np.inf, 1e-6, np.inf])
optimal_parameters, covariance = scipy.optimize.curve_fit(complicated_function,
                                                          x_data,
                                                          y_data,
                                                         bounds = bounds)
report(optimal_parameters, covariance)

# But notice it assigns a big uncertainty to $b$ even though we know (or are assuming) that it is zero. This then changes the uncertainty in $a$ and $c$. It is better to define a new function with only 2 parameters and fit that:

# In[11]:

def simplified_function(x, a, c):
    "A slightly simplified function with only 2 parameters: a and c"
    return a*x*x / (1 + c*x*x)
optimal_parameters, covariance = scipy.optimize.curve_fit(simplified_function,
                                                          x_data,
                                                          y_data)
# Can't reuse the 'report' function exactly, so do modify slightly
parameter_errors = np.sqrt(np.diag(covariance))
for i in range(len(optimal_parameters)):
    print("Parameter {}: {} +/- {} (1 st. dev.)".format(i,
                                                        optimal_parameters[i],
                                                        parameter_errors[i]))
plt.plot(x_data, y_data, 'o', label='data')
x_many_points = np.linspace(x_data.min(),x_data.max(),50)
plt.plot(x_many_points, simplified_function(x_many_points, *optimal_parameters), label='fitted')
plt.plot(x_many_points, complicated_function(x_many_points,20,0.5,20), ':',label='original')
plt.legend(loc='best')
plt.show()

# The current data are not sufficient to tell the two-parameter model and three-paramaeter models apart.
#
# Note that the function $f(x)$ could be a function of several variables, i.e. $x$ could be a vector, and `x_data` would be a 2-dimensional array.

# In[ ]:


import numpy as np

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

def fib_dp(n):
    if n < 2:
        return n
    solution = [0] * (n+1)
    solution[0] = 0
    solution[1] = 1
    for i in range(2,n+1):
        solution[i] = solution[i-1] + solution[i-2]

    return solution[-1]

def job_selection_w_recover(calendar):
    size = len(calendar)
    j = [0] * size
    s = [False] * size
    j[0] = calendar[0]
    s[0] = True
    j[1] = max(calendar[0], calendar[1])
    s[1] = calendar[1] > calendar[0]

    for i in range(2, size):
        with_last = j[i-2] + calendar[i]
        with_out_last = j[i-1]
        j[i] = max(with_last, with_out_last)
        s[i] = with_last > with_out_last

    days_worked = job_selection_recover(s)
    return days_worked, j[-1]

def job_selection_recover(job_solution):
    a = []
    i = len(job_solution) - 1

    while i >= 0:
        if job_solution[i]:
            a.append(i)
            i -= 2
        else:
            i -= 1
    return a

def job_selection_no_recover(calendar):
    size = len(calendar)
    j = [0] * size
    days_worked = [None] * size  # CAN ALSO MAINTAIN THE DAYS WORKED TO NOT REQUIRE THE RECOVERY (LIKE CHANGE MAKING)
    j[0] = calendar[0]
    days_worked[0] = [0]
    j[1] = max(calendar[0], calendar[1])
    days_worked[1] = [1 if calendar[1] > calendar[0] else 0]

    for i in range(2, size):
        with_last = j[i-2] + calendar[i]
        with_out_last = j[i-1]
        j[i] = max(with_last, with_out_last)
        if with_last > with_out_last:
            days_worked[i] = days_worked[i-2].copy()
            days_worked[i].append(i)
        else:
            days_worked[i] = days_worked[i-1].copy()

    return days_worked[-1], j[-1]

def knapsack(weights, values, capacity):
    solution = np.zeros((len(weights), capacity+1))
    # INITIALIZE THE FIRST ITEM
    for j in range(capacity+1):
        solution[0, j] = values[0] if j >= weights[0] else 0

    for i in range(1, len(weights)):
        for j in range(capacity+1):
            if j - weights[i] >= 0:  # IF THIS ITEM CAN FIT
                with_item = solution[i-1, j-weights[i]] + values[i]
            else:
                with_item = -1

            without_item = solution[i-1,j]
            solution[i, j] = max(without_item, with_item)

    return solution

def knapsack_recovery(solution, weights, values):
    rows, cols = solution.shape

    curr_row = rows - 1
    curr_col = cols - 1

    result = []

    curr_val = solution[curr_row, curr_col]

    while curr_val > 0:
        if curr_row == 0:
            result.append(curr_row)
            break
        elif curr_val != solution[curr_row - 1, curr_col]:
            result.append(curr_row)
            curr_val = curr_val - values[curr_row]
            curr_col = curr_col - weights[curr_row]
            curr_row -= 1
        else:
            curr_row -= 1

    return result

if __name__ == "__main__":
    # FIB DEMO:
    # for i in range(10):
    #     print(fib(i))
    # for i in range(50):
    #     print(fib_dp(i))

# JOB SELECTION DEMO
#     p = [15, 46, 43, 51, 92, 72, 61, 41, 39, 40, 82, 79, 42, 51]
#     days_worked, dollars = job_selection_no_recover(p)
#
#     print(f"By Working days: {days_worked} you can earn {dollars}")

    #
    w = [10,4,2,6,7,4,1,3]
    v = [1,1,5,6,7,2,4,1]
    c = 13
    solution = knapsack(w,v,c)
    print(solution)
    kp_solution = knapsack_recovery(solution, w, v)
    print(kp_solution)

    w = [5,4,1]
    v = [150, 100, 10]
    c = 10
    solution = knapsack(w,v,c)

    print(solution)
    kp_solution = knapsack_recovery(solution, w, v)

    t = 0
    for val in kp_solution:
        t += v[val]

    print(f"Total Value: {t}  but solution says: {solution[-1,-1]}")
    print(kp_solution)


from math import pow, log
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("darkgrid", {'xtick.top': False})
from matplotlib import rcParams
rcParams['pdf.fonttype'] = 42
rcParams['ps.fonttype'] = 42

x_range = np.arange(0,11)
y_exp = None
y_log = None

def exp_data(x):
    y = []
    for _ in x:
        tmp_ans = pow(2,_)
        y.append(tmp_ans)
    return y

def log_data(x):
    y = []
    for _ in x:
        if _ <= 0.0:
            y.append(float(_))
            continue
        tmp_ans = log(_,2)*300.0
        y.append(tmp_ans)
    return y

def SimpleLine(x,y):
        ax = plt.subplot(111)
        plt.plot(x,y, 'b--')
        #plt.bar(x,y, ecolor='black', capsize=4.0, align='center', edgecolor='black')
        y_label = "Y Data"
        plt.ylabel(y_label, fontsize=15)
        plt.yticks(fontsize=15)
        x_label = "X Range"
        plt.xlabel(x_label, fontsize=15)
        plt.xticks(x, [label for label in x], fontsize=15)
        # Personal preference (only horizontal lines across the plot)
        # comment-out to have squared (grid) pattern
        plt.grid(axis="x")
        # Show ticks on the left and bottom spines
        ax.yaxis.set_ticks_position('left')
        ax.xaxis.set_ticks_position('bottom')
        # Remove ticks on the right and top spine (they shouldn't be required, uncommented if needed)
        # ax.spines['right'].set_visible(False)
        # ax.spines['top'].set_visible(False)
        plt.tight_layout()
        # For the highest quality plot I create .png files using 600 dpi
        # This kind of high quality plotting is required for thesis or journal papers, rather than conferences or reports
        # As low as 200 dpi is perfect for visualising, however for presentations and papers it shouldn't be lower than 300 dpi
        plt.savefig("SimpleLine.png", dpi=600)
        plt.show()
        plt.close()

def SimpleMultiLine(value_list):
    ax = plt.subplot(111)
    colours = ["blue", "red"]
    markers = ["o", "x"]
    for i, (x,y) in enumerate(value_list):
        plt.plot(x,y, color=colours[i], marker=markers[i])
    y_label = "Y Data"
    plt.ylabel(y_label, fontsize=15)
    plt.yticks(fontsize=15)
    x_label = "X Range"
    plt.xlabel(x_label, fontsize=15)
    plt.xticks(x, [label for label in x], fontsize=15)
    plt.grid(axis="x")
    # Show ticks on the left and bottom spines
    ax.yaxis.set_ticks_position('left')
    ax.xaxis.set_ticks_position('bottom')
    plt.tight_layout()
    plt.savefig("SimpleMultiLine.png", dpi=600)
    plt.show()
    plt.close()
y_exp = exp_data(x_range)
y_log = log_data(x_range)

SimpleLine(x_range,y_log)
SimpleMultiLine([(x_range,y_exp),(x_range,y_log)])


# -*- coding: utf-8 -*-
"""
Created on Thu Sep  2 11:13:40 2021

@author: oiseth
"""

import numpy as np # Import numpy
from matplotlib import pyplot as plt # pyplot module for plotting

dt = 0.01 # Time step
t = np.arange(0,10.01,dt) # time vector
x = np.zeros(t.shape) # Initialize the x array
x[t<5] = 1.0 # Set the value of x to one for t<5
# Plot waveform
plt.figure()
plt.plot(np.hstack((t-20.0,t-10.0, t, t+10.0)),np.hstack((x,x,x,x))); # Plot four periods
plt.plot(t,x); #Plot one period
plt.ylim(-2, 2);
plt.xlim(-20,20);
plt.grid();
plt.xlabel('$t$');
plt.ylabel('$X(t)$');

nterms = 10 # Number of Fourier coefficeints
T = np.max(t) # The period of the waveform
a0 = 1/T*np.trapz(x,t) # Mean value
ak = np.zeros((nterms))
bk = np.zeros((nterms))
for k in range(nterms): # Integrate for all terms
    ak[k] = 1/T*np.trapz(x*np.cos(2.0*np.pi*(k+1.0)*t/T),t)
    bk[k] = 1/T*np.trapz(x*np.sin(2.0*np.pi*(k+1.0)*t/T),t)

# Plot Fourier coeffecients
fig, axs = plt.subplots(nrows=1, ncols=2, constrained_layout=True)

ax1 = axs[0]
ax1.plot(np.arange(1,nterms+1),ak)
ax1.set_ylim(-1, 1)
ax1.grid()
ax1.set_ylabel('$a_k$');
ax1.set_xlabel('$k$');

ax2 = axs[1]
ax2.plot(np.arange(1,nterms+1),bk)
ax2.set_ylim(-1, 1)
ax2.grid()
ax2.set_ylabel('$b_k$');
ax2.set_xlabel('$k$');
#%%
# Plot Fourier series approximation
tp  = np.linspace(-20,20,1000)
X_Fourier = np.ones(tp.shape[0])*a0
for k in range(nterms):
    X_Fourier = X_Fourier + 2.0*(ak[k]*np.cos(2.0*np.pi*(k+1.0)*tp/T) + bk[k]*np.sin(2.0*np.pi*(k+1.0)*tp/T))

plt.figure(figsize=(8,4))
plt.plot(np.hstack((t-20.0,t-10.0, t, t+10.0)),np.hstack((x,x,x,x))); # Plot four periods
plt.plot(tp,X_Fourier, label=('Fourier approximation Nterms='+str(nterms)));
plt.ylim(-2, 2)
plt.xlim(-20,20)
plt.grid()
plt.xlabel('$t$')
plt.ylabel('$X(t)$')
plt.legend();


#!/usr/bin/env python3

"""

https://tutorial.math.lamar.edu/classes/de/IntroSecondOrder.aspx

2nd order constant homogenous differential equation are equations of the form

ay'' + by' + cy = 0

The general solution is of the form
y(t) = c1*exp(r1*t) + c2*exp(r2*t)

To solve for r1 and r2, we can treat the equation as a quadratic equation where

r1 = [-b + sqrt(b^2 - 4ac)] / 2a
r2 = [-b - sqrt(b^2 - 4ac)] / 2a

If there is an initial condition we can solve for the constant c1 and c2

W = [[y1, y2], [y1', y2']]
c1 = [[y0, y2], [y0', y2']] / W
c2 = [[y1, y0], [y1', y0']] / W

W is called the wronskian and determines if there is a solution
y1(t) = exp(r1*t)
y2(t) = exp(r2*t)

Since we have the form of an exponential function, derivative is
y1'(t) = r1*y1(t)
y2'(t) = r2*y2(t)

Plug in t0 for the initial condition (t0 can be nonzero)

"""

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

def det2(m):
    return m[0][0]*m[1][1] - m[0][1]*m[1][0]

def solve(a, b, c, y0=None, y0p=None, t0=0):
    d = cmath.sqrt(b*b - 4*a*c)
    r1 = (-b + d) / (2*a)
    r2 = (-b - d) / (2*a)
    if y0 == None or y0p == None:
        return [r1, r2]

    y1 = cmath.exp(r1*t0)
    y2 = cmath.exp(r2*t0)
    y1p = r1*y1
    y2p = r2*y2
    W = det2([[y1, y2], [y1p, y2p]])
    if W == 0:
        return [r1, r2, None, None]

    c1 = det2([[y0, y2], [y0p, y2p]]) / W
    c2 = det2([[y1, y0], [y1p, y0p]]) / W
    return [r1, r2, c1, c2]

def plot(name, a, b, c, y0, y0p, t0):
    r = solve(a, b, c, y0, y0p, t0)
    assert(len(r) == 4)

    f = lambda t: abs(r[2]*cmath.exp(r[0]*t) + r[3]*cmath.exp(r[1]*t))
    ts = np.linspace(0, 10, 100)
    ys = np.array([f(t) for t in ts])
    plt.clf()
    plt.plot(ts, ys, label="a={} b={} c={} y(t0)={} y'(t0)={} t0={}\nr0={}\nr1={}\nc1={}\nc2={}".
            format(a, b, c, y0, y0p, t0, r[0], r[1], r[2], r[3]))
    plt.legend()
    plt.savefig(name)

plot("solution_1.png", 1, 11, 24, 0, -7, 0)
plot("solution_2.png", 1, 3, -10, 4, -2, 0)


"""Compute confidence intervals for normal data.

Using Null Hypothesis Significance Testing (NHST) framework as
context:
A (1 - alpha) confidence interval is an interval statistic
centered at some test statistic which will contain the null
hypothesis value 100*(1 - alpha)% of the time.
"""
import scipy.stats
import numpy as np

def z_ci_for_mean(samples, true_var: float, confidence: float = 0.95):
    """Confidence interval for the sample mean.

    Assumptions:
        i.i.d. x_1, ..., x_n ~ N(mu, sigma^2)
    Where:
        mu is unknown;
        sigma^2 is known and provided by the user (`true_var`).

    Confidence = 1 - (Type I Error).
    """
    assert 0 <= confidence <= 1.0

    n = len(samples)
    sample_mean = np.mean(samples)

    crit_point = scipy.stats.norm.isf(0.5 * (1.0 - confidence))

    dist = crit_point * np.sqrt(true_var / n)

    lower_bound = sample_mean - dist
    upper_bound = sample_mean + dist

    return (lower_bound, upper_bound)

def t_ci_for_mean(samples, confidence: float = 0.95):
    """Confidence interval for the sample mean.

    Assumptions:
        i.i.d. x_1, ..., x_n ~ N(mu, sigma^2)
    Where:
        mu and sigma^2 is unknown.

    Confidence = 1 - (Type I Error).
    """
    assert 0 <= confidence <= 1.0

    n = len(samples)
    sample_mean = np.mean(samples)
    sample_var = np.var(samples, ddof=1)

    crit_point = scipy.stats.t(n - 1).isf(0.5 * (1.0 - confidence))

    dist = crit_point * np.sqrt(sample_var / n)

    lower_bound = sample_mean - dist
    upper_bound = sample_mean + dist

    return (lower_bound, upper_bound)

def chi_square_ci_for_variance(samples, confidence: float = 0.95):
    """Confidence interval for the sample variance.

    Assumptions:
        i.i.d. x_1, ..., x_n ~ N(mu, sigma^2)
    Where:
        mu and sigma^2 is unknown.

    Confidence = 1 - (Type I Error).
    """
    assert 0 <= confidence <= 1.0

    n = len(samples)
    sample_var = np.var(samples, ddof=n - 1)
    half_err_type_1_rate = 0.5 * (1.0 - confidence)

    dist = scipy.stats.chi2(n - 1)

    crit_point_low = dist.isf(half_err_type_1_rate)
    crit_point_upper = dist.ppf(half_err_type_1_rate)

    lower_bound = sample_var / crit_point_low
    upper_bound = sample_var / crit_point_upper

    return (lower_bound, upper_bound)

def _test():
    true_var = 64
    n_samples = 95
    true_mean = 3
    samples = true_mean + np.sqrt(true_var) * np.random.randn(n_samples)
    print(z_ci_for_mean(samples, true_var))
    print(t_ci_for_mean(samples))
    print(chi_square_ci_for_variance(samples))

if __name__ == "__main__":
    _test()


import numpy as np

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

def sigmoid_d(z):
    return np.exp(-z) / (np.square((np.exp(-z) + 1)))

if __name__ == "__main__":
    print('Sigmoid Tests')
    testVector = np.matrix("1 0 -1")

    for i in range(2, 5):
        epsilon = 10 ** (-i) # Testing 0.01 ... 0.0001
        print("Epsilon: ", epsilon)

        testDerivative = (sigmoid(testVector + epsilon) - sigmoid(testVector)) / epsilon
        realDerivative = sigmoid_d(testVector)
        print(realDerivative - testDerivative)