import numpy as np
from sympy import cos, cosh, simplify, sinh, symbols

from einsteinpy.symbolic import GenericVector, MetricTensor

def euclidean_space_metric():
    symbolstr = "e1 e2"  # let angle between e1 & e2 be theta
    syms = symbols(symbolstr)
    th = symbols("theta")
    list2d = np.zeros((2, 2), dtype=int).tolist()
    # defining the metric tensor when axis are not orthogonal
    list2d[0][0] = list2d[1][1] = 1
    list2d[1][0] = list2d[0][1] = cos(th)
    metric = MetricTensor(list2d, syms, config="ll")
    return metric

def test_GenericVector_change_config_theoretical_test():
    # https://en.wikipedia.org/wiki/Covariance_and_contravariance_of_vectors#Definition
    a, b, th = symbols("a b theta")
    metric = euclidean_space_metric()
    # defining a contravariant vector
    cnvec = GenericVector([a, b], metric.syms, config="u", parent_metric=metric)
    covec = cnvec.change_config("l")  # get contravariant vector
    assert simplify(covec.tensor()[0] - (a + b * cos(th))) == 0
    assert simplify(covec.tensor()[1] - (b + a * cos(th))) == 0

def test_GenericVector_check_ValueErrors():
    a, b = symbols("a b")
    syms = symbols("e1 e2")
    # input a tensor with wring rank
    try:
        arr = [[a, b], [b, 1]]
        v1 = GenericVector(arr, syms, "l")
        boolstore = False
    except ValueError:
        boolstore = True
    assert boolstore
    # input a wrong length config
    try:
        arr = [a, b]
        v2 = GenericVector(arr, syms, "uu")
        boolstore = False
    except ValueError:
        boolstore = True
    assert boolstore

def test_lorentz_transform():
    def get_vector():
        syms = symbols("t x y z")
        t, x, y, z = syms
        return GenericVector([t, x, y, z], syms=syms, config="u")

    def get_lorentz_matrix():
        list2d = [[0 for t1 in range(4)] for t2 in range(4)]
        phi = symbols("phi")
        list2d[0][0], list2d[0][1], list2d[1][0], list2d[1][1] = (
            cosh(phi),
            -sinh(phi),
            -sinh(phi),
            cosh(phi),
        )
        list2d[2][2], list2d[3][3] = 1, 1
        return list2d

    t, x, phi = symbols("t x phi")
    v = get_vector().lorentz_transform(get_lorentz_matrix())
    print(v.tensor())
    assert simplify(v[0] - (t * cosh(phi) - x * sinh(phi))) == 0
    assert simplify(v[1] - (x * cosh(phi) - t * sinh(phi))) == 0


"""
  Name     : c11_12_normal_random.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""
import numpy as np
from scipy import stats,random
#
np.random.seed(12345)
n=5000000

ret = random.normal(0,1,n)
print('mean    =', np.mean(ret))
print('std     =',np.std(ret))
print('skewness=',stats.skew(ret))
print('kurtosis=',stats.kurtosis(ret))


# Project Euler Problem 6 Solution
#
# Problem statement:
# The sum of the squares of the first ten natural numbers is,
# 1^2 + 2^2 + ... + 10^2 = 385. The square of the sum of the
# first ten natural numbers is (1 + 2 + ... + 10)^2 = 55^2 = 3025.
# Hence the difference between the sum of the squares of the
# first ten natural numbers and the square of the sum is 3025 − 385 = 2640.
# Find the difference between the sum of the squares of the first
# one hundred natural numbers and the square of the sum.
#
# Solution description:
# Simple algebraic manipulations show, that the required sum equals
# 2*1*(2 + 3 + ... + n) + 2*2*(3 + 4 + 5 + ... + n) + 2*3*...
# This approach is implemented directly as a function with n as input.
# For some further utility, the function also takes another input m with
# default value m = 1, which determines the starting point for the summation.
#
# Author: Philipp Schuette
# Date: 2019/02/10
# License: MIT (see ../LICENSE.md)

import time

import numpy as np

def calc_sums(m, n):
	sum1 = 0  # gets incremented towards the final value
	sum2 = int((n*(n + 1) - m*(m + 1))/2)  # stores intermediate sums
	for i in range(m, n):
		sum1 += i*sum2
		sum2 -= (i + 1)
	return 2*sum1

if __name__ == "__main__":
    # calculate result and time it
    start = time.time()
    target = 100
    solution = calc_sums(1, target)
    end = time.time()

    # print result
    print("expired: {}s, solution: {}".format(
    np.round(end - start, 5), solution))


import numpy as np

def NewtonRaphson(f, fprime, x0, max_iter = 100, prec = 1e-10, verbose=False):

    x = [x0]
    x_val = x0

    for i in range(max_iter):

        val_i = f(x[i])
        df = fprime(x[i])
        epsilon = val_i/df
        x_val = x_val - epsilon
        x.append(x_val)

        if abs(epsilon) < prec:
            print(abs(df))
            return x_val, x
    if verbose:
        print(f"This calculation did not converge after {max_iter} iterations")

    return x_val, x

def NewtonRaphsonFact(f=None, fprime=None, x0=None, max_iter = 100, prec = 1e-10, verbose=False, **kwargs):

    x = [x0]
    x_val = x0

    for i in range(max_iter):

        val_i = f(x[i])
        df = fprime(x[i])
        epsilon = val_i/df
        x_val = x_val - epsilon
        x.append(x_val)

        if abs(epsilon) < prec:
            return i
    if verbose:
        print(f"This calculation did not converge after {max_iter} iterations")

    return i

def mandelbrot(c, max_iter = 80, **kwargs):
    z = 0
    n = 0
    while abs(z) <= 2 and n < max_iter:
        z = z**2 + c
        n += 1
    return n

def CreateImageMap(function, function_args, bounds, max_iter = 80, width = 200, height = 200):

    re_start, re_end, im_start, im_end = bounds
    if width > 1000:
        print(f'width of {width} is too large. Your computer only has so many pixels.')
        print("try zooming in with a smaller boundary to observe more detail")
        return

    if height > 1000:
        print(f'height of {height} is too large. Your computer only has so many pixels.')
        print("try zooming in with a smaller boundary to observe more detail")
        return

    X = np.zeros([width, height])
    for x in range(0, width):
        for y in range(0,height):
            real = re_start + (x/width) * ( re_end - re_start)
            imaginary = im_start + (y/width) * ( im_end - im_start)
            guess = complex(real, imaginary)
            try:
                function_args['mult']
            except KeyError:
                function_args['mult'] = 1.1

            function_args['x0'] = guess
            function_args['x1'] = function_args['mult'] * guess
            function_args['c'] = guess
            try:
                m = function(**function_args)
            except Exception as e:
                print(e)
                m = max_iter
            color = 255 - int(m * 255 / max_iter)

            X[x,y] = color

    return X

def secantfact(function, x0, x1, max_iter = 200, prec = 1e-5, verbose = False, **kwargs):

    f1 = function(x0)
    f = function(x1)

    if abs(f1) < abs(f):
        rts = x0
        x1 = x1
        f1, f = f, f1
    else:
        rts = x1
    for i in range(max_iter):
        dx = (x0 - rts) * f / (f - f1)
        x0 = rts
        f1 = f
        rts += dx
        f = function(rts)

        if abs(dx) < prec or f == 0:
            return i

    if verbose:
         print(f"This calculation did not converge after {max_iter} iterations")
    return i

def schroderfact(derivative, function, secondder, x0, prec = 1e-5, max_iter = 50, **kwargs):
    check = 1
    xim1 = x0
    n = 0
    while prec < check:
        num = (function(xim1) * derivative(xim1))
        dem = derivative(xim1)**2 - function(xim1) * secondder(xim1)
        xi = xim1 - num / dem
        n += 1
        check = abs(xi - xim1)
        xim1 = xi
        # print(check)
        if n == max_iter:
            break
    return  n

def halleyfact(derivative, function,seconder, x0, prec = 1e-5, max_iter = 100, **kwargs):
    check = 1
    xim1 = x0
    n = 0
    for i in range(max_iter):

        a = function(xim1) * seconder(xim1)
        b = 2 * derivative(xim1) ** 2
        c = 1 - a/b

        d = derivative(xim1) * c
        xi = xim1 - function(xim1) / d
        n += 1
        check = abs(xi - xim1)
        xim1 = xi
        eps = abs(function(xim1) / d)

        if eps < prec:
            break

    return  n

def nderiv(func, x, eps=np.sqrt(np.finfo(float).eps)):
    ''' Takes a vector of each component of a multivariate
    function and returns its Jacobian matrix as computed by
    finite differences'''

    N = len(x)

    J = [[None for i in range(N)] for j in range(N)]
    #xh is x + h in the derivative formula
    xh = x
    # very bad quick fix, will not work as a gradient
    if isinstance(x[0], complex):
        eps = complex(eps, eps)
    for i in range(N):
        temp = xh[i]
        h = eps * temp
        if h == 0: h = eps
        xh[i] = temp + h # scootch that point over
        # evalueat f(x+h)
        f = func(xh)

        xh[i] = temp
        fvec = func(x)
        # forward difference formula
        for j in range(N):
            J[j][i] = (f[j] - fvec[j])/h

    if len(x) == 1:
        return J[0][0]

    return J


import numpy as np

def gaussian(x,x0=0,sigma=1):
    '''Numpy implementation of gaussian'''
    return np.exp(
        -np.power((x - x0) / sigma, 2) / 2
    ) / (np.sqrt(2 * np.pi) * sigma)

def cumulative_gaussian_lut(step:float=0.01, sigma_count:int=3):
    '''
    Cumulative and normalized gaussian Lookup table.

    1. Function creates gaussian with x0 = 0.5 and sigma = 0.5 / 3 in range
       from 0 to 1.
       According to 3 sigma rule 99.7% values are in range from 0 to 1.
    2. Calculates cumulative sum on gaussian
    4. Normalize by max value

    Arguments are in range [0;1) and values are in range [0;1].
    Argument 0 has value 0.

    Parameters
    ==========
    step: float, optional
        Resolution of LUT. The smaller value, the less accuracy you get.
        The bigger value, the more memory you use. 0.01-0.001 should be good.
    sigma_count: int, optional
        What multiple of sigma should the half of width be.
        According the 1 sigma rule in range +- 1 sigma is 68.2% values.
        According the 2 sigma rule in range +- 2 sigma is 95.4% values.
        According the 3 sigma rule in range +- 3 sigma is 99.7% values.
        According the 4 sigma rule in range +- 4 sigma is 99.994% values.
        You may to use this parameter to control value change rate.
        1 sigma - linear, 4 sigma - slow at begin and at end, fast in middle.

    Returns
    =======
    Function for access to approximated value for given argument.
    It accepts argument from range [0;1) (from 0 (inclusive) to 1 (exclusive)).
    Returns 0 ir 1 if index out of bounds.
    Return the nearest precalculated for given argument (range (0; 1]
    from 0 (exclusive) to 1 (exclusive))).

    Examples
    ========
    >>> lut = cumulative_reversed_gaussian_lut()
    >>> lut(0)
        0
    >>> lut(0.5)
        0.49881326466597947
    >>> lut(0.25)
        0.9339886741945999
    '''
    a = np.arange(0, 1, step)
    width = 1
    half_width = width / 2
    x0 = half_width
    sigma = half_width / sigma_count

    gx = gaussian(a, x0=x0, sigma=sigma)
    cs = np.cumsum(gx)
    max_ = cs[len(cs) - 1]
    normalized_cs = cs / max_

    def get_value(x):
        if x < 0:
            return 0
        elif x >= 1:
            return 1
        idx = int(x / step)
        return normalized_cs[idx]
    return get_value

if __name__ == '__main__':
    # Example usage. Draw gaussian plots and cumulative, normalized
    # plots for different sigma.

    step = 0.001
    lut_count = 4
    r = range(1, lut_count + 1)
    luts = [cumulative_gaussian_lut(step=step, sigma_count=i)
                for i in r]
    a = [i * step for i in range(0, int(1 / step))]

    from matplotlib import pyplot as plt
    plt.subplot(1, 2, 1)
    for lut in luts:
        plt.plot(a, [lut(i) for i in a])
    plt.legend(["%d sigma" % (i,) for i in r])

    plt.subplot(1, 2, 2)
    for i in range(1, lut_count + 1):
        sigma = 1 / i
        g = gaussian(np.array(a), 0.5, sigma)
        max_ = g.max()
        g = g / max_
        plt.plot(a, g)
    plt.legend(["%d sigma" % (i,) for i in r])
    plt.suptitle(('Cumulative, normalized gaussian'))
    plt.show()


import numpy as np

def decimal_to_binlist(decimal, digits): # ex) 6,3 --> [1,1,0]

    bin_str = "{:0{digits}b}".format(decimal, digits=digits)
    return  [int(s) for s in list(bin_str)]

def binlist_to_decimal(bin_list): # ex) [0,1,1] --> 3

    return int("".join([str(i) for i in bin_list]), 2)

def make_hamming_matrix(r):

    # parity check matrix (H)
    A = []
    for x in range(1,2**r):
        bin_list = decimal_to_binlist(x, r)
        if sum(bin_list) == 1: continue
        A.append(bin_list)
    A = np.array(A)
    I_H = np.eye(r, dtype=int)
    H = np.concatenate([A, I_H])

    # represent integer each row of H matrix (for error correction algorithm)
    H_int = [binlist_to_decimal(row) for row in H]

    # generator matrix (G)
    I_G = np.eye(2**r-r-1, dtype=int)
    G = np.concatenate([I_G, A], 1)

    return G, H, H_int

def generate_data(k, N):  # random k-bits data

    for _ in range(N):
        yield np.random.randint(2, size=k)

def add_noise(d_in):  # bit flip to one bit (select randomly)

    idx = np.random.randint(len(d_in))
    err = np.array([1 if i == idx else 0 for i in range(len(d_in))])
    d_out = (d_in + err) % 2
    return d_out

def correct_error(d_in, H_int):

    d_out = d_in.copy()
    p = (d_out @ H) % 2
    x = binlist_to_decimal(p)
    err_idx = H_int.index(x)
    d_out[err_idx] = (d_out[err_idx] + 1) % 2  # bit flip (recover)
    return d_out

if __name__ == '__main__':

    r = 3
    n = 2**r - 1
    k = 2**r - r - 1
    N = 10

    G, H, H_int  = make_hamming_matrix(r)

    print("* input(random) -> encode -> add noise(random 1-bit flip) -> correct -> decode:")
    err_count = 0
    for x in generate_data(k, N):
        y = (x @ G)%2
        y_error = add_noise(y)
        y_correct = correct_error(y_error, H_int)
        x_correct = y_correct[0:k]  # decode (= extract 1st to k-th elements)
        print("{0:} -> {1:} -> {2:} -> {3:} -> {4:}".format(x,y,y_error,y_correct,x_correct))

        if sum((x+x_correct)%2) == 1: # if x != x_correct --> 1
            err_count += 1

    err_rate = err_count / N
    print("* error rate = {0:} (count:{1:} / total:{2:})".format(err_rate, err_count, N))


import numpy as np

def _fourDVR(start,stop,num):
    """
    Calculate periodic Fourier (exponential) DVR grid and operators.

    Parameters
    ----------
    start : float
        Grid start value
    stop : float
        Grid stop value. Must be larger than start
    num : int
        Number of grid points. Must be >= 3 and odd.

    Returns
    -------
    grid : ndarray
        Array of DVR grid points
    D : ndarray
        First derivative operator in DVR basis, shape (num, num)
    D2 : ndarray (num,num)
        Second derivative operator in DVR basis, shape (num, num)

    """

    if stop <= start:
        raise ValueError("stop value must be larger than start value")
    if num < 3 :
        raise ValueError("num must be >= 3")
    if num % 2 == 0:
        raise ValueError("num must be odd")

    grid = np.linspace(start, stop, num+1)[0:num]
    period = stop - start

    D = np.ndarray((num,num))
    D2 = np.ndarray((num,num))

    for i in range(num):
        for j in range(i,num):
            delta = i - j
            if delta == 0:
                D[i,j] = 0
                D2[i,j] = -(np.pi / period)**2 * (1/3.0) * (num**2 - 1)
            else:
                D[i,j] = (np.pi / period) * (-1)**delta / np.sin(np.pi*delta/num)
                D[j,i] = -D[i,j]

                D2[i,j] = -2*(np.pi / period)**2 * (-1)**delta * np.cos(np.pi * delta / num) / (np.sin(np.pi*delta/num)**2)
                D2[j,i] = +D2[i,j]

    return grid, D, D2

def _fourDVRwfs(q, start, stop, num):
    """
    Calculate Fourier (exponential) DVR wavefunctions.

    Parameters
    ----------
    q : ndarray
        A 1D array of coordinate values.
    start : float
        DVR grid start value.
    stop : float
        DVR grid stop value.
    num : int
        The number of DVR grid points

    Returns
    -------
    wfs : ndarray
        A (`q`.size, `num`) shaped array with
        the DVR wavefunctions evaluated at grid points `q`.

    """

    if np.ndim(q) != 1:
        raise ValueError("q must be 1-dimensional")
    if stop <= start:
        raise ValueError("stop value must be larger than start value")
    if num < 3 :
        raise ValueError("num must be >= 3")
    if num % 2 == 0:
        raise ValueError("num must be odd")

    nq = q.size

    period = stop - start

    wfs = np.ndarray((nq,num), dtype = q.dtype)

    m = (num-1)//2 # max frequency

    for i in range(num): # DVR function at i^th grid point
        t = 1
        for k in range(1,m+1): # Calculate exponential sum as cosines explicitly
            t += 2*np.cos(k*(q - start - i*period/num) * 2*np.pi/period)

        wfs[:,i] = t/np.sqrt(num*period)

    return wfs


import matplotlib.pyplot as plt
import numpy as np
from numpy.polynomial.polynomial import Polynomial
from scipy.interpolate import lagrange

# Interpolate basis vector e1.
plt.figure()
x = np.linspace(0, 1, 5)
e1 = np.array([1, 0, 0, 0, 0])
x_fine = np.linspace(0, 1)
print(f"x = {x}")

poly = lagrange(x, e1)
coef1 = poly.coef[::-1] # reverse order of coefficients

plt.scatter(x, e1, color="red", label="points and values")
plt.plot(x_fine, Polynomial(coef1)(x_fine), label="Lagrange polynomial")
plt.legend()
plt.show()

# Interpolate five basis vectors e1, e2, e3, e4, e5.
e2 = np.array([0, 1, 0, 0, 0])
e3 = np.array([0, 0, 1, 0, 0])
e4 = np.array([0, 0, 0, 1, 0])
e5 = np.array([0, 0, 0, 0, 1])

coef2 = lagrange(x, e2).coef[::-1] # reverse order of coefficients
coef3 = lagrange(x, e3).coef[::-1] # reverse order of coefficients
coef4 = lagrange(x, e4).coef[::-1] # reverse order of coefficients
coef5 = lagrange(x, e5).coef[::-1] # reverse order of coefficients

plt.figure()
plt.plot(x_fine, Polynomial(coef1)(x_fine), label="Lagrange polynomial of e1")
plt.plot(x_fine, Polynomial(coef2)(x_fine), label="Lagrange polynomial of e2")
plt.plot(x_fine, Polynomial(coef3)(x_fine), label="Lagrange polynomial of e3")
plt.plot(x_fine, Polynomial(coef4)(x_fine), label="Lagrange polynomial of e4")
plt.plot(x_fine, Polynomial(coef5)(x_fine), label="Lagrange polynomial of e5")
plt.legend()
plt.show()

# Interpolate arbitrary data vector y
y = np.random.sample(5)*10-5
coef = lagrange(x, y).coef[::-1] # reverse order of coefficients

plt.figure()
plt.scatter(x, y, color="red", label="points and values")
plt.plot(x_fine, Polynomial(coef1)(x_fine)*y[0] + Polynomial(coef2)(x_fine)*y[1] + Polynomial(coef3)(x_fine)*y[2] + Polynomial(coef4)(x_fine)*y[3] + Polynomial(coef5)(x_fine)*y[4], label="Sum")
plt.plot(x_fine, Polynomial(coef)(x_fine), label="Lagrange polynomial", linestyle="--")
plt.legend()
plt.show()


"""

