#%% [markdown]
# ## Simulated Signal Data Generation
# The simulated signal for this example will be constructed by the combination of two Gaussian peaks with some
# overlap.  The height of each peak will be dependent on a concentration value.  The target value for each generated
# signal will be the concentration value.  The goal of the project will be correctly classify each sample based on
# the concentration value.

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

mpl.style.use('seaborn-notebook')
plt.rcParams["figure.figsize"] = (12, 5)

#%% [markdown]
# The following plot shows the separate Gaussian peaks used to generate the project test signal.  This sample signal
# was derived from an example in the
# [**Rampy**](https://github.com/charlesll/rampy/blob/master/examples/Baseline_and_Centroid_determination.ipynb) project.

#%%
xnum = 600
np.random.seed(42)
x = np.arange(0, xnum, 1.0)
S_1 = norm.pdf(x, loc=310.0, scale=40.0)
S_2 = norm.pdf(x, loc=390.0, scale=20.0)
S_true = np.vstack((S_1, S_2))

fig, axs = plt.subplots()
axs.plot(x, S_1)
axs.plot(x, S_2)
fig.suptitle('Signal Base Gaussian Shapes')

#%% [markdown]
# The following plots will show the combined Gaussian peaks at varying concentration levels.  The first
# plot is generated using a fixed set of concentrations values.  The second plot generates a random set of
# curves with the line color determined from the concentration level.

#%%
C_true = np.array([[0.0, 1.0], [0.25, 0.75], [0.5, 0.5], [0.75, 0.25], [1.0, 0.0]])
signal = np.dot(C_true, S_true)

fig, axs = plt.subplots()
for i, level in enumerate(C_true):
    axs.plot(x, signal[i], label='{0:.2f}-concentration'.format(C_true[i, 0]))
fig.suptitle('Combined Signal at Varying Concentration Levels')
plt.legend()

#%%
nsamples = 100
C_levels = np.random.rand(nsamples)
C_matrix = np.vstack((C_levels, (1-C_levels))).T
samples = np.dot(C_matrix, S_true)

# norm is a class which, when called, can normalize data into the
# [0.0, 1.0] interval.
norm = mpl.colors.Normalize(vmin=np.min(C_levels), vmax=np.max(C_levels))

# create a ScalarMappable and initialize a data structure
jet = plt.get_cmap('jet')
s_m = mpl.cm.ScalarMappable(norm=norm, cmap=jet)
s_m.set_array([])

# plotting spectra
# calling the ScalarMappable that was initialised with c_m and norm
fig, axs = plt.subplots()
for i in range(nsamples):
    color = s_m.to_rgba(C_levels[i])
    axs.plot(x, samples[i, :].T, color=color)

# we plot the colorbar, using again our
# ScalarMappable
c_bar = plt.colorbar(s_m)
c_bar.set_label(r"C_")

fig.suptitle('Combined Signals Colored By Concentration Levels')
plt.xlabel('X')
plt.ylabel('Y')
plt.show()


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

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

from __future__ import print_function, division

import math
import numpy as np

import thinkdsp
import thinkplot

import matplotlib.pyplot as pyplot

import warnings
warnings.filterwarnings('ignore')

PI2 = math.pi * 2

def linear_chirp_evaluate(ts, low=440, high=880, amp=1.0):
    """Computes the waveform of a linear chirp and prints intermediate values.

    low: starting frequency
    high: ending frequency
    amp: amplitude
    """
    print('ts', ts)

    freqs = np.linspace(low, high, len(ts)-1)
    print('freqs', freqs)

    dts = np.diff(ts)
    print('dts', dts)

    dphis = np.insert(PI2 * freqs * dts, 0, 0)
    print('dphis', dphis)

    phases = np.cumsum(dphis)
    print('phases', phases)

    ys = amp * np.cos(phases)
    print('ys', ys)

    return ys

def discontinuity(num_periods=30, hamming=False):
    """Plots the spectrum of a sinusoid with/without windowing.

    num_periods: how many periods to compute
    hamming: boolean whether to apply Hamming window
    """
    signal = thinkdsp.SinSignal(freq=440)
    duration = signal.period * num_periods
    wave = signal.make_wave(duration)

    if hamming:
        wave.hamming()

    print(len(wave.ys), wave.ys[0], wave.ys[-1])
    spectrum = wave.make_spectrum()
    spectrum.plot(high=880)

def three_spectrums():
    """Makes a plot showing three spectrums for a sinusoid.
    """
    thinkplot.preplot(rows=1, cols=3)

    pyplot.subplots_adjust(wspace=0.3, hspace=0.4,
                           right=0.95, left=0.1,
                           top=0.95, bottom=0.1)

    xticks = range(0, 900, 200)

    thinkplot.subplot(1)
    thinkplot.config(xticks=xticks)
    discontinuity(num_periods=30, hamming=False)

    thinkplot.subplot(2)
    thinkplot.config(xticks=xticks, xlabel='Frequency (Hz)')
    discontinuity(num_periods=30.25, hamming=False)

    thinkplot.subplot(3)
    thinkplot.config(xticks=xticks)
    discontinuity(num_periods=30.25, hamming=True)

    thinkplot.save(root='windowing1')

def window_plot():
    """Makes a plot showing a sinusoid, hamming window, and their product.
    """
    signal = thinkdsp.SinSignal(freq=440)
    duration = signal.period * 10.25
    wave1 = signal.make_wave(duration)
    wave2 = signal.make_wave(duration)

    ys = np.hamming(len(wave1.ys))
    ts = wave1.ts
    window = thinkdsp.Wave(ys, ts, wave1.framerate)

    wave2.hamming()

    thinkplot.preplot(rows=3, cols=1)

    pyplot.subplots_adjust(wspace=0.3, hspace=0.3,
                           right=0.95, left=0.1,
                           top=0.95, bottom=0.05)

    thinkplot.subplot(1)
    wave1.plot()
    thinkplot.config(axis=[0, duration, -1.07, 1.07])

    thinkplot.subplot(2)
    window.plot()
    thinkplot.config(axis=[0, duration, -1.07, 1.07])

    thinkplot.subplot(3)
    wave2.plot()
    thinkplot.config(axis=[0, duration, -1.07, 1.07],
                     xlabel='Time (s)')

    thinkplot.save(root='windowing2')

def chirp_spectrum():
    """Plots the spectrum of a one-second one-octave linear chirp.
    """
    signal = thinkdsp.Chirp(start=220, end=440)
    wave = signal.make_wave(duration=1)

    thinkplot.preplot(3, cols=3)
    duration = 0.01
    wave.segment(0, duration).plot(xfactor=1000)
    thinkplot.config(ylim=[-1.05, 1.05])

    thinkplot.subplot(2)
    wave.segment(0.5, duration).plot(xfactor=1000)
    thinkplot.config(yticklabels='invisible',
                     xlabel='Time (ms)')

    thinkplot.subplot(3)
    wave.segment(0.9, duration).plot(xfactor=1000)
    thinkplot.config(yticklabels='invisible')

    thinkplot.save('chirp3')

    spectrum = wave.make_spectrum()
    spectrum.plot(high=700)
    thinkplot.save('chirp1',
                   xlabel='Frequency (Hz)',
                   ylabel='Amplitude')

def chirp_spectrogram():
    """Makes a spectrogram of a one-second one-octave linear chirp.
    """
    signal = thinkdsp.Chirp(start=220, end=440)
    wave = signal.make_wave(duration=1, framerate=11025)
    spectrogram = wave.make_spectrogram(seg_length=512)

    print('time res', spectrogram.time_res)
    print('freq res', spectrogram.freq_res)
    print('product', spectrogram.time_res * spectrogram.freq_res)

    spectrogram.plot(high=700)

    thinkplot.save('chirp2',
                   xlabel='Time (s)',
                   ylabel='Frequency (Hz)')

def overlapping_windows():
    """Makes a figure showing overlapping hamming windows.
    """
    n = 256
    window = np.hamming(n)

    thinkplot.preplot(num=5)
    start = 0
    for i in range(5):
        xs = np.arange(start, start+n)
        thinkplot.plot(xs, window)

        start += n/2

    thinkplot.save(root='windowing3',
                   xlabel='Index',
                   axis=[0, 800, 0, 1.05])

def invert_spectrogram():
    """Tests Spectrogram.make_wave.
    """
    signal = thinkdsp.Chirp(start=220, end=440)
    wave = signal.make_wave(duration=1, framerate=11025)
    spectrogram = wave.make_spectrogram(seg_length=512)

    wave2 = spectrogram.make_wave()

    for i, (y1, y2) in enumerate(zip(wave.ys, wave2.ys)):
        if abs(y1 - y2) > 1e-14:
            print(i, y1, y2)

def main():
    chirp_spectrum()
    chirp_spectrogram()
    overlapping_windows()
    window_plot()
    three_spectrums()

if __name__ == '__main__':
    main()


"""
Example 3 - Logistic Regression without regularization

NOTE: The example and sample data is being taken from the "Machine Learning course by Andrew Ng" in Coursera.

Problem:
  Suppose that you are the administrator of a university department and
  you want to determine each applicant’s chance of admission based on their
  results on two exams. You have historical data from previous applicants
  that you can use as a training set for logistic regression. For each training
  example, you have the applicant’s scores on two exams and the admissions
  decision.
  Your task is to build a classification model that estimates an applicant’s
  probability of admission based the scores from those two exams.

  The file 'data/ex1data1.txt' contains the dataset for our
  Logistic regression problem.
"""

# initial imports

import numpy as np
from matplotlib import pyplot as plt

plt.ion()
from models.data_preprocessing import add_bias_unit, map_feature, feature_normalize
from models.logistic_regression import cost_function, predict, gradient_descent, gradient_function, sigmoid
from models.plotter import plot_decision_boundary

data = np.loadtxt('data/ex2data2.txt', delimiter=',')
X = data[:, :-1]
y = data[:, -1, np.newaxis]

"""
==================== Part 1: Plotting ====================
  We start the exercise by first plotting the data to understand the
  the problem we are working with.
"""
print('Plotting data with + indicating (y = 1) examples and o indicating (y = 0) examples.')
"""
Example plotting for multiple markers
x = np.array([1,2,3,4,5,6])
y = np.array([1,3,4,5,6,7])
m = np.array(['o','+','+','o','x','+'])

unique_markers = set(m)  # or yo can use: np.unique(m)

for um in unique_markers:
    mask = m == um
    # mask is now an array of booleans that van be used for indexing
    plt.scatter(x[mask], y[mask], marker=um)
"""
fig, ax = plt.subplots()
y_slim = y.ravel()
# plotting y=1 values
ax.scatter(x=X[y_slim == 1, 0], y=X[y_slim == 1, 1], marker='+', color='black', s=50)
# plotting y=0 values
# X[y_slim == 0, 0] is logical indexing with rows with y=0 only
ax.scatter(x=X[y_slim == 0, 0], y=X[y_slim == 0, 1], marker='o', color='y', s=30)

# labels
ax.set_xlabel('Microchip Test 1')
ax.set_ylabel('Microchip Test 2')

# Specified in plot order
ax.legend(['y = 1', 'y = 0'])

# Adding Polynomial Features

# Note that mapFeature also adds a column of ones for us, so the intercept term is handled
X = map_feature(X, degree=6)

# ============ Part 2: Compute Cost and Gradient ============
# initial sizes
m, n = X.shape

# Initialize fitting parameters
initial_theta = np.zeros([n, 1])

# Compute and display initial cost and gradient
cost = cost_function(initial_theta, X, y, regularized=False)
grad = gradient_function(initial_theta, X, y, regularized=False)
print('Cost at initial theta (zeros): {}'.format(cost))
print('Expected cost (approx): 0.693')
print('Gradient at initial theta (zeros) - first five values only:')
print(grad[:5])
print('Expected gradients (approx) - first five values only:')
print('0.0085\n 0.0188\n 0.0001\n 0.0503\n 0.0115\n')

test_theta = np.ones([X.shape[1], 1])
cost = cost_function(test_theta, X, y, lamda=10, regularized=True)
grad = gradient_function(test_theta, X, y, lamda=10, regularized=True)

print('Cost at test theta (with lambda = 10): '.format(cost))
print('Expected cost (approx): 3.16')
print('Gradient at test theta - first five values only: {}'.format(grad[:5]))
print('Expected gradients (approx) - first five values only:')
print(' 0.3460\n 0.1614\n 0.1948\n 0.2269\n 0.0922\n')

# =========== Performing gradient descent================
from models.data_preprocessing import feature_normalize

X_norm, mu, sigma = feature_normalize(X[:, 1:])
X_norm = add_bias_unit(X_norm)

from scipy.optimize import minimize

theta_history = []  # for plotting cost vs iteration graph

def theta_store(value, *args):
    theta_history.append(value)

initial_theta = np.zeros(n)
op_result = minimize(fun=cost_function, x0=initial_theta, jac=gradient_function, args=(X, y, 1, True), method='tnc', callback=theta_store)

print('Cost at theta found by Gradient descent: {}'.format(op_result.fun))
print('theta: {}'.format(op_result.x))

# converting theta_history into J_history
J_history = (np.array(theta_history[::-1]) @ op_result.x)

# plot J_history
fig1, ax1 = plt.subplots()
ax1.plot(range(J_history.size), J_history)
ax1.set_xlabel('Iterations')
ax1.set_ylabel('Cost')

theta = op_result.x[:, np.newaxis]
plot_decision_boundary(theta=theta, X=X, y=y, hypothesis=sigmoid, precision=0.1, fig=fig, ax=ax, feature_map=(map_feature, 6))

