"""
Kindly install these libraries before executing this code:
  1. numpy
"""

import math
import numpy as np
import csv

def readCSV():
    csv_file = open("data.csv", "r")
    reader = csv.reader(csv_file)

    data = []
    for row in reader:
        data.append(row[4])

    data = data[1:]
    data = [float(x) for x in data]
    return data

def compute(data):
    u = []
    for idx in range(1, len(data)):
        u.append(math.log(data[idx]/data[idx - 1]))

    E = 0
    variance = 0
    mean = 0
    n = len(u)

    for i in u:
        E += i
    E /= len(u)

    for i in u:
        variance += (i - E)*(i - E)
    variance /= (n - 1)

    mean = E + variance/2
    return mean, variance

def estimate(mean, variance, data):
    s0 = data[len(data) - 1]
    predicted = [0, 0, 0]

    for i in range(0, 1000):
        si = s0

        # Generating Normal Random Variables
        Z = np.random.normal(0, 1, 14)
        idx = 0

        # Computation for dates from 1st Oct to 7th oct
        # Holidays - 2nd Oct, 3rd Oct, 4th Oct
        for j in range(0, 4):
            s = si*math.exp((mean - 0.5*variance) + math.sqrt(variance)*Z[idx])
            si = s
            idx += 1
        predicted[0] += s

        # Computation for dates from 8th Oct to 14th Oct
        # Holidays - 10th Oct, 11th Oct
        for j in range(0, 5):
            s = si*math.exp((mean - 0.5*variance) + math.sqrt(variance)*Z[idx])
            si = s
            idx += 1
        predicted[1] += s

        # Computation for dates from 15th Oct to 21st Oct
        # Holidays - 17th Oct, 18th Oct
        for j in range(0, 5):
            s = si*math.exp((mean - 0.5*variance) + math.sqrt(variance)*Z[idx])
            si = s
            idx += 1
        predicted[2] += s

    for idx in range(0, 3):
        predicted[idx] /= 1000

    return predicted

def main():

    # Reading from csv files
    data = readCSV()

    # Computing mean and variance from historical data
    mean, variance = compute(data)

    print('Mean = {}'.format(mean))
    print('Variance = {}'.format(variance))

    # Making predictions using gBm Model
    predicted = estimate(mean, variance, data)
    actual = [190.70, 200.05, 203.75]
    percentageError = []

    # Calculating Percentage Error
    for idx in range(0, 3):
        percentageError.append(((actual[idx] - predicted[idx]) * 100)/actual[idx])

    print('\n------ Predicted closing stock prices ------')
    print('\n\n### Date = 7th Oct ###\n')
    print('Predicted price = {}'.format(predicted[0]))
    print('Actual Price = {}'.format(actual[0]))
    print('Percentage error = {} %'.format(percentageError[0]))

    print('\n\n### Date = 14th Oct ###\n')
    print('Predicted price = {}'.format(predicted[1]))
    print('Actual Price = {}'.format(actual[1]))
    print('Percentage error = {} %'.format(percentageError[1]))

    print('\n\n### Date = 21st Oct ###\n')
    print('Predicted price = {}'.format(predicted[2]))
    print('Actual Price = {}'.format(actual[2]))
    print('Percentage error = {} %'.format(percentageError[2]))

if __name__=="__main__":
    main()


from sys import argv
from maths import count, mean, standard_deviation, min_, quantile, median, max_
import pandas as pd
import os
import numpy as np

def get_dataset(dataset_path):
    try:
        if os.path.exists(dataset_path) and os.path.isfile(dataset_path):
            dataframe = pd.read_csv(dataset_path)
            return dataframe
    except ValueError:
        pass
    return

def filter_dataframe(df_raw: pd.DataFrame) -> pd.DataFrame:
    df_raw = df_raw.drop(['Hogwarts House'], axis=1)
    df = df_raw.select_dtypes([np.number])
    return df

def manual_describe(df: pd.DataFrame) -> pd.DataFrame:
    output_df = pd.DataFrame(columns=[columnName for (columnName, columnData) in df.iteritems()],
                             index=['Count', 'Mean', 'Std', 'Min', '25%', '50%', '75%', 'Max'])
    for (columnName, columnData) in df.iteritems():
        if columnName in output_df.columns:
            my_values = [x for x in columnData.values[~np.isnan(columnData.values)]]
            my_values.sort()
            count_val = count(my_values)
            mean_val = mean(my_values)
            std_val = standard_deviation(my_values)
            min_val = min_(my_values)
            quant_25_val = quantile(my_values, 0.25)
            quant_50_val = median(my_values)
            quant_75_val = quantile(my_values, 0.75)
            max_val = max_(my_values)
            output_df[columnName] = [count_val, mean_val, std_val, min_val, quant_25_val,
                                     quant_50_val, quant_75_val, max_val]
    return output_df

def main():
    if len(argv) > 1:
        df_raw = get_dataset(argv[1])
    else:
        return print('Please input the path to the dataset as the first argument.')
    if df_raw is None:
        return print('Please input a valid path to the dataset.')
    df = filter_dataframe(df_raw)
    describe_df = manual_describe(df)
    print(describe_df)
    # print(df.describe())

if __name__ == "__main__":
    main()


import numpy as np

def p_dust(rho_0, b, rho_min, rho_vals):
    """Compute p_dust(rho | rho_0, b, rho_min)

    Parameters
    ----------
    rho_0 : `float`
        The value of rho_0
    b : `float`
        The value of b
    rho_min : `float`
        The value of rho_min
    rho_vals : `np.ndarray`
        Array of rho values to compute p_dust

    Returns
    -------
    p_dust : `np.ndarray`
        p_dust evaluated at rho_vals given rho0, b, rho_min.
    """
    lo = (rho_vals <= rho_min)
    hi = ~lo

    B = (b + 1)/(rho_0*((rho_min/rho_0)**b)*(b + 1 + (rho_min/rho_0)))

    p_dust = np.zeros(rho_vals.size)
    p_dust[lo] = B*((rho_vals[lo]/rho_0)**b)
    p_dust[hi] = B*((rho_min/rho_0)**b)*(np.exp(-(rho_vals[hi] - rho_min)/rho_0))

    return p_dust

def compute_normalized_rho_pars(u, b):
    """Compute normalized rho_0, rho_min given b, u=rho_min/rho_0

    Parameters
    ----------
    u : `float`
        u = rho_min/rho_0 parameter.
    b : `float`
        b parameter

    Returns
    -------
    rho_0, rho_min : (`float`, `float`)
    """
    rho_0 = ((b + 2)/(b + 1))*((u + b + 1)/(u**2. + (b + 2)*(u + 1)))
    rho_min = rho_0*u

    return rho_0, rho_min


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 11 20:02:37 2017

