import numpy as np

def runge_kutta_explicit(order,alpha=1):
    """ Coefficient of the explicit Runge-Kutta methods.

    :param int order: Order of the Runge-Kutta method
    :returns: Coefficient of the Butcher table and the number of stage
    :rtype: tuple(a,b,c,Nstage)
    """
    if order == 4:
        a = np.array([[0,0,0,0],[0.5,0,0,0],
                      [0,0.5,0,0],[0,0,1,0]])
        b = np.array([1.0/6.0,1.0/3.0,1.0/3.0,1.0/6.0])
        c = np.array([1,0.5,0.5,0])
        Nstage = 4
    elif order == 3:
        a = np.array([[0,0,0],[0.5,0,0],[-1,2,0]])
        b = np.array([1.0/6.0,2.0/3.0,1.0/6.0])
        c = np.array([1,0.5,0])
        Nstage = 3
    elif order == 2:
        # if alpha == 0.5, heun's method
        # if alpha == 1, midpoint method
        a = np.array([[0,0],[alpha,0]])
        b = np.array([1-0.5/alpha,0.5/alpha])
        c = np.array([0,alpha])
        Nstage = 2
    elif order == 1:
        # Euler explicit
        a = np.array([[0]])
        b = np.array([1])
        c = np.array([0])
        Nstage = 1

    return a,b,c,Nstage


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

import numpy as np

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

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

    Raises:
        RuntimeError if A is not upper triangular

    Returns:
        A numpy array with the system's X vector. Shape: (n,1)
    """
    if np.allclose(A, np.triu(A)):
        n = len(b)
        x = np.zeros((n, 1))

        for k in reversed(range(0, n)):

            tempSum = []
            for number in range(k + 1, n):
                tempSum.append(-1 * A[k][number] * x[number])

            tempSum = sum(tempSum)
            x[k] = (b[k] + tempSum) / A[k][k]

        return x

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

def test_linealSolverBackwards():
    """Test linealSolverBackwards.

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

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

    upperTriangMatrices = (
        (np.array([[12, 5, 6], [0, 1, -4], [0, 0, 9]]),
         np.array([[-37], [2], [9]]),
         np.array([[-73 / 12], [6], [1]])),
        (np.array([[-8, 35, 65], [0, 40, -64], [0, 0, 64]]),
         np.array([[20], [12], [43]]),
         np.array([[4595 / 512], [11 / 8], [43 / 64]])),
        (np.array([[-72, 8, 64, 91], [0, 70, -90, -27],
                   [0, 0, -39, -22], [0, 0, 0, -22] ]),
         np.array([ [-40], [18], [-47], [-2] ]),
         np.array([ [3499 / 1848], [3555 / 2002], [15 / 13], [1 / 11] ]))
    )

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

            ans = linealSolverBackwards(system[0], system[1])
            comparison = np.allclose(ans, system[2])
            assert \
                    comparison, \
                    "The last tested system did not pass the test."

            print("-> Pass <-")
        except RuntimeError:
            assert False, "The last tested system did not pass the test."

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


# -*- coding: utf-8 -*-
"""
Coursework 6: Phase space
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import simps
from numpy import trapz
os.getcwd()

# Compute the derivative dq from an array of consecutive points q with initial
# derivative dq0 with timestep d
def deriv(q,dq0,d=0.001):
   dq = (q[1:len(q)]-q[0:(len(q)-1)])/d
   dq = np.insert(dq,0,dq0)
   return dq

# Differential equation
def F(q):
    ddq = -2*q*(q**2-1)
    return ddq

# Compute the orbit of the differential equation F with initial values q0 and dq0,
# n points and timestep d
def orb(n,q0,dq0, F, d=0.001):
    q = np.empty([n+1])
    q[0] = q0
    q[1] = q0 + dq0*d
    for i in np.arange(2,n+1):
        args = q[i-2]
        q[i] = - q[i-2] + d**2*F(args) + 2*q[i-1]
    return q

# Plot an orbit of the differential equation F with initial values q0 and dq0,
# n points and timestep d with color col and linestyle marker
def simplectica(q0,dq0,F,col=0,d = 10**(-4),n = int(16/10**(-4)),marker='-'):
    q = orb(n,q0=q0,dq0=dq0,F=F,d=d)
    dq = deriv(q,dq0=dq0,d=d)
    p = dq/2
    plt.plot(q, p, marker,c=plt.get_cmap("winter")(col))

# Compute the period of the array q with timestep d, starting the period in a
# maximum (max=True) or a minimum (min=False)
def periods(q,d,max=True):
    epsilon = 5*d
    dq = deriv(q,dq0=None,d=d)
    if max == True:
        waves = np.where((np.round(dq,int(-np.log10(epsilon))) == 0) & (q >0))
    if max != True:
        waves = np.where((np.round(dq,int(-np.log10(epsilon))) == 0) & (q <0))
    diff_waves = np.diff(waves)
    waves = waves[0][1:][diff_waves[0]>1]
    pers = diff_waves[diff_waves>1]*d
    return pers, waves

# Compute and aproxmation of the area inside the orbit with initial values q0, dq0 and
# timestep d. Use method of integration funcarea and plot=True to plot the orbit
def area(q0,dq0,d,funcarea, plot=False):
    n = int(64/d)
    q = orb(n,q0=q0,dq0=dq0,F=F,d=d)
    dq = deriv(q,dq0=dq0,d=d)
    p = dq/2

    if plot:
        # Plot orbit
        fig, ax = plt.subplots(figsize=(5,5))
        plt.rcParams["legend.markerscale"] = 6
        ax.set_xlabel("q(t)", fontsize=12)
        ax.set_ylabel("p(t)", fontsize=12)
        plt.plot(q, p, '-')
        plt.show()

    # Compute period of orbit between minima
    T, W = periods(q,d,max=False)

    if plot:
        # Plot one period of the orbit
        plt.plot(q[W[0]:W[1]],p[W[0]:W[1]])
        plt.show()

    # Take half of the period obit
    mitad = np.arange(W[0],W[0]+np.int((W[1]-W[0])/2),1)

    # Integrate with numeric method
    area = funcarea(p[mitad],q[mitad])
    return 2*area

# Compute with Shoelace formula area of polygon with vertices (x,y)
def areapol(x,y):
    return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))

# Compute the 90th-percentile of array areas
def error(areas):
    errors = np.zeros(10)
    for i in range(1,len(areas)):
        errors[i-1] = np.abs(areas[i-1] - areas[i])
    errors = np.sort(errors)
    return errors[8]

#%%
"""
Plot numerical solution
"""

# Example of a solution
q0 = 0.
dq0 = 1.
fig, ax = plt.subplots(figsize=(12,5))
plt.ylim(-1.8, 1.8)
plt.rcParams["legend.markerscale"] = 6
ax.set_xlabel("t = n $\delta$", fontsize=12)
ax.set_ylabel("q(t)", fontsize=12)
iseq = np.array([1,1.1,1.5,1.8,3])
for i in iseq:
    d = 10**(-i)
    n = int(32/d)
    t = np.arange(n+1)*d
    q = orb(n,q0=q0,dq0=dq0,F=F,d=d)
    plt.plot(t, q, 'ro', markersize=0.5/i,label='$\delta$ ='+str(np.around(d,3)),
             c=plt.get_cmap("winter")(i/np.max(iseq)))
    ax.legend(loc=3, frameon=False, fontsize=12)

# Orbit of the solution in phase space (q, p)
dq = deriv(q,dq0=dq0,d=d)
p = dq/2
fig, ax = plt.subplots(figsize=(5,5))
plt.rcParams["legend.markerscale"] = 6
ax.set_xlabel("q(t)", fontsize=12)
ax.set_ylabel("p(t)", fontsize=12)
plt.plot(q, p, '-')
plt.show()

#%%
"""
Exercise 1: Plot phase space
"""

fig = plt.figure(figsize=(8,5))
fig.subplots_adjust(hspace=0.4, wspace=0.2)

# 12 orbits in D0 = [0,1]x[0,1] (p0 = dq0/2)
seq_q0 = np.linspace(0.,1.,num=12)
seq_dq0 = np.linspace(0.,2,num=12)
for i in range(len(seq_q0)):
    for j in range(len(seq_dq0)):
        q0 = seq_q0[i]
        dq0 = seq_dq0[j]
        ax = fig.add_subplot(1,1, 1)
        col = (1+i+j*(len(seq_q0)))/(len(seq_q0)*len(seq_dq0))
        simplectica(q0=q0,dq0=dq0,F=F,col=col,marker='ro',d= 10**(-3),n=int(16/d))
ax.set_xlabel("q(t)", fontsize=12)
ax.set_ylabel("p(t)", fontsize=12)
plt.show()

#%%
"""
Exercise 2: Area of the phase space
"""

# Values of timestep d to vary
deltavec = np.linspace(10**-4,10**-3,11)

# Compute the area of the orbits with intial values in D0

# with trapezoid integration method
i = 0
areastrap = np.zeros(11)
for d in deltavec:
    areamax = area(0.,2.,d,trapz)
    areamin = area(0.,10**(-10),d,trapz)
    areastrap[i] = areamax - areamin/2
    i += 1

print("The area with the trapezoid rule is",
      "{:.3g} - {:.1g}".format(areastrap[0],error(areastrap)))

# with Simpson integration method
i = 0
areassimps = np.zeros(11)
for d in deltavec:
    areamax = area(0.,2.,d,simps)
    areamin = area(0.,10**(-10),d,simps)
    areassimps[i] = areamax - areamin/2
    i += 1

print("The area with the Simpson rule is",
      "{:.3g} - {:.1g}".format(areassimps[0],error(areassimps)))

# Check Liouville theorem for D0 = [0,1]x[0,1]
points = 1000
x = np.concatenate((np.zeros(points),np.linspace(0,1,points),
                    np.ones(points), np.linspace(1,0,points)))
y = np.concatenate((np.linspace(0,1,points),np.ones(points),
                    np.linspace(1,0,points), np.zeros(points)))

# Print area of D0
print("The area of D0 is {:.2g}".format(areapol(x,y)))

plt.figure()
plt.axis('scaled')
plt.scatter(x,y)

# Evolve D0 n steps
n = 2000
x2 = np.zeros_like(x)
y2 = np.zeros_like(y)
for i in range(0,len(x)):
    q0 = x[i]
    dq0 = y[i]/2
    q = orb(n,q0,dq0,F)
    dq = deriv(q,dq0)
    x2[i] = q[n]
    y2[i] = dq[n]*2

plt.figure()
plt.scatter(x2,y2)

# Print area of Dn
print("The area of Dn is {:.2g}".format(areapol(x2,y2)))


# -*- coding: utf-8 -*-
"""
Created on Wed Feb 20 15:46:19 2019

@author: Ham
The inner tool returns the inner product of two arrays.

import numpy

A = numpy.array([0, 1])
B = numpy.array([3, 4])

print numpy.inner(A, B)     #Output : 4
outer

The outer tool returns the outer product of two arrays.

import numpy

A = numpy.array([0, 1])
B = numpy.array([3, 4])

print numpy.outer(A, B)     #Output : [[0 0]
                            #          [3 4]]
Task

You are given two arrays: A and B.
Your task is to compute their inner and outer product.

Input Format

The first line contains the space separated elements of array A.
The second line contains the space separated elements of array B.

Output Format

First, print the inner product.
Second, print the outer product.

Sample Input

0 1
2 3
Sample Output

3
[[0 0]
 [2 3]]

