# Matt @ WaveWalkerDSP.com
# https://wavewalkerdsp.com
# Twitter: @WaveWalkerDSP
#
#
# Copyright 2021, Matt @ WaveWalkerDSP.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
#
#
#
# A square-root raised cosine (SRRC) filter design script for Python. The
# math derivation is from Gaussian Waves:
#
# 	https://www.gaussianwaves.com/2018/10/square-root-raised-cosine-pulse-shaping/
#
#	The design equqations have been changed slightly such that the filter has
#	a gain of 0 dB at f=0.
#
# The SRRC design function requires NumPy to be installed:
#
#	$ sudo apt-get install python3-numpy
#
# To call the function in your Python code, first copy it to the location
# of your script. Then import the function and call it:
#
#	from srrcDesign import srrcDesign
#	span = 4
#	SPS = 2
#	beta = 0.2
#	srrcWeights = srrcDesign(SPS,span,beta)
#
# Parameters:
#
#	SPS: samples per symbol. this can also be considered the oversampling factor.
#		SPS must be an integer greater than 1.
#
#	span: the number of symbols in the filter to the left and right of the center tap.
#		the SRRC filter will have 2*span + 1 symbols in the impulse response.
#		span must be an integer greater than or equal to 1.
#
#	beta: the roll-off factor or excess bandwidth factor. a larger beta results in
#		more occupied bandwidth. beta must be greater than or equal to 0.
#
#

import numpy as np

def srrcDesign ( SPS, span, beta ):

	invalidParameter = False

	if (SPS <= 1):
		print('srrcDesign.py: SPS must be greater than 1')
		invalidParameter = True

	if ((int(SPS) - SPS) > 1e-10):
		print('srrcDesign.py: SPS must be an integer')
		invalidParameter = True

	if (span < 1):
		print('srrcDesign.py: span must be greater than or equal to 1')
		invalidParameter = True

	if ((int(span) - span) > 1e-10):
		print('srrcDesign.py: span must be an integer')
		invalidParameter = True

	if (beta < 0):
		print('srrcDesign.py: beta must be greater than or equal to 0')
		invalidParameter = True

	# check to see if a bad parameter was passed in
	if (invalidParameter):
		# return empty filter weights
		return []

	else: # parameters valid, proceed with design
		# build the time indexing
		nList = np.arange(-span*SPS,(span*SPS)+1)

		# pre-allocate memory for weights
		weights = np.zeros(len(nList))

		# compute the weights on a sample by sample basis
		for index in range(len(nList)):

			# select the time index
			n = nList[index]

			# design equations
			if (n == 0):
				weights[index] = (1/np.sqrt(SPS))*((1-beta) + (4*beta/np.pi))
			elif (np.abs(n*4*beta) == SPS):
				weights[index] = (beta/np.sqrt(2*SPS))*( (1+(2/np.pi))*np.sin(np.pi/(4*beta)) + (1-(2/np.pi))*np.cos(np.pi/(4*beta)) )
			else:
				weights[index] = (1/np.sqrt(SPS))*( (np.sin(np.pi*n*(1-beta)/SPS)) + (4*beta*n/SPS)*(np.cos(np.pi*n*(1+beta)/SPS)) ) / ( (np.pi*n/SPS) * (1 - (4*beta*n/SPS)**2) )

		# scale the weights to 0 dB gain at f=0
		weights = weights/np.sqrt(SPS)

		return weights


# Chapter 5 Compressing Data via Dimensionality Reduction
# Linear Discriminant Analysis(LDA)

# Supervised data compression via linear discriminant analysis
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data', header=None)

from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = \
train_test_split(X, y, test_size=0.3, random_state=0)
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
X_test_std = sc.transform(X_test)

# Computing the scatter matrices

np.set_printoptions(precision=4)
mean_vecs = []
for label in range(1,4):
    mean_vecs.append(np.mean(X_train_std[y_train==label], axis=0))
    print 'MV %s: %s\n' % (label, mean_vecs[label-1])

d = 13 # number of features
S_W = np.zeros((d, d))
for label, mv in zip(range(1,4), mean_vecs):
    class_scatter = np.zeros((d, d))
    for row in X_train[y_train == label]:
        row, mv = row.reshape(d, 1), mv.reshape(d, 1)
        class_scatter += (row-mv).dot((row-mv).T) # 13x1, 13x1 = 13x13
    S_W += class_scatter
print 'Within-class scatter matrix: %sx%s' % (S_W.shape[0], S_W.shape[1])
print 'Class label distribution: %s' % (np.bincount(y_train)[1:])

# Using covariance
d = 13 # number of features
S_W = np.zeros((d, d))
for label, mv in zip(range(1,4), mean_vecs):
    class_scatter = np.cov(X_train_std[y_train==label].T)
    S_W += class_scatter
print 'Scaled within-class scatter matrix: %sx%s' % (S_W.shape[0], S_W.shape[1])

mean_overall = np.mean(X_train_std, axis=0)
d = 13 # number of features
S_B = np.zeros((d, d))
for i, mean_vec in enumerate(mean_vecs):
    n = X_train[y_train==i+1, :].shape[0]
    mean_vec = mean_vec.reshape(d, 1)
    mean_overall = mean_overall.reshape(d, 1)
S_B += n * (mean_vec - mean_overall).dot((mean_vec - mean_overall).T)
print 'Between-class scatter matrix: %sx%s' % (S_B.shape[0], S_B.shape[1])

# Selecting linear discriminants for the new feature subspace
'''
The remaining steps of the LDA are similar to the steps of the PCA. However,
instead of performing the eigendecomposition on the covariance matrix, we solve
the generalized eigenvalue problem of the matrix:
'''
eigen_vals, eigen_vecs = np.linalg.eig(np.linalg.inv(S_W).dot(S_B))

'''
After we computed the eigenpairs, we can now sort the eigenvalues in
descending order:
'''
eigen_pairs = [(np.abs(eigen_vals[i]), eigen_vecs[:, i]) for i in range(len(eigen_vals))]
eigen_pairs = sorted(eigen_pairs, key=lambda k: k[0], reverse=True)
print 'Eigenvalues in decreasing order: \n'
for eigen_val in eigen_pairs:
    print eigen_val[0]

tot = sum(eigen_vals.real)
discr = [(i / tot) for i in sorted(eigen_vals.real, reverse=True)]
cum_discr = np.cumsum(discr)
plt.bar(range(1,14), discr, alpha=0.5, align='center', label='individual "discriminability"')
plt.step(range(1,14), cum_discr, where='mid', label='cumulative "discriminability"')
plt.ylabel('"discriminability" ratio')
plt.xlabel('Linear Discriminants')
plt.ylim([-0.1, 1.1])
plt.legend(loc='best')
plt.show()

'''
Let's now stack the two most discriminative eigenvector columns to create the
transformation matrix W:
'''
w = np.hstack((eigen_pairs[0][1][:, np.newaxis].real, eigen_pairs[1][1][:, np.newaxis].real))
print 'Matrix W: \n', w

# Projecting samples onto the new feature space
'''
Using the transformation matrix W that we created in the previous subsection,
we can now transform the training data set by multiplying the matrices:
'''
X_train_lda = X_train_std.dot(w)
colors = ['r', 'b', 'g']
markers = ['s', 'x', 'o']
for l, c, m in zip(np.unique(y_train), colors, markers):
    plt.scatter(X_train_lda[y_train==l, 0] * (-1), X_train_lda[y_train==l, 1] * (-1), c=c, label=1, marker=m)

plt.xlabel('LD 1')
plt.ylabel('LD 2')
plt.legend(loc='lower right')
plt.show()

# LDA via scikit-learn
'''
The step-by-step implementation was a good exercise for understanding the inner
workings of LDA and understanding the differences between LDA and PCA.
Now, let's take a look at the LDA class implemented in scikit-learn:
'''
from sklearn.lda import LDA
from sklearn.linear_model import LogisticRegression
from Plot_Decision_Regions import plot_decision_regions
lda = LDA(n_components=2)
X_train_lda = lda.fit_transform(X_train_std, y_train)

'''
Next, let's see how the logistic regression classifier handles the lower-dimensional
training dataset after the LDA transformation:
'''
lr = LogisticRegression()
lr = lr.fit(X_train_lda, y_train)
plot_decision_regions(X_train_lda, y_train, classifier=lr)
plt.xlabel('LD 1')
plt.ylabel('LD 2')
plt.legend(loc='lower left')
plt.show()
'''
Looking at the resulting plot, we see that the logistic regression model misclassifies
one of the samples from class 2:
By lowering the regularization strength, we could probably shift the decision
boundaries so that the logistic regression models classify all samples in the training
dataset correctly. However, let's take a look at the results on the test set:
'''
X_test_lda = lda.transform(X_test_std)
plot_decision_regions(X_test_lda, y_test, classifier=lr)
plt.xlabel('LD 1')
plt.ylabel('LD 2')
plt.legend(loc='lower left')
plt.show()

'''
As we can see in the resulting plot, the logistic regression classifier is able to get a
perfect accuracy score for classifying the samples in the test dataset by only using a
two-dimensional feature subspace instead of the original 13 Wine features:
'''

self_practice/Chapter 5 Linear Discriminant Analysis.py


import numpy as np
import math

'''
distance r valued on Gaussian basis
  exp(−(r − r0)**2 /σ**2)
  where r0 takes values at 20 locations linearly
  placed between 0 and 4, and the
  width σ = 0.5
'''

def gaussian_expansion(d, n_centers=20, sigma=0.5, min_d=0.0, max_d=4.0, centers=[None]):
  if None in centers:
    centers = np.linspace(min_d, max_d, n_centers)
    #print('{} centers between {} and {} :\n  {}'.format(n_centers,min_d,max_d,centers))
  return [ math.exp(-(d - x)**2 / sigma**2) for x in centers ], centers

if __name__=='__main__':
  centers=[None]

  distances = [0.5, 0.7, 1.4, 4.0]

  for dist in distances:
    exp_dist, centers = gaussian_expansion(dist, centers=centers)
    print('\nGauExp dist for {}A :\n  {}\n'.format(dist,[round(x,5) for x in exp_dist]))


from .activation_function import ActivationFunction
import numpy as np

class Sigmoid(ActivationFunction):
    """
    Sigmoid activation function.
    f(x) = 1/(1+e^(-x))
    derivative = f*(1-f)
    """

    def forward(self, net):
        return 1/(1 + np.exp(-net))

    def derivative(self, output):
        return output * (1 - output)


from scipy import stats
import numpy as np
import matplotlib.pylab as plt
import chisquare

#f = file('AllServiceTimes.txt', 'r+')
f = file('AllInterarrivalTimes.txt', 'r+')
data = [float(x) for x in f.read().split(', ')]#extract.extractData('data.txt')[0]

# plot normed histogram
plt.hist(data, normed=True)

# find minimum and maximum of xticks, so we know
# where we should compute theoretical distribution
xt = plt.xticks()[0]
xmin, xmax = min(xt), max(xt)
lnspc = np.linspace(xmin, xmax, len(data))

# Try the exponential disctubution
aexpon, muExp = stats.expon.fit(data)
pdf_exp = stats.expon.pdf(lnspc, aexpon, muExp)
plt.plot(lnspc, pdf_exp, label="Exponential")

