# -*- coding: utf-8 -*-
"""
Created on Mon Jun  8 23:41:53 2020

@author: lankuohsing
"""

# In[]
import numpy as np
from w2v_utils import *
# In[]
words, word_to_vec_map = read_glove_vecs('./data/glove.6B.50d.txt')

# In[]
# GRADED FUNCTION: cosine_similarity

def cosine_similarity(u, v):
    """
    Cosine similarity reflects the degree of similarity between u and v

    Arguments:
        u -- a word vector of shape (n,)
        v -- a word vector of shape (n,)

    Returns:
        cosine_similarity -- the cosine similarity between u and v defined by the formula above.
    """

    distance = 0.0

    ### START CODE HERE ###
    # Compute the dot product between u and v (≈1 line)
    dot = np.dot(u,v)
    # Compute the L2 norm of u (≈1 line)
    norm_u = np.sqrt(np.sum(u**2))

    # Compute the L2 norm of v (≈1 line)
    norm_v = np.sqrt(np.sum(v**2))
    # Compute the cosine similarity defined by formula (1) (≈1 line)
    cosine_similarity = dot/(norm_u*norm_v)
    ### END CODE HERE ###

    return cosine_similarity
# In[]
father = word_to_vec_map["father"]
mother = word_to_vec_map["mother"]
ball = word_to_vec_map["ball"]
crocodile = word_to_vec_map["crocodile"]
france = word_to_vec_map["france"]
italy = word_to_vec_map["italy"]
paris = word_to_vec_map["paris"]
rome = word_to_vec_map["rome"]

print("cosine_similarity(father, mother) = ", cosine_similarity(father, mother))
print("cosine_similarity(ball, crocodile) = ",cosine_similarity(ball, crocodile))
print("cosine_similarity(france - paris, rome - italy) = ",cosine_similarity(france - paris, rome - italy))

# In[]
# GRADED FUNCTION: complete_analogy

def complete_analogy(word_a, word_b, word_c, word_to_vec_map):
    """
    Performs the word analogy task as explained above: a is to b as c is to ____.

    Arguments:
    word_a -- a word, string
    word_b -- a word, string
    word_c -- a word, string
    word_to_vec_map -- dictionary that maps words to their corresponding vectors.

    Returns:
    best_word --  the word such that v_b - v_a is close to v_best_word - v_c, as measured by cosine similarity
    """

    # convert words to lowercase
    word_a, word_b, word_c = word_a.lower(), word_b.lower(), word_c.lower()

    ### START CODE HERE ###
    # Get the word embeddings e_a, e_b and e_c (≈1-3 lines)
    e_a, e_b, e_c = (word_to_vec_map[word_a],word_to_vec_map[word_b],word_to_vec_map[word_c])
    ### END CODE HERE ###

    words = word_to_vec_map.keys()
    max_cosine_sim = -100              # Initialize max_cosine_sim to a large negative number
    best_word = None                   # Initialize best_word with None, it will help keep track of the word to output

    # to avoid best_word being one of the input words, skip the input words
    # place the input words in a set for faster searching than a list
    # We will re-use this set of input words inside the for-loop
    input_words_set = set([word_a, word_b, word_c])

    # loop over the whole word vector set
    for w in words:
        # to avoid best_word being one of the input words, skip the input words
        if w in input_words_set:
            continue

        ### START CODE HERE ###
        # Compute cosine similarity between the vector (e_b - e_a) and the vector ((w's vector representation) - e_c)  (≈1 line)
        cosine_sim = cosine_similarity(e_b-e_a,word_to_vec_map[w]-e_c)

        # If the cosine_sim is more than the max_cosine_sim seen so far,
            # then: set the new max_cosine_sim to the current cosine_sim and the best_word to the current word (≈3 lines)
        if cosine_sim > max_cosine_sim:
            max_cosine_sim = cosine_sim
            best_word = w
        ### END CODE HERE ###

    return best_word
# In[]
triads_to_try = [('italy', 'italian', 'spain'), ('india', 'delhi', 'japan'), ('man', 'woman', 'boy'), ('small', 'smaller', 'large')]
for triad in triads_to_try:
    print ('{} -> {} :: {} -> {}'.format( *triad, complete_analogy(*triad,word_to_vec_map)))
# In[]
g = word_to_vec_map['woman'] - word_to_vec_map['man']
print(g)

# In[]
print ('List of names and their similarities with constructed vector:')

# girls and boys name
name_list = ['john', 'marie', 'sophie', 'ronaldo', 'priya', 'rahul', 'danielle', 'reza', 'katy', 'yasmin']

for w in name_list:
    print (w, cosine_similarity(word_to_vec_map[w], g))
# In[]
print('Other words and their similarities:')
word_list = ['lipstick', 'guns', 'science', 'arts', 'literature', 'warrior','doctor', 'tree', 'receptionist',
             'technology',  'fashion', 'teacher', 'engineer', 'pilot', 'computer', 'singer']
for w in word_list:
    print (w, cosine_similarity(word_to_vec_map[w], g))
# In[]
def neutralize(word, g, word_to_vec_map):
    """
    Removes the bias of "word" by projecting it on the space orthogonal to the bias axis.
    This function ensures that gender neutral words are zero in the gender subspace.

    Arguments:
        word -- string indicating the word to debias
        g -- numpy-array of shape (50,), corresponding to the bias axis (such as gender)
        word_to_vec_map -- dictionary mapping words to their corresponding vectors.

    Returns:
        e_debiased -- neutralized word vector representation of the input "word"
    """

    ### START CODE HERE ###
    # Select word vector representation of "word". Use word_to_vec_map. (≈ 1 line)
    e = word_to_vec_map[word]

    # Compute e_biascomponent using the formula given above. (≈ 1 line)
    e_biascomponent = np.dot(e,g)/(np.sum(g**2))*g

    # Neutralize e by subtracting e_biascomponent from it
    # e_debiased should be equal to its orthogonal projection. (≈ 1 line)
    e_debiased = e-e_biascomponent
    ### END CODE HERE ###

    return e_debiased
# In[]
e = "receptionist"
print("cosine similarity between " + e + " and g, before neutralizing: ", cosine_similarity(word_to_vec_map["receptionist"], g))

e_debiased = neutralize("receptionist", g, word_to_vec_map)
print("cosine similarity between " + e + " and g, after neutralizing: ", cosine_similarity(e_debiased, g))
# In[]
def equalize(pair, bias_axis, word_to_vec_map):
    """
    Debias gender specific words by following the equalize method described in the figure above.

    Arguments:
    pair -- pair of strings of gender specific words to debias, e.g. ("actress", "actor")
    bias_axis -- numpy-array of shape (50,), vector corresponding to the bias axis, e.g. gender
    word_to_vec_map -- dictionary mapping words to their corresponding vectors

    Returns
    e_1 -- word vector corresponding to the first word
    e_2 -- word vector corresponding to the second word
    """

    ### START CODE HERE ###
    # Step 1: Select word vector representation of "word". Use word_to_vec_map. (≈ 2 lines)
    w1, w2 = pair[0],pair[1]
    e_w1, e_w2 = word_to_vec_map[w1],word_to_vec_map[w2]

    # Step 2: Compute the mean of e_w1 and e_w2 (≈ 1 line)
    mu = (e_w1+e_w2)/2

    # Step 3: Compute the projections of mu over the bias axis and the orthogonal axis (≈ 2 lines)
    mu_B = np.dot(mu,bias_axis)/(np.sum(bias_axis**2))*bias_axis
    mu_orth = mu-mu_B

    # Step 4: Use equations (7) and (8) to compute e_w1B and e_w2B (≈2 lines)
    e_w1B = np.dot(e_w1,bias_axis)/(np.sum(bias_axis**2))*bias_axis
    e_w2B = np.dot(e_w2,bias_axis)/(np.sum(bias_axis**2))*bias_axis

    # Step 5: Adjust the Bias part of e_w1B and e_w2B using the formulas (9) and (10) given above (≈2 lines)
    corrected_e_w1B = np.sqrt(np.abs(1-np.sum(mu_orth**2)))*(e_w1B-mu_B)/np.sqrt(np.sum(((e_w1-mu_orth)-mu_B)**2))
    corrected_e_w2B = np.sqrt(np.abs(1-np.sum(mu_orth**2)))*(e_w2B-mu_B)/np.sqrt(np.sum(((e_w2-mu_orth)-mu_B)**2))

    # Step 6: Debias by equalizing e1 and e2 to the sum of their corrected projections (≈2 lines)
    e1 = corrected_e_w1B+mu_orth
    e2 = corrected_e_w2B+mu_orth

    ### END CODE HERE ###
    return e1, e2
# In[]
print("cosine similarities before equalizing:")
print("cosine_similarity(word_to_vec_map[\"man\"], gender) = ", cosine_similarity(word_to_vec_map["man"], g))
print("cosine_similarity(word_to_vec_map[\"woman\"], gender) = ", cosine_similarity(word_to_vec_map["woman"], g))
print()
e1, e2 = equalize(("man", "woman"), g, word_to_vec_map)
print("cosine similarities after equalizing:")
print("cosine_similarity(e1, gender) = ", cosine_similarity(e1, g))
print("cosine_similarity(e2, gender) = ", cosine_similarity(e2, g))


import numpy as np

class LinearRegression:

    def __init__(self, x, y, alpha=3e-2, max_epochs=10, epsilon=1e-2,
                 batch_size=10):
        self.x = np.concatenate((np.ones((x.shape[0], 1)), x), axis=1)
        self.y = y
        self.alpha = alpha
        self.maxEpochs = max_epochs
        self.epsilon = epsilon
        self.batchSize = batch_size

        self.history = {
            'theta': [],
            'cost': [],
        }

        self.theta = np.random.normal(loc=0.0, scale=1.0, size=(self.x.shape[1], 1))
        self.history['theta'].append(self.theta.squeeze().tolist())

        initialPrediction = self.getPrediction(self.x, self.theta)
        initialCost = self.getCost(initialPrediction, self.y)
        self.history['cost'].append(initialCost)

        self.prevTheta = self.theta
        self.printStats()

    def printStats(self):
        print('=' * 80)
        print('[INFO]\t\tHyperparameters for Linear Regression')
        print('=' * 80)
        print(f'[INFO] Learning Rate: {self.alpha}')
        print(f'[INFO] Mini Batch Size: {self.batchSize}')
        print(f'[INFO] Maximum Epochs: {self.maxEpochs}')
        print(f'[INFO] Epsilon for checking convergence: {self.epsilon}')
        print(f'[INFO] Starting value of theta: {self.theta.tolist()} | {self.theta.shape}')
        print(f'[INFO] Shape of x data with ones: {self.x.shape}')
        print(f'[INFO] Shape of y data: {self.y.shape}')
        print(f'[INFO] Initial cost: {self.history["cost"][0]}')
        print('=' * 80)

    def runGradientDescent(self):

        xBatches = np.array_split(self.x, self.x.shape[0] // self.batchSize)
        yBatches = np.array_split(self.y, self.y.shape[0] // self.batchSize)

        for i in range(self.maxEpochs):
            for j, (x, y) in enumerate(zip(xBatches, yBatches)):
                # keeping track of prev theta for checking convergence
                self.prevTheta = self.theta

                h = self.getPrediction(x, self.theta)
                cost = self.getCost(h, y)
                gradients = (1 / x.shape[0]) * np.dot(np.transpose(x), (h - y))
                self.theta = self.theta - self.alpha * gradients

                # log metrics
                self.history['theta'].append(self.theta.squeeze().tolist())
                self.history['cost'].append(cost)

                if self.isConverged():
                    print(f'[INFO] Gradient Descent converged at Epoch: {i + 1}, iteration: {j + 1}')
                    break

            if self.isConverged():
                break

    def getThetaByNormalEquations(self):
        return np.dot(np.linalg.inv(np.dot(np.transpose(self.x), self.x)),
                      np.dot(np.transpose(self.x), self.y))

    def isConverged(self):
        return (abs(self.theta - self.prevTheta) <= self.epsilon).all()

    def getHistory(self):
        return self.history

    @staticmethod
    def getPrediction(x, theta):
        return np.dot(x, theta)

    @staticmethod
    def getCost(y_pred, y_true):
        m = y_pred.shape[0]
        return (1 / (2 * m)) * np.sum((y_pred - y_true)**2)


#! python3
# -*- coding:utf-8 -*-
'''
  ユニバーサルファンクション
  map()のように、ndarray内の各要素に一括で処理を行える
'''
import random
import math
import numpy as np

#abs 絶対値に変換する
np.random.seed(123)
print("abs")
l = np.random.randint(-10, 10, 5)
print(type(l))
print(np.abs(l))
#実はndarrayの場合は標準absでもできる。
#標準リストを標準absで処理しようとするとリストじゃないと怒られる。
print(abs(l))
#print(abs([ random.randint(-5,-1) for x in range(5) ]))
print("--------")

#sin, cos
#サイン、コサインに変換する
#度数法(0〜360)ではなく弧度法で指定しないといけない点に注意。
print("sin, cos")
#度数法で角度を設定
l = np.array([0, 30, 60, 90])
#弧度法に変換
l = l / 360 * math.pi * 2
#ユニバーサルファンクションを適用してみる
print(np.sin(l))
print(np.cos(l))
print(np.tan(l))
print("--------")

#おまけ　度数法と弧度法を互いに変換する便利関数がある。
print("radians " + str(np.radians(180)))
print("deg2rad " + str(np.deg2rad(180)))
print("rad2deg " + str(np.rad2deg(3.14)))

#平均、中央値、最瀕値
print("mean, median, mod")
l = np.array([1, 2, 2, 2, 3, 3, 4])
print(np.mean(l))
print(np.median(l))
#print(np.mod(l))
print("--------")

#積
print("prod")
print(np.prod(l))
print("--------")
#幾何平均
print("mean prod")
print(np.prod(l) ** (1/len(l)))
print("--------")

#四捨五入、切り捨て、切り上げ
print("round, trunc, floor, ceil")
# 配列 [-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0] を作成
a = np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0])
# 四捨五入 (小数点以下 .5 以上は繰上げ、.5未満は切捨て)
print(np.round(a))
# 切り捨て (小数部分を取り除く)
print(np.trunc(a))
# 切り捨て (小さい側の整数に丸める)
print(np.floor(a))
# 切り上げ (大きい側の整数に丸める)
print(np.ceil(a))
# ゼロに近い側の整数に丸める
print(np.fix(a))
print("--------")

