#!/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 ###


### Univariate stochastic gradient descent algorithm

### author: Dr. Marko Mitic
### year 2015
### contact: miticm@gmail.com

import numpy as np
import matplotlib.pyplot as plt

class StochasticGD: #Main class for Univariate Stochastic Gradient Descent

    def __init__(self, data):
        ### data is given in two-column format, first column is input, second column is output
        self.x=data[:,0]
        self.y=data[:,1]
        self.m=len(data[:,0])

   ### Just for stochastic GD

    def selectrandomsample(self,rnd):
        self.xrnd=self.x[rnd]
        self.yrnd=self.y[rnd]
        return self.xrnd, self.yrnd

    def plotdata(self):
        ###
        plt.figure()
        plt.plot(self.x, self.y, 'ro')
        plt.show()

    def hypothesis(self, theta0, theta1):
        "Return univariate hypothesis"
        self.t0=theta0
        self.t1=theta1
        self.hy=self.t0+self.t1*self.xrnd ##current hypothesis
        return self.hy

    def cost_function(self):
        "Return Cost function for hypothesis hy(of input x) and output y"
        self.J=(1./(2.*self.m))*((self.hy-self.yrnd)**2)
        return self.J

    def gradientdescent(self,alpha):
        "Perform gradient descent algorithm"
        self.a=alpha #leaarning rate

        temptheta0=self.t0-self.a*(1./self.m)*(self.hy-self.yrnd)
        temptheta1=self.t1-self.a*(1./self.m)*((self.hy-self.yrnd)*self.xrnd)

        self.t0=temptheta0
        self.t1=temptheta1
        return self.t0, self.t1

    def finalhypothesis(self, theta0, theta1):
        "Return univariate hypothesis"
        self.t0=theta0
        self.t1=theta1
        self.hyfin=self.t0+self.t1*self.x ##current hypothesis
        return self.hyfin

    def plothyp(self,col,tlt):
        "Plot current hypothesis"
        plt.figure()
        plt.plot(self.x, self.y, 'ro')
        plt.plot(self.x, self.hyfin, col)
        #plt.gca().set_xlim(left=0)
        #plt.gca().set_ylim(bottom=0)
        plt.title(tlt)
        plt.show()

## END CLASS

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

### Univariate stochastic gradient descent algorithm

### author: Dr. Marko Mitic
### year: 2015
### contact: miticm@gmail.com

#import data
data = np.loadtxt("data_GD.txt", comments="#", delimiter=",", unpack=False)

gd=StochasticGD(data)
## visualize the data

gd.plotdata()
##starting values for hupothesis hy=theta0+theta1*x
gd.t0=1.5
gd.t1=-0.7

#test and plot the starting hypothesis
### Stochastic gradient descent
rnd=np.random.randint(0,gd.m)
[gd.xrnd,gd.yrnd]=gd.selectrandomsample(rnd)
gd.hy=gd.hypothesis(gd.t0,gd.t1)

#calculate initial gradient
gd.J=gd.cost_function()

#define error tolerance:
tol=5

#Adaptibile Learning rate
alpha=0.08

for i in range (0,100):

    rnd=np.random.randint(0,gd.m)
    [gd.xrnd,gd.yrnd]=gd.selectrandomsample(rnd)
    gd.hy=gd.hypothesis(gd.t0,gd.t1)
    gd.J=gd.cost_function()
    [gd.t0,gd.t1]=gd.gradientdescent(alpha) ##learning rate==0.002    ## interesting results with 0.2 ;)

    print "Current cost is", gd.J
    print 'Parameters Theta0 and Theta1 are {} and {}, respectively' .format(gd.t0, gd.t1)

print 'Final parameters Theta0 and Theta1 found by GD are {} and {}, respectively' .format(gd.t0, gd.t1)

gd.hyfin=gd.finalhypothesis(gd.t0,gd.t1)

gd.plothyp('k','Final hypothesis')

##END


import numpy as np

def pos_in_box(pos, lbox):
  """Positions in [-lbox/2, lbox/2)

  Args:
    pos (np.array): positions in open BC
    lbox (float): cubic box side length
  Return:
    np.array: positions in box centered at 0
  """
  return (pos+lbox/2.) % lbox - lbox/2.

def cubic_pos(nx, ndim=3):
  """Initialize simple cubic lattice at integer points

  Args:
    nx (int): number of points along each dimension
    ndim (int): number of spatial dimensions
  Return:
    np.array: simple cubic lattice positions, shape (nx**3, ndim)
  """
  nl = [range(nx)]*ndim
  pos = np.stack(
    np.meshgrid(*nl, indexing='ij'),
    axis=-1
  ).reshape(-1, ndim)
  return pos

def sc(npart, lbox, ndim=3):
  """Simple cubic positions

  Args:
    npart (int): number of particles
    lbox (float): side length of cubic box
  Return:
    np.array: simple cubic lattice
  """
  nx = int(round(npart**(1./3)))
  if nx**3 != npart:
    msg = 'SC npart %d is not a perfect cube' % npart
    raise RuntimeError(msg)
  pos = lbox*cubic_pos(nx)/nx
  assert len(pos) == npart
  return pos_in_box(pos, lbox)

def bcc(npart, lbox, ndim=3):
  """Body-centered cubic positions

  Args:
    npart (int): number of particles
    lbox (float): side length of cubic box
  Return:
    np.array: body-centered cubic lattice
  """
  nx = int(round((npart/2.)**(1./3)))
  if 2*nx**3 != npart:
    msg = 'BCC npart %d is not 2* a perfect cube' % npart
    raise RuntimeError(msg)
  nvecs = cubic_pos(nx)
  pos = lbox*np.concatenate([
    nvecs/float(nx), (nvecs+0.5)/nx
  ], axis=0)
  assert len(pos) == npart
  return pos_in_box(pos, lbox)

def fcc(npart, lbox, ndim=3):
  """Face-centered cubic positions

  Args:
    npart (int): number of particles
    lbox (float): side length of cubic box
  Return:
    np.array: face-centered cubic lattice
  """
  nx = int(round((npart/4.)**(1./3)))
  if 4*nx**3 != npart:
    msg = 'FCC npart %d is not 4* a perfect cube' % npart
    raise RuntimeError(msg)
  nvecs = cubic_pos(nx)
  posul = [nvecs/float(nx)]
  for idim in range(ndim):
    shift = 0.5*np.ones(ndim)
    shift[idim] = 0
    posu = (nvecs+shift)/float(nx)
    posul.append(posu)
  pos = lbox*np.concatenate(posul, axis=0)
  assert len(pos) == npart
  return pos_in_box(pos, lbox)


import matplotlib.pyplot as plt
import numpy as np
from featureNormalize import *
from gradientDescent import *
from normalEqn import *

plt.ion()

# ===================== Part 1: Feature Normalization =====================
print('Loading Data...')
data = np.loadtxt('ex1data2.txt', delimiter=',', dtype=np.int64)
X = data[:, 0:2]
y = data[:, 2]
m = y.size

# Print out some data points
print('First 10 examples from the dataset: ')
for i in range(0, 10):
    print('x = {}, y = {}'.format(X[i], y[i]))

input('Program paused. Press ENTER to continue')

# Scale features and set them to zero mean
print('Normalizing Features ...')

X, mu, sigma = feature_normalize(X)
X = np.c_[np.ones(m), X]  # Add a column of ones to X

# ===================== Part 2: Gradient Descent =====================

# ===================== Your Code Here =====================
# Instructions : We have provided you with the following starter
#                code that runs gradient descent with a particular
#                learning rate (alpha).
#
#                Your task is to first make sure that your functions -
#                computeCost and gradientDescent already work with
#                this starter code and support multiple variables.
#
#                After that, try running gradient descent with
#                different values of alpha and see which one gives
#                you the best result.
#
#                Finally, you should complete the code at the end
#                to predict the price of a 1650 sq-ft, 3 br house.
#
# Hint: At prediction, make sure you do the same feature normalization.
#

print('Running gradient descent ...')

# Choose some alpha value
alpha = 0.03
num_iters = 400

# Init theta and Run Gradient Descent
theta = np.zeros(3)
theta, J_history = gradient_descent_multi(X, y, theta, alpha, num_iters)

# Plot the convergence graph
plt.figure()
plt.plot(np.arange(J_history.size), J_history)
plt.xlabel('Number of iterations')
plt.ylabel('Cost J')

# Display gradient descent's result
print('Theta computed from gradient descent : \n{}'.format(theta))

# Estimate the price of a 1650 sq-ft, 3 br house
# ===================== Your Code Here =====================
# Recall that the first column of X is all-ones. Thus, it does
# not need to be normalized.
price = 0  # You should change this
predict = np.array([1650, 3])
predict = (predict - mu) / sigma
predict = np.r_[1, predict]
price = np.dot(predict, theta)

# ==========================================================

print('Predicted price of a 1650 sq-ft, 3 br house (using gradient descent) : {:0.3f}'.format(price))

input('Program paused. Press ENTER to continue')

# ===================== Part 3: Normal Equations =====================

print('Solving with normal equations ...')

# ===================== Your Code Here =====================
# Instructions : The following code computes the closed form
#                solution for linear regression using the normal
#                equations. You should complete the code in
#                normalEqn.py
#
#                After doing so, you should complete this code
#                to predict the price of a 1650 sq-ft, 3 br house.
#

# Load data
data = np.loadtxt('ex1data2.txt', delimiter=',', dtype=np.int64)
X = data[:, 0:2]
y = data[:, 2]
m = y.size

# Add intercept term to X
X = np.c_[np.ones(m), X]

theta = normal_eqn(X, y)

# Display normal equation's result
print('Theta computed from the normal equations : \n{}'.format(theta))

# Estimate the price of a 1650 sq-ft, 3 br house
# ===================== Your Code Here =====================
price = 0  # You should change this
predict = np.array([1, 1650, 3])
price = np.dot(predict, theta)

# ==========================================================

print('Predicted price of a 1650 sq-ft, 3 br house (using normal equations) : {:0.3f}'.format(price))

input('ex1_multi Finished. Press ENTER to exit')


import numpy as np

def osc_2nd_order(U_0, omega, dt, T):
    """
    Solve u'' + omega**2*u = 0 for t in (0,T], u(0)=U_0 and u'(0)=0,
    by a central finite difference method with time step dt.
    """
    Nt = int(round(T/dt))
    u = np.zeros(Nt+1)
    t = np.linspace(0, Nt*dt, Nt+1)

    u[0] = U_0
    u[1] = u[0] - 0.5*dt**2*omega**2*u[0]
    for n in range(1, Nt):
        u[n+1] = 2*u[n] - u[n-1] - dt**2*omega**2*u[n]
    return u, t


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Apr  1 17:01:03 2020

