"""
Author: Dat Quoc Ngo
Date: 02/05/2021
NET-ID: dqn170000
"""

# generating synthetic data

# The true function
def f_true(x):
  y = 6.0 * (np.sin(x + 2) + np.sin(2*x + 4))
  return y

import numpy as np                       # For all our math needs
n = 750                                  # Number of data points
X = np.random.uniform(-7.5, 7.5, n)      # Training examples, in one dimension
e = np.random.normal(0.0, 5.0, n)        # Random Gaussian noise
y = f_true(X) + e                        # True labels with noise

import matplotlib.pyplot as plt          # For all our plotting needs
plt.figure()

"""*************************"""

# Plot the data
plt.scatter(X, y, 12, marker='o')

# Plot the true function, which is really "unknown"
x_true = np.arange(-7.5, 7.5, 0.05)
y_true = f_true(x_true)
plt.plot(x_true, y_true, marker='None', color='r')

# scikit-learn has many tools and utilities for model selection
from sklearn.model_selection import train_test_split
tst_frac = 0.3  # Fraction of examples to sample for the test set
val_frac = 0.1  # Fraction of examples to sample for the validation set

# First, we use train_test_split to partition (X, y) into training and test sets
X_trn, X_tst, y_trn, y_tst = train_test_split(X, y, test_size=tst_frac, random_state=42)

# Next, we use train_test_split to further partition (X_trn, y_trn) into training and validation sets
X_trn, X_val, y_trn, y_val = train_test_split(X_trn, y_trn, test_size=val_frac, random_state=42)

# Plot the three subsets
plt.figure()
plt.scatter(X_trn, y_trn, 12, marker='o', color='orange')
plt.scatter(X_val, y_val, 12, marker='o', color='green')
plt.scatter(X_tst, y_tst, 12, marker='o', color='blue')

"""*************************"""

# Polynomial Basis Functions

# X float(n, ): univariate data
# d int: degree of polynomial
def polynomial_transform(X, d):
    # convert data to np.array
    X = np.array(X)

    # power univariate data to d-th and transpose
    return np.transpose(np.array([np.power(X, i) for i in range(d + 1)]))

# Phi float(n, d): transformed data
# y   float(n,  ): labels
def train_model(Phi, y):
    Phi_t = np.transpose(Phi) # transpose phi
    # comptue weight
    return np.dot(np.dot(np.linalg.inv(np.dot(Phi_t, Phi)),Phi_t), y)

# Phi float(n, d): transformed data
# y   float(n,  ): labels
# w   float(d,  ): linear regression model
def evaluate_model(Phi, y, w):
    # wT@phi -> converted to phi@w
    # this is due to the piece-wise subtraction
    return np.sum(np.power((y - np.dot(Phi, w)), 2)) / len(y)

w = {}               # Dictionary to store all the trained models
validationErr = {}   # Validation error of the models
testErr = {}         # Test error of all the models

for d in range(3, 25, 3):  # Iterate over polynomial degree
    Phi_trn = polynomial_transform(X_trn, d)                 # Transform training data into d dimensions
    w[d] = train_model(Phi_trn, y_trn)                       # Learn model on training data
    Phi_val = polynomial_transform(X_val, d)                 # Transform validation data into d dimensions
    validationErr[d] = evaluate_model(Phi_val, y_val, w[d])  # Evaluate model on validation data

    Phi_tst = polynomial_transform(X_tst, d)           # Transform test data into d dimensions
    testErr[d] = evaluate_model(Phi_tst, y_tst, w[d])  # Evaluate model on test data

# Plot all the models
plt.figure()
plt.plot(validationErr.keys(), validationErr.values(), marker='o', linewidth=3, markersize=12)
plt.plot(testErr.keys(), testErr.values(), marker='s', linewidth=3, markersize=12)
plt.xlabel('Polynomial degree', fontsize=16)
plt.ylabel('Validation/Test error', fontsize=16)
plt.xticks(list(validationErr.keys()), fontsize=12)
plt.legend(['Validation Error', 'Test Error'], fontsize=16)
plt.axis([2, 25, 15, 60])

plt.figure()
plt.plot(x_true, y_true, marker='None', linewidth=5, color='k')

for d in range(9, 25, 3):
  X_d = polynomial_transform(x_true, d)
  y_d = X_d @ w[d]
  plt.plot(x_true, y_d, marker='None', linewidth=2)

plt.legend(['true'] + list(range(9, 25, 3)))
plt.axis([-8, 8, -15, 15])

"""*******************************"""

# Radial Basis Functions
# X float(n, ): univariate data
# B float(n, ): basis functions
# gamma float : standard deviation / scaling of radial basis kernel
def radial_basis_transform(X, B, gamma=0.1):
    B = np.array(B)
    # compute radial basis
    return np.exp(-gamma * np.square(np.array([B - x for x in X])))

# Phi float(n, d): transformed data
# y   float(n,  ): labels
# lam float      : regularization parameter
def train_ridge_model(Phi, y, lam):
    # transpose phi
    Phi_t = np.transpose(Phi)
    return np.dot(np.dot(
        np.linalg.inv(np.dot(Phi_t, Phi) + np.dot(lam, np.identity(Phi.shape[-1]))), Phi_t), y)

w = {}               # Dictionary to store all the trained models
validationErr = {}   # Validation error of the models
testErr = {}         # Test error of all the models

Phi_trn = radial_basis_transform(X_trn, X_trn, gamma=0.1)
Phi_val = radial_basis_transform(X_val, X_trn, gamma=0.1)
Phi_tst = radial_basis_transform(X_tst, X_trn, gamma=0.1)
for lam in range(-3, 4):  # lambdas
    w[lam] = train_ridge_model(Phi_trn, y_trn, 10 ** lam)
    validationErr[lam] = evaluate_model(Phi_val, y_val, w[lam])  # Evaluate model on validation data

    testErr[lam] = evaluate_model(Phi_tst, y_tst, w[lam])  # Evaluate model on test data

# Plot lambda vs validation error
plt.figure()
plt.plot(validationErr.keys(), validationErr.values(), marker='o', linewidth=3, markersize=12)
plt.plot(testErr.keys(), testErr.values(), marker='s', linewidth=3, markersize=12)
plt.xlabel('Lambda values', fontsize=16)
plt.ylabel('Validation/Test error', fontsize=16)
plt.xticks(list(validationErr.keys()), fontsize=12)
plt.legend(['Validation Error', 'Test Error'], fontsize=16)

plt.figure()
plt.plot(x_true, y_true, marker='None', linewidth=5, color='k')

for lam in range(-3, 4):
    X_d = radial_basis_transform(x_true, X_trn, gamma = 0.1)
    y_d = X_d @ w[lam]
    plt.plot(x_true, y_d, marker='None', linewidth=2)

plt.legend(['true'] + list(range(-3, 4)), loc = 'lower right')
plt.axis([-8, 8, -15, 15])


# %% Import Packages
import numpy as np
import sklearn as skl
import matplotlib.pyplot as plt
import pandas as pd

# %% normal linear least square
from sklearn.linear_model import LinearRegression

reg = LinearRegression()
reg.fit([[0, 0], [1, 1], [2, 2]], [0, 1, 2])
print(reg.coef_, reg.intercept_)

# %% Ridge regression same complexity with linear least square
from sklearn.linear_model import Ridge

reg = Ridge(alpha=0.5)
reg.fit([[0, 0], [0, 0], [1, 1]], [0, 0.1, 1])
print(reg.coef_, reg.intercept_)

# %% Ridge CV Generalized Cross-Validation
from sklearn.linear_model import RidgeCV
reg = RidgeCV(alphas=[0.15, 0.1, 0.3, 1.0])
reg.fit([[0, 0], [0, 0], [1, 1]], [0, .1, 1])
print(reg.alpha_, reg.coef_)

# %% the influence of Ridge Coeff to weight
X = 1. / (np.arange(1, 11) + np.arange(0, 10)[:, np.newaxis])
y = np.ones(10)

n_alphas = 200
alphas = np.logspace(-10, -2, n_alphas)

coefs = []
for a in alphas:
    rg = Ridge(alpha=a, fit_intercept=False)
    rg.fit(X, y)
    coefs.append(rg.coef_)

ax = plt.gca()
ax.plot(alphas, coefs)
ax.set_xscale('log')
ax.set_xlim(ax.get_xlim()[::-1])  # reverse axis
plt.xlabel('alpha')
plt.ylabel('weights')
plt.title('Ridge coefficents as a function of the regularization')
plt.axis('tight')
plt.show()

# %% LASSO
from sklearn.linear_model import Lasso
reg = Lasso(alpha=0.1)
reg.fit([[0, 0], [1, 1]], [0, 1])
reg.predict([[1, 1]])

# %%


#!/usr/bin/env python

from __future__ import print_function

import time

import numpy as np

from numba import jit, stencil

@stencil
def jacobi_kernel(A):
    return 0.25 * (A[0,1] + A[0,-1] + A[-1,0] + A[1,0])

@jit(parallel=True)
def jacobi_relax_core(A, Anew):
    error = 0.0
    n = A.shape[0]
    m = A.shape[1]
    Anew = jacobi_kernel(A)
    error = np.max(np.abs(Anew - A))
    return error

def main():
    NN = 3000
    NM = 3000

    A = np.zeros((NN, NM), dtype=np.float64)
    Anew = np.zeros((NN, NM), dtype=np.float64)

    n = NN
    m = NM
    iter_max = 1000

    tol = 1.0e-6
    error = 1.0

    for j in range(n):
        A[j, 0] = 1.0
        Anew[j, 0] = 1.0

    print("Jacobi relaxation Calculation: %d x %d mesh" % (n, m))

    timer = time.time()
    iter = 0

    while error > tol and iter < iter_max:
        error = jacobi_relax_core(A, Anew)

        # swap A and Anew
        tmp = A
        A = Anew
        Anew = tmp

        if iter % 100 == 0:
            print("%5d, %0.6f (elapsed: %f s)" % (iter, error, time.time()-timer))

        iter += 1

    runtime = time.time() - timer
    print(" total: %f s" % runtime)

if __name__ == '__main__':
    main()


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

from check import *
from methods import compute_fibonacci_sequence

# -----------------------------------------------------------------------------
# Golden ratio approximation
# -----------------------------------------------------------------------------
def plot_golden_ratio_approximation(n):
    # check value of the argument
    if n < 2:
        raise ValueError("Argument n should be larger than 2")
    # plot exact value of the golden ratio
    GOLDEN_RATIO = (1 + 5**0.5)/2
    plt.axhline(GOLDEN_RATIO, color = "red",
        label = r'$\varphi = \left(1+\sqrt{5}\right)/2$', linestyle = "--")
    # compute F_{i+1}/F_i with i from 1 to n-1
    sequence = compute_fibonacci_sequence(n)
    ratios = np.array(sequence[2:]) / np.array(sequence[1:-1])
    # plot
    plt.plot(list(range(1, n)), ratios,
        color = "blue", label = r"$F_{n+1}/F_n$")
    plt.xlabel(r"$n$")
    plt.title(r"Approximation of $\varphi$")
    plt.legend()
    plt.savefig("plots/golden_ratio_approximation.png")
    plt.show()

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
if __name__ == "__main__":

    n = 15
    check_fibonacci_argument(n)

    plot_golden_ratio_approximation(n)


# required modules
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib import cm
from matplotlib.colors import Normalize
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation

# two-dimesional version
def plot_mse_loss_surface_2d(fig, ax, x, y, v=0.0, l2=0.0, w1_range=(-2, 2), w2_range=(2, -2)):
    # create weight space
    n_w = 100
    w1 = np.linspace(w1_range[0], w1_range[1], num=n_w)  # weight 1
    w2 = np.linspace(w2_range[0], w2_range[1], num=n_w)  # weight 2
    ws_x, ws_y = np.meshgrid(w1, w2)
    cost_ws = np.zeros((n_w, n_w))  # initialize cost matrix

    # Fill the cost matrix for each combination of weights
    for i in range(n_w):
        for j in range(n_w):
            y_pred = ws_x[i, j] * ws_y[i, j] * x
            y_true = y
            cost_ws[i, j] = 0.5 * (y_true - y_pred)**2 + \
                0.5 * l2 * (ws_x[i, j]**2 + ws_y[i, j]**2) + 0.5 * v * (ws_x[i, j]*ws_y[i, j])**2

    # compute gradients
    dy, dx = np.gradient(cost_ws)

    # plot vector space
    skip = (slice(None, None, 5), slice(None, None, 5))
    # fig, ax = plt.subplots(figsize=(8, 8))
    #ax.contour(ws_x, ws_y, cost_ws, 200)
    im = ax.imshow(cost_ws, extent=[ws_x.min(), ws_x.max(
    ), ws_y.min(), ws_y.max()], cmap=cm.coolwarm)
    ax.quiver(ws_x[skip], ws_y[skip], -dx[skip], dy[skip], cost_ws[skip])
    cbar = fig.colorbar(im, ax=ax)
    # ax.set(aspect=1, title='Loss Surface')
    cbar.ax.set_ylabel('$Loss$', fontsize=15)

    ax.set_xlabel('$w_1$', fontsize=15)
    ax.set_ylabel('$w_2$', fontsize=15)
    # ax.grid()

    # add saddle point
    ax.scatter(0, 0, label='Saddle point', c='red', marker='*')
    # ax.scatter(0,0, c='black', marker=r'$\rightarrow$', label='Negative gradient')

    settings = (x, y, v, l2, w1_range, w2_range)

    return ax, settings

