import numpy as np
from matplotlib.ticker import NullFormatter, FormatStrFormatter, StrMethodFormatter
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches  # for legends
import pandas as pd
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
import datetime
from scipy.optimize import curve_fit

from sklearn.metrics import r2_score
from math import floor

# We'll use this later - x_max (in days elapsed) before ebola in a particular region switched from exponential to sigmoid
# (determined by inspection, 2-3 readings before daily new cases/deaths peaked)
X_CAPS = {
        "cases": { "total": 440, "guinea": 410, "liberia": 435, "sierraleone": 425, },
        "deaths": { "total": 435, "guinea": 400, "liberia": 465, "sierraleone": 420, },
    }

def display_evdefender_plots():
    # Load and select data from CSV
    data = pd.read_csv("data/coronavirus_data.csv")

    # select data for [Jan 27, Feb 2], matching @evdefender's second cases (not deaths!) plot
    window = data[["date", "cases_china_ex", "deaths_china_ex"]][7:14]

    # Fit quadratic and exp models to cases and deaths timeseries; find coeffs and R2s
    x = list(range(len(window)))  # 7 datapoints
    y, y_predicted, popt, r2, label = ({}, {}, {}, {}, {})
    def fquadratic(x, a, b, c):
        return a*x**2 + b*x + c
    def fexp(x, a, b):
        return a*(1+b)**x
    for fittype in ["quad", "exp"]:
        callback = fquadratic if fittype == "quad" else fexp
        y_predicted[fittype], popt[fittype], r2[fittype], label[fittype] = ({}, {}, {}, {})
        for kind in ["cases", "deaths"]:
            y[kind] = window["{}_china_ex".format(kind)].values
            popt[fittype][kind], _ = curve_fit(callback, x, y[kind])
            if fittype == "quad":
                y_predicted[fittype][kind] = [callback(x_i, popt[fittype][kind][0], popt[fittype][kind][1], popt[fittype][kind][2]) for x_i in x]
                label[fittype][kind] = "{}d^2 + {}d + {}".format(round(popt[fittype][kind][0], 2), round(popt[fittype][kind][1], 1), round(popt[fittype][kind][2], 1))
            elif fittype == "exp":
                y_predicted[fittype][kind] = [callback(x_i, popt[fittype][kind][0], popt[fittype][kind][1]) for x_i in x]
                label[fittype][kind] = "{}*(1+{})^d".format(round(popt[fittype][kind][0], 2), round(popt[fittype][kind][1], 4))
            r2[fittype][kind] = r2_score(y[kind], y_predicted[fittype][kind])
            label[fittype][kind] += "; R2 = {}".format(round(r2[fittype][kind], 5))

    # let's reproduce evdefender's mysteriously-truncated Feb 2 deaths plot as well, just to double-check the numbers
    kind = "deaths_trunc"
    for fittype in ["quad", "exp"]:
        callback = fquadratic if fittype == "quad" else fexp
        popt[fittype][kind], _ = curve_fit(callback, x[1:], y["deaths"][1:])  # skip first date, Jan 27
        if fittype == "quad":
            y_predicted[fittype][kind] = [callback(x_i, popt[fittype][kind][0], popt[fittype][kind][1], popt[fittype][kind][2]) for x_i in x[1:]]
            label[fittype][kind] = "{}d^2 + {}d + {}".format(round(popt[fittype][kind][0], 2), round(popt[fittype][kind][1], 1), round(popt[fittype][kind][2], 1))
        elif fittype == "exp":
            y_predicted[fittype][kind] = [callback(x_i, popt[fittype][kind][0], popt[fittype][kind][1]) for x_i in x[1:]]
            label[fittype][kind] = "{}*(1+{})^d".format(round(popt[fittype][kind][0], 2), round(popt[fittype][kind][1], 4))
        r2[fittype][kind] = r2_score(y["deaths"][1:], y_predicted[fittype][kind])
        label[fittype][kind] += "; R2 = {}".format(round(r2[fittype][kind], 5))

    # Plot fits for cases and deaths timeseries
    fig, axs = plt.subplots(3, sharex=True)  # three vertically-stacked subplots
    fig.suptitle("Recreation of @evdefender Feb 2 China cases/deaths timeseries fits")
    for i, kind in enumerate(["cases", "deaths_trunc", "deaths"]):
        if kind == "deaths_trunc":
            axs[i].scatter(x[1:], y["deaths"][1:], color='blue')
            axs[i].set(ylabel="Confirmed China {}\nex-HK, Macau, Taipei".format("Deaths (trunc.)"))
        else:
            axs[i].scatter(x, y[kind], color='blue')
            axs[i].set(ylabel="Confirmed China {}\nex-HK, Macau, Taipei".format(kind.capitalize()))
        axs[i].grid(axis='y')
        patches = []
        for fittype in ["quad", "exp"]:
            fittype_color = 'red' if fittype == "quad" else 'green'
            axs[i].plot(x[1:] if kind == "deaths_trunc" else x, y_predicted[fittype][kind], color=fittype_color)
            patches.append(mpatches.Patch(color=fittype_color, label=label[fittype][kind]))
        axs[i].legend(handles=patches)
    axs[0].set_xticks(x)
    axs[0].set_xticklabels(["Jan {}".format(k) for k in range(27, 32)] + ["Feb {}".format(k) for k in range(1, 3)])

    plt.show()
    return r2["quad"]

def display_ebola_plots():
    # Load data from CSV
    data = pd.read_csv("data/ebola_2014_data.csv")

    # Convert dates from strings to datetimes
    dates = data["date"].values
    datetimes = [datetime.datetime.strptime(datestr, '%d %b %Y') for datestr in dates]  # date of month, month abbrev., year
    days_elapsed = [(date - min(datetimes)).days for date in datetimes]

    # queue up plots - we'll add scatter plots and fits as we go
    fig, axs = plt.subplots(2, sharex=True)  # two vertically-stacked subplots
    fig.suptitle("Quality of sigmoid fits to 2014 West African ebola epidemic in total + 3 regions")

    x = days_elapsed

    # compute sigmoid fits for cases and deaths, for all four locs
    def fsigmoid(x, a, b, c):
        return c / (1 + np.exp(-a*(x-b)))
    results = {"popt": {}, "pcov": {}, "labels": {}}
    colors = {
            "total": ["blue", "blue"],
            "guinea": ["red", "red"],
            "liberia": ["green", "green"],
            "sierraleone": ["black", "black"],
            }
    p0 = [[2.2e-02, 2.3e+02, 2.0e+04],
          [1.2e-02, 2.0e+02, 1.1e+04]]  # from observation from previous runs with no p0
    for kind in ["cases", "deaths"]:
        isdeaths = 1 if kind == "deaths" else 0
        results["popt"][kind] = {}
        results["pcov"][kind] = {}
        results["labels"][kind] = {}
        patches = []
        for loc in colors.keys():
            y = data["{}_{}".format(kind, loc)].values
            popt, pcov = curve_fit(fsigmoid, days_elapsed, y, p0 = p0[isdeaths])
            results["popt"][kind][loc] = popt
            results["pcov"][kind][loc] = pcov

            axs[isdeaths].scatter(x, y, color = colors[loc][0])
            y_predicted = [fsigmoid(x_i, popt[0], popt[1], popt[2]) for x_i in x]
            axs[isdeaths].plot(x, y_predicted, color = colors[loc][1])

            label = "{}: {}/(1 + exp({:.3f}*(x-{})))".format(loc, round(popt[2], 1), round(popt[0], 3), round(popt[1], 1))
            label += "; R2 = {}".format(round(r2_score(y, y_predicted), 4))
            results["labels"][kind][loc] = label
            patches.append(mpatches.Patch(color=colors[loc][1], label=label))
        axs[isdeaths].legend(handles=patches)
        axs[isdeaths].grid(axis='y')
        axs[isdeaths].set(ylabel=kind.capitalize())
    axs[1].set(xlabel="Days elapsed")

    # Plot cases and death fits
    plt.show()
    return

def display_ebola_quadratic_samples(r2s_evdef):
    # Load data from CSV
    data = pd.read_csv("data/ebola_2014_data.csv")

    # Convert dates from strings to datetimes
    dates = data["date"][::-1].values
    datetimes = [datetime.datetime.strptime(datestr, '%d %b %Y') for datestr in dates]  # date of month, month abbrev., year
    days_elapsed = [(date - min(datetimes)).days for date in datetimes]

    # for each kind/loc combo, for each 6-long range of in-window reports, compute an R2 for a quadratic fit
    r2s = []
    for kind in ["cases", "deaths"]:
        for loc in ["total", "guinea", "liberia", "sierraleone"]:
            y_full = data["{}_{}".format(kind,loc)][::-1].values
            for i_min in [x for x in range(len(days_elapsed) - 7) if days_elapsed[x] <= X_CAPS[kind][loc] and y_full[x] > 0]:
                x = np.array(days_elapsed[i_min:i_min+7]).reshape(-1,1)
                y = y_full[i_min:i_min+7]
                poly = PolynomialFeatures(degree = 2)
                x_poly = poly.fit_transform(x)
                poly.fit(x_poly, y)
                lin2_reg = LinearRegression().fit(x_poly, y)

                r2 = lin2_reg.score(poly.fit_transform(x), y)
                r2s.append(r2)
    x = np.array(r2s)
    np.random.seed(0)  # make the jitter deterministic
    y = np.random.normal(0, 1, len(x))
    fig, ax = plt.subplots()
    fig.suptitle("Quadratic fit R2 distribution comparison between selected\n7-long Ebola epidemic windows and China [7-long] Feb 2 fits")
    ax.set_xscale('logit')
    ax.set_yticks([])
    ax.scatter(x, y, alpha=0.4)
    ax.set_ylim([-10, 10])
    ax.xaxis.set_major_formatter(FormatStrFormatter('%.4g'))
    ax.xaxis.set_minor_formatter(NullFormatter())
    ax.set_xticks([floor(min(r2s)*10.0) / 10, 0.9, 0.99, 0.999, 0.9999])
    # add the two points for @evdefender's cases and deaths R2 scores
    x = np.array([r2s_evdef["cases"], r2s_evdef["deaths"]])
    y = np.zeros_like(x)
    ax.scatter(x, y, color='red', marker='^')
    ax.annotate("Feb 2 China\ncases R2", (x[0], y[0]), textcoords="offset pixels", xytext=(0,-40), ha="center", color="red", fontsize=9, weight="bold")
    ax.annotate("Feb 2 China\ndeaths R2", (x[1], y[1]), textcoords="offset pixels", xytext=(0,-40), ha="center", color="red", fontsize=9, weight="bold")
    plt.show()
    return

def display_ebola_quadratic_split_samples(r2s_evdef):
    # Load data from CSV
    data = pd.read_csv("data/ebola_2014_data.csv")

    # Convert dates from strings to datetimes
    dates = data["date"][::-1].values
    datetimes = [datetime.datetime.strptime(datestr, '%d %b %Y') for datestr in dates]  # date of month, month abbrev., year
    days_elapsed = [(date - min(datetimes)).days for date in datetimes]

    # We'll color-code each R2 dot in the scatterplots, by **min** counts observed in its window; higher is better
    SAMPLE_QUALITY_COLOR = [
            (1, "brown"),
            (50, "blue"),
            (2500, "green"),
            ]

    fig, axs = plt.subplots(2, 4, sharex=True)  # 2 rows, 4 cols, 8 plots total
    fig.suptitle("Breakdown of quadratic fit R2 distribution comparison by\nkind, region, and min observation in window")
    for plot_row, kind in enumerate(["cases", "deaths"]):
        axs[plot_row][0].set(ylabel=kind)
        for plot_col, loc in enumerate(["total", "guinea", "liberia", "sierraleone"]):
            axs[1][plot_col].set(xlabel=loc)
            y_full = data["{}_{}".format(kind,loc)][::-1].values
            r2s = []
            colors = []
            for i_min in [x for x in range(len(days_elapsed) - 7) if days_elapsed[x] <= X_CAPS[kind][loc] and y_full[x] > 0]:
                # color it based on quality (e.g. >=1, >=5, >= 20 entries)
                # repeat analysis above but in a 4x2 (regions x cases/deaths) plot grid, and color-code scatterpoints
                x = np.array(days_elapsed[i_min:i_min+7]).reshape(-1,1)
                y = y_full[i_min:i_min+7]
                color = max([pair for pair in SAMPLE_QUALITY_COLOR if min(y) >= pair[0]])[1]
                colors.append(color)
                poly = PolynomialFeatures(degree = 2)
                x_poly = poly.fit_transform(x)
                poly.fit(x_poly, y)
                lin2_reg = LinearRegression().fit(x_poly, y)
                r2 = lin2_reg.score(poly.fit_transform(x), y)
                r2s.append(r2)
            x = np.array(r2s)
            np.random.seed(0)  # make the jitter deterministic
            y = np.random.normal(0, 1, len(x))
            axs[plot_row][plot_col].set_xscale('logit')
            axs[plot_row][plot_col].set_yticks([])
            axs[plot_row][plot_col].scatter(x, y, alpha=0.4, color=colors)
            axs[plot_row][plot_col].set_ylim([-10, 10])
            axs[plot_row][plot_col].xaxis.set_major_formatter(FormatStrFormatter('%.4g'))
            axs[plot_row][plot_col].xaxis.set_minor_formatter(NullFormatter())
            axs[plot_row][plot_col].set_xticks([0.7, 0.9, 0.99, 0.999, 0.9999])
            x = np.array([r2s_evdef[kind]])
            y = np.zeros_like(x)
            axs[plot_row][plot_col].scatter(x, y, color='red', marker='^')  # add R2 from China dataset for corresponding kind (cases, deaths)
            axs[plot_row][plot_col].annotate("China R2", (x[0], y[0]), textcoords="offset pixels", xytext=(0,-20), ha="center", color="red", fontsize=9, weight="bold")
    patches = [mpatches.Patch(color=pair[1], label='All observations in window >= {}'.format(pair[0])) for pair in SAMPLE_QUALITY_COLOR]
    fig.legend(handles=patches)
    plt.show()

