import tkinter
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm

## HELPER FUNCTIONS ##

def plot_decision_function(model, ax, sv=True):
    ## Create a grid to evaluate the model
    xx, yy = np.meshgrid(np.linspace(-4, 4, 100),
                         np.linspace(-4, 4, 100))
    ## Evaluate the model
    Z = model.decision_function(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    ## Plot the margin
    ax = plt.contour(xx, yy, Z, levels=[0], linewidths=2, colors='darkred')
    ax = plt.contourf(xx, yy, Z, levels=[0, Z.max()], colors='palevioletred')

    ax = plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), 0, 6), cmap=plt.cm.PuBu)

    if sv:
        ax = plt.scatter(model.support_vectors_[:, 0],
                       model.support_vectors_[:, 1],
                       s=300, linewidth=1, facecolors='none', edgecolors='black')

def plot_new_observations(X_train, X_new, anomaly, ax):
    _ = plt.scatter(X_train[:,0], X_train[:,1],
                    axes=ax, color='w', s=40, edgecolors='k',
                    label='Training data')

    _ = plt.scatter(X_new[:,0], X_new[:,1], axes=ax,
                    color='violet', s=40, edgecolors='k',
                    label='New regular observations')

    _ = plt.scatter(anomaly[:,0], anomaly[:,1], axes=ax,
                    color='gold', s=40, edgecolors='k',
                    label='New abnormal observations')

    _ = ax.legend()
    _ = ax.set_xlim([-4, 4])
    _ = ax.set_ylim([-4, 4])

n_points = 200

## generate a cluster of points for the training
np.random.seed(42)
X_train = 0.5 * np.random.randn(n_points, 2)

## Plot
fig, ax = plt.subplots(figsize=(12,8))
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax, color='w', s=40, edgecolors='k')
_ = ax.set_xlim([-4, 4])
_ = ax.set_ylim([-4, 4])

model = svm.OneClassSVM(nu=0.05, kernel="rbf", gamma=0.7)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))

## Reduce nu, i.e. weight more the slack variables
model = svm.OneClassSVM(nu=0.005, kernel="rbf", gamma=0.7)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_2 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_2, n_points))
# Training error = 5/200

"""
As we can see from the picture above, by reducing $\nu$ we are increasing the weight of the slack variables (because $C \sim 1/\nu$). This leads to a reduction of the error but increase the risk of overfitting.
"""

new_observation = 25
new_anomaly = 10

## Generate new observation from the same distribution
np.random.seed(42)
X_new = 0.5 * np.random.randn(new_observation, 2)

## Generate outliers
anomaly = np.random.uniform(low=-3, high=3, size=(new_anomaly, 2))

## Plot
fig, ax = plt.subplots(figsize=(12,8))
plot_new_observations(X_train, X_new, anomaly, ax)

y_new = model.predict(X_new)
y_anomaly = model.predict(anomaly)

err_new = y_new[y_new == -1].size
err_anomaly = y_anomaly[y_anomaly == -1].size

print("Fraction of new regular observations misclassified = {}/{}".format(err_new, new_observation))
print("Fraction of new abnormal observations correctly classified = {}/{}".format(err_anomaly, new_anomaly))

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=False)
plot_new_observations(X_train, X_new, anomaly, ax)
# Fraction of new regular observations misclassified = 0/25
# Fraction of new abnormal observations correctly classified = 10/10

# Let's try to make the problem a little bit more complex, i.e. use two clusters as positive examples

## generate two cluster for the training
np.random.seed(42)
X_train1 = 0.5 * np.random.randn(n_points//2, 2)+1.5
X_train2 = 0.5 * np.random.randn(n_points//2, 2)-1.5

X_train = np.r_[X_train1, X_train2]

## Plot
fig, ax = plt.subplots(figsize=(12,8))
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax, color='w', s=40, edgecolors='k')
_ = ax.set_xlim([-4, 4])
_ = ax.set_ylim([-4, 4])

model = svm.OneClassSVM(nu=0.06, kernel="rbf", gamma=0.5)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))

new_observation = 50
new_anomaly = 20

## Generate new observation from the same distribution
np.random.seed(42)
X_new_1 = 0.5 * np.random.randn(new_observation//2, 2)+1.5
X_new_2 = 0.5 * np.random.randn(new_observation//2, 2)-1.5
X_new = np.r_[X_new_1, X_new_2]

## Generate outliers
anomaly = np.random.uniform(low=-4, high=4, size=(new_anomaly, 2))

## Plot
fig, ax = plt.subplots(figsize=(12,8))
plot_new_observations(X_train, X_new, anomaly, ax)

y_new = model.predict(X_new)
y_anomaly = model.predict(anomaly)

err_new = y_new[y_new == -1].size
err_anomaly = y_anomaly[y_anomaly == -1].size

print("Fraction of new regular observations misclassified = {}/{}".format(err_new, new_observation))
print("Fraction of new abnormal observations correctly classified = {}/{}".format(err_anomaly, new_anomaly))

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=False)
plot_new_observations(X_train, X_new, anomaly, ax)
# Fraction of new regular observations misclassified = 2/50
# Fraction of new abnormal observations correctly classified = 18/20

# We can try to move the cluster closer

np.random.seed(42)
X_train1 = 0.5 * np.random.randn(n_points//2, 2)+0.9
X_train2 = 0.5 * np.random.randn(n_points//2, 2)-0.9

X_train = np.r_[X_train1, X_train2]

## Plot
fig, ax = plt.subplots(figsize=(12,8))
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax, color='w', s=40, edgecolors='k')
_ = ax.set_xlim([-4, 4])
_ = ax.set_ylim([-4, 4])

model = svm.OneClassSVM(nu=0.05, kernel="rbf", gamma=0.1)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))

# If we want to try to separate the two cluster we need to increase the value of gamma and $\nu$ but this increase the errors a lot.

model = svm.OneClassSVM(nu=0.3, kernel="rbf", gamma=1)
model.fit(X_train)

fig, ax = plt.subplots(figsize=(12,8))

## Plot the decision function
plot_decision_function(model, ax, sv=True)

## Add the training points
_ = plt.scatter(X_train[:,0], X_train[:,1], axes=ax,
                color='w', s=40, edgecolors='k',label='Training data')
_ = ax.legend()

## Compute the empirical error
y_train = model.predict(X_train)
err_emp_1 = y_train[y_train == -1].size
print("Training error = {}/{}".format(err_emp_1, n_points))


from sympy import *
from datetime import datetime

if __name__ == "__main__":
    """使用SymPy进行方程求解"""
    x = Symbol('x')
    # str = '100 * (1 + x)**3 - 200'
    str = '5000 * (1 + x)**1 + 5000*(1 + x)**1.6 + 5000*(1 + x)**1.2 - 15000'
    print(solve(str, x))
    # print(solve(str, x)[0].evalf())
    # print(solve(str, x))
    # print(solve(2 ** x - 4, x))


import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')

import numpy as np
from matplotlib import pyplot as plt
from scipy import sparse

def upper_bound():
    x = np.linspace(25, 200)
    y1 = 2*x**3
    y2 = (1.5)*x**3+75*x**2+250*x+30
    plt.plot(x, y2, label="f(n)")
    plt.plot(x, y1, label="2n^3")
    plt.legend(loc='upper left')
    plt.savefig('asymp_upper_bound.pdf')

def solution1_new():
    x = np.array([100, 200, 400, 800])
    y1 = np.array([.584, 1.17, 2.34, 4.66])
    y2 = np.array([.648, 2.35, 9.05, 35.7])
    y3 = np.array([.592, 2.59, 10.4, 41.2])
    y4 = np.array([.591, 3.05, 19.1, 135])
    y5 = np.array([.579, 2.59, 15.1, 95.5])
    plt.plot(x, y1, label="Function 1")
    plt.plot(x, y2, label="Function 2")
    plt.plot(x, y3, label="Function 3")
    plt.plot(x, y4, label="Function 4")
    plt.plot(x, y5, label="Function 5")
    plt.legend(loc='upper left')
    plt.savefig('complexity_problem.pdf')

def solution1():
    runtimes = [8.95, 36.7, 144, 557]
    inputs = [1000, 2000, 4000, 8000]
    plt.plot(inputs, runtimes, 'go')
    plt.savefig('prob1.pdf')

def spy_sparse():
    n = 10000
    B = np.random.rand(3, n)
    A = sparse.spdiags(B, range(-1, 2), n, n)
    plt.spy(A)
    plt.savefig('spy.pdf')

def complexitycurves():
    plt.clf()
    x = np.linspace(.01, 20, 500)
    plt.plot(x, np.log2(x)*x, label='$n\log n$')
    plt.plot(x, x, label='$n$')
    plt.plot(x, x**2, label='$n^2$')
    plt.plot(x, 2**x, label='$2^n$')
    plt.axis([0., 20., 0., 90.])
    plt.xlabel("Problem size (n)")
    plt.ylabel("Execution time")
    plt.legend(loc=2)
    plt.savefig("complexitycurves.pdf")

if __name__ == "__main__":
    spy_sparse()
    complexitycurves()
    solution1()
    upper_bound()
    solution1_new()


# https://docs.sympy.org/latest/tutorial/basic_operations.html

from sympy import *
x, y, z = symbols("x y z")

expr = cos(x) + 1
print(expr)
print(expr.subs(x, y))

print(expr.subs(x, 0))

expr = x**y
print(expr)
expr = expr.subs(y, x**y)
print(expr)
expr = expr.subs(y, x**x)
print(expr)

expr = sin(2*x) + cos(2*x)
print(expr)
print(type(expr))
print(expand_trig(expr))
print(expr.subs(sin(2*x), 2*sin(x)*cos(x)))

# "SymPy expressions are immutable"

expr = cos(x)
print(expr)
print(expr.subs(x, 0))
print(expr)
print(x)

expr = x**3 + 4*x*y - z
print(expr)
print(expr.subs([(x, 2), (y, 4), (z, 0)]))

expr = x**4 - 4*x**3 + 4*x**2 - 2*x + 3
print(expr)
replacements = [(x**i, y**i) for i in range(5) if i % 2 == 0]
print(replacements)
print(expr.subs(replacements))

# why not just do a simple loop?

replacements=[]

for i in range(5):
    if i % 2 == 0:
        replacements.append((x**i, y**i))

print(replacements)
print(expr.subs(replacements))

str_expr = "x**2 + 3*x - 1/2"
print(type(str_expr))
print(str_expr)
expr = sympify(str_expr)
print(type(expr))
print(expr)
print(expr.subs(x, 2))

expr = sqrt(8)
print(expr)
print(expr.evalf())

# substitute variable for number and
# evaluate to float

expr = cos(2*x)
print(expr)
print(expr.evalf(subs={x: 2.4}))

import numpy
a = numpy.arange(10)
expr = sin(x)
print(expr)
f = lambdify(x, expr, "numpy")
print(f(a))
print(type(f))

f = lambdify(x, expr, "math")
print(f(0.1))

def mysin(x):
    """
    My sine. Note that this is only accurate for small x.
    """
    return x

f = lambdify(x, expr, {"sin":mysin})
print(f(0.1))


import numpy as np

def euclidean_distance(a, b):
    """
    Calculate the Euclidean distance of two vectors
    """
    distance = 0
    for x, y in zip(a, b):
        distance += (x - y) ** 2

    return sqrt(distance)

def snake_distance(a, b):
    """
    Calculate the Manhattan distance of two vectors.
    """
    distance = 0
    for x, y in zip(a, b):
        distance += (x - y)

    return distance

def chebyshev_distance(a, b):
    """
    Calculate the Chebyshev distance of two vectors.
    """
    distances = []
    for x, y in zip(a, b):
        distances.append(abs(x - y))
    distance = max(distances)

    return distance

def entropy(feature_data):
    """
    Example playTennis entropy(["yes","yes","yes","yes","yes","yes","yes","yes","yes","no","no","no","no","no"])
    Calculates the entropy for a feature data.
    """
    if len(feature_data) == 0:
        return 1.02
    data_and_uniqe = np.unique(feature_data, return_inverse=True)
    pi = np.bincount(data_and_uniqe[1]).astype(np.float64)
    return np.sum(- pi/len(feature_data) * np.log2(pi/len(feature_data)))

def information_gain(sample, labels):
    """
    sample = [31, 34, 32, 20, 11, 10, 8, 23, 7, 21, 23]
    labels = [0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1]
    # we will sort the first array, and also the second one
    # labels = [1, 1, 1, 1, 1, | 0, 0, | 1, | 0, 0, 0]
    # we calculate the information gain from each split and we return the
    # higher value
    """

    global_entropy = entropy(labels)
    cardinality = float(sample.shape[0])

    indexes = np.argsort(sample)
    flag = labels[indexes][0]

    splits_indexes = {}

    for pos, element in enumerate(labels[indexes]):

        # adjacent examples with different classes, for example: 1.0 and 0.0
        if flag != element:
            mean = (sample[indexes][pos] - sample[indexes][pos-1])/2.0
            split = sample[indexes][pos - 1] + mean
            splits_indexes[pos] = str(split)
            flag = element

    # get best split
    best_gain = ('0', 0.0)

    for index in splits_indexes:

        # < less-than split
        # index is the same as cardinality of subsets
        total =+ (index/cardinality) * entropy(labels[indexes][:index])
        # >= more-equal-than split
        total += ((cardinality - index)/cardinality) * entropy(labels[indexes][index:])

        gain = global_entropy - total

        if gain > best_gain[1]:
            best_gain = (splits_indexes[index], gain)

    # a tuple of the split value and the gain
    return best_gain


