from numpy import *

##################################################################
# Recursive generation of the Legendre polynomial of order n
def Legendre(n,x):
	x=array(x)
	if (n==0):
		return x*0+1.0
	elif (n==1):
		return x
	else:
		return ((2.0*n-1.0)*x*Legendre(n-1,x)-(n-1)*Legendre(n-2,x))/n

##################################################################
# Derivative of the Legendre polynomials
def DLegendre(n,x):
	x=array(x)
	if (n==0):
		return x*0
	elif (n==1):
		return x*0+1.0
	else:
		return (n/(x**2-1.0))*(x*Legendre(n,x)-Legendre(n-1,x))
##################################################################
# Roots of the polynomial obtained using Newton-Raphson method
def LegendreRoots(polyorder,tolerance=1e-20):
	if polyorder<2:
		err=1 # bad polyorder no roots can be found
	else:
		roots=[]
		# The polynomials are alternately even and odd functions. So we evaluate only half the number of roots.
		for i in range(1,int(polyorder)/2 +1):
			x=cos(pi*(i-0.25)/(polyorder+0.5))
			error=10*tolerance
		        iters=0
		        while (error>tolerance) and (iters<1000):
		                dx=-Legendre(polyorder,x)/DLegendre(polyorder,x)
		                x=x+dx
		                iters=iters+1
		                error=abs(dx)
			roots.append(x)
		# Use symmetry to get the other roots
		roots=array(roots)
		if polyorder%2==0:
			roots=concatenate( (-1.0*roots, roots[::-1]) )
		else:
			roots=concatenate( (-1.0*roots, [0.0], roots[::-1]) )
		err=0 # successfully determined roots
	return [roots, err]
##################################################################
# Weight coefficients
def GaussLegendreWeights(polyorder):
	W=[]
	[xis,err]=LegendreRoots(polyorder)
	if err==0:
		W=2.0/( (1.0-xis**2)*(DLegendre(polyorder,xis)**2) )
		err=0
	else:
		err=1 # could not determine roots - so no weights
	return [W, xis, err]
##################################################################
# The integral value
# func 		: the integrand
# a, b 		: lower and upper limits of the integral
# polyorder 	: order of the Legendre polynomial to be used
#
def GaussLegendreQuadrature(func, polyorder, a, b):
	[Ws,xs, err]= GaussLegendreWeights(polyorder)
	if err==0:
		ans=(b-a)*0.5*sum( Ws*func( (b-a)*0.5*xs+ (b+a)*0.5 ) )
	else:
		# (in case of error)
		err=1
		ans=None
	return [ans,err]
##################################################################
# The integrand - change as required
def func(x):
	return exp(x)
##################################################################
#

order=5
[Ws,xs,err]=GaussLegendreWeights(order)
if err==0:
	print("Order    : ", order)
	print("Roots    : ", xs)
	print("Weights  : ", Ws)
else:
	print("Roots/Weights evaluation failed")

# Integrating the function
[ans,err]=GaussLegendreQuadrature(func , order, -3,3)
if err==0:
	print("Integral : ", ans)
else:
	print("Integral evaluation failed")


# -*- coding: utf-8 -*-
"""
Created on Thu Sep 12 15:27:18 2019

@author: s.paramonov
"""

import numpy as np
from scipy import integrate

def z_est(lambdas):

    npow = np.arange(lambdas.shape[0])+1.

    def I(x):
        return (np.exp( -np.sum(  (np.power(x,npow)) * lambdas  )) )

    return integrate.quad(I, a=0, b=1)[0]

# means estimations from approximated pdf:
def means_est(lambdas, k):

    npow = np.arange(lambdas.shape[0])+1.

    def numerat(x):
        return ( np.power(x,k)) *  (np.exp( -np.sum(  (np.power(x,npow)) * lambdas  )))
    def denom (x):
        return np.exp( -np.sum(  (np.power(x,npow)) * lambdas  ))

    return (integrate.quad(numerat, a=0, b=1))[0] / (integrate.quad(denom, a=0, b=1))[0]

def Means(lambdas):

    M_est = np.zeros((lambdas.shape[0]))
    for i in range(M_est.shape[0]):

        M_est[i] = means_est(lambdas, (i+1))

    return M_est

# Hessian matrix for 2-nd order mimnimization
def Hessian(lambdas):

    H = np.zeros(( (lambdas.shape[0] ), (lambdas.shape[0]) ))

    for n in range (H.shape[0]):
        for m in range (H.shape[0]):

            H[n,m] = means_est(lambdas, (n+1+m+1)) - means_est(lambdas, m+1)*means_est(lambdas, n+1)

    return H

# opimize in cycle:
def optimization_4_lambdas(moments):

    lambdas = np.zeros((moments.shape[0] - 1)) # ecluding moments[0]==1

    for q in range(10):
        lambdas_ = lambdas

        H = Hessian(lambdas_)

        B = moments[1:] - Means(lambdas_)

        A = np.linalg.solve(H, B)

        lambdas = lambdas_ - A

    Z = z_est(lambdas)
    lambda0 = np.log(Z)

    return lambda0, lambdas

# building approximation function for pdf

def pdf_approx(moments):

    Lambda0, Lambdas =    optimization_4_lambdas(moments)

    def pdf_calc(x):

        npow = np.expand_dims((np.arange(Lambdas.shape[0])+1.),axis=1 )

        S = -np.sum(  (np.power( x,npow)).T * Lambdas, axis=1, keepdims=True  )

        return np.exp(S - Lambda0)

    return pdf_calc


import numpy as np

def check_vector_span(set_of_vectors, vector_to_check):
    # creates an empty vector of correct size
    vector_of_scalars = np.asarray([None]*set_of_vectors.shape[0])

    # Solves for the scalars that make the equation true if vector is within
    # the span
    try:
        # TODO: Use np.linalg.solve() function here to solve for
        # vector_of_scalars
        vector_of_scalars = np.linalg.solve(set_of_vectors, vector_to_check)
        if not (vector_of_scalars is None):
            print("\nVector is within span.\nScalars in s:", vector_of_scalars)
    # Handles the cases when the vector is NOT within the span
    except Exception as exception_type:
        if str(exception_type) == "Singular matrix":
            print("\nNo single solution\nVector is NOT within span")
        else:
            print("\nUnexpected Exception Error:", exception_type)
    return vector_of_scalars

t = np.array([4, 11])
vw = np.array([[1, 2], [3, 5]])

print("\nMatrix vw:", vw, "\nVector t:", t, sep="\n")
s = check_vector_span(vw, t)

vw2 = np.array([[1, 2], [2, 4]])
t2 = np.array([6, 12])
print("\nNew Vectors:\n Matrix vw2:", vw2, "\nVector t2:", t2, sep="\n")
# Call to check_vector_span
s2 = check_vector_span(vw2, t2)

vw3 = np.array([[1, 2], [1, 2]])
t3 = np.array([6, 10])
print("\nNew Vectors:\n Matrix vw3:", vw3, "\nVector t3:", t3, sep="\n")
# Call to check_vector_span
s3 = check_vector_span(vw3, t3)


