"""
Implementation of Expectation-maximization algorithm on Gaussian Mixture Model
"""

import numpy as np
import math
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs

def E_step(X, pi, mu, sigma):
    """
    Performs E-step on GMM model
    Each input is numpy array:
    X: (N x d), data points
    pi: (C), mixture component weights
    mu: (C x d), mixture component means
    sigma: (C x d x d), mixture component covariance matrices

    Returns:
    gamma: (N x C), probabilities of clusters for objects
    """
    N = X.shape[0] # number of objects
    C = pi.shape[0] # number of clusters
    gamma = np.zeros((N, C)) # distribution q(T)

    const_c = np.zeros(C)

    for i in range(C):
        pi_c = pi[i] # number
        mu_c = mu[i:i+1, :] # (1 x d)
        sigma_c = sigma[i] # (d x d)
        det_sigma_c = np.linalg.det(sigma_c)

        # assume determinant positive
        const_c[i] = pi_c/np.sqrt(det_sigma_c) #number
        X_centered = X - mu_c # (N x d)
        # y = (A^-1)x
        y = np.linalg.solve(sigma_c, X_centered.T)  #(d x N)
        # -1/2 * (xi-mu_c).T * sigma^-1 * (xi-mu_c) for all points together which diagonal of the dot product on right side
        gamma[:,i] = -0.5 * np.diag(np.dot(X_centered, y)) # (N)

    # subtract the max across column for a given row from the row (improve stability)
    gamma = gamma - gamma[range(N), np.argmax(gamma, axis=1)][:,np.newaxis]
    # take exponent of gamma
    gamma = np.exp(gamma)
    #mutiply each columns by respective constant pi_c / sqrt(det_sigma_c)
    gamma = np.multiply(gamma, const_c[np.newaxis,:])
    # normalize gamma
    gamma /= np.sum(gamma, axis=1)[:,np.newaxis]

    return gamma

def M_step(X, gamma):
    """
    Performs M-step on GMM model
    Each input is numpy array:
    X: (N x d), data points
    gamma: (N x C), distribution q(T)

    Returns:
    pi: (C)
    mu: (C x d)
    sigma: (C x d x d)
    """
    N = X.shape[0] # number of objects
    C = gamma.shape[1] # number of clusters
    d = X.shape[1] # dimension of each object

    mu = np.zeros((C, d))
    sigma = np.zeros((C, d, d))
    pi = np.sum(gamma, axis=0) / (1.0 * N)

    for i in range(C):
        mu[i, :] = np.sum(X * gamma[:,i][:,np.newaxis], axis=0) / (pi[i]*N)
        Xi = X - mu[i, :][np.newaxis, :]
        sigma[i, :, :] = np.dot((Xi*gamma[:, i][:,np.newaxis]).T, Xi) / (pi[i]*N)

    return pi, mu, sigma

def compute_vlb(X, pi, mu, sigma, gamma):
    """
    Each input is numpy array:
    X: (N x d), data points
    gamma: (N x C), distribution q(T)
    pi: (C)
    mu: (C x d)
    sigma: (C x d x d)

    Returns value of variational lower bound
    """
    N = X.shape[0] # number of objects
    C = gamma.shape[1] # number of clusters
    d = X.shape[1] # dimension of each object

    loss = 0
    for i in range(C):
        pi_i = pi[i]
        mu_i = mu[i]
        sigma_i = sigma[i]
        det_sigma_i = np.linalg.det(sigma_i)
        for j in range(N):
            loss += (gamma[j,i]+1e-20) * (np.log(pi_i) - 0.5 * np.log(det_sigma_i) - 0.5 * d * np.log(2 * np.pi))
            x_j = X[j]
            x_j = X[j] - mu_i
            y_j = np.linalg.solve(sigma_i, x_j)
            loss -= (gamma[j,i]+1e-20) * 0.5 * np.dot(x_j[np.newaxis,:], y_j[:, np.newaxis])
            loss -= (gamma[j,i]+1e-20) * np.log((gamma[j,i]+1e-20))

    return loss

def train_EM(X, C, rtol=1e-3, max_iter=100, restarts=10):
    '''
    Starts with random initialization *restarts* times
    Runs optimization until saturation with *rtol* reached
    or *max_iter* iterations were made.

    X: (N, d), data points
    C: int, number of clusters
    '''
    N, d = X.shape[0], X.shape[1] # N: number of objects; D: dimension of each object
    best_loss = float('-inf')
    best_pi = None
    best_mu = None
    best_sigma = None

    for _ in range(restarts):
        try:
            pi = np.array([1.0/C]*C,dtype=np.float32)
            mu = np.random.rand(C, d)
            sigma_ = np.random.rand(C, d, d)
            sigma = np.array([np.dot(A, A.T) for A in sigma_])
            prev_loss = None
            for i in range(max_iter):
                gamma = E_step(X, pi, mu, sigma)
                pi, mu, sigma = M_step(X, gamma)
                loss = compute_vlb(X, pi, mu, sigma, gamma)
                if not math.isnan(loss) and loss > best_loss:
                    best_loss = loss
                    best_mu = mu
                    best_pi = pi
                    best_sigma = sigma

                if prev_loss is not None:
                    diff = np.abs(loss - prev_loss)
                    if diff < rtol:
                        break
                prev_loss = loss
        except np.linalg.LinAlgError:
            print("Singular matrix: components collapsed")
            pass

    return best_loss, best_pi, best_mu, best_sigma

if __name__ == '__main__':
    # Initialize and plot the data
    X, y = make_blobs(n_samples=300, centers=3, n_features=2, random_state=0,cluster_std=[0.5,0.5,0.5])
    pi0 = np.random.random_sample((3))
    mu0 = np.random.random_sample((3,2))
    sigma0 = np.random.random_sample((3,2,2))
    plt.scatter(X[:, 0], X[:, 1], c='grey', s=30)
    plt.axis('equal')
    plt.show()

    # Perform E step and M step, calculate the loss
    gamma = E_step(X, pi0, mu0, sigma0)
    pi, mu, sigma = M_step(X, gamma)
    loss = compute_vlb(X, pi, mu, sigma, gamma)

    # Train with EM algorithm
    best_loss, best_pi, best_mu, best_sigma = train_EM(X, 3)

    gamma = E_step(X, best_pi, best_mu, best_sigma)
    # Classfy each point to clusters
    labels = gamma.argmax(1)
    # Visualize the result
    plt.scatter(X[:, 0], X[:, 1], c=labels, s=30)
    plt.axis('equal')
    plt.show()

Bayesian methods for Machine Learning/EM_GMM.py


import math
import numpy as np
import torch
import enum

# x, y - 20-d gaussian variable with mean 0 and correlation p
# mutual information I(x,y) = -d/2 * log(1-p^2)

class MI(enum.IntEnum):
    NCE = 0
    NWJ = 1
    TUBA = 2
    INTERPOLATE = 3
    IS = 4
    LSH = 5

def mutual_information(d, p):
    return -d/2. * math.log(1.-math.pow(p, 2.))

def correlation(d, mi):
    return math.sqrt(1. - math.exp(-2. * mi / d))

def sample(d, p):
    mean = np.zeros(2)
    cov = np.eye(2)
    cov[0, 1] = p
    cov[1, 0] = p
    item = np.random.multivariate_normal(mean, cov, d)
    x, y = zip(*item)
    return np.asarray(x), np.asarray(y)

def sample_single(d, p):
    x = list()
    y = list()
    for idx in range(d):
        a = np.random.normal(0, 1)
        b = np.random.normal(0, 1)
        u = a
        v = p*a - math.sqrt(1.-math.pow(p, 2.)) * b
        x.append(u)
        y.append(v)
    return np.asarray(x), np.asarray(y)

def generate(d, mi, N, transform=None):
    p = correlation(d, mi)
    xt = list()
    yt = list()
    for idx in range(N):
        x, y = sample_single(d, p)
        xt.append(x)
        yt.append(y)
    x_tensor = np.asarray(xt, dtype=np.float32)
    y_tensor = np.asarray(yt, dtype=np.float32)
    if transform is not None:
       y_tensor = np.power(np.matmul(y_tensor, transform), 3)
    return torch.from_numpy(x_tensor), torch.from_numpy(y_tensor)

def mi_schedule(n_iter, value=None):
    """Generate schedule for increasing correlation over time."""
    if value is None:
        #mis = np.round(np.linspace(5.5-1e-9, 0.5, n_iter))*2.0
        mis = np.round(np.linspace(0.5, 5.5-1e-9, n_iter))*2.0
        #mis = np.round(np.linspace(0.5, 2.5-1e-9, n_iter))*2.0
        #mis = np.asarray([2] * (n_iter//2) + [8] * (n_iter//2))
    else:
        mis = np.asarray([value] * n_iter)
    return mis.astype(np.float32)

def generate_dataset(n_iter, schedule, d, batch_size):
    xs = list()
    ys = list()
    #W = np.random.normal(size=(d, d)).astype(np.float32)
    W = None
    for batch_idx, MI in enumerate(schedule):
        x, y = generate(d, MI, batch_size, W)
        xs.append(x)
        ys.append(y)
    return torch.cat(xs, dim=0), torch.cat(ys, dim=0)


#ketTenQubit(i,n)       :   n-qubit state with |1> at i-th tensor and |0> elswhere

import numpy as np

from ..tenProdSelfMd import tenProdSelf
from ..tenProdMd import tenProd

__all__ = ['ketTenQubit']

def ketTenQubit(i,n):
    r"""Takes as input an integer representing a position 'i'
    and another integer representing the number of tensor products 'n'.
    Returns an n-qubit state(numpy.ndarray of length :math:'2^n') with i-th
    qubit |1> and rest |0>.

    Parameters
    ----------
    i : int
        index where ket should be |1>

    n : int
        the number of tensor products

    Returns
    ----------
    vec : numpy.ndarray
          A 1-d numpy.ndarray representing an n-qubit state

    Notes
    ----------
    The input argument 'ten' should be greater than 1
    """

    err1 = 'position of qubit greater or equal to tensor products in ketTenQubit'

    if (i >= n):
        raise ValueError(err1)

    zero = np.array([1.,0.])
    one = np.array([0.,1.])

    if (i == 0):
        block1 = one
        block2 = tenProdSelf(zero, n-1)

    elif (i == n - 1):
        block1 = tenProdSelf(zero, n-1)
        block2 = one

    else:
        block1 = ketTenQubit(i,i+1)
        block2 = tenProdSelf(zero,n-i-1)

    vec = tenProd(block1, block2)
    return vec


import math
import numpy as np
import scipy.stats as st

# Calculates the upper and lower bound for the underlying spot price based on volatility
# probability param: if probability=0.7, probability of stock price in prob_cone is 70%
def prob_cone(price_ls:list, stock_price:float, volatility:float, days_ahead:int, probability=0.7) -> tuple:

    # z_score param indicates the number of std deviations from the mean (i.e. 1.5 std dev covers about 87%)
    # Source: https://stackoverflow.com/questions/20864847/probability-to-z-score-and-vice-versa
    z_score = st.norm.ppf(1-((1-probability)/2))
    # z_score = st.norm.ppf(1-(probability/2))

    # z_score = st.norm.ppf(1-((1-probability)/2), log_price_array.mean(), log_price_array.std())

    # Source: https://www.biocrudetech.com/index.php?option=com_blankcomponent&view=default&Itemid=670
    std_dev = z_score * stock_price * volatility * math.sqrt(days_ahead/252)

    upper_bound = round(stock_price + std_dev, 2)
    lower_lound = round(stock_price - std_dev, 2)

    return (lower_lound,upper_bound)

def get_prob(stock_price:float, strike_price:float, volatility:float, days_ahead:int) -> float:

    if None or 0 not in (stock_price, strike_price, volatility, days_ahead):

        z_score = abs(stock_price - strike_price)/(stock_price * volatility * math.sqrt(days_ahead/252))

        return 2 * st.norm.cdf(z_score) - 1
    else:
        return 0

# Calculates annualized historical volatility using log returns
def get_hist_volatility(price_ls:list) -> float:

    if None not in (price_ls):

        # Source: https://goodcalculators.com/historical-volatility-calculator/
        # Source: https://quantivity.wordpress.com/2011/02/21/why-log-returns/

        log_stock_returns = [math.log(price_ls[i+1]/price_ls[i]) for i in range(len(price_ls)-1)]

        if len(log_stock_returns) != 0:
            ave_log_return = sum(log_stock_returns)/len(log_stock_returns)
        else:
            ave_log_return = 0

        returns_diff = [(returns - ave_log_return)**2 for returns in log_stock_returns]

        return math.sqrt(252) * math.sqrt(sum(returns_diff)/(len(returns_diff)-1))

    else:
        return 0


import sys
import numpy as np
from matplotlib import pyplot as plt
from knn import get_distance_matrix

class KMeans():
    def __init__(self, k):
        self.k = k
        self.centers = []

    def fit(self, data):
        sample_ids = np.random.choice(data.shape[0], self.k, replace= False)
        centers = data[sample_ids]
        cluster_ids = np.zeros(data.shape[0])
        while True:
            distance_matrix = get_distance_matrix(centers, data)
            new_cluster_ids = np.argmin(distance_matrix, 1)
            if np.any(new_cluster_ids != cluster_ids):
                cluster_ids = new_cluster_ids
                for i in range(self.k):
                    centers[i] = np.mean(data[cluster_ids == i], 0)
            else:
                break

        self.centers = centers

    def evaluate(self, data):
        distance_matrix = get_distance_matrix(self.centers, data)
        cluster_ids = np.argmin(distance_matrix, 1)
        error = 0
        for i in range(self.k):
            cluster_diff = data[cluster_ids == i] - self.centers[i]
            error += np.sum(cluster_diff ** 2)
        return error

