#!/usr/bin/env
# -*- coding: utf-8 -*-
# Copyright (C) Victor M. Mendiola Lau - All Rights Reserved
# Unauthorized copying of this file, via any medium is strictly prohibited
# Proprietary and confidential
# Written by Victor M. Mendiola Lau <ryuzakyl@gmail.com>, February 2017

import numpy as np

def X2(x, y, eps=1e-10):
    """Computes the Chi Squared (X2) distance value.

    Args:
        x (list): The first vector.
        y (list): The second vector.
        eps (float): Tolerance parameter to avoid zero division

    Returns:
        float: The chi squared distance between vectors x and y.

    References:
        . J. Puzicha, T. Hofmann, J. Buhmann, Non-parametric similarity measures for unsupervised
        texture segmentation and image retrieval, in: IEEE Computer Society Conference on Computer
        Vision and Pattern Recognition, CVPR ’97, 1997, pp. 267–272.

    Note:
        . Distance commonly used for histograms.

    Examples:
        >>> X2([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])
        4.0857142210006714
        >>> X2([1.0, 2.0, 3.0], [6.0, 5.0, 4.0])
        4.9999999552965164
        >>> X2([1.0, 2.0, 3.0, 20.0, 150.0, 3.0], [6.0, 5.0, 4.0, 3.0, 2.0, 1.0])
        162.67048735916615
        >>> X2([1.0, 2.0, 3.0, 20.0, 150.0, 3.0], [1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
        147.31182384490967

    """

    # getting the length of the vectors
    x_length = len(x)
    y_length = len(y)

    # validating parameters
    if x_length != y_length:
        raise Exception('Vectors with different sizes')

    # TODO: Here it is assumed that x and y are lists. Analyze the possibility for them to be tuples or numpy arrays

    # converting x and y to numpy arrays
    x_arr = np.array(x, np.float32)
    y_arr = np.array(y, np.float32)

    # converting x and y into probability distributions
    x_arr = x_arr if x_arr.min() >= 0 else x_arr + abs(x_arr.min())
    y_arr = y_arr if y_arr.min() >= 0 else y_arr + abs(y_arr.min())

    # computing the squared differences
    num = (x_arr - y_arr) ** 2

    # computing the sum of both arrays
    den = eps + x_arr + y_arr

    # computing fractions
    fracs = num / den

    # returning the sum of all the fractions
    return sum(fracs)


#!/usr/bin/env
# -*- coding: utf-8 -*-
'''
Script Name     : gauss_newton
Author          : Charles Young
Python Version  : Python 3.6.3
Requirements    : (Please check document: requirements.txt or use command "pip install -r requirements.txt")
Date     		: 2018-12-04
'''

import numpy as np

def gauss_newton(X, y, R0, tol = 1e-10, maxits = 256):
	"""Gauss-Newton algorithm.
	Parameters
	----------
	X : ndarray
		Original vectors.
	y : ndarray
		Target vectors after rotation.
	R0 : ndarray
		Initial guesses or starting estimates.
	tol : float
	    Tolerance threshold. The problem is considered solved when this value
	    becomes larger than the change of the correction vector.
	    Defaults to 1e-10.
	maxits : int
	    Maximum number of iterations of the algorithm to perform.
	    Defaults to 256.
	Return
	------
	Rn : ndarray
	    Resultant values. In this problem, the rotation matrix.
	err : float
		The standard deviation.
	its : int
	    Number of iterations performed.
	errs : list
		The standard deviations during iteration.
	"""

	dx = np.ones(len(R0))   # Correction vector
	Rn = np.array(R0)       # Approximation of solution
	errs = []

	def skew(u):
		# Span a (3,1) vector into its corresponding (3,3) skew symmetric matrix.

		return np.array([[0, -u[2], u[1]], [u[2], 0, -u[0]], [-u[1], u[0], 0]])

	def calc_step(X, y, Rn):
		# Solve function dx = inv(J'J)J'z.

		# Calculate Jacobian matrix.
		p = np.dot(Rn, X)
		# For independent errors the whole Jacobian is just the stacked matrix of individual Jacobians.
		J = np.vstack([-skew(pi) for pi in p.T])

		# Calculate error.
		err = y - p
		# For independent errors the whole error is just the stacked vector of individual errors.
		z = np.hstack([col for col in (y - p).T])

		return np.dot(np.linalg.inv(np.dot(J.T, J)), np.dot(J.T, z))

	def calc_err(X, y, R):
		# Calculate the standard deviation.
		err = y - np.dot(R, X)
		return np.trace(np.dot(err.T, err))

	its = 0
	while (its < maxits) and (dx[np.abs(dx) > tol].size > 0):
		# Calculate correction vector.
		dx = calc_step(X, y, Rn)
		# Update R.
		Rn = Rn + np.dot(skew(dx), Rn) # Adjust R.
		U,__,VT = np.linalg.svd(Rn)    # Apply SVD decomposition.
		Rn = np.dot(U, VT)             # Obtain the nearest rotation matrix.

		assert (Rn.T - np.linalg.inv(Rn) < 1e-10).all()	# Check if orthogonal.
		assert np.linalg.det(Rn) - 1 < 1e-10            # Check if its det is 1.

		# Update iteration.
		its += 1

		err = calc_err(X, y, Rn)
		errs.append(err)
		# print('  Iter: {0}, Error: {1}'.format(its, calc_err(X, y, Rn)))

	return Rn, err, its, errs

if __name__ == '__main__':

	# Load data.
	from scipy import io
	data = io.loadmat('../data/xy.mat')
	X, y = data['x'], data['y']

	U,__,VT = np.linalg.svd(np.zeros((3,3)))
	R0 = np.dot(U, VT)
	sol, err, __, __ = gauss_newton(X, y, R0)
	print("  Solution      : \r\n{}".format(sol))
	print("  Error         : {}".format(err))
	print('-'*50)


# uncomment the next line if running in a notebook
# %matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

# mass, spring constant, initial position and velocity
m = 1
k = 1
x = 0
v = 1

# simulation time, timestep and time
t_max = 100
dt = 0.1
t_array = np.arange(0, t_max, dt)

# initialise empty lists to record trajectories
x_list = []
v_list = []

# Euler integration :
for t in t_array:

    # append current state to trajectories
    x_list.append(x)
    v_list.append(v)

    # calculate new position and velocity
    a = -k * x / m
    x = x + dt * v
    v = v + dt * a

# convert trajectory lists into arrays, so they can be sliced (useful for Assignment 2)
x_array_euler = np.array(x_list)
v_array_euler = np.array(v_list)

# Verlet Integration :

x_verlet = []
v_verlet = []
x_verlet.append(0)
x_t = np.sin(0 + dt)
v_verlet.append(1)
v_v = (x_t - x_verlet[0])/dt

for i in range (0, len(t_array)-1) :

    # append current state to trajectories
    x_verlet.append(x_t)
    v_verlet.append(v_v)

    # calculate new position and velocity
    a_v = -k * x_t / m
    x_t = 2*x_t - x_verlet[-2] + (dt**2)*a_v
    v_v = (0.5/dt)*(x_t - x_verlet[-2])

x_array_verlet = np.array(x_verlet)
v_array_verlet = np.array(v_verlet)

# Analytical Solution :

x_anal = []
v_anal = []
x_a = 0
v_a = 1
i = 0

for t in t_array:

    # append current state to trajectories
    x_anal.append(x_a)
    v_anal.append(v_a)

    # calculate new position and velocity
    x_a = np.sin(t_array[i])
    v_a = np.cos(t_array[i])
    i += 1

x_array_anal = np.array(x_anal)
v_array_anal = np.array(v_anal)

# plot the position-time graph
plt.figure(1)
plt.clf()
plt.xlabel('time (s)')
plt.grid()
plt.plot(t_array, x_array_euler, label='x_euler (m)')
plt.plot(t_array, x_array_verlet, label='x_verlet (m)')
plt.plot(t_array, x_array_anal, label='x_analytical (m)')
plt.legend()
plt.show()

# plot the velocity-time graph
plt.figure(2)
plt.clf()
plt.xlabel('time (s)')
plt.grid()
plt.plot(t_array, v_array_euler, label='v_euler (m/s)')
plt.plot(t_array, v_array_verlet, label='v_verlet (m/s)')
plt.plot(t_array, v_array_anal, label='v_analytical (m/s)')
plt.legend()
plt.show()


# Copyright 2020, Battelle Energy Alliance, LLC
# ALL RIGHTS RESERVED
import numpy as np
import math
import random
from scipy.integrate import quad

def timeDepLambda(t,a,b):
  return a+t*b

def pdfFailure(t,a,b):
  first  = timeDepLambda(t,a,b)
  second = math.exp(-quad(timeDepLambda, 0, t, args=(a,b))[0])
  return first*second

def run(self,Input):
  # lambda(t) = a + t*b
  # intput: a_V1, b_V1, T (max time)
  # output: t_V1, p_V1

  self.p_V1 = np.zeros(Input['time'].size)

  for index,value in np.ndenumerate(Input['time']):
    #self.p_V1[index[0]] = quad(pdfFailure, 0, value, args=(Input['a_V1'],Input['b_V1']))[0]
    self.p_V1[index[0]] = 1. - math.exp(-quad(timeDepLambda, 0, value, args=(Input['a_V1'],Input['b_V1']))[0])


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 22 22:18:44 2017

