# -*- coding: utf-8 -*-
"""
Created on Sun Apr 12 10:36:53 2020

@author: Abdeljalil
"""

import numpy as np
import matplotlib.pyplot as plt

plt.rc('font', family='serif')
plt.rc('xtick', labelsize='10')
plt.rc('ytick', labelsize='10')
plt.rc('axes', titlesize=12)
plt.rc('text', usetex=True)
#%%

freq = 100
T = 1./freq
N = 300

x = np.linspace(0,N*T, N)
signal = np.sin(2*np.pi*5*x) + 3*np.sin(2*np.pi*15*x) + 2*np.sin(2*np.pi*30*x)+ np.random.randn(N)

#%% Plot FFT
fig, axes = plt.subplots(1,2,figsize=(6,2.5))
axes[0].plot(x, signal, c='k', lw=1.4)
axes[0].set_xticks([0,1,2,3])
axes[0].set_xlabel('Time (s)')
axes[0].set_yticklabels([])
axes[0].set_ylabel('Amplitude')
axes[0].grid(True)
axes[0].set_title('Time domain')

fft_y = np.fft.fft(signal)
fft_y = abs(fft_y[:N//2])
fft_x = np.linspace(0,freq/2,N//2)

axes[1].plot(fft_x, (2/N)*fft_y, c='k', lw=1)
axes[1].set_xlabel('Frequency (Hz)')
axes[1].set_ylabel('Amplitude')
axes[1].set_yticklabels([])
axes[1].set_ylim([0,3])
axes[1].grid(True)
axes[1].set_title('Frequency domain')
fig.tight_layout()
fig.savefig('/home/abdeljalil/Workspace/MasterThesis/figures/fft.pdf')

#%% Plot FFT [French]
fig, axes = plt.subplots(1,2,figsize=(6,2.5))
axes[0].plot(x, signal, c='k', lw=1.4)
axes[0].set_xticks([0,1,2,3])
axes[0].set_xlabel('Temps (s)')
axes[0].set_yticklabels([])
axes[0].set_ylabel('Amplitude')
axes[0].grid(True)
axes[0].set_title('Domaine temporel')

fft_y = np.fft.fft(signal)
fft_y = abs(fft_y[:N//2])
fft_x = np.linspace(0,freq/2,N//2)

axes[1].plot(fft_x, (2/N)*fft_y, c='k', lw=1)
axes[1].set_xlabel('Fréquence (Hz)')
axes[1].set_ylabel('Amplitude')
axes[1].set_yticklabels([])
axes[1].set_ylim([0,3])
axes[1].grid(True)
axes[1].set_title('Domaine fréquentiel')
fig.tight_layout()
fig.savefig('/home/abdeljalil/Workspace/MasterThesis/figures/fft_fr.pdf')


import numpy as np

def sigmoid(x):
    """Sigmoid function.
       @param x: input matrix/vector.
       @return: elementwise sigmoid"""
    return 1/(1+np.exp(-x))

def sigmoid_dash(x):
    """Sigmoid derivative function.
       @param x: input matrix/vector.
       @return: elementwise sigmoid derivative"""
    return sigmoid(x)*(1-sigmoid(x))

def relu(x):
    """Rectified linear activation function.
       @param x: input matrix/vector.
       @return: elementwise relu"""
    return np.maximum(x,0)

def relu_dash(x):
    """Relu derivative function.
       @param x: input matrix/vector.
       @return: elementwise relu derivative"""
    x[x<=0]=0
    x[x>0]=1
    return x

def softmax(x):
    """Softmax function.
       @param x: input matrix/vector.
       @return: columnwise softmax"""
    x_exp = np.exp(x)
    x_sum = np.sum(x_exp,axis=0)
    return x_exp/x_sum


import numpy as np

def find_num_solutions(A_pass, b_pass):
    """
        Using row reduction, determines the number of solutions that satisfy
        the linear system of equations given by A_pass.X = b_pass mod 2.
        Returns zero if no solutions exist.

    """
    A = np.copy(A_pass)
    b = np.copy(b_pass)

    M, N = A.shape

    h = 0
    k = 0

    while h<M and k<N:

        max_i = h

        for i in range(h, M):
            if A[i, k] == 1:
                max_i = i
                break

        if A[max_i, k] == 0:
            k += 1
        else:
            if h != max_i:
                A[[h, max_i]] = A[[max_i, h]]
                b[[h, max_i]] = b[[max_i, h]]

            for u in range((h+1), M):
                flip_val = A[u, k]
                A[u] = ( A[u] + flip_val*A[h] ) % 2
                b[u] = ( b[u] + flip_val*b[h] ) % 2

            h += 1
            k += 1

    # Find rows with all zeros
    num_all_zeros_rows = 0

    solutions_exist = True

    for i in range(M):
        if not np.any(A[i]): # All-zero row encountered

            if b[i] != 0:
                solutions_exist = False
                break

            num_all_zeros_rows += 1

    if solutions_exist:
        rank = M - num_all_zeros_rows
        num_solutions = np.power(2, N-rank)
    else:
        num_solutions = 0

    return num_solutions

def regular_xorsat(k, n):
    """
    Generates a k-regurlar k-XORSAT instance with n variables.

    """

    indices = np.zeros((n, k), dtype=int)

    while True:
        for i in range(k):
            indices[:, i] = np.random.permutation(n)

        if all(np.unique(row).size == k for row in indices):
            break

    b = np.random.choice(2, n)

    return indices, b

def plant_regular_xorsat(k, n):
    """
    Repetitively generates k-regular k-XORSAT problems until an instance
    with non-zero solutions is obtained.
    Returns the resultant XORSAT instance formulated as an
    Ising minimization problem.
    Also returns the ground state energy and ground state degenaracy.

    """

    while True:
        indices, b = regular_xorsat(k, n)

        A = np.zeros( (n, n), dtype=int )

        for i, row in enumerate(indices):
            A[i, row] = 1

        num_solutions = find_num_solutions(A, b)

        if num_solutions != 0:
            break

    gs_energy = -n

    bonds = []

    for i in range(n):
        bonds.append( tuple(indices[i]) + (-np.power(-1, b[i]), ) )

    return bonds, gs_energy, num_solutions


import math

import numpy as np
from scipy.fftpack import dct, idct

import matplotlib.pyplot as plt

# 离散余弦变换
def my_dct(x):
    assert x.ndim == 1
    size = x.shape[0]
    res = np.zeros(size)
    for i in range(size):
        for j in range(size):
            if i == 0:
                res[i] += math.sqrt(1.0 / size) * x[j] * math.cos((2 * j + 1) * math.pi * i / (2 * size))
            else:
                res[i] += math.sqrt(2.0 / size) * x[j] * math.cos((2 * j + 1) * math.pi * i / (2 * size))
    return res

def my_idct(x):
    assert x.ndim == 1
    size = x.shape[0]
    res = np.zeros(size)
    for i in range(size):
        for j in range(size):
            if j == 0:
                res[i] += math.sqrt(1.0 / size) * x[j] * math.cos((2 * i + 1) * math.pi * j / (2 * size))
            else:
                res[i] += math.sqrt(2.0 / size) * x[j] * math.cos((2 * i + 1) * math.pi * j / (2 * size))
    return res

if __name__ == '__main__':
    x = np.array([1, 2, 3, 5, 7])
    # x = np.random.uniform(low=0,high=2,size=1024)
    x = np.random.normal(loc=0,scale=10, size=102400)
    dct2 = dct(x, norm='ortho')
    idct2 = idct(dct2, norm='ortho')
    print("dct2", dct2)
    print("dct2 sum", sum(dct2))
    print("dct2 sum2", sum(dct2**2))
    print("idct2", idct2)

    x1 = range(102400)

    plt.plot(x1,dct2)
    plt.show()
    my_dct2 = my_dct(x)
    my_idct2 = my_idct(my_dct2)
    print("my_dct2", my_dct2)
    print("my_idct2", my_idct2)


import numpy as np

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t*np.log(y + delta))

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
print(cross_entropy_error(np.array(y), np.array(t)))

y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print(cross_entropy_error(np.array(y), np.array(t)))


import numpy as np
import cv2

def masking(A, kernel):
    value = 0
    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            value += (A[i, j] * kernel[i, j])
    return value

def laplacian1(im):
    ext_im = cv2.copyMakeBorder(im, 1, 1, 1, 1, cv2.BORDER_REFLECT)
    ext_im = np.asarray(a=ext_im, dtype=np.int)
    kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]])
    lap = np.zeros(im.shape, np.int)
    for i in range(1, ext_im.shape[0] - 1):
        for j in range(1, ext_im.shape[1] - 1):
            lap[i-1][j-1] = masking(ext_im[i-1:i+2, j-1:j+2], kernel)
    return lap

def laplacian2(im):
    ext_im = cv2.copyMakeBorder(im, 1, 1, 1, 1, cv2.BORDER_REFLECT)
    ext_im = np.asarray(a=ext_im, dtype=np.int)
    kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]]) / 3
    lap = np.zeros(im.shape, np.float32)
    for i in range(1, ext_im.shape[0] - 1):
        for j in range(1, ext_im.shape[1] - 1):
            lap[i-1][j-1] = masking(ext_im[i-1:i+2, j-1:j+2], kernel)
    return lap

def laplacian_min(im):
    ext_im = cv2.copyMakeBorder(im, 1, 1, 1, 1, cv2.BORDER_REFLECT)
    ext_im = np.asarray(a=ext_im, dtype=np.int)
    kernel = np.array([[2, -1, 2], [-1, -4, -1], [2, -1, 2]]) / 3
    lap = np.zeros(im.shape, np.float32)
    for i in range(1, ext_im.shape[0] - 1):
        for j in range(1, ext_im.shape[1] - 1):
            lap[i-1][j-1] = masking(ext_im[i-1:i+2, j-1:j+2], kernel)
    return lap

def laplacian_gauss(im):
    ext_im = cv2.copyMakeBorder(im, 5, 5, 5, 5, cv2.BORDER_REPLICATE)
    ext_im = np.asarray(a=ext_im, dtype=np.int)
    kernel = np.array([[0, 0, 0, -1, -1, -2, -1, -1, 0, 0, 0],
                       [0, 0, -2, -4, -8, -9, -8, -4, -2, 0, 0],
                       [0, -2, -7, -15, -22, -23, -22, -15, -7, -2, 0],
                       [-1, -4, -15, -24, -14, -1, -14, -24, -15, -4, -1],
                       [-1, -8, -22, -14, 52, 103, 52, -14, -22, -8, -1],
                       [-2, -9, -23, -1, 103, 178, 103, -1, -23, -9, -2],
                       [-1, -8, -22, -14, 52, 103, 52, -14, -22, -8, -1],
                       [-1, -4, -15, -24, -14, -1, -14, -24, -15, -4, -1],
                       [0, -2, -7, -15, -22, -23, -22, -15, -7, -2, 0],
                       [0, 0, -2, -4, -8, -9, -8, -4, -2, 0, 0],
                       [0, 0, 0, -1, -1, -2, -1, -1, 0, 0, 0]])
    lap = np.zeros(im.shape, np.float32)
    for i in range(5, ext_im.shape[0] - 5):
        for j in range(5, ext_im.shape[1] - 5):
            lap[i-5][j-5] = masking(ext_im[i-5:i+6, j-5:j+6], kernel)
    return lap

def laplacian_dif(im):
    ext_im = cv2.copyMakeBorder(im, 5, 5, 5, 5, cv2.BORDER_REPLICATE)
    ext_im = np.asarray(a=ext_im, dtype=np.int)
    kernel = np.array([[-1, -3, -4, -6, -7, -8, -7, -6, -4, -3, -1],
                       [-3, -5, -8, -11, -13, -13, -13, -11, -8, -5, -3],
                       [-4, -8, -12, -16, -17, -17, -17, -16, -12, -8, -4],
                       [-6, -11, -16, -16, 0, 15, 0, -16, -16, -11, -6],
                       [-7, -13, -17, 0, 85, 160, 85, 0, -17, -13, -7],
                       [-8, -13, -17, 15, 160, 283, 160, 15, -17, -13, -8],
                       [-7, -13, -17, 0, 85, 160, 85, 0, -17, -13, -7],
                       [-6, -11, -16, -16, 0, 15, 0, -16, -16, -11, -6],
                       [-4, -8, -12, -16, -17, -17, -17, -16, -12, -8, -4],
                       [-3, -5, -8, -11, -13, -13, -13, -11, -8, -5, -3],
                       [-1, -3, -4, -6, -7, -8, -7, -6, -4, -3, -1]])
    lap = np.zeros(im.shape, np.float32)
    for i in range(5, ext_im.shape[0] - 5):
        for j in range(5, ext_im.shape[1] - 5):
            lap[i-5][j-5] = masking(ext_im[i-5:i+6, j-5:j+6], kernel)
    return lap

def threshold(G, threshold):
    result = G
    for i in range(G.shape[0]):
        for j in range(G.shape[1]):
            if G[i][j] >= threshold:
                result[i][j] = 1
            elif G[i][j] <= -threshold:
                result[i][j] = -1
            else:
                result[i][j] = 0
    return result

def zerocross(result):
    ext_im = cv2.copyMakeBorder(result, 1, 1, 1, 1, cv2.BORDER_REFLECT)
    ext_im = np.asarray(a=ext_im, dtype=np.int)
    cross = False
    ans = np.zeros(result.shape, np.int)
    for i in range(1, ext_im.shape[0] - 1):
        for j in range(1, ext_im.shape[1] - 1):
            cross = zerocross_helper(ext_im[i-1:i+2, j-1:j+2])
            if cross:
                ans[i-1][j-1] = 0
            else:
                ans[i-1][j-1] = 255
    return ans

def zerocross_helper(square):
    if square[1, 1] >= 1:
        for i in range(square.shape[0]):
            for j in range(square.shape[1]):
                if square[i, j] <= -1:
                    return True
        return False
    else:
        return False