if __name__ == "__main__":
    r2s_evdef = display_evdefender_plots()
    display_ebola_plots()
    display_ebola_quadratic_samples(r2s_evdef)
    display_ebola_quadratic_split_samples(r2s_evdef)


import numpy as np
from numba import jit
# from scipy.stats import norm

# @jit
# def pdf_skewnormal(x, location=0.0, scale=1.0, shape=0.0):
#     scale = scale ** 2
#     t = (x - location) / scale
#     return 2.0 / scale * norm.pdf(t) * norm.cdf(shape * t)

def backGroundPlaneEstimation(x, y, A, B, C):
    return A * x + B * y + C

def gauss2D(x, y, Amp, x_0, y_0, sigma_x, sigma_y, theta):
    theta = np.deg2rad(theta)
    a = np.cos(theta) ** 2 / (2 * sigma_x ** 2) + np.sin(theta) ** 2 / (2 * sigma_y ** 2)
    b = -np.sin(2 * theta) / (4 * sigma_x ** 2) + np.sin(2 * theta) / (4 * sigma_y ** 2)
    c = np.sin(theta) ** 2 / (2 * sigma_x ** 2) + np.cos(theta) ** 2 / (2 * sigma_y ** 2)
    g = Amp * np.exp(-(a * (x - x_0) ** 2 + 2 * b * (x - x_0) * (y - y_0) + c * (y - y_0) ** 2))
    return g

@jit
def fitFunc(xy, Amp, x_0, y_0, sigma_x, sigma_y, theta, A, B, C):
    x, y = xy
    g = gauss2D(x, y, Amp, x_0, y_0, sigma_x, sigma_y, theta)
    g += backGroundPlaneEstimation(x, y, A, B, C)
    return g

def NGauss(numOfGauss):
    def makeNGauss(xy, *parameters):
        xi, yi = xy
        g = 0
        gaussParams = parameters[3:]
        g += backGroundPlaneEstimation(xi, yi, parameters[0], parameters[1], parameters[2])
        for i in range(numOfGauss):
            g += gauss2D(xi, yi, gaussParams[i * 6], gaussParams[i * 6 + 1], gaussParams[i * 6 + 2],
                         gaussParams[i * 6 + 3], gaussParams[i * 6 + 4], gaussParams[i * 6 + 5])
        return g

    return makeNGauss

# @jit
# def fit2GaussFunc(xy, Amp_1, x_0_1, y_0_1, sigma_x_1, sigma_y_1, theta_1, Amp_2, x_0_2, y_0_2, sigma_x_2, sigma_y_2,
#                   theta_2, A, B, C):
#     x, y = xy
#     g = gauss2D(x, y, Amp_1, x_0_1, y_0_1, sigma_x_1, sigma_y_1, theta_1)
#     g += gauss2D(x, y, Amp_2, x_0_2, y_0_2, sigma_x_2, sigma_y_2, theta_2)
#     g += backGroundPlaneEstimation(x, y, A, B, C)
#     return g


from astropy.coordinates import Angle
from numpy import cos, sin, array

def rotation_matrix_2d(angle):
    """construct a 2D rotation matrix as a numpy NDArray that rotates a
    vector clockwise. Angle should be any object that can be converted
    into an `astropy.coordinates.Angle`
    """
    psi = Angle(angle).rad
    return array([[cos(psi), -sin(psi)],
                  [sin(psi),  cos(psi)]])


import numpy as np
from sklearn.datasets import load_breast_cancer

def activation(z):
    return 1 / (1 + np.exp(-z))

def activationDerivative(z):
    tmp = activation(z)

    return np.multiply(tmp, 1 - tmp)

def feedforward(x, Weight, bias):

    layer = len(Weight) - 1
    a = x

    for l in range(1, layer + 1):
        z = Weight[l].T * a + bias[l]
        a = activation(z)

    return a

def costFunction(y, predicted_y):
    return -((1 - y) * np.log(1 - predicted_y) + y * np.log(predicted_y))

def normalize(X):
    """
    Normalize each input feature

    :param X:
    :return:
    """

    number_examples = X.shape[0]

    X_normalized = X - np.tile(np.mean(X, 0), [number_examples, 1])
    X_normalized = np.divide(X_normalized, np.tile(np.std(X_normalized, 0), [number_examples, 1]))

    return X_normalized

'''
Step 0: Load the training dataset
'''

dataset = load_breast_cancer()
y = np.matrix(dataset.target).T
X = np.matrix(dataset.data)

number_examples = X.shape[0]
number_features = X.shape[1]

# Normalize each input feature
X_new = normalize(X)

print("Dimension of X (normalized): {}".format(X_new.shape))
print("Dimension of y: {}".format(y.shape))
print('\n')

'''
Step 1: Construct a neural network as follows:
- 4 layers with sigmoid activation functions
- 6 units in 1st hidden layer
- 5 units in 2nd hidden layer
'''

hidden2 = 5
hidden1 = 6

# Dimension of np.matrix(Weights): 1x4
Weights = [[], np.random.normal(0, 0.1, [number_features, hidden1]),
           np.random.normal(0, 0.1, [hidden1, hidden2]),
           np.random.normal(0, 0.1, [hidden2, 1])]

# Dimension of np.matrix(Biases): 1x4
Biases = [[], np.random.normal(0, 0.1, [hidden1, 1]),
          np.random.normal(0, 0.1, [hidden2, 1]),
          np.random.normal(0, 0.1, [1, 1])]

number_layers = len(Weights) - 1

'''
Step 2: Implement the stochastic gradient descent with:
- learning rate = 0.01
- max iteration = 1,000
'''

learning_rate = 0.01
max_iteration = 1000

for iteration in range(0, max_iteration):
    loss_this_iter = 0
    order = np.random.permutation(number_examples)

    for i in range(0, number_examples):

        # Grab the pattern order[i]
        x_sample = X_new[order[i], :].T
        y_sample = y[order[i], 0]

        # Feed forward step
        activation_arrays = [x_sample]
        z_arr = [[]]
        delta = [[]]

        gradient_weights = [[]]
        gradient_biases = [[]]

        for l in range(1, number_layers + 1):
            z_arr.append(Weights[l].T * activation_arrays[l - 1] + Biases[l])
            activation_arrays.append(activation(z_arr[l]))

            # Just to give arrays the right shape for the backprop step
            delta.append([])
            gradient_weights.append([])
            gradient_biases.append([])

        loss_this_sample = costFunction(y_sample, activation_arrays[number_layers][0, 0])
        loss_this_iter = loss_this_iter + loss_this_sample

        # Backprop step
        delta[number_layers] = activation_arrays[number_layers] - y_sample

        for l in range(number_layers, 0, -1):
            gradient_biases[l] = delta[l].copy()
            gradient_weights[l] = activation_arrays[l - 1] * delta[l].T

            if l > 1:
                delta[l - 1] = np.multiply(activationDerivative(z_arr[l - 1]),
                                           Weights[l] * delta[l])

        for l in range(1, number_layers + 1):
            Weights[l] = Weights[l] - learning_rate * gradient_weights[l]
            Biases[l] = Biases[l] - learning_rate * gradient_biases[l]

    print('\n')
    print('Iteration %d loss %f' % (iteration, loss_this_iter))


import sympy as sp
import matplotlib.pyplot as plt

def lagrange (x_array, y_array):
    x = sp.symbols('x')
    result = 0
    for i in range(len(x_array)):
        temp = 1
        for j in range(len(x_array)):
            if j != i:
                temp *= ((x - x_array[j]) / (x_array[i] - x_array[j]))
        result += temp * y_array[i]
    return sp.simplify(result)

x_array = [0.1, 0.3, 0.5, 0.7, 0.9, 1.1]
y_array = [18.559624, 22.000392, 25.925000, 30.007048, 33.505416, 31.895624]

result = lagrange(x_array, y_array)
print(result)

x = sp.symbols("x")
sp.plot(result, (x, 0.1, 1.1))


"""Moran process."""
import numpy as np

from stochastic.processes.base import BaseSequenceProcess

class MoranProcess(BaseSequenceProcess):
    """Moran process.

    .. image:: _static/moran_process.png
        :scale: 50%

    A neutral drift Moran process, typically used to model populations. At
    each step this process will increase by one, decrease by one, or remain
    at the same value between values of zero and the number of
    states, :math:`n`. The process ends when its value reaches zero or the
    maximum valued state.

    :param int maximum: the maximum possible value for the process.
    :param numpy.random.Generator rng: a custom random number generator
    """

    def __init__(self, maximum, rng=None):
        super().__init__(rng=rng)
        self.maximum = maximum
        self.p = self._probabilities(maximum)

    def __str__(self):
        return "Moran process with %s states" % self._maximum

    def __repr__(self):
        return "MoranProcess(maximum={n})".format(n=str(self.maximum))

    @property
    def maximum(self):
        """Maximum value."""
        return self._maximum

    @maximum.setter
    def maximum(self, value):
        if not isinstance(value, int):
            raise TypeError("Number of states must be an integer.")
        if value <= 2:
            raise ValueError("Number of states must be at least 3.")
        self._maximum = value

    def _probabilities(self, n):
        """Generate the transition probabilities for state :math:`n`.

        :param int n: the current state for which to generate transition
            probabilities.
        """
        probabilities = []
        for k in range(1, n):
            p_down = 1.0 * (n - k) / n * k / n
            p_up = 1.0 * k / n * (n - k) / n
            p_same = 1.0 - p_down - p_up
            probabilities.append([p_down, p_same, p_up])

        return probabilities

    def _sample_moran_process(self, n, start):
        """Generate a realization of the Moran process.

        Generate a Moran process until absorption occurs (state 0 or n) or
        length of process reaches length :math:`maximum`.
        """
        if not isinstance(start, int):
            raise TypeError("Initial state must be a positive integer.")
        if start < 0 or start > self.maximum:
            raise ValueError("Initial state must be between 0 and " + str(self.maximum))

        if not isinstance(n, int):
            raise TypeError("Sample length must be positive integer.")
        if n < 1:
            raise ValueError("Sample length must be at least 1.")

        s = [start]
        increments = [-1, 0, 1]
        for k in range(n - 1):
            if start in [0, self.maximum]:
                break
            start += self.rng.choice(increments, p=self.p[start - 1])
            s.append(start)

        return np.array(s)

    def sample(self, n, start):
        """Generate a realization of the Moran process.

        Generate a Moran process until absorption occurs (state 0
        or :py:attr:`maximum`) or length of process reaches length :math:`n`.

        :param int n: the maximum number of steps to generate assuming
            absorption does not occur.
        :param int start: the initial state of the process.
        """
        return self._sample_moran_process(n, start)


# exponential distribution

# used for describing time till next event e.g failure / success etc.

# It has two parameters.
# scale - inverse of rate (see lam in poisson distribution) defaults to 1.0
# size - The shape of the returned array

# draw out a sample for exponential distribution with 2.0 scale with 2x3 size

from numpy import random

x = random.exponential(scale = 2, size = (2,3))

print(x)

# visualization of exponential distribution

# from numpy import random
import matplotlib.pyplot as plt
import seaborn as  sns

sns.distplot(random.exponential(size = 1000), hist = False)
plt.show

# Relationship between Poisson and exponential distribution

# poisson distribution deals with the number of occurences of an event in a time peroid
# whereas exponential distribution deals with the time between these events


"""
https://en.wikipedia.org/wiki/Rayleigh_quotient
"""
from typing import Any

import numpy as np

def is_hermitian(matrix: np.ndarray) -> bool:
    """
    Checks if a matrix is Hermitian.
    >>> import numpy as np
    >>> A = np.array([
    ... [2,    2+1j, 4],
    ... [2-1j,  3,  1j],
    ... [4,    -1j,  1]])
    >>> is_hermitian(A)
    True
    >>> A = np.array([
    ... [2,    2+1j, 4+1j],
    ... [2-1j,  3,  1j],
    ... [4,    -1j,  1]])
    >>> is_hermitian(A)
    False
    """
    return np.array_equal(matrix, matrix.conjugate().T)

def rayleigh_quotient(A: np.ndarray, v: np.ndarray) -> Any:
    """
    Returns the Rayleigh quotient of a Hermitian matrix A and
    vector v.
    >>> import numpy as np
    >>> A = np.array([
    ... [1,  2, 4],
    ... [2,  3,  -1],
    ... [4, -1,  1]
    ... ])
    >>> v = np.array([
    ... [1],
    ... [2],
    ... [3]
    ... ])
    >>> rayleigh_quotient(A, v)
    array([[3.]])
    """
    v_star = v.conjugate().T
    v_star_dot = v_star.dot(A)
    assert isinstance(v_star_dot, np.ndarray)
    return (v_star_dot.dot(v)) / (v_star.dot(v))

def tests() -> None:
    A = np.array([[2, 2 + 1j, 4], [2 - 1j, 3, 1j], [4, -1j, 1]])
    v = np.array([[1], [2], [3]])
    assert is_hermitian(A), f"{A} is not hermitian."
    print(rayleigh_quotient(A, v))

    A = np.array([[1, 2, 4], [2, 3, -1], [4, -1, 1]])
    assert is_hermitian(A), f"{A} is not hermitian."
    assert rayleigh_quotient(A, v) == float(3)

if __name__ == "__main__":
    import doctest

    doctest.testmod()
    tests()


#! /usr/bin/env python

import numpy as np
from scipy.optimize import leastsq
from leastsqbound import leastsqbound

def func(p, x):
    """model data as y = m*x+b """
    m, b = p
    return m * np.array(x) + b

def err(p, y, x):
    return y - func(p, x)

# extract data
temp = np.genfromtxt("sample_data.dat")
x = temp[:, 0]
y = temp[:, 1]

# perform unbounded least squares fitting
p0 = [1.0, 0.0]
p, cov_x, infodic, mesg, ier = leastsq(err, p0, args=(y, x), full_output=True)

