"""
In this file we compare the performance of numerical methods to solve
differential equations describing first order systems.
These include the Euler and improved-Euler as well as the Runge-Kutta
methods.

We will apply them on the example of a simple first-order system described
by the differential equation

    x' = -x

which is simple enough to be able to derive the exact solution analytically
such that it can be used to assess the performance of the numerical method.
"""
import argparse
import numpy as np
import matplotlib.pyplot as plt

def setup_cli(args):
    """
    Setup the command line interface.
    """
    parser = argparse.ArgumentParser(description="Solve the differential equation x'=-x numerically.")
    parser.add_argument(
        "--t0",
        type=float,
        default=0.0,
        help="The starting time t0 at which the initial condition is defined. Default: t0=0.0",
    )
    parser.add_argument("--x0", type=float, default=1.0, help="The initial condition x(t=t0)=x0. Default: x0=1.0")
    parser.add_argument("--step", type=float, default=0.0001, help="The step size. Default: s=0.0001")
    parser.add_argument("--end", type=float, default=1.0, help="The end point in the time evolution. Default: end=1.0")
    return parser.parse_args()

def exact_solution(t, x0=1.0):
    """
    The implementation of the exact solution to the differential equation
    x' = -x
    with the initial condition x(t=0)=`x0` (default x0=1, as in the problem).
    """
    return x0 * np.exp(-t)

def euler_method(f, t0: float, x0: float, timestep: float, end: float, exact_solution=None):
    """
    Implementation of the euler method to numerically compute the solution
    to the differential equation

                x'=f(x)

    Parameters
    ----------
    f: function
        The implementation of the function `f` appearing in the differential
        equation.
    t0: float
        The initial time.
    x0: float
        The initial condition to the differential equation, i.e. the value
        of x(t=t0).
    timestep: float
        The timestep to employ for the numerical solution of the differential
        equation.
    end: float
        The maximal time step up to which to compute the the solution.
    exact_solution: function
        The exact solution. If the value is different from `None` the exact
        solution will
        be evaluated at each time step and the corresponding values will be
        returned in order
        to be able to check the convergence of the numerical solution.

    """
    if end < t0:
        raise ValueError("Initial time is larger than the end time!")

    # Store the time steps
    time_steps = [t0]
    # Store the value at each time step
    values = [x0]
    # Store the exact values of the solutions at each time step, if the exact
    # solution is provided
    if exact_solution:
        exact_values = [exact_solution(t0)]

    # Now start solving the differential equation numerically
    t = t0
    x = x0
    while t < end:
        t = t + timestep
        time_steps.append(t)
        x = x + f(x) * timestep
        values.append(x)
        if exact_solution:
            exact_values.append(exact_solution(t))

    return np.array(time_steps), np.array(values), None if not exact_solution else np.array(exact_values)

def improved_euler_method(f, t0: float, x0: float, timestep: float, end: float, exact_solution=None):
    """
    Implementation of the improved euler method to numerically compute the solution
    to the differential equation

                x'=f(x)

    Parameters
    ----------
    f: function
        The implementation of the function `f` appearing in the differential
        equation.
    t0: float
        The initial time.
    x0: float
        The initial condition to the differential equation, i.e. the value
        of x(t=t0).
    timestep: float
        The timestep to employ for the numerical solution of the differential
        equation.
    end: float
        The maximal time step up to which to compute the the solution.
    exact_solution: function
        The exact solution. If the value is different from `None` the exact
        solution will
        be evaluated at each time step and the corresponding values will be
        returned in order
        to be able to check the convergence of the numerical solution.

    """
    if end < t0:
        raise ValueError("Initial time is larger than the end time!")

    # Store the time steps
    time_steps = [t0]
    # Store the value at each time step
    values = [x0]
    # Store the exact values of the solutions at each time step, if the exact
    # solution is provided
    if exact_solution:
        exact_values = [exact_solution(t0)]

    # Now start solving the differential equation numerically
    t = t0
    x = x0
    while t < end:
        t = t + timestep
        time_steps.append(t)
        x_tilde = x + f(x) * timestep
        x = x + 0.5 * (f(x) + f(x_tilde)) * timestep
        values.append(x)
        if exact_solution:
            exact_values.append(exact_solution(t))

    return np.array(time_steps), np.array(values), None if not exact_solution else np.array(exact_values)

def runge_kutta_method(f, t0: float, x0: float, timestep: float, end: float, exact_solution=None):
    """
    Implementation of the Runge-Kutta method to numerically compute the solution
    to the differential equation

                x'=f(x)

    Parameters
    ----------
    f: function
        The implementation of the function `f` appearing in the differential
        equation.
    t0: float
        The initial time.
    x0: float
        The initial condition to the differential equation, i.e. the value
        of x(t=t0).
    timestep: float
        The timestep to employ for the numerical solution of the differential
        equation.
    end: float
        The maximal time step up to which to compute the the solution.
    exact_solution: function
        The exact solution. If the value is different from `None` the exact
        solution will
        be evaluated at each time step and the corresponding values will be
        returned in order
        to be able to check the convergence of the numerical solution.

    """
    if end < t0:
        raise ValueError("Initial time is larger than the end time!")

    # Store the time steps
    time_steps = [t0]
    # Store the value at each time step
    values = [x0]
    # Store the exact values of the solutions at each time step, if the exact
    # solution is provided
    if exact_solution:
        exact_values = [exact_solution(t0)]

    # Now start solving the differential equation numerically
    t = t0
    x = x0
    while t < end:
        t = t + timestep
        time_steps.append(t)

        k1 = f(x) * timestep
        k2 = f(x + 0.5 * k1) * timestep
        k3 = f(x + 0.5 * k2) * timestep
        k4 = f(x + k3) * timestep

        x = x + 1.0 / 6.0 * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
        values.append(x)

        if exact_solution:
            exact_values.append(exact_solution(t))

    return np.array(time_steps), np.array(values), None if not exact_solution else np.array(exact_values)

if __name__ == "__main__":
    # Imports
    import sys
    import logging

    # Setup the logger
    logging.basicConfig(level=logging.INFO)

    # Setup the command-line parser and extract the parameters
    args = setup_cli(sys.argv)
    x0 = args.x0
    t0 = args.t0
    stop = args.end
    step_size = args.step

    # Log the parameters that we employ
    logging.info(f"Using initial time      t0  = {t0}")
    logging.info(f"Using initial condition x0  = {x0}")
    logging.info(f"Using step size         s   = {step_size}")
    logging.info(f"Using end time          end = {stop}")

    # Compare the performances
    numerical_methods = {
        "Euler": euler_method,
        "Improved Euler": improved_euler_method,
        "Runge-Kutta": runge_kutta_method,
    }

    fig1, ax1 = plt.subplots()
    fig2, ax2 = plt.subplots()

    for label, f in numerical_methods.items():
        ts, vs, exact = f(lambda x: -x, t0, x0, step_size, stop, exact_solution)
        error = np.abs(vs - exact)
        ax1.plot(ts, vs, label=label.lower())
        ax2.plot(ts, error, label=label.lower())

    time_exact = np.linspace(t0, stop, 1000)
    vals_exact = exact_solution(time_exact)
    ax1.plot(time_exact, vals_exact, label="exact")

    # Cosmetics for the plot
    ax1.set_title(r"Solution to the first-order system $\dot{x}=-x$")
    ax1.set_xlabel(r"$t$")
    ax1.set_ylabel(r"$|x_{\mathrm{num}}(t) - x_{\mathrm{exact}}(t)|$")
    ax1.legend(loc="best")
    # ax1.set_yscale('log')
    # ax1.set_xscale('log')
    fig1.tight_layout()

    ax2.set_title(r"Error of the solution to the first-order system $\dot{x}=-x$")
    ax2.set_xlabel(r"$t$")
    ax2.set_ylabel(r"$|x_{\mathrm{num}}(t) - x_{\mathrm{exact}}(t)|$")
    ax2.legend(loc="best")
    ax2.set_yscale("log")
    ax2.set_xscale("log")
    fig2.tight_layout()
    plt.show()
    # fig1.savefig("compare_numerical_solvers.pdf")
    # fig2.savefig("compare_numerical_solvers_error.pdf")


"""
.. versionadded:: 0.3

This function simulates logistic map equation. This logisti map is described
by following equation

:math:`x(k+1) = r x(k) (1 - x(k))`,

where :math:`x` is population (between 0 and 1) and :math:`x` is parameter
denoted as value of interest.

The logistic map can result in logistic behaviour for some values of :math:`r`.

For more information see great page about
`logistic map on Wikipedia <https://en.wikipedia.org/wiki/Logistic_map>`_

Example Usage
===============

Simple example

.. code-block:: python

    N = 200 # number of samples
    x = signalz.logistic_map(N, r=3)

Function Documentation
======================================
"""
import numpy as np

def logistic_map(n, r=4, initial=0.5):
    """
    Logist map equation

    **Args:**

    * `n` - length of the output data (int) - how many samples will be on output

    **Kwargs:**

    * `r` - function parameter (float)

    * `initial` - initial value (float), default is 0.1,
      should be in range from 0 to 1

    **Returns:**

    * simulation output - vector of numbers between 0 and 1
    """
    x = np.zeros(n)
    x[0] = initial
    for k in range(n-1):
        x[k+1] = r*x[k]*(1-x[k])
    return x


import math
from numpy import zeros_like, random
from matplotlib import pyplot as plt

def statsnp(x):
  n = 0
  S = 0.0
  m = 0.0
  Sarr = zeros_like(x)
  Marr = zeros_like(x)
  for x_i in x:
    n += 1
    m_prev = m
    m += (x_i - m) / n
    S += (x_i - m) * (x_i - m_prev)
    Marr[n-1] = m
    Sarr[n-1] = S/n

  return Marr, Sarr

def stats(x):
  n = 0
  S = 0.0
  m = 0.0
  for x_i in x:
    n += 1
    m_prev = m
    m += (x_i - m) / n
    S += (x_i - m) * (x_i - m_prev)
  return {'mean': m, 'variance': S/n}

def naive_stats(x):
  S1 = sum(x)
  n = len(x)
  S2 = sum([x_i**2 for x_i in x])
  return {'mean': S1/n, 'variance': (S2/n - (S1/n)**2) }

x1 = [1,-1,2,3,0,4.02,5]
x2 = [x+1e9 for x in x1]

print("naive_stats:")
print(naive_stats(x1))
print(naive_stats(x2))

print("stats:")
print(stats(x1))
print(stats(x2))

print("\n\nx3:")
random.seed(7)
x3 = random.rand(10000)
print(naive_stats(x3))
print(stats(x3))
m, s = statsnp(x3)
plt.plot(s, label='std')
plt.plot(m, label='avg')
plt.legend()
plt.show()


"""
@author: Maneesh D
@date: 29-May-17
@intepreter: Python 3.6

Consider a random 10 x 2 matrix representing Cartesian coordinates, convert them to Polar coordinates.
"""
import math

import numpy

numpy.set_printoptions(precision=2)
cartesian_matrix = numpy.random.randint(0, 9, size=(10, 2))

print(cartesian_matrix.shape)
print("Cartesian Co-ordinate matrix:")
print(cartesian_matrix)

# Converting to polar co-ordinates
polar_matrix = numpy.empty(shape=cartesian_matrix.shape, dtype=float)
for i in range(cartesian_matrix.shape[0]):
    row_data = cartesian_matrix[i]
    r = ((row_data[0] ** 2) + (row_data[1] ** 2)) ** 0.5
    phi = math.atan2(row_data[1], row_data[0])
    polar_matrix[i] = [r, phi]

print("Polar co-ordinate matrix:")
print(polar_matrix)


import tensorflow as tf
import numpy as np
import random
import time
import matplotlib.pyplot as plt

def print_progress(it, acc, loss):
    # Calculate the accuracy on the training-set.
    now = time.strftime("%c")
    print("Iteration "+str(it)+" --- Accuracy: "+str(acc)+", Loss: "+str(loss)+" --- "+now);

def main():

    learning_rate = 0.001;
    max_iterations = 20000;

    # Load training and eval data
    train_x = np.linspace(0,10,50);
    train_y = 4*train_x + 1;
    train_y += np.random.uniform(low=-1,high=1,size=len(train_y));

    # Show training data
    plt.figure();
    plt.plot(train_x,train_y,'ro');
    plt.show();

    # -------------------------------------------------------------------
    # Building the computational graph
    tf.reset_default_graph();

    x = tf.placeholder(tf.float32, shape=len(train_x), name='x')
    y_true = tf.placeholder(tf.float32, shape=len(train_y), name='y_true')

    m = tf.Variable(np.random.randn(), name="m");
    tf.summary.scalar("m",m);
    b = tf.Variable(np.random.randn(), name="b");
    tf.summary.scalar("b", b);

    pred = tf.add(tf.multiply(m,x),b);

    loss = tf.losses.mean_squared_error(labels=y_true, predictions=pred)
    tf.summary.scalar("loss",loss);

    optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)

    merged_summary = tf.summary.merge_all();

    print "Graph correctly created!";
    # -------------------------------------------------------------------

    session = tf.Session();
    session.run(tf.global_variables_initializer())
    folder = "linear_regression";
    train_writer = tf.summary.FileWriter("logs/" + folder, session.graph);

    i = 0;

    feed_dict_train = {x: train_x, y_true: train_y}

    print "Starting training...";

    while i <= max_iterations:

        s,train_loss,_ = session.run([merged_summary,loss,optimizer], feed_dict=feed_dict_train)

        if i % 10 == 0:
            #
            now = time.strftime("%c")
            print("Iteration " + str(i) + " --- Loss: " + str(train_loss) + " --- " + now);
            train_writer.add_summary(s,i);

        i += 1;

    print "Training ended!";

    print "Learned parameters:";
    print "m = " + str(session.run(m));
    print "b = " + str(session.run(b));

    plt.plot(train_x, train_y, 'ro', label='Original data')
    plt.plot(train_x, session.run(pred,feed_dict={x: train_x}), label='Fitted line')
    plt.legend()
    plt.show()

    session.close();

