# Laboratorul 7

import numpy as np

def metIacobi(B, eps, tol):
    """

    B: matricea simetrica
    eps: eroarea
    -----------------
    return: valori si vectori proprii

    """

    A=np.copy(B)
    n=np.shape(A)[0]
    modul_A = 0
    T = np.eye(n, n)

    for i in range(n):
        for j in range(n):
            if i != j:
                modul_A += A[i][j] ** 2

    modul_A = np.sqrt(modul_A)
    while modul_A >= eps:
        max = abs(A[1][0])
        p = 0
        q = 1
        for j in range(n):
            for i in range(j + 1, n):
                if abs(A[i][j]) > max:
                    max = abs(A[i, j])
                    p = j
                    q = i

        if abs(A[p, p] - A[q, q]) <= tol:
            teta = np.pi / 4
        else:
            teta = 1 / 2 * np.arctan(2 * A[p, q] / (A[q, q]-A[p, p]))

        c = np.cos(teta)
        s = np.sin(teta)

        R = np.eye(n, n)
        R[p, p] = c
        R[p, q] = s
        R[q, p] = -s
        R[q, q] = c
        A = np.transpose(R)@A@R

        T = T@R
        modul_A = 0
        for i in range(n):
            for j in range(n):
                if i != j:
                    modul_A += A[i][j] ** 2
        modul_A = np.sqrt(modul_A)

    return A, T

B = np.array([[17, -2, 3 * np.sqrt(3)],
              [-2, 8, 2 * np.sqrt(3)],
              [3 * np.sqrt(3), 2 * np.sqrt(3), 11]], float)
A, T = metIacobi(B, 10 ** (-3), 10 ** (-7))
print(A)
print(T)

B1 = np.array([[1, 1, 0],
               [1, 1, 0],
               [0, 0, 2]], float)

A1, T = metIacobi(B1, 10 ** (-3), 10 ** (-7))
print(A1)
print(T)

# Verificare
print(np.transpose(T)@B1@T)
v = T[:, 0]
print(B1@v - A1[0, 0] * v)

Calcul Numeric (CN)/Laborator/Laborator 7/lab7.py


import matplotlib.pyplot as plt
import numpy as np
from scipy.sparse import lil_matrix
from scipy.sparse.linalg import bicgstab
from scipy.linalg import solve
# is_inner_for = True
is_inner_for = False
is_sparse = True
# is_sparse = False

def get_analytical_solution(max_x, max_y, total_points_x, total_points_y):
    x = np.linspace(0, max_x, total_points_x)
    y = np.linspace(0, max_y, total_points_y)
    analytical_solution = np.zeros((total_points_x, total_points_y))
    for k in range(total_points_x):
        for i in range(total_points_y):
            analytical_solution[k, i] = \
                (
                        np.sin(x[k]) * np.sinh(y[i])
                 ) / (
                        np.sinh(max_y) * np.sin(max_x)
                )
    return analytical_solution

def get_linear_system_matrix_slow(mesh_points_x, mesh_points_y, mesh_step_ratio):
    matrix_size = mesh_points_x * mesh_points_y
    matrix = np.zeros((matrix_size, matrix_size))
    for k in range(matrix_size):
        for i in range(matrix_size):
            # origin for all finite differences
            if k == i:
                matrix[k, i] = -2 * (1 + mesh_step_ratio)
            # left and right finite differences
            if k == i + 1 and k % mesh_points_y != 0:
                matrix[k, i] = mesh_step_ratio
            if k == i - 1 and (k+1) % mesh_points_y != 0:
                matrix[k, i] = mesh_step_ratio
            # bottom and top finite differences
            if k == i + mesh_points_y or k == i - mesh_points_y:
                matrix[k, i] = 1

    return matrix

def get_linear_system_matrix(mesh_points_x, mesh_points_y, mesh_step_ratio):
    matrix_size = mesh_points_x * mesh_points_y
    matrix = lil_matrix((matrix_size, matrix_size)) if is_sparse else np.zeros((matrix_size, matrix_size))
    for k in range(matrix_size):
        # origin for all finite differences
        matrix[k, k] = -2 * (1 + mesh_step_ratio)

    for k in range(matrix_size-1):
        # left finite differences
        if (k+1) % mesh_points_y != 0:
            matrix[k+1, k] = mesh_step_ratio

    for k in range(1,matrix_size):
        # right finite differences
        if k % mesh_points_y != 0:
            matrix[k - 1, k] = mesh_step_ratio

    for k in range(matrix_size - mesh_points_y):
        # bottom finite differences
        matrix[k + mesh_points_y, k] = 1

    for k in range(mesh_points_y, matrix_size):
        # top finite differences
        matrix[k - mesh_points_y, k] = 1

    return matrix.tocsc() if is_sparse else matrix

def get_rhs_vector(boundary_x, boundary_y, step_ratio):
    mesh_points_x = len(boundary_x)
    mesh_points_y = len(boundary_y)
    all_mesh_points = mesh_points_x * mesh_points_y
    rhs_vector = np.zeros(all_mesh_points)
    for k in range(all_mesh_points):
        if (k + 1) % mesh_points_y == 0:
            rhs_vector[k] += -step_ratio * boundary_x[int(k / mesh_points_y)]
        if k > all_mesh_points - mesh_points_y - 1:
            rhs_vector[k] += -boundary_y[k % mesh_points_y]
    return rhs_vector

def initialize_with_boundary_conditions(top_boundary, right_boundary):
    numerical_solution = np.zeros((len(top_boundary), len(right_boundary)))
    numerical_solution[:, -1] = top_boundary
    numerical_solution[-1, :] = right_boundary

    # 5-point stencil doesn't use corner points of the analytical solution boundary
    return numerical_solution, top_boundary[1:-1], right_boundary[1:-1]

def get_step(max_x, max_y, total_points_x, total_points_y):
    step_x = max_x / (total_points_x - 1)
    step_y = max_y / (total_points_y - 1)
    step_ratio = (step_x / step_y) ** 2
    return step_ratio, step_y, step_y

def get_numerical_solution(top_boundary, right_boundary, step_ratio):
    numerical_solution, boundary_x, boundary_y = \
        initialize_with_boundary_conditions(top_boundary, right_boundary)

    if is_inner_for:
        linear_system_matrix = \
            get_linear_system_matrix_slow(len(boundary_x), len(boundary_y), step_ratio)
    else:
        linear_system_matrix = \
            get_linear_system_matrix(len(boundary_x), len(boundary_y), step_ratio)

    rhs_vector = get_rhs_vector(boundary_x, boundary_y, step_ratio)

    if is_sparse:
        linear_system_solution, exit_code = \
            bicgstab( linear_system_matrix, rhs_vector, tol=1e-14)
    else:
        linear_system_solution = solve( linear_system_matrix, rhs_vector)

    inner_shape = (len(boundary_x), len(boundary_y))
    numerical_solution[1:-1, 1:-1] = \
        np.reshape(linear_system_solution, inner_shape)
    return numerical_solution

def get_solution(max_x, max_y, total_points_x, total_points_y):
    analytical = get_analytical_solution(max_x, max_y,
                                         total_points_x, total_points_y)

    top_boundary = analytical[:, -1]
    right_boundary = analytical[-1, :]
    step_ratio, step_x, step_y = get_step(max_x, max_y,
                                          total_points_x, total_points_y)

    numerical = get_numerical_solution(top_boundary, right_boundary,
                                       step_ratio)
    solution_error = np.abs(numerical - analytical)
    error_rms = (
                    np.sum(
                        np.power(
                            # use only computed part for error estimate
                            solution_error[1:-1, 1:-1],
                            2
                        )
                    ) / (
                        (total_points_x-2) * (total_points_y-2)
                    )
                )**0.5
    return numerical, analytical, solution_error, error_rms, step_x, step_y

if __name__ == '__main__':
    max_x = 5
    max_y = 4
    total_points_x = 25
    total_points_y = 15

    numerical, analytical, solution_error, _, _, _ = \
        get_solution(max_x, max_y, total_points_x, total_points_y)

    data = [numerical, analytical, solution_error]
    titles = ['Numerical', 'Analytical', 'abs(error)']

    fig, axs = plt.subplots(figsize=(10, 5), ncols=len(data))
    for i in range(len(data)):
        numerical_plot = axs[i].imshow(data[i])
        fig.colorbar(numerical_plot, ax=axs[i])
        axs[i].set_title(titles[i])
    plt.show()


import numpy as np

def wallNormalGrid(num:int, eta=1., delta=1.):
    """The non-uniformly spaced grid for the wall-normal direction `[1]`_.

    Args:
        num (int): Number of grids to generate.
        eta (float, optional): The parameter to controls how strongly the points are clusterd near the wall. Defaults to 1..
        delta (float, optional): The channel half-width. Defaults to 1..

    Returns:
        np.array: Non-uniformly spaced grid.

    .. _[1] [Lee and Moser, "Direct numerical simulation of turbulent channel flow up to Ret=5200", 2015.](https://arxiv.org/abs/1410.7809)
    """
    zeta = np.linspace(start=-1., stop=1., num=num)
    return delta * np.sin(eta*zeta*np.pi*0.5) / np.sin(eta*np.pi*0.5)

# def wallNormalGrid2(num:int, alpha=None):
#     """The non-uniformly spaced grid for the wall-normal direction [#2]_.

#     Args:
#         num (int): Number of grids to generate.
#         alpha (float): Adjustable patameter. 0.967 for Ret=180, 0.98 for Ret=395, None for Ret=640

#     Returns:
#         _type_: _description_

#     ToDo:
#         Something wrong with this method.

#     .. [#2]  Hiroyuki Abe, Hiroshi Kawamura, Yuichi Matsuo, "Direct Numerical Simulation of a Fully Developed Turbulent Channel Flow With Respect to the Reynolds Number Dependence", 2001.
#     """
#     j = np.arange(num)
#     zeta = 2 * j / num - 1
#     if alpha==None:
#         alpha = 0.9885 - 0.5*zeta**2 + 0.405*zeta**3
#     return np.tanh(zeta / np.tanh(alpha)) / (2*alpha) + 0.5

if __name__=="__main__":
    Ret = 1000
    ny = 513
    eta = 0.99
    delta = 1.
    y = wallNormalGrid(ny, eta, delta)
    dy = np.diff(y)
    dyplus_wall = np.abs(dy).min() * Ret
    dyplus_center = np.abs(dy).max() * Ret
    print(dyplus_wall, dyplus_center)
    assert dyplus_wall < 0.45
    assert dyplus_center < 6.5
    # growthRate_wall = dy[1] / dy[0]
    # print(growthRate_wall)

    nx = 640
    Lx = 2*np.pi
    dx = Lx / nx
    dxplus = dx * Ret
    print(dxplus)
    assert dxplus < 10

    nz = 540
    Lz = np.pi
    dz = Lz / nz
    dzplus = dz * Ret
    print(dzplus)
    assert dzplus < 6


"""
Please see
https://computationalmindset.com/en/neural-networks/ordinary-differential-equation-solvers.html#sys1
for details
"""

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
import tensorflow_probability as tfp

def ode_sys(t, X):
	x=X[0]
	dx_dt=X[1]
	d2x_dt2=-dx_dt - 2*x
	return [dx_dt, d2x_dt2]

an_sol_x = lambda t : \
	np.exp(-t/2.) * (np.cos(np.sqrt(7) * t / 2.) + \
	np.sin(np.sqrt(7) * t / 2.)/np.sqrt(7.))

t_begin=0.
t_end=12.
t_nsamples=100
t_space = np.linspace(t_begin, t_end, t_nsamples)
t_init = tf.constant(t_begin)
x_init = tf.constant(1.)
dxdt_init = tf.constant(0.)

x_an_sol = an_sol_x(t_space)

num_sol = tfp.math.ode.BDF().solve(ode_sys, t_init, [x_init, dxdt_init],
	solution_times=tfp.math.ode.ChosenBySolver(tf.constant(t_end)) )

plt.figure()
plt.plot(t_space, x_an_sol, '--', linewidth=2, label='analytical')
plt.plot(num_sol.times, num_sol.states[0], linewidth=1, label='numerical')
plt.title('ODE 2nd order IVP solved by TFP with BDF')
plt.xlabel('t')
plt.ylabel('x')
plt.legend()
plt.show()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time     : 2018/12/04 15:05
# @Author   : Iydon
# @File     : course9.py

import numpy as np
import scipy as sp

def gersgorin_circle(A, numpy):
    r"""
    |z-a_{ii}|<=\sum_{j!=i}|a_{ij}|
    """
    shape = A.shape
    if shape[0] != shape[-1]:
        raise Exception("Dimensions do not match.")
    result = [[] for i in range(shape[0])]
    for i in range(shape[0]):
        summation = numpy.sum(numpy.abs(A[i,])) - numpy.abs(A[i,i])
        result[i] = [A[i,i], summation]
    return result

def power_method(A, numpy, x0=None, judge=None, eps=1e-6, max_loop=64, disply=False, p=0):
    r"""
    Numpy
    B = A - pI.
    """
    if x0==None:    x0 = np.ones((A.shape[0], 1))
    if judge==None: judge = lambda x,y: numpy.linalg.norm(x-y, float("inf"))
    A_    = A.copy() - p*numpy.eye(A.__len__())
    count = 0
    mu    = numpy.max(x0)
    x0    = x0 / mu
    last  = x0
    while count<max_loop:
        count += 1
        uv = numpy.matmul(A_, x0)
        mu = numpy.max(uv)
        x0 = uv / mu
        if disply:
            print("%%%dd: %%s"%(len(str(max_loop)))%(count, mu))
        if judge(last,x0)<eps:
            break
        last = x0
    return mu + p

