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

def duffing(var, t, gamma, a, b, F0, omega, delta):
    """
    var = [x, p]
    dx/dt = p
    dp/dt = -gamma*p + 2*a*x - 4*b*x**3 + F0*cos(omega*t + delta)
    """
    x_dot = var[1]
    p_dot = -gamma * var[1] + 2 * a * var[0] - 4 * b * var[0]**3 + F0 * np.cos(omega * t + delta)

    return np.array([x_dot, p_dot])

# parameter
F0, gamma, omega, delta = 10, 0.1, np.pi/3, 1.5*np.pi
a, b = 1/4, 1/2
#F0, gamma, omega, delta = 2.0, 0.1, 2.4, 0            #np.pi/3, 1.5*np.pi
#a, b = 1/5, 1/2
var, var_lin = [[0, 1]] * 2

#timescale
t = np.arange(0, 20000, 2*np.pi/omega)
#t = np.linspace(0, 200, 3000)   #10000)
t_lin = np.linspace(0, 100, 10000)

# solve
var = odeint(duffing, var, t, args=(gamma, a, b, F0, omega, delta))
#var_lin = odeint(duffing, var_lin, t_lin, args=(gamma, a, b, F0, omega, delta))
var_lin = odeint(duffing, [0.5, 0], t_lin, args=(gamma, a, b, F0, omega, delta))

x, p = var.T[0], var.T[1]
x_lin, p_lin = var_lin.T[0], var_lin.T[1]

plt.plot(t[:100], x[:100])
plt.pause(3)
#plt.close()

plt.plot(t[:100], p[:100])
plt.pause(3)
plt.savefig('plot_D_xpvst0.png', dpi=60)
plt.close()

plt.plot(x, p, ".", markersize=4)
plt.pause(3)
plt.savefig('plot_D_xp0.png', dpi=60)
plt.close()

# is chaotic?
plt.plot(t_lin, x_lin)
plt.pause(3)

#var_lin = odeint(duffing, [0.1, 1], t_lin, args=(gamma, a, b, F0, omega, delta))
var_lin = odeint(duffing, [0.5001, 0], t_lin, args=(gamma, a, b, F0, omega, delta))
x_lin, p_lin = var_lin.T[0], var_lin.T[1]
plt.plot(t_lin, x_lin)
plt.pause(3)
plt.savefig('plot_D_xpvst1.png', dpi=60)
plt.close()
plt.plot(x_lin, p_lin,".", markersize=4)
plt.pause(3)
plt.savefig('plot_D_xp1.png', dpi=60)
plt.close()

"""
scipy.integrate.odeint
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.odeint.html

The second order differential equation for the angle theta of a pendulum acted on by gravity with friction can be written:
theta''(t) + b*theta'(t) + c*sin(theta(t)) = 0

theta'(t) = omega(t)
omega'(t) = -b*omega(t) - c*sin(theta(t))
"""

def pend(y, t, b, c):
    theta, omega = y
    dydt = [omega, -b*omega - c*np.sin(theta)]
    return dydt

b = 0.025
c = 5.0
y0 = [np.pi - 0.1, 0.0]
t = np.linspace(0, 100, 1001)
sol = odeint(pend, y0, t, args=(b, c))

x, p = sol.T[0], sol.T[1]
plt.plot(x, p, ".", markersize=4)
plt.pause(3)
plt.savefig('plot_xp.png', dpi=60)
plt.close()

plt.plot(t, sol[:, 0], 'b', label='theta(t)')
plt.xlabel('t')
plt.grid()
plt.legend(loc='best')
plt.pause(3)
plt.plot(t, sol[:, 1], 'g', label='omega(t)')
plt.legend(loc='best')
plt.pause(3)
plt.savefig('plot_thomegavst.png', dpi=60)
plt.close()

def lorenz(var, t, p, r, b):
    #{\frac  {dx}{dt}}=-px+py
    #{\frac  {dy}{dt}}=-xz+rx-y
    #{\frac  {dz}{dt}}=xy-bz
    """
    var = [x,y,z, x_dot,y_dot,z_dot]
    dx/dt = -px+py
    dy/dt = -xz+rx-y
    dz/dt = xy-bz
    """
    x_dot = -p*var[0]+p*var[1]
    y_dot = -var[0]*var[2]+r*var[0]-var[1]
    z_dot = var[0]*var[1]-b*var[2]
    #x_dot= -p*x_dot +p*y_dot
    #y_dot= -x_dot*var[2] - var[0]*z_dot +r*x_dot -y_dot
    #z_dot= x_dot*var[1] + var[0]*y_dot -b*z_dot
    return x_dot,y_dot,z_dot  #_dot,py_dot,pz_dot

p = 10
b = 8/3
r = 28
y0 = [0.1,0, 0.0]  #,0]
t = np.linspace(0, 20, 3001)
sol = odeint(lorenz, y0, t, args=(p, r, b))

x, y, z = sol.T[0], sol.T[1], sol.T[2]
plt.plot(x, y, ".", markersize=4)
plt.pause(3)
plt.savefig('plot_xy.png', dpi=60)
plt.close()

plt.plot(y, z, ".", markersize=4)
plt.pause(3)
plt.savefig('plot_yz.png', dpi=60)
plt.close()

plt.plot(z, x, ".", markersize=4)
plt.pause(3)
plt.savefig('plot_zx.png', dpi=60)
plt.close()

#plt.savefig('plot_xyyzzx.png', dpi=60)
#plt.close()

plt.plot(t, sol[:, 0], 'b', label='x(t)')
plt.xlabel('t')
plt.grid()
plt.legend(loc='best')
plt.savefig('plot_xt.png', dpi=60)
plt.pause(3)
plt.close()

plt.plot(t, sol[:, 1], 'g', label='y(t)')
plt.xlabel('t')
plt.grid()
plt.legend(loc='best')
plt.savefig('plot_yt.png', dpi=60)
plt.pause(3)
plt.close()

plt.plot(t, sol[:, 2], 'g', label='z(t)')
plt.xlabel('t')
plt.grid()
plt.legend(loc='best')
plt.savefig('plot_zt.png', dpi=60)
plt.pause(3)
plt.close()

#plt.savefig('plot_xyzvst.png', dpi=60)
#plt.close()


import numpy as np

def get_long_run_state(pi, k, P):
    """
    For a Markov chain with transition matrix P and starting state vector pi,
    obtain the state distribution after k steps.
    """
    return pi @ np.linalg.matrix_power(P, k)

def extract_Q(P):
    """
    For an absorbing Markov chain with transition rate matrix P this computes the
    matrix Q.

    Note that this does not assume that P is in the required format. It
    identifies the rows and columns that have a 1 in the diagonal and removes
    them.
    """
    indices_without_1_in_diagonal = np.where(P.diagonal() != 1)[0]
    Q = P[indices_without_1_in_diagonal.reshape(-1, 1), indices_without_1_in_diagonal]
    return Q

def compute_N(Q):
    """
    For an absorbing Markov chain with transition rate matrix P that gives
    matrix Q this computes the fundamental matrix N.
    """
    number_of_rows, _ = Q.shape
    N = np.linalg.inv(np.eye(number_of_rows) - Q)
    return N

def compute_t(P):
    """
    For an absorbing Markov chain with transition rate matrix this computes the
    vector t which gives the expected number of steps until absorption.

    Note that this does not assume P is in the required format.
    """
    Q = extract_Q(P)
    N = compute_N(Q)
    number_of_rows, _ = Q.shape
    return N @ np.ones(number_of_rows)


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

df=pd.read_csv("ex2data1.txt",header=None)
X=df.iloc[:,:-1].values
y=df.iloc[:,-1].values

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

def costFunction(theta, X, y):
    """
    Takes in numpy array theta, x and y and return the logistic regression cost function and gradient
    """
    # print "Theta", theta
    # print "X", X
    # print "y", y
    m=len(y)

    predictions = sigmoid(np.dot(X,theta))
    error = (-y * np.log(predictions)) - ((1-y)*np.log(1-predictions))

    cost = 1/m * sum(error)

    grad = 1/m * np.dot(X.transpose(),(predictions - y))

    return cost[0] , grad

def gradientDescent(X,y,theta,alpha,num_iters):
    """
    Take in numpy array X, y and theta and update theta by taking num_iters gradient steps
    with learning rate of alpha

    return theta and the list of the cost of theta during each iteration
    """

    m=len(y)
    J_history =[]

    for i in range(num_iters):
        cost, grad = costFunction(theta,X,y)
        theta = theta - (alpha * grad)
        J_history.append(cost)

    return theta , J_history

def featureNormalization(X):
    """
    Take in numpy array of X values and return normalize X values,
    the mean and standard deviation of each feature
    """
    mean=np.mean(X,axis=0)
    std=np.std(X,axis=0)

    X_norm = (X - mean)/std

    return X_norm , mean , std

def plot(x, y):
    pos , neg = (y==1).reshape(100,1) , (y==0).reshape(100,1)
    plt.scatter(X[pos[:,0],0],X[pos[:,0],1],c="r",marker="+")
    plt.scatter(X[neg[:,0],0],X[neg[:,0],1],marker="o",s=10)
    plt.xlabel("Exam 1 score")
    plt.ylabel("Exam 2 score")
    plt.legend(["Admitted","Not admitted"],loc=0)

m , n = X.shape[0], X.shape[1]
X, X_mean, X_std = featureNormalization(X)
X= np.append(np.ones((m,1)),X,axis=1)
y=y.reshape(m,1)
initial_theta = np.zeros((n+1,1))
cost, grad= costFunction(initial_theta,X,y)
theta , J_history = gradientDescent(X,y,initial_theta,1,400)
print("Cost of initial theta is",cost)
print("Gradient at initial theta (zeros):",grad)
print("Theta optimized by gradient descent:",theta)
print("The cost of the optimized theta:",J_history[-1])


# -*- coding: utf8 -*-
from sympy import *

print '''2.10.1.1'''
a = Rational(1, 2)
print "a =", a

print "pi**2 =", pi ** 2

print "pi.evalf() =", pi.evalf()

print "pi + exp(1) =", pi + exp(1)

print "(pi + exp(1)).evalf() =", (pi + exp(1)).evalf()

print "oo =", oo

print "oo > 99999 =", oo > 99999

print '''2.10.1.2'''
print "sqrt(2) =", sqrt(2).evalf(100)

print "1/2 + 1/3 =", Rational(1, 2) + Rational(1, 3)

F, r, sigma_max, sf = symbols('F r sigma_max safety_factor')

area = pi * r * r
sigma = F / area
solution = solve([sigma - sigma_max / sf], r)
print "solution =", simplify(solution)

print '''2.10.1.3'''

x = Symbol('x')
y = Symbol('y')

print "x+y+x-y =", x + y + x - y

print "(x+y)**2 =", (x + y) ** 2

print '''2.10.2.1'''

print "expand((x+y)**3) =", expand((x + y) ** 3)

print "expand(x+y, complex=True) =", expand(x + y, complex=True)

print "expand(cos(x+y), trig=True) =", expand(cos(x + y), trig=True)

print '''2.10.2.2'''

print "simplify((x+x*y)/x) =", simplify((x + x * y) / x)

print '''2.10.3.1'''
print "limit(sin(x)/x,x,0) =", limit(sin(x) / x, x, 0)

print "limit(x,x,oo) =", limit(x, x, oo)

print "limit(1/x,x,oo) =", limit(1 / x, x, oo)

print "limit(x**x,x,0) =", limit(x ** x, x, 0)

print '''2.10.3.2'''

print "diff(sin(x), x) =", diff(sin(x), x)

print "diff(sin(2*x), x) =", diff(sin(2 * x), x)

print "diff(tan(x), x) =", diff(tan(x), x)

print "limit((tan(x+y)-tan(x))/y,y,0) =", limit((tan(x + y) - tan(x)) / y, y, 0)

print "diff(sin(2*x), x, 1) =", diff(sin(2 * x), x, 1)

print "diff(sin(2*x), x, 2) =", diff(sin(2 * x), x, 2)

print "diff(sin(2*x), x, 3) =", diff(sin(2 * x), x, 3)

print '''2.10.3.3'''

print "series(cos(x), x) =", series(cos(x), x)

print "series(1/cos(x), x) =", series(1 / cos(x), x)

# Series 설명을 위하여 그래프를 그림
# 그래프 그리기 관련 기능 등을 담고 있는 pylab 모듈을 불러 들임
import pylab

x_deg = pylab.arange(-90, 90 + 1)
x_rad = pylab.deg2rad(x_deg)
y_cos = pylab.cos(x_rad)
y_series_1 = 1 * pylab.ones_like(x_rad)
y_series_2 = 1 - x_rad ** 2 / 2
y_series_3 = 1 - x_rad ** 2 / 2 + x_rad ** 4 / 24

pylab.plot(x_deg, y_cos, label='cos')
pylab.plot(x_deg, y_series_1, label='series 1')
pylab.plot(x_deg, y_series_2, label='series 2')
pylab.plot(x_deg, y_series_3, label='series 3')
pylab.grid()
pylab.legend(loc=0)
# pylab.show()

print '''2.10.3.5'''
print "integrate(6*x**5, x) =", integrate(6 * x ** 5, x)

print "integrate(sin(x), x) =", integrate(sin(x), x)

print "integrate(log(x), x) =", integrate(log(x), x)

print "integrate(2*x + sinh(x), x) =", integrate(2 * x + sinh(x), x)

print "integrate(exp(-x**2)*erf(x), x) =", integrate(exp(-x ** 2) * erf(x), x)

print "integrate(x**3, (x, -1, 1)) =", integrate(x ** 3, (x, -1, 1))