def get_data():
    data = np.genfromtxt('nolabel.dat')
    return data

def problem_15():
    data = get_data()
    k_list = [2, 4, 6, 8, 10]
    error_mean_list = []
    repeat = 500
    for k in k_list:
        error = 0
        kmeans = KMeans(k)
        for _ in range(repeat):
            kmeans.fit(data)
            error += kmeans.evaluate(data)
        error_mean_list.append(error / repeat)

    plt.title('Problem 15')
    plt.xlabel('K value')
    plt.ylabel('Mean of Ein')
    plt.scatter(k_list, error_mean_list)
    plt.savefig('img/problem_15.png')

def problem_16():
    data = get_data()
    k_list = [2, 4, 6, 8, 10]
    error_var_list = []
    repeat = 500
    for k in k_list:
        error = []
        kmeans = KMeans(k)
        for _ in range(repeat):
            kmeans.fit(data)
            error.append(kmeans.evaluate(data))
        error_var_list.append(np.var(error))

    plt.title('Problem 16')
    plt.xlabel('K value')
    plt.ylabel('Variance of Ein')
    plt.scatter(k_list, error_var_list)
    plt.savefig('img/problem_16.png')

def test():
    data = get_data()
    kmeans = KMeans(10)
    kmeans.fit(data)
    print('Done!')

if __name__ == '__main__':
    mode = sys.argv[1]
    if mode == '15':
        problem_15()
    elif mode == '16':
        problem_16()
    elif mode == 'test':
        test()
    else:
        print('Error: Wrong argument !')


import numpy as np

def gaussian_elimination(A):

    pivot_row = 0

    # Step 1: Go by column
    for pivot_col in range(min(A.shape[0], A.shape[1])):

        # Step 2: Swap row with highest element in col
        max_i = np.argmax(abs(A[pivot_row:, pivot_col])) + pivot_row

        temp = A[pivot_row, :].copy()
        A[pivot_row, :] = A[max_i, :]
        A[max_i, :] = temp

        # Skip on singular matrix,  not actually a pivot
        if A[pivot_row, pivot_col] == 0:
            continue

        # Steps 3 & 4: Zero out elements below pivot
        for r in range(pivot_row + 1,  A.shape[0]):
            # Step 3: Get fraction
            frac = -A[r, pivot_col] / A[pivot_row, pivot_col]
            # Step 4: Add rows
            A[r, :] += frac * A[pivot_row, :]

        pivot_row += 1

# Assumes A is already row echelon form
def gauss_jordan_elimination(A):

    col = 0

    # Scan for pivots
    for row in range(A.shape[0]):
        while col < A.shape[1] and A[row, col] == 0:
            col += 1

        if col >= A.shape[1]:
            continue

        # Set each pivot to one via row scaling
        A[row, :] /= A[row, col]

        # Zero out elements above pivot
        for r in range(row):
            A[r, :] -= A[r, col] * A[row, :]

# Assumes A has a unique solution and A in row echelon form
def back_substitution(A):

    sol = np.zeros(A.shape[0]).T

    # Go by pivots along diagonal
    for pivot_i in range(A.shape[0] - 1,  -1,  -1):
        s = 0
        for col in range(pivot_i + 1,  A.shape[1] - 1):
            s += A[pivot_i, col] * sol[col]
        sol[pivot_i] = (A[pivot_i, A.shape[1] - 1] - s) / A[pivot_i, pivot_i]

    return sol

def main():
    A = np.array([[2, 3, 4, 6],
                  [1, 2, 3, 4,],
                  [3, -4, 0, 10]], dtype=float)

    print("Original")
    print(A, "\n")

    gaussian_elimination(A)
    print("Gaussian elimination")
    print(A, "\n")

    print("Back subsitution")
    print(back_substitution(A), "\n")

    gauss_jordan_elimination(A)
    print("Gauss-Jordan")
    print(A, "\n")

if __name__ == "__main__":
    main()


import numpy as np
from matplotlib import pyplot as plt
from noise_robust_differentiator import derivative_n2

def sin_demo():
    t = np.linspace(0, 4 * np.pi, 100)
    dt = t[1] - t[0]
    x = np.sin(t)

    deriv = derivative_n2(x, dt=dt)
    true_deriv = np.cos(t)

    plt.plot(
        t, deriv,
        color='tab:blue', label='Numerical')
    plt.plot(
        t, true_deriv,
        color='black', label='True', linestyle='--')
    plt.xlabel('$t$')
    plt.ylabel('$df / dt$')
    plt.legend()

def noisy_sin_demo():
    t = np.linspace(0, 4 * np.pi, 100)
    dt = t[1] - t[0]
    x = np.sin(t)
    x_noisy = x + np.random.normal(scale=0.05, size=len(x))

    deriv = derivative_n2(
        x_noisy, dt=dt, filter_length=11)
    true_deriv = np.cos(t)

    fig, axes = plt.subplots(
        figsize=(6, 7), nrows=2, ncols=1)
    axes[0].plot(
        t, x,
        color='black', label='True', linestyle='--')
    axes[0].plot(
        t, x_noisy,
        color='grey', label='Noisy')
    axes[0].set_xlabel('$t$')
    axes[0].set_ylabel('$f$')
    axes[0].legend()

    axes[1].plot(
        t, deriv,
        color='tab:blue', label='Numerical on noisy')
    axes[1].plot(
        t, true_deriv,
        color='black', label='True', linestyle='--')
    axes[1].set_xlabel('$t$')
    axes[1].set_ylabel('$df / dt$')
    axes[1].legend()

if __name__ == '__main__':
    # sin_demo()
    noisy_sin_demo()
    plt.show()


# J. Pocahontas Olson   June 2016
# Fibonacci generator

import fibonacci_module as fb         # Custom module made for this project
import math                           # Math functions (log, pow, sqrt, etc.)
import matplotlib.pyplot as plt       # Plotting
plt.style.use('ggplot')
import pylab
from scipy.optimize import curve_fit  # Curve fitting
from sklearn.metrics import mean_squared_error  # Error of fit
from distutils.util import strtobool  # Translates user answer to Yes/No question to bool

FIRSTPOINTS  = int(100)
PREDICTPOINT = int(500)

# Introduction to user
print("Welcome!")

###############################
### (1.)  Graph first 100 (FIRSTPOINTS) Fibonacci numbers
###############################
y = fb.fibList(FIRSTPOINTS)
x = list(range(1,FIRSTPOINTS+1))  # Adjusts for index starting at 0
print("Would you like to see a graph of the first", FIRSTPOINTS, "Fibonacci numbers? ")
showit = strtobool(input("  [Figure 1]  Y/N: "))
if showit:
    plt.figure(1)
    plt.plot(x, y)
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Fibonacci number')
    plt.title('First ' + str(FIRSTPOINTS) + ' Fibonacci numbers')
    #plt.yscale('log')  # Uncomment to see log graph
    pylab.show(block=False)
print("\nFrom Figure 1, we can see that the Fibonnaci numbers grow exponentially.")

##############################
## (2.) Let's try to fit the data!
##############################
print("Our first thought would be to fit an exponential to this data.")
print("However, because of the large numbers involved, it's better to fit")
print("a line to the log of the Fibonacci numbers.")
# Generate the log values of the true Fibonacci numbers
log_x = x[1:]  # Ignore first fibonacci number b/c log(0) is undefined
log_y = [math.log(fibNum) for fibNum in y[1:]]
# Define a line to fit the log of the Fib numbers
def fitLogPrediction(x, m, b):    # x is input, and m and b are the slope and y-intercept of the line, respectively
    return m * x + b
# Function to convert from the fit of log scale, to actual prediction of fibonacci number (rounds to nearest int)
def fitFibPrediction(x, m, b):
    return round(math.exp(fitLogPrediction(x, m, b)))
# Find 500th (PREDICTPOINT) Fibonacci number as well
predictList = fb.fibList(PREDICTPOINT)

# Fit the line, plot it in red, predict 500th point
popt, pcov = curve_fit(fitLogPrediction, log_x, log_y)   # Finds slope and y-intercept that best fit
fit_y = [fitLogPrediction(nFib,popt[0],popt[1]) for nFib in log_x]  # Makes the line that fits
fitlog500prediction = fitLogPrediction(PREDICTPOINT,popt[0],popt[1])
actual500num = predictList[PREDICTPOINT-1]
# Find error bars for extrapolated predictions
logerror = math.sqrt(mean_squared_error(log_y, fit_y))*math.sqrt(FIRSTPOINTS)
lower500bound = math.exp(fitlog500prediction-logerror)
upper500bound = math.exp(fitlog500prediction+logerror)
print("\nFitting a line to the data, the best fit has slope\n", popt[0], "and y-intercept", popt[1])

# For large n, the slope approaches phi = (1+sqrt(5))/2.  Let's see what we got.
print("We can compare this to the theoretical limit (applicable for large n),\n  which should yield the golden ratio.")
print("Compare the fit's prediction: ", math.exp(popt[0]))
print("     to the golden ratio phi: ", fb.phi)
print("\nSo after using only", FIRSTPOINTS, "Fibonacci numbers, the fit behavior differs from")
print("     the theoretical limit by", 100*(1- math.exp(popt[0])/fb.phi), "%")

print("\nWould you like to see a graph of the line we fit to the \nfirst", FIRSTPOINTS,
      "Fibonacci numbers, on a log scale? ")
showit = strtobool(input("  [Figure 2]  Y/N: "))
if showit:
    plt.figure(2)
    plt.subplot(211)
    plt.plot(log_x, log_y, "bs")  # Log values in blue squares
    plt.plot(log_x, fit_y, 'r-')  # Fit in red dashes
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Log of Fibonacci number')
    plt.title('Fitted prediction of Fibonacci numbers, on log scale')
    plt.subplots_adjust(hspace=0.5)
    plt.subplot(212)
    plt.plot(log_x + [PREDICTPOINT], log_y + [math.log(actual500num)], "bs")  # Log values in blue squares
    plt.plot(log_x + [PREDICTPOINT], fit_y + [fitlog500prediction], 'r-')  # Fit in red dashes
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Log of Fibonacci number')
    plt.title('Same graph, extrapolated to predict 500th Fibonacci number')
    pylab.show(block=False)
print("From Figure 2, we can see that the fit seems to match the first", int(FIRSTPOINTS),"Fibonacci numbers well,")
print("and extrapolating the fit to predict the", int(PREDICTPOINT),"th Fibonacci number seems pretty good also.")
print("\nIn fact, the fit predicts the{0:4d}th Fibonacci number to be {1:12E},".format(int(PREDICTPOINT), math.exp(fitlog500prediction)))
print("  within [{0:12E}, {1:12E}]. \nThe actual value, {2:12E}, is within these bounds.".format( lower500bound, upper500bound, actual500num))

print("\nNow let us investigate how much the fit is off by.")

#############################
# (3.) Let's see how well our fit did
#############################
fit_differences = [y[nFib-1] - fitFibPrediction(nFib-1,popt[0],popt[1]) for nFib in log_x]
fit_percent_differences = [(y[nFib-1] - fitFibPrediction(nFib-1,popt[0],popt[1]))/y[nFib-1] for nFib in log_x]

print("We have calculated the fit's prediction of the first", FIRSTPOINTS, "Fibonacci numbers,")
print("to compare to the actual Fibonacci numbers.")

## Differences from actual Fibonacci numbers
print("\nWould you like to see graphs of the differences, and percent differences,")
print("between the fit's prediction and the actual Fibonacci number?")
showit = strtobool(input("  [Figure 3]  Y/N: "))
if showit:
    plt.figure(3)
    plt.subplot(211)
    plt.plot(log_x, fit_differences)
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Difference')
    plt.title('Difference from true Fibonacci number and fit')
    plt.ticklabel_format(style='sci', axis='y', scilimits=(-2,10))  # Sets scientific notation for axes
    plt.subplots_adjust(hspace=0.5)
    plt.subplot(212)
    plt.plot(log_x, fit_percent_differences)
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Percent Difference')
    plt.title('Percent Difference from true Fibonacci number and fit')
    pylab.show(block=False)
print("From Figure 3, we can see that the prediction reproduces the Fibonacci numbers")
print("fairly well, but is off from the true value by more at large n.")
print("This is to be somewhat expected because of the expontentially increasing numbers involved.")
print("If you look at the percent differences, the prediction is within a hundreth of a percent.\n")

## Binet's prediction and its differences from actual Fibonacci numbers
print("The Fibonacci numbers have been well studied, and there is a closed-form expression")
print("to get the n^th Fibonacci number that uses the golden ratio.")
print("It's called Binet's formula, and more info's on wikipedia:")
print("   https://en.wikipedia.org/wiki/Fibonacci_number#Closed-form_expression")

print("\nBinet's formula is meant for large n (where the ratio of Fibonacci numbers is ")
print("closer to the golden ratio), but let's see what it predicts for the first 100 values.")

binet_differences = [y[nFib-1] - fb.f_Binet(nFib-1) for nFib in log_x]
binet_percent_differences = [(y[nFib-1] - fb.f_Binet(nFib-1))/y[nFib-1] for nFib in log_x]
print("\nWould you like to see the same graphs as before (differences and percent differences),")
print("but using Binet's formula to predict the n^th Fibonacci number instead of the fit we made?")
showit = strtobool(input("  [Figure 4]  Y/N: "))
if showit:
    plt.figure(4)
    plt.subplot(211)
    plt.plot(log_x, binet_differences)
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Difference')
    plt.title('Difference from true Fibonacci number and theoretical prediction')
    plt.subplots_adjust(hspace=0.5)
    plt.subplot(212)
    plt.plot(log_x, binet_percent_differences)
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Percent Difference')
    plt.title('Percent Difference from true Fibonacci number and theoretical prediction')
    pylab.show(block=False)