# Try the erlang distrubution
ae,be,muErl = stats.erlang.fit(data)
pdf_erl = stats.erlang.pdf(lnspc, ae, be, muErl)
plt.plot(lnspc, pdf_erl, label="Erlang")

# Try the gamma distrubution
ag,bg,thetaGamma = stats.gamma.fit(data)
pdf_gamma = stats.gamma.pdf(lnspc, ag, bg,thetaGamma)
plt.plot(lnspc, pdf_gamma, label="Gamma")

plt.legend(loc='upper right')
plt.savefig("fitting.png")

print "[*] Fitting Paramaters"
print "[#] Exponential: ", aexpon, '???, mu = ', muExp, '\n',
print "[#] Erlang: ", ae, '???, k = ', be, ', mu = ', muErl
print "[#] Gamma: ", ag, '??? k = ', bg, ', Theta = ', thetaGamma, '\n'

print "[*] Computing Chi Square Test"
# Apply Chi-Square test to the three fittings
dof = 2
n = 30

rv = stats.expon(aexpon, muExp)
exponFit = chisquare.model(data, n, dof, rv)
print "[#] Exponential: The chi_sq test value is %10.6f and the p-value is %10.6f" % (exponFit[0], exponFit[1])

rv = stats.erlang(ae, be, muErl)
erlangFit = chisquare.model(data, n, dof, rv)
print "[#] Erlang: The chi_sq test value is %10.6f and the p-value is %10.6f" % (erlangFit[0], erlangFit[1])

rv = stats.gamma(ag, bg, thetaGamma)
gammaFit = chisquare.model(data, n, dof, rv)
print "[#] Gamma: The chi_sq test value is %10.6f and the p-value is %10.6f" % (gammaFit[0], gammaFit[1])


"""
This module illustrates the computation of eigenvalues and eigenvectors using
the linalg class.
"""
import numpy as np
from numpy import linalg

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

v, B = linalg.eig(A)

i = 0 # First eigenvalue/eigenvector pair
lambda0 = v[i]
print(lambda0) # 6.823156164525971

x0 = B[:, i]
print(x0)
# array ([ 0.73271846, -0.20260301, 0.649672352])

linalg.norm(x0) # 1.0 - eigenvalues are normalized

lhs = A @ x0
rhs = lambda0 * x0
linalg.norm(lhs - rhs) # 2.8445583831733384e-15 - very small.


"""
Plot the dividend process and the state process for the Markov asset pricing
lecture.

"""
import numpy as np
import matplotlib.pyplot as plt
import quantecon as qe

mc = qe.tauchen(0.96, 0.25, n=25)
sim_length = 80

x_series = mc.simulate(sim_length, init=np.median(mc.state_values))
lambda_series = np.exp(x_series)
d_series = np.cumprod(lambda_series) # assumes d_0 = 1

fig, axes = plt.subplots(2, 2)
axes[0, 0].plot(x_series, 'b-', lw=2, label=r'$X_t$')
axes[0, 1].plot(lambda_series, 'b-', lw=2, label=r'$g_t$')
axes[1, 0].plot(d_series, 'b-', lw=2, label=r'$d_t$')
axes[1, 1].plot(np.log(d_series), 'b-', lw=2, label=r'$\log \, d_t$')
for ax in axes.flatten():
    ax.legend(loc='upper left', frameon=False)
plt.tight_layout()
plt.show()


import numpy as np

class LinearRegression:

    def __init__(self):
        self.coeff = None
        self.train_MSE = None

    def fit(self, X_train, y_train):
        intercept = np.ones((X_train.shape[0], 1))
        X = np.append(intercept, X_train, axis=1)

        beta = np.linalg.pinv(np.dot(X.T, X)).dot(np.dot(X.T, y_train))

        #beta = np.linalg.inv(np.dot(X.T, X)).dot(np.dot(X.T, y_train))
        #beta = scipy.linalg.solve(np.dot(X.T, X), np.dot(X.T, y_train))
        #beta, _, _, _ = np.linalg.lstsq(np.dot(X.T, X), np.dot(X.T, y_train))

        self.coeff = beta
        self.train_MSE = np.sqrt(np.linalg.norm(y_train - self.predict(X_train)) ** 2 / y_train.shape[0])

    def predict(self, X_test):
        intercept = np.ones((X_test.shape[0], 1))
        X = np.append(intercept, X_test, axis=1)

        y_test = np.dot(X, self.coeff)

        return y_test


from scipy.stats import linregress, pearsonr
from scipy import integrate
import numpy as np
from scipy.signal import find_peaks
import statistics
import math
from filters import butter_bandpass_filter, get_energy
import heartpy as hp

def signal_peak_frequency(sampled_signal, sampling_rate):
    """Finds the peak FFT frequency band in Hertz
    Source: https://stackoverflow.com/questions/3694918/how-to-extract-frequency-associated-with-fft-values-in-python
    :param sampled_signal: ndarray
        The signal used to find the peak frequency component
    :param sampling_rate: float
        The sampling rate of the signal when captured
    :return: float
        The peak frequency component in Hertz
    """
    w = np.fft.fft(sampled_signal)
    freqs = np.fft.fftfreq(len(sampled_signal))
    idx = np.argmax(np.abs(w))
    freq = freqs[idx]
    freq_in_hertz = abs(freq * sampling_rate)
    return freq_in_hertz

def signal_mean(sampled_signal):
    """Calculates the simple geometric mean of a signal
    Parameters
    :param sampled_signal: ndarray
        Signal used to calculate mean
    :return: float
        Geometric mean of signal
    """
    return np.mean(sampled_signal)

def signal_standard_deviation(sampled_signal):
    """Calculates the standard deviation of the signal passed
    Parameters
    :param sampled_signal: ndarray
        Signal used to calculate the standard deviation
    :return: float
        Standard deviation of signal
    """
    return statistics.stdev(sampled_signal)

def signal_integral(sampled_signal):
    """Calculates the integral of the signal using the composite Simpson's rule
    Parameters
    :param sampled_signal: ndarray
        The signal used to calculate the area under the curve
    :return: float
        Area under the curve
    """
    return integrate.simps(sampled_signal)

def signal_absolute(integrated_signal):
    """Calculates the absolute value of a signal, meant for use after the integration of the signal
    Parameters
    :param integrated_signal: float
        Output of the signal_integration function
    :return: float
        The absolute value of the input
    """
    return np.abs(integrated_signal)

def signal_dyanmic_range(sampled_signal):
    """Calculates the dynamic range of a signal, which is the ratio between the highest value and the lowest value
    Parameters
    :param sampled_signal: ndarray
        The signal used to calculate the dynamic range
    :return: float
        The dynamic range of the signal
    """
    return max(sampled_signal) / min(sampled_signal)

def signal_rms(sampled_signal):
    """Calculates the root mean square of a signal
    Source: https://stackoverflow.com/questions/40963659/root-mean-square-of-a-function-in-python
    Parameters
    :param sampled_signal: ndarray
        The signal used to calculate the root mean square
    :return: float
        The root mean square of the signal
    """
    return np.sqrt(np.mean(sampled_signal**2))

def signal_frequency_band_energies(sampled_signal, frequency_bands, sampling_frequency, order=5):
    """Iterates through a list provided in frequency_bands to extract the energies in each band from sampled_signal
    Parameters
    :param sampled_signal: ndarray
        The signal used to calculate the frequency band energies
    :param frequency_bands: list[[float, float]]
        List of frequency bands in the format [[low, high], [low, high],...,[low, high]]
    :param sampling_frequency: float
        The sampling frequency of the signal used to calculate the frequency band energies
    :param order: int
        The order of the butterworth filter to be used, defaults to 5
    :return: list[float]
        List appended with the calculated energies in the frequency bands of the form [float,...,float]
    """
    energies = []
    for bands in frequency_bands:
        energies.append(butter_bandpass_filter(sampled_signal, bands[0], bands[1], sampling_frequency, order))
    return energies

def signal_frequency_normalize(frequencies, axis=-1, order=2):
    """Normalizes the input frequency list in frequencies
    Source: https://stackoverflow.com/questions/21030391/how-to-normalize-an-array-in-numpy
    Parameters
    :param frequencies: list[float]
        List of frequency energies to be normalized
    :param axis: int
        Axis to be normalized on, default is -1
    :param order: int
        The order of the normalization, default is 2
    :return: list[float]
        List containing the normalized frequency energies
    """
    normalized_frequencies = []
    for frequency in frequencies:
        norm = np.atleast_1d(np.linalg.norm(frequency, order, axis))
        norm[norm == 0] = 1
        normalized_frequencies.append(frequency / np.expand_dims(norm, axis))
    return normalized_frequencies

def signal_relative_power(frequencies, scale):
    """Calculates the relative power in dB for the signal, centered around [-1, 1]
    Source: https://stackoverflow.com/questions/2445756/how-can-i-calculate-audio-db-level
    Parameters
    :param frequencies: list[float]
        List containing the frequency energies to be used in the calculation
    :param scale: list[int]
        List containing the high and low range for the signal, i.e. signed 8-bit scale is 128
    :return: list[float]
        List containing the relative powers of the frequencies passed in the same order
    """
    relative_power = []
    for frequency in frequencies:
        norm_freq = frequency / scale
        relative_power.append(20 * math.log10(max(norm_freq) + 1e-7))
    return relative_power

def signal_slope(sampled_signal, time_labels):
    """Calculates the slope of the signal using linear regression
    Source: https://stackoverflow.com/questions/9538525/calculating-slopes-in-numpy-or-scipy
    Parameters
    :param sampled_signal: ndarray
        The signal used to calculate the slope
    :param time_labels: ndarray
        For time-series data, using np.arange(0, len(sampled_signal)) should work
    :return: float
        Slope of the signal
    """
    slope, intercept, r_value, p_value, std_err = linregress(sampled_signal, time_labels)
    return slope

def signal_percentile(sampled_signal, percentile):
    """Calculates the specified q-th percentile of the signal
    Source: https://stackoverflow.com/questions/2374640/how-do-i-calculate-percentiles-with-python-numpy
    Parameters
    :param sampled_signal: ndarray
        The signal used to calculate the percentile
    :param percentile: ndarray
        Percentile or sequence of percentiles to compute, [0, 100]
    :return: scalar or ndarray
        Percentiles of the signal
    """
    return np.percentile(sampled_signal.reshape(-1), percentile)

def signal_correlation(sampled_signal, time_labels):
    """Calculates the Pearson R Coefficient of the signal against time
    Source: https://towardsdatascience.com/four-ways-to-quantify-synchrony-between-time-series-data-b99136c4a9c9
    Parameters
    :param sampled_signal: ndarray
        The signal used to calculate the Pearson R Coefficient
    :param time_labels: ndarray
        Array of labels corresponding to each sample, np.arange(0, len(sampled_signal)) can be used
    :return: float, float
        Pearson R Coefficient of the signal, two-tailed p-value
    """
    r, _ = pearsonr(sampled_signal, time_labels)
    return r

def signal_min_max(sampled_signal):
    """Returns the min and max of the signal
    Parameters
    :param sampled_signal: ndarray
        Signal used to get the min and max
    :return: scalar, scalar
        Returns the max and min of signal in form [max, min]
    """
    return [max(sampled_signal), min(sampled_signal)]

def signal_peak_count(sampled_signal):
    """Counts the number of peaks in the signal
    Parameters
    :param sampled_signal: ndarray
        Signal used to find the peakr
    :return: ndarray
        Returns an array of indexes corresponding to the peaks in the signal
    """
    peaks, _ = find_peaks(sampled_signal)
    return peaks