@author: tzave
"""

import numpy as np
import solver

A = np.array([[1., 1., 2.],
              [-1., 0., 2.],
              [3., 2., -1.]])
b = np.array([1., -3., 8.])
ret = solver.LU(A)
L = ret['L']
U = ret['U']
print('L\n', L)
print('\nU\n', U)
y = solver.ForwardSubstitution(L, b)
print('\ny=', y)
x = solver.BackSubstitution(U, y)
print('x=', x)


import numpy as np
import math

def focal_length_from_two_points(a, b, c, d):
    """
    Brief: focal length from two points
    See appendex 6.1 Focal Length from Two Points
    :param a:
    :param b:
    :param c:
    :param d:
    :return: 0 (failed) or focal lenth in pixels
    """
    c_2 = math.pow(c, 2)  # c^2
    d_2 = math.pow(d, 2)  # d^2
    t1 = 2 * (d_2 * a * b - c_2)
    t2 = math.pow(d_2 * (a + b) - 2 * c, 2) - 4 * (d_2 * a * b - c_2) * (d_2 - 1)

    f = 0
    if t2 < 0:
        print('warning: invalid focal length')
        return f
    assert t2 >= 0
    t3 = 2 * c - d_2 * (a + b) + math.sqrt(t2)

    if t3 == 0:
        print('warning: invalid focal length')
        return f
    assert t3 != 0

    f2 = t1 / t3
    if f2 > 0:
        f = math.sqrt(f2)
    else:
        print('warning: invalid focal length')

    return f

def pan_y_tilt_x(pan, tilt):
    """
    matrix from pan Y along with axis then tilt along with X axis
    m = Q_\phi * Q_\theta in equation (1)
    :param pan:
    :param tilt:
    :return:
    """
    pan = pan * math.pi / 180.0
    tilt = tilt * math.pi / 180.0
    r_tilt = np.asarray([[1, 0, 0],
                          [0, math.cos(tilt), math.sin(tilt)],
                          [0, -math.sin(tilt), math.cos(tilt)]])
    r_pan = np.asarray([[math.cos(pan), 0, -math.sin(pan)],
                        [0, 1, 0],
                        [math.sin(pan), 0, math.cos(pan)]])
    m = r_tilt * r_pan
    return m

def ptz_from_two_point(principal_point, pan_tilt1, pan_tilt2, point1, point2):
    """
    Estimate pan, tilt and zoom from two points
    See Section 3.1 Two-point Algorithm for Data Annotation
    :param principal_point: [u, v], e.g image center, 2 x 1, numpy array
    :param pan_tilt1: pan and tile of point1, unit degress
    :param pan_tilt2: same as pan_tilt1,
    :param point1: point 1 location, unit pixel, 2 x 1, numpy array
    :param point2: same as point1
    :return: None if fail, otherwise, pan_tilt_focal_length
    """
    p1 = point1 - principal_point
    p2 = point2 - principal_point

    a = np.dot(p1, p1)
    b = np.dot(p2, p2)
    c = np.dot(p1, p2)
    z = np.asarray([0, 0, 1])

    pan1, tilt1 = pan_tilt1[0], pan_tilt1[1]
    pan2, tilt2 = pan_tilt2[0], pan_tilt2[1]

    pan_tilt_z = np.matmul(pan_y_tilt_x(pan2 - pan1, tilt2 - tilt1), z)
    d = np.dot(z, pan_tilt_z)

    f = focal_length_from_two_points(a, b, c, d) # this may fail

    if f <= 0:
        print('Warning: estimate focal length failed')
        return None
    ptz = np.zeros(3)
    pi = math.pi
    theta1 = pan1 - math.atan2(p1[0], f) * 180.0 / pi
    theta2 = pan2 - math.atan2(p2[0], f) * 180.0 / pi
    ptz[0] = (theta1 + theta2) / 2.0

    phi1 = tilt1 - math.atan2(p1[1], f) * 180.0 / pi
    phi2 = tilt2 - math.atan2(p2[1], f) * 180.0 / pi
    ptz[1] = (phi1 + phi2) / 2.0
    ptz[2] = f

    return ptz

def pan_tilt_from_principle_point(pp, pp_ptz, p):
    """
    generate pan_tilt sample points using ptz ground truth
    :param pp:  pincipal point
    :param pp_ptz: pincipal point pan, tilt zoom, eg. ptz of the image
    :param p: a point in the image, unit pixel
    :return: return value, pan and tilt
    """
    dx = p[0] - pp[0]
    dy = p[1] - pp[1]
    pan_pp = pp_ptz[0]
    tilt_pp = pp_ptz[1]
    fl = pp_ptz[2]
    delta_pan = math.atan2(dx, fl) * 180 / math.pi
    delta_tilt = math.atan2(dy, fl) * 180 / math.pi
    pt = np.zeros((2, 1))
    pt[0] = pan_pp + delta_pan
    pt[1] = tilt_pp + delta_tilt

    return pt


import numpy as np
from scipy.spatial.distance import pdist, squareform
from itertools import combinations
from scipy.spatial import Delaunay
from scipy.spatial import distance
import bats

def get_Rips_filtration(pts, rmax=np.inf):
	pts = np.array(pts)
	simplices = []
	times = []
	n = len(pts)
	dists = squareform(pdist(pts))

	for i in range(n):
		simplices.append([i])
		times.append(0)

	for i in range(n):
		for j in range(i):
			if dists[i,j] <= rmax:
				simplices.append([j,i])
				times.append(dists[i,j])

	for i in range(n):
		for j in range(i):
			for k in range(j):
				tijk = np.max([dists[i,j], dists[i,k], dists[j,k]])
				if tijk <= rmax:
					simplices.append([k, j,i])
					times.append(np.max([dists[i,j], dists[i,k], dists[j,k]]))

	return simplices, times

def unique_edges(faces):
    """
    obtain unique simplices up to dimension dim from faces
    """
    edges = []
    # loop over faces
    for face in faces:
        # loop over dimension
        for s in combinations(face, 2):
            edges.append(np.sort(list(s)))

    edges = np.unique(edges, axis=0)

    return edges

def WeakAlphaFiltration(pts, dist=distance.euclidean, maxdim=2, t0=0.):
    """
    2D weak alpha filtration
    """
    tri = Delaunay(pts)
    edges = unique_edges(tri.simplices)
    fedges = []
    for e in edges:
        fedges.append(bats.FilteredEdge(*e, dist(pts[e[0]], pts[e[1]])))

    F = bats.FlagFiltration(fedges, pts.shape[0], maxdim, t0)
    return F

def RipsFiltration(pts, maxdim=2):
	pts = np.array(pts.T, dtype=np.float, order='F')
	data = bats.DataSet(bats.DenseDoubleMatrix(pts))
	return bats.RipsFiltration(data, bats.Euclidean(), np.inf, maxdim)


# --------------
# Importing header files
import numpy as np
import warnings

warnings.filterwarnings('ignore')

#New record
new_record=[[50,  9,  4,  1,  0,  0, 40,  0]]

#Reading file
data = np.genfromtxt(path, delimiter=",", skip_header=1)
census = np.concatenate((data,new_record))
#Code starts here
print(census.shape)
print(data.shape)
age= census[:,0]
print(age)
max_age=age.max()
min_age=age.min()
age_mean=np.mean(age)
age_std=np.std(age)
print(max_age)
print(min_age)
print(age_mean)
print(age_std)
race=census[:,2]
race_0=race[race==0]
print(race_0)
race_1=race[race==1]
race_2=race[race==2]
race_3=race[race==3]
race_4=race[race==4]

len_0 = np.size(race_0)
len_1=np.size(race_1)
len_2=np.size(race_2)
len_3=np.size(race_3)
len_4=np.size(race_4)
print(len_0,len_1,len_2,len_3,len_4)
racesize= np.array([len_0,len_1,len_2,len_3,len_4])
minority_race= np.argmin(racesize)
print(minority_race)

senior_citizens=census[census[:,0]>60]
working_hours_sum=senior_citizens.sum(axis=0)[6]
print(working_hours_sum)
senior_citizens_len=len(senior_citizens)
avg_working_hours = working_hours_sum/senior_citizens_len
print(avg_working_hours)
high=census[census[:,1]>10]
low=census[census[:,1]<=10]
avg_pay_high=high[:,7].mean()
avg_pay_low=low[:,7].mean()
print(avg_pay_high)
print(avg_pay_low)
print(avg_pay_high==avg_pay_low)


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

def show_image(name, img):
    print("Press any key to close the window(do not use x button on top of the window)")
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    return

zmg = cv2.imread("norm.jpg", 0)
img = np.zeros(zmg.shape)
img[:zmg.shape[0], :zmg.shape[1]] = zmg
show_image("curve", img)

Z = np.fft.fft2(img)
Zlog = np.log2(1 + np.abs(Z))
M = np.max(np.max(Zlog))
# print("Z:\n", Z)
# print("Zlog:\n", Zlog)
# print("M:\n", M)
show_image("dft", Zlog/M)

M = np.max(np.max(np.abs(Z)))
thresh_list = [0.0001, 0.001, 0.01, 0.05]
compression_ratios = []
distortions = []

for thresh in thresh_list:
    Zthresh = np.multiply(np.abs(Z) > thresh * M, Z)

    compression_ratio = np.sum(np.sum(np.abs(Zthresh) > 0)) / img.shape[0] / img.shape[1]
    print("Fraction of Fourier coefficients survived the cut:",
            compression_ratio)
    compression_ratios.append(compression_ratio)

    zthresh = np.real(np.fft.ifft2(Zthresh))
    show_image("dft with thresholding", zthresh)

    distortion = 100 * np.linalg.norm(img - zthresh, 'fro') ** 2 / np.linalg.norm(img, 'fro') ** 2
    print("Distortion of compressed signal:", distortion)
    distortions.append(distortion)

plt.plot(distortions, compression_ratios)
plt.xlabel(xlabel="distortion")
plt.ylabel(ylabel="compression ratio")
plt.show()


import pymc as pm
import numpy as np
from scipy.optimize import nnls

def model(a_matrix, b_vector):
    x_coeffs = [pm.Uniform('x_coeffs_%i' % i, 0.0, 5.00) for i in range(len(x_true))]

    @pm.deterministic(plot=False)
    def linear_solver(x_coeffs=x_coeffs, a_matrix=a_matrix):
        solver_solution = a_matrix.dot(x_coeffs)
        return solver_solution

    @pm.stochastic(observed=True)
    def likelihood(value=b_vector, fit_results=linear_solver, sigma=err):
        chiSq = np.sum(np.square(fit_results - value) / np.square(sigma))
        return - chiSq / 2

    return locals()

x_true = np.array([3, 2, 1])
a = np.array([[10, -5, 0], [15, 20, 0],
              [12, 5, -20]])
b = a.dot(x_true)
err = 0.05

# Least squares
non_negative_least_squares = nnls(a, b)[0]

# Bayesian
MDL1 = pm.MCMC(model(a, b))
MDL1.sample(10000, 5000, 1)

# Invert matrix
x_inversion = np.dot(np.linalg.inv(np.dot(a.T, a)), np.dot(a.T, b))

# Compare
for i in range(len(b)):
    bayesian_fit_coeff = MDL1.stats()['x_coeffs_' + str(i)]['mean']
    print x_true[i], bayesian_fit_coeff, non_negative_least_squares[i], x_inversion[i]


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

def loadCsv(filename):
  '''
  load data.
  https://stackoverflow.com/questions/4315506/load-csv-into-2d-matrix-with-numpy-for-plotting
  https://machinelearningmastery.com/index-slice-reshape-numpy-arrays-machine-learning-python/
  https://stackoverflow.com/questions/23911875/select-certain-rows-condition-met-but-only-some-columns-in-python-numpy
  '''
  lines = csv.reader(open(filename, 'rt', encoding = 'utf-8'))
  next(lines, None)  # skip the headers
  dataset = list(lines)
  result = np.array(dataset).astype("float")
  reduce_result = result[result[:,-1] > 0]
  np.random.shuffle(reduce_result) # randomly re-arrange the rows of the data samples
  X = reduce_result[:, 0:12]
  mean_X = np.mean(X, axis = 0)  # normalize the features
  X -= mean_X
  y = reduce_result[:, -1]
  return [X, y]

trainfile = 'train.csv'
[X_train, y_train]= loadCsv(trainfile)

testfile = 'test.csv'
[X_test, y_test]= loadCsv(testfile)

def plot_outcome(y):
  # https://matplotlib.org/1.2.1/examples/pylab_examples/histogram_demo.html
  # the histogram of the data
  #n, bins, patches = plt.hist(y, 10, density=1, log = False, facecolor='blue', alpha=0.75)
  n, bins, patches = plt.hist(y, 10, density=1, log = True, facecolor='blue', alpha=0.75)

  plt.xlabel('Outcome variable(Forest fire area)')
  #plt.ylabel('Probability')
  plt.ylabel('Probability(log)')
  plt.title(r'Histogram of the outcome variable.')
  plt.axis([0, 300, 0, 0.3])
  plt.grid(True)
  plt.show()

#plot_outcome(y_train)
#plot_outcome(y_test)

def train(X, y):
  num_train = X.shape[0]
  X = np.c_[np.ones(num_train),X]
  X_t = X.T
  inv_x_2 = np.linalg.inv(X_t.dot(X))
  weight_ols = (inv_x_2.dot(X_t)).dot(y)
  return weight_ols

weight = train(X_train, y_train)
X_test = np.c_[np.ones(X_test.shape[0]),X_test]
y_pred = X_test.dot(weight)
RSS = ((y_test - y_pred).T).dot(y_test - y_pred)
print('The RSS error of the OLS solution: ')
print(RSS)

correlation = np.cov(y_test, y_pred)
print('Correlation between y_test and y_pred:')
print(correlation)


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

class SphericalSampler:
    """
    Sample N points on unit sphere, to use these as camera positions in data generation process.
    """

    def __init__(self, N, sampling="LATTICE"):
        self.N = N
        if sampling == "LATTICE":
            self.points = self.sphere_fibonacci_grid_points(N)
        elif sampling == "RANDOM":
            self.points = self.sphere_sample_gaussian(N)
        elif sampling == "CURVE":
            self.points = self.sphere_sample_curve(N)
        elif sampling == "SPIRAL":
            self.points = self.sphere_sample_spiral(N)
        elif sampling == "HELIX":
            self.points = self.sphere_sample_helix(N)
        elif sampling == "ARC":
            self.points = self.sphere_sample_arc(N)

        else:
            raise NameError('Sampling of type: %s not supported. Use: LATTICE | RANDOM' % sampling)
        print("Sampled %d views from unit sphere with %s sampling." % (N, sampling))

    def sphere_fibonacci_grid_points(self, ng):
        """
         SPHERE_FIBONACCI_GRID_POINTS: Fibonacci spiral gridpoints on a sphere.

          Licensing:

            This code is distributed under the GNU LGPL license.

          Modified:

            15 May 2015
            Author:
                John Burkardt

          Reference:

            Richard Swinbank, James Purser,
            Fibonacci grids: A novel approach to global modelling,
            Quarterly Journal of the Royal Meteorological Society,
            Volume 132, Number 619, July 2006 Part B, pages 1769-1793.

          Parameters:

            Input, integer NG, the number of points.

            Output, real XG(3,N), the grid points.
        """
        phi = (1.0 + np.sqrt(5.0)) / 2.0

        theta = np.zeros(ng)
        sphi = np.zeros(ng)
        cphi = np.zeros(ng)

        for i in range(0, ng):
            i2 = 2 * i - (ng - 1)
            theta[i] = 2.0 * np.pi * float(i2) / phi
            sphi[i] = float(i2) / float(ng)
            cphi[i] = np.sqrt(float(ng + i2) * float(ng - i2)) / float(ng)

        xg = np.zeros((ng, 3))

        for i in range(0, ng):
            xg[i, 0] = cphi[i] * np.sin(theta[i])  # x
            xg[i, 1] = cphi[i] * np.cos(theta[i])  # y
            xg[i, 2] = sphi[i]  # z

        return xg

    def sphere_sample_gaussian(self, N):
        points = np.zeros((N,3))
        for i in range(N):
            x = np.random.normal()
            y = np.random.normal()
            z = np.random.normal()
            point = np.array([x,y,z])
            points[i,:] = point / np.linalg.norm(point)
            points[i,2] = abs(points[i,2])
        return points

    def sphere_sample_curve(self,N,  theta = np.pi/2):

        phi = np.linspace(0, 2*np.pi, num=N, endpoint=False, retstep=False, dtype=float, axis=0)
        points = np.zeros((N,3))

        x = np.sin(theta) * np.cos(phi)
        z = np.sin(theta) * np.sin(phi)
        y = np.cos(theta)
        points[:,0] = x
        points[:,1] = y
        points[:,2] = z

        print(points.shape)

        return points

    def sphere_sample_spiral(self,N):

        phi = np.linspace(0,1, num=N, endpoint=False, retstep=False, dtype=float, axis=0)
        points = np.zeros((N,3))

        x = phi * np.cos(16*phi)
        z = phi *  np.sin(16*phi)

        y = np.sqrt(1 - np.square(x) - np.square(z))

        #z = np.sin(theta) * np.sin(phi)
        points[:,0] = x
        points[:,1] = y
        points[:,2] = z

        for i in range(N):
            points[i,:] = points[i,:] / np.linalg.norm(points[i,:])
        print(points.shape)

        return points

    def sphere_sample_arc(self,N):

        t = np.linspace(0,1, num=N, endpoint=False, retstep=False, dtype=float, axis=0)
        points = np.zeros((N,3))
        theta_x = np.linspace(-0.5,0.5, num=N, endpoint=False, retstep=False, dtype=float, axis=0)
        theta_y = np.linspace(-0.2,0.2, num=N, endpoint=False, retstep=False, dtype=float, axis=0)
        #x = np.sin(theta_x)
        #y = np.cos(theta_y)
        #z = 0.7

        points[:,0] = theta_x
        points[:,1] = theta_y
        points[:,2] = 0.7

        # for i in range(N):
        #     points[i,:] = points[i,:] / np.linalg.norm(points[i,:])
        print(points.shape)

        return points

    def sphere_sample_helix(self,N):

        t = np.linspace(0,1, num=N, endpoint=False, retstep=False, dtype=float, axis=0)
        points = np.zeros((N,3))

        x = np.cos(3*t*np.pi)
        y = np.sin(3*t*np.pi)
        z = t

        points[:,0] = x
        points[:,1] = y
        points[:,2] = z

        # for i in range(N):
        #     points[i,:] = points[i,:] / np.linalg.norm(points[i,:])
        print(points.shape)

        return points

    def visualize(self):
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        ax.scatter(self.points[ :,0], self.points[:,1], self.points[:,2], zdir='z', s=5, c=None, depthshade=True)
        ax.set_xlabel('X Label')
        ax.set_ylabel('Y Label')
        ax.set_zlabel('Z Label')
        plt.show()

if __name__ == "__main__":
    sampler = SphericalSampler(200, 'LATTICE')
    sampler.visualize()
    print(sampler.points.shape)


'''
Functions for performing Otsu's algorithm.

3 variations:

1. the standard way Otsu's algorithm on the whole image
2. segmenting the image into blocks, and performing Otsu's algorithm on each block
3. use sliding window on the image, and use Otsu's algorithm within the window
'''
import cv2
import numpy as np

def threshold(image: np.ndarray) -> np.ndarray:
    '''
    Otsu algorithm, done the standard way. (global threshold)

    The mask that is obtained can be used to binarize the image, by doing:
    np.where(image > threshold_mask, 255, 0)

    :params:
    - image (np.ndarray): the image to find the threshold of

    :return:
    - mask of threshold (np.ndarray)
    '''
    histogram = cv2.calcHist(
        images=[image],
        channels=[0],
        mask=None,
        histSize=[256],
        ranges=[0, 256],
    )

    num_pixels = image.size

    gray_level_probabilities = [
        bin_count[0] / num_pixels
        for bin_count in histogram
    ]

    best_threshold = -1
    min_intraclass_variance = float('inf')

    for threshold in range(len(histogram)):  # 0 to 255
        lower_group_probability = sum(gray_level_probabilities[:threshold+1])
        upper_group_probability = sum(gray_level_probabilities[threshold+1:])

        if lower_group_probability == 0 or upper_group_probability == 0:
            continue  # otherwise, divide by 0

        lower_group_mean = sum([
            gray_level * probability  # gray level == pixel value
            for gray_level, probability in enumerate(gray_level_probabilities[:threshold+1])
        ]) / lower_group_probability

        upper_group_mean = sum([
            gray_level * probability
            for gray_level, probability in enumerate(gray_level_probabilities[threshold+1:])
        ]) / upper_group_probability

        lower_group_variance = sum([
            ((gray_level - lower_group_mean)**2) * probability
            for gray_level, probability in enumerate(gray_level_probabilities[:threshold+1])
        ]) / lower_group_probability

        upper_group_variance = sum([
            ((gray_level - upper_group_mean)**2) * probability
            for gray_level, probability in enumerate(gray_level_probabilities[threshold+1:])
        ]) / upper_group_probability

        intraclass_variance = lower_group_probability * lower_group_variance + \
            upper_group_probability * upper_group_variance

        if intraclass_variance < min_intraclass_variance:
            min_intraclass_variance = intraclass_variance
            best_threshold = threshold

    return np.full_like(image, fill_value=best_threshold)

def segmented_threshold(
    image: np.ndarray,
    num_vertical_segments: int,
    num_horizontal_segments: int,
) -> np.ndarray:
    '''
    Otsu threshold, done on segments of the image.

    Total number of segments = num_vertical_segments * num_horizontal_segments

    The mask that is obtained can be used to binarize the image, by doing:
    np.where(image > threshold_mask, 255, 0)

    :params:
    - image (np.ndarray): the image to find the threshold of
    - num_vertical_segments (int): number of times to segment vertically; at least 1
    - num_horizontal_segments (int): number of times to segment horizontally; at least 1

    :return:
    - mask of threshold (np.ndarray)
    '''
    if num_vertical_segments < 1:
        raise ValueError('There must be at least 1 segment in vertical direction')
    if num_horizontal_segments < 1:
        raise ValueError('There must be at least 1 segment in horizontal direction')

    image_height, image_length = image.shape

    # take ceiling to ensure entire image is covered
    segment_height = image_height // num_vertical_segments + 1
    segment_length = image_length // num_horizontal_segments + 1

    threshold_mask = np.zeros_like(image)

    for vertical_offset in range(0, image_height, segment_height):
        for horizontal_offset in range(0, image_length, segment_length):
            image_segment = image[
                vertical_offset: vertical_offset + segment_height,
                horizontal_offset: horizontal_offset + segment_length
            ]

            # standard Otsu's algorithm
            segmented_threshold_mask = threshold(image_segment)

            threshold_mask[
                vertical_offset: vertical_offset + segment_height,
                horizontal_offset: horizontal_offset + segment_length
            ] = segmented_threshold_mask

    return threshold_mask

def sliding_window_threshold(
    image: np.ndarray,
    window_height: int,
    window_length: int,
    vertical_stride: int,
    horizontal_stride: int,
) -> np.ndarray:
    '''
    Otsu threshold, done by using a sliding window over the image.

    The mask that is obtained can be used to binarize the image, by doing:
    np.where(image > threshold_mask, 255, 0)

    :params:
    - image (np.ndarray): the image to find the threshold of
    - window_height (int): height of the window i.e. vertical width
    - window_length (int): length of the window i.e. horizontal width
    - vertical_stride (int): number of pixel-wise steps to take in the vertical direction
    - horizontal_stride (int): number of pixel-wise steps to take in the horizontal direction

    :return:
    - mask of threshold (np.ndarray)
    '''
    if window_height < 1:
        raise ValueError('Window must have a height of at least 1 pixel')
    if window_length < 1:
        raise ValueError('Window must have a length of at least 1 pixel')
    if vertical_stride < 1:
        raise ValueError('Stride in the vertical direction must be of at least 1 pixel')
    if horizontal_stride < 1:
        raise ValueError('Stride in the horizontal direction must be of at least 1 pixel')

    image_height, image_length = image.shape

    threshold_mask = np.zeros_like(image, dtype=np.float64)
    num_repetitions = np.zeros_like(image)

    for vertical_offset in range(0, image_height - window_height + vertical_stride, vertical_stride):
        for horizontal_offset in range(0, image_length - window_length + horizontal_stride, horizontal_stride):
            window = image[
                vertical_offset: vertical_offset + window_height,
                horizontal_offset: horizontal_offset + window_length
            ]

            # standard Otsu's algorithm
            window_threshold_mask = threshold(window)

            threshold_mask[
                vertical_offset: vertical_offset + window_height,
                horizontal_offset: horizontal_offset + window_length
            ] += np.mean(window_threshold_mask)

            num_repetitions[
                vertical_offset: vertical_offset + window_height,
                horizontal_offset: horizontal_offset + window_length
            ] += 1

    return threshold_mask / num_repetitions  # get average


import matplotlib.pyplot as plt
import numpy as np

def laplace_function(x, b):
    result = (1/(2*b)) * np.e**(-1*(np.abs(x)/b))
    return result

#在-100到100之间等间隔的取10000个数
x = np.linspace(-100,100,10000)
y1 = [laplace_function(x_,0.5) for x_ in x]
y2 = [laplace_function(x_,1) for x_ in x]
y3 = [laplace_function(x_,2) for x_ in x]

plt.plot(x,y1,color='r',label='u:0|b:0.5')
plt.plot(x,y2,color='g',label='u:0|b:1')
plt.plot(x,y3,color='b',label='u:0|b:2')
plt.title("Laplace distribution")
plt.legend()
plt.show()


import numpy as np

def RamanujamSimultaneousEquations(a=np.array([2, 3, 16, 31, 103, 235, 674, 1669, 4526, 11595])):
    """
    This code implements the beautiful algorithm given by Ramanujam to solve a specific set of
    nonlinear simultaneous polynomial (in vectors x and y) equations. Although there is a necessary
    and sufficient condition for the existence of a solution, it is not appear to be checkable in polynomial time.
    Hence the code must be used with caution. Read more at: https://pdfs.semanticscholar.org/ae75/da0be9fb455e2c55daa5fca46ae63e6a60bd.pdf

    :param a: The RHS of the equations given as a vector. Default set to the example in Ramanujam's paper.
    :return: Two vectors x, y representing the solution.
    """
    if (len(a) % 2 != 0):
        print('There must be an even number of equations!')
        return -1, -1, -1

    n = int(len(a) / 2)

    Z = np.zeros((n, n))
    for k in range(n):
        Z[k, :] = np.flip(a[k:n + k])

    z = -a[n:2 * n]

    B = np.linalg.solve(Z,z)

    Z = np.zeros((n, n))
    for k in range(n):
        Z[k, 0:k + 1] = np.flip(a[0:k + 1])

    A = Z @ np.hstack((1, B[0:n - 1]))

    roots = np.roots(np.hstack((np.flip(B), 1)))

    if (np.linalg.norm(np.imag(roots)) != 0):
        print('There does not exist a solution!')
        return -1, -1, -1

    y = 1 / roots

    x = np.zeros(n)
    for k in range(n):
        den = np.polyval(np.flip(np.polynomial.polynomial.polyfromroots(np.delete(roots, k))), roots[k])
        num = np.polyval(np.flip(A), roots[k])
        x[k] = y[k] * num / den

    Y = np.transpose(np.vander(y, increasing=True, N=len(a)))
    diff_norm = np.linalg.norm(a - Y @ x)

    return x, y, diff_norm


import math
import numpy as np
import torch

def bitdecomp(x, n_bits):
    mods = []
    for _ in range(n_bits):
        mods.append(x % 2)
        x = x / 2

    bitrep = torch.stack(mods, dim=-1).byte()

    return bitrep

def bitrecon(x, b_source, b_target):
    bitrep = x.view(b_source * len(x) // b_target, b_target)
    exp = torch.ShortTensor([2**e for e in range(b_target)]).view(1, b_target)
    recon = (bitrep.short() * exp).sum(dim=1).view(-1)

    return recon

def numpack(n, n_bits):
    flat = n.view(-1)
    if len(flat) % 8 > 0:
        flat = torch.cat((flat, flat.new_zeros(8 - len(flat) % 8)))

    bitrep = bitdecomp(flat, n_bits)
    uint8rep = bitrecon(bitrep, n_bits, 8)

    return uint8rep.byte()

def unpack(p, n_bits, size=None):
    bitrep = bitdecomp(p, 8)
    recon = bitrecon(bitrep, 8, n_bits).short()

    if size is not None:
        nelements = np.prod(size)
        recon = recon[:nelements].view(size)

    return recon

if __name__ == '__main__':
    idx_high = 128
    a = torch.randint(low=0, high=idx_high, size=(4, 3)).long()
    p = numpack(a, int(math.log2(idx_high)))
    r = unpack(p, int(math.log2(idx_high)), a.size())
    diff = (a.short() - r).float().norm()

    print('Reconstruction error: {:.2f}'.format(diff))


from __future__ import division
import numpy as np
import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import NoNorm
import random
from scipy.linalg import circulant
import networkx as nx

def _distance_matrix(L):
    Dmax = L//2

    D  = list(range(Dmax+1))
    D += D[-2+(L%2):0:-1]

    return circulant(D)/Dmax

def _pd(d, p0, beta):
    return beta*p0 + (d <= p0)*(1-beta)

def watts_strogatz(L, p0, beta, directed=False, rngseed=1):
    rng = np.random.RandomState(rngseed)

    d = _distance_matrix(L)
    p = _pd(d, p0, beta)

    if directed:
        A = 1*(rng.random_sample(p.shape) < p)
        np.fill_diagonal(A, 0)
    else:
        upper = np.triu_indices(L, 1)

        A          = np.zeros_like(p, dtype=int)
        A[upper]   = 1*(rng.rand(len(upper[0])) < p[upper])
        A.T[upper] = A[upper]

    return A

def preserve_self_loops(matrix0, edges):
    # matrix0 needs to have 1's in the diagonal for this to work
    matrix1=matrix0
    for i in np.arange(0,matrix0.shape[0]):
        deg_dist = np.count_nonzero(matrix0[i,:])
        if deg_dist > edges:
            for k in np.arange(0,deg_dist-edges):
                non_zeros = (np.where(matrix0[i,:]>0)[0]).tolist()
                non_zeros.remove(i)
                random_index = non_zeros[np.random.randint(0,len(non_zeros))]
                matrix1[i,random_index] = 0
    return matrix1

def D_norm(D0):
    size = D0.shape[0]
    D1  = np.zeros((size,size)) # Preallocate matrix
    for i in np.arange(size):
        D1[:,i]  = (D0[:,i] / D0[:,i].sum()).T
    return D1

def gen_rand_matrix(seed):
    # Network size = 20
    #! Note: directed = False produces a symmetric matrix
    random.seed(seed)

    L = 20
    K = 4
    p0 = K/(L-1)
    regular = watts_strogatz(beta=0,directed=False,L=L,p0=p0)

    G = nx.Graph()
    G.add_nodes_from(np.arange(0,20))

    for i in np.arange(0,20):
        for j in np.arange(0,20):
            if regular[i,j] == 1:
                G.add_edge(i,j)

    reg_G = nx.to_numpy_matrix(G)
    di = np.diag_indices(20)

    random_G = nx.double_edge_swap(nx.from_numpy_matrix(reg_G), nswap=20, max_tries=1000)
    random_G2 = nx.to_numpy_matrix(random_G)

    di = np.diag_indices(20)
    random_G2[di]=1

    random_G2_revised = np.asarray(preserve_self_loops(random_G2,edges=5))

    random_G2_norm = D_norm(random_G2_revised)

    return random_G2_norm


import math
import numpy
import scipy
import pylab
import scipy.optimize

#function definitions.
#the 0.2 is the t-Tau moved to the other side so we can solve for x when y is 0.
def f(x):

	y = x - 0.2 * numpy.sin(x) - 0.8

	return y

def f_prime(x):

	y = 1.0 - 0.2 * numpy.cos(x)

	return y

#could also use this simple newton method.
def newt(x,n):
	for i in range(n):
		if f_prime(x) == 0:
			return x
		x = x - f(x)/f_prime(x)
	return x

#using a scipy function for computing the zero.
# Where 0.2 is the intial guess.
root = scipy.optimize.newton(f, .2, fprime = f_prime)

#could uncomment line 40 to use our own newton method. (The answer is still the same.)
#0.2 is the intial guess and 10 is the number of iterations.
#root = new(0.2, 10)

print 'this is the root for the given mean anomaly (radians):', root

#converting from radians to degrees.
root_degrees = root * ( 180.0 / numpy.pi )

print 'This is the root in degrees (E):', root_degrees

#now we want r and theta.
# r = a(1 - eccentricity * cos(E) )
# tan(theta/2) = sqrt((1+eccentricity) / (1-eccentricity)) * tan(E/2)
#We want to solve for theta
#where e = 0.2 and a = 1.1 A.U. (given)

eccentricity = 0.2
semi_major_axis = 1.1

#calculating r and theta.
r = semi_major_axis * (1.0 - eccentricity * numpy.cos(root))

theta = 2.0 * numpy.arctan(numpy.sqrt((1.0 + eccentricity) / (1.0 - eccentricity))) * (root_degrees/2.0)

print 'this is r:', r

print 'this is theta:', theta


#!/usr/bin/python3
#
# 1/4-cycle sine table
#
# 07-11-2016 E. Brombaugh

import numpy as np
import math as math
import matplotlib as mpl
import matplotlib.pyplot as plt
import scipy.signal as signal
from write_meminit import write_meminit
from write_memh import write_memh

# fixed params
sine_len = 1024
sine_bits = 16
scl = 2**(sine_bits-1)-1
LUT = np.zeros(sine_len, dtype=np.int)

# stuff into array
for i in np.arange(sine_len):
    LUT[i] = np.floor(math.sin((i+0.5)*math.pi/(2*sine_len))*scl + 0.5)

# Plot combined coeffs
if 1:
    wave = np.zeros(4*sine_len, dtype=np.int)
    wave[0:sine_len] = LUT;
    wave[sine_len:sine_len*2] = LUT[np.arange(sine_len-1,-1,-1)];
    wave[sine_len*2:sine_len*3] = -LUT;
    wave[sine_len*3:sine_len*4] = -LUT[np.arange(sine_len-1,-1,-1)];
    plt.figure()
    plt.plot(wave)
    plt.grid()
    plt.xlabel("index")
    plt.ylabel("value")
    plt.title("Sine Table (close to continue)")
    plt.show()

# Dump Coefficients to FPGA mem file
if 0:
    # Xilinx mem init file
    write_meminit("sine_table_1k.v", LUT)
else:
    # Verilog readmemh file
    write_memh("sine_table_1k.memh", LUT)


# -*- coding: utf-8 -*-
"""
Created on Thu May 26 22:22:48 2022