#最大値、最小値
print("max, min")
print(np.max(a))
print(np.min(a))
print("--------")

#平方根
print("sqrt")
print("before " + str([1, 4, 9, 16]))
print("sqrt " + str(np.sqrt([1, 4, 9, 16])))
print("--------")


import numpy as np
from math import atan2, asin

def Euler2Quaternion(phi, theta, psi):
    cth = np.cos(theta/2)
    cph = np.cos(phi/2)
    cps = np.cos(psi/2)

    sth = np.sin(theta/2)
    sph = np.sin(phi/2)
    sps = np.sin(psi/2)

    e0 = cps*cth*cph+sps*sth*sph
    e1 = cps*cth*sph-sps*sth*cph
    e2 = cps*sth*cph+sps*cth*sph
    e3 = sps*cth*cph+cps*sth*sph
    e = np.array([e0,e1,e2,e3])
    return e

def Quaternion2Euler(e):
    e0 = e.item(0)
    e1 = e.item(1)
    e2 = e.item(2)
    e3 = e.item(3)

    phi = atan2(2*(e0*e1 + e2*e3),(e0**2+e3**2-e1**2-e2**2))
    theta = asin(2*(e0*e2-e1*e3))
    psi = atan2(2*(e0*e3 + e1*e2),(e0**2+e1**2-e2**2-e3**2))
    return [phi,theta,psi]

def Quaternion2Rotation(e):
    e0 = e.item(0)
    e1 = e.item(1)
    e2 = e.item(2)
    e3 = e.item(3)

    R = np.array([[e0**2 + e1**2 - e2**2 - e3**2, 2*(e1*e2 - e0*e3), 2*(e1*e3 + e0*e2)],
                  [2*(e1*e2 + e0*e3), e0**2 - e1**2 + e2**2 - e3**2, 2*(e2*e3 - e0*e1)],
                  [2*(e1*e3 - e0*e2), 2*(e2*e3 + e0*e1), e0**2 - e1**2 - e2**2 + e3**2]
                  ])
    return R

def Euler2Rotation(phi, theta, psi):
    # print("orig:", phi, theta, psi)
    # e = Euler2Quaternion(phi, theta, psi)
    # R = Quaternion2Euler(e)

    cph = np.cos(phi)
    sph = np.sin(phi)
    cth = np.cos(theta)
    sth = np.sin(theta)
    cps = np.cos(psi)
    sps = np.sin(psi)

    Rbv2 = np.array([[1., 0., 0.],\
                    [0., cph, sph],\
                    [0., -sph, cph]])

    Rv2v1 = np.array([[cth, 0., -sth],\
                    [0., 1., 0.],\
                    [sth, 0., cth]])

    Rv1i = np.array([[cps, sps, 0.],\
                    [-sps, cps, 0.],\
                    [0., 0., 1.]])

    R = Rbv2@Rv2v1@Rv1i

    return R.T


# Exercise 3
# The binomial distribution describes the statistics of the number of successful events for a Bernoulli experiment
# repeated N times, where the probability of success of each experiment is p.
# 1. Execute Ntr = 10000 trials, each made of N = 100 Bernoulli experiments with probability of success p = 0.05.
# [Hint: to test whether a Bernoulli experiment is successful or not, draw a random number u ∼ U(0, 1), and check if u ≤ p.]
# 2. For each trial i, count the number of successes si, and draw the empirical probability mass function (PMF)
# of the number of successes throughout all trials. Compare against the theoretical binomial PMF.
# 3. Compare the empirical and the theoretical binomial distributions against a Poisson distribution of parameter λ = N p.
# Repeat the comparison for different values of N and p. When does the Poisson PMF accurately approximate the binomial PMF?

# import
import numpy as np
from matplotlib import pyplot as plt
from numpy import mean, min, max, median, quantile
from scipy.stats import binom, norm, t as student, expon, poisson
from math import sqrt, pow, floor, ceil, exp

# define corrected functions for std and variance
def variance(values):
	return np.var(values, ddof=1)

def std(values):
	return np.std(values, ddof=1)

Ntr = 10000
N = 100
p = 0.05
trials = []
for i in range(Ntr):
	trial = []
	for l in range(N):
		if np.random.rand() <= p:
			trial.append(1)
		else:
			trial.append(0)
	trials.append(trial)

# count successes s_i and draw pmf
trials = np.array(trials)
trial_sums = np.sum(trials, axis=1)
plt.hist(trial_sums, bins = np.arange(0, trial_sums.max() + 1.5) - 0.5, density=True)

# compare against theoretical binomial distribution
X = np.arange(0, 20, 1)
Y = binom.pmf(X, N, p)
plt.plot(X, Y, 'k-', zorder=2)

# compare against a poisson with lambda = N*p
X = np.arange(0, 20, 1)
Y = poisson.pmf(X, N * p)
plt.plot(X, Y, 'r-', zorder=3)

Y = poisson.pmf(X, 2 * p * N)
plt.plot(X, Y, 'm-', zorder=3)

Y = poisson.pmf(X, 4 * p * N)
plt.plot(X, Y, 'y-', zorder=3)
plt.show()


import numpy as np
import pandas as pd
from scipy.spatial.distance import pdist, squareform
from tabulate import tabulate
import random

import networkx as nx
from pygsp import graphs, filters, plotting

#### Network
def gaussian_kernel(dist, sigma):
    return np.exp(-dist**2 / (2*sigma**2))

def get_adjacency(X: np.ndarray, dist_metric, sigma=1, epsilon=0):
    """ X (n x d): coordinates of the n data points in R^d.
        sigma (float): width of the kernel
        epsilon (float): threshold
        Return:
        adjacency (n x n ndarray): adjacency matrix of the graph.
    """
    dist = squareform(pdist(X, metric=dist_metric))
    adjacency = np.exp(- dist ** 2 / (2 * sigma ** 2))
    adjacency[adjacency < epsilon] = 0
    np.fill_diagonal(adjacency, 0)
    return adjacency

def graph_basic_stats(G):
    nodes_number = G.number_of_nodes()
    edges_number = G.number_of_edges()

    g_degree = G.degree()
    sum_degree = sum(dict(g_degree).values())
    average_degree = sum_degree / nodes_number

    tab = [
        ["Number of nodes", nodes_number],
        ["Number of edges", edges_number],
        ["Graph density", round(nx.classes.function.density(G) * 100, 2)],
        ["Average degree", round(average_degree, 2)],
        ["Number of connected components", nx.number_connected_components(G)],
        ["Average clustering coefficient", round(nx.average_clustering(G), 2)],
        ["Diameter of the network (longest shortest path)", nx.diameter(G)]
    ]
    print(tabulate(tab, tablefmt='fancy_grid'))

#### GSP
def compute_laplacian(adjacency: np.ndarray, normalize: bool):
    """ Return:
        L (n x n ndarray): combinatorial or symmetric normalized Laplacian.
    """
    D = np.diag(np.sum(adjacency, 1)) # Degree matrix
    combinatorial = D - adjacency
    if normalize:
        D_norm = np.diag(np.clip(np.sum(adjacency, 1), 1, None)**(-1/2))
        return D_norm @ combinatorial @ D_norm
    else:
        return combinatorial

def spectral_decomposition(laplacian: np.ndarray):
    """ Return:
        lamb (np.array): eigenvalues of the Laplacian
        U (np.ndarray): corresponding eigenvectors.
    """
    return np.linalg.eigh(laplacian)

def fit_polynomial(lam: np.ndarray, order: int, spectral_response: np.ndarray):
    """ Return an array of polynomial coefficients of length 'order'."""
    A = np.vander(lam, order, increasing=True)
    coeff = np.linalg.lstsq(A, spectral_response, rcond=None)[0]
    return coeff

def polynomial_graph_filter(coeff: np.array, laplacian: np.ndarray):
    """ Return the laplacian polynomial with coefficients 'coeff'. """
    power = np.eye(laplacian.shape[0])
    filt = coeff[0] * power
    for n, c in enumerate(coeff[1:]):
        power = laplacian @ power
        filt += c * power
    return filt

#### GCNN
def get_masks(nb_nodes, test_ratio, seed=None):
    ''' Return the indices for the train and test sets
    '''
    if seed is not None:
        np.random.seed(seed)

    nb_test = int(nb_nodes*test_ratio)
    test_mask = np.sort(random.sample(range(0, nb_nodes), nb_test))
    train_mask = list(set(np.arange(0, nb_nodes))^set(test_mask))

    return train_mask, test_mask

def compute_accuracy(y, y_hat):
    assert(len(y)==len(y_hat)), 'y and y_hat must have the same length'
    return np.sum(y_hat == y) / (len(y) - int(pd.DataFrame(y).isna().sum())) # to deal with NaNs


import numpy as np
import cv2 # You must not use any methods which has 'hough' in it!
from utils import  hough_peaks

def hough_lines_vote_acc(edge_img, rho_res=1, thetas= np.arange(0,180)):
    h,w=edge_img.shape[:2]
    d=int(np.round(np.sqrt(h**2+w**2)))
    y,x=np.nonzero(edge_img>0)
    A=np.zeros((2*d+1,len(thetas)))
    thetas1=np.deg2rad(thetas)
    rhos=np.arange(0,2*d)
    #rhos=[]

    for i in range(len(x)):
        for theta in thetas1:
            rho=np.int(np.ceil(x[i]*np.cos(theta)+y[i]*np.sin(theta)))
            A[rho,np.int(np.round(np.rad2deg(theta)))]+=1
            #rhos.append(rho)

    return A, thetas, rhos

def hough_circles_vote_acc(edge_img, radius):
    r=radius
    h,w=edge_img.shape[:2]
    edges=edge_img>0
    y_idxs,x_idxs=np.nonzero(edges)
    A=np.zeros((h,w))
    theta=np.linspace(-180,180,360)
    for i in range(len(x_idxs)):
        x=x_idxs[i]
        y=y_idxs[i]
        for t in theta:
            b=y-r*np.sin(t)
            a=x+r*np.cos(t)
            if a<h and a>0 and b<w and b>0:
                A[int(b),int(a)]+=1

    return A

def find_circles(edge_img, radius_range=[1,2], threshold=100, nhood_size=10):
    """
      A naive implementation of the algorithm for finding all the circles in a range.
      Feel free to write your own more efficient method [Extra Credit].
      For extra credit, you may need to add additional arguments.

      Args
      - edge_img: numpy nd-array of dim (m, n).
      - radius_range: range of radius. All cicles whose radius falls
      in between should be selected.
      - nhood_size: size of the neighborhood from where only one candidate can be chosen.

      Returns
      - centers, and radii i.e., (x, y) coordinates for each circle.

      HINTS:
      - I encourage you to use this naive version first. Just be aware that
       it may take a long time to run. You will get EXTRA CREDIT if you can write a faster
       implementaiton of this method, keeping the method signature (input and output parameters)
       unchanged.
    """
    n = radius_range[1] - radius_range[0]
    H_size = (n,) + edge_img.shape
    H = np.zeros(H_size, dtype=np.uint)
    centers = ()
    radii = np.arange(radius_range[0], radius_range[1])
    valid_radii = np.array([], dtype=np.uint)
    num_circles = 0
    for i in range(len(radii)):
        H[i] = hough_circles_vote_acc(edge_img, radii[i])
        peaks = hough_peaks(H[i], numpeaks=10, threshold=threshold,
                            nhood_size=nhood_size)
        if peaks.shape[0]:
            valid_radii = np.append(valid_radii, radii[i])
            centers = centers + (peaks,)
            for peak in peaks:
                cv2.circle(edge_img, tuple(peak[::-1]), radii[i]+1, (0,0,0), -1)
        #  cv2.imshow('image', edge_img); cv2.waitKey(0); cv2.destroyAllWindows()
        num_circles += peaks.shape[0]
        print('Progress: %d%% - Circles: %d\033[F\r'%(100*i/len(radii), num_circles))
    print('Circles detected: %d          '%(num_circles))
    centers = np.array(centers)
    return centers, valid_radii.astype(np.uint)


from dataclasses import dataclass
import numpy as np
from numpy import linalg as nla, ndarray
from functools import cached_property

from config import DEBUG

def isPSD(arr: np.ndarray) -> bool:
    return np.allclose(arr, arr.T) and np.all(np.linalg.eigvals(arr) >= 0)

