### Bernstein Basis
import numpy as np

## Ex 4.1
from scipy.special import binom

def B1(i,n,t):
    """Restituisce il polinomio di Bernstein (i,n) valutato in t,
    usando la definizione binomiale"""
    if i < 0 or i > n:
        return 0
    return binom(n,i)* t**i * (1-t)**(n-i)

def B2(i,n,t):
    """Restituisce il polinomio di Bernstein (i,n) valutato in t,
    usando una definizione ricorsiva"""
    if i < 0 or i > n:
        return 0
    elif n == 0:
        return 1

    return (1-t) * B2(i,n-1,t) + t * B2(i-1,n-1,t)

def B3(k,n,t):
    """Restituisce il polinomio di Bernstein (i,n) valutato in t,
    usando l'espressione in serie di potenze"""
    return sum([(-1)**(i-k) * binom(n,i) * binom(i,k) * t**i
         for i in range(k, n+1)])

## Ex 4.2
import cProfile

cProfile.run("B1(7,20,1)")
cProfile.run("B2(7,20,1)")
cProfile.run("B3(7,20,1)")

## Ex 4.3
from matplotlib import pylab as plt

# Segue: plot di B1(i, n, x) per n in [20,40,80,160,320].
step = 2**(-7)
plotNodes = np.arange(0, 1+step, step)
for n in 10 * (2**np.arange(1,6,1)):
    for i in range(n+1):
        plt.plot(plotNodes, [B1(i,n,x) for x in plotNodes])
    plt.title("B1(i, "+ str(n) + ", x)")
    plt.show()

# Segue: codice per il plot di B2.
# ! E' stato commentato in quanto l'algoritmo ricorsivo impiega un
# tempo eccessivo anche solo per calcolare la griglia per n = 20.

#plotNodes = np.arange(0, 1+step, step)
#for n in 10 * (2**np.arange(1,6,1)):
#    for i in range(n+1):
#       plt.plot(plotNodes, [B2(i,n,x) for x in plotNodes])
#    plt.title("B2(i, "+ str(n) + ", x)")
#    plt.show()

# Segue: codice per il plot di B3
# ! Per ridurre il tempo di esecuzione l'algoritmo calcola B3(i, n, x)
# solo per n = 20, 40.
plotNodes = np.arange(0, 1+step, step)
for n in 10 * (2**np.arange(1,3,1)):
    for i in range(n+1):
        plt.plot(plotNodes, [B3(i,n,x) for x in plotNodes])
    plt.title("B3(i, "+ str(n) + ", x)")
    plt.show()
# Nel grafico per n = 40 si possono già notare gravi errori
# di approssimazione nelle vicinanze di 1, dovuti alla presenza
# di molte somme di termini molto piccoli.

## Ex 4.4
# Segue una reimplementazione della definizione di B3 con il modulo
# scipy.polynomial.
import numpy.polynomial as P

def B3Poly(i,n):
    """Restituisce il polinomio di Bernstein (i,n), implementato con
    numpy.polynomial e usando l'espressione in serie di potenze"""
    outPoly = P.Polynomial([0])
    for k in range(i,n+1):
        nextTerm = k*[0]
        nextTerm.append((-1)**(i-k) * binom(n,i) * binom(i,k))
        outPoly += nextTerm
    return outPoly

def DB3Poly(i,n):
    """Restituisce la derivata del polinomio di Bernstein (i,n),
    implementato con numpy.polynomial e usando l'espressione in serie
    di potenze"""
    return B3Poly(i,n).deriv()

# Tuttavia questa implementazione ha ancora problemi di stabilità
# numerica non risolti dalla rappresentazione con numpy.polynomial.
# Svolgendo i conti a mano per la derivata di B1 otteniamo la
# seguente funzione, numericamente più stabile.

def DB1(i,n,x):
    return n * (B1(i-1,n-1,x) - B1(i,n-1,x))

## Ex 4.5
def B(n,f,x):
    return sum([B1(i,n,x) * f(i/n) for i in range(n+1)])

for j in 4**np.arange(1,5,1):
    plt.plot(plotNodes,
             [B(j, lambda y: np.cos(16*y), x)
              for x in plotNodes])
    plt.title(str(j) + "th Bernstein approximation of cos(16*x)")
    plt.show()


'''
Purpose
-------
Sample from a 2-dim. Normal distribution using a Random Walk Sampler.
This is the Metropolis MCMC algorithm with a Gaussian proposal with controllable stddev

Target distribution:
# mean
>>> mu_D = np.asarray([-1.0, 1.0])
# covariance
>>> cov_DD = np.asarray([[2.0, 0.95], [0.95, 1.0]])

'''

import numpy as np
import scipy.stats
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')

from RandomWalkSampler import RandomWalkSampler2D

def calc_target_log_pdf(z_D):
    ''' Compute log pdf of provided z value under target bivariate Normal distribution

    Args
    ----
    z_D : 1D array, size (D,)
        Value of the random variable at which we should compute log pdf

    Returns
    -------
    logpdf : float real scalar
        Log probability density function value at provided input
    '''
    ## TODO compute logpdf of z_D value under target distribution
    return -0.5 * np.sum(np.square(z_D)) # fixme

if __name__ == '__main__':
    n_samples = 10000   # total number of iterations of MCMC
    n_keep = 5000       # number samples to keep
    random_state = 42   # seed for random number generator
    prng = np.random.RandomState(random_state)

    # Two initializations, labeled 'A' and 'B'
    z_initA_D = np.zeros(2)
    z_initB_D = np.asarray([1.0, -1.0])

    # Look at a range of std. deviations
    rw_stddev_grid = np.asarray([0.01, 0.1, 1.0, 10.0])
    G = len(rw_stddev_grid)

    # Prepare a plot to view samples from two chains (A/B) side-by-side
    _, ax_grid = plt.subplots(
        nrows=2, ncols=G, sharex=True, sharey=True,
        figsize=(2*G, 2*2))

    for rr, rw_stddev in enumerate(rw_stddev_grid):

        # TODO Create samplers and run them for specified num iterations
        # Make sure to provide rw_stddev and random_state as args

        # TODO Stack list of samples into a 2D array of size (S, D)
        # Keeping only the last n_keep samples (and thus discarding burnin)
        zA_SD = prng.randn(n_keep, 2)  # fixme
        zB_SD = prng.randn(n_keep, 2)  # fixme

        # TODO unpack info about accept rates
        samplerA_accept_rate_last_half = 0.0
        samplerB_accept_rate_last_half = 0.0

        # Plot samples as scatterplot
        # Use small alpha transparency value for visual debugging of rare/frequent samples
        ax_grid[0,rr].set_title("rw_stddev = %.2F" % rw_stddev)
        ax_grid[0,rr].plot(zA_SD[:,0], zA_SD[:,1], 'r.', alpha=0.05)
        ax_grid[1,rr].plot(zB_SD[:,0], zB_SD[:,1], 'b.', alpha=0.05)
        # Mark initial points with "X"
        ax_grid[0,rr].plot(z_initA_D[0], z_initA_D[1], 'rx')
        ax_grid[1,rr].plot(z_initB_D[0], z_initB_D[1], 'bx')

        # Pretty print some stats for the samples
        # To give a way to check "convergence" from the terminal's stdout
        msg_pattern = ("RW stddev %5.2f from init %s | kept %d of %d samples | accept rate %.3f"
            + "\n    percentiles z0: 10th % 5.2f   50th % 5.2f   90th % 5.2f"
            + "\n    percentiles z1: 10th % 5.2f   50th % 5.2f   90th % 5.2f"
            )
        print(msg_pattern % (
            rw_stddev, 'A', n_keep, n_samples,
            samplerA_accept_rate_last_half,
            *tuple(np.percentile(zA_SD[:,0:1], [10, 50, 90], axis=0)),
            *tuple(np.percentile(zA_SD[:,1:2], [10, 50, 90], axis=0)),
            ))
        print(msg_pattern % (
            rw_stddev, 'B', n_keep, n_samples,
            samplerB_accept_rate_last_half,
            *tuple(np.percentile(zB_SD[:,0:1], [10, 50, 90], axis=0)),
            *tuple(np.percentile(zB_SD[:,1:2], [10, 50, 90], axis=0)),
            ))

    # Make plots pretty and standardized
    for ax in ax_grid.flatten():
        ax.set_xlim([-5, 5]);
        ax.set_ylim([-5, 5]);
        ax.set_aspect('equal', 'box');
        ax.set_xticks([-4, -2, 0, 2, 4])
        ax.set_yticks([-4, -2, 0, 2, 4])

    plt.tight_layout()
    plt.show()


"""parab.py: Parabola fit around three points to find a true vertex."""

import numpy as np

def fit_peak(f, x):
	'''
	Uses solution to parabola to fit peak and two surrounding points
	This assumes there is a peak (i.e. parabola second deriv is negative)

	Parameters
	----------
	f : array f(x)
	x : array x with the indices corresponding to f

	If interested, this is educational to see with sympy
	 import sympy
	 y1, y2, y3 = sympy.symbols('y1 y2 y3')
	 A = sympy.Matrix([[(-1)**2, -1, 1],[0**2, 0, 1],[(1)**2,1,1]])
	 C = sympy.Matrix([[y1],[y2],[y3]])
	 D = A.inv().multiply(C)
	 D contains the values of a, b, c in ax**2 + bx + c
	 Peak position is at x = -D[1]/(2D[0])
	'''

	pk = np.argmax(f)

	y1 = f[pk - 1]
	y2 = f[pk]
	y3 = f[pk + 1]

	a = 0.5 * y1 - y2 + 0.5 * y3
	b = -0.5 * y1 + 0.5 * y3
	c = y2

	xindex = -b / (2 * a)
	findex = xindex * (x[1] - x[0]) + x[1]
	yindex = a * xindex ** 2 + b * xindex + c

	return findex, yindex, xindex

def ridge_finder(spectrogram, freq_bin):
	'''
	Uses parabolda to fit peak and two surrounding points
	This takes a spectrogram and the frequency bin spacing and wraps parab.fit_2d

	Parameters
	----------
	spectrogram : ndarray
		Returned by scipy.signal.spectrogram or stft or cwt
		Arranged in (frequencies, times) shape

	freq_bin : ndarray
		arrays corresponding the frequencies in the spectrogram

	Returns
	-------
	xindex : ndarray
		1D array of the frequency bins returned by parabolic approximation
	yindex : ndarray
		1D array of the peak values at the xindices supplied
	'''
	_argmax = np.argmax(np.abs(spectrogram), axis=0)
	cols = spectrogram.shape[1]

	# generate a (3, cols) matrix of the spectrogram values
	maxspec = np.array([spectrogram[(_argmax - 1, range(cols))],
						spectrogram[(_argmax, range(cols))],
						spectrogram[(_argmax + 1, range(cols))]])

	return fit_2d(maxspec, _argmax, freq_bin)

def fit_2d(f, p, dx):
	'''
	Uses solution to parabola to fit peak and two surrounding points
	This assumes there is a peak (i.e. parabola second deriv is negative).

	This is a broadcast version for speed purposes

	Parameters
	----------
	f : 2-d array f(x) of size (3 , samples)

	p : 1-d array with the peak positions for f

	dx : 1-d array with the frequency (x values) of f
	'''

	if f.shape[0] != 3:
		raise ValueError('Must be exactly 3 rows')

	a = 0.5 * f[0, :] - f[1, :] + 0.5 * f[2, :]
	b = -0.5 * f[0, :] + 0.5 * f[2, :]
	c = f[1, :]

	xindex = -b / (2 * a)
	findex = xindex * (dx[1] - dx[0]) + dx[p]
	yindex = a * (xindex ** 2) + b * xindex + c

	return findex, yindex, xindex

def fit_peak_old(f, x):
	"""
	f = array
	x = index of peak, typically just argmax

	Uses parabola equation to fit to the peak and two surrounding points
	"""

	x1 = x - 1
	x2 = x
	x3 = x + 1

	y1 = f[x - 1]
	y2 = f[x]
	y3 = f[x + 1]

	d = (x1 - x3) * (x1 - x2) * (x2 - x3)

	A = (x1 * (y3 - y2) + x2 * (y1 - y3) + x3 * (y2 - y1)) / d

	B = (x1 ** 2.0 * (y2 - y3) +
		 x2 ** 2.0 * (y3 - y1) +
		 x3 ** 2.0 * (y1 - y2)) / d

	C = (x2 * x3 * (x2 - x3) * y1 +
		 x3 * x1 * (x3 - x1) * y2 +
		 x1 * x2 * (x1 - x2) * y3) / d

	xindex = -B / (2.0 * A)
	yindex = C - B ** 2.0 / (4.0 * A)

	return xindex, yindex


import numpy as np
from timeit import default_timer as timer

def seminorm_loop(x, p, display = False):
    """
    Calculate for a given complex vector and some 1 <= p < inf the l_p-semi-norm
    using loops and scalar arithmetic
    """
    if p < 1:
        raise ValueError("p must be >= 1")

    # Using scalar arithmetic to calculate each elements absolute value raised to p
    x_abs_p = x.real**2 + x.imag**2 * (1j)
    x_abs_p = ((x_abs_p.real + x_abs_p.imag) ** 0.5) ** p

    # Sum all elements with an uneven index
    sum = 0
    for j in range(1, len(x_abs_p)+1, 2):
        # Since arrays in python are zero-based, substract 1 from j
        j -= 1
        sum += x_abs_p[j]

    # Raise to 1/p to get the l_p-semi-norm
    result = sum**(1/p)

    if display:
        print(result)

    return result

def seminorm(x, p, display = False):
    """
    Calculate for a given complex vector and some 1 <= p < inf the l_p-semi-norm
    using vector functions and arithmetics
    """
    if p < 1:
        raise ValueError("p must be >= 1")

    # Using slicing to get every second element, then calculate the absulte value for each element
    # raised to p, then take the sum of them which is raised to 1/p
    result = np.sum(np.abs(x[::2])**p)**(1/p)

    if display:
        print(result)

    return result