"""
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

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

    #Cost function in vectorized form
    h = X @ theta
    J = float((1./(2*m)) * (h - y).T @ (h - y));
    return J

def gradient_descent(X,y,theta,alpha = 0.0005,num_iters=1000):
    #Initialisation of useful values
    m = np.size(y)
    J_history = np.zeros(num_iters)
    theta_0_hist, theta_1_hist = [], [] #For plotting afterwards

    for i in range(num_iters):
        #Grad function in vectorized form
        h = X @ theta

        # update parameters by gradient descent
        theta = theta - alpha * (1/m)* (X.T @ (h-y))

        #Cost and intermediate values for each iteration
        J_history[i] = costfunction(X,y,theta)
        theta_0_hist.append(theta[0,0])
        theta_1_hist.append(theta[1,0])

    return theta,J_history, theta_0_hist, theta_1_hist

#Creating the dataset (as previously)
x = np.linspace(0,1,40)
noise = 1*np.random.uniform(  size = 40)
y = np.sin(x * 1.5 * np.pi )
y_noise = (y + noise).reshape(-1,1)
X = np.vstack((np.ones(len(x)),x)).T

#Setup of meshgrid of theta values
T0, T1 = np.meshgrid(np.linspace(-1,3,100),np.linspace(-6,2,100))

#Computing the cost function for each theta combination
zs = np.array(  [costfunction(X, y_noise.reshape(-1,1),np.array([t0,t1]).reshape(-1,1))
                     for t0, t1 in zip(np.ravel(T0), np.ravel(T1)) ] )
#Reshaping the cost values
Z = zs.reshape(T0.shape)

#Computing the gradient descent
theta_result,J_history, theta_0, theta_1 = gradient_descent(X,y_noise,np.array([0,-6]).reshape(-1,1),alpha = 0.3,num_iters=1000)

#Angles needed for quiver plot
anglesx = np.array(theta_0)[1:] - np.array(theta_0)[:-1]
anglesy = np.array(theta_1)[1:] - np.array(theta_1)[:-1]

fig = plt.figure(figsize = (16,8))

#Surface plot
ax = fig.add_subplot(1, 2, 1, projection='3d')
ax.plot_surface(T0, T1, Z, rstride = 5, cstride = 5, cmap = 'jet', alpha=0.5)
ax.plot(theta_0,theta_1,J_history, marker = '*', color = 'r', alpha = .4, label = 'Gradient descent')

ax.set_xlabel('theta 0')
ax.set_ylabel('theta 1')
ax.set_zlabel('Cost function')
ax.set_title('Gradient descent: Root at {}'.format(theta_result.ravel()))
ax.view_init(45, 45)

#Contour plot
ax = fig.add_subplot(1, 2, 2)
ax.contour(T0, T1, Z, 70, cmap = 'jet')
ax.quiver(theta_0[:-1], theta_1[:-1], anglesx, anglesy, scale_units = 'xy', angles = 'xy', scale = 1, color = 'r', alpha = .9)
# plt.axis('equal')
plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ch9Python.py. Nonlinear regression.

import matplotlib.pyplot as plt
import numpy as np
import statsmodels.api as sm

x1 = [1.00,1.25,1.50,1.75,2.00,2.25,2.50,2.75,3.00,3.25,3.50,3.75,4.00]
y = [3.34,4.97,4.15,5.40,5.21,4.56,3.69,5.86,4.58,6.94,5.57,5.62,6.87]

# Convert data to vectors.
x1 = np.array(x1)
y = np.array(y)
n = len(y)
x2 = x1**2 # define x2 as square of x1.

###############################
# Quadratic model. Repeated twice: 1) Vector-matrix, 2) Standard library.
###############################
ymean = np.mean(y)
ones = np.ones(len(y)) # 1 x n vector
Xtr = [ones, x1, x2]   # 3 x n matrix
X = np.transpose(Xtr)  # n x 3 matrix
y = np.transpose(y)    # 1 x n vector

# 1) Find slopes and intercept using vector-matrix notation.
Xdot = np.dot(Xtr,X)
Xdotinv = np.linalg.pinv(Xdot)
XdotinvA = np.dot(Xdotinv,Xtr)
params = np.dot(XdotinvA,y)

b0Quadratic = params[0] # 3.819
b1Quadratic = params[1] # 0.212
b2Quadratic = params[2] # 0.111

print('\nQUADRATIC VECTOR-MATRIX PARAMETERS')
print('slope b1 = %6.3f' % b1Quadratic)
print('slope b2 = %6.3f' % b2Quadratic)
print('intercept b0 = %6.3f' % b0Quadratic)

# 2) STANDARD LIBRARY Quadratic output.
quadraticModel = sm.OLS(y, X).fit()
print('\n\nQUADRATIC MODEL SUMMARY')
print(quadraticModel.params)
print(quadraticModel.summary())

###############################
# Linear model (using standard library).
###############################
Xtr = [ones, x1]
X = np.transpose(Xtr)
linearModel = sm.OLS(y, X).fit()
print('\n\nLINEAR MODEL SUMMARY')
print(linearModel.params)
print(linearModel.summary())

params = linearModel.params
b0LINEAR = params[0] # 3.225
b1LINEAR = params[1] # 0.764
yhatLINEAR = b1LINEAR * x1 + b0LINEAR

###############################
# PLOT DATA.
###############################
fig = plt.figure(1)
fig.clear()
yhatQuadratic = b1Quadratic * x1 + b2Quadratic * x2 + b0Quadratic

plt.plot(x1, y, "o", label="Data")
plt.plot(x1, yhatQuadratic, "b--",label="Quadratic fit")
plt.plot(x1, yhatLINEAR, "r--",label="Linear fit")
plt.legend(loc="best")
plt.show()

###############################
# STANDARD LIBRARY: Results of extra sum of squares method.
###############################
# test hypothesis that x2=0
hypothesis = '(x2 = 0)'
f_test = quadraticModel.f_test(hypothesis)
print('\nResults of extra sum of squares method:')
print('F df_num = %.3f df_denom = %.3f'
% (f_test.df_num, f_test.df_denom))       # 1, 10
print('F partial = %.3f' % f_test.fvalue) # 1.127
print('p-value (that x2=0) = %.3f' % f_test.pvalue)   # 0.729

###############################
# END OF FILE.
###############################


import math
import numpy as np
from matplotlib import pyplot as plt
from scipy import optimize
from methods import *
from pprint import pprint

def FirstDerivateSchwefelsFunction(x):
    result = []

    x_list = x.getA()[0]

    for i in x_list:
        result.append(-(math.sin(math.sqrt(abs(i))) + (i**2*math.cos(math.sqrt(abs(i)))) / (2 * math.sqrt(abs(i)**3))))

    return np.matrix([[x] for x in result])

def SecondDerivateSchwefelsFunction(x):
    x_list = x.getA()[0]

    result = [[0 for i in range(len(x_list))] for j in range(len(x_list))]

    for ind, i in enumerate(x_list):
        result[ind][ind] = ((i*math.pow(abs(i), 7/2)*math.sin(math.sqrt(abs(i)))-3*i**3*abs(i)*math.cos(math.sqrt(abs(i)))) / (4*math.pow(abs(i), 9/2)))

    return np.matrix(result)

#-------F(x)--------------
def F(x):
    result = 0

    for i in x:
        result += i * math.sin(math.sqrt(abs(i)))

    return -result

#--------For x_i <= 500 restrictions-------------
def g_i_1(x_i : float):
    return x_i -500

def g_i_1_plus(x_i : float):
    return max(0,x_i -500)
#------For x_i>= -500 restrictions---------------
def g_i_2(x_i : float):
    return -x_i-500

def g_i_2_plus(x_i : float):
    return max(0,-x_i-500)

#------Q(x,c)-for penalization method----------------

def Q(c):
    def Q_call(x):
        f_x_eval = F(x)

        g_i_1_sum = 0
        for variable in x:
            g_i_1_sum +=g_i_1_plus(variable)

        g_i_2_sum = 0
        for variable in x:
            g_i_2_sum +=g_i_2_plus(variable)

        return f_x_eval + c * g_i_1_sum + c * g_i_2_sum

    return Q_call

#------R(x,miu)- for barrier method-------------------
def R(miu):
    def R_call(x):
        f_x_eval = F(x)

        g_i_1_sum = 0
        for variable in x:
            g_i_1_sum -=1/g_i_1(variable)

        g_i_2_sum = 0
        for variable in x:
            g_i_2_sum -=1/g_i_2(variable)

        return f_x_eval + miu * g_i_1_sum + miu * g_i_2_sum

    return R_call

#------Omega( chequear si un vector x cumple con las restricciones)-------
def omega(x):
    # print(x)
    for element in x:
        # print(element)
        if element<-500 or element>500:
            return False
    return True

def plot():
    x = np.arange(-500, 500, 0.1)
    y = [F([i]) for i in x]

    plt.plot(x, y)
    plt.show()

#testing-------------------
if __name__ == '__main__':
    # plot()

    # ans = Penalization_method("BFGS", Q, omega, x0=np.array([770,770]), c0=1, alpha=1.5, epsilon=0.001, k_max=500)
    ans = Barrier_method("BFGS", R, x0=np.array([430,430]), miu_0=1, alpha=0.5, epsilon=0.001, k_max=500)
    # print("Penalization Method")
    # pprint(ans)
    # print()
    # print("Barrier Method")
    print(F(ans["1.Result"]))
    pprint(ans)
    # print()
    # print("SQP Method")
    # print(SQP_method(F, x0=np.array([380,380]), bounds=[(-500, 500), (-500, 500)], k_max=500))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri May 31 11:04:56 2019

@author: fietekrutein

A script to find a new direction of an equality constrained
convex optimization problem using pure Newton's method
"""