print "integrate(sin(x), (x, 0, pi/2)) =", integrate(sin(x), (x, 0, pi / 2))

print "integrate(cos(x), (x, -pi/2, pi/2)) =", integrate(cos(x), (x, -pi / 2, pi / 2))

print "integrate(exp(-x), (x, 0, oo)) =", integrate(exp(-x), (x, 0, oo))

print "integrate(exp(-x**2), (x, -oo, oo)) =", integrate(exp(-x ** 2), (x, -oo, oo))

print '''2.10.4'''
print "solve(x**4 - 1, x) =", solve(x ** 4 - 1, x)

print "solve([x + 5*y - 2, -3*x + 6*y - 15], [x,y]) =", solve([x + 5 * y - 2, -3 * x + 6 * y - 15], [x, y])

print "solve(exp(x) + 1, x) =", solve(exp(x) + 1, x)

f = x ** 4 - 3 * x ** 2 + 1
print "factor(f) =", factor(f)

print "factor(f, modulus=5) =", factor(f, modulus=5)

print "satisfiable(x&y) =", satisfiable(x & y)
print "satisfiable(x^y) =", satisfiable(x ^ y)

print '''2.10.5.1'''
print "Matrix([[1,0], [0,1]]) =", Matrix([[1, 0], [0, 1]])
A = Matrix([[1, x], [y, 1]])
print "A =", A
print "A**2 =", A ** 2

print '''2.10.5.2'''
f = Function('f')
print "f(x).diff(x, x) + f(x) =", f(x).diff(x, x) + f(x)
print "dsolve(f(x).diff(x, x) + f(x)) =", dsolve(f(x).diff(x, x) + f(x))

m, c, k, t = symbols('m c k t')
x = Function('x')
vib_eq = m * x(t).diff(t, t) + c * x(t).diff(t) + k * x(t)
result = dsolve(vib_eq)
print "dsolve() result"
print result

forced_vib_eq = m * x(t).diff(t, t) + c * x(t).diff(t) + k * x(t) - sin(t)
result = dsolve(forced_vib_eq)
print "forced vibration result:"
print result


# -*- coding: utf-8 -*-
"""

Estimate pi using random sampling

@author: 4oh4
Created 23/01/22

"""

import timeit

import numpy as np

def run(exp=6):
    # Run random sampling and calculate estimate of pi
    if exp>9:
        print('Limiting scale to 10^9 data points')
        exp = 9

    # Generate random data in (-1, 1) space
    n = 10**exp
    x = 2 * np.random.random(n) - 1
    y = 2 * np.random.random(n) - 1

    # Area of square
    area_square = 4

    # Estimate area of circle
    radius = 1

    points_in_circle = 0
    for i in range(n):

        distance = (x[i]**2 + y[i]**2)**0.5

        if distance < radius:
            points_in_circle += 1

    prob_point_in_circle = points_in_circle / n
    area_circle = prob_point_in_circle * area_square

    # Estimate pi
    pi_hat = area_circle / radius**2
    print('Estimate of pi: %f' % pi_hat)

if __name__ == "__main__":

    exp = 6
    repeats = 10

    print(f'Estimating pi using random sampling (non-optimised), n={10**exp}')

    wrapper_func = lambda : run(exp)

    duration = timeit.timeit(wrapper_func, number=repeats)

    print(f'{repeats} repeats: {duration} secs = {duration/repeats} secs/run')


import numpy as np
import sys
import pickle

def sigmoid(z, derivative = False):
    """Computes elemntwise sigmoid function

    Arguments:
        z {ndarray} -- Input array

    Keyword Arguments:
        derivative {bool} -- Will the output be a derivative (default: {False})

    Returns:
        ndarray -- values of sigmoid function or its derivative elementwise
    """
    z = np.clip(z, -709, 709)
    s =  np.exp(z) / (1 + np.exp(z))
    if derivative:
        s = s * ( 1 - s )
    return s

def ReLU(z, derivative = False):
    """Return value max(0,z)

    Arguments:
        z {ndarray} -- Input array or number

    Keyword Arguments:
        derivative {bool} -- Should the function return the derivative (default: {False})

    Returns:
        {ndarray} -- Outputs max(0,z) or the derivative
    """

    if derivative:
        return np.where(z > 0, 1, 0)
    else:
        return np.maximum(0,z)

def leaky_ReLU(z,leak_value=0.1, derivative=False):
    if derivative:
        return np.where(z > 0, 1, leak_value)
    else:
        return np.maximum(z * leak_value,z)

def initialize_parameters(layer_dims):
    """Initializes weights and biases according to input dimensions

    Arguments:
        layer_dims {array} -- input_dims, hidden_dims ..., output_dims

    Returns:
        Dictionary -- Values Wl and bl, 1 <= l <= L - 1, weights and biases respectively
    """
    params = {}
    L = len(layer_dims)
    for l in range(1, L):
        params["W" + str(l)] = np.random.uniform(low=-1, high=1, size=(layer_dims[l], layer_dims[l-1]))
        params["b" + str(l)] = np.zeros((layer_dims[l], 1))

    return params

def forward_activation(A_prev, W, b, activation):

    Z = np.dot(W,A_prev) + b

    if activation == "sigmoid":
        A = sigmoid(Z)
    elif activation == "relu" or activation == "ReLU":
        A = ReLU(Z)
    elif activation == "leaky ReLU" or activation == "leaky relu":
        A = leaky_ReLU(Z)

    cache = ( A_prev, W, b, Z)

    return A, cache

def forward_propagation(X, params):
    """Executes forward propagation algorithm

    Arguments:
        X {ndarray} -- Inputs
        params {dictionary} -- Weights and biases Wl and bl

    Returns:
        ndarray, list -- Returns output layer values and a list of caches containing A-prev, W, b, Z
    """

    caches = []
    A = X
    L = len(params) // 2

    for l in range(1,L):
        A_prev = A
        A,cache = forward_activation(A_prev, params["W" + str(l)], params["b" + str(l)], "sigmoid")
        caches.append(cache)

    AL,cache = forward_activation(A, params["W" + str(L)], params["b" + str(L)], "sigmoid")
    caches.append(cache)

    return AL, caches

def compute_cost(AL, Y, params, lambda_param):

    m = Y.shape[1]
    L = len(params) // 2
    regularization_param = 0

    for l in range(1, L + 1):
        regularization_param += np.sum(params["W" + str(l)] ** 2,keepdims=True)
    regularization_param = regularization_param * (lambda_param/(2*m))

    J = -( 1 / m ) * ( np.sum(Y * np.clip(np.log(np.clip(AL, sys.float_info.min, None)), -sys.float_info.max, sys.float_info.max) + (1 - Y) * np.clip(np.log(np.clip((1 - AL), sys.float_info.min, None)), -sys.float_info.max, sys.float_info.max), keepdims=True)) + regularization_param
    J = np.squeeze(J)

    return J

def backward_activation(dA, cache, activation):

    A_prev, W, b, Z = cache
    m = A_prev.shape[1]

    if activation == "sigmoid":
        dZ = dA * sigmoid(Z, derivative=True)
    elif activation == "relu" or activation == "ReLU":
        dZ = dA * ReLU(Z, derivative=True)
    elif activation == "leaky ReLU" or activation == "leaky relu":
        dZ = dA * leaky_ReLU(Z, derivative=True)

    dW = np.dot(dZ,A_prev.T)
    db = np.sum(dZ, axis=1, keepdims=True)
    dA_prev = np.dot(W.T,dZ)

    return dA_prev, dW, db

def backpropagation(AL, Y, caches):

    grads = {}
    L = len(caches)
    #m = AL.shape[1]

    dAL = -( (Y / np.clip(AL, sys.float_info.min, None)) - (1 - Y) / np.clip((1 - AL), sys.float_info.min, None))
    #dAL = AL - Y
    current_cache = caches[L-1]
    grads["dA" + str(L - 1)], grads["dW" + str(L)], grads["db" + str(L)] = backward_activation(dAL, current_cache, "sigmoid")

    for l in reversed(range(L - 1)):
        current_cache = caches[l]
        grads["dA" + str(l)], grads["dW" + str(l + 1)], grads["db" + str(l + 1)] = backward_activation(grads["dA" + str(l + 1)], current_cache, "sigmoid")

    return grads

def update_parameters(params, grads, learning_rate, lambda_param):

    L = len(params) // 2

    for l in range(1, L + 1):
        params["W" + str(l)] = (1 - learning_rate * lambda_param / 2) * params["W" + str(l)] - learning_rate * grads["dW" + str(l)]
        params["b" + str(l)] = params["b" + str(l)] - learning_rate * grads["db" + str(l)]

    return params

def train_L_layer_model(X, Y, layer_dims, learning_rate = 0.0075, lambda_param = 0, max_iterations = 3000, print_cost_every_iter = 0, load_parameters=False):

    costs = []

    """
    For randomizing input and output

    perm = np.random.permutation(X.shape[0])
    X = X.T[perm].T
    Y = Y.T[perm].T

    """

    if load_parameters:
        with open("learned_parameters.pickle", "rb") as handle:
            params = pickle.load(handle)
    else:
        params = initialize_parameters(layer_dims)

    for i in range(max_iterations):

        AL, caches = forward_propagation(X, params)
        cost = compute_cost(AL, Y, params, lambda_param)
        grads = backpropagation(AL, Y, caches)
        params = update_parameters(params, grads, learning_rate, lambda_param)

        if print_cost_every_iter and i % print_cost_every_iter == 0:
            print ("Cost after iteration %i: %f" %(i, cost))
        if print_cost_every_iter and i % print_cost_every_iter == 0:
            costs.append(cost)

    return params, costs

def predict(X, params, as_binary=False):
    Y_predicted, caches = forward_propagation(X, params)
    if as_binary:
        Y_predicted[Y_predicted > 0.5] = 1
        Y_predicted[Y_predicted <= 0.5] = 0
    return Y_predicted


import sys
import numpy as np
from scipy.stats import ttest_ind

# Use t-test to select features that are discriminative between two classes of samples
# data: an array, where rows are samples and columns are features
# label: a binary variable vector indicating the class labels. Its length must be the same
#       as the number of rows in data.
# cutoff: a positive number for selecting predictive features. If cutoff < 1, this function
#       selects features with p-values <= cutoff. If cutoff >= 1, it must be an integer
#       indicating the number of top features to be selected based on p-value.
# Returns a list of indices of the selected features.
def ttest_FS(data, label, cutoff):
    unique_label = list(set(label))
    if len(unique_label) != 2:
        print('T-test feature selection needs two sample classes')
        return None
    id0 = np.where(label == unique_label[0])[0]
    id1 = np.where(label == unique_label[1])[0]
    if len(id0) < 3 or len(id1) < 3:
        print('T-test feature selection requires every sample class has at least 3 samples')
        return None
    t, p = ttest_ind(a=data[id0, :], b=data[id1, :], axis=0, equal_var=False)
    if cutoff < 1:
        fid = np.where(p <= cutoff)[0]
    else:
        fid = sorted(range(len(p)), key=lambda x: p[x])[:int(cutoff)]
    return sorted(fid)

# Use Pearson correlation coefficient to select predictive features for regression problem.
# data: an array, where rows are samples and columns are features
# target: a vector of real numbers indicating the regression target. Its length is the same
#       as the number of rows in data.
# cutoff: a positive number for selecting predictive features. If cutoff < 1, this function
#       selects the features with an absolute correlation coefficient >= cutoff. If cutoff >= 1,
#       it must be an integer indicating the number of top features to be selected based on
#       the absolute correlation coefficient.
# Returns a list of indices of the selected features.
def correlation_FS(data, target, cutoff):
    cor = np.corrcoef(np.vstack((np.transpose(data), np.reshape(target, (1, len(target))))))
    cor = abs(cor[:-1, -1])
    if cutoff < 1:
        fid = np.where(cor >= cutoff)[0]
    else:
        fid = sorted(range(len(cor)), key=lambda x: cor[x], reverse=True)[:int(cutoff)]
    return sorted(fid)

# Use cross-correlation coefficient to select the features that are generalizable
# between data1 and data2.
# data1: an array, where rows are samples and columns are features
# data2: an array, where rows are samples and columns are features. data1 and data2 should have
#       an equal number of features. The orders of features in data1 and data2 should match.
# cutoff: a positive number for selecting generalizable features. If cutoff < 1, this function
#       selects the features with a cross correlation coefficient >= cutoff. If cutoff >= 1,
#       it must be an integer indicating the number of top features to be selected based on
#       the cross correlation coefficient.
# Returns a list of indices of the selected features.
def COXEN_FS(data1, data2, cutoff):
    cor1 = np.corrcoef(np.transpose(data1))
    cor2 = np.corrcoef(np.transpose(data2))
    num = data1.shape[1]
    cor = []
    for i in range(num):
        cor.append(np.corrcoef(np.vstack((list(cor1[:i, i]) + list(cor1[(i + 1):, i]),
                   list(cor2[:i, i]) + list(cor2[(i + 1):, i]))))[0, 1])
    cor = np.array(cor)
    if cutoff < 1:
        fid = np.where(cor >= cutoff)[0]
    else:
        fid = sorted(range(num), key=lambda x: cor[x], reverse=True)[:int(cutoff)]
    return sorted(fid)


#schmidtDecomp(...)   :   Gives Schmidt decomposition over spaces of given dimension

import numpy as np

__all__ = ['schmidtDecomp']

