def question_1():
    # packages
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import axes3d
    from matplotlib import cm
    import numpy as np

    # parameters
    t = np.linspace(-1.2, 1.2, 512)
    X, Y = np.meshgrid(t, t)
    norms = {
        '$\infty$-norm': lambda x, y: max(abs(x), abs(y)),
        '1-norm': lambda x, y: abs(x)+abs(y),
        '2-norm': lambda x, y: np.sqrt(x**2+y**2),
    }
    zlim = 0, 1.2
    figsize = 9, 3

    # body
    length = len(norms)
    Z = np.zeros_like(X)
    fig = plt.figure(figsize=figsize)
    for ith, (name, func) in enumerate(norms.items()):
        for jth in range(Z.size):
            value = func(X.flat[jth], Y.flat[jth])
            Z.flat[jth] = value if value<=1 else np.nan
        ax = fig.add_subplot(1, length, ith+1, projection='3d')
        ax.plot_surface(X, Y, Z, alpha=0.3)
        ax.contour(X, Y, Z, zdir='z', offset=zlim[0], cmap=cm.coolwarm)
        ax.set_zlim(*zlim)
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')
        ax.set_title(name)
    plt.show()

if __name__ == '__main__':
    question_1()


import math

import numpy as np

def hoeffding(n, delta, upper, lower):
    """
        Input:
            n: number of Xi's
            delta: error probability
            upper, lower:  lower <= Xi <= upper with probability 1

        Output:
            t: the number that satisfies the Hoeffding inequality:
                Pr(Sn >= t) <= {delta = exp(-t^2/{2n*sigma^2})}
                where Sn = X1 + ... + Xn.
    """
    tmp = -np.log(delta)
    half = (upper - lower) / 2
    return np.sqrt(2 * n * half ** 2 * tmp)

def adaptive_hoeffding(n, delta, upper, lower):
    """
        Adaptive Hoeffding-like Concentration Bounds
        as in Corollary 1 of Ermon 2016 NeurIPS paper.
        Valid for lower <= Xi <= upper.

        Input:
            n: number of Xi's
            delta: error probability
            upper, lower:  lower <= Xi <= upper with probability 1

        Output:
            The adaptive upper bound of Sn where Sn = X1 + ... + Xn.
    """
    b = -np.log(delta / 12.0) / 1.8
    return (upper - lower) * np.sqrt(0.6 * n * np.log(math.log(n, 1.1) + 1) + b * n)

def adaptive_hoeffding_seq(N, delta, upper, lower):
    """
        Adaptive Hoeffding-like Concentration Bounds
        as in Corollary 1 of Ermon 2016 NeurIPS paper.
        Valid for lower <= Xi <= upper.

        Input:
            N: number of Xi's
            delta: error probability
            upper, lower:  lower <= Xi <= upper with probability 1

        Output:
            A sequence of adaptive upper bounds for S1, S2, ..., SN.
    """
    b = -np.log(delta / 12.0) / 1.8
    n = np.arange(1, N + 1)
    return (upper - lower) * np.sqrt(
        0.6 * n * np.log(np.log(n) / np.log(1.1) + 1) + b * n
    )

def hrms_hoeffding_seq(N, delta, sigma=1.0):
    """
        Compute the uniform upper bound for Sn = X1 + ... + Xn, n in [N]
        using Eq 8 in Howard et al. 2018, where Xi is sigma sub-Gaussian.

        Input:
            N: number of Xi's
            delta: error probability
            sigma: subGaussian parameter. Xi is sigma-subGaussian

        Output:
            A sequence of adaptive upper bounds for S1, S2, ..., SN.
    """

    n = np.arange(1, N + 1)
    return (
        1.7 * sigma * np.sqrt(n * (np.log(np.log(2 * n)) + 0.72 * np.log(5.2 / delta)))
    )


"""

linear regression problem of mapping R_d to R, with two diffrent objective functions

Author: Zohreh Raziei - raziei.z@husky.neu.edu

"""

#1. Minimizing Sum of squared Error

# In[1]:

#Install dependency

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import use, cm
from sklearn.datasets import make_regression
from sklearn.metrics import r2_score

get_ipython().run_line_magic('matplotlib', 'inline')

# load the dataset

df = pd.read_csv('ex1data1.txt',names = ['Population','Profit'])
df.head()

#Plot the data with scaled units

plt.scatter(df['Population'],df['Profit'],c='r')
plt.xlabel('Population/(10,000)')
plt.ylabel('Profit/(10,000)')
plt.title('Original Data')
plt.show()

# making data
X = df['Population'].values
y = df['Profit'].values

X = X.reshape(-1,1)
X.shape

# No of examples
m = X.shape[0]

# No of features
n = X.shape[1] + 1

#Initialize theta
theta = np.zeros((n,1))
#theta = theta.reshape((len(theta), 1))

#adding extra feature theta0 as 1
X_ = np.append(np.ones((m, 1)), X.reshape((-1,1)),axis=1)

# Compute Cost function
def computeCost(X, y, theta):
    m  = y.size
    h = np.dot(X,theta)
    J = (np.sum(np.square(h-y))) / (2 * m)
    return J

# Calucating cost function and theta

def gradientDescent(X, y, theta, alpha, num_iters):

    # Initialize some useful values
    J_history = []
    m = y.size  # number of training examples

    for i in range(num_iters):
        h = np.dot(X, theta)
        theta = theta - ((alpha / m) * (np.dot(X.T,(h - y.reshape(-1,1)))))
        # Save the cost J in every iteration
        J_history.append(computeCost(X, y, theta))
        break
    return theta,J_history[len(J_history)-1]

final_theta,loss = gradientDescent(X_, y, theta, 0.01, 100)
print('loss :',loss)
print('final theta :',final_theta)

x_line = np.arange(1,23).reshape(-1,1)
y_line = final_theta[0] + x_line * final_theta[1]

def linefitline(x):
    return final_theta[0] + final_theta[1] * x
y_pred = linefitline(X)

print('R2 score For Dataset 1 (MSE): ',r2_score(y,y_pred))

# In[13]:

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimizing Sum of squared Error : Dataset 1')
plt.show()

# ## 2. Minimising sum of distance

# load the dataset

df = pd.read_csv('ex1data1.txt',names = ['Population','Profit'])
df.head()

# making data
X = df['Population'].values
y = df['Profit'].values

# Total Least Squares:
def line_total_least_squares(x,y):
    n = len(x)

    x_ = np.sum(x)/n
    y_ = np.sum(y)/n

    # Calculate the x~ and y~
    x1 = x - x_
    y1 = y - y_

    # Create the matrix array
    X = np.vstack((x1, y1))
    X_t = np.transpose(X)

    # Finding A_T_A and it's Find smallest eigenvalue::
    prd = np.dot(X,X_t)
    W,V = np.linalg.eig(prd)
    small_eig_index = W.argmin()
    a,b = V[:,small_eig_index]

    # Compute C:
    c = (-1*a*x_) + (-1*b*y_)

    return a,b,c

a1,b1,c1 = line_total_least_squares(X,y)

x_line = np.arange(1,23).reshape(-1,1)
y_line = (-1*(c1/b1)) + x_line * -1*(a1/b1)

def linefitline(x):
    return -1*(c1/b1) + -1*(a1/b1) * x
y_pred = linefitline(X)

print('R2 score For Dataset 1 (MSD): ',r2_score(y,y_pred))

plt.scatter(df['Population'],df['Profit'],c='r',label='given')
plt.xlabel('Population')
plt.ylabel('Profit')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimising sum of distance : Dataset 1')
plt.show()

# ## 3. Closed Form Solution

X_ = np.append(X.reshape((-1,1)),np.ones((m, 1)) ,axis=1)

# calculate coefficients using closed-form solution
from numpy.linalg import inv
coeffs = inv(X_.transpose().dot(X_)).dot(X_.transpose()).dot(y.reshape(-1,1))

coeffs

x_line = np.arange(1,23).reshape(-1,1)
y_line = (coeffs[1][0]) + x_line * coeffs[0][0]

def linefitline(x):
    return coeffs[1][0] + coeffs[0][0] * x
y_pred = linefitline(X)

print('R2 score For Dataset 1 (CFS) : ',r2_score(y,y_pred))

plt.scatter(df['Population'],df['Profit'],c='r',label='given')
plt.xlabel('Population')
plt.ylabel('Profit')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Close Form Solution : Dataset 1')
plt.show()

# # Dataset : 2

# In[29]:

X, y = make_regression(n_samples=100,n_features=1,noise=10)

plt.scatter(X,y)
plt.show()

# ## 1. Minimizing Sum of squared Error

X = X.reshape(-1,1)
X.shape

# No of examples
m = X.shape[0]

# No of features
n = X.shape[1] + 1

#Initialize theta
theta = np.zeros((n,1))
#theta = theta.reshape((len(theta), 1))

#adding extra feature theta0 as 1
X_ = np.append(np.ones((m, 1)), X.reshape((-1,1)),axis=1)

final_theta,loss = gradientDescent(X_, y, theta, 0.6, 200)
print('loss :',loss)
print('final theta :',final_theta)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = final_theta[0] + x_line * final_theta[1]

def linefitline(x):
    return final_theta[0] + final_theta[1] * x
y_pred = linefitline(X)

print('R2 score For Dataset 2 (MSE) : ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimizing Sum of squared Error : Dataset 2')
plt.show()

# ## 2. Minimising sum of distance

a1,b1,c1 = line_total_least_squares(X.reshape(-1),y)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = (-1*(c1/b1)) + x_line * -1*(a1/b1)

def linefitline(x):
    return -1*(c1/b1) + -1*(a1/b1) * x
y_pred = linefitline(X)

print('R2 score For Dataset 2 (MSD): ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimising sum of distance : Dataset 2')
plt.show()

# ## 3. Closed Form Solution

X_ = np.append(X.reshape((-1,1)),np.ones((m, 1)) ,axis=1)

# calculate coefficients using closed-form solution
from numpy.linalg import inv
coeffs = inv(X_.transpose().dot(X_)).dot(X_.transpose()).dot(y.reshape(-1,1))

print(coeffs)

def linefitline(x):
    return coeffs[1][0] + coeffs[0][0] * x
y_pred = linefitline(X)

print('R2 score For Dataset 2 (CFS) : ',r2_score(y,y_pred))

x_line = np.arange(-3,5).reshape(-1,1)
y_line = (coeffs[1][0]) + x_line * coeffs[0][0]

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Close Form Solution : Dataset 2')
plt.show()

# # Dataset : 3

# In[50]:

X, y = make_regression(n_samples=200,n_features=1,noise=20)

plt.scatter(X,y)
plt.show()

# ## 1. Minimizing Sum of squared Error

X = X.reshape(-1,1)
X.shape

# No of examples
m = X.shape[0]

# No of features
n = X.shape[1] + 1

#Initialize theta
theta = np.zeros((n,1))
#theta = theta.reshape((len(theta), 1))

#adding extra feature theta0 as 1
X_ = np.append(np.ones((m, 1)), X.reshape((-1,1)),axis=1)

final_theta,loss = gradientDescent(X_, y, theta, 0.5, 100)
print('loss :',loss)
print('final theta :',final_theta)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = final_theta[0] + x_line * final_theta[1]

def linefitline(x):
    return final_theta[0] + final_theta[1] * x
y_pred = linefitline(X)

print('R2 score For Dataset 3 (MSE) : ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimizing Sum of squared Error : Dataset 3')
plt.show()

# ## 2. Minimising sum of distance

a1,b1,c1 = line_total_least_squares(X.reshape(-1),y)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = (-1*(c1/b1)) + x_line * -1*(a1/b1)

def linefitline(x):
    return -1*(c1/b1) + -1*(a1/b1) * x
y_pred = linefitline(X)

print('R2 score For Dataset 3 (MSD): ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimising sum of distance : Dataset 3')
plt.show()

# ## 3. Closed Form Solution

X_ = np.append(X.reshape((-1,1)),np.ones((m, 1)) ,axis=1)

# calculate coefficients using closed-form solution
from numpy.linalg import inv
coeffs = inv(X_.transpose().dot(X_)).dot(X_.transpose()).dot(y.reshape(-1,1))

print(coeffs)

def linefitline(x):
    return coeffs[1][0] + coeffs[0][0] * x
y_pred = linefitline(X)

print('R2 score For Dataset 3 (CFS) : ',r2_score(y,y_pred))

x_line = np.arange(-3,5).reshape(-1,1)
y_line = (coeffs[1][0]) + x_line * coeffs[0][0]

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Close Form Solution : Dataset 3')
plt.show()

# # Dataset : 4

# In[71]:

X, y = make_regression(n_samples=300,n_features=1,noise=15)

plt.scatter(X,y)
plt.show()

# ## 1. Minimizing Sum of squared Error

X = X.reshape(-1,1)
X.shape

# No of examples
m = X.shape[0]

# No of features
n = X.shape[1] + 1

#Initialize theta
theta = np.zeros((n,1))
#theta = theta.reshape((len(theta), 1))

#adding extra feature theta0 as 1
X_ = np.append(np.ones((m, 1)), X.reshape((-1,1)),axis=1)

final_theta,loss = gradientDescent(X_, y, theta, 0.5, 100)
print('loss :',loss)
print('final theta :',final_theta)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = final_theta[0] + x_line * final_theta[1]

