from numpy import exp, array, random, dot

class NeuralNetwork():
	def __init__(self):
		# Seed the random number generator, so it generates the same numbers
		# every time the program runs
		random.seed(1)

		# We model a single neuron, with 3 input connections and 1 output connection.
		# We assign random weights to a 3 x 1 matrix, with values in the range -1 to 1
		# and mean 0
		self.synaptic_weights = 2 * random.random((3, 1)) - 1

	# The sigmoid function, which describes an s shaped curve
	# We pass the weights sum of the inputs through this function
	# to normalize them between 0 and 1
	def __sigmoid(self, x):
		return 1 / (1 + exp(-x))

	# gradient of the sigmoid curve
	def __sigmoid_derivative(self, x):
		return x * (1 - x)

	def train(self, training_set_inputs, training_set_outputs, number_of_training_iterations):
		for i in range(number_of_training_iterations):
			# The neural network output (y-hat)
			# pass the training set through our neural net
			output = self.predict(training_set_inputs)

			# output error (y - y-hat)
			# calculate the error
			error = training_set_outputs - output

			# error term (lowercase delta) and Gradient descent step
			# multiply the error by the input ad again by the gradient of the sigmoid curve
			adjustment = dot(training_set_inputs.T, error * self.__sigmoid_derivative(output))

			# adjust the weights
			self.synaptic_weights += adjustment

	def predict(self, inputs):
		# pass inputs through our neural network (our single neuron)
		return self.__sigmoid(dot(inputs, self.synaptic_weights))

if __name__ == '__main__':

	# Initialize a single neuron neural network
	neural_network = NeuralNetwork()

	print 'Random starting synapsis weights:'
	print neural_network.synaptic_weights

	# The training set. We have 4 examples, each consisting of 3 input values
	# and 1 output value.
	training_set_inputs = array([[0, 0, 1], [1, 1, 1], [1, 0, 1], [0, 1, 1]])
	training_set_outputs = array([[0, 1, 1, 0]]).T

	# Train the neural network using a training set.
	# Do it 10,000 times and make small adjustment each time.
	neural_network.train(training_set_inputs, training_set_outputs, 10000)

	print "New synapsis weights after training: "
	print neural_network.synaptic_weights

	# Test the neural network with a new situation
	print "Considering new situation [1, 0, 0] -> ?: "
	print neural_network.predict(array([1, 0, 0]))


"""
surface

simply generate points on the surface of a sphere
can be used to test simple prolate/oblate models

04-Mar-2021: introduction

"""
import numpy as np

def gen_sphere_surface(npoints,cartesian=True):
    """generate evenly spaced points on the surface of a sphere

    inputs
    -------------
    npoints    : integer
        the maximum number of points to generate on the surface of the sphere
    cartesian  : boolean, default True
        if true, return (x,y,z). If false, return (phi,theta)

    note that npoints is the maximum, because we are enforcing
    equal spacing: not all npoints values return even spacing.

    returns
    -------------
    x,y,z      : arrays, npoints long
        cartesian location of points on the unit sphere
    phi,theta  : arrays, npoints long
        surface of sphere points in radians

    """
    npoint = 0
    points = np.zeros([npoints,3])
    sphpoints = np.zeros([npoints,2])

    a = 4*np.pi/npoints
    d = np.sqrt(a)
    Mtheta = np.floor(np.pi/d).astype('int')
    dtheta = np.pi/Mtheta
    dphi   = a/dtheta

    for m in range(0,Mtheta):

        theta = np.pi*(m+0.5)/Mtheta
        Mphi  = np.floor(2*np.pi*np.sin(theta)/dphi).astype('int')

        for n in range(0,Mphi):
            phi = 2*np.pi*n/Mphi
            points[npoint] = np.array([np.sin(theta)*np.cos(phi),
                                       np.sin(theta)*np.sin(phi),
                                       np.cos(theta)])
            sphpoints[npoint] = np.array([phi,theta])
            npoint += 1

    if cartesian:
        return points[:npoint,0],points[:npoint,1],points[:npoint,2]
    else:
        return sphpoints[:npoint,0],sphpoints[:npoint,1]


import numpy as np
import pprint

ITERATIONS = 200
LEARNING_RATE = 0.01
BIAS = 1

count = 0

# reads the dataset from CSV file and normalizes it
def getData(filename):
    data = np.loadtxt(filename, delimiter=',') # load csv file into numpy array
    # normalize data (ignore last column because that is desired output)
    normalized = data[:, :-1] / data[:, :-1].max(axis=0)
    # put normalized data back into array with desired output column
    data[:, :-1] = normalized
    return data

# output function of neuron which calculates the weighted sum of inputs
# the output is 1 if sum is greater than 0, 0 otherwise
def activate(input, weights):
    # add bias for neuron to determine its own threshold
    output = BIAS * weights[0]
    # list comprehension to perform element wise multiplication of the input
    # and weights lists
    output += sum([x * w for x, w in zip(input, weights[1:])])
    return 1 if output > 0 else 0

# implementation of a 2 neuron perceptron where weights1 is the set of input
# weights feeding into neuron 1 and weights2 is the set of input weights feeding
# into neuron 2
# the outputs are concatenated and treated like a binary string
def perceptron(input, weights1, weights2):
    y1 = activate(input, weights1)
    y2 = activate(input, weights2)
    return str(y1) + str(y2)

# helper function to update the weights of perceptron while training
def updateWeights(x, w, y, d):
    weights = []
    # update weight of BIAS input, resulting in perceptron determining its own
    # threshold
    weights.append(w[0] + ((d - y) * LEARNING_RATE * BIAS))
    # update weight, w, for each input, x
    for i in range(len(x)):
        weights.append(w[i+1] + ((d - y) * LEARNING_RATE * x[i]))
    return weights

def trainPerceptron(input, weights1, weights2):
    global count
    for i in range(ITERATIONS):
        for row in input:
            y = perceptron(row[:-1], weights1, weights2)
            # print('y is: {}, and d is: {}'.format(int(y,2), row[len(row)-1]))
            d = row[len(row)-1]
            test = format(int(d), '02b')
            if int(test[0]) - int(y[0]) != 0:
                weights1 = updateWeights(row[:-1], weights1, int(y[0]), int(test[0]))
            if int(test[1]) - int(y[1]) != 0:
                weights2 = updateWeights(row[:-1], weights2, int(y[1]), int(test[1]))
            if int(y, 2) - int(row[len(row)-1]) == 0:
                count += 1
            # print(y)
        print('This many were right: {}'.format(count))
        count = 0
    print(weights1)
    print(weights2)
    return (weights1, weights2)

def testPerceptron(input, weights1, weights2):
    global count
    count = 0
    for row in input:
        y = perceptron(row[:-1], weights1, weights2)
        d = row[len(row)-1]
        # print(y)
        if int(y, 2) - int(d) == 0:
            count +=1
    print('This many were right: {}'.format(count))

def main():
    weights1 = [-0.1, 1, 0.1, 1, 0.1, -1, 0.1, -1]
    weights2 = [-1, 0.1, 1, 0.1, 1, 0.1, 1, 0.1]
    # first weight is for bias
    # weights1 = [-0.3600000000000002, 1.0124787535410458, 0.008660869565218606, 0.3704475661548351, -0.14466367041199163, -1.1815497148523761, 0.125947634237115, 0.47409770992366457]
    # weights2 = [-0.3399999999999994, -1.1172521246458798, 0.544226086956539, 0.7400718719372663, 0.7303610486891207, -0.307255145053313, -0.1705348219032502, -0.35072519083968534]

    trainingData = getData('trainSeeds.csv')
    np.random.shuffle(trainingData)

    # train perceptron
    weights1, weights2 = trainPerceptron(trainingData, weights1, weights2)

    testData = getData('testSeeds.csv')
    np.random.shuffle(testData)

    testPerceptron(testData, weights1, weights2)

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt

def deltau(u):
    # central difference
    return np.r_[-3*u[0]+4*u[1]-u[2],  u[2:]-u[:-2],  3*u[-1]-4*u[-2]+u[-3]]

def laplacianCyl(r, u):
    # (1/r) (r u_r)_r
    dr = r[1] - r[0]
    dudr = deltau(u)/(2*dr)
    return deltau(r*dudr)/(2*dr)/r

def laplacianSph(r, u):
    # (1/r*r) (r*r u_r)_r
    dr = r[1] - r[0]
    dudr = deltau(u)/(2*dr)
    return deltau(r*r*dudr)/(2*dr)/(r*r)

def laplacianCar(y, u):
    # u_yy
    dy = y[1] - y[0]
    return np.r_[2*u[0]-5*u[1]+4*u[2]-u[3], u[2:]+u[:-2]-2*u[1:-1], 2*u[-1]-5*u[-2]+4*u[-3]-u[-4]]/(dy*dy)

def test_sine():
    x = np.linspace(0, 2*np.pi)
    y = np.sin(x)
    plt.plot(x, laplacianCar(x,y), 'r--o', x, -np.sin(x), 'k-')
    plt.show()

if __name__ == "__main__":
    test_sine()


from numpy import sqrt, mean, square, array, count_nonzero

class Metric():

    greater_is_better = None

    @staticmethod
    def evaluate(prediction, target):
        pass

class RootMeanSquaredError(Metric):

    greater_is_better = False

    @staticmethod
    def evaluate(prediction, target):
        return sqrt(mean(square(prediction - target)))

class Accuracy(Metric):

    greater_is_better = True

    @staticmethod
    def evaluate(prediction, target):
        prediction_label = prediction > 0.5
        return count_nonzero(prediction_label == target) / prediction.shape[0]

def is_better(value_1, value_2, metric):
    if metric.greater_is_better:
        return value_1 > value_2
    else:
        return value_1 < value_2


import numpy as np

def l1(alpha, w,grad=False):
    if grad:
        return alpha * np.sign(w)
    else:
        return alpha * np.sum(np.abs(w))

def l2(alpha, w, grad=False):
    if grad:
        return alpha * w
    else:
        return alpha * np.sum(w * w)

def l1_l2(alpha, w, l1_ratio = 0.5, grad=False):
    if grad:
        return alpha * ((l1_ratio * np.sign(w)) + ((1 - l1_ratio) * w))
    else:
        return alpha * ((l1_ratio * np.sum(np.abs(w))) + ((1 - l1_ratio) * np.sum(w * w)))


import numpy as np

class LinearRegression:
    DEFAULT_ALPHA = 0.0005
    DEFAULT_EPOCHS_COUNT = 1000

    def __init__(self, x: np.ndarray, y: np.ndarray):
        self.y = y
        self.x = self._construct_x(x)
        self.theta = np.ones(2).reshape(2, 1)

    def precompute(self, alpha=DEFAULT_ALPHA, epochs_count=DEFAULT_EPOCHS_COUNT) -> np.ndarray:
        """
        Prepare model.

        Use Gradient Dissent to prepare model for future use.

        :param alpha: Learning rate for the model.
        :param epochs_count: Iterations count for Gradient Dissent.
        :return: Theta data (np.ndarray[b, multiplier]).
        """
        for _ in range(int(np.sqrt(epochs_count))):
            self._gd(alpha, epochs_count)
        return self.theta

    def _gd(self, alpha: float, epochs_count: int) -> np.ndarray:
        hx = np.matmul(self.x, self.theta)
        cost_1 = self.compute_cost(self.theta)
        for _ in range(epochs_count):
            temp = (alpha / self.y.size) * np.matmul(self.x.T, (hx - self.y))
            cost_2 = self.compute_cost(self.theta - temp)

            if cost_1 > cost_2:
                cost_1 = cost_2
                self.theta -= temp

        return self.theta

    def compute_cost(self, theta=None) -> float:
        """
        Compute sum of squared error.

        :param theta: Theta data (np.ndarray[b, multiplier]).
        :return: Calculated sum of squared error.
        """
        if theta is None:
            theta = self.theta
        hx = np.matmul(self.x, theta)
        return np.sum(np.power(np.subtract(hx, self.y), 2)) / (2 * self.y.size)

    def predict(self, one_x: float) -> float:
        b, m = self.theta[0][0], self.theta[1][0]
        return one_x * m + b

    @staticmethod
    def _construct_x(x: np.ndarray) -> np.ndarray:
        return np.hstack((np.ones(x.size).reshape(x.size, 1), x))

    def r2_score(self, x: np.ndarray, y: np.ndarray) -> float:
        """
        Calculate the r2 score of the model.

        :param x: Input dataset.
        :param y: Target dataset.
        :return Calculated r2 score of the model.
        """
        predicted = self._construct_x(x) @ self.theta
        return 1 - np.sum((y - predicted) ** 2) / np.sum((y - y.mean()) ** 2)


import numpy as np
import matplotlib.pyplot as plt

N = 50

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

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

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

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

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

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


import numpy as np

def rand_init_weight(Lin, Lout):
    """One effective strategy for choosing init is to base it on the number of units in the
       network. A good choice of epsilon is

       epsilon =√6/√(Lin+Lout)

       here Lin = sl and Lout = sl+1 are the number of units in the layers adjacent to Θ(l)
    """
    W = np.zeros((Lout, Lin+1))
    epsilon = np.sqrt(6)/np.sqrt(Lin+Lout)

    W = np.random.uniform(size=(Lout, Lin+1)) * 2 * epsilon - epsilon

    return W


import numpy as np
import matplotlib.pyplot as plt

