import numpy as np
from .base import BaseActivation

class Softmax(BaseActivation):
	def __init__(self, shift=False) -> None:
		self.shift = shift
		self.shift = True
		pass

	def forward(self, Z):
		# * Great einsum explanation:
		# 	https://stackoverflow.com/questions/26089893/understanding-numpys-einsum
		if self.shift:
			# * The exponential can quickly give NaNs
			# * To avoid them we can shift the softmax by substracting it's max value
			# * The resulting array will be composed of 0 and 1, loosing precision
			#	https://eli.thegreenplace.net/2016/the-softmax-function-and-its-derivative/
			shiftZ = Z - np.max(Z)
			exp_Z = np.exp(shiftZ)
		else:
			exp_Z = np.exp(Z)
		exp_sum = 1 / np.sum(exp_Z, axis=0)
		A = np.einsum("ij,j->ij", exp_Z, exp_sum)
		return A

	def backward(self, Z):
		exp_Z = np.exp(Z)
		exp_sum = np.sum(exp_Z, axis=0)
		dA = Z - np.log(exp_sum)
		# print(f"{dA.shape = }")
		return dA

if __name__ == "__main__":
	# Shape is (Classes, Examples)
	# 	Here -> (4, 3)
	Z = np.array([[5, 2, -1, 3],
               	  [7, 2, -1, 3],
               	  [6, 2, -1, 3]]).T
	print(f"{Z.shape = }")
	print(f"{Z = }")
	A = Softmax().forward(Z)
	print(f"{A.shape = }")
	print(f"{A = }")
	dA = Softmax().backward(A)
	print(f"{dA.shape = }")
	print(f"{dA = }")


import warnings
import operator as op
import numpy as np
from functools import reduce

import sys
if sys.version_info[0] < 3:
    raise Exception("Must be using Python 3")

# n choose r
def comb(n, r):
    r = min(r, n-r)
    numer = reduce(op.mul, range(n, n-r, -1), 1)
    denom = reduce(op.mul, range(1, r+1), 1)
    return numer // denom

# convolution of two distributions/measures with support {0, 1, ..., n}
def convol(A, B):
    C = [0 for i in range(len(A)+len(B))]
    for x in range(len(A)):
        for y in range(len(B)):
            C[x+y] += A[x]*B[y]
    return C

# returns the weight enumerator of a ball of radius r in F_2^n
def weights_ball(n, r):
    return [comb(n, int(i)) for i in range(r+1)]

def volume_ball(n,r):
    return sum( weights_ball(n,r) )