# Monte Carlo Valuation of a European Option in a Black-Scholes World
# With implementation of Delta-based control variate method
# by Michal Lyskawinski
# 10/31/2016

from math import *
import numpy as np
import random
from scipy.stats import norm

def CBS(S, K, T, r, sigma,t, option):

    t2t = T-t # time to maturity
    # Calculations for the solution to BSM equation
    dplus = (1 / (sigma * sqrt(t2t))) * ((log(S / K)) + (r + ((sigma ** 2) / 2)) * t2t)
    dminus = (1 / (sigma * sqrt(t2t))) * ((log(S / K)) + (r - (sigma ** 2) / 2) * t2t)

    # Calculating price of Call and Put
    if option == 'Call':
        return S * norm.cdf(dplus) - K * exp(-r * t2t) * norm.cdf(dminus)
    elif option == 'Put':
        return K * exp(-r * t2t) * norm.cdf(-dminus) - S * norm.cdf(-dplus)

# Initialize parameters
S = 100
r = 0.06
sig = 0.2
T = 1
K = 100
N = 10
M = 100
div = 0.03  # In percentage
option = 'Call'

# Precompute constants
dt = T/N
nu = r - div - 0.5*(sig**2)
nudt = nu*dt
sigsdt = sig*sqrt(dt)
erddt = exp((r-div)*dt)

beta1 = -1

sum_CT = 0
sum_CT2 = 0

for j in range(1,M): # For each simulation

    St = S
    cv = 0

    for i in range(1,N): # For each time step
        t = (i-1)*dt
        delta = CBS(St,K,T,r,sig,t,option)
        eps = np.random.normal(0, 1)
        Stn = St*exp(nudt+sigsdt*eps)
        cv1 = cv + delta*(Stn-St*erddt)
        St = Stn

    if option == 'Call':
        CT = max(0, St - K) + beta1*cv1
        sum_CT = sum_CT + CT
        sum_CT2 = sum_CT2 + CT*CT
    elif option == 'Put':
        CT = max(0, K - St) + beta1*cv1
        sum_CT = sum_CT + CT
        sum_CT2 = sum_CT2 + CT * CT
    else:
        break

Value = sum_CT/M*exp(-r*T)
SD = sqrt((sum_CT2 - sum_CT*sum_CT/M)*exp(-2*r*T)/(M-1))
SE = SD/sqrt(M)

print('The Value of European',option,'Option is',Value)
print('The Standard Deviation of this Option is',SD)
print('The Standard Error in this case is',SE)


import numpy as np
import scipy.optimize as op

def Sigmoid(z):
    return 1/(1 + np.exp(-z))

def Gradient(theta,x,y):
    m , n = x.shape
    theta = theta.reshape((n,1))
    y = y.reshape((m,1))
    sigmoid_x_theta = Sigmoid(x.dot(theta))
    grad = ((x.T).dot(sigmoid_x_theta-y))/m
    return grad.flatten()

def CostFunc(theta,x,y):
    m,n = x.shape;
    theta = theta.reshape((n,1))
    y = y.reshape((m,1))
    term1 = np.log(Sigmoid(x.dot(theta)))
    term2 = np.log(1-Sigmoid(x.dot(theta)))
    term1 = term1.reshape((m,1))
    term2 = term2.reshape((m,1))
    term = y * term1 + (1 - y) * term2
    J = -((np.sum(term))/m)
    return J

'''
# intialize X and y
X = np.array([[1,2,3],[1,3,4]])
y = np.array([[1],[0]])

m , n = X.shape
initial_theta = np.zeros(len(n))
Result = op.minimize(fun = CostFunc,
                                x0 = initial_theta),
                                args = (X, y),
                                method = 'TNC',
                                jac = Gradient)
optimal_theta = Result.x
'''

def optimize(X, y, theta):
    Result = op.minimize(fun = CostFunc, x0 = theta, args=(X, y), method='TNC', jac= Gradient, options={'maxiter': 400})
    return Result.x


import numpy as np

def get_mean(state, weights = None):
    """
    Get the mean statistics of the states given weights.
    If weights is not set, assumes equal weight samples.
    We assume the first dimension is state and second is
    the samples

    Parameters
    ----------
    state : np.ndarray
        The ensemble of states
    weights: np.ndarray or none
        The weights associated with the ensemble
        set to None implies equal weight

    Raises
    ------
    """

    if weights is None:
        means = np.mean(state, axis=1, keepdims=1)
    else:
        weights = weights/np.sum(weights)
        means = np.sum(weights*state, axis=1, keepdims=1)
    return means

def get_std(state, weights = None):
    """
    Get the std statistics of the states given weights.
    If weights is not set, assumes equal weight samples
    We assume the first dimension is state and second is
    the samples. Also we use an unbiased standard deviation

    Parameters
    ----------
    state : np.ndarray
        The ensemble of states
    weights: np.ndarray or none
        The weights associated with the ensemble
        set to None implies equal weight

    Raises
    ------
    """
    means = get_mean(state, weights)
    if weights is None:
        stds = np.std(state, axis=1, keepdims=1,ddof=1)
    else:
        weights = weights/np.sum(weights)
        ens_num = state.shape[1]
        stds = np.sqrt(np.sum((np.sqrt(weights)*(state-means))**2, axis=1, keepdims=1)*(ens_num/(ens_num-1)))
    return stds


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

colors = ['#342a77', '#ff464a', '#4881e9']
normal_mu = [0,0.5,1]
normal_sigma = [0.5,0.4,0.3]
x = np.arange(0.001, 7, .001) # for the log-normal PDF
x1 = np.arange(-2.5, 2.5, .001)  # for the normal PDF

fig, (ax1, ax2) = plt.subplots(nrows = 2, ncols = 1, figsize = (8,9))

for mu_n, sigma_n, color in zip(normal_mu, normal_sigma, colors):
     lognorm_pdf = lognorm.pdf(x, s=sigma_n, scale=np.exp(mu_n))
     r = lognorm.rvs(s=sigma_n, scale=np.exp(mu_n), size=15000)
     ax1.plot(x, lognorm_pdf, color=color, label=r"$\mu_n$ = " + str(mu_n) + r" - $\sigma_n$ = " + str(sigma_n))
     ax1.hist(r, bins='auto', density=True, color=color, edgecolor='#000000', alpha=0.5)
     logr= np.log(r)
     normal_pdf = norm.pdf(x1, loc= mu_n, scale = sigma_n)
     ax2.plot(x1, normal_pdf, color=color, label=r"$\mu_n$ = " + str(mu_n) + r" - $\sigma_n$ = " + str(sigma_n))
     ax2.hist(logr, bins='auto', density=True, color=color, edgecolor='#000000', alpha=0.5)
     my_mu = logr.mean()
     ax2.axvline(x=my_mu, color=color, linestyle="--", label=r"calculated $\mu_n$ = " + str(round(my_mu,3)))
     my_sigma = logr.std()
     print("Expected mean: " + str(mu_n) + " - Calculated mean: " + str(round(my_mu,3)))
     print("Expected std.dev.: " + str(sigma_n) + " - Calculated std.dev.: " + str(round(my_sigma,3)))

ax1.legend(title="log-normal distributions")
ax1.set_xlabel('x')
ax1.set_ylabel('Probability Density')
ax2.legend(title="normal distributions")
ax2.set_xlabel('ln(x)')
ax2.set_ylabel('Probability Density')

fig.tight_layout()


import sys

print(sys.version)

# We import NumPy into Python
import numpy as np

# We create a 1D ndarray that contains only integers
x = np.array([1, 2, 3, 4, 5])

# Let's print the ndarray we just created using the print() command
print('x = ', x)
print(type(x))

abc = np.array(['a','b','c','d','e','f','g','h','i','j'])
print("x: ", x)
print("shape: ", x.shape)
print("size: ", x.size)
print("type: ", x.dtype)

x = np.zeros((5,2))
print("x: ", x)
print("shape: ", x.shape)
print("size: ", x.size)
print("type: ", x.dtype)

# Using the Built-in functions you learned about in the
# previous lesson, create a 4 x 4 ndarray that only
# contains consecutive even numbers from 2 to 32 (inclusive)

x = np.arange(2,33,2).reshape((4,4))
print(x)

# Create a 5 x 5 ndarray with consecutive integers from 1 to 25 (inclusive).
# Afterwards use Boolean indexing to pick out only the odd numbers in the array

# Create a 5 x 5 ndarray with consecutive integers from 1 to 25 (inclusive).
X = np.arange(25).reshape((5,5))

print(X)
# Use Boolean indexing to pick out only the odd numbers in the array
Y = X[X % 2 != 0]
print(Y)

import numpy as np

# Use Broadcasting to create a 4 x 4 ndarray that has its first
# column full of 1s, its second column full of 2s, its third
# column full of 3s, etc..

# Do not change the name of this array.
# Please don't print anything from your code! The TEST RUN button below will print your array.

X = np.zeros((4,4)) + np.arange(1,5)
print(X)


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

def gaussian(x, mu, sig , plot_pref=False):
    '''
    A gaussian function (normalized similarly to scipy's function)
    RH 2021

    Args:
        x (np.ndarray): 1-D array of the x-axis of the kernel
        mu (float): center position on x-axis
        sig (float): standard deviation (sigma) of gaussian
        plot_pref (boolean): True/False or 1/0. Whether you'd like the kernel plotted

    Returns:
        gaus (np.ndarray): gaussian function (normalized) of x
        params_gaus (dict): dictionary containing the input params
    '''

    gaus = 1/(np.sqrt(2*np.pi)*sig)*np.exp((-((x-mu)/sig) **2)/2)

    if plot_pref:
        plt.figure()
        plt.plot(x , gaus)
        plt.xlabel('x')
        plt.title(f'$\mu$={mu}, $\sigma$={sig}')

    params_gaus = {
        "x": x,
        "mu": mu,
        "sig": sig,
    }

    return gaus , params_gaus

def generalised_logistic_function(x, a=0, k=1, b=1, v=1, q=1, c=1):
    '''
    Generalized logistic function
    See: https://en.wikipedia.org/wiki/Generalised_logistic_function
     for parameters and details
    RH 2021

    Args:
        a: the lower asymptote
        k: the upper asymptote when C=1
        b: the growth rate
        v: > 0, affects near which asymptote maximum growth occurs
        q: is related to the value Y (0). Center positions
        c: typically takes a value of 1

    Returns:
        output:
            Logistic function
     '''
    return a + (k-a) / (c + q*np.exp(-b*x))**(1/v)

def bounded_exponential(x, bounds=[1/10,10], base=2):
    """
    Bounded exponential function
    Computes an exponential function where when
     x is 0, the output is bounds[0], and when
     x is 1, the output is bounds[1]. The relative
     probability of outputting bounds[0[ over bounds[1]
     is base.
    Useful for randomly sampling over large ranges of
     values with an exponential resolution.
    RH 2021

    Args:
        x (float or np.ndarray):
            Float or 1-D array of the x-axis
        bounds (list):
            List of two floats, the lower and upper
             bounds
        base (float):
            The relative probability of outputting
             bounds[0] over bounds[1]

    Returns:
        output (float or np.ndarray):
            The bounded exponential output
    """

    range_additive = bounds[1] - bounds[0]

    return (((base**x - 1)/(base-1)) * range_additive) + bounds[0]

def polar2real(mag, angle):
    """
    Converts a polar coordinates to real coordinates
    RH 2021

    Args:
        mag (float or np.ndarray or torch.Tensor):
            Magnitude of the polar coordinates
        angle (float or np.ndarray or torch.Tensor):
            Angle of the polar coordinates

    Returns:
        output (float or np.ndarray or torch.Tensor):
    """
    if type(mag) is torch.Tensor:
        exp = torch.exp
    else:
        exp = np.exp
    return mag * exp(1j*angle)

def real2polar(x):
    """
    Converts a real coordinates to polar coordinates
    RH 2021

    Args:
        x (float or np.ndarray or torch.Tensor):
            Real coordinates

    Returns:
        Magnitude (float or np.ndarray or torch.Tensor):
            Magnitude of the polar coordinates
        Angle (float or np.ndarray or torch.Tensor):
            Angle of the polar coordinates
    """
    if type(x) is torch.Tensor:
        abs, angle = torch.abs, torch.angle
    else:
        abs, angle = np.abs, np.angle
    return abs(x), angle(x)


from Tests00Data import *
from scipy import stats
import statistics
import timeit

