import numpy as np
from si.data.Dataset import Dataset
from si.supervised.Model import Model
from si.util.Metrics import mse

class LinearRegression(Model):

    def __init__(self, gd = False, epochs = 1000, lr = 0.001):
        super(LinearRegression, self).__init__()
        self.gd = gd
        self.theta = None
        self.num_inter = epochs
        self.lr = lr

    def fit (self, dataset):
        X, Y = dataset.getXy()
        X = np.hstack((np.ones((X.shape[0], 1)), X))
        self.X = X
        self.Y = Y

        # closed from of GD
        if self.gd:
            self.train_gd(X, Y)
        else:
             self.train_closed(X, Y)
        self.is_fitted = True

    def train_closed(self, x, y):
        self.theta = np.linalg.inv(x.T.dot(x)).dot(x.T).dot(y)

    def train_gd(self, x, y):
        m = x.shape[0]
        n = x.shape[1]
        self.history = {} # dicionário com o histórico dos thetas e custo por epoch
        self.theta = np.zeros(n)
        for epoch in range(self.num_inter):
            grad = 1 / m * (x.dot(self.theta) - y).dot(x)
            self.theta -= self.lr * grad
            self.history[epoch] = [self.theta[:], self.cost()]

    def predict(self, x):
        assert self.is_fitted, 'Model must be fitted before predicting'
        _x = np.hstack(([1], x))
        return np.dot(self.theta, _x)

    def cost(self):
        y_pred = np.dot(self.X, self.theta)
        return mse(self.Y, y_pred) / 2

class LinearRegressionReg(LinearRegression):

    def __init__(self, gd = False, epochs = 1000, lr = 0.001, lbd = 1):
        super(LinearRegressionReg, self).__init__()
        self.lbd = lbd
        self.gd = gd
        self.num_inter = epochs

    def train_closed(self, x, y):
        n = x.shape[1]
        identify = np.eye(n)
        identify[0, 0] = 0 # mudar o primeiro elemento para 0 para não dar biased
        self.theta = np.linalg.inv(x.T.dot(x) + self.lbd * identify).dot(x.T).dot(y)
        self.is_fitted = True

    def train(self, x, y):
        m = x.shape[0]
        n = x.shape[1]
        self.history = {} # dicionário com o histórico dos thetas por epoch
        self.theta = np.zeros(n)
        lbds = np.full(m, self. lbd)
        lbds[0] = 0
        for epoch in range(self.num_inter):
            grad = 1 / m * (x.dot(self.theta) - y).dot(x)
            self.theta -= (self.lr / m) * (lbds + grad)
            self.history[epoch] = [self.theta[:], self.cost()]

    def predict(self, x):
        assert self.is_fitted
        _x = np.hstack(([1], x))
        return np.dot(self.theta, _x)

    def cost(self):
        y_pred = np.dot(self.X, self.theta)
        return mse(self.Y, y_pred) / 2


import numpy as np
import math

def main():

    """
    Operations:
    - vector addition
    - scalar multiplication
    - dot product
    - the cross product
    - the angle between two vectors:
    """

    vector_row = np.array([[1, 2, 3, 4]])
    vector_column = np.array([[1], [2], [3], [4]])

    assert vector_row.shape == (1, 4)
    assert vector_column.shape == (4, 1)

    assert (vector_row.T == vector_column).all()
    assert (vector_column.T == vector_row).all()

    assert np.linalg.norm(vector_row, 1) == 4.0
    assert math.isclose(np.linalg.norm(vector_row, 2), 5.5, rel_tol=1e-2)
    assert np.linalg.norm(vector_row, np.inf) == 10.0

    v = np.array([[9, 2, 5]])
    w = np.array([[-3, 8, 2]])
    theta = np.arccos(np.dot(v, w.T) / (np.linalg.norm(v) * np.linalg.norm(w)))

    assert math.isclose(theta[0][0], 1.582, rel_tol=1e-2)
    assert (np.cross(v, w) == [[-36, -33, 78]]).all()

if __name__ == "__main__":
    main()


import numpy as np

s1 = np.array([168, 398, 451, 337, 186, 232, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258,
242, 331, 251, 323, 106, 1055, 170])

s2 = np.array([168, 400, 451, 300, 186, 200, 262, 349, 189, 204, 220, 220, 207, 239, 259, 258,
242, 331, 251, 180, 106, 1055, 200])

#cálculo da distância euclidiana
distancia = np.linalg.norm(s1 - s2)

#média pode ser calculada dessa forma, manipulando as matrizes unidimensionais separadamente
med = (s1 + s2)/2

#Ou criando uma matriz 2X1 e criando a média das linhas para cada elemento
s3 = np.array([s1,s2])

media = np.average(s3, axis=0)

#Compara as matrizes e retorna com o máximo
max = np.maximum(s1,s2)

#Compara as matrizes e retorna com mínimo
min = np.minimum(s1,s2)

print(distancia, '\n', media,'\n', med, '\n', max, '\n', min)


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

# Function
def f(x):
    return exp(x)

# Compute Derivative of f(x) at x0 with step h
def ThreeMidpoint(x0, h):
    df = (f(x0+h)-f(x0-h))/(2*h)
    return df

if __name__ == '__main__':

    x = 0

    print(ThreeMidpoint(x, 0.01))


# Monte Carlo

import math
import numpy as np

S0 = 100.
K = 105.
T = 1.0
r = 0.05
sigma = 0.2

I = 100000

np.random.seed(1000)

z = np.random.standard_normal(I)

ST = S0 * np.exp((r - sigma ** 2 / 2) * T + sigma + math.sqrt(T) * z)
hT = np.maximum( ST - K, 0)
C0 = math.exp(-r * T) * np.mean(hT)

print('Value of the European Call Option: {:5.3f}.'.format(C0))


import numpy as np
import matplotlib.pyplot as plt

def assign_to_clusters(points, centers):
    num_clusters = len(centers)
    clusters = [[] for _ in range(num_clusters)]

    for x in points:

        best_cluster = np.array([np.linalg.norm(x - c) for c in centers]).argmin()

        clusters[best_cluster].append(x)

    return clusters

def compute_centers(clusters):
    return [sum(c) / len(c) for c in clusters]

def objective(clusters, centers):
    return sum(
        sum(np.linalg.norm(x - center) for x in cluster) \
                for center, cluster in zip(centers, clusters)
        )

def kmeans(points, num_clusters):
    centers = np.random.uniform(size=(num_clusters, 2))

    obj_prev, obj = np.inf, np.inf

    time = 0

    while True:
        time += 1

        clusters = assign_to_clusters(points, centers)
        centers = compute_centers(clusters)
        obj = objective(clusters, centers)

        if obj_prev == obj:
            break

        obj_prev = obj

    return assign_to_clusters(points, centers), centers, time

if __name__ == "__main__":
    num_clusters = 9
    num_points = 4000

    np.random.seed(0)

    # generate "true" cluster centers
    #centers = np.random.uniform(size=(num_clusters, 2), low=-5, high=5)

    # generate guassian clusters, points evenly distributed among
    # them
    #clusters = tuple(np.random.normal(loc=c, scale=0.375, size=(num_points // num_clusters, 2)) for c in centers)
    #points = np.concatenate(clusters)
    points = np.random.uniform(low=-1, high=1, size=(num_points, 2))

    clusters, centers, time = kmeans(points, num_clusters)

    print(f"k-means converged in {time} iterations")

    fig, ax = plt.subplots(1)
    for i, c in enumerate(map(np.array, clusters)):
        ax.scatter(c[:, 0], c[:, 1])
        ax.plot([centers[i][0]], [centers[i][1]], color="black", marker="o", markersize=10)

    plt.show()


import math
import numpy as np

""" The formula for sigmoid is S(x) = 1/(1+exp(-x))"""

def basic_sigmoid(x):
    return 1/(1+math.exp(-x))

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

def sigmoid_derivative(x):
    a = sigmoid(x)
    return a*(1-a)


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

def temperature_hist():
    nbin = 10
    bin_min = 36.05
    bin_max = 38.05
    bin_width = (bin_max - bin_min) / nbin
    vy = []
    with open("temperature.txt") as f:
        for line in f:
            vy.append(float(line.split()[0]))

    N = len(vy)
    mean = np.mean(vy)
    stdev = np.std(vy)

    label="Entries {0}\nMean {1:.2f}\nStd Dev {2:.4f}".format(N,mean,stdev)
    content = plt.hist(vy, bins=nbin, range=[
                       bin_min, bin_max], histtype="step", edgecolor='k',label=label)

    xx = np.linspace(bin_min, bin_max, 500)
    plt.plot(xx, norm.pdf(xx, mean, stdev)*N*bin_width, color="red")
    plt.xlim(bin_min, bin_max)
    plt.xlabel("Body temperature[$^\\circ$C]")
    plt.ylabel("Frequency")

    chi2 = 0
    for i in range(nbin):
        observed = content[0][i]
        center = (content[1][i]+content[1][i+1])*0.5
        if observed == 0:
            continue
        expected = norm.pdf(center, mean, stdev)*(N*bin_width)

        print("{:.2f} {:.4f} {:.0f}".format(center, expected, observed))
        chi2 += (observed - expected) * (observed - expected) / expected

    print("N    :{}".format(N))
    print("mean :{:.4f}".format(mean))
    print("stdev:{:.4f}".format(stdev))
    print("chi2 :{:.4f}".format(chi2))

    plt.legend()
    plt.show()

if __name__ == "__main__":
    temperature_hist()


# Program 03b: Nonlinear system, phase portrait with vector plot.
# See Figure 3.12.

import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint
import pylab as pl

# The 2-dimensional nonlinear system.
def dx_dt(x, t):
    return [x[1], x[0] * (1 - x[0]**2) + x[1]]

# Trajectories in forward time.
ts = np.linspace(0, 10, 500)
ic = np.linspace(-3, 3, 6)
for r in ic:
    for s in ic:
        x0 = [r, s]
        xs = odeint(dx_dt, x0, ts)
        plt.plot(xs[:, 0], xs[:, 1], 'r-')

# Trajectories in backward time.
ts = np.linspace(0, -10, 500)
ic = np.linspace(-3, 3, 6)
for r in ic:
    for s in ic:
        x0 = [r, s]
        xs = odeint(dx_dt, x0, ts)
        plt.plot(xs[:, 0], xs[:, 1], 'r-')

# Label the axes and set fontsizes.
plt.xlabel('x', fontsize=15)
plt.ylabel('y', fontsize=15)
plt.tick_params(labelsize=15)
plt.xlim(-3, 3)
plt.ylim(-3, 3)

# Plot the vectorfield.
X, Y = np.mgrid[-3:3:20j, -3:3:20j]
u = X
v = X * (1 - X**2) + Y
pl.quiver(X, Y, u, v, color='b')

plt.show()


# -*- coding: utf-8 -*-
"Compound interest function"
import numpy as np

def compound_interest(principal: float, annual_rate: float, years: int):
    """Calculates compound interest
    Solves A = P(1+r)^t for
    P = principal
    r = annual rate
    t = number of years to compound

    Args:
        principal (float): The principal (initial) value.
        annual_rate (float): The annual interest rate.
        years (int): Number of years to calculate.

    Returns:
        an array giving the new principal for each year in the sequence.
    """
    return np.array([principal * ((1 + annual_rate) ** yr) for yr in range(years)])


# Code made for Sergio Andrés Díaz Ariza
# 24 Abril 2021
# License MIT
# Transport Phenomena: Python Program-Assessment 2.1

import time
import numpy as np

start_time = time.time()

# Matrix for Pi's Buckingham
A = np.array([[0, 1, 1, 1], [0, -3, -1, 0], [0, -1, 0, 0], [1, 1, -1, -3]])

# We solve for the first Pi
B1 = np.array([-1, 3, 1, 0])
C1 = np.linalg.solve(A, B1)

# We solve for the second Pi
B2 = np.array([0, 2, 1, -2])
C2 = np.linalg.solve(A, B2)

# We solve for the third Pi
B3 = np.array([0, 0, 0, -1])
C3 = np.linalg.solve(A, B3)

# We solve for the fourth Pi
B4 = np.array([0, 1, 0, 0])
C4 = np.linalg.solve(A, B4)

# We solve for the fifth Pi
B5 = np.array([-1, 1, 0, 1])
C5 = np.linalg.solve(A, B5)

print(C1)
print(C2)
print(C3)
print(C4)
print(C5)

print("\n--- %s seconds ---" % (time.time() - start_time))


import numpy as np