#------------------------------Question 1--------------------------
def rp(x,p):
    t=1
    i=0
    if x==0 :
        return 0

    if x>1 or x<-1 : # si mon nombre n'est pas de la forme 0,....
        # on calcul t tel que x*t soit de la forme [+-][0-9],....
        while x*t>=10 or x*t<= -10 :
            t/=10
            i+=1
        res=round(x*t,p-1)*10**i

    else : # si mon nombre est de la forme 0,.....
        # on calcul t tel que x*t soit de la forme [+-][0-9],....
        while -1<=x*t<=1  :
            t*=10
            i+=1
        res = round(x,p-1+i)

    return res

def test_rp():
    print(rp(3.141592658,4))
    print(rp(3.141592658,6))
    print(rp(10507.1823,4))
    print(rp(10507.1823,6))
    print(rp(0.0001857563,4))
    print(rp(0.0001857563,6))

#test_rp()

#------------------------------Question 2--------------------------
def rp_add(x,y,p):
    return rp(rp(x,p)+rp(y,p),p)
def rp_multi(x,y,p):
    return rp(rp(x,p)*rp(y,p),p)

#------------------------------Question 3--------------------------
def erreur_relative_add (x,y,p):
    return abs((x+y)-rp_add(x,y,p))/abs(x+y)
def erreur_relative_multi (x,y,p):
    if (x==0 or y==0):
        return 0 ;
    return abs((x*y)-rp_multi(x,y,p))/abs(x*y)

#------------------------------Question 4--------------------------
def tracer(x,y)  :
    a=-y
    b=y
    y=np.linspace(a,b,201)
    z=np.linspace(0,0,201)
    t=np.linspace(0,0,201)
    for i in range (0,201):
        z[i]=erreur_relative_add(x,y[i],3)
        t[i]=erreur_relative_multi(x,y[i],3)
    plt.plot(y,z,label="Erreur relative de l'addition")
    plt.plot(y,t,label="Erreur relative de la multiplication")
    plt.title("Erreurs relatives sur l'addition et la multiplication")
    plt.legend()
    plt.xlabel("y")
    plt.ylabel("Erreur relative")
    plt.show()

#tracer(1000000,1)        Tests pour différentes valeurs de x
#tracer(100000,40)
tracer(0.000119,1)

#------------------------------Question 5--------------------------
def log_2(p):
    s=0
    signe=1
    t=10**p
    erreur=0
    for i in range (1,t):
        s+=signe/i
        signe*=-1
    return rp(s,p)

def rp_log_2(p):
    s=0
    signe=1
    t=10**p
    for i in range (1,t):
        s=rp_add(s,signe/i,p)
        signe*=-1
    return rp(s,p)
def test_log ():
    a2=rp(np.log(2),9)
    a1=rp(np.log(2),5)
    b1=rp_log_2(5)
    b2=log_2(9)
    print("l'erreur relative du calcul de log 2 en utilisant l'addition machine  sur 9 decimal est ", a2,"-",b2,"=",abs(a2-b2))
    print("l'erreur relative du calcul de log 2 en utilisant l'addition definie sur  5 decimal est ", a1,"-",b1,"=",abs(a1-b1))
#test_log()
#l'erreur relative du calcul de log 2 en utilisant l'addition machine  sur 9 decimal est  0.693147181 - 0.693147181 = 0.0
#l'erreur relative du calcul de log 2 en utilisant l'addition definie sur  5 decimal est  0.69315 - 0.69321 = 5.99999999999e-05


#!/usr/bin/env python3

import numpy as np

def er_random(N, p):
    '''
    Construct an unweighted bi-directional ER random network without self-loop

    Arguments:
    1. N:    Total number of nodes in the network
    2. p:    Connection probability

    Returns:
    1. A:    Adjacency matrix
    '''
    assert type(N) == int and N > 0, "N must be a positive integer"
    assert (type(p) == int or type(p) == float), "p must be a real number"
    assert p >= 0 and p <= 1, "p must be within 0 and 1 (inclusive)"

    if p == 0:
        print("[WARN] p = 0, the network has no links")
        A = np.zeros((N, N))
        return A

    elif p == 1:
        print("[WARN] p = 1, the network is fully connected")
        A = np.ones((N, N))
        return A

    else:
        # Generate a random matrix with size N x N
        # Each element takes value between 0 and 1
        A = np.random.rand(N, N)

        # Set the connectivity based on input probability threshold p
        A = A < p
        A = A.astype(int)

        # Extract only the upper triangle elements
        # And set other (lower triangle + diagonal) to zero
        A = np.triu(A, 1)

        # Symmetrize the matrix
        A = A + A.T

        return A


import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import newton

R = 8.314e-5  # universal gas constant, m3-bar/K-mol
class Molecule:
    """
    Store molecule info here
    """
    def __init__(self, name, Tc, Pc, omega):
        """
        Pass parameters desribing molecules
        """
        #! name
        self.name = name
        #! Critical temperature (K)
        self.Tc = Tc
        #! Critical pressure (bar)
        self.Pc = Pc
        #! Accentric factor
        self.omega = omega

    def print_params(self):
        """
        Print molecule parameters.
        """
        print("""Molecule: %s.
        \tCritical Temperature = %.1f K
        \tCritical Pressure = %.1f bar.
        \tAccentric factor = %f""" % (self.name, self.Tc, self.Pc, self.omega))

def preos(molecule, T, P, plotcubic=True, printresults=True):
    """
    Peng-Robinson equation of state (PREOS)
    http://en.wikipedia.org/wiki/Equation_of_state#Peng.E2.80.93Robinson_equation_of_state
    :param molecule: Molecule molecule of interest
    :param T: float temperature in Kelvin
    :param P: float pressure in bar
    :param plotcubic: bool plot cubic polynomial in compressibility factor
    :param printresults: bool print off properties

    Returns a Dict() of molecule properties at this T and P.
    """
    # build params in PREOS
    Tr = T / molecule.Tc  # reduced temperature
    a = 0.457235 * R**2 * molecule.Tc**2 / molecule.Pc
    b = 0.0777961 * R * molecule.Tc / molecule.Pc
    kappa = 0.37464 + 1.54226 * molecule.omega - 0.26992 * molecule.omega**2
    alpha = (1 + kappa * (1 - np.sqrt(Tr)))**2

    A = a * alpha * P / R**2 / T**2
    B = b * P / R / T

    # build cubic polynomial
    def g(z):
        """
        Cubic polynomial in z from EOS. This should be zero.
        :param z: float compressibility factor
        """
        return z**3 - (1 - B) * z**2 + (A - 2*B - 3*B**2) * z - (
                A * B - B**2 - B**3)

    # Solve cubic polynomial for the compressibility factor
    z = newton(g, 1.0)  # compressibility factor
    rho = P / (R * T * z)  # density

    # fugacity coefficient comes from an integration
    fugacity_coeff = np.exp(z - 1 - np.log(z - B) - A / np.sqrt(8) / B * np.log(
                (z + (1 + np.sqrt(2)) * B) / (z + (1 - np.sqrt(2)) * B)))

    if printresults:
        print("""PREOS calculation at
        \t T = %.2f K
        \t P = %.2f bar""" % (T, P))
        print("\tCompressibility factor : ", z)
        print("\tFugacity coefficient: ", fugacity_coeff)
        print("\tFugacity at pressure %.3f bar = %.3f bar" % (
                P, fugacity_coeff * P))
        print("\tDensity: %f mol/m3" % rho)
        print("\tMolar volume: %f L/mol" % (1.0 / rho * 1000))
        print("\tDensity: %f v STP/v" % (rho * 22.4 / 1000))
        print("\tDensity of ideal gas at same conditions: %f v STP/v" % (
                rho * 22.4/ 1000 * z))

    if plotcubic:
        # Plot the cubic equation to visualize the roots
        zz = np.linspace(0, 1.5)  # array for plotting

        plt.figure()
        plt.plot(zz, g(zz), color='k')
        plt.xlabel('Compressibility, $z$')
        plt.ylabel('Cubic $g(z)$')
        plt.axvline(x=z)
        plt.axhline(y=0)
        plt.title('Root found @ z = %.2f' % z)
        plt.show()
    return {"density(mol/m3)": rho, "fugacity_coefficient": fugacity_coeff,
            "compressibility_factor": z, "fugacity(bar)": fugacity_coeff * P,
            "molar_volume(L/mol)": 1.0 / rho * 1000.0}

def preos_reverse(molecule, T, f, plotcubic=False, printresults=True):
    """
    Reverse Peng-Robinson equation of state (PREOS) to obtain pressure for a particular fugacity
    :param molecule: Molecule molecule of interest
    :param T: float temperature in Kelvin
    :param f: float fugacity in bar
    :param plotcubic: bool plot cubic polynomial in compressibility factor
    :param printresults: bool print off properties

    Returns a Dict() of molecule properties at this T and f.
    """
    # build function to minimize: difference between desired fugacity and that obtained from preos
    def g(P):
        """
        :param P: pressure
        """
        return (f - preos(molecule, T, P, plotcubic=False, printresults=False)["fugacity(bar)"])

    # Solve preos for the pressure
    P = newton(g, f)  # pressure

    # Obtain remaining parameters
    pars = preos(molecule, T, P, plotcubic=plotcubic, printresults=printresults)
    rho = pars["density(mol/m3)"]
    fugacity_coeff = pars["fugacity_coefficient"]
    z = pars["compressibility_factor"]

    return {"density(mol/m3)": rho, "fugacity_coefficient": fugacity_coeff,
            "compressibility_factor": z, "pressure(bar)": P,
            "molar_volume(L/mol)": 1.0 / rho * 1000.0}