im = cv2.imread('lena.bmp', 0)
laplacian_1 = zerocross(threshold(laplacian1(im), 15))
cv2.imwrite('laplacian_1.bmp', laplacian_1)
laplacian_2 = zerocross(threshold(laplacian2(im), 15))
cv2.imwrite('laplacian_2.bmp', laplacian_2)
laplacian_min = zerocross(threshold(laplacian_min(im), 20))
cv2.imwrite('laplacian_min.bmp', laplacian_min)
laplacian_gauss = zerocross(threshold(laplacian_gauss(im), 3000))
cv2.imwrite('laplacian_gauss.bmp', laplacian_gauss)
laplacian_dif = zerocross(threshold(laplacian_dif(im), 1))
cv2.imwrite('laplacian_dif.bmp', laplacian_dif)


import numpy as np
import matplotlib.pyplot as plt
from math import sqrt
from scipy import stats
from scipy.stats import kurtosis, skew

def pdf(costh, P_mu):
    # define our probability density function
    return 0.5 * (1.0 - 1.0 / 3.0 * P_mu * costh)

def inv_cdf_pos(r, P_mu):
    # inverse of the cumulative density function
    # since we have positive and negative solutions for x(r), we have to consider both
    return 3./P_mu*(1.+np.sqrt(1.+2./3.*P_mu*(P_mu/6.+1.-2.*r)))

def inv_cdf_neg(r, P_mu):
    return 3./P_mu*(1.-np.sqrt(1.+2./3.*P_mu*(P_mu/6.+1.-2.*r)))

def inv_trans(N_measurements, P_mu):
    r = np.random.uniform(0.0, 1.0, size=N_measurements)
    data_pdf = inv_cdf_neg(r, P_mu)
    return data_pdf

def acc_rej(N_measurements, P_mu):
    x = np.random.uniform(-1.0, 1.0, size=N_measurements)

    x_axis = np.linspace(-1.0,1.0, 1000)
    fmax= np.amax(pdf(x_axis,P_mu)) #find the maximum of the function

    u = np.random.uniform(0, fmax, size= N_measurements)

    # we use a mask in order to reject the values we don't want
    data_pdf = x[u < pdf(x,P_mu)]
    return data_pdf

def t_variable(N_events, sample_mean, mean, unbiased_sample_mean):
    return sqrt(N_events)*(sample_mean-mean)/sqrt(unbiased_sample_mean)

def g_variable(N_events, sample_mean, mean, biased_sample_mean):
    return sqrt(N_events)*(sample_mean-mean)/sqrt(biased_sample_mean)

t = np.array([])
g = np.array([])
N_experiments = 0
N_experiments_max = 10000
N_measurements = 1000
P_mu=0.5
mean = -P_mu/9.

while N_experiments < N_experiments_max:
    data_pdf = inv_trans(N_measurements, P_mu) #the inv_trans methods works quite better because takes all the points
    sample_mean = np.mean(data_pdf)
    unbiased_sample_mean = np.var(data_pdf, ddof=1)
    biased_sample_mean = np.var(data_pdf, ddof=0)
    t = np.append(t, t_variable(N_measurements, sample_mean, mean, unbiased_sample_mean))
#    g = np.append(g, g_variable(N_measurements, sample_mean, mean, biased_sample_mean))
    N_experiments += 1
    print(N_experiments)

plt.hist(t, bins=30, normed=True, histtype="step", color="black", linewidth="1")
plt.xlabel(r't')
plt.ylabel(r'$N(t)$')
plt.xlim(-4,4)

#plt.hist(g, bins=30, normed=True, histtype="step", color="black", linewidth="1")
#plt.xlabel(r'g')
#plt.ylabel(r'$N(g)$')

x_axis=np.linspace(-20, 20, 1000000)
plt.plot(x_axis, stats.t.pdf(x_axis, N_measurements-1), color="orange", linewidth="1", label ="t-distribution")
plt.plot(x_axis, stats.norm.pdf(x_axis, loc=0, scale=1), color="red", linewidth="1", label = r"$\mathcal{N} \ (0,1)$")
plt.legend(loc="best")
leg = plt.legend()
leg.get_frame().set_edgecolor('black')
#plt.text(-3.5, 0.38, r"$N_{events}=$" + str(N_measurements))

mean_t= np.mean(t)
var_t= np.var(t)
skew_t = skew(t)
kurt_t = kurtosis(t, fisher=True)

print("MC:", mean_t, var_t, skew_t, kurt_t)

mean_g= np.mean(g)
var_g= np.var(g)
skew_g = skew(g)
kurt_g = kurtosis(g, fisher=True)

print("MC:", mean_g, var_g, skew_g, kurt_g)
#print("Theory:", 0,(N_measurements-1)/(N_measurements-3), 0, 6/(N_measurements-5))

plt.show()

Projects/Probability and Monte Carlo Techniques/_5_students_t.py


'''
Created on 21 Aug 2020

@author: Christoph Minz
@license: BSD 3-Clause
'''
from __future__ import annotations
from typing import List, Any, Callable
from math import log
from fractions import Fraction
import numpy as np

@np.errstate(divide='ignore')
def HarmonicNumber(n: int, approximate: int = 100) -> float:
    '''
    Returns the floating point value of the n-th harmonic number.
    For `n` greater or equal to `approximate`, the value is approximated by
    the asymptotic expansion.
    '''
    if n < approximate:
        return sum(1. / k for k in range(n, 0, -1))
    else:
        h: float = log(n) + np.euler_gamma
        h += 0.5 / n
        h -= 0.25 / 3 / n**2
        h += 0.025 / 3 / n**4
        return h

@np.errstate(divide='ignore')
def HarmonicNumbers(n: int, approximate: int = 100) -> np.ndarray:
    '''
    Returns a vector of floating point values for all harmonic numbers from 0
    to n.
    For `n` greater than `approximate`, the value is approximated by the
    asymptotic expansion.
    '''
    H: np.ndarray = np.empty((n + 1,))
    total: float = 0.
    for k in range(min(n + 1, approximate)):
        H[k] = total
        total += 1. / (k + 1)
    if n >= approximate:
        n_range: np.ndarray = np.arange(approximate, n + 1)
        H[n_range] = np.log(n_range) + np.euler_gamma
        H[n_range] += 0.5 / n_range
        H[n_range] -= 0.25 / 3 / np.square(n_range)
        H[n_range] += 0.025 / 3 / np.square(np.square(n_range))
    return H

def HarmonicNumberFraction(n: int) -> Fraction:
    '''
    Returns the exact rational number of the n-th harmonic number.
    '''
    if n == 0:
        return Fraction(0, 1)
    else:
        return Fraction(sum(Fraction(1, k) for k in range(1, n + 1)))

def HarmonicNumberFractions(n: int) -> List[Fraction]:
    '''
    Returns a vector of exact rational numbers for all harmonic numbers from
    0 to n.
    '''
    H: List[Fraction] = [Fraction()] * (n + 1)
    total: Fraction = Fraction(0, 1)
    for k in range(n + 1):
        H[k] = total
        total += Fraction(1, k + 1)
    return H

def NewtonsMethod(f: Callable[[Any], Any],
                  fprime: Callable[[Any], Any], x0: float,
                  y: float = 0.0, xmin: float = np.NINF, xmax: float = np.PINF,
                  default: float = 0.0, precission: float = 1.0e-6,
                  maxIterations: int = 50) -> float:
    '''
    Uses Newton's method to find the x value at which the function `f` (with
    its derivative `fprime`) reaches the value y with a given `precission` and
    maximal number of iterations `maxIterations`. At each iteration, the
    result will be restricted to the interval
    [`xmin` + `precission`, `xmax` - `precission`]
    If the method does not converge, `default` is returned.
    '''
    x_n: float = x0
    for _ in range(maxIterations):
        yprime_n: float = fprime(x_n)
        if yprime_n == 0.0:
            break
        x_next: float = x_n - (f(x_n) - y) / yprime_n
        if x_next < xmin:
            x_next = xmin + precission
        elif x_next > xmax:
            x_next = xmax - precission
        if abs(x_n - x_next) < precission:
            return x_next
        x_n = x_next
    return default


#symbolik:herhangi bir denklemi çözsün sonucunu x ler vs şeklinde sembolik bir şekilde işlem yapsın istersem Sympy kullanıyorum
#belirsiz sembolik matematiksel işlemleri yapmak için kullanıyorum
#yani özet olarak manuel yaptığımız x lerden y lerden oluşam matematik işlemlerimizi bu kütüphaneyle yapabiliriz
#denklem(dif denklemde dahil) çözdürüp grafik oluşturmak için Sympy kullanmak hoş olur.Sympy=SEMBOL
import sympy as sp

#sembol tanımlaması
x = sp.Symbol('xxx') #burada x bir symbol sınıfı türünden
print(x)
y = sp.Symbol('y')
z = sp.Symbol('z')
print(y)
print(z)

#3 sembolü aynı anda da yaratabilirim =>>>>>   x, y, z = sp.symbols('x, y, z')

#sembollerimi reel ve pozitif yapmak istersem
x = sp.Symbol('x', real=True, positive=True) #burada x sembolümü oluşturdum
print(x)

#integer ve float nesneler tanımlama
x = sp.Symbol('x', real=True, positive=True)
a = sp.Integer(3)
b = sp.Float('4.3476587364587364876345876867345')
print(x)
print(a)
print(b)

#x sembolüyle denklem tanımı yapma
x = sp.Symbol('x', real=True, positive=True)
y = x ** 2 - 2*x - 4
print(y)

# ifadeyi  sembolük sadeleştirmek istersem
x = sp.Symbol('x')
e = (x ** 2 - 1) / (x + 1)
result = sp.simplify(e)
print(result)

#sin2x sembolik olarak açılımını verip sonucu aldım
x = sp.Symbol('x')
e = 2 * sp.sin(x) * sp.cos(x)
result = sp.simplify(e)
print(result)

#iki değişkenden oluşan bir denklemi sadeleştirmek istersem
x = sp.Symbol('x')
y = sp.Symbol('y')
e = ((x ** 2 + x - 6) / (x ** 2 + 3 * x - 10)) * ((x ** 2 - x * y + 5 * x  - 5 * y) / (x **2 + x * y + 3 * x + 3 * y))
result = sp.simplify(e)
print(result)

#expand methoduyla denklem çarpması işlemi yaptım küp açılımı oldu
x = sp.Symbol('x')
y = sp.Symbol('y')
e = (x + y) ** 3
result = sp.expand(e)
print(result)

#çarpanlara ayırmak istersem factor methodunu kullanıyorum
x = sp.Symbol('x')
y = sp.Symbol('y')
e = x ** 2 - 2 * x + 1
result = sp.factor(e)
print(result)


import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D

"""
Draw 1D, 2D Normal Distribution to explain my modification in Fuzzy Curve
"""
def Gaussian_Distribution(N=2, M=1000, m=0, sigma=1):
    '''
    Function
        Generate N dimensions Normal distribution data
    ----------
    Parameters
    ----------
        N 维度
        M 样本数
        m 样本均值
        sigma: 样本方差
    Returns
    -------
        data  shape(M, N), M 个 N 维服从高斯分布的样本
        Gaussian  高斯分布概率密度函数
    '''
    mean = np.zeros(N) + m  # 均值矩阵，每个维度的均值都为 m
    cov = np.eye(N) * sigma  # 协方差矩阵，每个维度的方差都为 sigma

    # 产生 N 维高斯分布数据
    data = np.random.multivariate_normal(mean, cov, M)
    # N 维数据高斯分布概率密度函数
    Gaussian = multivariate_normal(mean=mean, cov=cov)
    return data, Gaussian

def generate_1D_data():
    #-------------------- 生成两个正态分布的数据 --------------------
    # 生成mean=0.3， sigma=0.1的数据，x=【0~1，step=0.001】
    mean0, sigma0 = 0.3, 0.1
    mean1, sigma1 = 0.7, 0.2
    x = np.linspace(0, 1, 1000)

    data0, Gaussian0 = Gaussian_Distribution(N=1, M=1000, m=mean0, sigma=sigma0)
    y1 = Gaussian0.pdf(x)
    data1, Gaussian1 = Gaussian_Distribution(N=1, M=1000, m=mean1, sigma=sigma1)
    y2 = Gaussian1.pdf(x)

    with open('1D_data.txt', 'a') as f:
        line = ''
        for a,b,c in zip(x, y1, y2):
            line += ','.join([str(s) for s in [a,b,c]]) + '\n'
        f.write(line)
    #-------------------- 生成两个正态分布的数据 --------------------
# generate_1D_data()

def generate_2D_data1():
    x, y = np.mgrid[-1:1:.01, -1:1:.01]
    pos = np.empty(x.shape + (2,))  # 从x.shape=(200,200)变为(200,200,2)
    pos[:, :, 0] = x
    pos[:, :, 1] = y
    # mean=[0.5, -0.2],cov=[[2.0, 0.3], [0.3, 0.5]]，声明一个带着指定mean和cov的rv对象
    rv0 = multivariate_normal([0.5, -0.2], [[2.0, 0.3], [0.3, 0.5]])
    z0 = rv0.pdf(pos)

    rv1 = multivariate_normal([0.3, -0.7], [[2.0, 0.3], [0.3, 0.5]])
    z1 = rv1.pdf(pos)

    with open('2D_data.txt', 'a') as f:
        line = ''
        for i in range(100):
            for j in range(100):
                a, b, c, d = x[i, j], y[i, j], z0[i, j], z1[i, j]
                line += ','.join([str(s) for s in [a, b, c, d]]) + '\n'
        f.write(line)
generate_2D_data1()

def norm1D_plot(mean=0, sigma=0.1):
    data, Gaussian = Gaussian_Distribution(N=1, M=1000, m=mean, sigma=sigma)
    x = np.linspace(-1, 1, 1000)
    # 计算一维高斯概率
    y = Gaussian.pdf(x)
    plt.plot(x, y)
    plt.show()
# norm1D_plot()

