# 160401092 MEHMET TAHIR TAS  - SAYISAL YONTEMLER FINAL ODEVI #

import numpy as np

# from numpy import trapz
# import scipy
# from scipy.integrate import quad

def fitPolynomeThroughOrigin(x, y, nthDegree):
    # Kaynak: https://stackoverflow.com/questions/17930473/how-to-make-my-pylab-poly1dfit-pass-through-zero
    # Bu fonksiyon uretilecek polinomun sabitinin 0 olmasini garantiliyor.
    # Ornek sifirlanmis 2. derecen polinom: y = x^2 + x + 0
    x = np.asarray(x);
    a = x[:, np.newaxis] ** np.arange(1, nthDegree + 1)
    coefficients = np.linalg.lstsq(a, y)[0]

    return np.polynomial.Polynomial(np.concatenate(([0], coefficients)))

def convertDataToPolynomial(data, polynomialDegree):
    y = data
    x = range(
        len(y))  # Her veri noktasi bir gune ait diye dusunursek, x ekseni kacinci gun oldugunu gosteriyor 0,1,2,3,..vs

    # Normalde direk polyfit() fonksiyonunu kullanirdik ama sonuclar (0,0) noktasindan gecmiyor o durumda
    # return np.poly1d(np.polyfit(x, y, polynomialDegree))

    # Dolayisiyla, (0,0) noktasindan gecmesini zorlayarak polinomu uretelim
    return fitPolynomeThroughOrigin(x, y, polynomialDegree)

def polynomialToString(polynomialDegree, coefficients):
    # Katsayilarin okunakli olmasi icin yuvarlayalim
    coefficients = np.round(coefficients, 5)

    res = str(polynomialDegree) + '. derece: y ='
    first_pow = len(coefficients) - 1
    i = 0
    for coef in reversed(coefficients):  # katsayilar sondan basa oldugu icin ters cevirmemiz lazim array'i
        power = first_pow - i

        if coef:
            if i == 0:
                sign = ' '
            elif coef < 0:
                sign, coef = (' - ' if res else '- '), -coef
            elif coef > 0:
                sign = (' + ' if res else '')

            str_coef = '' if coef == 1 and power != 0 else str(coef)

            if power == 0:
                str_power = ''
            elif power == 1:
                str_power = 'x'
            else:
                str_power = 'x' + '^' + str(power)

            res += sign + str_coef + str_power
            i += 1
    return res

# Hata payi hesaplama
# Kaynak: https://www.geeksforgeeks.org/python-mean-squared-error/
def calculateMeanSquaredError(y_true, y_pred):
    mse = np.square(np.subtract(y_true, y_pred)).mean()
    return mse

# integral icin kaynak: https://www.geeksforgeeks.org/trapezoidal-rule-for-approximate-value-of-definite-integral/
# En az hata ureten polinom (en iyi polinom) kullanilarak yapilan integral hesabi
def integrationWithPolynomial(a, b, n, p1):
    # Grid spacing
    h = (b - a) / n

    # Computing sum of first and last terms
    # in above formula
    s = (p1(a) + p1(b))

    # Adding middle terms in above formula
    i = 1
    while i < n:
        s += 2 * p1(a + i * h)
        i += 1

    # h/2 indicates (b-a)/2n.
    # Multiplying h/2 with s.
    return (h / 2) * s

# integral icin kaynak: https://www.geeksforgeeks.org/trapezoidal-rule-for-approximate-value-of-definite-integral/
# veriler.txt dosyasindaki veriler kullanilarak integral hesabi
def integrationWithDataArr(a, b, n, dataArr):
    # Grid spacing
    h = (b - a) / n

    # Computing sum of first and last terms
    # in above formula
    s = (dataArr[a] + dataArr[b - 1])

    # Adding middle terms in above formula
    i = 1
    while i < n:
        index = int(a + i * h)
        s += 2 * dataArr[index]
        i += 1

    # h/2 indicates (b-a)/2n.
    # Multiplying h/2 with s.
    return (h / 2) * s

########################################################################################################################
# verileri dosyadan okuma
infile = open('veriler.txt', 'r')
data_arr = []
for line in infile:
    data_arr.append(int(line))
infile.close()

mse_arr = []
polynomial_arr = []

# sonuc.txt dosyasi olusturulur
outfile = open('sonuc.txt', 'w')
outfile.write("POLINOMLAR: \n")

# 1'den 6'ya kadar her dereceden hesaplama yapalim
for polynomialDegree in range(1, 7):
    # Verileri kullanarak tahmini polinom'u uretelim
    fx = convertDataToPolynomial(data_arr, polynomialDegree)
    # 'x' verilerini polinom fonksiyonumuzdan gecirip y sonuclarini alalim
    x = range(len(data_arr))
    ornekSonuc = fx(x)  # Her bir x degeri icin polinomumuzun urettigi y'leri array olarak dondurur
    # Hata payi hesaplama
    hataPayi = calculateMeanSquaredError(data_arr, ornekSonuc)
    # Her bir dereceden polinom icin hesaplanmis hata paylari sonradan dosyaya yazdirilmak uzere mse_arr'de tutulur
    mse_arr.append(hataPayi)
    polynomial_arr.append(fx)
    # Dosyaya yazmak icin polinom etiketi uretelim
    label = polynomialToString(polynomialDegree, fx.coef);
    # Polinomlari dosyaya yazalim
    outfile.write(f"{label}\n")

# Her bir polinom icin hata paylarini dosyaya yazdirma
outfile.write("HATA PAYLARI: \n")
for i in range(0, 6):
    outfile.write(f"{i + 1}. dereceden polinomun hata payi: {mse_arr[i]} \n")

########################################################################################################################
# EN  UYGUN POLINOMUN BULUNMASI & DOSYAYA YAZDIRILMASI#
minimum_error = min(mse_arr)
best_index = mse_arr.index(minimum_error)
minimum_error_index = best_index + 1
best_label = polynomialToString(minimum_error_index, polynomial_arr[best_index].coef)

outfile.write(
    f"\nEn uygun polinom en kucuk hata payi ({minimum_error}) olan {minimum_error_index}. dereceden polinomdur. \n")
outfile.write(f"{best_label}")

########################################################################################################################
# INTEGRAL HESAPLAMALARI #

# a- b: Integralin sinirlari. Integral bu a ve b degerleri arasinda tanimlidir
a = 2  # 160401092 => son rakam: 2
b = len(data_arr)  # dosyanin satir sayisi
# n: Dikdortgen sayisi. n sayisi ne kadar buyuk ise, dogruluk payimiz o kadar artar.
n = 100000
outfile.write(
    f"\nPolinom kullanilarak hesaplanan integral degeri:  {integrationWithPolynomial(a, b, n, polynomial_arr[best_index])} \n")
outfile.write(
    f"Veriler kullanilarak hesaplanan integral degeri:  {integrationWithDataArr(a, b, n, data_arr)} \n")

# sonuc.txt dosyasini kapatiyoruz
outfile.close()

# print("Value of integral with polynomial is ", "%.4f" % integrationWithPolynomial(a, b, n, polynomial_arr[best_index]))
# print("Value of integral with data is ", "%.4f" % integrationWithDataArr(a, b, n, data_arr))

# HESAPLADIGIMIZ DEGERLERI HAZIR KUTUPHANE FONKSIYONLARININ HESAPLADIGI DEGERLERLE TEST ETMEK ICIN:
# NUMPY & SCIPY
# print(f"Polinomlu - Hazir kutuphane fonk ile hesaplanan deger: { quad(polynomial_arr[best_index], a, b) }")
# print(f"Verili - Hazir kutuphane fonk ile hesaplanan deger: { trapz(data_arr) }")

# sonuc.txt dosyasi olusturulur
out_file = open('160401092_yorum.txt', 'w')
out_file.write(f"POLINOM KULLANARAK YAPILAN INTEGRAL HESABI ILE VERILER.TXT DOSYASINDAKI VERILER \n"
               f"KULLANILARAK YAPILAN INTEGRAL HESABININ FARKLI OLMASININ SEBEPLERI: \n\n"
               f"Integral, x=a ve x=b sınırları arasında f(x) fonksiyonu altında kalan alanin hesaplanması ile elde edilir.\n"
               f"Öncelikle a, b aralığı daha alt aralıklara bölünür bu alt bölmelerin alanı (yaklaşık dikdörtgen şekilli) \n"
               f"hesaplanır ve bunlar toplanmak suretiyle toplam alan yaklaşık olarak hesaplanır. \n\n"
               f"Integrali alınacak fonksiyon polinom formunda olabileceği gibi deneysel verileri olan farklı noktalarda \n"
               f"x ve f(x) degerlerinin olduğu tablo seklinde bir fonksiyon da olabilir. (Biz de sorunun 2. adımında polinom \n"
               f"ile 3. adımında da veriler.txt dosyasındaki verileri kullanarak 2 farklı sekilde hesapladık.) \n\n"
               f"Sayısal integrallemede orjinal f(x) fonksiyonu yerine, interpolasyon fonksiyonu (n. mertebeden bir polinom)\n"
               f"olan g(x) fonksiyonunu yerlestirerek integrali kolayca hesaplayabiliyoruz. Sayısal integrallemenin sonucunun doğruluğu\n"
               f"bu yeni fonksiyonun orijinal fonksiyonu ne kadar iyi temsil ettiğine bağlıdır. Buyuzden hata oranı en düşük,\n"
               f"en uygun polinomu kullanarak integral hesabi yapmak, en doğru sonuca daha da yaklasmamizi sağlayacaktır.\n\n"
               f"*******************************\n"
               f"Polinom ile Veri kullanarak yapılan iki hesaplamanın farklı sonuclar üretmesi doğaldır. Polinom kullanarak \n"
               f"yaptigimiz hesaplamadaki alt bölmelerin alanları gercege daha yakin bir sonuc üretmektedir. (Daha hassas bir \n"
               f"hesaplama yaptigimiz icin daha fazla alanı hesaplamaya dahil edip, daha az veri kaybı yasamış oluyoruz. \n"
               f"Bu sebeple; hata payı, veri kullanarak yaptigimiz integral hesabında  daha fazla oluyor. [a,b] aralığıni \n"
               f"çok sayıda alt aralığa bolerek duyarlıligi artirip iyileştirmeler yapabiliriz.  Ne kadar çok alt bölme alırsak \n"
               f"her bir bölmedeki hata o oranda küçüklecektir. Ancak  alt bölme sayisini artirdigimizda da daha fazla hesaplama \n"
               f"yaptigimize icin programın calışma suresi uzamaktadır.  Bu sebeple alt bölme sayisini belirlerken programimizin \n"
               f"performansını da göz önünde bulundurmamız gerekir.\n"
               f"*******************************\n\n"
               f"Sayısal integrallemeleri yaparken tercih ettigimiz yöntem: Trapez Kuralidir. (Yamuk Yontemi)")
out_file.close()

import numpy,from numpy,import scipy,from scipy


from itertools import product
import numpy as np
import matplotlib.pyplot as plt

F = 0
P = 1

p_k = np.zeros(2)
p_k[P] = 0.8
p_k[F] = 0.2
p_s = np.zeros(2)
p_s[P] = 0.02
p_s[F] = 0.98

pw_w_k = np.zeros((2, 2))
pw_w_k[P, P] = 0.9
pw_w_k[P, F] = 0.1
pw_w_k[F, P] = 0.01
pw_w_k[F, F] = 0.99

pw_u_ks = np.zeros((2, 2, 2))
pw_u_ks[P, P, P] = 0.9
pw_u_ks[P, P, F] = 0.1
pw_u_ks[P, F, P] = 0.2
pw_u_ks[P, F, F] = 0.8
pw_u_ks[F, P, P] = 0.9
pw_u_ks[F, P, F] = 0.1
pw_u_ks[F, F, P] = 0.01
pw_u_ks[F, F, F] = 0.99

pw_b_u = np.zeros((2, 2))
pw_b_u[P, P] = 0.7
pw_b_u[P, F] = 0.3
pw_b_u[F, P] = 0.1
pw_b_u[F, F] = 0.9

# Obliczanie wybranych prawdopodobieństw warunkowch metodą dokładną

# Generowanie tablicy łącznego rozkładu pradopodobieństwa
p = np.zeros((2, 2, 2, 2, 2))
for K in [P, F]:
    for S in [P, F]:
        for W in [P, F]:
            for U in [P, F]:
                for B in [P, F]:
                    p[K, S, W, U, B] = p_k[K]*p_s[S]*pw_w_k[K, W]*pw_u_ks[K, S, U]*pw_b_u[U, B]

# P(B = P|K = P) = P(B = P, K = P)/P(K = P)
# P(B = P, K = P)
pw_b_and_k = sum([p[P, s, w, u, P] for s, w, u in product([P, F], [P, F], [P, F])])
# P(K = P)
pw_k = sum([p[P, s, w, u, b] for s, w, u, b in product([P, F], [P, F], [P, F], [P, F])])
pw_b_k_exact = pw_b_and_k/pw_k
print('P(B = P|K = P) exact: ', pw_b_k_exact)

# P(K = P|B = P) = P(K = P, B = P)/P(B = P)
# P(K = P, B = P)
pw_k_and_b = sum([p[P, s, w, b, P] for s, w, b in product([P, F], [P, F], [P, F])])
# P(B = P)
pw_b = sum([p[k, s, w, u, P] for k, s, w, u in product([P, F], [P, F], [P, F], [P, F])])
pw_k_b_exact = pw_k_and_b/pw_b
print('P(K = P|B = P) exact: ', pw_k_b_exact)