# print out results
print "Standard Least Squares fitting results:"
print "p:", p
print "cov_x:", cov_x
print "infodic['nfev']:", infodic['nfev']
print "infodic['fvec']:", infodic['fvec']
print "infodic['fjac']:", infodic['fjac']
print "infodic['ipvt']:", infodic['ipvt']
print "infodic['qtf']:", infodic['qtf']
print "mesg:", mesg
print "ier:", ier
print ""

# same as above using no bounds
p0 = [1.0, 0.0]
p, cov_x, infodic, mesg, ier = leastsqbound(err, p0, args=(y, x),
                                            full_output=True)

# print out results
print "Bounded Least Squares fitting with no bounds results:"
print "p:", p
print "cov_x:", cov_x
print "infodic['nfev']:", infodic['nfev']
print "infodic['fvec']:", infodic['fvec']
print "infodic['fjac']:", infodic['fjac']
print "infodic['ipvt']:", infodic['ipvt']
print "infodic['qtf']:", infodic['qtf']
print "mesg:", mesg
print "ier:", ier
print ""

# perform bounded least squares fitting
p0 = [1.0, 0.0]
bounds = [(0.0, 2.0), (-10.0, 10.0)]
p, cov_x, infodic, mesg, ier = leastsqbound(err, p0, args=(y, x),
                                            bounds = bounds, full_output=True)

# print out results
print "Bounded Least Squares fitting results:"
print "p:", p
print "cov_x:", cov_x
print "infodic['nfev']:", infodic['nfev']
print "infodic['fvec']:", infodic['fvec']
print "infodic['fjac']:", infodic['fjac']
print "infodic['ipvt']:", infodic['ipvt']
print "infodic['qtf']:", infodic['qtf']
print "mesg:", mesg
print "ier:", ier


import sys
import os
from scipy import stats
import tenseal as ts
import numpy as np
from scipy.stats import t

sys.path.append(os.path.abspath('../../tensealstat'))

from tensealstat.tools_context import ToolsContext as tc
from tensealstat.algebra.algebra_numpy import AlgebraNumpy
from tensealstat.algebra.algebra_tenseal import AlgebraTenseal
from tensealstat.statistic.student_t_equal_variance import StudentTEqualVariance

statistic_generator = StudentTEqualVariance()
#
# This test follows https://en.wikipedia.org/w/index.php?title=Welch%27s_t-test&oldid=994214781#Examples
# Example 1
#

# 1 done by the key holder
context = tc.get_context_default()
algebra_tenseal = AlgebraTenseal(context)
algebra_numpy = AlgebraNumpy()

# 2 done by the data holders
sample_0 = np.array([27.5, 21.0, 19.0, 23.6, 17.0, 17.9, 16.9, 20.1, 21.9, 22.6, 23.1, 19.6, 19.0, 21.7, 21.4])
sample_1 = np.array([27.1, 22.0, 20.8, 23.4, 23.4, 23.5, 25.8, 22.0, 24.8, 20.2, 21.9, 22.1, 22.9, 20.5, 24.4])

list_sample = [sample_0, sample_1]
list_sample_encrypted = [algebra_tenseal.encrypt_vector(sample) for sample in list_sample]

# 3 done by the agregator
statistic_encrypted = statistic_generator.encrypt_statistic(algebra_tenseal, list_sample_encrypted)

# # 4 done by the key holder
t_statistic, degrees_of_freedom = statistic_generator.decrypt_statistic(algebra_tenseal, statistic_encrypted)
p_value = t.cdf(t_statistic, degrees_of_freedom)
# Assert.IsTrue(chi2_statistic ~= -2.46)
# Assert.IsTrue(p_value ~= 0.010)
print('')
print('via tensealstattest')
print('t_statistic: ' + str(t_statistic))
print('p_value: ' + str(p_value))

# Test version
statistic_encrypted = statistic_generator.encrypt_statistic(algebra_numpy, list_sample)
t_statistic, degrees_of_freedom = statistic_generator.decrypt_statistic(algebra_numpy, statistic_encrypted)
p_value = t.cdf(t_statistic, degrees_of_freedom)
# Assert.IsTrue(chi2_statistic ~= -2.46)
# Assert.IsTrue(p_value ~= 0.010)
print('')
print('via tensealstat')
print('t_statistic: ' + str(t_statistic))
print('p_value: ' + str(p_value))

t_statistic, p_value = stats.ttest_ind(sample_0, sample_1, equal_var=True)
print('')
print('via scipy')
print('t_statistic: ' + str(t_statistic))
print('p_value: ' + str(p_value / 2))


"""Exactly solvable single-electron hydrogen-like atom."""
from typing import Callable, Union

import flax
import jax.numpy as jnp

import vmcnet.models as models
import vmcnet.physics as physics
from vmcnet.utils.typing import Array, P, ModelApply

class HydrogenLikeWavefunction(models.core.Module):
    """Model which computes -decay_rate * r, with trainable decay_rate.

    This model returns log|psi(x)|, where psi is the 1-s orbital exp(-decay_rate * r).
    This psi is an eigenfunction of the hydrogen-like Hamiltonian (one ion and one
    electron) when the decay rate is equal to 2 * nuclear_charge / (d - 1), with d > 1,
    where d is the dimension of the system.

    Thus when the system is 3-d, then this model computes the ground-state wavefunction
    precisely when decay_rate = nuclear_charge.

    Attributes:
        decay_rate (jnp.float32): initial decay rate in the model
    """

    init_decay_rate: jnp.float32

    @flax.linen.compact
    def __call__(self, x: Array) -> Array:  # type: ignore[override]
        """Log of isotropic exponential decay. Computes -decay_rate * ||x||.

        Args:
            x (Array): single electron positions, of shape (..., 1, d), where d
                is the dimension of the system

        Returns:
            Array: log of exponential decay wavefunction, with shape x.shape[:-2]
        """
        r = jnp.linalg.norm(x, axis=-1)
        scaled_r = models.core.Dense(
            1,
            kernel_init=lambda key, shape, **kwargs: jnp.array(
                [[self.init_decay_rate]]
            ),
            use_bias=False,
        )(r)
        return -jnp.squeeze(scaled_r, axis=-1)

def make_hydrogen_like_local_energy(
    log_psi_apply: Callable[[P, Array], Union[jnp.float32, Array]],
    charge: jnp.float32,
    d: int = 3,
) -> ModelApply[P]:
    """Local energy calculation for the hydrogen-like atom in general dimension d.

    Args:
        log_psi_apply (Callable): a function which computes log|psi(x)| for single
            inputs x. It is okay for it to produce batch outputs on batches of x as long
            as it produces a single number for single x. Has the signature
            (params, single_x_in) -> log|psi(single_x_in)|
        charge (jnp.float32): charge of the nucleus
        d (int, optional): Dimension of the system (number of coordinates that each
            ion and electron has). Defaults to 3.

    Returns:
        Callable: local energy function which computes -0.5 nabla^2 psi / psi + (Z / r),
        where psi is the wavefn, and Z is the charge of the nucleus. Has the signature
        (params, x) -> local energy array of shape (x.shape[0],)
    """
    ion_location = jnp.zeros((1, d))
    ion_charge = jnp.array([charge])

    kinetic_fn = physics.kinetic.create_continuous_kinetic_energy(log_psi_apply)
    potential_fn = physics.potential.create_electron_ion_coulomb_potential(
        ion_location, ion_charge
    )

    return physics.core.combine_local_energy_terms([kinetic_fn, potential_fn])


import numpy as np

def generate_data(ym_per_pix, xm_per_pix):
    '''
    Generates fake data to use for calculating lane curvature.
    In your own project, you'll ignore this function and instead
    feed in the output of your lane detection algorithm to
    the lane curvature calculation.
    '''
    # Set random seed number so results are consistent for grader
    # Comment this out if you'd like to see results on different random data!
    np.random.seed(0)
    # Generate some fake data to represent lane-line pixels
    ploty = np.linspace(0, 719, num=720)  # to cover same y-range as image
    quadratic_coeff = 3e-4  # arbitrary quadratic coefficient
    # For each y position generate random x position within +/-50 pix
    # of the line base position in each case (x=200 for left, and x=900 for right)
    leftx = np.array([200 + (y**2)*quadratic_coeff + np.random.randint(-50, high=51)
                      for y in ploty])
    rightx = np.array([900 + (y**2)*quadratic_coeff + np.random.randint(-50, high=51)
                       for y in ploty])

    leftx = leftx[::-1]  # Reverse to match top-to-bottom in y
    rightx = rightx[::-1]  # Reverse to match top-to-bottom in y

    # Fit a second order polynomial to pixel positions in each fake lane line
    # Fit new polynomials to x,y in world space
    left_fit_cr = np.polyfit(ploty*ym_per_pix, leftx*xm_per_pix, 2)
    right_fit_cr = np.polyfit(ploty*ym_per_pix, rightx*xm_per_pix, 2)

    return ploty, left_fit_cr, right_fit_cr

def measure_curvature_real():
    '''
    Calculates the curvature of polynomial functions in meters.
    '''
    # Define conversions in x and y from pixels space to meters
    ym_per_pix = 30/720  # meters per pixel in y dimension
    xm_per_pix = 3.7/700  # meters per pixel in x dimension

    # Start by generating our fake example data
    # Make sure to feed in your real data instead in your project!
    ploty, left_fit_cr, right_fit_cr = generate_data(ym_per_pix, xm_per_pix)

    # Define y-value where we want radius of curvature
    # We'll choose the maximum y-value, corresponding to the bottom of the image
    y_eval = np.max(ploty)

    # Calculation of R_curve (radius of curvature)
    left_curverad = ((1 + (2*left_fit_cr[0]*y_eval*ym_per_pix +
                           left_fit_cr[1])**2)**1.5) / np.absolute(2*left_fit_cr[0])
    right_curverad = ((1 + (2*right_fit_cr[0]*y_eval*ym_per_pix +
                            right_fit_cr[1])**2)**1.5) / np.absolute(2*right_fit_cr[0])

    return left_curverad, right_curverad

# Calculate the radius of curvature in meters for both lane lines
left_curverad, right_curverad = measure_curvature_real()

print(left_curverad, 'm', right_curverad, 'm')
# Should see values of 533.75 and 648.16 here, if using
# the default `generate_data` function with given seed number


import matplotlib
import numpy as np

def decoderBEC(RecievedM,ParityCheckMatrix):
    alpha=RecievedM
    n=len(RecievedM)
    syndrome=np.dot(ParityCheckMatrix,RecievedM)

    for z in range(50):
        tempalpha=alpha
        for i in range(0,n):
            if alpha[i]==-1:
                beta=np.zeros(n)
                SyndromeCount=0
                for j in range(0,np.size(ParityCheckMatrix,0)):
                    beta[0]=alpha[i]

                    if ParityCheckMatrix[j][i]==1:
                        SyndromeCount=SyndromeCount+1
                        sum=0
                        for k in range(0,n):
                            if ParityCheckMatrix[j][k]==1 and k!=i:
                                sum+=alpha[k]
                        sum=sum%2
                        beta[SyndromeCount]=sum

                Bincount=np.zeros(2)
                for  t in range(0,SyndromeCount+1):
                    if beta[t]==0:
                        Bincount[0]+=1
                    else:
                        Bincount[1]+=1

                if (Bincount[0]==Bincount[1]):
                    if (beta[0]==1):
                        tempalpha[i]=0
                    else:
                        tempalpha[i]=0

                elif Bincount[0]>Bincount[1]:
                   tempalpha[i]=0

                elif Bincount[0]<Bincount[1]:
                    tempalpha[i]=1

        alpha=tempalpha
        syndrome=(np.dot(ParityCheckMatrix,alpha))%2
        IsSyndromZero=np.count_nonzero(syndrome)

        if IsSyndromZero==0:
            break

    return alpha


import numpy as np

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

def relu(x):
    return np.maximum(0,x)

def tanh(x):
    return 2*sigmoid(2*x)-1

def leakyrelu(x,alpha=0.01):
    return np.maximum(alpha*x,x)

def elu(x,alpha=1):
    return np.where(x<0,alpha*(np.exp(x)-1),x)


import numpy as np

def compress_image(image, num_values):
    """Compress an image using SVD and keeping the top `num_values` singular values.

    Args:
        image: numpy array of shape (H, W)
        num_values: number of singular values to keep

    Returns:
        compressed_image: numpy array of shape (H, W) containing the compressed image
        compressed_size: size of the compressed image
    """
    compressed_image = np.zeros_like(image)
    compressed_size = 0

    # Steps:
    #     1. Get SVD of the image
    #     2. Only keep the top `num_values` singular values, and compute `compressed_image`
    #     3. Compute the compressed size

    # 1. Get SVD of the image
    U, S, Vt = np.linalg.svd(image)
    # sigma = np.zeros((image.shape))
    # np.fill_diagonal(sigma, S)
    # print(np.isclose(U @ sigma @ Vt, image)) # True,...

    # 2. Only keep the top `num_values` singular values, and compute `compressed_image`
    for i in range(num_values):
        u = np.expand_dims(U[:,i], axis=1)    # m * 1
        s = S[i]
        v = np.expand_dims(Vt[i,:], axis=0)  # 1 * n
        compressed_image += (u*s).dot(v)      # plus partial A

    # 3. Compute the compressed size
    compressed_size = (U.shape[0] + 1 + Vt.shape[0]) * num_values   # (m + 1 + n) * k

    assert compressed_image.shape == image.shape, \
           "Compressed image and original image don't have the same shape"

    assert compressed_size > 0, "Don't forget to compute compressed_size"

    return compressed_image, compressed_size


from typing import Sequence

import numpy as np
import scipy.sparse as sp
from scipy.sparse.linalg.eigen.arpack import eigsh

def renormalize_sparse(A: sp.spmatrix) -> sp.spmatrix:
    """Get (D**-0.5) * A * (D ** -0.5), where D is the diagonalized row sum."""
    A = sp.coo_matrix(A)
    A.eliminate_zeros()
    rowsum = np.array(A.sum(1))
    assert np.all(rowsum >= 0)
    d_inv_sqrt = np.power(rowsum, -0.5).flatten()
    d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0.0
    d_mat_inv_sqrt = sp.diags(d_inv_sqrt)
    return d_mat_inv_sqrt.dot(A).dot(d_mat_inv_sqrt)
    # return A.dot(d_mat_inv_sqrt).transpose().dot(d_mat_inv_sqrt).tocoo()