@author: Noah
"""
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
import statsmodels.api as sm
import scipy.stats as sps

#specifying the maximum power of 2
power = 10

#rolling sample lenght
n = 2**power

ticker = "^GSPC"
start = "2015-12-31"
end = "2021-06-16"

#downloading data
raw_data = yf.download(ticker, start, end)['Close']
returns = np.array(raw_data)[1:]/np.array(raw_data)[:-1]-1

#initializing arrays
hursts = np.array([])
tstats = np.array([])
pvalues = np.array([])

#calculating the rolling Hurst exponent

for t in np.arange(n, len(returns)+1):
    #specifying the subsample
    data = returns[t-n:t]
    X = np.arange(2,power+1)
    Y = np.array([])
    for p in X:
        m = 2**p
        s = 2**(power-p)
        rs_array = np.array([])

        #moving across subsamples
        for i in np.arange(0, s):
            subsample = data[i*m:(i+1)*m]
            mean = np.average(subsample)
            deviate = np.cumsum(subsample-mean)
            difference = max(deviate) - min(deviate)
            stdev = np.std(subsample)
            rescaled_range = difference/stdev
            rs_array = np.append(rs_array, rescaled_range)

        #calculating the log2 of average rescaled range
        Y = np.append(Y, np.log2(np.average(rs_array)))

    reg = sm.OLS(Y, sm.add_constant(X))
    res = reg.fit()
    hurst = res.params[1]
    tstat = (res.params[1]-0.5)/res.bse[1]
    pvalue = 2*(1 - sps.t.cdf(abs(tstat), res.df_resid))
    hursts = np.append(hursts, hurst)
    tstats = np.append(tstats, tstat)
    pvalues = np.append(pvalues, pvalue)

#visualising the Hurst exponent
plt.figure(1)
plt.title('Historical Hurst Exponent of S&P500', fontsize=10)
plt.rc('xtick', labelsize = 8)
plt.ylim(0.4, 0.6)
plt.plot(raw_data.index[n:], hursts)
plt.plot(raw_data.index[n:], np.ones(len(hursts))*0.5)

#visualising the t-stat and critical values
plt.figure(2)
plt.title('T values in hypothesis testing of Hurst Exponent for the S&P500', fontsize=10)
plt.rc('xtick', labelsize = 8)
plt.plot(raw_data.index[n:], tstats)
plt.plot(raw_data.index[n:], np.ones(len(tstats))*sps.t.ppf(0.005, res.df_resid)) #the greater than 2.8 the better
plt.plot(raw_data.index[n:], np.ones(len(tstats))*sps.t.ppf(0.995, res.df_resid)) #get very worried

#visualising the p-values
plt.figure(3)
plt.title('P values in hypothesis testing of Hurst Exponent for the S&P500', fontsize=10)
plt.rc('xtick', labelsize = 8)
plt.plot(raw_data.index[n:], pvalues)
plt.plot(raw_data.index[n:], np.ones(len(pvalues))*0.05) #reject h0 if pvalue is less than 0.05
plt.plot(raw_data.index[n:], np.ones(len(pvalues))*0.001) #reject h0 because either a highly rare data result has been observed, or the null hypothesis is incorrect.

#visualising the price
plt.figure(4)
plt.title('S&P500 Price')
plt.rc('xtick', labelsize = 8)
plt.plot(raw_data.index[n:], raw_data[1024:])

Data gathering/Dynamic Hurst Exponent on Python/Dynamic Hurst Exponent on Python for Yahoo finance.py

import numpy,import scipy,import statsmodels


"""
Bayesian updating of beliefs about the bias of a coin. The prior and posterior
distributions indicate probability masses at discrete candidate values of theta.
"""
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-darkgrid')

# theta is the vector of candidate values for the parameter theta.
# n_theta_vals is the number of candidate theta values.
# To produce the examples in the book, set n_theta_vals to either 3 or 63.
n_theta_vals = 3.
# Now make the vector of theta values:
theta = np.linspace(1/(n_theta_vals +1), n_theta_vals /(n_theta_vals +1), n_theta_vals )

# p_theta is the vector of prior probabilities on the theta values.
p_theta = np.minimum(theta, 1-theta)  # Makes a triangular belief distribution.
p_theta = p_theta / np.sum(p_theta)     # Makes sure that beliefs sum to 1.

# Specify the data. To produce the examples in the book, use either
# data = np.repeat([1,0], [3, 9]) or data = np.repeat([1,0], [1, 11])
data = np.repeat([1, 0], [3, 9])
n_heads = np.sum(data)
n_tails = len(data) - n_heads

# Compute the likelihood of the data for each value of theta:
p_data_given_theta = theta**n_heads * (1-theta)**n_tails

# Compute the posterior:
p_data = np.sum(p_data_given_theta * p_theta)
p_theta_given_data = p_data_given_theta * p_theta / p_data   # This is Bayes' rule!

# Plot the results.
plt.figure(figsize=(12, 11))
plt.subplots_adjust(hspace=0.7)

# Plot the prior:
plt.subplot(3, 1, 1)
plt.stem(theta, p_theta, markerfmt=' ')
plt.xlim(0, 1)
plt.xlabel('$\\theta$')
plt.ylabel('$P(\\theta)$')
plt.title('Prior')
# Plot the likelihood:
plt.subplot(3, 1, 2)
plt.stem(theta, p_data_given_theta, markerfmt=' ')
plt.xlim(0, 1)
plt.xlabel('$\\theta$')
plt.ylabel('$P(D|\\theta)$')
plt.title('Likelihood')
plt.text(0.6, np.max(p_data_given_theta)/2, 'D = %sH,%sT' % (n_heads, n_tails))
# Plot the posterior:
plt.subplot(3, 1, 3)
plt.stem(theta, p_theta_given_data, markerfmt=' ')
plt.xlim(0, 1)
plt.xlabel('$\\theta$')
plt.ylabel('$P(\\theta|D)$')
plt.title('Posterior')
plt.text(0.6, np.max(p_theta_given_data)/2, 'P(D) = %g' % p_data)

plt.savefig('Figure_4.1.png')


import numpy as np

__all__ = ['euclidean']

def euclidean(u, v):
    """Compute the euclidean distance between two numpy arrays.

    Args:
        u (numpy.ndarray): Input array.
        v (numpy.ndarray): Input array.

    Returns:
        float: Euclidean distance between u and v.

    """
    return np.sqrt(np.sum(np.square(u - v), axis=-1))


import math
import numpy as np
from scipy import stats

from matilda.quantitative_analysis.stochastic_modeling.base_models import ContinuousStochasticProcess

class BrownianMotionModel(ContinuousStochasticProcess):
    """
        Generate an instance of Brownian motion (i.e. the Wiener process):

            X(t) = X(0) + N(0, delta**2 * t; 0, t)

        where N(a,b; t0, t1) is a normally distributed random variable with mean a and
        variance b.  The parameters t0 and t1 make explicit the statistical
        independence of N on different time intervals; that is, if [t0, t1) and
        [t2, t3) are disjoint intervals, then N(a, b; t0, t1) and N(a, b; t2, t3)
        are independent.

        Written as an iteration scheme,

            X(t + dt) = X(t) + N(0, delta**2 * dt; t, t+dt)

        If `x0` is an array (or array-like), each value in `x0` is treated as
        an initial condition, and the value returned is a numpy array with one
        more dimension than `x0`.

        Arguments
        -----------------------------------------------------------------------------
        delta : float
            delta determines the "speed" of the Brownian motion.  The random variable
            of the position at time t, X(t), has a normal distribution whose mean is
            the position at time t=0 and whose variance is delta**2*t.

        Returns
        ------------------------------------------------------------------------------
        A numpy array of floats with shape `x0.shape + (n,)`.

        Note that the initial value `x0` is not included in the returned array.
        """
    def __init__(self, x0, delta=2):
        super().__init__(x0)
        self.delta = delta

    def sample_path_simulation(self):
        x0 = np.asarray(self.x0)
        # For each element of x0, generate a sample of n numbers from a normal distribution.
        r = stats.norm.rvs(size=x0.shape + (self.Nsim,), scale=self.delta * math.sqrt(self.dt))
        out = np.empty(r.shape)
        # This computes the Brownian motion by forming the cumulative sum of the random samples.
        np.cumsum(r, axis=-1, out=out)
        out += np.expand_dims(x0, axis=-1)  # Add the initial condition.

        return out


#!/usr/bin/env python3

'''
The Bifid Cipher uses a Polybius Square to encipher a message in a way that
makes it fairly difficult to decipher without knowing the secret.

https://www.braingle.com/brainteasers/codes/bifid.php
'''

import numpy as np

class BifidCipher():

    def __init__(self):
        SQUARE = [['a','b','c','d','e'],
                  ['f','g','h','i','k'],
                  ['l','m','n','o','p'],
                  ['q','r','s','t','u'],
                  ['v','w','x','y','z']]
        self.SQUARE = np.array(SQUARE)

    def letter_to_numbers(self, letter: str) -> np.ndarray:
        """
        Return the pair of numbers that represents the given letter in the
        polybius square
        """
        index1, index2 = np.where(self.SQUARE == letter)
        indexes = np.concatenate([index1 + 1, index2 + 1])
        return indexes

    def numbers_to_letter(self, index1: int, index2: int) -> str:
        """
        Return the letter corresponding to the position [index1, index2] in
        the polybius square
        """
        letter = self.SQUARE[index1 - 1, index2 - 1]
        return letter

    def encode(self, message: str) -> str:
        """
        Return the encoded version of message according to the polybius cipher
        """
        message = message.lower()
        message = message.replace(' ', '')
        message = message.replace("j", "i")

        first_step = np.empty((2, len(message)))
        for letter_index in range(len(message)):
            numbers = self.letter_to_numbers(message[letter_index])

            first_step[0, letter_index] = numbers[0]
            first_step[1, letter_index] = numbers[1]

        second_step = first_step.reshape(2 * len(message))
        encoded_message = ''
        for numbersIndex in range(len(message)):
            index1 = int(second_step[numbersIndex * 2])
            index2 = int(second_step[(numbersIndex * 2) + 1])
            letter = self.numbers_to_letter(index1, index2)
            encoded_message = encoded_message + letter

        return encoded_message

    def decode(self, message: str) -> str:
        """
        Return the decoded version of message according to the polybius cipher
        """
        message = message.lower()
        message.replace(' ', '')
        first_step = np.empty(2 * len(message))
        for letter_index in range(len(message)):
            numbers = self.letter_to_numbers(message[letter_index])
            first_step[letter_index * 2] = numbers[0]
            first_step[letter_index * 2 + 1] = numbers[1]

        second_step = first_step.reshape((2, len(message)))
        decoded_message = ''
        for numbersIndex in range(len(message)):
            index1 = int(second_step[0, numbersIndex])
            index2 = int(second_step[1, numbersIndex])
            letter = self.numbers_to_letter(index1, index2)
            decoded_message = decoded_message + letter

        return decoded_message


import numpy as np

def softmax_with_overflow(x):
    numerators = np.exp(x)
    denominator = np.sum(numerators)
    return numerators / denominator

def softmax(x):
    """return softmax for the NumPy array: x."""
    if x.ndim == 2: # mini-batch version
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T
    # constant for removing overflow
    c = np.max(x)
    numerators = np.exp(x - c)
    denominator = np.sum(numerators)
    return numerators / denominator

if __name__ == '__main__':
    x1 = np.array([0.1, 0.5, 1.0])
    x2 = np.array([1000, 2000, 3000]) # this input may cause a overflow

    print('[softmax_with_overflow]')
    print('{0} => {1}'.format(x1, softmax_with_overflow(x1)))
    print('{0} => {1}'.format(x2, softmax_with_overflow(x2)))

    print('\n[softmax]')
    print('{0} => {1}'.format(x1, softmax(x1)))
    print('{0} => {1}'.format(x2, softmax(x2)))


# Chapter 7 Combining Different Models for Ensemble Learning
# Learning with ensembles
from scipy.misc import comb
import math
def ensemble_error(n_classifier, error):
    k_start = math.ceil(n_classifier / 2.0)
    probs = [comb(n_classifier, k) * error**k * (1- error) ** (n_classifier - k)
             for k in range(int(k_start), n_classifier + 1)]
    return sum(probs)

ensemble_error(n_classifier=11, error=0.25)
'''
After we've implemented the ensemble_error function, we can compute
the ensemble error rates for a range of different base errors from 0.0 to 1.0
to visualize the relationship between ensemble and base errors in a line graph:
'''
import numpy as np
error_range = np.arange(0.0, 1.01, 0.01)
ens_errors = [ensemble_error(n_classifier=11, error=error) for error in error_range]

import matplotlib.pyplot as plt
plt.plot(error_range, ens_errors, label='Ensemble error', linewidth=2)
plt.plot(error_range, error_range, linestyle='--', label='Base error', linewidth=2)
plt.xlabel('Base error')
plt.ylabel('Base/Ensemble error')
plt.legend(loc='upper left')
plt.grid()
plt.show()

'''
As we can see in the resulting plot, the error probability of an ensemble is always
better than the error of an individual base classifier as long as the base classifiers
perform better than random guessing ( ε < 0.5 ). Note that the y-axis depicts the
base error (dotted line) as well as the ensemble error (continuous line):
'''

# Implementing a simple majority vote classifier
'''
To translate the concept of the weighted majority vote into Python code, we can use
NumPy's convenient argmax and bincount functions:'''
import numpy as np
np.argmax(np.bincount([0, 0, 1], weights=[0.2, 0.2, 0.6]))

'''
To implement the weighted majority vote based on class probabilities, we can again
make use of NumPy using numpy.average and np.argmax'''
ex = np.array([[0.9, 0.1], [0.8, 0.2], [0.4, 0.6]])
p = np.average(ex, axis=0, weights=[0.2, 0.2, 0.6])
p
np.argmax(p)

'''Putting everythin together, let's now implement a MajorityVoteClassifier in Python'''
from sklearn.base import BaseEstimator
from sklearn.base import ClassifierMixin
from sklearn.preprocessing import LabelEncoder
from sklearn.externals import six
from sklearn.base import clone
from sklearn.pipeline import _name_estimators
import numpy as np
import operator

class MajorityVoteClassifier(BaseEstimator, ClassifierMixin):
    '''A majority vote ensemble classifier
    Parameters
    ----------
    classifiers : array-like, shape = [n_classifiers]
      Different classifiers for the ensemble
    vote : str, {'classlabel', 'probability'}
      Default: 'classlabel'
      If 'classlabel' the prediction is based on
      the argmax of class labels. Else if
      'probability', the argmax of the sum of
      probabilities is used to predict the class label
      (recommended for calibrated classifiers).
    weights : array-like, shape = [n_classifiers]
      Optional, default: None
      If a list of `int` or `float` values are
      provided, the classifiers are weighted by
      importance; Uses uniform weights if `weights=None`. '''
    def __init__(self, classifiers, vote='classlabel', weights=None):

        self.classifiers = classifiers
        self.named_classifiers = {key: value for key, value in _name_estimators(classifiers)}

        self.vote = vote
        self.weights = weights

    def fit(self, X, y):
        '''Fit classifiers.

        Parameters
        ----------
        X : {array-like, sparse matrix},
            shape = [n_samples, n_features]
            Matrix of training samples.
        y : array-like, shape = [n_samples]
            Vector of target class labels.
        Returns
        -------
        self : object'''
        # Use LabelEncoder to ensure class labels start
        # with 0, which is important for np.argmax
        # call in self.predict
        self.lablenc_ = LabelEncoder()
        self.lablenc_.fit(y)
        self.classes_ = self.lablenc_.classes_
        self.classifiers_ = [ ]
        for clf in self.classifiers:
            fitted_clf = clone(clf).fit(X, self.lablenc_.transform(y))
            self.classifiers_.append(fitted_clf)
        return self

    def predict(self, X):
        '''Predict class labels for X.
        Parameters
       ----------
       X : {array-like, sparse matrix},
           Shape = [n_samples, n_features]
           Matrix of training samples.
       Returns
       ----------
       maj_vote : array-like, shape = [n_samples]
           Predicted class labels.'''
        if self.vote == 'probability':
            maj_vote = np.argmax(self.predict_proba(X), axis=1)

        else: # 'classlabel' vote
            # Collect results from clf.predict calls
            predictions = np.asarray([clf.predict(X) for clf in self.classifiers_]).T

            maj_vote = np.apply_along_axis(lambda x: np.argmax(np.bincount(x, weights=self.weights)), axis=1, arr=predictions)
        maj_vote = self.lablenc_.inverse_transform(maj_vote)
        return maj_vote

    def predict_proba(self, X):
        '''Predict class probabilities for X.
        Parameters
        ----------
        X : {array-like, sparse matrix},
            shape = [n_samples, n_features]
            Training vectors, where n_samples is
            the number of samples and
            n_features is the number of features.
        Returns
        ----------
        avg_proba : array-like,
            shape = [n_samples, n_classes]
            Weighted average probability for
            each class per sample.'''
        probas = np.asarray([clf.predict_proba(X) for clf in self.classifiers_])
        avg_proba = np.average(probas, axis=0, weights=self.weights)
        return avg_proba

    def get_params(self, deep=True):
        '''Get classifier parameter names for GridSearch'''
        if not deep:
            return super(MajorityVoteClassifier, self).get_params(deep=False)
        else:
            out = self.named_classifiers.copy()
            for name, step in\
                six.iteritems(self.named_classifiers):
                for key, value in six.iteritems(step.get_params(deep=True)):
                    out['%s__%s' % (name, key)] = value
            return out
        '''
        Also, note that we defined our own modified version of the get_params methods
        to use the _name_estimators function in order to access the parameters of individual
        classifiers in the ensemble. This may look a little bit complicated at first, but it will
        make perfect sense when we use grid search for hyperparameter-tuning in later sections.'''

# Combining different algorithms for classification with majority vote

'''We will take a shortcut and load the Iris dataset from scikit-learn's dataset module.
Furthermore, we will only select two features, sepal width and petal length, to make
the classification task more challenging. Although our MajorityVoteClassifier generalizes
to multiclass problems, we will only classify flower samples from the two classes,
Iris-Versicolor and Iris-Virginica, to compute the ROC AUC. The code is as follows:'''

from sklearn import datasets
from sklearn.cross_validation import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder
iris = datasets.load_iris()
X, y = iris.data[50:, [1, 2]], iris.target[50:]
le = LabelEncoder()
y = le.fit_transform(y)
'''
Next we split the Iris samples into 50 percent training and 50 percent test data:'''
X_train, X_test, y_train, y_test = \
train_test_split(X, y, test_size=0.5, random_state=1)
'''
Using the training dataset, we now will train three different classifiers—a
logistic regression classifier, a decision tree classifier, and a k-nearest neighbors
classifier—and look at their individual performances via a 10-fold cross-validation
on the training dataset before we combine them into an ensemble classifier:'''
from sklearn.cross_validation import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
import numpy as np
clf1 = LogisticRegression(penalty='l2', C=0.001, random_state=0)
clf2 = DecisionTreeClassifier(max_depth=1, criterion='entropy', random_state=0)
clf3 = KNeighborsClassifier(n_neighbors=1, p=2, metric='minkowski')

pipe1 = Pipeline([['sc', StandardScaler()], ['clf', clf1]])
pipe3 = Pipeline([['sc', StandardScaler()], ['clf', clf3]])
clf_labels = ['Logistic Regression', 'Decision Tree', 'KNN']
print '10- fold cross validation:\n'

for clf, label in zip([pipe1, clf2, pipe3], clf_labels):
    scores = cross_val_score(estimator=clf, X=X_train, y=y_train, cv=10, scoring='roc_auc')
    print 'ROC AUC: %0.2f (+/- %0.2f) [%s]' % (scores.mean(), scores.std(), label)

'''
Now let's move on to the more exciting part and combine the individual classifiers
for majority rule voting in our MajorityVoteClassifier:'''
mv_clf = MajorityVoteClassifier(classifiers=[pipe1, clf2, pipe3])
clf_labels += ['Majority Voting']
all_clf = [pipe1, clf2, pipe3, mv_clf]
for clf, label in zip(all_clf, clf_labels):
    scores = cross_val_score(estimator=clf, X=X_train, y=y_train, cv=10, scoring='roc_auc')
    print 'Accuracy: %.2f (+/- %.2f) [%s]' % (scores.mean(), scores.std(), label)
'''
As we can see, the performance of the MajorityVotingClassifier has substantially improved
over the individual classifiers in the 10-fold cross-validation evaluation.'''

# Evaluating and tuning the ensemble classifier
'''
the MajorityVoteClassifier generalizes well to unseen data. We should remember that
the test set is not to be used for model selection; its only purpose is to report an unbiased
estimate of the generalization performance of a classifier system. The code is as follows:'''
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
import matplotlib.pyplot as plt
colors = ['black', 'orange', 'blue', 'green']
linestyles = [':', '--', '-.', '-']
for clf, label, clr, ls \
    in zip(all_clf, clf_labels, colors, linestyles):
    # assuming the label of the positive class is 1
    y_pred = clf.fit(X_train, y_train).predict_proba(X_test)[:, 1]
    fpr, tpr, thresholds = roc_curve(y_true=y_test, y_score=y_pred)
    roc_auc = auc(x=fpr, y=tpr)
    plt.plot(fpr, tpr, color=clr, linestyle=ls, label='%s (auc = %0.2f)' % (label, roc_auc))

plt.legend(loc='lower right')
plt.plot([0, 1], [0, 1], linestyle='--', color='gray', linewidth=2)
plt.xlim([-0.1, 1.1])
plt.ylim([-0.1, 1.1])
plt.grid()
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.show()
'''
As we can see in the resulting ROC, the ensemble classifier also performs well on the
test set (ROC AUC = 0.95), whereas the k-nearest neighbors classifier seems to be
overfitting the training data (training ROC AUC = 0.93, test ROC AUC = 0.86):'''
'''
Since we only selected two features for the classification examples, it would be interesting
to see what the decision region of the ensemble classifier actually looks like. Although it is
not necessary to standardize the training features prior to model fitting because our logistic
regression and k-nearest neighbors pipelines will automatically take care of this, we will
standardize the training set so that the decision regions of the decision tree will be on the
same scale for visual purposes. The code is as follows:'''
sc = StandardScaler()
X_train_std = sc.fit_transform(X_train)
from itertools import product
x_min = X_train_std[:, 0].min() - 1
x_max = X_train_std[:, 0].max() + 1
y_min = X_train_std[:, 1].min() - 1
y_max = X_train_std[:, 1].max() + 1

xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
                      np.arange(y_min, y_max, 0.1))
f, axarr = plt.subplots(nrows=2, ncols=2,
                         sharex='col',
                         sharey='row',
                         figsize=(7, 5))

for idx, clf, tt in zip(product([0, 1], [0, 1]), all_clf, clf_labels):
    clf.fit(X_train_std, y_train)
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    axarr[idx[0], idx[1]].contour(xx, yy, Z, alpha=0.3)
    axarr[idx[0], idx[1]].scatter(X_train_std[y_train==0, 0],
                                                  X_train_std[y_train==0, 1],
                                                  c='red',
                                                  marker='o',
                                                  s=50)
    axarr[idx[0], idx[1]].scatter(X_train_std[y_train == 1, 0],
                                                  X_train_std[y_train == 1, 1],
                                                  c='red',
                                                  marker='o',
                                                  s=50)
    axarr[idx[0], idx[1]].set_title(tt)

plt.text(-3.5, -4.5, s='Sepal width [standardized]', ha='center', va='center', fontsize=12)
plt.text(-10.5, 4.5,
          s='Petal length [standardized]',
          ha='center', va='center',
          fontsize=12, rotation=90)
plt.show()

'''
Interestingly but also as expected, the decision regions of the ensemble classifier seem
to be a hybrid of the decision regions from the individual classifiers. At first glance, the
majority vote decision boundary looks a lot like the decision boundary of the k-nearest
neighbor classifier. However, we can see that it is orthogonal to the y axis for sepal width
 ≥1, just like the decision tree stump:'''
'''
Before you learn how to tune the individual classifier parameters for ensemble classification,
let's call the get_params method to get a basic idea of how we can access the individual
parameters inside a GridSearch object:'''
mv_clf.get_params()

'''
Based on the values returned by the get_params method, we now know how to access the
individual classifier's attributes. Let's now tune the inverse regularization parameter C of the
logistic regression classifier and the decision tree depth via a grid search for demonstration
purposes. The code is as follows:
'''
from sklearn.grid_search import GridSearchCV
params = {'decisiontreeclassifier__max_depth': [1, 2], 'pipeline-1__clf__C': [0.001, 0.1, 100.0]}
grid = GridSearchCV(estimator=mv_clf, param_grid=params, cv=10, scoring='roc_auc')
grid.fit(X_train, y_train)

'''

After the grid search has completed, we can print the different hyperparameter value combinations
and the average ROC AUC scores computed via 10-fold cross-validation. The code is as follows:'''
for params, mean_score, scores in grid.grid_scores_:
    print("%0.3f+/-%0.2f %r"
        % (mean_score, scores.std() / 2, params))

print 'Best parameters: %s' % grid.best_params_

print 'Accuracy: %.2f' % grid.best_score_

'''
As we can see, we get the best cross-validation results when we choose a lower regularization strength
(C = 100.0) whereas the tree depth does not seem to affect the performance at all, suggesting that a decision
stump is sufficient to separate the data. To remind ourselves that it is a bad practice to use the test dataset
more than once for model evaluation, we are not going to estimate the generalization performance of the tuned
hyperparameters in this section. We will move on swiftly to an alternative approach for ensemble learning: bagging.'''

self_practice/Chapter 7 Combining Different Models for Ensemble Learning part I.py


import numpy as np

def _average_path_length(n):
    """
    Average path length formula.
    """
    # Thank you Matwey.
    return 2.0 * (np.log(n) + np.euler_gamma + 1 / (2 * n) - 1 / (12 * n ** 2) - 1.0)

def average_path_length(n):
    """
    Average path length computation.

    Parameters
    ----------
    n
        Either array of tree depths to computer average path length of
        or one tree depth scalar.

    Returns
    -------
    Average path length.
    """
    if np.isscalar(n):
        if n <= 1:
            apl = 0
        else:
            apl = _average_path_length(n)
    else:
        n = np.asarray(n)
        apl = np.zeros_like(n)
        apl[n > 1] = _average_path_length(n[n > 1])

    return apl


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

# code starts here
df = pd.read_csv(path)

# Probability that the borrower's fico score above 700
p_a = len(df[df['fico'] > 700]) / len(df)

# Probability that the purpose of the borrower is to consolidate debt
p_b = len(df[df['purpose'] == 'debt_consolidation']) /len(df)

# Subset showing only borrowers with purpose for debt consolidation
df1 = df[df['purpose'] == 'debt_consolidation']

# Probability that borrowers with purpose as debt consolidation given that their fico score is above 700
p_a_b = len(df1[df1['fico'] > 700]) / len(df1)

# Check for independency
result = p_a_b == p_b
print("If the purpose of a borrower is debt consolidation then does his fico score get afftected? \n Answer :", result)
# code ends here

# --------------
# code starts here
# Probability that the loan has been paid back
S = df
A = df[df['paid.back.loan'] == 'Yes']
prob_lp = len(A) / len(S)
print("Probability that the loan has been paid back is",round(prob_lp, 2))
print('-'*80)

# Probability that the customer has met the credit underwriting criteria of LendingClub.com
B = df[df['credit.policy'] == 'Yes']
prob_cs = len(B) / len(S)
print("Probability that the customer has met the credit underwriting criteria is",round(prob_cs, 2))
print('-'*80)

# Probability that the customers who have paid back the loan and has met the credit policy
new_df = df[df['paid.back.loan'] == 'Yes']
A_n_B = new_df[new_df['credit.policy'] == 'Yes']
prob_pd_n_cs = len(A_n_B) / len(df)
print("Probability that the customers who have paid back the loan and has met the credit policy is",round(prob_pd_n_cs, 2))
print('-'*80)

# Probability that the customer has paid back the loan given that the credit policy was followed
prob_pd_cs = prob_pd_n_cs / prob_lp
print("Probability that the customer has paid back the loan given that the credit policy was followed is",round(prob_pd_cs, 2))
print('-'*80)

# Probability that the customer has followed the credit policy given that he has paid back the loan
bayes = prob_pd_cs * prob_lp / prob_cs
print("Probability that the customer has followed the credit policy given that he has paid back the Loan :",round(bayes, 2))

# code ends here

# --------------
# code starts here
import seaborn as sns

# Visualization of the initial customer data
plt.figure(figsize=[10,7])
plt.xticks(rotation=45)
sns.countplot(df['purpose'])
plt.show()

# Subsetting the customer data who haven't paid back their loan
df1 = df[df['paid.back.loan'] == 'No']

# Visualization of the customer data who havent paid back
plt.figure(figsize=[10,7])
plt.xticks(rotation=45)
sns.countplot(df1['purpose'])
plt.show()
# code ends here

# --------------
# code starts here
inst_median = df['installment'].median()
inst_mean = df['installment'].mean()

# code starts here
inst_median = df['installment'].median()
inst_mean = df['installment'].mean()
print("Median of the overall monthly installments paid by the borrowers is", round(inst_median, 2))
print('-'*50)
print("Mean of the overall monthly installments paid by the borrowers is", round(inst_mean, 2))
print('-'*50)

# Distribution of monthly installments of the borrowers
plt.figure(figsize=[10,7])
df['installment'].plot.hist()
plt.title('Average monthly installments of the borrowers')
plt.xlabel('Monthly installments')
plt.show()

# Annual income distribution of the borrowers
plt.figure(figsize=[10,7])
df['log.annual.inc'].plot.hist()
plt.title('Average annual income of the borrowers')
plt.xlabel('Annual Income(Lpa)')
plt.show()
# code ends here
# code ends here


# UoSM Python cheat sheet

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

# find roots
from scipy.optimize import bisect, newton, brentq, fsolve

# find minimum
from scipy.optimize import fmin

# interpolation
# from scipy.interpolate import interp1d

# integration
from scipy.integrate import trapz, quad

# ordinary diff. eqs.
from scipy.integrate import odeint

# reverse an array
# arr = [1,2,3,4,5]
# arr[::-1]

# access row/column in 2D array
# a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# a[i]  <== access rows
# a[:,i]  <== access column

# create array
# np.linspace(0, 100, )  <== (inclusive, inclusive, no. of data points)
#
# create array
# np.arange(x, y, z)  <== (inclusive, exclusive, interval size)


from scipy.integrate import quad
from scipy.optimize import root_scalar
import numpy as np

def get_mp_pdf(beta):
    """
    Gets the MP PDF.

    Parameters
    ----------
    beta: float
        TODO: document

    Output
    ------
    pdf, beta_minus, beta_plus

    pdf: callable
        The pdf function.

    beta_minus, beta_plus: float
        The lower and upper bound of the support.
    """

    beta_minus = (1 - np.sqrt(beta)) ** 2
    beta_plus = (1 + np.sqrt(beta)) ** 2

    def pdf(x):
        # assert (beta_minus <= t) and (t <= beta_plus)
        return np.sqrt((beta_plus - x) * (x - beta_minus)) / (2 * np.pi * x * beta)

    return pdf, beta_minus, beta_plus

def get_mp_cdf(beta):
    """
    Gets the MP CDF

    Parameters
    ----------
    beta: float
        TODO

    Output
    ------
    cdf, beta_minus, beta_plus

    cdf: callable
        The cdf function.

    beta_minus, beta_plus: float
        The lower and upper bound of the support.
    """
    pdf, beta_minus, beta_plus = get_mp_pdf(beta)

    def cdf(x):
        return quad(func=pdf, a=beta_minus, b=x)[0]

    return cdf, beta_minus, beta_plus

def compute_mp_quantile(beta, q=0.5):
    """
    Computes the qualtile for the MP distribution.

    Parameters
    ----------
    beta: float
        TODO

    q: float
        The desired quantile.

    """
    assert 0 < q and q < 1
    cdf, beta_minus, beta_plus = get_mp_cdf(beta)

    def root_func(x):
        return cdf(x) - q

    return root_scalar(f=root_func, method=None,
                       bracket=(beta_minus, beta_plus)).root


import sympy

# Used this to search for all sequences below 1e9.
# There are 8 sequences of length 13 or more with differences of
# 8 or less between successive primes.  The first prime in each
# sequence along with the sequence lengths are given here:
#
# In [616]: find_sequence(1,1_000_000_000,8,13)
#
#    First prime   Length
#    --------------------
#    2             30
#    2657          14
#    14713         13
#    86966771      13
#    172039573     13
#    296931731     13
#    369008657     13
#    875753231     13
#
# Some more details
#
#    FirstPrime  SeqLength  LastPrime  DeltaPrime  MeanDiff
# 0           2         30        113         111  3.827586
# 1        2657         14       2719          62  4.769231
# 2       14713         13      14783          70  5.833333
# 3    86966771         13   86966833          62  5.166667
# 4   172039573         13  172039633          60  5.000000
# 5   296931731         13  296931797          66  5.500000
# 6   369008657         13  369008729          72  6.000000
# 7   875753231         13  875753299          68  5.666667
#
#
# The runtime is substantial (about an hour) with the first
# three found almost instantly and the rest taking proportionally
# longer.
#
# FYI: the last value is sympy.prime(44833217) where sympy.prime(1)=2
#

def find_sequence(nstart=2,nstop=1000,maxdiff=8,minlength=13):
    """
    Search for sequences of prime numbers that meet the following
    criteria:

    difference between successive primes <= maxdiff
    length of sequence >= minlength

    Output the first prime in each sequence and the sequence length
    """
    zzz=[]
    lastprime=-1000
    firstprime=0
    seqlen=1
    for i in sympy.primerange(nstart,nstop):
        if (i-lastprime)<=maxdiff:
            seqlen=seqlen+1
        else:
            # Found the end of a sequence
            # If the last sequence was long enough then output the specs
            if seqlen>=minlength:
                print(firstprime,seqlen)
                zzz.append([firstprime,seqlen])
            seqlen=1
            firstprime=i
        lastprime=i
    # Process any sequence that is in progress when the loop ends
    if seqlen>=minlength:
        print(firstprime,seqlen)
        zzz.append([firstprime,seqlen])
    return(zzz)


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

class Hodgkin_Huxley:

    def __init__(self):
        # Membrane capacitance and potential
        self.C_m = 1.0
        self.V_m = -70.0

        # Max sodium conductance and membrane potential
        self.g_Na = 120.0
        self.V_Na = 56.0

        # Max potassium conductance and membrane potential
        self.g_K = 36.0
        self.V_K = -77.0

        # Max leak conductance and membrane potential
        self.g_l = 0.3
        self.V_l = -60

        # Time to integrate over
        self.time = np.arange(0.0, 1000.0, 0.1)

        # ion variables
        self.m = 0.05
        self.h = 0.6
        self.n = 0.32

    def I_inj(self, t):
        return np.piecewise(t, [(t < 500) * (t >= 200), (t < 950) * (t >= 700)], [10, 50])
        #return np.piecewise(self.time, [(self.time< 500)*(self.time>=200), (self.time< 950)*(self.time>=700)], [10,50])

    # potassium rate functions
    def a_n(self):
        return 0.01 * (self.V_m + 55) / (1 - np.exp(-(self.V_m + 55.0) / 10)) # + 1.E-7)

    def B_n(self):
        return 0.125 * np.exp(-(self.V_m + 65.0) / 80)

    # sodium rate functions
    def a_m(self):
        return 0.1 * (40 + self.V_m) / (1.0 - np.exp(-(self.V_m + 40.0) / 10.0)) # + 1.E-7)

    def a_h(self):
        return 0.07 * np.exp(-(self.V_m + 65.0) / 20.0)

    def B_m(self):
        return 4 * np.exp(-(self.V_m + 65.0) / 18.0)

    def B_h(self):
        return 1 / (np.exp(-(35 + self.V_m) / 10) + 1)

    # current functions
    def K_current(self):
        return self.g_K * self.n**4 * (self.V_m - self.V_K)

    def Na_current(self):
        return self.g_Na * self.m**3 * self.h * (self.V_m - self.V_Na)

    def leak_current(self):
        return self.g_l * (self.V_m - self.V_l)

    def hh_model(self, y, t, parameters):
        '''Takes in y dynamics variables, V_m, n, m and h. Array of time steps to integrate over.
        List of constant parameters, reversal potentials and conductances

        Returns values for derivatives w.r.t time for Voltage and ion rate coefficience n, m and h.
        '''
        self.V_m, self.n, self.m, self.h = y
        self.g_K, self.V_K, self.g_Na, self.V_Na, self.g_l, self.C_m = parameters

        # Total current through the membrane
        dVdt = self.I_inj(t) - self.K_current() - self.Na_current() - self.leak_current() / self.C_m

        # Derivative of n, potassium channel activation, w.r.t. time
        dndt = self.a_n() * (1 - self.n) - self.B_n() * self.n

        # Derivative of m, sodium channel activion, w.r.t. time
        dmdt = self.a_m() * (1 - self.m) - self.B_m() * self.m

        # Derivative of h, sodium channel in-activion, w.r.t. time

        dhdt = self.a_h() * (1 - self.h) - self.B_h() * self.h

        return [dVdt, dndt, dmdt, dhdt]

    def simulate(self):

        y0 = [self.V_m, self.n, self.m, self.h]
        parameters = [self.g_K, self.V_K, self.g_Na, self.V_Na, self.g_l, self.C_m]

        sol = odeint(self.hh_model, y0, self.time, args=(parameters,))
        return sol

model = Hodgkin_Huxley()
model.simulate()
#print(model.sol)


import numpy as np

def wmean(x, weights):
    """Returns Weighted Mean. Ignores NaNs and handles infinite weights.

    Parameters
    ----------
    x: array-like [n_samples]
        An array-like object that contains the data.

    weights: array-like [n_samples]
        An array-like object that contains the corresponding weights.

    Returns
    ----------------------------------
    m: number
        The weighted mean.
    """

    # Flatten x and weights
    x = x.flatten()
    weights = weights.flatten()

    # Find NaNs
    nans = np.isnan(x)
    infs = np.isinf(weights)

    # If all x are nans, return np.nan
    if nans.all() == True:
        m = np.nan
        return m

    # If there are infinite weights, use the corresponding x
    if infs.any() == True:
        m = np.nanmean(x[infs])
        return m

    # Set NaNs to zero
    x[nans] = 0
    weights[nans] = 0

    # Normalize the weights + calculate Weighted Mean
    weights = weights / np.sum(weights)
    m = np.matmul(weights, x)
    return m


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 12 22:16:41 2022

@author: m102324
"""
import math
import logging
import numpy as np
from cobindability import version