def schmidtDecomp(psi,da,db):
    r"""Takes an input a vector, and dimensions :math:'da' and :math:'db'
    of the two spaces over whose tensor product the vector is defined.
    Returns the Schmidt decomposition of the vector.

    Parameters
    ----------
    psi : numpy.ndarray
          1-d complex vector

    da : int
         Dimension of space 1

    db : int
         Dimension of space 2

    Returns
    ----------
    comp : numpy.ndarray
           A vector with components that are the Schmidt coefficients of the
           input psi

    U : numpy.ndarray
        Matrix with :math:'k^{th}' Schmidt vector on space 1 written as U[:,k]

    V : numpy.ndarray
        Matrix with :math:'k^{th}' Schmidt vector on space 2 written as V[:,k]
    """
    try:
        mat = psi.reshape(da,db)
    except:
        raise ValueError('Shape of psi and da*db do not seem compatible')
        return None
    U, D, V = np.linalg.svd(mat, full_matrices=True)
    return (D,U,V.T)


import numpy
from math import *

#radius of Earth in meters
R = 6371000

#returns the spherical distance between two coordinates in given in lat,lon
#result should be multiplied by R for the actual distance in meters
def distance(lat1, lon1, lat2, lon2):
    dlat = lat2-lat1
    dlon = lon2-lon1

    #using haversine formula
    a = sin(dlat/2.0) * sin(dlat/2.0) + \
        cos(lat1) * cos(lat2) * \
        sin(dlon/2.0) * sin(dlon/2)

    d = 2 * atan2(sqrt(a), sqrt(1.0-a))

    return d

#returns the area of the triangle enclosed by three lines of distances a, b, and c
#result should be multiplied with R**2 for the actual surface area in meters
def triangle_area(a, b, c):
    #compute the semiperimeter
    s = (a + b + c)/2

    #compute spherical excess using L'Huilier's Theorem
    tans = tan(s/2)
    tana = tan((s-a)/2)
    tanb = tan((s-b)/2)
    tanc = tan((s-c)/2)

    tanE4 = sqrt(tans * tana * tanb * tanc)

    E = 4 * atan(tanE4)

    return E

def triangle_area_points(lon1, lat1, lon2, lat2, lon3, lat3):
    a = distance(lat1, lon1, lat2, lon2)
    b = distance(lat2, lon2, lat3, lon3)
    c = distance(lat3, lon3, lat1, lon1)
    return triangle_area(a, b, c)


# demonstration of L2 regularization
#
# notes for this course can be found at:
# https://deeplearningcourses.com/c/data-science-linear-regression-in-python
# https://www.udemy.com/data-science-linear-regression-in-python

from __future__ import print_function, division
from builtins import range
# Note: you may need to update your version of future
# sudo pip install -U future

import numpy as np
import matplotlib.pyplot as plt

N = 50

# generate the data
X = np.linspace(0,10,N)
Y = 0.5*X + np.random.randn(N)

# make outliers
Y[-1] += 30
Y[-2] += 30

# plot the data
plt.scatter(X, Y)
plt.show()

# add bias term
X = np.vstack([np.ones(N), X]).T

# plot the maximum likelihood solution
w_ml = np.linalg.solve(X.T.dot(X), X.T.dot(Y))
Yhat_ml = X.dot(w_ml)
plt.scatter(X[:,1], Y)
plt.plot(X[:,1], Yhat_ml)
plt.show()

# plot the regularized solution
# probably don't need an L2 regularization this high in many problems
# everything in this example is exaggerated for visualization purposes
l2 = 1000.0
w_map = np.linalg.solve(l2*np.eye(2) + X.T.dot(X), X.T.dot(Y))
Yhat_map = X.dot(w_map)
plt.scatter(X[:,1], Y)
plt.plot(X[:,1], Yhat_ml, label='maximum likelihood')
plt.plot(X[:,1], Yhat_map, label='map')
plt.legend()
plt.show()


"""
tsp.py:  solve the traveling salesman problem

minimize the travel cost for visiting n customers exactly once
approach:
    - start with assignment model
    - add cuts until there are no sub-cycles
    - two cutting plane possibilities (called inside "solve_tsp"):
        - addcut: limit the number of edges in a connected component S to |S|-1
        - addcut2: require the number of edges between two connected component to be >= 2

Copyright (c) by Joao Pedro PEDROSO and Mikio KUBO, 2012
"""
import math
import random
import networkx
from pyscipopt import Model, quicksum

def solve_tsp(V,c):
    """solve_tsp -- solve the traveling salesman problem
       - start with assignment model
       - add cuts until there are no sub-cycles
    Parameters:
        - V: set/list of nodes in the graph
        - c[i,j]: cost for traversing edge (i,j)
    Returns the optimum objective value and the list of edges used.
    """

    def addcut(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))
        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            model.addCons(quicksum(x[i,j] for i in S for j in S if j>i) <= len(S)-1)
            print("cut: len(%s) <= %s" % (S,len(S)-1))
        return True

    def addcut2(cut_edges):
        G = networkx.Graph()
        G.add_edges_from(cut_edges)
        Components = list(networkx.connected_components(G))

        if len(Components) == 1:
            return False
        model.freeTransform()
        for S in Components:
            T = set(V) - set(S)
            print("S:",S)
            print("T:",T)
            model.addCons(quicksum(x[i,j] for i in S for j in T if j>i) +
                          quicksum(x[i,j] for i in T for j in S if j>i) >= 2)
            print("cut: %s >= 2" % "+".join([("x[%s,%s]" % (i,j)) for i in S for j in T if j>i]))
        return True

    # main part of the solution process:
    model = Model("tsp")

    model.hideOutput() # silent/verbose mode
    x = {}
    for i in V:
        for j in V:
            if j > i:
                x[i,j] = model.addVar(ub=1, name="x(%s,%s)"%(i,j))

    for i in V:
        model.addCons(quicksum(x[j,i] for j in V if j < i) + \
                        quicksum(x[i,j] for j in V if j > i) == 2, "Degree(%s)"%i)

    model.setObjective(quicksum(c[i,j]*x[i,j] for i in V for j in V if j > i), "minimize")

    EPS = 1.e-6
    isMIP = False
    while True:
        model.optimize()
        edges = []
        for (i,j) in x:
            if model.getVal(x[i,j]) > EPS:
                edges.append( (i,j) )

        if addcut(edges) == False:
            if isMIP:     # integer variables, components connected: solution found
                break
            model.freeTransform()
            for (i,j) in x:     # all components connected, switch to integer model
                model.chgVarType(x[i,j], "B")
                isMIP = True

    return model.getObjVal(),edges

def distance(x1,y1,x2,y2):
    """distance: euclidean distance between (x1,y1) and (x2,y2)"""
    return math.sqrt((x2-x1)**2 + (y2-y1)**2)

def make_data(n):
    """make_data: compute matrix distance based on euclidean distance"""
    V = range(1,n+1)
    x = dict([(i,random.random()) for i in V])
    y = dict([(i,random.random()) for i in V])
    c = {}
    for i in V:
        for j in V:
            if j > i:
                c[i,j] = distance(x[i],y[i],x[j],y[j])
    return V,c

if __name__ == "__main__":
    import sys

    # Parse argument
    if len(sys.argv) < 2:
        print("Usage: %s instance" % sys.argv[0])
        print("Using randomized example instead")
        n = 200
        seed = 1
        random.seed(seed)
        V,c = make_data(n)
    else:
        from read_tsplib import read_tsplib
        try:
            V,c,x,y = read_tsplib(sys.argv[1])
        except:
            print("Cannot read TSPLIB file",sys.argv[1])
            exit(1)

    obj,edges = solve_tsp(V,c)

    print("\nOptimal tour:",edges)
    print("Optimal cost:",obj)


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

# In[8]:

import numpy as np

def coin_em(rolls, theta_A=None, theta_B=None, maxiter=10):
    # Initial Guess
    theta_A = theta_A or random.random()
    theta_B = theta_B or random.random()
    thetas = [(theta_A, theta_B)]
    # Iterate
    for c in range(maxiter):
        print("#%d:\t%0.2f %0.2f" % (c, theta_A, theta_B))
        heads_A, tails_A, heads_B, tails_B = e_step(rolls, theta_A, theta_B)
        theta_A, theta_B = m_step(heads_A, tails_A, heads_B, tails_B)

    thetas.append((theta_A,theta_B))
    return thetas, (theta_A,theta_B)

def e_step(rolls, theta_A, theta_B):
    """Produce the expected value for heads_A, tails_A, heads_B, tails_B
    over the rolls given the coin biases"""

    heads_A, tails_A = 0,0
    heads_B, tails_B = 0,0
    for trial in rolls:
        likelihood_A = coin_likelihood(trial, theta_A)
        likelihood_B = coin_likelihood(trial, theta_B)
        p_A = likelihood_A / (likelihood_A + likelihood_B)
        p_B = likelihood_B / (likelihood_A + likelihood_B)
        heads_A += p_A * trial.count("H")
        tails_A += p_A * trial.count("T")
        heads_B += p_B * trial.count("H")
        tails_B += p_B * trial.count("T")
    return heads_A, tails_A, heads_B, tails_B

def m_step(heads_A, tails_A, heads_B, tails_B):
    """Produce the values for theta that maximize the expected number of heads/tails"""

    # Replace dummy values with your implementation
    theta_A = heads_A / (heads_A + tails_A)
    theta_B = heads_B / (heads_B + tails_B)
    return theta_A, theta_B

def coin_likelihood(roll, bias):
    # P(X | Z, theta)
    numHeads = roll.count("H")
    flips = len(roll)
    return pow(bias, numHeads) * pow(1-bias, flips-numHeads)

# In[9]:

rolls = [ "HTTTHHTHTH", "HHHHTHHHHH", "HTHHHHHTHH",
          "HTHTTTHHTT", "THHHTHHHTH" ]
thetas, _ = coin_em(rolls, 0.6, 0.5, maxiter=6)

# In[10]:

get_ipython().run_line_magic('matplotlib', 'inline')
from matplotlib import pyplot as plt
import matplotlib as mpl

def plot_coin_likelihood(rolls, thetas=None):
    # grid
    xvals = np.linspace(0.01,0.99,100)
    yvals = np.linspace(0.01,0.99,100)
    X,Y = np.meshgrid(xvals, yvals)

    # compute likelihood
    Z = []
    for i,r in enumerate(X):
        z = []
        for j,c in enumerate(r):
            z.append(coin_marginal_likelihood(rolls,c,Y[i][j]))
        Z.append(z)

    # plot
    plt.figure(figsize=(10,8))
    C = plt.contour(X,Y,Z,150)
    cbar = plt.colorbar(C)
    plt.title(r"Likelihood $\log p(\mathcal{X}|\theta_A,\theta_B)$", fontsize=20)
    plt.xlabel(r"$\theta_A$", fontsize=20)
    plt.ylabel(r"$\theta_B$", fontsize=20)

    # plot thetas
    if thetas is not None:
        thetas = np.array(thetas)
        plt.plot(thetas[:,0], thetas[:,1], '-k', lw=2.0)
        plt.plot(thetas[:,0], thetas[:,1], 'ok', ms=5.0)

def coin_marginal_likelihood(rolls, biasA, biasB):
    # P(X | theta)
    trials = []
    for roll in rolls:
        h = roll.count("H")
        t = roll.count("T")
        likelihoodA = coin_likelihood(roll, biasA)
        likelihoodB = coin_likelihood(roll, biasB)
        trials.append(np.log(0.5 * (likelihoodA + likelihoodB)))
    return sum(trials)

# In[11]:

plot_coin_likelihood(rolls, thetas)


import numpy as np

np.random.seed(0)

xs = np.array(((0,0), (0,1), (1,0), (1,1)), dtype=np.float32)
ts = np.array(((-1), (-1), (-1), (1)), dtype=np.float32)
lr = 0.1

# perceptron
w = np.random.normal(0., 1, (3))
print("weight >>", w)

# add bias
_xs = np.hstack([xs, [[1] for _ in range(4)]])

# train
ite = 0
while True:
    ite += 1
    # feed forward
    ys = np.dot(_xs, w)
    #ys = np.array(list(map(lambda x: np.dot(w, x), _xs)))

    print("iteration:", ite, "y >>", ys)

    # update parameters
    if len(np.where(ys * ts < 0)[0]) < 1:
        break

    _ys = ys.copy()
    _ts = ts.copy()
    _ys[ys * ts >= 0] = 0
    _ts[ys * ts >= 0] = 0
    En = np.dot(_ts, _xs)

    w += lr * En

    """
    for i in np.where(ys * ts < 0)[0]:
        En += ts[i]
        if ys[i] * ts[i] < 0:
            En += ts[i] * _xs[i]

    print("iteration:", ite, "y >>", ys)
    if np.any(En != 0):
        w += lr * En
    else:
        break
    """

print("training finished!")
print("weight >>", w)

# test
ys = np.array(list(map(lambda x: np.dot(w, x), _xs)))

for i in range(4):
    ys = np.dot(w, _xs[i])
    print("in >>", xs[i], ", out >>", ys)


# -*- coding: utf-8 -*-

import numpy as np

R = 6371000  # earth's radius in meters.

def great_circle_distance(start_lat, start_lon, end_lat, end_lon):
    """Distance in meters

    """
    start_lat, start_lon, end_lat, end_lon = map(np.deg2rad, [start_lat, start_lon, end_lat, end_lon])
    x = (end_lon - start_lon) * np.cos(0.5 * (start_lat + end_lat))
    y = end_lat - start_lat
    return R * np.sqrt(x ** 2 + y ** 2)

def bearing(start_lat, start_lon, end_lat, end_lon):
    """Bearing in radians

    """
    start_lat, start_lon, end_lat, end_lon = map(np.deg2rad, [start_lat, start_lon, end_lat, end_lon])
    del_lon = end_lon - start_lon
    num = np.sin(del_lon) * np.cos(end_lat)
    den = np.cos(start_lat) * np.sin(end_lat) \
          - np.sin(start_lat) * np.cos(end_lat) * np.cos(del_lon)
    return np.arctan2(num, den)

