import numpy as np
import matplotlib.pyplot as plt
from pandas.io.parsers import read_csv
import scipy.optimize as opt
from  scipy.io import loadmat
from sklearn.preprocessing import PolynomialFeatures

def gradient(thetas, X, Y, lamb):
    m = np.shape(X)[0]
    H = h(thetas, X)

    grad = (1/m) * np.dot(X.T, H-Y)
    grad[1:] += (lamb/m) * np.c_[thetas[1:]] # Regularization

    return grad

def cost(thetas, X, Y, lamb):
    m = np.shape(X)[0]

    cost = ((h(thetas, X)-Y)**2).sum() / (2*m)
    cost += (lamb/(2*m)) * (thetas**2).sum() # Regularization

    return cost

def regression(thetasVector, X_poly, Y, lamb):
    thetas = np.reshape(thetasVector, (X_poly.shape[1], 1))

    c = cost(thetas, X_poly, Y, lamb)
    c = np.ravel(c)

    grad = gradient(thetas, X_poly, Y, lamb)
    grad = np.ravel(grad)

    return (c, grad)

def h(thetas, X):
    return np.dot(X, thetas)

def generate_plot(X, Y, X_toPredict, Y_predicted):
    order = np.ravel(np.argsort(X_toPredict, 0))

    X_toPredict_ordered = X_toPredict[order, :]
    Y_predicted_ordered = Y_predicted[order, :]

    plt.figure()
    plt.scatter(X, Y[:,0], c='red')
    plt.plot(X_toPredict_ordered, Y_predicted_ordered, c='blue')
    plt.savefig('images/regression_lineal_regularized_results.png')
    plt.show()
    plt.close()

def getXPoly(X, degree, normalizeValues=True, mean=np.array([]), deviation=np.array([])):
    m = np.shape(X)[0]

    poly = PolynomialFeatures(degree)
    X_poly = poly.fit_transform(X)

    if (normalizeValues):
        normalization_results = normalize(X_poly[:,1:], mean, deviation)
        X_poly = normalization_results[0]
        mean = normalization_results[1]
        deviation = normalization_results[2]

    X_poly = np.hstack([np.ones([m, 1]), X_poly])

    return X_poly, mean, deviation

def normalize(X, mean=np.array([]), deviation=np.array([])):
    if (mean.size == 0): mean = np.mean(X, 0)
    if (deviation.size == 0): deviation = np.std(X, 0)

    normalized = (X-mean)/deviation

    return (normalized, mean, deviation)

def testModel(thetas, X, Y, degree, lamb, genPlot=False, normalize_X=True, mean=np.array([]), deviation=np.array([])):
    X_poly = getXPoly(X, degree, normalize_X, mean, deviation)[0]

    # Test the model
    c = cost(thetas, X_poly, Y, lamb)
    grad = gradient(thetas, X_poly, Y, lamb)

    # Show the results
    print("Cost: ", c)
    print("Gradient: ", grad)

    if (genPlot):
        X_plot = np.arange(X.min(), X.max(), 0.05)[:, np.newaxis]
        print(X_plot)
        X_plot_poly = getXPoly(X_plot, degree, normalize_X, mean, deviation)[0]
        generate_plot(X, Y, X_plot, h(thetas, X_plot_poly))

    return (c, grad)

def crossValidation_plot(trainingGroups, group_costs, crossValidation_costs):
    plt.figure()
    plt.plot(trainingGroups, group_costs, c='red')
    plt.plot(trainingGroups, crossValidation_costs, c='blue')
    plt.savefig('images/regression_lineal_regularized_crossValidation.png')
    plt.show()
    plt.close()

def train(X, Y, degree=1, lamb=1, thetas=np.array([]), normalize_X=True, mean=np.array([]), deviation=np.array([])):
    X_poly_results = getXPoly(X, degree, normalize_X)
    X_poly = X_poly_results[0]
    if (mean.size == 0): mean = X_poly_results[1]
    if (deviation.size == 0): deviation = X_poly_results[2]

    m = np.shape(X_poly)[0]
    n = np.shape(X_poly)[1]

    if (thetas.size == 0): thetas = np.ones((n, 1), dtype=float)
    thetasVector = np.ravel(thetas)

    results = opt.minimize(fun = regression, x0 = thetasVector,
                    args = (X_poly, Y, lamb), method = 'TNC',
                    jac = True, options = {'maxiter': 20})

    thetas = np.reshape(results['x'], (X_poly.shape[1], 1))

    return thetas, X_poly, mean, deviation

def train_crossValidation(k, X, Y, X_val, Y_val, degree=1, lamb=1, normalize_X=True):
    m = np.shape(X)[0]

    X_poly_results = getXPoly(X, degree, normalize_X)
    mean = X_poly_results[1]
    deviation = X_poly_results[2]
    X_val_poly = getXPoly(X_val, degree, normalize_X, mean, deviation)[0]

    if (k >= m):
        print("Error: k should be lower than the total number of training examples.")
        return

    crossValidation_costs = []
    group_costs = []

    trainingGroups = list(range(k, m, k))
    if (trainingGroups[-1] != m): trainingGroups.append(m)

    thetas=np.array([])
    for lastIndex in trainingGroups:
        X_group = X[0:lastIndex]
        Y_group = Y[0:lastIndex]

        group_results = train(X_group, Y_group, degree, lamb, thetas,
                                normalize_X, mean, deviation)

        thetas = group_results[0]
        group_cost = cost(thetas, group_results[1], Y_group, lamb)
        validation_cost = cost(thetas, X_val_poly, Y_val, lamb)

        group_costs.append(group_cost)
        crossValidation_costs.append(validation_cost)

        startIndex = lastIndex

    crossValidation_plot(trainingGroups, group_costs, crossValidation_costs)

    return thetas, X_poly_results[0], mean, deviation

def train_lamba_selector(X, Y, X_val, Y_val, degree=1, lambs=(0, 1), normalize_X=True, showResults=True):
    X_poly_results = getXPoly(X, degree, normalize_X)
    mean = X_poly_results[1]
    deviation = X_poly_results[2]
    X_val_poly = getXPoly(X_val, degree, normalize_X, mean, deviation)[0]

    bestLambda = 0
    bestCost = -1
    for lamb in lambs:
        trainingResult = train(X, Y, degree=degree, lamb=lamb)
        currentCost = cost(trainingResult[0], X_val_poly, Y_val, lamb)

        if (bestCost == -1 or currentCost < bestCost):
            bestLambda = lamb
            bestCost = currentCost
            thetas = trainingResult[0]

    if (showResults):
        print("Best lambda|cost: ", bestLambda, "|", bestCost)

    return thetas, bestLambda, bestCost, X_poly_results[0], mean, deviation

def main():
    # DATA PREPROCESSING
    data = loadmat("../data/ex5data1.mat")
    X, Y = data['X'], data['y']
    X_val, Y_val = data['Xval'], data['yval']
    X_test, Y_test = data['Xtest'], data['ytest']

    degree = 8
    lamb = 0
    lambs = (0, 0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10)
    k = 1

    # Model training
    #trainingResult = train(X, Y, degree=degree, lamb=lamb)
    #trainingResult = train_crossValidation(k, X, Y, X_val, Y_val, degree, lamb)
    trainingResult = train_lamba_selector(X, Y, X_val, Y_val, degree, lambs)

    thetas = trainingResult[0]

    # Model testing
    testModel(thetas, X_test, Y_test, degree, lamb, True, True, trainingResult[-2], trainingResult[-1])

if __name__ == "__main__":
    main()


import numpy as np
import ast
import json
def gaussSeidel(A, b, x, norm, tol, iteMax):
    A = ast.literal_eval(A)
    b = ast.literal_eval(b)
    x = ast.literal_eval(x)
    result = {}
    iters = []
    D = np.diag(np.diag(A))
    L = (-1 * np.tril(A))+D
    U = (-1 * np.triu(A))+D
    if(0 in np.diag(A)): return {"source" : "diagonal has 0", "error" : True}
    ite = 0
    #Change here for jacobi, Sor and Gauss seidel
    T = np.dot(np.linalg.inv(D-L), U)
    C = np.dot(np.linalg.inv(D-L),b)
    #End changes
    spectRad = np.max(np.absolute(np.linalg.eigvals(T)))
    if(spectRad > 1): return {"source" : "spectral radius > 1", "error" : True}
    #Saving into result dict
    result['TMatrix'] = json.dumps(T.tolist())
    result['CMatrix'] = json.dumps(C.tolist())
    result['SpectRad'] = spectRad

    iters.append({ "iter" : ite, "E" : "n/a", "x" : x})
    #End savings
    while(norm > tol and ite < iteMax):
        xold = x
        x = np.dot(T,xold)+C
        norm = np.linalg.norm(xold-x)
        ite += 1
        #Saving into iters
        iters.append({"iter": ite, "E": float(norm), "x": x.tolist()})
        #End saving
    result['iters'] = iters
    result['error'] = False
    return result


#!/usr/bin/env python3

import numpy as np
from math import factorial
from scipy.special import comb

__all__ = [
    "count_unique_haplotypes",
    "count_unique_genotypes",
    "count_unique_genotypes",
    "count_unique_genotype_permutations",
    "count_genotype_permutations",
]

def count_unique_haplotypes(u_alleles):
    """Calculate the number of unique haplotypes that can exist at
    a locus given the number of unique alleles at each variable
    position within the haplotype interval.

    Parameters
    ----------
    u_alleles : ndarray, int, shape (n_positions, )
        Number of unique alleles at each position defined by the locus.

    Returns
    -------
    u_haplotypes : int
        Number of possible unique haplotypes.

    """
    return np.prod(u_alleles)

def count_unique_genotypes(u_haps, ploidy):
    """Calculates number of possible unique genotypes at a locus
    given the number of possible unique haplotypes at that locus
    and a ploidy.

    Parameters
    ----------
    u_haps : int
        Number of possible unique haplotypes.
    ploidy : int
        Number of haplotype copys in an individual.

    Returns
    -------
    u_genotypes : int
        Number of possible unique genotypes excluding
        equivilent permutations.

    """
    return int(comb(u_haps, ploidy, repetition=True))

def count_unique_genotype_permutations(u_haps, ploidy):
    """Calculates number of possible genotypes at a locus (including
    equivilent permutations) given the number of possible unique
    haplotypes at that locus and a ploidy.

    Parameters
    ----------
    u_haps : int
        Number of possible unique haplotypes.
    ploidy : int
        Number of haplotype copys in an individual.

    Returns
    -------
    genotype_perms : int
        Number of possible genotypes including
        equivilent permutations.

    """
    return u_haps**ploidy

def count_haplotype_universial_occurance(u_haps, ploidy):
    """Counts the number of occurances of a haplotype among all
    possible unique genotypes at a locus.

    Parameters
    ----------
    u_haps : int
        Number of possible unique haplotypes.
    ploidy : int
        Number of haplotype copys in an individual.

    Returns
    -------
    occurance : int
        Number of of time a single haplotype will occur among
        all possible genotypes in cluding equivilent permutations
        of genotypes.

    """
    return factorial(u_haps + ploidy - 1) // (factorial(ploidy - 1) * factorial(u_haps))

def count_genotype_permutations(dosage):
    """Counts the total number of equivilent genotype permutations
    for a single given genotype.

    Parameters
    ----------
    dosage : ndarray, int, shape (ploidy)
        Array with dose of each haplotype within a genotype.

    Returns
    -------
    genotype_perms : int
        Number of equivilent permutations for a
        genotype of the given dosage.

    Notes
    -----
    The sum of elements of the dosage should be equal to
    the ploidy of the genotype in question.

    """
    ploidy = sum(dosage)
    numerator = factorial(ploidy)
    denominator = 1
    for i in range(len(dosage)):
        denominator *= factorial(dosage[i])
    return numerator // denominator


import sys
import os
sys.path.append(os.path.join(sys.path[0],'..','lib'))
from stats import *
import numpy as np
import scipy.stats
import pytest

"""
Using numpy as an initial benchmark for testing
"""

def test_mean():
    s = Stats(*range(1,10))
    assert s.func_mean() == np.mean(list(range(1,10)))

def test_mean2():
    s = Stats(*range(1,1010))
    assert s.func_mean() == np.mean(list(range(1,1010)))

def test_median():
    s = Stats(*range(1,10))
    assert s.func_median() == np.median(list(range(1,10)))

def test_median2():
    s = Stats(*range(1,1010))
    assert s.func_median() == np.median(list(range(1,1010)))

def test_variance():
    s = Stats(*range(1,1010))
    assert s.func_variance() == np.var(list(range(1,1010)))

def test_variance2():
    s = Stats(*range(1,100))
    assert s.func_variance() == np.var(list(range(1,100)))

def test_std():
    s = Stats(*range(1,1010))
    assert s.func_standard_deviation() == np.std(list(range(1,1010)))

def test_std2():
    s = Stats(*range(1,100))
    assert s.func_standard_deviation() == np.std(list(range(1,100)))

def test_large():
    s = Stats(*range(1,100))
    assert s.func_large() == max(list(range(1,100)))

def test_small():
    s = Stats(*range(1,100))
    assert s.func_small() == min(list(range(1,100)))

def test_range():
    s = Stats(*range(1,100))
    assert s.func_range() == np.ptp(list(range(1,100)))

def test_mode():
    a = [1,2,34,4,2,3,21,1,23,32,2,31,2,2432,234,2]
    s = Stats(*a)
    assert s.func_mode() == scipy.stats.mode(np.array(a))[0]