# three-dimensional version
def plot_mse_loss_surface_3d(ax, x, y, v=0.0, l2=0.0, w1_range=(-2, 2), w2_range=(2, -2), angle=30):
    # create weight space
    n_w = 100
    w1 = np.linspace(w1_range[0], w1_range[1], num=n_w)  # weight 1
    w2 = np.linspace(w2_range[0], w2_range[1], num=n_w)  # weight 2
    ws_x, ws_y = np.meshgrid(w1, w2)
    cost_ws = np.zeros((n_w, n_w))  # initialize cost matrix

    # Fill the cost matrix for each combination of weights
    for i in range(n_w):
        for j in range(n_w):
            y_pred = ws_x[i, j] * ws_y[i, j] * x
            y_true = y
            cost_ws[i, j] = 0.5 * (y_true - y_pred)**2 + \
                0.5 * l2 * (ws_x[i, j]**2 + ws_y[i, j]**2) + 0.5 * v * (ws_x[i, j]*ws_y[i, j])**2

    X = ws_x
    Y = ws_y
    Z = cost_ws

    #fig, ax = plt.subplots(figsize=(8, 8))
    #ax = fig.add_subplot(1,1,1, projection='3d')

    # fourth dimention - colormap
    # create colormap according to x-value (can use any 50x50 array)
    color_dimension = Z  # change to desired fourth dimension
    minn, maxx = color_dimension.min(), color_dimension.max()
    norm = Normalize(minn, maxx)
    m = plt.cm.ScalarMappable(norm=norm, cmap='jet')
    m.set_array([])
    fcolors = m.to_rgba(color_dimension)

    # plot
    # fig = plt.figure(figsize=(8, 8))
    # ax = fig.gca(projection='3d')
    ax.set_zlim(0, 50)
    ax.plot([0], [0], 'ro', c='red', marker='*', label='Saddle point')
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=fcolors,
                    vmin=minn, vmax=maxx, shade=False, alpha=1)

    ax.set_xlabel('$w_1$', fontsize=20)
    ax.set_ylabel('$w_2$', fontsize=20)
    ax.set_zlabel('$Loss$', fontsize=20)

    settings = (x, y, v, l2, w1_range, w2_range)
    ax.view_init(angle, 10)

    return ax, settings

def plot_global_minimum_manifold_2d(ax, settings):
    # retieve cached settings
    x, y, v, l2, w1_range, w2_range = settings

    n_w = 1000
    man_w1 = np.linspace(w1_range[0], w1_range[1], num=n_w)
    man_w2 = np.linspace(w2_range[0], w2_range[1], num=n_w)
    man_ws_x, man_ws_y = np.meshgrid(man_w1, man_w2)
    loss = 0.5 * y *(1 - man_ws_x * man_ws_y * x)**2 + \
        0.5 * l2 * (man_ws_x**2 + man_ws_y**2) + 0.5 * v * (man_ws_x * man_ws_y)**2
    min_loss = np.min(loss)
    manifold_indices = loss < min_loss + 1e-5
    manifold_x = man_ws_x[manifold_indices]
    manifold_y = man_ws_y[manifold_indices]

    # plot manifold of global minima
    ax.scatter(manifold_y, manifold_x, s=0.1, c='cyan',
               label='Manifold of global minima')

def plot_global_minimum_manifold_3d(ax, settings):
    # retieve cached settings
    x, y, v, l2, w1_range, w2_range = settings

    n_w = 1000
    man_w1 = np.linspace(w1_range[0], w1_range[1], num=n_w)
    man_w2 = np.linspace(w2_range[0], w2_range[1], num=n_w)
    man_ws_x, man_ws_y = np.meshgrid(man_w1, man_w2)
    loss = 0.5 * y * (1 - man_ws_x * man_ws_y * x)**2 + \
        0.5 * l2 * (man_ws_x**2 + man_ws_y**2) + 0.5 * v * (man_ws_x*man_ws_y)**2
    min_loss = np.min(loss)
    manifold_indices = loss < min_loss + 1e-5
    manifold_x = man_ws_x[manifold_indices]
    manifold_y = man_ws_y[manifold_indices]

    pos = np.where(np.abs(np.diff(manifold_y)) >= 0.1)[0]+1
    x = np.insert(manifold_x, pos, np.nan)
    y = np.insert(manifold_y, pos, np.nan)

    # plot manifold of global minima
    #ax.scatter(manifold_y, manifold_x, 0, s=0.5, c='cyan',
    #           label='Manifold of global minima')
    ax.plot(y, x, c='cyan',
                label='Manifold of global minima')

def plot_optimiser_trajectory_2d(ax, weights, **kwargs):
    w1_vals = weights['w1']
    w2_vals = weights['w2']
    ax.plot(w1_vals, w2_vals, **kwargs)

def plot_optimiser_trajectory_3d(ax, settings, weights, **kwargs):
    x, y, v, l2, _, _ = settings
    w1_vals = np.array(weights['w1'])
    w2_vals = np.array(weights['w2'])
    loss = 0.5 * y * (1 - w1_vals * w2_vals * x)**2 + \
        0.5 * l2 * (w1_vals**2 + w2_vals**2) + 0.5 * v * (w1_vals*w2_vals)**2
    ax.plot(w1_vals, w2_vals, loss, **kwargs)

def plot_optimiser_trajectory(x, y, weights, dim='2d', angle=45, manifold=False, **kwargs):
    if dim == '3d':
        ax, settings = plot_mse_loss_surface_3d(x, y, angle=angle)
        if manifold:
            plot_global_minimum_manifold_3d(ax, settings)
        plot_optimiser_trajectory_3d(ax, settings, weights, **kwargs)
    else:
        ax, settings = plot_mse_loss_surface_2d(x, y)
        if manifold:
            plot_global_minimum_manifold_2d(ax, settings)
        plot_optimiser_trajectory_2d(ax, weights, **kwargs)

def plot_weight_norm(ax, weights, **kwargs):
    w1_vals = np.array(weights['w1'])
    w2_vals = np.array(weights['w2'])
    epochs = np.arange(0, len(w1_vals), 1)
    norms = np.sqrt(w1_vals**2 + w2_vals**2)
    ax.set_xlabel('Epoch', fontsize=12)
    ax.set_ylabel('Weight norm', fontsize=12)
    ax.plot(epochs, norms, linewidth=2.0, **kwargs)

def animate_optimiser_trajectory_2d(i, ax, weights, **kwargs):
    w1_vals = weights['w1']
    w2_vals = weights['w2']
    ax.plot(w1_vals[:i], w2_vals[:i], **kwargs)
    return ax

def animate_optimiser_trajectory_3d(i, ax, settings, weights, **kwargs):
    x, y, v, l2, _, _ = settings
    w1_vals = np.array(weights['w1'])
    w2_vals = np.array(weights['w2'])
    loss = 0.5 * y * (1 - w1_vals * w2_vals * x)**2 + \
        0.5 * l2 * (w1_vals**2 + w2_vals**2) + 0.5 * v * (w1_vals*w2_vals)**2
    ax.plot(w1_vals[:i], w2_vals[:i], loss[:i], **kwargs)
    return ax

def plot_optimiser_loss(x, y, v, l2, weights, **kwargs):
    loss = []
    epoch = np.arange(0, len(weights['w1']))
    for w1, w2 in zip(weights['w1'], weights['w2']):
        loss_val = 0.5 * y * (1 - w1 * w2 * x)**2 + 0.5 * l2 * (w1**2 + w2**2) + 0.5 * v * (w1 * w2)**2
        loss.append(loss_val)
    plt.plot(epoch, loss, **kwargs)
    plt.xlabel('Epoch')
    plt.ylabel('Loss')

def plot_interpolated_trajectory_2d(ax, w1_a, w2_a, w1_b, w2_b, start=0, end=1, **kwargs):
    alpha = np.arange(start, end, 0.001)
    w1_path = []
    w2_path = []
    for a in alpha:
        ww1 = (1 - a) * w1_a + a * w1_b
        ww2 = (1 - a) * w2_a + a * w2_b
        w1_path.append(ww1)
        w2_path.append(ww2)
    ax.plot(w1_path, w2_path, **kwargs)

def plot_interpolated_trajectory_3d(ax, settings, w1_a, w2_a, w1_b, w2_b, start=0, end=1, **kwargs):
    x, y, _, _ = settings
    alpha = np.arange(start, end, 0.001)
    w1_path = []
    w2_path = []
    loss = []
    for a in alpha:
        ww1 = (1 - a) * w1_a + a * w1_b
        ww2 = (1 - a) * w2_a + a * w2_b
        loss_val = 0.5 * (y - ww1 * ww2 * x)**2 + 0.5 * l2 * (ww1**2 + ww2**2)
        loss.append(loss_val)
        w1_path.append(ww1)
        w2_path.append(ww2)
    ax.plot(w1_path, w2_path, loss, **kwargs)

def plot_interpolated_loss(x, y, w1_a, w2_a, w1_b, w2_b, start=0, end=1, **kwargs):
    alpha = np.arange(start, end, 0.001)
    interpolated_loss = []
    for a in alpha:
        ww1 = (1 - a) * w1_a + a * w1_b
        ww2 = (1 - a) * w2_a + a * w2_b
        loss_val = 0.5 * (y - ww1 * ww2 * x)**2 + 0.5 * l2 * (ww1**2 + ww2**2)
        interpolated_loss.append(loss_val)
    plt.plot(alpha, interpolated_loss, **kwargs)
    plt.xlabel(r'$\alpha$')
    plt.ylabel('Loss')

def plot_learning_dynamics(ax, weights, **kwargs):
    epoch = np.arange(0, len(weights['w1']))
    scores = []
    for w1, w2 in zip(weights['w1'], weights['w2']):
        scores.append(w1 * w2)
    ax.plot(epoch, scores, **kwargs)

def animate_learning_dynamics(i, ax, weights, y, **kwargs):
    n_epoch = len(weights['w1'])
    epoch = np.arange(1, n_epoch)
    scores = []
    for w1, w2 in zip(weights['w1'], weights['w2']):
        scores.append(w1 * w2)
    ax.set_xlim((1, n_epoch))
    ax.set_ylim((0, y))
    ax.set_xlabel('Epoch', fontsize=15)
    ax.set_ylabel('$w_2 \cdot w_1$', fontsize=15)
    ax.plot(epoch[:i], scores[:i], **kwargs)
    return ax

def animate_learning(weights, save=False, name='anim'):
    gs = gridspec.GridSpec(2, 4)
    gs.update(wspace=0.5)

    fig = plt.figure(figsize=(12, 8))

    ax1 = fig.add_subplot(gs[0, :2], )
    ax2 = fig.add_subplot(gs[0, 2:], projection='3d')
    ax3 = fig.add_subplot(gs[1, 1:3])

    # ax1 = fig.add_subplot(2, 2, 1)
    # ax2 = fig.add_subplot(2, 2, 2, projection = '3d')
    # ax3 = fig.add_subplot(2, 2, 3)
    # ax4 = fig.add_subplot(2, 2, 4)

    ax1, settings = plot_mse_loss_surface_2d(ax1, 1, 1)
    ax2, settings = plot_mse_loss_surface_3d(ax2, 1, 1, angle=60)
    plot_global_minimum_manifold_2d(ax1, settings)
    plot_global_minimum_manifold_3d(ax2, settings)

    def update(i):
        animate_optimiser_trajectory_2d(
            i, ax1, settings, weights, 'Gradient descent')
        animate_optimiser_trajectory_3d(
            i, ax2, settings, weights, 'Gradient descent')
        animate_learning_dynamics(i, ax3, weights, 1)
        # animate_weight_norm(i, ax4, scalarNet.history)

    # suncAnimation will call the 'update' function for each frame
    anim = FuncAnimation(fig, update, frames=100, interval=5, save_count=50)
    # HTML(anim.to_html5_video())

    if save:
        anim.save(name + '.gif', dpi=80, writer='imagemagick')
    plt.show()


# -*- coding: utf-8 -*-
# Inspired in: Justin Duke - http://jmduke.com/posts/basic-linear-regressions-in-python/

import unittest
import pandas as pd
import numpy as np
import random as rd
import matplotlib.pyplot as plt
import os