# TODO: Implement mixture in object-oriented way as well
def preos_mixture(molecule_a, molecule_b, delta, T, P_total, x, plotcubic=True, printresults=True):
    """
    Peng-Robinson equation of state (PREOS) for a binary mixture
    http://en.wikipedia.org/wiki/Equation_of_state#Peng.E2.80.93Robinson_equation_of_state
    :param molecule_a: Molecule molecule 1 of interest
    :param molecule_b: Molecule molecule 2 of interest
    :param delta: binary interaction parameter between molecules a and b
    :param T: float temperature in Kelvin
    :param P_total: float total pressure in bar
    :param x: array mole fractions
    :param plotcubic: bool plot cubic polynomial in compressibility factor
    :param printresults: bool print off properties
    """
    # build arrays of properties
    Tc = np.array([molecule_a.Tc, molecule_b.Tc])
    Pc = np.array([molecule_a.Pc, molecule_b.Pc])
    omega = np.array([molecule_a.omega, molecule_b.omega])

    # build params in PREOS
    Tr = T / Tc  # reduced temperature
    a0 = 0.457235 * R**2 * Tc**2 / Pc
    b = 0.0777961 * R * Tc / Pc
    kappa = 0.37464 + 1.54226 * omega - 0.26992 * omega**2
    a = a0 * (1 + kappa * (1 - np.sqrt(Tr)))**2

    # apply mixing rules
    aij = (1.0 - delta) * np.sqrt(a[0] * a[1])
    a_mix = a[0] * x[0]**2 + a[1] * x[1]**2 + 2.0 * x[0] * x[1] * aij
    b_mix = x[0] * b[0] + x[1] * b[1]
    A = a_mix * P_total / R**2 / T**2
    B = b_mix * P_total / R / T

    # build cubic polynomial
    def g(z):
        """
        Cubic polynomial in z from EOS. This should be zero.
        :param z: float compressibility factor
        """
        return z**3 - (1 - B) * z**2 + (A - 2*B - 3*B**2) * z - (
                A * B - B**2 - B**3)

    # Solve cubic polynomial for the compressibility factor
    z = newton(g, 1.0)  # compressibility factor
    rho = P_total / (R * T * z)  # density

    Lnfug_0 = -np.log(z - B) + (z - 1.0) * b[0] / b_mix - A / np.sqrt(8) / B * (2.0 / a_mix * (x[0] * a[0] + x[1] * aij) - b[0] / b_mix) *\
        np.log((z + (1.0 + np.sqrt(2)) * B) / (z + (1.0 - np.sqrt(2)) * B))
    Lnfug_1 = -np.log(z - B) + (z - 1.0) * b[1] / b_mix - A / np.sqrt(8) / B * (2.0 / a_mix * (x[1] * a[1] + x[0] * aij) - b[1] / b_mix) *\
        np.log((z + (1.0 + np.sqrt(2)) * B) / (z + (1.0 - np.sqrt(2)) * B))

    # fugacity coefficient comes from an integration
    fugacity_coefs = np.exp(np.array([Lnfug_0, Lnfug_1]))

    if printresults:
        print("""PREOS calculation at
        \t T = %.2f K
        \t P, total = %.2f bar""" % (T, P_total))
        print("\tDensity: %f mol/m3" % rho)
        print("\tCompressibility factor : %f" % z)
        print("Component 0, %s:" % molecule_a.name)
        print("\tFugacity coefficient: %f" % fugacity_coefs[0])
        print("\tFugacity: %f bar" % (fugacity_coefs[0] * x[0] * P_total))
        print("Component 1, %s:" % molecule_b.name)
        print("\tFugacity coefficient: %f" % fugacity_coefs[1])
        print("\tFugacity: %f bar" % (fugacity_coefs[1] * x[1] * P_total))

    if plotcubic:
        # Plot the cubic equation to visualize the roots
        zz = np.linspace(0, 1.5)  # array for plotting

        plt.figure()
        plt.plot(zz, g(zz), color='k')
        plt.xlabel('Compressibility, $z$')
        plt.ylabel('Cubic $g(z)$')
        plt.axvline(x=z)
        plt.axhline(y=0)
        plt.title('Root found @ z = %.2f' % z)
        plt.show()
    return {"density(mol/m3)": rho, "fugacity_coefficients": fugacity_coefs,
            "compressibility_factor": z}


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Mathematical utility functions
"""

import numpy as np
from numba import jit

@jit(nopython=True, cache=True)
def xyz2sph(v):
    """
    Convert a unit vector from cartesian coordinates to spherical coordinates
    :param v: unit vector
    :return: [theta, phi] polar coordinates
    """

    return np.array([np.arccos(v[2]), np.arctan2(v[1], v[0])])

@jit(nopython=True, cache=True)
def sph2xyz(theta, phi):
    """
    Convert spherical coordinates to unit vector
    :param theta: theta angle
    :param phi: phi angle
    :return: (x, y, z) coordinates
    """
    x = np.sin(theta) * np.cos(phi)
    y = np.sin(theta) * np.sin(phi)
    z = np.cos(theta)
    return np.array([x, y, z])

@jit(nopython=True, cache=True)
def sph_dot(theta1, theta2, delta_phi):
    """
    Compute the dot product of two unit vectors in spherical coordinates
    """
    return np.sin(theta1) * np.cos(delta_phi) * np.sin(theta2) + np.cos(theta1) * np.cos(theta2)

@jit(nopython=True, cache=True)
def sph_urand():
    """
    Generate random unit vector in spherical coordinates
    :return: (theta, phi) the two angles
    """

    phi = np.random.uniform(0, 2 * np.pi)
    u = np.random.uniform(0, 1)
    theta = np.arccos(2 * u - 1)

    return theta, phi


# salimt
# Import libraries
# Import libraries
import numpy as np
from scipy import optimize

# First we define the functions, YOU SHOULD IMPLEMENT THESE
def f (x, y) :
  return - np.exp(x - y**2 + x*y)

def g (x, y) :
  return np.cosh(y) + x - 2

def dfdx (x, y) :
  return (1 + y) * f (x, y)

def dfdy (x, y) :
  return (- 2*y + x) * f (x, y)

def dgdx (x, y) :
  return 1

def dgdy (x, y) :
  return np.sinh(y)

# Use the definition of DL from previously.
def DL (xyλ) :
    [x, y, λ] = xyλ
    return np.array([
            dfdx(x, y) - λ * dgdx(x, y),
            dfdy(x, y) - λ * dgdy(x, y),
            - g(x, y)
        ])

# To score on this question, the code above should set
# the variables x, y, λ, to the values which solve the
# Langrange multiplier problem.

# I.e. use the optimize.root method, as you did previously.

x, y, λ = (1,0,1)
x, y, λ = optimize.root(DL, [x, y, λ]).x
print("x = %g" % x)
print("y = %g" % y)
print("λ = %g" % λ)
print("f(x, y) = %g" % f(x, y))

Imperial College London - Mathematics for Machine Learning Specialization/Imperial College London - Mathematics for Machine Learning Multivariate Calculus/lagrange-multipliers.py


import numpy as np
from scipy import sparse

def comm_classes(P):
    '''
    Parameters:
        P - scipy.sparse.csr_matrix
            Markov chain transition matrix, rows sum to 1

    Outputs:
        classes - list
            List of lists describing the communication classes of the Markov chain
        types - list
            List of communication class types, Transient or Absorbing, corresponding to classes output
    '''
    num_classes,class_map = sparse.csgraph.connected_components(P,directed = True,connection = 'strong')

    CC = []
    for i in range(num_classes):
        CC += [list(np.where(class_map == i)[0])]

    Ts = []
    transient_classes = np.array([])
    for i in range(num_classes):
        if sum([P[CC[i][0],j] for j in CC[i]]) < 1:
            Ts += ['Transient']
            transient_classes = np.append(transient_classes,i)
        else:
            Ts += ['Absorbing']
    transient_classes = [int(i) for i in transient_classes]
    order = list(transient_classes) + [int(i) for i in range(num_classes) if not i in transient_classes]
    classes = []
    types = []
    for i in order:
        classes += [CC[i]]
        types += [Ts[i]]
    return classes, types

def decompose(P,classes,types):
    '''
    Parameters:
        P - scipy.sparse.csr_matrix
            Markov chain transition matrix, rows sum to 1
        classes - list
            List of lists describing the communication classes of the Markov chain
        types - list
            List of communication class types, Transient or Absorbing, corresponding to classes output

    Outputs:
        diagonal - list
            list of scipy.sparse.csr_matrix objects along the diagonal for sub-markov chains
        upper_triangle - list
            list of scipy.sparse.csr_matrix objects upper triangle matrices indicating transient class escape probabilities
    '''
    num_classes = len(classes)
    state_order = []
    for i in range(num_classes):
        state_order += classes[i]

    diagonal = []
    upper_triangle = []
    for i in range(num_classes):
        diagonal += [P[classes[i],:][:,classes[i]]]
        if types[i] == 'Transient':
            state_order = [j for j in state_order if not j in classes[i]]
            upper_triangle += [P[classes[i],:][:,state_order]]
    diagonal = [sparse.csr_matrix(d) for d in diagonal]
    upper_triangle = [sparse.csr_matrix(d) for d in upper_triangle]
    return diagonal,upper_triangle

def construct_canonical_matrix(diagonal,upper_triangle):
    '''
    Inputs:
        diagonal - list
            list of scipy.sparse.csr_matrix objects along the diagonal for sub-markov chains
        upper_triangle - list
            list of scipy.sparse.csr_matrix objects upper triangle matrices indicating transient class escape probabilities

    Outputs:
        A - scipy.sparse.csr_matrix
            Markov chain transition matrix in canonocal format
        Q - scipy.sparse.csr_matrix
            Transient portionof the Markov chain
        R - scipy.sparse.csr_matrix
            Escape probability matrix from the transient portion to absorbing
        I - scipy.sparse.csr_matrix
            Absorbing class submatrix, itself a transition matrix

        A = (Q | R)
            (0 | I)
    '''
    N = sum([d.shape[0] for d in diagonal])
    A = np.zeros((N,N))
    Tstates = 0
    current_loc = 0
    for i in range(len(diagonal)):
        if i < len(upper_triangle):
            m,n = upper_triangle[i].shape
            A[current_loc:current_loc+m,current_loc+m:current_loc+m+n] = upper_triangle[i].todense()
            Tstates += m
        else:
            m = diagonal[i].shape[0]
        A[current_loc:current_loc+m,current_loc:current_loc+m] = diagonal[i].todense()
        current_loc += m
    Q = sparse.csr_matrix(A[:Tstates,:Tstates])
    R = sparse.csr_matrix(A[:Tstates,Tstates:])
    I = sparse.csr_matrix(A[Tstates:,Tstates:])
    A = sparse.csr_matrix(A)
    return A,Q,R,I


# Analytic Hierarchy Process

import numpy as np

# class myAHP
class myAHP:
    # __init__(self)
    def __init__(self):
        # print("__init__(self).start...")

        self.RI_dict = {
             1: 0,       2: 0,        3: 0.58,    4: 0.90,    5:  1.12,   6: 1.24,    7: 1.32,
             8: 1.41,    9: 1.45,    10: 1.51,   11: 1.54,   12: 1.56,   13: 1.57,   14: 1.59,
            15: 1.59,   16: 1.60,    17: 1.61,   18: 1.62,   19: 1.63,   20: 1.64
        }
        self.comparison_matrix = np.array([
            [0.50, 0.77, 0.48, 0.68, 0.47, 0.38],
            [0.23, 0.50, 0.28, 0.45, 0.32, 0.23],
            [0.52, 0.72, 0.50, 0.75, 0.48, 0.40],
            [0.32, 0.55, 0.25, 0.50, 0.23, 0.20],
            [0.53, 0.68, 0.52, 0.77, 0.50, 0.48],
            [0.62, 0.77, 0.60, 0.80, 0.52, 0.50]
        ])
    # __init__(self)

    # get_w(array)
    def get_w(self, array):
        print("get_w(array).start...")

        row = array.shape[0]

        a_axis_0_sum = array.sum(axis=0)
        # print(a_axis_0_sum)

        b = array / a_axis_0_sum  # new matrix b
        # print(b)

        b_axis_0_sum = b.sum(axis=0)
        b_axis_1_sum = b.sum(axis=1)  #
        print("b_axis_1_sum")
        print(b_axis_1_sum)

        w = b_axis_1_sum / row  #

        nw = w * row

        AW = (w * array).sum(axis=1)
        print("AW:")
        print(AW)

        max_max = sum(AW / (row * w))
        print("max_max:")
        print(max_max)

        # calculating
        print("calculating")
        CI = (max_max - row) / (row - 1)
        print("CI:")
        print(CI)
        print("row:")
        print(row)
        print("RI_dictt[row]")
        print(self.RI_dict[row])
        print("/*------------------------*/")
        CR = CI / self.RI_dict[row]
        print("over!")

        if CR < 0.1:
            print("CR:")
            print(CR)
            # print(round(CR, 3))
            print('success!!')
            return w
        else:
            print(round(CR, 3))
            print('please modify your data')

        print("get_w(array).end...")
    # get_w(array)

    # def main(array)
    def mainAlgorithm(self, array):
        print("main(array).start...")

        if type(array) is np.ndarray:
            result = self.get_w(array)
            return result
        else:
            print('please input an instance of numpy')

        print("main(array).start...")
    # def main(array)

    # comparison_economic_function()
    def comparison_function(self):
        print("comparison_function(self).start...")

        e = np.array([
            [1,   2,   7,   5],
            [1/2, 1,   4,   3],
            [1/7, 1/4, 1,   2],
            [1/5, 1/4, 1/2, 1]
        ])

        a = self.comparison_matrix
        b = self.comparison_matrix
        c = self.comparison_matrix
        d = self.comparison_matrix
        f = self.comparison_matrix

        # print("e")
        e = self.mainAlgorithm(e)
        # print("e")

        # print("a")
        a = self.mainAlgorithm(a)
        # print("a")

        # print("b")
        b = self.mainAlgorithm(b)
        # print("b")

        # print("c")
        c = self.mainAlgorithm(c)
        # print("c")

        # print("d")
        d = self.mainAlgorithm(d)
        # print("d")

        try:
            print("try")

            # res = np.array([a, b, c, d, f])
            res = np.array([a, b, c, d])
            # print("res:")
            # print(res)

            resT = np.transpose(res)
            # print("resT:")
            # print(resT)
            # print("e:")
            # print(e)
            print("ret:")
            ret = (resT * e).sum(axis=1)
            print(ret)
            print("try.end: ret")
            return ret
        except TypeError:
            print('data error')

        print("comparison_function(self).end...")
    # comparison_function()
# class myAHP

# __main__
if __name__ == '__main__':
    print("MyAHP.py__main__.start...")
    ahp = myAHP()
    ahp.comparison_function()
    print("MyAHP.py__main__.end...")
# __main__


import random
import numpy as np

rand = random.SystemRandom()

def uniform(n, lower, upper):
    return np.array([rand.randint(lower, upper) for _ in range(n)], dtype=np.int)

class RLWE:
    def __init__(self, n, q):
        self.n = n
        self.q = q
        self.b = q // 2

    def generate_keys(self):
        s = uniform(self.n, -self.b, self.b)
        e = uniform(self.n, -self.b, self.b)
        a = uniform(self.n, 0, self.q)
        pub = (a * s + e) % self.q, a
        priv = s, e, a
        return pub, priv

    def sign(self, msg, priv):
        s, e, a = map(np.array, priv)
        msg = np.array(list(msg))
        y1 = uniform(self.n, -self.b, self.b)
        y2 = uniform(self.n, -self.b, self.b)
        w = (a*y1 + y2) % self.q
        c = (w + msg) % self.q
        z1 = (s*c + y1) % self.q
        z2 = (e*c + y2) % self.q
        return c, z1, z2

    def verify(self, msg, pub, sign):
        msg = np.array(list(msg))
        pub, a = map(np.array, pub)
        c, z1, z2 = map(np.array, sign)
        return all(c == ((a*z1 + z2 - pub*c) + msg) % self.q)


""" this script record useful functions """
import numpy as np
import matplotlib.pyplot as plt

#from scipy import fftpack

def transform_dft2(img, mode):
    ## convert to float
    img = img.astype(float)
    ## mean subtraction
    img = img - np.mean(img)
    ## rfft is different to fft
    if 'forward' in mode:
        ## 2d dft
        #img_dft = fftpack.fft2(img)
        img_dft = np.fft.fft2(img)
        ## shift to center
        img_dft_shifted = np.fft.fftshift(img_dft)
        return img_dft_shifted
    if 'inverse' in mode:
        img_dft_shifted = img
        ## shift to corner
        img_dft= np.fft.ifftshift(img_dft_shifted)
        ## 2d idft
        img = np.fft.rfft2(img_dft)
        return img

def cal_ringmean(img_fre):
    ## calculate ringing of 2d matrix from the center
    ## set the maximum spatial frequency
    res_max_spatial  = np.min([img_fre.shape[0],  img_fre.shape[1]])
    res_max_spatial_half = np.floor(res_max_spatial / 2).astype(int)
    ## calculate radial average of frequency coefficent
    coordinates_1d = np.arange(-res_max_spatial_half - 1, res_max_spatial_half, 1)
    coordinates_2d_x, coordinates_2d_y = np.meshgrid(coordinates_1d, coordinates_1d)
    radius_map = np.hypot(coordinates_2d_x, coordinates_2d_y)
    radius_map_floor = np.floor(radius_map)
    psd = np.zeros( (res_max_spatial_half + 1, 1) )

    for radius in range( res_max_spatial_half - 1):
        radius_loc = np.where(radius_map_floor == radius)
        coeffs = []
        #coeffs1 = img_fre_norm.take(radius_loc)
        coeffs = img_fre[radius_loc]
        psd[radius] = np.mean(coeffs)

    maxX = 10 ** (np.ceil(np.log10(float(res_max_spatial_half))));
    frequency = np.linspace(1, maxX, len(psd));
    frequency = (frequency) / (np.max(frequency)) / 2;
    return psd, frequency

def cal_auto_power_spectral_density(img_fre):
    img_fre_square = np.abs(img_fre ** 2)
    psd_auto, fre = cal_ringmean(img_fre_square)
    return psd_auto, fre

def cal_cross_power_spectral_density(img_fre, img_fre2):
    #img_fre_yx = np.multiply(img_fre2, np.conjugate(img_fre))
    img_fre_yx = np.multiply(img_fre2, img_fre)
    img_fre_xx = np.multiply(img_fre, img_fre)
    img_fre_cross = np.divide(img_fre_yx, img_fre_xx)
    psd_cross, fre = cal_ringmean(img_fre_cross)
    return psd_cross, fre

def cal_mtf_of_psd(coeff_src, coeff_ref, mode):
    ###
    if 'direct' in mode:
        coeff_src = coeff_src / np.max(coeff_src)
        coeff_ref = coeff_ref / np.max(coeff_ref)

        mtf = np.divide(coeff_src, coeff_ref)

    if 'cross' in mode:
       x=1

    return mtf


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

#Create data
x=np.array([0.,0.5,1.])
y_exact=np.array([0.,1.,1.])

#Intiliase W and b
W=0.5
b=0.5

#Set other constants
N=3
eta=0.01
MaxIter=16

#Initialise approximation
F=W * x + b

#Functions
def cost():
    return (1/N) * np.sum( 0.5* (y_exact - F)**2 )
#Partial derivates of cost
def cost_W(k):
    return (1/N) * np.sum( x[k]*(W*x[k]- y_exact[k] +b) )
def cost_b(k):
    return (1/N) * np.sum( W*x[k] - y_exact[k] + b )

#Cost_vec
cost_vec=np.empty(MaxIter)
j=np.arange(0,MaxIter,1)
w_vec=np.empty(MaxIter)
b_vec=np.empty(MaxIter)

#Peform stochastic gradient descent
for i in range(0,MaxIter):
    #Pick random index
    k=random.randint(0, (N-1))
    w_vec[i]=W
    b_vec[i]=b
    #Forward pass
    F=W*x+b
    #Alter weights and biases
    W= W - eta * cost_W(k)
    b= b - eta * cost_b(k)
    #Calculate newcost
    newcost=cost()
    cost_vec[i]=newcost

    #print(newcost)

plt.plot(j,cost_vec)
plt.title(f'Cost Eta={eta} MaxIter={MaxIter}')
plt.xlabel('Iteration')
plt.ylabel('Cost for SGD')
plt.show()

for i in range(0,MaxIter):
    print(f'{w_vec[i]} & {b_vec[i]} & {cost_vec[i]}')


import pandas as pd
import numpy as np
import theano.tensor as tt
import pymc3 as pm
import seaborn as sns
import matplotlib.pyplot as plt

iris = pd.read_csv('../data/iris.csv')

#######################
# single variable logit
#######################
df = iris.query("species == ('setosa', 'versicolor')")

y_0 = pd.Categorical(df['species']).codes
x_n = 'sepal_length'
x_0 = df[x_n].values
x_c = x_0 - x_0.mean()

# logistic regression
with pm.Model() as model_0:
    alpha = pm.Normal('alpha', mu=0, sd=10)
    beta = pm.Normal('beta', mu=0, sd=10)

    mu = alpha + pm.math.dot(x_c, beta)
    theta = pm.Deterministic('theta', pm.math.sigmoid(mu))

    bd = pm.Deterministic('bd', -alpha/beta)

    yl = pm.Bernoulli('yl', p=theta, observed=y_0)

    trace_0 = pm.sample(1000)

varnames = ['alpha', 'beta', 'bd']
pm.summary(trace_0, varnames)
pm.plot_trace(trace_0, varnames)

#######################
# multi variable logit
#######################

df = iris.query("species == ('setosa', 'versicolor')")
y_1 = pd.Categorical(df['species']).codes
x_n = ['sepal_length', 'sepal_width']
# note: not centering this time
x_1 = df[x_n].values

with pm.Model() as model_1:
    alpha = pm.Normal('alpha', mu=0, sd=10)
    beta = pm.Normal('beta', mu=0, sd=2, shape=len(x_n))

    mu = alpha + pm.math.dot(x_1, beta)

    # this time manually did it intead of pm.math.sigmoid
    theta = pm.Deterministic('theta', 1 / (1 + pm.math.exp(-mu)))

    bd = pm.Deterministic('bd', -alpha/beta[1] - beta[0]/beta[1] * x_1[:,0])

    yl = pm.Bernoulli('yl', p=theta, observed=y_1)

    trace_1 = pm.sample(2000)

varnames = ['alpha', 'beta']
pm.plot_forest(trace_1, var_names=varnames);

pm.summary(trace_1, ['alpha', 'beta'])

############################################
# multi variable logit w/ unbalanced classes
############################################

df = iris.query("species == ('setosa', 'versicolor')")
df = df[45:]
y_3 = pd.Categorical(df['species']).codes
x_n = ['sepal_length', 'sepal_width']
x_3 = df[x_n].values

with pm.Model() as model_3:
    alpha = pm.Normal('alpha', mu=0, sd=10)
    beta = pm.Normal('beta', mu=0, sd=2, shape=len(x_n))

    mu = alpha + pm.math.dot(x_3, beta)
    theta = 1 / (1 + pm.math.exp(-mu))
    bd = pm.Deterministic('bd', -alpha/beta[1] - beta[0]/beta[1] * x_3[:,0])

    yl = pm.Bernoulli('yl', p=theta, observed=y_3)

    trace_3 = pm.sample(1000)

###########################################
# softmax (logit but with multiple classes)
###########################################

iris = sns.load_dataset('iris')
y_s = pd.Categorical(iris['species']).codes
x_n = iris.columns[:-1]
x_s = iris[x_n].values
x_s = (x_s - x_s.mean(axis=0)) / x_s.std(axis=0)

with pm.Model() as model_s:
    # note alpha shape: 3 potential outputs
    alpha = pm.Normal('alpha', mu=0, sd=5, shape=3)
    # note beta shape: 4 explanatory variables, 3 potential outputs
    beta = pm.Normal('beta', mu=0, sd=5, shape=(4,3))

    mu = pm.Deterministic('mu', alpha + pm.math.dot(x_s, beta))

    theta = tt.nnet.softmax(mu)

    yl = pm.Categorical('yl', p=theta, observed=y_s)
    trace_s = pm.sample(2000)

data_pred = trace_s['mu'].mean(0)

y_pred = [np.exp(point)/np.sum(np.exp(point), axis=0)
          for point in data_pred]

f'{np.sum(y_s == np.argmax(y_pred, axis=1)) / len(y_s):.2f}'

###############################
# softmax with fewer parameters
###############################

with pm.Model() as model_sf:
    alpha = pm.Normal('alpha', mu=0, sd=2, shape=2)
    beta = pm.Normal('beta', mu=0, sd=2, shape=(4,2))
    alpha_f = tt.concatenate([[0] ,alpha])
    beta_f = tt.concatenate([np.zeros((4,1)) , beta], axis=1)
    mu = alpha_f + pm.math.dot(x_s, beta_f)
    theta = tt.nnet.softmax(mu)
    yl = pm.Categorical('yl', p=theta, observed=y_s)
    trace_sf = pm.sample(1000)

#####
# LDA
#####

with pm.Model() as lda:
    mu = pm.Normal('mu', mu=0, sd=10, shape=2)
    sigma = pm.HalfNormal('sigma', 10)
    # assuming sepal length normally distributed
    # put mu and sigma parameters on it - note: same sigma
    setosa = pm.Normal('setosa', mu=mu[0], sd=sigma, observed=x_0[:50])
    versicolor = pm.Normal('versicolor', mu=mu[1], sd=sigma,
                           observed=x_0[50:])
    # identifying bd, average of two means
    bd = pm.Deterministic('bd', (mu[0] + mu[1]) / 2)
    trace_lda = pm.sample(1000)

#######################
# zero-inflated poisson
#######################

n = 100
theta_real = 2.5
psi = 0.1

# Simulate some data
counts = np.array([(np.random.random() > (1-psi)) *
                   np.random.poisson(theta_real) for i in range(n)])

with pm.Model() as ZIP:
    # psi is prob being modeled by poisson
    # 1 - psi is prob of extra zeros
    psi = pm.Beta('psi', 1, 1)
    theta = pm.Gamma('theta', 2, 0.1)
    y = pm.ZeroInflatedPoisson('y', psi, theta,
                               observed=counts)
    trace = pm.sample(1000)

#######################################
# Poisson regression and ZIP regression
#######################################

fish_data = pd.read_csv('../data/fish.csv')

with pm.Model() as ZIP_reg:
    psi = pm.Beta('psi', 1, 1)
    alpha = pm.Normal('alpha', 0, 10)
    beta = pm.Normal('beta', 0, 10, shape=2)
    theta = pm.math.exp(alpha + beta[0] * fish_data['child'] + beta[1] *
                        fish_data['camper'])
    yl = pm.ZeroInflatedPoisson('yl', psi, theta, observed=fish_data['count'])
    trace_ZIP_reg = pm.sample(1000)

pm.plot_trace(trace_ZIP_reg)
pm.summary(trace_ZIP_reg)

############################
# Robust logistic regression
############################

iris = sns.load_dataset("iris")
df = iris.query("species == ('setosa', 'versicolor')")
y_0 = pd.Categorical(df['species']).codes
x_n = 'sepal_length'
x_0 = df[x_n].values
y_0 = np.concatenate((y_0, np.ones(6, dtype=int)))
x_0 = np.concatenate((x_0, [4.2, 4.5, 4.0, 4.3, 4.2, 4.4]))
x_c = x_0 - x_0.mean()

with pm.Model() as model_rlg:
    alpha = pm.Normal('alpha', mu=0, sd=10)
    beta = pm.Normal('beta', mu=0, sd=10)

    mu = alpha + x_c *  beta
    theta = pm.Deterministic('theta', pm.math.sigmoid(mu))
    bd = pm.Deterministic('bd', -alpha/beta)

    pi = pm.Beta('pi', 1., 1.)

    # saying prob is random (0.5) with prob pi, or logistic with 1-pi
    # way to do a robust logit
    p = pi * 0.5 + (1 - pi) * theta

    yl = pm.Bernoulli('yl', p=p, observed=y_0)

    trace_rlg = pm.sample(2000)


import tensorflow as tf
from scipy.ndimage import distance_transform_edt as eucl_distance
import numpy as np

def GDL_loss(y_true,y_pred):
    """
    Generalized Dice Loss
    y_true \in \mathbb{R}^{BxHxWxC}
    y_pred \in \mathbb{R}^{BxHxWxC}
    """
    eps= tf.constant(1e-20)
    y,y_hat = y_true,y_pred
    wG = 1/(tf.einsum('bijk->b',y)**2 - eps)
    wB = 1/tf.einsum('bijk->b',1-y)**2
    numerator = wG*tf.einsum('bijk,bijk->b',y,y_hat) + wB* tf.einsum('bijk,bijk->b',1-y,1-y_hat)
    denominator = wG*tf.einsum('bijk->b',y+y_hat) + wB*tf.einsum('bijk->b',2-y-y_hat)

    return  tf.reduce_mean(1 - 2*(numerator+eps)/(denominator+eps))

def distmaps(y_true):
    k = tf.shape(y_true)[0]
    res = np.zeros_like(y_true,dtype=np.float32)
    for i in range(k):
        posmask = y_true[i,...] > 0.5
        negmask = ~ posmask
        res[i,...] = tf.cast(eucl_distance(negmask),dtype=tf.float32)* tf.cast(negmask,tf.float32) -tf.cast(eucl_distance(posmask),dtype=tf.float32)*tf.cast(posmask,tf.float32)
    return res

def boundary_loss(y_true,y_pred):
    """
    Boundary loss
    y_true \in \mathbb{R}^{BxHxW}
    y_pred \in \mathbb{R}^{BxHxW}
    """
    dist_map = tf.py_function(func=distmaps, inp=[y_true], Tout=tf.float32) # tensor [batch,h,w]
    value = tf.einsum('ijlk,ijlk->i',dist_map,y_pred) #- tf.einsum('ijk,ijk->i',dist_map,y_true)  # tensor[batch]
    return tf.reduce_mean(value)#/tf.reduce_sum(tf.abs(dist_map))

def Dice_metric(y_true,y_pred):
    B = tf.shape(y_true)[0]
    res = 0.0
    for b in range(B):
        if tf.reduce_sum(y_true[b]) == 0:
            continue
        y, y_hat = y_true[b] , y_pred[b]
        intersection = tf.einsum('ijk,ijk->',y,y_hat) #haddamar -> sum j and k for each image
        union = tf.einsum('ijk->',y**2) + tf.einsum('ijk->',y_hat**2) #sum  j and k
        res +=  2*intersection/union # dice per image
    return res/tf.cast(B,tf.float32)

def Dice_loss(y_true,y_pred):
    nue = 2*tf.reduce_sum(y_true*y_pred)
    den = tf.reduce_sum(y_true**2) + tf.reduce_sum(y_pred**2)
    return 1-nue/den


def norm_eqn_fit(x,y):
     import numpy as np
     import matplotlib.pyplot as plt
     N=y.size
     D=len(x.shape)
     #SIMPLE LINEAR REGRESSION
     if D==1:
         dummy=np.array([[1]*N]).T
         X = np.column_stack((dummy,x))
         w=np.linalg.solve(X.T.dot(X),X.T.dot(y))
         yhat=X.dot(w)
         R2=1-np.sum((y-yhat)**2)/np.sum((y-y.mean())**2)
         fig=plt.figure()
         plt.scatter(x,y,color = "green")
         plt.plot(x,yhat)
         weights="Weights are: "
         Rsquared="Rsquared is: "
         result=( [weights, w],[Rsquared, R2],fig)

         return result

     #MULTIPLE LINEAR REGRESSION
     else:
         dummy=np.array([[1]*N]).T
         X = np.column_stack((dummy,x))
         w=np.linalg.solve(X.T.dot(X),X.T.dot(y))
         yhat=X.dot(w)
         R2=1-np.sum((y-yhat)**2)/np.sum((y-y.mean())**2)
         weights="Weights are: "
         Rsquared="Rsquared is: "
         result=( [weights, w],[Rsquared, R2])

         return result

def grad_descnt_fit(x,y,eta=0.01,epoch=2000,lmda_l2=0,lmda_l1=0):

    import numpy as np
    import matplotlib.pyplot as plt
    N=y.size
    D=x.shape[1]
    #SIMPLE LINEAR REGRESSION
    if D==1:
         dummy = np.array([[1]*N]).T
         X  = np.column_stack((dummy,x))
         #w = np.random.randn(D+1)
         w = np.array([np.random.randn(D+1)]).T
         J = []
         for t in range(epoch):
              yhat = X.dot(w)

              J.append(np.dot((y - yhat).T,y - yhat))
              w-=eta * X.T.dot(yhat-y)
         yhat = X.dot(w)

         R2=1-np.sum((y-yhat)**2)/np.sum((y-y.mean())**2)
         fig=plt.figure()
         plt.scatter(x,y,color = "green")
         plt.plot(x,yhat)
         weights="Weights are: "
         Rsquared="Rsquared is: "
         result=( [weights, w],[Rsquared, R2],fig)

         return result

    else:
        #MULTIPLE LINEAR REGRESSION
          dummy = np.array([[1]*N]).T
          X  = np.column_stack((dummy,x))
          w = np.random.randn(D+1)
          J = []
          for t in range(epoch):
               yhat = X.dot(w)
               J.append(np.dot((y - yhat).T,y - yhat))
               w-=eta * X.T.dot(yhat-y)
          yhat = X.dot(w)
          R2=1-np.sum((y-yhat)**2)/np.sum((y-y.mean())**2)
          weights="Weights are: "
          Rsquared="Rsquared is: "
          fig=plt.figure()
          plt.plot(J)
          result=( [weights, w],[Rsquared, R2],fig)

          return result

def predict(fit,x,y):
    import numpy as np
    import matplotlib.pyplot as plt
    #SIMPLE LINEAR PREDICTION
    D=len(fit[0][1])
    if D==2:
        yhat=fit[0][1][0]+x*fit[0][1][1]
        R2=1-np.sum((y-yhat)**2)/np.sum((y-y.mean())**2)
        fig=plt.figure()
        plt.scatter(x,y,color = "green")
        plt.plot(x,yhat)
        Rsquared="Rsquared is: "
        pred="Predictions are: "
        act="Actual Values are: "

        result=([Rsquared, R2],[act,y],[pred,yhat],fig)
        return result
    #MULTIPLE LINEAR PREDICTION
    else:
        N=len(y)
        w=fit[0][1].T
        dummy = np.array([[1]*N]).T
        X  = np.column_stack((dummy,x))
        yhat=X.dot(w)
        R2=1-np.sum((y-yhat)**2)/np.sum((y-y.mean())**2)
        Rsquared="Rsquared is: "
        relerror =np.abs((y - yhat) / y)
        mre=np.median(relerror)
        MRE= "Median Relative Error: "
        act="Actual Values are: "
        pred="Predictions are: "
        result=( [MRE, mre],[Rsquared, R2],[act,y],[pred,yhat])

        return result


import numpy as np
import tensorflow as tf

# 출석일수, 공부한시간
x_data = np.array([[1., 3.],
                   [2., 2.],
                   [3., 1.],
                   [4., 6.],
                   [5., 5.],
                   [6., 4.]])

y = np.array([[0], [0], [0], [1], [1], [1]])

print(x_data.shape)
print(y.shape)

X = tf.placeholder(tf.float32)
W = tf.Variable(tf.random_uniform([2, 1]))
b = tf.Variable(tf.random_uniform([1]))

z = tf.matmul(X, W) + b
hx = tf.sigmoid(z)

cost_i = y * (-tf.log(hx)) + (1 - y) * (-tf.log(1 - hx))
cost = tf.reduce_mean(cost_i)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
train = optimizer.minimize(cost)

sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)

for i in range(1000):
    _t, _c = sess.run([train, cost], feed_dict={X: x_data})
    if not i % 100:
        print(i, _c)

print(sess.run(W))
print(sess.run(b))

print(sess.run(z, feed_dict={X: np.array([[3., 5.]])}))
print(sess.run(hx, feed_dict={X: np.array([[3., 5.], [1., 1.]])}))

result = sess.run(hx, feed_dict={X: np.array([[3., 5.], [1., 1.]])})

# True -> 1 False -> 0
result1 = tf.cast(result > 0.5, dtype=np.int32)
print(sess.run(result1))


"""ARCH(1) model:

r_t = log(p_t) - log(p_(t-1))
r_t = s_t * e_t
s_t^2 = w + a * r_t^2
"""
from pprint import pprint
import numpy as np
from scipy.optimize import lsq_linear
import matplotlib.pyplot as plt
from time_series_model_template import TimeSeriesModel

class Arch1(TimeSeriesModel):
    def __init__(self):
        self.omega = None
        self.alpha = None

    def fit_parameters(self, dt, x):
        # least square method
        # E[r_t^2] = w + a * r_(t-1)^2
        r2 = x ** 2
        ones = np.ones_like(r2[1:])
        opt_result = lsq_linear(np.column_stack((ones, r2[:-1])), r2[1:])
        if opt_result.success:
            self.omega, self.alpha = opt_result.x
            pprint(self.parameters)
        else:
            pprint(opt_result)

    def simulate(self, x0, t):
        x = np.zeros_like(t)
        x[0] = x0
        w, a = self.omega, self.alpha
        for i in range(1, len(t)):
            s = np.sqrt(w + a * x[i-1] ** 2)
            x[i] = np.random.randn() * s
        return x

def main():
    np.random.seed(0)
    a_model = Arch1()
    a_model.omega = 1e-4
    a_model.alpha = 0.3
    dt = 1
    t = np.arange(0, 5000., dt)
    x = a_model.simulate(0, t)
    a_model.fit_parameters(dt, x)
    y = a_model.simulate(0, t)
    plt.plot(t, x.cumsum())
    plt.plot(t, y.cumsum())
    plt.show()

if __name__ == "__main__":
    main()


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

import itertools
import numpy as np

def entropy(*X):
    """
    Calculate the entropy of a variable, or joint entropy of several variables.

    Parameters
    ----------
    X : array, or list of arrays
        Variable or variables to compute entropy/joint entropy on

    Notes
    -----
    This function can be used to calculate the entropy of a single variable
    (provided as a single input) or to calculate the joint entropy between two
    variables (provided as a series of inputs)
    """
    n_instances = len(X[0])
    H = 0
    for classes in itertools.product(*[set(x) for x in X]):
        v = np.array([True] * n_instances)
        for predictions, c in zip(X, classes):
            v = np.logical_and(v, predictions == c)
        p = np.mean(v)
        H += -p * np.log2(p) if p > 0 else 0
    return H

def conditional_entropy(x, y):
    """
    The conditional entropy H(X|Y) = H(Y,X) - H(Y). X conditioned on Y
    """
    H_y = entropy(y)
    H_yx = entropy(y, x)
    return H_yx - H_y

def mutual_information(x, y):
    """
    The mutual information between two variables

    MI(X, Y) = H(X) + H(Y) - H(X | Y)

    Parameters
    ----------
    x, y : array

    Returns
    -------
    array : mutual information between x and y
    """
    H_x = entropy(x)
    H_y = entropy(y)
    H_xy = entropy(x, y)
    return H_x + H_y - H_xy

def entropy_cc(x, y):
    """
    The entropy correlation coefficient:

    p(H) = sqrt(MI(X, Y) / 0.5 * (H(X) + H(Y)))
    """
    H_x = entropy(x)
    H_y = entropy(y)
    I_xy = mutual_information(y, x)
    return np.sqrt(I_xy / (0.5 * (H_x + H_y)))

def transfer_entropy(x, y, lag=1):
    """
    Transfer entropy for two given signals.

    Parameters
    ----------
      x : array
        source
      y : array
        target
      lag : int

    Returns
    -------
    array : Transfer entropy from x to y
    """
    # Future of i
    Fi = np.roll(x, -lag)
    # Past of i
    Pi = x
    # Past of j
    Pj = y

    # Transfer entropy
    Inf_from_Pi_to_Fi = conditional_entropy(Fi, Pi)

    # Same as cond_entropy(Fi, Pi_Pj)
    H_y = entropy(Pi, Pj)
    H_yx = entropy(Fi, Pj, Pi)
    Inf_from_Pi_Pj_to_Fi = H_yx - H_y

    TE_from_j_to_i = Inf_from_Pi_to_Fi - Inf_from_Pi_Pj_to_Fi

    return TE_from_j_to_i


import math
import numpy as np

#converts matrix entries to mod26
vector_mod26 = np.vectorize(lambda n: round(n % 26))

#change to whatever, as long as it's mod26
A = np.array([[5, 2],
              [3, 1]])
A_inv = vector_mod26(np.linalg.inv(A))
b = np.array([[6],
              [7]])

#encoding function, could be modified for more complexity
def enc(p):
    return A @ p + b

#decoding, directly related to encoding, so need a new decoding for a new encoding function
def dec(p):
    return A_inv @ p - A_inv @ b

#lettertonumber
def ltn(c):
    return ord(c)-65

#numbertoletter
def ntl(n):
    return chr((n % 26)+65)

#for polyalphabetic encoding
def messageToVectors(msg):
    vec_list = []
    while len(msg) > 1 :
        vec_list.append(np.array([[ltn(msg[0])],
                                  [ltn(msg[1])]]))
        msg = msg[2:]
    if len(msg) == 1:
        vec_list.append(np.array([[ltn(msg[0])],
                                  [0]]))
    return vec_list

#applies encoding to vector list
def appEnc(vl):
    return list(map(enc, vl))

#applies decoding to vector list
def appDec(vl):
    return list(map(dec, vl))

#in the name
def vectorsToMessage(vl):
    f = lambda v : v.tolist()
    lol = map(f, vl)
    nums = []
    for sublist in lol:
        for item in sublist:
            for realitem in item:
                nums.append(realitem)
    chars = map(ntl, nums)
    return ''.join(chars)

#actual encryption function, which takes in message
def encrypt(msg):
    print(vectorsToMessage(appEnc(messageToVectors(msg))))

#actual decryption function
def decrypt(msg):
    print(vectorsToMessage(appDec(messageToVectors(msg))))

#tests
print(A_inv)
encrypt("LISTENINGTOGOODKIDMAADCITYFORTHEFIRSTTIMERIGHTNOWITSPRETTYGOOD")


# -*- coding: utf-8 -*-
"""
interest.py

