# coding=utf-8
"""
Bezier Surface using python, numpy and matplotlib.
"""

import numpy as np
import matplotlib.pyplot as mpl

__author__ = "Daniel Calderon"
__license__ = "MIT"

def generateT(t):
    return np.array([[1, t, t ** 2, t ** 3]]).T

def evalBernstein3(k, t):
    # Full Bezier Matrix
    Mb = np.array([[1, -3, 3, -1], [0, 3, -6, 3], [0, 0, 3, -3], [0, 0, 0, 1]])

    T = generateT(t)

    return np.dot(Mb[k, :], T)[0]

def evalBezierSurfaceSample(ps, t, s):
    Q = np.zeros(3)
    for k in range(4):
        for l in range(4):
            Bk = evalBernstein3(k, s)
            Bl = evalBernstein3(l, t)

            Q += Bk * Bl * ps[k, l]

    return Q

def evalBezierSurface(ps, ts, ss):
    Q = np.ndarray(shape=(N, N, 3), dtype=float)
    for i in range(len(ts)):
        for j in range(len(ss)):
            Q[i, j] = evalBezierSurfaceSample(ps, ts[i], ss[j])

    return Q

if __name__ == "__main__":
    """
    Defining control points
    """

    # A 4x4 array with a control point in each of those positions
    P = np.ndarray(shape=(4, 4, 3), dtype=float)
    P[0, 0, :] = np.array([[0, 0, 0]])
    P[0, 1, :] = np.array([[0, 1, 0]])
    P[0, 2, :] = np.array([[0, 2, 0]])
    P[0, 3, :] = np.array([[0, 3, 0]])

    P[1, 0, :] = np.array([[1, 0, 0]])
    P[1, 1, :] = np.array([[1, 1, 10]])
    P[1, 2, :] = np.array([[1, 2, 10]])
    P[1, 3, :] = np.array([[1, 3, 0]])

    P[2, 0, :] = np.array([[2, 0, 0]])
    P[2, 1, :] = np.array([[2, 1, 10]])
    P[2, 2, :] = np.array([[2, 2, 10]])
    P[2, 3, :] = np.array([[2, 3, 0]])

    P[3, 0, :] = np.array([[3, 0, 0]])
    P[3, 1, :] = np.array([[3, 1, -5]])
    P[3, 2, :] = np.array([[3, 2, -5]])
    P[3, 3, :] = np.array([[3, 3, 0]])

    # Setting up the matplotlib display for 3D
    fig = mpl.figure()
    ax = fig.gca(projection='3d')

    """
    Visualizing the control points
    """
    # They are sorted into a list of points
    Pl = P.reshape(16, 3)

    # Each component is queried from the previous array
    ax.scatter(Pl[:, 0], Pl[:, 1], Pl[:, 2], color=(1, 0, 0), label="Control Points")

    """
    Discretizing the surface
    """
    # We use the same amount of samples for both, t and s parameters
    N = 10

    # The parameters t and s should move between 0 and 1
    ts = np.linspace(0.0, 1.0, N)
    ss = np.linspace(0.0, 1.0, N)

    # This function evaluates the bezier surface at each t and s samples in the arrays
    # The solution is stored in the 2D-array Q, where each sample is a 3D vector
    Q = evalBezierSurface(P, ts, ss)

    """
    Visualizing the Bezier surface
    """
    # For convenience, we re-organize the data into a list of points
    QlinearShape = (Q.shape[0] * Q.shape[1], 3)
    Ql = Q.reshape(QlinearShape)

    # An option is to plot just each dot computed
    # ax.scatter(Ql[:,0], Ql[:,1], Ql[:,2], color=(0,0,1))

    # The more elegant option is to make a triangulation and visualize it as a surface
    surf = ax.plot_trisurf(Ql[:, 0], Ql[:, 1], Ql[:, 2], linewidth=0, antialiased=False)

    # Showing the result
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    ax.legend()
    mpl.title("Bezier Surface")
    mpl.show()


import numpy as np
from collections import Counter

from dbca.freq_distribution import FrequencyDistribution

def chernoff_similarity(P: FrequencyDistribution, Q: FrequencyDistribution, alpha: float = 0.5) -> float:
    """
    Return Chernoff similarity between 2 frequency distributions

    Parameters
    ----------
    P : FrequencyDistribution
    Q : FrequencyDistribution
    alpha: float
        Chernoff co-efficient (scalar in [0,1])

    Returns
    -------
    float
        Chernoff similarity.

    """
    all_elements = sorted(list(P.elements | Q.elements)) # ensure same iteration order
    p_probs = np.array([P.element_prob(e) for e in all_elements])**alpha
    q_probs = np.array([Q.element_prob(e) for e in all_elements])**(1-alpha)
    return np.dot(p_probs, q_probs)

def chernoff_divergence(P: FrequencyDistribution, Q: FrequencyDistribution, alpha: float = 0.5) -> float:
    """
    Return complement of Chernoff similarity between two distributions

    Parameters
    ----------
    P : FrequencyDistribution
    Q : FrequencyDistribution
    alpha : float, optional
        Chernoff co-efficient. The default is 0.5.

    Returns
    -------
    float
        DESCRIPTION.

    """
    return 1 - chernoff_similarity(P, Q, alpha)


#!/usr/bin/env python
# As v1, but using scipy.sparse.diags instead of spdiags
"""
Functions for solving a 1D diffusion equations of simplest types
(constant coefficient, no source term):

      u_t = a*u_xx on (0,L)

with boundary conditions u=0 on x=0,L, for t in (0,T].
Initial condition: u(x,0)=I(x).

The following naming convention of variables are used.

===== ==========================================================
Name  Description
===== ==========================================================
Nx    The total number of mesh cells; mesh points are numbered
      from 0 to Nx.
F     The dimensionless number a*dt/dx**2, which implicitly
      specifies the time step.
T     The stop time for the simulation.
I     Initial condition (Python function of x).
a     Variable coefficient (constant).
L     Length of the domain ([0,L]).
x     Mesh points in space.
t     Mesh points in time.
n     Index counter in time.
u     Unknown at current/new time level.
u_n   u at the previous time level.
dx    Constant mesh spacing in x.
dt    Constant mesh spacing in t.
===== ==========================================================

user_action is a function of (u, x, t, n), u[i] is the solution at
spatial mesh point x[i] at time t[n], where the calling code
can add visualization, error computations, data analysis,
store solutions, etc.
"""
import sys
import time
# import scitools.std as plt
import scipy.sparse
import scipy.sparse.linalg
import numpy as np

def solver_FE_simple(I, a, f, L, dt, F, T):
    """
    Simplest expression of the computational algorithm
    using the Forward Euler method and explicit Python loops.
    For this method F <= 0.5 for stability.
    """
    import time
    t0 = time.perf_counter()  # For measuring the CPU time

    Nt = int(round(T/float(dt)))
    t = np.linspace(0, Nt*dt, Nt+1)   # Mesh points in time
    dx = np.sqrt(a*dt/F)
    Nx = int(round(L/dx))
    x = np.linspace(0, L, Nx+1)       # Mesh points in space
    # Make sure dx and dt are compatible with x and t
    dx = x[1] - x[0]
    dt = t[1] - t[0]

    u = np.zeros(Nx+1)
    u_n = np.zeros(Nx+1)

    # Set initial condition u(x,0) = I(x)
    for i in range(0, Nx+1):
        u_n[i] = I(x[i])

    for n in range(0, Nt):
        # Compute u at inner mesh points
        for i in range(1, Nx):
            u[i] = u_n[i] + F*(u_n[i-1] - 2*u_n[i] + u_n[i+1]) + \
                   dt*f(x[i], t[n])

        # Insert boundary conditions
        u[0] = 0
        u[Nx] = 0

        # Switch variables before next step
        # u_n[:] = u  # safe, but slow
        u_n, u = u, u_n

    t1 = time.perf_counter()
    return u_n, x, t, t1-t0  # u_n holds latest u

def solver_FE(I, a, f, L, dt, F, T,
              user_action=None, version='scalar'):
    """
    Vectorized implementation of solver_FE_simple.
    """
    t0 = time.perf_counter()  # for measuring the CPU time

    Nt = int(round(T/float(dt)))
    t = np.linspace(0, Nt*dt, Nt+1)   # Mesh points in time
    dx = np.sqrt(a*dt/F)
    Nx = int(round(L/dx))
    x = np.linspace(0, L, Nx+1)       # Mesh points in space
    # Make sure dx and dt are compatible with x and t
    dx = x[1] - x[0]
    dt = t[1] - t[0]

    u = np.zeros(Nx+1)  # solution array
    u_n = np.zeros(Nx+1)  # solution at t-dt

    # Set initial condition
    for i in range(0, Nx+1):
        u_n[i] = I(x[i])

    if user_action is not None:
        user_action(u_n, x, t, 0)

    for n in range(0, Nt):
        # Update all inner points
        if version == 'scalar':
            for i in range(1, Nx):
                u[i] = u_n[i] +\
                       F*(u_n[i-1] - 2*u_n[i] + u_n[i+1]) +\
                       dt*f(x[i], t[n])

        elif version == 'vectorized':
            u[1:Nx] = u_n[1:Nx] +  \
                      F*(u_n[0:Nx-1] - 2*u_n[1:Nx] + u_n[2:Nx+1]) +\
                      dt*f(x[1:Nx], t[n])
        else:
            raise ValueError('version=%s' % version)

        # Insert boundary conditions
        u[0] = 0
        u[Nx] = 0
        if user_action is not None:
            user_action(u, x, t, n+1)

        # Switch variables before next step
        u_n, u = u, u_n

    t1 = time.perf_counter()
    return t1-t0

def solver_BE_simple(I, a, f, L, dt, F, T, user_action=None):
    """
    Simplest expression of the computational algorithm
    for the Backward Euler method, using explicit Python loops
    and a dense matrix format for the coefficient matrix.
    """
    import time
    t0 = time.perf_counter()  # for measuring the CPU time

    Nt = int(round(T/float(dt)))
    t = np.linspace(0, Nt*dt, Nt+1)   # Mesh points in time
    dx = np.sqrt(a*dt/F)
    Nx = int(round(L/dx))
    x = np.linspace(0, L, Nx+1)       # Mesh points in space
    # Make sure dx and dt are compatible with x and t
    dx = x[1] - x[0]
    dt = t[1] - t[0]

    u = np.zeros(Nx+1)
    u_n = np.zeros(Nx+1)

    # Data structures for the linear system
    A = np.zeros((Nx+1, Nx+1))
    b = np.zeros(Nx+1)

    for i in range(1, Nx):
        A[i, i-1] = -F
        A[i, i+1] = -F
        A[i, i] = 1 + 2*F
    A[0, 0] = A[Nx, Nx] = 1

    # Set initial condition u(x,0) = I(x)
    for i in range(0, Nx+1):
        u_n[i] = I(x[i])
    if user_action is not None:
        user_action(u_n, x, t, 0)

    for n in range(0, Nt):
        # Compute b and solve linear system
        for i in range(1, Nx):
            b[i] = u_n[i] + dt*f(x[i], t[n+1])
        b[0] = b[Nx] = 0
        u[:] = np.linalg.solve(A, b)

        if user_action is not None:
            user_action(u, x, t, n+1)

        # Update u_n before next step
        u_n, u = u, u_n

    t1 = time.perf_counter()
    return t1-t0

def solver_BE(I, a, f, L, dt, F, T, user_action=None):
    """
    Vectorized implementation of solver_BE_simple using also
    a sparse (tridiagonal) matrix for efficiency.
    """
    import time
    t0 = time.perf_counter()  # for measuring the CPU time

    Nt = int(round(T/float(dt)))
    t = np.linspace(0, Nt*dt, Nt+1)   # Mesh points in time
    dx = np.sqrt(a*dt/F)
    Nx = int(round(L/dx))
    x = np.linspace(0, L, Nx+1)       # Mesh points in space
    # Make sure dx and dt are compatible with x and t
    dx = x[1] - x[0]
    dt = t[1] - t[0]

    u = np.zeros(Nx+1)  # Solution array at t[n+1]
    u_n = np.zeros(Nx+1)  # Solution at t[n]

    # Representation of sparse matrix and right-hand side
    diagonal = np.zeros(Nx+1)
    lower = np.zeros(Nx)
    upper = np.zeros(Nx)
    b = np.zeros(Nx+1)

    # Precompute sparse matrix
    diagonal[:] = 1 + 2*F
    lower[:] = -F  # 1
    upper[:] = -F  # 1
    # Insert boundary conditions
    diagonal[0] = 1
    upper[0] = 0
    diagonal[Nx] = 1
    lower[-1] = 0

    A = scipy.sparse.diags(
        diagonals=[diagonal, lower, upper],
        offsets=[0, -1, 1], shape=(Nx+1, Nx+1),
        format='csr')
    print(A.todense())

    # Set initial condition
    for i in range(0, Nx+1):
        u_n[i] = I(x[i])

    if user_action is not None:
        user_action(u_n, x, t, 0)

    for n in range(0, Nt):
        b = u_n + dt*f(x[:], t[n+1])
        b[0] = b[-1] = 0.0  # boundary conditions
        u[:] = scipy.sparse.linalg.spsolve(A, b)

        if user_action is not None:
            user_action(u, x, t, n+1)

        # Update u_n before next step
        # u_n[:] = u
        u_n, u = u, u_n

    t1 = time.perf_counter()
    return t1-t0

