# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.13.7
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# %% [markdown]
# # Erzeugen von NumPy Arrays

# %% pycharm={"name": "#%%\n"}
import numpy as np

# %% [markdown]
# Erzeugen Sie folgende NumPy Arrays:

# %% [markdown]
# ```python
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# ```

# %% pycharm={"name": "#%%\n"}
np.arange(10)

# %% [markdown]
# ```python
# array([0.  , 1.25, 2.5 , 3.75, 5.  ])
# ```

# %% pycharm={"name": "#%%\n"}
np.linspace(0,5,5)

# %% [markdown]
# ```python
# array([ 1,  3, 12, 92])
# ```

# %% pycharm={"name": "#%%\n"}
np.array([1, 3, 12, 92])

# %% [markdown]
# ```python
# array([[1, 2, 3, 4],
#        [5, 6, 7, 8]])
# ```

# %% pycharm={"name": "#%%\n"}
np.array([[1, 2, 3, 4], [5, 6, 7, 8]])

# %%
np.array([np.arange(1, 5), np.arange(5, 9)])

# %%
np.arange(1, 9).reshape((2, 4))

# %% [markdown]
# ```python
# array([[[0, 2, 4, 6],
#         [1, 3, 5, 7]],
#
#        [[1, 2, 3, 4],
#         [5, 6, 7, 8]],
#
#        [[9, 8, 7, 6],
#         [5, 4, 3, 2]]])
# ```

# %% pycharm={"name": "#%%\n"}
np.array([[[0, 2, 4, 6], [1, 3, 5, 7]],
          [[1, 2, 3, 4], [5, 6, 7, 8]],
          [[9, 8, 7, 6], [5, 4, 3, 2]]])

# %%
np.array([[range(0, 7, 2), range(1, 8, 2)],
          [range(1, 5), range(5, 9)],
          [range(9, 5, -1), range(5, 1, -1)]])

# %% [markdown]
# Ein $2\times 8$ Array, das gleichverteilte Zufallszahlen in $[0, 1)$ enthält.

# %% pycharm={"name": "#%%\n"}
np.random.rand(2, 8)

# %% [markdown]
# ```python
# array([[1., 0., 0.],
#        [0., 1., 0.],
#        [0., 0., 1.]])
# ```

# %% pycharm={"name": "#%%\n"}
np.eye(3)

# %% [markdown]
# Einen Vektor der Länge 5, der standard-normalverteilte Zahlen enthält.

# %% pycharm={"name": "#%%\n"}
np.random.standard_normal(5)

# %% [markdown]
# Ein $3 \times 4$ Array, das normalverteilte Zahlen mit Mittelwert $5$ und Standardabweichung $0.5$ enthält.

# %% pycharm={"name": "#%%\n"}
np.random.normal(5, 0.5, (3, 4))

# %% [markdown] pycharm={"name": "#%%\n"}
# ## Gleichungssysteme
#
# Lösen Sie folgendes Gleichungssystem:
#
# $x_1 - x_2 + 2x_3 = 6$
#
# $2x_1 + 3x_2 + 2x_3 = 8$
#
# $3x_1 + 2x_2 + x_3 = 8$

# %%
a = np.array([[1., -1., 2.],
              [2.,  3., 2.],
              [3.,  2., 1.]])
b = np.array([6., 8., 8.])

# %%
import scipy.linalg
lu = scipy.linalg.lu_factor(a)

# %%
x = scipy.linalg.lu_solve(lu, b)

# %%
x

# %%
a.dot(x)

# %% [markdown]
# Einfacher:

# %%
np.linalg.solve(a, b)

# %% [markdown] pycharm={"name": "#%% md\n"}
# # Erzeugen von NumPy Arrays 2
#
# Erzeugen Sie das folgende NumPy Array:
#
# ```python
# array([[ 0,  2,  4,  6],
#        [ 8, 10, 12, 14],
#        [16, 18, 20, 22],
#        [24, 26, 28, 30],
#        [32, 34, 36, 38]])
# ```

# %% pycharm={"name": "#%%\n"}
np.arange(0, 40, 2).reshape(5, 4)

# %% [markdown] pycharm={"name": "#%% md\n"}
# Erzeugen Sie das folgende NumPy Array:
#
# ```python
# array([[10, 19, 28, 37, 46, 55],
#        [13, 22, 31, 40, 49, 58],
#        [16, 25, 34, 43, 52, 61]])
# ```

# %% pycharm={"name": "#%%\n"}
np.arange(10, 64, 3).reshape(3, 6, order="F")

# %%
np.arange(10, 64, 3).reshape((6, 3)).T

# %% [markdown]
# # Regression mit KNN
#
# Sampeln Sie 150 Werte der Funktion $cos(x) + 0.2x$ im Intervall $[0, 12]$, mit normalverteiltem Fehler mit `size`$=0.5$.  Führen Sie eine Regression mit dem KNN-Verfahren durch. Verwenden Sie dabei sowohl 'uniform' als auch 'distance' als `weight` und experimentieren Sie mit verschiedenen Werten von $k$. Plotten Sie die Regressionskurve und berechnen Sie den Mean Square Error.

# %% slideshow={"slide_type": "subslide"}
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error

# %% slideshow={"slide_type": "-"}
np.random.seed(12)
NUM_SAMPLES = 50
MAX_X = 12
RANDOM_SCALE=0.5

# %% slideshow={"slide_type": "subslide"}
X = np.sort(MAX_X * np.random.random(NUM_SAMPLES))
X = X.reshape(-1, 1)
X[:3]

# %%
def fun(x):
    return np.cos(x) + 0.2 * x

# %% slideshow={"slide_type": "subslide"}
y = fun(X) + np.random.normal(size=X.shape, scale=RANDOM_SCALE)
y = y.reshape(-1)
y[:3]

# %%
n_neighbors = 5
knn = KNeighborsRegressor(n_neighbors, weights='uniform')
knn.fit(X, y)

# %% slideshow={"slide_type": "subslide"}
Xs = np.linspace(0, MAX_X, 500).reshape(-1, 1)
ys = knn.predict(Xs)
ys[:3]

# %% slideshow={"slide_type": "subslide"}
true_ys = fun(Xs)
true_ys[:3]

# %%
mean_squared_error(true_ys, ys)

# %% slideshow={"slide_type": "subslide"}
def plot_prediction(ys):
    plt.figure(figsize=(15,6))
    plt.scatter(X, y, color='orange', label='samples')
    plt.plot(Xs, ys, color='blue', label='predictions')
    plt.plot(Xs, true_ys, color='goldenrod', label='true_values')
    plt.legend()
plot_prediction(ys);

# %%
knn_dist = KNeighborsRegressor(n_neighbors, weights='distance')
knn_dist.fit(X, y)

# %%
Xs_dist = np.linspace(0, MAX_X, 500).reshape(-1, 1)
ys_dist = knn_dist.predict(Xs_dist)
ys_dist[:3]

# %%
mean_squared_error(true_ys, ys_dist)

# %%
plt.figure(figsize=(15,6))
plt.scatter(X, y, color='orange', label='samples')
plt.plot(Xs_dist, ys_dist, color='blue', label='predictions')
plt.legend()

# %% [markdown] slideshow={"slide_type": "slide"}
# # Bilderkennung mit KNNs
#
# Da der MNIST-Datensatz für viele moderne Algorithmen zu einfach ist, wurde der Fashion-MNIST entwickelt, der die gleiche Struktur wie MNIST hat (60000 Trainingsbilder und 10000 Testbilder der Größe $32 \times 32$ Pixel in Graustufen. Im Gegensatz zu MNIST sind bei Fashion-MNIST aber 10 Kategorien von Kleidungsstücken zu Klassifizieren.
#
# Fashion MNIST ist in Tensorflow als `tensorflow.keras.datasets.fashion_mnist` verfügbar.
#
# Verwenden Sie KNN um einen Klassifizierer für Fashion MNIST zu erstellen. Evaluieren Sie Accuracy, Precision und Recall. Wie sind die Werte im Vergleich zu MNIST?

# %% pycharm={"name": "#%%\n"}
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras.datasets import fashion_mnist
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score

# %% pycharm={"name": "#%%\n"}
(X_train_in, y_train), (X_test_in, y_test) = fashion_mnist.load_data()

# %% pycharm={"name": "#%%\n"} slideshow={"slide_type": "subslide"}
def plot_random_datapoint():
    sample = np.random.randint(0, X_train_in.shape[0])

    plt.figure(figsize = (10,10))
    mnist_img = X_train_in[sample]
    plt.imshow(mnist_img, cmap="Greys")

    # Get the `Axes` instance on the current figure
    ax = plt.gca()

    plt.tick_params(
        axis='both', which='major', bottom=True, left=True,
        labelbottom=False, labelleft=False)

    plt.tick_params(
        axis='both', which='minor', bottom=False, left=False,
        labelbottom=True, labelleft=True)

    ax.set_xticks(np.arange(-.5, 28, 1))
    ax.set_yticks(np.arange(-.5, 28, 1))

    ax.set_xticks(np.arange(0, 28, 1), minor=True);
    ax.set_xticklabels([str(i) for i in np.arange(0, 28, 1)], minor=True);
    ax.set_yticks(np.arange(0, 28, 1), minor=True);
    ax.set_yticklabels([str(i) for i in np.arange(0, 28, 1)], minor=True);

    ax.grid(color='black', linestyle='-', linewidth=1.5)
    plt.colorbar(fraction=0.046, pad=0.04, ticks=[0,32,64,96,128,160,192,224,255])

# %% pycharm={"name": "#%%\n"} slideshow={"slide_type": "subslide"}
plot_random_datapoint()

# %% pycharm={"name": "#%%\n"}
def preprocess_data(data):
    return data.reshape(-1, 28 * 28)

# %% pycharm={"name": "#%%\n"} slideshow={"slide_type": "subslide"}
X_train = preprocess_data(X_train_in)
X_test = preprocess_data(X_test_in)

# %% pycharm={"name": "#%%\n"} slideshow={"slide_type": "subslide"}
def shuffle(X, y):
    Xs = np.column_stack((X, y))
    np.random.shuffle(Xs)
    return Xs[:, :-1], Xs[:, -1]

# %%
X_train, y_train = shuffle(X_train, y_train)
X_test, y_test = shuffle(X_test, y_test)

# %%
X_train, y_train = X_train[:5000], y_train[:5000]
X_test, y_test = X_test[:1000], y_test[:1000]

# %% pycharm={"name": "#%%\n"}
X_train.shape, X_test.shape, y_train.shape, y_test.shape

# %%
n_neighbors = 5
knn = KNeighborsClassifier(n_neighbors)
knn.fit(X_train, y_train)

# %%
y_pred = knn.predict(X_test)
y_pred[:10], y_test[:10]

# %%
(accuracy_score(y_test, y_pred),
 precision_score(y_test, y_pred, average='macro'),
 recall_score(y_test, y_pred, average='macro'))

# %% [markdown]
# # Bildverarbeitung mit Ensembles und Entscheidungsbäumen
#
# Verwenden Sie verschiedene Ensemble-Techniken und Endscheidungsbaum-Varianten um das im letzten Workshop eingeführte Bilderkennungsproblem zu lösen.

# %%

# %% [markdown] pycharm={"name": "#%% md\n"}
# # Extrema
#
# Erzeugen Sie einen Vektor der Länge 100 mit Zufallswerten, die im Intervall
# $[10, 20)$ gleichverteilt sind.
#
# Berechnen Sie Minimum und Maximum der im Vektor
# enthaltenen Werte sowie die Positionen von Minimum und Maximum.

# %% pycharm={"name": "#%%\n"}
vec = np.random.random(100) * 10 + 10
vec[:10]

# %% pycharm={"name": "#%%\n"}
vec.min()

# %% pycharm={"name": "#%%\n"}
vec.argmin()

# %% pycharm={"name": "#%%\n"}
vec.max()

# %% pycharm={"name": "#%%\n"}
vec.argmax()

# %% [markdown] pycharm={"name": "#%% md\n"}
# # Mittelwert
#
# Erzeugen Sie ein $6 \times 8$ Array, das standardverteilten Zahlen mit
# Mittelwert $2$ und Standardabweichung $1$ enthält.
#
#

# %% pycharm={"name": "#%%\n"}
my_array = np.random.normal(2.0, 1.0, (6, 8))

# %% [markdown] pycharm={"name": "#%% md\n"}
# Berechnen Sie den Mittelwert aller darin vorkommenden Werte.
#

# %% pycharm={"name": "#%%\n"}
my_array.mean()

# %% [markdown] pycharm={"name": "#%% md\n"}
# Berechnen Sie die zeilen- und spaltenweisen Mittelwerte.

# %% pycharm={"name": "#%%\n"}
my_array.mean(axis=0)

# %% pycharm={"name": "#%%\n"}
my_array.mean(axis=1)

# %% [markdown] pycharm={"name": "#%% md\n"}
# Berechnen Sie den Mittelwert aller vorkommenden Werte ohne Verwendung der
# Methode `mean()`.

# %% pycharm={"name": "#%%\n"}
mean = my_array.sum() / my_array.size
mean

# %% [markdown] pycharm={"name": "#%% md\n"}
# Berechnen Sie die zeilen- und spaltenweisen Mittelwerte ohne Verwendung der
# Methode `mean()`.

# %% pycharm={"name": "#%%\n"}
my_array.sum(axis=0) / my_array.shape[0]

# %% pycharm={"name": "#%%\n"}
my_array.sum(axis=1) / my_array.shape[1]

# %% [markdown]
# # Roulette
#
# Analysieren Sie die Gewinnerwartung eines Spielers in folgender vereinfachter Form eines Roulettespiels mittels Monte Carlo Simulation:
#
# - Der Kessel ist in 36 Zahlen unterteilt.
# - Der Spieler wählt eine der Zahlen 1 bis 36 und wettet 1 Euro.
# - Fällt die Kugel auf die gewählte Zahl, so erhält der Spieler seinen Einsatz plus 35 Euro zurück.
# - Andernfalls verliert der Spieler seinen Einsatz.
#
# Schreiben Sie eine Version der Simulation mit `for`-Schleife in Python und testen Sie die Performance dieser Version vor und nach Kompilierung mit Numba. Schreiben Sie dann eine vektorisierte Version und testen Sie deren Performance in beiden Fällen.
#
# *Hinweise:*
# - Die NumPy Bibliothek enthält eine Funktion `np.random.randint(low, high, size=None)`, mit der Sie ein Array mit Shape `size` erzeugen können, das gleichverteilte Zufallszahlen zwischen `low` (inklusive) und `high` (exklusive) enthält.
# - Wird `np.random.randint()` mit nur zwei Argumenten aufgerufen, so gibt es eine einzige Zahl zurück.
# - Die NumPy Bibliothek enthält eine Funtion `np.random.binomial(n, p, size=None)`, mit der Sie binomialverteilte Zufallszahlen erzeugen können.