def end_location(start_lat, start_lon, distance_in_meter, bearing):
    """End point latitude and longitude.

    arguments
    ---------
    start_lat, start_lon: array_like
        strating point latitudes and longitudes.
    distance_in_meter: array_like
        distance from the starting point to the desired end point.
    bearing: array_like
        angle in radians with the true north.

    returns
    -------
    end_lat, end_lon: ndarray or scalar
        The desired ending position latitude and longitude.

    """

    start_lat, start_lon = map(np.deg2rad, [start_lat, start_lon])
    alpha = np.asarray(distance_in_meter) / R

    lat = np.arcsin(np.sin(start_lat) * np.cos(alpha) \
                    + np.cos(start_lat) * np.sin(alpha) * np.cos(bearing))

    num = np.sin(bearing) * np.sin(alpha) * np.cos(start_lat)
    den = np.cos(alpha) - np.sin(start_lat) * np.sin(lat)

    lon = start_lon + np.arctan2(num, den)

    return (np.rad2deg(lat), np.rad2deg(lon))


import numpy as np
from numba import njit as jit
from numpy.linalg import norm

from poliastro.core.elements import coe_rotation_matrix, rv2coe

@jit
def eclipse_function(k, u_, r_sec, R_sec, R_primary, umbra=True):
    """Calculates a continuous shadow function.

    Parameters
    ----------
    k: float
        Standard gravitational parameter (km^3 / s^2).
    u_: ~np.array
        Satellite position and velocity vector with respect to the primary body.
    r_sec: ~np.array
        Position vector of the secondary body with respect to the primary body.
    R_sec: float
        Equatorial radius of the secondary body.
    R_primary: float
        Equatorial radius of the primary body.
    umbra: bool
        Whether to calculate the shadow function for umbra or penumbra, defaults to True
        i.e. calculates for umbra.

    Note
    ----
    The shadow function is taken from Escobal, P. (1985). Methods of orbit determination.
    The current implementation assumes circular bodies and doesn't account for flattening.

    """
    # Plus or minus condition
    pm = 1 if umbra else -1
    p, ecc, inc, raan, argp, nu = rv2coe(k, u_[:3], u_[3:])

    PQW = coe_rotation_matrix(inc, raan, argp)
    # Make arrays contiguous for faster dot product with numba.
    P_, Q_ = np.ascontiguousarray(PQW[:, 0]), np.ascontiguousarray(PQW[:, 1])

    r_sec_norm = norm(r_sec)
    beta = (P_ @ r_sec) / r_sec_norm
    zeta = (Q_ @ r_sec) / r_sec_norm

    sin_delta_shadow = np.sin((R_sec - pm * R_primary) / r_sec_norm)

    cos_psi = beta * np.cos(nu) + zeta * np.sin(nu)
    shadow_function = (
        ((R_primary ** 2) * (1 + ecc * np.cos(nu)) ** 2)
        + (p ** 2) * (cos_psi ** 2)
        - p ** 2
        + pm * (2 * p * R_primary * cos_psi) * (1 + ecc * np.cos(nu)) * sin_delta_shadow
    )

    return shadow_function

@jit
def line_of_sight(r1, r2, R):
    """Calculates the line of sight condition between two position vectors, r1 and r2.

    Parameters
    ----------
    r1: ~np.array
        The position vector of the first object with respect to a central attractor.
    r2: ~np.array
        The position vector of the second object with respect to a central attractor.
    R: float
        The radius of the central attractor.

    Returns
    -------
    delta_theta: float
        Greater than or equal to zero, if there exists a LOS between two objects
        located by r1 and r2, else negative.

    """
    r1_norm = np.linalg.norm(r1)
    r2_norm = np.linalg.norm(r2)

    theta = np.arccos((r1 @ r2) / r1_norm / r2_norm)
    theta_1 = np.arccos(R / r1_norm)
    theta_2 = np.arccos(R / r2_norm)

    return (theta_1 + theta_2) - theta


#!/usr/bin/env python3

import numpy as np

class PCA(object):
    """
    Performs Principal Component Analysis

    Parameters
    --------------------------------------------------
        k: int the number of clusters to form i.e. the number of centroids to generate
    """
    def __init__(self, k = 1):
        self.k = k

    def fit(self, x):
        """
        Parameters
        --------------------------------------------------
            x: ndarray of shape (n_samples, n_features)

        Returns
        --------------------------------------------------
            eigen_values : ndarray of shape (k, )
            eigen_vectors: ndarray of shape (n_features, k)
        """
        m = x.shape[0]
        n = x.shape[1]

        self.mu    = np.mean(x, axis = 0)
        self.sigma = np.std(x, axis = 0)

        if min(m, n) >= self.k:
            x_scaled = self.scale_and_normalize(x)
            c_matrix = self.compute_covariance_matrix(x_scaled)
            w, v     = self.compute_eigen_vectors(c_matrix)

            self.eigen_values  = w[  :self.k]
            self.eigen_vectors = v[:,:self.k]
        else:
            raise ValueError(f"k = {self.k} must be between 0 and min(m, n) = {min(m, n)}")

    def transform(self, x):
        """
        Parameters
        --------------------------------------------------
            x: ndarray of shape (n_samples, n_features)

        Returns
        --------------------------------------------------
            x_trans: ndarray of shape (n_samples, n_features)
        """
        x_scaled = self.scale_and_normalize(x)
        x_trans  = np.dot(x_scaled, self.eigen_vectors)

        return x_trans

    def inverse_transform(self, x):
        """
        Parameters
        --------------------------------------------------
            x: ndarray of shape (n_samples, k)

        Returns
        --------------------------------------------------
            x_orig: ndarray of shape (n_samples, n_features)
        """
        x_inv  = np.dot(x, self.eigen_vectors.T)
        x_orig = (x_inv * self.sigma) + self.mu

        return x_orig

    def scale_and_normalize(self, x):
        """
        Parameters
        --------------------------------------------------
            x: ndarray of shape (n_samples, n_features)

        Returns
        --------------------------------------------------
            x: ndarray of shape (n_samples, n_features)
        """
        x = (x - self.mu) / self.sigma

        return x

    def compute_covariance_matrix(self, x):
        """
        Parameters
        --------------------------------------------------
            x: ndarray of shape (n_samples, n_features)

        Returns
        --------------------------------------------------
            matrix: ndarray of shape (n_features, n_features)
        """
        m      = x.shape[0]
        matrix = (1 / (m - 1)) * np.dot(x.T, x)

        return matrix

    def compute_eigen_vectors(self, m):
        """
        Parameters
        --------------------------------------------------
            m: ndarray of shape (n_features, n_features)

        Returns
        --------------------------------------------------
            w: ndarray of shape (k, )
            v: ndarray of shape (n_features, k)
        """
        w, v = np.linalg.eig(m)

        return w, v


#Problem 21:
#Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
#If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable #numbers.
#For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284. The #proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.
#Evaluate the sum of all the amicable numbers under 10000.

import sympy as sp

def main():
    summa = 0
    for i in range(1,10000):
        num1 = sum(sp.divisors(i)) - i
        num2 = sum(sp.divisors(num1)) - num1

        if(num1 != num2):
            if(num2 == i):
                summa += i
                #print i
    print summa
main()


#! python3
# coding: utf-8
# The prime 41, can be written as the sum of six consecutive primes:
# 41 = 2 + 3 + 5 + 7 + 11 + 13
# This is the longest sum of consecutive primes that adds to a prime below one-hundred.
# The longest sum of consecutive primes below one-thousand that adds to a prime, contains 21 terms, and is equal to 953.
# Which prime, below one-million, can be written as the sum of the most consecutive primes?
# https://projecteuler.net/problem=50

from time import perf_counter
from functools import cache, lru_cache
import numpy as np

def get_prime_sieve(n):
    "return True if index is prime"
    sieve = [True]*(n+1)
    for i in range(2, len(sieve)):
        if sieve[i]:
            k=2
            while (prod := i*k) <= n:
                sieve[prod] = False
                k += 1
    sieve[0] = False
    sieve[1] = False
    return sieve

def list_of_primes(n):
    "Returns list of primes less than n+1"
    primes = []
    sieve = get_prime_sieve(n+1)
    for i in range(n+1):
        if sieve[i]:
            primes.append(i)
    return primes

@cache
def find_longest(limit):
    global longest_sum_prime, lengt_of_seq
    start = perf_counter()
    primes = list_of_primes(limit)
    sieve = get_prime_sieve(limit)
    print("\nTime: ", perf_counter() - start, '\n')
    sum_of_seq = 0
    i = 0
    while i < len(primes) - lengt_of_seq:
    #for i in range(len(primes)-lengt_of_seq):
        localsum = primes[i]
        for j in range(i+1+lengt_of_seq, len(primes)):
            if localsum > limit or not sieve[localsum]:
                continue
            locallen = j-i
            #if localsum in primes:
            if locallen > lengt_of_seq:
                sum_of_seq = localsum
                lengt_of_seq = locallen
                longest_sum_prime = localsum
            localsum += j #sum(primes[i:j])
        i += 1
    return longest_sum_prime, lengt_of_seq

def main():
    start = perf_counter()
    global longest_sum_prime, lengt_of_seq
    longest_sum_prime = 0
    lengt_of_seq = 0
    print(f"\nLongest sum of consecutive primes is: {find_longest(1_0_0)}")
    print(f"Lengt of seq is :", lengt_of_seq)
    print("\nTime: ", perf_counter() - start)

if __name__ == "__main__":
    main()


"""
  Commonly used model functions

  Copyright (c) 2013, rhambach.
    This file is part of the TEMareels package and released
    under the MIT-Licence. See LICENCE file for details.
"""
import numpy as np;

def asymmetry(x, x0, fwhm, a):
  """
  asymmetric broadening following
  [Vibrational Spectroscopy 47 (2008) 66-69]
  """
  return 2.*fwhm / ( 1. +  np.exp( a * (x-x0) ) );

def gauss(x, x0, A, fwhm):
  """
   normalised gaussian function
   A   ... integral
   x0  ... position
   fwhm... full widht half maximum
   [http://mathworld.wolfram.com/GaussianFunction.html]
  """
  sigma = fwhm / 2.354820045;  # fwhm / 2*sqrt(2 ln(2))
                               # normalis. const 1/sqrt(2pi)
  return A/2.50662827/sigma * \
          np.exp(-(x-x0)**2/(2*sigma**2));

def gauss_asymmetric(x, x0, A, fwhm, a):
  return gauss(x, x0, A, asymmetry(x,x0,fwhm,a));

def lorentz(x, x0, A, fwhm):
  """
   normalised lorentzian function
   A   ... integral
   x0  ... position
   fwhm... full widht half maximum
   [http://mathworld.wolfram.com/LorentzianFunction.html]
  """
  sigma = fwhm / 2.;
  return A/np.pi *  sigma / ( sigma**2 + (x-x0)**2 );

def lorentz_asymmetric(x, x0, A, fwhm, a):
  return lorentz(x, x0, A, asymmetry(x,x0,fwhm,a));

def lorentz_background(x, x0, A, fwhm, n, m):
  sigma = fwhm / 2.;
  return A/np.pi *  sigma / ( sigma**2 + (x-x0)**2 ) + m*x + n

def fano(x, x0, A, fwhm, a=1, q=10.):
  eps = (x-x0)/0.5/fwhm;
  return A*( a**2 * (q+eps)**2 / q**2/ (1.+eps)**2 + (1-a) );


#! /usr/bin/env python

import numpy as np

def gaussian_kernel(x, mu, sig):
    '''
    Construct a gaussian function.

    Args:
        x (array-like): Data array
        mu (float): Mean of the distribution
        sig (float): Standard deviation

    Returns:
        Gaussian function.
    '''

    return np.exp(-0.5*((x-mu)/sig)**2)


import numpy as np
from exam import hours_studied, calculated_coefficients, intercept

def log_odds(features, coefficients,intercept):
  return np.dot(features,coefficients) + intercept

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

def predict_class(features, coefficients,intercept,threshold):
  calculated_log_odds = log_odds(features, coefficients, intercept)
  #return calculated_log_odds
  probabilities = sigmoid(calculated_log_odds)
  #return probabilities
  return np.where(probabilities >= threshold, 1, 0)

final_results = predict_class(hours_studied, calculated_coefficients, intercept, 0.5)
print(final_results)

Introduction to Classification (Supervised Learning)/thresholding.py


from math import log

import numpy as np

def conditional_entropy(x_j, y):
    countX = {x: 0 for x in x_j}
    dictYByX = {x: {} for x in x_j}
    for i in range(len(y)):
        x_val = x_j[i]
        y_val = y[i]
        countX[x_val] += 1
        dictYByX[x_val].update({y_val: dictYByX[x_val].get(y_val, 0) + 1})
    entropy = 0.0
    for x in countX.keys():
        partEntropy = 0.0
        curDict = dictYByX[x]
        partEntropy = sum(map(lambda inClass: elog(inClass / countX[x]), curDict.values()))
        entropy += countX[x] / len(y) * partEntropy
    return -entropy

def matrix_mutual_information(x, y):
    return np.apply_along_axis(mutual_information, 0, x, y)

def mutual_information(x, y):
    return entropy(y) - conditional_entropy(x, y)

def conditional_mutual_information(x, y, z):
    return entropy(list(zip(x, z))) + entropy(list(zip(y, z))) - entropy(list(zip(x, y, z))) - entropy(z)