"""

import numpy

if __name__ == '__main__':
    a = [0, 1]
    b = [2, 3]
    #a = [int(x) for x in input().split()]
    #b = [int(x) for x in input().split()]
    print(numpy.inner(a, b))
    oter = numpy.outer(a, b)
    print(oter)
    #print("[" + str(oter[0]))
    #for e in oter[1:-2]:
    #    print(" " + str(e))
    #print(" " + str(oter[-1]) + "]")


import numpy as np
from matplotlib import pyplot as plt

# Generate time series from the logistic map
# ------------------------------------------------------------
# T                 length of time series
# x0                initial x value of the trajectory
# r                 r value of the logistic map
def GenerateTS(T, x0, r):
    X = np.zeros(T, dtype=np.float64)
    X[0] = x0
    for i in range(1, T):
        X[i] = r * X[i - 1] * (1 - X[i - 1])
    return X

# Plot the bifurcation diagram of the logistic map
# ------------------------------------------------------------
# rs                range of r values to use when plotting
# x0                initial x value of the trajectory
# N                 total length of time series to use (must be bigger than cutoff i.e. >500)
def BifurcationDiagram(rs, x0, N):
    cutoff = 500
    Xs = np.zeros((len(rs), N-cutoff))
    Rs = np.ones((len(rs), N-cutoff))
    for i, r in enumerate(rs):
        Rs[i, :] *= r
        Xs[i, :] = GenerateTS(N, x0, r)[cutoff:]
    Xs = Xs.flatten()
    Rs = Rs.flatten()
    plt.rc('text', usetex=True)
    plt.rc('font', family='serif', size=20)
    plt.figure(figsize=(8,6))
    plt.plot(Rs, Xs, 'k.', markersize=0.1)
    plt.axis([rs[0], rs[-1], 0, 1])
    plt.xlabel('$r$', fontsize=28)
    plt.ylabel('$X^*$', fontsize=28)
    plt.tight_layout()
    plt.savefig('LogisticBifurcationDiagram.png', dpi=200)
    plt.show()


from non_linear_equations import Equation
import numpy as np
import numpy.linalg
from sympy import diff

class EquationSolver():

    def __init__(self, equation ):
        self.equation = equation

        self.__jacobian_matrix = np.ndarray( shape=(len(equation), len(equation)), dtype = type(equation) )
        self.__initiate_jacobian_matrix()

    def __initiate_jacobian_matrix(self):

        iter = 0
        for column in self.__jacobian_matrix:
            atual_symbol_diff = self.equation.symbols_set[iter]
            self.__jacobian_matrix[:,iter] = [ diff(expr, atual_symbol_diff ) for expr in self.equation ]
            iter +=1

    def solve(self,
              initial_values,
              stop_criterion,
              max_iterations = 30,
              stop_criterion_value = 0.01,
              generate_output_info = False):

        method_converged = False
        old_values = initial_values

        log_output = []
        if( generate_output_info ):
            log_output.append( np.array( old_values ) )

        for i in range( max_iterations ):
            new_values = self.__next_iter( old_values )

            error_value = stop_criterion(new_values, old_values)

            old_values = new_values

            if( error_value <= stop_criterion_value ):
                method_converged = True
                break

        if( method_converged == False ):
            print( "WARNING: Method did not converge")

        return new_values

    def __next_iter(self, old_values):
        return old_values + self.__evaluate_determinants_vector(old_values)

    def __evaluate_determinants_vector(self, values):
        jacobian_matrix = self.__evaluate_jacobian_at( values )
        equation_value = -1*self.equation.at(values)
        result = np.linalg.solve( jacobian_matrix, equation_value  )

        return result

    def __evaluate_jacobian_at(self, values):
        subs = { symbol:value for(symbol, value) in zip(self.equation.symbols_set, values) }
        result = [ [ float( expr.evalf(subs=subs) ) for expr in row ] for row in self.__jacobian_matrix ]
        return np.array( result )

    @classmethod
    def max_absolute_error(cls, values1, values2 ):
        return np.max( np.abs( values1-values2 ) )

    @staticmethod
    def max_relative_error(values1, values2):
        return np.max(np.abs( (values1 - values2)/values1 ) )

    @property
    def jacobian_matrix(self):
        return self.__jacobian_matrix


import numpy as np
def action_evaluation(env, gamma, v):
    '''
    Convert V value to Q value with model.

    Inputs:
    env: OpenAI Gym environment
            env.P: dictionary
                    P[state][action] is list of tuples. Each tuple contains probability, nextstate, reward, terminal
                    probability: float
                    nextstate: int
                    reward: float
                    terminal: boolean
            env.nS: int
                    number of states
            env.nA: int
                    number of actions
    gamma: float
            Discount value
    v: numpy ndarray
            Values of states

    Outputs:
    q: numpy ndarray
            Q values of all state-action pairs
    '''

    nS = env.nS
    nA = env.nA
    q = np.zeros((nS, nA))
    P=env.P
    for s in range(nS):
        for a in range(nA):
            ############################
            # YOUR CODE STARTS HERE
            #[probability, nextstate, reward, terminal]=P[s][a]
            q_s_a = 0
            for i in range(len(P[s][a])):
                next_state_tuple = P[s][a][i]
                v_next_state = v[next_state_tuple[1]]
                p_next_state = next_state_tuple[0]
                reward_next_state = next_state_tuple[2]
                q_s_a+=  p_next_state*(reward_next_state + gamma*v_next_state)
            q[s][a] = q_s_a
            # YOUR CODE ENDS HERE
            ############################
    return q

def action_selection(q):
    '''
    Select action from the Q value

    Inputs:
    q: numpy ndarray

    Outputs:
    actions: int
            The chosen action of each state
    '''

    actions = np.argmax(q, axis = 1)
    return actions

def extract_policy(env, v, gamma):

    """ Extract the optimal policy given the optimal value-function
    Parameters:
    ----------
    env: OpenAI env.

    v: np.ndarray
        value function

    gamma: float
        Discount factor. Number in range [0, 1)
    Returns:
    ----------
    policy: np.ndarray
    """

    policy = np.zeros(env.nS, dtype=int)

    ############################
    # YOUR CODE STARTS HERE
    q = action_evaluation(env, gamma, v)
    policy = action_selection(q)
    # YOUR CODE ENDS HERE
    ############################
    return policy


import numpy as np
from dpsniper.mechanisms.abstract import Mechanism

def _identity(x):
    return x.item(0)

class LaplaceMechanism(Mechanism):

    def __init__(self, fun=_identity, eps: float = 0.1):
        """
        Create a Laplace mechanism.

        Args:
            fun: The function performed before adding noise. The function must accept a 1d array and produce a scalar.
            eps: target epsilon
        """
        self.fun = fun
        self.scale = 1.0 / eps

    def m(self, a, n_samples: int = 1):
        loc = self.fun(a)
        return np.random.laplace(loc=loc, scale=self.scale, size=n_samples)

class LaplaceFixed(Mechanism):

    def __init__(self, fun=_identity, eps: float = 0.1, B: int = 100):
        """
        Create a safe variant of the Laplace mechanism implementing the Snapping mechanism [1] to prevent
        vulnerabilities arising from floating-point arithmetic.

        [1] Mironov, Ilya. "On Significance of the Least Significant Bits for Differential Privacy."
            In Proceedings of the 2012 ACM Conference on Computer and Communications Security - CCS ’12.
            https://doi.org/10.1145/2382196.2382264.

        Args:
            fun: The function performed before adding noise. The function must accept a 1d array and produce a scalar.
            eps: target epsilon
            B: parameter restricting the output of fun to [-B, B]
        """
        self.fun = fun
        self.B = B

        # compute scale such that the resulting mechanism is eps-DP according to Theorem 1 in [1]
        self.scale = (1.0 + (self.B * (2 ** (-49)))) / eps
        assert(self.scale < self.B < ((2.0 ** 46) * self.scale))

        # find smallest power of 2 greater than or equal to scale
        self.Lambda = 2.0**(-20)
        while self.Lambda < self.scale:
            self.Lambda = self.Lambda * 2

    def clamp(self, x):
        if isinstance(x, float) or isinstance(x, int):
            return min(self.B, max(-self.B, x))
        else:
            return np.clip(x, -self.B, self.B)

    def round_to_Lambda(self, x):
        x = x / self.Lambda
        x = np.round(x)
        return x * self.Lambda

    def m(self, a, n_samples: int = 1):
        loc = self.clamp(self.fun(a))
        sign = 1 - (np.random.randint(2, size=n_samples) * 2)
        u = np.random.uniform(size=n_samples)
        assert(u.dtype == np.float64)   # ensure machine epsilon is 2^(-53) as required by Theorem 1 in [1]
        intermediate = loc + sign * self.scale * np.log(u)
        return self.clamp(self.round_to_Lambda(intermediate))


import numpy as np
from numpy import linalg as la
import invprob.sparse as sparse

def fb_lasso(A, y, reg_param, iter_nb, x_ini=None, inertia=False, verbose=False):
    ''' Use the Forward-Backward algorithm to find a minimizer of:
             reg_param*norm(x,1) + 0.5*norm(Ax-y,2)**2
        Eventually outputs the functional values and support of the iterates
        while running the method
        reg_param is either a number, in which case we use it all along the iterations
                  or a sequence of size iter_nb
    '''
    # Manage optional input/output
    if verbose:  # Optional output
        regret = np.zeros(iter_nb)
        sparsity = np.zeros(iter_nb)
        support = []
        path = np.zeros((A.shape[1], iter_nb))
    if x_ini is not None:  # Optional initialization
        x = x_ini
    else:
        x = np.zeros((A.shape[1], 1))
    if isinstance(reg_param, (int, float)):  # Fixed or not parameter
        param = reg_param * np.ones(iter_nb)
    else:
        param = reg_param
    if inertia:
        alpha = [k/(k+3) for k in np.arange(iter_nb)] # asymptotically equivalent to Nesterov
    else:
        alpha = np.zeros(iter_nb) # no inertia

    # The core of the algorithm
    stepsize = 0.5 * 2 / (la.norm(A, 2)**2)
    T = A.T@A
    ATy = A.T@y
    gradient = lambda x: x - stepsize*(T@x - ATy)
    forward_backward = lambda x, param: sparse.soft_thresholding(gradient(x), param*stepsize)
    x_old = x
    for k in range(iter_nb):
        if verbose:
            regret[k] = 0.5 * la.norm(A@x - y, 2)**2 + param[k] * la.norm(x, 1)
            support.append( tuple(np.where(np.abs(x) > 1e-15)[0]) )
            sparsity[k] = len(support[k])
            path[:, k] = x.reshape((x.shape[0]))
        x, x_old = forward_backward( (1+alpha[k])*x - alpha[k]*x_old, param[k] ), x

    # Output
    if verbose:
        details = {
            "function_value": regret,
            "iterate_support": support,
            "iterate_sparsity": sparsity,
            "iterate_path": path
        }
        return x, details
    else:
        return x


#!/usr/bin/env python3
# encoding: utf-8

"""
A module for manipulating vectors
"""

import math
import numpy as np

from zmats.exceptions import VectorsError

def get_angle(v1: list,
              v2: list,
              units: str = 'degs',
              ) -> float:
    """
    Calculate the angle between two vectors.

    Args:
         v1 (list): Vector 1.
         v2 (list): Vector 2.
         units (str, optional): The desired units, either 'rads' for radians, or 'degs' for degrees.

    Returns:
        float: The angle between ``v1`` and ``v2`` in the desired units.

    Raises:
        ZMatError: If ``v1`` and ``v2`` are of different lengths.
    """
    if len(v1) != len(v2):
        raise VectorsError(f'v1 and v2 must be the same length, got {len(v1)} and {len(v2)}.')
    v1_u, v2_u = unit_vector(v1), unit_vector(v2)
    conversion = 180 / math.pi if 'degs' in units else 1
    return float(np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0)) * conversion)

def get_dihedral(v1: list,
                 v2: list,
                 v3: list,
                 units: str = 'degs',
                 ) -> float:
    """
    Calculate the dihedral angle between three vectors.
    ``v2`` connects between ``v1`` and ``v3``.
    Inspired by ASE Atoms.get_dihedral().

    Args:
         v1 (list): Vector 1.
         v2 (list): Vector 2.
         v3 (list): Vector 3.
         units (str, optional): The desired units, either 'rads' for radians, or 'degs' for degrees.

    Returns:
        float: The dihedral angle between ``v1`` and ``v2`` in the desired units.

    Raises:
        ZMatError: If either ``v1`` or ``v2`` have lengths different than three.
    """
    if len(v1) != 3 or len(v2) != 3 or len(v3) != 3:
        raise VectorsError(f'v1, v2, and v3 must have a length of three, got {len(v1)}, {len(v2)}, and {len(v3)}.')
    v1, v2, v3 = np.array(v1, np.float64), np.array(v2, np.float64), np.array(v3, np.float64)
    v2_x_v1 = np.cross(v2, v1)
    v2_x_v1 /= float(np.linalg.norm(v2_x_v1))
    v3_x_v2 = np.cross(v3, v2)
    v3_x_v2 /= np.linalg.norm(v3_x_v2)
    dihedral = np.arccos(np.clip(np.vdot(v2_x_v1, v3_x_v2), -1, 1))
    if np.vdot(v2_x_v1, v3) > 0:
        dihedral = 2 * np.pi - dihedral
    if np.isnan(dihedral):
        raise VectorsError('Could not calculate a dihedral angle')
    conversion = 180 / math.pi if 'degs' in units else 1
    return float(dihedral * conversion)

def calculate_distance(coords: list or tuple,
                       atoms: list,
                       index: int = 0,
                       ) -> float:
    """
    Calculate a distance.

    Args:
        coords (list, tuple): The array-format or tuple-format coordinates.
        atoms (list): The 2 atoms to calculate defining the vector for which the length will be calculated.
        index (int, optional): Whether ``atoms`` is 0-indexed or 1-indexed (accepted values are 0 or 1).

    Returns:
        float: The distance in the coords units.

    Raises:
        ZMatError: If ``index`` is out of range, or ``atoms`` is of wrong length or has repeating indices.
        TypeError: If ``coords`` is of wrong type.
    """
    if isinstance(coords, dict) and 'coords' in coords:
        coords = coords['coords']
    if not isinstance(coords, (list, tuple)):
        raise TypeError(f'coords must be a list or a tuple, got\n{coords}\nwhich is a {type(coords)}')
    if index not in [0, 1]:
        raise VectorsError(f'index must be either 0 or 1, got {index}')
    if len(atoms) != 2:
        raise VectorsError(f'distance atom list must be of length two, got {len(atoms)}')
    if len(set(atoms)) < 2:
        raise VectorsError(f'some atoms are repetitive: {atoms}')
    new_atoms = list()
    for atom in atoms:
        if isinstance(atom, str) and 'X' in atom:
            new_atoms.append(int(atom[1:]))
        else:
            new_atoms.append(atom)
    if not all([isinstance(a, int) for a in new_atoms]):
        raise VectorsError(f'all entries in atoms must be integers, got: {new_atoms} ({[type(a) for a in new_atoms]})')
    new_atoms = [a - index for a in new_atoms]  # convert 1-index to 0-index
    coords = np.asarray(coords, dtype=np.float32)
    vector = coords[new_atoms[1]] - coords[new_atoms[0]]
    return get_vector_length(vector)

def calculate_angle(coords: list or tuple,
                    atoms: list,
                    index: int = 0,
                    units: str = 'degs',
                    ) -> float:
    """
    Calculate an angle.

    Args:
        coords (list, tuple): The array-format or tuple-format coordinates.
        atoms (list): The 3 atoms defining the angle.
        index (int, optional): Whether ``atoms`` is 0-indexed or 1-indexed (values are 0 or 1).
        units (str, optional): The desired units, either 'rads' for radians, or 'degs' for degrees.

    Returns:
        float: The angle.

    Raises:
        VectorsError: If ``index`` is out of range, or ``atoms`` is of wrong length or has repeating indices.
        TypeError: If ``coords`` is of wrong type.
    """
    if isinstance(coords, dict) and 'coords' in coords:
        coords = coords['coords']
    if not isinstance(coords, (list, tuple)):
        raise TypeError(f'coords must be a list or a tuple, got\n{coords}\nwhich is a {type(coords)}')
    if index not in [0, 1]:
        raise VectorsError(f'index must be either 0 or 1, got {index}')
    if len(atoms) != 3:
        raise VectorsError(f'angle atom list must be of length three, got {len(atoms)}')
    if len(set(atoms)) < 3:
        raise VectorsError(f'some atoms are repetitive: {atoms}')
    new_atoms = list()
    for atom in atoms:
        if isinstance(atom, str) and 'X' in atom:
            new_atoms.append(int(atom[1:]))
        else:
            new_atoms.append(atom)
    if not all([isinstance(a, int) for a in new_atoms]):
        raise VectorsError(f'all entries in atoms must be integers, got: {new_atoms} ({[type(a) for a in new_atoms]})')
    new_atoms = [a - index for a in new_atoms]  # convert 1-index to 0-index
    coords = np.asarray(coords, dtype=np.float32)
    v1 = coords[new_atoms[1]] - coords[new_atoms[0]]
    v2 = coords[new_atoms[1]] - coords[new_atoms[2]]
    return get_angle(v1, v2, units=units)

def calculate_dihedral_angle(coords: list or tuple,
                             torsion: list,
                             index=0,
                             units: str = 'degs',
                             ) -> float:
    """
    Calculate a dihedral angle.

    Args:
        coords (list, tuple): The array-format or tuple-format coordinates.
        torsion (list): The 4 atoms defining the dihedral angle.
        index (int, optional): Whether ``torsion`` is 0-indexed or 1-indexed (values are 0 or 1).
        units (str, optional): The desired units, either 'rads' for radians, or 'degs' for degrees.

    Returns:
        float: The dihedral angle in a 0-360 degrees range.

    Raises:
        VectorsError: If ``index`` is out of range, or ``torsion`` is of wrong length or has repeating indices.
        TypeError: If ``coords`` is of wrong type.
    """
    if isinstance(coords, dict) and 'coords' in coords:
        coords = coords['coords']
    if not isinstance(coords, (list, tuple)):
        raise TypeError(f'coords must be a list or a tuple, got\n{coords}\nwhich is a {type(coords)}')
    if index not in [0, 1]:
        raise VectorsError(f'index must be either 0 or 1, got {index}')
    if len(torsion) != 4:
        raise VectorsError(f'torsion atom list must be of length four, got {len(torsion)}')
    if len(set(torsion)) < 4:
        raise VectorsError(f'some indices in the given torsion are repetitive: {torsion}')
    new_torsion = list()
    for atom in torsion:
        if isinstance(atom, str) and 'X' in atom:
            new_torsion.append(int(atom[1:]))
        else:
            new_torsion.append(atom)
    if not all([isinstance(t, int) for t in new_torsion]):
        raise VectorsError(f'all entries in torsion must be integers, got: {new_torsion} '
                           f'({[type(t) for t in new_torsion]})')
    new_torsion = [t - index for t in new_torsion]  # convert 1-index to 0-index if needed
    coords = np.asarray(coords, dtype=np.float32)
    v1 = coords[new_torsion[1]] - coords[new_torsion[0]]
    v2 = coords[new_torsion[2]] - coords[new_torsion[1]]
    v3 = coords[new_torsion[3]] - coords[new_torsion[2]]
    return get_dihedral(v1, v2, v3, units=units)

def unit_vector(vector: list) -> list:
    """
    Calculate a unit vector in the same direction as the input vector.

    Args:
        vector (list): The input vector.

    Returns:
        list: The unit vector.
    """
    length = get_vector_length(vector)
    return [vi / length for vi in vector]

def get_vector_length(v: list) -> float:
    """
    Get the length of an ND vector

    Args:
        v (list): The vector.

    Returns:
        float: The vector's length.
    """
    return float(np.dot(v, v) ** 0.5)


""" Principal Component Analysis
"""

# Author: MohammadSadegh Akhondzadeh <ms.akhondzadeh@gmail.com>
#         Maryam Meghdadi <m.meghdadi@acm.org>

# License: BSD 3 clause
import numpy as np
import pandas as pd
import scipy as sp

class PCA():
    """
    Linear dimensionality reduction using Singular Value Decomposition of the
    data to project it to a lower dimensional space.

    Principal component analysis (PCA) is a statistical procedure that uses an orthogonal transformation to convert
    a set of observations of possibly correlated variables into a set of values of linearly uncorrelated variables
    called principal components

    Here, ordinally algorithm of PCA has been implemented

    """

    def __init__(self, n_component):
        """

        Parameters
        ----------
        n_component:  int, float, None or string
           Number of components to keep.
        """
        self.n_component = n_component
        self.ureduce = None

    def _fit(self, X):
        """

        Parameters
        ----------
        X: numpy.array
            Training data

        Returns
        -------

        """
        sigma = np.matmul(np.transpose(X), X) / len(X)
        U, S, V = np.linalg.svd(sigma)
        self.ureduce = U[:, 0:self.n_component]

    def fit(self, X):
        """
        fit the model with X.

        Parameters
        ----------
        X: numpy.array
            Training data, shape (n_samples, n_features)

        Returns
        -------
        self: object
            Returns the instance itself.
        """
        self._fit(X)
        return self

    def transform(self, X):
        """
        Apply dimensionality reduction on X.

        Parameters
        ----------
        X: numpy.array
            Training data, shape (n_samples, n_features)

        Returns
        -------
        X_new: new X after applying dimensionality reduction,
                shape (n_samples, n_components)
        """
        return np.matmul(X, self.ureduce)

    def inv_transform(self, X):
        """
        Transform data back to its original space.
        Parameters
        ----------
        X: numpy.array, shape (n_samples, n_components)
            X_new

        Returns
        -------
        X_original numpy array, shape (n_samples, n_features)
        """
        return np.matmul(X, np.transpose(self.ureduce))


'''
Joe Walter

difficulty: 50%
run time:   0:18
answer:     676333270

	***

146 Investigating A Prime Pattern

The smallest positive integer n for which the numbers n^2+1, n^2+3, n^2+7, n^2+9, n^2+13, and n^2+27 are consecutive primes is 10. The sum of all such integers n below one-million is 1242490.

What is the sum of all such integers n below 150 million?

	***

Observations

n is a multiple of 2 and 5, and hence 10.
n is not a multiple of 3, 7, or 13.

(n^2+3)%7  ≠ 0 -> n%7 ≠ 2,5
(n^2+7)%7  ≠ 0 -> n%7 ≠ 0
(n^2+13)%7 ≠ 0 -> n%7 ≠ 1,6

-> n%7 = 3,4
'''

from math import prod
from sympy import isprime

PK = [1,3,7,9,13,27]
CK = [5,11,15,17,19,21,23,25]

cycles = []
primes = [3,7,11,13,17,19] # congruence classes
for p in primes:
	cycle = [True]*p
	for k in PK:
		for n in range(p):
			if (n*n+k)%p == 0:
				cycle[n] = False
	if not all(cycle):
		cycles.append(cycle)

L = prod(len(c) for c in cycles)
supercycle = [True]*L
for c in cycles:
	for i, v in enumerate(c):
		if not v:
			supercycle[i::len(c)] = (False for _ in range(i, L, len(c)))

ans = 0
for n in range(10, 150*10**6, 10):
	if supercycle[n%L]:
		if all(not isprime(n*n+k) for k in CK) and all(isprime(n*n+k) for k in PK):
			#print(n)
			ans += n
print(ans)


import numpy as np
from scipy import sparse
import sympy
import matplotlib.pyplot as plt
import matplotlib
import progressbar
from cycler import cycler
import time
from sklearn.metrics import mean_squared_error as MSE

N = 1000
dx = 2*np.pi/N
dt = 2.5e-4
nu=0.05

x = np.arange(0, 2*np.pi, dx)
t = np.arange(0, 0.5+dt, dt)

#-------------------------------------------------------------------------------
def analytic_method():
    X, T = sympy.symbols('X T')
    phi = sympy.exp(-(X - 4 * T)**2 / (4 * nu * (T + 1))) + sympy.exp(-(X - 4 * T - 2 * np.pi)**2 / (4 * nu * (T + 1)))
    dphidx = phi.diff(X)

    u_analytic_ = -2 * nu / phi * dphidx + 4
    u_analytic  = sympy.utilities.lambdify((X, T), u_analytic_)

    return u_analytic

analytic_solution = analytic_method()

def u_analytic(t, x):
    return analytic_solution(x, t)

def FDM(ui):
    ''' FDM with first order (Euler) approximation
    '''
    u0 = ui[:-2]
    u1 = ui[1:-1]
    u2 = ui[2:]
    return u1 + dt*( -u1*(u1-u0)/dx + nu*(u2-2*u1+u0)/dx**2)

def set_colors(start_color="blue"):
    cvals  = [0., 1]
    colors = [start_color, "violet"]
    norm=plt.Normalize(min(cvals),max(cvals))
    tuples = list(zip(map(norm,cvals), colors))
    cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", tuples)

    ax = plt.axes()
    ax.set_prop_cycle(None)

    colors = [cmap(i) for i in np.linspace(0, 1, 8)[:6][::-1]]
    ax.set_prop_cycle(cycler('color', colors))
    return

def plot(x, u, dt, label, linestyle='solid', alpha=1, time_interval=0.1):
    step = int(time_interval/dt)
    u = u[::step]
    for un in u[:-1]:
        plt.plot(x, un, linestyle=linestyle, alpha=alpha)
    plt.plot(x, un, linestyle=linestyle, label=label, alpha=alpha)
    return
#-------------------------------------------------------------------------------

def main():
    u0 = u_analytic(0, x)

    u_prec = np.empty((t.shape[0], x.shape[0]))
    u_prec[0] = u_analytic(0, x)
    for i in range(1, t.shape[0]):
        u_prec[i] = u_analytic(t[i], x)

    u_fdm = np.empty((t.shape[0], x.shape[0]))
    u_fdm[0] = u_analytic(0, x)
    u_fdm[:, 0] = u_analytic(t, x[0])
    u_fdm[:, -1] = u_analytic(t, x[-1])

    start = time.time()
    for i in range(1, t.shape[0]):
        u_fdm[i, 1:-1] = FDM(u_fdm[i-1])
    end = time.time()
    print('elapsed time (FDM):', end-start)
    print('MSE (FDM):', MSE(u_fdm[-1], u_prec[-1]))

    set_colors()
    plot(x, u_fdm, dt, 'FDM (dt=2.5e-4)')
    plot(x, u_prec, dt, 'Analytic', linestyle='dashed', alpha=0.5)

    plt.grid()
    plt.ylim([0, 10])
    plt.xlim([0, 2*np.pi])
    plt.xlabel('x')
    plt.ylabel('u(t,x)')
    plt.legend()
    plt.show()
    return 0

if __name__ == "__main__":
    main()


import numpy as np
from numpy.lib.scimath import arcsin

import sys

EPSILON = sys.float_info.epsilon

# A simple function for determining whether a wave is propagating forward

def theta_forward(n, theta):

    assert n.real >= 0
    assert n.imag >= 0

    n_cos_theta = n * np.cos(theta)
    if np.absolute(n_cos_theta.imag) > 100 * EPSILON:
        value = (n_cos_theta.imag > 0)
    else:
        value = (n_cos_theta.real > 0)

    return value

# Snell's law as given in Eq.(3.10)

def snells_law(n_1, n_2, theta_1):

    theta_2 = arcsin(n_1 * np.sin(theta_1) / n_2)
    if theta_forward(n_2, theta_2):
        return theta_2
    else:
        return np.pi - theta_2

# Create a list of incident and refracted angles for all layers

def snells_law_list(n_list, theta_incident):

    theta_refracted_list = arcsin(n_list[0] * np.sin(theta_incident) / n_list)
    if not theta_forward(n_list[0], theta_refracted_list[0]):
        theta_refracted_list[0] = np.pi - theta_refracted_list[0]
    if not theta_forward(n_list[-1], theta_refracted_list[-1]):
        theta_refracted_list[-1] = np.pi - theta_refracted_list[-1]
    return theta_refracted_list

# Reflection amplitude as given in Eq.(3.22) and Eq.(3.24)

def reflection_amplitude(polarization, n_i, n_f, theta_i, theta_f):

    if polarization == 's':
        return ((n_i * np.cos(theta_i) - n_f * np.cos(theta_f)) /
                (n_i * np.cos(theta_i) + n_f * np.cos(theta_f)))
    elif polarization == 'p':
        return ((n_f * np.cos(theta_i) - n_i * np.cos(theta_f)) /
                (n_f * np.cos(theta_i) + n_i * np.cos(theta_f)))
    else:
        raise ValueError("Polarization must be 's' or 'p'")

def amplitude_r(polarization, n_i, n_f, theta_i, theta_f):

    r = reflection_amplitude(polarization, n_i, n_f, theta_i, theta_f)
    return reflected_power(r)

# Tranmission amplitude as given in Eq.(3.23) and Eq.(3.25)

def transmission_amplitude(polarization, n_i, n_f, theta_i, theta_f):

    if polarization == 's':
        return ((2 * n_i * np.cos(theta_i)) /
                (n_i * np.cos(theta_i) + n_f * np.cos(theta_f)))
    elif polarization == 'p':
        return ((2 * n_i * np.cos(theta_i)) /
                (n_f * np.cos(theta_i) + n_i * np.cos(theta_f)))
    else:
        raise ValueError("Polarization must be 's' or 'p'")

def amplitude_t(polarization, n_i, n_f, theta_i, theta_f):

    t = transmission_amplitude(polarization, n_i, n_f, theta_i, theta_f)
    return transmitted_power(polarization, t, n_i, n_f, theta_i, theta_f)

# Reflection and Transmission powers obtained from Eqs.(4.12 - 4.14)

def reflected_power(r):

    return np.absolute(r)**2

def transmitted_power(polarization, t, n_i, n_f, theta_i, theta_f):

    if polarization == 's':
        return np.absolute(t**2) * (((n_f * np.cos(theta_f)).real) / (n_i * np.cos(theta_i)).real)
    elif polarization == 'p':
        return np.absolute(t**2) * (((n_f * np.conj(np.cos(theta_f))).real) / (n_i * np.conj(np.cos(theta_i))).real)
    else:
        raise ValueError("Polarization must be 's' or 'p'")

# Template for our transfer matrices

def matrix_array(a, b, c, d, dtype=float):

    template_array = np.empty((2,2), dtype=dtype)
    template_array[0,0] = a
    template_array[0,1] = b
    template_array[1,0] = c
    template_array[1,1] = d
    return template_array

# The main function of this program; it takes the previous functions as inputs and returns a TMM

def transfer_matrix_method(polarization, n_list, d_list, theta_incident, wavelength):

# A series of input tests

    n_list = np.array(n_list)

    d_list = np.array(d_list, dtype=float)

    if ((hasattr(wavelength, 'size') and wavelength.size > 1)
          or (hasattr(theta_incident, 'size') and theta_incident.size > 1)):
        raise ValueError("Only 1 wavelenght and 1 incident angle can be entered at a time")
    if ((n_list.ndim != 1)
          or (d_list.ndim != 1)
          or (n_list.size != d_list.size)):
        raise ValueError("Problem with n_list or d_list!")

    assert d_list[0] == d_list[-1] == np.inf
    assert np.absolute((n_list[0] * np.sin(theta_incident)).imag) < 100*EPSILON
    assert theta_forward(n_list[0], theta_incident), 'Are the angles in radians?'

    num_layers = n_list.size

    theta_list = snells_law_list(n_list, theta_incident)

    k_list = 2 * np.pi * n_list * np.cos(theta_list) / wavelength

    phase_angle = k_list * d_list

# Define arrays for the reflection and transmission amplitudes

    r_list = np.zeros((num_layers, num_layers), dtype=complex)
    t_list = np.zeros((num_layers, num_layers), dtype=complex)
    for i in range(num_layers-1):
        r_list[i,i+1] = reflection_amplitude(polarization, n_list[i], n_list[i+1],
                                    theta_list[i], theta_list[i+1])
        t_list[i,i+1] = transmission_amplitude(polarization, n_list[i], n_list[i+1],
                                    theta_list[i], theta_list[i+1])

# The transfer matrix as defned in Eq.(4.5)

    Transfer1_list = np.zeros((num_layers, 2, 2), dtype=complex)
    for i in range(1, num_layers-1):
        Transfer1_list[i] = (1/t_list[i,i+1]) * np.dot(
            matrix_array(np.exp(-1j * phase_angle[i]), 0, 0, np.exp(1j * phase_angle[i]),
                           dtype=complex),
            matrix_array(1, r_list[i,i+1], r_list[i,i+1], 1, dtype=complex))

# The transfer matrix as defined in Eq.(4.9)

    Transfer2_list = matrix_array(1, 0, 0, 1, dtype=complex)
    for i in range(1, num_layers-1):
        Transfer2_list = np.dot(Transfer2_list, Transfer1_list[i])

    Transfer2_list = np.dot(matrix_array(1, r_list[0,1], r_list[0,1], 1,
                                   dtype=complex)/t_list[0,1], Transfer2_list)

    r = Transfer2_list[1,0]/Transfer2_list[0,0]
    t = 1/Transfer2_list[0,0]

    R = reflected_power(r)
    T = transmitted_power(polarization, t, n_list[0], n_list[-1], theta_incident, theta_list[-1])

    return {'r': r, 't': t, 'R': R, 'T': T,
            'k_list': k_list, 'theta_list': theta_list,
            'polarization': polarization, 'n_list': n_list, 'd_list': d_list, 'theta_incident': theta_incident,
            'wavelenght': wavelength}

# A simple function for an unpolarized beam of light

def unpolarized(n_list, d_list, theta_incident, wavelength):

    s_data = transfer_matrix_method('s', n_list, d_list, theta_incident, wavelength)
    p_data = transfer_matrix_method('p', n_list, d_list, theta_incident, wavelength)
    R = (s_data['R'] + p_data['R']) / 2.
    T = (s_data['T'] + p_data['T']) / 2.
    return {'R': R, 'T': T}

Tranfer Matrix Method Files/Python Files/tmm_final.py


## This code is stripped to the minimum required to implement the
## differentiable sort with no dependencies except numpy
import numpy as np

def bitonic_matrices(n):
    """Return the bitonic matrices to sort n elements, where n=2^k.
    This is a list of quadruples of matrices l,r,l_inv,r_inv
    l, r are n/2 x n and map the input to the left and right sides of a compare-and-swap
    l_inv, r_inv are n/2 x n and map the result of a compare-and-swap back to a vector of
    length n"""
    # number of outer layers
    layers = int(np.log2(n))
    matrices = []
    for layer in range(layers):
        for s in range(layer + 1):
            m = 1 << (layer - s)
            l, r = np.zeros((n // 2, n)), np.zeros((n // 2, n))
            map_l, map_r = np.zeros((n, n // 2)), np.zeros((n, n // 2))
            out = 0
            for i in range(0, n, m << 1):
                for j in range(m):
                    ix = i + j
                    a, b = ix, ix + m
                    l[out, a], r[out,b] = 1, 1
                    if (ix >> (layer + 1)) & 1:
                        a, b = b, a
                    map_l[a, out], map_r[b, out] = 1, 1
                    out += 1
            matrices.append((l, r, map_l, map_r))
    return matrices

def diff_sort(matrices, x, alpha=1):
    """
    Approximate differentiable sort. Takes a set of bitonic sort matrices generated by bitonic_matrices(n), sort
    a sequence x of length n. Values may be distorted slightly but will be ordered.
    """
    for l, r, map_l, map_r in matrices:
        a, b = l @ x, r @ x
        # smoothmax
        mx = (a * np.exp(a * alpha) + b * np.exp(b * alpha)) / (
            np.exp(a * alpha) + np.exp(b * alpha)
        )
        mn = a + b - mx
        x = map_l @ mn + map_r @ mx
    return x

def diff_argsort(matrices, x, sigma=0.1, alpha=1, transpose=False):
    """Return the smoothed, differentiable ranking of each element of x. Sigma
    specifies the smoothing of the ranking. Note that this function is deceptively named,
    and in the default setting returns the *ranking*, not the argsort.

    If transpose is True, returns argsort (but note that ties are not broken in differentiable
    argsort);
    If False, returns ranking (likewise, ties are not broken).
    """
    sortd = diff_sort(matrices, x, alpha)
    diff = (x.reshape(-1, 1) - sortd.reshape(1, -1)) ** 2
    rbf = np.exp(-(diff) / (2 * sigma ** 2))
    order = (rbf.T / np.sum(rbf, axis=1)).T
    if transpose:
        order = order.T
    return order @ np.arange(len(x))

if __name__ == "__main__":
    # test data, length 8
    x = np.array([5.0, -1.0, 9.5, 13.2, 16.2, 20.5, 42.0, 90.0])
    print(x)
    print("Sorted")
    print(np.sort(x))
    print("Diff. sorted")
    matrices = bitonic_matrices(8)
    print(diff_sort(matrices, x))
    print("Ranked")
    ixs = np.argsort(x)
    print(np.arange(8)[ixs])
    print("Diff. ranked")
    print(diff_argsort(matrices, x))


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2019-07-24 18:29:48
# @Author  : Zhi Liu (zhiliu.mind@gmail.com)
# @Link    : http://iridescent.ink
# @Version : $1.0$

import numpy as np
from improc.utils.const import EPS

def normalize(X, mean=None, std=None, axis=None, ver=False):
    r"""normalization

    .. math::
        \bar{X} = \frac{X-\mu}{\sigma}

    Parameters
    ----------
    X : {numpy ndarray}
        data to be normalized,
    mean : {list or None}, optional
        mean value (the default is None, which auto computed)
    std : {list or None}, optional
        standard deviation (the default is None, which auto computed)
    axis : {list or int}, optional
        specify the axis for computing mean value (the default is None, which all elements)
    axis : {bool}, optional
        if True, also return the mean and std (the default is False, which all elements)
    """

    if mean is None:
        if axis is None:
            mean = np.mean(X)
        else:
            mean = np.mean(X, axis, keepdims=True)
    if std is None:
        if axis is None:
            std = np.std(X)
        else:
            std = np.std(X, axis, keepdims=True)

    if ver is True:
        return (X - mean) / std, mean, std
    else:
        return (X - mean) / std

if __name__ == '__main__':

    X = np.random.randn(4, 3, 5, 6)
    # X = th.randn(3, 4)
    XX = normalize(X, axis=(0, 2, 3))
    XX, meanv, stdv = normalize(X, axis=(0, 2, 3), ver=True)
    print(XX.shape)
    print(meanv)
    print(stdv)


import numpy as np
import sympy as sp
#from numpy import log as ln
import math
from math import *
#import scipy.optimize as optimize
from scipy.misc import derivative
from prettytable import PrettyTable

def funcion(expr, val):
    x = val
    y = eval(expr)
    return y

def derivada(expr, x0):
    # funcion derivada
    x = sp.Symbol('x')
    f_prime = sp.Derivative(expr, x)
    derivada_de_la_funcion = f_prime.doit().subs(x, x0)
    return derivada_de_la_funcion

def Newton_R(expr, x0, emax, N=100):
    tabla = PrettyTable(["Iteracion", "Xi", "error", "f(x)", "%Error"])
    tabla.title = "METODO DE NEWTON RAPHSON "
    #emax = emax*0.1
    for k in range(1, N):
        xold = x0
        # funcion sustituida
        fx = funcion(expr, x0)
        df_dx = derivada(expr, x0)
        x0 = x0-fx/df_dx
        #analisis de errores
        e_porc = abs((x0-xold)/x0)
        error = abs(xold-x0)
        fx = funcion(expr, x0)
        # el pretitable
        tabla.add_row([k, "{0:.7f}".format(x0), "{0:.7f}".format(
            error), "{0:.7f}".format(fx), "{0:.2f}".format(e_porc*100)])
        # print(k,x0,error,function(expr,x0),e_porc*100)
        if error < emax:
            break
    print(tabla)
    x = "{0:.8f}".format(x0)
    print(f'''
        la raiz es: {x}
        ''')

#Newton_Rhapson(expr, valor_inicial, 1e-3)

# para obtener la raiz por medio optimize de scipy se da la funcion definida
#print(f"La raiz es: {optimize.newton(f,1)}")

src/1. Ecuaciones no lineales/Metodo/Newton_Raphson.py

import numpy,from numpy,import scipy,from scipy,import sympy


# coding: utf-8

# ### DEMDIF02
# # Demonstrates accuracy of one- and two-sided finite-difference derivatives of $e^x$ at $x=1$ as a function of step size $h$.

# In[1]:

import numpy as np
from compecon import demo
import matplotlib.pyplot as plt

# In[2]:

n, x = 18, 1.0
c = np.linspace(-15,0,n)
h = 10 ** c

# In[3]:

exp = np.exp
eps = np.finfo(float).eps

def deriv_error(l, u):
    dd = (exp(u) - exp(l)) / (u-l)
    return np.log10(np.abs(dd - exp(x)))

# ## One-sided finite difference derivative

# In[4]:

d1 = deriv_error(x, x+h)
e1 = np.log10(eps**(1/2))

# ## Two-sided finite difference derivative

# In[5]:

d2 = deriv_error(x-h, x+h)
e2 = np.log10(eps**(1/3))

# ## Plot finite difference derivatives

# In[6]:

ylim = [-15, 5]
xlim = [-15, 0]
lcolor = [z['color']  for z in plt.rcParams['axes.prop_cycle']]

demo.figure('Error in Numerical Derivatives','$\log_{10}(h)$','$\log_{10}$ Approximation Error',xlim,ylim)
plt.plot(c,d1, label='One-Sided')
plt.plot(c,d2, label='Two-Sided')
plt.vlines([e1,e2],*ylim, lcolor,linestyle=':')
plt.xticks(np.arange(-15,5,5))
plt.yticks(np.arange(-15,10,5))
demo.annotate(e1,2,'$\sqrt{\epsilon}$',color=lcolor[0],ms=0)
demo.annotate(e2,2,'$\sqrt[3]{\epsilon}$',color=lcolor[1],ms=0)
plt.legend(loc='lower left')

# In[7]:

demo.savefig([plt.gcf()])


#!/usr/bin/env python3
"""
File: heguilong.py
Author: heguilong
Email: hgleagle@gmail.com
Github: https://github.com/hgleagle
Description:
    斐波那契数列由0和1开始，之后的斐波那契系数就是由之前的两数相加而得出，例如