def signal_frequency_summation(energies):
    """Performs a summation of the frequencies passed in the list energies
    Parameters
    :param energies: list[float]
        List containing the energies in the frequency bands of a signal
    :return: list[float]
        Returns a list of the summed energies in the same order
    """
    band_energies = []
    for energy in energies:
        band_energies.append(get_energy(energy))
    return band_energies

def signal_lf_hf_ratio(lf, hf):
    """Computes the ratio between the high and low frequency components of a signal
    Parameters
    :param lf: scalar
        Summation of low frequency energies in a signal
    :param hf: scalar
        Summation of high frequency energies in a signal
    :return: scalar
        Ratio of the high and low frequency energies in a signal
    """
    return lf / hf

def bvp_signal_processing(bvp_signal, sampling_frequency):
    """Simple function wrapping the HeartPy process method for BVP signal metric extraction
    Parameters
    :param bvp_signal: ndarray
        BVP signal used to get metrics, run bvp_signal.reshape(-1) to make it a (1,n) array
    :param sampling_frequency: float
        The sampling frequency used when capturing the signal
    :return: ndarray, dict
        Returns two variables, the output signal to be used in the hp.plotter method and the metrics in a dict
    """
    try:
        _, m = hp.process(bvp_signal, sample_rate=sampling_frequency)
    except:
        NaN = float('nan')
        m = {'bpm': NaN, 'ibi': NaN, 'sdnn': NaN, 'sdsd': NaN, 'rmssd': NaN, 'pnn20': NaN, 'pnn50': NaN, 'hr_mad': NaN, 'sd1': NaN, 'sd2': NaN, 's': NaN, 'sd1/sd2': NaN, 'breathingrate': NaN}
    return m


import numpy as np

# create empty ndarray (2d)
a = np.arange(25)      # create 1D array
a = a.reshape((5, 5))  # one Dismensional reshape to twd D

"""
a:
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]
 [20 21 22 23 24]]
"""

b = np.array([10, 62, 1, 14, 2,
              56, 79, 2, 1, 45,
              4, 92, 5, 55, 63,
              43, 35, 6, 53, 24,
              56, 3, 56, 44, 78])
b = b.reshape((5,5))

print(a.dtype)
print(b.dtype)

# basic operator
print(a + b)    # add
print(a - b)    # sub
print(a * b)    # mul
print(b / a)    # div
print(a ** b)   # pow
print(a > b) # show True or False in orignal structor
print(a < b)
print(a.dot(b))

print(a.sum()) # 300
print(b.sum()) # 889 对所有的item进行求和
print(b.min()) # 找出该数组中的最小的一个元素
print(b.max()) # 找出该数组中的最大的一个元素
c = b.cumsum()
print(c)
print(len(c))


import numpy as np
from scipy.stats import norm

def geometric_brownian_motion(mu=0., sigma=1., t=2., dt=0.01, s0=1.):
    """
    Generate geometric brownian motion.
    :param mu:
    :param sigma:
    :param t:
    :param dt:
    :param s0:
    :return:
    """
    N = int(round(t / dt))
    tt = np.linspace(0, t, N)
    W = np.random.standard_normal(size=N)
    W = np.cumsum(W) * np.sqrt(dt)  ### standard brownian motion ###
    X = (mu - 0.5 * sigma ** 2) * tt + sigma * W
    S = s0 * np.exp(X)  ### geometric brownian motion ###
    return tt, S

def blackScholes(tau, S, K, sigma):
    """
    Computes a set of theoretical quantities associated to a call option in the Black Scholes model.
    :param tau: time to expiry
    :param S: the underlying's value
    :param K: the strike of the option
    :param sigma: the implied volatility
    :return: dict containing
        npv: net present value of the option
        delta: the sensitivity to the change to the underlying
        gamma: the sensitivity to the change in delta
        vega: the sensitivity to the change in volatility
        theta: the sensitivity to the passage of time
    """
    sqrt_tau = np.sqrt(tau)
    sigma_sqrt_tau = sigma * sqrt_tau
    d1 = np.log(S / K) / sigma / sqrt_tau + 0.5 * sigma_sqrt_tau if tau > 0 else 0
    d2 = d1 - sigma_sqrt_tau
    npv = (S * norm.cdf(d1) - K * norm.cdf(d2)) if tau > 0 else np.clip(S-K, 0., np.infty)
    delta = norm.cdf(d1)
    gamma = norm.pdf(d1) / (S * sigma_sqrt_tau)  if tau > 0 else 0
    vega = S * norm.pdf(d1) * sqrt_tau
    theta = -.5 * S * norm.pdf(d1) * sigma / sqrt_tau  if tau > 0 else 0
    return {'npv': npv, 'delta': delta, 'gamma': gamma, 'vega': vega, 'theta': theta}

class CallOption(object):
    """
    Represents a call option in the Black Scholes framework
    """
    def __init__(self, t, k, n):
        """

        :type t: int
        :type k: int
        :type n: int
        """
        self.expiry = t
        self.strike = k
        # self.start = start  # day to sell option
        self.N = n

    def calc(self, today, vola, underlying):
        # if today < self.start:
        #     return {'delta': 0, 'npv': 0, 'vega': 0, 'gamma': 0, 'theta': 0, 'intrinsic': 0}
        # if today > self.expiry:
        #     return {'delta': 0, 'npv': 0, 'vega': 0, 'gamma': 0, 'theta': 0, 'intrinsic': 0}
        # if today == self.expiry:
        #     return {'delta': 0, 'npv': 0, 'vega': 0, 'gamma': 0, 'theta': 0, 'intrinsic': self.N * max(0, underlying - self.strike)}

        tau = (self.expiry - today) / 250.

        call = blackScholes(tau, underlying, self.strike, vola)

        return {'delta': self.N * call['delta'],
                'npv': self.N * call['npv'],
                'vega': self.N * call['vega'],
                'gamma': self.N * call['gamma'],
                'theta': self.N * call['theta'],
                'intrinsic': self.N * max(0, underlying - self.strike)}


from math import sqrt, cos, acos, pi
from copy import deepcopy
import numpy as np
import matplotlib.pyplot as plt

class Distance:
    @staticmethod
    def euc(x1, y1, x2, y2):
        x_diff = x1 - x2
        y_diff = y1 - y2
        return int(sqrt(x_diff * x_diff + y_diff * y_diff)+0.5)

    @staticmethod
    def att(x1, y1, x2 ,y2):
        xd = x1 - x2
        yd = y1 - y2
        r = sqrt((xd*xd+yd*yd)/10.0)
        t = int(r+0.5)
        if (t < r):
            return t+1
        else:
            return t

    @staticmethod
    def geo(x1, y1, x2 ,y2):
        def conv_radians(x, y):
            pi_val = 3.141592
            deg = int(x+0.5)
            m = x - deg
            lat = pi_val * (deg + m * 5 / 3 ) / 180
            deg = int(y+0.5)
            m = y - deg
            lng = pi_val * (deg + m * 5 /3 ) / 180
            return lat, lng

        lat1, lng1 = conv_radians(x1, y1)
        lat2, lng2 = conv_radians(x2, y2)

        rrr = 6378.388

        q1 = cos(lng1 - lng2)
        q2 = cos(lat1 - lat2)
        q3 = cos(lat1 + lat2)
        return int( rrr* acos( ((1+q1)*q2 - (1-q1)*q3)/2) + 1.0)

class Problem:
    def __init__(self, name, comment, dim, dist, coords):
        self.name = name
        self.comment = comment
        self.dim = dim
        self.dist = dist
        self.coords = coords
        assert(len(coords) == dim)

    @classmethod
    def from_file(cls, filename):
        with open(filename, 'r') as f:
            name = f.readline().split(':')[1].strip()
            next(f)   # TYPE is fixed as TSP
            comment = f.readline().split(':')[1].strip()
            dim = int(f.readline().split(':')[1])
            distance_class = f.readline().split(':')[1].strip()
            if (distance_class == "EUC_2D"):
                distance_function = Distance.euc
            elif (distance_class == "ATT"):
                distance_function = Distance.att
            elif (distance_class == "GEO"):
                distance_function = Distance.geo
            else:
                print("No such distance")
                exit()
            while(True):
                if (f.readline().strip() == "NODE_COORD_SECTION"):
                    break
            coords = np.empty([dim, 2])
            while True:
                line = f.readline().strip()
                if (line == "EOF"):
                    break
                node, x, y = line.split()
                coords[int(node)-1] = [float(x), float(y)]
        return cls(name, comment, dim, distance_function, coords)

class Solution:
    def __init__(self, name, dim, tour):
        self.name = name
        self.dim = dim
        self.tour = tour
        assert(self.dim == len(self.tour))

    @classmethod
    def from_file(cls, filename):
        with open(filename, 'r') as f:
            name = f.readline().split(':')[1].strip()
            next(f)                                 # type is tour
            dim = int(f.readline().split(':')[1])
            next(f)                                 # tour_section
            tour = np.empty([dim], dtype=np.int32)
            for i in range(dim):
                tour[i] = int(f.readline()) - 1     # zero-indexed
        return cls(name, dim, tour)

def estimate_cost(problem, solution):
    prev_it = iter(solution.tour)
    next_it = iter(solution.tour)
    next(next_it)
    cost = 0
    stop = False
    while True:
        n = next(next_it, None)
        if (n is None):
            stop = True
            n = solution.tour[0]
        p = next(prev_it)
        cost += problem.dist(*problem.coords[n], *problem.coords[p])
        if stop:
            break

    return cost


'''
Integre numericamente o sistema de equações do modelo SIR usando método de Euler-Cromer. Para simplificar a exploração
do modelo use como parâmetros \gamma=0.5  e varie o parâmetro \beta e as condições iniciais como indicado nos ítens abaixo:

    1. Use a solução analítica para estimar um valor para \Delta t. Para testar se seu \Delta t está adequado, use
     \beta=1, S=99., I=1. e R=0.. Verifique se N se conserva em torno de 1\%.
    2. A partir da solução analítica encontre valores iniciais de S e I para os quais a infecção não se propaga.
     Integre o sistema a partir desses valores e mostre os gráficos respectivos de S,I,R . Mantenha os valores de \beta e \gamma do item 1.
    3. A partir da solução analítica encontre valores iniciais de S e I para os quais a infecção se propaga.
     Integre o sistema a partir desses valores e mostre os gráficos respectivos de S,I,R . Mantenha os valores de \gamma e \beta do item 1.
    4. Usando os dados do item 1 acima, variando apenas o parâmetro \beta, mostre que a infecção pode ser
    extinta sem que toda a população seja contaminada.
'''
import numpy as np
import matplotlib.pyplot as plt

def modsir(s0, i0, r0, tf, dt, b, g):
    n0 = s0 + i0 + r0
    fs = lambda i0, s0: -(b*i0*s0/n0)
    fi = lambda i0, s0: (b*i0*s0)/n0 - (g*i0)
    fr = lambda i0: g*i0
    s = [s0]
    i = [i0]
    r = [r0]
    n = [n0]
    for t in np.arange(0, tf, dt):
        s0 += fs(i0, s0)*dt
        s.append(s0)
        i0 += fi(i0, s0)*dt
        i.append(i0)
        r0 += fr(i0)*dt
        r.append(r0)
        n0 = s0 + i0 + r0
        n.append(n0)
    return s, i, r, n

