# -*- coding: utf-8 -*-
"""
Solution to Project Euler problem 75 - Singular integer right triangles

Author: Jaime Liew
https://github.com/jaimeliew1/Project_Euler_Solutions

Given a pair if integers, m and n such that m > n > 0, a pythagorian triple is:
a = m**2 - n**2, b = 2*m*n, c= m**2 + n**2
The length of the sides, L, is:
    L = a + b + c
    L = 2*m**2 + 2*m*n
    L = 2*m(m + n)

Lmax = 1,500,000
assuming m = n+1, Lmax = 2*(n+1)(2n+1) => max(n) = 611.62
"""

import numpy as np

def run():
    #%% New attempt - 9 March 2018
    makeTriple = lambda m, n: [m ** 2 - n ** 2, 2 * m * n, m ** 2 + n ** 2]

    Lmax = 1500000
    # Step 1: Generate triples based on above formula
    Triples = []
    for n in range(1, 611 + 1):
        m = n + 1
        while 2 * m * (m + n) < Lmax:
            Triples.append(makeTriple(m, n))
            m += 1
    Triples = np.sort(Triples)

    """
    The above algorithm does not generate all pythagorean triples. by
    multiplying the created triples by an integer, k, all triples can be found.
    """

    moreTriples = []
    for t in Triples:
        k = 2
        while sum(k * t) < Lmax:
            moreTriples.append(list(k * t))
            k += 1

    Triples = np.vstack([Triples, moreTriples])

    # eliminate repeated triples
    Triples = np.unique(Triples, axis=0)
    """
    This should be all the triples with L < Lmax. so lets find L for each triple
    and count occurennces.
    """
    L = Triples.sum(1)
    unique, counts = np.unique(L, return_counts=True)

    return sum(counts == 1)

if __name__ == "__main__":
    print(run())


print()
"""
Utilizando los conocimientos de la primera parte de la asignatura podemos
encontrar las soluciones numéricas cualquier una función de una variable.
Con un poco de álgebra simple se puede llegar a obtener un polinomio en
función de "u" que verifique las condiciones de solución
f(u, v) = g(u, v) = 0, sobre el cual se aplica el
programa que obtiene sus soluciones numéricas.
"""

# program setup
import numpy as np
import matplotlib.pyplot as plt

plt.rc('text', usetex = True)
plt.rc('font', family = 'serif')

def find_sols(f, arr, sign_check_func = lambda x, y: x*y < 0, verbose = False):
    """
    Given an array of section points, checks if function *f* has a solution
    in any of the intervals formed by said points.
    """
    sols = []
    for i in range(len(arr)-1):
        if sign_check_func(f(arr[i]), f(arr[i+1])):
            sols.append([arr[i], arr[i+1]])

    if verbose:
        print("solution(s) in: ", *sols, sep = '\n')
    return sols

def bisect(interval):
    """
    Splits number interval (given as a list of two values) in two halves.
    """
    a, b = interval
    h = (a+b)/2
    return [[a, h], [h, b]]

def bisect_solve(f, interval, iters = 8, sign_check_func = lambda x, y: x*y < 0,
    verbose = False):
    """
    Approximates a solution to a function *f* that is known to be in a given
    interval through `iters` number of iterations using the bisection method.
    """
    si = interval # starting interval
    try:
        for _ in range(iters):
            interval = [half for half in bisect(interval) \
                if sign_check_func(f(half[0]), f(half[1]))][0]
    except IndexError:
        if verbose:
            print(f"stopped at {_} iterations")
        if si == interval:
            raise ValueError(
                "solution was non-existent in the given interval")
    else:
        if verbose:
            print("successfully completed all iterations")

    sol = (interval[0] + interval[1])/2

    if f(sol) == np.inf:
        # cannot avoid using numpy for this
        if verbose:
            print(f"solution: x = {sol} -> FALSE SOLUTION (y = oo)")
        return sol

    if verbose:
        print(
            f"solution: x = {sol:.6f} (y = {f(sol):.6e})\niterations: {iters}")
    return sol

# with simple algebra, we find the polinomial associated to the problem
a = 0.1; b = 0.1; gamma = 1
def pol(u, a = a, b = b, gamma = gamma):
    return u * ((a-u) * (u-1) - (b/gamma))

# computation parameters
interval = [-1, 1] # interval chosen by inspection
xmin, xmax = interval
n = 201 # number of subintervals
bisect_iters = 999 # iterations for the bisect method

# helper arrays
xarr = np.linspace(xmin, xmax, n+1)
xlin = np.linspace(xmin, xmax, 1000)

# calculate the intervals where there is a solution
sols = find_sols(pol, xarr)
try:
    assert len(sols) > 0
except(AssertionError, TypeError):
    print("There are no solutions for f(x) in the given interval.")
print("There {} in: "
    .format("is a solution" if len(sols) == 1 else "are solutions"),
    *sols, sep = '\n', end = '\n\n')

bisect_sol_points = []
for sol in sols:
    print(f"case: solution in {sol}")
    bisect_sol_points.append(bisect_solve(pol, sol, iters = bisect_iters))
    print(f"solution: u = {bisect_sol_points[sols.index(sol)]}")
    print(f"iterations: {bisect_iters}", end = '\n\n')

# plotting
plt.figure(figsize = (9, 6))
plt.title(r"$P(u) = u\,\left[(a-u)\,(u-1) - (b/\gamma)\right]$", fontsize = 16)
plt.xlabel(r"$u$", fontsize = 16)
plt.ylabel(r"$P(u)$", fontsize = 16)

plt.plot(xlin, pol(xlin), 'blue')

# highlight the solution points
plt.hlines(0, min(xlin), max(xlin), 'black')
gsize = max(pol(xlin)) - min(pol(xlin))  # plot vertical size
for sol in bisect_sol_points:
    plt.vlines(sol, pol(sol)-gsize/20, pol(sol)+gsize/20, color='red')
    if bisect_sol_points.index(sol) == 1:
        plt.text(sol, pol(sol)+gsize/16+0.1, f'u = {sol:.5f}, v = {b/gamma*sol:.5f}',
            horizontalalignment = 'center')
    else:
        plt.text(sol, pol(sol)+gsize/16, f'u = {sol:.5f}, v = {b/gamma*sol:.5f}',
            horizontalalignment = 'center')

plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.savefig("pol.png")
plt.show()


import numpy as np

def integrate(deriv, init, time_min, time_max, numSteps, step):
    '''
    Solves a differential equation using a callable method from time_min to
    time_max, using a step of time_step

    deriv: callable; the derivative
    init: numpy 1D array; initial condition
    time_min: float; Minimum time
    time_max: float; Maximum time
    numSteps: int; number of stes
    step: callable; takes deriv, current time, current y, and time step and
    returns the next current y
    '''
    dim = init.shape[0]
    # checks for 1D array
    assert(init.shape == (dim,))

    # initialize times
    times = np.linspace(time_min, time_max, numSteps+1)
    time_step = float(time_max - time_min) / (numSteps - 1)

    ys = np.zeros((numSteps+1, dim))
    ys[0] = init

    for ind in xrange(numSteps):
        t = times[ind]
        curr = ys[ind]
        ys[ind + 1] = step(deriv, t, curr, time_step)

    return ys

def forward_euler_step(deriv, t, u, dt):
    return(u + dt * deriv(t, u))

def forward_euler(deriv, init, time_min, time_max, numSteps):
    return(integrate(deriv, init, time_min, time_max, numSteps,
            forward_euler_step))

def rk4_step(deriv, t, u, dt):
    k1 = dt*deriv(t, u)
    k2 = dt*deriv(t + 0.5*dt, u + 0.5*k1)
    k3 = dt*deriv(t + 0.5*dt, u + 0.5*k2)
    k4 = dt*deriv(t + dt, u + k3)

    return u+(k1+2.*k2+2.*k3+k4) / 6.
    #return(u + ((dt/6.) * (k1 + 2*k2 + 2*k3 + k4)))

def rk4(deriv, init, time_min, time_max, numSteps):
    return(integrate(deriv, init, time_min, time_max, numSteps, rk4_step))

#runge-kutta second order
def midpoint_step(deriv, t, u, dt):
    k1 = dt * deriv(t, u)
    return(u + dt*deriv(t + 0.5*dt, u + 0.5*k1))

def midpoint_method(deriv, init, time_min, time_max, numSteps):
    return(integrate(deriv, init, time_min, time_max, numSteps, midpoint_step))

if __name__ == "__main__":

    def deriv(t, u):
        y = u[0]
        v = u[1]
        return(np.array([v,
                        -2*v - 5*y]))

    def exact(t):
        exps = np.exp(-t)
        return(exps * np.cos(2*t) + 0.5*exps * np.sin(2*t))

    #nsteps_lst = [200, 400, 800, 1600, 3200, 6400, 12800, 25600, 51200, 102400, 204800, 409600]
    #nsteps_lst = [32, 64, 128, 256, 512, 1024]
    nsteps_lst = [200, 400, 800, 1600, 3200, 6400, 12800, 25600]
    init = np.array([1., 0.])
    tmin = 0.0
    tmax = 30.
    #print("type,nsteps,err")
    with open('out', 'wb') as f:
        f.write('type,nsteps,err\n')
        for nsteps in nsteps_lst:
            fe_res = forward_euler(deriv, init, tmin, tmax, nsteps)[:,0]
            rk4_res = rk4(deriv, init, tmin, tmax, nsteps)[:,0]
            midpoint_res = midpoint_method(deriv, init, tmin, tmax, nsteps)[:,0]

            times = np.linspace(tmin, tmax, nsteps+1)
            exact_res = exact(times)
            fe_err = np.abs(fe_res - exact_res)
            rk4_err = np.abs(rk4_res - exact_res)
            midpoint_err = np.abs(midpoint_res - exact_res)

            f.write("euler,%d,%e" %(nsteps, np.mean(fe_err)) + '\n')
            f.write("rk4,%d,%e" %(nsteps, np.mean(rk4_err)) + '\n')
            f.write("midpt,%d,%e" %(nsteps, np.mean(midpoint_err)) + '\n')


import numpy as np
import scipy.io as sio

def generate_mmc_center(var, dim_dense, num_class):

    mmc_centers = np.zeros((num_class, dim_dense))
    mmc_centers[0][0] = 1
    for i in range(1,num_class):
        for j in range(i):
            mmc_centers[i][j] = - (1/(num_class-1) + np.dot(mmc_centers[i],mmc_centers[j])) / mmc_centers[j][j]
        mmc_centers[i][i] = np.sqrt(np.absolute(1 - np.linalg.norm(mmc_centers[i]))**2)
    for k in range(num_class):
        mmc_centers[k] = var * mmc_centers[k]

    return mmc_centers

var, dim_dense, num_class = 10 , 256 , 10
mmc_centers = generate_mmc_center(var, dim_dense, num_class)

print("MMC centers shape:", mmc_centers.shape)
print("MMC centers:", mmc_centers)

sio.savemat('./meanvar'+str(var)+'_featuredim'+str(dim_dense)+'_class'+str(num_class)+'.mat', {'mean_logits': mmc_centers})


import numpy as np
import random

dataFile = 'data_banknote_authentication.txt'

def loadData(file):
    x = []
    y = []

    with open(file) as f:
        data = f.read()
    data = data.split()
    random.shuffle(data)

    for line in data:
        line = list(map(float, line.split(',')))
        x.append(line[:4])
        y.append(line[4])

    x_train = x[:1000]
    y_train = y[:1000]
    x_test = x[1000:]
    y_test = y[1000:]
    x_train = np.array(x_train)
    y_train = np.array(y_train)
    x_test = np.array(x_test)
    y_test = np.array(y_test)
    y_train = np.reshape(y_train, (y_train.shape[0], 1))
    y_test = np.reshape(y_test, (y_test.shape[0], 1))

    return x_train, y_train, x_test, y_test

class LogisticRegression:
    def __init__(self, input_dim = 1):
        self.w = np.ones((input_dim+1,1))

    def __sigmoid(self, v):
        return 1/(1+np.exp(-v))

    def fit(self, x, y, lr, epochs, verbose = True, ):
        X0 = np.ones((x.shape[0],1))
        X = np.hstack((X0, x))
        assert X.shape[1] == self.w.shape[0], "matrix x does not match input_dim"
        assert X.shape[0] == y.shape[0], "matrix x and vector y have different number of samples"

        for i in range(epochs):
            nabla = (1/y.shape[0]) * np.transpose(X) @ (y - self.__sigmoid(X @ self.w))

            self.w += lr * nabla

            if verbose:
                print(f"[Epoch: {i+1}/{epochs}]: accuracy: {self.evaluate(x, y)*100}%")

    def evaluate(self, X, y):
        X0 = np.ones((X.shape[0],1))
        X = np.hstack((X0, X))
        assert X.shape[1] == self.w.shape[0], "matrix x does not match input_dim"
        assert X.shape[0] == y.shape[0], "matrix x and vector y have different number of samples"

        predictions = self.__sigmoid(X @ self.w)

        diff = np.abs(predictions - y)
        n_correct = (diff < 0.5).sum()

        return n_correct / y.shape[0]

def main():
    x_train, y_train, x_test, y_test = loadData(dataFile)
    print(x_train.shape, x_test.shape)
    print(y_train.shape, y_test.shape)

    lr = LogisticRegression(4)
    lr.fit(x_train, y_train, 1, 30)

    print(lr.evaluate(x_test, y_test))

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
import sympy as sy

from dewloosh.math.linalg.tensor3333 import ComplianceTensor

__all__ = ['smat_sym_ortho_3d', 'cmat_sym_ortho_3d']