# https://docs.python.org/3/library/statistics.html
# https://docs.scipy.org/doc/scipy/reference/stats.html

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
mean_arithmetic = statistics.mean(data)
end = timeit.default_timer()
# Assert
print ("mean_arithmetic               = ", mean_arithmetic)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
mean_geometric = stats.gmean(data)
end = timeit.default_timer()
# Assert
print ("mean_geometric                = ", mean_geometric)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
mean_harmonic = stats.hmean(data)
end = timeit.default_timer()
# Assert
print ("mean_harmonic                 = ", mean_harmonic)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
median = statistics.median(data)
end = timeit.default_timer()
# Assert
print ("median                        = ", median)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
moment = stats.moment(data)
end = timeit.default_timer()
# Assert
print ("moment                        = ", moment)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
median_low = statistics.median_low(data)
end = timeit.default_timer()
# Assert
print ("median_low                    = ", median_low)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
median_high = statistics.median_high(data)
end = timeit.default_timer()
# Assert
print ("median_high                   = ", median_high)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")

print ("==================================================================================")
# Arrange
start = timeit.default_timer()
# Act
median_grouped = statistics.median_grouped(data)
end = timeit.default_timer()
# Assert
print ("median_grouped                = ", median_grouped)
print (u"              time [\u00B5s] = ", (end - start) * 100000)
#assert(mean_arithmetic == 3.8)
print ("==================================================================================")


#Modified Euler / Heuns method:
#Runge Kutta Second (2) Order Method
import numpy as np

def dy(ynew,xnew,y,x,h):
    dyvalue = x**2 + y**2
    return dyvalue
 #Note: change the derivative function based on question!!!!!!  Example: y-x

y0 = 2.3  #float(input"what is the y(0)?")

h = 0.1  #float(input"h?")

x_final = 1.2 #float(input"x_final")

#initiating input variables
x = 1
y = y0
# remember to change yn+1 and xn+1 values if you already know them!!!
ynew = 0
xnew = 0
i = 0

#####################################################
iterations = x_final/h

while x <= x_final:
    k1 = dy(ynew,xnew,y,x,h)
    k2 = dy(ynew,xnew,y+k1*h,x+h,h)
    xnew = x + h
    ynew = y + (h/2)*(k1+k2)
    print("iteration:        ____            ")
    print(i)
    print("\n")
    print("x = ")
    print(xnew)
    print("\n")
    print("y = ")
    print(ynew)
    x = xnew
    y = ynew
    i+=1


import numpy as np
import pandas as pd

class Moving_Averages(object):

    def simpleMA(tsPrice, k):
        """

        :param tsPrice: past price Series
        :param k: lag number
        :return sma: simple MA price Series
        """
        sma = tsPrice.rolling(window=k).apply(np.mean)
        return sma

    def weightedMA(tsPrice, weight):
        """
        :param tsPrice: past price Series
        :param weight: list of past price weights
        :return wma: weighted MA price Series
        """
        wma = tsPrice.rolling(window=len(weight)).apply(lambda x: np.average(x, weights=weight))
        return wma

    def ewMA(tsprice, period=5, exponential=0.2):
        Ewma = pd.Series(0.0, index=tsprice.index)
        Ewma[period-1] = np.mean(tsprice[:period])
        for i in range(period, len(tsprice)):
            Ewma[i] = exponential*tsprice[i] + (1- exponential)*Ewma[i-1]
        return Ewma


"""数学分析"""

import math

try:
    import sympy
    import sympy.abc
    from sympy import oo  # oo是无穷大
    # from sympy.abc import x
except ImportError as e:
    print(e)

# 另一种实现方法
# def fourier_odd(fx, x_tuple=(sympy.abc.x, 0, sympy.pi), T=0, silent=False):
#     x = x_tuple[0]
#     a = x_tuple[1]
#     b = x_tuple[2]

def fourier_odd(fx, x=sympy.abc.x, a=0, b=sympy.pi, T=0, silent=False):
    """
    计算奇延拓Fourier级数的Fourier系数,[0,b]区间,T周期(默认2b)
    """
    from sympy import pi
    n = sympy.Symbol('n', integer=True, positive=True)  # 定义符号n 为正整数

    if T == 0:
        T = 2 * b

    a0 = 0
    an = 0
    bn = sympy.simplify(sympy.integrate(fx * sympy.sin(2 * pi * n * x / T), (x, a, b)) * 4 / T)

    if not silent:
        print(f"{a0/2=}")
        print(f"{an=}")
        print(f"{bn=}")
    return [a0, an, bn]

def fourier_even(fx, x=sympy.abc.x, a=0, b=sympy.pi, T=0, silent=False):
    """计算偶延拓Fourier级数的Fourier系数,[0,b]区间,T周期(默认2b)"""
    from sympy import pi
    n = sympy.Symbol('n', integer=True, positive=True)  # 定义符号n 为正整数

    if T == 0:
        T = 2 * b

    a0 = sympy.simplify(sympy.integrate(fx, (x, a, b)) * 4 / T)
    an = sympy.simplify(sympy.integrate(fx * sympy.cos(2 * pi * n * x / T), (x, a, b)) * 4 / T)
    bn = 0

    if not silent:
        print(f"{a0/2=}")
        print(f"{an=}")
        print(f"{bn=}")
    return [a0, an, bn]

def fourier_series(fx, x=sympy.abc.x, a=-sympy.pi, b=sympy.pi, T=0, silent=False):
    """
    计算Fourier级数的Fourier系数,[a,b]区间,T周期(默认b-a)

    如果不加参数x=sympy.abc.x(即x=sympy.symbols('x')，没有限定条件)，则fx中其他的x会因与此函数中的x不是同一个x对象而导致计算错误

    注:sympy.fourier_series(1+x)或sympy.fourier_series(1+x, (x,-pi,pi))会得到
    FourierSeries(x + 1, (x, -pi, pi), (1, SeqFormula(Piecewise((2*sin(_n*pi)/_n, (_n > -oo) & (_n < oo) & Ne(_n, 0)), (2*pi, True))*cos(_n*x)/pi, (_n, 1, oo)), SeqFormula(Piecewise((-2*pi*cos(_n*pi)/_n + 2*sin(_n*pi)/_n**2, (_n > -oo) & (_n < oo) & Ne(_n, 0)), (0, True))*sin(_n*x)/pi, (_n, 1, oo))))
    这一串看不懂的东西，不如我这个好用

    """
    from sympy import pi
    n = sympy.Symbol('n', integer=True, positive=True)  # 定义符号n 为正整数

    if T == 0:
        T = b - a

    a0 = sympy.simplify(sympy.integrate(fx, (x, a, b)) * 2 / T)
    an = sympy.simplify(sympy.integrate(fx * sympy.cos(2 * pi * n * x / T), (x, a, b)) * 2 / T)
    bn = sympy.simplify(sympy.integrate(fx * sympy.sin(2 * pi * n * x / T), (x, a, b)) * 2 / T)

    if not silent:
        print(f"{a0/2=}")
        print(f"{an=}")
        print(f"{bn=}")
    return [a0, an, bn]

def generalized_fourier(fx, phi_nx, phi_0x=0, x=sympy.abc.x, a=-sympy.pi, b=sympy.pi, silent=False):
    """广义Fourier级数,phi_0x=0时不单独计算a0"""

    a0 = sympy.integrate(fx * phi_0x, (x, a, b))
    a0 = sympy.simplify(a0)

    an = sympy.integrate(fx * phi_nx, (x, a, b))
    an = sympy.simplify(an)

    if not silent:
        print(f"{a0=}")
        print(f"{an=}")
    return [a0, an]

def fourier_transform(fx, x=sympy.abc.x, silent=False):
    r"""Fourier变换

    .. math:: F(k) = \int_{-\infty}^\infty f(x) e^{- i x k} \mathrm{d} x.
    """
    k = sympy.symbols('k', real=True)
    # 无用 fx.subs(x, x1)  # 将fx中的 默认x 替换成 x1(实数x),若fx原来就是实数x也不会报错
    Fk = sympy.integrate(fx * sympy.exp(-sympy.I * k * x), (x, -oo, oo))
    if not silent:
        print(Fk)
    return Fk

def fourier_transform_inverse(Fk, k=sympy.abc.k, silent=False):
    r"""Fourier变换的逆变换

        .. math:: F(k) = \int_{-\infty}^\infty f(x) e^{- i x k} \mathrm{d} x.
        """
    x = sympy.symbols('x', real=True)
    fx = sympy.integrate(Fk * sympy.exp(sympy.I * k * x), (k, -oo, oo)) / (2 * sympy.pi)
    if not silent:
        print(fx)
    return fx

def dot_product2(fx, gx, x=sympy.abc.x, a=-1, b=1, silent=True):
    """积分形式的内积"""
    from sympy import integrate
    o1 = integrate(fx * gx, (x, a, b))
    if not silent:
        print(f"fx*gx={o1}")
    return o1

def schmidt_orthogonalization(fix: list, n, e: list, x=sympy.abc.x, a=-1, b=1):
    """施密特正交化,输出为列表,n=len(fix),手动选择积分区间[a,b]"""
    from sympy import sqrt
    # dot_product=dot_product2 #另一种实现方法,手动选择内积

    if n == 1:
        e[0] = fix[0] / sqrt(dot_product2(fix[0], fix[0], x, a, b))
    else:
        schmidt_orthogonalization(fix, n - 1, e, x, a, b)
        e[n - 1] = fix[n - 1]
        for j in range(0, n - 1):
            e[n - 1] -= dot_product2(fix[n - 1], e[j], x, a, b) * e[j]
        e[n - 1] /= sqrt(dot_product2(e[n - 1], e[n - 1], x, a, b))

def schmidt(fix: list, n, e: list, dot_product=dot_product2, x=sympy.abc.x):
    """施密特正交化,输出在e列表,n=len(fix),手动选择内积dot_product"""
    from sympy import sqrt

    if n == 1:
        e[0] = fix[0] / sqrt(dot_product(fix[0], fix[0], x))
    else:
        schmidt(fix, n - 1, e, dot_product, x)
        e[n - 1] = fix[n - 1]
        for j in range(0, n - 1):
            e[n - 1] -= dot_product(fix[n - 1], e[j], x) * e[j]
        e[n - 1] /= sqrt(dot_product(e[n - 1], e[n - 1], x))

def schmidt_orthogonalization_list(fix: list, n, x=sympy.abc.x, a=-1, b=1, silent=True):
    """施密特正交化,输出为列表,n=len(fix),手动选择积分区间[a,b]"""
    e = []
    for i in range(0, n):
        e.append(None)
    schmidt_orthogonalization(fix, n, e, x, a, b)
    if not silent:
        print(e)
    return e

def schmidt_list(fix: list, n, dot_product=dot_product2, x=sympy.abc.x, silent=True):
    """施密特正交化,输出为列表,n=len(fix),手动选择内积dot_product"""
    e = []
    for i in range(0, n):
        e.append(None)
    schmidt(fix, n, e, dot_product, x)
    if not silent:
        print(e)
    return e

def convolution1(ft, gx_t, silent=True):
    """卷积,核心算法"""
    from sympy import integrate
    t = sympy.symbols('t', real=True)
    o1 = integrate(ft * gx_t, (t, -oo, oo))
    if not silent:
        print(f"(f*g)(x)={o1}")
    return o1

def convolution(fx: str, gx: str):
    """卷积"""
    t = sympy.symbols('t', real=True)
    ft1 = fx.replace('x', 't')
    gx_t1 = gx.replace('x', '(x-t)')
    exec("ft2=" + ft1)
    exec("gx_t2=" + gx_t1)
    o1 = [None]  # 必须用引用类型(比如列表),否则报错
    exec("o1[0]=convolution1(ft2, gx_t2)")
    return o1[0]

def legrendre(n, x=sympy.abc.x):
    """勒让德多项式"""
    pnx = 1 / (2 ** n * sympy.factorial(n)) * sympy.diff((x * x - 1) ** n, x, n)
    # print(pnx)
    return pnx

def legrendre_list(n, x=sympy.abc.x):
    """勒让德多项式表"""
    l1 = []
    for i in range(0, n):
        l1.append(legrendre(i, x))
    print(l1)  # [1, x, (3*x**2 - 1)/2, x*(5*x**2 - 3)/2, (8*x**4 + 24*x**2*(x**2 - 1) + 3*(x**2 - 1)**2)/8]
    return l1

def laplace_transform(ft, t=sympy.abc.t, silent=False):
    r"""Laplace变换

    .. math:: F(p) = \int_{0}^\infty f(x) e^{-pt} \mathrm{d} t.
    """
    p = sympy.symbols('p')
    Fp = sympy.integrate(ft * sympy.exp(-p * t), (t, 0, oo))
    if not silent:
        print(Fp)
    return Fp

if __name__ == '__main__':
    x,t = sympy.symbols("x,t")  # sympy.symbols("x")等价于sympy.abc.x
    fourier_even(1 + x, x)
    fourier_odd(1 + x, x)
    fourier_series(1 + x, x, 0, sympy.pi)
    fourier_transform(sympy.sin(x), x)
    laplace_transform(1)
    laplace_transform(t ** 2, t)
    # 输出结果中Piecewise表示分段函数,Ne(n,0)表示n!=0,True表示除去前面(Ne(n,0))的情况(即n==0)
    # 已修复n的类型,不会输出上述结果了


''' Good students always try to solve exercise on their own first and then look at the ready made solution
    I know you are an awesome student !! :)
    Hence you will look into this code only after you have done your due diligence.
    If you are not an awesome student who is full of laziness then only you will come here
    without writing single line of code on your own. In that case anyways you are going to
    face my anger with fire and fury !!!
'''

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
import math