def joint_mutual_information(x, y, z):
    return mutual_information(x, z) + conditional_mutual_information(y, z, x)

def interaction_information(x, y, z):
    return mutual_information(x, z) + mutual_information(y, z) - joint_mutual_information(x, y, z)

def elog(x):
    if x <= 0. or x >= 1.:
        return 0
    else:
        return x * log(x)

def entropy(x):
    d = dict()
    for obj in x:
        d[obj] = d.get(obj, 0) + 1
    probs = map(lambda z: float(z) / len(x), d.values())
    return -sum(map(elog, probs))


import path_setup
#setup path to include external recourses
nb_dir = path_setup.path_setup()
data_dir = nb_dir + '/Data/'

# The modules used for actually crunching the simulation numbers (nDim_Symbolic_Function, Integrator) were made by me and are
# covered under the MIT free license. Please respect these terms and keep the license file with the code, Thank you!

import nDim_Symbolic_Function as vfunc
import matplotlib.pyplot as plt
import Integrator as numInt
import matplotlib as mpl
import sympy as sym
import numpy as np
import torch
import math

# This line is for debugging purposes, change from ignore to error if you want to try and debug
# the "dtype=object when creating an array from "ragged" sequence" warning
np.warnings.filterwarnings('ignore', category=np.VisibleDeprecationWarning)

# this is a major player in computational speed using c evaluatable lambda functions
# Without this module, we lose about on the highend, on the order of 100 cpu cycles to return a result fronm python vs c lambdas
from sympy.utilities.lambdify import lambdastr
# set output precision so as to not spam the screen with long numbers
np.set_printoptions(precision=2)
# needs ipympl for realtime interactive displays using vs code
from IPython.display import display, Markdown, Latex
# Shebang line for interactive output in vs_code, comment this out if you have troubles running the notebook
# %matplotlib widget

t = sym.symbols('t')

# Sympy Functions
x = sym.Function('x')(t)
y = sym.Function('y')(t)
z = sym.Function('z')(t)

dxdt = x.diff(t)
dydt = y.diff(t)
dzdt = z.diff(t)

Cd = 0.35       # unitless coefficent of drag : https://blogs.fangraphs.com/exploring-the-variation-in-the-drag-coefficient-of-the-baseball/
# according to this websight, the values of the Cd for a baseball depending on the season using a typical regulation baseball range from around
# 0.3 to o.375 (unitless), So I will be using a 0.35 for Cd. This differs from the 0.45 of a perfect sphere due to surface imperfections causing
# turbulance similar to a golfball, thereby lowering the Cd.
rho = 1.205     # kg/m^3 <=> 1.205 g/L : this is the density of air at astp
S_a = 0.004145  # m^2 : this is the minimum cross-sectional area of a regulation baseball, it was supprising to me that the top end of this can be
# as much as 5% greater! A bit of a loose regulation if you ask me!
drag_const = Cd*rho*S_a/2 # this is the constant for the drag equation
print(f'The drag constant is equal to {drag_const} kg/m')

# Force components in cartesian coords set equal to the mass times the acceleration and then later integrated numerically
Fx = - drag_const*dxdt**2
Fy = - drag_const*dydt**2
Fz = - drag_const*dzdt**2 - 9.81 # assuming nominal Earth gravity in meters per second

F_ = vfunc.nDim_Symbolic_Function([Fx,Fy,Fz],[dxdt,dydt,dzdt],[[dxdt],[dydt],[dzdt]])
# ^ this line is :                Functions   all variables  function specific variables

# I set this to 10 seconds for no other reason than to let it run for a while and add
# an order of magnitude to the run time, but you can delete the comment and input whatever you want.
real_time = 10 # int(input('Enter durration of (positive integer) time in seconds to simulate: '))

dt = 2.5E-03 # seconds

# The variable time_slice sets the output frequency so that we are not capturing more data then needed from the
# simulation, IE we dont need to follow the baseball every millimeter, but looking at it in say 5 centimeter
# increments is much less data by an order of magnitude or more, but gives a proper picture of the ball moving overall
# With this in mind, I set the output time step at 1 second after a little bit of experimenting and this seems to be a
# good compromise between granularity and memory for longer running simulations (say, a minute of real time or more).
output_time_step = 1 # second

# initial conditions
# We throw the baseball such that we can see it has proper behavior in 3D, it is thrown at a pi/4 angle from the xy plane and at a pi/4 angle from the xz plane.
V_mag = 44.7 # m/s <=> 100mph
theta = np.pi/4
phi = np.pi/4
Vx = V_mag*np.cos(theta)*np.sin(phi)
Vy = V_mag*np.sin(theta)*np.sin(phi)
Vz = V_mag*np.cos(phi)
x_0 = 0
y_0 = 0
z_0 = 1.8288 # meters. Eew american freedom units make messy numbers lol

print(f'The initial speed is {np.sqrt(Vx**2+Vy**2+Vz**2)} meters per second')

# This is mostly self contained right now, but needs to be reinstantized each time.
# The __del__ method in python is much more finicky than C++ destructors and a bit of a mystery to me :(

F = numInt.Integrator(F_,0,real_time,output_time_step,dt,x_0,y_0,z_0,Vx,Vy,Vz)
F.sim_and_plot(3)
del F

F = numInt.Integrator(F_,0,real_time,output_time_step,dt,x_0,y_0,z_0,Vx,Vy,Vz)
F.sim_and_plot(2)
del F

F = numInt.Integrator(F_,0,real_time,output_time_step,dt,x_0,y_0,z_0,Vx,Vy,Vz)
F.sim_and_plot(1)
del F


#!/usr/bin/env python3
# coding: utf-8
"""
@author: Ping Qiu  qiuping1@genomics.cn
@last modified by: Ping Qiu
@file:correlation.py
@time:2021/03/11
"""
import numpy as np
import pandas as pd
from scipy import stats

def pearson(arr1, arr2):
    """
    calculate pearson correlation between two numpy arrays.
    :param arr1: one array, the feature is a column. the shape is  `m * n`
    :param arr2: the other array, the feature is a column. the shape is `m * k`
    :return: a pearson score np.array , the shape is `k * n`
    """
    assert arr1.shape[0] == arr2.shape[0]
    n = arr1.shape[0]
    sums = np.multiply.outer(arr2.sum(0), arr1.sum(0))
    stds = np.multiply.outer(arr2.std(0), arr1.std(0))
    return (arr2.T.dot(arr1) - sums / n) / stds / n

def pearson_corr(df1, df2):
    """
    calculate pearson correlation between two dataframes.
    :param df1: one dataframe
    :param df2: the other dataframe
    :return: a pearson score dataframe, the index is the columns of `df1`, the columns is the columns of `df2`
    """
    v1, v2 = df1.values, df2.values
    corr_matrix = pearson(v1, v2)
    return pd.DataFrame(corr_matrix, df2.columns, df1.columns)

def spearmanr_corr(df1, df2):
    """
    calculate pearson correlation between two dataframes.
    :param df1: one dataframe
    :param df2: the other dataframe
    :return: a spearmanr score dataframe, the index is the columns of `df1`, the columns is the columns of `df2`
    """
    score, pvalue = stats.spearmanr(df1.values, df2.values)
    score = score[df1.shape[1]:, 0:df1.shape[1]]
    return pd.DataFrame(score, df2.columns, df1.columns)


########################################################################
#
# Various statistical functions.
#
########################################################################
#
# This file is part of FinanceOps:
#
# https://github.com/Hvass-Labs/FinanceOps
#
# Published under the MIT License. See the file LICENSE for details.
#
# Copyright 2021 by Magnus Erik Hvass Pedersen
#
########################################################################

import numpy as np
from scipy.stats import norm

########################################################################

def normal_prob_loss(mean, std):
    """
    Probability of loss for a normal distribution.

    :param mean: Float with mean.
    :param std: Float with standard deviation.
    :return: Float with probability of loss.
    """
    return norm.cdf(0.0, loc=mean, scale=std)

def normal_prob_less_than(mean1, std1, mean2, std2):
    """
    Probability that X1 < X2 for two independent and normal-distributed random
    variables X1 and X2, where X1 ~ N(mean1, std1^2) and X2 ~ N(mean2, std2^2)

    :param mean1: Float with mean for X1.
    :param std1: Float with std.dev. for X1.
    :param mean2: Float with mean for X2.
    :param std2: Float with std.dev. for X2.
    :return: Float with probability that X1 < X2.
    """
    # The difference of two normal random variables is also a random normal
    # variable with the following mean and std.dev. so we can simply calculate
    # the probability of that new random variable being less than zero.
    mean = mean1 - mean2
    std = np.sqrt(std1 ** 2 + std2 ** 2)
    return norm.cdf(0.0, loc=mean, scale=std)

########################################################################


import matplotlib.pyplot as plt
import numpy as np

n = 2
m = 1000

def predict(a_mat, x_vec):
    """You need to care about single and multidimensional arrays."""
    return (a_mat @ x_vec).flatten()

# create sin and cos input data
time = np.linspace(0, 10, m)
a = np.vstack((np.sin(time), np.cos(time))).T

# choose a parameter vector
x = np.array([[3.], [-8.]])
print("True params", x.flatten())

# create noise
noise = np.random.randn(m) * 0.02

# predict model with additive noise
b = predict(a, x) + noise

# solve least squares estimate
ata = a.T @ a
atb = a.T @ b
x_hat = np.linalg.solve(a=ata, b=atb)

print("Estimated params", x_hat)

plt.plot(b, 'k.')
plt.title("A linear regression task")
plt.plot(predict(a, x_hat), 'r:')
plt.legend(["Noisy data", "Least squares estimate"])
plt.show()


import numpy as np

class Activation:
    def __init__(self,activation):
        self.activation = activation
        self.activation_derivative = np.ones(shape = (1,1))

    def getActivation(self):
        if self.activation.lower() == 'sigmoid':
            def func(y):
                y_ret = np.matrix(1/(1+np.exp(1-0.01*y)*np.exp(-1)))
                self.activation_derivative = np.multiply(y_ret, np.ones(y_ret.shape[-1])- y_ret)
                return y_ret
        elif self.activation.lower() == 'relu':
            def func(y):
                y_ret = np.where(y<0, 0, y)
                self.activation_derivative = np.matrix(np.where(y>0, 1, 0))
                return y_ret
        elif self.activation.lower() == 'leakyrelu':
            def func(y):
                alpha = 0.03
                y_ret = np.where(y > 0, y, y*alpha)
                self.activation_derivative = np.matrix(np.where(y>0, 1, alpha))
                return y_ret
        elif self.activation.lower() == 'softmax':
            def func(y):
                shift_y = y - np.max(y)
                exps = np.exp(shift_y)
                softmax = np.array(exps / np.sum(exps,axis=1))
                self.activation_derivative = np.ones(y.shape[-1])
                return softmax
        elif self.activation.lower() == 'tanh':
            def func(y):
                act_tanh = np.tanh(y)
                self.activation_derivative = (1 - np.power(act_tanh, 2))/2
                return act_tanh
        elif self.activation.lower() == 'softplus':
            def func(y):
                y_ret = np.log(1.0 + (np.exp(y)))
                self.activation_derivative = 1.0 / (1.0 + np.exp(-y))
                return y_ret
        return func


'''
linearerror.py

- Written on 2/13/2018 for CLaSP 405 by Lab TA A. Azari.
Purpose:
- To calculate with x and y the error on the coefficents
as demonstrated in eqn. 8.15, 8.16, 8.17 in Taylor -
An Introduction to Error Analysis.

'''

def calcCoeffsErr(x, rootMeanSquareError):
    '''
    This function calculates the error on the coefficents or
    equation 8.16 & 8.17 in Taylor. Inputs are the x values
    of the equation and the RMSE on the fit.

    Outputs the error on the slope, THEN on the intercept.

    '''
    import numpy as np

    delta = len(x)*sum(x**2) - (sum(x)**2)

    errSlope = rootMeanSquareError * np.sqrt((len(x)) / delta)
    errIntercept = rootMeanSquareError * np.sqrt(sum((x**2)) / delta)

    return(errSlope, errIntercept)

def calcRMSE(yModel, yData):
    '''
    This calculates sigma y, or equation 8.15 in Taylor. Outputs
    error on the fit of regressions.
    '''
    import numpy as np

    sumOfDiff = sum(((yModel - yData)**2))
    meanSquareError = (sumOfDiff / (len(yModel) - 2.0))

    return(np.sqrt(meanSquareError))


import numpy as np

def cos_similarity(A, B):
    """
    returns cosine similarity between array A and B
    Args:
        A: array with shape (...,d)
        B: array with shape (...,d)

    Returns:
        cosine similarity with shape (...)

    """
    similarity = np.abs(np.einsum('...d,...d', A, B.conj()))
    similarity /= np.sqrt(np.abs(np.einsum('...d,...d', A, A.conj())))
    similarity /= np.sqrt(np.abs(np.einsum('...d,...d', B, B.conj())))
    return similarity

def cos_distance(a, b):
    """
    cosine distance between vector a and b: 1/2*(1-a*b/|a|*|b|)

    :param a: vector a (1xN or Nx1 numpy array)
    :param b: vector b (1xN or Nx1 numpy array)
    :return: distance (scalar)
    """
    return 0.5 * (1 - sum(a * b) / np.sqrt(sum(a ** 2) * sum(b ** 2)))

def normalize_vector_to_unit_length(data):
    """
    Normalized each vector to unit length. This is useful, if all other
    normalization techniques are not reliable.

    :param data: Assumes an input with shape (..., vector)
    :return: The input with the last dimension normalized
    """
    normalization = np.sqrt(np.abs(vector_H_vector(data, data)))
    return data / np.expand_dims(normalization, axis=-1)