You have $1000 on day 11, and every day you earn 10\%10% of what you already get,
so that on day 22 you have $1000+10%⋅$1000=$1100, and on day 33 you have $1100+10%⋅$1100=$1210.
 On which day you will have more than $1000000 for the first time?

Created on Thu Apr 16 17:42:39 2020

@author: Tim
"""

# %matplotlib inline

import matplotlib.pyplot as plt
import numpy as np

# You can play with the range of values for n and with the base 1.02, see what changes
sample = 200
x = np.arange(sample)
# This is used to plot the graph of 1.02^n, it is blue on the picture below
y = 1.02 ** x
plt.plot(x, y)
plt.xlabel('n')
plt.ylabel('Money($)')
# This is used to plot the graph of 1 + 0.02 * n, it is green on the picture below
z = 1 + 0.02 * x
plt.plot(x, z)
plt.show()

# Computes how much money you will have on day *day*, if you start with *starting_amount* of cash
# and earn *earn_percent* percents of what you already have every day.
def HowMuchMoney(starting_amount, earn_percent, day):
    day_multiplier = 1 + (earn_percent / 100.0)
    return starting_amount * (day_multiplier ** (day - 1))

def PrintExample(starting_amount, earn_percent, day):
    print("If you start with $%d and earn %d%% each day, you will have more than $%.0f on day %d!" %
          (starting_amount, earn_percent, HowMuchMoney(starting_amount, earn_percent, day), day))

# Prints what will happen by day 74 if you start with $1000 and earn 10% every day
# Feel free to play with the parameters

# PrintExample(1000, 10, 74)


import numpy as np
from numpy import fft

# Create a Gaussian kernel of size kernlen and standard deviation sigma
def gaussian_kernel(kernlen, sigma):
    x = np.linspace(-(kernlen // 2), kernlen // 2, kernlen)
    # Unidimensional Gaussian kernel
    kern1d = np.exp(-0.5 * (x ** 2 / sigma))
    # Bidimensional Gaussian kernel
    kern2d = np.outer(kern1d, kern1d)
    # Normalization
    return kern2d / kern2d.sum()

# Compute the FFT of the kernel 'K' of size 'd' padding with the zeros necessary
# to match the size of 'shape'
def psf_fft(K, d, shape):
    # Zero padding
    K_p = np.zeros(shape)
    K_p[:d, :d] = K

    # Shift
    p = d // 2
    K_pr = np.roll(np.roll(K_p, -p, 0), -p, 1)

    # Compute FFT
    K_otf = fft.fft2(K_pr)
    return K_otf

# Multiplication by A
def A(x, K):
    x = fft.fft2(x)
    return np.real(fft.ifft2(K * x))

# Multiplication by A transpose
def AT(x, K):
    x = fft.fft2(x)
    return np.real(fft.ifft2(np.conj(K) * x))

eps = 1e-2

# Variazione totale
def totvar(x):
    # Calcola il gradiente di x
    dx, dy = np.gradient(x)
    n2 = np.square(dx) + np.square(dy)

    # Calcola la variazione totale di x
    tv = np.sqrt(n2 + eps ** 2).sum()
    return tv

# Gradiente della variazione totale
def grad_totvar(x):
    # Calcola il numeratore della frazione
    dx, dy = np.gradient(x)

    # Calcola il denominatore della frazione
    n2 = np.square(dx) + np.square(dy)
    den = np.sqrt(n2 + eps ** 2)

    # Calcola le due componenti di F dividendo il gradiente per il denominatore
    Fx = dx / den
    Fy = dy / den

    # Calcola la derivata orizzontale di Fx
    dFdx = np.gradient(Fx, axis=0)

    # Calcola la derivata verticale di Fy
    dFdy = np.gradient(Fy, axis=1)

    # Calcola la divergenza
    div = dFdx + dFdy

    # Restituisci il valore del gradiente della variazione totale
    return -div

def next_step(x, f, df):  # backtracking procedure for the choice of the steplength
    alpha = 1.1
    rho = 0.5
    c1 = 0.25
    p = -df
    j = 0
    jmax = 10
    # Armijo rule and max iterations
    while (f(x + alpha * p) > f(x) + c1 * alpha * df.T @ p) and j < jmax:
        alpha = rho * alpha
        j += 1
    # Next step not found , convergence not reached
    if j > jmax:
        return -1
    else:
        # print('alpha=',alpha)
        return alpha


import numpy as np
import pandas as pd
from scipy.sparse import csr_matrix

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.colors import TABLEAU_COLORS

def classic_walk(n, go_left_prob, circle=True):
    """
    create transition matrix and initial state for classic random walk
    args:
    n: number of points on line, use even number to get nice animations, midpoint is adapted to be even point
    this is done in light of frame_option='even' in animation methods, see below
    circle: if True will glue end points together to obatin circle graph
    """
    go_right_prob = 1 - go_left_prob
    # prepare transition matrix
    T = np.diag(go_left_prob*np.ones(n-1),k=-1) + np.diag(go_right_prob*np.ones(n-1),k=1)
    # for circle, glue opposite border nodes by adding an edge between
    if circle:
        T = T + np.diag(go_left_prob*np.ones(1),k=(n-1)) + np.diag(go_right_prob*np.ones(1),k=-(n-1))
    #create start state with all probability in one mid point
    state_0 = np.zeros(n)
    state_0[n//2 - (n//2)%2] = 1
    return T, state_0

def anim_classic_walk(transition, projection, start_pd, n_steps=10, show_equil=False, frame_option='pairsmean'):

    """
    calculate the evolution of random walk and prepare dataframe df of the evolutionvof probability distribution
    the probability distribution at start is given as input start_pd. i
    in each loop through the n_steps frames for the animation, the transition matrix in applied

    projection: matrix would allow projection of whole graph for animation, not really useful for circle
    show_equil: equilibrium is the uniform distribution, which can be plotted so that conversion can be seen
    frame_option: three options of animation frames are possible:
    plain: notrecommended, just plots in each frame the actual probability distribution of that time step of the walk
           this gives a jumpy animation, as in every second step any fixed node has probability zero,
    therefore two variants are recommended:
    pairsmean: show in each animation frame the average of two consecutive evolution steps of probability distribtions
                (except first step, to show true start state)
    even: like a zoom out, it shows only even numbered nodes and only even time steps,
          this gives the smoothest animation, but might be losing some detail
          Note: this is only recommended for even number of nodes, otherwise even nodes become uneven nodes
          as they pass the glued border and parts of dynamic are then invisible in the animation
    """

    T = csr_matrix(transition)
    n = T.shape[0]

    if projection is None:
        P = np.diag(np.ones(n))
    else:
        P = projection
    P = csr_matrix(P)
    n_proj = P.shape[0]

    if frame_option in ['pairsmean', 'plain']:
        n_frames = n_steps-1
        pos_select_stepsize = 1
    elif  frame_option=='even':
        if (n%2)!=0:
            print("NOTE: animation with frame_option='even' and uneven number "
                  +"of nodes not recommended (part of dynamic will be hidden)")
        n_frames = n_steps//2
        pos_select_stepsize = 2
        T = T*T

    # create anim df
    T0 = csr_matrix(np.diag(np.ones(n)))
    T1 = T0
    equi_y = P.dot((1/n)*np.ones(n))*pos_select_stepsize

    df = pd.DataFrame({'x': [], 'y': []})
    for i in range(n_frames):
        T1 = T0*T
        if frame_option=='pairsmean':
            x = np.arange(n_proj)
            if i == 0:
                y = P*(T0*start_pd)
            else:
                y = 0.5*P*(T0*start_pd + T1*start_pd)
        elif  frame_option=='even':
            x = np.arange(0,n_proj+1,2)[:n_proj//2+n_proj%2]
            y = (P*(T0*start_pd))[::pos_select_stepsize]
        elif  frame_option=="plain":
            x = np.arange(n_proj)
            y = P*(T0*start_pd)
        T0 = T1

        df = df.append(pd.DataFrame({'x': x, 'y': y, 'frame': i, 'info': 'evol'}))
        if show_equil:
            df = df.append(pd.DataFrame({'x': x, 'y': equi_y[::pos_select_stepsize], 'frame': i, 'info': 'equi'}))

    return df

### adjusted function for marked state with extra coin
def run_state_evol(graph:dict, coin_mat:np.array, start_state:np.array , n_steps:int, probsum_tol=1e-5,
                  marked_state=None,mrk_state_coin=None, verbose=False):
    """
    run number of steps of quantum random walk on given graph with edge labelling,
    which related edges to dimensions in the coin space

    INPUT: graph = {'edges': (n,2) array , 'fw_edge_labels': (n,) array ,'bw_edge_labels': (n,) array}
    each edge has a label for forward and one for backward direction, the edge array defines what is fw
    edge labels refer to dimensions of the coin space and determine which coinspace
    coefficients are passed along the edge to neighbor node
    coin_mat= (d,d) array, the coin space operator applied before each step on the coin space at each node
    start_state= (n,d) array defines the start state of the coin space - graph - tensor product
    a marked state can be passed with separate coin
    OUT: stacked states for each time step
    """
    n,d = start_state.shape
    # get stacked transition operators on all dimensions of coin space
    A = np.zeros((n,n,d))
    A[graph['edges'][:,0],graph['edges'][:,1],graph['fw_edge_labels']] = 1
    A[graph['edges'][:,1],graph['edges'][:,0],graph['bw_edge_labels']] = 1
    # initialise all zero state evolution and set start state at index time 0
    state_evol = np.zeros((n,d,n_steps)).astype(np.complex64)
    state_evol[:,:,0] = start_state
    # run through all steps, update coin space and then apply transition operator
    if verbose: print('-'*(n_steps-1))
    for i in range(1,n_steps): # .. einstein sum is used for stacked matrix multiplications
        transf_state = coin_mat.dot(state_evol[:,:,i-1].T)
        if marked_state is not None:
            transf_state[:,marked_state] = mrk_state_coin.dot(state_evol[marked_state,:,i-1].T)
        state_evol[:,:,i] = np.einsum('ijk,kj->ik', A, transf_state)
        if verbose: print('-',end='')
    # check for consistent prob distribution
    assert(np.abs((1 - (np.linalg.norm(state_evol,ord=2,axis=1)**2).sum(axis=0))).max()<probsum_tol)
    if verbose: print("\nerror in amplitude sum: ",np.abs((1 - (np.linalg.norm(state_evol,ord=2,axis=1)**2).sum(axis=0))).max())
    return state_evol

def quantum_walk_circle(n):
    d = 2
    # this defines a circle graph with 2 dimensional direction space, one for each direction on the circle
    graph = {
        'edges': np.column_stack((np.arange(0,n),np.arange(1,n+1)%n)).astype(int),
        'fw_edge_labels': 0 * np.ones(n).astype(int) ,
        'bw_edge_labels': 1 * np.ones(n).astype(int)
    }
    # define the state at time 0
    start_state = np.zeros((n,d)).astype(np.complex64)
    start_state[n//2,:] = np.array([1,1])*(1/np.linalg.norm(np.array([1,1])))
    return graph, start_state

def anim_quantum_walk(probs_evol, projection, show_equil=False,show_cesaromean=True,
                       scale_projection=False, frame_option='pairsmean'):
    """
    animation for quantum random walk
    similar to anim_classic_walk above, only difference is that the probability evolution not calculated but an input
    the cesaro mean (which is just the mean of vurrent evolution at every step) is additionally calculated as y_avg
    """
    n = probs_evol.shape[0]

    if projection is None:
        P = np.diag(np.ones(n))
    elif scale_projection:
        P = projection
        P = (np.diag(1/(P.sum(axis=1)))*P)
    else:
        P = projection
    probs_evol = P.dot(probs_evol)
    n_proj = P.shape[0]

    equil_state = P.dot((1/P.shape[1])*np.ones(P.shape[1]))

    if frame_option in ['pairsmean', 'plain']:
        n_frames = probs_evol.shape[1]-1
        pos_select_stepsize = 1
    elif  frame_option=='even':
        if (n%2)!=0:
            print("NOTE: animation with frame_option='even' and uneven number "
                  +"of nodes is not recommended (part of dynamic will be hidden)")
        n_frames = probs_evol.shape[1]//2
        probs_evol = probs_evol[::2,:]
        equil_state = equil_state[::2]

    df = pd.DataFrame({'x': [], 'y': []})
    for i in range(n_frames):
        if frame_option=='pairsmean':
            x = np.arange(n_proj)
            y = 0.5*(probs_evol[:,max(i-1,0)] + probs_evol[:,i])
            y_avg = probs_evol[:,:max(1,i+1)].mean(axis=1)
        elif  frame_option=='even':
            x = np.arange(0,n_proj+1,2)[:n_proj//2+n_proj%2]
            y = probs_evol[:,2*i]
            y_avg = probs_evol[:,:max(2,2*i+1)].mean(axis=1)
        elif  frame_option=="plain":
            x = np.arange(n_proj)
            y = probs_evol[:,i]
            y_avg = y_avg = probs_evol[:,:max(2,i+1)].mean(axis=1)

        df = df.append(pd.DataFrame({'x': x, 'y': y, 'frame': i, 'info': 'Probability'}))
        if show_cesaromean:
            df = df.append(pd.DataFrame({'x': x, 'y': y_avg,
                                         'frame': i, 'info': 'Cesaro mean'}))
        if show_equil:
            df = df.append(pd.DataFrame({'x': x, 'y': equil_state,
                                         'frame': i, 'info': 'Equilibrium'}))

    return df


import math

import torch
import torch.nn as nn

class LocallyConnected(nn.Module):
    """Local linear layer, i.e. Conv1dLocal() with filter size 1.

    Args:
        num_linear: num of local linear layers, i.e.
        in_features: m1
        out_features: m2
        bias: whether to include bias or not

    Shape:
        - Input: [n, d, m1]
        - Output: [n, d, m2]

    Attributes:
        weight: [d, m1, m2]
        bias: [d, m2]
    """

    def __init__(self, num_linear, input_features, output_features, bias=True):
        super(LocallyConnected, self).__init__()
        self.num_linear = num_linear
        self.input_features = input_features
        self.output_features = output_features

        self.weight = nn.Parameter(
            torch.Tensor(num_linear, input_features, output_features)
        )
        if bias:
            self.bias = nn.Parameter(torch.Tensor(num_linear, output_features))
        else:
            # You should always register all possible parameters, but the
            # optional ones can be None if you want.
            self.register_parameter("bias", None)

        self.reset_parameters()

    @torch.no_grad()
    def reset_parameters(self):
        k = 1.0 / self.input_features
        bound = math.sqrt(k)
        nn.init.uniform_(self.weight, -bound, bound)
        if self.bias is not None:
            nn.init.uniform_(self.bias, -bound, bound)

    def forward(self, input: torch.Tensor):
        # [n, d, 1, m2] = [n, d, 1, m1] @ [1, d, m1, m2]
        out = torch.matmul(input.unsqueeze(dim=2), self.weight.unsqueeze(dim=0))
        out = out.squeeze(dim=2)
        if self.bias is not None:
            # [n, d, m2] += [d, m2]
            out += self.bias
        return out

    def extra_repr(self):
        # (Optional)Set the extra information about this module. You can test
        # it by printing an object of this class.
        return "num_linear={}, in_features={}, out_features={}, bias={}".format(
            self.num_linear, self.in_features, self.out_features, self.bias is not None
        )

def main():
    n, d, m1, m2 = 2, 3, 5, 7

    # numpy
    import numpy as np

    input_numpy = np.random.randn(n, d, m1)
    weight = np.random.randn(d, m1, m2)
    output_numpy = np.zeros([n, d, m2])
    for j in range(d):
        # [n, m2] = [n, m1] @ [m1, m2]
        output_numpy[:, j, :] = input_numpy[:, j, :] @ weight[j, :, :]

    # torch
    torch.set_default_dtype(torch.double)
    input_torch = torch.from_numpy(input_numpy)
    locally_connected = LocallyConnected(d, m1, m2, bias=False)
    locally_connected.weight.data[:] = torch.from_numpy(weight)
    output_torch = locally_connected(input_torch)

    # compare
    print(torch.allclose(output_torch, torch.from_numpy(output_numpy)))

if __name__ == "__main__":
    main()


#%% Load libraries
from pandas.plotting import scatter_matrix
import scipy.stats as stats
import statsmodels.formula.api as smf
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.style
import matplotlib.pyplot as plt
%matplotlib inline
mpl.style.use('classic')

#%% Import dataset

#%% Association between random variables
housing = pd.DataFrame.from_csv('data/housing.csv')

display(housing.shape)

display(housing.columns.values)
# columns: array(['LSTAT', 'INDUS', 'NOX', 'RM', 'MEDV'], dtype=object)
# LSTAT - percentage of the population classified as low status
# INDUS - proportion of non-retail business across the town
# NOX - NO2 concentration
# RM - average number of rooms per dwelling
# MEDV - median value of the owner-occupied homes, in $1000s

display(housing.describe())
display(housing.head())

# Check out covariance
# Strongest association - ? todo to understand that: coefficient of correlation
display(housing.cov())

# Check out correlation
display(housing.corr())

#%% Visualize the association between two variables
# Scatter matrix is a matrix of scatter plots for each pair of random variabless.
# The histograms in diagonal positions are those of variables.
housing_scatter_matrix = pd.plotting.scatter_matrix(housing, figsize=(10, 10))
plt.show()

#%% Observe the association more closely
housing.plot(
    kind='scatter',
    x='LSTAT', y='MEDV',
    figsize=(5, 5)
)
plt.show()

housing.plot(
    kind='scatter',
    x='RM', y='MEDV',
    figsize=(5, 5)
)
plt.show()

#%% Simple linear regression model

# y_i = betha_0 + betha_1 * x_i + eps_i

# Try to estimate relations between RM (OX) and MEDV (OY)

# Step 1. estimate values of betha_0 (intercept) and betha_1 (slope)

# Guess of b0 and b1
b0 = 0.1
b1 = 1
housing['guess_response'] = b0 + b1 * housing['RM']
# Calculate error of the guess; this shows how far is our guess response from the true response
housing['guess_error'] = housing['MEDV'] - housing['guess_response']
# Plot estimated line together with real points' values
plt.figure(figsize=(8, 8))
plt.scatter(
    x=housing['RM'], y=housing['MEDV'],
    color='g', label='Observed values'
)
plt.plot(
    housing['RM'], housing['guess_response'],
    color='red', label='guessed response (b0=0.1, b1=1)'
)
plt.legend()
plt.show()
# Calculate SSE
sse_value = (housing['guess_error']**2).sum()
display('Sum of squared error is {0}'.format(sse_value))

#%% Least square estimates
formula = 'MEDV~RM'
model = smf.ols(formula=formula, data=housing).fit()

display(model.params)

b0_ols = model.params[0]
b1_ols = model.params[1]

housing['best_response'] = b0_ols + b1_ols * housing['RM']

housing['best_error'] = housing['MEDV'] - housing['best_response']

# plot estimated line
plt.figure(figsize=(8, 8))
plt.scatter(
    x=housing['RM'], y=housing['MEDV'],
    color='g', label='Observed values'
)
plt.plot(
    housing['RM'], housing['best_response'],
    color='red', label='best response (b0=0.1, b1=1)'
)
plt.legend()
plt.show()

# Calculate SSEs
guess_sse_value = (housing['guess_error']**2).sum()
best_sse_value = (housing['best_error']**2).sum()

display('Sums of squared error are {0}'.format(
    [guess_sse_value, best_sse_value]))
# 'Sums of squared error are [170373.528047, 22061.8791962118]'  # much lower in the second case!!!

#%% Print model summary
model.summary()
# R-squared:	0.484
# F-statistic:	471.8

#%% Diagnostic of linear regression model

# Assumptions of Linear Regression Model
# linearity
# independence
# normality
# equal variance

#%% linearity
# in our case, scatter plot between MEDV and RM has linear pattern - OK

#%% Independence
# Demonstrate that observed error is independent mutually
housing['error'] = housing['MEDV'] - housing['best_response']
plt.figure(figsize=(10, 5))
plt.plot(housing.index, housing['error'], color='red')
plt.axhline(y=0, color='red')
plt.show()  # there is no obvious pattern in the plot

# Durbin Watson test for serial correlation
# Durbin-Watson:	0.684  -- bad score

#%% Normality
# use quantile-quantile plot (QQ plot)
z = (housing['error'] - housing['error'].mean()) / housing['error'].std(ddof=1)
stats.probplot(z, dist='norm', plot=plt)
plt.title('Normal q-q plot')
plt.show()

#%% Equal variance
housing.plot(
    kind='scatter',
    x='RM', y='error',
    figsize=(10, 5), color='green'
)
plt.title('Residuals vs predictor')
plt.axhline(y=0, color='red')
plt.show()

housing.plot(
    kind='scatter',
    x='LSTAT', y='error',
    figsize=(10, 5), color='green'
)
plt.title('Residuals vs predictor')
plt.axhline(y=0, color='red')
plt.show()

# We can see that the regression model (MEDV~LSTAT) violates all four assumptions. Therefore, we cannot make statistical inference using this model
# However, the model can still be applied to make a prediction. The accuracy and the consistency of your model, do not rely on these four assumptions.

#%% Multiple linear regression model
# mimic the process of building trading model of SPY, using historical data of DIFFERENT stock markets.
aord = pd.DataFrame.from_csv('data/ALLOrdinary.csv')
nikkei = pd.DataFrame.from_csv('data/Nikkei225.csv')
hsi = pd.DataFrame.from_csv('data/HSI.csv')
daxi = pd.DataFrame.from_csv('data/DAXI.csv')
cac40 = pd.DataFrame.from_csv('data/CAC40.csv')
sp500 = pd.DataFrame.from_csv('data/SP500.csv')
dji = pd.DataFrame.from_csv('data/DJI.csv')
nasdaq = pd.DataFrame.from_csv('data/nasdaq_composite.csv')
spy = pd.DataFrame.from_csv('data/SPY.csv')

display(nasdaq.head())
# ['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume']
display(nasdaq.columns.values)

#%% use timezones

indicepanel = pd.DataFrame(index=spy.index)

indicepanel['spy'] = spy['Open'].shift(-1)-spy['Open']
indicepanel['spy_lag1'] = indicepanel['spy'].shift(1)
indicepanel['sp500'] = sp500["Open"]-sp500['Open'].shift(1)
indicepanel['nasdaq'] = nasdaq['Open']-nasdaq['Open'].shift(1)
indicepanel['dji'] = dji['Open']-dji['Open'].shift(1)

indicepanel['cac40'] = cac40['Open']-cac40['Open'].shift(1)
indicepanel['daxi'] = daxi['Open']-daxi['Open'].shift(1)

indicepanel['aord'] = aord['Close']-aord['Open']
indicepanel['hsi'] = hsi['Close']-hsi['Open']
indicepanel['nikkei'] = nikkei['Close']-nikkei['Open']
indicepanel['Price'] = spy['Open']

#%% fix NaN values
display(indicepanel.isnull().sum())

# fill NaNs with forward values

indicepanel = indicepanel.fillna(method='ffill')
indicepanel = indicepanel.dropna()

# check for NaNs again
display(indicepanel.isnull().sum())

# save cleaned data
path_save = 'indicepanel.csv'
indicepanel.to_csv(path_save)

display(indicepanel.shape)

#%% Split dataset & explore train and test sets of data
# because this is financial data (== high noise level), divide into equal parts
Train = indicepanel.iloc[-2000:-1000, :]
Test = indicepanel.iloc[-1000:, :]

print(Train.shape, Test.shape)

# sm = scatter_matrix(Train, figsize=(10, 10))
# plt.show()

#%% Check out correlation of each index between SPY
corr_array = Train.iloc[:, :-1].corr()['spy']
print(corr_array)

formula = 'spy~spy_lag1+sp500+nasdaq+dji+cac40+aord+daxi+nikkei+hsi'
lm = smf.ols(formula=formula, data=Train).fit()
lm.summary()

#%% Make prediction
Train['PredictedY'] = lm.predict(Train)
Test['PredictedY'] = lm.predict(Test)

plt.scatter(Train['spy'], Train['PredictedY'])
plt.show()

#%% Evaluate the model

# RMSE - Root Mean Squared Error, Adjusted R^2

def adjustedMetric(data, model, model_k, yname):
    data['yhat'] = model.predict(data)
    SST = ((data[yname] - data[yname].mean())**2).sum()
    SSR = ((data['yhat'] - data[yname].mean())**2).sum()
    SSE = ((data[yname] - data['yhat'])**2).sum()
    r2 = SSR/SST
    adjustR2 = 1 - (1-r2)*(data.shape[0] - 1)/(data.shape[0] - model_k - 1)
    RMSE = (SSE/(data.shape[0] - model_k - 1))**0.5
    return adjustR2, RMSE

def assessTable(test, train, model, model_k, yname):
    r2test, RMSEtest = adjustedMetric(test, model, model_k, yname)
    r2train, RMSEtrain = adjustedMetric(train, model, model_k, yname)
    assessment = pd.DataFrame(index=['R2', 'RMSE'], columns=['Train', 'Test'])
    assessment['Train'] = [r2train, RMSEtrain]
    assessment['Test'] = [r2test, RMSEtest]
    return assessment

# Get the assement table fo our model
assessTable(Test, Train, lm, 9, 'spy')
# Train	Test
# R2	0.059020	0.067248
# RMSE	1.226068	1.701291

#%% Strategy built from Regression model
Train = indicepanel.iloc[-2000:-1000, :]
Test = indicepanel.iloc[-1000:, :]

formula = 'spy~spy_lag1+sp500+nasdaq+dji+cac40+aord+daxi+nikkei+hsi'
lm = smf.ols(formula=formula, data=Train).fit()

Train['PredictedY'] = lm.predict(Train)
Test['PredictedY'] = lm.predict(Test)

# Train
Train['Order'] = [1 if sig>0 else -1 for sig in Train['PredictedY']]
Train['Profit'] = Train['spy'] * Train['Order']

Train['Wealth'] = Train['Profit'].cumsum()
print('Total profit made in Train: ', Train['Profit'].sum())

plt.figure(figsize=(10, 10))
plt.title('Performance of Strategy in Train')
plt.plot(Train['Wealth'].values, color='green', label='Signal based strategy')
plt.plot(Train['spy'].cumsum().values, color='red', label='Buy and Hold strategy')
plt.legend()
plt.show()

# Test
Test['Order'] = [1 if sig>0 else -1 for sig in Test['PredictedY']]
Test['Profit'] = Test['spy'] * Test['Order']

Test['Wealth'] = Test['Profit'].cumsum()
print('Total profit made in Test: ', Test['Profit'].sum())

plt.figure(figsize=(10, 10))
plt.title('Performance of Strategy in Test')
plt.plot(Test['Wealth'].values, color='green', label='Signal based strategy')
plt.plot(Test['spy'].cumsum().values, color='red', label='Buy and Hold strategy')
plt.legend()
plt.show()

#%% Evaluate strategy
Train['Wealth'] = Train['Wealth'] + Train.loc[Train.index[0], 'Price']
Test['Wealth'] = Test['Wealth'] + Test.loc[Test.index[0], 'Price']

# Sharpe Ratio on Train data
Train['Return'] = np.log(Train['Wealth']) - np.log(Train['Wealth'].shift(1))
dailyr = Train['Return'].dropna()

print('Daily Sharpe Ratio is ', dailyr.mean()/dailyr.std(ddof=1))
print('Yearly Sharpe Ratio is ', (252**0.5)*dailyr.mean()/dailyr.std(ddof=1))

# Sharpe Ratio in Test data
Test['Return'] = np.log(Test['Wealth']) - np.log(Test['Wealth'].shift(1))
dailyr = Test['Return'].dropna()

print('Daily Sharpe Ratio is ', dailyr.mean()/dailyr.std(ddof=1))
print('Yearly Sharpe Ratio is ', (252**0.5)*dailyr.mean()/dailyr.std(ddof=1))

# Maximum Drawdown in Train data
Train['Peak'] = Train['Wealth'].cummax()
Train['Drawdown'] = (Train['Peak'] - Train['Wealth'])/Train['Peak']
print('Maximum Drawdown in Train is ', Train['Drawdown'].max())

# Maximum Drawdown in Test data
Test['Peak'] = Test['Wealth'].cummax()
Test['Drawdown'] = (Test['Peak'] - Test['Wealth'])/Test['Peak']
print('Maximum Drawdown in Test is ', Test['Drawdown'].max())

import numpy,import scipy,import statsmodels


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Dec  2 15:37:10 2020

Copyright 2020 by Hadrien Montanelli.
"""
# %% Imports.