print("From Figure 4, we can see that Binet's prediction does better at large n.")
print("Our fit is limited by only being able to look at the first", FIRSTPOINTS, "Fibonacci numbers.")
print("Binet's formula is better at large n.  If I were to implement this, I would probably use")
print("the theoretical limit (Binet's formula) after a certain cut-off point (maybe around 20).")
print("Also, getting more data for our initial fit (for example, the first 10,000 Fibonacci numbers)")
print("would improve the accuracy of our fit.")

##############################
## (4.) Clean up noisy data
##############################
HIGHEST_FIB_N      = int(1000)
NUMBEROFDATAPOINTS = int(200)

print("\n\nWe now move on to part 5, simulating some data collection process, which")
print("obtains", NUMBEROFDATAPOINTS, "Fibonacci numbers with some Gaussian noise introduced.")
print("It's pulling initial values from the first", HIGHEST_FIB_N, "Fibonacci numbers.")

## Import API that generates noisy data
import noisy_input_API as api
import random

## Generate true Fibonacci numbers (between 1 and HIGHEST_FIB), to compare to predictions
fibs = fb.fibList(HIGHEST_FIB_N)
x = [random.randint(1, HIGHEST_FIB_N) for i in range(NUMBEROFDATAPOINTS)]
y = [fibs[x[i]-1] for i in range(NUMBEROFDATAPOINTS)]  #minus 1 adjusts for index starting at zero

## Generate noisy data
y_noise = api.add_noise(y)  # fib numbers with noise
noise_differences = [y_noise[i]-y[i] for i in range(NUMBEROFDATAPOINTS)]
noise_percent_differences = [(noise_differences[i]/y[i] if y[i]>0 else 0) for i in range(NUMBEROFDATAPOINTS)]

## Plot of noise
print("\nWould you like to see a plot of the noise we added to the Fibonacci numbers?")
showit = strtobool(input("  [Figure 5]  Y/N: "))
if showit:
    plt.figure(5)
    plt.plot(x, noise_percent_differences, marker='o',color='b',linestyle="None")
    plt.hlines(0, 0, HIGHEST_FIB_N, colors='r')
    pylab.xlim([1,HIGHEST_FIB_N])
    plt.xlabel('n^th Fibonacci number')
    plt.ylabel('Percent difference (signal-true)/true')
    plt.title('Noise from simulated measurer of Fibonacci number')
    pylab.show(block=False)
print("From Figure 5, we can see that the noise is fairly random.")
print("Since we are looking at percent differences, the size of the noise increases at")
print("higher n, to mimic the difficulty in being precise when large numbers are involved.")

## Clean-up noise
print("\nWe clean-up the data, by nudging the noisy value to the nearest Fibonacci number.")
print("We can not use the theoretical prediction (Binet's formula), unfortunately, because")
print("the numbers get too big too fast.")
print("Instead, I've implemented a way to save off a large amount of Fibonacci numbers to a binary file.")
print("This trades space for computational time later.")
print("I've also written a function to extract the nth Fibonacci number from that file")
print("without having to go the whole way through the file or load it entirely to memory.")
# Prompt to make a new saved file
import os.path
import distutils.util
if not os.path.isfile(fb.filename):
    ans = input("That saved file does not already exist. \n  Would you like to create one?: (Y/N) ")
    makenew = distutils.util.strtobool(ans)
    if makenew:
        make_saved_Fibonacci_file()
    else:
        print("Can't show you the cool 'nudging' function until you create the saved file.  Exiting now.")
        quit()

# Correct the noisy inputs
corrected_fib_numbers = [fb.nearest_saved_fib(y_noise[eachPoint]) for eachPoint in range(NUMBEROFDATAPOINTS)]
wrongPrediction = []
for value in range(NUMBEROFDATAPOINTS):
    if corrected_fib_numbers[value] != y[value]:
        wrongPrediction.append(value)
cleaned_differences = [corrected_fib_numbers[i]-y[i] for i in range(NUMBEROFDATAPOINTS)]

## Plot of error in clean-up
print("\nWhen cleaning up the data, we were wrong",len(wrongPrediction),"out of", NUMBEROFDATAPOINTS, "times.")
#print("\nWould you like to see a plot of how often we were wrong in cleaning up the data?")
#showit = strtobool(input("  [Figure 6]  Y/N: "))
#if showit:
#    plt.figure(6)
#    plt.hist([abs(number) for number in cleaned_differences], bins = range(0,NUMBEROFDATAPOINTS,1))
#    plt.xlabel('How much we were wrong by')
#    plt.ylabel('How often we were wrong by this much')
#    plt.title('Error in cleaning up noisy data')
#    pylab.show(block=False)
print("Would you like to see when we got the wrong number?")
showit = strtobool(input("  [Table 1]  Y/N: "))
if showit:
    print("   Orig n     Orig Fib num     Noisey input     Corrected Fib")
    for wrong in wrongPrediction:
        print('{:11d}  {:11E} {:16E} {:16E}'.format(x[wrong], y[wrong], y_noise[wrong],corrected_fib_numbers[wrong]))
print("From Table 1, we see that we were only wrong when")
print("the noise was so big that it passed the nearest Fibonacci number.")

##############################
##           END            ##
##############################
print("\n\nThis concludes our run-though of using machine learning to predict Fibonacci numbers.")
print("Thank you, and enjoy your day!")
pylab.show()


from typing import Optional
import math
import numpy as np

from overrides import overrides
import torch

from allennlp.training.metrics.covariance import Covariance
from allennlp.training.metrics.metric import Metric

@Metric.register("pearson_correlation")
class PearsonCorrelation(Metric):
    """
    This ``Metric`` calculates the sample Pearson correlation coefficient (r)
    between two tensors. Each element in the two tensors is assumed to be
    a different observation of the variable (i.e., the input tensors are
    implicitly flattened into vectors and the correlation is calculated
    between the vectors).

    This implementation is mostly modeled after the streaming_pearson_correlation function in Tensorflow. See
    https://github.com/tensorflow/tensorflow/blob/v1.10.1/tensorflow/contrib/metrics/python/ops/metric_ops.py#L3267

    This metric delegates to the Covariance metric the tracking of three [co]variances:

    - ``covariance(predictions, labels)``, i.e. covariance
    - ``covariance(predictions, predictions)``, i.e. variance of ``predictions``
    - ``covariance(labels, labels)``, i.e. variance of ``labels``

    If we have these values, the sample Pearson correlation coefficient is simply:

    r = covariance / (sqrt(predictions_variance) * sqrt(labels_variance))
    """
    def __init__(self) -> None:
        self._predictions_labels_covariance = Covariance()
        self._predictions_variance = Covariance()
        self._labels_variance = Covariance()

    def __call__(self,
                 predictions: torch.Tensor,
                 gold_labels: torch.Tensor,
                 mask: Optional[torch.Tensor] = None):
        """
        Parameters
        ----------
        predictions : ``torch.Tensor``, required.
            A tensor of predictions of shape (batch_size, ...).
        gold_labels : ``torch.Tensor``, required.
            A tensor of the same shape as ``predictions``.
        mask: ``torch.Tensor``, optional (default = None).
            A tensor of the same shape as ``predictions``.
        """
        predictions, gold_labels, mask = self.unwrap_to_tensors(predictions, gold_labels, mask)
        self._predictions_labels_covariance(predictions, gold_labels, mask)
        self._predictions_variance(predictions, predictions, mask)
        self._labels_variance(gold_labels, gold_labels, mask)

    def get_metric(self, reset: bool = False):
        """
        Returns
        -------
        The accumulated sample Pearson correlation.
        """
        covariance = self._predictions_labels_covariance.get_metric(reset=reset)
        predictions_variance = self._predictions_variance.get_metric(reset=reset)
        labels_variance = self._labels_variance.get_metric(reset=reset)
        if reset:
            self.reset()
        denominator = (math.sqrt(predictions_variance) * math.sqrt(labels_variance))
        if np.around(denominator, decimals=5) == 0:
            pearson_r = 0
        else:
            pearson_r = covariance / denominator
        return pearson_r

    @overrides
    def reset(self):
        self._predictions_labels_covariance.reset()
        self._predictions_variance.reset()
        self._labels_variance.reset()


from lumos.numpy.casadi_numpy import linalg
from lumos.numpy.casadi_numpy.array import array
import numpy as _onp

def rotation_matrix_2D(angle,):
    """
    Gives the 2D rotation matrix associated with a counterclockwise rotation about an angle.
    Args:
        angle: Angle by which to rotate. Given in radians.

    Returns: The 2D rotation matrix

    """
    sintheta = _onp.sin(angle)
    costheta = _onp.cos(angle)
    rotation_matrix = array([[costheta, -sintheta], [sintheta, costheta]])
    return rotation_matrix

def rotation_matrix_3D(angle, axis, _axis_already_normalized=False):
    """
    Gives the 3D rotation matrix from an angle and an axis.
    An implmentation of https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
    :param angle: can be one angle or a vector (1d ndarray) of angles. Given in radians. # TODO note deprecated functionality; must be scalar
        Direction corresponds to the right-hand rule.
    :param axis: a 1d numpy array of length 3 (x,y,z). Represents the angle.
    :param _axis_already_normalized: boolean, skips normalization for speed if you flag this true.
    :return:
        * If angle is a scalar, returns a 3x3 rotation matrix.
        * If angle is a vector, returns a 3x3xN rotation matrix.
    """
    if not _axis_already_normalized:
        axis = axis / linalg.norm(axis)

    sintheta = _onp.sin(angle)
    costheta = _onp.cos(angle)
    cpm = array(
        [[0, -axis[2], axis[1]], [axis[2], 0, -axis[0]], [-axis[1], axis[0], 0],]
    )  # The cross product matrix of the rotation axis vector
    outer_axis = linalg.outer(axis, axis)

    rot_matrix = costheta * _onp.eye(3) + sintheta * cpm + (1 - costheta) * outer_axis
    return rot_matrix


import numpy as np
import pandas as pd
import math
import queue
from matplotlib import pyplot as plt
from sklearn.neighbors import NearestNeighbors # Only for part c.1

# The following codes are for part b, DBSCAN. The part of returning a file is in the main function.
UNASSIGNED = -2
NOISE = -1

class DBSCAN():
    def __init__(self, epsilon, MinPts):
        self.epsilon = epsilon
        self.MinPts = MinPts

    def distance(self, x, y):
        ans = math.sqrt(np.power(x-y, 2).sum())
        return ans

    def neighbours(self, example, objectID):
        objects = []
        for i in range(len(example)):
            dist = self.distance(example[i, :], example[objectID, :])
            if dist < self.epsilon:
                objects.append(i)
        return np.asarray(objects)

    def clustering(self, example, clusterLabel, objectID, clusterID):
        objects = self.neighbours(example, objectID)
        objects = objects.tolist()
        myQueue = queue.Queue()
        lengthObj = len(objects)

        if lengthObj < self.MinPts:
            clusterLabel[objectID] = NOISE
            return False
        else:
            clusterLabel[objectID] = clusterID

        for object in objects:
            if clusterLabel[object] == UNASSIGNED:
                myQueue.put(object)
                clusterLabel[object] = clusterID
        while not myQueue.empty():
            neighboursRemaining = self.neighbours(example, myQueue.get())
            lengthNbo = len(neighboursRemaining)
            if lengthNbo >= self.MinPts:
                for i in range(lengthNbo):
                    resultObject = neighboursRemaining[i]
                    if clusterLabel[resultObject] == UNASSIGNED:
                        myQueue.put(resultObject)
                        clusterLabel[resultObject] = clusterID
                    elif clusterLabel[clusterID] == NOISE:
                        clusterLabel[resultObject] = clusterID
        return True

    def fit(self, data):
        clusterID = 0
        length = len(data)
        clusterLabel = [UNASSIGNED] * length
        for objectID in range(length):
            if clusterLabel[objectID] == UNASSIGNED:
                if self.clustering(data, clusterLabel, objectID, clusterID):
                    clusterID += 1
        return np.asarray(clusterLabel)