class GroundTruthDoublePendulum:
    """
    A class to represent a ground truth representation of a double pendulum
    and all of its system properties
    """

    def __init__(self, m_matrix, l_vec):
        self.m = m_matrix
        self.lengths = l_vec
        self.g = 9.81
        self.state_hist = np.empty((0, 4))  # input
        self.state_dot_hist = np.empty((0, 4))  # label

    def dynamics(self, state):
        """
        :param state: [[theta1],
                       [theta2],
                       [theta1_dot],
                       [theta2_dot]]
               t: time_value (s)
        :return state_dot:
        """
        self.state_hist = np.vstack((self.state_hist, state))

        state_dot = np.zeros(4)

        c = np.cos(state[0] - state[1])  # intermediate variables
        s = np.sin(state[0] - state[1])  # intermediate variables

        state_dot[0] = state[2]  # d(theta 1)
        state_dot[1] = state[3]  # d(theta 2)
        state_dot[2] = (self.m[1] * self.g * np.sin(state[1]) * c - self.m[1] * s * (self.lengths[0] * c * state[2] ** 2 + self.lengths[1] * state[3] ** 2)
                        - (self.m[0] + self.m[1]) * self.g * np.sin(state[0])) / (self.lengths[0] * (self.m[0] + self.m[1] * s ** 2))
        state_dot[3] = ((self.m[0] + self.m[1]) * (self.lengths[0] * state[2] ** 2 * s - self.g * np.sin(state[1]) + self.g * np.sin(state[0]) * c)
                        + self.m[1] * self.lengths[1] * state[3] ** 2 * s * c) / (self.lengths[1] * (self.m[0] + self.m[1] * s ** 2))

        self.state_dot_hist = np.vstack((self.state_dot_hist, state_dot))

        return state_dot

    def polar_to_cartesian(self, theta1, theta2):
        # Mapping from polar to Cartesian
        x1 = self.lengths[0] * np.sin(theta1)  # First Pendulum
        y1 = -self.lengths[0] * np.cos(theta1)

        x2 = x1 + self.lengths[1] * np.sin(theta2)  # Second Pendulum
        y2 = y1 - self.lengths[1] * np.cos(theta2)

        return zip(x1, y1, x2, y2)

    def lagrangian(self, state):
        """
        :param state: [[theta1],
                       [theta2],
                       [theta1_dot],
                       [theta2_dot]]
        :return lagrangian:
        """

        link1_vel = (self.lengths[0]**2)*(state[2]**2)
        T_kinetic_link1 = 1/2*self.m[0]*link1_vel
        link2_vel = link1_vel + (self.lengths[1]**2)*(state[3]**2) \
                    + 2*self.lengths[0]*self.lengths[1]*state[2]*state[3]*np.cos(state[0]-state[1])
        T_kinetic_link2 = 1/2*self.m[1]*link2_vel

        T_kinetic = T_kinetic_link1 + T_kinetic_link2

        V_potential = -(self.m[0] + self.m[1])*self.g*self.lengths[0]*np.cos(state[0]) \
                      - self.m[1]*self.g*self.lengths[1]*np.cos(state[1])

        lagrangian = T_kinetic + V_potential

        return lagrangian


import numpy as np
from matplotlib import pyplot as plt
from matplotlib import ticker

def f(x, y):
    return (1 - x) ** 2 + 100 * (y - x * x) ** 2

def grad(x, y):
    return np.matrix([[2 * x - 2 + 400 * x * (x * x - y)],
                      [200 * (y - x * x)]])

def H(x, y):
    return np.matrix([[1200 * x * x - 400 * y + 2, -400 * x],
                      [-400 * x, 200]])

def gradient_descent_delta(x, y):
    return 1e-3 * grad(x, y)

def newton_delta(x, y):
    return H(x, y).I * grad(x, y)

def optimize(delta_func):
    step_count = 20000
    count = 0
    eps = 1e-8

    x = np.mat([-1, 1]).T
    x_list, y_list = [x[0, 0]], [x[1, 0]]
    while count < step_count:
        delta = delta_func(x[0, 0], x[1, 0])
        if np.linalg.norm(delta) < eps:
            break
        x = x - delta
        x_list.append(x[0, 0])
        y_list.append(x[1, 0])
        count += 1
    print(f'round = {count}')
    return x_list, y_list

if __name__ == '__main__':
    for name, func in zip(['./data/1.jpg', './data/2.jpg'], [gradient_descent_delta, newton_delta]):
        n = 1000
        x = np.linspace(-1, 1.1, n)
        y = np.linspace(-1, 1.1, n)
        X, Y = np.meshgrid(x, y)
        plt.figure()
        plt.contourf(X, Y, f(X, Y), 5, alpha=0, cmap=plt.cm.hot)
        plt.clabel(plt.contour(X, Y, f(X, Y), 8, locator=ticker.LogLocator(),
                               colors='black'), inline=True, fontsize=10)

        x_list, y_list = optimize(func)
        plt.plot(x_list, y_list)
        plt.savefig(name)
        print(f'({x_list[-1]:.3f}, {y_list[-1]:.3f})')


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as st
from matplotlib import style
from scipy.stats import multivariate_normal

sns.set_style('whitegrid')

# Metropolis - (Hastings) - Algorithm
def pgauss(x, y, mean, cov):
    """Returns density of bivariate normal with mean=mean and cov=cov."""
    return st.multivariate_normal.pdf([x, y], mean=mean, cov=cov)

def metropolis_hastings(p, nsamples):
    """Returns nsamples of density p using a gaussian proposal and the metropolis algorithm."""
    x, y = -1., -1. # starting values
    samples = np.zeros((nsamples+1, 2))
    samples[0, :] = np.array([x, y])

    for t in range(0, nsamples):
        x_proposal, y_proposal = np.array([x, y]) + np.random.normal(size=2)
        u = np.random.rand()
        A = p(x_proposal, y_proposal) / p(x, y)
        if u < A: # accept with probability min(1, A)
            x, y = x_proposal, y_proposal
        samples[t+1] = np.array([x, y])

    return samples

if __name__ == "__main__":
    # set seed
    np.random.seed(3)

    # location and scale parameters
    mean = np.array([2, 2])
    cov = 0.5 * np.array([[1, 0.5], [0.5, 1]])

    # construct Markov chain
    def ppgauss(x, y):
        return pgauss(x, y, mean, cov)
    mc_samples = metropolis_hastings(ppgauss, nsamples=500)

    # sampling points
    nsamples = 5000
    data = np.random.multivariate_normal(mean, cov, nsamples)

    # construct contour data
    x = np.linspace(-2,5,500)
    y = np.linspace(-2,5,500)
    X,Y = np.meshgrid(x,y)

    pos = np.array([X.flatten(),Y.flatten()]).T
    rv = multivariate_normal(mean, cov)

    # figsize
    plt.figure(figsize=(20, 12))

    # plot samples
    ax = sns.scatterplot(x=data[:,0], y=data[:, 1],
                         alpha=0.4)

    # plotting contours
    ax.contour(x, y, rv.pdf(pos).reshape(500,500),
               levels=[0.03, 0.15, 0.3],
               linewidths=2.5, colors=['black', 'grey', 'darkgray'])

    # ticks label size
    ax.tick_params(axis='both', which='major', labelsize=22)

    # coordinate system settings
    plt.axhline(0, color='black', linewidth=1.)
    plt.axvline(0, color='black', linewidth=1.)
    plt.ylim(-1.2, 4.5)
    plt.xlim(-1.2, 3.8)
    ax.set(frame_on=False)

    # save plot
    plt.savefig("../graphics/toy-mcmc.pdf", bbox_inches='tight', pad_inches=0, transparent=True)

    # add first 10 Markov chain samples
    plt.plot(mc_samples[:10,0],mc_samples[:10, 1], color='brown', marker='o', linewidth=2.25)

    # save plot
    plt.savefig("../graphics/toy-mcmc-with-samples-1.pdf", bbox_inches='tight', pad_inches=0, transparent=True)

    # add first 70 Markov chain samples
    plt.plot(mc_samples[10:70,0],mc_samples[10:70, 1], color='brown', marker='o', linewidth=2.25)

    # save plot
    plt.savefig("../graphics/toy-mcmc-with-samples-2.pdf", bbox_inches='tight', pad_inches=0, transparent=True)

    # add remaining Markov chain samples
    plt.plot(mc_samples[70:,0],mc_samples[70:, 1], color='brown', marker='o', linewidth=2.25)

    # save plot
    plt.savefig("../graphics/toy-mcmc-with-all-samples.pdf", bbox_inches='tight', pad_inches=0, transparent=True)


from numpy import square, sqrt
from scipy.stats import t
from statistics import mean as mn
from statistics import stdev as stdev

class Grubbs:
    def __init__(self, values: list, samples: list = False, remove: bool = False, alpha: float = 0.05):
        """Recieves a list of values used in graph and identify outliers

        Parameters
        ----------
        values : list
            List of values from individual values for graph/statistics
        samples: list
            List with the samples' names to be used as index
        Returns
        -------
        list
            List with new values removing outliers, or indexes to be removed from main table (?)
        """
        self.values = values
        self.alpha = alpha
        crit = self.z_crit(len(values), alpha)
        results, mean, std, crit = self.z_score(values, crit)

        self.mean = "Mean: {}".format(mean)
        self.std = "Standard deviation: {}".format(std)
        self.critical = "Critical Value(λ): {}".format(crit)

        if samples:
            results["samples"] = samples
        else:
            pass

        if remove:
            rmvd = [x if x is not False and y == "No" else "-"
                    for x, y in zip(results["values"], results["outlier"])]
            if "-" in rmvd:
                rmvd.remove("-")
            else:
                pass

            self.keeps = rmvd
        else:
            pass

        self.results = results

        if results["outlier"].count("Yes") == 0:
            self.outliers = "There are no outliers between all samples"
        elif results["outlier"].count("Yes") == 1:
            self.outliers = "There is 1 outlier between all samples"
        else:
            self.outliers = "There are {} outliers between all samples" \
                .format(results["outlier"].count("Yes"))
        pass

    @staticmethod
    def z_crit(n: int, alpha: float) -> float:
        """ Gets both degress of freedom and alpha selected by user
        which is then used to get t critical value using Scipy t ppf
        Also there is a possibility to use one-tailed test to find critical value

        Parameters
        ----------
        n : int
            Degrees of freedom passed automatically to the function
        alpha : float
            Alpha value to be used in the t critical value finding passed by the user
        Returns
        -------
        float
            Absolute value for the z critical value used to compare between z score of each sample
        """
        t_crit = t.ppf(alpha / (2 * n), n - 2)
        num = (n - 1) * t_crit
        den = sqrt(n * (n - 2 + square(t_crit)))
        val_crit = num / den
        return abs(val_crit)

    @staticmethod
    def z_score(vals: list, crit: float) -> tuple:
        """ Recieves values and critical z value to be compared with

        Parameters
        ----------
        vals : list
            List of sample values
        crit : float
            Critical z score to be used to compare with values
        Returns
        -------
        pd.DataFrame
            Dataframe with all data from z score and outlier results
        """
        mean = mn(vals)
        std = stdev(vals)
        zis = [(abs(mean - v) / std) for v in vals]
        otls = ["No" if z < crit else "Yes" for z in zis]
        results = {
            "values": vals,
            "zscore": zis,
            "outlier":otls
        }
        return results, mean, std, crit


import tensorflow as tf
import numpy as np

sess = tf.Session()

node1 = tf.constant(3.0)
node2 = tf.constant(4.0)
print(node1, node2)
print(sess.run([node1, node2]))

node3 = tf.add(node1, node2)
print("node3:", node3)
print("sess.run(node3):", sess.run(node3))

# Basic
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b
print(sess.run(adder_node, {a: 3, b: 4.5}))
print(sess.run(adder_node, {a: [1, 3], b: [2, 4]}))

add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b: 4.5}))
print(sess.run(add_and_triple, {a: [1, 3], b: [2, 4]}))

W = tf.Variable([.3])
b = tf.Variable([-.3])
x = tf.placeholder(tf.float32)
linear_model = W * x + b

init = tf.global_variables_initializer()
sess.run(init)

print(sess.run(linear_model, {x: [1, 2, 3, 4]}))

y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))

fixW = tf.assign(W, [-1.])
fixB = tf.assign(b, [1.])
sess.run([fixW, fixB])
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))

# Train
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
sess.run(init)  # reset values to incorrect defaults.
for i in range(1000):
    sess.run(train, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]})
print(sess.run([W, b]))

print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))

# Estimate
feature_columns = [tf.feature_column.numeric_column("x", shape=[1])]
estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns)

x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7, 0.])

