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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

z_end_script()


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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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


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

def PI(t):

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

def pi(t):

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

  return ab

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

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

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

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

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

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

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

    return s

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

    '''
    Return interpolated function value at x

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

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

    return _interpolate(x, a, b, c)

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

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

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

  s = 0

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

  return s

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

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

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


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

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

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

def main():

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

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

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

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

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

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

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

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

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

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

if __name__ == '__main__':
    main()


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

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

num_passos_f3 = 0
num_passos_f4 = 0

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

### Funcao pra achar a raiz ###

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

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

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

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

### Intervalo e chute inicial ###

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

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

ordem = 5
E = 10**-ordem

### Implementação do metodo

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

        _x = funcao(_x)
        iteracoes += 1

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

    return [_x, iteracoes]

### Obtem os resultados

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

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

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

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

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


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

# library with theano PDF functions

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

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

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

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


import numba
import numpy as np

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

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

        return distances

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

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

        return subtractions


import numpy as np
import scipy.special

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

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

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

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

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

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

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

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

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


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

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

# %%
import numpy as np
import pandas as pd

# %% [markdown]
# # Example 1

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

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

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

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

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

# %%
pd1

# %%
pd2

# %%
pd3

# %% [markdown]
# # Example 2

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

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

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

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

# %%
weights = cp.Variable(n)

# %%
rets = mu.T @ weights

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

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

# %%
minimize_variance.solve()

# %%
rets.value

# %%
weights.value

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

# %%
maximize_returns.solve()

# %%
portfolio_variance.value

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

# %%
maximize_returns_2.solve()

# %%
portfolio_variance.value

# %% [markdown]
# # More examples

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# Visualizing the Results

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

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

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

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


"""

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

How the project work:

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

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

 = 'k', zorder = 0)

plt.gca().add_patch(shape)

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

"""

import numpy as np

import sympy

from sympy.abc import x, y

from matplotlib import pyplot as plt

import matplotlib.patches as patches

plt.style.use("dark_background")

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

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

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

    theta = sympy.atan2(y, x)

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

def velocity_field(psi):

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

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

    return u, v

U_func, V_func = velocity_field(cylinder_stream_function() )

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

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

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

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

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

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

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

    = 'k', zorder = 0)

plt.gca().add_patch(shape)

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

plt.xticks([])

plt.yticks([])

plt.grid(False)

plt.box(False)

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

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

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

plt.show()

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


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


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

from numpy import array, sqrt

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

    plt.grid(True)

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

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

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

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

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

    return plt.show()

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

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

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

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

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

    Returns:
    Nullclines for physical and human capital
    """

    # a. grids

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

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

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

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

        h_vec_DeltaH0[i] = result.root

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

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

        h_vec_DeltaK0[i] = result.root

    return k_vec, h_vec_DeltaK0, h_vec_DeltaH0


import numpy as np
import matplotlib.pyplot as plt

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

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

plt.plot(xdata,ydata)

plt.show()


import numpy as np

# Compute every step manually

# Linear regression
# f = w * x

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

w = 0.0

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

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

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

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

# Training
learning_rate = 0.01
n_iters = 20

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

    # loss
    l = loss(Y, y_pred)

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

    # update weights
    w -= learning_rate * dw

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

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


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

import config

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

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

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

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

	return pdfMap

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

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

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

	return np.array([x,y])

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

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

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

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


import numpy as np

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

    Args :
       x : x values
       y : y values

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

    """

    n = len(x)   # number of galaxies

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

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

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

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

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


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

"""

import numpy as np

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

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

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

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

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

print("U_Transpose . U :", U_Transpose__U)

print("U_Transpose__U Inverse: ", U_Transpose__U__Inv)

print("U_Transpose__U__Inv . U_Transpose: ", U_Transpose__U__Inv__U_Transpose)

print("Projection", Projection)

Matrix and Vector Operations/ProjectionCalculation.py


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

import numpy as np
import matplotlib.pyplot as plt

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

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

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

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

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

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

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

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


import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

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

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

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

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

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

    return dw1, dw2, db

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

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

    e = error(y_vector, y_hat_vector)

    return w_vector, b_scalar, e

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    plt.show()

if __name__ == "__main__":
    __main__()


import numpy as np

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


"""
Fourier Transform Utilities

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

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

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

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

    return fft_data

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

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

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

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

    return dft_data


import numpy as np

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

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

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

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


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

# cluster input (below)

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

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

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

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

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

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

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

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

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

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

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


import numpy as np

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

    n = x.size
    y_int = 0

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

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

    return [y_int]

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

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

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

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

    return [y_int, q]


import numpy as np
from scipy.linalg import inv, det

def multivariate_kl(m1, s1, m2, s2):
    """
    Multivariate KL-divergence measure for high-dimensional gaussians. For
    use with the parameters, rather than evaluating the empirical distributions.
    See derivation:
    https://stats.stackexchange.com/questions/234757/how-to-use-kullback-leibler-divergence-if-mean-and-standard-deviation-of-of-two

    For the sake of notation: N(m1, s1) ~ p, and N(m2, s2) ~ q

    Parameters
    ----------
    m1, m2 : array, shape (d,)
        Means of the two distributions.
    s1, s2 : array, shape (d, d)
        Covariance matrices of the two distributions.

    Returns
    -------
    kl : float
        KL-divergence.
    """
    s2inv = inv(s2)
    deltamu = m2 - m1
    a = np.log(det(s2) / det(s1))
    b = np.trace(s2inv @ s1)
    c = deltamu.dot(s2inv).dot(deltamu)
    d = - m1.shape[0]
    return 0.5 * (a + b + c + d)

def multivariate_js(m1, s1, m2, s2):
    """
    Jensen-Shannon divergence measure for high-dimensional gaussians. For
    use with analytic parameters, rather than evaluating empirical distributions.
    See formulation: https://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence

    For the sake of notation: N(m1, s1) ~ p, and N(m2, s2) ~ q

    Parameters
    ----------
    m1, m2 : array, shape (d,)
        Means of the two distributions.
    s1, s2 : array, shape (d, d)
        Covariance matrices of the two distributions.

    Returns
    -------
    js : float
        JS-divergence.
    """
    pq = multivariate_kl(m1, s1, m2, s2)
    qp = multivariate_kl(m2, s2, m1, s1)
    return 0.5 * (pq + qp)

def multivariate_hellinger(u1, cov1, u2, cov2, gamma=0.00125):
    """
    Hellinger distance measure for high-dimensional gaussians. For
    use with analytic parameters, rather than evaluating empirical distributions.

    For the sake of notation: N(m1, s1) ~ p, and N(m2, s2) ~ q

    Parameters
    ----------
    u1, u2 : array, shape (d,)
        Means of the two distributions.
    cov1, cov2 : array, shape (d, d)
        Covariance matrices of the two distributions.
    gamma: float
        Probability measure

    Returns
    -------
    hellinger : float
            Hellinger distance.
    """

    mcov = 0.5 * cov1 + 0.5 * cov2
    dets = np.sqrt(np.sqrt(np.linalg.det(cov1)) * np.sqrt(
        np.linalg.det(cov2)) / np.linalg.det(mcov))
    mahala = (u1 - u2).dot(np.linalg.pinv(mcov)).dot(u1 - u2)
    h = np.exp(-gamma * mahala) * dets
    return 1 - np.sqrt(1 - h)


#!/usr/bin/env python3

from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets, linear_model

# heigh(cm), input data, each row is a data point
X = np.array([[147, 150, 153, 158, 163, 165, 168, 170, 173, 175, 178, 180, 183]]).T

# weight(kg)
y = np.array([49, 50, 51, 54, 58, 59, 60, 62, 63, 64, 66, 67, 68])

# Building Xbar
one = np.ones((X.shape[0], 1))
Xbar = np.concatenate((one, X), axis=1) # each point is one row

# Calculating weights of the fitting line
A = np.dot(Xbar.T, Xbar)
b = np.dot(Xbar.T, y)
w = np.dot(np.linalg.pinv(A), b)

# weights
w_0, w_1 = w[0], w[1]

# y1 = w_1 * 155 + w_0
# y2 = w_1 * 160 + w_0
#
# print('Input 155cm, true output 52kg, predicted output %.2fkg' %(y1))
# print('Input 160cm, true output 56kg, predicted output %.2fkg' %(y2))

# fit the model by Linear Regression
regr = linear_model.LinearRegression()
regr.fit(X, y)

# Compare two results
print('scikit-learn solution : w_1 = ', regr.coef_[0], 'w_0 = ', regr.intercept_)
print('our solution          : w_1 = ', w[1], 'w_0 = ', w[0])


"""
===============================
Minima and roots of a function
===============================

Demos finding minima and roots of a function.
"""

############################################################
# Define the function
############################################################

import numpy as np

x = np.arange(-10, 10, 0.1)
def f(x):
    return x**2 + 10*np.sin(x)

############################################################
# Find minima
############################################################

from scipy import optimize

# Global optimization
grid = (-10, 10, 0.1)
xmin_global = optimize.brute(f, (grid, ))
print("Global minima found %s" % xmin_global)

# Constrain optimization
xmin_local = optimize.fminbound(f, 0, 10)
print("Local minimum found %s" % xmin_local)

############################################################
# Root finding
############################################################

root = optimize.root(f, 1)  # our initial guess is 1
print("First root found %s" % root.x)
root2 = optimize.root(f, -2.5)
print("Second root found %s" % root2.x)

############################################################
# Plot function, minima, and roots
############################################################

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(111)

# Plot the function
ax.plot(x, f(x), 'b-', label="f(x)")

# Plot the minima
xmins = np.array([xmin_global[0], xmin_local])
ax.plot(xmins, f(xmins), 'go', label="Minima")

# Plot the roots
roots = np.array([root.x, root2.x])
ax.plot(roots, f(roots), 'kv', label="Roots")

# Decorate the figure
ax.legend(loc='best')
ax.set_xlabel('x')
ax.set_ylabel('f(x)')
ax.axhline(0, color='gray')
plt.show()


"""Numpy version of euclidean distance, etc.
Notice the input/output shape of methods, so that you can better understand
the meaning of these methods."""
import numpy as np

def normalize(nparray, order=2, axis=0):
  """Normalize a N-D numpy array along the specified axis."""
  norm = np.linalg.norm(nparray, ord=order, axis=axis, keepdims=True)
  return nparray / (norm + np.finfo(np.float32).eps)

def compute_dist(array1, array2, type='euclidean'):
  """Compute the euclidean or cosine distance of all pairs.
  Args:
    array1: numpy array with shape [m1, n]
    array2: numpy array with shape [m2, n]
    type: one of ['cosine', 'euclidean']
  Returns:
    numpy array with shape [m1, m2]
  """
  assert type in ['cosine', 'euclidean']
  if type == 'cosine':
    array1 = normalize(array1, axis=1)
    array2 = normalize(array2, axis=1)
    dist = np.matmul(array1, array2.T)
    return dist
  else:
    # shape [m1, 1]
    square1 = np.sum(np.square(array1), axis=1)[..., np.newaxis]
    # shape [1, m2]
    square2 = np.sum(np.square(array2), axis=1)[np.newaxis, ...]
    squared_dist = - 2 * np.matmul(array1, array2.T) + square1 + square2
    squared_dist[squared_dist < 0] = 0
    dist = np.sqrt(squared_dist)
    return dist


__all__ = ['sigmoid', 'minimum_jerk']

import numpy as np

def sigmoid(x, A=0, K=1, C=1, Q=1, B=1, v=1, x_offset=0):
    """
    Returns f(x) = A + (K - A) / ((C + Q * np.exp(-B*(x - x_offset)))**(1/v))
    This is a generalized logistic function.
    The default arguments reduce this to a simple sigmoid:
        f(x) = 1 / (1 + np.exp(-x))
    Args:
        x (np.ndarray):
        A (float):
        K (float):
        C (float):
        Q (float):
        B (float):
        v (float):
        x_offset:

    Returns:
        np.ndarray: A + (K - A) / ((C + Q * np.exp(-B*(x - x_offset)))**(1/v))
    """
    y = A + (K - A) / ((C + Q * np.exp(-B*(x - x_offset)))**(1/v))
    return y

def minimum_jerk(x: np.ndarray, a0=0, af=1, degree=1, duration=None) -> np.ndarray:
    r"""
    A 1-D trajectory that minimizes jerk (3rd time-derivative of position).
    A minimum jerk trajectory is considered "smooth" and to be a feature of natural limb movements.
    https://storage.googleapis.com/wzukusers/user-31382847/documents/5a7253343814f4Iv6Hnt/minimumjerk.pdf

    A minimum-jerk trajectory is defined by:
    ```math
    trajectory = a0 + (af - a0) * (10(dx^3) - 15(dx^4) + 6(dx^5))
    ```
    where a0 is the resting position, and af is the finishing position.

    duration is assumed to be the extent of x but it may be overidden.
    Args:
        x:
        a0:
        af:
        degree:
        duration (float): Total duration of x in seconds. If None (default), calculated duration from x.

    Returns:
        a0 + (af - a0) * (10*(dx.^3) - 15*(dx.^4) + 6*(dx.^5))
    """
    x = np.array(x)[:, None]
    assert(x.size == x.shape[0]), f"x must be 1D trajectory, found {x.shape}"

    a0 = np.atleast_2d(a0)
    af = np.atleast_2d(af)

    if duration is None:

        sorted_x = np.sort(x, axis=0)
        dx = np.diff(x, axis=0)
        duration = sorted_x[-1] - sorted_x[0] + np.min(dx)

    dx = x / duration

    if degree not in [1, 2]:
        # Default - no derivative
        k0 = a0
        x1 = 10 * dx**3
        x2 = -15 * dx**4
        x3 = 6 * dx**5
    elif degree == 1:
        # Velocity
        k0 = np.zeros_like(a0)
        x1 = 30 * dx**2
        x2 = -60 * dx**3
        x3 = 30 * dx**4
    elif degree == 2:
        # Acceleration
        k0 = np.zeros_like(a0)
        x1 = 60 * dx
        x2 = -180 * dx**2
        x3 = 120 * dx**3

    return k0 + (af - a0) * (x1 + x2 + x3)


import numpy as np

class Quaternion:

    """
    returns Q matrix of a quaternion. An anti-symmetric orthogonal
    matrix that helps with quaternion multiplication.
    See http://www.diva-portal.org/smash/get/diva2:1010947/FULLTEXT01.pdf
    """
    @staticmethod
    def Q_Matrix(q):
        q0 = q[0]
        q1 = q[1]
        q2 = q[2]
        q3 = q[3]
        return np.array([  [q0, -q1, -q2, -q3],
                           [q1, q0, -q3, q2],
                           [q2, q3, q0, -q1],
                           [q3, -q2, q1, q0]])
    """
    Returns Quaternion : the conjugate of the quaternion in question
    """
    @staticmethod
    def conjugate(q):
        q0 = q[0]
        q1 = q[1]
        q2 = q[2]
        q3 = q[3]
        return np.array([q0,-q1,-q2,-q3])

    """
    Returns rotation matrix of quaternion.
    """
    @staticmethod
    def rotationMatrix(q):
        q0 = q[0]
        q1 = q[1]
        q2 = q[2]
        q3 = q[3]
        R = np.zeros([3,3])

        R[0,0] = q0**2 + q1**2 - q2**2 - q3**2
        R[1,0] = 2*(q1*q2 + q0*q3)
        R[2,0] = 2*(q1*q3 - q0*q2)

        R[0,1] = 2*(q1*q2 - q0*q3)
        R[1,1] = q0**2 - q1**2 + q2**2 - q3**2
        R[2,1] = 2*(q2*q3 + q0*q1)

        R[0,2] = 2*(q1*q3 + q0*q2)
        R[1,2] = 2*(q2*q3 - q0*q1)
        R[2,2] = q0**2 - q1**2 - q2**2 + q3**2
        return R
    """
    q : List_like object that contains 4 elements e.g. ndarray, list, or tupple
    """
    def __init__(self, q):
        if len(q) != 4:
            raise Exception("Quaternion must have 4 real elements")
        self.q = np.array([q[0],q[1],q[2],q[3]])
        self.C = Quaternion.conjugate(self.q)
        self.Q = Quaternion.Q_Matrix(self.q)
        self.R = Quaternion.rotationMatrix(self.q)

    """
    Returns Length of the quaternion. Must always be 4.
    """
    def __len__(self):
        return len(self.q)
    def __str__(self) -> str:
        return "Quaternion(["+str(self[0])+","+str(self[1])+","+str(self[2]) +","+ str(self[3])+"])"
    def __repr__(self):
        return "Quaternion(["+str(self[0])+","+str(self[1])+","+str(self[2]) +","+ str(self[3])+"])"
    def __getitem__(self,key):
        return self.q[key]

    def __mul__(self,other):
        if not isinstance(other, (float, int)):
            raise TypeError("Right operand should be a numerical scalar value. It can't be " + str(type(other)))
        return Quaternion(other * self.q)
    def __rmul__(self,other):
        if not isinstance(other, (float, int)):
            raise TypeError("Left operand should be a numerical scalar value. It can't be " + str(type(other)))
        return Quaternion(other * self.q)

    """
    Quaternion multiplication. Plain and simple.
    """
    def __matmul__(self,other):
        if len(other) != 4:
            raise Exception("Right operand quaternion must have 4 elements")
        result = np.dot(self.Q, other.q)
        return Quaternion(result)
    """
    Returns the norm of the quaternion.
    """
    def norm(self):
        return np.linalg.norm(self.q)

    """
    Returns unit quaternion of the current quaternion.
    """
    def normalize(self):
        return Quaternion(self.q/self.norm())

    """
    Returns the inverse of the quaternion. q*q_inv = [1,0,0,0] (unit quaternion)
    """
    def inv(self):
        return Quaternion(self.C / (self.norm() ** 2))

    """
    Rotates vector, or any point in affine space, and expressing its image in a fixed reference frame

    Input v: an array-like containing 3 elements representing the vector expressed in a fixed frame.
    Returns: rotation of v in the fixed frame by the used quaternion.
    """
    def rotate(self,v):
        if len(v) != 3:
            raise ValueError("Vector v must have (3) elements. But it in reality, it had : ("+ str(len(v))+  ") elements")

        return self.R @ v
    """
    Transforms vector v to another reference frame, represented by quaternion q.
    """
    def transform(self,v):
        if len(v) != 3:
            raise ValueError("Vector v must have (3) elements. But it in reality, it had : ("+ str(len(v))+  ") elements")

        return self.R.T @ v


'''
Created on 2020-03-30 11:12:12
Last modified on 2020-11-03 15:03:06

