"""
This example approximates pi numerically using a Monte Carlo estimator:
I = (4/N) SUM_{i=1}^{N} h(x^(i), y^(i)) where h() is the indicator
function h(x,y) = {1 if x^2+y^2 <= 1 and 0 otherwise} and the tupels
(x^(i), y^(i)) are drawn from the uniform distribution U([0,1]x[0,1])
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
import math
from math import log10, floor
from matplotlib.ticker import MaxNLocator

rc('font', **{'size':12, 'family':'serif', 'serif':['Computer Modern Roman']})
rc('text', usetex=True)

# Parameters for simulation
NUM_RUNS = 10
NUM_DRAW_STEPS = 5

STEP_MULTIPLIER = 10
STEP_START = 100

PRECISION = 6

PATH_FIG = 'pi_monte_carlo.pdf'
PATH_DATA = 'pi_monte_carlo.txt'

def round_sig(x, sig):
	return round(x, sig-int(floor(log10(abs(x))))-1)

def indicator_function(x, y):
	if (np.power(x, 2) + np.power(y, 2)) <= 1:
		return 1
	else:
		return 0

def approximation(num_draws):
	"""
	Approximation of pi with (num_draws) number of draws
	"""
	samples = np.random.uniform(-1, 1, (num_draws, 2))

	draws_in_circle = 0

	for i in range(0, num_draws):
		draws_in_circle += indicator_function(samples[i,0], samples[i,1])

	return 4*(draws_in_circle/num_draws)

def simulation():
	"""
	The approximation is run (NUM_RUNS)x(NUM_DRAW_STEPS)
	"""
	pi_approximations = np.zeros((NUM_DRAW_STEPS, NUM_RUNS))

	num_draws = STEP_START

	for i in range(0, NUM_DRAW_STEPS):
		for j in range(0, NUM_RUNS):
			pi_approximations[i,j] = approximation(num_draws)

		num_draws *= STEP_MULTIPLIER

	return pi_approximations

def visualization(pi_approximations):
	"""
	Plotting the expected values of the simulation
	"""
	fig, ax = plt.subplots()

	labels = [r'$10^2$', r'$10^3$', r'$10^4$', r'$10^5$', r'$10^6$']
	x = [0, 1, 2, 3, 4]

	ax.plot([-0.5, NUM_RUNS], [math.pi, math.pi], color='k', linestyle='-', linewidth=1)
	ax.plot(pi_approximations,'kx',markersize=2)

	plt.xlabel(r'Number of draws')
	plt.ylabel(r'Expected value $I(h)$')
	plt.xlim(-0.5, NUM_DRAW_STEPS-0.5)
	ax.xaxis.set_major_locator(MaxNLocator(integer=True))
	plt.xticks(x, labels)

	plt.savefig(PATH_FIG, bbox_inches='tight')
	plt.show()

def data(pi_approximations):
	"""
	Saving the data of the expected values
	"""
	data_file = open(PATH_DATA, 'w')

	for i in range(0, NUM_DRAW_STEPS):
		expecation_sum = 0

		for j in range(0, NUM_RUNS):
			roundend_expactation = round_sig(pi_approximations[i, j], PRECISION)
			expecation_sum += roundend_expactation

			data_file.write(str(roundend_expactation) + ' ')

		mean = round_sig((expecation_sum/NUM_RUNS), PRECISION)

		data_file.write('\t' + str(mean))
		data_file.write('\n')

def main():
	approximation = simulation()
	visualization(approximation)
	data(approximation)

if __name__ == "__main__":
	main()


import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
from scipy.integrate import simps

xs = [0.0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,
    5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0]
ys = [0.2,0.165,0.167,0.166,0.154,0.134,0.117,0.108,0.092,0.06,0.031,0.028,
    0.048,0.077,0.103,0.119,0.119,0.103,0.074,0.038,0.003]

plt.scatter(xs,ys)
plt.xlabel("x")
plt.ylabel("Observed PDF")
plt.show()
#This is a continuous distribution

#span x from min to max with these data points
#interpolating the data
x = np.linspace(min(xs),max(xs),1000)
y1 = interp1d(xs,ys)(x)
#interpolating xs and ys to x line

plt.scatter(xs,ys,s=30,label="Data",c="b")
plt.plot(x,y1,label="Linear(default)")
plt.legend()
plt.show()
#linear interpolating in our data
#but if we have sparse data then it is not shown correctly
#depends on data

#another type of interpolation - nearest
x = np.linspace(min(xs),max(xs),1000)
y2 = interp1d(xs,ys,kind="nearest")(x)
#works as a step function in the graph shown below
#useful for discrete function but not very useful for
#smooth changing values

plt.scatter(xs,ys,s=30,label="Data",c="b")
plt.plot(x,y1,label="Linear(default)")
plt.plot(x,y2,label="Nearest")
plt.legend()
plt.show()

#another type of interpolation - quadratic
x = np.linspace(min(xs),max(xs),1000)
y3 = interp1d(xs,ys,kind="quadratic")(x)
#works as a step function in the graph shown below
#useful for discrete function but not very useful for
#smooth changing values

plt.scatter(xs,ys,s=30,label="Data",c="b")
plt.plot(x,y1,label="Linear(default)")
plt.plot(x,y2,label="Nearest",alpha=0.3)
plt.plot(x,y3,label="Quadratic",ls="-")
plt.legend()
plt.show()
#on the changing gradient you can see the difference
#between the nearest and quadratic

#another type of interpolation - cubic
x = np.linspace(min(xs),max(xs),1000)
y4 = interp1d(xs,ys,kind="cubic")(x)

plt.scatter(xs,ys,s=30,label="Data",c="b")
plt.plot(x,y1,label="Linear(default)")
plt.plot(x,y2,label="Nearest",alpha=0.3)
plt.plot(x,y3,label="Quadratic",ls="-")
plt.plot(x,y4,label="Cubic",ls="-")
plt.legend()
plt.show()
#the more complex interpolation,the slower
#it's(difference in quadratic and cubic) going to be

from scipy.interpolate import splev,splrep

#another type of interpolation - splev
x = np.linspace(min(xs),max(xs),1000)
y5 = splev(x,splrep(xs,ys))
#if data changing quickly, using cubic spline

plt.scatter(xs,ys,s=30,label="Data",c="b")
plt.plot(x,y1,label="Linear(default)")
plt.plot(x,y2,label="Nearest",alpha=0.3)
plt.plot(x,y3,label="Quadratic",ls="-")
plt.plot(x,y4,label="Cubic",ls="-")
plt.plot(x,y5,label="Spline",ls="-",alpha=0.5,c="#0000")
plt.legend()
plt.show()

#the red changes colour as both quadratic and spline are same

#Using the interp1d we can find the probability for any x value
#Using scipy.integrate we can calculate the CDF and probability in two bounds

#scipy.integrate.trapz = low accuracy but high speed - accuracy scales as O(h)
#scipy.integrate.simps = med accuracy but very high speed - acc scales as O(h^2)
#scipy.integrate.quad = high accuracy but low speed - arbitary accuracy

def get_prob(xs,ys,a,b): #add another variable here resolution=1000
  #a,b = bounds
  #to normalize, use the below
  x_norm = np.linspace(min(xs),max(xs),1000) #add resolution here instead of no
  y_norm = interp1d(xs,ys,kind="quadratic")(x_norm)
  normalisation = simps(y_norm,x=x_norm)
  x_vals = np.linspace(a,b,1000)#add resolution here instead of number
  y_vals = interp1d(xs,ys,kind="quadratic")(x_vals) #general solution
  #could be tweaked to gain more accuracy
  return simps(y_vals,x=x_vals)/normalisation

def get_cdf(xs,ys,v):#value of cdf at
  return get_prob(xs,ys,min(xs),v) #if xs is np array -> xs.min(),upto value v

def get_sf(xs,ys,v):
  return 1 - get_cdf(xs,ys,v)#definition of the survival function
  #OR return get_prob(xs,ys,v,max(xs))

print(get_prob(xs,ys,0,10)) #simps has some issue & needs to be normalized,
#check the normalized code above and check without normalized
#another problem is of numbers added to np.linspace,try changing
#x_vals = np.linspace(a,b,100), to solve this
#add a variable to it, use of resolution can solve the problem

v1,v2 = 6,9.3
area = get_prob(xs,ys,v1,v2)

plt.scatter(xs,ys,s=30,label="Data",color="w")
plt.plot(x,y3,linestyle="-",label="Interpolation")
plt.fill_between(x,0,y3,where=(x>=v1)&(x<=v2),alpha=0.2) #fill in the values
plt.annotate(f"p = {area}.3f",(7,0.05))
plt.legend()
plt.show()

#comparing cdf
x_new = np.linspace(min(xs),max(xs),100)
cdf_new = [get_cdf(xs,ys,i) for i in x_new] #expensive way of cdf_new

cheap_cdf =y3.cumsum()/y3.sum()
plt.plot(x_new,cdf_new,label="Interpolated CDF",c="r")
plt.plot(x,cheap_cdf,label="Super cheap CDF for specific cases",c="g")
plt.ylabel("CDF")
plt.xlabel("x")
plt.legend()
plt.show()
#for smooth data you can use the cheap cdf method
#you can see the interpolated and the cheap cdf coming together


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jul 22 16:42:12 2017

Originial Author: Daniel Homola
Licence: BSD 3-clause

@author: dhingratul

Newton's Root Finding Algorithm'
"""
from scipy.optimize import newton
from sklearn.utils.testing import assert_almost_equal

def f(x):
    return 6*x**5-5*x**4-4*x**3+3*x**2

def df(x):
    return 30*x**4-20*x**3-12*x**2+6*x

def dx(f, x):
    return abs(0 - f(x))

def newtons_method(f, df, x0, thresh, print_res=False):
    delta = dx(f, x0)
    while delta > thresh:
        x0 = x0 - f(x0)/df(x0)
        delta = dx(f, x0)
    if print_res:
        print('Root is at: ', x0)
        print('f(x) at root is: ', f(x0))
    return x0

def test_against_scipy(f, df, x0s, thresh):
    for x0 in x0s:
        my_newton = newtons_method(f, df, x0, thresh)
        scipy_newton = newton(f, x0, df, tol=thresh)
        assert_almost_equal(my_newton, scipy_newton, decimal=5)
        print('Tests passed.')

# Driver Program
x0s = [0, .5, 1]
test_against_scipy(f, df, x0s, 1e-5)

for x0 in x0s:
    newtons_method(f, df, x0, 1e-10, True)


#!/usr/bin/env python
"""Program to multiply 2  matrices together. (nxm)*(mxl)"""

import numpy as np
import timeit

speed_test = True

def check_shape(A,B):
	"""Checking if it's possible to multiply these matrices together"""
	shape_A = np.shape(A)
	shape_B = np.shape(B)
	if shape_A[1] != shape_B[0]:
		raise ValueError("shapes {0} and {1} not aligned".format(shape_A,shape_B))

def matrix_mult_nested(A,B):
	"""Matrix multiplication using nested for loops"""
	check_shape(A,B)
	rows = np.shape(A)[0]
	cols = np.shape(B)[1]
	C = np.zeros((rows,cols)) #resultant matrix after multiplication
	for i in range(rows):
		for j in range(cols): #loops give [i,j] location in matrix
			for k in range(cols): #each [i,j] location is a dot-product of a row of X and a column of Y
				C[i][j] += A[i][k]*B[k][j]
	return C

def matrix_mult_list_comp(A,B):
	"""Matrix multiplication using list comprehension"""
	check_shape(A,B)
	rows = np.shape(A)[0]
	cols = np.shape(B)[1]
	C = [[np.sum([A[i][k]*B[k][j] for k in range(cols)]) for j in range(cols)] for i in range(rows)]
	return C

def matrix_mult_numpy(A,B):
	"""Matrix multiplication using numpy dot function"""
	C = np.dot(A,B)
	return C

def time_elasped(function):
	start = timer()
	function
	end = timer()
	return end-start

#Example matrices
X = np.array([[1,2],[3,4],[5,6]])
Y = np.array([[5,6],[7,8]])

print("Example Matrices")
print("X * Y = \n{0} * \n{1}".format(X,Y))
print("Nested for loop:\n", matrix_mult_nested(X,Y))
print("List comprehension:\n", matrix_mult_list_comp(X,Y))
print("Numpy dot function:\n", matrix_mult_numpy(X,Y))

if speed_test: #speed test is optional
	#Large Matrix
	np.random.seed(1)
	large_X =  np.random.rand(20,20)
	large_X_inv = np.linalg.inv(large_X)
	print("\nSpeed test")

	#I only made these function because they don't have any arguments so I can use timeit on them easily
	def func1():
		return matrix_mult_nested(large_X,large_X_inv)
	def func2():
		return matrix_mult_list_comp(X,Y)
	def func3():
		return matrix_mult_numpy(X,Y)

	t_nested = np.min(timeit.repeat("func1()", setup="from __main__ import func1",repeat=5,number= 100))/100
	t_list_comp = np.min(timeit.repeat("func2()", setup="from __main__ import func2",repeat=5,number= 100))/100
	t_numpy = np.min(timeit.repeat("func3()", setup="from __main__ import func3",repeat=5,number= 100))/100
	print("Nested for loop:")
	print("{0:.3e} seconds".format(t_nested))
	print("List comprehension:")
	print("{0:.3e} seconds".format(t_list_comp))
	print("Numpy dot function:")
	print("{0:.3e} seconds".format(t_numpy))

#Code graveyard
	# print(timeit.repeat("func2()", setup="from __main__ import func2"))
	# print(timeit.repeat("func3()", setup="from __main__ import func3")
	# t_nested = time_elasped(matrix_mult_nested(large_X,large_X_inv))
	# t_list_comp = time_elasped(matrix_mult_list_comp(large_X,large_X_inv))
	# t_numpy = time_elasped(matrix_mult_numpy(large_X,large_X_inv))
	# print("Nested for loop:\n", t_nested*1e7)
	# #np.round(matrix_mult_nested(large_X,large_X_inv)))
	# print("List comprehension:\n", t_list_comp*1e7)
	# #np.round(matrix_mult_list_comp(large_X,large_X_inv)))
	# print("Numpy dot function:\n", t_numpy*1e7)
	# #np.round(matrix_mult_numpy(large_X,large_X_inv)))