__author__ = "Liguo Wang"
__copyright__ = "Copyleft"
__credits__ = []
__license__ = "MIT"
__version__ = version.version
__maintainer__ = "Liguo Wang"
__email__ = "wang.liguo@mayo.edu"
__status__ = "Development"

def ov_coef(x, y, xy, g):
	"""
	Calculate the overlap coefficient.

	Parameters
	----------
	x : int
		The cardinality of A.
	y : int
		The cardinality of B.
	xy : int
		The cardinality of A AND B.
	g : int
		The cardinality of background.

	Returns
	-------
	float
		Overlap coefficient.
	"""
	if xy > x or xy > y:
		logging.error("Invalid parameters.")
	if min(x,y,xy) < 0:
		logging.error("Invalid parameters.")
	if x == 0 or y == 0 or xy == 0:
		return 0
	else:
		return xy/(math.sqrt(x * y))

def ov_jaccard(x, y, xy, g):
	"""
	Calculate the Jaccard's coefficient (https://en.wikipedia.org/wiki/Jaccard_index).

	Parameters
	----------
	x : int
		The cardinality of A.
	y : int
		The cardinality of B.
	xy : int
		The cardinality of A AND B.
	g : int
		The cardinality of background.

	Returns
	-------
	float
		Overlap coefficient.
	"""
	if xy > x or xy > y:
		logging.error("Invalid parameters.")
	if min(x,y,xy) < 0:
		logging.error("Invalid parameters.")
	if x == 0 or y == 0:
		return 0
	else:
		return xy/(x + y - xy)