def test_summation():
    n = [1,2,34,4,2,3,21,1,23,32,2,31,2,2432,234,2]
    s = Stats(*n)
    assert s.func_summation() == sum(n)

def test_double_summation():
    n = [456,456,456]
    s = Stats(*n)
    assert s.func_double_summation() == 4104

def test_kurt():
    a = [1,2,34,4,2,3,21,1,23,32,2,31,2,2432,234,2]
    s = Stats(*a)
    assert pytest.approx(s.func_kurtosis(), .1) == scipy.stats.kurtosis(np.array(a))

def test_skew():
    a = [1,2,34,4,2,3,21,1,23,32,2,31,2,2432,234,2]
    s = Stats(*a)
    assert s.func_skewness() == scipy.stats.skew(np.array(a))


import numpy as np

def euclidean_distance(a, b):
    subtracted_elements_squared = np.power(np.subtract(a, b), 2)
    elements_total = np.sum(subtracted_elements_squared)
    return np.sqrt(elements_total)

def calculate_centroids(data_objects, assignments):
    clusters = np.unique(assignments)
    centroids = np.zeros((clusters.size, data_objects.shape[1]))
    for i, cluster in enumerate(clusters):
        cluster_data_objects = data_objects[assignments == cluster]
        centroids[i] = np.divide(np.sum(cluster_data_objects, axis=0), cluster_data_objects.shape[0])
    return centroids

def assign_to_centroids(data_objects, centroids):
    assignments = np.empty((data_objects.shape[0]), dtype=int)
    for i, data_object in enumerate(data_objects):
        closest_centroid = 0
        distance_from_closest = euclidean_distance(data_object, centroids[0])
        for j, centroid in enumerate(centroids):
            distance = euclidean_distance(data_object, centroid)
            if distance < distance_from_closest:
                closest_centroid = j
                distance_from_closest = distance
        assignments[i] = closest_centroid
    return assignments

def kmeans(data_objects, k, iterations):
    centroids = np.zeros((0, np.size(data_objects, 1)))
    # Pick the original centroids from the data objects
    for i in range(k):
        centroids = np.append(centroids, [data_objects[np.random.randint(0, np.size(data_objects, 0))]], axis=0)
    # Make initial assignments
    assignments = assign_to_centroids(data_objects, centroids)
    for i in range(iterations):
        updated_centroids = calculate_centroids(data_objects, assignments)
        assignments = assign_to_centroids(data_objects, updated_centroids)
    return assignments


import numpy as np
from math import sqrt

# Problem 3: test for sum of two squares

n = int(round(float(raw_input("Enter a number to test :"))))

# brute force (ok):

swsum = False
for i in range(1,n/2+1):
    j = n-i
    if int(sqrt(i))*int(sqrt(i)) == i and  \
       int(sqrt(j))*int(sqrt(j)) == j :
        print n," is sum of two squares namely",i,"and",j
        swsum = True

if not swsum:
    print n,"is not the sum of two squares."

print "Np solution"
# Numpy solution
sq = np.arange(1,int(sqrt(n))+1)**2
swsum = False
for x in sq:
    if n-x in sq:
        print n," is sum of two squares namely",x,"and",n-x
        swsum = True

if not swsum:
    print n,"is not the sum of two squares."


'''
Created on 2009-07-27

@author: malem-cnc
'''

import math
import scipy as sp
import collections

def frange(start, stop, steps):
    listOfValues = [0.0] * steps
    numberOfValues = steps - 1
    numberOfValuesInverse = 1.0 / numberOfValues
    for i in range(steps):
        listOfValues[i] = numberOfValuesInverse * (start*(numberOfValues - i) + stop*i)
    return listOfValues

def samplesMeanStandardDeviation(samplesByLabels):

    statisticsByLabel = collections.defaultdict(list)

    for label, samples in samplesByLabels.iteritems() :

        sampleSum = 0.0

        for sample in samples :
            sampleSum += sample

        samplesCount = len(samples)

        populationMean = sampleSum/samplesCount

        squaredDistanceFromMeanSum = 0.0

        for sample in samples :
            squaredDistanceFromMeanSum += (sample - populationMean)**2

        populationStdDev = math.sqrt(squaredDistanceFromMeanSum/(samplesCount-1))

        statisticsByLabel[label] = (populationMean, populationStdDev)

    return statisticsByLabel

def normalize_against_z(x, y, z):
    maxZ = max(z)
    minZ = min(z)
    zeroG = minZ + (maxZ - minZ) / 2.0
    scaleG = maxZ - zeroG

    zeroGArray =  sp.ones_like(z) * zeroG

    normalized = []
    for axis in (x, y, z) :
        axisArray = sp.r_[axis]
        axisArray -= zeroGArray
        axisArray /= scaleG
        normalized.append(axisArray.tolist())

    return tuple(normalized)


# -*- coding: utf-8 -*-
'''Hierarchical Model for inferring the mean (mu)
and precision (tau) of normal likelihood data via MCMC.
Python (PyMC) adaptation of the R code from "Doing Bayesian Data Analysis",
by John K. Krushcke.
More info: http://doingbayesiandataanalysis.blogspot.com.br/

'''
from __future__ import division

import pymc
import numpy as np
from matplotlib import pyplot as plot
from plot_post import plot_post

# For simplicity's sake, I will generate random data just like
# the R code in the book.

t_mean = 100
t_sd = 15
N = 20

# Generate N samples, no rounding.

y = np.random.normal(t_mean, t_sd, N)

# Defining the priors for mu and tau.

mu = pymc.Normal('mu', 0.0, 1.0e-10)  # Mean: 0.0, SD: 100000
tau = pymc.Gamma('tau', 0.01, 0.01)  # Mean: 1.0, SD: 10

# Now the likelihood function.

like = pymc.Normal('like', mu, tau, value=y, observed=True)

# Create the model, generate initialization values and sample its posterior.

model = pymc.Model([like, mu, tau])
map_ = pymc.MAP(model)
map_.fit()
mcmc = pymc.MCMC(model)
mcmc.sample(iter=60000, burn=40000, thin=2)

# Sample the posterior for the parameter estimates.

mu_sample = mcmc.trace('mu')[:]
tau_sample = mcmc.trace('tau')[:]

# Keeping the same idea as the book: convert the posterior samples to SD.

sigma_sample = 1 / np.sqrt(tau_sample)

# Plot the results.

plot.figure(figsize=(8.0, 8.0))

plot.subplot(211)
plot_post(mu_sample, title=r'$\mu$ posterior distribution')

plot.subplot(212)
plot_post(sigma_sample, title=r'$\sigma$ posterior distribution')

plot.subplots_adjust(wspace=0.2, hspace=0.2)
plot.show()


# the simplest toy SVD problem
# only to track RMSE

import pickle
import numpy as np

def get_rmse(mat1, mat2):
    return np.sqrt(np.square(mat1 - mat2).mean())

class ssvvdd:
    def __init__(self, num_latent):
        self.lat_dim = num_latent
        self.u = None
        self.sv = None
        self.vh = None

    def fit(self, matrix):
        # full factorization
        self.u, self.sv, self.vh = np.linalg.svd(matrix)
        # truncate to the size of the latent vectors
        self.u = self.u[:, :self.lat_dim]
        self.sv = self.sv[:self.lat_dim]
        self.vh = self.vh[:self.lat_dim, :]

    def reconstruct(self):
        diag = np.multiply(self.sv, np.eye(self.lat_dim))
        return self.u.dot(diag).dot(self.vh)

def main():
    # load the raw data matrix
    DATA_PATH = 'data/toy_mat.pickle'
    LATENT_DIM = 10
    matrix = pickle.load(open(DATA_PATH, 'rb'))

    # build svd for matrix factorization
    new_svd = ssvvdd(LATENT_DIM)
    new_svd.fit(matrix)
    print(f"\nFinished matrix factorization.")

    # reconstruct the matrix
    rebuilt = new_svd.reconstruct()
    print("Finished matrix reconstruction.")

    # compute rmse
    rmse = get_rmse(rebuilt, matrix)
    print(f"| RMSE = {rmse:.6f} |\n")
    return

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from scipy.optimize import minimize

df = pd.read_csv('datasets/indian_liver_patient_preprocessed.csv')

X = df.loc[:, df.columns != 'Liver_disease'].values
y = df['Liver_disease'].values

X_train, X_test, y_train, y_test = train_test_split(X, y,
                                    test_size=0.3,
                                    random_state=42)

# Mathematical functions for logistic and hinge losses
def log_loss(raw_model_output):
   return np.log(1+np.exp(-raw_model_output))
def hinge_loss(raw_model_output):
   return np.maximum(0,1-raw_model_output)

# Create a grid of values and plot
grid = np.linspace(-2,2,1000)
plt.plot(grid, log_loss(grid), label='logistic')
plt.plot(grid, hinge_loss(grid), label='hinge')
plt.legend()
plt.show()

# The logistic loss, summed over training examples
def my_loss(w):
    s = 0
    for i in range(X_train.shape[0]):
        raw_model_output = w@X_train[i]
        s = s + log_loss(raw_model_output * y_train[i])
    return s

# Returns the w that makes my_loss(w) smallest
w_fit = minimize(my_loss, X_train[0]).x
print(w_fit)

# Compare with scikit-learn's LogisticRegression
lr = LogisticRegression(fit_intercept=False, C=1000000).fit(X_train,y_train)
print(lr.coef_)


'''
Created on Feb 7, 2016

@author: Wuga
'''
import numpy as np
import scipy as sp

def CrossEntropyLoss(y,z,deriv=False):
    epsilon = 1e-15
    z=sp.maximum(z,epsilon)
    z=sp.minimum(z,1-epsilon)

    if deriv:
        return y-z
    else:
        return -1.0/len(y)*np.sum((np.multiply(y,np.log(z))+np.multiply((1-y),np.log(1-z))),axis=0)

def MeanSquaredError(y,z,deriv=False):
    if deriv:
        return (y-z)*y*(1-y)
    else:
        return -1.0/len(y)*np.sum(np.multiply(y-z,y-z),axis=0)

# y=np.array([1,0,0,1])
# z=np.array([0.9,0.1,0.11,0.93])
# print (np.multiply(y,np.log(z))+np.multiply((1-y),np.log(1-z)))


# -*- coding: utf-8 -*-
"""
Created on Tue Dec 10 00:38:04 2019

@author: iqbalsublime
"""

#================================================================================================================
#----------------------------------------------------------------------------------------------------------------
#									K MEANS CLUSTERING
#----------------------------------------------------------------------------------------------------------------
#================================================================================================================

# K means clustering is applied to normalized ipl player data

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

style.use('ggplot')

class K_Means:
	def __init__(self, k =3, tolerance = 0.0001, max_iterations = 500):
		self.k = k
		self.tolerance = tolerance
		self.max_iterations = max_iterations

	def fit(self, data):

		self.centroids = {}

		#initialize the centroids, the first 'k' elements in the dataset will be our initial centroids
		for i in range(self.k):
			self.centroids[i] = data[i]

		#begin iterations
		for i in range(self.max_iterations):
			self.classes = {}
			for i in range(self.k):
				self.classes[i] = []

			#find the distance between the point and cluster; choose the nearest centroid
			for features in data:
				distances = [np.linalg.norm(features - self.centroids[centroid]) for centroid in self.centroids]
				classification = distances.index(min(distances))
				self.classes[classification].append(features)

			previous = dict(self.centroids)

			#average the cluster datapoints to re-calculate the centroids
			for classification in self.classes:
				self.centroids[classification] = np.average(self.classes[classification], axis = 0)

			isOptimal = True

			for centroid in self.centroids:

				original_centroid = previous[centroid]
				curr = self.centroids[centroid]

				if np.sum((curr - original_centroid)/original_centroid * 100.0) > self.tolerance:
					isOptimal = False

			#break out of the main loop if the results are optimal, ie. the centroids don't change their positions much(more than our tolerance)
			if isOptimal:
				break

	def pred(self, data):
		distances = [np.linalg.norm(data - self.centroids[centroid]) for centroid in self.centroids]
		classification = distances.index(min(distances))
		return classification

def main():

	df = pd.read_csv("ipl.csv")
	df = df[['one', 'two']]
	dataset = df.astype(float).values.tolist()

	X = df.values #returns a numpy array

	km = K_Means(3)
	km.fit(X)

	# Plotting starts here
	colors = 10*["r", "g", "c", "b", "k"]

	for centroid in km.centroids:
		plt.scatter(km.centroids[centroid][0], km.centroids[centroid][1], s = 130, marker = "x")

	for classification in km.classes:
		color = colors[classification]
		for features in km.classes[classification]:
			plt.scatter(features[0], features[1], color = color,s = 30)

	plt.show()

if __name__ == "__main__":
	main()


#!/usr/bin/env python3
# encoding: utf-8

"""
Forecasting accuracy measures.
"""

import numpy as np

def naive_forecast(past, t=-1, horizon=1, seasonal_period=1):
    """
    Simple forecast method which predicts the next value by taking the current
    value (i.e. value at the end of the past array) using the default parameter.
    By tuning the default parameter it is possible to use the seasonal version
    of this algorithm which calculates: pred_t+h,t = y_t+h-K where pred is the
    list of past values, t is the current time index, h is the prediction
    horizon and K is the seasonal period.
    """
    return past[t - seasonal_period + horizon]

