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

''' polynomial regression
It is a form of regression analysis in which
the relationship between the independent variable x
and the dependent variable y is modelled as an
nth degree polynomial in x.

y = c0 * x^0 + c1 * x^1 +
    c2 * x^2 + c3 * x^3 + ..... + cn * x ^n
n = degree

PolynomialFeatures :

It generate a new feature matrix
consisting of all polynomial combinations of the
features with degree less than or equal to the
specified degree
'''

import random
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

''' sample polynomial equation with degree=3'''
''' t =    24.0 * x^0
         + 12.0 * x^1
         -  0.5 * x^2
         +  9.0 * x^3
'''

constant     = 24
error_induce = 0.01

def fx1(x1):
    res   = (12.0  * x1)
    error = 0.0
    error = res * random.uniform(-error_induce, error_induce)
    return res + error
def fx2(x2):
    res   = (-0.50 * x2 * x2)
    error = 0.0
    error = res * random.uniform(-error_induce, error_induce)
    return res + error
def fx3(x3):
    res   = (09.0 * x3 * x3 * x3)
    error = 0.0
    error = res * random.uniform(-error_induce, error_induce)
    return res + error

''' data preparation '''
max_sample_value = 50
total_samples    = 800
train_sample_cnt = int((total_samples * 60.0 / 100.0))
test_sample_cnt  = total_samples - train_sample_cnt

y1_samples       = fx1(np.arange(total_samples))
y2_samples       = fx2(np.arange(total_samples))
y3_samples       = fx3(np.arange(total_samples))

t_samples        = y1_samples + y2_samples + y3_samples

t_samples        = t_samples + constant

''' splitting samples into train data and test data '''
y1_samples_train, y1_samples_test = np.split(
                                     y1_samples, [train_sample_cnt,])
y2_samples_train, y2_samples_test = np.split(
                                     y2_samples, [train_sample_cnt,])
y3_samples_train, y3_samples_test = np.split(
                                     y3_samples, [train_sample_cnt,])
t_samples_train, t_samples_test   = np.split(
                                     t_samples, [train_sample_cnt,])

''' combining all variables in column structure '''
xyz_samples_train   = {'colx1' : y1_samples_train,
                       'colx2' : y2_samples_train,
                       'colx3' : y3_samples_train}
dfxyz_samples_train = pd.DataFrame(data=xyz_samples_train)
dft_samples_train   = pd.DataFrame(data=t_samples_train)

xyz_samples_test    = {'colx1' : y1_samples_test,
                       'colx2' : y2_samples_test,
                       'colx3' : y3_samples_test}

dfxyz_samples_test  = pd.DataFrame(data=xyz_samples_test)
dft_samples_test    = pd.DataFrame(data=t_samples_test)

''' use PolynomialFeatures to fit and transform a model'''
poly     = PolynomialFeatures(degree=3, include_bias=False)
poly_fit = poly.fit_transform(
            np.arange(train_sample_cnt)[:, np.newaxis])

''' prepare a linear regression model finally for prediction '''
lm            = LinearRegression().fit(poly_fit, dft_samples_train)
predictions   = lm.predict(dfxyz_samples_test)

print("predictions   = ", predictions)
print("lm.score(X,y) = ", lm.score(dfxyz_samples_test,
                                   dft_samples_test))
print("lm.coef_      = ", lm.coef_)
print("lm.intercept_ = %.2f" %lm.intercept_)

''' now we are going to plot the points
    just the difference between actual and expected '''
prediced_difference = np.subtract(
                            dft_samples_test.values,
                            predictions)
plt.title('Polynomial Regression - difference between actual and expected', fontsize=16)
plt.xlabel('test sample count', fontsize=14)
plt.ylabel('difference value' , fontsize=14)

plt.plot(np.arange(prediced_difference.size),
         prediced_difference,
         color='b')
plt.show()


# The toy problem in http://arxiv.org/abs/1403.4890
import numpy as np

def main(job_id, params):
  x1 = params['x']
  x2 = params['y']

  f  = x1 + x2
  c1 = 1.5 - x1 - 2.0*x2 - 0.5*np.sin(2*np.pi*(x1**2 - 2.0*x2))
  c2 = x1**2 + x2**2 - 1.5
  c1 = -c1
  c2 = -c2

  return {'f':f, 'c1':c1, 'c2':c2}

"""
Everything below this point is optional. It is used to specify
the true solution so that one can plot the error curve using
progress_curve.py in the visualizations/ directory.
"""
def true_val():
    return 0.5998
def true_sol():
    return {'x' : 0.1954, 'y' : 0.4044}
true_func = main


import numpy as np

init_arr = np.array([[1,2,3,4,5],[6,7,8,9,10]])
print(init_arr)

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

# All zero matrix
zeros_one = np.zeros((1,2,2))
print(zeros_one)

# All ones matrix
ones_one = np.ones((1,2,2), dtype="int32")
print(ones_one)

# array with  specific number. (2,2) it shows shape, 4 is the number we wanna add to matrix.
arr_one = np.full((2,2), 4)
print(arr_one)

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

# full like method use when we want use an array that built before
print(np.full_like(init_arr, 2))

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

# random matrix
print(np.random.rand(4,4))

print("----------------------------")
print(np.random.rand(2,2,3))

print("----------------------------")
# random matrix with shape
print(np.random.random_sample(init_arr.shape))

print("----------------------------")
# random matrix with integer value, random number below 5
print(np.random.randint(5, size=(3,3)))

print("----------------------------")
# random number in specific interval
print(np.random.randint(3,9, size=(4,4)))

print("----------------------------")
# create identical matrix, we only path one value, because identity matrix must be square
print(np.identity(3))

# copying array
n_arr = np.array([1,2,3,4])
o_arr = n_arr.copy()
o_arr[0] = 69
print(n_arr)
print(o_arr)

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


import matplotlib.pyplot as plt
import numpy as np
from ECDF import ecdf

samples_std1 = np.random.normal(20, 1, size=100000)
samples_std3 = np.random.normal(20, 3, size=100000)
samples_std10 = np.random.normal(20, 10, size=100000)

# Generate CDFs
x_std1, y_std1 = ecdf(samples_std1)
x_std3, y_std3 = ecdf(samples_std3)
x_std10, y_std10 = ecdf(samples_std10)

# Plot CDFs
plt.plot(x_std1, y_std1, marker='.', linestyle='none')
plt.plot(x_std3, y_std3, marker='.', linestyle='none')
plt.plot(x_std10, y_std10, marker='.', linestyle='none')

# Make a legend and show the plot
plt.legend(('std = 1', 'std = 3', 'std = 10'), loc='lower right')
plt.show()


#!/usr/bin/env python3
import numpy as np

def vxadd(v1, v2):
    """vector addition
    :param v1: first vector
    :param v2: second vector
    :return v_out: v_out = v1 + v2
    """
    v_out = np.zeros(len(v1))
    for ii in range(len(v1)):
        v_out[ii] = v1[ii]+v2[ii]
    return np.array(v_out)

def vdotv(v1, v2):
    """vector dot product
    :param v1: first vector
    :param v2: second vector
    :return: v_out = sum of (v1[ii] * v2[ii])
    """
    v_out = 0
    v1 = np.array(v1)
    v2 = np.array(v2)
    for ii in range(len(v1)):
        v_out += v1[ii]*v2[ii]
    return np.array(v_out)

def norm(v1):
    """returns the magnitude of the two vectors
    in work
    """
    return np.array(np.linalg.norm(v1))

def vcrossv(v1, v2):
    """cross product of two vectors of length 3
    :param v1: first vector (top)
    :param v2: second vector (bottom)
    :return v_out: v_out = v1xv2
    """
    v_out = np.zeros(len(v1))
    v_out[0] = v1[1]*v2[2] - v1[2]*v2[1]
    v_out[1] = -(v1[0]*v2[2] - v1[2]*v2[0])
    v_out[2] = v1[0]*v2[1] - v1[1]*v2[0]
    return np.array(v_out)

def vxvT(v1, v2):
    """vector multiplication
    :param v1: first row vector;
    :param v2: second row vector;
    :return m_out: matrix representing vector product;
                   m_out = v1*v2.T
    """
    m_out = np.zeros((len(v1), len(v1)))
    for ii in range(len(v1)):
        for jj in range(len(v1)):
            m_out[ii][jj] = v1[ii]*v2[jj]
    return np.array(m_out)

def v_cross(v1):
    """returns skew symmetric matrix
    """
    v_tilde = [[    0., -v1[2],  v1[1]],
               [ v1[2],     0., -v1[0]],
               [-v1[1],  v1[0],     0]]
    return np.array(v_tilde)

def vxs(scalar, v1):
    """scales a vector by a scalar
    :param scalar: scalar component to multiply
    :param v1: vector to be scaled
    :return v_out: resultant vector; v_out = scalar*v1
    """
    v_out = np.zeros((len(v1)))
    for ii in range(len(v1)):
        v_out[ii] = scalar * v1[ii]
    return np.array(v_out)

def get_dir_cosines(v1):
    """returns the directional cosines for a given vector
    :param v1: unit vector:
    :return: cosines and sines of vector;
    in work
    """
    cosa = v1[0]
    cosb = v1[1]
    cosc = v1[2]
    sina = np.sqrt(v1[1]**2+v1[2]**2)
    sinb = np.sqrt(v1[2]**2+v1[0]**2)
    sinc = np.sqrt(v1[0]**2+v1[1]**2)
    return cosa, cosb, cosc, sina, sinb, sinc

if __name__ == "__main__":

    pass


# Cowboy Linear Algebra python library at Video #7
# By: Nick the Space Cowboy

import numpy as np

def back_sub(Utri, b):
	# back substitution alogrithm for solving upper triangular system
	n = len(Utri) # row dimension of the Utri matrix
	x = np.zeros_like(b, dtype=np.float64)
	for i in range(n - 1, -1, -1):	# loop to iterate through row index
		x[i] += b[i] / Utri[i,i]
		for j in range(n-1, i, -1):	# loop to iterate through the off diagonal Sum part
			x[i] += (- (Utri[i, j] * x[j])) / Utri[i,i]
	return x

def SGE(A, b=None):
	'''
	function to perform structured gaussian elimination
	If a b vector isn't passed through, a LU decomposition is returned
	'''
	n = len(A)
	L = np.identity(n, dtype=np.float64)
	for i in range(0, n, 1):
		for j in range(i+1, n, 1):
			L[j,i] = A[j,i] / A[i,i]
			A[j] = A[j] - (L[j,i] * A[i])
			if b == None:
				pass
			else:
				b[j] = b[j] - (L[j,i] * b[i])
	if b==None:
		return L, A
	else:
		return A, b

def LU(A):
	'''
	Function to perform LU decomposition
	'''
	return SGE(A)

def LDV(A):
	'''
	Function to perform a LDV Matrix decomposition
	'''
	L, U = LU(A)
	n = len(A)
	D = np.identity(n, dtype=np.float64)
	for i in range(0, n, 1):
		D[i, i] = U[i, i]
	V = np.dot(np.linalg.inv(D), U)
	return L, D, V

def pos_def_check(A):
	'''
	Checks if a matrix is positive definate
	'''
	pd_state = True
	n = len(A)
	for i in range(1, n):
		if (A[i, i - 1] ** 2) >= A[i, i]:
			raise Exception ("Marix is not positive definate")
			pd_state =  False
	return pd_state

def cholesky(A):
	'''
	Function to perform a Cholesky decompostion
	'''
	pd = pos_def_check(A)
	if pd == True:
		n = len(A)
		L = np.zeros_like(A, dtype=np.float64)
		for i in range(0, n):
			for j in range(0, i + 1):
				sum_part = 0
				if i == j:
					for k in range(0, j):
						sum_part += L[j, k] ** 2
					L[i, j] = np.sqrt(A[j, j] - sum_part)
				else:
					for k in range(0, j):
						sum_part += L[i, k] * L[j, k]
					L[i, j] = (A[i, j] - sum_part) / L[j, j]
	else:
		L = f"Matrix is not positive definate"
	return L

def LDLT(A):
	'''
	Function to perform the LDL^T matrix decomposition
	'''
	C = cholesky(A)
	n = len(A)
	D = np.identity(n, dtype=np.float64)
	L = C
	for i in range(0, n):
		D[i, i] = C[i, i]**2
		L[:, i] = C[:, i] * D[i, i] ** (-1/2)
	return L, D


from __future__ import print_function

import numpy as np
import matplotlib.pyplot as plt

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures

# For reproducibility
np.random.seed(1000)

nb_samples = 200

def show_dataset(X, Y):
    fig, ax = plt.subplots(1, 1, figsize=(30, 25))

    ax.scatter(X, Y)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.grid()

    plt.show()

if __name__ == '__main__':
    # Create dataset
    X = np.arange(-5, 5, 0.05)

    Y = X + 2
    Y += X**2 + np.random.uniform(-0.5, 0.5, size=nb_samples)

    # Show the dataset
    show_dataset(X, Y)

    # Split dataset
    X_train, X_test, Y_train, Y_test = train_test_split(X.reshape(-1, 1), Y.reshape(-1, 1), test_size=0.25)

    lr = LinearRegression(normalize=True)
    lr.fit(X_train, Y_train)
    print('Linear regression score: %.3f' % lr.score(X_train, Y_train))

    # Create polynomial features
    pf = PolynomialFeatures(degree=2)
    X_train = pf.fit_transform(X_train)
    X_test = pf.fit_transform(X_test)

    lr.fit(X_train, Y_train)
    print('Second degree polynomial regression score: %.3f' % lr.score(X_train, Y_train))


import numpy as np

class ActivationFunctions:
    @staticmethod
    def ReLU(x):
        return np.maximum(x, 0)

    @staticmethod
    def Sigmoid(x):
        return 1 / (1 + np.exp(-x))

    @staticmethod
    def LeakyReLU(x):
        return x if x > 0 else 0.01 * x