# Generate dataset (y = a·x + b)
data = []
a = rd.randint(1, 10)
b = rd.randint(-100, 100)

for i in np.arange(1000):
  val = i * a + b * rd.randint(0, 10) + rd.uniform(-1, 1) * 10
  data.append(val)

data = pd.Series(data)

# Calculate linear regression
a0, b0 = np.polyfit(data.index, data, 1)

class LinearRegressionTestCase(unittest.TestCase):
  def calculated_values_are_similar_test(self):
    self.assertTrue(abs(a - a0) < abs(2 * a))
    self.assertTrue(abs(b0) < abs(b * 10))
    # Generate plot of dataset and regression result
    if os.environ['LEAVES_PYTHON_WITH_PLOTS'] == 'true':
      linear_regression = []
      for i in np.arange(1000):
        linear_regression.append(a0 * i + b0)
      linear_regression = pd.Series(linear_regression)

      plt.plot(data, 'o', ms=2)
      plt.plot(linear_regression, 'r')
      plt.title('Linear Regression Example')
      plt.tight_layout()
      filepath = 'reports/languages/python/'
      if not os.path.isdir(filepath): os.makedirs(filepath)
      plt.savefig(filepath + 'linear-regression.png')
      plt.close()


import numpy as np
from numpy.random import RandomState
from randomcsv.Column import Column

class RandomNumberColumn(Column):
    """
    Creates a Column with random float numbers between low (included) and high (excluded)
        If a value for digits is provided, the column values will be rounded to the number of digits

    Example
    -------
    >>> column = RandomNumberColumn('MyRandomNumbers', low=10, high=20, digits=2)
    >>> column.generate_entries(2)
    0    15.89
    1    10.71
    dtype: float64
    """
    def __init__(self, name, low=0, high=1, digits=None, dtype=None, null_ratio=0, null_element=np.NAN,
                 random_state=None):
        """
        Parameters
        ----------
        :param name: Name of the Column
        :param low: Lower bound for the generated values (included, default 0)
        :param high: Upper bound for the generated values (excluded, default 1)
        :param digits: Number of digits to which the values are rounded, if None values are not rounded
        :param dtype: the datatype of the resulting pandas.Series. Per default None (automatically determined by pandas)
        :param null_ratio: ratio of elements, which will be replaced by the null_element (default 0.0). See Column
        :param null_element: value of invalid entries (default numpy's NAN). See Column
        :param random_state: If provided, elements which are replaced by the null_element are deterministic, for testing
                             See Column
        """
        super().__init__(name, dtype=dtype, null_ratio=null_ratio, null_element=null_element, random_state=random_state)
        self.low = low
        self.high = high
        self.digits = digits

    def _create_data(self, count):
        return [self._map_to_range(rnd) for rnd in RandomState(seed=self.random_state).random(count)]

    def _map_to_range(self, rnd):
        value = self.low + (self.high - self.low) * rnd
        if self.digits:
            return round(value, self.digits)
        return value


from numpy   import sqrt as np_sqrt
from numpy   import abs as np_abs

from .GradientDescent import GradientDescent

class AdaDeltaGD(GradientDescent):
    '''Implementation of AdaDelta.'''

    def __init__(self,function,gamma=0.95,epsilon=1e-6,alpha=1):
        '''
        function: instance of Function
        gamma: the decaying parameter
        epsilon: safety parameter (to avoid division by 0)
        '''
        GradientDescent.__init__(self,function)

        self.gamma=gamma
        self.epsilon=epsilon
        self.alpha=alpha

        # counters for the decaying means of the gradient and dx
        self.gE=[0 for _ in range(self.dim)]
        self.dxE=[0 for _ in range(self.dim)]

    def update(self,abs_tol=1e-5, rel_tol=1e-3):
        '''
        update should return a number that when it is smaller than 1
        the main loop stops. Here I choose this number to be:
        sqrt(1/dim*sum_{i=0}^{dim}(grad/(abs_tol+x*rel_tol))_i^2)
        '''
        _x2=0
        _check=0

        self.function.derivative(self.function.x)

        for i in range(self.dim):

            self.gE[i]=self.gamma*self.gE[i] + (1-self.gamma)*self.function.grad[i]**2
            dx=np_sqrt( (self.dxE[i]+self.epsilon)/(self.gE[i]+self.epsilon)  )*self.function.grad[i]*self.alpha

            self.dxE[i]=self.gamma*self.dxE[i] + (1-self.gamma)*dx**2

            self.function.x[i]=self.function.x[i] - dx

            _x2=abs_tol + np_abs(self.function.x[i]) * rel_tol
            _check+=(dx/_x2)*(dx/_x2)
            self.function.grad[i]=0

        _check=np_sqrt(1./self.dim *_check)

        self.steps.append(self.function.x[:])

        tmp_min=self.function(self.function.x)
        if tmp_min<self.f_min:
            self.function.minimum=self.function.x[:]
            self.f_min=tmp_min

        return _check


import numpy as np
from sklearn.linear_model import Lasso, Ridge, ElasticNet, LinearRegression

def mean_return_historic(r: np.array):
    '''
    Computes mean historical expected returns

    r: t x n np.array of returns

    out: 1 x n np.array of expected returns
    '''
    comp = np.prod(1 + r, axis = 0)
    nPer = r.shape[0]
    annr = comp ** (252 / nPer) - 1
    return annr

def ema_return_historic(r, window=22):
    '''
    Computes exponentially weighted historical returns

    r: t x n pd.DataFrame of returns
    window: window for calculating exponential moving average

    out: 1 x n np.array of expected returns
    '''
    ema = np.mean((1 + _ewma_vectorized_2d(r, window = window, axis = 1)) ,axis=0)
    annr = ema ** 252 - 1
    return annr

def sample_cov(r):
    '''
    Sample empirical covariance estimator

    r: t x n pd.DataFrame of returns

    out: n x n np.array of covariances
    '''
    return np.cov(r, rowvar=False) * 252

def elton_gruber_cov(r):
    '''
    Constant correlation model of Elton and Gruber

    r: t x n pd.DataFrame of returns

    out: n x n np.array of covariances
    '''
    rho = np.corrcoef(r, rowvar= False)
    n_ = rho.shape[0]

    rho_bar = (rho.sum() - n_) / (n_ * (n_ - 1))
    ccor = np.full_like(rho, rho_bar)
    np.fill_diagonal(ccor, 1.)
    sig = r.std()
    ccov = ccor * np.outer(sig, sig)
    return ccov

def shrinkage_cov(r, delta=0.5, prior_model=elton_gruber_cov, *args, **kwargs):
    '''
    Shrinks the sample covariance towards a specified model such as Elton/Gruber

    r: n x 1 returns
    delta: shrinkage parameter [0,1] #0.5 is standard
    prior_model: prior_model function that we shrink towards

    out: n x n shrunk covariance matrix
    '''

    prior = prior_model(r, *args, **kwargs)
    sig_hat = sample_cov(r)

    # https://jpm.pm-research.com/content/30/4/110
    honey = delta * prior + (1 - delta) * sig_hat
    return honey

def linear_factor_model(Y, X, kernel = None, regularize = None):
    t_, n_ = Y.shape

    #setting weights
    if kernel is None:
        kernel = np.ones(t_) / t_

    m_Y = kernel @ Y
    m_X = kernel @ X

    Y_p = ((Y - m_Y).T * np.sqrt(kernel)).T
    X_p = ((X - m_X).T * np.sqrt(kernel)).T

    #model fit
    if regularize == 'L1':
        mod = Lasso(alpha = 0.01/(2.*t_), fit_intercept = True)
    if regularize == 'L2':
        mod = Ridge(alpha = 0.01/(2.*t_), fit_intercept = True)
    if regularize == 'net':
        mod = ElasticNet(alpha = 0.01/(2.*t_), fit_intercept = True)
    else:
        mod = LinearRegression()

    mod.fit(X_p, Y_p)

    alpha = mod.intercept_
    beta = mod.coef_
    residuals = np.subtract(Y - alpha, X @ np.atleast_2d(beta.T))

    return alpha, beta, residuals

def _ewma_vectorized(data, alpha, offset=None, dtype=None, order='C', out=None):
    """
    Calculates the exponential moving average over a vector.
    Will fail for large inputs.
    :param data: Input data
    :param alpha: scalar float in range (0,1)
        The alpha parameter for the moving average.
    :param offset: optional
        The offset for the moving average, scalar. Defaults to data[0].
    :param dtype: optional
        Data type used for calculations. Defaults to float64 unless
        data.dtype is float32, then it will use float32.
    :param order: {'C', 'F', 'A'}, optional
        Order to use when flattening the data. Defaults to 'C'.
    :param out: ndarray, or None, optional
        A location into which the result is stored. If provided, it must have
        the same shape as the input. If not provided or `None`,
        a freshly-allocated array is returned.
    """
    data = np.array(data, copy=False)

    if dtype is None:
        if data.dtype == np.float32:
            dtype = np.float32
        else:
            dtype = np.float64
    else:
        dtype = np.dtype(dtype)

    if data.ndim > 1:
        # flatten input
        data = data.reshape(-1, order)

    if out is None:
        out = np.empty_like(data, dtype=dtype)
    else:
        assert out.shape == data.shape
        assert out.dtype == dtype

    if data.size < 1:
        # empty input, return empty array
        return out

    if offset is None:
        offset = data[0]

    alpha = np.array(alpha, copy=False).astype(dtype, copy=False)

    # scaling_factors -> 0 as len(data) gets large
    # this leads to divide-by-zeros below
    scaling_factors = np.power(1. - alpha, np.arange(data.size + 1, dtype=dtype),
                               dtype=dtype)
    # create cumulative sum array
    np.multiply(data, (alpha * scaling_factors[-2]) / scaling_factors[:-1],
                dtype=dtype, out=out)
    np.cumsum(out, dtype=dtype, out=out)

    # cumsums / scaling
    out /= scaling_factors[-2::-1]

    if offset != 0:
        offset = np.array(offset, copy=False).astype(dtype, copy=False)
        # add offsets
        out += offset * scaling_factors[1:]

    return out

def _ewma_vectorized_2d(data, window, axis=None, offset=None, dtype=None, order='C', out=None):
    """
    Calculates the exponential moving average over a given axis.
    Credit to Jake Walden: https://stackoverflow.com/questions/42869495/
    :param data: Input data, must be 1D or 2D array.
    :param alpha: scalar float in range (0,1)
        The alpha parameter for the moving average.
    :param axis: The axis to apply the moving average on.
        If axis==None, the data is flattened.
    :param offset: optional
        The offset for the moving average. Must be scalar or a
        vector with one element for each row of data. If set to None,
        defaults to the first value of each row.
    :param dtype: optional
        Data type used for calculations. Defaults to float64 unless
        data.dtype is float32, then it will use float32.
    :param order: {'C', 'F', 'A'}, optional
        Order to use when flattening the data. Ignored if axis is not None.
    :param out: ndarray, or None, optional
        A location into which the result is stored. If provided, it must have
        the same shape as the desired output. If not provided or `None`,
        a freshly-allocated array is returned.
    """
    data = np.array(data, copy=False)

    assert data.ndim <= 2

    #calculate the window
    sum_proportion = 0.99 #proportion of the window to be summed upon
    alpha = 1 - np.exp(np.log(1 - sum_proportion) / window)

    if dtype is None:
        if data.dtype == np.float32:
            dtype = np.float32
        else:
            dtype = np.float64
    else:
        dtype = np.dtype(dtype)

    if out is None:
        out = np.empty_like(data, dtype=dtype)
    else:
        assert out.shape == data.shape
        assert out.dtype == dtype

    if data.size < 1:
        # empty input, return empty array
        return out

    if axis is None or data.ndim < 2:
        # use 1D version
        if isinstance(offset, np.ndarray):
            offset = offset[0]
        return _ewma_vectorized(data, alpha, offset, dtype=dtype, order=order,
                               out=out)

    assert -data.ndim <= axis < data.ndim

    # create reshaped data views
    out_view = out
    if axis < 0:
        axis = data.ndim - int(axis)

    if axis == 0:
        # transpose data views so columns are treated as rows
        data = data.T
        out_view = out_view.T

    if offset is None:
        # use the first element of each row as the offset
        offset = np.copy(data[:, 0])
    elif np.size(offset) == 1:
        offset = np.reshape(offset, (1,))

    alpha = np.array(alpha, copy=False).astype(dtype, copy=False)

    # calculate the moving average
    row_size = data.shape[1]
    row_n = data.shape[0]
    scaling_factors = np.power(1. - alpha, np.arange(row_size + 1, dtype=dtype),
                               dtype=dtype)
    # create a scaled cumulative sum array
    np.multiply(
        data,
        np.multiply(alpha * scaling_factors[-2], np.ones((row_n, 1), dtype=dtype),
                    dtype=dtype)
        / scaling_factors[np.newaxis, :-1],
        dtype=dtype, out=out_view
    )
    np.cumsum(out_view, axis=1, dtype=dtype, out=out_view)
    out_view /= scaling_factors[np.newaxis, -2::-1]

    if not (np.size(offset) == 1 and offset == 0):
        offset = offset.astype(dtype, copy=False)
        # add the offsets to the scaled cumulative sums
        out_view += offset[:, np.newaxis] * scaling_factors[np.newaxis, 1:]

    return out