def mase(training_set, test_set, prediction, seasonal_period=1):
    """
    Calculates the mean absolute scaled error (i.e. a measure for the accuracy
    of univariate forecast predictions). See https://www.otexts.org/fpp/2/5
    for more details.

    Args:
        training_set: Data to train the model is here used calculate the
        scaling factor for MASE as list.
        test_set: The true values to compare to the prediction as list.
        prediction: The forecasted values as list.
        seasonal_period: The seasonal period used in the naïve baseline
        forecast method.

    Returns:
       The mean scaled error of the prediction.
    """
    if seasonal_period < 1:
        raise ValueError('Seasonal period must be greater than zero.')

    if len(test_set) != len(prediction):
        raise ValueError('Prediction an test set must have the same length.')

    training_set = np.array(training_set)
    test_set = np.array(test_set)
    prediction = np.array(prediction)

    mae_naive_forecast = np.sum(np.abs(training_set[seasonal_period:] -
      training_set[:-seasonal_period])) / (len(training_set) - seasonal_period)

    forecast_errors = np.abs(test_set - prediction) / mae_naive_forecast

    return forecast_errors.mean()


import numpy as np

from src.continuous.uniform import UniformDist
from src.prob_distribution import ProbDist
from src.spaces.spaces1d_leafs import DiscreteSpace

class BernDist(ProbDist):
    """Simple bernoulli distribution."""

    def __init__(self, p = 0.5):
        """Create Ber(p) distribution.

        :param p Probability that random var is true
        """

        # save params
        assert 0 <= p <= 1
        self.p = p

        # create distribution for sampling
        self.UG = UniformDist()
        super().__init__(DiscreteSpace([0, 1]))

    def expectation(self):
        """Calculates the expectations for that distribution.

        :returns The expectation of the distribution"""

        return self.p

    def var(self):
        """Calculates the variance for that distribution.

        :returns The variance of the distribution"""

        return self.p * (1 - self.p)

    def sample(self, num_samples = 1):
        """Generate random numbers from Ber(p).

        :param num_samples How many random numbers should be generated.
        :returns Random numbers x ~ Ber(p).
        """

        # create gamma distributed vars
        U = self.UG.sample(num_samples)
        for k in range(num_samples):
            U[k] = int(U[k] <= self.p)

        return U

    def __density(self, x):
        """This method calculates the mass Ber(x|p).

        :param x Which value should be evaluated.
        :returns The probability this element occurs.
        """

        assert np.all([np.logical_or(np.equal(x, 0), np.equal(x, 1))])
        return np.power(self.p, x) * np.power(1 - self.p, np.subtract(1, x))


#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import itertools

def logistic_map_iter(r, x=0.2, iters=1):
    for _ in range(iters):
        x = r*x*(1-x)
    return x

def logistic_map_orbit(r, x=0.2, iters=100, start=0):
    orbit = []
    for _ in range(start):
        x = r*x*(1-x)
    for _ in range(iters - start):
        x = r*x*(1-x)
        orbit.append(x)
    return orbit

def logistic_map_orbit_set(r, x=0.2, iters=100, start=50):
    orbit = set()
    for _ in range(start):
        x = r*x*(1-x)
    for _ in range(iters - start):
        x = r*x*(1-x)
        orbit.add(x)
    return orbit

def plot_iters(r, x0=0.2, iters=100, start=0):
    orbit = logistic_map_orbit(r, x=x0, iters=iters, start=start)
    plt.plot(orbit, '.:')
    plt.xlabel("Iterations")
    plt.ylabel("Value")
    plt.ylim(0,1)
    plt.show()

"""
Return the attracting orbit for values of r in a given range.

rmin:  bottom of range for r
rmax:  top of range for r
x0:    starting point for iterations; should be something generic
iters: how many iterations of computation to perform
start: drop iterations before this from the orbit
res:   number of samples for r

Outputs a pair (rs, ys) of arrays
"""
def orbit_locus(rmin=0, rmax=4, x0=0.2, iters=400, start=200, res=300):
    points = []
    for r in np.linspace(rmin, rmax, res):
        orbit = logistic_map_orbit_set(r, x=x0, iters=iters, start=start)
        points += zip(itertools.repeat(r), orbit)

    return zip(*points)

def plot_locus(rs, ys):
    plt.scatter(rs, ys, marker='.', s=1)
    plt.xlabel("r")
    plt.ylabel("Orbit")
    plt.ylim(0,1)
    plt.show()

if __name__ == "__main__":
    rs, ys = orbit_locus(
            rmin = 3.5,
            rmax = 4,
            iters = 2000,
            start = 1500,
            res = 1000)
    plot_locus(rs, ys)
    #plot_iters(r=3.9, iters=50)


import keras.backend as K
import numpy as np

class LogLoss(object):

    """Logarithmic loss function for probability classifiers in Keras. Once instantiated produces
    a callable object that takes arguments y_true and y_pred (Keras tensors) to calculate the loss. Class weights
    can be used.

    Ussage example:

    # Compute some class weights if necessary, random for this example
    class_weights = np.random.randn(n_classes)

    # Instantiate the object:
    loss = LogLoss(weights=class_weights)

    # Instantiate a non-weighted loss to usee as metric, for example
    metric = LogLoss()

    # Create Keras model
    net = models.Sequential(...)

    # compile the model using the loss function and metric
    net.compile(
        optimizer='adadelta',
        loss=loss
        metrics=metric
    )

    # Fit the model
    net.fit(...)

    The loss functions can be used without instantiating the object:

    # For example
    loss = LogLoss._weighted_log_loss(weights=class_weights)

    # Or
    loss = LogLoss._log_loss
    """

    def __init__(self, weights=None):
        self.weights = weights
        self.__name__ = 'log_loss'

    # Define the log loss
    @staticmethod
    def _log_loss(y_true, y_pred):

        # Clip input to avoud numerical problems with log function
        y = K.clip(y_pred, 1e-15, 1 - 1e-15)

        # return the log loss value
        return - K.sum(y_true * K.log(y))

    # Define the weighted log loss
    @staticmethod
    def _weighted_log_loss(weights:np.ndarray):

        """Weighted logarithmic loss function
        Params:
        - weights: np.ndarray with shape (n_classes,) or (n_classes, 1)
        Returns:
        - Keras loss function with arguments (y_true, y_pred) (Keras tensors)
        """

        # Check weights type and shape
        if weights is np.array:
            if len(weights.shape) == 1:
                # Transform into (n_classes, 1) so it can be transposed
                weights = np.array([weights])
            elif len(weights.shape) > 2:
                raise ValueError('weights shape must be (n_classes,) or (n_classes, 1)')
            elif weights.shape[1] > 1:
                raise ValueError('weights shape must be (n_classes,) or (n_classes, 1)')
        else:
            weights = np.array([weights])

        # Transform weights into Keras backend variable
        weights = K.variable(value=weights)

        # Define the actual loss function
        def loss(y_true, y_pred):

            # Clip input to avoid numerical problems with log function
            y = K.clip(y_pred, 1e-15, 1 - 1e-15)

            # Return the weighted log loss value
            return - K.sum(K.dot(y_true * K.log(y), K.transpose(weights))) / K.sum(weights)

        # return the loss function (callable)
        return loss

    # Method to make thhe class callable
    def __call__(self, y_true, y_pred):
        if self.weights is None:
            return self._log_loss(y_true, y_pred)
        else:
            return self._weighted_log_loss(self.weights)(y_true, y_pred)


# -*- coding: utf-8 -*-
"""
Solution to Project Euler problem 318 - 2011 nines

Author: Jaime Liew
https://github.com/jaimeliew1/Project_Euler_Solutions

summary: (\sqrt{p} + \sqrt{q})^{2n} +(\sqrt{p} - \sqrt{q})^{2n} is an integer,
and \sqrt{p} - \sqrt{q} < 1 is a necessary condition.

According to Newton's Identities: the sum of the powers of roots of a polynomial
is an integer if the coefficients of that polynomial are integers. So all we
have to do is show that $\sqrt{p} + \sqrt{q}$ looks something like a root of a
polynomial with integer coefficients.

Let's start by defining $x = \sqrt{p} + \sqrt{q}$. Squaring both sides gives:
$x^2 = p + q + 2\sqrt{pq}$
$==> x^2 -p -q = 2\sqrt{pq}$

Squaring both sides again gives:
$(x^2 -p -q)^2 = 4pq$
$==> x^4 - 2(p+q)x^2 + (p-q)^2 = 0$

This is a fourth-order polynomial with integer coefficients. Therefore, the sum
of the powers of the roots must also be integers as mentioned before. The 4
roots of this polynomial are: $x_{root} = \pm\sqrt{p} \pm \sqrt{q}$

Great, so now we have a polynomial with integer coefficients, where one of the
roots is the quantity $\sqrt{p} + \sqrt{q}$ from the problem. Now let's consider
the sum of the powers of these roots. In fact, lets just consider the even
powers, as this is relevant to the problem: $(\sqrt{p} + \sqrt{q})^{2n} +
(\sqrt{p} - \sqrt{q})^{2n} + (\sqrt{q} - \sqrt{p})^{2n} + (-\sqrt{p} -
\sqrt{q})^{2n} = $ an integer

For integer $n$. As the exponent is even, we can see that the first and last
terms must be equal, and so are the middle two terms: $2(\sqrt{p} +
\sqrt{q})^{2n} + 2(\sqrt{p} - \sqrt{q})^{2n}  = $ an integer

Therefore, dividing everything by 2:
$(\sqrt{p} + \sqrt{q})^{2n} + (\sqrt{p} - \sqrt{q})^{2n}  = $ an integer

QED

Now, in order for the first term, $(\sqrt{p} + \sqrt{q})^{2n}$, to approach an
integer as $n --> \infty$, then the second term, $(\sqrt{p} -
\sqrt{q})^{2n}$ must approach zero as $n --> \infty$. This can only
happen when $\sqrt{p} - \sqrt{q} < 1$.

So now we just consider the term (\sqrt{p} - \sqrt{q})^(2n), which must have
2011 zeros after the decimal. This is found using logs to find exponent n.
"""
import numpy as np