def predict_using_sklean():
    df = pd.read_csv("test_scores.csv")
    r = LinearRegression()
    r.fit(df[['math']],df.cs)
    return r.coef_, r.intercept_

def gradient_descent(x,y):
    m_curr = 0
    b_curr = 0
    iterations = 1000000
    n = len(x)
    learning_rate = 0.0002

    cost_previous = 0

    for i in range(iterations):
        y_predicted = m_curr * x + b_curr
        cost = (1/n)*sum([value**2 for value in (y-y_predicted)])
        md = -(2/n)*sum(x*(y-y_predicted))
        bd = -(2/n)*sum(y-y_predicted)
        m_curr = m_curr - learning_rate * md
        b_curr = b_curr - learning_rate * bd
        if math.isclose(cost, cost_previous, rel_tol=1e-20):
            break
        cost_previous = cost
        print ("m {}, b {}, cost {}, iteration {}".format(m_curr,b_curr,cost, i))

    return m_curr, b_curr

if __name__ == "__main__":
    df = pd.read_csv("test_scores.csv")
    x = np.array(df.math)
    y = np.array(df.cs)

    m, b = gradient_descent(x,y)
    print("Using gradient descent function: Coef {} Intercept {}".format(m, b))

    m_sklearn, b_sklearn = predict_using_sklean()
    print("Using sklearn: Coef {} Intercept {}".format(m_sklearn,b_sklearn))


"""Measurements using spatial objects."""
import numpy as np

from skspatial.objects import Points
from skspatial.objects import Vector
from skspatial.typing import array_like

def area_triangle(point_a: array_like, point_b: array_like, point_c: array_like) -> np.float64:
    """
    Return the area of a triangle defined by three points.

    The points are the vertices of the triangle. They must be 3D or less.

    Parameters
    ----------
    point_a, point_b, point_c : array_like
        The three vertices of the triangle.

    Returns
    -------
    np.float64
        The area of the triangle.

    References
    ----------
    http://mathworld.wolfram.com/TriangleArea.html

    Examples
    --------
    >>> from skspatial.measurement import area_triangle

    >>> area_triangle([0, 0], [0, 1], [1, 0])
    0.5

    >>> area_triangle([0, 0], [0, 2], [1, 1])
    1.0

    >>> area_triangle([3, -5, 1], [5, 2, 1], [9, 4, 2]).round(2)
    12.54

    """
    vector_ab = Vector.from_points(point_a, point_b)
    vector_ac = Vector.from_points(point_a, point_c)

    # Normal vector of plane defined by the three points.
    vector_normal = vector_ab.cross(vector_ac)

    return 0.5 * vector_normal.norm()

def volume_tetrahedron(
    point_a: array_like,
    point_b: array_like,
    point_c: array_like,
    point_d: array_like,
) -> np.float64:
    """
    Return the volume of a tetrahedron defined by four points.

    The points are the vertices of the tetrahedron. They must be 3D or less.

    Parameters
    ----------
    point_a, point_b, point_c, point_d : array_like
        The four vertices of the tetrahedron.

    Returns
    -------
    np.float64
        The volume of the tetrahedron.

    References
    ----------
    http://mathworld.wolfram.com/Tetrahedron.html

    Examples
    --------
    >>> from skspatial.measurement import volume_tetrahedron

    >>> volume_tetrahedron([0, 0], [3, 2], [-3, 5], [1, 8])
    0.0

    >>> volume_tetrahedron([0, 0, 0], [2, 0, 0], [1, 1, 0], [0, 0, 1]).round(3)
    0.333

    >>> volume_tetrahedron([0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]).round(3)
    0.167

    """
    vector_ab = Vector.from_points(point_a, point_b)
    vector_ac = Vector.from_points(point_a, point_c)
    vector_ad = Vector.from_points(point_a, point_d)

    vector_cross = vector_ac.cross(vector_ad)

    # Set the dimension to 3 so it matches the cross product.
    vector_ab = vector_ab.set_dimension(3)

    return 1 / 6 * abs(vector_ab.dot(vector_cross))

def area_signed(points: array_like) -> float:
    """
    Return the signed area of a simple polygon given the 2D coordinates of its veritces.

    The signed area is computed using the shoelace algorithm. A positive area is
    returned for a polygon whose vertices are given by a counter-clockwise
    sequence of points.

    Parameters
    ----------
    points : array_like
         Input 2D points.

    Returns
    -------
    area_signed : float
        The signed area of the polygon.

    Raises
    ------
    ValueError
        If the points are not 2D.
        If there are fewer than three points.

    References
    ----------
    https://en.wikipedia.org/wiki/Shoelace_formula
    https://alexkritchevsky.com/2018/08/06/oriented-area.html
    https://rosettacode.org/wiki/Shoelace_formula_for_polygonal_area#Python

    Examples
    --------
    >>> from skspatial.measurement import area_signed

    >>> area_signed([[0, 0], [1, 0], [0, 1]])
    0.5

    >>> area_signed([[0, 0], [0, 1], [1, 0]])
    -0.5

    >>> area_signed([[0, 0], [0, 1], [1, 2], [2, 1], [2, 0]])
    -3.0

    """
    points = Points(points)
    n_points = points.shape[0]

    if points.dimension != 2:
        raise ValueError("The points must be 2D.")

    if n_points < 3:
        raise ValueError("There must be at least 3 points.")

    X = points[:, 0]
    Y = points[:, 1]

    indices = np.arange(n_points)
    indices_offset = indices - 1

    return 0.5 * np.sum(X[indices_offset] * Y[indices] - X[indices] * Y[indices_offset])


import numpy as np
from numba import jit, njit, vectorize, guvectorize

@njit
def haversine(lat1, lon1, lat2, lon2):
    """
    Haversine distance in kilometers between two points given in degrees.
    r = 6371.009, 2r = 12742.018
    Because the haversine distance assumes a spherical Earth, it will only
    be accurate within ~0.5%. The mean earth radius is used (6371km).
    """
    λ1 = np.radians(lat1)
    λ2 = np.radians(lat2)
    φ1 = np.radians(lon1)
    φ2 = np.radians(lon2)
    Δλ = λ2 - λ1
    Δφ = φ2 - φ1
    return 12742.018 * np.arcsin(np.sqrt(
        np.sin(Δλ/2.0)**2 + (np.cos(λ1) * np.cos(λ2) * np.sin(Δφ/2.0)**2)))

@guvectorize([("void(float64[:], float64[:], float64[:], float64[:],"
               " float64[:])")],
             "(n),(n),(n),(n)->(n)")
def haversine_elementwise(lat1, lon1, lat2, lon2, res):
    """ Elementwise haversine distance between vectors
        of latitudes and longitudes

        lat1, lon1: vectors for latitude and longitude of locations
        lat2, lon2: vectors for latitude and longitude of locations
    """
    for i in range(lat1.shape[0]):
        res[i] = haversine(lat1[i], lon1[i], lat2[i], lon2[i])

@guvectorize(["void(float64, float64, float64[:], float64[:], float64[:])"],
             "(),(),(n),(n)->(n)")
def haversine_vector(lat1, lon1, latcol, loncol, res):
    """ Haversine distance between a fixed lat/lon and vectors of lat/lon
    lat1, lon1: Fixed latitude and longitude
    latvec, lonvec: vector of latitudes and longitudes
    """
    for i in range(latcol.shape[0]):
        res[i] = haversine(lat1, lon1, latcol[i], loncol[i])

@guvectorize([("void(float64[:], float64[:], float64[:], float64[:],"
               " float64[:,:])")],
             "(n),(n),(m),(m)->(n,m)")
def haversine_outer_product(lat1, lon1, lat2, lon2, res):
    """ Haversine distance between vectors of latitudes and longitudes
        (outer product)

        lat1, lon1: vectors for latitude and longitude of locations
        lat2, lon2: vectors for latitude and longitude of locations
    """
    for i in range(lat1.shape[0]):
        for j in range(lat2.shape[0]):
            res[i, j] = haversine(lat1[i], lon1[i], lat2[j], lon2[j])


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

def calc_d1(St, K, r, s, T, t=0, q=0):
    return (np.log(St / K) + (r - q + 0.5 * s ** 2) * (T - t)) \
           / (s * np.sqrt(T - t))

def calc_d2(d1, s, T, t=0):
    return d1 - s * np.sqrt(T - t)

def problem2a():
    T_final = 5
    Ts = np.arange(dt, T_final, dt)

    mus_c = []
    sgs_c = []
    for T in Ts:
        d1 = calc_d1(St, K, r, sg, T, q=q)
        d2 = calc_d2(d1, sg, T)
        delta = np.exp(-q * T) * norm.cdf(d1)
        gamma = norm.pdf(d1) * np.exp(-q * T) / (St * sg * np.sqrt(T))
        theta = -(St * norm.pdf(d1) * sg * np.exp(-q * T)) / (2 * np.sqrt(T)) \
                + q * St * norm.cdf(d1) * np.exp(-q * T) \
                - r * K * np.exp(-r * T) * norm.cdf(d2)
        Ct = St * np.exp(-q * T) * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
        mu_call = (1. / Ct) * (theta + mu * St * delta + 0.5 * sg ** 2 * St ** 2 * gamma)
        sg_call = (1. / Ct) * sg  * St * delta
        mus_c.append(mu_call)
        sgs_c.append(sg_call)
    plt.figure()
    plt.plot(Ts, mus_c)
    plt.ylabel('Price')
    plt.xlabel('Time to maturity')
    plt.figure()
    plt.plot(Ts, sgs_c)
    plt.ylabel('Price')
    plt.xlabel('Time to maturity')
    plt.show()

def problem2b():
    T = 1
    Sts = np.arange(0.01, 100, 0.1)
    mus_c = []
    sgs_c = []
    for St in Sts:
        d1 = calc_d1(St, K, r, sg, T, q=q)
        d2 = calc_d2(d1, sg, T)
        delta = np.exp(-q * T) * norm.cdf(d1)
        gamma = norm.pdf(d1) * np.exp(-q * T) / (St * sg * np.sqrt(T))
        theta = -(St * norm.pdf(d1) * sg * np.exp(-q * T)) / (2 * np.sqrt(T)) \
                + q * St * norm.cdf(d1) * np.exp(-q * T) \
                - r * K * np.exp(-r * T) * norm.cdf(d2)
        Ct = St * np.exp(-q * T) * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
        mu_call = (1. / Ct) * (theta + mu * St * delta + 0.5 * sg ** 2 * St ** 2 * gamma)
        sg_call = (1. / Ct) * sg  * St * delta
        mus_c.append(mu_call)
        sgs_c.append(sg_call)
    plt.figure()
    plt.plot(Sts, mus_c)
    plt.ylabel('Price')
    plt.xlabel('Stock Price')
    plt.figure()
    plt.plot(Sts, sgs_c)
    plt.ylabel('Price')
    plt.xlabel('Stock Price')
    plt.show()

if __name__ == '__main__':

    K = 50
    sg = 0.5
    mu = 0.15
    q = 0.08
    r = 0.13
    St = 50
    dt = 1 / 252.

    # problem2a()
    problem2b()


import numpy as np

def distance(src_x, src_y, dest_x, dest_y):
    return np.sqrt(np.square(src_x - dest_x) + np.square(src_y - dest_y))

def distance_pt(matrix_pt_1, matrix_pt_2):
    return distance(matrix_pt_1[1], matrix_pt_1[0], matrix_pt_2[1], matrix_pt_2[0])


import numpy as np
import math

"""Pauli Gates X, Y, Z"""
X = np.array([[0,1],[1,0]])
Y = np.array([[0,-1j],[-1j,0]])
Z = np.array([[1,0],[0,-1]])

"""Hadamard Gate H"""
H = (1/np.sqrt(2))*np.array([[1,1],[1,-1]])

"""Phase Gate S"""
S = np.array([[1,0],[0,-1j]])

"""π/8 Gate T"""
T_pi = np.array([[1,0],[0,math.e**((math.pi/4)*1j)]])

"""T dagger"""
T_dag = np.array([[1,0],[0,math.e**((math.pi/4)*-1j)]])

"""Controlled NOT, CNOT"""
cnot = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]])

"""SWAP Gate swap"""
swap = np.array([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])

# """Controlled Z cZ"""
# cz = np.array([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])

"""Controlled Phase Gate cS"""
cs = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,-1j]])

"""Double Controlled NOT or Toffoli, ccNOT"""
ccnot = np.array([[1,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0],[0,0,1,0,0,0,0,0],[0,0,0,1,0,0,0,0],[0,0,0,0,1,0,0,0],[0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,0,1,0]])

"""Controlled Swap, Fredkin Gate, cSwap"""
cswap = np.array([[1,0,0,0,0,0,0,0],[0,1,0,0,0,0,0,0],[0,0,1,0,0,0,0,0],[0,0,0,1,0,0,0,0],[0,0,0,0,1,0,0,0],[0,0,0,0,0,0,1,0],[0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1]])

"""Controlled Pauli Z"""
cz = np.array([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,-1]])

"""Square root of NOT"""
sqrt_not = np.array([[(1+1j)/2 , (1-1j)/2],[(1-1j)/2 , (1+1j)/2]])

"""P"""
p = np.array([[1,0],[0,1j]])