g, b, s0, i0, r0, tf, dt = 0.5, 0.5, 99, 1, 0, 100, 0.01
t = np.arange(0, tf+dt, dt)

par1 = s0/(s0+i0)
par2 = g/b
print(par1 - par2)

plt.plot(t, modsir(s0, i0, r0, tf, dt, b, g)[0], c='purple', label='Suscetíveis')
plt.plot(t, modsir(s0, i0, r0, tf, dt, b, g)[1], c='r', label='Infectados')
plt.plot(t, modsir(s0, i0, r0, tf, dt, b, g)[2], c='orange', label='Removidos')
plt.plot(t, modsir(s0, i0, r0, tf, dt, b, g)[3], c='b', label='População')
plt.xlabel('t')
plt.ylabel('Pessoas(t)')
plt.grid()
plt.legend()
plt.title('Modelo SIR'+'\n'+r'$\beta$='+f'{b}'+r' | $\gamma$='+f'{g} | '+r'$I_{0}=$'+f'{i0}'+r' | $S_{0}=$'+f'{s0}')
plt.savefig('SIR4.png')
#plt.show()


"""
__FileCreationDate__  :  //2020
__Author__           :  CodePerfectPlus
__Package__         :  Python 3
__GitHub__         : https://www.github.com/codeperfectplus
"""
import numpy as np

class GradientDescent:
    """
    Gradient Descent Algorithm Implementation
    """

    def __init__(self, w, x, b, y):
        self.w = w
        self.b = b
        self.x = x
        self.y = y
        self.z = self.w * self.x + self.b

    def sigmoid(self, w, b, x):
        return 1.0 / (1.0 + np.exp(-w * self.x + self.b))

    def grad_w(self, w, b, x, y):
        fx = self.sigmoid(w, x, b)
        return (fx - y) * fx * (1 - fx) * x

    def grad_b(self, w, b, x, y):
        fx = self.sigmoid(w, b, x)
        return (fx - y) * fx * (1 - fx)

    def Compute(self):
        w, b, eta = -2, -2, 1.0
        epochs = 1000
        for i in range(epochs):
            dw = db = 0
            for x, y in zip(self.x, self.y):
                dw = dw + self.grad_w(w, b, x, y)
                db = db + self.grad_b(w, b, x, y)
            w = w - eta * dw
            b = b - eta * db

class Adam:
    def __init__(self, learning_rate=0.001, b1=0.9, b2=0.999):
        self.learning_rate = learning_rate
        self.eps = 1e-8
        self.m = None
        self.v = None

        self.b1 = b1
        self.b2 = b2

    def update(self, w, grad_wrt_w):
        if self.m is None:
            self.m = np.zeros(np.shape(grad_wrt_w))
            self.v = np.zeros(np.shape(grad_wrt_w))

        self.m = self.b1 * self.m + (1 - self.b1) * grad_wrt_w
        self.v = self.b2 * self.m + (1 - self.b2) * np.power(grad_wrt_w, 2)

        m_hat = self.m / (1 - self.b1)
        v_hat = self.m / (1 - self.b2)

        self.w_updt = self.learning_rate * m_hat / (np.sqrt(v_hat) + self.eps)
        return w - self.w_updt


import numpy as np

class sigmoid:
    """
    takes a real value as input and outputs another value between 0 and 1.
    """

    def __call__(self, z):
        """
        numeric stable version of: 1.0 / (1.0 + np.exp(-z))
        """
        return np.exp(-np.logaddexp(0, -z))

    def prime(self, z):
        return self(z) * (1 - self(z))

    def __repr__(self):
        return "Sigmoid"

class relu:
    """
    returns 0 if the input z is lower than 0, otherwise is linear (returns z)
    """

    def __call__(self, z):
        return np.maximum(0, z)

    def prime(self, z):
        return (z > 0).astype("float32")

    def __repr__(self):
        return "ReLu"

class lrelu:
    """
    like relu, but instead of 0 it returns z multiplied by a (very small) alpha
    """

    def __init__(self, alpha=0.01):
        self.alpha = alpha

    def __call__(self, z):
        return np.maximum(self.alpha * z, z)

    def prime(self, z):
        return np.where(z <= 0, 0, self.alpha)

    def __repr__(self):
        return "Leaky ReLu"

class swish:
    """
    new activation function proposed by google
    https://arxiv.org/abs/1710.05941
    """
    def __init__(self):
        self.sigmoid = sigmoid()
        self.relu = relu()

    def __call__(self, z):
        return z * self.sigmoid(z)

    def prime(self, z):
        return self.relu(z) + self.sigmoid(z)*(1 - self.relu(z))

    def __repr__(self):
        return "Swish"

class softmax:

    """
    Softmax function calculates the probabilities distribution of
    the event over ‘n’ different events (one output unit for every class)

    ---
    the softmax activation function can only work as an output layer
    together with the cross entropy loss function.
    since the derivative is set to 1, the softmax layer will directly
    pass the combined derivative of softmax + cross entropy to the
    next layer
    """

    def __call__(self, z):
        m = np.max(z, axis=1, keepdims=True)
        e = np.exp(z - m)
        return e / np.sum(e, axis=1, keepdims=True)

    def prime(self, z):
        return 1

    def __repr__(self):
        return "Softmax"

class identity:
    """
    linear activation function (returns the input)
    """

    def __call__(self, z):
        return z

    def prime(self, z):
        return np.ones(z.shape)

    def __repr__(self):
        return "Identity"

class tanh:
    """
    Tanh squashes a real-valued number to the range [-1, 1]
    """

    def __call__(self, z):
        return np.tanh(z)

    def prime(self, z):
        return 1 - np.power(self(z), 2)

    def __repr__(self):
        return "Tanh"


import collections
import math
import numpy as np

class Gaussian_Naive_Bayes():
    def fit(self, X_train, y_train):
        """
        fit with training data
        Inputs:
            - X_train: A numpy array of shape (N, D) containing training data; there are N
                training samples each of dimension D.
            - y_train: A numpy array of shape (N,) containing training labels; y[i] = c
                means that X[i] has label 0 <= c < C for C classes.

        With the input dataset, function gen_by_class will generate class-wise mean and variance to implement bayes inference.

        Returns:
        None

        """
        self.x = X_train
        self.y = y_train

        self.gen_by_class()

    def gen_by_class(self):
        """
        With the given input dataset (self.x, self.y), generate 3 dictionaries to calculate class-wise mean and variance of the data.
        - self.x_by_class : A dictionary of numpy arraies with the keys as each class label and values as data with such label.
        - self.mean_by_class : A dictionary of numpy arraies with the keys as each class label and values as mean of the data with such label.
        - self.std_by_class : A dictionary of numpy arraies with the keys as each class label and values as standard deviation of the data with such label.
        - self.y_prior : A numpy array of shape (C,) containing prior probability of each class
        """
        self.x_by_class = dict()
        self.mean_by_class = dict()
        self.std_by_class = dict()
        self.y_prior = None

        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Generate dictionaries.
        # hint : to see all unique y labels, you might use np.unique function, e.g., np.unique(self.y)
        classes = np.unique(self.y)
        counts = dict()
        for c in classes:
            self.x_by_class[c] = []
            self.mean_by_class[c] = []
            self.std_by_class[c] = []
            counts[c] = 0
        for i in range(0, self.x.shape[0]):
            c = self.y[i]
            self.x_by_class[c].append(self.x[i])
            counts[c] = counts[c] + 1
        for c in classes:
            self.mean_by_class[c] = self.mean(self.x_by_class[c])
            self.std_by_class[c] = self.std(self.x_by_class[c])
        self.y_prior = np.array([(counts[c] / len(self.y)) for c in classes])
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

    def mean(self, x):
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate mean of input x
        total = 0
        for i in x:
            total += i
        mean = total / len(x)
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################
        return mean

    def std(self, x):
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate standard deviation of input x, do not use np.std
        mean = self.mean(x)
        var = sum([(n-mean)**2 for n in x]) / (len(x)-1)
        std = [math.sqrt(n) for n in var]
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################
        return np.array(std)

    def calc_gaussian_dist(self, x, mean, std):
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # calculate gaussian probability of input x given mean and std
        for i in range(len(std)):
            if std[i] == 0:
                std[i] = 1e-5
        dist = np.exp(-((x-mean)**2 / (2*std**2))) / (math.sqrt(2*math.pi)*std)
        for i in range(len(dist)):
            if dist[i] == 0:
                dist[i] = 1e-5
        return dist
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

    def predict(self, x):
        """
        Use the acquired mean and std for each class to predict class for input x.
        Inputs:

        Returns:
        - prediction: Predicted labels for the data in x. prediction is (N, C) dimensional array, for N samples and C classes.
        """

        n = len(x)
        num_class = len(np.unique(self.y))
        prediction = np.zeros((n, num_class))

        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # calculate naive bayes probability of each class of input x
        for arr in range(n):
            for c in range(num_class):
                prediction[arr][c] = self.y_prior[c] + np.sum(np.log(self.calc_gaussian_dist(x[arr], self.mean_by_class[c], self.std_by_class[c])))
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

        return prediction