def solver_theta(I, a, f, L, dt, F, T, theta=0.5, u_L=0, u_R=0,
                 user_action=None):
    """
    Full solver for the model problem using the theta-rule
    difference approximation in time (no restriction on F,
    i.e., the time step when theta >= 0.5).
    Vectorized implementation and sparse (tridiagonal)
    coefficient matrix.
    """
    import time
    t0 = time.perf_counter()  # for measuring the CPU time

    Nt = int(round(T/float(dt)))
    t = np.linspace(0, Nt*dt, Nt+1)   # Mesh points in time
    dx = np.sqrt(a*dt/F)
    Nx = int(round(L/dx))
    x = np.linspace(0, L, Nx+1)       # Mesh points in space
    # Make sure dx and dt are compatible with x and t
    dx = x[1] - x[0]
    dt = t[1] - t[0]

    u = np.zeros(Nx+1)   # solution array at t[n+1]
    u_n = np.zeros(Nx+1)   # solution at t[n]

    # Representation of sparse matrix and right-hand side
    diagonal = np.zeros(Nx+1)
    lower = np.zeros(Nx)
    upper = np.zeros(Nx)
    b = np.zeros(Nx+1)

    # Precompute sparse matrix (scipy format)
    Fl = F*theta
    Fr = F*(1-theta)
    diagonal[:] = 1 + 2*Fl
    lower[:] = -Fl  # 1
    upper[:] = -Fl  # 1
    # Insert boundary conditions
    diagonal[0] = 1
    upper[0] = 0
    diagonal[Nx] = 1
    lower[-1] = 0

    diags = [0, -1, 1]
    A = scipy.sparse.diags(
        diagonals=[diagonal, lower, upper],
        offsets=[0, -1, 1], shape=(Nx+1, Nx+1),
        format='csr')
    print(A.todense())

    # Set initial condition
    for i in range(0, Nx+1):
        u_n[i] = I(x[i])

    if user_action is not None:
        user_action(u_n, x, t, 0)

    # Time loop
    for n in range(0, Nt):
        b[1:-1] = u_n[1:-1] + \
                  Fr*(u_n[:-2] - 2*u_n[1:-1] + u_n[2:]) + \
                  dt*theta*f(x[1:-1], t[n+1]) + \
                  dt*(1-theta)*f(x[1:-1], t[n])
        b[0] = u_L  # Boundary conditions
        b[-1] = u_R
        u[:] = scipy.sparse.linalg.spsolve(A, b)

        if user_action is not None:
            user_action(u, x, t, n+1)

        # Update u_n before next step
        u_n, u = u, u_n

    t1 = time.perf_counter()
    return t1-t0

def viz(I, a, L, dt, F, T, umin, umax,
        scheme='FE', animate=True, framefiles=True):

    def plot_u(u, x, t, n):
        plt.plot(x, u, 'r-', axis=[0, L, umin, umax],
                 title='t=%f' % t[n])
        if framefiles:
            plt.savefig('tmp_frame%04d.png' % n)
        if t[n] == 0:
            time.sleep(2)
        elif not framefiles:
            # It takes time to write files so pause is needed
            # for screen only animation
            time.sleep(0.2)

    user_action = plot_u if animate else lambda u, x, t, n: None

    cpu = eval('solver_'+scheme)(I, a, L, dt, F, T,
                                 user_action=user_action)
    return cpu

def plug(scheme='FE', F=0.5, Nx=50):
    L = 1.
    a = 1.
    T = 0.1
    # Compute dt from Nx and F
    dx = L/Nx
    dt = F/a*dx**2

    def I(x):
        """Plug profile as initial condition."""
        if abs(x-L/2.0) > 0.1:
            return 0
        else:
            return 1

    cpu = viz(I, a, L, dt, F, T,
              umin=-0.1, umax=1.1,
              scheme=scheme, animate=True, framefiles=True)
    print('CPU time:', cpu)

def gaussian(scheme='FE', F=0.5, Nx=50, sigma=0.05):
    L = 1.
    a = 1.
    T = 0.1
    # Compute dt from Nx and F
    dx = L/Nx
    dt = F/a*dx**2

    def I(x):
        """Gaussian profile as initial condition."""
        return exp(-0.5*((x-L/2.0)**2)/sigma**2)

    u, cpu = viz(I, a, L, dt, F, T,
                 umin=-0.1, umax=1.1,
                 scheme=scheme, animate=True, framefiles=True)
    print('CPU time:', cpu)

def expsin(scheme='FE', F=0.5, m=3):
    L = 10.0
    a = 1
    T = 1.2

    def exact(x, t):
        return exp(-m**2*pi**2*a/L**2*t)*sin(m*pi/L*x)

    def I(x):
        return exact(x, 0)

    Nx = 80
    # Compute dt from Nx and F
    dx = L/Nx
    dt = F/a*dx**2
    viz(I, a, L, dt, F, T, -1, 1, scheme=scheme, animate=True,
        framefiles=True)

    # Convergence study
    def action(u, x, t, n):
        e = abs(u - exact(x, t[n])).max()
        errors.append(e)

    errors = []
    Nx_values = [10, 20, 40, 80, 160]
    for Nx in Nx_values:
        eval('solver_'+scheme)(I, a, L, Nx, F, T, user_action=action)
        dt = F*(L/Nx)**2/a
        print(dt, errors[-1])

def test_solvers():
    def u_exact(x, t):
        return x*(L-x)*5*t  # fulfills BC at x=0 and x=L

    def I(x):
        return u_exact(x, 0)

    def f(x, t):
        return 5*x*(L-x) + 10*a*t

    a = 3.5
    L = 1.5
    Nx = 4
    F = 0.5
    # Compute dt from Nx and F
    dx = L/Nx
    dt = F/a*dx**2

    def compare(u, x, t, n):      # user_action function
        """Compare exact and computed solution."""
        u_e = u_exact(x, t[n])
        diff = abs(u_e - u).max()
        tol = 1E-14
        assert diff < tol, 'max diff: %g' % diff

    import functools
    s = functools.partial  # object for calling a function w/args
    solvers = [
        s(solver_FE_simple, I=I, a=a, f=f, L=L, dt=dt, F=F, T=0.2),
        s(solver_FE, I=I, a=a, f=f, L=L, dt=dt, F=F, T=2,
          user_action=compare, version='scalar'),
        s(solver_FE, I=I, a=a, f=f, L=L, dt=dt, F=F, T=2,
          user_action=compare, version='vectorized'),
        s(solver_BE_simple, I=I, a=a, f=f, L=L, dt=dt, F=F, T=2,
          user_action=compare),
        s(solver_BE, I=I, a=a, f=f, L=L, dt=dt, F=F, T=2,
          user_action=compare),
        s(solver_theta, I=I, a=a, f=f, L=L, dt=dt, F=F, T=2,
          theta=0, u_L=0, u_R=0, user_action=compare),
        ]
    # solver_FE_simple has different return from the others
    u, x, t, cpu = solvers[0]()
    u_e = u_exact(x, t[-1])
    diff = abs(u_e - u).max()
    tol = 1E-14
    print(u_e)
    print(u)
    assert diff < tol, 'max diff solver_FE_simple: %g' % diff

    for solver in solvers:
        solver()

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("""Usage %s function arg1 arg2 arg3 ...""" % sys.argv[0])
        sys.exit(0)
    cmd = '%s(%s)' % (sys.argv[1], ', '.join(sys.argv[2:]))
    print(cmd)
    eval(cmd)


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

import numpy as np

def linealSolverForward(A, b):
    """Solves a system of equations given a lower triangular matrix.

    Arguments:
        A: Lower triangular matrix. Shape: (n, n)
        b: System's solutions. Shape: (n, 1)

    Raises:
        RuntimeError if A is not lower triangular

    Returns:
        A numpy array with the system's X vector. Shape: (n, 1)
    """
    #First we check if the matrix is a lower triangle
    if np.allclose(A, np.tril(A)):
        n = len(b)
        x = np.zeros((n, 1))

        #we apply the forward formula for every element of x
        for k in range(0, n):
            tempSum = []
            for number in range(0, k):
                tempSum.append(-1 * A[k][number] * x[number])
            tempSum = sum(tempSum)
            x[k] = (b[k] + tempSum) / A[k][k]

        return x

    else:
        raise RuntimeError("Matrix A is not lower triangular.")

def test_linealSolverForward():
    """Test linealSolverForward.

    This function will stop the code execution when it finds an error.

    Returns:
        None
    """

    # ====== Forward and backwards substitution ======
    # Each tuple has tree elements, by index:
    # 0: matrix A (lower triangular)
    # 1: matrix b (System solution)
    # 2: matrix c (Expected answer)
    #
    # System's solutions were verified using WolframAlpha.

    lowTriangMatrices = (
        (np.array([[8, 0, 0], [2, 3, 0], [4, 7, 1]]),
         np.array([[8], [5], [0]]),
         np.array([[1], [1], [-11]])),
        (np.array([[8, 0, 0], [2, 3, 0], [4, 7, 1]]),
         np.array([[5], [1], [-8]]),
         np.array([[5 / 8], [-1 / 12], [-119 / 12]])),
        (np.array([[5, 0, 0], [76, 63, 0], [47, 77, 31]]),
         np.array([[69], [10], [4]]),
         np.array([[69 / 5], [-742 / 45], [28127 / 1395]])),
        (np.array([[44, 0, 0, 0], [17, 10, 0, 0],
                   [65, 43, 49, 0], [75, 5, 81, 76]]),
         np.array([[66], [74], [8], [22]]),
         np.array([[3 / 2], [97 / 20], [-5961 / 980], [9747 / 1960]])),
    )

    print("="*60, "\nTesting linearSolverForward() function.")
    for system in lowTriangMatrices:
        try:
            print('-'*50)
            print("Testing system:\n"
                  + f"A =\n{system[0]}\n"
                  + f"b =\n{system[1]}\n"
                  + f"Expected answer =\n{system[2]}")

            ans = linealSolverForward(system[0], system[1])
            comparison = np.allclose(ans, system[2])
            assert \
                    comparison, \
                    "The last tested system did not pass the test."
            print("-> Pass <-")
        except RuntimeError:
            assert False, "The last tested system did not pass the test."

if __name__ == "__main__":
    print("Executed as stand-alone script. Running test function.\n")
    test_linealSolverForward()


# Objective: Introduction to Numpy. Understanding it for data analysis
# Reference: https://www.machinelearningplus.com/python/numpy-tutorial-part1-array-python-examples/

import numpy as np

# There are many ways to create a numpy array. The most common one is to create a list and then pass it into `np.array()` function
list1=[1,2,3,4]
print("list and its type", type(list1))
arr1= np.array(list1)
print(type(arr1))

# A list cannot handle math operations while a numpy array can
#list1+2 will give error
print(arr1+2)

# create a 2d array
list2 = [[0,1,22],[3,4,5]]
print(list2)
print(np.array(list2))
# specify the datatype by setting the dtype argument
list2 = [[0,1,22],[3,4,5]]
print(np.array(list2, dtype=float)) # The decimal point after each number is indicative of the float datatype.

# You can also convert it to a different datatype using the astype method.
arr2d = np.array(list2)
print(arr2d.astype(str))

# if you are uncertain about what datatype your array will hold or if you want to hold characters and numbers in the same array, you can set the dtype as 'object'.
# create an array to hold numbers and strings
arr2d_obj = np.array([1,2.3,4.4,100,'cat','bat'], dtype=object)
print(arr2d_obj)

# Finally, you can always convert an array back to a python list using tolist().
print(arr2d_obj.tolist())

# How to check the array shape and size?
# In R, use dim() for dimesnions and shape()
# In Python use, ndim(), to check if its 1D or 2D array;
# In python use, shape(), to check how many items are present in each dimension.
# In python use, dtype(), to check the data type
# In python use, size(), to check the size of array
# In python use, shape(), to sample a first few items through indexing
print(arr2d.ndim)
print(arr2d_obj.shape)
print(arr2d_obj.dtype)
print(arr2d_obj.size)
#print(arr2d_obj.sample)

# How to extract specific elements from a numpy array. Use the : notation
list3 = [10,20,30,40]
arr2 = np.array(list3)
x = arr2[:4]
print(x)

# reverse the rows and the whole array. Use :: notation
print(arr2[::-1]) # reverse only the rows
list4 = [[1,2,3,4],[5,6,7,8]]
arr3 = np.array(list4, dtype=float)
print(arr3[::-1,::-1])

# Missing values can be represented using np.nan object, while np.inf represents infinite.
# Insert a nan and an inf
arr3[1,1] = np.nan  # not a number
arr3[1,2] = np.inf  # infinite
print(arr3)

# How to compute mean, min, max on the ndarray?
list5 = [[1,2,3,4],[5,6,7,8]]
arr4 = np.array(list5)
print("Mean value is ", arr4.mean())
print("Min value is ", arr4.min())
print("Max value is ", arr4.max())

# How to compute row-wise and col-wise mean? Use np.amin()
print("Row wise min is ", np.amin(arr4, axis=0))
print("Col wise min is ", np.amin(arr4, axis=1))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Oct 20 21:10:01 2018