def linefitline(x):
    return final_theta[0] + final_theta[1] * x
y_pred = linefitline(X)

print('R2 score For Dataset 4 (MSE) : ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimizing Sum of squared Error : Dataset 4')
plt.show()

# ## 2. Minimising sum of distance

a1,b1,c1 = line_total_least_squares(X.reshape(-1),y)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = (-1*(c1/b1)) + x_line * -1*(a1/b1)

def linefitline(x):
    return -1*(c1/b1) + -1*(a1/b1) * x
y_pred = linefitline(X)

print('R2 score For Dataset 4 (MSD): ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimising sum of distance : Dataset 4')
plt.show()

# ## 3. Closed Form Solution

X_ = np.append(X.reshape((-1,1)),np.ones((m, 1)) ,axis=1)

# calculate coefficients using closed-form solution
from numpy.linalg import inv
coeffs = inv(X_.transpose().dot(X_)).dot(X_.transpose()).dot(y.reshape(-1,1))

print(coeffs)

def linefitline(x):
    return coeffs[1][0] + coeffs[0][0] * x
y_pred = linefitline(X)

print('R2 score For Dataset 4 (CFS) : ',r2_score(y,y_pred))

x_line = np.arange(-3,5).reshape(-1,1)
y_line = (coeffs[1][0]) + x_line * coeffs[0][0]

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Close Form Solution : Dataset 4')
plt.show()

# # Dataset : 5

# In[92]:

X, y = make_regression(n_samples=500,n_features=1,noise=25)

plt.scatter(X,y)
plt.show()

# ## 1. Minimizing Sum of squared Error

X = X.reshape(-1,1)
X.shape

# No of examples
m = X.shape[0]

# No of features
n = X.shape[1] + 1

#Initialize theta
theta = np.zeros((n,1))
#theta = theta.reshape((len(theta), 1))

#adding extra feature theta0 as 1
X_ = np.append(np.ones((m, 1)), X.reshape((-1,1)),axis=1)

final_theta,loss = gradientDescent(X_, y, theta, 0.5, 500)
print('loss :',loss)
print('final theta :',final_theta)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = final_theta[0] + x_line * final_theta[1]

def linefitline(x):
    return final_theta[0] + final_theta[1] * x
y_pred = linefitline(X)

print('R2 score For Dataset 5 (MSE) : ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimizing Sum of squared Error : Dataset 5')
plt.show()

# ## 2. Minimising sum of distance

a1,b1,c1 = line_total_least_squares(X.reshape(-1),y)

x_line = np.arange(-5,5).reshape(-1,1)
y_line = (-1*(c1/b1)) + x_line * -1*(a1/b1)

def linefitline(x):
    return -1*(c1/b1) + -1*(a1/b1) * x
y_pred = linefitline(X)

print('R2 score For Dataset 5 (MSD): ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Minimising sum of distance : Dataset 5')
plt.show()

# ## 3. Closed Form Solution

X_ = np.append(X.reshape((-1,1)),np.ones((m, 1)) ,axis=1)

# calculate coefficients using closed-form solution
from numpy.linalg import inv
coeffs = inv(X_.transpose().dot(X_)).dot(X_.transpose()).dot(y.reshape(-1,1))

x_line = np.arange(-3,5).reshape(-1,1)
y_line = (coeffs[1][0]) + x_line * coeffs[0][0]

print(coeffs)

def linefitline(x):
    return coeffs[1][0] + coeffs[0][0] * x
y_pred = linefitline(X)

print('R2 score For Dataset 5 (CFS) : ',r2_score(y,y_pred))

plt.scatter(X,y,c='r',label='Original')
plt.xlabel('X')
plt.ylabel('y')
plt.plot(x_line,y_line,label='predicted')
plt.legend()
plt.title('Close Form Solution : Dataset 5')
plt.show()


# --------------
# Code starts here

import numpy as np

# Code starts here

# Adjacency matrix
adj_mat = np.array([[0, 0,0,  0,  0,  0,1/3,0],
                   [1/2,0,1/2,1/3,0,  0,0,  0],
                   [1/2,0,0,  0,  0,  0,0,  0],
                   [0,  1,0,  0,  0,  0,0,  0],
                   [0,  0,1/2,1/3,0,  0,1/3,0],
                   [0,  0,0,  1/3,1/3,0,0,1/2],
                   [0,  0,0,  0,  1/3,0,0,1/2],
                   [0,  0,0,  0,  1/3,1,1/3,0]])

# Compute eigenvalues and eigencevectrs
eigrnvalues , eigenvectors = np.linalg.eig(adj_mat)

# Eigen vector corresponding to 1
eigen_1 = abs(eigenvectors[:,0])* 1/np.linalg.norm(eigenvectors[:,0],1)

# most important page
page = np.where(np.max(eigen_1)==eigen_1)[0][0] + 1
print (page)
# Code ends here

# --------------
# Code starts here

# Initialize stationary vector I
init_I = np.array([1,0,0,0,0,0,0,0])

# Perform iterations for power method
for i in range(10):
    init_I = np.dot(adj_mat,init_I)
    init_I = init_I/np.linalg.norm(init_I,1)

power_page = np.where(np.max(init_I)==init_I)[0][0]+1
print (power_page)
# Code ends here

# --------------
# Code starts here
import numpy as np
# New Adjancency matrix
# New Adjancency matrix
new_adj_mat = np.array([[0,0,0,0,0,0,0,0],
                   [1/2,0,1/2,1/3,0,0,0,0],
                  [1/2,0,0,0,0,0,0,0],
                   [0,1,0,0,0,0,0,0],
                   [0,0,1/2,1/3,0,0,1/2,0],
                   [0,0,0,1/3,1/3,0,0,1/2],
                   [0,0,0,0,1/3,0,0,1/2],
                   [0,0,0,0,1/3,1,1/2,0]])

# Initialize stationary vector I
new_init_I = np.array([1,0,0,0,0,0,0,0])

# Perform iterations for power method
for i in range(10):
    new_init_I = np.dot(new_adj_mat,new_init_I)
    new_init_I = new_init_I / np.linalg.norm(new_init_I,1)

#display
print (new_init_I)

# Code ends here

# --------------
# Alpha value
alpha = 0.85

# Code starts here

# Modified adjancency matrix
n = len(new_adj_mat)
l = np.ones(new_adj_mat.shape)
G = alpha*new_adj_mat + (l-alpha)/n
# Initialize stationary vector I
final_init_I = np.array([1,0,0,0,0,0,0,0])

# Perform iterations for power method
for i in range(1000):
    final_init_I = np.dot(G,final_init_I)
    final_init_I = final_init_I / np.linalg.norm(final_init_I,1)

#display
print(final_init_I)
# Code ends here


#!/usr/bin/env python
__author__ = "Samuel Schmidgall"
__license__ = "MIT"
__email__ = "sschmidg@masonlive.gmu.edu"

import numpy as np

class MultipleLinear:
    def __init__(self):
        """
        Multiple Linear Regression Model
        """
        self._tuple_size = None
        self._data_set_len = None
        self._inp, self._pred, self._bias = None, None, None

    def train(self, inputs, exp_val):
        """
        Train the given model based on inputs and expected value
        :param inputs: input value which is expected to return corresponding exp_val
        :param exp_val: expected value for a given input
        """
        if len(inputs) != len(exp_val) <= 0:
            raise Exception('Invalid Data Set')
        self._tuple_size = len(inputs[0])
        self._data_set_len = len(inputs)
        self._inp, self._pred, self._bias = self._generate_init_values(inputs, exp_val)
        self._gradient_descent()

    def predict(self, predicted_values):
        """
        Compute multiple linear regression prediction
        :param predicted_values: ndarray -> values to predict
        :return: float -> predicted value
        """
        if self._inp is None or self._pred is None or self._bias is None:
            raise Exception('Model must be trained before prediction can occur')
        if type(predicted_values) == list:
            predicted_values = np.array(predicted_values)
            predicted_values = np.insert(predicted_values, 0, 1)
        return predicted_values.dot(self._bias)

    def _cost_function(self):
        """
        Cost function for regression
        """
        return np.sum((self._inp.dot(self._bias) - self._pred) ** 2) / (2 * self._data_set_len)

    def _generate_init_values(self, x, y):
        """
        Generate initial values
        :param x: list(tuple(float)) -> list of x values
        :param y: list(float) -> list of y values
        :return: ndarray -> initialized variables
        """
        return np.array([np.ones(len(x))] + [np.array([k[i] for k in x]) for i in range(self._tuple_size)]).T, \
               np.array(y), np.zeros(self._tuple_size+1)

    def _gradient_descent(self, learning_rate=0.0001, iterations=100000):
        """
        Perform gradient decent to train model
        :param learning_rate: float -> learning rate
        :param iterations: int -> training iterations
        """
        r_itr = range(iterations)
        for _ in r_itr:
            loss = self._inp.dot(self._bias) - self._pred
            gradient = self._inp.T.dot(loss) / self._data_set_len
            self._bias = self._bias - learning_rate * gradient

    def model_efficiency(self, y_prediction):
        """
        Test the efficency of trained model
        :param y_pred: ndarray -> y prediction vector
        :return: tuple(float, float) -> efficiency of given model
        """
        if type(y_prediction) == list:
            y_prediction = np.array(y_prediction)
        mean_s_err = np.sqrt(sum((self._pred - y_prediction) ** 2) / len(self._pred))
        y_mean = np.mean(self._pred)
        r2_score = 1 - (sum((self._pred - y_mean) ** 2) / sum((self._pred - y_prediction) ** 2))
        return mean_s_err, r2_score


from numpy import linalg
import numpy as np

class InconsistentSystemSolver:
    def __init__(self, method='conjugate'):
        self.method = method if method in ['lstsq', 'conjugate'] else 'conjugate'

    def conjugate_grad(self, A, b, x=None, eps=1e-5):
        """
        Description
        -----------
        Solve a linear equation Ax = b with conjugate gradient method.
        Parameters
        ----------
        A: 2d numpy.array of positive semi-definite (symmetric) matrix
        b: 1d numpy.array
        x: 1d numpy.array of initial point
        Returns
        -------
        1d numpy.array x such that Ax = b
        """
        n = A.shape[1]
        if not x:
            x = np.ones(n)
        r = A @ x - b
        p = - r
        r_k_norm = np.dot(r, r)
        for i in range(2 * n):
            Ap = np.dot(A, p)
            alpha = r_k_norm / np.dot(p, Ap)
            x += alpha * p
            r += alpha * Ap
            r_kplus1_norm = np.dot(r, r)
            beta = r_kplus1_norm / r_k_norm
            r_k_norm = r_kplus1_norm
            if r_kplus1_norm < eps:
                # print ('Itr:', i)
                break
            p = beta * p - r
        return x

    def nonlinear_conjugate_grad(self, A, b, eps=1e-5):
        return self.conjugate_grad(A.T @ A, A.T @ b, eps=eps)

    def lstsq(self, A, b):
        return linalg.lstsq(A, b)[0]

    def solve(self, A, b):
        return self.lstsq(A, b) if self.method == 'lstsq' else self.nonlinear_conjugate_grad(A, b)


import numpy as np

arr = np.arange(50).reshape((10,5))
print(arr)
'''
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]
 [20 21 22 23 24]
 [25 26 27 28 29]
 [30 31 32 33 34]
 [35 36 37 38 39]
 [40 41 42 43 44]
 [45 46 47 48 49]]
'''

# transposition
print(arr.T)

# dot product
print(np.dot(arr.T, arr))
'''
[[7125 7350 7575 7800 8025]
 [7350 7585 7820 8055 8290]
 [7575 7820 8065 8310 8555]
 [7800 8055 8310 8565 8820]
 [8025 8290 8555 8820 9085]]
'''

# 3d array
arr3d = np.arange(50).reshape((5,5,2))
print(arr3d)
'''
[[[ 0  1]
  [ 2  3]
  [ 4  5]
  [ 6  7]
  [ 8  9]]

 [[10 11]
  [12 13]
  [14 15]
  [16 17]
  [18 19]]

 [[20 21]
  [22 23]
  [24 25]
  [26 27]
  [28 29]]

 [[30 31]
  [32 33]
  [34 35]
  [36 37]
  [38 39]]

 [[40 41]
  [42 43]
  [44 45]
  [46 47]
  [48 49]]]
'''

# 3d array transposition
print(arr3d.transpose((1,0,2)))
'''
[[[ 0  1]
  [10 11]
  [20 21]
  [30 31]
  [40 41]]

 [[ 2  3]
  [12 13]
  [22 23]
  [32 33]
  [42 43]]

 [[ 4  5]
  [14 15]
  [24 25]
  [34 35]
  [44 45]]

 [[ 6  7]
  [16 17]
  [26 27]
  [36 37]
  [46 47]]

 [[ 8  9]
  [18 19]
  [28 29]
  [38 39]
  [48 49]]]
'''

# swapaxes
arr_s = np.array([[1,2,3]])
print(arr_s)
'''
[[1 2 3]]
'''

print(arr_s.swapaxes(0,1))
'''
[[1]
 [2]
 [3]]
'''