斐波那契数列的前10个数是 0, 1, 1, 2, 3, 5, 8, 13, 21, 34。
"""
import logging
import sys

import numpy as np

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s \
                    - %(message)s')

# solution 1, O(1.618^n)
class Fibonacci():
    def __init__(self):
        """TODO: Docstring for __init__.

        :arg1: TODO
        :returns: TODO

        """
        self.fib_dict = {}

    def calculate(self, number):
        """TODO: Docstring for calculate.

        :f: TODO
        :number: TODO
        :returns: TODO

        """
        # logging.debug("number: {%d}" % number)
        assert number >= 0
        if number in (0, 1):
            result = number
        else:
            result = self.calculate(number - 1) + self.calculate(number - 2)
        if number > 0 and number not in self.fib_dict:
            self.fib_dict[number] = result
        return result

    def show_fib_values(self):
        print(self.fib_dict.values())

# solution 2, O(n)
def fib_loop(n):
    a, b = 0, 1
    while n > 0:
        yield b
        a, b = b, a + b
        n -= 1

# solution 3, O(logn)
def fib_matr(n):
    return (np.matrix([1, 1], [1, 0]) ** (n - 1) * np.matrix([1], [0]))[0, 0]

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python3 heguilong.py number")
        sys.exit()
    number = int(sys.argv[1])
    if number <= 0:
        print("number should be larger than 0")

    # solution 1
    print("Solution 1:")
    fib_obj = Fibonacci()
    fib_obj.calculate(number)
    fib_obj.show_fib_values()

    # solution 2
    print("Solution 2:")
    for i in fib_loop(number):
        print(i, end=" ")

    # solution 3
    print("Solution 3:")
    for n in range(number):
        print(fib_matr(n), end=" ")


import numpy as np

def assert_extreme_point(A, b, C, d, x):
    """Check if x is an extrem point
    """
    if np.any(d != np.dot(C, x)):
        print "This point is infeasible. Don't satisfy EQ constraints"
        return False
    active_set = b - np.dot(A, x)
    if np.any(active_set < 0):
        print "This point is infeasible. Don't satisfy IEQ constraints"
        return False
    J = np.nonzero(active_set == 0)[0]
    print "Active constraints:\n", J
    r = np.linalg.matrix_rank(np.concatenate((A[J, :], C)))
    print "rank([A_J; C]) =", r
    if r == n:
        return True
    else:
        return False

# 6.1
A = np.array([[   0,   2,   2,   2,  -4],
              [   0,  -2,   2,  -2,   0],
              [   4,   0,   2,   0,   2],
             ], dtype=np.float)
b = np.array([[  2],
              [  2],
              [  2],
             ], dtype=np.float)
p, n = A.shape[0], A.shape[1]
C = np.concatenate((np.eye(n), -np.eye(n)))
m = C.shape[0]
d = np.ones((m, 1))
x_1 = np.array([[    1],
                [ -0.5],
                [    0],
                [ -0.5],
                [   -1],
               ])
x_2 = np.array([[  0],
                [  0],
                [  1],
                [  0],
                [  0],
               ], dtype=np.float)
x_3 = np.array([[  0],
                [  1],
                [  1],
                [ -1],
                [  0],
               ], dtype=np.float)

# (a)
print "Part (a):"
print "b - Ax_1 =\n", b - np.dot(A, x_1)
print "b - Ax_2 =\n", b - np.dot(A, x_2)
print "b - Ax_2 =\n", b - np.dot(A, x_3)
print "Part (b):"
print "x_1 is EP:\n", assert_extreme_point(C, d, A, b, x_1)
print "x_2 is EP:\n", assert_extreme_point(C, d, A, b, x_2)
print "x_3 is EP:\n", assert_extreme_point(C, d, A, b, x_3)


'''
	Package: cs771
	Module: helloWorld
	Author: Puru (purushot@cse.iitk.ac.in)
	Institution: CSE, IIT Kanpur
	License: GNU GPL v3.0

	Generate synthetic data of various sorts
'''

import numpy as np
import numpy.linalg as nplin
import numpy.random as nprnd

# Return n data points (as an n x d array) sampled from the surface of sphere of radius r centered at mu
def genSphericalData( d, n, mu, r ):
	X = nprnd.normal( 0, 1, (n, d) )
	norms = nplin.norm( X, axis = 1 )
	X = X / norms[:, np.newaxis]
	X = (X * r) + mu
	return X

# Return n data points (as an n x d array) sampled from the surface of ellipse of covariance cov centered at mu
def genEllipticalData( d, n, mu, cov ):
	X = genSphericalData( d, n, np.zeros((d,)), 1 )
	L = nplin.cholesky( cov )
	X = np.matmul( X, L ) + mu
	return X

# Return n data points (as an n x d array) sampled from N(mu, sigma^2 . I)
def genSphericalNormalData( d, n, mu, sigma ):
	X = nprnd.normal( 0, sigma, (n, d) ) + mu
	return X

# Return n data points (as an n x d array) sampled from N(mu, cov)
def genNormalData( d, n, mu, cov ):
	X = nprnd.multivariate_normal( mu, cov, n )
	return X

# Return n data points (as an n x d array) shaped as a hemisphere
# The manner of generation used below is similar to the sklearn
# method make_moons.However, the method below gives the additional
# flexibility of flipping and translating the moons
def genMoonData( d, n, mu, r, flipped = False ):
	X = np.vstack( (np.cos( np.linspace( 0, np.pi, n ) ), np.sin( np.linspace( 0, np.pi, n ) ) ) ).T
	if flipped:
		X[:,1] = -np.abs( X[:,1] )
	else:
		X[:,1] = np.abs( X[:,1] )
	X = (X * r) + mu
	return X


import numpy as np
from anova import anova_oneway

# These example are from
# @book{Johnson,
#     author={Richard Johnson},
#     title={Miller \& Freund's Probability and Statistics for Engineers},
#     edition={seventh},
#     publisher={Pearson Prentice Hall},
#     address={Upper Saddle River, New Jersey},
#     year={2005}
# }

# Example on page 401

a = [13, 10, 8, 11, 8]
b = [13, 11, 14, 14]
c = [4, 1, 3, 4, 2, 4]
result = anova_oneway(a, b, c)

print("Example, page 401")
with np.printoptions(formatter={'float': lambda t: format(t, '8.3f')}):
    print(result)
print()

# Example on page 408

a = np.array([0.25, 0.27, 0.22, 0.30, 0.27, 0.28, 0.32, 0.24, 0.31, 0.26, 0.21, 0.28])
b = np.array([0.18, 0.28, 0.21, 0.23, 0.25, 0.20, 0.27, 0.19, 0.24, 0.22, 0.29, 0.16])
c = np.array([0.19, 0.25, 0.27, 0.24, 0.18, 0.26, 0.28, 0.24, 0.25, 0.20, 0.21, 0.19])
d = np.array([0.23, 0.30, 0.28, 0.28, 0.24, 0.34, 0.20, 0.18, 0.24, 0.28, 0.22, 0.21])

result = anova_oneway(a, b, c, d)
print("Example, page 408")
with np.printoptions(formatter={'float': lambda t: format(t, '8.5f')}):
    print(result)
print()

# Example on the bottom of page 409

pos1 = [90, 82, 79, 98, 83, 91]
pos2 = [105, 89, 93, 104, 89, 95, 86]
pos3 = [83, 89, 80, 94]

result = anova_oneway(pos1, pos2, pos3)

print("Example, page 409")
with np.printoptions(formatter={'float': lambda t: format(t, '8.3f')}):
    print(result)


import numpy as np

def mape(actual, pred):
    # mean absolute percentage error
    actual, pred = np.array(actual), np.array(pred)
    return np.mean(np.abs((actual - pred) / actual)) * 100

def get_max_min_percentage_diff(ori, pred):
    z = np.abs((ori - pred) / ori)
    max_ind = np.argmax(z)
    print("Max Percentage Difference: %.2f ---- Original Price %.2f, Predict Price %.2f " %(z.max()*100,ori[max_ind],pred[max_ind]) )

    min_ind = np.argmin(z)
    print("Min Percentage Difference: %.2f ---- Original Price %.2f, Predict Price %.2f " %(z.min()*100,ori[min_ind],pred[min_ind]) )

    return z.max()*100, z.min()*100


# encoding: utf-8

# importanto a biblioteca numpy
import numpy as np

# inserindo as séries em arranjos matriciais

s1 = np.array((168, 398, 451, 337, 186, 232, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258, 242, 331, 251, 323, 106, 1055, 170))
s2 = np.array((168, 400, 451, 300, 186, 200, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258, 242, 331, 251, 180, 106, 1055, 200))

# subtraindo termos das séries S2 - S1
subtracao = s2-s1
# print("subtracao\n",subtracao)
# definindo o quadrado de cada subtração
quadrado = subtracao * subtracao
# print("quadrado\n",quadrado)
# aplicando somatório de cada termo na matriz 'quadrado'
somatorio = np.sum(quadrado)
# print("somatorio\n",somatorio)
# extraindo a raíz do somatorio
distancia_euclidiana = np.sqrt(somatorio)

print("distancia euclidiana: ",distancia_euclidiana)

# com valor da serie previamente definido, calcula-se em duas etapas
# soma das séries
soma_serie = s1 + s2
# print("soma\n", soma_serie)
# média das séries
media = soma_serie/2
print("Serie temporal com valores medios:\n", media)

# organizando as séries em um DataFrame
series_mistas = np.array((s1,s2))
# print("serie 1 e 2 em um data frame\n",series_mistas)

# atribuindo valor máximo do eixo 0, nas colunas
serie_max = series_mistas.max(axis=0)
print("serie com valores maximos:\n",serie_max)

# atribuindo valor mínimo do eixo 0, nas colunas
serie_min = series_mistas.min(axis=0)
print("serie com valores mínimos:\n",serie_min)


# AUTOGENERATED! DO NOT EDIT! File to edit: nbs/03_Metrics.ipynb (unless otherwise specified).

__all__ = ['__total_error', '__absolute_error', '__squared_error', '__mean_error', '__mean_absolute_error',
           '__mean_squared_error', '__root_mean_square_error', '__mean_percentage_error',
           '__mean_absolute_percent_error', 'SUMMARY']

# Cell
import numpy as np
from numba import jit
import pandas as pd
from time_series_model_basics import simulate_data, moving_average, metrics
from typing import List

# Cell
def __total_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the total error:

    $\sum^{n}_{i}(ts[i] - f[i]), n = len(ts) = len(f)$

    .Ignores nan values in times-series or the forecast.

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """

    d = ts - f
    return np.sum(d, where=~np.isnan(d))