imap_ref_ortho_3D = ComplianceTensor.__imap__
upper = lambda ij : (ij[1], ij[0]) if ij[0] >= ij[1] else (ij[0], ij[1])

def smat_sym_ortho_3d(*args, imap : dict=None, simplify=True, **subs):
    """
    Reurns the compliance matrix for a 3d orthotropic
    material in symbolic form.

    Parameters
    ----------
    imap : dict, optional
        Index map. Default is None.

    subs : dict, optional
        A dictionary of parameters to substitute.

    Notes
    -----
    The resulting symbolic matrix has 12 free symbols,
    but only 9 of these are independent.

    Returns
    -------
    sympy.Matrix
        6x6 symbolic compliance matrix
    """
    E1, E2, E3 = sy.symbols('E1 E2 E3')
    G23, G12, G13 = sy.symbols('G23 G12 G13')
    NU12, NU21 = sy.symbols('NU12 NU21')
    NU13, NU31 = sy.symbols('NU13 NU31')
    NU23, NU32 = sy.symbols('NU23 NU32')
    S = sy.Matrix([[1/E1, -NU12/E1, -NU13/E1, 0, 0, 0],
                   [-NU21/E2, 1/E2, -NU23/E2, 0, 0, 0],
                   [-NU31/E3, -NU32/E3, 1/E3, 0, 0, 0],
                   [0, 0, 0, 1/G23, 0, 0],
                   [0, 0, 0, 0, 1/G13, 0],
                   [0, 0, 0, 0, 0, 1/G12]])
    if len(subs) > 0:
        S = S.subs([(sym, val) for sym, val in subs.items()])
    if imap is None:
        return S
    assert len(imap) == 6, "Indexmap must contain 6 indices."
    invmap = {upper(v) : k for k, v in imap.items()}
    trmap = {i : invmap[imap_ref_ortho_3D[i]] for i in range(6)}
    S_new = sy.zeros(6, 6)
    for inds in trmap.items():
        S_new[inds[1], :] = swap1d(S[inds[0], :], inds)
    if simplify:
        S_new.simplify()
    return S_new

def cmat_sym_ortho_3d(*args, imap : dict=None, simplify=True, **subs):
    """
    Reurns the stiffness matrix for a 3d orthotropic
    material in symbolic form.

    Parameters
    ----------
    imap : dict, optional
        Index map. Default is None.

    subs : dict, optional
        A dictionary of parameters to substitute.

    Notes
    -----
    The resulting symbolic matrix has 12 free symbols,
    but only 9 of these are independent.

    Returns
    -------
    sympy.Matrix
        6x6 symbolic stiffness matrix
    """
    res = smat_sym_ortho_3d(*args, imap=imap, simplify=False, **subs).inv()
    if simplify:
        res.simplify()
    return res

def swap1d(a : sy.Matrix, inds : tuple):
    a.col_swap(*inds)
    return a

if __name__ == '__main__':
    C = cmat_sym_ortho_3d()


# -*- coding: utf-8 -*-
"""
ese5
"""
import numpy as np
import math
import funzioni_Interpolazione_Polinomiale as fz
import matplotlib.pyplot as plt

#nodi del problema di interpolazione
x = np.arange(0,2*math.pi+0.1,math.pi/2);
y1 = np.sin(x)
y2 = np.cos(x)
xx = np.arange(0,2*math.pi+0.1,math.pi/40);

yy1 = fz.interpL(x, y1, xx)
yy2 = fz.interpL(x, y2, xx)

plt.plot(x, y1, "b*", xx, yy1, "r", xx, np.sin(xx), "g")
plt.legend(["Pti di interpolazione", "Polinomio Lagrange", "Funzione"])
plt.title("Sen(x)")
plt.show()

plt.plot(x, y2, "b*", xx, yy2, "r", xx, np.cos(xx), "g")
plt.legend(["Pti di interpolazione", "Polinomio Lagrange", "Funzione"])
plt.title("Cos(x)")
plt.show()


import numpy as np

def gaussian_kl(mean_1: np.ndarray, std_1: np.ndarray, mean_2: np.ndarray, std_2: np.ndarray) -> np.ndarray:
    eps = 1e-20
    std_1 = np.maximum(std_1, eps)
    std_2 = np.maximum(std_2, eps)
    return np.log(std_2 / std_1) + (std_1**2 + (mean_1 - mean_2)**2) / std_2**2 / 2. - 0.5


import numpy as np
import matplotlib.pyplot as plt

# 1.圆半径
r = 2.0
# 2.圆心坐标
a, b = (0., 0.)
# 参数方程
theta = np.arange(0, 2*np.pi, 0.01)
x = a + r * np.cos(theta)
y = b + r * np.sin(theta)
# 绘图
fig = plt.figure()
axes = fig.add_subplot(111)

axes.plot(x, y)
axes.axis('equal')

plt.axhline(linewidth=2, color='r')
plt.axvline(linewidth=2, color='r')
plt.show()


# Question 04, Lab 07
# AB Satyaprakash - 180123062

# imports
from math import sqrt, log, exp
from scipy.stats import norm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt

# functions

def calEurCallPutPrices(T, K, S, r, σ, t):
    if(T == t):
        putp = max(K-S, 0)
        callp = max(S-K, 0)
        return [callp, putp]

    d1 = (log(S/K)+(r+(σ**2/2))*(T-t))/(σ*sqrt(T-t))
    d2 = d1-σ*sqrt(T-t)

    putp = K*exp(-r*(T-t))*norm.cdf(-d2) - S*norm.cdf(-d1)
    callp = S*norm.cdf(d1)-K*norm.cdf(d2)*exp(-r*(T-t))
    return [callp, putp]

# program body
# We will consider Strike Price (K), sigma(σ), rate (r) and Final Time (T)
# 2D Plots
# Varying with Strike Price:

T, S, r, sig = 1, 1, 0.05, 0.6
t = np.array([0, 0.2, 0.4, 0.6, 0.8, 1])
K = np.arange(0.05, 1.55, 0.05)

callPrices = np.zeros(shape=(t.shape[0], K.shape[0]))
putPrices = np.zeros(shape=(t.shape[0], K.shape[0]))

for i in range(t.shape[0]):
    for j in range(K.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T, K[j], S, r, sig, t[i])

for i in range(t.shape[0]):
    plt.plot(K, callPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Strike Price')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Call Prices vs K')
plt.legend()
plt.show()

for i in range(t.shape[0]):
    plt.plot(K, putPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Strike Price')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Put Prices vs K')
plt.legend()
plt.show()

# Varying with Volatility (sigma):

T, S, r, K = 1, 1, 0.05, 1
t = np.array([0, 0.2, 0.4, 0.6, 0.8, 1])
sig = np.arange(0.05, 1.0, 0.05)

callPrices = np.zeros(shape=(t.shape[0], sig.shape[0]))
putPrices = np.zeros(shape=(t.shape[0], sig.shape[0]))

for i in range(t.shape[0]):
    for j in range(sig.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T, K, S, r, sig[j], t[i])

for i in range(t.shape[0]):
    plt.plot(sig, callPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Volatility')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Call Prices vs Sigma')
plt.legend()
plt.show()

for i in range(t.shape[0]):
    plt.plot(sig, putPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Volatility')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Put Prices vs Sigma')
plt.legend()
plt.show()

# Varying with Rate (r):

T, S, sig, K = 1, 1, 0.6, 1
t = np.array([0, 0.2, 0.4, 0.6, 0.8, 1])
r = np.arange(0.05, 1.0, 0.05)

callPrices = np.zeros(shape=(t.shape[0], r.shape[0]))
putPrices = np.zeros(shape=(t.shape[0], r.shape[0]))

for i in range(t.shape[0]):
    for j in range(r.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T, K, S, r[j], sig, t[i])

for i in range(t.shape[0]):
    plt.plot(r, callPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Rate')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Call Prices vs r')
plt.legend()
plt.show()

for i in range(t.shape[0]):
    plt.plot(r, putPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Rate')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Put Prices vs r')
plt.legend()
plt.show()

# Varying with Final Time (T):

r, S, sig, K = 0.05, 1, 0.6, 1
t = np.array([0, 0.2, 0.4, 0.6, 0.8, 1])
T = np.arange(1.05, 5, 0.05)

callPrices = np.zeros(shape=(t.shape[0], T.shape[0]))
putPrices = np.zeros(shape=(t.shape[0], T.shape[0]))

for i in range(t.shape[0]):
    for j in range(T.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T[j], K, S, r, sig, t[i])

for i in range(t.shape[0]):
    plt.plot(T, callPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Expiry Time')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Call Prices vs T')
plt.legend()
plt.show()

for i in range(t.shape[0]):
    plt.plot(T, putPrices[i], label='t = {}'.format(t[i]))
plt.xlabel('Expiry Time')
plt.ylabel('Option Prices')
plt.title('Sensitivity of Put Prices vs T')
plt.legend()
plt.show()

# 3D Plots -
# We will consider Strike Price (K), sigma(σ), rate (r) and Final Time (T) 2 at a time!
# Fix t =0.5

# K and sigma
T, S, r, t = 1, 1, 0.05, 0.5
K = np.arange(0.05, 1.5, 0.05)
sig = np.arange(0.05, 1.0, 0.05)

callPrices = np.zeros(shape=(K.shape[0], sig.shape[0]))
putPrices = np.zeros(shape=(K.shape[0], sig.shape[0]))

for i in range(K.shape[0]):
    for j in range(sig.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T, K[i], S, r, sig[j], t)

callPricesT = np.matrix.transpose(callPrices)
putPricesT = np.matrix.transpose(putPrices)

x, y = np.meshgrid(K, sig)

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, callPricesT, cmap='inferno')
ax.set_title('Surface Plot of Call Option Prices vs K and sigma')
ax.set_xlabel('Strike Price')
ax.set_ylabel('Sigma')
ax.set_zlabel('Price of European Call Option')
plt.show()

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, putPricesT, cmap='inferno')
ax.set_title('Surface Plot of Put Option Prices vs K and sigma')
ax.set_xlabel('Strike Price')
ax.set_ylabel('Sigma')
ax.set_zlabel('Price of European Put Option')
plt.show()

# K and rate
T, S, sig, t = 1, 1, 0.6, 0.5
K = np.arange(0.05, 1.5, 0.05)
r = np.arange(0.05, 1.0, 0.05)

callPrices = np.zeros(shape=(K.shape[0], r.shape[0]))
putPrices = np.zeros(shape=(K.shape[0], r.shape[0]))

for i in range(K.shape[0]):
    for j in range(r.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T, K[i], S, r[j], sig, t)

callPricesT = np.matrix.transpose(callPrices)
putPricesT = np.matrix.transpose(putPrices)

x, y = np.meshgrid(K, r)

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, callPricesT, cmap='inferno')
ax.set_title('Surface Plot of Call Option Prices vs K and r')
ax.set_xlabel('Strike Price')
ax.set_ylabel('Rate')
ax.set_zlabel('Price of European Call Option')
plt.show()

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, putPricesT, cmap='inferno')
ax.set_title('Surface Plot of Put Option Prices vs K and r')
ax.set_xlabel('Strike Price')
ax.set_ylabel('Rate')
ax.set_zlabel('Price of European Put Option')
plt.show()

# K and Expiry Time (T)
r, S, sig, t = 0.05, 1, 0.6, 0.5
K = np.arange(0.05, 1.5, 0.05)
T = np.arange(1.05, 5.0, 0.05)

callPrices = np.zeros(shape=(K.shape[0], T.shape[0]))
putPrices = np.zeros(shape=(K.shape[0], T.shape[0]))

for i in range(K.shape[0]):
    for j in range(T.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T[j], K[i], S, r, sig, t)

callPricesT = np.matrix.transpose(callPrices)
putPricesT = np.matrix.transpose(putPrices)

x, y = np.meshgrid(K, T)

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, callPricesT, cmap='inferno')
ax.set_title('Surface Plot of Call Option Prices vs K and T')
ax.set_xlabel('Strike Price')
ax.set_ylabel('Expiry Time')
ax.set_zlabel('Price of European Call Option')
plt.show()

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, putPricesT, cmap='inferno')
ax.set_title('Surface Plot of Put Option Prices vs K and T')
ax.set_xlabel('Strike Price')
ax.set_ylabel('Expiry Time')
ax.set_zlabel('Price of European Put Option')
plt.show()

# Sigma and T
r, S, K, t = 0.05, 1, 1, 0.5
sig = np.arange(0.05, 1, 0.05)
T = np.arange(1.05, 5.0, 0.05)

callPrices = np.zeros(shape=(sig.shape[0], T.shape[0]))
putPrices = np.zeros(shape=(sig.shape[0], T.shape[0]))

for i in range(sig.shape[0]):
    for j in range(T.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T[j], K, S, r, sig[i], t)

callPricesT = np.matrix.transpose(callPrices)
putPricesT = np.matrix.transpose(putPrices)

x, y = np.meshgrid(sig, T)

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, callPricesT, cmap='inferno')
ax.set_title('Surface Plot of Call Option Prices vs sig and T')
ax.set_xlabel('Sigma')
ax.set_ylabel('Expiry Time')
ax.set_zlabel('Price of European Call Option')
plt.show()

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, putPricesT, cmap='inferno')
ax.set_title('Surface Plot of Put Option Prices vs sig and T')
ax.set_xlabel('Sigma')
ax.set_ylabel('Expiry Time')
ax.set_zlabel('Price of European Put Option')
plt.show()

# Sigma and Rate
T, S, K, t = 1, 1, 1, 0.5
sig = np.arange(0.05, 1, 0.05)
r = np.arange(1.05, 1.5, 0.05)

callPrices = np.zeros(shape=(sig.shape[0], r.shape[0]))
putPrices = np.zeros(shape=(sig.shape[0], r.shape[0]))