def ov_ss(x, y, xy, g):
	"""
	Calculate the Szymkiewicz–Simpson coefficient, (https://en.wikipedia.org/wiki/Overlap_coefficient).

	Parameters
	----------
	x : int
		The cardinality of A.
	y : int
		The cardinality of B.
	xy : int
		The cardinality of A AND B.
	g : int
		The cardinality of background.

	Returns
	-------
	float
		Szymkiewicz–Simpson coefficient.
	"""
	if xy > x or xy > y:
		logging.error("Invalid parameters.")
	if min(x,y,xy) < 0:
		logging.error("Invalid parameters.")
	if x == 0 or y == 0:
		return 0
	else:
		return xy/min(x,y)

def ov_sd(x, y, xy, g):
	"""
	Calculate the Sørensen–Dice coefficient (https://en.wikipedia.org/wiki/Sørensen–Dice_coefficient).
	Sørensen–Dice coefficient also called "Sørensen–Dice index", "Sørensen index" or "Dice's coefficient".

	Parameters
	----------
	x : int
		The cardinality of A.
	y : int
		The cardinality of B.
	xy : int
		The cardinality of A AND B.
	g : int
		The cardinality of background.

	Returns
	-------
	float
		Overlap coefficient.
	"""
	if xy > x or xy > y:
		logging.error("Invalid parameters.")
	if min(x,y,xy) < 0:
		logging.error("Invalid parameters.")
	if x == 0 or y == 0:
		return 0
	else:
		return 2*xy/(x + y)