# Cell
def __absolute_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the absolute error:

    $\sum^{n}_{i}|ts[i] - f[i]|, n = len(ts) = len(f)$

    .Ignores nan values in times-series or the forecast.

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """

    d = np.abs(ts - f)

    return np.sum(d, where=~np.isnan(d))

# Cell
def __squared_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the squared error:

    $\sum^{n}_{i}|ts[i] - f[i]|**2, n = len(ts) = len(f)$

    .Ignores nan values in times-series or the forecast.

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """

    d2 = (ts - f)**2
    return np.sum(d2, where=~np.isnan(d2))

# Cell
def __mean_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the mean error:

    $(1/n)\sum^{n*}_{i}ts[i] - f[i], n* = len(ts) = len(f)$

    .Ignores nan values in times-series or the forecast.
     Value n is n* minus the ignored values

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """
    d = ts - f
    w = ~np.isnan(d)
    n = len(d[w])

    return np.divide(
        np.sum(d, where=w),
        n,
        where=n > 0,
    )

# Cell
def __mean_absolute_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the mean absolute error:

    $$(1/n)\sum^{n}_{i} | ts[i] - f[i]|, n = len(ts) = len(f)$$

    .Ignores nan values in times-series or the forecast.

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """

    d = np.abs(ts - f)
    w = ~np.isnan(d)
    n = len(d[w])

    return np.divide(
        np.sum(d, where=w),
        n,
        where=n > 0,
    )