import numpy

def cos2degree(cos) -> float:
    """
    Convert cosine to degree. Support float, List[float], numpy.ndarray[float]
    :param cos:
    :return:
    """
    return numpy.arccos(cos) / numpy.pi * 180

def cos_angle(left_side: float, right_side: float, third_side: float) -> float:
    """
    Calculate cos of angle if give 3 side of triangle
    """
    return (left_side ** 2 + right_side ** 2 - third_side ** 2) / (2 * left_side * right_side)

def degree_angle(left_side: float, right_side: float, third_side: float) -> float:
    """
    Calculate degree of angle if give 3 side of triangle
    """
    return cos2degree(cos_angle(left_side, right_side, third_side))

def cos_triangle(first_side, second_side, third_side):
    """
    Calculate 3 angles's cos of triangle
    """
    first_angle = cos_angle(second_side, third_side, first_side)
    second_angle = cos_angle(first_side, third_side, second_side)
    third_angle = cos_angle(first_side, second_side, third_side)
    return first_angle, second_angle, third_angle

def degree_triangle(first_side, second_side, third_side):
    return cos2degree(cos_triangle(first_side, second_side, third_side))


#!/usr/bin/python

#############################################################
# Linear regressions: Polynomial basis functions #
# Sk. Mashfiqur Rahman #
#############################################################

import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt("crash.txt")
training = data[1::2]
test = data[0:-1:2]
training_x = np.array(training[:, 0]).reshape(len(training), 1)
training_t = np.array(training[:, 1]).reshape(len(training), 1)
test_x = np.array(test[:, 0]).reshape(len(training), 1)
test_t = np.array(test[:, 1]).reshape(len(training), 1)
Erms_training = np.zeros(20)
Erms_test = np.zeros(20)
Erms_reference_training = 1000.
best_w_training = 0
best_L_training = 0
Erms_reference_test = 1000.
best_w_test = 0
best_L_test = 0

for L in range(1,21):
    phi = training_x**range(L)
    w = np.linalg.solve(phi.T.dot(phi), phi.T.dot(training_t))
    E_training = 0.5 * np.square(np.linalg.norm(training_t - phi.dot(w)))
    Erms_training[L-1] = np.sqrt(2. * E_training / len(training))
    if Erms_training[L-1] < Erms_reference_training:
        Erms_reference_training = Erms_training[L-1]
        best_L_training = L
        best_w_training = w

    phi = test_x**range(L)
    E_test = 0.5 * np.square(np.linalg.norm(test_t - phi.dot(w)))
    Erms_test[L-1] = np.sqrt(2. * E_test / len(training))
    if Erms_test[L-1] < Erms_reference_test:
        Erms_reference_test = Erms_test[L-1]
        best_L_test = L
        best_w_test = w

print('Maximum likelihood RMS error between the actual data and the models prediction (for Training sets): \n')
print(Erms_training)
print('Maximum likelihood RMS error between the actual data and the models prediction (for Test sets): \n')
print(Erms_test)

plt.figure(figsize=(16,12))
plt.plot(Erms_training, '-o', markerfacecolor='none', color='b', label='Training')
plt.plot(Erms_test, '-o', markerfacecolor='none', color='r', label='Test')
plt.suptitle('Maximum likelihood RMS error between the actual data and the models prediction', fontsize=24)
plt.legend(fontsize=22)
plt.xlabel("M ", fontsize = 22)
plt.ylabel("Erms", fontsize = 22)
plt.show()

x = np.linspace(start=np.min(training_x), stop= np.max(training_x), num=100).reshape(100, 1)
phi = x**range(best_L_training)
y = phi.dot(best_w_training)
print('Lowest RMS L for training data:', best_L_training)

plt.figure(figsize=(16,12))
plt.plot(training_x, training_t, color='b', label='Training data')
plt.plot(x, y, color='r', label='Lowest RMS model output')
plt.suptitle('Best fit on the training set', fontsize=24)
plt.legend(fontsize=22)
plt.xlabel("time ", fontsize = 22)
plt.ylabel("acceleration", fontsize = 22)
plt.show()

x = np.linspace(start=np.min(test_x), stop=np.max(test_x), num=100).reshape(100, 1)
phi = x**range(best_L_test)
y = phi.dot(best_w_test)
print('Lowest RMS L for test data:', best_L_test)

plt.figure(figsize=(16,12))
plt.plot(test_x, test_t, color='b', label='Test data')
plt.plot(x, y, color='r', label='Lowest RMS model output')
plt.suptitle('Best fit on the test set', fontsize=24)
plt.legend(fontsize=22)
plt.xlabel("time ", fontsize = 22)
plt.ylabel("acceleration", fontsize = 22)
plt.show()


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

### Part 1 ###
# Consider a projectile object starting from (0,0) with initial velocity (300,600) m/s.
# Suppose we have a gravitational acceleration in y of -9.8 m/s.
# Suppose Q = 0.1*I, and R = 500*I, and delta t = 0.1.
# Evolve the system forward 1200 steps and keep the states and observations.

Fk = np.array([[1.,0.,.1,0.],[0.,1.,0.,.1],[0.,0.,1.,0.],[0.,0.,0.,1.]])
Q = np.eye(4)*.1
U = np.array([0.,0.,0.,-.98])
H = np.array([[1.,0.,0.,0.],[0.,1.,0.,0.]])
R = np.eye(2)*500

x_initial = np.array([0.,0.,300.,600.])

states, observations = kalman.generation(Fk,Q,U,H,R,x_initial,1250)

### Part 2 ###
# Suppose we are only able to see the observations from iteration 200 to 800.
# Plot these observations as red points and the entire projectile path as a blue curve.

plt.plot(observations[0,200:800],observations[1,200:800],'r.')
temp = np.array([x > -50 for x in states[1,:]])
plt.plot(states[0,temp],states[1,temp],'b')

### Part 3 ###
# Supposing we only have the given observations, estimate the state of the system at
# iteration 200, using the average of the measured velocities from iteration 200 to 210.
# Estimate the position of the projectile given this initial state estimate, using P = 10^6 * Q.
# Add to the plot the estimated path of the projectile as a green curve.

vel = np.array([np.mean(np.diff(observations[0,200:210])/.1),np.mean(np.diff(observations[1,200:210])/.1)])
x_est_initial = np.concatenate([observations[:,200],vel])

estimation = kalman.kalmanFilter(Fk,Q,U,H,R,x_est_initial,Q*(10**6),observations[:,200:800])

plt.plot(estimation[0,:],estimation[1,:],'g')

### Part 4 ###
# Given the final state estimate at iteration 800, iterate forward predictively to find the
# projectile's point of impact. Plot this with a yellow curve.

prediction = kalman.predict(Fk,U,estimation[:,599],500)
temp = np.array([x > -50 for x in prediction[1,:]])
plt.plot(prediction[0,temp],prediction[1,temp],'y')

### Part 4 ###
# Given the state estimate at iteration 250, rewind the system to identify the
# projectile's point of origin. Plot this with a cyan curve, and display the results.

rewound = kalman.rewind(Fk,U,estimation[:,50],300)
temp = np.array([x > -50 for x in rewound[1,:]])
plt.plot(rewound[0,temp],rewound[1,temp],'c')
plt.ylim([0,20000])


import numpy as np

from lazylog import LazyLog

class PCA(LazyLog):
    def __init__(self, dataset: np.ndarray):
        """
        :param dataset: A dataset, which is a matrix with the shape of (N x M), where:
                - N: number of samples
                - M: number of features
        """
        # Get the shape of the input data
        super().__init__()
        assert len(dataset.shape) == 2
        self._n_samples, self._n_features = dataset.shape
        self.logger.info({
            'msg': 'Shape of input data',
            'value': dataset.shape
        })

        # Change mean to zero
        self._mean = np.mean(dataset, axis=0)
        self._dataset = dataset - self._mean

        # Calculate covariance matrix of features
        self._covariance_matrix = (1 / (self._n_samples - 1)) * np.matmul(dataset.transpose(), self._dataset)
        assert self._covariance_matrix.shape == (self._n_features, self._n_features)
        self.logger.debug({
            'msg': 'Covariance matrix',
            'shape': self._covariance_matrix.shape,
            'matrix': self._covariance_matrix.tolist()
        })

        # Find eigenvectors and eigenvalues of the covariance matrix (which are the candidates of principal components)
        # Notes: technically in this context, eigenvectors are column vectors
        self._eigenvalues, self._eigenvectors = np.linalg.eig(self._covariance_matrix)
        self._component_indices = np.flip(np.argsort(self._eigenvalues))
        self._eigenvalues = self._eigenvalues[self._component_indices]
        self._eigenvectors = self._eigenvectors[:, self._component_indices]
        for i in range(self._n_features):
            self.logger.debug({
                'msg': '{}-th component'.format(i),
                'value': self._eigenvalues[i],
                'vector': self._eigenvectors[:, i].tolist()
            })
        self.logger.debug({
            'msg': 'Assert orthogonal characteristics',
            'basis': np.matmul(self._eigenvectors, self._eigenvectors.transpose()).tolist()
        })

    def project(self, vectors: np.ndarray, n_reduced_features: int) -> np.ndarray:
        """
        :param vectors: A dataset, which is a matrix with the shape of (N x M), where:
                - N: number of samples
                - M: number of features
        :param n_reduced_features: An integer, which is number of features to be reduced
        """
        # Assert shape of vectors
        assert len(vectors.shape) == 2
        assert vectors.shape[1] == self._n_features
        assert n_reduced_features < len(self._eigenvalues)
        return np.matmul(vectors - self._mean, self._eigenvectors[:, :n_reduced_features])

    def project_and_restore(self, vectors: np.ndarray, n_reduced_features: int) -> np.ndarray:
        """
        :param vectors: A dataset, which is a matrix with the shape of (N x M), where:
                - N: number of samples
                - M: number of features
        :param n_reduced_features: An integer, which is number of features to be reduced
        """
        projected = self.project(vectors, n_reduced_features)
        return np.matmul(projected, self._eigenvectors[:, :n_reduced_features].transpose()) + self._mean


# -*- coding: utf-8 -*-
"""
Example solutions for Worksheet 1

Ian Hawke
"""

def MatrixConditionCheck(A):
    import numpy as np

    MaxConditionNumber = 10 # This is absurdly low
    ConditionNumber = np.linalg.cond(A)
    if ConditionNumber > MaxConditionNumber:
        print "Condition number of matrix\n", A, "\ntoo large (bigger than", MaxConditionNumber, ").\n"

import numpy as np

print "\nQuestion 1\n"

A1 = np.array([[1,2],[3,4]])
A1T = np.transpose(A1)
A1I = np.linalg.inv(A1)
print "The matrix\n", A1, "\nhas transpose\n", A1T, "\nand inverse\n", A1I

A2 = np.array([[-3,2],[3,6]])
A2T = np.transpose(A2)
A2I = np.linalg.inv(A2)
print "The matrix\n", A2, "\nhas transpose\n", A2T, "\nand inverse\n", A2I

print "\nQuestion 2\n"

v1 = np.array([1,3,-1])
v2 = np.array([1,-2])
v3 = np.array([1,6,-3,1])

print "The vector\n", v1, "\nhas norms", np.linalg.norm(v1,1), np.linalg.norm(v1,2), np.linalg.norm(v1,np.inf)
print "The vector\n", v2, "\nhas norms", np.linalg.norm(v2,1), np.linalg.norm(v2,2), np.linalg.norm(v2,np.inf)
print "The vector\n", v3, "\nhas norms", np.linalg.norm(v3,1), np.linalg.norm(v3,2), np.linalg.norm(v3,np.inf)

print "The matrix\n", A1, "\nhas norms", np.linalg.norm(A1,1), np.linalg.norm(A1,np.inf)
print "The matrix\n", A2, "\nhas norms", np.linalg.norm(A2,1), np.linalg.norm(A2,np.inf)

print "\nQuestion 3\n"

MatrixConditionCheck(A1)
MatrixConditionCheck(A2)

print "\nQuestion 4\n"

# Bisection algorithm

tolerance = 1e-15
# Define the function
f = lambda x: np.tan(x) - np.exp(-x)
# Define the interval
x_min = 0.0
x_max = 1.0
# Values at the ends of the domain
f_min = f(x_min)
f_max = f(x_max)
assert(f_min * f_max < 0.0)
# The loop
x_c = (x_min + x_max) / 2.0
f_c = f(x_c)
iteration = 0
while ((x_max - x_min > tolerance) and (np.abs(f_c) > tolerance) and (iteration < 100)):
    iteration = iteration+1
    if f_min * f_c < 0.0:
        x_max = x_c
        f_max = f_c
    else:
        x_min = x_c
        f_min = f_c
    x_c = (x_min + x_max) / 2.0
    f_c = f(x_c)
#    print "Iteration ", iteration, " x ", x_c, " f ", f_c

print "The root is approximately ", x_c, " where f is ", f_c


import math
import numpy as np

def R_x(theta):
    '''
    rotate about x-axis
    '''
    return np.array([[1, 0, 0],
                     [0, np.cos(theta), -np.sin(theta)],
                     [0, np.sin(theta), np.cos(theta)]])

def R_y(theta):
    '''
    rotate about y-axis
    '''
    return np.array([[np.cos(theta), 0, np.sin(theta)],
                     [0, 1, 0],
                     [-np.sin(theta), 0, np.cos(theta)]])

def R_z(theta):
    '''
    rotate about z-axis
    '''
    return np.array([[np.cos(theta), -np.sin(theta), 0],
                     [np.sin(theta), np.cos(theta), 0],
                     [0, 0, 1]])

def normalize_array(arr):
    '''
    normalize a 1D array of values so the largest value is 1
    '''

    arr_nrm = (1/np.amax(arr))*arr

    return arr_nrm