@dataclass(frozen=True)
class MultiVarGaussian:
    """A class for using Gaussians"""
    mean: ndarray  # shape=(n,)
    cov: ndarray  # shape=(n, n)

    def __post_init__(self):
        if DEBUG:
            assert self.mean.shape * 2 == self.cov.shape
            assert np.all(np.isfinite(self.mean))
            assert np.all(np.isfinite(self.cov))
            assert isPSD(self.cov)

    @cached_property
    def ndim(self) -> int:
        return self.mean.shape[0]

    @cached_property
    def scaling(self) -> float:
        scaling = (2*np.pi)**(-self.ndim/2) * nla.det(self.cov)**(-1/2)
        return scaling

    def mahalanobis_distance_sq(self, x: np.ndarray) -> float:
        """Calculate the mahalanobis distance between self and x.

        This is also known as the quadratic form of the Gaussian.
        See (3.2) in the book.
        """
        # this method could be vectorized for efficient calls
        error = x - self.mean
        mahalanobis_distance = error.T @ nla.solve(self.cov, error)
        return mahalanobis_distance

    def pdf(self, x):
        density = self.scaling*np.exp(-self.mahalanobis_distance_sq(x)/2)
        return density

    def marginalize(self, idxs):
        return MultiVarGaussian(self.mean[idxs], self.cov[idxs][:, idxs])

    def __iter__(self):  # in order to use tuple unpacking
        return iter((self.mean, self.cov))

    def __eq__(self, o: object) -> bool:
        if not isinstance(o, MultiVarGaussian):
            return False
        return np.allclose(self.mean, o.mean) and np.allclose(self.cov, o.cov)


from __future__ import division
import pandas as pd
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
import numpy.linalg as npla
import scipy.linalg as spla
import scipy.special

# Code may print some warnings due to pandas in Python2.
# These warning are benign. Verigy here: https://stackoverflow.com/questions/40845304/runtimewarning-numpy-dtype-size-changed-may-indicate-binary-incompatibility

z = 20

J_0 = scipy.special.jv(0, z)
J_1 = scipy.special.jv(1, z)

bessel_val_by_rec = [J_0, J_1]

for i in range(2, 51):

    J_i = ( ( ( ( 2 * ( i - 1 ) ) / z ) * bessel_val_by_rec[i - 1] ) - bessel_val_by_rec[i - 2] )

    bessel_val_by_rec.append(J_i)

bessel_val_by_func = [scipy.special.jv(i, z) for i in range(0, 51)]

relative_err = []
for i in range(2, 51):
    relative_err.append((bessel_val_by_rec[i] - bessel_val_by_func[i])/bessel_val_by_func[i])

# Again, just printing magnitude of the Relative Error
# Comment this value to print just the ratio
relative_err = [abs(i) for i in relative_err]

df = pd.DataFrame({"n" : [i for i in range(2, 51)], "From Function" : bessel_val_by_func[2:], "From Recurrence" : bessel_val_by_rec[2:], "Relative Error" : relative_err}, columns=["n", "From Function", "From Recurrence", "Relative Error"])
print(df.to_string(index_names=False))

plt.xlabel("Value of n")
plt.ylabel("Absolute Value of Relative Error")
plt.plot( np.arange(2, 51), relative_err)
plt.savefig("problem_3b.png")
# Uncomment to show plot
# plt.show()


# coding: utf-8

# In[1]:

import numpy as np
import matplotlib.pyplot as plt
get_ipython().magic('matplotlib inline')

# In[2]:

def logistic(r, x):
    return r * x * (1 - x)

# In[3]:

x = np.arange(0, 1.02,0.02)
plt.plot(x, logistic(2, x), 'b')
plt.show()

# In[10]:

def plot_map(r, x0, n):
    t = np.linspace(0, 1)
    plt.plot(t, logistic(r, t), 'k', lw=2)
    plt.plot([0, 1], [0, 1], 'k', lw=2)
    x = x0
    for i in range(n):
        y = logistic(r, x)
        plt.plot([x, x], [x, y], 'k', lw=1)
        plt.plot([x, y], [y, y], 'k', lw=1)
        plt.plot([x], [y], 'ok', ms=10, alpha=(i + 1) / n)
        x = y

plot_map(2.5, .1, 10)

# In[11]:

plot_map(3.9, .1, 80)

# In[6]:

n = 10000
r = np.linspace(2.5, 4.0, n)

# In[7]:

iterations = 1000
last = 100

# In[8]:

x = 1e-5 * np.ones(n)
lyapunov = np.zeros(n)

# In[9]:

for i in range(iterations):
    x = logistic(r, x)
    if i >= (iterations - last):
        plt.plot(r, x, ',k',alpha=0.25)


from builtins import print

from numpy import *

'''
add any value to each element to the array
arr1 = array([1,2,3,4,7])

arr1+=5
print(arr1)
'''

'''
add two array also called vectorized operation
arr1 = array([1,2,3,4,5])
arr2 = array([6,7,8,9,10])

print("Addition is : " , arr1 + arr2)

'''

arr1 = array([2, 1, 45, 3, 21, 12, 2, 1])
arr2 = array([12, 54, 65, 32, 1, 8, 3, 2, 4])
print("sin : ", sin(arr1))
print("cos : ", cos(arr1))
print("log : ", log(arr1))
print("sqrt : ", sqrt(arr1))
print("sum : ", sum(arr1))
print("min : ", min(arr1))
print("max : ", max(arr1))
# check it print(subtract(arr1-1))
print("sort : ", sort(arr1))
print("unique : ", unique(arr1))
print("concatenate : ", sort(concatenate([arr1, arr2])))
#print("subtract : ",subtract(arr1-1))

# Creating array object
arr = array([[1, 2, 3],
                [4, 2, 5]])

# Printing type of arr object
print("Array is of type: ", type(arr))

# Printing array dimensions (axes)
print("No. of dimensions: ", arr.ndim)

# Printing shape of array rows * cols
print("Shape of array: ", arr.shape)

# Printing size (total number of elements) of array
print("Size of array: ", arr.size)

# Printing type of elements in array
print("Array stores elements of type: ", arr.dtype)


# This example describe how to integrate ODEs with scipy.integrate module and how
# to use the matplotlib module to plot trajectories, directions field and others
# useful informations.
#
# == Presentation of the Lokta-Volterra Model ==
#
#
# We will have a look at the Lokta-Volterra model, laso known as the
# predator-prey equations, re a pair of first order, non-linear, differential
# equations frequently used to describe the dynamics of biological systems in
# which two species interact, one a predator and one its prey. They were proposed
# independently by Alfred J. Lotka in 1925 and Vito Volterra in 1926 :
# du/dt =  a*u -   b*u*v
# dv/dt = -c*v + d*b*u*v
#
# with the following notations :
#
# *  u : number of prey (for example rabbits)
#
# *  v : number of predators (for example foxes)
#
# * a, b, c, d are constant parameters defining the behavior of the population :
#
#   + a is the natural growing rate of rabbits, when there's no foxes
#
#   + b is the natural dying rate of rabbits, due to predation
#
#   + c is the natural dying rate of foxes, when there's no rabbits
#
#   + d is the factor descibing how many catched rabbits let create new rabbits
#
#
# We will use X=[u, v] to describe the state of both populations.
# Definition of the equations:
#
from numpy import *
import pylab as p

# Parameters
a = 1.
b = 0.1
c = 1.5
d = 0.75

def dX_dt(X, t=0):
    """ Return the growth rate of foxes and rabbits populations. """
    return array([ a*X[0] -   b*X[0]*X[1] ,
                  -c*X[1] + d*b*X[0]*X[1] ])
#
# === Population equilibrium ===
#
# Before using scipy to integrate this system, we will have a closer look on
# position equilibrium. Equilibrium occurs when the growth rate is equal to 0.
# This gives two fixed points:
#
X_f0 = array([0., 0.])
X_f1 = array([ c/(d*b), a/b])
all(dX_dt(X_f0) == zeros(2) ) and all(dX_dt(X_f1) == zeros(2)) # => True
#
# === Stability of the fixed points ===
# Near theses two points, the system can be linearized :
# dX_dt = A_f*X where A is the Jacobian matrix evaluated at the corresponding point.
# We have to define the Jacobian matrix:
#
def d2X_dt2(X, t=0):
    """ Return the jacobian matrix evaluated in X. """
    return array([[a -b*X[1],   -b*X[0]     ],
                  [b*d*X[1] ,   -c +b*d*X[0]] ])
#
# So, near X_f0, which represents the extinction of both species, we have:
# A_f0 = d2X_dt2(X_f0)                    # >>> array([[ 1. , -0. ],
#                                         #            [ 0. , -1.5]])
#
# Near X_f0, the number of rabbits increase and the population of foxes decrease.
# X_f0 is a [http://en.wikipedia.org/wiki/Saddle_point saddle point].
#
# Near X_f1, we have :
A_f1 = d2X_dt2(X_f1)                    # >>> array([[ 0.  , -2.  ],
                                        #            [ 0.75,  0.  ]])

# whose eigenvalues are +/- sqrt(ca).j :
lambda1, lambda2 = linalg.eigvals(A_f1) # >>> (1.22474j, -1.22474j)

# They are imaginary number, so the fox and rabbit populations are periodic and
# their period is given by :
T_f1 = 2*pi/abs(lambda1)                # >>> 5.130199
#
# == Integrating the ODE using scipy.integate ==
#
# Now we will use the scipy.integrate module to integrate the ODE.
# This module offers a  method named odeint, very easy to use to integrade ODE:
#
from scipy import integrate

t = linspace(0, 15,  1000)              # time
X0 = array([10, 5])                     # initials conditions: 10 rabbits and 5 foxes

X, infodict = integrate.odeint(dX_dt, X0, t, full_output=True)
infodict['message']                     # >>> 'Integration successful.'
#
# `infodict` is optionnal, and you can omit the `full_output` argument if you don't want it.
# type "info(odeint)" if you want more information about odeint inputs and outputs.
#
# We will use matplotlib to plot the evolution of both populations:
#
rabbits, foxes = X.T

f1 = p.figure()
p.plot(t, rabbits, 'r-', label='Rabbits')
p.plot(t, foxes  , 'b-', label='Foxes')
p.grid()
p.legend(loc='best')
p.xlabel('time')
p.ylabel('population')
p.title('Evolution of fox and rabbit populations')
f1.savefig('rabbits_and_foxes_1.png')
#
#
# The populations are indeed periodic, and their period is near to the T_f1 we calculated.
#
# == Plotting directions field and trajectories in the phase plane ==
#
# We will plot some trajectories in a phase plane for different starting
# points between X__f0 and X_f1.
#
# We will ue matplotlib's colormap to define colors for the trajectories.
# These colormaps are very useful to make nice plots.
# Have a look on ShowColormaps if you want more information.
#
values = linspace(0.3, 0.9, 5)                          # position of X0 between X_f0 and X_f1
vcolors = p.cm.Greens(linspace(0.3, 1., len(values)))   # colors for each trajectory

f2 = p.figure()

#-------------------------------------------------------
# plot trajectories
for v, col in zip(values, vcolors):
    X0 = v * X_f1                               # starting point
    X = integrate.odeint( dX_dt, X0, t)         # we don't need infodict here
    p.plot( X[:,0], X[:,1], lw=3.5*v, color=col, label='X0=(%.f, %.f)' % ( X0[0], X0[1]) )

#-------------------------------------------------------
# define a grid and compute direction at each point
ymax = p.ylim(ymin=0)[1]                        # get axis limits
xmax = p.xlim(xmin=0)[1]
nb_points   = 20

x = linspace(0, xmax, nb_points)
y = linspace(0, ymax, nb_points)

X1 , Y1  = meshgrid(x, y)                       # create a grid
DX1, DY1 = dX_dt([X1, Y1])                      # compute growth rate on the gridt
M = (hypot(DX1, DY1))                           # Norm of the growth rate
M[ M == 0] = 1.                                 # Avoid zero division errors
DX1 /= M                                        # Normalize each arrows
DY1 /= M

#-------------------------------------------------------
# Drow direction fields, using matplotlib 's quiver function
# I choose to plot normalized arrows and to use colors to give information on
# the growth speed
p.title('Trajectories and direction field')
Q = p.quiver(X1, Y1, DX1, DY1, M, pivot='mid', cmap=p.cm.autumn)
p.xlabel('Number of Rabbits')
p.ylabel('Number of Foxes')
p.legend()
p.grid()
p.xlim(0, xmax)
p.ylim(0, ymax)
f2.savefig('rabbits_and_foxes_2.png')
#
#
# We can see on this graph that an intervention on fox or rabbit populations can
# have non intuitive effects. If, in order to decrease the number of rabbits,
# we introduce foxes, this can lead to an increase of rabbits in the long run,
# if that intervention happens at a bad moment.
#
#
# == Plotting contours ==
#
# We can verify that the fonction IF defined below remains constant along a trajectory:
#
def IF(X):
    u, v = X
    return u**(c/a) * v * exp( -(b/a)*(d*u+v) )

def IF2(X):
    u, v = X
    return u**(c/a) * v * exp( -(b/a)*(d*u+v) )

# We will verify that IF remains constant for differents trajectories
for v in values:
    X0 = v * X_f1                               # starting point
    X = integrate.odeint( dX_dt, X0, t)
    I = IF(X.T)                                 # compute IF along the trajectory
    I_mean = I.mean()
    delta = 100 * (I.max()-I.min())/I_mean
    print 'X0=(%2.f,%2.f) => I ~ %.1f |delta = %.3G %%' % (X0[0], X0[1], I_mean, delta)

# >>> X0=( 6, 3) => I ~ 20.8 |delta = 6.19E-05 %
#     X0=( 9, 4) => I ~ 39.4 |delta = 2.67E-05 %
#     X0=(12, 6) => I ~ 55.7 |delta = 1.82E-05 %
#     X0=(15, 8) => I ~ 66.8 |delta = 1.12E-05 %
#     X0=(18, 9) => I ~ 72.4 |delta = 4.68E-06 %
#
# Potting iso-contours of IF can be a good representation of trajectories,
# without having to integrate the ODE
#
#-------------------------------------------------------
# plot iso contours
nb_points = 80                              # grid size

x = linspace(0, xmax, nb_points)
y = linspace(0, ymax, nb_points)

X2 , Y2  = meshgrid(x, y)                   # create the grid
Z2 = IF([X2, Y2])                           # compute IF on each point