# %%
import numpy as np

# %%
def roulette1(n):
    # Wir können davon ausgehen, dass der Spieler immer auf die 1 wettet
    money_spent = 0
    money_won = 0
    for i in range(n):
        money_spent += 1
        if np.random.randint(1, 37) == 1:
            money_won += 36
    return (money_won - money_spent) / n

# %%
def test_roulette(roulette):
    np.random.seed(123)
    for n in [1000, 100_000, 1_000_000]:
        # %time print(f"Gewinnerwartung ist {100 * roulette(n):.1f}% ({n} Versuche)")

# %%
test_roulette(roulette1)

# %%
import numba
roulette1_nb = numba.jit(roulette1)

# %%
test_roulette(roulette1_nb)

# %%
def roulette2(n):
    money_spent = np.ones(n)
    money_won = np.random.binomial(1, 1.0/36.0, n) * 36
    return (money_won - money_spent).sum() / n

# %%
test_roulette(roulette2)

# %%
roulette2_nb = numba.jit(roulette2)

# %%
test_roulette(roulette2_nb)

# %%
def roulette3(n):
    money_spent = n
    money_won = np.random.binomial(n, 1.0/36.0) * 36
    return (money_won - money_spent) / n

# %%
test_roulette(roulette3)

# %%
roulette3_nb = numba.jit(roulette3)

# %%
test_roulette(roulette3_nb)

# %%
roulette3(100_000_000)

# %%


import numpy as np
from matplotlib import pyplot as plt

# Define error function
def error(vector_e):
  return abs(vector_e).mean()

# Load the data
N = 200
data = np.loadtxt("dataN%d.txt"%N)
sorted = True
s = "sorted" if sorted else ""
nmax = 71

# Some properties
color_t = "b"
color_p = "g"

# Sort or keep it unsorted
if sorted:
  data = data[np.argsort(data[:,0])]

# Split into training and prediction data
t = int(N*.7)
x_t = data[:t,0]
x_p = data[t:,0]
y_t = data[:t,1]
y_p = data[t:,1]

# Some helper variables for nicer plotting
x = np.linspace(data[:,0].min(), data[:,0].max(), 1000)

# Fit best several models and record training error and prediction error
n_range = range(1, nmax)
error_t = []
error_p = []
for n in n_range:
  fit_n = np.polyfit(x_t, y_t, n) # Obtains the best fitted polynomial of degree n
  pol_n = np.poly1d(fit_n) # Creates the polynomial with coefficients as in fit n
  plt.plot(x_t, y_t, 's'+color_t, alpha=0.5, label="Datos de Entrenamiento de Modelo")
  if t<N:
    plt.plot(x_p, y_p, 'o'+color_p, alpha=0.5, label="Datos para Testeo de Modelo")
  plt.plot(x, 5*np.cos(.25*np.pi*x), 'k-', lw=2.0, label="Relacion determinista")
  plt.plot(x, pol_n(x), 'r-', lw=2.0, label="Polinomio de grado %d"%n)
  plt.xlim([-2.5,2.5])
  plt.ylim([-5,10])
  plt.legend(numpoints = 1, loc="lower center")
  plt.savefig("images/data%sN%dpol%02d.png"%(s,N,n))
  plt.close()
  error_t.append( error(y_t - pol_n(x_t)) )
  error_p.append( error(y_p - pol_n(x_p)) )

# Plot the errors
plt.loglog(n_range, error_t, "-s"+color_t, lw=2.0, label="Training error")
if t<N:
  plt.loglog(n_range, error_p, "-o"+color_p, lw=2.0, label="Prediction error")
plt.legend(numpoints= 1)
plt.xlabel("Grado del polinomio")
plt.ylabel("Error")
plt.savefig("images/data%s_trainpred.png"%s)
plt.close()

# Save the error
data = np.array([ np.array(error_t), np.array(error_p)]).T
#print data
np.savetxt("images/data%serror_trainpred.txt"%s, data)


""" Solution for Project Euler's problem #2 """

import os
import time
from datetime import timedelta

import jax.numpy as jnp
from jax import jit

from gante_project_euler.math.sequences import fibonacci_up_to
from gante_project_euler.math.prime import is_multiple

@jit
def compute_solution(fibonacci_numbers):
    """ Auxiliary function to compute the solution to the problem (sum of even fibonacci numbers
    that do not exceed 4 million)

    NOTE: the original plan was to get an array with the even fibonacci numbers. However,
    the output of `fibonacci_numbers[is_even]` has unknown length at compile time, and thus
    JAX returns an error.
    There are ways to work around it, see https://github.com/google/jax/issues/2765
    """
    is_even = is_multiple(numbers=fibonacci_numbers, base=2)
    return jnp.sum(fibonacci_numbers * is_even)

def get_solution():
    """ Solves the problem and returns the answer.
    """
    fibonacci_up_to_4mil = jnp.asarray(fibonacci_up_to(value=4000000), dtype=jnp.int32)
    return compute_solution(fibonacci_up_to_4mil)

if __name__ == "__main__":
    os.environ["CUDA_VISIBLE_DEVICES"] = "0"
    start = time.time()
    solution = get_solution()
    end = time.time()
    print("Solution: {}".format(solution))
    print("Elapsed time (w/compile time):  {} (HH:MM:SS.us)".format(timedelta(seconds=end-start)))
    # The the code is compiled the first time it runs. This second run uses the cached compilation.
    start = time.time()
    _ = get_solution()
    end = time.time()
    print("Elapsed time (wo/compile time): {} (HH:MM:SS.us)".format(timedelta(seconds=end-start)))


import numpy as np
from numpy import cos, sin, pi

Ho = 5000  # ocean depth in meters
nx = 62    # number of gridpoints in x-direction
ny = 62    # number of gridpoints in y-direction
xo = 0     # origin in x,y for ocean domain
yo = 0     # (i.e. southwestern corner of ocean domain)
dx = 20    # grid spacing in x (km)
dy = 20    # grid spacing in y (km)
xeast  = xo + (nx-2)*dx   # eastern extent of ocean domain
ynorth = yo + (ny-2)*dy   # northern extent of ocean domain

# Flat bottom at z=-Ho
h = -Ho * np.ones((ny, nx))

# Walls (surrounding domain); generate bathymetry file
h[:, [0,-1]] = 0   # set ocean depth to zero at east and west walls
h[[0,-1], :] = 0   # set ocean depth to zero at south and north walls
# save as single-precision (float32) with big-endian byte ordering
h.astype('>f4').tofile('bathy.bin')

# Ocean domain extends from (xo,yo) to (xeast,ynorth)
# (i.e. the ocean spans nx-2, ny-2 grid cells)
# out-of-box-config: xo=yo=0, dx=dy=20 km, ocean extent (0,0)-(1200,1200) km
# model domain includes a land cell surrounding the ocean domain
# The full model domain cell centers are located at:
#    XC[0,:] = -10, +10, ..., +1210 (km)
#    YC[:,0] = -10, +10, ..., +1210 (km)
# and full model domain cell corners are located at:
#    XG[0,:] = -20, 0, ..., 1200 [, 1220] (km)
#    YG[:,0] = -20, 0, ..., 1200 [, 1220] (km)
# where the last value in brackets is not included in the MITgcm grid variable
# and reflects the eastern and northern edge of the model domain respectively.
# See section 2.11.4 of the MITgcm users manual.

# Zonal wind-stress, located at u-points (see section 2.11.4)
# here we non-dimensionalize: 0 at southern and western ocean boundary
# to 1.0 at eastern and northern ocean boundary
# for the purpose of applying sinusoidal-shaped wind stress curve
tauMax = 0.1  # wind stress maximum
x = (np.arange(nx)-1) / (nx-2)   # x-coordinate, located at XG points
y = (np.arange(ny)-.5) / (ny-2)  # y-coordinate, located at YC points
Y, X = np.meshgrid(y, x, indexing='ij')

tau = -tauMax * cos(Y*pi) # generate file for -cos(y) profile between 0-1200km
tau.astype('>f4').tofile('windx_cosy.bin')
tau = tauMax * sin(Y*pi) # generate file for +sin(y) profile between 0-1200km
tau.astype('>f4').tofile('windx_siny.bin')

# Meridional wind-stress, if desired, would be located at v-points (XC, YG)


import argparse
import math
import random
import numpy as np
from scipy.stats import norm
import seaborn as sns
import matplotlib.pyplot as plt
from .mh import MH

def main(args):

    std = math.sqrt(args.var)

    def p_eval(x):

        return 0.3 * math.exp(- 0.2 * x ** 2) + 0.7 * math.exp(- 0.2 * (x - 10) ** 2)

    def p_eval_real(x):

        return 0.3 * (1 / math.sqrt(math.pi * (1 / 0.2))) * math.exp(- 0.2 * x ** 2) + \
               0.7 * (1 / math.sqrt(math.pi * (1 / 0.2))) * math.exp(- 0.2 * (x - 10) ** 2)

    def q_eval(x1, x2):

        return norm.pdf(x1, loc=x2, scale=std)

    def q_sample(x):

        return random.gauss(x, std)

    # run metropolis-hastings
    mh = MH(p_eval, q_eval, q_sample, 0)

    samples = []

    for i in range(args.iters):
        samples.append(mh.step())

    # plot real distribution
    plt.title("{:d} iterations, {:.1f} variance".format(args.iters, args.var))
    colors = sns.color_palette()

    x = np.linspace(-10, 20, num=1000)
    y = [p_eval_real(xx) for xx in x]
    plt.plot(x, y, c=colors[0])

    # plot samples
    sns.distplot(samples, bins=30, hist=True, norm_hist=True, kde=False, color=colors[0])

    if args.save is not None:
        plt.savefig(args.save, figsize=(4, 3), dpi=60)

    if not args.no_show:
        plt.show()

if __name__ == "__main__":

    parser = argparse.ArgumentParser()

    parser.add_argument("--iters", type=int, default=5000)
    parser.add_argument("--var", type=float, default=100)
    parser.add_argument("--save")
    parser.add_argument("--no-show", default=False, action="store_true")

    parsed = parser.parse_args()
    main(parsed)


"""
Author : Achintya Gupta
Date Created : 22-09-2020
"""

"""
Problem Statement
------------------------------------------------
Q) Using names.txt (right click and 'Save Link/Target As...'), a 46K text file containing over five-thousand first names,
    begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, multiply this value by
    its alphabetical position in the list to obtain a name score.
    For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the
    938th name in the list. So, COLIN would obtain a score of 938 × 53 = 49714.
    What is the total of all the name scores in the file?
"""
from utils import timing_decorator, find_divisors
import numpy as np
import re

@timing_decorator
def calc_namescore(fnames):
    total_score = 0
    fnames = sorted(fnames)
    for nidx, name in enumerate(fnames):
        namescore = sum([int(ord(c)&31) for c in name])
        poscore = nidx+1
        total_score += poscore*namescore
    print(f'Total names score = ', total_score)

fnames = list(open('raw_data/p022_names.txt', 'r'))[0]
fnames = [re.sub('"','',k) for k in fnames.split(',')]
calc_namescore(fnames)


# solutions.py
"""Volume 2A: Wavelets. Solutions file."""

import numpy as np
from scipy.misc import imread
from scipy.signal import fftconvolve
from matplotlib import pyplot as plt
import pywt

# Problem 1
def dwt(X, L, H, n):
    """Compute the Discrete Wavelet Transform of X using filters L and H.

    Parameters:
        X (1D ndarray): The signal to be processed.
        L (1D ndarray): The low-pass filter.
        H (1D ndarray): The high-pass filter.
        n (int > 0):    Controls the degree of transformation.

    Returns:
        a list of the wavelet decomposition arrays.
    """
    i = 0                                    #Some initialization steps
    A = X
    D = []
    while i < n:
        D.append(fftconvolve(A, H)[1::2])    #High-pass filtering
        A = (fftconvolve(A, L)[1::2])        #Low-pass filtering
        i += 1
    D.append(A)
    D = D[::-1]
    return D

# Problem 1
def plot(X, L, H, n):
    """Plot the results of dwt with the given inputs.
    Your plot should be very similar to Figure 2.

    Parameters:
        X (1D ndarray): The signal to be processed.
        L (1D ndarray): The low-pass filter.
        H (1D ndarray): The high-pass filter.
        n (int > 0): Controls the degree of transformation.
    """

    coeffs = dwt(X, L, H, n)
    plt.subplot(n+2,1,1)
    plt.plot(X)
    for i in xrange(len(coeffs)):
        plt.subplot(n+2,1,i+2)
        plt.plot(coeffs[i])
    plt.show()

# Problem 2
def idwt(coeffs, L, H):
    """ Compute the Inverse Wavelet Transform using thw wavelet coefficients
    and filters L and H.

    Parameters:
        coeffs (list): a list of wavelet decomposition arrays.
        L (1D ndarray): The low-pass filter.
        H (1D ndarray): The high-pass filter.
    Returns:
        The reconstructed signal (as a 1D ndarray).
    """
    n = len(coeffs) - 1
    A = coeffs[0]
    coeffs = coeffs[1:]
    for i in xrange(n):
        D = coeffs[i]
        print len(D)
        up_A = np.zeros(2*A.size)
        up_A[::2] = A
        up_D = np.zeros(2*D.size)
        up_D[::2] = D
        print len(up_A),len(L),len(up_D),len(H)
        # now convolve and add, but discard last entry
        A = fftconvolve(up_A,L)[:-1] + fftconvolve(up_D,H)[:-1]
    return A

# Problem 3
def subbands(filename):
    """Plot the wavelet subbands for the image contained in filename.

    Parameters:
        filename (str): The name of the image file to be analyzed.
    Plots:
        Four subplots, of the wavelet subbands of the image.
    """
    image = image = imread(filename,True)
    # use the db4 wavelet with periodic extension
    lw = pywt.dwt2(image, 'db4', mode='per')
    plt.subplot(221)
    plt.imshow(np.abs(lw[0]), cmap=plt.cm.Greys_r, interpolation='none')
    plt.subplot(222)
    plt.imshow(np.abs(lw[1][0]), cmap=plt.cm.Greys_r, interpolation='none')
    plt.subplot(223)
    plt.imshow(np.abs(lw[1][1]), cmap=plt.cm.Greys_r, interpolation='none')
    plt.subplot(224)
    plt.imshow(np.abs(lw[1][2]), cmap=plt.cm.Greys_r, interpolation='none')
    plt.show()

