########################################################################
# This program illustrate many approaches to solve ODEs. In this case
#    we use the simple pendulum as the system.
#
# This module implements all the classes and functions for the problem.
#
#
#    1) We compare the solutions for theta = 10o and 100o.
#    2) We make a plot of both theta(t) vs t and E vs t.
#
#    Marina von Steinkirch, spring/2013
#    (based on Mike Zingale's codes)
#
########################################################################

import numpy as np
import pylab
import math
from classes import *

# global parameters and constants
CONST_l = 10.0
CONST_g = 10.0
CONST_m = 1.0
CONST_dt = [0.005, 0.01, 0.05, 0.1, 0.5]
CONST_Tmax = 10.0
CONST_theta0 = [10.0, 100.0]

# calculating for the two values of theta0
theta10 = pendulumTrajectory(CONST_theta0[0], CONST_l, CONST_g, CONST_m)
theta100 = pendulumTrajectory(CONST_theta0[1], CONST_l, CONST_g, CONST_m)

T10 = theta10.pendulumPeriod()
T100 = theta10.pendulumPeriod()
E10 = theta10.pendulumEnergy()
E100 = theta100.pendulumEnergy()

# printing results
print "The approx Period of the simple pendulum for theta0 = 10 degrees is %f s-1." % (T10)
print "The approx Period of the simple pendulum for theta0 = 100 degrees is %f s-1." % (T100)
print "The Absolute Total Energy of the simple pendulum for theta0 = 10 degrees is %f joules." % (E10)
print "TheAbsolute Total Energy of the simple pendulum for theta0 = 100 degrees is %f joules." % (E100)

# iterating for many time steps
for i in range(len(CONST_dt)):

    # calculates for euler and for euler-cromer
    histEuler10 = theta10.intEuler(CONST_dt[i], CONST_Tmax)
    histEC10 = theta10.intEulerCromer(CONST_dt[i], CONST_Tmax)
    histEuler100 = theta100.intEuler(CONST_dt[i], CONST_Tmax)
    histEC100 = theta100.intEulerCromer(CONST_dt[i], CONST_Tmax)

    # plotting theta vs t theta0 = 10
    time = histEC10.t
    ana = CONST_theta0[0]*np.cos((2*math.pi/T10)*time)

    pylab.clf()
    pylab.cla()

    pylab.plot(histEuler10.t, histEuler10.theta, label="Euler's method", color="m", lw = 2)
    pylab.plot(histEC10.t, histEC10.theta, label="Euler-Cromer's method", color="b", lw = 2)
    pylab.plot(time, ana, 'g*', label="Analytic Approximation")

    pylab.xlabel('Time (s)')
    pylab.ylabel('$\Theta$ (degrees)')
    pylab.title("Simple Pendulum: $\Theta_0$ = $10^o$, $\Delta$t = %s" % (str(CONST_dt[i])))
    pylab.grid(True)
    leg = pylab.legend(loc=3,labelspacing=0.005)
    ltext = leg.get_texts()
    pylab.setp(ltext, fontsize='small')
    leg.draw_frame(1)
    ax = pylab.gca()
    pylab.savefig("plots/theta_vs_t_for_theta0-10_dt-%s.png" % (str(CONST_dt[i])))

    # plotting theta vs t, theta0 = 100
    time = histEC100.t
    ana = CONST_theta0[0]*np.cos((2*math.pi/T100)*time)

    pylab.clf()
    pylab.cla()

    pylab.plot(histEuler100.t, histEuler100.theta, label="Euler's method", color="m", lw = 2)
    pylab.plot(histEC100.t, histEC100.theta, label="Euler-Cromer's method", color="b", lw = 2)
    pylab.plot(time, ana, 'g*', label="Analytic Approximation")

    pylab.xlabel('Time (s)')
    pylab.ylabel('$\Theta$ (degrees)')
    pylab.title("Simple Pendulum: $\Theta_0$ = $100^o$, $\Delta$t = %s" % (str(CONST_dt[i])))
    pylab.grid(True)
    leg = pylab.legend(loc=3,labelspacing=0.005)
    ltext = leg.get_texts()
    pylab.setp(ltext, fontsize='small')
    leg.draw_frame(1)
    ax = pylab.gca()
    pylab.savefig("plots/theta_vs_t_for_theta0-100_dt-%s.png" % (str(CONST_dt[i])))

    # plotting E(t) vs t, for theta0 = 10
    time = histEC10.t
    fun_en = []
    for j in range (len(time)):
                    fun_en.append(E10)
    pylab.clf()
    pylab.cla()
    pylab.plot(histEuler10.t, histEuler10.energy, label="Euler's method", color="r", lw=2)
    pylab.plot(histEC10.t, histEC10.energy, label="Euler-Cromer's method", color="b", lw =2)
    pylab.plot(time, fun_en, label="Approximation from initial values", color="g", lw = 2)
    leg = pylab.legend()
    ltext = leg.get_texts()
    pylab.setp(ltext, fontsize='small')
    leg.draw_frame(0)
    pylab.grid(True)
    pylab.xlabel('Time (s)')
    pylab.ylabel('Absolute Energy (J)')
    pylab.title("Simple Pendulum: $\Theta_0$ = $10^o$, $\Delta$t = %s" % (str(CONST_dt[i])))
    pylab.grid(True)
    leg = pylab.legend(loc=0,labelspacing=0.005)
    ltext = leg.get_texts()
    pylab.setp(ltext, fontsize='small')
    leg.draw_frame(1)
    ax = pylab.gca()
    pylab.savefig("plots/energy_vs_t_for_theta0-10_dt-%s.png" % (str(CONST_dt[i])))

    # plotting E(t) vs t, for theta0 = 100
    time = histEC100.t
    fun_en = []
    for j in range (len(time)):
                    fun_en.append(E100)
    pylab.clf()
    pylab.cla()
    pylab.plot(histEuler100.t, histEuler100.energy, label="Euler's method", color="r", lw=2)
    pylab.plot(histEC100.t, histEC100.energy, label="Euler-Cromer's method", color="b", lw =2)
    pylab.plot(time, fun_en, label="Approximation from initial values", color="g", lw = 2)
    leg = pylab.legend()
    ltext = leg.get_texts()
    pylab.setp(ltext, fontsize='small')
    leg.draw_frame(0)
    pylab.grid(True)
    pylab.xlabel('Time (s)')
    pylab.ylabel('Absolute Energy (J)')
    pylab.title("Simple Pendulum: $\Theta_0$ = $100^o$, $\Delta$t = %s" % (str(CONST_dt[i])))
    pylab.grid(True)
    leg = pylab.legend(loc=0,labelspacing=0.005)
    ltext = leg.get_texts()
    pylab.setp(ltext, fontsize='small')
    leg.draw_frame(1)
    ax = pylab.gca()
    pylab.savefig("plots/energy_vs_t_for_theta0-100_dt-%s.png" % (str(CONST_dt[i])))

# plotting E(t) vs t, for theta0 = 10 for the verlet method
CONST_dt = [0.0001, 0.001, 0.01, 0.1]
pylab.clf()
pylab.cla()
for j in range(len(CONST_dt)):
    histVer10 = theta10.intVerlet(CONST_dt[j], CONST_Tmax)
    pylab.loglog(histVer10.t, histVer10.dE, 'ro')
pylab.setp(ltext, fontsize='small')
pylab.grid(True)
pylab.ylim(10,0.001)
pylab.xlabel('Time Step ($\Delta t$)')
pylab.ylabel('Absolute Error in Energy')
pylab.title("Verlet Method for the Simple Pendulum, $\Theta_0$ = $10^o$")
ax = pylab.gca()
pylab.savefig("plots/verlet_energy_vs_t_for_theta0-10.png" )

# plotting E(t) vs t, for theta0 = 100 for the verlet method
CONST_dt = [0.0001, 0.001, 0.01, 0.1]
pylab.clf()
pylab.cla()
for k in range(len(CONST_dt)):
    histVer100 = theta100.intVerlet(CONST_dt[k], CONST_Tmax)
    pylab.loglog(histVer100.t, histVer100.dE, 'bo')
    print CONST_dt[k], histVer100.dE
pylab.setp(ltext, fontsize='small')
pylab.grid(True)
pylab.xlabel('Time Step ($\Delta t$)')
pylab.ylabel('Absolute Error in Energy')
pylab.title("Verlet Method for the Simple Pendulum, $\Theta_0$ = $100^o$")
ax = pylab.gca()
pylab.savefig("plots/verlet_energy_vs_t_for_theta0-100.png" )

print "\nDone!"


import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mpltcols
import matplotlib.patches as mpatches
from typing import Tuple

def cMDS(D: np.ndarray,
         is_similarity: bool = False
         ) -> Tuple:

    '''
    Computes Classical Multidimensional Scaling from a given distance, or
    similarity, matrix D.

    Parameters
    ----------
    D : np.ndarray
        A distance, or similarity, matrix (squared matrix)
    is_similarity: bool
        Determines if D is a similarity matrix (True) or a
        distance matrix (False)

    Returns
    -------
    Tuple
        F.T: np.ndarray
            transposed configuration matrix F
        D_sq: np.ndarray
            squared distance matrix D
        B: np.ndarray
            double centering matrix = -0.5*J*D^2*J
        e_vals: np.array
            eigenvalues of B

    Modified from: http://www.nervouscomputer.com/hfs/cmdscale-in-python/
    '''

    assert D.shape[0] == D.shape[1]

    if is_similarity:
        D = 1 - D # If D is a similarity matrix, convert it to a distance matrix
    # Number of samples
    n = len(D)
    # Create the Squared proximity matrix
    D_sq = D**2
    # Generate the Centering matrix: J
    # Defined as the identity matrix I_n minums an nxn all-ones matrix
    e1 = np.ones((n,1))
    m = ( np.ones((n, 1)) / n)
    I = np.eye(n) # Identity matrix I_n
    J = I - e1.dot(np.transpose(m)) # Centering matrix J
    # Generate B = -0.5*J*D^2*J
    B = -(1/2) * J.dot(D_sq).dot(J)

    # Compute the eigendecomposition of B
    e_vals, e_vecs = np.linalg.eigh(B)

    # Sort eigenvalues (descending)
    idx   = np.argsort(e_vals)[::-1] # Get the sorting indices
    e_vals = e_vals[idx] # Sort eigenvectors
    e_vecs = e_vecs[:,idx]
    w, = np.where(e_vals > 0) # To keep only positive eigenvalues
    e_vals = e_vals[w]
    # Configuration matrix F = M^(-1/2)*E_m*L_m^(1/2)
    M_neg_sq = np.diag(m[:,0]**(-0.5)) # M_neg_sq is the  M^(-1/2) matrix
    L_m_sq  = np.diag(np.sqrt(e_vals[w])) #  Lambda_sq (L_m^(1/2)) ia a diagonal matrix with the m positive eigenvalues
    E_m = e_vecs[:,w] # E_m  = sorted eigenvectors (positives only)
    # F  = M_neg_sq.dot(E_m.dot((L_m_sq)))  # F = M^(-1/2)*E_m*L_m^(1/2)
    F = E_m.dot((L_m_sq))
    # Return the computed matrices
    return F.T, D_sq, B, e_vals

def cMDS_proj(cMDS_obj: Tuple,
              sup_point: np.ndarray) -> np.ndarray:
    '''
    Computes Classical Multidimensional Scaling projection given
    a cMDS_obj (reference space) and a matrix of distances between
    supplementary and reference points.

    Parameters
    ----------
    cMDS_ob : Tuple
        A tuple with `F.T, D_sq, B, e_vals` values, returned from the
        cMDS function
    sup_point: np.ndarray
        A distance matrix of size nxm, where n is the number of
        reference samples and m is the number of supplementary samples

    Returns
    -------
    F_proj: np.ndarray
        A matrix with supplementary samples projected over the reference space

    Modified from: http://www.nervouscomputer.com/hfs/cmdscale-in-python/
    '''
    F = cMDS_obj[0].T # Matriz F de scores obtenida con cMDS a partir de los valores "Activos"
    D_sq = cMDS_obj[1] # Matrix Delta^2
    e_vals = cMDS_obj[3] # Eigenvalores positivos obtenidos del B de los valores "Activos"
    n = len(D_sq)
    e1 = np.ones((n, 1))
    m = len(sup_point)
    e1_sup = np.ones((m, 1))
    m = ( np.ones((n, 1)) / n)
    I = np.eye(n) # Matriz de identidad I_n
    J = I - e1.dot(np.transpose(m)) # Mariz de centrado
    # MDS out-of-smaple: Adición de un punto suplementario
    a_out = sup_point**2
    # D_sup_2 es un vector con los valores de RMSD del nuevo punto frente a los "Activos"
    # Se calcula B_sup como -0.5*J*(a_out - D^2*(m1^t))
    B_sup = -0.5 * J.dot(a_out.T - D_sq.dot(m.dot(np.transpose(e1_sup))))
    # Finalmente se calcula F_sup = (B_sup^t)*F*L_inv
    # Donde L_inv es la matriz diagonal con la inversa de los eingenvalores
    L_m_inv = np.diag( 1 / e_vals )
    F_proj = np.transpose(B_sup).dot( F ).dot( L_m_inv)
    return F_proj


import numpy as np
import pymc as pm
from ch1 import count_data

colors = ["#348ABD", "#A60628", "#7A68A6", "#467821", "#E24A33"]

n_count_data = len(count_data)

alpha = 1. / count_data.mean()