import numpy as np
import imageio
import math

def hough_line(img, angle_step=1, lines_are_white=True, value_threshold=5):
    """
    Hough transform for lines
    Input:
    img - 2D binary image with nonzeros representing edges
    angle_step - Spacing between angles to use every n-th angle
                 between -90 and 90 degrees. Default step is 1.
    lines_are_white - boolean indicating whether lines to be detected are white
    value_threshold - Pixel values above or below the value_threshold are edges
    Returns:
    accumulator - 2D array of the hough transform accumulator
    theta - array of angles used in computation, in radians.
    rhos - array of rho values. Max size is 2 times the diagonal
           distance of the input image.
    """
    # Rho and Theta ranges
    thetas = np.deg2rad(np.arange(-90.0, 90.0, angle_step))
    width, height = img.shape
    diag_len = int(round(math.sqrt(width * width + height * height)))
    rhos = np.linspace(-diag_len, diag_len, diag_len * 2)

    # Cache some resuable values
    cos_t = np.cos(thetas)
    sin_t = np.sin(thetas)
    num_thetas = len(thetas)

    # Hough accumulator array of theta vs rho
    accumulator = np.zeros((2 * diag_len, num_thetas), dtype=np.uint8)
    # (row, col) indexes to edges
    are_edges = img > value_threshold if lines_are_white else img < value_threshold
    y_idxs, x_idxs = np.nonzero(are_edges)

    # Vote in the hough accumulator
    for i in range(len(x_idxs)):
        x = x_idxs[i]
        y = y_idxs[i]

        for t_idx in range(num_thetas):
            # Calculate rho. diag_len is added for a positive index
            rho = diag_len + int(round(x * cos_t[t_idx] + y * sin_t[t_idx]))
            accumulator[rho, t_idx] += 1

    return accumulator, thetas, rhos

def show_hough_line(img, accumulator, thetas, rhos, save_path=None):
    import matplotlib.pyplot as plt

    fig, ax = plt.subplots(1, 2, figsize=(10, 10))

    ax[0].imshow(img, cmap=plt.cm.gray)
    ax[0].set_title('Input image')
    ax[0].axis('image')

    ax[1].imshow(
        accumulator, cmap='jet',
        extent=[np.rad2deg(thetas[-1]), np.rad2deg(thetas[0]), rhos[-1], rhos[0]])
    ax[1].set_aspect('equal', adjustable='box')
    ax[1].set_title('Hough transform')
    ax[1].set_xlabel('Angles (degrees)')
    ax[1].set_ylabel('Distance (pixels)')
    ax[1].axis('image')

    # plt.axis('off')
    if save_path is not None:
        plt.savefig(save_path, bbox_inches='tight')
    plt.show()

if __name__ == '__main__':
    imgpath = 'imgs/binary_crosses.png'
    img = imageio.imread(imgpath)
    if img.ndim == 3:
        img = rgb2gray(img)
    accumulator, thetas, rhos = hough_line(img)
show_hough_line(img, accumulator, save_path='imgs/output.png')


#! /usr/bin/env python
# -*- coding: utf-8 -*-
#======================================================================
#
# GradientDescent.py -
#
# Created by skywind on 2019/03/14
# Last Modified: 2019/03/14 21:00:27
#
#======================================================================
from __future__ import print_function, unicode_literals
import sys
import time

#----------------------------------------------------------------------
# GradientDescent
#----------------------------------------------------------------------
def GradientDescent(x, y, theta, alpha, iterations, limit):
    rows = len(x)
    if rows == 0:
        raise ValueError('Data size must great than zero.')
    cols = len(x[0])
    theta = [ t for t in theta ]
    for count in range(iterations):
        # iterate feature columns
        update = [ 1.0 ] * cols
        for j in range(cols):
            # calculate derivation
            derivation = 0
            for i in range(rows):
                h = sum([ x[i][n] * theta[n] for n in range(cols) ])
                derivation += (h - y[i]) * x[i][j]
            derivation = derivation / float(rows)
            update[j] = theta[j] - alpha * derivation
        # update theta
        theta = update
        # calculate error
        error = 0
        for i in range(rows):
            h = sum([ x[i][n] * theta[n] for n in range(cols) ])
            error += (h - y[i]) * (h - y[i])
        error = error / float(rows)
        if error < limit:
            break
    return theta

#----------------------------------------------------------------------
# GradientDescent
#----------------------------------------------------------------------
def GradientStep(x, y, theta, alpha):
    cols = len(x)
    if cols == 0:
        raise ValueError('Feature size must great than zero.')
    h = sum([ x[n] * theta[n] for n in range(cols) ])
    update = [ 1.0 ] * cols
    for j in range(cols):
        derivation = (h - y) * x[j]
        update[j] = theta[j] - alpha * derivation
    return update

#----------------------------------------------------------------------
# Increamental GradientDescent
#----------------------------------------------------------------------
def GradientDescent2(x, y, theta, alpha, iterations, limit):
    rows = len(x)
    if rows == 0:
        raise ValueError('Data size must great than zero.')
    cols = len(x[0])
    for count in range(iterations):
        for i in range(rows):
            theta = GradientStep(x[i], y[i], theta, alpha)
        # calculate error
        error = 0
        for i in range(rows):
            h = sum([ x[i][n] * theta[n] for n in range(cols) ])
            error += (h - y[i]) * (h - y[i])
        error = error / float(rows)
        if error < limit:
            break
    return theta

#----------------------------------------------------------------------
# numpy
#----------------------------------------------------------------------
def GradientDescent3(x, y, theta, alpha, iterations, limit):
    rows = len(x)
    if rows == 0:
        raise ValueError('Data size must great than zero.')
    import numpy
    x = numpy.array(x)
    y = numpy.array(y)
    theta = numpy.array([ t for t in theta ])
    t = x.transpose()
    for count in range(iterations):
        h = numpy.dot(x, theta)
        error = h - y
        derivation = numpy.dot(t, error) / float(rows)
        theta = theta - float(alpha) * derivation
        # update error
        e = numpy.dot(x, theta) - y
        s = sum(e * e) / float(rows)
        if s < limit:
            break
    return theta

#----------------------------------------------------------------------
# test
#----------------------------------------------------------------------
def TestGD(proc, x, y, alpha, maxiter, limit = 0.0001):
    k = [ 1 ] * len(x[0])
    t = time.time()
    k = proc(x, y, k, alpha, maxiter, limit)
    t = time.time() - t
    print('GradientDescent time:', t)
    print('Theta:', k)
    error = 0
    X = [[3.1, 5.5], [3.3, 5.9], [3.5, 6.3], [3.7, 6.7], [3.9, 7.1]]
    Y = [9.5, 10.2, 10.9, 11.6, 12.3]
    for i in range(len(X)):
        h = sum([ X[i][n] * k[n] for n in range(len(k)) ])
        error += (h - Y[i]) * (h - Y[i])
        print(Y[i], h)
    error /= len(X)
    print('error', error)
    print()
    return 0

#----------------------------------------------------------------------
# Samples
#----------------------------------------------------------------------
X = [[1.1, 1.5], [1.3, 1.9], [1.5, 2.3], [1.7, 2.7], [1.9, 3.1],
    [2.1, 3.5], [2.3, 3.9], [2.5, 4.3], [2.7, 4.7], [2.9, 5.1]]
Y = [2.5, 3.2, 3.9, 4.6, 5.3, 6, 6.7, 7.4, 8.1, 8.8]

TestGD(GradientDescent, X, Y, 0.1, 50000)
TestGD(GradientDescent2, X, Y, 0.1, 50000)
TestGD(GradientDescent3, X, Y, 0.1, 50000)


# -*- coding: utf-8 -*-
"""
Created on Fri Jul 26 08:20:48 2019

@author: MAHDI
"""
import numpy as np
import matplotlib.pyplot as plt
def GetY(solution , xval):
    Y=0.0
    for i in range(len(solution)):
        Y = Y+ xval**i * solution[i]
    return Y
def DrawPolinomial(solution,mycolor,mylabel):
    x= np.linspace(0.0,11,100)
#    print(x)
    #y=np.array( [GetY(solution,xval) for xval in x])
    y=GetY(solution,x)
#    print(y)
    plt.plot(x,y,color=mycolor,label=mylabel)
def PolinomialRegression(x,y,n,m):
    """
    m= order of polinomial
    """
# =============================================================================
#     if n<m+1:
#         return "impossible"
# =============================================================================
    a=np.zeros((m+1,m+1))
    b=np.zeros((m+1))
#    print(a,b)
    for i in range(m+1):
        sum = np.sum(x**i)
        xid=0
        yid=i
        while yid>=0:
            a[xid][yid]=sum
            xid = xid +1
            yid = yid -1
    for i in range(m+1,m+m+1):
        sum= np.sum(x**i)
        xid=i-m
        yid=m
        while xid<=m:
            a[xid][yid]=sum
            xid = xid +1
            yid = yid -1
#    print(a)
    for i in range(m+1):
        sum = np.sum([(xx**i) * yy for (xx,yy) in zip(x,y) ])
#        print(sum)
        b[i]=sum
#    print(b)
    sol = np.linalg.solve(a,b)
    return sol
def PrintSolution(Solution,x,y):
    print('----------============----------')
    print('Order of polynomial',len(Solution)-1)
    for i in range(len(Solution)):
        print('a',i,' = ',Solution[i],sep='')
    print('Regression coefficient:',RegressionCoefficient(Solution,x,y))
    print('----------============----------')
def RegressionCoefficient(Solution, x,y):
    yavg = np.sum(y) / len(y)
#    print(yavg)
    St = np.sum(np.array([(yval-yavg)*(yval-yavg) for yval in y]))
#    print(St)
    Sr = np.sum(np.array([(yval - GetY(Solution,xval))*(yval - GetY(Solution,xval)) for (xval,yval) in zip(x,y)]))
    return np.sqrt(1.0-Sr/St)

if __name__ == '__main__':
    file = open('data.txt', 'r+')
    data = [list(map(float,line.split(' '))) for line in file.readlines() ]
#    print(data)
    x= [dat[0] for dat  in data]
    y= [dat[1] for dat  in data]
    x=np.array(x)
    y=np.array(y)
    assert(len(x)==len(y))
    n=len(x)
# =============================================================================
#     for i in zip(x,y):
#         print(i)
# =============================================================================
    plt.figure(figsize=(15,15))
    plt.scatter(x,y,s=0.5,color = 'purple',label='original data')
    sol1 = PolinomialRegression(x,y,n,1)
    DrawPolinomial(sol1,'red','First order fit')

    sol2 = PolinomialRegression(x,y,n,2)
    DrawPolinomial(sol2,'black','Second order fit')

    sol3 = PolinomialRegression(x,y,n,3)
    DrawPolinomial(sol3,'green','Third order fit')

    plt.legend(loc='best',title='Curve Fitting Example')

# =============================================================================
#     sol100=PolinomialRegression(x,y,n,100)
#     DrawPolinomial(sol100,'pink','100th Order fit')
# =============================================================================

    PrintSolution(sol1,x,y)
    PrintSolution(sol2,x,y)
    PrintSolution(sol3,x,y)
#    PrintSolution(sol100,x,y)
# =============================================================================
#     print(sol1)
#     print(RegressionCoefficient(sol1,x,y))
#
#     print(sol2)
#     print(RegressionCoefficient(sol2,x,y))
#     print(sol3)
#     print(RegressionCoefficient(sol3,x,y))
# =============================================================================

Numerical/Offline 3 on Curve Fitting/1705003.py


import numpy as np

EPSILON = 1e-10

def _error(actual: np.ndarray, predicted: np.ndarray):
    """ Simple error """
    return actual - predicted

def me(actual: np.ndarray, predicted: np.ndarray):
    """ Mean Error """
    return np.mean(_error(actual, predicted))

def _percentage_error(actual: np.ndarray, predicted: np.ndarray):
    """
    Percentage error

    Note: result is NOT multiplied by 100
    """
    return _error(actual, predicted) / (actual + EPSILON)

def mae(actual: np.ndarray, predicted: np.ndarray):
    """ Mean Absolute Error """
    return np.mean(np.abs(_error(actual, predicted)))

def mse(actual: np.ndarray, predicted: np.ndarray):
    """ Mean Squared Error """
    return np.mean(np.square(_error(actual, predicted)))

def rmse(actual: np.ndarray, predicted: np.ndarray):
    """ Root Mean Squared Error """
    return np.sqrt(mse(actual, predicted))


"""
auralib module containing various window functions for signal processing.

Author:   Wes Hamlyn
Created:  24-Mar-2020
Last Mod: 24-Mar-2020
"""

import numpy as np

def papoulis(nsamp, normed=True):
    """
    Calculate a time-domain Papoulis (a.k.a Bohman) window

    Input:
    ------
        nsamp = number of samples to include in the window

    Output:
    -------
        w = window amplitudes

    Reference:
    ----------
        https://prod-ng.sandia.gov/techlib-noauth/access-control.cgi/2017/174042.pdf
        page: 84
    """

    tmin = -0.5
    tmax = 0.5
    dt = (tmax-tmin)/(nsamp-1)

    t = np.arange(nsamp)*dt + tmin

    w = (np.pi**2)/4 * (1-2*np.abs(t))*np.cos(2*np.pi*np.abs(t)) + \
        np.pi/4*np.sin(2*np.pi*np.abs(t))

    if normed:
        w = w / w.max()

    return w