def chebyshev_polynomials(A: sp.spmatrix, k: int) -> Sequence[sp.spmatrix]:
    """
    Calculate Chebyshev polynomials up to order k.

    Args:
        A: input sparse matrix
        k: order of chebyshev polynomial

    Returns:
        k+1 sparse matrices
    """

    A = renormalize_sparse(A)
    N = A.shape[0]
    laplacian = sp.eye(N) - A
    largest_eigval, _ = eigsh(laplacian, 1, which="LM")
    scaled_laplacian = (2.0 / largest_eigval[0]) * laplacian - sp.eye(N)

    t_k = [sp.eye(N), scaled_laplacian]
    rescaled_laplacian = (2 * scaled_laplacian).tocsr(copy=False)

    for _ in range(2, k + 1):
        t_k.append(rescaled_laplacian.dot(t_k[-1]) - t_k[-2])

    return [t.tocoo(copy=False) for t in t_k]


import numpy as np

class Parabolic(object):
    def __init__(self, focal_length, depth, height):
        self.a = self.focal_length_to_x_squared_coeff(focal_length)
        self.depth = depth
        self.height = height

    def set_x_offset(self, x_offset):
        self.c = x_offset

    def prop(self, eqns):
        a = self.a
        c = self.c

        result_ms = []
        result_bs = []
        result_xs = []
        result_ys = []
        result_freqs = []
        result_colors = []

        for eq in eqns:
            m, b, old_x, old_y, freq = eq

            if m == 0:
                # The special case is super easy
                y0 = b
                x0 = a * y0 * y0 + c
            else:
                # Newton's Method
                y0 = b
                for i in xrange(1):
                    fy = a * y0 * y0 - (1.0 / m) * y0 + c + b / m
                    fydot = 2 * a * y0 - (1.0 / m)
                    y1 = y0 - fy / fydot
                    y0 = y1

                x0 = a * y0 * y0 + c

            # Okay we've found the intercept, let's reflect.
            # First step is to find the slope of the reflected line
            slope_inverse = 2 * a * y0
            if slope_inverse == 0:
                # Another special case: the parabola is perfectly vertical here
                # All we gotta do is reflect off a vertical line, which amounts
                # to flipping the sign of the slope
                final_m = -m

            else:
                M1 = 1 / slope_inverse
                M2 = m
                M3 = (M1 * M1 * M2 + 2 * M1 - M2) / (1 + 2 * M1 * M2 - M1 * M1)
                # print "slope", slope
                final_m = M3

            # Great, just plug in the slope and a know point on the line
            # To find the y intercept
            final_b = y0 - final_m * x0

            # print "Intercept:", x0,  y0
            # print "m:", final_m
            # print "b:", final_b

            result_ms.append(final_m)
            result_bs.append(final_b)
            result_xs.append(x0)
            result_ys.append(y0)
            result_freqs.append(freq)

        return np.array(zip(
            result_ms, result_bs, result_xs, result_ys, result_freqs))

    def focal_length_to_x_squared_coeff(self, focal_length):
        '''
        ax^2 = focal_length
        2ax = 1
        x = 1 / (2a)
        x^2 = 1 / 4a^2 = 1/a * 1/4a
        a * 1/a * 1/4a = focal_length
        1/4a = focal_length
        4a = 1 / focal_length
        a = 1 / (4 * focal_length)
        '''
        return 1.0 / (4 * focal_length)

    def __str__(self):
        return "Parabolic: x = {} * y * y + {} : {}".format(self.a, self.c, self.height)


import numpy as np

def calculate_tilt_angle(initial_grade_percent):
    tilt_angle = np.arctan(initial_grade_percent / 100)
    tilt_angle = np.degrees(tilt_angle)
    return tilt_angle

def calculate_pipe_upper_invert(pipe_invert_elev, tilt_angle, pipe_length):
    pipe_upper_invert = np.tan(np.radians(tilt_angle)) * pipe_length + pipe_invert_elev
    return pipe_upper_invert

def calculate_upper_liquid_level(lower_liquid_level, tilt_angle, pipe_length):
    upper_liquid_level = lower_liquid_level
    return upper_liquid_level

def calculate_tilt_angle(initial_grade_percent):
    tilt_angle = np.arctan(initial_grade_percent / 100)
    tilt_angle = np.degrees(tilt_angle)

    return tilt_angle

def calculate_pipe_upper_invert(pipe_invert_elev, tilt_angle, pipe_length):
    pipe_upper_invert = np.tan(np.radians(tilt_angle)) * pipe_length + pipe_invert_elev
    return pipe_upper_invert

def calculate_upper_liquid_level(lower_liquid_level, tilt_angle, pipe_length):
    upper_liquid_level = lower_liquid_level
    return upper_liquid_level

def calculate_total_pipe_volume(pipe_diameter, pipe_length):
    pipe_cross_sectional_area = np.pi / 4 * (pipe_diameter ** 2)
    total_volume = pipe_cross_sectional_area * pipe_length
    total_volume = round(total_volume, 2)
    return total_volume

def calculate_sloped_pipe_volume(
    pipe_diameter, tilt_angle, pipe_length, relative_lower_liquid_level
):
    if tilt_angle == 0:
        tilt_angle = 0.001  # angle of zero breaks the np.tan function, so making a close approximation
    pipe_radius = pipe_diameter / 2
    tilt_angle_radians = np.radians(tilt_angle)
    _h0 = relative_lower_liquid_level - (pipe_length * np.tan(tilt_angle_radians))

    # "normal" case, where both ends have some liquid
    if _h0 >= 0:
        _lf = pipe_length

    # upper end has no liquid - liquid does not reach end of pipe
    elif _h0 < 0:
        # lower_liquid_level = 0
        _lf = pipe_length + _h0 / np.tan(tilt_angle_radians)
        _h0 = 0
        # assert _lf < pipe_length, "Liquid fill length cannot be greater than pipe length"

    _h1 = _h0 + _lf * np.tan(tilt_angle_radians)
    full_volume = None
    # Case where pipe is (at least) partially completely full.
    if _h1 > pipe_diameter:
        _lt = (_h1 - pipe_diameter) / np.tan(tilt_angle_radians)
        full_volume = np.pi * pipe_radius ** 2 * _lt
    else:
        _lt = 0

    _pipe_length = _lf - _lt

    _k = 1 - (_h0 / pipe_radius)
    _c = _k - ((_pipe_length * np.tan(tilt_angle_radians)) / pipe_radius)

    assert -1 <= _k <= 1, "k out of range"
    assert -1 <= _c <= 1, "c out of range"

    integral0 = pipe_radius ** 3 / np.tan(tilt_angle_radians)
    integral1 = _k * np.arccos(_k)
    integral2 = (1 / 3) * (np.sqrt(1 - (_k ** 2))) * (_k ** 2 + 2)
    integral3 = _c * np.arccos(_c)
    integral4 = (1 / 3) * (np.sqrt(1 - (_c ** 2))) * (_c ** 2 + 2)
    volume = integral0 * (integral1 - integral2 - integral3 + integral4)

    if (pipe_radius == relative_lower_liquid_level) and (
        tilt_angle == 0
    ):  # Pipe is exactly half full and no slope
        volume = (pipe_length * np.pi * pipe_radius ** 2) / 2
    if full_volume is not None:
        volume = volume + full_volume

    return volume

# initial values
initial_grade_percent = 0.1  # inital grade value
maximum_grade_value = 2  # maximum grade (%) to plot
pipe_length = 50
pipe_diameter = 2
pipe_invert_start = 1  # pipe invert at pipe end
lower_liquid_level = 3  # liquid level at pipe end

relative_lower_liquid_level = np.abs(pipe_invert_start - lower_liquid_level)

no_points_to_plot = 100
no_grades = np.arange(0, maximum_grade_value, 20)
pipe_chainage = np.linspace(0, pipe_length, no_points_to_plot).tolist()
pipe_chainage = [round(elem) for elem in pipe_chainage]

tilt_angle = calculate_tilt_angle(initial_grade_percent)

test_grades = np.linspace(0, 5, 10)

for i in test_grades:
    sloped_volume = calculate_sloped_pipe_volume(
        pipe_diameter, i, pipe_length, relative_lower_liquid_level
    )
    print(sloped_volume)


from math import ceil
import numpy as np
import matplotlib.pyplot as plt

#Ut + aUx =0 gia -2<=x<=3 kai t>=0
#me a.s. U0(x) = 1-|x| gia |x|<=1, 0 diaforetika
#kai sunoriakh U(-2, t)=0. psaxnw sto t=1.6
#akrivhs lush U(x, 1.6) = U0(x-1.6) gia -2<=x-1.6, 0 diaforetika

def u0(x):
	ax = np.abs(x)
	return np.where(ax > 1, 0, 1 - ax)

alpha = 1
a = -2;  b = 3;  J = 49;  h = (b - a) / (J+1)
x = np.linspace(-2, 3, J+2)
lam = 0.8;  k = lam * h;  t = 1.6;  N = ceil(t/k)

U = u0(x);  V = np.zeros(J+2)

for n in range(N):
	for j in range(J+1):
		V[j] = 1/2*( U[j+1] + U[j-1] - lam * alpha * ( U[j+1] - U[j-1] ) )
	for j in range(J+1):
		U[j] = V[j]

plt.plot(x, u0(x-t), 'k-')#, linewidth=1.5)
plt.plot(x, U, 'b-o')
plt.ylim(-0.1, 1.1)
plt.title('$\lambda = %g \quad h = %g \quad t = %g$' % (lam, h, t))
#plt.savefig('upwind.png')
plt.show()

Ex1-oral exercise-Finite Difference-Euler's Method/Code/LaxFriedrichs.py


import numpy as np
import pylab as plt

'''
Performs the Principal Component analysis of the Matrix X
Matrix must be n * m dimensions
where n is # features
m is # examples
'''

def PCA(X, varRetained=0.95, show=False):
    # Compute Covariance Matrix Sigma
    (n, m) = X.shape
    Sigma = 1.0 / m * X * np.transpose(X)
    # Compute eigenvectors and eigenvalues of Sigma
    U, s, V = np.linalg.svd(Sigma, full_matrices=True)

    # compute the value k: number of minumum features that
    # retains the given variance
    sTot = np.sum(s)
    var_i = np.array([np.sum(s[: i + 1]) / \
                      sTot * 100.0 for i in range(n)])
    k = len(var_i[var_i < (varRetained * 100)])
    print
    '%.2f %% variance retained in %d dimensions' \
    % (var_i[k], k)

    # plot the variance plot
    if show:
        plt.plot(var_i)
        plt.xlabel('Number of Features')
        plt.ylabel(' Percentage Variance retained')
        plt.title('PCA $\% \sigma^2 $ vs # features')
        plt.show()

    # compute the reduced dimensional features by projction
    U_reduced = U[:, : k]
    Z = np.transpose(U_reduced) * X

    return Z, U_reduced

import csv as csv
import numpy as np
import Activation, logReg, optim, loadData

#################################################################
# reading from csv
print
'Loading Training Data'
csv_train = csv.reader(open('../data/train.csv', 'rb'))
header = csv_train.next()
data = [[map(int, row[1:]), [int(row[0])]] for row in csv_train]

train = loadData.Data()
train.loadList(data, numClasses=10)
train.NormalizeScale(factor=255.0)

#################################################################
# PCA of training set
print
'Performing PCA - Principal COmponent Analysis'
import npPCA

Z, U_reduced = npPCA.PCA(train.X, varRetained=0.95, show=True)


import numpy as np

from ..density import Distribution
from ..util import interpret_array

class Uniform(Distribution):

    def __init__(self, ndim, sample_range=(0, 1)):
        super().__init__(ndim, True)
        self.low = sample_range[0]
        self.high = sample_range[1]
        self.vol = np.prod(np.diff(sample_range, axis=0))

    def pdf(self, xs):
        xs = interpret_array(xs, self.ndim)
        density = (np.all(xs > self.low, axis=1) *
                   np.all(xs < self.high, axis=1) / self.vol)
        return density

    def pdf_gradient(self, xs):
        return np.zeros(xs.shape)

    def pot(self, xs):
        xs = interpret_array(xs, self.ndim)
        logpdf = -(np.all(xs > self.low, axis=1) *
                   np.all(xs < self.high, axis=1) * np.log(self.vol))
        return logpdf

    def pot_gradient(self, xs):
        return np.zeros(xs.shape)

    def rvs(self, sample_size):
        return np.random.uniform(self.low, self.high, (sample_size, self.ndim))

    def __repr__(self):
        return type(self).__name__ + "(ndim=%s, sample_range=%s)" % (
            self.ndim, (self.low, self.high))


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

"""
Rotation matrix using the `Euler-Rodrigues formula
<http://en.wikipedia.org/wiki/Euler%E2%80%93Rodrigues_parameters>`_

Rotation using the `right hand rule
<http://en.wikipedia.org/wiki/Right_hand_rule>`_

Code downloaded from
`<http://stackoverflow.com/questions/6802577/python-rotation-of-3d-vector>`_
"""

import numpy as np
import math

def rotation_matrix_numpy(axis, theta):
    """ return the rotation matrix using numpy

    Parameters
    ----------
    axis : numpy.array([x,y,z])
    theta : rotation angle in radians
    """
    mat = np.eye(3, 3)
    axis = axis / math.sqrt(np.dot(axis, axis))
    a = math.cos(theta / 2.0)
    b, c, d = -axis * math.sin(theta / 2.0)

    return np.array(
        [
            [a * a + b * b - c * c - d * d, 2 * (b * c - a * d), 2 * (b * d + a * c)],
            [2 * (b * c + a * d), a * a + c * c - b * b - d * d, 2 * (c * d - a * b)],
            [2 * (b * d - a * c), 2 * (c * d + a * b), a * a + d * d - b * b - c * c],
        ]
    )