def run():

    ans = 0
    for i in range(2, 2012):
        for p in range(1, i//2 + 1): # loop over all p and q where p+q < 2011, and p < q.
            q = i - p
            if p == q: continue
            if p + q - 2*np.sqrt(p*q) < 1:
                exp_min = int(-2011/np.log10(p + q - 2*np.sqrt(p*q))) + 1
                ans += exp_min

    return ans

if __name__ == "__main__":
    print(run())


import argparse
import numpy as np
import sys

def rolling_detector(numbers, percentile):
    window = []
    limit = -1
    for i in range(len(numbers)):
        n = numbers[i]
        if limit > 0 and n > limit:
            print '[anomaly] {0} {1} {2}'.format(limit, i, n)
        window.append(n)
        limit = np.percentile(window, percentile)

def fixed_detector(numbers, percentile):
    limit = np.percentile(numbers, args.perc)
    for i in range(len(numbers)):
        if numbers[i] > limit:
            print '[anomaly] {0} {1} {2}'.format(limit, i, numbers[i])

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Detects anomalies in columnar data by computing percentiles.')
    parser.add_argument('--file', '-f', dest='file', default=None)
    parser.add_argument('--column', '-c', type=int, dest='col', default=0)
    parser.add_argument('--percentile', '-p', type=int, dest='perc', default=95)
    parser.add_argument('--rolling', '-r', action='store_true', dest='rolling', default=False)
    args = parser.parse_args()

    if not args.file:
        print 'File argument is required'
        sys.exit(1)
    if args.col < 0:
        print 'Column index must not negative'
        sys.exit(1)
    if args.perc < 1 or args.perc > 99:
        print 'Percentile must be in the interval [1,99]'
        sys.exit(1)

    fp = open(args.file, 'r')
    lines = fp.readlines()
    fp.close()

    numbers = map(lambda line: int(line.strip().split()[args.col]), lines[1:])
    print '[anomaly] percentile row value'
    if args.rolling:
        rolling_detector(numbers, args.perc)
    else:
        fixed_detector(numbers, args.perc)


# -*- coding: utf-8 -*-
"""
chebpy.cheb
===========

Chebyshev polynomial.

"""

import numpy as np
from math import cos, acos

__all__ = ['cheb_polynomial_recursion',
           'cheb_polynomial_trigonometric',
           'cheb_polynomial_series']

def cheb_polynomial_recursion(k, x):
    '''
    Compute T_k(x) = cos[k*arccos(x)] via recursion method.
    The recursive formula is (for k>1)
        T_k+1(x) = 2*x*T_k(x) - T_k-1(x)
    with
        T_0 = 1; T_1 = x

    Quoted from Kopriva, DA 2009:
        For |x| > 1/2, the coefficient of the leading order polynomial
        is larger than 1. This makes the algorithm (recursion) unstable
        for large k since (rounding) errors in the lower order
        polynomials are amplified.
    Thus, it is wise to switch to the cheb_polynomial_trigonometric
    as k > Kc, where Kc is around 70.

    :param:k: the order for the chebyshev series term.
    :param:x: the variable.
    '''

    if k == 0:
        return 1
    if k == 1:
        return x

    T0 = 1
    T1 = x
    for j in xrange(2,k+1):
        Tk = 2. * x * T1 - T0
        T0 = T1
        T1 = Tk
    return Tk

def cheb_polynomial_trigonometric(k, x):
    '''
    Compute T_k(x) = cos[k*arccos(x)] directly.

    :param:k: the order for the chebyshev series term.
    :param:x: the variable.
    '''

    if k == 0:
        return 1
    if k == 1:
        return x

    return cos(k * acos(x))

def cheb_polynomial_series(k, x):
    '''
    Compute T_0, T_1, ..., T_k via recursion method.
    The recursive formula is (for k>1)
        T_k+1(x) = 2*x*T_k(x) - T_k-1(x)
    with
        T_0 = 1; T_1 = x

    :param:k: the order for the chebyshev series term.
    :param:x: the variable.
    '''

    if k == 0:
        return np.array([1])
    if k == 1:
        return np.array([1, x])

    T0 = 1
    T1 = x
    TT = [T0, T1]
    for j in xrange(2, k+1):
        Tk = 2. * x * T1 - T0
        TT.append(Tk)
        T0 = T1
        T1 = Tk
    return np.array(TT)


"""
A Nash equilibrium is a two-player strategic game – similar to the Prisoner's
dilemma – that represents a "steady state" in which every player sees the
"best possible" outcome. However, this doesn't mean that the outcome linked to
a Nash equilibrium is the best overall. Nash equilibria are more subtle than
this. An informal definition of a Nash equilibrium is as follows: an action
profile in which no individual player can improve their outcome, assuming
that all other players adhere to the profile.

It is possible to explore the notion of Nash equilibrium with the classic
game of rock-paper-scissors. The rules are as follows. Each player can choose
one of the options: rock, paper, or scissors. Rock beats scissors, but loses
to paper; paper beats rock, but loses to scissors; scissors beats paper, but
loses to rock. Any game in which both players make the same choice is a draw.
Numerically, we represent a win by +1, a loss by -1, and a draw by 0. From
this, we can construct a two-player game and compute Nash equilibria for this
game.

This module illustrates how to compute Nash equilibria for the classic game
of rock-paper-scissors.
"""
import numpy as np
import nashpy as nash

rps_p1 = np.array([
    [0, -1, 1], # rock payoff
    [1, 0, -1], # paper payoff
    [-1, 1, 0]  # scissors payoff
])

rps_p2 = rps_p1.transpose()
rps = nash.Game(rps_p1, rps_p2)
equilibria = rps.support_enumeration()

for p1, p2 in equilibria:
    print("Player 1: ", p1)
    print("Player 2: ", p2)


"""Copyright (c) 2021, Deep Net. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

from numpy.typing import ArrayLike
import numpy as np

class Activation:
    """
    Abstract activation function which should be inherited by all the activation implementations.
    """

    def map(self, Z: ArrayLike) -> ArrayLike:
        """
        Map the input tensor to an output by applying the activation for each element in the tensor.

        :param Z: The input tensor
        :returns: The output tensor with the activation applied to each element
        """
        raise NotImplementedError()

    def derivative(self) -> ArrayLike:
        """
        Get the derivative of each element as a tensor.

        :returns: The output tensor with the the derivative of the activation applied to each element
        """
        raise NotImplementedError()

class Sigmoid(Activation):
    """
    Sigmoid activation function.

    sigmoid(x) = 1 / (1 + e ** -x)
    sigmoid_derivative(x) = sigmoid(x) * (1 - sigmoid(x))
    """

    _A: ArrayLike = None

    def map(self, Z: ArrayLike) -> ArrayLike:
        self._A = 1 / (1 + np.exp(-Z))
        return self._A

    def derivative(self) -> ArrayLike:
        if self._A is None:
            raise ValueError("map should be called before derivate")
        dA = self._A * (1 - self._A)
        self._A = None
        return dA

class ReLU(Activation):
    """
    Rectified Linear Unit activation function.

    relu(x) = max(x, 0)
    relu_derivative(x) = 1 if x > 0, 0 if x < 0
    """

    _Z: ArrayLike = None

    def map(self, Z: ArrayLike) -> ArrayLike:
        self._Z = Z
        return np.maximum(self._Z, np.zeros(self._Z.shape))

    def derivative(self) -> ArrayLike:
        if self._Z is None:
            raise ValueError("map should be called before derivate")
        dA = np.where(self._Z > 0, np.ones(self._Z.shape), np.zeros(self._Z.shape))
        self._Z = None
        return dA

class LeakyReLU(Activation):
    """
    Leaky Rectified Linear Unity activation function.

    leaky_relu(x) = max(x, alpha * x)
    leaky_relu_derivative(x) = 1 if x > 0, 0.01 if x < 0
    """

    _alpha: float
    _Z: ArrayLike = None

    def __init__(self, alpha: int = 0.01):
        """
        Initialize the activation.

        :param alpha: The gradient of the function when the input is lower than zero
        """
        self._alpha = alpha

    def map(self, Z: ArrayLike) -> ArrayLike:
        self._Z = Z
        return np.maximum(self._Z, self._Z * self._alpha)

    def derivative(self) -> ArrayLike:
        if self._Z is None:
            raise ValueError("map should be called before derivate")
        dA = np.where(
            self._Z > 0, np.ones(self._Z.shape), np.full(self._Z.shape, self._alpha)
        )
        self._Z = None
        return dA

class Tanh(Activation):
    """
    Hyperbolic Tangent activation function.

    tanh(x) = (e ** x - e ** -x) / (e ** x + e ** -x)
    tanh_derivative(x) = (1 - tanh(x) ** 2)
    """

    _A: ArrayLike = None

    def map(self, Z: ArrayLike) -> ArrayLike:
        self._A = np.tanh(Z)
        return self._A

    def derivative(self) -> ArrayLike:
        if self._A is None:
            raise ValueError("map should be called before derivate")
        dA = 1 - self._A ** 2
        self._A = None
        return dA


import numpy as np
from utilities import *
from test_functions import *

def isd(f, tol=1e-8, max_iter=1000, loc=None, verbose=False, plotting=False):

    # constants for gradient descent
    alpha = 1.1
    beta = 0.5
    ds = 0.5

    xy_range = get_range(f)

    if loc:
        x0 = loc[0]
        y0 = loc[1]

    else:
        x0 = np.random.uniform(xy_range[0], xy_range[1])
        y0 = np.random.uniform(xy_range[2], xy_range[3])

    current = f(x0,y0)
    last = current

    for i in range(0,max_iter):

        constraint = True # reset our constraint each loop

        gradx = -1 * x_partial(x0,y0, f, tol)
        grady = -1 * y_partial(x0,y0, f, tol)
        grad = np.sqrt(gradx * gradx + grady * grady)

        if (np.abs(grad - 0) < tol):
            x = x0
            y = y0
            break

        coeff = ds / grad

        x = x0 + coeff * gradx
        y = y0 + coeff * grady

        current = f(x, y)

        if ( x < xy_range[0] or x > xy_range[1] or y < xy_range[2] or y > xy_range[3]):
            constraint = True
            current = last # reset value of best so far to our last best since we are out of bounds

        if np.abs(current - last) <= tol:
            break

        dx = x - x0
        dy = y - y0

        if np.abs(dx) <= tol or np.abs(dy) <= tol:
            break

        if (current > last) or (not constraint):
            ds = ds * beta

        else:
            ds = ds * alpha
            last = current
            x0 = x
            y0 = y

    if verbose:
        return current, (x,y)
    else:
        return current


# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------

"""Defines metrics for validating model explanations."""

import numpy as np

def dcg(validate_order, ground_truth_order_relevance, top_values=10):
    """Compute the discounted cumulative gain (DCG).

    Compute the DCG as the sum of relevance scores penalized by the logarithmic position of the result.
    See https://en.wikipedia.org/wiki/Discounted_cumulative_gain for reference.

    :param validate_order: The order to validate.
    :type validate_order: list
    :param ground_truth_order_relevance: The ground truth relevancy of the documents to compare to.
    :type ground_truth_order_relevance: list
    :param top_values: Specifies the top values to compute the DCG for. The default is 10.
    :type top_values: int
    """
    # retrieve relevance score for each value in validation order
    relevance = np.vectorize(lambda x: ground_truth_order_relevance.get(x, 0))(validate_order[:top_values])
    gain = 2 ** relevance - 1
    discount = np.log2(np.arange(1, len(gain) + 1) + 1)
    sum_dcg = np.sum(gain / discount)
    return sum_dcg

def ndcg(validate_order, ground_truth_order, top_values=10):
    """Compute the normalized discounted cumulative gain (NDCG).

    Compute the NDCG as the ratio of the DCG for the validation order compared to the maximum DCG
    possible for the ground truth order.
    If the validation order is the same as the ground truth the NDCG will be the maximum of 1.0,
    and the least possible NDCG is 0.0.
    See https://en.wikipedia.org/wiki/Discounted_cumulative_gain for reference.

    :param validate_order: The order to validate for the documents. The values should be unique.
    :type validate_order: list
    :param ground_truth_order: The true order of the documents. The values should be unique.
    :type ground_truth_order: list
    :param top_values: Specifies the top values to compute the NDCG for. The default is 10.
    :type top_values: int
    """
    # Create map from true_order to "relevance" or reverse order index
    ground_truth_order_relevance = {}
    num_elems = len(ground_truth_order)
    for index, value in enumerate(ground_truth_order):
        # Compute the relevancy scores as a weighted reverse order in the given list.
        # Set the range of the relevance scores to be between 0 and 10
        # This is to prevent very large values when computing 2 ** relevance - 1
        ground_truth_order_relevance[value] = ((num_elems - index) / float(num_elems)) * 10.0
    # See https://en.wikipedia.org/wiki/Discounted_cumulative_gain for reference
    dcg_p = dcg(validate_order, ground_truth_order_relevance, top_values)
    idcg_p = dcg(ground_truth_order, ground_truth_order_relevance, top_values)
    ndcg = dcg_p / idcg_p
    return ndcg


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 26 20:40:30 2018

@author: deepayanbhadra
"""

# imports
import numpy as np
import scipy as sc
import numpy.linalg as LA
from sklearn.utils import shuffle

# Generating Data: Two Classes are generated from two different Gaussian Clusters

d = 5  # dimension
m = 4096*2 # number of training samples
c = 0.5 # parameter controlling seperation between the clusters
mu1 = c*np.array([1,1,1,1,1])
mu2 = -mu1
sigma_squared_1 = 1
sigma_squared_2 = sigma_squared_1

X_train_1 = np.random.multivariate_normal(mu1,sigma_squared_1*np.identity(d),int(m/2))
X_train_2 = np.random.multivariate_normal(mu2,sigma_squared_2*np.identity(d),int(m/2))
X_train = np.concatenate((X_train_1,X_train_2),axis=0)

Y_train_1 = np.ones((int(m/2),1))
Y_train_2 = 0*np.ones((int(m/2),1))
Y_train = np.concatenate((Y_train_1,Y_train_2),axis=0)

X_train = np.column_stack((np.ones((X_train.shape[0],1)),X_train)) # adding 1 as X0

plt.scatter(X_train_1[:,0],X_train_1[:,1], color='b',marker='o')
plt.scatter(X_train_2[:,0],X_train_2[:,1], color='r',marker='o')
plt.title('Two Gaussian clusters with different labels')
plt.xlabel('x1')
plt.ylabel('x2')

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

def cross_entropy(y,y_est):
    return -y*np.log(y_est)-(1-y)*np.log(1-y_est)

def compute_cost(X,Y,theta):
    m = X.shape[0]
    loss= 0
    for i in range(m):
        loss += cross_entropy(Y[i],sigmoid(np.dot(X[i,:],theta)))
    return loss

# Stochastic Gradient Descent

def gradient_step_log_reg(theta_current, X, Y, learning_rate):

    grad = np.zeros_like(theta_current)

    idx = np.arange(X.shape[0])
    np.random.shuffle(idx)
    X = X[idx,...]
    Y = Y[idx,...]

    for i in range(0,32): # Batch size
        grad += (sigmoid(np.dot(theta_current,X[i,:]))-Y[i])*X[i,:]

    theta_current += -learning_rate*grad
    return theta_current

# initializations
theta = np.random.randn(6)
learning_rate = 1e-5
num_iterations = 1000

# shuffle the data set
X_train, Y_train = shuffle(X_train, Y_train)

# gradient descent
cost_vec = []
for t in range(num_iterations):
    theta = gradient_step_log_reg(theta, X_train, Y_train, learning_rate)
    if t%25==0:
        cost_vec.append(compute_cost(X_train, Y_train, theta))

iterations = np.linspace(0,num_iterations, num=len(cost_vec))
plt.plot(iterations,cost_vec)
plt.xlabel('iterations')
plt.ylabel('cost')

# Visualize the final seperating line

plt.scatter(X_train_1[:,0],X_train_1[:,1], color='b',marker='o')
plt.scatter(X_train_2[:,0],X_train_2[:,1], color='r',marker='o')
plt.title('Logistic regression line seperating the two training classes')
plt.plot([-4,4], (np.array([-4,4])*-theta[1]-theta[0])/theta[2],'r')
plt.xlabel('x1')
plt.ylabel('x2')

# Test Dataset

X_test_1 = np.random.multivariate_normal(mu1,sigma_squared_1*np.identity(d),int(m/2))
X_test_2 = np.random.multivariate_normal(mu2,sigma_squared_2*np.identity(d),int(m/2))
X_test = np.concatenate((X_test_1,X_test_2),axis=0)

Y_test_1 = np.ones((int(m/2),1))
Y_test_2 = 0*np.ones((int(m/2),1))
Y_test = np.concatenate((Y_test_1,Y_test_2),axis=0)

X_test = np.column_stack((np.ones((X_test.shape[0],1)),X_test)) # adding 1 as X0

plt.scatter(X_test_1[:,0],X_test_1[:,1], color='b',marker='o')
plt.scatter(X_test_2[:,0],X_test_2[:,1], color='r',marker='o')
plt.title('Two Gaussian test clusters with different labels')
plt.xlabel('x1')
plt.ylabel('x2')

# Loss on the test set

print(compute_cost(X_test, Y_test, theta))

# Visualize the final seperating line on test data

plt.scatter(X_test_1[:,0],X_test_1[:,1], color='b',marker='o')
plt.scatter(X_test_2[:,0],X_test_2[:,1], color='r',marker='o')
plt.title('Logistic regression line seperating the two test classes')
plt.plot([-4,4], (np.array([-4,4])*-theta[1]-theta[0])/theta[2],'r')
plt.xlabel('x1')
plt.ylabel('x2')


import numpy as np

if __name__ == "__main__":

    # 矩阵创建
    A = np.array([[1, 2], [3, 4]])
    print(A)

    # 矩阵的属性
    print(A.shape)
    print(A.T)

    # 获取矩阵的元素
    print(A[1, 1])
    print(A[0])
    print(A[:, 0])
    print(A[1])

    # 矩阵的基本计算
    B = np.array([[5, 6], [7, 8]])
    print(A + B)
    print(A - B)
    print(10 * A)
    print(A * B)  # 无意义
    print(A.dot(B))

    p = np.array([10, 100])
    print(A + p)

    print(A.dot(p))

    # 单位矩阵
    I = np.identity(2)
    print(I)

    # 逆矩阵
    invA = np.linalg.inv(A)
    print(invA)
    print(invA.dot(A))

    C = np.array([[1, 2, 3], [4, 5, 6]])
    # np.linalg.inv(C)  # numpy.linalg.linalg.LinAlgError: Last 2 dimensions of the array must be square


import numpy as np

def featureNormalize(X):
    '''
    归一滑特征值

    :X: 每行一个样本,每列一个特征(不需归一化偏差项系数,此时X无偏差项系数)
    '''
    X_mean = np.mean(X, axis=0)
    X_std = np.std(X, axis=0)
    X_norm = (X - X_mean) / X_std

    return X_norm, X_mean, X_std

def computeCost(X, y, theta):
    '''
    计算线性回归的损失

    :X: 每行一个样本,每列一个特征(第一列为全1为偏差项系数)
    :y: 样本真实连续值
    :theta: 回归系数,shape (X.shape[-1],1)
    '''
    m = len(y)
    pred = np.dot(X, theta)
    J = 1/(2*m) * np.sum(np.square(pred-y))
    return J

def gradientDescent(X, y, theta, alpha, iterations):
    '''
    梯度下降实现

    :X: 每行一个样本,每列一个特征(第一列为全1为偏差项系数)
    :y: 样本真实连续值
    :theta: 回归系数,shape (X.shape[-1],1)
    :alpha: 学习率
    :iterations: 迭代次数(梯度下降更新次数)
    '''
    m = len(y)
    J_history = np.zeros((iterations, 1))

    for i in range(iterations):
        pred = np.dot(X, theta)
        errors = pred - y
        delta = 1 / m * np.dot(np.transpose(X), errors)

        theta = theta - alpha * delta
        J_history[i] = computeCost(X, y, theta)

    return theta, J_history

Coursera Ng: Machine Learning/Linear Regression.py


import numpy as np

def poly(x, p=2):
    x = np.array(x)
    X = np.transpose(np.vstack([x**k for k in range(p+1)]))
    return np.linalg.qr(X)[0][:,1:]

def ecdf(data):
    """ Compute ECDF """
    x = np.sort(data)
    n = x.size
    y = np.arange(1, n+1) / n
    return(x,y)


from collections import defaultdict

import numpy as np

from source.week2.ex2_1 import construct_element_table, xy
from source.week2.ex2_2b import outernormal
from source.week2.ex2_3 import assembly
from source.week2.ex2_6 import construct_boundary_edges, neumann_boundary_conditions
from source.week2.ex2_7 import dirichlet_boundary_conditions, find_boundary_indices, find_neighbors

def ut(x, y):
    return 3 * x + 5 * y - 7

def q(x, y, n1, n2):
    return -(lam1 * x * n1 + lam2 * y * n2)

def neumann_boundary_conditions_case1(VX, VY, EToV, gamma1, b1):
    b = np.array(b1)
    for e, r in gamma1:
        s = (r + 1) % 3
        vertice_tuple = EToV[e]
        global_r = vertice_tuple[r]
        global_s = vertice_tuple[s]
        x_r, y_r = VX[global_r - 1], VY[global_r - 1]
        x_s, y_s = VX[global_s - 1], VY[global_s - 1]

        xc = 0.5 * (x_r + x_s)
        yc = 0.5 * (y_r + y_s)
        n1, n2 = outernormal(e, r + 1, VX, VY, EToV)
        q12 = (q(xc, yc, n1, n2) / 2) * np.sqrt((x_r - x_s) ** 2 + (y_r - y_s) ** 2)
        b[global_r - 1] -= q12
        b[global_s - 1] -= q12
    return b

def dirichlet_boundary_conditions_case1(A_old, b_old, gamma2, EToV, VX, VY):
    A = np.array(A_old)
    b = np.array(b_old)
    S_gamma = find_boundary_indices(gamma2, EToV)

    for i in S_gamma:
        x_i, y_i = VX[i - 1], VY[i - 1]
        f_i = 3 * x_i + 5 * y_i - 7

        A[i - 1, i - 1] = 1
        b[i - 1] = f_i
        neighbors = find_neighbors(i, EToV)

        for j in neighbors:
            if j < i:
                b[j - 1] -= A[j - 1, i - 1] * f_i
                A[j - 1, i - 1] = 0
            else:
                b[j - 1] -= A[i - 1, j - 1] * f_i
                A[i - 1, j - 1] = 0
    return A, b

if __name__ == '__main__':
    noelms1 = 4
    noelms2 = 3
    L1 = 7.6
    L2 = 5.9
    x0 = -2.5
    y0 = -4.8
    lam1 = 1
    lam2 = 1
    tol = 5e-2
    # Solving the partial derivative of u_t
    qx = 3
    qy = 5
    qt = defaultdict(lambda: 0)

    # Building VX, VY and EToV based on the input information above. These % codes are explained in previous exercises. [VX, VY] = xy(x0, y0, L1, L2, noelms1, noelms2);
    VX, VY = xy(x0, y0, L1, L2, noelms1, noelms2)
    EToV, M = construct_element_table(noelms1, noelms2)
    A, b = assembly(VX, VY, EToV, lam1, lam2, qt, M)

    gamma2, gamma1 = construct_boundary_edges(VX, VY, EToV, tol=0.0005, x0=x0, y0=y0, L1=L1, L2=L2)
    b_nc = neumann_boundary_conditions_case1(VX, VY, EToV, gamma1, b)

    # Computing solution for Dirichlet boundary condition
    f = ut(VX, VY)
    # A_old, b_old, lower_edge, etov, VX, VY, exercise, test_case
    A_b, b_b = dirichlet_boundary_conditions_case1(A, b_nc, gamma2, EToV, VX, VY)
    u = np.linalg.solve(A_b, b_b)
    compare = np.stack([u, f])
    correct = np.isclose(u, f)

    print()
    # % Calculation the final solution and reshape it to 2-D array format
    # u = A_b \ b_b;
    # U = reshape(u,noelms2+1,noelms1+1);
    # disp("Solution in 2-D"); U


'''
# NUMPY based manually training. Later will make it completely with torch

import numpy as np

X = np.array([1,2,3,4], dtype= np.float32)
Y = np.array([2,4,6,8], dtype= np.float32)

w = 0.0

# model pred : Liner regression
def forward(x):
    return w * x

# loss : MSE
def loss(y, y_pred):
    return ((y_pred-y)**2).mean()

# compute gradients
# dJ/dw = 1/N * 2x * (wx - y)
def gradient(x, y, y_pred):
    return np.dot(2*x, y_pred-y ).mean()

print("Prediction before training: f(5) = ", forward(5))

# Training
lr = 0.01
n_iters = 10

for epoch in range(n_iters):
    # forward pass
    y_pred = forward(X)

    # loss
    l = loss(Y, y_pred)

    # gradient
    dw = gradient(X, Y, y_pred)

    # update weights
    w -= lr * dw

    if epoch % 1 == 0:
        print("epoch: ", epoch+1,  "w = ", w, "loss = ", l)

print("Prediction after training: f(5) = ", forward(5))

'''

'''
# TORCH based training 1
import torch

X = torch.tensor([1,2,3,4], dtype= torch.float32)
Y = torch.tensor([2,4,6,8], dtype= torch.float32)

w = torch.tensor(0.0, dtype = torch.float32, requires_grad = True)

# model pred : Liner regression
def forward(x):
    return w * x

# loss : MSE
def loss(y, y_pred):
    return ((y_pred-y)**2).mean()

# Not required now
# # compute gradients
# # dJ/dw = 1/N * 2x * (wx - y)
# def gradient(x, y, y_pred):
#     return np.dot(2*x, y_pred-y ).mean()

print("Prediction before training: f(5) = ", forward(5))

# Training
lr = 0.01
n_iters = 100

for epoch in range(n_iters):
    # forward pass
    y_pred = forward(X)

    # loss
    l = loss(Y, y_pred)

    # gradient = backward pass
    l.backward()    # Note: this will compute the gradients automatically

    # update weights
    with torch.no_grad():
        w -= lr * w.grad

    # zero grad. As by default gardients will automatically accumulate
    w.grad.zero_()

    if epoch % 10 == 0:
        print("epoch: ", epoch+1,  "w = ", w, "loss = ", l)

print("Prediction after training: f(5) = ", forward(5))
'''


import numpy as _np
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.preprocessing import PolynomialFeatures, MinMaxScaler
from sklearn.metrics import mean_absolute_error
from matplotlib import pyplot as plt
from ipywidgets import interact

def interact_polyreg(max_degree, x, y, regularized=False, verbose=True):
    """
    The function to plot polynomial linear regression.

    Args:
        max_degree: int
            Max polynomial degree.
        x,y: numpy.ndarray
            1D Training data.
        regularized: bool
            Whether to add l2-norm regularization term in loss. Default to False.
        verbose: bool
            Whether to print trained weights. Default to True.
    """
    x_plot = _np.linspace(x.min(), x.max(), 30).reshape(-1,1)

    def polyreg_helper(degree):
        plt.figure(figsize=(10,6))
        plt.scatter(x, y, c='r', label='true')
        linear = make_pipeline(PolynomialFeatures(degree, include_bias=False),
                               MinMaxScaler(),
                               LinearRegression(fit_intercept=True))
        linear.fit(x.reshape(-1,1), y)
        mae_linear = mean_absolute_error(y, linear.predict(x.reshape(-1,1)))

        if regularized:
            ridge = make_pipeline(PolynomialFeatures(degree, include_bias=False),
                                  MinMaxScaler(),
                                  Ridge(alpha=1.0))
            ridge.fit(x.reshape(-1,1), y)
            mae_ridge = mean_absolute_error(y, ridge.predict(x.reshape(-1,1)))
            plt.plot(x_plot, linear.predict(x_plot), label='predicted, w/o regularization')
            plt.plot(x_plot, ridge.predict(x_plot), label='predicted, with regularization')
            plt.title(f"Poly degree = {degree:2}, MAE_no_reg = {mae_linear:.3f}, MAE_reg = {mae_ridge:.3f}", fontsize=16)
            if verbose:
                print('weights without regularization')
                print(linear.named_steps['linearregression'].coef_)
                print('weights with regularization')
                print(ridge.named_steps['ridge'].coef_)
        else:
            plt.plot(x_plot, linear.predict(x_plot), label='predicted')
            plt.title(f"Polynomial degree = {degree:2}, MAE = {mae_linear:.3f}", fontsize=16)
        plt.legend(fontsize=16)
        plt.show()

    interact(polyreg_helper, degree=(1, max_degree))


""" Orbital Mechanics for Engineering Students Problem 1.5
Question:
Given the particle
    P = sin(3t) i + cos(t) j + sin(2t) k
find the velocity, acceleration, angles that the velocity and acceleration
vectors make with the inertial frame, and the location of the centre of
curvature of the path.
Written by: J.X.J. Bannwarth
"""
from numpy import array, dot, cross, sqrt, sin, cos, arccos, pi
from numpy.linalg import norm

# Function
def PrintVec(v, name, unit = '', inertial=True):
    if inertial:
        print(f"{name} = {v[0]:.4f} I + {v[1]:.4f} J + {v[2]:.4f} K {unit}")
    else:
        print(f"{name} = {v[0]:.4f} i + {v[1]:.4f} j + {v[2]:.4f} k {unit}")

# Title
print("Orbital Mechanics for Engineering Students Problem 1.5")

# Parameters
t = 3. # s

# Position
P = array([sin(3.*t), cos(t), sin(2.*t)])

# Answers
print("(a)")
V = array([3.*cos(3.*t), -sin(t), 2.*cos(2.*t)])
PrintVec(V, "V", "m/s")

print("(b)")
v = norm(V)
print(f"v = {v:.3f} m/s")

print("(c)")
uT = V/v
PrintVec(uT, "u_t")

print("(d)")
th = arccos(V/v)*180./pi
print(f"th_x = {th[0]:.1f} deg, th_y = {th[1]:.1f} deg, th_z = {th[2]:.1f} deg")

print("(e)")
A = array([-9.*sin(3.*t), -cos(t), -4.*sin(2.*t)])
PrintVec(A, "A", "m/s^2")

print("(f)")
uB = cross(V,A)/norm(cross(V,A))
PrintVec(uB, "u_b")

print("(g)")
uN = cross(uB, uT)
PrintVec(uN, "u_n")

print("h")
a = norm(A)
ph = arccos(A/a)*180./pi
print(f"ph_x = {ph[0]:.1f} deg, ph_y = {ph[1]:.1f} deg, ph_z = {ph[2]:.1f} deg")

print("(i)")
aT = dot(A, uT)
print(f"a_t = {aT:.3f} m/s^2")

print("(j)")
aN = dot(A, uN)
print(f"a_n = {aN:.3f} m/s^2")

print("(k)")
rho = v**2/aN
print(f"rho = {rho:.3f} m/s^2")

print("(l)")
RCP = rho*uN
C = P + RCP
PrintVec(C, "C", "m")


'''
Intro to numpy and arrays:

np.attribute calls numpy functions

np.array turns your list into an array
np.zeros(x, y, z...) produces an array full of zeros, Dimensions x*y*z*...
np.zeros(x, y, z...) produces an array full of zeros, Dimensions x*y*z*...

Write a simple python program that generates the following arrays:
An array consisting of 25 zeros
An array consisting of 8 ones
An array consisting of numbers from 0 to 24 in order
An array consisting of 9 entries of your choice
An two-dimensional array TD consisting of 6 rows of arrays with 5 entries
A transposed version of the two-dimensional array TD
Write furthermore an algorithm to print out the sum, maximum value, minimum value and
shape of all created arrays.
'''

import numpy as np
import random

# For loop to get sum and maximum value of each array generated
def search_array_return_sum_max(array):
    sum = 0
    max = 0
    for i in array:
        sum += i
        if max < i:
            max = i
    print(f'Array produced:\n{array}')
    print(f'Its maximum value is {max}')
    print(f"Sum of array produced is: {sum}")

# loops (for) over a matrix to give sum of entries and max value
def search_2d_array_return_sum_max(array):
    sum = 0
    max = 0
    for i in array:
        for j in i:
            sum += j
            if max < j:
                max = j
    print(f'Array produced:\n{array}')
    print(f'Its maximum value is {max}')
    print(f"Sum of array produced is: {sum}")

# Inputs numbers to the empty User-generated array defined later in array_choice(arguments),
# rejects if invalid and appends to the User-generated array in valid
def reject_invalid_inputs_floats(userchoice, array):
    while True:
        try:
            # Change to int or string or whatever as needed
            Inp = float(input(userchoice))
            # Here is space for you to execute once you have a valid input
            array.append(Inp)
            break
        except ValueError:
            print("Please input a number:\n") # Or whatever a valid entry would be
            continue

# Lets user input entries into their array
def array_choice(prompt1, prompt2, prompt3, prompt4,prompt5,prompt6,prompt7,prompt8,prompt9):
    UserArray = []
    reject_invalid_inputs_floats(prompt1, UserArray)
    reject_invalid_inputs_floats(prompt2, UserArray)
    reject_invalid_inputs_floats(prompt3, UserArray)
    reject_invalid_inputs_floats(prompt4, UserArray)
    reject_invalid_inputs_floats(prompt5, UserArray)
    reject_invalid_inputs_floats(prompt6, UserArray)
    reject_invalid_inputs_floats(prompt7, UserArray)
    reject_invalid_inputs_floats(prompt8, UserArray)
    reject_invalid_inputs_floats(prompt9, UserArray)

    print(f"User-defined Array:")
    search_array_return_sum_max(UserArray)
    print(f'Its minimum value is {np.array(UserArray).min()}')
    return
# Generate and print an array of 25 zeros
array1 = np.zeros(25)
print(f'\nFirst Array:')
search_array_return_sum_max(array1)
print(f'Its minimum value is {array1.min()}\n')

# Generate and print an array of 8 ones
array2= np.ones(8)
print(f'Second array:')
search_array_return_sum_max(array2)
print(f'Its minimum value is {array2.min()}\n')

# Generate and print an array of numbers 0-24
array3=np.array(range(0,25))
print(f"Third Array:")
search_array_return_sum_max(array3)
print(f'Its minimum value is {array3.min()}\n')

# Generate an array using 9 user choices
array_choice("Please enter the 1st number of your array:\n", "Please enter the 2nd number of your array:\n", "Please enter the 3rd number of your array:\n", "Please enter the 4th number of your array:\n", "Please enter the 5th number of your array:\n", "Please enter the 6th number of your array:\n", "Please enter the 7th number of your array:\n", "Please enter the 8th number of your array:\n", "Please enter the 9th and final number of your array:\n")

# Generate the 2d array 5x6
array4 = np.random.randint(1,100,(6,5))
print(f'Fourth array:')
search_2d_array_return_sum_max(array4)
print(f'Its minimum value is {array4.min()}')

# Transpose and print it
array5 = np.transpose(array4)
print(f"\nFifth array is a transposition (matrix) of the fourth array:")
print(f'Fifth array produced:\n{array5}')


import numpy as np

def he_normal(weight_shape):
    """
    Initialize weights according `He normal` distribution. With mean = 0, std = sqrt(2 / num_input)
    """
    if len(weight_shape) == 4:
        fW, fH, fC, _ = weight_shape
        return np.random.normal(0, np.sqrt(2 / (fW*fH*fC)), weight_shape)
    num_input, _ = weight_shape
    return np.random.normal(0, np.sqrt(2 / num_input), weight_shape)

def he_uniform(weight_shape):
    """
    Initialize weights according `He uniform` distribution within the range [-limit, limit].
                With limit = sqrt(6 / num_input)
    """
    if len(weight_shape) == 4:
        fW, fH, fC, _ = weight_shape
        return np.random.uniform(-np.sqrt(6 / (fW*fH*fC)), np.sqrt(6 / (fW*fH*fC)), weight_shape)
    num_input, _ = weight_shape
    return np.random.uniform(-np.sqrt(6 / num_input), np.sqrt(6 / num_input), weight_shape)

def xavier_normal(weight_shape):
    """
    Initialize weights according `Xavier normal` distribution. With mean = 0, std = sqrt(2 / (num_input + num_output))
    """
    if len(weight_shape) == 4:
        fW, fH, fC, num_fitls = weight_shape
        return np.random.normal(0, np.sqrt(2 / (fW*fH*fC + num_fitls)), weight_shape)
    num_input, num_output = weight_shape
    return np.random.normal(0, np.sqrt(2 / (num_input + num_output)), weight_shape)

def xavier_uniform(weight_shape):
    """
    Initialize weights according `Xavier uniform` distribution within the range [-limit, limit].
                With limit = sqrt(6 / (num_input + num_output))
    """
    if len(weight_shape) == 4:
        fW, fH, fC, num_fitls = weight_shape
        return np.random.uniform(-np.sqrt(6 / (fW*fH*fC + num_fitls)), np.sqrt(6 / (fW*fH*fC + num_fitls)), weight_shape)
    num_input, num_output = weight_shape
    return np.random.uniform(-np.sqrt(6 / (num_input + num_output)), np.sqrt(6 / (num_input + num_output)), weight_shape)

def standard_normal(weight_shape):
    """
    Initialize weights according standard normal distribution with mean 0 variance 1.
    """
    return np.random.normal(size=weight_shape)


import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')

import numpy as np
from matplotlib import pyplot as plt
from sklearn import linear_model

housing = np.load('housingprices.npy')
challenger = np.load('challenger.npy')

def raw():
    plt.plot(housing[:,1], housing[:,0], 'o')
    plt.savefig("california.pdf")
    plt.clf()

def linear():
    X=np.ones((42,2))
    X[:,1]=housing[:,1]
    Y = housing[:,0]
    betahat = np.linalg.inv(X.T.dot(X)).dot(X.T.dot(Y))
    plt.plot(X[:,1],Y,'o')
    xseq=np.arange(0,12,.1)
    plt.plot(xseq,betahat[0]+betahat[1]*xseq)
    plt.savefig("cali-linear.pdf")
    plt.clf()

def cubic():
    X=np.ones((42,4))
    X[:,1]=housing[:,1]
    X[:,2]=X[:,1]**2
    X[:,3]=X[:,1]**3
    Y = housing[:,0]

    betahat = np.linalg.inv(X.T.dot(X)).dot(X.T.dot(Y))
    plt.plot(X[:,1],Y,'o')
    xseq=np.arange(0,12,.1)
    plt.plot(xseq,betahat[0]+betahat[1]*xseq+betahat[2]*xseq**2+betahat[3]*xseq**3)
    plt.savefig("cali-quadratic.pdf")
    plt.clf()

def quartic():
    X=np.ones((42,5))
    X[:,1]=housing[:,1]
    X[:,2]=X[:,1]**2
    X[:,3]=X[:,1]**3
    X[:,4]=X[:,1]**4
    Y=housing[:,0]

    betahat = np.linalg.inv(X.T.dot(X)).dot(X.T.dot(Y))
    plt.plot(X[:,1],Y,'o')
    xseq=np.arange(0,12,.1)
    plt.plot(xseq,betahat[0]+betahat[1]*xseq
            +betahat[2]*xseq**2+betahat[3]*xseq**3+betahat[4]*xseq**4)
    plt.ylim([0,600000])
    plt.savefig("cali-quartic.pdf")

def challenger_cubic():
    plt.plot(challenger[:,0], challenger[:,1], 'o')
    plt.xlim(30,100)
    plt.xlabel('Ambient Temperature (F)')
    plt.ylim(-0.5,1.5)
    plt.ylabel('O-ring Damage Present')
    plt.title('Potential for Shuttle Damage - With Cubic Approximation')
    X=np.ones((challenger.shape[0],4))
    X[:,1] = challenger[:,0]
    Y=challenger[:,1]
    X[:,2]=X[:,1]**2
    X[:,3]=X[:,1]**3
    betahat = np.linalg.inv(X.T.dot(X)).dot(X.T.dot(Y))
    xseq=np.arange(30,100,.5)
    plt.plot(xseq,betahat[0]+betahat[1]*xseq+betahat[2]*xseq**2+betahat[3]*xseq**3)
    plt.savefig('cubicthrulogitpoints.pdf')
    plt.clf()

def challenger_logistic():
    ###Logreg plot #2
    plt.plot(challenger[:,0], challenger[:,1],'o')
    plt.xlim(30,100)
    plt.xlabel('Ambient Temperature (F)')
    plt.ylim(-0.5,1.5)
    plt.ylabel('O-ring Damage Present')
    plt.title('Potential for Shuttle Damage - With Logistic Regression Prediction')
    #X=np.ones((dat.shape[0],2))
    #X[:,1]=dat[:,0]
    X=challenger[:,0].reshape((23,1))
    Y=challenger[:,1]
    logreg = linear_model.LogisticRegression(C=1000000,penalty="l2")
    logreg.fit(X,Y)
    coef=logreg.coef_[0]
    xseq=np.arange(30,100,.5)[:,np.newaxis]
    #xseqmat=np.ones((len(xseq),2))
    #xseqmat[:,1]=xseq
    xB=logreg.intercept_[0]+logreg.coef_[0][0]*xseq
    #plt.plot(xseq,1/(np.exp(-xB)+1))
    plt.plot(xseq,logreg.predict_proba(xseq)[:,1])
    plt.savefig("logreg.pdf")
    plt.clf()

if __name__ == "__main__":
    raw()
    linear()
    cubic()
    quartic()
    challenger_cubic()
    challenger_logistic()


from .test_utils import TestCase, com

from sympy import S
from galgebra.ga import Ga

class TestChapter8(TestCase):

    def test8_2_1(self):
        """
        The commutator product.
        """
        GA = Ga("e*1|2|3", g=[1, 1, 1])

        A = GA.mv('A', 'mv')
        B = GA.mv('B', 'mv')
        C = GA.mv('C', 'mv')

        self.assertEqual(com(com(A, B), C) - com(A, com(B, C)), com(B, com(C, A)))
        self.assertEqual(com(com(A, B), C) + com(com(C, A), B) + com(com(B, C), A), S.Zero)

        B = GA.mv('B', 'blade', 2)
        X = GA.mv('A', 'mv')
        self.assertEqual(B.pure_grade(), 2)
        self.assertEqual(com(X, B).pure_grade(), -2)  # Not pure

        E = com(X, B).rev()
        self.assertEqual(E, (B.rev() * X.rev() - X.rev() * B.rev()) / 2)
        self.assertEqual(E, (X.rev() * B - B * X.rev()) / 2)
        self.assertEqual(E, com(X.rev(), B))

        alpha = GA.mv('alpha', 'scalar')
        self.assertEqual(alpha * X, alpha ^ X)

        a = GA.mv('a', 'vector')
        self.assertEqual(a * X, (a < X) + (a ^ X))

        A = GA.mv('A', 'grade', 2)
        self.assertEqual(A * X, (A < X) + com(A, X) + (A ^ X))


import numpy as np

def rotate(x,y,theta):
	'''Rotates the coordinates (x,y) in anit-clockwise direction by an angle theta.
	theta is in the units of degrees.'''

	theta_rad = np.radians(theta)
	x_rot  = x*np.cos(-theta_rad) + y*np.sin(-theta_rad)
	y_rot  = -x*np.sin(-theta_rad) + y*np.cos(-theta_rad)

	return x_rot, y_rot


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

# import the necessary packages
from neuralnetwork import  NeuralNetwork
import numpy as np

# construct the XOR dataset
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([[0], [1], [1], [0]])

# define our 2-2-1 neural network and train it
nn = NeuralNetwork([2,2,1], alpha=0.5)
nn.fit(X, y, epochs=20000)

# Now that our network is trained, loop over the XOR data points
for (x, target) in zip(X, y):
    # make a prediction on the data point and display the result
    # to our console
    pred = nn.predict(x)[0][0]
    step = 1 if pred > 0.5 else 0
    print("[INFO] data={}, ground-truth={}, pred={:.4f}, step={}".format(x, target[0], pred, step))

"""
    Demonstrating that our multi-layer neural network is capable of learning nonlinear function
"""


## Imports
import numpy as np

## Gloval Variables

## Classes
# Loss Function data
class LFData:
	def __init__(self, name, c, w):
		self.name = name
		self.correct = c
		self.wrong = w
		self.diff = c / w

# Loss Function data storage
class LFDataStorage:
	current = 0
	stop = 0
	item = []

#	def __init__(self):
#		pass

	def __iter__(self):
		return self

	def __next__(self):
		if self.current < self.stop:
			r = self.current
			self.current += 1
			return self.item[r]
		else:
			raise StopIteration

	def __getitem__(self, index):
		if index < self.stop:
			return self.item[index]
		else:
			raise IndexError

	# name: loss function name
	# c: error with correct prediction
	# w: error with wrong prediction
	def insert(self, name, c, w):
		self.item.append(LFData(name, c, w))
		self.stop += 1

## Functions
'''
y: predicted values
t: actual values
'''
def mean_squared_error(y, t):
	#return (1/t.size * np.sum((y-t)**2)
	return np.mean((y-t)**2)
mse = mean_squared_error	# alias

def mean_squared_logarithmic_error(y, t):
	return np.mean((np.log(y+1)-np.log(t+1))**2)
msle = mean_squared_logarithmic_error

def mean_absolute_error(y, t):
	return np.mean(np.absolute(y-t))
mae = mean_absolute_error

def mean_absolute_percentage_error(y, t):
	return np.mean(np.absolute((y-t)/y)*100)
mape = mean_absolute_percentage_error

def cross_entropy_error(y, t):
	delta = 1e-7 # we add a very small value (0.0000007) to y in order to prevent -inf where y is 0
#	return -np.sum(t * np.log(y + delta))
	return -np.sum(t * np.log(y + delta) + (1-t)*np.log(1-y + delta))
cee = cross_entropy_error

def log_likelihood_error(y, t):
	return
lle = log_likelihood_error

def main():
	# actual values
	t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
	# predicted values (right prediction)
	y1 = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
	# predicted values (wrong prediction)
	y2 = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]

	lfs = LFDataStorage()
	lfs.insert('MSE',  mse(np.array(y1),  np.array(t)), mse(np.array(y2),  np.array(t)))
	lfs.insert('MSLE', msle(np.array(y1), np.array(t)), msle(np.array(y2), np.array(t)))
	lfs.insert('MAE',  mae(np.array(y1),  np.array(t)), mae(np.array(y2),  np.array(t)))
	lfs.insert('CEE',  cee(np.array(y1),  np.array(t)), cee(np.array(y2),  np.array(t)))

	for i in lfs:
		print(i.name +
			'\tc:{0:.5f}'.format(i.correct) +	# error with correct prediction
			'  w:{0:.5f}'.format(i.wrong) + 	# error with wrong prediction
			'  d:{0:.5f}'.format(i.diff))		# error differential