input_fn = tf.estimator.inputs.numpy_input_fn({"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)
train_input_fn = tf.estimator.inputs.numpy_input_fn({"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)
eval_input_fn = tf.estimator.inputs.numpy_input_fn({"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)

estimator.train(input_fn=input_fn, steps=1000)

train_metrics = estimator.evaluate(input_fn=train_input_fn)
eval_metrics = estimator.evaluate(input_fn=eval_input_fn)
print("train metrics: %r"% train_metrics)
print("eval metrics: %r"% eval_metrics)


'''
Universidad Nacional Autonoma de Mexico.
Licenciatura en ciencias genomicas - Computo Cientifico 2020.
Salvador Gonzalez Juarez.

Proyecto final.
Programa 5: Lagrange.py
Calcula la interpolacion de un valor dado un conjunto de puntos u obtiene el polinomio de la funcion que siguen estos mismos, utilizando el metodo de Interpolacion de Lagrange.
'''

# Importa las librerias que ayudan a realizar el algebra del programa
import sympy as sp
from sympy import sympify, poly

def interpolacion(valores_x, valores_y, flag, valor = 1):
	'''
  	Funcion encargada de aplicar la formula general del metodo de interpolacion de Lagrange.
  	param valores_x: list, lista que contiene todos los valores de 'x'.
  	param valores_y: list, lista que contiene todos los valores de 'y.
  	param flag: bool, bandera que indica la modalidad del programa (si se busca interpolar un valor u obtener el polinomio).
  	param valor: float, valor que se busca interpolar. Por default es '1' y no es requerido si se quiere obtener el polinomio.
  	return el valor interpolado o el polinomio, dependiendo de la modalidad.
  	'''
	if flag:
		x = valor
	else:
		x = sp.Symbol('x')

	# Inicializa las variable que utilizan las iteraciones.
	nominador = 1
	denominador = 1
	polinomio = 0
	indice = 0

	# Realiza las iteraciones correspondientes al metodo de interpolacion de Lagrange.
	for xi in valores_x:
		for xj in valores_x:

			# Continua solo si se cumple la condicion impuesta por el metodo de Lagrange para los valores de 'xj' y 'xi'.
			if xj != xi:
				if flag:
					nominador = nominador * (x - xj)
					denominador = denominador * (xi - xj)
				else:
					nominador = sympify(nominador * (x - xj))
					denominador = sympify(denominador * (xi - xj))

		# Calcula el valor de cada uno de los terminos.
		if flag:
			termino = (nominador / denominador) * valores_y[indice]
			polinomio = polinomio + termino
		else:
			termino = sympify((nominador / denominador) * valores_y[indice])
			polinomio = sympify(polinomio + termino)

		# Inicializa los valores de las variables utilizadas para la siguiente iteracion.
		indice = indice + 1
		nominador = 1
		denominador = 1

	return polinomio

# Inicio del programa.
print("Bienvenido a Lagrange. Este programa calcula la interpolacion de un valor dado un conjunto de puntos u obtiene el polinomio de la funcion que siguen estos mismos, utilizando el metodo de Interpolacion de Lagrange.")

# Pide al usuario un numero correcto de puntos.
cantidad_puntos = 0
while cantidad_puntos <= 1:
	cantidad_puntos = int(input("\nEscribe el numero de puntos: "))
	if cantidad_puntos <= 1:
		print("El valor debe ser mayor a 1. Intentalo de nuevo.")

# Genera un par de listas donde se guardaran los valores en 'x' y en 'y' de los puntos, respectivamente.
valores_x = []
valores_y = []

# Pide al usuario los valores en 'x' y en 'y' de todos los puntos, y los guarda en la lista correspondiente.
for i in range(cantidad_puntos):
	valores_x.append(float(input("\nIngresa el valor en 'x' del punto " + str(i + 1) + ": ")))
	valores_y.append(float(input("Ingresa el valor en 'y' del punto " + str(i + 1) + ": ")))

# Pregunta al usuario que modalidad del programa quiere usar.
flag = None
while (flag != 'i') and (flag != 'p'):
	flag = input("\nSi quieres interpolar un valor, escribe 'i'. Si quieres obtener el polinomio, escribe 'p': ")
	if (flag != 'i') and (flag != 'p'):
		print("Intentalo de nuevo.")

# Si el usuario eligio la modalidad de interpolacion de un valor, continua con el siguiente codigo.
if flag == 'i':

	# Obtiene el valor a interpolar, lo interpola e imprime el resultado.
	lista_valores = (input("\nIngresa los valores que quieres interpolar (separa los valores con un espacio): "))
	lista_valores = lista_valores.split(" ")
	for valor in lista_valores:
		resultado = interpolacion(valores_x, valores_y, True, float(valor))
		print("\nEl valor interpolado de " + valor + " es: " + str(resultado))

# Si el usuario eligio la modalidad de obtencion del polinomio, continua con el siguiente codigo.
else:

	# Obtiene el polinomio de la funcion.
	resultado = interpolacion(valores_x, valores_y, False)

	# Prepara el polinomio para una correcta impresion.
	resultado = str(poly(resultado))
	resultado = resultado.lstrip("Poly(").rstrip(")")
	resultado = resultado.split(",")

	# Imprime solo el polinomio como resultado.
	print("\nEl polinomio es de la funcion es: " + str(resultado[0]))

'''
DICCIONARIO DE VARIABLES:

cantidad_puntos: int, variable que representa la cantidad de puntos que seran usados para hacer la interpolacion.

denominador: float o sympify, multiplicador usado para calcular el denominador de cada uno de los terminos en la funcion interpolacion( ).

flag: bool, bandera que indica la modalidad del programa (si se busca interpolar un valor u obtener el polinomio).

i: int, contador utilizado para recorrer listas.

indice: int, variable de tipo acumulador utilizada para poder accesar de manera correcta a los valores que contiene la lista valores_y, dentro de  la funcion interpolacion( ).

lista_valores: list, lista que contiene todos los valores que el usuario desea interpolar.

nominador: float o sympify, variable de tipo multiplicador usado para calcular el nominador de cada uno de los terminos en la funcion interpolacion( ).

polinomio: float o sympify, variable que contiene el valor del resultado de la interpolacion, ya sea en forma de polinomio o como polinomio resuelto. Se calcula como indica el metodo de Lagrange. Es el valor retornable de la funcion interpolacion( ).

resultado: float o sympify, variable que representa el resultado de la interpolacion de cada valor, o el polinomio que describe la curva formada por los puntos.

termino: float o sympify, variable temporal que almacena el valor de un termino del polinomio, para luego sumarlo con el resto del polinomio. Es usada dentro de la funcion interpolacion( ).

valor: float, variable que representa uno de los valores que el usuario quiere interpolar.

valores_x: list, lista que contiene el valor en 'x' de cada uno de los puntos proporcionados por el usuario.

valores_y: list, lista que contiene el valor en 'y' de cada uno de los puntos proporcionados por el usuario.

x (en modo interpolacion): float, variable que almacena el valor del parametro valor en la funcion interpolacion( ).

x (en modo polinomio): sympify, literal 'x' dentro de la ecuacion.

xi: float, variable que representa uno de los valores de la lista valores_x, y es utilizada para crear el denominador de cada uno de los terminos en la funcion interpolacion( ).

xj: float, variable que representa uno de los valores de la lista valores_x, y es utilizada para crear el nominador y denominador de cada uno de los terminos en la funcion interpolacion( ).
'''


'''
    This file is part of PM4Py (More Info: https://pm4py.fit.fraunhofer.de).

    PM4Py is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    PM4Py is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PM4Py.  If not, see <https://www.gnu.org/licenses/>.
'''
import numpy as np
import sympy

def compute_place_invariants(net):
    """
    We compute the NUllspace of the incidence matrix and obtain the place-invariants.
    :param net: Petri Net of which we want to know the place invariants.
    :return: Set of place invariants of the given Petri Net.
    """

    def compute_incidence_matrix(net):
        """
        Given a Petri Net, the incidence matrix is computed. An incidence matrix has n rows (places) and m columns
        (transitions).
        :param net: Petri Net object
        :return: Incidence matrix
        """
        n = len(net.transitions)
        m = len(net.places)
        C = np.zeros((m, n))
        i = 0
        transition_list = list(net.transitions)
        place_list = list(net.places)
        while i < n:
            t = transition_list[i]
            for in_arc in t.in_arcs:
                # arcs that go to transition
                C[place_list.index(in_arc.source), i] -= 1
            for out_arc in t.out_arcs:
                # arcs that lead away from transition
                C[place_list.index(out_arc.target), i] += 1
            i += 1
        return C

    def extract_basis_vectors(incidence_matrix):
        """
        The name of the method describes what we want t achieve. We calculate the nullspace of the transposed identity matrix.
        :param incidence_matrix: Numpy Array
        :return: a collection of numpy arrays that form a base of transposed A
        """
        # To have the same dimension as described as in https://www7.in.tum.de/~esparza/fcbook-middle.pdf and to get the correct nullspace, we have to transpose
        A = np.transpose(incidence_matrix)
        # exp from book https://www7.in.tum.de/~esparza/fcbook-middle.pdf
        x = sympy.Matrix(A).nullspace()
        # TODO: Question here: Will x be always rational? Depends on sympy implementation. Normaly, yes, we we will have rational results
        x = np.array(x).astype(np.float64)
        return x

    A = compute_incidence_matrix(net)
    return extract_basis_vectors(A)


from scipy.odr import ODR, Model, RealData, unilinear
import numpy as np

def fit_straight_line(x, y, x_err=None, y_err=None, m0=1.0, c0=0.0, nan_error=False, skip_nan=False):
    """
    Use scipy to fit a straight line, with errors or covariances allowed
    on both x and y.

    Parameters
    ----------
    x: array
        x-coordinate
    y: array
        y-coordinate
    m0: float
        optional, default=1, guess at gradient
    c0: float
        optional, default=1, guess at intercept
    x_err: array/float
        optional, default=None, errors either 1D std. dev., 2D covariance, or scalar constant, or None for unspecified
    y_err: array/float
        optional, default=None, errors either 1D std. dev., 2D covariance, or scalar constant, or None for unspecified

    Returns
    -------
    m: float
        gradient

    c: float
        intercept
    """

    kwargs = {}
    nx = np.ndim(x_err)

    if skip_nan:
        w = np.isfinite(x) & np.isfinite(y)
        x = x[w]
        y = y[w]
    else:
        w = slice(None)

    if x_err is None:
        pass
    elif nx == 0:
        kwargs['sx'] = x_err
    elif nx == 1:
        kwargs['sx'] = x_err[w]
    elif nx == 2:
        kwargs['cov_x'] = x_err[w][:,w]
    else:
        raise ValueError("x_sigma_or_cov must be None, scalar, 1D, or 2D")

    ny = np.ndim(y_err)

    if y_err is None:
        pass
    elif ny == 0:
        kwargs['sy'] = y_err
    elif ny == 1:
        kwargs['sy'] = y_err[w]
    elif nx == 2:
        kwargs['cov_y'] = y_err[w][:,w]
    else:
        raise ValueError("x_sigma_or_cov must be None, scalar, 1D, or 2D")

    data = RealData(x, y, **kwargs)
    odr = ODR(data, unilinear, beta0=[m0, c0], maxit=200)
    results = odr.run()

    if results.stopreason != ['Sum of squares convergence']:
        if nan_error:
            return np.nan, np.nan, np.zeros((2,2))*np.nan
        else:
            raise RuntimeError('Failed to straight line' + str(results.stopreason))

    m, c = results.beta
    cov = results.cov_beta
    return m, c, cov


# Jack C. Cook
# Sunday, August 30, 2020

import numpy as np

def process(path):
    """
    This function takes a path to a file and does stuff. [For documentation example.]

    :type path: string
    :param path: a path to the file which contains the features
    :return: None
    """
    print(path)
    return None

def least_squares_max_likelihood(phi_x: np.ndarray, y: np.ndarray) -> np.ndarray:
    """
    Compute the maximum likelihood for least squares
    :param phi_x: the input feature transformed
    :param y: the y values for regression
    :return: the maximum likelihood solution to least squares linear regression
    """
    # TODO: look into sklearn linear regression
    # Take the Moore-Penrose pseudo inverse
    # https://pythonhosted.org/algopy/examples/moore_penrose_pseudoinverse.html
    x_p = np.linalg.pinv(phi_x)
    # finish solving for maximum likelihood
    theta_ml = np.matmul(x_p, y)
    return theta_ml

def phi_polynomial(x: np.ndarray, order=1) -> np.ndarray:
    """
    A hypothesis or basis function which is a polynomial function of x up to order

    :type x: np.ndarray
    :type order: int

    :param x: the original input features used to generate the data
    :param order: the order of the basis function

    :return: the input feature vector after being processed by the basis function
    """
    # TODO: look into np.power()
    phi_x = np.ones_like(x)  # first term is one (bias)
    for i in range(1, order + 1):
        tmp = np.power(x, i)
        phi_x = np.hstack((phi_x, tmp))
    return phi_x

def predicted_values(theta, x):
    # TODO: remove the for loop by using phi_x
    m, n = theta.shape
    y_pred = np.zeros_like(x)
    for i in range(m):
        power = i
        y_pred += theta[i][0] * x ** power
    return y_pred

def predict_values(phi_x: np.ndarray, theta_ml: np.ndarray):
    """
    Predict the values of the maximum likely hood solution
    :param phi_x: the output of the feature vector after processing through the basis function (see phi_polynomial)
    :param theta_ml: the maximum likelihood solution for linear regression using least squares
    (see least_squares_maximum_likelihood)
    :return: the predicted values
    """
    return np.matmul(phi_x, theta_ml)

def average_least_square_error(y1: np.ndarray, y2: np.ndarray):
    """
    Compute the average or mean least squares error
    :param y1: one input vector
    :param y2: one input vector
    :return: the scalar value of the average least squares error
    """
    # https://stackoverflow.com/questions/16774849/mean-squared-error-in-numpy
    mse = (np.square(y2 - y1)).mean()
    return mse

def weight_matrix(x: np.ndarray, training_data: np.ndarray, tao: float):
    """

    :param x: the input value to be locally weighted to
    :param training_data: the training data or examples you have available to train with
    :param tao: the bandwidth parameter
    :return: an identity matrix which is the weight matrix

    >>> w[0] = np.exp(- (training_data[0] - x)**2 / (2 * tao ** 2) )

    """
    # https://www.geeksforgeeks.org/implementation-of-locally-weighted-linear-regression/
    # M is the No of training examples
    M = training_data.shape[0]
    # Initialising W with identity matrix
    W = np.mat(np.eye(M))
    # calculating weights for query points
    # TODO: turn this into vectorized code
    for i in range(M):
        xi = training_data[i]
        denominator = (-2 * tao * tao)
        W[i, i] = np.exp(np.dot((xi - x), (xi - x).T) / denominator)
    return W

def predict(training_data: np.ndarray, Y: np.ndarray, x: np.ndarray, tao: float):
    """

    :param training_data: the feature vector
    :param Y: the output values or target values
    :param x: the query point
    :param tao: bandwidth
    :return: the prediction and theta maximum likelihood
    """
    X_ = training_data
    qx = x
    W = weight_matrix(qx, X_, tao)
    # calculating parameter theta
    theta = np.linalg.pinv(X_.T*(W * X_))*(X_.T*(W * Y))
    # calculating predictions
    pred = np.dot(qx, theta)
    return theta, pred


# $Author$
# $Revision$
# $Date$
# $HeadURL$
# $Id$

#+
# NAME:
#	DISK
#
# PURPOSE:
#	This function returns a byte array containing a disk.  The
#	disk is centered at x, y, and has radius r.  The array is nx,
#	ny in size and has byte type.  Pixel values of 1 indicate that
#	the center of a pixel is within r of x,y.  Pixel values of 0
#	indicate the opposite.  The center of each pixel is the
#	integer position of that pixel.
#
# CATEGORY:
#	Array creation.
#
# CALLING SEQUENCE:
#
#	diskarr = disk(R, X, Y, Nx, Ny)
#		or
#	diskarr = disk(R, Ctr, Sz)
#		which is the same as
#	diskarr = disk(R, [X, Y], [Nx, Ny])
#
# INPUTS:
#	There are two ways to enter the inputs.  If Nx is defined, then:
#	R:	radius of the disk, may be fractional.
#	X:	X position of the center of the disk, may be fractional.
#	Y:	Y position of the center of the disk, may be fractional.
#	Nx:	width of the output array.
#	Ny:	height of the output array.
#	Otherwise:
#	R:	Radius of the disk, may be fractional.
#	Ctr:	Position of the center of the disk, 2-element vector.
#	Sz:	Size of the output array, 2-element vector.
#
# KEYWORDS:
#	STATUS:	(returned) Set to 1 if any part of the disk is
#	outside the image boundaries.
#
# OUTPUTS:
#	This function returns a byte array as described above.
#
# PROCEDURE:
#	Each pixel is evaluated to see whether it is within r of
#	(x,y).  If so, the value is set to 1.  Otherwise it is 0.
#
# SIDE EFFECTS:
#	STATUS is set in caller.
#
# EXAMPLE:
#
#		tvscl, DISK(6.4, 9.9, 10.1, 30, 30)
#		tvscl, DISK(6.4, [9.9,  10.1], [30, 30], STATUS=STATUS)
#		print, STATUS
#		tvscl, DISK(6.4, [ 3,   10.1], [30, 30], STATUS=STATUS)
#		print, STATUS
#		tvscl, DISK(6.4, [27,   10.1], [30, 30], STATUS=STATUS)
#		print, STATUS
#		tvscl, DISK(6.4, [ 9.9,  3],   [30, 30], STATUS=STATUS)
#		print, STATUS
#		tvscl, DISK(6.4, [ 9.9, 27],   [30, 30], STATUS=STATUS)
#		print, STATUS
#
# MODIFICATION HISTORY:
# 	Written by:	Joseph Harrington, Cornell.  2003 April 4
#			jh@oobleck.astro.cornell.edu
#
#	2004 Feb 27	jh added alternate input method
#	2005 Nov 16	jh added STATUS, simplified disk calculation,
#				use double precision
#-

import numpy as np

def disk(r, ctr, size, status=False):

  # return status
  retstatus = status

  # check if disk is off image
  status = 0
  if (ctr[0] - r < 0 or ctr[0] + r > size[0]-1 or
      ctr[1] - r < 0 or ctr[1] + r > size[1]-1 ):
    status = 1

  # calculate pixel distance from center
  # print('disk size:',  size)
  ind = np.indices(size)
  fdisk = (ind[0]-ctr[0])**2.0 + (ind[1]-ctr[1])**2.0

  # return mask disk (and status if requested)
  ret = fdisk <= r**2.0
  if retstatus:
    ret = ret, status
  return ret


import math

import matplotlib.pyplot as plt
from matplotlib import rc
import numpy as np

if __name__ == '__main__':
    # init config
    x_limit = math.pi * 2
    c = 2

    # rc('font', **{'family': 'sans-serif', 'sans-serif': ['Helvetica']})
    # for 'Palatino' and other serif fonts use:
    # rc('font',**{'family':'serif','serif':['Palatino']})
    # rc('text', usetex=True)

    # domain and co-domain settings
    x = np.linspace(-x_limit, x_limit, 100)
    f_x = np.sin(x)

    # upper function
    g_x = f_x + c

    # lower function
    h_x = f_x - c

    plt.figure(1)

    plt.plot(x, g_x, 'g--')
    plt.plot(x, f_x)
    plt.plot(x, h_x, 'y--')

    # get/set the x limits of the current axes
    plt.xlim(-math.pi, math.pi)

    # get/set the y limits of the current axes
    plt.ylim(-math.pi, math.pi)

    # add a horizontal line across the axis
    plt.axhline(y=0, color='k')

    # add a vertical line across the axes
    plt.axvline(x=0, color='k')

    # *math_fontfamily* can be used in most places where there is text,
    # like in the title:
    # plt.title(r"$f(x)\pm c$", size=14)  # , **code_font

    # save plot to file
    # after plt.show() is called, a new blank figure is created.
    # to avoid paving black images, just save the image before showing it
    plt.savefig('y_translation.svg')

    # display all open figures
    plt.show()

    # print(matplotlib.font_manager.fontManager.ttflist)

# https://stackoverflow.com/questions/21321670/how-to-change-fonts-in-matplotlib-python#21323217
# https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html
# https://python.astrotech.io/matplotlib/multiple-figures.html#multiple-plots-on-one-figure


"""
cookb_signalsmooth.py

from: http://scipy.org/Cookbook/SignalSmooth
"""

import numpy as np
import six

def smooth(x, window_len=10, window='hanning'):
    """smooth the data using a window with requested size.

    This method is based on the convolution of a scaled window with the signal.
    The signal is prepared by introducing reflected copies of the signal
    (with the window size) in both ends so that transient parts are minimized
    in the begining and end part of the output signal.

    input:
        x: the input signal
        window_len: the dimension of the smoothing window
        window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
            flat window will produce a moving average smoothing.

    output:
        the smoothed signal

    example:

    import numpy as np
    t = np.linspace(-2,2,0.1)
    x = np.sin(t)+np.random.randn(len(t))*0.1
    y = smooth(x)

    see also:

    numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
    scipy.signal.lfilter

    TODO: the window parameter could be the window itself if an array instead of a string
    """

    if x.ndim != 1:
        six.raise_from(ValueError, "smooth only accepts 1 dimension arrays.")

    if x.size < window_len:
        six.raise_from(ValueError, "Input vector needs to be bigger than window size.")

    if window_len < 3:
        return x

    if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
        six.raise_from(ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'")

    s=np.r_[2*x[0]-x[window_len:1:-1], x, 2*x[-1]-x[-1:-window_len:-1]]
    #print(len(s))

    if window == 'flat': #moving average
        w = np.ones(window_len,'d')
    else:
        w = getattr(np, window)(window_len)
    y = np.convolve(w/w.sum(), s, mode='same')
    return y[window_len-1:-window_len+1]

#*********** part2: 2d

from scipy import signal

def gauss_kern(size, sizey=None):
    """ Returns a normalized 2D gauss kernel array for convolutions """
    size = int(size)
    if not sizey:
        sizey = size
    else:
        sizey = int(sizey)
    x, y = np.mgrid[-size:size+1, -sizey:sizey+1]
    g = np.exp(-(x**2/float(size) + y**2/float(sizey)))
    return g / g.sum()

def blur_image(im, n, ny=None) :
    """ blurs the image by convolving with a gaussian kernel of typical
        size n. The optional keyword argument ny allows for a different
        size in the y direction.
    """
    g = gauss_kern(n, sizey=ny)
    improc = signal.convolve(im, g, mode='valid')
    return(improc)


# encoding: utf-8
"""
circstat.py -- Circular statistics functions

Exported namespace: mean, std, var

Note: all functions take an array of radian-angle values on [0, 2*pi] as input.

Written by Joe Monaco, 4/17/2007
Center for Theoretical Neuroscience

Copyright (c) 2007 Columbia University. All rights reserved.

This software is provided AS IS under the terms of the Open Source MIT License.
See http://www.opensource.org/licenses/mit-license.php.
"""

# Library imports
from numpy import ones, dot, sin, cos, empty, arange, pi
from scipy.stats import histogram

# Package imports
from .radians import xy_to_rad

def mean(theta, w=None):
	"""First circular moment

    Input: theta - array of radian angle values
          w - optional weighting if angle values are binned
    Returns: scalar circular mean of theta

    See:	http://en.wikipedia.org/wiki/Directional_statistics
	"""
	sz = theta.shape[0]
	if w is None:
	    w = ones(sz, 'd')
	elif w.size != sz:
	    raise ValueError, 'weight array size mismatch'
	s_bar = dot(w, sin(theta))
	c_bar = dot(w, cos(theta))
	return xy_to_rad(c_bar, s_bar)

def std(theta):
	"""Sample circular deviation

    Input: theta - array of radian angle values
    Returns: circular standard deviation
	"""
	return (var(theta))**0.5

def var(theta, Nbins=360):
	"""Sample circular variance, second moment

    Calculated using the minimum variance method with moving cut points.
    See: Weber RO (1997). J. Appl. Meteorol. 36(10), 1403-1415.

    Input: theta - array of radian angle values
          numbins - number of intervals across [0, 2pi] to minimize
    Returns: circular variance
	"""
	N = len(theta)
	delta_t = 2 * pi / Nbins
	lims = (0, 2 * pi)
	x = arange(delta_t, 2*pi + delta_t, delta_t)
	n, xmin, w, extra = histogram(theta, numbins=Nbins, defaultlimits=lims)

	tbar = empty((Nbins,), 'd')
	S = empty((Nbins,), 'd')
	s2 = empty((Nbins,), 'd')

	tbar[0] = (x*n).sum() / N											# A1
	S[0] = ((x**2)*n).sum() / (N - 1)									# A2
	s2[0] = S[0] - N * (tbar[0]**2) / (N - 1)							# A3

	for k in xrange(1, Nbins):
		tbar[k] = tbar[k-1] + (2*pi) * n[k-1] / N						# A4
		S[k] = S[k-1] + (2*pi) * (2*pi + 2*x[k-1]) * n[k-1] / (N - 1)	# A5
		s2[k] = S[k] - N * (tbar[k]**2) / (N - 1)						# A6

	return s2.min()


# https://www.programcreek.com/2014/05/leetcode-minimum-path-sum-java/
# but not really :] Those are the solutions for a general problem of finding the minimum path in a graph.
import numpy as np

def generate_path(map,  dist, sy,  sx,  dy,  dx):
    ''' Generates (and visualizes) path from (sy,sx) to (dy,dx)'''
    # We got the distance map, not build the path by simply picking the nodes with smallest cost.
    path = [['.' for x in range(map.shape[1])] for y in range(map.shape[0])]

    # "Initialize" position.
    x = sx
    y = sy

    while y != dy or x != dx:
        next_x = -1
        next_y = -1
        next_min = np.inf
        move = 'x'
        # Check four directions.
        if (x > 0)  and (dist[y,  x-1] < next_min):
            next_min = dist[y,  x-1]
            next_x = x-1
            next_y = y
            move = '<'
        if (x < map.shape[1]-1)  and (dist[y,  x+1] < next_min):
            next_min = dist[y,  x+1]
            next_x = x+1
            next_y = y
            move = '>'
        if (y > 0)  and (dist[y-1,x] < next_min):
            next_min = dist[y-1, x]
            next_x = x
            next_y = y-1
            move = '^'
        if (y < map.shape[0]-1)  and (dist[y+1,x] < next_min):
            next_min = dist[y+1,x]
            next_x = x
            next_y = y+1
            move = 'd'
        # Make move
        path[y][x] = move
        x = next_x
        y = next_y

    # Indicate start and destination nodes.
    path[sy][sx] = 'o'
    path[dy][dx] = 'x'
    return path

def recursive_cost(map,  dist,  y, x):
    if (x > 0) and (dist[y, x-1] > dist[y, x] + map[y, x]):
        dist[y, x-1] = dist[y, x] + map[y, x-1]
        recursive_cost(map,  dist,  y, x-1)

    if (x < map.shape[1]-1) and (dist[y, x+1] > dist[y, x] + map[y, x+1]):
        dist[y, x+1] = dist[y, x] + map[y, x+1]
        recursive_cost(map,  dist,  y, x+1)

    if (y > 0) and (dist[y-1, x] > dist[y, x] + map[y-1, x]):
        dist[y-1, x] = dist[y, x] + map[y-1, x]
        recursive_cost(map,  dist,  y-1, x)

    if (y < map.shape[0]-1) and (dist[y+1, x] > dist[y, x] + map[y+1, x]):
        dist[y+1, x] = dist[y, x] + map[y+1, x]
        recursive_cost(map,  dist,  y+1, x)
    # return

def min_path_recursive(map, start=None,  dest=None):
    '''Finds minimum path in a given map using a simple recursive algorithm'''
    print(map.shape)
    print("Map:\n",map)

    # Initialize cost map.
    dist = np.empty(map.shape)
    dist.fill(np.inf)

    # Get start and destination coordinates.
    if (start == None):
        sy=0
        sx=0
    else:
        sy=start[0]
        sx=start[1]
    if (dest == None):
        dy = map.shape[0]-1
        dx = map.shape[1]-1
    else:
        dy = dest[0]
        dx = dest[1]

    # Distance from dest to dest is set to 0
    dist[dy, dx] = 0

    recursive_cost(map,  dist,  dy, dx)

    print("Final distances:\n", dist)

    return generate_path(map,  dist,   sy,  sx,  dy,  dx)

def min_path_dijkstra(map, start=None,  dest=None):
    '''Finds minimum path in a given map using the Dijkstra algorithm'''
    print(map.shape)
    print("Map:\n",map)

    # Initialize cost map.
    dist = np.empty(map.shape)
    dist.fill(np.inf)

    # Get start and destination coordinates.
    if (start == None):
        sy=0
        sx=0
    else:
        sy=start[0]
        sx=start[1]
    if (dest == None):
        dy = map.shape[0]-1
        dx = map.shape[1]-1
    else:
        dy = dest[0]
        dx = dest[1]

    # Distance from dest to dest is set to 0
    dist[dy, dx] = 0

    #  Queue of all nodes in the graph.
    Q  = [(y, x) for y in range(map.shape[0]) for x in range(map.shape[1])]

    while any(Q):
        #print("Q=", Q)
        # Find node with minimum distance to target.
        min_dist = np.inf
        min_node =-1
        for i, (y, x) in enumerate(Q):
            if (dist[y, x] < min_dist):
                min_dist = dist[y, x]
                min_node = i
        # Propagate from that node...
        (y, x) = Q.pop(min_node)

        # ... for each new adjacent node - check distances.
        if (x > 0) and (dist[y, x-1] > dist[y, x] + map[y, x]):
            dist[y, x-1] = dist[y, x] + map[y, x-1]

        if (x < map.shape[1]-1) and (dist[y, x+1] > dist[y, x] + map[y, x+1]):
            dist[y, x+1] = dist[y, x] + map[y, x+1]

        if (y > 0) and (dist[y-1, x] > dist[y, x] + map[y-1, x]):
            dist[y-1, x] = dist[y, x] + map[y-1, x]

        if (y < map.shape[0]-1) and (dist[y+1, x] > dist[y, x] + map[y+1, x]):
            dist[y+1, x] = dist[y, x] + map[y+1, x]

        #print("Calculated distances:\n", dist)
    print("Final distances:\n", dist)

    # Path.
    if (start == None):
        sy=0
        sx=0
    else:
        sy=start[0]
        sx=start[1]

    return generate_path(map,  dist,   sy,  sx,  dy,  dx)

if __name__ == "__main__":
    #map = np.array([[1, 2, 11, 10],   [2, 1, 1, 1],  [2, 1, 4, 0]])
    #map = np.array([[1, 30, 1, 3,  1],   [2, 1, 1, 10,  1],  [2, 1, 40, 1,  1]])
    map = np.array([[1, 2, 111, 10],   [21, 1, 101, 11],  [2, 1, 40, 9],  [1, 10, 40, 9],  [1, 1, 10, 1]])

    #p = min_path_dijkstra(map,  start=(0, 0),  dest=(0, 3))
    p = min_path_recursive(map=map,  start=(0, 0),  dest=(2, 3))
    print("Final path: ")
    for row in p:
        print(row)


#!\usr\bin\python
"""
File: yx_ODE.py

Copyright (c) 2016 Michael Seaman

License: MIT

Excercise C.3:
Implements the derivative of a function y(x) to test
how well the Euler method approximates it
"""

import numpy as np
import sympy as sp
import matplotlib.pyplot as plt

from Euler import Euler

def dydx(x, y):
    return (2*y - 2)**-1

def plot_Euler_approx(dydx, y_a, delta_x, a, b):
    """
    Plots the Euler approximation of y(x) given function dfdx and
    the initial point y_a from a to b with stepsize delta_x
    """
    xList = np.arange(a, b, delta_x)
    yList = Euler(dydx, y_a, delta_x, a, b)
    plt.plot(xList, yList)
    plt.show()

def y_0(epsilon):
    """
    returns the initial condition given an epsilon value
    """
    return 1 + float(epsilon)**.5

def plot_approx_and_exact(exact, resolution, dfdx, y_a, delta_x, a, b):
    """
    Plots the Euler approximation y(x) in red alongside its
    given exact value in green from a to b
    """
    xList_a = np.arange(a, b, delta_x)
    yList_a = Euler(dfdx, y_a, delta_x, a, b)
    xList_e = np.linspace(a, b, resolution)
    yList_e = np.vectorize(exact)(xList_e)
    plt.plot(xList_a, yList_a, 'r')
    plt.plot(xList_e, yList_e, 'g')
    plt.show()


import numpy as np

class GeneticAlgorithm(object):
    """
        Implement a simple generationl genetic algorithm as described in the instructions
    """

    def __init__(self, chromosomeShape,
                 errorFunction,
                 elitism=1,
                 populationSize=25,
                 mutationProbability=.1,
                 mutationScale=.5,
                 numIterations=10000,
                 errorTreshold=1e-6
                 ):
        self.populationSize = populationSize  # size of the population of units
        self.p = mutationProbability  # probability of mutation
        self.numIter = numIterations  # maximum number of iterations
        self.e = errorTreshold  # threshold of error while iterating
        self.f = errorFunction  # the error function (reversely proportionl to fitness)
        self.keep = elitism  # number of units to keep for elitism
        self.k = mutationScale  # scale of the gaussian noise

        self.i = 0  # iteration counter
        # initialize the population randomly from a gaussian distribution
        # with noise 0.1 and then sort the values and store them internally

        self.population = []
        for _ in range(populationSize):
            chromosome = np.random.randn(chromosomeShape) * 0.1

            fitness = self.calculateFitness(chromosome)
            self.population.append((chromosome, fitness))

        # sort descending according to fitness (larger is better)
        self.population = sorted(self.population, key=lambda t: -t[1])

    def step(self):
        """
            Run one iteration of the genetic algorithm. In a single iteration,
            you should create a whole new population by first keeping the best
            units as defined by elitism, then iteratively select parents from
            the current population, apply crossover and then mutation.

            The step function should return, as a tuple:

            * boolean value indicating should the iteration stop (True if
                the learning process is finished, False othwerise)
            * an integer representing the current iteration of the
                algorithm
            * the weights of the best unit in the current iteration

        """

        self.i += 1

        #############################
        #       YOUR CODE HERE      #
        #############################
        new_population = self.bestN(self.keep)
        while len(new_population) < self.populationSize:
            first_parent, second_parent = self.selectParents()
            child = self.crossover(first_parent, second_parent)
            chromosome = np.array(self.mutate(child))
            fitness = self.calculateFitness(chromosome)
            new_population.append((chromosome, fitness))
        self.population = sorted(new_population, key=lambda t: -t[1])
        should_stop = self.i > self.numIter or self.best() < self.e
        return should_stop, self.i, self.best()[0]

    def calculateFitness(self, chromosome):
        """
            Implement a fitness metric as a function of the error of
            a unit. Remember - fitness is larger as the unit is better!
        """
        chromosomeError = self.f(chromosome)

        #############################
        #       YOUR CODE HERE      #
        #############################
        return 1 / chromosomeError

    def bestN(self, n):
        """
            Return the best n units from the population
        """
        #############################
        #       YOUR CODE HERE      #
        #############################
        return self.population[0:n]

    def best(self):
        """
            Return the best unit from the population
        """
        #############################
        #       YOUR CODE HERE      #
        #############################
        return self.population[0]

    def selectParents(self):
        """
            Select two parents from the population with probability of
            selection proportional to the fitness of the units in the
            population
        """
        #############################
        #       YOUR CODE HERE      #
        #############################
        fitness_sum = 0
        for value in self.population:
            fitness_sum += value[1]

        first_parent = self.roulette_wheel_selection(fitness_sum)
        second_parent = self.roulette_wheel_selection(fitness_sum)

        return first_parent, second_parent

    def roulette_wheel_selection(self, fitness_sum):
        pointer = np.random.uniform(0, fitness_sum)
        current_fittness = 0
        for chromosome, fitness in self.population:
            current_fittness += fitness
            if current_fittness > pointer:
                return chromosome

    def crossover(self, p1, p2):
        """
            Given two parent units p1 and p2, do a simple crossover by
            averaging their values in order to create a new child unit
        """
        #############################
        #       YOUR CODE HERE      #
        #############################
        return (p1 + p2) / 2

    def mutate(self, chromosome):
        """
            Given a unit, mutate its values by applying gaussian noise
            according to the parameter k
        """

        #############################
        #       YOUR CODE HERE      #
        #############################
        for index in range(len(chromosome)):
            if np.random.random() < self.p:
                chromosome[index] += np.random.normal(0, self.k)
        return chromosome


# --------------------
# cs user: yoav
# full name: Yoav Levy
# id: 314963257
# --------------------
# ----- imports: -----
import math
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.linalg import qr

# ---------------------

def get_orthogonal_matrix(dim):
    H = np.random.randn(dim, dim)
    Q, R = qr(H)
    return Q

def plot_3d(x_y_z: np.array, title: str):
    """
    plot points in 3D
    :param x_y_z: the points. numpy array with shape: 3 X num_samples (first dimension for x, y, z
    coordinate)
    """
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")
    ax.scatter(x_y_z[0], x_y_z[1], x_y_z[2], s=1, marker=".", depthshade=False)
    ax.set_xlim(-5, 5)
    ax.set_ylim(-5, 5)
    ax.set_zlim(-5, 5)
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("z")
    ax.set_title(title)
    fig.show()

def plot_2d(x_y, title):
    """
    plot points in 2D
    :param x_y_z: the points. numpy array with shape: 2 X num_samples (first dimension for x, y
    coordinate)
    """
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(x_y[0], x_y[1], s=1, marker=".")
    ax.set_xlim(-5, 5)
    ax.set_ylim(-5, 5)
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_title(title)
    fig.show()

def estimator_function(data):
    """
    This function is the mean estimator function.
    :param data: Coin tosses data.
    :return: The mean of tosses numbers.
    """
    values = []
    for index in range(len(data)):
        values.append(np.mean(data[: index + 1]))
    return values

def plot_q16():
    """
    This function plots the sample mean of a coin toss as a function of the number of tosses (m)
    as requested in question number 16 (a).
    :param data: 5d vector of vectors where the data of each batch is.
    :return: nothing.
    """
    data1 = estimator_function(ndata[0])
    data2 = estimator_function(ndata[1])
    data3 = estimator_function(ndata[2])
    data4 = estimator_function(ndata[3])
    data5 = estimator_function(ndata[4])
    fig, ax = plt.subplots()  # Create a figure containing a single axes.
    ax.plot(range(1, 1001), data1, label="sequence 1")  # plot 1
    ax.plot(range(1, 1001), data2, label="sequence 2")  # plot 2
    ax.plot(range(1, 1001), data3, label="sequence 3")  # plot 3
    ax.plot(range(1, 1001), data4, label="sequence 4")  # plot 4
    ax.plot(range(1, 1001), data5, label="sequence 5")  # plot 5
    ax.set_xlabel("Toss numbers (m)")
    ax.set_ylabel("Sample mean (estimator)")
    ax.legend()
    ax.set_ylim(bottom=0, top=1)
    ax.set_xlim(left=0, right=1000)
    plt.grid(color="#999999", linestyle="--", alpha=0.3)
    ax.set_title("Q16: 5 independent sequences of 1,000 tosses and their mean")
    fig.show()

def print_cov_mat(data):
    cov_mat = np.matmul(
        np.array(
            [
                [1 / (population - 1), 0, 0],
                [0, 1 / (population - 1), 0],
                [0, 0, 1 / (population - 1)],
            ]
        ),
        np.matmul(data, np.transpose(data)),
    )
    print(cov_mat)

def question_11(data):
    """
    Question 11, just plotting the given data.
    :param data: data
    :return: nothing
    """
    plot_3d(
        data, "Q11: Random points gen by the identity matrix as the cov matrix"
    )  # Q11

def question_12(data):
    """
    Question 12 linear transform the data, plot it, and show the newly created cov matrix.
    :param data: data
    :return: data after linear transformation
    """
    s_mat = np.array([[0.1, 0, 0], [0, 0.5, 0], [0, 0, 2]])
    new_data = np.matmul(s_mat, data)
    plot_3d(new_data, "Q12: Linear Transformed the prev data")
    print("------ Covariance Matrix (QUESTION 12) ------")
    print_cov_mat(new_data)
    return new_data

def question_13(data):
    """
    Question 13, create an orthogonal matrix, applay on the given data (already linear transformed)
    plot it, and show the cov matrix. (also prints the transformed data)
    :param data: Linear transformed data.
    :return: nothing.
    """
    print("------ Randomly created orthogonal Matrix ------")
    orthogonal_mat = get_orthogonal_matrix(3)  # Randomly orthogonal matrix
    print(orthogonal_mat)
    new_data = np.matmul(orthogonal_mat, data)
    plot_3d(new_data, "Q13: Applied orthogonal matrix")
    print("------ Covariance Matrix (AFTER ORTHGONAL TRANSFORMATION) ------")
    print_cov_mat(new_data)

def question_14(data):
    """
    Question 14, projection of the data to the x,y plane.
    and prints the result, and plotting the data.
    :param data: data.
    :return: nothing.
    """
    print("------ Projection of the data to the x, y axes (DATA) ------")
    proj_mat = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 0]])
    two_d_data = np.matmul(proj_mat, data)
    print(
        two_d_data[:-1]
    )  # "Elisha Kipnis" said we should plot in 2d if we project to 2d plane
    # (https://moodle2.cs.huji.ac.il/nu19/mod/forumng/discuss.php?d=7931)
    plot_2d(two_d_data[:-1], "Q14: Projection of the data to the x, y axes.")

def question_15(data):
    """
    Question 15, projection of the data which 0.1 > z > -0.4 to the x,y plane.
    and prints the result, and plotting the data.
    :param data:
    :return: nothing.
    """
    print("------ Question 15 0.1 > z > -0.4 (DATA) ------")
    x = []
    y = []
    bool_ind = (-0.4 < data[2]) & (data[2] < 0.1)
    for index, item in enumerate(data[0]):
        if bool_ind[index]:
            x.append(item)
            y.append(data[1][index])
    data_01zm04 = np.array([x, y])
    print(data_01zm04)
    plot_2d(data_01zm04, "Q15: Projection to x, y after filtering dots 0.1 > z > -0.4")

def question_16_a():
    """
    Question 16 a, plotting the experiment.
    :param data: data of 5 batches of 1000 coin tosses.
    :return: nothing.
    """
    plot_q16()

def create_pecentage_c(eps, cummean):
    """
    creates the percentage table
    :param eps: epsilon
    :return: percentage table
    """
    true_false_table = abs(cummean - 0.25) >= eps
    return np.count_nonzero(true_false_table, axis=0) / exp_number

def question_16_bc():
    """
    Question 16 b,
    :param data:
    :return:
    """
    variance = 0.1875
    cummean = np.add.accumulate(ndata, axis=1) / np.arange(1, data_len + 1)
    for eps in epsilon:
        fig, ax = plt.subplots()  # Create a figure containing a single axes.
        chebyshev = [min(variance / (x * (eps ** 2)), 1) for x in range(1, 1001)]
        hoeffding = [min(2 * math.exp(-2 * x * (eps ** 2)), 1) for x in range(1, 1001)]
        percentage = create_pecentage_c(eps, cummean)
        ax.plot(range(1, 1001), chebyshev, label="chebyshev bound")  # plot 1
        ax.plot(range(1, 1001), hoeffding, label="hoeffding bound")  # plot 1
        ax.plot(
            range(1, 1001), percentage, label="percentage satisfying event"
        )  # plot 1
        ax.set_xlabel("Toss numbers (m)")
        ax.set_ylabel("Upper Bound")
        ax.legend()
        ax.set_ylim(bottom=0, top=1.01)
        ax.set_xlim(left=0, right=1000)
        plt.grid(color="#999999", linestyle="--", alpha=0.3)
        ax.set_title("Q16b: epsilon = " + str(eps))
        fig.show()

if __name__ == "__main__":
    # ----------- settings -------------
    population = 50000
    mean = [0, 0, 0]
    cov = np.eye(3)
    data = np.random.multivariate_normal(mean, cov, population).T
    np.set_printoptions(suppress=True)
    # ----------------------------------
    question_11(data)
    transformed_data = question_12(data)
    question_13(transformed_data)
    question_14(data)
    question_15(data)
    # ----- Question no.16 -----
    exp_number =100  # 100000
    data_len = 1000  # 1000
    ndata = np.random.binomial(1, 0.25, (exp_number, data_len))
    epsilon = [0.5, 0.25, 0.1, 0.01, 0.001]
    question_16_a()
    question_16_bc()


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

import numpy as np
import numpy.linalg as ln
import math
import sys

""" This is a simple and deterministic method for matrix sketch.
The original method has been introduced in [Liberty2013]_ .

[Liberty2013] Edo Liberty, "Simple and Deterministic Matrix Sketching", ACM SIGKDD, 2013.
"""

def sketch(mat_a, ell):
    """Compute a sketch matrix of input matrix
    Note that \ell must be smaller than m * 2

    :param mat_a: original matrix to be sketched (n x m)
    :param ell: the number of rows in sketch matrix
    :returns: sketch matrix (\ell x m)
    """

    # number of columns
    m = mat_a.shape[1]

    # Input error handling
    if math.floor(ell / 2) >= m:
        raise ValueError('Error: ell must be smaller than m * 2')
    if ell >= mat_a.shape[0]:
        raise ValueError('Error: ell must not be greater than n')

    # initialize output matrix B
    mat_b = np.zeros([ell, m])

    # compute zero valued row list
    zero_rows = np.nonzero([round(s, 7) == 0.0 for s in np.sum(mat_b, axis = 1)])[0].tolist()

    # repeat inserting each row of matrix A
    for i in range(0, mat_a.shape[0]):

        # insert a row into matrix B
        mat_b[zero_rows[0], :] = mat_a[i, :]

        # remove zero valued row from the list
        zero_rows.remove(zero_rows[0])

        # if there is no more zero valued row
        if len(zero_rows) == 0:

            # compute SVD of matrix B
            mat_u, vec_sigma, mat_v = ln.svd(mat_b, full_matrices=False)

            # obtain squared singular value for threshold
            squared_sv_center = vec_sigma[math.floor(ell / 2)] ** 2

            # update sigma to shrink the row norms
            sigma_tilda = [(0.0 if d < 0.0 else math.sqrt(d)) for d in (vec_sigma ** 2 - squared_sv_center)]

            # update matrix B where at least half rows are all zero
            mat_b = np.dot(np.diagflat(sigma_tilda), mat_v)

            # update the zero valued row list
            zero_rows = np.nonzero([round(s, 7) == 0 for s in np.sum(mat_b, axis = 1)])[0].tolist()

    return mat_b

def calculateError(mat_a, mat_b):
    """Compute the degree of error by sketching

    :param mat_a: original matrix
    :param mat_b: sketch matrix
    :returns: reconstruction error
    """
    dot_mat_a = np.dot(mat_a.T, mat_a)
    dot_mat_b = np.dot(mat_b.T, mat_b)
    return ln.norm(dot_mat_a - dot_mat_b, ord = 2)

def squaredFrobeniusNorm(mat_a):
    """Compute the squared Frobenius norm of a matrix

    :param mat_a: original matrix
    :returns: squared Frobenius norm
    """
    return ln.norm(mat_a, ord = 'fro') ** 2


import sympy as sy

y0, y1, g0, g1, x = sy.symbols("y_0 y_1 g_0 g_1 x")
theta = sy.symbols("theta")
pi = sy.pi
R, w, h, H = sy.symbols("R w h H")

r = 1

theta0 = 2*sy.acos(h/r)
theta1 = 2*sy.acos((h-w)/r)
A0 = r**2 * (theta0 - sy.sin(theta0))/2
A1 = pi*r**2 - r**2 * (theta1 - sy.sin(theta1))/2

eq1 = sy.Eq(sy.simplify(A0/(A0+A1)), sy.simplify(y0/(y0+y1)))
print(eq1)
#print(sy.solve(eq1, h))

phi0 = 2*sy.acos(H/R)
phi1 = 2*sy.acos((H-w)/R)

beta0 = sy.asin(H/R)
beta1 = sy.asin((H-w)/R)

xup0 = -R*sy.sin(beta0)
xdown0 = -R*sy.sin(beta1)
#
#B00 = sy.integrate(sy.sqrt(R**2-x**2)-H,(x, -R*sy.sin(beta0), x0) )
#print(sy.simplify(B00))


import matplotlib.pyplot as plt
from numpy.linalg import solve

plt.style.use('seaborn')

def BlackLittermanNormal(mu_, s2_, tau, v_mu, mu_view, sig2_view):
    # This function computes the Black-Litterman parameters of the updated
    # normal distribution associated with a normal reference model for the
    # market variables
    #  INPUTS
    #   mu_        : [vector] (n_ x 1) base-case expectation
    #   s2_        : [matrix] (n_ x n_) base-case covariance matrix
    #   tau        : [scalar]  certainty level in the reference model
    #   v_mu       : [matrix] (k_ x n_) pick matrix specifying views
    #   mu_view    : [vector] (k_ x 1) vector quantifying views
    #   sig2_view  : [matrix] (k_ x k_) matrix of confidence level of views
    #  OPS
    #   BLmu       : [vector] (n_ x 1) updated expectation
    #   BLs2       : [matrix] (n_ x n_) updated covariance matrix

    # For details on the exercise, see here .

    ## Code
    pos = (1/tau)*v_mu@s2_@v_mu.T + sig2_view

    BLmu = mu_ + (1/tau)*s2_@v_mu.T@solve(pos,mu_view - v_mu@mu_)
    BLs2 = (1 + (1/tau))*s2_ - (1/tau)**2*s2_@v_mu.T@solve(pos,v_mu@s2_)
    return BLmu, BLs2


'''
Monty Hall Simulation in Python
as in: https://en.wikipedia.org/wiki/Monty_Hall_problem.

Particular implementation by Joel Stremmel
and licensed under the MIT license on July 28th, 2020.
'''

import random
import numpy as np

def simulate(switch, n_doors):

	# create doors numbered 1 to the number of doors
	doors = list(range(1, n_doors + 1))

	# randomly select the door that has the car, the players door, and the door revealed by Monty
	# note that Monty only reveals a door which is known to not contain the car
	door_with_car = random.choice(doors)
	door_selected_by_player = random.choice(doors)
	door_opened_by_monty = random.choice([door for door in doors if door not in [door_with_car, door_selected_by_player]])

	# apply switching strategy
	if switch:
		new_door_selected_by_player = random.choice([door for door in doors if door not in [door_selected_by_player, door_opened_by_monty]])

	# apply keeping strategy
	else:
		new_door_selected_by_player = door_selected_by_player

	# return a win as 1 and loss as 0
	return int(new_door_selected_by_player == door_with_car)

if __name__ == '__main__':

	SWITCH = True
	N_DOORS = 3
	N_SIMULATIONS = 10000

	results = [simulate(SWITCH, N_DOORS) for _ in range(N_SIMULATIONS)]
	win_pct = round(np.mean(results), 5) * 100.0

	print(f'Win percentage with {N_DOORS} doors, {N_SIMULATIONS} simulations, and SWITCH={SWITCH}: {win_pct}%.')


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

import numpy as np
import numpy.testing as npt
import pyss.util.coefficient as coeff

h = 0.1

class TestSSIntegral(npt.TestCase):
    def test_newton_cotes_coeff_degree_1(self):
        # Sampling points = 2
        x = coeff.newton_cotes_coeff(h, 1)
        y = np.array([1, 1]) / 2 * h
        npt.assert_array_almost_equal(x, y)

    def test_newton_cotes_coeff_degree_2(self):
        # Sampling points = 3
        x = coeff.newton_cotes_coeff(h, 2)
        y = np.array([1, 4, 1]) / 3 * h
        npt.assert_array_almost_equal(x, y)

    def test_newton_cotes_coeff_degree_3(self):
        # Sampling points = 4
        x = coeff.newton_cotes_coeff(h, 3)
        y = np.array([1, 3, 3, 1]) * 3 / 8 * h
        npt.assert_array_almost_equal(x, y)

    def test_newton_cotes_coeff_degree_6(self):
        # Sampling points = 7
        x = coeff.newton_cotes_coeff(h, 6)
        y = np.array([41, 216, 27, 272, 27, 216, 41]) * 1 / 140 * h
        npt.assert_array_almost_equal(x, y)

    def test_composite_newton_cotes_coeff_degree_1(self):
        # line integral
        n = 13
        x = coeff.composite_newton_cotes_coeff(h, 1, n, tr=None, contour=False)
        y = d1_coeff(h, n)
        npt.assert_array_almost_equal(x, y)
        # contour integral
        n = 12
        x = coeff.composite_newton_cotes_coeff(h, 1, n, tr=None, contour=True)
        y = d1_coeff(h, n + 1)
        y[0] = y[0] + y[n]
        y = y[0:n]
        npt.assert_array_almost_equal(x, y)

    def test_composite_newton_cotes_coeff_degree_2(self):
        # line integral
        n = 13
        x = coeff.composite_newton_cotes_coeff(h, 2, n, tr=None, contour=False)
        y = d2_coeff(h, n)
        npt.assert_array_almost_equal(x, y)
        # contour integral
        n = 12
        x = coeff.composite_newton_cotes_coeff(h, 2, n, tr=None, contour=True)
        y = d2_coeff(h, n + 1)
        y[0] = y[0] + y[n]
        y = y[0:n]
        npt.assert_array_almost_equal(x, y)

    def test_composite_newton_cotes_coeff_degree_3(self):
        # line integral
        n = 13
        x = coeff.composite_newton_cotes_coeff(h, 3, n, tr=None, contour=False)
        y = d3_coeff(h, n)
        npt.assert_array_almost_equal(x, y)
        # contour integral
        n = 12
        x = coeff.composite_newton_cotes_coeff(h, 3, n, tr=None, contour=True)
        y = d3_coeff(h, n + 1)
        y[0] = y[0] + y[n]
        y = y[0:n]
        npt.assert_array_almost_equal(x, y)

    def test_composite_newton_cotes_coeff_degree_6(self):
        n = 13
        x = coeff.composite_newton_cotes_coeff(h, 6, n, tr=None, contour=False)
        y = d6_coeff(h, n)
        npt.assert_array_almost_equal(x, y)
        # contour integral
        n = 12
        x = coeff.composite_newton_cotes_coeff(h, 6, n, tr=None, contour=True)
        y = d6_coeff(h, n + 1)
        y[0] = y[0] + y[n]
        y = y[0:n]
        npt.assert_array_almost_equal(x, y)

def d1_coeff(h, n):
    ws = np.empty(n)
    for i in range(n):
        if i == 0 or i == (n - 1):
            ws[i] = 1.0
        else:
            ws[i] = 2.0
    return 1 / 2 * h * ws

def d2_coeff(h, n):
    ws = np.empty(n)
    for i in range(n):
        if i == 0 or i == (n - 1):
            ws[i] = 1.0
        elif i % 2 == 0:
            ws[i] = 2.0
        else:
            ws[i] = 4.0
    return 1 / 3 * h * ws

def d3_coeff(h, n):
    ws = np.empty(n)
    for i in range(n):
        if i == 0 or i == (n - 1):
            ws[i] = 1.0
        elif i % 3 == 0:
            ws[i] = 2.0
        else:
            ws[i] = 3.0
    return 3 / 8 * h * ws

def d4_coeff(h, n):
    ws = np.empty(n)
    for i in range(n):
        if i == 0 or i == (n - 1):
            ws[i] = 7.0
        elif i % 4 == 0:
            ws[i] = 14.0
        elif i % 4 == 2:
            ws[i] = 12.0
        else:
            ws[i] = 32.0
    return 2 / 45 * h * ws

def d6_coeff(h, n):
    ws = np.empty(n)
    for i in range(n):
        if i == 0 or i == (n - 1):
            ws[i] = 41.0
        elif i % 6 == 0:
            ws[i] = 82.0
        elif i % 6 == 1 or i % 6 == 5:
            ws[i] = 216.0
        elif i % 6 == 2 or i % 6 == 4:
            ws[i] = 27.0
        else:
            ws[i] = 272.0
    return 1 / 140 * h * ws


import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets, neighbors

from matplotlib.colors import ListedColormap

def knn_comparison(data, n_neighbors = 15):
    '''
    This function finds k-NN and plots the data.
    '''
    X = data[:, :2]
    y = data[:,2]

    # grid cell size
    h = .02
    cmap_light = ListedColormap(['#FFAAAA',  '#AAAAFF'])
    cmap_bold = ListedColormap(['#FF0000', '#0000FF'])

    # the core classifier: k-NN
    clf = neighbors.KNeighborsClassifier(n_neighbors)
    clf.fit(X, y)

    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1

    # we create a mesh grid (x_min,y_min) to (x_max y_max) with 0.02 grid spaces
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))

    # we predict the value (either 0 or 1) of each element in the grid
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

    # xx.ravel() will give a flatten array

    # np.c_ : Translates slice objects to concatenation along the second axis.
    # > np.c_[np.array([1,2,3]), np.array([4,5,6])]
    # > array([[1, 4],
    #          [2, 5],
    #          [3, 6]])   (source: np.c_ documentation)

    # convert the out back to the xx shape (we need it to plot the decission boundry)
    Z = Z.reshape(xx.shape)

    # pcolormesh will plot the (xx,yy) grid with colors according to the values of Z
    # it looks like decision boundry
    plt.figure()
    plt.pcolormesh(xx, yy, Z, cmap=cmap_light)

    # scatter plot of with given points
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold)

    #defining scale on both axises
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())

    # set the title
    plt.title('K value = '+str(n_neighbors))
    plt.show()

