import numpy as np
from math import sqrt

# Ex 6a)
def minkowski_norm(x_k, q):
    mean = np.mean(x_k)
    s = (1/len(x_k) * sum([abs(x - mean)**q for x in x_k]))**(1/q)
    return (np.array(x_k) - mean) / s

def minkowski_metric(x_i, x_j, q=2, norm=False):
    if norm:
        x_i = minkowski_norm(x_i, q)
        x_j = minkowski_norm(x_j, q)
    return sum([abs(i - j)**q for i, j in zip(x_i, x_j)])**(1/q)

x_1 = [55, 2500, 4]
x_2 = [23, 1800, 3]
x_3 = [31, 3500, 0]

"""
print("Minkowski distance for q = 1:")
print(minkowski_metric(x_1, x_2, 1))
print(minkowski_metric(x_1, x_3, 1))
print(minkowski_metric(x_2, x_3, 1))
print("\n")

print("Minkowski distance for q = 2:")
print(minkowski_metric(x_1, x_2, 2))
print(minkowski_metric(x_1, x_3, 2))
print(minkowski_metric(x_2, x_3, 2))
print("\n")
"""

print("q = 1")
print(minkowski_metric(x_1, x_2, 1, norm=True))
print(minkowski_metric(x_1, x_3, 1, norm=True))
print(minkowski_metric(x_2, x_3, 1, norm=True))

print("q = 2")
print(minkowski_metric(x_1, x_2, 2, norm=True))
print(minkowski_metric(x_1, x_3, 2, norm=True))
print(minkowski_metric(x_2, x_3, 2, norm=True))

# Ex. 6b)
def mahalanobis_metric(X, x_i, x_j):
    #S = 1/len(X) * np.transpose(X - np.mean(X)) * (X - np.mean(X))
    S = np.cov(X)
    #return sqrt(np.transpose(x_i - x_j) * np.linalg.inv(S) * (x_i - x_j))
    return sqrt(np.transpose(x_i - x_j) * np.linalg.pinv(S) * (x_i - x_j))

x_1 = np.array(x_1)
x_1 = np.array(x_2)
x_1 = np.array(x_3)
X = [x_1, x_2, x_3]

#print(mahalanobis_metric(X, x_1, x_2))
#print(mahalanobis_metric(X, x_1, x_3))
#print(mahalanobis_metric(X, x_2, x_3))

print(np.cov(X))
print(np.linalg.det(np.cov(X)))

print("Normed Minkowski distance for q = 1:")
print(minkowski_metric(x_1, x_2, 1, norm=True))
print(minkowski_metric(x_1, x_3, 1, norm=True))
print(minkowski_metric(x_2, x_3, 1, norm=True))
print("\n")

print("Normed Minkowski distance for q = 2:")
print(minkowski_metric(x_1, x_2, 2, norm=True))
print(minkowski_metric(x_1, x_3, 2, norm=True))
print(minkowski_metric(x_2, x_3, 2, norm=True))
print("\n")

# Ex 6c)
def m_s_coeff(x_i, x_j, gamma=0.5, coeff="m"):
    a = 0
    b = 0
    c = 0
    d = 0

    for i, j in zip(x_i, x_j):
        if i == 1 and j == 1:
            a += 1
        elif i == 1 and j == 0:
            b += 1
        elif i == 0 and j == 1:
            c += 1
        elif i == 0 and j == 0:
            d += 1

    print(f"Table: a = {a}, b = {b}, c = {c}, d = {d}")

    if coeff == "m":
        s_ij = gamma * (a + d) / (gamma * (a + d) + (1 - gamma) * (b + c))
    elif coeff == "s":
        s_ij = gamma * a / (gamma * a + (1 - gamma) * (b + c))

    return s_ij

x_1 = [1, 1, 0]
x_2 = [0, 0, 0]
x_3 = [0, 1, 1]

# Test data from lecture.
#x_1 = [1, 0, 1, 1]
#x_2 = [1, 0, 1, 0]
#x_3 = [0, 0, 0, 1]

print("M-Coefficients")
print("--------------")
print("Simple-Matching-Coefficient")
print(m_s_coeff(x_1, x_2, gamma=0.5))
print(m_s_coeff(x_1, x_3, gamma=0.5))
print(m_s_coeff(x_2, x_3, gamma=0.5))
print("\n")

print("Rogers and Tanimoto")
print(m_s_coeff(x_1, x_2, gamma=1/3))
print(m_s_coeff(x_1, x_3, gamma=1/3))
print(m_s_coeff(x_2, x_3, gamma=1/3))
print("\n")

print("Sokal and Sneath")
print(m_s_coeff(x_1, x_2, gamma=2/3))
print(m_s_coeff(x_1, x_3, gamma=2/3))
print(m_s_coeff(x_2, x_3, gamma=2/3))
print("\n")

print("S-Coefficients")
print("--------------")
print("Jaccard-Coefficient")
print(m_s_coeff(x_1, x_2, gamma=0.5, coeff="s"))
print(m_s_coeff(x_1, x_3, gamma=0.5, coeff="s"))
print(m_s_coeff(x_2, x_3, gamma=0.5, coeff="s"))
print("\n")

print("Sokal und Sneath")
print(m_s_coeff(x_1, x_2, gamma=1/3, coeff="s"))
print(m_s_coeff(x_1, x_3, gamma=1/3, coeff="s"))
print(m_s_coeff(x_2, x_3, gamma=1/3, coeff="s"))
print("\n")

# Ex 6d)
x_1 = [0, 1, 0]
x_2 = [1, 0, 0]
x_3 = [1, 1, 1]

print("M-Coefficients")
print("--------------")
print("Simple-Matching-Coefficient for m = 1")
print(m_s_coeff(x_1, x_2, gamma=0.5))
print(m_s_coeff(x_1, x_3, gamma=0.5))
print(m_s_coeff(x_2, x_3, gamma=0.5))
print("\n")

print("M-Coefficients")
print("--------------")
print("Simple-Matching-Coefficient for w = 1")
print(m_s_coeff(x_1, x_2, gamma=0.5))
print(m_s_coeff(x_1, x_3, gamma=0.5))
print(m_s_coeff(x_2, x_3, gamma=0.5))
print("\n")


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 13 23:48:48 2021