for i in range(sig.shape[0]):
    for j in range(r.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T, K, S, r[j], sig[i], t)

callPricesT = np.matrix.transpose(callPrices)
putPricesT = np.matrix.transpose(putPrices)

x, y = np.meshgrid(sig, r)

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, callPricesT, cmap='inferno')
ax.set_title('Surface Plot of Call Option Prices vs sig and r')
ax.set_xlabel('Sigma')
ax.set_ylabel('Rate')
ax.set_zlabel('Price of European Call Option')
plt.show()

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, putPricesT, cmap='inferno')
ax.set_title('Surface Plot of Put Option Prices vs sig and r')
ax.set_xlabel('Sigma')
ax.set_ylabel('Rate')
ax.set_zlabel('Price of European Put Option')
plt.show()

# Time and Rate
sig, S, K, t = 0.6, 1, 1, 0.5
T = np.arange(1.05, 5, 0.05)
r = np.arange(1.05, 1.5, 0.05)

callPrices = np.zeros(shape=(T.shape[0], r.shape[0]))
putPrices = np.zeros(shape=(T.shape[0], r.shape[0]))

for i in range(T.shape[0]):
    for j in range(r.shape[0]):
        [callPrices[i][j], putPrices[i][j]] = calEurCallPutPrices(
            T[i], K, S, r[j], sig, t)

callPricesT = np.matrix.transpose(callPrices)
putPricesT = np.matrix.transpose(putPrices)

x, y = np.meshgrid(T, r)

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, callPricesT, cmap='inferno')
ax.set_title('Surface Plot of Call Option Prices vs sig and r')
ax.set_xlabel('Expiry Time')
ax.set_ylabel('Rate')
ax.set_zlabel('Price of European Call Option')
plt.show()

fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig)
ax.plot_surface(x, y, putPricesT, cmap='inferno')
ax.set_title('Surface Plot of Put Option Prices vs sig and r')
ax.set_xlabel('Expiry Time')
ax.set_ylabel('Rate')
ax.set_zlabel('Price of European Put Option')
plt.show()

Semester 6/MA 374 (Financial Engg. Lab)/Lab 7/180123062_AB_q4.py


import numpy as np
import numpy.linalg as la
import matplotlib.pyplot as plt
Mu = np.array([1.0, 3.0, 1.5, 6.0, 4.5])
Stdev = np.array([5.0, 10.0, 7.5, 15.0, 11.0])
CorrMatrix = np.array([[1.00, 0.25, 0.18, 0.10, 0.25],
                       [0.25, 1.00, 0.36, 0.20, 0.20],
                       [0.18, 0.36, 1.00, 0.25, 0.36],
                       [0.10, 0.20, 0.25, 1.00, 0.45],
                       [0.25, 0.20, 0.36, 0.45, 1.00]])
Sigma = np.diag(Stdev) @ CorrMatrix @ np.diag(Stdev)
iota = np.ones(Mu.shape)
inv_Sigma = la.inv(Sigma)
A = Mu @ inv_Sigma @ iota
B = Mu @ inv_Sigma @ Mu
C = iota @ inv_Sigma @ iota
D = B * C - A ** 2
V_Target = np.linspace(Mu.min(), Mu.max(), num=5)
V_Risk = np.zeros(V_Target.shape)
V_Weight = np.zeros((V_Target.shape[0], Mu.shape[0]))
for idx, Target_Return in enumerate(V_Target):
    V_Weight[idx, :] = (C * Target_Return - A) / D * la.solve(Sigma, Mu) \
                       + (B - A * Target_Return) / D * la.solve(Sigma, iota)
    V_Risk[idx] = (C / D) * (Target_Return - A / C) ** 2 + 1.0 / C
sigma_gmv = 1.0 / np.sqrt(C)
sigma_p = np.linspace(sigma_gmv, 1.05 * np.max(Stdev), num=250)
mu_p_efficient = (A + np.sqrt(np.abs(C * sigma_p ** 2 - 1.0) * D)) / C
mu_p_inefficient = (A - np.sqrt(np.abs(C * sigma_p ** 2 - 1.0) * D)) / C
fig1 = plt.figure(1, facecolor='w')
plt.plot(sigma_p, mu_p_efficient, 'b-')
plt.plot(sigma_p, mu_p_inefficient, 'b:')
plt.plot(np.sqrt(np.diagonal(Sigma)), Mu, 'rx')
plt.legend(['efficient frontier', 'inefficient frontier', 'asset'],
           loc='best', frameon=False)
plt.xlabel('standard deviation (%)')
plt.ylabel('expected return (%)')
plt.show()


import math
import numpy as np

class StatisticsCalculator:
    @staticmethod
    def mean(data):
        return np.mean(np.array(data))

    @staticmethod
    def variance(data):
        op_data = np.array(data)
        mean = StatisticsCalculator.mean(op_data)
        s = 0
        for i in op_data:
            s += (i - mean) ** 2
        return s / float(data.__len__() - 1)

    @staticmethod
    def standard_deviation(data):
        return math.sqrt(StatisticsCalculator.variance(np.array(data)))


"""
Date August 1, 2018
Author Aidan Salzmann

mincut takes in a graph object, and attempts to find a mincut
"""
from graph import DictGraph
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

"""
We repeat the following step: pick and edge uniformly at random
and merge the two vertices at its endpoints.

If, as a result, there are several edges between some pairs of
vertices, retain them all. Edges between contracted vertices are
removed such that there are no self loops.

This process is called the 'contraction' of edges.

A key insight is that 'contractions' don't decrese the mincut.

The algorithm continues until only two vertices remain, the edges
between them are taken as the min cut.

Pr min cut found on a run = 2 / n(n-1) = O(1 / n^2)

Pr min cut found after n^2 / 2 runs = 1/e ~ 36%

We can improve this by running the algorithm more times.
"""

def mincut(g):
	# check that g is a graph with > 2 vertices
	assert isinstance(g, nx.MultiGraph) and nx.is_connected(g)

	# check base cases of vertices
	if g.number_of_nodes() < 2:
		return 0
	elif g.number_of_nodes() == 2:
		return g.size()

	# while more than two nodes remain remove an edge
	while(g.number_of_nodes() > 2):
		# pick and edge at random
		idx = np.random.choice(len(g.edges()))
		random_edge = g.edges()[idx]

		# contract the edge in the graph
		#nx.draw(g)
		g = nx.contracted_edge(g, random_edge, self_loops=False)

	# return the number of edges remaining in g
	print(g)
	return g.size()

if __name__ == '__main__':
	# multigraph instantiation using networkx (graph lib)
	mc_graph = nx.MultiGraph()
	mc_graph.add_nodes_from([1,2,3,4,5])
	mc_graph.add_edges_from([(1,2),(1,3),(2,3), (1,5), (2,4), (4,5), (4,5)])
	n = mc_graph.size()
	pr = 0
	bestcut = -1
	for i in range(30):
		currcut = mincut(mc_graph)
		if bestcut == -1 or currcut < bestcut:
			bestcut = currcut
		pr = 1 - (( 1 - (2 / float(n * (n-1))) ) ** i)
		print('Current min-cut found : {}'.format(bestcut))
		print('Probability this min-cut is the overall min-cut : {:.10%}'.format(pr))

	# for a more lightweight option, use the DictGraph class included
	# sample = {'A' : ['B', 'C', 'E'],
	# 			'B' : ['C', 'D'],
	# 			'C' : [],
	# 			'D' : ['E'],
	# 			'E' : ['D']}
	# mc_graph = DictGraph(sample)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
FELPY