# Obliczanie wybranych prawdopodobieństw warunkowch metodą Monte Carlo.

I = 100 # liczba iteracji
K = 300 # liczba próbek na iterację

avg_pw_b_k = 0
avg_pw_k_b = 0

plt.axis([0, I, 0, 1])
plt.xlabel('Iteracja')
plt.ylabel('Prawdopodobieństwo')
plt.plot([0, I], [pw_b_k_exact, pw_b_k_exact], color='b')
plt.plot([0, I], [pw_k_b_exact, pw_k_b_exact], color='b')

np.random.seed(1)
for i in range(1, I):
    # Wygenerowanie wartości poszczególnych zmiennych losowych dla losowego przebiegu sieci
    k = np.random.random(K) < p_k[P]
    s = np.random.random(K) < p_s[P]
    uPP = np.random.random(K) < pw_u_ks[P, P, P]
    uPF = np.random.random(K) < pw_u_ks[P, F, P]
    uFP = np.random.random(K) < pw_u_ks[F, P, P]
    uFF = np.random.random(K) < pw_u_ks[F, F, P]
    u = np.logical_or.reduce((
        np.logical_and.reduce((k, s, uPP)),
        np.logical_and.reduce((k, np.logical_not(s), uPF)),
        np.logical_and.reduce((np.logical_not(k), s, uFP)),
        np.logical_and.reduce((np.logical_not(k), np.logical_not(s), uFF)),
    ))

    bP = np.random.random(K) < pw_b_u[P, P]
    bF = np.random.random(K) < pw_b_u[F, P]
    b = np.logical_or(
        np.logical_and(u, bP),
        np.logical_and(np.logical_not(u), bF)
    )

    # Oszacowanie pradopodobieństw warunkowych na podstawie tablic:  k, s, u, b

    # P(B = P|K = P) = P(B = P, K = P)/P(K = P)
    pw_b_k__mc = np.sum(np.logical_and(b, k)) / np.sum(k)

    # P(K = P|B = P) = P(K = P, B = P)/P(B = P)
    pw_k_b__mc = np.sum(np.logical_and(k, b))/np.sum(b)

    # Ze wzoru na iteracyjne liczenie średniej
    avg_pw_b_k = avg_pw_b_k + (pw_b_k__mc - avg_pw_b_k) / i
    avg_pw_k_b = avg_pw_k_b + (pw_k_b__mc - avg_pw_k_b) / i

    plt.scatter(i, avg_pw_b_k, marker='.', s=1, color='r')
    plt.scatter(i, avg_pw_k_b, marker='.', s=1, color='r')

    # Poniższą linię można odkomentować w celu śledzenia działania metody krok po kroku
    plt.pause(0.001)

print('P(B = P|K = P) Monte Carlo: ', avg_pw_b_k)
print('P(K = P|B = P) Monte Carlo: ', avg_pw_k_b)


import numpy as np

#     |a|
# U = |b|
#     |c|
#     |d|
#
# U = AU + B

A = np.array([
    [0., 1/4, 1/4, 0],
    [1/4, 0., 0., 1/4],
    [15/52, 0., 0., 17/52],
    [0., 15/52, 17/52, 0.],
])
B = np.array([-1/2, 1., 49/52, 443/26])

# Initial values
U = np.array([0., 0., 0., 0.])
print(f'''\
U = V = [u_11  u_12  u_21  u_22]

U = {U}''')

for i in range(5):
    U = A@U + B
    letter = 'V' if i % 2 == 0 else 'U'
    print(f'{letter} = {U}')

print(f'''
Result after 5 steps:
  u_11 = {U[0]}
  u_12 = {U[1]}
  u_21 = {U[2]}
  u_22 = {U[3]}
''')

for i in range(5, 20):
    U = A@U + B
    letter = 'V' if i % 2 == 0 else 'U'
    print(f'{letter} = {U}')

print(f'''
Result after 20 steps:
  u_11 = {U[0]}
  u_12 = {U[1]}
  u_21 = {U[2]}
  u_22 = {U[3]}
''')


import numpy as np

def show_dot_prod() -> None:
    """
        Demonstrate the dot product of two matricies. One being the
        transposed version of itself.
    """
    matrix = np.array([[4, 2, 3], [2, 0, 1]])
    transposed_matrix = np.transpose(matrix)
    dot_prod = np.dot(matrix, transposed_matrix)
    other_dot_prod = np.dot(transposed_matrix, matrix)
    print(dot_prod)
    print(other_dot_prod)

def element_wise_mult() -> None:
    """
        Demonstrate what element wise multiplcation would look like with
        matricies from numpy
    """
    first_mat = np.array([[1, 2], [3, 4]])
    second_mat = np.array([[5, 6], [7, 8]])

    # Show communitive properties of multiplying two matricies of the same dimension
    # by eachother. (using python and np.multiply())
    print(np.multiply(first_mat, second_mat))
    print(np.multiply(second_mat, first_mat))

    print(first_mat * second_mat)
    print(second_mat * first_mat)

def nn_with_numpy() -> None:
    def sigmoid(x):
        return 1 / (1 + np.exp(-x))

    output_neurons = 1
    hidden_layer_neurons = 3

    features = np.array([[1, 0, 1, 0], [1, 0, 1, 1], [0, 1, 0, 1]])
    desired_output = np.array([[1], [1], [0]])
    hidden_layer_weight = np.random.uniform(
        size=(features.shape[1], hidden_layer_neurons)
    )
    hidden_layer_bias = np.random.uniform(size=(1, hidden_layer_neurons))
    output_layer_weight = np.random.uniform(size=(hidden_layer_neurons, output_neurons))
    output_layer_bias = np.random.uniform(size=(1, output_neurons))

    hidden_layer_input = np.dot(features, hidden_layer_weight) + hidden_layer_bias
    hidden_layer_output = sigmoid(hidden_layer_input)

    output_layer_input = (
        np.dot(hidden_layer_output, output_layer_weight) + output_layer_bias
    )

    output = sigmoid(output_layer_input)
    print(output)

if __name__ == "__main__":
    show_dot_prod()
    element_wise_mult()
    nn_with_numpy()


#functions taken from https://doi.org/10.1007/s00521-017-3088-3
#Applying modified cuckoo search algorithm for solving systems of nonlinear equations
import numpy as np

class Camelback:
    @staticmethod
    def func_simple(x: float, y:float) -> float:
        result = (4 - 2.1*(x**2) + (x**4)/3.0) * (x**2)
        result += x*y
        result += (4*(y**2) - 4) * (y**2)
        return result

    @staticmethod
    def func(x_array: list) -> float:
        return Camelback.func_simple(x_array[0], x_array[1])

    @staticmethod
    def get_limits():
        return [-3, 3], [-2, 2]

    @staticmethod
    def get_theoretical_optimum():
        return -1.03163

    @staticmethod
    def get_type():
        return "min"

class Goldsteinprice:
    @staticmethod
    def func_simple(x: float, y:float) -> float:
        result = 1 + ((x+y+1)**2) * (19-14*x + 3*(x**2)-14*y + 6*x*y + 3*(y**2))
        result = result * ((30+ (2*x - 3*y)**2) * 18-32*x+ 12*(x**2) + 48*y - 36*x*y + 27*(y**2) )
        return result

    @staticmethod
    def func(x_array: list) -> float:
        return Goldsteinprice.func_simple(x_array[0], x_array[1])

    @staticmethod
    def get_limits():
        return [-2, 2]

    @staticmethod
    def get_theoretical_optimum():
        return 3

    @staticmethod
    def get_type():
        return "min"

class Pshubert1:
    @staticmethod
    def func_simple(x: float, y:float) -> float:
        res1 = 0
        for i in range(1, 6):#1, 2, 3, 4, 5
            res1 += float(i)*np.cos( (i+1.0)*x + i )
        res2 = 0
        for i in range(1, 6):#1,2,3,4,5
            res2 += float(i)*np.cos( (i+1.0)*y + i )
        result = (0.5*( (x+1.42513)**2 + (y+0.80032)**2 ) )
        result = (res1*res2) + result
        return result

    @staticmethod
    def func(x_array: list) -> float:
        return Pshubert1.func_simple(x_array[0], x_array[1])

    @staticmethod
    def get_limits():
        return [-10, 10]

    @staticmethod
    def get_theoretical_optimum():
        return -186.73091

    @staticmethod
    def get_type():
        return "min"

class Pshubert2:
    @staticmethod
    def func_simple(x: float, y:float) -> float:
        res1 = 0
        for i in range(1, 6):#1, 2, 3, 4, 5
            res1 += float(i)*np.cos( (i+1.0)*x + i )
        res2 = 0
        for i in range(1, 6):#1,2,3,4,5
            res2 += float(i)*np.cos( (i+1.0)*y + i )
        result = (x+1.42513)**2 + (y+0.80032)**2
        result = (res1*res2) + result
        return result

    @staticmethod
    def func(x_array: list) -> float:
        return Pshubert2.func_simple(x_array[0], x_array[1])

    @staticmethod
    def get_limits():
        return [-10, 10]

    @staticmethod
    def get_theoretical_optimum():
        return -186.73091

    @staticmethod
    def get_type():
        return "min"

class Shubert:
    @staticmethod
    def func_simple(x: float, y:float) -> float:
        res1 = 0
        for i in range(1, 6):#1, 2, 3, 4, 5
            res1 += float(i)*np.cos( (i+1.0)*x + i )
        res2 = 0
        for i in range(1, 6):#1,2,3,4,5
            res2 += float(i)*np.cos( (i+1.0)*y + i )
        return res1*res2

    @staticmethod
    def func(x_array: list) -> float:
        return Shubert.func_simple(x_array[0], x_array[1])

    @staticmethod
    def get_limits():
        return [-10, 10]

    @staticmethod
    def get_theoretical_optimum():
        return -186.73091

    @staticmethod
    def get_type():
        return "min"

class Quartic:
    @staticmethod
    def func_simple(x: float, y:float) -> float:
        return (x**4/float(4)) - (x**2/float(2)) + (x/float(10)) + (y**2/float(2))

    @staticmethod
    def func(x_array: list) -> float:
        return Quartic.func_simple(x_array[0], x_array[1])

    @staticmethod
    def get_limits():
        return [-10, 10]

    @staticmethod
    def get_theoretical_optimum():
        return -0.35239

    @staticmethod
    def get_type():
        return "min"


import numpy

def mean_square_error_loss(network_target, network_predicted):

	gradient = network_target - network_predicted
	loss_metric = gradient ** 2

	return gradient, loss_metric

def cross_entropy_loss(network_target, network_predicted):

	num_classes = network_target.shape[0]

	target_label = network_target.argmax()

	loss_metric = -numpy.log(network_predicted[target_label])

	gradient = numpy.zeros(num_classes)
	gradient[target_label] = -1 / network_predicted[target_label]

	return gradient, loss_metric

def loss_from_function(loss, network_target, network_predicted):

    if loss == 'mse':
        return mean_square_error_loss(network_target, network_predicted)
    if loss == 'categorical_crossentropy':
        return cross_entropy_loss(network_target, network_predicted)


from sympy.core import S, pi, Rational
from sympy.functions import hermite, sqrt, exp, factorial
from sympy.physics.quantum.constants import hbar

def psi_n(n, x, m, omega):
    """
    Returns the wavefunction psi_{n} for the One-dimensional harmonic oscillator.

    ``n``
        the "nodal" quantum number.  Corresponds to the number of nodes in the
        wavefunction.  n >= 0
    ``x``
        x coordinate
    ``m``
        mass of the particle
    ``omega``
        angular frequency of the oscillator

    :Example:

    >>> from sympy.physics.qho_1d import psi_n
    >>> from sympy import var
    >>> var("x m omega")
    (x, m, omega)
    >>> psi_n(0, x, m, omega)
    (m*omega)**(1/4)*exp(-m*omega*x**2/(2*hbar))/(hbar**(1/4)*pi**(1/4))

    """

    # sympify arguments
    n, x, m, omega = list(map(S, [n, x, m, omega]))
    nu = m * omega / hbar
    # normalization coefficient
    C =  (nu/pi)**(S(1)/4) * sqrt(1/(2**n*factorial(n)))

    return  C * exp(-nu* x**2 /2) * hermite(n, sqrt(nu)*x)

def E_n(n,omega):
    """
    Returns the Energy of the One-dimensional harmonic oscillator

    ``n``
        the "nodal" quantum number
    ``omega``
        the harmonic oscillator angular frequency

    The unit of the returned value matches the unit of hw, since the energy is
    calculated as:

        E_n = hbar * omega*(n + 1/2)
    """

    return hbar * omega*(n + Rational(1,2))


# -*- coding: utf-8 -*-
"""
Solution to Project Euler problem 113 - non-bouncy numbers

Author: Jaime Liew
https://github.com/jaimeliew1/Project_Euler_Solutions

there are (9 + k - 1) choose k ways to arrange the digits 1,2,3,4,5,6,7,8,9 with
repetition  in ascending order in a k digit number. (see 'combination with
repetitions' on wikipedia). These are increasing numbers.

There are an equal number of decreasing numbers for digits 1,2,3,4,5,6,7,8,9. If
0 is added to the digits for decreasing numbers, number of decreasing numbers
increases by a factor of (n_digits - k + 1).

"""
from scipy import special

def comb_with_replacements(n, k):
    """
    returns the number of ways to arrange k items drawn from a set of n unique
    items with replacement.
    """
    return special.comb(n + k - 1, k, exact=True)