import numpy as np

# functions as of example problem

def exp1(x):
    return(np.exp(x[0] + 3*x[1] + x[2] - 0.1))

def exp2(x):
    return (np.exp(x[0] - 3*x[1] + 2*x[2] - 0.1))

def exp3(x):
    return (np.exp(-x[0] - x[1] - 2*x[2] - 0.1))

def obj(x):
    return (exp1(x) + exp2(x) + exp3(x))

def grad(x):
    i1 = exp1(x) + exp2(x) - exp3(x)
    i2 = 3*exp1(x) - 3*exp2(x) - exp3(x)
    i3 = exp1(x) + 2*exp2(x) - 2*exp3(x)
    return (np.array([i1,i2,i3]))

def hess(x):
    i11 = exp1(x) + exp2(x) + exp3(x)
    i12 = 3*exp1(x) -3*exp2(x) + exp3(x)
    i13 = exp1(x) + 2*exp2(x) + 2*exp3(x)

    i21 = 3*exp1(x) - 3*exp2(x) + exp3(x)
    i22 = 9*exp1(x) + 9*exp2(x) + exp3(x)
    i23 = 3*exp1(x) - 6*exp2(x) + 2*exp3(x)

    i31 = exp1(x) + 2*exp2(x) + 2*exp3(x)
    i32 = 3*exp1(x) - 6*exp2(x) + 2*exp3(x)
    i33 = exp1(x) + 4*exp2(x) + 4*exp3(x)

    return (np.array([[i11,i12,i13],
                      [i21,i22,i23],
                      [i31,i32,i33]]))

# new direction search formulation

def NewDir(x, A):
    g = grad(x)
    h = hess(x)
    h_inv = np.linalg.inv(h)
    d = -np.dot(h_inv, g) + np.dot(np.dot(np.dot(h_inv, A.T),
                  np.linalg.inv(np.dot(A, np.dot(h_inv, A.T)))), np.dot(A, np.dot(h_inv, g)))
    return (d)

# initialize example
A = np.array([[1,2,0],
              [4,0,5]])
x = np.array([1,1,2/5])

# execute algorithm
NewDir(x,A)

# check null space / whether decent direction
xnew = x + NewDir(x,A)
d2 = NewDir(x,A)
nspace_t = np.dot(A,d2)
np.dot(grad(x).T, d2)


#Find Bell Numbers

def BellNumber(index):
    bell=[[0 for i in range(index+1)] for j in range(index+1)]
    bell[0][0] = 1
    for i in range(1,index+1):
        bell[i][0]=bell[i-1][i-1]
        for j in range(1,i+1):
            bell[i][j]=bell[i-1][j-1]+bell[i][j-1]
    return bell[index][0]

def MPmath(index,dps=30):
    import mpmath
    mpmath.mp.dps = dps
    return int(mpmath.bell(index))

def DobinskiFormula(n):
    from math import e
    from math import factorial
    try:
        return round((1/e) * sum([(k**(n))/(factorial(k)) for k in range(1,1000)]))
    except:
        return "inf"

def doTest(toPrint=False,toProgress=False,start=0,toEnd=1000,algo="s"):
    s=set()
    KK=10000
    from IPython.display import clear_output
    for i in range(start,toEnd+1):
        if(toProgress and (i<KK or (i>=KK and i%(KK/100)==0))):
            clear_output(wait=True)
            print(i,end="\t")
        if(algo=="s"):
            bell=BellNumber(i)
        elif(algo=="mpmath"):
            bell=MPmath(i)
        elif(algo=="dobinski"):
            bell=DobinskiFormula(i)
        if(bell):
            s.add(bell)
            if(toPrint and not toProgress):
                print(bell,end=", ")
        if(toProgress and (i<KK or (i>=KK and i%(KK/100)==0))):
            print(s)
    if(not toPrint):
        return s

#BellNumber(2000)
#MPmath(2000)
#DobinskiFormula(4)
#doTest(False,False,1,500) #7.97s
#doTest(False,False,1,500,"dobinski") #2.95s
#doTest(False,False,1,500,"mpmath") #1.73s


import numpy as np

class LogisticRegression:

    # Initialize with features and labels
    def __init__(self, X, Y):
        self.X = X
        self.Y = Y
        self.w = np.ones(X.shape[1])
        self.N = X.shape[0]

    # average cost function
    # w is the weights
    # X is the design matrix
    # y is the labels
    def cost(self):
        z = np.dot(self.X, self.w)
        J = np.mean(self.Y * np.log1p(np.exp(-z)) + (1 - self.Y) * np.log1p(np.exp(z)))
        return J

    # logistic function on x
    def logistic(self, x):
        return 1 / (1 + np.exp(-x))

    # gradient of logistic regression
    def gradient(self, regularization):

        # Calculate y_hat based on logistic func
        y_hat = self.logistic(np.dot(self.X, self.w))

        # Gradient of logistic descent
        gradient = np.dot(self.X.T, y_hat - self.Y) / self.N

        # L2 regularization
        gradient[1:] += regularization * self.w[1:]

        return gradient

    # lr is the learning rate
    # epochs is the number of iterations
    # w is the weights to be learned, default value is 0 array
    # eps is the termination condition, when the norm of the gradient is less than the value
    def fit(self, lr=0.5, epochs=10000, regularization=0, verbose=False, eps=1e-2):

        # Iterate epochs times, performing gradient descent
        for epoch in range(epochs):
            gradient = self.gradient(regularization)

            # go down the gradient with given learning rate
            self.w -= lr * gradient

            # print details about the training if needed
            if verbose:
                # every 1000 epochs, print the cost
                if epoch % 1000 == 0:
                    print('iter: ', epoch, ' cost: ', self.cost())

            if eps != None and np.linalg.norm(gradient) < eps:
                #print('Reached min cost.')
                break

        return self.cost()

    # X is a feature instance, default decision boundary = 0.5
    def predict(self, X, boundary=0.5):

        return (self.logistic(np.dot(X, self.w)) > boundary).astype(int)


import math as ma
import numpy as np
from numba import jit

@jit(nopython=True)
def binomialTree (r, sigma, S0, T, K, M, call = True, European = True):
    """Price a vanilla option using the Cox, Ross and Rubinstein binomial tree method, based
    on the algorithm presented in the Computational Finance script at the University of Kiel.

    Keyword arguments:
    r -- risk free rate
    sigma -- annual volatility
    S0 -- initial stock price
    T -- maturity of the option
    K -- strike price of the option
    call -- boolean value to differentiate between call and put options (default: call)
    European -- boolean value to differentiate between European and American options (default: European)
    M -- number of periods
    """

    # step 1: calibrate the lattice and the risk neutral probability
    dt = T/M
    alpha = ma.exp(r*dt)
    beta = (ma.pow(alpha,-1)+alpha*ma.exp((sigma**2)*dt))/2
    u = beta + ma.sqrt(beta**2-1)
    d = ma.pow(u,-1)
    q = (ma.exp(r*dt)-d)/(u-d)

    # step 2: build two empty lattices & initialize the first entry of the stock prices
    stockPrice = np.empty((M,M))
    stockPrice[0,0] = S0

    optionPrice = np.empty((M,M))

    # step 3: fill the lattice up with all possible stock prize realizations
    for i in range(M+1):
        for j in range(i+1):
            stockPrice[j,i] = S0 * u**j * d**(i-j)

    # step 4: compute the option value for every row in the last column
    if call == True:
        for j in range(M+1):
            optionPrice[j,M] = max(0, stockPrice[j,M]-K)

    else:
        for j in range(M+1):
            optionPrice[j,M] = max(0, K-stockPrice[j,M])

    # step 5: compute the option value backwardly
    if European == True:
        for i in reversed(range(M)):
            optionPrice[j,i] = ma.exp(-r*dt)*(q*optionPrice[j+1,i+1]+(1-q)*optionPrice[j,i+1])

    else:
        if call == True:
            optionPrice[j,i] = max(max(0,stockPrice[j,i]-K), ma.exp(-r*dt)*(q*optionPrice[j+1,i+1]+(1-q)*optionPrice[j,i+1]))
        else:
            max(max(0,K-stockPrice[j,i]), ma.exp(-r*dt)*(q*optionPrice[j+1,i+1]+(1-q)*optionPrice[j,i+1]))

    # step 6: return the option value at time t=0
    return optionPrice[0,0]


# Plot polynomial regression on 1d problem
# Based on https://github.com/probml/pmtk3/blob/master/demos/linregPolyVsDegree.m

import numpy as np
import matplotlib.pyplot as plt
from pyprobml_utils import save_fig

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import MinMaxScaler
import sklearn.metrics
from sklearn.metrics import mean_squared_error as mse

def make_1dregression_data(n=21):
    np.random.seed(0)
    xtrain = np.linspace(0.0, 20, n)
    xtest = np.arange(0.0, 20, 0.1)
    sigma2 = 4
    w = np.array([-1.5, 1/9.])
    fun = lambda x: w[0]*x + w[1]*np.square(x)
    ytrain = fun(xtrain) + np.random.normal(0, 1, xtrain.shape) * \
        np.sqrt(sigma2)
    ytest= fun(xtest) + np.random.normal(0, 1, xtest.shape) * \
        np.sqrt(sigma2)
    return xtrain, ytrain, xtest, ytest

xtrain, ytrain, xtest, ytest = make_1dregression_data(n=21)

#Rescaling data
scaler = MinMaxScaler(feature_range=(-1, 1))
Xtrain = scaler.fit_transform(xtrain.reshape(-1, 1))
Xtest = scaler.transform(xtest.reshape(-1, 1))