f3 = p.figure()
CS = p.contourf(X2, Y2, Z2, cmap=p.cm.Purples_r, alpha=0.5)
CS2 = p.contour(X2, Y2, Z2, colors='black', linewidths=2. )
p.clabel(CS2, inline=1, fontsize=16, fmt='%.f')
p.grid()
p.xlabel('Number of Rabbits')
p.ylabel('Number of Foxes')
p.ylim(1, ymax)
p.xlim(1, xmax)
p.title('IF contours')
f3.savefig('rabbits_and_foxes_3.png')
p.show()
#
#
# # vim: set et sts=4 sw=4:


import numpy as np
import matplotlib
import copy
np.set_printoptions(threshold=np.nan)
np.set_printoptions(precision=16)

def matgen(m,condno):
    [U,X] = np.linalg.qr(np.random.randn(m,m))
    [V,X] = np.linalg.qr(np.random.randn(m,m))
    S = np.diag(condno**((1-np.linspace(1,m,m))/(m-1)))
    return U@S@V

def exercise_1():
    ge_err,inv_err,cr_err = [],[],[]
    m = 20
    for condno in [0,4,8,12,16]:

        A = matgen(m,10**condno)
        xtrue = np.random.rand(m)
        b = A@xtrue

        x_ge = np.linalg.solve(A,b)
        ge_err.append([condno, np.linalg.norm(x_ge-xtrue)/np.linalg.norm(xtrue),
               np.linalg.norm(b-A@x_ge)/(np.linalg.norm(A)*np.linalg.norm(x_ge))])

        Ainv =  np.linalg.inv(A)
        x_inv = Ainv@b
        inv_err.append([condno, np.linalg.norm(x_inv-xtrue)/np.linalg.norm(xtrue),
               np.linalg.norm(b-A@x_inv)/(np.linalg.norm(A)*np.linalg.norm(x_inv))])

        detA = np.linalg.det(A)
        x_cr = np.zeros(m)
        for j in range(m):
            A_j = copy.deepcopy(A)
            A_j[:,j] = b
            x_cr[j] = np.linalg.det(A_j)/detA
        cr_err.append([condno, np.linalg.norm(x_cr-xtrue)/np.linalg.norm(xtrue),
               np.linalg.norm(b-A@x_cr)/(np.linalg.norm(A)*np.linalg.norm(x_cr))])

    return [ge_err,inv_err,cr_err]

def exercise_2():
    m = 60
    A = np.tril(np.full((m,m),-1),-1)+np.identity(m)
    A[:,m-1] = np.full(m,1)

    x = np.random.randn(m,1)
    b = A@x

    x_ge = np.linalg.solve(A,b)

    [Q,R] = np.linalg.qr(A)
    x_qr = np.linalg.solve(R,Q.T@b)

#    plt.scatter(np.log10(x-x_ge),range(m))
    return [np.linalg.cond(A,2),np.linalg.norm(x-x_ge,2),np.linalg.norm(x-x_qr,2)]


#!/usr/bin/env python
# coding: utf-8

# # Vector manipulation in Python
#
# In this lab, you will have the opportunity to practice once again with the NumPy library. This time, we will explore some advanced operations with arrays and matrices.
#
# At the end of the previous module, we used PCA to transform a set of many variables into a set of only two uncorrelated variables. This process was made through a transformation of the data called rotation.
#
# In this week's assignment, you will need to find a transformation matrix from English to French vector space embeddings. Such a transformation matrix is nothing else but a matrix that rotates and scales vector spaces.
#
# In this notebook, we will explain in detail the rotation transformation.

# ## Transforming vectors
#
# There are three main vector transformations:
# * Scaling
# * Translation
# * Rotation
#
# In previous notebooks, we have applied the first two kinds of transformations. Now, let us learn how to use a fundamental transformation on vectors called _rotation_.
#
# The rotation operation changes the direction of a vector, letting unaffected its dimensionality and its norm. Let us explain with some examples.
#
# In the following cells, we will define a NumPy matrix and a NumPy array. Soon we will explain how this is related to matrix rotation.

# In[1]:

import numpy as np                     # Import numpy for array manipulation
import matplotlib.pyplot as plt        # Import matplotlib for charts
from utils_nb import plot_vectors      # Function to plot vectors (arrows)

# ### Example 1

# In[2]:

# Create a 2 x 2 matrix
R = np.array([[2, 0],
              [0, -2]])

# In[3]:

x = np.array([[1, 1]]) # Create a 1 x 2 matrix

# The dot product between a vector and a square matrix produces a rotation and a scaling of the original vector.
#
# Remember that our recommended way to get the dot product in Python is np.dot(a, b):

# In[4]:

y = np.dot(x, R) # Apply the dot product between x and R
y

# We are going to use Pyplot to inspect the effect of the rotation on 2D vectors visually. For that, we have created a function `plot_vectors()` that takes care of all the intricate parts of the visual formatting. The code for this function is inside the `utils_nb.py` file.
#
# Now we can plot the vector $\vec x = [1, 1]$ in a cartesian plane. The cartesian plane will be centered at `[0,0]` and its x and y limits will be between `[-4, +4]`

# In[5]:

plot_vectors([x], axes=[4, 4], fname='transform_x.svg')

# Now, let's plot in the same system our vector $\vec x = [1, 1]$ and its dot product with the matrix
#
# $$Ro = \begin{bmatrix} 2 & 0 \\ 0 & -2 \end{bmatrix}$$
#
# $$y = x \cdot Ro = [[2, -2]]$$

# In[6]:

plot_vectors([x, y], axes=[4, 4], fname='transformx_and_y.svg')

# Note that the output vector `y` (blue) is transformed in another vector.

# ### Example 2
#
# We are going to use Pyplot to inspect the effect of the rotation on 2D vectors visually. For that, we have created a function that takes care of all the intricate parts of the visual formatting. The following procedure plots an arrow within a Pyplot canvas.
#
# Data that is composed of 2 real attributes is telling to belong to a $ RxR $ or $ R^2 $ space. Rotation matrices in $R^2$ rotate a given vector $\vec x$ by a counterclockwise angle $\theta$ in a fixed coordinate system. Rotation matrices are of the form:
#
# $$Ro = \begin{bmatrix} cos \theta & -sin \theta \\ sin \theta & cos \theta \end{bmatrix}$$
#
# **(Note:** This notebook uses $$y = x \cdot Ro$$ But if you use $$y = Ro \cdot x.T$$
#
# Then the rotation matrices in $R^2$ rotate a given vector $\vec x$ by a clockwise angle $\theta$ in a fixed coordinate system.**)**
#
# The trigonometric functions in Numpy require the angle in radians, not in degrees. In the next cell, we define a rotation matrix that rotates vectors by $100^o$.

# In[7]:

angle = 100 * (np.pi / 180) #convert degrees to radians

Ro = np.array([[np.cos(angle), -np.sin(angle)],
              [np.sin(angle), np.cos(angle)]])

x2 = np.array([2, 2]).reshape(1, -1) # make it a row vector
y2 = np.dot(x2, Ro)

print('Rotation matrix')
print(Ro)
print('\nRotated vector')
print(y2)

print('\n x2 norm', np.linalg.norm(x2))
print('\n y2 norm', np.linalg.norm(y2))
print('\n Rotation matrix norm', np.linalg.norm(Ro))

# In[8]:

plot_vectors([x2, y2], fname='transform_02.svg')

# Some points to note:
#
# * The norm of the input vector is the same as the norm of the output vector. Rotations matrices do not modify the norm of the vector, only its direction.
# * The norm of any $R^2$ rotation matrix is always $\sqrt 2 = 1.414221$

# ## Frobenius Norm
#
# The Frobenius norm is the generalization to $R^2$ of the already known norm function for vectors
#
# $$\| \vec a \| = \sqrt {{\vec a} \cdot {\vec a}} $$
#
# For a given $R^2$ matrix A, the frobenius norm is defined as:
#
# $$\|\mathrm{A}\|_{F} \equiv \sqrt{\sum_{i=1}^{m} \sum_{j=1}^{n}\left|a_{i j}\right|^{2}}$$
#

# In[9]:

A = np.array([[2, 2],
              [2, 2]])

# `np.square()` is a way to square each element of a matrix. It must be equivalent to use the * operator in Numpy arrays.

# In[10]:

A_squared = np.square(A)
A_squared

# Now you can sum over the elements of the resulting array, and then get the square root of the sum.

# In[11]:

A_Frobenius = np.sqrt(np.sum(A_squared))
A_Frobenius

# That was the extended version of the `np.linalg.norm()` function. You can check that it yields the same result.

# In[12]:

print('Frobenius norm of the Rotation matrix')
print(np.sqrt(np.sum(Ro * Ro)), '== ', np.linalg.norm(Ro))