@author: soumik
"""
# A SIR model for the spread of virus

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

N=10000     # Initial population
I=1         # Initially infected people
R=0         # 0 Recovered
S=N-I-R     # Susceptible (N=S+I+R)

radius = 1  # Radius of infection
prob = 0.2  # Probability of Infection
T=14        # Time-play

omega = 1                                               # Rate of social distancing
gamma = .5                                              # Rate of recovery
beta = 2.5           									# Reproduction Number
beta2 = radius*prob*T*(beta/(beta + omega + gamma))     # Rate of infection

def SIR(vals,t,b,g):
    S,I,R=vals

    dS=-b*S*I/N     # dS/dt
    dI=b*S*I/N - g*I
    dR=g*I

    return [dS,dI,dR]

t=np.linspace(0,4*T,1000)
x=[S,I,R]           # Initial params
rates=(beta2,gamma)
sol=odeint(SIR,x,t,args=rates)

# Plot ODE solutions
plt.plot(t,sol[:,0])
plt.plot(t,sol[:,1])
plt.plot(t,sol[:,2])

plt.xlabel('Time')
plt.ylabel('Population')
plt.legend(['S','I','R'],shadow=True)
plt.draw()
savefig("/home/soumik/Desktop/covid-19/results/"+"diff_radius"+str(radius)+"_prob"+str(prob)+"_omega"+str(omega)+".png",dpi=400)


import numpy as np
from collections import Counter

def entropy(labels:list) -> float:
    """Calculate the entropy of the given system

    Args:
        labels (list): Given state of the system

    Returns:
        float: Return the calculated entropy value of the given system
    """
    if not isinstance(labels, (list, np.ndarray)):
        raise TypeError('Labels must be a list or numpy array')

    label_size = len(labels)
    labels_prob = [value/label_size for value in Counter(labels).values()]

    return -np.sum(labels_prob * np.log2(labels_prob))

def information_gain(original_labels: list, original_labels_split: list) -> float:
    """Calculates the information gain after the system is split into different parts

    Args:
        original_labels (list): Original dataset
        original_labels_split (list): List of splits of original dataset

    Returns:
        float: Information gain obtained after the split
    """
    return entropy(original_labels) - np.sum([(len(split) / len(original_labels)) * \
        entropy(split) for split in original_labels_split])

def build_tree():
    pass


import logging
from functools import partial

import numpy as np

from src.local_optimization.gradient_descent import GradientDescent

logging.basicConfig(level=logging.INFO)

np.random.seed(21)

def f_eval(x, param):
    return param[0] * x + param[1]

def f_cost(param, x, y):
    y_estimate = f_eval(x, param)
    errors = y_estimate - y
    return np.mean(errors ** 2)

def main():
    x = np.arange(1, 100)
    param_true = np.array([1.0, 2.5])
    y = f_eval(x, param_true)
    f_step = lambda k: (0, 1e-3)
    optimizer = GradientDescent(f_cost=partial(f_cost, x=x, y=y), f_step=f_step)
    output = optimizer.run(np.random.randn(2))
    print(f"Param: {output.x}")
    print(f"Cost: {output.min_cost}")

if __name__ == "__main__":
    main()


#-------------------------------------------------------------------------------
# Name: covariance.py
# Purpose: This module provides apriori covariance matrix on parameter and data.
#
# 1) Uncertainties (variance) in data.
#    This covariance is assumed to be independent and the variance of
#    measurement is equally distributed.:
#                Cov_data = variance(d) * I,
#    where,
#         I - idendity matrix; d - data(attenuation) vector;
#
# 2) Variance in parameter represents confidence in parameter and as follows:
#         Cov_param(i,j) = ( sigmai*sigmaj ) * { exp(-0.5*( dmij/delta )^2)}
#    where,
#        sigmaij - scalar. Standard deviation of parameter at (i,j) location;
#        dij     - matrix. Distance which contains between ith and jth parameter.
#        decor   - scalar. Decorrelation distance [km]
#-------------------------------------------------------------------------------

from numpy import eye, around, exp

def covar_data(d_var):
    '''
    Returns data covariance matrix.
    Inputs:
        d_var - 1D array. Variance of data.
    Output:
        covar - 2D array. Data covariance matrix.
    '''
    covard = d_var * eye(len(d_var))
    return around(covard, 5)

def covar_param(p_var, decor, dij):
    '''
    Returns parameter covariance matrix.
    Inputs:
        p_var  - scalar. Parameter variance [-].
        decor  - scalar. Decorrelation distance [km].
        dij   - 2D array. Distance matrix [km].
    Output:
        covarp - 2D array. Parameter covariance.
    '''
    covarp = p_var * exp( -0.5 * (dij/ decor)**2 )
    return around(covarp, 5)


import sympy
import src.syntaxTree as syntaxTree
from src.visit import on, when

class Interpreter():
    @on("node")
    def visit(self, node):
        return "EMPTY"

    @when(syntaxTree.Command)
    def visit(self, node):
        for i in node.command:
            print(i.accept(self))

    @when(syntaxTree.VarExpr)
    def visit(self, node):
        return sympy.symbols(node.val)

    @when(syntaxTree.NumExpr)
    def visit(self, node):
        return node.val

    @when(syntaxTree.BinExpr)
    def visit(self, node):
        leftVal = node.left.accept(self)
        rightVal = node.right.accept(self)

        if node.op == "+":
            return sympy.simplify(leftVal) + sympy.simplify(rightVal)
        elif node.op == "-":
            return sympy.simplify(leftVal) - sympy.simplify(rightVal)
        elif node.op == "*":
            return sympy.simplify(leftVal) * sympy.simplify(rightVal)
        elif node.op == "/":
            return sympy.simplify(leftVal) / sympy.simplify(rightVal)
        elif node.op == "^":
            return sympy.simplify(leftVal) ** sympy.simplify(rightVal)
        elif node.op == "=":
            pass

        return None

    @when(syntaxTree.SolveExpr)
    def visit(self, node):
        equation = node.equation.accept(self)
        varDict = {}

        for i, j in zip(node.variables, node.values):
            varDict[i.accept(self)] = j.accept(self)

        return equation.evalf(subs=varDict)

    @when(syntaxTree.TrigExpr)
    def visit(self, node):
        equation = node.equation.accept(self)

        if node.function == "sin":
            return sympy.functions.sin(equation)
        elif node.function == "cos":
            return sympy.functions.cos(equation)
        elif node.function == "tg":
            return sympy.functions.tan(equation)
        elif node.function == "ctg":
            return sympy.functions.cot(equation)

    @when(syntaxTree.DiffExpr)
    def visit(self, node):
        function = node.function.accept(self)

        return sympy.diff(function, node.variables.accept(self))

    @when(syntaxTree.IntegrateExpr)
    def visit(self, node):
        function = node.function.accept(self)

        if node.start is None or node.end is None:
            return sympy.integrate(function, node.variable.accept(self))
        else:
            return sympy.integrate(function, (node.variable.accept(self), node.start.accept(self), node.end.accept(self)))

    @when(syntaxTree.LimitExpr)
    def visit(self, node):
        function = node.function.accept(self)

        if node.side is not None:
            if node.side == "left":
                return sympy.limit(function, node.variable.accept(self), node.value.accept(self), dir='-')
            elif node.side == "right":
                return sympy.limit(function, node.variable.accept(self), node.value.accept(self), dir='+')
        else:
            return sympy.limit(function, node.variable.accept(self), node.value.accept(self))


import numpy as np

def calculate_pooled_stdev(*arrays) -> float:
    """Compute pooled standard deviation from multiple arrays.

    https://en.wikipedia.org/wiki/Pooled_variance

    :param arrays: All arrays to pool
    :type arrays: np.ndarray, np.ndarray, ...

    :returns: Pooled standard deviation
    :rtype: float
    """
    pooled_variance_numerator = 0
    lengths = []
    k = len(arrays)

    for array in arrays:
        sample_variance = np.var(array, ddof=1)  # delta DF = 1 for unbiased
        pooled_variance_numerator += sample_variance * (len(array) - 1)
        lengths.append(len(array))

    pooled_variance_denominator = sum(lengths) - k
    pooled_variance = pooled_variance_numerator / pooled_variance_denominator

    return np.sqrt(pooled_variance)

def calculate_cohens_d(values_1: np.ndarray, values_2: np.ndarray) -> float:
    """Calculate Cohen's d using pooled standard deviation.

    :param values_1: First array of values
    :type values_1: np.ndarray

    :param values_2: Second array of values
    :type values_2: np.ndarray

    :returns: Cohen's d effect size
    :rtype: float
    """
    pooled_std = calculate_pooled_stdev(values_1, values_2)
    mu_1 = np.mean(values_1)
    mu_2 = np.mean(values_2)
    return np.abs(mu_1 - mu_2)/pooled_std

def calculate_cohens_f(*arrays) -> float:
    """Calculate Cohen's f using pooled standard deviation.

    tinyurl.com/4p47ffem
    sigma_m^2 = sum_{i=1}^G (n_i/N)(mu_i - mu_w)^2

    :param arrays: All arrays to pool
    :type arrays: np.ndarray, np.ndarray, ...

    :returns: Cohen's f effect size
    :rtype: float
    """
    pooled_std = calculate_pooled_stdev(*arrays)

    concat_array = np.concatenate(arrays)
    mu_total = np.mean(concat_array)

    effect_size_numerator = 0
    for array in arrays:
        mu_group = np.mean(array)
        effect_size_numerator += (
            len(array) / len(concat_array)
            * np.power(mu_group - mu_total, 2)
        )
    effect_size_numerator = np.sqrt(effect_size_numerator)

    return effect_size_numerator/pooled_std


#
# File: tridiagonal_exceptions.py
# Author: Ivan Gonzalez
#
"""Inverse power method applied to a tridiagonal matrix.