degs = np.arange(1, 21, 1)
ndegs = np.max(degs)
mse_train = np.empty(ndegs)
mse_test = np.empty(ndegs)
ytest_pred_stored = np.empty(ndegs, dtype=np.ndarray)
ytrain_pred_stored = np.empty(ndegs, dtype=np.ndarray)
for deg in degs:
    model = LinearRegression()
    poly_features = PolynomialFeatures(degree=deg, include_bias=False)
    Xtrain_poly = poly_features.fit_transform(Xtrain)
    model.fit(Xtrain_poly, ytrain)
    ytrain_pred = model.predict(Xtrain_poly)
    ytrain_pred_stored[deg-1] = ytrain_pred
    Xtest_poly = poly_features.transform(Xtest)
    ytest_pred = model.predict(Xtest_poly)
    mse_train[deg-1] = mse(ytrain_pred, ytrain)
    mse_test[deg-1] = mse(ytest_pred, ytest)
    ytest_pred_stored[deg-1] = ytest_pred

# Plot MSE vs degree
fig, ax = plt.subplots()
mask = degs <= 15
ax.plot(degs[mask], mse_test[mask], color = 'r', marker = 'x',label='test')
ax.plot(degs[mask], mse_train[mask], color='b', marker = 's', label='train')
ax.legend(loc='upper right', shadow=True)
plt.xlabel('degree')
plt.ylabel('mse')
save_fig('polyfitVsDegree.pdf')
plt.show()

# Plot fitted functions
chosen_degs = [1, 2, 14, 20]
for deg in chosen_degs:
    fig, ax = plt.subplots()
    ax.scatter(xtrain, ytrain)
    ax.plot(xtest, ytest_pred_stored[deg-1])
    ax.set_ylim((-10, 15))
    plt.title('degree {}'.format(deg))
    save_fig('polyfitDegree{}.pdf'.format(deg))
    plt.show()

# Plot residuals
#https://blog.minitab.com/blog/adventures-in-statistics-2/why-you-need-to-check-your-residual-plots-for-regression-analysis
chosen_degs = [1, 2, 14, 20]
for deg in chosen_degs:
    fig, ax = plt.subplots()
    ypred =  ytrain_pred_stored[deg-1]
    residuals = ytrain - ypred
    ax.plot(ypred, residuals, 'o')
    ax.set_xlabel('predicted y')
    ax.set_ylabel('residual')
    plt.title('degree {}. Predictions on the training set'.format(deg))
    save_fig('polyfitDegree{}Residuals.pdf'.format(deg))
    plt.show()

# Plot fit vs actual
# https://blog.minitab.com/blog/adventures-in-statistics-2/regression-analysis-how-do-i-interpret-r-squared-and-assess-the-goodness-of-fit
chosen_degs = [1, 2, 14, 20]
for deg in chosen_degs:
    for train in [True, False]:
        if train:
            ytrue = ytrain
            ypred = ytrain_pred_stored[deg-1]
            dataset = 'Train'
        else:
            ytrue = ytest
            ypred = ytest_pred_stored[deg-1]
            dataset = 'Test'
        fig, ax = plt.subplots()
        ax.scatter(ytrue, ypred)
        ax.plot(ax.get_xlim(), ax.get_ylim(), ls="--", c=".3")
        ax.set_xlabel('true y')
        ax.set_ylabel('predicted y')
        r2 = sklearn.metrics.r2_score(ytrue, ypred)
        plt.title('degree {}. R2 on {} = {:0.3f}'.format(deg, dataset, r2))
        save_fig('polyfitDegree{}FitVsActual{}.pdf'.format(deg, dataset))
        plt.show()


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

#calculate the Sum of Squared Error
def error(m, b, data):
    sum_of_squares = 0
    for i in range(len(data)):
        x = data[i, 0]
        y = data[i, 1]
        sum_of_squares += (y-(m*x + b))**2
    return sum_of_squares/len(data)

def gradient(m, b, data):
    m_final = 0
    b_final = 0
    N = len(data)
    for i in range(len(data)):
        x = data[i, 0]
        y = data[i, 1]
        b_final += -(2/N)*(y-(m*x+b))
        m_final += -(2/N)*x*(y-(m*x+b))
    return m_final, b_final

def gradient_descent(m, b, iterations, learning_rate, data):
    for i in range(iterations):
        m_final, b_final = gradient(m, b, data)
        m = m - (learning_rate*m_final)
        b = b - (learning_rate*b_final)
    return m, b

def start():
    pdata = pd.read_csv("../datasets/lactic.csv", usecols=['X', 'Y'])
    pdata = np.array(pdata)

    #learning conditions
    learning_rate = 0.001
    iterations = 1000

    #equation mx + b
    m0 = 0            #initial values of coefficients
    b0 = 0

    input_val = []
    output_val = []
    for i in range(len(pdata)):
        input_val.append(pdata[i,0])
        output_val.append(pdata[i,1])

    print("Initial value of coefficients y=mx+b:\n "
          "m = {0} b = {1} and error is {2} \n".format(m0, b0, error(m0, b0, pdata)))

    print("Gradient descent...\n")
    m, b = gradient_descent(m0, b0, iterations, learning_rate, pdata)

    print("Final value of coefficients y=mx+b:\n "
          "m = {0} b = {1} and error is {2}".format(m, b, error(m, b, pdata)))

    #not the best way to represent fit line
    #representation of linear regression results
    y_vals = [m*x+b for x in range(20)]
    x_vals = range(20)

    plt.scatter(input_val, output_val)
    plt.xlabel("Input")
    plt.ylabel("Output")
    plt.plot(x_vals, y_vals,color='blue', linewidth = 3)
    plt.show()

if __name__ == '__main__':
    start()


from numpy import array, zeros, float, dot
from copy import copy

def GaussJordan(A,b):

    n,m = A.shape
    C = zeros((n,m+1),float)
    C[:,0:n],C[:,n] = A, b

    for j in range(n):
        # partial pivoting.
        p = j
        # look for alternate pivot by searching for largest element in column
        for i in range(j+1,n):
            if abs(C[i,j]) > abs(C[p,j]): p = i
        if abs(C[p,j]) < 1.0e-16:
            print "matrix is singular" # its determinant is 0.
            return b
        # swap rows to get largest magnitude element on the diagonal
        C[p,:],C[j,:] = copy(C[j,:]),copy(C[p,:])

        pivot = C[j,j]
        C[j,:] = C[j,:] / pivot
        for i in range(n):
            if i == j: continue
            C[i,:] = C[i,:] - C[i,j]*C[j,:]
    I,x = C[:,0:n],C[:,n]
    return x


from sympy import ( symbols, solve, diff, integrate, exp, sqrt, lambdify, Integral, ln, pprint, oo )

# The clotting time of blood (in seconds) is a random variable with probability density function defined by

x = symbols( 'x' )
F = 1 / ( ln( 27 ) * x )

# for x in [1, 27].

a, b = 1, 27

def expected_value( f, var, a, b ):
	return integrate( var * f, ( var, a, b ) )

def variance( f, var, a, b, mu ):
	return integrate( ( ( var **2 ) * f ), ( var, a, b ) ) - mu**2

def std_dev( var ):
	return sqrt( var )

def expected_value( f, var, a, b ):
	return integrate( var * f, ( var, a, b ) )

mu = round( expected_value( F, x, a, b ).evalf(), 2 )
mu

# Find the standard deviation of the distribution.
var = round( variance( F, x, a, b, mu ).evalf(), 2 )
var

stdev = round( std_dev( var ), 2 )
stdev

# ind the probability that the value of the random variable is within one standard deviation of the mean.
prob_within_1sd = integrate( F, ( x, mu - stdev, mu + stdev ) )
round( prob_within_1sd.evalf(), 2 )

# Find the median clotting time.
mean = exp( ( ln( 27 ) /2 ).evalf() )
round( mean, 2 )


"""
 Purpose: The main purpose is to demonstrate how to find the running median, mode and
          mean over a sequence (list) of integers or reals or a mix of integers and reals.
          The secondary purpose, is to inspire Python programmers to explore some of
          the powerful packages (e.g. collections) available to the Python community and
          to learn more about list comprehension and lambda functions.
    Note:
       1. Much of the code here has been taken from code posted to the web (e.g. stackoverflow)
          by other Python programmers (e.g. Peter Otten)

  Author: V. Stokes (vs@it.uu.se)
 Version: 2013.03.06

"""
import numpy as np

#*******************************************************

from collections import deque,Counter
from bisect import insort, bisect_left
from itertools import islice

def RunningMode(seq,N,M):
    """
    Purpose: Find the mode for the points in a sliding window as it
             is moved from left (beginning of seq) to right (end of seq)
             by one point at a time.
     Inputs:
          seq -- list containing items for which a running mode (in a sliding window) is
                 to be calculated
            N -- length of sequence
            M -- number of items in window (window size) -- must be an integer > 1
     Otputs:
        modes -- list of modes with size M - N + 1
       Note:
         1. The mode is the value that appears most often in a set of data.
         2. In the case of ties it the last of the ties that is taken as the mode (this
            is not by definition).
    """
    # Load deque with first window of seq
    d = deque(seq[0:M])

    modes = [Counter(d).most_common(1)[0][0]]  # contains mode of first window

    # Now slide the window by one point to the right for each new position (each pass through
    # the loop). Stop when the item in the right end of the deque contains the last item in seq
    for item in islice(seq,M,N):
        old = d.popleft()                      # pop oldest from left
        d.append(item)                         # push newest in from right
        modes.append(Counter(d).most_common(1)[0][0])
    return modes

def RunningMedian(seq, M):
    """
     Purpose: Find the median for the points in a sliding window (odd number in size)
              as it is moved from left to right by one point at a time.
      Inputs:
            seq -- list containing items for which a running median (in a sliding window)
                   is to be calculated
              M -- number of items in window (window size) -- must be an integer > 1
      Otputs:
         medians -- list of medians with size N - M + 1
       Note:
         1. The median of a finite list of numbers is the "center" value when this list
            is sorted in ascending order.
         2. If M is an even number the two elements in the window that
            are close to the center are averaged to give the median (this
            is not by definition)
    """
    seq = iter(seq)
    s = []
    m = M // 2

    # Set up list s (to be sorted) and load deque with first window of seq
    s = [item for item in islice(seq,M)]
    d = deque(s)

    # Simple lambda function to handle even/odd window sizes
    median = lambda : s[m] if bool(M&1) else (s[m-1]+s[m])*0.5

    # Sort it in increasing order and extract the median ("center" of the sorted window)
    s.sort()
    medians = [median()]

    # Now slide the window by one point to the right for each new position (each pass through
    # the loop). Stop when the item in the right end of the deque contains the last item in seq
    for item in seq:
        old = d.popleft()          # pop oldest from left
        d.append(item)             # push newest in from right
        del s[bisect_left(s, old)] # locate insertion point and then remove old
        insort(s, item)            # insert newest such that new sort is not required
        medians.append(median())
    return medians

def RunningMean(seq,N,M):
    """
     Purpose: Find the mean for the points in a sliding window (fixed size)
              as it is moved from left to right by one point at a time.
      Inputs:
          seq -- list containing items for which a mean (in a sliding window) is
                 to be calculated (N items)
            N -- length of sequence
            M -- number of items in sliding window
      Otputs:
        means -- list of means with size N - M + 1

    """
    # Load deque (d) with first window of seq
    d = deque(seq[0:M])
    means = [np.mean(d)]             # contains mean of first window
    # Now slide the window by one point to the right for each new position (each pass through
    # the loop). Stop when the item in the right end of the deque contains the last item in seq
    for item in islice(seq,M,N):
        old = d.popleft()            # pop oldest from left
        d.append(item)               # push newest in from right
        means.append(np.mean(d))     # mean for current window
    return means

#*** Start of test area *****************************************************
#
# Set random seed for repeatability
#np.random.seed(7919) # the 1000th prime number

# Try the following sequences
#yn = np.random.random(18)*10 - 5
yn = [3,2,-1.0,2.0,3.0,5.0,-5.0,6.0,-5.0,4.0,9.0,6.3,1.3,0.0,-7.0,1.3,-5.0]
#yn = [3,2,-1.0,-1.0,-1.0,5.0,5.0]
#yn = [5,5.0,5,5.0,5]
#yn = [3,3,3,3,3,3,3]
#yn = [3.,3.,3.,3.,3.,3.,3.]
#yn = [-1,1,-1,1,-1,1]
#yn = [3,2,-1.0,2.0]
#yn = [5,3,2,2,-1]
#yn = [-5.0,3.0,2.0,2.0,-1.0]

N = len(yn)

# Try the follwing window sizes
#M = 1
#M = 2
M = 3
#M = 4
#M = 5
#M = 6
if M <= N and M >= 1:
    print 'M = %2d,'%M,
    print 'yn:'
    print yn

    means = RunningMean(yn,N,M)
    print ' Means(%d):' %(N-M+1)
    print means

    medians = RunningMedian(yn,M)
    print ' Medians(%d):' %(N-M+1)
    print medians

    modes = RunningMode(yn,N,M)
    print ' Modes(%d):' %(N-M+1)
    print modes
else:
    print 'Window size (M=%d) out of range'%M


"""
Author: Ali Hajimirza (ali@alihm.net)
Copyright Ali Hajimirza, free for use under MIT license.
"""
import numpy as np