# distance between two points in xy-coourdinate reference system
# can be calculated using euclidean, manhattan and other known
# metrices. however, distance between two points on earth has to
# be calculated using haversine distance, since earth is not flat
# and while calculating distance, the curvature has to be considered.
# the given function calculates haversine distance using `numpy` library
# and is inspired from sklearns haversine distance calculation function:
# https://scikit-learn.org/stable/modules/generated/sklearn.metrics.pairwise.haversine_distances.html

import numpy as np

def haversineDistance(lon1, lat1, lon2, lat2):
    """Calculate the Distance b/w two Geographic Points"""

    dlon = lon2 - lon1 # delta Longitude
    dlat = lat2 - lat1 # delta Latitude

    a = np.sin(dlat/2.0)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2.0)**2
    c = 2 * np.arcsin(np.sqrt(a))

    R = 6371 # radius of earth

    return R * c # distance in kilometer

if __name__ == "__main__":
    lon1 = float(input("Enter Point-1's Longitude: "))
    lat1 = float(input("Enter Point-1's Latitude: "))
    lon2 = float(input("Enter Point-2's Longitude: "))
    lat2 = float(input("Enter Point-2's Latitude: "))

    print(f"Distance b/w {lon1, lat1} and {lon2, lat2} is {haversineDistance(lon1, lat1, lon2, lat2)} km.")


import numpy as np

def distance_point_point(p1: np.ndarray, p2: np.ndarray) -> float:
    return abs(np.linalg.norm(p1 - p2))

def distance_point_segment(point, segment) -> float:
    return abs(
        np.cross(segment[1] - segment[0], point - segment[0]
        ) / np.linalg.norm(segment[1] - segment[0])
    )

def distance_point_polyline(point, polyline) -> float:
    segments = segments_of_polyline(polyline)
    return min(
        [distance_point_segment(point, segment) for segment in segments]
    )

def segments_of_polyline(polyline) -> list:
    return [
        [polyline[i], polyline[i+1]] for i in range(len(polyline) - 1)
    ]

def length_of_polyline(polyline) -> float:
    segments = segments_of_polyline(polyline)
    return sum([distance_point_point(segment[0], segment[1]) for segment in segments])

def point_at_parameter_segement(segment, param) -> np.ndarray:
    diss = segment[1] - segment[0]
    return segment[0] + param * diss

def point_at_parameter_polyline(polyline, param) -> np.ndarray:
    segments = segments_of_polyline(polyline)
    segment_lengths = [distance_point_point(segment[0], segment[1]) for segment in segments]
    entire_length = sum(segment_lengths)
    diss = param * entire_length

    def f(aim, lengths):
        for i in range(len(lengths)):
            if aim - lengths[i] <= 0:
                return [aim, i]
            else:
                aim -= lengths[i]

        return [aim, i]

    aim, index = f(diss, segment_lengths)
    segment = [polyline[index], polyline[index+1]]
    length = distance_point_point(segment[0], segment[1])
    param = aim / length
    return point_at_parameter_segement(segment, param)

def is_point_in_segment_domian(point, segment, include_intersection=True) -> bool:
    roundoff = 6
    a, b = segment
    minx = round(min(a[0], b[0]), roundoff)
    maxx = round(max(a[0], b[0]), roundoff)
    miny = round(min(a[1], b[1]), roundoff)
    maxy = round(max(a[1], b[1]), roundoff)
    x = round(point[0], roundoff)
    y = round(point[1], roundoff)

    tolerance =0.00001

    if not include_intersection:
        if (abs(point[0] - segment[0][0]) < tolerance and abs(point[1] - segment[0][1]) < tolerance) or \
                (abs(point[0] - segment[1][0]) < tolerance and abs(point[1] - segment[1][1]) < tolerance):
            return False

    if x < minx or x > maxx:
        return False

    if y < miny or y > maxy:
        return False

    return True

def is_point_in_polyline(point, polyline) -> bool:
    counter = 0
    x, y, p1 = point[0], point[1], polyline[0]

    for p2 in polyline:
        if min([p1[0], p2[0]]) < x <= max([p1[0], p2[0]]):
            if y <= max([p1[1], p2[1]]):
                if p1[0] != p2[0]:
                    xinters = (x - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1]
                    if p1[1] == p2[1] or y <= xinters:
                        counter += 1

        p1 = p2

    if counter % 2 == 0:
        return False
    else:
        return True

if __name__ == '__mian__':
    # distance_point_point
    p1, p2 = np.array([0, 0]), np.array([1, 1])
    distance = distance_point_point(p1, p2)
    assert distance - 1.4142135623730951 < .000001

    # distance_segment_point
    p = np.array([0, 5])
    segment = [np.array([0, 0]), np.array([10, 10])]
    distance = distance_point_segment(p, segment)
    assert distance - 3.5355339059327373 < .000001

    # segments_of_polyline
    polyline = [
        np.array([0, 0]),
        np.array([10, 10]),
        np.array([20, 8]),
        np.array([30, 25]),
        np.array([45, 30])
    ]
    segments = segments_of_polyline(polyline)
    assert segments == [
        [np.array([0, 0]), np.array([10, 10])],
        [np.array([10, 10]), np.array([20,  8])],
        [np.array([20,  8]), np.array([30, 25])],
        [np.array([30, 25]), np.array([45, 30])]
    ]

    # distance_polyline_point
    distance = distance_point_polyline(p, polyline)
    assert distance - 3.5355339059327373 < .000001

    # point_at_parameter_segement
    p = point_at_parameter_segement(segment, .5)
    assert p == np.array([5, 5])

    # point_at_parameter_polyline
    p = point_at_parameter_polyline(polyline, .3)


def stiffness(n_elem, E, Iz, L, fixed_dofs):

    #Assemblage of the stiffness matrix for a 1D Euler-Bernoulli beam.
    #Is a uniformly distributed Finite Element discretization of a beam with
    #n_elem elements, length L, Young Modulus E, Flexural Inertia Iz and
    #boundary conditions fixed_dofs.

    import numpy as np

    L = L / n_elem #Unioform discretization, size of each element is total
                   # length divided by number of elements.

    k = np.array([[12 * E * Iz / L ** 3, 6 * E * Iz / L ** 2,\
                  -12 * E * Iz / L ** 3, 6 * E * Iz / L ** 2],
                 [  6 * E * Iz / L ** 2, 4 * E * Iz / L,\
                   -6 * E * Iz / L ** 2, 2 * E * Iz / L],
                 [-12 * E * Iz / L ** 3,-6 * E * Iz / L ** 2,\
                   12 * E * Iz / L ** 3,-6 * E * Iz / L ** 2],
                 [  6 * E * Iz / L ** 2, 2 * E * Iz / L,\
                   -6 * E * Iz / L ** 2 ,4 * E * Iz / L]])

    dof_node = 2 #Number of DOFs per node.

    tot_dofs = n_elem * dof_node + dof_node #Total number of DOFs.

    k_struct = np.zeros([tot_dofs,tot_dofs])

    for i in range(n_elem):
        k_struct[2*i:2*i+4, 2*i:2*i+4] += k #Assembly to structure size.

    for dof in fixed_dofs:
        for i in [0, 1]: #Removal of fixed DOFs.
            k_struct = np.delete(k_struct, dof, axis = i)

    return  k_struct

def mass_matrix(n_elem, rho, A, L, fixed_dofs):
    #Assembly of consistent mass matrix of an Euler-Bernoulli beam with density
    #rho, cross-sectional area A, lenght L and BCs fixed_dofs.

    import numpy as np

    Le = L / n_elem
    mass  = rho * A * Le #kg

    #Local mass matrix
    m = mass / 420 * np.array([[156, 22 * Le, 54, -13 * Le],
                               [22 * Le, 4 * Le ** 2, 13 * Le, -3 * Le ** 2],
                               [54, 13  * Le, 156, -22 * Le],
                               [-13 * Le, -3 * Le ** 2, -22 * Le, 4 * Le ** 2]])
    dof_node = 2
    dof_elem = 4
    tot_dofs = n_elem * dof_node + 2

    m_struct = np.zeros([tot_dofs,tot_dofs])

    for i in range(n_elem):
        m_struct[2*i:2*i+4, 2*i:2*i+4] += m #Assembly.

    for dof in fixed_dofs:
        for i in [0, 1]:
            m_struct = np.delete(m_struct, dof, axis = i)

    return  m_struct

def moment_recovery(displ, n_elem, L):

    import numpy as np

    L = L / n_elem

    B_x = lambda x: np.array([- 6 / L ** 2 + 12 * x / L ** 3, \
                              - 4 / L      +  6 * x / L ** 2, \
                                6 / L ** 2 - 12 * x / L ** 3, \
                              - 2 / L      +  6 * x / L ** 2])

    timesteps = np.arange(0, np.size(displ, 1))
    stress_elem = np.zeros([n_elem, np.size(timesteps)])

    for time in timesteps:
        displ_t = np.concatenate(([0, 0], displ[:, time], [0, 0]))
        for elem in range(n_elem):
           stress_elem[elem, time] = - B_x(L / 2) @ displ_t[2*elem:2*elem+4]

    return stress_elem

def node_mapping(x, n_elem):
    #Returns the node for a given x in [0, 1]
    import numpy as np
    import pdb

    n_nod = n_elem + 1
    dof_per_node = 2
    tot_dofs = dof_per_node * n_nod

    x_axis = np.linspace(0, 1, n_nod)

    node_nbr = np.abs(x_axis - x) < 1e-4

    grouped = np.array(list(zip(*[iter(range(tot_dofs))] * 2)))  #pairs the dof belonging to each node. i.e. [(0,1), (2,3), ...]

    return grouped[node_nbr]


# -*- coding: utf-8 -*-
"""3D functions."""
import numpy as np

def angle_between_two_vectors(v1, v2):
    """Compute the angle between the angle between v1 and v2 (can be
    of any dimension).

    :param v1:
    :param v2:
    :type v1: tuple, list, `np.array`
    :type v2: tuple, list, `np.array`
    :return: a float between [0, pi]
    """
    dot_product = sum([a*b for a, b in zip(v1, v2)])
    len_v1 = np.sqrt(np.sum([a**2 for a in v1]))
    len_v2 = np.sqrt(np.sum([a**2 for a in v2]))

    _ = dot_product / (len_v1 * len_v2)

    if _ > 1:
        _ = 1
    elif _ < -1:
        _ = -1
    else:
        pass

    return np.arccos(_)


#!/usr/bin/env python3
#coding=utf-8

# imports
import numpy as np
import matplotlib.pyplot as plt

from QR_method.QR_algorithm import QR_algorithm

def make_tridiagonal_matrix(n,alfa,beta):
    A = alfa*np.eye(n)
    for i in range (0,n-1):
        A[i,i+1] = beta
        A[i+1,i] = beta
    return A

def test_matrix_values(A,V,lamb):
    print("V = \n", V)
    print("lamb \n= ",lamb)
    new_A = V@lamb@np.transpose(V)
    new_A = np.matrix.round(new_A,3)
    print("A = \n", A)
    print("V Λ V_transposto = \n", new_A)

def assignment_a():
    erro = 1e-6
    alfa = 2
    beta = -1

    k_with_spectral_shift_list = np.array([])
    k_without_spectral_shift_list = np.array([])
    print("Assignment A: Example of using the QR algorithm in a tridiagonal matrix")
    print("For this assignment we used: \n 'n' as size of the matirx \n 'k' as number of iterations")
    n_list = np.array([])
    for i in range (2,6):
        n = 2**i
        print("For n = ", n)
        n_list = np.append(n_list,n)
        A_matrix = make_tridiagonal_matrix(n,alfa,beta)

        V,lamb,k = QR_algorithm(A_matrix,erro = erro,spectral_shift = True)
        k_with_spectral_shift_list = np.append(k_with_spectral_shift_list,k)
        print("k with spectral shift = ", k)
        #test_matrix_values(A_matrix,V,lamb)

        V,lamb,k = QR_algorithm(A_matrix,erro = erro,spectral_shift = False)
        k_without_spectral_shift_list = np.append(k_without_spectral_shift_list,k)
        print("k without spectral shift = ", k)
        #test_matrix_values(A_matrix,V,lamb)

    plt.xlim(min(n_list), max(n_list))
    plt.ylim(0, 2000)

    plt.plot(n_list,k_with_spectral_shift_list,label='k_with_spectral_shift')
    plt.plot(n_list,k_without_spectral_shift_list,label='k_without_spectral_shift')
    plt.ylabel('number of iterations')
    plt.xlabel('matrix (nxn)')

    plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.15), ncol=2, frameon=False)
    plt.show()

if __name__ == "__main__":
    assignment_a()


"""
Correlation based distances and various modifications (angular, absolute, squared) described in Cornell lecture notes:
Codependence: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3512994&download=yes
"""

import numpy as np
from scipy.spatial.distance import squareform, pdist

# pylint: disable=invalid-name

def angular_distance(x: np.array, y: np.array) -> float:
    """
    Returns angular distance between two vectors. Angular distance is a slight modification of correlation which
    satisfies metric conditions.

    :param x: (np.array) X vector.
    :param y: (np.array) Y vector.
    :return: (float) angular distance.
    """
    corr_coef = np.corrcoef(x, y)[0][1]
    return np.sqrt(0.5 * (1 - corr_coef))