Adapted from Jaan Kiusalaas, Numerical Methods in Engineering with Python.
"""
import numpy as np
from LUdecomp3 import *
from tridiagonal_exceptions import TridiagonalException

def inversePower3(d, c, s, tol=1.0e-6, max_iterations=100):
    """ Inverse power method for triagonal matrix.

    Inverse power method applied to a tridiagonal matrix
    [A] = [c\d\c]. Returns the eigenvalue closest to 's'
    and the corresponding eigenvector.

    Parameters
    ----------
    d : a numpy array of ndim = 1.
        The matrix in the diagonal.
    c : a numpy array of ndim = 1.
        The matrix in the second-diagonal.
    s : a double.
        The eigenvalue you want to find its eigenvector. This is an
	approximate value.
    tol : a double (optional).
        The overlap between consecutive solutions has to be smaller than
	that to call the eigenvector converged.
    max_iterations : an int.
        The maximum number of iteration done before raising an exception.

    Returns
    -------
    s : a double.
        The eigenvalue corrected.
    x : a numpy array of ndim = 1.
        The corresponding eigenvector.

    Raises
    ------
    DMRGException
        if the number of iterations before converging exceeds
	`max_iterations`.
    """
    n = len(d)
    e = c.copy()
    cc = c.copy()                 # Save original [c]
    dStar = d - s                 # Form [A*] = [A] - s[I]
    LUdecomp3(cc,dStar,e)         # Decompose [A*]
    x = np.random.rand(n)         # Seed [x] with random numbers
    norm = np.linalg.norm(x)      # Normalize [x]
    x /= norm
    is_converged = False
    iteration = 0
    sign = 1.0
    while not is_converged:       # Begin iterations
	iteration +=1
	if iteration > max_iterations:
	    raise TridiagonalException('Inverse power method did not converge')
        xOld = x.copy()           # Save current [x]
        LUsolve3(cc,dStar,e,x)    # Solve [A*][x] = [xOld]
        norm = np.linalg.norm(x)  # Normalize [x]
        x /= norm
        if np.dot(xOld,x) < 0.0:     # Detect change in sign of [x]
            sign = -1.0
            x = -x
	overlap = np.linalg.norm(xOld - x)
        if overlap < tol:
	    is_converged =True
    s += sign/norm
    return s, x


import numpy as np
from scipy import stats

# ---------------------------
# Independent samples -------
# ---------------------------
def cles_ind(x1, x2):
    """Calc common language effect size

    Interpret as the probability that a score sampled
    at random from one distribution will be greater than
    a score sampled from some other distribution.

    Based on: http://psycnet.apa.org/doi/10.1037/0033-2909.111.2.361

    :param x1: sample 1
    :param x2: sample 2
    :return: (float) common language effect size
    """
    x1 = np.array(x1)
    x2 = np.array(x2)

    diff = x1[:, None] - x2
    cles = max((diff < 0).sum(), (diff > 0).sum()) / diff.size

    return cles

def rbc_ind(x1, x2):
    """Calculate rank-biserial correlation coefficient

    Output values range from [0, 1]; interpret as:
      * Values closer to 0 are a weaker effect
      * Values closer to 1 are a stronger effect

    :param x1: sample 1
    :param x2: sample 2
    :return: (float) rank-biserial correlation coefficient
    """
    n1 = x1.size
    n2 = x2.size

    u, _ = stats.mannwhitneyu(x1, x2)
    rbc = 1 - (2 * u) / (n1 * n2)

    return rbc

def calc_non_param_ci(x1, x2, alpha=0.05):
    """Calc confidence interval for 2 group median test

    Process:
      * Find all pairwise diffs
      * Sort diffs
      * Find appropriate value of k
      * Choose lower bound from diffs as: diffs[k]
      * Choose upper bound from diffs as: diffs[-k]

    Based on: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2545906/

    :param x1: sample 1
    :param x2: sample 2
    :param alpha: significance level
    :return: (tuple) confidence interval bounds
    """
    x1 = np.array(x1)
    x2 = np.array(x2)

    n1 = x1.size
    n2 = x2.size
    cv = stats.norm.ppf(1 - alpha / 2)

    # Find pairwise differences for every datapoint in each group
    diffs = (x1[:, None] - x2).flatten()
    diffs.sort()

    # For an approximate (1-a)% confidence interval first calculate K:
    k = int(round(n1 * n2 / 2 - (cv * (n1 * n2 * (n1 + n2 + 1) / 12) ** 0.5)))

    # The Kth smallest to the Kth largest of the n x m differences
    # n1 and n2 should be > ~20
    ci_lo = diffs[k]
    ci_hi = diffs[-k]

    return ci_lo, ci_hi

# ---------------------------
# Paired samples ------------
# ---------------------------
def cles_rel(x1, x2):
    """Calc common language effect size for paired samples

    Interpret as the probability that a pair's difference (x1 - x2)
    sampled at random will be greater than 0.

    :param x1: sample 1
    :param x2: sample 2
    :return: (float) common language effect size
    """
    x1 = np.array(x1)
    x2 = np.array(x2)

    diffs = x1 - x2
    # Convert differences to 0.0, 0.5, or 1.0:
    #   * 0.0 if x1 < x2
    #   * 0.5 if x1 == x2
    #   * 1.0 if x1 > x2
    diffs = np.where(diffs == 0.0, 0.5, diffs > 0)

    # Take average of array with [0s, 0.5s, 1s]
    # This indicates prob of pulling a random
    # diff and it being greater than 0
    return diffs.mean()

def rbc_rel(x1, x2):
    """Calculate rank-biserial correlation coefficient for paired samples

    Output values range from [-1, 1]; interpret as:
      * Values closer to 1 indicate that x1 is larger
      * Values closer to -1 indicate that x2 is larger

    :param x1: sample 1
    :param x2: sample 2
    :return: (float) rank-biserial correlation coefficient
    """
    x1 = np.array(x1)
    x2 = np.array(x2)

    diffs = x1 - x2
    diffs = diffs[diffs != 0]
    diff_ranks = stats.rankdata(abs(diffs))

    rank_sum = diff_ranks.sum()
    pos_rank_sum = np.sum((diffs > 0) * diff_ranks)
    neg_rank_sum = np.sum((diffs < 0) * diff_ranks)
    rbc = pos_rank_sum / rank_sum - neg_rank_sum / rank_sum

    return rbc


#All of these operations can be found in Kathrin Schacke's "On the Kronecker Product" written on August 1, 2013. This paper provides a review of the properties of the kronecker product, as well its history and applications. This code will implement many of the properties given in Section 2 : The Kronecker Product
#All comments that describe the functions below are directly quoted from Kathrin Schacke's paper.
#For all examples, c is a constant, and A and B are matrices
#I want these functions to be useful for when we have an arbitrarily sized list of matrices, and apply all of these operations to this list.
import numpy as np
from functools import reduce

def verifyInput(A):
    if type(A) == np.matrix:
        if len(A) == 0:
            assert "Can't apply operation, list of matrices is empty."

#Apply a constant to a matrix. When applying a constant to a kron product it doesn't matter where its applied, as long as its applied once.
#(cA) kron B == A kron (cB) == c(A kron B)
def applyConstant(A,constant):
    verifyInput(A)
    if(type(A) == list):
        return A[0] * constant
    else:
        return A * constant

#Taking the transpose of the kron prod is the same thing as taking the kron prod after
#(A kron B).T = A.T kron B.T
def transpose(A):
    verifyInput(A)
    if type(A) == list:
        A_T = []
        for a in A:
            A_T.append(a.T)
        return A_T
    else:
        return A.T

#Taking the complex conjugate before carrying out the Kron product yields the same results as doing so afterwards
#(A kron B)* = A* kron B*
def complexConjugate(A):
    verifyInput(A)
    if type(A) == list:
        A_Conj = []
        for a in A:
            A_Conj.append(np.conj(a))
        return A_Conj
    else:
        return np.conj(A)
#The trace of the kron product of two matrices is the product of the traces of the matrices
#trace(A kron B) = trace(A)trace(B)
def trace(A):
    verifyInput(A)
    if type(A) == list:
        return reduce(lambda x, y: np.trace(x)*np.trace(y), A)
    else:
        return np.trace(A)

#det(A kron B) = (det(A))^n * (det(B))^m where A is mxm and B is nxn
def det(A):
    verifyInput(A)
    if type(A) == list:
        if len(A) == 1:
            return np.linalg.det(A[0])
        power1 = None
        if len(A) == 2:
            power1 = A[1].shape[0]
        else:
            power1 = reduce(lambda x, y: x.shape[0]*y.shape[0], A[1:])
        power2 = A[0].shape[0]
        return det(A[0])**(power1) * det(A[1:])**(power2)
    else:
        return np.linalg.det(A)

def is_invertible(a):
    return a.shape[0] == a.shape[1] and np.linalg.matrix_rank(a) == a.shape[0]

def invert(A,allow_pinvert=True):
    verifyInput(A)
    if type(A) == list:
        A_inv = []
        for a in A:
            if(is_invertible(a)):
                A_inv.append(np.linalg.inv(a))
            elif(allow_pinvert == True):
                print("[Warning] Taking pinvert since matrix is not invertible")
                A_inv.append(pinvert(a))
            else:
                assert "Non invertible matrix given, and not allowed to take pinvert"
        return A_inv
    else:
        if(is_invertible(A)):
            return np.linalg.inv(A)
        elif(allow_pinvert == True):
            print("[Warning] Taking pinvert since matrix is not invertible")
            return pinvert(A)
        else:
            assert "Non invertible matrix given, and not allowed to take pinvert"

def pinvert(A):
    verifyInput(A)
    if type(A) == list:
        A_pinv = []
        for a in A:
            A_pinv.append(np.linalg.pinv(a))
        return A_pinv
    else:
        return np.linalg.pinv(A)


# Occasionally dishonest casino example [Durbin98, p54]. This script
# exemplifies a Hidden Markov Model (HMM) in which the throw of a die
# may result in the die being biased (towards 6) or unbiased. If the dice turns out to
# be biased, the probability of remaining biased is high, and similarly for the unbiased state.
# Assuming we observe the die being thrown n times the goal is to recover the periods in which
# the die was biased.
# Original matlab code: https://github.com/probml/pmtk3/blob/master/demos/casinoDemo.m
# Author: Gerardo Duran-Martin (@gerdm)

import hmm_lib as hmm
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import seed, choice
import pyprobml_utils as pml

def find_dishonest_intervals(z_hist):
    """
    Find the span of timesteps that the
    simulated systems turns to be in state 1
    Parameters
    ----------
    z_hist: array(n_samples)
        Result of running the system with two
        latent states
    Returns
    -------
    list of tuples with span of values
    """
    spans = []
    x_init = 0
    t = x_init
    for t, _ in enumerate(z_hist[:-1]):
        if z_hist[t + 1] == 0 and z_hist[t] == 1:
            x_end = t
            spans.append((x_init, x_end))
        elif z_hist[t + 1] == 1 and z_hist[t] == 0:
            x_init = t + 1
    return spans

def plot_inference(inference_values, z_hist, ax, state=1, map_estimate=False):
    """
    Plot the estimated smoothing/filtering/map of a sequence of hidden states.
    "Vertical gray bars denote times when the hidden
    state corresponded to state 1. Blue lines represent the
    posterior probability of being in that state given diﬀerent subsets
    of observed data." See Markov and Hidden Markov models section for more info
    Parameters
    ----------
    inference_values: array(n_samples, state_size)
        Result of runnig smoothing method
    z_hist: array(n_samples)
        Latent simulation
    ax: matplotlib.axes
    state: int
        Decide which state to highlight
    map_estimate: bool
        Whether to plot steps (simple plot if False)
    """
    n_samples = len(inference_values)
    xspan = np.arange(1, n_samples + 1)
    spans = find_dishonest_intervals(z_hist)
    if map_estimate:
        ax.step(xspan, inference_values, where="post")
    else:
        ax.plot(xspan, inference_values[:, state])

    for span in spans:
        ax.axvspan(*span, alpha=0.5, facecolor="tab:gray", edgecolor="none")
    ax.set_xlim(1, n_samples)
    #ax.set_ylim(0, 1)
    ax.set_ylim(-0.1, 1.1)
    ax.set_xlabel("Observation number")

# state transition matrix
A = np.array([
    [0.95, 0.05],
    [0.10, 0.90]
])

# observation matrix
B = np.array([
    [1/6, 1/6, 1/6, 1/6, 1/6, 1/6], # fair die
    [1/10, 1/10, 1/10, 1/10, 1/10, 5/10] # loaded die
])

n_samples = 300
init_state_dist = np.array([1, 1]) / 2
casino = hmm.HMMDiscrete(A, B, init_state_dist)
z_hist, x_hist = casino.sample(n_samples, 314)

z_hist_str = "".join((z_hist + 1).astype(str))[:60]
x_hist_str = "".join((x_hist + 1).astype(str))[:60]

print("Printing sample observed/latent...")
print(f"x: {x_hist_str}")
print(f"z: {z_hist_str}")

# Do inference
filtering, loglik = casino.forwards(x_hist)
print(loglik)
smoothing = casino.forwards_backwards(x_hist, filtering)
z_map = casino.map_state(x_hist)

# Plot results

fig, ax = plt.subplots()
plot_inference(filtering, z_hist, ax)
ax.set_ylabel("p(loaded)")
ax.set_title("Filtered")
pml.savefig("hmm_casino_filter.pdf")

fig, ax = plt.subplots()
plot_inference(smoothing, z_hist, ax)
ax.set_ylabel("p(loaded)")
ax.set_title("Smoothed")
pml.savefig("hmm_casino_smooth.pdf")

fig, ax = plt.subplots()
plot_inference(z_map, z_hist, ax, map_estimate=True)
ax.set_ylabel("MAP state")
ax.set_title("Viterbi")
pml.savefig("hmm_casino_map.pdf")
plt.show()


"""Mixture model using EM"""
from typing import Tuple
import numpy as np
from common import GaussianMixture

def estep(X: np.ndarray, mixture: GaussianMixture) -> Tuple[np.ndarray, float]:
    """E-step: Softly assigns each datapoint to a gaussian component

    Args:
        X: (n, d) array holding the data
        mixture: the current gaussian mixture

    Returns:
        np.ndarray: (n, K) array holding the soft counts
            for all components for all examples
        float: log-likelihood of the assignment
    """
    n, d = X.shape
    mu, var, p = mixture
    K, _ = mu.shape

    post = np.zeros((n, K), dtype=np.float64)
    for i in range(n):
        post[i, :] = 1 / (2 * np.pi * var)**(d / 2) * np.exp(-np.sum((X[i, :] - mu)**2, axis=1) / (2 * var))

    marginal_prob = p * post
    p_x = np.sum(marginal_prob, axis=1).reshape(-1, 1)

    post = marginal_prob / p_x
    log_likelihood = np.sum(np.log(p_x), axis=0).item()

    return post, log_likelihood

def mstep(X: np.ndarray, post: np.ndarray) -> GaussianMixture:
    """M-step: Updates the gaussian mixture by maximizing the log-likelihood
    of the weighted dataset

    Args:
        X: (n, d) array holding the data
        post: (n, K) array holding the soft counts
            for all components for all examples

    Returns:
        GaussianMixture: the new gaussian mixture
    """
    n, d = X.shape
    _, K = post.shape

    p_ji = np.sum(post, axis=0)
    p_hat = p_ji / n

    mu_hat = np.zeros((K, d))
    sigmas_hat = np.zeros(K)

    for j in range(K):
        mu_hat[j, :] = np.matmul(post[:, j], X) / p_ji[j]
        norm = np.sum((mu_hat[j] - X)**2, axis=1)
        sigmas_hat[j] = np.matmul(post[:, j], norm) / (d * p_ji[j])

    return GaussianMixture(mu_hat, sigmas_hat, p_hat)

def run(X: np.ndarray, mixture: GaussianMixture,
        post: np.ndarray) -> Tuple[GaussianMixture, np.ndarray, float]:
    """Runs the mixture model

    Args:
        X: (n, d) array holding the data
        post: (n, K) array holding the soft counts
            for all components for all examples

    Returns:
        GaussianMixture: the new gaussian mixture
        np.ndarray: (n, K) array holding the soft counts
            for all components for all examples
        float: log-likelihood of the current assignment
    """
    old_LL = None
    LL = None

    while old_LL is None or (LL - old_LL >= 1e-6 * np.abs(LL)):
        old_LL = LL
        post, LL = estep(X, mixture)
        mixture = mstep(X, post)

    return mixture, post, LL


import numpy as np
import numpy.linalg as la

verySmallNumber = 1e-14

def gsBasis(A):
    B = np.array(A, dtype=np.float_)  # Make B as a copy of A, since we're going to alter it's values.
    # Loop over all vectors, starting with zero, label them with i
    for i in range(B.shape[1]):
        # Inside that loop, loop over all previous vectors, j, to subtract.
        for j in range(i):
            # Complete the code to subtract the overlap with previous vectors.
            # you'll need the current vector B[:, i] and a previous vector B[:, j]
            B[:, i] = B[:, i] - B[:, i] @ B[:, j] * B[:, j]
        # Next insert code to do the normalisation test for B[:, i]
        if la.norm(B[:, i]) > verySmallNumber:
            B[:, i] = B[:, i] / la.norm(B[:, i])
        else:
            B[:, i] = np.zeros_like(B[:, i])

    return B

def dimensions(A) :
    return np.sum(la.norm(gsBasis(A), axis=0))

if __name__ == "__main__":
    V = np.array([[1, 0, 2, 6],
                  [0, 1, 8, 2],
                  [2, 8, 3, 1],
                  [1, -6, 2, 3]], dtype=np.float_)
    print(gsBasis(V))


# -*- coding: utf-8 -*-
"""
===== FIL_BPSK_SNR.py =================================================

 Simulate Bit Error Rate (BER) of BPSK digital modulation vs. SNR
 by Ivo Maljevic