__author__ = "Trey Guest"
__credits__ = ["Trey Guest"]
__license__ = "EuXFEL"
__version__ = "1.0.1"
__maintainer__ = "Trey Guest"
__email__ = "twguest@students.latrobe.edu.au"
__status__ = "Developement"
"""

import numpy as np

def lu_decomp3(a):
    """
    c,d,e = lu_decomp3(a).
    LU decomposition of tridiagonal matrix a = [c\d\e]. On output
    {c},{d} and {e} are the diagonals of the decomposed matrix a.
    """
    n = np.diagonal(a).size
    assert(np.all(a.shape ==(n,n))) # check if square matrix

    d = np.copy(np.diagonal(a)) # without copy (assignment destination is read-only) error is raised
    e = np.copy(np.diagonal(a, 1))
    c = np.copy(np.diagonal(a, -1))

    for k in range(1,n):
        lam = c[k-1]/d[k-1]
        d[k] = d[k] - lam*e[k-1]
        c[k-1] = lam
    return c,d,e

def lu_solve3(c,d,e,b):
    """
    x = lu_solve(c,d,e,b).
    Solves [c\d\e]{x} = {b}, where {c}, {d} and {e} are the
    vectors returned from lu_decomp3.
    """
    n = len(d)
    y = np.zeros_like(b)

    y[0] = b[0]
    for k in range(1,n):
        y[k] = b[k] - c[k-1]*y[k-1]

    x = np.zeros_like(b)
    x[n-1] = y[n-1]/d[n-1] # there is no x[n] out of range
    for k in range(n-2,-1,-1):
        x[k] = (y[k] - e[k]*x[k+1])/d[k]
    return x

from scipy.sparse import diags
def create_tridiagonal(size = 4):
    diag = np.random.randn(size)*100
    diag_pos1 = np.random.randn(size-1)*10
    diag_neg1 = np.random.randn(size-1)*10

    a = diags([diag_neg1, diag, diag_pos1], offsets=[-1, 0, 1],shape=(size,size)).todense()
    return a

# =============================================================================
# a = create_tridiagonal(4)
# b = np.random.randn(4)*10
#
# print('matrix a is\n = {} \n\n and vector b is \n {}'.format(a, b))
#
# c, d, e = lu_decomp3(a)
# x = lu_solve3(c, d, e, b)
#
# print("x from our function is {}".format(x))
#
# print("check is answer correct ({})".format(np.allclose(np.dot(a, x), b)))
#
#
# =============================================================================
## Test Scipy
from scipy.linalg import solve_banded

def diagonal_form(a, upper = 1, lower= 1):
    """
    a is a numpy square matrix
    this function converts a square matrix to diagonal ordered form
    returned matrix in ab shape which can be used directly for scipy.linalg.solve_banded
    """
    n = a.shape[1]
    assert(np.all(a.shape ==(n,n)))

    ab = np.zeros((2*n-1, n))

    for i in range(n):
        ab[i,(n-1)-i:] = np.diagonal(a,(n-1)-i)

    for i in range(n-1):
        ab[(2*n-2)-i,:i+1] = np.diagonal(a,i-(n-1))

    mid_row_inx = int(ab.shape[0]/2)
    upper_rows = [mid_row_inx - i for i in range(1, upper+1)]
    upper_rows.reverse()
    upper_rows.append(mid_row_inx)
    lower_rows = [mid_row_inx + i for i in range(1, lower+1)]
    keep_rows = upper_rows+lower_rows
    ab = ab[keep_rows,:]

    return ab

def make_diags(diags):
    # Make a linear array for the whole matrix
    n = len(diags[0])
    a = np.zeros(n * n, dtype=diags[0].dtype)
    # Assign each diagonal to the right stride
    step = n + 1
    for i, diag in enumerate(diags):
        a[i:(n - i) * n:step] = diag
    # Reshape
    return a.reshape(n, n)

# =============================================================================
# ab = diagonal_form(a, upper=1, lower=1) # for tridiagonal matrix upper and lower = 1
#
# x_sp = solve_banded((1,1), ab, b)
# print("is our answer the same as scipy answer ({})".format(np.allclose(x, x_sp)))
# =============================================================================

if __name__ == '__main__':

    a = make_diags([np.arange(1,100)*np.pi, np.arange(1,99)*np.pi])
    print("shape a", a.shape)

    ab = diagonal_form(a)
    print("shape ab", ab.shape)

    b = np.random.rand(99,1)
    b[8] = 0
    print("shape b", b.shape)

    x = solve_banded((1,1), ab, b)
    print(x.shape)

    rb = np.matmul(a,x) ## resulting b

    from matplotlib import pyplot as plt
    plt.plot(b-rb)


"""Is matrix commuting."""
import numpy as np

def is_commuting(mat_1: np.ndarray, mat_2: np.ndarray) -> bool:
    r"""
    Determine if two linear operators commute with each other [WikCom]_.

    For any pair of operators :math:`X, Y \in \text{L}(\mathcal{X})`, the
    Lie bracket :math:`\left[X, Y\right] \in \text{L}(\mathcal{X})` is defined
    as

    .. math::
        \left[X, Y\right] = XY - YX.

    It holds that :math:`` if and only if :math:`X` and :math:`Y` commute
    [WatCom18]_.

    Examples
    ==========

    Consider the following matrices:

    .. math::
        A = \begin{pmatrix} 0 & 1 \\ 0 & 0 \end{pmatrix},
        \quad \text{and} \quad
        B = \begin{pmatrix} 1 & 0 \\ 0 & 0 \end{pmatrix}.

    It holds that :math:`AB=0`, however

    .. math::
        BA = \begin{pmatrix} 0 & 1 \\ 0 & 0 \end{pmatrix} = A,

    and hence, do not commute.

    >>> from toqito.matrix_props import is_commuting
    >>> import numpy as np
    >>> mat_1 = np.array([[0, 1], [0, 0]])
    >>> mat_2 = np.array([[1, 0], [0, 0]])
    >>> is_commuting(mat_1, mat_2)
    False

    Consider the following pair of matrices:

    .. math::
        A = \begin{pmatrix}
            1 & 0 & 0 \\
            0 & 1 & 0 \\
            1 & 0 & 2
            \end{pmatrix}, \quad \text{and} \quad
        B = \begin{pmatrix}
            2 & 4 & 0 \\
            3 & 1 & 0 \\
            -1 & -4 & 1
            \end{pmatrix}.

    It may be verified that :math:`AB = BA = 0`, and therefore :math`A` and
    :math:`B` commute.

    >>> from toqito.matrix_props import is_commuting
    >>> import numpy as np
    >>> mat_1 = np.array([[1, 0, 0], [0, 1, 0], [1, 0, 2]])
    >>> mat_2 = np.array([[2, 4, 0], [3, 1, 0], [-1, -4, 1]])
    >>> is_commuting(mat_1, mat_2)
    True

    References
    ==========
    .. [WikCom] Wikipedia: Commuting matrices
        https://en.wikipedia.org/wiki/Commuting_matrices

    .. [WatCom18] Watrous, John.
        "The theory of quantum information."
        Section: "Lie brackets and commutants".
        Cambridge University Press, 2018.

    :param mat_1: First matrix to check.
    :param mat_2: Second matrix to check.
    :return: Return `True` if :code:`mat_1` commutes with :code:`mat_2` and False otherwise.
    """
    return np.allclose(mat_1 @ mat_2 - mat_2 @ mat_1, 0)


#!/usr/bin/python3
# mari von steinkirch @2013
# steinkirch at gmail

import numpy

def searching_in_a_matrix(m1, value):
    """ searches an element in a matrix where in every row, the values are increasing from left to
    right, but the last number in a row is smaller than the first number in the next row.
    The naive brute force solution scan all numbers and cost O(nm).  However, since the numbers are
    already sorted, the matrix can be viewed as a 1D sorted array.  The binary search algorithm is
    suitable. The efficience is O(logmn)."""

    rows = len(m1)
    cols = len(m1[0])
    lo = 0
    hi = rows*cols
    while lo < hi:
        mid = (lo + hi)//2
        row = mid//cols
        col = mid%cols
        v = m1[row][col]
        if v == value: return True
        elif v > value: hi = mid
        else: lo = mid+1
    return False

def test_searching_in_a_matrix():
    a = [[1,3,5],[7,9,11],[13,15,17]]
    b = numpy.array([(1,2),(3,4)])
    assert(searching_in_a_matrix(a, 13) == True)
    assert(searching_in_a_matrix(a, 14) == False)
    assert(searching_in_a_matrix(b, 3) == True)
    assert(searching_in_a_matrix(b, 5) == False)
    print('Tests passed!')

if __name__ == '__main__':
    test_searching_in_a_matrix()


import numpy as np

class Pose:
    def __init__(self,x,y,phi=None,z=None):
        self.x = x
        self.y = y
        if phi==None:
            self.phi = 0
        else:
            self.phi = phi

    def dist(self,other):
        d1 = (self.x-other.x)**2+(self.y-other.y)**2
        return d1**(0.5)

    def __str__(self):
        return "X=" + '%.03f'%(self.x) + ", Y=" + '%.03f'%(self.y)

    def __eq__(self,other):
        return type(self) == type(other) and self.dist(other)<0.0000001

# Some functions to make geometries
def posesCircle(nr,radius):
    #Return list of poses evenly spaced with radius commRadius
    R = radius
    return [Pose(R*np.cos(2*i*np.pi/nr),R*np.sin(2*i*np.pi/nr)) for i in range(nr)]

def posesRing(nr,commRadius):
    #Return list of poses evenly spaced with radius commRadius
    R = (0.95*commRadius)/np.sqrt(2-2*np.cos(2*np.pi/nr))
    return posesCircle(nr,R)
    # return [Pose(R*np.cos(2*i*np.pi/nr),R*np.sin(2*i*np.pi/nr)) for i in range(nr)]

def posesLine(nr,commRadius):
    R = 0.95*commRadius
    return [Pose(R*(i-(nr-1)/2.0),0) for i in range(nr)]

def posesRingFC(nr,commRadius):
    R = 0.45*commRadius
    return posesCircle(nr,R)

def posesStar(nr,commRadius):
    centerPose = Pose(0,0)
    perimPoses = posesCircle(nr-1,0.95*commRadius)
    return [centerPose] + perimPoses


#!/usr/bin/env python3
"""
Neuron Class
"""
import numpy as np

class Neuron:
    """
    Methods and attributes of Neuron class
    """

    def __init__(self, nx):
        """
        Constructor method
        ------------------

        nx: is the number of input features to the neuron
        W: The weights vector for the neuron. Upon instantiation, it should be
           initialized using a random normal distribution.
        b: The bias for the neuron. Upon instantiation, it should be
           initialized to 0.
        A: The activated output of the neuron (prediction). Upon
           instantiation, it should be initialized to 0.
        """
        if type(nx) is not int:
            raise TypeError('nx must be an integer')
        if nx < 1:
            raise ValueError('nx must be a positive integer')
        self.__W = np.random.randn(nx).reshape(1, nx)
        self.__b = 0
        self.__A = 0

    @property
    def W(self):
        """
        Getter method for weights
        """
        return self.__W

    @property
    def b(self):
        """
        Getter method for bias
        """
        return self.__b

    @property
    def A(self):
        """
        Getter method for activated output
        """
        return self.__A

    def forward_prop(self, X):
        """
        Calculates the forward propagation of the neuron using a sigmoid
        activation function

        X is a numpy.ndarray with shape (nx, m) that contains the input data
        nx is the number of input features to the neuron
        m is the number of examples
        fn is the neuron function applying weigths to input data + bias, it
        calculates the input for sigmoid activation function
        """
        fn = np.matmul(self.__W, X) + self.__b
        self.__A = 1. / (1 + np.exp(-fn))
        return self.__A


import numpy as np

def autocorr(sequence):
    """
    Calculate auto-correlation for a given sequence by way of convolution (FFT). High auto-correlation after N
     time shifts implies periodicity in the sequence where N is the period.
    Parameters:
        sequence (numpy array): the sequence to auto-correlate

    returns:
        r (float): a value that express the degree of auto-correlation
        lag (int): the period after which the signal resembles itself the most
    """

    n = sequence.size
    sequence = (sequence - np.mean(sequence)) # normalize the sequence
    result = np.correlate(sequence, sequence, mode='same')
    acorr = result[n//2 + 1:] / (sequence.var() * np.arange(n-1, n//2, -1))
    lag = acorr.argmax() + 1
    r = acorr[lag-1]
    '''
    if np.abs(r) > 0.5:
      print('Appears to be autocorrelated with r = {}, lag = {}'. format(r, lag))
    else:
      print('Appears to be not autocorrelated')
    '''
    return r, lag

def discrete_differential(sequence):
    differential_sequence = np.empty(len(sequence))
    for i in range(len(sequence)-1):
        differential_sequence[i] = sequence[i+1] - sequence[i]

    differential_sequence[-1] = 0

    return differential_sequence

def find_extrema(differential_sequence):
    extrema_indeces = []
    for i in range(len(differential_sequence)-1):
        if differential_sequence[i] * differential_sequence[i+1] < 0 or differential_sequence[i] == 0:
            extrema_indeces.append(i)

    return extrema_indeces

def is_oscillating(sequence):

    extrema = find_extrema(discrete_differential(sequence))
    corr_coeffficient, period = autocorr(sequence)
    oscillator = True

    if len(extrema) <= 1:
        oscillator = False
    else:
        for i in range(len(extrema)-2):
            if extrema[i+1] == extrema[i]+1: # there should not be extrema at neighbouring indeces
                oscillator = False
                break

    if corr_coeffficient < 0.5:
        periodic = False
    else:
        periodic = True

    return corr_coeffficient, period, oscillator and periodic

def freq_analysis(time_series, sampling_freq):
    assert isinstance(time_series, np.ndarray)
    N = time_series.size
    fourier_coeffs = np.fft.fft(time_series)
    freqs = np.fft.fftfreq(N)*sampling_freq

    return fourier_coeffs, freqs


# -*- coding: utf-8 -*-
# Filename: geoparams.py

"""
Geo parameters calculation. Include local Earth radius, Earth rotatoin rate,
local gravity.
Created on 2017-09-12
@author: dongxiaoguang
"""

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

# global
VERSION = '1.0'
GM = 3.986004418e14                 # m3/(s2)
Re = 6378137                        # Radius of earth at the equator in m
FLATTENING = 1/298.257223563        # Earth flattening, f = (a-b)/a
ECCENTRICITY = 0.0818191908426215   # Earth eccentricity, e2 = 2*f-f^2
E_SQR = ECCENTRICITY**2             # squared eccentricity
W_IE = 7292115e-11                  # Earth's rotation rate

def geo_param(pos):
    """
    Calculate local radius and gravity given the [Lat, Lon, Alt]
    Local radius include meridian radius rm and normal radius rn.
    Args:
        pos: [Lat, Lon, Alt], rad, m
    Returns:
        rm: meridian radius, m
        rn: normal radius, m
        g: gravity, m/s/s
        sl: sin(Lat)
        cl: cos(lat)
        w_ie: Earth's rotation rate w.r.t the inertial frame, rad/s
    """
    # some constants
    normal_gravity = 9.7803253359
    k = 0.00193185265241        # WGS-84 gravity model constant. For more details, refer to
                                # https://en.wikipedia.org/wiki/Gravity_of_Earth
    m = 0.00344978650684        # m = w*w*a*a*b/GM
    # calc
    sl = math.sin(pos[0])
    cl = math.cos(pos[0])
    sl_sqr = sl * sl
    h = pos[2]

    # https://www2.unb.ca/gge/Pubs/LN39.pdf page 10 eq 13
    rm = (Re*(1 - E_SQR)) / (math.sqrt(1.0 - E_SQR*sl_sqr) * (1.0 - E_SQR*sl_sqr) * (1.0 - E_SQR*sl_sqr))
    rn = Re / (math.sqrt(1.0 - E_SQR*sl_sqr))
    # https://en.wikipedia.org/wiki/Theoretical_gravity International Gravity Formula 1980 (IGF80)
    g1 = normal_gravity * (1 + k*sl_sqr) / math.sqrt(1.0 - E_SQR*sl_sqr)

    # https://www.geophysik.uni-muenchen.de/~jowa/praktikum09/geoid_tutorial.pdf
    g = g1 * \
        (1.0 - \
            (2.0/Re) * (1.0 + FLATTENING + m - 2.0*FLATTENING*sl_sqr)*h + \
            3.0*h*h/Re/Re)
    return rm, rn, g, sl, cl, W_IE

def earth_radius(lat):
    """
    Calculate Earth meridian radius and normal radius.
    Args:
        lat: Latitude, rad
    Returns:
        rm: meridian radius, m
        rn: normal radius, m
    """
    sl = math.sin(lat)
    sl_sqr = sl * sl
    rm = (Re*(1 - E_SQR)) / (math.sqrt(1.0 - E_SQR*sl_sqr) * (1.0 - E_SQR*sl_sqr))
    rn = Re / (math.sqrt(1.0 - E_SQR*sl_sqr))
    return rm, rn

def lla2ecef(lla):
    '''
    [Lat Lon Alt] position to xyz position
    Args:
        lla: [Lat, Lon, Alt], [rad, rad, meter], numpy array of size (3,)
    return:
        WGS-84 position, [x, y, z], [m, m, m], numpy array of size (3,)
    '''
    sl = math.sin(lla[0])
    cl = math.cos(lla[0])
    sl_sqr = sl * sl

    r = Re / math.sqrt(1.0 - E_SQR*sl_sqr)
    rho = (r + lla[2]) * cl
    x = rho * math.cos(lla[1])
    y = rho * math.sin(lla[1])
    z = (r*(1.0-E_SQR) + lla[2]) * sl
    return np.array([x, y, z])

def lla2ecef_batch(lla):
    '''
    [Lat Lon Alt] position to xyz position
    Args:
        lla: [Lat, Lon, Alt], [rad, rad, meter], numpy array of size (n,3)
    return:
        WGS-84 position, [x, y, z], [m, m, m], numpy array of size (n,3)
    '''
    # only one LLA
    if lla.ndim == 1:
        return lla2ecef(lla)
    # multiple LLA
    n = lla.shape[0]
    xyz = np.zeros((n, 3))
    for i in range(0, n):
        sl = math.sin(lla[i, 0])
        cl = math.cos(lla[i, 0])
        sl_sqr = sl * sl
        r = Re / math.sqrt(1.0 - E_SQR*sl_sqr)
        rho = (r + lla[i, 2]) * cl

        xyz[i, 0] = rho * math.cos(lla[i, 1])
        xyz[i, 1] = rho * math.sin(lla[i, 1])
        xyz[i, 2] = (r*(1.0-E_SQR) + lla[i, 2]) * sl
    return xyz

def ecef2lla(xyz):
    '''
    [x y z] position in ECEF to [Lat Lon Alt]
    Args:
        WGS-84 position, [x, y, z], [m, m, m], numpy array of size (3,)
    return:
        lla: [Lat, Lon, Alt], [rad, rad, meter], numpy array of size (3,)
    '''
    # longitude
    lon = math.atan2(xyz[1], xyz[0])
    # distance from the polar axis
    rho = math.sqrt(xyz[0]*xyz[0] + xyz[1]*xyz[1])
    # Spheroid properties
    b = (1.0 - FLATTENING) * Re             # Semiminor axis
    e2 = FLATTENING * (2.0 - FLATTENING)    # Square of (first) eccentricity
    ep2 = e2 / (1.0 - e2)                   # Square of second eccentricity
    # Bowring's formula for initial parametric (beta) and geodetic latitudes
    beta = math.atan2(xyz[2], (1.0 - FLATTENING) * rho)
    lat = math.atan2(xyz[2] + b*ep2*math.sin(beta)**3.0,\
                     rho - Re*e2*math.cos(beta)**3.0)
    # Fixed-point iteration with Bowring's formula
    # (typically converges within two or three iterations)
    beta_new = math.atan2((1.0 - FLATTENING)*math.sin(lat), math.cos(lat))
    count = 0
    while count < 5 and beta != beta_new:
        beta = beta_new
        lat = math.atan2(xyz[2] + b*ep2*math.sin(beta)**3.0,\
                         rho - Re*e2*math.cos(beta)**3.0)
        beta_new = math.atan2((1.0 - FLATTENING)*math.sin(lat), math.cos(lat))
        count += 1
    # Ellipsoidal height from final value for latitude
    slat = math.sin(lat)
    N = Re/math.sqrt(1.0-e2*slat*slat)
    alt = rho*math.cos(lat) + (xyz[2] + e2*N*slat)*slat - N
    return np.array([lat, lon, alt])


"""Defines symbolic linear algebra functions.