# ============== Part 4: Predict and Accuracies ==============
# Compute accuracy on our training set
p = predict(X, theta)

print('Train Accuracy: {}'.format(np.mean(p == y) * 100))
print('Expected accuracy (with lambda = 1): 83.1 (approx)')

# ===============End================
plt.ioff()
plt.show()


"""
General purpose math functions, mostly geometric in nature.
"""

import math

import numpy as np
from numpy.linalg import norm
from scipy.linalg import svd

from aspire.utils.random import Random

def cart2pol(x, y):
    """
    Convert Cartesian to Polar Coordinates. All input arguments must be the same shape.
    :param x: x-coordinate in Cartesian space
    :param y: y-coordinate in Cartesian space
    :return: A 2-tuple of values:
        theta: angular coordinate/azimuth
        r: radial distance from origin
    """
    return np.arctan2(y, x), np.hypot(x, y)

def cart2sph(x, y, z):
    """
    Transform cartesian coordinates to spherical. All input arguments must be the same shape.

    :param x: X-values of input co-ordinates.
    :param y: Y-values of input co-ordinates.
    :param z: Z-values of input co-ordinates.
    :return: A 3-tuple of values, all of the same shape as the inputs.
        (<azimuth>, <elevation>, <radius>)
    azimuth and elevation are returned in radians.

    This function is equivalent to MATLAB's cart2sph function.
    """
    hxy = np.hypot(x, y)
    r = np.hypot(hxy, z)
    el = np.arctan2(z, hxy)
    az = np.arctan2(y, x)
    return az, el, r

def _mgrid_slice(n, shifted, normalized):
    """
    Util to generate a `slice` representing a 1d linspace
    as expected by `np.mgrid`.

    :param shifted: shifted by half of grid or not when n is even.
    :param normalized: normalize the grid in the range of (-1, 1) or not.
    :return: `slice` to be used by `np.mgrid`.
    """

    num_points = n * 1j
    start = -n // 2 + 1
    end = n // 2

    if shifted and n % 2 == 0:
        start -= 1 / 2
        end -= 1 / 2
    elif n % 2 == 0:
        start -= 1
        end -= 1

    if normalized:
        # Compute the denominator for normalization
        denom = n / 2
        if shifted and n % 2 == 0:
            denom -= 1 / 2

        # Apply the normalization
        start /= denom
        end /= denom

    return slice(start, end, num_points)

def grid_1d(n, shifted=False, normalized=True, dtype=np.float32):
    """
    Generate one dimensional grid.

    :param n: the number of grid points.
    :param shifted: shifted by half of grid or not when n is even.
    :param normalized: normalize the grid in the range of (-1, 1) or not.
    :return: the rectangular and polar coordinates of all grid points.
    """

    r = x = np.mgrid[_mgrid_slice(n, shifted, normalized)].astype(dtype)

    return {"x": x, "r": r}

def grid_2d(n, shifted=False, normalized=True, indexing="yx", dtype=np.float32):
    """
    Generate two dimensional grid.

    :param n: the number of grid points in each dimension.
    :param shifted: shifted by half of grid or not when n is even.
    :param normalized: normalize the grid in the range of (-1, 1) or not.
    :param indexing: 'yx' (C) or 'xy' (F), defaulting to 'yx'.
    See https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html
    :return: the rectangular and polar coordinates of all grid points.
    """

    grid = _mgrid_slice(n, shifted, normalized)
    y, x = np.mgrid[grid, grid].astype(dtype)
    if indexing == "xy":
        x, y = y, x
    elif indexing != "yx":
        raise RuntimeError(
            f"grid_2d indexing {indexing} not supported." "  Try 'xy' or 'yx'"
        )

    phi, r = cart2pol(x, y)

    return {"x": x, "y": y, "phi": phi, "r": r}

def grid_3d(n, shifted=False, normalized=True, indexing="zyx", dtype=np.float32):
    """
    Generate three dimensional grid.

    :param n: the number of grid points in each dimension.
    :param shifted: shifted by half of grid or not when n is even.
    :param normalized: normalize the grid in the range of (-1, 1) or not.
    :param indexing: 'zyx' (C) or 'xyz' (F), defaulting to 'zyx'.
    See https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html
    :return: the rectangular and spherical coordinates of all grid points.
    """

    grid = _mgrid_slice(n, shifted, normalized)
    z, y, x = np.mgrid[grid, grid, grid].astype(dtype)

    if indexing == "xyz":
        x, y, z = z, y, x
    elif indexing != "zyx":
        raise RuntimeError(
            f"grid_3d indexing {indexing} not supported." "  Try 'xyz' or 'zyx'"
        )

    phi, theta, r = cart2sph(x, y, z)

    # TODO: Should this theta adjustment be moved inside cart2sph?
    theta = np.pi / 2 - theta

    return {"x": x, "y": y, "z": z, "phi": phi, "theta": theta, "r": r}

def uniform_random_angles(n, seed=None, dtype=np.float32):
    """
    Generate random 3D rotation angles
    :param n: The number of rotation angles to generate
    :param seed: Random integer seed to use. If None, the current random state is used.
    :return: A n-by-3 ndarray of rotation angles
    """
    # Generate random rotation angles, in radians
    with Random(seed):
        angles = np.column_stack(
            (
                np.random.random(n) * 2 * np.pi,
                np.arccos(2 * np.random.random(n) - 1),
                np.random.random(n) * 2 * np.pi,
            )
        )
    return angles.astype(dtype)

def register_rotations(rots, rots_ref):
    """
    Register estimated orientations to reference ones.

    Finds the orthogonal transformation that best aligns the estimated rotations
    to the reference rotations.

    :param rots: The rotations to be aligned in the form of a n-by-3-by-3 array.
    :param rots_ref: The reference rotations to which we would like to align in
        the form of a n-by-3-by-3 array.
    :return: o_mat, optimal orthogonal 3x3 matrix to align the two sets;
            flag, flag==1 then J conjugacy is required and 0 is not.
    """

    assert (
        rots.shape == rots_ref.shape
    ), "Two sets of rotations must have same dimensions."
    K = rots.shape[0]

    # Reflection matrix
    J = np.array([[1, 0, 0], [0, 1, 0], [0, 0, -1]])

    Q1 = np.zeros((3, 3), dtype=rots.dtype)
    Q2 = np.zeros((3, 3), dtype=rots.dtype)

    for k in range(K):
        R = rots[k, :, :]
        Rref = rots_ref[k, :, :]
        Q1 = Q1 + R @ Rref.T
        Q2 = Q2 + (J @ R @ J) @ Rref.T

    # Compute the two possible orthogonal matrices which register the
    # estimated rotations to the true ones.
    Q1 = Q1 / K
    Q2 = Q2 / K

    # We are registering one set of rotations (the estimated ones) to
    # another set of rotations (the true ones). Thus, the transformation
    # matrix between the two sets of rotations should be orthogonal. This
    # matrix is either Q1 if we recover the non-reflected solution, or Q2,
    # if we got the reflected one. In any case, one of them should be
    # orthogonal.

    err1 = norm(Q1 @ Q1.T - np.eye(3, dtype=rots.dtype), ord="fro")
    err2 = norm(Q2 @ Q2.T - np.eye(3, dtype=rots.dtype), ord="fro")

    # In any case, enforce the registering matrix O to be a rotation.
    if err1 < err2:
        # Use Q1 as the registering matrix
        U, _, V = svd(Q1)
        flag = 0
    else:
        # Use Q2 as the registering matrix
        U, _, V = svd(Q2)
        flag = 1

    Q_mat = U @ V

    return Q_mat, flag

def get_aligned_rotations(rots, Q_mat, flag):
    """
    Get aligned rotation matrices to reference ones.

    Calculated aligned rotation matrices from the orthogonal transformation
    that best aligns the estimated rotations to the reference rotations.

    :param rots: The reference rotations to which we would like to align in
        the form of a n-by-3-by-3 array.
    :param Q_mat:  optimal orthogonal 3x3 transformation matrix
    :param flag:  flag==1 then J conjugacy is required and 0 is not
    :return: regrot, aligned rotation matrices
    """

    K = rots.shape[0]

    # Reflection matrix
    J = np.array([[1, 0, 0], [0, 1, 0], [0, 0, -1]])

    regrot = np.zeros_like(rots)
    for k in range(K):
        R = rots[k, :, :]
        if flag == 1:
            R = J @ R @ J
        regrot[k, :, :] = Q_mat.T @ R

    return regrot

def get_rots_mse(rots_reg, rots_ref):
    """
    Calculate MSE between the estimated orientations to reference ones.

    :param rots_reg: The estimated rotations after alignment in the form of
        a n-by-3-by-3 array.
    :param rots_ref: The reference rotations.
    :return: The MSE value between two sets of rotations.
    """
    assert (
        rots_reg.shape == rots_ref.shape
    ), "Two sets of rotations must have same dimensions."
    K = rots_reg.shape[0]

    diff = np.zeros(K)
    mse = 0
    for k in range(K):
        diff[k] = norm(rots_reg[k, :, :] - rots_ref[k, :, :], ord="fro")
        mse += diff[k] ** 2
    mse = mse / K
    return mse

def common_line_from_rots(r1, r2, ell):
    """
    Compute the common line induced by rotation matrices r1 and r2.

    :param r1: The first rotation matrix of 3-by-3 array.
    :param r2: The second rotation matrix of 3-by-3 array.
    :param ell: The total number of common lines.
    :return: The common line indices for both first and second rotations.
    """

    assert r1.dtype == r2.dtype, "Ambiguous dtypes"

    ut = np.dot(r2, r1.T)
    alpha_ij = np.arctan2(ut[2, 0], -ut[2, 1]) + np.pi
    alpha_ji = np.arctan2(ut[0, 2], -ut[1, 2]) + np.pi

    ell_ij = alpha_ij * ell / (2 * np.pi)
    ell_ji = alpha_ji * ell / (2 * np.pi)

    ell_ij = int(np.mod(np.round(ell_ij), ell))
    ell_ji = int(np.mod(np.round(ell_ji), ell))

    return ell_ij, ell_ji

def crop_pad_2d(im, size, fill_value=0):
    """
    :param im: A 2-dimensional numpy array
    :param size: Integer size of cropped/padded output
    :return: A numpy array of shape (size, size)
    """

    im_y, im_x = im.shape
    # shift terms
    start_x = math.floor(im_x / 2) - math.floor(size / 2)
    start_y = math.floor(im_y / 2) - math.floor(size / 2)

    # cropping
    if size <= min(im_y, im_x):
        return im[start_y : start_y + size, start_x : start_x + size]
    # padding
    elif size >= max(im_y, im_x):
        # ensure that we return in the same dtype as the input
        to_return = fill_value * np.ones((size, size), dtype=im.dtype)
        # when padding, start_x and start_y are negative since size is larger
        # than im_x and im_y; the below line calculates where the original image
        # is placed in relation to the (now-larger) box size
        to_return[-start_y : im_y - start_y, -start_x : im_x - start_x] = im
        return to_return
    else:
        # target size is between mat_x and mat_y
        raise ValueError("Cannot crop and pad an image at the same time.")


# # Ejercicio _colabirativo_

#cambios introducidos 25/01
import numpy as np

# **Ejercicio 1**: Escribir un arreglo con 100 números equiespaciados del 0 al 9. Pista: `linspace`

np.linspace(0,9,100)

# **Ejercicio 2:** crear un arreglo 1D de 20 ceros.
# Reemplazar los primeros 15 elementos por unos.

arr = np.zeros(20)
nuevo = np.ones(15)
arr[:15] = nuevo
arr

# **Ejercicio 3:** crear un arreglo 1D de 50 ceros.
# Reemplazar los primeros 25 elementos por los números naturales del 0 al 24.

arr = np.zeros(50)
new = np.arange(25)
arr[:25] = new
arr

# **Ejercicio 4:** crear un arreglo 2D de 3 filas y 3 columnas, lleno de ceros.
# Reemplazar los elemento de la segunda columna por los números 1, 2 y 3 respectivamente.
# Es decir, crear la siguiente matriz:
#
# ```
# 0 1 0
# 0 2 0
# 0 3 0
# ```

arr = np.zeros((3,3))
arr[:,1] = [1,2,3]
arr

# **Ejercicio 5:** crear un arreglo 2D de 3 filas y 3 columnas, lleno de ceros.
# Reemplazar los elemento de la diagonal por unos. Es decir, crear la siguiente matriz:
#
# ```
# 1 0 0
# 0 1 0
# 0 0 1
# ```

arr = np.zeros((3,3))
for i in range(3):
    arr[i,i] = 1

# **Ejercicio 6:** crear un arreglo 2D de 100 filas y 100 columnas, lleno de ceros.
# Reemplazar los elemento de la diagonal por unos. Es decir, crear la siguiente matriz:
#
# ```
# 1 0 0 ... 0 0 0
# 0 1 0 ... 0 0 0
# 0 0 1 ... 0 0 0
# ...
# 0 0 0 ... 1 0 0
# 0 0 0 ... 0 1 0
# 0 0 0 ... 0 0 1
# ```

arr = np.zeros((100,100))
arr = np.eye(100)
arr


from scipy.optimize import linprog
import numpy as np

# Objective function
z = np.array([300,500,200])
expense = 75000