# -*- coding: utf-8 -*-
"""
Created on Fri Oct  1 21:39:53 2021

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

x=np.linspace(-10,10,50)
f =  x**2 + 4*x - 12
function = lambda x: x**2 + 4*x - 12
plt.plot(x,f,'g')
input_a =  int(input("Please give value of a: "))
input_b =  int(input("Please give value of b: "))

def bisection(f,a,b,N):
    if function(a)*function(b) >= 0:
        print("Bisection method fails.")
        return None
    a_n = a
    b_n = b
    for n in range(1,N+1):
        m_n = (a_n + b_n)/2
        f_m_n = function(m_n)
        if function(a_n)*f_m_n < 0:
                a_n = a_n
                b_n = m_n
        elif function(b_n)*f_m_n < 0:
                a_n = m_n
                b_n = b_n
        elif f_m_n == 0:
            print(f"Found exact solution after {n} tries")
            return m_n
        else:
            print("Bisection method fails.")
            return None
    return (a_n + b_n)/2

start = timeit.default_timer()
approx_phi = bisection(f,input_a,input_b,25)
stop = timeit.default_timer()
print('Time of original bisection without  error: ', stop - start)
print(f'The zero is at: {approx_phi}')


#########################
##                     ##
## Irving Gomez Mendez ##
##  February 12, 2021  ##
##                     ##
#########################

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import t

# We want to adjust the next function
#y = B + C cos(x) + D sin(x) + E cos(2x) + F sin(2x) + G cos(3x) + H sin(3x)

points = np.array([[-4,-1],[-3,0],[-2,-1.5],[-1,0.5],[0,1],[1,-1],[2,-0.5,],[3,2],[4,-1]])

points_x = points[:,0]
points_y = points[:,1]
cos_x  = np.cos(points_x)
sin_x  = np.sin(points_x)
cos_2x = np.cos(2*points_x)
sin_2x = np.sin(2*points_x)
cos_3x = np.cos(3*points_x)
sin_3x = np.sin(3*points_x)

n = points.shape[0]
yy = points_y
XX = np.vstack([np.ones(n),cos_x,sin_x,cos_2x,sin_2x,cos_3x,sin_3x]).T
p = XX.shape[1]

# We compute the coeff.
B, C, D, E, F, G, H = np.linalg.lstsq(XX, yy, rcond=None)[0]
params  = [B, C, D, E, F, G, H]

# Calculate the SSE
SSE = np.linalg.lstsq(XX, yy, rcond=None)[1]

# We get confidence interval
alpha = 0.05
x0 = np.linspace(-4.5, 4.5, 50)
X0 = np.vstack([np.ones(len(x0)),np.cos(x0),np.sin(x0),
    np.cos(2*x0),np.sin(2*x0),np.cos(3*x0),np.sin(3*x0)]).T

aux_t_conf = np.sqrt(SSE/(n-p)*(np.diag(X0 @ np.linalg.inv(XX.T @ XX) @ X0.T)))
yy0_hat = X0 @ np.array(params)
upp_conf = yy0_hat+t.ppf(1-alpha/2,n-p)*aux_t_conf
low_conf = yy0_hat-t.ppf(1-alpha/2,n-p)*aux_t_conf

# We get prediction interval
aux_t_pred = np.sqrt(SSE/(n-p)*(1+np.diag(X0 @ np.linalg.inv(XX.T @ XX) @ X0.T)))
yy0_hat = X0 @ np.array(params)
upp_pred = yy0_hat+t.ppf(1-alpha/2,n-p)*aux_t_pred
low_pred = yy0_hat-t.ppf(1-alpha/2,n-p)*aux_t_pred

plt.figure(figsize=(10,5))
plt.plot(points_x, points_y, 'o', label='Original data', markersize=5)
plt.plot(x0, B+C*np.cos(x0)+D*np.sin(x0)+E*np.cos(2*x0)+F*np.sin(2*x0)+G*np.cos(3*x0)+H*np.sin(3*x0),
    'r', label='Fitted trigonometric function')
plt.fill_between(x0, low_pred, upp_pred, facecolor='green', alpha=0.5, label='Prediction interval')
plt.fill_between(x0, low_conf, upp_conf, facecolor='yellow', alpha=0.5, label='Confidence interval')
plt.legend(loc='upper left')


import numpy as np
from numpy import log, nan_to_num

class CrossEntropyCost:
    @staticmethod
    def evaluate(activation, true_label):
        """Return the cost associated with an output activation and desired output
        ``y``.

        The cross-entropy is positive number.
        Note that np.nan_to_num is used to ensure numerical
        stability.  In particular, if both ``a`` and ``y`` have a 1.0
        in the same slot, then the expression (1-y)*np.log(1-a)
        returns nan.  The np.nan_to_num ensures that that is converted
        to the correct value (0.0).

        """
        return np.sum(nan_to_num(-true_label * log(activation) - (1 - true_label) * log(1 - activation)))

    @staticmethod
    def delta(z, activation, true_label):
        """Computes the error delta from the output layer.

        Note that the  parameter ``z`` is not used by the method.
        It is included in the method's parameters in order to make the interface
        consistent with the delta method for other cost classes.
        """
        return activation - true_label

class QuadraticCost:
    @staticmethod
    def evaluate(activation, true_label):
        """Return the cost associated with an output activation and desired true label (``y``)."""
        return 0.5 * np.linalg.norm(activation - true_label) ** 2

    @staticmethod
    def delta(z, activation, true_label):
        """Computes the error delta from the output layer."""
        return (activation - true_label) * QuadraticCost.sigmoid_prime(z)

    @staticmethod
    def derivative(output_activations, y):
        r"""Return the vector of partial derivatives

        \partial C_x / \partial a for the output activations."""
        return output_activations - y


import numpy as np

# Conval
def conv2(X, k):
    x_row, x_col = X.shape
    k_row, k_col = k.shape
    ret_row, ret_col = x_row - k_row + 1, x_col - k_col + 1

    ret = np.empty((ret_row, ret_col))
    for y in range(ret_row):
        for x in range(ret_col):
            sub = X[y: y + k_row, x: x + k_col]
            ret[y, x] = np.sum(k * sub)
    return ret

def conv3(X, k):
    x_row, x_col, x_ch = X.shape
    k_row, k_col = k.shape
    ret_row, ret_col, ret_ch = x_row - k_row + 1, x_col - k_col + 1, x_ch

    ret = np.empty((ret_row, ret_col, ret_ch))
    for c in range(ret_ch):
        for y in range(ret_row):
            for x in range(ret_col):
                sub = X[y: y + k_row, x: x + k_col, c: c+1]
                ret[y, x, c] = np.sum(k * sub.reshape(k_row, k_col))
    return ret

# MAX & MEAN Pooling
def pooling_2d(X, mode=np.max, size=2):
    x_row, x_col = X.shape
    ret_row, ret_col = x_row // size, x_col // size + 1

    ret = np.empty((ret_row, ret_col))
    for i1, y in enumerate(range(0, x_row, size)):
        for i2, x in enumerate(range(0, x_col, size)):
            sub = X[y: y + size, x: x + size]
            ret[i1, i2] = mode(sub)

    return ret

def pooling_3d(X, mode=np.max, size=2):
    x_row, x_col, x_ch = X.shape
    ret_row, ret_col = x_row // size, x_col // size + 1

    ret = np.empty((ret_row, ret_col, x_ch))
    for c in range(x_ch):
        for i1, y in enumerate(range(0, x_row, size)):
            for i2, x in enumerate(range(0, x_col, size)):
                sub = X[y: y + size, x: x + size, c: c + 1]
                ret[i1, i2, c] = mode(sub)

    return ret

# add Padding
def padding_2d(X, k_size=3):
    x_row, x_col = X.shape
    pad_size = (k_size - 1) // 2

    ret = np.zeros((x_row + pad_size*2, x_col+ pad_size*2))
    ret[pad_size: x_row + pad_size, pad_size: x_col + pad_size] = X[:, :]

    return ret

def padding_3d(X, k_size=3):
    x_row, x_col, x_ch = X.shape
    pad_size = (k_size - 1) // 2

    ret = np.zeros((x_row + pad_size*2, x_col+ pad_size*2, x_ch))
    ret[pad_size: x_row + pad_size, pad_size: x_col + pad_size, :] = X[:, :, :]

    return ret


"""
Math library example
"""
import numpy as np

def euler(n):

    """Function to calculate Euler's number :math:`e` thorugh Taylor series

    .. math::

        e = 1 + \\sum_n^\\infty \\frac{1}{n!}

    Parameters
    ----------
    n : int
        Specify the order of the truncated series

    Returns
    -------
    e_value : float
        Euler number
    """

    if n < 0:
        raise ValueError("Only positive integers are allowed")
    n += 1
    e_value = 0
    for i in range(n):
        e_value += 1 / factorial(i)
    return e_value

def factorial(n):
    if n > 0:
        return n * factorial(n - 1)
    else:
        return 1

def pi(mc_points=1e7):

    """Function to calculate :math:`\pi` using Monte-Carlo

    Parameters
    ----------
    mc_points : int
        Specify the number of points for the Monte Carlo integration

    Returns
    -------
    result : float
        Estimated value of :math:`\pi`
    """
    mc_points = int(mc_points)
    x = np.random.uniform(0, 1, mc_points).reshape(-1, 2)
    area = np.sum(np.linalg.norm(x, axis=1) < 1)
    result = (area / mc_points) * 8
    return result


#!/usr/bin/env python

import numpy as np

def sigmoid(x):
    """
    Compute the sigmoid function for the input here.

    Arguments:
    x -- A scalar or numpy array.

    Return:
    s -- sigmoid(x)
    """

    ### YOUR CODE HERE
    s = 1 / (1 + np.exp(-1 * x))
    ### END YOUR CODE

    return s

def sigmoid_grad(s):
    """
    Compute the gradient for the sigmoid function here. Note that
    for this implementation, the input s should be the sigmoid
    function value of your original input x.

    Arguments:
    s -- A scalar or numpy array.

    Return:
    ds -- Your computed gradient.
    """

    ### YOUR CODE HERE
    ds = s * (1 - s)
    ### END YOUR CODE

    return ds

def test_sigmoid_basic():
    """
    Some simple tests to get you started.
    Warning: these are not exhaustive.
    """
    print "Running basic tests..."
    x = np.array([[1, 2], [-1, -2]])
    f = sigmoid(x)
    g = sigmoid_grad(f)
    print f
    f_ans = np.array([
        [0.73105858, 0.88079708],
        [0.26894142, 0.11920292]])
    assert np.allclose(f, f_ans, rtol=1e-05, atol=1e-06)
    print g
    g_ans = np.array([
        [0.19661193, 0.10499359],
        [0.19661193, 0.10499359]])
    assert np.allclose(g, g_ans, rtol=1e-05, atol=1e-06)
    print "You should verify these results by hand!\n"

def test_sigmoid():
    """
    Use this space to test your sigmoid implementation by running:
        python q2_sigmoid.py
    This function will not be called by the autograder, nor will
    your tests be graded.
    """
    print "Running your tests..."
    ### YOUR CODE HERE
    raise NotImplementedError
    ### END YOUR CODE

if __name__ == "__main__":
    test_sigmoid_basic();
    #test_sigmoid()


#!/usr/local/bin/python
#-*- coding: utf-8 -*-
#
# Author:   kentarowada
# Mail:     www.kentaro.wada@gmail.com
# URL:      http://wkentaro.com
# Created:  2014-06-25
# Filename: my_inv.py
#
import numpy as np

def my_det(arr):
    n = len(arr)
    for i in range(n):
        for j in range(n):
            if i < j:
                tmp = arr[j][i] / arr[i][i]
                for k in range(n):
                    arr[j][k] -= arr[i][k]*tmp

    det = 1.0
    for i in range(n):
        det *= arr[i][i];
    return det

def my_inv(arr):
    arr = np.array(arr, dtype=np.float64)
    n = len(arr)
    iarr = np.identity(n)

    # 掃き出し法
    for i in range(n):
        tmp = 1 / arr[i][i]
        for j in range(n):
            arr[i][j] *= tmp
            iarr[i][j] *= tmp
        for j in range(n):
            if i != j:
                tmp = arr[j][i]
                for k in range(n):
                    arr[j][k] -= arr[i][k] * tmp
                    iarr[j][k] -= iarr[i][k] * tmp
    return iarr

def my_output_inv(arr):
    arr = np.array(arr)
    print "A-->"
    print arr

    det = my_det(arr)
    print "determinant -->",
    print det

    if int(det) == 0:
        print "--> No inverse array"
        return

    iarr = my_inv(arr)
    print "A inverse -->"
    print iarr

def main():
    arr = np.array([[1, 2],
                    [3, 4]])
    my_output_inv(arr)

    print "--"

    arr2 = np.array([[3, 2],
                     [6, 4]])
    my_output_inv(arr2)

if __name__ == '__main__':
    main()


import numpy as np
from numpy.random import default_rng

def residual_sum_of_squares(predictions, y_values):
    """
    Calculate the Residual Sum of Squares (RSS)

    The sum of the squares of the residuals and the residuals is
    the difference between the predicted output and the true output.

    Returns:
        the RSS value
    """
    # then compute the residuals (since we are squaring it doesn't matter which order you subtract)
    residuals = predictions - y_values
    # square the residuals and add them up
    return np.sum(residuals * residuals)

def predict_output(feature_matrix: np.ndarray, weights: np.ndarray) -> np.ndarray:
    """
    Predict the regression value for N input elements

    Args:
        feature_matrix: a 2D numpy matrix containing the features as columns
        weights: a 1D numpy array of the corresponding feature weights

    Returns:
        a 1D numpy array with the respective model prediction
    """
    return np.dot(feature_matrix, weights.T)

# def predict_output(feature_matrix: np.ndarray, weights: np.ndarray) -> np.ndarray:
#     """
#     Create a regression prediction vector by using dot product
#
#     Args:
#         feature_matrix: a 2D numpy array with features as columns
#         weights: a 1D numpy array of the corresponding feature weights
#
#     Returns:
#         a 1D numpy array with the respective model prediction
#     """
#     predictions = np.dot(feature_matrix, weights)
#     return predictions

def random_init(size, seed=12345678903141592653589793):
    rng = default_rng(seed)
    return rng.standard_normal(size)


import numpy as np

from typing import Tuple
from numpy.typing import ArrayLike
from liegroups.numpy import SO2, SE2, SO3, SE3
from numpy import sin, cos

def angle_to_se2(a: float, theta: float) -> SE2:
    """Transform a single set of DH parameters into an SE2 matrix
    :param a: link length
    :param theta: rotation
    :returns: SE2 matrix
    :rtype: lie.SE2Matrix
    """
    # R = SO2.from_angle(theta)  # TODO: active or passive (i.e., +/- theta?)
    R = SO2.from_angle(theta)
    return SE2(R, R.dot(np.array([a, 0.0])))  # TODO: rotate the translation or not?

def skew(x):
    """
    Creates a skew symmetric matrix from vector x
    """
    X = np.array([[0, -x[2], x[1]], [x[2], 0, -x[0]], [-x[1], x[0], 0]])
    return X

def trans_axis(t, axis="z") -> SE3:
    if axis == "z":
        return SE3(SO3.identity(), np.array([0, 0, t]))
    if axis == "y":
        return SE3(SO3.identity(), np.array([0, t, 0]))
    if axis == "x":
        return SE3(SO3.identity(), np.array([t, 0, 0]))
    raise Exception("Invalid Axis")

def rot_axis(theta, axis="z") -> SE3:
    if axis == "z":
        return SE3(SO3.rotz(theta), np.array([0, 0, 0]))
    if axis == "y":
        return SE3(SO3.roty(theta), np.array([0, 0, 0]))
    if axis == "x":
        return SE3(SO3.rotx(theta), np.array([0, 0, 0]))
    raise Exception("Invalid Axis")

def max_min_distance_revolute(r, P, C, N):
    delta = P-C
    d_min_s = N.dot(delta)**2 + (np.linalg.norm(np.cross(N, delta)) - r)**2
    if d_min_s > 0:
        d_min = np.sqrt(d_min_s)
    else:
        d_min = 0
    d_max_s = N.dot(delta)**2 + (np.linalg.norm(np.cross(N, delta)) + r)**2
    if d_max_s > 0:
        d_max = np.sqrt(d_max_s)
    else:
        d_max = 0

    return d_max, d_min

def best_fit_transform(A: ArrayLike, B: ArrayLike) -> Tuple[ArrayLike, ArrayLike]:
    """
    Calculates the least-squares best-fit transform that maps corresponding points A to B in m spatial dimensions
    Input:
      A: Nxm numpy array of corresponding points
      B: Nxm numpy array of corresponding points
    Returns:
      R: mxm rotation matrix
      t: mx1 translation vector
    """

    # try:
    assert A.shape == B.shape
    # except AssertionError:
    #     print("A: {:}".format(A))
    #     print("B: {:}".format(B))

    # get number of dimensions
    m = A.shape[1]

    # translate points to their centroids
    centroid_A = np.mean(A, axis=0)
    centroid_B = np.mean(B, axis=0)

    AA = A - centroid_A
    BB = B - centroid_B

    # rotation matrix
    H = np.dot(AA.T, BB)
    U, S, Vt = np.linalg.svd(H)
    R = np.dot(Vt.T, U.T)
    # translation
    #
    # special reflection case
    # if np.linalg.det(R) < 0:
    #     print("det(R) < R, reflection detected!, correcting for it ...\n")
    # Vt[2, :] *= -1
    # R = np.dot(Vt.T, U.T)

    t = centroid_B.T - np.dot(R, centroid_A.T)
    return R, t


#! /usr/bin/python

import sys, re
from scipy.optimize.optimize import fmin_cg, fmin_bfgs, fmin
import numpy as np
import matplotlib.pyplot as plt
from numpy import loadtxt, where, zeros, e, array, log, ones, mean, where
from pylab import scatter, show, legend, xlabel, ylabel, plot
from scipy.optimize import fmin_bfgs
import math

def sigmoid(X):
   g=1/(1+np.exp(-X))
   return g

def costFunction(theta,X,y):
   theta.shape = (1, 3)
   m = y.size
   h = sigmoid(X.dot(theta.conj().transpose()))
   first = ((-y).T.dot(log(h)))
   second = (1-y).T.dot(log(1-h))
   J =(first - second)/m
   return J.sum()

def gradFunction(theta,X,y):
   theta.shape = (1, 3)
   grad = zeros(3)
   h = sigmoid(X.dot(theta.conj().transpose()))
   delta = h - y
   l = grad.size
   for i in range(l):
      sumdelta = delta.conj().transpose().dot(X[:, i])
      grad[i] = (1.0 / m) * sumdelta * (-1)
   theta.shape = (3,)
   return grad