# https://stackoverflow.com/questions/36013063/what-is-the-purpose-of-meshgrid-in-python-numpy/36014586#36014586

data = np.genfromtxt('dataset/4.linearsep.csv', delimiter=',')
knn_comparison(data, 1)
knn_comparison(data, 5)
knn_comparison(data,15)
knn_comparison(data, 30)
knn_comparison(data, 50)


import numpy as np

def adam_step(i, g, params, alpha=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
    """
    Implements the ADAM optimization algorithm according to: https://arxiv.org/abs/1412.6980

    :param i: the current iteration index
    :param g: the gradient of the weight
    :param params: a parameter dictionary used to store the momentum
    :param alpha: learning rate
    :param beta1: 1st momentum decay rate
    :param beta2: 2nd momentum decay rate
    :param eps: small epsilon to avoid division by zero
    :return: the correction term to update the parameter the gradient belongs to
    """

    # Calculate the ADAM momenta
    mi = params['mi']
    vi = params['vi']

    mi = beta1 * mi + (1 - beta1) * g
    vi = beta2 * vi + (1 - beta2) * (g ** 2)
    mi_ = mi / (1 - beta1 ** (i+1))
    vi_ = vi / (1 - beta2 ** (i+1))

    # Update
    params['mi'] = mi
    params['vi'] = vi

    return alpha * mi_ / (np.sqrt(vi_) + eps)

def gradient_descent_step(g, learning_rate=0.00001):
    """
    simple gradient descent
    :param g: the gradient
    :param learning_rate: the learning rate
    :return: the corrective term for the parameters the gradient belongs to
    """
    return learning_rate * g


import numpy as np
import random

def data_circle(num_samples, noise):
    """
    Generates the two circles dataset with the given number of samples and noise
    :param num_samples: total number of samples
    :param noise: noise percentage (0 .. 50)
    :return: None
    """
    radius = 5

    def get_circle_label(x, y, xc, yc):
        return 1 if np.sqrt((x - xc)**2 + (y - yc)**2) < (radius * 0.5) else 0

    noise *= 0.01
    points = np.zeros([num_samples, 2])
    labels = np.zeros(num_samples).astype(int)
    # Generate positive points inside the circle.
    for i in range(num_samples // 2):
        r = random.uniform(0, radius * 0.5)
        angle = random.uniform(0, 2 * np.pi)
        x = r * np.sin(angle)
        y = r * np.cos(angle)
        noise_x = random.uniform(-radius, radius) * noise
        noise_y = random.uniform(-radius, radius) * noise
        labels[i] = get_circle_label(x + noise_x, y + noise_y, 0, 0)
        points[i] = (x, y)
    # Generate negative points outside the circle.
    for i in range(num_samples // 2, num_samples):
        r = random.uniform(radius * 0.7, radius)
        angle = random.uniform(0, 2 * np.pi)
        x = r * np.sin(angle)
        y = r * np.cos(angle)
        noise_x = random.uniform(-radius, radius) * noise
        noise_y = random.uniform(-radius, radius) * noise
        labels[i] = get_circle_label(x + noise_x, y + noise_y, 0, 0)
        points[i] = (x, y)
    return points, labels

def data_spiral(num_samples, noise):
    """
    Generates the spiral dataset with the given number of samples and noise
    :param num_samples: total number of samples
    :param noise: noise percentage (0 .. 50)
    :return: None
    """
    noise *= 0.01
    '''
    half = num_samples // 2
    points = np.zeros([num_samples, 2])
    labels = np.zeros(num_samples).astype(int)
    for j in range(num_samples):
        i = j % half
        label = 1
        delta = 0
        if j >= half:  # negative examples
            label = 0
            delta = np.pi
        r = i / half * 5
        t = 1.75 * i / half * 2 * np.pi + delta
        x = r * np.sin(t) + random.uniform(-1, 1) * noise
        y = r * np.cos(t) + random.uniform(-1, 1) * noise
        labels[j] = label
        points[j] = (x, y)
    '''
    n = np.sqrt(np.random.rand(num_samples,1)) * 780 * (2*np.pi)/360
    d1x = -np.cos(n)*n + np.random.rand(num_samples,1) * noise
    d1y = np.sin(n)*n + np.random.rand(num_samples,1) * noise
    points = np.vstack((np.hstack((d1x,d1y)),np.hstack((-d1x,-d1y))))
    labels = np.hstack((np.zeros(num_samples, dtype=np.int64),np.ones(num_samples, dtype=np.int64)))

    return points, labels

def grid_points():
    """
    Generates grid points as the test set
    """
    x_min, x_max = -15, 15  # grid x bounds
    y_min, y_max = -15, 15  # grid y bounds
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 200),
                         np.linspace(y_min, y_max, 200))
    x = np.c_[xx.ravel(),yy.ravel()]
    y = np.ones(shape=x.shape[0], dtype=np.int64)

    return x, y, xx, yy


import os
import sys

above_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, above_dir)