@author: Oscar
"""

# Tutorial 9: Trajectory of a thrown object

#Import Libraries
import numpy as np
import sympy as sp
import math as m
import matplotlib.pyplot as plt

# Define Parameters

# Initial Guess
#Min root
x_min0 = -3

#Max Root
x_max0 = 56

# Tolerance
Tolerance = 1e-10

# Initial Thrown angle
a0 = m.radians(30)

# Initial Velocity
v0 = 25 #(m/s)

# Initial Height
y0 = 1.5 #(m)

# Gravitational Accel
g = 9.81 #(m/s**2)

# Maxiterations
Max_iter = 100

# Define the Actual Equation
x = sp.Symbol('x')

func = sp.tan(a0)*x - (g/ (2 * v0**2 * (sp.cos(a0))**2) * x**2) + y0
f = sp.lambdify(x, func)

func_prime = sp.diff(func, x)
f_prime = sp.lambdify(x, func_prime)

func_prime2 = sp.diff(func, x, 2)
f_prime2 = sp.lambdify(x, func_prime2)

#%% Question A: roots
def NewtonRapshon(f, f_prime, x0, Tolerance, Max_iter):

    xn = x0

    for i in range(0,Max_iter):
        # If the value of the function at x is less than the tolerance, we print xn as it is close to the root
        if abs(f(xn)) < Tolerance:
            #print('Found solution after',i,'iterations.')
            return xn
        # Find the derivative at the value of xn, if none exists
        if f_prime(xn) == 0:
            #print('Zero derivative. No solution found.')
            return None
        # Calculate the new value of xn and repeat the process
        xn = xn - f(xn)/f_prime(xn)
    # If the Max number of iterations are reached, stop the program
    print('Exceeded maximum iterations. No solution found.')
    return

Min_root = NewtonRapshon(f, f_prime, x_min0, Tolerance, Max_iter)
Max_root = NewtonRapshon(f, f_prime, x_max0, Tolerance, Max_iter)
print('The ball will travel a distance of %.3f m' %Max_root)

#%% Question B: optimization
def NewtonOptimize(f_prime, f_prime2, x0, Tolerance, Max_iter):

    x_i = x0

    for i in range(0,Max_iter):
        xn = x_i - f_prime(x_i) / f_prime2(x_i)
        # If the value of the function at x is less than the tolerance, we print xn as it is close to the root
        if abs(f_prime(x_i)) < Tolerance:
            #print('Found solution after',i,'iterations.')
            return xn, f(xn)
        # Find the derivative at the value of xn, if none exists
        if np.float(f_prime(x_i)) == 0:
            #print('Solution found at', x_i)
            return None
        # Calculate the new value of xn and repeat the process
        x_i = xn
    # If the Max number of iterations are reached, stop the program
    print('Exceeded maximum iterations. No solution found.')
    return

max_distance, max_height = NewtonOptimize(f_prime, f_prime2, x_max0, Tolerance, Max_iter)
print('The Max height is achieved at x= %.3f m' %max_distance , 'reaching a height of y= %.3f m' %max_height)

#%% Question 3: Sensitivity to angle change
a0 = m.radians(29)
func = sp.tan(a0)*x - (g/ (2 * v0**2 * (sp.cos(a0))**2) * x**2) + y0
solutionDown = NewtonRapshon(f, f_prime, x_max0, Tolerance, Max_iter)
print('The ball will travel a distance of %.3f m with an angle %.3f' %(solutionDown,a0*180/np.pi))

a0 = m.radians(30)
func = sp.tan(a0)*x - (g/ (2 * v0**2 * (sp.cos(a0))**2) * x**2) + y0
solutionOld = NewtonRapshon(f, f_prime, x_max0, Tolerance, Max_iter)
print('The ball will travel a distance of %.3f m with an angle %.3f' %(solutionOld,a0*180/np.pi))

a0 = m.radians(31)
func = sp.tan(a0)*x - (g/ (2 * v0**2 * (sp.cos(a0))**2) * x**2) + y0
solutionUp = NewtonRapshon(f, f_prime, x_max0, Tolerance, Max_iter)
print('The ball will travel a distance of %.3f m with an angle %.3f' %(solutionUp,a0*180/np.pi))

sensitivityUp=abs((solutionUp-solutionOld)/solutionOld)
sensitivityDown=abs((solutionDown-solutionOld)/solutionOld)

print(f'A 1° increase at a launch angle of 30° gives a sensitivity of {sensitivityUp:.2f}metre per degree increase of the launch angle')
print(f'A 1° decrease at a launch angle of 30° gives a sensitivity of {sensitivityDown:.2f}metre per degree decrease of the launch angle')

#%% Question 4: if xf = 50, what is the optimum angle and pitch velocity

# Defining desired results
xf = 50

# Define Angles
angles = np.linspace(89.9, 0, 10000)

# Defining Functions
v = sp.Symbol('v')
a = sp.Symbol('a') # theta
vel = sp.tan(a)*xf - (g/ (2 * v**2 * (sp.cos(a))**2) * xf**2) + y0
f = sp.lambdify([v,a], vel)
vel_prime = sp.diff(vel, v)
v_prime = sp.lambdify([v,a], vel_prime)

velocity=[]
theta = np.zeros(len(angles))

for i in range(len(angles)):

    theta[i]=np.radians(angles[i])

    def NewtonVel(f, v_prime, v0, theta, Tolerance, Max_iter):

        vn = v0

        for n in range(0,Max_iter):
            # If the value of the function at x is less than the tolerance, we print xn as it is close to the root
            if abs(f(vn,theta[i])) < Tolerance:
                #print('Found solution after',i,'iterations.')
                return vn
            # Find the derivative at the value of xn, if none exists
            if v_prime(vn, theta[i]) == 0:
                #print('Zero derivative. No solution found.')
                return None
            # Calculate the new value of xn and repeat the process
            vn = vn - f(vn,theta[i])/v_prime(vn, theta[i])
        # If the Max number of iterations are reached, stop the program
        print('Exceeded maximum iterations. No solution found.')
        return vn

    velocity.append(NewtonVel(f, v_prime, 15, theta, Tolerance, Max_iter))

#%% Plot range of solutions
plt.plot(angles,velocity)
plt.ylabel('Launch Velocity [m/s]')
plt.xlabel('Launch Angle [Degrees]')
plt.ylim([10,40])
plt.xlim([0,90])
Opt_Angle=angles[velocity.index(min(velocity))]
a=min(velocity)
print(f'The minimum launch velocity to achieve a distance of 50 m is {a:.2f} m/s at an angle of {Opt_Angle:.2f} degrees')


from abc import ABC, abstractmethod
import numpy as np

class Cost(ABC):
    'This is an abstract class for Cost functions'
    def __init__(self, name="Default Cost"):
        self.name = name

    @abstractmethod
    def calculate(self, Y, yhat):
        '''Y is data, yhat is prediction'''
        self.cost = None

    @abstractmethod
    def derivative(self, Y, yhat):
        self.dA = None

    def __str__(self):
        return f'{self.__class__.__name__} cost is {self.cost}'

class MSE(Cost):
    'Mean Squared Error Cost'
    def __init__(self):
        super().__init__("MSE")

    def calculate(self, Y, Yhat):
        '''Returns normalized J = sum(|| Yhat - Y || ^ 2) / 2m'''
        assert (Y.shape == Yhat.shape)

        self.cost = np.square(Y - Yhat).mean()
        return self.cost

    def derivative(self, Y, Yhat):
        '''Returns J' = Yhat - Y'''
        self.dA = Yhat - Y
        return self.dA

class CrossEntropy(Cost):
    '''Loss function for binary classification'''
    def __init__(self):
        super().__init__(name="CrossEntropy")

    def calculate(self, Y, Yhat):
        '''Math:
            -Y * log(Yhat) - (1 - Y) * log(1 - Yhat)
        '''
        self.cost = ((0 - Y) * np.log(Yhat) - (1 - Y) * np.log(1 - Yhat)).mean()
        return self.cost

    def derivative(self, Y, Yhat):
        assert Y.shape == Yhat.shape

        self.dA = (0 - Y) / Yhat + (1 - Y) / (1 - Yhat)
        return self.dA

class CategoricalCrossEntropy(Cost):
    '''Loss function for multi-class classification'''
    def __init__(self):
        super().__init__(name="CategoricalCrossEntropy")

    def calculate(self, Y, Yhat):
        '''Math:
            -summation{yi ln( yhat i)}
        '''
        assert (Y.shape == Yhat.shape)

        self.cost = -np.sum( Y * np.log(Yhat) , axis=0 ).mean()
        return self.cost

    def derivative(self, Y, Yhat):
        '''Note: The return of this function is NOT a derivative.
        It is assumed that the derivative is going to be fed into Softmax layer
        during the propagation. Since derivative of the softmax layer
        dZ = Yhat - Y, it's just easier to return that.
        '''
        assert Y.shape == Yhat.shape

        self.dA = Yhat - Y
        return self.dA


from sympy import *
from numb
def methodOne(n):
	prime = "PRIME"
	composite = "COMPOSITE"
	decision = composite
	binaryDecision = True
	number = 2
	while (binaryDecision) and (number < n):
		if n%number == 0:
			binaryDecision = False
			decision = composite
			print("%s : NO"%number)
		else:
			binaryDecision = True
			decision = prime
			print("%s : YES"%number)
			number+=1
	return decision
def millerRabinPrimality(n,k):
	prime = "Probably PRIME" #true
	composite = "COMPOSITE" #false
	###write code here
	if n == 3:
		return True
	else if n == 2:
		return False
	else if n == 1:
		return True
	else if n <= 1:
		return False
	else if (n % 2) == 0:
		return False #number is even we are done
	else:
		r,d = factor(n-1) #must start with an even number!
		for i in range(k):
			#the loop thing
			a = random.randrange(2, n-1)
			x = pow(a,d,n)
			if x==1 or x== (n-1):
				continue
			done = False
			while r > 0 and not done:
				x = pow(x,2,n)
				if x ==1:
					return False
				if x== n-1:
					done = True
				r = r-1
			if not done:
				return False
	return True

def factor(n):
	#r is the number of steps
	#d is that odd number
	r = 0
	d = n
	if d % 2 == 0:
		r = r+1
		d = d//2
	else:
		r = r
		d = d
		return r, d
	return r, d
		'''
		write n − 1 as 2^r(·d, not in the variable) with d odd by factoring powers of 2 from n − 1
		WitnessLoop: repeat k times:
  		pick a random integer a in the range [2, n − 2]
   		x ← ad mod n
   		if x = 1 or x = n − 1 then
    	continue WitnessLoop
   		repeat r − 1 times:
      	x ← x2 mod n
      	if x = 1 then
        return composite
      	if x = n − 1 then
        continue WitnessLoop
   		return composite
		return probably prime
		'''
	return decision

n = 23 #temp
print(methodOne(n))
print(millerRabinPrimality(n,3))

'''
Write a driver file that generates 1,000 large random numbers (length = 200 bits each)
and checks their primality using your Miller Rabin algorithm.
Determine the accuracy of your Miller Rabin as a function of k (the number of iterations of your algorithm).
Print a table of “k” vs “accuracy rate”
'''
for i in range(1000):
	number = random.randrange(1000000000000000000000000,100000000000000000000000000000000000) #how do you decide this range??
	k = 1000
	isPrime = millerRabinPrimality(number, k)#how do you decide how many k??
	accruacy = 0.0
	####ok somehow determining accuracy rate
	print("K: %s        Accuracy Rate: %s" %(k,accuracy))


import numpy as np

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

def forward(X, w):
    weighted_sum = np.matmul(X, w)
    return sigmoid(weighted_sum)

def classify(X, w):
    y_hat = forward(X, w)
    labels = np.argmax(y_hat, axis=1)
    return labels.reshape(-1, 1)

def loss(X, Y, w):
    y_hat = forward(X, w)
    first_term = Y * np.log(y_hat)
    second_term = (1 - Y) * np.log(1 - y_hat)
    return -np.sum(first_term + second_term) / X.shape[0]

def gradient(X, Y, w):
    return np.matmul(X.T, (forward(X, w) - Y)) / X.shape[0]

def report(iteration, X_train, Y_train, X_test, Y_test, w):
    matches = np.count_nonzero(classify(X_test, w) == Y_test)
    n_test_examples = Y_test.shape[0]
    matches = matches * 100.0 / n_test_examples
    training_loss = loss(X_train, Y_train, w)
    print("%d - Loss: %.20f, %.2f%%" % (iteration, training_loss, matches))

def train(X_train, Y_train, X_test, Y_test, iterations, lr):
    w = np.zeros((X_train.shape[1], Y_train.shape[1]))
    for iteration in range(iterations):
        report(iteration, X_train, Y_train, X_test, Y_test, w)
        w -= gradient(X_train, Y_train, w) * lr
    report(iteration, X_train, Y_train, X_test, Y_test, w)
    return w


import numpy as np

def least_squares(X: np.ndarray, y: np.ndarray) -> np.ndarray:
    """
    Compute Least Squares Estimator.

    :param X: training features
    :param y: training labels
    :return: least squares weights
    """
    X_t = X.T
    # We use np.solve instead of inverting matrices for more numerical stability.
    return np.linalg.solve(X_t.dot(X), X_t.dot(y))

def ridge_regression(X: np.ndarray, y: np.ndarray, lambda_: float) -> np.ndarray:
    """
    Ridge regression using normal equations.

    :param X: training features
    :param y: training lables
    :param lambda_: penalty term
    :return: ridge regression weights
    """
    x_transpose = X.T
    left = x_transpose.dot(X) + lambda_ * np.identity(X.shape[1])
    right = x_transpose.dot(y)
    w = np.linalg.solve(left, right)
    return w


"""
This code is based on code found at: https://commons.wikimedia.org/wiki/File:Beta_distribution_pdf.svg by user Horas based on the work of user Krishnavedala
"""

from matplotlib.pyplot import *
from numpy import linspace
from scipy.stats import beta

x = linspace(0,1,75)

fig = figure()
ax = fig.add_subplot(111)
ax.plot(x,beta.pdf(x,0.5,0.5),label=r"$\alpha_1=\alpha_2=0.5$")
ax.plot(x,beta.pdf(x,5,1),label=r"$\alpha_1=5, \alpha_2=1$")
ax.plot(x,beta.pdf(x,1,3),label=r"$\alpha_1=1, \alpha_2=3$")
ax.plot(x,beta.pdf(x,2,2),label=r"$\alpha_1=2, \alpha_2=2$")
ax.plot(x,beta.pdf(x,1,1),label=r"$\alpha_1=1, \alpha_2=1$")
ax.grid(True)
ax.minorticks_on()
ax.legend(loc=9)
setp(ax.get_legend().get_texts(),fontsize='small')
ax.set_ylim(0,2.6)
ax.set_xlabel("Value of $P^x(1)$")
ax.set_ylabel("Probability Density")

fig.savefig("dirichlet_distribution_pdf.pdf",bbox_inches="tight",\
	pad_inches=.15)

fig = figure()
ax = fig.add_subplot(111)
ax.plot(x,beta.pdf(x,0.5,0.5),label=r"$\alpha_1=\alpha_2=0.5$")
ax.plot(x,beta.pdf(x,0.1,0.1),label=r"$\alpha_1=\alpha_2=0.1$")
ax.plot(x,beta.pdf(x,0.01,0.01),label=r"$\alpha_1=\alpha_2=0.01$")
ax.grid(True)
ax.minorticks_on()
ax.legend(loc=9)
setp(ax.get_legend().get_texts(),fontsize='small')
ax.set_ylim(0,2.6)
ax.set_xlabel("Value of $P^x(1)$")
ax.set_ylabel("Probability Density")

fig.savefig("dirichlet_distribution_sparse_pdf.pdf",bbox_inches="tight",\
	pad_inches=.15)

fig = figure()
ax = fig.add_subplot(111)
ax.plot(x,beta.pdf(x,2,4),label=r"$\alpha_1=2,\alpha_2=4$")
ax.plot(x,beta.pdf(x,100,200),label=r"$\alpha_1=100, \alpha_2=200$")
ax.plot(x,beta.pdf(x,1000,2000),label=r"$\alpha_1=1000, \alpha_2=2000$")
ax.grid(True)
ax.minorticks_on()
ax.legend(loc=9)
setp(ax.get_legend().get_texts(),fontsize='small')
ax.set_ylim(0,2.6)
ax.set_xlabel("Value of $P^x(1)$")
ax.set_ylabel("Probability Density")

fig.savefig("dirichlet_distribution_dense_pdf.pdf",bbox_inches="tight",\
	pad_inches=.15)


# modified from https://rosettacode.org/wiki/Julia_set#Python to remove the code that displayed the set
# we are only interested with the efficiency of the calculations, not the I/O

"""
Solution from:
https://codereview.stackexchange.com/questions/210271/generating-julia-set
"""
from functools import partial
from numbers import Complex
from typing import Callable

import numpy as np
from numba import jit

def douady_hubbard_polynomial(z: Complex,
                              c: Complex) -> Complex:
    """
    Monic and centered quadratic complex polynomial
    https://en.wikipedia.org/wiki/Complex_quadratic_polynomial#Map
    """
    return z ** 2 + c

@jit(nopython=True)
def julia_set(mapping: Callable[[Complex], Complex],
              *,
              min_coordinate: Complex,
              max_coordinate: Complex,
              width: int,
              height: int,
              iterations_count: int = 256,
              threshold: float = 2.) -> np.ndarray:
    """
    As described in https://en.wikipedia.org/wiki/Julia_set
    :param mapping: function defining Julia set
    :param min_coordinate: bottom-left complex plane coordinate
    :param max_coordinate: upper-right complex plane coordinate
    :param height: pixels in vertical axis
    :param width: pixels in horizontal axis
    :param iterations_count: number of iterations
    :param threshold: if the magnitude of z becomes greater
    than the threshold we assume that it will diverge to infinity
    :return: 2D pixels array of intensities
    """
    im, re = np.ogrid[min_coordinate.imag: max_coordinate.imag: height * 1j,
                      min_coordinate.real: max_coordinate.real: width * 1j]
    z = (re + 1j * im).flatten()

    live, = np.indices(z.shape)  # indexes of pixels that have not escaped
    iterations = np.empty_like(z, dtype=int)

    for i in range(iterations_count):
        z_live = z[live] = mapping(z[live])
        escaped = abs(z_live) > threshold
        iterations[live[escaped]] = i
        live = live[~escaped]
        if live.size == 0:
            break
    else:
        iterations[live] = iterations_count

    return iterations.reshape((height, width))

def single_julia_calc( ):
    mapping = partial(douady_hubbard_polynomial,
                      c=-0.7 + 0.27015j)  # type: Callable[[Complex], Complex]

    image = julia_set(mapping,
                      min_coordinate=-1.5 - 1j,
                      max_coordinate=1.5 + 1j,
                      width=800,
                      height=600,
                      iterations_count=256,
                      threshold=2.0)
def run_tests(n):
    for i in range(n):
        single_julia_calc( )

def main( ):
    run_tests(10)

if __name__ == "__main__":
    main( )


import numpy as np
import matplotlib.pyplot as plt
import pprint as pp

def missingIsNan(s):
	return np.nan if s == b'?' else float(s)

if __name__ == '__main__':
	# 1. Load the data.
	data = np.loadtxt("Data\\auto-mpg.data", usecols = range(8), converters = {3: missingIsNan})

	# 2. 'Clean' the data.
	Cdata = data[~np.isnan(data).any(axis = 1)]

	# 3. Split it into input (X) and target (T)
	# 	 Target = mpg (first column)
	#	 Input = remaining - columns 2 to 7
	T = Cdata[:, 0:1]
	X = Cdata[:, 1:]

	# 4. Append column of 1s to X
	X1 = np.insert(X, 0, 1, 1)

	# 5. Split the data into training (80 %) and testing data (20 %)
	nRows = X1.shape[0]
	nTrain = int(round(0.8*nRows))
	nTest = nRows - nTrain

	# Shuffle row numbers
	rows = np.arange(nRows)
	np.random.shuffle(rows)

	trainIndices = rows[:nTrain]
	testIndices = rows[nTrain:]

	# Check that training and testing sets are disjoint
	# print(np.intersect1d(trainIndices, testIndices))

	Xtrain = X1[trainIndices, :]
	Ttrain = T[trainIndices, :]
	Xtest = X1[testIndices, :]
	Ttest = T[testIndices, :]

	# 6. Find weights
	w = np.linalg.lstsq(Xtrain.T @ Xtrain, Xtrain.T @ Ttrain, rcond = None)

	# Keep only weights and discard other information.other
	w = w[0]

	# 7. Use model (weight) to predict (test).
	predict = Xtest @ w

	# 8. Compare to see how well we've done (plot) and RMSE.
	rmse = np.sqrt(np.mean((predict - Ttest)**2))

	print(rmse)


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Formula is from:
# link: https://en.wikipedia.org/wiki/Moran%27s_I

def get_moransI(ori_w, y):
    # w = spatial weight (topological or actual distance)
    # y = actual value
    # y_hat = mean value

    if not isinstance(y, np.ndarray):
        raise TypeError("Passed array (feature) should be in numpy array (ndim = 1)")
    if y.shape[0] != ori_w.shape[0]:
        raise ValueError("Feature array is not the same shape of weight")
    if ori_w.shape[0] != ori_w.shape[1]:
        raise ValueError("Weight array should be in square shape")

    w = np.copy(ori_w).astype('float')

    y_hat = np.mean(y)
    D = y - y_hat
    D_sq = (y - y_hat)**2

    N = y.shape[0]

    # ΣiΣj wij
    sum_W = np.sum(w)

    # W x D:
    # Wij x (Yi-Y_hat) x (Yj-Y_hat)
    for i in range(N):
        for j in range(N):
            if w[i,j]!=0:
                w[i,j]*=D[i]*D[j]

    moransI = (np.sum(w)/sum(D_sq)) * (N / sum_W)

    return round(moransI,8)

def get_expected_moransI(y):
    N = y.shape[0]
    EXP_I = -1 / (N-1)
    return EXP_I

def get_var_moransI(ori_w, y, EXP_I):

    if not isinstance(y, np.ndarray):
        raise TypeError("Passed array (feature) should be in numpy array (ndim = 1)")
    if y.shape[0] != ori_w.shape[0]:
        raise ValueError("Feature array is not the same shape of weight")
    if ori_w.shape[0] != ori_w.shape[1]:
        raise ValueError("Weight array should be in square shape")

    y_hat = np.mean(y)
    D = y - y_hat
    D_sq = (y - y_hat)**2

    N = y.shape[0]

    # use the original weights
    w = np.copy(ori_w).astype('float')
    sum_W = np.sum(w)

    # Calculating S1
    S1 = 0

    for i in range(N):
        for j in range(N):
            S1 += (w[i,j]+w[j,i])**2

    S1 *= 0.5

    # Calculating S2
    S2 = 0

    for i in range(N):
        sum_wij, sum_wji = 0, 0

        for j in range(N):
            sum_wij += w[i,j]
        for j in range(N):
            sum_wji += w[j,i]

        S2 += (sum_wij + sum_wji)**2

    # Calculating S3
    D_power_of_4 = [d**4 for d in D]

    S3 = (1/N * sum(D_power_of_4)) / (1/N * sum(D_sq))**2

    # Calculating S4
    S4 = (N**2 - 3*N + 3) * S1 - N * S2 + 3 * sum_W**2

    # Calculating S5
    S5 = (N**2 - N) * S1 - 2 * N * S2 + 6 * sum_W**2

    VAR_I = (N * S4 - S3 * S5) / ((N-1) * (N-2) * (N-3) * sum_W**2) - EXP_I**2

    return VAR_I

def hypothesis_testing(moransI, ori_w, y):
    # The null hypothesis for the test is that the data is randomly disbursed.
    # The alternate hypothesis is that the data is more spatially clustered than you would expect by chance alone.
    # Two possible scenarios are:
    # 1) A positive z-value: data is spatially clustered in some way.
    # 2) A negative z-value: data is clustered in a competitive way.
    #    For example, high values may be repelling high values or negative values may be repelling negative values.
    EXP_moransI = get_expected_moransI(y)
    VAR_moransI = get_var_moransI(ori_w, y, EXP_moransI)

    print('The expected value of Moran\'s I:', EXP_moransI)
    print('The variance of Moran\'s I:', VAR_moransI)
    print('Z score of Moran\'s I:', (moransI - EXP_moransI) / VAR_moransI ** 0.5)

def normalize_y(ori_w, y):
    W_Y = np.copy(ori_w)

    i, j = np.where(W_Y > 0)

    first = True
    lst = []
    avg_list = []

    for k in range(len(i)):
        if not first and i_idx != i[k]:
            avg_list.append(np.mean(lst))
            lst = []

        first = False

        i_idx, j_idx = i[k], j[k]

        lst.append(y[j_idx])

    avg_list.append(np.mean(lst))

    if not isinstance(y, np.ndarray):
        normalized_y = [(ele - np.mean(y.values)) / np.std(y.values) for ele in y.values]
    else:
        normalized_y = [(ele - np.mean(y)) / np.std(y) for ele in y]
    normalized_avg_list = [(ele - np.mean(avg_list)) / np.std(avg_list) for ele in avg_list]

    # transform into numpy array
    normalized_y = np.array(normalized_y)
    normalized_avg_list = np.array(normalized_avg_list)
    return normalized_y, normalized_avg_list

def moransI_scatterplot(moransI, ori_w, y):
    normalized_y, normalized_avg_list = normalize_y(ori_w, y)

    # HH
    hh_idx = np.intersect1d(np.where((np.array(normalized_y) > 0)), np.where((np.array(normalized_avg_list) > 0)))
    # low values surrounded by high values (outliers)
    lh_idx = np.intersect1d(np.where((np.array(normalized_y) <= 0)), np.where((np.array(normalized_avg_list) > 0)))
    # high values surrounded by low values (outliers)
    hl_idx = np.intersect1d(np.where((np.array(normalized_y) > 0)), np.where((np.array(normalized_avg_list) <= 0)))
    # LL
    ll_idx = np.intersect1d(np.where((np.array(normalized_y) <= 0)), np.where((np.array(normalized_avg_list) <= 0)))

    # plot different regions: High-High, High-Low, Low-High, Low-Low
    plt.scatter(normalized_y[hh_idx], normalized_avg_list[hh_idx],
                color='blue', alpha=0.5)
    plt.scatter(normalized_y[lh_idx], normalized_avg_list[lh_idx],
                color='blue', alpha=0.5)
    plt.scatter(normalized_y[hl_idx], normalized_avg_list[hl_idx],
                color='blue', alpha=0.5)
    plt.scatter(normalized_y[ll_idx], normalized_avg_list[ll_idx],
                color='blue', alpha=0.5)

    # separate the graph into 4 quartiles
    plt.axvline(x=0, linestyle='--', color='k', alpha=0.5)
    plt.axhline(y=0, linestyle='--', color='k', alpha=0.5)

    # plot moran's I
    x = np.linspace(-5, 5, 100)
    plt.plot(x, moransI * x, color='red')

    # layout
    plt.title(f'Moran\'s I: {moransI:.4f}')
    plt.xlabel('y')
    plt.ylabel('Lagged y')

def cal_EXP_Ii(wi, n):
    return (-wi) / (n - 1)

def cal_b2i(N, D_power_of_4, D_sq, D, i):
    numerator = N * (np.sum(D_power_of_4) - D[i])
    denominator = (np.sum(D_sq) - D_sq[i]) ** 2
    return numerator / denominator

def get_localMoransI(ori_w, y, y_name):
    # cal para
    y_hat = np.mean(y)
    N = y.shape[0]
    W = np.copy(ori_w).astype('float')

    D = y - y_hat
    D_sq = (y - y_hat) ** 2
    D_power_of_4 = [d ** 4 for d in D]

    # calculating the LISA of each y value
    lisa = []

    for i in range(N):
        tmp_I = 0
        for j in range(N):
            tmp_I += W[i, j] * D[j]
        tmp_I *= (N * D[i] / np.sum(D_sq))

        lisa.append(tmp_I)

    lisa = np.array(lisa).reshape(-1, 1)

    # Expected value of each local Moran's I
    EXP_Ii = []

    for i in range(N):
        EXP_Ii.append(cal_EXP_Ii(np.sum(W[i, :]), N))

    # calculating the variance of each local Moran's I
    b2i = []

    for i in range(N):
        b2i.append(cal_b2i(N, D_power_of_4, D_sq, D, i))

    VAR_Ii = []

    for i in range(N):
        var = (N - b2i[i]) * 1 ** 2 / (N - 1) - (
                    (2 * b2i[i] - N) * np.sum(np.multiply(W[i, :], W[i, :])) / ((N - 1) * (N - 2))) - EXP_Ii[i] ** 2
        VAR_Ii.append(var)

    # integrate the result as a dataframe
    Ii_results = {'Name':y_name, 'LISA':list(lisa.reshape(-1)), 'E(Ii)':EXP_Ii, 'VAR(Ii)':VAR_Ii}
    Ii_results = pd.DataFrame(Ii_results)
    Ii_results['Z Score'] = [
        (Ii_results.loc[i, 'LISA'] - Ii_results.loc[i, 'E(Ii)']) / Ii_results.loc[i, 'VAR(Ii)'] ** 0.5
        for i in range(Ii_results.shape[0])]

    return Ii_results

def LISA_scatterplot(moransI, ori_w, y, Ii_results):
    normalized_y, normalized_avg_list = normalize_y(ori_w, y)

    high_high = Ii_results[(abs(Ii_results['Z Score']) >= 2)].loc[Ii_results.LISA > 0].index
    insignificant = Ii_results[(abs(Ii_results['Z Score']) <= 2)].index
    low_low = Ii_results[(abs(Ii_results['Z Score']) >= 2)].loc[Ii_results.LISA < 0].index

    # plot different regions: High-High, Insignificant, Low-Low
    plt.scatter(normalized_y[high_high], normalized_avg_list[high_high],
                color='red', alpha=0.5)
    plt.scatter(normalized_y[insignificant], normalized_avg_list[insignificant],
                color='gray', alpha=0.2)
    plt.scatter(normalized_y[low_low], normalized_avg_list[low_low],
                color='blue', alpha=0.5)

    # separate the graph into 4 quartiles
    plt.axvline(x=0, linestyle='--', color='k', alpha=0.5)
    plt.axhline(y=0, linestyle='--', color='k', alpha=0.5)

    # plot moran's I
    x = np.linspace(-5, 20, 100)
    plt.plot(x, moransI * x, color='red')

    # layout
    plt.title(f'Moran\'s I: {moransI:.4f}')
    plt.xlabel('Views')
    plt.ylabel('W_Views')


import numpy as np
import matplotlib.pyplot as plt
import scipy.sparse as sp
import scipy.sparse.linalg as la
from functools import partial
import time

# Creating the MAtrix as describve din the report
def make_L(Nx, Ny):
    Dx = sp.diags((Nx-1)*[1])
    Dx += sp.diags((Nx-2)*[-1],-1)
    rowx = sp.csr_matrix((1,Nx-1))
    rowx[0,-1] = -1
    Dx = sp.vstack((Dx, rowx))
    Lx = Dx.transpose().dot(Dx)

    Dy = sp.diags((Ny-1)*[1])
    Dy += sp.diags((Ny-2)*[-1],-1)
    rowy = sp.csr_matrix((1,Ny-1))
    rowy[0,-1] = -1
    Dy = sp.vstack((Dy, rowy))
    Ly = Dy.transpose().dot(Dy)

    #Kronsum is jsut a cleaner way than creating Identity matrices and stuff
    return sp.kronsum(Lx,Ly)

def discretize(x_d, y_d, h):
    nx = int(x_d/h)
    ny = int(y_d/h)
    return make_L(nx,ny)/h/h

def get_grid(x_d, y_d, h):
    grid = np.mgrid[h:y_d:h, h:x_d:h]
    return (grid[1,:,:], grid[0,:,:])

def source(xx,yy):
    return 20*np.sin(np.pi*yy)*np.sin(1.5*np.pi*xx+np.pi)

def sourcevec(xx,yy):
    return np.reshape(source(xx,yy), (xx.shape[0]*xx.shape[1]))

def make_boundary_vec(x, y, xx, yy, f):
    Bx0 = f[0](xx[0,:])
    Bx1 = f[2](xx[0,:])
    By0 = f[1](yy[:,0])
    By1 = f[3](yy[:,0])

    return (Bx0, Bx1, By0, By1)

# Helper for BC creation
def unitary(n, first):
    x = np.zeros([n,1])
    if first:
        x[0] = 1
    else:
        x[-1] = 1
    return x

# Applying BC as described in the report
def apply_boundary(x, y, xx, yy, h):
    f1 = lambda x: np.sin(0.5*np.pi*x)
    f2 = lambda y: np.sin(2*np.pi*y)
    f3 = lambda x: 0*x
    f4 = lambda y: np.sin(2*np.pi*y)
    bds = make_boundary_vec(x, y, xx, yy, [f1, f2, f3, f4])
    ny = xx.shape[0]
    nx = xx.shape[1]
    B = sp.kron(unitary(ny,True),np.reshape(bds[0],[nx,1]))
    B += sp.kron(unitary(ny,False),np.reshape(bds[1],[nx,1]))
    B += sp.kron(np.reshape(bds[2],[ny,1]), unitary(nx,True))
    B += sp.kron(np.reshape(bds[3],[ny,1]), unitary(nx,False))
    return B/h/h

imshow = partial(plt.imshow)

#Domain is (0,0)x(x,y)
x = 2
y = 1

#choose grid spacing
h = 0.005

# Creating grid, L and BC
grid = get_grid(x,y,h)
L = discretize(x,y,h)
B = apply_boundary(2,1,*grid, h)

#Showing the structure of L
plt.spy(L, precision=0.001, marker='o', markersize=9*h)
plt.show()

#Creation of the source vector
sv = np.reshape(sourcevec(*grid), B.shape)

#Solving the system

start = time.time()
solution = la.spsolve(L,sv+B)
print("spsolve took: %fs"%(time.time()-start))

#Showing source then Solution
imshow(source(*grid)[::-1,:])
plt.colorbar()
plt.show()
imshow(np.reshape(solution, [grid[0].shape[0], grid[0].shape[1]])[::-1,:])
plt.colorbar()
plt.show()


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

def gauss(x,y):
  exp_term = -1.0/(2*(1-corr_hw**2))*((x-mean_h)**2/var_h+(y-mean_w)**2/var_w-2*corr_hw*(x-mean_h)*(y-mean_w)/(std_h*std_w))
  return 1.0 / (2*np.pi*std_h*std_w*np.sqrt(1-corr_hw**2)) * np.exp(exp_term)

# Importing dataset
## Dataset path
dataset_path = 'data/whData.dat'
## Column names
dt = np.dtype([('w', np.float), ('h', np.float), ('g', np.str_, 1)])
## Loading the dataset
dataset = np.loadtxt(dataset_path, dtype=dt, comments='#', delimiter=None)
## Loading dataset without outliers
dataset_without_outliers = dataset[np.where(dataset['w'] > 0)]
## Loading dataset outliers (w < 0)
dataset_outliers = dataset[np.where(dataset['w'] < 0)]

# Heights from the training data
train_data = dataset_without_outliers['h']
# Weights from the training data
train_labels = dataset_without_outliers['w']
# Heights to predict their corresponding weights
test_data = dataset_outliers['h']

# Calculate the Covariance matrix
covariance_matrix = np.cov(np.array([train_data, train_labels]))
## Heights mean
mean_h = np.mean(train_data)
## Heights variance
var_h = covariance_matrix[0, 0]
## Heights standard deviation
std_h = np.sqrt(var_h)
## Weight mean
mean_w = np.mean(train_labels)
## Weight variance
var_w = covariance_matrix[1, 1]
## Weights standard deviation
std_w = np.sqrt(var_w)
## Correlation coefficient between heights and weights
corr_hw = covariance_matrix[0, 1] / (std_h * std_w)

# Calculate (predict) the corresponding weight for given height
f = lambda x : mean_w + corr_hw * std_w / std_h * (x - mean_h)

# Plot data
plt.plot(train_data, train_labels, 'k.', label='Data')
# Plot predicted value
plt.plot(test_data, f(test_data), 'r.', label='Predictions')
# Plot the model
## Grid XY points to build contour
x = np.linspace(np.amin(train_data)-5, np.amax(train_data)+5, 1000)
y = np.linspace(np.amin(train_labels)-5, np.amax(train_labels)+5, 1000)
X, Y = np.meshgrid(x, y)
zi = mlab.bivariate_normal(X, Y, std_h, std_w, mean_h, mean_w, covariance_matrix[0, 1])
## Contour the gridded data
plt.contour(x,y,zi)

plt.xlim(np.amin(train_data)-5, np.amax(train_data)+5)
plt.ylim(np.amin(train_labels)-5, np.amax(train_labels)+5)
plt.legend(loc='upper left')
plt.savefig("out/task22/bivariate_gaussian.png", bbox_inches="tight", pad_inches=0)
plt.show()


import numpy as np
import matplotlib
matplotlib.rcParams['pdf.fonttype'] = 42
matplotlib.rcParams['ps.fonttype'] = 42
import matplotlib.pyplot as plt

def rastrigin(x, y):
    return 20 + x**2 - 10*np.cos(2*np.pi*x) + y**2 - 10*np.cos(2*np.pi*y)
def sphere(x, y):
    return x**2 + y**2
def ackley(x, y):
    return -20*np.exp(-0.2*np.sqrt(0.5*(x**2+y**2))) - np.exp(0.5*(np.cos(2*np.pi*x) + np.cos(2*np.pi*y))) + 20 + np.exp(1)

xR = np.linspace(-5.12, 5.12, 100)
yR = np.linspace(-5.12, 5.12, 100)
XR, YR = np.meshgrid(xR, yR)
ZR = rastrigin(XR, YR)

xS = np.linspace(-5.12, 5.12, 100)
yS = np.linspace(-5.12, 5.12, 100)
XS, YS = np.meshgrid(xS, yS)
ZS = sphere(XS, YS)

xA = np.linspace(-32.768, 32.768, 100)
yA = np.linspace(-32.768, 32.768, 100)
XA, YA = np.meshgrid(xA, yA)
ZA = ackley(XA, YA)

fig = plt.figure(figsize=(12,4))
ax = fig.add_subplot(131, projection='3d')
surf = ax.plot_surface(XR, YR, ZR, rstride=1, cstride=1, cmap='jet',
                       edgecolor='none')
ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$y$')
ax.set_title('Rastrigin function (2d)')
fig.colorbar(surf, shrink=0.2, aspect=5)

ax2 = fig.add_subplot(132, projection='3d')
surf2 = ax2.plot_surface(XS, YS, ZS, rstride=1, cstride=1, cmap='jet',
                       edgecolor='none')
ax2.set_xlabel(r'$x$')
ax2.set_ylabel(r'$y$')
ax2.set_title('Sphere function (2d)')
fig.colorbar(surf2, shrink=0.2, aspect=5)

ax3 = fig.add_subplot(133, projection='3d')
surf3 = ax3.plot_surface(XA, YA, ZA, rstride=1, cstride=1, cmap='jet',
                       edgecolor='none')
ax3.set_xlabel(r'$x$')
ax3.set_ylabel(r'$y$')
ax3.set_title('Ackley function (2d)')
fig.colorbar(surf3, shrink=0.2, aspect=5)
fig.tight_layout()

#plt.savefig("functions_landscape.eps", format="eps")
plt.savefig("functions_landscape.png", format="png",dpi=600)
plt.show()


#!/usr/bin/env python

# Plots various neural net activation functions.

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

folder = "/Users/kpmurphy/github/pyprobml/figures"

e = np.exp(1)
x = np.linspace(-4, 4, 1000)
y = e**x / (e**x + 1)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('sigmoid function')
plt.show()
plt.savefig(os.path.join(folder, 'sigmoidPlot.pdf'))

y = np.tanh(x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('tanh function')
plt.show()
plt.savefig(os.path.join(folder, 'tanhPlot.pdf'))

y = np.log(1+np.exp(x))
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('softplus function')
plt.show()
plt.savefig(os.path.join(folder, 'softplusPlot.pdf'))

y = 1.0*(x>0)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('Heaviside function')
plt.show()
plt.savefig(os.path.join(folder, 'heavisidePlot.pdf'))

y = np.maximum(0, x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('ReLU function')
plt.show()
plt.savefig(os.path.join(folder, 'reluPlot.pdf'))

lam = 0.5
y = np.maximum(0, x) + lam*np.minimum(0, x)
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('Leaky ReLU function')
plt.show()
plt.savefig(os.path.join(folder, 'leakyReluPlot.pdf'))

lam = 0.5
y = np.maximum(0, x) + np.minimum(0, lam*(e**x - 1))
fig, ax = plt.subplots()
ax.plot(x, y)
plt.title('ELU function')
plt.show()
plt.savefig(os.path.join(folder, 'eluPlot.pdf'))


'''
Defines function `calc_linear_kernel`

Examples
--------
>>> np.set_printoptions(precision=3, suppress=1)

# Kernel evaluations with F=1 features
>>> x_zero_11 = np.asarray([[0.0]])
>>> x_one_11 = np.asarray([[1.0]])

# Linear kernel k(0,0) should be zero
>>> k_11 = calc_linear_kernel(x_zero_11, x_zero_11)
>>> k_11.ndim
2
>>> k_11
array([[0.]])

# Linear kernel k(1,1) should be one
>>> calc_linear_kernel(x_one_11, x_one_11)
array([[1.]])

# Linear kernel k(1, 3.456) should be 3.456
>>> calc_linear_kernel(x_one_11, 3.456 * x_one_11)
array([[3.456]])

# Part 2: Kernel evaluations with F=2 features and several examples at once
>>> x_train_32 = np.asarray([[0.0, 0.0], [1.0, 1.0], [2.0, 2.0]])
>>> calc_linear_kernel(x_train_32, x_train_32)
array([[0., 0., 0.],
       [0., 2., 4.],
       [0., 4., 8.]])

>>> x_test_22 = np.asarray([[-1.0, -2.0], [2.2, 1.2]])
>>> calc_linear_kernel(x_test_22, x_train_32)
array([[ 0. , -3. , -6. ],
       [ 0. ,  3.4,  6.8]])

'''

import numpy as np

def calc_linear_kernel(x_QF, x_train_NF=None):
    ''' Evaluate linear kernel matrix between two datasets.

    Will compute the kernel function for all possible pairs of feature vectors,
    one from the query dataset, one from the reference training dataset.

	Args
	----
    x_QF : 2D numpy array, shape (Q, F) = (n_query_examples, n_features)
		Feature array for *query* dataset
		Each row corresponds to the feature vector on example

    x_train_NF : 2D numpy array, shape (N, F) = (n_train_examples, n_features)
		Feature array for reference *training* dataset
		Each row corresponds to the feature vector on example

    Returns
    -------
    k_QN : 2D numpy array, shape (Q, N)
        Entry at index (q,n) corresponds to the kernel function evaluated
        at the feature vectors x_QF[q] and x_train_NF[n]
    '''
    assert x_QF.ndim == 2
    assert x_train_NF.ndim == 2

    Q, F = x_QF.shape
    N, F2 = x_train_NF.shape
    assert F == F2

    k_QN = np.zeros((Q, N))
    # TODO compute linear kernel between rows of x_QF and rows of x_train_NF

    # Ensure the kernel matrix positive definite
    # By adding a small positive to the diagonal
    M = np.minimum(Q, N)
    k_QN[:M, :M] += 1e-08 * np.eye(M)
    return k_QN


import numpy as np

# scipy.stats.cramervonmises

# Suponha que desejamos testar se os dados gerados por
# scipy.stats.norm.rvs foram, de fato, extraídos da
# distribuição normal padrão. Escolhemos um nível de
# significância alfa = 0,05.

from scipy import stats
rng = np.random.default_rng()
x = stats.norm.rvs(size=500, random_state=rng)
res = stats.cramervonmises(x, 'norm')
res.statistic, res.pvalue
#CramerVonMisesResult(statistic=0.1276613786697622, pvalue=0.46556649116631343)

#O valor de p excede nosso nível de significância escolhido,
# portanto, não rejeitamos a hipótese nula de que a amostra observada
# é extraída da distribuição normal padrão.

# Agora, suponha que desejamos verificar se as mesmas amostras
# deslocadas em 2,1 são consistentes com o fato de terem sido tiradas
# de uma distribuição normal com uma média de 2.

y = x + 2.1
res = stats.cramervonmises(y, 'norm', args=(2,))
#CramerVonMisesResult(statistic=0.7040268563073291, pvalue=0.012420322007088758)

#Aqui, usamos a palavra-chave args para especificar a média (loc)
# da distribuição normal para testar os dados. Isso é equivalente ao
# seguinte, em que criamos uma distribuição normal com média 2,1 e,
# em seguida, passamos seu cdf método como um argumento.

frozen_dist = stats.norm(loc=2)
res = stats.cramervonmises(y, frozen_dist.cdf)
res.statistic, res.pvalue
#(0.7040268563073291, 0.012420322007088758)

#Em todos dos casos, rejeitaríamos a hipótese nula de que a amostra
# observada é retirada de uma distribuição normal com uma média de 2
# (e variância padrão de 1) porque o valor de p 0,01 é menor do que
# nosso nível de significância escolhido.

# scipy.stats.cramervonmises_2samp

# Suponha que desejamos testar se duas amostras geradas por scipy.stats.norm.rvstêm a mesma distribuição. Escolhemos um nível de significância alfa = 0,05.

from scipy import stats
rng = np.random.default_rng()
x = stats.norm.rvs(size=100, random_state=rng)
y = stats.norm.rvs(size=70, random_state=rng)
res = stats.cramervonmises_2samp(x, y)
res.statistic, res.pvalue #(0.12726890756302467, 0.47115054777270216)

#O valor p excede nosso nível de significância escolhido, portanto,
# não rejeitamos a hipótese nula de que as amostras observadas são
# retiradas da mesma distribuição.
#Para tamanhos de amostra pequenos, pode-se calcular os valores p exatos:

x = stats.norm.rvs(size=7, random_state=rng)
y = stats.t.rvs(df=2, size=6, random_state=rng)
res = stats.cramervonmises_2samp(x, y, method='exact')
res.statistic, res.pvalue #(0.042124542124541975, 0.9801864801864801)

# O valor p com base na distribuição assintótica é uma boa aproximação,
# embora o tamanho da amostra seja pequeno.

res = stats.cramervonmises_2samp(x, y, method='asymptotic')
res.statistic, res.pvalue #(0.042124542124541975, 0.9937806294485269)

#Independentemente do método, não se rejeitaria a hipótese nula no
# nível de significância escolhido neste exemplo.

x = stats.norm.rvs(size=700, random_state=rng)
y = stats.t.rvs(df=2, size=600, random_state=rng)
res = stats.cramervonmises_2samp(x, y)
print(res) #CramerVonMisesResult(statistic=0.6771188644688664, pvalue=0.014472209121915047)

#scipy.stats.kstest

from scipy import stats
rng = np.random.default_rng()
x = np.linspace(-15, 15, 9)
stats.kstest(x, 'norm')
# KstestResult(statistic=0.4443560271592436, pvalue=0.03885014008678778)
stats.kstest(stats.norm.rvs(size=100, random_state=rng), stats.norm.cdf)

#As linhas acima são equivalentes a:

stats.kstest(stats.norm.rvs, 'norm', N=100)

#Testando variáveis aleatórias t distribuídas em relação à distribuição normal

# Com 100 graus de liberdade, a distribuição t parece próxima da distribuição normal,
# e o teste KS não rejeita a hipótese de que a amostra veio da distribuição normal:

stats.kstest(stats.t.rvs(100, size=100, random_state=rng), 'norm')
# KstestResult(statistic=0.10694118810178882, pvalue=0.18878890547885985)

#Com 3 graus de liberdade, a distribuição t é suficientemente diferente da distribuição normal,
# de modo que podemos rejeitar a hipótese de que a amostra veio da distribuição normal no
# nível de 10%:

stats.kstest(stats.t.rvs(3, size=100, random_state=rng), 'norm')
#KstestResult(statistic=0.11786287323060995, pvalue=0.11456645992107758)

#scipy.stats.ks_2samp

from scipy import stats
rng = np.random.default_rng()
n1 = 200  # tamanho da primeira amostra
n2 = 300  # tamanho da segunda amostra

#Para uma distribuição diferente, podemos rejeitar a hipótese nula uma vez que o valor p está abaixo de 1%:

rvs1 = stats.norm.rvs(size=n1, loc=0., scale=1, random_state=rng)
rvs2 = stats.norm.rvs(size=n2, loc=0.5, scale=1.5, random_state=rng)
stats.ks_2samp(rvs1, rvs2)
# KstestResult(statistic=0.24, pvalue=1.5876939054582095e-06)

#Para uma distribuição ligeiramente diferente, não podemos rejeitar a hipótese nula em um alfa de 10% ou inferior,
# uma vez que o valor de p em 0,219 é superior a 10%

rvs3 = stats.norm.rvs(size=n2, loc=0.01, scale=1.0, random_state=rng)
stats.ks_2samp(rvs1, rvs3)
# KstestResult(statistic=0.095, pvalue=0.2192140768654085)

#Para uma distribuição idêntica, não podemos rejeitar a hipótese nula uma vez que o valor p é alto, 41%:

rvs4 = stats.norm.rvs(size=n2, loc=0.0, scale=1.0, random_state=rng)
stats.ks_2samp(rvs1, rvs4)

#scipy.stats.anderson_ksamp

from scipy import stats
rng = np.random.default_rng()

# A hipótese nula de que as duas amostras aleatórias vêm da mesma distribuição pode ser rejeitada
# no nível de 5% porque o valor de teste retornado é maior do que o valor crítico para 5% (1,961),
# mas não no nível de 2,5%. A interpolação dá um nível de significância aproximado de 3,2%:

stats.anderson_ksamp([rng.normal(size=50),
rng.normal(loc=0.5, size=30)])
# p valor = significance_level = 0.07396028404997687

# A hipótese nula não pode ser rejeitada para três amostras de uma distribuição idêntica.
# O valor p relatado (25%) foi limitado e pode não ser muito preciso (uma vez que corresponde ao valor 0,449,
# enquanto a estatística é -0,731):

stats.anderson_ksamp([rng.normal(size=50),
rng.normal(size=30), rng.normal(size=20)])
#Anderson_ksampResult(statistic=-0.5917988120678772, critical_values=array([0.44925884, 1.3052767 , 1.9434184 , 2.57696569, 3.41634856, 4.07210043, 5.56419101]), significance_level=0.25)

#scipy.stats.ansari

from scipy.stats import ansari
rng = np.random.default_rng()
#Para esses exemplos, criaremos três conjuntos de dados aleatórios. Os dois primeiros, com tamanhos 35 e 25,
# são extraídos de uma distribuição normal com média 0 e desvio padrão 2. O terceiro conjunto de dados tem
# tamanho 25 e é extraído de uma distribuição normal com desvio padrão 1,25.

x1 = rng.normal(loc=0, scale=2, size=35)
x2 = rng.normal(loc=0, scale=2, size=25)
x3 = rng.normal(loc=0, scale=1.25, size=25)

# Primeiramente, aplicamos ansari para x1 e x2 . Essas amostras são retiradas da mesma distribuição, portanto,
# esperamos que o teste de Ansari-Bradley não nos leve a concluir que as escalas das distribuições são diferentes.

ansari(x1, x2)
#AnsariResult(statistic=534.0, pvalue=0.811752031516162)

# Com um valor de p próximo de 1, não podemos concluir que existe uma diferença significativa nas escalas (conforme o esperado).

# Agora aplique o teste a x1 e x3 :

ansari(x1, x3)
# AnsariResult(statistic=464.0, pvalue=0.01846645873767982)

# A probabilidade de observar tal valor extremo da estatística sob a hipótese nula de escalas iguais é de apenas 1,84%.
# Tomamos isso como evidência contra a hipótese nula em favor da alternativa: as escalas das distribuições das quais as
# amostras foram retiradas não são iguais.

# Podemos usar o parâmetro alternativo para realizar um teste unilateral. No exemplo acima, a escala de x1 é maior do que x3 e,
# portanto, a proporção das escalas de x1 e x3 é maior do que 1. Isso significa que o valor p quando alternative='greater'deve estar próximo de 0 e,
# portanto, devemos ser capazes de rejeitar o nulo hipótese:

ansari(x1, x3, alternative='greater')

#Como podemos ver, o valor p é de fato bastante baixo. O uso de alternative='less'deve,
# portanto, produzir um grande valor p:

ansari(x1, x3, alternative='less')

# scipy.stats.fligner

#Testa se as listas de a , b e c vêm de populações com variâncias iguais.

from scipy.stats import fligner
a = [8.88, 9.12, 9.04, 8.98, 9.00, 9.08, 9.01, 8.85, 9.06, 8.99]
b = [8.88, 8.95, 9.29, 9.44, 9.15, 9.58, 8.36, 9.18, 8.67, 9.05]
c = [8.95, 9.12, 8.95, 8.85, 9.03, 8.84, 9.07, 8.98, 8.86, 8.98]
stat, p = fligner(a, b, c)
p #pvalue=0.00450826080004775

#O pequeno valor de p sugere que as populações não têm variâncias iguais.

#Isso não é surpreendente, dado que a variância da amostra de b é muito maior do que
# a de a e c :

[np.var(x, ddof=1) for x in [a, b, c]] #[0.007054444444444413, 0.13073888888888888, 0.008890000000000002]

#outros testes de variancia

from scipy.stats import bartlett, levene
bartlett(a, b, c)
#BartlettResult(statistic=22.789434813726768, pvalue=1.1254782518834628e-05)
levene(a, b, c)
#LeveneResult(statistic=7.584952754501659, pvalue=0.002431505967249681)

#scipy.stats.jarque_bera

from scipy import stats
rng = np.random.default_rng()
x = rng.normal(0, 1, 100000)
jarque_bera_test = stats.jarque_bera(x)
jarque_bera_test
# Jarque_beraResult(statistic=3.3415184718131554, pvalue= 0.18810419594996775)
jarque_bera_test.statistic
# 3.3415184718131554
jarque_bera_test.pvalue
# 0.18810419594996775

# scipy.stats.kurtosistest
from scipy.stats import kurtosistest
kurtosistest(list(range(20)))
# KurtosistestResult(statistic=-1.7058104152122062, pvalue=0.08804338332528348)
kurtosistest(list(range(20)), alternative='less')
# KurtosistestResult(statistic=-1.7058104152122062, pvalue=0.04402169166264174)
kurtosistest(list(range(20)), alternative='greater')
# KurtosistestResult(statistic=-1.7058104152122062, pvalue=0.9559783083373583)
rng = np.random.default_rng()
s = rng.normal(0, 1, 1000)
kurtosistest(s)
#KurtosistestResult(statistic=-0.3188545786000282, pvalue=0.7498367888656665)

# Pacote statsmodels

import numpy as np
import pandas as pd
import statsmodels.api as sm

nsample = 100
x = np.linspace(0, 10, 100)
X = np.column_stack((x, x ** 2))

print(X[0:3])
beta = np.array([1, 0.1, 10])

e = np.random.normal(size=nsample)
print(e[0:3])

X = sm.add_constant(X)
print(X[0:3])

y = np.dot(X, beta) + e
print(y[0:3])

model = sm.OLS(y, X)
results = model.fit()
print(results.summary())

#                             OLS Regression Results
# ==============================================================================
# Dep. Variable:                      y   R-squared:                       1.000
# Model:                            OLS   Adj. R-squared:                  1.000
# Method:                 Least Squares   F-statistic:                 4.409e+06
# Date:                Wed, 20 Oct 2021   Prob (F-statistic):          3.21e-241
# Time:                        19:47:11   Log-Likelihood:                -141.92
# No. Observations:                 100   AIC:                             289.8
# Df Residuals:                      97   BIC:                             297.6
# Df Model:                           2
# Covariance Type:            nonrobust
# ==============================================================================
#                  coef    std err          t      P>|t|      [0.025      0.975]
# ------------------------------------------------------------------------------
# const          1.2849      0.299      4.302      0.000       0.692       1.878
# x1            -0.0167      0.138     -0.121      0.904      -0.291       0.257
# x2            10.0099      0.013    749.389      0.000       9.983      10.036
# ==============================================================================
# Omnibus:                        3.186   Durbin-Watson:                   2.138
# Prob(Omnibus):                  0.203   Jarque-Bera (JB):                1.927
# Skew:                          -0.061   Prob(JB):                        0.382
# Kurtosis:                       2.331   Cond. No.                         144.
# ==============================================================================

print("Parameters: ", results.params)
print("R2: ", results.rsquared)

import numpy,from scipy,import statsmodels


import numpy as np
from scipy.sparse import csr_matrix, lil_matrix
from scipy.sparse.linalg import spsolve
import time

# PyAMG algebraic multigrid
from pyamg import *

def solvePoisson(mesh, boundaryVals=dict(), method='numpy-sparse', verbose=True):
    """
    Solve a Poisson problem on a mesh. Returns a vertex => value dictionary containing
    every vertex in the mesh (including any boundary vertices).
        boundaryVals: a {vertex => float} dictionary containing Dirichlet boundary
            conditions
        method: which solver should be used
                    {numpy-sparse, numpy-dense, pyamg-RS}
    """

    if verbose:
        print("Solving Poisson problem on mesh with " + str(len(mesh.verts)) + " vertices")
        print("  - Dirichlet boundary conditions were given at " + str(len(boundaryVals)) + " vertices")
        print("  - Using method: " + method)

    # Make sure a valid method was given
    if method not in ['numpy-sparse', 'numpy-dense','pyamg-RS']:
        raise ValueError("ERROR: Invalid method ("+method+") given to solvePoisson")

    # Announce setup and start timing
    if verbose:
        print("  Setting up problem...")
        t0 = time.time()

    # Enumerate the interior vertices at which we will compute a solution
    interiorVerts = mesh.verts - set(boundaryVals.keys())
    nInteriorVerts = len(interiorVerts)
    ind = mesh.enumerateVertices(interiorVerts)

    ## Build the poisson matrix

    if method == 'numpy-dense':
        A = np.zeros((nInteriorVerts,nInteriorVerts))
    else:
        A = lil_matrix((nInteriorVerts, nInteriorVerts))
    b = np.zeros((nInteriorVerts,1))

    # Iterate over the interior vertices, adding terms for the neighbors of each
    for vert in interiorVerts:
        i = ind[vert]
        for (neighEdge, neighVert) in vert.adjEdgeVerts():
            w = neighEdge.cotanLaplace

            # Boundary neighbor
            if neighVert in boundaryVals:
                A[i,i] -= w
                b[i] -= w * boundaryVals[neighVert]
            # Interior neighbor
            else:
                j = ind[neighVert]
                A[i,j] += w
                A[i,i] -= w

    # Announce solve and more timing
    if verbose:
        tSetup = time.time() - t0
        print("  ...setup complete.")
        print("  Solving system...")
        t0 = time.time()

    # Solve the system of equations

    if method == 'numpy-dense':
        sol = np.linalg.solve(A,b)
    elif method == 'numpy-sparse':
        A = A.tocsr()
        sol = spsolve(A, b)
    elif method == 'pyamg-RS':
        A = A.tocsr()
        ml = ruge_stuben_solver(A)
        sol = ml.solve(b, tol=1e-8)

    # Announce solve completed and timing info
    if verbose:
        tSolve = time.time() - t0
        print("  ...solve complete.")
        print("  setup time: "+str(tSetup)+" solve time: " + str(tSolve))

    # Build the result dictionary
    res = dict()
    for v in interiorVerts:
        res[v] = sol[ind[v]]

    # Add the values from the boundary conditions
    res.update(boundaryVals)

    return res


import numpy as np
import matplotlib.pyplot as plt
def data():
    global x,y,n
    x = []
    y = []
    with open('input.txt','r+') as f:
            for line in f.readlines():
                xt = float(line.split(' ')[0])
                yt = float(line.split(' ')[1])
                check = True
                for x_check in x:
                    if x_check == xt:
                        check = False
                        break
                if check:
                    x.append(xt)
                    y.append(yt)
    x = np.asarray(x)
    y = np.asarray(y)
    n = len(x)-1
    return x,y,n
x,y,n=data()
def spline3(k,x0):
    x, y, n = data()
    h = np.diff(x)
    # Duoi day la ham tính m
    def tinhM(x,y,n):
        m=np.empty(n+1)
        d=np.empty(n+1)
        anpha=np.empty(n+1)
        beta=np.empty(n+1)
        muy=np.empty(n+1)
        lamda=np.empty(n+1)
        h=np.diff(x)
        dh0=(y[1]-y[0])/(x[1]-x[0])
        dhn=(y[n]-y[n-1])/(x[n]-x[n-1])
        d[0]=6/h[0]*((y[1]-y[0])/h[0]-dh0)
        d[n]=6/h[n-1]*(dhn-(y[n]-y[n-1])/h[n-1])
        anpha[1]=1/(-2)
        beta[1]=d[0]/2
        for i in range(1,n):
            d[i]=6*((y[i+1]-y[i])/h[i]-(y[i]-y[i-1])/h[i-1])/(h[i]+h[i-1])
        for i in range(1,n):
            muy[i]=h[i-1]/(h[i-1]+h[i])
            lamda[i]=h[i]/(h[i-1]+h[i])
        for i in range(1,n):
            anpha[i+1]=lamda[i]/(-2-anpha[i]*muy[i])
            beta[i+1]=(muy[i]*beta[i]-d[i])/(-2-anpha[i]*muy[i])
        m[n]=(1*beta[n]-d[n])/(-2-1*anpha[n])
        for i in range(n-1,-1,-1):
            m[i]=anpha[i+1]*m[i+1]+beta[i+1]
        return m
    m=tinhM(x,y,n)
    if k==1:
        for j in range(1,n+1):
            print('S3' +'['+ str(x[j-1])+','+str(x[j])+']'+'= ' + str(round(m[j - 1] / (6 * h[j - 1]),3)) + '(' + str(x[j]) + '- x)^3' +
                  '+ ' + str(round(m[j] / (h[j - 1] * 6),3)) + '(x-' + str(x[j - 1]) + ')^3' +
                  '+ ' + str(round((1 / h[j - 1]) * (y[j - 1] - m[j - 1] / 6 * h[j - 1] ** 2),3)) + '(' + str(x[j]) + '-x)' +
                  '+ ' + str(round((y[j] - (m[j] / 6) * h[j - 1] ** 2) / h[j - 1],3)) + '(x-' + str(x[j - 1]) + ')')
    for i in range(1,n+1):
        if x[i-1]<=x0 and x0<=x[i]:
            s3=m[i-1]/(6*h[i-1])*(x[i]-x0)**3+m[i]/(h[i-1]*6)*(x0-x[i-1])**3+(1/h[i-1])*(y[i-1]-m[i-1]/6*h[i-1]**2)*(x[i]-x0) +(y[i]-(m[i]/6)*h[i-1]**2)*(x0-x[i-1])/h[i-1]
    return s3
def spline1(k,x0):
    x, y, n = data()
    h = np.diff(x)
    if k==1:
        for i in range(1,n+1):
            print('S1'+'['+str(x[i-1])+', '+str(x[i])+']='+str(round(y[i-1]/h[i-1],3))+'('+str(x[i])+'-x)'+'+'+str(round(y[i]/h[i-1],3))+'(x-'+str(x[i-1])+')')
    for i in range(1,n+1):
        if x[i-1]<=x0 and x0<=x[i]:
            s1=y[i-1]/h[i-1]*(x[i]-x0)+y[i]/h[i-1]*(x0-x[i-1])
    return s1
def spline2(k,x0):
    x, y, n = data()
    h = np.diff(x)
    m=np.empty(n+1)
    m[0]=(y[1]-y[0])/h[0]
    for i in range(1,n+1):
        m[i]=2/h[i-1]*(y[i]-y[i-1])-m[i-1]
    if k==1:
        for i in range(1,n+1):
            print('S2'+'['+str(x[i-1])+', '+str(x[i])+']='+str(round(-m[i-1]/2/h[i-1],3))+'('+str(x[i])+'-x)^2+'+str(round(m[i]/2/h[i-1],3))+'(x-'+str(x[i-1])+')^2'+'+'+str(round(y[i]-m[i]/2*h[i-1],3)))
    for i in range(1,n+1):
        if x[i-1]<=x0 and x0<=x[i]:
            s2=-m[i-1]/2/h[i-1]*(x[i]-x0)**2+m[i]/2/h[i-1]*(x0-x[i-1])**2+y[i]-m[i]/2*h[i-1]
    return s2
print("Cac da thuc spline bac 3")
spline3(1,x[0])
print("========================================================================================")
print("Cac da thuc spline bac 1:")
spline1(1,x[0])
print("========================================================================================")
print("Cac da thuc spline bac 2")
spline2(1,x[0])
x0=np.linspace(x[0], x[n], 1000)
y3=[]
y1=[]
y2=[]
for i in x0:
  y3.append(spline3(0,i))
  y1.append(spline1(0,i))
  y2.append(spline2(0,i))
plt.plot(x0,y3)
plt.plot(x0,y1)
plt.plot(x0,y2)
plt.scatter(x,y)
plt.show()

Topic 3 - Function Approximation/22.Spline_Curve/SPLINE.py


from numpy.testing import assert_almost_equal
import numpy as np
from ..utils.geometry import dist, dot, cross, mod

p = np.array([3.115, 1.287, -0.589])
q = np.array([6.072, 2.231, 1.554])

def test_dist():
    distance = np.linalg.norm(p - q)
    assert_almost_equal(dist(p, q), distance)

def test_dot():
    assert_almost_equal(dot(p, q), np.dot(p, q))

def test_cross():
    assert_almost_equal(cross(p, q), np.cross(p, q))

def test_mod():
    assert_almost_equal(mod(p), np.linalg.norm(p))


import numpy as np

# These functions to calculate orbital values and vectors can be used separately, or on the BodyOrb class

def r_elliptical(a,e,f):
    '''
    Function to find distance, r, at true anomaly, f, around a keplerian orbit

    Parameters
    ----------
    a
        semi major axis (m)
    e
        orbital eccentricity
    f
        true anomaly (rad)
    '''
    r = (a*(1.0-e**2))/(1.0+e*np.cos(f))
    return r

def calc_ep_vec(node,peri,inc):
    '''
    Function to find the normalised Lenz vector along the apsidal line, uses PSS eq11.39a

    Parameters
    ----------
    node
        longitude of ascending node
    peri
        argument of pericentre
    inc
        inclination
    '''
    epx=(np.cos(node)*np.cos(peri))-(np.cos(inc)*np.sin(node)*np.sin(peri)) # x component
    epy=(np.sin(node)*np.cos(peri))+(np.cos(inc)*np.cos(node)*np.sin(peri)) # y component
    epz=np.sin(inc)*np.sin(peri) # z component

    return  np.array([epx, epy, epz])

def calc_eQ_vec(node,peri,inc):
    '''
    Function to find the normalised e_Q vector which is perp to h and e_a and
    lies in the orbital plane, uses PSS eq11.39b

    Parameters
    ----------
    node
        longitude of ascending node
    peri
        argument of pericentre
    inc
        inclination
    '''
    eQx=-(np.cos(node)*np.sin(peri))-(np.cos(inc)*np.sin(node)*np.cos(peri)) #x
    eQy=-(np.sin(node)*np.sin(peri))+(np.cos(inc)*np.cos(node)*np.cos(peri)) #y
    eQz=np.sin(inc)*np.cos(peri) #z

    return np.array([eQx, eQy, eQz])

def M_from_E(E,e):
    """
    Find mean anomaly from the eccentric anomaly

    Parameters
    ----------
    E
        eccentric anomaly
    e
        eccentricity
    """
    M = E - (e*np.sin(E))
    return M

def M_from_t(t,n,M0,t0):
    """
    find mean anomaly at time t

    Parameters
    ----------
    t
        time to find M for
    n
        mean motion
    M0
        reference mean anomaly at time t0
    t0
        reference time t0

    Note that M = 0 at pericentre so if t0 = pericentre passage time then M0 = 0
    """

    M = M0 + (n*(t-t0))
    return M

def E_from_M(M,e,E_limit=1e-6):
    '''
    Solve Kepler's Equation, to find E from M, to within a certain limit.
    This is an iterative method and DIVERGES for e>0.662743
    See Murray and Dermott Solar System Dynamics p.35

    Parameters
    ----------
    M
        mean anomaly, right now only works for a single value of M, not an array
    e
        eccentricity
    E_limit
        Fractional cut off for the change in E between iterations
    '''

    if e>0.662743: # does this catch arrays too?
        # !!! throw a proper warning
        print("WARNING: high e, beware of divergence!")

    E_0=M
    deltaE=2.0*E_limit #set deltaE to some value, greater than the limit
    while deltaE>E_limit: # iterate until the change in E is less than the selected limit
        E_1=M+e*np.sin(E_0)
        deltaE=np.absolute(E_1-E_0)
        E_0=E_1
    return E_0

# !!! Add another function that is more accurate for high eccentrcity orbits?

def f_from_E(E,e):
    '''
    Function to find true anomaly f from eccentric anomaly E

    Parameters
    ----------
    E
        Eccentric anomaly
    e
        eccentricity
    '''

    f_true=2.0*np.arctan(np.sqrt((1.0+e)/(1.0-e))*np.tan(E/2.0))
    return f_true

def f_from_M(M,e):
    """
    Find true anomaly f from mean anomaly M

    Parameters
    ----------
    M
        mean anomaly
    e
        eccentricity
    """

    f_true=2.0*np.arctan(np.sqrt((1.0+e)/(1.0-e))*np.tan(E_from_M(M,e)/2.0))
    return f_true

def f_from_t(t,e,n,M0,t0):
    """
    find true anomaly f from time t

    Parameters
    ----------
    t
        time
    e
        eccentricity
    n
        mean motion
    M0
        reference mean anomaly
    t0
        reference time
    """
    M = M_from_t(t,n,M0,t0)
    f_true = f_from_M(M,e)
    return f_true


#Escriba una función en python que toma como entrada dos listas
#(valores_x, valores_y) y el grado de un polinomio (p_poli, un entero
#                                                   positivo) y ajusta
#por mínimos cuadrados el polinomio de orden p_poli que mejor ajusta
#los datos en valores_x y valores_y. La función debe implementar el
#ajuste en términos de operaciones de álgebra lineal trabajados en el
#"Ejercicio 28 - primera parte".

#La función de debe llamar ajuste_matricial.

#La función debe tomar como entrada, en ese orden, las variables
#valores_x, valores_y y p_poli.

#La función debe hacer el ajuste y hacer una gráfica donde se muestran
#los valores de entrada (valores_x, valores_y) y el polinomio de
#ajuste. La gráfica producida se debe guardar como "ajuste.png"
#(siguiendo el estilo de la Figura 1.4 del texto guía).

#La solución debe estar en un archivo llamado
#"ApellidoNombre_Ejercicio28.py" donde Apellido y Nombre debe
#reemplazarlos con su apellido y nombre.  Suba ese archivo como
#respuesta a esta actividad.

#Al ejecutar "python ApellidoNombre_Ejercicio28.py" no se debe producir
#ningún error. Al llamar la función con el nombre solicitado no se debe
#producir ningún error.
import numpy as np
import matplotlib.pyplot as plt

def ajuste_matricial(valores_x, valores_y, p_poli):
    N = len(valores_x)
    S = np.ones((N,p_poli+1))
    for i in range(N):
        for j in range(p_poli+1):
            S[i,j] = (valores_x[i])**j

    S_inv = np.linalg.pinv(S)
    Y = np.array(valores_y)
    C = S_inv @ Y

    x = np.linspace(np.min(valores_x), np.max(valores_x), 100)
    y = np.zeros(100)
    for i in range(p_poli+1):
        y += C[i]*(x**i)

    plt.figure()
    plt.scatter(valores_x, valores_y)
    plt.plot(x, y)
    plt.savefig("ajuste.png")

#ajuste_matricial([1,2,4,6],[4,8,7,1],3)
#ajuste_matricial([-2.0, -2.0, 0.0, 3.0, 4.0], [5.0, 1.0, -3.0, -2.0, -5.5],1)


import networkx as nx
import itertools
import math
import random

def empty_graph(num_nodes):
    g = nx.Graph()
    g.add_nodes_from(range(num_nodes))
    return g

def complete_graph(num_nodes):
    g = empty_graph(num_nodes)
    edges = itertools.combinations(range(num_nodes), 2)
    g.add_edges_from(edges)
    return g

def random_graph(num_nodes, p):
    g = empty_graph(num_nodes)
    if p <= 0:
        return g
    if p >= 1:
        return complete_graph(num_nodes)
    n = num_nodes
    w = -1
    lp = math.log(1.0 - p)
    # Nodes in graph are from 0,n-1 (start with v as the second node index).
    v = 1
    while v < n:
        lr = math.log(1.0 - random.random())
        w = w + 1 + int(lr / lp)
        while w >= v and v < n:
            w = w - v
            v = v + 1
        if v < n:
            g.add_edge(v, w)
    return g

def e_r(in_graph, config):
    """
    E-R graph generator
    :param in_graph: referenced graph, type: nx.Graph
    :param config: configure object
    :return: generated graphs, type: list of nx.Graph
    """
    num_edges = in_graph.number_of_edges()
    num_nodes = in_graph.number_of_nodes()
    p = num_edges/(num_nodes*(num_nodes-1)/2)
    out_graphs = []
    for i in range(config.num_gen):
        out_graph = random_graph(num_nodes, p)
        out_graphs.append(out_graph)
    return out_graphs


#!/usr/bin/env python3

import numpy as np

def qr(A):
    m, n = A.shape
    Q = np.eye(m)
    for i in range(n - (m == n)):
        H = np.eye(m)
        H[i:, i:] = make_householder(A[i:, i])
        Q = np.dot(Q, H)
        A = np.dot(H, A)
    return Q, A

def make_householder(a):
    v = a / (a[0] + np.copysign(np.linalg.norm(a), a[0]))
    v[0] = 1
    H = np.eye(a.shape[0])
    H -= (2 / np.dot(v, v)) * np.dot(v[:, None], v[None, :])
    return H

# task 1: show qr decomp of wp example
a = np.array(((
    (12, -51,   4),
    ( 6, 167, -68),
    (-4,  24, -41),
)))

q, r = qr(a)
print(('q:\n', q.round(6)))
print(('r:\n', r.round(6)))

# task 2: use qr decomp for polynomial regression example
def polyfit(x, y, n):
    return lsqr(x[:, None]**np.arange(n + 1), y.T)

def lsqr(a, b):
    q, r = qr(a)
    _, n = r.shape
    return np.linalg.solve(r[:n, :], np.dot(q.T, b)[:n])

x = np.array((0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
y = np.array((1, 6, 17, 34, 57, 86, 121, 162, 209, 262, 321))

print(('\npolyfit:\n', polyfit(x, y, 2)))


"""
面试题 10：斐波那契数列
题目：写一个函数，输入 n，求斐波那契（Fibonacci）数列的第 n 项。