def run():
    n_digits = 100
    n = 9 # number of available digits (1, 2, 3, 4, 5, 6, 7, 8, 9)
    N_bouncy = n
    for k in range(2, n_digits + 1):
        this_N = comb_with_replacements(n, k) * (n_digits - k + 2)
        N_bouncy += this_N
    return N_bouncy

if __name__ == "__main__":
    print(run())


import numpy as np
from scipy import stats

sync = np.array([94. , 84.9, 82.6, 69.5, 80.1, 79.6, 81.4, 77.8, 81.7, 78.8, 73.2,
       87.9, 87.9, 93.5, 82.3, 79.3, 78.3, 71.6, 88.6, 74.6, 74.1, 80.6])
asyncr = np.array([77.1, 71.7, 91. , 72.2, 74.8, 85.1, 67.6, 69.9, 75.3, 71.7, 65.7, 72.6, 71.5, 78.2])
check_normality(sync)
check_normality(asyncr)

check_variance_homogeneity(sync, asyncr)

ttest, p_value = stats.ttest_ind(sync, asyncr)
print("p value:%.8f" % p_value)
print("since the hypothesis is one sided >> use p_value/2 >> p_value_one_sided:%.4f" % (p_value/2))
if p_value/2 < 0.05:
    print('Reject null hypothesis')
else:
    print('Fail to reject null hypothesis')


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

def main():
    x = np.linspace(-8, 7, 150)
    mu = (-2, 0, 3)
    tau = (.7, 1, 2.8)
    colors = ["#348ABD", "#A60628", "#7A68A6"]
    parameters = zip(mu, tau, colors)

    for _mu, _tau, _color in parameters:
        plt.plot(x, stats.norm.pdf(x, _mu, scale=1. / _tau),
                 label="$\mu = %d,\;\\tau = %.1f$" % (_mu, _tau), color=_color)
        plt.fill_between(x, stats.norm.pdf(x, _mu, scale=1. / _tau),
                         color=_color, alpha=.33)

    plt.legend(loc="upper right")
    plt.xlabel("$x$")
    plt.ylabel("density function at $x$")
    plt.title("Probability distribution of three different Normal random \
    variables")
    plt.show()

if __name__ == '__main__':
    main()


import numpy as np
def recursive_fibonacci(n):
    return n  if n == 0 or n == 1 else recursive_fibonacci(n-1) + recursive_fibonacci(n-2)

def dynamic_fibonacci(n):
    if n == 0 or n == 1:
        return n
    size = n+1
    memoized = [0] * size
    memoized[1] = 1
    for i in range(2,size):
        memoized[i] = memoized[i-1] + memoized[i-2]
    return memoized[n]

def matrix_fibonacci(n):
    Q = np.array([[1,1],[1,0]])
    cache = {}

    def get_matrix_power(matrix, power):
        if power == 1:
            return matrix
        if power in cache:
            return cache[power]
        tmp = get_matrix_power(matrix, int(power/2))
        result = tmp @ tmp
        cache[power] = result
        return result

    def get_fibonacci_number(n):
        if n == 0 or n == 1:
            return n
        else:
            powers = [2**power for (power, digit) in enumerate(reversed(bin(n-1)[2:])) if digit == '1']
            matrices = [get_matrix_power(Q, power) for power in powers]

            while len(matrices) > 1:
                M1 = matrices.pop()
                M2 = matrices.pop()
                res = M1 @ M2
                matrices.append(res)
            return matrices[0][0][0]

    return get_fibonacci_number(n)


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

# Load the dataframe
df = pd.read_csv(path)

#Code starts here

#Task 1
total = df.shape[0]
p_a = len(df[df['fico'] > 700]) / total

p_b = len(df[df['purpose'] == 'debt_consolidation']) / total

p_a_b = len(df[(df['fico'] > 700) & (df['purpose'] == 'debt_consolidation')]) / total
p_a_b = p_a_b / p_b

result = p_a_b == p_a
print(result)

#Task 2
prob_lp = len(df[df['paid.back.loan'] == 'Yes']) /total

prob_cs = len(df[df['credit.policy'] == 'Yes']) /total

new_df = df[df['paid.back.loan'] == 'Yes']

prob_pd_cs = len(df[(df['paid.back.loan'] == 'Yes') & (df['credit.policy'] == 'Yes')]) / total
bayes = prob_pd_cs / prob_cs
print(bayes)

#Task 3
df['purpose'].value_counts(normalize=True).plot(kind='bar')
plt.title("Probability Distribution of Purpose")
plt.ylabel("Probability")
plt.xlabel("Number of Purpose")
plt.show()

df1 = df[df['paid.back.loan'] == 'No']

df1['purpose'].value_counts(normalize=True).plot(kind='bar')
plt.title("Probability Distribution of Purpose")
plt.ylabel("Probability")
plt.xlabel("Number of Purpose")
plt.show()

#Task 4
inst_median = df['installment'].median()
inst_mean = df['installment'].mean()

df['installment'].hist(normed = True, bins=50)
plt.axvline(x=inst_median, color='r')
plt.axvline(x=inst_mean, color='g')
plt.show()

df['log.annual.inc'].hist(normed = True, bins=50)
plt.show()


import numpy as np
import pandas as pd
from scipy.stats import binom_test

def simulation_binomial_test(observed_successes, n, p, alternative_hypothesis):
  #initialize null_outcomes
  null_outcomes = []

  #generate the simulated null distribution
  for i in range(10000):
    simulated_monthly_visitors = np.random.choice(['y', 'n'], size=n, p=[p, 1-p])
    num_purchased = np.sum(simulated_monthly_visitors == 'y')
    null_outcomes.append(num_purchased)

  null_outcomes = np.array(null_outcomes)

  if alternative_hypothesis == 'less':
    p_value = np.sum(null_outcomes <= observed_successes)/len(null_outcomes)
  elif alternative_hypothesis == 'greater':
    p_value = np.sum(null_outcomes >= observed_successes)/len(null_outcomes)
  else:
    difference = np.abs(p*n - observed_successes)
    upper = p*n + difference
    lower = p*n - difference
    p_value = np.sum((null_outcomes >= upper) | (null_outcomes <= lower))/len(null_outcomes)

  #return the p-value
  return p_value

#Test your function:
print('lower tail one-sided test:')
p_value1 = simulation_binomial_test(45, 500, .1, alternative_hypothesis = 'less')
print("simulation p-value: ", p_value1)

p_value2 = binom_test(45, 500, .1, alternative = 'less')
print("binom_test p-value: ", p_value2)

print('upper tail one-sided test:')
p_value1 = simulation_binomial_test(53, 500, .1, alternative_hypothesis = 'greater')
print("simulation p-value: ", p_value1)

p_value2 = binom_test(53, 500, .1, alternative = 'greater')
print("binom_test p-value: ", p_value2)

print('two-sided test:')
p_value1 = simulation_binomial_test(42, 500, .1, alternative_hypothesis = 'not_equal')
print("simulation p-value: ", p_value1)

p_value2 = binom_test(42, 500, .1)
print("binom_test p-value: ", p_value2)

Data Scientist Career Path/8. Hypothesis Testing/1. Testing a Sample Statistics/2. Binomial Test/12. review.py


# coding: utf-8
# Implement the Logistic Regression from scratch
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import math
from sklearn.metrics import roc_auc_score
from scipy.spatial.distance import euclidean

# GD update rule: para_new = para_old - learning_rate * gradient
class Sigmoid():
    def __call__(self, x):
        #allows the class's instance to be called as a function, not always modifying the instance itself.
        return 1 / (1 + np.exp(-x))

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

# s = Sigmoid()
# s()

class LogisticRegression(object):
    def __init__(self, learning_rate, max_iter=100, tot=0.1):
        self.lr = learning_rate
        self.max_iter = max_iter
        self.tot = tot
        self.sigmoid = Sigmoid()

    def _initialize_parameters(self, X):
        """
        :param X: shape (m, p)
        """
        n_features = X.shape[1]
        # Initialize parameters between [-1/sqrt(N), 1/sqrt(N)]
        limit = 1 / math.sqrt(n_features)
        self.param = np.random.uniform(-limit, limit, n_features) # shape: (n_features, )

    def _fit(self, X, y):
        self._initialize_parameters(X)
        num_iter = 0
        diff_param = 999
        while (diff_param > self.tot and num_iter < self.max_iter):
            num_iter += 1
            y_pred = self.sigmoid(X.dot(self.param))
            grad = (y_pred - y).dot(X) # shape: (n_features, )
            old_para = self.param
            new_para = old_para - self.lr * grad
            diff_param = euclidean(old_para, new_para)
            self.param = new_para

    def _predict(self, X):
        y_pred = self.sigmoid(X.dot(self.param))
        return y_pred

if __name__ == '__main__':
    breast_cancer = load_breast_cancer()
    X = breast_cancer['data']
    y = breast_cancer['target']
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
    clf = LogisticRegression(learning_rate=0.01, max_iter=200, tot=0.001)
    clf._fit(X_train, y_train)
    pred = clf._predict(X_test)
    print(roc_auc_score(y_test, pred))


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

import numpy as np
import sympy as sy

def dct(xn:np.ndarray)->np.ndarray:
    """离散余弦变换

    使用矩陈乘法来计算乘积累加。

    :Parameters:
        - xn: 离散信号序列

    :Returns: DCT变换序列
    """
    N = xn.size
    n = k = np.arange(N).reshape(N, 1)
    wnk = np.cos(np.dot((2*n + 1) * np.pi / (2.0 * N), k.T))
    ck = np.sqrt(2.0 / N) * np.dot(xn, wnk)
    ck[0] = 1.0 / np.sqrt(N) * np.sum(xn)
    return ck

def idct(ck:np.ndarray)->np.ndarray:
    """离散余弦逆变换

    :Parameters:
        - xn: DCT变换序列

    :Returns: 离散信号序列
    """
    N = ck.size
    n = k = np.arange(N).reshape(N, 1)
    wnk = np.cos(np.dot((2*n + 1) * np.pi / (2.0 * N), k.T))
    xn = np.sqrt(2.0 / N) * np.dot(ck, wnk.T)
    xn += 1.0 / np.sqrt(N) * ck[0]
    return xn


import numpy as np

X = [1, 2, 3, 4, 5]
Y = [1, 2, 3, 4, 5]

X_array = np.array(X)
Y_array = np.array(Y)

print(X_array)
# 转换为5*1矩阵
print(Y_array.reshape(5, 1))
# 矩阵加法
print(X + Y)

# 矩阵点乘
res_tmp = np.dot(X_array, Y_array)
print(res_tmp)

res_tmp1 = np.sum(X_array * Y_array)
print(res_tmp1)

# 向量之间的乘法

# 向量的叉乘

# 对应项相乘
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
# 点乘
print(np.inner(a, b))
# 叉乘
print(np.outer(a, b))
# 对应项相乘
print(np.multiply(a, b))

# TODO: 语法结构待理解
c = np.mat(((1, 2), (5, 6)))
d = np.mat(((0, 1), (2, 3)))
print(c)
print(c + d)


import numpy as np
from math import sqrt

def accuracy_score(y_true, y_predict):
    """计算 y_true 和 y_predict 之间的准确率"""
    assert y_true.shape[0] == y_predict.shape[0], \
        "the size of y_true must be equal to the size of y_predict"

    return sum(y_true == y_predict) / len(y_true)

def mean_squared_error(y_true, y_predict):
    # 计算 MSE
    assert len(y_true) == len(y_predict)
    return np.sum((y_true - y_predict)**2) / len(y_true)

def root_mean_squared_error(y_true, y_predict):
    # 计算 RMSE
    return sqrt(mean_squared_error(y_true, y_predict))

def mean_absolute_error(y_true, y_predict):
    # 计算 RAE
    assert len(y_true) == len(y_predict), \
        "The size of y_true must be equal to the size of y_predict"

    return np.sum(np.abs(y_predict - y_true)) / len(y_true)

def r2_score(y_true, y_predict):
    # 计算 y_true 和 y_predict 之间的 R Square
    # 1 - （MSE / 方差）
    return 1 - mean_squared_error(y_true, y_predict) / np.var(y_true)

def TN(y_ture, y_predict):
    assert len(y_ture) == len(y_predict)
    return np.sum((y_ture == 0) & (y_predict == 0))

def FN(y_ture, y_predict):
    assert len(y_ture) == len(y_predict)
    return np.sum((y_ture == 1) & (y_predict == 0))

def FP(y_ture, y_predict):
    assert len(y_ture) == len(y_predict)
    return np.sum((y_ture == 0) & (y_predict == 1))

def TP(y_ture, y_predict):
    assert len(y_ture) == len(y_predict)
    return np.sum((y_ture == 1) & (y_predict == 1))

def precision_score(y_ture, y_predict):
    tp = TP(y_ture, y_predict)
    fp = FP(y_ture, y_predict)
    try:
        return tp / (tp + fp)
    except:
        return 0.0

def recall_score(y_ture, y_predict):
    tp = TP(y_ture, y_predict)
    fn = FN(y_ture, y_predict)
    try:
        return tp / (tp + fn)
    except:
        return 0.0

def f1_score(precision, recall):
    try:
        return (2 * precision * recall) / (precision + recall)
    except:
        return 0.0

def TPR(y_true, y_predict):
    tp = TP(y_true, y_predict)
    fn = FN(y_true, y_predict)
    try:
        return tp / (tp + fn)
    except:
        return 0.0