# Problem 4
def clean_image(filename, show=False):
    """Clean the high-frequency noise out of the grayscale image contained
    in filename using the PyWavelets implementation of the 2-D Wavelet
    Transform.

    Parameters:
        filename (str): The name of the image file to be analyzed.

    Returns:
        The cleaned image (as an ndarray).
    """
    image = imread(filename,True)
    wavelet = pywt.Wavelet('haar')
    WaveletCoeffs = pywt.wavedec2(image,wavelet)
    new_image = pywt.waverec2(WaveletCoeffs[:-1], wavelet)

    if show:
        plt.subplot(121)
        plt.imshow(image, cmap='gray')  # visualize noisy image
        plt.subplot(122)
        plt.imshow(new_image, cmap='gray')  # visualize clean image
        plt.show()
    return new_image

#################### TEST DRIVER PREPARATION #########################

def test_prob1():
    """Tests Problem 1 as suggested in the lab."""
    L = np.ones(2)/np.sqrt(2)
    H = np.array([-1,1])/np.sqrt(2)
    n = 4
    domain = np.linspace(0,4*np.pi, 1024)
    noise = np.random.randn(1024)*.1
    X = np.sin(domain) + noise
    plot(X, L, H, n)

def test_prob2():
    """Tests Problem 2."""
    L = np.ones(2)/np.sqrt(2)
    H = np.array([-1,1])/np.sqrt(2)
    n = 4

    domain = np.linspace(0,4*np.pi, 1024)
    noise = np.random.randn(1024)*.1
    X = np.sin(domain) + noise
    coeffs = dwt(X, L, H, n)

    Ln = [1/np.sqrt(2),1/np.sqrt(2)]
    Hn = [1/np.sqrt(2),-1/np.sqrt(2)]
    A = idwt(coeffs, Ln, Hn)
    plt.plot(A)
    return np.allclose(X, A)


import numpy as np

def binaryStep(x):
    if x<0:
        return 0
    else:
        return 1

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

def sigmoid_p(x):                      # sigmoid prime
    return sigmoid(x)*(1-sigmoid(x))

def relu(x):                           # rectified linear unit
    if x>0:
        return x
    else:
        return 0

def softmax(x):
    z = np.exp(x)
    z_ = z/z.sum()
    return z_

def tanh(x):
    return (2/(1 + np.exp(-2*x))) -1

def elu(x, a):                          # exponential linear unit
    if x<0:
        return a*(np.exp(x)-1)
    else:
        return x


# -*- coding: utf-8 -*-
"""
Catalog of simplicial complexes

There are two main types: manifolds and examples related to graph
theory.

For manifolds, there are functions defining the `n`-sphere for any
`n`, the torus, `n`-dimensional real projective space for any `n`, the
complex projective plane, surfaces of arbitrary genus, and some other
manifolds, all as simplicial complexes.

Aside from surfaces, this file also provides functions for
constructing some other simplicial complexes: the simplicial complex
of not-`i`-connected graphs on `n` vertices, the matching complex on n
vertices, the chessboard complex for an `n` by `i` chessboard, and
others.  These provide examples of large simplicial complexes; for
example, ``simplicial_complexes.NotIConnectedGraphs(7,2)`` has over a
million simplices.

All of these examples are accessible by typing
``simplicial_complexes.NAME``, where ``NAME`` is the name of the example.

- :meth:`~sage.homology.examples.BarnetteSphere`
- :meth:`~sage.homology.examples.BrucknerGrunbaumSphere`
- :meth:`~sage.homology.examples.ChessboardComplex`
- :meth:`~sage.homology.examples.ComplexProjectivePlane`
- :meth:`~sage.homology.examples.DunceHat`
- :meth:`~sage.homology.examples.FareyMap`
- :meth:`~sage.homology.examples.K3Surface`
- :meth:`~sage.homology.examples.KleinBottle`
- :meth:`~sage.homology.examples.MatchingComplex`
- :meth:`~sage.homology.examples.MooreSpace`
- :meth:`~sage.homology.examples.NotIConnectedGraphs`
- :meth:`~sage.homology.examples.PoincareHomologyThreeSphere`
- :meth:`~sage.homology.examples.PseudoQuaternionicProjectivePlane`
- :meth:`~sage.homology.examples.RandomComplex`
- :meth:`~sage.homology.examples.RandomTwoSphere`
- :meth:`~sage.homology.examples.RealProjectivePlane`
- :meth:`~sage.homology.examples.RealProjectiveSpace`
- :meth:`~sage.homology.examples.RudinBall`
- :meth:`~sage.homology.examples.ShiftedComplex`
- :meth:`~sage.homology.examples.Simplex`
- :meth:`~sage.homology.examples.Sphere`
- :meth:`~sage.homology.examples.SumComplex`
- :meth:`~sage.homology.examples.SurfaceOfGenus`
- :meth:`~sage.homology.examples.Torus`
- :meth:`~sage.homology.examples.ZieglerBall`

You can also get a list by typing ``simplicial_complexes.`` and hitting the
TAB key.

EXAMPLES::

    sage: S = simplicial_complexes.Sphere(2) # the 2-sphere
    sage: S.homology()
    {0: 0, 1: 0, 2: Z}
    sage: simplicial_complexes.SurfaceOfGenus(3)
    Triangulation of an orientable surface of genus 3
    sage: M4 = simplicial_complexes.MooreSpace(4)
    sage: M4.homology()
    {0: 0, 1: C4, 2: 0}
    sage: simplicial_complexes.MatchingComplex(6).homology()
    {0: 0, 1: Z^16, 2: 0}
"""

from sage.homology.examples import (Sphere, Simplex, Torus, ProjectivePlane,
        RealProjectivePlane, KleinBottle, FareyMap, SurfaceOfGenus,
        MooreSpace,
        ComplexProjectivePlane, PseudoQuaternionicProjectivePlane,
        PoincareHomologyThreeSphere, RealProjectiveSpace, K3Surface,
        BarnetteSphere, BrucknerGrunbaumSphere, NotIConnectedGraphs,
        MatchingComplex, ChessboardComplex, RandomComplex, SumComplex,
        RandomTwoSphere, ShiftedComplex, RudinBall, ZieglerBall, DunceHat)


from doufo.tensor import Matrix, Vector, all_close
import numpy as np

def test_matmul_mv():
    result = Matrix([[1, 2], [3, 4], [5, 6]]) @ Vector([7, 8])
    assert isinstance(result, Vector)
    assert all_close(result, Vector([23, 53, 83]))

def test_matmul_vm():
    result = Vector([7, 8, 9]) @ Matrix([[1, 2], [3, 4], [5, 6]])
    assert isinstance(result, Vector)
    assert all_close(result, Vector([76, 100]))

def test_init():
    assert Matrix([[1, 2, 3], [4, 5, 6]]) == np.array([[1, 2, 3], [4, 5, 6]])

def test_fmap():
    def func(data):
        return data * 2

    assert Matrix([[1, 2, 3], [4, 5, 6]]).fmap(func) == np.array([[2, 4, 6], [8, 10, 12]])

def test_eye():
    res = Matrix.eye(3)

    assert res == np.eye(3)
    assert isinstance(res, Matrix)

def test_one_hot1():
    assert Matrix.one_hot([1, 2], [3, 4])==np.array([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]], dtype=float)

def test_one_hot2():
    try:
        Matrix.one_hot([1, 2], [1, 2])
    except Exception as e:
        assert e is not None

def test_one_hot3():
    assert all_close(Matrix.one_hot(1, 3), np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]], dtype=float))


import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)
y1 = np.power(2, x)
y2 = np.power(2, x - 1)

plt.plot(x, y1)
plt.plot(x, y2)

plt.grid(True)
plt.savefig('../../img/question_4_plots/e.png')


__all__ = ["shapeRectangleByCenter"]

import numpy as np
from LevelSetPy.Utilities.matlab_utils import *
from .rect_corners import shapeRectangleByCorners

def shapeRectangleByCenter(grid, center=None, widths=None):
    """
     shapeRectangleByCenter: implicit surface function for a (hyper)rectangle.

       data = shapeRectangleByCenter(grid, center, widths)

     Creates an implicit surface function (close to signed distance) for a
     coordinate axis aligned (hyper)rectangle specified by its center and
     widths in each dimension.

     Can be used to create intervals and slabs by choosing components of the
     widths as +Inf.

     The default parameters for shapeRectangleByCenter and
     shapeRectangleByCorners produce different rectangles.

     Inp.t Parameters:

       grid: Grid structure (see processGrid.m for details).

       center: Vector specifying center of rectangle.  May be a scalar, in
       which case the scalar is multiplied by a vector of ones of the
       appropriate length.  Defaults to 0 (eg centered at the origin).

       widths: Vector specifying (full) widths of each side of the rectangle.
       May be a scalar, in which case all dimensions have the same width.
       Defaults to 1.

     Output Parameters:

       data: Output data array (of size grid.size) containing the implicit
       surface function.

     Copyright 2004 Ian M. Mitchell (mitchell@cs.ubc.ca).
     This software is used, copied and distributed under the licensing
       agreement contained in the file LICENSE in the top directory of
       the distribution.

     Ian Mitchell, 6/23/04
     $Date: 2009-09-03 16:34:07 -0700 (Thu, 03 Sep 2009) $
     $Id: shapeRectangleByCenter.m 44 2009-09-03 23:34:07Z mitchell $

     Lekan Molu, Sept 07, 2021
    """
    #Default parameter values.
    if not np.any(center):
        center = zeros(grid.dim, 1)
    elif(numel(center) == 1):
        center *= ones(grid.dim, 1)

    if not np.any(widths):
        widths = ones(grid.dim, 1);
    elif numel(widths) == 1:
        widths *= ones(grid.dim, 1)

    #---------------------------------------------------------------------------
    # Implicit surface function calculation.
    #   This is basically the intersection (by max operator) of halfspaces.
    #   While each halfspace is generated by a signed distance function,
    #   the resulting intersection is not quite a signed distance function.

    # For the computation, we really want the lower and upper corners.
    lower = zeros(grid.dim, 1);
    upper = zeros(grid.dim, 1);
    for i in range(grid.dim):
        lower[i] = center[i] - 0.5 * widths[i]
        upper[i] = center[i] + 0.5 * widths[i]

    data = shapeRectangleByCorners(grid, lower, upper);

    #---------------------------------------------------------------------------
    # Warn the user if there is no sign change on the grid
    #  (ie there will be no implicit surface to visualize).
    if(np.all(data.flatten() < 0) or (np.all(data.flatten() > 0))):
        logger.warn(f'Implicit surface not visible because function has '
                'single sign on grid')

    return data


#!/usr/bin/env python
import numpy as np
import time
import argparse
from scipy.linalg import cho_factor, cho_solve

parser = argparse.ArgumentParser(prog="least_sqaures_cpu.py", description="Demo of fitting a line with least squares.")
parser.add_argument("--Nsamples", type=int, default=5000)
args = parser.parse_args()

np.random.seed(123456)

#First, build the "true" dataset with N=50 datapoints from a line model y=mx+b.
true_m, true_b = 0.5, -0.25
N = args.Nsamples
x = np.linspace(-5, 5, N)
y = true_m * x + true_b

#Introduce some noise with both measurement uncertainties
#   and non-trivial correlated errors.
yerr = 0.1 + 0.4 * np.random.rand(N)
yerr_hom = 0.4*np.ones(N)
hom_cov = np.diag(yerr_hom ** 2)
iid_cov = np.diag(yerr ** 2)
true_cov = 0.5 * np.exp(-0.5 * (x[:, None]-x[None, :])**2 / 1.3**2) + np.diag(yerr ** 2)
y_noised = np.random.multivariate_normal(y, true_cov)
#np.save('y_fake.npy', y)
#y = np.load('y_fake.npy')

#Linear algebra
A = np.vander(x, 2)
AT= A.T
C = iid_cov

t0 = time.time()
factor = cho_factor(C, overwrite_a=True)
S_inv = np.dot(AT, cho_solve(factor, A))
S = np.linalg.inv(S_inv)
ls_m, ls_b = np.dot(S, np.dot(AT, cho_solve(factor, y_noised)))
t1 = time.time()

net_time = t1-t0
print(" m: {:.2f} \n b: {:.2f} \n time: {}".format(ls_m, ls_b, net_time))


import numpy as np
import pandas as pd

from sm2.tsa.arima_process import arma_generate_sample

np.random.seed(12345)

# no constant
y_arma11 = arma_generate_sample([1., -.75],
                                [1., .35],
                                nsample=250)
y_arma14 = arma_generate_sample([1., -.75],
                                [1., .35, -.75, .1, .35],
                                nsample=250)
y_arma41 = arma_generate_sample([1., -.75, .25, .25, -.75],
                                [1., .35],
                                nsample=250)
y_arma22 = arma_generate_sample([1., -.75, .45],
                                [1., .35, -.9],
                                nsample=250)
y_arma50 = arma_generate_sample([1., -.75, .35, -.3, -.2, .1],
                                [1.],
                                nsample=250)
y_arma02 = arma_generate_sample([1.],
                                [1., .35, -.75],
                                nsample=250)

# constant
constant = 4.5
y_arma11c = constant + arma_generate_sample([1., -.75],
                                            [1., .35],
                                            nsample=250)
y_arma14c = constant + arma_generate_sample([1., -.75],
                                            [1., .35, -.75, .1, .35],
                                            nsample=250)
y_arma41c = constant + arma_generate_sample([1., -.75, .25, .25, -.75],
                                            [1., .35],
                                            nsample=250)
y_arma22c = constant + arma_generate_sample([1., -.75, .45],
                                            [1., .35, -.9],
                                            nsample=250)
y_arma50c = constant + arma_generate_sample([1., -.75, .35, -.3, -.2, .1],
                                            [1.],
                                            nsample=250)
y_arma02c = constant + arma_generate_sample([1.], [1., .35, -.75],
                                            nsample=250)

data = np.column_stack((y_arma11, y_arma14, y_arma41,
                        y_arma22, y_arma50, y_arma02,
                        y_arma11c, y_arma14c, y_arma41c,
                        y_arma22c, y_arma50c, y_arma02c))