def norm2D_plot(mean=0, sigma=0.1):
    M = 1000
    data, Gaussian = Gaussian_Distribution(N=2, M=mean, sigma=sigma)
    # 生成二维网格平面
    X, Y = np.meshgrid(np.linspace(-1, 1, M), np.linspace(-1, 1, M))
    # 二维坐标数据
    d = np.dstack([X, Y])
    # 计算二维联合高斯概率
    Z = Gaussian.pdf(d).reshape(M, M)

    '''二元高斯概率分布图'''
    fig = plt.figure(figsize=(12, 8))
    ax = Axes3D(fig)
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='seismic', alpha=0.8)
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    plt.show()
# norm2D_plot(mean=0, sigma=0.1)


import math
import glob
import pandas as pd
from scipy import stats

all_files = glob.glob("./microdata-enem-2016/part-*")
frame = pd.concat((pd.read_csv(f, header=-1)
                   for f in all_files))

with open('../data/microdata-enem-2016/labels', 'r') as f:
    labels = f.readline().split(',')

frame.columns = labels

matrix = frame.as_matrix()

rs = [0] * 45

print("--- PEARSON'S R ---")

for i in range(45):
    rs[i] = (
        i,
        stats.pearsonr(matrix[i], matrix[48])[0],
    )

rs.sort(key=lambda x: x[1])

for i in range(45):
    print((labels[rs[i][0]] + ": {}").format(rs[i][1]))

print("--- REFLECTIVE CORRELATION COEFFICIENT ---")

def alternative_r(xs, ys):
    # reflective correlation coefficient
    sxy = 0
    sx = 0
    sy = 0
    for i in range(xs.size):
        sxy += xs[i]*ys[i]
        sx += xs[i]**2
        sy += ys[i]**2

    return sxy/math.sqrt(sx*sy)

for i in range(45):
    rs[i] = (
        i,
        alternative_r(matrix[i], matrix[48]),
    )

rs.sort(key=lambda x: x[1])

for i in range(45):
    print((labels[rs[i][0]] + ": {}").format(rs[i][1]))


import numpy as np
import math

class StaticQuantizer():
    def __init__(self, width=4, full_scale=1.0):
        self.lsb = full_scale/(2**(width-1))
        self.width = width

    def quantize(self, inp_sig):
        quantized_signal = np.array([round(x/self.lsb) for x in inp_sig])
        return np.clip(quantized_signal, -2**(self.width-1), 2**(self.width-1))

    def __call__(self, inp_sig):
        return self.quantize(inp_sig)

# Assuming Binary Fixed Point Representation
class Quantizer():
    def __init__(self, width=4, full_scale=1.0, signed=False, lsb=None, **kwargs):
        self.signed = signed
        self.full_scale = full_scale
        self.width = width
        self.lsb = lsb

    # Performs quantization of the given fixed point size given in the constructor
    def quantize(self, ideal_signal):

        lsb = self.full_scale/(2**self.width)
        quantized_signal = np.array([round(x / lsb)*lsb for x in ideal_signal])

        if self.signed:
            return np.clip(quantized_signal, -self.full_scale, self.full_scale)
        else:
            return np.clip(quantized_signal, 0, self.full_scale)

    def quantize_int(self, ideal_signal):

        lsb = self.full_scale/(2**self.width)
        quantized_signal = np.array([round(x / lsb) for x in ideal_signal])

        if self.signed:
            return np.clip(quantized_signal, -self.full_scale*2**self.width, self.full_scale)
        else:
            return np.clip(quantized_signal, 0, self.full_scale)

    def quantize_2s_comp(self, ideal_signal):
        # Assume width is the size of N-bits 2's complement. E.g. width == 2 --> [-4, 3]

        # self.signed must be true
        assert(self.signed)

        # Set the maximum magnitude ideal signal value to correspond to -127 or
        # 128 depending on if it is signed.
        if self.lsb is None:
            max_idx = np.argmax(np.absolute(ideal_signal))
            if ideal_signal[max_idx] < 0:
                lsb = -ideal_signal[max_idx] / 2**(self.width-1)
            else:
                lsb = ideal_signal[max_idx] / (2**(self.width-1) - 1)
            self.lsb = lsb

        if isinstance(ideal_signal, (list, np.ndarray)):
            quantized_signal = np.array([round(x / self.lsb) for x in ideal_signal])
            return np.clip(quantized_signal, -1 * 2**(self.width-1), 2**(self.width-1)-1)
        else:
            quantized_signal = round(ideal_signal / self.lsb)
            return max(min(quantized_signal, 2**(self.width-1)-1), -1 * 2**(self.width-1))

# Used for testing
if __name__ == "__main__":
    q = Quantizer()
    l = 10 * np.random.random_sample(10) + -10*np.random.random_sample(10)
    print(l)
    print(q.quantize(l))
    print(q.quantize_int(l))


from sys import argv

import matplotlib.pyplot as plt
import numpy as np

from utils import *
from polynomial import *

import pylab as py

def plot(dots: list[float], polynomials: list[Polynomial]) -> None:
    x, y = [p[0] for p in dots], [p[1] for p in dots]
    a, b = min(x), max(x)
    x_arg = np.linspace(a, b)

    plt.clf()

    for i in range(len(polynomials)):
        plt.subplot(1, len(polynomials), i + 1)
        plt.title(type(polynomials[i]).__name__)
        plt.xlabel("x")
        plt.ylabel("y")
        plt.grid(which='minor', color='k', linestyle=':')
        plt.grid(which='major', color='k')
        plt.minorticks_on()

        plt.plot(x, y, "xk")

        plt.plot(x_arg, [polynomials[i](arg) for arg in x_arg])

    plt.show()

if __name__ == "__main__":
    dots = read_dots(argv[1])

    print("Loaded table:")

    print_dots(dots)

    print("Enter polynomial degree and X value:")
    deg, x = read_func_data()

    newton = NewtonPolynomial.build(dots, x, deg)

    print("Newton value in {: <5.6g} is {: <5.6g}".format(x, newton(x)))

    hermite = HermitePolynomial.build(dots, x, deg)

    print("Hermite value in {: <5.6g} is {: <5.6g}".format(x, hermite(x)))

    inv_dots = [[p[1], p[0]] for p in dots]
    root = NewtonPolynomial.build(inv_dots, 0.0, deg)(0.0)

    print(
        "Root found by inverted interpolation method is {: <5.6g}".format(root))

    plot(dots, [newton, hermite])


# this package is curve-fitting using Chebyshev polynomial 1st kind and then generating polynomial coefficient
# The methodology is developed by Prof. Kwang Soo Cho
# The source code is developed by Gun Woo Park

from numpy import *
from scipy import linalg

def factorial(N):
    re = 1
    for i in range(1,N+1):
        re *= i
    return re

def Chebyshev_1st(n, x):
    if n==0:
        return 1.0
    elif n==1:
        return x
    return 2.0*x*Chebyshev_1st(n-1, x) - Chebyshev_1st(n-2, x)

def coe_Chebyshev_1st(k, n):
    if k<0 or k > n or (k==0 and n==1):
        return 0
    elif (k==1 and n==1) or (k==0 and n==0):
        return 1
    else:
        return 2*coe_Chebyshev_1st(k-1, n-1) - coe_Chebyshev_1st(k, n-2)

def cal_Cheby_1st(cn, x):
    N_n = size(cn)
    N_x = size(x)
    y = zeros(N_x)
    for i in range(N_x):
        for k in range(N_k):
            y[i] += Chebyshev_1st(k, x)*x
    return 0

# def L_nk(N, t):
#     L = zeros([N, N])
#     for i in range(N):
#         for j in range(N):
#             for k in range(size(t)):
#                 L[i,j] += Chebyshev_1st(i, t[k])*Chebyshev_1st(j, t[k])
#     return L_nk

def get_Cheby_1st_coe(t, y, N):
    a = zeros(N) # coefficient
    L = zeros([N, N]) #La = yT
    for i in range(N):
        for j in range(N):
            for k in range(size(t)):
                L[i,j] += Chebyshev_1st(i, t[k])*Chebyshev_1st(j, t[k])

    yT = zeros(N)
    for i in range(N):
        for j in range(size(y)):
            yT[i] += y[j]*Chebyshev_1st(i, t[j])
    a = linalg.inv(L).dot(yT)
    return a

def get_polynomial_from_Cheby(x, y, N):
    # note that dx must be constant for this method
    x_min = min(x)
    x_max = max(x)
    x_c = (max(x) - min(x))/2.
    dx = x[1] - x[0]
    t = 2.*(x - x_c)/dx
    a = get_Cheby_1st_coe(t, y, N)

    b = zeros(N)

    for n in range(N):
        for k in range(n, N):

            b[n] += coe_Chebyshev_1st(k, n)*a[k]
    c = zeros(N)
    for n in range(N):
        for k in range(n, N):
            c[n] += ((2./dx)**k)*(factorial(k)/(factorial(n)*factorial(k-n)))*((-x_c)**(k-n))*b[k]
    return c

def gen_from_polynomial(x, c):
    N_x = size(x)
    y = zeros(N_x)
    for i in range(N_x):
        for k in range(size(c)):
            y[i] += c[k]*x[i]**k
    return y

def diff_gen_from_polynomial(x, c):
    N_x = size(x)
    y = zeros(N_x)
    for i in range(N_x):
        for k in range(size(c)-1):
            y[i] += c[k+1]*float(k+1)*x[i]**(k)
    return y


'''
Define Bernstein Polynomials

i and p index the BPs
t is the time variable
h is the size of the timestep
'''

import scipy.special
import math

def BP(t: float, i: int, p: int, h: float):
	if i < 1 or i > p:
		return 0
	elif t >= 0 and t <= h:
		norm_coef = scipy.special.binom(p-1,i-1)
		t = t/h
		return norm_coef * t**(i-1) *  (1-t)**(p-i)
	else:
		return 0

def dBP(t: float, i: int, p: int, h: float):
	if t >= 0 and t <= h:
		return (p-1)*(BP(t, i-1, p-1, h) - BP(t, i, p -1, h) ) / h
	else:
		return 0


import numpy

def gaussian_kernel(x1, x2, sigma):
    """
    Computes the radial basis function
    Returns a radial basis function kernel between x1 and x2.

    Parameters
    ----------
    x1 :  numpy ndarray
        A vector of size (n, ), representing the first datapoint.

    x2 : numpy ndarray
        A vector of size (n, ), representing the second datapoint.

    sigma : float
        The standard deviation (bandwidth) parameter for the Gaussian kernel.

    Returns
    -------
    rbf : float
        The computed RBF between the two provided data points.

    Instructions
    ------------
    Fill in this function to return the similarity between `x1` and `x2`
    computed using a Gaussian kernel with bandwidth `sigma`.
    """
    rbf = 0
    numr = -numpy.sum((x1 - x2)**2)
    denr = 2*(sigma**2)
    rbf = numpy.exp(numr/denr)

    return rbf


import numpy as np
class Perceptron:

    def __init__(self):
        self.inputs = np.array([[1,1,1,1], [-1,1,-1,-1], [1, 1, 1, -1], [1, -1, -1, 1]])
        self.targets = np.array([1,1,-1,-1])
        self.learning_rate = 1
        self.weights = np.array([0,0,0,0])
        self.bias_weight = 0
        super().__init__()

    def train(self, epochs):
        for o in range (epochs):
            for aux,i in enumerate(self.inputs):
                ans = self.predict(i)
                if ans != self.targets[aux]:
                    for idx,val in enumerate(self.weights):
                        self.weights[idx] += (self.targets[aux] - ans) * self.inputs[aux][idx] * self.learning_rate
                        self.bias_weight += (self.targets[aux] - ans) * self.learning_rate
        return self.weights

    def predict(self,inputs):
        sum = 0
        for indx,i in enumerate(inputs):
            sum += i * self.weights[indx]
        sum += self.bias_weight
        return self.step(sum)

    def step(self, x):
        return 1 if x >= 0 else -1

P = Perceptron()
epochs = 10
weights = P.train(epochs)
inputs_t = np.array([[1,1,1,1], [-1,1,-1,-1], [1, 1, 1, -1], [1, -1, -1, 1]])
print("\nEntradas:")
print(inputs_t)
print("\nSaidas:")
for e in inputs_t:
    res = P.predict(e)
    print(res)
print("\nPesos obtidos no treinamento com " + str(epochs) + " epocas\n")
print(weights)


######################### Test Dataset #########################

# generate gaussian data samples
from numpy.random import seed
from numpy.random import randn
from numpy import mean
from numpy import std
# seed the random number generator
seed(1)
# generate two sets of univariate observations
data1 = 5 * randn(100) + 50
data2 = 5 * randn(100) + 51
# summarize
print('data1: mean=%.3f stdv=%.3f' % (mean(data1), std(data1)))
print('data2: mean=%.3f stdv=%.3f' % (mean(data2), std(data2)))

######################### Mann-Whitney U Test #########################

# Mann-Whitney U test
from numpy.random import seed
from numpy.random import randn
from scipy.stats import mannwhitneyu
# seed the random number generator
seed(1)
# generate two independent samples
data1 = 5 * randn(100) + 50
data2 = 5 * randn(100) + 51
# compare samples
stat, p = mannwhitneyu(data1, data2)
print('Statistics=%.3f, p=%.3f' % (stat, p))
# interpret
alpha = 0.05
if p > alpha:
	print('Same distribution (fail to reject H0)')
else:
	print('Different distribution (reject H0)')

######################### Wilcoxon Signed-Rank Test #########################

# Wilcoxon signed-rank test
from numpy.random import seed
from numpy.random import randn
from scipy.stats import wilcoxon
# seed the random number generator
seed(1)
# generate two independent samples
data1 = 5 * randn(100) + 50
data2 = 5 * randn(100) + 51
# compare samples
stat, p = wilcoxon(data1, data2)
print('Statistics=%.3f, p=%.3f' % (stat, p))
# interpret
alpha = 0.05
if p > alpha:
	print('Same distribution (fail to reject H0)')