from tools import ProgressIterator, DataML, get_y, linear_percepton, output
import numpy as np

from math import ceil
from sympy import exp, log, sqrt, power
from sympy import Eq
from sympy import Symbol
from sympy import nsolve
from sympy import plot_implicit
from sympy.plotting import plot
from decimal import Decimal

from scipy.integrate import quad

np.random.seed(0)

# GENERALIZATION ERROR

def solved_vc_inequality(probability, error, approximate_datapoints_needed):
    n = Symbol('n')
    return nsolve(log(4) + 10 * log(2*n) - 1/8 * error ** 2 * n - log(probability), n, approximate_datapoints_needed)

def generate_growth_function_bound(d_vc):
    def growth_function_bound(n):
        return n ** d_vc
    return growth_function_bound

def original_vc_bound(n, delta, growth_function):
    return sqrt(8/n * log(4*growth_function(2*n)/delta))

def rademacher_penalty_bound(n, delta, growth_function):
    return sqrt(2 * log(2 * n * growth_function(n)) / n) + sqrt(2/n * log(1/delta)) + 1/n

def parrondo_van_den_broek_right(error, n, delta, growth_function):
    return sqrt(1/n * (2 * error + log(6 * growth_function(2*n)/delta)))

def devroye(error, n, delta, growth_function):
    return sqrt(1/(2*Decimal(n)) * (4 * error * (1 + error) + log(4 * growth_function(Decimal(n**2))/Decimal(delta))))