lambda_1 = pm.Exponential("lambda_1", alpha)
lambda_2 = pm.Exponential("lambda_2", alpha)

tau = pm.DiscreteUniform("tau", lower = 0, upper = n_count_data)

@pm.deterministic
def lambda_(tau = tau, lambda_1 = lambda_1, lambda_2 = lambda_2):
    out = np.zeros(n_count_data)
    out[:tau] = lambda_1
    out[tau:] = lambda_2
    return out

observation = pm.Poisson("obs", lambda_, value = count_data, observed = True)

model = pm.Model([observation, lambda_1, lambda_2, tau])
mcmc = pm.MCMC(model)
mcmc.sample(40000, 10000)

lambda_1_samples = mcmc.trace('lambda_1')[:]
lambda_2_samples = mcmc.trace('lambda_2')[:]
tau_samples = mcmc.trace('tau')[:]

print("\n\n")
print("lambda 1 is less than lambda 2 {0:.4f}% of the time".format(100 * (lambda_1_samples < lambda_2_samples).mean()))

for d in [1,2,5,10]:
    v = (abs(lambda_1_samples - lambda_2_samples) >= d).mean()
    print("What is the probability the difference is larger than {0}?: {1:.4f}%".format(d, v * 100))

exp_lift = ((lambda_2_samples - lambda_1_samples) / lambda_1_samples).mean()
print("Expected lift: {0:.4f}%".format(exp_lift * 100.))


"""
Author : Achintya Gupta
Date Created : 22-09-2020
"""

"""
Problem Statement
------------------------------------------------
Q)  A Pythagorean triplet is a set of three natural numbers, a < b < c, for which,
    a2 + b2 = c2
    For example, 32 + 42 = 9 + 16 = 25 = 52.
    There exists exactly one Pythagorean triplet for which a + b + c = 1000.
    Find the product abc.
"""

from utils import timing_decorator
import numpy as np

@timing_decorator
def pythagorean_triplet(N=1000):
    a,b,c = 0,0,0
    for i in range(1,N):
        b = i
        a = (N/2)*(1+(b/(b-N)))
        c = N-a-b
        if (a-int(a)==0) and (a>0 and b>0 and c>0 and a<b<c):
            print('a=', int(a),'; b=', int(b),'; c=', int(c), ' : ', int(a*b*c))

pythagorean_triplet(N=1000)


#!/usr/bin/env python3
"""
Hyperparameter Tuning module
"""
import numpy as np

class GaussianProcess:
    """
    Represents a noiseless 1D Gaussian process
    """
    def __init__(self, X_init, Y_init, l=1, sigma_f=1):
        """
        Class constructor

        X_init is a numpy.ndarray of shape (t, 1) representing the inputs
        already sampled with the black-box function
        Y_init is a numpy.ndarray of shape (t, 1) representing the outputs of
        the black-box function for each input in X_init
        t is the number of initial samples
        l is the length parameter for the kernel
        sigma_f is the standard deviation given to the output of the black-box
        function
        Sets the public instance attributes X, Y, l, and sigma_f corresponding
        to the respective constructor inputs
        Sets the public instance attribute K, representing the current
        covariance kernel matrix for the Gaussian process
        """
        self.X = X_init
        self.Y = Y_init
        self.l = l
        self.sigma_f = sigma_f
        self.K = self.kernel(self.X, self.X)

    def kernel(self, X1, X2):
        """
        Calculates the covariance kernel matrix between two matrices:

        X1 is a numpy.ndarray of shape (m, 1)
        X2 is a numpy.ndarray of shape (n, 1)
        the kernel should use the Radial Basis Function (RBF)

        Returns: the covariance kernel matrix as a numpy.ndarray of shape(m, n)
        """
        sqdist = (np.sum(X1**2, 1).reshape(-1, 1) + np.sum(X2**2, 1) - 2
                  * np.dot(X1, X2.T))
        return self.sigma_f ** 2 * np.exp(-0.5 / self.l ** 2 * sqdist)


import numpy as np
import matplotlib.pyplot as plt
from numpy.linalg import inv
from numpy import linalg as alg

# create the space and function
xList = np.linspace(-2.5, 2.5, 1000)
yList = np.linspace(-2.5, 2.5, 1000)
XP,YP = np.meshgrid(xList, yList)
ZP = XP**2 + 2 * YP**2

# create gradient and hessian of f
G = np.array([[2],[4]])
H = np.array([[2,0],[0,4]])

# set the staring position
X = np.array([[2.0],[2.0]])

# set the epsilon for stopping condition
eps = 1e-5

# create empty list to store points
x_list = [X[0]]
y_list = [X[1]]

iter = 0
while True:

    # increase the iteration number
    iter = iter + 1

    # compute the gradient vector
    GF = G * X

    # print some information
    print("Iter: ", iter, "Norm: ", alg.norm(GF))

    # if the stopping condition is satisfied, break the loop
    if alg.norm(GF) < eps:
        break

    # choose steepest descent direction as the negative gradient
    d = -GF

    # exact line search
    # method = 'exact_line_search'
    # eta1 = (d.T.dot(H.dot(X)))
    # eta2 = (d.T.dot(H.dot(d)))
    # eta = -(d.T.dot(H.dot(X))) / (d.T.dot(H.dot(d)))

    # small fixed step size
    method = 'small_fixed_step_size'
    eta = 0.05

    # do the computataion
    X = X + eta * d

    # add the results to the list
    x_list.append(X[0])
    y_list.append(X[1])

# create the plots
fig = plt.figure()
ax = fig.add_subplot(111)

# illustrate the function using contours
plt.contour(XP, YP, ZP, 30, alpha=0.7, zorder=1)
plt.xlabel('x')
plt.ylabel('y')
ax.set_aspect('equal')

# plot the steps
plt.plot(x_list, y_list, ls='-', color='royalblue', marker='o', mfc='orange', mec='orange', linewidth=3, markersize=5, zorder=2)
plt.scatter(x_list[0], y_list[0], c='black', s=30, zorder=3)

# save the result
filename = method + '_eta_' + str(eta) + '_iter_' + str(iter) + '.png'
plt.savefig(filename, bbox_inches='tight', dpi=300)

# display the results
plt.show()


from __future__ import division
import numpy as np
import cosmology as cosmo
from scipy.spatial import cKDTree as KDT

_CosPar={'Omega_M':0.3, 'Omega_L':0.7, 'Omega_b':0.045, 'Omega_nu':1e-5, 'n_degen_nu':3., 'h':0.7, 'sigma_8':0.8, 'ns':0.96}

def spherical_to_cartesian(scoord):
    """
    http://mathworld.wolfram.com/SphericalCoordinates.html
    scoord[nobj,3]
    0: r - distance
    1: ra - azimuthal, in deg
    2: dec - pi/2-polar, in deg
    """
    x = scoord[:,0]*np.cos(scoord[:,1]/180.*np.pi)*np.cos(scoord[:,2]/180.*np.pi)
    y = scoord[:,0]*np.sin(scoord[:,1]/180.*np.pi)*np.cos(scoord[:,2]/180.*np.pi)
    z = scoord[:,0]*np.sin(scoord[:,2]/180.*np.pi)
    return np.c_[x,y,z]

def cartesian_to_spherical(ccoord):
    """
    ccoord[nobj,3]
    0: x
    1: y
    2: z
    """
    r = np.sqrt(np.sum(np.power(ccoord,2)))
    ra = np.arctan2(ccoord[:,1], ccoord[:,0])/np.pi*180.
    if r != 0.0:
       dec = np.arcsin(ccoord[:,2]/r)/np.pi*180.
    else:
       dec = 0.*np.sign(ccoord[:,2])/np.pi*180.
    return np.c_[r, ra, dec]

def radecz_to_comoving(zcoord):
    """
    zcoord[nobj,3]
    0: z - redshift
    1: ra - azimuthal
    2: dec - pi/2-polar
    """
    # use transverse comoving distance dM instead?
    dc = cosmo.comoving_distance(zcoord[:,0], _CosPar)
    scoord = np.c_[dc, zcoord[:,1], zcoord[:,2]]
    return spherical_to_cartesian(scoord)

def comoving_neighbors(zcoord1, zcoord2, nthneighbor=1, maxscale=None):
    """
    """
    ccoord1 = radecz_to_comoving(zcoord1)
    ccoord2 = radecz_to_comoving(zcoord2)
    kdt = KDT(ccoord2)
    if nthneighbor == 1:
        indx2 = kdt.query(ccoord1)[1]
    elif nthneighbor > 1:
        indx2 = kdt.query(ccoord1, nthneighbor)[1][:, -1]
    else:
        raise ValueError('{0}th neighbor cannot be found'.format(nthneighbor))

    ds = np.linalg.norm(ccoord1-ccoord2[indx2,:], axis=1)
    indx1 = np.arange(ds.size)
    if maxscale != None:
       iselect = ds < maxscale
       indx1 = indx1[iselect]
       indx2 = indx2[iselect]
       ds = ds[iselect]

    return indx1, indx2, ds


import numpy as np

def pol_to_cart(deg, radius=1.0):
    # This method transforms degree to cartesian values
    # deg is a value in the range [-180, 180]

    # conveting deg to rad
    rad = np.array(deg) * (np.pi / 180.)
    x = radius * np.cos(rad)
    y = radius * np.sin(rad)
    return [x, y]

def cart_to_pol(x, y):
    # This method takes x, y values and convert
    # it to the degree in the range [-180, 180]
    deg = np.arctan2(y, x) * 180 / np.pi
    return deg


import numpy

C_3 = numpy.array([1, 2]) / 3
a_3 = numpy.array([[3, -1], [1, 1]]) / 2
sigma_3 = numpy.array([[[1, 0], [-2, 1]], [[1, 0], [-2, 1]]])

C_5 = numpy.array([1, 6, 3]) / 10
a_5 = numpy.array([[11, -7, 2], [2, 5, -1], [-1, 5, 2]]) / 6
sigma_5 = numpy.array([[[40, 0, 0],
                        [-124, 100, 0],
                        [44, -76, 16] ],
                       [[16, 0, 0],
                        [-52, 52, 0],
                        [20, -52, 16] ],
                       [[16, 0, 0],
                        [-76, 44, 0],
                        [100, -124, 40] ] ]) / 12

C_all = { 2 : C_3,
          3 : C_5 }
a_all = { 2 : a_3,
          3 : a_5 }
sigma_all = { 2 : sigma_3,
              3 : sigma_5 }

def weno3_upwind(q):
    order = 2
    epsilon = 1e-16
    alpha = numpy.zeros(order)
    beta = numpy.zeros(order)
    q_stencils = numpy.zeros(order)
    for k in range(order):
        for l in range(order):
            for m in range(l):
                beta[k] += sigma_3[k, l, m] * q[1 + k - l] * q[1 + k - m]
        alpha[k] = C_3[k] / (epsilon + beta[k]**2)
        for l in range(order):
            q_stencils[k] += a_3[k, l] * q[1 + k - l]
    w = alpha / numpy.sum(alpha)

    return numpy.dot(w, q_stencils)

def weno3(q, simulation):
    Nvars, Npoints = q.shape
    q_minus = numpy.zeros_like(q)
    q_plus  = numpy.zeros_like(q)
    for i in range(2, Npoints-2):
        for Nv in range(Nvars):
            q_plus [Nv, i] = weno3_upwind(q[Nv, i-1:i+2])
            q_minus[Nv, i] = weno3_upwind(q[Nv, i+1:i-2:-1])
    return q_minus, q_plus

def weno5_upwind(q):
    order = 3
    epsilon = 1e-16
    alpha = numpy.zeros(order)
    beta = numpy.zeros(order)
    q_stencils = numpy.zeros(order)
    for k in range(order):
        for l in range(order):
            for m in range(l):
                beta[k] += sigma_5[k, l, m] * q[2 + k - l] * q[2 + k - m]
        alpha[k] = C_5[k] / (epsilon + beta[k]**2)
        for l in range(order):
            q_stencils[k] += a_5[k, l] * q[2 + k - l]
    w = alpha / numpy.sum(alpha)

    return numpy.dot(w, q_stencils)

def weno5(q, simulation):
    Nvars, Npoints = q.shape
    q_minus = numpy.zeros_like(q)
    q_plus  = numpy.zeros_like(q)
    for i in range(3, Npoints-3):
        for Nv in range(Nvars):
            q_plus [Nv, i] = weno5_upwind(q[Nv, i-2:i+3])
            q_minus[Nv, i] = weno5_upwind(q[Nv, i+2:i-3:-1])
    return q_minus, q_plus

def weno_upwind(q, order):
    a = a_all[order]
    C = C_all[order]
    sigma = sigma_all[order]
    epsilon = 1e-16
    alpha = numpy.zeros(order)
    beta = numpy.zeros(order)
    q_stencils = numpy.zeros(order)
    for k in range(order):
        for l in range(order):
            for m in range(l):
                beta[k] += sigma[k, l, m] * q[order-1+k-l] * q[order-1+k-m]
        alpha[k] = C[k] / (epsilon + beta[k]**2)
        for l in range(order):
            q_stencils[k] += a[k, l] * q[order-1+k-l]
    w = alpha / numpy.sum(alpha)

    return numpy.dot(w, q_stencils)

def weno(q, simulation, order):
    Nvars, Npoints = q.shape
    q_minus = numpy.zeros_like(q)
    q_plus  = numpy.zeros_like(q)
    for i in range(order, Npoints-order):
        for Nv in range(Nvars):
            q_plus [Nv, i] = weno_upwind(q[Nv, i+1-order:i+order], order)
            q_minus[Nv, i] = weno_upwind(q[Nv, i+order-1:i-order:-1], order)
    return q_minus, q_plus