# Cell
def __mean_squared_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the mean squared error:

    $$(1/n)\sum^{n}_{i} | ts[i] - f[i]|**2, n = len(ts) = len(f)$$

    .Ignores nan values in times-series or the forecast.

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """
    d = (ts - f)**2
    w = ~np.isnan(d)
    n = len(d[w])

    return np.divide(
        np.sum(d, where=w),
        n,
        where=n > 0,
    )

# Cell
def __root_mean_square_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the root mean square error:

    $$\sqrt{(1/n)\sum^{n}_{i} | ts[i] - f[i]|**2},  n = len(ts) = len(f)$$

    .Ignores nan values in times-series or the forecast.

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """

    return np.sqrt(__mean_squared_error(ts=ts, f=f))

# Cell
def __mean_percentage_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the root mean percentage error:

    $$ (1/n)\sum ( ts[i] - f[i]) /ts[i],  n* = len(ts) = len(f)$$

    .Ignores nan values and division by zero.
    n is n* minus the ignored values

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """

    pe = np.divide(ts - f, ts, where=ts != 0)
    w = ~np.isnan(pe)

    n = len(pe[w])

    return np.divide(
        np.sum(pe, where=w),
        n,
        where=n > 0,
    )

# Cell
def __mean_absolute_percent_error(
    *,
    ts: np.array,
    f: np.array,
) -> float:
    """
    Computes the mean absolute percentage error:

    $$ (1/n)\sum | ts[i] - f[i]  /ts[i] \vert,  n* = len(ts) = len(f)$$

    .Ignores nan values and division by zero.
    n is n* minus the ignored values

    ------
    Parameters
    ------

    ts : np.array with the time-series
    f :  np.array with the forecast

    -------
    Returns
    -------

    """

    pe = abs(np.divide(ts - f, ts, where=ts != 0))
    w = ~np.isnan(pe)

    n = len(pe[w])

    return np.divide(
        np.sum(pe, where=w),
        n,
        where=n > 0,
    )

# Cell
def SUMMARY(
    df: pd.DataFrame = None,
    *,
    val_col: str = None,
    pred_cols: List[str] = None,
) -> pd.DataFrame:
    """
    Summary of Prediction Metrics

    -----
    Parameters
    -----

    df : dataframe , dafault None. If None it will generate a simulated dataframe.
    val_col : str , default None.  Name of the column with the actual values.
              It should be provided when a datafre is provided.
    pred_cols : List[str] . Names of the columns with the predictions for the values
              If not provided it will take all dataframe columns except val_col

    -----
    Returns
    ------

    dataframe : Summary results

    """
    if df is None:
        df = simulate_data.pandas_time_series()
        df, _ = moving_average.SMA(
            1,
            4,
            df=df,
        )

        val_col = 'time_series'

    if val_col is None:
        raise Exception('When df is provided val_col cannot be None')

    if pred_cols is None:
        pred_cols = [x for x in df.columns if x != val_col]

    actual_values = df[val_col].to_numpy()

    metric_names = [fn for fn in metrics.__all__ if fn[:2] == '__']
    result_df = pd.DataFrame()

    for col in pred_cols:
        forecast = df[col].to_numpy()
        result_df[col] = pd.Series({
            ' '.join(fn[2:].split('_')[:-1]): eval(fn)(ts=actual_values,
                                                       f=forecast)
            for fn in metric_names
        })
    result_df.index.name = 'Error'
    return result_df


# -*- coding: utf-8 -*-
from __future__ import division, print_function
from numpy import log2, ceil, abs, zeros

def bracketing(fun, a, b, N):
    """Estimate the root of a real function using bracketing

    Parameters
    ----------
    fun : function
        Function.
    a : float
        Initial point of the interval.
    b : float
        Final point of the interval.
    N : int
        NUmber of subdivisions for the interval

    Returns
    -------
    xR : array
        Array with initial estimates for roots.
    msg : string
        Message regarding the success of the method.

    """
    msg = "Maximum number of iterations reached."
    dx = (b - a)/(N - 1)
    iroot = 0
    x2 = a
    xR = zeros(N, float)
    for i in range(0, N):
        x1 = x2
        x2 = x1 + dx
        if (fun(x1) * fun(x2)) < 0:
            msg = "A change of sign was found."
            iroot = iroot + 1
            xR[i] = x1
    return xR, msg

def bisection(fun, a, b, xtol=1e-6, ftol=1e-12, verbose=False):
    """Use bisection method to estimate the root of a real function

    Parameters
    ----------
    fun : function
        Function.
    grad : function
        Derivative of the function.
    a : float
        Initial point of the interval.
    b : float
        Final point of the interval.
    xtol : float, optional
        Tolerance for the interval.
    ftol : float, optional
        Tolerance for the root.
    verbose : bool, optional
        If True, prints each iteration.

    Returns
    -------
    x : array
        Approximated root.
    msg : string
        Message regarding the success of the method.
    """
    if fun(a) * fun(b) > 0:
        c = None
        msg = "The function should have a sign change in the interval."
    else:
        nmax = int(ceil(log2((b - a)/xtol)))
        for cont in range(nmax):
            c = 0.5*(a + b)
            if verbose:
                print("n: {}, x: {}".format(cont, c))
            if abs(fun(c)) < ftol:
                msg = "Root found with desired accuracy."
                break
            elif fun(a) * fun(c) < 0:
                b = c
            elif fun(b) * fun(c) < 0:
                a = c
            msg = "Maximum number of iterations reached."
    return c, msg

def newton(fun, grad, x, niter=50, ftol=1e-8, verbose=False):
    """Use Newton method to estimate the root of a real function

    Parameters
    ----------
    fun : function
        Function.
    grad : function
        Derivative of the function.
    x : float
        Initial estimate.
    niter : int, optional
        Maximum number of iterations.
    ftol : float, optional
        Tolerance for the root.
    verbose : bool, optional
        If True, prints each iteration.

    Returns
    -------
    x : array
        Approximated root.
    msg : string
        Message regarding the success of the method.
    """
    msg = "Maximum number of iterations reached."
    for cont in range(niter):
        if abs(grad(x)) < ftol:
            x = None
            msg = "Derivative near to zero."
            break
        if verbose:
            print("n: {}, x: {}".format(cont, x))
        x = x - fun(x)/grad(x)
        if abs(fun(x)) < ftol:
            msg = "Root found with desired accuracy."
            break
    return x, msg


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

#matriz[linha][coluna]

#Le as matrizes do arquivo txt
def lerArquivo(nomeArquivo):
    arq = open(nomeArquivo, 'r')
    texto = arq.readlines()
    matriz = []

    for linha in texto:
        tmp = []
        for x in linha.split(','):
            tmp.append(float(x))
        matriz.append(tmp[:])
    arq.close()

    #print(matriz[3][3])
    return matriz

def tamanhoArquivo(nomeArquivo):
    arq = open(nomeArquivo, 'r')
    texto = arq.readlines()
    tamanho=0

    for linha in texto:
        tamanho=tamanho+1
    arq.close()

    return tamanho

#funcao para imprimir uma matriz
def imprimirMatriz(matriz):
    for i in range(len(matriz)):
        for j in range(len(matriz[i])):
            print '  %0.5f' % (matriz[i][j]),

        print '\n'

#resolve a matriz triangular
def resolveTri(matriz):
    linhad = len(matriz) -1
    colunad = len(matriz[0]) -2

    u = linhad
    x = []

    x.append(matriz[linhad][colunad]/(matriz[u][u]))
    u=u-1

    for i in range(u+1):
        linhad=linhad-1
        soma=0
        for j in range(u+1):
            if j<len(x):
                soma=soma+(matriz[u-i][u-j+1]*x[j])
        x.append(((matriz[linhad][colunad])-soma)/matriz[u-i][u-i])
    x.reverse()
    return x

#Funcao que acha o maior numero entre os valores da coluna passada
def achaMaior(mat, i, j):
    maior = 0
    maiorIndice = i
    while i < len(mat):
       # print(mat[i][j])
        if abs(mat[i][j]) > maior:
            maior = mat[i][j]
            maiorIndice = i
        i += 1
    return maiorIndice

#Se pivotacao=0 sem pivotacao pivotacao=1 com pivotacao
def eliminacaoGauss(matriz,pivotacao):
    n=len(matriz) #ordem da matriz

    for i in range(n-1):
        if pivotacao == 1:

            maior=achaMaior(matriz,i,i)
            aux=matriz[i]
            matriz[i]=matriz[maior]
            matriz[maior]=aux

        for j in xrange(i,n-1):
            m=matriz[j+1][i]/matriz[i][i]*(-1)

            for x in xrange(0,n+1):
                aux=(matriz[i][x]*m)+matriz[j+1][x]
                matriz[j+1][x]=aux

    #imprimirMatriz(matriz)
    x=resolveTri(matriz)
    return x

def copiarColuna(matriz, coluna):
    n=len(matriz) #ordem da matriz
    v=[]
    for x in xrange(0,n):
        v.append(matriz[x][coluna])

    return v

def criterioLinhas(matriz):

    u = len(matriz)

    flag=0 #0 - Satisfaz 1 - Nao Satisfaz

    for i in xrange(0,u):
        soma=0
        for j in xrange(0,u):
            if i==j:
                a=matriz[i][j]
            else:
                soma=soma+abs(matriz[i][j])

        #print'soma', (soma), 'a', (a)
        if abs(a)<soma:
            return 1

    return 0

def jacobi(matriz, xInicial,  toler, iterMax, imprimir):

    aux=criterioLinhas(matriz);
    x=[]
    v=[]

    if aux==1:
        print '\nEsse sistema nao satisfaz o criterio das linhas !!!'
        c = int(input('Deseja continuar mesmo assim? (0-Sim 1-nao)'))

        if c == 1:
            return x, 0

    n=len(matriz) #ordem da matriz
    a=matriz
    b=copiarColuna(matriz, len(matriz[0])-2)
    #print 'b ', (b)
    iter=0
    flag=0

    for i in xrange(0,n):
        r=1/a[i][i]
        for j in xrange(0,n):
            if(i!=j):
                a[i][j]=a[i][j]*r
        b[i]=b[i]*r
        x.append(xInicial)
    while flag == 0:
        iter=iter+1
        for i in xrange(0,n):
            soma=0
            for j in xrange(0,n):
                if i!=j:
                    soma=soma+a[i][j]*x[j]
            v.append(b[i]-soma)
        normaNum=0.0
        normaDen=0.0
        for i in xrange(0,n):
            t=abs(v[i]-x[i])
            if t>normaNum:
                normaNum=t
            if abs(v[i])>normaDen:
                normaDen=abs(v[i])
            x[i]=v[i]
        normaReal=normaNum/normaDen
        if imprimir == 0:
            print '\nIter:', (iter)
            print '  x:', (x)
            print '  norma:', (normaReal)
        v=[]

        #Testes de convergencia
        if(normaReal<=toler) or (iter>=iterMax):
            flag=1

    return (x, iter)

def criterioSassenfeld(matriz):
    u = len(matriz)
    vetorB=[]

    for i in xrange(0,u):
        vetorB.append(1)

    flag=0 #0 - Satisfaz 1 - Nao Satisfaz

    for i in xrange(0,u):
        soma=0
        for j in xrange(0,u):
            if i==j:
                a=abs(matriz[i][j])
            else:
                soma=soma+(abs(matriz[i][j])*vetorB[j])

        b=soma/a
        vetorB[i]=b
       # print'soma', (soma), 'a', (a), 'b', (b)
        if b>1:
            return 1

    return 0

def seidel(matriz,xInicial, toler, iterMax, imprimir):

    x=[]
    v=[]

    aux = criterioSassenfeld(matriz)
    if aux==1:
        print '\nEsse sistema nao satisfaz o criterio de Sassenfeld !!!'
        c = int(input('Deseja continuar mesmo assim? (0-Sim 1-nao)'))

        if c == 1:
            return x, 0

    n=len(matriz) #ordem da matriz
    a=matriz
    b=copiarColuna(matriz, len(matriz[0])-2)
    #print 'b ', (b)
    iter=0
    flag=0

    for i in xrange(0,n):
        r=1/a[i][i]
        for j in xrange(0,n):
            if(i!=j):
                a[i][j]=a[i][j]*r
        b[i]=b[i]*r
        x.append(xInicial)
    while flag == 0:
        iter=iter+1
        v=[]
        for i in xrange(0,n):
            soma=0
            for j in xrange(0,n):
                if i!=j:
                    soma=soma+a[i][j]*x[j]
            v.append(x[i])
            x[i]=b[i]-soma
        normaNum=0.0
        normaDen=0.0
        for i in xrange(0,n):
            t=abs(x[i]-v[i])
            if t>normaNum:
                normaNum=t
            if abs(x[i])>normaDen:
                normaDen=abs(x[i])
        normaReal=normaNum/normaDen
        if imprimir == 0:
            print '\nIter:', (iter)
            print '  x:', (x)
            print '  norma:', (normaReal)

        #Testes de convergencia
        if(normaReal<=toler) or (iter>=iterMax):
            flag=1

    return (x,iter)

def calculaResiduo(matriz, x):
    u = len(matriz)
    b = len(matriz[0])-2
    resp = []

#resolve o sistema
    for i in xrange(0,u):
        soma = 0
        for j in xrange(0,u):
            soma=soma+matriz[i][j]*x[j]
        resp.append(soma)

#Calcula Residuo
    residuo=[]

    for x in xrange(0,u):
        d=matriz[x][b]-resp[x]
        residuo.append(d)

    return residuo

def principal():

    ativo=0

    while ativo == 0:

        print'\nMenu:\n'
        print'1-Utilizar algum Metodo'
        print'2-Gerar Graficos'
        print'3-Comparar Residuo'
        print'4-Sair\n'
        menu = int(input('Digite sua opcao: '))

        if menu == 4:
            ativo=1

        if menu == 1:

            print'\nEscolha o Metodo:\n'
            print'1-Eliminacao de Gauss sem pivoteamento'
            print'2-Eliminacao de Gauss com pivoteamento'
            print'3-Gauss-Jacobi'
            print'4-Gauss-Seidel'
            print'5-Voltar\n'

            subMenu = int(input('Digite sua opcao: '))

            if subMenu == 1:

                nomeArquivo = raw_input('Digite o nome do arquivo: ')

                matriz = lerArquivo(nomeArquivo)
                print'\nMatriz de Entrada\n'
                imprimirMatriz(matriz)

                print '\n\nEleminacao de Gauss sem Pivotamento'
                resp=eliminacaoGauss(matriz,0)
                imprimirMatriz(matriz)
                print'resposta ',(resp)

            if subMenu == 2:

                nomeArquivo = raw_input('Digite o nome do arquivo: ')

                matriz = lerArquivo(nomeArquivo)
                print'\nMatriz de Entrada\n'
                imprimirMatriz(matriz)

                print '\nEleminacao de Gauss com Pivotamento'
                resp=eliminacaoGauss(matriz,1)
                imprimirMatriz(matriz)
                print'resposta ',(resp)

            if subMenu == 3:

                nomeArquivo = raw_input('Digite o nome do arquivo: ')
                erroMax = float(input('Digite o valor do erro maximo: '))
                iterMax = float(raw_input('Digite o valor maximo de iteracoes: '))
                x=0

                matriz = lerArquivo(nomeArquivo)
                print'\nMatriz de Entrada\n'
                imprimirMatriz(matriz)

                print '\n\nGauss Jacobi\n'
                r, k=jacobi(matriz,x, erroMax, iterMax,0)
                print '\nResposta Final', (r)
                print 'iteracoes: ', (k)

            if subMenu == 4:

                nomeArquivo = raw_input('Digite o nome do arquivo: ')
                erroMax = float(input('Digite o valor do erro maximo: '))
                iterMax = float(raw_input('Digite o valor maximo de iteracoes: '))
                x=0

                matriz = lerArquivo(nomeArquivo)
                print'\nMatriz de Entrada\n'
                imprimirMatriz(matriz)

                print '\n\nGauss Seidel\n'
                r, k =seidel(matriz,x, erroMax, iterMax,0)
                print '\nResposta Final', (r)
                print 'iteracoes: ', (k)

        if menu == 2:

            print'\nEscolha o Metodo:\n'
            print'1-Grafico comparando o tempo'
            print'2-Grafico para metodo iterativo'
            print'3-Voltar\n'

            subMenu = int(input('Digite sua opcao: '))

            if subMenu == 1:

                flag=0
                tamanhoMat=[]
                tempoExecucaoGSP=[]
                tempoExecucaoGCP=[]
                tempoExecucaoGJ=[]
                tempoExecucaoGS=[]

                while flag ==0:

                    nomeArquivo = raw_input('Digite o nome do arquivo: ')

                    matriz = lerArquivo(nomeArquivo)
                    tamanho = tamanhoArquivo(nomeArquivo)
                    tamanhoMat.append(tamanho)

                    tempoInicial = time.time()
                    resp=eliminacaoGauss(matriz,0)
                    tempoFinal = time.time()
                    tempoExecucaoGaussSemPiv=tempoFinal-tempoInicial
                    tempoExecucaoGSP.append(tempoExecucaoGaussSemPiv)

                    matriz = lerArquivo(nomeArquivo)

                    tempoInicial = time.time()
                    resp=eliminacaoGauss(matriz,1)
                    tempoFinal = time.time()
                    tempoExecucaoGausscomPiv=tempoFinal-tempoInicial
                    tempoExecucaoGCP.append(tempoExecucaoGausscomPiv)

                    matriz = lerArquivo(nomeArquivo)

                    x=0
                    erroMax=0.000000000000001
                    iterMax = 50

                    tempoInicial = time.time()
                    r, k =jacobi(matriz,x, erroMax, iterMax,1)
                    tempoFinal = time.time()
                    tempoExecucaofinalGJ=tempoFinal-tempoInicial
                    tempoExecucaoGJ.append(tempoExecucaofinalGJ)

                    matriz = lerArquivo(nomeArquivo)

                    tempoInicial = time.time()
                    r, k =seidel(matriz,x, erroMax, iterMax,1)
                    tempoFinal = time.time()
                    tempoExecucao=tempoFinal-tempoInicial
                    tempoExecucaoGS.append(tempoExecucao)

                    flag = int(input('Deseja adicionar mais algum sistema? (0-Sim , 1-Nao): '))

                plt.plot(tempoExecucaoGCP, tamanhoMat, color='red' , label='Gauss com Pivo')
                plt.plot(tempoExecucaoGSP, tamanhoMat, color='blue' , label='Gauss sem Pivo')
                plt.plot(tempoExecucaoGJ, tamanhoMat, color='green' , label='Gauss Jacobi')
                plt.plot(tempoExecucaoGS, tamanhoMat, color='yellow' , label='Gauss Seidel')
                plt.xlabel("Tempo")
                plt.ylabel("Tamanho")
                plt.title("Grafico de Tempo")
                plt.legend()
                plt.show()

            if subMenu ==2:

                flag=0
                tamanhoMat=[]
                numIteracoesGJ=[]
                numIteracoesGS=[]

                while flag==0:

                    nomeArquivo = raw_input('Digite o nome do arquivo: ')
                    erroMax = float(input('Digite o valor do erro maximo: '))
                    iterMax = float(raw_input('Digite o valor maximo de iteracoes: '))

                    erroVet=[]

                    matriz = lerArquivo(nomeArquivo)
                    tamanho = tamanhoArquivo(nomeArquivo)
                    tamanhoMat.append(tamanho)

                    x=0
                    erroMax=0.000000000000001
                    iterMax = 50
                    r, k=jacobi(matriz, x, erroMax, iterMax,1)
                    numIteracoesGJ.append(k)

                    matriz = lerArquivo(nomeArquivo)

                    x=0
                    r, k =seidel(matriz,x, erroMax, iterMax,1)
                    numIteracoesGS.append(k)

                    flag = int(input('\n\nDeseja adicionar mais algum sistema? (0-Sim , 1-Nao): '))

                plt.plot(numIteracoesGJ, tamanhoMat, color='red' , label='Gauss Jacobi')
                plt.plot(numIteracoesGS, tamanhoMat, color='blue' , label='Gauss Seidel')
                plt.xlabel("Iteracoes")
                plt.ylabel("Tamanho")
                plt.title("Numero de iteracoes x metodo x tamanho do sistema")
                plt.legend()
                plt.show()

        if menu == 3:

            nomeArquivo = raw_input('Digite o nome do arquivo: ')

            matriz = lerArquivo(nomeArquivo)
            print'\nMatriz de Entrada\n'
            imprimirMatriz(matriz)

            print '\nEleminacao de Gauss sem Pivotamento'
            resp=eliminacaoGauss(matriz,0)

            matriz = lerArquivo(nomeArquivo)
            residuoGSP=calculaResiduo(matriz, resp)
            print'\nresiduo ',(residuoGSP)

            print '\nEleminacao de Gauss com Pivotamento'
            resp=eliminacaoGauss(matriz,1)

            matriz = lerArquivo(nomeArquivo)
            residuoGCP=calculaResiduo(matriz, resp)
            print'\nresiduo ',(residuoGCP)

            erroMax = 0.000000000000001
            iterMax = 50
            x=0

            print '\nGauss Jacobi'
            r, k=jacobi(matriz,x, erroMax, iterMax,1)

            matriz = lerArquivo(nomeArquivo)
            residuoGJ=calculaResiduo(matriz, r)
            print'\nresiduo ',(residuoGJ)

            print '\nGauss Seidel'
            r, k =seidel(matriz,x, erroMax, iterMax,1)

            matriz = lerArquivo(nomeArquivo)
            residuoGS=calculaResiduo(matriz, r)
            print'\nresiduo ',(residuoGS)

            tamanho = tamanhoArquivo(nomeArquivo)

            rTaxa = [0,0,0,0]

            for x in xrange(0,tamanho):

                if residuoGSP[x]<0:
                    rTaxa[0]=rTaxa[0]+1

                if residuoGCP[x]<0:
                    rTaxa[1]=rTaxa[1]+1

                if residuoGJ[x]<0:
                    rTaxa[2]=rTaxa[2]+1

                if residuoGS[x]<0:
                    rTaxa[3]=rTaxa[3]+1

            maiorTaxa=0
            for x in xrange(0,4):
                if rTaxa[x]>maiorTaxa:
                    iMaior=x
                    maiorTaxa=rTaxa[x]

            if iMaior == -1:
                print '\n\nOs Metodos tem os mesmos residuos!!!!\n'
            else:
                if iMaior==0:
                    print '\n\nGauss sem pivotamento tem mais residuos!!!!\n'

                if iMaior==1:
                    print '\n\nGauss com pivotamento tem mais residuos!!!!\n'

                if iMaior==2:
                    print '\n\nGauss Jacobi tem mais residuos!!!!\n'

                if iMaior==3:
                    print '\n\nGauss Seidel tem mais residuos!!!!\n'

principal()


from typing import List, Tuple, Optional
import numpy as np
from matplotlib import pyplot as plt

def rmse(x: List[float], y: List[float]) -> float:
    r = 0
    for (a, b) in zip(x, y):
        r += (a - b) ** 2
    return r

def lin_reg(data: List[Tuple[float, float]]) -> Tuple[float, float]:
    d = np.array(data)
    m = d.shape[0]
    p = np.sum(d[:, 0])
    q = np.sum(d[:, 1])
    r = np.sum(d[:, 0] * d[:, 1])
    s = np.sum(d[:, 0] ** 2)
    d = (m + 1) * s - p ** 2
    a = ((m + 1) * r - p * q) / d
    b = (s * q - p * r) / d
    return (a, b)

class LinearRegressor():
    def __init__(self):
        self._coeffs = None  # type: Optional[Tuple[float, float]]

    def fit(self, data: List[Tuple[float, float]]) -> None:
        self._coeffs = lin_reg(data)

    def predict(self, x: List[float]) -> List[float]:
        return [self.coeffs[0] * xi + self.coeffs[1] for xi in x]

    @property
    def coeffs(self) -> Tuple[float, float]:
        if self._coeffs is None:
            raise Exception('You need to call `fit` on the model first.')

        return self._coeffs

X = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Y = [0.5, 3, 2.6, 4.4, 5.3, 6.9, 7.01, 8.12, 10]
agressor = LinearRegressor()
agressor.fit(list(zip(X, Y)))
print(rmse(agressor.predict(X), Y))

def plot_data(x: List[float], y: List[float]) -> None:
    lr = LinearRegressor()
    lr.fit(list(zip(X, Y)))
    plt.scatter(x, y)
    plt.plot(x, agressor.predict(x))
    plt.show()

y = [392.5, 46.2, 15.7, 422.2, 119.4, 170.9, 56.9, 77.5, 214, 65.3, 20.9, 248.1, 23.5, 39.6, 48.8, 6.6, 134.9, 50.9,
     4.4, 113, 14.8, 48.7, 52.1, 13.2, 103.9, 77.5, 11.8, 98.1, 27.9, 38.1, 0, 69.2, 14.6, 40.3, 161.5, 57.2, 217.6,
     58.1, 12.6, 59.6, 89.9, 202.4, 181.3, 152.8, 162.8, 73.4, 21.3, 92.6, 76.1, 39.9, 142.1, 93, 31.9, 32.1, 55.6,
     133.3, 194.5, 137.9, 87.4, 209.8, 95.5, 244.6, 187.5]
x = [108, 19, 13, 124, 40, 57, 23, 14, 45, 10, 5, 48, 11, 23,
     7, 2, 24, 6, 3, 23, 6, 9, 9, 3, 29,
     7, 4, 20, 7, 4, 0, 25, 6, 5, 22, 11, 61, 12, 4, 16, 13, 60, 41,
     37, 55, 41, 11, 27, 8, 3, 17, 13,
     13, 15, 8, 29, 30, 24, 9, 31, 14, 53, 26]

plot_data(x, y) # do bani


import numpy as np
import math
class Loss:
    def __init__(self): return None

    #good for regression
    def MeanSquareError(self, output, target, Derivative=False):
        if not Derivative:
            return np.sum( (output - target)**2 )/target.shape[0]
        else:
            return 2.0 / target.shape[0] * (output - target)
    #good for classification
    def CrossEntropyLoss(self, output, target, Derivative=False):
        if not Derivative:
            return -(np.sum( target * np.log(output+1e-8) ))/target.shape[0]
        else:
            return (output - target) * target


import numpy as np

np.random.seed(1)

def non_linear(x, derivative=False):
  if(derivative==True):
    return (x*(1-x))

  return 1/(1+np.exp(-x))

class Network(object):
  def __init__(self,
    input_size=3,
    intermediate_size=4,
    intermediate_depth=2,
    output_size=1,
    non_linear=non_linear,
  ):
    self.non_linear = non_linear
    self.synapses_connecting_layers = []
    self.synapses_connecting_layers.append(
      2*np.random.random((input_size,intermediate_size))-1 # input_size X intermediate_size matrix of random weights
    )
    for i in range(intermediate_depth-1):
      self.synapses_connecting_layers.append(
        2*np.random.random((intermediate_size,intermediate_size))-1
      )
    self.synapses_connecting_layers.append(
      2*np.random.random((intermediate_size,output_size))-1
    )

  def train(self, input_data, output_data, iterations=100000, verbose=False):
    for j in range(iterations):
      #run training input data through net and get neurons
      layers_of_neurons = self.analyze(input_data)

      #back propagation:

      #calculate amounts to adjust synaptic weights working backwards from associated output_data
      layer_of_neurons_error = output_data - layers_of_neurons[-1] #last layer of neurons
      if(j % 10000) == 0 and verbose:
        print("Error: {}".format(np.mean(np.abs(layer_of_neurons_error))))
      layers_of_neurons_delta = [
        layer_of_neurons_error*self.non_linear(layers_of_neurons[-1], derivative=True)
      ]

      for idx in range(len(layers_of_neurons)-2, 0, -1): #skip last layer of neurons
        layer_of_neurons_error = layers_of_neurons_delta[0].dot(
          self.synapses_connecting_layers[idx].T
        )
        layers_of_neurons_delta.insert(0,
          layer_of_neurons_error * self.non_linear(layers_of_neurons[idx], derivative=True)
        )

      for idx in range(len(self.synapses_connecting_layers)-1, -1, -1):
#idx, synapses_connecting_layer in enumerate(reversed(self.synapses_connecting_layers)):
        self.synapses_connecting_layers[idx] += layers_of_neurons[idx].T.dot(layers_of_neurons_delta[idx])
    return layers_of_neurons

  def analyze(self, input_data):
    #calculate neuron values based on prior neuron values times synaptic weights.
    layers_of_neurons = []
    layers_of_neurons.append(input_data)
    for synapses_between_two_layers in self.synapses_connecting_layers:
      layers_of_neurons.append(self.non_linear(
        np.dot(layers_of_neurons[-1], synapses_between_two_layers)
      ))
    return layers_of_neurons

def main(
  verbose,
  intermediate_size,
  intermediate_depth,
  training_data_in,
  training_data_out,
  analyze_data_in
):
  input_size = training_data_in.shape[1]
  output_size = training_data_out.shape[1]

  network = Network(
    input_size=input_size,
    intermediate_size=intermediate_size,
    intermediate_depth=intermediate_depth,
    output_size=output_size
  )
  layers_of_neurons = network.train(training_data_in, training_data_out, verbose=verbose)
  print("Result -> {}".format(str(
    network.analyze(analyze_data_in)[-1]
  )))

import sys, getopt
import pandas as pd

if __name__ == "__main__":
  help = (
    'usage: {} [options] [-s size |-d depth] -i index filename_training_csv analyze_data_in\n\n'
    'where:\n'
    '    size = size of intermediate node layers. Defaults to 3 if not provided.\n'
    '    depth = depth of intermediate node layers. Defaults to 1 if not provided.\n'
    '    index = column index (zero based) in filename_training_csv from which to slice training results\n'
    '    filename_training_csv = name of file with comma-delimited training data where [:index] is training input and [index:] is training output. First line is ignored.\n'
    '    analyze_data_in = comma delimited list of ints, e.g. 1,0,0\n\n'
    'Options\n'
    '    -v     verbose output\n'.format(sys.argv[0])
  )
  size = 3
  depth = 1
  verbose = False
  index = None
  try:
    (opts, args) = getopt.getopt(sys.argv[1:], "hvs:d:i:")
  except getopt.GetoptError:
    print(help)
    sys.exit(2)
  if len(args) < 2:
    print(help)
    sys.exit(2)
  for opt, arg in opts:
    if opt == '-h':
      print(help)
      sys.exit()
    elif opt in ('-s',):
      size = int(arg)
    elif opt in ('-d',):
      depth = int(arg)
    elif opt in ('-v',):
      verbose = True
    elif opt in ('-i',):
      index = int(arg)
  if index is None:
    print(help)
    sys.exit(2)
  #read data
  df_training = pd.read_csv(args[0], header=None, skiprows=[0])
  training_data_in = df_training.iloc[:,:index].values
  training_data_out = df_training.iloc[:,index:].values

  analyze_data_in = np.array(list(map(int, args[1].split(','))))

  main(
    verbose,
    size,
    depth,
    training_data_in,
    training_data_out,
    analyze_data_in
  )


import numpy as np

def tanh(x):
    return np.tanh(x)

def tanh_prime(x):
    return 1-np.tanh(x)**2

# loss function and its derivative
def mse(y_true, y_pred):
    return np.mean(np.power(y_true-y_pred, 2))

def mse_prime(y_true, y_pred):
    return 2*(y_pred-y_true)/y_true.size


from scipy.spatial.distance import pdist
from scipy.spatial.distance import squareform
import numpy as np

def centering(n):
  I = np.identity(n)
  O = np.ones((n, n))
  return I - O/n

def double_centering(D):
  n, n = D.shape
  J = centering(n)
  return (-1/2)*J*D*J

def eig_top(B, n):
  w, v = np.linalg.eig(B)
  i = np.argsort(w)[::-1]
  w, v = w[i].real, v[:,i].real
  return w[:n], v[:,:n]

"""Args:
X: input samples, array (num, dim)
n_components: dimension of output data