@author: vivekparashar
"""

# Try to run the following loop using arange function instead of range function
# for i in range(2,10,2):
#    print(i)
import numpy as np
for i in np.arange(2,10,2):
    print (i)

# Write a program that uses numpy array to generate the fibonacci sequence
import numpy as np
def fib_numpy(n):
    fib = np.zeros(n)
    fib[1] = 1
    for i in range(2, n):
        fib[i] = fib[i-2] + fib[i-1]
    return fib[1:]

# Write a Python function to print all perfect numbers till 10,000
# first perfect number is 6, because 1, 2, and 3 are its proper positive divisors, and 1 + 2 + 3 = 6
# The next perfect number is 28 = 1 + 2 + 4 + 7 + 14
def perfect_number(n):
    sum = 0
    for x in range(1, n):
        if n % x == 0:
            sum += x
    return sum == n
for i in range(10000):
    if perfect_number(i)==True:
        print (i)

# Write a Python program to sum and compute the product of a numpy array elements
# hint - sum() and prod() functions
import numpy as np
x = np.array([10, 20, 30], float)
print("Original array:")
print(x)
print("Sum of the array elements:")
print(x.sum())
print("Product of the array elements:")
print(x.prod())

# Write a Python program to count the frequency of unique values in numpy array
# hint - search for np.unique
import numpy as np
a = np.array( [10,10,20,10,20,20,20,30, 30,50,40,40] )
print("Original array:")
print(a)
unique_elements, counts_elements = np.unique(a, return_counts=True)
print("Frequency of unique values of the said array:")
print(np.asarray((unique_elements, counts_elements)))

# Write a Python program to remove the negative values in a numpy array with 0.
import numpy as np
x = np.array([-1, -4, 0, 2, 3, 4, 5, -6])
print("Original array:")
print(x)
print("Replace the negative values of the said array with 0:")
x[x < 0] = 0
print(x)

# Write a program to append two arrays
# hint - append function
a=np.array([1,2,3])
b=np.array([4,5,6])
c=np.append(a,b)


import numpy as np
from sklearn.datasets import load_iris

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

def predict(xs, w, thresh = 0.5):
    probas = 1 - sigmoid(np.dot(w.T, xs))
    predictions = probas > thresh # predict value 1 when prob > threshold
    return 1*predictions

def log_likelihood(ys, xs, w):
    '''
    :param ys:
    :param xs:
    :param W:
    :return:
    '''
    n = len(ys)
    exp_term = np.exp(np.dot(w.T, xs))
    ll_terms = -np.log(1 + exp_term) + ys*np.dot(w.T, xs)
    return np.sum(ll_terms)/n

def computeGrad(ys, xs, w):
    n = len(ys)
    exp_term = np.exp(np.dot(w.T, xs))
    prob_y0 = 1/(1 + exp_term)
    dWs = xs*(ys - (1 - prob_y0))
    return np.sum(dWs, axis=1,keepdims=True)/n

def logisticReg(ys, xs, lr=0.001, max_iter = 10000):
    '''
    :param ys: 1xn
    :param xs: dxn
    :return:
    '''
    w = np.zeros((xs.shape[0],1))
    ll = log_likelihood(ys, xs, w)
    dW = computeGrad(ys, xs, w)
    new_w = w + lr * dW
    curr_ll = log_likelihood(ys, xs, new_w)
    iter_ct = 0
    while iter_ct < max_iter:
        if curr_ll >= ll:
            w = new_w
            ll = curr_ll
        dW = computeGrad(ys, xs, new_w)
        print("Grads:{}".format(dW))
        new_w = new_w + lr*dW
        curr_ll = log_likelihood(ys, xs, new_w)
        iter_ct += 1
        print("Iter:{} Best.LL:{} Curr.LL:{}".format(iter_ct, ll, curr_ll))
    return w, ll

if __name__=="__main__":
    iris = load_iris()
    x = iris.data[:, :2]
    y = (iris.target != 0) * 1
    x0 = np.ones(len(y))
    xs = np.vstack([x0.T, x.T])
    w, ll = logisticReg(y.T, xs)
    preds = predict(xs, w)
    n = len(y)
    correct = np.sum(preds == y)
    print("Likelihood:{} Accuracy:{} Weights:{}".format(ll, correct/n, w))


import numpy as np

# solving a Linear system Ax=b

# if A is invertible , then x is unique

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

b = np.array([1,2])

# the solution to this is,
x = np.linalg.inv(A).dot(b)
print(x)

# since this is such a common operation in numpy, there is a better way
# to do it

y = np.linalg.solve(A,b) # inverse method is too computationally expensive
print(y)


import numpy as np
import gd
import utils

gamma = 0.001
stepsize = 1
maxiter = 100000
plot = False

Q = np.diag([gamma, 1])

def f(x):
    return 0.5 * x.T@Q@x

def fp(x):
    return Q@x

def f_2d(x1, x2):
    return 0.5 * gamma * x1**2 + 0.5 * x2**2

x0 = np.array([1.0, 1.0])
x_traces = gd.gd_const_ss(fp, x0, stepsize=stepsize, maxiter=maxiter)

print(
    f'gamma={gamma}, stepsize={stepsize}, number of iterations={len(x_traces)-1}')

if plot:
    utils.plot_traces_2d(
        f_2d, x_traces, f'../figures/gd_traces_gamma{gamma}_ss{stepsize}.pdf')
    utils.plot(f, x_traces, f'../figures/gd_f_gamma{gamma}_ss{stepsize}.pdf')


import numpy as np

def householder_qr_decomposition(matrix):
    matrix = np.array(matrix)
    m, n = matrix.shape
    matrix_copy = matrix.copy()
    q = np.identity(m)

    def householder_transformation(column_vector):
        vector = column_vector / (column_vector[0] + np.copysign(np.linalg.norm(column_vector), column_vector[0]))
        vector[0] = 1
        tau_value = 2 / np.matmul(np.transpose(vector), vector)
        return vector, tau_value

    for j in range(n):
        v, tau = householder_transformation(matrix_copy[j:, j, np.newaxis])
        householder = np.identity(m)
        householder[j:, j:] -= tau * (np.matmul(v, np.transpose(v)))
        matrix_copy = np.matmul(householder, matrix_copy)
        q = np.matmul(householder, q)
    q = np.transpose(q)
    r = np.triu(matrix_copy[:n])
    return q, r

'''a linear system of equation is solved by Householder QR decomposition with Complete Pivoting method by following
   these steps:
        1) given system: AX = b
        2) decompose A matrix to A = QR
        3) update system to: QRX = b (hence Q is orthogonal its inverse is equal to its transpose)
        4) solve system: RX = Q⁻¹b
        5) X = R⁻¹Q⁻¹b'''

def system_solver(matrix, vector):
    qr_decomposition = householder_qr_decomposition(matrix)  # decomposing matrix to Q and R
    q_matrix = qr_decomposition[0]
    r_matrix = qr_decomposition[1]
    redacted_vector = np.matmul(q_matrix.transpose(), np.array(vector).transpose())  # fourth step
    r_matrix_inverse = np.linalg.inv(r_matrix)
    x_vector = np.matmul(r_matrix_inverse, redacted_vector)  # fifth step
    return x_vector

if __name__ == '__main__':
    coefficients_matrix = eval(input("enter A matrix like: [[1, 1, 1], [2, 2, 2], [3, 3, 3]\n"))
    right_hand_side_vector = eval(input("enter b vector like: [1, 1, 1]\n"))
    print(system_solver(coefficients_matrix, right_hand_side_vector))


import numpy as np

def linear1d(x):
    x = abs(x)
    if 0 >= x < 1:
        f = -x + 1
    else:
        f = 0
    return f

def linear2d(x, y):
    x = abs(x)
    y = abs(y)
    if 0 >= (x+y) < 1:
        f = -x-y + 1
    else:
        f = 0
    return f

def linear3d(x, y, z):
    x = abs(x)
    y = abs(y)
    z = abs(z)
    if (((x+y+z)>= 0) and ((x+y+z) <1) ):
        f = -x-y-z + 1
    else:
        f = 0
    return f

def spline1D(x, a= -0.5):
    x = abs(x)
    if (x>=0 and x<1):
        f = (a+2)*(x**3) - (a+3)*(x**2) + 1
    elif (x>=1 and x<=2):
        f = (a)*(x**3) - 5*(a)*(x**2) + 8*a*x - 4*a
    else:
        f = 0
    return f

def spline2D(x, y, a= -0.5):
    x = abs(x)
    y = abs(y)
    if ( ((x+y)>=0) and ((x+y)<1)):
        f = (a+2)*((x+y)**3) - (a+3)*((x+y)**2) + 1
    elif ((x+y)>=1 and (x+y)<=2):
        f = (a)*((x+y)**3) - 5*(a)*((x+y)**2) + 8*a*(x+y) - 4*a
    else:
        f = 0
    return f

def spline3D(x, y, z, a= -0.5):
    x = abs(x)
    y = abs(y)
    z = abs(z)
    if ( ((x+y+z)>=0) and ((x+y+z)<1)):
        f = (a+2)*((x+y+z)**3) - (a+3)*((x+y+z)**2) + 1
    elif ((x+y+z)>=1 and (x+y+z)<=2):
        f = (a)*((x+y+z)**3) - 5*(a)*((x+y+z)**2) + 8*a*(x+y+z) - 4*a
    else:
        f = 0
    return f

def bspline1D(x):
    x = abs(x)
    if ( x>=0 and x<1):
        f = 0.5*(x**3) - x**2 + 4/6
    elif (x>=1 and x<=2):
        f = (-1/6)*(x**3) + x**2 -2*x + 8/6
    else:
        f = 0
    return f

def bspline2D(x, y):
    x = abs(x)
    y = abs(y)
    if ( ((x+y)>=0) and ((x+y)<1)):
        f = (0.5)*((x+y)**3) - ((x+y)**2) + 4/6
    elif ((x+y)>=1 and (x+y)<=2):
        f = (-1/6)*((x+y)**3) + ((x+y)**2) -2*(x+y) + 8/6
    else:
        f = 0
    return f

def bspline3D(x, y, z):
    x = abs(x)
    y = abs(y)
    z = abs(z)
    if ( ((x+y+z)>=0) and ((x+y+z)<1)):
        f = (0.5)*((x+y+z)**3) - ((x+y+z)**2) + 4/6
    elif ((x+y+z)>=1 and (x+y+z)<=2):
        f = (-1/6)*((x+y+z)**3) + ((x+y+z)**2) -2*(x+y+z) + 8/6
    else:
        f = 0
    return f

def mitchell1D(x, b=1/3, c=1/3):
    x = abs(x)
    if (x >= 0 and x < 1):
        f = (1/6)*((12 - 9*b - 6*c)*(x**3) + (-18 + 12*b + 6*c)*(x**2) + 6 - 2*b)
    elif (x >= 1 and x <= 2):
        f = (1/6)*((-b - 6*c)*(x**3) + (6*b + 30*c)*(x**2) + (-12*b - 48*c)*x + 8*b + 24*c)
    else:
        f = 0
    return f

def convDownsampleKernel(kernelName, dimension, kernelSize , a=-0.5, b=1/3, c=1/3, normalizeKernel=None):
    numOfPoints = kernelSize + 2
    XInput = np.linspace(-2, 2, num=numOfPoints )
    # print ('distance between samples is {:0.2f} voxel. This is suitable for downsampling with the factor of {:0.2f} '
    #        .format(XInput[1]-XInput[0], 1/(XInput[1]-XInput[0])))
    if dimension == 1:
        if kernelName == 'linear':
            Y = np.stack([linear1d (XInput[i]) for i in range(0, len(XInput))])
        elif kernelName == 'spline':
            Y = np.stack([spline1D (XInput[i], a = a) for i in range(0, len(XInput))])
        elif kernelName == 'bspline':
            Y = np.stack([bspline1D (XInput[i]) for i in range(0, len(XInput))])
        elif kernelName == 'mitchell':
            Y = np.stack([bspline1D (XInput[i]) for i in range(0, len(XInput))])
        else:
            raise ValueError('cannot find the  kernel ' + kernelName + ' !')
        Y = Y[1:-1]

    if dimension == 2:
        YInput = np.linspace(-2, 2, num=numOfPoints )
        xv, yv = np.meshgrid(XInput, YInput)
        if kernelName == 'linear':
            Y = np.stack([linear2d(xv[i, j], yv[i, j]) for i in range(0, np.shape(xv)[0]) for j in range(0, np.shape(xv)[0])])
        if kernelName == 'spline':
            Y = np.stack([spline2D(xv[i,j],yv[i,j],  a=a) for i in range(0, np.shape(xv)[0]) for j in range(0, np.shape(xv)[0])])
        if kernelName == 'bspline':
            Y = np.stack([bspline2D(xv[i,j],yv[i,j]) for i in range(0, np.shape(xv)[0]) for j in range(0, np.shape(xv)[0])])
        Y = np.reshape(Y , [len(XInput),len(XInput)])
        Y = Y[1:-1, 1:-1]

    if dimension == 3:
        YInput = np.linspace(-2, 2, num=numOfPoints)
        ZInput = np.linspace(-2, 2, num=numOfPoints)
        xv, yv, zv = np.meshgrid(XInput, YInput, ZInput)
        if kernelName == 'linear':
            Y = np.stack([linear3d(xv[i, j, k], yv[i, j, k], zv[i, j, k]) for i in range(0, np.shape(xv)[0]) for j in range(0, np.shape(xv)[0])
                          for k in range(0, np.shape(xv)[0])])
        if kernelName == 'spline':
            Y = np.stack([spline3D(xv[i,j,k], yv[i,j,k], zv[i,j,k], a=a) for i in range(0, np.shape(xv)[0]) for j in range(0, np.shape(xv)[0])
            for k in range(0, np.shape(xv)[0])])
        if kernelName == 'bspline':
            Y = np.stack([bspline3D(xv[i,j,k], yv[i,j,k], zv[i,j,k]) for i in range(0, np.shape(xv)[0]) for j in range(0, np.shape(xv)[0])
            for k in range(0, np.shape(xv)[0])])
        Y = np.reshape(Y , [len(XInput),len(XInput), len(XInput)])
        Y = Y[1:-1, 1:-1, 1:-1]
    if normalizeKernel:
        if np.sum(Y) != normalizeKernel:
            ratio = normalizeKernel / np.sum(Y)
            Y = ratio * Y

    Y[abs(Y) < 1e-6] = 0
    return Y.astype(np.float32)

def bilinear_up_kernel(dim=3, kernel_size=3):
    """
    bilinear kernel for upsampling with transposed convolution.

    :param dim: 1, 2, 3
    :param kernel_size: 3 or 5
    :return:
    """
    if kernel_size not in [3, 5]:
        raise ValueError('kernel_size should be in [3, 5]')

    center = kernel_size // 2
    if dim == 1:
        indices = np.arange(0,3)
        indices = indices - center
        distance_to_center = np.absolute(indices)
        kernel = (np.ones(np.shape(indices)) / (np.power(2, distance_to_center))).astype(np.float32)

    elif dim == 2:
        indices = [None] * dim
        indices[0], indices[1], = np.meshgrid(np.arange(0, 3), np.arange(0, 3), indexing='ij')
        for i in range(0, dim):
            indices[i] = indices[i] - center
        distance_to_center = np.absolute(indices[0]) + np.absolute(indices[1])
        kernel = (np.ones(np.shape(indices[0])) / (np.power(2, distance_to_center))).astype(np.float32)

    elif dim == 3:
        indices = [None] * dim
        indices[0], indices[1], indices[2] = np.meshgrid(np.arange(0, 3), np.arange(0, 3), np.arange(0, 3), indexing='ij')
        for i in range(0, dim):
            indices[i] = indices[i] - center
        distance_to_center = np.absolute(indices[0]) + np.absolute(indices[1]) + np.absolute(indices[2])
        kernel = (np.ones(np.shape(indices[0])) / (np.power(2, distance_to_center))).astype(np.float32)

    else:
        raise ValueError('bilinear_up_kernel is not defined for dimension larger than 3')
    return kernel


import numpy as np
import tensorflow as tf

def softmax_1(x):
    exp = np.exp(x)
    return exp / np.sum(exp, axis=1)

def softmax_2(x):
    """
    Softmax implementation with Tensorflow2
    Args:
        x [tensor]: 1xN tensors

    Returns:
        soft_x: [tensor] softmax of x
    """
    exp = tf.exp(x)
    return exp / tf.reduce_sum(exp, 1, keepdims=True)

if __name__ == "__main__":
    x = np.array([[1, 4.2, 0.6, 1.23, 4.3, 1.2, 2.5]])
    x_tensor = tf.convert_to_tensor(x, np.float32)
    print("Input Array: ", x)
    print("Softmax Array: ", softmax_1(x))
    print("Softmax Array: ", softmax_2(x_tensor).numpy())


import numpy as np

# Creating an 1d - array
li = [1, 2, 3]
arr = np.array(li)
print(arr)

# Creating an 2d - array
li = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
arr = np.array(li)
print(arr)

# Creating an array of a number sequence
arr = np.arange(0, 10)
print(arr)
arr = np.arange(0, 10, step=2)
print(arr)

# Creating an 1-d array with zeros
arr = np.zeros(3)
print(arr)

# Creating an 2-d array with zeros
my_tuple = ((5, 5))
arr = np.zeros(my_tuple)
print(arr)

# Creating an 1-d array with ones
arr = np.ones(8)
print(arr)

# Creating an 2-d array with ones
arr = np.ones((3, 5))
print(arr)

# Creating identity matrix
arr = np.eye(5)
print(arr)

# Creating an array
# initialize in a number
# finalizes in a number
# specifying the numbers of elements between initial and final number
initial = 0
final = 1
n_numbers = 3

arr = np.linspace(initial, final, n_numbers)
print(arr)

# Creating random numbers
# between 0 and 1
# with equal problably to be selected
# extrated from a uniform distribution

# 1-dimensional
n_numbers = 5
arr = np.random.rand(n_numbers)
print(arr)

# n-dimensional
arr = np.random.rand(10, 5)
print(arr)

# Creating random numbers
# between 0 and 1
# extracted from a normal distribution

# 1-dimensional
arr = np.random.rand(5)
print(arr)

# n-dimensional
arr = np.random.rand(10, 5)
print(arr)

# Creating random numbers
# integers

# 10 random integers between 0 and 99
arr = np.random.randint(0, 100, 10)

# Round the numbers of the arrays
arr = np.random.rand(5) * 100
arr_2 = np.round(arr, decimals=0)
print(arr_2)

# Reshape
arr = np.random.rand(25)
print(arr)

tpl: tuple = (5,5)
arr_2 = arr.reshape(tpl)

print(arr_2)
print(arr_2.shape) # shape attribute

# Find the max/min value of an array
arr = np.random.rand(2)
max_value = arr.max()
min_value = arr.min()
print(arr)
print(max_value)
print(arr, min_value)

# Find the index of the max/min value of an array
arr = np.random.rand(2)
max_value = arr.argmax()
min_value = arr.argmin()
print(arr)
print(max_value)
print(min_value)
print(max_value)
print(min_value)

"""
Indexing and Slicing arrays
"""

# Select element by index
arr = np.arange(0, 30, 3)
number = arr[4]
print(number)

# Slicing: selecet multiple elements
# like a list
arr = np.arange(0, 30, 3)

numbers = arr[0:4] # first 4 - 0 == 4 index elements
print(numbers)

numbers = arr[:4] # first 4 index elements
print(numbers)

numbers = arr[4:] # elements grather or equal than 4 index
print(numbers)

# attributing values to a slice of an array
arr_2 = arr.copy()
arr_2[4:] = 100
print(arr_2)
arr_2 = arr.copy()
arr_2[4:] = 100
print(arr_2)

# Slicing n-dim arrays
arr = np.arange(50).reshape(5, 10)
print(arr)
print(arr.shape)

def compare_arrays(arr, arr_2, arr_3):
	print(arr_2, id(arr_2))
	print(arr_3, id(arr_3))

	arr_boolean = arr_2 == arr_3 # compara values
	boolean: bool = arr_boolean.all() # true if all is true
	print(arr_boolean)
	print(boolean)

	arr_2[:] = 100 # setting 100 to all array elements
	print(arr_2)
	print(arr)

# way 01: arr.[lines][columns]
arr_2 = arr.copy()[:3][:] # copy function to avoid point to arr when alter arr_2 or arr_3
arr_3 = arr.copy()[:3]

compare_arrays(arr, arr_2, arr_3)

# way 02:
# comma notation
arr_2 = arr.copy()[1:4, ]
arr_3 = arr.copy()[1:4, :]

compare_arrays(arr, arr_2, arr_3)

# Select items by Logic operations in arrays
arr = np.arange(100).reshape(10, 10)
bol = arr > 50
arr_2 = arr.copy()[bol]

print(arr)
print(bol)
print(arr_2)

"""
Numpy Array Operations
"""

arr = np.arange(0, 16)

# sum
arr_2 = arr + arr
print(arr_2)

# sub
arr_2 = arr - arr
print(arr_2)

# multiplication
arr_2 = arr * arr
print(arr_2)

# division
arr_2 = arr / arr
"""
Error message equivalent to ZeroDivisionError in array operation (which not raise an error, and returns nan instead in 0/0)
<ipython-input-105-1a13d9f299b5>:1: RuntimeWarning: invalid value encountered in true_divide
  arr_2 = arr / arr