def main():
    # Test functions
    x = np.array([1+1j, 2+2j, 3+3j])
    seminorm_loop(x, 3, True)
    seminorm(x, 3, True)

    #---------------------------------------------------------
    print("### Test performance ###")
    # Generate random complex vector with dim 10**5
    np.random.seed(0)
    x = np.random.random(10**7) + np.random.random(10**7) * 1j
    p = np.random.randint(1, 1000)

    # Method 1: using loops and scalar arithmetic
    tic = timer()
    seminorm_loop(x, p)
    toc = timer()
    print("Method /w loops: ", toc - tic, "s")

    # Method 2: using vector functios and arithmetics
    tic = timer()
    seminorm(x, p)
    toc = timer()
    print("Method /w vec-fns: ", toc - tic, "s")

    """ Result (on my machine)
    Method /w loops:    3.6782882 s
    Method /w vec-fns:  0.3709317 s

    Interpretation:
    Using numpy vector function are apparently faster then larger the vector size gets.
    """

if __name__ == "__main__":
    main()


import numpy as np

def FE_logistic(p, u0, dt, Nt):
    u = np.zeros(Nt+1)
    u[0] = u0
    for n in range(Nt):
        u[n+1] = u[n] + dt*(1 - u[n])**p*u[n]
    return u

def BE_logistic(p, u0, dt, Nt, choice='Picard',
                eps_r=1E-3, omega=1, max_iter=1000):
    # u[n] = u[n-1] + dt*(1-u[n])**p*u[n]
    # -dt*(1-u[n])**p*u[n] + u[n] = u[n-1]
    if choice == 'Picard1':
        choice = 'Picard'
        max_iter = 1

    u = np.zeros(Nt+1)
    iterations = []
    u[0] = u0
    for n in range(1, Nt+1):
        c = -u[n-1]
        if choice == 'Picard':
            def F(u):
                return -dt*(1-u)**p*u + u + c

            u_ = u[n-1]
            k = 0
            while abs(F(u_)) > eps_r and k < max_iter:
                # u*(1-dt*(1-u_)**p) + c = 0
                u_ = omega*(-c/(1-dt*(1-u_)**p)) + (1-omega)*u_
                k += 1
            u[n] = u_
            iterations.append(k)

        elif choice == 'Newton':
            def F(u):
                return -dt*(1-u)**p*u + u + c

            def dF(u):
                return dt*p*(1-u)**(p-1)*u - dt*(1-u)**p + 1

            u_ = u[n-1]
            k = 0
            while abs(F(u_)) > eps_r and k < max_iter:
                u_ = u_ - F(u_)/dF(u_)
                k += 1
            u[n] = u_
            iterations.append(k)
    return u, iterations

def CN_logistic(p, u0, dt, Nt):
    # u[n+1] = u[n] + dt*(1-u[n])**p*u[n+1]
    # (1 - dt*(1-u[n])**p)*u[n+1] = u[n]
    u = np.zeros(Nt+1)
    u[0] = u0
    for n in range(0, Nt):
        u[n+1] = u[n]/(1 - dt*(1 - u[n])**p)
    return u

def test_asymptotic_value():
    T = 100
    dt = 0.1
    Nt = int(round(T/float(dt)))
    u0 = 0.1
    p = 1.8

    u_CN = CN_logistic(p, u0, dt, Nt)
    u_BE_Picard, iter_Picard = BE_logistic(
        p, u0, dt, Nt, choice='Picard',
        eps_r=1E-5, omega=1, max_iter=1000)
    u_BE_Newton, iter_Newton = BE_logistic(
        p, u0, dt, Nt, choice='Newton',
        eps_r=1E-5, omega=1, max_iter=1000)
    u_FE = FE_logistic(p, u0, dt, Nt)

    for arr in u_CN, u_BE_Picard, u_BE_Newton, u_FE:
        expected = 1
        computed = arr[-1]
        tol = 0.01
        msg = 'expected=%s, computed=%s' % (expected, computed)
        print msg
        assert abs(expected - computed) < tol

from scitools.std import *

def demo():
    T = 12
    p = 1.2
    try:
        dt = float(sys.argv[1])
        eps_r = float(sys.argv[2])
        omega = float(sys.argv[3])
    except:
        dt = 0.8
        eps_r = 1E-3
        omega = 1
    N = int(round(T/float(dt)))

    u_FE = FE_logistic(p, 0.1, dt, N)
    u_BE31, iter_BE31 = BE_logistic(p, 0.1, dt, N,
                                    'Picard1', eps_r, omega)
    u_BE3, iter_BE3 = BE_logistic(p, 0.1, dt, N,
                                  'Picard', eps_r, omega)
    u_BE4, iter_BE4 = BE_logistic(p, 0.1, dt, N,
                                  'Newton', eps_r, omega)
    u_CN = CN_logistic(p, 0.1, dt, N)

    print 'Picard mean no of iterations (dt=%g):' % dt, \
          int(round(mean(iter_BE3)))
    print 'Newton mean no of iterations (dt=%g):' % dt, \
          int(round(mean(iter_BE4)))

    t = np.linspace(0, dt*N, N+1)
    plot(t, u_FE, t, u_BE3, t, u_BE31, t, u_BE4, t, u_CN,
         legend=['FE', 'BE Picard', 'BE Picard1', 'BE Newton', 'CN gm'],
         title='dt=%g, eps=%.0E' % (dt, eps_r), xlabel='t', ylabel='u',
         legend_loc='lower right')
    filestem = 'logistic_N%d_eps%03d' % (N, log10(eps_r))
    savefig(filestem + '_u.png')
    savefig(filestem + '_u.pdf')
    figure()
    plot(range(1, len(iter_BE3)+1), iter_BE3, 'r-o',
         range(1, len(iter_BE4)+1), iter_BE4, 'b-o',
         legend=['Picard', 'Newton'],
         title='dt=%g, eps=%.0E' % (dt, eps_r),
         axis=[1, N+1, 0, max(iter_BE3 + iter_BE4)+1],
         xlabel='Time level', ylabel='No of iterations')
    savefig(filestem + '_iter.png')
    savefig(filestem + '_iter.pdf')
    raw_input()

def test_solvers():
    p = 2.5
    T = 5000
    dt = 0.5
    eps_r = 1E-6
    omega_values = [1]
    tol = 0.01
    N = int(round(T/float(dt)))

    for omega in omega_values:
        u_FE = FE_logistic(p, 0.1, dt, N)
        u_BE31, iter_BE31 = BE_logistic(p, 0.1, dt, N,
                                        'Picard1', eps_r, omega)
        u_BE3, iter_BE3 = BE_logistic(p, 0.1, dt, N,
                                      'Picard', eps_r, omega)
        u_BE4, iter_BE4 = BE_logistic(p, 0.1, dt, N,
                                      'Newton', eps_r, omega)
        u_CN = CN_logistic(p, 0.1, dt, N)

        print u_FE[-1], u_BE31[-1], u_BE3[-1], u_CN[-1]
        for u_x in u_FE, u_BE31, u_BE3, u_CN:
            print u_x[-1]
            assert abs(u_x[-1] - 1) < tol, 'u=%.16f' % u_x[-1]

    """
    t = np.linspace(0, dt*N, N+1)
    plot(t, u_FE, t, u_BE3, t, u_BE31, t, u_BE4, t, u_CN,
         legend=['FE', 'BE Picard', 'BE Picard1', 'BE Newton', 'CN gm'],
         title='dt=%g, eps=%.0E' % (dt, eps_r), xlabel='t', ylabel='u',
         legend_loc='lower right')
    filestem = 'tmp_N%d_eps%03d' % (N, log10(eps_r))
    savefig(filestem + '_u.png')
    savefig(filestem + '_u.pdf')
    """

if __name__ == '__main__':
    #demo()
    #test_solvers()
    test_asymptotic_value()


import numpy as np

# Initial conditon

def u0(x): return (x*(1-x))

#Exact Solution

def u(x, t): return 8*np.sin(np.pi*x)*np.exp(-t*np.pi**2)/np.pi**3

# Discretization parameters

J = 24;  dx = 1.0 / J;  x = np.linspace(0, 1, J+1)
T = 0.1;  N = 24;  dt = T / N;  mu = dt / dx**2
theta =1./2

# Build the system matrix

a = (1-theta)*mu*np.ones(J-2);  b = (1-2*(1-theta)*mu)*np.ones(J-1); #theta=1/2 a = 1/2*np one
A = np.diag(a, -1) + np.diag(b, 0) + np.diag(a, 1)

c = -(theta*mu)*np.ones(J-2);  d = (1+2*theta*mu)*np.ones(J-1);
B = np.diag(c, -1) + np.diag(d, 0) + np.diag(c, 1)

# Initialize the solution vectors

Uold = u0(x[1:J]);  Unew = np.zeros(J-1)

# Time stepping
#B*Unew = A*Uold

for n in range(N):
    Unew = np.linalg.solve(B, A.dot(Uold))
    for j in range(J-1):
         Uold[j] = Unew[j]
    print(Unew)

Ex2-Finite Difference Methods for the heat equation-Theta Methods/Code+Report/theta.py


# -*- coding: utf-8 -*-
"""
Created on Fri Sep 27 10:01:04 2019

@author: hamil
"""

import numpy as np
import matplotlib.pyplot as plt
import numpy.random as rand

#Define a function to calculate the integral by random sampling (pond idea)
def Probint(N, initial, final, limit):
    topx = []
    topy = []
    botx = []
    boty = []
    N_out = N
    #for each iteration, randomly generate the x and y values and calculate actual x**2 value
    for n in range(N):
        x = rand.uniform(initial,final)
        y = rand.uniform(initial,limit)
        y2 = x**2
        #determine which is greater and append to correct array
        if y > y2:
            N_out = N_out - 1
            topx.append(x)
            topy.append(y)
            #top_array = [x,y]
        elif y < y2:
            botx.append(x)
            boty.append(y)
            #bot_array = [x,y2]
    #calculate the total number of points and the results
    ratio = N_out/N
    area = final*limit
    results = area*ratio
    return results, topx,topy,botx,boty

test1, topx,topy,botx,boty = Probint(1000000, 0, 10, 100)
print("The estimated value for (x**2) is:", test1)
plt.scatter(topx, topy, s=2, color = 'green')
plt.scatter(botx, boty, s=2, color = 'blue')
plt.title("Distrubition of values for Integral of X**2")
plt.show()

#With 100,000 iterations, my function was able to get around a reasonable answer for
#for the integral of x**2 from 0 to 10. Even better result occurred with 1 million iterations.
#This is a reasonable value for the fact that it is
#calculated from randomly generated numbers. But it is interesting that you can use the distributation
#function to determine the integral of a function. Based on the fact that we have a correlation to
#area.

#Now use the function to calculate the integral for sin(x)
def Probint2(N, initial, final, up_limit, down_limit):
    topx = []
    topy = []
    botx = []
    boty = []
    N_out = N
    for n in range(N):
        x = rand.uniform(initial,final)
        y = rand.uniform(initial,up_limit)
        y2 = np.sin(x)
        if (y < y2) and (y >= 0) or (y > y2) and (y <= 0):
            topx.append(x)
            topy.append(y)
            N_out = N - 2
        else:
            botx.append(x)
            boty.append(y)
    tot_top = len(topy)
    tot_bot = len(boty)
    ratio = N/N_out
    lowx = np.min(botx)
    highx = np.max(botx)
    lowy = np.min(boty)
    highy = np.max(boty)
    xvalue = highx+lowx
    yvalue = highy+lowy
    area = xvalue*yvalue
    result = area*ratio
    return result, topx,topy,botx,boty

test2, toppx, toppy, bottx, botty = Probint2(1000000, -2*np.pi, 2*np.pi, 1, -1)
print("The estimated value for sin(x) is:", test2)
plt.scatter(toppx, toppy, s=2, color = 'purple')
plt.scatter(bottx, botty, s=2, color = 'pink')
plt.title("Distrubition of Values for Integral of sin(x)")
plt.ylim(-1,1)
plt.show()

#Here when looking at the calculated value from my function compared to the analytical value,
#my result is smaller by a couple orders of magnitude. It is close,but not quite there. I do not
#believe the function will be able to accurately calculate the integral of sin(x) from 0 to 10.
#With one million iterations alreasy, I do not know how feasible this method would be to precisely
#calculate the value. It is a good estimation though. But, the variation of the results for several runs,
#creates too much uncertainity. Some of the results are outside of pershaps two dtandard deviations.

#Now create a new function to calculate the integral of.
def Probint3(N, initial, final, up_limit):
    topx = []
    topy = []
    botx = []
    boty = []
    N_out = N
    for n in range(N):
        x = rand.uniform(initial,final)
        y = rand.uniform(initial,up_limit)
        y2 = np.exp(x**3)+ (2*x**2)+3
        if (y < y2) and (y >= 0) or (y > y2) and (y <= 0):
            topx.append(x)
            topy.append(y)
            N_out = N - 2
        else:
            botx.append(x)
            boty.append(y)
    tot_top = len(topy)
    tot_bot = len(botx)
    ratio = N/N_out
    area = up_limit*tot_bot
    #area = xvalue*yvalue
    result = area*ratio
    return result, topx,topy,botx,boty

test3, tx, ty, bx, by = Probint3(1000000, -10, 10, 500)
print("The estimated value of (e**(x**3)+ 2x**2 + 3) is:", test3)
plt.scatter(tx, ty, s=2, color = 'pink')
plt.scatter(bx, by, s=2, color = 'blue')
plt.title("Distrubition of Values for Integral of (e**(x**3)+2x**2+3)")
plt.show()