def inverse_power_method(A, scipy, x0=None, judge=None, eps=1e-6, max_loop=64, disply=False, p=0):
    r"""
    Scipy
    pass
    """
    from scipy import linalg
    if x0==None:    x0 = np.ones((A.shape[0], 1))
    if judge==None:
        norm  = lambda x: scipy.linalg.norm(x, float("inf"))
        judge = lambda x,y: abs(norm(x-y))
    A_    = A.copy() - p*scipy.eye(len(A))
    LU    = linalg.lu_factor(A_)
    count = 0
    mu    = max(x0)
    x0    = x0 / mu
    last  = x0
    while count<max_loop:
        count += 1
        uv = linalg.lu_solve(LU, x0)
        mu = max(abs(x0))[0]
        x0 = uv / mu
        if disply:
            print("%%%dd: %%s"%(len(str(max_loop)))%(count, p+1/mu))
        if judge(last,x0)<eps:
            break
        last = x0
    return p + 1/mu

A = np.matrix([[0.25,1,0.5],[1,0.25,0.25],[0.5,0.25,1.25]])
print(A)
result = gersgorin_circle(A, np)
print(result)

result = power_method(A, np, disply=True, p=0)
print(result.T)
print(np.max(np.linalg.eigvals(A)))

A = sp.matrix([[1,2,4],[1,3,9],[1,5,1]])
result = inverse_power_method(A, sp, disply=True, eps=1e-8, max_loop=64, p=-7)
print(result.T)
print(np.min(np.linalg.eigvals(A)))


from __future__ import division, print_function

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import spectrogram
from butter import butter_lowpass_filtfilt

time, pressure = np.loadtxt('pressure.dat', skiprows=2,
                            delimiter=',', unpack=True)

t0 = time.min()
t1 = time.max()

fs = 50000

nperseg = 80
noverlap = nperseg - 4

print("Spectrogram window length: %g ms (%d samples)" %
      (1000*nperseg/fs, nperseg))

f, t, spec = spectrogram(pressure, fs=fs, nperseg=nperseg, noverlap=noverlap,
                         window='hann')
t += t0/1000

cutoff = 1250
pressure_filtered = butter_lowpass_filtfilt(pressure, cutoff, fs, order=8)

f, t, filteredspec = spectrogram(pressure_filtered, fs=fs, nperseg=nperseg,
                                 noverlap=noverlap, window='hann')
t += t0/1000

tlo = t[0]*1000
thi = t[-1]*1000

spec_db = 10*np.log10(spec)
filteredspec_db = 10*np.log10(filteredspec)

vmax = max(spec_db.max(), filteredspec_db.max())
vmin = vmax - 80.0  # Clip display of values below 80 dB

linecolor = 'k'
cmap = plt.cm.coolwarm

plt.figure(figsize=(4.0, 4.5))
plt.subplot(211)
plt.plot(time, pressure, color=linecolor, alpha=0.8)
plt.xlim(tlo, thi)
plt.ylim(0, 10)
plt.ylabel("Pressure (MPa)")
plt.grid(alpha=0.25)

plt.subplot(212)
plt.pcolormesh(1000*t, f/1000, spec_db, vmin=vmin, vmax=vmax,
               cmap=cmap, shading='gouraud')
plt.xlim(tlo, thi)
plt.ylabel('Frequency (kHz)')
plt.xlabel('Time (ms)')
plt.tight_layout()
plt.savefig("pressure_example_input.pdf")

plt.figure(2, figsize=(4.0, 4.5))
plt.subplot(211)
plt.plot(time, pressure, color=linecolor, alpha=0.15)
plt.plot(time, pressure_filtered, color=linecolor, alpha=0.8)
plt.xlim(tlo, thi)
plt.ylim(0, 10)
plt.ylabel("Pressure (MPa)")
plt.grid(alpha=0.25)

plt.subplot(212)
plt.pcolormesh(1000*t, f/1000, filteredspec_db, vmin=vmin, vmax=vmax,
               cmap=cmap, shading='gouraud')
plt.axhline(cutoff/1000, color='k', linestyle='--', linewidth=1, alpha=0.5)
plt.xlim(tlo, thi)
plt.ylabel('Frequency (kHz)')
plt.xlabel('Time (ms)')
plt.tight_layout()
plt.savefig("pressure_example_filtered.pdf")


"""
Created on Friday 25 August 2017
Last update: Tuesday 26 December 2017

@author: Michiel Stock
michielfmstock@gmail.com

Some functions to illustrate the convergence of the optimization algorithms
for quadratic systems
"""

import sys
sys.path.append('helpers/')
from colors import colors_list
import matplotlib.pyplot as plt
import numpy as np
#import seaborn as sns
#sns.set_style('white')

def gd_error_decomposition(eigenvalues=[0.1, 1.5, 1.6, 1.8, 2],
                    x0=np.ones((5, 1)), n_steps=50, t='optimal', ax=None,
                    cumulative=True):
    """
    #FIX: docstring
    short description

    Inputs:
        -

    Output:
        -
    """
    if t=='optimal':
        t = 2 / (min(eigenvalues) + max(eigenvalues))
    n = len(eigenvalues)
    ev = np.reshape(eigenvalues, (-1, 1))  # vector format
    steps = np.arange(0, n_steps + 1)
    error_per_comp = (1 - t * ev) ** (2 * steps.reshape((1, -1)))
    error_per_comp *= ev * x0**2
    if ax is not None:
        colors = iter(colors_list)
        prev = np.zeros_like(steps) + 1e-10
        current = prev + 0
        for i, val in enumerate((eigenvalues)):
            label=r'$\lambda_{}=${}'.format(i+1, val)
            if cumulative:
                current += error_per_comp[i,:]
                ax.fill_between(steps+1, prev, current,
                        color=next(colors),
                        label=label)
                prev[:] = current
            else:
                ax.plot(steps+1, error_per_comp[i,:],color=next(colors),
                            label=label, lw=2)
        ax.legend(loc=0)
        if cumulative:
            ax.set_ylabel(r'$f(\mathbf{x}^{(k)})-f(\mathbf{x}^\star)$')
            #ax.set_ylim([1e-10, 5])
            ax.set_ylim([1e-10, error_per_comp[:,0].sum()])
        else:
            ax.set_ylabel(r'$(1-t\lambda_i)^{2k}\lambda_i[\mathbf{u}_i^\intercal(\mathbf{x}^{(k)} - \mathbf{x}^\star)]^2$')
        ax.set_xlabel(r'$k+1$')
        ax.set_ylim([1e-10, error_per_comp[:,0].sum()])
    return error_per_comp

def gd_convergence_and_bound(eigenvalues=[0.1, 1.5, 1.6, 1.8, 2], n_steps=50,
                        t='optimal', x0=np.ones((5, 1))):
    # condition number
    #FIX: docstring
    kappa = max(eigenvalues) / min(eigenvalues)
    c = 1 - 1/kappa
    error = gd_error_decomposition(eigenvalues=eigenvalues, n_steps=n_steps,
        t=t, x0=x0).sum(0)
    bound = np.sum([xi**2 * e for xi, e in zip(x0, eigenvalues)]) * c ** np.arange(0, n_steps+1)
    return error, bound

if __name__ == '__main__':
    # choose format of the plots
    format = 'png'

    # convergence plot

    n_steps = 1000 - 1

    fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(12, 6))
    E = gd_error_decomposition(ax=ax0, n_steps=n_steps, cumulative=True)
    ax0.semilogx()
    ax0.set_title('Error of gradient descent\n(cumulative eigencomponents)')

    gd_error_decomposition(ax=ax1, n_steps=n_steps, cumulative=False)
    ax1.set_title('Error of gradient descent\n(individual eigencomponents)')
    ax1.loglog()
    fig.tight_layout()
    fig.savefig('Chapters/01.Quadratic/Figures/convergence_decomposition.{}'.format(format))

    # error vs bound
    kappas = [1.1, 2.5, 5, 50, 100]

    n_steps = 10000
    colors = iter(colors_list)
    fig, ax = plt.subplots()
    steps = np.arange(0, n_steps+1)+1
    for kappa in kappas:
        color = next(colors)
        error, bound = gd_convergence_and_bound(eigenvalues=[1, kappa],
                    n_steps=n_steps, x0=np.ones((2, 1)))
        ax.plot(steps, error, color=color, ls='-', label=r'$\kappa$={}'.format(kappa), lw=2)
        ax.plot(steps, bound, color=color, ls='--', lw=2)

    ax.set_ylim([1e-6, 200])
    ax.loglog()
    ax.set_title('Convergence (-) and bound (--) of GD\nfor different condition numbers')
    ax.legend(loc=0)
    ax.set_ylabel(r'$f(\mathbf{x}^{(k)})-f(\mathbf{x}^\star)$')
    ax.set_xlabel(r'$k+1$')
    fig.tight_layout()
    fig.savefig('Chapters/01.Quadratic/Figures/convergence_bound.{}'.format(format))

    plt.close('all')


import numpy as np

#Task 1
#We define an array1
array1 = np.array ( [[1., 1., 1.],
                     [1., 1., 1.]], dtype=int)
#Use ones function to define array_ones so that it is equal to array1
array_ones = np.ones ( (2,3) )
#Make this true
print (np.array_equal(array_ones, array1) )

#Task 2
#We define an array2
array2 = np.array ( [0, 2, 4, 6, 8] )
#Use arange function to define array_arange so that it is equal to array2
array_arange = np.arange (0, 9, 2)
#Make this true
print (np.array_equal(array_arange, array2) )

#Task 3
#We define an array3
array3 = np.array ( [0, 0.25, 0.5, 0.75, 1] )
#Use linspace function to define array_linspace so that it is equal to array3
array_linspace = np.linspace (0, 1, 5)
#Make this true
print (np.array_equal(array_linspace, array3) )

#Task 4
#Before uncommenting and running these, think about what the results will be

print ("--------------simple operations----------------------")
print (array2 * 5)
print (array2 / 2)
print (array2 ** 2)
print (array2 <= 5)

print ("----------------upcasting----------------------------")
array4 = array2 + array3
print (array4)
print (array4.dtype)

#Task 5
#Which of the following demand will not generate errors?
#Uncomment that one
print ("------------------multiplication---------------------")
array1 = np.array ( [[1., 1., 1.],
                     [1., 1., 1.]], dtype=int)
array5 = np.array( [[1, 2],
                    [3, 4],
                    [5, 6]] )
#print (array1 * array5)
print (np.dot (array1, array5) )


from IMLearn.learners import UnivariateGaussian, MultivariateGaussian
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
import matplotlib.pyplot as plt
pio.templates.default = "simple_white"

def test_univariate_gaussian():
    # Question 1 - Draw samples and print fitted model
    mu, sigma, num_of_samples = 10, 1, 1000
    univariate_gaussian = UnivariateGaussian()
    X = np.random.normal(mu, sigma, num_of_samples)
    univariate_gaussian.fit(X)
    print('(', univariate_gaussian.mu_, ', ', univariate_gaussian.var_, ')', sep='')

    # Question 2 - Empirically showing sample mean is consistent
    ms = np.linspace(10, 1000, 100).astype(int)
    values = []
    for m in ms:
        univariate_gaussian.fit(X[:m])
        values.append(abs(mu - univariate_gaussian.mu_))

    plt.plot(ms, values, marker='.', markersize=4)
    plt.title('Abs. dist. between the estimated and true value of the expectation')
    plt.xlabel('Number of Samples')
    plt.ylabel('Absolute Distance')
    plt.show()

    # Question 3 - Plotting Empirical PDF of fitted model
    Y = univariate_gaussian.pdf(X)
    plt.scatter(X, Y, s=4)
    plt.title('The empirical pdf of drawn samples')
    plt.xlabel('samples')
    plt.ylabel('pdf')
    plt.show()

def test_multivariate_gaussian():
    # Question 4 - Draw samples and print fitted model
    num_of_samples = 1000
    mu = np.array([0, 0, 4, 0])
    sigma = np.array([[1, 0.2, 0, 0.5],
                      [0.2, 2, 0, 0],
                      [0, 0, 1, 0],
                      [0.5, 0, 0, 1]])

    X = np.random.multivariate_normal(mu, sigma, num_of_samples)
    multivariate_gaussian = MultivariateGaussian()
    multivariate_gaussian.fit(X)
    print(multivariate_gaussian.mu_, '\n', multivariate_gaussian.cov_, sep='')

    # Question 5 - Likelihood evaluation
    mesh_length = 200
    f1, f3 = np.linspace(-10, 10, mesh_length), np.linspace(-10, 10, mesh_length)
    x, y = np.meshgrid(f1, f3)
    f = np.vectorize(lambda i, j: multivariate_gaussian.log_likelihood(np.array([i, 0, j, 0]), sigma, X))
    z = f(x, y)
    plt.pcolormesh(y, x, z)
    plt.title('Heatmap of the Logliklihood of mu = [f1, 0, f3, 0]')
    plt.xlabel('f1 values')
    plt.ylabel('f3 values')
    plt.colorbar()
    plt.show()

    # Question 6 - Maximum likelihood
    maximizer = (np.argwhere(z == z.max()))[0]

    print('The maximizer of the liklihood is mu = [',
          round(f1[maximizer[0]], 4), ', 0, ',
          round(f3[maximizer[1]], 4), ', 0]', sep='')

if __name__ == '__main__':
    np.random.seed(0)
    test_univariate_gaussian()
    test_multivariate_gaussian()