else:
	print('Different distribution (reject H0)')

######################### Kruskal-Wallis H Test #########################

# generate three independent samples
data1 = 5 * randn(100) + 50
data2 = 5 * randn(100) + 50
data3 = 5 * randn(100) + 52

# Kruskal-Wallis H-test
from numpy.random import seed
from numpy.random import randn
from scipy.stats import kruskal
# seed the random number generator
seed(1)
# generate three independent samples
data1 = 5 * randn(100) + 50
data2 = 5 * randn(100) + 50
data3 = 5 * randn(100) + 52
# compare samples
stat, p = kruskal(data1, data2, data3)
print('Statistics=%.3f, p=%.3f' % (stat, p))
# interpret
alpha = 0.05
if p > alpha:
	print('Same distributions (fail to reject H0)')
else:
	print('Different distributions (reject H0)')

#########################  Friedman Test #########################

# Friedman test
from numpy.random import seed
from numpy.random import randn
from scipy.stats import friedmanchisquare
# seed the random number generator
seed(1)
# generate three independent samples
data1 = 5 * randn(100) + 50
data2 = 5 * randn(100) + 50
data3 = 5 * randn(100) + 52
# compare samples
stat, p = friedmanchisquare(data1, data2, data3)
print('Statistics=%.3f, p=%.3f' % (stat, p))
# interpret
alpha = 0.05
if p > alpha:
	print('Same distributions (fail to reject H0)')
else:
	print('Different distributions (reject H0)')


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Dec 28 10:48:04 2020

@author: jane_hsieh
"""
import numpy as np
import pandas as pd
#============================================== 0. Input ==============================================
# Observation O_t
Obs = pd.read_csv('Sample_Obs.csv', index_col = 0).values.reshape(-1,).tolist()
Obs = Obs[:100]

#Parameters
Obs_state = {str(i+1): i for i in range(6)} #name: index
Hid_state = {'F': 0, 'L': 1}                #name: index
pF = [1/6 for i in range(6)]
pL=[0.1, 0.1, 0.1, 0.1, 0.1, 0.5]

## HMM-related parameters
Pi = np.array([0.5, 0.5])   # initial probs of hidden states {0, 1}  [0: fair dice; 1: Loaded dice]; p(S_0=1) = 1-pi_0
P = np.array([[0.95, 0.05], [0.05, 0.95]]) #Transition matrix T = [p_{i,j}] = [p(Sj|Si)]
E = np.array([pF,pL]) #Emission matrix E = [q_i^y] = [p(O=y|S=i)]

#============================================== 1. Alpha ==============================================

def Build_Alpha(Obs, Pi, P, E):
    '''
    Use Forward Algorithm to compute Alpha (dim=TxK)
    Alpha = [alpha_1, ..., alpha_T]^T, alpha_t = [alpha_t^0, ..., alpha_t^{K-1}]^T
    Issue: if T too large, Alpha_t --> 0 as t--> T
    '''
    T = len(Obs)
    K = len(Pi)
    Alpha = np.zeros([T,K])

    #Initial:
    index_E = Obs_state[str(Obs[0])]
    Alpha[0] = Pi * E[:, index_E]

    #Iterate:
    for t in range(1,T):
        index_E = Obs_state[str(Obs[t])]
        Alpha[t] = np.dot(Alpha[t-1], P) * E[:, index_E]

    return Alpha

Alpha = Build_Alpha(Obs, Pi, P, E)

Evaluation = Alpha[-1].sum()

#============================================== 2. Beta ==============================================
def Build_Beta(Obs, Pi, P, E):
    '''
    Use Backward Algorithm to compute Beta (dim=TxK)
    Beta = [beta_1, ..., beta_T]^T, beta_t = [beta_t^0, ..., beta_t^{K-1}]^T
    '''
    T = len(Obs)
    K = len(Pi)
    Beta = np.zeros([T,K])

    #Initial:
    Beta[T-1] = np.ones(K)

    #Iterate:
    for t in range(T-2,-1,-1):
        index_E = Obs_state[str(Obs[t+1])]
        Beta[t] = np.dot(Beta[t+1] * E[:, index_E], P.T)

    return Beta

Beta = Build_Beta(Obs, Pi, P, E)

#Termination:
# Decoding1 matrix is p(S_t=k|{O_t}_{t=1}^T)
Decoding1 = Alpha * Beta
Decoding1 = Decoding1 / Decoding1.sum(axis =1)[:, np.newaxis]

Seq_Decoding1 = Decoding1.argmax(axis = 1)

#============================================== 3. Viterbi & Traceback ==================================================
def Build_Viterbi(Obs, Pi, P, E):
    '''
    Use Viterbi Algorithm to compute V (dim=TxK)
    V = [V_1, ..., V_T]^T, V_t = [V_t^0, ..., V_t^{K-1}]^T
    '''
    T = len(Obs)
    K = len(Pi)
    V = np.zeros([T,K])

    #Initial:
    index_E = Obs_state[str(Obs[0])]
    V[0] = Pi * E[:, index_E]

    #Iterate:
    for t in range(1,T):
        index_E = Obs_state[str(Obs[t])]
        V[t] =   (V[t-1].reshape(-1,1) * P).max(axis=0)  * E[:, index_E]

    return V

V = Build_Viterbi(Obs, Pi, P, E)

def Viterbi_Traceback(V):
    '''
    find most likely assignment of state sequence (i.e., Seq_Decoding2)
    '''
    T, K = V.shape
    Seq_Decoding2 = np.zeros(T)

    #Termination:
    Seq_Decoding2[T-1] = V[T-1].argmax()

    #Iterate:
    for t in range(T-1,0,-1):
        index_P = int(Seq_Decoding2[t] )
        Seq_Decoding2[t-1] = (V[t-1] * P[:, index_P]).argmax()

    return Seq_Decoding2

Seq_Decoding2 = Viterbi_Traceback(V)

Hidden Markov Model (HMM)/Dice Simulation Data/Step2. HMM Algorithms Breakdown/Step2. HMM Algorithms Breakdown.py


"""

https://apmonitor.com/pdc/index.php/Main/ModelSimulation
http://web.mit.edu/2.14/www/Handouts/StateSpaceResponse.pdf

Integrate the differential equation in various ways,
but give the approximately the same solution numerically