@author: L. F. Pereira (lfpereira@fe.up.pt)

Main goal
---------
Define matrix-related functions.

Notes
-----
-Care must be taken with imports to make sure this module can be used inside
Abaqus.
-scipy is not used because it does not exit in Abaqus.
'''

# imports

# third-party
import numpy as np

# TODO: test using abaqus python

# function definition

def symmetricize_vector(vec):
    '''
    Create a symmetric matrix given a vector.

    Parameters
    ----------
    vec : array-like
        Elements ordered by its position in the upper triangular matrix.
    '''

    # compute dimension
    n = int(1. / 2. * (-1. + np.sqrt(1. + 8. * len(vec))))

    # assign variables to the right positions
    matrix = np.empty((n, n))
    k = 0
    for i in range(n):
        for j in range(i, n):
            matrix[i, j] = vec[k]
            if i != j:
                matrix[j, i] = matrix[i, j]
            k += 1

    return np.array(matrix)

def sqrtm(matrix):
    '''
    Square root of a square symmetric matrix.

    Parameters
    ----------
    matrix : array-like. shape: [n, n]
        Symmetric matrix
    '''

    # get eigenvalues and vectors
    w, v = np.linalg.eig(matrix)

    # define spectral decomposition
    spec_decomp = np.zeros(np.shape(matrix))
    for i in range(np.size(w)):
        spec_decomp[i, i] = w[i]
    spec_decomp_sqrt = np.sqrt(spec_decomp)

    # square root of the original matrix
    matrix_sqrt = np.dot(v, np.dot(spec_decomp_sqrt, v.transpose()))

    return matrix_sqrt


import dezero as dz
from dezero.functions import Square, Exp
import numpy as np

def numerical_diff(f, x, eps=1e-4):
    """p.58에 나오는 수치 미분 함수. 중앙 차분으로 구현

    컴퓨터는 극한을 취급할 수 없어서 극한과 비슷한, 아주 작은 값으로 극한값을 대체하여 미분 연산에 대신 사용.

    수치 미분의 결과에는 자릿수 누락 때문에 오차가 포함되기 쉽다는 문제가 있으며,
    더 큰 문제로는 개산량이 매우 많다는 점이 있음.

    :param eps: epsilon의 약어. 아주 작은 양의 부동소수점값을 담는 변수.
    """
    x0 = dz.Variable(x.data - eps)
    x1 = dz.Variable(x.data + eps)
    y0 = f(x0)
    y1 = f(x1)
    return (y1.data - y0.data) / (2 * eps)

f = Square()
x1 = dz.Variable(np.array(2.0))
dy = numerical_diff(f, x1)
print(dy)

# 예상과 달리 eps가 더 작아지니 오차가 더 커짐

dy_1e6 = numerical_diff(f, x1, eps=1e-6)
print(dy_1e6)

dy_1e9 = numerical_diff(f, x1, eps=1e-9)
print(dy_1e9)

print("--------------------")

def f(x):
    """p.59에 나오는 임의의 연산 함수 """
    A = Square()
    B = Exp()
    C = Square()
    return C(B(A(x)))

x2 = dz.Variable(np.array(0.5))
dy2 = numerical_diff(f, x2)
print(dy2)


import numpy as np
import matplotlib.pyplot as plt

def prior(H):
    """
    Densidad de probabilidad de H
    (H es la probabilidad de sacar cara).
    """
    p = np.ones(len(H))
    return p

def like(secuencia, H):
    """
    Likelihod de sacar una secuencia de caras y sellos, dado H.
    H es la probabilidad de sacar una cara.
    """
    L = np.ones(len(H))
    n_caras = secuencia.count('c')
    n_sello = secuencia.count('s')
    L *= H**n_caras
    L *= (1-H)**n_sello
    return L

def posterior(H, secuencia):
    """
    Posterior calculado con la normalizacion adecuada
    """
    post =  like(secuencia, H) * prior(H)
    evidencia = np.trapz(post, H)
    return  post/evidencia

def maximo_sigma(x, y):
    deltax = x[1] - x[0]

    # maximo de y
    ii = np.argmax(y)

    # segunda derivada
    d = (y[ii+1] - 2*y[ii] + y[ii-1]) / (deltax**2)

    return x[ii], 1.0/np.sqrt(-d)

secuencia = 'scccc'
H = np.linspace(1E-4, 1.0-1.0E-4, 100)
post = posterior(H, secuencia)

max, sigma = maximo_sigma(H, np.log(post))

gauss = (1.0/np.sqrt(2.0*np.pi*sigma**2))*np.exp(-0.5*(H-max)**2/(sigma**2))

plt.figure()
plt.plot(H, post, label='datos={}'.format(secuencia))
plt.plot(H, gauss, ':', label='Aproximacion Gaussiana')
plt.title('H= {:.2f} $\pm$ {:.2f}'.format(max, sigma))
plt.xlabel('H')
plt.ylabel('prob(H|datos)')
plt.legend()
plt.savefig('coins')


# Cosine annealing learning rate schedule
# https://machinelearningmastery.com/snapshot-ensemble-deep-learning-neural-network/

#from matplotlib import pyplot
from math import pi
from math import cos
from math import floor

import numpy as np
import matplotlib.pyplot as plt
import os

figdir = "../figures"
def save_fig(fname):
    if figdir: plt.savefig(os.path.join(figdir, fname))

# cosine annealing learning rate schedule
def cosine_annealing(epoch, n_epochs, n_cycles, lrate_max):
	epochs_per_cycle = floor(n_epochs/n_cycles)
	cos_inner = (pi * (epoch % epochs_per_cycle)) / (epochs_per_cycle)
	return lrate_max/2 * (cos(cos_inner) + 1)

# create learning rate series
n_epochs = 100
n_cycles = 5
lrate_max = 0.01
series = [cosine_annealing(i, n_epochs, n_cycles, lrate_max) for i in range(n_epochs)]
# plot series
plt.figure()
plt.plot(series)
fname = 'lrschedule_cosine_annealing.pdf'
save_fig(fname)
plt.show()


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Wed Jul 26 19:29:06 2017

@author: luke

Levi-Civita in python:
https://stackoverflow.com/questions/20908754/how-to-speed-up-a-vector-cross-product-calculation

getitem with multiple arguments:
    https://stackoverflow.com/questions/1685389/possible-to-use-more-than-one-argument-on-getitem
"""
import numpy as np

def make_Levi_Civita():
    eijk = np.zeros((3, 3, 3))
    eijk[0, 1, 2] = eijk[1, 2, 0] = eijk[2, 0, 1] = 1
    eijk[0, 2, 1] = eijk[2, 1, 0] = eijk[1, 0, 2] = -1
    return eijk

eijk = make_Levi_Civita()

class LeviCivita(object):
    def __init__(self):
        self.lc = eijk
    def __call__(self,i,j,k):
        return self.lc[i,j,k]

    def __getitem__(self, pos):
        i,j,k = pos
        return self.lc[i,j,k]

    def __repr__(self):
        return 'LeviCivita({})'.format(self.lc)

    def __str__(self):
        return 'LeviCivita({})'.format(self.lc)

if __name__ == '__main__':
    self = LeviCivita()


# Exercise 7.29
# Author: Noah Waterfield Price

from Polynomial import Polynomial
from numpy import exp
import sys

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

x = float(sys.argv[1])
Nlist = sys.argv[2:]
plist = []
for N in Nlist:
    coeffs = [1. / factorial(k) for k in xrange(int(N) + 1)]
    plist.append(Polynomial(coeffs))

print '=' * 35
print '%-5s %-16s %-16s' % ('N', 'Taylor approx.', 'Exact')
print '-' * 35
for N, p in zip(Nlist, plist):
    print '%-5s %-16.12s %-16.12s' % (N, p(x), exp(x))
print '-' * 35

"""
Sample run:
python Polynomial_exp.py 0.5 2 5 10 15 25
===================================
N     Taylor approx.   Exact
-----------------------------------
2     1.625            1.6487212707
5     1.6486979166    1.6487212707
10    1.6487212706     1.6487212707
15    1.6487212707     1.6487212707
25    1.6487212707     1.6487212707
-----------------------------------
python Polynomial_exp.py 3 2 5 10 15 25
===================================
N     Taylor approx.   Exact
-----------------------------------
2     8.5              20.085536923
5     18.4             20.085536923
10    20.079665178     20.085536923
15    20.085534431     20.085536923
25    20.085536923     20.085536923
-----------------------------------
python Polynomial_exp.py 10 2 5 10 15 25
===================================
N     Taylor approx.   Exact
-----------------------------------
2     61.0             22026.465794
5     1477.6666666     22026.465794
10    12842.305114     22026.465794
15    20952.886968     22026.465794
25    22026.076360     22026.465794
-----------------------------------
"""


import sys
import numpy as np
from numpy import pi
from numpy import newaxis
rg = np.random.default_rng(1)  # create instance of default random number generator

########################################################################################################################
# Changing the shape of an array
print("\nChanging the shape\n")

"An array has a shape given by the number of elements along each axis:"

a = np.floor(10 * rg.random((3, 4)))  # Create array xx4 of random floats
print(a)
"""array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])"""
print(a.shape)  # (3, 4)

"""The shape of an array can be changed with various commands.
Note that the following three commands all return a modified array, but do not change the original array:"""

print(a.ravel())  # returns the array, flattened
# array([3., 7., 3., 4., 1., 4., 2., 2., 7., 2., 4., 9.])
print(a.reshape(6, 2))  # returns the array with a modified shape
"""array([[3., 7.],
       [3., 4.],
       [1., 4.],
       [2., 2.],
       [7., 2.],
       [4., 9.]])"""
print(a.T)  # returns the array, transposed
"""array([[3., 1., 7.],
       [7., 4., 2.],
       [3., 2., 4.],
       [4., 2., 9.]])"""
print(a.T.shape)  # (4, 3)
print(a.shape)  # (3, 4)

"""The reshape function returns its argument with a modified shape,
whereas the ndarray.resize method modifies the array itself:"""

print(a)  # original array
"""array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])"""
a.resize((2, 6))  # change the array itself
print(a)
"""array([[3., 7., 3., 4., 1., 4.],
       [2., 2., 7., 2., 4., 9.]])"""

"""If a dimension is given as -1 in a reshaping operation, the other dimensions are automatically calculated:"""

a.reshape(3, -1)  # reshape with unknown last parameter -> auto calculate
"""array([[3., 7., 3., 4.],
       [1., 4., 2., 2.],
       [7., 2., 4., 9.]])"""

########################################################################################################################
print("\nStacking together different arrays\n")

"""Several arrays can be stacked together along different axes:"""

a = np.floor(10 * rg.random((2, 2)))  # new array 2x2
print(a)
"""array([[9., 7.],
       [5., 2.]])"""
b = np.floor(10 * rg.random((2, 2)))  # new array 2x2
print(b)
"""array([[1., 9.],
       [5., 1.]])"""
print(np.vstack((a, b)))  # stack vertically
"""array([[9., 7.],
       [5., 2.],
       [1., 9.],
       [5., 1.]])"""
print(np.hstack((a, b)))  # Stack horizontally
"""array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])"""

"""The function column_stack stacks 1D arrays as columns into a 2D array.
It is equivalent to hstack only for 2D arrays:"""

print(np.column_stack((a, b)))  # with 2D arrays
"""array([[9., 7., 1., 9.],
       [5., 2., 5., 1.]])"""
a = np.array([4., 2.])
b = np.array([3., 8.])
print(np.column_stack((a, b)))  # returns a 2D array
"""array([[4., 3.],
       [2., 8.]])"""
print(np.hstack((a, b)))        # the result is different
"""array([4., 2., 3., 8.])"""
print(a[:, newaxis])  # view `a` as a 2D column vector
"""array([[4.],
       [2.]])"""
print(np.column_stack((a[:, newaxis], b[:, newaxis])))  # stacks columns
"""array([[4., 3.],
       [2., 8.]])"""
print(np.hstack((a[:, newaxis], b[:, newaxis])))  # the result is the same
"""array([[4., 3.],
       [2., 8.]])"""

"""On the other hand, the function row_stack is equivalent to vstack for any input arrays.
In fact, row_stack is an alias for vstack:

In general, for arrays with more than two dimensions,
    hstack stacks along their second axes,
    vstack stacks along their first axes,
    and concatenate allows for an optional arguments giving the number of the axis along which the concatenation
    should happen."""

print(np.column_stack is np.hstack)  # False
print(np.row_stack is np.vstack)  # True

"""In complex cases, r_ and c_ are useful for creating arrays by stacking numbers along one axis.
They allow the use of range literals :."""

print(np.r_[1:4, 0, 4])  # create complex array
"""array([1, 2, 3, 0, 4])"""

########################################################################################################################
print("\nSplitting one array into several smaller ones\n")

"""Using hsplit, you can split an array along its horizontal axis,
either by specifying the number of equally shaped arrays to return,
or by specifying the columns after which the division should occur:

vsplit splits along the vertical axis, and array_split allows one to specify along which axis to split.
"""

a = np.floor(10 * rg.random((2, 12)))
print(a)
"""array([[6., 7., 6., 9., 0., 5., 4., 0., 6., 8., 5., 2.],
       [8., 5., 5., 7., 1., 8., 6., 7., 1., 8., 1., 0.]])"""
# Split `a` into 3
print(np.hsplit(a, 3))
"""[array([[6., 7., 6., 9.],
       [8., 5., 5., 7.]]),
 array([[0., 5., 4., 0.],
       [1., 8., 6., 7.]]),
 array([[6., 8., 5., 2.],
       [1., 8., 1., 0.]])]"""
# Split `a` after the third and the fourth column
print(np.hsplit(a, (3, 4)))
"""[array([[6., 7., 6.],
       [8., 5., 5.]]),
 array([[9.],
       [7.]]),
 array([[0., 5., 4., 0., 6., 8., 5., 2.],
       [1., 8., 6., 7., 1., 8., 1., 0.]])]"""


#!/usr/bin/python
"""
author  : Bhekimpilo Ndhlela
author  : 18998712
module  : Applied Mathematics(Numerical Methods) TW324
task    : computer assignment 05 question 6
since   : Friday-27-04-2018
"""

def question_a(f, n, w0, I, G=9.81, C2=1.0/1000.0):
    h = 10.0/float(n) # step size
    w = zeros(n+1, dtype=float)
    w[0] = w0
    for i in xrange(1, n+1):
        w[i] = w[i-1] + h * f(w[i-1])
    return abs_error(w[-1], I)

def explicit_trapezium(f, n, w0, I, G=9.81, C2=1.0/1000.0):
    h = 10.0/float(n) # step size
    w, wt = zeros(n+1, dtype=float), zeros(n+1, dtype=float)
    w[0] = w0
    for i in xrange(1, n+1):
        wt[i] = w[i-1] + h * f(w[i-1])
        w[i]  = w[i-1] + h * f(wt[i-1] + h/2.0 * f(wt[i]))
    return abs_error(w[-1], I)

def explicit_midpoint(f, n, w0, I, G=9.81, C2=1.0/1000.0):
    h = 10.0/float(n) # step size
    w, wt = zeros(n+1, dtype=float), zeros(n+1, dtype=float)
    w[0] = w0
    for i in xrange(1, n+1):
        wt[i] = w[i-1] + 0.5 * h * f(w[i-1])
        w[i]  = w[i-1] + h * f(wt[i])
    return abs_error(w[-1], I)

def debug_on(N, err_eulr,err_mdp, err_trap, debug=False):
    if debug == True:
        print "*" * 125
        print "The Exact Value I = {:.20f}".format(float(I))
        print "*" * 125
        print "N ", "\t\t\t\t","Euler's Method", "\t\t\t\t\t\t" , "Explicit Midpoint", "\t\t\t\t\t","Explicit Trapezium"
        print "*" * 125
        for n, em, emm, etm in zip(N, err_eulr, err_mdp, err_trap):
            temp = ""
            if n == 10:
                temp = "  "
            elif n == 100:
                temp = " "
            print n,"\t\t\t",temp,"{:.20f}".format(em), "\t\t", temp,"{:.20f}".format(emm), "\t\t",temp,"{:.20f}".format(etm)

def abs_error(xc, x):
    return abs(float(xc) - float(x))

def plot_computed_error(N, abs_err_0, abs_err_1=None, abs_err_2=None):
    plt.ylabel("n = number of points")
    plt.xlabel("h = step size)")
    plt.xscale('log'); plt.yscale('log')
    if abs_err_1 == None and abs_err_2 == None:
        plt.title("6(b.) Absolute Error of Euler's method with n = 10, 100 1000")
        plt.plot(N, abs_err_0, 'k-')
        plt.show()
    else:
        plt.title("(6a.) Absolute Error of Euler's, Explicit Midpoint"\
                   "and Explicit Trapezium methods with n = 10, 100 1000")
        plt.plot(N, abs_err_0, 'k-', label="Error Euler's Method")
        plt.plot(N, abs_err_1, 'b-', label="Error Explicit Midpoint Method")
        plt.plot(N, abs_err_2, 'r-', label="Error Explicit Trapezium Method")
        plt.legend(bbox_to_anchor=(1.0, 1.0))
        plt.show()

if __name__ == "__main__":
    from numpy import (linspace, array, zeros)
    from scipy import (integrate, special)
    import matplotlib.pyplot as plt
    from math import (sqrt, tanh)

    f = lambda t : 9.81 - (1.0/1000.0) * (t**2)
    I = sqrt(9.81/(1.0/1000.0)) * tanh(10. * sqrt(9.81*(1.0/1000.0)))
    N = [10, 100, 1000]

    # get absolute error using Euler's meth6od
    abs_err_eulr = [question_a(f, n, 0.0, I) for n in N]
    abs_err_trap = [explicit_trapezium(f, n, 0.0, I) for n in N]
    abs_err_mdp = [explicit_midpoint(f, n, 0.0, I) for n in N]

    # debug or print the acquired values
    debug_on(N, abs_err_eulr, abs_err_mdp, abs_err_trap, debug=True)

    # plot the error for question 6(a.)
    plot_computed_error(N, abs_err_eulr)
    #plot the error for question 6(a.) and 6(b.) on the same cartesion plane
    plot_computed_error(N, abs_err_eulr, abs_err_mdp, abs_err_trap)
else:
    from sys import exit
    exit("USAGE: python question6.py")


"""
Provides utilities specific to the Browian Paths example
"""

from typing import Optional

import numpy as np

def simulate_wiener_process(time_steps: np.ndarray, n_paths: int) -> np.ndarray:
    """
    Simulate `n_paths` paths of the Wiener process at time
    steps as provided in the `time_steps` array

    Returns:
    A np.ndarray of shape (n, len(time_steps)) with each row
    representing a realisation of the path
    """

    time_differences = np.ediff1d(time_steps)
    normal_noise = np.random.normal(size=(n_paths, len(time_differences))) * time_differences
    wiener_paths = np.insert(np.cumsum(normal_noise, axis=1), 0, 0, axis=1)

    return wiener_paths

def simulate_gbm_process(
    time_steps: np.ndarray,
    n_paths: int,
    mu: float,
    sigma: float,
    initial_value: Optional[float] = 1,
) -> np.ndarray:
    """
    Simulate `n_paths` of a Stochastic process driven by the
    Geometric Brownian Motion dynamics with return `mu` and
    volatility `sigma`, at the time steps as provided in the
    `time_steps` array. Optionally specify a starting value
    which is taken to be one by default.

    Returns:
    An np.ndarray of shape (n, len(time_steps)) with each row
    representing a relisation of the process's path at the
    specified nodes
    """

    drift_component = (mu - 0.5*sigma*sigma) * np.tile(time_steps, reps=(n_paths, 1))
    wiener_paths = simulate_wiener_process(time_steps, n_paths)
    diffusion_component = sigma * wiener_paths
    gbm_paths = initial_value * np.exp(drift_component + diffusion_component)

    return gbm_paths


#!/usr/bin/env python

"""

an example of solving Poisson's equation via smoothing only.  Here, we
solve

u_xx = sin(x)
u = 0 on the boundary [0,1]

The analytic solution is u(x) = -sin(x) + x sin(1)

M. Zingale (2013-03-31)

"""
#from io import *
import numpy
import pylab
import sys

# the analytic solution
def true(x):
    return -numpy.sin(x) + x*numpy.sin(1.0)

# the L2 error norm
def error(ilo, ihi, dx, r):

    # L2 norm of elements in r, multiplied by dx to
    # normalize
    return numpy.sqrt(dx*numpy.sum((r[ilo:ihi+1]**2)))

# the righthand side
def f(x):
    return numpy.sin(x)

def computeResidual(ilo, ihi, dx, phi, frhs):

    r = numpy.zeros(len(phi))

    r[ilo:ihi+1] = frhs[ilo:ihi+1] - \
        (phi[ilo+1:ihi+2] - 2.0*phi[ilo:ihi+1] + phi[ilo-1:ihi])/dx**2

    return r

def smoothRun(nx):

    xmin = 0.0
    xmax = 1.0

    ng = 1

    print nx

    # initialize the solution to zero.  Put one ghost cell on either end
    phi = numpy.zeros(nx + 2*ng, dtype=numpy.float64)

    phinew = numpy.zeros(nx + 2*ng, dtype=numpy.float64)

    ilo = ng
    ihi = ng + nx - 1

    # coordinates of centers
    dx = (xmax - xmin)/nx
    x = (numpy.arange(nx+2*ng) - ng + 0.5)*dx + xmin

    # initialize the RHS using the function f
    frhs = numpy.zeros(nx + 2*ng, dtype=numpy.float64)
    frhs[ilo:ihi+1] = f(x[ilo:ihi+1])

    # smooth
    n = numpy.arange(20000) + 1
    e = []
    r = []

    # fill the ghost cells
    phi[ilo-1] = -phi[ilo]
    phi[ihi+1] = -phi[ihi]

    print "source norm: ", error(ilo, ihi, dx, frhs)
    print numpy.sum(frhs[ilo:ihi+1])

    for i in n:

        # do Jacobi
        #phinew[ilo:ihi+1] = \
        #    (-dx*dx*frhs[ilo:ihi+1] + phi[ilo+1:ihi+2] + phi[ilo-1:ihi])/2.0
        # phi[:] = phinew[:]

        # this also does Jacobi, since phi on the RHS is evaluated before
        # the LHS is updated
        #phi[ilo:ihi+1] = \
        #    0.5*(-dx*dx*frhs[ilo:ihi+1] + phi[ilo+1:ihi+2] + phi[ilo-1:ihi])

        # red-black Gauss-Seidel -- first do the odd, then even points
        print phi[ilo:ihi+1:2]
        print phi[ilo:ihi+1]
        phi[ilo:ihi+1:2] = \
            0.5*(-dx*dx*frhs[ilo:ihi+1:2] + \
                      phi[ilo+1:ihi+2:2] + phi[ilo-1:ihi:2])

        # fill the ghost cells
        phi[ilo-1] = -phi[ilo]
        phi[ihi+1] = -phi[ihi]

        phi[ilo+1:ihi+1:2] = \
            0.5*(-dx*dx*frhs[ilo+1:ihi+1:2] + \
                      phi[ilo+2:ihi+2:2] + phi[ilo:ihi:2])

        # fill the ghost cells
        phi[ilo-1] = -phi[ilo]
        phi[ihi+1] = -phi[ihi]

        # compute the true error (wrt the analytic solution)
        e.append(error(ilo, ihi, dx, phi - true(x)))

        # compute the residual
        resid = computeResidual(ilo, ihi, dx, phi, frhs)

        r.append(error(ilo, ihi, dx, resid))

    r = numpy.array(r)
    e = numpy.array(e)

    return n, r, e

# test the multigrid solver
N = [16, 32, 64]

c = ["r", "g", "b"]

for nx in N:

    n, r, e = smoothRun(nx)
    color = c.pop()
    pylab.plot(n, e, color=color, label = `nx`)
    pylab.plot(n, r, color=color, ls=":")

ax = pylab.gca()
ax.set_xscale('log')
ax.set_yscale('log')

pylab.xlabel("# of iterations")
pylab.ylabel("L2 norm of true error (solid) and residual (dotted)")
#pylab.legend(frameon=False, fontsize="small")

pylab.savefig("smooth-error.png")


x = 1
print x + x + 1
from sympy import Symbol
x = Symbol('x')
print x + x + 1
a = Symbol('x')
print a + a + 1
x = Symbol('x')
y = Symbol('y')
z = Symbol('z')
s = x*y + x*y
print s
p = x*(x + x)
print p
from sympy import factor, expand
expr = x**2 - y**2
print factor(expr)
factors = factor(expr)
print expand(factors)
expr = x**3 + 3*x**2*y + 3*x*y**2 + y**2
factors = factor(expr)
print factors
print expand(factors)
expr = x*y + x + y
print factor(expr)
expr = x*x + 2*x*y + y*y
print expr
from sympy import pprint
pprint(expr)
expr = 1 + 2*x + 2*x**2
pprint(expr)
from sympy import init_printing
init_printing(order='rev-lex')
pprint(expr)

def printseries(n):
    init_printing(order='rev-lex')
    x = Symbol('x')
    series = x
    for i in range(2, n+1):
        series = series + (x**i)/i
    pprint(series)
if __name__ == '__main__':
    n = input('Enter the number of terms you want in the series: ')
    printseries(int(n))

print x*x + y*y + x*y + y*y
expr = x*x + y*y + x*y + y*y
res = expr.subs({x:1, y:2})
print res

print expr.subs({x:1-y})

expr_subs = expr.subs({x:1-y})
from sympy import simplify
print simplify(expr_subs)

def print_series(n, x_val):
    init_printing(order='rev-lex')
    x = Symbol('x')
    series = x
    for i in range(2, n+1):
        series = series + (x**i)/i
    pprint(series)
    series_value = series.subs({x:x_val})
    print 'Value of the series at {0}: {1}'.format(x_val, series_value)

if __name__ == '__main__':
    n = input('Enter the number of terms you want in the series: ')
    x_value = input('Enter the value of x at which you want to evaluate the series: ')
    print_series(int(n), float(x_value))

from sympy import sympify
expr = input('Enter a mathematical expression: ')
expr = sympify(expr)
print 2 * expr

from sympy.core.sympify import SympifyError
expr = input('Enter a mathematical expression: ')
try:
    expr = sympify(expr)
except SympifyError:
    print 'Invalid Input'

from sympy import expand

def product(expr1, expr2):
    prod = expand(expr1 * expr2)
    print prod

if __name__ == '__main__':
    expr1 = input('Enter the first expression: ')
    expr2 = input('Enter the second expression: ')

    try:
        expr1 = sympify(expr1)
        expr2 = sympify(expr2)
    except SympifyError:
        print 'Invalid Input'
    else:
        product(expr1, expr2)

from sympy import solve
x = Symbol('x')
expr = x - 5 - 7
print solve(expr)

expr = x**2 + 5*x + 4
print solve(expr, dict=True)

expr = x**2 + x + 1
print solve(expr, dict=True)

x = Symbol('x')
a = Symbol('a')
b = Symbol('b')
c = Symbol('c')

expr = a*x*x + b*x + c
print solve(expr, x, dict=True)

s = Symbol('s')
u = Symbol('u')
t = Symbol('t')
a = Symbol('a')
expr = u*t + (1/2)*a*t*t - s
t_expr = solve(expr,t, dict=True)
pprint(t_expr)

y = Symbol('y')
expr1 = 2*x + 3*y - 6
expr2 = 3*x + 2*y - 12
print solve((expr1, expr2), dict=True)

soln = solve((expr1, expr2), dict=True)
soln = soln[0]
print expr1.subs({x:soln[x], y:soln[y]})
print expr2.subs({x:soln[x], y:soln[y]})

from sympy.plotting import plot
plot(2*x + 3)
plot((2*x + 3), (x, -5, 5))
plot(2*x + 3, (x, -5, 5), title='A Line', xlabel='x', ylabel='2x+3')
p = plot(2*x + 3, (x, -5, 5), title='A Line', xlabel='x', ylabel='2x+3', show=False)
p.save(line.png)

expr = input("Enter an expression with an 'x' and a 'y': ")
expr = sympify(expr)
y = Symbol('y')
print solve(expr, y)

solutions = solve(expr, 'y')
expr_y = solutions[0]
print expr_y

def plot_expression(expr):

    y = Symbol('y')
    solutions = solve(expr, y)
    expr_y = solutions[0]
    plot(expr_y)

if __name__ == '__main__':
    expr = input('Enter your expression in terms of x and y: ')
    try:
        expr = sympify(expr)
    except SympifyError:
        print 'Invalid Input'
    else:
        plot_expression(expr)

plot(2*x+3, 3*x+1)

p = plot(2*x+3, 3*x+1, legend=True, show=False)
p[0].line_color = 'b'
p[1].line_color = 'r'
p.show()


"""Vectorized anomaly conversion scripts"""

import numpy as np

def true_to_ecc(theta,e):
    """Converts true anomaly to eccentric anomaly.

       Args:
           theta(numpy array): array of true anomalies (in radians)
           e(float): eccentricity

       Returns:
           numpy array: array of eccentric anomalies (in radians)
    """

    pi2=2*np.pi
    raw = np.arctan2((1-e**2)*np.sin(theta), e+np.cos(theta))
    raw = raw%pi2

    # revolution correction code
    correct = (theta//pi2)*pi2
    raw = raw+correct
    correct = np.clip((raw-theta)//pi2,0,None)*pi2

    return raw-correct

def ecc_to_mean(E,e):
    """Converts eccentric anomaly to mean anomaly.

       Args:
           E(numpy array): array of eccentric anomalies (in radians)
           e(float): eccentricity

       Returns:
           numpy array: array of mean anomalies (in radians)
    """

    return E - e*np.sin(E)

def mean_to_t(M,a):
    """Converts mean anomaly to time elapsed.

       Args:
           M(numpy array): array of mean anomalies (in radians)
           a(float): semi-major axis

       Returns:
           numpy array: numpy array of time elapsed
    """

    n = (a**3/398600.4405)**0.5
    return n*(M-M[0])

if __name__ == "__main__":
    thetas = np.linspace(0,4*np.pi,21)
    e = 0.7151443
    print(thetas)
    ecc = true_to_ecc(thetas,e)
    print(ecc)
    mean = ecc_to_mean(ecc,e)
    print(mean)
    t = mean_to_t(mean,26505.1836)
    print(t)