"""

def fib_recursion(n: int) -> int:
    if n <= 0:
        return 0
    if n == 1:
        return 1
    return fib(n-1) + fib(n-2)

def fib_loop(n: int) -> int:
    # store the calculated ones
    if n <= 0:
        return 0
    if n == 1:
        return 1
    tmp = [0, 1]
    for i in range(2, n+1):
        res = sum(tmp)
        tmp = [tmp[-1], res]
    return res

def fib(n: int):
    res = [0,1]
    if n < 2:
        return res[n]
    f, s, fn = 0, 1, 0
    for i in range(2, n+1):
        fn = f + s
        f = s
        s = fn
    return fn

def fib_matrix(n: int) -> int:
    """
    [[f(n), f(n-1)], [f(n-1), f(n-2)]] = [[1, 1], [1, 0]] ^(n-1)
    a^n = a^{n/2} a^{n/2} when n is even
    a^n = a^{(n-1)/2} a^{(n-1)/2} a when n is odd
    """
    if n < 2: return [0, 1][n]
    return matrix_power([[1, 1], [1, 0]], n-1)[0][0]

def matrix_multiply(m1: [list], m2: [list]) -> [list]:
    n00 = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0]
    n01 = m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1]
    n10 = m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0]
    n11 = m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1]
    return [[n00, n01], [n10, n11]]

def matrix_power(base: [list], exp: int):
    if exp == 1:
        matrix = base
    elif exp > 1 and exp % 2 == 0:
        matrix = matrix_power(base, exp/2)
        matrix = matrix_multiply(matrix, matrix)
    elif exp > 1 and exp % 2 == 1:
        matrix = matrix_power(base, (exp-1)/2)
        matrix = matrix_multiply(base, matrix_multiply(matrix, matrix))
    else:
        matrix = [[0, 0], [0, 0]]
        print("exp invalid")
    return matrix

import numpy as np
def fib2(n):
    if n < 2:
        return [0, 1][n]
    base = np.array([[1, 1], [1, 0]])
    res = base
    for i in range(2, n):
        res = np.dot(base, res)
    return res[0][0]

import numpy as np
def fib3(n):
    if n < 2:
        return [0, 1][n]
    base = np.array([[1, 1], [1, 0]])
    return matrix_multiply(base, n-1)[0][0]

def matrix_multiply(base, exp):
    if exp == 1:
        res = base
    elif exp > 1 and exp % 2 == 0:
        res = matrix_multiply(base, exp/2)
        res = np.dot(res, res)
    elif exp > 1 and exp % 2 == 1:
        res = matrix_multiply(base, (exp-1)/2)
        res = np.dot(res, res).dot(base)
    else:
        res = base
    return res

if __name__ == '__main__':
    print(fib_loop(40))
    # print(fib(40))
    # print(fib_matrix(40))
    # print(fib_matrix(-2))
    print(fib2(40))
    print(fib3(40))


import numpy as np
from math import log2
from graphviz import Digraph

chiSquare = [[0, 2.706, 4.605, 6.251, 7.779, 9.236, 10.64, 12.02, 13.36],
            [0, 3.841, 5.991, 7.815, 9.488, 11.070, 12.592, 14.067, 15.507],
            [0, 1.323, 2.773, 4.108, 5.385, 6.626, 7.841, 9.037, 10.219]]

def entropy(bucket):
    H, total_elem = 0.0, sum(bucket)
    for i in bucket:
        p = i / total_elem
        if(p > 0): #gives value domain error for <= 0
            H -= (p)*log2(p)
    return H

def info_gain(parent_bucket, left_bucket, right_bucket):
    left, right = sum(left_bucket), sum(right_bucket)
    return entropy(parent_bucket) - (left*entropy(left_bucket) + right*entropy(right_bucket))/(left + right)

def gini(bucket):
    I, total_elem = 1.0, sum(bucket)
    for elem in bucket:
        I -= (elem/total_elem)**2
    return I

def avg_gini_index(left_bucket, right_bucket):
    left, right = sum(left_bucket), sum(right_bucket)
    return (left*gini(left_bucket) + right*gini(right_bucket))/(left + right)

def calculate_split_values(data, labels, num_classes, attr_index, heuristic_name):
    split_vals = []
    sorted_data, sorted_label = (list(t) for t in zip(*sorted(zip(data[:,attr_index], labels))))
    if(heuristic_name == 'info_gain'):
        for i in range(1,len(labels)):
            par_bucket = [sorted_label.count(x) for x in range(num_classes)]
            l_bucket, r_bucket = [sorted_label[:i].count(x) for x in range(num_classes)], [sorted_label[i:].count(x) for x in range(num_classes)]
            S = info_gain(par_bucket,l_bucket,r_bucket)
            ref_point = (sorted_data[i-1] + sorted_data[i]) / 2
            split_vals.append([ref_point, S])

    elif (heuristic_name == 'avg_gini_index'):
        for i in range(1,len(labels)):
            l_bucket, r_bucket = [sorted_label[:i].count(x) for x in range(num_classes)], [sorted_label[i:].count(x) for x in range(num_classes)]
            S = avg_gini_index(l_bucket, r_bucket)
            ref_point = (sorted_data[i-1] + sorted_data[i]) / 2
            split_vals.append([ref_point, S])
    else:
        print("Heuristic Name Error")
    return split_vals

def chi_squared_test(left_bucket, right_bucket):
    chi_val ,parent_bucket = 0.0, [(left_bucket[i] + right_bucket[i]) for i in range(len(left_bucket))]
    par_elem, l_elem, r_elem = sum(parent_bucket), sum(left_bucket), sum(right_bucket)
    #print(par_elem, l_elem, r_elem)
    p_expect = [elem/par_elem for elem in parent_bucket]
    l_expect, r_expect = [l_elem*p_expect[i] for i in range(len(left_bucket))],[r_elem*p_expect[i] for i in range(len(right_bucket))]
    for cur in range(len(parent_bucket)):
        if(l_expect[cur] != 0):
            l_val = (l_expect[cur] - left_bucket[cur])**2 / l_expect[cur]
            chi_val += l_val
        if(r_expect[cur] != 0):
            r_val = (r_expect[cur] - right_bucket[cur])**2 / r_expect[cur]
            chi_val += r_val
    return chi_val, (sum([1 if x > 0 else 0 for x in parent_bucket]) - 1)

class TreeNode:
    def __init__(self, feature = None, threshold = None, l_child = None, r_child = None, *, value = None):
        self.feature = feature
        self.threshold = threshold
        self.l_child = l_child
        self.r_child = r_child
        self.value = value

    def isLeaf(self):
        return (self.value != None)

class ID3:
    def __init__(self,  heuristic,  pre_prune = False, confidence = None,n_feature = None):
        self.parent = None
        self.heuristic = heuristic
        self.n_feature = n_feature
        self.pre_prune = pre_prune
        self.confidence = confidence
        self.title = "Tree Graph with " + self.heuristic + " when Pre-prunning: " + str(self.pre_prune)
    def fit(self, data, label):
        self.q = len(np.unique(label))
        self.n_feature = data.shape[1] if not self.n_feature else min(self.n_feature, data.shape[1])
        self.parent = self.grow_tree(data, label)
        #self.render_tree()

    def predict(self,data):
        return np.array([self.leaf_selector(elem, self.parent) for elem in data])

    def grow_tree(self, data, label, depth = 0):
        sample_size, feature_size = data.shape
        uniq_label_size = len(np.unique(label))
        #print("Depth of tree: ",depth," Number of elements: ",len(label))
        #leaf node
        if(uniq_label_size == 1):
            int_val = self.most_common_class(label)
            print("Depth:",depth, "Leaf(1) labelled with: ",int_val," Number of elements: ",len(label))
            return TreeNode(value=int_val)

        # default split
        best_attr, best_threshold = self.find_best_split(data, label, uniq_label_size, self.heuristic)

        l_idx, r_idx = self.split_holder_data(data[:,best_attr], best_threshold)
        if(not self.pre_prune):
            print("Left Size:",len(l_idx)," Right Size",len(r_idx))
            print("Depth: ",depth," Choosen Feature: ",best_attr," and choosen threshold: ",best_threshold," Number of elements: ",len(label))
            left_child = self.grow_tree(data[l_idx,:], label[l_idx], depth+1)
            right_child = self.grow_tree(data[r_idx,:], label[r_idx], depth+1)
            return TreeNode(best_attr,best_threshold, left_child, right_child)
        else:
            l_buc, r_buc = self.create_bucket(label, l_idx, r_idx)
            chi_val, df = chi_squared_test(l_buc, r_buc)
            if (chi_val < chiSquare[self.confidence][df]):
                int_val = self.most_common_class(label)
                print("CHI - Decisive Prune: Left: ", len(l_idx)," Right: ",len(r_idx))
                print("Depth:",depth,"Leaf labelled with(prune): ",int_val," Number of elements: ",len(label))
                return TreeNode(value=int_val)
            else:
                print("Left Size:",len(l_idx)," Right Size",len(r_idx))
                print("Depth: ",depth," Choosen Feature: ",best_attr," and choosen threshold: ",best_threshold," Number of elements: ",len(label))
                left_child = self.grow_tree(data[l_idx,:], label[l_idx], depth+1)
                right_child = self.grow_tree(data[r_idx,:], label[r_idx], depth+1)
                return TreeNode(best_attr,best_threshold, left_child, right_child)

    def split_holder_data(self, col_data, threshold):
        return list(np.argwhere(col_data <= threshold).flatten()), list(np.argwhere(col_data > threshold).flatten())

    def create_bucket(self, label,l_idx, r_idx):
        left_bucket = [np.sum(label[l_idx] == i) for i in range(self.q)]
        right_bucket = [np.sum(label[r_idx] == i) for i in range(self.q)]
        return left_bucket, right_bucket

    def find_best_split(self, data, label, num_labels, heuristic):
        best_at = -1
        if(heuristic == 'avg_gini_index'):
            best_gain = np.infty
            for i in range(data.shape[1]):
                split_values = calculate_split_values(data, label, self.n_feature, i, heuristic)
                holder = np.min(split_values,axis=0)[1]
                holder_at = np.argmin(split_values, axis=0)[1]
                l,r = self.split_holder_data(data[:,i], split_values[holder_at][0])
                if((holder < best_gain) and (len(l) != 0) and (len(r) != 0)):
                    best_gain = holder
                    best_at = i
                    best_thr = split_values[holder_at][0]

        elif(heuristic == 'info_gain'):
            best_gain = -np.infty
            for i in range(data.shape[1]):
                split_values = calculate_split_values(data, label, self.n_feature, i, heuristic)
                holder = np.max(split_values,axis=0)[1]
                holder_at = np.argmax(split_values, axis=0)[1]
                l,r = self.split_holder_data(data[:,i], split_values[holder_at][0])
                if((holder > best_gain) and (len(l) != 0) and (len(r) != 0)):
                    best_gain = holder
                    best_at = i
                    best_thr = split_values[holder_at][0]
        return best_at, best_thr

    def most_common_class(self,class_label):
        test_list = list(class_label)
        return max(set(test_list), key = test_list.count)

    def leaf_selector(self, data_point, current_node):
        if(current_node.isLeaf()):
            return current_node.value
        if(data_point[current_node.feature] <= current_node.threshold):
            return self.leaf_selector(data_point, current_node.l_child)
        return self.leaf_selector(data_point, current_node.r_child)

    def render_tree(self):
        dot = Digraph(comment= self.title)
        tree = self.traverse(dot,self.parent,1)
        tree.render('tree.pdf')

    def traverse(self,tree_holder,root_node,label_idx):
        tree_holder.node(str(label_idx), str(root_node.feature) + "  " + str(root_node.threshold))
        if (not root_node.l_child.isLeaf()):
            l_child = tree_holder.node(str(2*label_idx), str(root_node.l_child.feature) + " " + str(root_node.l_child.threshold))
            l_hold = self.traverse(l_child,root_node.l_child,2*label_idx)
            tree_holder.edge(str(label_idx),str(2*label_idx))
        if (not root_node.r_child.isLeaf()):
            r_child = tree_holder.node(str(2*label_idx+1), str(root_node.r_child.feature) + " " + str(root_node.r_child.threshold))
            r_hold = self.traverse(r_child, root_node, 2*label_idx+1)
            tree_holder.edge(str(label_idx),str(2*label_idx + 1))
        return tree_holder

if __name__ == '__main__':
    train_data = np.load('hw3_data/iris/train_data.npy')
    train_labels = np.load('hw3_data/iris/train_labels.npy')
    test_data = np.load('hw3_data/iris/test_data.npy')
    test_labels = np.load('hw3_data/iris/test_labels.npy')
    n_test = len(test_labels)

    #default cases
    print("Average gini index with no pre-pruning")
    classifier = ID3('avg_gini_index',False)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for avg_gini ==> "+ str(np.sum(predicter == test_labels)/len(test_labels)))

    print("Information Gain with no pre-pruning")
    classifier = ID3('info_gain', False)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for info_gain ==> "+ str(np.sum(predicter == test_labels)/len(test_labels)))

    # pre-prunned cases 90% confidence
    print("Average gini index with with 90% confidence level chi-square test")
    classifier = ID3('avg_gini_index',True,0)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for avg_gini with 90% confidence pre-prunning ==> "+ str(np.sum(predicter == test_labels)/len(test_labels)))

    print("Information Gain with 90% confidence level chi-square test")
    classifier = ID3('info_gain', True, 0)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for info_gain with 90% confidence pre-prunning ==> "+ str(np.sum(predicter == test_labels)/len(test_labels)))

    # pre-prunned cases 95% confidence
    print("Average gini index with with 95% confidence level chi-square test")
    classifier = ID3('avg_gini_index',True,1)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for avg_gini with 95% confidence pre-prunning ==> "+ str(np.sum(predicter == test_labels)/len(test_labels)))

    print("Information Gain with 95% confidence level chi-square test")
    classifier = ID3('info_gain', True, 1)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for info_gain with 95% confidence pre-prunning ==> " + str(np.sum(predicter == test_labels)/len(test_labels)))

    # pre-prunned cases 75% confidence
    print("Average gini index with with 75% confidence level chi-square test")
    classifier = ID3('avg_gini_index',True,2)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for avg_gini with 75% confidence pre-prunning ==> "+ str(np.sum(predicter == test_labels)/len(test_labels)))

    print("Information Gain with 75% confidence level chi-square test")
    classifier = ID3('info_gain', True, 2)
    classifier.fit(train_data, train_labels)
    predicter = classifier.predict(test_data)
    print("Labelled correctly:  " + str(np.sum(predicter == test_labels)))
    print("Accuracy for info_gain with 75% confidence pre-prunning ==> "+ str(np.sum(predicter == test_labels)/len(test_labels)))