def vector_H_vector(x, y):
    """ Inner product of last array dimensions.

    :param x: LHS. Same shape as y
    :param y: RHS. Same shape as x
    :return:
    """
    return np.einsum('...a,...a->...', x.conj(), y)


from sympy import Symbol, singularities, diff

def contains_singularity(f,var,a,b):
    contains = False

    singular_points = list(singularities(f,var))
    for singularity in singular_points:
        if singularity >= a and singularity <= b:
            contains = True

    return contains

def df_sign_constant(f,var,a,b):
    sign_constant = True
    df = diff(f, var)
    df1 = df.subs(var,a)
    df2 = df.subs(var,b)

    if(df1 * df2 < 0):
        sign_constant = False

    return sign_constant

def intvl_contains_root(f,var,a,b):
    contains_root = True
    f1 = f.subs(var, a)
    f2 = f.subs(var, b)

    if(f1 * f2 > 0):
        contains_root = False

    return contains_root

def is_applyable(f,var,a,b):
    applyable = True

    if not intvl_contains_root(f,var,a,b):
        applyable = False

    else:

        if not df_sign_constant(f,var,a,b):
            applyable = False

        else:
            if contains_singularity(f,var,a,b):
                applyable = False

    return applyable


import numpy as np

# The information for the conversions comes from:
# https://vvvv.org/blog/polar-spherical-and-geographic-coordinates

def convert_coordinates(latitude, longitude, altitude):

    # z coordinate in meters.
    # np.array() is there in case the data is input as a Python list.
    z = np.array(altitude)

    # Latitude angle in radians (for sinusoidal funcs)
    rad_lat = np.array(latitude)*np.pi/180

    # Longitude angle in radians.
    rad_long = np.array(longitude)*np.pi/180

    # Radius, from z = rsin(lat)
    r = z/np.sin(rad_lat)

    # x, from x = rcos(lat)cos(long)
    x = r*np.cos(rad_lat)*np.cos(rad_long)

    # y, from y = rcos(lat)sin(long)
    y = r*np.cos(rad_lat)*np.sin(rad_long)

    # Once again, so the user doesn't have to figure out which entry is what arbitrarily.
    # All values in meters.
    cartesian_coordinates = {'x': x, 'y': y, 'z': z}

    return cartesian_coordinates


# Question 3
import numpy as np
import matplotlib.pyplot as plt
import operator

ALPHA = 1e-6
weights = np.zeros(2,dtype=float)

def read_data(filename):
	my_data = np.genfromtxt(filename, delimiter = ',')
	# assign first column to input variable and second to target
	x, Y = my_data[:,0], my_data[:,1]
	# we need to order the array wrt x and keep each x paired with the corresponding Y
	my_sorted_data = sorted(zip(x, Y), key = operator.itemgetter(0))
	sorted_x, sorted_Y = zip(*my_sorted_data)
	return sorted_x, sorted_Y

def predict_Y(x):
	return weights[0] + weights[1] * x

def MSE(true_Y, estimated_Y):
	return sum([(estimated_Y[i] - true_Y[i]) ** 2 for i in range (len(true_Y))])/len(true_Y)

def sgd(x, y, weights, learning_rate):
	weights[0] -= learning_rate*(predict_Y(x) - y)
	weights[1] -= learning_rate*(predict_Y(x) - y)*x

def sgd_optimizer(x,y, learning_rate):
	for i in range(len(x)):
		sgd(x[i], y[i], weights, learning_rate)

def sgd_mse_list(x,y, learning_rate):
	mse_list = []
	x_list = []
	y_list = []
	for i in range(len(np.array(x))):
		x_list.append(x[i])
		y_list.append(y[i])
		sgd(x[i], y[i], weights, learning_rate)
		estimated_Y = [predict_Y(j) for j in x_list]
		mse_list.append(MSE(y_list, estimated_Y))
		mse_array = np.asarray(mse_list)
	return np.mean(mse_array)

def mse_log_graph(x_train,Y_train,x_valid,Y_valid,learning_rate,epochs):
	mse_list_val = []
	mse_list_train = []
	plt.figure(1)
	for i in range(epochs + 1):
		sgd_optimizer(x_train,Y_train, learning_rate)
		mse_list_val.append(np.log10(sgd_mse_list(x_valid,Y_valid, ALPHA)))
	plt.plot(mse_list_val)
	plt.title('Validation Logarithm of the Mean Squared Error vs. number of Epochs')
	plt.xlabel('Epochs')
	plt.ylabel('Log10(MSE)')
	plt.show()
	plt.figure(2)
	for i in range(epochs + 1):
		sgd_optimizer(x_train,Y_train, learning_rate)
		mse_list_train.append(np.log10(sgd_mse_list(x_valid,Y_valid, ALPHA)))
	plt.plot(mse_list_train)
	plt.title('Training Logarithm of the Mean Squared Error vs. number of Epochs')
	plt.xlabel('Epochs')
	plt.ylabel('Log10(MSE)')
	plt.show()

def mse_graph(x_train,Y_train,x_valid,Y_valid,learning_rate,epochs):
	mse_list_val = []
	mse_list_train = []
	plt.figure(1)
	for i in range(epochs + 1):
		sgd_optimizer(x_train,Y_train, learning_rate)
		mse_list_val.append(sgd_mse_list(x_valid,Y_valid, ALPHA))
	plt.plot(mse_list_val)
	plt.title('Validation Mean Squared Error vs. number of Epochs')
	plt.xlabel('Epochs')
	plt.ylabel('MSE')
	plt.show()
	plt.figure(2)
	for i in range(epochs + 1):
		sgd_optimizer(x_train,Y_train, learning_rate)
		mse_list_train.append(sgd_mse_list(x_train,Y_train, ALPHA))
	print("MSE reached after",epochs,"epochs is:",mse_list_val[-1])
	plt.plot(mse_list_train)
	plt.title('Training Mean Squared Error vs. number of Epochs')
	plt.xlabel('Epochs')
	plt.ylabel('MSE')
	plt.show()

def fit_visualization(x_valid, Y_valid, learning_rate, epochs):
	weights = [0.0, 0.0]
	fig, ax = plt.subplots(2, 3, sharex='col', sharey='row')
	fig.suptitle("Validation Fit as the Number of Epochs Increases", fontsize=18)
	pred_list_0 = []
	pred_list_1 = []
	pred_list_2 = []
	pred_list_3 = []
	pred_list_4 = []
	pred_list_5 = []
	#mse_list_val = []

	for i in range(epochs + 1):
		sgd_optimizer(x_train,Y_train, learning_rate)
		#mse_list_val.append(sgd_mse_list(x_valid,Y_valid, ALPHA))
		if i == 0:
			for i in range(len(x_valid)):
				pred_list_0.append(predict_Y(x_valid[i]))
			plt.subplot(2,3,1)
			plt.plot(x_valid,Y_valid, 'ro')
			plt.plot(x_valid, pred_list_0)
			plt.title('Fit after Epoch 1', fontsize = 9)
		if i == 200:
			for i in range(len(x_valid)):
				pred_list_1.append(predict_Y(x_valid[i]))
			plt.subplot(2,3,2)
			plt.plot(x_valid,Y_valid, 'ro')
			plt.plot(x_valid, pred_list_1)
			plt.title('Fit after Epoch 200', fontsize = 9)
		if i == 500:
			for i in range(len(x_valid)):
				pred_list_2.append(predict_Y(x_valid[i]))
			plt.subplot(2,3,3)
			plt.plot(x_valid,Y_valid, 'ro')
			plt.plot(x_valid, pred_list_2)
			plt.title('Fit after Epoch 500', fontsize = 9)
		if i == 1500:
			for i in range(len(x_valid)):
				pred_list_3.append(predict_Y(x_valid[i]))
			plt.subplot(2,3,4)
			plt.plot(x_valid,Y_valid, 'ro')
			plt.plot(x_valid, pred_list_3)
			plt.title('Fit after Epoch 1500', fontsize = 9)
		if i == 4000:
			for i in range(len(x_valid)):
				pred_list_4.append(predict_Y(x_valid[i]))
			plt.subplot(2,3,5)
			plt.plot(x_valid,Y_valid, 'ro')
			plt.plot(x_valid, pred_list_4)
			plt.title('Fit after Epoch 4000', fontsize = 9)
		if i == 9000:
			for i in range(len(x_valid)):
				pred_list_5.append(predict_Y(x_valid[i]))
			plt.subplot(2,3,6)
			plt.plot(x_valid,Y_valid, 'ro')
			plt.plot(x_valid, pred_list_5)
			plt.title('Fit after Epoch 9000', fontsize = 9)
	plt.subplots_adjust(hspace=0.3, wspace = 0.25)
	plt.show()

if __name__ == '__main__':
	x_train, Y_train = read_data("Dataset_2_train.csv")
	x_valid, Y_valid = read_data("Dataset_2_valid.csv")
	#mse_graph(x_train,Y_train,x_valid,Y_valid,ALPHA,5000)
	#mse_log_graph(x_train,Y_train,x_valid,Y_valid,ALPHA,5000)
	# plt.plot(x_train,Y_train, 'bo')
	# plt.plot(x_valid,Y_valid, 'ro')
	#pred_list = []
	# for i in range(len(x_valid)):
	# 	pred_list.append(predict_Y(x_valid[i]))
	# plt.plot(x_valid, pred_list)
	# plt.show()
	# print(weights)
	fit_visualization(x_valid,Y_valid,ALPHA,9000)


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

mu = 10, 20
sigma = 5, 2
dist = pd.DataFrame(np.random.normal(loc = mu, scale = sigma, size = (1000, 2)), columns=["x1", "x2"])
dist
dist.agg(["min", "max", "mean", "std"])

# Densidad y función de probabilidad

fig, ax = plt.subplots()
dist.plot.kde(ax = ax, legend = False, title = "Histograma de dos normales") # kde stands for kernel density estimator
dist.plot.hist(density = True, ax = ax)
ax.set_ylabel("probabilidad")
ax.grid(axis = "y", alpha = 0.75)
ax.set_facecolor("#d5d5d5")

from scipy import stats
dist = stats.norm() # Distribución normal teórica N(0,1) -exp(-x**2/2)/sqrt(2*pi)
sample = dist.rvs(size = 1000)
x = np.linspace(start = stats.norm.ppf(0.1), stop = stats.norm.ppf(0.99), num = 250)
gkde = stats.gaussian_kde(dataset = sample)

fig, ax = plt.subplots()
ax.plot(x, dist.pdf(x), linestyle = "solid", c = "green", lw = 4, alpha = 0.9, label = "Distribución normal teórica") #pdf stands for probability density function
ax.plot(x, gkde.evaluate(x), linestyle = "dotted", c = "red", lw=3, label="PDF estimada con KDE")
ax.legend(loc="best", frameon=False)
ax.set_title("Normal analítica vs estimada")
ax.text(0,0.10, r"$f(x) = \frac{e^{-x^2/2}}{\sqrt{2\pi}}$", fontsize = 20)

Curso estadistica descriptiva/datos cuantitativos/funciones de densidad y de probabilidad/funciones_d_y_p.py


import numpy as np
import warnings

def laplacian_eigenmaps(graph, n_components=2):
    """
    Computes the Lapacian eigenmap.

    Ref: M. Belkin, P. Niyogi, Laplacian eigenmaps and spectral techniques for
         embedding and clustering, in: NIPS, Vol. 14, 2001, pp. 585-591.
    """
    if graph.n_nodes > 10000:
        warnings.warn('The default implementation computes the full eigendecomposition, which is not efficient for large graphs.')
    if graph.directed:
        raise ValueError('Graph should be undirected.')
    if n_components < 0 or n_components > graph.n_nodes:
        raise ValueError('Number of components must be positive and less than the number of nodes.')
    L = graph.get_laplacian(normalize=True)
    l, v = np.linalg.eig(L)
    ind = np.argsort(np.abs(l))
    return v[:, ind[1:1+n_components]]


'''
Blend it all together
In the last few exercises you've learned everything there is to know about heights
and weights of baseball players. Now it's time to dive into another sport: soccer.

You've contacted FIFA for some data and they handed you two lists. The lists are the
following: positions = ['GK', 'M', 'A', 'D', ...] heights = [191, 184, 185, 180, ...]
Each element in the lists corresponds to a player. The first list, positions, contains
strings representing each player's position. The possible positions are: 'GK' (goalkeeper),
'M' (midfield), 'A' (attack) and 'D' (defense). The second list, heights, contains integers
representing the height of the player in cm. The first player in the lists is a goalkeeper
and is pretty tall (191 cm).

You're fairly confident that the median height of goalkeepers is higher than that of other
players on the soccer field. Some of your friends don't believe you, so you are determined
to show them using the data you received from FIFA and your newly acquired Python skills.

Instructions
-Convert heights and positions, which are regular lists, to numpy arrays. Call them np_heights
and np_positions.
-Extract all the heights of the goalkeepers. You can use a little trick here: use np_positions
== 'GK' as an index for np_heights. Assign the result to gk_heights.
-Extract all the heights of all the other players. This time use np_positions != 'GK' as an
index for np_heights. Assign the result to other_heights.
-Print out the median height of the goalkeepers using np.median(). Replace None with the correct code.
-Do the same for the other players. Print out their median height. Replace None with the correct code.
'''
# heights and positions are available as lists

# Import numpy
import numpy as np

# Convert positions and heights to numpy arrays: np_positions, np_heights
np_positions = np.array(positions)
np_heights = np.array(heights)