# Constraints
C = np.array([
    [ 10, 7.5,   4],        #C1
    [  0,  10,   0],        #C2
    [0.5, 0.4, 0.5],        #C3
    [  0, 0.4,   0],        #C4
    [0.5, 0.1, 0.5],        #C5
    [0.4, 0.2, 0.4],        #C6
    [  1, 1.5, 0.5],        #C7
    [  1,   0,   0],        #C8
    [  0,   1,   0],        #C9
    [  0,   0,   1]         #C10
])

b = np.array([4350, 2500, 280, 140, 280, 140, 700, 300, 180, 400])

# Bounds
x1 = (0, None)
x2 = (0, None)
x3 = (0, None)

#Solution
sol = linprog(-z, A_ub = C, b_ub = b, bounds = (x1, x2, x3), method='simplex')

#Profit Monthly.
profit = (sol.fun*-1) - expense

print(f"x1 = {sol.x[0]}, x2 = {sol.x[1]}, x3 = {sol.x[2]}, z = {profit}")


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

# Cost function
def cost(m, x, y, z):
	err=0.0
	for i in range(0, m):
		err += ((hypothesis(i, x, z) - y[i])**2)
	err = err/(2*m)
	return float(err)

# Delivetive of cost function by theta0
def find_local_minimum_theta0(m, _input, output, theta_array):
	err=0.0
	for i in range(0, m):
		err += (hypothesis(i, _input, theta_array) - output[i])
	err = err/m
	return float(err)

# Delivetive of cost function by theta1
def find_local_minimum_theta1(m, _input, output, theta_array):
	err=0.0
	for i in range(0, m):
		err += (hypothesis(i, _input, theta_array) - output[i])*_input[i]
	err = err/m
	return float(err)

# Hypothesis
def hypothesis(i, x, z):
	h =  z[0]*1+z[1]*x[i]
	return h

# Update theta by Gradient Descent
# Define theta0 as z0
# Define theta1 as z1
def find_local_minimum_theta(alpha, theta0, theta1, m, _input, output):
	z0 = theta0
	z1 = theta1
	t_z0 = 0.0
	t_z1 = 0.0
	r = 1

	while 1 :
		print "===== Round %d =====" %(r)
		z = np.array([z0, z1])
		t_z0 = t_z0 - alpha * find_local_minimum_theta0(m, _input, output, z)
		t_z1 = t_z1 - alpha * find_local_minimum_theta1(m, _input, output, z)
		if z0 == t_z0 and z1 == t_z1 :
			break
		else:
			print " -> update thata0 from %.8f to %.8f" %(z0, t_z0)
			print " -> update thata1 from %.8f to %.8f" %(z1, t_z1)
			# Update Thata
			z0 = t_z0
			z1 = t_z1
		r += 1
	return np.array([z0,z1])

# Generate linear function
def graph_gen(low, high, size):
	g=np.arange(size, dtype=float)
	for i in range(0 , size):
		g[i] = i + random.uniform(low,high)
	return g

# Input used for tranning
#x=np.array([0,1,2,3,4,5])
x=np.arange(100,dtype=float)

# Output used for tranning
#y=np.array([0,1,2,3,4,5])
#y=np.random.normal(5,1,100)
y=graph_gen(0,5,100)

# Initiaize theta
z=np.array([0,0])

# Range of theta value
theta_range=50

# Size of rows or Columns
theta_sz=2*theta_range+1 #1 for including 0 value

# Number of input elements
m=x.size

# Results of Squared Error
result_squared_err = np.ones(((2*theta_range)+1, (2*theta_range)+1), dtype=float)

# Leaning Rate
alpha = 0.00055

# Run Gradient Descent
theta_array = find_local_minimum_theta(alpha, 0.0, 0.0, m, x, y)
print "========================="
print "Gradient Descent : %f    " %(cost(m,x,y,theta_array))
print "========================="

# Plot original data
plt.plot(x,y,'rs')

# Plot linear regression function
x_grph=np.arange(m);
plt.plot(x_grph, theta_array[0]+x_grph*(theta_array[1]), 'b-')

# Display Graph
#plt.axis([0.,100.,0.,100.])
plt.show()


from numpy import *
from matplotlib.pyplot import *

def f(beta):
    return cosh(beta)*cos(beta) + 1

def damped(beta):
    """Damp the amplitude of f. It grows like cosh, i.e. exp."""
    return exp(-beta)*f(beta)

def plot_f():
    beta = linspace(0, 20, 501)
    #y = f(x)
    y = damped(beta)
    plot(beta, y, 'r', [beta[0], beta[-1]], [0, 0], 'b--')
    grid('on')
    xlabel(r'$\beta$')
    ylabel(r'$e^{-\beta}(\cosh\beta\cos\beta +1)$')
    savefig('tmp1.png'); savefig('tmp1.pdf')
    show()

plot_f()

from nonlinear_solvers import bisection
# Set up suitable intervals
intervals = [[1, 3], [4, 6], [7, 9]]
betas = []  # roots
for beta_L, beta_R in intervals:
    beta, it = bisection(f, beta_L, beta_R, eps=1E-6)
    betas.append(beta)
    print f(beta)
print betas

# Find corresponding frequencies

def omega(beta, rho, A, E, I):
    return sqrt(beta**4/(rho*A/(E*I)))

rho = 7850  # kg/m^3
E = 1.0E+11 # Pa
b = 0.025   # m
h = 0.008   # m
A = b*h
I = b*h**3/12

for beta in betas:
    print omega(beta, rho, A, E, I)


import numpy as np
"""
    Implementation of the loss function

    Arguments:
    y_pred -- probability vector corresponding to your label predictions, shape (1, number of examples)
    Y -- true "label" vector (for example: containing 0 if non-cat, 1 if cat), shape (1, number of examples)

    Returns:
    loss

"""
# Loss function is used to monitor, how the model is performing.
# It is supposed to approach global optimum i.e. smallest value possible.

def logistic_loss(a3, Y):
    m = Y.shape[0]

    logprobs = np.multiply(-np.log(a3),Y) + np.multiply(-np.log(1 - a3), 1 - Y)
    cost = 1./m * np.sum(logprobs)

    return cost

def MAE(y_pred, y):
    cost = np.sum(np.abs(y_pred - y))/m
    return cost

def MSE(y_pred, y):
    cost = np.sum((y_pred - y)**2)/m
    return cost


import numpy as np
from scipy.interpolate import CubicSpline
from scipy import integrate

def arc_length(cubic_spline,parameterization:np.ndarray):
    """
    Parameters
    ----------
    cubic_spline : scipy.interpolate._cubic.CubicSpline
        Cubic spline object
    parameterization : numpy.ndarray
        Array of size N. Represents the parameterization values of the cubic spline.
        Must be monotonically increasing.
    Returns
    -------
    arclength : numpy.ndarray
        Array of size N.
    errs :  numpy.ndarray
        DESCRIPTION.
    """
    df = lambda t : np.sqrt(np.sum(cubic_spline.derivative()(t)**2)) # arc-length
    segments = len(parameterization)-1
    sub_arclength = np.zeros((segments,))
    errs = np.zeros((segments,))
    for i in range(segments):
        subf,suberr = integrate.quad(df,parameterization[i],parameterization[i+1])
        sub_arclength[i] = subf
        errs[i] = suberr
    arclength = np.hstack((0,sub_arclength.cumsum()))
    return arclength,errs

def alp_spline(xyz:np.ndarray):
    ppdist = np.sqrt(np.sum(np.diff(xyz)**2,axis=0)) # point to point distance

    non_zero_indices = ppdist>0
    ppdist = ppdist[non_zero_indices]

    xyz = xyz[:,np.hstack((True,non_zero_indices))]

    linear_dist = np.hstack((0,ppdist.cumsum()))
    ldp_cs = CubicSpline(linear_dist,np.transpose(xyz))

    arclength,errs = arc_length(ldp_cs,linear_dist)
    alp_cs = CubicSpline(arclength,np.transpose(xyz))
    return linear_dist,ldp_cs,arclength,alp_cs


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Mar 10 16:23:09 2019

@author: alankar
"""

import numpy as np
#import matplotlib.pyplot as plt

distance = lambda x:np.dot(x,x.T)

dimension = 10
N = int(1e4)
x = np.random.uniform(low=-1,high=1,size=(N,dimension))
length = np.diag(distance(x))
f = np.piecewise(length,[length<=1.],[1.,0.])
I = (2**dimension/N)*np.sum(f)
varf = (1/N)*np.sum(f**2)-(I/2**dimension)**2
error = (2**dimension)*np.sqrt(varf/N)
print('Volume of a unit sphere in %d dimension: %f'%(dimension,I))
print('Error in the estimate: %f'%error)

"""
Output:

Volume of a unit sphere in 10 dimension: 2.150400
Error in the estimate: 0.468763
"""


import numpy as np

def simpson_formula(inte, func):
    """ Calculate integral of function func on interval [inte[0], inte[1]].

    Args:
        inte: list/ndarray, the intergration interval
        func: function object, the intergrand

    Returns:
        double, the integral value
    """
    x_lst = np.linspace(inte[0], inte[1], 3)
    # Simpson integral formula cofficients
    C = np.array([1, 4, 1])
    return (inte[1] - inte[0]) * np.sum(C @ func(x_lst)) / np.sum(C)

def adaptive_Simpson_formula(inte, func, tol=1e-9):
    """ Calculate integral of function func on interval[inte[0], inte[1]] with accuracy tol.

    Args:
        inte: list/ndarray, the intergration interval
        func: function, intergrand
        tol: double, accuracy of the integral value

    Returns:
        double, integral value
    """
    # Simpson formula
    S1 = simpson_formula(inte, func)
    S2 = simpson_formula([inte[0], sum(inte)/2], func)
    S3 = simpson_formula([sum(inte)/2, inte[1]], func)

    if np.abs(S1 - S2 - S3) >= 10 * tol:
        S2 = adaptive_Simpson_formula([inte[0], sum(inte)/2], func, 5 * tol)
        S3 = adaptive_Simpson_formula([sum(inte)/2, inte[1]], func, 5 * tol)

    return S2 + S3

def f(x):
    return np.exp(-1 * x**2)

def g(x):
    return np.sin(x) / x

if __name__ == '__main__':
    # integration interval
    inte = [1e-32, 1]
    # adaptive Simpson formula
    x1 = adaptive_Simpson_formula(inte, f)
    x2 = adaptive_Simpson_formula(inte, g)
    print(f"The first integral' value is {x1:.7f} by adaptive Simpson formula.")
    print(f"The second integral's value is {x2:.7f} by adaptive Simpson formula.")


import matplotlib.pyplot as plt
import numpy as np

# Define a main() function that prints a data statistics.
def main():
    data = np.loadtxt('data/populations.txt')
    year, hares, lynxes, carrots = data.T  # trick: columns to variables

    plt.axes([0.1, 0.1, 0.5, 0.8])
    plt.plot(year, hares, year, lynxes, year, carrots)
    plt.legend(('Hare', 'Lynx', 'Carrot'), loc=(1.05, 0.5))
    plt.show()

    # calculate mean and std population for each species (column) separately is slower.
    # calculating separately takes ~3.06 µs for each mean operation and on array
    # that contains all the data by axis=0 takes ~4.68 µs
    populations = data[:, 1:]
    print("Means by species: {}".format(populations.mean(axis=0)))
    print("Standard deviation by species: {}".format(populations.std(axis=0)))

    # calculate year when each species had the larges population
    max_populations = np.argmax(populations, axis=0)
    print("Max populations in years: {}".format(year[max_populations]))

    # calculate species that has larges population for each year
    max_species_idx = np.argmax(populations, axis=1)
    max_species = np.array(['H', 'L', 'C'])[max_species_idx]
    print("Max species: {}".format(tuple(zip(year, max_species))))

    # calculate years when any of the populations is above 50000
    above_mask = np.any(np.greater(populations, 50000), axis=1)
    print("Years any population above 50000: {}".format(year[above_mask]))

    # find the top 2 years for each species when they had the lowest populations
    sorted_indices = populations.argsort(axis=0)
    years_sorted = year[sorted_indices]
    print("Two smallest years: {}".format(years_sorted[:2, :]))

    # compare (plot) the change in hare population and the number of lynxes
    hare_gradients = np.gradient(hares)
    plt.axes([0.1, 0.1, 0.7, 0.8])
    plt.plot(year, hare_gradients, year, lynxes)
    plt.legend(('Hare', 'Lynx'), loc=(1.05, 0.5))
    plt.show()

    # calculate correlation
    print("Hares and lynxes correlation: {}".format(np.corrcoef(hare_gradients, lynxes)[0, 1]))

# This is the standard boilerplate that calls the main() function.
if __name__ == '__main__':
    main()


import numpy as np

from numba import jit, njit, prange

def is_symmetric(A, rtol=1e-05, atol=1e-08):
    # https://stackoverflow.com/questions/42908334/checking-if-a-matrix-is-symmetric-in-numpy
    return np.allclose(A, A.T, rtol=rtol, atol=atol)

def is_square(A):
    return A.shape[0] == A.shape[1]

@njit
def vec(A):
    # https://stackoverflow.com/questions/25248290/most-elegant-implementation-of-matlabs-vec-function-in-numpy
    return A.T.ravel().reshape(-1, 1)

def vech(A):
    if not is_square(A):
        raise Exception("It must be a square matrix.")
    return A.T[np.triu_indices_from(A)].reshape(-1, 1)

@njit(fastmath=True)
def kron(A, B=None):
    if B is not None:
        return np.kron(np.ascontiguousarray(A), np.ascontiguousarray(B))
    else:
        return np.kron(np.ascontiguousarray(A), np.ascontiguousarray(A))

@njit(fastmath=True,parallel=False)
def kron_preallocated(A, B, out):
#     #https://stackoverflow.com/questions/56067643/speeding-up-kronecker-products-numpy
    m, n = A.shape
    p, q = B.shape
    for i in prange(m):
        for j in range(p):
            for k in range(n):
                for l in range(q):
                    out[i, j, k, l] = A[i, k] * B[j, l]
    return out

# @njit(fastmath=True)
# def kron_vector(a, b):
#     return np.outer(a, b).reshape(-1, 1)

@njit
def unit_vector(n, i, j, p=None):
    if p is None:
        p = int(0.5 * n * (n + 1))
    u = np.zeros(p)
    i += 1
    j += 1
    u_idx = int((j - 1) * n + i - 0.5 * j * (j - 1)) - 1
    u[u_idx] = 1
    return u.reshape(p, 1)

@njit
def tij_matrix(n, i, j):
    T = np.zeros(shape=(n, n))
    T[i, j] = 1
    T[j, i] = 1
    return T

@njit(parallel=True)
def dup_matrix(n):
    # TODO: Deprecated
    p = int(n * (n + 1) / 2)
    nsq = n ** 2
    DT = np.zeros(shape=(p, nsq))
    for i in prange(n):
        for j in range(i + 1):
            u = unit_vector(n, i, j, p)
            T = tij_matrix(n, i, j)
            DT += u @ vec(T).T
    return DT.T

@njit
def diag_dup_prod(A, premul=True):
    n2, m2 = A.shape
    if premul:
        n = int(np.sqrt(n2))
        return A[::n+1, :]
    else:
        m = int(np.sqrt(m2))
        return A[:, ::m+1]

@njit(parallel=True)
def dupl_cols(n):
    k = 0
    col = np.empty(n ** 2, dtype=np.int32)
    for i in prange(n):
        for j in range(i, n):
            col[i + j * n] = col[j + i * n] = k
            k += 1
    return col

@njit
def duplication(n):
    # TODO: Validar que está funcionando bien!
    col = dupl_cols(n)
    dn = np.zeros((n ** 2, int(n * (n + 1) / 2)), dtype=np.int32)
    for i in range(n ** 2):
        dn[i, col[i]] = 1
    return dn

@njit(fastmath=True, parallel=True)
def kron_diag_dup(A, B):
    assert A.shape[1] == B.shape[1]
    q, n = A.shape
    m, _ = B.shape
    C = np.empty(shape=(m * q, n))
    for i in prange(n):
        C[:, i] = np.outer(A[:, i], B[:, i]).flatten()
    return C

def matrix_properties(a, rtol=1e-05, atol=1e-08):
    assert a.ndim == 2
    print(f"Any NaN? {np.isnan(a).any()}")
    print(f"Any Inf? {np.isinf(a).any()}")
    print(f"Is Square? {is_square(a)}")
    print(f"Is Symmetric? {is_symmetric(a, rtol=rtol, atol=atol)}")


import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
from numpy.linalg import norm
import os
from random import normalvariate
from math import sqrt
FOLDER = "./Dataset/"
FILES = os.listdir(FOLDER)
TEST_DIR = "./Testset/"

def load_images_train_and_test(TEST):
    test=np.asarray(Image.open(TEST)).flatten()
    train=[]
    for name in FILES:
        train.append(np.asarray(Image.open(FOLDER + name)).flatten())
    train= np.array(train)
    return test,train

def normalize(test,train):
    """
    TODO : Normalize test and train and return them properly
    Hint : To calculate mean properly use two arguments version of mean numpy method (https://www.javatpoint.com/numpy-mean)
    Hint : normalize test with train mean
    """
    arr = np.mean(train, axis=0)

    normalized_test = test - arr

    normalized_train = np.empty((0,train.shape[1]))
    for i in range(train.shape[0]):
        normalized_train = np.vstack([normalized_train, (np.array(train[i, :]) - arr)])

    return normalized_test,normalized_train
def svd_function(images):
    """
    TODO : implement SVD (use np.linalg.svd) and return u,s,v
    Additional(Emtiazi) todo : implement svd without using np.linalg.svd
    """
    if iteration == 0:
        return svd(images)
    else:
        return u,s,v
#   return np.linalg.svd(images, full_matrices=False)

def project_and_calculate_weights(img,u):
    """
    TODO : calculate element wise multiplication of img and u . (you can use numpy methods)
    """
    return np.multiply(img, u)

def predict(test,train):
    """
    TODO : Find the most similar face to test among train set by calculating errors and finding the face that has minimum error
    return : index of the data that has minimum error in train dataset
    Hint : error(i) = norm(train[:,i] - test)       (you can use np.linalg.norm)

    """
    min_error = 1_000_000_000
    min_index = 0
    for i in range(train.shape[1]):
        error = np.linalg.norm(train[:,i] - test)
        if error < min_error:
            min_index = i
            min_error = error

    return min_index
def plot_face(tested,predicted):
    """
    TODO : Plot tested image and predicted image . It would be great if you show them next to each other
    with subplot and figures that you learned in matplotlib video in the channel.
    But you are allowed to show them one by one
    """

    f, plt_arr = plt.subplots(1, 2 ,figsize=(7, 3))
    f.suptitle('Result Plots')

    plt_arr[0].imshow(tested, cmap = "gray")
    plt_arr[0].set_title('tested')

    plt_arr[1].imshow(predicted, cmap = "gray")
    plt_arr[1].set_title('predicted')

###################################### SVD part ######################################

def randomUnitVector(n):
    unnormalized = [normalvariate(0, 1) for _ in range(n)]
    theNorm = sqrt(sum(x * x for x in unnormalized))
    return [x / theNorm for x in unnormalized]

def svd_1d(A):
    ''' The one-dimensional SVD
        we use  Power iteration method to calculate svd :
        In fact this algorithm will produce the greatest (in absolute value) eigenvalue of A,
        so with help of this algo we can find eigen values and eigen vectors
        (eigen vectors will be orthogonal) one by one and then construct svd from them.

        In Power iteration method we start with v_0 which might be a random vector.
        At every iteration this vector is updated using following rule:
                v_k+1 = Bv_k / ||Bv_k||
        We’ll continue until result has converged.
        Power method has few assumptions:
         - v_0 has a nonzero component in the direction of an eigenvector associated with the dominant eigenvalue.
           (it means v_0 is NOT orthogonal to the eigenvector)
           Initializing v_0 randomly minimizes possibility that this assumption is not fulfilled.
         - matrix A has dominant eigenvalue which has strictly greater magnitude than other eigenvalues.
        These assumptions guarantee that algorithm converges to a reasonable result.

        So at the end when v_i converges enough this method found dominant singular value/eigenvector and return the eigen vector.
    '''

    n, m = A.shape

    # v_0 = x = random unit vector
    x = randomUnitVector(min(n,m))
    currentV = x

    #v_1 = ?
    lastV = None

    # calculate B according to shape of A so that we smaller size computation
    if n > m:
        B = np.dot(A.T, A)
    else:
        B = np.dot(A, A.T)

    # v_k+1 = Bv_k / ||Bv_k||
    iterations = 0
    epsilon=1e-10
    while True:
        iterations += 1
        lastV = currentV
        currentV = np.dot(B, lastV)
        currentV = currentV / norm(currentV)

        # continue until result has converged (updates are less than threshold).
        # if two normal vector become same then inner product of them will be 1
        if abs(np.dot(currentV, lastV)) > 1 - epsilon:
            return currentV

def svd(A):
    '''
        Compute the singular value decomposition of a matrix A
        using the power method. A is the input matrix, and k
        is the number of singular values you wish to compute.
        If k is None, this computes the full-rank decomposition.
    '''
    A = np.array(A, dtype=float)
    n, m = A.shape
    # save (singular value, u, v) as each element
    svdSoFar = []
    k = min(n, m)

    for i in range(k):
        matrixFor1D = A.copy()

        # remove all previous eigen values and vectors (dominant one) from matrix
        # so the next dominant eigen value and vector won't be repetitive
        # A_next = A-(singular_value)(u)(v.T)
        for singularValue, u, v in svdSoFar[:i]:
            matrixFor1D -= singularValue * np.outer(u, v)

        # 1. find v_i which is the next eigen vector for B = A.T @ A
        # 2. find sigma_i = ||Av_i|| (reason is in the next line)
        # ||Av_i||^2 = (Av_i).T A (Av_i) = (v_i).T @ A.T @ A @ v_i = (v_i).T @ (landa_i * v_i) ==v_i is orthonormal== landa_i = sigma_i ^ 2
        # 3. find u_i = 1/sigma_i * Av_i
        if n > m:
            # 1
            v = svd_1d(matrixFor1D)
            u_unnormalized = np.dot(A, v)
            # 2
            sigma = norm(u_unnormalized)  # next singular value
            # 3
            u = u_unnormalized / sigma
        else:
            u = svd_1d(matrixFor1D)  # next singular vector
            v_unnormalized = np.dot(A.T, u)
            sigma = norm(v_unnormalized)  # next singular value
            v = v_unnormalized / sigma

        # add new (sigma, u, v) we have found
        svdSoFar.append((sigma, u, v))

    singularValues, us, vs = [np.array(x) for x in zip(*svdSoFar)]
    return us.T, singularValues, vs
###################################################################################

true_predicts=0
all_predicts=0
iteration = 0
for TEST_FILE in os.listdir(TEST_DIR):
    # Loading train and test
    test,train=load_images_train_and_test(TEST_DIR+TEST_FILE)
    test,train=normalize(test,train)

    test=test.T
    train=train.T
    test = np.reshape(test, (test.size, 1))

    # Singular value decomposition
    u,s,v=svd_function(train)

    # Weigth for test
    w_test=project_and_calculate_weights(test,u)
    w_test=np.array(w_test, dtype='int8').flatten()

    # Weights for train set
    w_train=[]
    for i in range(train.shape[1]):
        w_i=project_and_calculate_weights(np.reshape(train[:, i], (train[:, i].size, 1)),u)
        w_i=np.array(w_i, dtype='int8').flatten()
        w_train.append(w_i)
    w_train=np.array(w_train).T

    # Predict
    index_of_most_similar_face=predict(w_test,w_train)
    # Showing results
    print("Test : "+TEST_FILE)
    print(f"The predicted face is: {FILES[index_of_most_similar_face]}")
    print("\n***************************\n")

    # Calculating Accuracy
    all_predicts+=1
    if FILES[index_of_most_similar_face].split("-")[0]==TEST_FILE.split("-")[0]:
        true_predicts+=1
        # Plotting correct predictions
        plot_face(Image.open(TEST_DIR+TEST_FILE),Image.open(FOLDER+FILES[index_of_most_similar_face]))
    else:
        # Plotting wrong predictions
        plot_face(Image.open(TEST_DIR+TEST_FILE),Image.open(FOLDER+FILES[index_of_most_similar_face]))
    iteration += 1
# Showing Accuracy
accuracy=true_predicts/all_predicts
print(f'Accuracy : {"{:.2f}".format(accuracy*100)} %')


import cmath
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import scipy.signal as signal
from ..utils.Autocorrelation import Autocorrelation

sns.set()

class AutocorrelationMethod():

    def __init__(self):
        self.f = None
        self.default_f = np.linspace(0, 0.5, 500)
        self.x = None
        self.p = None
        self.a = None
        self.var_u = None
        self.P = None

        self.r_xx = Autocorrelation()

    def estimate(self, x, f=None, p=2):
        '''
            Estimates P_xx for given signal sequence x.

            Args:
                x (numpy array of doubles): Signal
                f (numpy array of doubles): Frequence
                p (integer): Polinomial order in A(f)
        '''
        # Init values
        self.x = x
        if f is None:
            self.f = self.default_f
        else:
            self.f = f
        self.p = p
        self.P = np.zeros(len(self.f))

        # Estimate autoccorelation
        self.r_xx.estimate(x)

        # Compose matrix Rxx
        Rxx = np.zeros([p, p])
        for i in range(p):
            for j in range(p):
                Rxx[i][j] = self.r_xx[i - j]

        # Compose vector rxx
        rxx_vec = np.zeros([p, 1])
        for i in range(p):
            rxx_vec[i] = self.r_xx[i + 1]

        # Calculate a and append [1] for a[0]
        self.a = np.matmul(-np.linalg.inv(Rxx),
                           rxx_vec)
        self.a = np.append([[1]], self.a, axis=0)

        # Calculate var_u
        self.var_u = 0
        for i in range(p + 1):
            self.var_u += self.a[i] * self.r_xx[-i]

        # Calculate P
        for fi in range(len(self.f)):
            A = 0
            for i in range(p + 1):
                A += self.a[i] * cmath.exp(-1j * 2*cmath.pi * self.f[fi] * i)
            self.P[fi] = self.var_u / pow(abs(A), 2)

    def plot(self):
        '''
            Plots estimated P.
        '''
        # Check if anything is estimated.
        if self.f is None or self.P is None:
            return

        plt.figure()
        plt.semilogy(self.f, self.P)
        plt.title('Autocorrelation method estimation')
        plt.xlabel('f [Hz]')
        plt.ylabel('P')
        plt.show()

    def __getitem__(self, key):
        if key == 'f':
            return self.f
        if key == 'P':
            return self.P
        if key == 'x':
            return self.x
        if key == 'a':
            return self.a
        if key == 'var_u':
            return self.var_u
        if key == 'p':
            return self.p
        return None


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 28 15:41:38 2019

@author: alankar
"""

import numpy as np

def NR_sec(func,x0,tol,verbose,*args): #Newton Raphson
    x_new = 0.
    x_old = x0
    counter = 0
    while(np.abs(x_new-x_old[0])>=tol):
        counter += 1
        #x_new = x_old[1]-func(x_old[1],*args)*((x_old[1]-x_old[0])/(func(x_old[1],*args)-func(x_old[1],*args)))
        x_new = (x_old[0]*func(x_old[1],*args)-x_old[1]*func(x_old[0],*args))/(func(x_old[1],*args)-func(x_old[0],*args))
        x_old[0] = x_old[1]
        x_old[1] = x_new
    if (verbose==True): print("Converged in %d steps"%counter)
    return x_new

omega = 2.662e-6
R = 3.844e8
Mm = 7.348e22
Me = 5.974e24
G = 6.674e-11

def lagr1(r):
    r *= 1e8 #in units of 10^8 m
    return -(omega**2*r)-((G*Me)/r**2)+((G*Mm)/(R-r)**2)

print('Distance of Earth-Moon L1 from earth')
print("r = %.2e AU"%(NR_sec(lagr1,np.array([3.4,3.6]),1e-6,False)*1e8/1.496e+11)) # agrees with the value of L1 for Earth-Moon system available on literature

"""
Output

Distance of Earth-Moon L1 from earth
r = 2.37e-03 AU
"""


import numpy as np

def dct(f, axis=-1):
    """
    Compute the Discrete Cosine Transform over the specified axis.

    :param     f: The input array.
    :param  axis: Axis along which the DCT is computed. The default is over the last axis.
    :return    c: The computed DCT.
    """

    # Size of the input along the specified axis.
    n = f.shape[axis]

    # Create two vectors containing the integers from 0 to n-1.
    i = k = np.arange(n)

    # Compute the x-axis coordinate of the f function.
    x = (2 * i + 1) / (2 * n)

    # Compute the outer product of x and kπ, obtaining the nxn matrix that will
    # form the argument of the cosine.
    arg = np.multiply.outer(x, k * np.pi)

    # Normalization factors.
    alpha = np.where(k == 0, 1 / np.sqrt(n), np.sqrt(2 / n))

    # The orthonormal DCT basis.
    w = alpha * np.cos(arg)

    # Compute the convolution between the input array and the DCT basis.
    # The output contains the amplitude coefficient for every frequency.
    c = np.tensordot(f, w, axes=(axis, 0))

    # `axis` becomes the last dimension in the output of `np.tensordot`.
    # Move it back to its original position so that the output shape matches
    # the input shape.
    c = np.moveaxis(c, -1, axis)

    return c

def dctn(array, axes=None):
    """
    Compute the multidimensional Discrete Cosine Transform over the specified axes.

    :param array: The input array
    :param axes: Axes along which the DCT is computed. The default is over all axes.
    :return: The computed DCT
    """

    # Axes along which the DCT is computed. The default is over all axes.
    if axes is None:
        axes = range(array.ndim)

    # Apply the 1D DCT to each axis in sequence.
    for axis in axes:
        array = dct(array, axis=axis)

    return array


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

"""
最小二乘法线性回归
numpy
"""

import numpy as np
import matplotlib.pyplot as plt

def lstsq(X,Y):
    """
    最小二乘法线性回归
    """
    A = np.vstack([X, np.ones(len(X))]).T
    return np.linalg.lstsq(A, Y)[0]

if __name__ == '__main__':
    # 数据准备
    X = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    Y = [10, 11.5, 12, 13, 14.5, 15.5, 16.8, 17.3, 18, 18.7]

    #进行拟合
    a, b = lstsq(X,Y)
    print("y = %10.5fx + %10.5f" % (a, b))

    #展示拟合结果
    X1 = np.array(X)
    Y1 = np.array(Y)
    plt.plot(X1,Y1,'o',label='data',markersize=10)
    plt.plot(X1,a*X1+b,'b',label='line')
    plt.show()


import numpy as np

def LU_factor(A):
	"""Factor in place A in L*U=A. The lower triangular parts of A
	are the L matrix.  The L has implied ones on the diagonal.
	Args:
		A: N by N array
	Side Effects:
		A is factored in place.
	"""
	[Nrow, Ncol] = A.shape
	assert Nrow == Ncol
	N = Nrow
	for column in range(0,N):
		for row in range(column+1,N):
			mod_row = A[row]
			factor = mod_row[column]/A[column,column]
			mod_row = mod_row - factor*A[column,:]
			#put the factor in the correct place in the modified row
			mod_row[column] = factor
			#only take the part of the modified row we need
			mod_row = mod_row[column:N]
			A[row,column:N] = mod_row
	return

def LU_solve(A,b):
	"""Take a LU factorized matrix and solve it for RHS b Args:
	A: N by N array that has been LU factored with
	assumed 1's on the diagonal of the L matrix
	b: N by 1 array of righthand side
	Returns:
	x: N by 1 array of solutions
	"""
	[Nrow, Ncol] = A.shape
	assert Nrow == Ncol
	N = Nrow

	x = np.zeros(N)
	#temporary vector for L^-1 b
	y = np.zeros(N)

	#forward solve
	for row in range(N):
		RHS = b[row]
		for column in range(0,row):
			RHS -= y[column]*A[row,column]
		y[row] = RHS

	#back solve
	for row in range(N-1,-1,-1):
		RHS = y[row]
		for column in range(row+1,N):
			RHS -= x[column]*A[row,column]
		x[row] = RHS/A[row,row]

	return x


import numpy as np

def mean_square_error(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate MSE loss

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    MSE of given predictions
    """
    mse = 0
    for i in range(len(y_true)):
        mse += ((y_true[i] - y_pred[i]) ** 2)
    return mse / len(y_true)

def misclassification_error(y_true: np.ndarray, y_pred: np.ndarray,
                            normalize: bool = True) -> float:
    """
    Calculate misclassification loss

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values
    normalize: bool, default = True
        Normalize by number of samples or not

    Returns
    -------
    Misclassification of given predictions
    """
    misclassification = 0
    for i in range(y_true.size):
        if y_true[i] != y_pred[i]:misclassification +=1
    return misclassification/y_true.size if normalize else misclassification

def accuracy(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate accuracy of given predictions

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    Accuracy of given predictions
    """
    accuracy = 0
    for i in range(y_true.size):
        if y_true[i] == y_pred[i]:accuracy +=1
    return accuracy/y_true.size

def cross_entropy(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate the cross entropy of given predictions

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    Cross entropy of given predictions
    """
    raise NotImplementedError()


### Multivariate batch (standard) gradient descent algorithm

### author: Dr. Marko Mitic
### year 2015
### contact: miticm@gmail.com

import numpy as np
import matplotlib.pyplot as plt

class MultivariateBatchGD: #Main class for Batch Multivariate Gradient Descent

    def __init__(self, data):
        ### data is given in several-column format, last column is output
         self.m=len(data)
         self.size=data.shape
         self.x=data
         self.y=self.x[:,-1]

    def featurescaling(self):
        ### feature scaling using mean normalization and std instead of range
        self.xscaled=0.*self.x[:,0:self.x.shape[1]-1]

        for i in range (0, self.x.shape[1]-1): # last column is the output
            self.xscaled[:,i]=(self.x[:,i]-np.mean(self.x[:,i]))/np.std(self.x[:,i])

        #self.yscaled=(self.y-np.mean(self.y))/np.std(self.y) ##this is if you want to scale the output!
        self.yscaled=self.y
        return self.xscaled, self.yscaled

    def thetas(self):
        #self.thetas=np.random.rand(np.shape(self.xscaled)[1]) #randomly set values for theta
        self.thetas=np.zeros(3)
        return self.thetas

    def addones(self):
        self.xscaled=np.append(np.ones([self.m,1]),self.xscaled,1)
        return self.xscaled

    def hypothesis(self):
        #self.hy=self.thetas*self.xscaled
        self.hy=np.dot(self.xscaled, self.thetas)
        return self.hy

    def cost_function(self):
        "Return Cost function for hypothesis hy(of input x) and output y"
        #self.xtransp=self.xscaled.transpose()

        priv=np.dot(self.xscaled,(self.thetas.transpose()))
        self.J=(1./(2.*self.m))*sum((priv-self.y)**2)

        #priv=np.dot(self.xscaled,(self.thetas.transpose()))
        #self.J=(1./(2.*self.m))*sum((priv-self.yscaled)**2)
        return self.J

    def gradientdescent(self,alpha):
        "Perform gradient descent algorithm"
        self.a=alpha #leaarning rate
        temptheta=0.*np.arange(0.,np.shape(self.xscaled)[1])

        for i in range (0, self.x.shape[1]):
            #temptheta[i]=self.thetas[i]-self.a*(1./self.m)*sum(((self.hy[:,i]-self.yscaled)*self.xscaled[:,i]))
            temptheta[i]=self.thetas[i]-self.a*(1./self.m)*sum(((self.hy-self.yscaled)*self.xscaled[:,i]))

        self.thetas=temptheta

        return self.thetas

    def plotcost(self,col):
        plt.figure()
        iters=np.arange(0,len(self.Jhistory))
        plt.plot(iters, self.Jhistory, col)
        plt.xlabel('Number of iterations')
        plt.ylabel('Cost J')
        plt.show()

## END CLASS

################################################################################

#import data
data = np.loadtxt("data_MultiGD.txt", comments="#", delimiter=",", unpack=False)

gd=MultivariateBatchGD(data)

##initialize input and theta values
[gd.xscaled, gd.yscaled]=gd.featurescaling() ##dont scale output y! check Muktivariate BatchGD_class to see yscaled!
gd.xscaled=gd.addones()
gd.thetas=gd.thetas()

## starting hypothesis
gd.hy=gd.hypothesis()

#calculate initial gradient
gd.J=gd.cost_function()

max_iter=50
gd.Jhistory=np.zeros(max_iter)

for i in range (0,max_iter):

    gd.hy=gd.hypothesis()
    gd.J=gd.cost_function()
    gd.Jhistory[i]=gd.J
    gd.thetas=gd.gradientdescent(0.08) ##learning rate==0.008

    print "Current cost is", gd.J
    print 'Theta parameters are', gd.thetas

print 'Final Theta parameters', gd.thetas

gd.plotcost('b')

##END


""" Example showing how to create an annular NURBS surface as a Coons patch """

# -------------------------------------------------------------------------------------------------------------------- #
# Importing packages
# -------------------------------------------------------------------------------------------------------------------- #
import numpy as np
import nurbspy as nrb
import matplotlib.pyplot as plt

# -------------------------------------------------------------------------------------------------------------------- #
# Coons surface example: Annulus NURBS surface
# -------------------------------------------------------------------------------------------------------------------- #

# Define the outer circle
O = np.asarray([0.00, 0.00, 0.00])      # Circle center
X = np.asarray([1.00, 0.00, 0.00])      # Abscissa direction
Y = np.asarray([0.00, 1.00, 0.00])      # Ordinate direction
R = 1.00                                # Circle radius
theta_start = 0.00                      # Start angle (set any arbitrary value)
theta_end = 2*np.pi                     # End angle (set any arbitrary value)
nurbsOuter = nrb.CircularArc(O, X, Y , R, theta_start, theta_end).NurbsCurve

# Define the inner circle
O = np.asarray([0.00, 0.00, 0.00])      # Circle center
X = np.asarray([1.00, 0.00, 0.00])      # Abscissa direction
Y = np.asarray([0.00, 1.00, 0.00])      # Ordinate direction
R = 0.50                                # Circle radius
nurbsInner = nrb.CircularArc(O, X, Y , R, theta_start, theta_end).NurbsCurve

# Define start closing boundary
P = np.zeros((3, 2))
P[:, 0] = nurbsOuter.P[:, 0]
P[:, 1] = nurbsInner.P[:, 0]
W = np.asarray([nurbsOuter.W[0], nurbsInner.W[0]])
nurbsWest = nrb.NurbsCurve(control_points=P, weights=W)

# Define the west boundary (rational Bézier curve)
P = np.zeros((3, 2))
P[:, 0] = nurbsOuter.P[:, -1]
P[:, 1] = nurbsInner.P[:, -1]
W = np.asarray([nurbsOuter.W[-1], nurbsInner.W[-1]])
nurbsEast = nrb.NurbsCurve(control_points=P, weights=W)

# Create and plot the Coons NURBS surface to represent the annulus
coonsNurbsSurface = nrb.NurbsSurfaceCoons(nurbsOuter, nurbsInner, nurbsWest, nurbsEast).NurbsSurface
# coonsNurbsSurface = nrb.NurbsSurfaceRuled(nurbsOuter, nurbsInner).NurbsSurface

# Plot the annulus surface and some isoparametric curve
fig, ax = coonsNurbsSurface.plot(surface=True, surface_color='blue', control_points=False)
coonsNurbsSurface.plot_isocurve_u(fig, ax, np.linspace(0, 1, 20))
coonsNurbsSurface.plot_isocurve_v(fig, ax, np.linspace(0, 1, 5))
plt.show()


import random
import pylab
import numpy as np
def Markov_pi(N, delta):
        N_hits=0
        x, y=1, 1
        for i in range(1, N+1):
                Delta_x=random.uniform(-delta, delta)
                Delta_y=random.uniform(-delta, delta)
                if abs(x+Delta_x)<1 and abs(y+Delta_y)<1:
                        x+=Delta_x
                        y+=Delta_y
                if x**2+y**2<1: N_hits+=1
        return N_hits*4.0/N

def plot_Markov_pi_1(delta):
    X=[]
    Y=[]
    for i in range(1, 10000):
        X+=[i, ]
        Y+=[Markov_pi(i, delta), ]
    pylab.plot(X, Y, '-')
    pylab.show()

def plot_Markov_pi_2(N):
    X=[]
    Y=[]
    for delta in np.arange(-2, 2.05, 0.01):
        X+=[delta, ]
        Y+=[Markov_pi(N, delta), ]
    pylab.plot(X, Y, 'r-')
    pylab.xlabel('$\delta$')
    pylab.ylabel('Estimated value of pi')
    pylab.title('N=%s'%(N))
    pylab.show()


"""Numerical methods for mathematical functions neede for the program"""

import numpy as np
#This method finds the factorial of a given number(num).
#The method calls for an integer input num
#The method returns the factorial(represented as fact in the method)
def factorial(num):
    """Finds the factorial of the input integer.

    Args:
        num (int): The integer to find the factorial of.

    Returns:
        fact (int): The factorial of num.
    """
    #If the number provided is zero then the factorial is 1
    if num == 0:
        fact = 1
    #Otherwise set fact to 1 and begin finding the factorial r is
    #used to find each num-n for n=0 to n=num each value in r is
    #then used to compute the factorial
    else:
        fact = 1
        r = list(range(1,num+1))
        for i in r:
            fact *= i
    return fact

def binomial_coefficient(n, k):
    """Finds the binomial coefficient n choose k. See
    https://en.wikipedia.org/wiki/Binomial_coefficient for details.

    Args:
        n (int): An integer.
        k (int): An integer.

    Returns:
       t (int): The binomial coefficient, n choose k.

    """
    if not (-1 < k < n+1):
        return 0
    if k==0 and n == 0:
        return 1
    t = 1
    if k < n-k:
        for i in range(n, n-k, -1):
            t = t*i//(n-i+1)
    else:
        for i in range(n, k, -1):
            t = t*i//(n-i+1)

    return t

def multinomial(n):
    """Finds the multinomial coefficient for a given array of numbers.

    Args:
        n (list): the interegs to be used.
    """
    binomials = [[np.sum(n),n[0]]]
    for i in range(1,len(n)):
        new_sum = binomials[i-1][0]-binomials[i-1][1]
        binomials.append([new_sum,n[i]])

    bins = []
    for b in binomials:
        bins.append(binomial_coefficient(b[0],b[1]))

    return np.prod(bins)


# 1 DOF SYSTEM
import numpy as np
import matplotlib.pyplot as plt
import libraryTugas as lib

# 1. SYSTEMS PARAMETERS
#================
# a. Initial condition
x_init1, xDot_init1 =  0.1, 0   # [m], [m/s]

# b. System parameters
mass1, damp1, spring1 = 1, 1, 10 # [kg], [Ns/m], [N/m]
omega, forceMagnit = 2, 0.5 # [rad/s], [Newton]--> SYSTEM INPUT

# c. Time parameters
timeStart, timeStop, stepTime = 0, 30, 0.001 # [S]

# d. Define System MODEL!
def systemFunction (y, t):
    xDotDot = np.zeros((1,1), dtype = float)
    xDotDot[0] = (forceMagnit*np.sin(omega*t)-damp1*float(y[1])-spring1*float(y[0]))/mass1
    return xDotDot #*np.sin(omega*t)

# 2. INITIALIZE SIMULATION
# a. Simulation time
time = np.arange(timeStart, timeStop, stepTime, dtype = float)

# b. Initial Condition of THE state
y = np.array([[x_init1], [xDot_init1]], dtype = float)

# 3. SOLVING EQUATION OF MOTION USING RUNGE KUTTA 4th ORDER!!
position, velocity, acceleration = lib.rungeKutta4(y, time,
                                                systemFunction, stepTime)

# 4. PLOTING RESULTS!!
plt.figure(1)
plt.plot(time, position)
title = "position plot [step time = %1.6f s]" % stepTime
plt.title(title)
plt.ylabel('displacement [m]')
plt.xlabel('time [s]')
plt.grid(True)

plt.figure(2)
plt.plot(time, velocity)
title = "velocity plot [step time = %1.6f s]" % stepTime
plt.title(title)
plt.ylabel('velocity [m/s]')
plt.xlabel('time [s]')
plt.grid(True)

plt.figure(3)
plt.plot(time, acceleration)
title = "acceleration plot [step time = %1.6f s]" % stepTime
plt.title(title)
plt.ylabel('acceleration [m/s/s]')
plt.xlabel('time [s]')
plt.grid(True)
plt.show()


# routines for multiplying matrices
import numpy

def mult_Ax(A, x):
    """ return the product of matrix A and vector x: Ax = b """

    # x is a vector
    if not x.ndim == 1:
        print "ERROR: x should be a vector"
        return None

    N = len(x)

    # A is square, with each dimension of length N
    if not (A.shape[0] == N and A.shape[1] == N):
        print "ERROR: A should be square with each dim of same length as x"
        return None

    # allocation the product array
    b = numpy.zeros((N), dtype=A.dtype)

    # each row of b is the product of the like row of A dotted with
    # the vector x
    for i in range(N):
        b[i] = numpy.dot(A[i,:], x)

    print b

    return b


from typing import Callable
import numpy as np

def trapezoidalRule(f: Callable, N: float, start: float=-1e6,
                    stop: float=1e6) -> float:
    """Function to approximate the numeric integral of a function, f, using
    the Trapezoidal rule.

    Arguments:
        f {Callable} -- Function who's integral is to be estimated.
        N {int} -- Number of nodes to consider.

    Keyword Arguments:
        start {float} -- Starting point (default: {-1e6}).
        stop {float} -- Stopping point (default: {1e6}).

    Returns:
        float -- Approximation of the area under the function.
    """

    # Building values for approximation, and getting step size
    x, h = np.linspace(start=start, stop=stop, num=N, retstep=True)

    # Estimating area using trapezoidal rule, return
    return np.sum((h * f(x))) - ((h / 2) * (f(start) + f(stop)))


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

# Parameters
np.random.seed(421)
# generating data
# class_means = np.array([-3.0,-1.0,+3.0])
#
# class_deviations = np.array([1.2, 1.0, 1.3])
# #number of data points for each class.
# class_sizes = np.array([40, 30, 50])
#
# points1 = np.random.normal(class_means[0],class_deviations[0],class_sizes[0])
# points2 = np.random.normal(class_means[1],class_deviations[1],class_sizes[1])
# points3 = np.random.normal(class_means[2],class_deviations[2],class_sizes[2])
# points = np.concatenate((points1,points2,points3))
# # generating the labels.
# y = np.concatenate((np.repeat(1, class_sizes[0]), np.repeat(2, class_sizes[1]), np.repeat(3, class_sizes[2])))
#
# # print(points)
# # print(y)
#
# # Exporting data
# # First column is xi, second column is yi column.
# np.savetxt("lab01_data_set.csv", np.stack((points, y), axis=1), delimiter=",")
#
# # Plotting Data
# # Used for drawing purposes.
# data_interval = np.linspace(-7, +7, 1401)
# print(data_interval)
# # Probability density function for the normal distribution
# density1 = stats.norm.pdf(data_interval, loc= class_means[0], scale= class_deviations[0])
# density2 = stats.norm.pdf(data_interval, loc= class_means[1], scale= class_deviations[1])
# density3 = stats.norm.pdf(data_interval, loc= class_means[2], scale= class_deviations[2])
#
# plt.figure(figsize= (10,6))
# plt.plot(data_interval, density1, "r")
# plt.plot(data_interval, density2, "g")
# plt.plot(data_interval, density3, "b")
# plt.plot(points1, np.repeat(-0.01, class_sizes[0]),"r.", markersize= 12)
# plt.plot(points2, np.repeat(-0.02, class_sizes[1]),"g.", markersize= 12)
# plt.plot(points3, np.repeat(-0.03, class_sizes[2]),"b.", markersize= 12)
# plt.xlabel("x")
# plt.ylabel("density")
# plt.show()

#Starting the machine learning
# Importing data
data_set = np.genfromtxt("lab01_data_set.csv", delimiter= ",")
x = data_set[:,0]
y = data_set[:,1].astype(int)

# number of classes
K = np.max(y)
# number of data points
N = data_set.shape[0]

# Parameter Estimation
sample_means = [np.mean(x[y == (c + 1)]) for c in range(K)]
# y == c + 1 to get the x1s for example.
# x[y == ...] thing is where you slice x array into three pieces.
# with mean you get the mean of the data in x1 for example,
# or first 40 items in this case.

print(sample_means)
# parameters that we gave was, -3, -1, 3 so it is close but not perfect.

sample_deviations = [np.sqrt(np.mean((x[y == (c + 1)] - sample_means[c])**2)) for c in range(K)]
print(sample_deviations)
# Correct values were, 1.2, 1.0, 1.3
# Still close but not much.

class_priors = [np.mean(y == (c + 1)) for c in range(K)]
print(class_priors)
# 40 / 120, 30 /120 , 50 /120

# Parametric Classification
# We are giving 1401 data points to test the classification, between the intervals -7 to 7.

data_interval = np.linspace(-7, +7, 1401)

#gc(x) = log(x | y = c) + log P(y=c) = - 1/2 * log(2pi sigmac^2 )
# - (x - mu)^2 / 2 sigmac^2 + log P(y=c)
score_values = np.stack([-0.5 * np.log(2 * math.pi * sample_deviations[c]**2)
                         - 0.5 * (data_interval - sample_means[c])**2 / sample_deviations[c]**2
                         + np.log(class_priors[c])
                         for c in range(K)])
print("score_values:")
print(score_values)

# Score Functions
plt.figure(figsize=(10,6))
plt.plot(data_interval, score_values[0,:], "r")
plt.plot(data_interval, score_values[1,:], "g")
plt.plot(data_interval, score_values[2,:], "b")
plt.xlabel("x")
plt.ylabel("score")
plt.show()
# Based on the graph, you should pick what is on the top.
# So, from -7 to -2 you should pick red since it is on top.
# From -2 to 0.5, green, rest is blue.

# Posterior Probabilities
# calculate log posteriors:
log_posteriors = score_values - [np.max(score_values[:, r]) +
                                 np.log(np.sum(np.exp(score_values[:, r] - np.max(score_values[:, r]))))
                                 for r in range(score_values.shape[1])
                                 ]
print("log_posteriors")
print(log_posteriors)

# Posteriors

plt.figure(figsize = (10, 6))
# plot posterior probability of the first class
plt.plot(data_interval, np.exp(log_posteriors[0,:]), "r")
# plot posterior probability of the second class
plt.plot(data_interval, np.exp(log_posteriors[1,:]), "g")
# plot posterior probability of the third class
plt.plot(data_interval, np.exp(log_posteriors[2,:]), "b")

class_assignments = np.argmax(score_values, axis = 0)

#plot region where the first class has the highest probability
plt.plot(data_interval[class_assignments == 0],
         np.repeat(-0.05, np.sum(class_assignments == 0)), "r.", markersize = 10)
#plot region where the second class has the highest probability
plt.plot(data_interval[class_assignments == 1],
         np.repeat(-0.10, np.sum(class_assignments == 1)), "g.", markersize = 10)
#plot region where the third class has the highest probability
plt.plot(data_interval[class_assignments == 2],
         np.repeat(-0.15, np.sum(class_assignments == 2)), "b.", markersize = 10)

plt.xlabel("x")
plt.ylabel("probability")

plt.show()


import numpy as np
from scipy.special import gamma

from prml.rv.rv import RandomVariable

class Dirichlet(RandomVariable):
    """
    Dirichlet distribution
    p(mu|alpha)
    = gamma(sum(alpha))
      * prod_k mu_k ^ (alpha_k - 1)
      / gamma(alpha_1) / ... / gamma(alpha_K)
    """

    def __init__(self, alpha):
        """
        construct dirichlet distribution

        Parameters
        ----------
        alpha : (size,) np.ndarray
            pseudo count of each outcome, aka concentration parameter
        """
        super().__init__()
        self.alpha = alpha

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

    @alpha.setter
    def alpha(self, alpha):
        assert isinstance(alpha, np.ndarray)
        assert alpha.ndim == 1
        assert (alpha >= 0).all()
        self.parameter["alpha"] = alpha

    @property
    def ndim(self):
        return self.alpha.ndim

    @property
    def size(self):
        return self.alpha.size

    @property
    def shape(self):
        return self.alpha.shape

    def _pdf(self, mu):
        return (
            gamma(self.alpha.sum())
            * np.prod(mu ** (self.alpha - 1), axis=-1)
            / np.prod(gamma(self.alpha), axis=-1)
        )

    def _draw(self, sample_size=1):
        return np.random.dirichlet(self.alpha, sample_size)


import numpy as np
from scipy.spatial.transform import Rotation as R
import logging
from scipy.optimize import least_squares

"""
    Find the camera pose given 3D to 2D correspondences using a least-squares formulation.
"""

def pose(R: np.array, t: np.array):
    T = np.eye(4, 4)
    T[:3, 3] = t
    T[:3, :3] = R
    return T

def euler_zyx(alpha, gamma, beta):
    return R.from_euler('z', alpha, degrees=True) * R.from_euler('y', beta, degrees=True) * R.from_euler('x', gamma, degrees=True)

def residuals(x, c_q__h, w_X__h):
    c_R_w = euler_zyx(*x[:3]).as_matrix()
    c_t_w = x[3:]
    T = pose(c_R_w, c_t_w)
    p_c = np.eye(3, 4) @ T @ w_X__h
    p = p_c[:2, ...] / p_c[2, :]
    return (c_q__h - p).reshape(-1)

if __name__ == '__main__':

    logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)

    zyx_angles = [45, 20,50]
    c_R_w = euler_zyx(*zyx_angles).as_matrix()
    c_t_w = np.r_[0, 1, 2]
    logging.info("Created camera translation...")

    # generate random 4D configurations
    w_X = np.random.rand(3, 20)
    w_X__h = np.vstack((w_X, np.ones((1, 20))))
    logging.info("Created random configurations...")

    # reproject the 4D configurations into 2D coords
    c_T_w = pose(c_R_w, c_t_w)
    logging.info("Created pose...")

    # reproject the image coordinates
    c_q__h = np.eye(3, 4) @ c_T_w @ w_X__h # (3, 4) @ (4, 4) @ (4, 20)
    logging.info("Reprojected the 3D points...")

    # homogenize the coordinates
    c_q__h = c_q__h[:2, ...] / c_q__h[2, ...] #(2, 20)
    logging.info("Homogenize the image-pts...")

    # change the zyx_angles (disturabance)
    zyx_angles += np.random.rand(3,) * 10 # (3, )
    c_t_w = np.r_[0, 1, 2] # (3, )
    x = np.hstack((c_t_w, zyx_angles)) # (6, )
    logging.info("Stack the pose vector")

    # make a least_squares object in scipy
    lms = least_squares(fun=residuals, x0=x, args=(c_q__h, w_X__h))
    res = residuals(lms.x, c_q__h, w_X__h)

    print("The resulting pose: ", np.array2string(lms.x, precision=2, suppress_small=True))
    print("The residual-squared: ", np.array2string(np.dot(res, res), precision=16, suppress_small=True))

    # https://www.coursera.org/lecture/robotics-perception/bundle-adjustment-i-oDj0o


import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import fft, ifft, fftfreq

def diff(x, f, n):
    k = fftfreq(x.size, x[1] - x[0]) * 2 * np.pi
    return np.real(ifft((1j * k)**n * fft(f)))

def solve_heat_eq_pseudo_spectral(x, psi0, Delta_T, D):
    """
    D_t psi = D nabla^2 psi
    """
    k = fftfreq(x.size, x[1] - x[0]) * 2 * np.pi
    return np.real(ifft(np.exp(- D * Delta_T * k**2) * fft(psi0)))
    # D_t psi = D nabla^2 psi + V * psi
    # V = calc_pot(x)
    # return np.real(np.exp(- V / 2 * Delta_T) * ifft(np.exp(- D * Delta_T * k**2) * fft(np.exp(- V / 2 * Delta_T) * psi0)))

if __name__ == "__main__":
    x = np.linspace(0, 2*np.pi, 400)[:-1]
    f = np.sin(x)
    df = diff(x, f, 1)
    ddf = diff(x, f, 2)
    plt.subplot(2, 1, 1)
    plt.plot(x, f, label="f(x) = sin(x)")
    plt.plot(x, df, label="f'")
    plt.plot(x, ddf, label="f''")
    plt.ylim((-1.5, 1.5))
    plt.xlabel("x")
    plt.legend()

    psi0 = np.exp(- (x - np.pi)**2)
    Ts = [0, 1, 2, 3, 4]
    D = 0.5
    plt.subplot(2, 1, 2)
    for T in Ts:
        plt.plot(x, solve_heat_eq_pseudo_spectral(x, psi0, T, D), label="T = %.2f" % T)
    plt.legend()
    plt.xlabel("x")
    plt.ylabel(r"$\psi$")
    plt.title("Diffusion eq. using Spectral Method, D = %.2f" % D)
    plt.show()


# basic_stats.py: Basic stats functions (not available in Numpy alone) that are repeatedly used in the other fancier
# modules in this package.

import numpy as np

# =========================
# Basic statistics functions
# =========================
# Function for fisher transformation; used for one correlation coefficent averaging method
def fisher_transform(x, output_type='z', method='short'):
    """
    Calculates regular Fisher z-transformation of Pearson correlation
    coefficients (aka r values) into z-scores, or inverse fisher
    z-transformation of z-scores into Pearson correlation coefficients.

    Parameters
    ----------
    x : array-like
        The data array to be transformed.

    output_type : str
        The output statistic that is desired. 'z' implies regular
        Fisher z-transformation, while 'r' implies the inverse.

        (Default is 'z')

    method : str
        (Primarily exists for function test purposes)
        Chooses the method of calculation. 'short' uses numpy.arctanh and
        numpy.tanh to produce regular and inverse transformations,
        respectively. 'Long' instead calculates it step by step, but still
        uses numpy.log and numpy.exp for regular and inverse transforms.

        (Default is 'short')

    Returns
    -------
    output : array-like
        Will have the same shape/dimensions as x

    Warning:
    Using Fisher-transformation in within-between ISC (for calculating)
    average z then inverse transforming back to r) currently produces
    sporadic missing data, so the averaging method should use the
    median instead until this the effect of this function on results
    is better tested and validated.

    """
    # Currently, both output types assume the correct input (i.e., r or z) is provided
    # Transform Pearson r correlation coefficients into z by default or r when defined
    def arctanh_tanh(x, output): # fisher z transformation calculated with arctanh and tanh
        if output == 'z':
            z = np.arctanh(x)
            return z
        elif output == 'r':
            r = np.tanh(x)
            return r

    def long_fisher(x, output): # fisher z transformation calculated the long way
        if output == 'z':
            z = np.log((1+x)/(1-x)) / 2
            return z
        # Transform z scores back into r-values (presumably after z is averaged)
        elif output == 'r':
            r = (np.exp(2*x)-1) / (np.exp(2*x)+1)
            return r

    if method == 'short':
        transformed_output = arctanh_tanh(x, output_type)
    elif method == 'long':
        transformed_output = long_fisher(x, output_type)

    return transformed_output

# Retrieves upper triangle of a matrix
# TODO: I should make this upper or lower option
# NOTE: This function is not currently incorporated in any other function--should it be?
def unique_tri(array):
    """
    Retrieves the upper-triangle of a square m x m matrix; used in the
    context of getting unique x and y pairs. This currently excludes
    diagonal values since numpy.triu() already includes it.

    Parameters
    ----------
    array : array-like
        The matrix that the triangle is obtained from

    Returns
    -------
    output : the triangle (excluding the diagonal values)

    """

    m = array.shape[0]
    r, c = np.triu_indices(m,1)
    return array[r,c]

# Computes pairwise voxel correlations between two matrices
def pairwise_r(x_input, y_input):
    # Compute Pearsons' R for every x,y combination
    # Note:
    # - allows for (i,j) and (j,i)
    # - symmetry depends on whether x == y
    # - final matrix shape can be non-square

    # Save data and their number of rows
    r_matrix = np.array([])

    x = x_input
    y = y_input
    x_len, y_len = len(x), len(y) # default lengths of inputs

    # Reshape 1d data to matrix with 1 row
    if x.ndim == 1:
        x = x.reshape((1, x_len))
        x_len = 1

    if y.ndim == 1:
        y = y.reshape((1, y_len))
        y_len = 1

    # Correlate every combination of (x,y) pairs of rows
    for i in range(x_len):
        for j in range(y_len):
            r_i = np.corrcoef(x[i], y[j])[0,1]
            r_matrix = np.append(r_matrix, r_i)

    # Reshape into matrix and tranpose to correct x and y axes positions
    r_matrix = r_matrix.reshape((x_len, y_len)).T
    return r_matrix

# Function to easily choose the averaging method for a matrix of correlation coefficients
def r_mean(x, method='median', axis=None, fisher_method='short'):
    """
    Method can be
    - median (default)
    - fisher (fisher z -> mean -> fisher z inverse)
    - mean (not recommended, but included for comparison purposes)
    """
    if method=='median':
        r_mean = np.median(x, axis=axis)

    elif method=='fisher':
        if fisher_method=='short': # uses arctanh / tanh (inverse transform)
            r_mean = np.mean(fisher_transform(x), axis=axis) # get mean z values
            r_mean = fisher_transform(r_mean, output_type='r') # turn mean z-values back into r

        elif fisher_method=='long': # calculates transformation the long way
            r_mean = np.mean(fisher_transform(x, method='long'), axis=axis)
            r_mean = fisher_transform(r_mean, output_type='r', method='long')

    elif method=='mean':
        r_mean = np.mean(x, axis=axis)

    return r_mean


import numpy as np
import numpy.linalg as la

class Kernel(object):
    """Implements list of kernels from
    http://en.wikipedia.org/wiki/Support_vector_machine
    """
    @staticmethod
    def linear():
        return lambda x, y: np.inner(x, y)

    @staticmethod
    def gaussian(sigma):
        return lambda x, y: \
            np.exp(-np.sqrt(la.norm(x-y) ** 2 / (2 * sigma ** 2)))

    @staticmethod
    def _polykernel(dimension, offset):
        return lambda x, y: (offset + np.inner(x, y)) ** dimension

    @classmethod
    def inhomogenous_polynomial(cls, dimension):
        return cls._polykernel(dimension=dimension, offset=1.0)

    @classmethod
    def homogenous_polynomial(cls, dimension):
        return cls._polykernel(dimension=dimension, offset=0.0)

    @staticmethod
    def hyperbolic_tangent(kappa, c):
        return lambda x, y: np.tanh(kappa * np.dot(x, y) + c)


import scipy.fftpack as fft
import matplotlib.pyplot as plt
import numpy
import imageio
import math
from pylab import *
from numpy.fft import fft2, ifft2, fftshift, ifftshift

#Reference: https://lab4sys.com/en/fourier-transform-of-an-image/?cn-reloaded=1

#N =256
#t = np.arange(N)

#m=4
#nu = float(m)/N
#f= np.sin(2*np.pi*nu*t)
#ft=np.fft.fft(f)
#freq=np.fft.fftfreq(N)
#plt.plot(freq,ft.real**2+ft.imag**2)
#plt.show()

def matriceImage(matrice,gamma,rgb):
    s= matrice.shape
    a=1.0/gamma;
    norm=matrice.max()
    m=numpy.power(matrice/norm,a) #normalization, gamma correction
    im=numpy.zeros((s[0],s[1],3),dtype=float64) #declare image matrix
    im[:,:,0] = rgb[0]*m
    im[:,:,1] = rgb[1]*m
    im[:,:,2] = rgb[2]*m
    return im

def matriceImageLog(matrice,rgb):
    s= matrice.shape
    m = numpy.log10(1+matrice)
    min = m.min()
    max = m.max()
    m = (m-min)/(max-min)
    im=numpy.zeros((s[0],s[1],3),dtype=float64) #declare image matrix
    im[:,:,0] = rgb[0]*m
    im[:,:,1] = rgb[1]*m
    im[:,:,2] = rgb[2]*m
    return im

def plotSpectre(image,Lx,Ly):
    (Ny,Nx,p) = image.shape
    fxm = Nx*1.0/(2*Lx)
    fym = Ny*1.0/(2*Ly)
    imshow(image,extent=[-fxm,fxm,-fym,fym])
    xlabel("fx")
    ylabel("fy")

def low_pass_transfert(fx,fy,p): #low pass filtering
    fc=p[0]
    return 1.0/numpy.power(1+(fx*fx+fy*fy)/(fc*fc),2)

def high_pass_transfert(fx,fy,p): #high pass filtering
    fc=p[0]
    return 1.0-1.0/numpy.power(1+(fx*fx+fy*fy)/(fc*fc),2)

def Gaussian_notch_transfert(fx,fy,p): #Gaussian notch filter
    f0 = p[0]
    sigma = p[1]
    ux = numpy.absolute(fx)-f0
    return 1.0-numpy.exp(-(ux*ux)/(sigma*sigma))

def matriceFiltre(matrice,transfert,p):
    s = matrice.shape
    Nx = s[1]
    Ny = s[0]
    nx = Nx/2
    ny = Ny/2
    Mat = zeros((Ny,Nx),dtype=numpy.complex)
    for n in range(Nx):
        for l in range(Ny):
            fx = float(n-nx-1)/Nx
            fy = float(l-ny-1)/Ny
            Mat[l,n] = matrice[l,n]*transfert(fx,fy,p)
    return Mat

print('Hi')
imA = imageio.imread("imageA.png")
U=numpy.array(imA[:,:,0],dtype=numpy.float64) #remove red layer
V=fft2(U)
VC = fftshift(V)
P = numpy.power(numpy.absolute(VC),2)
img = matriceImage(P,2.0,[1.0,0.0,0.0])
#plotSpectre(img,200.0,100.0)
img = matriceImageLog(P,[1.0,0.0,0.0])
#plotSpectre(img,200.0,100.0)
H = numpy.ones(VC.shape,dtype=numpy.complex)
H = matriceFiltre(H,low_pass_transfert,[0.1])
VCF = VC*H
PF = numpy.power(numpy.absolute(VCF),2)
imPF = matriceImageLog(PF,[1.0,0,0])
plotSpectre(imPF,200,100)
VF = ifftshift(VCF)
UF=ifft2(VF)
imageF = matriceImage(UF,1.0,[1.0,1.0,1.0])
figure()
imshow(imageF)


import numpy as np

def normalize_vec(vec):
    return vec / np.linalg.norm(vec)

def normalize_a_to_b(a, b):
    vec = b - a
    return vec / np.linalg.norm(vec)

def calc_angle_360(v1, v2, n):

    x = np.cross(v1, v2)
    c = np.sign(np.dot(x, n)) * np.linalg.norm(x)
    a = np.arctan2(c, np.dot(v1, v2))

    return np.degrees(a)


from math import cos, radians, sin
from typing import Optional, Tuple

import numpy as np

from squad.constants import AngleType

def _get_sin_cos_theta(theta: float) -> Tuple[float, float]:
    """Gets the :obj:`sin` and :obj:`cos` of the given `theta` (in
    radians).
    """
    return sin(theta), cos(theta)

def _x_rotation_mat(theta: float) -> np.ndarray:
    """Gets the X-axis rotation matrix for the given `theta` (in
    radians).
    """
    t_sin, t_cos = _get_sin_cos_theta(theta)

    r_x = np.identity(3)
    r_x[1, 1] = t_cos
    r_x[1, 2] = -t_sin
    r_x[2, 1] = t_sin
    r_x[2, 2] = t_cos

    return r_x

def _y_rotation_mat(theta: float) -> np.ndarray:
    """Gets the Y-axis rotation matrix for the given `theta` (in
    radians).
    """
    t_sin, t_cos = _get_sin_cos_theta(theta)

    r_y = np.identity(3)
    r_y[0, 0] = t_cos
    r_y[0, 2] = t_sin
    r_y[2, 0] = -t_sin
    r_y[2, 2] = t_cos

    return r_y

def _z_rotation_mat(theta: float) -> np.ndarray:
    """Gets the Z-axis rotation matrix for the given `theta` (in
    radians).
    """
    t_sin, t_cos = _get_sin_cos_theta(theta)

    r_z = np.identity(3)
    r_z[0, 0] = t_cos
    r_z[0, 1] = -t_sin
    r_z[1, 0] = t_sin
    r_z[1, 1] = t_cos

    return r_z

def rotation_matrix(x: float, y: float, z: float) -> np.ndarray:
    """Gets the rotation transformation matrix for the given angles (in
    radians).
    """
    if x:
        r_x = _x_rotation_mat(x)
    else:
        r_x = np.identity(3)

    if y:
        r_y = _y_rotation_mat(y)
    else:
        r_y = np.identity(3)

    if z:
        r_z = _z_rotation_mat(z)
    else:
        r_z = np.identity(3)

    r_xyz = r_z @ r_y @ r_x
    return r_xyz

def coord_rotate_xyz(
    x: float,
    y: float,
    z: float,
    x_rotation: Optional[float] = None,
    y_rotation: Optional[float] = None,
    z_rotation: Optional[float] = None,
    *,
    angle_type: AngleType = AngleType.DEGREES,
) -> Tuple[float, float, float]:
    """Apply a rotation transformation for the given position.

    Parameters
    ----------
    x : float
        The X-coordinate to apply the rotation(s) to.
    y : float
        The Y-coordinate to apply the rotation(s) to.
    z : float
        The Z-coordinate to apply the rotation(s) to.
    x_rotation : float, optional
        The rotation about the X-axis to apply (in radians, if any).
    y_rotation : float, optional
        The rotation about the Y-axis to apply (in radians, if any).
    z_rotation : float, optional
        The rotation about the Z-axis to apply (in radians, if any).
    angle_type : AngleType, default=AngleType.DEGREES
        The format of the given rotation angle inputs
        (:obj:`AngleType.DEGREES`, default) or
        (:obj:`AngleType.RADIANS`).

    Returns
    -------
    Tuple[float, float, float]
        The new coordinates, as a 3-element :obj:`tuple` of (x, y, z).

    """
    v0 = np.array((x, y, z))
    v1 = coord_rotate(
        v0,
        x_rotation=x_rotation,
        y_rotation=y_rotation,
        z_rotation=z_rotation,
        angle_type=angle_type,
    )
    return tuple(x for x in v1)

def coord_rotate(
    v: np.ndarray,
    x_rotation: Optional[float] = None,
    y_rotation: Optional[float] = None,
    z_rotation: Optional[float] = None,
    *,
    angle_type: AngleType = AngleType.DEGREES,
) -> np.ndarray:
    """Apply a rotation transformation for the given vector.

    Parameters
    ----------
    v : np.ndarray
        A 3D vector to rotate based on the given rotation angles.
    x_rotation : float, optional
        The rotation about the X-axis to apply (in radians, if any).
    y_rotation : float, optional
        The rotation about the Y-axis to apply (in radians, if any).
    z_rotation : float, optional
        The rotation about the Z-axis to apply (in radians, if any).
    angle_type : AngleType, default=AngleType.DEGREES
        The format of the given rotation angle inputs
        (:obj:`AngleType.DEGREES`, default) or
        (:obj:`AngleType.RADIANS`).

    Returns
    -------
    np.ndarray
        The new coordinates, as a 3-element vector, of (x, y, z).

    """
    if angle_type == AngleType.DEGREES:
        r_xyz = rotation_matrix(
            radians(x_rotation or 0.0),
            radians(y_rotation or 0.0),
            radians(z_rotation or 0.0),
        )
    else:
        r_xyz = rotation_matrix(
            x_rotation or 0.0,
            y_rotation or 0.0,
            z_rotation or 0.0,
        )
    return r_xyz @ v


import numpy as np
import math

def rosenbrock(x,alpha=100):
    '''
        Rosenbrock function

        Single objective multiple input function
    '''
    f = 0
    for indx in range(len(x)-1):
        temp = alpha* math.pow(x[indx+1]-x[indx]*x[indx],2) + math.pow(1.0-x[indx],2)
        f+=temp
    return f

def KUR(x1,x2,x3):
    '''
        Kursawe Function
        mutiple output
    '''
    f1 = (-10*math.exp(-0.2*math.sqrt(x1*x1+x2*x2))) + (-10*math.exp(-0.2*math.sqrt(x2*x2+x3*x3)))

    f2 = (abs(x1)^0.8+5*math.sin(x1*x1*x1))+(math.pow(abs(x2),0.8)+5*math.sin(x2*x2*x2))+(math.pow(abs(x3),0.8)+5*math.sin(x3*x3*x3))

    return f1,f2


import numpy as np
import matplotlib.pyplot as plt

def rk2heun(x0, v0, tf, dt, a0, w0, w):
    fv = lambda x, t: -(w0**2)*np.sin(x) + a0*np.sin(w*t)
    pos = [x0]
    vel = [v0]
    for t in np.arange(0, tf, dt):
        k1x = v0
        k1v = fv(x0, t)
        xaux = x0 + k1x * dt
        vaux = v0 + k1v * dt
        k2x = vaux
        k2v = fv(xaux, t+dt)
        x0 += (k1x + k2x)*(dt/2)
        v0 += (k1v + k2v)*(dt/2)
        pos.append(x0)
        vel.append(v0)
    return pos, vel

x0 = 0.1
v0 = 0
tf = 25
dt = 0.1
a0 = 0.1
w0 = 1
w = 0.1

la = [0.1, 0.1, 1]
lw = [0.1, 0.5, 0.5]

t = np.arange(0, tf+dt, dt)

figura = plt.figure(figsize=(13, 5))
plt.subplot(121)
plt.plot(t, rk2heun(x0, v0, tf, dt, a0, w0, w)[0], label=r'$A_{0}=$'+f'{a0}'+r' | $\omega=$'+f'{w}')
#for a, w1 in zip(la, lw):
#    plt.plot(t, rk2heun(x0, v0, tf, dt, a, w0, w1)[0], label=r'$A_{0}=$'+f'{a}'+r' | $\omega=$'+f'{w1}')
plt.xlabel('t')
plt.ylabel('x')
plt.legend()
plt.title(r'Posição $\times$ Tempo')
plt.grid()

plt.subplot(122)
plt.plot(rk2heun(x0, v0, tf, dt, a0, w0, w)[0], rk2heun(x0, v0, tf, dt, a0, w0, w)[1], label=r'$A_{0}=$'+f'{a0}'+r' | $\omega=$'+f'{w}')
#for a, w1 in zip(la, lw):
#    plt.plot(rk2heun(x0, v0, tf, dt, a, w0, w1)[0], rk2heun(x0, v0, tf, dt, a, w0, w1)[1], label=r'$A_{0}=$'+f'{a}'+r' | $\omega=$'+f'{w1}')
plt.xlabel('x')
plt.ylabel('v')
plt.legend()
plt.title(r'Posição $\times$ Velocidade')
plt.grid()

plt.savefig('rk2heunpendulo1.png')


# This program and its computation instruction was written by Andre Christoga Pramaditya (drepram.com) with the student ID (2006570006).
# Program dan instruksi komputasi ini ditulis oleh Andre Christoga Pramaditya (drepram.com) dengan Nomor Pokok Mahasiswa (2006570006).

import numpy as np
import matplotlib.pyplot as plt

import warnings
warnings.filterwarnings("ignore") # insert programmer laziness joke here

# number of trials, probability of each trial
number_of_trials = 100
probability_5 = 0.5
probability_1 = 0.1
probability_9 = 0.9

sample_5 = np.random.binomial(number_of_trials, probability_5, 10000)
sample_1 = np.random.binomial(number_of_trials, probability_1, 10000)
sample_9 = np.random.binomial(number_of_trials, probability_9, 10000)

# disclaimer_text = "Orange bars (n=0.1), Blue bars (n=0.5), Green bars (n=0.9)."

combined_histogram = plt.figure(1, figsize=(7,7))

plt.hist(sample_5, 15, density=True)
plt.hist(sample_1, 15, density=True)
plt.hist(sample_9, 15, density=True)

combined_histogram.suptitle('Binomial Distribution Combined Histogram', fontsize=14, fontweight='bold')
combined_axes_title = combined_histogram.add_subplot(111)
combined_histogram.subplots_adjust(top=0.85)
combined_axes_title.set_title('Orange bars (n=0.1), Blue bars (n=0.5), Green bars (n=0.9)')
combined_histogram.show()

histogram_each, (case1, case2, case3) = plt.subplots(1,3, figsize=(15,9))
histogram_each.suptitle('Binomial Distribution for Each Case \n(with more specific range for the x axis)', fontweight='bold')
case1.hist(sample_1, 15, density=True, color="orange")
case1.set_title('retrieved 10.000 random number\nfrom n=100,p=0.1')
case2.hist(sample_5, 15, density=True, color="blue")
case2.set_title('retrieved 10.000 random number\nfrom n=100,p=0.5')
case3.hist(sample_9, 15, density=True, color="green")
case3.set_title('retrieved 10.000 random number\nfrom n=100,p=0.9')

# Standardization of the y axis if of any need, it is unnecessary for now
# case1.set_ylim([0, 0.2])
# case2.set_ylim([0, 0.2])
# case3.set_ylim([0, 0.2])

histogram_each.show()

### OLD METHOD OF SHOWING EACH FIGURES
# histogram_5 = plt.figure(2)
# plt.hist(sample_5, 15, density=True, color="blue")
# histogram_5.show()

# histogram_1 = plt.figure(3)
# plt.hist(sample_1, 15, density=True, color="orange")
# histogram_1.show()

# histogram_9 = plt.figure(4)
# plt.hist(sample_9, 15, density=True, color="green")
# histogram_9.show()

plt.show()


"""
Q1: Resolver as questões do lab 10:
"""
from Metodos_numericos.metodos import EDO
from math import exp
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from pprint import pprint

# Declarando constantes:
R = 33000
C = 90*10**(-6)
ti = 0
vi = 1
vo_linha = lambda t, v: -(1/(R*C))*v

#Resultado analitico:
vo = lambda t: exp(-t/(R*C))
x = np.arange(0, 15.01, 0.01)
v_analitico = np.array([vo(i) for i in x])

#Pelo metodo de Euler:
edo = EDO(vo_linha, ti, vi, 0.5)
euler = np.array([])
for i in x:
    edo.Euler(i)
    euler = np.append(euler, edo.y_novo)

#Pelo metodo de Heun:
edo = EDO(vo_linha, ti, vi)
heun = np.array([])
for i in x:
    edo.Heun(i)
    heun = np.append(heun, edo.y_novo)

#Pelo metodo de Runge Kutta 4° ordem:
edo = EDO(vo_linha, ti, vi)
rk = np.array([])
for i in x:
    edo.Runge_Kutta_4(i)
    rk = np.append(rk, edo.y_novo)

#plotando graficos comparativos:
plt.grid()
plt.tight_layout()
plt.style.use('ggplot')
plt.plot(x, v_analitico, 'r-', label='Valor analitico')
plt.plot(x, euler, 'y-', label='Metodo de Euler')
plt.title('Analise de EDO pelo metodo de Euler')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc='best')
plt.show()

plt.plot(x, v_analitico, 'r-', label='Valor analitico')
plt.plot(x, heun, 'g-', label='Metodo de Heun')
plt.title('Analise de EDOs pelo metodo de Heun')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc='best')
plt.show()

plt.plot(x, v_analitico, 'r-', label='Valor analitico')
plt.plot(x, rk, 'b-', label='Metodo de Runge Kutta de 4º ordem')
plt.title('Analise de EDOs pelo metodo de Runge Kutta')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc='best')
plt.show()

#Calculo dos erros:
erro_euler = max(abs(euler - v_analitico))
erro_heun = max(abs(heun - v_analitico))
erro_runge = max(abs(rk - v_analitico))

erros = {
    "Euler": round(erro_euler, 5),
    "Heun": round(erro_heun, 5),
    "Runge Kutta": round(erro_runge, 5)
}
db = pd.DataFrame(erros, index=['Erro absoluto'], columns=['Euler', 'Heun', 'Runge Kutta'])
pprint(db)