# Exponent function for estimating e
exponent_func = np.vectorize(lambda x, std: np.exp((-1.0*x)/(2 * std)))

# Performs the E step give a list of x values, a list of means and a matrix of estimated values
def E_step(x_list, mean_list, e_matrix):
	# Compute the standard deviation
	std_list = get_std(x_list, mean_list, e_matrix)
	estimated = list()
	for i,std in enumerate(std_list):
		estimated.append(exponent_func(np.square(x_list - mean_list[i]), std))
	# Calculating denominator
	estimated = np.array(estimated).transpose()
	for i, n in enumerate(estimated):
		estimated[i] /= n.sum()
	return estimated

# Performs the M step give a list of x values, a list of means
def M_step(x_list, e_matrix):
	# Calculating numerator
	numerator = np.dot(x_list, e_matrix)
	# Calculating denominator
	denominator = e_matrix.sum(axis=0)
	return np.divide(numerator, denominator)

# Compute the standard deviation
def get_std(x_list, mean_list, e_matrix):
	x_vector = x_list[np.newaxis].transpose()
	var = np.square(mean_list - x_vector) * e_matrix
	return np.sqrt(var.sum(axis=0)/e_matrix.sum(axis=0))

# Computes theta
def get_theta(e_matrix):
	return e_matrix.sum(axis=0) / len(e_matrix)

# Performs E-M for a number of steps
def simulate_E_M(x_list, e_matrix ,steps):
	mean_matrix = list()
	for i in xrange(steps):
		mean_list = M_step(x_list, e_matrix)
		mean_matrix.append(mean_list)
		e_matrix  = E_step(x_list, mean_list, e_matrix)
	return np.array(mean_matrix).transpose()


# Disable debbuging logs (to get rid of cuda warnings)
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

import numpy as np

'''
Using the convolution operation between two arrays.
Check: the equation of convultion
'''
# h = [2, 1, 0]
# x = [3, 4, 5]

# y = np.convolve(x, h)
# print(y); # [6, 11, 14, 5, 0]

'''
Now we will experimante with methods of applying a kernel on the matrix.
'''

# 1) Padding(full) method

'''
Think of the kernel as a sliding window.
We have to come with the solution of padding zeros
on the input array. This is a very famous implementation and
will be easier to show how it works with a simple example
'''

# import numpy as np

# x = [6, 2]
# h = [1, 2, 5, 4]

# y = np.convolve(x, h, "full")  #now, because of the zero padding, the final dimension of the array is bigger
# print(y) # 6 14 34 34 8

# 2) Padding (same)

'''
In this approach, we just add the zero to the left (and top of the matrix in 2D).
That is, only the first 4 steps of "full" method.
'''

import numpy as np

# x = [6, 2]
# h = [1, 2, 5, 4]

# y = np.convolve(x, h, "same")  # it is same as zero padding, but with returns an ouput with the same length as max of x or h
# print(y) # 6 14 34 34

# 3) No padding (valid)

'''
In the last case we only applied the kernel when we had
a comptaible position on the h array, in some cases you
want a dimensionality reduction. For this purpose, we ignore the
steps that would need padding (zeros before and after the array)
'''

x = [6, 2]
h = [1, 2, 5, 4]

y = np.convolve(x, h, "valid")
print(y) # 14 34 34

'''
Valid returns output of length max(x, h) - min(x, h) + 1
This is to ensure that values outside of the boundary of 'h'
will not be used in the calculation of the convultion.
In the next example we will understand why we used the argument valid.
'''


import numpy as np
from checker.fault import get_fault

np.set_printoptions(suppress=True)

def seidel(matrix_a: list, vector_b: list, vector_x: list) -> list:
    """
    Seidel algorithm part
    :param matrix_a: start matrix
    :param vector_b: start vector
    :param vector_x: solution vector
    :return:
    """
    x_ = vector_x.copy()
    n = len(matrix_a)
    for j in range(0, n):
        d = vector_b[j]

        for i in range(0, n):
            if j != i:
                d -= matrix_a[j][i] * x_[i]
        x_[j] = d / matrix_a[j][j]
    return x_

def solve(matrix_a: list, vector_b: list, vector_x: list, eps=10 ** (-6)) -> list:
    """
    Main function
    :param matrix_a: start matrix
    :param vector_b: start vector
    :param vector_x: solution vector
    :param eps: epsilon for comparing
    :return:
    """
    iterations = 0
    tmp = 0
    while True:
        errors = []
        new_x = seidel(matrix_a, vector_b, vector_x)
        for i in range(len(vector_x)):
            errors.append(abs(new_x[i] - vector_x[i]))
        vector_x = new_x
        if max(errors) < eps:
            print(f'Last result: {vector_x}')
            break
        else:
            if tmp < 3:
                print(f'Temporary result: {vector_x}')
                tmp += 1
            print(f'Residual vector: {np.matrix(np.subtract(vector_b, np.dot(matrix_a, vector_x)), float)}')
            iterations += 1
    print(f'Iterations: {iterations}')
    return vector_x

a = [[4.4944, 0.1764, 1.7956, 0.7744],
     [0.1764, 15.6025, 3.4969, 0.1849],
     [1.7956, 3.4969, 8.8804, 0.2116],
     [0.7744, 0.1849, 0.2116, 19.7136]]
b = [31.97212, 9.18339, 19.51289, 51.39451]
x = [0 for _ in range(len(a[0]))]
x = solve(a.copy(), b.copy(), x.copy())
print(f'Our solution: {x}')
x_np = np.linalg.solve(a, b)
print(f'NumPy solution: {x_np}')
print(f'Residual vector: {np.matrix(np.subtract(b, np.dot(a, x)), int)}')
print(f'Residual vector for NumPy: {np.matrix(np.subtract(b, np.dot(a, x_np)), int)}')
print('Fault:', round(get_fault(x, x_np), 6))


# calculate error
import numpy as np

# mean square error
def mean_square_error(pred, target):
    """
    Determine mean square error.
    f(y_t, y) = sum((y_t-y)**2)/n
        where, y_t = predicted value
                y  = target value
                n = number of values

    :param pred: {array}, shape(n_samples,)
            predicted values.
    :param target: {array}, shape(n_samples,)
            target values.
    :return: mean square error.
    """
    error = pred - target
    square_error = error * error
    return np.mean(square_error)

# mean absolute error
def mean_absolute_error(pred, target):
    """
    Determine mean absolute error.
    f(y_t, y) = sum(abs(y_t-y))/n
        where, y_t = predicted value
                y  = target value
                n = number of values

    :param pred: {array}, shape(n_samples,)
            predicted values.
    :param target: {array}, shape(n_samples,)
            target values.
    :return: mean absolute error.
    """
    abs_error = np.abs(pred - target)
    return np.mean(abs_error)

# mean log cosh error
def mean_log_cosh_error(pred, target):
    """
    Determine mean log cosh error.
    f(y_t, y) = sum(log(cosh(y_t-y)))/n
        where, y_t = predicted value
                y  = target value
                n = number of values

    :param pred: {array}, shape(n_samples,)
            predicted values.
    :param target: {array}, shape(n_samples,)
            target values.
    :return: mean log cosh error.
    """
    error = pred - target
    return np.mean(np.log(np.cosh(error)))

# log loss function
def log_loss(pred, target):
    """
    Determine mean log loss function.
    :param pred: {array}, shape{n_samples,}
            predicted values.
    :param target: {array}, shape{n_samples,}
            target values.
    :return: mean log loss error.
    """
    pred[pred == 0] = 0.00001
    pred[pred == 1] = 0.99999
    return -np.mean((target*np.log(pred) + (1-target)*np.log(1-pred)))

errors = {"mean_square_error": mean_square_error, "mean_absolute_error": mean_absolute_error,
          "mean_log_cosh_error": mean_log_cosh_error, "log_loss": log_loss}


import numpy as np
import scipy.stats as sp

def cosd(angle):

    return np.cos(np.pi * angle/180)

def sind(angle):

    return np.sin(np.pi * angle/180)

def tand(angle):

    return np.tan(np.pi * angle/180)

def arctand(angle):

    return np.arctan(angle) * 180/np.pi

def cart2pol(x, y):

    rho = np.sqrt(x**2 + y**2)
    phi = np.arctan2(y, x)

    return phi, rho

def pol2cart(phi, rho):

    x = rho * np.cos(phi)
    y = rho * np.sin(phi)

    return x, y

def iqr(data):
    """This function computes the iqr consistent with Matlab"""

    # If 2-D array use only 1st row
    if len(data.shape) > 1:
        data_1d = data.flatten()
    else:
        data_1d = data

    # Remove nan elements
    idx = np.where(np.isnan(data_1d) == False)[0]
    data_1d = data_1d[idx]

    # Compute statistics
    q25, q50, q75 = sp.mstats.mquantiles(data_1d, alphap=0.5, betap=0.5)
    sp_iqr = q75 - q25
    return sp_iqr

def azdeg2rad(angle):
    direction = np.deg2rad(90-angle)
    idx = np.where(direction < 0)[0]
    if len(idx) > 0:
        direction[idx] = direction[idx] + 2 * np.pi

    return direction

def rad2azdeg(angle):
    if isinstance(angle, float):
        deg = np.rad2deg(angle)
        deg = 90 - deg
        if deg < 0:
            deg += 360

        return deg
    else:
        # Multiple values
        deg = np.rad2deg(angle)
        deg = 90 - deg
        sub_zero = np.where(deg < 0)
        deg[sub_zero] = deg[sub_zero] + 360

        return deg

def nandiff(values):

    final_values = []
    for n in range(len(values) - 1):

        if np.isnan(values[n]):
            final_values.append(np.nan)
        else:
            i = n + 1
            while np.isnan(values[i]) and i < len(values) - 1:
                i += 1

            final_values.append(values[i] - values[n])

    return np.array(final_values)

def get_object_values(list_in, item, checked=None):
    if checked is not None:
        working_list = list_in[checked is True]
    else:
        working_list = list_in

    if working_list is list:
        out = []
        for obj in working_list:
            temp = getattr(obj, item)
            out.append(temp)
    else:
        out = getattr(working_list, item)
    return np.array(out)

def sontek_3d_arrange(data_in):
    r1 = np.squeeze(data_in[:, 0, :])
    r2 = np.squeeze(data_in[:, 1, :])
    r3 = np.squeeze(data_in[:, 2, :])
    r4 = np.squeeze(data_in[:, 3, :])
    new_array = np.array([r1, r2, r3, r4])
    return new_array

def valid_number(data_in):
    """Check to see if data_in can be converted to float.

    Parameters
    ----------
    data_in: str
        String to be converted to float

    Returns
    -------
    data_out: float
        Returns a float of data_in or nan if conversion is not possible
    """

    try:
        data_out = float(data_in)
    except ValueError:
        data_out = np.nan
    return data_out

def nans(shape, dtype=float):
    a = np.empty(shape, dtype)
    a.fill(np.nan)
    return a


import numpy as np

# Entropy and Gini Calculations
def entropy(y_labels):
    _, fraud_count = np.unique(y_labels, return_counts=True)
    p_i = fraud_count / fraud_count.sum() # probability (array) of each class
    entropy = np.sum(p_i * -np.log2(p_i))
    return entropy

def gini(y_labels):
    _, fraud_count = np.unique(y_labels, return_counts=True)
    p_i = fraud_count / fraud_count.sum() # probability (array) of each class
    gini = 1 - np.sum(p_i**2)
    return gini

def total_entropy(partition_0, partition_1):
    n = len(partition_0) + len(partition_1)
    prob_part0 = len(partition_0) / n # probability of partition 0
    prob_part1 = len(partition_1) / n # probabiltiy of partition 1
    tot_entropy = (prob_part0 * entropy(partition_0)
        + prob_part1 * entropy(partition_1) )
    return tot_entropy


import numpy
def zscore(X, m=None, s=None, in_place=False, limit_memory=False):
	if (m is not None) and (s is not None):
		if in_place:
			for i in range(X.shape[0]):
				for j in range(X.shape[1]):
					X[i, j] = (X[i, j] - m[j]) / s[j]
			return (X, m, s)
		return ((X - m)/s, m, s)
	if limit_memory:
		m, s = compute_mean_std_limit_space(X)
	else:
		m = numpy.mean(X, axis=0)
		s = numpy.std(X, axis=0)
	return zscore(X, m, s, in_place)

def compute_mean_std_limit_space(X):
	m = numpy.mean(X, axis=0)
	s = numpy.zeros_like(m)

	for j in range(X.shape[1]):
		for i in range(X.shape[0]):
			s[j] += (X[i,j] - m[j]) ** 2
		s[j] /= X.shape[0]
	s = numpy.sqrt(s)

	return (m, s)

# if __name__ == '__main__':
# 	t = [[0.8147, 0.1576, 0.6557, 0.7060],
# 		[0.9058, 0.9706, 0.0357, 0.0318],
# 		[0.1270, 0.9572, 0.8491, 0.2769],
# 		[0.9134, 0.4854, 0.9340, 0.0462],
# 		[0.6324, 0.8003, 0.6787, 0.0971],
# 		[0.0975, 0.1419, 0.7577, 0.8235],
# 		[0.2785, 0.4218, 0.7431, 0.6948],
# 		[0.5469, 0.9157, 0.3922, 0.3171],
# 		[0.9575, 0.7922, 0.6555, 0.9502],
# 		[0.9649, 0.9595, 0.1712, 0.0344]]
# 	t = numpy.asarray(t)