def main():
    # This part is for part a, preprocessing.
    print('......Reading Data......\n')
    data_file = "houshold2007.csv"
    print('......Reading Completed......\n')
    print('......Preprocessing......\n')
    data = pd.read_csv(data_file, sep = ",",low_memory = False)
    data = data[data['Date'].apply(lambda x: x.endswith('/1/2007'))] # Using January's data.
    data = data.iloc[:, [2,3,4,5]] # Using the data from 3rd to 6th column
    data = data.replace('?', np.nan)
    data = data.dropna()
    data = data.astype(float)
    data = data.apply(lambda x : (x-np.min(x))/(np.max(x)-np.min(x))) # Using min-max scaling.
    print('Further Explanation And Analysis of Preprocessing Is in The Report.\n')
    print('......Preprocessing Completed......\n')

    # This part is for part b, implementing DBSCAN and returning a file of cluster labels.
    print('......Implementing DBSCAN......\n')
    data = data.values
    clusterLabel = DBSCAN(epsilon = 0.030, MinPts = 90).fit(data)
    labelSeries = pd.Series(clusterLabel)
    cluster = list(labelSeries.value_counts().index)
    frequency = list(labelSeries.value_counts())
    print('Number of cluster labels is %d.' % (len(cluster)))
    print('Cluster labels are the following. ATTENTION: NOISE is -1, cluster labels start from 0.')
    print(cluster)
    print('Frequency of each cluster label is the following.')
    print(frequency)
    print('Further Explanation And Analysis of Histogram Is in The Report.\n')
    print('......Generating Cluster Label File......\n')
    with open("Output of Cluster Labels.txt","w") as f:
        f.write('Cluster labels are the following. ATTENTION: NOISE is -1, cluster labels start from 0.\n'+ str(cluster))
    print('......Cluster Label File Generated......\n')
    print('......Implementation Completed......\n')

    # This part is for part c.1, k distance diagram.
    # The following codes are modified and inspired by the website: https://medium.com/@tarammullin/dbscan-parameter-estimation-ff8330e3a3bd.
    # The following codes will use sklearn.neighbors.
    print('......Generating Figure 1: K Distance Diagram......\n')
    dimention = 4
    n_neighbors = 2 * dimention
    neighbors = NearestNeighbors(n_neighbors=8)
    neighbors_fit = neighbors.fit(data)
    distances, indices = neighbors_fit.kneighbors(data)
    distances = np.sort(distances, axis=0)
    distances = distances[:,1]
    plt.plot(distances)
    plt.xlabel("Number of Records")
    plt.ylabel("Epsilon")
    plt.title("Figure 1: K Distance Diagram")
    plt.savefig("Figure 1_K Distance Diagram.png")
    print('Further Explanation And Analysis of This Diagram Is in The Report.\n')
    plt.show()
    print('......Figure 1: K Distance Diagram Generated......\n')

    # This part is for part c.2, statistics of clustering.
    print('......Generating Figure 2: Histogram of Clusters......\n')
    plt.bar(cluster, frequency, width=0.5)
    plt.xlabel("Clusters")
    plt.ylabel("Frequency")
    plt.title("Figure 2: Histogram of Clusters")
    plt.savefig("Figure 2_Histogram of Clusters.png")
    print('Further Explanation And Analysis of This Histogram Is in The Report.\n')
    plt.show()
    print('......Figure 2: Histogram of Clusters Generated......\n')

if __name__ == '__main__':
    print('......This Is The Beginning of Assignment 2......\n')
    main()
    print('......This Is The End of Assignment 2......\n')


#!/usr/bin/env python
# coding: utf-8
# Theoretically, a distribution of a statistical dataset shows us the frequency at which possible values occur within an interval.
#
# There are many statistical distributions, and the most popular ones have their own names. In this notebook file, we will focus on one of them - the normal distribution, also called Gaussian distribution.
#
# The examples we show will best clarify the concept.
# To begin, import NumPy, Matplotlib Pyplot, the "norm" module from "scipy.stats", and matplotlib inline.
# In[1]:
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
get_ipython().run_line_magic('matplotlib', 'inline')
# In one of our earlier lectures, we learned how to create random numbers with np.random.random() and np.random.randint(). This time, we will use NumPy to create a random normal distribution:
# In[2]:
normal_d = np.random.normal(10,5,40)
normal_d
# In parentheses, we specified the mean value around which the distribution will be centered. We chose it to be 10. Next, the standard deviation will be equal to 5. This is why, when you skim through the numbers within the array, you will see they are all around 10 and rarely less than 5 or greater than 15.

#
# The last argument within the parentheses shows the number of observations generated by NumPy - 40. This is why the obtained one-dimensional array contains 40 values. Great!
# ***
# A specific case of the normal distribution is the standard normal distribution. This is a special case when the mean value of the distribution is 0 and the standard deviation equals 1.

#
# NumPy provides a tool for generating random numbers from a standard normal distribution. This time, you need not specify a mean and a standard deviation value in parentheses. Typing a single argument of 40 in parentheses will provide a one-dimensional array with 40 elements.
# In[3]:
std_norm = np.random.standard_normal(40)
std_norm
# If you would like to create a two-dimensional array with standard-normally distributed values, double the parentheses and designate the dimension in the following way:
# In[4]:
std_norm_2 = np.random.standard_normal((4,10))
std_norm_2
# *************
# Now, let's see how the obtained std_norm result looks when plotted on a graph.
# In[5]:
plt.plot(std_norm)
plt.show()
# This is a line chart. The line connects all the points obtained in the *std_norm* array, reading the numbers from left to right and from top to bottom.

#
# You can see all the values are around 0, and they rarely leave the interval [-1;1].

# *************
# The next graph shows you the probability with which the data points from a designated range are expected to occur. Hence, the function we will use is called probability density function, and it best exhibits the characteristics of a certain distribution. The probability density function of the normal distribution is contained in the 'scipy.stats' module.

#
# First, we must set the interval within which we would like to distribute our data. The arguments, -5 and 5, designate the end points of the range. The third argument, 0.01, specifies the distance between two adjacent values.
# In[6]:
range = np.arange(-5, 5, 0.01)
# Finally, we will plot data from this range with a mean of 0 and a standard deviation of 1.
# In[7]:
plt.plot(range, norm.pdf(range, 0, 1))
# The obtained curve is informally referred to as a bell-curve. It corresponds to the shape of any normal distribution.

#
# A typical feature of this distribution is its bell-curve is symmetrical. This means most observations will equal the mean value of the distribution, and there will be as many observations below the mean as there will be above it. The mean value is in the center of the graph. That the two ends of the curve are closer to the x-axis is explained by the rare occurrence of observations that largely deviate from the center.
# ***
# The normal distribution is important for statisticians because distributing a dataset containing 250 or more observations can be approximated to a normal distribution. Therefore, this distribution is used frequently. In our next lectures, we will run tests with hundreds, and even thousands, of data points, and we will often need to apply this technique.


import numpy as np
from composes.matrix.linalg import Linalg

class RegressionLearner(object):
    """
    Implements a set of regression methods.

    Supported regression methods are least squares regression and
    ridge regression. Ridge regression can be used with generalized
    cross validation. (Hastie, Tibshirani and Friedman, Second edition,
    page 244)
    """

    def __init__(self):
        '''
        Constructor
        '''

    def has_intercept(self):
        return self._intercept

class LstsqRegressionLearner(RegressionLearner):
    """
    This class performs Least Squares Regression.

    It finds the matrix X which solves:

    :math:`X = argmin(||AX - B||_2)`

    It can be used with intercept or without (by default intercept=True).

    """

    def __init__(self, intercept=True):
        self._intercept = intercept

    def train(self, matrix_a, matrix_b):
        return Linalg.lstsq_regression(matrix_a, matrix_b, self._intercept)

class RidgeRegressionLearner(RegressionLearner):
    """
    This class performs Ridge Regression.

    It finds the matrix X which solves:

    :math:`X = argmin(||AX - B||_2 + \\lambda||X||_2)`

    It can be used with intercept or without (by default intercept=True).
    Cross validation can be used with default :math:`\\lambda` range of
    :math:`linspace(0, 5, 11)`. By default Generalized cross validation is performed.
    If cross validation is set False it requires the input of a :math:`\\lambda` value.

    """

    def __init__(self, intercept=True, param_range=None, crossvalidation=True, param=None):
        self._intercept = intercept
        self._param_range = param_range if param_range is not None else np.linspace(0.0, 5, 11)

        self._param = param
        self._crossvalidation = crossvalidation

        if param:
            self._crossvalidation = False
            self._param = param

        if not self._crossvalidation and self._param is None:
            raise ValueError("Cannot run (no-crossvalidation) RidgeRegression with no lambda value!")

    def train(self, matrix_a, matrix_b):
        """
        If cross validation is set to True, it performs generalized
        cross validation. (Hastie, Tibshirani and Friedman, Second edition,
        page 244).
        """

        if not self._crossvalidation:
            return Linalg.ridge_regression(matrix_a, matrix_b, self._param,
                                           self._intercept)[0]

        else:
            min_err_param = 0
            min_err = np.Inf
            gcv_err = np.Inf

            N = matrix_a.shape[0]
            for param in self._param_range:

                mat_x, S_trace, err1 = Linalg.ridge_regression(matrix_a, matrix_b, param,
                                                               self._intercept)

                nom = pow(1 - S_trace / N, 2) * N
                if nom != 0:
                    gcv_err = (err1 * err1) / nom

                if gcv_err < min_err:
                    min_err = gcv_err
                    min_err_param = param

            #print "lambda:", min_err_param
            return Linalg.ridge_regression(matrix_a, matrix_b, min_err_param,
                                           self._intercept)[0]


import numpy as np
from scipy.linalg import eigh
import matplotlib.pyplot as plt
# from matplotlib.pyplot import legend

# k1,k2,k3,k4,k5,k6 = 1.,1.,1.,1.,1.,1.
# m1,m2,m3,m4,m5 = 1.,1.,1.,1.,1.
m=10
k=1

K = np.array([[k+k,-k,0,0,0],[-k,k+k,-k,0,0],[0,-k,k+k,-k,0],[0,0,-k,k+k,-k],[0,0,0,-k,k+k]])
M = np.array([[m,0,0,0,0],[0,m,0,0,0],[0,0,m,0,0],[0,0,0,m,0],[0,0,0,0,m]])
eigv,eigvec = eigh(K,M)
print('\u03BB =', eigv)
print(eigvec)

plt.plot(eigv,eigvec,'*-')
plt.ylabel('Eigenvectors')
plt.xlabel('Eigenvalues')
plt.suptitle('Mode Shapes')
plt.legend(['First Mode', 'Second Mode','Third Mode', 'Fourth Mode','Fifth Mode'],loc="lower center",bbox_to_anchor=(1.2, 0), ncol= 1)
# plt.legend(loc="lower center", bbox_to_anchor=(0.1, -0.15), ncol= 2)

plt.show()

#%%
k = 1
m1,m2,m3,m4,m5 = 10.,10.,5.,2.,2.

K1 = np.array([[k+k,-k,0,0,0],[-k,k+k,-k,0,0],[0,-k,k+k,-k,0],[0,0,-k,k+k,-k],[0,0,0,-k,k+k]])
M1 = np.array([[m1,0,0,0,0],[0,m2,0,0,0],[0,0,m3,0,0],[0,0,0,m4,0],[0,0,0,0,m5]])
eigv1,eigvec1 = eigh(K1,M1)
print('\u03BB =', eigv1)
print(eigvec1)

plt.plot(eigv1,eigvec1,'*-')
plt.ylabel('Eigenvectors')
plt.xlabel('Eigenvalues')
plt.suptitle('Mode Shapes')
plt.legend(['First Mode', 'Second Mode','Third Mode', 'Fourth Mode','Fifth Mode'],loc="lower center",bbox_to_anchor=(1.2, 0), ncol= 1)
# plt.legend(loc="lower center", bbox_to_anchor=(0.1, -0.15), ncol= 2)

#%%

# x=(int(eigv[0:5]))
# u=np.transpose(x)
x = np.array([0.0267949,0.1,0.2,0.3,0.373205])
u = x.transpose()


# use the pca to evaluate the registration results
#  -*- coding: utf-8 -*
import numpy as np
import ti_curve as tc
import SimpleITK as sitk
import pylab
import cv2
#零均值化
def zeroMean(dataMat):
    meanVal=np.mean(dataMat,axis=0)     #按列求均值，即求各个特征的均值
    newData=dataMat-meanVal
    return newData,meanVal

def pca(dataMat,n):
    newData,meanVal=zeroMean(dataMat)
    covMat=np.cov(newData,rowvar = False)    #求协方差矩阵,return ndarray；若rowvar非0，一列代表一个样本，为0，一行代表一个样本

    eigVals,eigVects=np.linalg.eig(np.mat(covMat))#求特征值和特征向量,特征向量是按列放的，即一列代表一个特征向量
    eigValIndice=np.argsort(eigVals)            #对特征值从小到大排序
    n_eigValIndice=eigValIndice[-1:-(n+1):-1]   #最大的n个特征值的下标
    n_eigVect=eigVects[:,n_eigValIndice]        #最大的n个特征值对应的特征向量
    lowDDataMat=newData*n_eigVect               #低维特征空间的数据
    reconMat=(lowDDataMat*n_eigVect.T)+meanVal  #重构数据
    return lowDDataMat,reconMat

def pca_eig(dataMat):
    newData,meanVal=zeroMean(dataMat)
    covMat=np.cov(newData,rowvar = False)    #求协方差矩阵,return ndarray；若rowvar非0，一列代表一个样本，为0，一行代表一个样本

    eigVals,eigVects=np.linalg.eig(np.mat(covMat))#求特征值和特征向量,特征向量是按列放的，即一列代表一个特征向量
    eigValIndice=np.argsort(eigVals)            #对特征值从小到大排序
    return eigValIndice

def draw_hist(dataMat):
    print("under edited")

if __name__=="__main__":
    mask_dir = '/home/cai/Documents/registration/test/input/mask.mha'
    mask_image = np.array(sitk.GetArrayFromImage(sitk.ReadImage(mask_dir)))[0,:,:]

    input_dir = '/home/cai/Documents/registration/test/out/lung/IM_1061/images'
    image_array = tc.getDataFrom_mhd(input_dir,key= 'result',mask = mask_image)

    new_data,mean = zeroMean(image_array.T)
    #eig = pca_eig(new_data)
    U,S,V = np.linalg.svd(new_data,full_matrices=False)
    eig = np.array(sorted(abs(S),reverse = True))
    x = (eig/np.sum(eig))
    print( x.round(3))
    input_dir = '/home/cai/Documents/registration/test/out/lung/my_method/images'
    image_array = tc.getDataFrom_mhd(input_dir,key= 'result',mask = mask_image)

    new_data,mean = zeroMean(image_array.T)
    #eig = pca_eig(new_data)
    U,S,V = np.linalg.svd(new_data,full_matrices=False)
    eig2 = np.array(sorted(abs(S),reverse = True))
    x = eig2/np.sum(eig2)
    print(x.round(3))