def error_bound(n):
    e = Symbol('e')
    y = Symbol('y')
    growth_function_bound = generate_growth_function_bound(50)
    a = original_vc_bound(n, 0.05, growth_function_bound)
    b = nsolve(Eq(rademacher_penalty_bound(n, 0.05, growth_function_bound), y), 1)
    c = nsolve(Eq(parrondo_van_den_broek_right(e, n, 0.05, growth_function_bound), e), 1)
    d = nsolve(Eq(devroye(e, n, 0.05, growth_function_bound), e), 1)
    return a, b, c, d

# def test_three():
#     e = Symbol('e')
#     y = Symbol('y')
#     n = Symbol('n')
#     growth_function_bound = generate_growth_function_bound(50)
#     a = original_vc_bound(5, 0.05, growth_function_bound)
#     b = nsolve(Eq(rademacher_penalty_bound(5, 0.05, growth_function_bound), y), 5)
#     c = nsolve(Eq(parrondo_van_den_broek_right(e, 5, 0.05, growth_function_bound), e), 1)
#     d = nsolve(Eq(devroye(e, 5, 0.05, growth_function_bound), e), 1)
#     return a, b, c, d

def plot():
    e = Symbol('e')
    y = Symbol('y')
    n = Symbol('n')
    generalized_vc_bounds = (original_vc_bound, rademacher_penalty_bound)
    growth_function_bound = generate_growth_function_bound(50)
    p1 = plot(original_vc_bound(n, 0.05, growth_function_bound), (n,100, 15000), show=False, line_color = 'black')
    p2 = plot(rademacher_penalty_bound(n, 0.05, growth_function_bound), (n,100, 15000), show=False, line_color = 'blue')
    plot_implicit(Eq(e, parrondo_van_den_broek_right(e, n, 0.05, growth_function_bound)), (n,100, 15000), (e,0,5))
    # plot_implicit(Eq(e, devroye(e, n, 0.05, growth_function_bound)), (n,100, 1000), (e,0,5))
    p1.extend(p2)
    p1.show()