import math
import random
from utils.num.ntheory import crt, egcd, modinv

#used for rsa problems where m^e < n meaning to decrypt c you can just do invpow(c,e)
#from https://stackoverflow.com/q/55436001/7941251
def invpow(x: int, n: int) -> int:
	"""Finds the integer component of the n'th root of x,
	an integer such that y ** n <= x < (y + 1) ** n.
	"""
	high = 1
	while high**n < x:
		high *= 2
	low = high // 2
	while low < high:
		mid = (low + high) // 2
		if low < mid and mid**n < x:
			low = mid
		elif high > mid and mid**n > x:
			high = mid
		else:
			return mid
	return mid + 1

# https://github.com/ashutosh1206/Crypton/tree/master/RSA-encryption/Factorisation-Fermat
def fermats_factorization(n):
	""" factor n by using difference of squares, only works if factors are close """
	a = math.isqrt(n) + 1
	b_2 = a**2 - n  # b squared
	while math.isqrt(b_2)**2 != b_2:
		a += 1
		b_2 = a**2 - n
	b = math.isqrt(b_2)
	return (a - b, a + b)

# see cryptopals 40
def hastad_broadcast_attack(cts, n_s, e):
	return invpow(crt(cts, n_s), e)

# see cryptopals 41
def decrypt_unpadded_oracle(c, e, n, oracle):
	s = random.randint(2, n - 1)
	c_prime = (pow(s, e, n) * c) % n
	p_prime = oracle(c_prime)
	return (p_prime * modinv(s, n)) % n

def common_mod_attack(c1, c2, e1, e2, n):
	""" pt is encrypted using 2 different e's but same n"""
	gcd, a, b = egcd(e1, e2)
	ct = (pow(c1, a, n) * pow(c2, b, n)) % n
	return invpow(ct, gcd)

def lsb_parity_oracle(c, e, n, oracle):
	# https://github.com/ashutosh1206/Crypton/tree/master/RSA-encryption/Attack-LSBit-Oracle
	upper = n
	lower = 0
	for _ in range(n.bit_length()):
		c = (c * pow(2, e, n)) % n
		if oracle(c):  #even
			upper = (upper + lower) // 2
		else:
			lower = (lower + upper) // 2
	return upper

def wieners_attack(e: int, n: int):
	""" returns d """
	#pylint: disable=import-outside-toplevel
	#lazy imports to avoid slowing down initial load
	from sympy.ntheory.continued_fraction import continued_fraction_convergents, continued_fraction_iterator
	from sympy.core.numbers import Rational
	c = 2
	convergents = continued_fraction_convergents(continued_fraction_iterator(Rational(e, n)))
	for convergent in convergents:
		d = convergent.denominator()
		p = pow(c, d, n)
		if pow(p, e, n) == c:
			return d
	raise ValueError(f"Couldn't find d for {e=} and {n=}")


import numpy as np

def bootstrap_test(tested_array, H0_mean, sampling = 10_000, seed=0, print_result=True):

  """This function outputs the probability of obtaining the observed difference between the mean of the tested_array and the H0_mean, given that the HO_mean is true (P-value).
  Type hints :
    tested_array : np.array()
    HO_mean : int or float
    sampling : int
    seed : int"""

  np.random.seed(seed)

  simulated_sample_means = [np.random.choice(tested_array,
                                  size=tested_array.shape[0],
                                  replace=True).mean()
                                  for _ in range(sampling)]

  array_mean = tested_array.mean()
  interval = [array_mean - abs(array_mean - H0_mean), array_mean + abs(array_mean - H0_mean)]

  number_extreme_values = 0
  for sample_mean in simulated_sample_means:
      if not interval[0] < sample_mean < interval[1]:
          number_extreme_values += 1

  p_value =  number_extreme_values / len(simulated_sample_means)

  if print_result == True :  print(f"P-value is approximately {p_value:.2f}")

  return p_value


"""
Peng-robinson equation of state calculator.

As used: https://www.poppyi.com/app_design_form/public_render/peng%20robinson%20eq%20of%20state
"""
import numpy as np

R = 8.31446  # Pa.m3/K

def validate_numeric(form_data, key):
    try:
        return float(form_data[key])
    except ValueError:
        raise Warning('Numeric input required for: {}'.format(key))

def main(form_data):
    temperature_kelvin = validate_numeric(form_data, 'temp_kelvin')
    pressure_pascal = validate_numeric(form_data, 'pressure_pascal')
    temperature_critical = validate_numeric(form_data, 'critical_temp_kelvin')
    pressure_critical = validate_numeric(form_data, 'critical_pressure_pa')
    acentric_factor = validate_numeric(form_data, 'acentric_factor')

    temperature_critical = float(temperature_critical)
    pressure_critical = float(pressure_critical)
    acentric_factor = float(acentric_factor)

    a = (0.457235 * (R * temperature_critical)**2) / pressure_critical
    b = 0.077796 * R * temperature_critical / pressure_critical

    if acentric_factor <= 0.49:
        kappa = 0.37464 + 1.54226 * acentric_factor - 0.26992 * acentric_factor**2
    else:
        kappa = 0.379642 + 1.48503 * acentric_factor - 0.164423 * acentric_factor**2 + 0.0166666 * acentric_factor**3

    reduced_temp = temperature_kelvin / temperature_critical

    alpha = (1 + kappa * (1 - reduced_temp**0.5))**2

    A = alpha * a * pressure_pascal / (R * temperature_kelvin)**2
    B = b * pressure_pascal / (R * temperature_kelvin)

    k3 = 1
    k2 = 1 - B
    k1 = A - 2 * B - 3 * B**2
    k0 = A * B - B**2 - B**3

    z_roots = np.roots([k3, -k2, k1, -k0])

    z = z_roots.real[z_roots.imag < 1e-5]

    z_filtered = [float(z_) for z_ in z if z_ >= 1e-3]

    if len(z_filtered) == 0:
        raise Warning('peng robinson eq of state error: no solutions found (no roots)')

    z_str = [str(i) for i in z_filtered]
    return {
        'compressibility_max': {
            'action': 'update',
            'value': max(z_filtered)
        },
        'compressibility_min': {
            'action': 'update',
            'value': min(z_filtered)
        },
        'peng_calc_output': {
            'action': 'update'
        },
        'debug_outputs': {
            'action': 'update'
        },
        'critical_temp': {
            'action': 'update',
            'value': temperature_critical
        },
        'critical_pressure': {
            'action': 'update',
            'value': pressure_critical
        },
        'acentric_factor': {
            'action': 'update',
            'value': acentric_factor
        },
        'reduced_temp': {
            'action': 'update',
            'value': reduced_temp
        },
        'a': {
            'action': 'update',
            'value': a
        },
        'b': {
            'action': 'update',
            'value': b
        },
        'k': {
            'action': 'update',
            'value': kappa
        },
        'alpha': {
            'action': 'update',
            'value': alpha
        },
        'A': {
            'action': 'update',
            'value': A
        },
        'B': {
            'action': 'update',
            'value': B
        },
    }


"""Transition matrix for spring packets example.
"""

import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse import kron as skron
from scipy.special import comb

def transition_matrix(N, m, p):
    """Markov chain transition matrix for chained spring packets.

    Parameters
    ----------
    N: int
        Number of spring packets
    m: int
        Number of springs per packet.
    p: float
        Failure probability of each single spring.

    Returns
    -------
    T: ((m+1)**(N-1), (m+1)**(N-1)) csr_matrix
        Sparse matrix of transition probabilities.
    """
    # generate spring packet transition matrix
    T = np.eye(m+1)
    for i in range(m+1):
        for j in range(i):
            T[i,j] = comb(i, i-j) * p**(i-j)
            T[i,i] -= T[i,j]
    T = csr_matrix(T)

    # assemble scenario matrix:
    # entry [i,j]: number of springs in packet j of parameter realization i
    n_realizations = (m+1)**N
    o_to_m = np.arange(m+1)
    e = np.ones((1, m+1), dtype=int)
    n_springs = np.array([o_to_m])
    for i in range(N-1):
        n_springs = np.vstack((np.kron(n_springs, e),
            np.kron(np.repeat(e, (m+1)**i), o_to_m)))

    # assemble global transition matrix
    A = T
    for _ in range(N-1):
        A = skron(T, A)
    A = csr_matrix(A) # BSR (kron) -> CSR
    return A, n_springs


# -*- coding: utf-8 -*-
"""Copy of Forward-Backward-and-Central-Differentiation (1).ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1G7csGz574pv_wAA3j5TQgERYRnA56S_4

Before you turn this problem in, make sure everything runs as expected. First, **restart the kernel** (in the menubar, select Kernel$\rightarrow$Restart) and then **run all cells** (in the menubar, select Cell$\rightarrow$Run All).

Make sure you fill in any place that says `YOUR CODE HERE` or "YOUR ANSWER HERE", as well as your name and collaborators below:
"""

NAME = "Prabal CHowdhury"
COLLABORATORS = ""

"""---

# Differentiation: Forward, Backward, And Central
---

## Task 1: Differentiation

We have already learnt about *forward differentiation*, *backward diferentiation* and *central differentiation*. In this part of the assignment we will write methods to calculate this values and check how they perform.

The equations are as follows,

\begin{align}
\text{forward differentiation}, f^\prime(x) \simeq \frac{f(x+h)-f(x)}{h} \tag{4.6} \\
\text{backward differentiation}, f^\prime(x) \simeq \frac{f(x)-f(x-h)}{h} \tag{4.7} \\
\text{central differentiation}, f^\prime(x) \simeq \frac{f(x+h)-f(x-h)}{2h} \tag{4.8}
\end{align}

## Importing libraries
"""

import numpy as np
import matplotlib.pyplot as plt
from numpy.polynomial import Polynomial

"""Here, `forward_diff(f, h, x)`, `backward_diff(f, h, x)`, and `central_diff(f, h, x)` calculates the *forward differentiation*, *backward differentiation* and *central differentiation* respectively. finally the `error(f, f_prime, h, x)` method calculates the different values for various $h$ and returns the errors.

Later we will run some code to test out performance. The first one is done for you.
"""

def forward_diff(f, h, x):
    return (f(x+h) - f(x)) / h

def backward_diff(f, h, x):
    # --------------------------------------------
    # YOUR CODE HERE
    return (f(x-h) - f(x)) / h
    # --------------------------------------------

def central_diff(f, h, x):
    # --------------------------------------------
    # YOUR CODE HERE
    return (f(x+h) - f(x-h)) / (2*h)
    # --------------------------------------------

def error(f, f_prime, h, x):
    Y_correct = f_prime(x)
    f_error = np.array([])
    b_error = np.array([])
    c_error = np.array([])

    for h_i in h:
        # for different values of h (h_i)
        # calculate the error at the point x for  (i) forward method
        #                                         (ii) backward method
        #                                         (ii) central method
        # the first one is done for you
        f_error_h_i = forward_diff(f, h_i, x) - Y_correct
        f_error = np.append(f_error, f_error_h_i)

        b_error_h_i = backward_diff(f, h_i, x) - Y_correct
        b_error = np.append(b_error, b_error_h_i)

        c_error_h_i = central_diff(f, h_i, x) - Y_correct
        c_error = np.append(c_error, c_error_h_i)

    return f_error, b_error, c_error

"""## Plot1
Polynomial and Actual Derivative Function
"""

fig, ax = plt.subplots()
ax.axhline(y=0, color='k')

p = Polynomial([2.0, 1.0, -6.0, -2.0, 2.5, 1.0])
data = p.linspace(domain=[-2.4, 1.5])
ax.plot(data[0], data[1], label='Function')

p_prime = p.deriv(1)
data2 = p_prime.linspace(domain=[-2.4, 1.5])
ax.plot(data2[0], data2[1], label='Derivative')

ax.legend()

h = 1
fig, bx = plt.subplots()
bx.axhline(y=0, color='k')

x = np.linspace(-2.0, 1.3, 50, endpoint=True)
y = forward_diff(p, h, x)
bx.plot(x, y, label='Forward; h=1')
y = backward_diff(p, h, x)
bx.plot(x, y, label='Backward; h=1')
y = central_diff(p, h, x)
bx.plot(x, y, label='Central; h=1')

data2 = p_prime.linspace(domain=[-2.0, 1.3])
bx.plot(data2[0], data2[1], label='actual')

bx.legend()

h = 0.1
fig, bx = plt.subplots()
bx.axhline(y=0, color='k')

x = np.linspace(-2.2, 1.3, 50, endpoint=True)
y = forward_diff(p, h, x)
bx.plot(x, y, label='Forward; h=0.1')
y = backward_diff(p, h, x)
bx.plot(x, y, label='Backward; h=0.1')
y = central_diff(p, h, x)
bx.plot(x, y, label='Central; h=0.1')

data2 = p_prime.linspace(domain=[-2.2, 1.3])
bx.plot(data2[0], data2[1], label='actual')

bx.legend()

h = 0.01
fig, bx = plt.subplots()
bx.axhline(y=0, color='k')

x = np.linspace(-2.2, 1.3, 50, endpoint=True)
y = forward_diff(p, h, x)
bx.plot(x, y, label='Forward; h=0.01')
y = backward_diff(p, h, x)
bx.plot(x, y, label='Backward; h=0.01')
y = central_diff(p, h, x)
bx.plot(x, y, label='Central; h=0.01')

data2 = p_prime.linspace(domain=[-2.2, 1.3])
bx.plot(data2[0], data2[1], label='actual')

bx.legend()

fig, bx = plt.subplots()
bx.axhline(y=0, color='k')