def cosine(nsamp, taper_len):
    """
    Calculate a time-domain window with cosine tapers at each end

    nsamp = total length of window operator
    nsamp_taper = length of cosine taper in samples at ends of window
    """

    # build cosine ramp from zero to one
    x = np.linspace(-np.pi, 0.0, taper_len)
    costaper = 0.5*(np.cos(x)+1)

    # make window with ones everwhere and add cosine tapers to ends
    w = np.ones(nsamp)
    w[0:taper_len] = costaper
    w[-taper_len:] = costaper[-1::-1]

    return w


# usage of regressions to find coefficients of formula to sum terms `i^m` from 0 to n
import numpy as np
from numpy.linalg import inv

def plot(y,z):
  import matplotlib.pyplot as plt

  plt.plot(y, '.')
  plt.plot(z)
  plt.show()

# solve Ordinary Least Squares
def solveOLS(X, Y):
  Z = inv(X.T*X)
  return Z*X.T*Y

# generate `n` samples
n = 15

# exponent
m = 8

x = np.arange(0,n,1)

# theory
z = .5*x*(x+1)

# generate output function
y = np.cumsum(x**m)
# format output
y.shape = (n,1)
Y = np.asmatrix(y)

# prepare matrix
X = np.ones((n,1))

x.shape = (n,1)

# add column of input until m+1 (inclusive)
for i in range(1, m+2):
  X = np.append(X, x**i, axis =1)

X = np.asmatrix(X)
r = solveOLS(X, Y)
print r
print np.cumsum(r)

# m=0: (1,   1)
# m=1: (0, 1/2, 1/2)
# m=2: (0, 1/6, 1/2, 1/3)
# m=3: (0,   0, 1/4, 1/2,  1/4)
# m=4: (0,   -1/30,   0, 1/3,  1/2, 1/5)
# m=5: (0,   0,   0,   0, 5/12, 1/2, 1/6)
#
# sum always 1!!? (except for 0)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Jan 26 16:48:41 2019

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

def quad_solve1(a,b,c):
    D = b**2-4*a*c
    return np.array([(-b+np.sqrt(D))/(2*a),(-b-np.sqrt(D))/(2*a)])

def quad_solve2(a,b,c):
    D = b**2-4*a*c
    return np.array([(2*c)/(-b-np.sqrt(D)),(2*c)/(-b+np.sqrt(D))])

print('Using form 1:')
print('Roots are:',end=' ')
print(quad_solve1(.001, 1000, .001))
print('Using form 2:')
print('Roots are:',end=' ')
print(quad_solve2(.001, 1000, .001))

"""
The two functions differ in their results. This happens when $b^2>>|4ac|$. In either of the two
formulae, finding one of the roots involve subtracting two nearly equal quantities (Discriminant is almost $b^2$).
This results in loss of numerical precision corresponding to that root. So in one formula if Root 1 is more accurate
than Root 2, then in the other formula, Root2 is more accurate than Root 1. Now the question is which root is more accurate
in which formula?
Well that depends on the sign of $b$ in $ax^2+bx+c=0$
When $b>0$, $-b-\sqrt{b^2-4ac}$ doesn't involve subtracting two nearly equal quantities. So Root 2 of quad_solve1 and
Root 1 of quad_solve2 gives accurate results.
Converse happens for $b<0$

Lets code this in the following function:
"""

def quad_solve_acc(a,b,c):
    if b>0:
        return np.array([quad_solve2(a,b,c)[0],quad_solve1(a,b,c)[1]])
    else:
        return np.array([quad_solve1(a,b,c)[10],quad_solve2(a,b,c)[1]])

print('Accurate Roots are:',end=' ')
print(quad_solve_acc(.001, 1000, .001))

x = -np.linspace(0,14,100)*1e-7
plt.plot(x,0.001*x**2+1000*x+0.001)
plt.grid()
plt.show()


"""
Example 2 - Multi Variable Linear Regression

NOTE: The example and sample data is being taken from the "Machine Learning course by Andrew Ng" in Coursera.

Problem:
  Suppose you are the CEO of a restaurant franchise and are considering
  different cities for opening a new outlet. The chain already has trucks
  in various cities and you have data for profits and populations from
  the cities. You would like to use this data to help you select which
  city to expand to next.

  The file 'data/linear_reg/ex1data1.txt' contains the dataset for our
  linear regression problem. The first column is the population of a city
  and the second column is the profit of a food truck in that city.
  A negative value for profit indicates a loss.
"""

# initial imports
import numpy as np
import matplotlib.pyplot as plt
from models.data_preprocessing import feature_normalize, add_bias_unit
from models.linear_regression import gradient_descent, normal_equation

plt.ion()

# ----------------Loading X and y matrix ---------------
print('Loading data ...')

data = np.loadtxt('data/ex1data2.txt', delimiter=',')
X = data[:, :-1]  # 47x2
y = data[:, -1, None]  # 47x1
m = y.size  # 47

# printing first 5 elements
print(X[0:5, :])

# ----------------Feature Normalization -----------------
print("Normalizing the features")
X, mu, sigma = feature_normalize(X)

# adding intercept term to X
X = add_bias_unit(X)

print('Running gradient descent ...')

# Choose some alpha value
alpha = 0.03
num_iters = 400

# Init Theta and Run Gradient Descent
theta = np.zeros((X.shape[1], 1))
theta, J_history = gradient_descent(X, y, theta, alpha, num_iters)

# Plot the convergence graph
fig = plt.figure("Covariance Graph")
ax = fig.subplots()
ax.plot(range(J_history.size), J_history, lw=2)
ax.set_xlabel('Number of iterations')
ax.set_ylabel('Cost J')
fig.show()

# Display gradient descent's result
print('Theta computed from gradient descent: ')
print(theta)

# Estimate the price of a 1650 sq-ft, 3 br house

test_data = np.array([1650, 3]).reshape(1, 2)
test_data, _, __ = feature_normalize(test_data, mu, sigma)
test_data = add_bias_unit(test_data)
price = test_data.dot(theta)

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

print('Predicted price of a 1650 sq-ft, 3 br house (using gradient descent): {}'.format(price))

# ================Normal Equations ================

print('Solving with normal equations...')

# ----------------Loading X and y matrix ---------------
print('Loading data ...')

data = np.loadtxt('data/ex1data2.txt', delimiter=',')
X = data[:, :-1]  # 47x2
y = data[:, -1, None]  # 47x1
m = y.size  # 47

# Add intercept term to X
X = add_bias_unit(X)

# Calculate the parameters from the normal equation
theta = normal_equation(X, y)

# Display normal equation's result
print('Theta computed from the normal equations: ')
print(theta)

# Estimate the price of a 1650 sq-ft, 3 br house
test_data = np.array([1650, 3]).reshape(1, 2)
test_data = add_bias_unit(test_data)
price = test_data.dot(theta)

print('Predicted price of a 1650 sq-ft, 3 br house (using normal equations): {}'.format(price))

# bloking matplotlib figures for obervations
plt.ioff()
plt.show()


from sympy.mpmath.calculus import ODE_step_euler, ODE_step_rk4, odeint, arange
from sympy.mpmath import odefun, cos, sin, mpf, sinc, mp

solvers = [ODE_step_euler, ODE_step_rk4]

def test_ode1():
    """
    Let's solve:

    x'' + w**2 * x = 0

    i.e. x1 = x, x2 = x1':

    x1' =  x2
    x2' = -x1
    """
    def derivs((x1, x2), t):
        return x2, -x1

    for solver in solvers:
        t = arange(0, 3.1415926, 0.005)
        sol = odeint(derivs, (0., 1.), t, solver)
        x1 = [a[0] for a in sol]
        x2 = [a[1] for a in sol]
        # the result is x1 = sin(t), x2 = cos(t)
        # let's just check the end points for t = pi
        assert abs(x1[-1]) < 1e-2
        assert abs(x2[-1] - (-1)) < 1e-2

def test_ode2():
    """
    Let's solve:

    x' - x = 0

    i.e. x = exp(x)

    """
    def derivs((x), t):
        return x

    for solver in solvers:
        t = arange(0, 1, 1e-3)
        sol = odeint(derivs, (1.,), t, solver)
        x = [a[0] for a in sol]
        # the result is x = exp(t)
        # let's just check the end point for t = 1, i.e. x = e
        assert abs(x[-1] - 2.718281828) < 1e-2

def test_odefun_rational():
    mp.dps = 15
    # A rational function
    f = lambda t: 1/(1+mpf(t)**2)
    g = odefun(lambda x, y: [-2*x*y[0]**2], 0, [f(0)])
    assert f(2).ae(g(2)[0])

def test_odefun_sinc_large():
    mp.dps = 15
    # Sinc function; test for large x
    f = sinc
    g = odefun(lambda x, y: [(cos(x)-y[0])/x], 1, [f(1)], tol=0.01, degree=5)
    assert abs(f(100) - g(100)[0])/f(100) < 0.01

def test_odefun_harmonic():
    mp.dps = 15
    # Harmonic oscillator
    f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0])
    for x in [0, 1, 2.5, 8, 3.7]:    #  we go back to 3.7 to check caching
        c, s = f(x)
        assert c.ae(cos(x))
        assert s.ae(sin(x))


import numpy as np

np.array([1, 2])

# Exercice 2.3

print("Question 1 \n")

A, x = np.array([[1, 2, 3], [4, 5, 6]]), np.array([1, 2, 3])

print(A.shape, x.shape)
print(A.size, x.size)
print(A.ndim, x.ndim)
print(A.dtype, x.dtype)
print(A.sum(), x.sum())
print(A.prod(), x.prod())

print("\n Question2 \n")

nbColsA, nbLignesA = A.shape

print(nbColsA, nbLignesA)

prodLignesA = A.prod(axis=1)
prodColA = A.prod(axis=0)

print(prodLignesA, prodColA)

print("\nQuestion 3\n")

print(A*x)
# Multiplie chaque ligne de A par x (coefficient à coefficient)

print(A.dot(x))
# Fait la multiplication matricielle pour A et x

print(np.dot(A, x))
# Fait la multiplication matricielle pour A et x

# Exercice 2.3
print("\nExercice 2.3\n")
A, B, C, D = np.array([[7, 0], [-1, 5], [-1, 2]]), np.array([[1, 4], [-4, 0]]), np.array([7, 3]), np.array([8, 2])

print("A x B=", np.dot(A, B))
print("A x C=", np.dot(A, C))
print("C x D=", np.dot(C, D))
print("D x C=", np.dot(D, C))
print("D x B X C=", np.dot(np.dot(D, B), C))
print("AT x A=", np.dot(A.T, A))
print("A x AT=", np.dot(A, A.T))

# Exercice 2.4
print("\nExercice 2.4")

v1 = np.arange(1, 17, 1)
v2 = np.arange(0.0, 2.1, 0.2)
v3 = np.array([2**x for x in range(7)])

print(v1)
print(v2)
print(v3)

print("\nQuestion 2\n")
# Sûrement à refaire, en fonction du cours
A = np.array([[y**x for x in range(9)]for y in [2, 3, 5]])
B = np.array([np.arange(.0, 1.1, .2) for x in range(4)])
C = np.array([[y for x in range(7)] for y in [.0, .5, 1.]])

print(A)
print(B)
print(C)

print("\nQuestion 3\n")

A = np.array([[1 if (x == 0 or x == 9) else (1 if (y == 0 or y == 9) else (1 if x == y else 0)) for x in range(10)] for y in range(10)])

print(A)

print("\nQuestion 4\n")

L = np.array([[2 if i == j else (-1 if (i == j - 1 or i == j + 1) else 0) for j in range(10)] for i in range(10)])

print(L)

print("\nQuestion 5\n")

T = np.array([[1 if ((i + j) % 2 == 0) else 0 for j in range(10)] for i in range(10)])

print(T)


# My Gauss Siedel Program implemented in Python submitted to GeeksforGeeks: (under review)

import numpy as np

# Iteration Limit.
Limit = 10
"""
Co-efficients of matrix considered : (LHS vectors)
[[10., -1., 2., 0.],[-1., 11., -1., 3.],[2., -1., 10., -1.],[0., 3., -1., 8.]]=A
Corresponding constants of equation on other side: (RHS vector)
[6., 25., -11., 15.]=B
-> We will solve for Ax=B, where x=[x1,x2,x3,x4]
"""

# Initialize the matrix we want to perform Gauss Siedel Method on.
A = np.array([[10., -1., 2., 0.],
              [-1., 11., -1., 3.],
              [2., -1., 10., -1.],
              [0., 3., -1., 8.]])
# Initialize the RHS column vector.
b = np.array([6., 25., -11., 15.])

print("System of equations:")
# Displaying in matrix form, x1*(co-efficient) + x2*(co-efficient) +...
for i in range(A.shape[0]):
    row = ["{0:3g}*x{1}".format(A[i, j], j + 1) for j in range(A.shape[1])]
    print("[{0}] = [{1:3g}]".format(" + ".join(row), b[i]))