# Standard library imports:
from math import pi
import matplotlib.pyplot as plt
import numpy as np
import time

# Chebpy imports:
from chebpy.trig import coeffs2vals, multmat, trigpts, vals2coeffs

# %% Multiplication g = f*h on [-1,1].

# Functions:
f = lambda x: np.cos(pi*x)
h = lambda x: np.exp(np.sin(10*pi*x))
gex = lambda x: f(x)*h(x)

# Grid:
n = 10000
x = trigpts(n)

# Compute coeffs of f:
F = vals2coeffs(f(x))

# Multiplication by h matrix in coefficient space:
start = time.time()
M = multmat(n, h)
end = time.time()
print(f'Time   (setup): {end-start:.5f}s')
plt.figure()
plt.spy(M)

# Multiply:
start = time.time()
G = M @ F
end = time.time()
print(f'Time (product): {end-start:.5f}s')

# Convert to value space:
g = coeffs2vals(G)

# Error:
error = np.max(np.abs(g - gex(x)))/np.max(np.abs(gex(x)))
print(f'Error  (L-inf): {error:.2e}')

# %% Multiplication g = f*h on [0,2*pi].

# Functions:
f = lambda x: np.cos(x)
h = lambda x: np.exp(np.sin(10*x))
gex = lambda x: f(x)*h(x)