Function list:

- norm(x): returns the L-2 norm of a vector
- norm2(x): returns the squared L-2 norm of a vector
- norm_L1(x): returns the L-1 norm of a vector
- norm_Linf(x): returns the L-infinity norm of a vector
- norm_fro(A): returns the Frobeneus norm of a matrix
- distance(x,y): returns the L-2 distance between two vectors
- distance2(x,y): returns the squared L-2 distance between two vectors
- distance_L1(x,y): returns the L-1 distance between two vectors
- distance_Linf(x,y): returns the L-infinity distance between two vectors
- mahalanobis_distance(x,y,A): returns the mahalanobis distance between two vectors weighted by A, i.e. :math:`\sqrt((x-y)^T A (x-y))`
- mahalanobis_distance2(x,y,A): returns the squared mahalanobis distance between two vectors weighted by A
- unit(x): returns the unit vector in the direction x
- inv(A): returns the inverse of a matrix
- pinv(A): returns the pseudoinverse of a matrix
- linear(x,A): the linear function :math:`A x`
- quadratic(x,A): the quadratic function :math:`x^T A x`
- bilinear(x,y,A): the bilinear function :math:`x^T A y`
- bound_contains(xmin,xmax,x): returns True if xmin <= x <= xmax element-wise
- bound_margin(xmin,xmax,x): returns the distance from x to boundaries of the bounding box [xmin,xmax], if inside (positive is inside)
- bound_overlaps(xmin,xmax,ymin,ymax): returns True if xmin <= x <= xmax element-wise

Also defines a context LinAlgContext that can be included into a Context under "linalg"

Completeness table

=================  =============  ==============
Function           Derivative     Simplification
=================  =============  ==============
  norm             Y
  norm2            Y
  norm_L1          Y
  norm_Linf        Y
  norm_fro         Y
  distance         Y
  distance2        Y
  distance_L1      Y
  distance_Linf    Y
  mahalanobis_dis  Y,Y,N
  mahalanobis...2  Y,Y,N
  unit             Y
  inv              Y
  pinv             Y
  linear           Y
  quadratic        Y,Y,N
  bilinear         Y,Y,N
  bound_contains   N/A
  bound_margin
  bound_overlaps   N/A
=================  =============  ==============

Module contents
~~~~~~~~~~~~~~~

.. autosummary::
    norm
    norm2
    norm_L1
    norm_Linf
    norm_fro
    distance
    distance2
    distance_L1
    distance_Linf
    mahalanobis_distance
    mahalanobis_distance2
    unit
    inv
    pinv
    linear
    quadratic
    bilinear
    bound_contains
    bound_margin
    bound_overlaps
    LinAlgContext

"""

from .symbolic import *
from . import vectorops
import numpy as np
import weakref

_x = Variable("x","V")
_y = Variable("y","V")
_A = Variable("A","M")
norm = Function('norm',np.linalg.norm,['x'],returnType='N')
norm.description = "Returns the L-2 norm of a vector"
norm2 = Function('norm2',dot(_x,_x),['x'],returnType='N')
norm_L1 = Function('norm_L1',lambda x:np.linalg.norm(x,ord=1),returnType='N')
_inf = float('inf')
norm_Linf = Function('norm_Linf',lambda x:np.linalg.norm(x,ord=_inf),returnType='N')
norm_fro = Function('norm_fro',np.linalg.norm,['A'],returnType='N')
norm2_fro = Function('norm2_fro',lambda x:np.linalg.norm(x)**2,['A'],returnType='N')
distance = Function('distance',norm(_x-_y),['x','y'],returnType='N')
distance2 = Function('distance2',norm2(_x-_y),['x','y'],returnType='N')
distance_L1 = Function('distance_L1',norm_L1(_x-_y),['x','y'],returnType='N')
distance_Linf = Function('distance_Linf',norm_Linf(_x-_y),['x','y'],returnType='N')
unit = Function('unit',vectorops.unit,['x'])
norm.argTypes = [Vector]
norm2.argTypes = [Vector]
norm_L1.argTypes = [Vector]
norm_Linf.argTypes = [Vector]
norm_fro.argTypes = [Matrix]
norm.properties['nonnegative'] = True
norm2.properties['nonnegative'] = True
norm_L1.properties['nonnegative'] = True
norm_Linf.properties['nonnegative'] = True
norm_fro.properties['nonnegative'] = True
norm2_fro.properties['nonnegative'] = True
norm.addSimplifier(['zero'],lambda x:0)
norm.addSimplifier(['unit'],lambda x:1)
norm2.addSimplifier(['zero'],lambda x:0)
norm2.addSimplifier(['unit'],lambda x:1)
norm_L1.addSimplifier(['zero'],lambda x:0)
norm_L1.addSimplifier(['basis'],lambda x:1)
norm_Linf.addSimplifier(['zero'],lambda x:0)
norm_Linf.addSimplifier(['basis'],lambda x:1)
norm.setDeriv(0,(lambda x,dx:dot(x,dx)/norm(x)),asExpr=True,stackable=True)
norm2.setDeriv(0,(lambda x,dx:dot(x,dx)*2),asExpr=True,stackable=True)
norm_L1.setDeriv(0,lambda x,dx:dot(sign(x),dx),asExpr=True,stackable=True)
def _norm_Linf_deriv(x,dx):
    imax = argmax(abs_(x))
    return sign(x[imax])*dx[imax]
norm_Linf.setDeriv(0,_norm_Linf_deriv,asExpr=True,stackable=True)
norm_fro.setDeriv(0,(lambda A,dA:mul(A,dA)/norm_fro(A)),asExpr=True,stackable=True)
norm2_fro.setDeriv(0,(lambda A,dA:mul(A,dA)*2),asExpr=True,stackable=True)
distance.argTypes = [Vector,Vector]
distance2.argTypes = [Vector,Vector]
distance_L1.argTypes = [Vector,Vector]
distance_Linf.argTypes = [Vector,Vector]
distance.properties['nonnegative'] = True
distance2.properties['nonnegative'] = True
distance_L1.properties['nonnegative'] = True
distance_Linf.properties['nonnegative'] = True
distance.addSimplifier(['zero',None],lambda x,y:norm(y))
distance.addSimplifier([None,'zero'],lambda x,y:norm(x))
distance2.addSimplifier(['zero',None],lambda x,y:norm2(y))
distance2.addSimplifier([None,'zero'],lambda x,y:norm2(x))
distance_L1.addSimplifier(['zero',None],lambda x,y:norm_L1(y))
distance_L1.addSimplifier([None,'zero'],lambda x,y:norm_L1(x))
distance_Linf.addSimplifier(['zero',None],lambda x,y:norm_Linf(y))
distance_Linf.addSimplifier([None,'zero'],lambda x,y:norm_Linf(x))
distance.setDeriv(0,(lambda x,y,dx:dot(x-y,dx)/distance(x,y)),asExpr=True,stackable=True)
distance.setDeriv(1,(lambda x,y,dy:dot(x-y,dy)/distance(x,y)),asExpr=True,stackable=True)
distance2.setDeriv(0,(lambda x,y,dx:dot(x-y,dx)*2),asExpr=True,stackable=True)
distance2.setDeriv(1,(lambda x,y,dy:dot(y-x,dy)*2),asExpr=True,stackable=True)
distance_L1.autoSetJacobians()
distance_Linf.autoSetJacobians()
linear = Function('linear',dot(_A,_x),["x","A"])
linear.argTypes = [Vector,Matrix]
linear.autoSetJacobians()
bilinear = Function('bilinear',dot(_x,dot(_A,_y)),["x","A","y"])
bilinear.argTypes = [Vector,Matrix,Vector]
bilinear.setJacobian(0,lambda x,A,y:dot(A,y),asExpr=True)
bilinear.setJacobian(1,lambda x,A,y:outer(x,y),asExpr=True)
bilinear.setJacobian(2,lambda x,A,y:dot(A,x),asExpr=True)
quadratic = Function('quadratic',dot(_x,dot(_A,_x)),["x","A"],returnType='N')
quadratic.argTypes = [Vector,Matrix]
quadratic.setJacobian('x',lambda x,A:2*dot(x,A),asExpr=True)
quadratic.setJacobian('A',lambda x,A:outer(x,x),asExpr=True)
mahalanobis_distance2 = Function('mahalanobis_distance2',quadratic(_x-_y,_A),['x','y','A'])
mahalanobis_distance2.autoSetJacobians()
mahalanobis_distance = Function('mahalanobis_distance',sqrt(mahalanobis_distance2(_x,_y,_A)),['x','y','A'])
mahalanobis_distance.autoSetJacobians()
unit.setDeriv(0,lambda x,dx:if_(x==0,zero(shape(x)),dx/norm(x)-x*(dot(x,dx)/norm(x))**3),stackable=True)
inv = Function('inv',np.linalg.inv,['A'],returnType='M')
inv.argTypes = [Matrix]
def _inv_deriv(A,dA):
    Ainv = inv(A)
    return -dot(Ainv,dot(dA,Ainv))
inv.properties['inverse'] = weakref.proxy(inv)
#patch the dot function
dot.addSimplifier(['inv','inv'],lambda Ainv,Binv:inv(dot(Binv.args[0],Ainv.args[0])))
dot.addSimplifier(['linalg.inv','linalg.inv'],lambda Ainv,Binv:inv(dot(Binv.args[0],Ainv.args[0])))
inv.addSimplifier(['neg'],lambda x:-inv(x.args[0]))
inv.setDeriv(0,_inv_deriv,asExpr=True)
inv.printers['str'] = lambda expr,astr:astr[0]+'^-1'
pinv = Function('pinv',np.linalg.pinv,['A'],returnType='M')
pinv.argTypes = [Matrix]
pinv.addSimplifier(['neg'],lambda x:-pinv(x.args[0]))
pinv.printers['str'] = lambda expr,astr:astr[0]+'^+'
def _pinv_deriv(A,dA):
    Ainv = pinv(A)
    return -dot(Ainv,dot(dA,Ainv))
pinv.setDeriv(0,_pinv_deriv,asExpr=True)
def _bound_contains(xmin,xmax,x):
    return all(a <= v and v <= b for v,a,b in zip(x,xmin,xmax))
def _bound_overlaps(xmin,xmax,ymin,ymax):
    for a,b,c,d in zip(xmin,xmax,ymin,ymax):
        if d < a or c > b: return False
        if b < c or a > d: return False
    return True
def _bound_margin(xmin,xmax,x):
    return min(min(v-a,b-v) for v,a,b in zip(x,xmin,xmax))
bound_contains = Function('bound_contains',_bound_contains,returnType = 'B')
bound_overlaps = Function('bound_overlaps',_bound_overlaps,returnType = 'B')
bound_margin = Function('bound_margin',_bound_margin,returnType = 'N')
bound_contains.argTypes = [Vector]*3
bound_overlaps.argTypes = [Vector]*4
bound_margin.argTypes = [Vector]*3
#bound_margin = Function('bound_margin',min_(min_(Variable('x','V',None)-Variable('xmin','V',None)),min_(Variable('xmax','V',None)-Variable('x','V',None))),['xmin','xmax','x'],returnType = 'N')

class LinAlgContext(Context):
    def __init__(self):
        Context.__init__(self)
        self.norm = self.declare(norm)
        self.norm2 = self.declare(norm2)
        self.norm_L1 = self.declare(norm_L1)
        self.norm_Linf = self.declare(norm_Linf)
        self.distance = self.declare(distance)
        self.distance2 = self.declare(distance2)
        self.distance_L1 = self.declare(distance_L1)
        self.distance_Linf = self.declare(distance_Linf)
        self.unit = self.declare(unit)
        self.linear = self.declare(linear)
        self.bilinear = self.declare(bilinear)
        self.quadratic = self.declare(quadratic)
        self.mahalanobis_distance = self.declare(mahalanobis_distance)
        self.mahalanobis_distance2 = self.declare(mahalanobis_distance2)
        self.inv = self.declare(inv)
        self.pinv = self.declare(pinv)
        self.bound_contains = self.declare(bound_contains)
        self.bound_overlaps = self.declare(bound_overlaps)
        self.bound_margin = self.declare(bound_margin)


# Activity 2: Evaluating Mean Inertia by Cluster After PCA Transformation

# Continuing from Exercise 7:

from sklearn.decomposition import PCA
model = PCA(n_components=best_n_components) # remember, best_n_components = 6

# fit model and transform scaled_features into best_n_components
df_pca = model.fit_transform(scaled_features)

# fit 100 models for each n_clusters 1-10
from sklearn.cluster import KMeans
import numpy as np
mean_inertia_list_PCA = [] # create a list for the average inertia at each n_clusters
for x in range(1, 11): # loop through n_clusters 1-10
    inertia_list = [] # create a list for each individual inertia value at n_cluster
    for i in range(100):
        model = KMeans(n_clusters=x) # instantiate model
        model.fit(df_pca) # fit model
        inertia = model.inertia_ # get inertia
        inertia_list.append(inertia) # append inertia to inertia_list
    # moving to the outside loop
    mean_inertia = np.mean(inertia_list) # get mean of inertia list
    mean_inertia_list_PCA.append(mean_inertia) # append mean_inertia to mean_inertia_list

# print mean_inertia_list_PCA
print(mean_inertia_list_PCA)


"""
Created on Tue Nov 26 14:56:47 2019