import numpy as np
import copy
# formula for capacitance: epsilon*4*w(energy)/V^2

def S_matrices(x1, x2, x3, x4, y1, y2, y3, y4):
    S1 = np.zeros(shape=(3, 3))
    S2 = np.zeros(shape=(3, 3))
    A = 1 / 2 * (x3 - x1) * (y1 - y2)
    # print(A)  # non-zero, which is good
    x5 = x1
    x6 = x3
    y5 = y1
    y6 = y3

    # the matrix responsible for the first triangle
    S1[0][0] = 1 / (4 * A) * ((y2 - y3) * (y2 - y3) + (x3 - x2) * (x3 - x2)) # used to be (y2 - y3) - (x3 - x2)
    S1[0][1] = 1 / (4 * A) * ((y2 - y3) * (y3 - y1) + (x3 - x2) * (x1 - x3))
    S1[0][2] = 1 / (4 * A) * ((y2 - y3) * (y1 - y2) + (x3 - x2) * (x1 - x2))

    S1[1][0] = copy.deepcopy(S1[0][1])
    S1[1][1] = 1 / (4 * A) * ((y3 - y1) * (y3 - y1) + (x1 - x3) * (x1 - x3)) # used to be  (y3 - y2)
    S1[1][2] = 1 / (4 * A) * ((y3 - y1) * (y3 - y1) + (x1 - x3) * (x1 - x2))

    S1[2][0] = copy.deepcopy(S1[0][2])
    S1[2][1] = copy.deepcopy(S1[1][2])
    S1[2][2] = 1 / (4 * A) * ((y1 - y2) * (y1 - y2) + (x1 - x2) * (x1 - x2))
    # print(S1)
    # The matrix responsible for the second triangle
    S2[0][0] = 1 / (4 * A) * ((y5 - y6) * (y5 - y6) + (x6 - x5) * (x6 - x5)) # used to be (y5 - y6) - (x6 - x5)
    S2[0][1] = 1 / (4 * A) * ((y5 - y6) * (y6 - y4) + (x6 - x5) * (x4 - x6))
    S2[0][2] = 1 / (4 * A) * ((y5 - y6) * (y4 - y5) + (x6 - x5) * (x4 - x5))

    S2[1][0] = copy.deepcopy(S2[0][1])
    S2[1][1] = 1 / (4 * A) * ((y6 - y4) * (y6 - y5) + (x4 - x6) * (x4 - x6))
    S2[1][2] = 1 / (4 * A) * ((y6 - y4) * (y6 - y4) + (x4 - x6) * (x4 - x5))

    S2[2][0] = copy.deepcopy(S2[0][2])
    S2[2][1] = copy.deepcopy(S2[1][2])
    S2[2][2] = 1 / (4 * A) * ((y4 - y5) * (y4 - y5) + (x4 - x5) * (x4 - x5))

    Sdis = np.zeros(shape=(6, 6))
    # concatenate these two matrices
    Sdis[0][0] = copy.deepcopy(S1[0][0])
    Sdis[0][1] = copy.deepcopy(S1[0][1])
    Sdis[0][2] = copy.deepcopy(S1[0][2])

    Sdis[1][0] = copy.deepcopy(S1[1][0])
    Sdis[1][1] = copy.deepcopy(S1[1][1])
    Sdis[1][2] = copy.deepcopy(S1[1][2])

    Sdis[2][0] = copy.deepcopy(S1[2][0])
    Sdis[2][1] = copy.deepcopy(S1[2][1])
    Sdis[2][2] = copy.deepcopy(S1[2][2])

    Sdis[3][3] = copy.deepcopy(S2[0][0])
    Sdis[3][4] = copy.deepcopy(S2[0][1])
    Sdis[3][5] = copy.deepcopy(S2[0][2])

    Sdis[4][3] = copy.deepcopy(S2[1][0])
    Sdis[4][4] = copy.deepcopy(S2[1][1])
    Sdis[4][5] = copy.deepcopy(S2[1][2])

    Sdis[5][3] = copy.deepcopy(S2[2][0])
    Sdis[5][4] = copy.deepcopy(S2[2][1])
    Sdis[5][5] = copy.deepcopy(S2[2][2])

    C = np.zeros(shape=(6, 4))
    C[0][0] = 1
    C[1][1] = 1
    C[2][2] = 1
    C[3][3] = 1
    C[4][0] = 1
    C[5][2] = 1
    # print(C)
    Sg = np.matmul(np.transpose(C), np.matmul(Sdis, C))

    return [Sg, S1, S2]

if __name__ == "__main__":
    x1, y1 = (0, 0.02)
    x2, y2 = (0, 0)
    x3, y3 = (0.02, 0)
    x4, y4 = (0.02, 0.02)

    mat = S_matrices(x1, x2, x3, x4, y1, y2, y3, y4)
    print(mat)


#!/usr/bin/env python
import numpy as np
from typing import Callable
import apriori_error
import integrate
import runge
import richardson
import adaptive

def rectangle(a: float, b: float, f: Callable[[np.array], np.array], h: float,
        I_True: float, M_2: float) -> None:
	while runge.rectangle(a, b, f, h) > eps:
		h /= 2

	h /= 2

	I_h, I_half_h, I_richardson = integrate.rectangle(a, b, f, h), \
		integrate.rectangle(a, b, f, h / 2), richardson.rectangle(a, b, f, h)

	I_adaptive = adaptive.rectangle(a, b, f, eps)

	print(
		f'\nRECTANGLE:\n'
		f'\th                 = {h}\n'
		f'\tI_true            = {I_true}\n'
		f'\tI_h               = {I_h}\n'
		f'\tR_h_true          = {abs(I_true - I_h)}\n'
		f'\tI_half_h          = {I_half_h}\n'
		f'\tR_half_h_true     = {abs(I_true - I_half_h)}\n'
		f'\tR_runge           = {runge.rectangle(a, b, f, h)}\n'
		f'\tI_richardson      = {I_richardson}\n'
		f'\tR_richardson_true = {abs(I_true - I_richardson)}\n'
		f'\tapriori_error     = {apriori_error.rectangle(a, b, M_2, h)}\n'
		f'\tI_apadtive        = {I_adaptive}\n'
		f'\tR_apadtive        = {abs(I_true - I_adaptive)}\n'
	)

def trapezoid(a: float, b: float, f: Callable[[np.array], np.array], h: float,
        I_True: float, M_2: float) -> None:
	while runge.trapezoid(a, b, f, h) > eps:
		h /= 2

	h /= 2

	I_h, I_half_h, I_richardson = integrate.trapezoid(a, b, f, h), \
		integrate.trapezoid(a, b, f, h / 2), richardson.trapezoid(a, b, f, h)

	I_adaptive = adaptive.trapezoid(a, b, f, eps)

	print(
		f'TRAPEZOID:\n'
		f'\th                 = {h}\n'
		f'\tI_true            = {I_true}\n'
		f'\tI_h               = {I_h}\n'
		f'\tR_h_true          = {abs(I_true - I_h)}\n'
		f'\tI_half_h          = {I_half_h}\n'
		f'\tR_half_h_true     = {abs(I_true - I_half_h)}\n'
		f'\tR_runge           = {runge.trapezoid(a, b, f, h)}\n'
		f'\tI_richardson      = {I_richardson}\n'
		f'\tR_richardson_true = {abs(I_true - I_richardson)}\n'
		f'\tapriori_error     = {apriori_error.trapezoid(a, b, M_2, h)}\n'
		f'\tI_apadtive        = {I_adaptive}\n'
		f'\tR_apadtive        = {abs(I_true - I_adaptive)}\n'
	)

def simpson(a: float, b: float, f: Callable[[np.array], np.array], h: float,
        I_True: float, M_4: float) -> None:
	while runge.simpson(a, b, f, h) > eps:
		h /= 2

	h /= 2

	I_h, I_half_h, I_richardson = integrate.simpson(a, b, f, h), \
		integrate.simpson(a, b, f, h / 2), richardson.simpson(a, b, f, h)

	I_adaptive = adaptive.simpson(a, b, f, eps)

	print(
		f'SIMPSON:\n'
		f'\th                 = {h}\n'
		f'\tI_true            = {I_true}\n'
		f'\tI_h               = {I_h}\n'
		f'\tR_h_true          = {abs(I_true - I_h)}\n'
		f'\tI_half_h          = {I_half_h}\n'
		f'\tR_half_h_true     = {abs(I_true - I_half_h)}\n'
		f'\tR_runge           = {runge.simpson(a, b, f, h)}\n'
		f'\tI_richardson      = {I_richardson}\n'
		f'\tR_richardson_true = {abs(I_true - I_richardson)}\n'
		f'\tapriori_error     = {apriori_error.simpson(a, b, M_4, h)}\n'
		f'\tI_apadtive        = {I_adaptive}\n'
		f'\tR_apadtive        = {abs(I_true - I_adaptive)}\n'
	)

if __name__ == '__main__':
	def f(t):
		return 3 * t * np.log(2 + t)

	a, b = -1, 1
	I_true = 6 - 9 / 2 * np.log(3)

	M_2, M_4 = 9, 42

	h = b - a
	eps = 1e-5

	rectangle(a, b, f, h, I_true, M_2)
	trapezoid(a, b, f, h, I_true, M_2)
	simpson(a, b, f, h, I_true, M_4)


import sys, os
sys.path.insert(0, os.path.join(os.pardir, 'src'))
import sympy as sym
import mpmath

def least_squares_orth(f, psi, Omega, symbolic=True,
                       print_latex=False):
    """
    Given a function f(x,y) on a rectangular domain
    Omega=[[xmin,xmax],[ymin,ymax]],
    return the best approximation to f(x,y) in the space V
    spanned by the functions in the list psi.
    This function assumes that psi are orthogonal on Omega.
    """
    # Modification of least_squares function: drop the j loop,
    # set j=i, compute c on the fly in the i loop.

    N = len(psi) - 1
    # Note that A, b, c becmes (N+1)x(N+1), use 1st column
    A = sym.zeros(N+1)
    b = sym.zeros(N+1)
    c = sym.zeros(N+1)
    x, y = sym.symbols('x y')
    print(('...evaluating matrix...', A.shape, b.shape, c.shape))
    for i in range(N+1):
        j = i
        print(('(%d,%d)' % (i, j)))

        integrand = psi[i]*psi[j]
        if symbolic:
            I = sym.integrate(integrand,
                             (x, Omega[0][0], Omega[0][1]),
                             (y, Omega[1][0], Omega[1][1]))
        if not symbolic or isinstance(I, sym.Integral):
            # Could not integrate symbolically, use numerical int.
            print(('numerical integration of', integrand))
            integrand = sym.lambdify([x,y], integrand, 'mpmath')
            I = mpmath.quad(integrand,
                            [Omega[0][0], Omega[0][1]],
                            [Omega[1][0], Omega[1][1]])
        A[i,0] = I

        integrand = psi[i]*f
        if symbolic:
            I = sym.integrate(integrand,
                             (x, Omega[0][0], Omega[0][1]),
                             (y, Omega[1][0], Omega[1][1]))
        if not symbolic or isinstance(I, sym.Integral):
            # Could not integrate symbolically, use numerical int.
            print(('numerical integration of', integrand))
            integrand = sym.lambdify([x,y], integrand, 'mpmath')
            I = mpmath.quad(integrand,
                            [Omega[0][0], Omega[0][1]],
                            [Omega[1][0], Omega[1][1]])
        b[i,0] = I
        c[i,0] = b[i,0]/A[i,0]
    print()
    print(('A:\n', A, '\nb:\n', b))
    c = [c[i,0] for i in range(c.shape[0])]  # make list
    print(('coeff:', c))

    # c is a sympy Matrix object, numbers are in c[i,0]
    u = sum(c[i]*psi[i] for i in range(len(psi)))
    print(('approximation:', u))
    print(('f:', sym.expand(f)))
    if print_latex:
        print((sym.latex(A, mode='plain')))
        print((sym.latex(b, mode='plain')))
        print((sym.latex(c, mode='plain')))
    return u, c

def sine_basis(Nx, Ny):
    """
    Compute basis sin((p+1)*pi*x)*sin((q+1)*pi*y),
    p=0,...,Nx, q=0,...,Ny.
    """
    x, y = sym.symbols('x y')
    psi = []
    for q in range(0, Ny+1):
        for p in range(0, Nx+1):
            r = sym.sin((p+1)*sym.pi*x)*sym.sin((q+1)*sym.pi*y)
            psi.append(r)
    return psi

def test_least_squares_orth():
    # Use sine functions
    x, y = sym.symbols('x y')
    N = 2  # (N+1)**2 = 9 basis functions
    psi = sine_basis(N, N)
    f_coeff = [0]*len(psi)
    f_coeff[3] = 2
    f_coeff[4] = 3
    f = sum(f_coeff[i]*psi[i] for i in range(len(psi)))
    # Check that u exactly reproduces f
    u, c = least_squares_orth(f, psi, Omega=[[0,1], [0,1]],
                              symbolic=False)
    import numpy as np
    diff = np.abs(np.array(c) - np.array(f_coeff)).max()
    print(('diff:', diff))
    tol = 1E-15
    assert diff < tol