"""

from math import *
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
from scipy.integrate import odeint
from scipy.linalg import expm
from sympy.abc import s, t
from sympy.integrals import inverse_laplace_transform

# Simulate taup * dy/dt = -y + K*u
Kp = 10
taup = 3

# (1) Transfer Function
num = [Kp]
den = [taup,1]
sys1 = signal.TransferFunction(num,den)
t1,y1 = signal.step(sys1)

# (2) State Space
A = -1.0/taup
B = Kp/taup
C = 1.0
D = 0.0
sys2 = signal.StateSpace(A,B,C,D)
t2,y2 = signal.step(sys2)

# (3) ODE Integrator
def model3(y, t):
    u = 1.0
    return (-y + Kp * u)/taup
t3 = np.linspace(0,14,100)
y3 = odeint(model3,0,t3)

# (4) Closed Form
# y' = -ay + b
# a = -1/taup
# b = Kp*u/taup
# k1 is the arbitrary constant based on initial condition
# y(0) = 0
# y(t) = b/a + k1*exp(-a*t)
def closed(t):
    u = 1.0
    a = 1.0/taup
    b = Kp*u/taup
    k1 = -b/a
    return b/a + k1*exp(-a*t)
y4 = np.array([closed(t) for t in t3])

# (5) Symbolic
# We multiply the transfer function G(s) with the unit step response U(s)
# and then take the inverse laplace transform to find the equation
# for evaluation
G = Kp/(taup*s + 1)
U = 1/s
Y = inverse_laplace_transform(G*U, s, t)
y5 = np.array([Y.subs(t, ts) for ts in t3])

# (6) Manual State Space
# This should be the same as (2) but we do it manually rather than using a library
# x(0) = 0 with unit step function gives rise to the following update equation
# x(t) = A**-1*(expm(A*t) - I)*B
# y(t) = C*x(t) + D*step(t)
def stspsim(t):
    MA = np.array([[A]])
    MB = np.array([[B]])
    MC = np.array([[C]])
    MD = np.array([[D]])
    I = np.array([[1.0]])

    x = MA**-1 @ (expm(MA*t) - I) @ MB
    y = MC*x + MD*np.heaviside(t, 1)
    return y
y6 = np.array([stspsim(t) for t in t3])
y6 = y6.flatten()

print("Transfer Function: {}".format(Y))

plt.figure(1)
plt.plot(t1,y1,'b--',linewidth=6,label='Transfer Function')
plt.plot(t2,y2,'g:',linewidth=5,label='State Space')
plt.plot(t3,y3,'r-',linewidth=4,label='ODE Integrator')
plt.plot(t3,y4,'p-',linewidth=3,label='Closed Form')
plt.plot(t3,y5,'c-',linewidth=2,label='Symbolic')
plt.plot(t3,y6,'m*',linewidth=1,label='State Space Manual')
plt.xlabel('Time')
plt.ylabel('Response (y)')
plt.legend(loc='best')
plt.savefig('1st_order.png')
plt.show()


'''
Applying an MA Model

The bouncing of the stock price between bid and ask induces a negative first order autocorrelation, but no autocorrelations at lags higher than 1. You get the same ACF pattern with an MA(1) model. Therefore, you will fit an MA(1) model to the intraday stock data from the last exercise.

The first step is to compute minute-by-minute returns from the prices in intraday, and plot the autocorrelation function. You should observe that the ACF looks like that for an MA(1) process. Then, fit the data to an MA(1), the same way you did for simulated data.

INSTRUCTIONS
100XP
Import plot_acf and ARMA modules from statsmodels
Compute minute-to-minute returns from prices:
Compute returns with the .pct_change() method
Use the pandas method .dropna() to drop the first row of returns, which is NaN
Plot the ACF function with lags up to 60 minutes
Fit the returns data to an MA(1) model and print out the MA(1) parameter
'''
# Import plot_acf and ARMA modules from statsmodels
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.tsa.arima_model import ARMA

# Compute returns from prices and drop the NaN
returns = intraday.pct_change()
returns = returns.dropna()

# Plot ACF of returns with lags up to 60 minutes
plot_acf(returns, lags=60)
plt.show()

# Fit the data to an MA(1) model
mod = ARMA(returns, order=(0,1))
res = mod.fit()
print(res.params)


import math
import random

import cv2
import numpy as np

eTranslate = 0
eHomography = 1

def computeHomography(f1, f2, matches, A_out=None):
    '''
    Input:
        f1 -- list of cv2.KeyPoint objects in the first image
        f2 -- list of cv2.KeyPoint objects in the second image
        matches -- list of cv2.DMatch objects
            DMatch.queryIdx: The index of the feature in the first image
            DMatch.trainIdx: The index of the feature in the second image
            DMatch.distance: The distance between the two features
        A_out -- ignore this parameter. If computeHomography is needed
                 in other TODOs, call computeHomography(f1,f2,matches)
    Output:
        H -- 2D homography (3x3 matrix)
        Takes two lists of features, f1 and f2, and a list of feature
        matches, and estimates a homography from image 1 to image 2 from the matches.
    '''
    num_matches = len(matches)

    # Dimensions of the A matrix in the homogenous linear
    # equation Ah = 0
    num_rows = 2 * num_matches
    num_cols = 9
    A_matrix_shape = (num_rows,num_cols)
    A = np.zeros(A_matrix_shape)

    for i in range(len(matches)):
        m = matches[i]
        (a_x, a_y) = f1[m.queryIdx].pt
        (b_x, b_y) = f2[m.trainIdx].pt

        #BEGIN TODO 2
        #Fill in the matrix A in this loop.
        #Access elements using square brackets. e.g. A[0,0]
        #TODO-BLOCK-BEGIN
        A[2*i, 0] = a_x
        A[2*i, 1] = a_y
        A[2*i, 2] = 1
        A[2*i, 3] = 0
        A[2*i, 4] = 0
        A[2*i, 5] = 0
        A[2*i, 6] = -b_x * a_x
        A[2*i, 7] = -b_x * a_y
        A[2*i, 8] = -b_x

        A[2*i+1,0] = 0
        A[2*i+1,1] = 0
        A[2*i+1,2] = 0
        A[2*i+1,3] = a_x
        A[2*i+1,4] = a_y
        A[2*i+1,5] = 1
        A[2*i+1,6] = -b_y * a_x
        A[2*i+1,7] = -b_y * a_y
        A[2*i+1,8] = -b_y

        #TODO-BLOCK-END
        #END TODO

    U, s, Vt = np.linalg.svd(A)

    if A_out is not None:
        A_out[:] = A

    #s is a 1-D array of singular values sorted in descending order
    #U, Vt are unitary matrices
    #Rows of Vt are the eigenvectors of A^TA.
    #Columns of U are the eigenvectors of AA^T.

    #Homography to be calculated
    H = np.eye(3)

    #BEGIN TODO 3
    #Fill the homography H with the appropriate elements of the SVD
    #TODO-BLOCK-BEGIN
    minsv = Vt.shape[0] - 1
    H = (Vt[minsv] / Vt[minsv][8]).reshape(3,3)
    #TODO-BLOCK-END
    #END TODO

    return H

def alignPair(f1, f2, matches, m, nRANSAC, RANSACthresh):
    '''
    Input:
        f1 -- list of cv2.KeyPoint objects in the first image
        f2 -- list of cv2.KeyPoint objects in the second image
        matches -- list of cv2.DMatch objects
            DMatch.queryIdx: The index of the feature in the first image
            DMatch.trainIdx: The index of the feature in the second image
            DMatch.distance: The distance between the two features
        m -- MotionModel (eTranslate, eHomography)
        nRANSAC -- number of RANSAC iterations
        RANSACthresh -- RANSAC distance threshold

    Output:
        M -- inter-image transformation matrix
        Repeat for nRANSAC iterations:
            Choose a minimal set of feature matches.
            Estimate the transformation implied by these matches
            count the number of inliers.
        For the transformation with the maximum number of inliers,
        compute the least squares motion estimate using the inliers,
        and return as a transformation matrix M.
    '''

    #BEGIN TODO 4
    #Write this entire method.  You need to handle two types of
    #motion models, pure translations (m == eTranslation) and
    #full homographies (m == eHomography).  However, you should
    #only have one outer loop to perform the RANSAC code, as
    #the use of RANSAC is almost identical for both cases.

    #Your homography handling code should call compute_homography.
    #This function should also call get_inliers and, at the end,
    #least_squares_fit.
    #TODO-BLOCK-BEGIN
    Sample_size = 1 if m == eTranslate else 4
    max_inlier = 0
    count_inlier = []

    for i in range(nRANSAC):
        H = np.eye(3)
        randMatch = np.random.choice(matches, Sample_size)

        if m == eTranslate:
            match = randMatch[0]
            H[0][0] = 1
            H[0][1] = 0
            H[0][2] = f2[match.trainIdx].pt[0] - f1[match.queryIdx].pt[0]
            H[1][0] = 0
            H[1][1] = 1
            H[1][2] = f2[match.trainIdx].pt[1] - f1[match.queryIdx].pt[1]
            H[2][0] = 0
            H[2][1] = 0
            H[2][2] = 1
        elif m == eHomography:
            H = computeHomography(f1,f2,randMatch)

        num_inliers = getInliers(f1, f2, matches, H, RANSACthresh)
        if len(num_inliers) > max_inlier:
            count_inlier = num_inliers
            max_inlier = len(num_inliers)

        #call least_squares_fit
        M = leastSquaresFit(f1, f2, matches, m, count_inlier)
    #TODO-BLOCK-END
    #END TODO
    return M

def getInliers(f1, f2, matches, M, RANSACthresh):
    '''
    Input:
        f1 -- list of cv2.KeyPoint objects in the first image
        f2 -- list of cv2.KeyPoint objects in the second image
        matches -- list of cv2.DMatch objects
            DMatch.queryIdx: The index of the feature in the first image
            DMatch.trainIdx: The index of the feature in the second image
            DMatch.distance: The distance between the two features
        M -- inter-image transformation matrix
        RANSACthresh -- RANSAC distance threshold

    Output:
        inlier_indices -- inlier match indices (indexes into 'matches')

        Transform the matched features in f1 by M.
        Store the match index of features in f1 for which the transformed
        feature is within Euclidean distance RANSACthresh of its match
        in f2.
        Return the array of the match indices of these features.
    '''

    inlier_indices = []

    for i in range(len(matches)):
        #BEGIN TODO 5
        #Determine if the ith matched feature f1[id1], when transformed
        #by M, is within RANSACthresh of its match in f2.
        #If so, append i to inliers
        #TODO-BLOCK-BEGIN
        # features
        m = matches[i]
        (a_x, a_y) = f1[m.queryIdx].pt
        (b_x, b_y) = f2[m.trainIdx].pt

        # Transform
        p = np.zeros(3)
        p[0] = a_x
        p[1] = a_y
        p[2] = 1
        translated = np.dot(M , p)
        xt = translated[0] / translated[2]
        yt = translated[1] / translated[2]

        # Euclidean distance
        x_diff = (b_x - xt) * (b_x - xt)
        y_diff = (b_y - yt) * (b_y - yt)
        distance = np.sqrt(x_diff + y_diff)
        if distance <= RANSACthresh:
        	inlier_indices.append(i)
        #TODO-BLOCK-END
        #END TODO

    return inlier_indices

def leastSquaresFit(f1, f2, matches, m, inlier_indices):
    '''
    Input:
        f1 -- list of cv2.KeyPoint objects in the first image
        f2 -- list of cv2.KeyPoint objects in the second image
        matches -- list of cv2.DMatch objects
            DMatch.queryIdx: The index of the feature in the first image
            DMatch.trainIdx: The index of the feature in the second image
            DMatch.distance: The distance between the two features
        m -- MotionModel (eTranslate, eHomography)
        inlier_indices -- inlier match indices (indexes into 'matches')

    Output:
        M - transformation matrix

        Compute the transformation matrix from f1 to f2 using only the
        inliers and return it.
    '''

    # This function needs to handle two possible motion models,
    # pure translations (eTranslate)
    # and full homographies (eHomography).

    M = np.eye(3)

    if m == eTranslate:
        #For spherically warped images, the transformation is a
        #translation and only has two degrees of freedom.
        #Therefore, we simply compute the average translation vector
        #between the feature in f1 and its match in f2 for all inliers.

        u = 0.0
        v = 0.0

        for i in range(len(inlier_indices)):
            #BEGIN TODO 6
            #Use this loop to compute the average translation vector
            #over all inliers.
            #TODO-BLOCK-BEGIN
            (a_x, a_y) = f1[matches[inlier_indices[i]].queryIdx].pt
            (b_x, b_y) = f2[matches[inlier_indices[i]].trainIdx].pt

        	#sum the difference
            u = u + b_x - a_x
            v = v + b_y - a_y
            #TODO-BLOCK-END
            #END TODO

        u /= len(inlier_indices)
        v /= len(inlier_indices)

        M[0,2] = u
        M[1,2] = v

    elif m == eHomography:
        #BEGIN TODO 7
        #Compute a homography M using all inliers.
        #This should call computeHomography.
        #TODO-BLOCK-BEGIN
        inmatches = []
        for i in range(len(inlier_indices)):
        	inmatches.append(matches[inlier_indices[i]])

        M = computeHomography(f1,f2,inmatches)
        #TODO-BLOCK-END
        #END TODO

    else:
        raise Exception("Error: Invalid motion model.")

    return M


"""
ndarray indexing
"""

import numpy as np
# given this
arr = np.array([[-1, 2, 0, 4],
                [4, -0.5, 6, 0],
                [2.6, 0, 7, 8],
                [3, -7, 4, 2.0]])

# grab first two rows of arr
a = arr[:2]

# grab second and third row of arr
b = arr[1:3]

# grab first two rows of arr and first two columns
c = arr[:2, :2]

# grab elements at indices (0, 3), (1, 2), (2, 1), (3, 0)
d = arr[[0, 1, 2, 3], [3, 2, 1, 0]]

# grab elements > 0
cond = arr > 0  # cond is a boolean array
e = arr[cond]


import numpy as np
import matplotlib.pyplot as plt

# X (ndarray)
# [[ 3.38156267  3.38911268]
#  [ 4.52787538  5.8541781 ]
#  [ 2.65568187  4.41199472]]

# support functions ---------------------------------------
def plot_n_image(X, n):
    """ plot first n images
    n has to be a square number
    """
    pic_size = int(np.sqrt(X.shape[1]))
    grid_size = int(np.sqrt(n))

    first_n_images = X[:n, :]

    fig, ax_array = plt.subplots(nrows=grid_size, ncols=grid_size,
                                    sharey=True, sharex=True, figsize=(8, 8))

    for r in range(grid_size):
        for c in range(grid_size):
            ax_array[r, c].imshow(first_n_images[grid_size * r + c].reshape((pic_size, pic_size)))
            plt.xticks(np.array([]))
            plt.yticks(np.array([]))

# PCA functions ---------------------------------------
def covariance_matrix(X):
    """
    Args:
        X (ndarray) (m, n)
    Return:
        cov_mat (ndarray) (n, n):
            covariance matrix of X
    """
    m = X.shape[0]

    return (X.T @ X) / m

def normalize(X):
    """
        for each column, X-mean / std
    """
    X_copy = X.copy()
    m, n = X_copy.shape

    for col in range(n):
        X_copy[:, col] = (X_copy[:, col] - X_copy[:, col].mean()) / X_copy[:, col].std()

    return X_copy

def pca(X):
    """
    http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.svd.html
    Args:
        X ndarray(m, n)
    Returns:
        U ndarray(n, n): principle components
    """
    # 1. normalize data
    X_norm = normalize(X)

    # 2. calculate covariance matrix
    Sigma = covariance_matrix(X_norm)  # (n, n)

    # 3. do singular value decomposition
    # remeber, we feed cov matrix in SVD, since the cov matrix is symmetry
    # left sigular vector and right singular vector is the same, which means
    # U is V, so we could use either one to do dim reduction
    U, S, V = np.linalg.svd(Sigma)  # U: principle components (n, n)

    return U, S, V

def project_data(X, U, k):
    """
    Args:
        U (ndarray) (n, n)
    Return:
        projected X (n dim) at k dim
    """
    m, n = X.shape

    if k > n:
        raise ValueError('k should be lower dimension of n')

    return X @ U[:, :k]

def recover_data(Z, U):
    m, n = Z.shape

    if n >= U.shape[0]:
        raise ValueError('Z dimension is >= U, you should recover from lower dimension to higher')

    return Z @ U[:, :n].T


""" Test of a Multi-layer Perceptron, the standard feedforward neural network.

In this program, the network can be trained to:
	- classify the rows of an identity matrix of size n, using log2(n) neurons in one hidden layer;
	- learn the n-dimensional XOR logical function.