"""
A Pythagorean triplet is a set of three natural numbers, a < b < c, for which,

a2 + b2 = c2
For example, 32 + 42 = 9 + 16 = 25 = 52.

There exists exactly one Pythagorean triplet for which a + b + c = 1000.
Find the product abc.
"""

import numpy

def find_triplet(rangenum: int, thousand: int):
    """
    get summary
    >>> find_triplet(500, 1000)
    31875000
    """
    for a in range(1, rangenum):
        for b in range(1, rangenum):
            c = numpy.sqrt(a ** 2 + b ** 2)

            if a + b + c == thousand:
                return int(a * b * c)

if __name__ == "__main__":
    import doctest
    doctest.testmod()


r"""
Solve Poisson equation in 1D with homogeneous Neumann bcs

    \nabla^2 u = f

Use Shen's Neumann basis

The equation to solve is

     (\nabla^2 u, v) = (f, v)

"""
import sys
import os
import importlib
from sympy import symbols, sin, cos, pi, chebyshevt
import numpy as np
from shenfun import inner, div, grad, TestFunction, TrialFunction, FunctionSpace, \
    Array, Function, legendre, chebyshev, extract_bc_matrices, la, SpectralMatrix

# Collect basis from either Chebyshev or Legendre submodules
assert len(sys.argv) == 3, "Call with two command-line arguments"
assert sys.argv[-1].lower() in ('legendre', 'chebyshev')
assert isinstance(int(sys.argv[-2]), int)
family = sys.argv[-1].lower()

# Use sympy to compute a rhs, given an analytical solution
x = symbols("x", real=True)
alpha = 0
ue = sin(pi*x)*(1-x**2)
fe = -ue.diff(x, 2)+alpha*ue

# Size of discretization
N = int(sys.argv[-2])

# alpha=0 requires a fixed gauge, but not alpha!=0 -> mean
SD = FunctionSpace(N, family=family, bc={'left': ('N', 0), 'right': ('N', 0)},
                   mean=0 if alpha==0 else None, basis='ShenNeumann')
u = TrialFunction(SD)
v = TestFunction(SD)

# Get f on quad points
fj = Array(SD, buffer=fe)

# Compute right hand side of Poisson equation
f_hat = inner(v, fj)

# Get left hand side of Poisson equation
A0 = inner(v, div(grad(u)))
B0 = inner(v, u)

# Solve
u_hat = Function(SD)
M = alpha*B0-A0
sol = la.Solve(M, SD)
u_hat = sol(f_hat, u_hat)

# Transform to real space
uj = u_hat.backward()

# Compare with analytical solution
ua = Array(SD, buffer=ue)
print(abs(uj-ua).max())
assert np.allclose(uj, ua)

if 'pytest' not in os.environ:
    import matplotlib.pyplot as plt
    plt.figure()
    X = SD.mesh()
    plt.plot(X, uj)

    plt.figure()
    plt.plot(X, ua)

    plt.figure()
    plt.plot(X, ua-uj)
    plt.title('Error')
    #plt.show()


# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""
import time as tm
import math as ma
#import random as rd
import numpy as np
import matplotlib.pyplot as mp
import funfit as f1

xnum=10
t0=np.linspace(0,1,1000)
x=np.random.random(xnum)
y=np.zeros(xnum,)

for ind in range(0,xnum,):
    y[ind]=ma.sin(x[ind]*ma.pi*2)

mp.plot(x,y,'b*')

#==============================================================================
# Fitting with y=w0+w1*x
#==============================================================================
w0=0
w1=0
lb=.1

for iter in range(1,10000):
    yfit=f1.gs1order(w0,w1,x)
    cf1=f1.cf(y,yfit)
#    print '# Iter',str(iter),': ',str(cf1)

    w0=w0-lb*np.sum(yfit-y)
    w1=w1-lb*np.sum((yfit-y)*x)

t1=w0+w1*t0
mp.plot(x,y,'rx')
mp.plot(t0,t1)

#==============================================================================
# Fitting with y=w0+w1*x+w2*x^2
#==============================================================================
w0=0
w1=0
w2=0
lb=.1

for iter in range(1,5000):
    yfit=f1.gs2order(w0,w1,w2,x)
    cf2=f1.cf(y,yfit)
#    print '# Iter',str(iter),': ',str(cf2)

    w0=w0-lb*np.sum(yfit-y)
    w1=w1-lb*np.sum((yfit-y)*x)
    w2=w2-lb*np.sum((yfit-y)*x**2)

t2=w0+w1*t0+w2*t0**2
mp.plot(x,y,'rx')
mp.plot(t0,t2)

#==============================================================================
# Fitting with 5 degree polynomial
#==============================================================================
w0=0
w1=0
w2=0
w3=0
w4=0
w5=0
lb=.1

for iter in range(1,10000):
    yfit=f1.gs5order(w0,w1,w2,w3,w4,w5,x)
    cf5=f1.cf(y,yfit)
#    print '# Iter',str(iter),': ',str(cf5)

    w0=w0-lb*np.sum(yfit-y)
    w1=w1-lb*np.sum((yfit-y)*x)
    w2=w2-lb*np.sum((yfit-y)*x**2)
    w3=w3-lb*np.sum((yfit-y)*x**3)
    w4=w4-lb*np.sum((yfit-y)*x**4)
    w5=w5-lb*np.sum((yfit-y)*x**5)

t5=f1.gs5order(w0,w1,w2,w3,w4,w5,t0)
mp.plot(x,y,'rx')
mp.plot(t0,t5)

#==============================================================================
# Fitting with n degree polynomial
#==============================================================================
n=5
w=np.zeros([1,n+1])
lb=.05
xext=f1.gsnxext(x,n)
cfprev=f1.cf(y,f1.gsnorder(w.T,n,x))
gdsucc=1

start=tm.clock()
for iter in range(1,90000):
    yfit=f1.gsnorder(w.T,n,x)
    cf=f1.cf(y,yfit)

    if (cf-cfprev)<1e-2:
#        print '# Iter',str(iter),': ',str(cf)
        w=w-lb*np.dot((yfit-y),xext)
        cfprev=cf
    else:
        print 'Parameter is too large! Please choose a smaller one.'
        gdsucc=0
        break

end=tm.clock()

if gdsucc==1:
    t1=np.dot(f1.gsnxext(t0,n),w.T)
    mp.plot(x,y,'ro')
    mp.plot(t0,t1)
    leg='M='+str(n)
    mp.legend(('Xn',leg))
    mp.title('Fitting with polynomial')
    print "Successful! Running time: %f s" % (end-start)
    print "> Cost Value: %f" % cf
    print "> Fitting with %d degree polynomial" % n
    print "> Parameter of the polynomial:",w
    print "> Absolute value of (y-yfit):",abs(y-yfit)

#==============================================================================
# Regularized fitting with n degree polynomial
#==============================================================================
n=12
w=np.zeros([1,n+1])
lb=.01
regpar=1
xext=f1.gsnxext(x,n)
cfprev=f1.cf(y,f1.gsnorder(w.T,n,x))
gdsucc=1

start=tm.clock()
for iter in range(1,90000):
    yfit=f1.gsnorder(w.T,n,x)
    cf=f1.cf(y,yfit)

    if (cf-cfprev)<1e-4:
#        print '# Iter',str(iter),': ',str(cf)
        w=w-lb*(np.dot((yfit-y),xext)+regpar*w)
        w[0]=w[0]-lb*(np.dot((yfit-y),xext)[0])
        cfprev=cf
    else:
        print 'Parameter is too large! Please choose a smaller one.'
        gdsucc=0
        break

end=tm.clock()

if gdsucc==1:
    t1=np.dot(f1.gsnxext(t0,n),w.T)
    mp.plot(x,y,'ro')
    mp.plot(t0,t1)
    leg='M='+str(n)
    mp.legend(('Xn',leg))
    mp.title('Regularized fitting with polynomial (lambda=%0.4f)'%lb)
    print "Successful! Running time: %f s" % (end-start)
    print "> Cost Value: %f" % cf
    print "> Fitting with %d degree polynomial" % n
    print "> Parameter of the polynomial:",w
    print "> Absolute value of (y-yfit):",abs(y-yfit)


from __future__ import annotations
import numpy as np
import numpy.random
from numpy.linalg import inv, det, slogdet
from matplotlib import pyplot as plt