# 	X, m, s = zscore(t)
# 	print X
# 	print m
# 	print s

# 	# X, m, s = zscore(t, in_place=True)
# 	# print X
# 	# print m
# 	# print s

# 	X, m, s = zscore(t, limit_memory=True)
# 	print X
# 	print m
# 	print s

# 	# X, m, s = zscore(t, in_place=True, limit_memory=True)
# 	# print X
# 	# print m
# 	# print s


# -*- coding: utf-8 -*-
"""
Linear Regression using two models:

There are few data files each of them having a set of values for two variables (say x & y) in the folder, one
pair per line in the data file. We are trying to determine if there is a linear relationship between
them.

• Regression functions are in a module called regression_functions.py.

• The two techniques used in the program are (1) Method of Least Squares that gives us a
Coefficient of Determination about the linearity of the relationship and also allows us try and fit a
line along a possible linear path (2) Pearson technique which gives us a Correlation Coefficient
about the linearity of the relationship

• The four data files are in1, in2, in3 and in4. Run the program against a data file and observe the
results

conditions:

• In order to plot the relationship of x and y, do the following:
• (1) Plot x vs y as a scatter plot (2) Plot x vs f(x) as a line plot
• Apply appropriate text in the plot for axes, title, as well as the coefficient of determination
(from Least Squares) and Pearson coefficient (from Pearson). Your plots should also
include meaningful information infer whether or not the visualization and numerical results agree with each
other.

@author: amith
"""
import regression_functions as regress
import matplotlib.pyplot as plt
import numpy as np

file1 = open('in1.txt', 'r')
r_file1 = file1.readlines()

file2 = open('in2.txt', 'r')
r_file2 = file2.readlines()

file3 = open('in3.txt', 'r')
r_file3 = file3.readlines()

file4 = open('in4.txt', 'r')
r_file4 = file4.readlines()

file_list = [r_file1, r_file2, r_file3, r_file4]

def main():

    for f in file_list:
        print ('Input File: ')
        x, y = np.loadtxt(f, delimiter=",", unpack=True, encoding="utf-8-sig")
        m, b = regress.compute_m_and_b(x, y)
        fx, residual = regress.compute_fx_residual(x, y, m, b)
        least_squares_r = regress.compute_sum_of_squared_residuals(residual)
        sum_squares = regress.compute_total_sum_of_squares(y)
        print()

        print("Data points: ", len(x))
        print("Least Squares Method")
        print("--------------------")
        print("Coefficients: m =", "%8.6f" % m, "\tb =", "%8.6f" % b)

        print("Sum of Squared Residuals: %12.6f" % least_squares_r)
        print("Total Sum of Squares: %12.6f" % sum_squares)
        coeff_of_determination = (1 - (least_squares_r/sum_squares))
        print("Coefficient of determination: %12.6f" % coeff_of_determination)
        print()
        pearson_r = regress.compute_pearson_coefficient(x, y)
        print("Pearson Method")
        print("--------------")
        print("Pearson Correlation Coefficient: %12.6f" % pearson_r)
        print("Predicted value of y (for x=80): %8.2f" % (m*80 + b))
        plt.figure()
        plt.scatter(x, y, color='red')
        plt.plot(x, fx, 'b')

    return

main()
plt.show()


import numpy as np

def power_iteration(
    input_matrix: np.ndarray,
    vector: np.ndarray,
    error_tol: float = 1e-12,
    max_iterations: int = 100,
) -> tuple[float, np.ndarray]:
    """
    Power Iteration.
    Find the largest eigenvalue and corresponding eigenvector
    of matrix input_matrix given a random vector in the same space.
    Will work so long as vector has component of largest eigenvector.
    input_matrix must be either real or Hermitian.

    Input
    input_matrix: input matrix whose largest eigenvalue we will find.
    Numpy array. np.shape(input_matrix) == (N,N).
    vector: random initial vector in same space as matrix.
    Numpy array. np.shape(vector) == (N,) or (N,1)

    Output
    largest_eigenvalue: largest eigenvalue of the matrix input_matrix.
    Float. Scalar.
    largest_eigenvector: eigenvector corresponding to largest_eigenvalue.
    Numpy array. np.shape(largest_eigenvector) == (N,) or (N,1).

    >>> import numpy as np
    >>> input_matrix = np.array([
    ... [41,  4, 20],
    ... [ 4, 26, 30],
    ... [20, 30, 50]
    ... ])
    >>> vector = np.array([41,4,20])
    >>> power_iteration(input_matrix,vector)
    (79.66086378788381, array([0.44472726, 0.46209842, 0.76725662]))
    """

    # Ensure matrix is square.
    assert np.shape(input_matrix)[0] == np.shape(input_matrix)[1]
    # Ensure proper dimensionality.
    assert np.shape(input_matrix)[0] == np.shape(vector)[0]
    # Ensure inputs are either both complex or both real
    assert np.iscomplexobj(input_matrix) == np.iscomplexobj(vector)
    is_complex = np.iscomplexobj(input_matrix)
    if is_complex:
        # Ensure complex input_matrix is Hermitian
        assert np.array_equal(input_matrix, input_matrix.conj().T)

    # Set convergence to False. Will define convergence when we exceed max_iterations
    # or when we have small changes from one iteration to next.

    convergence = False
    lamda_previous = 0
    iterations = 0
    error = 1e12

    while not convergence:
        # Multiple matrix by the vector.
        w = np.dot(input_matrix, vector)
        # Normalize the resulting output vector.
        vector = w / np.linalg.norm(w)
        # Find rayleigh quotient
        # (faster than usual b/c we know vector is normalized already)
        vectorH = vector.conj().T if is_complex else vector.T
        lamda = np.dot(vectorH, np.dot(input_matrix, vector))

        # Check convergence.
        error = np.abs(lamda - lamda_previous) / lamda
        iterations += 1

        if error <= error_tol or iterations >= max_iterations:
            convergence = True

        lamda_previous = lamda

    if is_complex:
        lamda = np.real(lamda)

    return lamda, vector

def test_power_iteration() -> None:
    """
    >>> test_power_iteration()  # self running tests
    """
    real_input_matrix = np.array([[41, 4, 20], [4, 26, 30], [20, 30, 50]])
    real_vector = np.array([41, 4, 20])
    complex_input_matrix = real_input_matrix.astype(np.complex128)
    imag_matrix = np.triu(1j * complex_input_matrix, 1)
    complex_input_matrix += imag_matrix
    complex_input_matrix += -1 * imag_matrix.T
    complex_vector = np.array([41, 4, 20]).astype(np.complex128)

    for problem_type in ["real", "complex"]:
        if problem_type == "real":
            input_matrix = real_input_matrix
            vector = real_vector
        elif problem_type == "complex":
            input_matrix = complex_input_matrix
            vector = complex_vector

        # Our implementation.
        eigen_value, eigen_vector = power_iteration(input_matrix, vector)

        # Numpy implementation.

        # Get eigenvalues and eigenvectors using built-in numpy
        # eigh (eigh used for symmetric or hermetian matrices).
        eigen_values, eigen_vectors = np.linalg.eigh(input_matrix)
        # Last eigenvalue is the maximum one.
        eigen_value_max = eigen_values[-1]
        # Last column in this matrix is eigenvector corresponding to largest eigenvalue.
        eigen_vector_max = eigen_vectors[:, -1]

        # Check our implementation and numpy gives close answers.
        assert np.abs(eigen_value - eigen_value_max) <= 1e-6
        # Take absolute values element wise of each eigenvector.
        # as they are only unique to a minus sign.
        assert np.linalg.norm(np.abs(eigen_vector) - np.abs(eigen_vector_max)) <= 1e-6

if __name__ == "__main__":
    import doctest

    doctest.testmod()
    test_power_iteration()


#For Python 3

import numpy as np
import matplotlib.pyplot as plt

N = 1000 #global variable; number of "partitions" for the interval [-2,2]

def f(x,y):
    """
    Takes in two numpy arrays that are result of meshgrid.
    Returns a numpy array with points representing the iteration number for divergence
    """
    max_iter = 100 #maximum number of interations
    c = x + 1j*y
    z = np.zeros((N,N),dtype=complex)
    r = np.zeros((N,N),dtype=int) #return
    mask = np.full((N,N), True, dtype=bool)
    for i in range(0,max_iter,1):
        z[mask] = z[mask]**2 + c[mask]  #z_i = z_i-1**2 + c
        r[mask] = i #i is the iteration number at which point escapes (diverges)
        #if point ever becomes larger than 2, the sequence will escape to infinity:
        #https://en.wikipedia.org/wiki/Mandelbrot_set#Basic_properties
        mask[np.abs(z) > 2] = False #points that diverge
    return r, mask

def plot_set(bounds,plot_binary):
    """
    Plots the Mandelbrot set with the given bounds
    INPUT:
    ::array:: bounds            #bounds of the plot
    ::boolean:: plot_binary     #whether or not to plot binary (for colour plot, send False)
    """
    x = np.linspace(bounds[0], bounds[1], N)
    y = np.linspace(bounds[2], bounds[3], N)
    #https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html
    xv, yv = np.meshgrid(x,y)
    vals, mandelbrot = f(xv, yv)
    vals = np.flip(vals,0) #flip array vertically
    mandelbrot = np.flip(mandelbrot,0)

    font = {'fontname':'Times New Roman'} #https://stackoverflow.com/a/21323217
    plt.figure(dpi=100)
    plt.title('Mandelbrot Set')
    plt.xticks(np.arange(0,N+1,N/4),np.arange(bounds[0],bounds[1]+1,(bounds[1]-bounds[0])/4)) #careful: arange works as [start, stop)
    plt.yticks(np.arange(0,N+1,N/4),np.arange(bounds[3],bounds[2]-1,(bounds[2]-bounds[3])/4))
    plt.xlabel('Re',**font)
    plt.ylabel('Im',**font)
    if plot_binary == False:
        #for colormaps see https://matplotlib.org/examples/color/colormaps_reference.html
        plt.imshow(vals, cmap='plasma_r') #_r to reverse colormap https://stackoverflow.com/a/3280732
        cbar = plt.colorbar()
        cbar.set_label('Number of Iterations for Divergence')
    else:
        plt.imshow(mandelbrot, cmap=plt.cm.gray)
    plt.show()
    return True

def plot_set_alternate(bounds):
    """
    Similar to plot_set() but instead uses matplotlib.pyplot.contourf()
    """
    x = np.linspace(bounds[0], bounds[1], N)
    y = np.linspace(bounds[2], bounds[3], N)
    #https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html
    xv, yv = np.meshgrid(x,y)
    vals, mandelbrot = f(xv, yv)

    font = {'fontname':'Times New Roman'} #https://stackoverflow.com/a/21323217
    plt.figure(dpi=100)
    plt.title('Mandelbrot Set')
    plt.contourf(xv, yv, vals, levels=100, cmap='plasma_r')
    plt.xlabel('Re',**font)
    plt.ylabel('Im',**font)
    plt.axes().set_aspect('equal')
    plt.colorbar(label='Number of Iterations for Divergence')
    plt.show()

    return True

def main():
    bounds1 = [-2,2,-2,2] #[xmin,xmax,ymin,ymax]
    bounds2 = [-2,0,-1,1]
    plot_set_alternate(bounds1)
    plot_set(bounds2, False)
    plot_set(bounds1, True)
    plot_set(bounds2, True)
    return True

if __name__ == "__main__":
    main()


import random
import matplotlib.pyplot as plt
from scipy.stats import poisson
from scipy.stats import norm

# Helper function
def Poisson_value(mu):
    r = random.uniform(0, 1)
    values = list()
    for i in range(1000):
        values.append(poisson.pmf(i,mu))
        if sum(values) > r: break
    return  len(values)-1

# Data gen
N = 10000
mu1 = [ Poisson_value(1.0)  for i in range(N)]
mu10_3 =  [ Poisson_value(10.3)  for i in range(N)]
mu102_1 = [ Poisson_value(102.1) for i in range(N)]

# Interpolation

def extract(data):
    counts = dict()
    for i in mu1:
        counts[i] = counts.get(i, 0) + 1
    data = sorted(counts.items())
    x_vals = [ v[0] for v in data ]
    y_vals = [ v[1]/N for v in data ]
    return x_vals , y_vals

### Figure 1
plot1 = plt.figure(1)
data = mu1
x , y = extract(data)
plt.hist(data, density = 1, bins = (max(x)+1), rwidth = 0.85 )

mu , std = norm.fit(data)
p = norm.pdf(range(int(mu-4*std), int(mu+4*std)),mu,std)
plt.plot(range(int(mu-4*std), int(mu+4*std)), p)

plt.xlabel("Values")
plt.ylabel("Probability")
plt.title("Poisson deviates for $\mu=1.0$")
plt.legend(["Fit", "Normalized Data"])

### Figure 2
plot2 = plt.figure(2)
data = mu10_3
x , y = extract(data)
plt.hist(data, density = 1, bins = (max(x)+1), rwidth = 0.85 )