@author: Jing Yang (jyang7@fsu.edu) and Ming Ye (mye@fsu.edu).
The MIT License (MIT)

Copyright (c) 2019 Jing Yang and Ming Ye

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""

import numpy as np

# Non-monotonic Sobol G Function (9 parameters)
def evaluate(values, a=None):
    '''
    The Sobol G function is described in the reference below:
    Francesca Campolongo, Jessica Cariboni, Andrea Saltelli,
    An effective screening design for sensitivity analysis of large models,
    Environmental Modelling & Software,
    Volume 22, Issue 10,
    2007,
    Pages 1509-1518,
    ISSN 1364-8152,
    https://doi.org/10.1016/j.envsoft.2006.10.004.

    Parameters:
    values: a matrix to store parameter values of the N random trajectories
    a: this vector is specifically for the Sobol G function.

    Returns
    -------
    Y: The model run outputs. A column vector with the dimension of (k+1)xN
    ----------
    '''
    if type(values) != np.ndarray:
        raise TypeError("The argument `values` must be a numpy ndarray")
    if a is None:
        a = [0.02, 0.03, 0.05, 11, 12.5, 13, 34, 35, 37]

    ltz = values < 0
    gto = values > 1

    if ltz.any() == True:
        raise ValueError("Sobol G function called with values less than zero")
    elif gto.any() == True:
        raise ValueError("Sobol G function called with values greater than one")

    Y = np.ones([values.shape[0]])

    len_a = len(a)
    for i, row in enumerate(values):
        for j in range(len_a):
            x = row[j]
            a_j = a[j]
            Y[i] *= (np.abs(4 * x - 2) + a_j) / (1 + a_j)

    return Y


import sys
import numpy as np

if "../" not in sys.path:
    sys.path.append('../')
np.random.seed(10)
from env.gridWorld import gridWorld

def value_iteration(env, theta=0.0001, discount_factor=1.0):
    """
    Value Iteration Algorithm.

    Args:
        env: OpenAI env. env.P represents the transition probabilities of the environment.
             env.P[s][a] is a list of transition tuples (prob, next_state, reward, done).
             env.nS is the number of states in the environment.
             env.nA is the number of actions in the environment.
        theta: We stop evaluation once our value function change is less than theta for all states.
        discount_factor: Gamma discount factor

    Returns:
        A tuple (policy, V) of the optimal policy and the optimal value function.
    """
    # 1. Init
    V = np.zeros(env.nS)
    while True:
        # Stopping condition
        delta = 0
        # Update each state...
        for s in range(env.nS):
            # Do a one-step lookahead to find the best action
            A = np.zeros(env.nA)
            for a in range(env.nA):
                for prob, next_state, reward, done in env.P[s][a]:
                    A[a] += prob * (reward + discount_factor * V[next_state])
            best_action_value = np.max(A)
            # Calculate delta across all states seen so far
            delta = max(delta, np.abs(best_action_value - V[s]))
            # Update the value function. Ref: Sutton book eq. 4.10.
            V[s] = best_action_value
        # Check if we can stop
        if delta < theta:
            break

    # Create a deterministic policy using the optimal value function
    policy = np.zeros([env.nS, env.nA])
    for s in range(env.nS):
        # One step lookahead to find the best action for this state
        A = np.zeros(env.nA)
        for a in range(env.nA):
            for prob, next_state, reward, done in env.P[s][a]:
                A[a] += prob * (reward + discount_factor * V[next_state])
        best_action = np.argmax(A)
        # Always take the best action
        policy[s, best_action] = 1.0

    return policy, V

if __name__ == "__main__":

    env = gridWorld()
    policy, v = value_iteration(env)

    print(f"Policy Probability Distribution:\n{policy}\n")

    print(f"Reshaped Grid Policy (0=up, 1=right, 2=down, 3=left):\n{np.reshape(np.argmax(policy, axis=1), env.shape)}\n")

    print(f"Value Function:\n{v}\n")

    print(f"Reshaped Grid Value Function:\n{v.reshape(env.shape)}\n")


import numpy as np

def covariance_self():

    np.random.seed(123)

    n_samples = 1000
    d_dimensions = 100

    A = np.random.rand(d_dimensions, d_dimensions)

    X = np.random.randn(n_samples, d_dimensions)
    Y = X @ A

    Sigma_x = np.cov(X, rowvar=False)
    print(Sigma_x.shape)

    print(f"RV Cov: {np.einsum('ij,ji->', Sigma_x, Sigma_x):.3f}")
    print(f"RV Cov: {np.trace(Sigma_x @ Sigma_x.T):.3f}")
    print(f"RV Cov: {np.sum(Sigma_x * Sigma_x):.3f}")
    print(f"RV Cov: {np.linalg.norm(Sigma_x) ** 2:.3f}")
    print(f"RV Cov: {(np.linalg.eigvalsh(Sigma_x) ** 2).sum():.3f}")

    Sigma_x = np.cov(X, rowvar=True)
    print(Sigma_x.shape)

    print(f"RV Cov: {np.einsum('ij,ji->', Sigma_x, Sigma_x):.3f}")
    print(f"RV Cov: {np.trace(Sigma_x @ Sigma_x.T):.3f}")
    print(f"RV Cov: {np.sum(Sigma_x * Sigma_x):.3f}")
    print(f"RV Cov: {np.linalg.norm(Sigma_x) ** 2:.3f}")
    print(f"RV Cov: {(np.linalg.eigvalsh(Sigma_x) ** 2).sum():.3f}")

def main():

    np.random.seed(123)

    n_samples = 1000
    d_dimensions = 100

    A = np.random.rand(d_dimensions, d_dimensions)

    X = np.random.randn(n_samples, d_dimensions)
    Y = X @ A

    Sigma_xy = np.cov(X, Y, rowvar=False)
    print(Sigma_xy.shape)

    print(f"RV Cov: {np.trace(Sigma_xy @ Sigma_xy.T):.3f}")
    print(f"RV Cov: {np.sum(Sigma_xy * Sigma_xy):.3f}")
    print(f"RV Cov: {np.linalg.norm(Sigma_xy) ** 2:.3f}")
    print(f"RV Cov: {(np.linalg.eigvalsh(Sigma_xy) ** 2).sum():.3f}")

def covariance_example():

    np.random.seed(123)

    n_samples = 1_000
    d_dimensions = 100

    A = np.random.rand(d_dimensions, d_dimensions)

    X = np.random.randn(n_samples, d_dimensions)
    Y = X @ A

    # Sample Covariance
    sigma_xy = np.cov(X, Y, rowvar=True)
    print(sigma_xy.shape)
    print(f"Cov (HS): {np.trace(sigma_xy @ sigma_xy.T):.3f}")

    # Feature Covariance
    sigma_xy = np.cov(X, Y, rowvar=False)
    print(sigma_xy.shape)
    print(f"Cov (HS): {np.trace(sigma_xy @ sigma_xy.T):.3f}")

    return None

def rv_coeff_example():

    np.random.seed(123)

    n_samples = 1_000
    d_dimensions = 100

    A = np.random.rand(d_dimensions, d_dimensions)

    X = np.random.randn(n_samples, d_dimensions)
    Y = X @ A

    # ===============
    # Primal Space
    # ===============

    # HS Norm - cross term
    sigma_xy = np.cov(X, Y, rowvar=True)

    sigma_xy_norm = np.trace(sigma_xy @ sigma_xy.T)

    # HS Norm - individual terms
    sigma_x = np.cov(X, X, rowvar=True)
    sigma_x_norm = np.linalg.norm(sigma_x)

    sigma_y = np.cov(Y, Y, rowvar=True)
    sigma_y_norm = np.linalg.norm(sigma_y)
    print(sigma_xy.shape, sigma_x.shape, sigma_y.shape)

    rv_coeff = sigma_xy_norm / (sigma_x_norm * sigma_y_norm)

    print(f"RV coeff (Samples): {rv_coeff:.3f}")

    # Dual Space
    sigma_xy = np.cov(X, Y, rowvar=False)
    print(sigma_xy.shape)
    print(f"Cov (HS): {np.trace(sigma_xy @ sigma_xy.T):.3f}")

    return None

if __name__ == "__main__":
    covariance_self()


#not an application just a module

'''what is this program?
    Assume we have N points on a sphere. points are always pushing each other and they cannot leave surface of the sphere.
    After some play we will see the points in a state of equilibrium. If N > 3 points form a 3d solid.
    Interestingly we can have a solid with declaring N.(ofcourse they are specific type of solids)
    I wanted to study those solids. so we have this program which gives scatter plot of points of those solids.
    '''
#lessthan 20 points are feasible.
import numpy as np

def magnitude(lst):#takes a list of coordinates. Returns magnitude value of that position vector.
    return np.sqrt(lst[0]*lst[0]+lst[1]*lst[1]+lst[2]*lst[2])

def normalize(lst):#takes a list of coordinates. Returns a normalized version of it.
    mag = magnitude(lst)
    return np.array([lst[0]/mag,lst[1]/mag,lst[2]/mag])

def angle_bw(v1,v2): #takes two lists of coordinates of point. Returns angle in degrees
    #Dot product
    dot_product = (v1[0]*v2[0])+(v1[1]*v2[1])+(v1[2]*v2[2])
    #dodging math domain error. making dot product not to overflow from range [-1,1]
    if dot_product < -1:
        dot_product = -1
    elif dot_product > 1:
        dot_product = 1

    return (np.arccos(dot_product))*180/math.pi

def points_on_unit_sphere(N):
    #generating N points from specified range to get unique points
    #And shuffling points for randomness
    rectangular = np.array([np.random.permutation(np.linspace(1,10,N)),
    np.random.permutation(np.linspace(11,20,N)),
    np.random.permutation(np.linspace(21,30,N))])
    rectangular = np.transpose(rectangular)
    for i in range(N):
        rectangular[i] = normalize(rectangular[i])
    #force_vector to find direction of force form all other points
    force_vector = np.zeros(3)
    condition = True

    while condition:
        #in this loop:
        #for a point we find all vectors looking at the point.
        #Add them and normalize resultant vector.
        #Make that resultant unit vector as positional vector of the point.
        max_jump_distance = 0
        for i in range(N):
            force_vector = N*rectangular[i]-np.sum(rectangular,axis = 0)
            force_vector = normalize(force_vector)
            #here we make sure if points are stabalized or not
            jump_distance = magnitude(force_vector-rectangular[i])
            if jump_distance > max_jump_distance:
                max_jump_distance = jump_distance
            rectangular[i] = force_vector
            force_vector = np.zeros(3)
        if max_jump_distance < 0.0000000001:
            condition = False
    #returning finalized rectangular coordinates to plot them.
    return rectangular


# Kalman filter in Python adopted from http://scipy-cookbook.readthedocs.io/items/KalmanFiltering.html
import numpy as np
import matplotlib.pyplot as plt
import time

class KalmanFilter:
    def __init__(self, base_value=24, iterations=200, initial_guess=20.0, posteri_estimate=4.0, data=[], plot=False):
        # intial parameters
        self.n_iter = iterations  # How many iterations to create test data
        sz = (self.n_iter,)  # size of array
        self.x = base_value  # This is the base value that shall be used to create noisy data. It is the true value
        if len(data) == 0:
            self.z = np.random.normal(self.x, 1, size=sz)  # observations (normal about x, sigma=0.1)
        else:
            self.z = data

        self.Q = 1e-5 # process variance

        # allocate space for arrays
        self.xhat = np.zeros(sz)      # a posteri estimate of x
        self.P = np.zeros(sz)         # a posteri error estimate
        self.xhatminus = np.zeros(sz)  # a priori estimate of x
        self.Pminus = np.zeros(sz)

        # a priori error estimate
        self.K = np.zeros(sz)         # gain or blending factor

        self.R = 2

        # intial guesses
        self.xhat[0] = initial_guess  # Initial estimate
        self.P[0] = posteri_estimate  # Estimate of the error made

        self.plot = plot

    def filter(self):
        start = time.time()
        for k in range(1, self.n_iter):
            # time update
            self.xhatminus[k] = self.xhat[k-1]
            self.Pminus[k] = self.P[k-1]+self.Q

            # measurement update
            self.K[k] = self.Pminus[k]/(self.Pminus[k]+self.R)
            self.xhat[k] = self.xhatminus[k]+self.K[k]*(self.z[k]-self.xhatminus[k])
            self.P[k] = (1-self.K[k])*self.Pminus[k]
        end = time.time()

        print("Took %s seconds" % (time.time() - start))

        print "Noisy data: "
        print self.z

        print "Estimates:"
        print self.xhat

        print "Truth Value:"
        print self.x

        #print "Error estimate"
        #print self.P

        if self.plot:
            plt = self.plot_results()
        else:
            plt = None

        return self.z, self.xhat, self.x, plt

    def plot_results(self):
        plt.rcParams['figure.figsize'] = (10, 8)
        plt.figure()
        plt.plot(self.z, 'k+', label='noisy measurements')
        plt.plot(self.xhat, 'b-', label='a posteri estimate')
        plt.axhline(self.x, color='g', label='truth value')
        plt.legend()
        plt.title('Estimate vs. iteration step', fontweight='bold')
        plt.xlabel('Iteration')
        plt.ylabel('Temperature')

        return plt