#	print(lfs[1].name);	# getitem test

if __name__ == '__main__':
	main()


import numpy

import log_reg_funcs

def gradient_function_regularized(theta, x_data, y_data, regularization_lambda=1.0):

    num_examples = y_data.shape[0]
    regularized_term = numpy.zeros(theta.shape)

    hypothesis = log_reg_funcs.sigmoid_function(numpy.dot(x_data, theta))
    hypothesis_gradient = numpy.dot((hypothesis - y_data), x_data)/num_examples

    regularized_term[1:] = (regularization_lambda/num_examples)*theta[1:]

    gradient = hypothesis_gradient + regularized_term

    return gradient


#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from typing import Tuple

a, b, c, d = 10, 25, 20, 30

def dxdt(x: float, y: float) -> float:
	return x * (a - b * y)

def dydt(x: float, y: float) -> float:
	return y * (d * x - c)

def f(x: float, y: float) -> Tuple[float, float]:
	return dxdt(x, y), dydt(x, y)

x_s, y_s = c / d, a / b

h = 0.01

t = np.arange(0, 3 + h, h)

def k1(x: float, y: float) -> Tuple[float, float]:
	return f(x, y)

def k2(x: float, y: float) -> Tuple[float, float]:
	k1x, k1y = k1(x, y)
	return f(x + (h / 2) * k1x, y + (h / 2) * k1y)

