import numpy as np
import random
import pandas as pd
from matplotlib import pyplot as plt

'''
Perceptron algorithm that finds the idea weights given a data set and labels

Parameters
----------
coords: data points in 2 dimensions ([x1, x2])
labels: binary target output for each data point (-1 or 1)
epochs: number of iterations that needs to be run
misclassifiedList

Returns
-------
w: resulting weight vector after learning the data points

'''
def perceptron_algorithm(coords, labels, epochs, misclassifiedList):
	w = np.zeros((coords.shape[1]) + 1)

	for i in range(epochs):
		misclassified = 0
		for x, y in zip(coords, labels):
			dotProd = np.dot(x, w[1:]) + w[0]

			target = 1.0 if (dotProd > 0.0) else -1.0

			if(target != y):
				print(y, target, "classified incorrectly")
				misclassified += 1
				w[1:] += y * x
				w[0] += x[0]
			else:
				print(y, target, "classified correctly")

			print("PRINTING W", w)

		misclassifiedList.append(misclassified)

	return w

if __name__ == "__main__":
	# Set up the random generator seed
	np.random.seed(121232141)

	# Total number of random points
	total = 1000

	# Create 20 random points that are linearly separable
	A = 2 * np.random.random_sample((total//2, 2)) + 0
	B = 2 * np.random.random_sample((total//2, 2)) - 2

	# Create the lists that hold the respective binary labels for the 20 points
	A1 = np.ones((total//2, 1))
	B1 = np.negative(np.ones((total//2, 1)))

	x = np.linspace(-3, 3)
	y = -1 * x

	# Plot the scatter points and the target function, y = -x
	plt.plot(x, y, color="blue", label="target function f")
	plt.scatter(A[:,0], A[:,1], marker="o")
	plt.scatter(B[:,0], B[:,1], marker="x")
	plt.title("PLA Testing")
	plt.xlabel("x1")
	plt.ylabel("x2")

	coords = np.concatenate((A, B))
	labels = np.concatenate((A1, B1))

	# Set up and run perceptron algorithm
	misclassifiedList = []
	epochs = 10
	w = perceptron_algorithm(coords, labels, epochs, misclassifiedList)

	# Create the coordinates to plot the separator line
	x = np.linspace(-3, 3)
	slope = -(w[0]/w[2]) / (w[0]/w[1])
	intercept = -(w[0]/w[2])
	y = (slope * x) + intercept

	# # Plot the classified points along with separator line to show that the dataset is linearly separable
	plt.plot(x, y, color="red", label="final hypothesis g")
	plt.legend(loc="upper right")

	# # Plot the number of iterations needed before termination
	# iterations = np.arange(1, epochs+1)
	# plt.plot(iterations, misclassifiedList, color="red")
	# plt.xlabel("Iterations")
	# plt.ylabel("Misclassified")

	plt.show()


#!/usr/bin/python

import sys
import time
import numpy as np
from scipy import linalg, stats

def rsvd(X, dstar, power_iters=2, delta=10):
	""" Perform rsvd algorithm on input matrix.
		Method must be supplied dstar.
		Returns truncated svd (U,S,V).
	Parameters
	----------
	X : int matrix
    	Matrix of n x m integers, where m <= n. If n < m,
    	matrix will be transposed to enforce m <= n.
   	dstar : int
   		The latent (underlying) matrix rank that will be
   		used to truncate the larger dimension (m).
   	power_iters : int
   		default: 2
   		Number of power iterations used (random matrix multiplications)
   	delta : int
   		default: 10
   		oversampling parameter (to improve numerical stability)
    Returns
	-------
	int matrix
    	Matrix of left singular vectors.
    int matrix
    	Matrix of singular values.
    int matrix
    	Matrix of right singular vectors.
    """
        transpose = False
	if(X.shape[0] < X.shape[1]):
		X = X.T
                transpose = True
	if(power_iters < 1):
		power_iters = 1
	# follows manuscript notation as closely as possible
	P = np.random.randn(X.shape[0],dstar+delta)
	for i in range(power_iters):
		P = np.dot(X.T,P)
		P = np.dot(X,P)
	Q,R = np.linalg.qr(P)
	B = np.dot(Q.T,X)
	U,S,V = linalg.svd(B)
	U = np.dot(Q,U)

        # remove extra dimensionality incurred by delta
        U = U[:,0:dstar]
        S = S[0:dstar]

        if(transpose):
                return V.T,S,U.T
        # else
	return U,S,V

def stabilityMeasure(X, d_max, B=5, power_iters=2):
	""" Calculate stability of
	Parameters
	----------
	X : int matrix
		input matrix to determine rank of
	d_max : int
		upper bound rank to estimate
	B : int
		default: 5
		number of projections to correlate
	power_iters : int
		default: 2
   		Number of power iterations used (random matrix multiplications)
	Returns
	-------
	int
		Latent (lower-dimensional) matrix rank
	"""
	singular_basis = np.zeros((B,X.shape[0],d_max))
	# calculate singular basis under multiple projections
	for i in range(B):
		U = rsvd(X,d_max)[0]
		singular_basis[i,:,:] = U[:,0:d_max]

	# calculate score for each singular vector
	stability_vec = np.zeros((d_max))
	for k in range(d_max):
		stability = 0
		for i in range(0,B-1):
			for j in range(i+1,B):
				corr = stats.spearmanr(singular_basis[i,:,k],singular_basis[j,:,k])[0]
				stability = stability + abs(corr)
		N = B*(B-1)/2
		stability = stability/N
		stability_vec[k] = stability

	# wilcoxon rank-sum test p-values
	p_vals = np.zeros(d_max-2)
	for k in range(2,d_max):
		p_vals[k-2] = stats.ranksums(stability_vec[0:k-1],stability_vec[k-1:d_max])[1]

	dstar = np.argmin(p_vals)

	return dstar


'''
## Python Example

Consider
'''
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': 22, 'figure.autolayout': True, 'figure.figsize': (15,5)})
# set seed for reproductibility
np.random.seed(999)

#%% Multinoulli example

n = 12                      # number of trials (games in one tourment)
pvals = [0.4, 0.35, 0.25]   # probabilities on a single trial

sizes =[] # number of tournments played
p = []    # a list to hold ratios (converge to prob) that player 1 wins 7 times, player 2 wins 2 times and 3 ties

for size in np.logspace(2,4):
    # the line below is where we actually generate discrete random variables according the multinomial distribution
    outcomes = np.random.multinomial(n, pvals, size=int(size))

    # let's count the ratio of the expected outcome over all the outcomes - this will lastly converge to the probability
    prob = sum((outcomes[:,0]==7)&(outcomes[:,1]==2)&(outcomes[:,2]==3))/len(outcomes)

    p.append(prob)
    sizes.append(int(size))

# Plotting
fig1 = plt.figure()
plt.plot(sizes,p,'o-')
plt.plot(sizes,[0.0248]*len(sizes),'--r')
plt.grid()
plt.xlim(xmin=0)
plt.xlabel('Number of Drawings')
plt.ylabel('p(X=K)')
plt.title('Theoretical p(X=K) = 0.0248')

#%% save figures

fig1.savefig('multinomial.png')


def newEq(theEquation, ans, theTerm, theVar):
    """
      Content under Creative Commons Attribution license CC-BY 4.0,
      code under MIT license (c)2018 Sergio Rojas (srojas@usb.ve)

      http://en.wikipedia.org/wiki/MIT_License
      http://creativecommons.org/licenses/by/4.0/

      Created on april, 2018
      Last Modified on: may 15, 2018

      This is a helper function to find step by step the solution
      of any linear equation via operations on both
      sides of the equation. In case of doubt, option 5 can be
      used to check the answer. This also can also be used
      to find solutions to any one variable non-linear equation.
    """
    from sympy import symbols, Eq, solveset, S, sympify
    from sympy import simplify, factor, expand, collect, nsimplify

    LHS, RHS = theEquation.split('=')
    LHS = sympify(LHS)
    RHS = sympify(RHS)
    if ans == 1:
         term = sympify(theTerm)
         RHS = nsimplify(RHS + term)
         LHS = nsimplify(LHS + term)
    elif ans == 2:
         term = sympify(theTerm)
         RHS = nsimplify(RHS - term)
         LHS = nsimplify(LHS - term)
    elif ans == 3:
         term = sympify(theTerm)
         #RHS = nsimplify( simplify(factor(RHS * term)) )
         #LHS = nsimplify( simplify(factor(LHS * term)) )
         RHS = nsimplify( simplify(RHS * term) )
         LHS = nsimplify( simplify(LHS * term) )
    elif ans == 4:
         term = sympify(theTerm)
         if term != 0:
            #RHS = nsimplify( simplify(expand(RHS / term)) )
            #LHS = nsimplify( simplify(expand(LHS / term)) )
            RHS = nsimplify( simplify(RHS / term) )
            LHS = nsimplify( simplify(LHS / term) )
    elif ans == 5:
         theVar = symbols(theVar)
         RHS = nsimplify( simplify(RHS) )
         LHS = nsimplify( simplify(LHS) )
        #RHS = solveset( Eq(LHS, RHS), theVar) ).args[0]
         RHS = list( solveset( Eq(LHS, RHS), theVar) )
         if len(RHS) == 1:
            RHS = RHS[0]
         LHS = theVar
    else:
         RHS = RHS
         LHS = LHS

    theneweq = '{0} = {1}'.format(LHS,RHS)
    return theneweq


"""
# !/usr/bin/env python
# -*- coding: utf-8 -*-
@Time        : 2022/3/26 13:47
@File        : gardent_descent.py
"""

import numpy as np

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

# Derivative of the sigmoid function
def sigmoid_prime(x):
    return sigmoid(x) * (1 - sigmoid(x))

learnrate = 0.5
x = np.array([1, 2])
y = np.array(0.5)

# Initial weights
w = np.array([0.5, -0.5])

# Calculate one gradient descent step for each weight
# TODO: Calculate output of neural network
nn_output = sigmoid(x[0] * w[0] + x[1] * w[1])
# or nn_output = sigmoid(np.dot(x, w))

# TODO: Calculate error of neural network
error = y - nn_output

# TODO: Calculate change in weights
# error term (lowercase delta)
# 第一种方法
# error_term = error * sigmoid_prime(np.dot(x,w))
# del_w = learnrate * error_term * x

# 第二种方法
del_w = learnrate * error * nn_output * (1 - nn_output) * x

print('Neural Network output:')
print(nn_output)

print('Amount of Error:')
print(error)

print('Change in Weights:')
print(del_w)

udacity-program_self_driving_car_engineer_v1.0/part01-computer vision and deep learning/module03-deep learning/lesson01-introduction to neural networks/exercise05-gardient descent/gardent_descent.py


# import necessary libraries
import matplotlib.pyplot as plt
import numpy as np

# import the Distribution class
from .GenericDistribution import Distribution

class Gaussian(Distribution):
    """ Gaussian distribution class for calculating and
    visualizing a Gaussian distribution.

    Attributes:
        mean (float) representing the mean value of the distribution
        stdev (float) representing the standard deviation of the distribution
        data_list (list of floats) a list of floats extracted from the data file

    """

    def __init__(self, mu=0, sigma=1):

        Distribution.__init__(self, mu, sigma)

    def calculate_mean(self):

        """Method to calculate the mean of the data set.

        Args:
            None

        Returns:
            float: mean of the data set

        """

        # Calculate the mean of the data set. Remember that the data set is stored in self.data
        # Change the value of the mean attribute to be the mean of the data set
        # Return the mean of the data set
        self.mean = np.mean(self.data)

        return self.mean

    def calculate_stdev(self, sample=True):

        """Method to calculate the standard deviation of the data set.

        Args:
            sample (bool): whether the data represents a sample or population

        Returns:
            float: standard deviation of the data set

        """

        #   Calculate the standard deviation of the data set
        #
        #   The sample variable determines if the data set contains a sample or a population
        #   If sample = True, this means the data is a sample.
        #   Keep the value of sample in mind for calculating the standard deviation
        #
        #   Make sure to update self.stdev and return the standard deviation as well
        # """

        if sample:
            number_of_data = len(self.data) - 1
        else:
            number_of_data = len(self.data)
        # convert the data to a numpy array
        data_np = np.array(self.data)
        sigma = np.sqrt(np.sum(np.square(data_np - self.mean)) / number_of_data)
        self.stdev = sigma

        return self.stdev

    def plot_histogram(self):
        """Method to output a histogram of the instance variable data using
        matplotlib pyplot library.

        Args:
            None

        Returns:
            None
        """

        # Plot a histogram of the data_list using the matplotlib package.

        plt.hist(self.data)
        plt.xlabel('Data')
        plt.ylabel('Count')
        plt.title('Histogram of the data')

    def pdf(self, x):
        """Probability density function calculator for the gaussian distribution.

        Args:
            x (float): point for calculating the probability density function

        Returns:
            float: probability density function output
        """

        # Calculate the probability density function of the Gaussian distribution
        #       at the value x. You'll need to use self.stdev and self.mean to do the calculation
        pdf = 1 / (self.stdev * np.sqrt(2 * np.pi)) * np.exp(-0.5 * (np.square(x - self.mean)
                                                                     * (1 / np.square(self.stdev))))

        return pdf

    def plot_histogram_pdf(self, n_spaces=50):

        """Method to plot the normalized histogram of the data and a plot of the
        probability density function along the same range

        Args:
            n_spaces (int): number of data points

        Returns:
            list: x values for the pdf plot
            list: y values for the pdf plot

        """

        mu = self.mean
        sigma = self.stdev

        min_range = min(self.data)
        max_range = max(self.data)

        # calculates the interval between x values
        interval = 1.0 * (max_range - min_range) / n_spaces

        x = []
        y = []

        # calculate the x values to visualize
        for i in range(n_spaces):
            tmp = min_range + interval * i
            x.append(tmp)
            y.append(self.pdf(tmp))

        # make the plots
        fig, axes = plt.subplots(2, sharex=True)
        fig.subplots_adjust(hspace=.5)
        axes[0].hist(self.data, density=True)
        axes[0].set_title('Normed Histogram of Data')
        axes[0].set_ylabel('Density')

        axes[1].plot(x, y)
        axes[1].set_title('Normal Distribution for \n Sample Mean and Sample Standard Deviation')
        axes[0].set_ylabel('Density')
        plt.show()

        return x, y

    def __add__(self, other):

        """Magic method to add together two Gaussian distributions

        Args:
            other (Gaussian): Gaussian instance

        Returns:
            Gaussian: Gaussian distribution

        """

        # Calculate the results of summing two Gaussian distributions
        #   When summing two Gaussian distributions, the mean value is the sum
        #       of the means of each Gaussian.
        #
        #   When summing two Gaussian distributions, the standard deviation is the
        #       square root of the sum of square ie sqrt(stdev_one ^ 2 + stdev_two ^ 2)

        # create a new Gaussian object
        result = Gaussian()

        # calculate the mean and standard deviation of the sum of two Gaussians
        # mean of the result distribution
        result.mean = self.mean + other.mean
        # standard deviation of the result distribution
        result.stdev = np.sqrt(np.square(self.stdev) + np.square(other.stdev))

        return result

    def __repr__(self):

        """Magic method to output the characteristics of the Gaussian instance

        Args:
            None

        Returns:
            string: characteristics of the Gaussian

        """

        # TODO: Return a string in the following format -
        # "mean mean_value, standard deviation standard_deviation_value"
        # where mean_value is the mean of the Gaussian distribution
        # and standard_deviation_value is the standard deviation of
        # the Gaussian.
        # For example "mean 3.5, standard deviation 1.3"
        # "mean {}, standard deviation {}".format(self.mean, self.stdev)
        # represent = print("mean {}, standard deviation {}".format(self.mean, self.stdev))

        return "mean {}, standard deviation {}".format(self.mean, self.stdev)


'''
Linear regression with multiple variables. Feature normalisation.

Bence Mélykúti
22/02/2018
'''

import numpy as np
import csv
import itertools
import matplotlib.pyplot as plt
from sklearn import linear_model, preprocessing

csvfile = open('../machine-learning-ex1/ex1/ex1data2.txt', 'r', newline='')
csvdata = csv.reader(csvfile, delimiter=',')
dataiter, dataiter2 = itertools.tee(csvdata) # https://docs.python.org/3/library/itertools.html
length=len(next(dataiter))
data=np.empty([0,length])
for row in dataiter2:
    data=np.vstack((data, [float(entry) for entry in row]))

X = data[:,:2]
X_orig = X
y = data[:,2]
m = len(y)

# https://matplotlib.org/2.0.2/api/pyplot_api.html
# fig = plt.figure()
plt.scatter(X[:,0], y, marker="x", c='r')
# fig.suptitle('Size-price plot', fontsize=20)
plt.ylabel('Price')
plt.show()

# Own implementation of feature normalisation; just use sklearn.preprocessing.StandardScaler instead
def featureNormalize(X):
    mu = np.mean(X, axis=0)
    sigma = np.std(X, axis=0, ddof=0)
    mu_matrix = np.concatenate([mu for i in range(X.shape[0])], axis=0)
    sigma_matrix = np.concatenate([sigma for i in range(X.shape[0])], axis=0)
    X = (X - mu)/sigma
    return X, mu, sigma

Xsc_own, mu_own, sigma_own = featureNormalize(X)

# feature normalisation with sklearn.preprocessing.StandardScaler:
# http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html
scaler = preprocessing.StandardScaler().fit(X)
Xsc = scaler.transform(X) # scaled X
mu = scaler.mean_
sigma2 = scaler.var_ # variance, i.e. sigma^2
sigma = scaler.scale_ # standard deviation, i.e. sigma

# Linear regression, classic fitting

# Linear regression example
# http://scikit-learn.org/stable/auto_examples/linear_model/plot_ols.html

reg = linear_model.LinearRegression()
reg.fit(Xsc,y) # for X already scaled from featureNormalize or StandardScaler
# reg.fit(scaler.transform(X),y)
print('\nTheta found by linear regression:')
print(reg.intercept_, reg.coef_)

data_prediction = reg.predict(Xsc) # for X already scaled from featureNormalize or StandardScaler
#data_prediction = reg.predict(scaler.transform(X))

# These four are equivalent:
#predict1 = reg.predict((((1650,3)-mu_own)/sigma_own).reshape(1,-1)) # using featureNormalize
#predict1 = reg.predict((((1650,3)-mu)/sigma).reshape(1,-1)) # using output of sklearn.preprocessing.StandardScaler
#predict1 = reg.predict((((1650,3)-mu)/sigma2**0.5).reshape(1,-1)) # using output of sklearn.preprocessing.StandardScaler
predict1 = reg.predict(scaler.transform(np.asarray((1650,3)).astype(float).reshape(1,-1))) # recommended way of using sklearn.preprocessing.StandardScaler
print('Predicted price of a 1650 sq-ft, 3 br house: ',predict1[0])

# Linear regression, normal equation

Xe = np.concatenate((np.ones((X.shape[0],1)), Xsc), axis=1)
theta=np.dot(np.linalg.inv(np.dot(np.transpose(Xe),Xe)), np.dot(np.transpose(Xe), y))
print('\nTheta found by normal equation:')
print(theta)

'''
# In comparison to ex1.py, there is no point in plotting it in 2 dim, as there are two features and one value, together 3 dim:
# https://matplotlib.org/2.0.2/api/pyplot_api.html
# fig = plt.figure()
plt.scatter(X[:,1],y, marker="x", c='r')
# fig.suptitle('Population-profit plot', fontsize=20)
plt.plot(X[:,1], data_prediction, color='blue', linewidth=3)
plt.ylabel('Price')
plt.show()
'''


"""E-Divisive Numpy Calculator."""
import numpy as np

from scipy.special import comb

def _calculate_q(cross_term: float, x_term: float, y_term: float, x_len: int, y_len: int) -> float:
    """
    Calculate the q value from the terms and coefficients.

    :param cross_term: The sum of differences between sample distributions X, Y.
    :param x_term: The sum of differences within sample distribution X.
    :param y_term: The sum of differences within sample distribution Y.
    :param x_len: The length of sample distribution X.
    :param y_len: The length of sample distribution Y.

    :return: The q value generated from the terms.
    """
    term1_reg = 0 if x_len < 1 or y_len < 1 else cross_term * (2.0 / (x_len * y_len))
    term2_reg = 0 if x_len < 2 else x_term * (comb(x_len, 2) ** -1)
    term3_reg = 0 if y_len < 2 else y_term * (comb(y_len, 2) ** -1)
    newq = (x_len * y_len / (x_len + y_len)) * (term1_reg - term2_reg - term3_reg)
    return newq

def calculate_diffs(series: np.ndarray) -> np.ndarray:
    """
    Given an array N calculate an NxN difference matrix.

    :param series: The array to calculate the matrix for.
    :return: The difference matrix.
    """
    row, col = np.meshgrid(series, series)
    diffs = abs(row - col)
    return diffs

def calculate_qhat_values(diffs: np.ndarray) -> np.ndarray:
    """
    Find Q-Hat values given a difference matrix.

    :param diffs: The difference matrix.
    :return: The qhat values.
    """
    qhat_values = np.zeros(len(diffs), dtype=np.float)

    # We will partition our signal into:
    # X = {Xi; 0 <= i < tau}
    # Y = {Yj; tau <= j < len(signal) }
    # and look for argmax(tau)Q(tau)

    # sum |Xi - Yj| for i < tau <= j
    cross_term = 0
    # sum |Xi - Xj| for i < j < tau
    x_term = 0
    # sum |Yi - Yj| for tau <= i < j
    y_term = 0

    for row in range(0, len(diffs)):
        y_term += np.sum(diffs[row, row:])

    for tau in range(0, len(diffs)):
        qhat_values[tau] = _calculate_q(cross_term, x_term, y_term, tau, len(diffs) - tau)

        column_delta = np.sum(diffs[:tau, tau])
        row_delta = np.sum(diffs[tau, tau:])

        cross_term = cross_term - column_delta + row_delta
        x_term = x_term + column_delta
        y_term = y_term - row_delta

    return qhat_values


from pprint import pprint
import numpy as np
from utils.lineq import Solver
import warnings
warnings.filterwarnings('ignore')

A = [[2, 1, 2],
     [3, -3, -1],
     [1, -2,  3]]

b = [-1,5,6]

lineq = Solver(A,b)
lineq.find_solution(method='jacobi', x0=[1,0,1])

print('A is diagonally dominant:', lineq.is_diag(lineq.A_orig))
print('transformed A is diagonally dominant:', lineq.is_diag(lineq.A))
print('')
print('Exact solution:                   ', lineq.exact_sol().T) # Not computationally feasible for very large matrices
print('Solution using Gauss elimination: ', lineq.gauss_elim().T) # Not computationally feasible for very large matrices
print('Approximate solution:             ', lineq.sol.T)


import numpy as np

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

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

class NeuronLayer:
    def __init__(self, n_neurons, n_inputs):
        self.synaptic_weights = 2 * np.random.random((n_inputs, n_neurons)) - 1

class ShallowNetwork:
    def __init__(self, data, seed=None):
        if seed != None:
            np.random.seed(seed)
        self.set_training_data(data)
        self.layer = NeuronLayer(1, len(self.training_inputs[0]))

    def get_training_data(self):
        return [[i, o] for i, o in zip(self.training_inputs, self.training_outputs)]

    def set_training_data(self, data):
        self.training_inputs = np.array([e[0] for e in data])
        self.training_outputs = np.array([[e[1] for e in data]]).T

    def train(self, iterations):
        for _ in range(iterations):
            output = self.think(self.training_inputs)
            error = self.training_outputs - output
            adjustment = np.dot(
                self.training_inputs.T, error * sigmoid_derivative(output)
            )
            self.layer.synaptic_weights += adjustment

    def think(self, inputs):
        return sigmoid(np.dot(np.array(inputs), self.layer.synaptic_weights))

class DeepNetwork:
    def __init__(self, data, depth, neurons, seed=None):
        if seed != None:
            np.random.seed(seed)
        self.set_training_data(data)
        self.layer = []

        assert depth > 1

        self.layer.append(NeuronLayer(neurons, len(self.training_inputs[0])))
        for _ in range(depth - 2):
            self.layer.append(NeuronLayer(neurons, neurons))
        self.layer.append(NeuronLayer(1, neurons))

    def get_training_data(self):
        return [[i, o] for i, o in zip(self.training_inputs, self.training_outputs)]

    def set_training_data(self, data):
        self.training_inputs = np.array([e[0] for e in data])
        self.training_outputs = np.array([[e[1] for e in data]]).T

    def train(self, iterations):
        for _ in range(iterations):
            thoughts = self.study(self.training_inputs)

            e = len(self.layer) - 1
            layer_error = {e: self.training_outputs - thoughts[e]}
            layer_delta = {e: layer_error[e] * sigmoid_derivative(thoughts[e])}
            for t in reversed(range(e)):
                layer_error[t] = layer_delta[t + 1].dot(
                    self.layer[t + 1].synaptic_weights.T
                )
                layer_delta[t] = layer_error[t] * sigmoid_derivative(thoughts[t])

            layer_adjustment = {0: self.training_inputs.T.dot(layer_delta[0])}
            for t in range(1, len(thoughts)):
                layer_adjustment[t] = thoughts[t - 1].T.dot(layer_delta[t])

            for t in range(len(self.layer)):
                self.layer[t].synaptic_weights += layer_adjustment[t]

    def study(self, inputs):
        thoughts = [np.array(inputs)]
        for e in self.layer:
            thoughts.append(sigmoid(np.dot(thoughts[-1], e.synaptic_weights)))
        return thoughts[1:]

    def think(self, inputs):
        iterative = np.array(inputs)
        for e in self.layer:
            iterative = self.__sigmoid(np.dot(iterative, e.synaptic_weights))
        return iterative


# Exercise #2: Generalize the code which was used to determine a limiting
# value at infinity.  Apply to Lial Section 11.1 Example 11.

import numpy
from numpy import sin, arange
import matplotlib.pyplot
from matplotlib.pyplot import *

# f = ( 12*x**2 - 15*x + 12 ) / ( x**2 + 1 )

# Lial Section 11.1 Example 11
def f(x):
    f = ( 12*x**2 - 15*x + 12 ) / ( x**2 + 1 )
    return f

# The next section shows convergence to a limit at infinity.
# The coding shows list manipulations resulting in a plot.
# For simplicity, equal intervals between calculated points will be used.

number = 210  # This is the number of points calculated (minus the increment).
increment = 10  # This is the increment between the points.

y = []
x = []

# The for loop traverses between 10 and 200 in increments of 10.
# A range statement is inclusive of the first number and exclusive of the last.

for k in range( increment, number, increment ):
    w = float( k )
    x = x + [ k ]
    y = y + [ f(w) ]

print( 'Final value equals' , y[-1] )  #Floating point with 4 decimals.

figure()
xlim( 0, number + increment )
ylim( min( y ) - 0.1 , max( y ) + 0.1 )

# The black points were computed.  The yellow point indicates the limit.

plot( x,y, color='r' )
scatter( x,y, color='k', s=30 )
scatter( number, y[-1], c='y', s=40 )

title( 'Example of Convergence to a Limit at Infinity' )
xlabel( 'x-axis' )
ylabel( 'y-axis' )
show()


# demo of antithetic sampling
# https://en.wikipedia.org/wiki/Antithetic_variates

import numpy as np
np.random.seed(0)

N = 750 #1500
u1 = np.random.uniform(size=N)
u2 = np.random.uniform(size=N)
u = np.concatenate((u1,u2))
f = 1 / (1+u)
mu_naive = np.mean(f)
se_naive  = np.sqrt(np.var(f)/(2*N))
print('naive {:0.4f}, se {:0.4f}'.format(mu_naive, se_naive))

# antithetic version
uprime = 1-u1
f1 = 1 / (1+u1)
fprime = 1 / (1+uprime)
f = (f1 + fprime) / 2.0 # paired samples!
mu_anti = np.mean(f)
se_anti  = np.sqrt(np.var(f)/(2*N))
print('anti {:0.4f}, se {:0.4f}'.format(mu_anti, se_anti))


"""Defines the most commonly used kernels."""
import math
from scipy import stats
import numpy as np

__author__ = "Miguel Carbajo Berrocal"
__email__ = "miguel.carbajo@estudiante.uam.es"

def normal(u):
    r"""Evaluate a normal kernel.

    .. math::
        K(x) = \frac{1}{\sqrt{2\pi}}e^{-\frac{x^2}{2}}

    """
    return stats.norm.pdf(u)

def cosine(u):
    r"""Cosine kernel.

    .. math::
        K(x) =
        \begin{cases}
        \frac{\pi}{4} cos\left( \frac{\pi x}{2} \right) & \mbox{if } |x| \le
        1 \\
        0 & \mbox{elsewhere}
        \end{cases}

    """
    if isinstance(u, np.ndarray):
        res = np.zeros(u.shape)
        res[abs(u) <= 1] = math.pi / 4 * (math.cos(math.pi * u[abs(u) <= 1]
                                                   / 2))
        return res
    if abs(u) <= 1:
        return math.pi / 4 * (math.cos(math.pi * u / 2))
    return 0

def epanechnikov(u):
    r"""Epanechnikov kernel.

    .. math::
        K(x) =
        \begin{cases}
        0.75(1-x^2) & \mbox{if } |x| \le 1 \\
        0 & \mbox{elsewhere}
        \end{cases}

    """
    if isinstance(u, np.ndarray):
        res = np.zeros(u.shape)
        res[abs(u) <= 1] = 0.75 * (1 - u[abs(u) <= 1] ** 2)
        return res
    if abs(u) <= 1:
        return 0.75 * (1 - u ** 2)
    return 0

def tri_weight(u):
    r"""Tri-weight kernel.

    .. math::
        K(x) =
        \begin{cases}
        \frac{35}{32} \left(1 - u^2 \right) ^3 & \mbox{if } |x| \le 1 \\
        0 & \mbox{elsewhere}
        \end{cases}

    """
    if isinstance(u, np.ndarray):
        res = np.zeros(u.shape)
        res[abs(u) <= 1] = 35 / 32 * (1 - u[abs(u) <= 1] ** 2) ** 3
        return res
    if abs(u) <= 1:
        return 35 / 32 * (1 - u ** 2) ** 3
    return 0

def quartic(u):
    r"""Quartic kernel.

    .. math::
        K(x) =
        \begin{cases}
        \frac{15}{16} \left( 1- u^2 \right) ^2 & \mbox{if } |x| \le 1 \\
        0 & \mbox{elsewhere}
        \end{cases}

    """
    if isinstance(u, np.ndarray):
        res = np.zeros(u.shape)
        res[abs(u) <= 1] = 15 / 16 * (1 - u[abs(u) <= 1] ** 2) ** 2
        return res
    if abs(u) <= 1:
        return 15 / 16 * (1 - u ** 2) ** 2
    return 0

def uniform(u):
    r"""Uniform kernel.

    .. math::
        K(x) =
        \begin{cases}
        0.5 & \mbox{if } |x| \le
        1 \\
        0 & \mbox{elsewhere}
        \end{cases}

    """
    if isinstance(u, np.ndarray):
        res = np.zeros(u.shape)
        res[abs(u) <= 1] = 0.5
        return res
    if abs(u) <= 1:
        return 0.5
    return 0


import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Rectangle
import logging
import math
# import pywt

def read_image(img_name):
    img = plt.imread(img_name).astype(float)
    logging.debug(img.shape)
    return img

def show_img(img, file_name = None):
    plt.imshow(img, cmap = 'gray')
    if not file_name:
        plt.show()
    else:
        plt.savefig('output/' + file_name)

h = np.array([1 + math.sqrt(3), 3 + math.sqrt(3), 3 - math.sqrt(3), \
                1 - math.sqrt(3)]) / (4 * math.sqrt(2))
g = np.array([h[3], -h[2], h[1], -h[0]])

def D4_1D(input_list, compress = False):

    odd_padded = False
    if len(input_list) % 2 == 1:
        odd_padded = True

    # repeat the last pixel at the right boundary
    # note we make a copy of the input list
    # because we do NOT want to change the length of it
    # len(input_padded) == n + 4

    input_padded = [input_list[1], input_list[0]]
    input_padded.extend(input_list)
    input_padded.extend([input_list[-1], input_list[-2]])

    if odd_padded:
        input_padded.append(input_list[-3])

    n = len(input_padded)
    half = n / 2 - 1
    # print(half)

    high_pass = [0] * half
    low_pass = [0] * half

    for i in range(0, half):
        for j in range(4):
            high_pass[i] += h[j] * input_padded[2 * i + j]
            # if we are doing a compression, the low_pass will be left zeros
            if not compress:
                low_pass[i] += g[j] * input_padded[2 * i + j]

    return high_pass, low_pass, odd_padded

def D4_1D_inv(high_pass, low_pass, odd_padded = False):

    h_inv = [h[2], g[2], h[0], g[0]]
    g_inv = [h[3], g[3], h[1], g[1]]

    n = len(high_pass)

    # len(input_padded) == 2 * n

    input_padded = []
    for i in range(n):
        input_padded.append(high_pass[i])
        input_padded.append(low_pass[i])

    # print(input_padded)

    output_list = [0] * (2 * n - 2)

    for i in range(0, n - 1):
        for j in range(4):
            output_list[2 * i] += h_inv[j] * input_padded[2 * i + j]
            output_list[2 * i + 1] += g_inv[j] * input_padded[2 * i + j]

    if odd_padded:
        output_list = output_list[ : -1]

    return output_list

def D4_2D(img, levels = 3):

    new_input = img.copy()
    results = []

    for level in range(levels):

        res_img, row_odd_padded, col_odd_padded = D4_2D_one_level(new_input, level + 1)
        row, col = res_img.shape
        new_input = res_img[0 : row / 2, 0: col / 2]

        results.append([res_img, row_odd_padded, col_odd_padded])

    return results

def D4_2D_one_level(img, level):

    show_img(img, 'input-level-%d' % level)

    # for level in range(levels):

    row_wave_img = []

    for row in img:

        high_pass, low_pass, row_odd_padded = D4_1D(row)
        row_wave_img.append(high_pass + low_pass)

    row_wave_img = np.array(row_wave_img)

    col_row_wave_img = []

    # transpose to iterate over the columns
    for col in row_wave_img.transpose():

        high_pass, low_pass, col_odd_padded = D4_1D(col)
        col_row_wave_img.append(high_pass + low_pass)

    # transpose back to its original arrangement
    col_row_wave_img = np.array(col_row_wave_img).transpose()

    show_img(col_row_wave_img, 'output-level-%d' % level)

    return col_row_wave_img, row_odd_padded, col_odd_padded

def D4_2D_inv_one_level(img, row_odd_padded, col_odd_padded, \
                                 level, decompress = False):

    col_inv_img = []
    for col in img.transpose():
        half = len(col) / 2
        high_pass = col[0 : half]
        low_pass = col[half :]
        col_inv = D4_1D_inv(high_pass, low_pass, col_odd_padded)
        col_inv_img.append(col_inv)

    col_inv_img = np.array(col_inv_img).transpose()

    row_col_inv_img = []

    for row in col_inv_img:
        half = len(row) / 2
        high_pass = row[0 : half]
        low_pass = row[half :]
        row_inv = D4_1D_inv(high_pass, low_pass, row_odd_padded)
        row_col_inv_img.append(row_inv)

    row_col_inv_img = np.array(row_col_inv_img)

    if not decompress:
        file_name = 'invesre-level-%d' % level
    else:
        file_name = 'decompress-level-%d' % level
    show_img(row_col_inv_img, file_name)

    return row_col_inv_img

def D4_2D_inv(wavelet_results, levels = 3):

    curr_level = levels - 1
    next_image = wavelet_results[curr_level][0]
    row_odd_padded = wavelet_results[curr_level][1]
    col_odd_padded = wavelet_results[curr_level][2]

    while curr_level >= 0:

        level_inv = D4_2D_inv_one_level(next_image, row_odd_padded, \
                            col_odd_padded, level = curr_level + 1)

        if curr_level == 0:
            break

        rows, cols = level_inv.shape

        curr_level -= 1
        next_image = wavelet_results[curr_level][0]
        row_odd_padded = wavelet_results[curr_level][1]
        col_odd_padded = wavelet_results[curr_level][2]

        next_image[ : rows, : cols] = level_inv

def compress_image(img, levels = 2):

    results = D4_2D(img, levels = levels)
    res_img, _, _ = results[-1]
    rows, cols = res_img.shape

    img_wavelet = res_img[ : rows / 2, : cols / 2]
    return img_wavelet

def decompress_image(img_wavelet, levels = 2):

    next_image = img_wavelet.copy()
    while levels > 0:

        rows, cols = next_image.shape
        img_wavelet_expanded = np.zeros([2 * rows, 2 * cols])
        img_wavelet_expanded[: rows, : cols] = next_image

        next_image = D4_2D_inv_one_level(img_wavelet_expanded, \
                                False, False, levels, decompress = True)

        levels -= 1

    return next_image

def test_pywt(l = None, compress = False):

    print('\n\n')
    if l == None:
        l = [3, 7, 1, 1, -2, 5, 4, 6]

    print(l)
    cA, cD = pywt.dwt(l, 'db2')
    print(cA)
    print(cD)

    if compress:
        cD = [0] * len(cD)

    l_inv = pywt.idwt(cA, cD, 'db2')
    print(l_inv)

    w = pywt.Wavelet('db2')
    print(w.dec_hi)
    print(w.dec_lo)

    print(w.rec_hi)
    print(w.rec_lo)

def test_1D(l = None, compress = False):
    if l == None:
        l = [3, 7, 1, 1, -2, 5, 4, 6]

    print(l)

    high_pass, low_pass, odd_padded = D4_1D(l, compress)
    print(high_pass)
    print(low_pass)

    l_inv = D4_1D_inv(high_pass, low_pass, odd_padded)
    print(l_inv)

def main():

    img = read_image('input.png')

    l = [3, 7, 1, 1, -2, 5, 4, 6]

    # test_1D(l)

    # test_pywt(l)

    # only handle the first channel
    img = img[ : , : , 0]
    print(img.shape)

    d4_3_level = D4_2D(img, levels = 3)
    D4_2D_inv(d4_3_level, levels = 3)

    img_wavelet = compress_image(img, levels = 2)
    # print(img_wavelet.shape)
    decompressed_img = decompress_image(img_wavelet, levels = 2)

if __name__ == '__main__':
    main()


r"""
Solve Helmholtz equation in 1D with Dirichlet bcs

.. math::

    \alpha u - \nabla^2 u = f, \quad u(\pm 1) = 0

The equation to solve is

.. math::

    \alpha (u, v)_w - (\nabla^2 u, v)_w = (f, v)_w

The weighted inner product over the domain :math:`\Omega` is defined as

.. math::

    (u, v)_w = \int_{\Omega} u v w dx

where :math:`w(x)` is a weight function.

For either Chebyshev, Legendre or Jacobi we choose a basis that satsifies
the boundary conditions.

"""
import sys
import importlib
from sympy import symbols, sin
import numpy as np
from shenfun import inner, div, grad, TestFunction, TrialFunction, \
    Array, Function, FunctionSpace, dx

assert len(sys.argv) == 3
assert sys.argv[-1].lower() in ('legendre', 'chebyshev', 'jacobi')
assert isinstance(int(sys.argv[-2]), int)

# Collect basis and solver from either Chebyshev or Legendre submodules
family = sys.argv[-1].lower()
base = importlib.import_module('.'.join(('shenfun', family)))
Solver = base.la.Helmholtz

# Use sympy to compute a rhs, given an analytical solution
alfa = 2.
x = symbols("x", real=True)
ue = sin(4*np.pi*x)*(1-x**2)
fe = alfa*ue - ue.diff(x, 2)

# Size of discretization
N = int(sys.argv[-2])

SD = FunctionSpace(N, family=family, bc=(0, 0))
u = TrialFunction(SD)
v = TestFunction(SD)

# Get f on quad points
fj = Array(SD, buffer=fe)

# Compute right hand side of Poisson equation
f_hat = Array(SD)
f_hat = inner(v, fj, output_array=f_hat)

# Get left hand side of Poisson equation
A = inner(v, -div(grad(u)))
B = inner(v, alfa*u)

H = Solver(A, B)
u_hat = Function(SD)           # Solution spectral space
u_hat = H(u_hat, f_hat)
uj = SD.backward(u_hat)

# Compare with analytical solution
ua = Array(SD, buffer=ue)

error = dx((uj-ua)**2)
print('Error=%2.6e'%(np.sqrt(error)))
assert np.allclose(uj, ua)


import numpy as np

class LinearRegression():
    def __init__(self):
        self.w = None

    def __featureNormaliza(self, X):
        """

        Normalized feature dataset.

        Parameters
        ----------
        X : {array-like, sparse matrix} of shape(n_samples, n_features)
            Training data

        Returns
        -------
        X_norm : norm data

        mu : mean of every row

        sigma : standard deviation of every row
        """

        X_norm = np.array(X)

        mu = np.mean(X_norm, 0) # average
        sigma = np.std(X_norm, 0) # standard deviation

        for i in range(X.shape[1]):
            X_norm[:, i] = (X_norm[:,i] - mu[i])/sigma[i]

        return X_norm

    def __gradientDescent(self, X, y, alpha=0.001, max_iteration_count=10000):
        """

        batch gradient descent

        Parameters
        ----------
        X : {array-like, sparse matrix} of shape(n_samples, n_features)
            Training data

        y : array-like of shape(n_samples,) or (n_samples, n_targets)
            Target values.

        alpha : learning rate

        Returns
        -------
        w : the final value list formed after gradient descent

        J_history : w change log
        """
        y = np.array(y)
        m,n = X.shape
        w = np.ones((n))

        iteration_count = 0

        J_history = []
        w_change_log = []

        while iteration_count < max_iteration_count:

            X_w_ip = np.dot(X, w) # w^T * X

            temp_d = X_w_ip.flatten() - y # h_theta(X) - y
            temp_dot = np.dot(np.transpose(X), temp_d) # (h_theta(X) - y)*X
            temp = w - ((alpha/m) * temp_dot)
            w = temp

            J = np.dot((np.dot(X,w) - y) , (np.transpose(np.dot(X,w) - y)))/(2 * m) # cost J
            J_history.append(J)
            iteration_count += 1

        self.w = w
        return w, J_history

    def fit(self, X, y):
        """

        Fit linear model.

        Parameters
        ----------
        X : {array-like, sparse matrix} of shape(n_samples, n_features)
            Training data

        y : array-like of shape(n_samples,) or (n_samples, n_targets)
            Target values.

        Returns
        -------
        self : returns an instance of self.
        """
        X = self.__featureNormaliza(X)
        X = np.insert(X, 0, 1, axis=1)

        # X_inv = np.linalg.inv(X.T.dot(X)) # X^T.dot(X)
        # self.w = X_inv.dot(X.T).dot(y)

        w, J_history = self.__gradientDescent(X, y)
        # print(J_history)

    def predict(self, X):
        """

        Predict using the linear model.

        Parameters
        ----------
        X : array-like or sparse matrix, shape(n_samples, n_features)
            Samples.

        Returns
        -------
        C : array, shape [n_samples]
            Predicted class label per sample.
        """
        # h(theta) = theta.T.dot(X)
        X = self.__featureNormaliza(X)
        X = np.insert(X, 0, 1, axis=1)
        C = X.dot(self.w)
        return C


import math
import numpy as np

b0 = 1.129552
b1 = 1.160436
b2 = 0.2642821
b3 = 0.01131791
b4 = 0.5900113
b5 = 0.04589742
b6 = 1.00
b7 = 0.5002034
b8 = 1.500
b9 = 1.979139

c0 = 4.39890
c1 = 0.43693
c2 = -4.16078
c3 = 0.090

# aquifer constant function
def b_function(poro,h,cw,cf,ro,f):
    b = (1.119*poro*h*(cw+cf)*ro*ro*f)
    return b

# fraction of perimeter function
def f_function(thitha):
    f = (thitha /360)
    return f

# dimensionless radius function
def rd_function(re,ro):
    rd = re/ro
    return rd

# dimensionless time function in years
def td_function(array,k,poro,miu,cw,cf,ro):
    td_result_array = []
    for t in array:
        td = (2.309*k*t)/(poro*miu*(cw+cf)*ro*ro)
        td_result_array.append(td)
    return td_result_array

# Here the Polynomial approach by Klins et al., 1988 was used
# to iterate for qd values at respective dimensionless radius
def qd_function(rd,td_array):
    try:
        qd_array = []
        for td in td_array:
            if td <= 0.01:
                qd = (2/math.sqrt(math.pi))*(math.sqrt(td))
            if (td > 0.01)  and (td < 200):
                qd = ((b0*(td**b7))+(b1*(td))+(b2*(td**b8))+(b3*(td**b9)))/((b4*(td**b7))+(b5*td)+(b6))
            if (td > 200)  and (td < 2000000000000):
                qd = 10**(c0 + (c1*(math.log(td)))+(c2*(math.log(td))**c3))
            qd_array.append(qd)  # add each value of qd to the qd_array
            reversed_qd=list(reversed(qd_array))  #reversed qd_array
        return reversed_qd, qd_array

    except:
        return "sorry! something went wrong"

# Water influx function
def water_influx_function(b,dp_array, reversed_qd_array):
    try:
        z = np.multiply(dp_array, reversed_qd_array)
        z_sum = np.sum(z)
        we = (b*z_sum)/1000000

    except Exception:
        we  = "sorry! something went wrong"
    return we

#Wd summation function
#  NB: this function is faulty, I am yet to figure out the algorithm for yearly  Wd summation
# majorly due to time constraint and also i would be feasible for me to get round it in the limited time
# Hence i brought in the actual values from the text book to get the real plot. Thanks
def wd_function(b,dp_array,qd_array):
    new_array = []
    correct_array = [0, 3.829, 13.46, 26.462, 41.935, 59.207, 77.628, 96.805, 116.284, 135.601, 154.4] #actual wd values from textbook
    for qd in qd_array:
        z = np.multiply(dp_array, qd)
        z_sum = np.sum(z)
        we = (b*z_sum)/1000000
        new_array.append(we)
    # return new_array    #return the wd values obtained from the function itself.
    return correct_array  #return the textbook values


import numpy as np
from cvxopt import solvers, matrix
import matplotlib.pyplot as plt

# Given data
P = 2*matrix([[0.2, -0.2, -0.12, 0.02], [-0.2, 1.4, 0.02, 0.0], [-0.12, 0.02, 1.0, -0.4], [0.02, 0.0, -0.4, 0.2]])
q = matrix([0.0, 0.0, 0.0, 0.0])
G = matrix([[-1.1, -1.0, 0.0, 0.0, 0.0], [-1.35, 0.0, -1.0, 0.0, 0.0], [-1.25, 0.0, 0.0, -1.0, 0.0], [-1.05, 0.0, 0.0, 0.0, -1.0]])
A = matrix([1.0, 1.0, 1.0, 1.0], (1, 4))
b = matrix(1.0)

# Vary the minimum expected return between 1.05 and 1.35
r_min_range = np.arange(1.05, 1.35, 0.01)

# For each minimum expected return find the QP solution and variance
variance_range = []
IBM_allocation = []
google_allocation = []
apple_allocation = []
intel_allocation = []

for r_min in r_min_range:
    h = matrix([-1 * r_min, 0.0, 0.0, 0.0, 0.0])
    sol = solvers.qp(P, q, G, h, A, b)
    IBM_allocation.append(sol['x'][0])
    google_allocation.append(sol['x'][1])
    apple_allocation.append(sol['x'][2])
    intel_allocation.append(sol['x'][3])
    variance_range.append((sol['x'].T * matrix([[0.2, -0.2, -0.12, 0.02], [-0.2, 1.4, 0.02, 0.0], [-0.12, 0.02, 1.0, -0.4], [0.02, 0.0, -0.4, 0.2]]) * sol['x'])[0])

# Plot variance for each minimum expected return
fig1 = plt.figure()
plt.plot(r_min_range, variance_range)
fig1.suptitle('Expected Return to Variance')
plt.xlabel('Expected Return (r_min)')
plt.ylabel('Variance')
plt.grid()

# Plot portfolio composition
fig2 = plt.figure()
plt.plot(r_min_range, IBM_allocation, label="IBM")
plt.plot(r_min_range, google_allocation, label="Google")
plt.plot(r_min_range, apple_allocation, label="Apple")
plt.plot(r_min_range, intel_allocation, label="Intel")
fig2.suptitle('Portfolio Composition over Varying Risk/Returns')
plt.xlabel('Expected Return (r_min)')
plt.ylabel('Allocation (out of total of 1)')
plt.legend()
plt.grid()

plt.show()


from math import asin, cos, sqrt

import numpy as np

def distance(lat1, lon1, lat2, lon2):
    p = 0.017453292519943295
    a = 0.5 - cos((lat2 - lat1) * p) / 2 + cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2
    return 12742 * asin(sqrt(a)) * 1000

def distances_vectorized(lats, lons, lat1, lon1):
    p = 0.017453292519943295
    lats_arr = np.array(lats)
    lons_arr = np.array(lons)
    a = 0.5 - np.cos((lats_arr - lat1) * p) / 2 + np.cos(lat1 * p) * \
        np.cos(lats_arr * p) * (1 - np.cos((lons_arr - lon1) * p)) / 2
    return list(12742 * np.arcsin(np.sqrt(a)) * 1000)

def coords_with_data(bdy_lon_vec, bdy_lat_vec, bdy_data):
    coords = []

    if bdy_data.dimensions[-1] == 'time':
        i_range = range(bdy_data.shape[0])
        j_range = range(bdy_data.shape[1])
    else:
        i_range = range(bdy_data.shape[1])
        j_range = range(bdy_data.shape[2])

    for i in i_range:
        for j in j_range:
            if bdy_data.dimensions[-1] == 'time':
                data_cell = bdy_data[i, j, 0]
            else:
                data_cell = bdy_data[0, i, j]
            if data_cell is not np.ma.masked:
                if len(bdy_lat_vec.shape) > 1:
                    coords.append({'lat': bdy_lat_vec[i][j],
                                   'lon': bdy_lon_vec[i][j],
                                   'i': i,
                                   'j': j})
                else:
                    coords.append({'lat': bdy_lat_vec[i],
                                   'lon': bdy_lon_vec[j],
                                   'i': i,
                                   'j': j})
    return coords

def get_nearest_index(coords, pt_lon, pt_lat):
    # if len(pt_lon.shape)==1:
    #    res = min(coords, key=lambda p: distance(pt_lat, pt_lon, p['lat'], p['lon']))
    # else:
    dists = distances_vectorized([p['lat'] for p in coords],
                                 [p['lon'] for p in coords],
                                 pt_lat, pt_lon)

    try:
        res_ind = dists.index(min(dists))
    except:
        md = [min(_) for _ in dists]
        res_ind = md.index(min(md))
    min_ind_i, min_ind_j = coords[res_ind]['i'], coords[res_ind]['j']

    return min_ind_i, min_ind_j


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 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'))


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