mu , std = norm.fit(data)
p = norm.pdf(range(int(mu-4*std), int(mu+4*std)),mu,std)
plt.plot(range(int(mu-4*std), int(mu+4*std)), p)

plt.xlabel("Values")
plt.ylabel("Probability")
plt.title("Poisson deviates for $\mu=10.3$")
plt.legend(["Normal Fit", "Normalized Data"])

### Figure 3
plot3 = plt.figure(3)
data = mu102_1
x , y = extract(data)
plt.hist(data, density = 1, bins = (max(x)+1), rwidth = 0.85 )

mu , std = norm.fit(data)
p = norm.pdf(range(int(mu-4*std), int(mu+4*std)),mu,std)
plt.plot(range(int(mu-4*std), int(mu+4*std)), p)

plt.xlabel("Values")
plt.ylabel("Probability")
plt.title("Poisson deviates for $\mu=102.1$")
plt.legend(["Normal Fit", "Normalized Data"])

plt.show()


import numpy as np

class Bayes:
    @staticmethod
    def mean(X):
        return np.mean(X,axis=0)

    @staticmethod
    def variance(X):
        return np.mean((X-Bayes.mean(X))**2,axis=0)

    def gaussian(self,x,avg,var):
        return (1./np.sqrt(2*np.pi*var)) * np.exp(-0.5*((x-avg)**2)/var)

    def fit(self,X,y):
        c = len(set(y))
        self.c = c
        Xs = [X[y==i] for i in range(c)]

        #各个类别的均值、方差、所占比率
        self.avgs = [Bayes.mean(X) for X in Xs]
        self.vars = [Bayes.variance(X) for X in Xs]
        self.percs = [len(y[y==i])/len(y) for i in range(c)]

    def predict(self,x):
        if len(x.shape) == 1:
            result = np.array(self.percs)
            for i in range(self.c):
                gaus = self.gaussian(x,self.avgs[i],self.vars[i])
                for j in range(len(x)):
                    result[i] *= gaus[j]
            return np.argmax(result)
        results = np.array([self.predict(x[i]) for i in range(len(x))])
        return results

    def score(self,X,y):
        y_pred = self.predict(X)
        return np.sum(y_pred==y)/len(y)

if __name__=="__main__":
    from sklearn.datasets import load_iris
    from sklearn.model_selection import train_test_split
    iris = load_iris()
    X,y = iris["data"],iris["target"]
    X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=22)
    # print(len(X_train))
    # print(len(X_test))
    bayes = Bayes()
    bayes.fit(X_train,y_train)
    # print(bayes.avgs)
    # print(bayes.vars)
    print(bayes.score(X_test,y_test))

    from sklearn.naive_bayes import GaussianNB
    clf = GaussianNB()
    clf.fit(X_train, y_train)
    print(clf.score(X_test,y_test))


import numpy as np

def sigmoid(Z):

    # Sigmoid function
    # g(Z) = 1 / (1 + e^{-Z})
    # Z: shape [m,]

    # Trick to prevent possible overflow of np.exp(-Z)
    # Output will never to be exact 0
    idx = (Z < -500)
    Z[idx] = -500

    return 1.0 / (1.0 + np.exp(-Z))

class LogisticRegressor(object):

    def __init__(self, alpha, c, T = 1000, random_seed = 0, intercept = True):

        # Initialize Logistic Regression
        # alpha: learning rate.
        # c: L2 regularization strength.

        self.alpha = alpha
        self.c = c
        self.T = T
        self.random_seed = random_seed
        self.intercept = intercept

    def fit(self, X, y):

        np.random.seed(self.random_seed)

        y = y.flatten()

        if self.intercept == True:
            X = np.insert(X, 0, np.ones(X.shape[0]), axis = 1)

        # Weights initialization
        self.theta = np.random.normal(0, 0.1, X.shape[1])

        losses = list()
        losses.append(self.logistic_regression_loss(X = X, y = y, theta = self.theta, c = self.c))

        for i in range(self.T):

            self.theta = self.logistic_regression_weight_update(X = X, y = y, theta = self.theta, alpha = self.alpha, c = self.c)
            loss = self.logistic_regression_loss(X = X, y = y, theta = self.theta, c = self.c)
            losses.append(loss)

        losses = np.array(losses)

        return losses

    def logistic_regression_loss(self, X, y, theta, c):

        # Loss function for logistic regression
        # X: input feature matrix, shape [m,n].
        # y: input target value, shape [m,1].
        # c: L2 regularization strength.

        h = sigmoid(Z = X.dot(theta))

        # Trick to prevent h is exact 1 for np.log(1 - h)
        idx = (h == 1.0)
        h[idx] = 1.0 - 1e-15

        loss_mean = 1.0 / X.shape[0] * (-np.sum(y * np.log(h) + (1 - y) * np.log(1 - h))) + c * np.sum((theta ** 2))

        return loss_mean

    def logistic_regression_weight_update(self, X, y, theta, alpha, c):

        # Weight update of gradient descent for logistic regression
        # X: input feature matrix, shape [m,n].
        # y: input target value, shape [m,1].
        # alpha: learning rate.
        # c: L2 regularization strength.
        # theta: parameters for features in X, shape [n+1,1].

        # Calculate activated values
        h = sigmoid(Z = X.dot(theta))
        # Update weigths
        theta += alpha * ((1.0 / X.shape[0] * (y - h).dot(X)) - 2 * c * theta)

        return theta

    def predict(self, X, threshold = 0.5):

        # threshold: predict 1 if above threshold.

        if self.intercept == True:
            X = np.insert(X, 0, np.ones(X.shape[0]), axis = 1)

        probabilities = sigmoid(Z = np.dot(X, self.theta))

        y_predicted = (probabilities > threshold).astype(int)

        return y_predicted


#!/usr/bin/python

'''
mjtsai1974@20180606, v1.0, MLE for https://mjtsai1974.github.io/DevBlog/2018/06/06/bayesian-ml-beyes-to-practice/

http://www.astroml.org/book_figures/chapter3/fig_gaussian_distribution.html

dist = scipy.stats.norm(...)

Where ... should be filled in with the desired distribution parameters Once we have defined the distribution parameters in this way, these distribution objects have many useful methods; for example:

    dist.pmf(x) computes the Probability Mass Function at values x in the case of discrete distributions
    dist.pdf(x) computes the Probability Density Function at values x in the case of continuous distributions
    dist.rvs(N) computes N random variables distributed according to the given distribution
'''

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

'''
#----------------------------------------------------------------------
# This function adjusts matplotlib settings for a uniform feel in the textbook.
# Note that with usetex=True, fonts are rendered with LaTeX.  This may
# result in an error if LaTeX is not installed on your system.  In that case,
# you can set usetex to False.
from astroML.plotting import setup_text_plots
setup_text_plots(fontsize=8, usetex=False)
'''

# Define the given measured weights
weights = [13.9, 14.1, 17.5]
n = len(weights)

# Define the distributions to be plotted
mu = np.mean(weights)
sample_variance = np.var(weights, ddof=1)
sample_std_deviation = np.sqrt(sample_variance)

population_variance = np.var(weights, ddof=0)
population_std_deviation = np.sqrt(population_variance)

sigma_values = [sample_std_deviation, population_std_deviation]
linestyles = ['-', '--']
colours = ['red', 'blue']
x_axis = np.linspace(mu - 2 * np.max(sigma_values), mu + 2 * np.max(sigma_values), 100) #use the

# plot the distributions
fig, ax = plt.subplots(figsize=(5, 3.75))

for sigma, ls, color in zip(sigma_values, linestyles, colours):
    # create a gaussian / normal distribution
    dist = norm(mu, sigma)

    plt.plot(x_axis, dist.pdf(x_axis), ls=ls, c=color,
             label=r'$\mu=%.3f,\ \sigma=%.3f$' % (mu, sigma))

plt.xlim(mu - 2 * np.max(sigma_values), mu + 2 * np.max(sigma_values))
plt.ylim(0, 0.45)

plt.xlabel('$x$')
plt.ylabel(r'$p(x|\mu,\sigma)$')
plt.title('Gaussian Distribution')

plt.legend()
plt.show()

# MLE to ask for possible real weight with max(sample std deviation, population std deviation)
MLE_max = 0
mu_max = mu

for i in range(len(x_axis)):
    dist = norm(x_axis[i], np.max(sigma_values))

    MLE_now = 1.0

    for j in range(len(weights)):
        MLE_now = dist.pdf(weights[j]) * MLE_now

    if MLE_now > MLE_max:
        MLE_max = MLE_now
        mu_max = x_axis[i]

print('using np.max(sigma_values), the MLE for weight {0: 5.3f}'.format(mu_max))

# MLE to ask for possible real weight with min(sample std deviation, population std deviation)
MLE_max = 0
mu_max = mu

for i in range(len(x_axis)):
    dist = norm(x_axis[i], np.min(sigma_values))

    MLE_now = 1.0

    for j in range(len(weights)):
        MLE_now = dist.pdf(weights[j]) * MLE_now

    if MLE_now > MLE_max:
        MLE_max = MLE_now
        mu_max = x_axis[i]

print('using np.min(sigma_values), the MLE for weight {0: 5.3f}'.format(mu_max))


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

Make a simple 1D gaussian profile.