from matplotlib import pyplot as plt

import numpy as np

# numpy's fft implementation is slow, so use FFTW as a drop-in replacement
import pyfftw.interfaces.numpy_fft as fft

from math import floor

# Load an image
image = plt.imread("einstein1_7.jpg")

# Make image greyscale
image = np.average(image, axis=2)

# Set up geometry
width, height = image.shape

centre_x = floor(width) / 2
centre_y = floor(height) / 2

x_axis = np.arange(width)
y_axis = np.arange(height)

# Create circular filter
filter_radius = 100
filter = np.zeros_like(image)

for i in range(width):
    for j in range(height):
        if (i - centre_x) ** 2 + (j - centre_y) ** 2 > filter_radius ** 2:
            filter[i, j] = 1

# Invert: lowpass filter
filter = 1 - filter

# Move to Fourier plane
FT = fft.fftshift(fft.fft2(image))

# Apply Fourier filter
filtered_FT = FT * filter

# Inverse Fourier transform to image plane
filtered_image = fft.ifft2(fft.ifftshift(filtered_FT))

# Plot results
plt.figure(figsize=[20, 20])

# Original image
plt.subplot(2, 2, 1)
plt.title("Original image")
plt.imshow(image)

# Fourier transform; log scale to bring out detail outside centre
plt.subplot(2, 2, 2)
plt.title("Fourier transform of original image")
plt.imshow(np.log(np.abs(FT) ** 2))

# Filtered fourier transform
plt.subplot(2, 2, 3)
plt.title("Fourier transform restricted to leading modes")
plt.imshow(np.log(np.abs(filtered_FT) ** 2))

# Observed image
plt.subplot(2, 2, 4)
plt.title("Observed image with filter")
plt.imshow(np.abs(filtered_image) ** 2)

plt.savefig("fourier_restricted.pdf")

def salt_pepper(image, r):
    '''Add random light and dark pixels with frequency `r` to `image`,
    returning the result.'''

    r = min(1, r)
    uniform_random = np.random.random(image.shape)

    # Pepper
    noisy_image = np.where(uniform_random < r / 2,
                           np.zeros_like(image),
                           image)

    # Salt
    noisy_image = np.where(uniform_random > 1 - r / 2,
                           np.ones_like(image) * 255,
                           noisy_image)
    return noisy_image

# Add noise to image
noisy_image = salt_pepper(image, 0.1)

# Reuse filter from previous task
lowpass_filter = 1 - filter

# Move to Fourier plane
FT = fft.fftshift(fft.fft2(noisy_image))

# Apply Fourier filter
filtered_FT = FT * lowpass_filter

# Inverse Fourier transform to image plane
filtered_image = fft.ifft2(fft.ifftshift(filtered_FT))

# Plot results
plt.figure(figsize=[15, 15])

# Original image
plt.subplot(2, 2, 1)
plt.title("Image with noise added")

# White noise doesn't show up very well with the default colour map
plt.imshow(noisy_image, cmap='gray')

# Fourier transform; log scale to bring out detail outside centre
plt.subplot(2, 2, 2)
plt.title("Fourier transport of noisy image")
plt.imshow(np.log(np.abs(FT) ** 2))

# Filtered fourier transform
plt.subplot(2, 2, 3)
plt.title("Fourier transform with leading-order modes filtered out")
plt.imshow(np.log(np.abs(filtered_FT) ** 2))

# Observed image
plt.subplot(2, 2, 4)
plt.title("Observed image, noise only")
plt.imshow(np.abs(filtered_image) ** 2 / np.max(abs(filtered_image) ** 2),
           cmap='gray')

plt.savefig("noise_isolation.pdf")

# Move to Fourier plane
FT = fft.fftshift(fft.fft2(image))

# Remove phase information in Fourier plane
phaseless_image = fft.ifft2(fft.ifftshift(abs(FT) ** 2))

# Plot results
plt.figure(figsize=[15, 15])

# Original image
plt.subplot(2, 2, 1)
plt.title("Original image")
plt.imshow(image, cmap='gray')

# Phaseless image
plt.subplot(2, 2, 2)
plt.title("Phaseless image")
plt.imshow(abs(phaseless_image) ** 2)

plt.savefig("phase_contrast.pdf")


import numpy as np
import plotly.graph_objects as go

# rotate about y, translate in x
def frame_yrotate_xtranslate(theta, x):
  theta = np.radians(theta)
  cos_theta = np.cos(theta)
  sin_theta = np.sin(theta)

  return np.array([
    [cos_theta, 0, sin_theta, x],
    [0, 1, 0, 0],
    [-sin_theta, 0, cos_theta, 0],
    [0, 0, 0, 1]
  ])

# rotate about z, translate in x and y
def frame_zrotate_xytranslate(theta, x, y):
  theta = np.radians(theta)
  cos_theta = np.cos(theta)
  sin_theta = np.sin(theta)

  return np.array([
    [cos_theta, -sin_theta, 0, x],
    [sin_theta, cos_theta, 0, y],
    [0, 0, 1, 0],
    [0, 0, 0, 1]
  ])

class Point:
  def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z

  def get_point_wrt(self, reference_frame):
    # given frame_ab which is the pose of frame_b wrt frame_a
    # given a point as defined wrt to frame_b
    # return point defined wrt to frame a
    p = np.array([self.x, self.y, self.z, 1])
    p = np.matmul(reference_frame, p)
    return Point(p[0], p[1], p[2])

class Linkage:
  def __init__(self, a, b, c, alpha=0, beta=0, gamma=0, new_x_axis=0, new_origin=Point(0, 0, 0)):
    self.store_linkage_attributes(a, b, c, new_x_axis, new_origin)
    self.save_new_pose(alpha, beta, gamma)

  def store_linkage_attributes(self, a, b, c, new_x_axis, new_origin):
    self._a = a
    self._b = b
    self._c = c
    self._new_origin = new_origin
    self._new_x_axis = new_x_axis

  def save_new_pose(self, alpha, beta, gamma):
    self._alpha = alpha
    self._beta = beta
    self._gamma = gamma

    # frame_ab is the pose of frame_b wrt frame_a
    frame_01 = frame_yrotate_xtranslate(theta=-self._beta, x=self._a)
    frame_12 = frame_yrotate_xtranslate(theta=90-self._gamma, x=self._b)
    frame_23 = frame_yrotate_xtranslate(theta=0, x=self._c)

    frame_02 = np.matmul(frame_01, frame_12)
    frame_03 = np.matmul(frame_02, frame_23)
    new_frame = frame_zrotate_xytranslate(self._new_x_axis + self._alpha, self._new_origin.x, self._new_origin.y)

    # find points wrt to body contact point
    p0 = Point(0, 0, 0)
    p1 = p0.get_point_wrt(frame_01)
    p2 = p0.get_point_wrt(frame_02)
    p3 = p0.get_point_wrt(frame_03)

    # find points wrt to center of gravity
    self.p0 = self._new_origin
    self.p1 = p1.get_point_wrt(new_frame)
    self.p2 = p2.get_point_wrt(new_frame)
    self.p3 = p3.get_point_wrt(new_frame)

  def change_pose(self, alpha=None, beta=None, gamma=None):
    if alpha is None:
      alpha = self._alpha
    if beta is None:
      beta = self._beta
    if gamma is None:
      gamma = self._gamma
    self.save_new_pose(alpha, beta, gamma)

class Hexagon:
  def __init__(self, f, m, s):
    self.f = f
    self.m = m
    self.s = s

    self.cog = Point(0, 0, 0)
    self.head = Point(0, s, 0)
    self.vertices = [
      Point(m, 0, 0),
      Point(f, s, 0),
      Point(-f, s, 0),
      Point(-m, 0, 0),
      Point(-f, -s, 0),
      Point(f, -s, 0),
    ]

    self.new_x_axes = [
      0, 45, 135, 180, 225, 315
    ]

class VirtualHexapod:
  def __init__(self, a=0, b=0, c=0, f=0, m=0, s=0):
    self.linkage_measurements = [a, b, c]
    self.body_measurements = [f, m, s]
    self.body = Hexagon(f, m, s)
    self.store_neutral_legs(a, b, c)

  def store_neutral_legs(self, a, b, c):
    self.legs = []
    for point, theta in zip(self.body.vertices, self.body.new_x_axes):
      linkage = Linkage(a, b, c, new_x_axis=theta, new_origin=point)
      self.legs.append(linkage)

class VirtualHexapodPlot:
  def __init__(self, _hexapod):
    self.hexapod = _hexapod
    self.fig = go.Figure()

  def _draw_lines(self, _name, _points, _size, _color, _is_name_visible=True):
    self.fig.add_trace(go.Scatter3d(
      name=_name,
      x=[point.x for point in _points],
      y=[point.y for point in _points],
      z=[point.z for point in _points],
      line={
        'color': _color,
        'width': _size
      },
      showlegend=_is_name_visible
    ))

  def _draw_point(self, _name, _point, _size, _color):
    self.fig.add_trace(go.Scatter3d(
      name=_name,
      x=[_point.x],
      y=[_point.y],
      z=[_point.z],
      mode='markers',
      marker={
        'size': _size,
        'color': _color,
        'opacity': 1.0
      }
    ))

  def draw(self):
    LINE_SIZE = 10
    HEAD_SIZE = 15
    COG_SIZE = 10
    BODY_COLOR = '#8e44ad'
    COG_COLOR = '#e74c3c'
    LEG_COLOR = '#2c3e50'

    # Add body outline
    points = self.hexapod.body.vertices
    self._draw_lines('body', points + [points[0]], LINE_SIZE, BODY_COLOR)

    # Add head and center of gravity
    self._draw_point('cog', self.hexapod.body.cog, COG_SIZE, COG_COLOR)
    self._draw_point('head', self.hexapod.body.head, HEAD_SIZE, BODY_COLOR)

    # Draw legs
    for leg in self.hexapod.legs:
      points = [leg.p0, leg.p1, leg.p2, leg.p3]
      self._draw_lines('leg', points, LINE_SIZE, LEG_COLOR, False)

    self.fig.show()

FRONT_LENGTH = 20
SIDE_LENGTH = 30
MID_LENGTH = 40
HIP_LENGTH = 25
KNEE_LENGTH = 40
ANKLE_LENGTH = 60

virtual_hexapod = VirtualHexapod(HIP_LENGTH, KNEE_LENGTH, ANKLE_LENGTH, FRONT_LENGTH, MID_LENGTH, SIDE_LENGTH)
VirtualHexapodPlot(virtual_hexapod).draw()


import numpy as np
import matplotlib.pyplot as plt

def hough(points, weights = None, discretizationRadius = 1000, discretizationAngle = 180):
    """
        Computes hough transforms of the points (in 2D)

        Arguments:
            points {Array n * 2} -- Datapoints to represent in Hough Space
            weights {Array n} -- Weights to associate to each points
                (default {None} - Equal weights)
            discretizationRadius {Int} -- Discretization radius Axis
            discretizationAngle {Int} -- Discretization Angle Axis
    """
    # Polar coordinate of all points
    r = np.sqrt(points[:,0]**2 + points[:,1]**2)
    theta = np.arctan2(points[:,1], points[:,0])

    # Hough space
    houghSpace = np.zeros((discretizationRadius, discretizationAngle))
    radiusBins = np.linspace(- np.max(r), np.max(r), discretizationRadius + 1)
    angleBins = np.linspace(-np.pi, np.pi, discretizationAngle)
    for i, phi in enumerate(angleBins):
        # For the given theta compute the radius of the line cutting the points
        radPhi = np.cos(phi - theta) * r
        hist, _ = np.histogram(radPhi, bins = radiusBins, weights = weights)
        houghSpace[:, i] += hist

    return np.flip(houghSpace, 0), angleBins * 180 / np.pi, radiusBins

def displayHough(points, weights = None):
    """
        Computes hough transforms of the points and displays it

        Arguments:
            points -- Datapoints to project and anlyze
            weights {Array n} -- Weights to associate to each points
    """
    houghSpace, xaxis, yaxis = hough(points, weights)

    plt.figure()
    plt.imshow(houghSpace, extent=[xaxis[0], xaxis[-1], yaxis[0], yaxis[-1]], aspect='auto', cmap='gray')
    plt.ylabel("Radius")
    plt.xlabel("Angle(in deg)")
    plt.show()


from argparse import ArgumentParser

from sympy import diff, dsolve, integrate, solve, var

from .abstract_problem import AbstractSolver
from .utils import sympy_eval, t, x, x_diff