h = np.array([1., 0.55, 0.3, .17, 0.1, 0.055, 0.03, 0.017, 0.01])
err = error(p, p_prime, h, 2.0)

bx.plot(h, err[0], label='Forward')
bx.plot(h, err[1], label='Backward')
bx.plot(h, err[2], label='Central')
bx.legend()


import numpy as np

#import numpy on your virtual environment

def add(matrix1, matrix2):
    matrix = [[0,0],[0,0]]

    for i in range(len(matrix1)):
        for j in range(len(matrix1[0])):
            matrix[i][j] = matrix1[i][j] + matrix2[i][j]
    return matrix

def add_python_way(matrix1, matrix2):
    return [[matrix1[i][j] + matrix2[i][j]  for j in range(len(matrix1[0]))] for i in range(len(matrix1))]

def print_array(array):
    for line in array:
        print('  '.join(map(str, line)))

def main():
    matrix1 = np.array([[1, -2],[-3, 4]])
    matrix2 = np.array([[2, -1],[0, -1]])

    print('matrix1:')
    print_array(matrix1)
    print()

    print('matrix2:')
    print_array(matrix2)
    print()

    print('matrix 1 + matrix2:')
    print_array(add(matrix1, matrix2))
    print()

    print('matrix 1 + matrix2 with pythonic list compreehension:')
    print_array(add_python_way(matrix1, matrix2))

if __name__ == "__main__":
    main()


import numpy as np
import pandas as pd

itr_limit = 1000
conv_crit = 0.0000001

def stop_cond(A, b, x):
    n = len(b)
    res = (A @ x - b)
    magnitude = (res @ res) ** 0.5

    if magnitude < conv_crit:
        return True
    else:
        return False

def GS(A, b, N=-1, x=None):
    n = len(b)
    itr = 0
    converged = False
    if x is None:
        x = np.zeros(n)

    while not converged and itr < itr_limit and itr != N:

        for i in range(n):
            dot = 0
            for j in range(n):
                if (i != j):
                    dot += A[i, j] * x[j]

            x[i] = (b[i] - dot) / A[i, i]

        itr += 1
        converged = stop_cond(A, b, x)

    if not converged:
        print("Iteration limit reached without convergence.")

    return x

Ni = 10
Nj = 10
dx = 0.1  # in m
dy = 0.1  # in m
K = 384.1  # Conductivity in W/mK
Su = 0  # Source in W/m^3
Sp = 0  # Source in W/(K.m^3)

Tw = 2500  # Temperatures in K
Ts = 400
Tn = 300
Te = 200
qw = 0  # Fluxes in W/m^2
qs = 0
qn = 0
qe = 0

def pad(x, i, j):
    if j < 0 or j >= x.shape[1]:
        return 0
    else:
        return x[i,j]

def diffusion_steady():
    n = Ni * Nj
    A = np.zeros((n,n))
    b = np.full(n, Su * dx * dy, dtype="double")

    for i in range(n):
        if i < Nj:
            A[i,i] += 2 * K * (dy / dx)
            b[i] += 2 * K * Tw * (dy / dx) + (qw * dy)
        else:
            A[i, i-Nj] -= K * dy / dx

        if i >= n - Nj:
            A[i,i] += 2 * K * (dy / dx)
            b[i] += 2 * K * Te * (dy / dx) + (qe * dy)
        else:
            A[i, i+Nj] -= K * dy / dx

        if i % Nj == 0:
            if i != 0 and i != (n-Nj):
                A[i,i] += 2 * K * (dx / dy)
                b[i] += 2 * K * Ts * (dx / dy) + (qs * dx)
        else:
            A[i, i-1] -= K * dx / dy

        if (i + 1) % Nj == 0:
            if i != (Nj-1) and i != (n-1):
                A[i,i] += 2 * K * (dx / dy)
                b[i] += 2 * K * Tn * (dx / dy) + (qn * dx)
        else:
            A[i, i+1] -= K * dx / dy

        A[i,i] -= pad(A,i,i-Nj) + pad(A,i,i-1) + pad(A,i,i+1) + pad(A,i,i+Nj) + Sp * dx * dy
    df1 = pd.DataFrame(A)
    df2 = pd.DataFrame(b)
    with pd.ExcelWriter('data.xlsx') as writer:
        df1.to_excel(writer, sheet_name='Sheet1')
        df2.to_excel(writer, sheet_name='Sheet2')
    return GS(A,b)

print(diffusion_steady())


import numpy as np

def get_cov_ellipsoid(cov, mu=np.zeros((3)), nstd=3):
    """
    Return the 3d points representing the covariance matrix
    cov centred at mu and scaled by the factor nstd.

    Plot on your favourite 3d axis.
    Example 1:  ax.plot_wireframe(X,Y,Z,alpha=0.1)
    Example 2:  ax.plot_surface(X,Y,Z,alpha=0.1)
    """
    assert cov.shape==(3,3)

    # Find and sort eigenvalues to correspond to the covariance matrix
    eigvals, eigvecs = np.linalg.eigh(cov)
    idx = np.sum(cov,axis=0).argsort()
    eigvals_temp = eigvals[idx]
    idx = eigvals_temp.argsort()
    eigvals = eigvals[idx]
    eigvecs = eigvecs[:,idx]

    # Set of all spherical angles to draw our ellipsoid
    n_points = 100
    theta = np.linspace(0, 2*np.pi, n_points)
    phi = np.linspace(0, np.pi, n_points)

    # Width, height and depth of ellipsoid
    rx, ry, rz = nstd * np.sqrt(eigvals)

    # Get the xyz points for plotting
    # Cartesian coordinates that correspond to the spherical angles:
    X = rx * np.outer(np.cos(theta), np.sin(phi))
    Y = ry * np.outer(np.sin(theta), np.sin(phi))
    Z = rz * np.outer(np.ones_like(theta), np.cos(phi))

    # Rotate ellipsoid for off axis alignment
    old_shape = X.shape
    # Flatten to vectorise rotation
    X,Y,Z = X.flatten(), Y.flatten(), Z.flatten()
    X,Y,Z = np.matmul(eigvecs, np.array([X,Y,Z]))
    X,Y,Z = X.reshape(old_shape), Y.reshape(old_shape), Z.reshape(old_shape)

    # Add in offsets for the mean
    X = X + mu[0]
    Y = Y + mu[1]
    Z = Z + mu[2]

    return X,Y,Z

#########################################################
## Testing some data
#########################################################
if __name__=='__main__':
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D

    # Setup the plot
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    plt.xlabel('x')
    plt.ylabel('y')
    # ax.set_aspect(1.0)
    # ax.set_xlim3d(-3,10)
    # ax.set_ylim3d(-3,10)
    # ax.set_zlim3d(-3,10)
    # Generate random 3d data points for 2 different offsets

    #########################################################
    ## Generate datasets s1 and s2
    #########################################################
    # s1
    mu1 = np.random.random((3)) * 5
    cov1 = np.array([            # Using an off-axis alignment for s1
        [2.5, 0.75, 0.175],
        [0.75, 0.70, 0.135],
        [0.175, 01.35, 0.43]
    ])
    s1 = np.random.multivariate_normal(mu1, cov1, (200))
    ax.scatter(s1[:,0],s1[:,1],s1[:,2], c='r')

    # s2
    mu2 = np.random.random((3)) * 5 + 4
    cov2 = np.diag((1,3,5))
    s2 = np.random.multivariate_normal(mu2, cov2, (200))
    ax.scatter(s2[:,0],s2[:,1],s2[:,2], c='b')

    #########################################################
    ## Process data and plot ellipsoid
    #########################################################
    nstd = 2    # 95% confidence interval
    # s1
    mu1_ = np.mean(s1, axis=0)
    cov1_ = np.cov(s1.T)
    X1,Y1,Z1 = get_cov_ellipsoid(cov1_, mu1_, nstd)
    ax.plot_wireframe(X1,Y1,Z1, color='r', alpha=0.1)

    # s2
    mu2_ = np.mean(s2, axis=0)
    cov2_ = np.cov(s2.T)
    X2,Y2,Z2 = get_cov_ellipsoid(cov2_, mu2_, nstd)
    ax.plot_wireframe(X2,Y2,Z2, color='b', alpha=0.1)

    plt.show()


import numpy as np

# Write a function that takes as input a list of numbers, and returns
# the list of values given by the softmax function.
def softmax(L):
    results = []
    sum = 0
    #Sum all exponentials
    for i in range(len(L)):
        sum = sum + np.exp(L[i])
    #Divide each exponential by the sum of all exponentials
    for i in range(len(L)):
        results.append(np.exp(L[i])/sum)
    return results

Documentation/Curs Neural Networks/Softmax function/softmax.py


import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import random
import math
import datetime

## GENETIC ALGORITHM

MUTATE_RATE = 0.1
#keep pop size a multiple of 4
POP_SIZE = 100
NUM_GENS = 100
CROSS_RATE = 0.5
GENOME_SIZE = 2
MUTATE_BOUND = [-2.5,2.5]
WINDOW = [0,50]
NUM_ELITES = 1

def get_fitness(genome):
#    return np.sum(genome)
#    return -3*genome[0]**8 + 7*genome[1]**7 - 1*genome[2]**6 + 4*genome[3]**5 - 3*genome[4]**4 + 11*genome[5]**3 - 5.5*genome[6]**2 + 2*genome[7] + genome[8]
#    return 3*math.exp(-((genome[0]+1)**2+(genome[1]+1)**2))
    x = genome[0]
    y = genome[1]
    return math.exp(-(math.cos(x/2)+math.cos(y/4))) + math.cos(x)

def select(genomes):
    random.shuffle(genomes)
    fittest = []
    for i in range(int(POP_SIZE/2)):
        p1 = genomes[i]
        p2 = genomes[2*i]
        if get_fitness(p1) >= get_fitness(p2):
            fittest.append(p1)
        else:
            fittest.append(p2)
    return fittest

def crossover(fittest):
    offspring = []
    elite = sorted(fittest.copy(), key=get_fitness, reverse=True)[0:NUM_ELITES]
    while len(offspring) < POP_SIZE:
        random.shuffle(fittest)
        females = fittest[0:int(len(fittest)/2)]
        males = fittest[int(len(fittest)/2):len(fittest)]
        for i in range(len(males)):
            mom = females[i]
            dad = males[i]
            if np.random.rand() < CROSS_RATE:
                child = []
                for i in range(len(dad)):
                    if np.random.rand() < 0.5:
                        child.append(dad[i])
                    else:
                        child.append(mom[i])
                offspring.append(child)
            else:
                #clone both parents
                offspring.append(dad)
                offspring.append(mom)

    return [*elite,*random.sample(offspring,POP_SIZE-NUM_ELITES)]

def mutate(offspring, gen_num):
    for child in offspring:
        for gene in range(GENOME_SIZE):
            if np.random.rand() < get_mutation_rate(gen_num) and (WINDOW[0] <= child[gene] + np.random.uniform(*MUTATE_BOUND) <= WINDOW[1]):
                child[gene] = child[gene] + np.random.uniform(*MUTATE_BOUND)

#initialize population all at starting point
genomes = POP_SIZE*[[12.5,25]]
#for i in range(POP_SIZE):
#    genomes.append(random.sample(range(0,10), GENOME_SIZE))
#    genomes.append([0 for _ in range(GENOME_SIZE)])

def get_mutation_rate(gen_num):
    #use 0.03 for ~80-120
    return MUTATE_RATE*math.exp(-(MUTATE_RATE/2)*gen_num)

rounds = []
for i in range(NUM_GENS):
    points = []
    top = select(genomes)
    offspring = crossover(top)
    mutate(offspring, i)
    genomes = offspring
    top = sorted(genomes.copy(), key=get_fitness, reverse=True)
    if i % 10 == 0:
        print(get_fitness(top[0]),top[0])
    for g in top:
        points.append((g[0], g[1]))
    rounds.append(points)

    #stop condition
#    if get_fitness(top[0]) > 8.3728:
#        break

def fun(x, y):
  return math.exp(-(math.cos(x/2)+math.cos(y/4))) + math.cos(x)

delta = 0.05
x = np.arange(0, 20, delta)
y = np.arange(0, 50, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)

norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max())
cmap = cm.PRGn

levels = np.arange(0, 10, 2)

filenames = []
for i,points in enumerate(rounds):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.contourf(X, Y, Z, levels,
                cmap=cm.get_cmap(cmap, len(levels)-1),
                norm=norm)
    ax.autoscale(False) # To avoid that the scatter changes limits

    ax.scatter(*zip(*points), color='red')
    #plt.show()
    if i % 2 == 0:
        file = 'plot'+ str(i) +'.png'
        fig.savefig(file)
        filenames.append(file)
    plt.close(fig)

import imageio
images = []
for filename in filenames:
    images.append(imageio.imread(filename))

st = datetime.datetime.now().strftime('%Y%m%d%H%M')
imageio.mimsave('climber_'+ st +'.gif', images)


import numpy as np
import scipy.linalg as linalg
import contextlib

def create_hilbert(n):
    A = np.zeros((n,n))
    for i in range(n):
        for j in range(n):
            A[i][j] = 1/(i+j+1)
    return A

def pivot_change(A,i,P):
    maxindex = i
    for j in range (i,len(A)):
        if (abs(A[j][i]) > abs(A[maxindex][i])):
            maxindex = j
    a = np.copy(A[i])
    A[i] = A[maxindex]
    A[maxindex] = a
    if (i != maxindex):
        P_ = np.zeros((len(A),len(A)))
        for fi in range(len(A)):
            for fj in range(len(A)):
                if (fi == fj):
                    P_[fi][fj] = 1
        P_[i][i] = 0
        P_[maxindex][maxindex] = 0
        P_[i][maxindex] = 1
        P_[maxindex][i] = 1
        P[:][:] = P_.dot(P)