def FPR(y_true, y_predict):
    fp = FP(y_true, y_predict)
    tn = TN(y_true, y_predict)
    try:
        return fp / (fp + tn)
    except:
        return 0.0


from PIL import Image
import os
import sys
import numpy as np
from scipy import linalg

def restored_tensor(X,a1,a2):
    pa1 = a1.T.dot(a1)
    pa2 = a2.T.dot(a2)
    X2 = np.tensordot(X,pa1,(1,0))
    X3 = np.tensordot(X2,pa2,(0,0))
    return X3.transpose(2,1,0)

def save_img(X, filename):
    Image.fromarray(np.uint8(X)).save(filename)
    print('Saved as ' + filename)

def hosvd(filename,ratio):
    path, ext = os.path.splitext(filename)
    img = Image.open(filename)
    w = img.width
    h = img.height
    X = np.asarray(img)
    X1 =  X.transpose(0,2,1).reshape(h*3,w)
    X2 = X.transpose(1,2,0).reshape(w*3,h)
    U,s,A1 = linalg.svd(X1)
    U,s,A2 = linalg.svd(X2)
    r1 = int(w*ratio)
    r2 = int(h*ratio)
    a1 = A1[:r1, :]
    a2 = A2[:r2, :]
    XP = restored_tensor(X, a1, a2)
    filename = path+'_hosvd'+ ext
    print linalg.norm(X-XP)/linalg.norm(X)
    save_img(XP, filename)

def hooi(filename, ratio):
    path, ext = os.path.splitext(filename)
    img = Image.open(filename)
    w, h = img.size
    r1 = int(w*ratio)
    r2 = int(h*ratio)
    X = np.asarray(img)
    np.random.seed(0)
    X1 = np.random.rand(w,w)
    X2 = np.random.rand(h,h)
    U,s,A1 = linalg.svd(X1)
    U,s,A2 = linalg.svd(X2)
    a1 = A1[:r1, :]
    a2 = A2[:r2, :]
    XP = restored_tensor(X,a1,a2)
    for i in range(10):
    	X1 = np.tensordot(X,a2.T,(0,0)).transpose(2,1,0).reshape(r2*3,w)
    	U,s,A1 = linalg.svd(X1)
    	a1 = A1[:r1, :]
    	X2 = np.tensordot(X,a1.T,(1,0)).transpose(2,1,0).reshape(r1*3,h)
    	U,s,A2 = linalg.svd(X2)
    	a2 = A2[:r2, :]
        XP = restored_tensor(X,a1,a2)
        print linalg.norm(X-XP)/linalg.norm(X)
    filename = path+'_hooi'+ ext
    save_img(XP, filename)

def main():
    ratio = 0.2
    argc = len(sys.argv)
    if (argc <2):
        print("usage:")
        print("$ python %s filename ratio" % sys.argv[0])
        return
    filename = sys.argv[1]
    if (os.path.exists(filename) == False):
        print ("File is not found: %s" % filename)
        return
    if (argc >2):
        ratio = float(sys.argv[2])
    print "Ratio = " + str(ratio)
    print "Performing HOSVD"
    hosvd(filename,ratio)
    print "Performing HOOI"
    hooi(filename,ratio)

if __name__ == '__main__':
    main()


import numpy as np
import numpy.linalg
import labfuns
from assign1 import mlParams

def computePrior(labels, W=None):
    """
    NOTE: you do not need to handle the W argument for this part!
    in: labels - N vector of class labels
    out: prior - C x 1 vector of class priors
    """
    Npts = labels.shape[0]
    if W is None:
        W = np.ones((Npts,1))/Npts
    else:
        assert(W.shape[0] == Npts)
    classes = np.unique(labels)
    Nclasses = np.size(classes)

    prior = np.zeros((Nclasses,1))

    # TODO: compute the values of prior for each class!
    # ==========================
    for l in labels:
        prior[l] += 1

    prior = prior / len(labels)
    # ==========================

    return prior

def classifyBayes(X, prior, mu, sigma):
    """
    in:      X - N x d matrix of M data points
         prior - C x 1 matrix of class priors
            mu - C x d matrix of class means (mu[i] - class i mean)
         sigma - C x d x d matrix of class covariances (sigma[i] - class i sigma)
    out:     h - N vector of class predictions for test points
    """
    Npts = X.shape[0]
    Nclasses,Ndims = np.shape(mu)
    logProb = np.zeros((Nclasses, Npts))

    # TODO: fill in the code to compute the log posterior logProb!
    # ==========================
    for c in range(Nclasses):
        log_prior = np.log(prior[c])
        log_det_sigma = np.log(np.linalg.det(sigma[c]))
        # calculate the inverse of the diagonal sigma
        inv_sigma = np.diag(1.0 / np.diag(sigma[c]))
        dev = X - mu[c]  # deviation
        # Mahalanobis distance
        mah_dis_sq = np.diag(dev.dot(inv_sigma).dot(dev.T))

        logProb[c, :] = -0.5*log_det_sigma - 0.5*mah_dis_sq + log_prior

    # ==========================
    # one possible way of finding max a-posteriori once
    # you have computed the log posterior
    h = np.argmax(logProb, axis=0)
    return h

if __name__ == '__main__':
    # testing
    X, labels = labfuns.genBlobs(centers=5)

    prior = computePrior(labels)

    # estimate the mu and sigma from maximum libklihood method
    mu, sigma = mlParams(X, labels)

    ret = classifyBayes(X, prior, mu, sigma)

    print(ret)
    print("--------------------------")
    print(labels)
    print(ret == labels)


import numpy as np
import algorith.clr_book.ch12_binary_srch_trees.tree as tr

## Per fig 15.9, q is 0 indexed while p is 1 indexed.
# This is why 1 will be subtracted from p's indices
# but not q's.
def opt_bst_recurse(p,q,i,j):
    w=0
    for l in range(i,j+1):
        w+=p[l-1]#p[l] if p were 1 indexed.
    for l in range(i-1,j+1):
        w+=q[l]
    if j==i-1:
        return q[i-1]
    else:
        minn = np.inf
        for r in range(i,j+1):
            trm = opt_bst_recurse(p,q,i,r-1)+\
                opt_bst_recurse(p,q,r+1,j)+w
            if trm<minn:
                minn=trm
        return minn

def opt_bst_top_dwn_strt(p,q):
    n=len(p)
    ## e is 1 to n+1, 0 to n.
    e = np.ones((n+2,n+1))*np.inf
    w = np.zeros((n+2,n+1)).astype(int)
    root = np.zeros((n+2,n+1)).astype(int)
    return opt_bst_top_dwn(p,q,1,len(p),e,w,root)

def opt_bst_top_dwn(p,q,i,j,e,w,root):
    if e[i,j]<np.inf:
        return e[i,j], w, root
    w1=0
    for l in range(i,j+1):
        w1+=p[l-1]#p[l] if p were 1 indexed.
    for l in range(i-1,j+1):
        w1+=q[l]
    w[i,j] = w1
    if j==i-1:
        e[i,j] = q[i-1]
    else:
        e[i,j] = np.inf
        for r in range(i,j+1):
            trm = opt_bst_top_dwn(p,q,i,r-1,e,w,root)[0]+\
                opt_bst_top_dwn(p,q,r+1,j,e,w,root)[0]+w1
            if trm<e[i,j]:
                e[i,j]=trm
                root[i,j] = r
    return e[i,j], w, root

def create_tree(root,i,j):
    if j<=i-1:
        node = tr.TreeNode("d"+str(i-1),None,None)
    else:
        node = tr.TreeNode("k"+str(root[i,j]),None,None)
        node.left = create_tree(root,i,root[i,j]-1)
        node.right = create_tree(root,root[i,j]+1,j)
    return node

def walk_preorder(tn,parent=None,left=True):
    if tn is not None:
        if parent is None:
            print(str(tn.key) + " is the root")
        elif left:
            print(str(tn.key) + " is the left child of " + str(parent.key))
        else:
            print(str(tn.key) + " is the right child of " + str(parent.key))
        walk_preorder(tn.left,tn,True)
        walk_preorder(tn.right,tn,False)

if __name__=="__main__":
    p=np.array([.15,.1,.05,.1,.2])
    q=np.array([.05,.1,.05,.05,.05,.1])
    res = opt_bst_recurse(p,q,1,len(p))
    print(res)
    e,w,root=opt_bst_top_dwn_strt(p,q)
    print(e)
    print(root)
    r_node = create_tree(root,1,len(p))
    # You can put a break point here and verify the tree is
    # the same as fig 15.9-b.
    walk_preorder(r_node)


from __future__ import division
import numpy as np
import matplotlib.pyplot as plt

class RK(object):
    """
        Implementation of the method fourth order Runge Kutta, to obtain the value of a 2nd order differential
        equation, given its initial conditions, obtain its graph.
        The function receives equations converted into a system of equations of the form:
        y = u
        du/dx = v
        dv/dx = y"

        du/dx = f(v)
        dv/dx = g(v, u, t)

        Example:
            from RungeKutta.SecondOrderODE.RK import *
            from math import e

            def f(v):
                return v

            def g(v, u, t):
                return 4*v + 6*e**(3*t) - 3*e**(-t)

            rk = RK(f, g)
            ► To get the solution given the initial conditions
            ui = 1
            vi = -1
            ti = 0
            h = 0.1
            done = 1.5
            r = rk.solve(ti, ui, vi, done, h)
            print("u =", r[0])
            print("v =", r[1])
            ► To obtain the graph
            rk.graph()
        """

    def __init__(self, function1, function2):
        """
        :param function1: Function that depends on v (f(v))
        :param function2: Function that depends on v, u, t (g(v, u, t))
        """

        self.f = function1
        self.g = function2
        self.ts = []
        self.us = []
        self.vs = []

    def solve(self, ti, ui, vi, done, h):
        """
        Initialize the ODE solution given the initial values
        :param ti: Value of the initial t
        :param ui: Value of the initial y
        :param vi: Value of the initial y'
        :param done: Values that you want to evaluate in the diff system
        :param h: Integration step
        :return: Values of u and v
        """

        st = np.arange(ti, done, h)

        for _ in st:
            K1 = self.f(vi)
            m1 = self.g(vi, ui, ti)

            K2 = self.f(vi + m1 * h / 2)
            m2 = self.g(vi + m1 * h / 2, ui + K1 * h / 2, ti + h / 2)

            K3 = self.f(vi + m2 * h / 2)
            m3 = self.g(vi + m2 * h / 2, ui + K2 * h / 2, ti + h / 2)

            K4 = self.f(vi + m3 * h)
            m4 = self.g(vi + m3 * h, ui + K3 * h, ti + h)

            ui += (h / 6) * (K1 + 2 * K2 + 2 * K3 + K4)
            self.us.append(ui)

            vi += (h / 6) * (m1 + 2 * m2 + 2 * m3 + m4)
            self.vs.append(vi)

            ti += h
            self.ts.append(ti)
        return ui, vi

    def graph(self, *args, **kwargs):
        """
        Graph the function with the values obtained from each iteration
        :param args:
        :param kwargs:
        :return:
        """

        plt.title("Graph of functions")
        plt.plot(self.ts, self.us, label="u", *args, **kwargs)
        plt.plot(self.ts, self.vs, label="v", *args, **kwargs)
        plt.legend()
        plt.grid()
        plt.xlabel("$ t $")
        plt.ylabel("$ u:v $")
        plt.show()

if __name__ == "__main__":
    from math import e

    def f(v):
        return v

    def g(v, u, t):
        return 4*v + 6*e**(3*t) - 3*e**(-t)

    u_i = 1
    v_i = -1
    t_i = 0
    sh = 0.1
    end = 1.5

    rk = RK(f, g)

    r = rk.solve(t_i, u_i, v_i, end, sh)
    print("u =", r[0])
    print("v =", r[1])
    rk.graph()


import numba
import numpy as np

from typing import List

@numba.njit()
def dominates(x: np.ndarray, y: np.ndarray) -> bool:
    """Returns true if x dominates y.

    Args:
        x (np.ndarray): First solution. Should be a 1-D array of numerics.
        y (np.ndarray): Second solution. Should be the same shape as x.

    Returns:
        bool: True if x dominates y, false otherwise.
    """
    dom = False
    for i in range(len(x)):
        if x[i] > y[i]:
            return False
        elif x[i] < y[i]:
            dom = True
    return dom

@numba.njit()
def non_dominated(data: np.ndarray) -> np.ndarray:
    """Finds the non-dominated front from a population of solutions.

    Args:
        data (np.ndarray): 2-D array of solutions, with each row being a single solution.

    Returns:
        np.ndarray: Boolean array of same length as number of solutions (rows). The value is
            true if corresponding solution is non-dominated. False otherwise
    """

    num_solutions = len(data)
    index = np.zeros(num_solutions, dtype=np.bool_)
    index[0] = True
    for i in range(1, num_solutions):
        index[i] = True
        for j in range(i):
            if not index[j]:
                continue
            if dominates(data[i], data[j]):
                index[j] = False
            elif dominates(data[j], data[i]):
                index[i] = False
                break
    return index

@numba.njit()
def fast_non_dominated_sort(data: np.ndarray) -> np.ndarray:
    """Conduct fast non-dominated sorting on a population of solutions.

    Args:
        data (np.ndarray): 2-D array of solutions, with each row being a single solution.

    Returns:
        np.ndarray: n x f boolean array. n is the number of solutions, f is the number of fronts.
            The value of an array element is true if the corresponding solution id (column) belongs in
            the corresponding front (row).
    """

    num_solutions = len(data)
    indices = np.arange(num_solutions)
    taken = np.zeros(num_solutions, dtype=np.bool_)
    fronts = np.zeros((num_solutions, num_solutions), dtype=np.bool_)

    for i in indices:
        current_front = non_dominated(data[~taken])

        current_front_all = np.zeros(num_solutions, dtype=np.bool_)
        current_front_all[~taken] = current_front
        fronts[i] = current_front_all

        taken = taken + fronts[i]
        if not fronts[i].any():
            break
    return fronts[:i]