=======================================================================
"""
from __future__ import division, print_function, unicode_literals

import numpy as np
import numpy.random as rnd
from numpy import arange, sqrt
import matplotlib.pyplot as plt
from scipy.special import erfc

SNR_MIN     = 0 # dB
SNR_MAX     = 9 # dB
Eb_No_dB    = arange(SNR_MIN,SNR_MAX+1)
SNR         = 10**(Eb_No_dB/10.0)  # linear SNR

Pe          = np.empty(np.shape(SNR))
BER         = np.empty(np.shape(SNR))

loop = 0
for snr in SNR:      # SNR loop
     Pe[loop] = 0.5*erfc(sqrt(snr)) # calculate error probability
     VEC_SIZE = int(np.ceil(100/Pe[loop]))  # vector length is a function of Pe

     # signal vector, new vector for each SNR value
     s = 2*rnd.randint(0,high=2,size=VEC_SIZE)-1

     # linear power of the noise; average signal power = 1
     No = 1.0/snr

     # noise
     n = sqrt(No/2)*rnd.randn(VEC_SIZE)

     # signal + noise
     x = s + n

     # decode received signal + noise
     y = np.sign(x)

     # find erroneous symbols
     err = np.where(y != s)
     error_sum = float(len(err[0]))
     BER[loop] = error_sum/VEC_SIZE
     print ('Eb_No_dB=%4.2f, BER=%10.4e, Pe=%10.4e' % \
    (Eb_No_dB[loop], BER[loop], Pe[loop]))
#     plt.figure(1)
#     plt.plot(arange(100),s[0:100])
     loop += 1
#plt.semilogy(Eb_No_dB, Pe,'r',Eb_No_dB, BER,'s')
plt.figure(2)
plt.semilogy(Eb_No_dB, Pe, linewidth=2)
plt.semilogy(Eb_No_dB, BER,'-s')
plt.grid(True)
plt.legend(('analytical','simulation'))
plt.xlabel('Eb/No (dB)')
plt.ylabel('BER')
plt.show()


from pyspark import SparkContext
import numpy as np
from pprint import pprint
from pyspark.mllib.stat import Statistics
from math import sqrt
import pandas as pd

file_path = "kddcup.data_10_percent.gz"

sc = SparkContext("local", "lesson7")

raw_data = sc.textFile(file_path)

# local vectors
# A local vector is often used as a base type for RDDs in Spark MLlib.
# A local vector has integer-typed and 0-based indices and double-typed values, stored on a single machine.
# MLlib supports two types of local vectors: dense and sparse.
# A dense vector is backed by a double array representing its entry values,
# while a sparse vector is backed by two parallel arrays: indices and values.

# An RDD of dense vectors
# Let's represent each network interaction in our dataset as a dense vector.
# For that we will use the *NumPy* `array` type.

def parse_interactions(line):
    line_split = line.split(",")
    symbolic_indexes = [1, 2, 3, 41]
    clean_line_split = [item for i, item in enumerate(
        line_split) if i not in symbolic_indexes]
    return np.array([float(x) for x in clean_line_split])

vector_data = raw_data.map(parse_interactions)

# Summary statistics
# Compute column summary statistics.
summary = Statistics.colStats(vector_data)

print("Duration Statistics:")
print(" Mean: {}".format(round(summary.mean()[0], 3)))
print(" St. deviation: {}".format(round(sqrt(summary.variance()[0]), 3)))
print(" Max value: {}".format(round(summary.max()[0], 3)))
print(" Min value: {}".format(round(summary.min()[0], 3)))
print(" Total value count: {}".format(summary.count()))
print(" Number of non-zero values: {}".format(summary.numNonzeros()[0]))

# Summary statistics by label
# The interesting part of summary statistics, in our case,
# comes from being able to obtain them by the type of network attack or 'label' in our dataset.
# By doing so we will be able to better characterise our dataset dependent variable
# in terms of the independent variables range of values.

def parse_interaction_with_key(line):
    line_split = line.split(",")
    # keep just numeric and logical values
    symbolic_indexes = [1, 2, 3, 41]
    clean_line_split = [item for i, item in enumerate(
        line_split) if i not in symbolic_indexes]
    return (line_split[41], np.array([float(x) for x in clean_line_split]))

# label_vector_data = raw_data.map(parse_interaction_with_key)
# normal_label_data = label_vector_data.filter(lambda x: x[0] == "normal.")
# normal_summary = Statistics.colStats(normal_label_data.values())
# print("Duration Statistics for label: {}".format("normal"))
# print(" Mean: {}".format(round(normal_summary.mean()[0], 3)))
# print(" St. deviation: {}".format(round(sqrt(normal_summary.variance()[0]), 3)))
# print(" Max value: {}".format(round(normal_summary.max()[0], 3)))
# print(" Min value: {}".format(round(normal_summary.min()[0], 3)))
# print(" Total value count: {}".format(normal_summary.count()))
# print(" Number of non-zero values: {}".format(normal_summary.numNonzeros()[0]))

def summary_by_label(raw_data, label):
    label_vector_data = raw_data.map(parse_interaction_with_key).filter(lambda x: x[0]==label)
    return Statistics.colStats(label_vector_data.values())

normal_sum = summary_by_label(raw_data, "normal.")
print("Duration Statistics for label: {}".format("normal"))
print(" Mean: {}".format(round(normal_sum.mean()[0],3)))
print(" St. deviation: {}".format(round(sqrt(normal_sum.variance()[0]),3)))
print(" Max value: {}".format(round(normal_sum.max()[0],3)))
print(" Min value: {}".format(round(normal_sum.min()[0],3)))
print(" Total value count: {}".format(normal_sum.count()))
print(" Number of non-zero values: {}".format(normal_sum.numNonzeros()[0]))

guess_passwd_summary = summary_by_label(raw_data, "guess_passwd.")
print("Duration Statistics for label: {}".format("guess_password"))
print(" Mean: {}".format(round(guess_passwd_summary.mean()[0],3)))
print(" St. deviation: {}".format(round(sqrt(guess_passwd_summary.variance()[0]),3)))
print(" Max value: {}".format(round(guess_passwd_summary.max()[0],3)))
print(" Min value: {}".format(round(guess_passwd_summary.min()[0],3)))
print(" Total value count: {}".format(guess_passwd_summary.count()))
print(" Number of non-zero values: {}".format(guess_passwd_summary.numNonzeros()[0]))

label_list = ["back.","buffer_overflow.","ftp_write.","guess_passwd.",
              "imap.","ipsweep.","land.","loadmodule.","multihop.",
              "neptune.","nmap.","normal.","perl.","phf.","pod.","portsweep.",
              "rootkit.","satan.","smurf.","spy.","teardrop.","warezclient.",
              "warezmaster."]

stats_by_label = [(label, summary_by_label(raw_data, label)) for label in label_list]

# duration_by_label = [
#     (stat[0], np.array([float(stat[1].mean()[0]), float(sqrt(stat[1].variance()[0])), float(stat[1].min()[0]), float(stat[1].max()[0]), int(stat[1].count())]))
#     for stat in stats_by_label]

# pd.set_option('display.max_columns', 50)
# stats_by_label_df = pd.DataFrame.from_dict(dict(duration_by_label), columns=["Mean", "Std Dev", "Min", "Max", "Count"], orient='index')
# print("Duration statistics, by label")
# print(stats_by_label_df)

def get_variable_stats_df(stats_by_label, column_i):
    column_stats_by_label = [
        (stat[0], np.array([float(stat[1].mean()[column_i]), float(sqrt(stat[1].variance()[column_i])), float(stat[1].min()[column_i]), float(stat[1].max()[column_i]), int(stat[1].count())]))
        for stat in stats_by_label
    ]
    return pd.DataFrame.from_dict(dict(column_stats_by_label), columns=["Mean", "Std Dev", "Min", "Max", "Count"], orient='index')

print(get_variable_stats_df(stats_by_label, 0))


r"""Root Mean Square Error metrics.