def rotation_matrix_weave(axis, theta, mat=None):
    """ return the rotation matrix using Weave
    (~20x faster than numpy)

    Parameters
    ----------
    axis : numpy.array([x,y,z])
    theta : rotation angle in radians
    """
    if mat == None:
        mat = np.eye(3, 3)

    support = "#include <math.h>"
    code = """
    double x = sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
    double a = cos(theta / 2.0);
    double b = -(axis[0] / x) * sin(theta / 2.0);
    double c = -(axis[1] / x) * sin(theta / 2.0);
    double d = -(axis[2] / x) * sin(theta / 2.0);

    mat[0] = a*a + b*b - c*c - d*d;
    mat[1] = 2 * (b*c - a*d);
    mat[2] = 2 * (b*d + a*c);

    mat[3*1 + 0] = 2*(b*c+a*d);
    mat[3*1 + 1] = a*a+c*c-b*b-d*d;
    mat[3*1 + 2] = 2*(c*d-a*b);

    mat[3*2 + 0] = 2*(b*d-a*c);
    mat[3*2 + 1] = 2*(c*d+a*b);
    mat[3*2 + 2] = a*a+d*d-b*b-c*c;
    """

    weave.inline(code, ["axis", "theta", "mat"], support_code=support, libraries=["m"])

    return mat

def rotate(arr, axis, theta, method="numpy"):
    """ rotate array around axis by theta with 'numpy' or 'weave'

    Arguments
    ---------
    arr : np.array([x,y,z])
    axis : np.array([x,y,z])
    theta : in radians
    method : 'numpy' or 'weave'

    Returns
    -------
    np.array([x,y,z])

    """
    if method == "numpy":
        return np.dot(rotation_matrix_numpy(axis, theta), arr)
    elif method == "weave":
        return np.dot(rotation_matrix_weave(axis, theta), arr)
    else:
        raise NameError("method for rotation matrix is 'numpy' or 'weave'")

if __name__ == "__main__":
    v = np.array([3, 5, 0])
    axis = np.array([4, 4, 1])
    theta = 1.2

    print("Rotation with Numpy:")
    print(np.dot(rotation_matrix_numpy(axis, theta), v))
    print("Rotation with Scipy.Weave:")
    print(np.dot(rotation_matrix_weave(axis, theta), v))


import numpy as np

# Tri Diagonal Matrix Algorithm(a.k.a. Thomas algorithm) solver
def tdma_solver(a, b, c, d):
    """
    TDMA solver, a b c d can be NumPy array type or Python list type.
    refer to https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm
    and to https://www.cfd-online.com/Wiki/Tridiagonal_matrix_algorithm_-_TDMA_(Thomas_algorithm)
    """
    nf = len(d)  # number of equations
    ac, bc, cc, dc = map(np.array, (a, b, c, d))  # copy arrays
    for it in range(1, nf):
        mc = ac[it - 1] / bc[it - 1]
        bc[it] = bc[it] - mc * cc[it - 1]
        dc[it] = dc[it] - mc * dc[it - 1]

    xc = bc
    xc[-1] = dc[-1] / bc[-1]

    for il in range(nf - 2, -1, -1):
        xc[il] = (dc[il] - cc[il] * xc[il + 1]) / bc[il]

    return xc


# %%
from pandas_profiling import ProfileReport
from typing import Dict, Tuple
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# %%

TP = 50
FP = 40
TN = 80
FN = 30

# %%
# Let's see the sensitivity of our logistic regression model
TP / float(TP+FN)
# %%
# Let us calculate specificity
TN / float(TN+FP)
# %%
# Calculate false positive rate - predicting churn when customer does not have churned
print(FP / float(TN+FP))
# %%
# positive predictive value
print(TP / float(TP+FP))

# %%
# Negative predictive value
print(TN / float(TN + FN))
# %%
TP = 400
FP = 200
TN = 300
FN = 100
# %%
# Let's see the sensitivity of our logistic regression model
TP / float(TP+FN)
# %%
# Let us calculate specificity
TN / float(TN+FP)
# %%
# Calculate false positive rate - predicting churn when customer does not have churned
print(FP / float(TN+FP))
# %%
# positive predictive value
print(TP / float(TP+FP))

# %%
# Negative predictive value
print(TN / float(TN + FN))
# %%

def TPR(TP: float, FN: float) -> float:

    return TP / float(TP+FN), f"{TP}/{TP+FN}"

def Sensitivity(TP: float, FN: float) -> float:
    return TPR(TP, FN)

def Specificity(TN: float, FP: float) -> float:
    return TN / float(TN+FP), f"{TN}/{TN+FP}"

def FPR(TN: float, FP: float) -> float:
    return 1 - Specificity(TN, FP)[0]

def Precision(TP: float, FP: float) -> float:
    return TP / float(TP+FP), f"{TP}/{TP+FP}"

def Recall(TP: float, FN: float) -> float:
    return TP / float(TP+FN), f"{TP}/{TP+FN}"

def F1Score(precision: float, recall: float) -> float:
    return (2 * precision*recall) / (precision+recall)

def Accuracy(TN: float, FP: float, FN: float, TP: float) -> float:
    return (TN+TP)/(TN+TP+FP+FN), f"{TN+TP}/{TN+TP+FP+FN}"

def get_classification_matrix(TN: float, FP: float, FN: float, TP: float) -> Dict[str, float]:
    precision = Precision(TP, FP)[0]
    recall = Recall(TP, FN)[0]
    return {
        "TPR": TPR(TP, FN),
        "Sensitivity": Sensitivity(TP, FN),
        "Specificity": Specificity(TN, FP),
        "FPR": FPR(TN, FP),
        "Precision": precision,
        "Recall": recall,
        "F1Score": F1Score(precision, recall),
        "Accuracy": Accuracy(TN, FP, FN, TP)
    }

# %%
# %%
%matplotlib inline
# %%
cutoff_df = pd.read_csv("tradeoff.csv")
# %%

cutoff_df.plot.line(x='Probability', y=[
                    'Accuracy', 'Sensitivity', 'Specificity'])
plt.show()

# %%
TP = 150
FP = 100
TN = 400
FN = 50

for k, v in get_classification_matrix(TN, FP, FN, TP).items():
    print(f"{k}: {v}")

# %%
# %%
%matplotlib inline
# %%
df = pd.read_csv("churn_data.csv")
# %%
profile = ProfileReport(df, title="Pandas Profiling Report")
profile

# %%
df.dropna(inplace=True)

# %%
df["Churn"] = df["Churn"].astype('category')
df["TotalCharges"] = df["TotalCharges"].astype(float)
# %%
sns.boxplot(data=df, x="Churn", y="TotalCharges")
# %%
# %%
TP = 359
FP = 234
TN = 1294
FN = 223

for k, v in get_classification_matrix(TN, FP, FN, TP).items():
    print(f"{k}: {v}")
# %%
# %%
for k, v in get_classification_matrix(1200, 400, 350, 1050).items():
    print(f"{k}: {v}")
# %%
for k, v in get_classification_matrix(1200, 400, 350, 1190).items():
    print(f"{k}: {v}")
# %%
0.7083333333333334 - 0.75
# %%
TN = 3
FP = 2
FN = 1
TP = 4
for k, v in get_classification_matrix(TN, FP, FN, TP).items():
    print(f"{k}: {v}")
# %%
TN = 500
FP = 20
FN = 40
TP = 440
for k, v in get_classification_matrix(TN, FP, FN, TP).items():
    print(f"{k}: {v}")
# %%

03Machine Learning 1/02Logistic Regression/02Multivariate Logistic Regression - Model Building/temp.py


import numpy as np
import scipy.stats as stats
import statsmodels.api as sm

def calculate_test_statistic(ts):
    """Calculate the test statistic defined by being
       the top zscore in the timeseries.

    Args:
        ts (list or np.array): The timeseries to compute the test statistic.

    Returns:
        tuple(int, float): The index of the top zscore and the value of the top zscore.

    """
    zscores = abs(stats.zscore(ts, ddof=1))
    max_idx = np.argmax(zscores)
    return max_idx, zscores[max_idx]

def calculate_critical_value(ts, alpha):
    """Calculate the critical value with the formula given for example in
    https://en.wikipedia.org/wiki/Grubbs%27_test_for_outliers#Definition

    Args:
        ts (list or np.array): The timeseries to compute the critical value.
        alpha (float): The significance level.

    Returns:
        float: The critical value for this test.

    """
    size   = len(ts)
    t_dist = stats.t.ppf(1 - alpha / (2 * size), size - 2)

    numerator   = (size - 1) * t_dist
    denominator = np.sqrt(size ** 2 - size * 2 + size * t_dist ** 2)

    return numerator / denominator

def seasonal_esd(ts, seasonality=None, hybrid=False, max_anomalies=10, alpha=0.05):
    """Compute the Seasonal Extreme Studentized Deviate of a time series.
       The steps taken are first to to decompose the timeseries into STL
       decomposition (trend, seasonality, residual). Then, calculate
       the Median Absolute Deviate (MAD) if hybrid (otherwise the median)
       and perform a regular ESD test on the residual, which we calculate as:
                        R = ts - seasonality - MAD or median

       Note: The statsmodel library requires a seasonality to compute the STL
       decomposition, hence the parameter seasonality. If none is given,
       then it will automatically be calculated to be 20% of the total
       timeseries.

    Args:
        ts (list or np.array): The timeseries to compute the ESD.
        seasonality (int): Number of time points for a season.
        hybrid (bool): See Twitter's research paper for difference.
        max_anomalies (int): The number of times the Grubbs' Test will be applied to the ts.
        alpha (float): The significance level.

    Returns:
        list int: The indices of the anomalies in the timeseries.

    """
    ts = np.array(ts)
    seasonal = seasonality or int(0.2 * len(ts)) # Seasonality is 20% of the ts if not given.
    decomp   = sm.tsa.seasonal_decompose(ts, freq=seasonal)
    if hybrid:
        mad      = np.median(np.abs(ts - np.median(ts)))
        residual = ts - decomp.seasonal - mad
    else:
        residual = ts - decomp.seasonal - np.median(ts)
    outliers = esd(residual, max_anomalies=max_anomalies, alpha=alpha)
    return outliers

def esd(timeseries, max_anomalies=10, alpha=0.05):
    """Compute the Extreme Studentized Deviate of a time series.
       A Grubbs Test is performed max_anomalies times with the caveat
       that each time the top value is removed. For more details visit
       http://www.itl.nist.gov/div898/handbook/eda/section3/eda35h3.htm

    Args:
        timeseries (list or np.array): The timeseries to compute the ESD.
        max_anomalies (int): The number of times the Grubbs' Test will be applied to the ts.
        alpha (float): The significance level.

    Returns:
        list int: The indices of the anomalies in the timeseries.

    """
    ts = np.copy(np.array(timeseries))
    test_statistics = []
    total_anomalies = -1
    for curr in range(max_anomalies):
        test_idx, test_val = calculate_test_statistic(ts)
        critical_value     = calculate_critical_value(ts, alpha)
        if test_val > critical_value:
            total_anomalies = curr
        test_statistics.append(test_idx)
        ts = np.delete(ts, test_idx)
    anomalous_indices = test_statistics[:total_anomalies + 1]
    return anomalous_indices

import numpy,import scipy,import statsmodels


import csv

import numpy as np

freqs = np.linspace(-2 * np.pi, 2 * np.pi, 100)
resp = np.sin(3 * freqs) / (6 * np.sin(freqs / 2)) * np.exp(freqs * -5j / 2)

mag = np.abs(resp)
ang = np.angle(resp)

with open("hw01/q6a-mag.csv", 'w') as f:
    writer = csv.writer(f)
    writer.writerow(['n', "q6a-mag"])
    writer.writerows(zip(freqs, mag))

with open("hw01/q6a-ang.csv", 'w') as f:
    writer = csv.writer(f)
    writer.writerow(['n', "q6a-ang"])
    writer.writerows(zip(freqs, ang))

inputs = np.arange(0, 11)

maf_impulse = 1 / 6 * np.logical_and(np.greater_equal(inputs, 0), np.less_equal(inputs, 5))
x = np.cos(np.pi / 3 * inputs) * np.greater_equal(inputs, 0)

with open("hw01/q6b.csv", 'w') as f:
    writer = csv.writer(f)
    writer.writerow(['n', "q6b"])
    writer.writerows(zip(inputs, np.convolve(maf_impulse, x)))


import numpy as np

print("Hi! Welcome to the magic square algorithm")
print("Please enter the number for which you would want a magic sqaure to be printed. Please enter an odd number")

# The odd number for which the magic square is created is stored in the variable N

N  = int(input())

# create a matrix with values 0 using numpy. The datatype is int for the elements in the matrix
magic_square = np.zeros((N,N), dtype=int)

n = 1
i, j = 0, N//2

# n iterates from 1 to N**2. The loop exits when n is equal to N**2
while n <= N**2:

	# Start in the middle of the first row.
	# (i = 0 and j = N//2 ) and the element at magic_square[i,j] is the middle in the first row.
	# insert n = 1 to begin with at magic_square[i,j]
	magic_square[i, j] = n
	# increment n by 1
	n += 1

	    # Move diagonally up and right, wrapping to the first column or last row if the move leads outside the grid

	new_i, new_j = (i-1) % N, (j+1)% N

    # if the cell is already filled with a number, move vertically down one space.
	if magic_square[new_i, new_j]:
		i += 1
	else:
        	i, j = new_i, new_j

print(magic_square)


from __future__ import print_function
import numpy
import time
import sys

def solve(n):
    A = numpy.random.randn(n, n)
    b = numpy.random.randn(n)
    start = time.time()
    x = numpy.linalg.solve(A, b)
    end = time.time()
    print("Runtime for n = %d: %d" % (n, end - start))