# -*- coding: utf-8 -*-
"""
Created on Sat Dec 19 22:47:49 2020

@author: Alex Fang
"""
import numpy as np
import pandas as pd
from math import log, sqrt, exp, pi
from scipy.stats import norm
import matplotlib.pyplot as plt

#Reference market data: Book, Options, Futures, and Other Derivatives, 8th edition,
#The Charpter: The Greek Letters, Page 377-396
S0=49
K=50
T=0.3846 #20weeks, unit: year
r=0.05 #unit: year
sigma=0.20

#Class Option Pricing
class Option_Price():

    def init(self):
        pass

    #Function set 1, B-S model
    def BSM(self,S0, K, T, r, sigma, type_):

        d1 = (log(S0/K) + (r + 0.5 * sigma**2) * T) / sigma / sqrt(T)
        d2 = d1 - sigma * sqrt(T)
        Callprice = S0 * norm.cdf(d1) - K * exp(-r*T) * norm.cdf(d2)
        Putprice = -S0 * norm.cdf(-d1) + K*exp(-r*T)* norm.cdf(-d2)
        if type_=="call" or type_=="Call":
            print("This Call Option Price is: %.4f" % Callprice)
        elif type_=="put" or type_=="Put" :
            print("This Put Option Price is: %.4f" % Putprice)
        else:
            print("No match, check your type...")

    #Function set 2, sensitive analysis on its important variables.
    #Function set 3, some visualization tools
    def Delta_to_S(self,S0, K, r, sigma):

        Call=[]
        Put=[]
        for S in range(30,80):
            Call.append(norm.cdf((log(S/K) + (r + 0.5 * sigma**2) * T) / sigma / sqrt(T)))
            Put.append(norm.cdf((log(S/K) + (r + 0.5 * sigma**2) * T) / sigma / sqrt(T))-1)

        plt.plot(range(len(Put)),Put,label='Put')
        plt.plot(range(len(Call)),Call,label='Call')
        plt.xlabel('S0')
        plt.ylabel('Delta')
        plt.legend(loc='upper right')
        plt.savefig("./fig/Delta_to_S.png")
        plt.close()

    def Delta_to_T(self,S0, K, r, sigma):

        In_the_time=[]
        At_the_time=[]
        Out_of_time=[]
        for T1 in range(1,200):
            T1/=100
            In_the_time.append(norm.cdf((log(S0/(S0-10)) + (r + 0.5 * sigma**2) * T1) / sigma / sqrt(T1)))
            At_the_time.append(norm.cdf((log(S0/(S0)) + (r + 0.5 * sigma**2) * T1) / sigma / sqrt(T1)))
            Out_of_time.append(norm.cdf((log(S0/(S0+10)) + (r + 0.5 * sigma**2) * T1) / sigma / sqrt(T1)))

        plt.plot(range(len(In_the_time)),In_the_time,label='In_the_time')
        plt.plot(range(len(In_the_time)),At_the_time,label='At_the_time')
        plt.plot(range(len(In_the_time)),Out_of_time,label='Out_of_time')
        plt.xlabel('T')
        plt.ylabel('Delta')
        plt.legend(loc='upper right')
        plt.savefig("./fig/Delta_to_T.png")
        plt.close()

    def Theta(self,S0, K,T, r, sigma, type_,plot=False):

        d1 = (log(S0/K) + (r + 0.5 * sigma**2) * T) / sigma / sqrt(T)
        d2 = d1 - sigma * sqrt(T)
        def n_1(x):
            return 1/sqrt(2*pi)*exp(-x**2/2)

        Call=-S0*n_1(d1)*sigma/2/sqrt(T)-r*K*exp(-r*T)*norm.cdf(d2)
        Put=-S0*n_1(d1)*sigma/2/sqrt(T)+r*K*exp(-r*T)*norm.cdf(-d2)

        if plot:
            return Call

        if type_=="call" or type_=="Call":
            print("This Call Option's theta is : %.4f" % Call)
        elif type_=="put" or type_=="Put" :
            print("This Put Option's theta is : %.4f" % Put)
        else:
            print("No match, check your type...")

    def Theta_to_S(self,S0, K,T, r, sigma):

        Call=[]
        x=[]
        for S in range(30,80):
            x.append(S)
            Call.append(self.Theta(S, K, T, r, sigma, 'call',plot=True))

        plt.plot(x,Call,label='Call: Theta')
        plt.plot([K]*10,np.array(range(1,101,10))/-10,'-.',label='K')
        plt.xlabel('S0')
        plt.ylabel('Theta')
        plt.legend(loc='upper right')
        plt.savefig("./fig/Theta_to_S(.png")
        plt.close()

    def Theta_to_T(self,S0, K,T, r, sigma):

        In_the_time=[]
        At_the_time=[]
        Out_of_time=[]
        for T1 in range(1,100):
            T1/=100
            In_the_time.append(self.Theta(S0, S0-2, T1, r, sigma, 'call',plot=True))
            At_the_time.append(self.Theta(S0, S0, T1, r, sigma, 'call',plot=True))
            Out_of_time.append(self.Theta(S0, S0+2, T1, r, sigma, 'call',plot=True))

        plt.plot(range(len(In_the_time)),In_the_time,label='In_the_time: Theta')
        plt.plot(range(len(In_the_time)),At_the_time,label='At_the_time: Theta')
        plt.plot(range(len(In_the_time)),Out_of_time,label='Out_of_time: Theta')
        plt.xlabel('T')
        plt.ylabel('Theta')
        plt.legend(loc='upper right')
        plt.savefig("./fig/Theta_to_T.png")
        plt.close()

    def Gamma(self,S0, K,T, r, sigma, type_,plot=False):

        d1 = (log(S0/K) + (r + 0.5 * sigma**2) * T) / sigma / sqrt(T)
        d2 = d1 - sigma * sqrt(T)
        def n_1(x):
            return 1/sqrt(2*pi)*exp(-x**2/2)

        Call=n_1(d1)/S0/sigma/sqrt(T)
        Put=-S0*n_1(d1)*sigma/2/sqrt(T)+r*K*exp(-r*T)*norm.cdf(-d2)

        if plot:
            return Call

        if type_=="call" or type_=="Call":
            print("This Call Option's gamma is : %.4f" % Call)
        elif type_=="put" or type_=="Put" :
            print("This Put Option's gamma is : %.4f" % Put)
        else:
            print("No match, check your type...")

    def Gamma_to_T(self,S0, K,T, r, sigma):

        In_the_time=[]
        At_the_time=[]
        Out_of_time=[]
        for T1 in range(1,100):
            T1/=100
            In_the_time.append(self.Gamma(S0, S0-2, T1, r, sigma, 'call',plot=True))
            At_the_time.append(self.Gamma(S0, S0, T1, r, sigma, 'call',plot=True))
            Out_of_time.append(self.Gamma(S0, S0+2, T1, r, sigma, 'call',plot=True))

        plt.plot(range(len(In_the_time)),In_the_time,label='In_the_time: Gamma')
        plt.plot(range(len(In_the_time)),At_the_time,label='At_the_time: Gamma')
        plt.plot(range(len(In_the_time)),Out_of_time,label='Out_of_time: Gamma')
        plt.xlabel('T')
        plt.ylabel('Gamma')
        plt.legend(loc='upper right')
        plt.savefig("./fig/Gamma_to_T.png")
        plt.close()

    def Vega(self,S0, K,T, r, sigma, type_,plot=False):

        d1 = (log(S0/K) + (r + 0.5 * sigma**2) * T) / sigma / sqrt(T)
        d2 = d1 - sigma * sqrt(T)
        def n_1(x):
            return 1/sqrt(2*pi)*exp(-x**2/2)

        Call=S0*sqrt(T)*n_1(d1)
        Put=S0*sqrt(T)*n_1(d1)

        if plot:
            return Call

        if type_=="call" or type_=="Call":
            print("This Call Option's Vega is : %.4f" % Call)
        elif type_=="put" or type_=="Put" :
            print("This Put Option's Vega is : %.4f" % Put)
        else:
            print("No match, check your type...")

    def Vega_to_S(self,S0, K,T, r, sigma):

        Call=[]
        x=[]
        for S in range(30,80):
            x.append(S)
            Call.append(self.Vega(S, K, T, r, sigma, 'call',plot=True))

        plt.plot(x,Call,label='Call: Vega')
        plt.plot([K]*10,np.array(range(1,201,20))/10,'-.',label='K')
        plt.xlabel('S0')
        plt.ylabel('Vega')
        plt.legend(loc='upper right')
        plt.savefig("./fig/Vega_to_S.png")
        plt.close()

    def Rho(self,S0, K,T, r, sigma, type_,plot=False):

        d1 = (log(S0/K) + (r + 0.5 * sigma**2) * T) / sigma / sqrt(T)
        d2 = d1 - sigma * sqrt(T)
        def n_1(x):
            return 1/sqrt(2*pi)*exp(-x**2/2)

        Call=K*T*exp(-r*T)*norm.cdf(d2)
        Put=-K*T*exp(-r*T)*norm.cdf(-d2)

        if plot:
            return Call

        if type_=="call" or type_=="Call":
            print("This Call Option's Rho is : %.4f" % Call)
        elif type_=="put" or type_=="Put" :
            print("This Put Option's Rho is : %.4f" % Put)
        else:
            print("No match, check your type...")

    def Real_example(self):

        #calculate the table in excel
        data=pd.read_excel("./Portfolio.xlsx").iloc[1:]
        data=data[data['Type']=='Put']
        data.index=range(len(data))
        S0=4.572
        t0='2020-09-30'
        from datetime import datetime
        input_columns=['S0','T','r','K','sigma']
        input_data=np.zeros((len(data),5))
        input_data[:,0]=np.array([S0]*len(data)).reshape(-1,)
        temp=[]
        for i in range(len(data)):
            temp.append(np.abs((data['Maturity'][i]-datetime.strptime(t0, '%Y-%m-%d')).days)/252)

        input_data[:,1]=np.array(temp).reshape(-1,)
        input_data[:,2]=np.array(data['Rate']-data['SSRate']).reshape(-1,)
        input_data[:,3]=np.array(data['Strike']).reshape(-1,)
        input_data[:,4]=np.array(data['ActVol']).reshape(-1,)

        d1 = (np.log(input_data[:,0]/input_data[:,3]) + (input_data[:,2] + 0.5 * input_data[:,4]**2) * input_data[:,1]) / input_data[:,4] / np.sqrt(input_data[:,1])
        d2 = d1 - input_data[:,4] * np.sqrt(input_data[:,1])
        delta = norm.cdf(d1)-1
        delta=delta*data['NetPos']*data['Multi']

        cash_delta=delta*S0

        def n_1(x):
            return 1/np.sqrt(2*pi)*np.exp(-np.square(x)/2)

        vega=input_data[:,0]*np.sqrt(input_data[:,1])*n_1(d1)
        vega*=data['NetPos']
        vega*=data['Multi']

        gamma=input_data[:,0]*n_1(d1)*input_data[:,4]/2/np.sqrt(input_data[:,1])-input_data[:,2]*input_data[:,3]*np.exp(-input_data[:,2]*input_data[:,1])*norm.cdf(-d2)
        gamma*=data['NetPos']
        gamma*=data['Multi']

        new_d1=(np.log(input_data[:,0]*1.01/input_data[:,3]) + (input_data[:,2] + 0.5 * input_data[:,4]**2) * input_data[:,1]) / input_data[:,4] / np.sqrt(input_data[:,1])
        new_cash_delta = (norm.cdf(new_d1)-1)*data['NetPos']*data['Multi']*S0
        #the change rate of 0.01S
        cash_gamma=gamma*np.sqrt(input_data[:,0])/100

        theta=-input_data[:,0]*n_1(d1)*input_data[:,4]/2/np.sqrt(input_data[:,1])+input_data[:,2]*input_data[:,3]*np.exp(-input_data[:,2]*input_data[:,1])*norm.cdf(-d2)
        theta=theta*data['Multi']*data['NetPos']/252

        summary=pd.DataFrame()
        summary['Delta']=delta
        summary['CashDelta']=cash_delta
        summary['Vega']=vega/100
        summary['Gamma']=gamma
        summary['CashGamma']=cash_gamma
        summary['Theta']=theta
        return summary

    def Real_example2(self):

        #calculate the change of greek letters when S0 and Volatility changes
        raw=pd.read_excel("./Portfolio.xlsx")
        raw.index=range(len(raw))
        S0=4.572
        t0='2020-09-30'
        from datetime import datetime

        def calculate_greek(a,b,type_):

            data=raw[raw['Type']==type_]
            data.index=range(len(data))
            input_columns=['S0','T','r','K','sigma']
            input_data=np.zeros((len(data),5))
            input_data[:,0]=S0
            #change the underlying assets S0
            input_data[:,0]*=a
            temp=[]
            for i in range(len(data)):
                temp.append(np.abs((data['Maturity'][i]-datetime.strptime(t0, '%Y-%m-%d')).days)/252)

            input_data[:,1]=np.array(temp).reshape(-1,)
            input_data[:,2]=np.array(data['Rate']-data['SSRate']).reshape(-1,)
            input_data[:,3]=np.array(data['Strike']).reshape(-1,)
            input_data[:,4]=np.array(data['ActVol']).reshape(-1,)
            #change the underlying assets S0
            input_data[:,4]+=b

            d1 = (np.log(input_data[:,0]/input_data[:,3]) + (input_data[:,2] + 0.5 * input_data[:,4]**2) * input_data[:,1]) / input_data[:,4] / np.sqrt(input_data[:,1])
            d2 = d1 - input_data[:,4] * np.sqrt(input_data[:,1])
            if type_=='Put':
                delta = norm.cdf(d1)-1
            elif type_=='Call':
                delta = norm.cdf(d1)

            delta=delta*data['NetPos']*data['Multi']

            cash_delta=delta*S0

            def n_1(x):
                return 1/np.sqrt(2*pi)*np.exp(-np.square(x)/2)

            vega=input_data[:,0]*np.sqrt(input_data[:,1])*n_1(d1)*data['NetPos']*data['Multi']
            gamma=n_1(d1)/input_data[:,0]/input_data[:,4]/np.sqrt(input_data[:,1])
            gamma=gamma*data['NetPos']*data['Multi']

            '''
            new_d1=(np.log(input_data[:,0]*1.01/input_data[:,3]) + (input_data[:,2] + 0.5 * input_data[:,4]**2) * input_data[:,1]) / input_data[:,4] / np.sqrt(input_data[:,1])
            if type_=='Put':
                new_cash_delta = (norm.cdf(new_d1)-1)*data['NetPos']*data['Multi']*S0*1.01
            elif type_=='Call':
                new_cash_delta = norm.cdf(new_d1)*data['NetPos']*data['Multi']*S0*1.01

            #the change rate of 0.01S
            cash_gamma=new_cash_delta-cash_delta
            '''
            cash_gamma=gamma*np.square(input_data[:,0])/100

            if type_=='Put':
                theta=-input_data[:,0]*n_1(d1)*input_data[:,4]/2/np.sqrt(input_data[:,1])+input_data[:,2]*input_data[:,3]*np.exp(-input_data[:,2]*input_data[:,1])*norm.cdf(-d2)
            elif type_=='Call':
                theta=-input_data[:,0]*n_1(d1)*input_data[:,4]/2/np.sqrt(input_data[:,1])-input_data[:,2]*input_data[:,3]*np.exp(-input_data[:,2]*input_data[:,1])*norm.cdf(d2)

            theta=theta/252*data['NetPos']*data['Multi']
            summary=pd.DataFrame()
            summary['Delta']=delta
            summary['CashDelta']=cash_delta
            summary['Vega']=vega/100
            summary['Gamma']=gamma
            summary['CashGamma']=cash_gamma
            summary['Theta']=theta

            return [np.sum(summary['CashDelta']),np.sum(summary['CashGamma']),np.sum(summary['Theta']),np.sum(summary['Vega'])]

        m1,m2,m3,m4,ind_=[],[],[],[],[]
        for a in [1,1.05,1.1,1.2,0.95,0.9,0.8]:
            for b in [0.1,0.2,-0.1,-0.2]:
                ans=np.array(calculate_greek(a,b,'Put'))+np.array(calculate_greek(a,b,'Call'))
                x1=(ans[0]+raw['CashDelta'][0])/10000
                x2=(ans[1]+raw['CashGamma'][0])/10000
                x3=(ans[2]+raw['Theta'][0])/10000
                x4=(ans[3]+raw['Vega'][0])/10000
                ind="SO changes "+"%.2f"%(a-1)+", Vol changes "+str(b)
                ind_.append(ind)
                m1.append(x1)
                m2.append(x2)
                m3.append(x3)
                m4.append(x4)

        res=pd.DataFrame()
        res['Sensitivity']=ind_
        res['CashDelta']=m1
        res['CashGamma']=m2
        res['Theta']=m3
        res['Vega']=m4

        return res