def demo(N):
    """
    Find the approximation of f by the least squares method.
    The basis is sin((p+1)*pi*x)sin((q+1)*pi*y) where
    0<p<=N, p<q<=N.
    """
    x, y = sym.symbols('x y')
    f = x*(1-x)*y*(1-y)*sym.exp(-x-y)

    psi = sine_basis(N, N)

    Omega = [[0,1], [0,1]]
    u, c  = least_squares_orth(f, psi, Omega, symbolic=False)
    from approx2D import comparison_plot
    comparison_plot(f, u, Omega, title='N=%d' % N)
    print(c)

if __name__=='__main__':
    #test_least_squares_orth()
    demo(N=2)


import random
import numpy as np
import numpy.random

def softmax(X):
    """Calculates softmax row-wise"""
    if X.ndim == 1:
        X = X - np.max(X)
        e = np.exp(X)
        return e/e.sum()
    else:
        X = X - np.max(X,1)[:,np.newaxis]
        e = np.exp(X)
        return e/e.sum(1)[:,np.newaxis]

def inverse_softmax(X):
    """Calculates inverse of softmax row-wise"""
    if X.ndim == 1:
        t = np.log(X)
        return t - t.min()
    else:
        t = np.log(X)
        return t - t.min(1)[:,np.newaxis]

def j_softmax(x):
    x = np.asarray(x)
    e = np.exp(x)
    s = np.sum(e)
    return np.diag(e)/s - np.outer(e,e)/s**2

def bp_softmax(do, x):
    return j_softmax(x).dot(do)

def bp_row_softmax(dW, W):
    o = np.zeros_like(W)
    for i in xrange(W.shape[0]):
        o[i,:] = bp_softmax(dW[i,:], W[i,:])
    return o

def normalize(x):
    return x / x.sum()

def normalize_rows(X):
    return X / X.sum(1)[:,np.newaxis]

def j_normalize(x):
    D = len(x)
    s = np.sum(x)
    return np.eye(D)*1/s - np.ones((D,D))*x/s**2

def bp_normalize(do, x):
    return j_normalize(x).dot(do)

def logsumexp(X, axis = 1):
    """Calculates logsumexp row-wise"""
    if X.ndim == 1:
        m = np.max(X)
        return  m + np.log(np.sum(np.exp(X-m)))
    if X.ndim == 2:
        if axis == 0:
            m = np.max(X, 0)
            return  m + np.log(np.sum(np.exp(X-m[np.newaxis,:]), axis=0))
        elif axis == 1:
            m = np.max(X, 1)
            return  m + np.log(np.sum(np.exp(X-m[:,np.newaxis]), axis=1))
    if X.ndim == 3:
        if axis == 0:
            m = np.max(X, 0)
            return  m + np.log(np.sum(np.exp(X-m[np.newaxis,:,:]), axis=0))
        elif axis == 1:
            m = np.max(X, 1)
            return  m + np.log(np.sum(np.exp(X-m[:,np.newaxis,:]), axis=1))
        elif axis == 2:
            m = np.max(X, 2)
            return  m + np.log(np.sum(np.exp(X-m[:,:,np.newaxis]), axis=2))
    else:
        raise "Not implemented"

def random_component(component_probabilities):
    r = np.random.random(1)[0]
    accum = 0.0
    for i,p in enumerate(component_probabilities):
        accum += p
        if r <= accum:
            return i

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

def relax_matrix(X, k=0.01):
    props = softmax(numpy.random.rand(*X.shape))
    return X * (1.0-k) + k * props


# Find the point of diminishing returns (x,y) for the function R(x),
# where R(x) represents revenue (in thousands of dollars) and x represents the amount spent on
# advertising (in thousands of dollars).

# R(x) = 11,000 - x^3 + 42x^2 + 800x,
#  0 <= x <= 20

from sympy import solve, Limit, lambdify, symbols, diff, ln, discriminant
import matplotlib.pyplot as plt
import numpy as np

domain_end = 20

g_xlim = [-5, 40]
g_ylim = [-5, 70]

# eq

x = symbols('x', positive = True ) # 0 <= x
R = 11000 - x ** 3 + 42 * x ** 2 + 800 * x # Revenue

lam_x = lambdify( x, R, np )

x_vals = np.linspace( g_xlim[0], g_xlim[1], 1000, endpoint=True )
y_vals = lam_x( x_vals )

plt.plot( x_vals, y_vals, label = 'Revenue' )

# Inflection Point, second derivative, ( Green )

dR = diff( R, x, 2 )
inflection_point = solve( dR, x )[ 0 ]
inflection_value = R.subs( { x: inflection_point } )

section = np.arange( 0, int( inflection_point ), 1/20.)
plt.fill_between( section,lam_x(section), alpha = .4 )

plt.vlines( x = inflection_point, ymin = 0, ymax = inflection_value, color='G', zorder = 1 )
plt.hlines( y = inflection_value, xmin = 0, xmax = inflection_point, color = 'G', zorder = 1 )
plt.text( 0, inflection_value + 2000, 'Inflection Point: {0}'.format( inflection_value ) )

# Domain End (Black)
plt.vlines( x = domain_end, ymin = 0, ymax = R.subs( { x: domain_end } ), color='Black', zorder=2)
plt.text( domain_end + 1, 1000, 'Domain Bounds' )

# Linear Domain

plt.xlabel('ROI')
plt.ylabel('Advertising $(k)')
plt.legend()
plt.show()


"""
Given two 2D polygons write a function that calculates the IoU of their areas,
defined as the area of their intersection divided by the area of their union.
The vertices of the polygons are constrained to lie on the unit circle and you
can assume that each polygon has at least 3 vertices, given and in sorted order.

- You are free to use basic math functions/libraries (sin, cos, atan2, numpy etc)
  but not geometry-specific libraries (such as shapely).
- You are free to look up geometry-related formulas, optionally copy paste in
  short code snippets and adapt them to your needs.
- We do care and evaluate your general code quality, structure and readability
  but you do not have to go crazy on docstrings.
"""
import numpy as np
from math import atan2, sqrt

def computeLine(p1, p2):

    """
    Computing the lines parameters m and b given two points

    """

    dx = p1[0] - p2[0]
    return (p1[1] - p2[1]) / dx, (p1[0] * p2[1] - p2[0] * p1[1]) / dx

def cleanPoly(poly):

    """
    Removing duplicate points from polygon list of vertices

    """

    cleanedPoly = []
    for point in poly:
        if isNotInList(point, cleanedPoly):
            cleanedPoly.append(point)

    return cleanedPoly

def computeIntersection(p11, p12, p21, p22, tol=1e-6):

    """
    Computing intersection of two lines

    """

    # Compute difference in x co-ordinates for two lines

    dx1, dx2 = p11[0] - p12[0], p21[0] - p22[0]

    # If both difference in x are below tolerance, the lines are vertical and parallel => no intersection

    if abs(dx1) < tol and abs(dx2) < tol:
        return None

    # If just the first line difference in x is below tolerance, the first line is vertical
    elif abs(dx1) < tol:
            x = (p11[0] + p12[0])/2 # x co-ordinate of intersection
            m2, b2 = computeLine(p21, p22) # get second line parameters
            return x, m2 * x + b2

    # If just the second line difference in x is below tolerance, the second line is vertical
    elif abs(dx2)< tol:
            x = (p21[0] + p22[0]) / 2 # x co-ordinate of intersection
            m1, b1 = computeLine(p11, p12) # get first line parameters
            return x, m1 * x + b1

    # If none of the differences in x difference is below tolerance, none of the line is vertical

    else:
            m1,b1 = computeLine(p11, p12) # get first line parameters
            m2,b2 = computeLine(p21, p22) # get second line parameters
            dm = m1 - m2 # difference in slope

            # if difference in slope is below tolerance, the two lines are parallel i.e. no intersection
            if abs(dm) < tol:
                return None
            # else, compute intersection x, y
            else:
                return (b2 - b1) / dm, (m1 * b2 - b1 * m2) / dm

def distanceBetween(p1, p2):

    """
    Euclidean distance between two points

    """

    return sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)

def iou(poly1, poly2):

    """
    Computing intersection over union for given polygon

    """

    poly1, poly2 = cleanPoly(poly1), cleanPoly(poly2) # clean polygons of duplicate points
    poly3 = polyIntersection(poly1, poly2) # compute intersected polygon

    # if intersection exists (its a convex polygon with atleat 3 vertices)

    if poly3:
        # converting polygons to np arrays

        poly1 = np.array(poly1, dtype = np.float32)
        poly2 = np.array(poly2, dtype = np.float32)
        poly3 = np.array(poly3, dtype = np.float32)

        # computing area of intersection
        intersectionArea = polyArea(poly3[:,0], poly3[:,1])

        # computing area of union (= area of polygon - area of intersection)
        unionArea = polyArea(poly1[:,0], poly1[:,1]) + polyArea(poly2[:,0], poly2[:,1]) - intersectionArea

        # IoU = area of intersection / area of union
        return intersectionArea / unionArea

    # else, ploygons do not intersect i.e. IoU = 0.0
    else:

        return 0.0

def isNotInList(point, list, tol=1e-6):

    """
    Checking if the point is already in the list

    """

    for p in list:

        if distanceBetween(point, p) < tol:
            return False

    return True

def polyArea(x, y):

    """
    Computing the area of polygon using Shoeloace formula given ordered x and y coordinates of its vertices. (https://en.wikipedia.org/wiki/Shoelace_formula).

    """

    area = 0.5 * np.abs(np.dot(y, np.roll(x,1)) - np.dot(x, np.roll(y,1)))
    return area

def polyIntersection(poly1, poly2):

    """
    Computing the intersection between two polygons

    """

    intersections, orientations = [], [] # list of intersection points and respective orientations w.r.t the origin
    n1, n2 = len(poly1), len(poly2) # no. of vertices of two polygons

    # for each vertex in 1st polygon
    for i, currentVertex1 in enumerate(poly1):

        previousVertex1 = poly1[(i + n1 - 1) % n1]         # pervious vertex of 1st polygon

        # bounding box of current edge of 1st polygon
        xMax = max(currentVertex1[0], previousVertex1[0])
        xMin = min(currentVertex1[0], previousVertex1[0])
        yMax = max(currentVertex1[1], previousVertex1[1])
        yMin = min(currentVertex1[1], previousVertex1[1])

        # for each vertex in 2nd polygon
        for j, currentVertex2 in enumerate(poly2):

            previousVertex2 = poly2[(j + n2 - 1) % n2]

            # compute intersection between 2 lines of 2 polygons
            intersect = computeIntersection(currentVertex1, previousVertex1, currentVertex2, previousVertex2)

            # if intersection exists, it is in the bounding box and has not been already accounted
            if intersect:

                if xMin <= intersect[0] <= xMax and yMin <= intersect[1] <= yMax:
                    if isNotInList(intersect, intersections):

                        intersections.append(intersect) # appending it to the list
                        orientations.append(atan2(intersect[1], intersect[0])) # appending to the corresponding orientation

    # if fewer than 3 vertices
    if len(intersections) < 3:

        return None # its not a polygon (intersection is null)
    else:

        #sorting the vertices of polygon by orientation
        intesectionPoly = [x for _, x in sorted(zip(orientations, intersections))]
        return intesectionPoly

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

if __name__ == "__main__":

    cases = []
    # Case 1: a vanilla case (see https://imgur.com/a/dSKXHPF for a diagram)
    poly1 = [
        (-0.7071067811865475, 0.7071067811865476),
        (0.30901699437494723, -0.9510565162951536),
        (0.5877852522924729, -0.8090169943749476),
    ]
    poly2 = [
        (1, 0),
        (0, 1),
        (-1, 0),
        (0, -1),
        (0.7071067811865475, -0.7071067811865477),
    ]
    cases.append((poly1, poly2, "simple case", 0.12421351279682288))
    # Case 2: another simple case
    poly1 = [
        (1, 0),
        (0, 1),
        (-0.7071067811865476, -0.7071067811865476),
    ]
    poly2 = [
        (-0.1736481776669303, 0.984807753012208),
        (-1, 0),
        (0, -1),
    ]
    cases.append((poly1, poly2, "simple case 2", 0.1881047657147776))
    # Case 3: yet another simple case, note the duplicated point
    poly1 = [
        (0, -1),
        (-1, 0),
        (-1, 0),
        (0, 1),
    ]
    poly2 = [
        (0.7071067811865476, 0.7071067811865476),
        (-0.7071067811865476, 0.7071067811865476),
        (-0.7071067811865476, -0.7071067811865476),
        (0.7071067811865476, -0.7071067811865476),
        (0.7071067811865476, -0.7071067811865476),
    ]
    cases.append((poly1, poly2, "simple case 3", 0.38148713966109243))

    # Case 4: shared edge
    poly1 = [
        (-1, 0),
        (-0.7071067811865476, -0.7071067811865476),
        (0.7071067811865476, -0.7071067811865476),
        (1, 0),
    ]
    poly2 = [
        (0, 1),
        (-1, 0),
        (1, 0),
    ]
    cases.append((poly1, poly2, "shared edge", 0.0))

    # Case 5: same polygon
    poly1 = [
        (0, -1),
        (-1, 0),
        (1, 0),
    ]
    poly2 = [
        (0, -1),
        (-1, 0),
        (1, 0),
    ]
    cases.append((poly1, poly2, "same same", 1.0))

    # Case 6: polygons do not intersect
    poly1 = [
        (-0.7071067811865476, 0.7071067811865476),
        (-1, 0),
        (-0.7071067811865476, -0.7071067811865476),
    ]
    poly2 = [
        (0.7071067811865476, 0.7071067811865476),
        (1, 0),
        (0.7071067811865476, -0.7071067811865476),
    ]
    cases.append((poly1, poly2, "no intersection", 0.0))

    import time
    t0 = time.time()

    for poly1, poly2, description, expected in cases:
        computed = iou(poly1, poly2)
        print('-'*20)
        print(description)
        print("computed:", computed)
        print("expected:", expected)
        print("PASS" if abs(computed - expected) < 1e-8 else "FAIL")

    # details here don't matter too much, but this shouldn't be seconds
    dt = (time.time() - t0) * 1000
    print("done in %.4fms" % dt)