if __name__ == "__main__":
    main();


"""
Spectral differentiation from J.A.C. Weideman and S.C. Reddy 1998, ACM TOMS.
"""

import numpy as N
import numpy.ma as ma

def herroots(n):
    """Returns the roots of the Hermite polynomial of degree n."""
    # Jacobi matrix
    J = N.diag(N.arange(1, n)**0.5, 1) + N.diag(N.arange(1, n)**0.5, -1)
    return N.sort(N.linalg.eigvalsh(J)) / (2**0.5)

def herdif(n, m, b):
    """Computes differentiation matrices D1, D2, ..., Dm on Hermite points.

    Args:
        n: Number of points, which is also the order of accuracy.
        m: Number of derivative matrices to return.
        b: Scaling parameter. Real and positive.

    Returns:
        x: Array of nodes, zeros of Hermite polynomial of degree n, scaled by b.
        Dm: A list s.t. Dm[i] is the (i+1)-th derivative matrix, i=0...m-1.

    Note: 0 < m < n-1.
    """
    x = herroots(n)
    # Compute weights
    alpha = N.exp(-x**2 / 2.)
    # Set up beta matrix s.t. beta[i,j] =
    #  ( (i+1)-th derivative of alpha(x) )/alpha(x), evaluated at x = x(j).
    beta = N.zeros((m+1, x.shape[0]))
    beta[0] = 1.0
    beta[1] = -x
    for i in range(2, m+1):
        beta[i] = -x * beta[i-1] - (i-1) * beta[i-2]
    # Remove initializing row from beta
    beta = N.delete(beta, 0, 0)
    # Compute differentiation matrix (b=1).
    Dm = poldif(x, alpha=alpha, B=beta)
    # Scale nodes by the factor b.
    x = x/b
    # Adjust derivatives for b not equal to 1.
    for i in range(1, m+1):
        Dm[i-1] *= b**i

    return x, Dm

def poldif(x, m=None, alpha=None, B=None):
    """
    Computes the differentiation matrices D1, D2, ..., Dm on arbitrary nodes.

    The function is called with either keyword argument m OR
    keyword args alpha and B.
    If m is given, then the weight function is constant.
    If alpha and B are given, then the weights are defined by alpha and B.

    Args:
        x: 1D array of n distinct nodes.

    Kwargs:
        m: Number of derivatives.
        alpha: 1D array of weight values alpha[x], evaluated at x = x[k].
        B: Array of size m x n where B[i,j] = beta[i,j] = ((i+1)-th derivative
            of alpha(x))/alpha(x), evaluated at x = x[j].

    Returns:
        Dm: A list s.t. Dm[i] is the (i+1)-th derivative matrix, i=0...m-1.

    Note: 0 < m < n-1.
    """
    x = x.flatten()
    n = x.shape[0]
    if m is not None and B is None and alpha is None:
        alpha = N.ones(n)
        B = N.zeros((m, n))
    elif m is None and B is not None and alpha is not None:
       alpha = alpha.flatten()
       m = B.shape[0]
    else:
        raise RuntimeError('Keyword args to poldif are inconsistent.')

    XX = N.tile(x, (n, 1)).transpose()
    # DX contains entries x[k] - x[j].
    DX = XX - XX.transpose()
    # Put 1's one the main diagonal.
    N.fill_diagonal(DX, 1.)

    # C has entries c[k]/c[j].
    c = alpha * N.prod(DX, 1)
    C = N.tile(c, (n, 1)).transpose()
    C = C/C.transpose()

    # Z has entries 1/(x[k]-x[j])
    Z = 1./DX
    N.fill_diagonal(Z, 0.)

    # X is Z' but with the diagonal entries removed.
    X = Z.transpose()
    X = ma.array(X.transpose(), mask=N.identity(n)).compressed().\
        reshape((n, n-1)).transpose()

    # Y is matrix of cumulative sums and D is a differentiation matrix.
    Y = N.ones((n, n))
    D = N.eye(n)
    Dm = []
    for i in range(1, m+1):
        # Diagonals
        Y = N.cumsum(N.concatenate((B[i-1].reshape((1,n)),
            i * Y[0:n-1] * X), axis=0), axis=0)
        # Off-diagonals
        D = i * Z * (C * N.tile(N.diag(D), (n,1)).transpose() - D)
        # Correct the diagonal
        D.flat[::n+1] = Y[-1]
        Dm.append(D)

    return Dm


# Ben Eggers <ben.eggers36@gmail.com>
#
# A simple, gradient-descent-based linear system solver. The idea is as follows:
#
# We are given a matrix A and a vector b, and want to find x such that Ax = b.
#
# We turn this into an optimization problem, with our objective function f(x) =
# the A-norm of the error (Ax - b). We use an iterative method. At each step,
# we find the gradient at our current guess (initialized to 0), then take the
# optimal step size (computable because matrices are awesome) in the direction
# of the gradient. We repeat until the gradient = 0, which is when our guess
# will be the solution to the system.
#
# I only want to deal with SDD systems here, so the matrix input will be in
# graph form, with the matrix defined to be the Laplacian of the input graph.

import numpy as np
import time

# Get the gradient of A at x
def grad(A, b, x):
    return -2 * rhe(A, b, x)

# Error on the right-hand side. b - Ax
def rhe(A, b, x):
    return b - A.dot(x)

def get_matrix():
    ec = int(raw_input("How many edges on input graph? "))
    edges = []
    for i in xrange(ec):
        inp = raw_input("Next edge(of the form '<node_#>, <node_#>'): ")
        edges.append((int(inp.split(", ")[0]), int(inp.split(", ")[1])))

    m = int(max([max(e[0], e[1]) for e in edges]))

    ret = np.zeros((m, m), dtype = np.int)

    for e in edges:
        ret[e[0] - 1][e[1] - 1] = ret[e[1] - 1][e[0] - 1] = -1
        # These should be "+= 1" for the true Laplacian, but we need the
        # matrix to be non-singular. So...
        ret[e[0] - 1][e[0] - 1] += 2
        ret[e[1] - 1][e[1] - 1] += 2

    print ret
    return ret

def get_target(length):
    print "Please input", length, "numbers for the target vector"
    ret = []
    while len(ret) < length:
        ret.append(int(raw_input("Next number? ")))
    return np.array(ret)

def report(A, b, x):
    print "x =", x
    print "Error norm:", np.linalg.norm(rhe(A, b, x))

# ititial conditions
A = get_matrix()
b = get_target(A.shape[0])

x = np.array([0 for i in range(b.shape[0])])

# do the actual descent
g = grad(A, b, x)
# Ideally we'd go until the norm equals 0, but numerical analysis is a thing
while np.linalg.norm(g) > 0.000005:
    r = rhe(A, b, x)
    x = x + ((1. * r.dot(r)) / (r.dot(A).dot(r))) * r
    g = grad(A, b, x)

report(A, b, x)


import numpy as np
import matplotlib.pyplot at plt
from scipy import linalg as LA

### for knn
from sklearn.neighbors import NearestNeighbors

def dist_mtx(X, Y=None):
    """Return the distance matrix between rows of X and rows of Y

    Input:
        X: an array of shape (N,d)
        Y: an array of shape (M,d)
            if None, Y = X

    Output:
        the matrix [d_ij] where d_ij is the distance between
        the i-th row of X and the j-th row of Y
    """
    if isinstance(Y, np.ndarray):
        pass
    elif Y == None:
        Y = X.copy()
    else:
        raise TypeError("Y should be a NumPy array or None")
    X_col = X[:, np.newaxis, :]
    Y_row = Y[np.newaxis, :, :]
    diff = X_col - Y_row
    dist = np.sqrt(np.sum(diff**2, axis=-1))
    return dist

def inertia(X, labels, centers):
    """Return the inertia of X corresponding to its labels and cluster centers

    Input:
        X: an array of shape (N,d)
        labels: an array of shape (N,)
            that records the labels in (0, ..., k-1) of each sample
        centers: an array of shape (k,d)
            that records the cluster centers
    """
    k = centers.shape[0]
    return sum(np.sum((X[labels==i,:] - centers[i])**2)  for i in range(k))

def pca(X, r=2):
    """PCA dimensionality reduction

    Input:
        X: an array of shape (N,d)
            rows for samples and columns for features
        r: target dimention (<= d)

    Output:
        an array of shape (N,r)

    Example:
        mu = np.array([3,4])
        cov = np.array([[1.1,1],
                        [1,1.1]])
        X = np.random.multivariate_normal(mu, cov, 100)
        X_new = PCA(X)
    """
    N,d = X.shape
    X = X - X.mean(axis= 0)
    C = 1. / N * X.T.dot(X)
    vals,vecs = LA.eigh(C, subset_by_index=[d-r,d-1])
    vals = vals[::-1] ### from large to small
    U = vecs[:,::-1] ### from large to small
    return X.dot(U)

def mds(X, r=2, n_iter=100, verbose=0):
    """MDS dimensionality reduction

    Input:
        X: an array of shape (N,d)
            rows for samples and columns for features
        r: target dimention (<= d)
        n_iter: an integer for the number of iterations to run
        verbose: 0,1,2
            shows more details when higher value

    Output:
        an array of shape (N,r)

    Example:
        mu = np.array([3,4])
        cov = np.array([[1.1,1],
                        [1,1.1]])
        X = np.random.multivariate_normal(mu, cov, 100)
        X_new = MDS(X, verbose=2)
    """
    N,d = X.shape
    goal = dist_mtx(X) ### dissimilarity in sklearn.manifold._mds.py

    X0 = np.random.randn(N, r)
    dis = dist_mtx(X0)
    stress = np.sum( (dis - goal)**2 ) / 2
    if verbose >= 2:
        print("Iter %3d: stress = %f"%(0, stress))

    Xk = X0
    for k in range(1, n_iter + 1):
        s = np.zeros_like(dis)
        mask = (dis != 0)
        s[mask] = 1 / dis[mask]
        B = -goal * s
        B[np.arange(N), np.arange(N)] = -B.sum(axis=1)
        Xk = 1. / N * B.dot(Xk) ### Vdagger B(Xk) Xk
        dis = dist_mtx(Xk)
        stress = np.sum( (dis - goal)**2 ) / 2
        if verbose >= 2:
            print("Iter %3d: stress = %f"%(k, stress))

    return Xk

def k_means(X, k, init="random"):
    """k-means clustering algorithm

    Input:
        X: an array of shape (N,d)
            rows for samples and columns for features
        k: number of clusters
        init: "random" or an array of shape (k,d)
            if "random", k points are chosen randomly from X as the initial cluster centers
            if an array, the array is used as the initial cluster centers

    Output:
        (y_new, centers)
        y_new: an array of shape (N,)
            that records the labels in (0, ..., k-1) of each sample
        centers: an array of shape (k,d)
            that records the cluster centers

    Example:
        mu = np.array([3,3])
        cov = np.eye(2)
        X = np.vstack([np.random.multivariate_normal(mu, cov, 100),
                       np.random.multivariate_normal(-mu, cov, 100)])
        y_new,centers = k_means(X, 2)
    """
    N,d = X.shape

    ### initialize y and center
    if isinstance(init, np.ndarray):
        centers = init.copy()
    elif init == "random":
        inds = np.random.choice(np.arange(N), k, replace=False)
        centers = X[inds, :]
    else:
        raise TypeError("init can only be a NumPy array or 'random'")

    dist = dist_mtx(X, centers)
    y_new = dist.argmin(axis=1)

    while True:
        ### compute the new centers
        for i in range(k):
            mask = (y_new == i)
            centers[i] = X[mask].mean(axis=0)

        ### generate the new y_new
        dist = dist_mtx(X, centers)
        y_last = y_new.copy()
        y_new = dist.argmin(axis=1)

        if np.all(y_last == y_new):
            break

    return y_new, centers