class Neural_Network():
    def __init__(self, hidden_size=64, output_size=1):
        self.W1 = None
        self.b1 = None
        self.W2 = None
        self.b2 = None

        self.hidden_size = hidden_size
        self.output_size = output_size

    def fit(self, x, y, batch_size=64, iteration=2000, learning_rate=1e-3):
        """
        Train this 2 layered neural network classifier using mini-batch stochastic gradient descent.
        Inputs:
        - X: A numpy array of shape (N, D) containing training data; there are N
          training samples each of dimension D.
        - y: A numpy array of shape (N,) containing training labels; y[i] = c
          means that X[i] has label 0 <= c < C for C classes.
        - learning_rate: (float) learning rate for optimization.
        - iteration: (integer) number of steps to take when optimizing
        - batch_size: (integer) number of training examples to use at each step.

        Use the given learning_rate, iteration, or batch_size for this homework problem.

        Returns:
        None
        """
        dim = x.shape[1]
        num_train = x.shape[0]

        # initialize W
        if self.W1 is None:
            self.W1 = 0.001 * np.random.randn(dim, self.hidden_size)
            self.b1 = 0

            self.W2 = 0.001 * np.random.randn(self.hidden_size, self.output_size)
            self.b2 = 0

        for it in range(iteration):
            batch_ind = np.random.choice(num_train, batch_size)

            x_batch = x[batch_ind]
            y_batch = y[batch_ind]

            loss, gradient = self.loss(x_batch, y_batch)

            ############################################################
            ############################################################
            # BEGIN_YOUR_CODE
            # Update parameters with mini-batch stochastic gradient descent method
            self.W1 = self.W1 - learning_rate * gradient["dW1"]
            self.W2 = self.W2 - learning_rate * gradient["dW2"]
            self.b1 = self.b1 - learning_rate * gradient["db1"]
            self.b2 = self.b2 - learning_rate * gradient["db2"]
            pass
            # END_YOUR_CODE
            ############################################################
            ############################################################

            y_pred = self.predict(x_batch)
            acc = np.mean(y_pred == y_batch)

            if it % 50 == 0:
                print('iteration %d / %d: accuracy : %f: loss : %f' % (it, iteration, acc, loss))

    def loss(self, x_batch, y_batch, reg=1e-3):
        """
            Implement feed-forward computation to calculate the loss function.
            And then compute corresponding back-propagation to get the derivatives.

            Inputs:
            - X_batch: A numpy array of shape (N, D) containing a minibatch of N
              data points; each point has dimension D.
            - y_batch: A numpy array of shape (N,) containing labels for the minibatch.
            - reg: hyperparameter which is weight of the regularizer.

            Returns: A tuple containing:
            - loss as a single float
            - gradient dictionary with four keys : 'dW1', 'db1', 'dW2', and 'db2'
            """
        gradient = {'dW1': None, 'db1': None, 'dW2': None, 'db2': None}

        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate y_hat which is probability of the instance is y = 0.
        g1 = x_batch.dot(self.W1) + self.b1
        h1 = self.activation(g1)
        g2 = h1.dot(self.W2) + self.b2
        y_hat = self.sigmoid(g2)
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate loss and gradient
        n = y_hat.shape[0]
        loss = -np.sum(y_batch * np.log(y_hat + 1e-5)) / n

        gradient['dW1'] = x_batch.transpose().dot(y_hat-y_batch)*self.W2.transpose() / (2*n)
        gradient['db1'] = np.mean((y_hat-y_batch)*self.W2) / n
        gradient['dW2'] = h1.transpose().dot(y_hat-y_batch) / (2*n)
        gradient['db2'] = np.mean(y_hat-y_batch) / n
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################
        return loss, gradient

    def activation(self, z):
        """
        Compute the ReLU output of z
        Inputs:
        z : A scalar or numpy array of any size.
        Return:
        s : output of ReLU(z)
        """
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Implement ReLU
        s = np.maximum(0, z)
        pass

        # END_YOUR_CODE
        ############################################################
        ############################################################

        return s

    def sigmoid(self, z):
        """
        Compute the sigmoid of z
        Inputs:
        z : A scalar or numpy array of any size.
        Return:
        s : sigmoid of input
        """
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        s = 1 / (1 + np.exp(-z))
        pass
        # END_YOUR_CODE
        ############################################################
        ############################################################

        return s

    def predict(self, x):
        """
        Use the trained weights of this linear classifier to predict labels for
        data points.
        Inputs:

        Returns:
        - y_pred: Predicted labels for the data in X. y_pred is a 1-dimensional
          array of length N, and each element is an integer giving the predicted
          class.
        """
        ############################################################
        ############################################################
        # BEGIN_YOUR_CODE
        # Calculate predicted y
        g1 = x.dot(self.W1) + self.b1
        h1 = self.activation(g1)
        g2 = h1.dot(self.W2) + self.b2
        y_hat = np.round(self.sigmoid(g2))
        pass

        # END_YOUR_CODE
        ############################################################
        ############################################################
        return y_hat


import random
import numpy as np
import time
import random
import multiprocessing
import datetime

def simulationpi():
    # initialize circle points, square points amd interbal
    INTERVAL = 2000
    circle_points = 0
    rect_points = 0

    for _ in range(INTERVAL):
        x = random.uniform(-1, 1)
        y = random.uniform(-1, 1)

        dist = x ** 2 + y ** 2
        if dist <= 1:
            circle_points += 1
        rect_points += 1

        res = 4 * (circle_points/rect_points)

    return res

def simulationvect(n=20000):
    x = np.random.random(n)
    y = np.random.random(n)
    print(x*x + y*y <= 1)
    return (x*x + y*y <= 1).sum() / 20000 * 4

if __name__ == "__main__":
    begin = time.time()
    print(simulationpi())
    time.sleep(1)
    end = time.time()
    print("Time cost is {}",format(end - begin))

    begin = time.time()
    print(simulationvect())
    time.sleep(1)
    end = time.time()
    print("Time cost is {}",format(end - begin))


import numpy as np
# np.random.seed(1)
import matplotlib.pyplot as plt
import pickle

from statsmodels.tsa.vector_ar.var_model import VARProcess

'''
Sample time series data by VAR model
'''

#### Ten nodes
# # (ndarray (p x k x k)) – coefficients for lags of endog, part or params reshaped
# coefs = np.array([[[0.6, 0, 0, 0.1, 0, -0.2, 0, 0, 0.1, 0], # node 1
#                    [0, 0.7, 0, 0, 0, 0, 0, 0, 0, 0],       # node 2
#                    [0, 0, 0.7, 0, 0.2, 0, 0, 0, 0, 0],     # node 3
#                    [0.1, 0, 0, 0.6, 0, 0.2, 0, 0, 0.2, 0], # node 4
#                    [0, 0, 0.2, 0, 0.7, 0, 0, 0, 0, 0],     # node 5
#                    [-0.2, 0, 0, 0.2, 0, 0.6, 0, 0, 0.1, 0], # node 6
#                    [0, 0, 0, 0, 0, 0, 0.6, 0, 0, 0],       # node 7
#                    [0, 0, 0, 0, 0, 0, 0, 0.5, 0, 0],       # node 8
#                    [0.1, 0, 0, 0.2, 0, 0.1, 0, 0, 0.6, 0], # node 9
#                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0.7]        # node 10
#                    ]])
#
# # (ndarray (k x k)) – residual covariance
# coefs_exog = np.array([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) # (ndarray) – parameters for trend and user provided exog
# sigma_u = np.array([[1, 0, 0, 0.5, 0, 0.5, 0, 0, 0.5, 0], # node 1
#                     [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],  # node 2
#                     [0, 0, 1, 0, 0.5, 0, 0, 0, 0, 0],  # node 3
#                     [0.5, 0, 0, 1, 0, 0.5, 0, 0, 0.5, 0],  # node 4
#                     [0, 0, 0.5, 0, 1, 0, 0, 0, 0, 0],  # node 5
#                     [0.5, 0, 0, 0.5, 0, 1, 0, 0, 0.5, 0],  # node 6
#                     [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],  # node 7
#                     [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],  # node 8
#                     [0.5, 0, 0, 0.5, 0, 0.5, 0, 0, 1, 0],  # node 9
#                     [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]  # node 10
#                     ])

#### Three nodes
# (ndarray (p x k x k)) – coefficients for lags of endog, part or params reshaped
coefs = np.array([[[0.6, 0.2, 0], # node 1
                   [0.2, 0.7, 0], # node 2
                   [0, 0, 0.7]  # node 3
                   ]])

# (ndarray (k x k)) – residual covariance
coefs_exog = np.array([-1, 2, -3]) # (ndarray) – parameters for trend and user provided exog
sigma_u = np.array([[1, 0.5, 0], # node 1
                    [0.5, 1, 0], # node 2
                    [0, 0, 1]    # node 3
                    ])

# intercepts are 0s if coefs_exog=None
model = VARProcess(coefs=coefs, coefs_exog=coefs_exog, sigma_u=sigma_u)

# check whether the VAR process is stable and output long run intercept of stable VAR process
print(model)

# check whether the VAR process is stable and output its eigenvalues
# print(model.is_stable(verbose=True))

# simulate VAR data, steps=1000 if none
simulated_VARdata = model.simulate_var(steps=505000, seed=1)
data_train = simulated_VARdata[5000:405000, ].tolist()
data_test = simulated_VARdata[405000:455000, ].tolist()
data_valid = simulated_VARdata[455000:505000, ].tolist()

# plot VAR data
plt.plot(simulated_VARdata)
plt.savefig('figs/VAR_simulated_data.eps', transparent=True)
plt.savefig('figs/VAR_simulated_data.png', transparent=True)
plt.show()

# store simulated data to disk
VARdata_train_pickleout = open("./datasets/simulated_ts/data_train_3nodes.pickle", "wb")
pickle.dump(data_train, VARdata_train_pickleout)
VARdata_train_pickleout.close()

VARdata_test_pickleout = open("./datasets/simulated_ts/data_test_3nodes.pickle", "wb")
pickle.dump(data_test, VARdata_test_pickleout)
VARdata_test_pickleout.close()

VARdata_valid_pickleout = open("./datasets/simulated_ts/data_valid_3nodes.pickle", "wb")
pickle.dump(data_test, VARdata_valid_pickleout)
VARdata_valid_pickleout.close()

# store simulated data to .txt files
# with open('./datasets/simulated_ts/data_train.txt', 'w') as output:
#     for item in data_train:
#         output.write("%s\n" % item)


from __future__ import annotations
import numpy as np
from numpy.linalg import inv, det, slogdet

ERR_NOT_FITTED = "Estimator must first be fitted before calling `pdf` function"

# MG:
# fit:
# - axis=0 | axis=1 (cols)
# - rowvar=False | rowvar=True (for rows variables)
# pdf:
# - float return value?
# - used X.size instead of len(self.mu_)
# l_l:
# - used X.shape[0] instead of len(mu)
# - used mu.size instead of len(X)