Returns:
Y: output samples, array (num, n_components)
"""
def MDS(X, n_components=2):
  D = squareform(pdist(X) ** 2)
  B = double_centering(D)
  w, E = eig_top(B, n_components)
  A = np.diag(np.sqrt(np.abs(w)))
  Y = E.dot(A)
  return Y


import numpy as np

# MCPI > Monte-Carlo PI estimation
# Array opeations:
#  * vrand (x2)
#  * vsum (x1)
#  * vsquare (x2)
#  * vadd2 (x1)
#  * vless2 (x1)
####
def MCPI(N):
    # Generate N points (x, y) with x,y in [0, 1]
    x = np.random.rand(1, N)  # vrand
    y = np.random.rand(1, N)  # vrand
    # Count points in circle
    # vsum, vsquare, vadd2, vless2
    pointsInCircle = np.sum(
        np.less((np.add(np.power(x, 2), np.power(y, 2))), 1))
    # Compute ratio of points in circle and all points
    piValue = 4 * pointsInCircle / N  # (!) scalars discarded


import numpy as np
import math
def Sphere(ind):
    sum = 0
    for i in ind:
        sum += i**2
    return sum

def Rastrigin(ind):
    sum = 10 * len(ind)
    for i in ind:
        sum += i**2 - 10 * np.cos(2*np.pi*i)
    return sum

def Rosenbrock(ind):
    sum = 0
    for i in range(len(ind) - 1):
        sum += 100 * (ind[i + 1] - ind[i]**2)**2 + (ind[i] - 1)**2
    return sum

def Griewank(d):
    sum_1 = 0
    for i in d:
        sum_1 += (i*i)/4000
    sum_2 = 1
    for i in range(len(d)):
        sum_2 *= np.cos(d[i]/math.sqrt(i + 1))
    return sum_1 - sum_2 + 1

def Ackley(d):
    a = 20
    b = 0.2
    c = 2 * np.pi
    sum1 = 0
    sum2 = 0
    for i in range(len(d)):
        sum1 += d[i] ** 2
        sum2 += np.cos(c * d[i])
    term1 = -a * np.exp(-b * np.sqrt(sum1 / len(d)))
    term2 = -np.exp(sum2 / len(d))

    return term1 + term2 + a + np.exp(1)


import torch as th
import torch.nn as nn
import numpy as np
'''
prediction:
gradient computation:
loss computation:
parameter updates:
'''

'''Complete training pipeline with torch'''

# 1. Design model(input_size, output_size, forward pass)
# 2. Construct Loss and optimizer
# 3. Training loop
#   - forward pass:
#   - backward pass:
#   - update weights

# f = w*x
# f = 2.x

X = th.tensor([[1],[2],[3],[4]],dtype=th.float32)
Y = th.tensor([[2],[4],[6],[8]],dtype=th.float32)

X_test = th.tensor([5], dtype=th.float32)

n_samples, n_features = X.shape
print(n_samples, n_features)

input_size = n_features
output_size = n_features
# model = nn.Linear(input_size, output_size)

#custom model

class LinearRegression(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(LinearRegression,self).__init__()
        # define layers
        self.lin = nn.Linear(input_dim, output_dim)

    def forward(self, x):
        return self.lin(x)

model = LinearRegression(input_size, output_size)
# w = th.tensor(0.0,dtype=th.float32,requires_grad=True)

# def forward(x):
#     return w*x

# def loss(y,yp):
#     return ((y-yp)**2).mean()

# def gradient(x,y,yp):
#     #MSE = 1/N*(wx-y)**2
#     #dJ/dw = 1/N*2x*(w*x - y)
#     return np.dot(2*x, yp-y).mean()

print(f'prediction before training f(5)= {model(X_test).item():.3f}')

# training
lr = 0.01
n_iters = 100

loss = nn.MSELoss()
optimizer = th.optim.SGD(model.parameters(),lr=lr)

for epoch in range(n_iters):
    # prediction
    y_pred = model(X)

    # loss
    l = loss(Y,y_pred)

    #calculate gradients
    l.backward() # dl/dw

    # update weights
    # before optim step, gradients are already there wrt loss function in w.grad aha!
    optimizer.step()

    # zero gradients
    optimizer.zero_grad()

    if epoch%10==0:
        [w,b] = model.parameters()
        print(f'epoch {epoch+1}: w: {w[0][0]}, b: {b[0]}, loss = {l:.8f}')

print(f'Prediction after training: f(5) = {model(X_test).item():.3f}')