The root mean square error can be described as:

.. math::
    \sqrt{\frac{1}{n}\sum_{p}^{n}{(y_p-\hat{y_p})}^2}
"""
from math import sqrt

import numpy as np

from .base import EvaluationMetric
from .exceptions import NoSampleError

class RootMeanSquareError(EvaluationMetric):
    """Root Mean Square Error metric.

    The metric is defined for grayscale depth images.

    Attributes:
        sum_of_root_mean_square_error (float): the sum of RMSE
        for all the images in a branch
        num_samples (int): the number of samples in all mini-batches
    """

    def __init__(self):
        self.sum_of_root_mean_square_error = 0
        self.num_samples = 0

    def reset(self):
        self.sum_of_root_mean_square_error = 0
        self.num_samples = 0

    def update(self, output):
        y_pred, y = output
        if y.shape != y_pred.shape:
            raise ValueError(
                f"The shapes of real data {y.shape} and predicted data {y_pred.shape} \
                               should be the same."
            )

        self.num_samples += y.shape[0]
        self.sum_of_root_mean_square_error += (
            np.square(y - y_pred).mean() * y.shape[0]
        )

    def compute(self):
        if self.num_samples == 0:
            raise NoSampleError(
                "RootMeanSquareError must have at least one example before it "
                "can be computed."
            )
        return sqrt(self.sum_of_root_mean_square_error / self.num_samples)


'''
RBF Interpolation on a unit sphere. This is done by converting theta
(azimuthal angle) and phi (polar angle) into cartesian coordinates and
then interpolating over R^3.
'''
import numpy as np
from rbf.interpolate import RBFInterpolant
import matplotlib.pyplot as plt
np.random.seed(1)

def spherical_to_cartesian(theta, phi):
    x = np.sin(phi)*np.cos(theta)
    y = np.sin(phi)*np.sin(theta)
    z = np.cos(phi)
    return np.array([x, y, z]).T

def true_function(theta, phi):
  # create some arbitary function that we want to recontruct with
  # interpolation
  cart = spherical_to_cartesian(theta, phi)
  out = (np.cos(cart[:, 2] - 1.0)*
         np.cos(cart[:, 0] - 1.0)*
         np.cos(cart[:, 1] - 1.0))
  return out

# number of observation points
nobs = 50
# make the observation points in spherical coordinates
theta_obs = np.random.uniform(0.0, 2*np.pi, nobs)
phi_obs = np.random.uniform(0.0, np.pi, nobs)
# get the cartesian coordinates for the observation points
cart_obs = spherical_to_cartesian(theta_obs, phi_obs)
# get the latent function at the observation points
val_obs = true_function(theta_obs, phi_obs)
# make a grid of interpolation points in spherical coordinates
theta_itp, phi_itp = np.meshgrid(np.linspace(0.0, 2*np.pi, 100),
                                 np.linspace(0.0, np.pi, 100))
theta_itp = theta_itp.flatten()
phi_itp = phi_itp.flatten()
# get the catesian coordinates for the interpolation points
cart_itp = spherical_to_cartesian(theta_itp, phi_itp)
# create an RBF interpolant from the cartesian observation points. I
# am just use the default `RBFInterpolant` parameters here, nothing
# special.
I = RBFInterpolant(cart_obs, val_obs)
# evaluate the interpolant on the interpolation points
val_itp = I(cart_itp)

## PLOTTING

# plot the true function in spherical coordinates
val_true = true_function(theta_itp, phi_itp)
plt.figure(1)
plt.title('True function')
p = plt.tripcolor(theta_itp, phi_itp, val_true,
                  cmap='viridis')
plt.colorbar(p)
plt.xlabel('theta (azimuthal angle)')
plt.ylabel('phi (polar angle)')
plt.xlim(0, 2*np.pi)
plt.ylim(0, np.pi)
plt.grid(ls=':', color='k')
plt.tight_layout()
plt.savefig('../figures/interpolate.b.1.png')

# plot the interpolant in spherical coordinates
plt.figure(2)
plt.title('RBF interpolant (points are observations)')
# plot the interpolated function
p = plt.tripcolor(theta_itp, phi_itp, val_itp,
                  cmap='viridis')
# plot the observations
plt.scatter(theta_obs, phi_obs, c=val_obs,
            s=50, edgecolor='k', cmap='viridis',
            vmin=p.get_clim()[0], vmax=p.get_clim()[1])
plt.colorbar(p)
plt.xlabel('theta (azimuthal angle)')
plt.ylabel('phi (polar angle)')
plt.xlim(0, 2*np.pi)
plt.ylim(0, np.pi)
plt.grid()
plt.grid(ls=':', color='k')
plt.tight_layout()
plt.savefig('../figures/interpolate.b.2.png')

# compute and print the mean L2 error
mean_error = np.mean(np.abs(val_true - val_itp))
print('mean interpolation error: %s' % mean_error)
plt.show()


import numpy as np;

import matplotlib.pyplot as plt

def load(filename):
    f = open(filename, 'rb')
    return np.loadtxt(f, delimiter=",")

def plotData(x, y):
    plt.plot(x, y, 'rx', markersize=10)
    plt.ylabel('Profit in $10,000s')
    plt.xlabel('Population of City in 10,000s')
    # plt.show()

data = load('ex1data1.txt')
X = data[:,0:1]
y = data[:,1:2]

plotData(X, y)

m = len(X);
X = np.concatenate((np.ones((m, 1)), X), 1)
theta = np.zeros((2, 1))
iterations = 1500;
alpha = 0.01;

def computeCost(X, y, theta):
    m = len(y)
    return np.sum((X @ theta - y) ** 2, axis=0) / (2 * m)

# test case
# print('computeCost() test, 11.9450, 7.0175')
# print(computeCost(np.array([[1, 2], [1, 3], [1,4], [1,5]]), np.array([[7], [6], [5], [4]]), np.array([[0.1], [0.2]])))
# print(computeCost(np.array([[1, 2, 3], [1, 3, 4], [1, 4, 5], [1, 5, 6]]), np.array([[7], [6], [5], [4]]), np.array([[0.1], [0.2], [0.3]])))
# print('test over')

print(computeCost(X, y, theta))
print(computeCost(X, y, np.array([[-1],[2]])))

def gradientDescent(X, y, theta, alpha, num_iters):
    m = len(y)
    J_history = np.zeros((num_iters, 1))
    for iter in range(num_iters):
        h = (X @ theta - y)
        theta = theta - alpha * (np.sum(h * X, axis=0, keepdims=True) / m).T
        J_history[iter] = computeCost(X, y, theta)
    return (theta, J_history)

# print('gradientDescent() test')
# theta_test, J_hist = gradientDescent(np.array([[1, 5], [1, 2], [1, 4], [1, 5]]), np.array([[1], [6], [4], [2]]), np.array([[0], [0]]), 0.01, 1000)
# print(theta_test, J_hist[1], J_hist[-1])

# theta_test, J_hist = gradientDescent(np.array([[1, 5], [1, 2]]), np.array([[1], [6]]), np.array([[.5], [.5]]), 0.1, 10)
# print(theta_test, J_hist)
# print('test over')

(theta, _) = gradientDescent(X, y, theta, alpha, iterations)

print('Theta computed from gradient descent:\n', theta[0], theta[1])

plt.plot(X[:,1:2], X @ theta, '-', markersize=10)
plt.legend(['Training data', 'Linear Regression'])
plt.show()

predict1 = np.array([[1, 3.5]]) @ theta
print('For population = 35,000, we predict a profit of', predict1 * 10000)

predict2 = np.array([[1, 7]]) @ theta;
print('For population = 70,000, we predict a profit of', predict2 * 10000)

theta0_vals = np.linspace(-10, 10, 100)
theta1_vals = np.linspace(-1, 4, 100)

J_vals = np.zeros((len(theta0_vals), len(theta1_vals)))

for i in range(len(theta0_vals)):
    for j in range(len(theta1_vals)):
        t = [[theta0_vals[i]], [theta1_vals[j]]]
        J_vals[i, j] = computeCost(X, y ,t)

J_vals = J_vals.T

ax = plt.axes(projection='3d')
theta0_vals, theta1_vals = np.meshgrid(theta0_vals, theta1_vals)
ax.plot_surface(theta0_vals, theta1_vals, J_vals, cmap=plt.get_cmap('magma'))
ax.invert_xaxis()
plt.xlabel(r'$\theta_0$')
plt.ylabel(r'$\theta_1$')

plt.show()

plt.figure()
plt.contour(theta0_vals, theta1_vals, J_vals, np.logspace(-2, 3, 20))
plt.xlabel(r'$\theta_0$')
plt.ylabel(r'$\theta_1$')
plt.plot(theta[0], theta[1], 'rx', markersize=10, linewidth=2);
plt.show()


"""K-Means clustering Algorithm"""
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

class KMeans:
    """K-Means clustering

    -- Arguments
        k: int, optional, default 2
            The number of clusters to form as well as the number of centroids to generate.
        max_iter: int, optional, default 300
            Maximum number of iterations of the k-means algorithm to run.
        tol: float, optional
            The relative increment in the results before declaring convergence.
    """

    def __init__(self, k=2, tol=0.001, max_iter=300):
        self.k = k
        self.tol = tol
        self.max_iter = max_iter

    def fit(self, data):
        """Compute k-means clustering."""
        self.centroids = centroids = dict()

        for i in range(self.k):
            centroids[i] = data[i]

        for i in range(self.max_iter):
            self.classifications = classifications = dict()

            for i in range(self.k):
                classifications[i] = []

            for featureset in data:
                distances = []
                for centroid in centroids:
                    n = np.linalg.norm(featureset - centroids[centroid])
                    distances.append(n)

                classification = distances.index(min(distances))
                classifications[classification].append(featureset)

            prev_centroids = dict(centroids)

            for classification in classifications:
                centroids[classification] = np.average(classifications[classification],
                                                        axis=0)

            optimized = True

            for c in centroids:
                original_centroid = prev_centroids[c]
                current_centroid = centroids[c]
                s = np.sum((current_centroid - original_centroid)
                            / original_centroid*100)
                if s > self.tol:
                    print(s)
                    optimized = False

            if optimized:
                break

    def predict(self, data):
        """Predict the closest cluster each sample in data belongs to."""
        distances = []
        for centroid in self.centroids:
            n = np.linalg.norm(data-self.centroids[centroid])
            distances.append(n)

        classification = distances.index(min(distances))
        return classification

if '__main__' == __name__:
    main_data = np.array([[1, 2],
             [1.5, 1.8],
             [5, 8],
             [8, 8],
             [1, 0.6],
             [9, 11]])

    colors = ['r','g','b','c','k','o','y']

    clf = KMeans()
    clf.fit(main_data)

    for centroid in clf.centroids:
        plt.scatter(clf.centroids[centroid][0], clf.centroids[centroid][1],
                    marker="o", color="k", s=150, linewidths=5)

    for classification in clf.classifications:
        color = colors[classification]

        for featureset in clf.classifications[classification]:
            plt.scatter(featureset[0], featureset[1], marker="x",
                        color=color, s=150, linewidths=5)

    new_data = np.array([[1,3],
                        [8,9],
                        [0,3],
                        [5,4],
                        [6,4]])

    for item in new_data:
        classification = clf.predict(item)
        plt.scatter(item[0], item[1], marker="*", color=colors[classification],
                    s=150, linewidths=5)

    plt.show()


__author__ = 'pittnuts'

from numpy import *
from numpy.linalg import inv
from scipy.linalg import eigh
import matplotlib.pyplot as plt
import warnings
# PCA analysis
def pca(X):
    """
    :param X: n-by-d data matrix X. Rows of X correspond to observations and columns correspond to dimensions.
    :return: Y - transformed new coordinate in new space, eig_vec - eigenvectors, eig_value
    """
    #X = array(X);
    if (X.ndim != 2) or (X.shape[0]==1):
        raise ValueError('dimension of X should be 2 and X shoud have >1 samples')

    #mean
    avg = mean(X,axis=0)
    shift = abs(avg/std(X,axis=0))
    if shift.mean()>0.1:
        #warnings.warn("shift of mean/std: max=={:f}, mean=={:f}".format(shift.max(), shift.mean()),RuntimeWarning)
        pass
        #print shift.max(), shift.mean()

    #deprecated
    #avg = tile(avg,(X.shape[0],1))
    ##X -= avg; #don't change the input value
    #C = dot((X-avg).transpose(),X-avg)/(X.shape[0]-1)

    #covariance matrix
    C = dot(X.transpose(),X)/(X.shape[0]-1)
    C = (C+C.transpose())/2 #make sure it is more symmetric

    #get eigenvalues and eigenvectors
    eig_values,eig_vecs = linalg.eig(C)
    idx = eig_values.argsort()
    idx = idx[ : :-1]
    eig_values = eig_values[idx]
    eig_vecs = eig_vecs[:,idx]

    #new coordinate in new space
    Y = dot(X,eig_vecs)

    return (Y, eig_vecs, eig_values)

#refer to paper "Boyuan Liu, sparse convolutional neural networks"
def kernel_factorization(K):
    """
    :param K: a 4D kernel/filer of convolutional layer (filter size x filter size x channel # x filter #)
    :return: factors
    """
    #if want to get a sparse R, mean value of R should be reduced
    # ??????????

    #K = array(K)
    filter_size = K.shape[0]
    channel = K.shape[2]
    filter_num = K.shape[3]

    #reshape 4D K to 2D along channel dimension
    K2D = transpose(K,(0,1,3,2)).reshape(((filter_size**2)*filter_num,channel))
    assert (K == K2D.reshape(filter_size,filter_size,filter_num,channel).transpose((0,1,3,2))).all()

    #PCA: mean value is NOT intrinsically reduced
    R2D, P, eig_values_K = pca(K2D)
    assert (R2D == dot(K2D,P)).all()
    P = transpose(P)

    #print inv(P)
    #print inv(P)
    # #assert (transpose(P) == inv(P)).all()

    R = R2D.reshape(filter_size,filter_size,filter_num,channel).transpose((0,1,3,2))
    #print "R: %{} zeros".format(100*sum((abs(R)<0.0001).flatten())/(float)(R.size))
    #check
    for u in range(0,filter_size):
        for v in range(0,filter_size):
            for i in range(0,channel):
                for j in range(0,filter_num):
                    assert abs(K[u,v,i,j] - dot(R[u,v,:,j],P[:,i])) < 0.000001

    # initilize factors for all channels
    S = zeros((channel,filter_size**2,filter_num))
    Q = zeros((channel,filter_size,filter_size,filter_size**2))
    q = zeros(channel)

    for ch in range(0,channel):
        tmp = R[:,:,ch,:].transpose().reshape((filter_num,filter_size**2))
        S_tmp, Q_tmp, eig_values_tmp = pca(tmp)
        #get the index of eigenvalues with 99% accumulative sum
        accumu_rate = cumsum(eig_values_tmp)/sum(eig_values_tmp)
        q[ch] = nonzero(accumu_rate>0.99)[0][0] #the first index with 99% accumulative sum
        #print eig_values_tmp
        #print accumu_rate

        assert (dot(tmp,Q_tmp)==S_tmp).all()

        #get factors
        #print transpose(Q_tmp)
        #print inv(Q_tmp)
        S[ch] = S_tmp.transpose() #.reshape(filter_num,filter_size,filter_size).transpose(2,1,0)
        Q_tmp = Q_tmp.transpose()
        Q[ch] = Q_tmp.reshape(filter_size**2,filter_size,filter_size).transpose()

    #check
    count = 0;
    for u in range(0,filter_size):
        for v in range(0,filter_size):
            for i in range(0,channel):
                for j in range(0,filter_num):
                    assert abs(R[u,v,i,j] - dot(S[i,:,j],Q[i,u,v,:]))<0.000001
                    if abs(R[u,v,i,j] - dot(S[i,:,j],Q[i,u,v,:]))>0.0000001:
                        count += 1
    #print count

    return (P,S,Q,q)

#recover kernel from factors returned from kernel_factorization
def kernel_recover(P,S,Q,q):
    """
    :param P: bases for K
    :param S: coefficient of Q
    :param Q: bases for R
    :param q: number of preserved dimension
    :return: original and new kernels
    """
    filter_size = Q.shape[1]
    channel = P.shape[1]
    filter_num = S.shape[2]
    R = zeros((filter_size,filter_size,channel,filter_num))
    K = zeros((filter_size,filter_size,channel,filter_num))

    #recover R
    for i in range(0,channel):
        #S_mean = mean(S[i],1)
        #S_shift = S[i] - tile(S_mean.reshape((S[i].shape[0],1)),(1,S[i].shape[1]))
        for u in range(0,filter_size):
            for v in range(0,filter_size):
                    for j in range(0,filter_num):
                        #R[u,v,i,j] = dot(S_shift[0:q[i],j],Q[i,u,v,0:q[i]]) + dot(S_mean,Q[i,u,v,:])
                        #R[u,v,i,j] = dot(S_shift[:,j],Q[i,u,v,:]) + dot(S_mean,Q[i,u,v,:])
                        R[u,v,i,j] = dot(S[i,0:q[i],j],Q[i,u,v,0:q[i]])

    #recover K
    for u in range(0,filter_size):
        for v in range(0,filter_size):
            for i in range(0,channel):
                for j in range(0,filter_num):
                    K[u,v,i,j] = dot(R[u,v,:,j],P[:,i])

    return (K,R)

def contains(X,v):
    X = array(X).flatten()
    for x in X:
        if x==v:
            return True

    return False

def contains2D(X,v):
    X = array(X)
    row_num = X.shape[0]
    res = zeros((row_num,1))
    for i in range(0,row_num):
        if contains(X[i,:],v[i]):
            res[i,0] = True
        else: res[i,0] = False
    return res

def zero_out(X,thre):
    orig_shape = X.shape
    zero_out_idx = nonzero(abs(X)<thre)
    #print X[zero_out_idx]
    X[zero_out_idx] = 0
    #assert (X[zero_out_idx] == 0).all()

def zerout_smallest(X,thre):
    orig_shape = X.shape
    zero_out_idx = nonzero(abs(X)<thre)
    #print X[zero_out_idx]
    X[zero_out_idx] = 0
    #assert (X[zero_out_idx] == 0).all()

def get_sparsity(X,thre=0):
    #flatten_X = X.flatten()
    thre = abs(thre)
    mask_mat = abs(X)<=thre
    return sum(mask_mat)/(float)(mask_mat.size)

def show_matrix(mat):
    if len(mat.shape) > 2:
        return

    filt_min, filt_max = mat.min(), mat.max()
    plt.figure()
    plt.imshow(mat, vmin=filt_min, vmax=filt_max,cmap=plt.get_cmap('Greys'),interpolation='none')
    #plt.axis('off')
    plt.tick_params(axis='both', which='both', bottom='off', top='off', labelbottom='off', right='off', left='off', labelleft='off')


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import random

style.use('fivethirtyeight')

x = np.array([1,2,3,4,5,6],dtype=np.float64)
y = np.array([5,4,6,5,6,7],dtype=np.float64)

def create_dataset(n, variance, step=2, correlation='pos'):
    val = 1
    y = []
    for i in range(n):
        y.append(  val + random.randrange(-variance, variance)  )
        if correlation and correlation == 'pos':
            val += step
        elif correlation and correlation == 'neg':
            val -= step

    x = [i for i in range(len(y))]
    return np.array(x , dtype=np.float64), np.array(y , dtype=np.float64)

def m_b(x,y):
    m = ((mean(x)*mean(y)) - mean(x*y)) / ((mean(x)**2) - mean(x**2))
    b = mean(y)-m*mean(x)
    return m,b

def squared_error(ys_orig,ys_line):
    return sum((ys_line-ys_orig)**2)

def coefficient_of_determination(y,y_):
    squared_error_y_reg = squared_error(y, y_)
    squared_error_y_mean = squared_error(y, mean(y) )
    return 1 - (squared_error_y_reg/squared_error_y_mean)

#x,y = create_dataset(40, 80, 2, correlation=False)

m,b= m_b(x,y)
y_ = m*x + b

# prediction
prediction_x = 6.5
prediction_y = m*prediction_x + b

r_squared = coefficient_of_determination(y,y_)
print("Accuracy %s"%r_squared)

plt.scatter(x,y, label='real data points', color='r')
plt.scatter(prediction_x,prediction_y, label='single prediction', color='g')
plt.plot(x,y_, label='prediction line(y_hat)')

plt.legend(loc=4)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Simple Linear Regression(SLR)')
plt.tight_layout()
plt.savefig('pics/simple_linear_regression1.png')
plt.show()

# error line plotting
plt.scatter(x,y, label='real data points', color='r')
plt.plot(x,y_, label='prediction line')
i=0

for xi,y_i,yi in zip(np.nditer(x),np.nditer(y_),np.nditer(y)):
    plt.plot([xi,xi],[y_i,yi],label='error',color='b')
    if i == 0:
        i=1
        plt.legend(['Prediction line (y_hat)', 'Prediction Error(y_hat-y)' , 'Data Points'],loc=4)

plt.xlabel('X')
plt.ylabel('Y')
plt.title('Errors of Prediction SLR')
plt.tight_layout()
plt.savefig('pics/errors1.png')
plt.show()


""" Classes for defining decay schedules for simulated annealing."""

# Author: Genevieve Hayes
# License: BSD 3 clause

import numpy as np

class ExpDecay:
    """
    Schedule for exponentially decaying the simulated
    annealing temperature parameter T according to the formula:

    .. math::

        T(t) = \\max(T_{0} e^{-rt}, T_{min})

    where:

    * :math:`T_{0}` is the initial temperature (at time t = 0);
    * :math:`r` is the rate of exponential decay; and
    * :math:`T_{min}` is the minimum temperature value.

    Parameters
    ----------
    init_temp: float, default: 1.0
        Initial value of temperature parameter T. Must be greater than 0.
    exp_const: float, default: 0.005
        Exponential constant parameter, r. Must be greater than 0.
    min_temp: float, default: 0.001
        Minimum value of temperature parameter. Must be greater than 0.

    Example
    -------
    .. highlight:: python
    .. code-block:: python

       >>> import mlrose
       >>> schedule = mlrose.ExpDecay(init_temp=10, exp_const=0.05, min_temp=1)
       >>> schedule.evaluate(5)
       7.78800...
    """

    def __init__(self, init_temp=1.0, exp_const=0.005, min_temp=0.001):

        self.init_temp = init_temp
        self.exp_const = exp_const
        self.min_temp = min_temp

        if self.init_temp <= 0:
            raise Exception("""init_temp must be greater than 0.""")

        if self.exp_const <= 0:
            raise Exception("""exp_const must be greater than 0.""")

        if self.min_temp < 0:
            raise Exception("""min_temp must be greater than 0.""")
        elif self.min_temp > self.init_temp:
            raise Exception("""init_temp must be greater than min_temp.""")

    def evaluate(self, t):
        """Evaluate the temperature parameter at time t.

        Parameters
        ----------
        t: int
            Time at which the temperature paramter T is evaluated.

        Returns
        -------
        temp: float
            Temperature parameter at time t.
        """

        temp = self.init_temp*np.exp(-1.0*self.exp_const*t)

        if temp < self.min_temp:
            temp = self.min_temp

        return temp

    def get_info__(self, t=None, prefix=''):
        prefix = f'_{prefix}__schedule_' if len(prefix) > 0 else 'schedule_'
        info = {
            f'{prefix}type': 'exponential',
            f'{prefix}init_temp': self.init_temp,
            f'{prefix}exp_const': self.exp_const,
            f'{prefix}min_temp': self.min_temp,
        }
        if t is not None:
            info[f'{prefix}current_value'] = self.evaluate(t)
        return info

    def __str__(self):
        return str(self.init_temp)

    def __repr__(self):
        return f'{self.__class__.__name__}(init_temp={self.init_temp}, ' \
               f'exp_const={self.exp_const}, min_temp={self.min_temp})'


"""odeintegrate.py: Implements a few time integration routines for ODEs."""

__author__      = "Tony Saad"
__copyright__   = "Copyright 2018, Tony Saad under the MIT license"

import numpy as np
from scipy.optimize import fsolve

def forward_euler(rhs, f0, tend, dt):
    ''' Computes the forward_euler method '''
    nsteps = int(tend/dt)
    f = np.zeros(nsteps)
    f[0] = f0
    time = np.linspace(0,tend,nsteps)
    for n in np.arange(nsteps-1):
        f[n+1] = f[n] + dt * rhs(f[n], time[n])
    return time, f

def forward_euler_system(rhsvec, f0vec, tend, dt):
    '''
    Solves a system of ODEs using the Forward Euler method
    '''
    nsteps = int(tend/dt)
    neqs = len(f0vec)
    f = np.zeros( (neqs, nsteps) )
    f[:,0] = f0vec
    time = np.linspace(0,tend,nsteps)
    for n in np.arange(nsteps-1):
        t = time[n]
        f[:,n+1] = f[:,n] + dt * rhsvec(f[:,n], t)
    return time, f

def be_residual(fnp1, rhs, fn, dt, tnp1):
    '''
    Nonlinear residual function for the backward Euler implicit time integrator
    '''
    return fnp1 - fn - dt * rhs(fnp1, tnp1)

def backward_euler(rhs, f0, tend, dt):
    '''
    Computes the backward euler method
    :param rhs: an rhs function
    '''
    nsteps = int(tend/dt)
    f = np.zeros(nsteps)
    f[0] = f0
    time = np.linspace(0,tend,nsteps)
    for n in np.arange(nsteps-1):
        fn = f[n]
        tnp1 = time[n+1]
        fnew = fsolve(be_residual, fn, (rhs, fn, dt, tnp1))
        f[n+1] = fnew
    return time, f

def cn_residual(fnp1, rhs, fn, dt, tnp1, tn):
    '''
    Nonlinear residual function for the Crank-Nicolson implicit time integrator
    '''
    return fnp1 - fn - 0.5 * dt * ( rhs(fnp1, tnp1) + rhs(fn, tn) )

def crank_nicolson(rhs,f0,tend,dt):
    nsteps = int(tend/dt)
    f = np.zeros(nsteps)
    f[0] = f0
    time = np.linspace(0,tend,nsteps)
    for n in np.arange(nsteps-1):
        fn = f[n]
        tnp1 = time[n+1]
        tn = time[n]
        fnew = fsolve(cn_residual, fn, (rhs, fn, dt, tnp1, tn))
        f[n+1] = fnew
    return time, f


import numpy as np
from random import shuffle

def softmax_loss_naive(W, X, y, reg):
  """
  Softmax loss function, naive implementation (with loops)

  Inputs have dimension D, there are C classes, and we operate on minibatches
  of N examples.

  Inputs:
  - W: A numpy array of shape (D, C) containing weights.
  - X: A numpy array of shape (N, D) containing a minibatch of data.
  - y: A numpy array of shape (N,) containing training labels; y[i] = c means
    that X[i] has label c, where 0 <= c < C.
  - reg: (float) regularization strength

  Returns a tuple of:
  - loss as single float
  - gradient with respect to weights W; an array of same shape as W
  """
  # Initialize the loss and gradient to zero.
  loss = 0.0
  dW = np.zeros_like(W)
  num_train = X.shape[0]
  #############################################################################
  # TODO: Compute the softmax loss and its gradient using explicit loops.     #
  # Store the loss in loss and the gradient in dW. If you are not careful     #
  # here, it is easy to run into numeric instability. Don't forget the        #
  # regularization!                                                           #
  #############################################################################
  # Loss
  d = X.dot(W) # NumInstances x Classes
  d -= np.max(d, axis=1).reshape(-1, 1) # For numeric stability
  d_exp = np.exp(d) # Raise all of them to Exp
  d_exp_sums = np.sum(d_exp, axis=1).reshape(-1, 1) # Calculate the denominators
  d_exp_reg = d_exp / d_exp_sums # Normalize
  Li = -np.log(d_exp_reg[np.arange(len(d_exp_reg)), y])
  loss = np.sum(Li) / num_train # Data loss
  loss += 0.5 * reg * np.sum(W * W) # Add the regularization loss

  # Gradient
  # https://eli.thegreenplace.net/2016/the-softmax-function-and-its-derivative/
  # for derivation
  probs = d_exp_reg
  probs[np.arange(num_train),y] -= 1
  dW = X.T.dot(probs)
  dW /= num_train
  dW += reg * W
  #############################################################################
  #                          END OF YOUR CODE                                 #
  #############################################################################

  return loss, dW

def softmax_loss_vectorized(W, X, y, reg):
  """
  Softmax loss function, vectorized version.

  Inputs and outputs are the same as softmax_loss_naive.
  """
  # Initialize the loss and gradient to zero.
  loss = 0.0
  dW = np.zeros_like(W)
  num_train = X.shape[0]
  #############################################################################
  # TODO: Compute the softmax loss and its gradient using no explicit loops.  #
  # Store the loss in loss and the gradient in dW. If you are not careful     #
  # here, it is easy to run into numeric instability. Don't forget the        #
  # regularization!                                                           #
  #############################################################################
  # Loss
  d = X.dot(W) # NumInstances x Classes
  d -= np.max(d, axis=1).reshape(-1, 1) # For numeric stability
  d_exp = np.exp(d) # Raise all of them to Exp
  d_exp_sums = np.sum(d_exp, axis=1).reshape(-1, 1) # Calculate the denominators
  d_exp_reg = d_exp / d_exp_sums # Normalize
  Li = -np.log(d_exp_reg[np.arange(len(d_exp_reg)), y])
  loss = np.sum(Li) / num_train # Data loss
  loss += 0.5 * reg * np.sum(W * W) # Add the regularization loss

  # Gradient
  probs = d_exp_reg
  probs[np.arange(num_train),y] -= 1
  dW = X.T.dot(probs)
  dW /= num_train
  dW += reg * W
  #############################################################################
  #                          END OF YOUR CODE                                 #
  #############################################################################

  return loss, dW


from numpy import log2, zeros, mean, var, sum, loadtxt, arange, array, cumsum, dot, transpose, diagonal, floor
from numpy.linalg import inv
from time import time

def block(x):
    # preliminaries
    n = len(x); d = log2(n); s, gamma = zeros(d), zeros(d);
    mu = mean(x); t0 = time()

    # estimate the auto-covariance and variances
    # for each blocking transformation
    for i in arange(0,d):
        n = len(x)
        # estimate autocovariance of x
        gamma[i] = (n)**(-1)*sum( (x[0:(n-1)]-mu)*(x[1:n]-mu) )
        # estimate variance of x
        s[i] = var(x)
        # perform blocking transformation
        x = 0.5*(x[0::2] + x[1::2])

    # generate the test observator M_k from the theorem
    M = (cumsum( ((gamma/s)**2*2**arange(1,d+1)[::-1])[::-1] )  )[::-1]

    # we need a list of magic numbers
    q =array([6.634897,9.210340, 11.344867, 13.276704, 15.086272, 16.811894, 18.475307, 20.090235, 21.665994, 23.209251, 24.724970, 26.216967, 27.688250, 29.141238, 30.577914, 31.999927, 33.408664, 34.805306, 36.190869, 37.566235, 38.932173, 40.289360, 41.638398, 42.979820, 44.314105, 45.641683, 46.962942, 48.278236, 49.587884, 50.892181])

    # use magic to determine when we should have stopped blocking
    for k in arange(0,d):
        if(M[k] < q[k]):
            break
    if (k >= d-1):
        print "Warning: Use more data"
    ans = s[k]/2**(d-k)
    print "Runtime: %g sec" % (time()-t0); print "Blocking Statistics :"
    print "average            iterations      std. error"
    print "%8g %20g %15g" % (mu, k, ans**.5)
    return ans

# input data must be a power of two
x = loadtxt("resources/fulldata.txt")

ans = block(x)


import numpy as np
import pandas as pd
def normalEqn(X,y):
    inner = X.T*X
    theta = inner.I*X.T*y
    return theta
"""单特征数据"""
data1 = pd.read_csv("ex1data1.txt",names = ['population','profit'])
x = data1.population
y = data1.profit
df = data1.copy()
df.insert(0,"one",1)
X = df.iloc[:,0:df.shape[1]-1]
y = df.iloc[:,df.shape[1]-1:df.shape[1]]
y = np.matrix(y)
X = np.matrix(X)
x = np.matrix(x)
theta1 = normalEqn(X,y)
print('theta1:','\n',theta1)

"""多特征数据"""
data2 = pd.read_csv('ex1data2.txt',names=['square', 'bedrooms', 'price'])

"""特征缩放"""
x_2 = data2.iloc[:,0:data2.shape[1]-1]
y_2 = data2.iloc[:,data2.shape[1]-1:]
x_2 = (x_2 - np.average(x_2,axis = 0))/np.std(x_2,axis = 0,ddof = 1)#ddof = 1,有偏和无偏？
y_2 = (y_2 - np.average(y_2,axis = 0))/np.std(y_2,axis = 0,ddof = 1)

X_2 = x_2.copy()
X_2.insert(0,'one',1)
X_2 = np.matrix(X_2)
y_2 = np.matrix(y_2)
theta2 = normalEqn(X_2,y_2)
print('theta2:','\n',theta2)


import numpy as np
import matplotlib.pyplot as plt

fftaxis = -1

f0 = 10.0

Tss = -1.0
Tse = 1.0
Ts = Tse - Tss

Fs = 100
Ns = Fs * Ts

t = np.linspace(Tss, Tse, Ns)
x = np.sin(2 * np.pi * f0 * t)

X = []
M = 256

for i in range(M):
    X.append(x)

X = np.array(X)

Y = np.fft.fft(X, axis=fftaxis)
Yshift = np.fft.fftshift(np.fft.fft(X, axis=fftaxis))
Yshift1 = np.fft.fftshift(np.fft.fft(np.fft.fftshift(X), axis=fftaxis))

print(np.sum(Yshift-Yshift1), "-=-=-=-=")

f = np.linspace(0, Fs, Ns)
fshift = np.linspace(-Fs / 2.0, Fs / 2.0, Ns)

plt.figure()
plt.subplot(131)
plt.imshow(X)
plt.xlabel('Times/s')
plt.ylabel('Amplitude')
plt.title('Original Signal(' + str(f0) + 'Hz)')

plt.subplot(132)
# plt.imshow(abs(Y))
plt.imshow(abs(Yshift))
plt.xlabel('Frequency/Hz')
plt.ylabel('Amplitude')
plt.title('FFT of Signal')

plt.subplot(133)
plt.imshow(abs(Yshift1))
plt.xlabel('Frequency/Hz')
plt.ylabel('Amplitude')
plt.title('FFT of Signal(shifted)')
# plt.subplots_adjust(wspace=0.8)
plt.tight_layout()
plt.show()


# Methods for sampling evenly-spaced points from various domains, using Fibonacci spirals
# (The sampling is *not* from a uniform random distribution. There is nothing random about this.)
import numpy as np

# Golden ratio
phi = (1 + np.sqrt(5)) / 2

# Returns n sample points from a Fibonacci spiral on the unit disk
def unitdisk(n):
    r2 = np.linspace(1/(2*n), 1-1/(2*n), n)
    r = np.sqrt(r2)
    theta = (2*np.pi*phi) * np.arange(n)
    x, y = r*np.cos(theta), r*np.sin(theta)
    return np.array([x, y]).transpose()

# Returns n sample points from a Fibonacci spiral on the unit sphere
def unitsphere(n):
    # z-coordinates of sample points
    z = np.linspace(1-(1/n), -1+(1/n), n)

    # Longitude and latitude of sample points
    lon = (2*np.pi*phi) * np.arange(n)
    lat = np.arcsin(z)

    # x,y-coordinates of sample points
    x, y = np.cos(lon)*np.cos(lat), np.sin(lon)*np.cos(lat)

    # Return a numpy.Array containing the sample points
    return np.array([x, y, z]).transpose()


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

# Author: Daniel Yang <daniel.yj.yang@gmail.com>
#
# License: BSD 3 clause

import numpy as np

def SE(y_true: np.ndarray, y_pred: np.ndarray) -> np.ndarray:
    """
    squared error
    """
    return np.square(y_true - y_pred)

def SSE(y_true: np.ndarray, y_pred: np.ndarray) -> int:
    """
    sum of squared error
    """
    return SE(y_true, y_pred).sum()

def MSE(y_true: np.ndarray, y_pred: np.ndarray) -> int:
    """
    mean squared error
    Pros: penalizing large errors
    """
    return SE(y_true, y_pred).mean()

def RMSE(y_true: np.ndarray, y_pred: np.ndarray) -> int:
    """
    root mean squared error
    Pros: penalizing large errors
    """
    return MSE(y_true, y_pred) ** 0.5

def MAE(y_true: np.ndarray, y_pred: np.ndarray) -> int:
    """
    mean absolute error
    Pros: easy to interpret
    """
    return np.absolute(y_true - y_pred).mean()

def demo_metrics_regression():
    y_true = np.random.rand(1, 100)
    y_pred = np.random.rand(1, 100)
    for func in [SSE, MSE, RMSE, MAE]:
        print(f"{repr(func)}: {func(y_true, y_pred):.3f}")


import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from math import sqrt
from sklearn.model_selection import train_test_split

def main():

    df = pd.read_csv('father_son.csv',error_bad_lines=False, delimiter=',')
    X = df['Father'].values[:,np.newaxis]
    y = df['Son'].values
    print("Loading ...")
    #Find the mean
    x_mean = mean(X)
    y_mean = mean(y)
    #Subtract mean from the points
    sub_X = sub(X,x_mean)
    sub_y = sub(y,y_mean)
    #Multiply x and y
    mult_Xy = mul(sub_X,sub_y)
    meansq = meanSquared(sub_X)

    sumMul = sumXY(mult_Xy)
    sumMeansq =  sumXY(meansq)

    B1 = sumMul/sumMeansq
    B0 = y_mean - B1 * x_mean

    predicted = predict(X,B1,B0)

    print(sqrt(RMSE(predicted,y)/5))
    plt.scatter(X,y)
    plt.scatter(predicted,predicted)
    plt.xlabel((sqrt(RMSE(predicted,y)/5)))
    plt.ylabel('predicted')

    plt.show()

def mean(n):
    s = 0
    length = len(n)
    for j in range(0,length):
    	s = s+n[j]
    mean = s/length
    return mean

def sub(x,mean):
    arr = []
    length = len(x)
    for j in range(0,length):
        arr.append(x[j] - mean)
    return arr

def mul(x,y):
    mult = []
    length = len(x)
    for j in range(0,length):
        mult.append(x[j] * y[j])
    return mult

def meanSquared(x):
    meansq = []
    length = len(x)
    for j in range(0,length):
        meansq.append(x[j][0] *x[j][0])
    return meansq

def sumXY(x):
    sumRes = 0
    length = len(x)
    for j in range(0,length):
        sumRes = sumRes + x[j]
    return sumRes

def predict(x,B1,B0):
    pred = []
    length = len(x)
    for j in range(0,length):
        pred.append(B0+B1*x[j])
    return pred

def RMSE(predicted,y):
    rmse = 0
    error = []
    errorsq = []
    length = len(y)
    for i in range(0,length):
        error.append(predicted[i] - y[i])
    for j in range(0,length):
        errorsq.append(error[j][0] * error[j][0])
    for k in range (0,length):
        rmse = rmse + errorsq[k]
    return rmse

main()

Machine Learning & AI/Linear_Regression.py


# --------------
#Importing header files
import pandas as pd
import scipy.stats as stats
import math
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.stats.weightstats import ztest
from statsmodels.stats.weightstats import ztest
from scipy.stats import chi2_contingency

import warnings

warnings.filterwarnings('ignore')
#Sample_Size
sample_size=2000

#Z_Critical Score
z_critical = stats.norm.ppf(q = 0.95)

# Critical Value
critical_value = stats.chi2.ppf(q = 0.95, # Find the critical value for 95% confidence*
                      df = 6)   # Df = number of variable categories(in purpose) - 1

#Reading file
data=pd.read_csv(path)

#Code starts here
data_sample = data.sample(n=sample_size,random_state = 0)
print(data_sample.shape)
#population mean
true_mean = data['installment'].mean()
#population standard deviation
true_std = data_sample['installment'].std()
#sample mean
sample_mean = data_sample['installment'].mean()
#sample standard deviation
sample_std = data_sample['installment'].std()
#margin of error
margin_of_error = z_critical*(true_std/(np.sqrt(sample_size)))
print('Margin of error is',margin_of_error)
print('=='*30)
#confidence interval
confidence_interval = [round(sample_mean-margin_of_error,2),round(sample_mean+margin_of_error,2)]

print('Confidence Interval is ',confidence_interval)
print('=='*30)
print('True mean is',true_mean)
print('=='*30)

#Central Limit theorem for installment column
print('Central Limit Theorem for installment column')

#creating array of three sample sizes
## sample from population with different number of sampling
# a list of sample mean
meansample = []
# number of sample
numofsample = 1000
# sample size
samplesize = [20,50,100]
# for each number of sampling (1000 to 50000)
array_1 = []

for j in range(1,1000):
    for i in samplesize:
        array_1.append(data['installment'].sample(n=100,replace= True).mean())

#print(array)
plt.hist(array_1, bins=100)

plt.xlabel('installment')
plt.ylabel('frequency')
plt.title('Histogram of Installment frequency')
plt.axvline(x=np.mean(array_1),color='r')

print('=='*30)
print(' Performing the Z-test on int.rate')
data['int.rate'] = data['int.rate'].map(lambda x : x.split('%')[0])
data['int.rate'] = data['int.rate'].astype(float)
df_ = data[data['purpose']=='small_business']
value_ = data['int.rate'].mean()

z_statistic_1 ,p_value_1 = ztest(df_['int.rate'],value=value_,alternative='larger')
print('z-test is ',z_statistic_1)
print('=='*30)
print('p-value is',p_value_1)

print('=='*30)
print('Performing the two-sided the z-test on installments')
installment_paid_mean = data[data['paid.back.loan']=='Yes']['installment']
installment_notpaid_mean = data[data['paid.back.loan']=='No']['installment']

z_statistic_2 ,p_value_2 = ztest(x2 = installment_paid_mean,x1=installment_notpaid_mean,alternative='two-sided')
print('z-test is ',z_statistic_2)
print('=='*30)
print('p-value is',p_value_2)

print('=='*30)
print('Performing chi-test on purpose and loan default' )
yes=data[data['paid.back.loan']=='Yes']['purpose'].value_counts()
no=data[data['paid.back.loan']=='No']['purpose'].value_counts()

#Concating yes and no into a single dataframe
observed=pd.concat([yes.transpose(),no.transpose()], 1,keys=['Yes','No'])

print(observed)

chi2, p, dof, ex = chi2_contingency(observed)

import numpy,import scipy,from scipy,from statsmodels