def absolute_angular_distance(x: np.array, y: np.array) -> float:
    """
    Returns a modification of angular distance where absolute value of correlation coefficient is used.

    :param x: (np.array) x vector
    :param y: (np.array) y vector
    :return: (float) absolute angular distance
    """

    corr_coef = np.corrcoef(x, y)[0][1]
    return np.sqrt(0.5 * (1 - abs(corr_coef)))

def squared_angular_distance(x: np.array, y: np.array) -> float:
    """
    Returns a modification of angular distance where square of correlation coefficient is used.

    :param x: (np.array) X vector
    :param y: (np.array) Y vector
    :return: (float) squared angular distance
    """

    corr_coef = np.corrcoef(x, y)[0][1]
    return np.sqrt(0.5 * (1 - corr_coef ** 2))

def distance_correlation(x: np.array, y: np.array) -> float:
    """
    Distance correlation captures both linear and non-linear dependencies.
    Distance correlation coefficient is described in https://en.wikipedia.org/wiki/Distance_correlation

    :param x: (np.array) X vector
    :param y: (np.array) Y vector
    :return: (float) distance correlation coefficient
    """

    x = x[:, None]
    y = y[:, None]

    x = np.atleast_2d(x)
    y = np.atleast_2d(y)

    a = squareform(pdist(x))
    b = squareform(pdist(y))

    A = a - a.mean(axis=0)[None, :] - a.mean(axis=1)[:, None] + a.mean()
    B = b - b.mean(axis=0)[None, :] - b.mean(axis=1)[:, None] + b.mean()

    d_cov_xx = (A * A).sum() / (x.shape[0] ** 2)
    d_cov_xy = (A * B).sum() / (x.shape[0] ** 2)
    d_cov_yy = (B * B).sum() / (x.shape[0] ** 2)

    coef = np.sqrt(d_cov_xy) / np.sqrt(np.sqrt(d_cov_xx) * np.sqrt(d_cov_yy))

    return coef


"""
Programming problems related to prime numbers.  A good source of fodder
for number theory and prime number problems is the youtube channel
https://www.youtube.com/user/numberphile
"""
from collections import namedtuple
from sympy import isprime

# this produces the left truncatable prime with the larges value
# there must be more graceful ways to do it
def largest_ltrunc():
	"""
	Computes the largest left truncatable prime https://youtu.be/azL5ehbw_24
	A left truncatable prime is still prime when the left most digit is
	dropped.  For example 317 -> 17 -> 7 are prime so 317 is a left
	truncatable prime

	returns 357686312646216567629137
	"""

	# Prime.value is the value of the prime number
	# Prime.k is the power of 10 needed to generate the first digit
	Prime = namedtuple('Prime',['value','k'])

	# list single digit prime numbers
	prime_list = [Prime(x,0) for x in (2,3,5,7)]

	# the largest single digit prime number
	N = max([prime.value for prime in prime_list])

	# pop from the front of the list then
	# keep adding left truncatable primes derived from it
	while prime_list:
		prime = prime_list.pop(0)

		# digits 1,...,9 are valid left additions
		for t in range(1,10):
			n = t*10**(prime.k + 1) + prime.value

			# if the number is prime it is a left truncatable prime
			# add it to the list and see if it is the largest
			if isprime(n):
				prime_list.append(Prime(n,prime.k + 1))
				N = max(n,N)

	return N

# this counts the number of left truncatable primes
def count_ltrunc():
	"""
	Counts the number of left truncatable primes https://youtu.be/azL5ehbw_24
	A left truncatable prime is still prime when the left most digit is
	dropped.  For example 317 -> 17 -> 7 are prime so 317 is a left
	truncatable prime

	returns 4260
	"""

	# Prime.value is the value of the prime number
	# Prime.k is the power of 10 needed to generate the first digit
	Prime = namedtuple('Prime',['value','k'])

	# list single digit prime numbers
	prime_list = [Prime(x,0) for x in (2,3,5,7)]

	count = 0

	# pop from the front of the list then
	# keep adding left truncatable primes derived from it
	while prime_list:
		prime = prime_list.pop(0)
		count += 1

		# digits 1,...,9 are valid left additions
		for t in range(1,10):
			n = t*10**(prime.k + 1) + prime.value

			# if the number is prime it is a left truncatable prime
			# add it to the list and see if it is the largest
			if isprime(n):
				prime_list.append(Prime(n,prime.k + 1))

	return count

# an iterator that produces left truncatable primes in increasing order
# while storing a minimal number of unused values
def ordered_ltrunc():
	"""
	Iterates through the left truncatable prime https://youtu.be/azL5ehbw_24
	A left truncatable prime is still prime when the left most digit is
	dropped.  For example 317 -> 17 -> 7 are prime so 317 is a left
	truncatable prime

	returns an iterator that goes through them in increasing order
	"""

	# Prime.value is the value of the prime number
	# Prime.k is the power of 10 needed to generate the first digit
	Prime = namedtuple('Prime',['value','k'])

	# list single digit prime numbers
	working_list = [Prime(x,0) for x in (2,3,5,7)]

	# stores the set of left truncatable primes that have one more
	# digit than the working list
	expanding_list = []

	# the expanding is built with the primes ordered
	while working_list:
		# digits 1,...,9 are valid left additions
		for t in range(1,10):
			for prime in working_list:
				n = t*10**(prime.k + 1) + prime.value
				# the left additions just got constructed in ascending order
				# if the number is prime it is a left truncatable prime
				# add it to the expanding list
				if isprime(n):
					expanding_list.append(Prime(n,prime.k + 1))

		# the expanding list is built so yield the working list one by one
		while working_list:
			yield working_list.pop(0).value

		# working list is exhausted - replace it with the expanding list
		working_list = expanding_list

		# start a new expanding list
		expanding_list = []


#!/usr/bin/pxi2thon
# -*- coding: utf-8 -*-

from sympy import *
from sympy.abc import *

xi1,xi2 = symbols('xi1 xi2')
Phi1, Phi2, Phi3 = symbols('Phi1 Phi2 Phi3')
# 3 4
# 1 2
x11, x12, x13 = symbols('x11 x12, x13')
x21, x22, x23 = symbols('x21 x22, x23')
x31, x32, x33 = symbols('x31 x32, x33')
x41, x42, x43 = symbols('x41 x42, x43')

# arbitrary 2D case

# define Phi(xi)
Phi1 = (1-xi1)*(1-xi2)*x11 + xi1*(1-xi2)*x21 + (1-xi1)*xi2*x31 + xi1*xi2*x41
Phi2 = (1-xi1)*(1-xi2)*x12 + xi1*(1-xi2)*x22 + (1-xi1)*xi2*x32 + xi1*xi2*x42
Phi3 = (1-xi1)*(1-xi2)*x13 + xi1*(1-xi2)*x22 + (1-xi1)*xi2*x33 + xi1*xi2*x43

# compute J2
dPhi1_dxi1 = diff(Phi1, xi1)
dPhi2_dxi1 = diff(Phi2, xi1)
dPhi3_dxi1 = diff(Phi3, xi1)
dPhi1_dxi2 = diff(Phi1, xi2)
dPhi2_dxi2 = diff(Phi2, xi2)
dPhi3_dxi2 = diff(Phi3, xi2)

dPhi_dxi1_squared = dPhi1_dxi1**2 + dPhi2_dxi1**2 + dPhi3_dxi1**2
dPhi_dxi2_squared = dPhi1_dxi2**2 + dPhi2_dxi2**2 + dPhi3_dxi2**2
dPhi_dxi1_dxi2 = dPhi1_dxi1*dPhi1_dxi2 + dPhi2_dxi1*dPhi2_dxi2 + dPhi3_dxi1*dPhi3_dxi2

gram_det = dPhi_dxi1_squared*dPhi_dxi2_squared - dPhi_dxi1_dxi2**2
j = sqrt(gram_det)

print "arbitrary J_2:", j

# rectangular cartesian 2D
l1, l2 = symbols('l1 l2')
Phi1 = x11 + xi1*l1
Phi2 = x12 + xi2*l2
Phi3 = 0

# compute J2
dPhi1_dxi1 = diff(Phi1, xi1)
dPhi2_dxi1 = diff(Phi2, xi1)
dPhi3_dxi1 = diff(Phi3, xi1)
dPhi1_dxi2 = diff(Phi1, xi2)
dPhi2_dxi2 = diff(Phi2, xi2)
dPhi3_dxi2 = diff(Phi3, xi2)

dPhi_dxi1_squared = dPhi1_dxi1**2 + dPhi2_dxi1**2 + dPhi3_dxi1**2
dPhi_dxi2_squared = dPhi1_dxi2**2 + dPhi2_dxi2**2 + dPhi3_dxi2**2
dPhi_dxi1_dxi2 = dPhi1_dxi1*dPhi1_dxi2 + dPhi2_dxi1*dPhi2_dxi2 + dPhi3_dxi1*dPhi3_dxi2

gram_det = dPhi_dxi1_squared*dPhi_dxi2_squared - dPhi_dxi1_dxi2**2
j = sqrt(gram_det)

print "RC J_2:", simplify(j)


from scipy.special import legendre
import numpy as np

def legendre_prime(x, n):
    """Calculate first derivative of the nth Legendre Polynomial recursively.

    Args:
        x (float,np.array) = domain.
        n (int) = degree of Legendre polynomial (L_n).
    Return:
        legendre_p (np.array) = value first derivative of L_n.
    """
    # P'_n+1 = (2n+1) P_n + P'_n-1
    # where P'_0 = 0 and P'_1 = 1
    # source: http://www.physicspages.com/2011/03/12/legendre-polynomials-recurrence-relations-ode/
    if n == 0:
        if isinstance(x, np.ndarray):
            return np.zeros(len(x))
        elif isinstance(x, (int, float)):
            return 0
    if n == 1:
        if isinstance(x, np.ndarray):
            return np.ones(len(x))
        elif isinstance(x, (int, float)):
            return 1
    legendre_p = (n * legendre(n - 1)(x) - n * x * legendre(n)(x))/(1-x**2)
    return legendre_p

def legendre_prime_lobatto(x,n):
    return (1-x**2)**2*legendre_prime(x,n)

def legendre_double_prime(x, n):
    """Calculate second derivative legendre polynomial recursively.

    Args:
        x (float,np.array) = domain.
        n (int) = degree of Legendre polynomial (L_n).
    Return:
        legendre_pp (np.array) = value second derivative of L_n.
    """
    legendre_pp = 2 * x * legendre_prime(x, n) - n * (n + 1) * legendre(n)(x)
    return legendre_pp * (1 - x ** 2)


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

def de_mean(x):
    xmean = np.mean(x)
    return [xi - xmean for xi in x]

def covariance(x, y):
    n = len(x)
    return np.dot(de_mean(x), de_mean(y)) / (n-1)

MySample = np.random.rand(10, 3)*50
MySample = np.rint(MySample)
MySample = MySample.astype(np.int8)
print(MySample)

dim1 = MySample[:,0]
dim2 = MySample[:,1]
dim3 = MySample[:,2]
cov12 = covariance(dim1, dim2)
cov13 = covariance(dim1, dim3)
cov23 = covariance(dim2, dim3)

var1 = np.var(dim1)
var2 = np.var(dim2)
var3 = np.var(dim3)
tvar1 = np.sum((dim1 - np.mean(dim1)) ** 2) / (len(dim1)-1)
tvar2 = np.sum((dim2 - np.mean(dim2)) ** 2) / (len(dim2)-1)
tvar3 = np.sum((dim3 - np.mean(dim3)) ** 2) / (len(dim3)-1)

print(
np.array(
    [
        [var1, cov12, cov13],
        [cov12, var2, cov23],
        [cov13, cov23, var3],
    ]
)
)

print(
np.array(
    [
        [tvar1, cov12, cov13],
        [cov12, tvar2, cov23],
        [cov13, cov23, tvar3],
    ]
)
)

np_covariance = np.cov(MySample, rowvar=False)
print(np_covariance)


import numpy as np
from numpy.linalg import svd
from scipy.io import loadmat
import matplotlib.pyplot as plt

def featureNormalize(X):
    """
    Normalize the dataset X

    :param X:
    :return:
    """

    mu = np.mean(X, axis=0)
    sigma = np.std(X, axis=0)

    X_normalized = (X - mu) / sigma

    return X_normalized, mu, sigma

def pca(X):
    """
    Compute eigenvectors of the covariance matrix X

    :param X:
    :return:
    """

    number_of_examples = X.shape[0]
    sigma = (1/number_of_examples) * np.dot(X.T, X)
    U, S, V = svd(sigma)

    return U, S, V

def projectData(X, U, K):
    """
    Computes the reduced data representation when projecting only onto
    the top K eigenvectors

    :param X: Dataset
    :param U: Principal components
    :param K: The desired number of dimensions to reduce
    :return:
    """

    number_of_examples = X.shape[0]
    U_reduced = U[:, :K]

    Reduced_representation = np.zeros((number_of_examples, K))

    for i in range(number_of_examples):
        for j in range(K):
            Reduced_representation[i, j] = np.dot(X[i, :], U_reduced[:, j])

    return Reduced_representation