import numpy as np

def kullback_leibler(p1, p2):
    return np.sum(p1 * np.log(p1 / p2), axis=-1)

def entropy(p):
    return -np.sum(p * np.log(p), axis=-1)

def logsumexp(s):
    smax = np.amax(s, axis=-1)
    return smax + np.log(
        np.sum(np.exp(s - np.expand_dims(smax, axis=-1)), axis=-1))

def logit2proba(s):
    return np.exp(s - np.expand_dims(logsumexp(s), axis=-1))

def proba2logit(p):
    s = np.log(p)
    s -= np.mean(s, axis=-1, keepdims=True)
    return s

def test_proba2logit():
    p = np.random.dirichlet(np.ones(50), size=300)
    s = proba2logit(p)
    assert np.allclose(0, np.sum(s, axis=-1))

    q = logit2proba(s)
    assert np.allclose(1, np.sum(q, axis=-1)), q
    assert np.allclose(p, q), p - q

if __name__ == "__main__":
    test_proba2logit()


# -*- coding: utf-8 -*-
import numpy as np

def h(x, theta):
    return 1 / (1 + np.exp(-np.dot(x, theta)))

def cost(x, y, theta):
    _h = h(x, theta)
    if y == 1:
        return -np.log(_h)
    else:
        return -np.log(1 - _h)

def J(X, Y, theta):
    m, n = np.shape(X)
    loss = 0
    for k in range(m):
        loss += cost(X[k], Y[k], theta)
    return loss / m

def bgd(X, Y, alpha=0.01, epsilon=1e-6, trace=True):
    m = len(X)
    _X = np.column_stack((np.ones(m), X))
    m, n = np.shape(_X)
    theta, j1, cnt = np.ones(n), 0, 0
    Xt = _X.T

    while True:
        loss = h(_X, theta) - Y
        gradient = np.dot(Xt, loss) / m
        theta -= alpha * gradient

        j = J(_X, Y, theta)

        if trace:
            print("[ Epoch {0} ] theta = {1}, loss = {2}, error = {3})".format(cnt, theta, loss, j))

        if abs(j - j1) < epsilon:
            break
        else:
            j1 = j

        cnt += 1
    return theta

def predict2d(theta, x, y):
    data = [1, x, y]
    v = h(theta, data)
    print('predict ({0}, {1}) = {2}, z = {3}'.format(x, y, v, np.dot(theta, data)))
    return v

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

    m = 12
    x1 = np.linspace(0, 6, m + 1)
    x2 = np.linspace(0, 6, m + 1)
    X, Y = [], []

    print(x1)
    for i in range(m):
        for j in range(m):
            X.append((x1[i], x2[j]))
            if x1[i] > 5 or x2[j] > 5 or x1[i] + x2[j] > 5:
                Y.append(1)
            else:
                Y.append(0)

    for i in range(len(X)):
        c, m = 'r', 'o'
        if Y[i] == 0:
            c, m = 'b', 'x'
        plt.scatter(X[i][0], X[i][1], c=c, marker=m)

    plt.grid(True)
    plt.show()

    theta2 = bgd(X, Y, 0.5, trace=False)
    print('theta = {0}'.format(theta2))

    predict2d(theta2, 10, 10)
    predict2d(theta2, 10, 1)
    predict2d(theta2, 2, 8)
    predict2d(theta2, 5, 5)
    predict2d(theta2, 4, 4)
    predict2d(theta2, 3, 4)
    predict2d(theta2, 3, 3)
    predict2d(theta2, 2, 4)
    predict2d(theta2, 3, 2.5)
    predict2d(theta2, 2.5, 3)
    predict2d(theta2, 2.5, 2.5)
    predict2d(theta2, 3, 2)
    predict2d(theta2, 1, 1)
    predict2d(theta2, -3, -3)
    predict2d(theta2, -5, -5)
    predict2d(theta2, -10, -10)


# The following code implements the NP-complete problem 'Traveling Salesman Problem' .
# With dynamic programming approach, the algorithm is able to complete in reasonable time.
import numpy as np
import math
from itertools import combinations
from tqdm.auto import tqdm

def read_file(name):
    """Given the path/name of the text file, return the
    """
    file = open(name,'r')
    data = file.readlines()

    # Initialize the vertices list
    vertices = []

    num = int(data[0][0])
    for line in data[1:]:
        item = line.split()
        vertices.append((float(item[0]), float(item[1])))

    return vertices

def euclidean(a,b):
    """Return the euclidean distance of a(x1,y1) and b(x2,y2)
    """
    dis = np.sqrt((a[0] - b[0])**2 + (a[1]- b[1])**2)
    return dis

def n_choose_k(n,k):
    """Return the number of combinations of 'n choose k'
    """
    return int(math.factorial(n)/(math.factorial(k)*math.factorial(n-k)))

def subset(arr, k):
    """Given array arr and integer k, find the combinations of subsets. With 1 prepend.
    """
    sub = []
    a = list(combinations(arr, k))
    for i in a:
        i = tuple([1]+list(i))
        sub.append(i)
    return sub

def find_min(A, vertices, subset, j):
    """Given subset and j, find the minimum value.

    """
    # remove the last vertice.
    subset = list(subset)
    subset.remove(j)

    s_removed = subset

    if len(s_removed)>1:

        s_removed = tuple(s_removed)
        mini = min(A[(s_removed, k)] + euclidean(vertices[k-1],vertices[j-1]) for k in s_removed)

    else:
        s_removed = 1
        mini = A[(1, 1)] + euclidean(vertices[0],vertices[j-1])

    return mini

def find_min_final(A,vertices):
    """Given the whole matrix(graph), find the minimum value of traveling salesman problem.
    """

    mini = min(A[(tuple(range(1,len(vertices)+1)), j)] + euclidean(vertices[j-1], vertices[0]) for j in range(2,len(vertices)+1))

    return mini

def salesman(name):
    """Main function of traveling salesman problem.
    Given the path/name of the file, return the minimum value.
    """

    vertices = read_file(name)
    num = len(vertices)

    # Initialize the matrix
    inf = 1e6
    A = {}
    A[(1,1)] = 0
    keys = {}
    indices = []
    for i in range(1, num):
        current_keys = []
        S = subset(range(2,num+1), i)
        for j in S:
            A[(j,1)] = inf
            indices.append(j)
            current_keys.append(j)
        keys[i+1] = current_keys

    # Fill in the graph(matrix)
    for m in tqdm(range(2, num+1)):
        for key in tqdm(keys[m]):
            s = key
            #print(s)
            for j in s:
                if j !=1:
                    A[(s,j)] = find_min(A, vertices, s, j)

    # Find the minimum value
    mini = find_min_final(A, vertices)

    return mini

def main():
    mini = salesman('tsp.txt')
    print(mini)

if __name__ == '__main__':
    main()

C4: Shortest Path Revisited, NP-Complete/TSP.py


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

pi = np.pi
sin = np.sin
cos = np.cos
log = np.log
inv = np.linalg.inv

def FDM(x0,xn,n,a,b,c,f,alpha,beta):
    """
    Finite Differential Method in 1D coordination system
    ay'' + by' + c = f(x)

    alpha and beta are boundary condition at the begin and the end x0 and xn, respectively
    f(x0) = alpha, f(xn) = beta

    a(x),b(x),c(x) can be either a function of x or constants. (Scalar or Vector with the same length of x
    """

    inv = np.linalg.inv
    x,h = np.linspace(x0,xn,n,retstep = True)
    def sc2vec(a):
        if np.isscalar(a)==True:
            a = np.ones(len(x))*a
        return a
    a,b,c = list(map(sc2vec,[a,b,c]))
    #define A
    diagonal = h**2*c - 2*a
    diagonal_side = a[1:] + h*b[1:]/2
    A = np.diag(diagonal) + np.diag(diagonal_side,1) + np.diag(diagonal_side,-1)
    A = 1/h**2*A

    #define F
    F = f(x)
    F[0] = F[0] - (a[0]/h**2 - b[0]/2/h)*alpha
    F[n-1] = F[n-1] - (a[n-1]/h**2 + b[n-1]/2/h)*beta
    Y = inv(A)@F
    return Y,A,F,x


import numpy as np
from numpy import exp, sqrt, cos, pi, sin

from base_function import BaseFunction

class Ackley(BaseFunction):
    #note: this function is not smooth at the origin.  the gradient will never
    #converge in the minimizer
    target_E = 0.
    xmin = np.array([-5.,-5.])
    xmax = np.array([5.,5.])
    def getEnergy(self, coords):
        x, y = coords
        E = -20. * exp(-0.2 * sqrt(x**2 + y**2)) - \
            exp(0.5 * (cos(2. *pi * x) + cos(2. * pi * y))) + 20. + np.e
        return E

    def getEnergyGradient(self, coords):
        E = self.getEnergy(coords)
        x, y = coords
        R = sqrt(x**2 + y**2)
        term1 = -20. * exp(-0.2 * R)
        term2 = -exp(0.5 * (cos(2. *pi * x) + cos(2. * pi * y)))

        deriv1 = term1 * (-0.2 * 0.5 / R)

        dEdx = 2.* deriv1 * x  - term2 * pi * sin(2.*pi*x)
        dEdy = 2.* deriv1 * y  - term2 * pi * sin(2.*pi*y)

        return E, np.array([dEdx, dEdy])

if __name__ == "__main__":
    f = Ackley()
    f.test_potential(f.get_random_configuration())

    from base_function import makeplot2d
    makeplot2d(f)


import math
import numpy as np
from random import randint