# Returning array of zeros for RHS vector (b) for initial approximation.
x = np.zeros_like(b)
for it_count in range(1, Limit):
    x_new = np.zeros_like(x)
    # Display solution-vector/approximate values of x1,x2,x3,x4 for each iteration.
    print("Iteration {0}: {1}".format(it_count, x))
    for i in range(A.shape[0]):
        s1 = np.dot(A[i, :i], x_new[:i])
        s2 = np.dot(A[i, i + 1:], x[i + 1:])
        x_new[i] = (b[i] - s1 - s2) / A[i, i]

     # Using tolerance limit to reach closer approximation to answer and checking if desired accuracy is obtained. (considering relative tolerance/error limit=1e-8, and checking between arrays x and new x) if desired accuracy is obtained, break.

    if np.allclose(x, x_new, rtol=1e-8):
        break
    x = x_new

# Solution to our matrix, vector x=[x1,x2,x3,x4]
print("Solution: {0}".format(x))
error = np.dot(A, x) - b
# Approximation error.
print("Error: {0}".format(error))

Computational Mathematics | Python/Gauss_Siedel.py


import numpy as _np

def euler(f, initial, interval, h):
    """This function solves the differential equation using Euler's method and initial
    value condition.

    Parameters
    ----------
    f : function
        function form of the differential equation to be solved
    initial : tuple
        tuple of the initial values
    interval : tuple
        tuple of the interval in which the differential eq
    h : float
        step size for each iteration

    Returns
    -------
    ts : 1d array
        array of t's
    ys : 1d array
        array of corresponding y's

    """
    a, b = interval
    ts = _np.arange(a, b + h, h)
    ys = _np.zeros_like(ts)

    if initial[0] != interval[0]:
        print("The point for initial value doesn't match the start of interval")
        return None, None

    ys[0] = initial[1]

    for i in range(len(ts) - 1):
        ys[i + 1] = ys[i] + f(ys[i], ts[i]) * h

    return ts, ys

def midpoint_euler(f, initial, interval, h):
    """This function solves the differential equation using Ralstone's method and initial
    value condition.

    Parameters
    ----------
    f : function
        function form of the differential equation to be solved
    initial : tuple
        tuple of the initial values
    interval : tuple
        tuple of the interval in which the differential eq
    h : float
        step size for each iteration

    Returns
    -------
    ts : 1d array
        array of t's
    ys : 1d array
        array of corresponding y's

    """
    A = 0.5
    B1 = 0.0
    B2 = 1
    a, b = interval
    ts = _np.arange(a, b + h, h)
    ys = _np.zeros_like(ts)

    if initial[0] != interval[0]:
        print("The point for initial value doesn't match the start of interval")
        return None, None

    ys[0] = initial[1]

    for i in range(len(ts) - 1):
        p_i = f(ys[i], ts[i])
        q_i = f(ys[i] + A * h * p_i, ts[i] + h * A)
        ys[i + 1] = ys[i] + (B1 * p_i + B2 * q_i) * h

    return ts, ys

def heun(f, initial, interval, h):
    """This function solves the differential equation using Heun's method and initial
    value condition.

    Parameters
    ----------
    f : function
        function form of the differential equation to be solved
    initial : tuple
        tuple of the initial values
    interval : tuple
        tuple of the interval in which the differential eq
    h : float
        step size for each iteration

    Returns
    -------
    ts : 1d array
        array of t's
    ys : 1d array
        array of corresponding y's

    """
    A = 1
    B1 = 0.5
    B2 = 0.5
    a, b = interval
    ts = _np.arange(a, b + h, h)
    ys = _np.zeros_like(ts)

    if initial[0] != interval[0]:
        print("The point for initial value doesn't match the start of interval")
        return None, None

    ys[0] = initial[1]

    for i in range(len(ts) - 1):
        p_i = f(ys[i], ts[i])
        q_i = f(ys[i] + A * h * p_i, ts[i] + h * A)
        ys[i + 1] = ys[i] + (B1 * p_i + B2 * q_i) * h

    return ts, ys

def ralstone(f, initial, interval, h):
    """This function solves the differential equation using Ralstone's method and initial
    value condition.

    Parameters
    ----------
    f : function
        function form of the differential equation to be solved
    initial : tuple
        tuple of the initial values
    interval : tuple
        tuple of the interval in which the differential eq
    h : float
        step size for each iteration

    Returns
    -------
    ts : 1d array
        array of t's
    ys : 1d array
        array of corresponding y's

    """
    A = 2 / 3
    B1 = 0.25
    B2 = 0.75
    a, b = interval
    ts = _np.arange(a, b + h, h)
    ys = _np.zeros_like(ts)

    if initial[0] != interval[0]:
        print("The point for initial value doesn't match the start of interval")
        return None, None

    ys[0] = initial[1]

    for i in range(len(ts) - 1):
        p_i = f(ys[i], ts[i])
        q_i = f(ys[i] + A * h * p_i, ts[i] + h * A)
        ys[i + 1] = ys[i] + (B1 * p_i + B2 * q_i) * h

    return ts, ys

def rk3(f, initial, interval, h):
    """This function solves the differential equation using 3rd order Runga-Kutta
    method and initial value condition.

    Parameters
    ----------
    f : function
        function form of the differential equation to be solved
    initial : tuple
        tuple of the initial values
    interval : tuple
        tuple of the interval in which the differential eq
    h : float
        step size for each iteration

    Returns
    -------
    ts : 1d array
        array of t's
    ys : 1d array
        array of corresponding y's

    """
    a, b = interval
    ts = _np.arange(a, b + h, h)
    ys = _np.zeros_like(ts)

    if initial[0] != interval[0]:
        print("The point for initial value doesn't match the start of interval")
        return None, None

    ys[0] = initial[1]

    for i in range(len(ts) - 1):
        p_i = f(ys[i], ts[i])
        q_i = f(ys[i] + 0.5 * h * p_i, ts[i] + h * 0.5)
        r_i = f(ys[i] - p_i * h + 2 * q_i * h, ts[i] + h)
        ys[i + 1] = ys[i] + (p_i + 4 * q_i + r_i) * h / 6

    return ts, ys

def rk4(f, initial, interval, h):
    """This function solves the differential equation using 4th order Runga-Kutta
    method and initial value condition.

    Parameters
    ----------
    f : function
        function form of the differential equation to be solved
    initial : tuple
        tuple of the initial values
    interval : tuple
        tuple of the interval in which the differential eq
    h : float
        step size for each iteration

    Returns
    -------
    ts : 1d array
        array of t's
    ys : 1d array
        array of corresponding y's

    """
    a, b = interval
    ts = _np.arange(a, b + h, h)
    ys = _np.zeros_like(ts)

    if initial[0] != interval[0]:
        print("The point for initial value doesn't match the start of interval")
        return None, None

    ys[0] = initial[1]

    for i in range(len(ts) - 1):
        p_i = f(ys[i], ts[i])
        q_i = f(ys[i] + 0.5 * h * p_i, ts[i] + h * 0.5)
        r_i = f(ys[i] + 0.5 * h * q_i, ts[i] + h * 0.5)
        s_i = f(ys[i] + h * r_i, ts[i] + h)
        ys[i + 1] = ys[i] + (p_i + 2 * q_i + 2 * r_i + s_i) * h / 6

    return ts, ys


"""Gell-Mann matrices."""
from scipy.sparse import csr_matrix

import numpy as np

def gell_mann(ind: int, is_sparse: bool = False) -> np.ndarray:
    r"""
    Produce a Gell-Mann operator [WikGM]_.

    Generates the 3-by-3 Gell-Mann matrix indicated by the value of
    :code:`ind`.  When :code:`ind = 0` gives the identity matrix, while values
    1 through 8 each indicate one of the other 8 Gell-Mann matrices.

    The 9 Gell-Mann matrices are defined as follows:

    .. math::
        \begin{equation}
            \begin{aligned}
                \lambda_0 = \begin{pmatrix}
                                1 & 0 & 0 \\
                                0 & 1 & 0 \\
                                0 & 0 & 1
                            \end{pmatrix}, \quad
                \lambda_1 = \begin{pmatrix}
                                0 & 1 & 0 \\
                                1 & 0 & 0 \\
                                0 & 0 & 0
                            \end{pmatrix}, \quad &
                \lambda_2 = \begin{pmatrix}
                                0 & -i & 0 \\
                                i & 0 & 0 \\
                                0 & 0 & 0
                            \end{pmatrix},  \\
                \lambda_3 = \begin{pmatrix}
                                1 & 0 & 0 \\
                                0 & -1 & 0 \\
                                0 & 0 & 0
                            \end{pmatrix}, \quad
                \lambda_4 = \begin{pmatrix}
                                0 & 0 & 1 \\
                                0 & 0 & 0 \\
                                1 & 0 & 0
                            \end{pmatrix}, \quad &
                \lambda_5 = \begin{pmatrix}
                                0 & 0 & -i \\
                                0 & 0 & 0 \\
                                i & 0 & 0
                            \end{pmatrix},  \\
                \lambda_6 = \begin{pmatrix}
                                0 & 0 & 0 \\
                                0 & 0 & 1 \\
                                0 & 1 & 0
                            \end{pmatrix}, \quad
                \lambda_7 = \begin{pmatrix}
                                0 & 0 & 0 \\
                                0 & 0 & -i \\
                                0 & i & 0
                            \end{pmatrix}, \quad &
                \lambda_8 = \frac{1}{\sqrt{3}} \begin{pmatrix}
                                                    1 & 0 & 0 \\
                                                    0 & 1 & 0 \\
                                                    0 & 0 & -2
                                                \end{pmatrix}.
                \end{aligned}
            \end{equation}

    Examples
    ==========

    The Gell-Mann matrix generated from :code:`idx = 2` yields the following
    matrix:

    .. math::

        \lambda_2 = \begin{pmatrix}
                            0 & -i & 0 \\
                            i & 0 & 0 \\
                            0 & 0 & 0
                    \end{pmatrix}

    >>> from toqito.matrices import gell_mann
    >>> gell_mann(2)
    [[ 0.+0.j, -0.-1.j,  0.+0.j],
     [ 0.+1.j,  0.+0.j,  0.+0.j],
     [ 0.+0.j,  0.+0.j,  0.+0.j]]

    References
    ==========
    .. [WikGM] Wikipedia: Gell-Mann matrices,
        https://en.wikipedia.org/wiki/Gell-Mann_matrices

    :param ind: An integer between 0 and 8 (inclusive).
    :param is_sparse: Boolean to determine whether matrix is sparse.
    """
    if ind == 0:
        gm_op = np.identity(3)
    elif ind == 1:
        gm_op = np.array([[0, 1, 0], [1, 0, 0], [0, 0, 0]])
    elif ind == 2:
        gm_op = np.array([[0, -1j, 0], [1j, 0, 0], [0, 0, 0]])
    elif ind == 3:
        gm_op = np.array([[1, 0, 0], [0, -1, 0], [0, 0, 0]])
    elif ind == 4:
        gm_op = np.array([[0, 0, 1], [0, 0, 0], [1, 0, 0]])
    elif ind == 5:
        gm_op = np.array([[0, 0, -1j], [0, 0, 0], [1j, 0, 0]])
    elif ind == 6:
        gm_op = np.array([[0, 0, 0], [0, 0, 1], [0, 1, 0]])
    elif ind == 7:
        gm_op = np.array([[0, 0, 0], [0, 0, -1j], [0, 1j, 0]])
    elif ind == 8:
        gm_op = np.array([[1, 0, 0], [0, 1, 0], [0, 0, -2]]) / np.sqrt(3)
    else:
        raise ValueError("Gell-Mann index values can only be values from 0 to " "8 (inclusive).")

    if is_sparse:
        gm_op = csr_matrix(gm_op)

    return gm_op


"""
test_numpy.linspace_example_01.py
This is an example code for numpy.linspace
https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html

"""

import numpy as np
import matplotlib.pyplot as plt

np.linspace(2.0,3.0, num=5)
np.linspace(2.0,3.0, num=5, endpoint=False)
np.linspace(2.0,3.0, num=5, retstep=True)

N = 8
y = np.zeros(N)
x1 = np.linspace(0,10,N, endpoint=True)
x2 = np.linspace(0,10,N, endpoint=False)
plt.plot(x1,y,'o')
plt.plot(x2,y+0.5, 'o')
plt.ylim([-0.5,1])
plt.show()


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

def composite_midpoint(f, m, a=0.0, b=1.0):
    h = (b - a) / m
    return h * sum([f((a+h/2.0) + i*h) for i in xrange(0, m)])

def composite_trapezium(f, m, a=0.0, b=1.0):
    h = (b - a) / m
    return h/2.0 * (f(a) + f(b) + 2 * sum([ f(a + i * h) for i in xrange(1, m)]))

def composite_simpson(f, m, a=0.0, b=1.0):
    sum = float(f(a) + f(b))
    h   = (b-a) / (2*m)
    oddSum, evenSum = 0.0, 0.0

    for i in range(1, m): #evaluating all odd values of n (not first and last)
        oddSum += f(2 * h * i + a)
    sum += oddSum * 2
    for i in range(1,m+1): #evaluating all even values of n (not first and last)
        evenSum += f(h * (-1 + 2 * i) + a)
    sum += evenSum * 4
    return sum * h / 3