def recoverData(Z, U, K):
    """
    Recovers an approximation of the original data when using the projected data

    :param Z: Reduced representation
    :param U: Principal components
    :param K: The desired number of dimensions to reduce
    :return:
    """

    number_of_examples = Z.shape[0]
    number_of_features = U.shape[0]

    X_recovered = np.zeros((number_of_examples, number_of_features))
    U_reduced = U[:, :K]

    for i in range(number_of_examples):
        X_recovered[i, :] = np.dot(Z[i, :], U_reduced.T)

    return X_recovered

'''
Step 0: Load the dataset
'''

dataset = loadmat("data/lab9faces.mat")
print(dataset.keys(), '\n')

X = dataset["X"]

'''
Step 1: Visualize the dataset
'''

figure1, axes1 = plt.subplots(nrows=10, ncols=10, figsize=(6, 6))

for i in range(0, 100, 10):
    for j in range(10):
        axes1[int(i / 10), j].imshow(X[i + j, :].reshape(32, 32, order="F"), cmap="gray")
        axes1[int(i / 10), j].axis("off")

'''
Step 2: Normalize the dataset and run PCA on it
'''

X_normalized = featureNormalize(X)[0]  # [0] for converting from 'tuple' to 'array'

# Run PCA
U = pca(X_normalized)[0]  # [0] for converting from 'tuple' to 'array'

# Visualize the top 100 eigenvectors found
U_reduced = U[:, :100].T
figure2, axes2 = plt.subplots(nrows=10, ncols=10, figsize=(6, 6))

for i in range(0, 100, 10):
    for j in range(10):
        axes2[int(i / 10), j].imshow(U_reduced[i + j, :].reshape(32, 32, order="F"), cmap="gray")
        axes2[int(i / 10), j].axis("off")

'''
Step 3: Reduce the dimension of the face dataset from 1024 dimension to 100 dimension
'''

reduced_representation = projectData(X_normalized, U, 400)

print("The projected dataset has a shape of: {} whereas".format(reduced_representation.shape))
print("the original dataset has a shape of: {}".format(X_normalized.shape))

X_recovered = recoverData(reduced_representation, U, 400)

# Visualize the reconstructed data
figure3, axes3 = plt.subplots(nrows=10, ncols=10, figsize=(6, 6))
for i in range(0, 100, 10):
    for j in range(10):
        axes3[int(i / 10), j].imshow(X_recovered[i + j, :].reshape(32, 32, order="F"), cmap="gray")
        axes3[int(i / 10), j].axis("off")


from numpy import log, dot, e
from numpy.random import rand

class LogisticRegression:

    def sigmoid(self, z):
        return 1 / (1 + e**(-z))

    def cost_function(self, X, y, weights):
        z = dot(X, weights)
        predict_1 = y * log(self.sigmoid(z))
        predict_0 = (1 - y) * log(1 - self.sigmoid(z))
        return -sum(predict_1 + predict_0) / len(X)

    def fit(self, X, y, epochs=25, lr=0.05):
        loss = []
        weights = rand(X.shape[1])
        N = len(X)

        for _ in range(epochs):
            # Gradient Descent
            y_hat = self.sigmoid(dot(X, weights))
            weights -= lr * dot(X.T,  y_hat - y) / N
            # Saving Progress
            loss.append(self.cost_function(X, y, weights))

        self.weights = weights
        self.loss = loss

    def predict(self, X):
        # Predicting with sigmoid function
        z = dot(X, self.weights)
        # Returning binary result
        return [1 if i > 0.5 else 0 for i in self.sigmoid(z)]


#' % Linear Regression model with Python
#' % Matti Pastell
#' % 19.4.2013

#' #Requirements
#' This en example of doing linear regression analysis using Python
#' and [statsmodels](http://statsmodels.sourceforge.net). We'll use the new formula API
#' which makes fitting the models very familiar for R users.
#' You'll also need [Numpy](http://www.numpy.org/), [Pandas](http://pandas.pydata.org/)
#' and [matplolib](http://matplotlib.org/).

#' The analysis can be published using  Pweave 0.22 and later.

#' Import libraries

import pandas as pd
import numpy as np
import statsmodels.formula.api as sm
import statsmodels
import matplotlib.pyplot as plt

#' Statsmodels api seems to change often, check release version:
#+ term=True

statsmodels.__version__

#' We'll use [whiteside](http://stat.ethz.ch/R-manual/R-patched/library/MASS/html/whiteside.html) dataset from R package MASS. You can read the description of the dataset from the link, but in short it contains:

#' >*The weekly gas consumption and average external temperature at a house in south-east England for two
#' heating seasons, one of 26 weeks before, and one of 30 weeks after cavity-wall insulation was installed.*

#' Load dataset using Pandas:

url = 'https://raw.githubusercontent.com/mpastell/Rdatasets/master/csv/MASS/whiteside.csv'
whiteside = pd.read_csv(url)

#' # Fitting the model
#' Let's see what the relationship between the gas consumption is before the insulation.
#' See [statsmodels documentation](http://statsmodels.sourceforge.net/devel/example_formulas.html)
#' for more information about the syntax.

model = sm.ols(formula='Gas ~ Temp', data=whiteside, subset = whiteside['Insul']=="Before")
fitted = model.fit()
print(fitted.summary())

#' # Plot the data and fit

Before = whiteside[whiteside["Insul"] == "Before"]
plt.plot(Before["Temp"], Before["Gas"], 'ro')
plt.plot(Before["Temp"], fitted.fittedvalues, 'b')
plt.legend(['Data', 'Fitted model'])
plt.ylim(0, 10)
plt.xlim(-2, 12)
plt.xlabel('Temperature')
plt.ylabel('Gas')
plt.title('Before Insulation')

#' # Fit diagnostiscs
#' Statsmodels [OLSresults](http://statsmodels.sourceforge.net/devel/generated/statsmodels.regression.linear_model.OLSResults.html) objects contain the usual diagnostic information about the model and you can use the `get_influence()` method to get more diagnostic information (such as Cook's distance).

#' ## A look at the residuals
#' Histogram of normalized residuals

plt.hist(fitted.resid_pearson)
plt.ylabel('Count')
plt.xlabel('Normalized residuals')

#' ## Cooks distance

#' [OLSInfluence](http://statsmodels.sourceforge.net/devel/generated/statsmodels.stats.outliers_influence.OLSInfluence.html)
#'  objects contain more diagnostic information

influence = fitted.get_influence()
#c is the distance and p is p-value
(c, p) = influence.cooks_distance
plt.stem(np.arange(len(c)), c, markerfmt=",")

#' # Statsmodels builtin plots

#' Statsmodels includes a some builtin function for plotting residuals against leverage:

from statsmodels.graphics.regressionplots import *
plot_leverage_resid2(fitted)
influence_plot(fitted)

import numpy,import statsmodels,from statsmodels


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

# In[2]:

import numpy as np

# In[4]:

minha_lista = [1,2,3]

# In[6]:

minha_lista

# In[8]:

np.array(minha_lista) #Vai converter minha lista em um Array de Python

# In[41]:

minha_matriz = [[1,2,3], [4,5,6],[7,8,9]]

# In[42]:

minha_matriz

# In[43]:

np.array(minha_matriz)

# In[16]:

np.arange(0, 10, 2) #Cria uma sequência de números pulando de acordo com a constante dada

# In[17]:

np.zeros(5)

# In[19]:

arr = np.zeros((5,5))

# In[21]:

arr

# In[22]:

np.ones(10)

# In[24]:

arr2 = np.ones((10,10))

# In[26]:

arr2

# In[28]:

np.eye(5) # Cria uma matrix identidade

# In[30]:

np.linspace(0,10,5) # Números igualmente espaçados

# In[32]:

np.random.rand(5) # Números entre 0 e 1

# In[34]:

np.random.rand(5)*100

# In[40]:

np.random.rand(5,5) # 5 Linhas e 5 colunas

# In[46]:

np.random.rand(5,5,5) # Matriz de 3 dimensões

# In[48]:

np.random.randn(4) # Distribuição Gauciana (ver em estatística)

# In[50]:

np.random.randint(0,100,10) # Tirei 10 números dentro intervalo de 100 números.

# In[52]:

arr3 = np.random.rand(25)

# In[54]:

arr3

# In[69]:

arr3.reshape((5,5))

# In[70]:

arr3.shape

# In[72]:

arr.shape

# In[74]:

arr3.max()

# In[76]:

arr3.min()

# In[78]:

arr3.argmax() # Vai retornar o índice na qual o maior elemento se encontra.

# In[80]:

arr3.argmin() # Vai retornar o índice na qual o menor elemento se encontra.


# 100 numpy exercises

# This is a collection of exercises that have been collected in the numpy mailing list, on stack overflow
# and in the numpy documentation. The goal of this collection is to offer a quick reference for both old
# and new users but also to provide a set of exercises for those who teach.

# If you find an error or think you've a better way to solve some of them, feel
# free to open an issue at <https://github.com/rougier/numpy-100>.
# File automatically generated. See the documentation to update questions/answers/hints programmatically.

#### 1. Import the numpy package under the name `np` (★☆☆)
import numpy as np

#### 2. Print the numpy version and the configuration (★☆☆)
print(np.__version__)

#### 3. Create a null vector of size 10 (★☆☆)
null_vec_10 = np.zeros(10)

#### 4. How to find the memory size of any array (★☆☆)
print(null_vec_10.nbytes)

#### 5. How to get the documentation of the numpy add function from the command line? (★☆☆)

#help(np.add)

#### 6. Create a null vector of size 10 but the fifth value which is 1 (★☆☆)
x = np.zeros(10)
x[4] = 1

#### 7. Create a vector with values ranging from 10 to 49 (★☆☆)
x = np.array(range(10, 50))

#### 8. Reverse a vector (first element becomes last) (★☆☆)
x = np.flip(x)

#### 9. Create a 3x3 matrix with values ranging from 0 to 8 (★☆☆)
x = np.array([range(0, 3),
              range(3, 6),
              range(6, 9)])

#### 10. Find indices of non-zero elements from [1,2,0,0,4,0] (★☆☆)
x = np.array([1,2,0,0,4,0])
non0_indices = np.where(x != 0)[0]

#### 11. Create a 3x3 identity matrix (★☆☆)
x = np.identity(3)

#### 12. Create a 3x3x3 array with random values (★☆☆)
x = np.random.rand(3, 3, 3)

#### 13. Create a 10x10 array with random values and find the minimum and maximum values (★☆☆)
x = np.random.rand(10, 10)
x_min = np.min(x)
x_max = np.max(x)

#### 14. Create a random vector of size 30 and find the mean value (★☆☆)
x = np.random.rand(30)
x_mean = np.mean(x)

#### 15. Create a 2d array with 1 on the border and 0 inside (★☆☆)
ones = np.ones(3, dtype=int)
x = np.array([ones, [1, 0, 1], ones])

#### 16. How to add a border (filled with 0's) around an existing array? (★☆☆)
x = np.pad(x, pad_width=1, mode='constant', constant_values=0)

#### 17. What is the result of the following expression? (★☆☆)

# 0 * np.nan                   # nan
# np.nan == np.nan             # False
# np.inf > np.nan              # False
# np.nan - np.nan              # nan
# np.nan in set([np.nan])      # True
# 0.3 == 3 * 0.1               # False

#### 18. Create a 5x5 matrix with values 1,2,3,4 just below the diagonal (★☆☆)
x = np.diag([1, 2, 3, 4], -1)

#### 19. Create a 8x8 matrix and fill it with a checkerboard pattern (★☆☆)
x = np.indices([8, 8]).sum(axis=0) % 2

#### 20. Consider a (6,7,8) shape array, what is the index (x,y,z) of the 100th element? (★☆☆)
print (np.unravel_index(100, (6,7,8)))

#### 21. Create a checkerboard 8x8 matrix using the tile function (★☆☆)

#### 22. Normalize a 5x5 random matrix (★☆☆)

#### 23. Create a custom dtype that describes a color as four unsigned bytes (RGBA) (★☆☆)

#### 24. Multiply a 5x3 matrix by a 3x2 matrix (real matrix product) (★☆☆)

#### 25. Given a 1D array, negate all elements which are between 3 and 8, in place. (★☆☆)

#### 26. What is the output of the following script? (★☆☆)
#```python
# Author: Jake VanderPlas

#print(sum(range(5),-1))
#from numpy import *
#print(sum(range(5),-1))
#```

#### 27. Consider an integer vector Z, which of these expressions are legal? (★☆☆)
# ```python
# Z**Z
# 2 << Z >> 2
# Z <- Z
# 1j*Z
# Z/1/1
# Z<Z>Z
#```

#### 28. What are the result of the following expressions? (★☆☆)
#```python
# np.array(0) / np.array(0)
# np.array(0) // np.array(0)
# np.array([np.nan]).astype(int).astype(float)
#```

#### 29. How to round away from zero a float array ? (★☆☆)

#### 30. How to find common values between two arrays? (★☆☆)

#### 31. How to ignore all numpy warnings (not recommended)? (★☆☆)

#### 32. Is the following expressions true? (★☆☆)
#```python
#np.sqrt(-1) == np.emath.sqrt(-1)
#```

#### 33. How to get the dates of yesterday, today and tomorrow? (★☆☆)

#### 34. How to get all the dates corresponding to the month of July 2016? (★★☆)

#### 35. How to compute ((A+B)*(-A/2)) in place (without copy)? (★★☆)

#### 36. Extract the integer part of a random array of positive numbers using 4 different methods (★★☆)