@author: Thiago Almeida
"""

import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import numpy as np
import math as m

#imports locais
from Utils import Utils
from interpoladores.Lagrange import Lagrange
from interpoladores.Linear import Linear
from interpoladores.Newton import Newton

#funcao exata
def F(x):
    #return m.cos(x)
    return 1.0 / (1.0 + 25.0 * x**2)

#gera pontos para letra A
def gerarPontos_a(a, b, n):
    xk = np.zeros((n+1,), dtype=np.float128)

    for k in range (0,n+1):
        xk[k] = a + ((b-a)*k)/n

    return xk

#gera pontos para letra B
def gerarPontos_b(a, b, n):
    xk = np.zeros((n+1,), dtype=np.float128)

    for k in range (0,n+1):
        xk[k] = ((a+b) / 2.0) - ((b-a) / 2.0) * m.cos(float(k)/n * m.pi)

    return xk

#gera pontos para letra C
def gerarPontos_c(a, b, n):
    xk = np.zeros((n+1,), dtype=np.float128)

    for k in range (0,n+1):
        xk[k] = -1.0 + (2.0 * k) / float(n)

    return xk

##### Geradores de graficos #####

def gerarGrafico(tempo, solucao_aproximada, solucao_exata, metodo, ordem):

    #print("len tempo: ", len(tempo))
    #print("len solucao_aproximada: ", len(solucao_aproximada))
    #print("len solucao_exata: ", len(solucao_exata))

     #plota grafico da função
    plt.plot(
        tempo, solucao_exata, 'b--',
        tempo, solucao_aproximada, 'r-'
        )
    #plt.ylabel(u"Valor de f(x), P(x)") #esse 'u' antes da string é pra converter o texto pra unicode
    #plt.xlabel(u"Valor de x, " + str(len(tempo)) + u" partições")
    plt.xlabel(str(len(tempo)) + u" partições, N = " + str(ordem))

    #legendas do grafico
    se_line = mlines.Line2D([], [], color='blue', marker='', markersize=0, label=u'Sol Exata')
    ac_line = mlines.Line2D([], [], color='red', marker='', markersize=0, label=u'Met. ' + metodo)

    plt.legend(handles=[se_line, ac_line])

    plt.title(u"Metodo "+metodo+u" x Solução exata", )

    #plt.axis([0, 50, 0, 100])
    plt.show()

def gerarGraficoErro(tempo, solucao_aproximada, solucao_exata, metodo, ordem):

    #print("len tempo: ", len(tempo))
    #print("len solucao_aproximada: ", len(solucao_aproximada))
    #print("len solucao_exata: ", len(solucao_exata))

    arr_erro = np.zeros((len(tempo),), dtype=np.float128)

    #gera array com a comparacao do erro
    for k in range (0, num_particoes):
        arr_erro[k] = abs(solucao_aproximada[k] - solucao_exata[k])

     #plota grafico da função
    plt.plot(
        tempo, arr_erro, 'b--'
        )
    #plt.ylabel(u"Erro f(x) - P(x)") #esse 'u' antes da string é pra converter o texto pra unicode
    #plt.xlabel(u"Valor de x, " + str(len(tempo)) + u" partições")
    plt.xlabel(str(len(tempo)) + u" partições, N = " + str(ordem))

    #legendas do grafico
    se_line = mlines.Line2D([], [], color='blue', marker='', markersize=0, label=u'Erro')

    plt.legend(handles=[se_line])

    plt.title(u"Erro relativo do metodo "+metodo)

    #plt.axis([0, 50, 0, 100])
    plt.show()

#configura grafico
num_particoes = 100
N = 6
a = -1.0
b = 1.0

#gera os pontos pra interpolacao
pontos = gerarPontos_a(a, b, N)

#inicia os arrays do numpy com 128bits de precisao
particoes = np.zeros((num_particoes,), dtype=np.float128)
sol_exata = np.zeros((num_particoes,), dtype=np.float128)
sol_aprox = np.zeros((num_particoes,), dtype=np.float128)

#gera array com a solucao exata e particoes do grafico
dp = (b-a) / num_particoes
for k in range (0, num_particoes):
    x = a + dp * k
    particoes[k] = x
    sol_exata[k] = F(x)

#gera array com pontos pra interpolacao
num_pontos = len(pontos)
y = np.zeros((num_pontos,), dtype=np.float128)
for k in range (0, num_pontos):
    y[k] = F(pontos[k])

'''
print(particoes)
print(pontos)
print(y)
'''

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

#inicio = Utils().getTime()
#Newton().interpolacao(pontos, vy);
coeficientes = Newton().interpolacaoSlide(num_pontos, pontos, y);

#gera array com a solucao aproximada
for k in range (0, num_particoes):
    #sol_aprox[k] = Newton().avaliarPonto(particoes[k])
    sol_aprox[k] = Newton().avaliarPontoSlide(num_pontos, particoes[k], pontos, coeficientes)

#fim  = Utils().getTime()
#Utils().imprimeDiferencaTempo(inicio, fim)
erroNorma = Utils().distanciaMaximo(sol_exata, sol_aprox)
print("Erro (Norma do Maximo): " + repr(erroNorma))

gerarGrafico(particoes, sol_aprox, sol_exata, "Newton", N)
gerarGraficoErro(particoes, sol_aprox, sol_exata, "Newton", N)
'''

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

#inicio = Utils().getTime()

#gera array com a solucao aproximada
for k in range (0, num_particoes):
    sol_aprox[k] = Lagrange().interpolacao(num_pontos, pontos, y, particoes[k]);

#fim  = Utils().getTime()
#Utils().imprimeDiferencaTempo(inicio, fim)
erroNorma = Utils().distanciaMaximo(sol_exata, sol_aprox)
print("Erro (Norma do Maximo): " + repr(erroNorma))

gerarGrafico(particoes, sol_aprox, sol_exata, "Lagrange", N)
gerarGraficoErro(particoes, sol_aprox, sol_exata, "Lagrange", N)

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

#inicio = Utils().getTime()

#gera array com a solucao aproximada
for k in range (0, num_particoes):
    sol_aprox[k] = Linear().interpolacao(num_pontos, pontos, y, particoes[k]);

#fim  = Utils().getTime()
#Utils().imprimeDiferencaTempo(inicio, fim)
erroNorma = Utils().distanciaMaximo(sol_exata, sol_aprox)
print("Erro (Norma do Maximo): " + repr(erroNorma))

gerarGrafico(particoes, sol_aprox, sol_exata, "Linear", N)
gerarGraficoErro(particoes, sol_aprox, sol_exata, "Linear", N)


# daub.py
## This is my implementation of daub.m
## Computation of Daubechies' filter coefficients (cepstrum method)
## using Python libraries numpy, scipy
##
## The main reference that I'll use is
## Gilbert Strang, and Kevin Amaratunga. 18.327 Wavelets, Filter Banks and Applications, Spring 2003. (Massachusetts Institute of Technology: MIT OpenCourseWare), http://ocw.mit.edu (Accessed 19 Jun, 2015). License: Creative Commons BY-NC-SA
## daub.py dovetails with Handout 8 and I explain it in my notes On 18.327 Wavelets pdf.
##
##
#####################################################################################
## Copyleft 2015, Ernest Yeung <ernestyalumni@gmail.com>
##
## 20150630
##
## This program, along with all its code, is free software;
## you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You can have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software Foundation, Inc.,
## S1 Franklin Street, Fifth Floor, Boston, MA
## 02110-1301, USA
##
## Governing the ethics of using this program, I default to the Caltech Honor Code:
## ``No member of the Caltech community shall take unfair advantage of
## any other member of the Caltech community.''
##
## If you like what I'm doing and would like to help and contribute support,
## please take a look at my crowdfunding campaign at ernestyalumni.tilt.com
## and subscription-based Patreon
## read my mission statement and give your financial support,
## no matter how small or large,
## if you can
## and to keep checking my ernestyalumni.wordpress.com blog and
## various social media channels
## for updates as I try to keep putting out great stuff.
##
## Fund Science! Help my physics education outreach and research efforts at
## Open/Tilt or subscription Patreon - Ernest Yeung
##
## ernestyalumni.tilt.com
##
## Facebook     : ernestyalumni
## gmail        : ernestyalumni
## google       : ernestyalumni
## linkedin     : ernestyalumni
## Patreon      : ernestyalumni
## Tilt/Open    : ernestyalumni
## tumblr       : ernestyalumni
## twitter      : ernestyalumni
## youtube      : ernestyalumni
## wordpress    : ernestyalumni
##
##
################################################################################
##
##
##
##
##

import numpy as np
import scipy

# much credit should go to Kevin Amaratunga for writing the daub.m Matlab file implementing
# Daubeschies orthogonal wavelets first, of which this Python file is based upon.
# I hope that translating daub.m to Python, and the documentation (see my wordpress.com blog and look for the Wavelets pdf write up on 18.327) I'm continually doing will help
# extend the its use

def daub(Nh):

    """
    function h = daub(Nh)

    Generate filter coefficients for the Daubechies orthogonal wavelets.

    Kevin Amaratunga
    9 December, 1994.

    h = filter coefficients of Daubechies orthonormal compactly supported
        wavelets
    Nh = length of filter.

    EY:20150630 I've implemented daub.m in Python with numpy, scipy ernestyalumni.wordpress.com
    """

    K = Nh/2
    L = Nh/2.  # EY : 20150630 you need L to be a float to match the results of daub.m
    N = 512 # Use a 512 point FFT by default
    k = np.array(range(N))

    # Determine samples of the z transform of Mz (= Mz1 Mz2) on the unit circle.
    # Mz2 = z.^L .* ((1 + z.^(-1)) / 2).^(2*L); # or in Python numpy
    # Mz2 = np.multiply( np.power(z, L) , np.power( (( 1+ np.power(z,-1))/2.) , (2*L) ) )

    z = np.exp( 2j*np.pi*k/N)
    tmp1 = (1 + np.power( z,-1 ) )/2
    tmp2 = (-z + 2 - np.power(z,-1) )/4 # sin^2(w/2)

    Mz1 = np.zeros(N)
    vec = np.ones(N)
    for l in range(K):
        # Mz1 = Mz1 + binomial(L+l-1,l) * tmp2.^l;
        Mz1 = Mz1 + vec
        vec = np.multiply( vec, tmp2*(L+l)/(l+1) )  # you need L to be a float to match the results of daub.m

    Mz1 = 4 * Mz1

    # Mz1 has no zeros on the unit circle, so use the complex cepstrum to find
    # its minimum phase spectral factor

    Mz1hat = np.log(Mz1)
    m1hat = np.fft.ifft( Mz1hat )     # Real cepstrum of np.fft.ifft(Mz1). (= cmplx
                                      # cepstrum since Mz1 real, +ve.)
    m1hat[N/2:N] = np.zeros(N/2)  # Retain just the causal part.
    m1hat[0] = m1hat[0]/2             # Value at zero is shared between
                                      # the causal and anticausal part.
    G = np.exp(np.fft.fft(m1hat,N))   # Min phase spectral factor of Mz1

    # Mz2 has zeros on the unit circle, but its minimum phase spectral factor
    # is just tmp1.^L i.e. np.power( tmp1, L )
    Hz = np.multiply( G, np.power( tmp1, L ) )  # you need L to be a float to match the results of daub.m
    h = np.real( np.fft.ifft(Hz) ) # there are problems here with precision

# EY : 20150630 there is a difference of precision between GNU Octave and Python numpy
# cf. http://stackoverflow.com/questions/8717099/ifft-in-matlab-and-numpy-give-different-results
# This might be promising for increasing speed and making grids, including numba
# cf. https://jakevdp.github.io/blog/2015/02/24/optimizing-python-with-numpy-and-numba/

    h = np.vstack( h[0:Nh] )
    return h


from engine.functions.anomaly_computation import compute_eccentric_anomaly
from sympy import acos, atan, cos, pi, sin, sqrt, tan
from sympy.utilities.lambdify import implemented_function

def TrueAnomalyAtT(true_anomaly_at_epoch, eccentricity, period, t):
    eccentric_anomaly_at_epoch = EccentricAnomalyFromTrueAnomaly(
        true_anomaly_at_epoch, eccentricity
    )
    mean_anomaly_at_epoch = MeanAnomalyFromEccentricAnomaly(
        eccentric_anomaly_at_epoch, eccentricity
    )

    mean_anomaly = MeanAnomalyAtT(mean_anomaly_at_epoch, period, t)

    eccentric_anomaly = EccentricAnomalyFromMeanAnomaly(mean_anomaly, eccentricity)
    return TrueAnomalyFromEccentricAnomaly(eccentric_anomaly, eccentricity)

def EccentricAnomalyFromTrueAnomaly(true_anomaly, eccentricity):
    return acos(
        (eccentricity + cos(true_anomaly)) / (1 + eccentricity * cos(true_anomaly))
    )

def MeanAnomalyFromEccentricAnomaly(eccentric_anomaly, eccentricity):
    return eccentric_anomaly - eccentricity * sin(eccentric_anomaly)

def MeanAnomalyAtT(mean_anomaly_at_epoch, period, t):
    return mean_anomaly_at_epoch + (2 * pi / period) * t

EccentricAnomalyFromMeanAnomaly = implemented_function(
    "Ecc",
    lambda mean_anomaly, eccentricity: compute_eccentric_anomaly(
        float(mean_anomaly), float(eccentricity)
    ),
)

def TrueAnomalyFromEccentricAnomaly(eccentric_anomaly, eccentricity):
    return 2 * atan(
        sqrt((1 + eccentricity) / (1 - eccentricity)) * tan(eccentric_anomaly / 2)
    )


from math import atan
import numpy as np

class YawController(object):
    def __init__(self, wheel_base, steer_ratio, min_speed, max_lat_accel, max_steer_angle, kp):
        self.wheel_base = wheel_base
        self.steer_ratio = steer_ratio
        self.min_speed = min_speed
        self.max_lat_accel = max_lat_accel

        self.min_angle = -max_steer_angle
        self.max_angle = max_steer_angle

        self.kp = kp

    def get_angle(self, radius):
        angle = atan(self.wheel_base / radius) * self.steer_ratio
        return max(self.min_angle, min(self.max_angle, angle))

    def get_steering(self, linear_velocity, angular_velocity, current_velocity):
        angular_velocity = current_velocity * angular_velocity / linear_velocity if abs(linear_velocity) > 0. else 0.

        if abs(current_velocity) > 0.1:
            max_yaw_rate = abs(self.max_lat_accel / current_velocity);
            angular_velocity = max(-max_yaw_rate, min(max_yaw_rate, angular_velocity))

        return self.get_angle(max(current_velocity, self.min_speed) / angular_velocity) if abs(angular_velocity) > 0. else 0.0;

    def get_steering_P(self, pose, p1, p2):
        # this use closest pt and second closest pt to form a line and calculate
        # offset of the current position and time kp
        # https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
        d = np.cross(p2-p1, p1-pose) / np.linalg.norm(p2-p1)
        angle = self.kp * d
        return  max(self.min_angle, min(self.max_angle, angle))


import matplotlib.pyplot as plt
import numpy as np
from scipy.fftpack import fft, fftshift

plt.figure(1, figsize=(9.5, 7))
M = 64
N = 64
x1 = np.hanning(M)
x2 = np.cos(2*np.pi*2/M*np.arange(M))
y1 = x1*x2
mY1 = 20 * np.log10(np.abs(fftshift(fft(y1, N))))

plt.subplot(3,2,1)
plt.title('x1 (hanning)')
plt.plot(np.arange(-M/2, M/2), x1, 'b', lw=1.5)
plt.axis([-M/2,M/2,0,1])

plt.subplot(3,2,2)
plt.title('x2 (cosine)')
plt.plot(np.arange(-M/2, M/2),x2, 'b', lw=1.5)
plt.axis([-M/2,M/2,-1,1])

mX1 = 20 * np.log10(np.abs(fftshift(fft(x1, M)))/M)
plt.subplot(3,2,3)
plt.title('X1')
plt.plot(np.arange(-N/2, N/2),mX1, 'r', lw=1.5)
plt.axis([-N/2,N/2,-80,max(mX1)])

mX2 = 20 * np.log10(np.abs(fftshift(fft(x2, M)))/M)
plt.subplot(3,2,4)
plt.title('X2')
plt.plot(np.arange(-N/2, N/2),mX2, 'r', lw=1.5)
plt.axis([-N/2,N/2,-80,max(mX2)])

plt.subplot(3,2,5)
plt.title('DFT(x1 x x2)')
plt.plot(np.arange(-N/2, N/2),mY1, 'r', lw=1.5)
plt.axis([-N/2,N/2,-80,max(mY1)])

Y2 = np.convolve(fftshift(fft(x1, M)), fftshift(fft(x2, M)))
mY2 = 20 * np.log10(np.abs(Y2)) - 40

plt.subplot(3,2,6)
plt.title('X1 * X2')
plt.plot(np.arange(-N//2, N//2),mY2[M//2:M+M//2], 'r', lw=1.5)
plt.axis([-N/2,N/2,-80,max(mY2)])

plt.tight_layout()
plt.savefig('convolution-2.png')
plt.show()


import numpy as np

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

# define derivative of sigmoid
def sigmoid_prime(x):
    return sigmoid(x) * (1-sigmoid(x))

# weight initialization
weight = np.array([-0.8, 0.5])
# input
x = np.array([0.1, 0.3])
# output
y = 0.2
# learning rate
learning_rate = 0.5

# linear combination
h = np.dot(x, weight)

# neural network output (y_hat)
y_hat = sigmoid(h)

# error (y - y_hat)
error = y - y_hat

# error term
error_term = error * sigmoid_prime(h)

# gradient descent step
del_w = learning_rate * error_term * x

print(del_w)

### Expect output
[-0.0039638  -0.01189141]
###


import numpy as np

def MakeFromPolar(r: float, theta: float, phi: float) -> np.ndarray:
    """
    Convert from polar coordinates to cartesian coordinates
    """
    # get sine and cosine of the angles
    ct = np.cos(theta)
    st = np.sin(theta)
    cp = np.cos(phi)
    sp = np.sin(phi)
    # and convert to cartesian coordinates
    x = r * st * cp
    y = r * st * sp
    z = r * ct
    # create the array
    return np.array([x, y, z])

def buildBeams(N: int) -> np.ndarray:
    """
    Builds a sphere of unit vectors with N beams in the xy plane.
    """
    N = int(np.floor(np.sqrt(int(N) * np.pi + 0.5)))

    # start with an empty list
    beams = []

    # iterate over the whole angle in the xy plane
    for i in range(N):
        # get the Nth part of the total rotation
        theta = (2 * np.pi / N) * i

        # estimate how many vectors we need to cover the phi angle (for the z direction)
        jmax = int(np.floor(N * np.sin(theta) + 0.5))

        # iterate over those angles to get beams in every direction
        for j in range(jmax):
            # get the phi angle
            phi = (2 * np.pi / jmax) * j

            # and create a unit vector from the polar coordinates theta and phi
            beams.append(MakeFromPolar(1.0, theta, phi))

    # return all the vectors
    return np.array(beams)

def saveBeams(beams: np.ndarray, fname: str):
    np.savetxt(fname, beams)


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

v1 = np.mat([1, 2, 3])
v2 = np.mat([5, 7, 9])

print('v1 = {0}, v2 = {1}'.format(v1, v2))

def manhattan(x1, x2):
    return np.sum(np.abs(x1 - x2))

def euclidean(x1, x2):
    v = x1 - x2
    return np.sqrt(np.dot(v, v.T))

def chebyshev(x1, x2):
    return np.max(np.abs(x1 - x2))

print('manhattan = {0}\neuclidean = {1}\nchebyshev = {2}'.format(manhattan(v1, v2), euclidean(v1, v2).item(0), chebyshev(v1, v2)))


import numpy as np
def std_deviation(arr_x,arr_y):
    # arr_y = np.zeros((len(data)))
    # arr_x = np.zeros((len(data)))
    # for i in range(len(data)):
    #    arr_x[i] = data[i][0]
    #    arr_y[i] = data[i][1]
    mean_x = np.mean(arr_x)
    mean_y = np.mean(arr_y)
    std_x = np.std(arr_x)
    std_y = np.std(arr_y)
    print (len(arr_x), len(arr_y))
    data_filter_x = []
    data_filter_y = []

    for i in range(len(arr_x)):
        if arr_x[i] > mean_x - 1.5 * std_x and \
           arr_x[i] < mean_x + 1.5 * std_x:
            data_filter_x.append(arr_x[i])
    for i in range(len(arr_y)):
        if arr_y[i] > mean_y - 1.5 * std_y and \
           arr_y[i] < mean_y + 1.5 * std_y:
            data_filter_y.append(arr_y[i])
                # data_filter.append([arr_x[i], arr_y[i]])
    # print (data_filter_x, data_filter_y)
    print (std_x, std_y)
    print (mean_x, mean_y)
    print (len(data_filter_x), len(data_filter_y))
    # print (data_filter)
    right_eye = (np.array(data_filter_x))
    left_eye = (np.array(data_filter_y))
    print(np.std(left_eye), np.std(right_eye))
    left_mean = np.mean(left_eye)
    right_mean = np.mean(right_eye)
    print(left_mean, right_mean)
    return left_mean - right_mean


import pandas
import numpy as np

class CalmarRatioIndicator(object):
    """ Calmar Ratio of a trading strategy KPI """
    KPIdf: np.float64

    def __init__(self, a_df: pandas.DataFrame):
        self.__setIndicator(a_df)

    def __setIndicator(self, a_df: pandas.DataFrame):
        """function to calculate Calmar Ratio"""
        df: pandas.DataFrame = a_df.copy()
        self.KPIdf = self.__getCagr(df) / self.__getMaxDrawDown(df)

    @staticmethod
    def __getCagr(a_df: pandas.DataFrame):
        """function to calculate the Cumulative Annual Growth Rate of a trading strategy"""
        df: pandas.DataFrame = a_df.copy()
        df['DailyReturn'] = a_df['Adj Close'].pct_change()
        df['CumulativeReturn'] = (1 + df['DailyReturn']).cumprod()
        annual_length = len(df) / 252
        return (df['CumulativeReturn'][-1]) ** (1 / annual_length) - 1

    @staticmethod
    def __getMaxDrawDown(a_df: pandas.DataFrame):
        """function to calculate the Maximum Draw Down of a trading strategy"""
        df: pandas.DataFrame = a_df.copy()
        df['DailyReturn'] = a_df['Adj Close'].pct_change()
        df['CumulativeReturn'] = (1 + df['DailyReturn']).cumprod()
        df['CumulativeRollMax'] = df['CumulativeReturn'].cummax()
        df['DrawDown'] = df['CumulativeRollMax'] - df['CumulativeReturn']
        df['DrawDownPercent'] = df['DrawDown'] / df['CumulativeRollMax']
        return df['DrawDownPercent'].max()


from numpy.linalg import cond
from scipy import zeros
from scipy.interpolate import CubicSpline
from scipy.linalg import solve
from gauss import gauss
from roots_legendre_interval import roots_legendre_interval

def integral_equation(K, f, a, b, n = 5, use_python_libs = False):
    x, w = roots_legendre_interval(n, a, b, use_python_libs=use_python_libs)
    matrix = zeros((n, n + 1))
    for i in range(n):
        for j in range(n):
            matrix[i, j] = w[j] * K(x[i], x[j])
        matrix[i, n] = f(x[i])
    y = solve(matrix[:, 0: -1], matrix[:, -1]) if use_python_libs else gauss(matrix)
    assert len(x) == len(y), f'len(x) = {len(x)}, len(y) = {len(y)}'
    result = CubicSpline(x, y)
    return result, cond(matrix)


import numpy as np
import numpy.matlib
from scipy.integrate import quad
from scipy.integrate import simps

def lagrange_k(x, nodes, k):
    nodes_but = np.delete(nodes,k)
    D = x-nodes_but[:,None]
    N = nodes[k]-nodes_but
    l_k = np.prod(D/N[:,None], axis = 0 )
    return l_k

def quadrature_weights(nodes, interval, mapping = None):
    N_nodes = len(nodes)
    a,b = interval
    if mapping is None:
        mapping = lambda x: x
    lagr = lambda y,k: lagrange_k(mapping(y),mapping(nodes),k)
    weights = [ quad( lagr, a, b, epsabs = 1.e-9, limit=250, args=(k,))[0] for k in range(N_nodes) ]
    #x = np.linspace( a, b, 10*N_nodes+1 )
    #weights = [ simps(lagr(x,k), x) for k in range(N_nodes) ]
    return np.array(weights)


 ## Solve test problem HS15 from the Hock & Schittkowski collection.
 #
 #  min   100 (x2 - x1^2)^2 + (1 - x1)^2
 #  s.t.  x1 x2 >= 1
 #        x1 + x2^2 >= 0
 #        x1 <= 0.5
 #
 #  The standard start point (-2, 1) usually converges to the standard
 #  minimum at (0.5, 2.0), with final objective = 306.5.
 #  Sometimes the solver converges to another local minimum
 #  at (-0.79212, -1.26243), with final objective = 360.4.
 ##
from __future__ import print_function

import numpy
import argparse
from pyoptsparse import Optimization, OPT
parser = argparse.ArgumentParser()
parser.add_argument("--opt", help="optimizer", type=str, default='SLSQP')
parser.add_argument("--storeHistory", help="option to store history",type=int, default=0)
args = parser.parse_args()
optOptions = {}

def objfunc(xdict):
    x = xdict['xvars']
    funcs = {}
    funcs['obj'] = [100*(x[1] - x[0]**2)**2 + (1-x[0])**2]
    conval = numpy.zeros(2, 'D')
    conval[0] = x[0]*x[1]
    conval[1] = x[0] + x[1]**2
    funcs['con'] = conval
    fail = False

    return funcs, fail

def sens(xdict, funcs):
    x = xdict['xvars']
    funcsSens = {}
    funcsSens['obj', 'xvars'] = [2*100*(x[1]-x[0]**2)*(-2*x[0]) - 2*(1-x[0]),
                                  2*100*(x[1]-x[0]**2)]
    funcsSens['con', 'xvars'] = [[x[1], x[0]], [1, 2*x[1]]]

    fail = False

    return funcsSens, fail

# Optimization Object
optProb = Optimization('HS15 Constraint Problem', objfunc)

# Design Variables
lower = [-5, -5]
upper = [0.5,  5]
value = [-2, 1]
optProb.addVarGroup('xvars', 2, lower=lower, upper=upper, value=value)

# Constraints
lower = [1, 0]
upper = [None, None]
optProb.addConGroup('con', 2, lower=lower, upper=upper)

# Objective
optProb.addObj('obj')

# Check optimization problem:
print(optProb)

# Optimizer
opt = OPT(args.opt, options=optOptions)

# Solution
if args.storeHistory:
    histFileName = '%s_hs015_Hist.hst'%(args.opt.lower())
else:
    histFileName = None
# end
sol = opt(optProb, sens=sens,storeHistory=histFileName)

# Check Solution
print(sol)


import numpy as np

import time
import util as u

# simulation parameters
dt = 0.1       # time step
T = 3000       # number of time steps

# parameters of the system
gamma = .00002 # gravitational coefficient
m1 = 300       # mass of the first planet
m2 = 1         # mass of the second planet

# initial conditions: state of the system in the first two time steps
x0 = np.array([0, 0, 1, 0])
x1 = np.array([0, 0, 1, 0.005])

# initializing the vector of system states
xs = [x0, x1]

# defining the function that calculates the forces acting on the planets at a time step
def F(x):
    r1 = x[:2]   # position of the first planet
    r2 = x[2:4]  # position of the second planet

    posdiff_vec = r2 - r1                 # vector of difference of the positions
    dist = np.linalg.norm(posdiff_vec)    # distance between the planets

    # Newton's law of universal gravitation
    F1 = gamma * m2 * posdiff_vec / (dist**2)    # force acting on the first planet
    F2 = - gamma * m1 * posdiff_vec / (dist**2)  # force acting on the second planet

    return np.concatenate([F1, F2], axis=0)

for i in range(T):
    # getting previous values of the iteration
    Lx = xs[-1]
    LLx = xs[-2]

    # approximating the second derivative with finite differences
    F_prev = F(Lx)                  # force acting at the previous time step
    x = dt**2*F_prev + 2*Lx - LLx   # estimated position at the next time step

    xs.append(x)

####################################################

x, y = np.split(np.array(xs), 2, axis=1)

u.plotAnim(x, y, T, isSaveVideo=False)

# this is slow and not working properly
#u.plotScatter(x, y)


import numpy as np
from scipy import stats

class GaussianMixtureModel():
    """Density estimation with Gaussian Mixture Models (GMM).

    You can add new functions if you find it useful, but **do not** change
    the names or argument lists of the functions provided.
    """
    def __init__(self, X, K):
        """Initialise GMM class.

        Arguments:
          X -- data, N x D array
          K -- number of mixture components, int
        """
        self.X = X
        self.n = X.shape[0]
        self.D = X.shape[1]
        self.K = K

    def E_step(self, mu, S, pi):
        """Compute the E step of the EM algorithm.

        Arguments:
          mu -- component means, K x D array
          S -- component covariances, K x D x D array
          pi -- component weights, K x 1 array

        Returns:
          r_new -- updated component responsabilities, N x K array
        """
        # Assert that all arguments have the right shape
        assert(mu.shape == (self.K, self.D) and\
               S.shape  == (self.K, self.D, self.D) and\
               pi.shape == (self.K, 1))
        r_new = np.zeros((self.n, self.K))

        # Task 1: implement the E step and return updated responsabilities
        # Write your code from here...
        for i in range(self.n):
            som=0
            for k in range(self.K):
                som+=pi[k]*stats.multivariate_normal.pdf(self.X[i,:],mu[k,:],S[k,:,:])
            for k in range(self.K):
                r_new[i,k]=pi[k]*stats.multivariate_normal.pdf(self.X[i,:],mu[k,:],S[k,:,:])/som

        # ... to here.
        assert(r_new.shape == (self.n, self.K))
        return r_new

    def M_step(self, mu, r):
        """Compute the M step of the EM algorithm.

        Arguments:
          mu -- previous component means, K x D array
          r -- previous component responsabilities,  N x K array

        Returns:
          mu_new -- updated component means, K x D array
          S_new -- updated component covariances, K x D x D array
          pi_new -- updated component weights, K x 1 array
        """
        assert(mu.shape == (self.K, self.D) and\
               r.shape  == (self.n, self.K))
        mu_new = np.zeros((self.K, self.D))
        S_new  = np.zeros((self.K, self.D, self.D))
        pi_new = np.zeros((self.K, 1))

        # Task 2: implement the M step and return updated mixture parameters
        # Write your code from here...
        for k in range(self.K):
            Nk=0
            som1=np.zeros((self.K, self.D))
            som2=np.zeros((self.K, self.D, self.D))
            for i in range(self.n):
                Nk+=r[i,k]
                som1[k,:]+=r[i,k]*self.X[i,:]
            mu_new[k,:]=som1[k,:]/Nk
            for i in range(self.n):
                C = np.reshape(self.X[i,:]-mu_new[k,:], (-1,1))
                som2[k]+=r[i,k]*(C @ C.T)
            S_new[k,:]=som2[k,:,:]/Nk
            pi_new[k]=Nk/self.n

        # ... to here.
        assert(mu_new.shape == (self.K, self.D) and\
               S_new.shape  == (self.K, self.D, self.D) and\
               pi_new.shape == (self.K, 1))
        return mu_new, S_new, pi_new

    def negloglikelihood(self, mu, S, pi):
        """Compute the E step of the EM algorithm.

        Arguments:
          mu -- component means, K x D array
          S -- component covariances, K x D x D array
          pi -- component weights, K x 1 array

        Returns:
          nlogl -- negative log-likelihood, 1x 1 array
        """
        # Assert that all arguments have the right shape
        assert(mu.shape == (self.K, self.D) and\
               S.shape  == (self.K, self.D, self.D) and\
               pi.shape == (self.K, 1))
        nlogl= 0
        l=0
        for i in range(self.n):
            for k in range(self.K):
                l+=pi[k]*stats.multivariate_normal.pdf(self.X[i,:],mu[k,:],S[k,:,:])
            nlogl+=(np.log(l))[0]
        return -nlogl

    def train(self, initial_params):
        """Fit a Gaussian Mixture Model (GMM) to the data in matrix X.

        Arguments:
          initial_params -- dictionary with fields 'mu', 'S', 'pi' and 'K'

        Returns:
          mu -- component means, K x D array
          S -- component covariances, K x D x D array
          pi -- component weights, K x 1 array
          r -- component responsabilities, N x K array
        """
        # Assert that initial_params has all the necessary fields
        assert(all([k in initial_params for k in ['mu', 'S', 'pi']]))

        mu = np.zeros((self.K, self.D))
        S  = np.zeros((self.K, self.D, self.D))
        pi = np.zeros((self.K, 1))
        r  = np.zeros((self.n, self.K))

        # Task 3: implement the EM loop to train the GMM
        # Write your code from here...
        l0 =0
        l1 =0
        mu = initial_params["mu"]
        S = initial_params["S"]
        pi = initial_params["pi"]

        maxstep=100
        precision=1e-6

        r  = self.E_step( mu, S, pi)
        mu1, S1, pi1 =self.M_step(mu, r)
        l0  = self.negloglikelihood( mu, S, pi)
        l1  =  self.negloglikelihood( mu1 , S1, pi1)
        actual_precision = np.abs(l0-l1)
        nstep = 0

        while(actual_precision > precision or nstep < maxstep):
            r  = self.E_step(mu, S, pi)
            mu1, S1, pi1 = self.M_step(mu, r)
            l0  =  self.negloglikelihood(mu, S, pi)
            l1  =  self.negloglikelihood(mu1 , S1, pi1)
            actual_precision = l0-l1
            mu = mu1
            S  = S1
            pi = pi1
            nstep +=1

        # ... to here.
        assert(mu.shape == (self.K, self.D) and\
               S.shape  == (self.K, self.D, self.D) and\
               pi.shape == (self.K, 1) and\
               r.shape  == (self.n, self.K))
        return mu, S, pi, r

if __name__ == '__main__':
    np.random.seed(43)

    ##########################
    # You can put your tests here
    #
    #
    #
    #
    ##########################


import numpy as np

#
#Here is a general solution to the largest palindrome problem. The input is two arrays with the range of values we want to find the largest palindrome for:
#For our example, x = [100, 999], y = [100, 999]
#
def large_palin(x, y):
    pals = []
    z = np.arange(x[0], x[1] + 1)
    t = np.arange(y[0], y[1] + 1)
    for i in z:
        for j in t:
            a = str(i*j)
            if a == a[::-1]:
                pals.append(int(a))
    return 'The largest palindromic number is ' + str(np.max(pals))

#
# Alternatively, here is the direct solution which will print upon typing `python dskinner_04.py`
#
z = np.arange(100, 999 + 1)
t = np.arange(100, 999 + 1)
pals = []
for i in z:
    for j in t:
        a = str(i*j)
        if a == a[::-1]:
            pals.append(int(a))
print('The largest palindromic number is ' + str(np.max(pals)))


import matplotlib.pyplot as plt
import numpy as np
import integral_calculation as ic

def vel_field_plot(vinf, alpha, x_pan, y_pan, phi, panel_len, lambda_list):

    # Grid Parameters
    x_pts = 30
    y_pts = 30
    x_range = [-0.3, 1.5]
    y_range = [-0.4, 0.6]

    # Generate Grid
    x_grid = np.linspace(x_range[0], x_range[1], x_pts)
    y_grid = np.linspace(y_range[0], y_range[1], y_pts)
    X, Y = np.meshgrid(x_grid, y_grid)

    # Initialize velocities
    u_vel = np.zeros([len(x_grid), len(y_grid)])
    v_vel = np.zeros([len(x_grid), len(y_grid)])
    tot_vel = np.zeros([len(x_grid), len(y_grid)])

    # Compute panel's number
    panel_num = len(x_pan) - 1

    # For loop to compute velocities in each point
    for x in range(len(x_grid)):
        for y in range(len(y_grid)):

            # Compute integrals for 1 point
            # Mx, My = ic.compute_Mpj(x_pan, y_pan, X[x, y], Y[x, y], phi, panel_len)
            Mx, My = teste.STREAMLINE_SPM(X[x,y],Y[x,y],x_pan,y_pan,phi,panel_len)
            mx_sum = 0
            my_sum = 0

            # Sum integral's values for each panel
            for j in range(panel_num):
                mx_sum += (lambda_list[j]/(2*np.pi)) * Mx[j]
                my_sum += (lambda_list[j]/(2*np.pi)) * My[j]

            # Compute velocities
            u_vel[x,y] = (vinf * np.cos(alpha)) + mx_sum
            v_vel[x,y] = (vinf * np.sin(alpha)) + my_sum
            tot_vel[x,y] = (u_vel[x,y]**2 + v_vel[x,y]**2) ** 0.5

    ##%% Plotting

    # Plot airfoil
    plt.fill(x_pan, y_pan, 'grey')
    plt.axis('equal')

    # Plot total velocity vector field
    contf = plt.contourf(X, Y, tot_vel, levels=np.linspace(0, 1.6*vinf ,100), extend='both')
    cbar = plt.colorbar(contf,orientation='horizontal',shrink=0.5, pad = 0.1)
    cbar.set_label('Velocity', fontsize=16)
    plt.title('Contour of velocity field', fontsize=16);
    plt.show()

def vel_streamlines(vinf, alpha, x_pan, y_pan, phi, panel_len, lambda_list):

    # Grid Parameters
    x_pts = 40
    y_pts = 40
    x_range = [-0.3, 1.5]
    y_range = [-0.4, 0.6]

    # Generate Grid
    x_grid = np.linspace(x_range[0], x_range[1], x_pts)
    y_grid = np.linspace(y_range[0], y_range[1], y_pts)
    X, Y = np.meshgrid(x_grid, y_grid)

    # Initialize velocities
    u_vel = np.zeros([len(x_grid), len(y_grid)])
    v_vel = np.zeros([len(x_grid), len(y_grid)])

    # Compute panel's number
    panel_num = len(x_pan) - 1

    # For loop to compute velocities in each point
    for x in range(len(x_grid)):
        for y in range(len(y_grid)):

            # Compute integrals for 1 point
            Mx, My = ic.compute_Mpj(x_pan, y_pan, X[x, y], Y[x, y], phi, panel_len)
            mx_sum = 0
            my_sum = 0

            # Sum integral's values for each panel
            for j in range(panel_num):
                mx_sum += (lambda_list[j]/(2*np.pi)) * Mx[j]
                my_sum += (lambda_list[j]/(2*np.pi)) * My[j]

            # Compute velocities
            u_vel[x,y] = (vinf * np.cos(alpha)) + mx_sum
            v_vel[x,y] = (vinf * np.sin(alpha)) + my_sum

    print(u_vel)
    # Plot airfoil
    plt.fill(x_pan, y_pan, 'grey')
    plt.axis('equal')
    plt.quiver(X, Y,u_vel, v_vel)
    plt.show()

def panel_vis(x_pan, y_pan, xc, yc, panel_len, phi):

    # Compute panel's angle with x axis
    delta = phi + np.pi/2

   # Create list with normal points
    panel_num = len(x_pan) - 1
    norm_pts_1 = list()
    norm_pts_2 = list()

   # Compute points to plot panel's normals
    for j in range(panel_num):
        x1 = xc[j]
        y1 = yc[j]

        x2 = xc[j] + panel_len[j]*np.cos(delta[j])
        y2 = yc[j] + panel_len[j]*np.sin(delta[j])

        norm_pts_1.append([x1, x2])
        norm_pts_2.append([y1, y2])

    ##%% PLOTTING

    #Plot airfoil
    plt.plot(x_pan, y_pan, 'black', linewidth=0.75)

    #Plot Control points
    plt.scatter(list(xc), list(yc))

    #Plot normals
    for n in range(panel_num):
        x_val = norm_pts_1[n]
        y_val = norm_pts_2[n]
        plt.plot(x_val, y_val, 'grey', linewidth=0.75)

    plt.axis('equal')
    plt.show()


from FuncDesigner import *
from numpy import ones
a, b = oovars('a', 'b')
a_infsup = (-ones(3), [1, 2, 3]) # Python list or tuple with 2 elements LowerBound, UpperBound
b_infsup = (2, 50.5)
f1 = 2 * a
f2 = b + 15
f = f1 + f2

f_interval = f.interval({a: a_infsup, b: b_infsup})
print(f_interval.lb, f_interval.ub) # (array([ 15.,  15.,  15.]), array([ 67.5,  69.5,  71.5]))

# for some fixed coords (b) and some interval coords (a):
f_interval = f.interval({a: a_infsup, b: -15})
print(f_interval.lb, f_interval.ub) # (array([-2., -2., -2.]), array([ 2.,  4.,  6.]))
print(f_interval) # "FuncDesigner interval with lower bound [-2. -2. -2.] and upper bound [ 2.  4.  6.]"

f = sin(b) + cos(b+0.15) + a/100
f_interval = f.interval({a: 1000, b: (0.1, 3.14/2)}) # fixed a = 1000, b from -0.1 to 3.14/2
print(f_interval.lb, f_interval.ub) # (9.951182716375465, 11.96891210464248)


'''**********************************************
CODE TO IMPLEMENT FISHER'S LDA -
Given two dimensional dataset with two classes 0 and 1,
Perform Fisher's LDA on the dataset,
Perform dimensionality reduction and find the suitable vector to project it onto,
Find the threshold value for separation of the two classes
***********************************************'''
import numpy as np
import matplotlib.pyplot as plt
import time

#  to calculate the execution time of th clustering
start_time = time.time()

# reading data csv file
my_data = np.genfromtxt('datasets/dataset_3.csv', delimiter=',')

# deleting the serial number column
data=np.delete(my_data,0,1)

# separating the two classes and deleting the target variable column
class0 = data[np.nonzero(data[:,2] == 0)]
class1=data[np.nonzero(data[:,2]==1)]
class0=np.delete(class0,2,1)
class1=np.delete(class1,2,1)

# finding the mean of the the two classes
mean0=np.mean(class0,0)
mean1=np.mean(class1,0)

''' calculating the variability of the two classes using the formula :
    variability=summation over points belonging to class 1((xi-mean)(xi-mean)tanspose)
'''
var0=np.zeros(1)
temp=np.array(mean0)
for i in range (class0.shape[0]) :
    temp=(class0[i,:]-mean0)
    var0+=np.dot(temp, temp.T)
var1=np.zeros(1)
temp=np.array(mean1)
for i in range (class1.shape[0]) :
    temp=(class1[i,:]-mean1)
    var1+=np.dot(temp, temp.T)
sw=var1+var0

# calculating the inverse of Sw matrix
invsw=np.array([(1/sw[0])])

# calculating the w vector using below formula
w=invsw*(mean1-mean0)

# declaring arrays for storing points' distance from the vector
dist0=np.zeros((class0.shape[0],1))
dist1=np.zeros((class1.shape[0],1))

# finding the the vector to project the points on;
# such that the means are farthest from each other
wperp=np.array([-w[1],w[0]])

# finding the norm of the w vector
norm_w=np.linalg.norm(wperp)

''' calculating the distance of original data points from the vector using the formula:
    r=w.T/norm(w)
'''
for i in range(dist0.shape[0]):
    dist0[i]=np.dot(wperp.T,class0[i,:])/norm_w
for i in range(dist1.shape[0]):
    dist1[i]=np.dot(wperp.T,class1[i,:])/norm_w

''' declaring the arrays to store the projected points data using formula:
    x_projected = x_actual-r*w/norm(w)
'''
class0proj=np.zeros((class0.shape[0],2))
class1proj=np.zeros((class1.shape[0],2))
for i in range(class0.shape[0]):
    class0proj[i,:]=np.subtract((class0[i,:]),(dist0[i]*wperp.T/norm_w))
for i in range(class1.shape[0]):
    class1proj[i,:]=np.subtract((class1[i,:]),(dist1[i]*wperp.T/norm_w))

# displaying the plot with the original data , projected points and line
plt.scatter(class0[:,0],class0[:,1])
plt.scatter(class1[:,0],class1[:,1])

plt.scatter(class0proj[:,0],class0proj[:,1],color='blue')
plt.scatter(class1proj[:,0],class1proj[:,1],color='red')

#concatenating the two classes into a single array
pointsproj=np.concatenate((class0proj,class1proj),axis=0)
plt.plot(pointsproj[:,0],pointsproj[:,1],'m')

# storing dimensionally reduced projected points in array using formula:
#  y(x) = w.T*x
newproj0=np.zeros((class0.shape[0],1))
newproj1=np.zeros((class1.shape[0],1))

for i in range(class0.shape[0]):
    newproj0[i,:]=np.dot(wperp.T,class0[i,:])
for i in range(class1.shape[0]):
    newproj1[i,:]=np.dot(wperp.T,class1[i,:])

# storing the means and standard deviations of the projected points
proj0mean=np.mean(newproj0)
proj1mean=np.mean(newproj1)

proj0std=np.std(newproj0)
proj1std=np.std(newproj1)

'''
 Below function "solve" to finds the threshold value separating the two
 classes when dimensionally reduced -
 input : m1, m2 - means of the two classes whose point of intersection needs to be found
         std1, std2 - the standard deviations of the two classes
'''
def solve(m1,m2,std1,std2):
    a = 1/(2*std1**2) - 1/(2*std2**2)
    b = m2/(std2**2) - m1/(std1**2)
    c = m1**2 /(2*std1**2) - m2**2 / (2*std2**2) - np.log(std2/std1)
    roots= np.roots([a,b,c])

    # since two possible points of intersection , we select the one which lies in between the two means
    if roots.shape[0]>1:
        for i in range(2):
            if roots[i] !=max(m1,m2,roots[i]) or roots[i]!=min(m1,m2,roots[i]):
                return roots[i]
    else:
        return roots

threshold=solve(proj0mean,proj1mean,proj0std,proj1std)

print("Threshold value =", threshold)
print("Time taken = ",(time.time()-start_time))
plt.savefig('Results/Result3.png')


import numpy as np

def sigmaAB(A, uA, B, uB, op='*'):
    '''
    Calculates the uncertainty of two parameters with uncorrolated
    errors propogated through either multiplication or division.

    For f = A * B   or   f = A / B, find sigma_f

    Parameters:

        A, B    - array-like (assumed to be 1D arrays) parameters used in the
                  function 'f'.  Must be same size or broadcastable.

        uA, uB  - array-like (assumed constants or 1D arrays) of the uncertainty
                  associated with each measurement of the parameter A or B.
                  Must be same size as or broadcastable to associated parameter
                  (uA to A; uB to B)

        op      - 'Operation'  Either '*' or '/' for if
                  f = A * B
                  or   f = A / B

    Returns:
        'uf' array-like (assumed to be 1D array) of the propogated uncertainty
        of the function 'f' in units of the function 'f' such that 'F = f +/- uf'

    See https://en.wikipedia.org/wiki/Propagation_of_uncertainty for more resources

    (c) ajr - 06.05.2020
    Apache 2.0 - See github.com/unh-mms-rogers/mms-curvature
    '''

    uAB = np.sqrt(np.power(np.divide(uA,A), 2) + np.power(np.divide(uB,B),2))

    if op == '*': f = np.multiply(A,B)
    elif op == '/': f = np.divide(A,B)
    else:
        print("Invalid operator.  Must be '*' or '/'")
        return None

    return uAB, np.abs(np.multiply(uAB, f))

# end


import scipy.stats as st
import math

# Confidence Interval
def confIntrv(signLevel, mean, sd, n):
    alpha = ((signLevel/100) - 1) * (-1)
    alphaOver2 = alpha / 2
    print(alphaOver2)
    criticalV = round(st.norm.ppf(1-alphaOver2), 2)
    marginE = criticalV * sd / math.sqrt(n)
    return (mean - marginE, mean + marginE)

# Two Sample T-test
# Degree of freedom when standard error is given
def find_df_se(m, n, se1, se2):
    top = ((se1**2) + (se2**2))**2
    bot = ((se1**4)/(m-1)) + ((se2**4)/(n-1))
    return top/bot

# ANOVA
def calAnova(sample2D):

    xidot = []
    xidotbar = []
    xdotdot = 0
    for i in sample2D:
        accu = 0
        for j in i:
            accu += j
        xidot.append(accu)
        xidotbar.append(accu/len(i))
        xdotdot += accu/len(i)
    xdotdot = xdotdot / len(sample2D)

    # SSTr
    sstr = 0
    for i in xidotbar:
        sstr += (i - xdotdot)**2
    sstr = sstr * len(sample2D[0])

    # SSE
    sse = 0
    for i in range(len(sample2D)):
        for j in range(len(sample2D[i])):
            sse += (sample2D[i][j] - xidotbar[i]) ** 2

    # SST
    sst = 0
    for i in sample2D:
        for j in i:
            sst += (j - xdotdot) ** 2

    print("Xi. = ", xidot)
    print("Xi.Bar = ", xidotbar)
    print("X.. = ", xdotdot)
    print("SSTr = ", sstr)
    print("SSE = ", sse)
    print("SST = ", sst)

# Estimating model parameter
def find_Sxx_yy(yArr):
    sumYisqr = 0
    for i in yArr:
        sumYisqr += i**2
    sumYi = 0
    for i in yArr:
        sumYi += i
    return sumYisqr - (sumYi**2)/len(yArr)

def find_Sxy(xArr, yArr):
    sumXiYi = 0
    sumXi = 0
    sumYi = 0
    for i in range(len(xArr)):
        sumXiYi += xArr[i] * yArr[i]
        sumXi += xArr[i]
        sumYi += yArr[i]
    return sumXiYi - (sumXi * sumYi) / len(xArr)

def find_SSE(xArr, yArr):
    slope = find_Sxy(xArr, yArr) / find_Sxx_yy(xArr)

    muY = 0
    muX = 0
    for i in range(len(yArr)):
        muY += yArr[i]
        muX += xArr[i]
    muY = muY / len(yArr)
    muX = muX / len(xArr)

    yintrc = muY - slope * muX

    sum = 0

    for i in range(len(xArr)):
        yihat = yintrc + slope * xArr[i]
        sum += (yArr[i] - yihat)**2

    return sum

# Calculate R-Square for linear model
def find_rsqr(xArr, yArr):
    sse = find_SSE(xArr, yArr)
    sst = find_Sxx_yy(yArr)
    return 1 - sse / sst


import numpy as np
from copy import deepcopy as dc
from numba import njit

### Warning: generating this 3D pseudofractal can be really slow
### Dimns: dimension limits to the pseudofractal generator
### Params: equation parameters that change the final shape
### Maxiter: the number of calculation iterations

@njit
def mandelbrot(x, y, z, x0, y0, z0, maxiter, params):
    A, B, C, D = params
    for n in range(maxiter + 1):
        if abs(x - x0) + abs(y - y0) + abs(z - z0) > 2:
            return n
        x = x**5 - 10*(x**3)*(y**2 + A*y*z + z**2) + 5*x*(y**4 + B*(y**3)*z + C*(y**2)*(z**2) + B*y*(z**3) + z**4) + D*(x**2)*y*z*(y+z) + x0
        y = y**5 - 10*(y**3)*(x**2 + A*x*z + z**2) + 5*y*(z**4 + B*(z**3)*x + C*(z**2)*(x**2) + B*z*(x**3) + x**4) + D*(y**2)*z*x*(z+x) + y0
        z = z**3 - 10*(z**3)*(x**2 + A*x*y + y**2) + 5*z*(x**4 + B*(x**3)*y + C*(x**2)*(y**2) + B*x*(y**3) + y**4) + D*(z**2)*x*y*(x+y) + z0
    return 0

# Draw our image
def mandelbrot_set(xmin, xmax, ymin, ymax, zmin, zmax, w, h, d, maxiter, params):
    r1 = np.linspace(xmin, xmax, w)
    r2 = np.linspace(ymin, ymax, h)
    r3 = np.linspace(zmin, zmax, d)
    n3 = np.empty((w, h, d))
    for i in range(w):
        for j in range(h):
            for k in range(d):
                x, y, z = r1[i], r2[j], r3[k]
                x0, y0, z0 = dc(x), dc(y), dc(z)
                n3[i,j, k] = mandelbrot(x, y, z, x0, y0, z0, maxiter, params)
    return (r1, r2, r3, n3)

def mandelbrot_image(dims, params, width = 20, height = 20, depth = 20, maxiter=64):
    xmin, xmax, ymin, ymax, zmin, zmax = dims
    dpi = 50
    img_width = dpi * width
    img_height = dpi * height
    img_depth = dpi * depth
    _, _, _, frac = mandelbrot_set(xmin, xmax, ymin, ymax, zmin, zmax, img_width, img_height, img_depth, maxiter, params)
    return frac

if __name__ == '__main__':
    dims = (-2, 2, -2, 2, -2, 2)
    params = (1, 0, 1, 0)
    frac = mandelbrot_image(dims, params)


from math import sqrt

import numpy as np

def est_sigma(hist, tau="day"):
    if tau == "day":
        tau = 1 / 252

    n = len(hist) - 1

    u = np.log(hist[1:n + 1] / hist[0:n])

    s = np.std(u) * sqrt(n) / sqrt(n - 1)

    sigma = s / sqrt(tau)

    return sigma


import numpy as np

class LogisticRegression:
    def __init__(self,lr=0.001,thr=1e-2,max_epoch=600): #学习率和阈值
        self.lr = lr
        self.thr = thr
        self.max_epoch = max_epoch
        self.best_loss = np.inf
        self.best_theta = None
        self.losses_process = []

    def _predict(self,X,theta):
        sigmoid = lambda x: 1/(1+np.exp(-x)) if x>=0 else np.exp(x)/(1+np.exp(x))
        pX = np.ones([X.shape[0],X.shape[1]+1])
        pX[:,:-1] = X
        preds = [sigmoid(i) for i in pX@theta.ravel()]
        return np.array(preds).reshape(-1,1)

    def predict(self,X):
        preds = self._predict(X,self.best_theta).ravel()
        preds[preds<=0.5] = 0
        preds[preds>0.5] = 1
        return preds

    def score(self,X,y):
        return np.sum(self.predict(X)==y) / len(y)

    def fit(self,X,y):
        y = y.reshape(-1,1)
        pX = np.ones([X.shape[0],X.shape[1]+1])
        pX[:,:-1] = X

        #构造theta
        theta = np.random.randn(X.shape[-1]+1).reshape(-1,1)
        #开始训练
        epoch = 0
        while True:
            #计算loss
            loss = -1/X.shape[0]*np.squeeze(y.T@np.log(self._predict(X,theta)+1e-6) + (1-y).T@np.log(1-self._predict(X,theta)+1e-6))
            self.losses_process.append(loss)
            if loss<self.best_loss:
                self.best_loss = loss
                self.best_theta = theta
#             print("第{}epoch,loss为{}".format(epoch,loss))
            #计算梯度
            grad = pX.T@(self._predict(X,theta)-y)
            #是否收敛
            if np.sum(np.abs(grad)) < self.thr or epoch>self.max_epoch: break
            #梯度下降
            theta -= self.lr * grad
            epoch += 1

if __name__ == "__main__":
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection  import train_test_split
    import matplotlib.pyplot as plt

    breast_cancer = load_breast_cancer()
    X = breast_cancer.data
    y = breast_cancer.target
    #数据标准化
    X_std = (X-X.mean(axis=0)) / X.std(axis=0)
    #划分为训练集和测试集
    X_train,X_test,y_train,y_test = train_test_split(X,y)

    #逻辑回归训练
    lr = LogisticRegression(max_epoch=1000)
    lr.fit(X_train,y_train)
    print(f"train score {lr.score(X_train,y_train):.2f}")
    print(f"test  score {lr.score(X_test, y_test):.2f}")

    plt.plot(lr.losses_process)


import numpy as np
from enum import Enum

class CrossSectionTypes(Enum):
    RECT = 1
    CIRC = 2
    I = 3

class CrossSection(object):
    def __init__(self, crossSectionType, dims=None):
        """
        `crossSectionType` - a CrossSectionType object (CIRC, RECT, I, ...)

        `dims`  - CrossSection dimensions in form of an array/tuple:
            RECT    -   dims (width, height)

            CIRC    -   dims (radius)

            I       -   dims (beamWidth, beamHeight, flangeThickness, webThickness)
        """
        if not crossSectionType in set(cst for cst in CrossSectionTypes):
            raise Exception(f"Invalid crossSectionType: {crossSectionType}")

        self.CrossSectionType = crossSectionType

        if not dims or len(dims) == 0:
            raise Exception(f"Cannot create CrossSection {crossSectionType} without any dimensions")

        self.Dims = dims

    def getArea(self):
        if self.CrossSectionType == CrossSectionTypes.RECT:
            return self.Dims[0] * self.Dims[1]
        if self.CrossSectionType == CrossSectionTypes.CIRC:
            return np.pi * self.Dims[0]**2
        if self.CrossSectionType == CrossSectionTypes.I:
            # 2 x Flanges + Web
            return 2 * self.Dims[0] * self.Dims[2] + (self.Dims[1] - 2 * self.Dims[2]) * self.Dims[3]

    def getI(self):
        if self.CrossSectionType == CrossSectionTypes.RECT:
            return (1 / 12) * self.Dims[0] * self.Dims[1] **3
        if self.CrossSectionType == CrossSectionTypes.CIRC:
            return (np.pi / 4) * self.Dims[0] ** 4
        if self.CrossSectionType == CrossSectionTypes.I:
            wH = self.Dims[1] - 2 * self.Dims[2]  # inner web height
            iW = (1 / 12) * wH  # web inertia
            iF = (1 / 12) * (wH * self.Dims[2]**3) / 12 + (1 / 4) * self.Dims[2] * self.Dims[0] * self.Dims[3]**2  # flange inertia
            return iW + 2 * iF

    def getPlotPoints(self, xOffset, yOffset, zOffset, n=20):
        """
        returns n x,y,z points about the perimeter of the CrossSection
        """
        x, y, z = [], [], []
        if self.CrossSectionType == CrossSectionTypes.RECT:
            # TODO
            pass
        if self.CrossSectionType == CrossSectionTypes.CIRC:
            r = self.Dims[0]
            ith = 2 * np.pi / n  # radian fraction
            for i in range(0, n):
                x.append(xOffset)
                y.append(zOffset + np.cos(ith * i) * r)
                z.append(yOffset + np.sin(ith * i) * r)
            return x, y, z
        if self.CrossSectionType == CrossSectionTypes.I:
            # TODO
            pass


from sympy import *
from time import time
from mpmath import radians
import tf

## Insert IK code here!

### Your FK code here
# Create symbols
q1, q2, q3, q4, q5, q6, q7 = symbols('q1:8')   #theta_i
d1, d2, d3, d4, d5, d6, d7 = symbols('d1:8')
a0, a1, a2, a3, a4, a5, a6 = symbols('a0:7')
alpha0, alpha1, alpha2, alpha3, alpha4, alpha5, alpha6 = symbols('alpha0:7')

# Create Modified DH parameters
dh = {alpha0:      0,    a0:      0,   d1:  0.75,
      alpha1: -pi/2.,    a1:   0.35,   d2:     0,   q2:q2-pi/2,
      alpha2:      0,    a2:   1.25,   d3:     0,
      alpha3: -pi/2.,    a3: -0.054,   d4:   1.5,
      alpha4:  pi/2.,    a4:      0,   d5:     0,
      alpha5: -pi/2.,    a5:      0,   d6:     0,
      alpha6:      0,    a6:      0,   d7: 0.303,   q7:0}
#
# Define Modified DH Transformation matrix
def TF_matrix(alpha, a, d, q):
    TF = Matrix([[           cos(q),           -sin(q),           0,             a],
                 [sin(q)*cos(alpha), cos(q)*cos(alpha), -sin(alpha), -sin(alpha)*d],
                 [sin(q)*sin(alpha), cos(q)*sin(alpha),  cos(alpha),  cos(alpha)*d],
                 [                0,                 0,           0,             1]])
    return TF

#
# Create individual transformation matrices
T0_1 = TF_matrix(alpha0, a0, d1, q1).subs(dh)
T1_2 = TF_matrix(alpha1, a1, d2, q2).subs(dh)
T2_3 = TF_matrix(alpha2, a2, d3, q3).subs(dh)
T3_4 = TF_matrix(alpha3, a3, d4, q4).subs(dh)
T4_5 = TF_matrix(alpha4, a4, d5, q5).subs(dh)
T5_6 = TF_matrix(alpha5, a5, d6, q6).subs(dh)
T6_G = TF_matrix(alpha6, a6, d7, q7).subs(dh)

T0_G = T0_1 * T1_2 * T2_3 * T3_4 * T4_5 * T5_6 * T6_G

print(T0_G.evalf(subs={q1:-0.65, q2:0.45, q3:-0.36, q4:0.95, q5:0.79, q6:0.49}))


from numpy import matrix, dot
from scipy.linalg import solve
from itertools import chain
from typing import List

# generator matrix  the lattice you want to analyze
# possible array cases (0,2(r+1)), (r+1,r+1)), (2r+ 1,1),(r,r+2) for r in N
lattice_matrix = matrix('0 6; 3 3')

# possible isometries coordinates in 2-dimensional lattices
coordinates_isometry_matrix = [
    '1 0; 0 1', '-1 0; 0 -1', '-1 0; 0 1',
    '-1 0; 0 1', '1 0; 0 -1', '0 1; 1 0',
    '0 -1; -1 0', '0 -1; 1 0', '0 1; -1 0'
]

# isometries in matrix form
all_isometry_matrix = [
    matrix(isometry)
    for isometry in coordinates_isometry_matrix
]

# all new lattices generated from isometries
def list_newlattices(
    lattice_matrix: matrix, all_isometry_matrix: List[matrix]
) -> List[matrix]:

    return [
        dot(lattice_matrix, matrix)
        for matrix in all_isometry_matrix
    ]

# checks whether the lines of matrix 2 can be written as integer linear combinations
# of the lines of matrix 1
# here we solve all linear systems and then check if there is one that is not integer
def check_interger_solutions(
    first_matrix: matrix, second_matrix: matrix
) -> bool:

    _system_solutions = [
        solve(first_matrix, row.transpose()).tolist()
        for row in second_matrix
    ]

    _all_elements_solutions = list(chain(*_system_solutions))
    linear_system_solutions = list(chain(*_all_elements_solutions))

    for number in linear_system_solutions:
        if number // 1 != number:
            return False

    return True

# check if the lattices are equal from the generating matrix
def compare_lattice(first_matrix: matrix, second_matrix: matrix) -> bool:
    return (
        True if (
            check_interger_solutions(first_matrix, second_matrix)
            and check_interger_solutions(second_matrix, first_matrix)
        )
        else False
    )

# shows which lattices are the same as the initial and which are not
def show_equals(lattice_matrix: matrix, all_isometry_matrix: List[matrix]) -> None:

    all_lattices = list_newlattices(lattice_matrix, all_isometry_matrix)
    pattern_message = f'O Reticulado gerado pela isometria (phi)'

    for index, lattice in enumerate(all_lattices):
        if compare_lattice(lattice_matrix.transpose(), lattice):
            print(
                f'{pattern_message} {index} {lattice.tolist()} '
                f'é igual ao original {lattice_matrix.tolist()}.'
            )
        else:
            print(
                f'{pattern_message} {index} {lattice.tolist()} '
                f'é diferente do original {lattice_matrix.tolist()}.'
            )

    return None

show_equals(lattice_matrix, all_isometry_matrix)


import sympy as sp

print('Double integral computed by SymPy definite integrate')
print('Example 2-03 definite')
print('Integral of xye^-xy from y=1 to y=4 and from x=y-1 to x=y+1')

x, y = sp.symbols('x y')
f = x * y * sp.exp(-x) * sp.exp(-y)
ya = 0.
yb = 4.
xa = y-1.
xb = y+1.

integral = sp.integrate(f, (x, xa, xb), (y, ya, yb))
integral = integral.evalf()
print('Result is ', integral)


# %%
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
import seaborn as sns

# %%
# Differential equations

def S_dash(t, S, I, transm):
    return - transm * S * I

def I_dash(t, S, I, transm, recov):
    return transm*S*I - recov*I

def R_dash(t, S, I, recov):
    return recov*I

def SIR_model(t, y, transm, recov):
    S, I, R = y
    return [
        S_dash(t, S, I, transm),
        I_dash(t, S, I, transm, recov),
        R_dash(t, S, I, recov)
    ]

# %%
# Setup initial conditions
# Population size, N
population_size = 1
# Number of infected people at the start
infected_start = 0.001
# Number of susceptible people at the start
susceptible_start = population_size - infected_start
# Number of recovered
recovered_start = 0.

# %%
# Variables
basic_reproduction_number = 2.1
recovery_time = 2
recov_rate = recovery_time**-1
transm_rate = basic_reproduction_number * recov_rate / population_size
max_time = 24

# %%
sol = solve_ivp(
    fun=SIR_model,
    t_span=[0, max_time],
    y0=[susceptible_start, infected_start, recovered_start],
    args=(transm_rate, recov_rate),
    dense_output=True
)
t = np.linspace(0, max_time, 300)
y = sol.sol(t).T
labels = ['Susceptible', 'Infected', 'Recovered']
plt.plot(t, y)
plt.xlabel('Time')
plt.title(f'SIR Model $R_0$={basic_reproduction_number}')
for i, label in enumerate(labels):
    plt.text(t[-1] + 0.1, y[-1, i], label, color=f'C{i}')
plt.ylim(0, 1)
_, max_x = plt.xlim()
plt.xlim(0, max_x * 1.2)
sns.despine()
plt.show()


""" this script record useful functions """
import numpy as np

def cart2pol(point):
    """
    convert cartesian coordinate system to polar
    """
    x = point[0]
    y = point[1]
    rho = np.sqrt(x ** 2 + y ** 2)
    phi = np.arctan2(y, x)
    return 360 - (phi / np.pi *180 + 180)

def points_sort_by_clockwise(points_input):
    """ this function sort 2d points by clockwise order"""
    """ the image center is subtracted to move the coordinate system """
    ## convert to numpy array
    points_input = np.array(points_input)
    width, height = np.mean(points_input[:,0]), np.mean(points_input[:,1])
    ## flip y axis
    points_input[:,1] = height*2 - points_input[:,1]
    ## centering
    points_input_centered = points_input - np.array([width , height ])
    ## sort by angle to the x negative axis clockwise
    points_input_sorted = sorted(points_input_centered, key = cart2pol)
    ## convert to numpy array
    points_input_sorted = np.array(points_input_sorted)
    ## un centering
    points_input_sorted = points_input_sorted + np.array([width , height ])
    ## flip y axis
    points_input_sorted[:,1] = height*2 - points_input_sorted[:,1]
    return points_input_sorted.astype(int)

def points_maximum_inscribed_rectangle(points_input):
    """ output points from 1.top left -->2.top right-->3.bottom right-->4.bottom left """
    points_input_x_start = np.max([points_input[0][0], points_input[3][0]])
    points_input_x_end = np.min([points_input[1][0], points_input[2][0]])
    points_input_y_start = np.min([points_input[0][1], points_input[1][1]])
    points_input_y_end = np.max([points_input[2][1], points_input[3][1]])
    #points_input_bl =
    points_output = [[points_input_x_start, points_input_y_start], [points_input_x_end, points_input_y_start], [points_input_x_end, points_input_y_end], [points_input_x_start, points_input_y_end]]
    return np.array(points_output)


#Imports
import numpy as np
from numpy.fft import rfftn, irfftn #Importing built in nD Fast Fourier Transforms: rfft2 is nD real fast fourier transform, irfft2 is nD inverse real fast fourier transform

InputArray = np.zeros((100,100,100)) #Array to be fourier transformed
for i in range(100):
    for j in range(100):
        for k in range(100):
            InputArray[i,j,k] = i+j+k
print InputArray[10,10,10]

FT = rfftn(InputArray) #Fourier transform function takes array as input and outputs an array of 1/2 N +1 size. e.g. If InputArray is 100x100x100, FT will be 100x100x51. FT is array of coefficients of sinusoidal functions.
print FT[10,10,10]

#FT array can be manipulated e.g. Removing coefficients for smoothing

IFT = irfftn(FT) #Inverse fourier transform function takes array as input and outputs an array of 2(N-1) size e.g. If FT is 100x100x51, IFT will be 100x100x100
print IFT[10,10,10]

#An explicit Discrete Fourier Transform code is supplied in the slides, but is more complex and less efficient than the in built fast fourier transforms.


# re-implementation of k-means algorithm
# author Yan Shu

import numpy as np
import matplotlib.pyplot as plt

# task 1  separate 19 samples into two clusters
# input
x_list=[0,1,0,1,2,1,2,3,6,7,8,7,8,9,7,8,9,8,9]
y_list=[0,0,1,1,1,2,2,2,6,6,6,7,7,7,8,8,8,9,9]

sample=[]
for i in range(len(x_list)):
    x=x_list[i]
    y=y_list[i]
    num=np.array([[x,y]])
    print(num)
    sample.append(num)
"""
plt.scatter(x_list,y_list,marker='s',s=50)
for x, y in zip(x_list, y_list):
    plt.annotate('(%s,%s)'%(x,y), xy=(x,y),xytext = (0, -10),textcoords='offset points')