def fast_non_dominated_sort_indices(data: np.ndarray) -> List[np.ndarray]:
    """Conduct fast non-dominated sorting on a population of solutions.

    This function returns identical results as `fast_non_dominated_sort`, but in a different format.
    This function returns an array of solution indices for each front, packed in a list.

    Args:
        data (np.ndarray): 2-D array of solutions, with each row being a single solution.

    Returns:
        List[np.ndarray]: A list with f elements where f is the number of fronts in the data,
            arranged in ascending order. Each element is a numpy array of the indices of solutions
            belonging to the corresponding front.
    """
    fronts = fast_non_dominated_sort(data)
    return [np.where(fronts[i]) for i in range(len(fronts))]


import numpy as np
from scipy import linalg

def sum_numbers(x, y):
    """
    TODO: IMPLEMENT ME
    Sum two numbers.

    Args:
        x (int, float): first number in sum
        y (int, float): second number in sum
    Returns:
        Sum of x and y.
    """
    # replace the following line with an actual implementation that returns something
    #raise NotImplementedError
    return x+y
def multiply_numbers(x, y):
    """
    TODO: IMPLEMENT ME
    Multiply two numbers.

    Args:
        x (int, float): first number in product
        y (int, float): second number in product
    Returns:
        Product of x and y.
    """

    # replace the following line with an actual implementation that returns something
    return x*y

def create_add_matrix(x):
    """
    TODO: IMPLEMENT ME
    Step 1. Create a 3x3 numpy array whose elements are all ones.
    Step 2. Sum the array and the input array x.
    Step 3. Return the result

    Args:
        x (np.ndarray): a 2D numpy array
    Returns:
        output (np.ndarray): the operation result
    """

    # replace the following line with an actual implementation that returns something
    ones3=np.ones((3,3))
    sumwith=ones3+x
    return sumwith

def indexing_aggregation(x, n):
    """
    TODO: IMPLEMENT ME
    Return the mean value of the first n elements of the input array x.

    Args:
        x (np.ndarray): a 1D numpy array
    Returns:
        output (float): the operation result

    """
    # replace the following line with an actual implementation that returns something
    return np.mean(np.array(x)[:n])

def matrix_inverse(A):
    """
    TODO: IMPLEMENT ME
    Return the inverse of Matrix A.

    Checks for dimension mismatch

    Args:
        x (np.ndarray): a 2D numpy array
    Returns:
        output (np.ndarray): the operation result

    """
    # replace the following line with an actual implementation that returns something
    if (A.shape[0] != A.shape[1]):
        return None
    else:
        return np.linalg.inv(A)


import os.path
from os import path
import numpy as np

# Prints a bar
def print_split():
    print("--------------------------------------------------\n")

def eliminate(A, b):
    print_split()

    n = len(A)
    m = len(A[0])

    eliminate_index = m - 1

    I = []
    J = []
    K = []
    for i in range(n):
        if A[i, eliminate_index] > 0:
            I.append(i)
        elif A[i, eliminate_index] < 0:
            J.append(i)
        else:
            K.append(i)

    print("Elimination:\n")
    print("I: ", I)
    print("J: ", J)
    print("K: ", K)

    # Inequalities from sets I and J are combined to eliminate one variable
    I_length = len(I)
    J_length = len(J)

    # Elimination of element x(m-1) produces new system
    new_A = []
    new_A = []
    new_b = []

    for i in range(I_length):
        for j in range(J_length):
            for p in range(m):
                if p == eliminate_index:
                    continue
                new_A.append(((A[I[i], p] / A[I[i], eliminate_index]) - (A[J[j], p] / A[J[j], eliminate_index])))
            new_b.append((b[I[i]] / A[I[i], eliminate_index]) - (b[J[j]] / A[J[j], eliminate_index]))

    # Inequalities from set K are just copied to new system (m is in K if A(m, n-1) = 0)
    K_length = len(K)
    for k in range(K_length):
        for p in range(m):
            if p == eliminate_index:
                continue
            new_A.append(A[K[k], p])
        new_b.append(b[K[k]])

    new_A = np.array(new_A)
    height = I_length * J_length + K_length
    new_A = np.reshape(new_A, (height, m-1))

    print("New A:\n", new_A)
    print("New b:", new_b)

    # new_m = m-1 --> eliminate if new_m > 1 <=> m > 2
    if m > 2:
        return eliminate(new_A, new_b)
    else:
        return interval_intersection(new_A, new_b)

def interval_intersection(A, b):
    n = len(b)
    left_border = -np.Inf
    right_border = np.Inf

    # interval_intersection is used when all variables except one are eliminated
    # current system:
    # c1*x1 > b1
    # c2*x1 > b2
    # ...
    # cK*x1 > bK
    # intersection of all inequalities returns interval

    for p in range(n):
        if A[p, 0] == 0:
            # Impossible inequality
            if b[p] > 0:
                return right_border, left_border

        # Division by zero
        if A[p, 0] == 0:
            # Negative Inf
            if (np.sign(b[p]) * np.sign(A[p, 0])) <= -0:
                value = -np.Inf
            # Positive Inf
            elif (np.sign(b[p]) * np.sign(A[p, 0])) >= 0:
                value = np.Inf
        # Divisor != 0
        else:
            value = b[p]/A[p, 0]

        # Observe c*x1 > b:
        # 1) if c < 0:
        #      then x1 < b/c <=> x1 is in (-INF, b/c]
        # 2) else
        #      then x1 > b/c <=> x1 is in [b/c, +INF]

        if A[p, 0] < 0:
            right_border = min(right_border, value)
        else:
            left_border = max(left_border, value)

    print_split()
    # right < left <=> empty set
    msg = ""
    if right_border < left_border:
        print("The solution doesn't exist")
    else:
        if left_border == -np.Inf:
            msg += "(-Inf, "
        else:
            msg += "[" + str(round(left_border , 3)) + ", "

        if right_border == np.Inf:
            msg += "Inf)"
        else:
            msg += str(round(right_border, 3)) + "]"

        msg += "\n"
        print("VAR is in: " + msg)

    return left_border, right_border

# set x(index) = value and remove column(index) from inequality system
def transform(A, b, index, value):
    n = len(A)
    for p in range(n):
        b[p] -= A[p, index] * value
    A = np.delete(A, index, axis=1)
    return A, b

def replace_var_with_function(A, b, c):
    n = len(A)
    m = len(A[0])
    if n == 0:
        return

    for i in range(n):
        for j in range(1, m, 1):
            A[i, j] -= (c[j] / c[0]) * A[i, 0]
        A[i, 0] *= (1 / c[0])

def input_option_1():
            print("---- NOTE ----\nThe file must be in the following format:\nN M\t\t\tWhere N - number of equations and M - number of variables\nA11 A12 ... A1M\nA21 A22 ... A2M\n................\nAN1 AN2 ... ANM\nb1 b2 b3 ... bN")
            print_split()
            file_name = input("Enter the file name: ")
            # Creating absolute path to the file in folder examples
            file_dir = os.path.dirname(__file__)
            rel_path = "examples/opt1/" + file_name
            abs_file_path = os.path.join(file_dir, rel_path)
            # Checking if the file exists
            if os.path.exists(abs_file_path) == False:
                # File not found, throw error
                print("The file doesn't exist!")
                raise Exception("The file didn't load because it doesn't exist")
            # File found, opening
            f = open(abs_file_path, 'r')

            # Read the dimensions and A and b
            n, m = [int(x) for x in next(f).split()] # Reads the dimensions
            tmp_array = []
            for line in f: # Read the next lines
                tmp_array.append([str(x) for x in line.split()])
            f.close() # File not needed, all is in tmp_array

            in_A = np.zeros((n, m), dtype=float)
            in_b = np.zeros(n, dtype=float)
            # print(tmp_array)
            for i in range(n + 1):
                if i < n:   # Loading A
                    for j in range(m):
                        in_A[i, j] = (float(tmp_array[i][j]))
                else:       # Loading b
                    for j in range(n):
                        in_b[j] = (float(tmp_array[i][j]))
                    break

            return n, m, in_A, in_b

def input_option_2():
            print("---- NOTE ----\nThe file must be in the following format:\nN M\t\t\tWhere N - number of equations and M - number of variables\nA11 A12 ... A1M\nA21 A22 ... A2M\n................\nAN1 AN2 ... ANM\nb1 b2 b3 ... bN\nx1 x2 x3 ... xM")
            print_split()
            file_name = input("Enter the file name: ")
            # Creating absolute path to the file in folder examples
            file_dir = os.path.dirname(__file__)
            rel_path = "examples/opt2/" + file_name
            abs_file_path = os.path.join(file_dir, rel_path)
            #Checking if the file exists
            if os.path.exists(abs_file_path) == False:
                # File not found, throw error
                print("The file doesn't exist!")
                raise Exception("The file didn't load because it doesn't exist")
            # File found, opening
            f = open(abs_file_path, 'r')

            # Read the dimensions and A and b
            n, m = [int(x) for x in next(f).split()] # Reads the dimensions
            tmp_array = []
            for line in f: # Read the next lines
                tmp_array.append([str(x) for x in line.split()])
            f.close() # File not needed, all is in tmp_array

            # Loading variables
            in_A = np.zeros((n, m), dtype=float)
            in_b = np.zeros(n, dtype=float)
            in_x = np.zeros(m, dtype=float)
            # print(tmp_array)
            for i in range(n + 2):
                if i < n:           # Loading A
                    for j in range(m):
                        in_A[i, j] = (float(tmp_array[i][j]))
                elif i == n:        # Loading b
                    for j in range(n):
                        in_b[j] = (float(tmp_array[i][j]))
                else:               # Loading x
                    for j in range(m):
                        in_x[j] = (float(tmp_array[i][j]))

            return n, m, in_A, in_b, in_x

def input_option_3():
            print("---- NOTE ----\nThe file must be in the following format:\nN M\t\t\tWhere N - number of equations and M - number of variables\n\"min\" or \"max\"\t\t\tObjective function minimisation or maximisation\nc1 c2 c3 ... cM\n\nA11 A12 ... A1M\nA21 A22 ... A2M\n................\nAN1 AN2 ... ANM\t\tWhere constrains are <=\n  for x1, x2, x3, ... xM >= 0\n\nnb1 b2 b3 ... bN")
            print_split()
            file_name = input("Enter the file name: ")
            # Creating absolute path to the file in folder examples
            file_dir = os.path.dirname(__file__)
            rel_path = "examples/opt3/" + file_name
            abs_file_path = os.path.join(file_dir, rel_path)
            # Checking if the file exists
            if os.path.exists(abs_file_path) == False:
                # File not found, throw error
                print("The file doesn't exist!")
                raise Exception("The file didn't load because it doesn't exist")
            # File found, opening
            f = open(abs_file_path, 'r')

            # Read the dimensions and A and b and objective function type
            n, m = [int(x) for x in next(f).split()] # Reads the dimensions
            otype = next(f).split()
            otype = str(otype[0])
            if otype != "min" and otype != "max":
                f.close()
                raise Exception("The objective function type is wrong it should be either \"min\" or \"max\"!\n")

            tmp_array = []
            for line in f: # Read the next lines
                tmp_array.append([str(x) for x in line.split()])
            f.close() # File not needed, all is in tmp_array

            # Loading variables
            in_A = np.zeros((n, m), dtype=np.float64)
            in_b = np.zeros(n, dtype=np.float64)
            in_c = np.zeros(m, dtype=np.float64)
            # print(tmp_array)
            for i in range(n + 2):
                if i > 0 and i < n + 1: # Loading A
                    for j in range(m):
                        in_A[i-1, j] = -(float(tmp_array[i][j]))
                elif i == 0:            # Loading c
                    for j in range(m):
                        in_c[j] = (float(tmp_array[i][j]))
                else:                   # Loading b
                    for j in range(n):
                        in_b[j] = -(float(tmp_array[i][j]))

            if otype == "max":
                in_c = -in_c
            return n, m, in_c, in_A, in_b

def main():
    print("Furie-Mockin method of elimination")
    print_split()
    print("Select one of the possible opions:\n\t(1) Calculate intervals (input: matrix A, vector b)\n\t(2) Check if a vertex is in solution (input: matrix A, vector b, vertex x\n\t(3) Furie-Mockin method of elimination (input: vector c, matrix A, vector b)")
    option = int(input("Option: "))

    if option == 1:
        n, m, A, b = input_option_1()

        value = 0.0
        it = 1
        while 1:
            result = []
            if m > 1:
                result = eliminate(A, b)
            else:
                result = interval_intersection(A, b)
                break
            if result[0] > result[1]:    # left > right
                print("The solution doesn't exist!")
                break
            value = float(input("Input value:\nX" + str(it) + " = "))
            A, b = transform(A, b, 0, value)
            m -= 1
            it += 1
        return 0

    elif option == 2:
        n, m, A, b, x = input_option_2()

        print_split()
        print("Input:")
        print("A:\n", A)
        print("b: ", b)
        print("x: ", x)
        print_split()

        for i in range(n):
            for j in range(m):
                b[i] -= A[i, j] * x[j]
            if b[i] > 0:
                print("Given point is not a solution!")
                return 0
        print("Given point is a solution!")
        return 0

    elif option == 3:
        n, m, c, A, b = input_option_3()
        for i in range(m):
            tmp = np.zeros(m, dtype=np.float64)
            tmp[i] = 1
            A = np.vstack((A, tmp))
            b = np.append(b, 0)

        print_split()
        print("Input:")
        print("c: ", c)
        print("A:\n", A)
        print("b: ", b)
        print_split()

        replace_var_with_function(A, b, c)

        print("Replace variable with the objective funciton:")
        print("c: ", c)
        print("A:\n", A)
        print("b: ", b)
        print_split()

        interval = eliminate(A, b)
        print_split()
        if interval[0] < interval[1]:
            print("Solution: ", round(interval[0], 3))
        else:
            print("The solution doesn't exist!\n")

    else:
        print("Enter a valid option!")
        return -1