class SimplestSolver(AbstractSolver):

    """
    Solver for simplest problem in calculus of variation.

    Attributes:
        L: Integrand.
        t0: Lower limit of the integral.
        t1: Upper limit of the integral.
        x0: Boundary condition in t0.
        x1: Boundary condition in t1.

    To use:
        solution = SimplestSolver(L="x_diff ** 2", t0="0", t1="1", x0="0", x1="1")
        solution.solve(verbose=True)
    """

    C1 = var("C1")
    C2 = var("C2")

    def __init__(self, L: str, t0: str, t1: str, x0: str, x1: str):
        self._L_str = L

        self.L = sympy_eval(L)
        self.t0 = sympy_eval(t0)
        self.t1 = sympy_eval(t1)
        self.x0 = sympy_eval(x0)
        self.x1 = sympy_eval(x1)

    def __str__(self):
        task = f"integral from {self.t0} to {self.t1} of ({self._L_str})dt -> extr\n"
        condition_1 = f"x({self.t0}) = {self.x0}\n"
        condition_2 = f"x({self.t1}) = {self.x1}\n"
        return f"{task}{condition_1}{condition_2}"

    def __repr__(self):
        return self.__str__()

    def _general_solution(self):
        """
        Find general solution.
        """

        self.L_x_diff = diff(self.L, x_diff)
        self.L_x = diff(self.L, x)

        general_solution = dsolve(self.L_x - diff(self.L_x_diff, t), x).rhs.expand()
        self.general_solution = general_solution

    def _coefficients(self):
        """
        Find particular solution coefficients.
        """

        self.first_eq = self.general_solution.subs(t, self.t0) - self.x0
        self.second_eq = self.general_solution.subs(t, self.t1) - self.x1

        coefficients = solve([self.first_eq, self.second_eq], [self.C1, self.C2])
        self.coefficients = coefficients

    def _particular_solution(self):
        """
        Substitute particular solution coefficients to general solution.
        """

        super()._particular_solution()

    def _extrema_value(self):
        """
        Find extrema value for particular solution.
        """

        L_subs = self.L.subs(
            [(x_diff, diff(self.particular_solution, t)), (x, self.particular_solution)]
        )
        extrema_value = integrate(L_subs, (t, self.t0, self.t1))

        self.extrema_value = extrema_value

    def solve(self, verbose: bool = True):
        """
        Solve task using all encapsulated methods.
        """

        super().solve(verbose=verbose)

if __name__ == "__main__":

    # argparse
    parser = ArgumentParser()
    parser.add_argument("-L", type=str, required=True, help="integrand")
    parser.add_argument(
        "-t0", type=str, required=True, help="lower limit of the integral"
    )
    parser.add_argument(
        "-t1", type=str, required=True, help="upper limit of the integral"
    )
    parser.add_argument("-x0", type=str, required=True, help="boundary condition in t0")
    parser.add_argument("-x1", type=str, required=True, help="boundary condition in t1")
    args = parser.parse_args()

    # solve
    SimplestSolver(L=args.L, t0=args.t0, x0=args.x0, t1=args.t1, x1=args.x1).solve()


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

def generate_zc_sequence(N, u):
    # Add a Zadoff-Chu sequence for
    n = np.arange(N)
    zadoff_chu_seq = np.exp(-1j * (np.pi * u * n * (n + (N % 2)))/N)

    return zadoff_chu_seq

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

    N = 353
    u = 73
    seq = generate_zc_sequence(N, u)
    fig, axes = plt.subplots(2, 1)
    axes[0].set_title('Zadoff Chu sequence, N = {}, u = {}'.format(N, u))
    axes[0].plot(seq.real)
    axes[0].set_ylabel('Real part')
    axes[1].plot(seq.imag)
    axes[1].set_ylabel('Imag part')

    plt.figure()
    autocorrelation = np.correlate(seq, seq, mode='same')
    plt.plot(autocorrelation)
    plt.title('Autocorrelation of Zadoff Chu sequence, N = {}, u = {}'.format(N, u))


import numpy as np
import matplotlib.pyplot as plt

g = 9.81
def analytical_p(h):
    return (1000 * h + h**2 / 2) * g

def rho(h):
    return 1000.0 + h

H = 400.0
N = 20

p = np.zeros(N)
dh = H / (N - 1)

for i in range(1, N):
    h = i * dh
    p[i] = p[i-1] + rho(h) * g * dh

print("%f"%p[N-1])
plt.gca().invert_yaxis()
height = np.linspace(0.0, H, N)

plt.plot(p, height, "o", label="Numerical", ms=8)
plt.plot(analytical_p(height), height, "-", lw=3, label="Analytical")

plt.xlabel("$p$", fontsize=20)
plt.ylabel("$h$", fontsize=20)

plt.grid()
plt.legend(loc="best")

plt.savefig("pressure.png")
plt.show()


import numpy as np
import math
from numpy.random import default_rng
from crisp.utils.planar_utils import radial_squared_error

def generate_circumference(center, radius, num_points):
    """
    Generates a circle trajectory
    :param center: coordinates of circumference center
    :param radius: circumference radius
    :param num_points: number of points in the trajectory
    :return: a ndarray with the trajectory of points
    """
    theta_z = np.linspace(0, 360, num=num_points)
    Circle = np.zeros((num_points, 3))
    Circle[:, 0] = center[0] + radius * np.cos(np.radians(theta_z))
    Circle[:, 1] = center[1] + radius * np.sin(np.radians(theta_z))
    Circle[:, 2] = center[2]

    return Circle

def generate_noisy_circumference_samples(center, radius1, radius2, num_points):
    """
    Generates a circle trajectory
    :param center: coordinates of circumference center
    :param radius1: Main circumference radius
    :param radius2: small torus radius
    :param num_points: number of points to sample
    :return: a ndarray with the trajectory of points
    """

    sample = np.zeros((num_points, 3))

    # Initialize random number generator
    rng = default_rng()

    for i in range(num_points):

        # Uniformly sample joint_ranges
        theta1 = rng.uniform(0, 2*np.pi, 1)
        theta2 = rng.uniform(0, 2*np.pi, 1)
        r = radius2*np.sqrt(rng.uniform(0, 1, 1))

        # Sample on main circumference
        sample[i, 0] = center[0] + radius1 * np.cos(theta1)
        sample[i, 1] = center[1] + radius1 * np.sin(theta1)
        sample[i, 2] = center[2]

        # Add torus noise (see https://stackoverflow.com/a/50746409)
        sample[i, 0] = sample[i, 0] + r * np.cos(theta2) * np.cos(theta1)
        sample[i, 1] = sample[i, 1] + r * np.cos(theta2) * np.sin(theta1)
        sample[i, 2] = sample[i, 2] + r * np.sin(theta2)

    return sample

def generate_spiral(center, radius, theta_max, height, num_points):
    """
    Generates a spiral trajectory
    :param center: coordinates of the center of the containing cylinder
    :param radius: spiral radius
    :param theta_max: maximum angle of the spiral trajectory (0, +inf)
    :param height: height of the spiral along z
    :param num_points: number of points in the trajectory
    :return: a ndarray with the trajectory of points
    """
    theta_z = np.linspace(0, theta_max, num=num_points)

    spiral = np.zeros((num_points, 3))
    spiral[:, 0] = center[0] + radius * np.cos(np.radians(theta_z))
    spiral[:, 1] = center[1] + radius * np.sin(np.radians(theta_z))
    spiral[:, 2] = np.concatenate( (np.linspace(center[2] - height/2.0, center[2] + height/2.0, math.floor(num_points/2)),
                                    np.linspace(center[2] + height/2.0, center[2] - height/2.0, math.ceil(num_points/2))))

    return spiral

def generate_8_shape(center, radius, num_points):
    """
    Generates an eight-like  trajectory
    :param center: coordinates of shape center
    :param radius: shape radius
    :param num_points: number of points in the trajectory
    :return: a ndarray with the trajectory of points
    """
    t = np.linspace(0, 2*np.pi, num_points)
    z = center[2] * np.ones(num_points)
    return np.vstack((radius * np.sin(t) * np.cos(t) + center[0],
                    radius * np.sin(t) + center[1],
                    z
                    )).T

def sqdist(X1, X2):
    """ Given two matrices whose rows are points, computes the distances
    between all the points of the first matrix and all the points of the
    second matrix
    Arguments:
    X1: [N1 x d], earch row is a d-dimensional point
    X2: [N2 x d], each row is a d-dimensional point
    Returns:
    M: [N1 x N2], each element  is the distance between two points
    M_ij = || X1_i - X2_j ||"""

    if not isinstance(X1, np.ndarray):
        X1 = np.array(X1)
    if not isinstance(X2, np.ndarray):
        X2 = np.array(X2)
    if X1.ndim <= 1:
        sqx         = np.array(X1*X1, dtype=np.float32)
        rows_X1     = 1
    else:
        sqx = np.sum(np.multiply(X1, X1), 1)
        rows_X1     = sqx.shape[0]

    if X2.ndim <= 1:
        sqy         = np.array(X2*X2, dtype=np.float32)
        rows_X2     = 1
    else:
        sqy = np.sum(np.multiply(X2, X2), 1)
        rows_X2     = sqy.shape[0]
    X1_squares      = np.squeeze(np.outer(np.ones(rows_X1), sqy.T))
    X2_squares      = np.squeeze(np.outer(sqx, np.ones(rows_X2)))
    double_prod     = np.squeeze(2 * np.dot(X1,X2.T))

    return np.maximum(X1_squares + X2_squares - double_prod, np.zeros(X1_squares.shape))

def check_joint_limits_respected(lower_joint_limits, upper_joint_limits, joints_configuration_query):
    """
    Generates a circle trajectory
    :param lower_joint_limits: upper joint limits
    :param upper_joint_limits: lower joint limits
    :param joints_configuration_query: joints configuration to be checked
    :return: True if respected, False otherwise
    """

    # Check list sizes match
    assert len(lower_joint_limits) == len(upper_joint_limits) == len(joints_configuration_query)

    size = len(lower_joint_limits)

    is_ok = True
    for i in range(size):
        is_ok = lower_joint_limits[i] <= joints_configuration_query[i] <= upper_joint_limits[i]
        if not is_ok:
            break

    return is_ok

def compute_rmse(trajectory_1, trajectory_2):

    if trajectory_1.ndim == 2 and trajectory_2.ndim == 2:
        m1 = trajectory_1.shape[0]
        m2 = trajectory_2.shape[0]
        assert m1 == m2
    elif trajectory_1.ndim == 1 and trajectory_2.ndim == 1:
        m1 = 1
        assert trajectory_1.shape[0] == trajectory_2.shape[0]
    else:
        return np.nan

    mse = 0

    for i in range(m1):
        mse += np.linalg.norm(trajectory_1[i] - trajectory_2[i]) ** 2

    mse /= m1

    rmse = np.sqrt(mse)

    # TODO: vectorized form
    # error = np.linalg.norm(trajectory_1 - trajectory_2)
    # mse = np.sqrt(np.mean(np.sum(error ** 2))
    #               )

    return rmse

def predict_panda_trajectory_CRiSP(xte, invert_kin, forward, out, s, v, alg):

    predictions, data = invert_kin(xte, is_sequence=True)

    # Compare results on test set
    reconstructed = forward(predictions)
    error = np.full((xte.shape), np.inf)

    error[:,:3] = np.sqrt((reconstructed[:, :3] - xte[:, :3])**2)
    error[:, 3:] = np.sqrt(radial_squared_error(reconstructed[:, 3:], xte[:, 3:]))

    rmse_orientation = np.mean(np.sum(error[:, 3:], axis=1))
    var_orientation = np.std(np.sum(error[:, 3:], axis=1))

    rmse_position = np.mean(np.sum(error[:, :3], axis=1))
    var_position = np.std(np.sum(error[:, :3], axis=1))

    rmse = np.mean(np.sum(error, axis=1))
    var = np.std(np.sum(error, axis=1))

    out(f"\nAlgorithm: {alg}")
    results =   {'rmse_orientation': rmse_orientation,
                'var_orientation': var_orientation,
                'rmse_position': rmse_position,
                'var_position': var_position,
                'rmse': rmse,
                'var': var,
                'xte': xte,
                'alpha': data['alpha'],
                'Kx': data['Kx'],
                'trajectory_pos': reconstructed[:, :3],
                'trajectory_orn': reconstructed[:, 3:],
                'trajectory_joints_pred_CRiSP': predictions}

    out(f"s: {s}\t v: {v}")
    out(f"\t\tRMSE ori: {results['rmse_orientation']:7.6f} ± {results['var_orientation']:7.6f}")
    out(f"\t\tRMSE pos: {results['rmse_position']:7.6f} ± {results['var_position']:7.6f}\n"
             f"\t\tRMSE: {results['rmse']:7.6f} ± {results['var']:7.6f}")
    out(f"Norm of alphas = {np.linalg.norm(data['alpha'])}")

    return results

def predict_panda_trajectory_PB(xte, invert_kin, forward, out, s, v, alg):

    predictions = invert_kin(xte)

    # Compare results on test set
    reconstructed = forward(predictions)
    error = np.full((xte.shape), np.inf)

    error[:,:3] = np.sqrt((reconstructed[:, :3] - xte[:, :3])**2)
    error[:, 3:] = np.sqrt(radial_squared_error(reconstructed[:, 3:], xte[:, 3:]))

    rmse_orientation = np.mean(np.sum(error[:, 3:], axis=1))
    var_orientation = np.std(np.sum(error[:, 3:], axis=1))

    rmse_position = np.mean(np.sum(error[:, :3], axis=1))
    var_position = np.std(np.sum(error[:, :3], axis=1))

    rmse = np.mean(np.sum(error, axis=1))
    var = np.std(np.sum(error, axis=1))

    out(f"\nAlgorithm: PyBullet Inverse Kinematics")
    results = {'rmse_orientation': rmse_orientation, 'var_orientation': var_orientation,
                'rmse_position': rmse_position, 'var_position': var_position,
                'rmse': rmse, 'var': var, 'xte': xte, 'predictions': predictions,
               'trajectory_PyBullet_IK_reference_joints': predictions,
               'trajectory_pos_ikpb': reconstructed[:, :3],
               'trajectory_orn_ikpb': reconstructed[:, 3:],
               }
    out(f"\t\tRMSE ori: {results['rmse_orientation']:7.6f} ± {results['var_orientation']:7.6f}")
    out(f"\t\tRMSE pos: {results['rmse_position']:7.6f} ± {results['var_position']:7.6f}\n"
             f"\t\tRMSE: {results['rmse']:7.6f} ± {results['var']:7.6f}")

    return results