class UnivariateGaussian:
    """
    Class for univariate Gaussian Distribution Estimator
    """

    def __init__(self, biased_var: bool = False) -> None:
        """
        Estimator for univariate Gaussian mean and variance parameters

        Parameters
        ----------
        biased_var : bool, default=False
            Should fitted estimator of variance be a biased or unbiased estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `UnivariateGaussian.fit` function.

        mu_: float
            Estimated expectation initialized as None. To be set in `UnivariateGaussian.fit`
            function.

        var_: float
            Estimated variance initialized as None. To be set in `UnivariateGaussian.fit`
            function.
        """
        self.biased_ = biased_var
        self.fitted_, self.mu_, self.var_ = False, None, None

    def fit(self, X: np.ndarray) -> UnivariateGaussian:
        """
        Estimate Gaussian expectation and variance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Training data

        Returns
        -------
        self : returns an instance of self.

        Notes
        -----
        Sets `self.mu_`, `self.var_` attributes according to calculated estimation (where
        estimator is either biased or unbiased). Then sets `self.fitted_` attribute to `True`
        """
        # equations 1.2 & 1.3 in course material
        # self.mu_ = np.divide(np.sum(X), X.size)
        self.mu_ = X.mean()

        if self.biased_:
            self.var_ = np.var(X)
        else:
            self.var_ = np.var(X, ddof=1)

        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray) -> np.ndarray:
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, var_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError(ERR_NOT_FITTED)

        #  1.1.2 in course materials
        numerator = np.exp(-(np.divide(np.square(X - self.mu_), (2 * self.var_))))
        denominator = np.sqrt(2 * np.pi * self.var_)
        return np.divide(numerator, denominator).reshape(X.shape)

    @staticmethod
    def log_likelihood(mu: float, sigma: float, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : float
            Expectation of Gaussian
        sigma : float
            Variance of Gaussian
        X : ndarray of shape (n_samples, )
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated
        """
        # equation 1.6 from course material

        # numerator = np.prod(np.exp(-np.divide(np.square(X.T - mu), 2 * sigma)))
        # denominator = np.power(2 * np.pi * sigma, 0.5 * X.size)
        # return np.log(np.divide(numerator, denominator))

        # applying natural log on the LL:
        return -0.5 * (X.size * np.log(2 * np.pi * sigma) + (np.sum(np.square(X - mu)) / sigma))

class MultivariateGaussian:
    """
    Class for multivariate Gaussian Distribution Estimator
    """

    def __init__(self) -> None:
        """
        Initialize an instance of multivariate Gaussian estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `MultivariateGaussian.fit` function.

        mu_: ndarray of shape (n_features,)
            Estimated expectation initialized as None. To be set in `MultivariateGaussian.fit`
            function.

        cov_: ndarray of shape (n_features, n_features)
            Estimated covariance initialized as None. To be set in `MultivariateGaussian.fit`
            function.
        """
        self.mu_, self.cov_ = None, None
        self.fitted_ = False

    def fit(self, X: np.ndarray) -> MultivariateGaussian:
        """
        Estimate Gaussian expectation and covariance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Training data

        Returns
        -------
        self : returns an instance of self

        Notes
        -----
        Sets `self.mu_`, `self.cov_` attributes according to calculated estimation.
        Then sets `self.fitted_` attribute to `True`
        """

        self.mu_ = X.mean(axis=0)

        #  1.2.4 in course materials
        self.cov_ = np.cov(rowvar=False)
        self.fitted_ = True

        return self

    def pdf(self, X: np.ndarray) -> np.ndarray:
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, cov_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError(ERR_NOT_FITTED)

        # TODO: delete later
        print(f"pdf:\nlen(self.mu_) -->  X.size\nX.size = {X.size}, len(self.mu_) = {len(self.mu_)}\n")
        assert X.size == len(self.mu_), "X.size == self.mu_ (Delete this later)"

        #  1.2.5 in course materials
        d = X.size  # number of random variables
        numerator = np.exp(-0.5 * (X - self.mu_).T @ inv(self.cov_) @ (X - self.mu_))
        denominator = np.sqrt(np.power(2 * np.pi, d) * det(self.cov_))

        return np.divide(numerator, denominator)

    @staticmethod
    def log_likelihood(mu: np.ndarray, cov: np.ndarray, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : ndarray of shape (n_features,)
            Expectation of Gaussian
        cov : ndarray of shape (n_features, n_features)
            covariance matrix of Gaussian
        X : ndarray of shape (n_samples, n_features)
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated over all input data and under given parameters of Gaussian
        """
        # TODO: delete later
        print(f"log_likelihood:\n"
              f"len(mu) --> X.shape[0]\n"
              f"len(X) --> mu.size\n"
              f"len(mu) = {len(mu)}, X.shape[0] = {X.shape[0]} | len(X) = {len(X)}, mu.size = {mu.size}")

        print(f"|||||| {X.shape[0]} <-> {mu.size} | m <-> p ||||||")

        assert X.shape[0] == mu.size, "X.size == self.mu_ (Delete this later)"

        #  derived from 1.2.5 in course materials
        m = X.shape[0]  # number of random variables (n_samples)
        p = mu.size  # (n_features)

        e1 = -0.5 * m * p * np.log(2 * np.pi)

        e2 = -0.5 * m * np.log(det(cov))

        e3 = -0.5 * np.sum((X - mu).T @ inv(cov) @ (X - mu))  # np.sum([(x-mu).T @ inv(cov) @ (x-mu) for x in X])

        return e1 + e2 + e3

if __name__ == '__main__':
    # tests for UnivariateGaussian:

    sample_test_0 = np.random.randint(1, 15, size=20)
    print(f"{'-' * 30}\nsample 0:\n{sample_test_0}\nshape:{sample_test_0.shape}\n")

    numer = np.exp(-(np.divide(np.square(sample_test_0 - 1), (2 * 2))))
    deno = np.sqrt(2 * np.pi * 2)
    sample_test_1 = np.divide(numer, deno)
    print(f"{'-' * 30}\nsample 1:\n{sample_test_1}\nshape:\n{sample_test_1.shape}\n")

    sample_test_2 = sample_test_1.reshape(sample_test_0.shape)
    print(f"{'-' * 30}\nsample 2:\n{sample_test_2}\nshape:\n{sample_test_2.shape}\n")

    sample_test_3 = np.random.randint(1, 15, size=20)
    print(f"{'-' * 30}\nsample 3:\n{sample_test_3}\nshape:\n{sample_test_3.shape}\nsize:\n{sample_test_3.size}\n")

    sample_test_4 = np.random.randint(1, 15, (4, 5))
    print(f"{'-' * 30}\nsample 4:\n{sample_test_4}\nshape:\n{sample_test_4.shape}\nsize:\n{sample_test_4.size}\n")

    sample_test_5 = np.array([1, 3, 0])
    prod = np.prod(sample_test_5)
    print(f"{'-' * 30}\nsample 5:\n{sample_test_5}\nnp.prod:\n{prod}\n")

    sample_test_6 = np.random.normal(10, 1, size=10)
    print(f"{'-' * 30}\nsample 4:\n{sample_test_6}\nshape:\n{sample_test_6.shape}\nsize:\n{sample_test_6.size}\n")

    # tests for MultivariateGaussian:
    sample_test_7 = np.arange(34)
    print(f"{'-' * 30}\nshape:\n{sample_test_7.shape}\nsize:\n{sample_test_7.size}\nlen:\n{len(sample_test_7)}\n")

    x = np.ones((3, 4))
    print(x)
    print(len(x))
    print(x.size)


# calculation for linear algebra textboox, 6.1
#!/bin/python3

import numpy as np
from numpy import linalg
import random
import sympy as sp

def randVec(n):
    """
    @param n: dimension of vector space the generated vector in
    @return: numpy array of n-dimension vector ranging from [-1, 1)
    """
    return np.array([random.random() * 2 - 1 for x in range(n)])

def randIntVec(n):
    """
    @param n: dimension of vector space the generated vector in
    @return: numpy array of n-dimension vector ranging from [-9, 9)
    """
    return np.array([random.randint(-9, 9) for x in range(n)])

def vecCos(a, b):
    """
    @param
        a, b: two vectors this function calculates cosine from
            using dot(a, b) / (||a|| * ||b||)
    @return: the cosine calculated by above algorithm
    """
    return np.dot(a, b) / np.linalg.norm(a) / np.linalg.norm(b)

def print_delimeter():
    print("-" * 20)

def problem32():
    A = np.array([[.5,.5,.5,.5],[.5,.5,-.5,-.5],[.5,-.5,.5,-.5],[.5,-.5,-.5,.5]])
    vecs = [np.array(x) for x in [[.5,.5,.5,.5],[.5,.5,-.5,-.5],[.5,-.5,.5,-.5],[.5,-.5,-.5,.5]]]

    for i in range(len(vecs)-1):
        for j in range(i+1, len(vecs)):
            print("%d x %d" % (i, j))
            print(np.dot(vecs[i], vecs[j]))

    print("-" * 20)

    u,v = randVec(4), randVec(4)

    print("norm u")
    print(np.linalg.norm(u))

    print("norm Au")
    print(np.linalg.norm(np.matmul(A, u)))

    print("norm v")
    print(np.linalg.norm(v))

    print("norm Av")
    print(np.linalg.norm(np.matmul(A, v)))

    print("-" * 20)
    print("u")
    print(u)

    print("Au")
    print(np.matmul(A, u))

    print("v")
    print(v)

    print("Av")
    print(np.matmul(A, v))

    print("-" * 20)

    print("cosine between v and u: %f" % (vecCos(u, v)))
    print("cosine between Av and Au: %f" % (vecCos(np.matmul(A, u), np.matmul(A, v))))

    print("-" * 20)
    print(sp.Matrix.rref(sp.Matrix(A)))

def problem33():
    x, y, v = randIntVec(4), randIntVec(4), randIntVec(4)
    print("x \n", x)
    print("y \n", y)
    print("v \n", v)

    print_delimeter();

    print("(dot(x, v) / dot(v, v)) v\n",np.dot(x, v) / np.dot(v, v) * v)
    print("(dot(y, v) / dot(v, v)) v\n",np.dot(y, v) / np.dot(v, v) * v)
    print("(dot(x+y, v) / dot(v, v)) v\n",np.dot(x+y, v) / np.dot(v, v) * v)
    print("(dot(10x, v) / dot(v, v)) v\n",np.dot(10*x, v) / np.dot(v, v) * v)

def problem34():
    A = sp.Matrix([[-6,3,-27,-33,13],[6,-5,25,28,14],[8,-6,34,38,18],
        [12,-10,50,41,23],[14,-21,49,29,33]])
    print("reduced echelon form of A\n", sp.Matrix.rref(A))

    N = sp.Matrix([-5,-1,1,0,0])
    R = sp.Matrix([[1,0,5,0,0],[0,1,1,0,0],[0,0,0,1,0],[0,0,0,0,1]])

    print("NR \n", R * N)

if __name__ == "__main__":
    problem34()


# Based on codecademy KMeans lesson
# Expanded to plot and make gif of clustering
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import datasets
from copy import deepcopy
from time import sleep
import imageio #https://ndres.me/post/matplotlib-animated-gifs-easily/

iris = datasets.load_iris()

samples = iris.data

x = samples[:,0]
y = samples[:,1]

sepal_length_width = np.array(list(zip(x, y)))
noise = np.random.normal(0, 0.05, sepal_length_width.shape)
sepal_length_width = sepal_length_width + noise
# Step 1: Place K random centroids

k = 3

centroids_x = np.random.uniform(min(x), max(x), size=k)
centroids_y = np.random.uniform(min(y), max(y), size=k)

centroids = np.array(list(zip(centroids_x, centroids_y)))

# Distance formula
def distance(a, b, s=0):
  for ai, bi in zip(a,b):
    s += (ai - bi) ** 2
  return s ** 0.5

# Cluster labels for each point (either 0, 1, or 2)
labels = np.zeros(len(samples))
# Distances to each centroid
distances = np.zeros(k)

centroids_old = np.zeros(centroids.shape)
error = np.zeros(3)
for i in range(k):
  error[i] = distance(centroids[i], centroids_old[i])

colors = ['tab:blue', 'tab:orange', 'tab:green']
sns.set_style('darkgrid')

def kmeans_plot():
  f, ax = plt.subplots(figsize=(10,6))
  for i in range(k):
    points = sepal_length_width[labels == i]
    ax.scatter(points[:,0] ,points[:,1], c=colors[i], alpha=0.5)

  ax.scatter(centroids[:,0], centroids[:,1], marker = 'D', s=150, c=colors)
  ax.set(
    xlabel = 'sepal length (cm)',
    ylabel = 'sepal width (cm)',
    xlim = (4, 8),
    ylim = (1.8, 4.6),
    title = 'KMeans iris')
  f.canvas.draw()
  image = np.frombuffer(f.canvas.tostring_rgb(), dtype='uint8') # Params from #https://ndres.me/post/matplotlib-animated-gifs-easily/
  image = image.reshape(f.canvas.get_width_height()[::-1] + (3,)) # Looks complicated
  return image

# Repeat Steps 2 and 3 until convergence:
images = [] #
while error.all() != 0:
    centroids_old = deepcopy(centroids) # Store independent copy

    # Assign to the closest centroid
    for i in range(len(samples)):
        for j in range(k):
            distances[j] = distance(sepal_length_width[i], centroids[j])
        labels[i] = np.argmin(distances)

    images.append(kmeans_plot())
    sleep(1)

    # Calculate new centroids
    for i in range(k):
        points = sepal_length_width[labels == i]
        centroids[i] = points.mean(axis=0)
        error[i] = distance(centroids[i], centroids_old[i])
        print(error)

# Create gif
kwargs_write = {'fps':1.0, 'quantizer':'nq'}
imageio.mimsave('./kmeans_iris.gif', images, fps=1)

actual_labels = iris.target
#print(actual_labels)

f, ax = plt.subplots(figsize=(10,6))
for i in range(k):
  points = sepal_length_width[actual_labels == i]
  s = ax.scatter(points[:,0] ,points[:,1], c=colors[i], alpha=0.5)
plt.xlabel('sepal length (cm)')
plt.ylabel('sepal width (cm)')
plt.title('Target')
plt.show()

# Plot a crosstab of species and model results
species_ = ['setosa', 'versicolor', 'virginica']
species = [species_[i] for i in iris.target]

df = pd.DataFrame({'labels': labels, 'species': species})
#print(df)

ct = pd.crosstab(df['labels'], df['species'])
print(ct)

#gr = df.groupby(['labels', 'species']).labels.count()
# pv = gr.pivot(
#   columns = 'species',
#   index = 'labels'
# )


from numpy.random import randn
import numpy as np
import scipy as sp
from matrix_util import *

def Ginibre(M, N, COMPLEX=False):
    if COMPLEX==True:
        out=(randn(M,N) + 1j*randn(M,N) )/ sp.sqrt(2*N)
    else:
        out=randn(M,N)/ sp.sqrt(N)
    return np.matrix(out)

def haar_unitary(M, COMPLEX=False):
    G = Ginibre(M,M,COMPLEX)
    U,D,V = np.linalg.svd(G)
    return U

def random_from_diag(M,N,zero_dim=0, min_singular=0,COMPLEX=False):
    assert zero_dim <= min(M,N)
    array  = np.random.uniform( min_singular, 1, N)
    for i in range(zero_dim):
        array[i] = 0
    D = rectangular_diag(array, M,N)
    U = haar_unitary(M, COMPLEX)
    V = haar_unitary(N, COMPLEX)
    return U @ D @ V

def signal_plus_noise_symm(p_dim , dim, param_mat, sigma=1, COMPLEX=False):
    out = np.zeros([2*p_dim, 2*p_dim])
    C = Ginibre(p_dim , dim , COMPLEX)
    for i in range(p_dim):
        for j in range(dim):
            out[p_dim + i][j] = C[i,j]
            out[j][p_dim +i] = np.conj(C[i,j])

    return sigma*out

def signal_plus_noise(param_mat,sigma=1,COMPLEX=False, RAW=False):
    p_dim = param_mat.shape[0]
    dim = param_mat.shape[1]
    assert np.allclose(param_mat.shape, [p_dim,dim])
    X = Ginibre(p_dim, dim,COMPLEX)
    P = param_mat + sigma*X
    if RAW:
        return P
    else:
        if COMPLEX:
            Y = P.H.dot(P)
        else:
            Y = P.T.dot(P)

        return Y


#Nabin Chapagain
#1001551151

#knn_classify(<training_file>, <test_file>, <k>)

# Importing all needed libraries
import numpy as np
import math
import sys
import random
from scipy import stats
from scipy.spatial import distance
import statistics as s
from statistics import mean, median, mode, stdev

fname = sys.argv[1]
fname1 = sys.argv[2]
k_nearest = int(sys.argv[3])

mat_train = np.loadtxt(fname)
mat_train_prime = mat_train[:,0:-1]
mat_test= np.loadtxt(fname1)
mat_test_prime = mat_test[:,0:-1]

no_train_mat_row = len(mat_train)
no_train_mat_col = len(mat_train[0])

mean_arr = np.zeros(no_train_mat_col-1)
std_arr = np.zeros(no_train_mat_col-1)
std_in_between = 0

#getting mean and standard deviation for each attribute before normalizing

for i in range (0,no_train_mat_col-1):
    mean_arr[i] = mean(mat_train[:,i])
    std_in_between = stdev(mat_train[:,i])
    if (std_in_between == 0):
        std_in_between = 1
    std_arr[i] = std_in_between

#next step, normalizing the values

for j in range(0,no_train_mat_col-1):
    mat_train[:,j] = (mat_train[:,j] - mean_arr[j])/std_arr[j]
    # F(v) = (v - mean)/std

for j in range(0,no_train_mat_col-1):
    mat_test[:,j] = (mat_test[:,j] - mean_arr[j])/std_arr[j]
    # F(v) = (v - mean)/std

#here, we have normalized training matrix and test matrix

classification_accuracy = 0

for i in range(0,len(mat_test)):
    E_dist = np.zeros(no_train_mat_row)
    for k in range(len(E_dist)):
        E_dist[k]= distance.euclidean(mat_test_prime[i],mat_train_prime[k])

    accuracy = 0
    true_class =  mat_test[i][-1]

    k_nearest_points = E_dist.argsort()[:k_nearest]
    #This is the index of k_nearest_points number of lowest numbers in the list E_dist

    predicted_array = mat_train[k_nearest_points,-1]

    accuracy = 0
    mode_class = s.multimode(predicted_array)
    mode_class.sort()
    #print(mode_class,"\n")
    if(len(mode_class) == 1):
        predicted_class = mode_class[0]

        if(predicted_class == true_class):
            accuracy = 1
    else:
        #now to deal with ties
        for m in range(0,len(mode_class)):
            predicted_class = mode_class[m]
            if(true_class == predicted_class):
                accuracy = len(np.where(true_class == mode_class))/len(mode_class)
                break

    object_id = i+1
    print('ID=%5d, predicted=%3d, true=%3d, accuracy=%4.2f'%( object_id, predicted_class, true_class, accuracy))
    classification_accuracy+=accuracy
print('classification accuracy=%6.4f\n'% (classification_accuracy/len(mat_test)))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
np.set_printoptions(threshold=np.nan)
np.set_printoptions(precision=5)
from scipy import integrate
from scipy import io

#%%
def f(t,u):
    return np.array([-1000*u[0] + u[1], -u[1]/10])

#<startRK>
def r_k(f,u0,N,T):
    k = T/N
    u = np.zeros((len(u0),N+1))
    u[:,0] = u0
    for n in range(N):
        tn = k*n
        Y1 = u[:,n]
        Y2 = u[:,n] + k/2*f(tn,Y1)
        Y3 = u[:,n] + k/2*f(tn+k/2,Y2)
        Y4 = u[:,n] + k*f(tn+k/2,Y3)
        u[:,n+1] = u[:,n] + k/6*( f(tn,Y1) + 2*f(tn+k/2,Y2) + 2*f(tn+k/2,Y3) + f(tn+k,Y4))
    return u
#<endRK>

#%%
T = 1
u0 = [1,2]
error_rk = []
Ns = np.array([51,375,500,1200])
for N in Ns:
    t = np.linspace(0,T,N+1)
    u_true = np.array([9979/9999*np.exp(-1000*t)+20/9999*np.exp(-t/10),2*np.exp(-t/10)])
    u_rk = r_k(f,u0,N,T)

    error_rk.append(np.linalg.norm(u_true-u_rk,np.inf))

    plt.figure()
    ax = plt.gca()
    ax.plot(t,u_true[0],color='.7')
    ax.plot(t,u_rk[0],color='.1',marker='o',ms=3,linestyle='None')

    ax.plot(t,u_true[1],color='.7')
    ax.plot(t,u_rk[1],color='.1',marker='s',ms=3,linestyle='None')

#        ax.set_xscale('log')
    ax.set_yscale('log')
    plt.savefig('img/5/N'+str(N)+'.pdf')

#%%
sol = integrate.solve_ivp(f,[0,1],[1,2],method='RK23')

t_23 = sol.t
u_23 = sol.y
u_true = np.array([9979/9999*np.exp(-1000*t_23)+20/9999*np.exp(-t_23/10),2*np.exp(-t_23/10)])
error_23 = np.linalg.norm(u_true-u_23,np.inf)

ax = plt.gca()
ax.plot(t_23,u_true[0],color='.5')
ax.plot(t_23,u_23[0],color='.1',marker='o',ms=3,linestyle='None')

ax.plot(t_23,u_true[1],color='.5')
ax.plot(t_23,u_23[1],color='.1',marker='s',ms=3,linestyle='None')

#        ax.set_xscale('log')
ax.set_yscale('log')

plt.savefig('img/5/RK23.pdf')

plt.figure()
ax = plt.gca()
ax.plot(np.linspace(0,1,len(t_23)),t_23,'k')
plt.savefig('img/5/RK23_t.pdf')

#%%

tu = io.loadmat('tu.mat')
t_23s = tu['t'][:,0]
u_23s = tu['u'].T

u_true = np.array([9979/9999*np.exp(-1000*t_23s)+20/9999*np.exp(-t_23s/10),2*np.exp(-t_23s/10)])
error_23s = np.linalg.norm(u_true-u_23s,np.inf)

ax = plt.gca()
ax.plot(t_23s,u_true[0],color='.5')
ax.plot(t_23s,u_23s[0],color='.1',marker='o',ms=3,linestyle='None')

ax.plot(t_23s,u_true[1],color='.5')
ax.plot(t_23s,u_23s[1],color='.1',marker='s',ms=3,linestyle='None')

#        ax.set_xscale('log')
ax.set_yscale('log')

plt.savefig('img/5/ode23s.pdf')

plt.figure()
ax = plt.gca()
ax.plot(np.linspace(0,1,len(t_23s)),t_23s,'k')
plt.savefig('img/5/ode23s_t.pdf')


from sage.all import *

def bsgs(g, y, p):
    """
    Reference:

    To solve DLP: y = g^x % p and get the value of x.
    We use the property that x = i*m + j, where m = ceil(sqrt(n))

    :parameters:
        g : int/long
                Generator of the group
        y : int/long
                Result of g**x % p
        p : int/long
                Group over which DLP is generated. Commonly p is a prime number

    :variables:
        m : int/long
                Upper limit of baby steps / giant steps
        x_poss : int/long
                Values calculated in each giant step
        c : int/long
                Giant Step pre-computation: c = g^(-m) % p
        i, j : int/long
                Giant Step, Baby Step variables
        lookup_table: dictionary
                Dictionary storing all the values computed in the baby step
    """
    mod_size = len(bin(p-1)[2:])

    print "[+] Using BSGS algorithm to solve DLP"
    print "[+] Modulus size: " + str(mod_size) + ". Warning! BSGS not space efficient\n"

    m = ceil(sqrt(p-1))
    # Baby Step
    lookup_table = {pow(g, j, p): j for j in range(m)}
    # Giant Step pre-computation
    c = pow(g, m*(p-2), p)
    # Giant Steps
    for i in range(m):
        temp = (y*pow(c, i, p)) % p
        if temp in lookup_table:
            # x found
            return i*m + lookup_table[temp]
    return None

if __name__ == "__main__":
    try:
        assert pow(2, bsgs(2, 4178319614, 6971096459), 6971096459) == 4178319614
        assert pow(3, bsgs(3, 362073897, 2500000001), 2500000001) == 362073897
    except:
        print "[+] Function inconsistent and incorrect, check the implementation"


from math import pi, radians, degrees, sin, cos, atan, sqrt, sinh, cosh, asinh
import numpy as np
from scipy.integrate import quadrature
from scipy.optimize import newton
import matplotlib.pyplot as plt

# Parameters of projectile (Modelled after a baseball)
V_0     = 44.7                 # Initial velocity (m/s)
g       = 9.81                 # Acceleration due to gravity (m/s^2)
psi     = 75                   # Launch angle (deg.)
c       = 0.5                  # Drag coefficient (spherical projectile)
r       = 0.0366               # Radius of projectile (m)
m       = 0.145                # Mass of projectile (kg)
rho_air = 1.29                 # Air density (kg/m^3)
a       = pi * r**2.0          # Cross-sectional area of projectile (m^2)
psi     = radians(psi)         # Convert to radians

# Display of parameters
print('Parameters:')
print('Launch angle (deg.)                      : {:.3f}'.format(degrees(psi)))
print('Launch speed (m/s)                       : {:.3f}'.format(V_0))
print('Drag coefficient - Spherical projectile  : {:.3f}'.format(c))
print('Radius of spherical projectile (m)       : {:.3f}'.format(r))
print('Mass of projectile (kg)                  : {:.3f}'.format(m))
print('Air density (kg/m^3)                     : {:.3f}'.format(rho_air))
print('Cross-sectional area of projectile       : {:.3f}'.format(a))

# Initial position and launch velocity
x_0 = 0.0
u_0 = V_0 * cos(psi)
y_0 = 0.0
v_0 = V_0 * sin(psi)

# Constants and function definitions for solution
mu = 0.5 * c * rho_air * a / m
Q_0 = asinh(v_0 / u_0)
A   = g / (mu * u_0**2.0) + (Q_0 + 0.5 * sinh(2.0 * Q_0))

def lam(Q):
    return A - (Q + 0.5 * sinh(2.0 * Q))

def u_s(Q):
    return sqrt(g / mu) / sqrt(lam(Q))

def v_s(Q):
    return sqrt(g / mu) * sinh(Q) / sqrt(lam(Q))

def f_t(Q):
    return cosh(Q) / sqrt(lam(Q))

def f_x(Q):
    return cosh(Q) / lam(Q)

def f_y(Q):
    return sinh(2.0 * Q) / lam(Q)

def t_s(Q):
    return - quadrature(f_t, Q_0, Q, vec_func=False)[0] / sqrt(g * mu)

def x_s(Q):
    return x_0 - quadrature(f_x, Q_0, Q, vec_func=False)[0] / mu

def y_s(Q):
    return y_0 - quadrature(f_y, Q_0, Q, vec_func=False)[0] / (2.0 * mu)

def y_s_p(Q):
    return -(1.0 / (2.0 * mu)) * sinh(2.0 * Q) / lam(Q)

# Time of flight
Q_T_est = asinh(-v_0 / u_0)      # Initial estimate for Newton's method
Q_T = newton(y_s, Q_T_est, y_s_p)
T = t_s(Q_T)
print('\nResults:')
print('Time of flight (s)                       : {:.3f}'.format(T))

# Horizontal range
R = x_s(Q_T)
print('Horizontal range (m)                     : {:.3f}'.format(R))

# Maximum height
H = y_s(0.0)
print('Maximum height (m)                       : {:.3f}'.format(H))

# Vectorize scalar functions
t_vec = np.vectorize(t_s)
x_vec = np.vectorize(x_s)
y_vec = np.vectorize(y_s)
u_vec = np.vectorize(u_s)
v_vec = np.vectorize(v_s)

# Array for variable 'Q'
N = 101
psi_T = degrees(atan(sinh(Q_T)))
Q = np.arcsinh(np.tan(np.radians(np.linspace(degrees(psi), psi_T, N))))

# Arrays for projectile path variables
t = t_vec(Q)
x = x_vec(Q)
y = y_vec(Q)
u = u_vec(Q)
v = v_vec(Q)

# Plot of trajectory
fig, ax = plt.subplots()
line, = ax.plot(x, y, 'r-', label='Numerical')
ax.set_title(r'Projectile path')
ax.set_aspect('equal')
ax.grid(b=True)
ax.legend()
ax.set_xlabel('x (m)')
ax.set_ylabel('y (m)')
plt.show()

# Plot of velocity components
fig, ax = plt.subplots()
line, = ax.plot(t, u, 'b-', label='u')
ax.set_title(r'Horizontal velocity component')
ax.grid(b=True)
ax.legend()
ax.set_xlabel('t (s)')
ax.set_ylabel('u (m/s)')
plt.show()

fig, ax = plt.subplots()
line, = ax.plot(t, v, 'b-', label='v')
ax.set_title(r'Vertical velocity component')
ax.grid(b=True)
ax.legend()
ax.set_xlabel('t (s)')
ax.set_ylabel('v (m/s)')
plt.show()


import numpy as np
m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
print( m )
#transpose
print( m.T )
#inverse
print( m.I )
#vector
v = np.matrix([[2],[3],[4]])
print( v )

print( m * v )

import numpy.linalg

#determinant
print(numpy.linalg.det( m ))

#Eigenvalues
print( numpy.linalg.eigvals( m ) )

#solve x for mx=v
x = numpy.linalg.solve( m,v)
print( x )
print( m * x )

chapter3/3_10Performing Matrix and Linear Algebra Calculations/3_10.py


from math import sqrt, asin, acos, sin, cos, pi, atan2

from scipy.optimize import fsolve

R = 6.378138 * 10 ** 8
f = 0.003353

def wgs2xyz(lat, long):
    global R
    global f

    latRad = lat * pi / 180
    longRad = long * pi / 180

    theta = fsolve(lambda thetaIt: latRad - thetaIt - f * sin(2 * thetaIt), latRad)
    theta = theta[0]

    Re = R * (1 + (1 / (1 - f) ** 2 - 1) * sin(theta)) ** (-1 / 2)

    x = Re * cos(theta) * cos(longRad)
    y = Re * cos(theta) * sin(longRad)
    z = Re * sin(theta)

    return x, y, z

def xyz2wgs(x, y, z):
    global R
    global f

    Re = sqrt(x ** 2 + y ** 2 + z ** 2)

    theta = asin(z / Re)
    lat = f * sin(2 * theta) + theta
    long = acos(round(x / (Re * cos(theta)), 10))

    if y < 0:
        long = -long

    return lat * 180 / pi, long * 180 / pi

def flat2lla(flat, llo, psiRef, hRef):
    global R
    global f

    xp = flat[0]
    yp = flat[1]
    zp = flat[2]

    muRef = llo[0]
    lRef = llo[1]

    muRef = muRef * pi / 180
    lRef = lRef * pi / 180
    psiRef = psiRef * pi / 180

    dN = xp * cos(psiRef) - yp * psiRef
    dE = xp * sin(psiRef) + yp * cos(psiRef)

    Rn = R / sqrt(1 - (2 * f - f ** 2) * sin(muRef) ** 2)
    Rm = Rn * (1 - (2 * f - f ** 2)) / (1 - (2 * f - f ** 2) * sin(muRef) ** 2)

    dMu = atan2(1, Rm) * dN
    dL = atan2(1, Rn * cos(muRef)) * dE

    mu = muRef + dMu
    L = lRef + dL
    h = -zp - hRef

    return mu * 180 / pi, L * 180 / pi, h

def lla2flat(lla, llo, psio, href):
    global R
    global f
    '''
    from:https://gist.github.com/skakri/ca94fd73bf7483c1fffec500c92cacf8
    lla  -- array of geodetic coordinates
            (latitude, longitude, and altitude),
            in [degrees, degrees, meters].
            Latitude and longitude values can be any value.
            However, latitude values of +90 and -90 may return
            unexpected values because of singularity at the poles.
    llo  -- Reference location, in degrees, of latitude and
            longitude, for the origin of the estimation and
            the origin of the flat Earth coordinate system.
    psio -- Angular direction of flat Earth x-axis
            (degrees clockwise from north), which is the angle
            in degrees used for converting flat Earth x and y
            coordinates to the North and East coordinates.
    href -- Reference height from the surface of the Earth to
            the flat Earth frame with regard to the flat Earth
            frame, in meters.
    usage: print(lla2flat((0.1, 44.95, 1000.0), (0.0, 45.0), 5.0, -100.0))
    '''

    Lat_p = lla[0] * pi / 180.0  # from degrees to radians
    Lon_p = lla[1] * pi / 180.0  # from degrees to radians
    Alt_p = lla[2]  # meters

    # Reference location (lat, lon), from degrees to radians
    Lat_o = llo[0] * pi / 180.0
    Lon_o = llo[1] * pi / 180.0

    psio = psio * pi / 180.0  # from degrees to radians

    dLat = Lat_p - Lat_o
    dLon = Lon_p - Lon_o

    ff = (2.0 * f) - (f ** 2)  # Can be precomputed

    sinLat = sin(Lat_o)

    # Radius of curvature in the prime vertical
    Rn = R / sqrt(1 - (ff * (sinLat ** 2)))

    # Radius of curvature in the meridian
    Rm = Rn * ((1 - ff) / (1 - (ff * (sinLat ** 2))))

    dNorth = (dLat) / atan2(1, Rm)
    dEast = (dLon) / atan2(1, (Rn * cos(Lat_o)))

    # Rotate matrice clockwise
    Xp = (dNorth * cos(psio)) + (dEast * sin(psio))
    Yp = (-dNorth * sin(psio)) + (dEast * cos(psio))
    Zp = -Alt_p - href

    return Xp, -Yp, Zp

if __name__ == '__main__':
    lat = -37
    long = 101.7210

    x, y, z = wgs2xyz(lat, long)
    lat2, long2 = xyz2wgs(x, y, z)

    print('x , y , z = ' + str(x) + ', ' + str(y) + ', ' + str(z))
    print('latOrig = ' + str(lat))
    print('lat2 = ' + str(lat2))

    print('LongOrig = ' + str(long))
    print('Long2 = ' + str(long2))

    # for latI in range(-90, 90):
    #     for longJ in range(-179, 180):
    #         x, y, z = wgs2xyz(latI, longJ)
    #         lat2, long2 = xyz2wgsV2(x, y, z)
    #         print('i = ' + str(latI) + ', j = ' + str(longJ))
    #         if (latI - lat2)>1 or latI-lat2<-1 or longJ-long2>1 or longJ-long2<-1:
    #             print('-----------------------')
    #             print('latOrig = ' + str(latI))
    #             print('lat = ' + str(lat2))
    #             print('LongOrig = ' + str(longJ))
    #             print('Long = ' + str(long2))
    #
    print(flat2lla([x, y, z], [0, 10], 0, -1000))


#==================================================================================================
import math
import numpy as np

#==================================================================================================
def romberg(f, a, b, eps = 1e-10, nmax = 20, nmin = 2):

    r = np.zeros((nmax,nmax))

    h = b-a
    n = 1
    r[0,0] = 0.5 * h * (f(a) + f(b))

    for i in range(1,nmax):
        n *= 2
        h = h / 2.0

        # Create array of x-values (spaced between existing points)
        xa = a + (2.0 * np.arange(1, n/2 +1) - 1.0) * h
        # Create array of function values from x-coords
        fa = f(xa)
        # Sum up
        t = fa.sum()

        # Set up first entry at this level (equivalent to trapezoidal)
        r[i,0] = 0.5 * r[i-1,0] + t*h

        # Extrapolate along row
        for j in range(1,i+1):
            r[i,j] = r[i,j-1] + 1.0 / (4**j - 1) * (r[i,j-1] - r[i-1,j-1])

        # Test relative tolerance if we've done the minimum number of
        # iterations
        if(i > nmin and np.abs(1.0 - r[i-1,j-1] / r[i,j]) < eps):
            return r[i,j]

    # If max number of iterations reached return the final
    # extrapolation
    return r[nmax-1,nmax-1]

#==================================================================================================