def pmi_value(x, y, xy, g):
	"""
	Calculate the pointwise mutual information (PMI).

	Parameters
	----------
	x : int
		The cardinality of A.
	y : int
		The cardinality of B.
	xy : int
		The cardinality of A AND B.
	g : int
		The cardinality of background.

	Returns
	-------
	float
		Pointwise mututal information.
	"""
	if g <= 0:
		logging.error("The cardinality of background must be a postive integer.")
	if xy > x or xy > y:
		logging.error("Invalid parameters.")
	if x > g or y > g:
		logging.error("Invalid parameters.")
	if min(x,y,xy) < 0:
		logging.error("Invalid parameters.")
	px = x/g
	py = y/g
	pxy = xy/g
	if pxy == 0:
		return -np.inf
	else:
		return np.log(pxy) - np.log(px) - np.log(py)

def npmi_value(x, y, xy, g):
	"""
	Calculate the normalized pointwise mutual information (NPMI).

	Parameters
	----------
	x : int
		The cardinality of A.
	y : int
		The cardinality of B.
	xy : int
		The cardinality of A AND B.
	g : int
		The cardinality of background.

	Returns
	-------
	float
		Normalized pointwise mutual information.
	"""
	if g <= 0:
		logging.error("The cardinality of background must be a postive integer.")
	if xy > x or xy > y:
		logging.error("Invalid parameters.")
	if x > g or y > g:
		logging.error("Invalid parameters.")
	if min(x,y,xy) < 0:
		logging.error("Invalid parameters.")
	px = x/g
	py = y/g
	pxy = xy/g
	if pxy == 0:
		return -1
	else:
		return np.log(px*py)/np.log(pxy) - 1