#Just use BS model to calculate the option price
Calculator=Option_Price()
Calculator.BSM(S0, K, T, r, sigma, 'call')
#Calculate the Greek Letters, and plot all the pictures shown in the text book
#For the charpter, the greek letters.
Calculator.Delta_to_S(S0, K, r, sigma)
Calculator.Delta_to_T(S0, K, r, sigma)
Calculator.Theta(S0, K, T, r, sigma,type_='Call')
Calculator.Theta_to_S(S0, K, T, r, sigma)
Calculator.Theta_to_T(S0, K, T, r, sigma)
Calculator.Gamma(S0, K, T, r, sigma,type_='Call')
Calculator.Gamma_to_T(S0, K, T, r, sigma)
Calculator.Vega(S0, K, T, r, sigma,type_='Call')
Calculator.Vega_to_S(S0, K, T, r, sigma)
Calculator.Rho(S0, K, T, r, sigma,type_='Call')
#Use BS model to calculate Greek Letters
#The data is based on a portfolio, given by a stock company.
summary=Calculator.Real_example()
#Use BS model to calculate Greek Letters, and do pressure test
#Test it on different underlying assets price S0, and volatility sigma.
summary=Calculator.Real_example2()


#simple k-means
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')
plt.rcParams['axes.facecolor']='w'
from matplotlib.lines import Line2D
class KMeans:
    '''
    k = number of groups / clusters / ... (group)
    tolerance = acceptable level of variation in precision (tol)
    Iteration : repetition of process
    '''
    ##you could use another tolerance stop limits as :
    #error : (actual-forecast/forecast)*100
    #accuracy : 1-error
    #Note : centroid, center of mass and geometric center could be different.
    def __init__(self, group=2, maxTolerance=0.001, iteration=300):
        self.k = group
        self.tol = maxTolerance
        self.iteration = iteration
        self.fig = plt.figure('K-Means PLOT',figsize=(9, 6))
        self.ax = self.fig.add_subplot(111)#1*1 grid , no.1
        self.colors = 200*["r","g","b","k","c"]
    def fit(self,data):
        self.centroids = {}
        #start with first k data as centroids
        self.centroids={i:data[i] for i in range(self.k)}
        for _ in range(self.iteration):
            self.classes={i:[] for i in range(self.k)}
            for j in data:#j : featureset
                distances = [np.linalg.norm(j-self.centroids[i]) for i in self.centroids]
                self.classes[np.argmin(distances)].append(j)#min as cluster
            pc = self.centroids #pc : prev_centroids
            self.centroids={i:np.average(self.classes[i],axis=0) for i in self.classes}
            print(self.centroids)
            op=[False for c in self.centroids if np.sum(self.centroids[c]-pc[c]) > self.tol]
            if op==[] : break #not op : optimum
    def predict(self,data):
        distances = [np.linalg.norm(data-self.centroids[i]) for i in self.centroids]
        self.ax.scatter(data[0], data[1], marker="*",
                    color=self.colors[np.argmin(distances)], s=150, linewidths=2)
        return np.argmin(distances)
    def visualize(self):
        for centroid in clf.centroids:
            self.ax.scatter(clf.centroids[centroid][0], clf.centroids[centroid][1],
                            marker="$C$", color="k", s=100, linewidths=2)
        for j in clf.classes:
            [plt.scatter(i[0],i[1],marker="x",color=self.colors[j],s=150,linewidth=2) for i in clf.classes[j]]
        self.ax.set_title('K-Means clustering, untagged data',fontsize=14)
        self.ax.set_xlabel('X1',fontsize=12)
        self.ax.set_ylabel('X2',fontsize=12)
        customLines = [Line2D([0], [0], color='w', marker='*',
                               markersize=15,markerfacecolor='k'),
                        Line2D([0], [0], color='w', marker='$x$',
                               markersize=15,markerfacecolor='k'),
                       Line2D([0], [0], color='w', marker='$C$',
                               markersize=15,markerfacecolor='k')]
        self.ax.legend(customLines,['new data','data','Center'],
                       loc='upper center', shadow=True)
#define input data
X = np.array([[1,4],[1.5,1.8],[7,7],[8,8],
              [1,0.6],[9,9],[0,2],[8,6],
              [0,1],[3,8],[2,10],[0,10],[1,8],
              [2,8]])
#call Kmeans functions
clf = KMeans(group=3,maxTolerance=0.001, iteration=300)
clf.fit(X)
clf.visualize()
newData = np.array([[1,3],[8,4],[0,3],[4,4],[3,6],[6,6],[4.5,7],[4.6,7]])
for unknown in newData:
    clf.predict(unknown)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Sun Feb 28 19:38:58 2016