@author Gabriel Nogueira (Talendar)
@author Marcel Otoboni
"""

from multilayer_perceptron import MultilayerPerceptron
from cost_functions import MeanSquaredError

import numpy as np
from functools import reduce
from operator import xor

def accuracy(labels, predictions):
	""" Given a model's predictions and a set of labels, calculates the model's accuracy. """
	hits = 0
	for l, p in zip(labels.T, predictions):
		try:
			if len(l) > 1:
				l = np.argmax(l, axis=0)
				p = np.argmax(p, axis=0)
		except TypeError:
			p = np.rint(p)

		if l == p:
			hits += 1

	return hits / len(labels)

def identity_n(nrows, epochs, lr, hidden_size):
	""" Trains the MLP to deal with the auto-encoder problem for a nxn-dimensional ID matrix."""
	print("\n" + 50*'#' + "\n")
	print("Training case: ID Matrix %dx%d." % (nrows, nrows))

	X, Y = np.identity(nrows), np.identity(nrows)
	mlp = MultilayerPerceptron(input_size=nrows, layers_size=[hidden_size, nrows], layers_activation="sigmoid")
	print("Initial accuracy: %.2f%%" % (100 * accuracy(mlp.predict(X), Y)))

	print("\nStarting training session...")
	mlp.fit(
		data=X, labels=Y,
		cost_function=MeanSquaredError(),
		epochs=epochs,
		learning_rate=lr,
	)

	predictions = mlp.predict(X)
	for x, y, h in zip(X, Y, predictions):
		h = np.rint(h)
		print("OUT: {}  |  EXPECTED: {}  ({})".format(np.rint(h), y, "OK" if np.array_equal(h, y) else "WRONG"))

	print("\nTraining session for ID Matrix %dx%d finished." % (nrows, nrows))
	print("MLP architecture: {}".format([nrows] + [hidden_size] + [nrows]))
	print("Final Accuracy : %.2f%%" % (100*accuracy(Y, mlp.predict(X))))

def make_xor_data(num_var):
	""" Creates a data set using the XOR logic function. """
	assert num_var > 1
	X, Y = [], []

	bin_str = "{:0%db}" % num_var
	for n in range(2**num_var):
		bits = [int(c) for c in bin_str.format(n)]
		Y.append(reduce(xor, bits))
		X.append(np.array(bits))

	return np.array(X), np.array(Y)

def xor_train(num_var, epochs, lr, hidden_layers):
	""" Trains an MLP to solve the n-dimensional XOR problem. """
	print("\n" + 50*'#' + "\n")
	print("Training case: XOR with %d variables." % num_var)

	X, Y = make_xor_data(num_var)
	mlp = MultilayerPerceptron(input_size=num_var, layers_size=hidden_layers + [1], layers_activation="sigmoid")
	print("Initial accuracy: %.2f%%" % (100 * accuracy(mlp.predict(X), Y)))

	print("\nStarting training session...")
	mlp.fit(
		data=X, labels=Y,
		cost_function=MeanSquaredError(),
		epochs=epochs,
		learning_rate=lr,
		batch_size=2**num_var,
	)

	predictions = mlp.predict(X)[0]
	for x, y, h in zip(X, Y, predictions):
		print("IN: {}  |  OUT: {:.2f} -> {}  |  EXPECTED: {})".format(x, h, np.rint(h), y))

	print("\nTraining session for %d variables XOR finished." % num_var)
	print("MLP architecture: {}".format([num_var] + hidden_layers + [1]))
	print("Final accuracy : %.2f%%\n" % (100 * accuracy(Y, predictions)))

if __name__ == "__main__":
	opt = -1
	while opt != 2:
		print(
			"\n\n< Multilayer-perceptron Tester >\n" +
			"\t[0] Auto-encoder (ID matrix)\n" +
			"\t[1] XOR\n" +
			"\t[2] Exit\n" +
			"Option: ", end=""
		)
		opt = int(input())

		if opt == 0 or opt == 1:
			print("\nLearning rate: ", end="")
			lr = float(input())

			print("Training epochs: ", end="")
			epochs = int(input())

			# ID(n)
			if opt == 0:
				print("Identity matrix size (n): ", end="")
				n = int(input())

				hidden_size = int(np.ceil(np.log2(n)))
				print("The hidden layer size is log2(n) = %d" % hidden_size)

				identity_n(n, epochs, lr, hidden_size)
			# XOR
			else:
				print("Number of variables for the XOR function: ", end="")
				num_xor = int(input())

				print("Hidden layers: ", end="")
				hidden_layers = [int(i) for i in input().split()]

				xor_train(num_xor, epochs, lr, hidden_layers)

	print("\nLeaving...\n")


import numpy as np
import time
from scipy.linalg import solve
def create_tridiag(l, r, u, n):
    A = np.zeros((n, n))
    temp = np.ones(n - 1)
    np.fill_diagonal(A, r)
    np.fill_diagonal(A[1:], l)
    np.fill_diagonal(A[:, 1:], u)
    return A

def seidel(a, x, b, Iteration= False):
    x0 = np.copy(x)
    n = len(x)
    # Finding X1
    for j in range(n):
        d = b[j]
        for i in range(0, n):
            if (j != i):
                d -= a[j][i] * x[i]
        x[j] = d / a[j][j]
    # Iterate Seidel
    while np.linalg.norm(np.subtract(x, x0)) > 0.0001:
        if Iteration == True:
            print("Iteration: " + str(x))
        x0 = np.copy(x)
        for j in range(0, n):
            # temp variable d to store b[j]
            d = b[j]
            for i in range(0, n):
                if (j != i):
                    d -= a[j][i] * x[i]
                # updating the value of our solution
            x[j] = d / a[j][j]
        # returning our updated solution
    return x

n = 50
a = create_tridiag(1, 4, -1, n)
b = np.ones(n)
x = np.ones(n)
print(x)

# loop run for m times depending on m the error value
start = time.time()
x = seidel(a, x, b, Iteration= False)
    # print each time the updated solution
end = time.time()
print("Scipy Result: ", solve(a, b))
print(x)
print("Time: ", end - start)


import functools

import numpy as np
import sympy
from matplotlib import pyplot as plt, animation
from matplotlib.animation import FuncAnimation
from numpy import sin, cos
from scipy.integrate import odeint

def get_expressions():
    """
    Create symoblic expressions for the system.

    Returns:
        Expressions for theta_dd_1 and theta_dd_2.
    """

    s = 'l_1 theta_dd_1 m_1 m_2 l_2 theta_dd_2 theta_1 theta_2 theta_d_2 theta_d_1 g'
    l_1, theta_dd_1, m_1, m_2, l_2, theta_dd_2, theta_1, theta_2, theta_d_2, theta_d_1, g = sympy.symbols(s)
    expr1 = l_1 * theta_dd_1 * (m_1 + m_2) + m_2 * l_2 * (theta_dd_2 * sympy.cos(theta_1 - theta_2) + theta_d_2 ** 2 * sympy.sin(theta_1 - theta_2)) + (m_1 + m_2) * g * sympy.sin(theta_1)
    expr2 = m_2 * l_2 * theta_dd_2 + m_2 * l_1 * (theta_dd_1 * sympy.cos(theta_1 - theta_2) - theta_d_1 ** 2 * sympy.sin(theta_1 - theta_2)) + m_2 * g * sympy.sin(theta_2)
    theta_dd_1_expr = sympy.solveset(expr1, theta_dd_1).args[0]
    theta_dd_2_eval = sympy.solveset(expr2.subs(theta_dd_1, theta_dd_1_expr), theta_dd_2).args[0]
    theta_dd_2_expr = sympy.solveset(expr2, theta_dd_2).args[0]
    theta_dd_1_eval = sympy.solveset(expr1.subs(theta_dd_2, theta_dd_2_expr), theta_dd_1).args[0]

    res1 = sympy.simplify(theta_dd_1_eval)
    res2 = sympy.simplify(theta_dd_2_eval)

    sympy.init_printing()

    print(res1, res2, sep='\n\n')

    return res1, res2

def double_pend(
    y: list,
    t: float,
    m_1: float,
    l_1: float,
    m_2: float,
    l_2: float,
    g: float
) -> list:
    """
    Calculate one step for coupled pendulums.

    Args:
        y: (theta_1, theta_d_1, theta_2, theta_d_2) at this timestep.
        t: Time (unused).
        m_1: Mass 1.
        l_1: Length of pendulum 1.
        m_2: Mass 2.
        l_2: Length of pendulum 2.
        g: Gravity constant.

    Returns:
        Derivative estimates for each element of y.
    """

    theta_1, theta_d_1, theta_2, theta_d_2 = y

    # From get_expressions()
    theta_dd_1 = (g*m_1*sin(theta_1) + g*m_2*sin(theta_1)/2 + g*m_2*sin(theta_1 - 2*theta_2)/2 + l_1*m_2*theta_d_1**2*sin(2*theta_1 - 2*theta_2)/2 + l_2*m_2*theta_d_2**2*sin(theta_1 - theta_2))/(l_1*(-m_1 + m_2*cos(theta_1 - theta_2)**2 - m_2))
    theta_dd_2 = (-g*m_1*sin(theta_2) + g*m_1*sin(2*theta_1 - theta_2) - g*m_2*sin(theta_2) + g*m_2*sin(2*theta_1 - theta_2) + 2*l_1*m_1*theta_d_1**2*sin(theta_1 - theta_2) + 2*l_1*m_2*theta_d_1**2*sin(theta_1 - theta_2) + l_2*m_2*theta_d_2**2*sin(2*theta_1 - 2*theta_2))/(2*l_2*(m_1 - m_2*cos(theta_1 - theta_2)**2 + m_2))

    dydt = [theta_d_1, theta_dd_1, theta_d_2, theta_dd_2]

    return dydt

def plot(
    y0: list,
    t: np.ndarray,
    system_params: dict,
    trajectory: bool = True,
    angles: bool = False,
    show: bool = True,
    save: str = ''
):
    """
    Animate the coupled pendulums.

    Args:
        y0: Initial conditions for (theta_1, theta_d_1, theta_2, theta_d_2).
        t: Times to evaluate equations of motion.
        system_params: Physical parameters of the system.
        trajectory: Animate trajectory.
        angles: Plot angles over time.
        show: Display trajectory animation.
        save: Filepath to save trajectory animation.

    """

    pend_func = functools.partial(double_pend, **system_params)

    theta_1, theta_d_1, theta_2, theta_d_2 = odeint(pend_func, y0, t).T

    if angles:
        theta_1 = theta_1 % np.pi
        theta_2 = theta_2 % np.pi

        plt.plot(t, theta_1)
        plt.plot(t, theta_2)
        plt.show()

    if trajectory:

        l_1, l_2 = system_params['l_1'], system_params['l_2']

        x_1 = l_1 * np.sin(theta_1)
        y_1 = l_1 * np.cos(theta_1)
        x_2 = x_1 + l_2 * np.sin(theta_2)
        y_2 = y_1 + l_2 * np.cos(theta_2)

        fig, ax = plt.subplots()

        ln, = ax.plot([], [], 'o-', lw=2)

        def init():
            bound = l_1 + l_2
            ax.set_xlim(-bound, bound)
            ax.set_ylim(-bound, bound)
            ln.set_data([], [])
            return ln,

        def update(vals):
            ln.set_data(vals[:3], vals[3:])
            return ln,

        frames = np.stack([np.zeros_like(x_1), x_1, x_2, np.zeros_like(y_1), -y_1, -y_2], axis=-1)

        ani = FuncAnimation(fig, update, init_func=init, frames=frames, blit=True, interval=25, repeat=False)

        if save:
            Writer = animation.writers['ffmpeg']
            writer = Writer(fps=40, metadata=dict(artist='Me'), bitrate=1800)

            ani.save(save, writer=writer)

        if show:
            plt.show()

if __name__ == '__main__':

    system_params = {
        'm_1': 1,
        'l_1': 1,
        'm_2': 1,
        'l_2': 1,
        'g': 9.81
    }

    # Initial conditions for (theta_1, theta_d_1, theta_2, theta_d_2)
    y0 = [np.pi / 2, 0, np.pi, 0]

    t = np.linspace(0, 20, 1000)

    plot(y0, t, system_params)

import numpy,from numpy,from scipy,import sympy


import numpy as np
import matplotlib.pyplot as plt
from numpy.core.multiarray import ndarray
from numpy import sign
from typing import Optional, Union, Tuple

"""
1	Topic Introduction
--------------------------
Determine the roots of f(x) = -12-21x-18x&2-2.75x^3 through various root finding techniques.

2	Topic Theory/Approach
--------------------------
Utilize incremental function to search the roots as a sense of where the roots are.
Use bisection and false position functions to estimate the roots.

3	Problems
--------------------------
    (a)	Problem Statement

Determine the roots of f(x) = 􀀀12 􀀀 21x 􀀀 18x2 􀀀 2:75x3 between xl = 􀀀10 and xu = 10, and a
stopping criterion of 0.1%. You should use your icremental function rst to nd all roots, and use
those ranges to get better results using the bisection and false position functions.

    (b)	Problem Approach
Utilize bisection and false position techinques with the lower x of -10 and upper x of 10 with a stopping
criterion of .001. The first

    (c)	Problem Results

Incremental:	                -5.25000000000019, -5.24900000000019
Bisection Root Estimate:	    -5.24901, -0.0
False Position Root Estimate:	-5.25927, 0.61139

    (d)	Problem Discussion of Results
The incremental function returns the range in where the roots reside.

The Bisection Root result returns the estimated root of the function
between the range of -10 and 10 and a stopping criteria of 100 iterations.

The False Position result of a trial and error technique to determine the root
of the function and adjusts the values until a criteria is met or if the root is found.

"""

def incremental(f, xa, xb, dx):
    x1 = xa
    f1 = f(xa)

    x2 = xa + dx
    f2 = f(x2)

    while sign(f1) == sign(f2):
        if x1 >= xb:
            return None, None
        x1 = x2
        f1 = f2
        x2 = x1 + dx
        f2 = f(x2)
    else:
        print('Incremental:\t' + str(x1) + ', ' + str(x2))
        return x1, x2

def false_position(f, xa, xb):
    x_lp = xa
    x_up = xb

    for i in range(1, 21, 1):
        x_rp = x_up - ((f(x_up) * (x_lp - x_up)) / (f(x_lp) - f(x_up)))
        if f(x_lp) * f(x_rp) < 0:
            x_up = x_rp
        else:
            x_lp = x_rp
    print('False Position Root Estimate:\t' + str(round(x_rp, 5)) + ', ' + str(round(f(x_rp), 5)))
    plt.plot(x_rp, f(x_rp), 'b*')

def bisection(f, xa, xb):
    x_lower = xa
    x_upper = xb
    x_r = x_lower
    for i in range(1, 100, 1):
        x_r = ((x_upper + x_lower) / 2)
        if (f(x_lower) * f(x_r) < 0):
            x_upper = x_r
        else:
            x_lower = x_r
    print('Bisection Root Estimate:\t' + str(round(x_r, 5)) + ', ' + str(round(f(x_r), 5)))
    plt.plot(x_r, f(x_r), 'g*')

if __name__ == "__main__":
    x: Union[ndarray, Tuple[ndarray, Optional[float]]] = np.linspace(-10, 10)
    y = lambda x: -12 - 21 * x - 18 * (x ** 2) - 2.75 * (x ** 3)

    line, = plt.plot(x, y(x), 'r')
    line.set_antialiased(False)  # turn off antialising

    plt.setp(line, 'linewidth', 1.0)

    roots1, roots2 = incremental(y, -10, 10, .001)
    bisection(y, -10, 10)
    false_position(y, -10, 10)

    plt.legend((line, ), ('f(x) = -12 - 21x - 18x^2 - 2.75x^3',))
    plt.grid(True)
    plt.show()


### Interpolation

import numpy as np
from matplotlib import pylab as plt

# Ex 3.1
def l(q, i, x):
    """returns the ith Lagrangian polynomial associated to the grid q,
    evaluated at x"""
    return np.prod([(x - q[j])/(q[i] - q[j])
                    for j in range(len(q)) if j!=i])

# Ex 3.2
def L(q, g, x):
    """returns the Lagrangian interpolation of g associated to the grid q,
    evaluated at x"""
    return sum([g(q[i])*l(q,i,x) for i in range(len(q))])

# Ex 3.3 - 3.4
for N in range(1, 14, 4):
    grid = np.linspace(0, 1, N)
    plt.plot([L(list(grid), lambda t : 1/(1 + (10*t - 5)**2), x)
              for x in np.linspace(0, 1, 1000)])
    plt.plot([1/(1 + (10*x - 5)**2) for x in np.linspace(0, 1, 1000)])
    plt.title("Degree "+ str(N) +" lagrangian approximation vs Runge function")
    plt.show()

#Ex 3.5
import numpy.polynomial as P

def lP(q, i):
    """returns the ith Lagrangian polynomial associated to the grid q,
    implemented with numpy.polynomial"""
    lPoly = P.Polynomial([1])
    for j in range(len(q)):
        if j!=i:
            den = (q[i]-q[j]) # the denominator of lagrangian polynomials
            lPoly *= P.Polynomial([1/den, -q[i]/den])
    return lPoly

def LP(q, g):
    """returns the Lagrangian interpolation of g associated to the grid q,
    implemented with numpy.polynomial"""
    LPoly = P.Polynomial([0])
    for j in range(len(q)):
        LPoly += g(q[j]) * lP(q,j)
    return LPoly

evalPoints = [h * 2**(-6) for h in range(2**6)]
interPoints = [h * 2**(-2) for h in range(2**2)]
DlP = lP(interPoints, 1).deriv(2)
# The 2nd lagrange polynomial with 4 interpolation points in [0,1] is
# 32 - 24x + 6x^2 - .5x^3; so its 2nd derivative should be 12 - 3x
plt.plot(evalPoints, [DlP(x) for x in evalPoints])
plt.title("2nd derivative of 32-24x+6x^2-.5x^3; indeed it's 12-3x")
plt.show()

# Ex 3.6
def Lambda(q, x):
    return sum([abs(l(q, i, x)) for i in range(len(q))])

for N in range(1, 14, 4):
    plt.plot([Lambda(np.linspace(0, 1, N), x)
              for x in np.linspace(0, 1, 1000)])
    plt.title("Degree " + str(N) + " Lambda function")
    plt.show()
# The oscillations on the edges get bigger as N increases.
# We can damp the oscillations adding more point on the edges;
# a quantitative estimate on where and how many points to add
# to obtain the "best" dampening is given by Chebyshev nodes.

# Ex 3.7
from scipy.optimize import minimize_scalar

def LambdaMaxList(q):
    """returns an approximation of the max of the Lambda function
    and the point in which it is attained, using minize_scalar from scipy
    in each interval of the grid q"""
    gridMax = [0, 0]

    for j in range(len(q)-1):
        # We will use minimize_scalar looping on all intervals [q[j], q[j+1]]
        start = q[j]
        end = q[j+1]
        midpoint = (q[j]+q[j+1])/2

        localMinInfo = minimize_scalar(
            lambda x : -Lambda(q,x),
            bracket = (start, midpoint, end))

        localMaxValue = abs(localMinInfo.get("fun"))
        if localMaxValue > gridMax[0]:
            gridMax = [localMaxValue, localMinInfo.get("x")]
            # At each step we check, and eventually update, where Lambda
            # is maximum and the point in which the max is attained

    return gridMax

# Ex 3.8
def greedyStepMinimizeLambda(M, q):
    """given a starting grid q of [0,1], returns the M grid points
    obtained by adding points to q, at each addition requiring that
    a new point is placed where Lambda is maximum"""
    for j in range(M):
        q.append(LambdaMaxList(q)[1])
    return q

startGrid = [0, 0.5, 1]   # The starting grid of points
NPoints = 15              # Number of points which will be added to the grid
finalGrid = greedyStepMinimizeLambda(NPoints, startGrid)
plt.plot(finalGrid, [0 for i in range(len(finalGrid))] ,'ro')
plt.title("Greedy step minimization for "
          + str(len(finalGrid)) + " nodes")
plt.show()
# As the graph shows, indeed the nodes tend to concetrate on the edges


import numpy as np
import matplotlib.pyplot as plt

n = 20
mean = 30
variance = 20
a = 1
b = 1
c = 3
d = 50

def gibbs_sampler(size):
    mu_set = np.ndarray(size)
    tau_set = np.ndarray(size)
    mu, tau = 3, 1  # initial values of mu and tau
    para_a = a + n / 2
    for i in range(size):
        para_b = b + ((n - 1) * variance + n * ((mean - mu) ** 2)) / 2
        tau = np.random.gamma(para_a, para_b)
        para_c = (c * d + n * tau * mean) / (n * tau + d)
        para_d = 1 / (n * tau + d)
        mu = np.random.normal(para_c, np.sqrt(para_d))

        mu_set[i] = mu
        tau_set[i] = tau

    return mu_set, tau_set

samples = gibbs_sampler(10000)

def plot_joint(samples):
    index = [i for i in range(len(samples[0])) if 29.94 < samples[0][i] < 30.01 and samples[1][i] < 5000]
    x = [samples[0][i] for i in index]
    y = [samples[1][i] for i in index]
    plt.scatter(x, y, alpha=0.02, color='black', linewidths=0.5)
    plt.title('Joint distribution for mu and tau')
    plt.xlabel('mu')
    plt.ylabel('tau')
    plt.show()

def plot_marginal_mu(samples):
    index = [i for i in range(len(samples[0])) if 29.94 < samples[0][i] < 30.01 and samples[1][i] < 5000]
    x = [samples[0][i] for i in index]
    plt.hist(x, bins=60, rwidth=0.8)
    plt.title('Marginal distribution for mu')
    plt.xlabel('mu')
    plt.show()

def plot_marginal_tau(samples):
    index = [i for i in range(len(samples[0])) if 29.94 < samples[0][i] < 30.01 and samples[1][i] < 5000]
    x = [samples[1][i] for i in index]
    plt.hist(x, bins=60, rwidth=0.9)
    plt.title('Marginal distribution for tau')
    plt.xlabel('tau')
    plt.show()

def plot_marginal_variance(samples):
    index = [i for i in range(len(samples[0])) if 29.94 < samples[0][i] < 30.01 and samples[1][i] < 5000]
    x = [1/samples[0][i] for i in index]
    plt.hist(x, bins=60, rwidth=0.9)
    plt.title('Marginal distribution for variance')
    plt.xlabel('variance')
    plt.show()

# plot_joint(samples)
plot_marginal_mu(samples)
# plot_marginal_tau(samples)
# plot_marginal_variance(samples)


""" NumPy is a Linear Algebra Library for Python, the reason it is so important for Data
    Science with Python is that almost all of the libraries in the PyData Ecosystem rely on
    NumPy as one of their main building blocks.

    Numpy is also incredibly fast, as it has bindings to C libraries. For more info on why
    you would want to use Arrays instead of lists

    Numpy has many built-in functions and capabilities.
    Exemplos de utilização do Numpy: vectors, arrays, matrices, and number generation.
    Arrays: vetores (1-D) e matrizes(2-D).

    conda install numpy    or      pip install numpy

    User Guide:     https://docs.scipy.org/doc/numpy-1.15.0/numpy-user-1.15.0.pdf """