def jacobi(A, b, max_iter=1024, termination_l2=1.0e-12):
    # get size
    n = len(A[0])

    # initialize variables
    start = time.time()
    x = numpy.zeros(n)
    D = numpy.diag(A)
    R = A - numpy.diagflat(D)

    # use jacobi till maxiteration or termination criteria
    for i in range(0, max_iter):
        x = (b - numpy.dot(R, x))/D
        if (numpy.linalg.norm(numpy.dot(A, x) - b) < termination_l2):
            break
    end = time.time()
    l2 = numpy.linalg.norm(numpy.dot(A, x) - b)
    print("Jacobi: n = %d: time %fs, l2 %e" % (n, end - start, l2))
    return x

def jacobi_test(n):
    A = 100.0*numpy.random.randn(n, n)
    b = numpy.random.randn(n)
    row_sums = numpy.linalg.norm(A, 1)
    # make sure A is diagonally dominant
    A = A + row_sums*numpy.identity(n)
    #print(A)
    start = time.time()
    x = jacobi(A, b)
    end = time.time()
    #print(x)
    print("Total Runtime for n = %d: %d" % (n, end - start))

def write_rand_problem(n, name="input"):
    A = rand_matrix(n)
    A.tofile(name + "_A.bin")
    b = rand_result(n)
    b.tofile(name + "_b.bin")
    x = jacobi(A, b)
    print(A)
    print(b)
    print(numpy.dot(A, b))

def check_result(file_A, file_b, file_x):
    # read input
    b = numpy.fromfile(file_b)
    print(b)
    n = b.size
    A = numpy.fromfile(file_A)
    if A.size != n*n:
        print("File dimensions do not match")
        return
    A = A.reshape((n, n))
    print(A)
    x = numpy.fromfile(file_x)
    if x.size != n:
        print("File dimensions do not match")
        return
    # check result
    if n < 256:
        real_x = numpy.linalg.solve(A, b)
    else:
        real_x = jacobi(A, b)

    if not numpy.allclose(x, real_x, 1e-10):
        print("Error: output is not close to input")
    else:
        print("Output is correct!")

def main():
    if len(sys.argv) < 4:
        print("Usage: python check_output.py <input_A> <input_b> <output_x>")
        return
    check_result(sys.argv[1], sys.argv[2], sys.argv[3])

if __name__ == '__main__':
    main()


import numpy as np
import os
import torch
import copy
from math import cos, sqrt, pi

def dct(x, y, v, u, n):
    # Normalisation
    def alpha(a):
        if a == 0:
            return sqrt(1.0 / n)
        else:
            return sqrt(2.0 / n)

    return alpha(u) * alpha(v) * cos(((2 * x + 1) * (u * pi)) / (2 * n)) * cos(((2 * y + 1) * (v * pi)) / (2 * n))

def generate_2d_dct_basis(root_path, image_height, sub_dim=75):
    path = "{}/attacked_images/GeoDA/2d_dct_basis_height_{}_subdim_{}.npy".format(root_path, image_height, sub_dim)
    os.makedirs(os.path.dirname(path),exist_ok=True)
    if os.path.exists(path):
        return np.load(path)
    n = image_height  # Assume square image, so we don't have different xres and yres

    # We can get different frequencies by setting u and v
    # Here, we have a max u and v to loop over and display
    # Feel free to adjust
    maxU = sub_dim
    maxV = sub_dim

    dct_basis = []
    for u in range(0, maxU):
        for v in range(0, maxV):
            basisImg = np.zeros((n, n))
            for y in range(0, n):
                for x in range(0, n):
                    basisImg[y, x] = dct(x, y, v, u, max(n, maxV))
            dct_basis.append(basisImg)
    dct_basis = np.mat(np.reshape(dct_basis, (maxV*maxU, n*n))).transpose()
    np.save(path, dct_basis)
    return dct_basis

def clip_image_values(x, minv, maxv):
    if not isinstance(minv, torch.Tensor):
        return torch.clamp(x,min=minv,max=maxv)
    return torch.min(torch.max(x, minv), maxv)

def valid_bounds(img, delta=255):

    im = copy.deepcopy(np.asarray(img))
    im = im.astype(np.int)

    # General valid bounds [0, 255]
    valid_lb = np.zeros_like(im)
    valid_ub = np.full_like(im, 255)

    # Compute the bounds
    lb = im - delta
    ub = im + delta

    # Validate that the bounds are in [0, 255]
    lb = np.maximum(valid_lb, np.minimum(lb, im))
    ub = np.minimum(valid_ub, np.maximum(ub, im))

    # Change types to uint8
    lb = lb.astype(np.uint8)
    ub = ub.astype(np.uint8)

    return lb, ub

def inv_tf(x, mean, std):

    for i in range(len(mean)):

        x[i] = np.multiply(x[i], std[i], dtype=np.float32)
        x[i] = np.add(x[i], mean[i], dtype=np.float32)

    x = np.swapaxes(x, 0, 2)
    x = np.swapaxes(x, 0, 1)

    return x

def inv_tf_pert(r):

    pert = np.sum(np.absolute(r), axis=0)
    pert[pert != 0] = 1

    return pert

def get_label(x):
    s = x.split(' ')
    label = ''
    for l in range(1, len(s)):
        label += s[l] + ' '

    return label

def nnz_pixels(arr):
    return np.count_nonzero(np.sum(np.absolute(arr), axis=0))


import numpy as np
import math

def ReadMatrixAndVec():
    matrixSize = int(input())

    matrix = [list(map(float, input().split())) for i in range(matrixSize)]
    vec = list(map(float, input().split()))

    return matrix, vec

def GetDiagsFromTridiagMatrix(matrix):
    aVec = [matrix[i][0] for i in range(1, len(matrix))]

    bVec = [matrix[i][1] for i in range(1, len(matrix))]
    bVec.insert(0, matrix[0][0])

    cVec = [matrix[i][2] for i in range(1, len(matrix) - 1)]
    cVec.insert(0, matrix[0][1])

    return aVec, bVec, cVec

def GetPV(aVec, bVec, cVec, xVec):
    pVec = [0 for i in range(len(bVec))]
    qVec = pVec.copy()
    pVec[0] = -cVec[0] / bVec[0]
    qVec[0] = xVec[0] / bVec[0]

    for i in range(1, len(bVec) - 1):
        pVec[i] =  -cVec[i] / (bVec[i] + aVec[i - 1] * pVec[i - 1])

        qVec[i] = (xVec[i] - aVec[i - 1] * qVec[i - 1]) / (bVec[i] + aVec[i - 1] * pVec[i - 1])

    qVec[-1] = (xVec[-1] - aVec[-1] * qVec[-2]) / (bVec[-1] + aVec[-1] * pVec[-2])

    return pVec, qVec

def SolveSLAU(pVec, qVec):
    xVec = qVec.copy()

    for i in reversed(range(len(qVec) - 1)):
        xVec[i] = pVec[i] * xVec[i + 1] + qVec[i]

    return xVec

def TridiagMatrixAlg(matrix, vec):
    aVec, bVec, cVec = GetDiagsFromTridiagMatrix(matrix)

    print("a diagonal:")
    print(np.matrix(aVec).transpose())
    print("b diagonal:")
    print(np.matrix(bVec).transpose())
    print("c diagonal:")
    print(np.matrix(cVec).transpose())
    print("Vector:")
    print(np.matrix(vec).transpose())

    pVec, qVec = GetPV(aVec, bVec, cVec, vec)

    print("P vector:")
    print(np.matrix(pVec).transpose())
    print("Q vector:")
    print(np.matrix(qVec).transpose())

    result = SolveSLAU(pVec, qVec)

    return result

if __name__ == "__main__":
    matrix, vec = ReadMatrixAndVec()

    res = TridiagMatrixAlg(matrix, vec)

    print("Result vector:")
    print(np.matrix(res).transpose())


"""
Lorenz system of ODEs.
Created on Sun Jul 11 21:39:25 2021
@author: j
"""
import numpy as np
from scipy.integrate import odeint
from matplotlib import pyplot as plt

# Lorenz system of equations. Strange atractor. Parameters sigma, beta and rho
# are specified directly.
def lorenz(state, t, sigma=10.0, beta=8.0/3.0, rho=28.0):
    x, y, z = state
    dx = sigma * (y - x)
    dy = x * (rho - z) - y
    dz = x * y - beta * z
    return [dx, dy, dz]

# We are gonna solve the lorenz system for 9 initial conditions. We want the
# butterfly shape fast, and that is why we use many initial conditions. All
# will be random numbers between -15 and 15. We use a seed for the
# np.random.random function so the graphics are the same allways. The
# simulation time will be of 20 units of time, using 8000 points.
np.random.seed(10) # Try: 6, 7, 10*, 15
N   = 9
x0  = -15 + 30 * np.random.random((N, 3))
t   = np.linspace(0, 20, 8000)

# Solutions to Lorenz are stored in a tensor. Results will all be ploted in a
# 3D projection using matplotlib
xt  = np.asarray([odeint(lorenz, i, t) for i in x0])

# Figure object of one axis only. All will be 3D projections on a plane. We
# turn off the axis so we only wet the plot. We define the limits of the plot.
fig = plt.figure(figsize=(12, 12))
ax  = fig.add_subplot(1, 1, 1, projection='3d')
ax.axis('off')
ax.set_xlim((-25, 25))
ax.set_ylim((-35, 35))
ax.set_zlim((5, 55))

# Each solution curve in phase space will be plotted with a color. All collors
# used are in this list:
colors = ['darkslategray', 'slateblue', 'blue', 'navy', 'black', 'gray',
          'silver','royalblue','gainsboro']

# Using a forloop we plot all sets of points in the solution tensor. We pick
# the colors for each curve in order
for i in range(N):
    ax.plot3D(xt[i,:,0], xt[i,:,1], xt[i,:,2], colors[i])

# We set the angles at which we see the 3D plot. Then we save a transparent
# image to be used in the CV
ax.view_init(-25, -40)
name = "lorenz_attractor.png"
plt.savefig(name, transparent=True, dpi=300, bbox_inches=0.0, pad_inches=0.0)
plt.show()
print('The PNG image may be cropped later.')


import numpy as np

def lrpd(a):
    """L,R,P,D,DA from LR = PDA factorization
    Method: Dahmen W., Reusken A.; Numerik fuer Ingenieure und Naturwissenschaeftler; Springer S. 79
    :param a: numpy.matrix NxN
    """
    n = a.shape[0]
    p = np.matrix(np.eye(n).astype(dtype=float))
    d = np.matrix(np.eye(n).astype(dtype=float))
    da = np.matrix(np.zeros_like(p))
    l = np.matrix(np.eye(n))
    r = np.matrix(np.zeros_like(p))
    indexes_r = range(n)
    for i in range(n):
        d[i, i] = 1 / abs(a[i]).sum()
        # scaling
        da[i] = d[i, i] * a[i]
    for j in range(n):
        indexes_r[j] = j + abs(da[j:, j]).argmax()
        # columns pivoting
        da[[j, indexes_r[j]]] = da[[indexes_r[j], j]]
        p[[j, indexes_r[j]]] = p[[indexes_r[j], j]]
        for i in range(j + 1, n):
            # new entries in L
            da[i, j] = da[i, j] / da[j, j]
            for k in range(j + 1, n):
                # new entries in R
                da[i, k] = da[i, k] - da[i, j] * da[j, k]
    for i in range(n):
        l[i + 1:n, i] = da[i + 1:n, i]
        r[i, i:n] = da[i, i:n]
    return l, r, p, d, da

def gauss_elimination(a, b):
    """Solution of the system Ax = b (LRx=PDb) through forward substitution of Ly = Pb, and then
    backward substitution of Rx = y
    :param a: numpy.matrix n X n
    :param b: numpy.matrix n X 1
    """
    n = a.shape[0]
    x = np.matrix(np.zeros_like(b))
    y = np.matrix(np.zeros_like(b))
    l, r, p, d, da = lrpd(a)
    pdb = p * d * b
    sum_lik_xk = 0
    sum_lik_yk = 0
    for j in range(0, n, +1):
        # Forward substitution Ly = PDb
        for k in range(0, j, +1):
            sum_lik_yk = sum_lik_yk + l[j, k] * y[k]
        y[j] = (pdb[j, 0] - sum_lik_yk) / 1.0
        sum_lik_yk = 0
    for j in range(n - 1, -1, -1):
        # Backward substitution Rx = y
        for k in range(n - 1, j, -1):
            sum_lik_xk = sum_lik_xk + r[j, k] * x[k]
        x[j] = (y[j, 0] - sum_lik_xk) / r[j, j]
        sum_lik_xk = 0
    return x