similarity
@author: liudiwei
"""

import numpy as np

#欧几里得距离，使用1/(1+distance)归一化
def eulisSim(matA, matB):
    return 1.0/(1.0 + np.linalg.norm(matA - matB))

#pearson相关系数，使用0.5+0.5*corrcof()将数值归一化
def pearsSim(matA, matB):
    if len(matA) < 3:
        return 1.0
    return 0.5 + 0.5 * np.corrcoef(matA, matB, rowvar = 0)[0][1]

#余弦相似度，使用0.5+0.5*cos(theta)归一化
def cosSim(matA, matB):
    val1 = float(matA * matB.T)
    val2 = np.linalg.norm(matA) * np.linalg.norm(matB)
    cos_theta = val1/val2
    return 0.5 + 0.5 * cos_theta

if __name__=="__main__":
    matA = np.mat([1,2,3,4,5])
    matB = np.mat([2,3,4,5,6])
    edist = eulisSim(matA, matB)  #output: 0.3090169943749474
    pdist = pearsSim(matA, matB)  #output: 1.0
    cdist = cosSim(matA, matB)    #output: 0.99746833816309111


import numpy as np
import scipy.special as sc

def fact2(n):
    """Double factorial function.

    Parameters
    ----------
    n : int
        Input number of double factorial function

    Raises
    ------
    TypeError
        If input parameter is not an integer.
    """
    if not isinstance(n, int):
        raise TypeError("Input parameter must be an integer")
    if n <= 0:
        return 1
    else:
        return n * fact2(n-2)

def boys(n,t):
    """Boys function for the calculation of coulombic integrals.

    Parameters
    ----------
    n : int
        Order of boys function

    t : float
        Varible for boys function.

    Raises
    ------
    TypeError
        If boys function order is not an integer.

    ValueError
        If boys function order n is not a none negative number.
    """
    if not isinstance(n, int):
        raise TypeError("Boys function order n must be an integer")
    if n < 0:
        raise ValueError("Boys function order n must be a none negative number")
    if not isinstance(t, float):
        raise TypeError("Boys function varible t must be integer or float")
    return sc.hyp1f1(n+0.5,n+1.5,-t)/(2.0*n+1.0)

def gaussian_product_center(a,A,b,B):
    """
    """
    A = np.array(A)
    B = np.array(B)
    return (a*A+b*B)/(a+b)

def eint(a,b,c,d):
    """
    """
    if a > b: ab = a*(a+1)/2 + b
    else: ab = b*(b+1)/2 + a
    if c > d: cd = c*(c+1)/2 + d
    else: cd = d*(d+1)/2 + c
    if ab > cd: abcd = ab*(ab+1)/2 + cd
    else: abcd = cd*(cd+1)/2 + ab
    return int(abcd)


import numpy as np

def greenTheorem(array):
    array = array[array[:, 0].argsort()]
    x_vector = array[:,0]
    y_vector = array[:,1]

    rolled_x_vector = np.roll(x_vector.copy(), shift=1)
    rolled_y_vector = np.roll(y_vector.copy(), shift=1)

    return 0.5 * (( rolled_x_vector @  y_vector) - ( rolled_y_vector @ x_vector))


import numpy as np
import matplotlib.pyplot as plt

N = 100
D = 2

X = np.random.rand(N, D)

X[:50, :] = X[:50, :] - 2*np.ones((50, D))
X[50:, :] = X[50:, :] + 2*np.ones((50, D))

T = np.array([0]*50 + [1]*50)

ones = np.array([[1]*N]).T
Xb = np.concatenate((ones, X), axis=1)
w = np.random.rand(D + 1)

Z = Xb.dot(w)

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

Y = sigmoid(Z)

def cross_entropy(T, Y):
    E = 0
    for i in range(N):
        if T[i] == 1:
            E -= np.log(Y[i])
        else:
            E -= np.log(1 - Y[i])
    return E

print(cross_entropy(T, Y))

# Discriminant line
plt.scatter(X[:, 0], X[:, 1], c=T, alpha=0.5, s=100)
x_axis = np.linspace(-6, 6, 100)
y_axis = -x_axis
plt.plot(x_axis, y_axis)
plt.show()

# GD

learning_rate = 0.1
for i in range(10000):
    if i % 10 == 0:
        print(cross_entropy(T, Y))

    w += learning_rate * Xb.T.dot(T - Y)
    Y = sigmoid(Xb.dot(w))

print("Final w: ", w)


import numpy as np
import matplotlib.pyplot as plt

def DFT(x): # To calculate DFT of a 1D real-valued signal x

    N = len(x)
    n = np.arange(N)
    k = n.reshape((N, 1))
    e = np.exp(-2j * np.pi * k * n / N)
    X = np.dot(e, x)
    return X

sr = 8    # sampling rate
ts = 1.0/sr # sampling interval
t = np.arange(0,1,ts)

freq = 1.
x = [1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4]

X = DFT(x)

# calculate the frequency
N = len(X)
n = np.arange(N)
T = N/sr
freq = n/T

n_oneside = N//2
# get the one side frequency
f_oneside = freq[:n_oneside]

# normalize the amplitude
X_oneside =X[:n_oneside]/n_oneside

plt.figure(figsize = (12, 6))
plt.subplot(121)
plt.stem(f_oneside, abs(X_oneside), 'b', \
         markerfmt=" ", basefmt="-b")
plt.xlabel('Freq (Hz)')
plt.ylabel('DFT Amplitude |X(freq)|')

plt.subplot(122)
plt.stem(f_oneside, abs(X_oneside), 'b', \
         markerfmt=" ", basefmt="-b")
plt.xlabel('Freq (Hz)')
plt.xlim(0, 20)
plt.tight_layout()
plt.show()


# coding=utf-8
"""Logistic loss objective function."""

import numpy as np

def logistic_loss(y, y_1):
    """Calculate logistic loss.

    Formula:
        LL = E(yln(1 + e^-y_1) + (1-y)ln(1+e^y_1))

    Args:
        y: observed features.
        y_1: predicted features.

    Returns:
        calculated logistic loss.
    """
    return np.sum(y * np.log(1 + np.exp(-y_1)) + (-y + 1) * (np.log(1 + np.exp(y_1))))


import numpy as np
import matplotlib.pylab as plt
from mpl_toolkits import mplot3d

AU = 149597870700                     # 1 Unidad Astronómica en metros
G = 6.673*(10**-11)                   # Constante de Gravitación Universal (N*m2/kg2)

earth=np.genfromtxt('EarthOrbit.dat')
Et=np.array(earth[:,0]*31536000)                # El tiempo se pasa de años a segundos
EX=np.array(earth[:,1]*AU)                     # Posición de la Tierra se pasa a metros en x, y y z
EY=np.array(earth[:,2]*AU)
EZ=np.array(earth[:,3]*AU)
Er=np.array(np.sqrt((EX**2)+(EY**2)+(EZ**2)))  # Lista de distancias hasta el sol en cada posición

mars=np.genfromtxt('MarsOrbit.dat')
Mt=np.array(mars[:,0]*31536000)                 # El tiempo se pasa de años a segundos
MX=np.array(mars[:,1]*AU)                      # Posición de Marte se pasa a metros en x, y y z
MY=np.array(mars[:,2]*AU)
MZ=np.array(mars[:,3]*AU)
Mr=np.array(np.sqrt((MX**2)+(MY**2)+(MZ**2)))   # Lista de distancias hasta el sol en cada posición

plt.figure()
ax = plt.axes(projection='3d')
ax.plot3D(EX, EY, EZ,'black',label='Earth')
ax.plot3D(MX, MY, MZ,'blue',label='Mars')
plt.title('ORBITS')
plt.xlabel('X(m)')
plt.ylabel('Y(m)')
plt.legend()
plt.savefig('Orbitas.pdf')
plt.close()

def derivada(x,t):

    D=np.zeros(len(x))

    for i in range(len(t)):
        if(i==len(t)-1):
            D[i]=(x[-1]-x[-2])/(t[-1]-t[-2])
        else:
            D[i]=(x[i+1]-x[i])/(t[i+1]-t[i])

    return D

def velocidad(x,t):
    velocidad = derivada(x,t)
    return velocidad

def aceleracion(x,y,z,t):

    ax=derivada(velocidad(x,t),t)
    ay=derivada(velocidad(y,t),t)
    az=derivada(velocidad(z,t),t)

    lista_aceleraciones = np.sqrt((ax**2)+(ay**2)+(az**2))

    return np.array(lista_aceleraciones)

Ea = aceleracion(EX,EY,EZ,Et)
Ma = aceleracion(MX,MY,MZ,Mt)

def masaSol(a,r):

    m = (a*(r**2))/G                        # Fórmula obtenida después de igualar la ecuación de la fuerza gravitacional con F = m*a

    masa=np.mean(m)

    return masa

E_mSol=masaSol(Ea,Er)
M_mSol=masaSol(Ma,Mr)

print('La masa estimada del Sol obtenida a partir de las posiciones de la Tierra es', E_mSol,' kg y la obtenida a partir de las posiciones de Marte es', M_mSol,' kg')


import numpy as np
a = np.array([0,30,45,60,90])

print('Sine of different angles:')
# Convert to radians by multiplying with pi/180
print (np.sin(a*np.pi/180))
print ('\n')

print ('Cosine values for angles in array:')
print (np.cos(a*np.pi/180))
print ('\n')

print ('Tangent values for given angles:')
print (np.tan(a*np.pi/180))


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

a = "not set"; b = "not set"; A = "not set"; lam = "not set"; phi = "not set"; sigma = "not set"
N = "not set"
for i in range(1,len(sys.argv)):
    if (sys.argv[i] == "-a"):
        a = float(sys.argv[i+1])
        i += 1
    elif (sys.argv[i] == "-b"):
        b = float(sys.argv[i+1])
        i += 1
    elif (sys.argv[i] == "-A"):
        A = float(sys.argv[i+1])
        i += 1
    elif (sys.argv[i] == "-lam"):
        lam = float(sys.argv[i+1])
        i += 1
    elif (sys.argv[i] == "-phi"):
        phi = float(sys.argv[i + 1])*math.pi/180.0
        i += 1
    elif (sys.argv[i] == "-N"):
        N = int(sys.argv[i+1])
        i += 1
    elif (sys.argv[i] == "-sigma"):
        sigma = float(sys.argv[i + 1])
        i += 1

if (a == "not set"):
    a = float(input("Enter low value of x range: "))
if (b == "not set"):
    b = float(input("Enter high value of x range: "))
if (A == "not set"):
    A = float(input("Enter amplitude: "))
if (lam == "not set"):
    lam = float(input("Enter wavelength: "))
if (phi == "not set"):
    phi = float(input("Enter phase angle in degrees: "))*math.pi/180.0
if (sigma == "not set"):
    sigma = float(input("Enter variance as percentage of amplitude: "))
if (N == "not set"):
    N = int(input("Enter number of data points: "))

x = np.linspace(a, b, N)
k = 2*math.pi/lam
y = A*np.sin(k*x + phi)
y = np.random.normal(y,sigma*A,N)

plt.errorbar(x,y,yerr=A*sigma,fmt="o")
plt.savefig("sinusoidPlot2.pdf")

outFile = open("sinusoidPlot2.txt", "w")
for i in range(0,N):
    outFile.write(str(x[i]) + " " + str(y[i]) + " " + str(sigma*A) + "\n")
outFile.close()


import numpy as np

class KMeans:
    clusters: np.ndarray

    def __init__(self,
                 k: int,
                 max_iter: int
                 ) -> None:
        self.k: int = k
        self.max_iter: int = max_iter

    def train(self,
              X: np.ndarray
              ) -> None:

        # Random initialization
        self.clusters = X[np.random.randint(0, len(X), size=self.k)]

        predictions: np.ndarray = self.predict(X)

        # Loop until predictions on next iteration
        # will not change
        iteration: int = 0
        while True or iteration < self.max_iter:
            iteration += 1
            for cluster in range(self.k):
                cluster_elements: np.ndarray = X[predictions == cluster]
                self.clusters[cluster, :] = np.mean(cluster_elements, axis=0)

            new_predictions: np.ndarray = self.predict(X)
            if np.all(new_predictions == predictions):
                break
            else:
                predictions = new_predictions

    def predict(self,
                X: np.ndarray
                ) -> np.ndarray:
        return np.array([np.argmin(np.linalg.norm(self.clusters - x, axis=1)) for x in X])


from __future__ import annotations

import math

import numpy as np

def _circ_inout(v):
    v = np.array(v)
    out = np.empty_like(v)
    mask = v < 0.5
    out[mask] = -0.5 * (np.sqrt(1 - 4 * v[mask] ** 2) - 1)
    mask = ~mask
    out[mask] = np.sqrt(-(v[mask] - 1.5) * (v[mask] - 0.5)) + 0.5
    return out

EASING_FUNCTIONS = {
    "linear": lambda v, a: v,
    "quad_in": lambda v, a: v * v,
    "quad_out": lambda v, a: -v * (v - 2),
    "quad_inout": lambda v, a: np.where(v < 0.5, 2 * v * v, -2 * v * (v - 2) - 1),
    "cubic_in": lambda v, a: v * v * v,
    "cubic_out": lambda v, a: (v - 1) ** 3 + 1,
    "cubic_inout": lambda v, a: np.where(v < 0.5, 4 * v * v * v, 4 * (v - 1) ** 3 + 1),
    "power_in": lambda v, a: v**a,
    "power_out": lambda v, a: 1 - (1 - v) ** a,
    "power_inout": lambda v, a: np.where(
        v < 0.5, (2 * v) ** a / 2, 1.0 - 0.5 * (2 - 2 * v) ** a
    ),
    "sin_in": lambda v, a: 1.0 - np.cos(v * math.pi / 2),
    "sin_out": lambda v, a: np.sin(v * math.pi / 2),
    "sin_inout": lambda v, a: 0.5 * (1 - np.cos(v * math.pi)),
    "exp_in": lambda v, a: 2 ** (a * (v - 1)),
    "exp_out": lambda v, a: 1 - 2 ** (-a * v),
    "exp_inout": lambda v, a: np.where(
        v < 0.5,
        (2 ** (a * (2 * v - 1))) / 2,
        1 - 2 ** (a * (1 - 2 * v) - 1),
    ),
    "circ_in": lambda v, a: -(np.sqrt(1 - v * v) - 1),
    "circ_out": lambda v, a: np.sqrt(-v * (v - 2)),
    "circ_inout": lambda v, a: _circ_inout(v),
}


#!/usr/bin/env python

import math
import numpy as np

def decomp_LTL(A, overwrite=False, zero_upper=True, out=None):
    m, n = A.shape
    assert m == n
    if overwrite:
        L = A
    else:
        if out is not None:
            assert n,n == out.shape
            out[:,:] = 0.0
            L = out
        else:
            L = np.zeros((n,n))

    for i in range(n-1,-1,-1):
        L[i,i] = math.sqrt(A[i,i] - np.dot(L[i+1:,i],L[i+1:,i]))
        for j in range(i):
            L[i,j] = (A[i,j] - np.dot(L[i+1:,i], L[i+1:,j]))/L[i,i]

    if overwrite and zero_upper:
        for i in range(n):
            for j in range(i+1,n):
                L[i,j] = 0.0
    return L

np.set_printoptions(linewidth=160)
n = 6
R = np.random.random((n,n))
A = np.dot(R, R.T)
B = np.copy(A)

L = decomp_LTL(B, overwrite=True)

print('A---------------')
print(A)
print('L---------------')
print(L)

print(np.allclose(A, np.dot(L.T, L)))


# -*- coding: utf-8 -*-
#!/usr/bin/python
# Author: Niam Moltta
# UY - 2017
# MIT License
# Measures of Central Tendency with Python

import math
import numpy as np
import pandas as pd
from scipy import stats
from scipy.stats import norm
import scipy.stats as st
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import re
import seaborn
import scipy.stats

print(' ')
print(' ')
print('                Welcome to MoCT.py')
print('                --by Niam Moltta--')
print ('                    ~~/\//V\ ')
print(' ')
print(' ')
print("Application: MEASURES OF CENTRAL TENDENCY.\n\nINSTRUCTIONS:\n\n- Make sure that the .csv file is in the same folder of this script.\n- To start, enter the name of the file without 'quotes' and ending with .csv\n  Example: scores.csv\n- Enter 'ya' to analyze more columns or quit.\n- Returns measures of central tendency:\n  N, mean, standard deviation, variance, standard error, etc...\n- Returns Normal Distribution graph.\n- Select sample, select point estimate for sampling distribution.\n- Returns z-score and p-value from z-table.\n- Returns Sampling Distribution graph.\n- Returns One tailed T-test: default alpha = 0.05\n- Returns acceptance/rejection of the null hypothesis.\n")

fhand = raw_input('Enter file name: ')

filecsv = str(fhand)

if filecsv == '':
    print(' ')
    print ('Ciao, human!')
    print(' ')
    exit()

data = pd.read_csv(filecsv)

print ' '

frame = pd.DataFrame(data)

coolist = frame.columns
columns = np.asarray(coolist)

while True:

    print ' '
    print 'Columns in', re.findall('(.+?).csv', filecsv), 'are:\n'
    print columns
    print ' '

    hand = raw_input('Enter column header:\n\n')

    column = str(hand)

    if (column == 'ya') | (column == ''):
        print ' '
        print 'Hasta la vista, human.'
        print ' '
        exit()

    else:

        numbers = data[column]

        data[column].fillna(0,inplace=True) # Missing values to zeros.

        A = list()

        for number in numbers :
            value = float(number)
            A.append(value)

        sigma = sum(A) #sumation
        n = len(A) #total of elements
        mean = sigma / n
        Dev = list()
        AbsDev = list()
        SqDev = list()

        for number in A :
            val = number - mean
            Dev.append(val) #Deviation from the mean

        for element in Dev :
            val = abs(element)
            AbsDev.append(val) # Absolute Deviation

        for element in AbsDev :
            val = (element**2)
            SqDev.append(val) #Square Deviations

        SS = sum(SqDev) #Sum of Squares
        Var = SS / n #Variance
        StdDev = math.sqrt(Var) #Standard Deviation
        StdE = StdDev / math.sqrt(n) #Standard Error

        print ('---------------------------------------------')
        print ('MEASURES OF CENTRAL TENDENCY for:'), column
        print (' ')
        print ('N ='), n
        print ('Mean ='), mean
        print ('Sum of Squares ='), SS
        print ('Variance ='), Var
        print ('Standard Deviation ='), StdDev
        print ('Standard Error ='), StdE
        print ('---------------------------------------------')
        print(' ')

        Array = np.asarray(A)
        lista = Array.sort()
        legend = str(column) + ' distribution'

        Mean = np.mean(Array)
        Variance = np.var(Array)
        Sigma = np.sqrt(Variance)

        plt.figure(1)
        plt.hist(Array, bins=20, normed=True)
        plt.xlim((min(Array), max(Array)))

        x = np.linspace(min(Array), max(Array), n)
        fig = plt.plot(x, mlab.normpdf(x,Mean,Sigma))

        plt.title(legend)
        plt.xlabel("Value")
        plt.ylabel("Frequency")
        plt.show(fig)

        print ('To continue, you must save the figure and close it. \nYou can also zoom in it or move the graph to see it better, \nuse the buttons.\n')
        print ' '

    while True:

        sample = raw_input('Enter n value for sample distribution: ')
        fh = str(sample)

        if fh[0] == '#':
            continue

        if (fh == 'ya') | (fh == ''):
            print  ' '
            break

        newn = float(fh)
        standarderror = StdDev / math.sqrt(newn)

        print('----------------------------------------------------------------')
        print 'The Standard Error for', newn,'is: ', standarderror
        print('----------------------------------------------------------------')
        print ' '

        anyvalue = raw_input('Enter point estimate: ')

        if anyvalue[0] == '#':
            continue

        if anyvalue == '':
            print  ' '
            break

        newmean = float(anyvalue)
        rest = newmean - mean
        zscore = rest / standarderror

        print ' '
        print '| The z-score for', anyvalue, 'is:', zscore, '|'
        pvalue = st.norm.cdf(zscore)
        print('---------------------------------------------')
        print '| Z table value =', pvalue, '|'
        print('---------------------------------------------')
        prob = 1 - pvalue
        print 'The probability of getting at least', anyvalue,'is:'
        print ' '
        print 'p =', prob
        print ' '
        print '-------------------------------------------------------------'
        Devs = (1.96 * standarderror)
        print 'Approximately 95% of the sample means fall within', Devs, '\nof', mean, '(Population mean)'
        Dev1 = mean - Devs
        Dev2 = mean + Devs
        print ' '
        print 'The 95% confidence interval is:', Dev1,'<', newmean, '<', Dev2
        print ' '
        Devss = (2.33 * standarderror)

        ''' Some notes for T-test:
         Ho = (null hypothesis):
               Mean = Intervention Mean:
               - The sample mean falls somewhere out the critical region.
         Ha = (alternative hypothesis):
               Mean != Intervention Mean (two-tailed),
               Mean < Intervention Mean (one-tailed - right side),
               Mean > Intervention Mean (one-tailed - left side):
               - The sample mean falls somewhere in the critical region.'''

        Dev3 = mean - Devss
        Dev4 = mean + Devss

        print 'The 98% confidence interval is:', Dev3,'<', newmean, '<', Dev4
        print ' '
        print 'The margin of error is:', Devs, 'for 95% \nand:', Devss,'for 98%'
        print '--------------------------------------------------------------'
        print ' '

        if (zscore < 1.96)&(zscore > -1.96):
            print 'Alpha = 0.05\nHo = Accepted' # Alpha is 0.05 because is used more often
            print ' '
            print '--------------------------------------------------------------'

        else:
            print 'Alpha = 0.05\nHo = Rejected'
            print ' '
            print '--------------------------------------------------------------'

        pdf2 = stats.norm.pdf(Array, mean, standarderror)
        altn = int(newn)
        legen = ("n = "+str(altn))
        fig2 = plt.plot(Array, pdf2, label=legen)
        plt.title("Sampling distribution\nalpha = 0.05")
        plt.xlabel("Value")
        plt.ylabel("Frequency")
        altm = str(mean)
        legenda = ("Mean =\n "+altm)
        plt.axvline(x= mean, color='r', linestyle='dashed', label=legenda)
        plt.axvline(x= Dev1, color ='g', linestyle='dashed', label=Dev1)
        plt.axvline(x= Dev2, color = 'g', linestyle='dashed', label=Dev2)
        zscorev = mean+(zscore*standarderror)
        zscor = ("z-score =\n"+str(zscore))
        plt.axvline(x= zscorev, color = 'purple', label=zscor)
        print ('To continue, you must save the figure and close it, or just close it. \nYou can also zoom in it or move the graph to see it better, \nuse the buttons.\n')
        plt.legend()
        plt.show(fig2)
        continue

print(' ')
print 'Ciao, human!'
print(' ')


import numpy as np
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import minmax_scale

def step(x, th):
    return 1 if x > th else 0 if -th <= x <= th else -1

def percep_fit(s, t, th=0., a=1, draw=False):
    nx = len(s[0])
    ny = len(t[0])
    w = np.zeros((nx + 1, ny))
    b = np.ones((len(s), 1))
    s = np.hstack((s, b))
    stop = False
    epoch = 0

    while not stop:
        stop = True
        epoch += 1

        print(f'Epoch: {epoch}')

        for i, s_ in enumerate(s):
            for j, t_ in enumerate(t[i]):
                yin = np.dot(s_, w[:, j:j + 1])[0]
                y = step(yin, th)

                print(f'yin: {yin}')
                print(f'y: {y}')
                print(f'w: {w}')

                if y != t_:
                    stop = False
                    dw = a * t_ * s_
                    w[:, j:j + 1] += dw.reshape(nx + 1, -1)

                print(f'w\': {w}')

                # if draw == 'loop' and nx == 2:
                #     plot([line(w, th), line(w, -th)], s, t)

        stop = True

        # if draw == 'result' and nx == 2:
        #     plot([line(w, th), line(w, -th)], s, t)

    return w, epoch

def percep_predict(X, w, th=0):
    for x in X:
        y_in = np.dot([*x, 1], w)
        y = [step(i, th) for i in y_in]

        yield y

def quiz():
    s = [[1, -1, 1],
         [-1, -1, -1],
         [-1, 1, -1]]
    t = [[-1], [1], [1]]
    percep_fit(s, t, .5, 1)

def test():
    s = [[1, 1],
         [1, 0],
         [0, 1],
         [0, 0]]

    t = [[1, 0], [0, 1], [0, 1], [0, 1]]

    w, epoch = percep_fit(s, t, .2)
    # y = percep_test([-1, 1, -1], w)

    print(w)
    print(epoch)

def test_iris():
    iris = sns.load_dataset('iris')
    # iris = iris.loc[iris['species'] != 'virginica']
    iris = iris.drop(['sepal_width', 'petal_width'], axis=1)
    iris.loc[iris['species'] == 'virginica', 'petal_length'] += 2

    X = iris[['sepal_length', 'petal_length']].to_numpy()
    X = minmax_scale(X)

    y = iris['species'].to_numpy()
    c = {'virginica': [1, -1, -1], 'setosa': [-1, 1, -1], 'versicolor': [-1, -1, 1]}
    y = [c[i] for i in y]

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3)
    w, epoch = percep_fit(X_train, y_train)

    # to_class = lambda x: [0 if i == [-1, -1] else 1 if i == [-1, 1] else 2 for i in x]
    # out = list(percep_predict(X_test, w))
    # out = to_class(out)

    # y_test = to_class(y_test)
    # acc = accuracy_score(out, y_test)

    # print(f'Epoch: {epoch}')
    # print(f'Accuracy: {acc}')

if __name__ == '__main__':
    quiz()


'''
______     _            _       _        _____                                              _      ___              _           _
| ___ \   (_)          (_)     | |      /  __ \                                            | |    / _ \            | |         (_)
| |_/ / __ _ _ __   ___ _ _ __ | | ___  | /  \/ ___  _ __ ___  _ __   ___  _ __   ___ _ __ | |_  / /_\ \_ __   __ _| |_   _ ___ _ ___
|  __/ '__| | '_ \ / __| | '_ \| |/ _ \ | |    / _ \| '_ ` _ \| '_ \ / _ \| '_ \ / _ \ '_ \| __| |  _  | '_ \ / _` | | | | / __| / __|
| |  | |  | | | | | (__| | |_) | |  __/ | \__/\ (_) | | | | | | |_) | (_) | | | |  __/ | | | |_  | | | | | | | (_| | | |_| \__ \ \__ \
\_|  |_|  |_|_| |_|\___|_| .__/|_|\___|  \____/\___/|_| |_| |_| .__/ \___/|_| |_|\___|_| |_|\__| \_| |_/_| |_|\__,_|_|\__, |___/_|___/
                         | |                                  | |                                                      __/ |
                         |_|                                  |_|                                                     |___/
'''

import numpy as np

def PCA(X,k):
# Dataset information
    mean = np.mean(X,axis=0)
    X_norm = X - mean # Normalised
    cov = np.cov(X_norm,rowvar=False)
    vals = np.linalg.eigh(cov)[0]#Eigenvalues
    vecs = np.linalg.eigh(cov)[1]#Eigenvectors

# Select Wanted Components
    components = []
    vals = vals.tolist()
    for i in range(k):
        argmax = vals.index(max(vals)) # Index of largest eigenvalue
        components.append(vecs[:,argmax]) # Add Max Eigenvector to components
        del vals[argmax] # Remove eigenvalue for next iter

    W = np.asarray(components)
    return W, mean

def ProjectDatapoints(X,W,u):
    X_norm = X - u #normalise x
    projection = np.dot(W,X_norm.T).T #project

    return projection


import numpy as np
from numpy import ndarray

import solution

def get_cross_matrix(vec: ndarray) -> ndarray:
    """Get the matrix equivalent of cross product. S() in (10.68)

    cross_product_matrix(vec1)@vec2 == np.cross(vec1, vec2)

    Hint: see (10.5)

    Args:
        vec (ndarray[3]): vector

    Returns:
        S (ndarray[3,3]): cross product matrix equivalent
    """
    S  = np.array([
        [0, -vec[2], vec[1]],
        [vec[2], 0, -vec[0]],
        [-vec[1], vec[0], 0]
    ])

    return S


import numpy as np

def _sincDVR(start,stop,num):
    """
    Calculate sinc DVR grid and operators

    Parameters
    ----------
    start : float
        Grid start value
    stop : float
        Grid stop value. Must be larger than start
    num : int
        Number of grid points. Must be >= 2.

    Returns
    -------
    grid : ndarray
        Array of DVR grid points
    D : ndarray
        First derivative operator in DVR basis, shape (num, num)
    D2 : ndarray (num,num)
        Second derivative operator in DVR basis, shape (num, num)

    """

    if stop <= start:
        raise ValueError("stop value must be larger than start value")
    if num < 2 :
        raise ValueError("num must be >= 2")

    grid = np.linspace(start,stop,num)

    D = np.zeros((num,num))
    D2 = np.zeros((num,num))

    delta = grid[1]-grid[0] # Grid spacing

    for i in range(num):
        for j in range(num):

            # Calculate first derivative operator D
            if i==j:
                pass #zero
            else:
                D[i,j] = (-1.0)**(i-j) / (delta * (i-j))

            # Calculate second derivative operator D2
            if i==j:
                D2[i,j] = -np.pi**2 / (3.0 * delta**2)
            else:
                D2[i,j] = -2.0 * (-1.0)**(i-j) / (delta**2 * (i-j)**2)

    return grid,D,D2

def _sincDVRwfs(q,start,stop,num):
    """
    Calculate sinc DVR wavefunctions.

    Parameters
    ----------
    q : ndarray
        A 1D array of coordinate values.
    start : float
        DVR grid start value.
    stop : float
        DVR grid stop value.
    num : int
        The number of DVR grid points

    Returns
    -------
    wfs : ndarray
        A (`q`.size, `num`) shaped array with
        the DVR wavefunctions evaluated at grid points `q`.

    """

    if np.ndim(q) != 1:
        raise ValueError("q must be 1-dimensional")

    nq = q.size

    wfs = np.ndarray((nq,num), dtype = q.dtype)

    grid = np.linspace(start,stop,num)
    delta = grid[1] - grid[0]

    for i in range(num):
        # Compute sinc DVR basis function at
        # grid position i
        # Note: np.sinc function is defined as sin(pi*x)/pi*x
        #
        fi = np.sqrt(1.0/delta) * np.sinc( (q - grid[i])/delta )
        wfs[:,i] = fi

    return wfs


import numpy as np
from scipy.special import legendre as _legendre

def legendre(t, order=0):
    """Generate Legendre polynomial regressors.

    Parameters
    ----------
    n_acq : int
        Number of time points.
    order : int
        Highest order of Legendre polynimals to generate.

    Returns
    -------
    Z : array, shape=(t,order+1)
        Legendre polynomial regressors.
    """

    ## Error catching.
    assert order >= 0

    ## Define grid.
    x = np.linspace(-1,1,t)

    ## Return Legendre polynomials.
    return np.column_stack([_legendre(i)(x) for i in range(order+1)])


# -*- coding: utf-8 -*-
"""
Created on Thu Mar 11 15:02:32 2021