def k3(x: float, y: float) -> Tuple[float, float]:
	k2x, k2y = k2(x, y)
	return f(x + (h / 2) * k2x, y + (h / 2) * k2y)

def k4(x: float, y: float) -> Tuple[float, float]:
	k3x, k3y = k3(x, y)
	return f(x + h * k3x, y + h * k3y)

def rk(x: float, y: float) -> Tuple[float, float]:
	k1x, k1y = k1(x, y)
	k2x, k2y = k2(x, y)
	k3x, k3y = k3(x, y)
	k4x, k4y = k4(x, y)
	return x + (h / 6) * (k1x + 2 * k2x + 2 * k3x + k4x), \
		y + (h / 6) * (k1y + 2 * k2y + 2 * k3y + k4y)

def plot_2d(x0: float, y0: float) -> None:
	x, y = [x0], [y0]

	for _ in range(len(t) - 1):
		_x, _y = rk(x[-1], y[-1])
		x.append(_x)
		y.append(_y)

	plt.figure(figsize=(20,10))

	plt.plot(t, x, 'b-', label=f'$x(t), x(0) = {x0:.2f}$')
	plt.plot(t, y, 'r-', label=f'$y(t), y(0) = {y0:.2f}$')

	plt.title(f'Solution plots with Runge-Kutta on $[{t[0]}, {t[-1]}]$\n'
		f'$x(0) = {x0:.2f}, y(0) = {y0:.2f}$', fontsize=20)

	plt.xlabel('$t$', fontsize=16)
	plt.ylabel('$x, y$', fontsize=16)

	plt.ylim(0, 1.25)

	plt.legend()

	plt.savefig(f'../tex/plot_2d_{x0:.2f}_{y0:.2f}.png')