# returns the weight enumerator of a fundamental ball of length n
def weights_fundamental_ball(n):
    if n%2 > 0:
        L = [comb(n, i) for i in range((n+1)//2)]
    else:
        L = [comb(n, i) for i in range((n+1)//2)] + [comb(n, (n+1)//2)//2]
    return L

# returns the weight enumerator of the SizeRed fundamental domain
def weights_fundamental_domain(profile):
    fundamental_balls = [weights_fundamental_ball(int(l)) for l in profile]
    n = sum(profile)
    k = len(profile)
    C = fundamental_balls[0]
    for i in range(1, k):
        C = convol(C, fundamental_balls[i])
    return C

# return the expected weights of visited codewords by Lee-Brickell
def weights_LB(n, k, w2):
    a,b = weights_LB_absolute(n, k, w2)
    return [x/float(b) for x in a]

# returns integer list a and integer b such that a/b = weights_LB(n,k,w2)
# prevents overflow
def weights_LB_absolute(n, k, w2):
    A = weights_ball(n-k, n-k)
    B = weights_ball(k, w2)
    return convol(A, B), 2**int(n-k)

# returns the expected weights of visited codewords by Lee-Brickell-Babai
def weights_LBB(profile, k1, w2):
    a,b = weights_LBB_absolute(profile, k1, w2)
    return [x/float(b) for x in a]

# returns integer list a and integer b such that a/b = weights_LBB(profile,k1,w2)
# prevents overflow
def weights_LBB_absolute(profile, k1, w2):
    n = sum(profile)
    k = len(profile)
    A = weights_fundamental_domain(profile[:k1])
    B = weights_ball(k - k1, w2)
    return convol(A, B), 2**int(sum(profile[:k1])-k1)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on %(date)s
@author: %(username)s
Email: jijunyu140@gmail.com
Github: https://github.com/yujijun
Description:
    This scripts is for
Input:
Output:

"""
import numpy as np

#a example for numpy array create a simple array and find all attribute
a = np.array([1,2,4])
type(a)
a.shape
a.ndim
a.size
a.itemsize
a.dtype

#array create
a = np.array([1,2,3])
b = np.array((1,2,3))
c = np.array(((1,2),(3,4)))

d = np.zeros((2,3))
e = np.ones((2,3))
e = np.eye((3))
f = np.arange(12).reshape(3,4)
f_1 = np.arange(0,12,3)
g = np.linspace(0,2,5) #better, you can konw the number of sequence
h = np.random.random((2,3))

#basic operation
a = np.array([1,2,3,4])
b = np.arange(0,4,1)

a + b
a -b
a**2
a/b

a = np.arange(4).reshape(2,2)
b = np.arange(2,6).reshape(2,2)
a * b
a @ b
a.dot(b)

np.exp(a)
np.sqrt(b)
#compare data # when you are use the function, you need to include bracket
a = np.random.random((2,3))
a
a.min()
a.max()
a.sum()

a.min(axis=0)
a.min(axis=1)
a.cumsum(axis=1)

#reshape
a = np.random.random((2,6))
a.ravel()
a.reshape(3,4)
a.resize(3,4)

#copy (always the same(data and shape))
a = np.arange(12)
b = a
b[3] = 100
a

#view and shallow copy(just data is the same,shape could be different)
a = np.arange(12).reshape(3,4)
b = a.view()
b.shape = 2,6
a.shape
b[0,0] = 200
a

#deep copy (a and b is completely different)
a
b = a.copy()
b.shape = 2,6
b[:,2] = 10
a

#stacking together different arrays
a = np.floor(10*np.random.random((2,2)))
b = np.arange(4).reshape(2,2)
np.hstack((a,b))
np.vstack((a,b))
np.r_[1:4,3,4]
np.c_[1:4,2:5]

#fancy index
#(1)array can be indexed by arrays of integers and arrays of booleans

a = np.arange(10)**3
a[0]
a[1:3]
a[::-1]
a[:6:2]
a[[0,0,1]] = [1,2,3] #last one will be replaced
a = np.arange(12)**2
i = np.array([1,2,2,8,5])
a[i] # order replication

j = np.array([[3,4],[9,7]])
a[j] #the same shape as j

#For example:
palette = np.array([[0,0,0],
                    [255,0,0],
                    [0,255,0],
                    [0,0,255],
                    [255,255,255]])
image = np.array([[0,1,2,0],
                  [0,3,4,0]])
palette[image]

#(2) two array
a = np.arange(12).reshape(3,4)
i = np.array([[0,1],
              [1,2]])
j = np.array([[2,1],
              [3,3]])
a[i,j]
l = [i,j]
a[l]
a[i,2]
a[:,j]

#(3)boolean
a = np.arange(12).reshape(3,4)
b1 = np.array([False,True,False])
a[b1,:]
a[b1]

#(4)how about 0/1
b1_1 = np.array([0,1,0])
b1_2 = np.array([0,1,1])
a[b1_1,:]
a[b1_2,:]

#(5)
# Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
import matplotlib.pyplot as plt
mu, sigma = 2, 0.5
v = np.random.normal(mu, sigma, 10000)
plt.hist(v,bins=50,density=1)
plt.show()

#compute the histogram with numpy and them plot it
(n,bins) = np.histogram(v,bins=50,density=True)
plt.plot(.5*(bins[1:]+bins[:-1]),n)
plt.show()


import numpy as np
from scipy.integrate import ode
import pylab as pl

# set equation of motion for harmonic oscillator
def dxdt(t,y,coefficient_matrix):
    r"""Harmonic oscillator:
        m d^2x/dt^2 = -k x

        in momentum space:
        dx/dt = v
        dv/dt = (-k/m) x

        as vector equation
         d   / x \     / 0  -k/m \   / x \    dy
        --- |     | = |           | |     | = --
        d t  \ v /     \ 1    0  /   \ v /    dt
    """

    return coefficient_matrix.dot( y )

# set parameters of harmonic oscillator problem
m = 1.0 # mass
k = 1.0 # spring constant
coefficient_matrix = np.array([ [0,1], [-k/m,0]])

# initial values
x_0 = 0 # intial position
v_0 = 1 # initial momentum
t_0 = 0 # intitial time

# initial y-vector from initial position and momentum
y0 = np.array([x_0,v_0])

# initialize integrator
r = ode(dxdt)

# Runge-Kutta with step size control
r.set_integrator('dopri5')

# set initial values
r.set_initial_value(y0,t_0)

# set coefficient matrix to pass to dx/dt
r.set_f_params(coefficient_matrix)

# max value of time and points in time to integrate to
t_max = 10
N_spacing_in_t = 100

# create vector of time points you want to evaluate
t = np.linspace(t_0,t_max,N_spacing_in_t)

# create vector of positions for those times
x_result = np.zeros_like(t)

# loop through all demanded time points
for it, t_ in enumerate(t):

    # get result of ODE integration
    y = r.integrate(t_)

    # write result to result vector
    x_result[it] = y[0]

# plot result
pl.plot(t,x_result)
pl.xlabel('time $t$')
pl.ylabel('position $x$')

# ===================== ANOTHER WAY OF DOING THIS =========================

# set equation of motion for harmonic oscillator
def dxdt(t,y,k,m):
    r"""Harmonic oscillator:
        m d^2x/dt^2 = -k x

        in momentum space:
        dx/dt = v
        dv/dt = (-k/m) x

        as vector equation
         d   / x \     / 0  -k/m \   / x \    dy
        --- |     | = |           | |     | = --
        d t  \ v /     \ 1    0  /   \ v /    dt
    """

    result = np.zeros_like(y)
    result[0] = -k/m * y[1]
    result[1] = y[0]

    return coefficient_matrix.dot( y )

# set parameters of harmonic oscillator problem
m = 1.0 # mass
k = 1.0 # spring constant

# initial values
x_0 = 0 # intial position
v_0 = 1 # initial momentum
t_0 = 0 # intitial time

# initial y-vector from initial position and momentum
y0 = np.array([x_0,v_0])

# initialize integrator
r = ode(dxdt)

# Runge-Kutta with step size control
r.set_integrator('dopri5')

# set initial values
r.set_initial_value(y0,t_0)

# set k, m to pass to dx/dt
r.set_f_params(k,m)

# max value of time and points in time to integrate to
t_max = 10
N_spacing_in_t = 100

# create vector of time points you want to evaluate
t = np.linspace(t_0,t_max,N_spacing_in_t)

# create vector of positions for those times
x_result = np.zeros_like(t)

# loop through all demanded time points
for it, t_ in enumerate(t):

    # get result of ODE integration
    y = r.integrate(t_)

    # write result to result vector
    x_result[it] = y[0]

# plot result
pl.plot(t,x_result,'--',lw=3)
pl.xlabel('time $t$')
pl.ylabel('position $x$')

pl.show()


from scipy import stats
import numpy as np
import math

# This method computes entropy for information gain
def entropy(class_y):
    # Input:
    #   class_y         : list of class labels (0's and 1's)

    # TODO: Compute the entropy for a list of classes
    #
    # Example:
    #    entropy([0,0,0,1,1,1,1,1,1]) = 0.92
    ones = sum(class_y)
    zeros = len(class_y)-ones
    p1 = float(ones) / len(class_y)
    p0 = float(zeros) / len(class_y)
    if p1 == 1 or p0 == 1:
        entropy = 0
    else:
        entropy = - p1*math.log(p1, 2) - p0*math.log(p0, 2)
    return entropy

def partition_classes(X, y, split_attribute, split_val):
    # Inputs:
    #   X               : data containing all attributes
    #   y               : labels
    #   split_attribute : column index of the attribute to split on
    #   split_val       : either a numerical or categorical value to divide the split_attribute

    # TODO: Partition the data(X) and labels(y) based on the split value - BINARY SPLIT.
    #
    # You will have to first check if the split attribute is numerical or categorical
    # If the split attribute is numeric, split_val should be a numerical value
    # For example, your split_val could be the mean of the values of split_attribute
    # If the split attribute is categorical, split_val should be one of the categories.
    #
    # You can perform the partition in the following way
    # Numeric Split Attribute:
    #   Split the data X into two lists(X_left and X_right) where the first list has all
    #   the rows where the split attribute is less than or equal to the split value, and the
    #   second list has all the rows where the split attribute is greater than the split
    #   value. Also create two lists(y_left and y_right) with the corresponding y labels.
    #
    # Categorical Split Attribute:
    #   Split the data X into two lists(X_left and X_right) where the first list has all
    #   the rows where the split attribute is equal to the split value, and the second list
    #   has all the rows where the split attribute is not equal to the split value.
    #   Also create two lists(y_left and y_right) with the corresponding y labels.

    '''
    Example:

    X = [[3, 'aa', 10],                 y = [1,
         [1, 'bb', 22],                      1,
         [2, 'cc', 28],                      0,
         [5, 'bb', 32],                      0,
         [4, 'cc', 32]]                      1]

    Here, columns 0 and 2 represent numeric attributes, while column 1 is a categorical attribute.

    Consider the case where we call the function with split_attribute = 0 and split_val = 3 (mean of column 0)
    Then we divide X into two lists - X_left, where column 0 is <= 3  and X_right, where column 0 is > 3.

    X_left = [[3, 'aa', 10],                 y_left = [1,
              [1, 'bb', 22],                           1,
              [2, 'cc', 28]]                           0]

    X_right = [[5, 'bb', 32],                y_right = [0,
               [4, 'cc', 32]]                           1]

    Consider another case where we call the function with split_attribute = 1 and split_val = 'bb'
    Then we divide X into two lists, one where column 1 is 'bb', and the other where it is not 'bb'.

    X_left = [[1, 'bb', 22],                 y_left = [1,
              [5, 'bb', 32]]                           0]

    X_right = [[3, 'aa', 10],                y_right = [1,
               [2, 'cc', 28],                           0,
               [4, 'cc', 32]]                           1]

    '''
    X_left = []
    X_right = []

    y_left = []
    y_right = []

    if isinstance(split_val, int) or isinstance(split_val,float):
        for i in range(len(X)):
            compare = X[i][split_attribute] if isinstance(X[i],list) else X[i]
            if compare<=split_val:
                X_left.append(X[i])
                y_left.append(y[i])
            else:
                X_right.append(X[i])
                y_right.append(y[i])
    else:
        for i in range(len(X)):
            compare = X[i][split_attribute] if isinstance(X[i],list) else X[i]
            if compare==split_val:
                X_left.append(X[i])
                y_left.append(y[i])
            else:
                X_right.append(X[i])
                y_right.append(y[i])

    return (X_left, X_right, y_left, y_right)

def information_gain(previous_y, current_y):
    # Inputs:
    #   previous_y: the distribution of original labels (0's and 1's)
    #   current_y:  the distribution of labels after splitting based on a particular
    #               split attribute and split value

    # TODO: Compute and return the information gain from partitioning the previous_y labels
    # into the current_y labels.
    # You will need to use the entropy function above to compute information gain
    # Reference: http://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15381-s06/www/DTs.pdf

    """
    Example:

    previous_y = [0,0,0,1,1,1]
    current_y = [[0,0], [1,1,1,0]]

    info_gain = 0.45915
    """
    pre = entropy(previous_y)
    cur = 0
    for row in current_y:
        if len(row) > 0:
            cur += float(len(row))/len(previous_y) * entropy(row)
    return pre - cur


# -*- coding: utf-8 -*-

import numpy as np
x = np.array([
    [0, 3, 4],
    [1, 6, 4]])
#默认参数ord=None，axis=None，keepdims=False
print("默认参数（矩阵整体元素平方和开根号，不保留矩阵二维特性）：",np.linalg.norm(x))
print("矩阵整体元素平方和开根号，保留矩阵二维特性：",np.linalg.norm(x,keepdims=True))
print("矩阵每个行向量求向量的2范数：",np.linalg.norm(x,axis=1,keepdims=True))
print("矩阵每个列向量求向量的2范数：",np.linalg.norm(x,axis=0,keepdims=True))
print("矩阵1范数：",np.linalg.norm(x,ord=1,keepdims=True))
print("矩阵2范数：",np.linalg.norm(x,ord=2,keepdims=True))
print("矩阵∞范数：",np.linalg.norm(x,ord=np.inf,keepdims=True))
print("矩阵每个行向量求向量的1范数：",np.linalg.norm(x,ord=1,axis=1,keepdims=True))


import math
from numpy import sign, linspace
import matplotlib.pyplot as plt
"""

Find root for f(x) = x^3 - 10x^2 + 5 = 0 within the range (0.6, 0.8)
via Ridder's Method

"""
def ridder(f, a, b, stop_criterion):
    fa = f(a)

    if fa == 0.0:
        return a

    fb = f(b)

    if fb == 0.0:
        return b

    if sign(f2) is not sign(f3):
        x1 = x3
        f1 = f3

    for i in range(100):  # Compute the improved root x from Ridder’s formula
        c = 0.5*(a + b)
        fc = f(c)
        s = math.sqrt(fc**2 - fa*fb)

        if s == 0.0:
            return None

        dx = (c - a) * fc / s

        if (fa - fb) < 0.0:
            dx = -dx

        x = c + dx
        fx = f(x)

        if i > 0:  # test for convergence
            if abs(x - xOld) < stop_criterion * max(abs(x), 1.0):
                return x

        xOld = x

        if sign(fc) == sign(fx):  # rebracket the root
            if sign(fa) is not sign(fx):
                b = x
                fb = fx

            else:
                a = x
                fa = fx

        else:
            a = c
            b = x
            fa = fc
            fb = fx

    print('Too many iterations')
    return None

x_ax = linspace(0.6, 0.8, 100)

f = lambda x: x ** 3 - 10 * x ** 2 + 5

x1 = 0.6
x2 = 0.8
x3 = 0.7

f1 = f(x1)
f2 = f(x2)
f3 = f(x3)

root = ridder(f, 0.6, 0.8, 1.0e-10)

poly_func, = plt.plot(x_ax, f(x_ax), 'r--')
poly_func.set_antialiased(False)  # turn off antialising
plt.setp(poly_func, 'linewidth', 2.0)

ridder_root, = plt.plot([root], [0], marker='o', label='root')
plt.setp(ridder_root, 'color', 'g', 'linewidth', 2.0)

plt.title('Ridder Roots Solver')
plt.xlabel('X Values')
plt.ylabel('Y Values')

plt.legend((poly_func, ridder_root), ('f(x) = x^3 - 10x^2 + 5', 'Ridder root @ ' + str(round(root, 6))))

plt.grid(True)
plt.show()


from timeit import timeit
import matplotlib.pyplot as plt
from numpy.random import randint

import math

def merge_sort(array: list):
    if len(array) > 1:
        mid = math.ceil(len(array) / 2)
        l = array[:mid]
        r = array[mid:]

        merge_sort(l)
        merge_sort(r)

        i, j, k = 0, 0, 0
        while i < len(l) and j < len(r):
            if l[i] < r[j]:
                array[k] = l[i]
                i += 1
            else:
                array[k] = r[j]
                j += 1
            k += 1
        while i < len(l):
            array[k] = l[i]
            i += 1
            k += 1

        while j < len(r):
            array[k] = r[j]
            j += 1
            k += 1
    return array

if __name__ == '__main__':
    arr = [2, 57, 68, 21, 55, 99, 1, 25, 28, 5, 9, 100000, 503]
    print(f"original: {arr}")
    print(f"sorted: {merge_sort(arr)}")

    maxkey = (2 ^ 32) - 1

    # Add code to do the rest of this problem
    sizes: list = []
    runtimes: list = []
    gradients: list = []

    ns = [2 ** t for t in range(1, 20)]

    for n in ns:
        print(f"Generating and sorting data with n = {n}")
        data = randint(maxkey, size=n).tolist()
        # print(data)
        sizes.append(n)
        runtimes.append(timeit("merge_sort(data)",
                               number=10, globals=globals()))
        gradients.append(runtimes[-1] / sizes[-1])

    plt.subplot(121)
    plt.grid(True, which="both")
    plt.title("MERGE SORT")
    plt.xlabel("Size of array")
    plt.ylabel("Runtime")
    plt.plot(sizes, runtimes, "--ro")

    plt.subplot(122)
    plt.grid(True, which="both")
    plt.xlabel("Size of array")
    plt.ylabel("Gradient")
    plt.plot(sizes, gradients, "--b+")
    plt.savefig('./output/merge-sort.png', dpi=300, transparent=False)
    plt.show()
    print("\nFINISHED.\n")


import numpy as np

def reduce_dim(examples, explained_var_ratio=0.85, debug=False):
    X = examples - np.mean(examples, axis=0)
    eig_vals, eig_vecs = np.linalg.eig(np.cov(X.T))

    idx = np.argsort(eig_vals)[::-1]
    eig_vals = eig_vals[idx]
    eig_vecs = eig_vecs[idx]

    var_sum = np.sum(eig_vals)
    var_cumsum = 0
    n_components = 0
    for eig_val in eig_vals:
        var_cumsum += eig_val
        n_components += 1
        if var_cumsum >= var_sum * explained_var_ratio:
            if debug:
                print '{} components selected.'.format(n_components)
                print '{:.2%} variance explained.'.format(var_cumsum / var_sum)
            break

    W = eig_vecs[:n_components].T
    return np.dot(X, W)


#SYDE 675
#Matthew Cann
#20863891
#Assignment 1 Question 3

#.......................................................................IMPORTS
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import mean_squared_error
import os
import cv2
import time

#..............................................................................
Directory = os.getcwd()

#.....................................................................CONSTANTS
pixels_num = 784 #28x28

#.....................................................................DATA PREP
(images_train, labels_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
image_index = 7777 # You may select anything up to 60,000
image_index = 12000 # You may select anything up to 60,000

#Each image is Vectoized.......
#Current shape is 60000, 28x28, I want 60000, 784x1
images_train = images_train.reshape(images_train.shape[0], pixels_num)
X_data = images_train.T # Matrix of size Dxn or 784x60,000

nsample = images_train.shape[0] #number of features 60,000

#%%...................................................................FUNCTIONS
def eigen_analysis(X_data,nsample):
    X = X_data #784x60,000 data
    means = X.mean(axis=1).reshape(1,784)
    X_bar = np.subtract(X, means.T)
    denom = nsample - 1
    cov = np.matmul(X_bar,X_bar.T)/denom #784x784

    w, v = np.linalg.eig(cov)
    idx = np.argsort(w)[::-1] #Sorts the eigenvalues but in decending order using [::-1]
    w = w[idx]
    v = v[:,idx]
    return w, v, X_bar, means

def PCA(v, d, X_bar):
    U_matrix = v
    U_reduced = U_matrix[:, :d]
    W = U_reduced   # mapping function as a Dxd matrix in this case (784x2)
    Y = np.matmul(W.T,X_bar) # reduced function as a dxN matirx in this case (2, 60000)
    return W, Y

def Part_A(X_data):
    w, v, X_bar, means = eigen_analysis(X_data, nsample)
    d = 784
    W, Y = PCA(v, d, X_bar)
    return Y

def suitable_d(POV_all, w):
    '''Determines the suitable number of eigen values(w) to achieve the
    specific proportion of variencce (POV)'''

    total = np.sum(w).real # Summation of all the eigen values.
    count = 0 #Sum of eigen values initialized to zero
    for i in range(len(w)):
        count += w[i].real #Adds the eigen value of index i to the count.
        POV = count/total # Calculates POV
        if POV >= POV_all:
            # When the POV is equal or greater than the POV allowable it saves the value and d and breaks loop.
            POV_final = POV
            d_final = i + 1 #since i starts at 0
            break
    print('The suitable d proposed to achieve a proportion of variance POV = 95% is {} resulting in a POV of {:0.2f}%'.format(d_final,POV_final*100)    )
    return
def PCA_reconstruction(W,Y, means):
    '''PCA reconstruction takes the reduced Y(dxN) and returns the reconstructed X(DxN)'''
    X_recon = (np.dot(W, Y) + means.T).real #Reconstruts the images as a DxN matrix
    #recon = recon.T.real
    return X_recon

def z_end_script():
    print ('\nProgrammed by Matt Cann \nDate: ',\
    time.ctime(),' \nEnd of processing''\n'  )
    return

#%%........................................................................MAIN

#%%PARTA.......................................................................
'''PCA that takes X(DxN) and returns Y (dxN)'''
Y = Part_A(X_data)

#%%PARTB.......................................................................
'''Determines a suitable d using proportion of variance (POV) =95%'''
w, v, X_bar, means = eigen_analysis(X_data, nsample)
POV_all = 0.95

suitable_d(POV_all, w)
#%%PARTC.......................................................................
'''PCA reconstruction takes the reduced Y(dxN) and returns the reconstructed X(DxN)
then applies to different values of d and average mean square error is calculated and plotetd'''

# Make list of different d values of {1, 20, 40, 60, 80, …, 760, 784}
d_range = list(range(0, len(w), 20))[1:-1]
d_range.insert(0,1)
d_range.append(len(w))

w, v, X_bar, means= eigen_analysis(X_data, nsample)
MSE = []
for d in d_range:
    W,Y = PCA(v,d, X_bar)
    X_recon = PCA_reconstruction(W,Y, means)
    X_recon = X_recon.T # X_recon is DxN matrix, converts to NxD for easy index
    MSE.append( mean_squared_error(X_data.T, X_recon)) # Calculated the meansquared error

fig = plt.figure(20)
plt.plot(d_range,MSE)
plt.xlabel('Reconstructed d')
plt.ylabel('Mean Squared Error')
plt.savefig(Directory +'\partc2.png',bbox_inches="tight")
plt.show()

#%%PARTD.......................................................................
'''Reconstruct a sample from the class of number ‘8’ and show it as a ‘png’ image for d=
{1, 10, 50, 250, 784}.'''

index_8 = np.where(labels_train == 8)[0][0] # Finds the first index of the number 8.

w, v, X_bar, means = eigen_analysis(X_data, nsample) # Re calc eigen values

original_image = images_train[index_8].reshape(28,28)
plt.imshow(original_image, cmap='Greys') # Show and save orginal image.
plt.imsave(Directory +'orginal.png', original_image, cmap='Greys')

for d in [1,10,50,250,784]:
    W,Y = PCA(v,d, X_bar)
    X_recon = PCA_reconstruction(W,Y, means)
    X_recon = X_recon.T # X_recon is DxN matrix, converts to NxD for easy index
    image = X_recon[index_8].reshape(28, 28)
    plt.figure(3)
    plt.imshow(image, cmap='Greys')
    #plt.imsave(Directory +'test.png', image, cmap='Greys')
    #cv2.imwrite('test.png', image, cmap='Greys')
    plt.show()

#%%PART E......................................................................
'''For the values of d= {1, 2, 3, 4, …, 784} plot eigenvalues (y-axis) versus d (x-axis).'''

d_range = list(range(0, len(w)+1, 1))[1:]

plt.figure(3)
plt.plot(d_range,w.real)
plt.xlabel('Values d')
plt.ylabel('Eigenvalues')
plt.savefig(Directory +'Eigen.png')
plt.show()

print ('\nProgrammed by Matthew Cann\nDate: ',\
    time.ctime(),' \nEnd of processing''\n'  )

z_end_script()


from deepnumpy.loss._loss import Loss
import numpy as np

class MeanSquaredError(Loss):

    def __init__(self, predicted_value, actual_value):
        super().__init__()
        self.predicted_value = predicted_value
        self.actual_value = actual_value
        self.type = 'Mean Squared Error'

    def forward(self):
        '''
        Loss = mean((Yp - Ya)^2)
        '''
        return np.power(self.predicted_value - self.actual_value, 2).mean()

    def backward(self):
        '''
        dLoss = 2*(Yp - Ya)
        '''
        return 2 * (self.predicted_value - self.actual_value).mean()


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

data = pd.read_csv('ex1data1.txt', header = None) #read from dataset
X = data.iloc[:,0] # read first column
y = data.iloc[:,1] # read second column
m = len(y) # number of training example
data.head() # view first few rows of the data

plt.scatter(X, y)
plt.xlabel('Population of City in 10,000s')
plt.ylabel('Profit in $10,000s')
#plt.show()

X = X[:,np.newaxis]
y = y[:,np.newaxis]
theta = np.zeros([2,1])
iterations = 15000
alpha = 0.001
ones = np.ones((m,1))
X = np.hstack((ones, X)) # adding the intercept term
#print(X)

def computeCostMulti(X, y, theta):
    temp = np.dot(X, theta) -y
    return np.sum(np.power(temp, 2)) / (2*m)
J = computeCostMulti(X, y, theta)
print(J)

def gradientDescent(X, y, theta, alpha, iterations):
    for i in range(iterations):
        temp = np.dot(X, theta) - y #this code is or*asm ! wow fuckin python lol
        temp = np.dot(X.T, temp)
        theta = theta - (alpha/m) * temp
        print("i'th train:",i,computeCostMulti(X,y,theta))
    return theta
theta = gradientDescent(X, y, theta, alpha, iterations)
print(theta)

plt.scatter(X[:,1], y)
plt.xlabel('Population of City in 10,000s')
plt.ylabel('Profit in $10,000s')
plt.plot(X[:,1], np.dot(X, theta))
#plt.show()

print("What would be for let say 5000 city people profit? \n")
myfunc = np.ones([2,1])
#myfunc[0][0] = 5000
#a = np.sum(np.dot(theta,myfunc))
print(theta[0]+theta[1]*5000)


import numpy as np
from matplotlib import pyplot as plt
from matplotlib import widgets as wg
from scipy.misc import comb
from numpy.random import rand

# Decasteljau's algorithm problem
def decasteljau(p,t):
    """ Evaluates a Bezier curve with control points 'p' at time 't'.
    The points in 'p' are assumed to be stored in its rows.
    'p' is assumed to be 2-dimensional."""
    n = p.shape[0]
    m = p.shape[1]
    q1 = p
    q2 = np.zeros((n, m))
    for i in xrange(n-1, 0, -1):
        q2 = np.zeros((i, m))
        for k in xrange(i):
            q2[k] = (1-t) * q1[k] + t * q1[k+1]
        q1 = q2
    return q2[0]

# used in interactive plot problem.
def decasteljau_animated(p, t, res=401):
    """ Produces a plot illustrating the Decasteljau algorithm
    at a given time 't'."""
    n = p.shape[0]
    plt.plot(p[:,0], p[:,1])
    plt.scatter(p[:,0], p[:,1])
    q1 = p
    q2 = np.zeros((n, 2))
    for i in xrange(n-1, 0, -1):
        q2 = np.zeros((i, 2))
        for k in xrange(i):
            q2[k] = (1-t) * q1[k] + t * q1[k+1]
        plt.plot(q2[:,0], q2[:,1])
        plt.scatter(q2[:,0], q2[:,1])
        q1 = q2
    # This line double-plots a point, but it will probably
    # be less costly to replot the last point than it
    # is to check on each iteration.
    plt.scatter(q2[:,0], q2[:,1], color='r', linewidth=3)
    t = np.linspace(0, 1, res)
    X = np.array([decasteljau(p,i) for i in t])
    plt.plot(X[:,0], X[:,1])
    pass

# interactive plot problem.
def decasteljau_interactive(n):
    """ Makes an interactive plot using a slider bar to show the
    Decasteljau algorithm at different times."""
    ax = plt.subplot(1, 1, 1)
    plt.subplots_adjust(bottom=0.25)
    plt.axis([-1, 1, -1, 1])
    axT = plt.axes([0.25, 0.1, 0.65, 0.03])
    sT = wg.Slider(axT, 't', 0, 1, valinit=0)
    pts = plt.ginput(n, timeout=240)
    plt.subplot(1, 1, 1)
    pts = np.array(pts)
    plt.cla()
    T0=0
    decasteljau_animated(pts, T0)
    plt.axis([-1, 1, -1, 1])
    plt.draw()
    def update(val):
        T = sT.val
        ax.cla()
        plt.subplot(1, 1, 1)
        decasteljau_animated(pts,T)
        plt.xlim((-1, 1))
        plt.ylim((-1,1))
        plt.draw()
    sT.on_changed(update)
    plt.show()

# Bernstein polynomial problem
def bernstein(i, n):
    """ Returns the 'i'th Bernstein polynomial of degree 'n'."""
    return comb(n, i, exact=True) * (np.poly1d([-1,1]))**(n-i) * (np.poly1d([1,0]))**i

# Coordinate function problem.
def bernstein_pt_aprox(X):
    """ Returns the 'x' and 'y' coordinate functions for a 2-dimensional
    Bezier curve with control points 'X'."""
    n = X.shape[0]
    xpoly = np.poly1d([0])
    ypoly = np.poly1d([0])
    for i in xrange(n):
        npoly = bernstein(i, n-1)
        xpoly += X[i,0] * npoly
        ypoly += X[i,1] * npoly
    return xpoly, ypoly

# plot demonstrating numerical instability in Bernstein polys.
def compare_plot(n, res=501):
    """ Produces a plot showing a Bezier curve evaluated via
    the Decasteljau algorithm and a Bezier curve evaluated using
    Bernstein polynomials. Control points are chosen randomly.
    Instability should be evident for moderately large values of 'n'."""
    X = rand(n, 2)
    t = np.linspace(0, 1, res)
    # This is slow.
    # It would be better to have a
    # decasteljau function that supported
    # arrays of time values.
    decasteljau_curve = np.array([decasteljau(X, ti) for ti in t])
    xpoly, ypoly = bernstein_pt_aprox(X)
    plt.plot(decasteljau_curve[:,0], decasteljau_curve[:,1], xpoly(t), ypoly(t))
    plt.show()

if __name__ == '__main__':
    decasteljau_interactive(5)
    compare_plot(4)
    compare_plot(30)


"""
Empirical estimation of the truncation error in a scheme.
Examples on the Forward Euler scheme for the decay ODE u'=-au.
"""
import numpy as np
import trunc_empir

def decay_FE(dt, N):
    dt = float(dt)
    t = np.linspace(0, N*dt, N+1)
    u_e = I*np.exp(-a*t)  # exact solution, I and a are global
    u = u_e  # naming convention when writing up the scheme
    R = np.zeros(N)

    for n in range(0, N):
        R[n] = (u[n+1] - u[n])/dt + a*u[n]

    # Theoretical expression for the trunction error
    R_a = 0.5*I*(-a)**2*np.exp(-a*t)*dt

    return R, t[:-1], R_a[:-1]

if __name__ == '__main__':
    I = 1; a = 2  # global variables needed in decay_FE
    trunc_empir.estimate(decay_FE, T=2.5, N_0=6, m=4, makeplot=True)


""" Implementation of cubic spline interpolation """
import numpy as np

def PI(t):

  abt = np.abs(t)
  if (abt <= 1):
    return 4 - 6 * abt**2 + 3 * abt **3
  elif (abt <= 2):
    return (2 - abt)**3
  else:
    return 0

def pi(t):

  abt = np.abs(t)
  ab = np.zeros_like(abt)
  mask1 = np.where(abt<=1)
  #print mask1
  mask2 = np.where((abt>1)&(abt<=2))
  #print mask2
  mask3 = np.where(abt>2)
  ab[mask1] = 4 - 6*abt[mask1]**2 + 3 * abt[mask1]**3
  ab[mask2] = (2 - abt[mask2])**3

  return ab

def u(x, k, a, h):

    """ x : float, k: integer,
    a: float, h : float """

    return PI((x - a)/h - (k - 2))

def _interpolate(x, a, b, c):

    """ x : where we want to estimate the function at,
        a : lower bound of the input grid
        b : upper bound of the input grid
        c : array containing (n+3) cubic-spline coefficients
            where n is the number of spacing between
            the input grid points (meaning the number
            of input grid points is n+1)
    """

    n = c.shape[0] - 3
    h = (b - a)/n
    #print n
    l = np.int((x - a)/h) + 1
    #print l
    m = min(l + 3, n + 3)

    s = 0
    for k in xrange(l, m + 1):
        s += c[k - 1] * u(x, k, a, h)

    return s

def interpolate(x, a, b, c):

    '''
    Return interpolated function value at x

    Parameters
    ----------
    x : float
        The point where the function will be approximated at
    a : double
        Lower bound of the grid
    b : double
        Upper bound of the grid
    c : ndarray
        Coefficients of spline

    Returns
    -------
    out : float
        Approximated function value at x
    '''

    return _interpolate(x, a, b, c)

def _interpolate_2d(x, y, a1, b1, a2, b2, c):

  """
  parameters:
  (x,y) = where we want to estimate the function
  a1 , b1 = lower bounds of the grid
  a2 , b2 = upper bounds of the grid
  c = spline coefficients
  """
  n1 = c.shape[0] - 3
  n2 = c.shape[1] - 3
  h1 = (b1 - a1)/n1
  h2 = (b2 - a2)/n2

  l1 = np.int((x - a1)/h1) + 1
  l2 = np.int((y - a2)/h2) + 1
  m1 = min(l1 + 3, n1 + 3)
  m2 = min(l2 + 3, n2 + 3)

  s = 0

  for i1 in xrange(l1, m1 + 1):
    u_x = u(x, i1, a1, h1)
    for i2 in xrange(l2, m2 + 1):
      u_y = u(y, i2, a2, h2)
      s += c[i1 - 1, i2 - 1] * u_x * u_y

  return s

def interpolate_2d(x, y, a1, b1, a2, b2, c):
    '''
    Return interpolated function value at x

    Parameters
    ----------
    x, y : float
        The values where the function will be approximated at
    a1, b1 : double
        Lower and upper bounds of the grid for x
    a2, b2 : double
        Lower and upper bounds of the grid for y
    c : ndarray
        Coefficients of spline

    Returns
    -------
    out : float
        Approximated function value at (x, y)
    '''
    return _interpolate_2d(x, y, a1, b1, a2, b2, c)


import itertools
import pytest

import numpy as np

from scifin import statistics as st

class TestStatistics:
    """
    Tests the functions in statistics.py.
    """

    def test_covariance_to_correlation(self):
        # Test 1 - Arbitrary covariance matrix converted to correlation matrix
        corr0 = st.covariance_to_correlation(np.array([[1.5, -0.75], [-0.75, 1.5]]))
        corr0_correct = np.array([[1., -0.5], [-0.5,  1.]])

        for i, j in itertools.product(range(corr0.shape[0]), range(corr0.shape[0])):
            assert corr0[i, j] == pytest.approx(corr0_correct[i, j])

        # Test 2 - Random covariance matrix converted to covariance matrix
        corr1 = st.covariance_to_correlation(st.random_covariance_matrix(3, 5))

        for i, j in itertools.product(range(corr1.shape[0]), range(corr1.shape[0])):
            if i == j:
                assert corr1[i, j] == pytest.approx(1.)
            else:
                assert corr1[i, j] == corr1[j, i]

    def test_eigen_value_vector(self):
        # Test 1 - Real valued matrix
        cov1 = np.array([[1.5, -0.75], [-0.75, 1.5]])
        eval1, evec1 = st.eigen_value_vector(cov1)
        eval1_correct = np.array([[2.25, 0.], [0., 0.75]])
        evec1_correct = np.array([[-0.7071067811865475, -0.7071067811865475],
                                       [0.7071067811865475, -0.7071067811865475]])

        for i, j in itertools.product(range(cov1.shape[0]), range(cov1.shape[0])):
            assert eval1[i, j] == eval1_correct[i, j]
            assert evec1[i, j] == evec1_correct[i, j]

        # Test 2 - Complex valued matrix
        cov2 = np.array([[1,2+3.j],[2-3.j,4]])
        eval2, evec2 = st.eigen_value_vector(cov2)
        eval2_correct = np.array([[6.405124837953327, 0.0], [0.0, -1.4051248379533274]])
        evec2_correct = np.array([[-0.7071067811865475, -0.7071067811865475], [0.7071067811865475, -0.7071067811865475]])


"""Assignment - using numpy and making a PR.

The goals of this assignment are:
    * Use numpy in practice with two easy exercises.
    * Use automated tools to validate the code (`pytest` and `flake8`)
    * Submit a Pull-Request on github to practice `git`.

The two functions below are skeleton functions. The docstrings explain what
are the inputs, the outputs and the expected error. Fill the function to
complete the assignment. The code should be able to pass the test that we
wrote. To run the tests, use `pytest test_numpy_question.py` at the root of
the repo. It should say that 2 tests ran with success.

We also ask to respect the pep8 convention: https://pep8.org.
This will be enforced with `flake8`. You can check that there is no flake8
errors by calling `flake8` at the root of the repo.
"""
import numpy as np

def max_index(X):
    """Return the index of the maximum in a numpy array.

    Parameters
    ----------
    X : ndarray of shape (n_samples, n_features)
        The input array.

    Returns
    -------
    (i, j) : tuple(int)
        The row and columnd index of the maximum.

    Raises
    ------
    ValueError
        If the input is not a numpy array or
        if the shape is not 2D.
    """
    i = 0
    j = 0

    # TODO

    return i, j

def wallis_product(n_terms):
    """Implement the Wallis product to compute an approximation of pi.

    See:
    https://en.wikipedia.org/wiki/Wallis_product

    Parameters
    ----------
    n_terms : int
        Number of steps in the Wallis product. Note that `n_terms=0` will
        consider the product to be `1`.

    Returns
    -------
    pi : float
        The approximation of order `n_terms` of pi using the Wallis product.
    """
    # XXX : The n_terms is an int that corresponds to the number of
    # terms in the product. For example 10000.
    return 0.


import numpy as np
import matplotlib.pyplot as plt
from math import sin, cos, pi, sqrt

# Graph parameters
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
plt.rcParams['axes.labelsize'] = 14

# Global constants
g = 9.8  # Standard gravity value
L = 1.0  # Wire length
theta_0 = pi/3  # Initial angle,
omega_0 = 0.0  # Initial angular velocity

def main():

    # Constants
    a = 0.0  # Start the interval
    b = 10.0  # Finish the interval
    N = 1000  # Steps for numerical integration
    h = (b - a) / N  # Size of one step

    time = np.arange(a, b, h)
    theta_rk4, omega_rk4 = [], []

    r_0 = np.array([theta_0, omega_0], float)  # Initial condition F(a)
    r = r_0
    for t in time:  # Numerical integration
        theta_rk4.append(r[0])
        omega_rk4.append(r[1])
        r += Runge_Kutta4(f, r, t, h)

    # Exact solution for comparison
    theta_e = []
    omega = np.sqrt(g / L)
    a = theta_0
    b = omega_0 / omega
    for t in time:
        theta_e.append(a*cos(omega*t) + b*sin(omega*t))

    # Graphing theta
    plt.figure(figsize=(12,12))
    plt.plot(time, theta_rk4, 'r.', label='Numrical solution')
    plt.plot(time, theta_e, label='Exact solution')
    plt.legend(loc='best')
    plt.xlabel('t')
    plt.ylabel('theta(t)')
    plt.show()

    # Graphing theta and omega
    plt.figure(figsize=(12,12))
    plt.plot(time, theta_rk4, label='Theta(t)')
    plt.plot(time, omega_rk4, label='Omega(t)')
    plt.legend(loc='best')
    plt.xlabel('t')
    plt.ylabel('theta(t), omega(t)')
    plt.show()

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

def f(r, t):
    theta, omega = r[0], r[1]
    f0, f1 = omega, -g/L * sin(theta)
    return np.array([f0, f1], float)

def Runge_Kutta4(f, r, t, h):
    k1 = h * f(r, t)
    k2 = h * f(r+0.5*k1, t+0.5*h)
    k3 = h * f(r+0.5*k2, t+0.5*h)
    k4 = h * f(r+k3, t+h)
    return (k1+ 2.0*(k2+k3) + k4) / 6.0

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

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Mon Sep 17 11:40:34 2018

@author: Thiago, Renan
"""
import numpy as np
import math as m

num_passos_f3 = 0
num_passos_f4 = 0

def insert_dash(string, index):
    return string[:index] + '|' + string[index:]

### Funcao pra achar a raiz ###

def F(x):
    return x**3 + 4.0 * x**2 - 10.0

### Funcoes de ponto fixo que convergem, de acordo com a demonstracao ###

def f3(x):
    return m.sqrt(10.0 / (4.0+x))

def f4(x):
    return x - ((x**3 + 4.0*x**2 - 10.0) / (3.0 * x**2 + 8.0*x))

### Intervalo e chute inicial ###

a = np.dtype('f8')
b = np.dtype('f8')
xi = np.dtype('f8')
E = np.dtype('f8')

a = 1.0
b = 2.0
xi = 1.5 #chute inicial

ordem = 5
E = 10**-ordem

### Implementação do metodo

def pontoFixo(xi, funcao):
    _x = xi
    iteracoes = 0
    while(abs(F(_x)) > E):

        _x = funcao(_x)
        iteracoes += 1

        if(iteracoes >= 10000):
            print("saiu no break")
            break

    return [_x, iteracoes]

### Obtem os resultados

p3 = pontoFixo(xi, f3)
p4 = pontoFixo(xi, f4)

raiz_funcao3 = p3[0]
num_passos_f3 = p3[1]
raiz_funcao4 = p4[0]
num_passos_f4 = p4[1]

string_3 = repr(raiz_funcao3)
string_4 = repr(raiz_funcao4)

print("\nFuncao 3:\n")
print("Raiz:"+insert_dash(string_3, ordem + string_3.find('.') + 1)+"")
print("F(x):"+repr(F(raiz_funcao3)))
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_f3)+"")

print("\nFuncao 4:\n")
print("Raiz:"+insert_dash(string_4, ordem + string_4.find('.') + 1)+"")
print("F(x):"+repr(F(raiz_funcao4)))
print("Precisao:"+str(E)+"")
print("Num de Iteracoes:"+str(num_passos_f4)+"")


import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score

def lin_regplot(X, y, model):
    plt.scatter(X, y, c='steelblue', edgecolor='white', s=70)
    plt.plot(X, model.predict(X), color='black', lw=2)
    return None

df = pd.read_json('environment_log_data.json')

regression_pairs = {'Temp and Hum Performance': [['Output Temp', 'Real Temperature', 0], ['Output Humidity', 'Real Humidity', 0], ['Raw Temperature', 'Real Temperature', 0], ['Raw Humidity', 'Real Humidity', 0], ['Raw Bar', 'Output Bar', 0]],
                    'Gas Sensor Performance': [['Raw Temperature', 'Raw RedRS', 23], ['Raw Humidity', 'Raw RedRS', 50], ['Raw Bar', 'Raw RedRS', 1013],
                                               ['Raw Temperature', 'Raw OxiRS', 23], ['Raw Humidity', 'Raw OxiRS', 50], ['Raw Bar', 'Raw OxiRS', 1013],
                                               ['Raw Temperature', 'Raw NH3RS', 23], ['Raw Humidity', 'Raw NH3RS', 50], ['Raw Bar', 'Raw NH3RS', 1013]]}
regression_summary = {performance_type:{} for performance_type in regression_pairs}
print(regression_summary)

for performance_type in regression_pairs:
    for pair in range(len(regression_pairs[performance_type])):
        X = df[[regression_pairs[performance_type][pair][0]]].values - regression_pairs[performance_type][pair][2]
        y = df[[regression_pairs[performance_type][pair][1]]].values
        regr = LinearRegression()
        quadratic = PolynomialFeatures(degree=2)
        cubic = PolynomialFeatures(degree=3)
        X_quad = quadratic.fit_transform(X)
        X_cubic = cubic.fit_transform(X)
        X_fit = np.arange(X.min(), X.max(), 1) [:, np.newaxis]
        regr = regr.fit(X, y)
        y_lin_fit = regr.predict(X_fit)
        linear_r2 = r2_score(y, regr.predict(X))
        print('')
        print(regression_pairs[performance_type][pair][0] + ' and ' + regression_pairs[performance_type][pair][1])
        print('Slope: %.4f' % regr.coef_[0])
        print('Intercept: %.4f' % regr.intercept_)
        print('R2: %.2f' % linear_r2)
        regression_summary[performance_type][regression_pairs[performance_type][pair][0] + ' ' + regression_pairs[performance_type][pair][1]] = {'Slope': regr.coef_[0], 'Intercept': regr.intercept_, 'R2': linear_r2}
        regr = regr.fit(X_quad, y)
        y_quad_fit = regr.predict(quadratic.fit_transform(X_fit))
        reg_label = "Inliers coef:%s - b:%0.4f" % \
                    (np.array2string(regr.coef_,
                                     formatter={'float_kind': lambda fk: "%.4f" % fk}),
                    regr.intercept_)
        print('Quad Fit', reg_label)
        quadratic_r2 = r2_score(y, regr.predict(X_quad))
        print('R2: %.2f' % quadratic_r2)
        regr = regr.fit(X_cubic, y)
        y_cubic_fit = regr.predict(cubic.fit_transform(X_fit))
        reg_label = "Inliers coef:%s - b:%0.5f" % \
                    (np.array2string(regr.coef_,
                                     formatter={'float_kind': lambda fk: "%.5f" % fk}),
                    regr.intercept_)
        print('Cubic Fit', reg_label)
        cubic_r2 = r2_score(y, regr.predict(X_cubic))
        print('R2: %.2f' % cubic_r2)
        plt.scatter(X, y, label='training points', color='lightgray')
        plt.plot(X_fit, y_lin_fit, label='linear (d=1), $R^2=%.2f$' % linear_r2, color='blue', lw=2, linestyle=':')
        plt.plot(X_fit, y_quad_fit, label='quadratic (d=2), $R^2=%.2f$' % quadratic_r2, color='red', lw=2, linestyle='-')
        plt.plot(X_fit, y_cubic_fit, label='cubic (d=3), $R^2=%.2f$' % cubic_r2, color='green', lw=2, linestyle='--')
        plt.xlabel(regression_pairs[performance_type][pair][0])
        plt.ylabel(regression_pairs[performance_type][pair][1])
        plt.legend(loc='lower right')
        plt.show()

for pt in regression_summary:
    print('')
    print(pt, 'Summary')
    for pair in (regression_summary[pt]):
        print(pair, regression_summary[pt][pair])

# Acknowledgement: Thanks to https://www.packtpub.com/au/data/python-machine-learning-third-edition


import numpy as np
import theano
import theano.tensor as T
import math

# library with theano PDF functions

c = - 0.5 * math.log(2*math.pi)

def normal(x, mean, sd):
	return c - T.log(T.abs_(sd)) - (x - mean)**2 / (2 * sd**2)

def normal2(x, mean, logvar):
	return c - logvar/2 - (x - mean)**2 / (2 * T.exp(logvar))

def standard_normal(x):
	return c - x**2 / 2


import numba
import numpy as np

class Math:
    @staticmethod
    @numba.njit
    def euclidean(x_matrix: np.ndarray, y_matrix: np.ndarray) -> np.ndarray:
        """
        Computes the euclidean distance between all vectors in `x_matrix` and all vectors in `y_matrix`.
        :param x_matrix: A matrix of size (number of vectors, number of dimensions).
        :param y_matrix: A matrix of size (number of vectors, number of dimensions).
        :return: A matrix of size (number of vectors in `x_matrix`, number of vectors in `y_matrix`).
        """
        distances = np.empty((x_matrix.shape[0], y_matrix.shape[0]), dtype=x_matrix.dtype)

        for index in range(x_matrix.shape[0]):
            distances[index, :] = np.sqrt(np.sum(np.square(y_matrix - x_matrix[index]), axis=1))

        return distances

    @staticmethod
    @numba.njit
    def subtractions(x_matrix: np.ndarray, y_matrix: np.ndarray) -> np.ndarray:
        """
        Computes the x - y for all vectors between `x_matrix` and `y_matrix`.
        :param x_matrix: A matrix of size (number of vectors, number of dimensions).
        :param y_matrix: A matrix of size (number of vectors, number of dimensions).
        :return: A tensor of size (number of vectors in `x_matrix`, number of vectors in `y_matrix`, number of dimensions).
        """
        subtractions = np.empty((x_matrix.shape[0], y_matrix.shape[0], x_matrix.shape[1]), dtype=x_matrix.dtype)

        for index in range(x_matrix.shape[0]):
            subtractions[index, :, :] = x_matrix[index] - y_matrix

        return subtractions


import numpy as np
import scipy.special

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

class Layer:
    def __init__(self, number_of_features, activation_units, layer_type):
        # init_epsilon = [[-sys.float_info.epsilon, sys.float_info.epsilon]]
        self.weight = (np.random.rand(activation_units, number_of_features))#  @ init_epsilon) - init_epsilon
        # self.weight = np.random.normal(0.0, pow(number_of_features, -0.5), (activation_units, number_of_features))
        # self.bias = np.ones(activation_units)
        self.bias = np.random.rand(activation_units, 1)
        self.activation_function = scipy.special.expit #sigmoid
        self.type = layer_type

    # /!\ Matrix order is very important here
    def z(self, x):
        return (self.weight @ x) +  self.bias

    def feedforward(self, x):
        self.h = self.activation_function(self.z(x))
        return self.h

class MultilayerPerceptron:
    def __init__(self, layers, lr=0.1):
        self.lr = lr
        self.layers=layers

    # def loss(self, p, y):
    #     return -((y @ np.log(p) + (1 - y) @ np.log(1 - p)) / y.shape[0])

    def forward_propagation(self, x):
        for i, layer in enumerate(self.layers):
            x = layer.feedforward(x)
        return x

    def backward_propagation(self, inputs, targets):
        fw = self.forward_propagation(inputs)
        error = targets - fw

        t = [self.layers[0].h, inputs]
        for i, layer in enumerate(self.layers[::-1]):
            gradient = error * (layer.h * (1.0 - layer.h))
            error = layer.weight.T @ error
            layer.weight += self.lr * (gradient @ t[i].T)
            layer.bias += (self.lr * gradient)


#coding = utf8
import numpy as np
import pandas as pd
class LinearRegression():
 	def __init__(self,alpha=1,epsilon=0.001,maxIteration=1000):
 		self.alpha = float(alpha)
 		self.epsilon = float(epsilon)
 		self.maxIteration = int(maxIteration)

 	def fit(self, X_,y):
 		X =  np.hstack(np.ones((X_.shape[0],1)),X_)
 		m,n = X.shape
 		self.theta = np.zeros((n,1))
 		for i in range(maxIteration):
 			Dtheta = X.T @ ( X @ self.theta-y)/m
 			self.theta -= alpha*Dtheta
 			if i % 50 ==0:
 				square_loss = np.sum(np.square(X @ self.theta -y))/2.0/m
 				print("the {} iteration: the loss is ")
 			if np.linalg.norm(Dtheta) <= self.epsilon:
 				break
 	def predict(self,X_):
 		X = np.hstack(np.ones(X_.shape[0],1),X_)
 		return X @ self.theta


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from numpy.linalg import *

def fit_linear_regression(X: np.array, y: np.array) -> (np.array, np.array):
    """
    Calculates coefficients vector w given design matrix X (data) and response vector y (labels)
    :param X: p*n matrix
    :param y: n*1 vector
    :return: tuple of coefficients vector w (p*1) and singular values of X
    """
    w = np.linalg.pinv(X).T @ y
    s = svd(X, compute_uv=False)
    return w,s

def q20(df: pd.DataFrame):
    """
    Plots the graphs for question 20
    :param df:
    :return:
    """
    x_values = df['day_num']
    y_values1 = df['log_detected']
    y_values2 = df['detected']

    # Log graph
    plt.scatter(x_values, y_values1)
    plt.plot(x_values, X.dot(w))
    plt.xlabel('day_num')
    plt.ylabel('log_detected')
    plt.title('log_detected as function of day')
    plt.show()

    # Exponential grpah
    plt.scatter(x_values, y_values2)
    plt.plot(x_values, np.exp(X.dot(w)))
    plt.xlabel('day_num')
    plt.ylabel('detected')
    plt.title('detected as function of day')
    plt.show()

if __name__ == '__main__':
    # Q18
    df = pd.read_csv("C:\\Users\\anton\\PycharmProjects\\EX2\\covid19_israel.csv")

    # Q19
    df['log_detected'] = np.log(df['detected'])

    # Q20
    X = pd.DataFrame(df['day_num'].values.T)
    X['bias'] = 1
    y = df['log_detected']
    w, s = fit_linear_regression(X.T, y)

    # Q21
    q20(df)


# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.13.0
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# # !pip install cvxopt
import cvxopt
import cvxpy as cp

# %%
import numpy as np
import pandas as pd

# %% [markdown]
# # Example 1

# %%
# Reproduce the example from `https://www.cvxpy.org/index.html`.

# Problem data.
m = 30
n = 20
np.random.seed(1)
A = np.random.randn(m, n)
b = np.random.randn(m)

# Construct the problem.
x = cp.Variable(n)
objective = cp.Minimize(cp.sum_squares(A @ x - b))
constraints = [0 <= x, x <= 1]
prob = cp.Problem(objective, constraints)

# The optimal objective value is returned by `prob.solve()`.
result = prob.solve()
# The optimal value for x is stored in `x.value`.
print(x.value)
# The optimal Lagrange multiplier for a constraint is stored in
# `constraint.dual_value`.
print(constraints[0].dual_value)

# %%
# https://www.cvxpy.org/tutorial/advanced/index.html#getting-the-standard-form
pd1, pd2, pd3 = prob.get_problem_data(cp.CVXOPT)

# %%
pd1

# %%
pd2

# %%
pd3

# %% [markdown]
# # Example 2

# %%
# https://druce.ai/2020/12/portfolio-opimization

# %%
# Problem data.
m = 100
n = 20
np.random.seed(1)
historical_mu = np.random.randn(m, n)

# %%
mu = np.random.randn(n)

# %%
# The `transpose()` is necessary.
covariance = np.cov(historical_mu.transpose())

# %%
weights = cp.Variable(n)

# %%
rets = mu.T @ weights

# %%
portfolio_variance = cp.quad_form(weights, covariance)

# %%
minimize_variance = cp.Problem(
    cp.Minimize(portfolio_variance), [cp.sum(weights) == 1]
)

# %%
minimize_variance.solve()

# %%
rets.value

# %%
weights.value

# %%
maximize_returns = cp.Problem(
    cp.Maximize(rets),
    [
        cp.sum(weights) == 1,
        # Long-short will try to increase leverage to infinity (and fail to converge)
        weights >= 0,
    ],
)

# %%
maximize_returns.solve()

# %%
portfolio_variance.value

# %%
maximize_returns_2 = cp.Problem(
    cp.Maximize(rets),
    [
        cp.norm(weights) <= 1.5,
        cp.sum(weights) == 1,
        portfolio_variance <= 0.05,
    ],
)

# %%
maximize_returns_2.solve()

# %%
portfolio_variance.value

# %% [markdown]
# # More examples

# %%
# https://nbviewer.org/github/cvxgrp/cvx_short_course/blob/master/applications/portfolio_optimization.ipynb

# %%
# https://github.com/druce/portfolio_optimization/blob/master/Portfolio%20optimization.ipynb


"""Functions needed by multiple modules"""

import numpy as np
from collections import OrderedDict

def generate_array(low: int, high: int, size: int = None) -> np.ndarray:
    """Returns generated numbers to be used for search and sort visualizations"""
    return np.random.randint(low, high, size)

def remove_duplicates(array: list) -> list:
    """Return list of array with no duplicates. Preserves order. Used by radix sort."""
    return list(OrderedDict.fromkeys(array))

def get_factorial(num: int) -> int:
    """Returns factorial of a number. Used by bogosort"""
    return np.math.factorial(num)


# -*- coding: utf-8 -*-

#-------------------------------------- DATA PREPROCESSING ---------------------------------#

# Imports
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math

# Reading the dataset from data
dataset = pd.read_csv(r'..\\data\\auto_insurance.csv')

# Creating Dependent and Independent variables
X = dataset['X'].values
y = dataset['Y'].values

# Visualizing the data
title='Linear Regression on <Dataset>'
x_axis_label = 'X-value < The corresponding attribute of X in dataset >'
y_axis_label = 'y-value < The corresponding attribute of X in dataset >'
plt.scatter(X,y)
plt.title(title)
plt.xlabel(x_axis_label)
plt.ylabel(y_axis_label)
plt.show()

# Splitting the data into training set and test set
# This splitting can be done with scikit learns test train split or manully by below code
X_train,X_test = np.split(X,indices_or_sections = [int(len(X)*0.8)])
y_train,y_test = np.split(y,indices_or_sections = [int(len(X)*0.8)])

# Reshaping the numpy arrays since the scikit learn model expects 2-D array in further code
X_train_fw = np.reshape(X_train,newshape = (-1,1))
y_train_fw = np.reshape(y_train,newshape = (-1,1))
X_test_fw = np.reshape(X_test,newshape = (-1,1))
y_test_fw = np.reshape(y_test,newshape = (-1,1))

#------------------------------------ DATA PREPROCESSING ENDS -----------------------------#

#--------------------------------------- TRAINING   ---------------------------------------#

# Computing the values of sigma
sigma_X = sum(X_train)
sigma_y = sum(y_train)
sigma_xy = sum(np.multiply(X_train,y_train))
sigma_X_square = sum(np.square(X_train))
n = len(X_train)

# Computing the values of slope and intercept
m_numerator = (n*sigma_xy)-(sigma_X*sigma_y)
m_denominator =  n*sigma_X_square - math.pow(sigma_X,2)
m = m_numerator/m_denominator

c_numerator = (sigma_y*sigma_X_square)-(sigma_xy*sigma_X)
c_denominator = (n*sigma_X_square) - math.pow(sigma_X,2)
c = c_numerator/c_denominator

# Importing the linear model from sklearn framework
from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(X = X_train_fw, y = y_train_fw)

#-------------------------------------- TRAINING ENDS  ------------------------------------#

#------------------------------- PREDICTION AND PLOTING -----------------------------------#

# Predicting the Results
y_pred_stat = X_test*m + c
y_pred_fw = lr.predict(X_test_fw)

# Visualizing the Results

# Plotting each result individually
plt.scatter(X_test,y_test,c='red')
plt.plot(X_test,y_pred_fw,c='cyan',label='framework')
plt.scatter(X,y)
plt.title(title)
plt.xlabel(x_axis_label)
plt.ylabel(y_axis_label)
plt.show()

plt.scatter(X_test,y_test,c='red')
plt.plot(X_test,y_pred_stat,c='green',label='statistical formula')
plt.scatter(X,y)
plt.title(title)
plt.legend()
plt.xlabel(x_axis_label)
plt.ylabel(y_axis_label)
plt.show()

# Combining the results
plt.scatter(X_test,y_test,c='red')
plt.plot(X_test,y_pred_fw,c='cyan',label='framework')
plt.plot(X_test,y_pred_stat,c='green',label='statistical formula')
plt.scatter(X,y)
plt.title(title)
plt.legend()
plt.xlabel(x_axis_label)
plt.ylabel(y_axis_label)
plt.show()

#------------------------------ PREDICTION AND PLOTING ENDS--------------------------------#


import sources.distributions as distributions
import sources.generate_y as hi_square_work
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import expon

# generate normal distributions
def generate_normal_distributions(batch = 1000, size = 1000):
    x_values = []
    for _ in range(batch):
        x_values.append(distributions.generate_normal_distribution(size = size))

    return np.array(x_values)

# generate exponentials distributions
def generate_expon_distributions(batch=1000, size = 1000):
    x_values = []
    for _ in range(batch):
        x = np.linspace(expon.ppf(0.01), expon.ppf(0.99), size)
        rv = expon()
        x_values.append(rv.pdf(x))

    return np.array(x_values)

# mix two distributions by delimiter
def generate_mix_normal_and_expon(normal_distributions, expon_distributions, delimiter = 5):
    mix_distributions = []
    counter = 0
    for n in range(1, len(normal_distributions) + 1):
        if n % delimiter == 0:
            mix_distributions.append(expon_distributions[counter])
            counter += 1
        else:
            mix_distributions.append(normal_distributions[n-1])

    return np.asarray(mix_distributions)

# mix distribution by shuffle
def mix_distributions(arr1, arr2):
    mix_distr = []
    [mix_distr.append(item) for item in arr1.tolist()]
    [mix_distr.append(item) for item in arr2.tolist()]
    np.random.shuffle(mix_distr)

    return mix_distr

# get array with 0 and 1
# 1 - distribution is normal
# 0 - distribution is not normal
def get_y_train(distributions):
    return np.asarray([[dist] for dist in hi_square_work.hi_square_working(distributions)])


"""

Vector fields associate a 2D vector to each point of the 2D plane. Vector fields are common in Physics as they provide solutions to differential equations. Matplotlib provides functions to visualize vector fields. To illustrate the visualization of vector fields, let's visualize the velocity flow of an incompressible fluid around a cylinder. We do not need to bother about how to compute such a vector field but only about how to show it. The pyplot.quiver() function is what we need.

How the project work:

Although the script is a bit long, the purely graphical part is simple. The vector field is stored in the matrices U and V: the coordinates of each vector we sampled from the vector field. The matrices X and Y contain the sample positions. The matrices X, Y, U, and V are passed to pyplot.quiver(), which renders the vector field. Note that pyplot.quiver() can take just U and V as parameters, but then the legend will show the indexes of the samples rather than their coordinates. As the vector field that we used as an illustration here the fluid flow around a shape, the shape itself is shown as follows:

shape = patches.Circle((0, 0), radius = 1., lw = 2., fc = 'w', ec

 = 'k', zorder = 0)

plt.gca().add_patch(shape)

The vector field inside the cylinder does not appear; we use a masked array. We first create a mask that defines which samples should be shown. Then, we apply this mask on U and V, as shown in the following script:

"""

import numpy as np

import sympy

from sympy.abc import x, y

from matplotlib import pyplot as plt

import matplotlib.patches as patches

plt.style.use("dark_background")

fig = plt.figure(figsize=(3.641, 3.295), dpi=100)

def cylinder_stream_function(U = 1, R = 1):

    r = sympy.sqrt(x ** 2 + y ** 2)

    theta = sympy.atan2(y, x)

    return U * (r - R ** 2 / r) * sympy.sin(theta)

def velocity_field(psi):

    u = sympy.lambdify((x, y), psi.diff(y), 'numpy')

    v = sympy.lambdify((x, y), -psi.diff(x), 'numpy')

    return u, v

U_func, V_func = velocity_field(cylinder_stream_function() )

xmin, xmax, ymin, ymax = -2.5, 2.5, -2.5, 2.5

Y, X = np.ogrid[ymin:ymax:19j, xmin:xmax:16j]

U, V = U_func(X, Y), V_func(X, Y)

M = (X ** 2 + Y ** 2) < 1

U = np.ma.masked_array(U, mask = M)

V = np.ma.masked_array(V, mask = M)

shape = patches.Circle((0, 0), radius = 1, lw = 2, fc = 'w', ec

    = 'k', zorder = 0)

plt.gca().add_patch(shape)

plt.quiver(X, Y, U, V, zorder = 1, color="red")

plt.xticks([])

plt.yticks([])

plt.grid(False)

plt.box(False)

plt.suptitle('Vector fields are well-known to provide solutions of differentialⁿ equations!', fontsize=6, color="white")

plt.title('Vector Fields', color="white", fontsize=10)

plt.figtext(0.82, 0.01, "© Mǟɖ↻ôɖɆⱤ™", color="white", fontsize=6)

plt.show()

plt.savefig('myfig.png', dpi= 200)


#! /usr/bin/env python

# This example came from an answer on Stack Exchange, which was found by
# Googling "plot sine wave python". The answer is available here:
# http://stackoverflow.com/questions/22566692/python-how-to-plot-graph-sine-wave

# The original solution was:
#
# import matplotlib.pyplot as plt
# import numpy as np
#
# Fs = 8000
# f = 5
# sample = 16
# x = np.arange(sample)
# y = np.sin(2 * np.pi * f * x / Fs)
# plt.plot(x, y)
# plt.xlabel('voltage(V)')
# plt.ylabel('sample(n)')
# plt.show()
#

# In class, we went through this answer trying to figure out what each command
# does. We kept changing and deleting parts, and re-running the script to see
# the effect.

# This makes the plotting functions in pyplot available to us.
import matplotlib.pyplot as plt
# This makes the numerical functions of numpy available to us.
import numpy as np

# We decided to delete the following variables in the original solution.
# That meant we had to replace any place they were used with the actual values.
# Fs = 8000
# f = 5
# sample = 16

# Original: x = np.arange(sample)
# We changed "sample" to "16", which was sample's value.
# x = np.arange(16)
# We then figured out, by copying and pasting np.arange(16) into the iPython
# console, that np.arange gives us a list of (in this case, 16) numbers back.
# We had a look at the numpy arange documentation online, and figured out
# we could change the step value, and add a starting value.
# Since we had been talking about values between -4pi and 4pi, which are approx.
# -12 and 12, we stuck those in as start and end points, and set the step to
# 0.1 (try changing the step to 0.5 or 1 or 2 and see what happens).
x = np.arange(-12.0, 12.0, 0.1)

# Original: y = np.sin(2 * np.pi * f * x / Fs)
# Again, here we replaced variables with their values, and it became this:
# y = np.sin(2 * np.pi * 5 * x / 8000)
# Then we just deleted the uninteresting number out (2, 5 and 8000) and the
# graph changed a bit, but it still looked okay.
# y = np.sin(np.pi * x)
# We then realised that np.pi represented the number pi, which is about 3.14.
# We decided to remove it from here (since we were already dealing with pi)
# in the arange function.
y = np.sin(x)

# We knew this just creates a 2D plot of x versus y.
plt.plot(x, y)

# And we deleted/commented out these two lines, since all they did was add
# labels to the axes.
# plt.xlabel('voltage(V)')
# plt.ylabel('sample(n)')

# This just tells Python to show a window with the plot we just created.
plt.show()

# We had a few observations about the graph:
# 1. The maximum value of sin(x) is 1.
# 2. The minimum value is -1.
# 3. These max/min values were related to te sin function itself, being the
#    "opposite" divided by the "hypotenuse" in a right-angled triangle. The
#    hypotenuse is always at least as long as the opposite, dividing one by
#    the other always gives at most 1.
# 4. You might wonder how you could have a negative value for sin, as defined.
#    This is related to the unit circle, which you can Google.


import numpy as np
import matplotlib.pyplot as plt

def RandomPattern(xsize, ysize):
    X, Y = np.meshgrid(np.arange(ysize), np.arange(xsize))
    yco =X.flatten().astype('int')
    np.random.shuffle(yco)
    xco =Y.flatten().astype('int')
    np.random.shuffle(xco)
    return xco, yco

def NormalPattern(xsize, ysize):
    X, Y = np.meshgrid(np.arange(ysize), np.arange(xsize))
    yco =X.flatten().astype('int')
    xco =Y.flatten().astype('int')
    return xco, yco

def SnakeScan(xsize, ysize):
    X, Y = np.meshgrid(np.arange(ysize), np.arange(xsize))
    X[1::2, :] = X[1::2, ::-1]
    yco =X.flatten().astype('int')
    xco =Y.flatten().astype('int')
    return xco, yco

def HilberPattern(xsize, ysize):
    order = int(np.max(np.ceil([np.log2(xsize), np.log2(ysize)])))

    A = np.zeros((0,2));
    B = np.zeros_like(A)
    C = np.zeros_like(A)
    D = np.zeros_like(A)

    north = np.array([[ 0,  1]])
    east  = np.array([[ 1,  0]])
    south = np.array([[ 0, -1]])
    west  = np.array([[-1,  0]])

    for i in range(int(order)):
            AA = np.concatenate((B, north, A, east, A, south, C))
            BB = np.concatenate((A, east, B, north, B, west, D))
            CC = np.concatenate((D, west, C, south, C, east, A))
            DD = np.concatenate((C, south, D, west, D, north, B))
            A = AA
            B = BB
            C = CC
            D = DD

    co = np.concatenate((np.array([[0, 0]]),np.cumsum(A, axis = 0))).astype('int')
    return co[:,0], co[:,1]

def LoadScanPattern(scan_name, xsize, ysize):
    if scan_name ==  'Normal Scan':
        xco, yco = NormalPattern(xsize, ysize)
    elif scan_name ==  'Random Scan':
        xco, yco = RandomPattern(xsize, ysize)
    elif scan_name ==  'Snake Scan':
        xco, yco = SnakeScan(xsize, ysize)
    elif scan_name ==  'Hilbert Scan':
        xco, yco = HilberPattern(xsize, ysize)
    return xco, yco


#!/usr/bin/python3
# NumpyUltraQuickTutorial by Google
# https://colab.research.google.com/github/google/eng-edu/blob/master/ml/cc/exercises/numpy_ultraquick_tutorial.ipynb
#
import numpy as np
print(np.version.version)
# Examples
one_dimensional_array = np.array([1.2, 2.4, 3.5, 4.7, 6.1, 7.2, 8.3, 9.5])
print("1D array:\n",one_dimensional_array)
#
two_dimensional_array = np.array([[6, 5], [11, 7], [4, 8]])
print("2D array:\n",two_dimensional_array)
#
zeros_array=np.zeros((3,4))
print("Zeros array 3x4:\n",zeros_array)
#
ones_array=np.ones((2,3))
print("Ones array 2x3\n",ones_array)
#
sequence_of_integers = np.arange(5,12)
print("Lower bound sequence from 5 to 12\n",sequence_of_integers)
#
random_integers_between_50_and_100 = np.random.randint(low=50,high= 101,size=(6))
print("Sequence of 6 random numbers between 50 and 100\n",random_integers_between_50_and_100)
#
random_floats_between_0_and_1 = np.random.random_sample([6]) # Updated from np.random.random()
print("Random floats between 0 and 1\n:",random_floats_between_0_and_1)
#
random_floats_between_2_and_3 = random_floats_between_0_and_1 + 2.0
print(random_floats_between_2_and_3)
#
random_integers_between_150_and_300 = random_integers_between_50_and_100 * 3
print(random_integers_between_150_and_300)
#
# Task 1: Create a linear dataset
#
# Assign a sequence of integers from 6 to 20 (inclusive) to a NumPy array named feature.
#
feature = np.arange(6,21)
print(feature)
#
# Assign 15 values to a NumPy array named label such that;
#   label = (3)(feature) + 4
#
label = 3 * feature + 4
print(label)
#
# Task 2: Add some noise to the Dataset
# Insert a little floating point random noise between -2 and 2 into each element of the label array
#
noise = (np.random.random_sample([15]) * 4) - 2.0 # Updated from np.random.random()
print(noise)
label = label+noise
print(label)


#this code implements the naive way of getting an interpolating
#polynomial from a set of data points given.

import numpy as np
import data_reader
import sys

def create_matrix(x_values:list):
	nor = len(x_values) #nor - number of rows
	m = np.empty((nor,nor),dtype=np.float64)
	for i in range(0,nor):
		for j in range(nor-1,-1,-1):
			if j == nor-1:
				m[i,j] = 1
				continue
			m[i,j] = m[i,j+1] * x_values[i]
	return m

def solve_sle(A,b):
	'''solves the sle-system of linear equation'''
	return np.linalg.solve(A,b)

if __name__ == '__main__':
	help_message = f"""
	Usage: {sys.argv[0]} <filename> [p|d]
	* the first option is for the filename
	* the second option is p - for python version, d - for desmos version
	"""

	if len(sys.argv) != 3:
		print(help_message)
		exit(1)

	f = data_reader.InterpolationData(sys.argv[1])
	data = f.get_data_in_two_lists()
	m = create_matrix(data[0])
	coeff = solve_sle(m,data[1])

	output = ""
	MODE = sys.argv[2]

	if MODE == 'p':
		output += "f = lambda x : "

	for i in range(len(coeff)):
		if i != 0:
			output += '+'
		output += str(coeff[i])
		if i == len(coeff) - 1:
			continue
		output += '*x'
		if MODE == 'p':
			output += '**'
		elif MODE == 'd':
			output += '^'
		output += str(len(coeff)-1-i)
		output += ' '
	print(output)


# Copyright 2021 Neuron-AI GitHub Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
from typing import Iterable
from warnings import warn

from numpy import array, sqrt

def euclidean_distance(x: Iterable, y: Iterable, **kwargs) -> float:
	"""Euclidean distance function, returns the distance between x and y
	Pass "supress_warnings=True" to the function to avoid warnings when x and y have different lengths.

	Parameters
	----------
	x : Iterable
		The first item to be calucated with
	y : Iterable
		The second item to be calucated with

	Returns
	-------
	float
		The euclidean distance
	"""
	x = array(x).reshape(1, -1) # to make them 1D
	y = array(y).reshape(1, -1)

	if x.shape != y.shape and kwargs.get("suppress_warnings") != True:
		warn(UserWarning("using sequences which do not contain equivalent numbers of items can result in unexpected results."))

	return sqrt(sum((x - y)**2 for x, y in zip(x[0], y[0])))

def manhattan_distance(x, y, **kwargs):
	"""Manhattan distance function, returns the manhattan distance between x and y
	Pass "supress_warnings=True" to the function to avoid warnings when x and y have different lengths.

	Parameters
	----------
	x : Iterable
		The first item to be calucated with
	y : Iterable
		The second item to be calucated with

	Returns
	-------
	float
		The manhattan distance
	"""
	x = array(x).reshape(1, -1)
	y = array(y).reshape(1, -1)

	if x.shape != y.shape and kwargs.get("suppress_warnings") != True:
		warn(UserWarning("using sequences which do not contain equivalent numbers of items can result in unexpected results."))

	return sum(abs((x - y)) for x, y in zip(x[0], y[0]))

distance_functions = {
    "euclidean": euclidean_distance,
    "manhattan": manhattan_distance
}


import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
from types import SimpleNamespace

# Function to find steady state (ss) value for capital (k) of the basic Solow model using scipy.optimize
def ss_bas(n, s, B, alpha, delta):
    """
    Args:
    alpha  (float): Return on physical capital
    delta  (float): Depreciation rate on capital
    s      (float): Savings/investments
    n      (float): Growth rate of population/labour force
    B      (int)  : Total factor productivity (TFP)

    Returns:
    Steady state value for capital, k*
    """
    res = lambda k: s*B*k**alpha - (n+delta)*k # Objective function we wish to find the root of

    return optimize.bisect(res, 1,100, full_output=False) # We use the bisect method optimizer from the scipy package to find the root of res

# Function to plot the basic solow model
def ss_bas_plot(n,s,B,alpha,delta,k_max):
    """
    Args:
    alpha  (float): Return on physical capital
    delta  (float): Depreciation rate on capital
    s      (float): Savings/investments
    n      (float): Growth rate of population/labour force
    B      (int)  : Total factor productivity (TFP)
    k_max  (int)  : Used to set the maximum value of x-axis

    Returns:
    The solow diagram with (n+delta)*k_t line and the growth paths of physical capital per capita and output per capita"""

    # Make an empty list to contain the k values
    k_vec = [0]

    # Make an empty list to contain the y values
    y_vec = [0]

    # An empty list is made to contain the (n+delta)*k_t line
    line = [0]

    # (n+delta)*k_t line
    for k in range(1,k_max):
        l = (n + delta)*k
        line.append(l)

    # We generate the growth in capital per capita
    for k in range(1,k_max):
        k_t = s*B*k**alpha
        k_vec.append(k_t)

    # We generate the growth in output per capita
    for k in range(1,k_max):
        y_t = B*k**alpha
        y_vec.append(y_t)

    # Create the plot
    plt.figure(figsize=(14,7))
    plt.plot(k_vec[:k_max], label=r'$sBk_t^{\alpha}$', color = 'blue')
    plt.plot(line[:k_max], label = r'$(n+\delta)k_t$', color = 'green')
    plt.plot(y_vec[:k_max], label=r'$y_t$', color = 'orange')
    plt.xlim(0,k_max-1)
    plt.ylim(0,y_vec[-1])
    plt.xlabel('$k_t$')

    plt.grid(True)

    plt.title('Figure 1. Solow diagram - Basic model')

    res = lambda k: s*B*k**alpha - (n+delta)*k

    k_star = optimize.bisect(res, 1e-8,100000, full_output=False)
    y_star = B*k_star**alpha
    c_star = (1-s)*y_star

    plt.scatter(k_star,y_star-c_star,color='black',s=80,zorder=3)
    plt.scatter(k_star,y_star,color='black',s=80,zorder=3)
    plt.axvline(k_star, ymin=(y_star-c_star)/y_vec[-1] ,ymax=y_star/y_vec[-1],color='red',linestyle='-', label='c*')
    plt.axvline(k_star, ymax=(y_star-c_star)/y_vec[-1] ,color='gray',linestyle='--')
    plt.text(k_star + 0.5,0.02 , 'k*', fontsize=12)
    plt.legend()

    print(f'For the chosen values of parameters the steady state for k is: k* = {k_star:.3f}')
    print(f'For the chosen values of parameters the steady state for y is: y* = {y_star:.3f}')
    print(f'For the chosen values of parameters the steady state for c is: c* = {c_star:.3f}')

    return plt.show()

# Function to find transition equation for human capital per capita
def h_func(k,s_H,s_K,g,n,alpha,phi,delta,h):
    """args:
    k     (float): Physical capital
    s_H   (float): Investments in human capital
    s_K   (float): Investments in physical capital
    g     (float): Growth in technology
    n     (float): Growth rate of population/labour force
    delta (float): Depreciation rate
    alpha (float): Return on physical capital
    phi   (float): Return on human capital
    h     (float): Human capital

    Returns:
    The transition equation for human capital per capita
        """
    return 1/((1+n)*(1+g))*(s_H*k**(alpha)*h**(phi)+(1-delta)*h)-h

# Function to find transition equation for physical capital per capita
def k_func(h,s_H,s_K,g,n,alpha,phi,delta,k):
    """args:
    h     (float): Human capital
    s_h   (float): Investments in human capital
    s_k   (float): Investments in physical capital
    g     (float): Growth in technology
    n     (float): Growth rate of population/labour force
    delta (float): Depreciation rate
    alpha (float): Return on physical capital
    phi   (float): Return on human capital
    k     (float): Physical capital

    Returns:
    The transition equation for physical capital per capita
    """
    return 1/((1+n)*(1+g))*(s_K*k**(alpha)*h**(phi)+(1-delta)*k)-k

#We define a function expressing the nullclines
def solve_ss(s_H,s_K,g,n,alpha,phi,delta):
    """args:
    sh    (float): Investments in human capital
    sk     float): Investments in physical capital
    g     (float): Growth in technology
    n     (float): Growth in labour force
    alpha (float): Return on physical capital
    phi   (float): Return on human capital
    delta (float): Depriciation rate

    Returns:
    Nullclines for physical and human capital
    """

    # a. grids

    k_vec = np.linspace(1e-4,100,500)
    h_vec_DeltaK0 = np.empty(500)
    h_vec_DeltaH0 = np.empty(500)

    # b. solve for each k in grid
    for i,k in enumerate(k_vec):
        #Solve for delta_h=0

        # i. objective
        obj = lambda h: -h_func(k,s_H,s_K,g,n,alpha,phi,delta,h)

        # ii. optimizer
        result = optimize.root_scalar(obj,method='brentq',bracket=[1e-20,1000])

        h_vec_DeltaH0[i] = result.root

        #Solve for delta_k=0
        # i. objective
        obj = lambda h: -k_func(h,s_H,s_K,g,n,alpha,phi,delta,k)

        # ii. optimizer
        result = optimize.root_scalar(obj,method='brentq',bracket=[1e-20,1000])

        h_vec_DeltaK0[i] = result.root

    return k_vec, h_vec_DeltaK0, h_vec_DeltaH0


import numpy as np
import matplotlib.pyplot as plt

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

# Return evenly spaced numbers over a specified interval.
xdata = np.linspace(-8, 8, 960,endpoint=True)
ydata = func(xdata)

plt.plot(xdata,ydata)

plt.show()


import numpy as np

def f1_score(y_true,y_pred):
    """
    Calculates the f1_score which is one of the metrics used to evaluate a classification model
    :param y_true: true labels
    :param y_pred: predicted labels
    :return: returns the f1_score of the classification model using the formula f1_score = (2*precision*recall)/(precision+recall)
    """
    if(len(y_true)!=len(y_pred)):
        raise ValueError("Length mismatch: Lengths of y_true and y_pred don't match")

    y_true = np.squeeze(y_true)
    y_pred = np.squeeze(y_pred)

    total_positve = np.sum(y_pred)
    true_positive = np.sum((y_pred==1)&(y_true==y_pred))
    precision = np.round(true_positive/total_positve,2)

    total_positve = np.sum(y_true)
    recall = np.round(true_positive/total_positve,2)
    f1_score = (2*(precision*recall))/(precision+recall)
    return np.round(f1_score,2)


import numpy as np

# Compute every step manually

# Linear regression
# f = w * x

# here : f = 2 * x
X = np.array([1, 2, 3, 4], dtype=np.float32)
Y = np.array([2, 4, 6, 8], dtype=np.float32)

w = 0.0

# model output
def forward(x):
    return w * x

# loss = MSE
def loss(y, y_pred):
    return ((y_pred - y)**2).mean()

# J = MSE = 1/N * (w*x - y)**2
# dJ/dw = 1/N * 2x(w*x - y)
def gradient(x, y, y_pred):
    return np.dot(2*x, y_pred - y).mean()

print(f'Prediction before training: f(5) = {forward(5):.3f}')

# Training
learning_rate = 0.01
n_iters = 20

for epoch in range(n_iters):
    # predict = forward pass
    y_pred = forward(X)

    # loss
    l = loss(Y, y_pred)

    # calculate gradients
    dw = gradient(X, Y, y_pred)

    # update weights
    w -= learning_rate * dw

    if epoch % 2 == 0:
        print(f'epoch {epoch+1}: w = {w:.3f}, loss = {l:.8f}')

print(f'Prediction after training: f(5) = {forward(5):.3f}')


import numpy as np
import math
import time
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

import config

def gauss2D(xx,yy,mu,sigma,rho):
	z = np.zeros_like(xx)
	z = np.exp( (((xx-mu[0])/sigma[0])**2+((yy-mu[1])/sigma[1])**2-2*rho*np.multiply((xx-mu[0]),(yy-mu[1]))/sigma[0]/sigma[1])/(-2*(1-rho**2)) )/(2*math.pi*sigma[0]*sigma[1])
	maxValue = 1/(2*math.pi*sigma[0]*sigma[1])
	return z,maxValue

def generateMap(mu,sigma,lamb,plot=False):
	assert mu.shape[0] == sigma.shape[0], "Number of mu and sigma pairs are wrong"
	if mu.shape[0] == 1:
		pdf,maxValue = gauss2D(config.xx,config.yy,mu[0,:],sigma[0,:],0)
		pdfMap = pdf*lamb + 1/(config.lengthx*config.lengthy)
	else:
		pdfs = []
		maxs = []
		for i in range(mu.shape[0]):
			pdf,maxValue = gauss2D(config.xx,config.yy,mu[i,:],sigma[i,:],0)
			pdfs.append(pdf)
			maxs.append(maxValue)
		minValue = min(maxs[1:])
		scale = maxs/minValue
		pdfMap = pdfs[0]/scale[0]*lamb
		for i in range(1,mu.shape[0]):
			pdfMap -= pdfs[i]/scale[i]
		pdfMap += minValue

	pdfMap = np.true_divide(pdfMap,np.max(pdfMap)) #normalize

	if plot == True:
		fig = plt.figure()
		ax = fig.add_subplot(111,projection='3d')
		ax.plot_surface(config.xx,config.yy,pdfMap)
		plt.show()

	return pdfMap

def sample(pdf):
	row = np.sum(pdf,axis=1)
	col = np.sum(pdf,axis=0)
	x_cdf = np.cumsum(col)/np.sum(col) # normalize x position cdf
	y_cdf = np.cumsum(row)/np.sum(row) # normalize y position cdf

	prob = np.random.uniform(0,1,2)
	x_ind = np.digitize(prob[0],x_cdf)
	y_ind = np.digitize(prob[1],y_cdf)

	x = x_ind*config.dx
	y = y_ind*config.dy

	return np.array([x,y])

if __name__ == '__main__':
	plot_map = True
	plot_sample = True
	######################################
	#    Single Gaussian Distribution    #
	######################################
	'''
	mu = np.squeeze(config.mu)
	sigma = np.squeeze(config.sigma)
	dist,_ = gauss2D(config.xx,config.yy,mu,sigma,0)

	if plot_sample == True:
		plt.figure()
		start =int(round(time.time()*1000))
		for i in range(1000):
			(x,y) = sample(dist)
			plt.scatter(x,y)
		plt.show()
		print("--- %s milliseconds ---"%(int(round(time.time()*1000))-start))
	'''

	######################################
	#        With Involving Vehicle      #
	######################################
	mu = np.array([[0.8509,1.4478],[0.3,0.8809]])
	sigma = np.array([[0.01,0.01],[7.32e-1,3.33e-1]])
	lamb = 1e5
	pdfMap = generateMap(mu,sigma,lamb,True)

	if plot_sample == True:
		plt.figure()
		start =int(round(time.time()*1000))
		for i in range(1000):
			pt = sample(pdfMap)
			plt.scatter(pt[0],pt[1])
		print("--- %s milliseconds ---"%(int(round(time.time()*1000))-start))
		plt.show()


import numpy as np

# define a function to linear fit x-y data without error bars
def least_squares(x, y):
    """Perform a least-squares fit to data (x,y)

    Args :
       x : x values
       y : y values

    Returns :
       a : intercept
       b : slope
       sigma : total uncertainty (sqrt(variance/(n-2)))
       sigma_a : uncertainty on a
       sigma_b : uncertainty on b

    """

    n = len(x)   # number of galaxies

    if n <= 2 :
        print ('Error! Need at least two data points!')
        exit()

    # Compute all of the stat. variables we need
    s_x = np.sum(x)
    s_y = np.sum(y)
    s_xx = np.sum( x**2 )
    s_xy = np.sum( x*y )
    denom = n * s_xx - s_x**2
    if abs( denom ) < 0.000001 :
        print ('Error! Denomominator is zero!')
        exit()

    # Compute y-intercept and slope
    a = (s_xx * s_y - s_x * s_xy) / denom
    b = (n*s_xy - s_x * s_y) / denom

    # Compute uncertainties
    if n > 2 :
          sigma = np.sqrt(np.sum((y - (a+b*x))**2 ) / (n-2))
          sigma_a = np.sqrt(sigma**2 * s_xx / denom)
          sigma_b = np.sqrt(sigma**2 * n / denom)
    else :
          sigma = 0.
          sigma_a = 0.
          sigma_b = 0.

    return [a, b, sigma, sigma_a, sigma_b]


"""
#########   Homework_4.3  ################

"""

import numpy as np

U = [[1, 1], [1, 2], [1, 3], [1, 4]]
x = [[-1], [0], [2], [5]]

U_Transpose =np.transpose(U)
U_Transpose__U = np.matrix(U_Transpose)* np.matrix(U)

U_Transpose__U__Inv = np.linalg.inv(U_Transpose__U)
U_Transpose__U__Inv__U_Transpose = np.matrix(U_Transpose__U__Inv) * np.matrix(U_Transpose)

Projection = np.matrix(U) * (np.matrix(U_Transpose__U__Inv__U_Transpose)*np.matrix(x))

print("U:", U)
print("U_Transpose:", U_Transpose)

print("U_Transpose . U :", U_Transpose__U)

print("U_Transpose__U Inverse: ", U_Transpose__U__Inv)

print("U_Transpose__U__Inv . U_Transpose: ", U_Transpose__U__Inv__U_Transpose)

print("Projection", Projection)

Matrix and Vector Operations/ProjectionCalculation.py


#-*- coding:utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

'''
最小二乘法回归
参考博客：http://blog.csdn.net/wangyangzhizhou/article/details/60133958
line 代数推导
matrixLine 矩阵推导
'''

def calcAB(x,y):
    n = len(x)
    sumX,sumY,sumXY,sumXX =0,0,0,0
    for i in range(0,n):
        sumX  += x[i]
        sumY  += y[i]
        sumXX += x[i]*x[i]
        sumXY += x[i]*y[i]
    a = (n*sumXY -sumX*sumY)/(n*sumXX -sumX*sumX)
    b = (sumXX*sumY - sumX*sumXY)/(n*sumXX-sumX*sumX)
    return a,b,

def line():
	xi = [1,2,3,4,5,6,7,8,9,10]
	yi = [10,11.5,12,13,14.5,15.5,16.8,17.3,18,18.7]
	a, b = calcAB(xi,yi)
	print("y = %10.5fx + %10.5f" %(a,b))
	x = np.linspace(0,10)
	y = a * x + b
	plt.plot(x,y)
	plt.scatter(xi,yi)
	plt.show()

def matrixLine():
	x = [1,2,3,4,5,6,7,8,9,10]
	y = [10,11.5,12,13,14.5,15.5,16.8,17.3,18,18.7]

	A = np.vstack([x,np.ones(len(x))]).T

	a, b = np.linalg.lstsq(A,y)[0]
	print("y = %10.5fx + %10.5f" %(a,b))
	x = np.array(x)
	y = np.array(y)

	plt.plot(x,y,'o',label='data',markersize=10)
	plt.plot(x,a*x+b,'r',label='line')
	plt.show()

if __name__ == '__main__':
	# line()
	matrixLine()


import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

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

def error_vector(y_vector, y_hat_vector):
    errors = [y_vector[i] * np.log(y_hat_vector[i]) +
              (1 - y_vector[i]) * np.log(1 - y_hat_vector[i])
              for i in range(len(y_vector))]
    return errors

def error(y_vector, y_hat_vector):
    errors = error_vector(y_vector, y_hat_vector)
    return -sum(errors) / len(y_vector)

def prediction(X_mat, w_vector, b_scalar):
    y_hat_vector = sigmoid(np.matmul(X_mat, w_vector) + b_scalar)
    return y_hat_vector

def gradients(X_mat, y_vector, y_hat_vector):
    dw1 = [-(y_vector[i] - y_hat_vector[i]) * X_mat[i][0] for i in range(len(y_vector))]
    dw2 = [-(y_vector[i] - y_hat_vector[i]) * X_mat[i][1] for i in range(len(y_vector))]
    db = [-(y_vector[i] - y_hat_vector[i]) for i in range(len(y_vector))]

    return dw1, dw2, db

def gradient_descent(X_mat, y_vector, w_vector, b_scalar, learn_rate=0.01):
    y_hat_vector = prediction(X_mat, w_vector, b_scalar)
    dw1, dw2, db = gradients(X_mat, y_vector, y_hat_vector)

    w_vector[0] -= learn_rate * sum(dw1)
    w_vector[1] -= learn_rate * sum(dw2)
    b_scalar -= learn_rate * sum(db)

    e = error(y_vector, y_hat_vector)

    return w_vector, b_scalar, e

def __main__():
    X_mat = []
    y_vector = []
    type1_x = []
    type1_y = []
    type2_x = []
    type2_y = []

    with open("./data.txt", 'r') as f:
        for line in f:
            rows = line.split(',')
            num0 = float(rows[0])
            num1 = float(rows[1])
            num2 = int(rows[2])

            if num2 == 0:
                type1_x.append(num0)
                type1_y.append(num1)
            elif num2 == 1:
                type2_x.append(num0)
                type2_y.append(num1)

            X_mat.append([num0, num1])
            y_vector.append(num2)

    X_mat = np.array(X_mat)
    y_vector = np.array(y_vector)

    w_vector = np.array(np.random.rand(2, 1)) * 2 -1
    b_scalar = np.random.rand(1)[0] * 2 - 1

    line_args = [] # wx + b
    errors = []

    while True:
        w_vector, b_scalar, error = gradient_descent(X_mat, y_vector, w_vector, b_scalar)
        # w1x1 + w2x2 + b == x2 = -w1x1/w2 - b/w2
        line_args.append((-w_vector[0] / w_vector[1], -b_scalar / w_vector[1]))
        errors.append(error)
        if error < 0.15:
            break

    print(line_args[len(line_args) - 1])
    print(errors)

    plt.figure(figsize=(8, 5), dpi=80)
    axes = plt.subplot()

    type1 = axes.scatter(type1_x, type1_y, s=40, c='red')
    type2 = axes.scatter(type2_x, type2_y, s=40, c='green')

    plt.xlabel('x1')
    plt.ylabel('x2')
    axes.legend((type1, type2), ('0', '1'), loc=1)

    x = np.linspace(0, 1.0)
    y = line_args[len(line_args) - 1][0] * x + line_args[len(line_args) - 1][1]
    axes.plot(x, y)

    # for args in line_args:
    #     x = np.linspace(0, 1.0)
    #     y = args[0] * x + args[1]
    #     axes.plot(x, y)

    plt.show()

if __name__ == "__main__":
    __main__()


import numpy as np

def Cov(xs, ys, meanx=None, meany=None):
    '''mid compute covariance of xs and ys'''
    xs = np.asarray(xs)
    ys = np.asarray(ys)
    if meanx is None:
        meanx = np.mean(xs)
    if meany is None:
        meany = np.mean(ys)
    cov = np.dot(xs-meanx, ys-meany) / len(xs)
    return cov


"""
Fourier Transform Utilities

MIT License
"""
import numpy as np
import math
import cmath

class FourierDatum:
    """
    Holds Fourier Transform data: complex result, frequency, phase, and amplitude
    """
    def __init__(self, complex_num, freq):
        self.complex_num = complex_num
        self.freq = freq
        self.phase = math.atan2(complex_num.imag, complex_num.real)
        self.amplitude = np.sqrt(complex_num.real ** 2 + complex_num.imag ** 2)

def fft(z):
    """
    Take FFT of complex vector z and store its values in FourierDatum array
    :param z Complex-valued vector
    :returns Array of FourierDatum objects
    """
    fft_vals = np.fft.fft(z)
    fft_data = []
    N = len(z)
    k = 0

    for fft_val in fft_vals:
        # divide by N to keep drawing size reasonable
        fft_data.append(FourierDatum(fft_val / N, k))
        k += 1

    return fft_data

def dft(z):
    """
    Take DFT of complex vector z and store its values in FourierDatum array
    :param z Complex-valued vector
    :returns Array of FourierDatum objects
    """
    dft_data = []
    N = len(z)

    # k is frequency
    for k in range(0, N):
        zk = complex(0, 0)

        for n in range(0, N):
            phi = (2 * np.pi * k * n) / N
            zk += z[n] * complex(np.cos(phi), -np.sin(phi))

        zk /= N
        dft_data.append(FourierDatum(zk, k))

    return dft_data


import numpy as np

def gaussian_KL(mu0, Sig0, mu1, Sig1inv):
  t1 = np.dot(Sig1inv, Sig0).trace()
  t2 = np.dot((mu1-mu0),np.dot(Sig1inv, mu1-mu0))
  t3 = -np.linalg.slogdet(Sig1inv)[1] - np.linalg.slogdet(Sig0)[1]
  return 0.5*(t1+t2+t3-mu0.shape[0])

def weighted_post_KL(th0, Sig0inv, sigsq, X, Y, w, reverse=True):
  muw, Sigw = weighted_post(th0, Sig0inv, sigsq, X, Y, w)
  mup, Sigp = weighted_post(th0, Sig0inv, sigsq, X, Y, np.ones(X.shape[0]))
  if reverse:
    return gaussian_KL(muw, Sigw, mup, np.linalg.inv(Sigp))
  else:
    return gaussian_KL(mup, Sigp, muw, np.linalg.inv(Sigw))

def weighted_post(th0, Sig0inv, sigsq, X, Y, w):
  Sigp = np.linalg.inv(Sig0inv + (w[:, np.newaxis]*X).T.dot(X)/sigsq)
  mup = np.dot(Sigp,  np.dot(Sig0inv,th0) + (w[:, np.newaxis]*Y[:,np.newaxis]*X).sum(axis=0)/sigsq )
  return mup, Sigp

def potentials(sigsq, X, Y, samples):
  XST = X.dot(samples.T)
  return -1./2.*np.log(2.*np.pi*sigsq) - 1./(2.*sigsq)*(Y[:,np.newaxis]**2 - 2*XST*Y[:,np.newaxis] + XST**2)


import math
import numpy as np
import scipy
import scipy.linalg

# cluster input (below)

#def EuclideanDistance(point1, point2):
#	xDiff = point1[0] - point2[0]; <- cluster1, 2
#	yDiff = point1[1] - point2[1];
#	return math.sqrt(xDiff*xDiff + yDiff*yDiff);

def Multiplication(a, b):
	a = np.array(a);
	b = np.array(b);
	ans = np.matmul(a, b);
	return ans.tolist();

def Transpose(a):
	a = np.array(a);
	ans = a.T;
	return ans.tolist();

def Determinant(a):
	a = np.array(a);
	ans = np.linalg.det(a);
	return ans;

def Inverse(a):
	a = np.array(a);
	ans = np.linalg.inv(a);
	return ans.tolist();

def EigenValue(a):
	a = np.array(a);
	answ, ansv = np.linalg.eig(a);
	answ = ",".join(str(x) for x in answ);
	ansv = ",".join(str(x) for x in ansv.reshape(1,-1)[0]);
	wLength = str(len(answ));
	wLLength = str(len(wLength));
	return wLLength+wLength+answ+ansv;

def Special(a, b):
	if a==0:
		ans = np.zeros(b);
	else:
		ans = np.identity(b);
	return ans.tolist();

def Rank(a):
        ans = np.linalg.matrix_rank(a);
        return ans;

def LUD(a):
        P, L, U = scipy.linalg.lu(a);
        ans = (P.tolist(), L.tolist(), U.tolist());
        return ans;

def SolveLin(matrix, vector):
        ans=np.linalg.solve(matrix,vector);
        return ans.tolist();

def SVD(a, only_specific):
        if only_specific == 1:
                full_matrices = False;
        else:
                full_matrices = True;
        u, sv, vh = np.linalg.svd(a, full_matrices);
        s = np.diag(sv);
        ans = (sv.tolist(), u.tolist(), s.tolist(), vh.tolist());
        return ans;


import numpy as np

def lagrange(x, y, x_int):
    """
    Interpolates a value using Lagrange polynomial
    Parameters:
        x: Array containing x values
        y: Array containing y values
        x_int: Value to interpolate
    Returns:
        y_int: Interpolated value
    """

    n = x.size
    y_int = 0

    for i in range(0, n):
        p = y[i]
        for j in range(0, n):
            if i != j:
                p = p * (x_int - x[j]) / (x[i] - x[j])

                print('L{}(x) = (x - x_{}) / (x_{} - x_{}) '.format(i, j, i, j))
        y_int = y_int + p

    return [y_int]

def neville(x, y, x_int):
    """
    Interpolates a value using Neville polynomial
    Parameters:
        x: Array containing x values
        y: Array containing y values
        x_int: Value to interpolate
    Returns:
        y_int: Interpolated value
        q: Coefficients matrix
    """

    n = x.size
    q = np.zeros((n, n - 1))
    q = np.concatenate((y[:, None], q), axis=1)

    for i in range(1, n):
        for j in range(1, i + 1):
            q[i, j] = ((x_int - x[i - j]) * q[i, j - 1] - (x_int - x[i]) * q[i - 1, j - 1]) / (x[i] - x[i - j])

    y_int = q[n - 1, n - 1]

    return [y_int, q]


import numpy as np

def running_average(x):
    """
    Calculate the running average of an array.

    Parameters
    ----------
    x : array
        The input array

    Returns
    -------
    ave : array
        The running average of x
    """
    if x.ndim == 1:
        x = x.reshape((-1, 1))
    ave = np.empty(x.shape)

    ave[0, :] = x[0, :]
    for i in range(1, len(ave)):
        ave[i, :] = ave[i - 1, :] + 1 / (i + 1) * (x[i, :] - ave[i - 1, :])

    return ave

# TODO: Move to testing module.
assert all(
    running_average(np.array([0, 5, 4, 9, 3]))
    == np.reshape([0, 2.5, 3, 4.5, 4.2], (-1, 1))
)

def arg_maxima(x):
    """Return arguments (indices) of all maxima in an array."""
    max_value = np.max(x)
    return np.argwhere(x == max_value).reshape(-1)


#
# sine_wave.py
# Animate a sine wave and save it as MP4 file
# Sparisoma Viridi | https://github.com/dudung/cookbook
# 20220331 Create this program by modifying original one [1].
#
# 1. marlise23, "Python unknown file extension .mp4",
#    Stack Overflow, 20 Oct 2019,
#    url https://stackoverflow.com/q/58469357/9475509
#    [20220331].
#

# url https://realpython.com/python-import/
# Make code in one module available in anothe
import numpy as np

# url https://www.w3schools.com/python/ref_keyword_from.asp
# Import only certain section from a module
from matplotlib import pyplot as plt
from matplotlib import animation

# url https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.figure.html
# Create a new figure, or activate an existing figure
fig = plt.figure()

# url https://matplotlib.org/3.5.0/api/_as_gen/matplotlib.pyplot.axes.html
# Add an axes to the current figure and make it the current axes
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))