# BIAS AND VARIANCE

def sinusoid_over_axis(x):
    return np.sin(np.pi * x[0])

def sinusoid(x):
    return np.sin(np.pi * x)

def random_x_points(n):
    '''
    [[  0.09762701]
     [  0.20552675]
     [ -0.1526904 ]
     [ -0.12482558]
     [  0.92732552]
     [  0.58345008]
     [  0.13608912]
     [ -0.85792788]]
    '''
    return np.random.uniform(-1,1, size=(n,1))

def gen_data(n, target_function, transform=None):
    x = random_x_points(n)
    y = get_y(target_function, x)
    if transform == None:
        return DataML((x, y))
    else:
        return DataML((x, y), transform)

def bias_variance_out_sample_error(n):
    """
    Out of the following hypothesis sets
    h(x) = b
    h(x) = ax
    h(x) = ax + b
    h(x) = ax**2
    h(x) = ax**2 + b
    which has the least out of sample error, when
    the target function is sin(pi * x).
    The hypothesis error is made out of two components,
    the bias and variance.
    The bias is equal to the following
    bias = E_x[(g_mean(x) - f(x)) ** 2]
    variance = E_x[E_d[(g^(d)(x) - g_mean(x))**2]]
    error = bias plus variance.
    """
    trials = [ gen_data(2, sinusoid_over_axis) for _ in range(n) ]
    hypothesis_functions = (
            (constant, get_constant_parameters),
            (line, get_line_to_parameters),
            (line, get_line_parameters),
            (quadratic, generate_quadratic_to_parameters),
            (quadratic, generate_quadratic_parameters),
            )
    analysis  = [ analyse(hypothesis, get_parameters, trials) for hypothesis, get_parameters in hypothesis_functions ]
    # print('argmin', ',', 'min')
    # print(np.argmin(out_of_sample_errors), ',', min(out_of_sample_errors))
    return analysis