plt.show()
"""

K=2 # number of cluster
#initialize cluster center
u1=np.random.rand(1,2)
u2=np.random.rand(1,2)

flag=True
T=np.random.rand(1,19)

sum=0
last_temp=0
index1_list,index2_list=[],[]
while (flag == True):   #until yi is unvariable
    index1_list, index2_list = [], []
    sum=sum+1
    y_label=[]
    for i in range(len(sample)):
        distance1=np.linalg.norm(sample[i]-u1,2)
        distance2=np.linalg.norm(sample[i]-u2,2)
        distance=np.array([distance1,distance2])    # update yi

        y=np.argmin(distance)
        y_label.append(y)

    y_label=np.array(y_label)
    temp = np.linalg.norm(y_label - T, 2)
    if last_temp == temp:
        flag=False
    last_temp=temp

    index1=np.argwhere(y_label==0)
    index2=np.argwhere(y_label==1)
    index1_list.append(index1)
    index2_list.append(index2)

    # update u1,u2
    # calculate u1
    sum1=np.array([[0,0]])
    for i in range(len(index1)):
        index=index1[i][0]
        sum1=sum1+sample[index]
    u1=sum1/len(index1)
    print("u1",u1)

    # calculate u2
    sum2=np.array([[0,0]])
    for i in range(len(index2)):
        index=index2[i][0]
        sum2=sum2+sample[index]

    u2=sum2/len(index2)
    print("u2",u2)

cluster1x,cluster1y=[],[]
for i in  index1_list:
    for j in i:
        print(j[0])
        x=x_list[j[0]]
        cluster1x.append(x)
        y=y_list[j[0]]
        cluster1y.append(y)

cluster2x,cluster2y=[],[]
for i in  index2_list:
    for j in i:
        print(j[0])
        x=x_list[j[0]]
        cluster2x.append(x)
        y=y_list[j[0]]
        cluster2y.append(y)

plt.scatter(cluster1x,cluster1y,marker='s',s=50)
plt.scatter(cluster2x,cluster2y,marker='o',s=50)
plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 19 13:05:23 2019

@author: TempestGuerra
"""