def PrimeGen( n = 10000 ):
    sieve = np.ones ( n//2, dtype = np.bool )
    for i in range( 3, int ( n**0.5 ) + 1, 2 ):
        if sieve[ i//2 ]:
            sieve[ i * i//2::i ] = False
    return np.r_[ 2, 2 * np.nonzero ( sieve )[ 0 ][ 1:: ] + 1 ]

def lcm( p, q ):
    return ( p * q ) / math.gcd ( p,q )

def extended_gcd ( aa, bb ):
    lastremainder, remainder = abs ( aa ), abs ( bb )
    x, lastx, y, lasty = 0, 1, 1, 0
    while remainder:
        lastremainder, ( quotient, remainder) = remainder, divmod ( lastremainder, remainder )
        x, lastx = lastx - quotient*x, x
        y, lasty = lasty - quotient*y, y
    return lastremainder, lastx * ( -1 if aa < 0 else 1 ), lasty * ( -1 if bb < 0 else 1 )

def modinv ( a, m ):
    g, x, y = extended_gcd ( a, m )
    if g !=  1:
        raise ValueError
    return x % m

def mult_inverse ( e, lamda ):
    d = 0
    x1 = 0
    x2 = 1
    y1 = 1
    temp_lamda = lamda

    while e > 0:
        temp1 = temp_lamda / e
        temp2 = temp_lamda - temp1 * e
        temp_lamda = e
        e = temp2

        x = x2- temp1 * x1
        y = d - temp1 * y1

        x2 = x1
        x1 = x
        d = y1
        y1 = y

    if temp_lamda ==  1:
        return d + lamda

def keyGen ( ):

    i_p = randint( 0,20 )
    i_q = randint( 0,20 )
    while i_p == i_q:
        continue
    primes = PrimeGen ( 100 )
    p = primes[ i_p ]
    q = primes[ i_q ]

    n = p * q

    lamda_n = int ( lcm ( p - 1, q - 1 ) )
    e = randint( 1, lamda_n )

    while math.gcd ( e, lamda_n ) != 1:
        e = randint ( 1, lamda_n )

    d = modinv( e, lamda_n )

    return ( ( e, n ), ( d, n ) )

def encrypt( pk, message ):

    key, n = pk

    cipher = [ ( ord ( char ) ** key ) % n for char in message ]

    return cipher

def decrypt( pk, cipher ):

    key, n = pk

    message = [ chr( ( char ** key ) % n ) for char in cipher ]

    return ''.join ( message )


##########################
##                      ##
## Irving Gomez Mendez  ##
##   October 30, 2021   ##
##                      ##
##########################

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

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

dat = pd.read_csv('banknotes.csv')

dat

sns.pairplot(data=dat, hue='conterfeit', corner=True)

class PCA_varios_metodos:
    def __init__(self, dat):
        X = dat.drop('conterfeit', axis=1)
        self.n, self.p = X.shape
        self.X = X

    def centrar_X(self):
        self.X_centered = pd.DataFrame(StandardScaler(with_std=False).fit_transform(self.X))

    def calcula_S(self):
        self.S = self.X.cov()

    def SVD(self):
        self.u, self.d, self.v_T  = np.linalg.svd(self.X_centered, full_matrices=False)

    def verifica_SVD(self):
        return(self.u @ np.diag(self.d) @ self.v_T)

    def PCA_con_SVD(self):
        self.SVD_loads       = self.v_T.T
        self.SVD_variance_CP = self.d**2/(self.n-1)
        self.SVD_CP_UD       = (self.u @ np.diag(self.d))
        self.SVD_CP_V        = self.X_centered @ self.v_T.T

    def PCA_con_matriz_cov(self):
        eigval, eigvect    = np.linalg.eig(self.S)
        self.S_loads       = eigvect
        self.S_variance_CP = eigval
        self.S_CP          = self.X_centered @ eigvect

    def PCA_con_sklearn(self):
        pca                      = PCA(n_components = self.p)
        pca_fit                  = pca.fit_transform(self.X)
        self.sklearn_loads       = pca.components_.T
        self.sklearn_variance_CP = pca.explained_variance_
        self.skelarn_CP          = pca_fit

pca_varios = PCA_varios_metodos(dat)
pca_varios.centrar_X()
pca_varios.calcula_S()
pca_varios.SVD()

pca_varios.X_centered

pd.DataFrame(pca_varios.verifica_SVD())

## PCA through SVD
pca_varios.PCA_con_SVD()
pd.DataFrame(pca_varios.SVD_loads)
pca_varios.SVD_variance_CP
pd.DataFrame(pca_varios.SVD_CP_UD)
pca_varios.SVD_CP_V

## PCA through covariance matrix
pca_varios.PCA_con_matriz_cov()
pd.DataFrame(pca_varios.S_loads)
pca_varios.S_variance_CP
pca_varios.S_CP

pca_varios.PCA_con_sklearn()
pd.DataFrame(pca_varios.sklearn_loads)
pca_varios.sklearn_variance_CP
pd.DataFrame(pca_varios.skelarn_CP)

plt.figure(figsize=(12,10))
plt.plot(pca_varios.sklearn_variance_CP.cumsum()/pca_varios.sklearn_variance_CP.sum())

loads = pd.DataFrame(pca_varios.sklearn_loads)
loads.index = dat.drop('conterfeit', axis=1).columns

loads

plt.figure(figsize=(12,10))
sns.scatterplot(x=loads[0], y=loads[1], hue=loads.index, palette='deep', s=300)
plt.axvline(0, color='red', linestyle='--')
plt.axhline(0, color='red', linestyle='--')
plt.legend(fontsize=15)

Components = pd.DataFrame(pca_varios.skelarn_CP)

plt.figure(figsize=(12,10))
sns.scatterplot(x=Components[0], y=Components[1], hue=dat['conterfeit'], palette='deep', s=300)
plt.legend(fontsize=15)

###


import numpy as np

"""
Q:
Write a binomial tree program to calculate the put prices of Bermuda options.
For such options, early exercise is allowed only on specific dates.

Inputs:
S (stock price)
X (strike price)
r (continuously compounded annual interest rate in percentage)
s (annual volatility in percentage)
T (time to maturity in days, which of course is also an exercise date)
E (set of early exercise dates from now)
m (number of periods per day for the tree)

Output:
11.2486

Long put:MAX{X-ST,0}
X (Strike Price)
ST (represent the price of the underlying at maturity)
"""

class node:
    def __init__(self,price):
        self.price = price
        self.value = 0
        self.u_parent = None
        self.d_parent = None
        self.u_child = None
        self.d_child = None

def price(S,X,r,s,T,E,m):
    if T not in E:
        E.append(T)
    deltaT = (T/365)/(T*m)
    R = np.exp(r*deltaT)
    u = np.exp(s*np.sqrt(deltaT))
    d = 1/u
    p = (R - d) / (u-d) #Risk Neutral Probability
    root = node(S)
    BinomialTree = [[root]]

    for period in range(T*m):
        currentLevel = BinomialTree[-1]
        childLevel = [node(currentLevel[0].price * u)]
        #print(childLevel[0].price)
        for currentNode in currentLevel:
            currentNode.u_child = childLevel[-1]
            currentNode.u_child.d_parent = currentNode
            downChild = node(currentNode.price * d)
            downChild.u_parent = currentNode
            currentNode.d_child = downChild
            childLevel.append(downChild)
        BinomialTree.append(childLevel)

    total = T * m
    for levelNodes in BinomialTree[::-1]:
        if int(total/m) in E:
            for tree_node in levelNodes:
                if tree_node.u_child != None:
                    binomialValue = ( ( 1/R ) * (p * tree_node.u_child.value + (1 - p) * tree_node.d_child.value))
                else:
                    exerciseValue = max(0,X - tree_node.price)
                    binomialValue = exerciseValue
                    #print(exerciseValue)

                exerciseValue = max(0,X - tree_node.price)
                tree_node.value = max(binomialValue, exerciseValue)
        else:
            for tree_node in levelNodes:
                binomialValue = ( ( 1/R ) * (p * tree_node.u_child.value + (1 - p) * tree_node.d_child.value))
                tree_node.value = binomialValue
        total -= 1

    putPrice = root.value

    return putPrice

if __name__ == "__main__":
    S = float(input("輸入Stock Price ex:100\n"))
    X = float(input("輸入Strike price ex:110\n"))
    r = float(input("輸入Continuously compounded annual interest rate in percentage(%) ex:3\n"))/100
    s = float(input("輸入Annual volatility in percentage(%) ex:30\n"))/100
    T = int(input("輸入time to maturity in days,which of course is also an exercise date ex:60\n"))
    Temp = input("輸入set of early exercise dates from now ex:10 20 30 40 50\n").split(' ')
    E = []
    for temp in Temp:
        E.append(int(temp))
    m = int(input("輸入number of periods per day for the tree ex:5\n"))
    print("S= %f" %S)
    print("X= %f" %X)
    print("r= %f" %r)
    print("s= %f" %s)
    print("T= %d" %T)
    print("E=",E)
    print("m= %d\n" %m)

    output = price(S,X,r,s,T,E,m)
    print(output)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Feb 10 15:03:10 2022

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

f = lambda x : -.5*x+4
a = -5; b = 5;
J = int(input("what is your delta x"))

x = np.linspace(a,b,J+1)
y = f(x)

X = np.linspace(a,b,J+1)
Y = f(X)

dx = (b-a)/J
x_left = np.linspace(a,b-dx,J)
x_midpoint = np.linspace(dx/2,b - dx/2,J)
x_right = np.linspace(a+dx,b,J)

print("Partition with",J,"subintervals.")
left_riemann_summ = np.sum(f(x_left) * dx)
print("Left Riemann Sum:",left_riemann_summ)

right_riemann_summ = np.sum(f(x_right) * dx)
print("Right Riemann Sum:",right_riemann_summ)

f = lambda x : -.29*x**2 -x +12.5
a = -5; b = 5;
N = int(input("what is your delta x"))

x = np.linspace(a,b,N+1)
y = f(x)

X = np.linspace(a,b,N+1)
Y = f(X)

dx = (b-a)/N
x_left = np.linspace(a,b-dx,N)
x_midpoint = np.linspace(dx/2,b - dx/2,N)
x_right = np.linspace(a+dx,b,N)

print("Partition with",N,"subintervals.")
left_riemann_sum = np.sum(f(x_left) * dx)
print("Left Riemann Sum:",left_riemann_sum)

right_riemann_sum = np.sum(f(x_right) * dx)
print("Right Riemann Sum:",right_riemann_sum)

f = lambda x : 1+10*((x+1)*np.exp(-x**2))
a = -5; b = 5;
R = int(input("what is your delta x"))

x = np.linspace(a,b,R+1)
y = f(x)

X = np.linspace(a,b,R+1)
Y = f(X)

dx = (b-a)/R
x_left = np.linspace(a,b-dx,R)
x_midpoint = np.linspace(dx/2,b - dx/2,R)
x_right = np.linspace(a+dx,b,R)

print("Partition with",R,"subintervals.")
left_riemann_summm = np.sum(f(x_left) * dx)
print("Left Riemann Sum:",left_riemann_summm)

right_riemann_summm = np.sum(f(x_right) * dx)
print("Right Riemann Sum:",right_riemann_summm)

eq1 = ((abs(40 - left_riemann_summ))/((40+left_riemann_summ)/2))*100
print ("left sum percent difference for first equation is", eq1)

eq2 = ((abs(100.83 - left_riemann_sum))/((100.83+left_riemann_sum)/2))*100
print ("left sum percent difference for second equation is", eq2)

eq3 = ((abs(27.72 - left_riemann_summm))/((27.72+left_riemann_summm)/2))*100
print ("left sum percent difference for first equation is", eq3)

x_coordinates = [J, N, R]
y_coordinates = [eq1, eq2, eq3]
plt.xlabel('delta x value')

plt.ylabel('percentage diference')

plt.scatter(x_coordinates, y_coordinates)


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Generate the data
visualize = False
verbose = False

w0_t = 2
w1_t = 1
sigma = 1

x_t = np.arange(-100, 101, 0.1)
# x_t = np.arange(0, 10, 1)
noise = sigma * np.random.randn(x_t.size)

y_t = w1_t * x_t + w0_t
y_noise = y_t + noise

if visualize:
    plt.plot(x_t, y_t)
    plt.hold(True)
    plt.plot(x_t, y_noise)
    plt.show()

N = 400  # Number of steps of the simulation
eta = 0.01
w_0 = np.random.rand()
w_1 = np.random.rand()

print ('Random guess about w0 and w1')
print (w_0, w_1)

shuffler = np.random.choice(x_t.size, size=x_t.size, replace=False)
x_data = x_t[shuffler]
y_data = y_noise[shuffler]

# x_data = x_t
# y_data = y_t

def calculate_error(x, y, w0, w1):
    est_y = w0 + x * w1
    diff = est_y - y
    error = np.sum(diff ** 2)

    return np.sqrt(error / x.size)

# This is a cycle
for i in range(N):
    for point in shuffler:
        aux = (w_0 + w_1 * x_data[point] - y_data[point])
        w_0 = w_0 - eta * 2 * aux / x_data.size
        w_1 = w_1 - eta * 2 * x_data[point] * aux / x_data.size
        if verbose:
            print ('Estimated w0 and w1')
            print (w_0, w_1)
    error = calculate_error(x_data, y_data, w_0, w_1)
    print('error = ', error, 'at step = ', i)

# Now we need to calculate the error
print ('Estimated w0 and w1')
print (w_0, w_1)

print ('Truth values of w0 and w1')
print (w0_t, w1_t)


# Credit to Lester Leong and his "Python Risk Management: Monte Carlo Simulations"
# article which helped with starting off and understanding the material.

import datetime as dt
import numpy as np
import pandas as pd
import pandas_datareader as pdr
import matplotlib.pyplot as plt
from scipy.stats import norm

#20
np.random.seed(8) # seeding the pseudorandom number generator to compare output plots more easily
start = dt.datetime(2018,1,1)
end = dt.datetime.now()
ticker = 'INTC' # symbol for Intel Corp
time_steps = 31 # (time_steps - 1) is the amount of steps taken into the future for the simulation
sims = 100 # the number of simulations that will be run

# Setting up data in a DataFrame
# Using adjusted closing prices of Intel stock as an example
data = pd.DataFrame()
data[ticker] = pdr.DataReader(ticker, data_source='yahoo', start=start, end=end)['Adj Close']

# log returns
# pct_change is the percentage change between current and prior element
log_returns = np.log(1 + data.pct_change())

# Setting up the values necessary for the daily_returns expression
mu = log_returns.mean() # average/mean
var = log_returns.var() # variance
drift = mu - (0.5 * var) # drift
sigma = log_returns.std() # standard deviation

daily_returns = np.exp(drift.values + sigma.values * norm.ppf(np.random.rand(time_steps, sims)))

# Takes last data point as as the starting point for the simulations
initial = data.iloc[-1]
monte_list = np.zeros_like(daily_returns)
monte_list[0] = initial

# For the amount of steps taken into the future, this loop calculated the simulation data to be plotted
# Takes input price, multiplies it by the exponential value calculated in daily_returns, sets simulated price
for t in range(1, time_steps):
    monte_list[t] = monte_list[t - 1] * daily_returns[t]

# Histogram for the price frequencies, number of bins can be adjusted
plt.figure(figsize=(10, 6))
plt.hist(monte_list[1], bins=10, density=True)

# Fit a normal distribution to the first simulation point in monte_list data
# Could implement for each successive iteration in the monte_list index (monte_list[2], monte_list[3], ..., monte_list[time_steps - 1])
sim_mu, sim_sig = norm.fit(monte_list[1])

# Probability Density Function
xmin, xmax = plt.xlim() # set the xmin and xmax along the x-axis for the pdf
x = np.linspace(xmin, xmax)
p = norm.pdf(x, sim_mu, sim_sig)

# Plots of raw data; log return; Monte Carlo Simulations;
# and frequencies of the Monte Carle simulations fit to normal distribution
plt.plot(x, p, 'k') # normal distribution fit
plt.xlabel('Adjusted Closing Price')
plt.ylabel('Probability Density')
title = "Histogram for 100 Simulations of Adjusted Closing Price 1 Day into the Future\nPDF fit results: mu = %.4f,  sigma = %.4f" % (sim_mu, sim_sig)
plt.title(title)

#Adjusted Closing Price data acquired from Yahoo Finance
data.plot(figsize=(10, 6))
plt.xlabel('Date')
plt.ylabel('Adjusted Closing Price')
title = "Intel's Adjusted Closing Prices Over Time"
plt.title(title)

# log returns plot
log_returns.plot(figsize=(10, 6))
plt.xlabel('Date')
plt.ylabel('Log Return')
title = "Log Returns Over Time"
plt.title(title)

# Monte Carlo Simulations for each day into the future
plt.figure(figsize=(10, 6))
plt.plot(monte_list) # monte carlo
plt.xlabel('Days into the Future')
plt.ylabel('Adjusted Closing Price')
title = "Monte Carlo Simulations (n = 100) for Intel's Adjusted Closing Prices"
plt.title(title)
plt.show()