def analyse(hypothesis, get_parameters, trials):
    parameters = get_parameters(trials)
    # print('parameters)
    # print(parameters)
    mean_parameters = np.mean(parameters, axis=0)
    # print('mean_parameters')
    # print(mean_parameters)
    bounds = (-1,1)
    bias = expected_bias(hypothesis, sinusoid, bounds, mean_parameters)
    # print('expected_bias')
    # print(bias)
    variance = expected_variance(hypothesis, bounds, parameters, mean_parameters)
    # print('expected_variance')
    # print(variance)
    return {"mean parameters" : mean_parameters,
            "bias" : bias,
            "variance" : variance,
            "expected out of sample error" : bias + variance
            }

def expected_bias(hypothesis, target_function, bounds, mean_parameters):
    sq_error = generate_sq_error(hypothesis, target_function)
    return expected_value_integral(sq_error, bounds, (mean_parameters, ))

def expected_variance(hypothesis, bounds, parameters_list, mean_parameters):
    """
    variance = E_x[E_d[(g^(d)(x) - g_mean(x))**2]]
    variance = E_d[E_x[(g^(d)(x) - g_mean(x))**2]]
    """
    # print('parameters_list')
    # print(parameters_list)
    individual_variances = [ expected_value_integral(
            generate_sq_error(hypothesis, hypothesis),
            bounds, (parameters, mean_parameters)
        )
            for parameters in parameters_list ]
    # print('individual_variances')
    # print(individual_variances)
    return np.mean(individual_variances)

def expected_value_integral(func, bounds, parameters):
    return quad(func, *bounds, parameters)[0] / (bounds[1] - bounds[0])

def generate_sq_error(func1, func2):
    def sq_error(x, func1_parameters=[], func2_parameters=[]):
        # print('func1_parameters')
        # print(func1_parameters)
        # print('func2_parameters')
        # print(func2_parameters)
        return (func1(x, *func1_parameters) - func2(x, *func2_parameters)) ** 2
    return sq_error

def constant(x, c):
    return c

def line(x, gradient, constant=0):
    return gradient * x + constant

def quadratic(x, a, b=0):
    return a * x ** 2 + b

def get_constant_parameters(trials):
    """ put each parameter in a list for uniformity with other parameter functions"""
    return np.array([ [np.mean(training_set.y)] for training_set in trials ])

def get_line_to_parameters(trials):
    """line_to : line through origin"""
    gradients = np.array([ linear_percepton(training_set.x, training_set.y) for training_set in trials ])
    return gradients

def get_line_parameters(trials):
    new_trials = [
            DataML((np.insert(training_set.z, 0, 1, axis=1), training_set.y))
        for training_set in trials ]
    weights = np.array([ linear_percepton(training_set.z, training_set.y) for training_set in new_trials ])
    return weights

def generate_quadratic_to_parameters(trials):
    """line_to : line through origin
       function of form ax^2
    """
    def transform(x):
        return x ** 2

    new_trials = [ DataML((training_set.z, training_set.y), transform)
        for training_set in trials ]
    weights = np.array([ linear_percepton(training_set.x, training_set.y) for training_set in new_trials ])
    return weights

def generate_quadratic_parameters(trials):
    """ax^2 + b"""
    def transform(x):
        """
        transform
        x1  --->   1 x1**2
        """
        ones = np.ones(len(x))
        x1 = x[:, 0]
        x1_sqr = x1 ** 2
        return np.stack([ones, x1_sqr], axis=1)

    new_trials = [
            DataML((training_set.z, training_set.y), transform)
        for training_set in trials ]
    weights = [ linear_percepton(training_set.z, training_set.y) for training_set in new_trials ]
    return np.array(weights)

def main():
    output(simulations)

def simulations():
    que ={}
    progress_iterator = ProgressIterator(5)
    progress_iterator.next()
    sample_size = ceil(solved_vc_inequality(1 - 0.95, 0.05, 400000))
    que[1] = ("sample size needed :", sample_size)
    def error_bound_format(n):
        original_vc_bound, rademacher_penalty_bound, parrondo_van_den_broek_bound, devroye_bound = error_bound(n)
        output = ("Bounds for N=" + str(n),
            "\noriginal vc : " + str(original_vc_bound)
            + "\n" + "rademacher penalty : " + str(rademacher_penalty_bound)
            + "\n" + "parrondo and van den broek : " + str(parrondo_van_den_broek_bound)
            + "\n" + "devroye : "  + str(devroye_bound)
            + "\n"
            )
        return output

    progress_iterator.next()
    que[2] = error_bound_format(10000)

    progress_iterator.next()
    que[3] = error_bound_format(5)

    progress_iterator.next()
    analysis = bias_variance_out_sample_error(1000)
    def bias_variance_format(analysis):
        names = [ "constant : a",
                "\n\nline through origin : ax",
                "\n\nline : ax + b",
                "\n\nquadratic through origin : ax**2",
                "\n\nquadratic : ax**2 + b"]
        output = ""
        for i in range(len(analysis)):
            if i == 1:
                output += names[i] \
                        + "\nmean parameters : " + str(analysis[i]["mean parameters"]) + " # ans to question 4 this differs from solution given" \
                        + "\nbias : " + str(analysis[i]["bias"]) + " # ans to question 5" \
                        + "\nvariance : " + str(analysis[i]["variance"]) + " # ans to question 6" \
                        + "\nexpected out of sample error : " + str(analysis[i]["expected out of sample error"])
            else:
                output += names[i] \
                        + "\nmean parameters : " + str(analysis[i]["mean parameters"]) \
                        + "\nbias : " + str(analysis[i]["bias"]) \
                        + "\nvariance : " + str(analysis[i]["variance"]) \
                        + "\nexpected out of sample error : " + str(analysis[i]["expected out of sample error"])
        output += "\n\nbest hypothesis is 'line throgh origin' with an expected out of sample error of " + str(round(analysis[1]["expected out of sample error"], 3))
        return output

    progress_iterator.next()
    que[4] = ("Also includes answers to question 5,6,7\n\nAnalysis of various hypotheses",
            "\n" + str(bias_variance_format(analysis)))
    return que

ans = {
        1 : 'd',
        2 : 'd',
        3 : 'c',
        4 : 'e?d',
        5 : 'b',
        6 : 'a',
        7 : 'b',
        8 : 'c',
        9 : 'b',
        10 : 'dxe',
        }

if __name__ == '__main__':
    main()


import numpy as np

def partitionrnd(n, prop, g, seed=2):
	"""Generate a random partition.
	n    : number of instances
	prop : proportion of clusters
	g    : number of clusters
	"""
	np.random.seed(seed)
	tmp = np.random.uniform(low=0.0, high=1.0, size=n)
	return g + 1 - np.sum(np.tile(tmp, (g, 1)).T < np.tile(np.cumsum(prop), (n, 1)), axis=1)

def generatedata(mu, n=100, d=100, prop_r=None, prop_c=None, noise=1, sigma = None, seed=2):
	"""Generate a data matrix with a gxm bloc structure.
	n      : number of rows
	d      : number of columns
	prop_r : proportion of row clusters
	prop_c : proportion of column clusters
	g      : number of row clusters
	m      : number of column clusters
	mu     : a gxm matrix with the mean of each block
	Return the matrix and the partitions
	"""
	np.random.seed(seed)
	g, m = mu.shape
	if prop_r is None:
		prop_r = (1 / g * np.ones(g))
	if prop_c is None:
		prop_c = (1 / m * np.ones(m))
	if sigma is None:
		s = (g, m)
		sigma = 0.1 * np.ones(s) * noise

	# Generate one partition for instances and one for variables
	z_i = partitionrnd(n, prop_r, g)
	w_j = partitionrnd(d, prop_c, m)

	# Generate the data matrix x
	s = (n, d)
	data = np.zeros(s)

	for i in range(n):
		for j in range(d):
			data[i][j] = np.random.normal(mu.item(z_i[i] - 1, w_j[j] - 1), sigma.item(z_i[i] - 1, w_j[j] - 1), 1)[0]

	return data, z_i, w_j