import numpy as np
import math as mt
from HerfunChebNodesWeights import chebpolym, cheblb

def computeChebyshevDerivativeMatrix(DIMS):

       # Get data from DIMS
       ZH = DIMS[2]
       NZ = DIMS[4]

       # Initialize grid and make column vector
       xi, wcp = cheblb(NZ)

       # Get the Chebyshev transformation matrix
       CTD = chebpolym(NZ-1, -xi)

       # Make a diagonal matrix of weights
       W = np.diag(wcp)

       # Compute scaling for the forward transform
       S = np.eye(NZ)

       for ii in range(NZ - 1):
              temp = W.dot(CTD[:,ii])
              temp = ((CTD[:,ii]).T).dot(temp)
              S[ii,ii] = temp ** (-1)

       S[NZ-1,NZ-1] = 1.0 / mt.pi

       # Compute the spectral derivative coefficients
       SDIFF = np.zeros((NZ,NZ))
       SDIFF[NZ-2,NZ-1] = 2.0 * NZ

       for ii in reversed(range(NZ - 2)):
              A = 2.0 * (ii + 1)
              B = 1.0
              if ii > 0:
                     c = 1.0
              else:
                     c = 2.0

              SDIFF[ii,:] = B / c * SDIFF[ii+2,:]
              SDIFF[ii,ii+1] = A / c

       # Chebyshev spectral transform in matrix form
       temp = CTD.dot(W)
       STR_C = S.dot(temp);
       # Chebyshev spatial derivative based on spectral differentiation
       # Domain scale factor included here
       temp = (CTD).dot(SDIFF)
       DDM = - (2.0 / ZH) * temp.dot(STR_C);

       return DDM, STR_C