def print_matrix_as_latex(mat, n_digs=3):
    '''
    print a matrix in latex form
    inputs:
        mat: matrix, n_digs: number of digits in largest value
    '''
    n_rows, n_cols = mat.shape
    mat_max = np.amax(mat) # max value in matrix
    max_pow = int(np.floor(np.log10(mat_max))) # max value power of 10
    # power of 10 to divide matrix by so largest element is in range [100,999]
    scl_pow = max_pow - (n_digs - 1)
    scl = 10**scl_pow # value to divide matrix by
    mat_scl = mat/scl

    # print in latex form
    print('10^{' + str(scl_pow) + '} \\times')
    print(r'\begin{bmatrix}')
    for i in range(n_cols):
        for j in range(n_rows):
            if j < n_rows - 1:
                print('%.0f' %  mat_scl[i, j], '& ', end='')
            else: # last row
                if i < n_rows - 1:
                    print('%.0f' %  mat_scl[i, j], '\\\ ')
                else: # last column and last row
                    print('%.0f' %  mat_scl[i, j])
    print(r'\end{bmatrix}')


import numpy as np

# CRIAÇÃO DE DOIS ARRAYS X E Y
print("\nCRIAÇÃO DE DOIS ARRAYS X E Y!!\n")
x = np.ones((2, 2))
y = np.eye(2)
print("x : \n", x)
print('y: \n', y)

# multiplicações
print('\n***MULTIPLICAÇÕES***\n')
print('multiplicação de dois arrays: \n ', x * y)
print("multiplicação com float/int:\n", x * 2) #broadcasting

# multiplicação matricial
print('\n*** MULTIPLICAÇÃO MATRICIAL***\n')
print('multiplicação matricial (np.dot): \n', np.dot(x, y))
print('multiplicação matricial (@): \n', x @ y)
print('multiplicação matricial (.dot): \n', x.dot(y))
print('\n\n')
'''
Exemplo:

Solução de um sistema de equações:
    a + 2*b = 7
    3*a - 2*b = -11

    solução analitica: (a,b) = (-1, 4)

Matricialmente, este problema te a seguinte forma:
    Ax =c, onde:
    - x = [a, b]
    - A = [[1, 2], [3, 2]]
    - c = [7, -11]
    solução numérica: x = inv(A) @ c,

'''
# Defição do problema

print('Definição do Problema!')
A = np.array ([[1, 2], [3, -2]])
c = np.array([[7], [-11]])
print('A: \n', A)
print("c: \n", c)

# solução
x = np.dot(np.linalg.inv(A), c)
# x = np.linalg.inv(A) @ c
print('(a, b):', x.ravel())


from numpy.linalg import matrix_rank
import numpy as np

from toolbox.centerCols import *

def pcaPmtk(X, K=None, method=None):
    n, d = X.shape
    if method is None:
        cost = np.array([d**3, n**3, min(n*d**2, d*n**2)])
        method = np.argmin(cost) + 1
    methodNames = ['eig(Xt X)', 'eig(X Xt)', 'SVD(X)']
    print("Using method %s" % methodNames[method - 1])
    XCenter, mu = centerCols(X)
    if K is None:
        K = matrix_rank(XCenter)
    if method == 1:
        cov_matrix = np.cov(XCenter, rowvar=False, bias=True)
        evals, evec = np.linalg.eig(cov_matrix)
        sorted_idx = np.argsort(-evals)
        evals = evals[sorted_idx]
        evec = evec[:, sorted_idx]
        B = evec[:, 0:K]
    elif method == 2:
        w = np.dot(XCenter, XCenter.T)
        evals, evec = np.linalg.eig(w)
        sorted_idx = np.argsort(-evals)
        evals = evals[sorted_idx]
        evec = evec[:, sorted_idx]
        B = np.dot(np.dot(X.T, evec), np.diag(1. / np.sqrt(evals)))
        B = B[:, 0:K]
        evals = evals / n
        r = np.linalg.matrix_rank(XCenter)
        evals[r:] = 0
    elif method == 3:
        if n > d:
            full_matrices = False
        else:
            full_matrices = True
        u, s, vh = np.linalg.svd(XCenter, full_matrices=full_matrices)
        B = vh.T[:, 0:K]
        evals = 1/n * np.square(s)
    Z = np.dot(XCenter, B)
    Xrecon = np.dot(Z, B.T) + mu
    return B, Z, evals, Xrecon, mu

if __name__ == '__main__':
    # octave
    # X = [[2, 8, 3, 4, 9]; [6, 3, 5, 8, 2]; [1, 4, 5, 9, 3]; [5, 5, 1, 2, 7]]
    # [B, Z, evals, Xrecon, mu] = pcaPmtk(X, 2, 1)
    # -0.025365  - 0.78857
    # 0.35044    0.36752
    # -0.3313    0.23639
    # -0.60956    0.36123
    # 0.62866    0.23814
    X = np.array([[2, 8, 3, 4, 9], [6, 3, 5, 8, 2], [1, 4, 5, 9, 3], [5, 5, 1, 2, 7]])
    B, Z, evals, Xrecon, mu = pcaPmtk(X, 2, 3)
    print(B, Z)


import numpy as np

#勾配を求める関数
def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)

    for i in range(x.size):
        v = x[i]

        x[i] = v + h
        f1 = f(x)

        x[i] = v - h
        f2 = f(x)

        grad[i] = (f1 - f2) / (2 * h)
        x[i] = v

    return grad

def function_2(x):
    return np.sum(x**2)

print(numerical_gradient(function_2, np.array([3.0, 4.0])))
print(numerical_gradient(function_2, np.array([0.0, 2.0])))
print(numerical_gradient(function_2, np.array([3.0, 0.0])))

#勾配降下法
def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x

    for i in range(step_num):
        x -= lr * numerical_gradient(f, x)

    return x

init_x = np.array([-3.0, 4.0])
print(gradient_descent(function_2, init_x, lr=0.1, step_num=100))
print(gradient_descent(function_2, init_x, lr=10, step_num=100))
print(gradient_descent(function_2, init_x, lr=1e-10, step_num=100))

#重要なのは微分の特性を思い出すこと
#ほんのちょっとだけxを動かしたらyがどれだけ変化するか
#傾き
#微分の数学的典型的解(x^2 > 2x, x > 1, e^x > e^x)


import numpy as np

def Heaviside(z):
    ''' Heaviside function.
        @params:
            z : float values  -- required
        @return:
            1 if z is greater or equal to 0 and 0 in other case'''
    return 1 * (z >= 0)

def d_Heaviside(z):
    return 0

def sgn(z):
    '''Sign function
        @params:
            z : float values -- required
        @return:
            1 if z is greater or equal to 0 and -1 in other case'''
    return 2 * Heaviside(z) - 1

def d_sgn(z):
    return 0

def saturation_function(z):
    ''' Saturation function:
        @params:
            z : float values -- required
        @return:
            1 if z is greater than 1, z if z is lower or equal than 1 and greater
             or equal than -1 and -1 in other case'''
    x = 1 * (z > 1)
    y = -1 * (z < -1)
    w = (abs(z) <= 1) * z
    return x + y + w

def d_saturation_function(z):
    return 1

def logistic(z, s=1):
    ''' Logistic function:
        @params:
            z : float values -- required
            s : weight  -- float values -- required
        @return:
            return the logistic function values '''
    return 1. / (1. + np.exp(-s * z))

def sigmoid(z, s=1):
    ''' Sigmoid function:
        @params:
            z : float values -- required
            s : weight -- float values -- required
        @return:
            return thte sigmoid values'''
    return s * logistic(z, s) * (1 - logistic(z, s))

def tanh(z):
    ''' Hiperbolic tangent function:
        @params:
            z : float values -- required
        @return:
            return the tanh values'''
    return 2 * logistic(z, 2) - 1

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

def relu(z, smoothed=False):
    ''' REctified Linear Units:
        It's a no lineal activation function that is defined like:
                           [ z  if z >= 0
                    f(z) = |
                           [ 0  if z < 0

        @params:
            z : float values -- required
        @return:
            return the tanh values
        '''
    if smoothed:
        return np.log(1 + np.exp(z))
    else:
        return (z > 0) * z

def d_relu(z, smoothed=False):
    if smoothed:
        return z * np.exp(z) / (1 + relu(z, smoothed))
    else:
        return (z > 0) * 1

act_func = {
            'heaviside': Heaviside,
            'sgn': sgn,
            'saturation_function': saturation_function,
            'sigmoid': logistic,
            'tanh': tanh,
            'relu': relu
}

der_act_func = {
            'heaviside': d_Heaviside,
            'sgn': d_sgn,
            'saturation_function': d_saturation_function,
            'sigmoid': sigmoid,
            'tanh': d_tanh,
            'relu': d_relu
}


import numpy as np
from matplotlib import pyplot as plt

def make_default_q_curve(Snom, Qmin, Qmax, n=3):
    """
    Compute the generator capability curve
    :param Snom: Nominal power
    :param Qmin: Minimum reactive power
    :param Qmax: Maximum reactive power
    :param n: number of points, at least 3
    :return: Array of points [(P1, Qmin1, Qmax1), (P2, Qmin2, Qmax2), ...]
    """
    assert(n > 2)

    pts = np.zeros((n, 3))
    s2 = Snom * Snom

    # Compute the intersections of the Qlimits with the natural curve
    p0_max = np.sqrt(s2 - Qmax * Qmax)
    p0_min = np.sqrt(s2 - Qmin * Qmin)
    p0 = min(p0_max, p0_min)  # pick the lower limit as the starting point for sampling

    pts[1:, 0] = np.linspace(p0, Snom, n - 1)
    pts[0, 0] = 0
    pts[0, 1] = Qmin
    pts[0, 2] = Qmax

    for i in range(1, n):
        p2 = pts[i, 0] * pts[i, 0]  # P^2
        q = np.sqrt(s2 - p2)  # point that naturally matches Q = sqrt(S^2 - P^2)

        # assign the natural point if it does not violates the limits imposes, else set the limit
        qmin = -q if -q > Qmin else Qmin
        qmax = q if q < Qmax else Qmax

        # Enforce that Qmax > Qmin
        if qmax < qmin:
            qmax = qmin
        if qmin > qmax:
            qmin = qmax

        # Assign the points
        pts[i, 1] = qmin
        pts[i, 2] = qmax

    return pts

def get_q_limits(q_points, p):
    """
    Get the reactive power limits
    :param q_points: Array of points [(P1, Qmin1, Qmax1), (P2, Qmin2, Qmax2), ...]
    :param p: active power value (or array)
    :return:
    """
    all_p = q_points[:, 0]
    all_qmin = q_points[:, 1]
    all_qmax = q_points[:, 2]

    qmin = np.interp(p, all_p, all_qmin)
    qmax = np.interp(p, all_p, all_qmax)

    return qmin, qmax

Snom = 650
points = make_default_q_curve(Snom=Snom, Qmin=-100, Qmax=300)

# plot the capability curve
p = points[:, 0]
qmin = points[:, 1]
qmax = points[:, 2]
plt.plot(qmax, p, 'x-')
plt.plot(qmin, p, 'x-')

# generate random points and interpolate the curve to get the reactive power limits
p2 = np.random.random(10) * Snom
qmin2, qmax2 = get_q_limits(q_points=points, p=p2)
plt.plot(qmax2, p2, 'o')
plt.plot(qmin2, p2, 'o')

plt.show()


import numpy as np

# matrix_size = n x n
def build_magic_squares(matrix_size):
	magic_square = np.zeros((matrix_size, matrix_size), dtype=int)

	n = 1
	i = 0
	j = matrix_size // 2

	while n <= matrix_size * matrix_size:
		magic_square[i, j] = n

		n += 1
		ii = (i - 1) % matrix_size
		jj = (j + 1) % matrix_size

		if magic_square[ii, jj]:
			i += 1
		else:
			i, j = ii, jj

	return magic_square
	print("\n")

# Square formatting here:
def format_magic_square(magic_square):
	nice_list = [["%3s" % str(str(j) + " ") for j in i] for i in magic_square]
	for line in nice_list:
		print(" ".join(map(str, line)))

# Verification/test matrix
def magic_square_test(matrix):
	iSize = len(matrix[0])
	sum_list = []

	#Horizontal Part:
	sum_list.extend([sum(lines) for lines in matrix])

	#Vertical Part:
	for col in range(iSize):
		sum_list.append(sum(row[col] for row in matrix))

	#Diagonals Part
	result1 = 0
	for i in range(0, iSize):
		result1 += matrix[i][i]
	sum_list.append(result1)

	result2 = 0
	for i in range(iSize - 1, -1, -1):
		result2 += matrix[i][i]
	sum_list.append(result2)

	if len(set(sum_list)) > 1:
		return "incorrect"
	return "correct"

def main(matrix_size):
	print("\n")
	magic_square = build_magic_squares(matrix_size)
	print("\n")
	format_magic_square(magic_square)
	print("\n")
	print(magic_square_test(magic_square))

if __name__ == "__main__":
	while True:
		try:
			num = int(input("Please enter a positive odd integer:  "))
		except ValueError:
			print("Sorry, enter valid number")
			continue
		if (num < 0) or (num % 2) == 0:
			print("Sorry, your response must be positive odd number.")
			continue
		else:
			break

if (num % 2) == 1:
	main(num)


# Question 1, Lab 6
# AB Satyaprakash, 180123062

# imports
from sympy.abc import t,y
import numpy as np
import sympy as sp
import pandas as pd

# functions
def getEulerApproximation(f,X,Y,h):
    for i in range(1,Y.shape[0]):
        Y[i]=Y[i-1] + (f.subs({t:X[i-1], y:Y[i-1]})*h)

def getActualValues(g,X):
    for i in range(X.shape[0]):
        Z[i]=g.subs(t,X[i])

# program body
# t belongs to [0,3] and y(0)=0, with h = 0.5
a, b, h = 0, 3, 0.5
X = np.arange(a,b+h/2,h)
Y = np.zeros(X.shape[0])
Z = np.zeros(X.shape[0])
Y[0] = 0

f = -20*y + sp.cos(t) + 20*sp.sin(t) # from question
g = sp.sin(t)

getEulerApproximation(f,X,Y,h)
getActualValues(g,X)
print('Approximate value of y({}) = {}'.format(3,Y[-1]))
print('The actual value of y({}) = {}'.format(3, Z[-1]))
print('The error in this case = {}'.format(abs(Y[-1]-Z[-1])))