df = pd.DataFrame(data,
                  columns=['y_arma11', 'y_arma14', 'y_arma41',
                           'y_arma22', 'y_arma50', 'y_arma02',
                           'y_arma11c', 'y_arma14c', 'y_arma41c',
                           'y_arma22c', 'y_arma50c', 'y_arma02c'])
df.to_csv('y_arma_data.csv', index=False)


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

def plot_marginal(m, v, zs=None, i=0, show=True):
    mu = m[i]
    variance = v[i]
    sigma = math.sqrt(variance)
    x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
    plt.plot(x, mlab.normpdf(x, mu, sigma), label='general marginal pdf')

    if zs:
        zs_i = zs[i]
        mu = zs_i[1]
        variance = zs_i[2]
        sigma = math.sqrt(variance)
        x = np.linspace(mu - 3*sigma, mu + 3*sigma, 100)
        plt.plot(x, mlab.normpdf(x, mu, sigma), label='specific soft pdf')
    plt.title('index={i}'.format(i=str(i)))
    plt.legend()
    if show:
        plt.show()
    return plt


from numpy import exp,sqrt

def simpson(f,a,b,n):
    h = (b-a)/n
    simp = 0.0
    for i in range(1,n):
        x = a + i*h
        if (i%2 == 1):
            simp = simp + 4.0*f(x)
        else:
            simp = simp + 2.0*f(x)
    simp = h*(f(a) + simp + f(b))/3.0
    return simp

def f(x):
    return x**2*exp(-x)/sqrt(1.0 + x**2)

a = -1.0
b = 1.0
n = 128
T = simpson(f,a,b,n)
print('T = %8.4f' % T)


import numpy as np
import sympy as s

def f_a(a, b, x):
    return a * np.sin(2 * x) + b * np.cos(2 * x) - (x / 4.0) * np.cos(2 * x)

def f_da(a, b):
    x = s.Symbol('x')
    f = a * s.sin(2 * x) + b * s.cos(2 * x) - (x / 4.0) * s.cos(2 * x)

    f_prime = f.diff(x)
    print(f_prime)

    l = s.lambdify(x, f_prime)
    return l

def f_b(a, b, x):
    return a * np.sin(2 * x) + b * np.cos(2 * x) - (x / 4.0) * np.sin(2 * x)

def f_db(a, b):
    x = s.Symbol('x')
    f = a * s.sin(2 * x) + b * s.cos(2 * x) - (x / 4.0) * s.sin(2 * x)

    f_prime = f.diff(x)
    print(f_prime)

    l = s.lambdify(x, f_prime)
    return l

def f_c(a, b, x):
    return a * np.sin(2 * x) + b * np.cos(2 * x)

def f_dc(a, b):
    x = s.Symbol('x')
    f = a * s.sin(2 * x) + b * s.cos(2 * x)

    f_prime = f.diff(x)
    print(f_prime)

    l = s.lambdify(x, f_prime)
    return l

def f_d(a, b, x):
    return (a + b * x) * np.e ** (-2 * x) - (x / 4.0) * np.cos(2 * x)

def f_dd(a, b):
    x = s.Symbol('x')
    f = (a + b * x) * s.exp(-2 * x) - (x / 4.0) * s.cos(2 * x)

    f_prime = f.diff(x)
    print(f_prime)

    l = s.lambdify(x, f_prime)
    return l

# print(f_a(2, 2, 0))
# # 1
# print(f_b(2, 2, 0))
# # 1
# print(f_c(2, 2, 0))
# # 1
# print(f_d(2, 2, 0))
# 1

print(f_da(1, 1)(0))
print(f_db(1, 1)(0))
print(f_dc(1, 1)(0))
print(f_dd(1, 1)(0))


import numpy as np

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

eigenvalue,eigenvector=np.linalg.eig(a)
print(eigenvalue)
print(eigenvector)
sort_ind=np.argsort(eigenvalue)
eigenvalue=eigenvalue[sort_ind]
eigenvector=eigenvector[:,sort_ind]
print('after sorting')
print(eigenvalue)
print(eigenvector)


#solving the heat equation using finite difference method
import numpy as np
from tqdm.notebook import tqdm_notebook
import shutil
import os

def heatBC(a,b,c,d,T,dx,dy):

    # 'N' refers to Neumann boundary condition where the heat flux is specified on the boundary
    # 'D' refers to Dirichlet boundary condition where the temperature is specified on the boundary

    #left
    if a[0]=='N':
        T[:, 0]=a[1]*dx+T[:, 1]
    elif a[0]=='D':
        T[:,0]=a[1]
    else:
        return('Please enter a valid BC type')

    if b[0]=='N':
        T[:, -1]=b[1]*dx+T[:, -2]
    elif b[0]=='D':
        T[:, -1]=b[1]
    else:
        return('Please enter a valid BC type')

    #top
    if c[0]=='N':
        T[-1,:]=c[1]*dy+T[-2, :]
    elif c[0]=='D':
        T[-1,:]=c[1]
    else:
        return('Please enter a valid BC type')

    #bottom
    if d[0]=='N':
        T[0,:]=d[1]*dy+T[1, :]
    elif d[0]=='D':
        T[0,:]=d[1]
    else:
        return('Please enter a valid BC type')

def heat_equation(T,dx,dy,alpha,dt,ds,nt,TBCs):
    Tn = np.empty_like(T)
    i=0
    for n in tqdm_notebook(range(nt)):
        Tn = T.copy()
        i=i+1
        T[1:-1,1:-1]=Tn[1:-1,1:-1]+(alpha*dt/dx**2)*(Tn[2:,1:-1]-2*Tn[1:-1,1:-1]+Tn[0:-2,1:-1])+(alpha*dt/dy**2)*(Tn[1:-1,2:]- 2 * Tn[1:-1, 1:-1] + Tn[1:-1, 0:-2])

        if ((n+1)%ds==0):
            np.savetxt('T'+str(i)+'.csv', T, delimiter=',')

        TLeft=TBCs[0]
        TRight=TBCs[1]
        TTop=TBCs[2]
        TBottom=TBCs[3]

        heatBC(TLeft,TRight,TTop,TBottom,T,dx,dy)
    return T

def clearResults():
    fileList=os.listdir('./')

    if os.path.isdir('./Results'):
        shutil.rmtree('./Results')
        os.mkdir('Results')
    else:
        os.mkdir('Results')

    for file in fileList:
        if file.endswith('.csv'):
            shutil.move(file,'Results/.')

"""def heatBC(a,b,c,d,T,dx,dy):
    #left
    if a[0]=='N':
        T[0,:]=a[1]*dy+T[1, :]
    elif a[0]=='D':
        T[0,:]=a[1]
    else:
        return('Please enter a valid BC type')

    if b[0]=='N':
        T[-1,:]=b[1]*dy+T[-2, :]
    elif b[0]=='D':
        T[-1,:]=b[1]
    else:
        return('Please enter a valid BC type')

    #top
    if c[0]=='N':
        T[:, -1]=c[1]*dx+T[:, -2]
    elif c[0]=='D':
        T[:, -1]=c[1]
    else:
        return('Please enter a valid BC type')

    #bottom
    if d[0]=='N':
        T[:, 0]=d[1]*dx+T[:, 1]
    elif d[0]=='D':
        T[:, 0]=d[1]
    else:
        return('Please enter a valid BC type')
"""


import scipy as sp
from scipy.linalg import norm, det

def QR(X):
    """Compute the Gram Schmidt of the column vectors in X

    The formula: x_k := x_k-<x_k, q_1>*q_1  (k=2,...,n)
    Then we normalize x_k"""

    #transpose so we are dealing with rows instead of columns
    #check types on X
    Q = X.T.copy()
    nrows, ncols = X.shape
    R = sp.zeros((nrows, ncols))

    for i in xrange(nrows):
        R[i,i] = norm(Q[i])
        Q[i] = Q[i]/R[i,i]
        for j in xrange(i+1,nrows):
            R[i,j] = Q[j].dot(Q[i])
            Q[j] = Q[j]-(R[i,j]*Q[i])

    return Q.T, R

def detQR(X):
    """Computes the determinant of X using the QR decomposition"""
    Q, R = QR(X)
    return la.det(Q)*la.det(R)

def eigvv(A, niter=50):
    Qlist = [A]
    x0 = sp.rand(A.shape[1])
    for i in range(niter):
        Q,R = QR(Qlist[-1])
        A = Q.T.dot(A.dot(Q))
        Qlist.append(A)

    eigvals = sp.diag(Qlist[-1])

    L = sp.eye(Q.shape[0])
    for qm in Qlist:
        L = sp.dot(L,qm)

    return eigvals, x0, L


import torch as th
import numpy as np
'''
prediction:
gradient computation:
loss computation:
parameter updates:
'''

'''All Manual with numpy'''
# f = w*x
# f = 2.x

X = np.array([1,2,3,4],dtype=np.float32)
Y = np.array([2,4,6,8],dtype=np.float32)

w = 0.0

def forward(x):
    return w*x
def loss(y,yp):
    return np.mean((y-yp)**2)
def gradient(x,y,yp):
    #MSE = 1/N*(wx-y)**2
    #dJ/dw = 1/N*2x*(w*x - y)
    return np.dot(2*x, yp-y).mean()

print(f'prediction before training f(5)= {forward(5):.3f}')

# training
lr = 0.01
n_iters = 100

for epoch in range(n_iters):
    # prediction
    y_pred = forward(X)

    # loss
    l = loss(Y,y_pred)

    #gradient
    dw = gradient(X,Y,y_pred)

    # Update weights
    w-=lr*dw
    if epoch%10==0:
        print(f'epoch {epoch+1}: w: {w:.8f}, loss = {l:.8f}')

print(f'Prediction after training: f(5) = {forward(5):.3f}')


import numpy as np
from nayzakflow.utils import _onehot

def MSE(y,yhat):
    a=np.square(yhat-y)
    a=np.sum(a)
    b= 1/(2*y.shape[1])
    return a*b

def _diff_MSE(y,yhat):
    return (yhat-y)

def MAE(y,yhat):
    return (1/(y.shape[1]))* np.sum(np.abs(yhat-y))

def _diff_MAE(y,yhat):
    diff= np.array((yhat-y)>0 , dtype="int")
    diff= ((1-diff)*-1)+diff

    return diff

def PerceptronCriteria(y,yhat):
    return (1/(y.shape[1]))* np.sum(np.maximum(0, -1*y*yhat))

def _diff_PerceptronCriteria(y,yhat):
    diff = np.array( (y*yhat)<=0, dtype="int" )
    diff*= (-1*y)
    return diff

def HingeLoss(y,yhat):
    return (1/(y.shape[1]))* np.sum(np.maximum(0, 1-y*yhat))

def _diff_HingeLoss(y,yhat):
    diff = np.array( (y*yhat)<=1, dtype="int" )
    diff*= (-1*y)
    return diff

def SigmoidLogLikelihood(y,yhat):
    return (1/(y.shape[1]))*np.sum(-1*np.log(np.abs((y/2)-0.5+yhat)))

def _diff_SigmoidLogLikelihood(y,yhat):
    return -1* y/ ( (((1+y)/2)*yhat) + (((1-y)/2)*(1-yhat))  )

def IdentityLogLikelihood(y,yhat):
    return (1/(y.shape[1]))*np.sum(np.log(1+np.exp(-1*y*yhat)))

def _diff_IdentityLogLikelihood(y,yhat):
    return (-1*y*np.exp(-1*y*yhat))/(1+np.exp(-1*y*yhat))

def SoftmaxLogLikelihood(y,yhat):
    onehotY= _onehot(y,yhat.shape[0])
    yhat_r = np.max(onehotY*yhat, axis=0,keepdims=True)
    return (1/(y.shape[1]))*-1*np.sum(np.log(yhat_r))

def SoftmaxLogLikelihood_OneHotEncoded(y,yhat):
    onehotY= y
    yhat_r = np.max(onehotY*yhat, axis=0,keepdims=True)
    return (1/(y.shape[1]))*-1*np.sum(np.log(yhat_r))

def MultiClassPerceptronCriteria(y,yhat):
    onehotY= _onehot(y,yhat.shape[0])
    yhat_r = np.max(onehotY*yhat, axis=0,keepdims=True)

    d= np.maximum(yhat-yhat_r,0)
    return (1/(y.shape[1]))*np.sum(np.max(d, axis=0))

def _diff_MultiClassPerceptronCriteria(y,yhat):
    onehotY= _onehot(y,yhat.shape[0])
    max_index=np.argmax(yhat,axis=0)
    temp=np.zeros_like(yhat)
    for i in range(temp.shape[1]): temp[max_index[i],i]=1
    return  temp+ (-1* onehotY)

def MultiClassHingeLoss(y,yhat):
    onehotY= _onehot(y,yhat.shape[0])
    yhat_r = np.max(onehotY*yhat, axis=0,keepdims=True)
    z=1+(yhat-yhat_r)
    z= (1-onehotY)*z
    d= np.maximum(z,0)
    return (1/(y.shape[1]))*np.sum(np.sum(d, axis=0))

def _diff_MultiClassHingeLoss(y,yhat):
    onehotY= _onehot(y,yhat.shape[0])
    yhat_r = np.max(onehotY*yhat, axis=0,keepdims=True)
    z=1+(yhat-yhat_r)
    z= (1-onehotY)*z
    d= np.maximum(z,0)
    de = np.sum(d>0,axis=0)
    out = np.zeros_like(yhat)
    out[d>0]=1
    out = out+ onehotY*(-de)
    return out

def MultiClassPerceptronCriteria_OneHotEncoded(y,yhat):
    onehotY= y
    yhat_r = np.max(onehotY*yhat, axis=0,keepdims=True)

    d= np.maximum(yhat_r-yhat,0)
    return (1/(y.shape[1]))*np.sum(np.max(d, axis=0))

def MultiClassHingeLoss_OneHotEncoded(y,yhat):
    onehotY= y
    yhat_r = np.max(onehotY*yhat, axis=0,keepdims=True)
    z=1+(yhat_r-yhat)
    z= (1-onehotY)*z
    d= np.maximum(z,0)
    return (1/(y.shape[1]))*np.sum(d)

def get_diffs():
    return {MSE:_diff_MSE , MAE:_diff_MAE, PerceptronCriteria:_diff_PerceptronCriteria, HingeLoss:_diff_HingeLoss,
            SigmoidLogLikelihood: _diff_SigmoidLogLikelihood , IdentityLogLikelihood:_diff_IdentityLogLikelihood,
            MultiClassPerceptronCriteria:_diff_MultiClassPerceptronCriteria , MultiClassHingeLoss:_diff_MultiClassHingeLoss}