import math
import numpy as np

def interpolateDirections(dir1_xyz, dir2_xyz, N):
#
# interpolate two direction vectors by performing spherical (rotational)
# linear interpolation, generating direction vectors in between the two,
# across the great circle passing from them. The angle between the two is
# divided in N intervals, hence N+1 vectors are returned, including the two
# original ones.
#
# dir1_xyz: 	numpy array, star vector
# dir2_xyz: 	numpy array, end vector
# N: 		integer, number of intervals to interpolate

	angle12 = math.acos(dir1_xyz.dot(dir2_xyz))
	cross12 = np.cross(dir1_xyz, dir2_xyz)
	purpvec12 = cross12/math.sqrt(cross12.dot(cross12))

	dtheta = angle12/N
	psi = np.linspace(0,angle12,N+1)
	interp_dirs_xyz = []
	for x in psi:
		dirn_xyz = dir1_xyz * math.cos(x) + np.cross(purpvec12,dir1_xyz)*math.sin(x) + purpvec12.dot(np.cross(purpvec12,dir1_xyz))*(1-math.cos(x))
		interp_dirs_xyz.append(dirn_xyz)

        return np.array(interp_dirs_xyz)


import numpy as np
import tensorflow as tf
from tensorflow import keras

# Include preprocessing in model #1
means = np.mean(X_train, axis=0, keepdims=True)
stds = np.std(X_train, axis=0, keepdims=True)
eps = keras.backend.epsilon()
model = keras.models.Sequential([
    keras.layers.Lambda(lambda inputs: (inputs - means) / (stds + eps)),
[...] # other layers
])