"""
from numpy.linalg import inv
import pandas as pd
import functools
import os
import time

def OLS_regression(X,Y, add_constant = True):

    if add_constant:
        X['Constant'] = 1

    n = len(Y)
    k = len(X.columns)

    Beta_hat = pd.Series(data = inv(X.T.dot(X)).dot(X.T.dot(Y)), index = X.columns )

    Y_hat = X.dot(Beta_hat)

    Epsilon_hat = Y - Y_hat

    Sigma_hat_square = sum(Epsilon_hat ** 2) / (n - k)

    Var_Beta_hat_hat = Sigma_hat_square * inv(X.T.dot(X))

    Std_err_Beta_hat = pd.Series(Var_Beta_hat_hat.diagonal() ** 0.5, index = X.columns)

    t_stat_Beta_hat = Beta_hat / Std_err_Beta_hat

    return {'Beta_hat': Beta_hat,
            'Y_hat': Y_hat,
            'Epsilon_hat': Epsilon_hat,
            'Sigma_hat_square': Sigma_hat_square,
            'Var_Beta_hat_hat': Var_Beta_hat_hat,
            'Std_err_Beta_hat': Std_err_Beta_hat,
            't_stat_Beta_hat': t_stat_Beta_hat
            }

def as_of_date_to_quarter (dt):
    month, year = dt.month, dt.year
    Q = int((int(month) - 0.01)/ 3) + 1
    return str(Q)+'Q'+ str(year)

def pandas_csv_cache(folder, file_template, expiration_in_sec,
                     read_csv_kwargs={'sep', '|'}, to_csv_kwargs={'sep': '|'}):
    def decorator_pandas_csv_cache(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if not os.path.isdir(folder):
                os.mkdir(folder)
            if '{ticker}' in file_template:
                ticker = kwargs['ticker'] if 'ticker' in kwargs else args[0]
                file_path = os.path.join(folder, file_template.format(ticker=ticker))
            else:
                file_path = os.path.join(folder, file_template)
            if os.path.isfile(file_path):
                if time.time() - os.path.getmtime(file_path) <= expiration_in_sec:
                    return pd.read_csv(file_path, **read_csv_kwargs)
                else:
                    os.remove(file_path)
            df = func(*args, **kwargs)
            df.to_csv(file_path, **to_csv_kwargs)
            return df
        return wrapper
    return decorator_pandas_csv_cache

def Rolling_Regression( Y_ts, X_ts_arr, window=42):
    input_dict = {}
    X_cols = []
    output_dict = {}
    if type(Y_ts.index[0]) == pd._libs.tslibs.timestamps.Timestamp:
        Y_ts.index = Y_ts.index.map(lambda x: x.date())
    for ts in X_ts_arr:
        if type(ts.index[0]) == pd._libs.tslibs.timestamps.Timestamp:
            ts.index = ts.index.map(lambda x: x.date())
        input_dict[ts.name] = ts
        X_cols.append(ts.name)

    input_dict[Y_ts.name] = Y_ts
    input_df = pd.DataFrame(input_dict).dropna()
    input_df['Constant'] = 1
    X_cols.append('Constant')
    total_observations = len(input_df)
    nbr_regressions = total_observations - window

    for i in range (nbr_regressions):
        output_date = input_df.index[i+window]
        run_date_index = input_df.index[i:i+window]
        Y = input_df.loc[run_date_index, Y_ts.name]
        X = input_df.loc[run_date_index, X_cols]
        output_dict[output_date] = OLS_regression(X,Y, add_constant = False)
    return output_dict

def _DEP_align_date_index(obj_1, obj_2):
    idx = obj_1.index.intersection(obj_2.index)
    return obj_1.loc[idx], obj_2.loc[idx]

def align_date_index(obj_arr):
    # build index intersection of all objects
    # Use index to filter all objects
    idx = None
    for obj in obj_arr:
        if idx is None:
            idx = obj.index
        else:
            idx = idx.intersection(obj.index)
    return [obj.loc[idx] for obj in obj_arr]


import numpy as np

def l2_regularization(W, reg_strength):
    loss = reg_strength * np.sum(pow(W, 2))
    grad = 2 * reg_strength * W
    return loss, grad

def softmax(predictions):
    copy_predictions = np.copy(predictions)
    if predictions.ndim == 1:
        copy_predictions -= np.max(predictions) # numerical stability
        probs = np.exp(copy_predictions)/np.sum(np.exp(copy_predictions))
    else:
        copy_predictions -= np.amax(predictions, axis=1, keepdims=True) # numerical stability
        probs = np.exp(copy_predictions)/np.sum(np.exp(copy_predictions), axis=1, keepdims=True)

    return probs

def cross_entropy_loss(probs, target_index):
    if probs.ndim == 1:
        loss = -np.log(probs[target_index])
    else:
        batch_size = probs.shape[0]
        batch_loss = -np.log(probs[np.arange(batch_size), target_index.flatten()]) # return a copy of the array collapsed into one dimension
        loss = np.sum(batch_loss)/batch_size
    return loss

def softmax_with_cross_entropy(predictions, target_index):
    dprediction = softmax(predictions)
    loss = cross_entropy_loss(dprediction, target_index)

    if predictions.ndim == 1:
        dprediction[target_index] -= 1
    else:
        batch_size = predictions.shape[0]
        dprediction[np.arange(batch_size), target_index.flatten()] -= 1
        dprediction /= batch_size

    return loss, dprediction

class Param:
    def __init__(self, value):
        self.value = value
        self.grad = np.zeros_like(value)

class ReLULayer:
    def __init__(self):
        pass

    def forward(self, X):
        self.X = X
        return X * (X > 0)

    def backward(self, d_out):
        d_result = d_out * (self.X > 0)
        return d_result

    def params(self):
        # ReLU Doesn't have any parameters
        return {}

class FullyConnectedLayer:
    def __init__(self, n_input, n_output):
        self.W = Param(0.001 * np.random.randn(n_input, n_output))
        self.B = Param(0.001 * np.random.randn(1, n_output))
        self.X = None

    def forward(self, X):
        self.X = X
        return np.dot(self.X, self.W.value) + self.B.value

    def backward(self, d_out):
        self.W.grad = np.dot(self.X.T, d_out)
        self.B.grad = np.sum(d_out, axis = 0, keepdims = True)
        d_input = np.dot(d_out, self.W.value.T)
        return d_input

    def params(self):
        return {'W': self.W, 'B': self.B}


# 2.4 통계 기반 기법 개선하기

# 2.4.1 상호정보량
# '발생(고빈도 단어)' 횟수라는 특징은 좋은 특징은 아님
'''
    점별 상호정보량(PMI, Pointwise Mutual Information)
    PMI(x, y) = log2(P(x, y) / (P(x)P(y))
    P(x): x가 일어날 확률
    P(y): y가 일어날 확률
    P(x, y): x와 y가 동시에 일어날 확률
    문제점: 두 단어의 동시발생 횟수가 0이면 (-)무한대가 된다
'''
'''
    점별 상호정보량의 문제를 해결하기 위해 양의 상호정보량(PPMI, Positive PMI)를 사용함
    PPMI(x, y) = max(0, PMI(x, y))
    PMI가 음수일 경우 0으로 취급함
'''

# 동시발생 행렬을 PPMI 행렬로 변환하는 함수
# common.util.ppmi()
import numpy as np
from common.util import preprocess, create_co_matrix, cos_similarity, ppmi

text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size)
W = ppmi(C)

np.set_printoptions(precision=3)    # 유효 자릿수를 세 자리로 표시
print('동시 발생 행렬')
print(C)
print('-' * 50)
print('PPMI')
print(W)

'''
    이 헹렬의 원소 대부분은 0이다.
    즉, 벡터의 원소 대부분 '중요도'가 낮다는 뜻이다.
    이런 벡터는 노이즈에 약하고 견고하지 못하다는 단점이 있다.
    이 문제를 대체하고자 수행되는 기법이 '벡터의 차원 감소' 이다.
'''

# 2.4.2 차원 감소
# 차원 감소(dimensionality redution)는 '중요한 정보'는 최대한 유지하면서 벡터의 차원을 줄이는 방법이다.
'''
    특잇값분해(SVD, Singular Value Decomposition)
    SVD는 임의의 행렬을 세 행렬의 곱으로 분해한다.
    X = U * S * V.T
    U, V: 직교행렬(orthogonal matrix)이고, 그 열벡터를 서로 직교한다.
    S: 대각행렬(diagonal matrix, 대각성분 외에는 모두 0인 행렬)이다.
    U 행렬은 '단어공간', S 행렬은 '특잇값'이 큰 순서로 나열한다.
'''

# 2.4.3 SVD에 의한 차원 감소
# import sys
# sys.path.appen('..')
import numpy as np
import matplotlib.pyplot as plt
from common.util import preprocess, create_co_matrix, ppmi

text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
vocab_size = len(word_to_id)
C = create_co_matrix(corpus, vocab_size,window_size=1)
W = ppmi(C)

# SVD
U, S, V = np.linalg.svd(W)

print(C[0])     # 동시발생 행렬
print(W[0])     # PPMI 행렬
print(U[0])     # SVD
print(U[0, :2])

for word, word_id in word_to_id.items():
    plt.annotate(word, (U[word_id, 0], U[word_id, 1]))

plt.scatter(U[:, 0], U[:, 1], alpha=0.5)
plt.show()

# 2.4.4 PTB 데이터셋
# 펜 트리뱅크(PTB, Penn Treebank): 주어진 기법의 품질을 측정하는 벤치마크로 자주 이용된다.
# 펜 트리뱅크를 사용하는 코드
# import sys
# sys.path.append('..')
from dataset import ptb

corpus, word_to_id, id_to_word = ptb.load_data('train')

print('말뭉치 크기:', len(corpus))
print('corpus[:30]:', corpus[:30])
print()
print('id_to_word[0]:', id_to_word[0])
print('id_to_word[1]:', id_to_word[1])
print('id_to_word[2]:', id_to_word[2])
print()
print("word_to_id['car']:", word_to_id['car'])
print("word_to_id['happy']:", word_to_id['happy'])
print("word_to_id['lexus']:", word_to_id['lexus'])

# 2.4.5 PTB 데이터셋 평가
# PTB 데이터셋 평가 코드
# import sys
# sys.path.append('..')
import numpy as np
from common.util import most_similar, create_co_matrix, ppmi
from dataset import ptb

window_size = 2
wordvec_size = 100

corpus, word_to_id, id_to_word = ptb.load_data('train')
vocab_size = len(word_to_id)
print('동시 발생 수 계산 ...')
C = create_co_matrix(corpus, vocab_size, window_size)
print('PPMI 계산 ...')
W = ppmi(C, verbose=True)

print('SVD 계산 ...')
try:
    # truncated SVD (빠르다!)
    from sklearn.utils.extmath import randomized_svd
    U, S, V = randomized_svd(W, n_components=wordvec_size, n_iter=5, random_state=None)
except ImportError:
    # SVD (느리다)
    U, S, V = np.linalg.svd(W)

word_vecs = U[:, :wordvec_size]

querys = ['you', 'year', 'car', 'toyota']
for query in querys:
    most_similar(query, word_to_id, id_to_word, word_vecs, top=5)


# Starting in the top left corner of a 2×2 grid, and only being able to move to the right and down, there are exactly 6 routes to the bottom right corner.

# How many such routes are there through a 20×20 grid?

import numpy as np
np.set_printoptions(linewidth=240)

# size is the number of complete square on one dimension
# for example a size of 2 would consist of 3x3 dots to make a total of 4 squares
size = 20
z = np.zeros((size+1,size+1), dtype=np.int64)
z[0,:] = 1
z[:,0] = 1

for i in range(1,size+1):
    for j in range(1,size+1):
        z[i,j] = z[i-1,j] + z[i,j-1]
print(np.rot90(np.rot90(z)))
print(z[size,size])


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

class MeanVarOptManager:
    def __init__(self):
        self.backtesting = dict()

    def simulation_portfolio(self, tickers, ticker_names, num_try=10):
        port_returns = []
        port_volatility = []
        sharpe_ratio = []
        stock_weights = []

        portfolio = {'returns': port_returns,
                     'volatility': port_volatility,
                     'sharpe_ratio': sharpe_ratio}

        num_of_tickers = len(ticker_names)

        for i in range(num_try):
            weights = self.get_randomized_weights(num_of_tickers)
            portfolio_summary = self.evaluate_portfolio(tickers, weights)

            port_returns.append(portfolio_summary['return'])
            port_volatility.append(portfolio_summary['volatility'])
            sharpe_ratio.append(portfolio_summary['sharpe'])
            stock_weights.append(weights)

        for idx, symbol in enumerate(ticker_names):
            portfolio[symbol + ' weight'] = [weights[idx] for weights in stock_weights]

        column_order = ['returns', 'volatility', 'sharpe_ratio'] + [stock+' weight' for stock in ticker_names]
        portfolio_df = pd.DataFrame(portfolio)
        return portfolio_df[column_order]

    def evaluate_portfolio(self, tickers, weights, display=False):
        sample_data_returns = tickers.pct_change()

        # variance
        portfolio_variance = self.get_variance(sample_data_returns, weights)

        # standard deviation
        portfolio_volatility = np.sqrt(portfolio_variance)

        # Expected return
        expected_return = self.get_annual_returns(sample_data_returns, weights)
        sharp_ratio = expected_return / portfolio_volatility

        if display:
            print('Variance of Portfolio', str(round(portfolio_variance, 4) * 100) + '%')
            print('Variance of Risk', str(round(portfolio_volatility, 4) * 100) + '%')

            sample_data_returns.plot(figsize=(20,14))

        portfolio_dic = dict()
        selected_tickers_name = [ticker.split('.')[0] for ticker in tickers.columns]

        portfolio_dic['tickers'] = selected_tickers_name
        portfolio_dic['weights'] = weights
        portfolio_dic['volatility'] = portfolio_volatility
        portfolio_dic['return'] = expected_return
        portfolio_dic['sharpe'] = sharp_ratio

        return portfolio_dic

    def get_simulation_summary(self, portfolio_df: pd.DataFrame, ticker_names, display=False):
        min_volatility = portfolio_df['volatility'].min()
        max_sharpe = portfolio_df['sharpe_ratio'].max()

        sharpe_portfolio = portfolio_df.loc[portfolio_df['sharpe_ratio'] == max_sharpe]
        min_variance_port = portfolio_df.loc[portfolio_df['volatility'] == min_volatility]

        if display:
            # use the min, max values to locate and create the two special portfolios

            plt.style.use('seaborn-dark')
            portfolio_df.plot.scatter(x='volatility', y='returns', c='sharpe_ratio',
                                      cmap='RdYlGn', edgecolors='black', figsize = (10, 8), grid=True)
            plt.scatter(x=sharpe_portfolio['volatility'],
                        y=sharpe_portfolio['returns'],
                        c='red', marker='D', s=200)
            plt.scatter(x=min_variance_port['volatility'],
                        y=min_variance_port['returns'],
                        c='blue', marker='D', s=200)

            plt.xlabel('Volatility (Std. Deviation)')
            plt.ylabel('Expected Returns')
            plt.title('Efficient Frontier')
            plt.show()

        simulation_result = {
            'min_volatility_portfolio': min_variance_port.T,
            'max_sharpe_portfolio': sharpe_portfolio.T,
            'min_volatility': min_volatility,
            'max_sharpe': max_sharpe,
            'tickers': ticker_names
        }

        return simulation_result

    def get_ticker_names(self, tickers):
      return [ticker.split('.')[0] for ticker in tickers.columns]

    def get_randomized_weights(self, size):
        randomized_values = np.random.random([size])
        return randomized_values / sum(randomized_values)

    def get_sample_selected_ticker(self, stock_prices: pd.DataFrame, num_of_tickers=None):
        if not num_of_tickers:
            num_of_tickers = len(stock_prices.columns)

        selected_tickers = np.random.permutation(stock_prices.columns)[:num_of_tickers]
        selected_data = stock_prices[selected_tickers]
        exist_range =\
            selected_data.notnull().as_matrix().astype(int).mean(axis=1) == 1
        exist_percent = sum(exist_range)/len(exist_range)

        return selected_data[exist_range]

    def get_variance(self, sample_data_returns, weights):
        cov_matrix_portfolio = sample_data_returns.cov() * 250
        variance = np.dot(weights.T, np.dot(cov_matrix_portfolio, weights))
        return variance

    def get_annual_returns(self, sample_data_returns, weights):
        annual_returns = sample_data_returns.mean() * 250
        expected_return = np.sum(annual_returns * weights)
        return expected_return

if __name__ == '__main__':
    import pprint
    pp = pprint.PrettyPrinter(indent=4)
    mvo_manager = MeanVarOptManager()
    date_rng = pd.date_range(start='1/1/2018', end='1/08/2018')
    sample_data = {'stockA': [3,4,5,], 'stockB': [4,5,6], 'stockC': [1,7,5]}
    sample_stock_price_df = pd.DataFrame(sample_data, index=date_rng[:3])
    for i in range(5):
        selected_ticker = mvo_manager.get_sample_selected_ticker(stock_prices=sample_stock_price_df, num_of_tickers=2)
        # print(selected_ticker)
        ticker_names = mvo_manager.get_ticker_names(tickers=selected_ticker)
        portfolio_df = mvo_manager.simulation_portfolio(selected_ticker, ticker_names=ticker_names, num_try=5)
        res = mvo_manager.get_simulation_summary(portfolio_df=portfolio_df, ticker_names=ticker_names)

        print("min_volatility_portfolio")
        pp.pprint(res['min_volatility_portfolio'])
        print()
        print("max_sharpe_portfolio")
        pp.pprint(res['max_sharpe_portfolio'])
        print('-' * 50)


# -*- coding: utf-8 -*-
"""
Created on 2021/02/11 15:51