#### 37. Create a 5x5 matrix with row values ranging from 0 to 4 (★★☆)

#### 38. Consider a generator function that generates 10 integers and use it to build an array (★☆☆)

#### 39. Create a vector of size 10 with values ranging from 0 to 1, both excluded (★★☆)

#### 40. Create a random vector of size 10 and sort it (★★☆)

#### 41. How to sum a small array faster than np.sum? (★★☆)

#### 42. Consider two random array A and B, check if they are equal (★★☆)

#### 43. Make an array immutable (read-only) (★★☆)

#### 44. Consider a random 10x2 matrix representing cartesian coordinates, convert them to polar coordinates (★★☆)

#### 45. Create random vector of size 10 and replace the maximum value by 0 (★★☆)

#### 46. Create a structured array with `x` and `y` coordinates covering the [0,1]x[0,1] area (★★☆)

#### 47. Given two arrays, X and Y, construct the Cauchy matrix C (Cij =1/(xi - yj)) (★★☆)

#### 48. Print the minimum and maximum representable value for each numpy scalar type (★★☆)

#### 49. How to print all the values of an array? (★★☆)

#### 50. How to find the closest value (to a given scalar) in a vector? (★★☆)

#### 51. Create a structured array representing a position (x,y) and a color (r,g,b) (★★☆)

#### 52. Consider a random vector with shape (100,2) representing coordinates, find point by point distances (★★☆)

#### 53. How to convert a float (32 bits) array into an integer (32 bits) in place?

#### 54. How to read the following file? (★★☆)
#```
#1, 2, 3, 4, 5
#6,  ,  , 7, 8
# ,  , 9,10,11
#```

#### 55. What is the equivalent of enumerate for numpy arrays? (★★☆)

#### 56. Generate a generic 2D Gaussian-like array (★★☆)

#### 57. How to randomly place p elements in a 2D array? (★★☆)

#### 58. Subtract the mean of each row of a matrix (★★☆)

#### 59. How to sort an array by the nth column? (★★☆)

#### 60. How to tell if a given 2D array has null columns? (★★☆)

#### 61. Find the nearest value from a given value in an array (★★☆)

#### 62. Considering two arrays with shape (1,3) and (3,1), how to compute their sum using an iterator? (★★☆)

#### 63. Create an array class that has a name attribute (★★☆)

#### 64. Consider a given vector, how to add 1 to each element indexed by a second vector (be careful with repeated indices)? (★★★)

#### 65. How to accumulate elements of a vector (X) to an array (F) based on an index list (I)? (★★★)

#### 66. Considering a (w,h,3) image of (dtype=ubyte), compute the number of unique colors (★★☆)

#### 67. Considering a four dimensions array, how to get sum over the last two axis at once? (★★★)

#### 68. Considering a one-dimensional vector D, how to compute means of subsets of D using a vector S of same size describing subset  indices? (★★★)

#### 69. How to get the diagonal of a dot product? (★★★)

#### 70. Consider the vector [1, 2, 3, 4, 5], how to build a new vector with 3 consecutive zeros interleaved between each value? (★★★)

#### 71. Consider an array of dimension (5,5,3), how to mulitply it by an array with dimensions (5,5)? (★★★)

#### 72. How to swap two rows of an array? (★★★)

#### 73. Consider a set of 10 triplets describing 10 triangles (with shared vertices), find the set of unique line segments composing all the  triangles (★★★)

#### 74. Given a sorted array C that corresponds to a bincount, how to produce an array A such that np.bincount(A) == C? (★★★)

#### 75. How to compute averages using a sliding window over an array? (★★★)

#### 76. Consider a one-dimensional array Z, build a two-dimensional array whose first row is (Z[0],Z[1],Z[2]) and each subsequent row is  shifted by 1 (last row should be (Z[-3],Z[-2],Z[-1]) (★★★)

#### 77. How to negate a boolean, or to change the sign of a float inplace? (★★★)

#### 78. Consider 2 sets of points P0,P1 describing lines (2d) and a point p, how to compute distance from p to each line i (P0[i],P1[i])? (★★★)

#### 79. Consider 2 sets of points P0,P1 describing lines (2d) and a set of points P, how to compute distance from each point j (P[j]) to each line i (P0[i],P1[i])? (★★★)

#### 80. Consider an arbitrary array, write a function that extract a subpart with a fixed shape and centered on a given element (pad with a `fill` value when necessary) (★★★)

#### 81. Consider an array Z = [1,2,3,4,5,6,7,8,9,10,11,12,13,14], how to generate an array R = [[1,2,3,4], [2,3,4,5], [3,4,5,6], ..., [11,12,13,14]]? (★★★)

#### 82. Compute a matrix rank (★★★)

#### 83. How to find the most frequent value in an array?

#### 84. Extract all the contiguous 3x3 blocks from a random 10x10 matrix (★★★)

#### 85. Create a 2D array subclass such that Z[i,j] == Z[j,i] (★★★)

#### 86. Consider a set of p matrices wich shape (n,n) and a set of p vectors with shape (n,1). How to compute the sum of of the p matrix products at once? (result has shape (n,1)) (★★★)

#### 87. Consider a 16x16 array, how to get the block-sum (block size is 4x4)? (★★★)

#### 88. How to implement the Game of Life using numpy arrays? (★★★)

#### 89. How to get the n largest values of an array (★★★)

#### 90. Given an arbitrary number of vectors, build the cartesian product (every combinations of every item) (★★★)

#### 91. How to create a record array from a regular array? (★★★)

#### 92. Consider a large vector Z, compute Z to the power of 3 using 3 different methods (★★★)

#### 93. Consider two arrays A and B of shape (8,3) and (2,2). How to find rows of A that contain elements of each row of B regardless of the order of the elements in B? (★★★)

#### 94. Considering a 10x3 matrix, extract rows with unequal values (e.g. [2,2,3]) (★★★)

#### 95. Convert a vector of ints into a matrix binary representation (★★★)

#### 96. Given a two dimensional array, how to extract unique rows? (★★★)

#### 97. Considering 2 vectors A & B, write the einsum equivalent of inner, outer, sum, and mul function (★★★)

#### 98. Considering a path described by two vectors (X,Y), how to sample it using equidistant samples (★★★)?

#### 99. Given an integer n and a 2D array X, select from X the rows which can be interpreted as draws from a multinomial distribution with n degrees, i.e., the rows which only contain integers and which sum to n. (★★★)

#### 100. Compute bootstrapped 95% confidence intervals for the mean of a 1D array X (i.e., resample the elements of an array with replacement N times, compute the mean of each sample, and then compute percentiles over the means). (★★★)


import numpy as np

def generate_brownian(
    n_steps: int,
    n_paths: int,
    volatility: float = 0.2,
    dt: float = 1 / 250,
    drift: float = 0.0,
) -> np.ndarray:
    """Generate Brownian motion.

    Args:
        n_steps (int): Number of time steps.
        n_paths (int): Number of simulated paths.
        volatility (float, default=0.2): Volatility of the motion.
        dt (float, default=1/250): Intervals of time steps.
        drift (float, default=0.0): Drift of the motion.

    Examples:

        >>> np.random.seed(42)
        >>> generate_brownian(5, 2)
        array([[0.        , 0.        ],
               [0.00819268, 0.01926497],
               [0.00523085, 0.01630335],
               [0.02520649, 0.02601072],
               [0.01926806, 0.03287362]])
    """
    randn = np.random.randn(n_steps, n_paths)
    randn[0] = 0.0

    w = randn.cumsum(0)
    drift = (drift * np.arange(n_steps)).reshape(-1, 1)

    return drift + volatility * np.sqrt(dt) * w

def generate_geometric_brownian(
    n_steps: int,
    n_paths: int,
    volatility: float = 0.2,
    dt: float = 1 / 250,
    drift: float = 0.0,
) -> np.ndarray:
    """Generate geometric Brownian motion.

    Args:
        n_steps (int): Number of time steps.
        n_paths (int): Number of simulated paths.
        volatility (float, default=0.2): Volatility of the motion.
        dt (float, default=1/250): Intervals of time steps.
        drift (float, default=0.0): Drift of the motion.

    Examples:

        >>> np.random.seed(42)
        >>> generate_geometric_brownian(5, 2)
        array([[1.        , 1.        ],
               [0.98826212, 0.99926524],
               [0.96582835, 0.97658191],
               [0.96580482, 0.96658186],
               [0.94107547, 0.95396683]])
    """
    w = generate_brownian(
        n_steps=n_steps,
        n_paths=n_paths,
        volatility=volatility,
        dt=dt,
        drift=drift - (volatility ** 2 / 2),
    )

    return np.exp(w)


import numpy as np
from copy import copy

# Example backpropagation code for binary classification with 2-layer
# neural network (single hidden layer)

sigmoid = lambda x: 1 / (1 + np.exp(-x))

def fprop(x, y, params):
  # Follows procedure given in notes
  W1, b1, W2, b2 = [params[key] for key in ('W1', 'b1', 'W2', 'b2')]
  z1 = np.dot(W1, x) + b1
  a1 = sigmoid(z1)
  z2 = np.dot(W2, a1) + b2
  a2 = sigmoid(z2)
  loss = -(y * np.log(a2) + (1-y) * np.log(1-a2))
  ret = {'x': x, 'y': y, 'z1': z1, 'a1': a1, 'z2': z2, 'a2': a2, 'loss': loss}
  for key in params:
    ret[key] = params[key]
  return ret

def bprop(fprop_cache):
  # Follows procedure given in notes
  x, y, z1, a1, z2, a2, loss = [fprop_cache[key] for key in ('x', 'y', 'z1', 'a1', 'z2', 'a2', 'loss')]
  dz2 = (a2 - y)
  dW2 = np.dot(dz2, a1.T)
  db2 = dz2
  dz1 = np.dot(fprop_cache['W2'].T, dz2) * sigmoid(z1) * (1-sigmoid(z1))
  dW1 = np.dot(dz1, x.T)
  db1 = dz1
  return {'b1': db1, 'W1': dW1, 'b2': db2, 'W2': dW2}

# Gradient checking