# Heights of the goalkeepers: gk_heights
gk_heights = np_heights[np_positions == 'GK']

# Heights of the other players: other_heights
other_heights = np_heights[np_positions != 'GK']

# Print out the median height of goalkeepers. Replace 'None'
print("Median height of goalkeepers: " + str(np.median(gk_heights)))

# Print out the median height of other players. Replace 'None'
print("Median height of other players: " + str(np.median(other_heights)))

Intro to Python for Data Science/NumPy/blend-it-all-together.py


"""

Some functions for working with the Bansal -- Yaron constant volatility model.
Consumption growth and dividend growth are given by

    ln (C_{t+1} / C_t) = μ_c + X_{t+1} + σ_c ε_{t+1}

    ln (D_{t+1} / D_t) = μ_d + X_{t+1} + σ_d ζ_{t+1}

    X_{t+1} = ρ X_t + σ η_{t+1}

with all innovations iid and N(0, 1).

Preferences are CRRA.  Also,

    Φ_{t+1} = β exp( ln(D_{t+1} / D_t) - γ ln(C_{t+1} / C_t) )

"""

import numpy as np
from numpy import sqrt, exp
from numpy.random import randn
from numba import njit, prange
from scipy.linalg import eigvals
import quantecon as qe

class BYCV:
    """
    Represents the Bansal--Yaron constant volatility model.  Parameters are
    from Bansal and Yaron, 2004, table II.

    """

    def __init__(self, β=0.998,
                       γ=2.5,
                       ρ=0.979,
                       σ=0.00034,
                       μ_c=0.0015,
                       σ_c=0.0078,
                       σ_d=0.035,    # 4.5 * 0.0078
                       μ_d=0.0015):

        self.ρ, self.σ = ρ, σ
        self.σ_c, self.μ_c = σ_c, μ_c
        self.σ_d, self.μ_d = σ_d, μ_d
        self.γ, self.β = γ, β

def stability_exponent_mc_factory(by, m=1000, n=1000, seed=1234, parallel_flag=True):
    """

    Compute the stability coefficient by Monte Carlo.

    * by is an instance of BYCV

    """

    ρ, σ = by.ρ, by.σ
    σ_c, μ_c = by.σ_c, by.μ_c
    σ_d, μ_d = by.σ_d, by.μ_d
    β, γ = by.β, by.γ

    @njit(parallel=parallel_flag)
    def stability_exponent_mc(m=1000, n=1000):

        np.random.seed(seed)

        Phi_prod_sum = 0.0

        for j in prange(m):
            Phi_prod = 1.0
            X = 0.0

            for i in range(n):
                d_growth = μ_d + X + σ_d * randn()
                c_growth = μ_c + X + σ_c * randn()
                Phi_prod = Phi_prod * β * exp(d_growth - γ * c_growth)
                X = ρ * X + σ * randn()

            Phi_prod_sum += Phi_prod

        Phi_prod_mean = Phi_prod_sum / m

        return np.log(Phi_prod_mean) / n

    return stability_exponent_mc

def stability_exp_analytic(by):
    """
    Compute stability exponent exactly.

    * by is an instance of BYCV

    """
    # Unpack parameters
    ρ, σ = by.ρ, by.σ
    σ_c, μ_c = by.σ_c, by.μ_c
    σ_d, μ_d = by.σ_d, by.μ_d
    β, γ = by.β, by.γ

    a = np.log(β) + μ_d - γ * μ_c
    b = σ_d**2 + (γ * σ_c)**2
    c =  σ**2 * (1 - γ)**2 /  (1 - ρ)**2

    return  a + (b + c) / 2

def stability_exp_discretized(by, D=10):
    """
    Compute stability exponent by numerical linear algebra.

    * by is an instance of BYCV

    """

    # Unpack parameters
    ρ, σ = by.ρ, by.σ
    σ_c, μ_c = by.σ_c, by.μ_c
    σ_d, μ_d = by.σ_d, by.μ_d
    β, γ = by.β, by.γ

    # Discretize the state process
    X_mc = qe.rouwenhorst(D, 0.0, σ, ρ)
    x_vals = X_mc.state_values
    P = X_mc.P

    # Build the matrix
    #
    #   V(x, y) = β * exp(μ_d - γ*μ_c  + (1-γ)*x + (σ_d**2 + (γ*σ_c)**2) / 2) Π(x, y)
    #
    V = np.empty((D, D))
    for i, x in enumerate(x_vals):
        for j, y in enumerate(x_vals):
            a = β * exp(μ_d - γ*μ_c  + (1-γ)*x + (σ_d**2 + (γ*σ_c)**2) / 2)
            V[i, j] = a * P[i, j]

    # Return the log of r(V)
    return np.log(np.max(np.abs(eigvals(V))))

import numpy,from numpy,from scipy,from numba


import numpy as np
from random import sample
import matplotlib.pyplot as plt

class KMeans:
    '''
    KMeans Algorithm

    ----------
    Attributes
    ----------
    - k: Integer Number of Clusters. Default value is 2.
    - norm: Function. Vector Space Norm. Default
        value is euclidean norm (numpy.linalg.norm).
    '''

    def __init__(self, k = 2, norm = np.linalg.norm):
        self.k = k
        self.norm = norm

    def cluster(self, data, maxIter = 15, tolerance = 0.01, silent = True):
        '''
        Executes KMeans Algorithm.
        ----------
        Paramaters
        ----------
        - data: Data Matrix that will be clusterized.
        - maxIter (optional): Integer. Maximum iterations that the
            algorithm will be executed. Default value is 15.
        - tolerance (optional): Float. Default value = 0.01.
        - silent(optional): Boolean. Show log if False. Default value True.

        ------
        Return
        ------
        Tupla clusters, centroids, y_pred. Clusters is a list (size k) and each
        list member is a data Matrix of points. Centroids is a list (size k)
        with last centroids calculated. y_pred is a list of size len(data) with
        correspondient class to each point.
        '''

        data = np.array(data)
        centroids = []
        n = len(data) #Size of date
        m = len(data[0]) #Dimensions
        # Inicialice centroids randomly
        randomIndex = sample(range(0, n), self.k)
        for i in range(0, self.k):
            centroids.append(data[randomIndex[i]])

        isOptimal = False;
        iterations = 0;

        while (not isOptimal and iterations < maxIter):

            # Inicialize k  empty clusters
            clusters = [[] for i in range(0, self.k)]
            y_pred = []

            iterations += 1

            # Classify each individual in the cluster which minimices distance to its centroid.
            for x in data:
                distances = [self.norm(x - i) for i in centroids]
                minCluster = distances.index(min(distances))
                clusters[minCluster].append(x)
                y_pred.append(minCluster + 1)
            clusters = [np.array(i) for i in clusters]

            auxCentroids = list(centroids)

            #New centroids are the mean of each cluster
            for i in range (0, self.k):
                centroids[i] = [np.average([clusters[i][:,j]]) for j in range(0, m)]

            if max([self.norm(np.array(centroids[i]) - np.array(auxCentroids[i]))
                    /self.norm(auxCentroids[i]) for i in range (0, self.k)]) <= tolerance:
                isOptimal = True

                    #If at least a centroid change substantially, then we keep iterating
        if (not silent):
            print('number of iterations:', iterations)
        return clusters, centroids, y_pred

def clusterPlot(clusters, x, y, centroids):
    '''
    Draw all members of a cluster list and centroids

    ----------
    Paramaters
    ----------

    - clusters: Clusters list and each cluster is a data Matrix of points
    - x: coordinate x in graphic.
    - y: coordinate y in graphic.
    - centroids: List of points.
    '''
    for i in range(0, len(centroids)):
        cluster = clusters[i]
        p = plt.plot(cluster[:,x], cluster[:,y], 'o')
        centroid= centroids[i]
        plt.plot(centroid[x], centroid[y], 'x', markersize = 40, color = p[-1].get_color())
    plt.show()

def create_2d_data(K, sigma_class=10, sigma=0.5, min_num=10, max_num=20):
    '''Creates some random 2D data for testing.
    Return points X belonging to K classes given
    by tags.

    Args:
        K: number of classes
        sigma_class: measures how sparse are the classes
        sigma: measures how clustered around its mean are
               the points of each class
        min_num, max_num: minimum and maximum number of points of each class

    Returns:
        X: (N, 2) array of points
        tags: list of tags [k0, k1, ..., kN]
    '''

    tags = []
    N_class_list = []
    mu_list = []

    mu_list = [np.random.randn(2)*sigma_class]

    for k in range(1, K):
        try_mu = np.random.randn(2)*sigma_class
        while min([np.linalg.norm(mu - try_mu) for mu in mu_list]) < 2*sigma_class:
            try_mu = np.random.randn(2)*sigma_class
        mu_list.append(try_mu)

    for k in range(K):
        N_class = np.random.randint(min_num, max_num)
        tags += [k] * N_class
        N_class_list += [N_class]

    N = sum(N_class_list)
    X = np.zeros((2, N))
    count = 0
    for k in range(K):
        X[:, count:count + N_class_list[k]] = \
            mu_list[k][:, np.newaxis] + np.random.randn(2, N_class_list[k])*sigma
        count += N_class_list[k]

    return X.T, tags

if __name__ == '__main__':

    K = 10
    X, _ = create_2d_data(K, sigma = 5)

    model = KMeans(K)
    clusters, centroids, _ = model.cluster(X, tolerance = 0, silent = False)
    clusterPlot(clusters,0,1, centroids)


"""
K-means Clustering
============================

CR-Sparse includes a K-means implementation as
part of its sparse subspace clustering module.

"""

# %%
# Configure JAX to work with 64-bit floating point precision.
from jax.config import config
config.update("jax_enable_x64", True)

# %%
# Let's import necessary libraries
from jax import random
import jax.numpy as jnp
import cr.sparse as crs
# vector quantization
import cr.sparse.cluster.vq as vq
# Plotting
import matplotlib.pyplot as plt
# Some PRNGKeys for later use
key = random.PRNGKey(0)
keys = random.split(key, 10)

# %%
# Prepare sample data

# Number of points for each cluster
pts = 50
# Mean vector for first cluster
mu_a = jnp.array([0, 0])
# Covariance matrix for first cluster
cov_a = jnp.array([[4, 1], [1, 4]])
# Sampled points for the first cluster
a = random.multivariate_normal(keys[0], mu_a, cov_a, shape=(pts,))
# Mean vector for second cluster
mu_b = jnp.array([30, 10])
# Covariance matrix for second cluster
cov_b = jnp.array([[10, 2], [2, 1]])
# Sampled points for the second cluster
b = random.multivariate_normal(keys[1], mu_b, cov_b, shape=(pts,))
# combined points
features = jnp.concatenate((a, b))
# plot the points
plt.scatter(features[:, 0], features[:, 1])

# %%
# K-means clustering
# ------------------------------

# number of clusters
k=2
# Perform K-means clustering
result = vq.kmeans_jit(keys[3], features, k)
centroids = result.centroids
assignment = result.assignment
for i in range(k):
    # points for the k-th cluster
    cluster = features[assignment == i]
    plt.plot(cluster[:,0], cluster[:,1], "o", alpha=0.4)
# plot the centroids
plt.scatter(centroids[:, 0], centroids[:, 1], c='r')

# %%
# Let's print the assignment to verify that points have
# indeed been assigned to respective clusters
print(assignment)
# %%
# Let's print the number of points for each cluster
print(vq.assignment_counts(assignment, k))
# %%
# Let's print the number of iterations taken to converge
print(result.iterations)

# %%
# If we have points and centroids, we can compute the assignments
assignment, distances = vq.find_assignment_jit(features, centroids)
print(assignment)

# %%
# For any new point, it is easy to find the index of nearest centroid
pt = jnp.array([1, 4])
idx = vq.find_nearest_jit(pt, centroids)
print(centroids[idx])


import glob
import matplotlib.pyplot as plt
import numpy
import os
import pandas
import scipy
import scipy.integrate
import sympy

beta0 = 0.175
gamma0 = 0.07

def dRIdt(x, t=0):
    return numpy.array([ gamma0 * x[1],
                        beta0 * numpy.exp( (-beta0/gamma0) * x[0] ) - (gamma0 * x[1]) ])

def dSIdt(x, t=0):
    return numpy.array([ -beta0 * x[0] * x[1],
                        beta0 * x[0] * x[1] - gamma0 * x[1] ])

def dSRdt(x, t=0):
    # return numpy.array([])
    pass

def phaseRI():
    # see https://scipy-cookbook.readthedocs.io/items/LoktaVolterraTutorial.html
    r = numpy.linspace(0, 1, 50)
    i = numpy.linspace(0, 1, 50)
    R, I = numpy.meshgrid(r,i)
    dR, dI = dRIdt([R,I])
    M = (numpy.hypot(dR, dI))
    M [ M == 0 ] = 1.
    dR /= M
    dI /= M
    # plt.quiver(R, I, dR, dI, M, pivot='mid')
    plt.streamplot(R, I, dR, dI)
    plt.xlabel("Recovered")
    plt.ylabel("Infected")
    plt.show()

def phaseSI():
    s = numpy.linspace(0, 1, 50)
    i = numpy.linspace(0, 1, 50)
    S, I = numpy.meshgrid(s,i)
    dS, dI = dSIdt([S,I])
    M = (numpy.hypot(dS, dI))
    M [ M == 0 ] = 1.
    dS /= M
    dI /= M
    # plt.quiver(R, I, dR, dI, M, pivot='mid')
    plt.streamplot(S, I, dS, dI)
    plt.xlabel("Susceptible")
    plt.ylabel("Infected")
    plt.show()

if __name__ == "__main__":
    phaseRI()
    # phaseSI()