if __name__ == '__main__':
   main()


import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler

df_wine = pd.read_csv('wine.data',
                      header=None)
df_wine.columns = ['Class label', 'Alcohol', 'Malic acid', 'Ash',
                   'Alcalinity of ash', 'Magnesium', 'Total phenols',
                   'Flavanoids', 'Nonflavanoid phenols', 'Proanthocyanins',
                   'Color intensity', 'Hue', 'OD280/OD315 of diluted wines',
                   'Proline']

X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values

X_train, X_test, y_train, y_test =train_test_split(X, y, test_size=0.3,
                    									 random_state=0,
                     									 stratify=y)
mms = MinMaxScaler()
X_train_norm = mms.fit_transform(X_train)
X_test_norm = mms.transform(X_test)

stdsc = StandardScaler()
X_train_std = stdsc.fit_transform(X_train)
X_test_std = stdsc.transform(X_test)

ex = np.array([0, 1, 2, 3, 4, 5])

print('standardized:', (ex - ex.mean()) / ex.std())

# Please note that pandas uses ddof=1 (sample standard deviation)
# by default, whereas NumPy's std method and the StandardScaler
# uses ddof=0 (population standard deviation)

# normalize
print('normalized:', (ex - ex.min()) / (ex.max() - ex.min()))

Chapter 04/04 Bringing Features Onto The Same Scale/program.py


import numpy as np
from prml.nn.math.exp import exp
from prml.nn.math.log import log
from prml.nn.random.random import RandomVariable
from prml.nn.tensor.constant import Constant
from prml.nn.tensor.tensor import Tensor

class Exponential(RandomVariable):
    """
    Exponential distribution aka negative exponential distribution
    p(x|rate) = rate * exp(-rate * x)
    rate > 0
    Parameters
    ----------
    rate : tensor_like
        rate parameter
    data : tensor_like
        realization of this distribution
    p : RandomVariable
        original distribution of a model
    """

    def __init__(self, rate, data=None, p=None):
        super().__init__(data, p)
        rate = self._convert2tensor(rate)
        self.rate = rate

    @property
    def rate(self):
        return self.parameter["rate"]

    @rate.setter
    def rate(self, rate):
        try:
            ispositive = (rate.value > 0).all()
        except AttributeError:
            ispositive = (rate.value > 0)

        if not ispositive:
            raise ValueError("value of rate must be positive")
        self.parameter["rate"] = rate

    def forward(self):
        eps = np.random.standard_exponential(size=self.rate.shape)
        self.output = eps / self.rate.value
        if isinstance(self.rate, Constant):
            return Constant(self.output)
        return Tensor(self.output, self)

    def backward(self, delta):
        drate = -delta * self.output / self.rate.value
        self.rate.backward(drate)

    def _pdf(self, x):
        return self.rate * exp(-self.rate * x)

    def _log_pdf(self, x):
        return -self.rate * x + log(self.rate)


import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
from sklearn.metrics import r2_score

from commons.utils import Download

class NonLinearRegression:

    def __init__(self, is_download: bool = False, filename: str = 'china_gdp.csv') -> None:
        if is_download:
            Download(
                url='https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/china_gdp.csv',
                filename=filename
            )
        self.df = pd.read_csv('download/' + filename)

    def summarize(self):
        print(self.df.head(10))
        print(self.df.describe())

    def plotting_dataset(self) -> None:
        plt.figure(figsize=(8, 5))
        x_data, y_data = (self.df['Year'].values, self.df['Value'].values)
        plt.plot(x_data, y_data, 'ro')
        plt.xlabel('GDP')
        plt.ylabel('Year')
        plt.show()
        plt.close()

        # Training model
        beta_1 = 0.10
        beta_2 = 1990.0

        # logistic function
        Y_pred = self.sigmoid(x_data, beta_1, beta_2)
        plt.plot(x_data, Y_pred * 15000000000000.)
        plt.plot(x_data, y_data, 'ro')
        plt.show()

        # normalize our data
        xdata = x_data / max(x_data)
        ydata = y_data / max(y_data)

        popt, pcov = curve_fit(self.sigmoid, xdata, ydata)
        # print('beta_1 = %f, beta_2 = %f' % (popt[0], popt[1]))
        x = np.linspace(1960, 2015, 55)
        x = x / max(x)
        plt.figure(figsize=(8, 5))
        y = self.sigmoid(x, *popt)
        plt.plot(xdata, ydata, 'ro', label='data')
        plt.plot(x, y, linewidth=3.0, label='fit')
        plt.legend(loc='best')
        plt.xlabel('Year')
        plt.ylabel('GDP')
        plt.show()
        plt.close()

        self.training_model(xdata, ydata)

    def training_model(self, xdata, ydata):
        msk = np.random.rand(len(self.df)) < 0.8
        train_x = xdata[msk]
        test_x = xdata[~msk]
        train_y = ydata[msk]
        test_y = ydata[~msk]

        # Building model
        popt, pcov = curve_fit(self.sigmoid, train_x, train_y)

        # predict using test set
        y_hat = self.sigmoid(test_x, *popt)

        # Evaluation
        print('Mean absolute error: %.2f' % np.mean(np.absolute(y_hat - test_y)))
        print('Residual sum of squares (MSE): %.2f' % np.mean((y_hat - test_y) ** 2))
        print('R2-score: %.2f' % r2_score(y_hat, test_y))

    def sigmoid(self, x, beta_1: float, beta_2: float):
        y = 1 / (1 + np.exp(-beta_1 * (x - beta_2)))
        return y


"""
%% Tridiagonal System solver v1.0
Written by J.A. Ferrand B.Sc (ID: 2431646)
Embry-Riddle Aeronautical University - Daytona Beach

%% Description
A simple function that solves linear systems of the form $Ax = b$ using
the Thomas algorithm. The function, therefore, is intended for linear
algebra problems where the matrix $A$ is tridiagonal. The $b$ vector is
referred to as the Right-Hand-Side (RHS). The $A$ matrix can be input in
one of two ways. Firstly as an $r \times r$ square matrix (which should
be avoided). Secondly (and preferrably) as an $r \times 3$ rectangular
matrix in which the first column is a zero that is vertically
concatenated with A's subdiagonal, the second column is the main
diagonal, and the third column is the superdiagonal vertically
concatenated with a zero. This 2nd method takes advantage of the inherent
sparsity of tridiagonal systems to save memory and improve speed. If the
entire matrix is input, then the code first checks to ensure that the
matrix is in fact tridiagonal before implementing the Thomas algorithm.

%% Changelog
v1.1,(05/15/2021): Improved index vetorization. Enabled multiple RHS.
v1.0,(05/13/2021): Initial Release (Single RHS only).

%% Syntax
* INPUT(*A*): Tridiagonal Coefficient Matrix of size $r$.
* INPUT(*b*): Right-Hand Side. (matrix that is $r\times n$)
* OUTPUT(*x*): Solution. (Same size as *b*)

%% Function Definition
"""
def tridiag(A,B):
    import numpy as np
    dA = A.shape
    rA,cA = dA[0],dA[1] #Get rows and columns of coefficient matrix.
    a = A.ravel() #Linearized reference for A matrix. (Vectorization purposes)
    dB = B.shape
    rB,cB = dB[0],dB[1] #Get rows and columns of Right-Hand-Side vector.
    b = B.ravel() #Linearized reference for RHS vector. (Vectorization purposes)
    if rA != rB:
        raise Exception('Coefficient matrix and RHS do not have equal rows!')
    if rA == cA: #Square Mode (rA by rA "A" matrix)
        print('Non-sparse matrices take longer to process than sparse ones!')
        nonzero = a != 0 #Count number of nonzero elements in the coefficient matrix.
        if sum(nonzero) > 3*rA-2: #If more zeroes than tridiagonal elements permit.
            raise Exception('Input matrix is NOT tridiagonal.')
        start = 0 #LI of first main diagonal element in square mode.
        dia = cA + 1 #LI for a main diagonal step in square mode.
        edpA = rA*cA-1 #LI of the last entry in the A matrix in square mode.
    elif rA != cA and cA == 3: #Sparse Mode (rA by 3 "A" matrix).
        start = 1 #LI of first main diagonal element in sparse mode.
        dia = cA #LI for a main diagonal step in sparse mode.
        edpA = cA*(rA-1)+1 #LI of the last entry in the A matrix in sparse mode.
    else:
        raise Exception('Invalid coefficient matrix. Must be square or a 3-column sparse matrix.')

    #Thomas algorithm deployment
    X = np.zeros([rA,cB]) #Preallocate memory for the solution vector(s).
    x = X.ravel() #Linearized reference for solution vector(s).
    edpB = rB*cB-1 #LI of the last entry in the RHS.

    #Forward elimination of subdiagonal.
    row = np.arange(0,cB) #LI's of the first row (x & b)
    for i in range(0,rA-1):
        Mi = start + dia*i #LI of "ith" row superdiagonal element.
        Ui = Mi + 1 #LI of "ith+1" row main diagonal element.
        Mip1 = Mi + dia #LI of "ith" row main diagonal element.
        Lip1 = Mi + cA #LI of "ith+1" subdiagonal element of "ith" row.
        LoM = a[Lip1]/a[Mi] #Row elimination ratio.
        prow = row #Copy last row LI's
        row = prow + cB #Update LI's for next row.
        a[Mip1] = a[Mip1] - a[Ui]*LoM #Forward elimination update on main diagonal.
        b[row] = b[row] - b[prow]*LoM #Forward elimination on RHS vector(s).

    #Backward substitution to solve for x.
    row = np.arange(edpB-cB+1,edpB+1) #LI of the last row (x & b).
    x[row] = b[row]/a[edpA] #Solve for the last x-value.
    for i in range(1,rA):
        Mi = edpA - dia*i #LI of main diagonal element of "ith" row.
        Ui = Mi + 1 #LI of "ith" row superdiagonal element.
        prow = row[:] #Store indices of previous row.
        row = prow[:] - cB #Get indices of next row.
        x[row] = (b[row] - x[prow]*a[Ui])/a[Mi] #Solve for all "ith" x-values.
    return X

#Testing the function
#import numpy as np
#A = np.array([[2,1,0,0],[1,2,1,0],[0,1,2,1],[0,0,1,2]], dtype = np.float64)
#B = np.array([[1,2],[1,2],[1,2],[1,2]], dtype = np.float64)
#X = tridiag(A,B)


"""
Implementação dos metodos de interpolação de Newton e de Lagrange.
"""
import numpy as np

def Pol_Newton(x, y, po, pf):
    """
    Implementação do metodo de interpolação de Newton.
    :param x: Vetor com os valores de X.
    :param y: Vetor com os valores de Y.
    :param po: O valor inicial de X onde se pretende calcular a aproximação.
    :param pf: O valor final de X onde se pretende calcular a aproximação.
    :return: Dicionario com os valores entre os ponto pedidos e suas respectivas aproximações de F(x).
    """
    tam = len(x)
    res = np.array([], dtype='float32')
    pontos = np.linspace(po, pf, 100)       #Intervalo a ser calculado
    for p in pontos:
        FDD = np.ones(tam**2).reshape((tam, tam))*-1#Tabela de diferenças divididas(começa em -1 para visualização)
        for i in range(tam):
            FDD[i][0] = y[i]                        #Primeira coluna
        for i in range(1, tam):
            for j in range(tam-i):                  #Demais colunas
                FDD[j][i] = (FDD[j+1][i-1] - FDD[j][i-1])/(x[j+i] - x[j])    #Faz todas as colunas antes de iterar a linha
        x_termo = 1                                 #Auxiliar para a multiplicaçãos pelos termos de X
        pol = FDD[0][0]                             #Polinomio de Newton
        for ordem in range(1, tam):
            x_termo = x_termo*(p - x[ordem-1])
            pol += FDD[0][ordem]*x_termo            #Operador(primeiro termo de cada coluna)*multiplicação dos x
        res = np.append(res, pol)
    resultado = {"X": pontos,
                 "Y": res}
    return resultado

def Pol_Lagrange(x, y, po, pf):
    """
    Implementação do metodo de interpolação de Lagrande.
    :param x: Vetor com os valores de X.
    :param y: Vetor com os valores de Y.
    :param po: O valor inicial de X onde se pretende calcular a aproximação.
    :param pf: O valor final de X onde se pretende calcular a aproximação.
    :return: Dicionario com os valores entre os ponto pedidos e suas respectivas aproximações de F(x).
    """
    res = np.array([], dtype='float32')
    r = 0
    tam = len(x)
    pontos = np.linspace(po, pf, 100)  # Intervalo a ser calculado
    for ponto in pontos:
        for i in range(0, tam):         #Cada ordem de y(i)L(i)
            produtorio = 1              #L(i)
            for j in range(0, tam):
                if j != i:
                    produtorio *= (ponto - x[j])/(x[i] - x[j])
            r += produtorio*y[i]
        res = np.append(res, r)
        r = 0
    resultado = {"X": pontos,
                 "Y": res}
    return resultado