@Project -> mi_lombardi.py

@Author: luolei

@Email: dreisteine262@163.com

@Describe: 基于KNN的经典信息熵估计器
"""

__doc__ = """
    参考文献: D. Lombardi, S. Pant: A Non-Parametric K-Nearest Neighbor Entropy Estimator, \
        Physical Review E., 2016.
"""

from sklearn.neighbors import NearestNeighbors
from scipy.special import gamma, psi
import numpy as np

def _nearest_distances(x, k: int, metric: str):
    """Returns the distance to the kth nearest neighbor for every point in X"""
    knn = NearestNeighbors(n_neighbors=k, metric=metric)
    knn.fit(x)
    d, _ = knn.kneighbors(x)
    return d[:, -1]

def _entropy(x: np.ndarray, k: int, metric: str):
    """
    根据X的样本计算对应的信息熵

    Reference:
    1. Kozachenko, Leonenko:
    """
    try:
        N, D = x.shape
    except:
        raise ValueError('X应为二维数组')
    r = _nearest_distances(x, k, metric)
    volume_unit_ball = (np.pi ** (.5 * D)) / gamma(.5 * D + 1)  # 高维空间单位球体积
    return D * np.mean(np.log2(r + np.finfo(x.dtype).eps)) + np.log2(volume_unit_ball) + psi(N) - psi(k)

class KLEstimator(object):
    """
    基于KNN的经典信息熵估计器, 又名KL估计器

    Reference:
    1. D. Lombardi, S. Pant: "A Non-Parametric K-Nearest Neighbor Entropy Estimator", Physical Review E., 2016
    """

    # TODO: 分别针对x为连续型或离散型进行计算.
    def __init__(self, x: np.ndarray, y: np.ndarray):
        self.x, self.y = x.flatten(), y.flatten()

    def cal_mi_cc(self, k: int, metric: str = 'euclidean'):
        x, y = self.x.reshape(-1, 1), self.y.reshape(-1, 1)
        vars = np.hstack((x, y))
        return _entropy(x, k, metric) + _entropy(y, k, metric) - _entropy(vars, k, metric)

# if __name__ == '__main__':

#     import pandas as pd
#     import sys
#     import os

#     BASE_DIR = os.path.abspath(os.path.join(
#         os.path.abspath(__file__), '../../../../'))
#     sys.path.append(BASE_DIR)

#     from src.settings import *
#     from core.dataset.data_generator import DataGenerator

#     # ---- 测试代码 --------------------------------------------------------------------------------

#     from core.dataset.data_generator import FUNC_NAMES

#     N = 5000
#     entropy_results = []
#     for func in FUNC_NAMES:
#         # 生成样本.
#         data_gener = DataGenerator(N=N)
#         x, y, _, _ = data_gener.gen_data(func)

#         # 计算MIC.
#         kl_estimator = KLEstimator(x, y)
#         mic = kl_estimator.cal_mi_cc(k=10)

#         entropy_results.append([func, mic])

#     # 整理结果.
#     entropy_results = pd.DataFrame(
#         entropy_results, columns=['func', 'entropy'])


#!/usr/bin/env python
# coding: utf-8
# ___
#
#
# ___
# *Copyright Pierian Data 2017*
# *For more information, visit us at www.pieriandata.com*
# In[19]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#get_ipython().run_line_magic('matplotlib', 'inline')
# In[20]:
airline = pd.read_csv('airline_passengers.csv',index_col="Month")
# In[21]:
airline.dropna(inplace=True)
airline.index = pd.to_datetime(airline.index)
# In[22]:
airline.head()
# # SMA
# ## Simple Moving Average
#
# We've already shown how to create a simple moving average, for a quick review:
# In[23]:
airline['6-month-SMA']=airline['Thousands of Passengers'].rolling(window=6).mean()
airline['12-month-SMA']=airline['Thousands of Passengers'].rolling(window=12).mean()
# In[24]:
airline.head()
# In[25]:
airline.plot()
# # EWMA
# ## Exponentially-weighted moving average
#
# We just showed how to calculate the SMA based on some window.However, basic SMA has some "weaknesses".
# * Smaller windows will lead to more noise, rather than signal
# * It will always lag by the size of the window
# * It will never reach to full peak or valley of the data due to the averaging.
# * Does not really inform you about possible future behaviour, all it really does is describe trends in your data.
# * Extreme historical values can skew your SMA significantly
#
# To help fix some of these issues, we can use an EWMA (Exponentially-weighted moving average).
# EWMA will allow us to reduce the lag effect from SMA and it will put more weight on values that occured more recently (by applying more weight to the more recent values, thus the name). The amount of weight applied to the most recent values will depend on the actual parameters used in the EWMA and the number of periods given a window size.
# [Full details on Mathematics behind this can be found here](http://pandas.pydata.org/pandas-docs/stable/computation.html#exponentially-weighted-windows)
# Here is the shorter version of the explanation behind EWMA.
#
# The formula for EWMA is:
# $ y_t =   \frac{\sum\limits_{i=0}^t w_i x_{t-i}}{\sum\limits_{i=0}^t w_i} $
# Where x_t is the input value, w_i is the applied weight (Note how it can change from i=0 to t), and y_t is the output.
#
# Now the question is, how to we define the weight term w_i ?
#
# This depends on the adjust parameter you provide to the .ewm() method.
#
# When adjust is True (default), weighted averages are calculated using weights:
#
# ### $y_t = \frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ...
# + (1 - \alpha)^t x_{0}}{1 + (1 - \alpha) + (1 - \alpha)^2 + ...
# + (1 - \alpha)^t}$
# When adjust=False is specified, moving averages are calculated as:
#
# ### $\begin{split}y_0 &= x_0 \\
# y_t &= (1 - \alpha) y_{t-1} + \alpha x_t,\end{split}$
#
# which is equivalent to using weights:
#
#  \begin{split}w_i = \begin{cases}
#     \alpha (1 - \alpha)^i & \text{if } i < t \\
#     (1 - \alpha)^i        & \text{if } i = t.
# \end{cases}\end{split}
# When adjust=True we have y0=x0 and from the last representation above we have yt=αxt+(1−α)yt−1, therefore there is an assumption that x0 is not an ordinary value but rather an exponentially weighted moment of the infinite series up to that point.
#
# One must have 0<α≤1, and while since version 0.18.0 it has been possible to pass α directly, it’s often easier to think about either the span, center of mass (com) or half-life of an EW moment:
# \begin{split}\alpha =
#  \begin{cases}
#      \frac{2}{s + 1},               & \text{for span}\ s \geq 1\\
#      \frac{1}{1 + c},               & \text{for center of mass}\ c \geq 0\\
#      1 - \exp^{\frac{\log 0.5}{h}}, & \text{for half-life}\ h > 0
#  \end{cases}\end{split}
# * Span corresponds to what is commonly called an “N-day EW moving average”.
# * Center of mass has a more physical interpretation and can be thought of in terms of span: c=(s−1)/2
# * Half-life is the period of time for the exponential weight to reduce to one half.
# * Alpha specifies the smoothing factor directly.
# In[30]:
airline['EWMA12'] = airline['Thousands of Passengers'].ewm(span=12).mean()
# In[32]:
airline[['Thousands of Passengers','EWMA12']].plot()
# Great! That is all for now, let's move on to ARIMA modeling!


# code copied from Scipy cookbook
# http://www.scipy.org/Cookbook/SignalSmooth

import numpy

def smooth(x,window_len=11,window='hanning'):
    """smooth the data using a window with requested size.

    This method is based on the convolution of a scaled window with the signal.
    The signal is prepared by introducing reflected copies of the signal
    (with the window size) in both ends so that transient parts are minimized
    in the begining and end part of the output signal.

    input:
        x: the input signal
        window_len: the dimension of the smoothing window; should be an odd integer
        window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
            flat window will produce a moving average smoothing.

    output:
        the smoothed signal

    example:

    t=linspace(-2,2,0.1)
    x=sin(t)+randn(len(t))*0.1
    y=smooth(x)

    see also:

    numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
    scipy.signal.lfilter

    TODO: the window parameter could be the window itself if an array instead of a string
    NOTE: length(output) != length(input), to correct this: return y[(window_len/2-1):-(window_len/2)] instead of just y.
    """

    if x.ndim != 1:
        raise ValueError, "smooth only accepts 1 dimension arrays."

    if x.size < window_len:
        raise ValueError, "Input vector needs to be bigger than window size."

    if window_len<3:
        return x

    if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
        raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'"

    s=numpy.r_[x[window_len-1:0:-1],x,x[-1:-window_len:-1]]
    #print(len(s))
    if window == 'flat': #moving average
        w=numpy.ones(window_len,'d')
    else:
        w=eval('numpy.'+window+'(window_len)')

    y=numpy.convolve(w/w.sum(),s,mode='valid')
    return y

if __name__=='__main__':
    from numpy import *
    from pylab import *

    t=linspace(-4,4,100)
    x=sin(t)
    xn=x+randn(len(t))*0.1
    y=smooth(x)

    ws=31

    subplot(211)
    plot(ones(ws))

    windows=['flat', 'hanning', 'hamming', 'bartlett', 'blackman']

    hold(True)
    for w in windows[1:]:
        eval('plot('+w+'(ws) )')

    axis([0,30,0,1.1])

    legend(windows)
    title("The smoothing windows")
    subplot(212)
    plot(x)
    plot(xn)
    for w in windows:
        plot(smooth(xn,10,w))
    l=['original signal', 'signal with noise']
    l.extend(windows)

    legend(l)
    title("Smoothing a noisy signal")
    show()