# url https://matplotlib.org/3.5.0/api/_as_gen/matplotlib.axes.Axes.plot.html
# Plot y versus x as lines and/or markers
line, = ax.plot([], [], lw=2)

# url https://stackoverflow.com/q/58469357/9475509
# Initialize each frame by plotting the background
def init():
  # url https://matplotlib.org/3.5.0/api/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D.set_data
	# Set the x and y data
  line.set_data([], [])
  return line,

# url https://stackoverflow.com/q/58469357/9475509
# Call sequentially plotted function
def animate(i):
  # url https://numpy.org/doc/stable/reference/generated/numpy.linspace.html
	# Return evenly spaced numbers over a specified interval
  x = np.linspace(0, 2, 1000)

  # url https://numpy.org/doc/stable/reference/generated/numpy.sin.html
  # Trigonometric sine, element-wise
  y = np.sin(2 * np.pi * (x - 0.01 * i))

  # url https://matplotlib.org/3.5.0/api/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D.set_data
  line.set_data(x, y)
  return line,

# url https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.animation.FuncAnimation.html
# Makes an animation by repeatedly calling a function func
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200, interval=20, blit=True)

# url https://matplotlib.org/2.0.2/api/_as_gen/matplotlib.animation.Animation.save.html
# Saves a movie file by drawing every frame
anim.save('sine_wave_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])

# url https://matplotlib.org/3.5.0/api/_as_gen/matplotlib.pyplot.show.html
# Display all open figures
plt.show()