class UnivariateGaussian:
    """
    Class for univariate Gaussian Distribution Estimator
    """

    def __init__(self, biased_var: bool = False) -> UnivariateGaussian:
        """
        Estimator for univariate Gaussian mean and variance parameters

        Parameters
        ----------
        biased_var : bool, default=False
            Should fitted estimator of variance be a biased or unbiased estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `UnivariateGaussian.fit` function.

        mu_: float
            Estimated expectation initialized as None. To be set in `UnivariateGaussian.fit`
            function.

        var_: float
            Estimated variance initialized as None. To be set in `UnivariateGaussian.fit`
            function.
        """
        self.biased_ = biased_var
        self.fitted_, self.mu_, self.var_ = False, None, None

    def fit(self, X: np.ndarray) -> UnivariateGaussian:
        """
        Estimate Gaussian expectation and variance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Training data

        Returns
        -------
        self : returns an instance of self.

        Notes
        -----
        Sets `self.mu_`, `self.var_` attributes according to calculated estimation (where
        estimator is either biased or unbiased). Then sets `self.fitted_` attribute to `True`
        """
        self.mu_ = np.average(X)
        self.var_ = np.var(X)
        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray) -> np.ndarray:
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, )
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, var_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")
        exponent = np.exp(-1 * np.divide(np.power(X - self.mu_, 2), (2 * self.var_)))
        return np.multiply((1 / np.sqrt(2 * np.pi * self.var_)), exponent)

    @staticmethod
    def log_likelihood(mu: float, sigma: float, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : float
            Expectation of Gaussian
        sigma : float
            Variance of Gaussian
        X : ndarray of shape (n_samples, )
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated
        """
        var = sigma ** 2
        exponent = np.exp(-1 * np.divide(np.power(X - mu, 2), (2 * var)))
        y = np.multiply((1 / np.sqrt(2 * np.pi * var)), exponent)
        summery = 0
        for elem in y:
            summery +=np.log2(elem)
        return summery

class MultivariateGaussian:
    """
    Class for multivariate Gaussian Distribution Estimator
    """

    def __init__(self):
        """
        Initialize an instance of multivariate Gaussian estimator

        Attributes
        ----------
        fitted_ : bool
            Initialized as false indicating current estimator instance has not been fitted.
            To be set as True in `MultivariateGaussian.fit` function.

        mu_: ndarray of shape (n_features,)
            Estimated expectation initialized as None. To be set in `MultivariateGaussian.fit`
            function.

        cov_: ndarray of shape (n_features, n_features)
            Estimated covariance initialized as None. To be set in `MultivariateGaussian.fit`
            function.
        """
        self.mu_, self.cov_ = None, None
        self.fitted_ = False

    def fit(self, X: np.ndarray) -> MultivariateGaussian:
        """
        Estimate Gaussian expectation and covariance from given samples

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Training data

        Returns
        -------
        self : returns an instance of self

        Notes
        -----
        Sets `self.mu_`, `self.cov_` attributes according to calculated estimation.
        Then sets `self.fitted_` attribute to `True`
        """
        self.mu_ = np.mean(X, axis=0)
        self.cov_ = np.cov(X.T)
        self.fitted_ = True
        return self

    def pdf(self, X: np.ndarray):
        """
        Calculate PDF of observations under Gaussian model with fitted estimators

        Parameters
        ----------
        X: ndarray of shape (n_samples, n_features)
            Samples to calculate PDF for

        Returns
        -------
        pdfs: ndarray of shape (n_samples, )
            Calculated values of given samples for PDF function of N(mu_, cov_)

        Raises
        ------
        ValueError: In case function was called prior fitting the model
        """
        if not self.fitted_:
            raise ValueError("Estimator must first be fitted before calling `pdf` function")
        d = len(self.mu_)
        divider = 1 / (((2 * np.pi) ** d) * det(self.cov_))
        f_arr = []
        for x in X:
            power = -0.5 * np.matmul(np.matmul((x - self.mu_).T, self.cov_.T), (x - self.mu_))
            f_arr.append(divider * np.exp(power.item()))
        return f_arr

    @staticmethod
    def log_likelihood(mu: np.ndarray, cov: np.ndarray, X: np.ndarray) -> float:
        """
        Calculate the log-likelihood of the data under a specified Gaussian model

        Parameters
        ----------
        mu : ndarray of shape (n_features,)
            Expectation of Gaussian
        cov : ndarray of shape (n_features, n_features)
            covariance matrix of Gaussian
        X : ndarray of shape (n_samples, n_features)
            Samples to calculate log-likelihood with

        Returns
        -------
        log_likelihood: float
            log-likelihood calculated over all input data and under given parameters of Gaussian
        """
        d = len(mu)
        divider = 1 / (((2 * np.pi) ** d) * det(cov))
        f_arr = []
        for x in X:
            power = -0.5 * np.matmul(np.matmul((x - mu).T, cov.T), (x - mu))
            f_arr.append(+divider * np.exp(power.item()))
        summery =0
        for i in range(len (f_arr)):
            summery += np.log2(f_arr[i])
        # if prod == 0:
        #     return 0
        # return prod
        return summery

if __name__ == "__main__":
    Q_3_array = np.array([1, 5, 2, 3, 8, -4, -2, 5, 1, 10, -10, 4, 5, 2, 7, 1, 1, 3, 2, -1, -3, 1, -4, 1, 2, 1,
                          -4, -4, 1, 3, 2, 6, -6, 8, 3, -6, 4, 1, -2, 3, 1, 4, 1, 4, -2, 3, -1, 0, 3, 5, 0, -2])
    print("mu=1 : {}".format(UnivariateGaussian.log_likelihood(1, 1, Q_3_array)))
    print("mu=10 : {}".format(UnivariateGaussian.log_likelihood(10, 1, Q_3_array)))


# -*- coding: utf-8 -*-
"""
Created on Mon Apr 13 19:19:16 2020

@author: Wallace

Based (with some adaptations) on:

"Hands On Machine Learning" 2nd Edition,
by Aurélien Géron https://www.oreilly.com/library/view/hands-on-machine-learning/9781492032632/
"""

from sklearn import datasets
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import classification_report, confusion_matrix

iris = datasets.load_iris()

X = iris["data"][:, 3:]  # petal width
y = (iris["target"] == 2).astype(np.int)  # 1 if Iris virginica, else 0
print(y)

from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression(solver="lbfgs", random_state=42,C=10**0)
log_reg.fit(X, y)

X_new = np.linspace(0, 3, 1000).reshape(-1, 1)
y_proba = log_reg.predict_proba(X_new)

#plt.plot(X_new, y_proba[:, 1], "g-", linewidth=2, label="Iris virginica")
#plt.plot(X_new, y_proba[:, 0], "b--", linewidth=2, label="Not Iris virginica")

X_new = np.linspace(0, 3, 1000).reshape(-1, 1)
y_proba = log_reg.predict_proba(X_new)
decision_boundary = X_new[y_proba[:, 1] >= 0.5][0]

plt.figure(figsize=(8, 3))
plt.plot(X[y==0], y[y==0], "bs")
plt.plot(X[y==1], y[y==1], "g^")
plt.plot([decision_boundary, decision_boundary], [-1, 2], "k:", linewidth=2)
plt.plot(X_new, y_proba[:, 1], "g-", linewidth=2, label="Iris virginica")
plt.plot(X_new, y_proba[:, 0], "b--", linewidth=2, label="Not Iris virginica")
plt.text(decision_boundary+0.02, 0.15, "Decision  boundary", fontsize=14, color="k", ha="center")
plt.arrow(decision_boundary, 0.08, -0.3, 0, head_width=0.05, head_length=0.1, fc='b', ec='b')
plt.arrow(decision_boundary, 0.92, 0.3, 0, head_width=0.05, head_length=0.1, fc='g', ec='g')
plt.xlabel("Petal width (cm)", fontsize=14)
plt.ylabel("Probability", fontsize=14)
plt.legend(loc="center left", fontsize=14)
plt.axis([0, 3, -0.02, 1.02])
plt.show()

print('Model Accuracy: \n')
print(log_reg.score(X,y))
print('\n')

print('Confusion Matrix:')
print(confusion_matrix(y, log_reg.predict(X)))
print('\n')

print(classification_report(y, log_reg.predict(X)))

b0 = log_reg.intercept_
b1 = log_reg.coef_[0]
y_log_reg = b0 + b1*X # Logit function: y = b0 + b1*x

from scipy.special import expit
xt = np.linspace(0,10,100)
pt = expit(b0+ b1*xt)

fig, ax = plt.subplots()
plt.plot(X[y==0], y[y==0], "bs",label='Not Virginica')
plt.plot(X[y==1], y[y==1], "g^",label='Virginica')
plt.plot(X,y_log_reg, 'r-',label='Regr. Line, f(x)')
plt.axis([0, 3, -0.02, 1.02])
plt.plot(xt,pt, 'k-',label='Logit Line, p(x)')
plt.legend(facecolor='white')
plt.show()


#Activation function

import numpy as np
import nnfs
from nnfs.datasets import spiral_data

nnfs.init()

X = [[1, 2, 3, 2.5],
		[2.0, 5.0, -1.0, 2.0],
		[-1.5, 2.7, 3.3, -0.8]]

X, y = spiral_data(100, 3)
# inputs = [0, 2, -1, 3.3, -2.7, 1.1, 2.2, -100]
# output = []

'''
for i in inputs:
	output.append(max(0, i))
	# if i>0:
	# 	output.append(i)
	# elif i<=0:
	# 	output.append(0)

print(output)
'''

class Layer_Dense:
	def __init__(self, n_inputs, n_neurons):
		self.weights = 0.10 * np.random.randn(n_inputs, n_neurons)
		self.biases = np.zeros((1, n_neurons))
	def forward(self, inputs):
		self.output = np.dot(inputs, self.weights) + self.biases

class Activation_ReLU:
	def forward(self, inputs):
		self.output = np.maximum(0, inputs)

layer1 = Layer_Dense(2, 5)
# layer2 = Layer_Dense(5, 2)

activation1 = Activation_ReLU()

layer1.forward(X)
activation1.forward(layer1.output)

print(activation1.output)
# print(layer1.output)
# activation1.forward(layer1.output)
#print(layer1.output)
# layer2.forward(layer1.output)
# print(layer2.output)


# USEFUL METHODS OF THE NUMPY ARRAYS
# Santiago Garcia Arango, June 2020
# -------------------------------------------------------------------------
import numpy as np

array_1 = np.arange(25)  # Equivalent to: [0,1,2,3, ...,22, 23, 24]
array_2 = np.random.randint(0, 50, 10)  # Vector of 5 random integers(0,50)

print("array_1:\n", array_1, "\n")
print("array_2:\n", array_2, "\n")

# ----------------------RESHAPE METHOD--------------------------------------
# This method allows us to reshape an array into a given shape (must match)
# Remark: the product of dimensions N*M must be equal to the size of vector
new_array_1 = array_1.reshape(5, 5)
print("array_1 (reshaped):\n", new_array_1, "\n")

# -------------------------GET SHAPE OF ARRAY------------------------------
print("SHAPE(array_1) -->", array_1.shape)
print("SHAPE(array_1 (reshaped) ) -->", new_array_1.shape)
print("SHAPE(array_2) -->", array_2.shape, "\n")

# -------------------MAXIMUM AND MINIMUM OF ARRAY---------------------------
# array.max(): returns the max VALUE of a given array
# array.min(): returns the min VALUE of a given array
# array.argmax(): returns INDEX location of the max VALUE of given array
# array.argmin(): returns INDEX location of the min VALUE of given array
print("MAX(array_2) =", array_2.max(), " (at index", array_2.argmax(), ")\n")
print("MIN(array_2) =", array_2.min(), " (at index", array_2.argmin(), ")\n")

# ------------------------DATATYPE OF ARRAY----------------------------------
print("DATATYPE(array_1) -->", array_1.dtype)
print("DATATYPE(array_2) -->", array_2.dtype)


import numpy as np

from optimizer import Optimizer

class Bfgs(Optimizer):
    """
    Broyden–Fletcher–Goldfarb–Shanno algorithm.

    Arguments:
        L (float, optional): an upper bound on the smoothness constant
            to initialize the Hessian estimate
        hess_estim (float array of shape (dim, dim)): initial Hessian estimate
        lr (float, optional): stepsize (default: 1)
    """
    def __init__(self, L=None, hess_estim=None, lr=1, line_search=False, ls_backtrack=0.5, ls_it_max=100, *args, **kwargs):
        super(Bfgs, self).__init__(*args, **kwargs)
        if L is None and hess_estim is None:
            L = self.loss.smoothness()
            if L is None:
                raise ValueError("Either smoothness constant L or Hessian estimate must be provided")
        self.lr = lr
        self.L = L
        self.B = hess_estim
        self.line_search = line_search
        self.ls_backtrack = ls_backtrack
        self.ls_it_max = ls_it_max

    def step(self):
        self.grad = self.loss.gradient(self.x)
        x_new = self.x - self.B_inv @ self.grad
        if self.line_search:
            lr = min(1., self.lrs[-1] / self.ls_backtrack if len(self.lrs) > 0 else .1)
            for i in range(self.ls_it_max):
                f_new = self.loss.value(self.x + lr * (x_new - self.x))
                if f_new < self.f:
                    break
                lr *= self.ls_backtrack
            self.lrs.append(lr)
            self.f = f_new
        else:
            lr = self.lr
        x_new = self.x + lr * (x_new - self.x)

        s = x_new - self.x
        grad_new = self.loss.gradient(x_new)
        y = grad_new - self.grad
        self.grad = grad_new
        Bs = self.B @ s
        sBs = s @ Bs
        Binv_y = self.B_inv @ y
        y_Binv_y = y @ Binv_y
        self.B += np.outer(y, y)/(y@s) - np.outer(Bs, Bs)/sBs
        self.B_inv += (s@y + y_Binv_y) * np.outer(s, s) / (s@y)**2
        self.B_inv -= (np.outer(Binv_y, s) + np.outer(s, Binv_y)) / (s@y)
        self.x = x_new

    def init_run(self, *args, **kwargs):
        super(Bfgs, self).init_run(*args, **kwargs)
        if self.B is None:
            self.B = self.L * np.eye(self.loss.dim)
            self.B_inv = 1 / self.L * np.eye(self.loss.dim)
        else:
            self.B_inv = np.linalg.pinv(self.B)
        self.grad = self.loss.gradient(self.x)
        if self.line_search:
            self.f = self.loss.value(self.x)
            self.lrs = []


#%%
import random
import numpy as np
from numpy.random import randn
num_data  = 200
e_z = randn(num_data)
e_y = randn(num_data)

Z = e_z
Y = 2*Z + e_y
# %%
np.corrcoef(Z, Y)

# %%
import scipy.stats
import matplotlib.pyplot as plt
%matplotlib inline
Z_std = scipy.stats.zscore(Z)
Y_std = scipy.stats.zscore(Y)

plt.scatter(Z_std, Y_std)
# %%
e_z = randn(num_data)
e_y = randn(num_data)

Y = e_y
Z = 2*Y + e_z
np.corrcoef(Z, Y)

# %%
Z_std = scipy.stats.zscore(Z)
Y_std = scipy.stats.zscore(Y)

plt.scatter(Z_std, Y_std)
# %%

e_x = randn(num_data)
e_y = randn(num_data)
e_z = randn(num_data)

Z = 3.3*e_x + e_z
Y = 3.3*e_x + e_y

np.corrcoef(Z, Y)
# %%
Z_std = scipy.stats.zscore(Z)
Y_std = scipy.stats.zscore(Y)

plt.scatter(Z_std, Y_std)
# %%
num_data = 600
e_x = randn(num_data)
e_y = randn(num_data)

x = e_x
y = e_y

plt.scatter(x, y)
# %%
np.corrcoef(x, y)
# %%
z = x + y

x_new = np.array([])
y_new = np.array([])
# %%

for i in range(num_data):
    if z[i] > 0.0:
        x_new = np.append(x_new, x[i])
        y_new = np.append(y_new, y[i])

plt.scatter(x_new, y_new)
# %%
np.corrcoef(x_new, y_new)
# %%

num_data = 200
x_1 = np.random.randint(15, 76, num_data)

x_2 = np.random.randint(0, 2, num_data)
# %%
from scipy.special import expit # 1.
e_x = randn(num_data)

z_base = x_1 + (1 - x_2)*10 - 40 + 5*e_z

z_prob = expit(0.1 * z_base)
# %%
Z = np.array([])
for i in range(num_data):
    Z_i = np.random.choice(2, size=1, p=[1-z_prob[i], z_prob[i]])[0]
    Z = np.append(Z, Z_i)
# %%
e_y = randn(num_data)
Y = -x_1 + 30*x_2 + 10*Z + 80 + 10*e_y

# %%
import pandas as pd
df = pd.DataFrame({'age':x_1,
                   'Sex':x_2,
                   'WatchedCM':Z,
                   'PurchaseAmount':Y,
                   })
df.head()

# %%
print(df[df["WatchedCM"] == 1.0].mean())
print("-----------")
print(df[df["WatchedCM"] == 0.0].mean())
# %%
from sklearn.linear_model import LinearRegression
X = df[["age","Sex","WatchedCM"]]
y = df["PurchaseAmount"]

reg = LinearRegression().fit(X, y)
print("Coef:", reg.coef_)
# %%
from sklearn.linear_model import LogisticRegression
X = df[["age", "Sex"]]
Z = df["WatchedCM"]

reg = LogisticRegression().fit(X, Z)
print("Coef_beta:", reg.coef_)
print("Coef_alpha:", reg.intercept_)
# %%

Z_pre = reg.predict_proba(X)
print(Z_pre[0:5])
print("-----")
print(Z[0:5])
# %%

ATE_i = Y/Z_pre[:, 1]*Z - Y/Z_pre[:, 0]*(1-Z)
ATE = 1/len(Y)*ATE_i.sum()
print("predicted_ATE", ATE)
# %%
X = df[["age","Sex","WatchedCM"]]
y = df["PurchaseAmount"]

reg2 = LinearRegression().fit(X, y)
X_0 = X.copy()
X_0["WatchedCM"] = 0
Y_0 = reg2.predict(X_0)

X_1 = X.copy()
X_1["WatchedCM"] = 1
Y_1 = reg2.predict(X_1)

X = df[["age", "Sex"]]
Z = df["WatchedCM"]

reg = LogisticRegression().fit(X, Z)
Z_pre = reg.predict_proba(X)
print(Z_pre[0:5])
# %%
ATE_1_i = Y/Z_pre[:, 1]*Z + (1-Z/Z_pre[:, 1])*Y_1
ATE_0_i = Y/Z_pre[:, 0]*(1-Z) + (1-(1-Z)/Z_pre[:, 0])*Y_0
ATE = 1/len(Y)*(ATE_1_i-ATE_0_i).sum()
print("Estimated_ATE",ATE)
# %%

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split

X = df[["age", "Sex"]]
Z = df["WatchedCM"]

X_train, X_val, Z_train, Z_val = train_test_split(
    X, Z, train_size = 0.6, random_state = 0)

clf = DecisionTreeClassifier(max_depth=1, random_state=0)
clf.fit(X_train, Z_train)
print("Performance_of_Depth1:",clf.score(X_val, Z_val))
# %%

clf = DecisionTreeClassifier(max_depth=2, random_state=0)
clf.fit(X_train, Z_train)
print("Performance_of_Depth2:",clf.score(X_val, Z_val))

clf = DecisionTreeClassifier(max_depth=3, random_state=0)
clf.fit(X_train, Z_train)
print("Performance_of_Depth3:",clf.score(X_val, Z_val))
# %%

from sklearn.tree import DecisionTreeRegressor
X = df[["age", "Sex"]]
Y = df["PurchaseAmount"]

X_train, X_val, Y_train, Y_val = train_test_split(
    X, Y, train_size = 0.6, random_state = 0)

reg = DecisionTreeRegressor(max_depth=2, random_state=0)
reg.fit(X_train, Y_train)
print("Performance_of_Depth1:",reg.score(X_val, Y_val))

reg = DecisionTreeRegressor(max_depth=3, random_state=0)
reg.fit(X_train, Y_train)
print("Performance_of_Depth2:",reg.score(X_val, Y_val))

reg = DecisionTreeRegressor(max_depth=4, random_state=0)
reg.fit(X_train, Y_train)
print("Performance_of_Depth3:",reg.score(X_val, Y_val))
# %%
from sklearn.ensemble import RandomForestClassifier

X = df[["age", "Sex"]]
Z = df["WatchedCM"]

X_train, X_val, Z_train, Z_val = train_test_split(
    X, Z, train_size = 0.6, random_state = 0)

clf = RandomForestClassifier(max_depth=1, random_state=0)
clf.fit(X_train, Z_train)
print("Performance_of_Depth1:",clf.score(X_val, Z_val))

clf = RandomForestClassifier(max_depth=2, random_state=0)
clf.fit(X_train, Z_train)
print("Performance_of_Depth2:",clf.score(X_val, Z_val))

clf = RandomForestClassifier(max_depth=3, random_state=0)
clf.fit(X_train, Z_train)
print("Performance_of_Depth3:",clf.score(X_val, Z_val))

import numpy,from numpy,import scipy,from scipy


import numpy as np
from typing import List

def nums_median(nums):
    len_nums = len(nums)
    if len_nums % 2 == 0:
        return float((nums[len_nums//2-1]+nums[len_nums//2])/2)
    else:
        return float(nums[len_nums//2])

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        len_nums1 = len(nums1)
        len_nums2 = len(nums2)
        if len(nums1)!=0 and len(nums2)!=0 and len_nums1 > len_nums2:
            nums1, nums2 = nums2, nums1
            len_nums1, len_nums2 = len_nums2, len_nums1

        if len_nums1 == 0:
            return nums_median(nums2)
        if len_nums2 == 0:
            return nums_median(nums1)

        if len_nums1 <= len_nums2:
            index_nums2 = 0
            for index, num1 in enumerate(nums1):
                new_len_nums2 = len(nums2)
                while nums2[index_nums2] < num1 and index_nums2 - new_len_nums2 + 1:
                    index_nums2 += 1

                if num1 <= nums2[index_nums2]:
                    nums2.insert(index_nums2, num1)
                    index_nums2 += 1

                elif nums2[index_nums2] > num1:
                    nums2.insert(index_nums2, num1)
                    if index_nums2 - len_nums2:
                        index_nums2 += 1

                elif num1 >= nums2[-1]:  # 特殊情况nums2比nums1所有小
                    for num in nums1[index:]:
                        nums2.append(num)
                    return nums_median(nums2)

        return nums_median(nums2)

if __name__ == '__main__':
    s = Solution()
    while 1:
        nums1 = list(map(int, input().split()))
        nums2 = list(map(int, input().split()))
        print(s.findMedianSortedArrays(nums1, nums2))


import numpy as np
import numpy.testing as npt
import unittest
import wisdem.commonse.frustum as f
from wisdem.commonse import eps

myones = np.ones((100,))
rb = 4.0
rt = 2.0
t  = 0.1
h  = 3.0

class TestFrustum(unittest.TestCase):

    def testFrustumVol(self):

        V = np.pi/3*h * (rb**2 + rt**2 + rb * rt)

        # Test volume- scalar and vector inputs
        self.assertEqual(f.frustumVol(rb, rt, h, False), V)
        self.assertEqual(f.frustumVol(2*rb, 2*rt, h, True), V)
        npt.assert_equal(f.frustumVol(rb*myones, rt*myones, h*myones, False), V*myones)
        npt.assert_equal(f.frustumVol(2*rb*myones, 2*rt*myones, h*myones, True), V*myones)

    def testFrustumCG_solid(self):

        cg_solid = h/4 * (rb**2 + 3*rt**2 + 2*rb*rt) / (rb**2 + rt**2 + rb*rt)

        # Test cg of solid- scalar and vector inputs
        self.assertEqual(f.frustumCG(rb, rt, h, False), cg_solid)
        self.assertEqual(f.frustumCG(2*rb, 2*rt, h, True), cg_solid)
        npt.assert_equal(f.frustumCG(rb*myones, rt*myones, h*myones, False), cg_solid*myones)
        npt.assert_equal(f.frustumCG(2*rb*myones, 2*rt*myones, h*myones, True), cg_solid*myones)

    def testFrustum_shell(self):

        # In limit of thickness approaching radius, should recover regular formulas
        self.assertEqual(f.frustumShellVol(rb, rb, rb, h, False), f.frustumVol(rb, rb, h, False))
        self.assertEqual(f.frustumShellVol(2*rt, 2*rt, rt, h, True), f.frustumVol(rt, rt, h, False))

        self.assertEqual(f.frustumShellCG(rb, rb, rb, h, False), f.frustumCG(rb, rb, h, False))
        self.assertEqual(f.frustumShellCG(2*rt, 2*rt, rt, h, True), f.frustumCG(rt, rt, h, False))

        self.assertEqual(f.frustumShellIzz(rb, rb, rb, h, False), f.frustumIzz(rb, rb, h, False))
        self.assertEqual(f.frustumShellIzz(2*rt, 2*rt, rt, h, True), f.frustumIzz(rt, rt, h, False))

        self.assertAlmostEqual(f.frustumShellIxx(rb, rb, rb-eps, h, False), f.frustumIxx(rb, rb, h, False))
        self.assertAlmostEqual(f.frustumShellIxx(2*rt, 2*rt, rt-eps, h, True), f.frustumIxx(rt, rt, h, False))

def suite():
    suite = unittest.TestSuite()
    suite.addTest(unittest.makeSuite(TestFrustum))
    return suite

if __name__ == '__main__':
    unittest.TextTestRunner().run(suite())


import numpy as np
import matplotlib.pyplot as plt

entrada = np.array([[-0.4712, 1.7698], [0.1103, 3.1334], [2.0263,3.2474],
                   [1.5697, 0.7579], [1.7254, 4.0834], [2.2676, 0.4092],
                   [-0.4753, 1.1308], [3.2018, 3.1839], [2.0614,  1.6423],
                   [2.4969, 1.6099], [7.1547, 5.4719], [5.8240, 6.5220],
                   [7.0105, 8.9157], [6.3086, 6.0023], [6.1122, 6.2530],
                   [4.1822, 5.1714], [7.5074, 7.1391], [8.5628, 7.4580],
                   [6.9596, 5.4075], [6.7379, 10.1990], [2.1959, 7.1072],
                   [4.9906, 7.8603], [4.0592, 6.7196], [-0.3881, 6.8434],
                   [3.1318, 6.1018], [3.2421, 8.2583], [1.2560, 5.9102],
                   [2.8671, 5.8639], [1.8885, 5.8148], [1.0263, 7.9487],
                   [4.9782, 0.8005], [6.5436, 2.1400], [6.4685, 2.3265],
                   [7.8461, 1.3620], [6.6673, 2.8004], [6.8124, 2.7228],
                   [5.8382, 1.9450], [7.1404, 3.3512], [7.1251, 4.9571],
                   [4.7644, 2.3254],[7.5, 10.4], [-2.23, -1.522], [1.9, -7.2], [13.455, -5.0],[-3, -4],
                    [3.388, 5.239],[0.23, 0.1], [-1, 2.34]])

def plot(idx):
    idx = np.array(idx)
    plt.scatter(entrada[:, 0], entrada[:, 1], c = idx, s=100)
    plt.scatter(Centroide[:, 0], Centroide[:, 1], s=1000, c=np.array([i for i in range(len(Centroide))]), marker='*')
    plt.title('Centroides')
    plt.xlabel('Entrada X0')
    plt.ylabel('Entrada X1')
    plt.axis('equal')
    plt.pause(0.75)

def initList(size):
    mylist = list()
    for i in range(size):
        mylist.append( list() ) #different object reference each time
    return mylist

def init(NCentroides, NEntradas):
     idx = np.random.permutation(entrada.shape[0])[0:NCentroides]
     return entrada[idx, :]

def EuclideanDistance(Centroide, Entrada):
    distance = np.zeros((Entrada.shape[0], Centroide.shape[0]))
    for k in range(Centroide.shape[0]):
        for i in range(Entrada.shape[0]):
            for j in range(Entrada.shape[1]):
                distance[i, k] += (Entrada[i, j] - Centroide[k, j])**2
            distance[i, k] = np.sqrt(distance[i, k])
    return distance

def KMeans(Entrada, Centroide):

    oldC = np.zeros((Centroide.shape))
    count = 0
    while True:
        count += 1
        pointCentroides = initList(Centroide.shape[0])
        Distance = EuclideanDistance(Centroide, Entrada)
        Distance = list(Distance)
        idx = []
        for i in range(len(Distance)):
            Distance[i]= list(Distance[i])
        for i in range(Entrada.shape[0]):
            shortest = min(Distance[i])
            index = Distance[i].index(shortest)
            idx.append(index)
            pointCentroides[index].append(Entrada[i, :].tolist())
        nppc = [0]*len(pointCentroides)
        for x in range(len(pointCentroides)):
            nppc[x] = len(pointCentroides[x])
            pointCentroides[x] = np.array(pointCentroides[x])
            pointCentroides[x] = sum(pointCentroides[x])/len(pointCentroides[x])
            Centroide[x] = pointCentroides[x]
        if (Centroide == oldC).all():
            plot(idx)
            break
        else:
            plot(idx)
            plt.gcf().clear()
        oldC = np.copy(Centroide)
        print('Centroides finais: ', Centroide, '\nCount', count)
    plt.show()
    return Centroide, nppc

Centroide = init(6, entrada.shape[1])
KMeans(entrada, Centroide)

# nc = 15
# nmppc = int(0.1*entrada.shape[0])
# for i in range(nc, 0, -1):
#     Centroide = init(i, entrada.shape[1])
#     _, xxx = KMeans(entrada, Centroide)
#     if sum(list(map(lambda x: x >= nmppc, xxx))) == i:
#         print(i)
#         break


import pytest
import numpy as np
from pyquil.gate_matrices import X, Y, Z, H
from forest.benchmarking.operator_tools.random_operators import haar_rand_unitary
from forest.benchmarking.operator_tools.validate_operator import *

# Matrix below is from https://en.wikipedia.org/wiki/Normal_matrix
# it is normal but NOT unitary or Hermitian
NORMAL = np.array([[1, 1, 0], [0, 1, 1], [1, 0, 1]])

# not symmetric
SIGMA_MINUS = (X + 1j*Y)/2 # np.array([[0, 1], [0, 0]])

# idempotent
PROJ_ZERO = np.array([[1, 0], [0, 0]])

def test_is_square_matrix():
    assert is_square_matrix(np.eye(3))
    with pytest.raises(ValueError):
        is_square_matrix(np.ndarray(shape=(2, 2, 2)))
    assert not is_square_matrix(np.array([[1, 0]]))

def test_is_symmetric_matrix():
    assert is_symmetric_matrix(X)
    assert not is_symmetric_matrix(SIGMA_MINUS)
    with pytest.raises(ValueError):
        is_symmetric_matrix(np.ndarray(shape=(2, 2, 2)))
    with pytest.raises(ValueError):
        is_symmetric_matrix(np.array([[1, 0]]))

def test_is_identity_matrix():
    assert not is_identity_matrix(Z)
    assert is_identity_matrix(np.eye(3))
    with pytest.raises(ValueError):
        is_identity_matrix(np.ndarray(shape=(2, 2, 2)))
    with pytest.raises(ValueError):
        is_identity_matrix(np.array([[1, 0]]))

def test_is_idempotent_matrix():
    assert not is_idempotent_matrix(SIGMA_MINUS)
    assert is_idempotent_matrix(PROJ_ZERO)
    assert is_idempotent_matrix(np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]))

def test_is_normal_matrix():
    assert is_normal_matrix(NORMAL)
    assert not is_normal_matrix(SIGMA_MINUS)

def test_is_hermitian_matrix():
    assert not is_hermitian_matrix(NORMAL)
    assert is_hermitian_matrix(X)
    assert is_hermitian_matrix(Y)

def test_is_unitary_matrix():
    assert not is_unitary_matrix(NORMAL)
    assert is_unitary_matrix(Y)
    assert is_unitary_matrix(haar_rand_unitary(4))

def test_is_positive_definite_matrix():
    # not atol is = 1e-08
    assert not is_positive_definite_matrix(np.array([[-1e-08, 0], [0, 0.1]]))
    assert is_positive_definite_matrix(np.array([[0.5e-08, 0], [0, 0.1]]))

def test_is_positive_semidefinite_matrix():
    # not atol is = 1e-08
    assert not is_positive_semidefinite_matrix(np.array([[-1e-07, 0], [0, 0.1]]))
    assert is_positive_semidefinite_matrix(np.array([[-1e-08, 0], [0, 0.1]]))
    assert is_positive_semidefinite_matrix(np.array([[0.5e-08, 0], [0, 0.1]]))


import os
import numpy as np
import random
from constraint import *

def create_graph_pair(n,k):
	"""
		Starts with a disjoint set of n vertices (zero edges). Iteratively
		adds edges selected uniformly at random while the resulting graph
		admits a k-coloring. The penultimate and last of such graphs, which
		differ only by a single edge, are respectively k-colorable and not
		k-colorable.
	"""
	k_colorable = True
	M1 = np.zeros((n,n),dtype=int)
	M2 = np.zeros((n,n),dtype=int)
	while k_colorable:

		# Choose (and add) an edge uniformly at random
		a,b = random.choice([ (i,j) for i in range(n) for j in range(n) if i != j and M1[i,j]==0])
		M2[a,b] = 1

		# Create a CSP problem with n integer variables each of which can assume k different values ("colors")
		problem = Problem()
		problem.addVariables(range(n), range(k))
		for i in range(n):
			for j in range(n):
				if M2[i,j] == 1:
					# Add one constraint for each edge
					problem.addConstraint(lambda x,y: x != y, (i,j))
				#end
			#end
		#end

		# Check if k-colorable
		k_colorable = problem.getSolution() is not None

		if k_colorable:
			M1[a,b] = 1
		#end
	#end

	return M1, M2
#end

def write_graph(M, k, k_colorable, path):
	with open(path,"w") as out:
		out.write("p {} {} {} {}\n".format(M.shape[0], len(np.nonzero(M)[0]), k, k_colorable ))

		for (i,j) in zip(list(np.nonzero(M)[0]), list(np.nonzero(M))[1]):
			out.write("{} {}\n".format(i,j))
		#end
	#end
#end

def read_graph(path):
	with open(path,"r") as f:
		n, m, k, k_colorable = [ int(x) for x in f.readline().split()[1:]]
		M = np.zeros((n,n),dtype=int)
		for edge in range(m):
			i,j = [ int(x) for x in f.readline().split() ]
			M[i,j] = 1
		#end
	#end
	return M,k,k_colorable
#end

def create_dataset(n, samples = 1000, path="instances"):

	if not os.path.exists(path):
		os.makedirs(path)
	#end if

	for i in range(samples//2):
		# Select a random k ~ Bernouilli(0.3) + Geo(0.4)
		k = 3 #random.choice(range(2,5+1))
		print("Creating instances pair with k={}".format(k))
		M1, M2 = create_graph_pair(n,k)
		write_graph(M1,k,0,"{}/{}.graph".format(path,2*i))
		write_graph(M2,k,1,"{}/{}.graph".format(path,2*i+1))
	#end for
#end

if __name__ == '__main__':
	create_dataset(20, samples = 32*128, path = "train")
	create_dataset(20, samples = 32*128, path = "test")
#end


"""
Write a function validSolution/ValidateSolution/valid_solution() that accepts a 2D array representing a Sudoku board, and returns true if it is a valid solution, or false otherwise. The cells of the sudoku board may also contain 0's, which will represent empty cells. Boards containing one or more zeroes are considered to be invalid solutions.
The board is always 9 cells by 9 cells, and every cell only contains integers from 0 to 9.

(More info at: http://en.wikipedia.org/wiki/Sudoku)

"""
#import numpy as np

testing = [0]*20;
for i in range(20):
    testing[i] = False

# Using dict/hash-table
from collections import defaultdict
def valid_solution_hashtable(board):
    for i in range(len(board)):
        dict_row = defaultdict(int)
        dict_col = defaultdict(int)
        for j in range(len(board[0])):
            value_row = board[i][j]
            value_col = board[j][i]
            if not value_row or value_col == 0:
                return False
            if value_row in dict_row:
                return False
            else:
                dict_row[value_row] += 1

            if value_col in dict_col:
                return False
            else:
                dict_col[value_col] += 1

    for i in range(3):
        for j in range(3):
            grid_add = 0
            for k in range(3):
                for l in range(3):
                    grid_add += board[i*3+k][j*3+l]
            if grid_add != 45:
                return False
    return True

# Without hash-table/dict
def valid_solution(board):
    correct = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    # check rows
    for row in board:
        if sorted(row) != correct:
            return False

    # check columns
    for column in zip(*board):
        if sorted(column) != correct:
            return False

    # check regions
    for i in range(3):
        for j in range(3):
            region = []
            for line in board[i*3:(i+1)*3]:
                region += line[j*3:(j+1)*3]

            if sorted(region) != correct:
                return False

    # if everything correct
    return True

# Using set
def valid_solution_set (board):

    valid = set(range(1, 10))

    for row in board:
        testing[0] = True
        #If 1
        if set(row) != valid:
            testing[2] = True
            return False
        else:
            testing[3] = True;
    if row not in board:
        testing[1] = True
    for col in [[row[i] for row in board] for i in range(9)]:
        testing[4] = True
        #If 2
        if set(col) != valid:
            testing[6] = True
            return False
        else:
            testing[7] = True
    if col not in [[row[i] for row in board] for i in range(9)]:
        testing[5] = True
    for x in range(3):
        testing[8] = True
        for y in range(3):
            testing[10] = True
            #If 3
            if set(sum([row[x*3:(x+1)*3] for row in board[y*3:(y+1)*3]], [])) != valid:
                testing[12] = True
                return False
            else:
                testing[13] = True
        if y not in range(3):
            testing[11] = True
    if x not in range(3):
        testing[9] = True
    return True

def return_thingy(i):
    return testing[i]

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

breads = [
                    [5, 3, 4, 6, 7, 8, 9, 1, 2],
                    [6, 7, 2, 1, 9, 5, 3, 4, 8],
                    [1, 9, 8, 3, 4, 2, 5, 6, 7],
                    [8, 5, 9, 7, 6, 1, 4, 2, 3],
                    [4, 2, 6, 8, 5, 3, 7, 9, 1],
                    [7, 1, 3, 9, 2, 4, 8, 5, 6],
                    [9, 6, 1, 5, 3, 7, 2, 8, 4],
                    [2, 8, 7, 4, 1, 9, 6, 3, 5],
                    [3, 4, 5, 2, 8, 6, 1, 7, 9]
                ]

#test = valid_solution_set(breads)
#print(test)

#for x in breads:
#    print(x)

#print(bread[:,2])

#for i in range(9):
    #print(bread[:,i])


import numpy as np

class SoftmaxCrossEntropyLossLayer():
    def __init__(self, reduction="mean", parent=None):
        """

        :param reduction: mean reduction indicates the results should be summed and scaled by the size of the input (excluding the axis dimension).
            sum reduction means the results should be summed.
        """
        self.reduction = reduction
        # q, one_hot and targets from last forward computation
        self.q = None
        self.one_hot = None
        self.targets = None

    def forward(self, logits, targets, axis=-1) -> float:
        """

        :param logits: N-Dimensional non-softmaxed outputs. All dimensions (after removing the "axis" dimension) should have the same length as targets.
            Example: inputs might be (4 x 10), targets (4) and axis 1.
        :param targets: (N-1)-Dimensional class id integers.
        :param axis: Dimension over which to run the Softmax and compare labels.
        :return: single float of the loss.
        """
        # TODO
        self.targets = targets
        max_over_dims = np.amax(logits, axis=axis, keepdims=True) #(n x 1)
        logits -= max_over_dims
        exp_logits = np.exp(logits) # (n x D)
        sum_exp = np.sum(exp_logits, axis=axis, keepdims=True) #(n x 1)
        self.q = exp_logits / sum_exp # (n x D)
        log_sum_exp = np.log(sum_exp)
        log_q = logits - log_sum_exp #(n x D)

        self.one_hot = self._one_hot_encode(logits, targets, axis=axis)
        total_cross_entropy = self.one_hot * -log_q
        total_cross_entropy = np.sum(total_cross_entropy, axis=axis)

        if self.reduction == "mean":
            return np.mean(total_cross_entropy)
        elif self.reduction == "sum":
            return np.sum(total_cross_entropy)
        return 0

    def backward(self) -> np.ndarray:
        """
        Takes no inputs (should reuse computation from the forward pass)
        :return: gradients wrt the logits the same shape as the input logits
        """
        # TODO
        res = self.q - self.one_hot
        if self.reduction == "sum":
            return res
        elif self.reduction == "mean":
            return res / (self.targets.size)

    def _one_hot_encode(self, logits, targets, axis=-1):
        # dimension oblivious one-hot means dimension oblivious softmax
        idx = np.unravel_index(np.arange(targets.size), targets.shape)
        lidx = list(idx)
        if axis < 0:
            axis = len(logits.shape) + axis
        lidx.insert(axis, targets.flatten())
        lidx = np.array(lidx)
        res = np.zeros_like(logits)
        res[tuple(lidx)] = 1
        return res

if __name__ == "__main__":
    batch = 3
    classes = 10
    truth = np.array([8, 7, 0])
    fx = (np.array([-1.34340341, -0.45974003, -1.13929274, -0.70190898, -0.44352927,
        0.75805997,  0.57303502, -0.48544892, -0.84611373,  2.63468655,
       -0.89735092, -0.68660326, -0.17047781,  0.71524846, -1.53322612,
        1.51689624, -1.01533733, -0.34391119, -0.48589473, -0.11330478,
        0.39868253, -1.51340956,  0.66346402,  0.68727878,  2.15907718,
        1.19633974,  0.46431238, -1.61770077, -0.02563707,  0.70544018])
        .astype(np.float32)
        .reshape((batch, classes)))
    dut = SoftmaxCrossEntropyLossLayer()
    print(dut.forward(fx, truth))
    print(list(dut.q.flatten()))
    print(list(dut.backward().flatten()))


import numpy as np

            # zip Function
softmax_outputs = np.array([[ 0.7 , 0.1 , 0.2 ],
                            [ 0.1 , 0.5 , 0.4 ],
                            [ 0.02 , 0.9 , 0.08 ]])
class_target = [0,1,1]

print("Length : ",len(softmax_outputs))
print("Shape : ",softmax_outputs.shape)

for indx, distribution in  zip(class_target, softmax_outputs):
    print(distribution[indx])

print(softmax_outputs[[0,1,2], class_target])   #0,1,2 define the row
b=softmax_outputs[range(len(softmax_outputs)), class_target]
print("here We are", b)  #size of array can also work
# print(softmax_outputs[range(0,3), class_target])

''' Now apply Cross entropy on this values'''
print("Final CE", -np.log(softmax_outputs[range(len(softmax_outputs)), class_target]))

cal = -np.log(softmax_outputs[range(len(softmax_outputs)), class_target])
average_loss = np.mean(cal)
print(average_loss)

''' What if class target have 3*3 dimension instead of 1'''
softmax_outputs = np.array([[ 0.7 , 0.1 , 0.2 ],
                            [ 0.1 , 0.5 , 0.4 ],
                            [ 0.02 , 0.9 , 0.08 ]])

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

output = np.sum(softmax_outputs*class_target, axis=1)
print('average_loss', np.mean(-np.log(output)))
# print(output)


import matplotlib.pyplot as plt
import numpy as np

""" a = np.float64(5)
i = np.float64(1)
while True:
   b = np.float64(a + 10**(-i))
   print("b:%.16f, a:%.16f, i:%.0f "%(b,a,i));
   if a == b:
      break
   i += 1 """

"""
funcion  X(t) = 1.7  y dejando el función de w
"""

###Funciones auxiliares

#funcion a resolver
g = 32.17
t = 1
def  func(x):
  value = (3.4/g)*x**2 + 0.5*(np.exp(x*t) - np.exp(-x*t)) - np.sin(x*t)
  return value

def sign(x):
  if x > 0:
    return 1
  elif x < 0:
    return -1
  else:
    return 0

###Metodo

#longitud intervalo
ai = -1.0
bi = 1.5

#Tolerancia
Tol = 1.0e-5

#Error inicial
error  = 1.0

#Nmax de interaciones
Nmax = 1000
iteracion = 0

#Verificacion del valor medio

if (sign(func(ai))* sign(func(bi))<0):
    while(error > Tol):
        pi = 0.5*(ai+bi)
        pi_previo = pi

        #redefinimos los limites del intervalo
        #if(abs(func(pi)) < Tol):
          # print("La raiz del polinomio es %.5f"%(pi))
        #else:
        if(sign(func(pi)) == sign(func(ai))):
            ai = pi
            bi = bi
        elif(sign(func(pi)) == sign(func(bi))):
            ai = ai
            bi = pi

        pi = 0.5*(ai+bi)
        error = abs(pi - pi_previo)
        iteracion = iteracion+1

        print("pi=%f\t ai=%f\t f(pi)=% f\t error=%f"%(pi,ai,func(pi),error))

        if(iteracion > Nmax):
            print("Numero de iteraciones excedido")
            break
else:
    print("Intervalo inadecuado")

print("Cero de F(x)  = %f , F(pi)= %.5f"%(pi, func(pi)))

"""
x1 = np.linspace(-1.5,1.5)
y1  = []

for i in range(0,len(x1)):
  y1.append(func(x1[i]))

fig = plt.figure()
ax = plt.axes()
ax.plot(x1, y1, label='Datos Discretos')
#ax.plot(x_interpol, y_interpol,'.', label='Puntos interpolados')
#ax.plot(x_interpol, np.sin(x_interpol), label='sin(x)')
ax.legend()
plt.show()
"""


from pyccel.decorators import types

#==============================================================================
@types('double[:]','int','double')
def find_span( knots, degree, x ):
    """
    Determine the knot span index at location x, given the
    B-Splines' knot sequence and polynomial degree. See
    Algorithm A2.1 in [1].

    For a degree p, the knot span index i identifies the
    indices [i-p:i] of all p+1 non-zero basis functions at a
    given location x.

    Parameters
    ----------
    knots : array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    x : float
        Location of interest.

    Returns
    -------
    span : int
        Knot span index.

    """
    # Knot index at left/right boundary
    low  = degree
    high = 0
    high = len(knots)-1-degree

    # Check if point is exactly on left/right boundary, or outside domain
    if x <= knots[low ]: returnVal = low
    elif x >= knots[high]: returnVal = high-1
    else:
        # Perform binary search
        span = (low+high)//2
        while x < knots[span] or x >= knots[span+1]:
            if x < knots[span]:
               high = span
            else:
               low  = span
            span = (low+high)//2
        returnVal = span

    return returnVal

#==============================================================================
@types('double[:]','int','double','int','double[:]')
def basis_funs( knots, degree, x, span, values ):
    """
    Compute the non-vanishing B-splines at location x,
    given the knot sequence, polynomial degree and knot
    span. See Algorithm A2.2 in [1].

    Parameters
    ----------
    knots : array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    x : float
        Evaluation point.

    span : int
        Knot span index.

    Results
    -------
    values : numpy.ndarray
        Values of p+1 non-vanishing B-Splines at location x.

    Notes
    -----
    The original Algorithm A2.2 in The NURBS Book [1] is here
    slightly improved by using 'left' and 'right' temporary
    arrays that are one element shorter.

    """
    from numpy      import empty
    left   = empty( degree  , dtype=float )
    right  = empty( degree  , dtype=float )

    values[0] = 1.0
    for j in range(0,degree):
        left [j] = x - knots[span-j]
        right[j] = knots[span+1+j] - x
        saved    = 0.0
        for r in range(0,j+1):
            temp      = values[r] / (right[r] + left[j-r])
            values[r] = saved + right[r] * temp
            saved     = left[j-r] * temp
        values[j+1] = saved


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.graph_objs as go

pd.options.display.max_columns = 5
pd.options.display.max_rows = 5

data = pd.read_csv("D:\Masaüstüm\Projects\PythonProjects\Regression Types\Linear Regression\FuelConsumption.csv")

# %% Take a look at the data

data.head(10)

"""
    MODELYEAR   MAKE  ... FUELCONSUMPTION_COMB_MPG CO2EMISSIONS
0        2014  ACURA  ...                       33          196
1        2014  ACURA  ...                       29          221
..        ...    ...  ...                      ...          ...
8        2014  ACURA  ...                       24          267
9        2014  ACURA  ...                       31          212
"""

data.columns
"""
Index(['MODELYEAR', 'MAKE', 'MODEL', 'VEHICLECLASS', 'ENGINESIZE', 'CYLINDERS',
       'TRANSMISSION', 'FUELTYPE', 'FUELCONSUMPTION_CITY',
       'FUELCONSUMPTION_HWY', 'FUELCONSUMPTION_COMB',
       'FUELCONSUMPTION_COMB_MPG', 'CO2EMISSIONS'],
      dtype='object')
"""

data.describe()
"""
       MODELYEAR   ENGINESIZE  ...  FUELCONSUMPTION_COMB_MPG  CO2EMISSIONS
count     1067.0  1067.000000  ...               1067.000000   1067.000000
mean      2014.0     3.346298  ...                 26.441425    256.228679
         ...          ...  ...                       ...           ...
75%       2014.0     4.300000  ...                 31.000000    294.000000
max       2014.0     8.400000  ...                 60.000000    488.000000
"""

data.isna().sum()
#Detect missing values.
"""
MODELYEAR                   0
MAKE                        0
MODEL                       0
VEHICLECLASS                0
ENGINESIZE                  0
CYLINDERS                   0
TRANSMISSION                0
FUELTYPE                    0
FUELCONSUMPTION_CITY        0
FUELCONSUMPTION_HWY         0
FUELCONSUMPTION_COMB        0
FUELCONSUMPTION_COMB_MPG    0
CO2EMISSIONS                0
dtype: int64
"""
# %%

n_data = data[['ENGINESIZE','CYLINDERS','FUELCONSUMPTION_COMB','CO2EMISSIONS']]
n_normalized = (n_data - np.min(n_data)) / (np.max(n_data) - np.min(n_data)).values

for each in n_data:
    g = n_data[[each]]
    g.hist()
    plt.show()

# We can plot each of these vs to find a relation.
g = sns.PairGrid(n_normalized)
g.map(sns.regplot)

# Correlation
sns.heatmap(n_normalized.corr(), annot = True, fmt = ".2f")
plt.show()

# %% train test split
from sklearn.model_selection import train_test_split

n_train, n_test = train_test_split(n_normalized,test_size = 0.21, random_state = 42)

# %% Regression
from sklearn.linear_model import LinearRegression
LinearReg = LinearRegression()

n_train_engine = np.asanyarray(n_train[['ENGINESIZE']])
n_train_emission = np.asanyarray(n_train[['CO2EMISSIONS']])

LinearReg.fit(n_train_engine, n_train_emission)
LinearReg.coef_
"""
LinearReg.coef_
Out[77]: array([[0.7577476]])
"""

LinearReg.intercept_
"""
LinearReg.intercept_
Out[78]: array([0.15048711])
"""

plt.scatter(n_train.ENGINESIZE, n_train.CO2EMISSIONS,  color='red')
plt.plot(n_train_engine, LinearReg.coef_[0][0]*n_train_engine + LinearReg.intercept_[0], '-r')
plt.xlabel("Engine size")
plt.ylabel("Emission")

# %% Evaluate
from sklearn.metrics import r2_score

n_test_engine = np.asanyarray(n_test[['ENGINESIZE']])
n_test_emission = np.asanyarray(n_test[['CO2EMISSIONS']])

n_test_predict = LinearReg.predict(n_test_engine)

print("Mean absolute error: %.2f" % np.mean(np.absolute(n_test_predict - n_test_engine)))
print("Residual sum of squares (MSE): %.2f" % np.mean((n_test_predict - n_test_engine) ** 2))
print("R2-score: %.2f" % r2_score(n_test_engine , n_test_predict) )

"""
Mean absolute error: 0.08
Residual sum of squares (MSE): 0.01
R2-score: 0.79
"""


#Nessas linhas de codigo o objetivo é calcular a taxa media de retorno anual para um portfolio

import numpy as np
import pandas as pd
from pandas_datareader import data as wb
import matplotlib.pyplot as plt

tickers = ['AAPL', 'MSFT', 'PG', 'GE']

new_data = pd.DataFrame()

for t in tickers:
    new_data[t] = wb.DataReader(t, data_source='yahoo', start='1995-1-1')['Adj Close']

#Vamos criar um grafico de linhas para entender como essas açoes se comportaram em todo o periodo analisado.
#Para normalizar os dados na base 100 podemos fazer P1/P0 * 100
#O indexador .iloc[0] ira extrair os dados da primeira coluna da tabela

(new_data / new_data.iloc[0] * 100).plot(figsize=(15, 6))
#Fazendo isso, esses calculos vao normalizar o grafico com 100 como parametro, todas iniciando no mesmo ponto vertical 100

#O comando .loc{rotulo} o rotulo é para indicar a primeira linha, as datas sao rotulos
#Para usar o .iloc[numero da linha], usa-se o numero da linha que se deseja
#Pode-se usar os dois, depende da praticidade que o programador achar
plt.show()

#Tem como objetivo analisar o historico das acoes e tentar prever o futuro, uma negociacao lucrativa

#Iremos calcular o retorno simples dessas açoes e criar uma nova tabela com eles
#Usa-se mais retornos simples para negociaçoes num mesmo periodo de tempo
#Utiliza-se a funcao numpy
returns = (new_data / new_data.shift(1)) - 1
returns.head()

weights = np.array([0.25, 0.25, 0.25, 0.25])
#Estamos atribuindo que os pesos das açoes sao iguais, e sempre os pesos devem somar 1
#Utilizando o metodo np.dot, podemos calcular o produto vetorial ou de matrizes de forma rapida
#Em python a matriz unidimensional ou multidimensional que obtivemos é referida como o produto dot entre as variaveis
#Fazendo isso iremos multiplicar o peso de cada açao com o seu respectivo retorno
#Fazendo isso obtem-se o retorno diario da carteira, e não é isso que desejamos no momento
#Podemos calcular o anual como
annual_returns = returns.mean() * 250

np.dot(annual_returns, weights)

pfolio1 = str(round(np.dot(annual_returns, weights), 5) * 100) + '%'
print(pfolio1)

weights2 = np.array([0.4, 0.4, 0.15, 0.05])

pfolio2 = str(round(np.dot(annual_returns, weights2), 5) * 100) + '%'

print(pfolio2)