def lu_decomposition (A) :
    P = np.zeros((len(A),len(A)))
    for i in range(len(A)):
        for j in range(len(A)):
            if (i == j) :
                P[i][j] = 1
    for i in range(len(A)):
        pivot_change(A,i,P)
        u_ii = A[i][i]
        for j in range(i+1,len(A)):
            A[j][i] = A[j][i] / u_ii
            for k in range(i+1,len(A)):
                A[j][k] = - A[j][i] * A[i][k] + A[j][k]
    L = np.zeros((len(A),len(A)))
    for i in range(len(A)):
        for j in range(len(A)):
            if (i == j) :
                L[i][j] = 1
            elif (i > j):
                L[i][j] = A[i][j]
    U = np.zeros((len(A),len(A)))
    for i in range(len(A)):
        for j in range(len(A)):
            if (i == j) :
                U[i][j] = A[i][j]
            elif (i < j) :
                U[i][j] = A[i][j]
    return (L, U, P)

if __name__ == "__main__" :
    A = create_hilbert(6)
    L,U,P = lu_decomposition(A)
    np.set_printoptions(formatter={'float': '{: 0.5f}'.format})
    print("L = ")
    print(L)
    print("U = ")
    print(U)
    print("P = ")
    print(P)
    print("PA = ")
    print(P.dot(create_hilbert(6)))
    print("LU = ")
    print(L.dot(U))


"""
Tests for metrics
"""

import math
import numpy as np
from pyai.metrics import euclidean_distance, mean_squared_error

def test_mse():
    """Test Mean Squared Error."""

    y_true = np.array([3, -0.5, 2, 7])
    y_pred = np.array([2.5, 0.0, 2, 8])

    error = mean_squared_error(y_true, y_pred)
    assert math.isclose(error, (0.25 + 0.25 + 1) / 4)

    y_true = np.array([[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1]])
    y_pred = np.array([[0, 0, 0, 1], [1, 0, 1, 1], [0, 0, 0, 1]])

    error = mean_squared_error(y_true, y_pred)
    assert math.isclose(error, (1.0 / 3 + 2.0 / 3 + 2.0 / 3) / 4)

def test_euclidean_distance():
    """Test euclidean distance."""

    a = np.array([1, 1.5])
    b = np.array([3, 3])

    dist = euclidean_distance(a, b)
    assert math.isclose(dist, np.linalg.norm(a - b))

    a = np.random.rand(3, 3)
    b = np.random.rand(3, 3)

    dist = euclidean_distance(a, b)
    assert math.isclose(dist, np.linalg.norm(a - b))


import numpy as np

def simplex_coordinates1 ( m ):

#*****************************************************************************80
#
## SIMPLEX_COORDINATES1 computes the Cartesian coordinates of simplex vertices.
#
#  Discussion:
#
#    The simplex will have its centroid at 0
#
#    The sum of the vertices will be zero.
#
#    The distance of each vertex from the origin will be 1.
#
#    The length of each edge will be constant.
#
#    The dot product of the vectors defining any two vertices will be - 1 / M.
#    This also means the angle subtended by the vectors from the origin
#    to any two distinct vertices will be arccos ( - 1 / M ).
#
#  Licensing:
#
#    This code is distributed under the GNU LGPL license.
#
#  Modified:
#
#    28 June 2015
#
#  Author:
#
#    John Burkardt
#
#  Parameters:
#
#    Input, integer M, the spatial dimension.
#
#    Output, real X(M,M+1), the coordinates of the vertices
#    of a simplex in M dimensions.
#

  x = np.zeros ( [ m, m + 1 ] )

  for k in range ( 0, m ):
#
#  Set X(K,K) so that sum ( X(1:K,K)^2 ) = 1.
#
    s = 0.0
    for i in range ( 0, k ):
      s = s + x[i,k] ** 2

    x[k,k] = np.sqrt ( 1.0 - s )
#
#  Set X(K,J) for J = K+1 to M+1 by using the fact that XK dot XJ = - 1 / M.
#
    for j in range ( k + 1, m + 1 ):
      s = 0.0
      for i in range ( 0, k ):
        s = s + x[i,k] * x[i,j]

      x[k,j] = ( - 1.0 / float ( m ) - s ) / x[k,k]

  return x


"""
Inferring two binomial proportions using PyMC.
"""
from __future__ import division
import matplotlib.pyplot as plt
plt.style.use('seaborn-darkgrid')
import numpy as np
import pymc3 as pm

# Generate the data
y1 = np.array([1, 1, 1, 1, 1, 0, 0])  # 5 heads and 2 tails
y2 = np.array([1, 1, 0, 0, 0, 0, 0])  # 2 heads and 5 tails

with pm.Model() as model:
    # define the prior
    theta1 = pm.Beta('theta1', 3, 3)  # prior
    theta2 = pm.Beta('theta2', 3, 3)  # prior
    # define the likelihood
    y1 = pm.Bernoulli('y1', p=theta1, observed=y1)
    y2 = pm.Bernoulli('y2', p=theta2, observed=y2)

    # Generate a MCMC chain
    trace = pm.sample(1000)

# create an array with the posterior sample
theta1_sample = trace['theta1']
theta2_sample = trace['theta2']

# Plot the trajectory of the last 500 sampled values.
plt.plot(theta1_sample[:-500], theta2_sample[:-500], marker='o',  color='skyblue')
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.xlabel(r'$\theta1$')
plt.ylabel(r'$\theta2$')

# Display means in plot.
plt.plot(0, label='M = %.3f, %.3f' % (np.mean(theta1_sample), np.mean(theta2_sample)), alpha=0.0)

plt.legend(loc='upper left')
plt.savefig('Figure_8.6.png')

# Plot a histogram of the posterior differences of theta values.
theta_diff = theta1_sample - theta2_sample
pm.plot_posterior(theta_diff, ref_val=0.0, bins=30, color='skyblue')
plt.xlabel(r'$\theta_1 - \theta_2$')
plt.savefig('Figure_8.8.png')

# For Exercise 8.5:
# Posterior prediction. For each step in the chain, use the posterior thetas
# to flip the coins.
chain_len = len(theta1_sample)
# Create matrix to hold results of simulated flips:
y_pred = np.zeros((2, chain_len))
for step_idx in range(chain_len):  # step through the chain
    # flip the first coin:
    p_head1 = theta1_sample[step_idx]
    y_pred[0, step_idx] = np.random.choice([0,1], p=[1-p_head1, p_head1])
    # flip the second coin:
    p_head2 = theta2_sample[step_idx]
    y_pred[1, step_idx] = np.random.choice([0,1], p=[1-p_head2, p_head2])

# Now determine the proportion of times that y1==1 and y2==0
pY1eq1andY2eq0 = sum((y_pred[0] ==1) & (y_pred[1] == 0)) / chain_len

print(pY1eq1andY2eq0)
plt.show()


#------------------------------------------#
# Using numpy arrays, here are some ways   #
# to inizialize different types of array   #
# in different ways                        #
#------------------------------------------#

import numpy as np

# All 0 matrix
a = np.zeros((5, 5))
print(a)

# All 1 matrix
b = np.ones((2,4, 3), dtype="int16")
print(b)

# Any other number or symbol
c = np.full((2, 4), 9)
print(c)

# Create an array from the syntax of another array
d = np.full_like(c, 3)
print(d)

# Random decimal numbers
e = np.random.rand(2, 4, 3)
print(e)
#With shape from another array
f = np.random.random_sample(d.shape)
print(f)

# Random Integer values
g = np.random.randint(4, size=(3, 4))
print(g)

# Identity matrix (Square matrix)
h = np.identity(5)
print(h)

# Repeat an array
i = np.array([[1,2,3]])
i = np.repeat(i, 3, axis=0)
print(i)


#!/usr/bin/env python

import numpy as np

def normalizeRows(x):
    """ Row normalization function

    Implement a function that normalizes each row of a matrix to have
    unit length.
    """
    N = x.shape[0]
    x /= np.sqrt(np.sum(x**2, axis=1)).reshape((N,1)) + 1e-30
    return x

def softmax(x):
    """Compute the softmax function for each row of the input x.
    It is crucial that this function is optimized for speed because
    it will be used frequently in later code.

    Arguments:
    x -- A D dimensional vector or N x D dimensional numpy matrix.
    Return:
    x -- You are allowed to modify x in-place
    """
    orig_shape = x.shape

    if len(x.shape) > 1:
        # Matrix
        tmp = np.max(x, axis=1)
        x -= tmp.reshape((x.shape[0], 1))
        x = np.exp(x)
        tmp = np.sum(x, axis=1)
        x /= tmp.reshape((x.shape[0], 1))
    else:
        # Vector
        tmp = np.max(x)
        x -= tmp
        x = np.exp(x)
        tmp = np.sum(x)
        x /= tmp

    assert x.shape == orig_shape
    return x


import numpy as np
import random
import time
import os
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

def fib(n):
    if n == 1 or n == 2:
        result = 1
    else:
        result = fib(n-1) + fib(n-2)
    return result

def fib_2(n, memo):
    if memo[n] is not None:
        return memo[n]
    if n == 1 or n == 2:
        result = 1
    else:
        result = fib_2(n-1, memo) + fib_2(n-2, memo)
    memo[n] = result
    return result

def fib_memo(n):
    memo = [None] * (n + 1)
    return fib_2(n, memo)

def fib_bottom_up(n):
    if n == 1 or n == 2:
        return 1
    bottom_up = [None] * (n+1)
    bottom_up[1] = 1
    bottom_up[2] = 1
    for i in range(3, n+1):
        bottom_up[i] = bottom_up[i-1] + bottom_up[i-2]
    return bottom_up[n]

a = 10
b = 20
c = 30
d = 40

fib_arr = []
fib_memo_arr = []
fib_bottom_up_arr = []

start_time = time.time()
fib(a)
fib_arr.append(time.time() - start_time)

start_time = time.time()
fib(b)
fib_arr.append(time.time() - start_time)

start_time = time.time()
fib(c)
fib_arr.append(time.time() - start_time)

start_time = time.time()
fib(d)
fib_arr.append(time.time() - start_time)

start_time = time.time()
fib_memo(a)
fib_memo_arr.append(time.time() - start_time)

start_time = time.time()
fib_memo(b)
fib_memo_arr.append(time.time() - start_time)

start_time = time.time()
fib_memo(c)
fib_memo_arr.append(time.time() - start_time)

start_time = time.time()
fib_memo(d)
fib_memo_arr.append(time.time() - start_time)

start_time = time.time()
fib_bottom_up(a)
fib_bottom_up_arr.append(time.time() - start_time)

start_time = time.time()
fib_bottom_up(b)
fib_bottom_up_arr.append(time.time() - start_time)

start_time = time.time()
fib_bottom_up(c)
fib_bottom_up_arr.append(time.time() - start_time)

start_time = time.time()
fib_bottom_up(d)
fib_bottom_up_arr.append(time.time() - start_time)

print(fib_arr)
print(fib_memo_arr)
print(fib_bottom_up_arr)

y = [10, 20, 30, 40]
plt.plot(fib_arr, y, color='red')
plt.plot(fib_memo_arr, y, color='blue')
plt.plot(fib_bottom_up_arr, y, color='green')
plt.title("Comparação Tempo de Fibonacci")
red_patch = mpatches.Patch(color='red', label='Naive')
blue_patch = mpatches.Patch(color='blue', label='Memo')
green_patch = mpatches.Patch(color='green', label='Bottom Up')
plt.legend(handles=[red_patch, blue_patch, green_patch])
plt.xlabel('Tempo(s)')
plt.ylabel('Tamanho do vetor')
plt.grid(color='r', linestyle='-', linewidth=0.2)
plt.show()


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

import numpy as np
a = np.array([1, 2, 3, 4])
b = np.array([4, 2, 2, 4])
print(a == b)
print(a > b)
a = np.array([1, 2, 3, 4])
b = np.array([4, 2, 2, 4])
c = np.array([1, 2, 3, 4])
np.array_equal(a, b)
np.array_equal(a, c)
a = np.array([1, 1, 0, 0], dtype=bool)
b = np.array([1, 0, 1, 0], dtype=bool)
print(np.logical_or(a, b))
print(np.logical_and(a, b))
a = np.arange(5)
print(np.sin(a))
print(np.log(a))
print(np.exp(a))
a = np.arange(4)
try:
    a + np.array([1, 2])
except ValueError as ve:
    print(ve)
# see help(np.triu)
a = np.triu(np.ones((3, 3)), 1)
print(a)
print(a.T)
a = np.arange(9).reshape(3, 3)
a.T[0, 2] = 999
print(a.T)
print(a)
print(np.allclose([1e10,1e-7],
                  [1.00001e10,1e-8]))
print(np.allclose([1e10,1e-8],
                  [1.00001e10,1e-9]))
print(np.allclose([1e10,1e-8],
                  [1.0001e10,1e-9]))
print(np.allclose([1.0, np.nan],
                  [1.0, np.nan]))
print(np.allclose([1.0, np.nan],
                  [1.0, np.nan],
                  equal_nan=True))


import numpy as np
import tqdm
import sklearn.metrics
import warnings
import pdb
from p_tqdm import p_umap

RESAMPLE_FAILED_WARNING = "Resampling distrubution for estimator {} failed because of : {}"
CONFIDENCE_INTERVAL_EXCEPTION = "Cannot calculate confidence interval. Sampled {} times for found {} (target {}) valid samples."