def debug(abs_err_cm, abs_err_ct, abs_err_cs, debug=True):
    if debug == True:
        print("DEBUG MODE STATUS = <ON>")
        print("Composite Midpoint\tComposite trapezium\tComposite_Simpson")
        for m, t, s in zip(abs_err_cm, abs_err_ct, abs_err_cs):
                print "{:.20f}     ".format(m), \
                      "{:.20f}     ".format(t), \
                      "{:.20f}     ".format(s)

def plot_abs_errs(abs_err_cm, abs_err_ct, abs_err_cs):
    plt.title("|xc-x| of: The Composite Midpoint, Simpson & \
              Trapezium Methods against h")
    plt.ylabel("Composite Midpoint vs Composite Simpson vs Composite Trapezium")
    plt.xlabel("Number of Points")
    plt.xscale('log')
    plt.yscale('log')
    plt.plot(M, abs_err_cm, 'k-', label="Composite Midpoint")
    plt.plot(M, abs_err_ct, 'r-', label="Composite Trapezium")
    plt.plot(M, abs_err_cs, 'b-', label="Composite Simpson")
    plt.legend(bbox_to_anchor=(.95, .9))
    plt.show()

if __name__ == "__main__":
    import matplotlib.pyplot as plt
    from math import (exp, pi, sin)
    from scipy import (integrate, special)
    from numpy import (abs, array, linspace)

    f = lambda x : exp(sin(2 * pi * x))
    I = integrate.quad(f, 0.0, 1.0)[0]
    M = linspace(3, 19, 5)

    abs_err_cm = [abs(composite_midpoint(f, int(m)) - I) for m in M]
    abs_err_ct = [abs(composite_trapezium(f, int(m)) - I) for m in M]
    abs_err_cs = [abs(composite_simpson(f, int(m)) - I) for m in M]
    debug(abs_err_cm, abs_err_ct, abs_err_cs, debug=True)
    plot_abs_errs(abs_err_cm, abs_err_ct, abs_err_cs)
else:
    from sys import exit
    exit("USAGE: python question2.py")


import numpy as np
import matplotlib.pyplot as plt

# 평균 제곱 오차 mean squared error
def mean_squared_error(y,t):
    return 0.5 * np.sum((y-t)**2)

y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]

# print(mean_squared_error(np.array(y), np.array(t)))

y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
# print(mean_squared_error(np.array(y), np.array(t)))

# 교차 엔트로피 오차 cross entropy error
x = np.arange(0.001, 1.0, 0.001)
y = np.log(x)

# plt.plot(x, y)
# plt.ylim(-5.0, 0.0) # y축의 범위 지정
# plt.show()

def cross_entropy_error(y,t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta)) # 아주 작은 값 delta를 더하는 이유 : np.log에 함수 0을 입력하면 -inf(무한)가 되어 계산 x

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]

# print(cross_entropy_error(np.array(y), np.array(t)))

y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
# print(cross_entropy_error(np.array(y), np.array(t)))

# 미니 배치 학습

import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

print(x_train.shape)
print(t_train.shape)

train_size = x_train.shape[0]
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size)
# 훈련 데이터에서 무작위로 10장만 꺼내기? np.random.choice() 사용
# np.random.choice(60000,10)은 0에서 60000 미만의 수 중 무작위로 10개를 골라냄
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]

# (배치용) 교차 엔트로피 오차 구현하기
def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)

    batch_size = y.shape[0]
    return -np.sum(t * np.log(y)) / batch_size


###
# Introduction to Data Science Homework Assignment # 1
# Student: Alan Fernandez, aefernandez@wpi.edu
# Date: 08/29/18
# Course: DS501, Introduction to Data Science (Grad Level)
# Worcester Polytechnic Institute (WPI), Worcester, MA
###

import numpy as np

from problem3 import random_walk
from problem4 import compute_S

#-------------------------------------------------------------------------
'''
    Problem 5: Solving sink-region problem in PageRank
    In this problem, we implement the pagerank algorithm which can solve both the sink node problem and sink region problem.
    We will consider a random surfer model where a user has 2 options at every timestep: (option 1) randomly follow a link on the page or (option 2) randomly go to any page in the graph.
    The probabilities are as follows:
        Randomly follow a link: alpha, for example, 0.95
        Randomly go to any page in the graph: (1 - alpha), for example, 0.05
    You could test the correctness of your code by typing `nosetests test5.py` in the terminal.
'''

#--------------------------
def compute_G(A, alpha = 0.95):
    '''
        compute the pagerank transition Matrix G from addjacency matrix A, which solves both the sink node problem and the sing region problem.
        G[j][i] represents the probability of moving from node i to node j.
        If node i is a sink node, S[j][i] = 1/n.
        Input:
                A: adjacency matrix, a (n by n) numpy matrix of binary values. If there is a link from node i to node j, A[j][i] =1. Otherwise A[j][i]=0 if there is no link.
                alpha: a float scalar value, which is the probability of choosing option 1 (randomly follow a link on the page)
        Output:
                G: the transition matrix, a (n by n) numpy matrix of float values.  G[j][i] represents the probability of moving from node i to node j.
    The values in each column of matrix G should sum to 1.
    '''
    #########################################
    ## INSERT YOUR CODE HERE
    # Generate the new matrix with equal probabilities for every node
    n = A.shape[0]

    equal_probability_matrix = np.zeros(list(A.shape))
    equal_probability_matrix.fill(1/n)

    # Compute the transitional matrix with the sink node problem solved
    S = compute_S(A)

    # Combine the two matrices to form the final transitional matrix
    G = equal_probability_matrix * (1-alpha) + S * alpha

    #########################################
    return G

#--------------------------
def pagerank(A, alpha = 0.95):
    '''
        The final PageRank algorithm, which solves both the sink node problem and sink region problem.
        Given an adjacency matrix A, compute the pagerank score of all the nodes in the network.
        Input:
                A: adjacency matrix, a numpy matrix of binary values. If there is a link from node i to node j, A[j][i] =1. Otherwise A[j][i]=0 if there is no link.
                alpha: a float scalar value, which is the probability of choosing option 1 (randomly follow a link on the page)
        Output:
                x: the ranking scores, a numpy vector of float values, such as np.array([[.3], [.5], [.7]])
    '''

    # Initialize the score vector with all one values
    num_nodes, _ = A.shape # get the number of nodes (n)
    x_0 =  np.ones((num_nodes,1)) # create an all-one vector of shape (n by 1)

    # compute the transition matrix from adjacency matrix
    G = compute_G(A, alpha)

    # random walk
    x, n_steps = random_walk(G,x_0)
    return x


import numpy
from matplotlib import pyplot

def euler_pc(f, x_end, y0, N):
    x, dx = numpy.linspace(0, x_end, N+1, retstep=True)
    y = numpy.zeros((len(y0),N+1))
    y[:,0] = y0
    for n in range(N):
        fn = f(x[n], y[:,n])
        yp = y[:,n] + dx * fn
        y[:,n+1] = y[:,n] + dx / 2 * (fn + f(x[n+1], yp))
    return x, dx, y

def ab2(f, x_end, y0, N):
    x, dx = numpy.linspace(0, x_end, N+1, retstep=True)
    y = numpy.zeros((len(y0),N+1))
    fn = numpy.zeros((len(y0),N+1))
    y[:,0] = y0
    fn[:,0] = f(x[0], y[:,0])
    x_epc, dx_epc, y_epc = euler_pc(f, dx, y0, 1)
    y[:,1] = y_epc[:,1]
    for n in range(1,N):
        fn[:,n] = f(x[n], y[:,n])
        y[:,n+1] = y[:,n] + dx * (3 * fn[:,n] - fn[:,n-1]) / 2
    return x, dx, y

def milne(f, x_end, y0, N):
    x, dx = numpy.linspace(0, x_end, N+1, retstep=True)
    y = numpy.zeros((len(y0),N+1))
    fn = numpy.zeros((len(y0),N+1))
    y[:,0] = y0
    fn[:,0] = f(x[0], y[:,0])
    x_epc, dx_epc, y_epc = euler_pc(f, dx, y0, 1)
    y[:,1] = y_epc[:,1]
    for n in range(1,N):
        fn[:,n] = f(x[n], y[:,n])
        yp = y[:,n] + dx * (3 * fn[:,n] - fn[:,n-1]) / 2 #AB2 predictor
        fp = f(x[n+1], yp)
        y[:,n+1] = y[:,n-1] + dx * (fp + 4 * fn[:,n] + fn[:,n-1]) / 3
    return x, dx, y

if __name__=="__main__":

    def f_exp(x, y):
        return -y

    x, dx, y_ab2 = ab2(f_exp, 30, [1], 3000)
    pyplot.figure(figsize=(12,6))
    pyplot.plot(x, y_ab2[0,:])
    pyplot.ylim(-1.1,1.1)
    pyplot.xlabel(r"$x$")
    pyplot.ylabel("Adams-Bashforth 2")
    pyplot.show()

    x, dx, y_milne = milne(f_exp, 30, [1], 3000)
    pyplot.figure(figsize=(12,6))
    pyplot.plot(x, y_milne[0,:])
    pyplot.ylim(-1.1,1.1)
    pyplot.xlabel(r"$x$")
    pyplot.ylabel("Milne")
    pyplot.show()


import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../../matplotlibrc')

import numpy as np
import numpy.linalg as la
from matplotlib import pyplot as plt

def mc_circle():
    np.random.seed(42)
    points = np.random.rand(2, 500).T
    points = 4*(points-.5)
    pointsNorm = np.hypot(points[:,0],points[:,1]) <= 1
    InCircle = points[pointsNorm]
    OutCircle = points[~pointsNorm]
    plt.plot(InCircle[:,0], InCircle[:,1], 'r.')
    plt.plot(OutCircle[:,0], OutCircle[:,1], 'b.')

    # Plot the circle
    theta = np.linspace(0, 2*np.pi, 50)
    plt.plot(np.cos(theta),np.sin(theta),'k')

    plt.axes().set_aspect('equal')
    plt.axis([-2, 2, -2, 2])

    plt.savefig("figures/MC_Circle.pdf")
    plt.clf()

def prob1(N=10000):
    """Return an estimate of the volume of the unit sphere using Monte
    Carlo Integration.

    Input:
        N (int, optional) - The number of points to sample. Defaults
            to 10000.

    """
    points = np.random.rand(3, N)
    points = points*2 - 1
    radiusMask = la.norm(points,axis=0)
    radiusMask[radiusMask>1] = 0
    numInSphere = np.count_nonzero(radiusMask)
    return 8.*numInSphere/N

def mc_error_plot(numIters=50):
    actual = 4.1887902047863905

    N = [i*1000 for i in xrange(1,51)]
    N = [50,100,500] + N
    errors = []

    for n in N:
        meanErr = 0.
        for i in xrange(numIters):
            I = prob1(n)
            err = np.abs(I - actual)/actual
            meanErr += err
        errors.append(meanErr/float(numIters))

    plt.plot(N,errors,label='Error')
    plt.plot(N,[1./n**0.5 for n in N],'r--',label=r'$1/\sqrt{N}$')
    plt.ylim([0,max(errors)])
    plt.xlim([0,max(N)])
    plt.xlabel(r'$N$')
    plt.ylabel('Relative error')
    plt.legend()

    plt.savefig("figures/MC_error_2.pdf")
    plt.clf()

if __name__ == "__main__":
    #mc_circle()
    mc_error_plot()


import numpy as np
from sympy import *
import matplotlib.pyplot as plt
import time

def actual_result(func, val):
	x = Symbol('x')
	dfunc = diff(func, 'x')
	x = val
	return eval(str(dfunc))

def two_point(func, x, h):
	funcx = eval(str(func))
	x = x+h
	funcxh = eval(str(func))
	return (funcxh - funcx)/h

def three_point(func, x, h):
	x = x+h
	funcxh = eval(str(func))
	x = x-2*h
	funcxmh = eval(str(func))
	return (funcxh - funcxmh)/(2*h)

def plot(Result2Point,Result3Point,Error2Point,Error3Point):
	plt.plot(Result2Point, 'k--',label= "2 Point")
	plt.plot(Result3Point,'k:',label= "3 Point")
	plt.plot(Error2Point,'k',label= "2 Point Error")
	plt.plot(Error3Point,'k',label= "3 Point Error")

	legend = plt.legend(loc='upper center', shadow=True)
	frame = legend.get_frame()
	frame.set_facecolor('0.90')

	plt.show()

def differenceMethods(x,y,z):
	return 0

def main():
	x = Symbol('x')
	Result2Point= []
	Result3Point= []
	Error2Point = []
	Error3Point = []

	## User input
	func = sin(x)
	xval = 0.0
	h=[0.1,0.01,0.001]

	start = time.time()
	actualResult = actual_result(func, xval)
	for item in h:
		Result2Point.append(two_point(func, xval, item))
		Result3Point.append(three_point(func, xval, item))

	Error2Point = np.asarray(Result2Point) - actualResult
	Error3Point = np.asarray(Result3Point) - actualResult

	end = time.time()
	timeElapsed = end-start
	print ("Elapsed time: %f" % timeElapsed)

	plot(Result2Point, Result3Point, Error2Point, Error3Point)

	#print actual_result(func, xval)

if __name__ =='__main__':
	main()