def dbscan(X, eps, min_samples, draw=False):
    """DBSCAN clustering algorithm

    Input:
        X: an array of shape (N,d)
            rows for samples and columns for features
        eps: the radius used for finding neighborhood
        min_samples: a sample is considered as a core sample
            if its epsilon-ball contains at least `min_sample` samples
            (including itself)
        draw: boolean, return a illustrative figure or not

    Output:
        (y_new, core_indices, fig)  or (y_new, core_indices) depending on draw or not
        y_new: an array of shape (N,)
            that records the labels in of each sample, where -1 stands for a noise
        core_indices: an array of shape (n_core_samples,) that stores the indices of the core samples
        fig: an illustrative figure showing the data points, DFS tree, core samples, and noises

    Example:
        mu = np.array([3,3])
        cov = np.eye(2)
        X = np.vstack([np.random.multivariate_normal(mu, cov, 100),
                       np.random.multivariate_normal(-mu, cov, 100)])
        y_new,core_indices,fig = dbscan(X, 1, 5, draw=True)
    """
    N,d = X.shape
    dist = dist_mtx(X)

    ### find core samples
    adj = (dist <= eps)
    core_mask = (adj.sum(axis=1) >= min_samples)
    core_indices = np.where(core_mask)[0]
    nbrhoods = [np.where(adj[i])[0] for i in range(N)]

    ### Run DFS to label each vertex
    y_new = -np.ones((N,), dtype=int)
    label_num = 0
    tree = []
    for i in range(N):
        if y_new[i] == -1 and core_mask[i]:
            stack = [i]
            while stack != []:
                j = stack.pop()
                if y_new[j] == -1:
                    tree.append((i,j))
                    y_new[j] = label_num
                    if core_mask[j]:
                        stack += list(nbrhoods[j])
            label_num += 1

    if draw:
        fig = plt.figure()
        for i,j in tree:
            plt.plot(*zip(X[i], X[j]), c='b', zorder=-1)
        plt.scatter(X[:,0], X[:,1], c=y_new, cmap='viridis')
        components = X[core_indices, :]
        plt.scatter(components[:,0], components[:,1], c='r', s=10)
        noise = X[y_new == -1]
        plt.scatter(noise[:,0], noise[:,1], c='k', s=100, marker='x')
        plt.close()
        return y_new, core_indices, fig

    return y_new, core_indices

def linear_regression(X, y, fit_intercept=True, algorithm='projection',
                     alpha=1, learning_rate=0.01, n_iter=1000, regularization=None, verbose=False):
    """Linear Regression algorithm

    Input:
        X: an array of shape (N,d)
            rows for samples and columns for features
        y: the labels of shape (N,)
        fit_intercept: whether to calculate the intercept or not
        algorithm: "projction" or "grad_descent"
        alpha: weight for the L1 or L2 regularization
        learning_rate: learning rate for the gradient descent algorithm
        n_iter: number of iterations for the gradient descent algorithm

    Output:
        (predict, coefs, intercep)
        predict: a function that can takes some samples X_test and
            return the prediction X_test.dot(coefs) + intercept
        coef: an array of shape (d,) that stores the coefficients
        intercept: a float for the intercept

    Example:
        x = np.arange(10)
        X = np.vstack([x]).T
        y = 0.5 * x + 3 + 0.3*np.random.randn(10)
        predict,coef,intercept = linear_regression(X, y, algorithm="grad_descent",
                                                  learning_rate=0.03, regularization=None, verbose=True)
        x_sample = np.linspace(0,10,20)
        X_sample = x_sample[:,np.newaxis]
        y_new = predict(X_sample)
    """
    N,d = X.shape
    if fit_intercept:
        A = np.hstack([np.ones((N,1)), X])
    else:
        A = X.copy()
    dp = A.shape[1]

    if algorithm == "projection":
        ATAinv = np.linalg.pinv(A.T.dot(A))
        v = ATAinv.dot(A.T).dot(y)

    if algorithm == "grad_descent":
        v = np.random.randn(dp)
        if verbose:
            mse = np.mean((A.dot(v) - y)**2)
            print("Iter %3d: mes = %f"%(0, mse))
        for _ in range(n_iter):
            Av = A.dot(v)
            ### before 1 / N
            diff = Av - y ### (n_sample, ) as a row vector
            ### now do 1 / N
            grad = 1 / N * np.dot(2*diff, A)

            ### regularization gradient
            if regularization == None:
                grad_reg = np.zeros_like(grad)
            elif regularization == "L1":
                grad_reg = alpha * np.sign(v, dtype=float)
            elif regularization == "L2":
                grad_reg = alpha * 2 * v
            else:
                raise TypeError("invalid input for regularization keyword")

            v -= learning_rate * (grad + grad_reg)
            if verbose:
                mse = np.mean(diff**2)
                print("Iter %3d: mes = %f"%(_, mse))

    if fit_intercept:
        coef = v[1:]
        intercept = v[0]
    else:
        coef = v.copy()
        intercept = 0

    predict = lambda X_test: X_test.dot(coef) + intercept
    return predict, coef, intercept

def polynomial_regression(X, y, degree=2, **kwargs):
    """Polynomial Regression algorithm

    Input:
        X: an array of shape (N,1)
            rows for samples and columns for features
        y: the labels of shape (N,)
        degree: the degree of the polynomial
        **kwargs: keywords for linear_regression function

    Output:
        output of linear_regression function

    Example:
        x = np.arange(10)
        y = 0.1*x**2 + 0.2*x + 0.3 + 0.5*np.random.randn(10)
        X = x[:,np.newaxis]
        predict,coef,intercept = jeplearn.polynomial_regression(X, y, algorithm="grad_descent",
                                                               learning_rate=0.0001, regularization=None, verbose=True)
        x_sample = np.linspace(0,10,20)
        X_sample = x_sample[:,np.newaxis]
        y_new = predict(X_sample)
    """
    X_ex = X**np.arange(1, degree+1)
    predict_lin,coef,intercept = linear_regression(X_ex, y, **kwargs)
    predict = lambda X_test: (X_test**np.arange(1, degree+1)).dot(coef) + intercept
    return predict, coef, intercept

def knn(X, y, k=5, algorithm='brute'):
    """k-nearest neighbors classification algorithm

    Input:
        X: an array of shape (N,d)
            rows for samples and columns for features
        y: the labels of shape (N,)
        k: numbers of neighbors (including self) to vote
        algorithm: 'brute', 'ball_tree', or 'kd_tree'

    Output:
        (predict, k_nearest_neighbors)
        predict: a function that takes data X_sample and output their predicted labels
        k_nearest_neighbors: a function that takes data X_sample and
            return the indices of the nearest neighbors in X

    Example:
        X = np.vstack([np.random.randn(50,2) + np.array([3,3]),
                       np.random.randn(50,2) - np.array([3,3])])
        y = np.array([0]*50 + [1]*50)
        X_sample = np.random.randn(5,2)
        predict,nbrs = knn(X, y, k=5, algorithm='brute')
        y_new = predict(X_sample)
    """
    N,d = X.shape
    classes = np.unique(y)

    if algorithm=="brute":
        def k_nearest_neighbors(X_sample):
            dist = dist_mtx(X_sample, X)
            argp = dist.argpartition(k-1)
            return argp[:,:k]
    elif algorithm in ["ball_tree", "kd_tree"]:
        def k_nearest_neighbors(X_sample):
            nbr = NearestNeighbors(n_neighbors=k, algorithm=algorithm)
            nbr.fit(X)
            return nbr.kneighbors(X_sample, return_distance=False)
    else:
        raise TypeError("invalid input for the algorithm keyword")

    def predict(X_sample):
        nbrhoods = k_nearest_neighbors(X_sample)
        votes = y[nbrhoods]
        count = (votes[:,:,np.newaxis] == classes).sum(axis=1)
        y_new = classes[count.argmax(axis=1)]
        return y_new
    return predict, k_nearest_neighbors

def decision_tree(X, y, criteria="gini"):
    """Decision tree classification algorithm

    Input:
        X: an array of shape (N,d)
            rows for samples and columns for features
        y: the labels of shape (N,)
        criterion: "gini" or "entropy"

    Output:
        (predict, tree)
        predict: a function that takes data X_sample and output their predicted labels
        tree: a dictionary that contains the information same as those in model.tree_

    Example:
        mu = np.array([1,1])
        cov = np.array([[1.1,-1],
                        [-1,1.1]])
        X = np.vstack([np.random.multivariate_normal(mu, cov, 100),
                       np.random.multivariate_normal(-mu, cov, 100)])
        y = np.array([0]*100 + [1]*100)

        X_sample = 2*np.random.randn(1000,2) - np.array([1,1])
        predict,tree = decision_tree(X, y, criteria="gini")
        y_new = predict(X_sample)
    """
    N,d = X.shape
    classes = np.unique(y)

    def impurity(arr):
        dtrib = np.unique(arr, return_counts=True)[1]
        dtrib = dtrib / dtrib.sum()
        if criteria == "gini":
            return np.sum(dtrib * (1 - dtrib))
        if criteria == "entropy":
            return np.sum(-dtrib * np.log2(dtrib))

    ### every sample are in node 0
    node = np.zeros((N,), dtype=int)

    queue = [0]
    node_count = 0 ### index of the last existing node

    tree = {"children_left": [],
            "children_right": [],
            "feature": [],
            "threshold": [],
            "n_node_samples": [],
            "impurity": []}

    while queue != []:
        k = queue[0]
        queue.remove(k)

        ### all samples in node k
        mask_U = (node == k)
        U = X[mask_U]
        y_U = y[mask_U]
        NU = U.shape[0]

        imp = impurity(y_U)
        tree["impurity"].append(imp)
        tree["n_node_samples"].append(NU)
        if imp == 0:
            tree["children_left"].append(-1)
            tree["children_right"].append(-1)
            tree["feature"].append(-2)
            tree["threshold"].append(-1)
            continue

        Iprime = np.zeros_like(U)
        ### calculate the Iprime for each cut
        for j in range(d):
            for i in range(NU):
                mask = (U[:,j] <= U[i,j])
                NL, NR = mask.sum(), (~mask).sum()
                HL = impurity(y_U[mask])
                HR = impurity(y_U[~mask])
                Iprime[i,j] = (NL*HL + NR*HR) / NU

        ### note here i and j are specific choices
        ind = Iprime.argmin()
        i,j = ind//d, ind%d
        mask = (U[:,j] <= U[i,j])
        ### the following syntax does not change node itself
        ### Use np.where instead
        #  node[mask_U][mask] = node_count + 1
        #  node[mask_U][~mask] = node_count + 2

        inds = np.where(mask_U)[0]
        node[inds[mask]] = node_count + 1
        node[inds[~mask]] = node_count + 2
        queue += [node_count + 1, node_count +2]

        tree["children_left"].append(node_count + 1)
        tree["children_right"].append(node_count + 2)
        tree["feature"].append(j)
        tree["threshold"].append(U[i,j])

        node_count += 2

    def predict(X_sample):
        n_nodes = len(tree["children_left"])
        ### array of shape (n_nodes,)
        ### consisting of the class number
        ### or -3 if not leaf (-3 is just for debugging)
        node_to_class = -3*np.ones((n_nodes,), dtype=int)
        for v in range(n_nodes):
            if tree["children_left"][v] == -1:
                node_to_class[v] = classes[np.bincount(y[node==v]).argmax()]

        node_sample = np.zeros((X_sample.shape[0],), dtype=int)

        for v in range(n_nodes):
            if tree["children_left"][v] == -1:
                continue
            inds = np.where(node_sample == v)[0]
            mask = (X_sample[inds][:, tree["feature"][v]] <= tree["threshold"][v])
            node_sample[inds[mask]] = tree["children_left"][v]
            node_sample[inds[~mask]] = tree["children_right"][v]
        return node_to_class[node_sample]

    return predict, tree


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Oct 15 07:04:58 2020

Illustrates several ways to do direct polynomials fits with python

@author: zettergm
"""

# Imports
import numpy as np
import matplotlib.pyplot as plt
import sys
sys.path.append("../linear_algebra")
from elimtools import Gauss_elim,backsub

# Data for a direct polynomial fit
#x=np.array([1,2,3,4])
#y=2*x**3-3*x**2+4*x+9
x=np.array([1,2,3,4,5,6])
y=x**5-2*x**4+2*x**3-3*x**2+4*x+9

# Create a plot to show results
plt.figure(1)
plt.plot(x,y,'*',markersize=20)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Illustration of direct fit methods")

# Fit using Python functions (numpy.polyfit)
n=x.size-1     # degree of polynomial to be fitted, compute from data point array size
coeffs=np.polyfit(x,y,n)
xlarge=np.linspace(x.min(),x.max(),24)
polyfun=np.poly1d(coeffs)
plt.plot(xlarge,polyfun(xlarge),'--')

# Execute direct fit using self-coded Gaussian Elimination
A=np.zeros((n+1,n+1))
for icol in range(n,-1,-1):
    newcol=x**icol
    A[:,n-icol]=newcol
[Amod,order]=Gauss_elim(A,y.reshape(-1,1),False)   #note conversion of y to column vector
coeffsGE=backsub(Amod[order,:],False)
polyfunGE=np.poly1d(coeffs)
plt.plot(xlarge,polyfunGE(xlarge),'.')
plt.legend(("original data","built-in fit","manual GE fit"))
plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 24 15:33:13 2019

@author: alankar
"""

import numpy as np
import matplotlib.pyplot as plt

N = 1000

square = lambda n: np.piecewise(n, [n>=N/2], [1.,0.])

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