"""
Use Gaussian distributions to randomly generate two sets. Then use bhatta_dist() on the sets. Compare the results to the
 theoretical Bhattacharyya distance for the distributions.

The Bhattacharyya distance between two Gaussian distributions is given on this page:
  https://en.wikipedia.org/wiki/Bhattacharyya_distance

Created on 4/17/2018
Author: Eric Williamson (ericpaulwill@gmail.com)
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde
from bhatta_dist import bhatta_dist

###We will generate our distributions from these parameters:
sigma1 = 0.5 #standard deviation
mu1 = 2.0    #mean
n1 = 5000    #population size
sigma2 = 1.5
mu2 = 3.1
n2 = 10000

###Calculate theoretical Bhattacharyya distance:
var1 = sigma1**2
var2 = sigma2**2
bdist_theory = np.log( (var1/var2 + var2/var1 + 2)/4 ) / 4  +  (mu1-mu2)**2 / (var1+var2) / 4

###Generate random data that follows our normal distributions:
X1 = np.random.normal(mu1,sigma1,n1)
X2 = np.random.normal(mu2,sigma2,n2)

###Plot distributions of X1 and X2 to verify that we generated the data we want:
def get_density(x,cov_factor=0.1):
    #Produces a continuous density function for the data in 'x'. Some benefit may be gained from adjusting the cov_factor.
    #Note that cov_factor is not related to scaling or bias.
    density = gaussian_kde(x)
    density.covariance_factor = lambda:cov_factor
    density._compute_covariance()
    return density
N_STEPS = 200
d1 = get_density(X1)
d2 = get_density(X2)
xs = np.linspace(0,10,N_STEPS)
fig, ax = plt.subplots()
p1 = np.exp(-((xs-mu1)**2) / (2*var1)) / np.sqrt(2*np.pi*var1)
p2 = np.exp(-((xs-mu2)**2) / (2*var2)) / np.sqrt(2*np.pi*var2)
ax.plot(xs,d1(xs))
ax.plot(xs,d2(xs))
ax.plot(xs,p1)
ax.plot(xs,p2)
ax.legend(['X1 density','X2 density','theoretical 1','theoretical 2'])
plt.show()

###Use bhatta_dist() function to calculate Bhattacharyya distance:
#Note that the 'noiseless' method is not tested here, since the feature we've generated is quantitative.
bdist_cont = bhatta_dist(X1,X2,method='continuous')
bdist_hist = bhatta_dist(X1,X2,method='hist')
bdist_ahist = bhatta_dist(X1,X2,method='autohist')
#Show results:
print("Theoretical:       {:.3f}".format(bdist_theory))
print("Test 'continuous': {:.3f}   Error: {:.6f}".format(bdist_cont, bdist_cont-bdist_theory))
print("Test 'hist':       {:.3f}   Error: {:.6f}".format(bdist_hist, bdist_hist-bdist_theory))
print("Test 'autohist':   {:.3f}   Error: {:.6f}".format(bdist_ahist, bdist_ahist-bdist_theory))

###Test 'noiseless' method by binning the values first (makes no sense for a real dataset, just for testing):
cX = np.concatenate((X1,X2))
N_BINS = 20
h1 = np.histogram(X1,bins=N_BINS,range=(min(cX),max(cX)), density=False)[0]
h2 = np.histogram(X2,bins=N_BINS,range=(min(cX),max(cX)), density=False)[0]
fakeX1 = []
fakeX2 = []
for i in range(N_BINS):
    fakeX1 += [i] * h1[i]
    fakeX2 += [i] * h2[i]
bdist_nl = bhatta_dist(fakeX1,fakeX2,method='noiseless')
print("Test 'noiseless':  {:.3f}   Error: {:.6f}".format(bdist_nl, bdist_nl-bdist_theory))


#!/usr/bin/env python3

import numpy as np
import sys,os,time
import math

def rand_double(N):
	return np.random.randint(0,N)/(N)

def random_walk_one_dimension(data, pleft, steps, wks):
	for wk in range(wks):
		x=0
		for ith in range(steps):
			rate=np.random.rand()
			if rate <= pleft:
				x=x+1
			else:
				x=x-1
			data[ith]+=x**2
	for ith in range(steps):
		data[ith]=data[ith]/wks

def random_walk_two_dimension(data, r1, r2, r3, steps, wks):
	for wk in range(wks):
		x=0
		y=0
		for ith in range(steps):
			rate=np.random.rand()
			if rate <= r1:
				x=x+1
			if r1< rate <= r2:
				x=x-1
			if r2<rate <= r3:
				y=y+1
			if rate > r3:
				y=y-1
			data[ith]+=x**2+y**2
	for ith in range(steps):
		data[ith]=data[ith]/wks

def random_walk_three_dimension(data, x1, x2, x3, x4, x5, steps, wks):
	for wk in range(wks):
		x=0
		y=0
		z=0
		for ith in range(steps):
			rate=np.random.rand()
			if 0<rate < x1:
				x=x+1
			if x1<=rate < x2:
				x=x-1
			if x2<=rate < x3:
				y=y+1
			if x3<=rate<x4:
				y=y-1
			if x4<=rate<x5:
				z=z+1
			if rate>=x5:
				z=z-1
			data[ith]+=x**2+y**2+z**2
	for ith in range(steps):
		data[ith]=data[ith]/wks

def random_walk_n_dimension(data, pleft, steps, wks, n):
	for wk in range(wks):
		x=0
		for ith in range(steps):
			rate=np.random.rand()
			if rate <= pleft:
				x=x+1
			else:
				x=x-1
			data[ith]+=x**2
	for ith in range(steps):
		data[ith]=data[ith]/wks

if __name__=="__main__":

	walkers=10000
	steps=100
	#  pleft=0.5
	pleft=float(sys.argv[1])
	#  steps=int(sys.argv[1])
	data=np.zeros(steps,float)
	data2=np.zeros(steps,float)
	data3=np.zeros(steps,float)
	random_walk_one_dimension(data, 0.5, steps, walkers)
	random_walk_two_dimension(data2, 0.25, 0.5, 0.75, steps, walkers)
	random_walk_three_dimension(data3, 1/6, 2/6, 3/6, 4/6, 5/6, steps, walkers)
	with open("rand.txt","w") as out:
		for i in range(steps):
			#  print("{}  {}".format(i, math.sqrt(data[i])))
			out.write("{}  {} {}  {}\n".format(i, data[i], data2[i], data3[i]))


import pandas as pd
import pandas
import numpy as np
#provide local path
testfile='../input/test.csv'
data = open(testfile).readlines()

sequences={}   #(key, value) = (id , sequence)
for i in range(1,len(data)):
    line=data[i]
    line =line.replace('"','')
    line = line[:-1].split(',')
    id = int(line[0])
    sequence=[int(x) for x in line[1:]];
    sequences[id]=sequence

# In[ ]:

def checkRecurrence(seq, order= 2, minlength = 7):
    """
    :type seq: List[int]
    :type order: int
    :type minlength: int
    :rtype: List[int]

    Check whether the input sequence is a recurrence sequence with given order.
    If it is, return the coefficients for the recurrenec relation.
    If not, return None.
    """
    if len(seq)< max((2*order+1), minlength):
        return None

    ################ Set up the system of equations
    A,b = [], []
    for i in range(order):
        A.append(seq[i:i+order])
        b.append(seq[i+order])
    A,b =np.array(A), np.array(b)
    try:
        if np.linalg.det(A)==0:
            return None
    except TypeError:
        return None

    #############  Solve for the coefficients (c0, c1, c2, ...)
    coeffs = np.linalg.inv(A).dot(b)

    ############  Check if the next terms satisfy recurrence relation
    for i in range(2*order, len(seq)):
        predict = np.sum(coeffs*np.array(seq[i-order:i]))
        if abs(predict-seq[i])>10**(-2):
            return None

    return list(coeffs)

def predictNextTerm(seq, coeffs):
    """
    :type seq: List[int]
    :type coeffs: List[int]
    :rtype: int

    Given a sequence and coefficienes, compute the next term for the sequence.
    """

    order = len(coeffs)
    predict = np.sum(coeffs*np.array(seq[-order:]))
    return int(round(predict))

# ## Example: ##
# * Given a sequence [1,5,11,21,39,73,139,269,527].
# * We verify if it's 3rd order recurrence sequence and find the coefficients (2,-5,4).
# * We then predict the next term using the last 3 terms and the relation $a_{n+3} = 2a_{n}-5a_{n+1}+4a_{n+2}$.

# In[ ]:

seq = [1,5,11,21,39,73,139,269,527]
print (checkRecurrence(seq,3))
print (predictNextTerm(seq, [2,-5,4]))

# # Find 2nd order sequeneces in the test set #

# In[ ]:

order2Seq={}   #(key, value) = (sequence id, [prediction, coefficients])
for id in sequences:
    seq = sequences[id]
    coeff = checkRecurrence(seq,2)
    if coeff!=None:
        predict = predictNextTerm(seq, coeff)
        order2Seq[id]=(predict,coeff)

print ("We found %d sequences\n" %len(order2Seq))

print  ("Some examples\n")
print ("ID,  Prediction,  Coefficients")
for key in sorted(order2Seq)[0:5]:
    value = order2Seq[key]
    print ("%s, %s, %s" %(key, value[0], [int(round(x)) for x in value[1]]))

# # Find 3rd order sequeneces in the test set #

# In[ ]:

order3Seq={}
for id in sequences:
    if id in order2Seq:
        continue
    seq = sequences[id]
    coeff = checkRecurrence(seq,3)
    if coeff!=None:
        predict = predictNextTerm(seq, coeff)
        order3Seq[id]=(predict,coeff)

print ("We found %d sequences\n" %len(order3Seq))

print  ("Some examples\n")
print ("ID,  Prediction,  Coefficients")
for key in sorted(order3Seq)[0:5]:
    value = order3Seq[key]
    print ("%s, %s, %s" %(key, value[0], [int(round(x)) for x in value[1]]))

# # Find 4th order sequeneces in the test set #

# In[ ]:

order4Seq={}
for id in sequences:
    if id in order2Seq or id in order3Seq:
        continue
    seq = sequences[id]
    coeff = checkRecurrence(seq,4)
    if coeff!=None:
        predict = predictNextTerm(seq, coeff)
        order4Seq[id]=(predict,coeff)

print ("We found %d sequences \n" %len(order4Seq))
print  ("Some examples\n")
print ("ID,  Prediction,  Coefficients")
for key in sorted(order4Seq)[4:5]:
    value = order4Seq[key]
    print ("%s, %s, %s" %(key, value[0], [int(round(x)) for x in value[1]]))

print (sequences[239][0:17])

# ## Recurrence relations not included in OEIS ##
# In the previous cells,
#     * We find that Sequence 239 is a 4th order sequence and predict the next term as 5662052980.
#     * We check OEIS https://oeis.org/A000773, which confirms the prediction is correct.
#     * We observe that this recurrence relation is not described in OEIS. (There are more such sequences.)

# In[ ]:

print("Conclusion:")
print("Number of sequences in the test set:", len(sequences))
print("Number of 2nd order sequences:", len(order2Seq))
print("Number of 3rd order sequences:", len(order3Seq))
print("Number of 4th order sequences:", len(order4Seq))


import numpy as np

import scipy
from scipy.stats import pearsonr
from scipy.spatial.distance import pdist, squareform

import sklearn
from sklearn.linear_model import LinearRegression

def pcorr(x1,x2,y):
    """
    Computes the (signed square-root of) proportion of variance (r^2) in y that is uniquely explained
    by x1, uniquely explained by x2, or simultaneously explained by x1 and x2
    (intersection). These signed r values are returned respectively in
    unique_x1_r, unique_x2_r and inter_r.
    In terms of a venn diagram with 3 circles representing x1, x2 and y, the 3
    outputs represent the 3 separate regions of overlap with y.
    """
    x1x2   = np.concatenate([x1, x2], axis=1)
    lm1    = LinearRegression().fit(x1, y)
    lm2    = LinearRegression().fit(x2, y)
    lmboth = LinearRegression().fit(x1x2, y)

    x1_r2    = lm1.score(x1,y)
    x2_r2    = lm2.score(x2,y)
    union_r2 = lmboth.score(x1x2,y)
    inter_r2 = max(x1_r2 + x2_r2 - union_r2, 0)

    unique_x1_r2 = x1_r2 - inter_r2
    unique_x2_r2 = x2_r2 - inter_r2

    inter_r     = np.sqrt(inter_r2)
    unique_x1_r = np.sqrt(np.maximum(0, unique_x1_r2)) * np.sign(lmboth.coef_[0])
    unique_x2_r = np.sqrt(np.maximum(0, unique_x2_r2)) * np.sign(lmboth.coef_[1])

    # if np.count_nonzero(np.sign(lmboth.coef_[0]) != np.sign(lmboth.coef_[1])) != 0:
    #     print("Unequal sign")
    #     raise Exception("Unequal sign")

    return unique_x1_r, unique_x2_r, inter_r

def get_rdm(vectors, distance='correlation'):
    """
    Computes the rdm of the given vectors using the specified distance.
    """
    rdm = pdist(vectors, distance)
    if not np.any(np.isnan(rdm)):
        return pdist(vectors, distance)
    else:
        raise ValueError('Found some NaNs in the RDM')

def calculate_noise_ceilings(all_rdms,corr_func=pearsonr):
    """
    Calcuilate the lower noise ceilings for each subject.
    """
    total_nums = len(all_rdsm)

    all_corrs = []
    for each_sub in range(1,total_nums+1):
        sub_rdm = all_rdms[each_sub-1]
        other_rdms = [all_rdms[x-1] for x in range(1,total_nums+1) if x!=each_sub]
        assert len(other_rdms)== (total_nums - 1)

        all_corrs.append(np.array([corr_func(x,sub_rdm)[0] for x in other_rdms]).mean())

    assert len(all_corrs) == total_nums
    upper_ceiling = np.array(all_corrs)

    # mean = upper_ceiling.mean()
    # sem = scipy.stats.sem(upper_ceiling)
    # return mean,sem

    return upper_ceiling


import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('messi5.jpg',0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20*np.log(np.abs(fshift))

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

img = cv2.imread('messi5.jpg',0)
rows, cols = img.shape
crow,ccol = rows/2 , cols/2
fshift[crow-30:crow+30, ccol-30:ccol+30] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)

plt.subplot(131),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(img_back, cmap = 'gray')
plt.title('Image after HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])

plt.show()

# Fourier Transform in OpenCV
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('messi5.jpg',0)

dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

rows, cols = img.shape
crow,ccol = rows/2 , cols/2

# create a mask first, center square is 1, remaining all zeros
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1

# apply mask and inverse DFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])

plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

OpenCV Python/4. Image Processing/11. Image Transforms/fourier-transform.py


import scipy
import numpy
import matplotlib.pyplot as plt
import sympy
#puntos a interpolar
A= numpy.array([-3, -1 , 2, 4])
B= numpy.array([0, 4, 3, 1])

#array de polinomios
#polinomio = numpy.array([0, 0])
x = sympy.symbols("x")
##### lagrange en x , y
def base_coef(j , pts, A ):
        alfa = 1
        for i in range(pts):
            if i is not j:
                alfa = alfa * ( (x- A[i]) / (A[j]-A[i])   )
        return alfa
def Lagrange():
    polinomio = 0
    for r in range(4):
        polinomio = polinomio + base_coef(r,4,A)*B[r]
    return polinomio

poly=Lagrange()
polisimple = sympy.expand(poly)
print(poly)
print(polisimple)
fx = sympy.lambdify(x,poly) # funcion
muestras = 100
a = numpy.min(A)
b = numpy.max(A)
p_xi = numpy.linspace(a,b,muestras)

pfi = fx(p_xi)
#grafica
plt.plot(A,B,'o')
plt.plot(p_xi,pfi)
plt.show()
"""