"""

import numpy as np
import matplotlib.pyplot as plt

def gaussian_1D_profile(x_min, x_max, x_step, center, sigma, amplitude):
    """Function to create a 1D Gaussian distribution.

    Parameters
    ----------

    x_min, x_max, x_step: float, float, float
        Creates a sequence (1D ndarray) of points over which to compute the Gaussian
    center: float
        The center point of the gaussian profile
    sigma: float
        1/e-squared width of beam
    amplitude: float
        Amplitude at peak value

    Returns
    -------

    x,y: ndarray
        the gaussian profile amplitude values

    """

    x = np.arange(x_min, x_max,x_step)  #create spatial array
    d = 2*float(sigma)
    y = amplitude*np.e**(-2*np.power((x-center)/d, 2))

    return x,y

    # todo: learn how to do proper unit testing...heres some manual checks
    # what if center > max(X)?  still works, just get the tail end
    # what if center, sigma negative?  Since is getting squared, doesn't matter
    # what if amplitude is neg or zero? Straight line at zero
    # what if d = 0? Straight line
    # what if the ndarray goes negative?  Is ok.
    # What if the array is empty or null? should catch an error.

def plot_1d_gaussian(x,y,hold=True):
    """Plot the gaussian profile.

    Parameters
    ----------

    x: ndarray
        X axis values
    y: float
        Y axis values

    """
    plt.hold = hold
    plt.plot(x,y)
    plt.xlabel('X axis')
    plt.ylabel('Amplitude')
    plt.title('Gaussian 1D Profile')
    plt.show()

    # todo: check if the hold true works or not

if __name__ == '__main__':

    x,y = gaussian_1D_profile(-50,50,.2, 0, 10, 1)
    plot_1d_gaussian(x,y,True)


import numpy as np
def gd(x_start, step, g):
    x = x_start
    for i in range(20):
        grad = g(x)
        x -= grad * step
        print '[ Epoch {0} ] grad = {1}, x = {2}'.format(i, grad, x)
        if abs(grad) < 1e-6:
            break;
    return x

def f(x):
    return x * x - 2 * x + 1

def g(x):
    return 2 * x - 2

import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5,7,100)
y = f(x)
plt.plot(x, y)

gd(5,0.1,g)

gd(5,100,g)

gd(5,1,g)

gd(4,1,g)

def f2(x):
    return 4 * x * x - 4 * x + 1
def g2(x):
    return 8 * x - 4
gd(5,0.25,g2)


import numpy as np

# spearman coefficient rs
def spearman(R, Q):
    """
    Calculate Spearman rank correlation coefficient between two vectors

    Parameters
    -----------
        R : ndarray
            First vector containing values
        Q : ndarray
            Second vector containing values

    Returns
    --------
        float
            Value of correlation coefficient between two vectors

    Examples
    ----------
    >>> rS = spearman(R, Q)
    """

    N = len(R)
    denominator = N*(N**2-1)
    numerator = 6*sum((R-Q)**2)
    rS = 1-(numerator/denominator)
    return rS

# weighted spearman coefficient rw
def weighted_spearman(R, Q):
    """
    Calculate Weighted Spearman rank correlation coefficient between two vectors

    Parameters
    -----------
        R : ndarray
            First vector containing values
        Q : ndarray
            Second vector containing values

    Returns
    --------
        float
            Value of correlation coefficient between two vectors

    Examples
    ---------
    >>> rW = weighted_spearman(R, Q)
    """

    N = len(R)
    denominator = N**4 + N**3 - N**2 - N
    numerator = 6 * sum((R - Q)**2 * ((N - R + 1) + (N - Q + 1)))
    rW = 1 - (numerator / denominator)
    return rW

# pearson coefficient
def pearson_coeff(R, Q):
    """
    Calculate Pearson correlation coefficient between two vectors

    Parameters
    -----------
        R : ndarray
            First vector containing values
        Q : ndarray
            Second vector containing values

    Returns
    --------
        float
            Value of correlation coefficient between two vectors

    Examples
    ----------
    >>> corr = pearson_coeff(R, Q)
    """

    numerator = np.sum((R - np.mean(R)) * (Q - np.mean(Q)))
    denominator = np.sqrt(np.sum((R - np.mean(R))**2) * np.sum((Q - np.mean(Q))**2))
    corr = numerator / denominator
    return corr


from scipy.special import erf
import math

# compute erf(1) by numerically doing the integral as a 3-point quadrature:
# trapezoid (compound), Simpson's, and Gauss-Legendre
#
# erf(x) = 2/sqrt(pi) int_0^x exp(-y^2) dy
#

# analytic integral
true = erf(1)

def f(y):
    """ integrand """
    return (2.0/math.sqrt(math.pi))*math.exp(-y**2)

def x(z, a, b):
    """ convert from [-1, 1] (the integration range of Gauss-Legendre)
        to [a, b] (our general range) through a change of variables z
        -> x """

    return 0.5*(b + a) + 0.5*(b - a)*z

# integration limits
a = 0.0
b = 1.0

# we are doing 3-point quadrature for all methods.  delta is the width
# of the slab
delta = 0.5

# trapezoidal
trap = 0.5*delta*(f(a) + f(0.5*(a+b))) + 0.5*delta*(f(0.5*(a+b)) + f(b))

# Simpson's
simp = (delta/3.0)*(f(a) + 4.0*f(0.5*(a+b)) + f(b))

# Gauss-Legendre

# we need to convert from [-1, 1] (the range in which the roots are
# found) to [a, b] (the range in which our integrand is defined), so
# convert the roots z1, z2, and z3

z1 = -math.sqrt(3./5.)
x1 = x(z1, a, b)
w1 = 5./9.

z2 = 0
x2 = x(z2, a, b)
w2 = 8./9.

z3 = math.sqrt(3./5.)
x3 = x(z3, a, b)
w3 = 5./9.

# 3-point Gauss-Legendre quadrature -- note the factor in the front
# is a result of the change of variables from x -> z
integral = 0.5*(b-a)*( w1*f(x1) + w2*f(x2) + w3*f(x3) )

print "erf(1) (exact):         ", true
print "3-point trapezoidal:    ", trap, trap-true
print "3-point Simpson's:      ", simp, simp-true
print "3-point Gauss-Legendre: ", integral, integral-true


import numpy as np
my_list = [1,2,3,4,5,6]

print(my_list)

#creating single dimension array of vector by casting python list to numPy array
npvector = np.array(my_list)

print(npvector)

my_mat = [[1,2,3],[4,5,6],[7,8,9]]

nptwodarray = np.array(my_mat)

print(nptwodarray)

print(np.arange(0,10))# python range like function to generate vector array

print(np.arange(0,10, 2))#with step

print(np.zeros(3))#generate single/vector array using zeros

print(np.zeros((3,3)))#generate 2d/matrix array using zeros or ones and passing arg as tuple

print(np.ones((3,3)))

print(np.linspace(0,5,10))

print(np.eye(3))

print(np.random.rand(5))

print(np.random.rand(3,3))

print(np.random.randn(3,3))

print(np.random.randn(3))

print(np.random.randint(0,10,3))

rangearr = np.arange(0,25)
print(rangearr)
print(rangearr.reshape((5,5)))
print(rangearr.min())
print(rangearr.max())
print(rangearr.argmax())
print(rangearr.argmin())

print("Index and Selection")
#numpy indexing and selection
indexarr = np.arange(0,11)
print(indexarr)
#indexing
print(indexarr[7])
#slicing
print(indexarr[2:4])
#slicing will change original instance as below
slice_of_arr = indexarr[0:5]
print(slice_of_arr)
slice_of_arr[:] = 0
print(slice_of_arr)
print(indexarr)
#to avoid use copy
indexarr_copy = indexarr.copy()
print(indexarr)
print(indexarr_copy)
indexarr_copy[:]=1
print(indexarr_copy)
print(indexarr)

arr_2d = np.array([[5,10,15], [20, 25, 30], [35, 40, 45]])
print(arr_2d)
print(arr_2d[1][1])
print(arr_2d[1,1])

#grabbing sub matrix from matrix
print(arr_2d[:2,1:])
print(arr_2d[:2,:2])

#boolean array using comparator operator
normal_arr = np.arange(1, 11)
print(normal_arr)
bool_arr = normal_arr > 3
print(bool_arr)

#grabbing from normal array from bool_arr
print(normal_arr[bool_arr])
print(normal_arr[normal_arr>4])

new_2d_array = np.arange(50).reshape(5,10)
print(new_2d_array)
print(new_2d_array[2:4,2:4]) #new_2d_array([from_row:to_row,from_col,to_col])

#numpy operation
print("numpy operation")

op_arr = np.arange(0,10)
print(op_arr + op_arr)
print(op_arr - op_arr)
print(op_arr * op_arr)
print(op_arr / 2)
print(op_arr / op_arr)


import numpy as np
from matplotlib import pyplot as plt
import sympy
from sympy.abc import x, y

XMIN = -3
XMAX = 3
RADIUS = 1.0
N = 25

def cylinder_stream_function(U=1, R=1):
    r = sympy.sqrt(x**2 + y**2)
    theta = sympy.atan2(y, x)
    return U * (r - R**2 / r) * sympy.sin(theta)

def velocity_field(psi):
    """ u, v -> give U or V vector component for position X/Y """

    u = sympy.lambdify((x, y), psi.diff(y), 'numpy')
    v = sympy.lambdify((x, y), -psi.diff(x), 'numpy')
    return u, v

def plot_streamlines(ax, u, v, xlim=(-1, 1), ylim=(-1, 1)):
    x0, x1 = xlim
    y0, y1 = ylim
    Y, X =  np.ogrid[y0:y1:25j, x0:x1:25j]
    U = u(X,Y)
    V = v(X,Y)
    ax.streamplot(X, Y, u(X, Y), v(X, Y), density=2, color='cornflowerblue')
    #ax.quiver(X,Y,u(X,Y),v(X,Y))

def compute_components(domask):
    """ Returns:

    x: 1-D array with x coordinates
    y: 1-D array with y coordinates
    u: 2-D array with x vector components
    v: 2-D array with y vector components
    """

    uf, vf = velocity_field(cylinder_stream_function(R=RADIUS))

    y, x = np.ogrid[XMIN:XMAX:N*1j, XMIN:XMAX:N*1j]

    u = uf(x,y)
    v = vf(x, y)

    xx, yy = np.mgrid[XMIN:XMAX:N*1j, XMIN:XMAX:N*1j]

    r = np.sqrt(xx**2 + yy**2)

    mask = r <= RADIUS

    if domask:
        u[mask] = 0
        v[mask] = 0

    x = x.reshape((N,))
    y = y.reshape((N,))

    return x, y, u, v

def makeplot(show=False):

    x, y, u, v = compute_components(True)

    np.savetxt('streamlines_u.txt', u)
    np.savetxt('streamlines_v.txt', v)

    if show:
        mag = np.sqrt(u**2 + v**2)

        plt.figure(1)
        plt.clf()
        plt.axes(aspect=True)#, axisbg='#444444')

        print x.shape
        plt.streamplot(x,y,u,v,color=mag,cmap='Blues',density=1.5, arrowsize=2)
        plt.colorbar().set_label("Flow speed")

        c = plt.Circle((0,0), radius=RADIUS, facecolor='#aaaacc', linewidth=0, zorder=10)
        plt.gca().add_patch(c)
        plt.xlim(-3,3)
        plt.ylim(-3,3)


from math import sin, pi
from scipy.integrate import quad, dblquad, nquad

# Quad Function:

f = lambda x: x*sin(x)  # Equation to be integrated
a = 0                   # Lower limit
b = pi/2                # Upper limit

I,_ = quad(f, a, b)     # Neglects the 2nd return which is estimated abs error

print('I,_ = quad(f, ', a,', %f)' %b, sep='')
print('I   = %f' % I, end='\n\n')

# DblQuad Function:

fn = lambda x, y: x**2 * y + x * y**2   # Equation to be integrated
ax = 1                                  # Lower limit of inner integral
bx = 2                                  # Upper limit of inner integral
ay = -1                                 # Lower limit of outer integral
by = 1                                  # Upper limit of outer integral

I,_ = dblquad(fn, ax, bx, lambda y:ay, lambda y:by)

print('I,_ = dblquad(fn, ', ax, ', ', bx, ', lambda y:', ay, \
      ', lambda y:', by, ')', sep='')
print('I   = %f' % I, end='\n\n')

# NQuad Function:

I,_ = nquad(f, [[0, pi/2]])

print('I,_ = nquad(f, [[0, pi/2]])')
print('I   = %f' % I, end='\n\n')

I,_ = nquad(fn, [[ax, bx], [ay, by]])

print('I,_ = nquad(fn, [[',ax,', ',bx,'], [',ay,', ',by,']])', sep='')
print('I   = %f' % I)

4. Numerical Integration (Quadrature)/0. Numerical Integration functions of SciPy.py


import sympy as sp
import numpy as np
import matplotlib.pyplot as plt

# Symbolic function and derivatives
h, t = sp.symbols('h t');
h = sp.exp(3*t)*t**2;
hp = h.diff(t);
#hpp = hp.diff(t);

# Taylor expansions around point t0
t0 = 1;
h0 = h.subs(t,t0);
h1 = h0 + hp.subs({t:t0})*(t-t0);
#h2 = h0 + hp.subs(t,t0)*(t-t0) + 1/2*hpp.subs(t,t0)*(t-t0)**2;

# Direct Taylor expansion using sympy
h5s = sp.series(h, t, t0, 6).removeO();
print("Taylor 5: ", h5s);

# Convert symbolic to functions that can be evaluated
lam_h = sp.lambdify(t, h, modules=['numpy']);
lam_h1 = sp.lambdify(t, h1, modules=['numpy']);
lam_h5s = sp.lambdify(t, h5s, modules=['numpy']);

# Plots
fig, ax = plt.subplots(1,1);
t_vals = np.linspace(0.5, 1.5, 100);
ax.plot(t_vals, lam_h(t_vals), 'r');
ax.plot(t_vals, lam_h1(t_vals), 'g');
#ax.plot(t_vals, lam_h5s(t_vals), 'b');

# Symbolic plotting also probably works
#sp.plot(h, h1, (t, 0, 1.5))


import numpy as np

'''
Proof of concept: Logits and softmax probability computations
'''

def softmax(logits):

    softmax = []
    q = 0
    for i in logits:
        softmax.append(np.exp(i) / sum(np.exp(logits)))
        q += np.exp(i) / sum(np.exp(logits))

    return softmax, q

def logits_func(prob):
    return np.log(prob / (1+prob))

proba = [0.006382342879717207, 0.2564389982593307, 0.04506310418417042, 0.08901875647497302, 0.0920775142662204,
        0.04941506219220917, 0.16701976595076282, 0.06772062316943511, 0.22686383262318108]
logits = [-5.053441047668457, -1.3600854873657227, -3.098912477493286, -2.4181292057037354, -2.384345531463623,
        -3.006721019744873, -1.7888641357421875, -2.6915855407714844, -1.4826263189315796]

softmax(logits)

for i in proba:
    print(logits_func(i))

def logit2prob(logit):
    return np.exp(logit) / (np.exp(logit) + 1)

# x2 samples of > 0.5
logits = [-4.636117458343506, -4.46367883682251, -3.3428733348846436, 2.175978183746338, -4.3089518547058105,
        -4.214722633361816, -5.114277362823486, 2.573429822921753, -4.526679039001465]

for i in logits:
    print(logit2prob(i))


#=============================================================================#
#
#    File:   sieve_of_eratosthenes.py
#    Author: Jack Morgan
#    Date:   May 2021
#    Description:
#        Uses a sieve to find all of the prime numbers up to a limit
#        Three versions, each faster than the last
#
#=============================================================================#

import numpy as np
from time import time
from math import sqrt, ceil

# print the prime numbers up to limit
# first version
def sieve_of_eratosthenes_1(limit):
    possible_primes = [True for _ in range(limit)]
    possible_primes[0] = possible_primes[1] = False
    for index, isprime in enumerate(possible_primes):
        if isprime:
            yield index
            for i in range(index**2, limit, index):
                possible_primes[i] = False
    return possible_primes

#second version
def sieve_of_eratosthenes_2(limit):
    possible_primes = np.ones(limit)
    possible_primes[0:2:1] = 0
    for (i, isprime) in enumerate(possible_primes):
        if bool(isprime):
            yield i
            possible_primes[i*i:limit:i] = 0

# third version
def sieve_of_eratosthenes_3(limit):
    possible_primes = np.ones(limit, dtype=bool)
    possible_primes[0:2:1] = False
    for i in range(2, ceil(sqrt(limit))):
        possible_primes[i*i:limit:i] = False
    return np.flatnonzero(possible_primes)

def main():
    limit = 1*10**6

    start1 = time()
    print(list(sieve_of_eratosthenes_1(limit)))
    end1 = time()

    start2 = time()
    print(list(sieve_of_eratosthenes_2(limit)))
    end2 = time()

    start3 = time()
    print(sieve_of_eratosthenes_3(limit))
    end3 = time()

    print(f'time 1: {end1-start1} seconds')
    print(f'time 2: {end2-start2} seconds')
    print(f'time 3: {end3-start3} seconds')

if __name__ == "__main__":
    main()