import numpy as np
from numpy.random import normal
import pandas as pd
from typing import Union, List

N_SAMPLES = 500
NOISE = 0.1

# Rings
MULTIPLIER = 2

# Moons, blobs
X_OFFSET = 0.5
Y_OFFSET = 0.25

class DataGenerator:

    def create_blobs(self,
                     n_samples: int = N_SAMPLES,
                     noise: Union[int, float] = NOISE,
                     x_offset: Union[int, float] = X_OFFSET,
                     y_offset: Union[int, float] = Y_OFFSET) -> dict:
        """
        | Generate x and y coordinates for two clusters
        |
        | -----------------------------------------------------------
        | Parameters
        | ----------
        |  n_samples : int
        |    Number of samples
        |
        |  noise : int, float
        |    How tight the blobs are around their mean
        |
        |  x_offset: int, float
        |    Offset in x dimension. Large values move moons apart,
        |    small move moons closer
        |
        |  y_offset: int, float
        |    Offset in y dimension. Positive values move moons apart,
        |    negative move moons closer
        |
        |
        | Returns
        | -------
        |   dict with keys ['x', 'y']
        """
        x1 = normal(0, noise, n_samples) + x_offset
        x2 = normal(0, noise, n_samples) - x_offset

        y1 = normal(0, noise, n_samples) + y_offset
        y2 = normal(0, noise, n_samples) - y_offset

        return {'x': [*np.concatenate([x1, x2]).round(3)],
                'y': [*np.concatenate([y1, y2]).round(3)]}

    def create_rings(self,
                     n_samples: int = N_SAMPLES,
                     noise: Union[int, float] = NOISE,
                     multiplier: Union[int, float] = MULTIPLIER) -> dict:
        """
        | Generate x and y coordinates for two concentric rings whose radii
        | are offset by multiplier.
        |
        | -----------------------------------------------------------------
        | Parameters
        | ----------
        |  n_samples: int
        |    Number of samples
        |
        |  noise : int, float
        |    Noise to add to rings. 0 = perfect curves
        |
        |  multiplier: int, float
        |    Offset for second ring's radius
        |
        |
        | Returns
        | -------
        |   dict with keys ['x', 'y']
        """
        x_range = np.arange(-np.pi, np.pi, (2*np.pi)/n_samples)

        x1 = np.cos(x_range) + normal(0, noise, n_samples)
        y1 = np.sin(x_range) + normal(0, noise, n_samples)

        x2 = multiplier * np.cos(x_range) + normal(0, noise, n_samples)
        y2 = multiplier * np.sin(x_range) + normal(0, noise, n_samples)

        return {'x': [*np.concatenate([x1, x2]).round(3)],
                'y': [*np.concatenate([y1, y2]).round(3)]}

    def create_moons(self,
                     n_samples: int = N_SAMPLES,
                     noise: Union[int, float] = NOISE,
                     x_offset: Union[int, float] = X_OFFSET,
                     y_offset: Union[int, float] = Y_OFFSET) -> dict:
        """
        | Generate x and y coordinates for two crescent moons offset by
        |  x_offset and y_offset.
        |
        | ------------------------------------------------------------------
        | Parameters
        | ----------
        |  n_samples: int
        |    Number of samples
        |
        |  noise : int, float
        |    Noise to add to moons. 0 = perfect curves
        |
        |  x_offset: int, float
        |    Offset in x dimension. Large values move moons apart,
        |    small move moons closer
        |
        |  y_offset: int, float
        |    Offset in y dimension. Positive values move moons apart,
        |    negative move moons closer
        |
        |
        | Returns
        | -------
        |   dict with keys ['x', 'y']
        """
        x_range = np.arange(-np.pi, np.pi, (2*np.pi)/n_samples)
        half_n = int(n_samples/2)

        # First moon
        x1 = np.cos(x_range[:half_n]) + normal(-x_offset, noise, half_n)
        y1 = np.sin(x_range[:half_n]) + normal(-y_offset, noise, half_n)

        # Second moon
        x2 = np.cos(x_range[half_n:]) + normal(x_offset, noise, half_n)
        y2 = np.sin(x_range[half_n:]) + normal(y_offset, noise, half_n)

        return {'x': [*np.concatenate([x1, x2]).round(3)],
                'y': [*np.concatenate([y1, y2]).round(3)]}


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

# # Chapter 3

# ### Our first TensorFlow graph

# In[69]:

import tensorflow as tf
tf.compat.v1.disable_eager_execution()

with tf.compat.v1.Session() as sess:
    a = tf.constant(5)
    b = tf.constant(2)
    c = tf.constant(3)
    d = tf.multiply(a,b)
    e = tf.add(c,b)
    f = tf.subtract(d,e)

    outs = sess.run(f)
    sess.close()
    print("outs = {}".format(outs))

# ### Constructing and managing our graph

# In[70]:

import tensorflow as tf
print(tf.compat.v1.get_default_graph())
g = tf.Graph()
print(g)

# In[71]:

a = tf.compat.v1.constant(5)
print(a.graph is g)
print(a.graph is tf.compat.v1.get_default_graph())

# In[72]:

g1 = tf.compat.v1.get_default_graph()
g2 = tf.Graph()

print(g1 is tf.compat.v1.get_default_graph())

with g2.as_default():
    print(g1 is tf.compat.v1.get_default_graph())

print(g1 is tf.compat.v1.get_default_graph())

# ### Fetches

# In[73]:

with tf.compat.v1.Session() as sess:
    fetches = [a,b,c,d,e,f]
    outs = sess.run(fetches)

print("outs = {}".format(outs))
print(type(outs[0]))

# ### Nodes are operations, edges are Tensor objects

# In[74]:

c = tf.constant(4.0)
print(c)

# ### Data types

# In[75]:

c = tf.constant(4.0, dtype=tf.float64)
print(c)
print(c.dtype)

# In[76]:

x = tf.constant([1,2,3],name='x',dtype=tf.float32)
print(x.dtype)
x = tf.cast(x,tf.int64)
print(x.dtype)

# ### Tensor arrays and Shapes

# In[77]:

import numpy as np

c = tf.constant([[1,2,3],
                 [4,5,6]])
print("Python List input: {}".format(c.get_shape()))

c = tf.constant(np.array([
                 [[1,2,3],
                  [4,5,6]],

                 [[1,1,1],
                  [2,2,2]]
                 ]))

print("3d Numpy array input: {}".format(c.get_shape()))

# In[90]:

import matplotlib.pyplot as plt

sess = tf.compat.v1.InteractiveSession()

# === Noramal and Truncated normal distributions ===
mean = 0
std = 1
x_normal = tf.compat.v1.random_normal((1,50000),mean,std).eval()
x_truncated = tf.compat.v1.truncated_normal((1,50000),mean,std).eval()

# === Uniform distribution
minval = -2
maxval = 2
x_uniform = tf.compat.v1.random_uniform((1,50000),minval,maxval).eval()

sess.close()

def simpleaxis(ax):
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.get_xaxis().tick_bottom()
    ax.get_yaxis().tick_left()
#     ax.set_ylim([-1.1,1.1])
    ax.tick_params(axis='both', which='major', labelsize=15)

def get_axis_limits(ax, scale=.8):
    return ax.get_xlim()[1]*scale, ax.get_ylim()[1]*scale

f,axarr = plt.subplots(1,3,figsize=[15,4],sharey=True)
titles = ['Normal','Truncated Normal','Uniform']

print(x_normal.shape)
for i,x in enumerate([x_normal,x_truncated,x_uniform]):
    ax = axarr[i]
    ax.hist(x[0],bins=100,color='b',alpha=0.4)
    ax.set_title(titles[i],fontsize=20)
    ax.set_xlabel('Values',fontsize=20)
    ax.set_xlim([-5,5])
    ax.set_ylim([0,1800])

    simpleaxis(ax)

axarr[0].set_ylabel('Frequency',fontsize=20)
plt.suptitle('Initialized values',fontsize=30, y=1.15)

for ax,letter in zip(axarr,['A','B','C']):
    simpleaxis(ax)
    ax.annotate(letter, xy=get_axis_limits(ax),fontsize=35)

plt.tight_layout()

plt.savefig('histograms.png', bbox_inches='tight', format='png', dpi=200, pad_inches=0,transparent=True)

plt.show()

# In[91]:

sess = tf.compat.v1.InteractiveSession()
c = tf.linspace(0.0, 4.0, 5)
print("The content of 'c':\n {}\n".format(c.eval()))
sess.close()

# ### Matrix multiplication

# In[92]:

A = tf.constant([ [1,2,3],
                  [4,5,6] ])
print(a.get_shape())

x = tf.constant([1,0,1])
print(x.get_shape())

x = tf.expand_dims(x,1)
print(x.get_shape())

b = tf.matmul(A,x)

sess = tf.compat.v1.InteractiveSession()
print('matmul result:\n {}'.format(b.eval()))
sess.close()

# ### Names

# In[93]:

with tf.Graph().as_default():
    c1 = tf.constant(4,dtype=tf.float64,name='c')
    c2 = tf.constant(4,dtype=tf.int32,name='c')
print(c1.name)
print(c2.name)

# ### Name scopes

# In[94]:

with tf.Graph().as_default():
    c1 = tf.constant(4,dtype=tf.float64,name='c')
    with tf.name_scope("prefix_name"):
        c2 = tf.constant(4,dtype=tf.int32,name='c')
        c3 = tf.constant(4,dtype=tf.float64,name='c')

print(c1.name)
print(c2.name)
print(c3.name)

# ### Variables
#

# In[95]:

init_val = tf.compat.v1.random_normal((1,5),0,1)
var = tf.Variable(init_val, name='var')
print("pre run: \n{}".format(var))

init = tf.compat.v1.global_variables_initializer()
with tf.compat.v1.Session() as sess:
    sess.run(init)
    post_var = sess.run(var)

print("\npost run: \n{}".format(post_var))

# ### New variables are created each time

# In[96]:

init_val = tf.compat.v1.random_normal((1,5),0,1)
var = tf.Variable(init_val, name='var')
print("pre run: \n{}".format(var))

init = tf.compat.v1.global_variables_initializer()
with tf.compat.v1.Session() as sess:
    sess.run(init)
    post_var = sess.run(var)

print("\npost run: \n{}".format(post_var))

# ### Placeholders

# In[97]:

x_data = np.random.randn(5,10)
w_data = np.random.randn(10,1)

with tf.Graph().as_default():
    x = tf.compat.v1.placeholder(tf.float32,shape=(5,10))
    w = tf.compat.v1.placeholder(tf.float32,shape=(10,1))
    b = tf.fill((5,1),-1.)
    xw = tf.matmul(x,w)

    xwb = xw + b
    s = tf.reduce_max(xwb)
    with tf.compat.v1.Session() as sess:
        outs = sess.run(s,feed_dict={x: x_data,w: w_data})

print("outs = {}".format(outs))

# ### Example 1: Linear Regression

# In[98]:

# === Create data and simulate results =====
x_data = np.random.randn(2000,3)
w_real = [0.3,0.5,0.1]
b_real = -0.2

noise = np.random.randn(1,2000)*0.1
y_data = np.matmul(w_real,x_data.T) + b_real + noise

# In[99]:

NUM_STEPS = 10

g = tf.Graph()
wb_ = []
with g.as_default():
    x = tf.compat.v1.placeholder(tf.float32,shape=[None,3])
    y_true = tf.compat.v1.placeholder(tf.float32,shape=None)

    with tf.name_scope('inference') as scope:
        w = tf.Variable([[0,0,0]],dtype=tf.float32,name='weights')
        b = tf.Variable(0,dtype=tf.float32,name='bias')
        y_pred = tf.matmul(w,tf.transpose(x)) + b

    with tf.name_scope('loss') as scope:
        loss = tf.reduce_mean(tf.square(y_true-y_pred))

    with tf.name_scope('train') as scope:
        learning_rate = 0.5
        optimizer = tf.compat.v1.train.GradientDescentOptimizer(learning_rate)
        train = optimizer.minimize(loss)

    # Before starting, initialize the variables.  We will 'run' this first.
    init = tf.compat.v1.global_variables_initializer()
    with tf.compat.v1.Session() as sess:
        sess.run(init)
        for step in range(NUM_STEPS):
            sess.run(train,{x: x_data, y_true: y_data})
            if (step % 5 == 0):
                print(step, sess.run([w,b]))
                wb_.append(sess.run([w,b]))

        print(10, sess.run([w,b]))

# ### Example 2: Logistic Regression
#

# In[100]:

N = 20000

def sigmoid(x):
    return 1 / (1 + np.exp(-x))
# === Create data and simulate results =====
x_data = np.random.randn(N,3)
w_real = [0.3,0.5,0.1]
b_real = -0.2
wxb = np.matmul(w_real,x_data.T) + b_real

y_data_pre_noise = sigmoid(wxb)
y_data = np.random.binomial(1,y_data_pre_noise)

# In[101]:

NUM_STEPS = 50