def plot_3d(x0: float, y0: float) -> None:
	x, y = [x0], [y0]

	for _ in range(len(t) - 1):
		_x, _y = rk(x[-1], y[-1])
		x.append(_x)
		y.append(_y)

	fig = plt.figure(figsize=(10,10))

	ax = fig.gca(projection='3d')

	ax.plot(x, y, t, 'g-', label=f'$x(0) = {x0:.2f}, y(0) = {y0:.2f}$')

	ax.plot([x_s for _ in t], [y_s for _ in t], t, 'k--',
		label=f'$x(0) = {x_s:.2f}, y(0) = {y_s:.2f}$')

	ax.set_xlim3d(0, 1.25)
	ax.set_ylim3d(0, 1.25)

	plt.title(f'Solution plot with Runge-Kutta on $[{t[0]}, {t[-1]}]$\n'
		f'$x(0) = {x0:.2f}, y(0) = {y0:.2f}$', fontsize=20)

	ax.set_xlabel('$x(t)$', fontsize=16)
	ax.set_ylabel('$y(t)$', fontsize=16)
	ax.set_zlabel('$t$', fontsize=16)

	plt.savefig(f'../tex/plot_3d_{x0:.2f}_{y0:.2f}.png')

for x, y in [(.8, .2), (.5, .5), (.4, .6)]:
	plot_2d(x, y)
	plot_3d(x, y)


#!/usr/bin/env sage

import sys
from sage.all import *

F = FiniteField(101) # Construct finite field
P = F['x'] # Polynomial ring

generate_points = 1
single_poly = 0
adding_shares_linearity = 1
adding_constant_linearity = 1
multiplying_constant_linearity = 1
multiplying_shares_nonlinearity = 1
multiplication_subprotocol = 1
beavers_triple=1

if generate_points:
    print "Creating points for the curve 5x^2 + 10x + 56 and reconstructing from shares:"
    print "=============================================================================="
    f_a = P("56 + 10x + 5x^2")
    shares_a = []
    for i in range(1,4):
        shares_a.append((i,f_a(i)))
    print "Shares of a:",shares_a
    f_a_rec = P.lagrange_polynomial(shares_a)
    print "f_a_rec(x) = ",f_a_rec
    print "Secret = f_a_rec(0) = ",f_a_rec(0)

if single_poly:
    # How to use P:
    original_polynomial = P("56 + 10x + 5x^2")
    # Convert the shares to elements of F (otherwise lagrange_polynomial will fail)
    shares = [(F(x), F(y)) for x,y in [(25, 98), (47, 57), (19, 31)]]
    # Now lagrange_polynomial works fine
    reconstructed_polynomial = P.lagrange_polynomial(shares)

    print "Correct reconstruction!" if reconstructed_polynomial == original_polynomial else "Something wrong!"
    print shares
    print "p(x) =", reconstructed_polynomial
    print "Secret = p(0) =", reconstructed_polynomial(0)

if adding_shares_linearity:
    F = FiniteField(101) # Construct finite field
    P = F['x'] # Polynomial ring

    print "Adding shares of 2 and 4:"
    print "=========================="
    f_a = P("2 + x");    f_b = P("4 + x");    f_c = P("4 + 2x")
    shares_a = [];       shares_b = [];       shares_c = []
    for i in range(1,4):
        shares_a.append((i,f_a(i)))
        shares_b.append((i,f_b(i)))
        shares_c.append((i,f_a(i)+f_b(i)))
    print "Shares_a", shares_a,"\nShares_b",shares_b,"\nShares_c",shares_c
    f_a_rec = P.lagrange_polynomial(shares_a)
    f_b_rec = P.lagrange_polynomial(shares_b)
    f_c_rec = P.lagrange_polynomial(shares_c)
    print "f_a_rec(x) = ",f_a_rec, "\tSecret = f_a_rec(0) = ",f_a_rec(0)
    print "f_b_rec(x) = ",f_b_rec, "\tSecret = f_b_rec(0) = ",f_b_rec(0)
    print "f_c_rec(x) = ",f_a_rec, "\tSecret = f_c_rec(0) = ",f_c_rec(0)

if adding_constant_linearity:
    F = FiniteField(101) # Construct finite field
    P = F['x'] # Polynomial ring

    print "Adding constant 10 to the shares of 2:"
    print "======================================="
    f_a = P("2 + x");    f_b = P("4 + x");
    shares_a = [];       shares_b = [];
    for i in range(1,4):
        shares_a.append((i,f_a(i)))
        shares_b.append((i,f_a(i)+10))
    print "Shares_a", shares_a,"\nShares_b",shares_b
    f_a_rec = P.lagrange_polynomial(shares_a)
    f_b_rec = P.lagrange_polynomial(shares_b)
    print "f_a_rec(x) = ",f_a_rec, "\tSecret = f_a_rec(0) = ",f_a_rec(0)
    print "f_b_rec(x) = ",f_b_rec, "\tSecret = f_b_rec(0) = ",f_b_rec(0)

if multiplying_constant_linearity:
    F = FiniteField(101) # Construct finite field
    P = F['x'] # Polynomial ring

    print "Multiplying constant 4 to the shares of 2:"
    print "==========================================="
    f_a = P("2 + x");    f_b = P("4 + x");
    shares_a = [];       shares_b = [];
    for i in range(1,4):
        shares_a.append((i,f_a(i)))
        shares_b.append((i,f_a(i)*4))
    print "Shares_a", shares_a,"\nShares_b",shares_b
    f_a_rec = P.lagrange_polynomial(shares_a)
    f_b_rec = P.lagrange_polynomial(shares_b)
    print "f_a_rec(x) = ",f_a_rec, "\tSecret = f_a_rec(0) = ",f_a_rec(0)
    print "f_b_rec(x) = ",f_b_rec, "\tSecret = f_b_rec(0) = ",f_b_rec(0)

if multiplying_shares_nonlinearity:
    F = FiniteField(101) # Construct finite field
    P = F['x'] # Polynomial ring

    print "Multiplying shares of 2 and 4:"
    print "==========================================="
    f_a = P("2 + x");    f_b = P("4 + x");    f_c = P("4 + 2x")
    shares_a = [];       shares_b = [];       shares_c = []
    for i in range(1,4):
        shares_a.append((i,f_a(i)))
        shares_b.append((i,f_b(i)))
        shares_c.append((i,f_a(i)*f_b(i)))
    print "Shares_a", shares_a,"\nShares_b",shares_b,"\nShares_c",shares_c
    f_a_rec = P.lagrange_polynomial(shares_a)
    f_b_rec = P.lagrange_polynomial(shares_b)
    f_c_rec = P.lagrange_polynomial(shares_c)
    print "f_a_rec(x) = ",f_a_rec, "\tSecret = f_a_rec(0) = ",f_a_rec(0)
    print "f_b_rec(x) = ",f_b_rec, "\tSecret = f_b_rec(0) = ",f_b_rec(0)
    print "f_c_rec(x) = ",f_c_rec, "\tSecret = f_c_rec(0) = ",f_c_rec(0)
    print "f_c_rec's factors are ",f_c_rec.factor()