import numpy as np
import dykstra

# Define projection functions
def p_box(x,l,u):
    return np.minimum(np.maximum(x,l), u)

def p_ball(x,c,r):
    return c + (r/np.max([np.linalg.norm(x-c),r]))*(x-c)

def p_halfspace(x,a,b):
    return x - a*np.maximum(a.dot(x) - b,0)/a.dot(a)

# Generate projection functions
ball_c = [1,1]
ball_r = 2
pball = lambda x: p_ball(x,np.array(ball_c),ball_r)

box_l = np.array([-1,-1])
box_u = np.array([1,1])
pbox = lambda x: p_box(x,box_l,box_u)

a = [1,1]
bconst = 0.2
phalf = lambda x: p_halfspace(x,np.array(a),bconst)

# Use Dykstra's algorithm to project onto the intersection
#  np.random.seed(12)
start = np.random.uniform(low=-10,high=80, size=(2,))
x_pred = dykstra.project([pbox,pball,phalf], start, max_iter=10000, tol=1.0e-20)
print("Start:", start)
print("Pred:", x_pred)

if x_pred[0]+x_pred[1] <= 0.2 and (box_l[0] <= x_pred[0] <= box_u[0] and box_l[1] <= x_pred[1] <= box_u[1]) and ((x_pred[0]-1)**2 + (x_pred[1]-1)**2 <= 2**2):
    print("In constraint set")