"""
Simultaneous iteration algorithm
Takes a matrix Q and a parameter p denoting the last column
of A considered for computing the eigenspace

>>> A = array([[ 1.079,  1.105,  0.559],
...            [ 1.105,  0.725,  0.988],
...            [ 0.559,  0.988,  0.732]])
>>> Q,L,iters,err = simit(A,3, details=True)
>>> iters < 200
True
>>> err < 1.0e-15
True
"""

from numpy import *

def simit(A, p, details=False):
    n,_ = shape(A)
    t = random.rand(n,p)
    Q,R = linalg.qr(t)
    err = 1.0
    iters = 0
    while err > 1.0e-15 and iters < 1000:
        z = A.dot(Q)
        Q,R = linalg.qr(z)
        err = linalg.norm(A.dot(Q) - z)
        iters += 1
    if details:
        return Q, Q.T.dot(A).dot(Q), iters, err
    else:
        return Q, Q.T.dot(A).dot(Q)

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


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

from sklearn.neighbors import KernelDensity

import scipy.integrate as integrate
from sklearn.kernel_ridge import KernelRidge

matplotlib.rcParams['mathtext.fontset'] = 'stix'
matplotlib.rcParams['font.family'] = 'STIXGeneral'
matplotlib.rcParams.update({'font.size': 18})

mu1, sigma1 = 3.0, 1.0
mu2, sigma2 = 8.0, 1.5

def sample_points():
    s1 = np.random.normal(mu1, sigma1, 50)

    s2 = np.random.normal(mu2, sigma2, 50)

    return list(s1) + list(s2)

# generate points used to plot
x_plot = np.linspace(0, 12, 100)

# generate points and keep a subset of them
x = sample_points()

colors = ['red', 'blue', 'orange', 'green']
lw = 2

def kernel(x1, x2, bi = 2.0):
    z = (x1 - x2) / bi
    return (1.0/math.sqrt(2.0 * 3.14)) * math.exp((-1.0/2.0)*(z**2))

def fb(xx, data, bi):
    return (1/(len(data)*bi)) * sum([kernel(xx, xi, bi) for xi in data])

def fbi(i, data, bi):
    data_minus_i = []
    for ii in range(len(data)):
        if i != ii:
            data_minus_i.append(data[ii])
    return (1/(len(data_minus_i)*bi)) * sum([kernel(data[i], xi, bi) for xi in data_minus_i])

def sum_pdf(x):
    result = []
    for i in range(len(x)):
        result.append((sp.stats.norm.pdf(x, mu1, sigma1)[i] + sp.stats.norm.pdf(x, mu2, sigma2)[i])/2.0)
        #result.append(sp.stats.norm.pdf(x, mu1, sigma1)[i])
    return result

b = np.linspace(0.01, 3.0, 100)

score = []
for bi in b:
    def fb2(xx):
        return fb(xx, x, bi)**2

    s = integrate.quad(fb2, -np.inf, np.inf)[0] - 2.0*np.mean([fbi(i, x, bi) for i in range(len(x))])
    score.append(s)

plt.figure(1)
plt.plot(b,score)
plt.xlabel("$b$")
plt.ylabel("$l$")
plt.tight_layout()
plt.xticks(np.arange(0, 3.5, 0.5))
#plt.show()
fig1 = plt.gcf()
fig1.subplots_adjust(top = 0.98, bottom = 0.1, right = 0.98, left = 0.08, hspace = 0, wspace = 0)
fig1.savefig('../../Illustrations/density-estimation-loss.eps', format='eps', dpi=1000, bbox_inches = 'tight', pad_inches = 0)
fig1.savefig('../../Illustrations/density-estimation-loss.pdf', format='pdf', dpi=1000, bbox_inches = 'tight', pad_inches = 0)
fig1.savefig('../../Illustrations/density-estimation-loss.png', dpi=1000, bbox_inches = 'tight', pad_inches = 0)
minb = [bi for bi, s in zip(b, score) if s == min(score)][0]
print minb

for count, degree in enumerate([round(minb, 2)] + [0.2, 2.0]):
    plt.figure(count+2)
    axes = plt.gca()
    axes.set_xlim([0,12])
    axes.set_ylim([0,0.3])
    plt.xlabel("$x$")
    plt.ylabel("pdf")
    plt.scatter(x, [0.005] * len(x), color='navy', s=30, marker=2, label="training examples")
    plt.plot(x_plot, [fb(xp ,x, degree) for xp in x_plot], color=colors[count], linewidth=lw, label="$\\hat{f}_b$, $b = " + str(degree) + "$")
    plt.plot(x_plot,sum_pdf(x_plot), label="true pdf")

    plt.legend(loc='upper right')
    plt.tight_layout()

    fig1 = plt.gcf()
    fig1.subplots_adjust(top = 0.98, bottom = 0.1, right = 0.98, left = 0.08, hspace = 0, wspace = 0)
    fig1.savefig('../../Illustrations/density-estimation-' + str(count) + '.eps', format='eps', dpi=1000, bbox_inches = 'tight', pad_inches = 0)
    fig1.savefig('../../Illustrations/density-estimation-' + str(count) + '.pdf', format='pdf', dpi=1000, bbox_inches = 'tight', pad_inches = 0)
    fig1.savefig('../../Illustrations/density-estimation-' + str(count) + '.png', dpi=1000, bbox_inches = 'tight', pad_inches = 0)
    plt.show()


# An implementation of the billiard walk algorithm given in

# GRYAZINA, Elena; POLYAK, Boris. Random sampling: Billiard walk algorithm.
# European Journal of Operational Research, 2014, 238.2: 497-504.

# as applied to an n-dimensional simplex.

# Simplex: Ax <= b. All x >= 0 and sum of all x <= 1.
# A = [ -I          ]  b = [0]
#     [-------------]      [-]
#     [ 1 1 1 ... 1 ]      [1]

import numpy as np

# The import below is only needed for the test instance. Comment it out if
# you're going to use the functions below as a library

import matplotlib.pyplot as plt

def get_simplex(dimension):
	sum_constraint = np.array([1] * dimension)
	negident = -np.eye(dimension)

	A = np.concatenate((negident, np.array([sum_constraint])), axis=0)
	b = np.concatenate(([0]*dimension, [1]))

	return A, b

def is_inside(A, b, x):
		return np.all(np.dot(A, x) <= b)

# Note, can be larger than the dimensions!

def random_unit_vector(dimension):
	# Create a ray pointing in a random direction with unit magnitude.
	# Having unit magnitude makes k the distance to the closest edge.
	x_v = np.random.multivariate_normal([0]*dimension, np.eye(dimension))
	x_v /= np.linalg.norm(x_v)

	return x_v

def get_closest_half_plane_dist(A, b, x_p, x_v):

	# Find closest half-plane

	k_record = np.inf
	halfplane = -1

	# Note, can be larger than the number of dimensions!
	num_halfplanes = A.shape[0]

	# Hack to avoid numerical precision issues. In essence, this
	# factor gives each edge a thickness, where we'll never go from
	# inside the thickness of the edge to some other point inside
	# that band.
	k_epsilon = 1e-9

	for i in xrange(num_halfplanes):
		travel_magnitude = np.dot(x_v, A[i])

		# If x_v is parallel to the edge, skip.
		if travel_magnitude == 0:
			continue

		k = (b[i] - np.dot(x_p, A[i])) / travel_magnitude

		if k < k_record and k > k_epsilon:
			k_record = k
			halfplane = i

		# If this is true, we're at an edge. x_v may be pointing away from
		# the polytope or not. If x_v is pointing out of the polytope, then
		# the derivative of (x_p + k * x_v) * a[i] - b[i] is positive. In that
		# case, we can't travel any amount along that direction, so we need to
		# reflect.
		# Otherwise, we're still good to go.

		# Travel_magnitude is exactly this derivative, which gives us...

		if k >= 0 and k <= k_epsilon and travel_magnitude > 0:
			return (0, i, False)

	# TODO: Somehow handle if k_record is infinity and halfplane is -1.
	# This pretty much means the space is unbounded and x_v is pointing
	# in the direction that the polytope is unbounded.

	return (k_record, halfplane, True)

def billiard_walk_int(A, b, x_p, x_v, max_distance, max_reflections):

	#print "X_p: ", x_p
	#print "X_v: ", x_v
	#print "max distance: ", max_distance

	distance_remaining = max_distance

	for i in xrange(max_reflections):
		distance, halfplane, dest_inside = get_closest_half_plane_dist(
			A, b, x_p, x_v)

		# If dest_inside is true, then x_p + distance * x_v keeps us
		# inside the polytope. That means we can travel towards the
		# closest edge along the direction given by x_v. We will then
		# either exhaust our max distance, or end up at an edge with x_v
		# pointing out of the polytope.

		# On the other hand, if it's false, then we're at an edge with x_v
		# pointing outwards, so we can't travel at all before we need to
		# reflect.

		if dest_inside:
			if distance >= distance_remaining:
				#print "Exit in", i
				return x_p + x_v * distance_remaining

			distance_remaining -= distance
			x_p = x_p + x_v * distance

		# We're at an edge and need to reflect
		# TODO if we want to optimize: cache np.linalg.norm(A[halfplane])
		# or normalize each row of A and scale b appropriately.
		int_normal = -A[halfplane] / np.linalg.norm(A[halfplane])
		x_v -= 2 * np.dot(x_v, int_normal) * int_normal

	# Passed max_reflections without covering the required distance.
	return None

def billiard_walk(A, b, x_0, tau_distance, max_reflections,
	max_retries=100):

	dimension = A.shape[1]

	candidate = None

	for i in xrange(max_retries):
		x_p = np.copy(x_0)
		x_v = random_unit_vector(dimension)

		max_distance = -tau_distance * np.log(np.random.uniform())

		candidate = billiard_walk_int(A, b, x_p, x_v, max_distance, max_reflections)

		if candidate is not None:
			return candidate

if __name__ == "__main__":

	g_dimension = 2

	# The diameter of the polytope is equal to the max distance between points
	# in it.
	max_diameter = np.sqrt(2)
	g_x_0 = np.array([0]*g_dimension)

	g_A, g_b = get_simplex(g_dimension)

	x = []
	y = []

	for i in xrange(1000):
		print i
		if not is_inside(g_A, g_b, g_x_0):
			print "!",
			continue
		g_x_0 = billiard_walk(g_A, g_b, g_x_0, max_diameter, 10 * g_dimension)
		#print g_x_0
		x.append(g_x_0[0])
		y.append(g_x_0[1])
		#print ""

	plt.scatter(x, y, s=[10])
	plt.show()


from itertools import chain
import numpy as np

def jaccard_timespan_event_score(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Jaccard-Timespan-Event-Score (JTES) is a timespan score based on the Jaccard index
    also known as Intersection over Union (IoU).

    This index is designed to score events that are defined by a timespan defined by two timestamps (t0, t1).
    Score Goals:
      * Score is in range [0-1], where 0 is lowest and 1 best.
      * False-Positives and False-Negatives are equally bad
      * If a true event spans over multiple predicted events, the result is averaged
      * If the predicted event spans over multiple true events, the result is accounted accordingly
      * If len(y_true) == 0 and len(y_pred) > 0, score = 0
      * If len(y_true) > 0 and len(y_pred) == 0, score = 0
      * If ordered(y_true) == ordered(y_pred), score = 1
      * Overlap in y_true is not allowed

    Jaccard index in general is defined as |A ∩ B| / |A ∪ B|
    For each element in y_pred a score for each y_true is calculated.

    sum(avg(non_zero_score_per_y_true))/(len(y_true) + len(False-Positive))

    returns sum-of-max-score / max(len(y_pred), len(y_true))
    Always divide by longer list to assure punishment of false-negatives

    :param y_true: np.ndarray([[t0, t1], [t0,t1], ...])
    :param y_pred: np.ndarray([[t0, t1], [t0,t1], ...])
    :return: jaccard_timespan_event_score as float
    """

    # Test t0 < t1
    for t0, t1 in chain(y_pred, y_true):
        if t0 > t1:
            raise ValueError(f"t0 must be before t1: {t0} > {t1}")

    if len(y_pred) == 0 and len(y_true) == 0:
        # 1 score for empty predictions
        return 1.0

    if len(y_pred) == 0 or len(y_true) == 0:
        # 0 score miss-matching empty predictions
        return 0.0

    # Test non overlapping events in y_true
    for i in range(1, len(y_true)):
        t00, t01 = y_true[i-1]
        t10, t11 = y_true[i]
        if not (t01 < t10 or t00 > t11):
            raise ValueError(f"Overlapping events in y_true are not allowed: {t00}-{t01} and {t10}-{t11}")

    # IoU
    scores = np.zeros((len(y_pred), len(y_true)))
    for ip, (tp0, tp1) in enumerate(y_pred):
        for it, (tt0, tt1) in enumerate(y_true):
            if tp1 <= tt0 or tp0 >= tt1:  # no intersection
                scores[ip][it] = 0
            else:  # intersection
                union = (max(tp0, tp1, tt0, tt1) - min(tp0, tp1, tt0, tt1)).astype('float64')
                intersection = (min(tp1, tt1) - max(tp0, tt0)).astype('float64')
                scores[ip][it] = intersection / union

    # Average prediction score per true event
    avg_col = np.zeros(scores.shape[1])
    for i, col in enumerate(scores.T):
        non_zero = col[col != 0]
        if non_zero.shape[0] > 0:
            avg_col[i] = np.average(col[col != 0])

    # count false positives to punish FP
    fp_count = len(np.where(~scores.any(axis=1))[0])

    return sum(avg_col) / (scores.shape[1] + fp_count)