# Error bound is nh^2Y, where Y = 1/2(max|y"(x)|) for x in {x0,x1..xn}
func = sp.sin(t)
func = sp.diff(sp.diff(func,t),t)
n = X.shape[0]-1
maxi = 0
for x in X:
    maxi=max(maxi,abs(func.subs(t,x)))
K = maxi/2
errorBound = n*(h**2)*K
print('The error bound in this case = {}'.format(errorBound))
print('Clearly, absolute error with h = 0.5 greatly exceeds the error bound computed using (I) in Q4\n')

print('If we reduce h by 10 times, i.e make it 0.05 we observe that:')
a, b, h = 0, 3, 0.05
X = np.arange(a,b+h/2,h)
Y = np.zeros(X.shape[0])
Z = np.zeros(X.shape[0])
Y[0] = 0
getEulerApproximation(f,X,Y,h)
getActualValues(g,X)
print('Approximate value of y({}) = {}'.format(3,Y[-1]))
print('The actual value of y({}) = {}'.format(3, Z[-1]))
print('The error in this case = {}'.format(abs(Y[-1]-Z[-1])))

Semester 6/MA 322 (Scientific Computing Theory)/Labs/Lab 6/Code/q5.py


"""
https://projecteuler.net/problem=12
The sequence of triangle numbers is generated by adding the natural numbers. So the 7th triangle number would be
1 + 2 + 3 + 4 + 5 + 6 + 7 = 28. The first ten terms would be:

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...

Let us list the factors of the first seven triangle numbers:

 1: 1
 3: 1,3
 6: 1,2,3,6
10: 1,2,5,10
15: 1,3,5,15
21: 1,3,7,21
28: 1,2,4,7,14,28
We can see that 28 is the first triangle number to have over five divisors.

What is the value of the first triangle number to have over five hundred divisors?
"""
import numpy as np

from Common.Logger import get_logger, init_logger
from Common.Utilities import performance_run
from Common.Numbers import divisors

PERFORMANCE_RUNS = 10
DIVISOR_COUNT = 500

# def fastest(...) -> ...:
#     """
#     :return: ...
#     """
#     return ...

def generative(divisor_count: int = DIVISOR_COUNT) -> int:
    """
    This method starts from the beginning and performs a full search of triangular numbers to find the first one that
    satisfies the minimum number of required divisors
    :param divisor_count: This is the minimum number of divisors criteria that the solution has to find
    :return: This is the number that satisfies the minimum number of divisors criteria
    """
    divisor_list = []
    i = 0
    number = 0
    while len(divisor_list) < divisor_count:
        i += 1
        number += i
        divisor_list = divisors(number)
    return number

def predictive(divisor_count: int = DIVISOR_COUNT) -> int:
    """
    This method jumps ahead to a triangular number that could viable contain the number of divisors that are being
    sought in the answer.
    :param divisor_count: This is the minimum number of divisors criteria that the solution has to find
    :return: This is the number that satisfies the minimum number of divisors criteria
    """
    n = int((1 + np.sqrt(8*divisor_count + 1)) / 2)
    n -= 1  # This primes the algorithm to start at the correct value
    triangular_number = 0
    divisor_list = []

    while len(divisor_list) < divisor_count:
        n += 1
        triangular_number = int(n*(n+1)/2)
        divisor_list = divisors(triangular_number)

    return triangular_number

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

    # Performance run for fastest
    performance_run(generative, iterations=PERFORMANCE_RUNS)()
    performance_run(predictive, iterations=PERFORMANCE_RUNS)()


#Central Limit Theorem states that dist. of sample mean is normally dist.

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import expon,skewnorm