def confidence_interval(emperical_distribution, confidence_interval=0.95, num_resamples=5000, estimator=np.mean, clusters=None):
    '''
    Estimates confidence interval of the mean of emperical_distribution
    using emperical bootstrap. Method details are available at:

    https://ocw.mit.edu/courses/mathematics/18-05-introduction-to-probability-and-statistics-spring-2014/readings/MIT18_05S14_Reading24.pdf

    -emperical_distribution: Emperical distributions for which we want to
    estimate the confidence interval of the mean
    -confidence_interval: Amount of probability mass interval should cover.
    -num_resamples: Amount of trails to use to estimate confidence interval.
    Should as large as computationally feasiable.

    if Clusters is not none, perform two stage bootstrap.
    '''
    emp_mean = estimator(emperical_distribution)
    num_samples = len(emperical_distribution)
    deltas = []
    num_tries = 0
    if clusters is not None:
        cluster_to_inds = {}
        for ind, cluster in enumerate(clusters):
            if cluster not in cluster_to_inds:
                cluster_to_inds[cluster] = []
            cluster_to_inds[cluster].append(ind)
        num_clusters = len(cluster_to_inds)
        cluster_ids = list(cluster_to_inds.keys())

    def get_delta(dummy_ind):
        if clusters is None:
            resample_ind = np.random.choice(a=range(len(emperical_distribution)),
                                    size=num_samples,
                                    replace=True
                                    )
        else:
            resample_clusters = np.random.choice(a=cluster_ids,
                                                size=num_clusters,
                                                replace=True)
            resample_ind = []
            for cluster in resample_clusters:
                resampled_inds_for_cluster = np.random.choice(a=cluster_to_inds[cluster],
                                                            size=len(cluster_to_inds[cluster]),
                                                            replace=True)
                resample_ind.extend(resampled_inds_for_cluster)

        resample_dist = [ emperical_distribution[ind] for ind in resample_ind]
        delta = estimator(resample_dist) - emp_mean
        return delta

    deltas = p_umap(get_delta, range(num_resamples))
    lower_bound, upper_bound = get_ci_bounds(emp_mean, deltas, confidence_interval, num_resamples)

    return emp_mean, (lower_bound, upper_bound)

def get_ci_bounds(mean, deltas, confidence_interval, num_resamples):
    '''
    Returns the lower and upper bounds of the confidence interval.
    mean : the empirical mean
    deltas : deltas from the empirical mean from different samples
    '''
    deltas = np.sort(deltas)
    index_offset = int((1 - confidence_interval)/2. * num_resamples)
    lower_delta, upper_delta = deltas[index_offset], deltas[-index_offset]
    lower_bound, upper_bound = mean - upper_delta, mean - lower_delta
    return lower_bound, upper_bound


## module gaussQuad2
''' I = gaussQuad2(f,xc,yc,m).
    Gauss-Legendre integration of f(x,y) over a
    quadrilateral using integration order m.
    {xc},{yc} are the corner coordinates of the quadrilateral.
'''
from gaussNodes import *
import numpy as np
def gaussQuad2(f,x,y,m):

    def jac(x,y,s,t):
        J = np.zeros((2,2))
        J[0,0] = -(1.0 - t)*x[0] + (1.0 - t)*x[1]  \
                + (1.0 + t)*x[2] - (1.0 + t)*x[3]
        J[0,1] = -(1.0 - t)*y[0] + (1.0 - t)*y[1]  \
                + (1.0 + t)*y[2] - (1.0 + t)*y[3]
        J[1,0] = -(1.0 - s)*x[0] - (1.0 + s)*x[1]  \
                + (1.0 + s)*x[2] + (1.0 - s)*x[3]
        J[1,1] = -(1.0 - s)*y[0] - (1.0 + s)*y[1]  \
                + (1.0 + s)*y[2] + (1.0 - s)*y[3]
        return (J[0,0]*J[1,1] - J[0,1]*J[1,0])/16.0

    def map(x,y,s,t):
        N = np.zeros(4)
        N[0] = (1.0 - s)*(1.0 - t)/4.0
        N[1] = (1.0 + s)*(1.0 - t)/4.0
        N[2] = (1.0 + s)*(1.0 + t)/4.0
        N[3] = (1.0 - s)*(1.0 + t)/4.0
        xCoord = np.dot(N,x)
        yCoord = np.dot(N,y)
        return xCoord,yCoord

    s,A = gaussNodes(m)
    sum = 0.0
    for i in range(m):
        for j in range(m):
            xCoord,yCoord = map(x,y,s[i],s[j])
            sum = sum + A[i]*A[j]*jac(x,y,s[i],s[j])  \
                       *f(xCoord,yCoord)
    return sum


import numpy as np

def poisson(lam, k):
    """
    Possion Function (lambda^k)*(e^-lambda)/(k!)
    :param lam:
    :param k:
    :return:
    """
    # The exponential part, pretty much a direct translation from eqn. to code
    exponent_part = np.float64(np.exp(-1. * lam))
    first_part = np.float64(lam ** k)
    # Factorial part....
    fact = np.float64(1)
    for i in range(1, np.int64(k + 1)):
        fact *= i

    final_result = np.float64((first_part * exponent_part) / fact)
    return final_result

import sys
sys.stdout = open('1a.txt', 'w')
print("The Poisson value for $\lambda$ = {} and k = {} is: {} ".format(1, 0, poisson(1, 0)))
print("The Poisson value for $\lambda$ = {} and k = {} is: {} ".format(5, 10, poisson(5, 10)))
print("The Poisson value for $\lambda$ = {} and k = {} is: {} ".format(3, 21, poisson(2.6, 40)))


import math
import pandas as pd
import numpy as np
import sys

class MultiLayerPerceptron:
    def __init__(self, batch_size, learning_rate, num_epochs):
        self.batch_size = batch_size
        self.learning_rate = learning_rate
        self.num_epochs = num_epochs

        # Layer 1: weight, bias
        # w: 512 * (28*28)
        # x: (28*28) * 1
        # b: 512 * 1
        # r: 512 * 1
        # Layer 2: weight, bias
        # w: 256 * 512
        # x: 512 * 1
        # b: 256 * 1
        # r: 256 * 1
        # Layer 3: weight, bias
        # w: 10 * 256
        # x: 256 * 1
        # b: 10 * 1
        # r: 10 * 1
        self.params = {
            'W1': np.random.randn(512, 784) * np.sqrt(1.0 / 784),
            'b1': np.random.randn(512, 1) * np.sqrt(1.0 / 748),
            'W2': np.random.randn(256, 512) * np.sqrt(1.0 / 512),
            'b2': np.random.randn(256, 1) * np.sqrt(1.0 / 512),
            'W3': np.random.randn(10, 256) * np.sqrt(1.0 / 256),
            'b3': np.random.randn(10, 1) * np.sqrt(1.0 / 256)
        }

    def start(self):

        if len(sys.argv) > 3:
            trainX = pd.read_csv(sys.argv[1], header=None)
            trainY = pd.read_csv(sys.argv[2], header=None)
            testX = pd.read_csv(sys.argv[3], header=None)
        else:
            trainX = pd.read_csv('train_image.csv', header=None)
            trainY = pd.read_csv('train_label.csv', header=None)
            testX = pd.read_csv('test_image.csv', header=None)

        # Transpose
        trainX = trainX.T
        trainY = trainY.T
        testX = testX.T

        # pd to np
        trainX = trainX.values
        trainY = trainY.values
        testX = testX.values

        # categorical encoding
        onehotY = np.zeros((trainY.size, 10))
        onehotY[np.arange(trainY.size), trainY] = 1
        onehotY = onehotY.T

        return trainX, onehotY, testX

    # e^x/1+e^x
    def sigmoid(self, x):
        x = np.clip(x, -500, 500)
        return 1.0 / (1.0 + np.exp(-x))

    # v * s * 1 - s
    def sigmoid_backward(self, dA, Z):
        sig = self.sigmoid(Z)
        return dA * sig * (1 - sig)

    # 10 labels
    def softmax(self, x):
        exps = np.exp(x - x.max())
        return exps / np.sum(exps, axis=0)

    def get_mini_batches(self, X, y, batch_size):
        n = X.shape[1]
        mini_batches = list()

        for i in range(0, math.floor(n / batch_size)):
            mx = X[:, i * batch_size: (i + 1) * batch_size]
            my = y[:, i * batch_size: (i + 1) * batch_size]
            mini_batch = (mx, my)
            mini_batches.append(mini_batch)

        if n % batch_size != 0:
            mx = X[:, batch_size * math.floor(n / batch_size): n]
            my = y[:, batch_size * math.floor(n / batch_size): n]
            mini_batch = (mx, my)
            mini_batches.append(mini_batch)

        return mini_batches

    # sigmoid(w*x + b)
    # last layer: softmax(w*x + b)
    def forward_pass(self, X):
        cache = dict()

        cache['Z1'] = np.dot(self.params['W1'], X) + self.params['b1']
        cache['A1'] = self.sigmoid(cache['Z1'])
        cache['Z2'] = np.dot(self.params['W2'], cache['A1']) + self.params['b2']
        cache['A2'] = self.sigmoid(cache['Z2'])
        cache['Z3'] = np.dot(self.params['W3'], cache['A2']) + self.params['b3']
        cache['A3'] = self.softmax(cache['Z3'])

        return cache

    # backward pass
    def backward_pass(self, X, Y, cache):
        m = X.shape[1]

        dZ3 = cache['A3'] - Y

        dW3 = np.dot(dZ3, cache["A2"].T) / m
        db3 = np.sum(dZ3, axis=1, keepdims=True) / m

        dA2 = np.dot(self.params['W3'].T, dZ3)
        dZ2 = self.sigmoid_backward(dA2, cache['Z2'])

        dW2 = np.dot(dZ2, cache['A1'].T) / m
        db2 = np.sum(dZ2, axis=1, keepdims=True) / m

        dA1 = np.dot(self.params['W2'].T, dZ2)
        dZ1 = self.sigmoid_backward(dA1, cache['Z1'])

        dW1 = np.dot(dZ1, X.T) / m
        db1 = np.sum(dZ1, axis=1, keepdims=True) / m

        return {'dW3': dW3, 'db3': db3, 'dW2': dW2, 'db2': db2, 'dW1': dW1, 'db1': db1}

    def train(self, X, y):
        # i = 0
        for i in range(0, self.num_epochs):
            # j = 0
            random = np.arange(len(X[1]))
            np.random.shuffle(random)
            X_shuffle = X[:, random]
            y_shuffle = y[:, random]

            mini_batches = self.get_mini_batches(X_shuffle, y_shuffle, self.batch_size)

            for mini_batch in mini_batches:
                mx, my = mini_batch
                cache = self.forward_pass(mx)
                grads = self.backward_pass(mx, my, cache)

                self.params['W1'] = self.params['W1'] - (self.learning_rate * grads['dW1'])
                self.params['b1'] = self.params['b1'] - (self.learning_rate * grads['db1'])
                self.params['W2'] = self.params['W2'] - (self.learning_rate * grads['dW2'])
                self.params['b2'] = self.params['b2'] - (self.learning_rate * grads['db2'])
                self.params['W3'] = self.params['W3'] - (self.learning_rate * grads['dW3'])
                self.params['b3'] = self.params['b3'] - (self.learning_rate * grads['db3'])
                # print('Over {},{}'.format(i, j))
                # j += 1
            # i += 1

# network
nn = MultiLayerPerceptron(batch_size=32, learning_rate=0.01, num_epochs=80)
trainX, onehotY, testX = nn.start()
nn.train(trainX, onehotY)
out = nn.forward_pass(testX)
output = out['A3']
pred = np.argmax(output, axis=0)
pd.DataFrame(pred).to_csv('test_predictions.csv', header=None, index=None)


"""
Methods for fitting a beta binomial model, to be used for skills measurement and
A/B test analysis.
"""
import numpy as np
import scipy.stats as scs

def beta_values(alpha, beta, numpoints=1000):
    domain = np.linspace(0, 1, numpoints)
    dist = scs.beta.pdf(domain, alpha, beta)
    return (domain, dist,)

def fit_beta_prior(data):
    """
    Fit the parameters of a beta prior given probability data.
    Arguments:
        data (numpy array):
            Values ranging 0 to 1, representing the population level data.

    Returns:
        List of beta distribution parameters
    """
    alpha, beta, _, _ = scs.beta.fit(data, floc=0, fscale=1)
    return (alpha, beta,)

def beta_posterior_values(successes, trials, alpha, beta, numpoints=1000):
    failures = trials - successes
    return beta_values(alpha + successes, beta + failures, numpoints)

def beta_expected_value(alpha, beta):
    return alpha / (alpha + beta)

def compare_beta_distros(alpha_sa, beta_sa, alpha_sb, beta_sb):
    """
    Evaluate the probability that a random sample of the beta distribution from
    sample a is greater than a random sample from the beta distribution of sample b.
    This gives a good measure of whether sample a is *better* than sample b.
    """
    sa_samples = np.random.beta(alpha_sa, beta_sa, 100000)
    sb_samples = np.random.beta(alpha_sb, beta_sb, 100000)
    return np.sum(sa_samples > sb_samples) / len(sa_samples)


# data is from:
# http://college.cengage.com/mathematics/brase/understandable_statistics/7e/students/datasets/mlr/frames/mlr02.html

# The data (X1, X2, X3) are for each patient.
# X1 = systolic blood pressure
# X2 = age in years
# X3 = weight in pounds

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