[nan  1.  1.  1.  1.  1.  1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
"""

# Index by index division
arr_3 = 1 / arr
"""
Error message equivalent to ZeroDivisionError in array operation (which not raise an error, and returns inf instead in 1/0)
"""
print(arr_2)

# Exponentiation: index by index
# Are the same with +, - * and / operations
arr_2 = arr ** 2
print(arr_2)

# square root
arr_2 = np.sqrt(arr)

# exponentiation of all array
arr_2 = np.exp(arr)

# mean
mean: np.float64 = np.mean(arr)

# standard deviation
std: np.float64 = np.std(arr)

# sin
arr_2: np.float64 = np.sin(arr)

# the major value of the array
max_value: np.int64 = np.max(arr)
max_value: np.int64 = arr.max()

# the mininum value of the array
min_value: np.int64 = np.min(arr)
min_value: np.int64 = arr.min()


"""Measurements using spatial objects."""

import numpy as np

from skspatial.objects import Vector
from skspatial.typing import array_like

def area_triangle(point_a: array_like, point_b: array_like, point_c: array_like) -> np.float64:
    """
    Return the area of a triangle defined by three points.

    The points are the vertices of the triangle. They must be 3D or less.

    Parameters
    ----------
    point_a, point_b, point_c : array_like
        The three vertices of the triangle.

    Returns
    -------
    np.float64
        The area of the triangle.

    References
    ----------
    http://mathworld.wolfram.com/TriangleArea.html

    Examples
    --------
    >>> from skspatial.measurement import area_triangle

    >>> area_triangle([0, 0], [0, 1], [1, 0])
    0.5

    >>> area_triangle([0, 0], [0, 2], [1, 1])
    1.0

    >>> area_triangle([3, -5, 1], [5, 2, 1], [9, 4, 2]).round(2)
    12.54

    """
    vector_ab = Vector.from_points(point_a, point_b)
    vector_ac = Vector.from_points(point_a, point_c)

    # Normal vector of plane defined by the three points.
    vector_normal = vector_ab.cross(vector_ac)

    return 0.5 * vector_normal.norm()

def volume_tetrahedron(
    point_a: array_like, point_b: array_like, point_c: array_like, point_d: array_like
) -> np.float64:
    """
    Return the volume of a tetrahedron defined by four points.

    The points are the vertices of the tetrahedron. They must be 3D or less.

    Parameters
    ----------
    point_a, point_b, point_c, point_d : array_like
        The four vertices of the tetrahedron.

    Returns
    -------
    np.float64
        The volume of the tetrahedron.

    References
    ----------
    http://mathworld.wolfram.com/Tetrahedron.html

    Examples
    --------
    >>> from skspatial.measurement import volume_tetrahedron

    >>> volume_tetrahedron([0, 0], [3, 2], [-3, 5], [1, 8])
    0.0

    >>> volume_tetrahedron([0, 0, 0], [2, 0, 0], [1, 1, 0], [0, 0, 1]).round(3)
    0.333

    >>> volume_tetrahedron([0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]).round(3)
    0.167

    """
    vector_ab = Vector.from_points(point_a, point_b)
    vector_ac = Vector.from_points(point_a, point_c)
    vector_ad = Vector.from_points(point_a, point_d)

    vector_cross = vector_ac.cross(vector_ad)

    # Set the dimension to 3 so it matches the cross product.
    vector_ab = vector_ab.set_dimension(3)

    return 1 / 6 * abs(vector_ab.dot(vector_cross))


import numpy
import math
from mkRamp import mkRamp

def mkSine(*args):
    ''' IM = mkSine(SIZE, PERIOD, DIRECTION, AMPLITUDE, PHASE, ORIGIN)
                           or
        IM = mkSine(SIZE, FREQ, AMPLITUDE, PHASE, ORIGIN)

        Compute a matrix of dimension SIZE (a [Y X] 2-vector, or a scalar)
        containing samples of a 2D sinusoid, with given PERIOD (in pixels),
        DIRECTION (radians, CW from X-axis, default = 0), AMPLITUDE (default
        = 1), and PHASE (radians, relative to ORIGIN, default = 0).  ORIGIN
        defaults to the center of the image.

        In the second form, FREQ is a 2-vector of frequencies (radians/pixel).

        Eero Simoncelli, 6/96. Python version by Rob Young, 7/15.  '''

    # REQUIRED args:

    if len(args) < 2:
        print "mkSine(SIZE, PERIOD, DIRECTION, AMPLITUDE, PHASE, ORIGIN)"
        print "       or"
        print "mkSine(SIZE, FREQ, AMPLITUDE, PHASE, ORIGIN)"
        print "first two arguments are required"
        exit(1)
    else:
        sz = args[0]
        if isinstance(sz, (int)):
            sz = (sz, sz)
        elif not isinstance(sz, (tuple)):
            print "first argument must be a two element tuple or an integer"
            exit(1)

    if isinstance(args[1], (int, float, long)):
        frequency = (2.0 * numpy.pi) / args[1]
        # OPTIONAL args:
        if len(args) > 2:
            direction = args[2]
        else:
            direction = 0
        if len(args) > 3:
            amplitude = args[3]
        else:
            amplitude = 1
        if len(args) > 4:
            phase = args[4]
        else:
            phase = 0
        if len(args) > 5:
            origin = args[5]
        else:
            origin = 'not set'
    else:
        frequency = numpy.linalg.norm(args[1])
        direction = math.atan2(args[1][0], args[1][1])
        # OPTIONAL args:
        if len(args) > 2:
            amplitude = args[2]
        else:
            amplitude = 1
        if len(args) > 3:
            phase = args[3]
        else:
            phase = 0
        if len(args) > 4:
            origin = args[4]
        else:
            origin = 'not set'

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

    if origin == 'not set':
        res = amplitude * numpy.sin(mkRamp(sz, direction, frequency, phase))
    else:
        res = amplitude * numpy.sin(mkRamp(sz, direction, frequency, phase,
                                           [origin[0]-1, origin[1]-1]))

    return res


from numpy import power

from optmlstat.opt.iteration import Iteration
from optmlstat.opt.learning_rate.learning_rate_strategy import LearningRateStrategy

class VanishingLearningRateStrategy(LearningRateStrategy):
    """
    Implements vanishing (or decreasing) learning rate strategy (depending on outer iteration).
    The learning rate is

      eta = a / (outer_iteration + b)^k

    where k is a positive exponent controlling decreasing rate.
    """

    def __init__(self, initial_value: float, exponent: float, half_life: int) -> None:
        """
        The learning rate is given by

         eta = a / (outer_iteration + b)^k

        where k is the exponent. The other two constants a and b are determined by initial_value and half_life,
        i.e.,

        a / (1 + b)^k = initial_value
        a / (half_life + b)^k = initial_value / 2

        Therefore

        (half_life + b)/(1 + b) = 2^(1/k) <=> (2^(1/k) - 1) b = half_life - 2^(1/k)
         <=> b = (half_life - 2^(1/k)) / (2^(1/k) - 1)

        and

        a = initial_value * (1 + b)^k

        Parameters
        ----------
        initial_value:
         initial_value
        exponent:
         exponent
        half_life:
         half_life
        """
        assert exponent > 0.0
        assert half_life > 1

        # In this class, self.constant_learning_rate is the initial value.
        super(VanishingLearningRateStrategy, self).__init__(initial_value)

        self.exponent: float = exponent
        self.half_life: int = half_life

        tmp_val: float = power(2.0, 1.0 / exponent)

        self.constant_b: float = float(self.half_life - tmp_val) / (tmp_val - 1.0)
        self.constant_a: float = float(self.constant_learning_rate) * power(1.0 + self.constant_b, self.exponent)

    def get_learning_rate(self, iteration: Iteration) -> float:
        assert iteration.outer_iteration > 0
        return self.constant_a / power(float(iteration.outer_iteration) + self.constant_b, self.exponent)


import numpy as np
import math

def functn(ifunc, nopt, x):
    if ifunc == 1:
        return goldstein_price(nopt, x)

    if ifunc == 2:
        return rosenbrock(nopt, x)

    if ifunc == 3:
        return six_hump_camelback(nopt, x)

    if ifunc == 4:
        return rastrigin(nopt, x)

    if ifunc == 5:
        return griewank(nopt, x)

    if ifunc == 6:
        return shekel(nopt, x)

    if ifunc == 7:
        return hartman(nopt, x)

    if ifunc == 8:
        return hosaki(nopt, x)

def goldstein_price(nopt, x):
    #
    # This is the Goldstein-Price Function
    # Bound X1=[-2,2], X2=[-2,2]
    # Global Optimum: 3.0,(0.0,-1.0)
    x1 = x[0]
    x2 = x[1]
    u1 = (x1 + x2 + 1.0) ** 2
    u2 = 19. - 14. * x1 + 3. * x1 ** 2 - 14. * x2 + 6. * x1 * x2 + 3. * x2 ** 2
    u3 = (2. * x1 - 3. * x2) ** 2
    u4 = 18. - 32. * x1 + 12. * x1 ** 2 + 48. * x2 - 36. * x1 * x2 + 27. * x2 ** 2
    u5 = u1 * u2
    u6 = u3 * u4
    f = (1. + u5) * (30. + u6)
    return f

def rosenbrock(nopt, x):
    #
    # This is the Rosenbrock Function
    # Bound: X1=[-5,5], X2=[-2,8]
    # Global Optimum: 0,(1,1)
    x1 = x[0]
    x2 = x[1]
    a = 100
    f = a * (x2 - x1 ** 2) ** 2 + (1 - x1) ** 2

    return f

def six_hump_camelback(nopt, x):
    #
    #  This is the Six-hump Camelback Function.
    #  Bound: X1=[-5,5], X2=[-5,5]
    #  True Optima: -1.031628453489877, (-0.08983,0.7126), (0.08983,-0.7126)
    #
    x1 = x[0]
    x2 = x[1]
    f = (4 - 2.1 * x1 ** 2 + (x1 ** 4) / 3) * x1 ** 2 + x1 * x2 + (-4 + 4 * x2 ** 2) * x2 ** 2

    return f

def rastrigin(nopt, x):
    #
    #  This is the Rastrigin Function
    #  Bound: X1=[-1,1], X2=[-1,1]
    #  Global Optimum: -2, (0,0)

    x1 = x[0]
    x2 = x[1]
    f = x1 ** 2 + x2 ** 2 - math.cos(18.0 * x1) - math.cos(18.0 * x2)

    return f

def griewank(nopt, x):
    #
    #  This is the Griewank Function (2-D or 10-D)
    #  Bound: X(i)=[-600,600], for i=1,2,...,10
    #  Global Optimum: 0, at origin
    if nopt == 2:
        d = 200
    else:
        d = 4000

    u1 = 0.0
    u2 = 1.0
    for j in range(nopt):
        u1 = u1 + x[j] ** 2 / d
        u2 = u2 * math.cos(x[j] / math.sqrt(j + 1))
    f = u1 - u2 + 1

    return f

def shekel(nopt, x):
    #
    #  This is the Shekel Function
    #  Bound: X(i)=[0,10], j=1,2,3,4
    #  Global Optimum:-10.5364098252,(4,4,4,4)
    #
    #  Data for Skekel function coefficients (n=4, m=10)

    a1 = np.array([[4., 1., 8., 6., 3., 2., 5., 8., 6., 7.],
                   [4., 1., 8., 6., 7., 9., 5., 1., 2., 3.6],
                   [4., 1., 8., 6., 3., 2., 3., 8., 6., 7.],
                   [4., 1., 8., 6., 7., 9., 3., 1., 2., 3.6]])
    c1 = np.array([.1, .2, .2, .4, .4, .6, .3, .7, .5, .5])

    f = 0.0
    for i in range(10):
        u = 0.0
        for j in range(nopt):
            u = u + (x[j] - a1[j, i]) ** 2

        u = 1.0 / (u + c1[i])
        f = f - u

    return f

def hartman(nopt, x):
    #
    #   This is the Hartman Function
    #   Bound: X(j)=[0,1], j=1,2,...,6
    #   Global Optimum:-3.322368011415515,
    #   (0.201,0.150,0.477,0.275,0.311,0.657)
    #
    #   Data for Hartman function coefficients (6-D)
    a2 = np.array([[10., 0.05, 3., 17.],
                   [3., 10., 3.5, 8.],
                   [17., 17., 1.7, .05],
                   [3.5, 0.1, 10., 10.],
                   [1.7, 8., 17., .1],
                   [8., 14., 8., 14.]])
    c2 = np.array([1., 1.2, 3., 3.2])
    p2 = np.array([[.1312, .2329, .2348, .4047],
                   [.1696, .4135, .1451, .8828],
                   [.5569, .8307, .3522, .8732],
                   [.0124, .3736, .2883, .5743],
                   [.8283, .1004, .3047, .1091],
                   [.5886, .9991, .6650, .0381]])

    #  Data for Hartman function coefficient (3-D)
    a3 = np.array([[3., .1, 3., .1],
                   [10., 10., 10., 10.],
                   [30., 35., 30., 35.]])
    c3 = np.array([1., 1.2, 3., 3.2])
    p3 = np.array([[.3689, .4699, .1091, .03815],
                   [.1170, .4387, .8732, .5743],
                   [.2673, .7470, .5547, .8828]])

    f = 0.0
    for i in range(4):
        u = 0.0
        for j in range(nopt):
            if nopt == 3:
                a2[j, i] = a3[j, i]
                p2[j, i] = p3[j, i]

            u = u + a2[j, i] * (x[j] - p2[j, i]) ** 2

        if nopt == 3:
            c2[i] = c3[i]
        f = f - c2[i] * math.exp(-u)

    return f

def hosaki(nopt, x):
    #
    # This is the Hosaki Function
    #  Bound: X1=[0, 5], X2=[0, 6]
    #  Global Optimum: -2.3458, (4,2))
    x1 = x[0]
    x2 = x[1]
    f = (1 - 8 * x1 + 7 * pow(x1, 2) - 7 * pow(x1, 3) / 3 + pow(x1, 4) / 4) * pow(x2, 2) * math.exp(-x2)

    return f


import numpy as np
from scipy.linalg import toeplitz
from numpy.fft import fft

def cheb_dif(N, M):
    I = np.eye(N)

    n1 = int(np.floor(N / 2))
    n2 = int(np.ceil(N / 2))

    k = np.array([np.arange(0, N)]).T
    th = k * np.pi / (N - 1)

    # Compute Chebyshev points.
    vec = np.arange(N - 1, 1 - N - 1, -2)
    x = np.sin(np.pi * vec / (2 * (N - 1)))

    T = np.tile(th / 2, (1, N))  # Like repmat(th/2, 1, N) for 2nd order tensors.
    Tt = T.T

    DX = 2 * np.sin(Tt + T) * np.sin(Tt - T)
    DX = np.vstack([DX[0:n1, :], -np.flipud(np.fliplr(DX[0:n2, :]))])

    DX[range(N), range(N)] = 1.0

    C = toeplitz((-1.0) ** k)
    C[0, :] = C[0, :] * 2.0
    C[N - 1, :] = C[N - 1, :] * 2.0
    C[:, 0] = C[:, 0] / 2.0
    C[:, N - 1] = C[:, N - 1] / 2.0

    Z = 1.0 / DX
    Z[range(N), range(N)] = 0.0

    D = np.eye(N)
    DM = np.zeros([N, N, M])

    for ell in range(1, M + 1):
        D = ell * Z * (C * np.tile(np.array([np.diag(D)]).T, (1, N)) - D)
        diag = -np.sum(D, 1)
        D[range(N), range(N)] = diag

        DM[:, :, ell - 1] = D

    return (x, DM)

def cosine_transform(f, axis=0):
    """
    :type f: numpy.array, 2D.
    """
    if axis == 1:
        f = f.T

    sz = f.shape
    N = sz[0]
    even_extension = np.vstack((f, np.flipud(f[1:N - 1, :])))
    cc = fft(even_extension, axis=0)
    result = cc[:N, :]
    if np.isreal(f).all():
        result = np.real(result)

    if axis == 1:
        return result.T

    return result

def cheb_derivative(f, axis=0):
    if axis == 1:
        f = f.T

    sz = f.shape
    N = sz[0]

    cc = cosine_transform(np.real(f))
    dd = np.zeros(f.shape)

    dd[N-2, :] = 2*(N-1)*cc[N-1, :]
    for k in range(N-3, -1, -1):
        dd[k, :] = dd[k+2, :] + 2.*(k+1.)*cc[k+1, :]

    dd = cosine_transform(np.real(dd))/(2.*(N-1.))

    if axis == 1:
        return dd.T

    return dd


import numpy as np
from keris.layers.layer import Layer

class Softmax(Layer):

    """
    Compute softmax in a numerically stable way
    https://eli.thegreenplace.net/2016/the-softmax-function-and-its-derivative/
    """

    def __init__(self, name, axis=-1):
        super().__init__(name)
        self.axis = axis

    def forward(self, x, mode):
        out = np.exp(x - np.max(x, axis=self.axis, keepdims=True))
        out /= np.sum(out, axis=self.axis, keepdims=True)
        self.x, self.out = x, out

        return out

    def backward(self, dout, mode):
        """
        https://stackoverflow.com/a/40576872
        """
        x, out = self.x, self.out
        dx = np.zeros_like(dout)

        for i in range(len(out)):
            for j in range(len(x)):
                if i == j:
                    dx[i, j] = out[i] * (1 - x[i])
                else:
                    dx[i, j] = -out[i] * x[j]
        return dx, None


import seaborn as sns
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import norm
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset

sns.set_style("darkgrid")

df = pd.read_csv("ibex_35.csv")

df = df.dropna()

log_returns = np.diff(np.log(df["Close"].values))

mu , std = norm.fit(log_returns)

fig , ax = plt.subplots(figsize=(12,8))
plt.rcParams.update({'font.size': 16})
ret = plt.hist(log_returns , bins = 100 , density = True , color="dodgerblue" , label = "Empirical Distribution")
xmin , xmax = plt.xlim()

x = np.linspace(xmin , xmax , 100)
p = norm.pdf(x , mu , std)
plt.plot(x , p ,'k' , linewidth=2 , label = "Gaussian Fit")
plt.legend(loc=2)

plt.title("IBEX-35 Log Returns Distribution")
plt.xlabel("$R_{(t)}$")

axins1 = zoomed_inset_axes(ax, zoom = 5, loc=1)
axins1.hist(log_returns , bins=100 , density = True , color="dodgerblue" , label="Fat Tails")
axins1.plot(x,p , 'k')
axins1.legend(loc=1)
x1, x2, y1, y2 = 0.035,0.060,0.05,2
axins1.set_xlim(x1, x2)
axins1.set_ylim(y1, y2)
plt.yticks(visible=False)
mark_inset(ax, axins1, loc1=4, loc2=3, fc="none", ec="0.5")
plt.savefig("gaussian_distributionibex.pdf")


import numpy as np

def polar2cart(ra,dec):

    x = np.cos(np.deg2rad(ra)) * np.cos(np.deg2rad(dec))
    y = np.sin(np.deg2rad(ra)) * np.cos(np.deg2rad(dec))
    z = np.sin(np.deg2rad(dec))

    return np.array([x,y,z])

def cart2polar(vector):

    ra = np.arctan2(vector[1],vector[0])
    dec = np.arcsin(vector[2])

    return np.rad2deg(ra), np.rad2deg(dec)


import numpy as np
import math
from numpy import linalg as LA
import matplotlib.pyplot as plt
import matplotlib.colors as ListedColormap
from numpy.linalg import inv

"""
The class has all the attribute which does not depend on the training examples
and are common in most of the calculations

The class has mainly two methods:
    fit - it finds the class of the data point passed as argument
    classifier - it returns an array of predicted values for the array of data
    points passed as arguments

    the subscript represents the case on covariance matrix according to the
    assignment.
"""
class BayesClassifier:
    class_i = 0
    class_j = 0
    xi_train = np.array([])
    yi_train = np.array([])
    xj_train = np.array([])
    yj_train = np.array([])
    P_ci = 0
    P_cj = 1
    mu_i = np.array([])
    mu_j = np.array([])
    cov_matrix_i = np.array([])
    cov_matrix_j = np.array([])
    total_size = 0

    def __init__(self, class_i, class_j, xi_train, yi_train, xj_train, yj_train, total_size):
        self.class_i = class_i
        self.class_j = class_j
        self.xi_train = xi_train
        self.yi_train = yi_train
        self.xj_train = xj_train
        self.yj_train = yj_train
        self.P_ci = np.size(yi_train) / total_size
        self.P_cj = np.size(yj_train) / total_size
        self.mu_i = np.array([np.mean(xi_train[:, 0]), np.mean(xi_train[:, 1])])
        self.mu_j = np.array([np.mean(xj_train[:, 0]), np.mean(xj_train[:, 1])])
        self.cov_matrix_i = np.cov(np.transpose(xi_train))
        self.cov_matrix_j = np.cov(np.transpose(xj_train))
        self.total_size = total_size

    def fit_one(self, x_vec):
        cov = (self.cov_matrix_i[0][0]+self.cov_matrix_j[0][0]+self.cov_matrix_i[1][1]+self.cov_matrix_j[1][1])/4
        dev_term = cov*cov*math.log(self.P_ci/self.P_cj)*(self.mu_i - self.mu_j)/(LA.norm(self.mu_i-self.mu_j)*LA.norm(self.mu_i-self.mu_j))
        x0 = (self.mu_i + self.mu_j)/2 - dev_term
        w = (self.mu_i - self.mu_j)
        return np.dot(w, x_vec) - np.dot(w, x0)

    def classifier_one(self, X_test):
        value = []
        for x_vec in X_test:
            if (self.fit_one(x_vec) > 0):
                value.append(self.class_i)
            else:
                value.append(self.class_j)

        return np.array(value)

    def fit_two(self, x_vec):
        avg_cov = np.array([[(self.cov_matrix_i[0][0]+self.cov_matrix_j[0][0])/2, (self.cov_matrix_i[0][1]+self.cov_matrix_j[0][1])/2],
                             [(self.cov_matrix_i[1][0]+self.cov_matrix_j[1][0])/2, (self.cov_matrix_i[1][1]+self.cov_matrix_j[1][1])/2]])
        dev_term = math.log(self.P_ci/self.P_cj)*(self.mu_i - self.mu_j)/(np.dot((self.mu_i - self.mu_j), inv(avg_cov).dot(self.mu_i - self.mu_j)))
        x0 = (self.mu_i + self.mu_j)/2 - dev_term
        w = inv(avg_cov).dot(self.mu_i - self.mu_j)
        return np.dot(w, x_vec) - np.dot(w, x0)

    def classifier_two(self, X_test):
        value = []
        for x_vec in X_test:
            if (self.fit_two(x_vec) > 0):
                value.append(self.class_i)
            else:
                value.append(self.class_j)

        return np.array(value)

    def fit_three(self, x_vec):
        dia_i = np.array([[self.cov_matrix_i[0][0], 0], [0, self.cov_matrix_i[1][1]]])
        W_i = -1/2 * inv(dia_i)
        w_i = inv(dia_i).dot(self.mu_i)
        w_i0 = -1/2 * np.dot(self.mu_i, w_i) - 1/2 * math.log(abs(np.linalg.det(dia_i))) + math.log(self.P_ci)
        g_i = np.dot(x_vec, W_i.dot(x_vec)) + np.dot(w_i, x_vec) + w_i0

        dia_j = np.array([[self.cov_matrix_j[0][0], 0], [0, self.cov_matrix_j[1][1]]])
        W_j = -1/2 * inv(dia_j)
        w_j = inv(dia_j).dot(self.mu_j)
        w_j0 = -1/2 * np.dot(self.mu_j, w_j) - 1/2 * math.log(abs(np.linalg.det(dia_j))) + math.log(self.P_cj)
        g_j = np.dot(x_vec, W_j.dot(x_vec)) + np.dot(w_j, x_vec) + w_j0

        return g_i - g_j

    def classifier_three(self, X_test):
        value = []
        for x_vec in X_test:
            if (self.fit_three(x_vec) > 0):
                value.append(self.class_i)
            else:
                value.append(self.class_j)

        return np.array(value)

    def fit_four(self, x_vec):
        W_i = -1/2 * inv(self.cov_matrix_i)
        w_i = inv(self.cov_matrix_i).dot(self.mu_i)
        w_i0 = -1/2 * np.dot(self.mu_i, w_i) - 1/2 * math.log(abs(np.linalg.det(self.cov_matrix_i))) + math.log(self.P_ci)
        g_i = np.dot(x_vec, W_i.dot(x_vec)) + np.dot(w_i, x_vec) + w_i0

        W_j = -1/2 * inv(self.cov_matrix_j)
        w_j = inv(self.cov_matrix_j).dot(self.mu_j)
        w_j0 = -1/2 * np.dot(self.mu_j, w_j) - 1/2 * math.log(abs(np.linalg.det(self.cov_matrix_j))) + math.log(self.P_cj)
        g_j = np.dot(x_vec, W_j.dot(x_vec)) + np.dot(w_j, x_vec) + w_j0

        return g_i - g_j

    def classifier_four(self, X_test):
        value = []
        for x_vec in X_test:
            if (self.fit_four(x_vec) > 0):
                value.append(self.class_i)
            else:
                value.append(self.class_j)

        return np.array(value)

    def contour_curve(self, X1, X2, cov_matrix):
        return (1/math.sqrt(2*3.1417*abs(np.linalg.det(cov_matrix))))*pow(2.71828, -(cov_matrix[0][0]*X2*X2
               + cov_matrix[1][1]*X1*X1 - 2*cov_matrix[0][1]*X1*X2)/2)

    def contour_func_i(self, x1, x2, case):
        if (case == 1):
            cov = (self.cov_matrix_i[0][0]+self.cov_matrix_j[0][0]+self.cov_matrix_i[1][1]+self.cov_matrix_j[1][1])/4
            cov_matrix = np.array([[cov, 0], [0, cov]])
        elif (case == 2):
            cov_matrix = np.array([[(self.cov_matrix_i[0][0]+self.cov_matrix_j[0][0])/2, (self.cov_matrix_i[0][1]+self.cov_matrix_j[0][1])/2],
                             [(self.cov_matrix_i[1][0]+self.cov_matrix_j[1][0])/2, (self.cov_matrix_i[1][1]+self.cov_matrix_j[1][1])/2]])
        elif (case == 3):
            cov_matrix = np.array([[self.cov_matrix_i[0][0], 0], [0, self.cov_matrix_i[1][1]]])
        elif (case == 4):
            cov_matrix = self.cov_matrix_i

        X1 = x1 - self.mu_i[0]
        X2 = x2 - self.mu_i[1]

        return self.contour_curve(X1, X2, cov_matrix)

    def contour_func_j(self, x1, x2, case):
        if (case == 1):
            cov = (self.cov_matrix_i[0][0]+self.cov_matrix_j[0][0]+self.cov_matrix_i[1][1]+self.cov_matrix_j[1][1])/4
            cov_matrix = np.array([[cov, 0], [0, cov]])
        elif (case == 2):
            cov_matrix = np.array([[(self.cov_matrix_i[0][0]+self.cov_matrix_j[0][0])/2, (self.cov_matrix_i[0][1]+self.cov_matrix_j[0][1])/2],
                             [(self.cov_matrix_i[1][0]+self.cov_matrix_j[1][0])/2, (self.cov_matrix_i[1][1]+self.cov_matrix_j[1][1])/2]])
        elif (case == 3):
            cov_matrix = np.array([[self.cov_matrix_j[0][0], 0], [0, self.cov_matrix_j[1][1]]])
        elif (case == 4):
            cov_matrix = self.cov_matrix_j

        X1 = x1 - self.mu_j[0]
        X2 = x2 - self.mu_j[1]

        return self.contour_curve(X1, X2, cov_matrix)


'''

hypothesis
    h(O) = 1/(1+e^(-O^Tx))

cost function
    cost(h(x), y) = -log(h(x)) if y == 1 this function will penalize learning algorithm by big margin if h(x) == 0
    cost(h(x), y) = -log(1-h(x)) if y == 0 this function will penalize learning algorithm by big margin if h(x) == 1

    'more compact way for cost writing cost function for both cases'
    cost(h(x), y) = -y*log(h(x)) - (1-y)*log(1-h(x))
    J(O) = (1/2*m) * sum(cost(h(x), y))

fit parameters with gradient descent
    Oj = Oj - learning_rate * partial_derivative(J(O))
    partial_derivative = 1/m * sum((h(xi) - yi) * xij)

'''

import numpy as np

class LogisticRegression:
    def __init__(self, learning_rate=0.001, iters=10000, fit_intercept=True):
        self.learning_rate = learning_rate
        self.iters = iters
        self.fit_intercept = fit_intercept

    def __add_intercept(self, X):
        intercept = np.ones((X.shape[0], 1))
        return np.concatenate((intercept, X), axis=1)

    def __sigmoid(self, z):
        """
        sigmoid function
        :param z: function parameter z
        :return:
        """
        return 1 / (1 + np.exp(-z))

    def __cost(self, h, y):
        """
        Cost function
        :param h: hypothesis value
        :param y: actual value
        :return:
        """
        return (-y * np.log(h) - (1 - y) * np.log(1 - h))/(2*y.size)

    def fit(self, X, y):
        if self.fit_intercept:
            X = self.__add_intercept(X)

        self.theta = np.zeros(X.shape[1])

        for i in range(self.iters):
            z = np.dot(X, self.theta)
            h = self.__sigmoid(z)
            for j in range(len(self.theta)):
                gradient = np.dot(h-y, X[:, j])
                self.theta[j] = self.theta[j] - (self.learning_rate/y.size) * gradient

        print(self.__cost(h, y))

    def predict_prob(self, X):
        if self.fit_intercept:
            X = self.__add_intercept(X)

        return self.__sigmoid(np.dot(X, self.theta))

    def predict(self, X, threshold=0.5):
        return self.predict_prob(X) >= threshold

if __name__ == '__main__':
    from sklearn import datasets

    iris = datasets.load_iris()
    X = iris.data[:, :2]
    y = (iris.target != 0) * 1

    model = LogisticRegression()
    model.fit(X, y)


import numpy as np
import matplotlib

class DFT:
    @staticmethod
    def slow_one_dimension(a):
        a = np.asarray(a, dtype=complex)
        N = a.shape[0]
        res = np.zeros(N, dtype=complex)

        for k in range(N):
            for n in range(N):
                res[k] += a[n] * np.exp(-2j * np.pi * k * n / N)

        return res

    @staticmethod
    def slow_one_dimension_inverse(a):
        a = np.asarray(a, dtype=complex)
        N = a.shape[0]
        res = np.zeros(N, dtype=complex)

        for n in range(N):
            for k in range(N):
                res[n] += a[k] * np.exp(2j * np.pi * k * n / N)

            res[n] /= N

        return res

    @staticmethod
    def fast_one_dimension(a):
        a = np.asarray(a, dtype=complex)
        N = a.shape[0]

        if N % 2 != 0:
            raise AssertionError("size of a must be a power of 2")
        elif N <= 16:
            return DFT.slow_one_dimension(a)
        else:
            even = DFT.fast_one_dimension(a[::2])
            odd = DFT.fast_one_dimension(a[1::2])
            res = np.zeros(N, dtype=complex)

            half_size = N//2
            for n in range(N):
                res[n] = even[n % half_size] + \
                    np.exp(-2j * np.pi * n / N) * odd[n % half_size]

            return res

    @staticmethod
    def fast_one_dimension_inverse(a):
        a = np.asarray(a, dtype=complex)
        N = a.shape[0]

        if N % 2 != 0:
            raise AssertionError("size of a must be a power of 2")
        elif N <= 16:
            return DFT.slow_one_dimension_inverse(a)
        else:
            even = DFT.fast_one_dimension_inverse(a[::2])
            odd = DFT.fast_one_dimension_inverse(a[1::2])
            res = np.zeros(N, dtype=complex)

            half_size = N//2
            for n in range(N):
                res[n] = half_size * even[n % half_size] + \
                    np.exp(2j * np.pi * n / N) * half_size * odd[n % half_size]
                res[n] /= N

            return res

    @staticmethod
    def slow_two_dimension(a):
        a = np.asarray(a, dtype=complex)
        N, M = a.shape
        res = np.zeros((N, M), dtype=complex)

        for k in range(N):
            for l in range(M):
                for m in range(M):
                    for n in range(N):
                        res[k, l] += a[n, m] * \
                            np.exp(-2j * np.pi * ((l * m / M) + (k * n / N)))

        return res

    @staticmethod
    def slow_two_dimension_inverse(a):
        a = np.asarray(a, dtype=complex)
        N, M = a.shape
        res = np.zeros((N, M), dtype=complex)

        for k in range(N):
            for l in range(M):
                for m in range(M):
                    for n in range(N):
                        res[k, l] += a[n, m] * \
                            np.exp(2j * np.pi * ((l * m / M) + (k * n / N)))
                res[k, l] /= M * N

        return res

    @staticmethod
    def fast_two_dimension(a):
        a = np.asarray(a, dtype=complex)
        N, M = a.shape
        res = np.zeros((N, M), dtype=complex)

        for col in range(M):
            res[:, col] = DFT.fast_one_dimension(a[:, col])

        for row in range(N):
            res[row, :] = DFT.fast_one_dimension(res[row, :])

        return res

    @staticmethod
    def fast_two_dimension_inverse(a):
        a = np.asarray(a, dtype=complex)
        N, M = a.shape
        res = np.zeros((N, M), dtype=complex)

        for row in range(N):
            res[row, :] = DFT.fast_one_dimension_inverse(a[row, :])

        for col in range(M):
            res[:, col] = DFT.fast_one_dimension_inverse(res[:, col])

        return res

    @staticmethod
    def test():
        # one dimension
        a = np.random.random(1024)
        fft = np.fft.fft(a)

        # two dimensions
        a2 = np.random.rand(32, 32)
        fft2 = np.fft.fft2(a2)

        tests = (
            (DFT.slow_one_dimension, a, fft),
            (DFT.slow_one_dimension_inverse, fft, a),
            (DFT.fast_one_dimension, a, fft),
            (DFT.fast_one_dimension_inverse, fft, a),
            (DFT.slow_two_dimension, a2, fft2),
            (DFT.slow_two_dimension_inverse, fft2, a2),
            (DFT.fast_two_dimension, a2, fft2),
            (DFT.fast_two_dimension_inverse, fft2, a2)
        )

        for method, args, expected in tests:
            if not np.allclose(method(args), expected):
                print(args)
                print(method(args))
                print(expected)
                raise AssertionError(
                    "{} failed the test".format(method.__name__))


import numpy as np
import numpy.linalg as la
from auxiliary import *

A = np.array([
    [-5.773502691896260e-01, -5.773502691896260e-01, 1],
    [ 5.773502691896260e-01, -5.773502691896260e-01, 1],
    [-5.773502691896260e-01,  5.773502691896260e-01, 1],
    [ 5.773502691896260e-01,  5.773502691896260e-01, 1],
], dtype=float)

U, s, Vt = la.svd(A)

mprint('amat', A)
mprint('uCorrect',  U)
vprint('sCorrect',  s)
mprint('vtCorrect', Vt)

m, n = np.shape(A)
ns   = min([m, n])

S = np.zeros((m, n))
for i in range(ns):
    S[i,i] = s[i]
USVt = np.dot(U, np.dot(S, Vt))

print 'A    =\n', A
print 'USVt =\n', USVt
print np.allclose(A, USVt)


import numpy as np

G = 6.67e-11

def velocity_squared(solution, N, number_of_points):
    '''
    A function that returns the square of all velocities at all times for all bodies in the system by cycling through the solution and grabbing each velocity vector.
    Args:
        solution: array containing the solution of an N-body system evolving under gravity. Created from N_body_solver in the
                    N_body_simulator.py module.
        N: integer number of bodies.
        number_of_points: integer number of time steps used in N_body_solver simulation.
    Returns:
        velocities: array containing the square velocities of each body within the system for each point in time.
    '''
    velocities = np.zeros((N, number_of_points))
    for i in range(N):
        vx_start = 6*i + 3
        vy_start = vx_start + 1
        vz_start = vx_start + 2
        vx_squared = solution[:,vx_start]**2
        vy_squared = solution[:,vy_start]**2
        vz_squared = solution[:,vz_start]**2
        v_squared = vx_squared + vy_squared + vz_squared
        velocities[i,:] = v_squared

    return velocities

def PE_calculator(N, masses, x_positions, y_positions, z_positions):
    '''
    A simple function that calculates the potential energy of the system given N bodies. Used in system_energy function below.
    This is a vectorised calculation.
    Args:
        N: integer number of bodies.
        masses: array containing masses in order of their initial conditions given to N_body_solver.
        x_positions: array containing all x positions for all bodies across all time.
        y_positions: as above, for y.
        z_positions: as above, for z.
    Returns:
        PE: array of system potential energy across all time.
    '''
    PE = 0
    # Cycles through each body i
    for i in range(N):
        # Then every other body, skipping itself and preventing double counting
        for j in range(i, N):
            if j == i:
                pass
            else:
                separation = np.sqrt( (x_positions[i,:] - x_positions[j,:])**2 +\
                                    (y_positions[i,:] - y_positions[j,:])**2 +\
                                    (z_positions[i,:] - z_positions[j,:])**2 )
                PE += -G*masses[i]*masses[j]/separation
    return PE

def system_energy(solution, masses, N, number_of_points):
    '''
    This function calculates the energy of the system over the run-time of the simulation. It grabs positions for each body over all time,
    calculates the potential energy of the system and kinetic energy of the bodies, and adds them. The function then returns the system
    energy over all time.
    Args:
        solution: array containing the positions and velocities of all bodies, produced by N_body_simulator.
        masses: array with the masses of each body.
        N: integer number of bodies in the simulation.
        number_of_points: integer number of time steps

    Returns:
        system_energy: array containing the system energy at every point in time.
    '''
    # Grabbing all positions
    x_positions = np.zeros([N, number_of_points])
    y_positions = np.zeros([N, number_of_points])
    z_positions = np.zeros([N, number_of_points])
    for i in range(N):
        x_start = 6*i
        y_start = x_start+1
        z_start = x_start+2
        x_positions[i,:] = solution[:, x_start]
        y_positions[i,:] = solution[:, y_start]
        z_positions[i,:] = solution[:, z_start]

    # PE term
    PE = PE_calculator(N, masses, x_positions, y_positions, z_positions)

    # Calculating square velocity and kinetic energy
    velocities_squared = velocity_squared(solution, N, number_of_points)
    kinetic_energy = np.zeros((N, number_of_points))
    for i in range(N):
        mass = masses[i]
        kinetic_energy[i,:] = 0.5 * mass * velocities_squared[i,:]
    kinetic_energy = kinetic_energy.sum(axis=0)

    system_energy = kinetic_energy + PE
    return system_energy, kinetic_energy, PE


import os, sys, time
import numpy as np

def vec_length(vector):
    return np.sqrt(np.sum(vector**2))

def make_unitvec(x, y, z):
    temp = np.array([x, y, z]).astype('float')
    return temp / vec_length(temp)

def angle_sep_rad(vec1, vec2):
    return np.arccos(np.dot(vec1, vec2))

def angle_sep_deg(vec1, vec2):
    return np.degrees(angle_sep_rad(vec1, vec2))

def calc_incident_angle(v_incident, surf_norm):
    return angle_sep_rad(-v_incident, surf_norm)

def calc_tangent_vector(v_incident, surf_norm):
    temp = v_incident - surf_norm * np.dot(v_incident, surf_norm)
    return temp / vec_length(temp)  # as unit vector

def calc_refract_angle(incid_rad, n1n2_ratio):
    return np.arcsin(n1n2_ratio * np.sin(incid_rad))

def calc_refract_vector(v_incident, surf_norm, n1n2_ratio):
    theta_1 = calc_incident_angle(v_incident, surf_norm)
    theta_2 = calc_refract_angle(theta_1, n1n2_ratio)
    tgt_vec = calc_tangent_vector(v_incident, surf_norm)
    refrvec = tgt_vec * np.sin(theta_2) - surf_norm * np.cos(theta_2)
    return refrvec

def test_tgt_vec(v_incident, surf_norm):
    temp = v_incident + surf_norm * np.dot(v_incident, -surf_norm)
    return temp

## Calculate vectors of reflection and reflaction (Wikipedia):
#def wiki_calc_refract_vector(v_incident, surf_norm, n1_n2_ratio):
def calc_surface_vectors(v_incident, surf_norm, n1_n2_ratio):
    cti = -1.0 * np.dot(v_incident, surf_norm)     # cos(theta_i)
    nnr = n1_n2_ratio
    #sys.stderr.write("cti: %10.5f\n" % cti)
    v_reflect = v_incident + 2. * cti * surf_norm
    #sys.stderr.write("v_incident: %s\n" % str(v_incident))
    #sys.stderr.write("v_reflect:  %s\n" % str(v_reflect))
    smult = nnr*cti - np.sqrt(1.0 - nnr**2 * (1.0 - cti**2))
    v_refract = nnr * v_incident + smult * surf_norm
    return v_reflect, v_refract
    #return v_refract

vecsize = 1.0

surface_outer = np.array([0.0, 0.0, 1.0])
surface_inner = -1.0 * surface_outer

almost_normal = make_unitvec(0.1, 0, 1)
almost_faceon = -almost_normal

#fortyfive_xz = np.array([1.0, 0.0, 1.0])
fortyfive_xz = make_unitvec(1., 0., 1.)
fortyfive_yz = make_unitvec(0., 1., 1.)

#inbound_45 = -fortyfive_xz
incident_vec = almost_faceon
test_result = calc_refract_vector(incident_vec, surface_outer, 0.9)

v_reflect, v_refract = \
    calc_surface_vectors(incident_vec, surface_outer, 0.9)


# -*- coding: utf-8 -*-
"""
Created on Fri Aug 30 10:14:27 2019

@author: hamil
"""
import numpy as np
import matplotlib.pyplot as plt

def projectile(mass,initial_pos,initial_v, launch_angle):
    force_g = 9.8
    final_x_array =[]
    final_y_array =[]
    velocity_x_array =[]
    velocity_y_array =[]
    total_time = 0
    change_time = 0.1
    #calculate output variables and append to array
    final_x = (initial_pos*np.cos(launch_angle))+((initial_v*np.cos(launch_angle))*(change_time))
    final_x_array.append(final_x)
    final_y = (initial_pos*np.sin(launch_angle))+((initial_v*np.sin(launch_angle))*(change_time))
    final_y_array.append(final_y)
    velocity_x = initial_v*np.cos(launch_angle)
    velocity_x_array.append(velocity_x)
    velocity_y = (initial_v*np.sin(launch_angle))+((-(force_g)/mass)*change_time)
    velocity_y_array.append(velocity_y)
    time = np.arange(0,100,0.1)
    for each_time in time:
        final_x= final_x+velocity_x*change_time
        final_x_array.append(final_x)
        final_y = final_y+velocity_y*change_time
        final_y_array.append(final_y)
        velocity_x = velocity_x
        velocity_x_array.append(velocity_x)
        velocity_y= velocity_y+(-force_g/mass)*change_time
        velocity_y_array.append(velocity_y)
        total_time = each_time + 0.1
        if final_y <= 0:
            break
    return final_x_array, final_y_array, total_time, final_x

test_1 = projectile(5,0,10,np.radians(15))
test_2 = projectile(5,0,10,np.radians(30))
test_3 = projectile(5,0,10,np.radians(45))
test_4 = projectile(5,0,10,np.radians(60))

print("Final x position:", test_1[3])
print("total time for test1:", test_1[2])
print("Total time for test2:", test_2[2])
print("Total time for test3:", test_3[2])
print("Total time for test4:", test_4[2])
plt.scatter(test_1[0],test_1[1], s=3, label = "15 deg Launch")
plt.scatter(test_2[0], test_2[1], s=3, label = "30 deg Launch")
plt.scatter(test_3[0],test_3[1], s=3, label = "45 deg Launch")
plt.scatter(test_4[0],test_4[1], s=3, label = "60 deg Launch")
plt.title("Trajectory of 5kg projectile at 15, 30, 45, and 60 Degrees")
plt.xlabel("Position in X")
plt.ylabel("Position in Y")
plt.legend()
plt.savefig("ProjectileMotionPart1.pdf")
plt.show()

#Now add in air resistance/Drag and run same tests

def projectile(mass,initial_pos,initial_v, launch_angle):
    force_g = 9.8
    c = 2.0e-03
    final_x_array =[]
    final_y_array =[]
    velocity_x_array =[]
    velocity_y_array =[]
    total_time = 0
    change_time = 0.1
    #calculate output variables and append to array
    final_x = (initial_pos*np.cos(launch_angle))+((initial_v*np.cos(launch_angle))*(change_time))
    final_x_array.append(final_x)
    final_y = (initial_pos*np.sin(launch_angle))+((initial_v*np.sin(launch_angle))*(change_time))
    final_y_array.append(final_y)
    f_dragx = c*(initial_v*np.cos(launch_angle)**2)
    f_dragy = c*(initial_v*np.sin(launch_angle)**2)
    velocity_x = (initial_v*np.cos(launch_angle))+(f_dragx/mass)*change_time
    velocity_x_array.append(velocity_x)
    velocity_y = (initial_v*np.sin(launch_angle))+((f_dragy-force_g)/mass)*change_time
    velocity_y_array.append(velocity_y)
    time = np.arange(0,100,0.1)
    for each_time in time:
        final_x= final_x+velocity_x*change_time
        final_x_array.append(final_x)
        final_y = final_y+velocity_y*change_time
        final_y_array.append(final_y)
        f_dragx = c*(velocity_x**2)
        f_dragy = c*(velocity_y**2)
        velocity_x = velocity_x+(f_dragx/mass)*change_time
        velocity_x_array.append(velocity_y)
        velocity_y= velocity_y+((f_dragy-force_g)/mass)*change_time
        velocity_y_array.append(velocity_y)
        total_time = each_time + 0.1
        if final_y <= 0:
            break
    return final_x_array, final_y_array, total_time,velocity_y_array

test1 = projectile(6,0,10,np.radians(15))
test2 = projectile(6,0,10,np.radians(30))
test3 = projectile(6,0,10,np.radians(45))

print("total time for test1:", test1[2])
print("Total time for test2:", test2[2])
print("Total time for test3:", test3[2])
plt.scatter(test_1[0],test_1[1], s=3, c= "#BB8FCE")
plt.scatter(test_2[0], test_2[1], s=3, c="#3498DB")
plt.scatter(test_3[0],test_3[1], s=3, c= "#5B2C6F")
plt.scatter(test1[0],test1[1], s=2, c= "#BB8FCE", label = "15 deg Launch")
plt.scatter(test2[0], test2[1], s=2, c= "#3498DB", label = "30 deg Launch")
plt.scatter(test3[0],test3[1], s=2, c= "#5B2C6F", label = "45 deg Launch")
plt.title("Trajectory of 8 kg projectile with added drag")
plt.xlabel("Position in X")
plt.ylabel("Position in Y")
plt.xlim(0,110)
plt.ylim(0,30)
plt.legend()
plt.savefig("ProjectileMotionPart2.pdf")

x =10*np.cos(np.radians(15))*(2.7)
print(x)


'''
PDS por analises de Fourier.

Escrito por Saulo G. Felix
Licença BSD ou parecida.
'''

import numpy as np

def index2d(mat):
    try:
        return mat.shape
        raise ValueError('Apenas matriz 2-d')
    except Exception:
        pass

def matdft(dim=1):
    mat = np.matrix([[np.exp(-1j*2*np.pi*i*k/dim) for i in range(dim)] for k in range(dim)])
    return mat

def dft2d(mat):
    mat = np.matrix(mat)
    [M, N] = index2d(mat)
    U = matdft(M)
    V = U if M == N else matdft(N)
    xx = (U * mat) * V.conj().T
    return xx

def idft2d(mat):
    mat = np.matrix(mat)
    [M, N] = index2d(mat)
    U = matdft(M)
    V = U if M == N else matdft(N)
    xx = 1/(M*N) * U.H (mat * V)
    return xx

def dft(vet):
    L = len(vet)
    vet = np.array([vet])
    U = matdft(L)
    xx = np.array(U * vet.T)
    return xx

def idft(vet):
    L = len(vet)
    vet = np.array(vet)
    U = matdft(L)
    xx = (1/L) * np.array(U.H * vet)
    return xx

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    tf = 2
    fa = 30
    N = tf*fa
    t = np.arange(0, tf, 1/fa)
    y = .7*np.sin(2*np.pi*t*4) + .4*np.cos(2*np.pi*t*12)
    w = [n*fa/N for n in range(0, N)]
    Y = dft(y)
    espectro = abs(Y)
    plt.plot(w, espectro, lw=2)
    plt.xticks(np.arange(0, fa, 2))
    plt.show()


import numpy as np

class LogisticRegressionNumpy:
    """Logistic Regresion using gradient descent algorithm.
    Implemented in numpy.
    Related equations:
    # hypothesis function: y_pred = h(x) = sigmoid(xw + b).
    # Loss function: Log loss aka. Binary cross entropy loss
    # J = -1/N sum_i^N (yi*log(h(xi)) + (1-yi)log(1-h(xi)))
    # Take derivative of the loss function w.r.t. the parameters: dJ/dw = 1/N sum_i^N (xi*(h(xi)-yi))
    """

    def __init__(self):
        self.w_ = None
        self.intercept_ = None
        self.coef_ = None

    def fit(self, x, y, learning_rate=0.05, n_iterations=1000):
        """Fit the training data using Gradient Descent
        Args:
            x (np.array): Training samples, shape = [n_samples, n_features]
            y (np.array): Target samples, shape = [n_samples, n_target_values]
            learning_rate (float, optional): Learning rate. Defaults to 0.5.
            n_iterations (int, optional): Number of iterations i.e., num. of epochs. Defaults to 1000.
        """

        self.cost_ = []

        # Create weight matrix including the bias term
        np.random.seed(0)
        self.w_ = np.random.rand(x.shape[1]+1, 1)*0.01

        # Add 1 to the beginning of input vectors for the bias.
        m = x.shape[0]
        x = np.hstack((np.ones((m,1)), x))

        # Shuffle the training data.
        indices = np.arange(0, m)
        np.random.shuffle(indices)
        x = x[indices]
        y = y[indices]
        y = np.reshape(y, (y.shape[0], 1))

        # Start of training.
        for _ in range(n_iterations):

            # hypothesis function
            h = self._sigmoid(np.dot(x, self.w_)) # y = sigmoid(wx+b)

            # Derivatives of the loss function w.r.t weights, dw/dJ:
            gradient_vector = np.dot(x.T, (h - y))

            # Update weights
            self.w_ -= (learning_rate / m) * gradient_vector

            # Save loss function. Might be useful.
            # Cross entropy loss.
            cost = (-y * np.log(h) - (1 - y) * np.log(1 - h)).mean()
            self.cost_.append(cost)

        # Intercept and slope (coefficients).
        self.intercept_ = self.w_[0]
        self.coef_ = self.w_[1:]

    def _sigmoid(self, x):
        """Sigmoid function."""
        return 1./(1 + np.exp(-x))

    def predict(self, x):
        """Predicts for test samples.
        Args:
            x (np.array): Test samples, shape = [n_samples, n_features]

        Returns:
            np.array: Predicted value, shape = [n_samples, ]
        """

        # Add 1 vector to the inputs for bias.
        m = x.shape[0]
        x = np.hstack((np.ones((m,1)), x))

        # Get predicted values predicted_labels, y = h(x)
        predicted_labels = (self._sigmoid(np.dot(x, self.w_))>0.5) # y = h(x)
        predicted_labels = predicted_labels.astype(np.uint8) # Convert bool to int.
        predicted_labels = predicted_labels.reshape((m,)) # Reshape to 1D array.
        return predicted_labels


import numpy as np

def kepler_E(e, M):
    """Solve Kepler's equation for the eccentric anomaly.

    Based on algorithm 3.1 on page 115.

    :param float e: Eccentricity
    :param float M: Mean anomaly (radians)

    :returns: Eccentric anomaly (radians)
    :rtype: float
    """

    tol = 1.e-8  # set an error tolerance

    # select a starting value for E
    if M < np.pi:
        E = M + e / 2
    else:
        E = M - e / 2

    error = 1

    while abs(error) > tol:
        error = (E - e * np.sin(E) - M) / (1 - e * np.cos(E))
        E = E - error

    return E

def universal_anomaly(r0_norm, vr0, delta_t, mu, alpha):
    """Calculate the univeral anomaly using Newton's method.

    Based on algorithm 3.3 on page 138.

    :param float r0_norm: Initial distance
    :param float vr0: Intial radial component of velocity
    :param float delta_t: Time elapsed since the intial conditions
    :param float mu: Gravitational parameter
    :param float alpha: Reciprocal of the semimajor axis

    :returns: The universal anomaly
    :rtype: float
    """

    chi = np.sqrt(mu) * np.abs(alpha) * delta_t  # initial estimate for the universal anomaly
    tol = 1e-8  # convergence tolerance
    max_iter = 1000  # iteration limit
    i = 0  # step counter
    error = 1  # error tracker

    while np.abs(error) > tol and i < max_iter:
        # calculate the stumpff functions
        (C, S) = stumpff_functions(alpha, chi)

        # calculate algorithm function and its derivative
        f = (r0_norm * vr0 / np.sqrt(mu) * chi * chi * C + (1 - alpha * r0_norm) * chi ** 3 * S
             + r0_norm * chi - np.sqrt(mu) * delta_t)
        dfdchi = (r0_norm * vr0 / np.sqrt(mu) * chi * (1 - alpha * chi * chi * S)
                  + (1 - alpha * r0_norm) * chi * chi * C + r0_norm)

        error = f / dfdchi  # calculate error
        chi = chi - error  # update chi
        i += 1  # update step counter

    if i >= max_iter:
        print("Max iterations exceeded in universal_anomaly algorithm.")

    return chi

def orbital_update(r0, v0, delta_t, mu):
    """Calculate the orbital state given the initial state vector and a time difference.

    Based on algorithm 3.4 on page 142.

    :param r0: Initial position vector
    :type r0: :class:`numpy.ndarray`
    :param v0: Initial velocity vector
    :type v0: :class:`numpy.ndarray`
    :param float delta_t: Time elapsed since the intial conditions
    :param float mu: Gravitational parameter

    :returns: Current state and velocity vector (r, v)
    :rtype: tuple(:class:`numpy.ndarray`)
    """

    r0_norm = np.sqrt(np.dot(r0, r0))  # calculate the initial distance
    v0_norm = np.sqrt(np.dot(v0, v0))  # calculate the initial speed
    vr0 = np.dot(r0, v0) / r0_norm  # calculate the intial radial component of velocity

    alpha = 2 / r0_norm - v0_norm * v0_norm / mu  # calculate the reciprocal of the semimajor axis
    # note if alpha > 0 orbit is an ellipse; if alpha = 0 orbit is a parabola;
    # if alpha < 0 orbit is a hyperbola

    chi = universal_anomaly(r0_norm, vr0, delta_t, mu, alpha)  # calculate the univeral anomaly
    (C, S) = stumpff_functions(alpha, chi)  # calculate the stumpff functions

    # calculate the lagrange position coefficients
    f = 1 - chi * chi / r0_norm * C
    g = delta_t - 1 / np.sqrt(mu) * chi ** 3 * S

    r = f * r0 + g * v0  # calculate the new position vector
    r_norm = np.sqrt(np.dot(r, r))  # calculate the initial distance

    # calculate the lagrange position coefficients
    fdot = np.sqrt(mu) / (r_norm * r0_norm) * (alpha * chi ** 3 * S - chi)
    gdot = 1 - chi * chi / r_norm * C

    v = fdot * r0 + gdot * v0  # calculate the new velocity vector

    return (r, v)

def orbital_elements(r, v, mu, deg=False):
    """Calculate the orbital elements given the state vector.

    Based on algorithm 4.1 on page 159.

    The orbital elements are as follows:
    * h: Magnitude of the specific angular momentum
    * i: Inclincation
    * Omega: Right acension of the ascending node
    * e: Eccentricity
    * omega: Argument of perigee
    * theta: True anomaly
    * a: Semi-major axis

    :param r: Position vector
    :type r: :class:`numpy.ndarray`
    :param v: Velocity vector
    :type v: :class:`numpy.ndarray`
    :param float mu: Gravitational parameter
    :param bool deg: Whether or not to output in degrees or radians

    :returns: Returns the orbital elements (h, i, Omega, e, omega, theta, a)
    :rtype: tuple(float)
    """

    r_norm = np.sqrt(np.dot(r, r))  # calculate the distance
    v_norm = np.sqrt(np.dot(v, v))  # calculate the speed

    v_r = np.dot(r, v) / r_norm  # calculate the radial velocity
    # note if v_r > 0, the satellite is flying away from perigee
    # if vr < 0, it is flying towards perigee

    h = np.cross(r, v)  # calculate the specific angular momentum
    h_norm = np.sqrt(np.dot(h, h))  # calculate the magnitude of the specific angular momentum

    i = np.arccos(h[2] / h_norm)  # calculate the inclination
    # note if the 0 < i < pi/2 the orbit is prograge
    # if pi/2 < i < pi the orbit is retrograde

    N = np.cross([0, 0, 1], h)  # calculate the node line
    N_norm = np.sqrt(np.dot(N, N))  # calculate the magnitude of the node line

    # calculate the right acension of the ascending node
    if N[1] >= 0:
        Omega = np.arccos(N[0] / N_norm)
    else:
        Omega = 2 * np.pi - np.arccos(N[0] / N_norm)

    # calculate the eccentricity vector
    e = 1 / mu * ((v_norm * v_norm - mu / r_norm) * r - r_norm * v_r * v)
    e_norm = np.sqrt(np.dot(e, e))  # calculate the eccentricity

    # calculate the argument of perigee
    if e[2] >= 0:
        omega = np.arccos(np.dot(N, e) / (N_norm * e_norm))
    else:
        omega = 2 * np.pi - np.arccos(np.dot(N, e) / (N_norm * e_norm))

    # calculate the true anomaly
    if v_r >= 0:
        theta = np.arccos(np.dot(e, r) / (e_norm * r_norm))
    else:
        theta = 2 * np.pi - np.arccos(np.dot(e, r) / (e_norm * r_norm))

    # calculate the semi-major axis
    a = h_norm * h_norm / mu / (1 - e_norm * e_norm)

    if deg:
        return (
            h_norm, i * 180 / np.pi, Omega * 180 / np.pi, e_norm, omega * 180 / np.pi,
            theta * 180 / np.pi, a
        )
    else:
        return (h_norm, i, Omega, e_norm, omega, theta, a)

def state_vector(h, i, Omega, e, omega, theta, mu):
    """Calculate the state vector given the six orbital elements.

    Based on algorithm 4.2 on page 175.

    :param float h: Magnitude of the specific angular momentum
    :param float i: Inclincation
    :param float Omega: Right acension of the ascending node
    :param float e: Eccentricity
    :param float omega: Argument of perigee
    :param float theta: True anomaly
    :param float mu: Gravitational parameter

    :returns: Current state and velocity vector (r, v)
    :rtype: tuple(:class:`numpy.ndarray`)
    """

    r_xbar = h * h / (mu * (1 + e * np.cos(theta))) * np.array([np.cos(theta), np.sin(theta), 0])
    v_xbar = mu / h * np.array([-np.sin(theta), e + np.cos(theta), 0])

    a = np.cos(Omega) * np.cos(omega) - np.sin(Omega) * np.sin(omega) * np.cos(i)
    b = -np.cos(Omega) * np.sin(omega) - np.sin(Omega) * np.cos(i) * np.cos(omega)
    c = np.sin(Omega) * np.sin(i)
    d = np.sin(Omega) * np.cos(omega) + np.cos(Omega) * np.cos(i) * np.sin(omega)
    e = -np.sin(Omega) * np.sin(omega) + np.cos(Omega) * np.cos(i) * np.cos(omega)
    f = -np.cos(Omega) * np.sin(i)
    g = np.sin(i) * np.sin(omega)
    j = np.sin(i) * np.cos(omega)
    k = np.cos(i)

    Q_xbarX = np.array([
        [a, b, c],
        [d, e, f],
        [g, j, k]]
    )

    r_X = np.matmul(Q_xbarX, r_xbar)
    v_X = np.matmul(Q_xbarX, v_xbar)

    return (r_X, v_X)

def lambert(r1, r2, delta_t, mu):
    """Calculate the velocity vectors given two positions and the time difference between them.

    Assumes a prograde trajectory (0 < i < 90).

    Based on algorithm 5.2 on page 208.

    :param r1: Position vector 1
    :type r1: :class:`numpy.ndarray`
    :param r2: Position vector 2
    :type r2: :class:`numpy.ndarray`
    :param float delta_t: Time difference between the two position vectors
    :param float mu: Gravitational parameter

    :returns: Velocity vectors v1 & v2
    :rtype: tuple(:class:`numpy.ndarray`)
    """

    r1_norm = np.sqrt(np.dot(r1, r1))  # calculate the distance to r1
    r2_norm = np.sqrt(np.dot(r2, r2))  # calculate the distance to r2

    if np.cross(r1, r2)[2] >= 0:
        delta_theta = np.arccos(np.dot(r1, r2) / (r1_norm * r2_norm))
    else:
        delta_theta = 2 * np.pi - np.arccos(np.dot(r1, r2) / (r1_norm * r2_norm))

    A = np.sin(delta_theta) * np.sqrt(r1_norm * r2_norm / (1 - np.cos(delta_theta)))

    def y(z, C, S):
        return r1_norm + r2_norm + A * (z * S - 1) / (np.sqrt(C))

    # solve eq. 5.39 for z using Newton's method
    tol = 1e-8  # convergence tolerance
    max_iter = 5000  # iteration limit
    i = 0  # step counter
    error = 1  # error tracker
    z = 0  # initial guess for z

    while np.abs(error) > tol and i < max_iter:
        # calculate algorithm function and its derivative
        (C, S) = stumpff_functions(z, 1)
        y_z = y(z, C, S)
        f = (y_z / C) ** 1.5 * S + A * np.sqrt(y_z) - np.sqrt(mu) * delta_t

        if z == 0:
            dfdz = (np.sqrt(2) / 40 * y_z ** 1.5 + A / 8 * (np.sqrt(y_z)
                    + A * np.sqrt(1 / (2 * y_z))))
        else:
            dfdz = ((y_z / C) ** 1.5 * (1 / (2 * z) * (C - 1.5 * S / C) + 0.75 * S * S / C)
                    + A / 8 * (3 * S / C * np.sqrt(y_z) + A * np.sqrt(C / y_z)))

        error = f / dfdz  # calculate error
        z = z - error  # update chi
        i += 1  # update step counter

    if i >= max_iter:
        print("Max iterations exceeded in lambert algorithm.")

    y_z = y_z = y(z, C, S)  # calculate final value of y

    # determine lagrange coefficients
    f = 1 - y_z / r1_norm
    g = A * np.sqrt(y_z / mu)
    g_dot = 1 - y_z / r2_norm

    # calculate velocities
    v1 = 1 / g * (r2 - f * r1)
    v2 = 1 / g * (g_dot * r2 - r1)

    return (v1, v2)

def julian(y, m, d):
    """Calculate julian day number at 0 UT between 1900 & 2100.

    Based on Equation 5.48 on page 214.

    :param int y: Year between 1900 & 2100, i.e. 1901 <= y <= 2099.
    :param int m: Month i.e. 1 <= m <= 12
    :param int d: Day i.e. 1 <= m <= 31

    :returns: Julian day number
    :rtype: float
    """

    return (367 * y - np.fix(7 * (y + np.fix((m + 9) / 12)) / 4) + np.fix(275 * m / 9) + d
            + 1721013.5)

def planetry_elements(planet_id):
    """Extract a planet's J2000 orbital elements and centennial rates.

    Based on Table 8.1 on page 388.

    Planet IDs are as follows:
    * 0 - Mercury
    * 1 - Venus
    * 2 - Earth
    * 3 - Mars
    * 4 - Jupiter
    * 5 - Saturn
    * 6 - Uranus
    * 7 - Neptune
    * 8 - Pluto

    :param int planet_id: ID of the planet

    :returns: J2000 orbital elements and centennial rates.
    :rtype: tuple(:class:`numpy.ndarray`)
    """

    J2000_elements = np.array([
        [0.38709893, 0.20563069, 7.00487, 48.33167, 77.45645, 252.25084],
        [0.72333199, 0.00677323, 3.39471, 76.68069, 131.53298, 181.97973],
        [1.00000011, 0.01671022, 0.00005, -11.26064, 102.94719, 100.46435],
        [1.52366231, 0.09341233, 1.85061, 49.57854, 336.04084, 355.45332],
        [5.20336301, 0.04839266, 1.30530, 100.55615, 14.75385, 34.40438],
        [9.53707032, 0.05415060, 2.48446, 113.71504, 92.43194, 49.94432],
        [19.19126393, 0.04716771, 0.76986, 74.22988, 170.96424, 313.23218],
        [30.06896348, 0.00858587, 1.76917, 131.72169, 44.97135, 304.88003],
        [39.48168677, 0.24880766, 17.14175, 110.30347, 224.06676, 238.92881],
    ])

    cent_rates = np.array([
        [0.00000066, 0.00002527, -23.51, -446.30, 573.57, 538101628.29],
        [0.00000092, -0.00004938, -2.86, -996.89, -108.80, 210664136.06],
        [-0.00000005, -0.00003804, -46.94, -18228.25, 1198.28, 129597740.63],
        [-0.00007221, 0.00011902, -25.47, -1020.19, 1560.78, 68905103.78],
        [0.00060737, -0.00012880, -4.15, 1217.17, 839.93, 10925078.35],
        [-0.00301530, -0.00036762, 6.11, -1591.05, -1948.89, 4401052.95],
        [0.00152025, -0.00019150, -2.09, -1681.4, 1312.56, 1542547.79],
        [-0.00125196, 0.00002514, -3.64, -151.25, -844.43, 786449.21],
        [-0.00076912, 0.00006465, 11.07, -37.33, -132.25, 522747.90],
    ])

    J2000_oe = J2000_elements[planet_id, :]
    rates = cent_rates[planet_id, :]

    # convert AU to km
    au = 149597871
    J2000_oe[0] *= au
    rates[0] *= au

    # convert arcseconds to fractions of a degree
    rates[2:] *= 1 / 3600

    return (J2000_oe, rates)

def planet_state(planet_id, y, m, d, hour, minute, second, deg=False):
    """Calculate the orbital elements and state vector of a planet from the date and UT.

    Based on algorithm 8.1 on page 389.

    Planet IDs are as follows:
    * 0 - Mercury
    * 1 - Venus
    * 2 - Earth
    * 3 - Mars
    * 4 - Jupiter
    * 5 - Saturn
    * 6 - Uranus
    * 7 - Neptune
    * 8 - Pluto

    The orbital elements are as follows:
    * h: Magnitude of the specific angular momentum
    * i: Inclincation
    * Omega: Right acension of the ascending node
    * e: Eccentricity
    * omega: Argument of perigee
    * theta: True anomaly
    * a: Semi-major axis

    :param int planet_id: ID of the planet
    :param int y: Year between 1900 & 2100, i.e. 1901 <= y <= 2099.
    :param int m: Month i.e. 1 <= m <= 12
    :param int d: Day i.e. 1 <= m <= 31
    :param int hour: UT hour i.e. 0 <= hour <= 23
    :param int minute: UT minute i.e. 0 <= minute <= 59
    :param int second: UT second i.e. 0 <= second <= 59
    :param bool deg: Whether or not to output in degrees or radians

    :returns: Orbital elements (h, i, Omega, e, omega, theta, a, omega_hat, L, M, E), the state
        vector (r, v) and the Julian day
    :rtype: tuple(tuple(float), :class:`numpy.ndarray`, :class:`numpy.ndarray`, float)
    """

    mu = 1.327124e11  # mu for planets orbiting the sun
    j0 = julian(y, m, d)  # julian day at UT 0
    ut = (hour + minute / 60 + second / 3600) / 24  # univeral time
    jd = j0 + ut  # julian day
    t0 = (jd - 2451545) / 36525  # calculate number of julian centuries between J2000 and date

    (J2000_oe, rates) = planetry_elements(planet_id)  # get data for the planet
    orbital_elements = J2000_oe + rates * t0  # calculate orbital elements at date

    # extract orbital elements
    a = orbital_elements[0]
    e = orbital_elements[1]
    h = np.sqrt(mu * a * (1 - e * e))
    i = orbital_elements[2]
    Omega = zero_to_360(orbital_elements[3])
    omega_hat = zero_to_360(orbital_elements[4])
    L = zero_to_360(orbital_elements[5])
    omega = zero_to_360(omega_hat - Omega)

    # calculate true anomaly
    M = zero_to_360((L - omega_hat))
    E = kepler_E(e, M * np.pi / 180)
    theta = zero_to_360(2 * np.arctan(np.sqrt((1 + e) / (1 - e)) * np.tan(E / 2)) * 180 / np.pi)

    # collect the orbital elements (degrees)
    oe = (h, i, Omega, e, omega, theta, a, omega_hat, L, M, E * 180 / np.pi)

    # calculate the state vector (angles in radians)
    (r, v) = state_vector(
        h, i * np.pi / 180, Omega * np.pi / 180, e, omega * np.pi / 180, theta * np.pi / 180, mu
    )

    if deg:
        return (oe, r, v, jd)
    else:
        angle_list = [1, 2, 4, 5, 7, 8, 9, 10]
        oe = list(oe)

        for i in angle_list:
            oe[i] *= np.pi / 180

        return (oe, r, v, jd)

def interplanetary(depart, arrive):
    """Calculate spacecraft trajectory from SOI of planet 1 to planet 2.

    Based on algorithm 8.2 on page 393.

    Planet IDs are as follows:
    * 0 - Mercury
    * 1 - Venus
    * 2 - Earth
    * 3 - Mars
    * 4 - Jupiter
    * 5 - Saturn
    * 6 - Uranus
    * 7 - Neptune
    * 8 - Pluto

    :param depart: List of parameters defining the departure
        [planet_id, y, m, d, hour, minute, second]
    :type depart: list(float)
    :param arrive: List of parameters defining the arrival
        [planet_id, y, m, d, hour, minute, second]
    :type arrive: list(float)

    :returns: The state vector and time of depature at planet 1 (rp1, vp1, jd1), the state vector
        and time of arrival at planet 2 (rp2, vp2, jd2) and the velocity at departure and arrival
        (v1, v2).
    :rtype: tuple(tuple(:class:`numpy.ndarray`, :class:`numpy.ndarray`, float),
        tuple(:class:`numpy.ndarray`, :class:`numpy.ndarray`, float),
        tuple(:class:`numpy.ndarray`, :class:`numpy.ndarray`))
    """

    mu = 1.327124e11  # mu for planets orbiting the sun

    # get state vector at departure planet
    (_, rp1, vp1, jd1) = planet_state(
        depart[0], depart[1], depart[2], depart[3], depart[4], depart[5], depart[6]
    )

    # get state vector at arrival planet
    (_, rp2, vp2, jd2) = planet_state(
        arrive[0], arrive[1], arrive[2], arrive[3], arrive[4], arrive[5], arrive[6]
    )

    # calculate time of flight
    tof = (jd2 - jd1) * 24 * 3600

    # calculate trajectory by lambert's problem
    [v1, v2] = lambert(rp1, rp2, tof, mu)

    return ((rp1, vp1, jd1), (rp2, vp2, jd2), (v1, v2))

if __name__ == '__main__':
    # mu = 398600
    depart = [2, 1996, 11, 7, 0, 0, 0]
    arrive = [3, 1997, 9, 12, 0, 0, 0]

    print(interplanetary(depart, arrive))


import tensorflow as tf
import numpy as np

# set y = wx + b
# loss func: (1/N) * sum[((wx + b) - y) ^ 2]
# update w: (1/N) * x

# generate random data [x, y]
def initial_random(N, w, b):
    res = []
    for i in range(N):
        x = np.random.uniform(-10, 10)
        noise = np.random.normal(0, 0.01)
        y = w * x + b + noise  # x in -10, 10, and get y by w, b
        res.append([x, y])
    res = np.array(res)
    return res

# points are sample point
# w b is value when updating(training)
# according loss func to get error
def mse(b, w, points) :
    N = len(points)

    error = 0
    for i in range(N):
        x = points[i, 0]
        ry = points[i, 1]
        py = w * x + b
        error += (ry - py) ** 2
    return error / float(N)

# update w and b
def update_gradient(b_current, w_current, points, lr):
    b_grad = 0
    w_grad = 0
    N = float(len(points))
    for i in range(int(N)):
        x = points[i, 0]
        y = points[i, 1]
        b_grad += (2 / N) * ((w_current * x + b_current) - y)
        w_grad += (2 / N) * x * ((w_current * x + b_current) - y)
    new_b = b_current - (lr * b_grad)
    new_w = w_current - (lr * w_grad)

    return [new_b, new_w]

def gradient_descent(points, starting_b, starting_w, lr, num_iteration):
    b = starting_b
    w = starting_w
    for step in range(num_iteration):
        b, w = update_gradient(b, w, points, lr)
        loss = mse(b, w, points)

        if step % 50 == 0:
            print(f"iter:{step}, loss:{loss}, w:{w}, b:{b}")

    return [b, w]

if __name__ == "__main__":
    data = initial_random(100, 2.232, 1.432)

    lr = 0.01
    initial_b = 0
    initial_w = 0
    num_iter = 1000

    [b, w] = gradient_descent(data, initial_b, initial_w, lr, num_iter)

    loss = mse(b, w, data)

    print(f"final loss:{loss}, w:{w}, b:{b}")


#!/usr/bin/env python3

import numpy as np
import sys
from math import sin
import matplotlib.pyplot as plt
from scipy.integrate import quad

def f(x):
    return x**2
def fsin(x):
    return sin(x)

def integrate (f,a, b, n = 100):
     x = np.linspace(a,b,n)
     #print(x)
     y = []
     interval = (b - a ) / n
     print (interval)
     area  = 0
     for i in range (n):
         y += [f(x[i])]
     for i  in range (n - 1):
         #print (y[i] , y[i + 1])
         area += (y[i] + y[i + 1]) * interval / 2
     return area

def integrate_mc(f, a, b,c,d, n  = 1000):
     count = 0;
     rec_area = abs(a-b) * abs(c-d)

     for i in range (n):
         rand_x = np.random.uniform(a, b)
         rand_y = np.random.uniform(c, d)
         #if rand_y > 0:
         #  if rand_y <= f(rand_x):
         #       count += 1
         #else:
         #   if rand_y >= f(rand_x):
         #       count += 1
         if (rand_y > 0 and f(rand_x) > rand_y):
            count += 1
         elif (rand_y < 0 and f(rand_x) < rand_y):
            count -= 1
     area = rec_area * count / n
     return area

def approximate_pi(n):
    x_max = 1.0
    x_min = -1.0
    y_max = 1.0
    y_min = -1.0
    count = 0
    rec_area = abs(x_max-x_min) * abs(y_max-y_min)
    for i in range(n):
        rand_x = np.random.uniform(x_min,x_max)
        rand_y = np.random.uniform(y_min,y_max)
        if (f(rand_x)+f(rand_y)) < 1:
            count += 1
        print("Process:{0}%".format(round((i + 1) * 100 / n)), end="\r")
    print("")
    area = rec_area * count / n
    return area

Rm= integrate(f,1,100,100000)

Mc = integrate_mc(fsin,1,100,-1,1,1000000)

print("Riemann: ",Rm)
print("MC: ",Mc)
pi = approximate_pi(100000)
print(pi)

#Calculate the definite integral by hand in python built-in function and Plot the absolute error in the approximations
w,err = quad(f,1,100)
print(w,err)

er1 = abs(w - Rm) / w
er2 = abs(w - Mc) / w
print("%.20f%%" % er1)
print( er2)


import scipy.integrate as spi
import numpy as np

print('Double integral computed by SciPy nquad')
print('Example 2-01 nquad')
print('Integral of 2xye^-xy from y=1 to y=5 and from x=y-1 to x=y+1')

integrand = lambda x, y : 2 * x * y * np.exp(-x * y)

bounds_y = lambda : [1., 5.]
bounds_x = lambda y : [y-1, y+1]

result, error = spi.nquad(integrand, [bounds_x, bounds_y])
print('Result is ', result, 'with error ', error)


"""
Module uses Taylor series to calculate 1/(1+sinx) function\
and creates graphs for comparison of accuracy.
"""

from cProfile import label
from math import sin, pi
import matplotlib.pyplot as plt
import numpy as np

def calculate(num, x_value):
    """
    (int, int/float) -> (float/int)
    Function uses Taylor's series to calculate value.
    The function returns an approximate function value.
    Function: 1/(1+sinx)
    """
    x_value = x_value%(2*pi)
    sinx = 0
    for n in range(num):
        n += 1
        factorial = 1
        range1 = 2*n - 1
        for i in range(1,range1+1):
            factorial *= i
        sinx += (((-1)**(n-1))*x_value**(2*n-1))/factorial
    return 1/(1+sinx)

def create_plot_1(seq_mem, num):
    """
    Function accepts two arguments:\
    seq_mem which will be passed to\
    calculate() function, and num which stands for\
    how many x values the function will calculate results\
    (with the step of 0.1).
    Function also calculates function values (for the same list\
    of x values) using built-in math.sin() function.
    Then two graphs are created.
    """
    x_args = list()
    i = 0.1
    for _ in range(num):
        x_args.append(i)
        i += 0.1
    y_talor = list()
    for x in x_args:
        y = calculate(seq_mem, x)
        y_talor.append(y)
    y_builtin = list()
    for x in x_args:
        y = 1/(1+sin(x))
        y_builtin.append(y)

    plt.plot(x_args, y_talor, label="Taylor series")
    plt.plot(x_args, y_builtin, label="built-in function")
    plt.title(f"comparison of two graphs\nnumber of terms: {seq_mem}")
    plt.legend()
    plt.show()

def create_plot_2(num):
    """
    Function creates two big graphs for\
    1/(1+sinx) function using built-in math.sin()\
    function and Taylor series.
    Argument num stands for the number of terms,\
    it will be passed to calculate() function.
    """
    x_args = np.linspace(-np.pi*10, np.pi*10, 1000)
    y_talor = list()
    for x in x_args:
        y = calculate(num, x)
        y_talor.append(y)
    y_builtin = list()
    for x in x_args:
        y = 1/(1+sin(x))
        y_builtin.append(y)

    fig = plt.figure()
    axes = fig.add_subplot(1, 1, 1)
    axes.spines['left'].set_position('center')
    axes.spines['bottom'].set_position('center')
    axes.spines['right'].set_color('none')
    axes.spines['top'].set_color('none')
    axes.xaxis.set_ticks_position('bottom')
    axes.yaxis.set_ticks_position('left')

    axes.plot((1), (0), ls="", marker=">", ms=10, color="k",
            transform=axes.get_yaxis_transform(), clip_on=False)
    axes.plot((0), (1), ls="", marker="^", ms=10, color="k",
            transform=axes.get_xaxis_transform(), clip_on=False)

    plt.axis([-30, 30, -10, 10])
    plt.plot(x_args, y_builtin, label="built-in function")
    plt.plot(x_args, y_talor, label="Taylor series")
    plt.legend()
    plt.show()