data = loadtxt('ex2data1.txt', delimiter=',')
X = data[:, 0:2]
y =  data[:, 2]
pos = where(y == 1)
neg = where(y == 0)
scatter(X[pos, 0], X[pos, 1], marker='o', c='b')
scatter(X[neg, 0], X[neg, 1], marker='x', c='r')
xlabel('X')
ylabel('Y')
legend(['X', 'Y'])

m, n = X.shape
y.shape = (m, 1)
i = ones(shape=(m, 3))
i[:, 1:3] = X

def learning_parameters(i, y):
    def f(theta):
        return costFunction(theta, i, y)

    def fprime(theta):
        return gradFunction(theta, i, y)
    theta = zeros(3)
    return fmin_bfgs(f, theta, fprime, disp=True, maxiter=400)

learning_parameters(i, y)
theta = [-25.161272, 0.206233, 0.201470]

plot_x = array([min(i[:, 1]) - 2, max(i[:, 2]) + 2])
plot_y = (-1/theta[2]) * (theta[1] * plot_x + theta[0])

plot(plot_x, plot_y)
legend(['Decision', 'Admitted', 'Not-Admitted'])
show()

prob = sigmoid(array([1.0, 45.0, 85.0]).dot(array(theta).conj().transpose()))
print 'Probability: %f' % prob

def predict(theta,X):
   m, n = X.shape
   p = zeros(shape=(m, 1))
   h = sigmoid(X.dot(theta.conj().transpose()))

   for i in range(0, h.shape[0]):
        if h[i] > 0.5:
            p[i, 0] = 1
        else:
            p[i, 0] = 0
   return p

p = predict(array(theta), i)
print "Train Accuracy:",((y[where(p == y)].size / float(y.size)) * 100.0)


"""Exercise 1"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from scipy.signal import butter, lfilter
from scipy import fftpack

def gaussian(x, mu, sigma, A):
    return A*np.exp(-(x - mu)**2/(2*sigma**2))

sample_rate = 30
T = 5
t = np.linspace(0, T, T*sample_rate, endpoint=False)
y = gaussian(t, 3.1, 0.2, 3) + np.sin(1.2*2*np.pi*t) + 1.5*np.cos(9*2*np.pi*t)
plt.plot(t, y)

z = fftpack.rfft(y)
f = fftpack.rfftfreq(len(t), t[1] - t[0])

maxima = f[np.abs(z)**2 > 2100]
print("Noise frequencies:", maxima)

plt.figure(1)
plt.plot(f, np.abs(z)**2)
plt.xlabel('f / Hz')
for freq in maxima:
    plt.axvline(x=freq, color='black', alpha=0.3, lw=5)
plt.title('Discovery of Noise Frequencies')

def bandstop_filter(data, freq_window, fs, order=5):
    nyquist_frequency = fs/2
    freq_window = np.array(freq_window)
    normal_freq = freq_window/nyquist_frequency
    b, a = butter(order, normal_freq, btype='bandstop')
    y = lfilter(b, a, data)
    return y

plt.figure(2)
sample_rate = (len(t) - 1)/(t[-1])
y_filt = bandstop_filter(y, [1.15, 1.25], sample_rate)
y_filt = bandstop_filter(y_filt, [8.9, 9.1], sample_rate)
plt.plot(t, y_filt)
plt.title('Data after bandstop filtering')

params, __ = curve_fit(gaussian, t, y_filt)
print("Gaussian paramaters:", params)

plt.figure(3)
plt.plot(t, gaussian(t, *params))
plt.plot(t, y_filt)
plt.title('Filtered data fit with Gaussian');

"""Exercise 2"""

from scipy.special import eval_legendre
from scipy.integrate import quad

def f(x):
    return np.sin(np.pi*x)

def a(n):
    g = lambda x: f(x)*eval_legendre(n, x)
    integral = quad(g, -1, 1)[0]
    return (2*n + 1)/2 * integral

# first few a_n coefficients up to n = 5
print("Coefficients:", [a(n) for n in range(5+1)])

def legendre_series(x, N):
    y = np.zeros(len(x))
    for n in range(N+1):
        y += a(n)*eval_legendre(n, x)
    return y

x = np.linspace(-1, 1, 100)
plt.plot(x, f(x), label=r'$\sin(nx)$')
plt.plot(x, legendre_series(x, 5), label=r'$\sum_{n=0}^{5} a_n P_n(x)$')
plt.legend(loc='best');


import numpy as np
import polyFit
import scipy
from scipy import interpolate
import matplotlib.pyplot as plt

h=[0.0,1.525,3.050,4.575,6.1,7.625,9.15] #x values
den=[1,0.8617,0.7385,0.6292,0.5328,0.4481,0.3741] #y values
hrang=np.linspace(0,9.15,100)

print 'Using Polynomial Interpolation: ', '\n'

l_001=interpolate.barycentric_interpolate(h,den,(2,5)) #Using interpolate from scipy. Uses barycentric polynomial interpolation. Arguments: (x values, y values, points at which you wish to evaluate)

print 'At 2km the density of air will be ', l_001[0]

print 'At 5km the density of air will be ', l_001[1]

###
print '\n', 'Using Cubic Splines: ', '\n'

l_002=interpolate.interp1d(h,den,kind='cubic') #Interpolatioon from scipy. Uses cubic splines. Arguments: (x values, y values, order of splines)

print 'At 2km the density of air will be ', l_002(2) #Calls for interpolation to be evaluated at argument

print 'At 5km the density of air will be ', l_002(5)

###

print '\n', 'Using Least Squares Fit: ', '\n'

l=polyFit.polyFit(h,den,2) #Using polyFit module to generate a list of coefficients. Arguments: (x values, y values, degree of polynomial)

def l_003(x): #defining a function for the polynomial generated by polyFit
	l_003=l[0]+l[1]*x+l[2]*x**2
	return l_003

print 'At 2km the density of air will be ', l_003(2) #using function as defined above

print 'At 5km the density of air will be ', l_003(5)

###

denl_001=interpolate.barycentric_interpolate(h,den,hrang)
denl_002=l_002(hrang)
denl_003=l_003(hrang)

plt.figure(1)
plt.plot(hrang,denl_001,'r-',2,interpolate.barycentric_interpolate(h,den,2),'bo',5,interpolate.barycentric_interpolate(h,den,5),'bo')
plt.axis([0,10,0.2,1])
plt.xlabel('Height (km)')
plt.ylabel('Air Density')
plt.title('Barycentric Interpolation')
plt.show()

plt.figure(2)
plt.plot(hrang,denl_002,'r-',2,l_002(2),'bo',5,l_002(5),'bo')
plt.axis([0,10,0.2,1])
plt.xlabel('Height (km)')
plt.ylabel('Air Density')
plt.title('Cubic Splines')
plt.show()

plt.figure(3)
plt.plot(hrang,denl_003,'r-',2,l_003(2),'bo',5,l_003(5),'bo')
plt.axis([0,10,0.2,1])
plt.xlabel('Height (km)')
plt.ylabel('Air Density')
plt.title('Least Squares Fit')
plt.show()

plt.figure(4)
plt.plot(h,den,'ro')
plt.axis([0,10,0.2,1])
plt.xlabel('Height (km)')
plt.ylabel('Air Density')
plt.title('Original Data')
plt.show()

ExamPrep/04 Curve Fitting & Interpolation/shigInterp.py


# Accepts a filename containing numeric values in each line of which the statistics need to be calculated.
# Alternately as second argument, the bucket size can also be passed.
import sys
import numpy as np

fileName = sys.argv[1]
# find median of bucket splits of data
buckets = 5

if len(sys.argv) == 3:
    # Assuming the 2nd argument is the bucket size
    # if it exists
    buckets = int(sys.argv[2])

data = np.fromfile(fileName,sep="\n")
nmap = len(data) # Number of hashes used.
print("(mean of {} hashes) = {}".format(nmap,np.mean(data)))

# Finding mean of medians and vice-versa now
idxes = np.arange(nmap)
np.random.shuffle(idxes)
# numpy.array_split allows unequal splits of data
groups = np.array_split(idxes, buckets)

mean_of_medians = np.mean([np.median(data[group_idxs]) for group_idxs in groups])
median_of_means = np.median([np.mean(data[group_idxs]) for group_idxs in groups])
print("mean of medians (from {} buckets of data) : {}".format(buckets, mean_of_medians))
print("median of means (from {} buckets of data) : {}".format(buckets, median_of_means))
print("Standard Deviation : {}".format(np.std(data)))


import numpy as np

def f1(x, y, z):
    res = x + 3 * y + 2 * z
    return res, res == 5

def f2(x, y, z):
    res = 2 * x + -y + -z
    return res, res == 1

def f3(x, y, z):
    res = -x + 2 * y + z
    return res, res == 3

x = 0.75
y = 3.25
z = -2.75

print(f1(x, y, z))
print(f2(x, y, z))
print(f3(x, y, z))

b = np.array([5, 1, 3])
A = np.array([
    [1, 3, 2],
    [2, -1, -1],
    [-1, 2, 1]
])

sol2 = np.linalg.solve(A, b)
print(sol2)
print(A.dot(sol2))

# Ax = b
# inv(A)x = inv(A)b
# x = inv(A)b

print(np.linalg.inv(A).dot(b))

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

B2 = np.array([5,7,11])

print(np.linalg.solve(A2, B2))


## module jacobi
''' lam,x = jacobi(a,tol = 1.0e-8).
    Solution of std. eigenvalue problem [a]{x} = lam{x}
    by Jacobi's method. Returns eigenvalues in vector {lam}
    and the eigenvectors as columns of matrix [x].
'''
import numpy as np
import math

def jacobi(a,tol = 1.0e-8): # Jacobi method

    def threshold(a):
        sum = 0.0
        for i in range(n-1):
            for j in range (i+1,n):
                sum = sum + abs(a[i,j])
        return 0.5*sum/n/(n-1)

    def rotate(a,p,k,l): # Rotate to make a[k,l] = 0
        aDiff = a[l,l] - a[k,k]
        if abs(a[k,l]) < abs(aDiff)*1.0e-36: t = a[k,l]/aDiff
        else:
            phi = aDiff/(2.0*a[k,l])
            t = 1.0/(abs(phi) + math.sqrt(phi**2 + 1.0))
            if phi < 0.0: t = -t
        c = 1.0/math.sqrt(t**2 + 1.0); s = t*c
        tau = s/(1.0 + c)
        temp = a[k,l]
        a[k,l] = 0.0
        a[k,k] = a[k,k] - t*temp
        a[l,l] = a[l,l] + t*temp
        for i in range(k):      # Case of i < k
            temp = a[i,k]
            a[i,k] = temp - s*(a[i,l] + tau*temp)
            a[i,l] = a[i,l] + s*(temp - tau*a[i,l])
        for i in range(k+1,l):  # Case of k < i < l
            temp = a[k,i]
            a[k,i] = temp - s*(a[i,l] + tau*a[k,i])
            a[i,l] = a[i,l] + s*(temp - tau*a[i,l])
        for i in range(l+1,n):  # Case of i > l
            temp = a[k,i]
            a[k,i] = temp - s*(a[l,i] + tau*temp)
            a[l,i] = a[l,i] + s*(temp - tau*a[l,i])
        for i in range(n):      # Update transformation matrix
            temp = p[i,k]
            p[i,k] = temp - s*(p[i,l] + tau*p[i,k])
            p[i,l] = p[i,l] + s*(temp - tau*p[i,l])

    n = len(a)
    p = np.identity(n,float)
    for k in range(20):
        mu = threshold(a)       # Compute new threshold
        for i in range(n-1):    # Sweep through matrix
            for j in range(i+1,n):
                if abs(a[i,j]) >= mu:
                    rotate(a,p,i,j)
        if mu <= tol: return np.diagonal(a),p
    print('Jacobi method did not converge')


from __future__ import division
import numpy as np
from scipy.sparse import spdiags
from scipy.sparse.linalg import spsolve, cg

def general_secondorder_ode_fd(func,a1,a2,a3,a=0.,b=1.,alpha=1.,beta=3.,N=5):
	# A Simple Finite Difference Scheme to solve BVP's of the form
	# a1(x)u''(x) + a2(x)u'(x) + a3(x)u(x) = f(x), x \in [a,b]
	# u(a) = alpha
	# u(b) = beta
	# (Dirichlet boundary conditions)
	#
	# U_0 = alpha, U_1, U_2, ..., U_m, U_{m+1} = beta
	# We use m+1 subintervals, giving m algebraic equations
    m = N-1
    h = (b-a)/(m+1.)         # Here we form the diagonals
    D0,Dp,Dm,diags = np.zeros((1,m)), np.zeros((1,m)), np.zeros((1,m)), np.array([0,-1,1])
    for j in range(1,D0.shape[1]):
		xj = a + (j)*h
		D0[0,j]   = h**2.*a3(xj)-2.*a1(xj)
		Dp[0,j]   = a1(xj)-h*a2(xj)/2.
		Dm[0,j-1] = a1(xj)+h*a2(xj)/2.
    # xj = a + 1.*h
    # D0[0,0] = h**2.*a3(xj)-2.*a1(xj)

    # Here we create the matrix A
    data = np.concatenate((D0,Dm,Dp),axis=0) # This stacks up rows
    A=h**(-2.)*spdiags(data,diags,m,m).asformat('csr')

	# Here we create the vector B
    B = np.zeros(m+2)
    for j in range(2,m):
        B[j] = func(a + j*h)
    xj = a+1.*h
    B[0], B[1] = alpha, func(xj)-alpha *( a1(xj)*h**(-2.) + a2(xj)*h**(-1)/2. )
    xj = a+m*h
    B[-1], B[-2]  = beta, func(xj)-beta*( a1(xj)*h**(-2.) - a2(xj)*h**(-1)/2. )

    # Here we solve the equation AX = B and return the result
    B[1:-1] = spsolve(A,B[1:-1])
    return np.linspace(a,b,m+2), B

# def general_secondorder_ode_fd(func,a1,a2,a3,a=0.,b=1.,alpha=1.,beta=3.,N=5):
# 	# A Simple Finite Difference Scheme to solve BVP's of the form
# 	# a1(x)u''(x) + a2(x)u'(x) + a3(x)u(x) = f(x), x \in [a,b]
# 	# u(a) = alpha
# 	# u(b) = beta
# 	# (Dirichlet boundary conditions)
# 	#
# 	# U_0 = alpha, U_1, U_2, ..., U_m, U_{m+1} = beta
# 	# We use m+1 subintervals, giving m algebraic equations
#     m = N-1
#     h = (b-a)/(m+1.)         # Here we form the diagonals
#     D0,D1,D2,diags = np.zeros((1,m)), np.zeros((1,m)), np.zeros((1,m)), np.array([0,-1,1])
#     for j in range(1,D1.shape[1]):
# 		xj = a + (j+1)*h
# 		D0[0,j] = h**2.*a3(xj)-2.*a1(xj)
# 		D1[0,j] = a1(xj)+h*a2(xj)/2.
# 		D2[0,j-1] = a1(xj)-h*a2(xj)/2.
#     xj = a + 1.*h
#     D0[0,0] = h**2.*a3(xj)-2.*a1(xj)
#
#     # Here we create the matrix A
#     data = np.concatenate((D0,D2,D1),axis=0) # This stacks up rows
#     A=h**(-2.)*spdiags(data,diags,m,m).asformat('csr')
#
# 	# Here we create the vector B
#     B = np.zeros(m+2)
#     for j in range(2,m):
#         B[j] = func(a + j*h)
#     xj = a+1.*h
#     B[0], B[1] = alpha, func(xj)-alpha *( a1(xj)*h**(-2.) - a2(xj)*h**(-1)/2. )
#     xj = a+m*h
#     B[-1], B[-2]  = beta, func(xj)-beta*( a1(xj)*h**(-2.) + a2(xj)*h**(-1)/2. )
#
#     # Here we solve the equation AX = B and return the result
#     B[1:-1] = spsolve(A,B[1:-1])
#     return np.linspace(a,b,m+2), B
#


import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats

# (a)
n_1 = 50
n_2 = 50
x_1 = 40
x_2 = 30
p_hat_1 = x_1 / n_1
p_hat_2 = x_2 / n_2
tau_hat = p_hat_1 - p_hat_2
tau_se_hat = np.sqrt(
    p_hat_1 * (1 - p_hat_1) / n_1 + p_hat_2 * (1 - p_hat_2) / n_2
)
alpha = 0.1
z_alpha = stats.norm.ppf(1 - alpha / 2)
ci = (tau_hat - z_alpha * tau_se_hat, tau_hat + z_alpha * tau_se_hat)
print(f"MLE for tau is {tau_hat:.4}.")
print(f"Standard error for tau is {tau_se_hat:.4}.")
print(f"90% CI for tau is [{ci[0]:.4}, {ci[1]:.4}].")
print()

# (b)
sims = 5_000
rng = np.random.default_rng(42)
x_1_draws = rng.binomial(n_1, p_hat_1, size=sims)
x_2_draws = rng.binomial(n_2, p_hat_2, size=sims)
tau_draws = x_1_draws / n_1 - x_2_draws / n_2
bootstrap_ci = (
    np.quantile(tau_draws, alpha / 2),
    np.quantile(tau_draws, 1 - alpha / 2),
)
print(
    "90% parametric bootstrap CI for tau is",
    f"[{bootstrap_ci[0]:.4}, {bootstrap_ci[1]:.4}].",
)
print()

# (c)
taus = np.arange(-n_2, n_1 + 1)
p_1_posterior_draws = rng.beta(x_1 + 1, n_1 - x_1 + 1, size=sims)
p_2_posterior_draws = rng.beta(x_2 + 1, n_2 - x_2 + 1, size=sims)
tau_posterior_draws = p_1_posterior_draws - p_2_posterior_draws
tau_posterior_mean = tau_posterior_draws.mean()
posterior_ci = (
    np.quantile(tau_posterior_draws, alpha / 2),
    np.quantile(tau_posterior_draws, 1 - alpha / 2),
)
print(f"The posterior mean for tau is {tau_posterior_mean:.4}.")
print(
    "90% posterior CI for tau is",
    f"[{posterior_ci[0]:.4}, {posterior_ci[1]:.4}].",
)
print()

# (d)
psi_hat = np.log((p_hat_1 / (1 - p_hat_1)) / (p_hat_2 / (1 - p_hat_2)))
psi_se_hat = np.sqrt(
    1 / (n_1 * (p_hat_1 - p_hat_1 ** 2)) + 1 / (n_2 * (p_hat_2 ** 2 - p_hat_2))
)
psi_ci = (psi_hat - z_alpha * psi_se_hat, psi_hat + z_alpha * psi_se_hat)
print(f"MLE for psi is {psi_hat:.4}.")
print(f"Standard error for psi is {psi_se_hat:.4}.")
print(f"90% CI for psi is [{psi_ci[0]:.4}, {psi_ci[1]:.4}].")
print()

# (e)
psi_posterior_draws = np.log(
    (p_1_posterior_draws / (1 - p_1_posterior_draws))
    / (p_2_posterior_draws / (1 - p_2_posterior_draws))
)
psi_posterior_mean = psi_posterior_draws.mean()
psi_posterior_ci = (
    np.quantile(psi_posterior_draws, alpha / 2),
    np.quantile(psi_posterior_draws, 1 - alpha / 2),
)
print(f"The posterior mean for psi is {psi_posterior_mean:.4}.")
print(
    "90% posterior CI for psi is",
    f"[{psi_posterior_ci[0]:.4}, {psi_posterior_ci[1]:.4}].",
)


import numpy as np
import scipy.linalg as la

def constructor_matrix(M):
  """
  Building matrix
  """
  return np.matrix(M).transpose()

def minimum_squares(X, Y):
  """
  That function shows least squares of the values
  """
  media_X = np.mean(X)
  media_Y = np.mean(Y)
  erro_x = X-media_X
  erro_y = Y-media_Y
  soma_erro_xy = np.sum(erro_x*erro_y)
  erro_x_quadratico = (X-media_X)**2.0
  soma_erro_x_quadratico = np.sum(erro_x_quadratico)
  m = soma_erro_xy / soma_erro_x_quadratico
  c = media_Y - m*media_X
  reta = m*X+c

  return {
    'media_X': media_X,
    'media_Y': media_Y,
    'erro_x': erro_x,
    'erro_y': erro_y,
    'soma_erro_xy': soma_erro_xy,
    'erro_x_quadratico': erro_x_quadratico,
    'soma_erro_x_quadratico': soma_erro_x_quadratico,
    'm': m,
    'c': c,
    'reta': reta
  }

def plu(A):
  """
  This function shows PLU
  (permutation matrices, lower triangular and upper triangular)
  """
  (P, L, U) = la.lu(A)
  return {
    'P': P,
    'L': L,
    'U': U
  }

def autovalores_autovetores(A):
  """
  That function uses eigenvalues and eigenvectors
  to build the espectral decomposition
  """
  autovalores, autovetores = np.linalg.eig(A)
  return {
    'autovalores': autovalores,
    'autovetores': autovetores
  }

def espectral(autovetores, matrizDiagonal):
  """
  Espectral Decomposition
  """
  return np.matmul(np.matmul(autovetores,matrizDiagonal),np.linalg.inv(autovetores))

def pvd(A):
  """
  That function return the singular values decomposition
  """
  (U,s,V) = np.linalg.svd(A)
  return {
    'U': U,
    's': s,
    'V': V
  }

def back_substitution(A, x, n):
  """
  That function shows back substitution values of a matrix
  """
  b = np.dot(A, x)
  xcomp = np.zeros(n)

  for i in range(n-1, -1, -1):
      tmp = b[i]
      for j in range(n-1, i, -1):
          tmp -= xcomp[j]*A[i,j]

      xcomp[i] = tmp/A[i,i]

  return xcomp


"""
64-point Radix 2 DIF FFT implementation
by zeeshan0309