else:
    # Print out constraints and values x_pred obtains to determine the error
    print(f'x+y = {x_pred[0]+x_pred[1]} (0.2), point: ({x_pred[0]},{x_pred[1]}), box upper: {box_u}, box lower: {box_l}, (x-1)^2+(y-1)^2 = {(x_pred[0]-1)**2 + (x_pred[1]-1)**2} (<=4)')


import GPy
import numpy as np
from dotdic import DotDic
from loader import save_to

def genData(Ntrain, Ntest, Mbuild, D, lengthscale, sig2_noise, sig2_0,lower,upper, seed=123):
    np.random.seed(seed)

    k = GPy.kern.RBF(input_dim=D,lengthscale=lengthscale)
    k.variance = sig2_0

    range_ = upper - lower

    RRm = np.random.rand(Mbuild,D) * range_ + lower
    Xtrain = np.random.rand(Ntrain,D) * range_ + lower
    Xtest = np.random.rand(Ntest,D) * range_ + lower

    mu = np.zeros((Mbuild)) # vector of the means
    C = k.K(RRm,RRm) # covariance matrix
    w = np.random.multivariate_normal(mu,C,1).transpose()

    iKrr = np.linalg.inv(C + np.eye(Mbuild)*1e-7)
    iKrr_w = np.dot(iKrr, w)

    Kxtr = k.K(Xtrain, RRm)
    Kxpr = k.K(Xtest, RRm)

    Ftrain = np.dot(Kxtr, iKrr_w)
    Ftest = np.dot(Kxpr, iKrr_w)

    Y_train = Ftrain + np.random.randn(Ntrain,1) * np.sqrt(sig2_noise)
    Y_test = Ftest + np.random.randn(Ntest,1) * np.sqrt(sig2_noise)

    return Xtrain, Xtest, Y_train, Y_test, Ftrain, Ftest