import numpy as np
from itertools import product
from math import floor, sqrt, exp

def pres_value_per_year(payments, r, immediate=True):
    if immediate:
        s = [payments[k]/(1+r)**k for k in range(len(payments))]
        s = sum(s)
    else:
        s = [payments[k] / (1 + r) ** k for k in range(1, len(payments))]
        s = payments[0] + sum(s)
    s = round(s, 2)
    print("Present Value:", s)
    return s

def comp_int(money, r, n, quartely=False):
    if quartely:
        per_year = 4
        s = money * (1 + r/per_year) ** (per_year*n)
        print("Get {} after {} months".format(s, n))
    else:
        s = money*(1+r)**n
        print("Get {} after {} months".format(s, n))
    return s

def bound_perpetuity(A, r_lend, r_borrow):
    pv_borrow = A / r_borrow
    pv_lend = A / r_lend

    print("{} <= p <= {}".format(pv_borrow, pv_lend))
    return pv_borrow, pv_lend

def discount_rate(r, n, semi=False):
    if semi:
        d = 1 / (1 + r / 2) ** n
    else:
        d = 1 / (1 + r) ** n
    return d

def swap_int_rate(rates, years):
    num = 1 - discount_rate(rates[years-1], years)
    disc = [discount_rate(rates[i], i+1) for i in range(years)]
    den = sum(disc)

    return num/den

def futures_hedge_ratio(corr, needed_std, alt_std):
    cov = corr * needed_std * alt_std
    return cov/(needed_std*needed_std)

def options_binomial_model(S0, u, d, R, K, periods=1):
    S = [[]]*(periods+1)
    S[0] = [S0]
    changes = {
        'u': u,
        'd': d
    }

    for i in range(1, len(S)):
        S[i] = [0]*2**i
        j = 0
        changes_len = int(sqrt(len(S[i])))
        for p in product('ud' * changes_len, repeat=changes_len):
            for char in p:
                S[i][j] = S[i - 1][floor(j/2)] * changes[char]
                j += 1

    # Ax = b

    a = np.array([
        [S[1][0], R],
        [S[1][1], R]
    ])

    b = np.array([S[1][0]-K, 0])

    solution, resid, rank, s = np.linalg.lstsq(a, b)

    print("x: {}"
          "\ny: {}".format(round(solution[0], 3), round(solution[1], 3)))

    value = solution[0] * S0 + solution[1] * 1

    return value

def black_scholes_calibration(T, r, sigma, c, n):
    R = exp(r * T/n)
    u = exp(sigma * sqrt(T/n))
    d = 1/u
    q = (exp((r - c)*T/n) - d) / (u - d)

    return R, u, d, q


import numpy as np

def haversine(p1, p2):
    """
    Calculate the great circle distance between two points
    on the earth (specified in decimal degrees)

    All args must be of equal length.
    # Points in lat lon order
    """
    p1, p2 = map(np.radians, [p1, p2])

    dlat = p1[0] - p2[0]
    dlon = p1[1] - p2[1]

    a = np.sin(dlat/2.0)**2 + np.cos(p1[0]) * np.cos(p2[0]) * np.sin(dlon/2.0)**2

    c = 2 * np.arcsin(np.sqrt(a))
    dist = 6371000 * c # [m]
    return dist

def haversineForGrid(grid):
    """
    This function is used to obtain vertical and horizontal distances inside a grid
    :param grid:
    :return:
    """
    grid_rad = list(map(np.radians, grid))
    lat_rad = grid_rad[1]
    lon_rad = grid_rad[0]

    dlat = lat_rad[1:,:] - lat_rad[:-1,:]
    dlon = lon_rad[:,1:] - lon_rad[:,:-1]

    # We are creating horizontal and vertical distances
    out_dims = (2, grid[0].shape[0]-1,grid[0].shape[1]-1)
    output = np.zeros(out_dims)

    # Filling by cols
    for c_col in range(out_dims[2]):
        output[0,:,c_col] = np.sin(dlat[:,c_col]/2.0)**2
        output[1,:,c_col] = (np.cos(lat_rad[:,c_col+1]) * np.cos(lat_rad[:,c_col]) * np.sin(dlon[:,c_col]/2.0)**2)[:-1]

    c = 2 * np.arcsin(np.sqrt(output))
    dist = (6371000 * c)  # [m]
    return dist

if __name__ == "__main__":

    # ------ For two points
    # p1 = [0, 0]
    # p2 = [0, 1]
    # d = haversine(p1, p2)
    # print(F"Distance between {p1}(lat,lon) and {p2}(lat,lon) is {d:.3f} m")

    # ------ For grid
    lat = np.linspace(-10,10,21)
    lon = np.linspace(-20,20,41)
    grid = np.meshgrid(lon, lat)


from scipy import linalg as la
from scipy import sparse as spar
import scipy as sp

def Problem1(n):
    """Use linalg.toeplitz() and linalg.triu() to generate matrices of arbitrary size"""

    from scipy.linalg import triu

    ut = triu([[0]*i+[x for x in range(1,(n+1)-i)] for i in range(n)])
    toep = la.toeplitz([1.0/(i+1) for i in range(n)])

    return ut, toep

def Problem4(n):
    return la.toeplitz([2,-1]+[0]*(n-2), [2,-1]+[0]*(n-2))

def Problem5(n):
    diags = sp.array([[-1]*n, [2]*n, [-1]*n])
    return spar.spdiags(diags, [-1,0,1], n, n,format='csr')

def Problem6(n, sparse=False):

    b = sp.rand(n,1)

    if sparse is True:
        from scipy.sparse import linalg as sla
        A=Problem5(n)
        return sla.spsolve(A,b)
    else:
        A=Problem4(n)
        return la.solve(A,b)

def Problem7(n):
    A = Problem4(n)
    eig = la.eigvals(A).min()
    return eig*(n**2)


import numpy as np

"""
Turns vector into a diagonal matrix.
This is used especially for eigenvalue/vector calculations.
"""
def as_singular(sigma, rank=None):
    sigma_len = len(sigma)
    if rank is None:
        rank = sigma_len
    array = np.zeros((sigma_len, sigma_len))
    for i in range(rank):
        array[i][i] = sigma[i] if sigma[i] >= 0 else 0
    return array

def eig(A, output="all", k=1):
    e, U = np.linalg.eigh(A)
    if output == "all":
        return e, U
    elif output == "max":
        new_e = np.argsort(e)
        return new_e[:k], U[:,new_e[:k]]
    elif output == "min":
        new_e = np.argsort(-e)
        return new_e[:k], U[:,new_e[:k]]

def generalized_eig(A, B, output="all", k=1):
    # Calculate EVD of B
    e, U = np.linalg.eigh(B)
    E = as_singular(e ** -0.5)
    C = E @ U.T @ A @ U @ E

    # Calculating EVD of C
    return eig(C, output, k)


from numpy import matrix, array, arange
from numpy.linalg import solve

def regression_quadratic(data):
    n = len(data)
    sx = sum(x for [x,y] in data)
    sx2 = sum(x**2 for [x,y] in data)
    sx3 = sum(x**3 for [x,y] in data)
    sx4 = sum(x**4 for [x,y] in data)
    sy = sum(y for [x,y] in data)
    sxy = sum(x*y for [x,y] in data)
    sx2y = sum(x**2*y for [x,y] in data)

    A = matrix([
        [sx4,sx3,sx2],
        [sx3,sx2,sx],
        [sx2,sx,n]
    ])
    v = array([sx2y,sxy,sy])
    [a,b,c] = solve(A,v)
    return {
        "a": a, "b": b, "c": c,
        "f": lambda x: a*x**2+b*x+c
    }

def regression(data,X,step):
    import matplotlib.pyplot as plot

    t = regression_quadratic(data)

    print("a = {}".format(t["a"]))
    print("b = {}".format(t["b"]))
    print("c = {}".format(t["c"]))

    [x,y] = zip(*data)
    plot.plot(x,y,'o',color='black')

    x = arange(X[0],X[1],step)
    plot.plot(x,t["f"](x),color='black')

    plot.show()

data = [
    [0,2], [1,2.5], [1,3], [2,3.4],
    [2.5,4], [3,4.2], [4,4.2], [5,4.0]
]
regression(data,X=[0,6],step=0.1)


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

# The host will reveal a door that doesn't contain the prize
def get_non_prize_door(host, num_doors, player_choice):
  i = 1
  while (i == host or i== player_choice ):
    i = (i+1)%(num_doors)

  return i

# Have the player switch to the other unopened door
def switch_function(shown_door, num_doors, player_choice):
  i = 1
  while (i == shown_door or i== player_choice ):
    i = (i+1)%(num_doors)

  return i

# Play the game
def monty_hall_game(switch, num_tests):
  win_switch_cnt = 0
  win_no_switch_cnt = 0
  lose_switch_cnt = 0
  lose_no_switch_cnt = 0

  # Get the doors
  doors = [0,1,2]

  # Get the number of doors
  num_doors = len(doors)

  for i in range(0,num_tests):
    # Randomly choose the door with the wanted prize
    door_with_prize = random.randint(0, num_doors-1)

    # The host knows which door has the prize
    host = door_with_prize

    # The player initially chooses a random door that they believe has the prize
    player_choice = random.randint(0, num_doors-1)
    original_player_choice = player_choice
    shown_door = get_non_prize_door(host, num_doors, player_choice)

    if switch == True:
      player_choice = switch_function(shown_door,num_doors, player_choice)

    if player_choice == host and switch == False:
      # The player wins from not switching
      print('Player Wins (No Switch) - The player chose door: ', player_choice,' Original choice: ',original_player_choice ,', Door with prize:', door_with_prize, ', Shown Door: ',shown_door )
      win_no_switch_cnt = win_no_switch_cnt + 1

    elif player_choice == host and switch == True:
      # The player wins from switching
      print('Player Wins (Switch) - The player chose door: ', player_choice,' Original choice: ',original_player_choice , ', Door with prize:', door_with_prize, ', Shown Door: ',shown_door )
      win_switch_cnt = win_switch_cnt +1

    elif player_choice != host and switch == False:
      # The player loses from not switching
      print('Player Lost (No Switch) - The player chose door: ', player_choice,' Original choice: ',original_player_choice , ', Door with prize:', door_with_prize, ', Shown Door: ',shown_door )
      lose_no_switch_cnt = lose_no_switch_cnt + 1

    elif player_choice != host and switch == True:
      # The player loses from switching
      print('Player Lost (Switch) - The player chose door: ', player_choice,' Original choice: ',original_player_choice , ', Door with prize:', door_with_prize, ', Shown Door: ',shown_door )
      lose_switch_cnt = lose_switch_cnt + 1

  return win_no_switch_cnt,win_switch_cnt,lose_no_switch_cnt,lose_switch_cnt, num_tests

# Play the game
num_tests = []
win_percentage = []
switch = True

for i in range(1,100):
  num_tests.append(i)
  y = monty_hall_game(switch, i)
  win_percentage.append(y[1]/ y[4])

# Visually show the number of tests and the win percentage

# Expected winning porbability
x = np.linspace(0, 100, 100000)

@np.vectorize
def constant_function(xyz):
    return 0.66

plt.plot(x,constant_function(x), dashes=[6, 2], color = "red")

plt.plot( num_tests, win_percentage  )
plt.title('Monty Hall Problem')
plt.xlabel('Number of Tests',fontsize=18)
plt.ylabel('Win Percentage',fontsize=18)
plt.show()

print('The win percentage for test playing ', y[4], ' games is:' ,y[1]/ y[4])


# Based on http://d2l.ai/chapter_computer-vision/transposed-conv.html

import torch
from torch import nn
import numpy as np

def trans_conv(X, K):
    h, w = K.shape
    Y = torch.zeros((X.shape[0] + h - 1, X.shape[1] + w - 1))
    for i in range(X.shape[0]):
        for j in range(X.shape[1]):
            Y[i:i + h, j:j + w] += X[i, j] * K
    return Y

# Example from D2L fig 13.10.1
X = torch.tensor([[0., 1], [2, 3]])
K = torch.tensor([[0., 1], [2, 3]])
Y = trans_conv(X, K)
print(Y)

X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2)
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, bias=False)
tconv.weight.data = K
Y2 = tconv(X)
#print(Y2)
assert torch.allclose(Y, Y2)

'''
X, K = X.reshape(1, 1, 2, 2), K.reshape(1, 1, 2, 2)
tconv = nn.ConvTranspose2d(1, 1, kernel_size=2, padding = 1, bias=False)
tconv.weight.data = K
Y2 = tconv(X)
print('Y2', Y2)
'''

# Transposed Matrix multiplication

K = torch.tensor([[1,2], [3, 4]])

def kernel2matrix(K):
    k, W = torch.zeros(5), torch.zeros((4, 9))
    k[:2], k[3:5] = K[0, :], K[1, :]
    W[0, :5], W[1, 1:6], W[2, 3:8], W[3, 4:] = k, k, k, k
    return W

W = kernel2matrix(K)

X = torch.tensor([[0.0, 1], [2, 3]])
Y = trans_conv(X, K)
Y2 = torch.mv(W.T, X.reshape(-1)).reshape(3, 3)
assert torch.allclose(Y, Y2)

# Example from Geron fig 14.27

X = torch.ones((2,3))
K = torch.ones(3,3)
X, K = X.reshape(1, 1, 2, 3), K.reshape(1, 1, 3, 3)
tconv = nn.ConvTranspose2d(1, 1, kernel_size=3, stride=2, bias=False)
tconv.weight.data = K
Y2 = tconv(X)
print(Y2.shape)


import numpy as np

def sigmoid(z):
    return np.true_divide(1, 1 + np.exp(-z))

def sigmoid_grad(z):
    return np.multiply(sigmoid(z), (1 - sigmoid(z)))


from math import *
import numpy as np
import matplotlib.pyplot as plt
import os
import re

def calculate_damping_coefficient(stiffness, restitution, m1, m2):
    ln_rest = log(restitution)
    return -2 * ln_rest * (get_reduced_particle_mass(m1, m2) * stiffness / (pi ** 2 + ln_rest ** 2)) ** 0.5

def get_mass(density, diameter):
    return density * pi * diameter ** 3 / 6

def get_reduced_particle_mass(m1, m2):
    return m1 * m2 / (m1 + m2)

def get_pos(time, mass, friction_coeff, u_0, g):
    return - friction_coeff * g * time ** 2 / 2 + u_0 * time