# Include preprocessing in model #2
class Standardization(keras.layers.Layer):
    def adapt(self, data_sample):
        self.means_ = np.mean(data_sample, axis=0, keepdims=True)
        self.stds_ = np.std(data_sample, axis=0, keepdims=True)
    def call(self, inputs):
        return (inputs - self.means_) / (self.stds_ + keras.backend.epsilon())

std_layer = Standardization()
std_layer.adapt(data_sample)

model = keras.Sequential()
model.add(std_layer)
[...] # create the rest of the model
model.compile([...])
model.fit([...])

# Do batch preprocessing before sending data through model
def preprocess(line):
    defs = [0.] * n_inputs + [tf.constant([], dtype=tf.float32)]
    fields = tf.io.decode_csv(line, record_defaults=defs)
    x = tf.stack(fields[:-1])
    y = tf.stack(fields[-1:])
    return (x - X_mean) / X_std, y

shuffle_buffer_size=10000
n_parse_threads=5
batch_size=32
repeat=1

dataset = dataset.map(preprocess, num_parallel_calls=n_parse_threads)
dataset = dataset.shuffle(shuffle_buffer_size).repeat(repeat)
dataset = dataset.batch(batch_size).prefetch(1)


from typing import Tuple, Union, Sequence, Optional
from collections.abc import Iterable

import numpy as np

from PDE.typing import FloatArray, VecApply

class Derivator:
    FDM_ORDER: set[int] = {2, 4}
    MAX_DERIVATIV_ORDER: int = 2
    MAX_SYSTEM_DIM: int = 2
    # (order, derivative) -> Coefs
    C_COEF: dict[tuple[int, int], list[float]] = {
        (2, 1): [-0.5, 0, 0.5],
        (2, 2): [1, 2, 1],
        (4, 1): [1/12, -2/3, 0, 2/3, -1/12],
        (4, 2): [-1/12, 4/3, -5/2, 4/3, -1/12]
    }
    A_COEF: dict[tuple[int, int], list[float]] = {
        (2, 1): [-1.5, 2, -0.5],
        (2, 2): [2, -5, 4, -1],
        (4, 1): [-25/12, 4, -3, 4/3, -1/4],
        (4, 2): [15/14, -77/6, 107/6, -13, 61/12]
    }

    def __init__(
        self,
        system_shape: Tuple[int],
        derive_order: int,
        FDM_order: int,
        space_interval: Union[Sequence[float], float],
        boundary_condition: Optional[VecApply]
        ):
        if derive_order > type(self).MAX_DERIVATIV_ORDER:
            raise ValueError(f"Derive order should not exces {MAX_DERIVATIV_ORDER}")
        if FDM_order not in type(self).FDM_ORDER:
            raise ValueError(f"Derivation methode order not supported")
        self._dim = system_shape
        self.FDM_order = FDM_order
        if isinstance(space_interval, Iterable):
            self._inverth = [1 / h for h in list(space_interval)]
        else:
            self._inverth = [1 / space_interval] * len(self._dim)
        self._deriv = {i: list() for i in range(1, derive_order + 1)}
        self._bound = boundary_condition
        for key in self._deriv:
            for i in range(1, len(self._dim) + 1):
                self._deriv[key].append(self.create_derive(key, i))

    def create_derive(self, dorder: int, axis: int) -> VecApply:
        c = np.array(type(self).C_COEF[(self.FDM_order, dorder)])
        a_f = np.array(type(self).A_COEF[(self.FDM_order, dorder)])
        a_b = -a_f[::-1]
        clim = len(c) // 2
        alim = len(a_f)
        doc= f"""
        Centered finite differences methode
        {dorder} order derivative vectorized over {axis + 1}th dimension

        centered coeficient: {c}
        forward coeficient: {a_f}
        backward coreficient: -forward_coeficient\
        """
        if len(self._dim) == 1:
            M = np.zeros((self._dim[0], self._dim[0]))
            for row in range(len(M)):
                if row < clim:
                    M[row, row:(alim + row)] =a_f
                elif row >= self._dim[0] - clim:
                    M[row, (row - alim + 1):(row + 1)] = a_b
                else:
                    M[row, (row - clim):(row + clim + 1)] = c
            M = M * self._inverth[axis - 1] ** dorder
            def derive(u: FloatArray) -> FloatArray:
                bounded = self._bound(u)
                return M.dot(bounded)
            derive.__doc__ = doc
            return derive

    def D(self, order: int, axis: int, u: FloatArray) -> FloatArray:
        return self._deriv[order][axis](u)