# Grid:
n = 10000
x = trigpts(n, [0, 2*pi])

# Compute coeffs of f:
F = vals2coeffs(f(x))

# Multiplication by h matrix in coefficient space:
start = time.time()
M = multmat(n, h, [0, 2*pi])
end = time.time()
print(f'Time   (setup): {end-start:.5f}s')
plt.figure()
plt.spy(M)

# Multiply:
start = time.time()
G = M @ F
end = time.time()
print(f'Time (product): {end-start:.5f}s')

# Convert to value space:
g = coeffs2vals(G)

# Error:
error = np.max(np.abs(g - gex(x)))/np.max(np.abs(gex(x)))
print(f'Error  (L-inf): {error:.2e}')


# AUTOGENERATED! DO NOT EDIT! File to edit: notebooks/02_hypothesis_testing.ipynb (unless otherwise specified).

__all__ = ['get_mean_test_statistic', 'get_boot_test_statistics', 'get_p_value',
           'plot_hist_with_kde_for_hypothesis_test', 'get_chi2_test_statistic', 'get_spearmanr_test_statistic']

# Cell
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
from scipy import stats
import pandas as pd

from thinkstats import basic_stats
from thinkstats import nsfg

# Cell
def get_mean_test_statistic(a:np.ndarray, b: np.ndarray) -> float: return a.mean() - b.mean()