g = tf.Graph()
wb_ = []
with g.as_default():
    x = tf.compat.v1.placeholder(tf.float32,shape=[None,3])
    y_true = tf.compat.v1.placeholder(tf.float32,shape=None)

    with tf.name_scope('inference') as scope:
        w = tf.Variable([[0,0,0]],dtype=tf.float32,name='weights')
        b = tf.Variable(0,dtype=tf.float32,name='bias')
        y_pred = tf.matmul(w,tf.transpose(x)) + b

    with tf.name_scope('loss') as scope:
        loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=y_true,logits=y_pred)
        loss = tf.reduce_mean(loss)

    with tf.name_scope('train') as scope:
        learning_rate = 0.5
        optimizer = tf.compat.v1.train.GradientDescentOptimizer(learning_rate)
        train = optimizer.minimize(loss)

    # Before starting, initialize the variables.  We will 'run' this first.
    init = tf.compat.v1.global_variables_initializer()
    with tf.compat.v1.Session() as sess:
        sess.run(init)
        for step in range(NUM_STEPS):
            sess.run(train,{x: x_data, y_true: y_data})
            if (step % 5 == 0):
                print(step, sess.run([w,b]))
                wb_.append(sess.run([w,b]))

        print(50, sess.run([w,b]))

# In[ ]:

# In[ ]:


import numpy as np
import matplotlib.pyplot as plt
from tensorFiles import *
from pcaF import *
from plottingFunctions import *
from classifier import *

#==== MNIST data ====
M1,Mtest = tnsrFile2numpy('data/mnist/mnist_train_images.npz'), tnsrFile2numpy('data/mnist/mnist_test_images.npz')
M3,L1 = tnsrFile2numpy('data/mnist/mnist_validate_images.npz'), tnsrFile2numpy('data/mnist/mnist_train_labels.npz')
Ltest,L3 = tnsrFile2numpy('data/mnist/mnist_test_labels.npz'),    tnsrFile2numpy('data/mnist/mnist_validate_labels.npz')
Mtrain, Ltrain = np.array(list(M1) + list(M3)), np.array(list(L1)+list(L3)) # 60K training images
ltrain,ltest = [],[]
for vbin in Ltrain: ltrain.append(np.argmax(vbin))
for vbin in Ltest: ltest.append(np.argmax(vbin))
ltest,ltrain = np.array(ltest), np.array(ltrain)
print('Training set', Mtrain.shape, Ltrain.shape, len(ltrain), 'Validation set', Mtest.shape, Ltest.shape, len(ltest))

#==== PCA ====
nEv = 100
useReducedDims = True
Ured,S,P = pca(Mtrain, t=True, nv=nEv)
if useReducedDims:
	Mtrain = np.matmul(Mtrain, Ured)
	Mtest = np.matmul(Mtest, Ured)
	print('TRAIN matrix after PC projection (dim reduce)', Mtrain.shape)

#==== some training and validation sets ====
train_M01, train_l01     = selectDigits([0,1],   Mtrain, ltrain)
train_M12, train_l12     = selectDigits([1,2],   Mtrain, ltrain)
train_M89, train_l89     = selectDigits([8,9],   Mtrain, ltrain)
train_M015,train_l015    = selectDigits([0,1,5], Mtrain, ltrain)
train_M0157,train_l0157  = selectDigits([0,1,5,7], Mtrain, ltrain)
val_M12,    val_l12      = selectDigits([1,2],   Mtest, ltest)
val_M015,   val_l015     = selectDigits([0,1,5], Mtest, ltest)
[train_M12, train_l12]   = randperm([train_M12,  train_l12]) # randomize input order
[train_M89, train_l89]   = randperm([train_M89,  train_l89])
[train_M015,train_l015]  = randperm([train_M015, train_l015])
[train_M0157,train_l0157]= randperm([train_M0157, train_l0157])
[val_M12,   val_l12]     = randperm([val_M12,    val_l12])
[val_M015,  val_l015]    = randperm([val_M015,   val_l015])

#==== LDA ====
LD01 =  lda(train_M01,  train_l01)            # train LDA 2 digits, easy to separate
LD12 =  lda(train_M12,  train_l12)            # train LDA 2 digits
LD89 =  lda(train_M89,  train_l89)            # train LDA 2 digits, hard to separate
LD015 = lda(train_M015, train_l015)           # train LDA 3 digits
LD0157 = lda(train_M0157, train_l0157)        # train LDA 4 digits
LDall = lda(Mtrain, ltrain)                   # train all 10 digits
pred12lda  = LD12.predict(val_M12)            # PREDICT (classify, 2)
pred1015da = LD015.predict(val_M015)          # PREDICT (classify, 3)
predAllLda = LDall.predict(Mtest)             # PREDICT (classify, all 10)
F3l, fLab3l = confusion(pred1015da, val_l015) # Analyze error/misclassification
F10l, fLab10l = confusion(predAllLda, ltest)  # Analyze error/misclassification

#==== SVM ====
SVM12 =  svm(train_M12,  train_l12)           # train SVM 2 digits
SVM015 = svm(train_M015, train_l015)          # train SVM 3 digits
SVM = svm(Mtrain, ltrain)                     # train all 10 digits
predl12svm = SVM12.predict(val_M12)
predl015svm = SVM015.predict(val_M015)
predAllSvm = SVM.predict(Mtest)
F10s, fLab10s = confusion(predAllSvm, ltest)  # Analyze error/misclassification

#==== DTREE ====
DT12 = dtree(train_M12,  train_l12)
pred12dtree = DT12.predict(val_M12)

#==== output ====
plotSV(S)
plotImageMatrix([Ured[:,0],Ured[:,1],Ured[:,2],Ured[:,3],Ured[:,4],Ured[:,5]],2,3) # first 6 eigendigits
plot3(P[:,0][0:1000], P[:,1][0:1000], P[:,2][0:1000], c=ltrain[0:1000], ttl='All Digits projected on PC 1,2,3', cbar=True, fname='')
plot3(P[:,1][0:1000], P[:,2][0:1000], P[:,3][0:1000], c=ltrain[0:1000], ttl='All Digits projected on PC 2,3,4', cbar=True, fname='')
Rs = np.matmul(train_M12[0:12], Ured.T) #- from PCA compressed space back to original dimensions
plotImageMatrix(Rs,3,4)
Rs = np.matmul(train_M89[0:12], Ured.T) #- from PCA compressed space back to original dimensions
plotImageMatrix(Rs,3,4)
RsAll = np.matmul(P, Ured.T)
plotImageMatrix(RsAll[0:12],3,4)
plotLDA(LD01.transform(train_M01), train_l01, ttl='LDA of digits 0 & 1')
plotLDA(LD89.transform(train_M89), train_l12, ttl='LDA of digits 8 & 9')
plotLDA(LD015.transform(train_M015), train_l015, ttl='LDA of digits 0,1,5')
plotLDA(LD0157.transform(train_M0157), train_l0157, ttl='LDA of digits 0,1,5,7')
print('train 1,2', train_M12.shape, 'val 1,2', val_M12.shape)
print('2 digit prediction')
for k in range(10): print('\tpred LDA', pred12lda[k],  'pred SVM', predl12svm[k], 'D Tree', pred12dtree[k], 'true', val_l12[k])
print('3 digit prediction')
for k in range(20): print('\tpred LDA', pred1015da[k], 'pred SVM', predl015svm[k], 'true', val_l015[k])
print('10 digit prediction')
for k in range(20): print('\tpred LDA', predAllLda[k], 'pred SVM', predAllSvm[k], 'true', ltest[k])

print('---- 3 digits LDA ----')
print('label indices', fLab3l)
for r in F3l:
	print(r)
correct = np.trace(F3l)
wrong = np.sum(F3l) - correct
print('Correct', correct, 'wrong', wrong)

print('---- 10 digits LDA ----')
correct = np.trace(F10l)
wrong = np.sum(F10l) - correct
for r in F10l:
	print(r)
print('Correct', correct, 'wrong', wrong)

print('---- 10 digits SVM ----')
correct = np.trace(F10s)
wrong = np.sum(F10s) - correct
for r in F10s:
	print(r)
print('Correct', correct, 'wrong', wrong)


""" Nearest-neighbor analysis.

Author:

    C.M. Gosmeyer

Date:

    Mar 2018

References:

    "Introduction to Statistical Problem Solving in Geography",
    J.C. McGrew, Jr., A.J. Lembo, Jr., C.B. Monroe

"""

import numpy as np

class NearestNeighbor(object):
    """ Determine whether a random (Poisson) process has generated a point
    pattern.

    R = 2.149 (perfectly dispersed)
    R = 1.0 (random)
    R = 0.0 (perfectly clustered)

    where

    R = MeanNNDistance /  MeanPerfectlyRandomNNDistance

    Requirements
    ------------
    1. Randaom sample of points from a population.
    2. Sample points are independently selected.

    Null Hypthothesis
    -----------------
    mean NN distance = mean perfectly random NN distance (point pattern
        is random)

    Test Statistic
    --------------

        Z_n = (MeanNNDistance - MeanPerfectlyRandomNNDistance) / StandardErrorNN

        where

        StandardErrorNN = 0.26136 / sqrt( NumberPoints * Density )
    """
    def __init__(self, area, x_pos=[], y_pos=[], NND=None, n=None):
        """
        Parameters
        ----------
        area : int
            The area of test region.
        x_pos : array
            The x-positions of points.
        y_pos : array
            The y-positions of points.
        NND : float
            Nearest neighbor density, if know already.
        n : int
            Number of points. Only need to fill out if already know NND.
        """
        if NND == None:
            self.x_pos = np.asarray(x_pos)
            self.y_pos = np.asarray(y_pos)
            self.n = len(x_pos)
            self.NND = self.mean_nearest_neighbor()

        else:
            self.n = n
            self.NND = NND

        self.area = float(area)
        self.density = self.n / self.area
        self.NND_R = 1. / (2. * np.sqrt(self.density))  # Perfectly random sample
        self.R = self.NND / self.NND_R

        self.sigma_NND = None
        self.standard_error()
        self.Z_n = None
        self.test_statistic()

        self.test_stat = self.Z_n

    def mean_nearest_neighbor(self):
        NND = []

        # Find nerarest neighbor distances.
        for i1, j1 in zip(self.x_pos, self.y_pos):
            a = [np.sqrt((i1-i2)**2 + (j1-j2)**2) for i2, j2 in zip(self.x_pos, self.y_pos)]
            a = np.asarray(a)
            NND.append((min(a[a != 0])))

        np.asarray(NND)
        return np.mean(NND)

    def standard_error(self):
        self.sigma_NND = 0.26136 / np.sqrt(self.n * self.density)

    def test_statistic(self):
        self.Z_n = (self.NND - self.NND_R) / self.sigma_NND


import numpy as np

def optimal_log_cutting(p, l):
	'''
	Analog Cutting Machinery - given an array p with cutting points p1, p2, ..., pk of
	an analog and l which is its length, computes the minimal cost based on the best
	cutting arrangement.
	'''
	p.insert(0, 0)
	p.insert(len(p), l)

	n = len(p)
	c = [[-1 for j in range(n)] for i in range(n)] # initializing the c matrix of size n+1 x n

	for i in range(0, n-1):
		c[i][i+1] = 0

	for k in range(1, n):
		for i in range(0, n-k-1):
			j = i+k+1
			c[i][j] = float('inf')

			m = i+1
			while m < j:
				aux = c[i][m] + c[m][j]
				if aux < c[i][j]:
					c[i][j] = aux
				m += 1

			c[i][j] = c[i][j] + p[j] - p[i]

	return c, c[0][n-1]

def main():
	#p = [25,50,75]
	#l = 100
	#p = [2,4,7]
	p = [4,5,7,8]
	l = 10
	print
	print '==============================================='
	print 'Analog cutting machinery'
	print '==============================================='
	print 'p = ', p
	print 'l = ', l
	c, cost = optimal_log_cutting(p, l)

	c = np.array(c)
	print 'c = '
	print c
	print 'The minimal cost is ', cost

if __name__=="__main__":
    main()


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

trial = 100000
a = 5.0
b = 5.0

init = 0.5
states = [init]

for i in range(trial):
    s = states[-1]
    # sample from uniform(0.0, 1.0)
    y = random.uniform(0.0, 1.0)
    # accept the proposal with probability
    # min(1.0, (y^(a-1) * (1.0-y)^(b-1)) / (s^(a-1) * (1-s)^(b-1)) )
    p = min(1.0, (y**(a-1) * (1.0-y)**(b-1)) / (s**(a-1) * (1-s)**(b-1)))
    new = np.random.choice([s, y], p=[1-p, p])

    states.append(new)

plt.hist(states, 50, density=True)
plt.title('simulation results of beta distribution')
plt.savefig('beta_distribution.png', dpi=300)
plt.show()

means, variances = [], []
step = trial // 1000
states = np.array(states)
for i in range(1000):
    end = step * (i+1)
    mean = np.mean(states[:end])
    variance = np.var(states[:end])
    means.append(mean)
    variances.append(variance)
means = np.array(means)
variances = np.array(variances)

plt.figure(figsize=(12, 6))
plt.subplot(121)
m = a / (a + b)
plt.plot(range(1000), np.abs(m - means))
plt.title('convergence rate of mean')
plt.xlabel("iteration (* %d)"%(step))
plt.ylabel("approximation error")

plt.subplot(122)
v = a*b / ((a+b)**2 * (a+b+1))
plt.plot(range(1000), np.abs(v-variances))
plt.title("convergence rate of variance")
plt.xlabel("iteration (* %d)"%(step))

plt.ylabel("approximation error")
plt.savefig("beta_distribution_convergence_rate.png", dpi=300)
plt.show()