def get_vel(time, mass, friction_coeff, u_0, g):
    return - friction_coeff * g * time + u_0

def plot_pos_and_vel():
    stiffness = 1e5
    density = 2000
    diameter = 0.1
    mass = get_mass(density, diameter)
    restitution = 0.8
    friction_coeff = 0.6
    g = 9.81
    u_0 = 1

    damping_coeff = calculate_damping_coefficient(stiffness, restitution, mass, mass)

    col_duration = pi * sqrt(mass / stiffness)

    # Generate Analytic Solution Data
    t_max = u_0 / (friction_coeff * g)  # From Velocity equation
    # print("t_max = " + str(t_max))
    times = np.arange(0, t_max, col_duration / 16)
    positions = []
    velocities = []

    for t in times:
        positions.append(get_pos(t, mass, friction_coeff, u_0, g))
        velocities.append(get_vel(t, mass, friction_coeff, u_0, g))
    times = np.append(times, 0.5)
    positions.append(positions[-1])
    velocities.append(0)

    # Get Simulation Data
    data = []
    increments = []
    data_dir = os.listdir("data")
    for filename in data_dir:
        name_match = re.match("1_friction_(\d+)_\d+.txt", filename)
        if name_match:
            i = int(name_match.group(1))
            if increments.count(i) == 0:
                increments.append(i)
    increments.sort()
    for i in increments:
        timestep_data = np.array([]).reshape((0, 3))  # Times, Positions, Velocities
        for filename in data_dir:
            name_match = re.match("1_friction_" + str(i) + "_(\d+).txt", filename)
            if name_match:
                time = int(name_match.group(1)) / 1e6
                file = open("data/" + filename, "r")
                line = file.readlines()[0]
                data_match = re.match("(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+)",
                                      line)
                if data_match:
                    timestep_data = np.append(timestep_data,
                                              [[time, float(data_match.group(1)), float(data_match.group(4))]], axis=0)
        timestep_data = timestep_data[timestep_data[:, 0].argsort()]  # Sort by time
        data.append(timestep_data)

    # Normalize Data
    # Normalize Data
    for incr_data in data:
        for n in range(len(incr_data)):
            incr_data[n, 0] = incr_data[n, 0] / col_duration  # Normalize time with collision duration
            incr_data[n, 1] = incr_data[n, 1] / diameter  # Normalize position with particle diameter
            incr_data[n, 2] = incr_data[n, 2] / u_0  # Normalize velocity with initial velocity

    for n in range(len(positions)):
        times[n] = times[n] / col_duration
        positions[n] = positions[n] / diameter
        velocities[n] = velocities[n] / u_0

    # Plot Data
    fig = plt.figure(figsize=(8, 10))
    plt.rc('axes', labelsize=15)
    fig.patch.set_facecolor('white')
    ax1 = fig.add_subplot(211)
    ax1_lines = []
    analytic_line, = ax1.plot(times, positions, 'k', label="Analytic")
    ax1_lines.append(analytic_line)
    for i in range(len(data)):
        timestep_data = data[i]
        timestep_label = "$t_{collision} / " + str(increments[i]) + "$"
        line, = ax1.plot(timestep_data[:, 0], timestep_data[:, 1], '--', label=timestep_label)
        ax1_lines.append(line)
    ax1.set_xlabel('$t / t_{collision}$')
    ax1.set_ylabel('$x / d_p$')
    ax1.legend(handles=ax1_lines, loc=4)

    ax2 = fig.add_subplot(212)
    ax2_lines = []
    analytic_line, = ax2.plot(times, velocities, 'k', label="Analytic")
    ax2_lines.append(analytic_line)
    for i in range(len(data)):
        timestep_data = data[i]
        timestep_label = "$t_{collision} / " + str(increments[i]) + "$"
        line, = ax2.plot(timestep_data[:, 0], timestep_data[:, 2], '--', label=timestep_label)
        ax2_lines.append(line)
    ax2.set_ylabel('$u / u_0$')
    ax2.set_xlabel('$t / t_{collision}$')
    ax2.legend(handles=ax2_lines, loc=1)

    fig.tight_layout()
    plt.show()

plot_pos_and_vel()


from anim import *
import numpy as np
from scipy.integrate import odeint
import random

class LorenzAttractor(Scene):
    def __init__(self, width, height, xrange, yrange, color=BLACK,
            sigma=10, rho=28, beta=8/3):
        super().__init__(width, height, xrange, yrange, color)
        self.sigma = sigma
        self.rho = rho
        self.beta = beta

    def deltaX(self, x, y, sigma):
        return sigma*y - sigma*x

    def deltaY(self, x, y, z, rho):
        return rho*x - x*z - y

    def deltaZ(self, x, y, z, beta):
        return x*y - beta*z

    def nextPoint(self, state, t):
        x, y, z = state
        x1 = self.deltaX(x, y, self.sigma)
        y1 = self.deltaY(x, y, z, self.rho)
        z1 = self.deltaZ(x, y, z, self.beta)
        return x1, y1, z1

    def graph(self, state0, t_end=40, t_step=0.01, t_start=0, trailLength=100, frame_skip=40, radius=2, color=PINK, opacity=255):
        self.graphFunction(self.nextPoint, state0, t_end, t_step, t_start, trailLength, frame_skip, radius, color, opacity)

    def graphRandomParticles(self, particles, t_end=40, t_step=0.01, t_start=0,
            trailLength=100, frame_skip=40, radii_min=0.5, radii_max=1.75, color=None,
            opacity=255):
        self.graphFunctionWithRandomParticles(self.nextPoint, particles, t_end, t_step, t_start,
            trailLength, frame_skip, radii_min, radii_max, color,
            opacity)


# Impoting Numpy library
import numpy as np

# Initialising arrays
x = np.array([[1,2,3],[3,2,1],[1,0,-1]])
y = np.array([[-4,-3,2],[1,2,1],[2,4,2]])

# Printing rank of x
print("rank of x: {}".format(np.linalg.matrix_rank(x)))

# Storing and Finding eigen vector and value of X Using eig function of Numpy
eigen_valuesX,eigen_vectorX= np.linalg.eig(x)

# Printing eigen value and vector of X
print("\nEIGEN VALUES OF X:\n{}".format(eigen_valuesX))
print("\nEIGEN VECTOR OF X:\n{}".format(eigen_vectorX))

# Printing rank of y
print("rank of y : {}".format(np.linalg.matrix_rank(y)))

# Storing and Finding eigen vector and value of Y Using eig function of Numpy
eigen_valuesY,eigen_vectorY= np.linalg.eig(y)

# Printing eigen value and vector of Y
print("\nEIGEN VALUES OF Y:\n{}".format(eigen_valuesY))
print("\nEIGEN VECTOR OF Y:\n{}".format(eigen_vectorY))

#*************** OUTPUT ************************#
# rank of x: 2

# EIGEN VALUES OF X:
# [ 4.31662479e+00 -2.31662479e+00  1.93041509e-17]

# EIGEN VECTOR OF X:
# [[ 0.58428153  0.73595785  0.40824829]
#  [ 0.80407569 -0.38198836 -0.81649658]
#  [ 0.10989708 -0.55897311  0.40824829]]

# rank of y : 2

# EIGEN VALUES OF Y:
# [-4.12310563e+00  4.12310563e+00 -7.83686841e-16]

# EIGEN VECTOR OF Y:
# [[ 0.96413818 -0.05497126  0.66742381]
#  [-0.11869083 -0.44653738 -0.57207755]
#  [-0.23738167 -0.89307476  0.47673129]]
#*************** END OF OUTPUT ************************#


from .coord_transform import CoordTransform as _CoordTransform
class Cylindrical2Cartesian(_CoordTransform):
    def __init__(self, syms=None):
        from sympy import symbols, Array, cos, sin
        if syms is None:
            _R = symbols('R', positive=True)
            _Z, _phi = symbols('Z, phi', real=True)
            syms = (_R, _Z, _phi)
        R, Z, phi = syms[0], syms[1], syms[2]
        _CoordTransform.__init__(self, [R*cos(phi), R*sin(phi), Z], syms)

class Cartesian2Cylindrical(_CoordTransform):
    def __init__(self, syms=None):
        from sympy import symbols, Array, cos, sin, sqrt, atan2
        if syms is None:
            syms = symbols('x, y, z', real=True)
        x, y, z = syms[0], syms[1], syms[2]
        _CoordTransform.__init__(self, [sqrt(x**2+y**2), z, atan2(y, x)], syms)


"""This file contains code used in "Think Stats",
by Allen B. Downey, available from greenteapress.com

Copyright 2014 Allen B. Downey
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
"""

from __future__ import print_function, division

import thinkbayes2
import thinkplot

import numpy

"""
Bayesian solution to the Lincoln index, described in a blog
article at Probably Overthinking It.

http://tinyurl.com/lincoln14

Last year my occasional correspondent John D. Cook wrote an excellent
blog post about the Lincoln index, which is a way to estimate the
number of errors in a document (or program) by comparing results from
two independent testers.

http://www.johndcook.com/blog/2010/07/13/lincoln-index/

Here's his presentation of the problem:

   "Suppose you have a tester who finds 20 bugs in your program. You
want to estimate how many bugs are really in the program. You know
there are at least 20 bugs, and if you have supreme confidence in your
tester, you may suppose there are around 20 bugs. But maybe your
tester isn't very good. Maybe there are hundreds of bugs. How can you
have any idea how many bugs there are? There's no way to know with one
tester. But if you have two testers, you can get a good idea, even if
you don't know how skilled the testers are."

Then he presents the Lincoln index, an estimator "described by
Frederick Charles Lincoln in 1930," where Wikpedia's use of
"described" is a hint that the index is another example of Stigler's
law of eponymy.

    "Suppose two testers independently search for bugs. Let k1 be the
number of errors the first tester finds and k2 the number of errors
the second tester finds. Let c be the number of errors both testers
find. The Lincoln Index estimates the total number of errors as k1 k2
/ c [I changed his notation to be consistent with mine]."

So if the first tester finds 20 bugs, the second finds 15, and they
find 3 in common, we estimate that there are about 100 bugs.

Of course, whenever I see something like this, the idea that pops into
my head is that there must be a (better) Bayesian solution! And there
is.

"""

def choose(n, k, d={}):
    """The binomial coefficient "n choose k".

    Args:
      n: number of trials
      k: number of successes
      d: map from (n,k) tuples to cached results

    Returns:
      int
    """
    if k == 0:
        return 1
    if n == 0:
        return 0

    try:
        return d[n, k]
    except KeyError:
        res = choose(n-1, k) + choose(n-1, k-1)
        d[n, k] = res
        return res

def binom(k, n, p):
    """Computes the rest of the binomial PMF.

    k: number of hits
    n: number of attempts
    p: probability of a hit
    """
    return p**k * (1-p)**(n-k)

class Lincoln(thinkbayes2.Suite, thinkbayes2.Joint):
    """Represents hypotheses about the number of errors."""

    def Likelihood(self, data, hypo):
        """Computes the likelihood of the data under the hypothesis.

        hypo: n, p1, p2
        data: k1, k2, c
        """
        n, p1, p2 = hypo
        k1, k2, c = data

        part1 = choose(n, k1) * binom(k1, n, p1)
        part2 = choose(k1, c) * choose(n-k1, k2-c) * binom(k2, n, p2)
        return part1 * part2

def main():

    data = 20, 15, 3
    probs = numpy.linspace(0, 1, 31)
    hypos = []
    for n in range(32, 350):
        for p1 in probs:
            for p2 in probs:
                hypos.append((n, p1, p2))

    suite = Lincoln(hypos)
    suite.Update(data)

    n_marginal = suite.Marginal(0)

    thinkplot.Pmf(n_marginal, label='n')
    thinkplot.Save(root='lincoln1',
                   xlabel='number of bugs',
                   ylabel='PMF',
                   formats=['pdf', 'png'])

    print('post mean n', n_marginal.Mean())
    print('MAP n', n_marginal.MaximumLikelihood())

    p1_marginal = suite.Marginal(1, label='p1')
    p2_marginal = suite.Marginal(2, label='p2')

    thinkplot.Pdf(p1_marginal)
    thinkplot.Pdf(p2_marginal)
    thinkplot.Show()

    print('post mean p1', p1_marginal.Mean())
    print('MAP p1', p1_marginal.MaximumLikelihood())

    print('post mean p2', p2_marginal.Mean())
    print('MAP p2', p2_marginal.MaximumLikelihood())

    print('p1 > p2', p1_marginal > p2_marginal)
    print('p1 < p2', p1_marginal < p2_marginal)

if __name__ == '__main__':
    main()


#!/usr/bin/env python
""" LMS adaptive filters """
from __future__ import print_function, division
__author__ = 'Richard Lindsley'

import sys
import numpy as np
import matplotlib.pyplot as plt
import convm

def lms(x, d, p, mu, w0=None):
    """ Finds the LMS adaptive filter w based on the inputs

    Adapted from the Hayes MATLAB code.

    Inputs:
        x: input data to the filter, Nx1 vector
        d: desired output signal, Nx1 vector
        p: filter order
        mu: step size, scalar or Nx1 vector
        w0: initial guess (optional)

    Outputs:
        w: filter coefficient matrix: w[n,p] where
            n is the time index and
            p is the filter tap index
        e: error sequence e(n): Nx1 vector

    """
    # Initialize
    N = len(x)
    X = convm.convm(x, p)
    if not w0:
        w0 = np.zeros(p)
    # Promote mu to be a vector if it's not already
    if not isinstance(mu, np.ndarray):
        mu = np.ones(N) * mu
    w = np.zeros((N,p))
    e = np.zeros(N)
    w[0,:] = w0

    # Run the filter
    for n in xrange(N-1):
        y = np.dot(w[n,:], X[n,:])
        e[n] = d[n] - y
        w[n+1,:] = w[n,:] + mu[n] * e[n] * X[n,:].conj()

    return w, e