import numpy as np
# ________________________________________________ Criar Arrays
# Array a partir de uma lista
lista = [1,2,3,4]
print(lista)
np.array(lista)
ar_lista = np.array(lista)
print(ar_lista)
array = np.array([5,3,9])
print(array)

matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matriz)
np.array(matriz)

# Built-in Methods: built-in to generate Arrays
np.arange(10)               # Return evenly spaced values within a given interval."
np.arange(1,10,0.5)         # start(inclusive), stop(exclusive), step

# Random: numpy tem varias formas de criar random number arrays
np.random.rand(2)          # Create an array of the given shape and populate it with random samples from a uniform distribution over [0, 1)
np.random.rand(3,5)

np.random.randn(2)          # Return a sample (or samples) from the standard normal distribution
np.random.randn(6,6)

np.random.randint(10)
np.random.randint(1,100)    # Return random integers from low (inclusive) to high(exclusive)
np.random.randint(1,100,10)

## ________________________________________________ Array Attributes and Methods
np.random.seed(303)         # todos usarem os mesmos numeros aleatorios
arr = np.arange(10)
a1 = np.random.randint(0,50,10)

print(a1)
a1.max()
a1.min()
a1.argmin()
a1.argmax()

# Shape:  is an attribute that arrays have (not a method):"
a1.shape
a1.reshape(2,5)
a8 = np.array([[5,7],[7,9],[9,11],[11,13],[13,15]])
a1.reshape(a8.shape)

# ________________________________________________ Array indexing / SLICE
d = np.arange(0,11)
c = np.arange(10,21)
print( 'c: ', c,'\nd: ', d)
d[2]
d[-1]
#  !!!!! começa sempre com indice 0
d[3:6]  # [ inicio(inclusive):fim(exclusive)]
c[8]
c[2:5]
c[0:7]
c[:7]
d[5:]
d[::-1]

a3 = np.arange(0,11)
a3 > 5   #boolean
bol_a3 = a3>5
a3[bol_a3] # retorna somente os valores com 'True'
a3[a3>5]
a4 = a3[a3>5]

#Funções para Array

np.sqrt(array)
np.exp(2)
np.exp(array)
np.max(array)
array.max()

np.pi
np.sin(10)              # radianos
np.sin(90*np.pi/180)    # 90 graus
np.sin(array)
graus = np.arange(0,361,90)
np.sin(graus)
np.sin(graus*np.pi/180)
np.rint(np.sin(graus*np.pi/180))

# https://docs.scipy.org/doc/numpy/reference/ufuncs.html


import numpy as np
import matplotlib.pyplot as plt
import networkx as nx

def randomGraph(n, p):
    graph = nx.erdos_renyi_graph(n, p)
    while not nx.is_connected(graph):
        graph = nx.erdos_renyi_graph(n, p)
    return graph

def getTime(graph):
    n = graph.number_of_nodes()
    paths = dict(nx.all_pairs_shortest_path_length(graph))
    return sum([paths[i][i + 1] for i in range(n - 1)])

def relabelGraph(graph, relabel):
    nodes = list(graph.nodes)
    relabelMap = dict(zip(nodes, relabel))
    return nx.relabel_nodes(graph, relabelMap)

def shuffleGraph(graph):
    from random import shuffle
    shuffled = list(graph.nodes)
    shuffle(shuffled)
    gshuffled = relabelGraph(graph, shuffled)
    return gshuffled

def randomOrder(graph):
    gshuffled = shuffleGraph(graph)
    return getTime(gshuffled)

def DFSOrder(graph):
    def init(v, w):
        graph.nodes[v]['done'] = True
        graph.nodes[v]['parent'] = w
        message(v)

    def message(v):
        nonlocal t
        order = len([w for w in graph.nodes if graph.nodes[w]['done']])
        nodes = [w for w in graph[v] if not graph.nodes[w]['done']]
        par = graph.nodes[v]['parent']
        if nodes:
            t += 1
            w = nodes[0]
            init(w, v)
        elif order != graph.number_of_nodes():
            t += 1
            message(par)

    for i in range(graph.number_of_nodes()):
        graph.nodes[i]['done'] = False
    t = 0
    init(0, -1)
    return t

def bestOrder(graph):
    from itertools import permutations
    n = graph.number_of_nodes()
    perm = list(permutations(range(n)))
    T = 2*n
    for p in perm:
        graphRe = relabelGraph(graph, p)
        Tp = getTime(graphRe)
        if Tp < T:
            T = Tp
    return T

def main():
    n = 40
    p = .05
    N = 300
    graphs = [randomGraph(n, p) for _ in range(N)]
    randomRes = np.array([randomOrder(gr) for gr in graphs])
    dfsRes = np.array([DFSOrder(gr) for gr in graphs])
    # bestRes = np.array([bestOrder(gr) for gr in graphs])
    print(randomRes)
    print(dfsRes)
    # print(bestRes)
    kwargs = dict(histtype='stepfilled', alpha=0.6, density=True, ec="k")
    plt.hist((randomRes, dfsRes), bins=50)
    # plt.hist(dfsRes, **kwargs)
    # plt.hist(bestRes, **kwargs)
    plt.show()

if __name__ == '__main__':
    # graph = nx.Graph()
    # graph.add_nodes_from([0, 1, 2, 3, 4, 5])
    # graph.add_edges_from([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)])
    # print(bestOrder(graph))
    main()


# https://github.com/sympy/sympy/wiki/Generating-tables-of-derivatives-and-integrals

from sympy import *
t, dt, n, w = symbols('t dt n w', real=True)

# Finite difference operators

def D_t_forward(u):
    return (u(t + dt) - u(t))/dt

def D_t_backward(u):
    return (u(t) - u(t-dt))/dt

def D_t_centered(u):
    return (u(t + dt/2) - u(t-dt/2))/dt

def D_2t_centered(u):
    return (u(t + dt) - u(t-dt))/(2*dt)

def D_t_D_t(u):
    return (u(t + dt) - 2*u(t) + u(t-dt))/(dt**2)

op_list = [D_t_forward, D_t_backward,
           D_t_centered, D_2t_centered, D_t_D_t]

def ft1(t):
    return t

def ft2(t):
    return t**2

def ft3(t):
    return t**3

def f_expiwt(t):
    return exp(I*w*t)

def f_expwt(t):
    return exp(w*t)

func_list = [ft1, ft2, ft3, f_expiwt, f_expwt]
import inspect

for func in func_list:
    print(('\n--- Function:', inspect.getsource(func), '---'))
    for op in op_list:
        print(('\nOperator:', op.__name__))
        f = func
        e = op(f)
        e = simplify(expand(e))
        print(('simplify(expand(operator(function)):', e))
        if func in [f_expiwt, f_expwt]:
            e = e/f(t)
        e = e.subs(t, n*dt)
        print(('t -> n*dt:', expand(e)))
        print(('factor(simplify(expand(e))):', factor(simplify(expand(e)))))


#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# =========================================================================== #
# Project : ML Studio                                                         #
# Version : 0.1.0                                                             #
# File    : activation.py                                                     #
# Python  : 3.8.2                                                             #
# --------------------------------------------------------------------------  #
# Author  : John James                                                        #
# Company : DecisionScients                                                   #
# Email   : jjames@decisionscients.com                                        #
# URL     : https://github.com/decisionscients/MLStudio                       #
# --------------------------------------------------------------------------  #
# Created       : Saturday, May 16th 2020, 11:39:45 pm                        #
# Last Modified : Saturday, May 16th 2020, 11:39:45 pm                        #
# Modified By   : John James (jjames@decisionscients.com)                     #
# --------------------------------------------------------------------------  #
# License : BSD                                                               #
# Copyright (c) 2020 DecisionScients                                          #
# =========================================================================== #
"""Activation functions."""
from abc import ABC, abstractmethod

import numpy as np
from sklearn.base import BaseEstimator

from mlstudio.utils.validation import validate_range
# --------------------------------------------------------------------------  #
# Functions adapted from the following sources.
# Reference https://en.wikipedia.org/wiki/Activation_function
# Reference https://www.python-course.eu/softmax.php
# --------------------------------------------------------------------------  #
class Activation(ABC, BaseEstimator):
    """Base class for activation classes."""
    @abstractmethod
    def __call__(self, x):
        pass

    @abstractmethod
    def gradient(self, x):
        pass

class Sigmoid(Activation):
    """Sigmoid activation function."""

    def __call__(self, x):
        if np.all(x >= 0):
            s = 1 / (1 + np.exp(-x))
        else:
            s =  np.exp(x) / (1 + np.exp(x))
        return s

    def gradient(self, x):
        return self.__call__(x) * (1 - self.__call__(x))

class Softmax(Activation):
    def __call__(self, x):
        # Subtract the max to avoid underflow or overflow errors
        e_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
        s = e_x / np.sum(e_x, axis=-1, keepdims=True)
        return s

    def gradient(self, x):
        p = self.__call__(x)
        return p * (1 - p)

class TanH(Activation):
    def __call__(self, x):
        return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-1))

    def gradient(self, x):
        return 1 - np.power(self.__call__(x), 2)

class ReLU(Activation):
    def __call__(self, x):
        return np.where(x > 0, x, 0)

    def gradient(self, x):
        return np.where(x > 0, 1, 0)