"""Utilities for Math operations."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np

def outlier_bounds_std(data: list, m: int = 2):
    data = np.array(data)
    max_boundary = np.mean(data) + (m * np.std(data))
    min_boundary = np.mean(data) - (m * np.std(data))
    return min_boundary, max_boundary

def outlier_bounds_double_mad(data: list, thresh: float = 3.5):
    data = np.array(data)
    median = np.median(data)
    abs_dev = np.abs(data - median)
    left_mad = np.median(abs_dev[data <= median])
    right_mad = np.median(abs_dev[data >= median])
    min_boundary = thresh / 0.6745 * left_mad
    max_boundary = thresh / 0.6745 * right_mad
    return min_boundary, max_boundary

def outlier_bounds_mad(data: list, thresh: float = 3.5):
    data = np.array(data)
    if len(data.shape) == 1:
        data = data[:, None]
    median = np.median(data, axis=0)
    diff = np.sum((data - median) ** 2, axis=-1)
    diff = np.sqrt(diff)
    med_abs_deviation = np.median(diff)
    max_boundary = thresh / 0.6745 * med_abs_deviation
    min_boundary = 0
    return min_boundary, max_boundary

def outlier_bounds_percentile(data: list, thresh: int = 99):
    data = np.array(data)
    diff = (100 - thresh) / 2.0
    min_boundary, max_boundary = np.percentile(data, [diff, 100 - diff])
    return min_boundary, max_boundary

def outlier_bounds_iqr(data: list):
    data = np.array(data)
    p25 = np.percentile(data, 25)
    p75 = np.percentile(data, 75)
    min_boundary = p25 - 1.5 * (p75 - p25)
    max_boundary = p75 + 1.5 * (p75 - p25)
    return min_boundary, max_boundary


# A Shell, brought to you by PharaohCola13

import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt
from matplotlib import *
from numpy import *
from mpl_toolkits.mplot3d.art3d import *
from matplotlib.animation import *

name = "Shell"

def shape(fig, alpha, color, edge_c, edge_w, grid, sides, edges, multi_pi):
# Definition of x
	def x_(u,v):
		x = power(1.2, v) * (sin(u)**2 * sin(v))
		return x

# Definition of y
	def y_(u,v):
		y = power(1.2, v) * (sin(u)**2 * cos(v))
		return y

# Definition of z
	def z_(u,v):
		z = power(1.2, v) * (sin(u) * cos(u))
		return z

#Value of the angles
	s = sides
	u = linspace(0, pi, s + 1)
	v = linspace(-pi/4, 5 * pi/2, edges)

	u, v = meshgrid(u, v)

# Symbolic representation
	x = x_(u,v)
	y = y_(u,v)
	z = z_(u,v)

# Figure Properties
	ax = p3.Axes3D(fig)
	ax.set_facecolor('black') # Figure background turns black

# Axis Properties
	plt.axis(grid) # Turns off the axis grid
	plt.axis('equal')

# Axis Limits
	#ax.set_xlim(-1,1)
	#ax.set_ylim(-1,1)
	#ax.set_zlim(-1,1)

# Surface Plot
	shell = ax.plot_surface(x, y, z)

	shell.set_alpha(alpha) # Transparency of figure
	shell.set_edgecolor(edge_c) # Edge color of the lines on the figure
	shell.set_linewidth(edge_w) # Line width of the edges
	shell.set_facecolor(color) # General color of the figure


from utils import *

import numpy as np
from scipy.sparse import csc_matrix
import matplotlib.pyplot as plt

def sigmoid(x):
    """ Apply sigmoid function.
    """
    return np.exp(x) / (1 + np.exp(x))

def neg_log_likelihood(data, theta, beta):
    """ Compute the negative log-likelihood.

    You may optionally replace the function arguments to receive a matrix.

    :param data: A dictionary {user_id: list, question_id: list,
    is_correct: list}
    :param theta: Vector
    :param beta: Vector
    :return: float
    """
    #####################################################################
    # TODO:                                                             #
    # Implement the function as described in the docstring.             #
    #####################################################################
    usr = np.array(data["user_id"])
    q = np.array(data["question_id"])
    c = np.array(data["is_correct"])
    para = theta[usr] - beta[q]
    log_like = np.log(sigmoid(para)) * c + np.log(1 - sigmoid(para)) * (1 - c)
    log_lklihood = np.sum(log_like)
    #####################################################################
    #                       END OF YOUR CODE                            #
    #####################################################################
    return -log_lklihood

def update_theta_beta(data, lr, theta, beta):
    """ Update theta and beta using gradient descent.

    You are using alternating gradient descent. Your update should look:
    for i in iterations ...
        theta <- new_theta
        beta <- new_beta

    You may optionally replace the function arguments to receive a matrix.

    :param data: A dictionary {user_id: list, question_id: list,
    is_correct: list}
    :param lr: float
    :param theta: Vector
    :param beta: Vector
    :return: tuple of vectors
    """
    #####################################################################
    # TODO:                                                             #
    # Implement the function as described in the docstring.             #
    #####################################################################
    usr = np.array(data["user_id"])
    q = np.array(data["question_id"])
    c = np.array(data["is_correct"])
    para = theta[usr] - beta[q]
    values = c - sigmoid(para)

    sparse = csc_matrix((values, (usr, q)), shape=(len(theta), len(beta))).toarray()
    theta = theta + np.sum(sparse, axis=1) * lr

    para = theta[usr] - beta[q]
    values = c - sigmoid(para)
    sparse = csc_matrix((values, (usr, q)), shape=(len(theta), len(beta))).toarray()
    beta = beta - np.sum(sparse, axis=0) * lr
    #####################################################################
    #                       END OF YOUR CODE                            #
    #####################################################################
    return theta, beta

def irt(data, val_data, lr, iterations):
    """ Train IRT model.

    You may optionally replace the function arguments to receive a matrix.

    :param data: A dictionary {user_id: list, question_id: list,
    is_correct: list}
    :param val_data: A dictionary {user_id: list, question_id: list,
    is_correct: list}
    :param lr: float
    :param iterations: int
    :return: (theta, beta, val_acc_lst)
    """
    # TODO: Initialize theta and beta.
    theta = np.zeros(542)
    beta = np.zeros(1774)

    val_acc_lst = []
    train_loglik = []
    valid_loglik = []

    for i in range(iterations):
        neg_lld = neg_log_likelihood(data, theta=theta, beta=beta)
        val_neg_lld = neg_log_likelihood(val_data, theta=theta, beta=beta)
        train_loglik.append(-neg_lld)
        valid_loglik.append(-val_neg_lld)

        score = evaluate(data=val_data, theta=theta, beta=beta)
        val_acc_lst.append(score)
        print("NLLK: {} \t Score: {}".format(neg_lld, score))
        theta, beta = update_theta_beta(data, lr, theta, beta)

    # TODO: You may change the return values to achieve what you want.
    return theta, beta, val_acc_lst, train_loglik, valid_loglik

def evaluate(data, theta, beta):
    """ Evaluate the model given data and return the accuracy.
    :param data: A dictionary {user_id: list, question_id: list,
    is_correct: list}

    :param theta: Vector
    :param beta: Vector
    :return: float
    """
    pred = []
    for i, q in enumerate(data["question_id"]):
        u = data["user_id"][i]
        x = (theta[u] - beta[q]).sum()
        p_a = sigmoid(x)
        pred.append(p_a >= 0.5)
    return np.sum((data["is_correct"] == np.array(pred))) \
           / len(data["is_correct"])

def main():
    train_data = load_train_csv("../data")
    # You may optionally use the sparse matrix.
    sparse_matrix = load_train_sparse("../data")
    val_data = load_valid_csv("../data")
    test_data = load_public_test_csv("../data")

    #####################################################################
    # TODO:                                                             #
    # Tune learning rate and number of iterations. With the implemented #
    # code, report the validation and test accuracy.                    #
    #####################################################################
    theta, beta, val_acc, train_loglik, valid_loglik \
        = irt(train_data, val_data, 0.01, 50)
    # plt.plot(val_acc)
    # plt.show()
    plt.plot(train_loglik, label="Training Log-likelihood")
    plt.plot(valid_loglik, label="Validation Log-likelihood")
    plt.xlabel("Num of Iteration")
    plt.ylabel("Log-likelihood")
    plt.title("Training curve")
    plt.legend()
    plt.show()
    #####################################################################
    #                       END OF YOUR CODE                            #
    #####################################################################

    #####################################################################
    # TODO:                                                             #
    # Implement part (c)
    validation_accuracy = evaluate(val_data, theta, beta)
    test_accuracy = evaluate(test_data, theta, beta)
    print("Final validation accuracy is: " + str(validation_accuracy))
    print("Final test accuracy is: " + str(test_accuracy))
    #####################################################################
    # implement part (d)
    p_1 = sigmoid(theta - beta[0])
    p_2 = sigmoid(theta - beta[1])
    p_3 = sigmoid(theta - beta[2])
    p_4 = sigmoid(theta - beta[3])
    p_5 = sigmoid(theta - beta[4])

    plt.scatter(theta, p_1, label="question 1")
    plt.scatter(theta, p_2, label="question 2")
    plt.scatter(theta, p_3, label="question 3")
    plt.scatter(theta, p_4, label="question 4")
    plt.scatter(theta, p_5, label="question 5")
    plt.xlabel("Value of theta")
    plt.ylabel("Probability of the correct response")
    plt.legend()
    plt.show()
    #####################################################################
    #                       END OF YOUR CODE                            #
    #####################################################################

if __name__ == "__main__":
    main()


#!/usr/bin/env python3

# Name: Grady Lynch, Frank Entriken
# Student ID: 002297574,
# Email: grlynch@chapman.edu,
# Course: PHYS220/MATH220/CPSC220 Fall 2018
# Assignment: cw11

import matplotlib.pyplot as plt
import numpy as np

def Euler(N):

    n = np.linspace(0, 10 * np.pi, N)

    k1 = np.zeros(N)

    k2 = np.zeros(N)
    k2[0] = 1

    d = (10 * np.pi)/N

    for x in range(1, len(k1)):
        k1[x] = k1[x-1] + d*k2[x-1]
        k2[x] = k2[x-1] - d*k1[x-1]

    plt.plot(n, k1)
    plt.plot(n, k2)

def Heun(N):

    n = np.linspace(0, 10 * np.pi, N)

    k1 = np.zeros(N)
    k1[0] = 0

    k2 = np.zeros(N)
    k2[0] = 1

    d = (10 * np.pi)/N

    for x in range(1, len(n)):

        temp1 = k2[x-1] - d*k1[x-1]
        temp2 = k1[x-1] + d*k2[x-1]
        k1[x] = k1[x-1] + (d/2)*(k2[x-1]+temp1)
        k2[x] = k2[x-1] - (d/2)*(temp2+k1[x-1])

    plt.plot(n, k1)
    plt.plot(n, k2)

def RK2(N):

    n = np.linspace(0, 10 * np.pi, N)

    k1 = np.zeros(N)
    k1[0] = 0

    k2 = np.zeros(N)
    k2[0] = 1

    d = (10 * np.pi)/N

    x1=0
    y1=0
    x2=0
    y2=0

    for x in range(1, len(k1)):
        x1 = d*k2[x-1]
        x2 = d*(k2[x-1]+x1/2)
        y1 = -d*k1[x-1]
        y2 = -d*(k1[x-1] + y1/2)
        k1[x] = k1[x-1] + x2
        k2[x] = k2[x-1] + y2

    plt.plot(n, k1)
    plt.plot(n, k2)

def RK4(N):

    n = np.linspace(0, 10 * np.pi, N)

    k1 = np.zeros(N)
    k1[0] = 0

    k2 = np.zeros(N)
    k2[0] = 1

    d = (10 * np.pi)/N

    x1=0
    y1=0
    x2=0
    y2=0
    x3=0
    y3=0
    x4=0
    y4=0

    for x in range(1, len(k1)):
        x1 = d*k2[x-1]
        x2 = d*(k2[x-1]+x1/2)
        x3 = d*(k2[x-1]+x2/2)
        x4 = d*(k2[x-1]+x3)
        k1[x] = k1[x-1]+(x1+2*x2+2*x3+x4)/6
        y1 = -d*k1[x-1]
        y2 = -d*(k1[x-1]+y1/2)
        y3 = -d*(k1[x-1]+y2/2)
        y4 = -d*(k1[x-1]+y3/3)
        k2[x] = k2[x-1]+(y1+2*y2+2*y3+y4)/6

    plt.plot(n, k1)
    plt.plot(n, k2)


# %% [markdown]
# ## Support Vector Machines
# Support vector machines (SVMs) are a particularly powerful and flexible class of supervised algorithms for both classification and regression.
# src: https://jakevdp.github.io/PythonDataScienceHandbook/05.07-support-vector-machines.html

# %% Standard imports
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

# use seaborn plotting defaults
import seaborn as sns
sns.set()

# %% A simple case of a classification task
from sklearn.datasets.samples_generator import make_blobs
X, y = make_blobs(
    n_samples=50,
    centers=2,
    random_state=0,
    cluster_std=0.60
)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')

# %% [markdown]
# A linear discriminative classifier would attempt to draw a straight line separating the two sets of data, and thereby create a model for classification.
# A problem: there is more than one possible dividing line that can perfectly discriminate between the two classes.
#
 A new data point (e.g., the one marked by the "X" in this plot) will be assigned a different label. Intuition of "drawing a line between classes" is not enough, and we need to think a bit deeper.

# %% Drawing different lines that divide two classes
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plt.plot([0.6], [2.1], 'x', color='red', markeredgewidth=2, markersize=10)

for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]:
    plt.plot(xfit, m * xfit + b, '-k')

plt.xlim(-1, 3.5)

# %% [markdown]
# SVM: Maximizing the Margin
#
 Intuition: rather than simply drawing a zero-width line between the classes, we can draw around each line a margin of some width, up to the nearest point.
#
 In support vector machines, the line that maximizes this margin is the one we will choose as the optimal model.
#
 Support vector machines are an example of such a maximum margin estimator.

# %% Displaying margin for svm division lines
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')

for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]:
    yfit = m * xfit + b
    plt.plot(xfit, yfit, '-k')
    plt.fill_between(xfit, yfit - d, yfit + d,
                     edgecolor='none', color='#AAAAAA', alpha=0.4)

plt.xlim(-1, 3.5)

#%% Fitting a support vector machine
from sklearn.svm import SVC  # Support Vector Classifier

model = SVC(C=1e10, kernel='linear')
model.fit(X, y)

def plot_svc_decision_function(model, ax=None, plot_support=True):
    """Plot the decision function for a 2D SVC"""
    if ax is None:
        ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    # create grid to evaluate model
    x = np.linspace(xlim[0], xlim[1], 30)
    y = np.linspace(ylim[0], ylim[1], 30)
    Y, X = np.meshgrid(y, x)
    xy = np.vstack([X.ravel(), Y.ravel()]).T
    P = model.decision_function(xy).reshape(X.shape)

    # plot decision boundary and margins
    ax.contour(X, Y, P, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])

    # plot support vectors
    if plot_support:
        ax.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=300, linewidth=1, facecolors='none')
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)

plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(model)

model.support_vectors_

#%% [markdown]
# A key to this classifier's success is that for the fit, only the position of the support vectors matter; any points further from the margin which are on the correct side do not modify the fit!
#
 Technically, this is because these points do not contribute to the loss function used to fit the model, so their position and number do not matter so long as they do not cross the margin.

#%% plot the model learned from the first 60 points and first 120 points of this dataset
def plot_svm(N=10, ax=None):
    X, y = make_blobs(n_samples=200, centers=2,
                      random_state=0, cluster_std=0.60)
    X = X[:N]
    y = y[:N]
    model = SVC(kernel='linear', C=1E10)
    model.fit(X, y)

    ax = ax or plt.gca()
    ax.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    ax.set_xlim(-1, 4)
    ax.set_ylim(-1, 6)
    plot_svc_decision_function(model, ax)

fig, ax = plt.subplots(1, 2, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
for axi, N in zip(ax, [60, 120]):
    plot_svm(N, axi)
    axi.set_title('N = {0}'.format(N))

#%% [markdown]
# In the left panel, we see the model and the support vectors for 60 training points.
#
 In the right panel, we have doubled the number of training points, but the model has not changed: the three support vectors from the left panel are still the support vectors from the right panel.
#
 This insensitivity to the exact behavior of distant points is one of the strengths of the SVM model.

#%% Interactivity
from ipywidgets import interact, fixed
interact(plot_svm, N=[10, 200], ax=fixed(None))

#%% Kernel SVM
from sklearn.datasets.samples_generator import make_circles

X, y = make_circles(100, factor=.1, noise=.1)

clf = SVC(kernel='linear').fit(X, y)

plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(clf, plot_support=False)

#%% radial basis function
r = np.exp(-(X ** 2).sum(1))

from mpl_toolkits import mplot3d

def plot_3D(elev=30, azim=30, X=X, y=y):
    ax = plt.subplot(projection='3d')
    ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
    ax.view_init(elev=elev, azim=azim)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('r')

interact(plot_3D, elev=[-90, 90], azip=(-180, 180), X=fixed(X), y=fixed(y))

#%% using rbf kernel
clf = SVC(kernel='rbf', C=1E6)
clf.fit(X, y)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(clf)
plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=300, lw=1, facecolors='none')

#%% Tuning the SVM: Softening Margins
X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=1.2)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')

X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)

fig, ax = plt.subplots(1, 2, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)

for axi, C in zip(ax, [10.0, 0.1]):
    model = SVC(kernel='linear', C=C).fit(X, y)
    axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_svc_decision_function(model, axi)
    axi.scatter(model.support_vectors_[:, 0],
                model.support_vectors_[:, 1],
                s=300, lw=1, facecolors='none')
    axi.set_title('C = {0:.1f}'.format(C), size=14)

#%% Example: face recognition
from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people(min_faces_per_person=60)
print(faces.target_names)
print(faces.images.shape)

#%% Plotting a few of these face
fig, ax = plt.subplots(3, 5)
for i, axi in enumerate(ax.flat):
    axi.imshow(faces.images[i], cmap='bone')
    axi.set(xticks=[], yticks=[], xlabel=faces.target_names[faces.target[i]])

#%% extract fundamental components & apply svm
from sklearn.decomposition import PCA as RandomizedPCA
from sklearn.pipeline import make_pipeline

pca = RandomizedPCA(n_components=150, whiten=True, random_state=42)
svc = SVC(kernel='rbf', class_weight='balanced')
model = make_pipeline(pca, svc)

from sklearn.model_selection import train_test_split
Xtrain, Xtest, ytrain, ytest = train_test_split(faces.data, faces.target, random_state=42)

from sklearn.model_selection import GridSearchCV
param_grid = {'svc__C': [1, 5, 10, 50], 'svc__gamma': [0.0001, 0.0005, 0.001, 0.005]}
grid = GridSearchCV(model, param_grid)

grid.fit(Xtrain, ytrain)
print(grid.best_params_)

#%% A prediction
model = grid.best_estimator_
yfit = model.predict(Xtest)
fig, ax = plt.subplots(4, 6)
for i, axi in enumerate(ax.flat):
    axi.imshow(Xtest[i].reshape(62, 47), cmap='bone')
    axi.set(xticks=[], yticks=[])
    axi.set_ylabel(faces.target_names[yfit[i]].split()[-1],
                   color='black' if yfit[i] == ytest[i] else 'red')
fig.suptitle('Predicted Names; Incorrect Labels in Red', size=14)

#%% Report
from sklearn.metrics import classification_report
print(classification_report(ytest, yfit, target_names=faces.target_names))

#%% Confusion matrix
# This helps us get a sense of which labels are likely to be confused by the estimator.
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest, yfit)
sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False,
            xticklabels=faces.target_names,
            yticklabels=faces.target_names)
plt.xlabel('true label')
plt.ylabel('predicted label')

#%% Summary [markdown]
# These methods are a powerful classification method for a number of reasons:
# - Their dependence on relatively few support vectors means that they are very compact models, and take up very little memory.
# - Once the model is trained, the prediction phase is very fast.
# - Because they are affected only by points near the margin, they work well with high-dimensional data—even data with more dimensions than samples, which is a challenging regime for other algorithms.
# - Their integration with kernel methods makes them very versatile, able to adapt to many types of data.
#
 Disadvantages are:
# - The scaling with the number of samples N is O[N3] at worst, or O[N2] for efficient implementations. For large numbers of training samples, this computational cost can be prohibitive.
# - The results are strongly dependent on a suitable choice for the softening parameter C. This must be carefully chosen via cross-validation, which can be expensive as datasets grow in size.
# - The results do not have a direct probabilistic interpretation. This can be estimated via an internal cross-validation (see the probability parameter of SVC), but this extra estimation is costly.

#

 With those traits in mind, I generally only turn to SVMs once other simpler, faster, and less tuning-intensive methods have been shown to be insufficient for my needs. Nevertheless, if you have the CPU cycles to commit to training and cross-validating an SVM on your data, the method can lead to excellent results.


#! /usr/bin/env python

"""
File: adaptive_trapezint.py
Copyright (c) 2016 Chinmai Raman
License: MIT