def nlms(x, d, p, beta, w0=None, verbose=False):
    """ Finds the NLMS adaptive filter w based on the inputs

    Adapted from the Hayes MATLAB code.

    Inputs:
        x: input data to the filter, Nx1 vector
        d: desired output signal, Nx1 vector
        p: filter order
        beta: normalized step size (0 < beta < 2)
        w0: initial guess (optional)
        verbose: verbosity flag

    Outputs:
        w: filter coefficient matrix: w[n,p] where
            n is the time index and
            p is the filter tap index
        e: error sequence e(n): Nx1 vector

    """
    # Initialize
    N = len(x)
    eps = 1e-4 # epsilon for denominator
    X = convm.convm(x, p)
    if not w0:
        w0 = np.zeros(p)
    w = np.zeros((N,p))
    e = np.zeros(N)
    w[0,:] = w0
    if verbose:
        print(' ')

    # Run the filter
    for n in xrange(N-1):
        if verbose and n % 10000 == 0:
            print(' {}/{}\r'.format(n,N-1), end='')
            sys.stdout.flush()
        y = np.dot(w[n,:], X[n,:])
        e[n] = d[n] - y
        norm = eps + np.dot(X[n,:], X[n,:].T.conj())
        w[n+1,:] = w[n,:] + beta / norm * e[n] * X[n,:].conj()

    if verbose:
        print(' ')
    return w, e

def plot_traj(w, w_true, fname=None):
    """ Plot the trajectory of the filter coefficients

    Inputs:
        w: matrix of filter weights versus time
        w_true: vector of true filter weights
        fname: what filename to export the figure to. If None, then doesn't
        export

    """
    plt.figure()
    n = np.arange(w.shape[0])
    n_ones = np.ones(w.shape[0])
    plt.hold(True)
    # NOTE: This construction places a limit of 4 on the filter order
    plt_colors = ['b', 'r', 'g', 'k']
    for p in xrange(w.shape[1]):
        plt.plot(n, w[:,p], '{}-'.format(plt_colors[p]), label='w({})'.format(p))
        plt.plot(n, w_true[p] * n_ones, '{}--'.format(plt_colors[p]))
    plt.xlabel('Iteration')
    plt.ylabel('Coefficients')
    plt.legend()
    if fname:
        plt.savefig(fname)
        plt.close()

def plot_error(e, fname=None):
    """ Plot the squared error versus time

    Inputs:
        e: vector of the error versus time
        fname: what filename to export the figure to. If None, then doesn't
        export

    """
    plt.figure()
    n = np.arange(len(e))
    e2 = np.power(e, 2)
    plt.plot(n, e2)
    #plt.semilogy(n, e2)
    plt.xlabel('Iteration')
    plt.ylabel('Squared error')
    if fname:
        plt.savefig(fname)
        plt.close()


"""
levenberg_marquardt_algorithm.py

Returns the minimizer of the function
X0 - initial guess
func_residual- anonimous function residual
func_jacobian- anonimous function jacobian
"""

import numpy as np

def levenberg_marquardt_algorithm(X0, func_residual, func_jacobian, options):

    reg_coeff = 1;
    report = {};
    N_iter_max = options['N_iter_max'];
    tolerance_x = options['tolerance_x'];
    tolerance_y = options['tolerance_y'];
    X_lower = options['x_lower'];
    X_upper = options['x_upper'];
    progress_x = np.zeros((X0.size, N_iter_max + 1));
    progress_y = np.zeros((1, N_iter_max + 1));
    progress_x[:, [0]] = X0;
    progress_y[0, [0]] = np.sum(func_residual(X0));
    X_old = X0;

    for iter_no in range(1, N_iter_max + 1):
        jacobian = func_jacobian(X_old);
        residuals = func_residual(X_old);

        #d = np.linalg.solve(jacobian, residuals); # Newton's algorithm
        #d = np.linalg.solve(jacobian.T @ jacobian, jacobian.T @ residuals); # Newton's algorithm
        d = np.linalg.solve(jacobian.T @ jacobian + reg_coeff * np.eye(X_old.size, X_old.size), jacobian.T @ residuals); # with Levenberg-Marquardt modification
        X = X_old + d.reshape(X_old.shape);

        # Projection onto constrained parameter space
        indx_limits = (X < X_lower);
        X[indx_limits] = X_lower[indx_limits];
        indx_limits = (X > X_upper);
        X[indx_limits] = X_upper[indx_limits];

        progress_x[:, [iter_no]] = X;
        progress_y[0, [iter_no]] = np.sum(func_residual(X));

        if (np.linalg.norm(X - X_old) < tolerance_x * np.linalg.norm(X_old)):
            print('Tolerance in X is reached in %d iterations, exit..' % (iter_no));
            break;

        if (np.abs(progress_y[0, [iter_no]] - progress_y[0, [iter_no - 1]]) < tolerance_y * np.abs(progress_y[0, [iter_no - 1]])):
            print('Tolerance in Y is reached in %d iterations, exit..' % (iter_no));
            break;

        X_old = X;

    report = {'N_iter_max' : N_iter_max, 'iter_no' : iter_no, 'X0' : X0, 'X' : X, 'progress_x' : progress_x, 'progress_y' : progress_y};
    return (X, report);


import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import ui
import scipy.io as sio
import scipy.optimize as opt
from scipy.io import loadmat
import seaborn as sb

print('PART 1 异常检测')
ui.split_line2()
print('用高斯模型检测数据是否异常')
print('载入数据')
data = loadmat('data/ex8data1.mat')
X = data['X']
print(X.shape)
print('查看数据分布')
fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(X[:,0], X[:,1])
plt.show()
print('定义高斯函数')
def estimate_gaussian(X):
    mu = X.mean(axis=0)
    sigma = X.var(axis=0)

    return mu, sigma
print('进行高斯预估')
mu, sigma = estimate_gaussian(X)
print(mu, sigma)
print('确定验证集')
Xval = data['Xval']
yval = data['yval']
print(Xval.shape, yval.shape)
print('计算数据点属于正态分布的概率')
from scipy import stats
dist = stats.norm(mu[0], sigma[0])
print(dist.pdf(15))
print('每个点的概率密度')
print(dist.pdf(X[:,0])[0:50])
print('计算数据集中每个点的概率密度')
p = np.zeros((X.shape[0], X.shape[1]))
p[:,0] = stats.norm(mu[0], sigma[0]).pdf(X[:,0])
p[:,1] = stats.norm(mu[1], sigma[1]).pdf(X[:,1])
print(p.shape)
print('计算验证集中每个点的概率密度')
pval = np.zeros((Xval.shape[0], Xval.shape[1]))
pval[:,0] = stats.norm(mu[0], sigma[0]).pdf(Xval[:,0])
pval[:,1] = stats.norm(mu[1], sigma[1]).pdf(Xval[:,1])
print(pval.shape)
print('找到判断的最佳阈值')
def select_threshold(pval, yval):
    best_epsilon = 0
    best_f1 = 0
    f1 = 0

    step = (pval.max() - pval.min()) / 1000

    for epsilon in np.arange(pval.min(), pval.max(), step):
        preds = pval < epsilon

        tp = np.sum(np.logical_and(preds == 1, yval == 1)).astype(float)
        fp = np.sum(np.logical_and(preds == 1, yval == 0)).astype(float)
        fn = np.sum(np.logical_and(preds == 0, yval == 1)).astype(float)

        precision = tp / (tp + fp)
        recall = tp / (tp + fn)
        f1 = (2 * precision * recall) / (precision + recall)

        if f1 > best_f1:
            best_f1 = f1
            best_epsilon = epsilon

    return best_epsilon, best_f1

epsilon, f1 = select_threshold(pval, yval)
print(epsilon, f1)
print('将该阈值应用到数据集，并可视化结果')
# indexes of the values considered to be outliers
outliers = np.where(p < epsilon)
print(outliers)

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(X[:,0], X[:,1])
ax.scatter(X[outliers[0],0], X[outliers[0],1], s=50, color='r', marker='o')
plt.show()

ui.split_line1()
print('PART 2 协同过滤之电影推荐')
print('载入数据')
data = loadmat('data/ex8_movies.mat')
print(data)
print('Y是包含从1到5的等级的（数量的电影x数量的用户）数组.R是包含指示用户是否给电影评分的二进制值的“指示符”数组')
print('两者应该具有相同的维度。')
Y = data['Y']
R = data['R']
print(Y.shape, R.shape)
print('电影的平均评级')
print(Y[1,np.where(R[1,:]==1)[0]].mean())
print('可视化')
fig, ax = plt.subplots(figsize=(12,12))
ax.imshow(Y)
ax.set_xlabel('Users')
ax.set_ylabel('Movies')
fig.tight_layout()
plt.show()

print('定义代价函数')
def cost(params, Y, R, num_features):
    Y = np.matrix(Y)  # (1682, 943)
    R = np.matrix(R)  # (1682, 943)
    num_movies = Y.shape[0]
    num_users = Y.shape[1]

    # reshape the parameter array into parameter matrices
    X = np.matrix(np.reshape(params[:num_movies * num_features], (num_movies, num_features)))  # (1682, 10)
    Theta = np.matrix(np.reshape(params[num_movies * num_features:], (num_users, num_features)))  # (943, 10)

    # initializations
    J = 0

    # compute the cost
    error = np.multiply((X * Theta.T) - Y, R)  # (1682, 943)
    squared_error = np.power(error, 2)  # (1682, 943)
    J = (1. / 2) * np.sum(squared_error)

    return J

print('评估一小段数据')
params_data = loadmat('data/ex8_movieParams.mat')
X = params_data['X']
Theta = params_data['Theta']
X.shape, Theta.shape

users = 4
movies = 5
features = 3

X_sub = X[:movies, :features]
Theta_sub = Theta[:users, :features]
Y_sub = Y[:movies, :users]
R_sub = R[:movies, :users]

params = np.concatenate((np.ravel(X_sub), np.ravel(Theta_sub)))
print('计算 Cost')
print(cost(params, Y_sub, R_sub, features))

print('定义梯度计算')
def cost0(params, Y, R, num_features):
    Y = np.matrix(Y)  # (1682, 943)
    R = np.matrix(R)  # (1682, 943)
    num_movies = Y.shape[0]
    num_users = Y.shape[1]

    # reshape the parameter array into parameter matrices
    X = np.matrix(np.reshape(params[:num_movies * num_features], (num_movies, num_features)))  # (1682, 10)
    Theta = np.matrix(np.reshape(params[num_movies * num_features:], (num_users, num_features)))  # (943, 10)

    # initializations
    J = 0
    X_grad = np.zeros(X.shape)  # (1682, 10)
    Theta_grad = np.zeros(Theta.shape)  # (943, 10)

    # compute the cost
    error = np.multiply((X * Theta.T) - Y, R)  # (1682, 943)
    squared_error = np.power(error, 2)  # (1682, 943)
    J = (1. / 2) * np.sum(squared_error)

    # calculate the gradients
    X_grad = error * Theta
    Theta_grad = error.T * X

    # unravel the gradient matrices into a single array
    grad = np.concatenate((np.ravel(X_grad), np.ravel(Theta_grad)))

    return J, grad

print('计算梯度')
J, grad = cost0(params, Y_sub, R_sub, features)
print(J, grad)
print('定义正则化 cost')
def cost1(params, Y, R, num_features, learning_rate):
    Y = np.matrix(Y)  # (1682, 943)
    R = np.matrix(R)  # (1682, 943)
    num_movies = Y.shape[0]
    num_users = Y.shape[1]

    # reshape the parameter array into parameter matrices
    X = np.matrix(np.reshape(params[:num_movies * num_features], (num_movies, num_features)))  # (1682, 10)
    Theta = np.matrix(np.reshape(params[num_movies * num_features:], (num_users, num_features)))  # (943, 10)

    # initializations
    J = 0
    X_grad = np.zeros(X.shape)  # (1682, 10)
    Theta_grad = np.zeros(Theta.shape)  # (943, 10)

    # compute the cost
    error = np.multiply((X * Theta.T) - Y, R)  # (1682, 943)
    squared_error = np.power(error, 2)  # (1682, 943)
    J = (1. / 2) * np.sum(squared_error)

    # add the cost regularization
    J = J + ((learning_rate / 2) * np.sum(np.power(Theta, 2)))
    J = J + ((learning_rate / 2) * np.sum(np.power(X, 2)))

    # calculate the gradients with regularization
    X_grad = (error * Theta) + (learning_rate * X)
    Theta_grad = (error.T * X) + (learning_rate * Theta)

    # unravel the gradient matrices into a single array
    grad = np.concatenate((np.ravel(X_grad), np.ravel(Theta_grad)))

    return J, grad

print('计算梯度')
J, grad = cost1(params, Y_sub, R_sub, features, 1.5)
print(J, grad)

print('加载字典')
movie_idx = {}
f = open('data/movie_ids.txt',encoding= 'gbk')
for line in f:
    tokens = line.split(' ')
    tokens[-1] = tokens[-1][:-1]
    movie_idx[int(tokens[0]) - 1] = ' '.join(tokens[1:])
print('查看第一条')
print(movie_idx[0])

print('进行评分')
ratings = np.zeros((1682, 1))

ratings[0] = 4
ratings[6] = 3
ratings[11] = 5
ratings[53] = 4
ratings[63] = 5
ratings[65] = 3
ratings[68] = 5
ratings[97] = 2
ratings[182] = 4
ratings[225] = 5
ratings[354] = 5

print('Rated {0} with {1} stars.'.format(movie_idx[0], str(int(ratings[0]))))
print('Rated {0} with {1} stars.'.format(movie_idx[6], str(int(ratings[6]))))
print('Rated {0} with {1} stars.'.format(movie_idx[11], str(int(ratings[11]))))
print('Rated {0} with {1} stars.'.format(movie_idx[53], str(int(ratings[53]))))
print('Rated {0} with {1} stars.'.format(movie_idx[63], str(int(ratings[63]))))
print('Rated {0} with {1} stars.'.format(movie_idx[65], str(int(ratings[65]))))
print('Rated {0} with {1} stars.'.format(movie_idx[68], str(int(ratings[68]))))
print('Rated {0} with {1} stars.'.format(movie_idx[97], str(int(ratings[97]))))
print('Rated {0} with {1} stars.'.format(movie_idx[182], str(int(ratings[182]))))
print('Rated {0} with {1} stars.'.format(movie_idx[225], str(int(ratings[225]))))
print('Rated {0} with {1} stars.'.format(movie_idx[354], str(int(ratings[354]))))

print('将评分添加到已有数据集中')
R = data['R']
Y = data['Y']

Y = np.append(Y, ratings, axis=1)
R = np.append(R, ratings != 0, axis=1)

print(Y.shape, R.shape, ratings.shape)
print('对变量进行归一化')
movies = Y.shape[0]  # 1682
users = Y.shape[1]  # 944
features = 10
learning_rate = 10.

X = np.random.random(size=(movies, features))
Theta = np.random.random(size=(users, features))
params = np.concatenate((np.ravel(X), np.ravel(Theta)))