df = pd.read_excel('./data/mlr02.xls')
df['ones'] = 1
X = df[['X2', 'X3', 'ones']]
Y = df['X1']

plt.scatter(X['X2'], Y)
plt.show()

plt.scatter(X['X3'], Y)
plt.show()

X2_only = df[['X2', 'ones']]
X3_only = df[['X3', 'ones']]
Exclude_noise = df[['X2', 'X3']]

def get_r2(x, y):
    w = np.linalg.solve(x.T.dot(x), x.T.dot(y))
    y_hat = x.dot(w)

    d1 = y - y_hat
    d2 = y - y.mean()
    r2 = 1 - d1.dot(d1) / d2.dot(d2)
    return r2

print("R2 for X2 only", get_r2(X2_only, Y))
print("R2 for X3 only", get_r2(X3_only, Y))
print("R2 for both", get_r2(X, Y))
print("R2 for both exclude noise", get_r2(Exclude_noise, Y))


import numpy as np
import multiplyingMartices as mm
def inverse2x2(matrix1):
    diff = (matrix1[1][1] * matrix1[0][0])-  (matrix1[1][0] * matrix1[0][1])
    final =  np.array([
        [matrix1[1][1]/diff, (-1 * matrix1[0][1]) / diff],
        [(-1 * matrix1[1][0]) / diff, (1 * matrix1[0][0]) / diff],
    ])

    result = mm.martixMultiply(matrix1,final)

    id = np.array([[1,0],[0,1]])
    if(np.array_equal(id, result)):
        return final
    else:
        return False


from math import sqrt
import numpy as np

'''
Gradient Descent Algorithm for Linear Regression
    feature_matrix: matrix whose rows are samples and columns are features
    output: vector containing actual value
    initial_weights: vector containing intial weight values
    step_size: scalar value for step size
    tolerance: scalar value to test convergence
'''
def regression_gradient_descent(feature_matrix, output, initial_weights, step_size, tolerance):
    converged = False
    weights = np.array(initial_weights)

    while not converged:
        predictions = predict_output(feature_matrix, weights)
        errors = predictions - output
        gradient_sum_squares = 0

        # update each feature's weight
        for i in range(len(weights)):
            derivative = feature_derivative(errors, feature_matrix[:,i])
            gradient_sum_squares += derivative * derivative
            weights[i] = weights[i] - step_size * derivative

        gradient_magnitude = sqrt(gradient_sum_squares)

        if gradient_magnitude < tolerance:
            converged = True

    return(weights)

def feature_derivative(errors, feature):
    derivative = 2 * np.dot(errors, feature)
    return(derivative)

def predict_output(feature_matrix, weights):
    predictions = np.dot(feature_matrix, weights)
    return(predictions)


'''
This script provides simple examples to data visualization using Python Matplotlib library.
For more details, see: https://matplotlib.org/
'''

import matplotlib.pyplot as plt
import numpy as npy

## Createa simple figure ##

x = npy.linspace(0, 2*npy.pi, 1000)
y1 = npy.sin(x)

plt.plot(x,y1)

# We can set title, xlabel, and ylabel simply as follows:
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Figure of f(x) = sin(x)')
plt.show()

# ---------------------------------------

# Curve color and type can be edited:
y2 = npy.cos(x)
plt.figure(2) # To create a new figure
plt.plot(x,y2, '--m')

# We can set title, xlabel, and ylabel simply as follows:
plt.xlabel('x')
plt.ylabel('cos(x)')
plt.title('Figure of f(x) = cos(x)')
plt.show()
# ---------------------------------------

## Creating multi-panel(subplot) figures ##
plt.figure(3)
plt.subplot(1,2,1)
plt.plot(x,y1)
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.title('Figure of f(x) = sin(x)')

plt.subplot(1,2,2)
plt.xlabel('x')
plt.ylabel('cos(x)')
plt.title('Figure of f(x) = cos(x)')
plt.plot(x,y2, '--m')

plt.show()
# ---------------------------------------

## Creating figures using figure objects ##

# Using figure objects, we have more freedom on the figures and we can create more personal figures.

fig = plt.figure(4) # This is an empty panel.

# We need to add axes to the panel on which the curve will be plotted.
ax = fig.add_axes([0.1, 0.1, 0.75, 0.75]) # The list that we pass specifies the size of the axes in the format: [left, bottom, width, height] in range 0 to 1.

y3 = x ** 2
ax.plot(x,y3,'-.r')

# The title, xlabel and ylabel can be added as follows:
ax.set_xlabel('x')
ax.set_ylabel('y = $x^2$')
ax.set_title('Figure of f(x) = $x^2$')
plt.show()
# ---------------------------------------

# More complex example:

# The figure size can be edited when creating figure object as well:
fig = plt.figure(5, figsize = (12, 6))

ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax2 = fig.add_axes([0.2, 0.5, 0.3, 0.3])
ax3 = fig.add_axes([0.65, 0.2, 0.2, 0.2])

ax1.plot(x,y3, '-.r')
ax1.set_xlabel('x')
ax1.set_ylabel('y = $x^2$')
ax1.set_title('Multi-panel custom figure')

ax2.plot(x,y1)
ax2.set_xlabel('x')
ax2.set_ylabel('sin(x)')
ax2.set_title('Figure of f(x) = sin(x)')

ax3.plot(x,y2, '--m', linewidth = 2)
ax3.set_xlabel('x')
ax3.set_ylabel('cos(x)')
ax3.set_title('Figure of f(x) = cos(x)')

plt.show()

# ---------------------------------------

## Multiple curves can be plotted on the same axes ##

fig = plt.figure(6, figsize = (8,6))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax.plot(x, x, 'y')
ax.plot(x, x**(1/2), 'b')
ax.plot(x, x**(1/3), 'r')
ax.plot(x, x**(1/4), 'k')
ax.plot(x, x**(1/5), 'g')
ax.legend(['y = $x$', 'y = $x^{1/2}$', 'y = $x^{1/3}$', 'y = $x^{1/4}$', 'y = $x^{1/5}$']) # adds legend for the curves
ax.set_title('Multiple Curves')
ax.set_xlabel('x')
ax.set_ylabel('f(x)')

# The location of the legends can be specified using .legend(loc = 1) for northeast of the panel
# loc = 2 --> for northwest of the panel
# loc = 3 --> for southeast of the panel
# loc = 4 --> for southwest of the panel

plt.show()

# ---------------------------------------

## Matplotlib.pyplot has built-in special plot functions ##
randData = npy.random.randn(1000)

# Histogram Plot #
plt.figure(7)
plt.hist(randData, bins = 50)

plt.title('Histogram Plot')
plt.show()

# Scatter Plot #
randData = npy.random.randn(2,1000)

plt.figure(8)
plt.scatter(x = randData[0,:], y = randData[1,:])

plt.title('Scatter Plot')
plt.show()

# Box Plot #

plt.figure(9)
plt.boxplot(randData.transpose())

plt.title('Box Plot')
plt.show()

# Violin Plot #

plt.figure(10)
plt.violinplot(randData.transpose())

plt.title('Violin Plot')
plt.show()

# For more examples for other plot types, see: https://matplotlib.org/gallery/index.html


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

import numpy as np
from matplotlib.pyplot import *

def value_iteration(P, r, gamma = 0., policy = None, iter_no = None):
    '''Value iteration for finite state and action spaces'''

    actions_no, states_no = np.shape(P)[0:2]

    # A simple heuristic to set the number of iterations
    iter_no = min(1000, int(20./(1-gamma)))

    Q = np.zeros((states_no,actions_no))
    Q_new = Q

    if policy is None:
        Bellman_optimality = True
    else:
        Bellman_optimality = False

    for m in range(iter_no):
        Q = Q_new
        for s in range(states_no):
            for a in range(actions_no):

                value_next = 0

                # This loop could be optimized using numpy
                for s_next in range(states_no):
                    if Bellman_optimality:
                        value_next += P[a,s,s_next] * max(Q[s_next]) #
                    else:
                        value_next += P[a,s,s_next] * Q[s_next,policy[s_next]]

                Q_new[s,a] = r[s,a] + gamma* value_next

    return Q

P = np.array( [ [[0, 1.], [0., 1.0]],
     [ [1.,0 ], [1.0,0.0 ] ] ] )

r = np.array( [ [-1., 1.0], [0.0, 5.]] )
gamma = 0.9

pol_1 = np.array([0, 0]) # Saving policy
pol_2 = np.array([1,1]) # Spending policy

Q = value_iteration(P,r, gamma) #, policy=pol_2)
print 'Q:', Q
print 'Optimal policy:', np.argmax(Q,axis = 1)

# Varying discount factor (gamma) and computing the optimal action-value and value function, and policy
gamma_set = np.linspace(0,0.99,100)
Q_res = []
V_res = []
pi_res = []
for gamma in gamma_set:
    Q = value_iteration(P,r, gamma) #, policy=pol_2)
    V = np.max(Q,axis = 1)
    pi_greedy = np.argmax(Q,axis = 1)

    Q_res.append(Q*(1. - gamma))
    V_res.append(V*(1. - gamma))
    pi_res.append(pi_greedy)

subplot(1,2,1)
plot(gamma_set, np.array(pi_res)[:,0],'o')
xlabel('$\gamma$')
ylabel('Policy at state $s_1$')
ylim((-0.05,1.05))
subplot(1,2,2)
plot(gamma_set, np.array(pi_res)[:,1],'o')
ylim((-0.05,1.05))
xlabel('$\gamma$')
ylabel('Policy at state $s_2$')
figure()
plot(gamma_set, V_res)
legend(['State 1', 'State 2'])
xlabel('$\gamma$')
ylabel('(Normalized) optimal value function')


import numpy as np
from sigmoid import *

def cost_function(theta, X, y):
    m = y.size

    # You need to return the following values correctly
    cost = 0
    grad = np.zeros(theta.shape)

    h = sigmoid(np.dot(X,theta))

    mt = 1e-7
    cost = (1 / m) * np.sum(-y * np.log(h + mt) - (1 - y) * np.log(-h + 1 + mt), 0)

    grad = (1 / m) * np.dot(X.T, (h - y))

    # ===================== Your Code Here =====================
    # Instructions : Compute the cost of a particular choice of theta
    #                You should set cost and grad correctly.
    #

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

    return cost, grad


import numpy as np

def bootstrapped_correlation(x, y, perc=5, N=1000):
    assert (len(x) == len(y))
    corrs = []
    for i in range(N):
        indices = np.random.choice(len(x), replace=True, size=len(x))
        corr = np.corrcoef(x[indices], y[indices])[0, 1]
        corrs.append(corr)
    corrs = np.array(corrs)
    meancorr = np.corrcoef(x, y)[0, 1]
    upper = np.percentile(corrs, q=100 - perc)
    lower = np.percentile(corrs, q=perc)

    return meancorr, lower, upper

def bootstrapped_correlation_difference(x1,y1,x2,y2, perc=5, N=1000):
    """ compute the difference between corr(x1,y1) and corr (x2,y2) plus
    bands [perc,100-perc] of the differecne in  correlation, estimated via bootstrpaping.
    This method is useful when the uncertainty between the two correlations is not independent.
    x1,y1,x2,y2 must all have the same length"""
    assert (len(x1) == len(y1))
    assert (len(x1) == len(y2))
    assert (len(x2) == len(x1))
    n_samples = len(x1)
    diff_corrs = []
    # we use the same indices both for sample 1 and sample 2, and then compute the difference in correlation
    for i in range(N):
        indices = np.random.choice(n_samples, replace=True, size=n_samples)
        corr1 = np.corrcoef(x1[indices], y1[indices])[0, 1]
        corr2 = np.corrcoef(x2[indices], y2[indices])[0, 1]
        diff_corrs.append(corr1-corr2)
    corrs = np.array(diff_corrs)
    meancorrdiff = np.corrcoef(x1, y1)[0, 1] - np.corrcoef(x2, y2)[0, 1]
    upper = np.percentile(corrs, q=100 - perc)
    lower = np.percentile(corrs, q=perc)
    assert(upper>=lower)
    return meancorrdiff, lower, upper

def bootstrapped_rmse_difference(x1,x2, perc=5, N=1000):
    """ compute difference between x1 and x2 plus uncertainty, when x1 and x2 are either rmse or standarddeviation

    """
    assert(len(x1)==len(x2))
    n_samples = len(x1)
    means = []
    for i in range(N):
        indices = np.random.choice(n_samples, replace=True, size=n_samples)
        # now compute difference in  RMSE on this subsample
        mm = np.sqrt(np.mean(x1[indices]**2)) - np.sqrt(np.mean(x2[indices]**2))
        means.append(mm)
    means = np.array(means)
    mmean = np.sqrt(np.mean(x1**2)) - np.sqrt(np.mean(x2**2))
    upper = np.percentile(means, q=100 - perc)
    lower = np.percentile(means, q=perc)
    # assert (upper >= lower) # we deactivate this check here because if one or both of x1 and x2
    # concist only of repreated values, then numerical inaccuracis can lead to
    # lower being a tiny little larger than upper (even though they should be the same in this case)
    return np.array([mmean, lower, upper])


import scipy.stats as st
import numpy as np
import pandas as pd