Course: PHYS227
Assignment: 1.1
Date: Feb 12, 2016
Email: raman105@mail.chapman.edu
Name: Chinmai Raman
Description: Uses Trapezoid Rule to find the integral of a function within a given error margin,
and outputs the number of trapezoids used.
"""
import numpy as np

def sec_dev(f, x, h=1e-6):
    """
    Calculates the second derivative of a function at a given x, given a small h
    """
    return (f(x - h) - 2 * f(x) + f(x + h)) / float(h**2)

def sec_dev_max(f, a, b):
    """
    Calculates the max second derivative within the range [a,b]
    """
    n = 999999
    i = (b - a) / float(n)
    max = abs(sec_dev(f, a))
    for counter in range(n):
        temp = abs(sec_dev(f, a + (counter + 1) * i))
        if temp > max:
            max = temp
    return max

def adaptive_trapezint(f, a, b, eps=1e-5):
    """
    Uses Trapezoid Rule to find the integral of a function within a given error margin,
    and outputs the number of trapezoids used.
    """
    # h = np.sqrt(12.0 * eps) * (((b - a) * sec_dev_max(f, a, b))**(-1/2.0))

    h = np.sqrt(12.0 * eps) / np.sqrt((b - a) * sec_dev_max(f, a, b))
    n = (b - a) / float(h)
    return (trapezint(f, a, b, n), n)

def trapezint(f, a, b, n):
    """
    Uses trapezoid rule to find the integral of a function
    """
    sum = 0.0
    h = (b - a) / float(n)
    for counter in range(int(n)):
        sum += (1 / 2.0) * h * (f(a + counter * h) + f (a + (counter + 1) * (h)))
    return sum

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

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

def integrals():
    """
    Outputs the results of estimation of integral by the trapezoid rule for
    the given functions and outputs the errors from the actual integrals.
    """
    cos1 = adaptive_trapezint(cos, 0, np.pi)
    sin1 = adaptive_trapezint(sin, 0, np.pi)
    sin2 = adaptive_trapezint(sin, 0, np.pi / 2)

    exactcos = 0
    exactsin = 2
    exactsin2 = 1

    errorcos = abs(cos1[0] - exactcos)
    errorsin = abs(sin1[0] - exactsin)
    errorsin2 = abs(sin2[0] - exactsin2)

    print("Function\tError\t\t\tEstimated n")
    print("Cos [0, pi]\t" + str(errorcos) + "\t" +str(cos1[1]))
    print("Sin [0, pi]\t" + str(errorsin) + "\t" +str(sin1[1]))
    print("Sin [0, pi/2]\t" + str(errorsin2) + "\t" +str(sin2[1]))

def f(x):
    return 2 * x + 4

def test():
    print(abs(trapezint(f, 0, 2, 10)))
    assert (abs(trapezint(f, 0, 2, 10) - 12.0) < 1E-5)

integrals()


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

from UncertainSCI.distributions import NormalDistribution
from scipy.stats import multivariate_normal

dim = 2
mean = np.array([0, 0])
cov = np.array([[1, 0], [0, 5]])

p = NormalDistribution(mean=mean, cov=cov)

mu = p.mean()
cov = p.cov()

print("The mean of this distribution is")
print(np.array2string(mu))
print("\nThe covariance matrix of this distribution is")
print(np.array2string(cov))

# Create a grid to plot the density
M = 100
x = np.linspace(-5, 5, M)
y = np.linspace(-5, 5, M)

X, Y = np.meshgrid(x, y)
XY = np.vstack([X.flatten(), Y.flatten()]).T

pdf = p.pdf(XY)
# Reshape for plotting
pdf = np.reshape(pdf, [M, M])

fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
surf = ax.plot_surface(X, Y, pdf, cmap=cm.coolwarm)
fig.colorbar(surf)
plt.title('PDF for a bivariate normal distribution')

plt.show()

# x, y = np.random.multivariate_normal(mean, cov, M).T
# xx = np.random.multivariate_normal(mean, cov, M)
var = multivariate_normal(mean=mean, cov=cov)
true_pdf = var.pdf(XY).reshape([M, M])
print("The error of pdf is")
print(np.linalg.norm(pdf - true_pdf))


"""Functions for fascicle detection - FaDe"""

import math as m
import cv2
import numpy as np
#------------------------------------------------------------------------------#
def d2_gaussianMasks(s):
    """Implements Gaussian's second derivatives masks for a given standard
    deviation s.
    The masks' size is set as 6*s rounded to the superior integer (+1 if even).

    Args:
        s (double) : standard deviation

    Returns:
        mask_xx, mask_xy, mask_yy (array_like): masks obtained from derivation of a gaussian,
        according to x and x, to x and y, to y and y respectively.

    """

    'Calculation of the size of the masks'
    size = m.ceil(s*6)
    if size%2==0: #if size if even, make it odd
        size+=1

    half =  m.floor(size/2)
    mask_xx = np.zeros((size,size))
    mask_xy = np.zeros((size,size))
    mask_yy = np.zeros((size,size))

    'Second derivatives expressions'
    for x in range (-half, half +1):
        for y in range (-half, half +1):

            mask_xx[x + half][y + half] = (x**2/s**2-1.)/(2.*m.pi*s**4)\
                                                *m.exp(-(x**2+y**2)/(2.*s**2))

            mask_xy[x + half][y + half] = x*y/(2.*m.pi*s**6)\
                                                *m.exp(-(x**2+y**2)/(2.*s**2))

            mask_yy[x + half][y + half] = (y**2/s**2-1.)/(2.*m.pi*s**4)\
                                                *m.exp(-(x**2+y**2)/(2.*s**2))
    return mask_xx, mask_xy, mask_yy

'-----------------------------------------------------------------------------'

def MVEF_2D(I, scales, thresholds):
    """Multiscale Vessel Enhancement Method for 2D images - based on Frangi's,
    Rana's, and Jalborg's works. This method searches for geometrical
    structures which can be regarded as tubular.
    The function is coded to enhance dark tube-like structure, so make
    sure that you do not need to inverse your image I before using this function.

    Args:
        I (2D array):       I is a grayscale image (otherwise it is
                            converted to grayscale)
        thresholds (list):  thresholds is a list of two thresholds that
                            control the sensitivity of the line filter
                            to the measures Fr and R. It should look like
                            [b, c], where b must not be zero. If c is zero,
                            then its value is set to half the frobenius norm
                            of the hessian matrix.
        scales (list):      scales is a list of lengths that correspond to
                            the diameter (in pixels) of the tube-like structure to find

    Outputs:
        I2 (2D array):  one-canal image I, filtered by the multiscale vessel enhancement
                        filter
    References:
        Based on [ Automatic detection of skeletal muscle architecture
        features, Frida Elen Jalborg, Master’s Thesis Spring 2016 ]
                [Frangi, 1998, Multiscale Vessel Enhancement filtering]
                [ Rana et al., 2009, Automated tracking of muscle fascicle
                orientation in B-mode ultrasound images]
                [ Rana et al., 2011, In-vivo determination of 3D muscle
                architecture of human muscle using free hand ultrasound]

    """
    from skimage.feature import hessian_matrix, hessian_matrix_eigvals

    if len(I.shape)>2:
        I = cv2.cvtColor(I, cv2.COLOR_RGB2GRAY)

    vesselness = np.zeros((I.shape[0], I.shape[1], len(scales)))
    I2 = np.zeros((I.shape[0], I.shape[1]))
    b = thresholds[0]
    c = thresholds[1]
    if b == 0:
         ValueError('first element of thresholds cannot be null')

    #calculation of Hessian matrix and its eigen values.
    #Corresponding eigen vectors are normal to and in the direction of
    #the tubular structure
    for sc in range(len(scales)):
        H = hessian_matrix(image = I, sigma = scales[sc], order = 'rc')
        eigvals = hessian_matrix_eigvals(H) #2 eigenvalues in decreasing order; shape of ei = (2,I.shape[0], I.shape[1])
        frobeniusnorm =  np.sqrt(H[0]**2+ H[1]**2 + 2 * H[2]**2)
        if c == 0:
            c = 1 / 2 * np.amax(frobeniusnorm)

        for i in range(I.shape[0]):
            for j in range(I.shape[1]):

                'Calculation of vesselness: looking for a POSITIVE highest'
                'eigenvalue <=> looking for dark tube-like structures; looking'
                'for a NEGATIVE highest eigen value <=> looking for bright'
                'tube-like structures'
                #bright tube-like structures search did not work so the
                #temporary solution is to inverse the ultrasound image
                #and search for dark tube-like structures

                #if eigvals[0,i,j]<=0:
                    #vesselness[i,j,sc] = 0
                    #not necessary since vesselness initialized with zeros

                if eigvals[0,i,j]>0:

                    'ratio - for second order ellipsoid'
                    R = eigvals[1,i,j]/eigvals[0,i,j]

                    'Frobenius norm - for second order structureness'
                    Fr = m.sqrt(eigvals[0,i,j]*eigvals[0,i,j] + eigvals[1,i,j]*eigvals[1,i,j])

                    vesselness[i,j,sc]=scales[sc]*m.exp(-R*R/(2.*b*b))*(1.-m.exp(-Fr*Fr/(2*c*c)))

    'Keep the highest value of vesselness across all scales'
    for ind1 in range(I2.shape[0]):
        for ind2 in range(I2.shape[1]):
            I2[ind1,ind2] = np.max(vesselness[ind1,ind2,:])

    return I2
'-----------------------------------------------------------------------------'

def locateSnippets(I, xcalib, ycalib, minLength, offSetX = 0, offSetY = 0):
    """
    This function aims at detecting portions of fascicles in a binary image I,
    obtained from the binarization of an image filtered with MVEF.
    These portions of fascicles are filtered with statistical filtering
    on the following parameters: length, alignement, aspect ratio and
    angle with the horizontal.

    Args:
        I (array): binary image (one canal)
        xcalib (float): vertical calibration factor
        ycalib (float): horizontal calibration factor
        minLength (float):  length in pixels, under which a portion of fascicle
                            will be considered or removed from the analysis.
        offSetX, offSetY (integers):  if the image I is a subimage of another bigger image
                            I2, and if you want to obtain the localization of the
                            snippets in I2, offSetX and offSetY should correspond
                            to the first line of I in I2 and the first column of I
                            in I2 respectively.
    Outputs:
        list of arrays: each array is a snippet's points coordinates
        list of parameters that caracterize the modeling line of a snippet. Each
        parameter is such as [slope, [point1_line + offSetY, pont1_column + offSetX]]
    """
    #attention, contours points found by findContours have the structure (columns, rows)
    snippets = cv2.findContours(np.uint8(I), mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)[0]

    #if no contour is found, stop analysis:
    if len(snippets) == 0:
        return 'error', 'error'

    line_snip = [] #list which contains a linear approximation of the snippet sn
    vect = np.zeros((len(snippets), 4))

    for i in range(len(snippets)):
        sn = list(snippets[i][:,0,:])
        sn.sort(key=lambda x:x[0]) #sort according to columns

        #######################################################################
        # construction of 2 points p1, p2 to represent sn by a line
        third = 1/3.*len(sn)/2
        left_points = [sn[i] for i in range(len(sn)) if sn[i][0] == sn[0][0] + int(third)]
        right_points = [sn[i] for i in range(len(sn)) if sn[i][0] == sn[-1][0] - int(third)]
        left_points.sort(key=lambda x:x[1]) #sort according to lines
        right_points.sort(key = lambda x:x[1])

        if len(left_points)==2:
            p1 = [int((left_points[0][0] + left_points[-1][0])/2),\
                      int((left_points[0][1] + left_points[-1][1])/2)]
        elif len(left_points) == 1:
            p1 = left_points[0]
        elif len(left_points) > 2: #take the extremal left point of the snippet
            p1 = [sn[i] for i in range(len(sn))\
                      if sn[i][0] == np.amin(np.array(sn)[:,0])][0]

        if len(right_points)==2:
            p2 = [int((right_points[0][0] + right_points[-1][0])/2),\
                    int((right_points[0][1] + right_points[-1][1])/2)]
        elif len(right_points)==1:
            p2 = right_points[0]
        elif len(right_points) > 2: #take right extremal point of the snippet
            p2 = [sn[i] for i in range(len(sn))\
                    if sn[i][0] == np.amax(np.array(sn)[:,0])][0]

        # create line (x_list, y_list) inside snippet's contour
        cmin = np.amin(snippets[i][:,0,0])
        cmax = np.amax(snippets[i][:,0,0])
        y_list = np.arange(cmin, cmax, 1)
        if p2[0]-p1[0] != 0:
            slope = (p2[1]-p1[1])/(p2[0]-p1[0])
            x_list = (slope*(y_list-p1[0])+p1[1])
            line_snip.append([slope, [p1[0] + offSetY, p1[1] + offSetX]])
        else:
            slope = 0
            x_list = np.arange(np.amin(snippets[i][:,0,1]), np.amax(snippets[i][:,0,1])+1, 1)
            line_snip.append([slope, [p1[0] + offSetY, p1[1] + offSetX]])

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

        # Features: length, alignement, aspect ratio and angle with horizontal
        if len(sn) > 5 : #make sure minimum of points required for fitEllipse function is reached
            (x0,y0),(mia,maa), ang = cv2.fitEllipse(snippets[i])
            angle = 90 - ang
            aspra = float(mia/maa)
            if angle == 90:
                align = 0
            else:
                pix_white = 0
                for n in range(y_list.shape[0]):
                    if int(x_list[n])<I.shape[0]:
                        if I[int(x_list[n]), int(y_list[n])] > 0 :
                            pix_white = pix_white + 1
                align = pix_white/y_list.shape[0]
        else:
            maa = 0
            angle = 0
            aspra = 0.
            align = 0

        vect[i,0] = maa * xcalib
        vect[i,1] = angle
        vect[i,2] = align
        vect[i,3] = aspra

    # Normalization
    medianL = np.median(vect[:,0])
    medianAng = np.median(vect[:,1])
    medianAl = np.median(vect[:,2])
    medianAR = np.median(vect[:,3])

    madL = np.median(abs(vect[:,0] - medianL))
    madAng = np.median(abs(vect[:,1] - medianAng))
    madAl = np.median(abs(vect[:,2] - medianAl))
    madAR = np.median(abs(vect[:,3] - medianAR))

    vect[:,0] = (vect[:,0] - medianL) / madL
    vect[:,1] = (vect[:,1] - medianAng) / madAng
    vect[:,2] = (vect[:,2] - medianAl) / madAl
    vect[:,3] = (vect[:,3] - medianAR) / madAR

    norm = np.sqrt(vect[:,0]*vect[:,0] + vect[:,1]*vect[:,1]\
        + vect[:,2]*vect[:,2] + vect[:,3]*vect[:,3])

    #Filtering on norm
    #The value "4" has been empirically chosen by observing snippets depending on their norm value
    lines = []
    filtered_indices = [] # list which contains the ID of the filtered snippets
    for n in range(len(snippets)):
        if norm[n] >= 4 and vect[n,0] > minLength:
            filtered_indices.append(n)
            lines.append(line_snip[n])

    #outputs
    #retrieve snippets' points coordinates if their ID is in filtered_indices
    filtered_snippets = []
    for fa in range(len(filtered_indices)):
        fasc = snippets[filtered_indices[fa]][:,0,:]
        fasc[:, [0,1]] = fasc[:,[1,0]] #change structure to (rows, columns)
        fasc[:, 0] = fasc[:, 0] + offSetX
        fasc[:,1] = fasc[:, 1] + offSetY
        filtered_snippets.append(fasc)

    return filtered_snippets, lines

def combineSnippets(I, listS, listS_paramlines, min_nb_sn, thresh_alignment = 20):
    """
    This function aims at combining aligned snippets (we consider that, if two
    snippets are aligned, they correspond to the same muscle fascicle) so that to
    reconstruct muscle fascicles.

    Args:
        I : one-canal or three-canal image
        listS (list of arrays): list of arrays, each array contains one snippet's points coordinates (each
        point is given by (line, column))
        listS_paramlines (list of lists): list of sublist. Each sublists contains parameters that
        caracterize the modeling line of a snippet. The format of this list is
        the same output from the function locateSnippets. The format of sublists:
        [slope, [point1_line + offSetY, pont1_column + offSetX]]
        min_nb_sn (integer): once aligned, you have the possibility to differentiate
        the fascicles with a number of detected snippets below min_nb_sn, and above
        min_nb_sn. This allows you to process those fascicles differently in the
        following of your script (for exemple for the degree of modeling curves).
        thresh_alignment (interger): this is the threshold that determines if two snippets are aligned.

                Potential aligned snippets are such that the modeling line of one intercept the contour
                of the other.
                thresh_alignment corresponds to the maximum number of lines accepted between:
                        - point1 of the most right snippet
                        - the point with same column as point1, on the modeling line of the most left snippet
                In addition, the two snippets must be less than 1/4 of the image length far,
                to be considered as aligned snippets.

    Outputs:
        two lists of arrays. Each array contains the points coordinates of the
        aligned snippets of a same fascicle.
        The first list contains the fascicles with more than min_nb_sn snippets.
        The second list contains the fascicles with less than min_nb_sn snippets.
    """
    fascicles_points = []
    fascicles2_points = []
    if len(listS) != 0:
        aligned_snip = [[j] for j in range(len(listS))] # contains list of snippets indices part of same fascicle

        #modeling line of snippet s
        for s in range(len(listS) - 1):
            ylist_s = np.arange(0, I.shape[1],1, dtype = np.int64)
            slope_s = listS_paramlines[s][0] #slope
            p1_s = listS_paramlines[s][1] #point on the line of the snippet s = [y, x]
            xlist_s = (slope_s*(ylist_s-p1_s[0])+p1_s[1])
            xy_list_s = np.uint64(np.vstack((xlist_s, ylist_s))).T

            for n in range(s+1, len(listS)):
                #check if the previous line intersect a snippet n different from s

                slope_n = listS_paramlines[n][0]
                p1_n = listS_paramlines[n][1]
                ylist_n = np.arange(0, I.shape[1],1, dtype = np.int64)
                xlist_n = (slope_n*(ylist_n-p1_n[0])+p1_n[1])
                xy_list_n = np.uint64(np.vstack((xlist_n, ylist_n))).T

                intersection1 = set.intersection(set(map(tuple,xy_list_s)), set(map(tuple, listS[n])))
                intersection2 = set.intersection(set(map(tuple,xy_list_n)), set(map(tuple, listS[s])))

                #if intersection not empty
                if (intersection1 or intersection2):

                    if np.amin(np.array(listS[n])[:,1]) < np.amin(np.array(listS[s])[:,1]):
                        # if snippets roughly aligned :
                        if abs(p1_s[1]-xlist_n[p1_s[0]])<thresh_alignment:
                            # if snippets not too far from each other
                            # ie maximum a quarter of I's length
                            if abs(np.amax(np.array(listS[n])[:,1])\
                               - np.amin(np.array(listS[s])[:,1])) < I.shape[1]/4:
                                aligned_snip[s].append(n)

                    elif np.amin(np.array(listS[n])[:,1]) >= np.amin(np.array(listS[s])[:,1]):
                        # if snippets roughly aligned :
                        if abs(p1_n[1]-xlist_s[p1_n[0]])<thresh_alignment:
                            # if snippets not too far from each other
                            # ie maximum a quarter of I's length
                            if abs(np.amin(np.array(listS[n])[:,1])\
                                   - np.amax(np.array(listS[s])[:,1])) < I.shape[1]/4:
                                aligned_snip[s].append(n)

        #example : we obtain aligned_snip = [[1,2,3],[2,5],[3],[4,5],[5],[7],[8,10],[10]]
        # we need to merge [1,2,3], [2,5], [3], [4,5] and [5]; and we need to merge
        #[8,10] with [10].
        # this is what is done below.
        grouped_snip = [aligned_snip[0]]
        for f in range(1, len(aligned_snip)):
            test = 0

            for ff in range(len(grouped_snip)):
                if set.intersection(set(aligned_snip[f]), set(grouped_snip[ff])):
                    grouped_snip[ff] = list(set(grouped_snip[ff] + aligned_snip[f])) #conversion to set type removes duplicates
                    test = 1

            if test == 0:
                grouped_snip.append(aligned_snip[f])

        #outputs
        for fa in range(len(grouped_snip)):
            fasc = listS[grouped_snip[fa][0]]
            if len(grouped_snip[fa]) < min_nb_sn:
                fascicles2_points.append(fasc)
            else:
                for s in range(1,len(grouped_snip[fa])):
                    fasc = np.concatenate((fasc, listS[grouped_snip[fa][s]]), axis = 0)
                fascicles_points.append(fasc)

    return fascicles_points, fascicles2_points

def contourAverage(inputC):
    """Function that approximate a contour by a line inside the contour.
        It realizes the mean of the lines of two points on a same column.
        If there is one point or strictly more than 2 points in a same
        column, the column is not considered and no mean is realized.

    Args:
        inputC (list of arrays): each array corresponds to the points coordinates
        of a fascicle. The format of inputC is the same as one of the output lists
        of function combineSnippets.
        Points coordinates are (rows along axis 0, columns along axis 1).

    Outputs:
        ouputL : list of arrays that contain points [row, column]
    """
    input_list = list(inputC)

    outputL = []

    min_column = np.amin(inputC[:,1])
    max_column = np.amax(inputC[:,1])

    for c in range(min_column, max_column + 1):
        pt = [point for point in input_list if point[1] == c]
        if len(pt) == 2:
            outputL.append(np.uint64(np.mean(pt, axis = 0)))
    return outputL

def approximateFasc(typeapprox, listF, d):
    """
    Realizes the interpolation of a list of fascicles, either with polynomial fitting
    or with B-splines.

    Inputs:
        listF is the list of fascicles (same format as output from function contourAverage).
        Each fascicle is an array of points defining its line. Each point = (row, column).
        d (integer) : degree of the curve that will be interpolated
        typeapprox (string): either 'Bspline' or 'polyfit', depending on the type of
        approximation that you want to realize (spline or fitting with a polynom respectively)
    Outputs:
        a list of splines.
    """
    approx_fasc = []

    if typeapprox == 'Bspline':
        import scipy.interpolate as interpolate

        #transform each fascicle format in list
        for n in range(len(listF)):
            if type(listF[n]) == list:
                f = listF[n]
            else:
                f = list(listF[n])

            #sort all fascicle points in increasing columns order
            f.sort(key = lambda x: x[1])

            #remove potential double points
            to_remove = []
            for pt in range(len(f)-1):
                if f[pt][1] == f[pt+1][1]:
                    to_remove.append(pt+1)
            fa = [f[ind] for ind in range(len(f)) if ind not in to_remove]

            ycoord = [fa[i][1] for i in range(len(fa))]
            xcoord = [fa[i][0] for i in range(len(fa))]

            #interpolation
            spline = interpolate.UnivariateSpline(ycoord, xcoord, k=d, ext = 0)
            approx_fasc.append(spline)

    if typeapprox == 'polyfit':
        for n in range(len(listF)):

            #change format to list if necessary
            if type(listF[n]) == list:
                f = listF[n]
            else:
                f = list(listF[n])

            #sort fascicle points in increasing columns order
            f.sort(key = lambda x: x[1])

            #remove potential double points
            to_remove = []
            for pt in range(len(f)-1):
                if f[pt][1] == f[pt+1][1]:
                    to_remove.append(pt+1)
            fa = [f[ind] for ind in range(len(f)) if ind not in to_remove]

            ycoord = [fa[i][1] for i in range(len(fa))]
            xcoord = [fa[i][0] for i in range(len(fa))]

            #interpolation and conversion to spline
            p = np.polyfit(ycoord, xcoord, deg=d)
            spline = np.poly1d(p)
            approx_fasc.append(spline)

    return approx_fasc


import numpy
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt

# Define some test data which is close to Gaussian
data = plt.hist(step_sizes_list,50)
bin_centres = data[1][1:]
hist = data[0]

# Define model function to be used to fit to the data above:
# Adapt it to as many gaussians you may want
# by copying the function with different A2,mu2,sigma2 parameters
def gauss(x, *p):
    A, mu, sigma = p
    return A*numpy.exp(-(x-mu)**2/(2.*sigma**2))

def gauss2(x, *p):
    A1, mu1, sigma1, A2, mu2, sigma2 = p
    return A1*numpy.exp(-(x-mu1)**2/(2.*sigma1**2)) + A2*numpy.exp(-(x-mu2)**2/(2.*sigma2**2))

# p0 is the initial guess for the fitting coefficients initialize them differently so the optimization algorithm works better
p0 = [100., 100., 10.,20., 200., 10.]

#optimize and in the end you will have 6 coeff (3 for each gaussian)
coeff, var_matrix = curve_fit(gauss2, bin_centres, hist, p0=p0)

#you can plot each gaussian separately using
pg1 = coeff[0:3]
pg2 = coeff[3:]

g1 = gauss(bin_centres, *pg1)
g2 = gauss(bin_centres, *pg2)

fig = plt.figure()

plt.plot(bin_centres, hist, label='Data')
plt.plot(bin_centres, g1, label='Gaussian1', 'r--')
plt.plot(bin_centres, g2, label='Gaussian2','r--')
plt.legend()

plt.show()


import numpy as np
import scipy.linalg as linalg

class KalmanFilter(object):
    """Simple multi-dimensional Kalman filter"""

    def __init__(self, state_dim, measurement_dim):
        if state_dim < 1:
            raise ValueError('state_dim must be 1 or greater')
        if measurement_dim < 1:
            raise ValueError('measurement_dim must be 1 or greater')

        self.state_dim = state_dim                              # Number of state variables
        self.measurement_dim = measurement_dim                  # Number of measurement variables
        self.x = np.zeros((state_dim, 1))                       # State mean
        self.P = np.eye(state_dim)                              # State covariance
        self.Q = np.eye(state_dim)                              # Process covariance
        self.F = np.eye(state_dim)                              # State transition matrix
        self.H = np.zeros((measurement_dim, state_dim))         # Measurement function
        self.R = np.eye(measurement_dim)                        # Measurement uncertainty

        # Computed by update, here for inspection
        self.K = np.zeros((state_dim, measurement_dim))         # Kalman gain
        self.y = np.zeros((measurement_dim, 1))                 # Residual
        self.S = np.zeros((measurement_dim, measurement_dim))   # System uncertainty

    def check_shapes(self):
        if self.x.shape != (self.state_dim, 1):
            raise ValueError('x shape is wrong')
        if self.P.shape != (self.state_dim, self.state_dim):
            raise ValueError('P shape is wrong')
        if self.Q.shape != (self.state_dim, self.state_dim):
            raise ValueError('Q shape is wrong')
        if self.F.shape != (self.state_dim, self.state_dim):
            raise ValueError('F shape is wrong')
        if self.H.shape != (self.measurement_dim, self.state_dim):
            raise ValueError('H shape is wrong')
        if self.R.shape != (self.measurement_dim, self.measurement_dim):
            raise ValueError('R shape is wrong')
        if self.K.shape != (self.state_dim, self.measurement_dim):
            raise ValueError('K shape is wrong')
        if self.y.shape != (self.measurement_dim, 1):
            raise ValueError('y shape is wrong')
        if self.S.shape != (self.measurement_dim, self.measurement_dim):
            raise ValueError('S shape is wrong')

    def predict(self):
        self.check_shapes()
        self.x = np.dot(self.F, self.x)
        self.P = np.dot(self.F, self.P).dot(self.F.T) + self.Q
        return self.x, self.P

    def update(self, z):
        self.check_shapes()
        if z.shape != (self.measurement_dim, 1):
            raise ValueError('z shape is wrong')

        PHT = np.dot(self.P, self.H.T)
        self.S = np.dot(self.H, PHT) + self.R
        self.K = np.dot(PHT, linalg.inv(self.S))
        self.y = z - np.dot(self.H, self.x)
        self.x = self.x + np.dot(self.K, self.y)
        self.P = self.P - np.dot(self.K, self.H).dot(self.P) # Should be P = (I - KH)P, but might be unstable?
        return self.x, self.P


from sympy import *

x = symbols('x')
init_printing(use_unicode=True)

f=Lambda(x, cos(x))
g=Lambda(x, diff(f(x),x,2))

#f''(c)= (-1/12*f(c-2h)+4/3*f(c-h)-5/2*f(c)+4/3*f(c+h)-1/12*f(c+2h))/h**2
def fdn2(f, c, h):
    return (-1/12*f(c-2*h)+4/3*f(c-h)-5/2*f(c)+4/3*f(c+h)-1/12*f(c+2*h))/h**2

h=0.001
c=0

fdn2c=fdn2(f, c, h)
print("FDN2: f''(c)=", fdn2c)
print("Error FDN2:", abs(fdn2c-g(c)))


import numpy as np
import matplotlib.pyplot as plt

def bernstein(t, n, i):
    cn = 1.0
    ci = 1.0
    cni = 1.0
    for k in range(2, n, 1):
        cn = cn * k
    for k in range(1, i, 1):
        if i == 1:
            break
        ci = ci * k
    for k in range(1, n - i + 1, 1):
        if n == i:
            break
        cni = cni * k
    j = t**(i - 1) * (1 - t)**(n - i) * cn / (ci * cni)
    return j

def bezierplot(t, cp):
    n = len(cp)
    r = np.zeros([len(t), 2])
    for k in range(len(t)):
        sum1 = 0.0
        sum2 = 0.0
        for i in range(1, n + 1, 1):
            bt = bernstein(t[k], n, i)
            sum1 += cp[i - 1, 0] * bt
            sum2 += cp[i - 1, 1] * bt
        r[k, :] = [sum1, sum2]
    return np.array(r)

cp = np.array([[0, -2], [1, -3], [2, -2], [3, 2], [4, 2], [5, 0]])
t = np.arange(0, 1 + 0.01, 0.01)
p = bezierplot(t, cp)
plt.figure()
plt.plot(p[:, 0], p[:, 1])
plt.plot(cp[:, 0], cp[:, 1], ls=':', marker='o')
plt.show()


import numpy as np
import os,sys
import os_file as tos
#import matplotlib.pyplot as plt
import math

work_path='./work/'

def create_city(low=0,up=90):
    x=np.random.uniform(low,up,2)
    return x

def create_cities(num=100):
    cities=np.random.uniform(0,90,(num,2))
    return cities

def save_data(num=100):
    cities=create_cities(num)
    files=os.listdir(work_path)
    if 'work' not in files:
        os.mkdir(work_path)
    np.save(work_path+'cities.npy',cities)

def load_data(filepath):
    cities=np.load(filepath)
    return cities

def dist(city1,city2):
    theta=city1[0]-city2[0]
    dist=math.sin(math.radians(city1[1]))*math.sin(math.radians(city2[1]))+math.cos(math.radians(city1[1]))*math.cos(math.radians(city2[1]))*math.cos(theta)
    dist=math.acos(dist)
    dist=math.degrees(dist)
    dist=dist*60*1.1515
    dist=dist*1.609344
    return dist

def distance_matrix(cities):
    num=len(cities)
    matrix=np.zeros((num,num))
    for i in range(num):
        for j in range(i+1,num):
            matrix[i][j]=dist(cities[i],cities[j])
            matrix[j][i]=matrix[i][j]
    return matrix

def cost(solution,matrix):
    #tmp=np.copy(solution)
    tmp=np.append(solution,solution[0])
    #print("tmp",tmp)
    n=len(solution)
    cost=0
    for i in range(n):
        cost+=matrix[tmp[i]][tmp[i+1]]
    return cost

def random_solution(num=100):
    return np.random.permutation(num)

def init_solutions(num=10,cities=100):
    solutions=[]
    for i in range(num):
        solutions.append(random_solution(cities))
    return solutions

def mutation_1(solution):
    n=len(solution)
    start=np.random.randint(0,n-2)
    end=np.random.randint(start+1,n)
    k=solution[start:end]
    solution[start:end]=k[::-1]
    return solution

def mutation_2(solution):
    n=len(solution)
    start=np.random.randint(0,n-2)
    end=np.random.randint(start+1,n)
    k=solution[start]
    solution[start:end-1]=solution[start+1:end]
    solution[end-1]=k
    return solution

if __name__=="__main__":
    tos.clean_work_path(work_path)
    cities=create_cities(100)
    tos.set_np_file(work_path,'cities.npy',cities)
    with open(work_path+'new_job.txt','w') as ff:
        ff.write('cities.npy')


import numpy as np

alturas = [167, 166, 146, 168, 181, 174, 173, 148, 150, 152, 160, 152, 150, 156, 158, 171, 177, 156, 151, 173, 151, 159,
169, 163, 164, 167, 171, 151, 156, 179, 182, 157, 166, 161, 179, 172, 159, 155, 162, 155]
alturas = np.array(alturas)
n_alturas = len(alturas)
mc_counts = 10000
means = np.zeros(mc_counts)
stds = np.zeros(mc_counts)

for i in range(mc_counts):
    indices_sr = np.random.randint(0, n_alturas, n_alturas)
    alturas_sr = alturas[indices_sr]
    means[i] = np.mean(alturas_sr)
    stds[i] = np.std(alturas_sr)

mean_mc = means.mean()
std_mc = means.std()
std_mc_real = stds.mean()

print(f"usual method {alturas.mean()}, {alturas.std()}")
print(f"mean_mc = {mean_mc}, std_mc = {std_mc}")
print(f"mean_mc = {mean_mc}, std_mc_real = {std_mc_real}")


import numpy as np
import pandas
x=[1.0,1.5,2.0,2.5,3.0,3.5,4.0]
y=[1.1,1.3,1.6,2.0,2.7,3.4,4.1]
sigmay=sum(y)
sigmax=sum(x)
n=len(x)
xsquare=[]
for i in x:
    xsquare.append(i*i)
sigmaxsquare=sum(xsquare)
xy=[]
for i in range(len(x)):
    xy.append(x[i]*y[i])
sigmaxy=sum(xy)
xcube=[]
for i in x:
    xcube.append(i*i*i)
sigmaxcube=sum(xcube)
xfour=[]
for i in x:
    xfour.append(i*i*i*i)
sigmaxfour=sum(xfour)
xsquarey=[]
for i in range(len(x)):
    xsquarey.append(x[i]*x[i]*y[i])
sigmaxsquarey=sum(xsquarey)
eq1=f'{n}a + b{sigmax} + c{sigmaxsquare} = {sigmay}'
eq2=f'{sigmax}a + b{sigmaxsquare} + c{sigmaxcube} = {sigmaxy}'
eq3=f'{sigmaxsquare}a + b{sigmaxcube} + c{sigmaxfour} = {sigmaxsquarey}'
x.append('Total')
y.append(sigmay)
xy.append(sigmaxy)
xsquare.append(sigmaxsquare)
xsquarey.append(sigmaxsquarey)
xcube.append(sigmaxcube)
xfour.append(sigmaxfour)
df=pandas.DataFrame({'X': x,'Y':y,'XY':xy,'X²':xsquare,'X²Y':xsquarey,'X³':xcube,'X⁴':xfour})
print(df)
print()
print(eq1)
print(eq2)
print(eq3)
print()
xy_list1=[]
xy_list1.append(n)
xy_list1.append(sigmax)
xy_list1.append(sigmaxsquare)
xy_list2=[]
xy_list2.append(sigmax)
xy_list2.append(sigmaxsquare)
xy_list2.append(sigmaxcube)
xy_list3=[]
xy_list3.append(sigmaxsquare)
xy_list3.append(sigmaxcube)
xy_list3.append(sigmaxfour)
xylist=[xy_list1,xy_list2,xy_list3]

A=np.array(xylist)
inv_A=np.linalg.inv(A)
coff=[]
coff.append(sigmay)
coff.append(sigmaxy)
coff.append(sigmaxsquarey)
B=np.array(coff)
X=np.linalg.inv(A).dot(B)
x1=round(X[0],2)
x2=round(X[1],2)
x3=round(X[2],2)
final_eq=f'{x1}+{x2}*x+{x3}*x**2'
print()
print(final_eq)
x=eval(input('Enter value of x to find the value of y:- '))
print(round(eval(final_eq),2))


import numpy as np

def cov_shrink(X):
    '''
    Estimate covariance of given data using shrinkage estimator.

    Synopsis:
        C= cov_shrink(X)
    Argument:
        X: data matrix (features x samples)
    Output:
        C: estimated covariance matrix
    '''
    Xc = X - np.mean(X, axis=1, keepdims=True)
    d, n = Xc.shape
    Cemp= Xc.dot(Xc.T)/(n-1)

    sumVarCij = 0
    for ii in range(d):
        for jj in range(d):
            varCij = np.var(Xc[ii,:]*Xc[jj,:])
            sumVarCij += varCij

    nu = np.mean(np.diag(Cemp))
    gamma = n/(n-1.)**2 * sumVarCij / sum(sum((Cemp-nu*np.eye(d,d))**2))
    S= gamma*nu*np.eye(d,d) + (1-gamma)*Cemp
    return S

def train_LDAshrink(X, y):
    '''
    Synopsis:
        w, b= train_LDAshrink(X, y)
    Arguments:
        X: data matrix (features X samples)
        y: labels with values 0 and 1 (1 x samples)
    Output:
        w: LDA weight vector
        b: bias term
    '''
    mu1 = np.mean(X[:, y==0], axis=1)
    mu2 = np.mean(X[:, y==1], axis=1)
    # pool centered features to estimate covariance on samples of both classes at once
    Xpool = np.concatenate((X[:, y==0]-mu1[:,np.newaxis], X[:, y==1]-mu2[:,np.newaxis]), axis=1)
    C = cov_shrink(Xpool)
    w = np.linalg.pinv(C).dot(mu2-mu1)
    b = w.T.dot((mu1 + mu2) / 2.)
    return w, b


from klusterpy.metric.distance import euclidean_dist

import numpy as np
import math, random

class KMeans:
    def __init__(self, n_clusters=2, init='random', max_iter=300, tolerance=0.0001):
        self.n_clusters = n_clusters
        self.init = init
        if isinstance(init, np.ndarray):
            self.centroids = init
        self.max_iter = max_iter
        self.tolerance = tolerance

    def fit(self, data):
        if isinstance(self.init, str):
            if self.init == 'k-means++':
                self.__init_kmeans_plus2(data)
            elif self.init == 'random':
                self.__init_random(data)
            else:
                raise ValueError('Init must be one of ["k-means++", "random"] or an ndarray')
        self.labels = [None] * len(data)

        iteration_count = 1
        self.__assignment_clustering(data)
        error = self.__square_error(data)
        while iteration_count <= self.max_iter:
            iteration_count += 1
            self.__recalculate_centroids(data)
            self.__assignment_clustering(data)
            new_error = self.__square_error(data)
            if math.fabs(error - new_error) <= self.tolerance:
                error = new_error
                break
            error = new_error
        self.n_iter = iteration_count
        self.error = error
        return self

    def predict(self, samples):
        predictions = []
        for sample in samples:
            predictions.append(self.__assign_to_cluster(sample))
        return predictions

    def fit_predict(self, data):
        self.fit(data)
        return self.labels

    def __init_random(self, data):
        self.centroids = random.sample(range(len(data)), self.n_clusters)
        return self

    def __init_kmeans_plus2(self, data):
        # not implemented yet
        raise NotImplementedError('__init_kmeans_plus2')
        return self

    def __assignment_clustering(self, data):
        n_rows = len(data)
        self.labels = [self.__assign_to_cluster(data[i]) for i in range(n_rows)]
        self.clusters = [[j for j, x in enumerate(self.labels) if x == i] for i in range(self.n_clusters)]
        return self

    def __assign_to_cluster(self, row):
        min_c = 0
        min_dist = euclidean_dist(row, self.centroids[min_c])
        for c in range(min_c+1, self.n_clusters):
            dist = euclidean_dist(row, self.centroids[c])
            if dist < min_dist:
                min_dist, min_c = dist, c
        return min_c

    def __recalculate_centroids(self, data):
        for c, indexes in enumerate(self.clusters):
            sum_point = [0] * len(data[len(data) - 1])
            for index in indexes:
                for i, col in enumerate(data[index]):
                    sum_point[i] += col
            c_size = len(indexes)
            self.centroids[c] = [x/c_size for x in sum_point]
        return self

    def __square_error(self, data):
        square_error = 0
        for i, label in enumerate(self.labels):
            square_error += euclidean_dist(data[i], self.centroids[label])
        return square_error


# Hermite interpolation

# Calculate hermite's interpolation polynomial from x, f(x) and f'(x) lists
# n**5 operations

from lagrange import *
from scipy import misc

def hermite_polynomial(xcoords, ycoords, dycoords):
    P = []
    n = len(xcoords)
    for i in range(n):
        Li = lagrange_polynomial_ith(xcoords, i)
        dLi = polynomial_derivative(Li)
        dLi_xi = polynomial_image(dLi, xcoords[i])
        hi = multiply_polynomials(add_polynomials([1], multiply_polynomial([2 * xcoords[i], -2], dLi_xi)),
                                  multiply_polynomials(Li, Li))
        ki = multiply_polynomials([-xcoords[i], 1], multiply_polynomials(Li, Li))
        P = add_polynomials(P,
                            add_polynomials(multiply_polynomial(hi, ycoords[i]), multiply_polynomial(ki, dycoords[i])))
    return P

def hermite_graph(xcoords, ycoords, dycoords):
    P = hermite_polynomial(xcoords, ycoords, dycoords)
    a, b = min(xcoords), max(xcoords)
    coordsx = np.linspace(a, b, 1000)
    coordsy = [polynomial_image(P, x) for x in coordsx]
    plt.plot(coordsx, coordsy, "r-")

def hermite_graph_function(xcoords, f):
    ycoords = [f(x) for x in xcoords]
    dycoords = [misc.derivative(f, x) for x in xcoords]
    hermite_graph(xcoords, ycoords, dycoords)

def hermite_tchebychev(f, a, b, n):
    n += 1
    racines = [(a + b) / 2 + (a - b) / 2 * np.cos((2 * k + 1) * (np.pi) / (2 * n)) for k in range(0, n)]
    ycoords = [f(x) for x in racines]
    dycoords = [misc.derivative(f, x) for x in racines]
    L = hermite_polynomial(racines, ycoords, dycoords)
    return L

def hermite_graph_tchebychev(f, a, b, n):
    L = hermite_tchebychev(f, a, b, n)
    coordsx = np.linspace(a, b, 1000)
    coords = [f(x) for x in coordsx]
    coordsy = [polynomial_image(L, x) for x in coordsx]
    plt.clf()
    plt.plot(coordsx, coords, "r-", linewidth=2, label="function to interpolate")
    plt.plot(coordsx, coordsy, "b-", label="tchebychev's n degree interpolating polynomial")
    plt.legend(loc="best")
    plt.show()

def random_vs_tchebychev_h(f, a, b, n):
    res = []
    LT = hermite_tchebychev(f, a, b, n)
    for k in range(0, n + 1):
        res += [np.random.uniform(a, b)]
    ycoords = [f(x) for x in res]
    dycoords = [misc.derivative(f, x) for x in res]
    L = hermite_polynomial(res, ycoords, dycoords)
    coordsx = np.linspace(a, b, 1000)
    coords = [f(x) for x in coordsx]
    coordsy = [polynomial_image(L, x) for x in coordsx]
    coordsy2 = [polynomial_image(LT, x) for x in coordsx]
    plt.clf()
    plt.plot(coordsx, coords, "b-", label="function to interpolate")
    plt.plot(coordsx, coordsy, "r--", label="hermite's interpolating polynomial")
    plt.plot(coordsx, coordsy2, "g--", label="hermite's tchebychev interpolating polynomial")
    plt.legend(loc="best")
    plt.show()

def tchebychev_integral_h(f, a, b, n):
    L = hermite_tchebychev(f, a, b, n)
    return polynomial_integral(L, a, b)


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

def get_spin_result(win_prob):
	result = False
	if np.random.random() <= win_prob:
		result = True
	return result

def exp1_gambling_simulator():
	"""
    @summary: Simulate the martingale roulette strategy for unlimited funds
    @return winnings: net profit or loss after each bet
	@return total_winnings: total winnings at the end of the experiment
    """

	target_win = 80.0
	total_winnings = 0.0
	init_bet = 1.0
	max_bets = 1000
	bet_num = 0
	overall_win = 1

	winnings = np.full(1000,80)
	while total_winnings < target_win and bet_num < max_bets:
		won = False
		bet_amount = init_bet
		while not won:
			# wager bet_amount on black means set win_prob = 0.5
			win_prob = 0.4737 # set appropriately to the probability of a win
			won = get_spin_result(win_prob) # test the roulette spin
			if won:
				total_winnings += bet_amount
			else:
				total_winnings -= bet_amount
				bet_amount *= 2

			winnings[bet_num] = total_winnings
			bet_num += 1

	if total_winnings < target_win: overall_win = 0

	return winnings, overall_win

def make_bet(bank, proposed_bet):
	"""
    @summary: Make a bet based on a limited bank allocation
    @param bank: limited bank allocation
	@param proposed_bet: bet for the forthcoming roulette spin to check against the bank
	@return final_bet: updated bet based on cash available in bank
	@return bank: available bank after bet has been made
    """

	if proposed_bet > bank:
		final_bet = bank
		bank = 0
	else:
		final_bet = proposed_bet
		bank -= proposed_bet

	return final_bet, bank

def exp2_gambling_simulator():
	"""
    @summary: Simulate the martingale roulette strategy for limited funds
    @return winnings: net profit or loss after each bet
	@return total_winnings: total winnings at the end of the experiment
    """

	init_bank = 256.0
	bank = init_bank
	target_win = 80.0
	total_winnings = 0.0
	init_bet = 1.0
	max_bets = 1000
	bet_num = 0
	overall_win = 1

	winnings = np.full(1000,80)
	while total_winnings < target_win and bet_num < max_bets and bank > 0:
		won = False
		bet_amount, bank = make_bet(bank, init_bet)
		while not won and bet_num < max_bets:
			# wager bet_amount on black means set win_prob = 0.5
			win_prob = 0.4737 # set appropriately to the probability of a win
			won = get_spin_result(win_prob) # test the roulette spin
			if won:
				total_winnings += bet_amount
				bank += bet_amount
			else:
				total_winnings -= bet_amount
				bank -= bet_amount

				bet_amount, bank = make_bet(bank, bet_amount * 2)

			winnings[bet_num] = total_winnings
			bet_num += 1

	if bank == 0:
		winnings[bet_num:] = -init_bank
		if total_winnings < target_win: overall_win = 0

	return winnings, overall_win

#############################################
############# HELPER FUNCTIONS ##############
#############################################

def plot_data(df, title='Gambling Simulator'):
	'''Plot Gambling Simulations'''
	SMALL_SIZE = 10
	MEDIUM_SIZE = 12
	LARGE_SIZE = 15

	ax = df.plot(title=title, fontsize=LARGE_SIZE)
	ax.set_xlabel('Bets (in #)',fontsize=MEDIUM_SIZE)
	ax.set_ylabel('Winnings (in $)',fontsize=MEDIUM_SIZE)
	ax.set_xlim([0,300])
	ax.set_ylim([-256,100])
	plt.xticks(fontsize=SMALL_SIZE)
	plt.yticks(fontsize=SMALL_SIZE)
	ax.legend(loc="best")
	plt.savefig(title+'.png',dpi=500)

def run_figure1():
	labels = ['Run-1','Run-2','Run-3','Run-4','Run-5','Run-6','Run-7','Run-8','Run-9','Run-10']
	runs = pd.DataFrame(data=np.full((1000,10),80),columns=labels)
	for run in labels:
		runs[run],_ = exp1_gambling_simulator()
	plot_data(runs,'Experiment 1 - Figure 1')

def run_figure2():
	runs = pd.DataFrame(data=np.full((1000,1000),80))
	total_wins = 0

	for run in range(1000):
		runs[run], current_wins = exp1_gambling_simulator()
		total_wins += current_wins

	mean = runs.mean(axis=1)
	std = runs.std(axis=1)
	upper_bound = mean + std
	lower_bound = mean - std

	data = {'Mean': mean, 'Upper Bound': upper_bound, 'Lower Bound': lower_bound}
	df = pd.DataFrame(data=data)
	plot_data(df,'Experiment 1 - Figure 2')

def run_figure3():
	runs = pd.DataFrame(data=np.full((1000,1000),80))
	for run in range(1000):
		runs[run], _ = exp1_gambling_simulator()

	median = runs.median(axis=1)
	std = runs.std(axis=1)
	upper_bound = median + std
	lower_bound = median - std

	data = {'Median': median, 'Upper Bound': upper_bound, 'Lower Bound': lower_bound}
	df = pd.DataFrame(data=data)
	plot_data(df,'Experiment 1 - Figure 3')

def run_figure4():
	runs = pd.DataFrame(data=np.full((1000,1000),80))
	total_wins = 0

	for run in range(1000):
		runs[run], current_win = exp2_gambling_simulator()
		total_wins += current_win

	mean = runs.mean(axis=1)
	std = runs.std(axis=1)

	upper_bound = mean + std
	lower_bound = mean - std
	data = {'Mean': mean, 'Upper Bound': upper_bound, 'Lower Bound': lower_bound}
	df = pd.DataFrame(data=data)
	plot_data(df,'Experiment 2 - Figure 4')

def run_figure5():
	runs = pd.DataFrame(data=np.full((1000,1000),80))
	for run in range(1000):
		runs[run], _ = exp2_gambling_simulator()

	median = runs.median(axis=1)
	std = runs.std(axis=1)
	upper_bound = median + std
	lower_bound = median - std
	data = {'Median': median, 'Upper Bound': upper_bound, 'Lower Bound': lower_bound}
	df = pd.DataFrame(data=data)
	plot_data(df,'Experiment 2 - Figure 5')

def run_exp1():
	run_figure1()
	run_figure2()
	run_figure3()

def run_exp2():
	run_figure4()
	run_figure5()

def test_code():
	run_exp1()
	run_exp2()

if __name__ == "__main__":
    test_code()


import numpy as np
from numpy import ndarray

eps = np.finfo(float).eps

def sigmoid(x):
    """
    Element-wise sigmoid function

    Parameters
    ----------
    x: np.array
        a numpy array of any shape

    Returns
    -------
    np.array
        an array having the same shape of x.
    """

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

def loss(y_true: ndarray, y_pred: ndarray):
    """
    The binary crossentropy loss.

    Parameters
    ----------
    y_true: np.array
        real labels in {0, 1}. shape=(n_examples,)
    y_pred: np.array
        predicted labels in [0, 1]. shape=(n_examples,)

    Returns
    -------
    float
        the value of the binary crossentropy.
    """

    n_el = y_true.size
    side_1 = - y_true * np.log(y_pred)
    side_2 = (1 - y_true) * np.log(1 - y_pred)
    val = (side_1 - side_2) / n_el
    return - np.mean(val)

def dloss_dw(y_true: ndarray, y_pred: ndarray, X: ndarray):
    """
    Derivative of loss function w.r.t. weights.

    Parameters
    ----------
    y_true: np.array
        real labels in {0, 1}. shape=(n_examples,)
    y_pred: np.array
        predicted labels in [0, 1]. shape=(n_examples,)
    X: np.array
        predicted data. shape=(n_examples, n_features)

    Returns
    -------
    np.array
        derivative of loss function w.r.t weights.
        Has shape=(n_features,)
    """

    n_el = y_true.shape[0]
    internal = y_true - y_pred
    der = np.dot(X.T, internal)
    return - der / n_el

class LogisticRegression:
    """ Models a logistic regression classifier. """

    def __init__(self):
        """ Constructor method """

        # weights placeholder
        self._w = None

    def fit_gd(self, X: ndarray, Y: ndarray, n_epochs, learning_rate, verbose=False):
        """
        Implements the gradient descent training procedure.

        Parameters
        ----------
        X: np.array
            data. shape=(n_examples, n_features)
        Y: np.array
            labels. shape=(n_examples,)
        n_epochs: int
            number of gradient updates.
        learning_rate: float
            step towards the descent.
        verbose: bool
            whether or not to print the value of cost function.
        """
        n_examples, n_features = X.shape
        self._w = np.random.rand(n_features)

        for e in range(0, n_epochs):
            product = X.dot(self._w)
            predict = sigmoid(product)

            cost = loss(Y, predict)
            if (verbose):
               print("Loss at epoch {}: {}".format(e + 1, cost))
            gradient = dloss_dw(Y, predict, X)
            self._w = self._w - learning_rate * gradient

    def predict(self, X: ndarray):
        """
        Function that predicts.

        Parameters
        ----------
        X: np.array
            data to be predicted. shape=(n_test_examples, n_features)

        Returns
        -------
        prediction: np.array
            prediction in {0, 1}.
            Shape is (n_test_examples,)
        """

        product = X.dot(self._w)
        predict = sigmoid(product)
        predict[predict > 0.5] = 1
        predict[predict <= 0.5] = 0
        return predict


import numpy as np

def project(W, X, mu=None):
	if mu is None:
		return np.dot(X,W)
	return np.dot(X - mu, W)

def reconstruct(W, Y, mu=None):
	if mu is None:
		return np.dot(Y,W.T)
	return np.dot(Y, W.T) + mu

def pca(X, y, num_components=0):
	[n,d] = X.shape
	if (num_components <= 0) or (num_components>n):
		num_components = n
	mu = X.mean(axis=0)
	X = X - mu
	if n>d:
		C = np.dot(X.T,X)
		[eigenvalues,eigenvectors] = np.linalg.eigh(C)
	else:
		C = np.dot(X,X.T)
		[eigenvalues,eigenvectors] = np.linalg.eigh(C)
		eigenvectors = np.dot(X.T,eigenvectors)
		for i in xrange(n):
			eigenvectors[:,i] = eigenvectors[:,i]/np.linalg.norm(eigenvectors[:,i])
	# or simply perform an economy size decomposition
	# eigenvectors, eigenvalues, variance = np.linalg.svd(X.T, full_matrices=False)
	# sort eigenvectors descending by their eigenvalue
	idx = np.argsort(-eigenvalues)
	eigenvalues = eigenvalues[idx]
	eigenvectors = eigenvectors[:,idx]
	# select only num_components
	eigenvalues = eigenvalues[0:num_components].copy()
	eigenvectors = eigenvectors[:,0:num_components].copy()
	return [eigenvalues, eigenvectors, mu]

def lda(X, y, num_components=0):
	y = np.asarray(y)
	[n,d] = X.shape
	c = np.unique(y)
	if (num_components <= 0) or (num_component>(len(c)-1)):
		num_components = (len(c)-1)
	meanTotal = X.mean(axis=0)
	Sw = np.zeros((d, d), dtype=np.float32)
	Sb = np.zeros((d, d), dtype=np.float32)
	for i in c:
		Xi = X[np.where(y==i)[0],:]
		meanClass = Xi.mean(axis=0)
		Sw = Sw + np.dot((Xi-meanClass).T, (Xi-meanClass))
		Sb = Sb + n * np.dot((meanClass - meanTotal).T, (meanClass - meanTotal))
	eigenvalues, eigenvectors = np.linalg.eig(np.linalg.inv(Sw)*Sb)
	idx = np.argsort(-eigenvalues.real)
	eigenvalues, eigenvectors = eigenvalues[idx], eigenvectors[:,idx]
	eigenvalues = np.array(eigenvalues[0:num_components].real, dtype=np.float32, copy=True)
	eigenvectors = np.array(eigenvectors[0:,0:num_components].real, dtype=np.float32, copy=True)
	return [eigenvalues, eigenvectors]

def fisherfaces(X,y,num_components=0):
	y = np.asarray(y)
	[n,d] = X.shape
	c = len(np.unique(y))
	[eigenvalues_pca, eigenvectors_pca, mu_pca] = pca(X, y, (n-c))
	[eigenvalues_lda, eigenvectors_lda] = lda(project(eigenvectors_pca, X, mu_pca), y, num_components)
	eigenvectors = np.dot(eigenvectors_pca,eigenvectors_lda)
	return [eigenvalues_lda, eigenvectors, mu_pca]


import logging

import numpy as np
import cv2

from Calc2D.rFourier import realFourier, realInverseFourier

def GenerateGaussianData(sigma, size, points, A=1):
    xr = np.linspace(-size / 2.0, size / 2.0, points)
    yr = np.linspace(-size / 2.0, size / 2.0, points)
    step = xr[1] - xr[0]
    x, y = np.meshgrid(
        xr, yr, indexing='ij', sparse=True)  # indexing is important
    del xr, yr

    #use the more easy formula
    Value = A * np.exp(-(x**2 + y**2) / (2 * sigma**2))

    kx, ky, FValue = realFourier(step, Value)
    kxr, kyr = np.meshgrid(kx, ky, indexing='ij', sparse=True)

    k = np.sqrt(kxr**2 + kyr**2)
    del kxr, kyr

    kx = (min(kx), max(kx))  #just return the extremal values to save memory
    ky = (min(ky), max(ky))

    ValueE = (Value.min(), Value.max())

    return ValueE, FValue, k, kx, ky

def GenerateSIData(A, size, points, limit=None, ns=0.96):
    xr = np.linspace(-size / 2.0, size / 2.0, points)
    yr = np.linspace(-size / 2.0, size / 2.0, points)
    step = xr[1] - xr[0]

    x, y = np.meshgrid(
        xr, yr, indexing='ij', sparse=True)  # indexing is important
    del xr, yr
    Value = 0 * x + 0 * y

    kx, ky, FValue = realFourier(step, Value)  #FValue==0

    kxr, kyr = np.meshgrid(kx, ky, indexing='ij', sparse=True)

    k = np.sqrt(kxr**2 + kyr**2)
    del kxr, kyr

    if limit == None:

        ktilde = k.flatten()
        ktilde[np.argmin(k)] = 10**9  #just let the background be arbitrary low
        ktilde = ktilde.reshape(k.shape)

        FValue = np.random.normal(
            loc=0,
            scale=np.sqrt(A / ktilde**(
                2 - (ns - 1) * 2. / 3.)) / np.sqrt(2)) + np.random.normal(
                    loc=0,
                    scale=np.sqrt(A / ktilde**
                                  (2 - (ns - 1) * 2. / 3.)) / np.sqrt(2)) * 1j

    elif type(limit) == list or type(limit) == tuple:

        iunder, junder = np.where(k < limit[1])

        for t in range(len(iunder)):

            if k[iunder[t]][junder[t]] > limit[0] and k[iunder[t]][junder[t]] > 0:

                FValue[iunder[t]][junder[t]] = np.random.normal(
                    loc=0,
                    scale=np.sqrt(A / k[iunder[t]][junder[t]]**
                                  (2 - (ns - 1) * 2. / 3.)) /
                    np.sqrt(2)) + np.random.normal(
                        loc=0,
                        scale=np.sqrt(A / k[iunder[t]][junder[t]]**
                                      (2 -
                                       (ns - 1) * 2. / 3.)) / np.sqrt(2)) * 1j

    else:
        raise ValueError("limit must be None or tuple or list")

    Value = realInverseFourier(FValue)

    kx = (min(kx), max(kx))
    ky = (min(ky), max(ky))

    ValueE = (Value.min(), Value.max())

    return ValueE, FValue, k, kx, ky


from matplotlib.pyplot import plot, grid, axis
from numpy import cos, sin, pi, linspace, diag, sqrt, tile, array, sort, argsort, diagflat, maximum
from numpy.linalg import eig

import numpy as np
np.seterr(invalid='ignore')

def PlotTwoDimEllipsoid(mu, sigma2, r=1, PlotAxes=False, PlotTangBox=False, color='k', linewidth=2, PlotEll=True,
                        n_points=1000, fig=None, ax=None):
    '''This def creates and plots the two dimensional ellipsoid:
    (x - mu).T * (sigma2**(-1)) * (x - mu) = r**2
     INPUTS
      mu          : [vector] (2 x 1)
      sigma2      : [matrix] (2 x 2) symmetric and positive definite matrix
      r           : [scalar] radius of the ellipsoid
      PlotAxes    : [boolean] if true then the principal axes are plotted
      PlotTangBox : [boolean] if true then the tangent box is plotted.
      color       : [char] color of the line defining the ellipsoid
      linewidth   : [scalar] width of the line defining the ellipsoid
      PlotEll     : [boolean] if true then the ellipsoid is plotted
      n_points    : [scalar] number of points of the ellipsoid
     OUTPUTS
      ell_handle  : [figure handle] ellipsoid
      ell_points  : [matrix] (2 x n_points) points of the ellipsoid
      ax1, ax2    : [figure handle] principal axes
'''

    # For details on the exercise, see here: https://www.arpm.co/lab/redirect.php?permalink=EllipsFunction
    ## Code
    theta = linspace(0, 2 * pi, n_points)

    # compute the initial sphere
    y = [r * cos(theta), r * sin(theta)]

    # principal axes
    y_axes1 = array([[-r, r], [0, 0]])
    y_axes2 = array([[0, 0], [-r, r]])

    # spectral decomposition of sigma2
    Diag_lambda2, e = eig(sigma2)
    lambda2, order = sort(Diag_lambda2), argsort(Diag_lambda2)
    e = e[:, order]
    Diag_lambda = diagflat(sqrt(maximum(lambda2,0)))

    # compute the ellipsoid as affine transformation of the sphere
    u = e@Diag_lambda@y
    u_axes1 = e@Diag_lambda@y_axes1
    u_axes2 = e@Diag_lambda@y_axes2
    ell_points = tile(mu, (1, n_points)) + u

    # if fig is None and ax is None:
    #     fig = figure()
    #     ax = fig.add_subplot()
    # elif ax is None:
    #     ax = gca()
    # plot the ellipsoid
    if PlotEll:
        ell_handle = plot(ell_points[0], ell_points[1], lw=linewidth, color=color)
        grid(True)
    else:
        ell_handle = None

    # plot the tangent box
    if PlotTangBox:
        sigvec = sqrt(diag(sigma2))

        tangBox_low = [[mu[0] - r * sigvec[0],  mu[0] + r * sigvec[0]], [mu[1] - r * sigvec[1], mu[1] - r * sigvec[1]]]
        tangBox_up = [[mu[0] - r * sigvec[0],  mu[0] + r * sigvec[0]], [mu[1] + r * sigvec[1], mu[1] + r * sigvec[1]]]
        tangBox_left = [[mu[0] - r * sigvec[0], mu[0] - r * sigvec[0]], [mu[1] - r * sigvec[1],  mu[1] + r * sigvec[1]]]
        tangBox_right = [[mu[0] + r * sigvec[0], mu[0] + r * sigvec[0]], [mu[1] - r * sigvec[1],  mu[1] + r * sigvec[1]]]

        h1 = plot(tangBox_low[0], tangBox_low[1], color=color, lw=linewidth)
        h2 = plot(tangBox_up[0], tangBox_up[1], color=color, lw=linewidth)
        h3 = plot(tangBox_left[0], tangBox_left[1], color=color, lw=linewidth)
        h4 = plot(tangBox_right[0], tangBox_right[1], color=color, lw=linewidth)

    # plot the principal axes
    if PlotAxes:
        ax1 = plot(u_axes1[0] + mu[0], u_axes1[1] + mu[1], color=color, lw=linewidth)
        ax2 = plot(u_axes2[0] + mu[0], u_axes2[1] + mu[1], color=color, lw=linewidth)
        axis('equal')
    else:
        ax1 = None
        ax2 = None

    return ell_handle, ell_points, ax1, ax2


import math
import numpy

def main_func(x):
    return (math.sinh(math.sqrt(1 - x))) / (math.sqrt(1 - x))

class Trapezoidal(object):
    def __init__(self, integral_func):
        self.func = integral_func

    def integral(self, a_start, b_end, n_sub_intervals):
        # Check for some incorrect input.
        if a_start > b_end:
            raise ValueError('[A] is greater than [B]')
        if n_sub_intervals <= 0:
            raise ValueError('[n] is equal or less than 0')
        if a_start == b_end:
            return {
                'x_coord_intervals': a_start,
                'y_coord_intervals': self.func(a_start),
                'h_interval': 0.0,
                'integral': 0.0
            }

        # Array to hold X coordinates for each interval. Type: array of doubles.
        x_coord_intervals = list()
        # Array to hold Y coordinates for each interval. Type: array of doubles.
        y_coord_intervals = list()
        # Variable to hold sum of inner trapezoid values. Type: double.
        sum_inner_trapezoids = 0.0
        # Get elementary interval length. Type: double.
        h_interval = (b_end - a_start) / n_sub_intervals
        # Get exact X and Y values for start of each interval.
        for i in range(n_sub_intervals + 1):  # Loop: (i=0;i<=n;i++)
            x_coord_intervals.append(a_start + i * h_interval)
            y_coord_intervals.append(
                self.func(x_coord_intervals[i])
            )
        # Evaluate sum of elementary trapezoids, excluding starting and ending points.
        for i in range(1, n_sub_intervals):  # Loop: (i=1;i<n;i++)
            sum_inner_trapezoids += h_interval * y_coord_intervals[i]
        # Evaluate final integral value. Type: double.
        two_outer_trapezoids = (y_coord_intervals[0] + y_coord_intervals[-1]) / 2.0
        integral_value = h_interval * two_outer_trapezoids + sum_inner_trapezoids
        # Return data from whole algorithm as dictionary.
        return {
            'x_coord_intervals': x_coord_intervals,
            'y_coord_intervals': y_coord_intervals,
            'h_interval': h_interval,
            'integral': integral_value
        }

class Romberg(object):
    def __init__(self, integral_func, epsilon_pwr):
        self.func = integral_func
        self.epsilon_pwr = epsilon_pwr

    def integral(self, a_start, b_end, n_max):  # n_max: maximal levels of recursion
        r_kminus1_hi_holder = list()
        h_step_holder = list()

        main_array = numpy.array([[0] * (n_max + 1)] * (n_max + 1), float)
        h = b_end - a_start  # h0
        main_array[0, 0] = 0.5 * h * (self.func(a_start) + self.func(b_end))
        power_of_2 = 1
        i = 0

        h_step_holder.append(h)

        while True:
            i += 1

            # Update stepsize: halve it.
            # Use updated h to sum at all the new points (in between the points already computed)
            h = 0.5 * h

            sum = 0.0
            power_of_2 = 2 * power_of_2
            for k in range(1, power_of_2, 2):
                sum += self.func(a_start + k * h)

            # Compute the composite trapezoid rule for the next level of subdivision.
            # Use Richardson extrapolation to make these values more accurate.
            main_array[i, 0] = 0.5 * main_array[i - 1, 0] + sum * h

            h_step_holder.append(h)

            power_of_4 = 1
            for j in range(1, i + 1):
                power_of_4 = 4 * power_of_4
                r_kminus1_hi = (main_array[i, j - 1] - main_array[i - 1, j - 1]) / (power_of_4 - 1)
                r_kminus1_hi_holder.append(r_kminus1_hi)
                main_array[i, j] = main_array[i, j - 1] + r_kminus1_hi
                if math.fabs(r_kminus1_hi) <= math.pow(math.e, self.epsilon_pwr):
                    main_array_jsonify = main_array.tolist()
                    return {
                        'final_step': i + 1,
                        'r_kminus1_hi_values': r_kminus1_hi_holder,
                        'final_array': main_array_jsonify,
                        'h_step_holder': h_step_holder
                    }


'''
wmed computes the weighted median for data vector y and weights w, i.e.
it solves the optimization problem:

$$\beta=argmin_{b} \sum_{i} |y_{i}-b|*w_{i}$$
inputs:
   * y : (numeric) 1darray data given (real or complex)
   * w : (numeric) 1darray positive real-valued weights. Inputs need to be of
       same length
   * verbose: (logical) true of false (default). Set as true if you wish  to see convergence as iterations evolve
   * TOL: Tolerance level breaks when update is below. default =1e-7
   * iterMAX: Maximum Number of iterations for complex y. default = 2000

outputs:
   * beta: (numeric) weighted median
   * converged: (logical) flag of convergence (in the complex-valued data case)
   * iter: (numeric) the number of iterations (complex-valued case)

'''

import numpy as np
from robustsp import propform

def wmed(y,w,verbose=False, TOL = 1.0e-7, iterMAX = 2000):

    y= np.asarray(y)
    w= np.asarray(w)

    converged = 0
    itr = 0

    N = len(y)

    if not np.isrealobj(w) or all(w<0):
        raise ValueError('input w needs to be a non-negative weight vector')

    if len(w) != N or np.sum(w >=0) != N:
        raise ValueError('wmed: nr of elements of y and w are not equal or w is not non-neg.')

    # real value case
    if np.isrealobj(y):
        y = np.sort(y)
        w = w[np.argsort(y)]
        wcum = np.cumsum(w)
        i = np.nonzero(wcum[::-1]<0.5*np.sum(w))[0][0]
        beta = y[i] # due to equation (2.21) of the book
        return beta
    else: # complex valued
        beta0 = np.median(np.real(y)+1j*np.median(np.imag(y))) # initial guess
        abs0 = np.abs(beta0)

        sign = lambda x: x/np.abs(x)

        for itr in range(iterMAX):
            wy = np.abs(y-beta0)
            wy[wy<=10**-6] = 10**-6
            update = np.sum(w * sign(y-beta0))/np.sum(w/wy)
            beta = beta0 + update
            delta = np.abs(update)/abs0
            if verbose and (iter+1) % 10 == 0:
                print('At iter = %3d, delta=%.8f\n' % (iter,delta))
            if delta <= TOL:
                break
            beta0 = beta
            abs0 = np.abs(beta)

        converged = False if itr+1 == iterMAX else True

    return beta, itr+1, converged


import numpy as np
from matplotlib import pyplot as plt

def lognormal(x,mu,sigma):
    return np.exp( -(np.log(x)-mu)**2/(2*sigma**2) ) / (x*sigma*np.sqrt(2*np.pi))

def normal(x,mu,sigma):
    return np.exp( -(x-mu)**2/(2*sigma**2) ) / (sigma*np.sqrt(2*np.pi))

x = np.arange(0.01,500.01,0.01)
mu = 3.0
sigma_arr = [1.0,0.5,0.2]
color_arr = ['r','g','b']
#norm = normal(x,mu,sigma)

m = 10.

i=0
for sigma in sigma_arr:
    #v = sigma**2
    #mu = np.log(m/np.sqrt(1+v/(m**2)))
    mu = np.log(m)-sigma**2/2
    lognorm = lognormal(x,mu,sigma)
    mean = np.exp(mu+sigma**2/2)
    total = np.sum(lognorm)
    meanarr = np.sum(x*lognorm)/total
    print(mean,meanarr,total)
    if i==0: mean0=mean
    meandiff = mean-mean0
    var = (np.exp(sigma**2)-1)*np.exp(2*mu+sigma**2)

    #plt.plot(np.log(x),np.exp(norm))
    plt.loglog(x,lognorm,color_arr[i])
    plt.plot([mean0,mean0],[0.0001,np.max(lognorm)],color_arr[i],linestyle=':')
    i=i+1

plt.ylim(1.e-4,np.max(lognorm))
plt.ylabel('Probability distribution')
plt.xlabel('Mass')
plt.savefig('lognorm.png')
plt.show()


'''
Estimate Order of Model: PACF

One useful tool to identify the order of an AR model is to look at the Partial Autocorrelation Function (PACF). In this exercise, you will simulate two time series, an AR(1) and an AR(2), and calculate the sample PACF for each. You will notice that for an AR(1), the PACF should have a significant lag-1 value, and roughly zeros after that. And for an AR(2), the sample PACF should have significant lag-1 and lag-2 values, and zeros after that.

Just like you used the plot_acf function in earlier exercises, here you will use a function called plot_pacf in the statsmodels module.

INSTRUCTIONS
100XP
Import the modules for simulating data and for plotting the PACF
Simulate an AR(1) with ϕ=0.6
ϕ
=
0.6
 (remember that the sign for the AR parameter is reversed)
Plot the PACF for simulated_data_1 using the plot_pacf function
Simulate an AR(2) with ϕ1=0.6,ϕ2=0.3
ϕ
1
=
0.6
,
ϕ
2
=
0.3
 (again, reverse the signs)
Plot the PACF for simulated_data_2 using the plot_pacf function
'''
# Import the modules for simulating data and for plotting the PACF
from statsmodels.tsa.arima_process import ArmaProcess
from statsmodels.graphics.tsaplots import plot_pacf

# Simulate AR(1) with phi=+0.6
ma = np.array([1])
ar = np.array([1, -0.6])
AR_object = ArmaProcess(ar, ma)
simulated_data_1 = AR_object.generate_sample(nsample=5000)

# Plot PACF for AR(1)
plot_pacf(simulated_data_1, lags=20)
plt.show()

# Simulate AR(2) with phi1=+0.6, phi2=+0.3
ma = np.array([1])
ar = np.array([1, -0.6, -0.3])
AR_object = ArmaProcess(ar, ma)
simulated_data_2 = AR_object.generate_sample(nsample=5000)

# Plot PACF for AR(2)
plot_pacf(simulated_data_2, lags=20)
plt.show()


# encoding:utf-8
import numpy as np
from numpy.linalg import inv

# 创建矩阵
# 使用numpy创建矩阵有两种方式，一是使用matrix类，二是使用二维ndarray
# 主要区别在于默认的乘法不同，matrix类的默认乘法是矩阵乘法，ndarray默认乘法是Hadamard乘法（element-wise multiplication)
print("-- 创建矩阵 --")
A = np.matrix([[1,2,5],[3,4,6],[5,6,7]])
print("A: \n", A)

B = np.array(range(1, 7)).reshape(3,2)
print("B: \n",B)

# 矩阵乘法
print("A*A: \n",A * A) # matrix product
print("B*B \n",B * B) #element-wise

# ndarray 其它方法
zrs = np.zeros((3,2))
print("zeros: \n",zrs)

idt = np.identity(3, dtype=np.int64) # 创建单位矩阵
print(idt)
e = np.eye(3,4)
print("eye: \n", e)

# 矩阵中的向量提取
print("-- 矩阵中向量提取 --")
M = np.array(range(1,10)).reshape(3,3)
print("M: \n", M)
row1 = M[[0,2]] # 第一行和第三行，也可以使用 M[[True, False, True]]
print(row1)
col1 = M[:, [1,2]] #第二列和第三列， 或者使用M[:, [False, True, True]]
print(col1)

# 矩阵运算
print("-- 矩阵运算 --")
# 转置矩阵, 不管是Matrix类还是ndarray都可以用一以下两种方式
transposeOfMatrixObject = A.T
transpostOf2dArray = np.transpose(B)
print("Transpose: ")
print(transposeOfMatrixObject)
print(transpostOf2dArray)
# 矩阵加减法，都是对应元素想加减
print(A - M) # 矩阵减法
print(A + M) # 矩阵加法

# 矩阵乘法
print('-- 矩阵乘法 --')
print(A.dot(M))
print(M * M) # element-wise
print(M.dot(M)) # matrix produce

# 逆矩阵
# 使用numpy.linalg库提供的inv方法
print("-- 矩阵求逆 --")
print("A-inverse: \n",inv(A))
print("A * inv(A): \n", A*inv(A))