print(X.shape, Theta.shape, params.shape)

Ymean = np.zeros((movies, 1))
Ynorm = np.zeros((movies, users))

for i in range(movies):
    idx = np.where(R[i,:] == 1)[0]
    Ymean[i] = Y[i,idx].mean()
    Ynorm[i,idx] = Y[i,idx] - Ymean[i]

print(Ynorm.mean())

print('进行模型训练与优化')
from scipy.optimize import minimize

fmin = minimize(fun=cost1, x0=params, args=(Ynorm, R, features, learning_rate),
                method='CG', jac=True, options={'maxiter': 100})
print(fmin)

X = np.matrix(np.reshape(fmin.x[:movies * features], (movies, features)))
Theta = np.matrix(np.reshape(fmin.x[movies * features:], (users, features)))

print(X.shape, Theta.shape)
print('进行预测')
predictions = X * Theta.T
my_preds = predictions[:, -1] + Ymean
print(my_preds.shape)
print('排序预测')
idx = np.argsort(my_preds, axis=0)[::-1]
print(idx)
print("Top 10 movie predictions:")
for i in range(10):
    j = int(idx[i])
    print('Predicted rating of {0} for movie {1}.'.format(str(float(my_preds[j])), movie_idx[j]))


import numpy as np
import matplotlib.pyplot as plt
from numpy.linalg import inv

N = 25
X = np.reshape(np.linspace(0, 0.9, N), (N, 1))
Y_train = np.cos(10*X**2) + 0.1 * np.sin(100*X)
#K = 12  # order of the func
listTrainning = []
for i in np.linspace(0, 0.9, N):
    listTrainning.append(np.cos(10*i**2) + 0.1 * np.sin(100*i))

def Phi(K):
    Psii = np.zeros((N, K + 1))
    for i in range(N):
        for j in range(0, K+1):
            Psii[i][j] = X[i][0]**j

    return Psii

def lml(alpha, beta, Phi, Y):
    """
    4 marks

    :param alpha: float
    :param beta: float
    :param Phi: array of shape (N, M)
    :param Y: array of shape (N, 1)
    :return: the log marginal likelihood, a scalar
    """
    N = len(Phi)
    M = len(Phi[0])
    part1 = (-N*0.5)*np.log(2*np.pi)
    wholePhi = np.dot(np.dot(Phi, alpha*np.identity(M)),Phi.T)
    wholeBeta = beta*np.identity(N)
    part2 = - 0.5*np.log(np.linalg.det( wholePhi + wholeBeta))

    part3 = -0.5*np.dot(np.dot(Y.T, inv((wholePhi + wholeBeta))),Y)
    logFunc  = part1 + part2 + part3

    return logFunc[0][0]

def grad_lml(alpha, beta, Phi, Y):
    """
    8 marks (4 for each component)

    :param alpha: float
    :param beta: float
    :param Phi: array of shape (N, M)
    :param Y: array of shape (N, 1)
    :return: array of shape (2,). The components of this array are the gradients
    (d_lml_d_alpha, d_lml_d_beta), the gradients of lml with respect to alpha and beta respectively.

    """
    N = len(Phi)
    M = len(Phi[0])
    X = np.dot(np.dot(alpha, Phi), Phi.T) + np.dot(beta, np.identity(N))
    common = 0.5 * np.dot(np.dot(inv(X), np.dot(Y, Y.T) - X), inv(X))
    d_alpha = np.trace(np.dot(common.T, np.dot(Phi, Phi.T)))
    d_bete = np.trace(common.T)

    return np.array([d_alpha, d_bete])

#order = 1
Phi = Phi(1)
# From calculation, it is expected that the local minimum occurs at x=9/4

cur_x = np.array([0.5, 0.65]) # The algorithm starts at x=1
gamma = 0.01 # step size multiplier

max_iters = 20000 # maximum number of iterations
iters = 0 #iteration counter

x_gd = []
y_gd = []
z_gd = []
while (iters < max_iters):
    x_gd.append(cur_x[0])
    y_gd.append(cur_x[1])
    z_gd.append(lml(cur_x[0],cur_x[1], Phi, Y_train))

    prev_x = np.array([cur_x[0], cur_x[1]])
    #cur_x = prev_x + gamma * grad_f2(prev_x)
    cur_x = prev_x + gamma * grad_lml(prev_x[0], prev_x[1], Phi, Y_train)
    iters += 1

print("The local maximum occurs at", cur_x)
print("the maximum is ", lml(cur_x[0], cur_x[1], Phi, Y_train))

xlist = np.linspace(0.2, 1.0, 50)
ylist = np.linspace(0.3, 0.7, 50)

X, Y = np.meshgrid(xlist, ylist)
# new an array with all 0 inside
Z = np.zeros((50, 50))

for i in range(50):
    for j in range(50):
        #Z[i][j] = f2(np.array([xlist[i], ylist[j]]))
        Z[i][j] = lml(X[i,j], Y[i,j], Phi, Y_train)

# print(Z)
plt.contour(X, Y, Z, 100, cmap='jet')
plt.colorbar()

plt.plot(x_gd, y_gd, color='green', marker='v', linewidth=2, markersize=0, label='Gradient descent of order 1')
plt.legend(loc='best')
plt.ylabel('beta')
plt.xlabel('alpha')
plt.title('this 1 b')
plt.show()
#print(theta)


import numpy as np

class TriangularMemoizer(object):
    """Specific class for triangular recurrence sequences of the kind
    s(0, 0) = s(1, 1) = 0, s(a, 0) = 0 for a > 0, s(a, b) = 0 for b > a,
    s(a, b) = f(a, b) * s(a - 1, b) + g(a, b) s(a - 1, b - 1).

    For instance, Stirling numbers of the first kind are recovered setting
    f(a, b) = (a - 1) and g(a, b) = 1
    """

    def __init__(self, first_term_multiplier, second_term_multiplier):
        self.memo = [np.array([1])]
        self.max_a = 0
        self.first_term_multiplier = first_term_multiplier
        self.second_term_multipier = second_term_multiplier

    def __call__(self, a, b):
        if a == b == 0:
            return 1

        if b == 0:
            return 0

        if b > a:
            return 0

        if a > self.max_a:
            self.add_rows(a)

        return self.memo[a][b]

    def add_rows(self, new_max_a):
        old_max_a = self.max_a
        self.max_a = new_max_a
        for a in range(old_max_a + 1, new_max_a + 1):
            self.memo.append(np.zeros(a + 1))
            self.memo[a][0] = 0
            for b in range(1, a + 1):
                res = self.first_term_multiplier(a, b) * self.__call__(a - 1, b) + \
                    self.second_term_multipier(a, b) * self.__call__(a - 1, b - 1)
                self.memo[a][b] = res

stirling = TriangularMemoizer(lambda a, b: a - 1, lambda a, b: 1)

class generalized_factorial_memoizer(TriangularMemoizer):
    """Computes the Generalized Factorial numbers and stores the results
    in a memoizer.
    The generalized factorial with parameter sigma in [0, 1) satisfy the
    triangular recurrence relation
    s(n, k) = (n - 1 - sigma * k) s(n - 1, k) + sigma * s(n - 1, k-1)
    """

    def __init__(self, sigma):
        def first_arg(n, k): return n-1 - sigma * k
        def second_arg(n, k): return sigma
        super().__init__(first_arg, second_arg)


import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import distance_matrix

# Squared exponential covariance function (for stationary GP).
def sqexp_cov_fn(d, rho, alpha):
    return alpha ** 2 * np.exp(-0.5 * (d / rho) ** 2)

# Function to create gp prediction function.
def gp_predict_maker(y, x, x_new, cov_fn):
    N = x.shape[0]
    N_new = x_new.shape[0]
    M = N + N_new
    xx = np.concatenate((x_new, x)).reshape(M, 1)
    D = distance_matrix(xx, xx)

    # Function which takes parameters of covariance function
    # and predicts at new locations.
    def gp_predict(rho, alpha, sigma):
        K = cov_fn(D, rho, alpha) + np.eye(M) * np.power(sigma, 2)
        K_new_old = K[:N_new, N_new:]
        K_old_inv = np.linalg.inv(K[N_new:, N_new:])
        C = K_new_old.dot(K_old_inv)
        mu = C.dot(y)
        S = K[:N_new, :N_new] - C.dot(K_new_old.T)
        return np.random.multivariate_normal(mu, S)

    return gp_predict

# Function for plotting parameter posterior.
def plot_post(samples, key, bins=None, suffix=""):
    plt.hist(samples[key], density=True, bins=bins)
    plt.xlabel(key)
    plt.ylabel('density')
    if suffix != "":
        suffix = "({})".format(suffix)

    plt.title("{} {}".format(key, suffix));

# Function for making all plots.
def make_plots(samples, x, y, x_grid, f, sigma_true, cov_fn=sqexp_cov_fn,
               n_new=100, figsize=(12,4), figsize_f=(12, 4), suffix="",
               x_min=-3.5, x_max=3.5, ci=95):
    # Create new locations for prediction.
    x_new = np.linspace(x_min, x_max, n_new)

    # Create gp predict function.
    gp_predict = gp_predict_maker(y, x, x_new, cov_fn)

    # Number of posterior samples.
    nsamples = len(samples['alpha'])

    # Make predictions at new locations.
    preds = np.stack([gp_predict(alpha=samples['alpha'][b],
                                 rho=samples['rho'][b],
                                 sigma=samples['sigma'][b])
                      for b in range(nsamples)])

    # Plot parameters posterior.
    plt.figure(figsize=figsize)
    plt.subplot(1, 3, 1)
    plot_post(samples, 'alpha', bins=30, suffix=suffix)
    plt.subplot(1, 3, 2)
    plot_post(samples, 'rho', bins=30, suffix=suffix)
    plt.subplot(1, 3, 3)
    plot_post(samples, 'sigma', bins=30, suffix=suffix)
    plt.axvline(sigma_true, ls=":", color='red', label="truth")
    plt.legend()
    plt.tight_layout()

    # Summarize function posterior.
    ci_lower = (100 - ci) / 2
    ci_upper = (100 + ci) / 2
    preds_mean = preds.mean(0)
    preds_lower = np.percentile(preds, ci_lower, axis=0)
    preds_upper = np.percentile(preds, ci_upper, axis=0)

    # Make suffix
    if suffix != "":
        suffix = "({})".format(suffix)

    # Plot function posterior.
    plt.figure(figsize=figsize_f)
    plt.scatter(x, y, c='black', zorder=3, label='data')
    plt.fill_between(x_new, preds_upper, preds_lower, alpha=.3, label='95% CI')
    plt.plot(x_new, preds.mean(0), lw=2, label="mean fn.", color="blue")
    plt.plot(x_grid, f, label="truth", lw=2, c='red', ls=':')
    plt.title("GP Posterior Predictive with 95% CI {}".format(suffix))
    plt.legend()
    plt.tight_layout();


'''
datecreated: 190926
objective: first practice with naive bayes classification
source: https://www.youtube.com/watch?v=CPqOCI0ahss
KJG191119: use this to prep for bayes with iris dataset

'''

import numpy as np
# import matplotlib.pyplot as plt

# pseudo dataset: probability to play golf, given variables
# outlook: sunny,overcast,rainy = 0,1,2
# temperture: cool, mild, hot = 0,1,2
# humidity: normal,high = 0,1
# windy: false,true = 0,1
# play: no, yes = 0,1

dat=np.array(
# outlook | temperature | humidity | windy | play
[[0,2,1,0,0],
[0,2,1,1,0],
[1,2,1,0,1],
[2,1,1,0,1],
[2,0,0,0,1],
[2,0,0,1,0],
[1,0,0,1,1],
[0,1,1,0,0],
[0,0,0,0,1],
[2,1,0,0,1],
[0,1,0,1,1],
[1,1,1,1,1],
[1,2,0,0,1],
[2,1,1,1,0]
])

def filt(arr,column,condition):
    return arr[arr[:,column]==condition,:]

def p(arr,col0,cond0,col1,cond1):
    ''' What is the probability col0 is at cond0, given col1 is at cond1?
    Ex: What is the probability the weather outlook is
        sunny, given that we will play golf? '''
    n0 = len(filt(filt(arr,col0,cond0),col1,cond1)) # ex: P(sunny|yes)
    nT = len(filt(arr,col1,cond1)) # ex: P(yes)
    return n0/nT
# first, get p(yes) and p(no):
ntot = dat.shape[0]
nyes = dat[:,-1].sum()
nno  = ntot-nyes # binary

p_yes = nyes/ntot
p_no  = 1-p_yes # binary outcome

print(p_yes,p_no)

# look at probabilities that we can play given all variable combinations

# outlook table
# should have as seen in temp.png
print('outlook:')
print('sunny & yes:',p(dat,0,0,-1,1))
print('ocast & yes:',p(dat,0,1,-1,1))
print('rainy & yes:',p(dat,0,2,-1,1))
print('sunny &  no:',p(dat,0,0,-1,0))
print('ocast &  no:',p(dat,0,1,-1,0))
print('rainy &  no:',p(dat,0,2,-1,0))

print('example set of factors.')
print('sunny, cool, humid, windy')

p_x_yes=(p(dat,0,0,-1,1)* # outlook
         p(dat,1,0,-1,1)* # temp
         p(dat,2,1,-1,1)* # humidity
         p(dat,3,1,-1,1)* # wind
         1)
p_x_no =(p(dat,0,0,-1,0)* # outlook
         p(dat,1,0,-1,0)* # temp
         p(dat,2,1,-1,0)* # humidity
         p(dat,3,1,-1,0)* # wind
         1)
p_x = ( len(filt(dat,0,0))/ntot*
        len(filt(dat,1,0))/ntot*
        len(filt(dat,2,1))/ntot*
        len(filt(dat,3,1))/ntot*
        1)
print('P(x) = ',p_x)
print('P(play|factors):   ',p_x_yes*p_yes/p_x)
print('P(noplay|factors): ',p_x_no*p_no/p_x)

# print('ocast & yes:',p(dat,0,0,-1,1))
# print('ocast & yes:',p(dat,0,0,-1,1))
# print('ocast & yes:',p(dat,0,0,-1,1))
# sunny    2,3,2/9,3/5 -- p(sunny|golf)
# overcast 4,0,4/9,0/5
# rainy    3,2,3/9,2/5

# temperature table
# hot   2,2,2/9,2/5
# mild  4,2,4/9,2/5
# cold  3,1,3/9,1/5

if(__name__=='__main__'):
    print('done')

# eof