# Cell
def get_boot_test_statistics(a:np.ndarray, b: np.ndarray, n_tests:int=100,
                             test_statistic_fun:callable=get_mean_test_statistic,
                             mode="merge+shuffle"):
    "Computes bootstrapped test statistics using shuffling"
    test_statistic_boot = np.zeros(n_tests)
    if mode == "merge+shuffle":
        samples = np.hstack((a, b))
        n = len(a)
        for i in range(n_tests):
            np.random.shuffle(samples)
            _a, _b = samples[:n], samples[n:]
            test_statistic_boot[i] = test_statistic_fun(_a, _b)
    elif mode == "shuffle":
        for i in range(n_tests):
            _a, _b = np.random.choice(a, size=len(a)), np.random.choice(b, size=len(b))
            test_statistic_boot[i] = test_statistic_fun(_a, _b)
    else:
        raise NotImplementedError("`mode` needs to be 'merge+shuffle' or 'shuffle'.")
    return test_statistic_boot

# Cell
def get_p_value(x:np.ndarray, test_statistic:float):
    "Computes the p-value using `x` as generated from `get_boot_test_statistics` and the original `test_statistic`"
    kde = stats.gaussian_kde(x)
    p_val = kde.integrate_box_1d(-np.inf, test_statistic)
    if p_val > .5:
        p_val = 1. - p_val
    return p_val

# Cell
def plot_hist_with_kde_for_hypothesis_test(test_statistic_boot:np.ndarray, p_val:float,
                                           left_ext:float=.5, right_ext:float=.5):
    ax = basic_stats.plot_hist_with_kde(test_statistic_boot,
                                   plot_params={"xlabel": "test statistic", "ylabel": "frequency",
                                                "title": f"Sampled test statistics: p-value = {p_val:.4f}"},
                                   fig_params={"figsize": (7,7)}, left_ext=left_ext, right_ext=right_ext)
    ax.plot([test_statistic, test_statistic], ax.get_ylim(), marker="o", color="red", label="actual test statistic")
    ax.legend(loc="best")
    return ax

# Cell
def get_chi2_test_statistic(a:np.ndarray, b: np.ndarray) -> float:
    "`b` is the reference"
    freq_e = pd.Series(b).value_counts(normalize=True).sort_index()
    freq_o = pd.Series(a).value_counts(normalize=True).sort_index()
    return ((freq_o - freq_e)**2/freq_e).sum()

# Cell
def get_spearmanr_test_statistic(a:np.ndarray, b:np.ndarray) -> float:
    "Computes the ranked Spearman correlation"
    assert len(a) == len(b)
    return stats.spearmanr(a, b).correlation


import numpy as np

# function which transforms factorization from the form of the product WH to the sum of rank 1 matrices
def from_WH_to_rank_1_list(W, H):
    l = []
    for i in range(W.shape[1]):
        l.append(W[:, i:i+1] @ H[i:i+1, :])
    return l

# function which transforms factorization from the form of the sum of rank 1 matrices to the product WH
def from_rank_1_list_to_WH(rank_1_list):
    cols_W = []
    rows_H = []

    for V in rank_1_list:
        w, h = from_rank_1_matrix_to_wh(V)
        cols_W.append(w)
        rows_H.append(h)

    W = np.hstack(cols_W)
    H = np.vstack(rows_H)
    return W, H

def from_rank_1_matrix_to_wh(V):
    idx = np.where(V > 0)
    idx = [idx[0][0], idx[1][0]]

    v = V[idx[0], idx[1]]

    w = V[:, [idx[1]]]
    h = V[[idx[0]], :] / v

    if np.sum(np.abs(V - w @ h)) / (V.shape[0] * V.shape[1]) > 1e-9:
        raise Exception("Matrix appears to be of rank higher than 1\n{}".format(V))
    return w, h

# a function which makes sure that for i, ith column of W and ith row of H are of the same magnitude
# and that these rows / columns are sorted according to this magnitude in a decreasing order
def rescale_WH(W, H):
    s1 = np.sum(W, axis=0)
    s2 = np.sum(H, axis=1)
    s1[s1 == 0] = 1e-10
    s2[s2 == 0] = 1e-10

    srt_prod = np.sqrt(s1 * s2)
    idx = np.argsort(-1 * srt_prod)
    S1 = (srt_prod / s1).reshape((1, -1))
    S2 = (srt_prod / s2).reshape((-1, 1))
    return (W * S1)[:, idx], (H * S2)[idx, :]


import numpy as np

y_predicted = np.array([1, 1, 0, 0, 1])
y_true = np.array([0.30, 0.7, 1, 0, 0.5])

def mae(y_predicted, y_true):
    total_error = 0
    for yp, yt in zip(y_predicted, y_true):
        total_error += abs(yp - yt)
    print("Total error is:", total_error)
    mae = total_error / len(y_predicted)
    print("Mean absolute error is:", mae)
    return mae

mae(y_predicted, y_true)

# todo: Implement same thing using numpy in much easier way
print("np abs", np.abs(y_predicted - y_true))
print("np mean", np.mean(np.abs(y_predicted - y_true)))

def mae_np(y_predicted, y_true):
    return np.mean(np.abs(y_predicted - y_true))

print("np mean by", mae_np(y_predicted, y_true))

# todo: Implement Log Loss or Binary Cross Entropy
print("np log[0]", np.log([0]))
epsilon = 1e-15
print("np.log[1e-15]", np.log([1e-15]))
print("y_predicted", y_predicted)

y_predicted_new = [max(i, epsilon) for i in y_predicted]
print("y_predicted_new", y_predicted_new)

print("1 - epsilon", 1 - epsilon)
y_predicted_new = [min(i, 1 - epsilon) for i in y_predicted_new]
print("y_predicted_new", y_predicted_new)
y_predicted_new = np.array(y_predicted_new)
print("np.log(y_predicted_new)", np.log(y_predicted_new))

print("long line", -np.mean(y_true * np.log(y_predicted_new) + (1 - y_true) * np.log(1 - y_predicted_new)))

def log_loss(y_true, y_predicted):
    y_predicted_new = [max(i, epsilon) for i in y_predicted]
    y_predicted_new = [min(i, 1 - epsilon) for i in y_predicted_new]
    y_predicted_new = np.array(y_predicted_new)
    return -np.mean(y_true * np.log(y_predicted_new) + (1 - y_true) * np.log(1 - y_predicted_new))

print("last line", log_loss(y_true, y_predicted))

Python/Deep Learning/Code Basics/11 Loss or Cost Function.py


import jax.numpy as jnp

import matplotlib.pyplot as plt

from jsl.gym_envs.configs.regression_kf_test import get_config, make_evenly_spaced_x_sampler, sample_fn
from jsl.gym_envs.run import main as train

from numpy.linalg import inv

def posterior_lreg(X, y, R, mu0, Sigma0):
    Sn_bayes_inv = inv(Sigma0) + X.T @ X / R
    Sn_bayes = inv(Sn_bayes_inv)
    mn_bayes = Sn_bayes @ (inv(Sigma0) @ mu0 + X.T @ y / R)

    return mn_bayes, Sn_bayes

def main():
    config = get_config()
    params_per_trial, _ = train(config)

    trial = 0

    w0_hist, w1_hist = params_per_trial[trial]["mu"].T
    w0_err, w1_err = jnp.sqrt(params_per_trial[trial]["sigma"][:, [0, 1], [0, 1]].T)

    # Offline estimation
    input_dim, num_train = 2, 21
    x_generator = make_evenly_spaced_x_sampler(input_dim)
    X, y = sample_fn(None, x_generator, num_train, None)

    R = config.agent.init_kwargs['R']
    mu0 = config.agent.init_kwargs['mu0']
    Sigma0 = config.agent.init_kwargs['Sigma0']
    (w0_post, w1_post), Sigma_post = posterior_lreg(X, y, R, mu0, Sigma0)

    w0_std, w1_std = jnp.sqrt(Sigma_post[[0, 1], [0, 1]])

    dict_figures = {}

    timesteps = jnp.arange(num_train)
    fig, ax = plt.subplots()
    ax.errorbar(timesteps, w0_hist, w0_err, fmt="-o", label="$w_0$", color="black", fillstyle="none")
    ax.errorbar(timesteps, w1_hist, w1_err, fmt="-o", label="$w_1$", color="tab:red")

    ax.axhline(y=w0_post, c="black", label="$w_0$ batch")
    ax.axhline(y=w1_post, c="tab:red", linestyle="--", label="$w_1$ batch")

    ax.fill_between(timesteps, w0_post - w0_std, w0_post + w0_std, color="black", alpha=0.4)
    ax.fill_between(timesteps, w1_post - w1_std, w1_post + w1_std, color="tab:red", alpha=0.4)
    plt.legend()
    ax.set_xlabel("time")
    ax.set_ylabel("weights")
    ax.set_ylim(-8, 4)
    ax.set_xlim(-0.5, num_train)
    dict_figures["linreg_online_kalman"] = fig

    return dict_figures

if __name__ == "__main__":
    from jsl.demos.plot_utils import savefig
    plt.rcParams["axes.spines.right"] = False
    plt.rcParams["axes.spines.top"] = False
    dict_figures = main()
    savefig(dict_figures)
    plt.show()


from functools import partial

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

def my_kde_bandwidth(obj, fac=1./5):
    """We use Scott's Rule, multiplied by a constant factor."""
    return np.power(obj.n, -1./(obj.d+4)) * fac

loc1, scale1, size1 = (-2, 1, 175)
loc2, scale2, size2 = (2, 0.2, 50)
x2 = np.concatenate([np.random.normal(loc=loc1, scale=scale1, size=size1),
                     np.random.normal(loc=loc2, scale=scale2, size=size2)])

x_eval = np.linspace(x2.min() - 1, x2.max() + 1, 500)

kde = stats.gaussian_kde(x2)
kde2 = stats.gaussian_kde(x2, bw_method='silverman')
kde3 = stats.gaussian_kde(x2, bw_method=partial(my_kde_bandwidth, fac=0.2))
kde4 = stats.gaussian_kde(x2, bw_method=partial(my_kde_bandwidth, fac=0.5))

pdf = stats.norm.pdf
bimodal_pdf = pdf(x_eval, loc=loc1, scale=scale1) * float(size1) / x2.size + \
              pdf(x_eval, loc=loc2, scale=scale2) * float(size2) / x2.size

fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111)

ax.plot(x2, np.zeros(x2.shape), 'b+', ms=12)
ax.plot(x_eval, kde(x_eval), 'k-', label="Scott's Rule")
ax.plot(x_eval, kde2(x_eval), 'b-', label="Silverman's Rule")
ax.plot(x_eval, kde3(x_eval), 'g-', label="Scott * 0.2")
ax.plot(x_eval, kde4(x_eval), 'c-', label="Scott * 0.5")
ax.plot(x_eval, bimodal_pdf, 'r--', label="Actual PDF")

ax.set_xlim([x_eval.min(), x_eval.max()])
ax.legend(loc=2)
ax.set_xlabel('x')
ax.set_ylabel('Density')

plt.show()