import numpy as np
from numpy.random import rand
import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')
from matplotlib import pyplot as plt

from solutions import sqrt64

def invsqrt64(A, reps):
    Ac = A.copy()
    if 0 < reps:
        Ac2 = A.copy()
        Ac2 /= - 2
        Ac3 = np.empty_like(Ac)
    I = Ac.view(dtype=np.int64)
    I >>= 1
    I *= -1
    I += 0x5fe6ec85e7de30da #hexadecimal representation of the constant
    for j in xrange(reps):
        Ac3[:] = Ac
        Ac3 *= Ac
        Ac3 *= Ac2
        Ac3 += 1.5
        Ac *= Ac3
    return Ac

def sqrt0():
    X = np.linspace(0, 3, 501)
    plt.plot(X, sqrt64(X, 0), X, np.sqrt(X))
    plt.savefig("sqrt0.pdf")
    plt.clf()

def sqrt1():
    X = np.linspace(0, 3, 501)
    plt.plot(X, sqrt64(X, 1), X, np.sqrt(X))
    plt.savefig("sqrt1.pdf")
    plt.clf()

def invsqrt0():
    X = np.linspace(.1, 3, 291)
    plt.plot(X, invsqrt64(X, 0), X, 1./np.sqrt(X))
    plt.savefig("invsqrt0.pdf")
    plt.clf()

def invsqrt1():
    X = np.linspace(.1, 3, 291)
    plt.plot(X, invsqrt64(X, 1), X, 1./np.sqrt(X))
    plt.savefig("invsqrt1.pdf")
    plt.clf()

if __name__ == "__main__":
    sqrt0()
    sqrt1()
    invsqrt0()
    invsqrt1()


import numpy             as np
import scipy.sparse      as sparse
import scipy.linalg      as linear
import scipy.special     as fun

dtype = np.float128

def sparse_symm_to_banded(matrix):
    """Convert sparse symmetric to upper-banded form."""
    diag = matrix.todia()
    B, I  = max(abs(diag.offsets)), diag.data.shape[1]
    banded = np.zeros((B+1, I), dtype=diag.dtype)
    for i, b in enumerate(diag.offsets):
        if b >= 0:
            banded[B-b] = diag.data[i]
    return banded

def grid_guess(Jacobi_matrix,symmetric=True):
    """Returns reasonable Gauss quadrature grid as the Jacobi matrix eigenvalues.
       For P(z) = [p_{0}(z),...,p_{N}(z)], J.P(z) = z P(z); or p_{N+1}(z) = 0.

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       symmetric: T/F
       A normalised metric implies a self-adjoint matrix is symmetric.

    """

    if symmetric:
        J_banded = sparse_symm_to_banded(Jacobi_matrix)
        return (np.sort(linear.eigvals_banded(J_banded).real)).astype(dtype)
    else:
        J_dense = Jacobi_matrix.todense()
        return (np.sort(linear.eigvals(J_dense).real)).astype(dtype)