class CI:
    def one_proportion(self, n, phat, conf):
        z_val = st.norm.ppf(1 - (1 - conf) / 2) # this is two-side
        # z_val = st.norm.ppf(1 - (1 - conf)) # this is one-side
        se = np.sqrt(phat*(1-phat)/n)
        print('z-value=', z_val, "se=", se)
        ci = (phat - z_val * se, phat + z_val * se)
        print(ci)

    def one_proportion_conserv(self, n, phat, conf):
        # we center at phat, and try to estimate the margin of errors
        # use normal-dist (z-value)
        z_val = st.norm.ppf(1 - (1 - conf) / 2) # this is two-side
        # z_val = st.norm.ppf(1 - (1 - conf)) # this is one-side

        se = 1 / (2 * np.sqrt(n))
        print('z-value=', z_val, "se=", se)
        ci = (phat - z_val * se, phat + z_val * se)
        print(ci)

    def two_proportions(self, pos_num1, pos_num2, total_num1, total_num2, conf):
        m1, m2 = pos_num1, pos_num2 # positive numbers
        n1, n2 = total_num1, total_num2 # total numbers

        p1, p2 = m1 / n1, m2 / n2
        phat = p1 - p2
        # phat *= -1
        z_val = st.norm.ppf(1 - (1 - conf) / 2) # this is two-side
        se = np.sqrt(p1 * (1 - p1) / n1 + p2 * (1 - p2) / n2)
        print('z-value=', z_val, "se=", se)
        print(f'{phat} +/- {z_val * se}')
        ci = (phat - z_val * se, phat + z_val * se)
        print(ci)

    def one_mean(self, mu, n, sd, conf):
        dof = n - 1
        se = sd / np.sqrt(n)
        t = st.t.ppf(1 - (1 - conf) / 2, df = dof)

        print('t-value=', t, "se=", se)
        print(f'{mu} +/- {t * se}')
        ci = (mu - t * se, mu + t * se)
        print(ci)

    def two_mean_paired(self, mu_diff, sd, n, conf):
        dof = n - 1
        mu = mu_diff
        se = sd / np.sqrt(n)
        t = st.t.ppf(1 - (1 - conf) / 2, df = dof)
        print('t-value=', t, "se=", se)
        print(f'{mu} +/- {t * se}')
        ci = (mu - t * se, mu + t * se)
        print(ci)

        if ci[0] <= 0 <= ci[1]:
            print('0 is included. maybe no difference')
        else:
            print('0 is NOT included. some difference')

    def two_means_independent_unpooled(self, mu1, mu2, sd1, sd2, n1, n2, conf):
        dof = np.min([n1 - 1, n2 - 1])
        mu_hat = mu1 - mu2
        se = np.sqrt(sd1**2 / n1 + sd2**2 / n2)
        t = st.t.ppf(1 - (1 - conf) / 2, df = dof)
        print('DOF=', dof)
        print('t-value=', t, "se=", se)

        print(f'{mu_hat} +/- {t * se}')
        ci = (mu_hat - t * se, mu_hat + t * se)
        print(ci)

    def two_means_independent_pooled(self, mu1, mu2, sd1, sd2, n1, n2, conf):
        dof = n1 + n2 - 2
        mu_hat = mu1 - mu2
        se = np.sqrt(((n1 - 1) * sd1**2 + (n2 - 1) * sd2**2)/ (n1 + n2 - 2)) * np.sqrt(1/n1 + 1/n2)
        t = st.t.ppf(1 - (1 - conf) / 2, df = dof)
        print('DOF=', dof)
        print('t-value=', t, "se=", se)

        print(f'{mu_hat} +/- {t * se}')
        ci = (mu_hat - t * se, mu_hat + t * se)
        print(ci)

class HT:
    def one_proportion_two_sides(self, p0, phat, n, alpha):
        # check for assumption
        if (n * p0 >= 10 and n * (1 - p0) >= 10):
            print('sample size is large enough')
        else:
            print('sample size is NOT large enough')

        se = np.sqrt(p0 * (1 - p0) / n)
        z = abs(phat - p0) / se
        print(f'z-value is {z}')
        print(f'which means our observed sample proportion is {z} null SE above our hypothesized population proportion ABS value')
        p_val = (1 - st.norm.cdf(z)) * 2
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

    def one_proportion_phat_larger(self, p0, phat, n, alpha):
        # check for assumption
        if (n * p0 >= 10 and n * (1 - p0) >= 10):
            print('sample size is large enough')
        else:
            print('sample size is NOT large enough')

        se = np.sqrt(p0 * (1 - p0) / n)
        z = (phat - p0) / se
        print(f'z-value is {z}')
        print(f'which means our observed sample proportion is {z} null SE above our hypothesized population proportion')
        p_val = 1 - st.norm.cdf(z)
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

    def one_proportion_phat_smaller(self, p0, phat, n, alpha):
        # check for assumption
        if (n * p0 >= 10 and n * (1 - p0) >= 10):
            print('sample size is large enough')
        else:
            print('sample size is NOT large enough')

        se = np.sqrt(p0 * (1 - p0) / n)
        z = (p0 - phat) / se
        print(f'z-value is {z}')
        print(f'which means our observed sample proportion is {z} null SE above our hypothesized population proportion')
        p_val = 1 - st.norm.cdf(z)
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

    def two_proportion_two_sides(self, pos1_num, pos2_num, n1, n2, alpha):
        m1, m2 = pos1_num, pos2_num # positive numbers
        # check for assumption
        phat = (m1 + m2) / (n1 + n2)
        print('phat=', phat)
        if (n1 * phat >= 10 and n1 * (1 - phat) >= 10 and n2 * phat >= 10 and n2 * (1 - phat) >= 10):
            print('sample size is large enough')
        else:
            print('sample size is NOT large enough, should not use this method')

        p1, p2 = m1 / n1, m2 / n2
        se = np.sqrt(phat * (1 - phat) * (1 / n1 + 1 / n2))
        z = (p1 - p2 - 0) / se
        print(f'z-stat is {z}')

        p_val = (1 - st.norm.cdf(abs(z))) * 2
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

    def two_proportion_pos1_larger(self, pos1_num, pos2_num, n1, n2, alpha):
        m1, m2 = pos1_num, pos2_num # positive numbers
        # check for assumption
        phat = (m1 + m2) / (n1 + n2)
        print('phat=', phat)
        if (n1 * phat >= 10 and n1 * (1 - phat) >= 10 and n2 * phat >= 10 and n2 * (1 - phat) >= 10):
            print('sample size is large enough')
        else:
            print('sample size is NOT large enough, should not use this method')

        # p1, p2 = m1 / n1, m2 / n2
        p1, p2 = 0.52, 0.35
        # se = np.sqrt(phat * (1 - phat) * (1 / n1 + 1 / n2))
        se = 0.0338
        z = (p1 - p2 - 0) / se
        print(f'p1={p1} and p2={p2}')
        print(f'z-stat is {z}')
        # assert z > 0, "p1 > p2"

        p_val = (1 - st.norm.cdf(abs(z)))
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

    def two_proportion_pos1_smaller(self, pos1_num, pos2_num, n1, n2, alpha):
        m1, m2 = pos1_num, pos2_num # positive numbers
        # check for assumption
        phat = (m1 + m2) / (n1 + n2)
        print('phat=', phat)
        if (n1 * phat >= 10 and n1 * (1 - phat) >= 10 and n2 * phat >= 10 and n2 * (1 - phat) >= 10):
            print('sample size is large enough')
        else:
            print('sample size is NOT large enough, should not use this method')

        p1, p2 = m1 / n1, m2 / n2
        se = np.sqrt(phat * (1 - phat) * (1 / n1 + 1 / n2))
        z = (p2 - p1 - 0) / se
        print(f'p1={p1} and p2={p2}')
        print(f'z-stat is {z}')
        # assert z > 0, "p1 > p2"

        p_val = (1 - st.norm.cdf(abs(z)))
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

    def one_mean_two_sides(self, mu0, mu_hat, n, sd, alpha):
        dof = n - 1
        se = sd / np.sqrt(n)
        t = (mu_hat - mu0) / se
        print(f't-stat is {t}')
        p_val = (1 - st.t.cdf(abs(t), df = dof)) * 2
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

        # confidence
        conf = 1 - alpha
        t_conf = st.t.ppf(1 - (1 - conf) / 2, df=dof)
        ci = (mu_hat - t_conf * se, mu_hat + t_conf * se)
        print('0 exist in the CI?')
        print(f'CI with {conf} confidence level = ', ci)

    def one_mean_mu_hat_larger(self, mu0, mu_hat, n, sd, alpha):
        dof = n - 1
        se = sd / np.sqrt(n)
        t = (mu_hat - mu0) / se
        print(f't-stat is {t}')
        p_val = (1 - st.t.cdf(abs(t), df = dof)) * 1
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

        # confidence
        conf = 1 - alpha
        t_conf = st.t.ppf(1 - (1 - conf), df=dof)
        ci = (mu_hat - t_conf * se, mu_hat + t_conf * se)
        print('0 exist in the CI?')
        print(f'CI with {conf} confidence level = ', ci)

    def one_mean_mu_hat_smaller(self, mu0, mu_hat, n, sd, alpha):
        dof = n - 1
        se = sd / np.sqrt(n)
        t = (mu0 - mu_hat) / se
        print(f't-stat is {t}')
        p_val = (1 - st.t.cdf(abs(t), df = dof)) * 1
        print(f'p-value is {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

        # confidence
        conf = 1 - alpha
        t_conf = st.t.ppf(1 - (1 - conf), df=dof)
        ci = (mu_hat - t_conf * se, mu_hat + t_conf * se)
        print('0 exist in the CI?')
        print(f'CI with {conf} confidence level = ', ci)

    def two_means_paired_two_sides(self, mu, sd, n, alpha):
        dof = n - 1
        se = sd / np.sqrt(n)
        t = (mu - 0) / se
        p_val = (1 - st.t.cdf(t, df= dof)) * 2
        print(f't-val = ', t)
        print(f'Our observed mean difference is {t} (estimated) SE above our null value of 0')
        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

        # confidence
        conf = 1 - alpha
        t_conf = st.t.ppf(1 - (1 - conf)/2, df=dof)
        ci = (mu - t_conf * se, mu + t_conf * se)
        print('0 exist in the CI?')
        print(f'CI with {conf} confidence level = ', ci)

    def two_means_independent_unpooled(self, mu1, mu2, sd1, sd2, n1, n2, alpha):
        dof = np.min([n1-1, n2-1])
        se = np.sqrt(sd1**2 / n1 + sd2**2 / n2)
        # se = 11.8831
        t = abs((mu1 - mu2) / se) # pay attention here
        print('dof=', dof)
        print('t-value=', t, 'se=', se)
        p_val = (1 - st.t.cdf(t, df = dof)) * 2 # if two sides
        # p_val = (1 - st.t.cdf(t, df = dof))  # if one sides
        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

        # using CI (two side)
        conf = 1 - alpha
        t_conf = st.t.ppf(1 - (1 - conf)/2, df=dof)
        ci = (mu1 - mu2 - t_conf * se, mu1 - mu2 + t_conf * se)
        print('0 exist in the CI?')
        print(f'CI with {conf} confidence level = ', ci)

    def two_means_independent_pooled(self, mu1, mu2, sd1, sd2, n1, n2, alpha):
        dof = n1 + n2 - 2
        sp = np.sqrt(((n1 - 1) * sd1**2 + (n2 - 1) * sd2**2)/(n1 + n2 - 2))
        se = sp * np.sqrt(1/n1 + 1/n2)
        t = abs((mu1 - mu2) / se) # pay attention here
        print('dof=', dof)
        print('t-value=', t, 'se=', se)
        p_val = (1 - st.t.cdf(t, df = dof)) * 2 # if two sides
        # p_val = (1 - st.t.cdf(t, df = dof))  # if one sides
        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')

        # using CI (two side)
        conf = 1 - alpha
        t_conf = st.t.ppf(1 - (1 - conf)/2, df=dof)
        ci = (mu1 - mu2 - t_conf * se, mu1 - mu2 + t_conf * se)
        print('0 exist in the CI?')
        print(f'CI with {conf} confidence level = ', ci)

    def chi_squared_homogeneity(self, df, alpha):
        col_sum = df.sum(axis = 0)
        row_sum = df.sum(axis = 1)
        df_sum = df.values.sum()

        df_vis = df.copy()
        df_vis['total'] = row_sum
        df_vis.loc['total'] = col_sum
        df_vis.iloc[-1,-1] = df_sum
        # df_vis

        df_expected = df.copy()

        if 1 in df.shape:
            for i in range(df.shape[0]):
                for j in range(df.shape[1]):
                    df_expected.iloc[i,j] = df_sum / np.multiply(*df.shape)
        else:
            for i in range(df.shape[0]):
                for j in range(df.shape[1]):
                    # df_expected.iloc[i,j] = (row_sum[i] / df_sum) * (col_sum[j] / df_sum) * df_sum
                    df_expected.iloc[i,j] = (row_sum[i] / df_sum) * col_sum[j]

        # df_expected

        if np.all(df_expected.values.flatten() >= 5):
            print('assumption is ok: every expected value is at least 5')
        else:
            print('assumption is NOT ok')

        if 1 in df.shape:
            ddof = len(df.values.flatten()) - 1
        else:
            ddof = (df.shape[0] - 1) * (df.shape[1] - 1)
        df_e_flat = df_expected.values.flatten()
        df_flat = df.values.flatten()
        print(f'DOF = {ddof}')
        chi2 = np.sum((df_flat - df_e_flat) **2 / (df_e_flat))
        print(f'chi2 = {chi2}')

        p_val = 1 - st.chi2.cdf(chi2, df = ddof)
        print(f'p-value = {p_val}')

        print(f'reject if p_value {p_val} < alpha {alpha}')
        if p_val < alpha:
            print('Reject!')
        else:
            print('Cannot reject.')
        return {'vis': df_vis, 'expected': df_expected}