square = square(n)
sawtooth = n
modulated = np.sin(1*np.pi*n/N)*np.sin(20*np.pi*n/N)

plt.figure(figsize=(13,10))
plt.plot(n,square)
plt.grid()
plt.xlabel(r'$x$',size=18)
plt.ylabel(r'$f(x)$',size=20)
plt.title(r'Square Wave', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('1_1.png')
plt.show()

plt.figure(figsize=(13,10))
plt.plot(n,sawtooth)
plt.grid()
plt.xlabel(r'$x$',size=18)
plt.ylabel(r'$f(x)$',size=20)
plt.title(r'Sawtooth Wave', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('1_2.png')
plt.show()

plt.figure(figsize=(13,10))
plt.plot(n,modulated)
plt.grid()
plt.xlabel(r'$x$',size=18)
plt.ylabel(r'$f(x)$',size=20)
plt.title(r'Modulated Sine Wave', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('1_3.png')
plt.show()

square_fft = np.abs(np.fft.fft(square))**2
sawtooth_fft = np.abs(np.fft.fft(sawtooth))**2
modulated_fft = np.abs(np.fft.fft(modulated))**2

sampling = n[1] - n[0]
f = np.linspace(0,1/sampling,N)

plt.figure(figsize=(13,10))
plt.plot(f[1:N//2],square_fft[1:N//2])
plt.grid()
plt.xlabel(r'$k$',size=18)
plt.ylabel(r'$\~f(k)$',size=20)
plt.title(r'FT[Square Wave]', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.ylim(0,400)
plt.savefig('1_4.png')
plt.show()

plt.figure(figsize=(13,10))
plt.plot(f[1:N//2],sawtooth_fft[1:N//2])
plt.grid()
plt.xlabel(r'$k$',size=18)
plt.ylabel(r'$\~f(k)$',size=20)
plt.title(r'FT[Sawtooth Wave]', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('1_5.png')
plt.ylim(0,1e7)
plt.show()

plt.figure(figsize=(13,10))
plt.plot(f[1:N//2],modulated_fft[1:N//2])
plt.grid()
plt.xlabel(r'$k$',size=18)
plt.ylabel(r'$\~f(k)$',size=20)
plt.title(r'FT[Modulated Sine Wave]', size=21)
plt.tick_params(axis='both', which='major', labelsize=15)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.savefig('1_6.png')
#plt.ylim(0,100)
plt.show()


import numpy as np

def fill_knapsack(current_volume, item, volumes, values, dynamic_results):
    # check for valid item count
    if item < len(volumes):
        # did we calculate the value already (dynamic programming)
        if dynamic_results[item][current_volume] != -1:
            return dynamic_results[item][current_volume]
        # Calculate result without current item
        a: int = fill_knapsack(current_volume, item + 1, volumes, values, dynamic_results)
        # Calculate result with current item
        b: int = 0
        if current_volume - volumes[item] >= 0:
            b = values[item] + fill_knapsack(current_volume - volumes[item], item + 1, volumes, values, dynamic_results)
        # The maximum of the two is saved and returned
        dynamic_results[item][current_volume] = max(a, b);
        return dynamic_results[item][current_volume]
    return 0

def backtracking(knapsack_value, dynamic_results, volumes, values):
    number_of_items = len(volumes) -1
    # Find maximum volume for optimal value (always first row)
    current_volume = np.argmax(dynamic_results[0])

    # iterate the items
    item: int = 0
    while item < number_of_items:
        # does the item fit the knapsack and if so, does the current value - item-value fit the volume-value?
        # If so; he item was selected to be optimal and we can reduce the volume and value and check the next item.
        if (current_volume - volumes[item] >= 0
                and knapsack_value - values[item] == dynamic_results[item + 1][current_volume - volumes[item]]):
            print_item(item, volumes, values)
            knapsack_value -= values[item]
            current_volume -= volumes[item]
        else:
            # Item is not in the Knapsack
            print_item(item, volumes, values, False)
        item += 1
    # If the last item was inside the kp, we didn't find it in the loop.
    print_item(number_of_items, volumes, values, knapsack_value > 0)

def print_item(item: int, volumes, values, selected: bool = True):
    out = ""
    if not selected:
        out = "NOT "
    out += "selecting item:{} (vol: {}, val: {})"
    print(out.format(item, volumes[item], values[item]))

def main(max_n=300, max_v=1000):
    rucksack_volume = 30
    volumes = np.fromstring('5, 5, 6, 8, 10, 11, 12, 15, 15, 30', dtype=np.int, sep=',')
    values = np.fromstring('8, 8, 6, 5, 10, 5, 10, 17, 20, 20', dtype=np.int, sep=',')

    dynamic_results = np.zeros((len(volumes), rucksack_volume + 1))
    dynamic_results[:] = -1

    kp_value = fill_knapsack(rucksack_volume, 0, volumes, values, dynamic_results)
    print("Value of knapsack:{}".format(kp_value))
    backtracking(kp_value, dynamic_results, volumes, values)

if __name__ == '__main__':
    main()


"""Fourier 2D shape reconstruction with epicycles representation"""
# Original version from D. Shiffman (2019), adapted for vedo by M. Musy (2022)
# https://thecodingtrain.com/CodingChallenges/130.2-fourier-transform-drawing.html
import numpy as np
import vedo

order = 30  # restrict to this nr of fourier coefficients in reconstruction

def DFT(x):
    X = []
    N = len(x)
    for freq in range(N):
        re, im = [0, 0]
        for n in range(N):
            phi = (2 * np.pi * freq * n) / N
            re += x[n] * np.cos(phi)
            im -= x[n] * np.sin(phi)
        re, im = [re/N, im/N]
        amp = np.sqrt(re*re + im*im)
        phase = np.arctan2(im, re)
        X.append([re, im, freq, amp, phase])
    return vedo.utils.sortByColumn(X, 3, invert=True)

def epicycles(time, rotation, fourier, order):
    global objs
    plt.remove(objs)
    x, y = [0, 0]
    objs, path = [], []
    for i in range(len(fourier[:order])):
        re, im, freq, amp, phase = fourier[i]
        if amp > 0.2:
            c = vedo.Circle([x,y], amp).wireframe().lw(0.1)
            objs.append(c)
        x += amp * np.cos(freq * time + phase + rotation)
        y += amp * np.sin(freq * time + phase + rotation)
        path.append([x,y])

    if len(points):
        hline = vedo.Line([x,y], points[-1], c='red5', lw=0.1)
        pline = vedo.Line(path, c='green5', lw=2)
        oline = vedo.Line(points, c='red4', lw=5)
        objs += [hline, pline, oline]
        plt.add(objs, resetcam=False)
    return [x, y]

# Load some 2D shape and make it symmetric
shape  = vedo.load(vedo.dataurl+'timecourse1d.npy')[55]
shaper = vedo.Line(shape).mirror('x').reverse()
shape = vedo.merge(shape, shaper)
x, y, _ = shape.points().T

# Compute Fourier Discrete Transform in x and y separately:
fourierX = DFT(x)
fourierY = DFT(y)

vedo.settings.defaultFont = 'Glasgo'
vedo.settings.allowInteraction = True

plt = vedo.Plotter(size=(1500,750), bg='black', axes=1, interactive=False)
txt = vedo.Text2D(f"{__doc__} (order={order})", c='red9', bg='white', pos='bottom-center')
plt.show(shape, txt, mode='image', zoom=1.9)

objs, points = [], []
times = np.linspace(0, 2*np.pi, len(fourierX), endpoint=False)
for time in times:
    x, _ = epicycles(time,       0, fourierX, order)
    _, y = epicycles(time, np.pi/2, fourierY, order)
    points.append([x, y])

plt.interactive().close()


"""
   Algoritmo del simplesso per problema primale:

     max sum(c[j] * x[j]  for j in 1..n)
    s.t. sum(- A[i][j] * x[j] for j in 1..n) <= b[i]  for i in 1..m
         x[j] >= 0  for j in 1..n

    Autore: Bernardo Forni, bernardo.forni01@universitadipavia.it, (C) 2020.

"""

import numpy as np

# Parameters
OptimalityTol  = 1e-06
FeasibilityTol = 1e-06

# Main class
class LinearProblem:
    def __init__(self, c, A, b):
        """ Default costructor """
        self.not_base = list(range(1, len(c)+1))  # j = 1..n
        self.base = list(range(len(c)+1, len(c)+len(b)+1))  # n+i, i = 1..m

        self.D = np.row_stack(([0] + c, np.column_stack((b, A)))).astype(float)  # (m+1)*(n+1) matrix

    def __str__(self):
        """ Print current dictionary values """

        str = 'Current Basic Solution: '

        for x in list(range(1, len(self.not_base)+1)):
            str += f'x_{x}=0, ' if x in self.not_base else f'x_{x}={self.D[self.base.index(x)+1,0]:g}, '
        str += f'z={-self.D[0][0]:g}'

        return str

    def get_obj(self):
        return -self.D[0,0]

    def check_optimality(self):
        """ Check if the current basic solution is optimal """
        return not any(c > OptimalityTol for c in self.D[0, 1:])

    def check_feasibility(self):
        """ Check if the current basic solution is feasible """
        return not any(c < -FeasibilityTol for c in self.D[1:, 0])

    def select_entering(self):
        """ Select an entering variable with largest c[j]. Return index k """
        return 1 + np.argmax(self.D[0, 1:])

    def select_leaving(self, k):
        """ Select a leaving variable that maximize A[i][k] / b[i]. Return index h """
        # TODO: check for degenerate problem (b[h] == 0 or A[h][k] == 0)
        return 1 + np.argmax(self.D[1:,k] / self.D[1:,0])

    def pivoting(self, k, h):
        """ Change the current dictionary according to the pivoting operations.
            Do not return anything (void function)
        """
        no_h = np.ones(self.D.shape[0], dtype=bool)
        no_h[h] = False
        no_k = np.ones(self.D.shape[1], dtype=bool)
        no_k[k] = False

        # pivot leaving variables
        self.D[h,k] = 1 / self.D[h,k]
        self.D[h,:][no_k] = self.D[h,:][no_k] * self.D[h,k]

        # pivot other variables
        self.D[np.ix_(no_h, no_k)] -= np.outer(self.D[:,k][no_h], self.D[h,:][no_k])

        # pivot entering variables
        self.D[:,k][no_h] = - self.D[:,k][no_h] * self.D[h,k]

        # enter, leave base
        self.not_base[k-1], self.base[h-1] = self.base[h-1], self.not_base[k-1]

    def solve(self):
        """ Using the previous functions, solve the given LP """

        if not self.check_feasibility():
            print('ERROR: First Basic solution must be feasible!')
            return False

        while not self.check_optimality():
            k = self.select_entering()
            h = self.select_leaving(k)

            print(f'Pivoting: k={k}, h={h}')
            self.pivoting(k, h)

        return True

def main():
    # Exercise 2.1 https://vanderbei.princeton.edu/JAVA/pivot/simple.html
    c = [6, 8, 5, 9]  # cost
    A = [[2, 1, 1, 3],
         [1, 3, 1, 2]]  # constraint coefficients
    b = [5, 3]  # RHS

    # Exercise 2.2
    # c = [2, 1]
    # A = [[2, 1],
    #      [2, 3],
    #      [4, 1],
    #      [1, 5]]
    # b = [4, 3, 5, 1]

    # solve LP problem
    LP = LinearProblem(c, A, b)
    LP.get_obj()
    LP.solve()
    LP.get_obj()
    print(LP)

if __name__ == "__main__":
    main()


"""
Rayleigh Quotient iteration
uses inverse iteration for faster convergence

>>> A = array([[ 1.56260001, 0.22898325, 1.04332261, 0.4558478,  0.89455427],
...            [ 0.22898325, 0.54532437, 1.51480666, 0.96986641, 1.5202461 ],
...            [ 1.04332261, 1.51480666, 0.53536642, 0.77569108, 1.23270255],
...            [ 0.4558478 , 0.96986641, 0.77569108, 1.6110797 , 1.32133154],
...            [ 0.89455427, 1.5202461 , 1.23270255, 1.32133154, 0.27904888]])

>>> lam, v, iters, err = raquo(A,True)
>>> iters <= 10
True

>>> err < 1.0e-15
True
"""

from numpy import *

def raquo(A, details=False):
    n,_ = shape(A)
    v = random.rand(n)
    v = v / linalg.norm(v)
    lam = v.dot(A).dot(v)
    err = 1.0
    iters = 0
    while err > 1.0e-15 and iters < 1000:
        w = linalg.solve(A - lam*eye(n), v)
        v = w / linalg.norm(w)
        lam = v.dot(A).dot(v)
        err = linalg.norm(A.dot(v) - lam*v)
        iters += 1
    if details:
        return lam, v, iters, err
    else:
        return lam, v

if __name__ == "__main__":
    import doctest
    doctest.testmod()


import numpy as np

def moment(source, maxn=1):
    """Computes moments from a given source.

    Args:
        source (array-like, distribution or arrival):
            data to compute moments for. Treated as a set of samples
            if an array-like, otherwise is expected to have a `moment(k)`
            method (e.g. distribution or arrival process)
        maxn (int): a number of moments to compute.

    Returns:
        ndarray containing computed moments
    """
    if hasattr(source, 'moment'):
        return np.asarray([source.moment(k) for k in range(1, maxn + 1)])
    else:
        n = len(list(source))
        ret = [1./n * sum([pow(x, k) for x in source])
               for k in range(1, maxn + 1)]
        return np.asarray(ret)

def lag(source, maxn=1):
    """Computes lags from a given source.

    Args:
        source (array-like or arrival):
            data to compute moments for. Treated as a set of samples
            if an array-like, otherwise is expected to have a `lag(k)`
            method (e.g. arrival process)
        maxn (int): a number of lags to compute.

    Returns:
        ndarray containing computed lags
    """
    if hasattr(source, 'lag'):
        return np.asarray([source.lag(k) for k in range(1, maxn + 1)])
    else:
        n = len(list(source))
        maxn = min(maxn, n - 1)  # need at least k + 1 samples for k-th lag
        moments = moment(source, 2)
        m1 = moments[0]
        sigma2 = moments[1] - moments[0] ** 2
        ret = []
        for k in range(1, maxn + 1):
            values = [(source[i] - m1) * (source[i + k] - m1)
                      for i in range(n - k)]
            ret.append(sum(values) / ((n - k) * sigma2))
        return np.asarray(ret)

def normalize_moments(moments, k=None):
    # TODO: add unit test
    """
    Normalizes moments using a given or computed coefficient.

    Args:
        moments (array-like): an array of the first moments
        k (scalar, callable or None): a coefficient. If None, it will be $M1$
            if only one moment is given or $\frac{M2}{M1}$. If scalar,
            this scalar will be used. If callable, it will be called as
            `k(moments)` to get the coefficient.

    Returns: tuple (ndarray, mu), where the first component is normalized
     moments, the second is the coefficient being used.
    """
    if k is None:
        try:
            mu = moments[1] / moments[0]
        except IndexError:
            mu = moments[0]
    else:
        try:
            mu = k(moments)
        except TypeError:
            mu = k
    ret = list(moments)
    for i in range(len(ret)):
        ret[i] /= pow(mu, i + 1)
    return np.asarray(ret), mu


# Generalized Lagrangian Networks | 2020
# Miles Cranmer, Sam Greydanus, Stephan Hoyer (...)

import jax
import jax.numpy as jnp
from jax import jit

@jit
def kinetic_energy(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8):
  (t1, t2), (w1, w2) = q, q_dot

  T1 = 0.5 * m1 * (l1 * w1)**2
  T2 = 0.5 * m2 * ((l1 * w1)**2 + (l2 * w2)**2 + 2 * l1 * l2 * w1 * w2 * jnp.cos(t1 - t2))
  T = T1 + T2
  return T

@jit
def potential_energy(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8):
  (t1, t2), (w1, w2) = q, q_dot

  y1 = -l1 * jnp.cos(t1)
  y2 = y1 - l2 * jnp.cos(t2)
  V = m1 * g * y1 + m2 * g * y2
  return V

# Double pendulum lagrangian
@jit
def lagrangian_fn(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8):
  (t1, t2), (w1, w2) = q, q_dot

  T = kinetic_energy(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8)
  V = potential_energy(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8)
  return T - V

# Double pendulum lagrangian
@jit
def hamiltonian_fn(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8):
  (t1, t2), (w1, w2) = q, q_dot

  T = kinetic_energy(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8)
  V = potential_energy(q, q_dot, m1=1, m2=1, l1=1, l2=1, g=9.8)
  return T + V

# Double pendulum dynamics via analytical forces taken from Diego's blog
@jit
def analytical_fn(state, t=0, m1=1, m2=1, l1=1, l2=1, g=9.8):
  t1, t2, w1, w2 = state
  a1 = (l2 / l1) * (m2 / (m1 + m2)) * jnp.cos(t1 - t2)
  a2 = (l1 / l2) * jnp.cos(t1 - t2)
  f1 = -(l2 / l1) * (m2 / (m1 + m2)) * (w2**2) * jnp.sin(t1 - t2) - (g / l1) * jnp.sin(t1)
  f2 = (l1 / l2) * (w1**2) * jnp.sin(t1 - t2) - (g / l2) * jnp.sin(t2)
  g1 = (f1 - a1 * f2) / (1 - a1 * a2)
  g2 = (f2 - a2 * f1) / (1 - a1 * a2)
  return jnp.stack([w1, w2, g1, g2])


# Fundamental of Tensor Operations - 1D

#Import libraries
import torch
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt

# Plot vecotrs, please keep the parameters in the same length
# @param: Vectors = [{"vector": vector variable, "name": name of vector, "color": color of the vector on diagram}]

def plotVec(vectors):
    ax=plt.axes() #defining axis

    #loop to draw the vector
    for vec in vectors:
        ax.arrow(0,0,*vec['vector'],head_width=0.05,
        color=vec['color'],head_length=0.1)
        plt.text(*(vec['vector']+0.1),vec['name'])
    plt.ylim(-2,2)
    plt.xlim(-2,2)

## Type (.type()) and shape (.dtype) of tensor

#------------Convert a integer list with length 5 to a tensor
ints_to_tensor=torch.tensor([0,1,2,3,4])
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Integer to tensor: \n',file=f)
    print('Dtype of tensor after converting it to tensor',ints_to_tensor.dtype,file=f)
    print('Type of tensor after converting it to tensor',ints_to_tensor.type(),file=f)
    print('\n',file=f)
# the integer list has been converted to a long tensor. But python type is still torch

#------------Convert a float list with length 5 to a tensor
floats_to_tensor = torch.tensor([0.0, 1.0, 2.0, 3.0, 4.0])
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Float to tensor: \n',file=f)
    print("The dtype of tensor object after converting it to tensor: ", floats_to_tensor.dtype,file=f)
    print("The type of tensor object after converting it to tensor: ", floats_to_tensor.type(),file=f)
    print('\n',file=f)

#-------------Convert float to integer
list_floats=[0.0,1.0,2.0,3.0,4.0]
floats_int_tensor=torch.tensor(list_floats,dtype=torch.int64)
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Float to Integer: \n',file=f)
    print("The dtype of tensor is : ", floats_int_tensor.dtype,file=f)
    print("The type of tensor is : ", floats_int_tensor.type(),file=f)
    print('\n',file=f)

# Alternatively, using specific methods in torch, conversions can be implemented as well.

new_float_tensor=torch.FloatTensor([0,1,2,3,4])
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Integer to float(method): \n',file=f)
    print("The type of new_float_tensor is : ", new_float_tensor.type(),file=f)
    print('\n',file=f)

# You can also convert an existing tensor object (tensor_obj) to another tensor type

#---------------Convert the integer tensor to a float tensor:
old_int_tensor=torch.tensor([0,1,2,3,4])
new_float_tensor1=old_int_tensor.type(torch.FloatTensor)
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Integer to float(Another method): \n',file=f)
    print("The type of new_float_tensor1 is : ", new_float_tensor1.type(),file=f)
    print('\n',file=f)

### Tensor Object  - Size (.size()), Dimension (.ndimension()), Reshaping (.view(row,column))

# Reshaping
twoD_float_tensor = new_float_tensor.view(5, 1) # reshaping 1D to 2D matrix
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Size,Dimension,Reshaping \n',file=f)
    print("The size of new_float_tensor1 is : ", new_float_tensor1.size(),file=f)
    print("The dimension of new_float_tensor1 is : ", new_float_tensor1.ndimension(),file=f) #Dimension =1
    print("The original size new_float_tensor1 is : ", new_float_tensor1, file=f)
    print("The modified of new_float_tensor1 is : \n ", twoD_float_tensor,file=f)
    print('\n',file=f)

# If we have a tensor with dynamic size and wants to reshape it.
twoD_float_tensor1 = new_float_tensor.view(-1, 1)
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Reshaping using general and dynamic form (-1) \n',file=f)
    print("The original size new_float_tensor1 is : ", new_float_tensor1, file=f)
    print("The modified of new_float_tensor1 is : \n ", twoD_float_tensor,file=f)
    print("The modified of new_float_tensor1 using  is : \n ", twoD_float_tensor1,file=f)
    print("The size of twoD_float_tensor1 is : ", twoD_float_tensor1.size(),file=f)
    print("The dimension of twoD_float_tensor1 is : ", twoD_float_tensor1.ndimension(),file=f) #Dimension=2
    print('\n',file=f)

#----------------- Convert numpy array and pandas series to a tensor or vice-versa------------------

# -using torch.from_numpy(), <file_tensor_name>.numpy()

np_array=np.array([0.0,1.0,2.0,3.0,4.0])
new_tensor=torch.from_numpy(np_array)

back_to_numpy=new_tensor.numpy()

with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Numpy <--> Tensor: \n',file=f)
    print("The dtype of new tensor is : ", new_tensor.dtype,file=f)
    print("The type of new tensor is : ", new_tensor.type(),file=f)
    print("The dtype of numpy from tensor is : ", back_to_numpy.dtype,file=f)
    print("The numpy array from tensor is : ", back_to_numpy,file=f)
    print('\n',file=f)

#------ Please note: back_to_numpy and new_tensor still point to numpy_array.
# As a result if we change numpy_array both back_to_numpy and new_tensor will change.

# Pandas Series can also be converted by using the numpy array that is stored in pandas_series.values.

pd_series=pd.Series([0.1,0.2,0.3,10.1])
new_tensor_pd=torch.from_numpy(pd_series.values)

with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Pandas <--> Tensor: \n',file=f)
    print("The new tensor from numpy array: ", new_tensor_pd,file=f)
    print("The dtype of new tensor: ", new_tensor_pd.dtype,file=f)
    print("The type of new tensor: ", new_tensor_pd.type(),file=f)
    print('\n',file=f)

#Using .item() - returns of value of tensor as number, tolist() - to return a list

this_tensor=torch.tensor([0,1,2,3])
torch_to_list=this_tensor.tolist()
with open('Lab1_Tensors_1D_Results.txt','a') as f:
    print('Illustration of .item and .tolist: \n',file=f)
    print("The first item is given by: ",this_tensor[0].item(),' The first rensor value is given by: ',this_tensor[0],file=f)
    print("The first item is given by: ",this_tensor[1].item(),' The first rensor value is given by: ',this_tensor[1],file=f)
    print("The first item is given by: ",this_tensor[2].item(),' The first rensor value is given by: ',this_tensor[2],file=f)
    print("Tensor: ", this_tensor,'\nlist:',torch_to_list, file=f)

Deep Neural Networks with PyTorch/Module1/Lab1_1DTensors_Part1.py


#! /usr/bin/env python

"""
File: Lagrange_poly1.py
Copyright (c) 2016 Austin Ayers
License: MIT

Course: PHYS227
Assignment: 5.23
Date: Feb 11, 2016
Email: ayers111@mail.chapman.edu
Name: Austin Ayers
Description: Calculates Lagrange's interpolation formula
"""
import sympy as sp

def p_L(x, xp, yp):
    """
    Evaluates (5.21)
    """
    result = 0
    for i, elem_y in enumerate(yp):
        result += float(elem_y) * L_k(x, i, xp, yp)
    return result

def L_k(x, k, xp, yp):
    """
    Evaluates (5.22)
    """
    product = 1.0
    for i, elem_x in enumerate(xp):
        if(i == k):
            continue
        product *= (float((x - elem_x)) / float((xp[k] - elem_x)))
    return product

def test_p_L():
    """
    Nosetest for p_L
    """
    x = [0.2,0.4,0.6,0.8,1.0]
    y = [0.19866933079, 0.3894193423, 0.56464247339, 0.7173560909, 0.8414909878]
    for i, elem_x, in enumerate(x):
        print abs(y[i] - p_L(elem_x, x, y))
        assert(abs(y[i] - p_L(elem_x, x, y)) < 0.2)


import numpy as np

"""
QR Factorization via Modified Gram Schmidt

Example 1: A is a complex 3x2 matrix with full column rank
>>> A = np.array([[1 + 2.1j, 2.1 + 3.1j],[2.7 + .1j, 3.1 + .2j],[4.1, 2.3j]])
>>> Q,R = gsm(A)
>>> np.linalg.norm(Q.dot(R) - A) < eps
True
>>> np.abs(inner(Q[:,0],Q[:,1])) < 2.*eps
True
>>> np.abs(inner(Q[:,1],Q[:,1]) - 1.) < 2.*eps
True

Example 2: A is a 3x3 real matrix with linearly dependent columns
>>> A = np.array([[1.,2.,3.]])
>>> A = A.T.dot(A)
>>> Q,R = gsm(A)
>>> np.linalg.norm(Q.dot(R) - A) < 8*eps
True
>>> np.abs(inner(Q[:,0],Q[:,1])) < 8*eps
True
>>> np.abs(inner(Q[:,1],Q[:,1]) - 1.) < 2*eps
True

Example 3: A is a 3x3 real matrix with one column of zeros
>>> A = np.array([[1,2,3],[0,0,0],[3,2,1]]).T+0.0
>>> Q,R = gsm(A)
>>> np.linalg.norm(Q.dot(R) - A) < 4*eps
True
>>> np.abs(inner(Q[:,0],Q[:,1])) < 2*eps
True
>>> np.abs(inner(Q[:,1],Q[:,1]) - 1.) < 2*eps
True

Note: test results may vary for Examples 2 and 3
due to the random vectors added.
"""

eps = 2**-52

def gsm(A,dbg=False):
    """modified gram-schmidt (reduced)
       takes a complex matrix of any shape and computes its
       QR factorization using the modified Gram-Schmidt factorization
       Handles the case; input matrix doesn't have full Column rank
    """
    m,n = np.shape(A)
    tp = A.dtype
    R = np.zeros((n,n),dtype=tp)
    Q = np.zeros((m,n),dtype=tp)
    V = A.copy()
    for i in range(n):
        R[i,i] = np.linalg.norm(V[:,i])
        if R[i,i] < 2*eps:
            if dbg: print("lin dep column found with i=%d" % i)
            Q[:,i] = random_orthonormal(Q,i)
        else:
            Q[:,i] = V[:,i] / R[i,i]
        for j in range(i+1,n):
            R[i,j] = inner(Q[:,i],V[:,j])
            V[:,j] -= R[i,j]*Q[:,i]
    return Q, R

def random_orthonormal(Q,i):
    """get a random unit length vector orthonormal to the
       first i-1 columns of Q
    """
    m,n=np.shape(Q)
    z = np.random.rand(m)
    v = z[:]
    for j in range(i):
        x = inner(Q[:,j],v)
        v -= x*Q[:,j]
    return v / np.linalg.norm(v)

def inner(v,w):
    """return the inner product of two vectors
       using the convention of conjugating the first argument
    """
    return v.conj().dot(w)

def bslv(R,c):
    """
    R should be upper triangular mxn with nonzero diagonal entries
    and c mx1.  Solves Rx = c by back substitution
    """
    tp = R.dtype
    _,n = shape(R)
    x = zeros(n,dtype=tp)
    for i in range(n-1,-1,-1):
        x[i] = (c[i]-R[i,i+1:].dot(x[i+1:])) / R[i,i]
    return x

def slv(A,b):
    """
    Solve linear system Ax = b
    """
    Q,R = gsm(A)
    c = Q.T.dot(b)
    x = bslv(R,c)
    return x

if __name__ == "__main__":
    import doctest
    doctest.testmod()


# -*- coding: utf-8 -*-
"""
Created on Tue Jul  3 14:55:08 2018

@author: SilverDoe
"""

'''==== Linear algebra====

SciPy.linalg vs NumPy.linalg
============================
A scipy.linalg contains all the functions that are in numpy.linalg. Additionally, scipy.
linalg also has some other advanced functions that are not in numpy.linalg. Another advantage
of using scipy.linalg over numpy.linalg is that it is always compiled with BLAS/LAPACK support,
while for NumPy this is optional. Therefore, the SciPy version might be faster depending on how
NumPy was installed.

https://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html
'''
#============= Solving lenear Equations =======================================

#importing the scipy and numpy packages
from scipy import linalg
import numpy as np

#Declaring the numpy arrays
a = np.array([[3, 2, 0], [1, -1, 0], [0, 5, 1]])
b = np.array([2, 4, -1])

#Passing the values to the solve function
x = linalg.solve(a, b)

#printing the result array
print(x)

#========== Finding Determinanat ==============================================

#importing the scipy and numpy packages
from scipy import linalg
import numpy as np

#Declaring the numpy array
A = np.array([[1,2],[3,4]])

#Passing the values to the det function
x = linalg.det(A)

#printing the result
print(x)

#======= Finding Eigen values and Eigen vectors ===============================

#importing the scipy and numpy packages
from scipy import linalg
import numpy as np

#Declaring the numpy array
A = np.array([[1,2],[3,4]])

#Passing the values to the eig function
l, v = linalg.eig(A)

#printing the result for eigen values
print(l)

#printing the result for eigen vectors
print(v)

#==================== SVD =====================================================

'''A Singular Value Decomposition (SVD) can be thought of as an extension of the eigenvalue
problem to matrices that are not square.

The scipy.linalg.svd factorizes the matrix ‘a’ into two unitary matrices ‘U’ and ‘Vh’ and a
 1-D array ‘s’ of singular values (real, non-negative) such that a == U*S*Vh, where ‘S’ is a
 suitably shaped matrix of zeros with the main diagonal ‘s’.

'''

#importing the scipy and numpy packages
from scipy import linalg
import numpy as np

#Declaring the numpy array
a = np.random.randn(3, 2) + 1.j*np.random.randn(3, 2)

#Passing the values to the eig function
U, s, Vh = linalg.svd(a)

# printing the result
print(U, Vh, s)

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


# -*- coding: utf-8 -*-
"""Linear_Regerssion.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/github/Abdokarawia/Linear-Regerssion/blob/main/Linear_Regerssion.ipynb
"""

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

EDS = pd.read_csv('/content/FuelConsumptionCo2.csv')
EDS.head()   # Showing a sample of the dataset

EDS.describe()

sns.pairplot(EDS , aspect=1 , height=4)
plt.show()

DS = EDS [['ENGINESIZE', 'CYLINDERS', 'FUELCONSUMPTION_COMB',  'FUELCONSUMPTION_CITY', 'CO2EMISSIONS' , 'FUELCONSUMPTION_COMB_MPG' ]]
DS.head(9)

sns.pairplot(data =DS , kind='scatter', height=4,   x_vars=['ENGINESIZE', 'CYLINDERS', 'FUELCONSUMPTION_COMB' , "FUELCONSUMPTION_COMB_MPG"], y_vars="CO2EMISSIONS" )
plt.show()

sns.boxplot(DS.CO2EMISSIONS ,color="lightblue")
plt.show()

sns.heatmap(DS.corr(), cmap="YlGnBu")
plt.show()

# First Way
mask  = np.random.rand(len(DS)) < 0.75
train = DS[mask]
test  = DS [~mask]

# Second Way
# from sklearn.model_selection import train_test_split
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

sns.pairplot(data =train , kind='scatter', height=4, aspect=1.2 , x_vars=['ENGINESIZE', 'CYLINDERS', 'FUELCONSUMPTION_COMB'], y_vars="CO2EMISSIONS" )
sns.pairplot(data =test , kind='scatter', height=4, aspect=1.2 , x_vars=['ENGINESIZE', 'CYLINDERS', 'FUELCONSUMPTION_COMB'], y_vars="CO2EMISSIONS" )
plt.show()

from sklearn.linear_model import  LinearRegression
LR = LinearRegression()
x_train = np.asarray(train[["ENGINESIZE"]])
y_train = np.asarray(train[["CO2EMISSIONS"]])
x_test  = np.asarray(test[["ENGINESIZE"]])
y_test  = np.asarray(test[["CO2EMISSIONS"]])
LR.fit(x_train , y_train)
intercept = LR.intercept_
coef = LR.coef_

plt.scatter(x_train, y_train , c="blue")
plt.plot(x_train, LR.predict(x_train)  , c="red")
predict_test = LR.predict(x_test)

from sklearn.metrics import r2_score
MAE = np.mean(np.abs(predict_test - y_test))
MSE = np.mean(np.abs(predict_test - y_test) **2 )
R2 = r2_score(y_test , predict_test)
print("Mean absolute error: %.2f" % MAE)
print("Residual sum of squares (MSE): %.2f" % MSE)
print("R2-score: %.4f" % R2 )


#!/usr/bin/env python
# coding: utf-8

# # Método de la secante
# J.J
# ---
# Está dado por las iteraciones
# \begin{equation}
# x_{i+1} = x_{i} - \frac{f(x_{i})}{d_{i}},
# \end{equation}
# con
# \begin{equation}
# d_{i} = \frac{f(x_{i})-f(x_{i-1})}{x_{i} - x_{i-1}}.
# \end{equation}

# Ejemplo: $f(x) = x^{2} + 3x +1$

# In[1]:

import numpy as np
import matplotlib.pyplot as plt

# In[2]:

def f(x):
    f = x**2 + 3*x + 1
    return f

# In[3]:

X = np.linspace(-3, 1, 100)
plt.plot(X, f(X))

# In[4]:

e = 0.0001 #error
maxit = 1000 #iteraciones máximas

# In[5]:

def Secante(x0, x1, func = f, error = e, iterations = maxit):
    it = 0
    while it < maxit:
        it += 1
        x = x1 - ((x1-x0)/(f(x1)-f(x0)))*f(x1)
        if abs(f(x)) > e:
            x0 = x1
            x1 = x
        else:
            break
    return x

# In[6]:

sol1 = Secante(-3.,-2.)
sol2 = Secante(-1.,0.)
print(sol1)
print(sol2)

# In[7]:

X = np.linspace(-3, 1, 100)
plt.plot(X, f(X))
plt.plot(sol1,f(sol1),'ro')
plt.plot(sol2,f(sol2),'ro')
plt.show()

# In[ ]:


#from numpy import zeros, linspace, pi, cos
import numpy as np
import matplotlib.pyplot as plt

def osc_Heun(X_0, omega, dt, T):
    N_t = int(round(T/dt))
    u = np.zeros(N_t+1)
    v = np.zeros(N_t+1)
    t = np.linspace(0, N_t*dt, N_t+1)

    # Initial condition
    u[0] = X_0
    v[0] = 0

    # Step equations forward in time
    for n in range(N_t):
        u_star = u[n] + dt*v[n]
        v_star = v[n] - dt*omega**2*u[n]
        u[n+1] = u[n] + 0.5*dt*(v[n] + v_star)
        v[n+1] = v[n] - 0.5*dt*omega**2*(u[n] + u_star)
    return u, v, t

def demo():
    omega = 2
    P = 2*np.pi/omega
    dt = P/20
    T = 10*P
    X_0 = 2
    u, v, t = osc_Heun(X_0, omega, dt, T)

    fig = plt.figure()
    l1, l2 = plt.plot(t, u, 'b-', t, X_0*np.cos(omega*t), 'r--')
    fig.legend((l1, l2), ('numerical', 'exact'), 'upper left')
    plt.xlabel('t')
    plt.savefig('tmp.pdf'); plt.savefig('tmp.png')
    plt.show()

if __name__ == '__main__':
    demo()


# Code is from Osvaldo Martin et al,
# "Bayesian Modeling and Comptuation In Python"
# https://github.com/aloctavodia/BMCP/blob/master/Code/chp_3_5/splines.py

import superimport

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from patsy import bs, dmatrix
import pyprobml_utils as pml

x = np.linspace(0., 1., 500)
knots = [0.25, 0.5, 0.75]

B0 = dmatrix("bs(x, knots=knots, degree=0, include_intercept=True) - 1", {"x": x, "knots":knots})
B1 = dmatrix("bs(x, knots=knots, degree=1, include_intercept=True) - 1", {"x": x, "knots":knots})
B3 = dmatrix("bs(x, knots=knots, degree=3, include_intercept=True) - 1", {"x": x, "knots":knots})

np.random.seed(1563)
_, ax = plt.subplots(2, 3, figsize=(12, 6), sharex=True,
                     sharey='row')
for idx, (B, title) in enumerate(zip((B0, B1, B3),
                                     ("Piecewise constant", "Piecewise linear", "Cubic spline"))):
    # plot spline basis functions
    for i in range(B.shape[1]):
        ax[0, idx].plot(x, B[:, i], color=str(1-(i+1)/B.shape[1]), lw=2, ls="--")
    # we generate some positive random coefficients (there is nothing wrong with negative values)
    β = np.abs(np.random.normal(0, 1, size=B.shape[1]))
    # plot spline basis functions scaled by its β
    for i in range(B.shape[1]):
        ax[1, idx].plot(x, B[:, i]*β[i],
                        color=str(1-(i+1)/B.shape[1]), lw=2, ls="--")
    # plot the sum of the basis functions
    ax[1, idx].plot(x, np.dot(B, β), color='k', lw=3)
    # plot the knots
    ax[0, idx].plot(knots, np.zeros_like(knots), "ko")
    ax[1, idx].plot(knots, np.zeros_like(knots), "ko")
    ax[0, idx].set_title(title)

pml.savefig('splines_weighted.pdf', dpi=300)
plt.show()


##################################################################################################################
#   ADAPTIVE SUPERRESOLUTION WAVELET (SUPERLET) TRANSFORM
#
#   ORIGINAL AUTHOR:         Harald Bârzan
#   PYTHON TRANSLATION:     Manuel Lima
#   DATE:           June 2021
#   DESCRIPTION:
#
#   Computes the adaptive superresolution wavelet (superlet) transform on
#   input data to produce a time-frequency representation. For each
#   frequency of interest, the closest integer order from the order
#   interval will be chosen to produce each superlet. A superlet is a set
#   of wavelets with the same center frequency but different number of
#   cycles.
#
#   REFERENCE:
#
#   Superlets: time-frequency super-resolution using wavelet sets
#   Moca, V.V., Nagy-Dãbâcan, A., Bârzan, H., Mure?an, R.C.
#   https://www.biorxiv.org/content/10.1101/583732v1.full
#
#   NOTES:
#
#   If the input data consists of multiple buffers, a wavelet spectrum will
#   be computed for each of the buffers and averaged to produce the final
#   result.
#   If the order parameter (ord) is empty, this function will return the
#   standard CWT (one wavelet per frequency of interest).
#
#   INPUT:
#   > input         - input data as numpy array with shape (n_buffers, n_samples)
#   > Fs            - sampling frequency in Hz
#   > F             - frequency-of-interest numpy array with shape (n_frequencies)
#   > Ncyc          - number of initial wavelet cycles (default: 3)
#   > ord           - two-element integer tuple (min_ord, max_ord) (default: None)
#   > mult          - specifies the use of multiplicative superresolution
#                     (False - additive, True - multiplicative)
#
#   OUTPUT:
#   > wtresult      - superlet spectrum as numpy array with shape (n_frequencies, n_samples)
##################################################################################################################
import numba as nb
import numpy as np

# compute the complex wavelet coefficients for the desired time point t, bandwidth bw and center frequency cf
@nb.jit(nb.complex128(nb.float64, nb.float64, nb.float64), nopython=True, fastmath=True)
def bw_cf(t, bw, cf):
    cnorm = 1.0 / (bw * np.sqrt(2 * np.pi))
    exp1 = cnorm * np.exp(-(t**2) / (2 * bw**2))
    return np.exp(2j * np.pi * cf * t) * exp1

# compute the gaussian coefficient for the desired time point t and standard deviation sd
@nb.jit(nb.float64(nb.float64, nb.float64), nopython=True, fastmath=True)
def gauss(t, sd):
    cnorm = 1 / (sd * np.sqrt(2 * np.pi))
    res = cnorm * np.exp(-(t ** 2) / (2 * sd ** 2))
    return res

# computes the complex Morlet wavelet for the desired center frequency Fc with Nc cycles, with a sampling frequency Fs.
@nb.jit(nb.complex128[:](nb.float64, nb.int64, nb.float64), nopython=True, fastmath=True)
def cxmorlet(Fc, Nc, Fs):
    #we want to have the last peak at 2.5 SD
    sd = (Nc / 2) * (1 / Fc) / 2.5
    wl = int(2 * np.floor((6 * sd * Fs) / 2) + 1)
    w = np.zeros(wl, dtype=np.complex128)
    gi = 0
    off = int(wl / 2)

    for i in range(0,wl):
        t = (i - 1 - off) / Fs
        w[i] = bw_cf(t, sd, Fc)
        gi = gi + gauss(t, sd)

    w = w / gi
    return w

def aslt(input, Fs, F, Ncyc, ord=None, mult=True):
    if (ord is None):
        order_ls = np.ones(F.shape[0], dtype=np.int64)
    else:
        order_ls = np.fix(np.linspace(ord[0], ord[1], F.shape[0])).astype(int)

    # get the input size
    Nbuffers, Npoints = input.shape

    padding = 0

    wavelets = [[None for o in range(0,np.max(ord))] for f in range(0,F.shape[0])]

    # initialize wavelet sets for either additive or multiplicative superresolution
    if (mult):
        for i_freq in range(0,F.shape[0]):
            for i_ord in range(0,order_ls[i_freq]):
                # each new wavelet has Ncyc extra cycles (multiplicative superresolution)
                wavelets[i_freq][i_ord] = cxmorlet(F[i_freq], Ncyc * i_ord + 1, Fs)

                # the margin will be the half-size of the largest wavelet
                padding = np.max([padding, int(wavelets[i_freq][i_ord].shape[0] / 2.0)])
    else:
        for i_freq in range(0,F.shape[0]):
            for i_ord in range(0,order_ls[i_freq]):
                # each new wavelet has an extra cycle (additive superresolution)
                wavelets[i_freq][i_ord] = cxmorlet(F[i_freq], Ncyc + i_ord + 1, Fs)

                # the margin will be the half-size of the largest wavelet
                padding = np.max([padding, int(wavelets[i_freq][i_ord].shape[0] / 2.0)])

    # the zero-padded buffer
    buffer = np.zeros(Npoints + 2 * padding)

    # the output scalogram
    wtresult = np.zeros([F.shape[0], Npoints])

    # convenience indexers for the zero-padded buffer
    bufbegin = padding
    bufend = padding + Npoints

    # loop over the input buffers
    for i_buf in range(0, Nbuffers):
        for i_freq in range(0, F.shape[0]):

            # pooling buffer, starts with 1 because we're doing geometric mean
            temp = np.ones(Npoints)

            # fill the central part of the buffer with input data
            buffer[bufbegin:bufend] = input[i_buf, :]

            # compute the convolution of the buffer with each wavelet in the current set
            for i_ord in nb.prange(0, order_ls[i_freq]):
                # restricted convolution (input size == output size)
                tempcx = np.convolve(buffer, wavelets[i_freq][i_ord], mode='same')

                # accumulate the magnitude (times 2 to get the full spectral energy
                temp = temp * (2 * np.abs(tempcx[bufbegin:bufend]) ** 2).T

            # compute the power of the geometric mean
            root = 1 / order_ls[i_freq]
            temp = temp ** root

            # accumulate the current FOI to the result spectrum
            wtresult[i_freq, :] = wtresult[i_freq, :] + temp

    # scale the output by the number of input buffers
    wtresult = wtresult / Nbuffers

    return wtresult


import numpy as np
import scipy.stats as sp

__all__ = ["linear_regression"]

def _lr_statistics(independent_vars, dependent_var, coefficients):
    """Computes t-test and confidence intervals for linear regression."""
    n_groups = independent_vars.shape[0] - 1
    deg_of_freedom = dependent_var.size - n_groups - 1

    sum_squared_total = np.var(dependent_var) * dependent_var.size
    sum_squared_error = np.square(dependent_var
                                  - independent_vars.T @ coefficients).sum()
    sum_squared_residual = sum_squared_total - sum_squared_error

    mean_squared_residual = sum_squared_residual / n_groups
    mean_squared_error = sum_squared_error / deg_of_freedom

    f_statistic = mean_squared_residual / mean_squared_error
    f_p_value = sp.f.sf(f_statistic, n_groups, deg_of_freedom)

    # R-Squared
    r_squared = sum_squared_residual / sum_squared_total
    r_squared_adjust = (1 - mean_squared_error
                        * (dependent_var.size - 1) / sum_squared_total)

    # T-Tests
    estimator = np.linalg.inv(independent_vars @ independent_vars.T)
    estimator *= mean_squared_error
    standard_errors = np.sqrt(np.diag(estimator))

    # Use 2-tailed tests
    t_value = coefficients / standard_errors
    t_p_value = sp.t.sf(np.abs(t_value), deg_of_freedom) * 2
    coefficient_statistics = list(zip(t_value, t_p_value))

    # 95th Percentile confidence intervals
    scalar = sp.t.isf(0.025, deg_of_freedom)
    confidence_interval = np.array([coefficients - scalar * standard_errors,
                                    coefficients + scalar * standard_errors])

    statistics = {
        'DF': (n_groups, deg_of_freedom),
        'F_value': f_statistic, 'P_value': f_p_value,
        'Coefficient T-Tests': coefficient_statistics,
        'RSq': r_squared, 'RSq_adj': r_squared_adjust,
        '95th CI': confidence_interval}

    return statistics

def linear_regression(independent_vars, dependent_var):
    """Performs a least-squares linear regression.

    Args:
        independent_vars: [n_vars x n_observations] array of independent variables
        dependent_var:  dependent variable [n_observations]

    Returns:
        regression_dict: Dictionary with regression parameters and statistics

    Example:
        result = linear_regression(np.vstack((independent1, independent2, ...)),
                                    dependent_y)

    Note:
        Missing data (marked by nan) is removed from all data.
    """
    # Clean the data if necessary
    valid_mask_dependent = ~np.isnan(dependent_var)
    valid_mask_independent = ~np.isnan(independent_vars)
    valid_mask = valid_mask_dependent & np.all(valid_mask_independent, axis=0)

    independent_vars = independent_vars[:, valid_mask]
    dependent_var = dependent_var[valid_mask]

    # Remove Mean and Standard Deviation
    dep_mean = dependent_var.mean()
    dep_std = dependent_var.std(ddof=1)

    idep_mean = independent_vars.mean(axis=1)
    idep_std = independent_vars.std(axis=1, ddof=1)

    # Centered - Scaled Variables
    new_dependent = (dependent_var - dep_mean) / dep_std
    new_independent = (independent_vars - idep_mean[:, None]) / idep_std[:, None]

    # "Beta" (Standardized) Coefficients
    coeffs = (np.linalg.pinv(new_independent.T) @ new_dependent[:, None]).squeeze()
    coeffs = np.atleast_1d(coeffs)

    # Regression Coefficients
    ratio = dep_std / idep_std
    regression_coefficients = np.ones((coeffs.size + 1))
    regression_coefficients[1:] = ratio * coeffs
    regression_coefficients[0] = dep_mean - (regression_coefficients[1:] * idep_mean).sum()

    output = {'Regression Coefficients': regression_coefficients,
              'Standard Coefficients': np.concatenate(([0], coeffs))}

    # Account for the intercept for statistics
    independent_vars = np.vstack((np.ones_like(dependent_var), independent_vars))
    statistics = _lr_statistics(independent_vars, dependent_var,
                                regression_coefficients)
    output.update(statistics)

    return output


#
# @Author: kuroitu (2020)
# @email: skuroitu@gmail.com
#

import numpy as np

from ..dual import *
from .const import pi

##########
# 双曲線関数 hyperbolical functions
#######
def sinh(obj):
    obj = to_dual(obj)
    return Dual(np.sinh(obj.re), obj.im * np.cosh(obj.re))

def cosh(obj):
    obj = to_dual(obj)
    return Dual(np.cosh(obj.re), obj.im * np.sinh(obj.re))

def tanh(obj):
    obj = to_dual(obj)
    return Dual(np.tanh(obj.re), obj.im / np.cosh(obj.re) ** 2)

def arcsinh(obj):
    obj = to_dual(obj)
    return Dual(np.arcsinh(obj.re), obj.im / np.sqrt(1 + obj.re ** 2))

def arccosh(obj):
    obj = to_dual(obj)
    return Dual(np.arccosh(obj.re), obj.im / np.sqrt(obj.re ** 2 - 1))

def arctanh(obj):
    obj = to_dual(obj)
    return Dual(np.arctanh(obj.re), obj.im / (1 - obj.re ** 2))

def csch(obj, switch=True):
    if switch:
        obj = to_dual(obj)
        return Dual(1 / np.sinh(obj.re),
                    - obj.im / (np.tanh(obj.re) * np.sinh(obj.re)))
    return 1 / sinh(obj)

def sech(obj, s=True):
    if s:
        obj = to_dual(obj)
        return Dual(1 / np.cosh(obj.re),
                    - obj.im * np.tanh(obj.re) / np.cosh(obj.re))
    return 1 / cosh(obj)

def coth(obj, s=True):
    if s:
        obj = to_dual(obj)
        return Dual(1 / np.tanh(obj.re),
                    - obj.im / np.sinh(obj.re) ** 2)
    return 1 / tanh(obj)

def arccsch(obj, s=True):
    if s:
        obj = to_dual(obj)
        return Dual(np.arcsinh(1 / obj.re),
                    - obj.im/(obj.re ** 2 * np.sqrt(1 + 1 / obj.re ** 2)))
    return arcsinh(1 / obj)

def arcsech(obj, s=True):
    obj = to_dual(obj)
    return Dual(np.arccosh(1 / obj.re),
                - obj.im / (obj.re ** 2 * np.sqrt(1 / obj.re - 1)
                         * np.sqrt(1 / obj.re + 1)))

def arccoth(obj):
    obj = to_dual(obj)
    return Dual(np.arctanh(1 / obj.re), obj.im / (1 - obj.re ** 2))

def sinch(obj, norm=True):
    if norm:
        obj = to_dual(obj)
        return Dual(np.sinh(obj.re * pi) / (obj.re * pi),
                    obj.im * (obj.re * np.cosh(obj.re * pi)
                              - np.sinh(obj.re * pi) / pi) / obj.re ** 2)
    else:
        obj = to_dual(obj)
        return Dual(np.sinh(obj.re) / obj.re,
                    obj.im * (obj.re * np.cosh(obj.re)
                              - np.sinh(obj.re)) / obj.re ** 2)


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')

xs = np.array([1,2,3,4,5,6], dtype = np.float64)
ys = np.array([5,4,6,5,6,7], dtype = np.float64)

def best_fit_slope_and_intercept(xs, ys):
	m = (  ((mean(xs)*mean(ys)) - mean(xs*ys)) /
		((mean(xs)*mean(xs)) - mean(xs*xs)) )

	b = ( mean(ys) -(m* mean(xs)) )
	return m, b

m, b = best_fit_slope_and_intercept(xs, ys)

regression_line = [(m*x)+b for x in xs]

predict_x = 1.5
predict_y = (m*predict_x + b)

#accuracy =

plt.scatter(xs, ys)
plt.scatter(predict_x, predict_y, color='g')
plt.plot(xs, regression_line)
plt.show()


import numpy as np

def covarianceEllipse(cov_mat,img_dim = (1,1), center_point = (0,0)):
    eigenvalues, eigenvectors = np.linalg.eig(cov_mat)
    theta = np.linspace(0, 2*np.pi, 1000)
    ellipse = (np.sqrt(eigenvalues[None,:]) * eigenvectors) @ [np.sin(theta), np.cos(theta)]
    ellipse[0,:] = ellipse[0,:]*img_dim[0]
    ellipse[1,:] = ellipse[1,:]*img_dim[1]
    print(center_point[0],center_point[1],"\n---------------------\n")
    for i in range(ellipse.shape[1]):
        ellipse[0,i] += center_point[0]
        ellipse[1,i] += center_point[1]

    return ellipse


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 2021 Rex McAllister
# https://downloads.rene-schwarz.com/download/M001-Keplerian_Orbit_Elements_to_Cartesian_State_Vectors.pdf
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Imports
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

from tletools import TLE
import matplotlib.pyplot as plt
import numpy as np
import math
from math import *
import sys

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Constants
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

gravParam = 3.986004418 * (10 ** 14)
pi = math.pi
toRad = pi / 180
toDec = 180 / pi

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Variables
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# TLE to Keplerian Elements
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# converts the longitude of the ascending node from TLE degrees to radians
def calc_lan(tle):
    lan = (tle.raan * toRad)
    return lan

# converts the argument of perigee from TLE degrees to radians
def calc_argp(tle):
    argp = (tle.argp * toRad)
    return argp

# converts the inclination from TLE degrees to radians
def calc_inc(tle):
    inc = (tle.inc * toRad)
    return inc

# returns the native format of eccentricity from TLE
def calc_eccentricity(tle):
    ecc = (tle.ecc)
    return ecc

# converts the mean motion from TLE rev/day to rad/s
def calc_n(tle):
    n = float(tle.n) * ((1.0/86400.0) * (2.0 * pi))
    return n

# calculates the length of the semimajor axis in kilometers
def calc_sma(tle):
    n = calc_n(tle)
    sma = (gravParam / (n ** 2)) ** (1.0/3.0)
    return sma

# converts the mean anomaly from TLE degrees to radians
def calc_M(tle):
    M = tle.M * toRad
    return M

# calculates the eccentric anomaly (rad) using a Newton-Rhapson setup
def calc_E(tle):
    M = calc_M(tle)
    ecc = calc_eccentricity(tle)
    E = M
    nextE = E - ((E - (ecc * sin(E - M))) / (1 - ecc * cos(E)))
    while (abs((nextE - E) / nextE) > 1e-14):
        E = nextE
        # both versions of the equation yield the same answer
        # nextE = (((M) - (ecc * (E * math.cos(E) - math.sin(E)))) / (1 - ecc * math.cos(E)))
        nextE = E - ((E - (ecc * sin(E)) - M) / (1 - (ecc * cos(E))))
    return E

# calculates the true anomaly (rad)
def calc_truea(tle):
    M = calc_M(tle)
    E = calc_E(tle)
    ecc = calc_eccentricity(tle)

    truea = 2 * atan2(sqrt(1 + ecc) * sin(E / 2), sqrt(1 - ecc) * cos(E / 2))

    return truea

def calc_truea_deltaT(tle, deltaTSeconds) :
    M = calc_M(tle)
    ecc = calc_eccentricity(tle)
    n = calc_n(tle)
    M = M + (n * deltaTSeconds)
    # same as calc_E except is propogates the mean anomaly forward in time
    E = M
    nextE = E - ((E - (ecc * sin(E - M))) / (1 - ecc * cos(E)))
    while (abs((nextE - E) / nextE) > 1e-14):
        E = nextE
        # both versions of the equation yield the same answer
        # nextE = (((M) - (ecc * (E * math.cos(E) - math.sin(E)))) / (1 - ecc * math.cos(E)))
        nextE = E - ((E - (ecc * sin(E)) - M) / (1 - (ecc * cos(E))))
    v = 2 * atan2(sqrt(1 + ecc) * sin(E / 2), sqrt(1 - ecc) * cos(E / 2))
    return v

# creates an array of the value of true anomaly for a period of time over the orbit
def calc_truea_time(tle, increment, numSeconds):
    E = calc_E(tle)
    M = calc_M(tle)
    ecc = calc_eccentricity(tle)
    n = calc_n(tle)
    epochX = []
    trueaY = []

    def my_range(start, end, step):
        while start <= end:
            yield start
            start += step
    for i in my_range(0, numSeconds, 60):

        epochX.append(i / 60.0)
        trueaY.append(calc_truea_deltaT(tle, i))
    return epochX, trueaY

# returns a dictionary of calculated orbital elements for a TLE
def tleToKepler(tle):

    lan = calc_lan(tle)           # longitude ascending node
    argp = calc_argp(tle)         # argument periapsis
    inc = calc_inc(tle)           # inclination
    ecc = calc_eccentricity(tle)  # eccentricity
    n = calc_n(tle)               # mean motion
    M = calc_M(tle)               # mean anomaly
    a = calc_sma(tle)             # semimajor axis
    E = calc_E(tle)               # eccentric anomaly
    v = calc_truea(tle)           # true anomaly

    return lan, argp, inc, ecc, n, M, a, E, v


import numpy as np
import numpy.linalg as la

def GOE(N):
	H1 = np.triu(np.random.normal(scale=1,size=(N,N)))
	H = H1+H1.T
	H[range(N),range(N)] /=np.sqrt(2)
	return H

def GUE(N):
	H0 = np.random.normal(scale=np.sqrt(0.5),size=(N,N))
	H1 = np.triu(H0,k=1)
	H2 = np.tril(H0,k=-1)
	H3 = np.diag(np.diag(H0))*np.sqrt(2)
	H = H1+H1.T+np.complex(0,1)*H2+np.complex(0,-1)*H2.T+H3;
	return H

def Wigner_Real(N):
	H1 = np.triu(np.random.normal(scale=1,size=(N,N)))
	H = H1+H1.T
	H[range(N),range(N)] /=2
	return H

def Wigner_Complex(N,alpha):
	H0 = np.random.normal(scale=np.sqrt(1),size=(N,N))
	H1 = np.triu(H0,k=1)
	H2 = np.tril(H0,k=-1)
	H3 = np.diag(np.diag(H0))
	H = np.sqrt(alpha)*(H1+H1.T)+np.sqrt(1-alpha)*(np.complex(0,1)*H2+np.complex(0,-1)*H2.T)+H3;
	return H

def space(H):
	N = H.shape[0]
	eigs = np.real(la.eigvalsh(H))
	eigs.sort()
	space_H = eigs[1:]-eigs[:-1]
	return space_H

def mean_space(N, sample = 200, mtype = 'GOE'):
	m_space = np.zeros(N-1)
	for i in range(sample):
		if mtype == 'GOE':
			m_space += space(GOE(N))
		if mtype == 'GUE':
			m_space += space(GUE(N))
	m_space/=sample
	return m_space

def multi_space(N, sample = 200, mtype = 'GOE'):
	m_space = np.zeros([sample,N-1])
	for i in range(sample):
		if mtype == 'GOE':
			m_space[i] = space(GOE(N))
		if mtype == 'GUE':
			m_space[i] = space(GUE(N))
	return m_space


import numpy as np

def f(x):
    y = 1.2 * x + 2
    return y

def mean_absolute_eror(f, points):
    summed = 0.0
    m = len(points)
    for point in points:
        x, y = point
        yp = f(x)
        summed += np.abs(yp - y)
    return summed / m

def mean_squared_error(f, points):
    summed = 0.0
    m = len(points)
    for point in points:
        x, y = point
        yp = f(x)
        summed += ((yp - y)**2.0)
    return summed / (2.0 * m)

if __name__ == '__main__':

    test_points = [(2, -2), (5, 6), (-4, -4), (-7, 1), (8, 14)]
    mae = mean_absolute_eror(f, test_points)
    mse = mean_squared_error(f, test_points)
    print("MAE", mae)
    print("MSE", mse)


# A x = b
# 1. Elimination
# 2. Back Substitution

import numpy as np

A = np.array([[3,-2,5,0],
              [4,5,8,1],
              [1,1,2,1],
              [2,7,6,5]], float)

b = np.array([2,4,5,7], float)
n = len(b)

# Solution vector
x = np.zeros(n, float)

# 1. Elimination

for k in range(n-1): # to index fixed rows and cols
    for i in range(k+1,n):

        # if the element is already 0, skip to next i
        if A[i,k] == 0:
            continue
        factor = A[k,k] / A[i,k]

        for j in range(k,n):
            A[i,j] = A[k,j] - A[i,j]*factor

        b[i] = b[k] - b[i]*factor

# 2. Back Substitution

# starting from last row
x[n-1] = b[n-1] / A[n-1, n-1]

for i in range(n-2,-1,-1):
    sum_ax = 0
    for j in range(i+1,n):
        sum_ax += A[i,j] * x[j]
    x[i] = (b[i] - sum_ax) / A[i,i]

print('The Solution of the system:')

for i in range(n):
    print('{}. {:.2f}'.format(i, x[i]))


import numpy as np

def reimp():
  """Reimport this module"""
  import importlib
  importlib.reload('ch01.startr')

def pv_f(fv, r, n):
  """Objective: estimate present value
  fv  : = future value
  r   : = rate of discount
  n   : = num of iterations (usually num of years, num of months etc)
                    fv
  formula : pv =  --------
                   (1 + r) ^ n

  """
  pv = 0
  iCount = 0
  for cash in fv:
    pv = pv + cash / (1 + r) ** iCount
    iCount = iCount + 1

  # you can use enumeration too
  pv2 = 0
  for i2, cashFlow in enumerate(fv):
    pv2 = pv2 + cashFlow / (1 + r) ** i2

  return (pv, pv2)

def np_itr_a() :
  """ create an array using numpy and itrate"""
  cashFlows = np.array([1, 20])
  for cash in cashFlows:
    print(cash + 100)

def calc():
  fv = [-100,-30, 10, 40,50, 45,20]
  rate = 3.5 / 100
  cycles = 100

  pv1, pv2 = pv_f(fv, rate, cycles)
  print("Present value at rate=", rate, " where fv=", fv )
  print("      -- is 1:",pv1)
  print("      -- is 2:",pv2)

  pv3, _ = pv_f(fv, rate, cycles)
  print("Present value at rate=", rate, " where fv=", fv )
  print("      -- is 1:",pv3)


import numpy as np
from matplotlib import pyplot as plt
import sys
X = np.array([
    [-2,4,-1],
    [4,1,-1],
    [1, 6, -1],
    [2, 4, -1],
    [6, 2, -1],

])
#X1 = np.array([
#    [-2,4],
#    [4, 1],
#    [1, 6],
#    [2, 4],
#    [6, 2]
#
#])
y = np.array([-1,-1,1,1,1])
#print(X1.shape)
#print(y.shape)
#
#plt.scatter(X1[:,0],y)
#plt.scatter(X1[:, 1],y)
#plt.title("Pricing Bids")
#plt.xlabel('Price')
#plt.ylabel('Status (1:Won, 0:Lost)')
#plt.show()

#sys.exit(-1)
def perceptron_sgd(X, Y):
    w = np.zeros(len(X[0]))
    eta = 1
    epochs = 20

    for t in range(epochs):
        for i, x in enumerate(X):
            if (np.dot(X[i], w)*Y[i]) <= 0:
                w = w + eta*X[i]*Y[i]

    return w

def perceptron_sgd_plot(X, Y):
    '''
    train perceptron and plot the total loss in each epoch.

    :param X: data samples
    :param Y: data labels
    :return: weight vector as a numpy array
    '''
    w = np.zeros(len(X[0]))
    eta = 1
    n = 30
    errors = []

    for t in range(n):
        total_error = 0
        for i, x in enumerate(X):
            if (np.dot(X[i], w)*Y[i]) <= 0:
                total_error += (np.dot(X[i], w)*Y[i])
                w = w + eta*X[i]*Y[i]
        errors.append(total_error*-1)

    plt.plot(errors)
    plt.xlabel('Epoch')
    plt.ylabel('Total Loss')

    return w

w = perceptron_sgd_plot(X,y)
print(w)

for d, sample in enumerate(X):
    # Plot the negative samples
    if d < 2:
        plt.scatter(sample[0], sample[1], s=120, marker='_', linewidths=2)
    # Plot the positive samples
    else:
        plt.scatter(sample[0], sample[1], s=120, marker='+', linewidths=2)

# Add our test samples

plt.scatter(2,2, s=120, marker='_', linewidths=2, color='yellow')
plt.scatter(4,3, s=120, marker='+', linewidths=2, color='blue')

# Print the hyperplane calculated by perceptron_sgd()
x2=[w[0],w[1],-w[1],w[0]]
x3=[w[0],w[1],w[1],-w[0]]

x2x3 =np.array([x2,x3])
X,Y,U,V = zip(*x2x3)
ax = plt.gca()
ax.quiver(X,Y,U,V,scale=1, color='blue')
plt.show()