""""
The goal of this module is to implement all algorithms and numerical
methods needed to solve the Task 5 from the coding homeworks in the
Machine Learning course on coursera.com.
"""

from typing import Tuple
import numpy as np
import scipy.optimize

def hypothesis_function(x: np.ndarray, theta: np.ndarray) -> np.ndarray:
    """
    Hypothesis function for linear regression. It is a linear function  of the form:
    h(x) = theta0 + theta1 * x1 + theta2 * x2 + ... + thetaN * xN.
    Theta is a vector containing the parameter values.

    Args:
        x: Matrix of features.
        theta: Vector of parameters.

    Returns:
        Vector of predictions.
    """
    return np.dot(x, theta)

def compute_cost(
    x: np.ndarray, y: np.ndarray, theta: np.ndarray = None, _lambda: int = 0
) -> np.float64:
    """
    Computes the cost of using theta as the parameter for linear regression to fit the data points in x and y.

    Args:
        x: Matrix of features.
        y: Vector of labels.
        theta: Vector of parameters.
        _lambda: Regularization parameter.

    Returns:
        Cost of using theta as the parameter for linear regression to fit the data points in x and y.
    """
    if theta is None:
        theta = np.zeros((x.shape[1], 1))

    m = y.size

    j = (
        1
        / (2 * m)
        * np.dot(
            (hypothesis_function(x, theta).reshape((m, 1)) - y).T,
            (hypothesis_function(x, theta).reshape((m, 1)) - y),
        )
    )
    reg = _lambda / (2 * m) * np.dot(theta[1:].T, theta[1:])

    return (j + reg)[0][0]

def compute_gradient(
    x: np.ndarray, y: np.ndarray, theta: np.ndarray = None, _lambda: int = 0
) -> np.ndarray:
    """
    Computes the gradient of the cost function.

    Args:
        x: Matrix of features.
        y: Vector of labels.
        theta: Vector of parameters.
        _lambda: Regularization parameter.

    Returns:
        Vector of gradient.
    """
    if theta is None:
        theta = np.zeros((x.shape[1], 1))

    m = y.size
    theta = theta.reshape((theta.shape[0], 1))

    gradient = 1 / m * np.dot(x.T, hypothesis_function(x, theta) - y)
    reg = _lambda / m * theta

    # don't regularize the bias term
    reg[0] = 0

    return gradient + reg.reshape((gradient.shape[0], 1))

def optimize_theta(
    x: np.ndarray, y: np.ndarray, theta: np.ndarray, _lambda: int = 0
) -> np.ndarray:
    """
    Optimizes theta using the scipy.optimize.minimize function.

    Args:
        x: Matrix of features.
        y: Vector of labels.
        theta: Vector of parameters.
        _lambda: Regularization parameter.

    Returns:
        Vector of optimized parameters.
    """
    return scipy.optimize.minimize(
        lambda _theta: compute_cost(x, y, _theta, _lambda),
        x0=theta,
        method="BFGS",
        options={
            "disp": False,
            "gtol": 1e-05,
            "eps": 1.4901161193847656e-08,
            "return_all": False,
            "maxiter": None,
        },
    ).x

def construct_polynomial_matrix(x: np.ndarray, p: int) -> np.ndarray:
    """
    Takes an x matrix and returns an x matrix with additional columns.
    First additional column is 2'nd column with all values squared,
    the next additional column is 2'nd column with all values cubed etc.

    Args:
        x: Matrix of features.
        p: Degree of the polynomial.

    Returns:
        Matrix of features with additional columns.
    """

    p_matrix = x.copy()

    for i in range(2, p + 2):
        p_matrix = np.insert(p_matrix, p_matrix.shape[1], np.power(x[:, 1], i), axis=1)

    return p_matrix