input here is the sampled signal of the CT function
{3.Sin(16pi.t)+24.Sin(2pi.t)+8.Sin(6pi.t)}
"""

from matplotlib import pyplot as plt
import math
from math import sqrt
import numpy as np

pi = math.pi
def sine(num):
    return math.sin(num)

def cosine(num):
    return math.cos(num)

#twiddle factors W_64 from 0 to 31 (only 32 Twiddle factors required for 64-point DFT)
twiddle = []
N = 64
x = [i for i in range(0,64)]

for i in range(0, 32):
    t_element = complex(cosine(2*pi*i/64), sine(2*pi*i/64))
    twiddle.append(t_element)

#generating random noise using numpy
noise = 3*np.random.randn(N)

#sampling {3.Sin(16pi.t)+24.Sin(2pi.t)+8.Sin(6pi.t)} at fs = 64Hz
# at t = {0, 1/64, 2/64, 3/64, ..., 63/64}
inputSignal = []

for i in range(0, 64):
    s_element = 3*sine(16*pi*i/64)+24*sine(2*pi*i/64)+8*sine(6*pi*i/64)
    inputSignal.append(s_element)

#adding Noise to sample
for i in range(0, N):
    inputSignal[i] += noise[i]

#function for evaluating Even indices term
#def evenAddTerms()
def evenAddTerms(arr, N):
    evenListOut = []
    temp_e_arr = [0]*int(N/2)
    temp_e_arr = arr
    for i in range(0, int(N/2)):
        even_element = temp_e_arr[i]+temp_e_arr[i+int(N/2)]
        evenListOut.append(even_element)
    return evenListOut

#function for evaluating Odd indices term
#def oddSubTerms()
def oddSubTerms(arr, N, twiddle):
    oddListOut = []
    temp_o_arr = [0]*int(N/2)
    temp_o_arr = arr
    for i in range(0, int(N/2)):
        odd_element = (temp_o_arr[i]-temp_o_arr[i+int(N/2)])*twiddle[int((64*i)/N)]
        oddListOut.append(odd_element)
    return oddListOut

#lists for output of each stage (no of stages = log(64) = 6)
stage1 = [0 for i in range(0, 64)]
stage2 = [0+0j]*64
stage3 = [0+0j]*64
stage4 = [0+0j]*64
stage5 = [0+0j]*64
stage6 = [0+0j]*64

stage_list = [inputSignal, stage1, stage2, stage3, stage4, stage5, stage6]

#calculation of all stages done in this loop
for n in range(1, 7, 1):
    step = int(N/(2**(n-1)))
    int_step = int(N/(2**n))
    index_list = [i for i in range(0, N+1, step)]
    for i in range(0, 2**(n-1)):
        stage_list[n][index_list[i]:index_list[i]+int_step] =  evenAddTerms(stage_list[n-1][index_list[i]:index_list[i+1]], int(N/(2**(n-1))))
        stage_list[n][index_list[i]+int_step:index_list[i+1]] = oddSubTerms(stage_list[n-1][index_list[i]:index_list[i+1]], int(N/(2**(n-1))), twiddle)

    print(stage_list[n])
    print("_________________________")

#converting the output to "Magnitude Spectrum"
outputSignal = []
for i in range(len(stage6)):
    temp = sqrt((stage6[i].real)**2+(stage6[i].imag)**2)
    outputSignal.append(temp)

#optional
plt.plot(x, outputSignal)
plt.show()


'''
Using the Shooting method to solve
y" = -3y
with the boundary conditions y(0) = 7 and y(2*pi) = 0
and we know the exact solution
'''

# importing stuff
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt

x = np.linspace(0,2*np.pi, 100)
y_exact = 7 * np.cos(np.sqrt(3) * x) - 7 * np.cos(2 * np.pi * np.sqrt(3) ) / np.sin(2 * np.pi * np.sqrt(3) )* np.sin(np.sqrt(3)*x)

# code our second order ODE to two first order ODE

def equations(x,y):
    yprime = np.zeros(2)

    yprime[0] = y[1]
    yprime[1] = -3* y[0]

    return yprime

'''
We will use an iteration scheme to adjust the
values of the initial conditions such that we
match the boundary conditions. What type of
iteration scheme to use is up to you. I will
define high and low bounds and set our natural
guess As the meaning of those two points. I will
then adjust the bounds depending on whether we
overshoot or undershoot our boundary. The exact
way to adjust the bounds is problem dependent.
'''

tol = 1e-6
max_iters = 100
low = -10
high = 10
count = 0

while count <= max_iters:
    count = count + 1
    xspan = (x[0], x[-1])

    #  Use the midpoint between high and low as our guess
    yprime0 = np.mean([low, high])

    #  Set the initial condition vector to be passed into the solver
    y0 = [7, yprime0 ]

    # Solve the system using our guess
    sol = solve_ivp(equations, xspan, y0, t_eval = x)

    #  For ease of use, extract the function values from the solution object.
    y_num = sol.y[0, :]

    #  Check to see if we within our desire tolerance
    if np.abs(y_num[-1]) <= tol:
        break

    #  Adjust our bounds if we are not within tolerance
    if y_num[-1] < 0:
        high = yprime0
    else:
        low = yprime0

    #print(count, y_num[-1])

#  Plot the solution and compare it to the analytical form defined above
plt.plot(x, y_exact, 'b-', label='Exact')
plt.plot(x, y_num, 'ro', label='Numeric')
plt.plot([0, 2*np.pi], [7,0], 'ro')
plt.grid(True)
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
#plt.show()
plt.savefig('shooting_1.png')


import random as rand
import math
import numpy as np
from matplotlib import pyplot as plt
from time import sleep
from sys import stdin

##### generate data for a moving object
### all the following equations are integrals/derivatives of each other
def eq_pos(x, a=-10, b=3, c=-0.2):
    return a*x + b*x**2 + c*x**3
def eq_vel(x):
    return -10 + 6*x - 0.6*x**2
def eq_acc(x):
    return 6 - 1.2*x

def calculate_max_error(x, y, eq):
    time_stamps = np.arange(x[0], x[-1], 0.001)
    m = 0

    xi = 1
    for i in time_stamps:
        while x[xi] < i and xi < len(x):
            xi += 1
        x0 = x[xi-1]
        x1 = x[xi]
        if x0 > i or x1 < i:# prevents comparing outside data set
            continue
        if x1 == 0 or i == 0: # prevents divide by 0
            continue
        slope = (y[xi] - y[xi-1])/(x1-x0)
        p = y[xi-1] + slope*(i-x0)

        m = max(m, abs(p-eq(i)))
    return m

# takes the derivative by calculating the slope between all adjacent points
# by default, calculates the first derivative (deg). Higher degrees calculated recursively
# note: |d_points_y| = |points_y| - 1
def points_derivative(points_x, points_y, deg=1):
    if deg <= 0:
        return (points_x, points_y)

    d_points_y = [] # y of derivative
    d_points_x = [] # ditto for x
    for i in range(len(points_y)-1): # compares adjacent pairs so one fewer than # points
        ## x and y values for adjacent pairs
        ax = points_x[i]
        bx = points_x[i+1]

        ay = points_y[i]
        by = points_y[i+1]

        # the slope of the adjacent point is the y value of the derivative at the x point between the pair
        slope = (by-ay)/(bx-ax)
        d_points_y.append(slope)

        d_points_x.append((ax+bx)/2)

    return points_derivative(d_points_x, d_points_y, deg-1)

# takes the integral by treating the y value between a pair of points as the slope of the integral between those two points
# the point between the x values of the same points
# note: |i_points_y| = |points_y|
def points_integral(points_x, points_y, c=0, deg=1):
    if deg <= 0:
        return (points_x, points_y)

    i_points_y = [c]
    for i in range(len(points_y)-1):
        ax = points_x[i]
        bx = points_x[i+1]
        dx = bx-ax

        slope = (points_y[i]+points_y[i+1])/2

        dy = slope*dx

        y = i_points_y[-1]+dy

        i_points_y.append(y)

    return points_integral(points_x, i_points_y, c, deg-1)

# simulates running a rangefinder by using eq_pos() to get the position values and derives the velocity and acceleration
def simulate_rangefinder(p, x):
    print("simulating rangefinder")
    ## setup plot
    p.grid(color="black", linestyle="-", linewidth=1)
    p.set_title("rangefinder simulation")

    y = eq_pos(x) # measurements taken
    add_line(p, x, y, "r", "position")

    vel_x, vel_y = points_derivative(x, y)
    add_line(p, vel_x, vel_y, "g", "velocity")
    print("velocity " + u"\u00B1" + str(calculate_max_error(vel_x, vel_y, eq_vel)))

    acc_x, acc_y = points_derivative(vel_x, vel_y)
    add_line(p, acc_x, acc_y, "b", "acceleration")
    print("acceleration " + u"\u00B1" + str(calculate_max_error(acc_x, acc_y, eq_acc)), "\n")

# simulates an accelometer using eq_acc to get the acceleration values that would be recorded and integrating to get the
#    velocity and position values
# uses known values of c from eq_vel
def simulate_accelometer(p, x):
    print("simulating accelometer")

    p.grid(color="black", linestyle="-", linewidth=1)
    p.set_title("accelometer simulation")

    acc_y = eq_acc(x) # measurements taken
    add_line(p, x, acc_y, "b", "acceleration")

    vel_x, vel_y = points_integral(x, acc_y, c=-10)
    add_line(p, vel_x, vel_y, "g", "velocity")
    print("velocity " + u"\u00B1" + str(calculate_max_error(vel_x, vel_y, eq_vel)))

    pos_x, pos_y = points_integral(x, vel_y)
    add_line(p, pos_x, pos_y, "r", "position")
    print("position " + u"\u00B1" + str(calculate_max_error(pos_x, pos_y, eq_pos)),"\n")

# draws a line from the list of floats x and y onto the plot p
def add_line(p, x, y, color, name):
    p.plot(x, y, color+".-", label=name) #################
    p.legend()

# this uses the equations for pos, vel, and acc rather than deriving/integrating one to get the other values
def calc_plot(p, x):
    p.grid(color="black", linestyle="-", linewidth=1)
    p.set_title("calculated plot")

    acc_y = eq_acc(x) # measurements taken
    add_line(p, x, acc_y, "b", "acceleration")

    vel_y = eq_vel(x)
    add_line(p, x, vel_y, "g", "velocity")

    pos_y = eq_pos(x)
    add_line(p, x, pos_y, "r", "position")

if __name__ == "__main__":
    # start, end, increment
    x = np.arange(0, 12, 0.01)
    print("number of points generated:", len(x))

    plt.figure(figsize=(9,7))
    rangefinder_plot = plt.subplot2grid((2,2),(0,0))
    accelometer_plot = plt.subplot2grid((2,2), (1,0))
    calculated_plot = plt.subplot2grid((2,2), (0,1))

    simulate_rangefinder(rangefinder_plot, x)
    simulate_accelometer(accelometer_plot, x)
    calc_plot(calculated_plot, np.arange(0, 12, 0.01))

    plt.show()


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
import random

def create_dataset(n,variance,step=2,correlation=False):
    val = 1
    ys = []
    for i in range(n):
        y = val + random.randrange(-variance,variance)
        ys.append(y)
        if correlation and correlation== "pos":
            val += step
        elif correlation and correlation=="neg":
            val -= step
    xs = [i for i in range(len(ys))]
    return np.array(xs,dtype=np.float64) , np.array(ys , dtype=np.float64)

def best_fit_slope(xs,ys):
    m = ( ((mean(xs)*mean(ys)) - mean(xs*ys)) /
        (mean(xs)*mean(xs) - mean(xs*xs) )  )
    return m

def best_intercept(slope,xs,ys):
    b = mean(ys) - m*mean(xs)
    return b

def squared_error(ys_orig,ys_line):
    return sum((ys_line-ys_orig)**2)

def coefficient_of_determination(ys_orig,ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_regr = squared_error(ys_orig,ys_line)
    squared_error_mean = squared_error(ys_orig,y_mean_line)
    return 1 - (squared_error_regr/squared_error_mean)

xs , ys = create_dataset(40 , 20 , 2 , correlation="neg")

m = best_fit_slope(xs,ys)
b = best_intercept(m , xs, ys)

regression_line = [(m*x)+b for x in xs]

predic_x = 8
predic_y = m*predic_x + b

r_squared = coefficient_of_determination(ys , regression_line)
print(r_squared)

plt.scatter(xs , ys)
plt.scatter(predic_x,predic_y , s = 100)
plt.plot(xs,regression_line)
plt.show()


import numpy as np
try:
    import matplotlib.pyplot as plt
except:
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt

class PolynomialRegression():
    def __init__(self, degree):
        """
        Implement polynomial regression from scratch.

        This class takes as input "degree", which is the degree of the polynomial
        used to fit the data. For example, degree = 2 would fit a polynomial of the
        form:

            ax^2 + bx + c

        Your code will be tested by comparing it with implementations inside sklearn.
        DO NOT USE THESE IMPLEMENTATIONS DIRECTLY IN YOUR CODE. You may find the
        following documentation useful:

        https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html
        https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html

        Here are helpful slides:

        http://interactiveaudiolab.github.io/teaching/eecs349stuff/eecs349_linear_regression.pdf

        The internal representation of this class is up to you. Read each function
        documentation carefully to make sure the input and output matches so you can
        pass the test cases. However, do not use the functions numpy.polyfit or numpy.polval.
        You should implement the closed form solution of least squares as detailed in slide 10
        of the lecture slides linked above.

        Usage:
            import numpy as np

            x = np.random.random(100)
            y = np.random.random(100)
            learner = PolynomialRegression(degree = 1)
            learner.fit(x, y) # this should be pretty much a flat line
            predicted = learner.predict(x)

            new_data = np.random.random(100) + 10
            predicted = learner.predict(new_data)

            # confidence compares the given data with the training data
            confidence = learner.confidence(new_data)

        Args:
            degree (int): Degree of polynomial used to fit the data.
        """
        self.degree = degree
        self.confidence = None
        self.w = None  # define the weights
        self.f = None  # function
        self.X_training = None

    def fit(self, features, targets):
        """
        Fit the given data using a polynomial. The degree is given by self.degree,
        which is set in the __init__ function of this class. The goal of this
        function is fit features, a 1D numpy array, to targets, another 1D
        numpy array.

        Args:
            features (np.ndarray): 1D array containing real-valued inputs.
            targets (np.ndarray): 1D array containing real-valued targets.
        Returns:
            None (saves model and training data internally)
        """
        X = np.ones((features.size, self.degree + 1))
        X_row = X.shape[0]
        X_column = X.shape[1]

        for i in range(X_row):
            for j in range (1, X_column):
                X[i,j] = features[i]**j

        # formula for weights: (X^TX)X^ty
        X_inv = X.T
        #self.w = np.matmul(np.matmul((np.linalg.inv(np.matmul(X_inv, X))), X_inv), targets)
        self.w = np.linalg.inv(X.T @ X) @ X.T @ targets
        #self.w = ((np.linalg.inv((X.T).dot(X))).dot(X.T)).dot(targets)

        self.features_row = features.shape[0]
        self.f = np.zeros((self.features_row))
        for i in range(self.w.size):
            self.f += self.w[i] * np.sort(features)**i

        self.X_training = features  # save training data internally

    def predict(self, features):
        """
        Given features, a 1D numpy array, use the trained model to predict target
        estimates. Call this after calling fit.

        Args:
            features (np.ndarray): 1D array containing real-valued inputs.
        Returns:
            predictions (np.ndarray): Output of saved model on features.
        """
        predictions = np.zeros((features.shape[0]))

        for i in range(self.w.size):
            predictions += self.w[i] * features**i

        return predictions

    def visualize(self, features, targets, path, title, color='g'):
        """
        This function should produce a single plot containing a scatter plot of the
        features and the targets, and the polynomial fit by the model should be
        graphed on top of the points.

        DO NOT USE plt.show() IN THIS FUNCTION. Instead, use plt.savefig().

        Args:
            features (np.ndarray): 1D array containing real-valued inputs.
            targets (np.ndarray): 1D array containing real-valued targets.
        Returns:
            None (plots to the active figure)
        """

        plt.figure()
        plt.scatter(features, targets)
        plt.plot(np.sort(self.X_training), self.f)
        plt.title(title)
        plt.xlabel('x')
        plt.ylabel('y')
        plt.legend()
        plt.grid(True)
        plt.savefig(path)


'''
Created on 2013/12/29
@author: Duong Nguyen
@note: Nonnegative Matrix Factorization (NMF) with multiplicative update rules.
@version: Kullback-Leibler

V = W*H, where V, W, H >= 0. Here, ">=" means element-wise relation
V: n x m matrix
W: n x b matrix
H: b x m matrix
Minimize_over_{W,H} KL-divergence(V, W*H)

See also, http://hebb.mit.edu/people/seung/papers/nmfconverge.pdf for more details.
'''

import numpy as np
import matplotlib.pyplot as plt

def kl_distance(A, B):
    """ Compute the KL divergence between two matrices A, B.
        KL(A, B) = \sum_{i,j} (A_ij * log(A_ij / B_ij) - A_ij + B_ij)
    """
    return np.sum(A * np.log(A/B) - A + B)

def factorize(V, b, init="uniform", max_iter=100, tol=1e-8, verbose=False):
    """ Factorization with multiplicative update rules.
        @param init: specify how to initialize W, H. Default: uniformly random in [0,1)
        @param max_iter: Maximum number of iterations of updating
        @param tol: Tolerance value to check if converge

        @return: n x b matrix W, and b x m matrix H such that KL(V, W*H) is minimized.
                The values of KL-divergence at each iteration step are also returned as dists.
    """

    assert init in ("uniform", "normal"), "Unsupported initialization!"
    n, m = V.shape

    if init == "uniform":
        W, H = np.random.random((n,b)), np.random.random((b,m))
    else:
        W, H = np.random.randn(n,b), np.random.randn(b,m)

    dists = []
    for i in xrange(max_iter):
        WH = W.dot(H)

        cur_dist = kl_distance(V, WH)
        dists.append(cur_dist)

        if verbose and i % 10 == 0:
            print "%d iteration: %f" %(i, cur_dist)

        if cur_dist <= tol:
            break

        # Update H
        nu = (W.T).dot(V/(WH))
        de = np.tile(np.sum(W,0), (m,1)).T
        H = H*nu/de # element-wise update

        # Update W
        nu = (V/(W.dot(H))).dot(H.T)
        de = np.tile(np.sum(H,1), (n,1))
        W = W*nu/de # element-wise update

    return W, H, dists

def test(plot=False):
    """ Test KL-based NMF."""

    V = np.random.random((10, 9))
    W, H, dists = factorize(V, 3, init="uniform", max_iter=300, tol=1e-8, verbose=True)

    if plot:
        plt.figure()
        plt.clf()
        plt.plot(dists, "b-", lw=2)
        plt.xlabel("Iteration")
        plt.ylabel("KL divergence as cost function")
        plt.title("KL-based NMF Demo")
        plt.show()
        plt.savefig("kl_nmf_demo.png") # save figure at current working directory

    print "V =", V
    print "W * H =", W.dot(H)
    print "KL divergence =", dists[-1]

if __name__ == "__main__":
    test(plot=True)