if __name__ == '__main__':
  # Initialize random parameters and inputs
  W1 = np.random.rand(2,2)
  b1 = np.random.rand(2, 1)
  W2 = np.random.rand(1, 2)
  b2 = np.random.rand(1, 1)
  params = {'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}
  x = np.random.rand(2, 1)
  y = np.random.randint(0, 2)  # Returns 0/1

  fprop_cache = fprop(x, y, params)
  bprop_cache = bprop(fprop_cache)

  # Numerical gradient checking
  # Note how slow this is! Thus we want to use the backpropagation algorithm instead.
  eps = 1e-6
  ng_cache = {}
  # For every single parameter (W, b)
  for key in params:
    param = params[key]
    # This will be our numerical gradient
    ng = np.zeros(param.shape)
    for j in range(ng.shape[0]):
      for k in xrange(ng.shape[1]):
        # For every element of parameter matrix, compute gradient of loss wrt
        # that element numerically using finite differences
        add_eps = np.copy(param)
        min_eps = np.copy(param)
        add_eps[j, k] += eps
        min_eps[j, k] -= eps
        add_params = copy(params)
        min_params = copy(params)
        add_params[key] = add_eps
        min_params[key] = min_eps
        ng[j, k] = (fprop(x, y, add_params)['loss'] - fprop(x, y, min_params)['loss']) / (2 * eps)
    ng_cache[key] = ng

  # Compare numerical gradients to those computed using backpropagation algorithm
  for key in params:
    print key
    # These should be the same
    print(bprop_cache[key])
    print(ng_cache[key])


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Oct 22 07:39:12 2020

Illustrating various approaches to computing ordinary derivatives

@author: zettergm
"""

# Imports
import numpy as np
import matplotlib.pyplot as plt

# Test function and exact derivative
lx=20       #number of points in numerical grid
x=np.linspace(-10,10,lx)
dx=x[1]-x[0]     # grid step size or resolution
y=np.sin(0.5*x)
yprime=0.5*np.cos(0.5*x)
ydprime=-0.25*np.sin(0.5*x)

# Plots
plt.figure(1)
plt.plot(x,y)
plt.plot(x,yprime)
plt.xlabel("x")
plt.ylabel("y and dy/dx")

# Centered difference approximation to the first derivative (recommended approach for most problems)
dydx=np.zeros(lx)
dydx[0]=(y[1]-y[0])/dx                  #cannot do centered difference on the edges; foward difference at the beginning
for ix in range(1,lx-1):
    dydx[ix]=(y[ix+1]-y[ix-1])/2/dx     #centered difference in the interior points
dydx[lx-1]=(y[lx-1]-y[lx-2])/dx         #backward difference on the end

plt.plot(x,dydx,"--")

# Foward difference approximation
dydx_fwd=np.zeros(lx)
dydx_fwd[0]=(y[1]-y[0])/dx                  #foward difference at the beginning
for ix in range(1,lx-1):
    dydx_fwd[ix]=(y[ix+1]-y[ix])/dx     #centered difference in the interior points
dydx_fwd[lx-1]=(y[lx-1]-y[lx-2])/dx         #backward difference on the end

plt.plot(x,dydx_fwd,'.')

# Demo of second derivative, centered formula
d2ydx2=np.zeros(lx)     # note that we are not computing edges values for 2nd deriv.
for ix in range(1,lx-1):
    d2ydx2[ix]=(y[ix+1]-2*y[ix]+y[ix-1])/dx**2

# Plot second derivative and legend
plt.plot(x,ydprime,'-')
plt.plot(x,d2ydx2,'^')
plt.legend(("y","dy/dx","centered","forward","2nd deriv. by hand","2nd deriv."))
plt.show()


"""
@Author=Monotera
"""

import numpy as np

"""
 2x + y   =  7
-5x + 11y = -1
"""

"""
Fucnion de python:

Pre-condiciones
la matriz de coeficientes debe ser cuadrada y de rango completo, es decir,
todas las filas (o, de manera equivalente, las columnas) deben ser linealmente .
independientes; si no es cierto, use lstsq para la mejor “solución” de mínimos
cuadrados del sistema / ecuación.
"""
def python_solution():
    a = np.array([[2,1],[-5,11]])
    b = np.array([7,-1])

    x = np.linalg.solve(a,b);

    #np.dot = producto de dos array
    print(x)
    print(np.allclose(np.dot(a,x),b));

"""
Permite resolver para una sola incognita
"""
def cramer_solution():
    A = np.array([[2,1],[-5,11]])
    b = np.array([7,-1])
    D = np.linalg.det(A)
    N = len(b)
    x = np.zeros(N)

    for i in range(0,N):
        Ai = A.copy()
        Ai.T[i] = b
        Di = np.linalg.det(Ai)
        x[i] = Di/D

    print("Solucion: ", x)
    print("Numero de iteraciones: ", N)

def gauss_jordan_solution():
# Método de Gauss-Jordan
# Solución a Sistemas de Ecuaciones
# de la forma A.X=B

# INGRESO
    A = np.array([[2,1],[-5,11]])

    B = np.array([[7.0],[-1.0]])

# Evitar truncamiento en operaciones
    A = np.array(A,dtype=float)

# Matriz aumentada
    AB = np.concatenate((A,B),axis=1)
    AB0 = np.copy(AB)

# Pivoteo parcial por filas
    tamano = np.shape(AB)
    n = tamano[0]
    m = tamano[1]

    # Para cada fila en AB
    for i in range(0,n-1,1):
        # columna desde diagonal i en adelante
        columna = abs(AB[i:,i])
        dondemax = np.argmax(columna)

    # dondemax no está en diagonal
        if (dondemax !=0):
            # intercambia filas
            temporal = np.copy(AB[i,:])
            AB[i,:] = AB[dondemax+i,:]
            AB[dondemax+i,:] = temporal

    AB1 = np.copy(AB)

# eliminacion hacia adelante
    for i in range(0,n-1,1):
        pivote = AB[i,i]
        adelante = i + 1
        for k in range(adelante,n,1):
            factor = AB[k,i]/pivote
            AB[k,:] = AB[k,:] - AB[i,:]*factor
    AB2 = np.copy(AB)

# elimina hacia atras
    ultfila = n-1
    ultcolumna = m-1
    for i in range(ultfila,0-1,-1):
        pivote = AB[i,i]
        atras = i-1
        for k in range(atras,0-1,-1):
            factor = AB[k,i]/pivote
            AB[k,:] = AB[k,:] - AB[i,:]*factor
            # diagonal a unos
        AB[i,:] = AB[i,:]/AB[i,i]
    X = np.copy(AB[:,ultcolumna])
    X = np.transpose([X])

# SALIDA
    print('Matriz aumentada:')
    print(AB0)
    print('Pivoteo parcial por filas')
    print(AB1)
    print('eliminacion hacia adelante')
    print(AB2)
    print('eliminación hacia atrás')
    print(AB)
    print('solución de X: ')
    print(X)

python_solution()
cramer_solution()
gauss_jordan_solution()


import numpy as np

class Layer:
    def __init__(self, name):
        self.name = name
        self.params = {}
        self.grads = {}

    def __call__(self, inputs):
        raise NotImplementedError

    def backward(self, grad):
        raise NotImplementedError

def xavier_init(input_dims, output_dims):

    return np.random.normal(
            scale=np.sqrt(2 / (input_dims + output_dims)),
            size=(input_dims, output_dims),
    )

def bias_init(output_dims):
    return np.zeros((output_dims,))

class Affine(Layer):
    def __init__(self, name, input_dims, output_dims):
        super().__init__(name)
        self.params['w'] = xavier_init(input_dims, output_dims)
        self.params['b'] = bias_init(output_dims) #np.random.randn(output_dims,)
        self.grads['w'] = np.zeros((input_dims, output_dims))
        self.grads['b'] = np.zeros(output_dims)

    def __call__(self, inputs):
        self.inputs = inputs
        return inputs@self.params['w'] + self.params['b']

    def backward(self, grad):
        # (m,n) @ (n,  1) = (m, 1)
        #print("gradshape", grad.shape)
        self.grads['w'] = self.inputs.T @ grad
        # (n, 1) @ (n, 1) = (1,)
        self.grads['b'] = np.sum(grad, axis=0)
        # return (n, 1) @ (1 , m) = (n, m)
        return grad@self.params['w'].T

class Activation(Layer):
    """
    An activation layer just applies a function
    elementwise to its inputs
    """
    def __init__(self, name, f, f_prime):
        super().__init__(name)
        self.f = f
        self.f_prime = f_prime

    def __call__(self, inputs):
        self.inputs = inputs
        return self.f(inputs)

    def backward(self, grad):
        # (N,1) * (N,1) = (N,1)
        return self.f_prime(self.inputs) * grad

sigmoid = lambda f: 1/(1+np.exp(-f))

def sigmoid_prime(f):
    h = sigmoid(f)
    return h*(1-h)

class Sigmoid(Activation):
    def __init__(self, name):
        super().__init__(name, sigmoid, sigmoid_prime)

def tanh(x):
    return np.tanh(x)

def tanh_prime(x):
    y = tanh(x)
    return 1 - y ** 2

class Tanh(Activation):
    def __init__(self, name):
        super().__init__(name, tanh, tanh_prime)

relu = lambda x: np.maximum(0, x)

def relu_prime(x):
    y = relu(x)
    dreludx = np.zeros(y.shape)
    dreludx[np.where(y > 0)] = 1
    return dreludx

class Relu(Activation):
    def __init__(self, name):
        super().__init__(name, relu, relu_prime)


import numpy as np

def euclidean(x, y):
    """
    Compute the Euclidean (`L2`) distance between two real vectors

    Notes
    -----
    The Euclidean distance between two vectors **x** and **y** is

    .. math::

        d(\mathbf{x}, \mathbf{y}) = \sqrt{ \sum_i (x_i - y_i)^2  }

    Parameters
    ----------
    x,y : :py:class:`ndarray <numpy.ndarray>` s of shape `(N,)`
        The two vectors to compute the distance between

    Returns
    -------
    d : float
        The L2 distance between **x** and **y**.
    """
    return np.sqrt(np.sum((x - y) ** 2))

def manhattan(x, y):
    """
    Compute the Manhattan (`L1`) distance between two real vectors

    Notes
    -----
    The Manhattan distance between two vectors **x** and **y** is

    .. math::

        d(\mathbf{x}, \mathbf{y}) = \sum_i |x_i - y_i|

    Parameters
    ----------
    x,y : :py:class:`ndarray <numpy.ndarray>` s of shape `(N,)`
        The two vectors to compute the distance between

    Returns
    -------
    d : float
        The L1 distance between **x** and **y**.
    """
    return np.sum(np.abs(x - y))

def chebyshev(x, y):
    """
    Compute the Chebyshev (:math:`L_\infty`) distance between two real vectors

    Notes
    -----
    The Chebyshev distance between two vectors **x** and **y** is

    .. math::

        d(\mathbf{x}, \mathbf{y}) = \max_i |x_i - y_i|

    Parameters
    ----------
    x,y : :py:class:`ndarray <numpy.ndarray>` s of shape `(N,)`
        The two vectors to compute the distance between

    Returns
    -------
    d : float
        The Chebyshev distance between **x** and **y**.
    """
    return np.max(np.abs(x - y))

def minkowski(x, y, p):
    """
    Compute the Minkowski-`p` distance between two real vectors.

    Notes
    -----
    The Minkowski-`p` distance between two vectors **x** and **y** is

    .. math::

        d(\mathbf{x}, \mathbf{y}) = \left( \sum_i |x_i - y_i|^p \\right)^{1/p}

    Parameters
    ----------
    x,y : :py:class:`ndarray <numpy.ndarray>` s of shape `(N,)`
        The two vectors to compute the distance between
    p : float > 1
        The parameter of the distance function. When `p = 1`, this is the `L1`
        distance, and when `p=2`, this is the `L2` distance. For `p < 1`,
        Minkowski-`p` does not satisfy the triangle inequality and hence is not
        a valid distance metric.

    Returns
    -------
    d : float
        The Minkowski-`p` distance between **x** and **y**.
    """
    return np.sum(np.abs(x - y) ** p) ** (1 / p)

def hamming(x, y):
    """
    Compute the Hamming distance between two integer-valued vectors.

    Notes
    -----
    The Hamming distance between two vectors **x** and **y** is

    .. math::

        d(\mathbf{x}, \mathbf{y}) = \\frac{1}{N} \sum_i \mathbb{1}_{x_i \\neq y_i}

    Parameters
    ----------
    x,y : :py:class:`ndarray <numpy.ndarray>` s of shape `(N,)`
        The two vectors to compute the distance between. Both vectors should be
        integer-valued.

    Returns
    -------
    d : float
        The Hamming distance between **x** and **y**.
    """
    return np.sum(x != y) / len(x)


'''
    Bisection Method
    Purpose:
        Computes approximate solution of f(x) = 0
    Input:
        f: function handle
        a, b: interval [a, b], such that f(a)*f(b)<0 and f(x) = 0 has a unique
        solution over [a, b]
        tol: tolerance error
    Output:
        Approximate solution

Last edited by Xiaozhou Li, September 9, 2018
'''

import numpy as np
import matplotlib.pyplot as plt

def bisect(f, a, b, tol):
    if (np.sign(f(a)*f(b)) >= 0):      # wrong input
        print ('$f(a)f(b) <0$ not satisfied')
        return
    fa = f(a)
    fb = f(b)
    while ((b - a)/2) > tol:
        c = (a + b)/2;
        print (c)
        fc = f(c);
        if fc == 0:                 # c is a solution, done
            return c
        if (np.sign(fc*fa) < 0):       # new interval [a, c]
            b = c;
            fb = fc
        else:                       # new interval [c, b]
            a = c
            fa = fc
    return (a + b)/2                #new midpoint is best estimate

#########################################################
# Example
def fun(x):
    return np.exp(-x) - np.sin(np.pi/2*x)

print (bisect(fun,0,1,1.e-12))


import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)

def run_ch04():
    X = 2 * np.random.rand(100, 1)
    y = 4 + 3 * X + np.random.rand(100, 1)

    X_b = np.c_[np.ones((100,1)), X]
    theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
    print(theta_best)

    X_new = np.array([[0], [2]])
    X_new_b = np.c_[np.ones((2,1)), X_new]
    y_predict = X_new_b.dot(theta_best)
    print(y_predict)

    plt.plot(X_new, y_predict, 'r-')
    plt.plot(X, y, 'b.')
    plt.axis([0, 2, 0, 15])
    plt.show()

    from sklearn.linear_model import LinearRegression
    lin_reg = LinearRegression()
    lin_reg.fit(X,y)
    print(lin_reg.intercept_, lin_reg.coef_)
    print(lin_reg.predict(X_new))

    theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)
    print(theta_best_svd)

    print(np.linalg.pinv(X_b).dot(y))

    eta = 0.1  # learning rate
    n_iterations = 1000
    m = 100

    theta = np.random.randn(2, 1)  # random initialization

    for iteration in range(n_iterations):
        gradients = 2 / m * X_b.T.dot(X_b.dot(theta) - y)
        theta = theta - eta * gradients

    print(theta)

    m = len(X_b)

    n_epochs = 50
    t0, t1 = 5, 50  # learning schedule hyperparameters

    def learning_schedule(t):
        return t0 / (t + t1)

    theta = np.random.randn(2, 1)  # random initialization

    for epoch in range(n_epochs):
        for i in range(m):
            random_index = np.random.randint(m)
            xi = X_b[random_index:random_index + 1]
            yi = y[random_index:random_index + 1]
            gradients = 2 * xi.T.dot(xi.dot(theta) - yi)
            eta = learning_schedule(epoch * m + i)
            theta = theta - eta * gradients

    from sklearn.linear_model import SGDRegressor

    sgd_reg = SGDRegressor(max_iter=1000, tol=1e-3, penalty=None, eta0=0.1, random_state=42)
    sgd_reg.fit(X, y.ravel())

    print(sgd_reg.intercept_, sgd_reg.coef_)

    import numpy.random as rnd

    m = 100
    X = 6 * np.random.rand(m, 1) - 3
    y = 0.5 * X ** 2 + X + 2 + np.random.randn(m, 1)

    plt.plot(X, y, "b.")
    plt.xlabel("$x_1$", fontsize=18)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.axis([-3, 3, 0, 10])
    # save_fig("quadratic_data_plot")
    plt.show()

    from sklearn.preprocessing import PolynomialFeatures
    poly_features = PolynomialFeatures(degree=2, include_bias=False)
    X_poly = poly_features.fit_transform(X)
    print(X[0])

    print(X_poly[0])

    from sklearn.metrics import mean_squared_error
    from sklearn.model_selection import train_test_split

    def plot_learning_curves(model, X, y):
        X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=10)
        train_errors, val_errors = [], []
        for m in range(1, len(X_train)):
            model.fit(X_train[:m], y_train[:m])
            y_train_predict = model.predict(X_train[:m])
            y_val_predict = model.predict(X_val)
            train_errors.append(mean_squared_error(y_train[:m], y_train_predict))
            val_errors.append(mean_squared_error(y_val, y_val_predict))

        plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")
        plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")
        plt.legend(loc="upper right", fontsize=14)  # not shown in the book
        plt.xlabel("Training set size", fontsize=14)  # not shown
        plt.ylabel("RMSE", fontsize=14)  # not shown

    lin_reg = LinearRegression()
    plot_learning_curves(lin_reg, X, y)
    plt.axis([0, 80, 0, 3])  # not shown in the book
    # save_fig("underfitting_learning_curves_plot")  # not shown
    plt.show()  # not shown

    from sklearn.pipeline import Pipeline

    polynomial_regression = Pipeline([
        ("poly_features", PolynomialFeatures(degree=10, include_bias=False)),
        ("lin_reg", LinearRegression()),
    ])

    plot_learning_curves(polynomial_regression, X, y)
    plt.axis([0, 80, 0, 3])  # not shown
    # save_fig("learning_curves_plot")  # not shown
    plt.show()  # not shown

    m = 20
    X = 3 * np.random.rand(m, 1)
    y = 1 + 0.5 * X + np.random.randn(m, 1) / 1.5
    X_new = np.linspace(0, 3, 100).reshape(100, 1)

    from sklearn.linear_model import Ridge
    ridge_reg = Ridge(alpha=1, solver="cholesky", random_state=42)
    ridge_reg.fit(X, y)
    ridge_reg.predict([[1.5]])

    ridge_reg = Ridge(alpha=1, solver="sag", random_state=42)
    ridge_reg.fit(X, y)
    ridge_reg.predict([[1.5]])

    from sklearn.linear_model import Ridge
    from sklearn.preprocessing import StandardScaler

    def plot_model(model_class, polynomial, alphas, **model_kargs):
        for alpha, style in zip(alphas, ("b-", "g--", "r:")):
            model = model_class(alpha, **model_kargs) if alpha > 0 else LinearRegression()
            if polynomial:
                model = Pipeline([
                    ("poly_features", PolynomialFeatures(degree=10, include_bias=False)),
                    ("std_scaler", StandardScaler()),
                    ("regul_reg", model),
                ])
            model.fit(X, y)
            y_new_regul = model.predict(X_new)
            lw = 2 if alpha > 0 else 1
            plt.plot(X_new, y_new_regul, style, linewidth=lw, label=r"$\alpha = {}$".format(alpha))
        plt.plot(X, y, "b.", linewidth=3)
        plt.legend(loc="upper left", fontsize=15)
        plt.xlabel("$x_1$", fontsize=18)
        plt.axis([0, 3, 0, 4])

    plt.figure(figsize=(8, 4))
    plt.subplot(121)
    plot_model(Ridge, polynomial=False, alphas=(0, 10, 100), random_state=42)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.subplot(122)
    plot_model(Ridge, polynomial=True, alphas=(0, 10 ** -5, 1), random_state=42)

    # save_fig("ridge_regression_plot")
    plt.show()

    sgd_reg = SGDRegressor(penalty="l2", max_iter=1000, tol=1e-3, random_state=42)
    sgd_reg.fit(X, y.ravel())
    sgd_reg.predict([[1.5]])

    from sklearn.linear_model import Lasso
    lasso_reg = Lasso(alpha=0.1)
    lasso_reg.fit(X, y)
    lasso_reg.predict([[1.5]])

    from sklearn.linear_model import ElasticNet
    elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5, random_state=42)
    elastic_net.fit(X, y)
    elastic_net.predict([[1.5]])

    m = 100
    X = 6 * np.random.rand(m, 1) - 3
    y = 2 + X + 0.5 * X ** 2 + np.random.randn(m, 1)

    X_train, X_val, y_train, y_val = train_test_split(X[:50], y[:50].ravel(), test_size=0.5, random_state=10)

    from sklearn.base import clone

    poly_scaler = Pipeline([
        ("poly_features", PolynomialFeatures(degree=90, include_bias=False)),
        ("std_scaler", StandardScaler())
    ])

    X_train_poly_scaled = poly_scaler.fit_transform(X_train)
    X_val_poly_scaled = poly_scaler.transform(X_val)

    sgd_reg = SGDRegressor(max_iter=1, tol=-np.infty, warm_start=True,
                           penalty=None, learning_rate="constant", eta0=0.0005, random_state=42)

    minimum_val_error = float("inf")
    best_epoch = None
    best_model = None
    for epoch in range(1000):
        sgd_reg.fit(X_train_poly_scaled, y_train)  # continues where it left off
        y_val_predict = sgd_reg.predict(X_val_poly_scaled)
        val_error = mean_squared_error(y_val, y_val_predict)
        if val_error < minimum_val_error:
            minimum_val_error = val_error
            best_epoch = epoch
            best_model = clone(sgd_reg)

    from sklearn import datasets
    iris = datasets.load_iris()
    list(iris.keys())

    print(iris.DESCR)

    X = iris["data"][:, 3:]  # petal width
    y = (iris["target"] == 2).astype(np.int)  # 1 if Iris virginica, else 0

    from sklearn.linear_model import LogisticRegression
    log_reg = LogisticRegression(solver="lbfgs", random_state=42)
    log_reg.fit(X, y)

    X_new = np.linspace(0, 3, 1000).reshape(-1, 1)
    y_proba = log_reg.predict_proba(X_new)

    plt.plot(X_new, y_proba[:, 1], "g-", linewidth=2, label="Iris virginica")
    plt.plot(X_new, y_proba[:, 0], "b--", linewidth=2, label="Not Iris virginica")

    X_new = np.linspace(0, 3, 1000).reshape(-1, 1)
    y_proba = log_reg.predict_proba(X_new)
    decision_boundary = X_new[y_proba[:, 1] >= 0.5][0]

    plt.figure(figsize=(8, 3))
    plt.plot(X[y == 0], y[y == 0], "bs")
    plt.plot(X[y == 1], y[y == 1], "g^")
    plt.plot([decision_boundary, decision_boundary], [-1, 2], "k:", linewidth=2)
    plt.plot(X_new, y_proba[:, 1], "g-", linewidth=2, label="Iris virginica")
    plt.plot(X_new, y_proba[:, 0], "b--", linewidth=2, label="Not Iris virginica")
    plt.text(decision_boundary + 0.02, 0.15, "Decision  boundary", fontsize=14, color="k", ha="center")
    plt.arrow(decision_boundary, 0.08, -0.3, 0, head_width=0.05, head_length=0.1, fc='b', ec='b')
    plt.arrow(decision_boundary, 0.92, 0.3, 0, head_width=0.05, head_length=0.1, fc='g', ec='g')
    plt.xlabel("Petal width (cm)", fontsize=14)
    plt.ylabel("Probability", fontsize=14)
    plt.legend(loc="center left", fontsize=14)
    plt.axis([0, 3, -0.02, 1.02])
    # save_fig("logistic_regression_plot")
    plt.show()

    print(decision_boundary)
    print(log_reg.predict([[1.7], [1.5]]))

    X = iris["data"][:, (2, 3)]  # petal length, petal width
    y = iris["target"]

    softmax_reg = LogisticRegression(multi_class="multinomial", solver="lbfgs", C=10, random_state=42)
    softmax_reg.fit(X, y)

    print(softmax_reg.predict([[5, 2]]))

    print(softmax_reg.predict_proba([[5, 2]]))


import numpy  as np
import matplotlib.pyplot as plt

"""
x_list = [1,3,5,7,8]
y_list = [5,2,4,1,2]
"""
x_list = [0.0,0.6,0.9]
y_list = [np.log(1+0.0),np.log(1+0.6),np.log(1+0.9)]

def PolyLagrange(x_list,y_list,x):
    sum = 0

    for i in  range(0, len(x_list)):
        prod = 1
        for j in range(0,len(x_list)):
            if(j != i):
                prod = prod*(x - x_list[j])/(x_list[i] - x_list[j])
        sum += y_list[i]*prod

    return sum

print(PolyLagrange(x_list,y_list,0.45))
print(np.log(1+0.45))

"""
x_array = np.linspace(min(x_list), max(x_list),100)
y_array = []

for i in range(0,len(x_array)):
    y_array.append(PolyLagrange(x_list,y_list,x_array[i]))

fig = plt.figure()
ax = plt.axes()
ax.plot(x_list,y_list,'*')
ax.plot(x_array, y_array)
plt.show()
"""


# 0.5*dy/dt + y(t) = 2*u(t),	y(t=0) = 0
# tau = 0.5 sec & k = 2

import numpy as np
import matplotlib.pyplot as plt

from scipy.integrate import odeint
from scipy.signal import step
from scipy.signal import TransferFunction as tf
from scipy.signal import StateSpace as ss

#########################
# SIMULATION'S SETTINGS #
#########################
k, tau = 2.0, 0.5

############################
# ODE: NUMERIC INTEGRATION #
############################
# 0.5*dy/dt + y(t) = 2*u(t)	<===>	dy/dt = 1/0.5*(-y + 2*u)
def mySys(x, t):
	u = 1
	return 1/0.5*(-x + 2*u)

tspan = np.linspace(0.0, 7.0, 100)
y1 = odeint(mySys, 0, tspan)

######
# TF #
######
hTF = tf([k], [tau, 1])
_, y2 = step(hTF, T=tspan)

######
# SS #
######
A, B, C, D = -1/tau, k/tau, 1, 0
hSS = ss(A, B, C, D)
_, y3 = step(hSS, T=tspan)

plt.plot(tspan, y1, 'r--', linewidth=2, label='ODE')
plt.plot(tspan, y2, 'g--', linewidth=2, label='TF')
plt.plot(tspan, y3, 'b--', linewidth=2, label='SS')

plt.title('Step response of 1st order LTI plant')
plt.xlabel('t (sec)')
plt.ylabel('y(t)')
plt.grid()
plt.legend(loc='best')
plt.show()


"""Utilities and atomic functions
"""
import math
import numpy as np

def spherical2cartesian(a):
    """Transform the spherical coordinate system `a` to cartesian
    """
    ndim = len(a)
    x = np.empty(ndim+1)

    asin = np.sin(a)
    acos = np.cos(a)

    for i in range(ndim):
        x[i] = acos[i]
        for j in range(i):
            x[i] *= asin[j]

    x[-1] = 1.0
    for j in range(ndim):
        x[-1] *= asin[j]

    return x

def cartesian2spherical(v):
    """Transform the cartesian coordinate system `v` to spherical
    """
    ndim = len(v)
    a = np.empty(ndim-1)

    v2 = np.power(v, 2)

    for i in range(ndim-2):
        a[i] = math.acos(v[i]/np.sqrt(np.sum(v2[i:])))

    d = math.acos(v[-2]/np.sqrt(v2[-1] + v2[-2]))
    if v[-1] >= 0.0:
        a[-1] = d
    else:
        a[-1] = 2*np.pi - d

    return a

def orthonormal_basis(a):
    """Determine an orthonormal basis with the first vector `a`
    """
    # a is the first direction
    d = len(a)

    # orthonormal basis of a
    U = np.eye(d)
    U[:, 0] = a  # the first column is the first

    # Q is the orthonormal component having
    # the first column the direction a (normalised)
    Q, R = np.linalg.qr(U)

    return Q


# -*- coding: utf-8 -*-
"""Student Activity: Financial Analysis using NPV.

This script will choose the optimal project scenario to
undertake based on max NPV values.
"""

# Import the NumPy library
import numpy_financial as npf

# Discount Rate
discount_rate = .1

# Initial Investment, Cash Flow 1, Cash Flow 2, Cash Flow 3, Cash Flow 4
cash_flows_conservative = [-1000, 400, 400, 400, 400]
cash_flows_neutral = [-1500, 600, 600, 600, 600]
cash_flows_aggressive = [-2250, 800, 800, 800, 800]

# Initialize dictionary to hold NPV return values
npv_dict = {}

# Calculate the NPV for each scenario
npv_dict['conservative'] = npf.npv(discount_rate, cash_flows_conservative)
npv_dict['neutral'] = npf.npv(discount_rate, cash_flows_neutral)
npv_dict['aggressive'] = npf.npv(discount_rate, cash_flows_aggressive)

# Initialize variables
max_value = 0
max_key = 0

# Iterate over the dictionary and find the max key-value pair
for key, value in npv_dict.items():
    print(f"{key} {value}")

    if max_value == 0:
        max_value = value
        max_key = key
    elif value > max_value:
        max_value = value
        max_key = key

# Print out the optimal project scenario with the highest NPV value
print("---------------------------------------------------------")
print(f"The project scenario with the max NPV value is '{max_key}' with a NPV of {max_value}")