class LeakyReLU(Activation):
    def __init__(self, alpha=0.01):
        self.alpha = alpha

    def __call__(self, x):
        return np.where(x >= 0, x, self.alpha * x)

    def gradient(self, x):
        return np.where(x >= 0, 1, self.alpha)


import re
import numpy as np

def split_equation(equation):
    num_set = ['0','1','2','3','4','5','6','7','8','9','%', '.', 'P', 'I']
    start = ''
    equ_list = []
    for char in equation:
        if char not in num_set:
            if start != '':
                equ_list.append(start)
            equ_list.append(char)
            start = ''
        else:
            start += char
    if start != '':
        equ_list.append(start)
    return equ_list

def postfix_equation(equ_list):
    stack = []
    post_equ = []
    op_list = ['+', '-', '*', '/', '^']
    priori = {'^':3, '*':2, '/':2, '+':1, '-':1}
    for elem in equ_list:
        if elem == '(':
            stack.append('(')
        elif elem == ')':
            while 1:
                op = stack.pop()
                if op == '(':
                    break
                else:
                    post_equ.append(op)
        elif elem in op_list:
            while 1:
                if stack == []:
                    break
                elif stack[-1] == '(':
                    break
                elif priori[elem] > priori[stack[-1]]:
                    break
                else:
                    op = stack.pop()
                    post_equ.append(op)
            stack.append(elem)
        else:
            #if elem == 'PI':
            #    post_equ.append('3.14')
            #else:
            #    post_equ.append(elem)
            post_equ.append(elem)
    while stack != []:
        post_equ.append(stack.pop())
    return post_equ

def post_solver(post_equ):
    stack = []
    op_list = ['+', '-', '/', '*', '^']
    for elem in post_equ:
        if elem not in op_list:
            op_v = elem
            if '%' in op_v:
                op_v = float(op_v[:-1])/100.0
            stack.append(str(op_v))
        elif elem in op_list:
            op_v_1 = stack.pop()
            op_v_1 = float(op_v_1)
            op_v_2 = stack.pop()
            op_v_2 = float(op_v_2)
            if elem == '+':
                stack.append(str(op_v_2+op_v_1))
            elif elem == '-':
                stack.append(str(op_v_2-op_v_1))
            elif elem == '*':
                stack.append(str(op_v_2*op_v_1))
            elif elem == '/':
                stack.append(str(op_v_2/op_v_1))
            else:
                stack.append(str(op_v_2**op_v_1))
    return stack.pop()

def solve_equation(equ_list):
    post_equ = postfix_equation(equ_list)
    ans = post_solver(post_equ)
    return ans

def equ_api_1(equ, ans):
    '''
    template: list, x = xxxxx
    return boolean variable
    '''
    equ = ''.join(equ)
    equ = equ[2:]
    equ = split_equation(equ)
    p_ans = solve_equation(equ)
    #print p_ans
    if abs(float(p_ans) - float(ans))<1e-5:
        return True
    return False


# the tools I used work properly --> reliability
# the result I got by using analysis tools is what I want --> validity
from coefficient import coefficient
import numpy as np
import pandas as pd
# retest reliability
def retest_reliablity():
    """check whether a test is reliable in different period"""
    X1 = np.array([54, 67, 67, 83, 87, 89, 84, 90, 98, 65])
    X2 = np.array([56, 77, 87, 89, 89, 90, 87, 92, 99, 76])
    r = coefficient(X1, X2)
    return r

def parallel_forms_reliability():
    """check whether the same equality and similarity of the test
    on different parallel forms.
    """
    X1 = np.array([4, 5, 3, 6, 7, 5, 6, 4, 3, 3])
    X2 = np.array([5, 6, 5, 6, 7, 6, 7, 8, 7, 7])
    r = coefficient(X1, X2)
    return r

def internal_consisitency_reliability():
    """ using coefficient of Cronbach's
                     k       s_y^2 - sum(s_i^2)
        epsilon = ------- * --------------------
                   k - 1           s_y^2

    """
    data = {
        'proj1': [3, 4, 3, 3, 3, 4, 2, 3, 3, 3],
        'proj2': [5, 4, 4, 3, 4, 5, 5, 4, 5, 3],
        'proj3': [1, 3, 4, 5, 5, 5, 5, 4, 4, 2],
        'proj4': [4, 5, 4, 2, 4, 3, 3, 2, 4, 3],
        'proj5': [1, 3, 4, 1, 3, 2, 4, 4, 3, 2],
    }
    df = pd.DataFrame(data=data, index=range(1, 11))
    k = df.shape[1]
    s_y = df.sum(axis=1).var()
    sum_of_s_i = df.var(axis=0).sum()
    epsilon = (k / (k - 1)) * ((s_y - sum_of_s_i) / s_y)
    return epsilon

def interrater_reliability():
    """determine whether two interrater are consistent with
    observation
    """
    X1 = np.array([1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1])
    X2 = np.array([1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1])
    r = (X1 == X2).sum() / X1.shape[0]
    return r

# content validity: whether the content and reflect the target?
# criterion validity: whether this index is relative to others?
# construct validity:


import math,time
import numpy as np

def great_circle(lon1,lat1,lon2,lat2):
    radius = 3956 #miles
    x = math.pi/180.0

    a = (90.0-lat1)*(x)
    b = (90.0-lat2)*(x)
    theta = (lon2-lon1)*(x)
    c = math.acos((math.cos(a)*math.cos(b)) +
                  (math.sin(a)*math.sin(b)*math.cos(theta)))
    return radius*c

def great_circle_numpy(mat):
    """numpys great circle"""
    radius = 3956
    x = np.pi/180.0
    lon1 = mat[:,0]
    lat1 = mat[:,1]
    lon2 = mat[:,2]
    lat2 = mat[:,3]

    a = (90.0-lat1)*(x)
    b = (90.0-lat2)*(x)
    theta = (lon2-lon1)*(x)
    c = np.arccos((np.cos(a)*np.cos(b)) +
                  (np.sin(a)*np.sin(b)*np.cos(theta)))
    return radius*c

def main():
    timeStart = time.time()

    ## create the matrix
    lon1,lat1,lon2,lat2 = 42,0.5,-13,-32
    n = int(1e06)
    mat = np.zeros((n,4),)
    mat = mat + [lon1,lat1,lon2,lat2]

    for i in range(mat.shape[0]):
        x = great_circle(*mat[i,:])

    runTime1 = time.time() - timeStart
    print('The run time by for loop = ', time.strftime('%H:%M:%S', time.gmtime(runTime1)))

    timeStart = time.time()
    c = great_circle_numpy(mat)
    runTime2 = time.time() - timeStart
    print('The run time by using numpy = ',time.strftime('%H:%M:%S', time.gmtime(runTime2)))
    return runTime1, runTime2

if __name__=='__main__':
    main()


from scipy import linalg as lg
import numpy as np

arr = np.array([
    [1, 2],
    [3, 4]
])

# Matrix arr's determinant
print('Det: ', lg.det(arr))
# Matrix arr's inverse
print('Inv: ', lg.inv(arr))

# Solve linear equations
"""
|1,2||x1|_|6 |
|3,4||x2|-|14|

the solution is x1 = 2, x2 = 2
"""
b = np.array([6, 14])
print('Sol: ', lg.solve(arr, b))

# Matrix arr's eigenvalue ['aɪdʒənˌvælju:]
print('Eig: ', lg.eig(arr))

# Matrix LU factorisation
print('LU decomposition')
P, L, U = lg.lu(arr)
print("L:  \n", L)
print("U:  \n", U)


import numpy as np
import matplotlib.pyplot as plt
from pprint import pprint

# declare vars
h = 50  # height
N = 10000
beta = np.pi
theta1 = np.linspace(0, np.pi, N)
theta2 = np.linspace(np.pi, 2 * np.pi, N)

def func1(angle):
    fixed_var = (h / (4 + np.pi))
    if 0 <= angle <= (1 / 8) * beta:
        return fixed_var * \
               (np.pi * angle / beta - 0.25 * np.sin(4 * np.pi * angle / beta))
    elif (1 / 8) * beta < angle <= (7 / 8) * beta:
        return fixed_var * \
               (2 + np.pi * angle / beta - 9 * np.sin(4 * np.pi * angle / (3 * beta) + np.pi / 3) / 4)
    elif (7 / 8) * beta < angle <= beta:
        return fixed_var * \
               (4 + np.pi * angle / beta - np.sin(4 * np.pi * angle / beta) / 4)

def func2(angle):
    fixed_var = (h / (4 + np.pi))
    if 0 <= angle <= (1 / 8) * beta:
        return h - fixed_var * \
               (np.pi * angle / beta - 0.25 * np.sin(4 * np.pi * angle / beta))
    elif (1 / 8) * beta < angle <= (7 / 8) * beta:
        return h - fixed_var * \
               (2 + np.pi * angle / beta - 9 * np.sin(4 * np.pi * angle / (3 * beta) + np.pi / 3) / 4)
    elif (7 / 8) * beta < angle <= beta:
        return h - fixed_var * \
               (4 + np.pi * angle / beta - np.sin(4 * np.pi * angle / beta) / 4)

if __name__ == '__main__':
    y1 = list()
    y2 = list()
    x1 = list()
    x2 = list()
    y_all_1th_diff = list()
    y_all_2th_diff = list()

    for angle in theta1:
        y1.append(func1(angle))
        # y2.append(func2(angle))

    for i in range(len(y1)):
        if i > 0:
            y_all_1th_diff.append((y1[i] - y1[i - 1]) / (theta1[i] - theta1[i - 1]))
            x1.append(theta1[i])
        else:
            y_all_1th_diff.append(0)
            x1.append(0)

    for i in range(len(y_all_1th_diff)):
        if i > 0:
            y_all_2th_diff.append((y_all_1th_diff[i] - y_all_1th_diff[i - 1]) / (x1[i] - x1[i - 1]))
            x2.append(x1[i])
        else:
            y_all_2th_diff.append(0)
            x2.append(0)

    print(len(y_all_2th_diff))

    plt.plot(x2, y_all_2th_diff)
    # plt.title(r'height = 50mm, $\beta$ = 180$^\circ$')
    # plt.xlabel(r'$\theta$')
    plt.grid()
    # plt.ylabel('mm')
    plt.show()


import numpy as np

#1. numpy calculation
a=np.arange(1,10).reshape(3,3)
b=np.arange(10,19).reshape(3,3)
print(a)
print(b)
#所有元素都加1
print(a+1)

#所有元素都平方
print(a**2)

#判斷式輸出boolen
print(a<5)

#相對應的元素相加
print(a*b)

#矩陣內積
print(np.dot(a,b))

#2. function i numpy and statistic
c=np.arange(1,10).reshape(3,3)
print(c)
#[[1 2 3]
# [4 5 6]
# [7 8 9]]

#最小值與最大值
print(np.min(c),np.max(c))                  #1 9

#總和 乘積 平均
print(np.sum(c),np.product(c),np.mean(c))   #45 362880 5.0

#標準差
print(np.std(c))                            #2.581988897471611

#變異數
print(np.var(c))                            #6.666666666666667

#中位數
print(np.median(c))                         #5.0

#max-min
print(np.ptp(c))                            #8

#3. sort in numpy
d=np.random.choice(50,size=10,replace=False)
#before sorting
print(d)
#after sorting
print(np.sort(d))
#the index after sorting
print(np.argsort(d))
#use the index to get the value
for i in np.argsort(d):
    print(d[i],end=" ")
print("\n")

#N dimension array sorting
e=np.random.randint(0,10,(3,5))
print(e)
#對每一直行sort
print(np.sort(e,axis=0))
#對每一橫列sort
print(np.sort(e,axis=1))


import math
import numpy as np
"""
From tensorflow playground code
"""

def gen2Spiral(sample=500,noise=0,addSin=False):
    n = sample//2
    Xdata = []
    Ydata = []

    def genSpiral(deltaT,label):
        for i in range(n):
            r = float(i)/n*5.0
            t = 1.75*float(i)/n*2.0*math.pi + deltaT
            x = r*math.sin(t) #+ np.random.uniform(-1,1)*noise
            y = r * math.cos(t)  # + np.random.uniform(-1,1)*noise

            if addSin:
                Xdata.append([x, y,math.sin(x),math.sin(y)])
            else:
                Xdata.append([x, y])

            Ydata.append(label)

    genSpiral(0,1.0)
    genSpiral(math.pi,0.0)

    Xdata = np.array(Xdata)
    Ydata = np.array(Ydata)
    return Xdata,Ydata

# X,y=gen2Spiral(addSin=True)

# from matplotlib import pyplot as plt
# X,y=gen2Spiral()
# print(X)
# plt.scatter(X[:, 0], X[:, 1], marker='o', c=y,
#             s=25, edgecolor='k')
# plt.show()


import numpy as np

#******************
#Useful TOOLS
#******************

def spherical2cartesian(r = None, theta = None, phi = None):
    """
    Converts spherical coordinates to rectangular cartesian coordinates.

    Parameters
    ----------
    r : scalar or array_like, shape (n,)
       Radial distance from the origin of coordinates to the point in question.

    theta : scalar or array_like, shape (n,)
       Polar angle referred to the positive `z` - axis.

    phi : scalar or array_like, shape (n,)
       Azimuthal angle referred to the positive `x` - axis.

    Returns
    -------
    out : `numpy.ndarray`, shape (3,n)
       Array-like object with the resulting x,y,z transformation.

    See Also
    --------
    cartesian2spherical

    Notes
    -----
    Transformation equations:

    .. math:: x &= r\\sin(\\theta)\\cos(\\phi) \n
              y &= r\\sin(\\theta)\\sin(\\phi) \n
              z &= r\\cos(\\theta)

    """

    r = np.asarray(r)
    theta = np.asarray(theta)
    phi = np.asarray(phi)

    return np.array([
            r * np.sin(theta) * np.cos(phi),
            r * np.sin(theta) * np.sin(phi),
            r * np.cos(theta)
            ])

def cartesian2spherical(x=None, y=None, z=None):
    """
    Under development
    """
    return 0