# noinspection PyAugmentAssignment
def nr_ls(x0, f, j, tol, max_it, inner_loop_condition,
          notify_status_func, method_loops, process_func_handle):
    x = x0
    # Newton method: G(x) = J(x)^-1 * F(x)
    outer_it_k = 0
    inner_it_j = 0
    j_val = j(x)
    f_val = f(x)
    y = np.matrix(np.ones(len(x))).T * tol / (np.sqrt(len(x)) * tol)
    magnitude_f = np.sqrt((f_val.T * f_val).item())
    # Line search variable lambda
    lambda_ls = 0.0
    accum_step = 0.0
    # For progress bar, use log scale to compensate for quadratic convergence
    log10_to_o_max_magnitude_f = np.log10(tol / magnitude_f)
    progress_k = (1.0 - np.log10(tol / magnitude_f) /
                  log10_to_o_max_magnitude_f) * 100.0
    diff = np.matrix(np.empty([len(x), 1]))
    diff.fill(np.nan)
    stop = False
    divergent = False
    # Non-functional status notification
    notify_status_func(progress_k, stop, outer_it_k,
                       inner_it_j, lambda_ls, accum_step,
                       x, diff, f_val, j_val, lambda_ls * y,
                       method_loops)
    # End non-functional notification
    while outer_it_k <= max_it and not stop:
        outer_it_k += 1
        method_loops[1] += 1
        inner_it_j = 0
        lambda_ls = 1.0
        accum_step += lambda_ls
        x_k_m_1 = x
        progress_k_m_1 = progress_k
        y = gauss_elimination(j_val, -f_val)
        # First attempt without backtracking
        x = x + lambda_ls * y
        diff = x - x_k_m_1
        j_val = j(x)
        f_val = f(x)
        magnitude_f = np.sqrt((f_val.T * f_val).item())
        if magnitude_f < tol and inner_loop_condition(x):
            stop = True  # Procedure successful
        else:
            # Non-functional status notification
            notify_status_func(progress_k, stop, outer_it_k,
                               inner_it_j, lambda_ls, accum_step,
                               x, diff, f_val, j_val, lambda_ls * y,
                               method_loops)
            # End non-functional notification

            # For progress use log scale to compensate for quadratic
            # convergence
            progress_k = (1.0 - np.log10(tol / magnitude_f) /
                          log10_to_o_max_magnitude_f) * 100.0
            if np.isnan(magnitude_f) or np.isinf(magnitude_f):
                # TODO: Re-implement steepest descent
                stop = True  # Divergent method
                divergent = True
                progress_k = 0.0
            else:
                pass
            if progress_k == progress_k_m_1:
                # Non-functional gui processing
                process_func_handle()
                # End non-functional processing
                # if form.progress_var.wasCanceled():
                # stop = True
        while inner_it_j <= max_it and \
                not inner_loop_condition(x) and \
                not stop:
            # Backtrack if any conc < 0. Line search method.
            # Ref. http://dx.doi.org/10.1016/j.compchemeng.2013.06.013
            inner_it_j += 1
            lambda_ls = lambda_ls / 2.0
            accum_step += -lambda_ls
            x = x_k_m_1
            progress_k = progress_k_m_1
            x = x + lambda_ls * y
            diff = x - x_k_m_1
            j_val = j(x)
            f_val = f(x)
            # Non-functional status notification
            notify_status_func(progress_k, stop, outer_it_k,
                               inner_it_j, lambda_ls, accum_step,
                               x, diff, f_val, j_val, lambda_ls * y,
                               method_loops)
            # End non-functional notification
            method_loops[0] += 1
    if stop and not divergent:
        progress_k = 100.0
    elif divergent:
        progress_k = 0.0
    # Non-functional status notification
    notify_status_func(progress_k, stop, outer_it_k,
                       inner_it_j, lambda_ls, accum_step,
                       x, diff, f_val, j_val, lambda_ls * y,
                       method_loops)
    # End non-functional notification
    return progress_k, stop, outer_it_k,\
        inner_it_j, lambda_ls, accum_step,\
        x, diff, f_val, lambda_ls * y,\
        method_loops


"""This module provides functions to sample from the unit simplex.
"""
import numpy as np
from scipy.special import comb

def sample(dimension, n_points=1):
    """Draw random samples from the unit simplex in R^d.

    Parameters
    ----------
    dimension : int
        number of variables.
    n_points : int, optional
        number of points to sample, by default 1

    Returns
    -------
    numpy array
        Random samples from the unit simplex
    """
    s = np.random.standard_exponential((n_points, dimension))
    return (s.T / s.sum(axis=1)).T

def grid(dimension, levels=2):
    """Construct a regular grid on the unit simplex in R^d.

    Parameters
    ----------
    dimension : int
        Number of variables.
    levels : int
        Number of levels between 0 and 1 for each variable.

    Returns
    -------
    ndarray of shape (n_points, n_dim)
        Regularily spaced grid points on the unit simplex.

    References
    ----------
    Nijenhuis and Wilf, Combinatorial Algorithms, Chapter 5, Academic Press, 1978.

    Examples
    --------
    >>> simplex.grid(3, 3)
    array([
        [0. , 0. , 1. ],
        [0. , 0.5, 0.5],
        [0. , 1. , 0. ],
        [0.5, 0. , 0.5],
        [0.5, 0.5, 0. ],
        [1. , 0. , 0. ]
    ])
    """
    m = dimension
    n = levels - 1
    n_points = comb(n + m - 1, m - 1, exact=True)

    x = np.zeros(m, dtype=int)
    x[-1] = n

    out = np.empty((n_points, m), dtype=int)
    out[0] = x

    h = m
    for i in range(1, n_points):
        h -= 1

        val = x[h]
        x[h] = 0
        x[h - 1] += 1
        x[-1] = val - 1

        if val != 1:
            h = m

        out[i] = x

    return out / n


import numpy

def find_step_length(f, fd, x, alpha, direction, c2):
  g = lambda alpha: f(x+alpha*direction)
  gd = lambda alpha: numpy.dot(fd(x + alpha*direction), direction)
  return interpolation(g, gd, alpha, c2)

def wolf1(f, fd, alpha):
  c1 = 1e-4
  return f(alpha) <= f(0) + c1*alpha*fd(alpha)

def wolf_strong(f, fd, alpha, c2):
  return abs(fd(alpha)) <= -c2*fd(0)

def simple_backtracking(f, fd, alpha, c2):
  rate = 0.5
  while not (wolf1(f, fd, alpha) or wolf_strong(f, fd, alpha, c2)):
    alpha = rate*alpha
  return alpha

def interpolation(f, fd, alpha, c2):
  lo = 0.0
  hi = 1.0

  for i in range(0, 20):
    if wolf1(f, fd, alpha):
      if wolf_strong(f, fd, alpha, c2):
        return alpha

    half = (lo+hi)/2.0
    alpha = - (fd(lo)*hi*hi) / (2*(f(hi)-f(lo)-fd(lo)*hi))

    if alpha < lo or alpha > hi: # quadratic interpolation failed. reduce by half instead
      alpha = half
    if fd(alpha) > 0:
      hi = alpha
    elif fd(alpha) <= 0:
      lo = alpha
  return alpha

Computational Mathematics | Python/Line_search.py


import numpy as np

class Derivative(object):
    def __init__(self, f, h=0.001, order=1, n_points=3, vectorized=False):
        self.f = np.vectorize(f) if vectorized else f
        self.h = h
        self.order = order
        self.n_points = n_points
        self.weights = self._weights()

    def _weights(self):
        if self.order == 1:
            if self.n_points == 3:
                return [0.5, -3, 4, -1]
            if self.n_points == 5:
                return [1 / 12, -25, 48, -36, 16, -3]

    def __str__(self):
        return "Difference Method"

    def description(self):
        pass

    def __call__(self, x0):
        fp = 0
        for i, w in enumerate(self.weights):
            if i == 0:
                continue
            fp += w * self.f(x0 + (i - 1) * self.h)
        fp *= self.weights[0] / self.h
        return fp


import numpy as np

def centroid(X):
    """
    Centroid is the mean position of all the points in all of the coordinate
    directions, from a vectorset X.
    https://en.wikipedia.org/wiki/Centroid
    C = sum(X)/len(X)
    Parameters
    ----------
    X : array
        (N,D) matrix, where N is points and D is dimension.
    Returns
    -------
    C : float
        centroid
    """
    C = X.mean(axis=0)
    return C

def rmsd(V, W):
    """
    Calculate Root-mean-square deviation from two sets of vectors V and W.
    Parameters
    ----------
    V : array
        (N,D) matrix, where N is points and D is dimension.
    W : array
        (N,D) matrix, where N is points and D is dimension.
    Returns
    -------
    rmsd : float
        Root-mean-square deviation between the two vectors
    """
    D = len(V[0])
    N = len(V)
    result = 0.0
    for v, w in zip(V, W):
        result += sum([(v[i] - w[i])**2.0 for i in range(D)])
    return np.sqrt(result/N)

def kabsch_rmsd(P, Q, translate=False):
    """
    Rotate matrix P unto Q using Kabsch algorithm and calculate the RMSD.
    Parameters
    ----------
    P : array
        (N,D) matrix, where N is points and D is dimension.
    Q : array
        (N,D) matrix, where N is points and D is dimension.
    translate : bool
        Use centroids to translate vector P and Q unto each other.
    Returns
    -------
    rmsd : float
        root-mean squared deviation
    """
    if translate:
        Q = Q - centroid(Q)
        P = P - centroid(P)

    P = kabsch_rotate(P, Q)
    return rmsd(P, Q)

def kabsch_rotate(P, Q):
    """
    Rotate matrix P unto matrix Q using Kabsch algorithm.
    Parameters
    ----------
    P : array
        (N,D) matrix, where N is points and D is dimension.
    Q : array
        (N,D) matrix, where N is points and D is dimension.
    Returns
    -------
    P : array
        (N,D) matrix, where N is points and D is dimension,
        rotated
    """
    U = kabsch(P, Q)

    # Rotate P
    P = np.dot(P, U)
    return P

def kabsch(P, Q):
    """
    Using the Kabsch algorithm with two sets of paired point P and Q, centered
    around the centroid. Each vector set is represented as an NxD
    matrix, where D is the the dimension of the space.
    The algorithm works in three steps:
    - a centroid translation of P and Q (assumed done before this function
      call)
    - the computation of a covariance matrix C
    - computation of the optimal rotation matrix U
    For more info see http://en.wikipedia.org/wiki/Kabsch_algorithm
    Parameters
    ----------
    P : array
        (N,D) matrix, where N is points and D is dimension.
    Q : array
        (N,D) matrix, where N is points and D is dimension.
    Returns
    -------
    U : matrix
        Rotation matrix (D,D)
    """

    # Computation of the covariance matrix
    C = np.dot(np.transpose(P), Q)

    # Computation of the optimal rotation matrix
    # This can be done using singular value decomposition (SVD)
    # Getting the sign of the det(V)*(W) to decide
    # whether we need to correct our rotation matrix to ensure a
    # right-handed coordinate system.
    # And finally calculating the optimal rotation matrix U
    # see http://en.wikipedia.org/wiki/Kabsch_algorithm
    V, S, W = np.linalg.svd(C)
    d = (np.linalg.det(V) * np.linalg.det(W)) < 0.0

    if d:
        S[-1] = -S[-1]
        V[:, -1] = -V[:, -1]

    # Create Rotation matrix U
    U = np.dot(V, W)

    return U


import numpy as np
import math

def norm(u: np.ndarray) -> np.ndarray:
    u = np.array(u)
    _min = u.min()
    _max = u.max()

    return (u - _min) / (_max - _min)

def norm_max(u: np.ndarray) -> np.ndarray:
    return u / np.finfo(np.float).max

def ztest(mean1, mean2, std1, std2, n_data_points) -> float:
    # http://homework.uoregon.edu/pub/class/es202/ztest.html
    """ If the Z-statistic is less than 2, the two samples are the same.
        If the Z-statistic is between 2.0 and 2.5, the two samples are marginally different
        If the Z-statistic is between 2.5 and 3.0, the two samples are significantly different
        If the Z-statistic is more then 3.0, the two samples are highly signficantly different """
    sigma1 = std1 / math.sqrt(n_data_points)
    sigma2 = std2 / math.sqrt(n_data_points)

    return abs((mean1 - mean2) / math.sqrt(sigma1 ** 2 + sigma2 ** 2))

def check_size(u: np.ndarray, v: np.ndarray):
    if len(u) != len(v):
        raise ValueError(f'u and v must be of the same size')

def hellinger(u: np.ndarray, v: np.ndarray) -> float:
    """ If returns 0.0 matchs
        if returns 1.0 mismatch"""

    check_size(u, v)

    u = norm(u)
    v = norm(v)

    return np.sqrt(np.sum((np.sqrt(u) - np.sqrt(v)) ** 2)) / np.sqrt(2)

def hellinger2(u: np.ndarray, v: np.ndarray) -> float:
    check_size(u, v)

    u1 = norm([u, v])[0]
    v1 = norm([u, v])[1]

    print(u1)
    print(v1)

    u = u1
    v = v1

    return np.sqrt(np.sum((np.sqrt(u) - np.sqrt(v)) ** 2)) / np.sqrt(2)

def pearson(u: np.ndarray, v: np.ndarray) -> float:
    """ If returns 1.0 matchs
        If returns 0.5 half matchs
        if returns 0.0 mismatch"""

    check_size(u, v)

    u1 = norm([u, v])[0]
    v1 = norm([u, v])[1]

    _u = np.mean(u)
    _v = np.mean(v)

    a = np.sum((u - _u) * (v - _v))
    b = np.sqrt(np.sum((u - _u) ** 2) * np.sum((v - _v) ** 2))

    p = a / b

    # return p - (-1) / (1 - (-1))
    return p

def pearson2(u: np.ndarray, v: np.ndarray) -> float:
    """ If returns 1.0 matchs
        If returns 0.5 half matchs
        if returns 0.0 mismatch"""

    check_size(u, v)

    u1 = norm([u,v])[0]
    v1 = norm([u,v])[1]

    _u = np.mean(u1)
    _v = np.mean(v1)

    a = np.sum((u1 - _u) * (v1 - _v))
    b = np.sqrt(np.sum((u1 - _u) ** 2) * np.sum((v1 - _v) ** 2))

    return a/b

def chisquare(u: np.ndarray, v: np.ndarray) -> float:
    """ If returns 0.0 matchs
        if returns unbounded mismatch"""

    check_size(u, v)

    return np.sum(((u - v) ** 2) / (u + v))

def cosine(u: np.ndarray, v: np.ndarray) -> float:
    check_size(u, v)

    return 1 - np.sum(u * v)

def kullback(u: np.ndarray, v: np.ndarray) -> float:
    check_size(u, v)

    return np.sum(u * np.log(u / v))

def intersection(u: np.ndarray, v: np.ndarray) -> float:
    check_size(u, v)

    u = norm(u)
    v = norm(v)

    return np.sum(np.minimum(u, v))