def remainders(Jacobi_matrix,grid):
    """Given length-N three-term recursion, returns P_{N+1}(grid) and P'_{N+1}(grid)

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       grid: float array

    """

    J, z, N = sparse.dia_matrix(Jacobi_matrix).data, grid, len(grid)

    P = np.zeros((N,N),dtype=dtype)
    D = np.zeros((N,N),dtype=dtype)

    P[0] = np.ones(N,dtype=dtype)
    D[1] = P[0]/J[-1][1]

    if np.shape(J)[0] == 3:
        P[1]  = (z-J[1][0])*P[0]/J[-1][1]
        for n in range(2,N):
            P[n] = ((z-J[1][n-1])*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
            D[n] = ((z-J[1][n-1])*D[n-1] - J[0][n-2]*D[n-2])/J[-1][n] + P[n-1]/J[-1][n]
        return ((z-J[1][N-1])*P[N-1] - J[0][N-2]*P[N-2]), ((z-J[1][N-1])*D[N-1] - J[0][N-2]*D[N-2]+P[N-1])

    P[1]  = z*P[0]/J[-1][1]
    for n in range(2,N):
        P[n] = (z*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
        D[n] = (z*D[n-1] - J[0][n-2]*D[n-2])/J[-1][n] + P[n-1]/J[-1][n]

    return (z*P[N-1] - J[0][N-2]*P[N-2]), (z*D[N-1] - J[0][N-2]*D[N-2] + P[N-1])

def gauss_quadrature(Jacobi_matrix,mass=1,niter=3,guess=None,report_error=False):
    """Returns accurate grid and weights for general Gauss quadrature.

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       mass: float
       mass = integral_{-1}^{+1} w(z)dz
       niter: int
       Number of times to run the Newton iteration for P_{N+1}(grid) = 0
       guess: float array
       Optional grid estimate
       report_error: T/F
       Show the error estimate in the grid values.

    """

    if guess == None:
        z = grid_guess(Jacobi_matrix)
    else:
        z = guess

    #Newton iteration
    for i in range(niter):
        P, D = remainders(Jacobi_matrix,z)
        if report_error: print(np.max(np.abs(P/D)))
        z = z - P/D

    w = 1/((1-z**2)*D**2)
    w = (mass/np.sum(w))*w

    return z, w

def three_term_recursion(Jacobi_matrix,grid,max_degree,init):
    """Returns weighted orthogonal polynomials on a grid from a three-term recursion.
       P_{-1}(z) = 0; P_{0}(z) = init(z)
       P_{n}(z)  = (A_{n}*z + B_{n})*P_{n-1}(z) + C_{n}*P_{n-2}(z)

       Parameters
       ----------
       Jacobi_matrix: square self-adjoint tri-diagonal matrix (arbitrary metric)
       grid: float array
       max_degree: int
       Returns max_degree + 1 weighted polynomials
       init: float array
       len(init) = len(grid)

    """

    if max_degree==0: return np.array([init])

    J, z, N = sparse.dia_matrix(Jacobi_matrix).data, grid, max_degree+1

    P     = np.zeros((N,len(grid)),dtype=dtype)
    P[0]  = init

    if np.shape(J)[0] == 3:
        P[1]  = (z-J[1][0])*P[0]/J[-1][1]
        for n in range(2,N):
            P[n] = ((z-J[1][n-1])*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
        return P

    P[1]  = z*P[0]/J[-1][1]
    for n in range(2,N):
        P[n] = (z*P[n-1] - J[0][n-2]*P[n-2])/J[-1][n]
    return P

def normalise(functions,weights):
    """unit-normalise functions on a grid according to weighted L2 inner product. """
    for k in range(len(functions)):
        functions[k] = functions[k]/np.sqrt(np.sum(weights*functions[k]**2))

def quadrature(max_degree,a,b,**kw):
    """Returns accurate grid and weights for specific Gauss-Jacobi quadrature.

        Parameters
        ----------
        max_degree: int
        Integrates polynomials on (-1,+1) exactly up to degree = 2*max_degree+1.
        a,b: int
        Jacobi polynomial parameter for generating Jacobi matrix
        niter: int
        Number of times to run the Newton iteration for P_{N+1}(grid) = 0
        guess: float array
        Optional grid estimate
        report_error: T/F
        Show the error estimate in the grid values.

    """

    mu = mass(a,b)
    J  = operator('J',max_degree,a,b)
    return gauss_quadrature(J,mass=mu,**kw)

def envelope(a,b,a0,b0,grid):
    """Returns sqrt( ((1-z)**(a-a0)) * ((1+z)^(b-b0)) / mass(a,b) )
       Integral envelope(z)**2 (1-z)**a0 (1+z)**b0 dz = 1

       Parameters
       ----------
       a,b,a0,b0: float > -1
       grid: float array

    """

    mu = mass(a,b)
    return np.exp( ((a-a0)/2)*np.log(1-grid) + ((b-b0)/2)*np.log(1+grid) )/np.sqrt(mu)

def recursion(max_degree,a,b,grid,init):
    """Returns weighted Jacobi polynomials on a grid from (a,b) three-term recursion

       Parameters
       ----------
       max_degree: int
       a,b: float > -1

    """

    if max_degree==0: return np.array([init])

    J  = operator('J',max_degree,a,b)
    return three_term_recursion(J,grid,max_degree,init)

def push(op,data):
    """Pushforward"""
    return (op).dot(data)

def pull(op,data):
    """Pullback"""
    return (op.transpose()).dot(data)

def mass(a,b):
    """Returns int_{-1}^{+1} (1-z)^{a} (1+z)^b dz.

       Parameters
       a,b:  float > -1, or string = 'inf', 'inf'. 'inf' returns value for a or b --> infinity.
       grid: float array
    """

    if a=='inf' and b=='inf': return np.sqrt(np.pi)
    if a=='inf'             : return np.exp(fun.gammaln(b+1))
    if              b=='inf': return np.exp(fun.gammaln(a+1))
    return np.exp( (a+b+1)*np.log(2) + fun.gammaln(a+1) + fun.gammaln(b+1) - fun.gammaln(a+b+2) )

def operator(op,max_degree,a,b,format='csr',rescale=None):
    """ Returns various martix operators for acting on coefficients
        in a Jacobi polynomial series.

        Parameters
        ----------
        Jacobi polynomial parameter
        max_degree: int
        a,b: float > -1
        Operator options
        op: string = '0', 'I', 'A-', 'A+', 'B-', 'B+', 'C-', 'C+', 'D-', 'D+', 'J', 'z=+1', 'z=-1'
        0  : 0 <a,b| = <a,b| 0
        I  : <a,b| = <a,b| I
        A- : (1-z) <a,b| = <a-1,b| A-
        A+ : <a,b| = <a+1,b| A+
        B- : (1+z) <a,b| = <a,b-1| B-
        B+ : <a,b| = <a,b+1| B+
        C- : ( a - (1-z)*d/dz ) <a,b| = <a-1,b+1| C-
        C+ : ( b + (1+z)*d/dz ) <a,b| = <a+1,b-1| C+
        D- : ( a(1+z) - b(1-z) - (1-z^2)*d/dz ) <a,b| = <a-1,b-1| D-
        D+ : d/dz <a,b| = <a+1,b+1| D+
        J  : z <a,b| = <a,b| J
        z=+1 : <a,b|z=+1>
        z=-1 : <a,b|z=-1>
        format:  string = 'dia', 'csr'
        rescale: float, multiply operator by factor on output

    """

    def diag(bands,locs):
        return sparse.dia_matrix((bands,locs),shape=(len(bands[0]),len(bands[0])))

    N = max_degree+1
    n = np.arange(0,N,dtype=dtype)
    na, nb, nab, nnab = n+a, n+b, n+a+b, 2*n+a+b

    # 0 <a,b| = <a,b| 0
    if op == '0': out = diag([np.zeros(N,dtype=dtype)],[0])

    # 1 <a,b| = <a,b| I
    if op == 'I': out = diag([np.ones(N,dtype=dtype)],[0])

    # (1-z) <a,b| = <a-1,b| A-
    if op == 'A-' and (a>0):
        if a+b==0:
            middle = na/(2*n+1)
            lower  = (nb+1)/(2*n+1)
            middle[0]  = 2*a
        else:
            middle = 2*na*nab/(nnab*(nnab+1))
            lower  = 2*(n+1)*(nb+1)/((nnab+1)*(nnab+2))
        out = diag([-np.sqrt(lower),np.sqrt(middle)],[-1,0])

    # <a,b| = <a+1,b| A+
    if op == 'A+':
        if a+b == 0 or a+b == -1:
            middle = (na+1)/(2*n+1)
            upper  = nb/(2*nab+1)
            middle[0], upper[0] = (1+a)*(1-(a+b)), 0
        else:
            middle = 2*(na+1)*(nab+1)/((nnab+1)*(nnab+2))
            upper  = 2*n*nb/(nnab*(nnab+1))
        out = diag([np.sqrt(middle),-np.sqrt(upper)],[0,+1])

    # (1+z) <a,b| = <a,b-1| B-
    if op == 'B-' and (b > 0):
        if a+b == 0:
            middle = nb/(2*n+1)
            lower  = (na+1)/(2*n+1)
            middle[0] = 2*b
        else:
            middle = 2*nb*nab/(nnab*(nnab+1))
            lower  = 2*(n+1)*(na+1)/((nnab+1)*(nnab+2))
        out = diag([np.sqrt(lower),np.sqrt(middle)],[-1,0])

    # <a,b| = <a,b+1| B+
    if op == 'B+':
        if a+b == 0 or a+b == -1:
            middle = (nb+1)/(2*n+1)
            upper  = na/(2*nab+1)
            middle[0], upper[0] = (1+b)*(1-(a+b)), 0
        else:
            middle = 2*(nb+1)*(nab+1)/((nnab+1)*(nnab+2))
            upper  = 2*n*na/(nnab*(nnab+1))
        out = diag([np.sqrt(middle),np.sqrt(upper)],[0,+1])

    # ( a - (1-z)*d/dz ) <a,b| = <a-1,b+1| C-
    if op == 'C-' and (a > 0):
        out = diag([np.sqrt(na*(nb+1))],[0])

    # ( b + (1+z)*d/dz ) <a,b| = <a+1,b-1| C+
    if op == 'C+' and (b > 0):
        out = diag([np.sqrt((na+1)*nb)],[0])

    # ( a(1+z) - b(1-z) - (1-z^2)*d/dz ) <a,b| = <a-1,b-1| D-
    if op == 'D-' and (a > 0) and (b > 0):
        out = diag([np.sqrt((n+1)*nab)],[-1])

    # d/dz <a,b| = <a+1,b+1| D+
    if op == 'D+':
        out = diag([np.sqrt(n*(nab+1))],[+1])

    # z <a,b| = <a,b| J
    if op == 'J':
        A, B = operator('A+',max_degree,a,b), operator('B+',max_degree,a,b)
        out = 0.5*( pull(B,B) - pull(A,A) )

    # <a,b|z=+1>
    if op == 'z=+1':
        n = np.arange(0,N,dtype=np.float64)
        out = np.sqrt(2*n+a+b+1)*np.sqrt(fun.binom(n+a,a))*np.sqrt(fun.binom(n+a+b,a))
        if a+b==-1:
            out[0] = np.sqrt(np.sin(np.pi*np.abs(a))/np.pi)

    # <a,b|z=-1>
    if op == 'z=-1':
        n = np.arange(0,N,dtype=np.float64)
        out = ((-1)**n)*np.sqrt(2*n+a+b+1)*np.sqrt(fun.binom(n+b,b))*np.sqrt(fun.binom(n+a+b,b))
        if a+b==-1:
            out[0] = np.sqrt(np.sin(np.pi*np.abs(b))/np.pi)

    if (op != 'z=+1') and (op != 'z=-1'):
        if format == 'dia': out = sparse.dia_matrix(out)
        if format == 'csr': out = sparse.csr_matrix(out)

    if rescale == None: return out

    return rescale*out


import wave, struct, time
import numpy as np
import scipy.io.wavfile as wav
import scipy.fftpack as fft

def openWavFile(fileName):
    data = wav.read(fileName)
    ssize = data[1].shape[0]
    nparray = data[1].astype('float32')
    return nparray

def stftWindowFunction(xPhi, xMag):
    oldShapePhi = xPhi.shape
    oldShapeMag = xMag.shape
    xPhi = np.reshape(xPhi, (-1, xPhi.shape[-1]))
    xMag = np.reshape(xMag, (-1, xMag.shape[-1]))
    retValPhi = []
    retValMag = []
    for xValPhi, xValMag in zip(xPhi, xMag):
        w = np.hanning(xValPhi.shape[0])
        phiObj = np.zeros(xValPhi.shape[0], dtype=complex)
        phiObj.real, phiObj.imag = np.cos(xValPhi), np.sin(xValPhi)
        xIfft = np.fft.ifft(xValMag * phiObj)
        wFft = np.fft.fft(w*xIfft.real)
        retValPhi.append(np.angle(wFft))
        retValMag.append(np.abs(wFft))
    retValMag = np.reshape(retValMag, oldShapeMag)
    retValPhi = np.reshape(retValPhi, oldShapePhi)
    return retValPhi, retValMag

def stft(x, framesz, hop):
    framesamp = int(framesz)
    hopsamp = int(hop)
    #w = np.hanning(framesamp)
    X = np.asarray([np.fft.fft(x[i:i+framesamp]) for i in range(0, len(x) - framesamp, hopsamp)])
    xPhi = np.angle(X)
    xMag = np.abs(X)
    return xPhi, xMag

def istft(X, fs, hop, origs):
    x = np.zeros(origs)
    framesamp = X.shape[1]
    hopsamp = int(hop*fs)
    for n,i in enumerate(range(0, len(x)-framesamp, hopsamp)):
        x[i:i+framesamp] += np.real(np.fft.ifft(X[n]))
    return x

def waveToSTFT(waveData, sampCount, blkSize, hop):
    initLen = len(waveData)
    sampSize = int(initLen/sampCount)

    phiObj = []
    magObj = []
    for sInd in xrange(0, sampCount):
        tempTmSpls = []
        sampBlk = waveData[sInd * sampSize:(sInd + 1) * sampSize]
        stftPhi, stftMag = stft(sampBlk, blkSize, hop)
        phiObj.append(stftPhi)
        magObj.append(stftMag)
    return ([], np.asarray(phiObj), np.asarray(magObj))

def waveToMFCC(waveData, sampCount, blkCount=False, blkSize=False):
    waveLen = len(waveData)
    sampSize = int(waveLen/sampCount)
    retTmSpl = []
    if blkSize:
        blkCount = sampSize/blkSize
    elif blkCount:
        blkSize = sampSize/blkCount
    else:
        return False
    for sInd in xrange(0, sampCount):
        tempTmSpls = []
        sampBlk = waveData[sInd * sampSize:(sInd + 1) * sampSize]
        for bInd in xrange(0, blkCount):
            tempBlk = sampBlk[bInd * blkSize:(bInd + 1) * blkSize]

            complexSpectrum = np.fft.fft(tempBlk)
            powerSpectrum = np.abs(complexSpectrum) ** 2
            filteredSpectrum = powerSpectrum
            logSpectrum = np.log(filteredSpectrum)
            dctSpectrum = fft.dct(logSpectrum, type=2)
            tempTmSpls.append(dctSpectrum)
        retTmSpl.append(tempTmSpls)
    retTmSpl = np.asarray(retTmSpl)
    return retTmSpl

def waveToBlock(waveData, sampCount, blkCount=False, blkSize=False, olapf=1, shift=False):
    if shift:
        waveData = np.concatenate((waveData[shift:], waveData[:shift]))
    waveLen = len(waveData)
    sampSize = int(waveLen/sampCount)
    retPhase = []
    retMag = []
    retTmSpl = []
    if blkSize and blkCount:
        tlen = sampCount * blkCount * blkSize
        sampSize = blkCount * blkSize
        diff = tlen - waveLen
        if diff > 0:
            waveData = np.pad(waveData, (0,diff), 'constant', constant_values=0)
    elif blkSize:
        blkCount = sampSize/blkSize
    elif blkCount:
        blkSize = sampSize/blkCount
    else:
        return False
    for sInd in xrange(0, sampCount):
        tempPhases = []
        tempMags = []
        tempTmSpls = []
        sampBlk = waveData[sInd * sampSize:(sInd + 1) * sampSize]
        for bInd in xrange(0, blkCount - (olapf - 1)):
            tempBlk = sampBlk[bInd * blkSize:(bInd + olapf) * blkSize]
            tempFFT = np.fft.fft(tempBlk)
            tempPhase = np.angle(tempFFT)
            tempMagn = np.abs(tempFFT)
            tempPhases.append(tempPhase)
            tempMags.append(tempMagn)
            tempTmSpls.append(tempBlk)
        retPhase.append(tempPhases)
        retMag.append(tempMags)
        retTmSpl.append(tempTmSpls)
    retPhase = np.asarray(retPhase)
    retTmSpl = np.asarray(retTmSpl)
    retMag = np.asarray(retMag)
    return (retTmSpl, retPhase, retMag)

def sectionFeatureScaling(data):
    dataShape = data.shape
    flatData = np.copy(data).flatten()
    flatMax = np.max(flatData)
    flatMin = np.min(flatData)
    scaledData = (flatData - flatMin)/(flatMax- flatMin)
    scaledData = np.reshape(scaledData, dataShape)
    return scaledData, flatMax, flatMin

def blockFeatureScaling(kData):
    data = np.copy(kData)
    maxVal = np.max(np.max(data, axis=0), axis=0)
    minVal = np.min(np.min(data, axis=0), axis=0)
    scaledData = (data - minVal)/(maxVal- minVal)
    return scaledData, maxVal, minVal

def sectionNormalize(data):
    dataShape = data.shape
    flatData = np.copy(data).flatten()
    flatMean = np.mean(flatData)
    flatStd = np.std(flatData)
    scaledData = (flatData - flatMean)/flatStd
    scaledData = np.reshape(scaledData, dataShape)
    return scaledData, flatMean, flatStd

def blockNormalize(data):
    dataStartShape = data.shape
    if len(data.shape) == 2:
        data = np.reshape(data, (1, data.shape[0], data.shape[1]))

    if len(data.shape) == 1:
        data = np.reshape(data, (1, 1, data.shape[0]))
    npNorm = np.zeros_like(data)
    xCount = data.shape[0]
    yCount = data.shape[1]
    for sectInd in xrange(xCount):
        for blockInd in xrange(yCount):
            npNorm[sectInd][blockInd] = data[sectInd][blockInd]
    mean = np.mean(np.mean(npNorm, axis=0), axis=0)
    std = np.sqrt(np.mean(np.mean(np.abs(npNorm-mean)**2, axis=0), axis=0))
    std = np.maximum(1.0e-8, std)
    norm = npNorm.copy()
    norm[:] -= mean
    norm[:] /= std
    return norm, mean, std

def extractSTFTWaveData(wavData, sampCount, blkSize=False, returnObj="all", olapf=100):#(waveData, sampCount, blkCount, blkSize, hop):
    #wavData = openWavFile(fileName)
    #wavObj, wavPhi, wavMag = waveToBlock(wavData, sampCount, blkCount=blkCount, blkSize=blkSize, olapf=olapf, shift=False)
    wavObj, wavPhi, wavMag = waveToSTFT(wavData, sampCount, blkSize=blkSize, hop=olapf)
    #mfccObj = waveToMFCC(wavData, sampCount, blkCount, blkSize)
    phiWav, meanPhiWav, stdPhiWav = blockNormalize(wavPhi)
    magWav, meanMagWav, stdMagWav = blockNormalize(wavMag)
    #MfccWav, meanMfcc, stdMfcc = blockNormalize(mfccObj)

    if returnObj == "phase":
        return phiWav, meanPhiWav, stdPhiWav
    elif returnObj == "magnitude":
        return magWav, meanMagWav, stdMagWav
    else:
        return phiWav, meanPhiWav, stdPhiWav, magWav, maxMagWav, minMagWav

def extractWaveData(wavData, sampCount, blkCount=False, blkSize=False, returnObj="all", olapf=1, shift=False):
    #wavData = openWavFile(fileName)
    wavObj, wavPhi, wavMag = waveToBlock(wavData, sampCount, blkCount=blkCount, blkSize=blkSize, olapf=olapf, shift=False)
    #mfccObj = waveToMFCC(wavData, sampCount, blkCount, blkSize)
    phiWav, meanPhiWav, stdPhiWav = blockNormalize(wavPhi)
    magWav, meanMagWav, stdMagWav = blockNormalize(wavMag)
    #MfccWav, meanMfcc, stdMfcc = blockNormalize(mfccObj)

    if returnObj == "phase":
        return phiWav, meanPhiWav, stdPhiWav
    elif returnObj == "magnitude":
        return magWav, meanMagWav, stdMagWav
    else:
        return phiWav, meanPhiWav, stdPhiWav, magWav, maxMagWav, minMagWav

def blockShift(data, shift=1):
    retObj = []
    for sectInd in xrange(data.shape[0]):
        retObj.append( np.concatenate((data[sectInd][shift:], data[sectInd][0:shift])) )
    return np.reshape(retObj, data.shape)


import sympy as sp

def calc_taylor_series(equation, xInit, a, n:int):
    """
    Method to estimate a function using taylor series

    Parameters:
    equation: The equation f(x)
    xInit: Initial value of x
    a: Another value of x
    n: number of derivatives
    """
    #Variables and settings
    x = sp.Symbol('x')
    fOri = equation
    xVal = xInit  #Initial value of x
    derivatives = []

    #Create derivatives
    derivatives.append(fOri)
    for i in range(1, n+1):
        derivatives.append(sp.diff(derivatives[i-1]))

    #Calculate derivatives
    for i in range(len(derivatives)):
        derivatives[i] = derivatives[i].evalf(subs={x: a})

    #Calculate series
    result = 0
    for i in range(len(derivatives)):
        result += (derivatives[i] * (xVal - a)**i)/sp.factorial(i)

    return result

def calc_maclaurin_series(equation, xInit, n:int):
    """
    Method to estimate a function using maclaurin series

    Parameters:
    equation: The equation f(x)
    xInit: Initial value of x
    n: number of derivatives
    """
    return calc_taylor_series(equation, xInit, 0, n)

def get_derivatives(equation, n:int):
    derivatives = []

    for _ in range(n):
        derivatives.append(sp.diff(equation))

    return derivatives

def get_n_derivative(equation, n:int):
    return get_derivatives(equation, n)[-1]

def example():
    x = sp.Symbol('x');
    ori = (sp.sin(x))**3
    print(calc_taylor_series(ori, 0.1, 1.5, 2))


import matplotlib
matplotlib.use('Agg')
import numpy as np
import matplotlib.pyplot as plt
import sys

inp = sys.argv[1]

# hyper parameters
epoch = 1000 # how many times to repeat
lr = 0.001 # learning rate
train_data_ratio = 0.8 # train data

#read data
data = np.genfromtxt(inp, dtype=np.float64, skip_header=1)

data_pivot = int(len(data)*train_data_ratio)
train_data = data[:data_pivot]
test_data = data[data_pivot:]

m = len(train_data) # number of data
k = len(train_data[0]) - 1 # number of dimension on x

x_train = [list(x)[:-1] for x in train_data]
y_train = [x[-1:] for x in train_data]

x_test = [list(x)[:-1] for x in test_data]
y_test = [x[-1:] for x in test_data]

# hypothesis = w1*x1 + w2*x2 + ... + wk*xk + b
w = np.random.rand(k, 1)
b = np.random.random()

cost_log = []
for i in range(epoch):
	pred = np.matmul(x_train, w) + b
	cost =  1/(2*m) * np.sum(np.square(np.subtract(pred, y_train)))
	# cost = (h-y_train)**2.mean()/2

	if i % (epoch//100) == 0:
		print(i//(epoch//100), "%")
		cost_log.append(cost)

	w_gradient = 1/m * np.sum(np.multiply(np.subtract(pred, y_train), x_train))
	b_gradient = 1/m * np.sum(np.subtract(pred, y_train))

	# update
	w -= lr * w_gradient
	b -= 2 * lr * b_gradient

# visualize
if k == 1:
	plt.figure()
	plt.title('graph of train data')
	plt.scatter(x_train, y_train)
	pred = np.matmul(x_train, w) + b
	plt.plot(x_train, pred, color='green')
	plt.xticks(())
	plt.yticks(())
	plt.savefig('graph.png')

plt.figure()
plt.title('cost')
plt.plot(range(100), cost_log)
plt.savefig('cost.png')

# cost for test data
pred = np.matmul(x_test, w) + b
cost =  1/(2*len(test_data)) * np.sum(np.square(np.subtract(pred, y_test)))
print("average cost for test data : ", cost)

input()


import warnings
import math
import numpy as np
from scipy.stats import norm

def make_data(mu1, sigma1, mu2, sigma2, n_per_group, percent_outliers=0, sd_outlier_factor=2.0, rand_seed=None):
    """
    Auxiliary function for generating random values from a mixture of normal distributions.
    :param mu1: mean of first group
    :param sigma1: standard deviation of first group
    :param mu2: mean of second group
    :param sigma2: standard deviation of second group
    :param n_per_group: number of samples per group
    :param percent_outliers: percentage of outliers to generate
    :param sd_outlier_factor: factor to apply to sigma1, sigma2 to define outlier standard deviation
    :param rand_seed (optimal), int
    :return: y1, y2
    """
    if rand_seed is not None:
        np.random.seed(int(rand_seed))

    n_outliers = math.ceil(n_per_group * percent_outliers / 100)  # Number of outliers.
    n = n_per_group - n_outliers  # Number from main distribution.

    _validate_input(n, n_outliers, percent_outliers)

    y1 = _generate_data(mu1, sigma1, n, n_outliers, sd_outlier_factor)
    y2 = _generate_data(mu2, sigma2, n, n_outliers, sd_outlier_factor)

    return y1, y2

def _validate_input(n, n_outliers, percent_outliers):
    if percent_outliers > 100 or percent_outliers < 0:
        raise ValueError("percent_outliers must be between 0 and 100.")
    if 0 < percent_outliers < 1:
        raise ValueError("percent_outliers is specified as percentage 0-100, not proportion 0-1.")
    if percent_outliers > 50:
        warnings.warn("percent_outliers indicates more than 50% outliers; did you intend this?")
    if n_outliers < 2 and percent_outliers > 0:
        raise ValueError("Combination of nPerGrp and pcntOut yields too few outliers.")
    if n < 2:
        raise ValueError("Too few non-outliers.")

def _generate_data(mu, sigma, n, n_outliers, sd_outlier_factor):
    y = norm.rvs(loc=mu, scale=sigma, size=n)
    if n_outliers > 0:
        y_out = norm.rvs(size=n_outliers)  # Random values for outliers
        y_out = ((y_out - np.mean(y_out)) / np.std(y_out)) * (sigma * sd_outlier_factor) + mu  # Realize exactly.
        y = np.concatenate([y, y_out])
    return y


import numpy as np
# Painless algorithm using numpy

def solutionM(x):
    sar = np.sum(x)
    esar = np.sum(np.arange(1,len(x)+2,1))-sar
    if esar >= 1:
        return int(esar)
    else:
        return None

# Painless algorithm using only Python standard library
def solution(x):
    actual_sum = 0
    for i in x:
        actual_sum += i
    max_number = len(x) +1
    expected_sum = (max_number * (max_number+1)//2)
    return expected_sum - actual_sum

print (solutionM([2,3,1,5,4,9,8,6,7,11,12,13,14,15,16,26,17,10,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,34,35,36,37,39]))
print (solution([2,3,1,5,4,9,8,6,7,11,12,13,14,15,16,26,17,10,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,34,35,36,37,39]))
print (solutionM([]))
print (solution([]))
print (solutionM([1,2,3,4]))
print (solution([1,2,3,4,]))


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

def split(start_pt, end_pt, segments):
    """
        Segmenta la linea que va desde el punto start_pt a end_pt en la
        cantidad de segmentos definida por segments
    :param (tuple) start_pt:
        punto de partida
    :param (tuple) end_pt:
        punto final
    :param (int) segments:
        cantidad de segmentos que se desean
    :return (list(tuple)):
        lista con todos los puntos de las divisiones de los segmentos de la
        linea
    """
    # Define el incremento que hay que hacer en el eje x y eje y por cada segmento
    x_delta = (end_pt[0] - start_pt[0]) / float(segments)
    y_delta = (end_pt[1] - start_pt[1]) / float(segments)
    points = []
    # Añade cada punto de cada segmento a la lista points
    for i in range(1, segments):
        points.append((round(start_pt[0] + i * x_delta), round(start_pt[1] + i * y_delta)))
    # Agrega puntos inicial y final a points y retorna esto
    return [start_pt] + points + [end_pt]

def angle_line(point, angle, length):
    """
        Genera una recta en el punto point con un ángulo igual a
        angle y una longitud de 2*length. Retorna el punto inicial
        y el final de la recta
    :param (tuple) point:
        punto donde se quiere generar la recta
    :param (number) angle:
        ángulo de la recta (con respecto a la horizontal)
    :param (int) length:
        longitud de cada brazo de la recta
    :return (tuple,tuple):
        el punto inicial y el final de la recta
    """
    x, y = point
    # Define cual es el desface (absoluto) de los límite
    # de la recta con respecto al punto origen
    delta_y = length * math.sin(math.radians(angle))
    delta_x = length * math.cos(math.radians(angle))

    # Devuelve una recta con longitud 2*length
    return (x-delta_x,y-delta_y),(x+delta_x,y+delta_y)

def line_intersection(line1, line2):
    """
        Encuentra la intersección entre las rectas generadas por los pares
        de puntos line1 y line2, si es que lo hay
    :param (tuple(tuple)) line1:
        par de puntos de primera recta
    :param (tuple(tuple)) line2:
        par de puntos de segunda recta
    :return (int,int) o (None):
        las coordenadas x e y de la intersección. Si no hay intersección
        retorna un None
    """
    # Define las diferencias de coordenadas entre los puntos de cada par
    # de puntos de las lineas
    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])
    def det(a, b):
        """
            Retorna la determinante entre la matriz formada
            por la concatenación de los vectores a y b
        :param (tuple) a:
            primer vector
        :param (tuple) b:
            segundo vector
        :return (int):
            la determinante
        """
        return a[0] * b[1] - a[1] * b[0]

    # Define la determinante entre los deltas de coordenadas de
    # cada par de puntos
    div = det(xdiff, ydiff)
    if div == 0:
       return None
    d = (det(*line1), det(*line2))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return int(x), int(y)

def check_in_segment(pt,line):
    """
        Chequea si es que el punto pt pertenece al segmento de linea que une
        el par de puntos line
    :param (tuple) pt:
        punto cuya pertenencia a segmento se quiere verificar
    :param (tuple(tuple)) line:
        par de puntos que define el segmento de linea
    :return (bool):
        si el punto pertenece o no al segmento de linea
    """
    # Este parámetro define un margen de error (pixeles) para verificar la pertenencia del
    # punto en el segmento
    error = 1
    # Verifica si las coordenadas en x del punto e encuentran en el rango horizontal definido
    # por el par de puntos
    check1 = min(line[0][0], line[1][0])-error  <= pt[0] <= max(line[0][0], line[1][0])+error
    # Verifica si las coordenadas en y del punto e encuentran en el rango vertical definido
    # por el par de puntos
    check2 = min(line[0][1], line[1][1])-error  <= pt[1] <= max(line[0][1], line[1][1])+error
    # Se cumple pertenencia si ambas verificaciones de rango se cumplen
    return check1 and check2

def find_polygon_divisions(line_pts, polygon_pts, angle):
    """
        Esta función primero genera una recta, con un ángulo definido por angle, en cada
        punto contenido dentro de line_pts. Luego, para cada recta generada, encuentra la intersección
        con cada mitad del polígono definido en polygon_pts. Retorna los puntos de intersección encontrados
    :param list(tuples) line_pts:
        puntos a partir de los cuales se generan las rectas de intersección
    :param list(list(tuples),list(tuples)) polygon_pts:
        vértices del polígono almacenados como una lista de dos listas. la primera de estas listas
        contiene los vértices del polígono de los lados donde empiezan las hileras y la segunda de
        estas listas los vértices del polígono de los lados donde terminan las hileras
    :param (number) angle:
        ángulo, con respecto a la horizontal, con la cual se generan las rectas de intersección
    :return list(tuples), list(tuples):
        las coordenadas de los puntos de intersección encontrados para cada lado del polígono
    """
    # Vértices de los lados donde empiezan las hileras
    start_poly_vertices = polygon_pts[0]

    # Vértices de los lados donde terminan las hileras
    end_poly_vertices = polygon_pts[1]

    # Largo de los brazos de cada recta de intersección generada
    line_length = 5000
    # Se inicializan los primeros par de vértices, tanto para los vértices finales
    # como iniciales, donde se evaluará intersección
    start_v_line = (start_poly_vertices[0], start_poly_vertices[1])
    end_v_line = (end_poly_vertices[0], end_poly_vertices[1])
    start = []
    end = []
    # Las variables siguientes se ocupan para ir cambiando los pares de puntos de cada lado
    # en caso de no encontrar intersección
    start_pt_indx = 0
    end_pt_indx = 0
    for pt in line_pts:
        # Genera una recta con ángulo angle y de longitud 2*length sobre el punto pt
        pt_line = angle_line((pt[0],pt[1]),angle,line_length)
        # Encuentra la intersección de la recta anterior con el lado donde empiezan las hileras
        # y el lado donde terminan
        start_intersection = (line_intersection(start_v_line,pt_line))
        end_intersection = (line_intersection(end_v_line,pt_line))
        start_sign = 1
        # Mientras no se encuentre alguna intersección con el lado de inicio o la intersección
        # encontrada no pertenezca al perímetro del polígono, se sigue intentando con otro lado
        # de inicio
        while start_intersection is None or not check_in_segment(start_intersection,start_v_line):
            # Indice del primer vertice del lado inicial
            start_pt_indx += 1*start_sign
            # Si el índice es igual o mayor al largo de la lista de vértices, se invierte
            # el sentido de exploración de esta. Lo mismo para si es menor a 0
            if start_pt_indx >= len(start_poly_vertices)-1:
                start_pt_indx -=2
                start_sign = -1
            elif start_pt_indx < 0:
                start_pt_indx += 2
                start_sign = 1
            # Actualiza el lado inicial para el cual se evaluará la intersección
            start_v_line = (start_poly_vertices[start_pt_indx],\
                            start_poly_vertices[start_pt_indx+1])
            start_intersection = (line_intersection(start_v_line,pt_line))
        end_sign = 1
        # Hace lo mismo que el loop anterior pero para los lados de fin de la hilera
        while end_intersection is None or not check_in_segment(end_intersection,end_v_line):
            end_pt_indx += 1*end_sign
            if end_pt_indx >= len(end_poly_vertices)-1:
                end_pt_indx -=2
                end_sign=-1
            elif end_pt_indx < 0:
                end_pt_indx += 2
                end_sign = 1
            end_v_line = (end_poly_vertices[end_pt_indx],\
                        end_poly_vertices[end_pt_indx+1])
            end_intersection = (line_intersection(end_v_line,pt_line))
        # Agrega el punto de intersección con el lado inicial y con el lado final
        # de la hilera a las lista correspondiente
        start.append(start_intersection)
        end.append(end_intersection)
    # Elimina los puntos de intersección encontrados para los puntos iniciales
    # y finales de cada parte del polígono (generan hileras muy chicas)
    start = start[1:-1]
    end = end[1:-1]
    return start, end

if __name__ == '__main__':
    # Algunas pruebas
    polygon = [[(641,38),(402,38),(388,613)],[(641,38),(649,610),(388,613)]]
    hilera_or = [polygon[0][2],polygon[1][0]]
    pts = split(hilera_or[0],hilera_or[1],113)
    start, end = find_polygon_divisions(pts,polygon,70)
    print(len(start))
    print(len(end))
    fig,ax = plt.subplots()
    for pt in start:
        ax.plot(start_pt[0],start_pt[1],"*")
    for pt in end:
        ax.plot(end_pt[0],end_pt[1],"*")

    img = plt.imread("./satImages/curicoMap.jpg")
    ax.imshow(img)
    plt.show()


import csv
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
import numpy as np
from scipy.special import expit

csvfile=open("logisticX.csv", 'r')
x = list(csv.reader(csvfile))
csvfile=open("logisticY.csv", 'r')
y = list(csv.reader(csvfile))

m=len(x)
n=2
x3=[]
y2=[]
for i in range(m):
    x3.append([float(x[i][0]),float(x[i][1])])
    y2.append(float(y[i][0]))

# normalise the data
meanx=[0,0]
# meanx[0]=sum(x3[0])/m
# meanx[1]=sum(x3[1])/m

for i in range(m):
    meanx[0]+=x3[i][0]
    meanx[1]+=x3[i][1]
meanx[0]/=m
meanx[1]/=m

v=[0,0]
for i in range(m):
    temp=x3[i][0]-meanx[0]
    v[0]+=temp*temp
    temp=x3[i][1]-meanx[1]
    v[1]+=temp*temp
v[0]/=m
v[0]=math.sqrt(v[0])
v[1]/=m
v[1]=math.sqrt(v[1])
for i in range(m):
    x3[i][0]=(x3[i][0]-meanx[0])/v[0]
    x3[i][1]=(x3[i][1]-meanx[1])/v[1]

x2=[]
# for y=1
x4=[]
x5=[]
# for y=0
x6=[]
x7=[]

for i in range(m):
    x2.append(np.array([1,x3[i][0],x3[i][1]]))

    if(y2[i]==1):
        x4.append(float(x3[i][0]))
        x5.append(float(x3[i][1]))
    else:
        x6.append(float(x3[i][0]))
        x7.append(float(x3[i][1]))

X=np.array(x2)
Y=np.array(y2)

epsilon=0.000001
t=0
theta=np.array([0,0,0])

while(True):

    X3=np.matmul(X,theta)
    X2=expit(np.matmul(X,theta))

    gradient=np.dot(X.T,(Y-X2))

# calulate Hessian matrix
    H=np.zeros((n+1,n+1))
    for j in range(m):
        x2=X3[j]
        H=H-((expit(x2)*(1-expit(x2)))*(np.outer(X[j].T,X[j])))

    t+=1

# update theta
    thetaold=theta
    theta=theta-np.matmul(np.linalg.inv(H),gradient)

# check if converged
    convergence=np.linalg.norm(theta-thetaold)
    if(convergence<epsilon):
        break

print("Number of iterations={}".format(t))
print("theta={}".format(theta))

plt.ioff()
fig=plt.figure()
ax1 = fig.add_subplot(1,1,1)

# plots the training data & decision boundary
def pl():
    ax1.clear()
    ax1.scatter(x4, x5, label= "y=1 Training Data", color= "red",
                marker= ".", s=10)
    ax1.scatter(x6, x7, label= "y=0 Training Data", color= "blue",
                marker= ".", s=10)

    xvalues=np.linspace(min(min(x4),min(x6)),max(max(x4),max(x6)),100)

    the=theta
    yvalues=-1*(the[1]*xvalues+the[0])/the[2]

    ax1.plot(xvalues, yvalues, label="Decision boundary learned",color ="#66cc00")

    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.legend()
    plt.title('Q3 (b)')
    plt.show()
    plt.pause(0.001)

pl()


import numpy as np

def kernelList(restrictiveU):
    if restrictiveU:
        return ['uniform', 'triangle', 'cosinus', 'epanechnikov1', 'epanechnikov2', 'epanechnikov3']
    else:
        return ['gaussian', 'cauchy', 'picard']

def kernel(kernelString):
    if kernelString == 'gaussian':
        return gaussianKernel
    elif kernelString == 'cauchy':
        return cauchyKernel
    elif kernelString == 'picard':
        return picardKernel
    elif kernelString == 'uniform':
        return uniformKernel
    elif kernelString == 'triangle':
        return triangleKernel
    elif kernelString == 'cosinus':
        return cosKernel
    elif kernelString == 'epanechnikov1':
        return epanichnikov1
    elif kernelString == 'epanechnikov2':
        return epanichnikov1
    elif kernelString == 'epanechnikov3':
        return epanichnikov1
    else:
        raise NameError('Kernel function not found! Use a valid kernel function.')

def gaussianKernel(u, derivative):
    K = 1 / np.sqrt(2 * np.pi) * np.exp(-np.power(u, 2) / 2)
    if derivative == 0:
        return K
    else:
        dK = -u * K

        if derivative == 1:
            return K, dK
        else:
            ddK = (np.power(u, 2) - 1) * K

            return K, dK, ddK

def cauchyKernel(u, derivative):
    K = 1 / np.sqrt(np.pi * (1+np.power(u, 2)))
    if derivative == 0:
        return K
    else:
        dK = -2*u /(1+np.power(u, 2)) * K

        if derivative == 1:
            return K, dK
        else:
            ddK = (-2*np.power(1+np.power(u, 2),2) + 8*np.power(u, 2)) / \
                  (np.pi * np.power(1+np.power(u, 2),3))

            return K, dK, ddK

def picardKernel(u, derivative):
    K = 1 / 2 * np.exp(-np.abs(u))
    if derivative == 0:
        return K
    else:
        dK = -np.sign(u) * K

        if derivative == 1:
            return K, dK
        else:
            ddK = K

            return K, dK, ddK

def uniformKernel(u, derivative):
    indeces_u = np.abs(u) > 1

    K = 1 / 2 * np.ones(u.shape)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = np.zeros(u.shape)

        if derivative == 1:
            return K, dK
        else:
            ddK = np.zeros(u.shape)

            return K, dK, ddK

def triangleKernel(u, derivative):
    indeces_u = np.abs(u) > 1

    K = 1 - np.abs(u)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = -np.sign(u)
        dK[indeces_u] = 0

        if derivative == 1:
            return K, dK
        else:
            ddK = np.zeros(u.shape)

            return K, dK, ddK

def cosKernel(u, derivative):
    indeces_u = np.abs(u) > 1

    K = np.pi/4 * np.cos(np.pi/2*u)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = -np.power(np.pi,2)/8 * np.sin(np.pi/2*u)
        dK[indeces_u] = 0

        if derivative == 1:
            return K, dK
        else:
            ddK = -np.power(np.pi,3)/16 * np.cos(np.pi/2*u)
            ddK[indeces_u] = 0
            return K, dK, ddK

def epanechnikovKernel(u, derivative, p):
    indeces_u = np.abs(u) > 1

    if p == 1:
        Cp = 3/4
    elif p == 2:
        Cp = 15/16
    elif p == 3:
        Cp = 35/32
    else:
        raise ValueError('Wrong p! Use 1, 2 or 3.')

    K = Cp * np.power(1-np.power(u,2),p)
    K[indeces_u] = 0

    if derivative == 0:
        return K
    else:
        dK = -2*p*Cp*u*np.power(1-np.power(u,2),p-1)
        dK[indeces_u] = 0

        if derivative == 1:
            return K, dK
        else:
            ddK = 2*p*Cp*(2*np.power(u,2)*np.power(1-np.power(u,2),p-2)\
                           -np.power(1-np.power(u,2),p-1))
            ddK[indeces_u] = 0
            return K, dK, ddK

def epanichnikov1(u, derivative):
    return epanechnikovKernel(u, derivative, p = 1)

def epanichnikov2(u, derivative):
    return epanechnikovKernel(u, derivative, p = 2)

def epanichnikov3(u, derivative):
    return epanechnikovKernel(u, derivative, p = 3)

def nadarayaWatsonEstomator(u_feature, y_feature, kernelstring, h, scaleKernel=True, \
                            derivative=0):
    m_u = u_feature.shape

    u = u_feature / h

    kernelFunction = kernel(kernelstring)

    if derivative == 0:
        K = kernelFunction(u, derivative)
    elif derivative == 1:
        K, dK = kernelFunction(u, derivative)
    else:
        K, dK, ddK = kernelFunction(u, derivative)

    # print(f'a={a}')
    # print(f'b={b}')
    # print(f'K={K}')
    # print(f'm_u={m_u}')
    # print(f'u_feature={u_feature}')

    if scaleKernel:
        Kh = K / h

        a = np.dot(Kh, y_feature)
        b = np.sum(Kh, axis=1)
    else:
        a = np.dot(K, y_feature)
        b = np.sum(K, axis=1)

    # print(f'b={b}')
    # print(f'a={a}')
    # print(f'Kh={Kh}')
    # m = a / b
    m = np.divide(a, b)

    if np.any(np.isnan(m)):
        if scaleKernel:
            m[np.isnan(m)] = np.sum(y_feature / h) / np.sum(1 / h)
        else:
            m[np.isnan(m)] = np.sum(y_feature)

    if derivative == 0:
        return m, b / K.shape[1]
    else:
        if scaleKernel:
            dKh = -(K + u * dK) / np.power(h, 2)
            da = dKh * y_feature
            db = dKh
        else:
            dK = -u / h * dK
            da = dK * y_feature
            db = dK

        if h.size == 1:
            da = np.sum(da, axis=1)
            db = np.sum(db, axis=1)
        else:
            a = a.reshape((m_u[0], 1))
            b = b.reshape((m_u[0], 1))

        dm = da / b - a * db / np.power(b, 2)

        if np.any(np.isnan(dm)):
            dm[np.isnan(dm)] = 0

        if np.any(np.isinf(dm)):
            dm[np.isinf(dm)] = 0

        if derivative == 1:
            return m, dm
        else:
            if scaleKernel:
                ddKh = -(u * dK + K) / np.power(h, 3) - u / h * (dKh + u * ddK)
                dda = ddKh * y_feature
                ddb = ddKh
            else:
                ddK = u / np.power(h, 2) * (2 * dK + u * ddK)
                dda = ddK * y_feature
                ddb = ddK

            if h.size == 1:
                dda = np.sum(dda, axis=1)
                ddb = np.sum(ddb, axis=1)

            ddm = (dda * b - 2 * da * db - a * ddb) / np.power(b, 2) + \
                  (a * np.power(db, 2) / np.power(b, 3))

            if np.any(np.isnan(ddm)):
                if scaleKernel:
                    ddm[np.isnan(ddm)] = -np.sum(y_feature / h) / np.sum(1 / h)
                else:
                    ddm[np.isnan(ddm)] = -np.sum(y_feature)

            return m, dm, ddm


import numpy as np
import math

def multiply_veca_vecb(vec_a: np.ndarray, vec_b: np.ndarray) -> float:
    """returns :math:`a \\cdot b`. this dot means inner product.

    Parameters
    ----------
    vec_a : np.ndarray
        vector a.
    vec_b : np.ndarray
        vector b.

    Returns
    -------
    float
        value of :math:`a \\cdot b`.

    Raises
    ------
    ValueError
        dimension of vec_a is not 1.(not vector)
    ValueError
        dimension of vec_b is not 1.(not vector)
    """
    if vec_a.ndim != 1:
        raise ValueError(
            f"dimension of vec_a must be 1. dimension of vec_a is {vec_a.ndim}"
        )
    if vec_b.ndim != 1:
        raise ValueError(
            f"dimension of vec_b must be 1. dimension of vec_b is {vec_b.ndim}"
        )

    val = np.dot(vec_a, vec_b)
    return val

def multiply_veca_vecb_matc(
    vec_a: np.ndarray, vec_b: np.ndarray, mat_c: np.ndarray
) -> float:
    """returns :math:`a \\cdot C b`. this dot means inner product.

    Parameters
    ----------
    vec_a : np.ndarray
        vector a.
    vec_b : np.ndarray
        vector b.
    mat_c : np.ndarray
        matrix C.

    Returns
    -------
    float
        value of :math:`a \\cdot C b`.

    Raises
    ------
    ValueError
        dimension of vec_a is not 1.(not vector)
    ValueError
        dimension of vec_b is not 1.(not vector)
    ValueError
        dimension of mat_c is not 2.(not matrix)
    """
    if vec_a.ndim != 1:
        raise ValueError(
            f"dimension of vec_a must be 1. dimension of vec_a is {vec_a.ndim}"
        )
    if vec_b.ndim != 1:
        raise ValueError(
            f"dimension of vec_b must be 1. dimension of vec_b is {vec_b.ndim}"
        )
    if mat_c.ndim != 2:
        raise ValueError(
            f"dimension of mat_c must be 2. dimension of mat_c is {mat_c.ndim}"
        )

    val = np.dot(vec_a, mat_c @ vec_b)
    return val

def project_to_traceless_matrix(A: np.ndarray) -> np.ndarray:
    """returns a matrix projected to a trace-less matrix subspace.

    Parameters
    ----------
    A : np.ndarray
        Square matrix

    Returns
    ----------
    B : np.ndarray
        Square matrix, :math:`B = A - \\mathrm{Tr}[b_0 A] b_0`, where :math:`b_0 = \\frac{I}{\\sqrt{d}}`.

    """
    if A.shape[0] != A.shape[1]:
        raise ValueError(f"A must be square matrix.")

    d = A.shape[0]
    b0 = np.eye(d) / math.sqrt(d)
    tr = np.trace(b0 @ A)
    B = A - tr * b0
    return B


import argparse
import numpy as np
from scipy.constants import h, hbar, m_e, pi
from scipy.linalg import eigh
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple
import seaborn as sns
#sns.set()
from utils.plotting import latexconfig
from utils.metrics import rmse

parser = argparse.ArgumentParser(description='Takes the number of grid points.')
parser.add_argument('-n', '--num_of_points',
                    type=int,
                    metavar='',
                    help='Finite Difference approximation of the wave equation for\
                        an electron in the infinite quantum well based on the given\
                        number of grid points.',
                    default=10)

def psi_close_form(x, n, L):
    """analyitcal solution for 1-D wave eqn"""
    amp = np.sqrt(2/L)
    return amp * np.sin(n * pi / L * x)

def pdf_close_form(x, n, L):
    r"""probability of locating an electron in x \in (0, L)"""
    return psi_close_form(x, n, L)**2

def V(x):
    """Potential function"""
    return 0. * x

def fdm(N, xmin, L, bcs):
    """(- hbar**2/(2*m_e) * d^2/dx^2) psi = E * psi"""
    dx = (L - xmin)/(N-1)           # distance between grid points
    xmesh = np.linspace(xmin, L, N) # mesh

    # hamiltonian formulation
    d_xx = np.diag(np.full(N, 2), k=0) +\
          np.diag(np.full(N-1, -1), k=-1) +\
          np.diag(np.full(N-1, -1), k=+1)
    H = hbar/(2 * m_e * dx**2) * d_xx + np.diag(np.full(N, V(xmesh)), k=0)

    # applying dirichlet conds
    if bcs == (0, 0):
        H = H[1:-1, 1:-1]
        # eigenvalues (E) and eigenvector (psi)
        E, psi = eigh(H)
        psi = np.r_[np.zeros((1, psi.shape[1])), psi]
        psi = np.r_[psi, np.zeros((1, psi.shape[1]))]
    else:
        raise ValueError

    # eigenvector normalization
    psi = psi * np.linalg.norm(psi)

    # probability density function |psi|^2
    pdf = psi * np.conj(psi)
    return E, psi[::-1], pdf

def main():
    args = parser.parse_args()
    N = args.num_of_points          # number of grid points
    xmin = 0.0                      # left boundary
    L = 1.0                         # right boundary
    psi_0 = 0                       # left Dirichlet bc
    psi_L = 0                       # right Dirichlet bc
    x = np.linspace(xmin, L, N)
    _x = np.linspace(xmin, L, 1000)

    principal_quantum_numbers = list(range(1, 4))
    nonreletivistic_energies = []
    for n in principal_quantum_numbers:
        E = n**2 * h**2 / (8 * m_e * L)
        nonreletivistic_energies.append(E)

    # H psi = E psi
    # H = - hbar**2/(2*m_e) * d^2/dx^2 -> eigenvalue problem
    E, psi, pdf = fdm(N, xmin, L, bcs=(psi_0, psi_L))

    fig, axs = plt.subplots(len(principal_quantum_numbers), 1, sharex='all', figsize=(7, 9))
    for i, (n, E_analytic) in enumerate(zip(principal_quantum_numbers, nonreletivistic_energies)):
        l1, = axs[i].plot(_x, psi_close_form(_x, n, L), 'b-')
        l2, = axs[i].plot(_x, pdf_close_form(_x, n, L), 'r-')
        l3, = axs[i].plot(x, psi[:, i], 'bo', markersize=5, alpha=0.8)
        l4, = axs[i].plot(x, pdf[:, i], 'ro', markersize=5, alpha=0.8)

        pdf_analytic = pdf_close_form(x, n, L)
        axs[i].set_title(f'RMSE = {np.round(rmse(pdf_analytic, pdf[:, i]), 5)}')
        axs[i].legend([l1, l2, (l3, l4)], [r'$\psi(x)$', r'$|\psi(x)|^2$', 'FDM'],
                      handler_map={tuple: HandlerTuple(ndivide=None)})
    plt.xlabel(r'$x$')
    plt.tight_layout()
    plt.show()

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


# %% Time results using fft
import numpy as np
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N * T, N)
y = np.sin(50.0 * 2.0 * np.pi * x) + 0.5 * np.sin(80.0 * 2.0 * np.pi * x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0 / (2.0 * T), int(N / 2))

plt.plot(x, y, ".")

fig, ax = plt.subplots()
ax.plot(xf, 2.0 / N * np.abs(yf[: N // 2]))
plt.show()

temp.loc[:, ["datetimeint", "count"]]
norm_count = (temp["count"] - temp["count"].mean()).to_numpy()
norm_count.shape
yf = scipy.fftpack.fft(norm_count,)
xf = temp["datetimeint"]
temp["datetimeint"].max() - temp["datetimeint"].min()
# np.linspace(0.0, 1.0/(2.0*T), int(N/2))

fig, ax = plt.subplots()
ax.plot(xf, 2.0 / N * np.abs(yf))
ax.plot(xf, 2.0 / N * np.abs(yf[: N // 2]))
plt.show()

plt.plot(temp["datetimeint"].diff())

temp["datetimeint"].diff()[1]

temp["datetimeint"][:2]

Y = np.fft.fft(norm_count)
freq = np.fft.fftfreq(len(norm_count), temp["datetimeint"].diff()[1])

plt.figure()
plt.plot(freq, np.abs(Y), ".")
plt.figure()
plt.plot(freq, np.angle(Y))
plt.show()

# %% [markdown]
# Convert to jupyter notebook -> Export current (no output)
# # Convert to markdown file
# `jupyter nbconvert data_proc.ipynb --to markdown`


import numpy as np
#from: https://stackoverflow.com/questions/41576536/normalizing-complex-values-in-numpy-python
#setup: import numpy as np; np.random.seed(0); N = 10000; x = np.random.random(N) + 1j *  np.random.random(N)
#run: normalize_complex_arr(x)

#pythran export normalize_complex_arr(complex[])

def normalize_complex_arr(a):
    a_oo = a - a.real.min() - 1j*a.imag.min() # origin offsetted
    return a_oo/np.abs(a_oo).max()


import numpy as np
import sympy as sp
from kaa.parallelotope import Parallelotope

def test_min_max_parall_1():

    x, y = sp.Symbol('x'), sp.Symbol('y')

    A = np.empty([4,2])
    b = np.empty(4)

    A[0] = [1,0]
    A[1] = [0,1]
    A[2] = [-1,0]
    A[3] = [0,-1]

    b[0] = 1
    b[1] = 1
    b[2] = 1
    b[3] = 1

    p = Parallelotope(A, b, [x,y])
    min_point, max_point = p.getMinPoint(), p.getMaxPoint()

    assert np.equal(max_point, [1,1]).all()
    assert np.equal(min_point, [-1,-1]).all()

def test_min_max_parall_2():

    x, y = sp.Symbol('x'), sp.Symbol('y')

    A = np.empty([4,2])
    b = np.empty(4)

    A[0] = [1,1]
    A[1] = [-1,1]
    A[2] = [-1,-1]
    A[3] = [1,-1]

    b[0] = 2
    b[1] = 2
    b[2] = 2
    b[3] = 2

    p = Parallelotope(A, b, [x,y])
    min_point, max_point = p.getMinPoint(), p.getMaxPoint()

    assert np.equal(max_point, [1,1]).all()
    assert np.equal(min_point, [-1,-1]).all()

def test_min_max_parall_3():

    x, y = sp.Symbol('x'), sp.Symbol('y')

    A = np.empty([4,2])
    b = np.empty(4)

    A[0] = [1,0]
    A[1] = [0,1]
    A[2] = [-1,0]
    A[3] = [0,-1]

    b[0] = 0
    b[1] = 0
    b[2] = 2
    b[3] = 2

    p = Parallelotope(A, b, [x,y])
    min_point, max_point = p.getMinPoint(), p.getMaxPoint()

    assert np.equal(max_point, [0,0]).all()
    assert np.equal(min_point, [-2,-2]).all()

def test_min_max_parall_4():

    x, y = sp.Symbol('x'), sp.Symbol('y')

    A = np.empty([4,2])
    b = np.empty(4)

    A[0] = [1,0]
    A[1] = [0,1]
    A[2] = [-1,0]
    A[3] = [0,-1]

    b[0] = -2
    b[1] = -2
    b[2] = 4
    b[3] = 4

    p = Parallelotope(A, b, [x,y])
    min_point, max_point = p.getMinPoint(), p.getMaxPoint()

    assert np.equal(max_point, [-2,-2]).all()
    assert np.equal(min_point, [-4,-4]).all()


#!/usr/bin/env python3
"""
Optimization Module
"""
import numpy as np

def moving_average(data, beta):
    """
    Calculates the WMA using bias correction

    data is the list of data to calculate the moving average of
    beta is the weight used for the moving average

    Returns: a list containing the moving averages of data
    """
    vt = 0
    ema = []
    for i in range(len(data)):
        vt = (beta * vt) + ((1 - beta) * data[i])
        ema.append(vt / (1 - (beta ** (i + 1))))
    return ema


import scipy.io
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import math
from utils import read_mat_file, std_normalize, get_normal_prob, get_mean_std, get_accuracy, plot_clustered_graph, get_multivariate_pdf
from Multivariate_Bayes_Classifier import Multivariate_Bayes_Classifier
from Bayes_Classifier import Bayes_Classifier

data_path="data.mat" # Change data path here if required
# Link to example data file :
training_size=100
variables={"data_path":data_path, \
           "training_size":training_size, \
           "convert_std_normal": False
           }

def main(variables):
  data=read_mat_file(variables["data_path"])
  f1_data,f2_data=np.array(data['F1']),np.array(data['F2'])
  n_samples=f1_data.shape[0]
  ground_truth=np.array([[0,1,2,3,4] for _ in range(n_samples)])
  print("About the data")
  print("Source of data: ",variables["data_path"])
  print("Classes of data: 0,1,2,3,4")
  print("No. of samples: ",n_samples,"\n")

  #Training on 100 samples

  #m_std is dictionary of f1, f2 for each column, c1 c2 c3 c4 and c5.
  print("\n---------- Section 1: Training -------------")
  print("\n Calculating the means and standard deviations for 100 samples\n")
  train_size=variables['training_size']
  b1=Bayes_Classifier(f1_data,train_size)
  m_std_train=b1.train()

  ## Section 2.1: Testing
  print("\n---------- Section 2.1: Testing -------------")
  print("\n Predicting the classes for 101: 1000 samples")

  predicted=b1.predict()

  ## Section 2.2: Calculating accuracy and error rate
  print("\n---------- Section 2.2: Calculating accuracy for the classifier -------------")
  print("\nAccuracy for the Bayes classifier: ")
  acc=b1.validate(predicted)

  ## Section 3: Standard Normal (z score)
  print("---------- Section 3: Standard normal(Z Score) -------------")

  # z1_data is the standard normalized data.
  z1_data=np.swapaxes(np.array([std_normalize(f1_data[:,i],m_std_train['f1'][i]['m'],\
                        m_std_train['f1'][i]['std'])
          for i in range(5)]),0,1)
  print("Plot of Z1 vs F2")
  plot_clustered_graph(z1_data.flatten(),f2_data.flatten(),ground_truth.flatten(),name="z1vsf2.png",labels=['z1','f2'])

  # z1_data is the standard normalized data.
  print("\n Plot of F1 vs F2")
  plot_clustered_graph(f1_data.flatten(),f2_data.flatten(),ground_truth.flatten(),name="f1vsf2.png",labels=['f1','f2'])

  ## Section 4
  ### Case 1: Training with the z1 data
  print("\n---------- Section 4, Case 2: Training with the z1 data -------------")
  b=Bayes_Classifier(z1_data)
  b.train()
  predicted=b.predict()
  acc=b.validate(predicted)

  print("\n---------- Section 4, Case 3: Training with the f2 data -------------")
  b=Bayes_Classifier(f2_data)
  b.train()
  predicted=b.predict()
  acc=b.validate(predicted)

  print("\n---------- Section 4, Case 4: Training with the [z1, f2] data -------------")
  data={'z1':z1_data,'f2':f2_data}
  b=Multivariate_Bayes_Classifier(data)
  b.train()
  predicted=b.predict()
  acc=b.validate(predicted)

main(variables)


import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp

def henon_heiles_rhs(t, s):
    x, y, px, py = s
    Fx = - x - 2*x*y
    Fy = - y - (x**2 - y**2)
    return px, py, Fx, Fy

def henon_heiles_system(initial_pos, initial_vel,
        time_span=100, num_samples=1000):
    sol = solve_ivp(henon_heiles_rhs, (0, time_span),
            tuple(initial_pos) + tuple(initial_vel),
            t_eval=np.linspace(0, time_span, num_samples), method="BDF")
    plt.plot(sol.y[0, :], sol.y[1, :])
    plt.plot([initial_pos[0]], [initial_pos[1]], "or")
    plt.title("Henon Heiles System")
    plt.xlabel("x")
    plt.ylabel("y")
    plt.show()
    return sol

if __name__ == "__main__":
    henon_heiles_system((0, 1), (0.01, 0))


import numpy as np
from numpy.linalg import inv
from InputFunction import func, gradf, hesf
from numpy.linalg import norm as L2

def hessupd(Bi, yi, yj):
    p=yj-yi
    q=gradf(yj)-gradf(yi)
    r1=np.dot(p,q)
    r2=np.dot(q, np.dot(Bi, q))
    t1=np.outer(p, p)/r1
    t2=np.dot(np.dot(Bi, np.outer(q, q)), Bi)/r2
    return (Bi+t1-t2)
#def newton(X, mxiter=30, err=0.001, miter_LS=30,rho_LS=0.15, sigma_LS=0.30, err_LS=10**-4):
def DFP(X, mxiter=30, DIH=1, dimf=2, err=0.0001, LSf='1', LSP={}):
    valsX = [X]
    l=[]
    vals = []
    ovfs = []
    if(DIH==0):
        Bi=B0=inv(hesf(X))
    elif(DIH==1):
        Bi=B0=np.identity(dimf, dtype = float)
    Y=X
    i, j, k=0, 1, 1
    while (i < mxiter):
        i += 1
        vals.append(Y)
        ovfs.append(func(Y))
        gr=gradf(Y)
        if(L2(gr)<err):
            break;

        d=np.dot(Bi, gr)
        l.append(np.array([Y, -d]))
        if(LSf=='1'):
            step=0.10
        else:
            if(bool(LSP)):
                step=LSf(Y, -d, LSP)
            else:
                step=LSf(Y, -d)

        temp = Y-step*d
        if (np.abs(func(temp)-func(Y))<err):
            break;

        if(j==dimf):
            k+=1
            j=1
            Bi=B0
            X=temp
            Y=X
            valsX.append(X)

        elif(j<dimf):
            Bj=hessupd(Bi, Y, temp)
            j+=1
            Bi=Bj
            Y=temp
    if(i==mxiter):
        print("Algorithm failed to converge in. Increasing 'maXiter' maXimum of iteration may give better result")

    return (np.array(valsX), np.array(vals), np.array(ovfs), i, np.array(l))


#!/usr/bin/env python3
# coding=utf-8

import numpy as np

from householder_method.householder_algorithm import householder_algorithm
from QR_method.QR_algorithm import QR_algorithm

INFO = '''
TAREFA 4.1:
(a) Tarefa 4.1.a: Calculo dos auto valores e autovetores da matriz:
    [2., 4., 1., 1.]
    [4., 2., 1., 1.]
    [1., 1., 1., 2.]
    [1., 1., 2., 1.]

(b) Tarefa 4.1.b: Calculo dos auto valores e autovetores da matriz:
    [ n  n-1 n-2 ...  2   1 ]
    [n-1 n-1 n-2 ...  2   1 ]
    [n-2 n-2 n-2 ...  2   1 ]
    [ :   :   :  ...  :   : ]
    [ 2   2   2   2   2   1 ]
    [ 1   1   1   1   1   1 ]
'''
def make_matrixB(n):
    B = np.zeros((n,n),dtype = float)
    for i in range (2,n+2,1):
        add = (i-1)*np.ones((n-i+2,n-i+2),dtype=float)
        B[0:n-i+2,0:n-i+2] = add[0:n-i+2:,0:n-i+2]
    return B

def make_matrixB_alt(n):
    B= np.zeros((n,n))
    B[0,0] = n
    for i in range (1,len(B),1):
        B[i,0:i] = n-i
        B[0:i,i] = n-i
        B[i,i] = n-i
    return B

def get_eigenvalues(n):
    lamb = np.array([])
    eigenvaluesi = lambda i: ((1/2)*((1 - np.cos((2*i-1)*np.pi/(2*n+1)))**(-1)))
    for i in range(1,n+1):
        lamb = np.append(lamb,eigenvaluesi(i))
    return lamb

def assignment_1_a(debug = False):
    np.set_printoptions(precision=5,threshold=5) #print options
    A = np.array([[2., 4., 1., 1.],
                  [4., 2., 1., 1.],
                  [1., 1., 1., 2.],
                  [1., 1., 2., 1.]])

    A = householder_algorithm(A)
    V,lamb = QR_algorithm(A)[0:2]
    print("autovetores =\n",V)
    print("autovalores =\n",np.diagonal(lamb),"\n")

    if(debug == True):
        for i in range(0,len(A)):
            print('teste {j}'.format(j=i))
            print(A@V[:,i],"\n",np.diagonal(lamb)[i]*V[:,i],"\n")

def assignment_1_b():
    np.set_printoptions(precision=3,threshold=200,linewidth=1000) #print options
    n = 20
    B = make_matrixB(n)
    B = householder_algorithm(B)
    V,lamb = QR_algorithm(B)[0:2]
    eigenvalues = get_eigenvalues(n)

    print("autovetores =\n",V)
    print("autovalores =\n",np.diagonal(lamb))
    print("autovalores esperados =\n",eigenvalues)

def assignment_1():
    print(INFO,"\n")
    item = input("Gostaria de ver qual item? ")
    if ((item == "a") or (item == "4.1.a")):
        assignment_1_a()
        print("\n")
    elif ((item == "b") or (item == "4.1.b")):
        assignment_1_b()
        print("\n")
    else:
        print("your input isn't valid \nplease, try again\n")

if __name__ == "__main__":
    try:
        assignment_1()

    except KeyboardInterrupt:
        print("\n Better luck next time")


#%%
import numpy as np
import pandas as pd
import seaborn as sns
sns.set(style="darkgrid")
import matplotlib.pyplot as plt
import cvxpy as cp  #pip install cvxpy --upgrade
import yfinance as yf
#%% Multi-asset analysis
#Calculate returns of individual stocks
tickers=['AAPL','FB','AMZN','BAC','GE','MSFT','TSLA','NFLX']
df=pd.DataFrame()
for t in tickers:
    df[t]=yf.download(t, start='2010-01-01')['Adj Close']

df.head(5)
# %%
print(df.iloc[0])

(df/df.iloc[0]).plot(figsize=(16,5))
plt.title('Price change')
plt.xlabel('Date')
plt.ylabel('Percentage')
plt.show
# %%
returns=(df/df.shift(1))-1
returns= returns.dropna()
print(returns.tail(5))
daily_returns=returns.mean()*250
print(daily_returns)

plt.bar(tickers,list(daily_returns),align='center',alpha=0.5)
plt.ylabel('Value')
plt.title('Annual return')
plt.show()

# %%
# train test split 70-30
T_trn = round(len(returns)*.7)
ret_mat = np.ascontiguousarray(returns.iloc[1::,:])
ret_trn = ret_mat[:T_trn,]
ret_tst = ret_mat[T_trn:,]

mu_trn = np.mean(ret_trn, axis=0)
sigma_trn = np.cov(ret_trn.T)
print(mu_trn,sigma_trn)
# Equally weighted portfolio
w_EWP = np.ones(returns.shape[1])/returns.shape[1]
w_EWP
#%% Minimum variance portfolio:
# As investors are assumed risk-averse but still want to make a good profit
# we introduce the global minimum variance portfolio and the minimum variance portfolio with shortselling contraints

def GMVP(Sigma):
    ones = np.ones(Sigma.shape[0])
    Sigma_inv_1 = np.linalg.solve(Sigma, ones) # same as Sigma_inv @ ones
    w = Sigma_inv_1 / (np.sum(Sigma_inv_1))
    return w
w_GMVP = GMVP(sigma_trn)
print(w_GMVP)

# Minimum variance portfolio with short-selling
def MVP(mu, Sigma, cons ,w_EWP):
    w = cp.Variable(len(mu))
    variance = cp.quad_form(w, Sigma)
    expected_return = w @ mu
    if cons == "no short sell":
        constraint = [w @ mu >= w_EWP @ mu, w >= 0, cp.sum(w) == 1]
    if cons == "short sell":
        constraint = [w @ mu >= w_EWP @ mu,cp.sum(w) == 1]
    problem = cp.Problem(cp.Minimize(variance), constraint)
    problem.solve()
    return w.value
w_MVP_short = MVP(mu_trn, sigma_trn,"short sell",w_EWP)
print(w_MVP_short)
#%% Maximum sharpe ratio portfolio
# This portfolio show the optimized return/risks ratios
def MSR(mu, Sigma):
    ones = np.ones(Sigma.shape[0])
    Sigma_inv_1 = np.linalg.solve(Sigma, mu) # same as Sigma_inv @ ones
    w = Sigma_inv_1 / (np.sum(Sigma_inv_1  ))
    return w
w_MSR = MSR(mu_trn, sigma_trn)
print(w_MSR)

#%%
allocation = pd.DataFrame([w_GMVP,w_MVP_short,w_EWP,w_MSR],columns = tickers)
allocation.index = ['GMVP','MVP','EWP','MSR']
allocation.T.plot.bar(figsize = (15,5))
plt.xticks(rotation=0)
plt.show()

print(allocation)

#%%
def performance(returns,allocation):
    ret = []
    vol = []
    sharpe = []
    wealth = []
    for i in range(4):
        ret.append((np.array(returns) @ (allocation.iloc[i,:].T)).mean())
        vol.append(np.sqrt(np.dot(allocation.iloc[i,:],np.dot(returns.cov(),allocation.iloc[i,:].T))))
        sharpe.append(ret[i]/vol[i])
    performance = pd.DataFrame([ret,vol,sharpe],columns = allocation.index).T
    return performance

performance  = pd.concat([performance(returns.iloc[:T_trn,],allocation),
                         performance(returns.iloc[T_trn:,],allocation)], axis = 1)
performance.columns = ['avg excess return train','volatility train','sharpe ratio train',
                       'avg excess return test','volatility test','sharpe ratio test']
print(performance)

#%%

performance.T.plot.bar(figsize = (15,5))
plt.xticks(rotation=0)
plt.show()

#%%
wealth_geom_trn = []
wealth_geom_tst = []
for i in range(4):
    wealth_geom_trn.append(np.cumprod(np.array(returns.iloc[:T_trn,]) @ (allocation.iloc[i,:].T)+1))
    wealth_geom_tst.append(np.cumprod(np.array(returns.iloc[T_trn:,]) @ (allocation.iloc[i,:].T)+1))

# plots
fig, (ax1, ax2) = plt.subplots(1, 2,figsize=(15,5))
plt.figure(figsize=(10,15))
ax1.plot(np.array(wealth_geom_trn).T)
ax2.plot(np.array(wealth_geom_tst).T)
fig.suptitle('Portfolios performance (compounded)')
ax1.set_title("in-sample")
ax2.set_title("out-of-sample")
ax1.legend(allocation.index,loc = 2)
ax2.legend(allocation.index,loc = 2)
plt.show()

#%%

# zoom in on the in sample plot:

fig, (ax1, ax2) = plt.subplots(1, 2,figsize=(15,5))
plt.figure(figsize=(10,15))
ax1.plot(np.array(wealth_geom_trn).T)
ax2.plot(np.array(wealth_geom_tst).T)
fig.suptitle('Portfolios performance (compounded)')
ax1.set_title("in-sample")
ax2.set_title("out-of-sample")
ax1.legend(allocation.index,loc = 2)
ax2.legend(allocation.index,loc = 2)
ax1.set(ylim=(0,10))
plt.show()

#%%
'''
MSR:
As we can see that, the MSR allow short-selling the GE stocks,
which results in significant return in train set
Although it shows the best performance in test set, we can see that the
the performance dropped dramatically for MSR portfolio
This portfolio is good for risk seekers, not risk adverse investors

GMVP:
This portfolio shows consistency in both train and test dataset
As can be seen from the chart, GMVP show lowest volatility but also lowest returns
This portfolio is the safest bet for risk-adverse investors

MVP:
For this portfolio, we set the expected return to be equal to EWP portfolio.
Although we did include the short-selling option in building this portfolio,
the allocation doesn't have short position.
This portfolio shows higher return than GMVP still with a certain low volatility
In theory the MVP is risk-return optimal for risk-adverse investors.

EWP:
This portfolio is a naive way to build a portoflio.
It isn't belong to Mean-variance framework or Markowitz framework and doesn't optimize any criteria.
However, sometimes this portfolio yields good results, as in this case with this particular dataset of growing companies.

'''

# %%


# Gerhard van der Linde - 19 February 2018
# G00364778
# Excercise #4
# Euler Problem # 5
#
# Write a Python program using for and range to calculate the smallest
# positive number that is evenly divisible by all of the numbers from 1 to 20.

# ANSWER: Smallest value devisable by 1 to 20 is 232792560. (calculated in 46.820 s)

# Optimisation steps were required to arrive at a workable solution.
# The factors was reduce to 11,12,13,14,15,16,17,18,19,20 since the
# smaller values were all factors of the bigger ones in any case. Also
# by incrementing by twenty every cycle reduced a lot of unnessesary attemps
# that would have failed in a test in any case, so this resulted in a 47 second
# solution that was more acceptable.

'''
import time # to calculate the execution time

foundanswer=False # set the starting condition of the value to false
c=20 # Start the test value at twenty
maxval=20
#devisors=[2,3,5,7,11,13,17,19]
devisors=[11,13,14,15,16,17,18,19]
#devisors=[11,12,13,14,15,16,17,18,19]

tstart=time.time()
while foundanswer == False:
    c+=20 # increment the test value by twenty every cycle the answer is not found
    testval=0 # reset the testval for the next test cyle
    #for x in range(11,maxval+1):
    for x in range(len(devisors)):
        #print(c,devisors[x],c%devisors[x])
        testval+=c%devisors[x] # sum all the test values and test for zero
    if testval==0:
        foundanswer=True
        tend=time.time()
        print('Smallest value devisable by 1 to %i is %i. (calculated in %0.3f s)' %(maxval, c,tend-tstart))
'''
# script output sample
# Smallest value devisable by 1 to 20 is 232792560. (calculated in 19.231 s)

# A lengthy group discussion on the topic showed some smarter mathmatical approaches is possible.

# n  Pfact   Prime   Prime Multiple  N
# 1  -
# 2  2       y                       2
# 3  3       y                       3
# 4  2x2             y               2
# 5  5       y                       5
# 6  2x3
# 7  7       y                       7
# 8  2x2x2           y               2
# 9  3x3             y               3
# 10 2x5
# 11 11      y                      11
# 12 2x2x3
# 13 13      y                      13
# 14 2x7
# 15 3x5
# 16 2x2x2x2         y               2
# 17 17      y                      17
# 18 2x3x3
# 19 19      y                      19
# 20 2x2x5

# So in order to calculate the number the table above aims to provide a mathmatical guid
# for developing an improved algorythm to solve the question.

# The answer is a simple multiplcation and includes all the prime numbers in the range plus
# all the occurances of prime facors in the range, so lines 4, 8, 9 and 16, so the final formula
#  for the sample range(20) is: N = 2 * 3 * 2 * 5 * 7 * 2 * 3 * 11 * 13 * 2 * 17 * 19 = 232792560

# Constructing an algorythm from this information then:
# 1. For the range of values(20 for example) list the prime numbers
# 2. generate a sorted list of prime factors in same range to test against
# 3. create a list of primes and and prime factors in the range
# 4. multiply the list to get the final answer

import numpy as np # to calculate product of list N
import time # to calculate the execution time
tstart=time.time()
TestRange = 27
max_r=int(TestRange**.5)# experimental - not sure about this
#max_r=TestRange
print('\n                     Test range: 1 - ',TestRange)
#print('max_r:',max_r) # determine the range of primes to explore for factors
#l=[i for i in range(2,TestRange+1)] # generate a list of numbers to test
p=[] # prime numbers
pf=[] # prime factors
def testprimes(x):
    if x < 2:
        return False
    for n in range(2,x):
        if x % n == 0:
            return False
    return True

for i in range(1,TestRange+1):
    test=testprimes(i)
    if test==True:
        p.append(i) # 1. generate a list of primes numbers in the range
print('prime numbers in the test range:',p)

for i in p:
    if i <= max_r: # only check for number possible smaller than testrange
        #print('\t',i)
        for j in range(2,max_r+1):
            if i**j<TestRange:
                pf.append(i) # 2. Generate the list of prme factors in the range
                #print('\t',i,'^', j,'=', i**j)
print('prime factors in the test range:',pf)
N=pf+p # 3. Combine the two lists
print('   product of the two lists (N): {} = {:,d}'.format(N,np.prod(N)))# calculate the product ofthe list
tend=time.time()
print('                Processing Time: {:f} ms\n'.format((tend-tstart)*100))

# Sample output generated during testing:

#                      Test range: 1 -  20
# prime numbers in the test range: [2, 3, 5, 7, 11, 13, 17, 19]
# prime factors in the test range: [2, 2, 2, 3]
#    product of the two lists (N): [2, 2, 2, 3, 2, 3, 5, 7, 11, 13, 17, 19] = 232,792,560
#                 Processing Time: 0.100327 ms


'''
This is a 2-dimensional example of the rejection method for
better monte carlo estimation. It works as the 1-D case (see 1-D_ARMC.py).
We write this code, in order for the user to understand how the
N-dimensional case is implimented.

As you can see, the 2-D case is almost identical to the 1-D.
The only difference is in #1-4
'''
import numpy as np

def func(x,y):#1
    return y*np.sin(10*(x+1))

N_tot=5000

sample=np.random.rand(N_tot,2)#2

F_sample=[func(i,j) for i,j in sample]#3
abs_F_sample=[ np.abs(func(i,j)) for i,j in sample]#4
_max=np.max(abs_F_sample)

I=0
N_acc=0
for i in np.arange(N_tot):
    _tmp_rand=np.random.rand()
    wi=F_sample[i]
    abs_wi=abs_F_sample[i]
    if _tmp_rand<=abs_wi/_max:
        N_acc+=1.
        I+=wi/np.abs(wi)

norm=N_acc/float(N_tot)*_max
print  (I/(N_acc/norm))


import numpy as np

def imgquadfit(img,imgunc=None,doalpha=False):

    '''
    Fits a quadratic surface to an image

    Input Parameters
    ----------------
    img : array_like
        The image to to fit.

    imgunc : array_like, optional
        The uncertainty image.

    doalpha : {False, True}, optional
        If true, then A**T * A is computed directly instead of A.

    Returns
    --------
    numpy.ndarray
        The coefficients of the fit

    Notes
    -----
    Standard least squares fitting (e.g. Numerical Recipes).
    The program fits the following function:

    z = c[0] + c[1]*x + c[2]*y + c[3]x^2 + c[4]y^2 + c[5]*x*y

    where x is the column number and y is the row number.

    By default, the design matrix A is constructed.  The alpha
    (A^T ## A) and beta (A^T ## b) arrays of the normal equations
    are then constructed and then passed to np.linalg.solve.

    If the dataset to be fit is large and/or the number of coefficients
    is large, the design matrix A can be large (ncoeff,ndat) and may
    take too much memory and/or too much time to construct.  In this case,
    set `doalpha` to True and the alpha and beta arrays are constructed
    directly.  There is a trade off since constructing the alpha and beta
    arrays directly takes longer for smaller surfaces.

    Unlike polyfit1d and polyfit2d, this function ONLY computes the
    coefficients.

    Examples
    --------
    later?

    Modification History
    --------------------
    2022-06-17 - Written by M. Cushing, University of Toledo.
    Based on Spextool IDL program mc_quadfit.pro

    '''

# Do basic things and create basic things

    nrows, ncols = img.shape
    if imgunc is None: imgunc = np.full((nrows,ncols),1.0)

# Create coordinate images

    ximg = np.tile(np.arange(ncols,dtype=float),(nrows,1))
    yimg = np.tile(np.reshape(np.arange(nrows,dtype=float),\
                   (nrows,1)),(1,ncols))

# Ravel the images

    x = np.ravel(ximg)
    y = np.ravel(yimg)
    z = np.ravel(img)
    zunc = np.ravel(imgunc)

# Get rid of NaNs

    znonan = ~np.isnan(z)
    nnan = np.size(znonan)-np.sum(znonan)
    xx = x[znonan]
    yy = y[znonan]
    zz = z[znonan]
    zzunc = zunc[znonan]

# Set up to construct the equations

    ndat = len(xx)
    ncoeffs = 6
    exp = np.array([[0,0],[1,0],[0,1],[2,0],[0,2],[1,1]])

    b = zz/zzunc

# Now start the linear algebra construction

    if doalpha is True:

        alpha = np.empty((ncoeffs,ncoeffs))
        beta = np.empty(ncoeffs)

        for i in range(ncoeffs):

            for j in range(i,ncoeffs):

                at = (xx**exp[i,0] * yy**exp[i,1])/zzunc
                a  = (xx**exp[j,0] * yy**exp[j,1])/zzunc

                val = np.sum(at*a)

                alpha[j,i] = val
                alpha[i,j] = val
                beta[i] = np.sum(at*b)

    elif doalpha is False:

        AT = np.empty((ncoeffs,ndat))
        for i in range(ncoeffs):

            AT[i,:] = xx**exp[i,0]*yy**exp[i,1]/zzunc

        alpha = np.matmul(AT,np.transpose(AT))
        beta = np.matmul(AT,b)

# Solve things

    coeffs = np.linalg.solve(alpha,beta)

    return coeffs


# SI infectous model
# Author: José Alfredo de León
# 09.10.2020

# Program 03b: Nonlinear system, phase portrait with vector plot.
# See Figure 3.12.
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint
import pylab as pl

beta = 1
# The 2-dimensional nonlinear system.
def dx_dt(x, t):
    return [-beta*x[0]*x[1], beta*x[0]*x[1]]
# Trajectories in forward time.
ts = np.linspace(0, 10, 500)
ic = np.linspace(-5, 5, 10)
for r in ic:
    for s in ic:
        x0 = [r, s]
        xs = odeint(dx_dt, x0, ts)
        plt.plot(xs[:,0], xs[:,1], "r-")

# Trajectories in backward time.
ts = np.linspace(0, -10, 500)
ic = np.linspace(-5, 5, 10)
for r in ic:
    for s in ic:
        x0 = [r, s]
        xs = odeint(dx_dt, x0, ts)
        plt.plot(xs[:,0], xs[:,1], "r-")

# Label the axes and set fontsizes.
plt.xlabel("S", fontsize=12)
plt.ylabel("I", fontsize=12)
plt.tick_params(labelsize=10)
plt.xlim(-5, 5)
plt.ylim(-5, 5);

# Plot the vectorfield.
X, Y = np.mgrid[-5:5:12j, -5:5:12j]
u = -beta*X*Y
v = beta*X*Y
pl.quiver(X, Y, u, v, color = 'b')
plt.savefig('SI-infeccioso.pdf')


"""
This module provides functions for generating regularizer matrices
for regularized least squares.
"""
from typing import List
import numpy as np

def get_regularizer_matrix(n_features: int,
                           ridge: float = 0.0,
                           curvature: float = 1.0
                           ) -> np.ndarray:
    """
    Generates additive regularization matrix for linear regression
        using curvature penalty and/or L2 (ridge) penalty.
        The curvature penalty here applies to adjacent coefficients
        in one dimension.

    Args:
        n_features (int): number of features in linear regression problem.
        curvature (float): curvature regularization strength (multiplicative).
            Rule-of-thumb may be similar to ridge regression,
            e.g. optimized through cross-validation between 1e-3 to 1e3
        ridge (float): L2 regularization strength (multiplicative)
            for ridge regression.

    Returns:
        matrix (numpy.ndarray): square matrix of size (n_features x n_features)
            with ridge and curvature penalty a.k.a. fused ridge regression.
    """
    reg_diag = np.eye(n_features) * 2 * curvature
    reg_minus = np.eye(n_features, k=-1) * -curvature
    reg_plus = np.eye(n_features, k=1) * -curvature
    matrix = reg_diag + reg_minus + reg_plus
    matrix[0, 0] /= 2
    matrix[n_features - 1, n_features - 1] /= 2
    if ridge > 0:
        matrix = matrix + np.eye(n_features) * ridge
    return matrix

def combine_regularizer_matrices(matrices: List) -> np.ndarray:
    """
    Combine square penalty matrices.
        Example:
            [X---      [Y-      [Z--        [X--------
             -X--       -Y]      -Z-         -X-------
             --X-                --Z]        --X------
             ---X]                           ---X-----
            (4x4)      (2x2)    (3x3)   ->   ----Y----  (9x9)
                                             -----Y---
                                             ------Z--
                                             -------Z-
                                             --------Z]
    Args:
        matrices (list): square matrices, e.g. for separate optimization
            objectives like A-A, A-B, B-B interactions.

    Returns:
        full_matrix (numpy.ndarray): square penalty matrix equal in length
            to the sum of constituent lengths.
    """
    sizes = [len(m) for m in matrices]
    n_features = int(np.sum(sizes))
    full_matrix = np.zeros((n_features, n_features))
    origins = np.insert(np.cumsum(sizes), 0, 0)
    for i, matrix in enumerate(matrices):
        start = origins[i]
        end = origins[i + 1]
        full_matrix[start:end, start:end] = matrix
    return full_matrix

def get_penalty_matrix_2D(L: int,
                          M: int,
                          ridge: float = 0.0,
                          curvature: float = 1.0
                          ) -> np.ndarray:
    """
    Generates additive regularization matrix for linear regression
        using curvature penalty and/or L2 (ridge) penalty.
        Curvature penalty here applies to coefficients that are spatially
        related in two dimensions that may not be adjacent after flattening
        for linear least-squares.

    Args:
        L (int): length of coefficient matrix before flattening.
        M (int): width of coefficient matrix before flattening.
        ridge (float): L2 (ridge) regularization strength.
        curvature (float): Local curvature regularization strength.

    Returns:
        matrix_2d (numpy.ndarray): square penalty matrix for linear
            least-squares of shape (L*M , L*M).
    """
    matrix_2d = np.zeros((L * M, L, M))
    idx = 0
    for i in range(L):
        for j in range(M):
            if any([i == 0, i == L - 1, j == 0, j == M - 1]):
                matrix_2d[idx, i, j] = 1
            else:
                matrix_2d[idx, i, j] = 2

            if i > 0:
                matrix_2d[idx, i - 1, j] = -1
            if i + 1 < L:
                matrix_2d[idx, i + 1, j] = -1
            if j > 0:
                matrix_2d[idx, i, j - 1] = -1
            if j + 1 < M:
                matrix_2d[idx, i, j + 1] = -1
            idx += 1
    matrix_2d = matrix_2d.reshape(L * M, L * M) * curvature
    if ridge > 0:
        matrix_2d = matrix_2d + np.eye(L * M) * ridge
    return matrix_2d

def get_penalty_matrix_3D(L: int,
                          M: int,
                          N: int,
                          ridge: float = 0.0,
                          curvature: float = 1.0,
                          periodic=(False, False, True),
                          ) -> np.ndarray:
    """
    Generates additive regularization matrix for linear regression
        using curvature penalty and/or L2 (ridge) penalty.
        Curvature penalty here applies to coefficients that are spatially
        related in three dimensions that may not be adjacent after flattening
        for linear least-squares

    Args:
        L (int): length of coefficient matrix before flattening.
        M (int): width of coefficient matrix before flattening.
        N (int): depth of coefficient matrix before flattening.
        ridge (float): L2 (ridge) regularization strength.
        curvature (float): Local curvature regularization strength.
        periodic (list): periodicity in regularization for three dimensions.
            Default (False, False, True) for periodicity in angular space.

    Returns:
        matrix_3d (numpy.ndarray): square penalty matrix for linear
            least-squares of shape (L*M*N , L*M*N).
    """
    matrix_3d = np.zeros((L * M * N, L, M, N))
    idx = 0
    i_pbc, j_pbc, k_pbc = periodic

    for i in range(L):
        for j in range(M):
            for k in range(N):
                center_value = 2
                # i dimension
                if i_pbc:
                    matrix_3d[idx, (i - 1) % L, j, k] = -1
                    matrix_3d[idx, (i + 1) % L, j, k] = -1
                else:
                    if i > 0:  # lower bound
                        matrix_3d[idx, i - 1, j, k] = -1
                    else:
                        center_value = 1
                    if i + 1 < L:  # upper bound
                        matrix_3d[idx, i + 1, j, k] = -1
                    else:
                        center_value = 1
                # j dimension
                if j_pbc:
                    matrix_3d[idx, i, (j - 1) % M, k] = -1
                    matrix_3d[idx, i, (j + 1) % M, k] = -1
                else:
                    if j > 0:  # lower bound
                        matrix_3d[idx, i, j - 1, k] = -1
                    else:
                        center_value = 1
                    if j + 1 < M:
                        matrix_3d[idx, i, j + 1, k] = -1
                    else:
                        center_value = 1
                # k dimension
                if k_pbc:
                    matrix_3d[idx, i, j, (k - 1) % N] = -1
                    matrix_3d[idx, i, j, (k + 1) % N] = -1
                else:
                    if k > 0:  # lower bound
                        matrix_3d[idx, i, j, k - 1] = -1
                    else:
                        center_value = 1
                    if k + 1 < N:  # upper bound
                        matrix_3d[idx, i, j, k + 1] = -1
                    else:
                        center_value = 1

                matrix_3d[idx, i, j, k] = center_value

                idx += 1
    matrix_3d = matrix_3d.reshape(L * M * N, L * M * N) * curvature
    if ridge > 0:
        matrix_3d = matrix_3d + np.eye(L * M * N) * ridge
    return matrix_3d


import numpy as np

#     |a|
#     |b|
# U = |c|
#     |d|
#     |e|
#     |f|
#
# MU = C
M = np.array([
    [-4.,  1.,  1.,  0.,  0.,  0.],
    [ 1., -4.,  0.,  1.,  0.,  0.],
    [ 1.,  0., -4.,  1.,  1.,  0.],
    [ 0.,  1.,  1., -4.,  0.,  1.],
    [ 0.,  0.,  2.,  0., -4.,  1.],
    [ 0.,  0.,  0.,  2.,  1., -4.],
])
C = np.array([
       2.,
      -4.,
       5.,
     -28.,
      -2.,
    -116.,
])

if not np.all(M.diagonal() == M[0,0]):
    print('행렬의 대각성분 값이 모두 같아야합니다.')
    exit(1)

# If M = X + kI:
#     MU = C
#  => XU + kU = C
#  => U = -1/k*XU + 1/k*C
#
# U = AU + B
#     where A = -1/k * X,
#           B =  1/k * C
k = M[0,0]

X = M.copy()
np.fill_diagonal(X, 0)

A = -1/k * X
B =  1/k * C

# Initial values
U = np.array([0., 0., 0., 0., 0., 0.])
print(f'''\
U = V = [u_11  u_12  u_21  u_22  u_31  u_32]

U = {U}''')

for i in range(5):
    U = A@U + B
    letter = 'V' if i % 2 == 0 else 'U'
    print(f'{letter} = {U}')

print(f'''
Result after 5 steps:
  u_11 = {U[0]}
  u_12 = {U[1]}
  u_21 = {U[2]}
  u_22 = {U[3]}
''')

for i in range(5, 20):
    U = A@U + B
    letter = 'V' if i % 2 == 0 else 'U'
    print(f'{letter} = {U}')

print(f'''
Result after 20 steps:
  u_11 = {U[0]}
  u_12 = {U[1]}
  u_21 = {U[2]}
  u_22 = {U[3]}
''')