def get_data(n):
    data = np.concatenate((expon.rvs(scale=1,size=n//2),skewnorm.rvs(5,loc=3,size=n//2)))
    #getting exp dist data
    #now shuffle the data
    np.random.shuffle(data)
    return data

plt.hist(get_data(2000))    #not a gaussian,normal dist
plt.show()

d10 = get_data(10)
print(d10.mean())   #gives mean of 10 random numbers

means = [get_data(10).mean() for i in range(10000)]
plt.hist(means)
plt.show()
print("Std. Dev.=",np.std(means))

means = [get_data(100).mean() for i in range(10000)]
plt.hist(means)
plt.show()  #this gives better result than before
print("Std. Dev. for 100 data point mean=",np.std(means))

num_sample = [10,50,100,500,1000,5000,10000]
stds=[]
for n in num_sample:
    stds.append(np.std([get_data(n).mean() for i in range(1000)]))
plt.plot(num_sample,stds,'o',label='Obs Scatter')
plt.plot(num_sample,1/np.sqrt(num_sample),label='Random function',alpha=0.5)
#standard deviation is related to inverse number of samples
plt.legend()
plt.show()

plt.hist([get_data(100).mean() for i in range(100)])
plt.show()
#by changing the range to power of 10, you can observe that it reaches normal dist.
#width depends on samples went into number(10,100,1000)

n = 1000
data = get_data(n)
sample_mean = np.mean(data)
uncertainity_mean = np.std(data)/np.sqrt(n)
print(f"We have determined the mean of population to be {sample_mean:0.2f} +- {uncertainity_mean:.2f}")
#1 sigma = 1 standard deviation away from mean, represented by +-

from scipy.stats import norm

xs = np.linspace(sample_mean -0.2,sample_mean+0.2,100)
ys = norm.pdf(xs,sample_mean,uncertainity_mean)
plt.plot(xs,ys)
plt.xlabel("population mean")
plt.ylabel("Probability")
plt.show()
#normal distribution is always formed when sample size increases
#using CLT, to provide uncertainity, is the most way it's used


## NUMERICS ASSIGNMENT 4
# ADVECTION
""" Solving du/dt + du/dx = 0 """

import numpy as np
import matplotlib.pyplot as plt
import pylan as pn

## set up the grid
dx = .5
xend = 30
x = np.arange(0,xend+dx,dx)
nx = len(x)

tend = 10
## analytical solution
def ua(t):
    return (((x-t) >= 1.5) * ((x-t) <= 6.5))*1

## set up LEFT-DERIVATIVE, CENTRED DERIVATIVE
Gl = (np.eye(nx) + np.diag(-np.ones(nx-1),-1))/dx
Gc = (np.diag(np.ones(nx-1),1) + np.diag(-np.ones(nx-1),-1))/2/dx

#Gl[0,-1] = -1
#Gc[0,-1] = -1
#Gc[-1,0] = 1

## forward in time, centred in space
def FTUP(nt,dt):
    u = np.zeros((nt,nx))
    u[0,:] = ((x >= 1.5) * (x <= 6.5))*1 #initial conditions

    for i in range(nt-1):
        u[i+1,:] = u[i,:] - dt*Gl.dot(u[i,:])

    return u

## leap frog in time, centered in space
def LFCS(nt,dt):
    u = np.zeros((nt,nx))
    u[:2,:] = FTUP(2,dt) #use forward in time for n = 1

    for i in range(1,nt-1):
        u[i+1,:] = u[i-1,:] - 2*dt*Gc.dot(u[i,:])

    return u

## plotting
dtlist = np.array([.1,.5,1])

fig,axs = plt.subplots(2,3,sharex=True,sharey=True,figsize=(11,6))
for i in range(3):

    n = int(tend/dtlist[i])+1
    u = FTUP(n,dtlist[i])
    u2 = LFCS(n,dtlist[i])

    axs[0,i].plot(x,u[int(n/2),:],label='FT+UP')
    axs[0,i].plot(x,u2[int(n/2),:],label='LF+CS')
    axs[0,i].plot(x,ua(5.),'--',label='analytic',lw=2)

    axs[1,i].plot(x,u[-1,:])
    axs[1,i].plot(x,u2[-1,:])
    axs[1,i].plot(x,ua(10.),'--',lw=2)

    if i == 0:
        axs[0,i].legend()
        axs[0,i].set_ylabel(r'$u$')
        axs[1,i].set_ylabel(r'$u$')

    axs[1,i].set_xlabel(r'$x$')
    axs[0,i].set_title(r'$t = %i, \Delta t = %.1f$' % (int(tend/2),dtlist[i]))
    axs[1,i].set_title(r'$t = %i, \Delta t = %.1f$' % (int(tend),dtlist[i]))

axs[0,0].set_ylim(-.5,1.5)

plt.tight_layout()
plt.show()


import numpy as np
import sympy

def decompLU(A):
	L = np.eye(np.shape(A)[0]) #matriz L com a dimensao de A com a diagonal principla igual a 1
	U = np.zeros(np.shape(A)) #matriz U full zero
	lim = np.shape(A)[1] #pegando o tamanho da coluna de A (matriz quadrada)
	sum = 0

	for i in range(0, lim):
		for j in range(0, lim):
			sum = 0
			#Parte do somatorio de U que vai de 0 a i-1 e acumula L[i][k]*U[k][j] sendo k a variavel iteradora
			for k in range (0, i):
				sum += L[i,k]*U[k,j]
			U[i,j] = A[i,j] - sum #atribuindo o valor de U[i][j]

		for j in range (0, lim):
			sum = 0
			#Parte do somatorio de L que vai de 0 a i-1 e acumula L[j][k]*U[k][i] sendo k a variavel iteradora
			for k in range (0, i):
				sum+= L[j,k]*U[k,i]
			L[j,i] = (A[j,i] - sum)/U[i,i] #atribuindo o valor L[i][j]

	return L, U #retornando o resultado

def solveLU(L, U, b):
	#Resolvendo o sistema A*x = b
	#Como A = L*U, entao (L*U)*x = b
	#Vamos fazer L*(U*x) = b e U*x=y, portanto L*y=b
	#Logo, para encontrarmos o resultado resolvemos o sistema U*x = y

	y = np.zeros(np.shape(b)) #matriz coluna y com a mesma dimensao de b
	x = np.zeros(np.shape(b)) #matriz coluna x (resultado do sistema) com a mesma dimensao de b

	for i in range (0, np.shape(b)[0]): #resolvendo o sistema  L*y=b
		for j in range (0,i):
			y[i] -= y[j]*L[i,j]
		y[i]+=b[i]

	for i in range (np.shape(b)[0]-1, -1, -1): #resolvendo o sistema U*x=b
		for j in range (np.shape(b)[0]-1, i, -1):
			x[i] -= x[j]*U[i,j]
		x[i]+=y[i]
		x[i]/=U[i,i]

	return x #retornando o resultado

A = np.array([[1, 2,0], [1, 3,1],[-2, 0,1]])
b = np.array([3,5,-1])
(L, U) = decompLU(A)
x = solveLU(L, U, b)
print('Matriz L:\n{}'.format(L))
print('Matriz U:\n{}'.format(U))
print('Solve LU:{}'.format(x))

A = sympy.Matrix([[1, 2,0], [1, 3,1],[-2, 0,1]])
b = sympy.Matrix([3, 5,-1])
L, U, _ = A.LUdecomposition()
x = A.solve(b)

print('Matriz L funcao do python:\n{}'.format(L))
print('Matriz U funcao do python:\n{}'.format(U))
print('Solve LU: {}'.format(x))


def main():

    from copy import deepcopy
    import numpy as np
    import pandas as pd
    from matplotlib import pyplot as plt
    plt.rcParams['figure.figsize'] = (16, 9)
    plt.style.use('ggplot')

    # Importing the dataset
    data = pd.read_csv('xclara.csv')
    print("Input Data and Shape")
    print(data.shape)
    data.head()

    # Getting the values and plotting it
    f1 = data['V1'].values
    f2 = data['V2'].values
    X = np.array(list(zip(f1, f2)))
    plt.scatter(f1, f2, c='black', s=7)

    # Euclidean Distance Caculator
    def dist(a, b, ax=1):
        return np.linalg.norm(a - b, axis=ax)

    # Number of clusters
    k = 3
    # X coordinates of random centroids
    C_x = np.random.randint(0, np.max(X)-20, size=k)
    # Y coordinates of random centroids
    C_y = np.random.randint(0, np.max(X)-20, size=k)
    C = np.array(list(zip(C_x, C_y)), dtype=np.float32)
    print("Initial Centroids")
    print(C)

    # Plotting along with the Centroids
    plt.scatter(f1, f2, c='#050505', s=7)
    plt.scatter(C_x, C_y, marker='*', s=200, c='g')

    # To store the value of centroids when it updates
    C_old = np.zeros(C.shape)
    # Cluster Lables(0, 1, 2)
    clusters = np.zeros(len(X))
    # Error func. - Distance between new centroids and old centroids
    error = dist(C, C_old, None)
    # Loop will run till the error becomes zero
    while error != 0:
        # Assigning each value to its closest cluster
        for i in range(len(X)):
            distances = dist(X[i], C)
            cluster = np.argmin(distances)
            clusters[i] = cluster
        # Storing the old centroid values
        C_old = deepcopy(C)
        # Finding the new centroids by taking the average value
        for i in range(k):
            points = [X[j] for j in range(len(X)) if clusters[j] == i]
            C[i] = np.mean(points, axis=0)
        error = dist(C, C_old, None)

    colors = ['r', 'g', 'b', 'y', 'c', 'm']
    fig, ax = plt.subplots()
    for i in range(k):
            points = np.array([X[j] for j in range(len(X)) if clusters[j] == i])
            ax.scatter(points[:, 0], points[:, 1], s=7, c=colors[i])
    ax.scatter(C[:, 0], C[:, 1], marker='*', s=200, c='#050505')

    '''
    ==========================================================
    scikit-learn
    ==========================================================
    '''

    from sklearn.cluster import KMeans

    # Number of clusters
    kmeans = KMeans(n_clusters=3)
    # Fitting the input data
    kmeans = kmeans.fit(X)
    # Getting the cluster labels
    labels = kmeans.predict(X)
    # Centroid values
    centroids = kmeans.cluster_centers_

    # Comparing with scikit-learn centroids
    print("Centroid values")
    print("Scratch")
    print(C) # From Scratch
    print("sklearn")
    print(centroids) # From sci-kit learn


import numpy as np

def correlation(X, Y):
    '''
    Correlation function
    '''
    N = X.size
    mu_X = X.sum()/N
    mu_Y = Y.sum()/N
    cov = ((X - mu_X)*(Y - mu_Y)).sum()/N
    sigma_X = X.std()
    sigma_Y = Y.std()
    return cov/(sigma_X*sigma_Y)

######################
### Synthetic Data ###
######################

def synthData1():
    '''
    Returns synthetic data for linear regression simple
    '''
    np.random.seed(sum([ord(c) for c in 'Regression']))
    N = 20
    x = np.linspace(0, 1, N)
    yA = x
    yB = x + (np.random.random(N)*2 - 1)*0.15
    yC = x + (np.random.random(N)*2 - 1)*0.5
    yD = np.random.random(N)
    return [x, yA, yB, yC, yD]

def synthData2(M):
    '''
    Returns synthetic data for linear regression multiple
    '''
    np.random.seed(sum([ord(c) for c in 'Regression']))
    N = complex(0, M)
    s, t = np.mgrid[-1:1:N, -1:1:N]
    x1 = s.reshape(1, -1)[0]
    x2 = t.reshape(1, -1)[0]
    y = (x1 + x2)*0.5 + (np.random.random(int(N.imag**2))*2 - 1)*0.75
    return [s, t, x1, x2, y]

def synthData3():
    '''
    Returns synthetic data for linear regression gradient descent
    '''
    np.random.seed(sum([ord(c) for c in 'Regression']))
    N = 20
    x = np.linspace(0, 1, N)
    x_ = np.linspace(-5, 5, N)
    y = x + (np.random.random(N)*2 - 1)*0.25
    return [x, x_, y]

def synthData4():
    '''
    Returns synthetic data for linear regression non-linear analysis
    Anscombes quartet
    '''
    x1 = [10,   8,    13,   9,    11,   14,   6,    4,    12,   7,    5]
    y1 = [8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84,4.82, 5.68]
    x2 = [10,   8,    13,   9,    11,   14,   6,    4,    12,   7,    5]
    y2 = [9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]
    x3 = [10,   8,    13,   9,    11,   14,   6,    4,    12,   7,    5]
    y3 = [7.46, 6.77, 12.74,7.11, 7.81, 8.84, 6.08, 5.39, 8.15, 6.42, 5.73]
    x4 = [8,    8,    8,    8,    8,    8,    8,    19,   8,    8,    8]
    y4 = [6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50,5.56, 7.91, 6.89]
    return np.array([x1, y1, x2, y2, x3, y3, x4, y4])

def synthData5():
    '''
    Returns synthetic data for logistic regression
    '''
    np.random.seed(sum([ord(c) for c in 'Regression']))
    N = 512
    x1 = np.random.normal(1, 0.3, N//2)
    x1 = np.concatenate([x1, np.random.normal(2, 0.3, N//2)])
    x2 = np.random.normal(0, 0.3, N//2)
    x2 = np.concatenate([x2, np.random.normal(0.25, 0.3, N//2)])
    y = np.zeros(N//2, np.int8)
    y = np.concatenate([y, np.ones(N//2, np.int8)])
    return [x1, x2, y]

def synthData6():
    '''
    Returns synthetic data for polynomial regression
    '''
    np.random.seed(sum([ord(c) for c in 'Regression']))
    N = 21
    x = np.random.uniform(-3, 3, N)
    y = x**3 - 3*x**2 + x + 1 + np.random.uniform(-3, 3, N)
    return [x, y]


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 17 16:26:48 2017

@author: lamahamadeh
"""

#First: Python-based implementation
#------------------------------------
'''
source:
-------
Video 2.4.2: Examples Involving Randomness
Week 2 Overview/Python Libraries and Concepts Used in Research
Using python for research
Harvard
online course provided by edx.org
url: https://courses.edx.org/courses/course-v1:HarvardX+PH526x+3T2016/courseware/317ce880d7644d35840b1f734be76b06/391063d8f58242e892efafc9903b36e8/
'''
#roll a dice 100 times and plot a histogram of the outcomes
#meaning: a histogram that shows how frequent the numbers from 1 to 6 appeared in the 100 samples

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

random.choice([1,2,3,4,5,6]) #this line throws the dice one time

rolls = []

for k in range(100):#we can try 1000, 10000000 times. We can notice that the histogram gets more flat when the number of rolling times increases.
    rolls.append(random.choice([1,2,3,4,5,6]))#in this case, after using for loop, we wre rolling the dice 100 times

print(len(rolls))

#draw a histogram

plt.figure()
plt.hist(rolls, bins = np.linspace(0.5,6.5,7));
plt.show()

start_time = time.clock()
#This time we will roll 10 dice not jsut one
ys = []
for rep in range(100000):#By increasing the number of dice rolls for each dice the distrbution follows the central limit theorem
#The central limit theorem (CLT) states that the sum of a large number of random variables regardless of their distribution will
#approximately follow a normal distribution (or Gaussian distribution).
    y = 0
    for k in range (10):
        x = random.choice([1,2,3,4,5,6])
        y = y + x
    ys.append(y)

end_time = time.clock()

speed1 = end_time - start_time
print(speed1)#1.19823723963

print(len(ys)) #100
print(min(ys))
print(max(ys))

plt.figure()
plt.hist(ys); #the semicolon suppresses the output
plt.show()

#------------------------------------------------------------------

#Second: NumPy random module implementation
#------------------------------------------
'''
source:
-------
Video 2.4.3: using the NumPy Random Module
Week 2 Overview/Python Libraries and Concepts Used in Research
Using python for research
Harvard
online course provided by edx.org
url: https://courses.edx.org/courses/course-v1:HarvardX+PH526x+3T2016/courseware/317ce880d7644d35840b1f734be76b06/391063d8f58242e892efafc9903b36e8/
'''
# We will repeate the previous example, but this time we will use Numpy

start_time = time.clock()

X= np.random.randint(1,7,(100000,10)) #generate random numbers between 1 and 6 in a 2D matrix where we have 100 rows and 10 columns
Y = np.sum(X, axis=1) #sum over all the columns (the length of Y =100)
#if you sum over all the columns, the length of the resulting list = the length of your rows
#if you sum over all the rows, the length of the resulting list = the length of your columns

end_time = time.clock()
speed2 = end_time - start_time
print(speed2)#0.0248949445651

plt.hist(Y) #It can be seen from the histogram that its shape tends to have a normal distribution shape as we go higher in the
#number of rows.

#Generally using numpy is much faster than using standard python implementation. This is very important in scientific research.
#we can prove that by calculating the value of:
#speed1/speed2=1.19823723963/0.0248949445651=49.9(approximately 50 times faster)
#and it is obviously can be seen taht using numpy is not only uses a less code but also much faster than the python-based
#implementation (with approximately 50 times faster in our case).
#ususally the coding speed value depends on your computer speed, so it doesn't have to hold the same value for everyone.
#You might get a different value
#------------------------------------------------------------------


import numpy as np
import matplotlib.pyplot as mplt

def twoDGradient(vals, dx, dy):

    n = len(vals); j = len(vals[0])

    gradX =  [ [0.0] * n for _ in range(j) ]
    gradY = [ [0.0] * n for _ in range(j) ]

    for i in range(1, n - 1):
        for k in range(1, j - 1):

            gradX[i][k] = (vals[i + 1][k] - vals[i - 1][k]) / (2.0 * dx)
            gradY[i][k] = (vals[i][k + 1] - vals[i][k - 1]) / (2.0 * dy)

    return gradX, gradY

#grid information
Lx = 10; Ly = 10
Nx = 51; Ny = 51; Nt = 500
dx = Lx/(Nx - 1); dy = Ly/(Ny - 1)
rho = 1; cp = 1
c = 1; C = 0.05; dt = C*dx/c

#initialize mats to correct sizes
Tc = [ [0.0] * Ny for _ in range(Nx) ]
Tn = [ [0.0] * Ny for _ in range(Nx) ]
k = [ [1.0] * Ny for _ in range(Nx) ]

#place initial source
for i in range(20, 25):
    for j in range( 30, 35):
        k[i][j] = 0.0001 #square hole - thermal conductivity is very low

#Source Term / Location of Source
Sx = round(7.0*Nx/Lx); Sy = round(3.0*Ny/Ly)

t = 0;
for n in range( 0, Nt ):

    Tc = Tn #copy old temps for new time step

    for i in range( 1, Nx - 1 ):
        for j in range( 1, Ny - 1 ):
            #five point stencil with forcing function that includes the source
            Tn[j][i] = Tc[j][i] + dt * (k[j][i]/rho/cp) * ((Tc[j][i+1] + Tc[j+1][i] - 4.0*Tc[j][i] + Tc[j][i-1] + Tc[j-1][i])/dx/dx)

    t = t + dt #inc time

    if(t < 1):
        #the source turns off at t == 1
        Tn[Sy][Sx] = Tn[Sy][Sx] + dt*100/rho/cp;

    #mixed boundary conditions
    for i in range(Nx):
        Tn[0][i] = 0.0
        Tn[Ny - 1][i] = 0.0
        Tn[i][0] = 0
        Tn[i][Ny - 1] = Tn[i][Ny - 2];

twoDGradX, twoDGradY = twoDGradient(Tn, dx, dy)
sanityCheck = np.gradient(Tn, dx, dy)

fig, (ax1, ax2) = mplt.subplots(2)
ax1.plot(sanityCheck[0], label = 'numpy implementation')
ax2.plot(twoDGradX, label = 'my implementation')
mplt.show()


import numpy as np

def compute_mse(theta_0, theta_1, data):
    acum_error = 0.0
    for k in range(len(data)):
        acum_error += (first_order_pol(theta_0, theta_1, data[k][0]) - data[k][1])**2
    return acum_error / len(data)

def first_order_pol(theta_0, theta_1, x):
    return theta_0 + theta_1 * x

def step_gradient(theta_0, theta_1, data, alpha):
    df_dtheta_0 = 0
    df_dtheta_1 = 0
    for k in range(len(data)):
        e = first_order_pol(theta_0, theta_1, data[k][0]) - data[k][1]
        df_dtheta_0 += e
        df_dtheta_1 += e * data[k][0]
    df_dtheta_0 *= 2/len(data)
    df_dtheta_1 *= 2/len(data)
    return (theta_0-alpha*df_dtheta_0, theta_1-alpha*df_dtheta_1)

def fit(data, theta_0, theta_1, alpha, num_iterations):
    theta_0_record = [theta_0]
    theta_1_record = [theta_1]
    t0 = theta_0
    t1 = theta_1
    for i in range(num_iterations):
        t0, t1 = step_gradient(t0, t1, data, alpha)
        theta_0_record.append(t0)
        theta_1_record.append(t1)
    return theta_0_record, theta_1_record


##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
##                    Centering & Scaling
## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#%% Standard scaling
import numpy as np
from sklearn.preprocessing import StandardScaler

X = np.array([[ 1000, 0.01,  300],
              [ 1200,  0.06,  350],
              [ 1500,  0.1, 320]])
scaler = StandardScaler().fit(X) # computes mean & std column-wise
X_scaled = scaler.transform(X) # transform using computed mean and std

# check mean = 0 and variance = 1 for every variable/column after scaling
print(X_scaled.mean(axis=0)) # return 1D array of size(3,1)
print(X_scaled.std(axis=0)) # return 1D array of size(3,1)

# access mean and variance via object properties
print(scaler.mean_) # return 1D array of size(3,1)
print(scaler.var_) # return 1D array of size(3,1)

#%% Normalization
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler() # create object
X_scaled = scaler.fit_transform(X) # fit & transform

# check min = 0 and max = 1 for every variable/column after scaling
print(X_scaled.min(axis=0))
print(X_scaled.max(axis=0))

# access min and max via object properties
print(scaler.data_min_)
print(scaler.data_max_)

##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
##                    Robust Centering & Scaling
## %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

#%% Generate oulier-infested data
X = np.random.normal(40, 1, (1500,1))
X[200:300] = X[200:300] +8; X[1000:1150] = X[1000:1150] + 8

# plot
import matplotlib.pyplot as plt
plt.plot(X, '.-')
plt.xlabel('sample #'), plt.ylabel('variable measurement')
plt.title('Raw measurements')

#%% Transform via standard scaling
scaler = StandardScaler().fit(X)
X_scaled = scaler.transform(X)

# mean and std
print('Estimated mean = ', scaler.mean_[0])
print('Estimated standard deviation = ', np.sqrt(scaler.var_[0]))

# plot
plt.figure()
plt.plot(X_scaled, '.-')
plt.xlabel('sample #'), plt.ylabel('scaled variable measurement')
plt.xlim((0,1500))
plt.title('Standard scaling')

#%% Transform via robust MAD scaling
# compute median and MAD
from scipy import stats
median = np.median(X)
MAD = stats.median_absolute_deviation(X)

# scale
X_scaled = (X - median)/MAD[0]

# median and MAD
print('Estimated robust location = ', median)
print('Estimated robust spread = ', MAD)

# plot
plt.figure()
plt.plot(X_scaled, '.-')
plt.xlabel('sample #'), plt.ylabel('scaled variable measurement')
plt.xlim((0,1500))
plt.title('Robust MAD scaling')


import pandas as pd
import scipy.stats

def compare_averages(filename):
    """
    Performs a t-test on two sets of baseball data (left-handed and right-handed hitters).

    You will be given a csv file that has three columns.  A player's
    name, handedness (L for lefthanded or R for righthanded) and their
    career batting average (called 'avg'). You can look at the csv
    file by downloading the baseball_stats file from Downloadables below.

    Write a function that will read that the csv file into a pandas data frame,
    and run Welch's t-test on the two cohorts defined by handedness.

    One cohort should be a data frame of right-handed batters. And the other
    cohort should be a data frame of left-handed batters.

    We have included the scipy.stats library to help you write
    or implement Welch's t-test:
    http://docs.scipy.org/doc/scipy/reference/stats.html

    With a significance level of 95%, if there is no difference
    between the two cohorts, return a tuple consisting of
    True, and then the tuple returned by scipy.stats.ttest.

    If there is a difference, return a tuple consisting of
    False, and then the tuple returned by scipy.stats.ttest.

    For example, the tuple that you return may look like:
    (True, (9.93570222, 0.000023))
    """

    data = pd.read_csv(filename)

    data_l = data[data['handedness'] == 'L']
    data_r = data[data['handedness'] == 'R']

    t, p = scipy.stats.ttest_ind(data_l['avg'], data_r['avg'], equal_var=False)
    no_difference = p > 0.05

    return no_difference, (t, p)

file = 'baseball_data/baseball_stats.csv'
print(compare_averages(file))


import numpy as np
import matplotlib.pyplot as plt

#step1
def initialize_parameters(lenw):
    w=np.random.randn(1,lenw)
    #w=np.zeroes((1,lenw))
    b=0
    return w,b

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

#step2
def forward_prop(X,w,b): #w-->1xn , X-->nxm
    z = sigmoid(np.dot(w,X)+b)
    return z

#    ln  = len(X.shape[1])
#   for i in range(0,ln):
#        if z[0,i]>0.5:
#            z[0,i]=1
#        else:
#            z[0,i]=0
#    z = z.astype(int)

#step 3
def cost_function(z,y):
    #cost = -1/m * np.sum(Y * np.log(A) + (1-Y) * (np.log(1-A)))
    cost = ((-y * np.log(z))-((1-y)* np.log(1-z))).mean()
    return cost

#step4
def back_prop(X,y,z):
    m=y.shape[1]
    dz = (1/m)*(z-y)
    dw = np.dot(dz,X.T) #dw --> 1xn
    db = np.sum(dz)
    return dw,db

#step5
def gradient_descent_update(w,b,dw,db,learning_rate):
    w=w-learning_rate*dw
    b=b-learning_rate*db
    return w,b

#step6
def logistic_regression_model(X_train,y_train,X_val,y_val,learning_rate,epochs,threshold):

    lenw = X_train.shape[0]
    w,b= initialize_parameters(lenw) #step1
    th=threshold
    iteration=2

    costs_train = []
    costs_val   = []
    m_train     = y_train.shape[1]
    m_val       = y_val.shape[1]

    for i in range(1,epochs+1):
        z_train    = forward_prop(X_train,w,b) #step2
        cost_train = cost_function(z_train,y_train) #step3
        dw,db      = back_prop(X_train,y_train,z_train) #step4
        w,b        = gradient_descent_update(w,b,dw,db,learning_rate) #step5

        count=i
        #store trining cost in a list for plotting purpose
        #if i%10==0:
        costs_train.append(cost_train)

        #MAE_train
        MAE_train = (1/m_train)*np.sum(np.abs(z_train-y_train))

        #cost_val,MAE val
        z_val    = forward_prop(X_val,w,b)
        cost_val = cost_function(z_val,y_val)
        MAE_val  = (1/m_val)*np.sum(np.abs(z_val-y_val))
        #if i%10==0:
        costs_val.append(cost_val)

        if count>1:
            diff = (costs_train[-2]-costs_train[-1])
            iteration=iteration+1
            if diff <th:
                break
            else:
                continue

    return costs_train,costs_val,w,iteration


import numpy as np

# Zeros Matrix
print(np.zeros((3, 3)), '\n')

# Identity Matrix
print(np.identity(3), '\n')

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

# Matrix Copy
AC = A.copy()
print(AC, '\n')

# Transpose a matrix
AT = np.transpose(A)
print(AT, '\n')

# Add and Subtract
SumAC = A + C
print(SumAC, '\n')

DifCA = C - A
print(DifCA, '\n')

# Matrix Multiply
ProdAC = np.matmul(A, C)
print(ProdAC, '\n')

# Multiply a List of Matrices
arr = [A, C, A, C, A, C]
Prod = np.matmul(A, C)
num = len(arr)
for i in range(2, num):
    Prod = np.matmul(Prod, arr[i])
print(Prod, '\n')

ChkP = np.matmul(
            np.matmul(
                np.matmul(
                    np.matmul(
                        np.matmul(arr[0], arr[1]),
                        arr[2]), arr[3]), arr[4]), arr[5])
print(ChkP, '\n')

# Check Equality of Matrices
print(Prod == ChkP, '\n')
# https://docs.scipy.org/doc/numpy/reference/generated/numpy.allclose.html
print(np.allclose(Prod, ChkP), '\n')

# Dot Product (follows the same rules as matrix multiplication)
# https://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html
v1 = np.array([[2, 4, 6]])
v2 = np.array([[1], [2], [3]])
ans1 = np.dot(v1, v2)
ans2 = np.dot(v1, v2)[0, 0]
print(f'ans1 = {ans1}, ans2 = {ans2}\n')

# Unitize an array
mag1 = (1*1 + 2*2 + 3*3) ** 0.5
mag2 = np.linalg.norm(v2)
norm1 = v2 / mag1
norm2 = v2 / mag2
print(f'mag1 = {mag1}, mag2 = {mag2}, they are equal: {mag1 == mag2}\n')
print(norm1, '\n')
print(norm2, '\n')
print(norm1 == norm2)


# trapezoidal rule
#
# M. Zingale (2013-02-13)

import math
import numpy

# function we wish to integrate
def fun(x):
    return numpy.exp(-x)

# analytic value of the integral
def I_exact(a,b):
    return -math.exp(-b) + math.exp(-a)

# do a trapezoid integration by breaking up the domain [a,b] into N
# slabs
def trap(a,b,f,N):

    xedge = numpy.linspace(a,b,N+1)

    integral = 0.0

    n = 0
    while n < N:
        integral += 0.5*(xedge[n+1] - xedge[n])*(f(xedge[n]) + f(xedge[n+1]))
        n += 1

    return integral

N = 3
a = 0.0
b = 1.0

N = 2
while (N <= 128):
    t = trap(a,b,fun,N)
    e = t - I_exact(a,b)
    print N, t, e

    N *= 2


import numpy as np
import matplotlib.pyplot as plt

##### Sigmoid
sigmoid = lambda x: 1 / (1 + np.exp(-x))

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,100)

fig = plt.figure()
plt.plot(y,sigmoid(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('Sigmoid Function')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-2, -1, 0, 1, 2])

plt.ylim(-2, 2)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('sigmoid.png')

fig = plt.figure()

##### TanH
tanh = lambda x: 2*sigmoid(2*x)-1

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,100)

plt.plot(y,tanh(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('TanH Function')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])

plt.ylim(-4, 4)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('tanh.png')

fig = plt.figure()

##### ReLU
relu = lambda x: np.where(x>=0, x, 0)

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,1000)

plt.plot(y,relu(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('ReLU')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])

plt.ylim(-4, 4)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('relu.png')

fig = plt.figure()

##### Leaky ReLU
leakyrelu = lambda x: np.where(x>=0, x, 0.1*x)

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,1000)

plt.plot(y,leakyrelu(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('Leaky ReLU')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])

plt.ylim(-4, 4)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('lrelu.png')

fig = plt.figure()

##### Binary Step
bstep = lambda x: np.where(x>=0, 1, 0)

x=np.linspace(-10,10,10)

y=np.linspace(-10,10,1000)

plt.plot(y,bstep(y),'b', label='linspace(-10,10,100)')

plt.grid(linestyle='--')

plt.xlabel('X Axis')

plt.ylabel('Y Axis')

plt.title('Step Function')

plt.xticks([-4, -3, -2, -1, 0, 1, 2, 3, 4])
plt.yticks([-2, -1, 0, 1, 2])

plt.ylim(-2, 2)
plt.xlim(-4, 4)

plt.show()
#plt.savefig('step.png')

print('done')


import numpy as np
import matplotlib.pyplot as plt
np.set_printoptions(threshold=np.nan)
np.set_printoptions(precision=16)

res = np.zeros((2,20))
for m in [10,20]:
    h = 1/m
    h2 = h/2

    x = np.linspace(0,1,m+1)
    f = 2*(3*x[1:]**2-2*x[1:]+1) # start at x[1] since we evaluate for j=1,2,...

    f[0] -= (1+(x[1]-h2)**2)/h**2

    A = np.zeros((m,m))
    A[0,0:2] = [-2*(1+x[2]**2+h2**2),(1+(x[2]+h2)**2)]
    for j in range(2,m):
        A[j-1,j-2:j+1] = [(1+(x[j]-h2)**2),-2*(1+x[j]**2+h2**2),(1+(x[j]+h2)**2)]
    A[m-1,m-2:m] = [2*(1+x[j]**2+h2**2),-2*(1+x[j]**2+h2**2)]

    u = np.linalg.solve(A/h**2,f)
    res[int(m/10-1),0:m] = u
    print(h,np.sqrt(h)*np.linalg.norm(u-(1-x[1:])**2))

#<startTeX>
# richardson extrapolation
m = 10
h = 1/m
x = np.linspace(0,1,m+1)
richardson = (4*res[1,1::2] - res[0,0:10])/3

plt.scatter(x[1:],((1-x[1:])**2)) #actual solution
plt.scatter(x[1:],richardson) # numerical solution
print("m=10:",np.sqrt(h)*np.linalg.norm(res[0,0:10]-(1-x[1:])**2))
print("m=20 (subsampled):",np.sqrt(h)*np.linalg.norm(res[1,1::2]-(1-x[1:])**2))
print("richardson:",np.sqrt(h)*np.linalg.norm(richardson-(1-x[1:])**2))
#<endTeX>


from numpy import *
from tqdm import trange # for a fancy looking progress bar while running the algorithm (use with for loop)

def mean_sq_error(b, m, data):
    squared_error = 0

    # calculate squared_error
    for i in range(len(data)):
        x = data[i, 0]
        y = data[i, 1]
        squared_error += (y - (m * x + b))**2  #this will calculate squared error

    # for mean squared error, divide squared error by number of data points or len(data)
    return squared_error / float(len(data)) #float bc we want error to be precise

def gradient_descent(b_init, m_init, data, learning_rate):
    gradient_wrt_b = 0 #gradient wrt b_init
    gradient_wrt_m = 0 #gradient wrt m_init
    N = float(len(data))

    # compute gradients wrt b_init and m_init
    for i in range(len(data)):
        x = data[i, 0]
        y = data[i, 1]
        gradient_wrt_b += -(2/N) * (y - ((m_init * x) + b_init))
        gradient_wrt_m += -(2/N) * x * (y - ((m_init * x) + b_init))

    #update and return b_init and m_init to b_new and m_new respectively
    b_new = b_init - (learning_rate * gradient_wrt_b)
    m_new = m_init - (learning_rate * gradient_wrt_m)
    return (b_new, m_new)

def run_gradient_descent(b_init, m_init, data, learning_rate, num_of_iterations):
    # b and m before running gradient descent
    b_final = b_init
    m_final = m_init

    # run gradient descent for the given number of iterations
    for i in trange(num_of_iterations):
        (b_final, m_final) = gradient_descent(b_final, m_final, array(data), learning_rate)

    # return the final values of b and m
    return (b_final, m_final)

def run_linear_regression():
  # load data
  dataset = genfromtxt('data.csv', delimiter= ',')

  # define hyperparameters
  learning_rate = 0.0001
  b_init = 0 # y-intercept
  m_init = 0 # slope of regression line
  num_of_iterations = 100000

  # start gradient descent
  print("\n Starting gradient descent at b =", b_init, ", m =", m_init, ", error =", mean_sq_error(b_init, m_init, dataset))
  print("Running...")
  (b_init, m_init) = run_gradient_descent(b_init, m_init, dataset, learning_rate, num_of_iterations)

  #print final results
  print("Gradient descent successful...")
  print("After", num_of_iterations,  "iterations, b =", b_init, ", m =", m_init, "error =", mean_sq_error(b_init, m_init, dataset))

if __name__ == '__main__':
  run_linear_regression()


# Python script of the samples of numpy
# Author: Sandeep Mewara
# Location: Learn By Insight
# Github: https://github.com/samewara/python-examples/blob/master/numpy-basic.py
# #####################################
# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %% [markdown]
# ## NUMPY SAMPLES ##

# %%
import numpy as np

# %% [markdown]
# *** Random Walk simulation ***
#
# 1000-step random walk exercise where a person starting from 0 takes one step to the right(+1) or to the left(-1) with equal probability. To get 'd' being the max distance reached from the origin during the walk.
#
# Simulate it (N trials) and find the mean of the max distance of all trails.

# %%
# unit
steps = 1000
rw_seq = np.random.choice([-1,1],steps) #random sequence of steps
net_d = rw_seq.cumsum() #total distance = cumulative sum of all steps
max_d_from_origin = np.max(np.abs(net_d))

print('Max distance from origin: ',max_d_from_origin)

# %%
# for simulation

def simulate_d():
    steps = 1000
    rw_seq = np.random.choice([-1,1],steps) #random sequence of steps
    net_d = rw_seq.cumsum() #total distance = cumulative sum of all steps
    max_d_from_origin = np.max(np.abs(net_d))
    return max_d_from_origin

simulation_count = 100,1000,10000,100000
for i in range(len(simulation_count)):
    max_d_all = [simulate_d() for x in range(simulation_count[i])] # list comprehension - all max_d in a list
    mean_d = np.mean(max_d_all)
    print('Mean distance for {0} simulation is: {1}'.format(simulation_count[i], mean_d))

# based on the results, seems the max distance far from origin is around 40

# %% [markdown]
# *** Triangle simulation ***
# With a unit length stick - break into 3 sticks randomly. Carry out N trails and see the probability of finding the right pieces that can form the sides of the triangle using these 3 broken pieces
#

# %%
# unit validation

stick_l = 1
# this would be incorrect logically as fixed unit length is there
# pieces_length = np.random.rand(2) #2 random numbers between 0 & 1

#x = pieces_length[0]
#y = pieces_length[1]

x = np.random.uniform(0,1)
y = np.random.uniform(0,1-x)

z = 1 - (x+y)
print ('x:{0},y:{1},z:{2}'.format(x,y,z))

# for triangle to be valid any two side sum should always be greater than third
if(x+y>z and y+z>x and z+x>y):
    print('We got a traingle!')
else:
    print('SORRY! No triangle')

# %%
# for simulation
stick_l = 1

def getxyz():
    x = np.random.uniform(0,stick_l)
    y = np.random.uniform(0,stick_l-x)
    z = stick_l - (x+y)
    return x,y,z

def istriangle(x,y,z):
    if (x+y>z and y+z>x and z+x>y):
        return 1
    else:
        return 0

simulation_count = 100,1000,10000,100000
for i in range(len(simulation_count)):
    triangle_all = [istriangle(*(getxyz())) for x in range(simulation_count[i])] # list comprehension - all true/false in a list
    probability = np.sum(triangle_all)/simulation_count[i]
    print('Probablity for {0} simulation is: {1}'.format(simulation_count[i], probability))

# based on the results, seems the probablility f formaing triangle from pieces would be just under 20%

# %% [markdown]
# *** Random Number ***
#
# Generate 10 random numbers in the interval 0,1 and obtain an array X
#
# Generate another array Y such that $Y[i]$=1 if $X[i]$≥0.5 and 0 otherwise.

# %%
X = np.random.rand(10)

# Generate the array Y == Random Number
Y = np.where(X>=0.5,1,0)
Y

# %% [markdown]
# *** Pearson's correlation coefficient ***
#
# Given two arrays $X,Y$, correlation is a measure of linear dependence of values of one array on the other. For example, heights and weights of a group of people are correlated. Validate the below formula for correlation and verify the result using numpy's corrcoef method.
#
# This is given by $\frac{\sum(X-X_{mean})(Y-Y_{mean})}{\sqrt{\sum(X-X_{mean})^2\sum(Y-Y_{mean})^2}}$  .
#

# %%
X = np.random.randint(0,10,20)
Y = X + np.random.randint(20)

X_mean = np.mean(X)
Y_mean = np.mean(Y)

exp = np.sum((X-X_mean)*(Y-Y_mean))
std = np.sqrt(np.sum((X-X_mean)**2) * np.sum((Y-Y_mean)**2))
coeff_corr = exp/std
print('PCC via fomula: ',coeff_corr)
print('PCC via Numpy: ',np.corrcoef(X,Y))

# %% [markdown]
# *** Mean & Variance of crude oil prices ***
#
# data-file: crude_oil.csv
# Find the mean & the standard deviation of the two types of crude oil.
#
# Handle:
# 1. Header
# 2. Missing/Unknown data (should not be part of calcs)

# %%
data = np.genfromtxt('./data-files/numpy/crude_oil.csv',delimiter=";")
# data
# Row1 & Column1 look like not a number here so
data=data[1:,1:]
data

# %%
data = np.genfromtxt('./data-files/numpy/crude_oil.csv',delimiter=";",skip_header=1, encoding=None)
data=data[:,1:]
data

# %%
crude_data = np.nan_to_num(data)
crude_data

# %%
crude1_data = crude_data[~np.isnan(data[:,0])][:,0] #all rows but not nan, 0th column => using masking
crude2_data = crude_data[~np.isnan(data[:,1])][:,1] #all rows but not nan, 1st column

# %%
print('crude1_mean:{0},crude2_mean:{1}'.format(np.mean(crude1_data),np.mean(crude2_data)))
print('crude1_sd:{0},crude2_sd:{1}'.format(np.std(crude1_data),np.std(crude2_data)))


from pprint import pprint
import time
import numpy
import math
from uniform_generator import uniform_generator

def normal_generator():
    """
    Samples two independent random variables from standard Gaussian distribution.
    Uses Box-Muller Transform to generate random variables.
    Equation for Box-Muller Transform is
            Z_1 = -2log(U_1)^(1/2)*cos(2*pi*U_2)
            Z_2 = -2log(U_1)^(1/2)*sin(2*pi*U_2)
    where U_1 and U_2 are two independent random variables generated from uniform
    distribution.
    """
    uni_gen = uniform_generator()
    while True:
        u1 = next(uni_gen)
        u2 = next(uni_gen)
        if u1 == 0 or u2 == 0:
            continue
        random_number_1 = ((-2 * math.log(u1)) ** (1/2)) * math.cos(2 * math.pi * u2)
        random_number_2 = ((-2 * math.log(u1)) ** (1/2)) * math.sin(2 * math.pi * u2)
        yield random_number_1, random_number_2

limit = 10001
if __name__ == '__main__':
    output_list = []
    print("Generating 10000 random numbers from standard normal distribution.")
    start = time.time()
    norm_gen = normal_generator()
    for i in range(10000):
       op1, op2 = next(norm_gen)
       output_list.append(op1)
       output_list.append(op2)
    end = time.time()
    print("Time taken " + str(end - start))
    print("Mean is: " + str(numpy.mean(output_list)))
    print("Standard Deviation: " + str(numpy.std(output_list)))


from __future__ import division
import numpy as np
from numpy import linalg

def jacobi(A, b, x0, tol, N):
    # preliminares
    A, b, x0 = A.astype("double"), b.astype("double"), x0.astype("double")

    n = np.shape(A)[0]
    x = np.zeros(n)
    it = 0
    # iteracoes
    while it < N:
        it = it + 1
        # iteracao de Jacobi
        for i in np.arange(n):
            x[i] = b[i]
            for j in np.concatenate((np.arange(0, i), np.arange(i + 1, n))):
                x[i] -= A[i, j] * x0[j]
            x[i] /= A[i, i]
        # tolerancia
        if np.linalg.norm(x - x0, np.inf) < tol:
            return x
        # prepara nova iteracao
        x0 = np.copy(x)
    raise NameError("num. max. de iteracoes excedido.")

def gauss_seidel(A, b, x0, tol, N):
    # preliminares
    A, b, x0 = A.astype("double"), b.astype("double"), x0.astype("double")

    n = np.shape(A)[0]
    x = np.copy(x0)
    it = 0
    # iteracoes
    while it < N:
        it = +1
        # iteracao de Jacobi
        for i in np.arange(n):
            x[i] = b[i]
            for j in np.concatenate((np.arange(0, i), np.arange(i + 1, n))):
                x[i] -= A[i, j] * x[j]
            x[i] /= A[i, i]
        # tolerancia
        if np.linalg.norm(x - x0, np.inf) < tol:
            return x
        # prepara nova iteracao
        x0 = np.copy(x)
    raise NameError("num. max. de iteracoes excedido.")

A = np.matrix(
    [
        [1, -1, 0, 0, 0],
        [-1, 2, -1, 0, 0],
        [0, -1, (2 + 10 ** -3), -1, 0],
        [0, 0, -1, 2, -1],
        [0, 0, 0, 1, 2],
    ]
)
b = np.matrix([[1], [1], [1], [1], [1]])
x0 = np.matrix([[0], [0], [0], [0], [0]])

print("Metodo Eliminacao de Gauss")
A_inversa = np.linalg.inv(A)
x = np.dot(A_inversa, b)
for i in range(5):
    print("x{} = {}".format(i + 1, x[i, 0]))
print

print("Metodo Jacobi")
x = jacobi(A, b, x0, 10 ** -2, 100)  # utilizando 100 iteracoes
for i in range(5):
    print("x{} = {}".format(i + 1, x[i]))
print

print("Metodo Gauss-Seidel")
x = gauss_seidel(A, b, x0, 10 ** -2, 100)  # utilizando 100 iteracoes
for i in range(5):
    print("x{} = {}".format(i + 1, x[i, 0]))


import numpy as np
import math
import time

def timeit(func):
	def timed(*args, **kwargs):
		t1 = time.time()
		res = func(*args, **kwargs)
		t2 = time.time()
		return (t2-t1, res)
	return timed

def powerIntegrate(p, a, b):
	'''Integrate x^p over (a,b)'''
	p += 1
	return (b**p - a**p)/p

@timeit
def gaussIntegrate(
	# Interval
	a,b,
	# Function
	f,
	# Weight function alpha, where w(x) = x^p,
	p,
	# Number of intervals
	n=1
):
	'''Composite Gauss 1-point rule over n subintervals of (a,b), for callable f(x).
	Uses weight function w(x) = x**p
	'''
	# Divide into subintervals
	intervals = np.linspace(a,b,n+1)

	# Bounds of intervals, as lists
	ai = intervals[:-1]
	bi = intervals[1:]

	# The evaluated points, according to p
	# This only works explicitly for linear w(x), otherwise
	# We would need lagrange solver or something
	pg = powerIntegrate(p+1, ai, bi) / \
		powerIntegrate(p, ai, bi)

	# Compute the weight at each xi
	# Weight is just the integral of w(x) on subinterval
	wg = powerIntegrate(p, ai, bi)

	# Evaluate f(xi) for each xi
	fpg = list(map(f, pg))

	# Sum wi*f(xi)
	return wg.T @ fpg

cases = [
	(
		'x',
		[(2, 5),]
	),

	(
		'x**0.5',
		[ (-0.5, 4), (-0.5, 40) ],
	),
	(
		'(1-math.sin(x))**2',
		[(-0.5, 5), (-0.5, 50), (-0.5, 100)]
	),
	(
		'(x/math.sin(x/2))**0.5',
		[(-0.5, 2), (-0.5, 10)]
	)
]

if __name__ == '__main__':

	for i, z in enumerate(cases):

		# This is case #i, with function 'name'
		name, other = z
		f = lambda x: eval(name)

		print('Case #{0}, f(x) = {1}'.format (i, name))

		# Now run over each of the p's and n's
		for p, n in other:

			elapsed, integral = gaussIntegrate(
				0,1,
				f,
				p=p,
				n=n
			)

			print('\t(n={0})\t===>\t{1:.5f} ({2:.3f} ms)'.format(n,integral, elapsed * 1000))

		print()


import numpy as np

def jacobi_iteration(A, b, tol=1e-9, Max_iter=5000):
    """ Solve linear equations by Jacobi iteration method.

    Args:
        A: ndarray, coefficients matrix
        b: ndarray, constant vector
        tol: double, iteration accuracy
        Max_iter: int, maximum iteration number

    Returns:
        y: ndarray, solution of the linear equations
        k: int, iteration number
    """
    # decompose coefficients matrix
    D = np.diag(np.diag(A))
    L_plus_U = D - A

    # construct iterative coefficients matrix
    B = np.linalg.inv(D).dot(L_plus_U)
    f = np.linalg.inv(D).dot(b)

    # initial solution vector
    x = np.ones_like(b)
    # first iteration
    k = 1
    y = B.dot(x) + f

    # iteration
    while np.max(np.abs(y - x)) >= tol and k < Max_iter:
        k += 1
        x = y
        y = B.dot(x) + f

    return (y, k)

if __name__ == '__main__':
    # cofficients matrix
    A = np.array([[5, 2, 1], [2, 8, -3], [1, -3, -6]])
    # constant vector
    b = np.array([8, 21, 1])
    # Jacobi iteration method
    x, n = jacobi_iteration(A, b, 1e-5)

    print("The solution of the linear euqations is:")
    for i in range(len(x)):
        print(f'x_{i+1} = {x[i]}')
    print(f"The iteration number is {n}.")


import numpy as np

def ref(A):
    for j in xrange(A.shape[0]-1):
        for i in xrange(j+1, A.shape[0]):
            A[i,j:] -= A[i,j] * A[j,j:] / A[j,j]

def ref2(A):
    # This is an alternate version
    # It is faster but less intuitive.
    for i in xrange(A.shape[0]):
        A[i+1:,i:] -= np.outer(A[i+1:,i]/A[i,i], A[i,i:])

def LU(A):
    U = A.copy()
    L = np.eye(A.shape[0])
    for j in xrange(A.shape[0]-1):
        for i in xrange(j+1, A.shape[0]):
            # operation corresponding to left mult by
            # the elementary matrix desired
            L[i,j] = U[i,j] / U[j,j]
            # now we apply the change to U
            U[i,j:] -= L[i,j] * U[j,j:]
    return L, U

def LU2(A):
    # This is an alternate similar to ref2.
    U = A.copy()
    L = np.eye(A.shape[0])
    for i in xrange(A.shape[0]-1):
        L[i+1:,i] = U[i+1:,i] / U[i,i]
        U[i+1:,i:] -= np.outer(L[i+1:,i], U[i,i:])
    return L, U

def LU_inplace(A):
    for j in xrange(A.shape[0]-1):
        for i in xrange(j+1, A.shape[0]):
			# change to L
            A[i,j] /= A[j,j]
            # change to U
            A[i,j+1:] -= A[i,j] * A[j,j+1:]

def LU_solve(A,B):
    for j in xrange(A.shape[0]-1):
        for i in xrange(j+1, A.shape[0]):
            B[i] -= A[i,j] * B[j]
    for j in xrange(A.shape[0]-1, -1, -1):
        B[j] /= A[j,j]
        for i in xrange(j):
            B[i] -= A[i,j] * B[j]

def LU_det(A):
    B = A.copy()
    ref(B)
    # now extract diagonal and take product
    return np.prod(B.diagonal())

def cholesky(A):
    L = np.zeros_like(A)
    for i in xrange(A.shape[0]):
        for j in xrange(i):
            L[i,j]=(A[i,j] - np.inner(L[i,:j], L[j,:j])) / L[j,j]
        sl = L[i,:i]
        L[i,i] = sqrt(A[i,i] - np.inner(sl, sl))
    return L

def cholesky_inplace(A):
    for i in xrange(A.shape[0]):
        A[i,i+1:] = 0.
        for j in range(i):
            A[i,j] = (A[i,j] - np.inner(A[i,:j],A[j,:j])) / A[j,j]
        sl = A[i,:i]
        A[i,i] = sqrt(A[i,i] - np.inner(sl, sl))

def cholesky_solve(A, B):
    for j in xrange(A.shape[0]):
        B[j] /= A[j,j]
        for i in xrange(j+1, A.shape[0]):
            B[i] -= A[i,j] * B[j]
    for j in xrange(A.shape[0]-1, -1, -1):
        B[j] /= A[j,j]
        for i in xrange(j):
            B[i] -= A[j,i] * B[j]


"""
Calculating standard deviation and normality of a user defined random set of integers
"""

def summary_stats(n="Enter integer here"):

    """
    Calculate STD and normality of a random set of integers
    % function ex = summary_stats(n)
    %- INPUT:
    % 1) One whole integer that defines how many random numbers to generate in the data set.
    %- OUTPUT:
    % 1) How many values are within +/- 1 std.
    % 2) How many values are not within +/- 1 std.
    % 3) The percent of values that fall within +/- 1 std.
    % 4) Is the data set normally distributed?
    % 5) Graph of ther data set
    """

    import numpy as np
    import matplotlib.pyplot as plt
    data = np.random.randint(low=1,high=100,size=n).astype(np.float)
    summation=0.0
    counter=0
    mean=0
    sumsq=0.0
    var=0.0
    sqsum=0.0
    std_list=[]
    for i in range(len(data)):
        summation+=data[i]
        counter+=1
    mean=(summation/counter)
    for i in range(len(data)):
        sumsq=((data[i]-mean)**2)
        std_list.append(sumsq)
    for i in range(len(std_list)):
        sqsum+=std_list[i]
    var=(sqsum/counter)
    std=(var**0.5)
    print('The average(mu) of this data list is: '+ str(mean) + ' and the standard deviation (sigma) is: ' + str(std))
    lower=(mean-std)
    upper=(mean+std)
    yes=0.0
    no=0.0
    for i in data:
        if i < (upper) and i > (lower):
            yes+=1
        else:
            no+=1
    print("These are how many values are within +/- 1 std: " + str(yes))
    print("These are how many values are not within +/- 1 std: " + str(no))
    # A normal distribution will have approx. 68% of the values within this range.
    print("The percent of values that fall within +/- 1 std are: " + str((yes/counter)*100) + "%")
    # Based on this criteria is the list normally distributed?
    if ((yes/counter)) >= .68:
        print("This is normally distributed")
    else:
        print("This is not normally distributed")
    #Make a figure object
    fig = plt.figure()
    # make histogram plot with 10 bins
    plt.hist(data, bins=10)
    #Label x-axis
    plt.xlabel("Integers in [data]")
    #Label y-axis
    plt.ylabel("Count of intergers")
    #Label title
    plt.title('Histrogram of numbers');
    #Save the figure
    fig.savefig("normality_testing.png")
    print('Image save complete')


from math import sqrt, acos
import numpy as np

# parte 1, operacoes aritmeticas com vetores
def norma(vetor):
    return sqrt(sum(i ** 2 for i in vetor))

def mult_escalar(vetor, escalar):
    return [escalar * i for i in vetor]

def add_vetores(v1, v2):
    # implementacao para v1 e v2 de mesmo tamanho
    return [i + j for i, j in zip(v1, v2)]

def prod_escalar(v1, v2):
    # implementacao para v1 e v2 de mesmo tamanho
    return [i * j for i, j in zip(v1, v2)]

def prod_vetorial(a, b):
    # implementacao para v1 (a) e v2 (b) no espaço R3
    resultado = []
    resultado.append( a[1]  * b[2] - b[1] * a[2])
    resultado.append(-a[0]  * b[2] + b[0] * a[2])
    resultado.append( a[0]  * b[1] - b[0] * a[1])
    return resultado

def angulo_entre_vetores(v1, v2):
    return acos(sum(prod_escalar(v1, v2)) / (norma(v1) * norma(v2)))

# parte 2, operacoes com matrizes
def transposicao(matriz):
    return [list(x) for x in zip(*matriz)]

def mult_escalar_matriz(matriz, escalar):
    return [mult_escalar(linha, escalar) for linha in matriz]

def add_matrizes(m1, m2):
    return [add_vetores(l1, l2) for l1, l2 in zip(m1, m2)]

def mult_matrizes(m1, m2):
    # return [prod_escalar(l1, l2) for l1, l2 in zip(m1, m2)]
    np_m1 = np.matrix(m1)
    np_m2 = np.matrix(m2)
    return np_m1 * np_m2

def determinante(matriz):
    # por sarrus e somente para matriz 3x3
    aei = matriz[0][0] * matriz[1][1] * matriz[2][2]
    bfg = matriz[0][1] * matriz[1][2] * matriz[2][0]
    cdh = matriz[0][2] * matriz[1][0] * matriz[2][1]
    ceg = matriz[0][2] * matriz[1][1] * matriz[2][0]
    afh = matriz[0][0] * matriz[1][2] * matriz[2][1]
    bdi = matriz[0][1] * matriz[1][0] * matriz[2][2]
    return (aei + bfg + cdh) - (ceg + afh + bdi)

def inversa(matriz):
    # Uma matriz quadrada, cujo determinante e diferente de zero e dita
    # inversıvel e o produto de uma matriz pela sua inversa e a matriz
    # identidade
    # 1. Achar a matriz de determinantes menores;
    # 2. Achar a matriz de cofatores;
    # 3. Achar a matriz adjunta;
    # 4. Inversa = 1/det × adjunta.
    pass


import numpy as np
from numpy import ndarray

def mae(pred: ndarray, label: ndarray) -> ndarray:
    """Returns the mean absolute error between the predicted values and the
    actual values.

    Args:
        pred (ndarray): the array of predicted values.
        label (ndarray): the array of ground truths.
    Returns:
        (ndarray): mean absolute errors.
    """
    return np.mean(np.abs(label - pred))

def sse(pred: ndarray, label: ndarray) -> ndarray:
    """Returns the residual sum of squared errors between the predicted
    values and the actual values.

    Args:
        pred (ndarray): the array of predicted values.
        label (ndarray): the array of ground truths.
    Returns:
        (ndarray): residual sum of squared errors.
    """
    return np.sum(np.power((label - pred), 2))

def mse(pred: ndarray, label: ndarray) -> ndarray:
    """Returns the mean squared errors between the predicted
    values and the actual values.

    Args:
        pred (ndarray): the array of predicted values.
        label (ndarray): the array of ground truths.
    Returns:
        (ndarray): mean squared errors.
    """
   return np.mean(np.power((label - pred), 2))

def rmse(pred: ndarray, label: ndarray) -> ndarray:
    """Returns the root mean squared error between the predicted values
    and the actual values.

    Args:
        pred (ndarray): the array of predicted values.
        label (ndarray): the array of ground truths.
    Returns:
        (ndarray): root mean squared errors.
    """
  return np.sqrt(np.mean(np.power((label - pred), 2)))


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

x = np.arange(0, np.pi*2, 0.1)

y = np.sin(x)
yHalf = y/2

def rms(array):

  sumOfSquares = 0.0

  for val in array:
    sumOfSquares += val**2.0

  meanSquare = sumOfSquares / array.size

  return meanSquare ** 0.5

combinedRMS = ((rms(y)**2.0 + rms(yHalf)**2.0)/2.0)**0.5
combinedRMS2 = rms(np.concatenate((y, yHalf), axis=None))

print(f"\n RMS of y: {rms(y)}")
print(f"RMS of yHalf: {rms(yHalf)}")
print(f"Twice RMS of yHalf: {rms(yHalf) * 2.0}")
print(f"Combined RMS: {combinedRMS}")
print(f"Combined RMS2: {combinedRMS2} \n")

# plt.plot(x, y)
# plt.plot(x, yHalf)
# plt.show()


#####Gradient Methods Example
#####Muhammad Umer
#####GIT_ROOT
#####Import different packages
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
from scipy.misc import derivative

#####Define function and its derivatives in symbolic form
x= sp.symbols('x')
def f(x):
    return ((x**2)/10 - 2*sp.sin(x))
def d(x):
    return derivative(f,x)
def df(x):
    return x/5 - 2*sp.cos(x)
def ddf(x):
    return 1/5 + 2*sp.sin(x)

#####Simple Gradient Descent Function with fixed step size
def GD(x0, alpha, eps,max_iter):
    iter = 0
    y = x0
    y_new = np.zeros((max_iter,1))
    y_new[iter,] = y
    iter += 1
    y = y - alpha * df(y)
    y_new[iter,] = y
    while (np.abs(y_new[iter,] - y_new[iter-1,]) > eps and iter < max_iter):
        iter += 1
        if (iter == max_iter):
            break
        y = y - alpha* df(y)
        y_new[iter,] = y
    return y,iter,y_new

#####Steepest Gradient Descent Function with adaptive step size
def SGD(x0, alpha, eps,max_iter,gamma):
    iter = 0
    y = x0
    y_new = np.zeros((max_iter,1))
    y_new[iter,] = y
    iter += 1
    alpha = gamma * alpha
    y = y - alpha * df(y)
    y_new[iter,] = y
    while(np.abs(y_new[iter,] - y_new[iter-1,]) > eps and iter < max_iter) :
        iter += 1
        if(iter == max_iter):
            break
        y = y - alpha* df(y)
        y_new[iter,] = y
        alpha = gamma * alpha
    return y,iter,y_new

#####Steepest Gradient Descent Function with proposed adaptive step size
def SGD_proposed(x0, alpha, eps,max_iter,gamma):
    iter = 0
    y = x0
    y_new = np.zeros((max_iter,1))
    y_new[iter,] = y
    iter += 1
    alpha = (gamma**(iter)) * alpha
    y = y - alpha * df(y)
    y_new[iter,] = y
    while(np.abs(y_new[iter,] - y_new[iter-1,]) > eps and iter < max_iter) :
        iter += 1
        if(iter == max_iter):
            break
        y = y - alpha* df(y)
        y_new[iter,] = y
        alpha = (gamma**(iter)) * alpha
    return y,iter,y_new

#####Steepest Gradient Descent Function with Armijo rule reduction step size
def SGD_w_armijo(x0, s, sig, beta, max_iter):
    iter = 0
    y = x0
    alpha = s
    y_new = np.zeros((max_iter,1))
    y_new[iter,] = y
    for i in range(1,max_iter):
        alpha = s * (beta ** (i))
        y = y - alpha * df(y)
        print(y)
        y_new[i,] = y
        if ((f(y_new[i-1,]) - f(y_new[i,])) <= (1*sig *s *(beta ** (i)) * (df(y_new[i-1,]))**2)):
            break
    return y, i, y_new

#####Newton Method
def NM(x0, eps, max_iter):
        iter = 0
        y = x0
        y_new = np.zeros((max_iter, 1))
        y_new[iter,] = y
        iter += 1
        y = y - df(y)/ddf(y)
        y_new[iter,] = y
        while (np.abs(y_new[iter,] - y_new[iter - 1,]) > eps and iter < max_iter):
            iter += 1
            if (iter == max_iter):
                break
            y = y - df(y)/ddf(y)
            y_new[iter,] = y
        return y, iter, y_new

#####Call Gradient Descent with fixed step size
res,iter,y_new = GD(x0=0.5,alpha=1,eps=1e-5,max_iter  = 1000)
#####Call Gradient Descent with adaptive step size
res_sgd,iter_sgd,y_new_sgd = SGD(x0=0.5,alpha=1,eps=1e-5,max_iter  = 1000, gamma = 0.5)
#####Call Gradient Descent with proposed adaptive step size
res_sgd_p,iter_sgd_p,y_new_sgd_p = SGD_proposed(x0=0.5,alpha=1,eps=1e-5,max_iter  = 1000, gamma = 0.95)
#####Call Gradient Descent with armijo rule reduced step size
res_sgd_w_armijo,iter_sgd_w_armijo,y_new_sgd_w_armijo = SGD_w_armijo(x0=0.5,s=1, sig=1e-5, beta = 0.9, max_iter = 1000)
#####Call Newton Method
res_NM,iter_NM,y_new_NM = NM(x0=-6,eps=1e-5,max_iter  = 1000)

#####Print Results for Gradient Descent Method with fixed step size
print(res)
print(iter)
print(y_new[0:iter,])

#####Print Results for Gradient Descent Method with adaptive step size
print(res_sgd)
print(iter_sgd)
print(y_new_sgd[0:iter_sgd,])

#####Print Results for Gradient Descent Method with proposed step size
print(res_sgd_p)
print(iter_sgd_p)
print(y_new_sgd_p[0:iter_sgd_p,])

#####Print Results for Gradient Descent Method with Armijo Rule
print(res_sgd_w_armijo)
print(iter_sgd_w_armijo)
print(y_new_sgd_w_armijo[0:iter_sgd_w_armijo,])

#####Print Results for Newton Method
print(res_NM)
print(iter_NM)
print(y_new_NM[0:iter_NM,])

#####Plotting Results

y = np.linspace(-10,10)
f_orig = (np.power(y, 2))/10 - 2*np.sin(y)
plt.plot(y,f_orig)
f1 = (np.power(y_new[0:iter,], 2))/10 - 2*np.sin(y_new[0:iter,])
plt.plot(y_new[0:iter,],f1,'k--o')
plt.title('steepest descent with x0=0.5 and alpha=1')
plt.show()
plt.figure()
plt.plot(y,f_orig)
f1_sgd = (np.power(y_new_sgd[0:iter_sgd,], 2))/10 - 2*np.sin(y_new_sgd[0:iter_sgd,])
plt.plot(y_new_sgd[0:iter_sgd,],f1_sgd,'k--o')
plt.title('steepest descent with a forgetting factor rule x0=0.5, gamma = 0.5, and alpha=1')
plt.show()
plt.figure()
plt.plot(y,f_orig)
f1_sgd_p = (np.power(y_new_sgd_p[0:iter_sgd_p,], 2))/10 - 2*np.sin(y_new_sgd_p[0:iter_sgd_p,])
plt.plot(y_new_sgd_p[0:iter_sgd_p,],f1_sgd_p,'k--o')
plt.title('steepest descent with a proposed forgetting factor rule x0=0.5, gamma = 0.95, and alpha=1')
plt.show()
plt.figure()
plt.plot(y,f_orig)
f1_sgd_w_armijo = (np.power(y_new_sgd_w_armijo[0:iter_sgd_w_armijo,], 2))/10 - 2*np.sin(y_new_sgd_w_armijo[0:iter_sgd_w_armijo,])
plt.plot(y_new_sgd_w_armijo[0:iter_sgd_w_armijo,],f1_sgd_w_armijo,'k--o')
plt.show()
plt.figure()
plt.plot(y,f_orig)
f1_NM = (np.power(y_new_NM[0:iter_NM,], 2))/10 - 2*np.sin(y_new_NM[0:iter_NM,])
plt.plot(y_new_NM[0:iter_NM,],f1_NM,'k--o')
plt.title('Newton Method with x0=-6')
plt.show()