if multiplication_subprotocol:
    F = FiniteField(5) # Construct finite field
    P = F['x'] # Polynomial ring

    print "Multiplying shares of 2 and 2 in multiplication subprotocol:"
    print "============================================================="
    f_a = P("2 + x");    f_b = P("2 +2x");    f_c = P("4 + 2x")
    shares_a = [];       shares_b = [];       shares_c = []

    f_c1 = P("2 + x");    f_c2 = P("4");        f_c3 = P("4x")
    shares_c1 = [];       shares_c2 = [];       shares_c3 = [];     shares_C = []

    rec_vector = []
    for i in range(1,4):
        num = 1; denom = 1
        for k in range(1,4):
            if i is not k:
                num = num * -k;   denom = denom * (i-k);
        tmp = num / denom
        rec_vector.append((P(i),P(tmp)))
    print "Reconstruction vector:", rec_vector

    for i in range(1,4):
        shares_a.append((P(i),f_a(i)))
        shares_b.append((P(i),f_b(i)))
        shares_c.append((P(i),f_a(i)*f_b(i)))

    print "Shares_a", shares_a,"\nShares_b",shares_b,"\nShares_c",shares_c

    f_a_rec = P.lagrange_polynomial(shares_a)
    f_b_rec = P.lagrange_polynomial(shares_b)
    f_c_rec = P.lagrange_polynomial(shares_c)
    print "f_a_rec(x) = ",f_a_rec, "\tSecret = f_a_rec(0) = ",f_a_rec(0)
    print "f_b_rec(x) = ",f_b_rec, "\tSecret = f_b_rec(0) = ",f_b_rec(0)
    print "f_c_rec(x) = ",f_c_rec, "\tSecret = f_c_rec(0) = ",f_c_rec(0)
    print "f_c_rec's factors are ",f_c_rec.factor()

    for i in range(1,4):
        shares_c1.append((P(i),f_c1(i)))
        shares_c2.append((P(i),f_c2(i)))
        shares_c3.append((P(i),f_c3(i)))
    print "Shares_c1", shares_c1,"\nShares_c2",shares_c2, "\nShares_c3",shares_c3

    (x,r1) = rec_vector[0]; (x,r2) = rec_vector[1]; (x,r3) = rec_vector[2]
    for i in range(1,4):
        #print f_c1(i),f_c2(i), f_c3(i)
        #print r1, r2, r3
        #print f_c1(i)*r1 + f_c2(i)*r2 + f_c3(i)*r3
        val = f_c1(i)*r1 + f_c2(i)*r2 + f_c3(i)*r3
        shares_C.append((P(i),val))
    print "Shares_C",shares_C

    f_C_rec = P.lagrange_polynomial(shares_C)
    print "f_C_rec(x) = ",f_C_rec, "\tSecret = f_C_rec(0) = ",f_C_rec(0)

if beavers_triple:
    F = FiniteField(101) # Construct finite field
    P = F['x'] # Polynomial ring

    print "Multiplying shares of 2 and 4 using beavers triple:"
    print "============================================================="
    f_a = P("3 + x");    f_b = P("2 +2x");    f_ab = P("6 + 3x")
    shares_a = [];       shares_b = [];       shares_ab = []

    f_x = P("2 + x");    f_y = P("4 +2x");
    shares_x = [];       shares_y = [];

    for i in range(1,4):
        shares_a.append((P(i),f_a(i)))
        shares_b.append((P(i),f_b(i)))
        shares_ab.append((P(i),f_ab(i)))
        shares_x.append((P(i),f_x(i)))
        shares_y.append((P(i),f_y(i)))

    print "Shares_a", shares_a,"\nShares_b",shares_b,"\nShares_ab",shares_ab
    print "Shares_x", shares_x,"\nShares_y",shares_y

    f_a_rec = P.lagrange_polynomial(shares_a)
    f_b_rec = P.lagrange_polynomial(shares_b)
    f_x_rec = P.lagrange_polynomial(shares_x)
    f_y_rec = P.lagrange_polynomial(shares_y)
    f_ab_rec = P.lagrange_polynomial(shares_ab)
    print "f_a_rec(x) = ",f_a_rec, "\tSecret = f_a_rec(0) = ",f_a_rec(0)
    print "f_b_rec(x) = ",f_b_rec, "\tSecret = f_b_rec(0) = ",f_b_rec(0)
    print "f_ab_rec(x) = ",f_ab_rec, "\tSecret = f_ab_rec(0) = ",f_ab_rec(0)
    print "f_x_rec(x) = ",f_x_rec, "\tSecret = f_x_rec(0) = ",f_x_rec(0)
    print "f_y_rec(x) = ",f_y_rec, "\tSecret = f_y_rec(0) = ",f_y_rec(0)

    shares_alpha_t = [];   shares_beta_t = [];
    for i in range(1,4):
        (x,xi) = shares_x[i-1]; (x,yi) = shares_y[i-1]
        (x,ai) = shares_a[i-1]; (x,bi) = shares_b[i-1]
        shares_alpha_t.append((P(i),xi-ai))
        shares_beta_t.append((P(i),yi-bi))
    print "Shares_alpha_t", shares_alpha_t,"\nShares_beta_t",shares_beta_t

    f_alpha_rec = P.lagrange_polynomial(shares_alpha_t)
    f_beta_rec = P.lagrange_polynomial(shares_beta_t)
    alpha = f_alpha_rec(0); beta = f_beta_rec(0)

    print "Alpha = ",alpha,"\tBeta = ",beta;

    shares_xy = []
    for i in range(1,4):
        (x,ab) = shares_ab[i-1]; (x,ai) = shares_a[i-1]; (x,bi) = shares_b[i-1]
        val = ab + alpha*bi + beta*ai + alpha*beta
        shares_xy.append((P(i),val))
    print "Shares_xy", shares_xy

    f_xy_rec = P.lagrange_polynomial(shares_xy)
    print "f_xy_rec(x) = ",f_xy_rec, "\tSecret = f_xy_rec(0) = ",f_xy_rec(0)


# Illustrate the three solutions of the Bertrand paradox on a disk.
# The paradox asks what is the probability of a random chord in a
# circle being greater than a side of equilateral triangle inscribed in
# the circle. Traditionally this problem has three different solutions.
# The three solutions are labelled A, B and C, which correspond to
# solutions 1, 2 and 3 in, for example, the Wikipedia article:
# https://en.wikipedia.org/wiki/Bertrand_paradox_(probability)
# The empirical estimates are also done for the probability of the chord
# length being greater than the side of the triangle.
# The results are not plotted if the number of lines is equal to or
# greater than a thousand (ie numbLines<1000 for plotting).
# Author: H. Paul Keeler, 2019.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts
# For more details, see the post:
# hpaulkeeler.com/the-bertrand-paradox/

import numpy as np;  # NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt  # for plotting
from matplotlib import collections  as mc  # for plotting line chords

plt.close('all');  # close all figures

###START Parameters START###
# Simulation disk dimensions
xx0 = 0;
yy0 = 0;  # center of disk
r = 1;  # disk radius
numbLines = 10 ** 2;  # number of lines
###END Parameters END###

###START Simulate three solutions on a disk START###
# Solution A
thetaA1 = 2 * np.pi * np.random.uniform(0, 1, numbLines);  # choose angular component uniformly
thetaA2 = 2 * np.pi * np.random.uniform(0, 1, numbLines);  # choose angular component uniformly

# calculate chord endpoints
xxA1 = xx0 + r * np.cos(thetaA1);
yyA1 = yy0 + r * np.sin(thetaA1);
xxA2 = xx0 + r * np.cos(thetaA2);
yyA2 = yy0 + r * np.sin(thetaA2);
# calculate midpoints of chords
xxA0 = (xxA1 + xxA2) / 2;
yyA0 = (yyA1 + yyA2) / 2;

# Solution B
thetaB = 2 * np.pi * np.random.uniform(0, 1, numbLines);  # choose angular component uniformly
pB = r * np.random.uniform(0, 1, numbLines);  # choose radial component uniformly
qB = np.sqrt(r ** 2 - pB ** 2);  # distance to circle edge (along line)

# calculate trig values
sin_thetaB = np.sin(thetaB);
cos_thetaB = np.cos(thetaB);

# calculate chord endpoints
xxB1 = xx0 + pB * cos_thetaB + qB * sin_thetaB;
yyB1 = yy0 + pB * sin_thetaB - qB * cos_thetaB;
xxB2 = xx0 + pB * cos_thetaB - qB * sin_thetaB;
yyB2 = yy0 + pB * sin_thetaB + qB * cos_thetaB;
# calculate midpoints of chords
xxB0 = (xxB1 + xxB2) / 2;
yyB0 = (yyB1 + yyB2) / 2;

# Solution C
# choose a point uniformly in the disk
thetaC = 2 * np.pi * np.random.uniform(0, 1, numbLines);  # choose angular component uniformly
pC = r * np.sqrt(np.random.uniform(0, 1, numbLines));  # choose radial component
qC = np.sqrt(r ** 2 - pC ** 2);  # distance to circle edge (alonge line)

# calculate trig values
sin_thetaC = np.sin(thetaC);
cos_thetaC = np.cos(thetaC);

# calculate chord endpoints
xxC1 = xx0 + pC * cos_thetaC + qC * sin_thetaC;
yyC1 = yy0 + pC * sin_thetaC - qC * cos_thetaC;
xxC2 = xx0 + pC * cos_thetaC - qC * sin_thetaC;
yyC2 = yy0 + pC * sin_thetaC + qC * cos_thetaC;
# calculate midpoints of chords
xxC0 = (xxC1 + xxC2) / 2;
yyC0 = (yyC1 + yyC2) / 2;
###END Simulate three solutions on a disk END###

###START Do some statistics on chord lengths START###
lengthSide = r * np.sqrt(3);  # length of triangle side
# chord lengths
lengthA = np.hypot(xxA1 - xxA2, yyA1 - yyA2);  # Method A
lengthB = np.hypot(xxB1 - xxB2, yyB1 - yyB2);  # Method B
lengthC = np.hypot(xxC1 - xxC2, yyC1 - yyC2);  # Method C

# estimated probability of chord being longer than triangle side
probEstA = np.mean(lengthA > lengthSide);  # Method A
probEstB = np.mean(lengthB > lengthSide);  # Method B
probEstC = np.mean(lengthC > lengthSide);  # Method C
###END Do some statistics on chord lengths END###

# Plot if there are less than a thousand lines.
if numbLines < 1000:
    # create points for circle
    t = np.linspace(0, 2 * np.pi, 200);
    xp = r * np.cos(t);
    yp = r * np.sin(t);

    ### START Plotting START ###
    # Solution A
    # draw circle
    fig, ax = plt.subplots(1, 2);
    ax[0].plot(xx0 + xp, yy0 + yp, color='k');
    ax[0].axis('equal');
    ax[0].set_xlabel('x');
    ax[0].set_ylabel('y');
    ax[0].set_title('Chords of Solution A');
    # plot chords of Solution A
    # need to create a list to plot the chords (probably a better way to do this)
    chordsA = [[(xxA1[i], yyA1[i]), (xxA2[i], yyA2[i])] for i in range(numbLines)];
    lc = mc.LineCollection(chordsA, colors='r');
    ax[0].add_collection(lc)  # plot chords of Solution A
    # draw circle
    ax[1].plot(xx0 + xp, yy0 + yp, color='k');
    ax[1].axis('equal');
    ax[1].set_xlabel('x');
    ax[1].set_ylabel('y');
    ax[1].set_title('Midpoints of Solution A');
    # plot midpoints of Solution A
    ax[1].plot(xxA0, yyA0, 'r.', markersize=10);
    plt.show()

    # Solution B
    # draw circle
    fig, ax = plt.subplots(1, 2);
    ax[0].plot(xx0 + xp, yy0 + yp, color='k');
    ax[0].axis('equal');
    ax[0].set_xlabel('x');
    ax[0].set_ylabel('y');
    ax[0].set_title('Chords of Solution B');
    # plot chords of Solution B
    # need to create a list to plot the chords (probably a better way to do this)
    chordsB = [[(xxB1[i], yyB1[i]), (xxB2[i], yyB2[i])] for i in range(numbLines)];
    lc = mc.LineCollection(chordsB, colors='b');
    ax[0].add_collection(lc)  # plot chords of Solution B
    # draw circle
    ax[1].plot(xx0 + xp, yy0 + yp, color='k');
    ax[1].axis('equal');
    ax[1].set_xlabel('x');
    ax[1].set_ylabel('y');
    ax[1].set_title('Midpoints of Solution B');
    # plot midpoints of Solution B
    ax[1].plot(xxB0, yyB0, 'b.', markersize=10);
    plt.show()

    # Solution C
    # draw circle
    fig, ax = plt.subplots(1, 2);
    ax[0].plot(xx0 + xp, yy0 + yp, color='k');
    ax[0].axis('equal');
    ax[0].set_xlabel('x');
    ax[0].set_ylabel('y');
    ax[0].set_title('Chords of Solution C');
    # plot chords of Solution C
    # need to create a list to plot the chords (probably a better way to do this)
    chordsC = [[(xxC1[i], yyC1[i]), (xxC2[i], yyC2[i])] for i in range(numbLines)];
    lc = mc.LineCollection(chordsC, colors='g');
    ax[0].add_collection(lc)  # plot chords of Solution C
    # draw circle
    ax[1].plot(xx0 + xp, yy0 + yp, color='k');
    ax[1].axis('equal');
    ax[1].set_xlabel('x');
    ax[1].set_ylabel('y');
    ax[1].set_title('Midpoints of Solution C');
    # plot midpoints of Solution C
    ax[1].plot(xxC0, yyC0, 'g.', markersize=10);
    plt.show()
    ### END Plotting END ###