if __name__ == '__main__':
    mu_1 = 0.5  # mean of the first distribution
    mu_2 = 0.5  # mean of the second distribution
    data_1 = np.random.normal(mu_1, 0., 1000)
    data_2 = np.random.normal(mu_2, 0., 1000)
    hist_1, _ = np.histogram(data_1, bins=100, range=[-15, 15])
    hist_2, _ = np.histogram(data_2, bins=100, range=[-15, 15])

    data_1 = hist_1
    data_2 = hist_2

    ztest1 = ztest(76.4, 102, 12.97, 4.08, 300 // 15)

    # print(f'ztest: {ztest1}')
    print(f'helli: {hellinger(data_1, data_2)}')
    print(f'pears: {pearson(data_1, data_2)}')
    # print(f'chisq: {chisquare(data_1, data_2)}')
    # print(f'chial: {chisquare_alt(data_1, data_2)}')
    # print(f'kullb: {kullback(data_1, data_2)}')
    # print(f'inter: {intersection(data_1, data_2)}')


# 3.2.2 カテゴリ分布の学習と予測

#%%

# 3.2.2項で利用するライブラリ
import numpy as np
import math # 対数ガンマ関数:lgamma()
#from scipy.stats import dirichlet # ディリクレ分布
import matplotlib.pyplot as plt

#%%

## 真のモデルの設定

# 次元数:(固定)
K = 3

# 真のパラメータを指定
pi_truth_k = np.array([0.3, 0.5, 0.2])

# x軸の値を作成
k_line = np.arange(1, K + 1)

#%%

# 全てのパターンのデータを作成
s_kk = np.identity(K)

# 確率を計算:式(2.29)
true_model = np.prod(pi_truth_k**s_kk, axis=1)

# 確率を計算:SciPy ver
from scipy.stats import multinomial # 多項分布
true_model = multinomial.pmf(x=s_kk, n=1, p=pi_truth_k)
print(true_model)

# 尤度を作図
plt.bar(x=k_line, height=pi_truth_k, color='purple') # 真のモデル
plt.xlabel('k')
plt.ylabel('prob')
plt.xticks(ticks=k_line, labels=k_line) # x軸目盛
plt.suptitle('Categorical Distribution', fontsize=20)
plt.title('$\pi=(' + ', '.join([str(k) for k in  pi_truth_k]) + ')$', loc='left')
plt.ylim(0, 1)
plt.show()

#%%

## 観測データの生成

# データ数を指定
N = 50

# (観測)データを生成
s_nk = np.random.multinomial(n=1, pvals=pi_truth_k, size=N)

# 観測のデータを確認
print(np.sum(s_nk, axis=0))

#%%

# 観測データのヒストグラムを作図
plt.bar(x=k_line, height=np.sum(s_nk, axis=0)) # 観測データ
plt.xlabel('k')
plt.ylabel('count')
plt.xticks(ticks=k_line, labels=k_line) # x軸目盛
plt.suptitle('Observation Data', fontsize=20)
plt.title('$N=' + str(N) +
          ', \pi=(' + ', '.join([str(k) for k in pi_truth_k]) + ')$', loc='left')
plt.show()

#%%

## 事前分布の設定

# 事前分布のパラメータを指定
alpha_k = np.array([1.0, 1.0, 1.0])

#%%

# 作図用の点を設定
point_vec = np.arange(0.0, 1.001, 0.02)

# 格子状の点を作成
X, Y, Z = np.meshgrid(point_vec, point_vec, point_vec)

# 確率密度の計算用にまとめる
pi_point = np.array([list(X.flatten()), list(Y.flatten()), list(Z.flatten())]).T
pi_point = pi_point[1:, :] # (0, 0, 0)の行を除去
pi_point /= np.sum(pi_point, axis=1, keepdims=True) # 正規化
pi_point = np.unique(pi_point, axis=0) # 重複を除去

#%%

# 事前分布(ディリクレ分布)の確率密度を計算:式(2.41)
ln_C_dir = math.lgamma(np.sum(alpha_k)) - np.sum([math.lgamma(a) for a in alpha_k]) # 正規化項(対数)
prior = np.exp(ln_C_dir) * np.prod(pi_point**(alpha_k - 1), axis=1)

# 事前分布(ディリクレ分布)の確率密度を計算:SciPy ver
#prior = np.array([
#    dirichlet.pdf(x=pi_point[i], alpha=alpha_k) for i in range(len(pi_point))
#])

#%%

# 三角座標に変換
tri_x = pi_point[:, 1] + pi_point[:, 2] / 2
tri_y = np.sqrt(3) * pi_point[:, 2] / 2

# 事前分布を作図
plt.scatter(tri_x, tri_y, c=prior, cmap='jet') # 事前分布
plt.xlabel('$\pi_1, \pi_2$') # x軸ラベル
plt.ylabel('$\pi_1, \pi_3$') # y軸ラベル
plt.xticks(ticks=[0.0, 1.0], labels=['(1, 0, 0)', '(0, 1, 0)']) # x軸目盛
plt.yticks(ticks=[0.0, 0.87], labels=['(1, 0, 0)', '(0, 0, 1)']) # y軸目盛
plt.suptitle('Dirichlet Distribution', fontsize=20)
plt.title('$\\alpha=(' + ', '.join([str(k) for k in alpha_k]) + ')$', loc='left')
plt.colorbar() # 凡例
plt.gca().set_aspect('equal') # アスペクト比
plt.show()

#%%

## 事後分布の計算

# 事後分布のパラメータを計算:式(3.28)
alpha_hat_k = np.sum(s_nk, axis=0) + alpha_k
print(alpha_hat_k)

# 事後分布(ディリクレ分布)の確率密度を計算:式(2.41)
ln_C_dir = math.lgamma(np.sum(alpha_hat_k)) - np.sum([math.lgamma(a) for a in alpha_hat_k]) # 正規化項(対数)
posterior = np.prod(pi_point**(alpha_hat_k - 1), axis=1)
posterior *= np.exp(ln_C_dir)

# 事後分布(ディリクレ分布)の確率密度を計算:SciPy ver
#posterior = np.array([
#    dirichlet.pdf(x=pi_point[i], alpha=alpha_hat_k) for i in range(len(pi_point))
#])

#%%

# 真のパラメータの値を三角座標に変換
tri_x_truth = pi_truth_k[1] + pi_truth_k[2] / 2
tri_y_truth = np.sqrt(3) * pi_truth_k[2] / 2

# 事後分布を作図
plt.scatter(tri_x, tri_y, c=posterior, cmap='jet') # 事後分布
plt.xlabel('$\pi_1, \pi_2$') # x軸ラベル
plt.ylabel('$\pi_1, \pi_3$') # y軸ラベル
plt.xticks(ticks=[0.0, 1.0], labels=['(1, 0, 0)', '(0, 1, 0)']) # x軸目盛
plt.yticks(ticks=[0.0, 0.87], labels=['(1, 0, 0)', '(0, 0, 1)']) # y軸目盛
plt.suptitle('Dirichlet Distribution', fontsize=20)
plt.title('$\\alpha=(' + ', '.join([str(k) for k in alpha_hat_k]) + ')$', loc='left')
plt.colorbar() # 凡例
plt.gca().set_aspect('equal') # アスペクト比
plt.scatter(tri_x_truth, tri_y_truth, marker='x', color='black', s=200) # 真のパラメータ
plt.show()

#%%

## 予測分布の計算

# 予測分布のパラメータを計算
pi_hat_star_k = alpha_hat_k / np.sum(alpha_hat_k)
pi_hat_star_k = (np.sum(s_nk, axis=0) + alpha_k) / np.sum(np.sum(s_nk, axis=0) + alpha_k)
print(pi_hat_star_k)

#%%

# 予測分布を作図
plt.bar(x=k_line, height=pi_truth_k, label='truth',
        alpha=0.5, color='white', edgecolor='red', linestyle='dashed') # 真のモデル
plt.bar(x=k_line, height=pi_hat_star_k, label='predict',
        alpha=0.5, color='purple') # 予測分布
plt.xlabel('k')
plt.ylabel('prob')
plt.xticks(ticks=k_line, labels=k_line) # x軸目盛
plt.suptitle('Categorical Distribution', fontsize=20)
plt.title('$N=' + str(N) + ', \hat{\pi}_{*}=(' + ', '.join([str(k) for k in np.round(pi_hat_star_k, 2)]) + ')$', loc='left')
plt.ylim(0, 1)
plt.show()

#%%

### アニメーション

# 利用するライブラリ
import numpy as np
from scipy.stats import dirichlet # ディリクレ分布
import matplotlib.pyplot as plt
import matplotlib.animation as animation

#%%

# 描画用の点を設定
point_vec = np.arange(0.0, 1.001, 0.025)

# 格子状の点を作成
X, Y, Z = np.meshgrid(point_vec, point_vec, point_vec)

# 確率密度の計算用にまとめる
pi_point = np.array([list(X.flatten()), list(Y.flatten()), list(Z.flatten())]).T
pi_point = pi_point[1:, :] # (0, 0, 0)の行を除去
pi_point /= np.sum(pi_point, axis=1, keepdims=True) # 正規化
pi_point = np.unique(pi_point, axis=0) # 重複を除去

# 三角座標に変換
tri_x = pi_point[:, 1] + pi_point[:, 2] / 2
tri_y = np.sqrt(3) * pi_point[:, 2] / 2

#%%

## 推論処理

# 次元数:(固定)
K = 3

# 真のパラメータを指定
pi_truth_k = np.array([0.3, 0.5, 0.2])

# 事前分布のパラメータを指定
alpha_k = np.array([1.0, 1.0, 1.0])

# 初期値による予測分布のパラメータを計算
mu_star_k = alpha_k / np.sum(alpha_k)

# データ数を指定
N = 100

# 記録用の受け皿を初期化
s_nk = np.empty((N, K))
trace_alpha = [list(alpha_k)]
trace_posterior = [[dirichlet.pdf(x=pi_point[i], alpha=alpha_k) for i in range(len(pi_point))]]
trace_predict = [list(mu_star_k)]

# ベイズ推論
for n in range(N):
    # (観測)データを生成
    s_nk[n] = np.random.multinomial(n=1, pvals=pi_truth_k, size=1)[0]

    # 事後分布のパラメータを更新
    alpha_k += s_nk[n]

    # 値を記録
    trace_alpha.append(list(alpha_k))

    # 事後分布(ディリクレ分布)の確率密度を計算
    trace_posterior.append(
        [dirichlet.pdf(x=pi_point[i], alpha=alpha_k) for i in range(len(pi_point))]
    )

    # 予測分布のパラメータを更新
    mu_star_k = alpha_k / np.sum(alpha_k)

    # 予測分布(カテゴリ分布)の確率を記録
    trace_predict.append(list(mu_star_k))

    # 途中経過を表示
    print('n=' + str(n + 1) + ' (' + str(np.round((n + 1) / N * 100, 1)) + '%)')

# 観測のデータを確認
print(np.sum(s_nk, axis=0))

#%%

## 事後分布の推移をgif画像化

# 真のパラメータの値を三角座標に変換
tri_x_truth = pi_truth_k[1] + pi_truth_k[2] / 2
tri_y_truth = np.sqrt(3) * pi_truth_k[2] / 2

# 画像サイズを指定
fig = plt.figure(figsize=(12, 9))

# 作図処理を関数として定義
def update_posterior(n):
    # 前フレームのグラフを初期化
    plt.cla()

    # nフレーム目の事後分布を作図
    plt.scatter(tri_x, tri_y, c=trace_posterior[n], cmap='jet') # 事後分布
    plt.scatter(tri_x_truth, tri_y_truth, marker='x', color='black', s=200) # 真のパラメータ
    plt.xlabel('$\pi_1, \pi_2$') # x軸ラベル
    plt.ylabel('$\pi_1, \pi_3$') # y軸ラベル
    plt.xticks(ticks=[0.0, 1.0], labels=['(1, 0, 0)', '(0, 1, 0)']) # x軸目盛
    plt.yticks(ticks=[0.0, 0.87], labels=['(1, 0, 0)', '(0, 0, 1)']) # y軸目盛
    plt.suptitle('Dirichlet Distribution', fontsize=20)
    plt.title('$N=' + str(n) + ', \hat{\\alpha}=(' + ', '.join([str(a) for a in trace_alpha[n]]) + ')$', loc='left')
    plt.gca().set_aspect('equal') # アスペクト比

# gif画像を作成
posterior_anime = animation.FuncAnimation(fig, update_posterior, frames=N + 1, interval=100)
posterior_anime.save("ch3_2_2_Posterior.gif")

#%%

## 予測分布の推移をgif画像化

# x軸の値を作成
k_line = np.arange(1, K + 1)

# 画像サイズを指定
fig = plt.figure(figsize=(12, 9))

# 作図処理を関数として定義
def update_predict(n):
    # 前フレームのグラフを初期化
    plt.cla()

    # nフレーム目の予測分布を作図
    plt.bar(x=k_line, height=pi_truth_k, label='truth',
            alpha=0.5, color='white', edgecolor='red', linestyle='dashed') # 真の分布
    plt.bar(x=k_line, height=trace_predict[n], label='predict',
            alpha=0.5, color='purple') # 予測分布
    plt.xlabel('k')
    plt.ylabel('prob')
    plt.xticks(ticks=k_line, labels=k_line) # x軸目盛
    plt.suptitle('Categorical Distribution', fontsize=20)
    plt.title('$N=' + str(n) + ', \hat{\pi}_{*}=(' + ', '.join([str(k) for k in np.round(trace_predict[n], 2)]) + ')$', loc='left')
    plt.ylim(0, 1)
    plt.legend()

# gif画像を作成
predict_anime = animation.FuncAnimation(fig, update_predict, frames=N + 1, interval=100)
predict_anime.save("ch3_2_2_Predict.gif")

#%%

print('end')


import numpy as np
import math

'''
Y is the column vector containing the set of yns and
Y_pred is the predicted ys represented in a l x N array
where N is the number of points in the training set
'''
def MSE(Y_pred,Y,N, verbose=False):
    """Getting the Mean Squared Error """

    temp = []
    for i in range(0, len(Y_pred)):
        sub = np.subtract(Y.T, Y_pred[i])
        msq_error=0
        for i in range(0, N):
            sq_error = math.pow(sub[0,i], 2)
            if verbose :
                print("the square error for n = ",i,"is: ",sq_error)
            msq_error += sq_error
        msq_error = msq_error/N
        temp.append(msq_error)

    MSE = min(temp)
    return MSE