def normalize_features(x: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    Normalizes the features in x.

    Args:
        x: Matrix of features.

    Returns:
        The normalized x, the mean of the original features and the standard deviation of the original features
    """
    x_norm = x.copy()

    feature_means = np.mean(x_norm, axis=0)
    x_norm[:, 1:] = x_norm[:, 1:] - feature_means[1:]
    feature_stds = np.std(x_norm, axis=0, ddof=1)
    x_norm[:, 1:] = x_norm[:, 1:] / feature_stds[1:]

    return x_norm, feature_means, feature_stds


import numpy as np

def gaussian_elimination_sequence(a, b):
    """
    用「顺序高斯消去法」解线性方程 `ax = b`。

    Args:
        a : np_array_like 系数矩阵（nxn）
        b : np_array_like 右端常数（n）

    Returns:
        x : np.array `ax=b` 的解（n）

    Raises:
        Exception("求解失败") if a[k][k] == 0
    """
    A = np.c_[a, b]  # 增广矩阵

    n, c = A.shape
    assert c == n + 1, f'bad shape: {A.shape}'

    x = np.zeros(n)

    # 消元
    for k in range(n-1):
        if A[k][k] == 0:
            raise Exception("求解失败")

        for i in range(k+1, n):
            m = A[i][k] / A[k][k]
            A[i][k] = 0
            for j in range(k+1, n+1):
                A[i][j] -= A[k][j] * m

    # 回代
    x[n-1] = A[n-1][n] / A[n-1][n-1]
    for k in range(n-2, -1, -1):  # from n-2 (included) to 0 (included)
        for j in range(k+1, n):
            A[k][n] -= A[k][j] * x[j]
        x[k] = A[k][n] / A[k][k]

    return x

def gaussian_elimination(a, b, sequence=False):
    """
    用「列主元高斯消去法」解线性方程 `ax = b`。

    本函数可以通过「列主元高斯消元法」或「顺序高斯消元法」计算，
    通过参数 sequence 控制，默认 sequence=False 使用「列主元高斯消元法」。

    Args:
        a : np_array_like 系数矩阵（nxn）
        b : np_array_like 右端常数（n）

    Returns:
        x : np.array `ax=b` 的解（n）

    Raises:
        Exception("求解失败") if a[k][k] == 0
    """
    A = np.c_[a, b]  # 增广矩阵

    n, c = A.shape
    assert c == n + 1, f'bad shape: {A.shape}'

    x = np.zeros(n)

    # 消元
    for k in range(0, n-1):
        if not sequence:  # 列主元
            i_max = k + np.argmax(np.abs(A[k:n, k]))

            if A[i_max][k] == 0:
                raise Exception("A 奇异，求解失败")

            A[[i_max, k]] = A[[k, i_max]]  # swap rows

        for i in range(k+1, n):
            m = A[i][k] / A[k][k]
            for j in range(k+1, n+1):
                A[i][j] -= A[k][j] * m

    # 回代
    if a[n-1][n-1] == 0:
        raise Exception("矩阵奇异，求解失败")

    x[n-1] = A[n-1][n] / A[n-1][n-1]
    for i in range(n-2, -1, -1):  # from n-2 (included) to 0 (included)
        for j in range(i+1, n):
            A[i][n] -= A[i][j] * x[j]
        x[i] = A[i][n] / A[i][i]

    return x


from pyquaternion import Quaternion
import numpy as np
import math

def angle2radians(angle):
    return angle / 180.0 * math.pi

def radians2angle(radians):
    return radians / math.pi * 180.0

def rpy_to_rotation_matrix(rpy):
    roll = rpy[0]
    pitch = rpy[1]
    yaw = rpy[2]

    R_roll = np.array([[1, 0, 0], [0, math.cos(roll), -math.sin(roll)], [0, math.sin(roll), math.cos(roll)]])

    R_pitch = np.array([[math.cos(pitch), 0, math.sin(pitch)], [0, 1, 0], [-math.sin(pitch), 0, math.cos(pitch)]])

    R_yaw = np.array([[math.cos(yaw), -math.sin(yaw), 0], [math.sin(yaw), math.cos(yaw), 0], [0, 0, 1]])

    return R_yaw.dot(R_pitch).dot(R_roll)

def rpy_to_angle_axis(rpy):
    quat = rpy_to_quaternion(rpy)
    len = quat[0] ** 2 + quat[1] ** 2 + quat[2] ** 2 + quat[3] ** 2
    quat /= len
    print("quat = ", quat)

    angle = 2.0 * math.acos(quat[0])
    while (angle < -math.pi):
        angle += math.pi * 2.0
    while (angle > math.pi):
        angle -= math.pi * 2.0

    a = math.sqrt(1.0 - quat[0] * quat[0])
    axis = np.array([quat[1] / a, quat[2] / a, quat[3] / a])
    print("angle = ", angle)
    print("axis = ", axis)

    return axis * angle

def quaternion_to_rpy(attitude_quaternion):
    # based on wikipedia: https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
    attitude_quaternion._normalise()

    w = attitude_quaternion[0]
    x = attitude_quaternion[1]
    y = attitude_quaternion[2]
    z = attitude_quaternion[3]

    t0 = 2.0 * (w * x + y * z)
    t1 = 1.0 - 2.0 * (x * x + y * y)
    X = math.atan2(t0, t1)

    t2 = 2.0 * (w * y - z * x)
    t2 = 1.0 if t2 > 1.0 else t2
    t2 = -1.0 if t2 < -1.0 else t2
    Y = math.asin(t2)

    t3 = 2.0 * (w * z + x * y)
    t4 = 1.0 - 2.0 * (y * y + z * z)
    Z = math.atan2(t3, t4)

    return np.array([X, Y, Z])

def rpy_to_quaternion(rpy):
    # based on wikipedia: https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
    roll = rpy[0]
    pitch = rpy[1]
    yaw = rpy[2]

    cy = math.cos(yaw * 0.5)
    sy = math.sin(yaw * 0.5)
    cr = math.cos(roll * 0.5)
    sr = math.sin(roll * 0.5)
    cp = math.cos(pitch * 0.5)
    sp = math.sin(pitch * 0.5)

    w = cy * cr * cp + sy * sr * sp
    x = cy * sr * cp - sy * cr * sp
    y = cy * cr * sp + sy * sr * cp
    z = sy * cr * cp - cy * sr * sp

    quat = Quaternion(w, x, y, z)
    quat._normalise()

    return quat

def angular_rate_to_euler_rate_matrix(rpy):
    roll = rpy[0]
    pitch = rpy[1]
    s_roll = math.sin(roll)
    c_roll = math.cos(roll)
    c_pitch = math.cos(pitch)
    t_pitch = math.tan(pitch)
    return np.array([[1, s_roll * t_pitch, c_roll * t_pitch], [0, c_roll, -s_roll], [0, s_roll / c_pitch, c_roll / c_pitch]])

def compute_rpy_rate(rpy, angular_rate):
    # problematic, the incoming angular rate is not in body frame, but it will not affect the simulation
    return angular_rate_to_euler_rate_matrix(rpy).dot(angular_rate)

# testing
import random

def test_rpy_quaternion():
    # testing rpy-quaternion conversion
    rpy = np.zeros(3)
    rpy[0] = random.uniform(-math.pi, math.pi )
    rpy[1] = random.uniform(-math.pi / 2.0, math.pi / 2.0)
    rpy[2] = random.uniform(-math.pi, math.pi)

    orientation = rpy_to_quaternion(rpy)
    inverse_rpy = quaternion_to_rpy(orientation)

    roll_quat = Quaternion(axis = [1, 0, 0], angle = rpy[0])
    pitch_quat = Quaternion(axis = [0, 1, 0], angle = rpy[1])
    yaw_quat = Quaternion(axis = [0, 0, 1], angle = rpy[2])

    orientation_from_rpy = yaw_quat * pitch_quat * roll_quat

    print("-------------test rpy to orientation-----------------")
    print("rpy = ", rpy, ", inverse rpy = ", inverse_rpy, ", norm(diff) = ", np.linalg.norm(rpy - inverse_rpy))
    print("orientation = ", orientation, ", another orientation = ", orientation_from_rpy, ", diff = ", orientation - orientation_from_rpy)

def test_rpy_rotation_matrix():
    # testing rpy-rotation conversion
    rpy = np.zeros(3)
    rpy[0] = random.uniform(-math.pi, math.pi )
    rpy[1] = random.uniform(-math.pi / 2.0, math.pi / 2.0)
    rpy[2] = random.uniform(-math.pi, math.pi)

    orientation = rpy_to_quaternion(rpy)

    rotation_from_quat = orientation.rotation_matrix

    rotation = rpy_to_rotation_matrix(rpy)

    print("-------------test rpy to rotation-----------------")
    print("rpy = ", rpy)
    print("rotation = \n", rotation)
    print("rotation from quat = \n", rotation_from_quat)
    print("diff = \n", rotation - rotation_from_quat)
    if (np.linalg.norm(rotation - rotation_from_quat) > 1e-7):
        input()

def test_rpy_angle_axis():
    # testing rpy-angle axis conversion
    rpy = np.zeros(3)
    rpy[0] = random.uniform(-math.pi / 2.0, math.pi / 2.0)
    rpy[1] = random.uniform(-math.pi / 2.0, math.pi / 2.0)
    rpy[2] = random.uniform(0, math.pi * 2.0)
    # rpy[0] = 3.0664
    # rpy[1] = 0.17985
    # rpy[2] = -2.4785

    orientation = rpy_to_quaternion(rpy)
    print("quat = ", orientation)
    print("angle = ", orientation.angle)
    print("axis = ", orientation.axis)

    angleaxis_from_quat = orientation.angle * orientation.axis

    angleaxis = rpy_to_angle_axis(rpy)

    print("-------------test rpy to angle axis-----------------")
    print("rpy = ", rpy)
    print("angle axis = \n", angleaxis)
    print("angle axis from quat = \n", angleaxis_from_quat)
    print("diff = \n", angleaxis - angleaxis_from_quat)
    if (np.linalg.norm(angleaxis - angleaxis_from_quat) > 1e-7):
        input()

def main():
    for i in range(1000):
        # test_rpy_quaternion()

        # test_rpy_rotation_matrix()

        test_rpy_angle_axis()

if __name__=='__main__':
    main()


'''
Created on Apr 29, 2018

@author: Brandon Bevan
'''

import numpy
import matplotlib.pyplot as plt

import imageio

class Fourier2:

    def __init__(self, image):
        img = imageio.imread(image)
        self.trans = numpy.fft.fft2(img)
        phase = self.trans.imag
        freq = self.trans.real
        self.inverse = numpy.fft.ifft2(self.trans)

    def viewfft2(self,f):
        fp = abs(f)

        # The following two lines are for the sake of Histogram equalization. This is the dirtiest implementation imaginable.
        # We use the mean as the maximum value since the max of an fft2 is usually an outlier.
        mx = fp.mean()
        mn = fp.min()

        #fp = numpy.divide(fp, mx)

        # Shift for viewing
        fps = numpy.fft.fftshift(fp)

        plt.imshow(fps, vmin=mn, vmax=mx)

        plt.show()

    def viewifft2(self):
        fp = abs(self.inverse)

        # The following two lines are for the sake of Histogram equalization. This is the dirtiest implementation imaginable.
        # We use the mean as the maximum value since the max of an fft2 is usually an outlier.
        mx = fp.mean()
        mn = fp.min()

        #fp = numpy.divide(fp, mx)

        fps = fp

        plt.imshow(fps, vmin=mn, vmax=mx)

        plt.show()

    def view(self):
        self.viewfft2(self.trans)

    def viewinverse(self):
        self.viewifft2()

    def viewphase(self):
        self.viewfft2(phase)

    def viewfrequency(self):
        self.viewfft2(freq)


import numpy as np
from scipy.special import expit
import matplotlib.pyplot as plt
from scipy.stats import norm
from scipy.stats import multivariate_normal

##################################################################################################################################
############################################################# PART A #############################################################
##################################################################################################################################
mu = np.zeros(2)
covariance = np.array([[3, 2.9], [2.9, 3]])
true_dist = multivariate_normal(mean=mu, cov=covariance).rvs(500)
plt.scatter(true_dist[:, 0], true_dist[:, 1])
plt.xlabel("z0")
plt.ylabel("z1")
plt.title("Drawing samples")
plt.show()

##################################################################################################################################
############################################################# PART B #############################################################
##################################################################################################################################
n = 2
n_iter = 100
# Initialise x
z = np.array([-4, -4], dtype=np.float64)
accepted = np.array(z)[np.newaxis,:]
for i in range(n_iter):
    for j in range(n):
        k = (j+1)%2
        mu_j = mu[j] + covariance[j][k]*(z[k] - mu[k])/covariance[k][k]
        var_j = covariance[j][j] - covariance[j][k]*covariance[k][j]/covariance[k][k]
        z[j] = norm(loc = mu_j, scale=np.sqrt(var_j)).rvs(1)
        accepted = np.vstack((accepted, z))

plt.plot(accepted[:, 0], accepted[:, 1], 'ro')
plt.xlabel("z0")
plt.ylabel("z1")
plt.title("Gibbs Sampling")
plt.show()

true_dist = multivariate_normal(mean=mu, cov=covariance).rvs(500)
plt.scatter(true_dist[:, 0], true_dist[:, 1], label="Normal Sampling")
plt.plot(accepted[:, 0], accepted[:, 1], 'ro', label="Gibbs Sampling")
plt.xlabel("z0")
plt.ylabel("z1")
plt.title("Gibbs Sampling vs Normal Sampling")
plt.legend()
plt.show()

##################################################################################################################################
############################################################# PART C #############################################################
##################################################################################################################################
def dU_dz(mu, cov, z):
    z = np.array(z-mu)
    grad = np.dot(np.linalg.inv(cov),z)
    return grad

def leapfrog(z, r, s, mu, cov, eps, L):

    for i in range(L):
        r -= (eps/2)*dU_dz(mu, cov, np.copy(z))
        z += eps*np.dot(np.linalg.inv(s), r)
        r -= (eps/2)*dU_dz(mu, cov, np.copy(z))
    return (z, r)

def accept_prob(pos_dist, current_state, next_state, mu, cov, s):
    current_state_p = pos_dist(current_state, mu, cov, s)
    next_state_p = pos_dist(next_state, mu, cov, s)
    return(np.min([1, next_state_p/current_state_p]))

def total_energy(state, mu, cov, s):
    z = state[0]
    r = np.array(state[1])
    z = np.array(z-mu)
    u = 0.5*(np.dot(np.dot(z.transpose(),np.linalg.inv(cov)), z))
    k = 0.5*(np.dot(np.dot(r.transpose(),np.linalg.inv(s)), r))
    return(np.exp(-u-k))

def hybrid_monte_carlo(mu, cov, burn_in, n_iter, eps, L, z):
    s = np.eye(2)
    r = multivariate_normal(mean=np.zeros(2), cov=s)
    mu = mu[:,np.newaxis]
    z_p = z[:,np.newaxis]
    rejected = np.array(z_p)
    accepted = np.array(z_p)
    for i in range(1, burn_in + 1):
        r_p = r.rvs(1)[:, np.newaxis]
        z_n, r_n = leapfrog(np.copy(z_p), np.copy(r_p), s, mu, cov, eps, L)
        r_n *= (-1)
        prob = accept_prob(total_energy, [z_p, r_p], [z_n, r_n], mu, cov, s)
        u = np.random.uniform(0, 1, 1)
        if (u <= prob):
            z_p = z_n

    for i in range(1, n_iter + 1):
        accept = False
        r_p = r.rvs(1)[:, np.newaxis]
        z_n, r_n = leapfrog(np.copy(z_p), np.copy(r_p), s, mu, cov, eps, L)
        r_n *= (-1)
        prob = accept_prob(total_energy, [z_p, r_p, s], [z_n, r_n, s], mu, cov, s)
        u = np.random.uniform(0, 1, 1)
        if (u <= prob):
            accept = True
        if (i % m == 0):
            if (accept):
                accepted = np.hstack((accepted, z_n))
            else:
                accepted = np.hstack((accepted, z_p))
                rejected = np.hstack((rejected, z_n))
        if (accept):
            z_p = z_n
    return accepted.transpose() #, rejected.transpose()

eps = 0.1; L = 20; m = 1
burn_in = 100000
accepted_monte = hybrid_monte_carlo(mu, covariance, burn_in, 100, eps, L, z)

plt.plot(accepted_monte[:, 0], accepted_monte[:, 1], 'ro')
plt.xlabel("z0")
plt.ylabel("z1")
plt.title("Monte Carlo Sampling")
plt.show()

true_dist = multivariate_normal(mean=mu, cov=covariance).rvs(500)
plt.scatter(true_dist[:, 0], true_dist[:, 1], label="Normal Sampling")
plt.plot(accepted_monte[:, 0], accepted_monte[:, 1], 'ro', label="Monte Carlo Sampling")
plt.plot(accepted[:, 0], accepted[:, 1], 'go', label="Gibbs Sampling")
plt.xlabel("z0")
plt.ylabel("z1")
plt.title("Monte Carlo Sampling vs Normal Sampling")
plt.legend()
plt.show()


import numpy as np
import matplotlib.pyplot as plt

class MohrsCircle():
    def __init__(self,sigmax,sigmay,tauxy):
        ''' INPUT INITIAL INFORMATION
                σ_x : sigmax
                σ_y : sigmay
                τ_xy : tauxy  (shear stress on x-face)
        '''
        self.sigmax = sigmax
        self.sigmay = sigmay
        self.tauxy = tauxy

    def circleCenter(self):
        '''
        Calculate the value of center of the Mohrs circle which is the average of sigma x and sigma y
        '''
        return((self.sigmax + self.sigmay)/2)

    def radius(self):
        '''
        Calculate the radius of Mohrs circle which is the distance between center point and the point of tau_xy
        '''
        a = (self.sigmax-self.sigmay)/2
        b = self.tauxy
        return np.sqrt(a**2 + b**2)

    def maximumPrincipalStress(self):
        '''
        Return the value of center point of the circle added with the radius
        '''
        return self.circleCenter() + self.radius()

    def minimumPrincipalStress(self):
        '''
        Return the value of center point of the circle subtracted with the radius
        '''
        return self.circleCenter() - self.radius()

    def maximumShearStress(self):
        '''
        Return the value of radius of Mohrs circle
        '''
        return self.radius()

    def mohrsCirclePlot(self):
        radians = np.linspace(0,360,361)*(2*np.pi/360)
        sigmapts = self.circleCenter() + self.radius()*np.cos(radians)
        taupts = self.radius()*np.sin(radians)

        # Figure size and lines
        plt.figure(figsize = [5,5])
        plt.fill_between(sigmapts,taupts,color='blue',alpha=0.1)
        plt.plot(sigmapts, taupts, label = "Mohrs' Circle", color = 'k')
        plt.plot([self.sigmax, self.sigmay],[self.tauxy, -self.tauxy], color = 'r')
        plt.plot([self.circleCenter()],[0], marker = 'o', color = 'k')

        # Stress labels on the graph
        plt.text(self.maximumPrincipalStress(), 0, r'$\sigma_{max}$', va = 'bottom', ha = 'right', fontsize = 14)
        plt.text(self.minimumPrincipalStress(), 0, r'$\sigma_{min}$', ha = 'left', va = 'bottom', fontsize = 14)
        plt.text(self.circleCenter(), self.maximumShearStress(), r'$\tau_{max}$', va='top', ha = 'center', fontsize = 14)

        # Labels
        plt.title('Mohrs\' Circle',fontsize=18)
        plt.xlabel(r'$\sigma$',fontsize=14)
        plt.ylabel(r'$\tau$',fontsize=14)

        # Create horizontal and vertical lines at center
        plt.axhline(color = 'k')
        plt.axvline(color = 'k')

        #'Fits everything in one window'
        plt.tight_layout()
        plt.grid()
        plt.show()

if __name__ == '__main__':
    x1 = float(input('What is the value of sigmax ? '))
    x2 = float(input('What is the value of sigmay ? '))
    x3 = float(input('What is the value of tauxy ? '))

    y =MohrsCircle(x1,x2,x3)
    print('The maximum principal stress is ',round(y.maximumPrincipalStress(),2))
    print('The minimum principal stress is ',round(y.minimumPrincipalStress(),2))
    print('The maximum shear stress is ',round(y.maximumShearStress(),2))
    print('The center of the circle is ',round(y.circleCenter(),2))
    y.mohrsCirclePlot()

# Source : https://topdogengineer.com/object-oriented-programming-mohrs-circle/
#        : https://www.youtube.com/watch?v=lRUkVG25bUU


# transfer functions

import numpy as np

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

# derivative of sigmoid

def dsigmoid(y):
    return y * (1.0 - y)

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

# derivative for tanh sigmoid

def dtanh(y):
    return 1 - y*y


# At the core, numpy provides the excellent ndarray objects, short for n-dimensional arrays.

# In a ‘ndarray’ object, aka ‘array’, you can store multiple items of the same data type.
# It is the facilities around the array object that makes numpy so convenient for performing math
# and data manipulations.

import numpy as np

# Create an 1d array from a list
list = [0, 1, 2, 3, 4]
array_1d = np.array(list)

# The key difference between an array and a list is,
# arrays are designed to handle vectorized operations while a python list is not.
# That means, if you apply a function it is performed on every item in the array, rather than on the whole array object.
new_array_1d = array_1d + 2
print(new_array_1d)

# Create a 2d array from a list of lists
list2 = [[0,1,2], [3,4,5], [6,7,8], [9,10,11]]
array_2d = np.array(list2)

# Convert an array back to a list
array_2d.tolist()

# Inspect the size and shape of a numpy array
print(array_2d.shape, array_2d.dtype, array_2d.size, array_2d.ndim)

# Extract the first 2 rows and columns
# create a new array from an existing array
print(array_2d[:2, :2])

# Reverse the row and column positions
print(array_2d[::-1, ::-1])

# mean, max and min
print("Mean value is: ", array_2d.mean())
print("Max value is: ", array_2d.max())
print("Min value is: ", array_2d.min())

# By row and by column min
print("Column wise minimum: ", np.amin(array_2d, axis=0))
print("Row wise minimum: ", np.amin(array_2d, axis=1))

# Reshaping
# Reshape a 3x4 array to 4x3 array
array_2d.reshape(4, 3)

# Flatten it to a 1d array
array_2d.flatten()

# Create sequences, repetitions and random numbers
# Lower limit is 0 be default
print(np.arange(5))

# 0 to 9
print(np.arange(0, 10))

# 0 to 9 with step of 2
print(np.arange(0, 10, 2))

# 10 to 1, decreasing order
print(np.arange(10, 0, -1))

# The np.zeros and np.ones functions lets you create arrays of desired shape where all the items are either 0’s or 1’s.
np.zeros([2,2])
np.ones([2,2])

#  create repeating sequences
a = [1,2,3]

# Repeat whole of 'a' two times
print('Tile:   ', np.tile(a, 2))

# Repeat each element of 'a' two times
print('Repeat: ', np.repeat(a, 2))

# generate random numbers
# Random numbers between [0,1) of shape 2,2
print(np.random.rand(2,2))

# Normal distribution with mean=0 and variance=1 of shape 2,2
print(np.random.randn(2,2))

# Random integers between [0, 10) of shape 2,2
print(np.random.randint(0, 10, size=[2,2]))

# One random number between [0,1)
print(np.random.random())


import numpy as np

# roll (x), pitch (y), yaw (z)
def RotX(theta):
    cost, sint = np.cos(theta), np.sin(theta)
    return np.array([
        [1, 0, 0],
        [0, cost, -sint],
        [0, sint, cost]
    ])

def RotY(theta):
    cost, sint = np.cos(theta), np.sin(theta)
    return np.array([
        [cost, 0, sint],
        [0, 1, 0],
        [-sint, 0, cost]
    ])

def RotZ(theta):
    cost, sint = np.cos(theta), np.sin(theta)
    return np.array([
        [cost, -sint, 0],
        [sint, cost, 0],
        [0, 0, 1]
    ])

def GetRollPitchYawFromRot(R):
    pitch = np.arcsin(-R[2,0])
    yaw = np.arctan2(R[1,0], R[0,0])
    roll = np.arctan2(R[2,1],R[2,2])

    return np.array([roll, pitch, yaw])

def GetRotFromAngles(rpy):
    return RotZ(rpy[2]) @ RotY(rpy[1]) @ RotX(rpy[0])

def RotToTrans(R, p):
    return np.concatenate((np.concatenate((R, np.expand_dims(p, 1)), axis = 1), np.array([[0, 0, 0, 1]])))

def GetRotAndPosFromTrans(T):
    return T[:3, :3], T[:3, -1]

def SkewMatrix(k):
    return np.array([
        [0, -k[2], k[1]],
        [k[2], 0, -k[0]],
        [-k[1], k[0], 0]
    ])

def RodriguesFormula(k, theta):
    K = SkewMatrix(k)
    return np.eye(K.shape[0]) + np.sin(theta) * K + (1 - np.cos(theta)) * K.dot(K)

def degree2radian(d):
    return np.pi * d / 180

def radian2degree(r):
    return r * 180 / np.pi

def euler2quaternion(z, y, x, is_degree = True):
    if is_degree:
        z = degree2radian(z)
        y = degree2radian(y)
        x = degree2radian(x)
    else:
        assert z >= -1 and y >= -1 and x >= -1 and z <= 1 and y <= 1 and x <= 1
    cosz2, sinz2 = np.cos(z/2), np.sin(z/2)
    cosy2, siny2 = np.cos(y/2), np.sin(y/2)
    cosx2, sinx2 = np.cos(x/2), np.sin(x/2)
    return (
        cosx2 * cosy2 * cosz2 + sinx2 * siny2 * sinz2,
        sinx2 * cosy2 * cosz2 - cosx2 * siny2 * sinz2,
        cosx2 * siny2 * cosz2 + sinx2 * cosy2 * sinz2,
        cosx2 * cosy2 * sinz2 - sinx2 * siny2 * cosz2
    )

def quaternion2euler(quaternion):
    # length(quaternion) = 1
    qr, qi, qj, qk = quaternion
    return np.array([
        [1 - 2*(qj**2 + qk**2), 2*(qi*qj - qk*qr), 2*(qi*qk + qj*qr)],
        [2*(qi*qj + qk*qr), 1 - 2*(qi**2 + qk**2), 2*(qj*qk - qi*qr)],
        [2*(qi*qk - qj*qr), 2*(qj*qk + qi*qr), 1 - 2*(qi**2 + qj**2)]
    ])


import codecademylib3_seaborn
import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets
from copy import deepcopy

iris = datasets.load_iris()

samples = iris.data

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

sepal_length_width = np.array(list(zip(x, y)))

# Step 1: Place K random centroids

k = 3

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

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

def distance(a, b):
  one = (a[0] - b[0]) ** 2
  two = (a[1] - b[1]) ** 2
  distance = (one + two) ** 0.5
  return distance

# To store the value of centroids when it updates
centroids_old = np.zeros(centroids.shape)

# Cluster labeles (either 0, 1, or 2)
labels = np.zeros(len(samples))

distances = np.zeros(3)

# Initialize error:
error = np.zeros(3)

error[0] = distance(centroids[0], centroids_old[0])
error[1] = distance(centroids[1], centroids_old[1])
error[2] = distance(centroids[2], centroids_old[2])

# Repeat Steps 2 and 3 until convergence:

while error.all() != 0:

  # Step 2: Assign samples to nearest centroid

  for i in range(len(samples)):
    distances[0] = distance(sepal_length_width[i], centroids[0])
    distances[1] = distance(sepal_length_width[i], centroids[1])
    distances[2] = distance(sepal_length_width[i], centroids[2])
    cluster = np.argmin(distances)
    labels[i] = cluster

  # Step 3: Update centroids

  centroids_old = deepcopy(centroids)

  for i in range(3):
    points = [sepal_length_width[j] for j in range(len(sepal_length_width)) if labels[j] == i]
    centroids[i] = np.mean(points, axis=0)

  error[0] = distance(centroids[0], centroids_old[0])
  error[1] = distance(centroids[1],   centroids_old[1])
  error[2] = distance(centroids[2], centroids_old[2])

colors = ['r', 'g', 'b']

for i in range(k):
  points = np.array([sepal_length_width[j] for j in range(len(samples)) if labels[j] == i])
  plt.scatter(points[:, 0], points[:, 1], c=colors[i], alpha=0.5)

plt.scatter(centroids[:, 0], centroids[:, 1], marker='D', s=150)

plt.xlabel('sepal length (cm)')
plt.ylabel('sepal width (cm)')

plt.show()

Data Scientist Career Path/12. Foundations of Machine Learning Unsupervised Learning/1. KMeans/6. step4.py


import sys
sys.path.append('..')
import numpy as np
import matplotlib.pyplot as plt
from chebyspectral import *

deg = 16  # degree of Chebyshev polynomial

# Source term
C = -4*np.exp(1)/(1+np.exp(2))
f = lambda xq: np.exp(xq) + C
fHat = chebfit(f, deg)

# Boundary conditions
bc_value_1 = 0
bc_derivative_order_1 = 0  # Dirichlet (0th order derivative)
bc_position_1 = -1
bc_axis_1 = 0
bc_1 = [bc_value_1, bc_derivative_order_1, bc_position_1, bc_axis_1]

bc_value_2 = 0
bc_derivative_order_2 = 0 # Dirichlet
bc_position_2 = 1
bc_axis_2 = 0
bc_2 = [bc_value_2, bc_derivative_order_2, bc_position_2, bc_axis_2]

# Differentiation matrix
l_operator = [4, -4, 1]
L = chebdiff(l_operator, deg)
L, fHat = chebbc(L, fHat, [bc_1, bc_2])

# Compute solution
u = np.dot(np.linalg.pinv(L), fHat)

# Plot solution
N = 100
x = np.linspace(-1, 1, N)
fig, ax = plt.subplots()
ax.plot(x, chebeval(u, x), 'b', label='Approximation')
ax.plot(x, np.exp(x)-np.sinh(1)/np.sinh(2)*np.exp(2*x) + C/4, 'r--', label='Exact')
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.show()


import numpy as np

from mathpy.combinatorics.binomial import binom_coeff

def ramanujan_approx(verbose=False):
    s = 0.0
    n = 1

    while s != (1 / np.pi):
        s += float(binom_coeff(2.0 * n, n) ** 3.0 * ((42.0 * n + 5.0) / (2.0 ** (12.0 * n + 4.0))))
        n += 1.0

        if verbose is True:
            print(s, n)

    return s


from pyomo.environ import *

model = ConcreteModel()

weights = [20,16,24,27,28,13,13,30,22,24,37,41,16,21,18]

model.ykl = Var([i for i in range(6)], [j for j in range(15)], within=Binary)
model.zk = Var([i for i in range(6)], within=Binary)

model.maximumBins = Constraint(expr = sum(model.zk[i] for i in range(6)) <= 6)

def bins(model, i):
    return sum(model.ykl[i, j] * weights[j] for j in range(15)) - 70*model.zk[i] <= 0
model.bins = Constraint([i for i in range(6)], rule=bins)

def maximumItems(model, i):
    return sum(model.ykl[i, j] for j in range(15)) <= 3
model.maximumItems = Constraint([i for i in range(6)], rule=maximumItems)

def eachIteminOneBin(model, item):
    return sum(model.ykl[i, item] for i in range(6)) == 1
model.eachItem = Constraint([i for i in range(15)], rule=eachIteminOneBin)

model.Objective = Objective(expr = sum(model.zk[i] for i in range(6)), sense=minimize)
#model.Objective = Objective(expr= sum(model.ykl[i, j] * weights[j] for j in range(15) for i in range(6)), sense=maximize)
### SOLVE
solver_manager = SolverFactory('glpk')

results = solver_manager.solve(model)
print(results)
print(model.display(filename="result.txt"))


import numpy as np
import math

# Write a function that takes as input a list of numbers, and returns
# the list of values given by the softmax function.
def softmax(L):
    exp_list = [math.exp(i) for i in L]
    sum_of_exp = sum(exp_list)

    return ([i/sum_of_exp for i in exp_list])


import math

import numpy as np
from scipy.fftpack import fft, ifft

# 离散傅里叶变换，采样数 N，周期2pi 采样频率2pi/M
# 时域信号认为是以N为周期的,因为逆变换恢复的时候延展后是以N为周期的
def my_dct1(x):
    assert x.ndim == 1
    size = x.shape[0]
    res = np.zeros(size, dtype=complex)
    for i in range(size):
        for j in range(size):
            rel = x[j] * math.cos(2 * math.pi * i * j / size)
            ima = -x[j] * math.sin(2 * math.pi * i * j / size)
            res[i] += complex(rel, ima)
    return res

def my_dct2(x):
    assert x.ndim == 1
    size = x.shape[0]
    n = np.arange(size)
    k = n.reshape((size, 1))
    m = np.exp(-2j * math.pi * k * n/size)
    return np.dot(m, x)

def my_idct1(x):
    assert x.ndim == 1
    size = x.shape[0]
    res = np.zeros(size, dtype=complex)
    for i in range(size):
        for j in range(size):
            rel = x[j] * math.cos(2 * math.pi * i * j / size)
            ima = -x[j] * math.sin(2 * math.pi * i * j / size)
            res[i] += complex(rel, ima)
    return res

def my_idct2(x):
    assert x.ndim == 1
    size = x.shape[0]
    n = np.arange(size)
    k = n.reshape((size, 1))
    m = np.exp(2j * math.pi * k * n/size)
    return np.real(np.dot(m, x))/size

if __name__ == '__main__':
    x = np.array([1, 2, 3, 5, 7])
    x = np.random.normal(loc=0, scale=1, size=64)
    fft1 = fft(x)
    ifft1 = ifft(fft1)
    print("fft1", fft1)
    print("ifft1", ifft1)

    my_dct1 = my_dct1(x)
    my_dct2 = my_dct2(x)
    my_ifft2 = my_idct2(my_dct1)
    print("my_dct1", my_dct1)
    print("my_dct2", my_dct2)
    print("my_ifft2", my_ifft2)


import numpy

def madelung_rocksalt(threshold_x=10,threshold_y=10,threshold_z=10) :
    """ Calculates the madelung constant for a cubic cell """
    sum=0
    for i in range(-threshold_x,threshold_x+1) :
        for j in range(-threshold_y,threshold_y+1) :
            for k in range(-threshold_z,threshold_z+1) :
                if (i,j,k)!=(0,0,0) :
                    sum+=((-1)**(i+j+k))/numpy.sqrt(i**2+j**2+k**2)
    return sum

for threshold in range(0,50) :
    print(threshold , madelung_rocksalt(threshold, threshold, threshold))

threshold=10
threshold_x=threshold
threshold_y=threshold
threshold_z=threshold

A=numpy.array([((-1)**(i+j+k))/numpy.sqrt(i**2+j**2+k**2)  for i in range(-threshold_x,threshold_x+1) for j in range(-threshold_y,threshold_y+1)
for k in range(-threshold_z,threshold_z+1)  if (i,j,k)!=(0,0,0)  ])

print(sum(A))


import numpy as np
import matplotlib.pyplot as plt

def f(t,x,y):
  return 3*x - x*y

def g(t,x,y):
  return x*y - 2*y

N = 10000
h = 20/(N-1)

x = [1]
y = [2]
t = [0]

for n in range(N):
  t.append(t[n]+h)
  kx1 = f(t[n],x[n],y[n])
  ky1 = g(t[n],x[n],y[n])
  kx2 = f(t[n]+(h/2),x[n]+(h*kx1)/2,y[n]+(h*ky1)/2)
  ky2 = g(t[n]+(h/2),x[n]+(h*kx1)/2,y[n]+(h*ky1)/2)
  kx3 = f(x[n]+(h/2),x[n]+(h*kx2)/2,y[n]+(h*ky2)/2)
  ky3 = g(x[n]+(h/2),x[n]+(h*kx2)/2,y[n]+(h*ky2)/2)
  kx4 = f(x[n]+(h/2),x[n]+(h*kx3)/2,y[n]+(h*kx3)/2)
  ky4 = g(x[n]+(h/2),x[n]+(h*kx3)/2,y[n]+(h*kx3)/2)
  x.append(x[n]+(h/6)*(kx1+2*kx2+2*kx3+kx4))
  y.append(y[n]+(h/6)*(ky1+2*ky2+2*ky3+ky4))

plt.plot(t,x,'b', label='x')
plt.plot(t,y,'r', label="y")
plt.grid(True)
plt.show()
plt.plot(x,y,'g', label = 'x(t),y(t)')
plt.grid(True)


import numpy as np

# Different electric field shapes that all approach a delta function in time

def gaussian(t,sigma):
    """t is time.  Gaussian pulse, with time-domain standard deviation sigma,
    normalized to behave like a delta function as sigma -> 0"""
    pre = 1/(np.sqrt(2*np.pi)*sigma)
    return pre * np.exp(-t**2/(2*sigma**2))

def exponential(t,sigma):
    """t is time.  Exponential pulse, with frequency-domain decay constant
    gamma, normalized to behave like a delta function as sigma -> 0"""
    pre = 1/(2*sigma)
    return pre * np.exp(-np.abs(t)/sigma)

def lorentzian(t,sigma):
    """t is time.  Lorentzian pulse, with time-domain width sigma,
    normalized to behave like a delta function as sigma -> 0"""
    pre = sigma/2/np.pi
    return pre / (t**2 + (sigma/2)**2)

def sech(t,sigma):
    pre = 1/(np.pi*sigma)
    return pre * 1/np.cosh(t/sigma)

def constant_bandwidth_chirped_gaussian(t,bandwidth,chirp):
    sigma = np.sqrt(1+chirp**2)/bandwidth
    newsig = sigma/np.sqrt(1+1j*chirp)
    return gaussian(t,newsig)

def razorblade_gaussian(t,width,sigma):
    dt = t[1] - t[0]
    a = np.sinc(t*width/np.pi/2)*width/(2*np.pi)
    if sigma == 0:
        return a
    b = gaussian(t,sigma)
    return fftconvolve(a,b,mode='same')*dt


import math
import random
import warnings
import sympy

def phi(n):
    amount = 0
    for k in range(1, n + 1):
        if math.gcd(n, k) == 1:
            amount += 1
    return amount

def gcd(a, b):
    """Calculate the Greatest Common Divisor of a and b.

    Unless b==0, the result will have the same sign as b (so that when
    b is divided by it, the result comes out positive).
    """
    while b:
        a, b = b, a%b
    return a

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, r = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
    gcd = b
    return gcd, x, y

def modinv(a, m):
    gcd, x, y = egcd(a, m)
    if gcd != 1:
        return None  # modular inverse does not exist
    else:
        return x % m

small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
                53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,
                113]

def check_small_primes(p):
    for i in small_primes:
        if p%i==0:
            return -1
    return 1

def random_prime(lower_bound, upper_bound):
    warnings.simplefilter('ignore')
    chck = False
    while chck == False:
        p = random.randrange(lower_bound, upper_bound)
        if p < 10000:
            chck = sympy.isprime(p)
        elif check_small_primes(p) == 1:
            chck = sympy.isprime(p)
    warnings.simplefilter('default')
    return p

# Example usage
'''
p = random_prime(2**128)
q = random_prime(2**128)
n = p*q
phi_n = (p-1)*(q-1)

e = 67
d = modinv(e,phi_n)

m = b'my top secret key!!!???????....?'
print("m: ", m, len(m))

c = pow(int.from_bytes(m, byteorder='big'), e, n)

m_ = pow(c, d, n)

print(m_.to_bytes((m_.bit_length() + 7) // 8, byteorder='big'))
'''


import numpy as np
import scipy.stats

def zm2(events, M):
    """The Z_m^2 test for periodicity.

    The Z_m^2 test uses a sequence of events to construct an estimate of
    the first M Fourier coefficients of the distribution the events are drawn
    from. It then adds up the power in these coefficients and evaluates the
    likelihood that events drawn from a uniform distribution would have this
    much harmonic power.

    Parameters
    ----------

    events : array-like
        Events to be tested for uniformity modulo 1.
    M : integer
        Number of harmonics to use.

    Returns
    -------

    S : float
        The score.
    fpp : float
        The chance of random events from a uniform distribution having a
        score this high.

    """
    ev = np.reshape(events, (-1,))
    cs = np.sum(np.exp(2.j*np.pi*np.arange(1,M+1)*ev[:,None]),axis=0)/len(ev)
    Zm2 = 2*len(ev)*np.sum(np.abs(cs)**2)
    return Zm2, scipy.stats.chi2(2*M).sf(Zm2)


import numpy as np
import operator

def dct_ii(N: int) -> np.ndarray:
    """
    Generate the 1D DCT transform matrix of size N.

    Parameters:
    N (int): Size of DCT matrix required

    Returns:
    C (2D np array): 1D DCT transform matrix

    Uses an orthogonal Type-II DCT.
    Y = C * X tranforms N-vector X into Y.
    """
    C = np.ones((N, N)) / np.sqrt(N)
    theta = (np.arange(N) + 0.5) * (np.pi/N)
    g = np.sqrt(2/N)
    for i in range(1, N):
        C[i, :] = g * np.cos(theta*i)

    return C

def dct_iv(N: int) -> np.ndarray:
    """
    Generate the 1D DCT transform matrix of size N.

    Parameters:
    N (int): Size of DCT matrix required

    Returns:
    C (2D np array): 1D DCT transform matrix

    Uses an orthogonal Type-IV DCT.
    Y = C * X tranforms N-vector X into Y.
    """
    C = np.ones((N, N)) / np.sqrt(N)
    theta = (np.arange(N) + 0.5) * (np.pi/N)
    g = np.sqrt(2/N)
    for i in range(N):
        C[i, :] = g * np.cos(theta*(i+0.5))

    return C

def colxfm(X: np.ndarray, C: np.ndarray) -> np.ndarray:
    """
    Transforms the columns of X using the tranformation in C.

    Parameters:
        X: Image whose columns are to be transformed
        C: N-size 1D DCT coefficients obtained using dct_ii(N)
    Returns:
        Y: Image with transformed columns

    PS: The height of X must be a multiple of the size of C (N).
    """
    N = len(C)
    m, n = X.shape

    # catch mismatch in size of X
    if m % N != 0:
        raise ValueError('colxfm error: height of X not multiple of size of C')

    Y = np.zeros((m, n))
    # transform columns of each horizontal stripe of pixels, N*n
    for i in range(0, m, N):
        Y[i:i+N, :] = C @ X[i:i+N, :]

    return Y

def regroup(X, N):
    """
    Regroup the rows and columns in X.
    Rows/Columns that are N apart in X are adjacent in Y.

    Parameters:
    X (np.ndarray): Image to be regrouped
    N (list): Size of 1D DCT performed (could give int)

    Returns:
    Y (np.ndarray): Regoruped image
    """
    # if N is a 2-element list, N[0] is used for columns and N[1] for rows.
    # if a single value is given, a square matrix is assumed
    try:
        N_m = N_n = operator.index(N)
    except TypeError:
        N_m, N_n = N

    m, n = X.shape

    if m % N_m != 0 or n % N_n != 0:
        raise ValueError('regroup error: X dimensions not multiples of N')

    X = X.reshape(m // N_m, N_m, n // N_n, N_n)  # subdivide the axes
    X = X.transpose((1, 0, 3, 2))                # permute them
    return X.reshape(m, n)                       # and recombine

if __name__ == "__main__":
    import matplotlib.pyplot as plt

    from .familiarisation import load_mat_img
    from .familiarisation import prep_cmap_array_plt
    from .familiarisation import plot_image

    # code testing dct_iv
    print(dct_iv(8))
    # code for images
    '''
    N = 8
    C8 = dct_ii(N)
    # print(len(C8))
    # print(C8.shape)
    img = 'lighthouse.mat'
    img_info = 'X'
    cmap_info = {'map', 'map2'}
    X, cmaps_dict = load_mat_img(img, img_info, cmap_info)
    # print(X)
    X = X - 128
    # print(X)
    # Y = colxfm(X, C8)
    Y = colxfm(colxfm(X, C8).T, C8).T
    # plot_image(Y)

    cmap_array = cmaps_dict['map']
    cmap_plt = prep_cmap_array_plt(cmap_array, 'map')
    # plot_image(X, cmap_plt='gray')
    # plot_image(Y)
    print(regroup(Y, N)/N)
    # plot_image(regroup(Y, N)/N, cmap_plt)
    '''
    # code to check produced matrices are the same
    '''
    X = np.array([[1,2,3,4,5,6,7,8],
        [10,20,30,40,50,60,70,80],
        [3,6,9,13,15,17,22,32],
        [4,7,88,97,23,45,34,54],
        [1,2,3,4,5,6,7,8],
        [10,20,30,40,50,60,70,80],
        [3,6,9,13,15,17,22,32],
        [4,7,88,97,23,45,34,54]])
    #print(X)
    C4 = dct_ii(4)
    #print(C4)
    Y = colxfm(colxfm(X, C4).T, C4).T
    #print(Y)
    print(regroup(Y,4)/4)
    #plot_image(Y)
    '''


import numpy as np
import math

x1 = 5
x2 = 150
x3 = 150
x4 = 5
y1 = 5
y2 = 5
y3 = 150
y4 = 150
xp1 = 100
xp2 = 200
xp3 = 220
xp4 = 100
yp1 = 100
yp2 = 80
yp3 = 80
yp4 = 200
A = np.matrix([
                [-x1, -y1, -1, 0, 0, 0, x1*xp1, y1*xp1, xp1],
                [0, 0, 0, -x1, -y1, -1, x1*yp1, y1*yp1, yp1],
                [-x2, -y2, -1, 0, 0, 0, x2*xp2, y2*xp2, xp2],
                [0, 0, 0, -x2, -y2, -1, x2*yp2, y2*yp2, yp2],
                [-x3, -y3, -1, 0, 0, 0, x3*xp3, y3*yp3, yp3],
                [0, 0, 0, -x3, -y3, -1, x3*yp3, y3*yp3, yp3],
                [-x4, -y4, -1, 0, 0, 0, x4*xp4, y4*xp4, xp4],
                [0, 0, 0, -x4, -y4, -1, x4*yp4, y4*yp4, yp4]
             ])
# Printing the original matrix A
print("The entered matrix is : ")
print(A)

# AT is the transpose of matrix A, using the function matrix.transpose()
AT = A.transpose()
print("\n \n A transpose is : ")
print(AT)

# AAT is the multiplication of matrix A and AT, used the function numpy.matmul(matrix 1, matrix 2)
AAT = np.matmul(A, AT)
print("\n \n A*AT is : ")
print(AAT)

# ATA  is the multiplication of matrix AT and A, used the function numpy.matmul(matrix 1, matrix 2)
ATA = np.matmul(AT, A)
print("\n \n AT*A is : ")
print(ATA)

# Matrix U gives the eigen values for AAT and matrix S gives the eigen vectors for matrix AAT using function numpy.linalg.eig(matrix)
U, S = np.linalg.eig(AAT)
print("\n \n")
print("Orthogonal eigen values of AAT :")
print(U)
print("\n \n Orthogonal eigen vectors of AAT which is the U matrix of SVD :")
print(S)

# Matrix U_sqr gives the ∑ matrix of SVD which is calculated using numpy.sqrt(matrix)
U_sqr = np.sqrt(U)
print("\n \n The S matrix which is the ∑ matrix of SVD is : ")
print(U_sqr)

# Matrix V gives the eigen vectors for ATA and matrix Y gives the eigen values for matrix ATA using function numpy.linalg.eig(matrix)
V, Y = np.linalg.eig(ATA)
VT = V.transpose()
print("\n \n Orthogonal eigen values of ATA :")
print(VT)
print("\n \n Orthogonal eigen vectors of ATA :")
print(Y)

#Matrix Y_trans gives the transpose of Y matrix which is the desired VT matrix of SVD
print("\n \n Transpose of eigen vectors of ATA is the VT matrix of SVD which is : ")
Y_trans = Y.transpose()
print(Y_trans)

# Calculation using SVD function directly
u, s, vt = np.linalg.svd(A, full_matrices=True)

#print("Orthogonal eigen vectors of AAT :")
print("\n \n The Matrix U is : ")
print(u)

#print("Orthogonal eigen values of AAT :")
print("\n \n The Matrix S is : ")
print(s)

#print("Orthogonal eigen vectors of ATA :")
print("\n \n The Matrix VT is : ")

#vt_trans = vt.transpose()
print(vt)


import numpy as np
##This linear approach is done in order to determine the marks scored in an exam on the factors such as hours studied, hours slept and hours played on a day.
x1= np.array([6,3,4,7,8,10,6,8,3,9])## hours studied
x2= np.array([6,7,4,6,9,5,5,6,8,6])## hours slept
x3= np.array([6,5,5,3,2,2,4,5,3,1])## hours played
y = np.array([90,91,70,75,80,89,93,77,79,86])## marks scored
n=len(x1)
m1= 12
m2= 12
m3= 12
c = 12

learning_rate= 0.001

p=int(input('Enter the number of hours studied in a day :- '))
q=int(input('Enter the number of hours slept in a day :-  '))
r=int(input('Enter the number of hours played in a day :- '))
if (p+q+r<24) : ##Since it Impossible to things more than 24 hours in a day
    def gradient_descent(m1,m2,m3,c):
        for i in range(10000):
            c_gradient = 0
            m1_gradient = 0
            m2_gradient = 0
            m3_gradient = 0
            for j in range(n):
                m1_gradient += 2*(m1*x1[j] +m2*x2[j] +m3*x3[j] + c - y[j])*x1[j]/n
                m2_gradient += 2*(m1*x1[j] +m2*x2[j] +m3*x3[j] + c - y[j])*x2[j]/n
                m3_gradient += 2*(m1*x1[j] +m2*x2[j] +m3*x3[j] + c - y[j])*x3[j]/n
                c_gradient += 2*(m1*x1[j] +m2*x2[j] +m3*x3[j] + c - y[j])/n
            m1 = m1 - (learning_rate * m1_gradient)
            m2 = m2 - (learning_rate * m2_gradient)
            m3 = m3 - (learning_rate * m3_gradient)
            c = c - (learning_rate * c_gradient)
        return m1,m2,m3,c

    m1,m2,m3,c = gradient_descent(m1,m2,m2,c)

    def expect_value (p,q,r):
        y1=m1*p+m2*q+m3*r+c
        if (y1>100): ## since marks scored cannot be greater than 100
            return ('Marks scored :- 100')
        else:
            return print('Marks scored :- ',y1)

    expect_value(p,q,r)

else:
    print('Impossible time distribution')

users/Sourav/linear regression/multivariate linear regression.py


from numpy import apply_along_axis, linalg as LA
import numpy as np
from math import sqrt
from collections import OrderedDict
from numpy.lib.function_base import _append_dispatcher
from scipy.linalg import fractional_matrix_power

'''
    -define B_1 :  =  B  and  compute p_1 : =  maximum { p  |    p  | 1 ><  1  |  ≤  B_1 }
    Explicitly,  p_1  :  =  <1|  B_1  |1>

    -define B_2 :  = B_1 - p_1  |1><1|    and  compute p_2 : =  maximum { p  |    p  | 2 ><  2  |  ≤  B_2 }
    Explicitly,  p_2  :  =  <2|  B_2  |2>

    -iterate B_{k+1}  =  B_k  -  p_k |k><k|

    Finally, the probability of success is    sum_i  p_i  a_i

    Finally, you can compare the two solution.

    The above algorithm provides a lower bound to the optimal solution. It would be very cool if, actually, the lower bound were the exact solution.

'''

def heuristic(rho0, rho1):
    n = len(rho0)

    # A and B are positive semidefinite matrices. B is an invertible matrix.
    A = rho0
    B = rho0 + rho1
    x = fractional_matrix_power(B, -0.5)
    E = x @ A @ x

    w, v = LA.eig(E)

    d = {}
    for (index, eigenvalue) in enumerate(w):
        d[eigenvalue] = np.array(v[:, index]).reshape(n, 1)  # Column vectors.

    # Order eigenvalues descendingly.
    d_descending = OrderedDict(sorted(d.items(), reverse=True))

    # DEBUGGING.
    # print("Input rho_0 is:")
    # print(rho0)
    # print("Input rho_1 is:")
    # print(rho1)

    # print("E := B^-1/2 @ A @ B^-1/2 is:")
    # print(E)

    # print("Eigenvalues of E are:")
    # print(w)
    # print("Eigenvectors of E are:")
    # print(v)

    # print("d_descending is:")
    # print(d_descending)

    prevDensityMatrix, prevP, prevB = np.zeros((n, n)), 0, B
    p_i, ans = [], 0

    for index, (key, value) in enumerate(d_descending.items(), start=1):
        # Calculate B_{k+1} = B_k - p_k |k><k|
        newB = prevB - prevP * prevDensityMatrix
        newP = (value.T @ newB @ value).item()
        newP = min(newP, 1 - sum(p_i))  # Constraints to p.
        ans += newP * key
        p_i.append(newP)
        prevDensityMatrix = value @ value.T  # Calculate |k><k|.
        prevP, prevB = newP, newB

        # DEBUGGING.
        # print(f'B{index} is:')
        # print(newB)
        # print(f"p{index} is {newP}.")
        # print(f"|{index}><{index}| is:")
        # print(prevDensityMatrix)

    # print("p_i:", p_i)
    # print("The optimal value is", ans)

    return ans

if __name__ == "__main__":
    '''
    Change rho_0 and rho_1 here
    '''
    rho0 = np.array([[0.25, sqrt(3)/4], [sqrt(3)/4, 0.75]])
    # rho0 = np.array([[0.5625, 3 * sqrt(7)/16], [3 * sqrt(7)/16, 7/16]])    rho1 = np.array([[0.5, 0], [0, 0.5]])
    # rho0 = np.array([[1, 0], [0, 0]])
    rho1 = np.array([[0.5, 0], [0, 0.5]])
    # rho0 = np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])
    # rho1 = np.array([[0.25, 0, 0, 0], [0, 0.25, 0, 0],
    #                  [0, 0, 0.25, 0], [0, 0, 0, 0.25]])
    # rho0 = np.array([[1/9, 1/9, sqrt(7)/9], [1/9, 1/9, sqrt(7)/9],
    #                  [sqrt(7)/9, sqrt(7)/9, 7/9]])
    # rho1 = np.array([[1/4, 1/4, 1/(2 * sqrt(2))], [1/4, 1/4, 1/(2 * sqrt(2))],
    #                  [1/(2 * sqrt(2)), 1/(2 * sqrt(2)), 1/2]])
    heuristic(rho0, rho1)


# by Colm Doherty, started on 2018-4-4. Scripting statistical analysis for Project-2018
# to figure out how to manipulate the dataset to discover max, min and mean
# values in each column, and more if possible. Initial source for methodolgy was a
# free tutorial at https://www.youtube.com/watch?v=Tq6rCWPdXoQ and then an
# essay on "Functions, modules, packages and libraries" recommended by Ian McLoughlin at:
# https://nbviewer.jupyter.org/github/ianmcloughlin/python-fundamentals-notes/blob/master/functions-modules.ipynb#

import numpy as np
import matplotlib.pyplot as plt
    # importing the numpy & matplot libraries to help manipulate the data, and giving them shorthand names
    # Alternately I could import these into iPython while testing. Remember I'm working on a multivariate dataset
data = np.genfromtxt('Data/Iris.csv', delimiter=',')
    # importing the iris dataset, as a csv file (syntax found on stack overflow)
col1 = (data[:,0])
col2 = (data[:,1])
col3 = (data[:,2])
col4 = (data[:,3])
    # name the columns, so I can call them later if required e.g. for histogram plots

    #NEW FUNCTION for column mean (prototyped in my FuncTest.py in the CDs-Sample-Python-Code repository):
def colmean(colno):
    meancol = (np.mean(data[:,colno]))
    return meancol

print("Column 1 mean is:",'{:0.3f}'.format(colmean(0)))
print("Column 2 mean is:",'{:0.3f}'.format(colmean(1)))
print("Column 3 mean is:",'{:0.3f}'.format(colmean(2)))
print("Column 4 mean is:",'{:0.3f}'.format(colmean(3)))
    # YAY! took >2hrs of trial & error to get right, w/help from Ian's "Defining functions" video

    # NEW FUNCTIONS for column max, min, std dev:
def colmax(colno):
    maxcol = (np.max(data[:,colno]))
    return maxcol

print("Column 1 max is:",'{:0.1f}'.format(colmax(0)))
print("Column 2 max is:",'{:0.1f}'.format(colmax(1)))
print("Column 3 max is:",'{:0.1f}'.format(colmax(2)))
print("Column 4 max is:",'{:0.1f}'.format(colmax(3)))

def colmin(colno):
    mincol = (np.min(data[:,colno]))
    return mincol

print("Column 1 min is:",'{:0.1f}'.format(colmin(0)))
print("Column 2 min is:",'{:0.1f}'.format(colmin(1)))
print("Column 3 min is:",'{:0.1f}'.format(colmin(2)))
print("Column 4 min is:",'{:0.1f}'.format(colmin(3)))

def colstd(colno):
    stdcol = (np.std(data[:,colno]))
    return stdcol

print("Column 1 std dev is:",'{:0.3f}'.format(colstd(0)))
print("Column 2 std dev is:",'{:0.3f}'.format(colstd(1)))
print("Column 3 std dev is:",'{:0.3f}'.format(colstd(2)))
print("Column 4 std dev is:",'{:0.3f}'.format(colstd(3)))

    # Now lets split out the 3 varieties in the dataset, for closer analysis ...

print("the value at row 3, column 2 is:",data[2,1])
    # checking that I can call a value from a specific cell. Syntax is row,column
    # now I want to call a range of rows in each column ...
    # NEW generic functions for Mean, Max, Min of each Variety:

def colmeanS(colno):
    meancolS = (np.mean(data[0:49,colno]))
    return meancolS

print("(C1) Setosa S.L. mean is:",'{:0.3f}'.format(colmeanS(0)))
print("(C2) Setosa S.W. mean is:",'{:0.3f}'.format(colmeanS(1)))
print("(C3) Setosa P.L. mean is:",'{:0.3f}'.format(colmeanS(2)))
print("(C4) Setosa P.W. mean is:",'{:0.3f}'.format(colmeanS(3)))
    # displays the mean for each Column in the Setosa sample

def colmaxS(colno):
    maxcolS = (np.max(data[0:49,colno]))
    return maxcolS

print("(C1) Setosa S.L. max is:",'{:0.3f}'.format(colmaxS(0)))
print("(C2) Setosa S.W. max is:",'{:0.3f}'.format(colmaxS(1)))
print("(C3) Setosa P.L. max is:",'{:0.3f}'.format(colmaxS(2)))
print("(C4) Setosa P.W. max is:",'{:0.3f}'.format(colmaxS(3)))
    # displays the max for each Column in the Setosa sample

def colminS(colno):
    mincolS = (np.min(data[0:49,colno]))
    return mincolS

print("(C1) Setosa S.L. min is:",'{:0.3f}'.format(colminS(0)))
print("(C2) Setosa S.W. min is:",'{:0.3f}'.format(colminS(1)))
print("(C3) Setosa P.L. min is:",'{:0.3f}'.format(colminS(2)))
print("(C4) Setosa P.W. min is:",'{:0.3f}'.format(colminS(3)))
  # displays the min for each Column in the Setosa sample

def colmeanVr(colno):
    meancolVr = (np.mean(data[50:99,colno]))
    return meancolVr

print("(C1) Versicolor S.L. mean is:",'{:0.3f}'.format(colmeanVr(0)))
print("(C2) Versicolor S.W. mean is:",'{:0.3f}'.format(colmeanVr(1)))
print("(C3) Versicolor P.L. mean is:",'{:0.3f}'.format(colmeanVr(2)))
print("(C4) Versicolor P.W. mean is:",'{:0.3f}'.format(colmeanVr(3)))
    # displays the mean for each Column in the Versicolor sample

def colmaxVr(colno):
    maxcolVr = (np.max(data[50:99,colno]))
    return maxcolVr

print("(C1) Versicolor S.L. max is:",'{:0.3f}'.format(colmaxVr(0)))
print("(C2) Versicolor S.W. max is:",'{:0.3f}'.format(colmaxVr(1)))
print("(C3) Versicolor P.L. max is:",'{:0.3f}'.format(colmaxVr(2)))
print("(C4) Versicolor P.W. max is:",'{:0.3f}'.format(colmaxVr(3)))
    # displays the max for each Column in the Versicolor sample

def colminVr(colno):
    mincolVr = (np.min(data[50:99,colno]))
    return mincolVr

print("(C1) Versicolor S.L. min is:",'{:0.3f}'.format(colminVr(0)))
print("(C2) Versicolor S.W. min is:",'{:0.3f}'.format(colminVr(1)))
print("(C3) Versicolor P.L. min is:",'{:0.3f}'.format(colminVr(2)))
print("(C4) Versicolor P.W. min is:",'{:0.3f}'.format(colminVr(3)))
    # displays the min for each Column in the Versicolor sample

def colmeanVg(colno):
    meancolVg = (np.mean(data[100:149,colno]))
    return meancolVg

print("(C1) Virginica S.L. mean is:",'{:0.3f}'.format(colmeanVg(0)))
print("(C2) Virginica S.W. mean is:",'{:0.3f}'.format(colmeanVg(1)))
print("(C3) Virginica P.L. mean is:",'{:0.3f}'.format(colmeanVg(2)))
print("(C4) Virginica P.W. mean is:",'{:0.3f}'.format(colmeanVg(3)))
    # displays the mean for each Column in the Virginica sample

def colmaxVg(colno):
    maxcolVg = (np.max(data[100:149,colno]))
    return maxcolVg

print("(C1) Virginica S.L. max is:",'{:0.3f}'.format(colmaxVg(0)))
print("(C2) Virginica S.W. max is:",'{:0.3f}'.format(colmaxVg(1)))
print("(C3) Virginica P.L. max is:",'{:0.3f}'.format(colmaxVg(2)))
print("(C4) Virginica P.W. max is:",'{:0.3f}'.format(colmaxVg(3)))
    # displays the max for each Column in the Virginica sample

def colminVg(colno):
    mincolVg = (np.min(data[100:149,colno]))
    return mincolVg

print("(C1) Virginica S.L. min is:",'{:0.3f}'.format(colminVg(0)))
print("(C2) Virginica S.W. min is:",'{:0.3f}'.format(colminVg(1)))
print("(C3) Virginica P.L. min is:",'{:0.3f}'.format(colminVg(2)))
print("(C4) Virginica P.W. min is:",'{:0.3f}'.format(colminVg(3)))
    # displays the min for each Column in the Virginica sample

# Std Deviations for Petal Length on 3 varieties(column3):
col3Setstd = (np.std(data[0:49,2]))
print("Petal Length Setosa std is:",'{:0.3f}'.format(col3Setstd))
col3Varstd = (np.std(data[50:99,2]))
print("Petal Length Versicolor std is:",'{:0.3f}'.format(col3Varstd))
col3Virgstd = (np.std(data[100:149,2]))
print("Petal Length Virginica std is:",'{:0.3f}'.format(col3Virgstd))

# Selecting data to plot Histograms:
plt.hist(col2)
plt.title('Histogram of Sepal Widths')
plt.xlabel('Sepal Width (cm)')
plt.ylabel('Frequency')
plt.show()

plt.hist(col3)
plt.title('Histogram of Petal Lengths')
plt.xlabel('Petal Length (cm)')
plt.ylabel('Frequency')
plt.show()

# learnt labeling cmds at https://matplotlib.org/gallery/pyplots/pyplot_text.html#sphx-glr-gallery-pyplots-pyplot-text-py
# also useful ref (though code used is R): http://www.lac.inpe.br/~rafael.santos/Docs/R/CAP386/IntroEDA-Iris.html

# Add the remaining two variables to plot Histograms:
plt.hist(col1)
plt.title('Histogram of Sepal Lengths')
plt.xlabel('Sepal Length (cm)')
plt.ylabel('Frequency')
plt.show()

plt.hist(col4)
plt.title('Histogram of Petal Widths')
plt.xlabel('Petal Width (cm)')
plt.ylabel('Frequency')
plt.show()

# now to plot the Petal Lengths by Variety:
col31 = (data[0:49,2])
col32 = (data[50:99,2])
col33 = (data[100:149,2])

plt.hist(col31)
plt.title('Petal Lengths of Setosa variety')
plt.xlabel('Petal Length (cm)')
plt.ylabel('Frequency')
plt.show()

plt.hist(col32)
plt.title('Petal Lengths of Versicolor variety')
plt.xlabel('Petal Length (cm)')
plt.ylabel('Frequency')
plt.show()

plt.hist(col33)
plt.title('Petal Lengths of Virginica variety')
plt.xlabel('Petal Length (cm)')
plt.ylabel('Frequency')
plt.show()

# method for Histogram 3-way layout found on Stack overflow:
# https://stackoverflow.com/questions/24319505/how-can-one-display-images-side-by-side-in-a-github-readme-md

#  FOOTNOTES
#  which data is found where?
#           	|   SL    |   SW    |   PL   |   PW   |
#	     	    -—————————————————————————————————————
#  Setosa	    | 	    	    0 - 49 			      |
#  Versicolor	|	           50 - 99		    	  |
#  Virginica	|	          100 - 149		          |
#	        	——————————————————————————————————————-

# Listen. Strange women lying in ponds distributing swords is no basis for a system of government.


import numpy as np
from scipy.stats import multivariate_normal

class GaussianMixture:

    def __init__(self, n_components, max_iter=100, n_restart=10, tol=1e-3):
        self.n_components = n_components
        self.max_iter = max_iter
        self.n_restart = n_restart
        self._loss = -np.inf
        self._w = None
        self._mu = None
        self._sigma = None
        self._tol = tol

    def fit(self, X, y=None):
        for i in range(self.n_restart):
            w, mu, sigma, loss = self._fit(X, y)

            if loss > self._loss:
                self._loss = loss
                self._w = w
                self._mu = mu
                self._sigma = sigma

        return self

    def predict(self, X):
        return self._e_step(X, self._w, self._mu, self._sigma)

    def fit_predict(self, X, y=None):
        return self.fit(X).predict(X)

    def _fit(self, X, y=None):
        loss = -np.inf
        w, mu, sigma = self._init_params(X)

        for i in range(self.max_iter):
            loss_prev = loss
            probs = self._e_step(X, w, mu, sigma)
            w, mu, sigma = self._m_step(X, probs)
            loss = self._compute_loss(X, w, mu, sigma)

            if np.abs(loss_prev - loss) < self._tol:
                break

        return w, mu, sigma, loss

    def _init_params(self, X):
        w = np.ones((self.n_components)) / self.n_components
        mu = (X[np.random.randint(X.shape[0], size=self.n_components)]).T
        sigma = np.dstack([np.cov(X.T)] * self.n_components)

        return w, mu, sigma

    def _gaussian_prob(self, x, mu, sigma, log=False):
        if log:
            return multivariate_normal.logpdf(x, mean=mu, cov=sigma)
        else:
            return multivariate_normal.pdf(x, mean=mu, cov=sigma)

    def _e_step(self, X, w, mu, sigma):
        probs = np.zeros((X.shape[0], self.n_components))
        for k in range(self.n_components):
            probs[:, k] = w[k] * self._gaussian_prob(X, mu[:, k], sigma[:, :, k])

        probs /= probs.sum(axis=1).reshape(-1, 1)

        return probs

    def _m_step(self, X, probs):
        n_k, n = probs.sum(axis=0), X.shape[0]

        w = n_k / n
        mu = (X.T @ probs) / n_k
        sigma = np.zeros((X.shape[1], X.shape[1], self.n_components))
        for k in range(self.n_components):
            sigma[:, :, k] = (probs[:, k].reshape(X.shape[0], 1) * (X - mu[:, k])).T @ (X - mu[:, k]) / (probs[:, k].sum())

        return w, mu, sigma

    def _compute_loss(self, X, w, mu, sigma):
        loss = 0
        for k in range(self.n_components):
            loss += np.log(w[k]) + self._gaussian_prob(X, mu[:, k], sigma[:, :, k], log=True)

        return loss.sum()


# Building matrices for power graph
# Topology used is from Topology.JPG

import numpy as np

#A = np.matrix([[0, 1, 1, 0, 0], [1, 0, 1, 1, 0], [1, 1, 0, 0, 0], [0, 1, 0, 0, 1], [0, 0, 0, 1, 0]])
#Hale's A matrix
A = np.matrix([[0, 1, 0, 0, 0, 0], [1, 0, 1, 1, 1, 0], [0, 1, 0, 1, 0, 0], [0, 1, 1, 0, 0, 1], [0, 1, 0, 0, 0, 1], [0, 0, 0, 1, 1, 0]])

# admittance of each lines (1/reactance), adjust if necessary
# w1 = 1/2
# w2 = 1/4
# w3 = 1/10
# w4 = 1/20
# w5 = 1/0.5

L=7 #number of links
w1 = 1
w2 = 1
w3 = 1
w4 = 1
w5 = 1
w6 = 1
w7 = 1

#W = np.matrix([[0, w1, w2, 0, 0], [w1, 0, w3, w4, 0], [w2, w3, 0, 0, 0], [0, w4, 0, 0, w5], [0, 0, 0, w5, 0]])
#Hale's W matrix
W = np.matrix([[0, w1, 0, 0, 0, 0], [w1, 0, w3, w4, w5, 0], [0, w2, 0, w5, 0, 0], [0, w3, w5, 0, 0, w6], [0, w4, 0, 0, 0, w7], [0, 0, 0, w6, w7, 0]])
# NxL weighted incidence matrix
#B = np.matrix([[w1, w2, 0, 0, 0], [-w1, 0, -w3, w4, 0], [0, -w2, w3, 0, 0], [0, 0, 0, -w4, w5], [0, 0, 0, 0, -w5]])
#Hale's B matrix
B = np.matrix([[w1, 0, 0, 0, 0, 0, 0], [-w1, w2, w3, w4, 0, 0, 0], [0, -w2, 0, 0, w5, 0, 0], [0, 0, -w3, 0, -w5, w6, 0], [0, 0, 0, -w4, 0, 0, w7], [0, 0, 0, 0, 0, -w6, -w7]])
# Power input per node
P = np.matrix([[2], [1], [0], [-1.5], [-1.5], [0]])
N = W.shape[0]

def diagonal(W):
    """Make a diagonal matrix D from W.
    Each diagonal entry is formed by sum of components of W of the corresponding row.
    Returns diagonal matrix D"""

    # Axis is 1 to sum over the rows
    rowsum = W.sum(axis=1)

    # squeeze to 1-D numpy array
    rowsum = np.asarray(rowsum).squeeze()

    # lay out over diagonal
    D = np.diag(rowsum)

    return D

def svd(Q, reduced=False, test=False):
    """Gives the the spectral decomposition of matrix Q. If test=True, it will output the matrix
    Q - v^T*diag(w)*v. If all the values are (close to) zero, the spectral decomposition has succeeded.
    If reduced is true, it returns the v and w with the smallest eigenvalue and its corresponding
    eigenvector left out.
    Output:
    w : the eigenvalues in ascending order
    v : matrix of eigenvectors. column v[:, i] is the normalized eigenvector corresponding to eigenvalue w[i]"""

    # See description of numpy.linalg.eigh for details about what is returned exactly
    w,v = np.linalg.eigh(Q)
    N = len(w)

    if test == True:

        # Create matrix with eigenvalues on the diagonal.
        eigv = np.zeros((N, N))
        for i in range(N):
            eigv[i][i] = w[i]

        Q2 = np.linalg.multi_dot([v, eigv, v.transpose()])
        Q4 = np.subtract(Q,Q2)
        print('Matrix Q minus v * w * v^T should be (approaching) the zero matrix, which is:', Q4 )

    if reduced == True:
        eigv = w[1:N]
        w = eigv
        v = v[:,1:N]  # +1 because the new w is reduced

    return w,v

def laplacian(w,v):
    """Returns the inverse of a matrix, based on its reduced eigenvectors and eigenvalues."""

    w_recip = np.reciprocal(w)
    N = len(w_recip)
    w_inv = np.zeros((N,N))
    for i in range(N):
        w_inv[i][i] = w_recip[i]

    Qinv = np.linalg.multi_dot([v, w_inv, v.transpose()])

    return Qinv

def new_laplacian(i, j, Qinv, omega, w):
    """New laplacian calculated for the link addition between node i and j.
    Returns the new laplacian. Q'inv
    --------------------------
    i : node from
    j : node to
    Qinv : old laplacian
    omega : effective resistance matrix
    w : weight of the added link"""

    N = Q.shape[0]

    e = np.subtract(e(N,i), e(N,j))
    x = np.linalg.multi_dot([Qinv, e, e.transpose(), Qinv])
    scale = w/(1+(omega.item(j,i)*w))
    Qinv_new = np.subtract(Qinv, np.multiply(scale,x))

    return Qinv_new

def e(size, index):
    """Returns the base vector.
    ------------
    size : length of the base vector
    index : index at which base vector contains a one, note: index starts from 0 in python """
    arr = np.zeros(size)
    arr[index] = 1.0

    return arr

D = diagonal(W)

Q = np.subtract(D,W)

# #Pseudoinverse function for checking
# Qpinv = np.linalg.pinv(Q, rcond=1e-10)

# Pseudo inverse of the laplacian, when reduced=True
w,v = svd(Q,reduced=True,test=False)
Qinv = laplacian(w,v)

#Flow in the graph per link
F = np.linalg.multi_dot([B.transpose(),Qinv,P])
#print(A)
# print(np.where(A==0)[0], np.where(A==0)[1])

def get_nodes_lines(A, exist=True):
    """From adjacency matrix A, it finds the nodes between which there exists or does not exists a line.
    Returns the coordinates in a list of tuples.
    -----------
    A : adjacency matrix of a graph
    """
    if not exist:
        A = -(A - 1)

    # get upper triangles with ones
    uptr_ones = np.tril(A, -1).T

    # get boolean coordinates matrix
    coord_matrix = np.multiply(A, uptr_ones)

    # extract coordinates and rezip to list of coordinates
    coordinates = np.where(coord_matrix==1)
    coordinates = list(zip(*coordinates))

    return coordinates

def omega(Qinv : np.matrix):
    """Get Omega from the pseudoinverse.
    Omega = z*u^T + u*z^T - 2*Qinv
    --------------
    Qinv : the pseudoinverse"""

    # make sure the input is an np.matrix class
    assert isinstance(Qinv, np.matrix)

    # get z & u
    z = np.matrix(np.diag(Qinv)).T
    u = np.matrix(np.ones(z.shape))

    # calculate omega
    omega = z * u.T + u * z.T - 2 * Qinv

    return omega

def delta_flow(omega, A, W, L):
    """Builts the deltaf matrix from figure 2.2 (thesis hale) considering f_ij is unity. """
    new_row_coord = [0, 0, 0, 1, 2, 2, 2]
    new_col_coord = [1, 4, 5, 5, 3, 4, 5]

    new_line_coordinates = list(zip(new_row_coord, new_col_coord))
    # new_line_coordinates = get_nodes_lines(A, exist=False)
    exist_line_coordinates = get_nodes_lines(A, exist=True)

    omega = omega(Qinv)
    deltaf = np.zeros((len(new_line_coordinates), len(exist_line_coordinates)))

    for x, (i, j) in enumerate(new_line_coordinates):
        for y, (a, b) in enumerate(exist_line_coordinates):
            delta = (W[b,a] * (omega[i,a]-omega[j,a]+omega[j,b]-omega[i,b]))/2
            deltaf[x,y] = delta

    return deltaf

delta_f = delta_flow(omega, A, W, L)

np.set_printoptions(suppress=True, precision=4)
print(delta_f)


import math
import timeit
import numpy as np
from numpy.random import randn
from scipy.misc import factorial
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

#problem 1

def arrmul(A,B):
    new = []
    for i in range(len(A)):
        newrow = []
        for k in range(len(B[0])):
            tot = 0
            for j in range(len(B)):
                tot += A[i][j] * B[j][k]
            newrow.append(tot)
        new.append(newrow)
    return new

def timefunction(f, *args, **kwargs):
	pfunc = lambda: f(*args, **kwargs)
	print min(timeit.repeat(pfunc, number = 1, repeat = 1))

k = 100
A = [range(i, i+k) for i in range(0, k**2, k)]
B = [range(i, i+k) for i in range (0, k**2, k)]

#timefunction(numpy.dot, NumA, NumA
#timefunction(arrmul, A, B)
'''
Lists
k=100: 0.195740438121
k=200: 1.96796994247
k=300: 7.87688692047

Arrays
k=100: 0.000890023231932
k=200: 0.00714212847242
k=300: 0.0233234591569

It takes significantly less time to square a two dimensional NumPy array
than it does to square a two dimensional list. This is because Python is a
high level interpreted language and thus slower than lower level compiled
languages. NumPy has heavily optimized algorithms that use Python to run code that
has been written and optimized in other languages (usually C or Fortran)
'''

#problem 2
def problem2():
    A = rand(1000,1000)
    #timeit A.reshape(A.size)
    #timeit A.flatten()
    #timeit A.reshape((1, A.size))
    print "A.reshape(A.size) had a best time of 5.65 microseconds"
    print "A.flatten() had a best time of 26.2 milliseconds"
    print "A.reshape((1, A.size)) had a best time of 3.15 microseconds"
    '''
    A.flatten() takes longer because it is allocating a new array
    in memory and copying all of the values from the input array into
    the new array. Will return a copy (which takes more time).

    A.reshape() only changes the way the array is read from memory by changing
    the shape of the array. It doesn't touch any of the data of the array.
    Will return a view (which takes less time) if possible.

  	'''

# problem 3
def laplace(U, tol):
    new = U.copy()
    dif = tol
    while tol <= dif:
        new[1:-1,1:-1] = (U[:-2,1:-1] + U[2:,1:-1] + U[1:-1,:-2] + U[1:-1,2:])/4.0
        dif = np.max(np.absolute(U-new))
        U[:] = new

n = 100
tol = .0001
U = np.ones ((n , n ))
U [:,0] = 100 # set north boundary condition
U [:,-1] = 100 # set south boundary condition
U [0] = 0 # set west boundary condition
U [-1] = 0 # set east boundary condition
laplace(U, tol) # U has been changed in place
x = np.linspace (0, 1, n)
y = np.linspace (0, 1, n)
X, Y = np.meshgrid (x, y)
fig = plt.figure()
ax = fig.gca( projection = '3d')
ax.plot_surface (X, Y, U, rstride=5)
plt.show()

# problem 4

#as n increases the variance approaches 0.

def large_numbers(n):
	# demonstrates law of large numbers
	# as n increases, variance goes to 0.
    A = randn(n, n)
    return A.mean(axis=1).var()

# problem 5
def rgb():
	A = np.random.randint(0, 256, (100, 100, 3))
	A * [.5, .5, 1]

# problem 6
def arcsin_approx():
	n = 70
	s = 1. * np.arange(70,-1,-1)
	r = factorial(2*s)/((2*s+1)*(factorial(s)**2)*(4**s)) # computes coefficients
	q = np.zeros(142)
	q[0::2] = r
	P = np.poly1d(q)
	return P(1/math.sqrt(2))*4

def W_approx():
	n = 20
	s = 1. * np.arange(20,0,-1)
	r = ((-s)**(s-1))/(factorial(s)) # computes coefficients
	q = np.zeros(21)
	q[0:-1] = r
	P = np.poly1d(q)
	return P(.25)

print W_approx()*math.e**W_approx() #verification!

# The problems from here on are no longer in the first lab.

# problem 6
def egcd(a, b):
    '''
    Extended Euclidean algorithm
    Returns (b, x, y) such that mx + ny = b
    Source: http://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
    '''
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q,r = b//a,b%a; m,n = x-u*q,y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
    return b, x, y

def modinv(a, m):
    '''
    Find the modular inverse.
    Source: http://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
    '''
    g, x, y = egcd(a, m)
    if g != 1:
        return None  # modular inverse does not exist
    else:
        return x % m

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

def blockize(msg, n):
    lut = {a:i for i ,a in enumerate(string.lowercase)}
    msg = "".join(msg.lower().split())
    return list(map(np.array, grouper(map(lut.__getitem__, msg), n, fillvalue=lut['x'])))

def inv_mat(n):
    tries = 0
    while True:
        a = np.random.randint(1000, size=(n, n)) % 26
        d = round(linalg.det(a))

        if gcd(int(d), 26) == 1:
            break
        tries += 1

    return a, d

def encode(msg, k):
    ciphertext = []
    n = k.shape[0]
    ilut = {i:a for i, a in enumerate(string.lowercase)}
    for i in blockize(msg, n):
        s = i.dot(k) % 26
        ciphertext.append("".join(map(ilut.__getitem__, s)))

    return "".join(ciphertext)

def inv_key(key):
    d = round(linalg.det(key))
    inv_d = modinv(int(d), 26)
    ik = np.round(d*linalg.inv(key))
    return (ik*inv_d) % 26

def decode(msg, k):
    ik = inv_key(k)
    n = ik.shape[0]
    plaintext = []
    ilut = {i:a for i, a in enumerate(string.lowercase)}
    for i in blockize(msg, n):
        s = i.dot(ik) % 26
        plaintext.append("".join(map(ilut.__getitem__, s)))

    return "".join(plaintext)

def prob5():
    im = np.random.randint(1,256,(100,100,3))
    b = np.array([0.5,0.5,1])
    im_bluer = (im * b).astype(int)

def broadcast_1():
    """All input arrays have exactly the same shape"""
    a = np.random.rand(4, 5)
    b = np.random.rand(4, 5)
    r = a * b
    print "Case 1: {} * {} = {}".format(a.shape, b.shape, r.shape)

def broadcast_2():
    """All input arrays are of the same dimension and
    the length of corresponding dimensions match or is 1"""

    a = np.random.rand(5, 4, 1, 6)
    b = np.random.rand(5, 4, 1, 1)
    r = a * b
    print "Case 2: {} * {} = {}".format(a.shape, b.shape, r.shape)

def broadcast_3():
    """All input arrays of fewer dimension can have 1
    prepended to their shapes to satisfy the second criteria."""

    a = np.random.rand(1, 6)
    b = np.random.rand(5, 4, 1, 6)
    r = a * b
    print "Case 3: {} * {} = {}".format(a.shape, b.shape, r.shape)

def series_problem_a():
	c = np.arange(70, -1, -1) # original values for n
	c = factorial(2*c) / ((2*c+1) * factorial(c)**2 * 4**c) #series coeff's
	p = np.zeros(2*c.size) # make space for skipped zero-terms
	p[::2] = c # set nonzero polynomial terms to the series coeff's
	P = np.poly1d(p) # make a polynomial out of it
	return 6 * P(.5) #return pi (since pi/6 = arcsin(1/2))

def series_problem_b():
	p = np.arange(20, -1, -1) # original values for n
	p = (-p)**(p-1) / factorial(p) #compute coefficients
	p[-1] = 0. # get rid of NAN in the zero-term
	P = np.poly1d(p) # Make a polynomial
	print P(.25) * np.exp(P(.25)) # test it
	return P(.25) # return the computed value


"""
Implementations of functions for
Black-Scholes European Options Pricing

"""

import numpy as np
from numpy.random import default_rng
from scipy.stats import norm
from scipy.optimize import brentq

def generate_GBM_paths(n_samples, S0, T, r, sigma, dt, seed=2021):
    """
    Exact simulation of GBM under the risk-neutral measure Q

    + n_samples: Number of paths to simulate
    + S0: Initial Stock Price
    + T: Timesteps
    + r: risk free rate
    + sigma: volatility
    + dt: Time increment, e.g. dt = 1/250 years. Ensure that sigma and dt are of the same scale
    + seed: seed for reproducibilitys

    Returns:

    + tis: timesteps
    + Sts: n_samples * (T + 1) numpy array , in which each row corresponds to a stock path

    Note: prices are generated under Q such that the drift is the risk free rate r

    """
    rng = default_rng(seed)
    Zs = rng.standard_normal((n_samples, T)) # Brownian Motion increments
    Zs = np.hstack([np.zeros((n_samples, 1)), Zs])
    tis = np.arange(T + 1) #0, 1 .. T
    tis = np.tile(tis, n_samples).reshape(n_samples, T + 1) # [[0, 1.. T], [0, 1.. T]...]
    # Sample paths
    Sts = S0 * np.exp((r - 0.5 * sigma ** 2) * tis * dt + sigma * np.sqrt(dt) * np.cumsum(Zs, axis=1))
    return tis, Sts

def BlackScholes(St, K, r, sigma, tau, flag):
    """
    Inputs:

    + St: Current Price of the stock at time t
    + K: Strike Price
    + r: risk-free rate
    + sigma: Black-Scholes implied volatility
    + tau: time-to-maturity. Ensure that sigma, tau are in the same scale.
    + flag: 1 if call, 0 if put

    Outputs:

    Returns the Black-Scholes price

    """
    d1 = (np.log(St/K) + (r + 0.5 * sigma ** 2) * (tau)) / (sigma * np.sqrt(tau))
    d2 = d1 - sigma * np.sqrt(tau)
    if flag == 1:
      return norm.cdf(d1) * St - np.exp(-r * (tau)) * norm.cdf(d2) * K
    else:
      return np.exp(-r * (tau)) * K * norm.cdf(-d2) - St * norm.cdf(-d1)

def delta(St, K, r, sigma, tau, flag):
    """
    Inputs:

    + St: Current Price of the stock at time t
    + K: Strike Price
    + r: risk-fre rate
    + sigma: Black-Scholes implied volatility
    + tau: time-to-maturity. Ensure that sigma, tau are in the same scale.
    + flag: 1 if call, 0 if put

    Outputs:

    Returns the Black-Scholes Delta

    """
    d1 = (np.log(St/K) + (r + 0.5 * sigma ** 2) * (tau)) / (sigma * np.sqrt(tau))
    if flag == 1:
      return norm.cdf(d1)
    else:
      return -norm.cdf(-d1)

def vega(St, K, r, sigma, tau, flag):
    """
    Inputs:

    + St: Current Price of the stock at time t
    + K: Strike Price
    + r: risk-free rate
    + sigma: Black-Scholes implied volatility
    + tau: time-to-maturity. Ensure that sigma, tau are in the same scale.
    + flag: 1 if call, 0 if put

    Outputs:

    Returns the Black-Scholes Vega

    """
    d1 = (np.log(St/K) + (r + 0.5 * sigma ** 2) * (tau)) / (sigma * np.sqrt(tau))
    return St * norm.pdf(d1) * np.sqrt(tau) / 100

def bsinv(price, St, K, r, tau, flag):
    """
    Inputs:

    + price: price of the liability
    + St: current stock price
    + K: strike price
    + r: risk-free rate
    + tau: time to maturity
    + flag: 1 if call, 0 if put

    Outputs:

    + imp_vol: Black Scholes implied volatility

    """

    error = lambda s: BlackScholes(St, K, r, s, tau, flag) - price
    imp_vol = brentq(error, 1e-9, 1e+9)

    return imp_vol


"""Design Matrix

Basis functions are
    - gauss
    - sigmoid
    - polynomial

"""
import numpy as np
from abc import abstractclassmethod,ABCMeta

class DesignMat(metaclass=ABCMeta):
    """DesignMat Base Class

    Attributes:
        mu (1-D array) : mean parameter
        s (1-D array) : standard deviation parameter

    """
    def __init__(self,mu = None,s = None):
        if mu is None:
            self.mu = np.random.normal(loc = 0,scale = 10,size = 10)
        else:
            self.mu = mu
        if s is None:
            s = np.random.normal(loc = 0,scale = 10,size = 10)
            s[abs(s) < 1e-10] = 1e-5
            self.s = s
        else:
            self.s = s

    @abstractclassmethod
    def _basis_function(self,x):
        """Basis function

        Args:
            x (1-D array) : data,shape = (N_dim)

        Returns :
            1-D array: data.shape = (N_featuredim)

        """
        pass

    def __call__(self,X):
        """Make design matrix using X

        Args :
            X (2-D array) : data, shape = (N_samples,N_dim)

        Returns :
            2-D array: design_mat, shape = (N_samples,N_featuredim)

        """
        return np.vstack([self._basis_function(x) for x in X])

class GaussMat(DesignMat):
    """Gauss Basis Function

    Attributes:
        mu (1-D array) : mean parameter
        s (1-D array) : standard deviation parameter

    """
    def __init__(self,mu = None,s = None):
        super(GaussMat,self).__init__(mu,s)

    def _basis_function(self,x):
        """Basis function

        Args:
            x (1-D array) : data,shape = (N_dim)

        Returns :
            1-D array: data.shape = (N_featuredim)

        """
        phi = np.exp(-(x.reshape(-1,1) - self.mu)/(2*self.s**2))
        phi = np.concatenate(([1],phi.ravel()))
        return phi

class SigmoidMat(DesignMat):
    """Sigmoid Basis Function

    Attributes:
        mu (1-D array) : mean parameter
        s (1-D array) : standard deviation parameter

    """
    def __init__(self,mu = None,s = None):
        super(SigmoidMat,self).__init__(mu,s)

    def _basis_function(self,x):
        """Basis function

        Args:
            x (1-D array) : data,shape = (N_dim)

        Returns :
            1-D array: data.shape = (N_featuredim)

        """
        a = (x.reshape(-1,1) - self.mu)/self.s
        a = a.ravel()
        phi = np.zeros(a.shape[0])
        phi[a >= 0] = 1/(1 + np.exp(-a[a >= 0]))
        phi[a < 0] = np.exp(a[a < 0])/(np.exp(a[a < 0]) + 1)
        return np.concatenate(([1],phi))

class PolynomialMat(DesignMat):
    """Polynomial Basis Function

    Attributes:
        deg (int) : max degree of polynomial features

    """
    def __init__(self,deg = None):
        super(PolynomialMat,self).__init__()
        if deg is None:
            self.deg = 5
        else:
            self.deg = deg

    def _basis_function(self,x):
        """Basis function

        Args:
            x (1-D array) : data,shape = (N_dim)

        Returns :
            1-D array: data.shape = (N_featuredim)

        """
        phi = [np.array([1])]
        for n in range(1,self.deg+1):
            deg_n_feature = np.power(x,n)
            phi.append(deg_n_feature)
        return np.concatenate(phi)


import numpy as np

def selectThreshold(yval, pval):
    """
    finds the best
    threshold to use for selecting outliers based on the results from a
    validation set (pval) and the ground truth (yval).
    """

    bestEpsilon = 0
    bestF1 = 0
    F1 = 0

    for eps in np.linspace(min(pval), max(pval), 1001, dtype='float'):

        # ====================== YOUR CODE HERE ======================
        # Instructions: Compute the F1 score of choosing epsilon as the
        #               threshold and place the value in F1. The code at the
        #               end of the loop will compare the F1 score for this
        #               choice of epsilon and set it to be the best epsilon if
        #               it is better than the current choice of epsilon.
        #
        # Note: You can use predictions = (pval < eps) to get a binary vector
        #       of 0's and 1's of the outlier predictions

        # If an example x has a low probability p(x) < ε,
        # then it is considered to be an anomaly.
        pred = pval < eps

        # tp is the number of true positives
        # : an anomaly and our algorithm classified it as an anomaly.
        tp = np.sum(np.logical_and(yval == 1, pred == 1))

        # fp is the number of false positives
        # : not an anomaly, but our algorithm classified it as an anomaly.
        fp = np.sum(np.logical_and(yval == 0, pred == 1))

        # fn is the number of false negatives
        # : an anomaly, but our algorithm classified it as not being anomalous.
        fn = np.sum(np.logical_and(yval == 1, pred == 0))

        prec = tp / (tp + fp)
        rec = tp / (tp + fn)
        F1 = (2 * prec * rec) / (prec + rec)

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

        if F1 > bestF1:
            bestF1 = F1
            bestEpsilon = eps

    return bestEpsilon, bestF1


from methods import Newton_raphson_method
from sympy import *
#
# x = Symbol('x')
# function_formula = exp(-x) - x
#
# call_func = Newton_raphson_method.NewtonRaphson(function_formula, 0.0, 0, 0)
#
# # bool1 = call_func.verify_there_is_a_root()
# # print(bool(bool1))
#
# root = call_func.compute_root()
# print(root)
#
# call_func.plot_function()
# call_func.plot_derivative_function()

# ----------------------------------------------------- test 2 --------------------------------------------------

x = Symbol('x')
function_formula = x ** 3 - 0.165 * (x ** 2) + 3.993 * (10 ** (-4))

call_func = Newton_raphson_method.NewtonRaphson(function_formula, 0.05, 0, 0)

# bool1 = call_func.verify_there_is_a_root()
# print(bool(bool1))

root = call_func.compute_root()
print(root)

call_func.plot_function()
call_func.plot_derivative_function()


import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")

#-------------------------------------------------------------------------------
# Helper functions

# Derive the function f and evaluate it in t
def differentiate(f, t, dt=0.01):
    f_prime = (f(t + dt) - f(t))/dt
    return f_prime

# Integrate the function f and evaluate it in t
def integrate(f_prime, t0, t, f0, dt=0.01):
    f = f0
    _ = np.linspace(t0, t, int((t - t0)/dt) )
    for i in _:
        f += f_prime(i) * dt
    return f

# Vectorize differentiate
differentiate = np.vectorize(differentiate)
# Vectorize Integrate
integrate = np.vectorize(integrate)

################################################################################
#                          Quick working example
################################################################################

# Interval
#t = np.linspace(0.01, 10, 1000)

# Function
#def f(t):
#    return t**2

# Evaluate the derivative of f on the interval t
#plt.plot(t, differentiate(f = f, t = t))
# Evaluate the integral of f on the interval t
#plt.plot(t, integrate(f_prime = f, t0 = 0, t = t, f0 = 0))
#plt.show()

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

M = 80      # kg Mass of the diver
RHO = 1.23  # kg/m^3 Air density
A = 0.53    # m^2 Area facing the stream of air
C = 1.05    # Adimensional drag coefficient
g = 9.81    # m/s^2
T_MAX = 20  # s Maximum time window
t = np.linspace(0.01, T_MAX, 1000)

# Position function from Wolfram Alpha
def position(t):
    y = (M / (0.5*RHO*A*C) ) * np.log( np.cosh( (np.sqrt(0.5*RHO*A*C)*np.sqrt(g) * t)/np.sqrt(M) ) )
    return y

# Calculate speed
speed = differentiate(f = position, t = t)

# Calculate acceleration
acceleration = g - (0.5*RHO*A*C / M)*speed**2

# Plot
plt.plot(t, position(t), label = 'Position m')
plt.plot(t, speed, label = 'Speed m/s')
plt.plot(t, acceleration, label = 'Acceleration m/s^2', color = 'green')
plt.xlim([t[0], t[-1]])
plt.xlabel("Time (seconds)")
plt.legend()

plt.show()

print("Vmax after %s seconds: %0.02f m/s (%0.02f km/h)" %(t[-1], speed[-1], speed[-1]*3.6))

################################################################################
#                               OUTPUT
################################################################################
#
# Vmax after 20.0 seconds: 47.86 m/s (172.29 km/h)
#


from __future__ import division
import numpy as np

def spherical_to_cartesian(ra, dec, r):
    """
    http://mathworld.wolfram.com/SphericalCoordinates.html
    ra - azimuthal
    dec - pi/2-polar
    r - distance
    """
    x = r*np.cos(ra)*np.cos(dec)
    y = r*np.sin(ra)*np.cos(dec)
    z = r*np.sin(dec)
    return (x,y,z)

def cartesian_to_spherical(x, y, z):
    r = np.sqrt(x**2+y**2+z**2)
    ra = np.arctan2(y, x)
    if r != 0.0:
       dec = np.arcsin(z/r)
    else:
       dec = 0.*np.sign(z)
    return (ra, dec, r)


import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt
import scipy.optimize

# Analytical Vasicek bond price
def A(t1, t2, alpha):
    return (1-np.exp(-alpha*(t2-t1)))/alpha

def D(t1, t2, alpha, b, sigma):
    val1 = (t2-t1-A(t1, t2, alpha))*(sigma**2/(2*alpha**2)-b)
    val2 = sigma**2*A(t1, t2, alpha)**2/(4*alpha)
    return val1-val2

def bond_price_fun(r, t, T, alpha, b, sigma):
    return np.exp(-A(t, T, alpha)*r+D(t, T, alpha, b, sigma))

def F(x):
    alpha = x[0]
    b = x[1]
    sigma = x[2]
    return sum(np.abs(bond_price_fun(r0,0,years,alpha,b,sigma)-bond_prices))

years = np.linspace(1,10,10)
yield_curve = (years)**(1/5)/75 + 0.04
bond_prices = np.exp(-yield_curve*years)
# bond_prices=np.exp(-((1+6*years)*np.log(1+years))/40)  # TODO: use ZCB market prices for different maturities
r0 = 0.05

# Minimizing F
bnds = ((0,2),(0,0.5),(0,0.2))
opt_val = scipy.optimize.fmin_slsqp(F,(0.3,0.05,0.03),bounds=bnds)
opt_alpha = opt_val[0]
opt_b = opt_val[1]
opt_sig = opt_val[2]

# Calculating model prices and yield
model_prices = bond_price_fun(r0,0,years,opt_alpha,opt_b,opt_sig)


# a. Import Packages
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from ipywidgets import interact, fixed

# b. Defining functions
def u(c, alpha):
    """ Defines the CRRA utility function (Constant-Relative Risk Aversion)
    """
    return (1/alpha)* c**(1-alpha)

def U(c1, c2, alpha, delta):
    """ Defines the households life-time utility function
    """
    return u(c1, alpha) + delta*u(c2, alpha)

def bud_cons(c1, r, e1, e2):
    """ Defines the budget constraint

        e = e1 + e2/(1+r) is total endowment
    """
    e = e1 + e2/(1+r)
    return e*(1+r) - c1*(1+r)

def indif_curve(c1, ubar, alpha, delta):
    """ Defines the indifference curve
    """
    return  ( ((1-alpha)/delta)*(ubar - u(c1, alpha)) )**(1/(1-alpha))

def solve_opt(r, alpha, delta, e1, e2):
    """ This function solves the model by defining the first order condition.
        Returning consumption in both periods as well as the life-time utility
    """
    e = e1 + e2/(1+r)
    A = (delta*(1+r))**(1/alpha)
    c1 = e/(1+A/(1+r))
    c2 = c1*A
    u = U(c1, c2, alpha, delta)
    return c1, c2, u

# c. Parameter definitions
alpha = 0.5
delta = 1
r = 0
e1, e2 = 80, 20

rmin, rmax = 0, 1
cmax = 150

# d. Defining plotting function
def cons_plot(r, delta, alpha, e1, e2):
    """ This function will create a graph of the intertemporal utility and the budget constraint
    """
    c1 = np.linspace(0.1,cmax,num=100)
    c1e, c2e, uebar = solve_opt(r, alpha, delta, e1, e2)
    idfc = indif_curve(c1, uebar, alpha, delta)
    budg = bud_cons(c1,  r, e1, e2)

    fig, ax = plt.subplots(figsize=(8,8))
    ax.plot(c1, budg, lw=2.5)
    ax.plot(c1, idfc, lw=2.5)
    ax.vlines(c1e,0,c2e, linestyles="dashed")
    ax.hlines(c2e,0,c1e, linestyles="dashed")
    ax.plot(c1e,c2e,'ob')
    ax.vlines(e1,0,e2, linestyles="dashed")
    ax.hlines(e2,0,e1, linestyles="dashed")
    ax.plot(e1,e2,'ob')
    ax.text(e1-7,e2-6,r'$e^*$',fontsize=16)
    ax.set_xlim(0, cmax)
    ax.set_ylim(0, cmax)
    ax.set_xlabel(r'$c_1$', fontsize=16)
    ax.set_ylabel('$c_2$', fontsize=16)
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.fill_between(c1, bud_cons(c1, r, e1, e2), alpha = 0.2)
    ax.grid()
    plt.show()

# e. Define function that returns allocation as a pandas frame and graph
def allocation():
    c1e, c2e, uebar = solve_opt(r, alpha, delta, e1, e2)

    lst = {"Consumption": [c1e,c2e],
           "Endowment": [e1,e2],
           "Saving": [e1-c1e,0]}
    """ Creating a list with Consumption, Endowment and Saving as variables
        with their respective values
    """

    df = pd.DataFrame(lst,columns= ["Consumption", "Endowment", "Saving"])
    df.index = ["Period 1", "Period2"]
    """ The code above creates our Pandas Dataframe which takes lst as input and applies the variables
        as column names
    """

    return df

# f.1 Allocation in the saving case
allocation()
cons_plot(r, delta, alpha, e1, e2)

# f.2 Allocation in the borrowing case
e1,e2 = 20,80
allocation()
cons_plot(r, delta, alpha, e1, e2)

# g. interactive graph - can ONLY be used in the notebook
interact(cons_plot, r=(rmin,rmax,0.1), alpha=fixed(alpha), delta=(0.5,1,0.1), e1=(10,100,1), e2=(10,100,1));

modelproject/Group DYE Code - Modelproject final.py


from __future__ import division
from math import pi, cos, sin, atan2, acos
import numpy as np
import math
from collections import namedtuple

EPSILON = 1e-3

position = namedtuple('position', ['x', 'y'])
jointangle = namedtuple('jointangle', ['theta1', 'theta2'])
makeVector = namedtuple('makeVector', ['x', 'y'])

class Arm(object):
        #q0 - initial positions of joints
        #origin - position of the base of the arm in carthesian space
    def __init__(self, link1=1.0, link2=1.0, q0=jointangle(0,0), origin=makeVector(0,0)):
        self.link1 = link1
        self.link2 = link2
        self.lsq = link1 ** 2 + link2 ** 2
        self.joints = q0
        self.origin = origin
        self.end_effector = self.compute_end_effector()

    def forward_kinematics(self, input_joints):
        self.joints = input_joints
        self.end_effector = self.compute_end_effector()
        return self.end_effector

    def compute_end_effector(self):
	#the return of the function is the position of end_effector, start with self.joints.theta1 and self.joints.theta2
        ################################ Computes end_effector position knowing joint angles, your code goes between ##############################
        Ang1 = self.joints.theta1   # store angle 1 of the first link
        Ang2 = self.joints.theta2   # store angle 2 of the second link

        x = self.link1*math.cos(Ang1) + self.link2*math.cos(Ang1 + Ang2) + self.origin.x    # calculate x position of end effector with trigonometry of links
        y = self.link1*math.sin(Ang1) + self.link2*math.sin(Ang1 + Ang2) + self.origin.y    # calculate y position of end effector with trigonometry of links
		###########################################################################################################################################
        return position(x, y)

    def inverse_kinematics(self, input_ee):
	############################### check if the end effector position is reachable, your code goes below #####################################
	#in your code, please include
	#raise ValueError('your words')
	#so that your code can pass the test case
        if(input_ee.x == 0 and input_ee.y == 0):    # check if end effector out of range
            raise ValueError('Out of Range')        # send ValueError to pass last test case

        self.end_effector = input_ee
        self.joints = self.compute_joints()
        return self.joints

    def compute_joints(self):
	#the return of the function are angles of joints, which should stay between -pi and pi. Start with self.end_effector.x and self.end_effector.y.
        ################################# Computes joint angle knowing end effector position, your code goes below #################################

        x = self.end_effector.x - self.origin.x # store x position of end effector
        y = self.end_effector.y - self.origin.y # store y position of end effector

        # check if end effector is within possible range
        if(x != 0 or y != 0):
            L2 = self.link2 # length a for law of cosines
            L1 = self.link1 # length c for law of cosines
            b = math.sqrt(x**2 + y**2) # length b for law of cosines; hypotenuse of tryangle formed by links

            # use law of cosines to find alpha and beta
            alpha = math.acos((-L2**2 + b**2 + L1**2)/(2*b*L1)) # alpha angle
            beta = math.acos((L2**2 -b**2 + L1**2)/(2*L2*L1))   # beta angle
            gamma = math.atan2(y,x) # gamma angle

            # determine theta1 and theta2 based on angles found using law of cosines
            theta1 = gamma - alpha
            theta2 = pi - beta
        else:
            # output if out of bounds for default
            theta1 = 0
            theta2 = 0

        return jointangle(theta1, theta2)


from sympy import summation, Symbol, simplify
from sympy import factorial
from sympy import binomial as nC

n = Symbol("n", integer=True, nonnegative=True)
i = Symbol("i")
print(summation(nC(n, i), (i, 0, n)))

n = Symbol("n", integer=True, positive=True)
i = Symbol("i")
print(simplify(summation(i*nC(n, i), (i, 1, n))))

k = Symbol("k", integer=True, positive=True)
n = Symbol("n", integer=True, positive=True)
i = Symbol("i")
print(simplify(summation(nC(i, k), (i, 0, n - 1))))

n = Symbol("n", integer=True, nonnegative=True)
i = Symbol("i")
print(summation(i*factorial(i), (i, 0, n)))

m = Symbol("m", integer=True, nonnegative=True)
n = Symbol("n", integer=True, nonnegative=True)
i = Symbol("i")
print(simplify(summation(nC(m+i-1, i), (i, 0, n))))

m = Symbol("m", integer=True, nonnegative=True)
n = Symbol("n", integer=True, nonnegative=True)
i = Symbol("i")
print(simplify(summation(m**i, (i, 0, n))))

n = Symbol("n", integer=True, nonnegative=True)
i = Symbol("i")
print(simplify(summation(i, (i, 0, n))))

n = Symbol("n", integer=True, nonnegative=True)
i = Symbol("i")
print(simplify(summation(i*2**i, (i, 0, n-1))))


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

class KNN(object):

    def fit(self, x, y, k=3):
        self.x = x
        self.y = y
        self.k = k

    def classify(self, point):

        nearPosition = []
        nearClass = []
        amountNearClass = []

        dist = self.calculateEuclidianDistance(self.x, point)
        distOrdered = sorted(dist)

        for i in range(self.k):
            for j in range(len(dist)):
                if distOrdered[i] == dist[j]:
                    nearPosition.append(j)
                    dist[j] = -1

        for i in range(0, self.k):
            nearClass.append(self.y[nearPosition[i]])

        unique, counts = np.unique(nearClass, return_counts=True)
        countClass = dict(zip(unique, counts))

        max =  -10
        pointClass = -1
        for key, number in countClass.items():
            if max < number:
                max = number
                pointClass = key
        return pointClass

    def calculateEuclidianDistance(self,x, point):
        dist = np.array(x) - np.array(point)
        dist = dist**2
        dist = np.array(list(map(lambda x: sqrt(sum(x)), dist)))

        return dist

x = [[1, 2, 4],
     [2, 1, 4],
     [1, 1, 4],
     [2, 2, 4],
     [4, 4, 4],
     [5, 4, 4],
     [3, 5, 4],
     [5, 6, 4]]
y = [0, 0, 0, 0, 1, 1, 1, 1]

knn = KNN()
knn.fit(x, y)
result = knn.classify([4, 4, 4])