#funcion de mezcla
 i es el numero de  funcion de corte
    pts es la cantidad de puntos
def mezcla(i , pts ):
        t = sympy.symbols("t")
        alfa = 1
        for k in range(pts):
            if i is not k:
                alfa = alfa * (t-k)/(i-k)
        print(type(alfa))
        return alfa

#calcular el polinomio
for r in range(2):
    for j in range(tamaño_fila):
        alf = mezcla(j,tamaño_fila)  # alf es simbolo se puede
        polinomio[:,r] = alf* x[:,j] +  polinomio[]
print(polinomio[0])
"""


from pathlib import Path
from typing import List, Tuple

import numpy as np
import scipy.io
import tensorly as tl
from tensorly.decomposition import tucker

from tucker.tucker_als import TuckerALS

def compute_relative_error(X: np.ndarray, X_hat: np.ndarray) -> float:
    """Computes the relative Tucker error using the true and approximated tensor

    Parameters
    ----------
    X : np.ndarray
        True tensor
    X_hat : np.ndarray
        Approximated tensor from Tucker decomposition

    Returns
    -------
    float
        Relative error
    """
    signal = 0.0
    error = 0.0

    for k in range(X.shape[2]):
        signal += np.linalg.norm(X[..., k], ord="fro") ** 2
        error += np.linalg.norm(X[..., k] - X_hat[..., k], ord="fro") ** 2

    return error / signal

def compute_tensor_approximation(
    U: np.ndarray,
    V: np.ndarray,
    W: np.ndarray,
    G0: np.ndarray,
    orig_shape: Tuple[int, ...],
) -> np.ndarray:

    X0_hat = U @ G0 @ np.kron(V, W).T
    return tl.fold(X0_hat, 0, orig_shape)

def face_data_comparison(p: Path):
    X: np.ndarray = scipy.io.loadmat(p / "FERETC80A45.mat")["fea2D"] / 255.0

    # Get the Tucker approximation using my version of the ALS algorithm
    t_als = TuckerALS(X, (5, 5, 15))
    U, V, W, G0 = t_als.fit()
    X_hat = compute_tensor_approximation(U, V, W, G0, X.shape)
    relative_error = compute_relative_error(X, X_hat)
    print(f"Faces relative error (my implementation): {relative_error}")

    # Get the TensorLy Tucker approximation for comparison
    G, factors = tucker(X, rank=[5, 5, 15])
    G0 = tl.unfold(G, mode=0)
    X_hat = compute_tensor_approximation(
        factors[0], factors[1], factors[2], G0, X.shape
    )

    relative_error = compute_relative_error(X, X_hat)
    print(f"Faces relative error (TensorLy version): {relative_error}")

def generate_rank_options(num_choices: int, rng) -> List[Tuple[int, ...]]:
    # Assumes 3-way tensors
    r0 = rng.integers(low=1, high=15, size=num_choices)
    r1 = rng.integers(low=2, high=4, size=num_choices)
    r2 = rng.integers(low=2, high=4, size=num_choices)
    return list(zip(r0, r1, r2))

def compute_compression_ratio(
    tensor_shape: Tuple[int, ...], ranks: Tuple[int, ...]
) -> float:

    i, j, k = tensor_shape
    r0, r1, r2 = ranks
    return (i * j * k) / ((i * r0) + (j * r1) + (k * r2) + (r0 * r1 * r2))

def run_own_data_compression_experiment(X: np.ndarray, ranks: Tuple[int, ...]) -> None:
    t_als = TuckerALS(X, ranks, max_iter=200)
    U, V, W, G0 = t_als.fit()
    X_hat = compute_tensor_approximation(U, V, W, G0, X.shape)
    relative_error = compute_relative_error(X, X_hat)
    compression_ratio = compute_compression_ratio(X.shape, ranks)

    msg = f"Rank configuration: {ranks}; Relative Error: {relative_error}; Compression Ratio: {compression_ratio}"
    print(msg)

def compute_svd_approx_matrix_diff(X: np.ndarray, mode: int, rank: int) -> np.ndarray:
    X_mode = tl.unfold(X, mode)
    U, S, Vt = np.linalg.svd(X_mode)

    # Subset the factors based on the rank
    U = U[:, :rank]
    S = np.diag(S[:rank])
    Vt = Vt[:rank, :]

    X_hat = U @ S @ Vt
    return X_mode - X_hat

def compute_svd_error(X: np.ndarray, X_diff: np.ndarray) -> float:
    # First have to compute the "signal" for the original tensor
    signal = 0.0
    for k in range(X.shape[-1]):
        signal += np.linalg.norm(X[..., k], ord="fro") ** 2

    # Next compute the approximation error from the SVD for the unfolding
    error = np.linalg.norm(X_diff, ord="fro") ** 2
    return error / signal

def main():
    p = Path("data")

    face_data_comparison(p)

    # Personal data compression experiment; I'll try various random rank
    # tuples and log the results
    X: np.ndarray = np.load(p / "data.npy")
    run_own_data_compression_experiment(X, (5, 2, 3))

    """
    To now do the SVD approximation we have three unfoldings: X0 = (2000 x (4*10)),
    X1 = (4 x (2000*10)), and X2 = (10 x (2000*4)). Using the Tucker model,
    we're representing the tensor with
    (5 * 2000) + (1 * 4) + (2 * 10) + (5 * 1 * 2) = 10,068 floats.
    So for the three unfoldings we need, X0: 2,040 * r -> r = 5;
    X1: 20,004 * r -> r = 1; X2: 8,010 * r -> r = 2 as the closest approximations
    at the given compression ratio
    """
    # X0 SVD approximation
    X_diff = compute_svd_approx_matrix_diff(X, 0, rank=5)
    relative_error = compute_svd_error(X, X_diff)
    print(f"Mode 0 Relative Error {relative_error}")

    # X1 SVD approximation
    X_diff = compute_svd_approx_matrix_diff(X, 1, rank=1)
    relative_error = compute_svd_error(X, X_diff)
    print(f"Mode 1 Relative Error {relative_error}")

    # X2 SVD approximation
    X_diff = compute_svd_approx_matrix_diff(X, 2, rank=2)
    relative_error = compute_svd_error(X, X_diff)
    print(f"Mode 2 Relative Error: {relative_error}")

    return None

if __name__ == "__main__":
    main()


"""Constraints by half-spaces."""
import numpy as np

from optimus.types import Constraint

class HalfSpace(Constraint):
    """Constraint based on a linear inequality."""

    def __init__(self, vector: np.ndarray, threshold: float):
        """Create consrtaint from the defining vector and threshold.

        This defines the inequality vector.dot(point) <= threshold."""
        self.vector = vector
        self.threshold = threshold

    def project(self, point: np.ndarray) -> np.ndarray:
        dot_product_value = self.vector.dot(point)
        if dot_product_value <= self.threshold:
            return point
        return point - self.vector * (
            dot_product_value - self.threshold
        ) / np.linalg.norm(self.vector)


# SPDX-License-Identifier: MIT
# Copyright (c) 2021 ETH Zurich, Luc Grosheintz-Laval

import numpy as np
import scipy.integrate

from morinth.quadrature import GaussLegendre
from morinth.grid import Grid

def sinusoidal(x):
    x = (x - 10.0)/100.0
    fx = np.sin(2.0*np.pi*x) * np.cos(2.0*np.pi*x)**2
    return fx

def quadrature_error(n_cells, n_points):
    grid = Grid([10.0, 100.0], n_cells, 1)
    integrate = GaussLegendre(n_points)

    fbar = integrate(grid.edges, sinusoidal)
    fref = np.empty(n_cells)

    for i in range(n_cells):
        fref[i], err = scipy.integrate.quadrature(sinusoidal, grid.edges[i], grid.edges[i+1],
                                                  tol = 1e-10, rtol=1e-10)

    fref *= 1.0/grid.dx

    return np.max(np.abs(fbar - fref))

def quadrature_rate(n_points):
    resolutions = np.array([10, 20, 50, 100, 200, 500])
    errors = np.array([quadrature_error(res, n_points) for res in np.nditer(resolutions)])
    rate = np.max(np.abs(np.log(errors[1:]/errors[:-1])/np.log(resolutions[1:]/resolutions[:-1])))

    return rate

def test_quadrature_vs_scipy():
    for n_points in range(1,6):
        empirical = quadrature_rate(n_points)
        order = 2.0*n_points
        assert empirical > order - 0.1, "{:.1f} vs. {:.1f}".format(empirical, float(order))


import numpy as np

# sigmoid function
def nonlin(x, deriv=False):
    if(deriv==True):
        return x*(1-x)
    return 1/(1+np.exp(-x))

# input dataset
X = np.array([  [0,0,1],
                [0,1,1],
                [1,0,1],
                [1,1,1] ])                  # 4x3

# output dataset
y = np.array([[0,0,1,1]]).T                 # 4x1

# seed random numbers to make calculation
# deterministic (just a good practice)
np.random.seed(1)

# initialize weights randomly with mean 0
syn0 = 2*np.random.random((3,1)) - 1        # 3x1 ranging [-1,1)

for iter in xrange(10000):

    # forward propagation
    l0 = X
    l1 = nonlin(np.dot(l0,syn0))            # 4x1

    # how much did we miss?
    l1_error = y - l1

    # element wise multiply how much we missed by the
    # slope of the sigmoid at the values in l1
    l1_delta = l1_error * nonlin(l1,True)   # 4x1

    # update weights with backpropagation
    syn0 += np.dot(l0.T,l1_delta)           # 3x1

print "Output After Training: \n", l1
print "Weight values: \n", syn0

'''
Output After Training:
[[0.00966449]
 [0.00786506]
 [0.99358898]
 [0.99211957]]
Weight values:
[[ 9.67299303]
 [-0.2078435 ]
 [-4.62963669]]
'''


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

# var.py

from __future__ import print_function

import datetime

import numpy as np
import pandas.io.data as web
from scipy.stats import norm

def var_cov_var(P, c, mu, sigma):
    """
    Variance-Covariance calculation of daily Value-at-Risk
    using confidence level c, with mean of returns mu
    and standard deviation of returns sigma, on a portfolio
    of value P.
    """
    alpha = norm.ppf(1-c, mu, sigma)
    return P - P*(alpha + 1)

if __name__ == "__main__":
    start = datetime.datetime(2010, 1, 1)
    end = datetime.datetime(2014, 1, 1)

    citi = web.DataReader("C", 'yahoo', start, end)
    citi["rets"] = citi["Adj Close"].pct_change()

    P = 1e6   # 1,000,000 USD
    c = 0.99  # 99% confidence interval
    mu = np.mean(citi["rets"])
    sigma = np.std(citi["rets"])

    var = var_cov_var(P, c, mu, sigma)
    print("Value-at-Risk: $%0.2f" % var)


import numpy as np
import matplotlib.pyplot as plt

#class Orthogonal_Polinomyals:

def Hermite(x2, n):

	if n==0:

		return np.ones(len(x2))

	elif n==1:

		return 2*x2

	else:

		return 2*x2*Hermite(x2,n-1)-2*(n-1)*Hermite(x2,n-2)

def Legendre(x, n):

	if n == 0:

		return np.ones(len(x))

	elif n == 1:

		return x

	else:

		return  (( x*(1+ 2*(n-1))*Legendre(x, n - 1) - (n-1)*Legendre(x, n - 2) )/(n))

x = np.linspace(-1,1,1001)

for i in range(6):

	plt.plot(x, Legendre(x, i), label = f"$P_{i}$")

plt.title("Polinomios de Legendre")
plt.grid() ; plt.legend()
plt.show()


from pyomo.environ import *

model = ConcreteModel()

model.y = Var([1,2,3], within=Binary)
model.z = Var([1,2,3], within=NonNegativeReals)

model.obj = Objective(expr=12*model.y[1]+20*model.y[2]+15*model.y[3]-8*model.z[1]-6*model.z[2]+2*model.z[3], sense=minimize)

model.constraint1 = Constraint(expr=10*model.y[1]+8*model.y[2]-2*model.z[1]-model.z[2]+model.z[3]>=4)
model.constraint2 = Constraint(expr=5*model.y[1]+8*model.y[3]-model.z[1]-model.z[2]-model.z[3]>=3)
model.constraint3 = Constraint(expr=model.y[1]+model.y[2]+model.y[3]>=1)

### SOLVE
solver_manager = SolverFactory('glpk')

results = solver_manager.solve(model)

print(model.display())


# -*- coding: utf-8 -*-
#Example of numerical integration with Gauss-Legendre quadrature
#Translated to Python by Kyrre Ness Sjøbæk

import sys
import numpy
from computationalLib import pylib

#Read input
if len(sys.argv) == 1:
    print "Number of integration points:"
    n = int(sys.stdin.readline())
    print "Integration limits (a, b)"
    (a,b) = sys.stdin.readline().split(",")
    a = int(a)
    b = int(b)
elif len(sys.argv) == 4:
    n = int(sys.argv[1])
    a = int(sys.argv[2])
    b = int(sys.argv[3])
else:
    print "Usage: python", sys.argv[0], "N a b"
    sys.exit(0)

#Definitions
m = pylib(inputcheck=False,cpp=False)

def integrand(x):
    return 4./(1. + x*x)

#Integrate with Gaus-legendre!
(x,w) = m.gausLegendre(a,b,n)
int_gaus = numpy.sum(w*integrand(x))

#Final output
print "integration of f(x) 4/(1+x**2) from",a,"to",b,"with",n,"meshpoints:"
print "Gaus-legendre:", int_gaus
print "Trapezoidal:  ", m.trapezoidal(a,b,n, integrand)
print "Simpson:      ", m.simpson(a,b,n,integrand)


"""Collection of norm functions i..e, ways to measure the norm of a input- (or explanation) vector."""
import numpy as np

def fro_norm(a: np.array) -> float:
    """Calculate Froberius norm for an array."""
    assert a.ndim == 1, "Check that 'fro_norm' receives a 1D array."
    return np.linalg.norm(a)

def l2_norm(a: np.array) -> float:
    """Calculate L2-norm for an array."""
    assert a.ndim == 1, "Check that 'l2_norm' receives a 1D array."
    return np.linalg.norm(a)

def linf_norm(a: np.array) -> float:
    """Calculate L inf-norm for an array."""
    assert a.ndim == 1, "Check that 'linf_norm' receives a 1D array."
    return np.linalg.norm(a, ord=np.inf)


import pdb
import numpy as np

def produto_interno_canonico(u, v):
    resultado = 0
    for i in range(len(u)):
        resultado += u[i] * v[i]
    return resultado

def produto_interno(u, v, funcional_linear = produto_interno_canonico):
    return funcional_linear(u, v)

def norma(v, funcional_linear = produto_interno_canonico):
    return funcional_linear(v, v) ** (0.5)

def angulo(u, v, funcional_linear = produto_interno_canonico):
    return np.arccos(np.around(funcional_linear(u, v) / (norma(u, funcional_linear = funcional_linear) * norma(v, funcional_linear = funcional_linear)), decimals = 6))

def projecao_escalar(u, v, funcional_linear = produto_interno_canonico):
    return np.around(produto_interno(u, v, funcional_linear = funcional_linear)\
         / produto_interno(v, v, funcional_linear = funcional_linear), decimals = 6)

def projecao_vetorial(u, v, funcional_linear = produto_interno_canonico):
    return projecao_escalar(u, v, funcional_linear = funcional_linear) * v

def v_ortogonal_u(u, v, funcional_linear = produto_interno_canonico):
    return v - projecao_escalar(v, u, funcional_linear = funcional_linear) * u

def normalizar(u, funcional_linear = produto_interno_canonico):
    return u / norma(u, funcional_linear = funcional_linear)

def gram_schmidt(i, vetores, funcional_linear = produto_interno_canonico):
    w = [vetores[0]]
    for j in range(1, i + 1):
        v = vetores[j]
        w += [v - sum([projecao_vetorial(v, w[k], funcional_linear = funcional_linear) for k in range(j)])]
    return w[i]

def ortogonalizar_vetores(vetores, funcional_linear = produto_interno_canonico):
    ortogonalizados = []
    for i in range(len(vetores)):
        ortogonalizados += [gram_schmidt(i, vetores, funcional_linear = funcional_linear)]
    return ortogonalizados

def normalizar_vetores(vetores, funcional_linear = produto_interno_canonico):
    normalizados = []
    for v in vetores:
        normalizados += [normalizar(v, funcional_linear = funcional_linear)]
    return normalizados

def ortonormalizar_vetores(vetores, funcional_linear = produto_interno_canonico):
    return normalizar_vetores(ortogonalizar_vetores(vetores, funcional_linear = funcional_linear), funcional_linear = funcional_linear)


"""
Author: Drishika Nadella
Date: 20th April 2021
Time: 16:09PM
Email: drishikanadella@gmail.com
"""

import numpy as np
import matplotlib.pyplot as plt

# Creating a list with the zeroth Catalan number
C = [1]
# Creating the counter
i = 0

# Calculating and printing Catalan numbers upto 1 billion
while C[-1] <= 10**9:
    print(C[-1])
    C.append((4*i + 2)/(i+2)*C[i])
    i+=1

# Plotting the Catalan numbers
plt.plot(range(len(C)), C, marker='o')
plt.title("Visualizing the Catalan numbers upto 1 billion")
plt.show()


# based on https://sebastianraschka.com/Articles/2014_kernel_pca.html

from __future__ import annotations
from typing import Union
import numpy as np
from scipy.spatial.distance import pdist, squareform
from scipy.linalg import eigh

class KernelPCA:
    """KernelPCA
    Parameters:
    -----------
    n_components: int = 2
        Number of components to keep.
    gamma: float = None
        Kernel coefficient
    """
    def __init__(self, n_components: int = 2, gamma: float = None):
        self.n_components = n_components
        self.gamma = gamma
        self.alphas = None
        self.lambdas = None
        self.X = None

    def fit(self, X: Union[list, np.ndarray]) -> KernelPCA:
        if self.gamma == None:
            self.gamma = 1 / X.shape[1]

        sq_dists = pdist(X, 'sqeuclidean')

        mat_sq_dists = squareform(sq_dists)

        K = np.exp(-self.gamma * mat_sq_dists)

        N = K.shape[0]
        one_n = np.ones((N,N)) / N
        K_norm = K - one_n.dot(K) - K.dot(one_n) + one_n.dot(K).dot(one_n)

        eigenvalues, eigenvectors = eigh(K_norm)

        alphas = np.column_stack((eigenvectors[:,-i] for i in range(1, self.n_components+1)))
        lambdas = [eigenvalues[-i] for i in range(1, self.n_components+1)]

        self.alphas = alphas
        self.lambdas = lambdas
        self.X = X

        return self

    def fit_transform(self, X: Union[list, np.ndarray]) -> np.ndarray:
        self.fit(X)
        return self.alphas * np.sqrt(self.lambdas)

    def transform(self, X: Union[list, np.ndarray]) -> np.ndarray:
        # TODO: Rewrite as this is very inefficient
        def transform_row(X_r):
            pair_dist = np.array([np.sum((X_r-row)**2) for row in self.X])
            k = np.exp(-self.gamma * pair_dist)
            return k.dot(self.alphas / self.lambdas)

        return np.array(list(map(transform_row, X)))

if __name__ == '__main__':
    from sklearn.datasets import make_circles
    import matplotlib.pyplot as plt

    X, y = make_circles(n_samples=1000, random_state=123, noise=0.1, factor=0.2)

    plt.figure(figsize=(8,6))

    pca = KernelPCA(n_components=3)
    pca.fit(X)
    X = pca.transform(X)

    print(X)


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

# lets define the van der pol oscillator!
# d^2x/tx^2 - mu(1 - x^2)*dx/dt + x = 0
# d^2x/tx^2 = mu(1 - x^2)*dx/dt - x
# a = dv/dt = d^2x/dt^2 = mu*(1 - x^2)*dx/dt - x = mu*(1 - x^2)v - x
# dx/dt = v
# dv/dt = mu*(1 - x^2)v - x

def get_vdp_accel(x, v):
    return mu*(1 - x**2)*v - x

def vdp_rhs(state, t, mu):
    x, v = state[0], state[1]
    a = get_vdp_accel(x, v)
    return np.array([v, a])

# for mu = 0 we get the harmonic oscillator for k = 1
# d^2x/tx^2 - 0(1 - x^2)*dx/dt + x = 0
# d^2x/tx^2 + x = 0
# -d^2x/tx^2 = x

# let's simulate the system
x0 = 2.0
v0 = 0.0
init_state = np.array([x0, v0])

T = 100.0
steps = 1000
ts = np.linspace(0, T, steps)

mu = 5.0

hist = odeint(vdp_rhs, init_state, ts, args=(mu,))
xs, vs = hist[:, 0], hist[:, 1]

# display the evolution of the system over time
plt.subplot(2,1,1)
plt.title("Van der Pol oscillator over time for mu={}".format(mu))
plt.plot(ts, xs)
plt.ylabel("x")
plt.subplot(2,1,2)
plt.xlabel("t")
plt.ylabel("dx/dt")
plt.plot(ts, vs)

# make a direction field [dx/dt, d^2x/dt^2]
res = 20
x = np.linspace(min(xs), max(xs), res)
v = np.linspace(min(vs), max(vs), res)
xx, vv = np.meshgrid(x, v)
aa = get_vdp_accel(xx, vv)
plt.title("Direction field of the van der pol oscillator with mu={}".format(mu))
plt.quiver(xx, vv, vv, aa)
plt.xlabel("x")
plt.ylabel("dx/dt")
plt.plot(xs, vs, label="from [{}, {}]".format(x0, v0))
plt.legend()
plt.grid()

# how does adding an epsilon value effect the system?
mu = 15.0
hist = odeint(vdp_rhs, init_state, ts, args=(mu,))
xs, vs = hist[:, 0], hist[:, 1]
eps = 1e-3
init_state_eps = np.array([x0 + eps, v0 + eps])
res_eps = odeint(vdp_rhs, init_state_eps, ts, args=(mu,))
xs_eps, vs_eps = res_eps[:, 0], res_eps[:, 1]
x_err = xs - xs_eps
v_err = vs - vs_eps
plt.subplot(2,1,1)
plt.title("Error evolution in the van der pol oscillator")
plt.plot(ts, x_err)
plt.ylabel("x - x'")
plt.subplot(2,1,2)
plt.plot(ts, v_err)
plt.ylabel("v - v'")
plt.xlabel("t")
# ok, we have some kind of oscillation going on
plt.plot(ts, xs)
plt.plot(ts, xs_eps) # almost equal
plt.show()


import numpy as np
import numpy.random as npr
import matplotlib.pyplot as plt
# 比较模拟结果的分布特性.
import scipy.stats as scs
# 字体
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['SimHei']

def print_statistics(a1, a2):
    """
    Prints selected statistics.

    Parameters
    ==========
    al, a2 : ndarray objects
    results object from simulation
    """
    # 主体
    sta1 = scs.describe(a1)
    sta2 = scs.describe(a2)
    print("%14s %14s %14s"%("statistic", "data set 1", "data set 2"))
    print("-" * 45)
    print("%14s %14.3f %14.3f"%("size", sta1[0], sta2[0]))
    print("%14s %14.3f %14.3f"%("min", sta1[1][0], sta2[1][0]))
    print("%14s %14.3f %14.3f"%("max", sta1[1][1], sta2[1][1]))
    print("%14s %14.3f %14.3f"%("mean", sta1[2], sta2[2]))
    print("%14s %14.3f %14.3f"%("std", np.sqrt(sta1[3]), np.sqrt(sta2[3])))
    print("%14s %14.3f %14.3f"%("skew", sta1[4], sta2[4]))
    print("%14s %14.3f %14.3f"%("kurtosis", sta1[5], sta2[5]))

S0    = 100    # initial value
r     = 0.05   # constant short rate
sigma = 0.25   # constant volatility
T     = 2.0    # in years
I     = 10000  # number of random draws

ST1   = S0 * np.exp(
            (r-sigma**2/2)*T + sigma*np.sqrt(T)*npr.standard_normal(I)
        )
ST2   = S0 * npr.lognormal(mean  = (r-sigma**2/2)*T,
                           sigma = sigma*np.sqrt(T),
                           size  = I)
print_statistics(ST1, ST2)

"""
plt.hist(ST1, bins=50)
plt.xlabel("index level")
plt.ylabel("frequency")
plt.grid(True)
plt.show()
"""

# 几何布朗运动
S0    = 100    # initial value
r     = 0.05   # constant short rate
sigma = 0.25   # constant volatility

M  = 50
T  = 2.0
I  = 10000
dt = T / M
S  = np.zeros((M+1, I))
S[0] = S0
for t in range(1, M+1):
    S[t] = S[t-1] * npr.lognormal(mean  = (r-sigma**2/2)*dt,
                                 sigma = sigma*np.sqrt(dt),
                                 size  = I)

plt.plot(S[:, :10], lw=1.5)
plt.title("模拟几何布朗运动路径")
plt.xlabel("time")
plt.ylabel("index level")
plt.grid(True)
plt.show()

# 平方根扩散(欧拉格式)
x0    = 0.05
kappa = 3.0
theta = 0.02
sigma = 0.1

M  = 50
T  = 2.0
I  = 10000
dt = T / M
x  = np.zeros((M+1, I))
x[0] = x0
clamp = lambda x: np.maximum(x, 0)
for t in range(1, M+1):
    x[t] = x[t-1] + kappa*(theta-clamp(x[t-1]))*dt \
         + sigma*np.sqrt(clamp(x[t-1]))*np.sqrt(dt)*npr.standard_normal(I)
x = clamp(x)

plt.plot(x[:, :10], lw=1.5)
plt.title("模拟平方根扩散路径(欧拉格式)")
plt.xlabel("time")
plt.ylabel("index level")
plt.grid(True)
plt.show()

# 平方根扩散(精确格式)
x0    = 0.05
kappa = 3.0
theta = 0.02
sigma = 0.1

M  = 50
T  = 2.0
I  = 10000
dt = T / M
y  = np.zeros((M+1, I))
y[0] = x0
for t in range(1, M+1):
    df = 4*theta*kappa / sigma**2
    c  = (sigma**2 * (1-np.exp(-kappa*dt))) / (4*kappa)
    nc = np.exp(-kappa*dt) * y[t-1] / c
    y[t] = c * npr.noncentral_chisquare(df, nc, size=I)

plt.plot(y[:, :10], lw=1.5)
plt.title("模拟平方根扩散路径(精确格式)")
plt.xlabel("time")
plt.ylabel("index level")
plt.grid(True)
plt.show()

# 比较欧拉格式与精确格式
print_statistics(x[-1], y[-1])

# 随机波动率
r     = 0.05
v0    = 0.1
kappa = 3.0
theta = 0.25
sigma = 0.1
rho   = 0.6

M  = 50
T  = 2.0
I  = 10000
dt = T / M
v  = np.zeros((M+1, I))
v[0] = v0

ran_num = npr.standard_normal((2, M+1, I))
corr_mat = np.array([[1.0,rho], [rho,1.0]])
cho_mat  = np.linalg.cholesky(corr_mat)
clamp    = lambda x: np.maximum(x, 0)
for t in range(1, M+1):
    ran = np.dot(cho_mat, ran_num[:,t,:])
    v[t] = v[t-1] + kappa*(theta-clamp(v[t-1]))*dt \
         + sigma*np.sqrt(clamp(v[t-1]))*np.sqrt(dt)*ran[1]
v = clamp(v)

w0 = 100
w  = np.zeros((M+1, I))
w[0] = w0
for t in range(1, M+1):
    ran = np.dot(cho_mat, ran_num[:,t,:])
    w[t] = w[t-1] * np.exp((r-v[t])*dt +
                           np.sqrt(v[t])*np.sqrt(dt)*ran[0])

fig,(ax1,ax2) = plt.subplots(2, 1, sharex=True, figsize=(7,6))
ax1.plot(w[:,:10], lw=1.5)
ax1.set_title("模拟随机波动率模型路径")
ax1.set_ylabel("index level")
ax1.grid(True)
ax2.plot(v[:,:10], lw=1.5)
ax2.set_xlabel("time")
ax2.set_ylabel("volatility")
ax2.grid(True)

fig.show()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-12-17 20:22:27
# @Author  : Zhi Liu (zhiliu.mind@gmail.com)
# @Link    : http://blog.csdn.net/enjoyyl
# @Version : $1.0$

import random
import numpy as np

def circle1(x0, y0, r, nps):
    r"""Generates circle (x, y)

    :math:`(x-x_0)^2 + (y-y_0)^2 = r^2`

    Arguments:
       x0 {Float} -- x coordinate of circle center
       y0 {Float} -- y coordinate of circle center
       r {Float} -- radius of circle
       nps {Integer} -- number of points
    """

    xmin = x0 - r
    xmax = x0 + r

    x = np.linspace(xmin, xmax, nps)
    y1 = y0 - np.sqrt(r * r - (x - x0) ** 2)
    y2 = y0 + np.sqrt(r * r - (x - x0) ** 2)

    x = np.concatenate((x, x), axis=0)
    y = np.concatenate((y1, y2), axis=0)
    return x, y

def circle2(x0, y0, r):
    r"""Generates circle (x, y)

    :math:`(x-x_0)^2 + (y-y_0)^2 = r^2`

    Arguments:
       x0 {Float} -- x coordinate of circle center
       y0 {Float} -- y coordinate of circle center
       r {Float} -- radius of circle
       nps {Integer} -- number of points
    """

    angles_circle = [i * np.pi / 180 for i in range(0, 360)]
    x = x0 + r * np.cos(angles_circle)
    y = y0 + r * np.sin(angles_circle)

    return x, y

def ellipse(a, b):
    """[summary]

    [description]

    Arguments:
       a {[type]} -- [description]
       b {[type]} -- [description]
    """
    pass

def ellipse_surface(a, b, x0, y0, nps, mod):
    """generates ellipse surface

    :math:`\frac{(x-x_0)^2}{a^2} + \frac{{y-y_0}^2}{b^2} \ngt 1`

    Arguments:
       a {float} -- axis of ellipse
       b {float} -- axis of ellipse
       x0 {float} -- x center of ellipse
       y0 {float} -- y center of ellipse
       nps {Integer} -- number of points
       mod {float} -- mode: 'rand' or 'order'
    """

    if mod is None:
        mod = 'order'

    aa = a ** 2
    bb = b ** 2

    xmin = x0 - a
    xmax = x0 + a
    ymin = y0 - b
    ymax = y0 + b

    x = []
    y = []

    if mod is 'order':
        xx = np.linspace(xmin, xmax, nps)
        yy = np.linspace(ymin, ymax, nps)
        for xi in xx:
            for yj in yy:
                if (xi - x0) ** 2 / aa + (yj - y0) ** 2 / bb <= 1:
                    x.append(xi)
                    y.append(yj)

    cnt = 0
    if mod is 'rand':
        for i in range(0, 10 * nps):
            print(i)
            xi = (xmax - xmin) * np.random.rand(1) + xmin
            yi = (ymax - ymin) * np.random.rand(1) + ymin
            if (xi - x0) ** 2 / aa + (yi - y0) ** 2 / bb <= 1:
                x.append(xi)
                y.append(yi)
            if cnt < nps:
                cnt = cnt + 1
            else:
                break
    return x, y

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

    x, y = circle1(0, 0, 1, 1000)
    plt.figure()
    plt.plot(x, y, '.')
    plt.title('circle')
    plt.show()

    x, y = circle2(0, 0, 1)
    plt.figure()
    plt.plot(x, y, '.r')
    plt.title('circle')
    plt.show()

    x, y = ellipse_surface(8, 2, 1, 1, 1000, 'rand')
    plt.figure()
    plt.scatter(x, y, c='g', marker='+')
    plt.axis('equal')
    plt.title('ellipse surface')
    plt.show()


import typing

import matplotlib.pyplot as plt
import numpy as np
from numpy.polynomial.polynomial import Polynomial

def pi_n(alpha: float, n: int):
    """
    Вычисляет значение предельной вероятности системы при n заявках.
    Значение n <= числа обслуживающих каналов в системе.
    :param alpha: Коэффициент загрузки системы.
    :param n: Количество заявок в системе.
    :return: Значение предельной вероятности системы при n заявках.
    """
    return alpha ** n / (np.math.factorial(n) * np.sum(
        [alpha ** k / np.math.factorial(k) for k in range(n + 1)]) + alpha ** (n + 1) / (
                                 n - alpha))

def calc_ey(mu: float, lam: float, n: int):
    """
    Вычилсяет математическое ожидание заявки от поступления ее в систему
    до начала обслуживания одним из каналов.
    :param mu: Интенсивность обслуживания заявок одним каналом узла.
    :param lam: Интенсивность входящего в узел потока из одиночных пассажиров.
    :param n: Количество каналов обслуживания в узле.
    :return: Значение математического ожидания времени ожидания заявки в очереди.
    """
    alpha = lam / mu
    return n * pi_n(alpha, n) / (mu * (n - alpha) ** 2)

def fit_ey(mu: float, lam: float, n_min: int, n_max: int, p_pow=4, plot=False):
    """
    Аппроксимирует математическое ожидание времени ожидания заявки в очереди
    полиномом заданной степени.
    :param mu: Интенсивность обслуживания заявок одним каналом узла.
    :param lam: Интенсивность входящего в узел потока из одиночных пассажиров.
    :param n_min: Минимально возможное число каналов обслуживания в узле.
    :param n_max: Максимально возможное число каналов обслуживания в узле.
    :param p_pow: Степень полинома для аппроксимации.
    :param plot: Визуализировать приближение полиномом.
    :return: Массив из коэффициентов полинома.
    """
    n = np.arange(n_min, n_max + 1)
    ey = np.array([calc_ey(mu, lam, n_) for n_ in n])
    if len(n) < p_pow:
        p_pow = len(n) - 1
    if p_pow:
        res = Polynomial.fit(n, np.log(ey), p_pow, full=True)
        coef = res[0].convert().coef[::-1]
        rss = res[1][0][0]
    else:
        coef = np.log(ey)
        rss = 0.

    if plot:
        x = np.linspace(n_min - 1, n_max + 1, 100)
        f = np.poly1d(coef)

        plt.figure(figsize=(13, 8))
        plt.scatter(n, np.log(ey), s=55)
        plt.plot(x, f(x), label=f'Аппроксимирующая кривая (полином), RSS = {np.round(rss, 2)}')
        plt.title(
            'Логарифм мат. ожидания времени ожидания начала обслуживания в зависимости от числа обслуживающих каналов в узле')
        plt.xlabel('Количество обслуживающих каналов в узле')
        plt.ylabel('Логарифм мат. ожидания времени ожидания начала обслуживания')
        plt.legend()
        plt.grid()
        plt.show()

        rss = np.sum(np.square(ey - np.array([np.exp(f(n_)) for n_ in n])))
        plt.figure(figsize=(13, 8))
        plt.scatter(n, ey, s=55)
        plt.plot(x, np.exp(f(x)), label=f'Аппроксимирующая кривая, RSS = {np.round(rss, 2)}')
        plt.title(
            'Мат. ожидание времени ожидания начала обслуживания в зависимости от числа обслуживающих каналов в узле')
        plt.xlabel('Количество обслуживающих каналов в узле')
        plt.ylabel('Мат. ожидание времени ожидания начала обслуживания')
        plt.legend()
        plt.grid()
        plt.show()

    return coef

def calc_time(x: list, p: list):
    """
    Рассчет суммарного среднего времени ожидания.
    :param x: Количество каналов обслуживания в каждом узле.
    :param p: Массив из коэффициентов полинома,
    описывающего логарифм среднего времени ожидания в каждом узле.
    :return: Суммарное среднее время ожидания.
    """
    res = 0
    for x_, p_ in zip(x, p):
        f = np.poly1d(p_)
        res += np.exp(f(x_))
    return res

def constraint_cost(x: np.ndarray, p: list):
    """
    Ограничение для затрат на обслуживающий персонал.
    Должно быть >= 0.
    :param x: Массив. Количество каналов обслуживания в каждом узле.
    :param p[0]: Максимальное значение затрат на каналы обслуживания.
    :param p[1]: Массив. Стоимость каждого канала обслуживания.
    :return: Значение ограничения.
    """
    return np.array([p[0] - sum(x * p[1])])

def calc_cost(x: np.ndarray, p: np.ndarray):
    """
    Рассчет затрат на обслуживающий персонал.
    :param x: Количество каналов обслуживания в каждом узле.
    :param p: Массив. Стоимость каждого канала обслуживания.
    :return: Значение затрат.
    """
    return sum(x * p)

def constraint_time(x: list, p: list):
    """
    Ограничение для суммарного среднего времени ожидания. Должно быть >= 0.
    :param x: Количество каналов обслуживания в каждом узле.
    :param p[0]: Максимальное суммарное время ожидания пассажира в очереди.
    :param p[1]: Массив из коэффициентов полинома,
    описывающего логарифм среднего времени ожидания в каждом узле.
    :return: Значение ограничения.
    """
    res = p[0]
    for x_, p_ in zip(x, p[1]):
        f = np.poly1d(p_)
        res -= np.exp(f(x_))
    return res

class ConstraintTime:
    def __init__(self, t_max: float, coefs: np.ndarray):
        """
        Ограничение для суммарного среднего времени ожидания. Должно быть >= 0.
        :param t_max: Максимальное суммарное время ожидания пассажира в очереди.
        :param coefs: Массив из коэффициентов полинома,
        описывающего логарифм среднего времени ожидания в каждом узле.
        """
        self.t_max = t_max
        self.coefs = coefs

    def __call__(self, x: typing.Union[list, np.ndarray]):
        """
        :param x: Количество каналов обслуживания в каждом узле.
        описывающего логарифм среднего времени ожидания в каждом узле.
        :return: Значение ограничения.
        """
        res = self.t_max
        for x_, p_ in zip(x, self.coefs):
            f = np.poly1d(p_)
            res -= np.exp(f(x_))
        return res

def f_ey(n: list, mu: list, lam: float, p: list):
    """
    Вычилсяет суммарное математическое ожидание заявки
    в сети массового обслуживания.
    :param n: Массив. Количество каналов обслуживания в узлах сети.
    :param mu: Массив. Интенсивности обслуживания заявок узлами сети.
    :param lam: Интенсивность входящего в первый узел потока из одиночных пассажиров.
    :param p: Массив из вероятностей перехода в следующие после первого узлы сети.
    :return: Значение суммарного математического ожидания времени ожидания заявки в очередях сети.
    """
    l = np.insert(lam * np.cumprod(p), 0, lam)
    return sum([calc_ey(mu[i], l[i], n[i]) for i in range(len(n))])

def f_conv(n: list, mu: list, lam: float, p: list, n_min: list, n_max: list, s: list, w: list):
    """
    Рассчет взвешенной суммы суммарного времени ожидания и затрат на обслуживающий персонал.
    :param n: Массив. Количество каналов обслуживания в узлах сети.
    :param mu: Массив. Интенсивности обслуживания заявок узлами сети.
    :param lam: Интенсивность входящего в первый узел потока из одиночных пассажиров.
    :param p: Массив из вероятностей перехода в следующие после первого узлы сети.
    :param n_min: Массив. Минимальное число каналов в каждом узле.
    :param n_max: Массив. Максимальное число каналов в каждом узле.
    :param s: Массив. Стоимость каждого канала обслуживания.
    :param w: Массив. Коэффициенты свертки каждого критерия.
    :return: Значение взвешенной суммы критериев.
    """
    a1 = f_ey(n_max, mu, lam, p)
    a1_max = f_ey(n_min, mu, lam, p)
    a2 = calc_cost(n_min, s)
    a2_max = calc_cost(n_max, s)
    return w[0] * (f_ey(n, mu, lam, p) - a1) / (a1_max - a1) + w[1] * (
            calc_cost(n, s) - a2) / (a2_max - a2)

def f_ideal(n: list, mu: list, lam: float, p: list, n_min: list, n_max: list, s: list, w: list, m: str):
    """
    Рассчет расстояния до идеальной точки.
    :param n: Массив. Количество каналов обслуживания в узлах сети.
    :param mu: Массив. Интенсивности обслуживания заявок узлами сети.
    :param lam: Интенсивность входящего в первый узел потока из одиночных пассажиров.
    :param p: Массив из вероятностей перехода в следующие после первого узлы сети.
    :param n_min: Массив. Минимальное число каналов в каждом узле.
    :param n_max: Массив. Максимальное число каналов в каждом узле.
    :param s: Массив. Стоимость каждого канала обслуживания.
    :param w: Массив. Веса каждого критерия.
    :param m: Строка ('2-norm' или 'inf'). Задает норму для вычисления отклонения
    критерия от идеальной точки.
    :return: Расстояние до идеальной точки.
    """
    metrics = ['2-norm', 'inf']
    assert m in metrics
    a1 = f_ey(n_max, mu, lam, p)
    a1_max = f_ey(n_min, mu, lam, p)
    a2 = calc_cost(n_min, s)
    a2_max = calc_cost(n_max, s)
    if m == metrics[0]:
        return w[0] * ((f_ey(n, mu, lam, p) - a1) / (a1_max - a1)) ** 2 + w[1] * (
                (calc_cost(n, s) - a2) / (a2_max - a2)) ** 2
    else:
        return max(w[0] * abs((f_ey(n, mu, lam, p) - a1) / (a1_max - a1)), w[1] *
                   abs((calc_cost(n, s) - a2) / (a2_max - a2)))

def calc_conv(x: list, p: list):
    """
    Рассчет взвешенной суммы времени ожидания и затрат на обслуживающий персонал.
    :param x: Количество каналов обслуживания в каждом узле.
    :param p[0]: Массив. Коэффициенты свертки каждого критерия.
    :param p[1]: Массив из коэффициентов полинома,
    описывающего логарифм среднего времени ожидания в каждом узле.
    :param p[2]: Массив. Стоимость каждого канала обслуживания.
    :param p[3]: Массив. Минимальное число каналов в каждом узле.
    :param p[4]: Массив. Максимальное число каналов в каждом узле.
    :return: Значение взвешенной суммы критериев.
    """
    a1 = calc_time(p[4], p[1])
    a1_max = calc_time(p[3], p[1])
    a2 = calc_cost(p[3], p[2])
    a2_max = calc_cost(p[4], p[2])
    return p[0][0] * (calc_time(x, p[1]) - a1) / (a1_max - a1) + p[0][1] * (
            calc_cost(x, p[2]) - a2) / (a2_max - a2)

def calc_ideal(x: list, p: list):
    """
    Рассчет расстояния до идеальной точки.
    :param x: Количество каналов обслуживания в каждом узле.
    :param p[0]: Массив. Веса каждого критерия.
    :param p[1]: Массив из коэффициентов полинома,
    описывающего логарифм среднего времени ожидания в каждом узле.
    :param p[2]: Массив. Стоимость каждого канала обслуживания.
    :param p[3]: Массив. Минимальное число каналов в каждом узле.
    :param p[4]: Массив. Максимальное число каналов в каждом узле.
    :param p[5]: Строка ('2-norm' или 'inf'). Задает норму для вычисления отклонения
    критерия от идеальной точки.
    :return: Расстояние до идеальной точки.
    """
    metrics = ['2-norm', 'inf']
    assert p[5] in metrics
    a1 = calc_time(p[4], p[1])
    a1_max = calc_time(p[3], p[1])
    a2 = calc_cost(p[3], p[2])
    a2_max = calc_cost(p[4], p[2])
    if p[5] == metrics[0]:
        return p[0][0] * ((calc_time(x, p[1]) - a1) / (a1_max - a1)) ** 2 + p[0][1] * (
                (calc_cost(x, p[2]) - a2) / (a2_max - a2)) ** 2
    else:
        return max(p[0][0] * abs((calc_time(x, p[1]) - a1) / (a1_max - a1)), p[0][1] *
                   abs((calc_cost(x, p[2]) - a2) / (a2_max - a2)))


#!/usr/bin/env python
# coding: utf-8
# # Portfolio Optimization
# “Modern Portfolio Theory (MPT), a hypothesis put forth by Harry Markowitz in his paper “Portfolio Selection,”
# (published in 1952 by the Journal of Finance) is an investment theory based on the idea that risk-averse investors can construct portfolios
# to optimize or maximize expected return based on a given level of market risk, emphasizing that risk is an inherent part of higher reward.
# It is one of the most important and influential economic theories dealing with finance and investment.
# ## Monte Carlo Simulation for Optimization Search
# We could randomly try to find the optimal portfolio balance using Monte Carlo simulation
# In[21]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import minimize

# Download and get Daily Returns
aapl = pd.read_csv('Data/09-Python-Finance-Fundamentals_AAPL_CLOSE.csv', index_col='Date', parse_dates=True)
cisco = pd.read_csv('Data/09-Python-Finance-Fundamentals_CISCO_CLOSE.csv', index_col='Date', parse_dates=True)
ibm = pd.read_csv('Data/09-Python-Finance-Fundamentals_IBM_CLOSE.csv', index_col='Date', parse_dates=True)
amzn = pd.read_csv('Data/09-Python-Finance-Fundamentals_AMZN_CLOSE.csv', index_col='Date', parse_dates=True)
stocks = pd.concat([aapl, cisco, ibm, amzn], axis=1)
stocks.columns = ['aapl', 'cisco', 'ibm', 'amzn']
stocks.head()
# In[25]:
mean_daily_ret = stocks.pct_change(1).mean()
mean_daily_ret
# In[26]:
stocks.pct_change(1).corr()
# # Simulating Thousands of Possible Allocations
stocks.head()
# In[28]:
stock_normed = stocks / stocks.iloc[0]
stock_normed.plot()
# In[29]:
stock_daily_ret = stocks.pct_change(1)
stock_daily_ret.head()
# ## Log Returns vs Arithmetic Returns
# We will now switch over to using log returns instead of arithmetic returns, for many of our use cases they are almost the same,
# but most technical analyses require detrending/normalizing the time series and using log returns is a nice way to do that.
# Log returns are convenient to work with in many of the algorithms we will encounter.
# For a full analysis of why we use log returns, check [this great article](https://quantivity.wordpress.com/2011/02/21/why-log-returns/).
log_ret = np.log(stocks / stocks.shift(1))
log_ret.head()
# In[31]:
log_ret.hist_df(bins=100, figsize=(12, 6));
plt.tight_layout()
# In[32]:
log_ret.describe().transpose()
# In[33]:
log_ret.mean() * 252
# Compute pairwise covariance of columns
log_ret.cov()
# In[35]:
log_ret.cov() * 252  # multiply by days
# ## Single Run for Some Random Allocation
# Set seed (optional)
np.random.seed(101)
# Stock Columns
print('Stocks')
print(stocks.columns)
print('\n')
# Create Random Weights
print('Creating Random Weights')
weights = np.array(np.random.random(4))
print(weights)
print('\n')
# Rebalance Weights
print('Rebalance to sum to 1.0')
weights = weights / np.sum(weights)
print(weights)
print('\n')
# Expected Return
print('Expected Portfolio Return')
exp_ret = np.sum(log_ret.mean() * weights) * 252
print(exp_ret)
print('\n')
# Expected Variance
print('Expected Volatility')
exp_vol = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov() * 252, weights)))
print(exp_vol)
print('\n')
# Sharpe Ratio
SR = exp_ret / exp_vol
print('Sharpe Ratio')
print(SR)
# Great! Now we can just run this many times over!
num_ports = 15000
all_weights = np.zeros((num_ports, len(stocks.columns)))
ret_arr = np.zeros(num_ports)
vol_arr = np.zeros(num_ports)
sharpe_arr = np.zeros(num_ports)
for ind in range(num_ports):
    # Create Random Weights
    weights = np.array(np.random.random(4))
    # Rebalance Weights
    weights = weights / np.sum(weights)
    # Save Weights
    all_weights[ind, :] = weights
    # Expected Return
    ret_arr[ind] = np.sum((log_ret.mean() * weights) * 252)
    # Expected Variance
    vol_arr[ind] = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov() * 252, weights)))
    # Sharpe Ratio
    sharpe_arr[ind] = ret_arr[ind] / vol_arr[ind]
sharpe_arr.max()
# In[39]:
sharpe_arr.argmax()
# In[40]:
all_weights[1419, :]
# In[41]:
max_sr_ret = ret_arr[1419]
max_sr_vol = vol_arr[1419]
# ## Plotting the data
plt.figure(figsize=(12, 8))
plt.scatter(vol_arr, ret_arr, c=sharpe_arr, cmap='plasma')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Volatility')
plt.ylabel('Return')
# Add red dot for max SR
plt.scatter(max_sr_vol, max_sr_ret, c='red', s=50, edgecolors='black')

# # Mathematical Optimization
# There are much better ways to find good allocation weights than just guess and check! We can use optimization functions to find the ideal weights mathematically!
# ### Functionalize Return and SR operations
def get_ret_vol_sr(weights):
    """
    Takes in weights, returns array or return,volatility, sharpe ratio
    """
    weights = np.array(weights)
    ret = np.sum(log_ret.mean() * weights) * 252
    vol = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov() * 252, weights)))
    sr = ret / vol
    return np.array([ret, vol, sr])

# To fully understand all the parameters, check out:
# https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
help(minimize)

# Optimization works as a minimization function, since we actually want to maximize the Sharpe Ratio,
# we will need to turn it negative so we can minimize the negative sharpe (same as maximizing the postive sharpe)
def neg_sharpe(weights):
    return get_ret_vol_sr(weights)[2] * -1

# Contraints
def check_sum(weights):
    '''
    Returns 0 if sum of weights is 1.0
    '''
    return np.sum(weights) - 1

# By convention of minimize function it should be a function that returns zero for conditions
cons = ({'type': 'eq', 'fun': check_sum})
# 0-1 bounds for each weight
bounds = ((0, 1), (0, 1), (0, 1), (0, 1))
# Initial Guess (equal distribution)
init_guess = [0.25, 0.25, 0.25, 0.25]
# Sequential Least SQuares Programming (SLSQP).
opt_results = minimize(neg_sharpe, init_guess, method='SLSQP', bounds=bounds, constraints=cons)
opt_results
# In[53]:
opt_results.x
# In[54]:
get_ret_vol_sr(opt_results.x)
# # All Optimal Portfolios (Efficient Frontier)
# The efficient frontier is the set of optimal portfolios that offers the highest expected return for a defined level of risk or
# the lowest risk for a given level of expected return. Portfolios that lie below the efficient frontier are sub-optimal,
# because they do not provide enough return for the level of risk. Portfolios that cluster to the right of the efficient frontier are also sub-optimal,
# because they have a higher level of risk for the defined rate of return.
# Efficient Frontier http://www.investopedia.com/terms/e/efficientfrontier
# Our returns go from 0 to somewhere along 0.3
# Create a linspace number of points to calculate x on
frontier_y = np.linspace(0, 0.3, 100)  # Change 100 to a lower number for slower computers!

# In[56]:
def minimize_volatility(weights):
    return get_ret_vol_sr(weights)[1]

# In[57]:
frontier_volatility = []
for possible_return in frontier_y:
    # function for return
    cons = ({'type': 'eq', 'fun': check_sum},
            {'type': 'eq', 'fun': lambda w: get_ret_vol_sr(w)[0] - possible_return})
    result = minimize(minimize_volatility, init_guess, method='SLSQP', bounds=bounds, constraints=cons)
    frontier_volatility.append(result['fun'])
plt.figure(figsize=(12, 8))
plt.scatter(vol_arr, ret_arr, c=sharpe_arr, cmap='plasma')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Volatility')
plt.ylabel('Return')
# Add frontier line
plt.plot(frontier_volatility, frontier_y, 'g--', linewidth=3)
# # Great Job!


from numpy import linalg as _LA
import numpy as _np

def matrix_system(q, N, type='even', period='one'):
    ''' Creates tridianogal matrix system of order NxN, associated with
    each of the four classes of simply-periodic functions.

    Input:
        q: parameter, real or purely imaginary.
        N: Size of the matrix, and thus the order of the highest harmonic in
            the trigonometric series that defines each Mathieu function.
        type: str, `even` or `odd`.
        period: str, `one` or 'two'. If `one`, function is assumed to be
            pi-periodic. If `two` function is taken to be `2pi`-periodic.
    Outout:
        A: ndarray, the square matrix associated with each of the four types
            of simply-periodic Mathieu-functions.
    '''
    if type is 'even':
        if period is 'one':  # ce_{2n}
            d = [(2. * r) ** 2 for r in range(N)]
            e = q * _np.ones(N - 1)
            A = _np.diag(d) + _np.diag(e, k=-1) + _np.diag(e, k=1)
            A[0, 1] = _np.sqrt(2) * A[0, 1]
            A[1, 0] = A[0, 1]
        elif period is 'two':  # se_{2n+2}
            pass
    elif type is 'odd':
        if period is 'one':  # se_{2n+1}
            pass
        elif period is 'two':  # ce_{2n+1}
            d = [((2. * r) + 1)**2 for r in range(N)]
            e = q * _np.ones(N - 1)
            A = _np.diag(d) + _np.diag(e, k=-1) + _np.diag(e, k=1)
    return A

def eig_pairs(A, type='even', period='one'):
    ''' Calculates the characteristic value (eigenvalue) and the Fourier
    coefficients associated with the Mathieu function. Both the eigenvalues
    and Fourier coefficients are given in ascending order.
    '''
    w, V = _LA.eig(A)  # calculates the eigenvalue
    if [type, period] == ['even', 'one']:
        V[0, :] = V[0, :] / _np.sqrt(2)  # remove factor on all first entries
    #  Sort the eigenvalues and in accordance, re-arrange eigenvectors
    ord_w, V = order_check(w, V)
    return ord_w, V

def order_check(a, v):
    """ Check the ordering of the eigenvalue array, from smaller to larger. If
    true, return a unchanged. Ordering also matters if a is complex. If a is
    complex, ordering again is first set according to real(a). If two
    eigenvalues are complex conjugates, then ordering is in accordance to the
    sign of complex(a). Negative sign is first.
    """
    if a.imag.any() == 0:
        ordered_a = a
        nv = v
    else:
        Ind = _np.argsort(_np.round(a, 5))  # sorting through 5 decimals
        ordered_a = a[Ind]
        nv = 0 * _np.copy(v)
        for k in range(len(Ind)):
            nv[:, k] = v[:, Ind[k]]
    return ordered_a, nv


import numpy as np
import cmath
import math
import matplotlib.pyplot as plt
from scipy.linalg import hessenberg

# A function that gets the Termination condition for the Eigen value iterations
def term_eigs(H,Q,R,n):
	flag = True
	for i in range(n):
		temp = np.linalg.norm(np.matmul(H,Q[:,i]) - (R[i,i] * Q[:,i]))
		# If norm of any corresponding index is greater than the threshold
		if temp >= 10**-6:
			flag = False
			break
	return flag

## A function that gets the n smallest Eigen values of A
def eigs(A,n):
	# Performing matrix inversion
	A_orig = A.copy()
	A = np.linalg.inv(A)

	# Getting the Hessenberg matrix (Tridiagonal in case of Symmetric matrix)
	# Also gets out the reflector Q for the transformation
	H, Q = hessenberg(A, calc_q=True)

	# Defining variables for the iteration
	m = A.shape[0]
	Q_eig, R_eig = np.eye(m,m), np.eye(m,m)
	k = 0

	while (k<50) and (not (term_eigs(H, Q_eig, R_eig,n))):
		Q_eig, R_eig = np.linalg.qr(H, mode = 'reduced')
		H = np.matmul(R_eig,Q_eig)
		k +=1

	# Getting the eigen values
	lamda = np.zeros((n,1))
	for i in range(n):
		lamda[i] = 1.0/R_eig[i,i]

	# Reducing the eigen vectors to the original basis
	Qhat = np.matmul(Q.T, np.matmul(Q_eig,Q))

	return lamda,(Qhat[:,:(n)]/np.linalg.norm(Qhat[:,:(n)]))

# A function that checks the working of Eigen QR iterations
def check_eigen_correctness():
	A = np.array([[1,2,4,2,5],[3,5,6,2,5],[6,8,5,4,3],[7,6,5,3,2],[7,5,4,3,2]])
	n = 4
	print(eigs(A,n))

# A function that would compute the 2 norm of a complex number
def complex_norm(temp):
	return math.sqrt(((temp.real)**2) + ((temp.imag)**2))

# A function that computes the value of the function p
def compute_p(a,x0):
	n = a.shape[0]
	temp = complex(0,0)
	z = complex(x0[0],x0[1])

	for i in range(n):
		temp += a[i] * (z ** i)

	return temp

# A function that computes the value of derivative the function p
def compute_derivative_p(a,x0):
	n = a.shape[0]
	temp = complex(0,0)
	z = complex(x0[0],x0[1])

	for i in range(1, n):
		temp += i * a[i] * (z ** (i-1))

	return temp

## A function that computes the Jacobian of the function at a given point
def compute_J_p(a,x0):
	temp = compute_derivative_p(a,x0)
	alpha, beta = temp.real, temp.imag

	J = np.zeros((2,2))
	J[0,0] = alpha
	J[0,1] = -1.0 * beta
	J[1,0] = beta
	J[1,1] = alpha

	return J

# The function that solves Netwon's method
def newton(a,x0):
	k = 0
	temp = compute_p(a,x0)
	xstar = x0
	while k<50 and complex_norm(temp)>=10**-6:
		f_vec = np.array([temp.real, temp.imag])
		xstar = xstar - np.matmul((np.linalg.inv(compute_J_p(a,xstar))),f_vec)
		temp = compute_p(a,xstar)
		k += 1
	return xstar

# The function that would make the colored mesh as required
def create_mesh():
	a = np.array([-1.,-1.,0.,1.])
	x1_list = np.arange(-1.0, 2.05, 0.05)
	x2_list = np.arange(-1.0, 1.05, 0.05)

	# Hard-coding the solutions
	sol_1 = np.array([-0.66235, 0.56227])
	sol_2 = np.array([-0.66235, -0.56227])
	sol_3 = np.array([1.32471, 0.0])

	# Starting the plot
	plt.xlim([-1, 2])
	plt.ylim([-1, 1])

	for x1 in x1_list:
		for x2 in x2_list:
			sol = newton(a,np.array([x1,x2]))
			if np.linalg.norm(sol - sol_1) < 10**-3:
				plt.scatter(x1,x2,s=10,color = 'r')
			elif np.linalg.norm(sol - sol_2) < 10**-3:
				plt.scatter(x1,x2,s=10,color = 'b')
			else:
				plt.scatter(x1,x2,s=10,color = 'g')

	# The configurations required for the plot
	plt.xlabel('x1')
	plt.ylabel('x2')
	plt.title('Netwon\'s method convergence for various starting points')
	plt.savefig('q6_scatter.png')

# The function that checks the correctness of the Newton's algorithm
def check_newton_correctness():
	a = np.array([3.0, -4.0, 1.0])
	x0 = np.array([1.0, 10.0])
	xstar = newton(a,x0)
	print('The solution for the given polynomial is: ', xstar)

# Here the main function is called
if __name__ == '__main__':
	create_mesh()


def matrix_modulus(N, p=2, sigma=None):
    """
    Computes the modulus of a family given its N matrix.

    Parameters
    ----------
    N : numpy array OR scipy sparse matrix
        The N matrix for the family.

    p : float or np.inf
        The modulus energy exponent.

    sigma : numpy array
        The weights sigma.  If sigma = None, all weights
        are treated as 1.

    Returns
    -------
    mod : float
        An approximation of the modulus.

    rho : numpy array
        An optimal rho^* for modulus.

    lam : numpy array
        An optimal dual lambda^* for modulus.

    Notes
    -----
    This function is just a template solver.  For specialized
    problems, it is probably best to implement a particular
    solver based on the energy exponent and the desired
    tolerances in the approximation.
    """

    import cvxpy as cvx
    import numpy as np

    from warnings import warn

    # problem dimension
    m = N.shape[1]

    # set sigma to default if necessary
    if sigma is None:
        sigma = np.ones(m)

    # convert inputs to cvxpy constants
    N = cvx.Constant(N)
    sigma = cvx.Constant(sigma)

    # primal variables
    rho = cvx.Variable(m)

    # objective
    if p is not np.inf:
        obj = cvx.Minimize(sigma.T@rho**p)
    else:
        obj = cvx.Minimize(cvx.max(cvx.multiply(sigma, rho)))

    # constraints
    cons = [rho >= 0, N@rho >= 1]

    # set up the problem
    prob = cvx.Problem(obj, cons)

    # attempt to solve
    prob.solve()
    if prob.status != 'optimal':
        warn('cvxpy solve returned status {}'.format(prob.status))

    return prob.value, np.array(rho.value).flatten(), np.array(cons[1].dual_value).flatten()

def modulus(m, solve_subproblem, find_shortest, p = 2, sigma = None, tol = 1e-3, max_iter = 1000,
            output_every = None):
    """
    Implements the basic algorithm for modulus.

    Parameters
    ----------
    m : int
        The dimension of the modulus problem (number of edges).

    solve_subproblem : callable
        See below.

    find_shortest : callable
        See below.

    p : float or np.inf
        The modulus energy exponent.

    sigma : numpy array
        The weights sigma.  If sigma = None, all weights
        are treated as 1.

    tol : float
        The tolerance.  The modulus algorithm stops when the approximate
        density is within tol of being admissible.

    max_iter : int
        Maximum number of iterations to perform before terminating with an error.

    output_every : int
        Frequency of output to stderr.  If this is set to None, output is supressed.

    Returns
    -------
    mod : float
        Approximation to modulus.

    cons : list
        List of constraints added during the iteration.  The format of the elements of
        this list is determined by the output of the find_violated_constraint function.

    rho : numpy array
        Approximation to an optimal density.

    lam : numpy array
        Approximation to the dual variables for the constraints listed in cons.

    Notes
    -----
    The function solve_subproblem should have the following signature

        mod, rho, lam = solve_subproblem(N, p, sigma)

    See the function matrix_modulus for an example.

    The function find_shortest should have the following signature

        cons, n = find_shortest(rho, tol)

    This function should find a "most violated constraint" using the specified values
    for rho.  Upon return, cons may contain any representation desired for describing
    the constraint.  (This is purely informational for the user and it is acceptable
    for cons to be set to None.)  n should contain the numpy row vector representing the
    violated constraint.  This is the row that will be added to N on the next iteration.

    The argument tol may be ignored.  However, it is acceptable for the function to return
    the tuple (None, None) if every constraint is satisfied to within a tolerance of tol.
    """

    import numpy as np
    import scipy.sparse as sp
    from time import perf_counter

    # timers
    search_time = 0.
    update_time = 0.
    mod_start = perf_counter()

    # initialize variables
    rho = np.zeros(m)
    N = sp.csr_matrix((0,m))
    lam = np.array([])
    mod = 0.
    upper = np.inf
    cons = []

    # default sigma
    if sigma is None:
        sigma = np.ones(m)

    # initialize output table
    if output_every:
        print('| {:>6s} | {:>9s} | {:>9s} | {:>9s} | {:>6s} | {:>9s} |' \
            .format( 'it', 'l bnd', 'u bnd', 'rel gap', '# cons', 'time (s)' ))
        print('+--------+-----------+-----------+-----------+--------+-----------+')

    # loop to at most max_iter
    for iter_count in range(max_iter):

        # find a constraint to add
        start = perf_counter()
        c, n = find_shortest(rho, tol)
        search_time += perf_counter()-start

        # compute the length of the shortest object
        if n is None:
            length = 1.
        else:
            length = n.dot(rho)

        # update the upper bound
        if length > 0:
            if p is np.Inf:
                upper = np.abs(sigma*rho/length)
            else:
                upper = np.sum(sigma*(rho/length)**p)

        # check if we can stop
        if length > 1-tol:
            if output_every:
                print('| {:6d} | {:9.3e} | {:9.3e} | {:9.3e} | {:6d} | {:9.3e} |' \
                    .format( iter_count+1, mod, upper, (upper-mod)/mod, N.shape[0], perf_counter() - mod_start ))

                print()
                print('program running time = {} sec'.format( perf_counter() - mod_start ))
                print('constraint search    = {} sec'.format( search_time ))
                print('solution update      = {} sec'.format( update_time ))

            return mod, cons, rho, lam

        # if not, we need to add a constraint
        N = sp.vstack([N,n], format='csr')
        cons.append(c)

        # re-optimize
        start = perf_counter()
        mod, rho, lam =solve_subproblem(N, p, sigma)
        update_time += perf_counter()-start

        # print some feedback if desired
        if output_every and (iter_count+1) % output_every == 0:
            if mod == 0:
                rel_gap = np.inf
            else:
                rel_gap = (upper-mod)/mod
            print('| {:6d} | {:.3e} | {:.3e} | {:.3e} | {:6d} | {:.3e} |' \
                .format( iter_count+1, mod, upper, rel_gap, N.shape[0], perf_counter() - mod_start ))

    # if we got here, we failed to converge
    raise RuntimeError('Modulus algorithm failed to converge in {} iterations.'.format(max_iter))


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

def get_module(x):
    """
        x为需要求长度的向量
        欧拉距离/欧式距离(Euclidean Distance)
    """
    return np.sqrt(np.sum(np.square(x)))

def get_angle(x, y):
    """
        一维数据，dot点乘，对数学而言即为求取其数量积
    """
    return np.arccos(np.dot(x, y)/(get_module(x)*get_module(y)))

def get_mudule_len_by_dot():
    print('----- 通过数量积求模长 ----- ')
    # --------------------------------
    # 纯numpy(数学)方式
    OA = np.array([3, 0])
    OB = np.array([0, 4])
    AB = OB - OA  # 指向被减数
    # 长度
    d = get_module(AB)
    print('mlen = ', d)

    # -------------------------------
    # numpy + scipy方式
    xy = np.vstack([OA, OB])
    print(xy)
    dist = pdist(xy)
    print('mlen = ', dist[0])

def get_angle_by_cross():
    """
        已知M(1,1,1),A(2,2,1),B(2,1,2)
        求角度AMB
    """
    print('----- 通过向量积求角度 ----- ')
    M = np.array([1, 1, 1])
    A = np.array([2, 2, 1])
    B = np.array([2, 1, 2])
    MA = A - M
    MB = B - M
    angle = get_angle(MA, MB)
    print(angle)
    if angle - np.pi/3 < 0.0001:
        print('数量积结果正确')

def get_triangle_area_by_cross():
    """
        已知三角形三点坐标，A，B，C坐标点。
        求三角形面积
    """
    print('----- 通过向量积求三角形面积 ----- ')
    A = np.array([1, 2, 3])
    B = np.array([3, 4, 5])
    C = np.array([2, 4, 7])
    AB = B - A
    AC = C - A
    # 面积
    AB_AC = 1/2 * np.cross(AB, AC)
    S = get_module(AB_AC)
    print(S)
    if S - np.sqrt(14) < 0.0001:
        print('向量积结果正确')

if __name__ == "__main__":
    get_mudule_len_by_dot()
    get_angle_by_cross()
    get_triangle_area_by_cross()


from sympy import *

x = symbols('x')

psi = [2*(x-Rational(1,2))*(x-1),
       4*x*(1-x),
       2*x*(x-Rational(1,2))]

dpsi = [ diff(psi[k], x) for k in range(3) ]

def a(p, q):
    return integrate(dpsi[q]*dpsi[p], (x, 0, 1))

def c(p,q):
    return integrate(psi[q]*psi[p], (x, 0, 1))

A = Matrix(3, 3, a)
C = Matrix(3, 3, c)

print('Quadratic element stiffness matrix is 1/(3h) times')
display(3*A)
print('Quadratic element mass matrix is h/30 times')
display(30*C)