def save(D,lengthscale,variance,gaussian_noise,
         Ntrain=100000,Ntest=10000,M_build=2000,
         lower = -1,upper = 1,seed = 123):

    Xtrain, Xtest, Ytrain, Ytest, Ftrain, Ftest = \
        genData(Ntrain=Ntrain,
                Ntest=Ntest,
                Mbuild=M_build,
                D=D,
                lengthscale=lengthscale,
                sig2_0=variance,
                sig2_noise=gaussian_noise,
                lower=lower,
                upper=upper,
                seed = seed)

    data = {}
    data['Xtrain'] = Xtrain
    data['Xtest'] = Xtest
    data['Ytrain'] = Ytrain
    data['Ytest'] = Ytest
    data['Ftrain'] = Ftrain
    data['Ftest'] = Ftest

    data['lengthscale'] = np.repeat(lengthscale, D)
    data['variance'] = variance
    data['gaussian_noise'] = gaussian_noise

    data['lower'] = lower
    data['upper'] = upper
    data['D'] = D

    data = DotDic(data)

    name = "gendata_{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}".format(
        D,
        Ntrain,
        Ntest,
        lengthscale,
        variance,
        gaussian_noise,
        lower,
        upper)

    print("Saving {0}".format(name))
    save_to(obj=data,
            object_str='data',
            path='./data/{0}/'.format(name))


from numpy import sqrt, arctan, pi, exp, random, shape
I = 1.0j
# A package for beam optics in python

def spot_size(z, zR, w0):
    """Calculate spot size at z, given zR, and w0"""
    return w0 * sqrt(1 + (z/zR)*(z/zR))

def radius_curvature(z, zR):
    """calculate R(z)"""
    # This could be smarter, just adding epsilon to avoid nan's
    if (z == 0):
        z += 1e-31
    return z * (1 + (zR/z)*(zR/z))

def guoy_phase(z, zR):
    """really just atan(z/zR)"""
    return arctan(z/zR)

def rayleigh_range(w0, wavelambda):
    """Calculate rayleigh range"""
    return pi*w0*w0/wavelambda

def gaussian_beam(x, y, z, E0, wavelambda, w0, k):
    """full gaussian beam at x, y, z given the beam parameters\n
    E0 is the electric field amplitude
    wavelambda is the wavelength
    w0 is the beam waist (1/e field radius and 1/e^2 intensity radius)
    k is a tuple of [kx,ky,kz]"""

    r = sqrt(x*x + y*y)
    zR = rayleigh_range(w0, wavelambda)
    w = spot_size(z, zR, w0)
    R = radius_curvature(z, zR)
    eta = guoy_phase(z, zR)
    return E0 * w0/w * exp(- r*r/(w*w)) *\
        exp(-I*k[2]*z - I*k[2]*r*r/(2*R) + I*eta)*exp(I*k[0]*x + I*k[1]*y) #\
        #+ sqrt(E0)*random.random([max(shape(x)), max(shape(y))])
    # The exp(ikz) term in this definition causes extra phase accumulation
    # compared to the BPM. I need to sort this out for sure.
    # The following agrees with BPM:
    # return w0/w * exp(- r*r/(w*w)) * exp(- 1j*k*r*r/(2*R) + 1j*eta)

def plane_wave_beam(x, y, z, A, k):
    """a simple plane wave mostly used for testing"""
    #dphase = 1/(2*A**2)
    #noiseamp = sqrt(A) * (random.random() - 0.5)
    #noisephase = exp(2*pi*1j*dphase*(random.random() - 0.5))
    # noise = random.normal(0, 0.5) * exp(1j*2*pi*random.random())
    # return (A + noise) * exp(I*k[0]*x + I*k[1]*y + I*k[2]*z)
    return (A) * exp(I*k[0]*x + I*k[1]*y + I*k[2]*z)
    #return A * exp(I*k[0]*x + I*k[1]*y + I*k[2]*z)
    # this is more accurate, the best way to model is to let
    # the amplitude have noise.


import numpy as np

def angular_distance(ra1, dec1, ra2, dec2):
    """
    Returns the angular distance between two points, two sets of points, or a set of points and one point.

    :param ra1: array or float, longitude of first point(s)
    :param dec1: array or float, latitude of first point(s)
    :param ra2: array or float, longitude of second point(s)
    :param dec2: array or float, latitude of second point(s)
    :return: angular distance(s) in degrees
    """

    # Vincenty formula, slower than the Haversine formula in some cases, but stable also at antipodes

    lon1 = np.deg2rad(ra1)
    lat1 = np.deg2rad(dec1)
    lon2 = np.deg2rad(ra2)
    lat2 = np.deg2rad(dec2)

    sdlon = np.sin(lon2 - lon1)
    cdlon = np.cos(lon2 - lon1)
    slat1 = np.sin(lat1)
    slat2 = np.sin(lat2)
    clat1 = np.cos(lat1)
    clat2 = np.cos(lat2)

    num1 = clat2 * sdlon
    num2 = clat1 * slat2 - slat1 * clat2 * cdlon
    denominator = slat1 * slat2 + clat1 * clat2 * cdlon

    return np.rad2deg(np.arctan2(np.sqrt(num1 ** 2 + num2 ** 2), denominator))