import numpy as np

def softmax(predictions):
    """
    Computes probabilities from scores

    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output

    Returns:
      probs, np array of the same shape as predictions -
        probability for every class, 0..1
    """

    preds = np.array(predictions.copy())

    if preds.ndim > 1:
        preds -= np.max(preds, axis=1).reshape(-1, 1)
        proba = np.exp(preds) / np.sum(np.exp(preds), axis=1).reshape(-1, 1)
    else:
        preds -= np.max(preds)
        proba = np.exp(preds) / np.sum(np.exp(preds))

    return proba

def cross_entropy_loss(probs, target_index):
    """
    Computes cross-entropy loss

    Arguments:
      probs: np array, shape is either (N) or (batch_size, N) -
        probabilities for every class
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss: single value
    """

    if probs.ndim > 1:
        true_probs = probs[np.arange(len(target_index)), target_index.reshape(-1)]
    else:
        true_probs = probs[target_index]

    loss = np.sum(-np.log(true_probs))
    return loss

def softmax_with_cross_entropy(predictions, target_index):
    """
    Computes softmax and cross-entropy loss for model predictions,
    including the gradient

    Arguments:
      predictions: np array, shape is either (N) or (batch_size, N) -
        classifier output
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss: single value - cross-entropy loss
      dprediction: np array same shape as predictions - gradient of predictions by loss value
    """

    # TODO implement softmax with cross-entropy

    if predictions.ndim > 1:
        mask = np.zeros(predictions.shape)
        mask[np.arange(len(target_index)), target_index.reshape(-1)] = 1
    else:
        mask = np.zeros(predictions.shape)
        mask[target_index] = 1

    probs = softmax(predictions.copy())
    loss = cross_entropy_loss(probs, target_index)
    dpredictions = probs - mask

    return loss, dpredictions

def l2_regularization(W, reg_strength):
    """
    Computes L2 regularization loss on weights and its gradient

    Arguments:
      W: np array, weights
      reg_strength: float value

    Returns:
      loss: single value - l2 regularization loss
      gradient: np.array same shape as W - gradient of weight by l2 loss
    """

    w = W.copy()
    loss = np.sum(w * w) * reg_strength
    grad = w * 2 * reg_strength

    return loss, grad

def linear_softmax(X, W, target_index):
    """
    Performs linear classification and returns loss and gradient over W

    Arguments:
      X, np array, shape (num_batch, num_features) - batch of images
      W, np array, shape (num_features, classes) - weights
      target_index, np array, shape (num_batch) - index of target classes

    Returns:
      loss, single value - cross-entropy loss
      gradient, np.array same shape as W - gradient of weight by loss
    """

    predictions = np.dot(X, W)
    loss, dpred = softmax_with_cross_entropy(predictions, target_index)
    dW = X.T.dot(dpred)
    return loss, dW

class LinearSoftmaxClassifier():
    def __init__(self):
        self.W = None

    def fit(self, X, y, batch_size=100, learning_rate=1e-7, reg=1e-5, epochs=1):
        """
        Trains linear classifier

        Arguments:
          X, np array (num_samples, num_features) - training data
          y, np array of int (num_samples) - labels
          batch_size, int - batch size to use
          learning_rate, float - learning rate for gradient descent
          reg, float - L2 regularization strength
          epochs, int - number of epochs
        """

        num_train = X.shape[0]
        num_features = X.shape[1]
        num_classes = np.max(y)+1
        if self.W is None:
            self.W = 0.001 * np.random.randn(num_features, num_classes)

        loss_history = []
        for epoch in range(epochs):
            loss = 0
            shuffled_indices = np.arange(num_train)
            np.random.shuffle(shuffled_indices)
            sections = np.arange(batch_size, num_train, batch_size)
            batches_indices = np.array_split(shuffled_indices, sections)

            for batch in batches_indices:
                loss_softmax, dw_softmax = linear_softmax(X[batch], self.W, y[batch])
                loss_reg, dw_reg = l2_regularization(self.W, reg)
                loss += (loss_softmax + loss_reg)
                self.W -= learning_rate * (dw_softmax + dw_reg)
            loss_history.append(loss)
            # end
            if epoch % 100 == 0:
                print("Epoch %i, loss: %f" % (epoch, loss))

        return loss_history

    def predict(self, X):
        """
        Produces classifier predictions on the set

        Arguments:
          X, np array (test_samples, num_features)

        Returns:
          y_pred, np.array of int (test_samples)
        """

        y_pred = np.zeros(X.shape[0], dtype=np.int)

        # TODO Implement class prediction
        # Your final implementation shouldn't have any loops
        pred = np.argmax(softmax(X.dot(self.W)), axis=1)

        return pred


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
import random

# xs = np.array([1, 2, 3, 4, 5, 6], dtype=np.float64)
# ys = np.array([5, 4, 6, 5, 6, 7], dtype=np.float64)

# This will be used to create random data sets
def create_dataset(size, varience, step=2, correlation=False):
    val = 0
    ys = []

    # Assign random values to the ys list
    for i in range(size):
        y = val + random.randrange(-varience, varience)
        ys.append(y)

        if correlation and correlation == 'pos':
            val += step
        elif correlation and correlation == 'neg':
            val -= step

    xs = [i for i in range(len(ys))]

    return np.array(xs, dtype=np.float64), np.array(ys, dtype=np.float64)

def best_fit_slope_and_intercept(xs, ys):
    m = (((mean(xs) * mean(ys)) - mean(xs * ys)) /
         (mean(xs) * mean(xs) - mean(xs * xs)))

    b = mean(ys) - m * mean(xs)
    return m, b

def squared_error(ys_orig, ys_line):
    return sum((ys_line - ys_orig) ** 2)

# This is meant to tell us how good of a fit the line
# We made is
def coefficient_of_determination(ys_orig, ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_regr = squared_error(ys_orig, ys_line)
    squared_error_y_mean = squared_error(ys_orig, y_mean_line)

    return 1 - (squared_error_regr / squared_error_y_mean)

# Create our data set
xs, ys = create_dataset(1000, 100, 2, correlation='pos')

# Make the line of best fit
m, b = best_fit_slope_and_intercept(xs, ys)
regression_line = []

for x in xs:
    regression_line.append((m * x) + b)

# This is how we know how good of a fit the line actually is
r_squared = coefficient_of_determination(ys, regression_line)
print(r_squared)

# Show the graph
plt.scatter(xs, ys, s=10)
plt.plot(xs, regression_line)
plt.show()


"""
This code implements a K-means and EM Gaussian mixture models per week 8 assignment of the machine learning module part of Columbia University Micromaster programme in AI.
Written using Python 3.7 for running on Vocareum
Version run on Vocareum for grading (all docs removed to avoid issues with grading platform)

Execute as follows:
$ python3 hw3_clustering.py X.csv
"""

# builtin modules
import sys
import os
import math
from random import randrange
import functools
import operator
import requests
import psutil

# 3rd party modules
import numpy as np
import pandas as pd
import scipy as sp
from scipy.cluster.vq import kmeans2
from scipy.stats import multivariate_normal
from scipy.spatial.distance import cdist
from scipy.special import logsumexp
from scipy import stats

def KMeans(data, k, iterations, **kwargs):
    #data = data.to_numpy() # Convert dataframe to np array
    centroids_list = []
    labels_list = []

    for i in range(iterations):
        centroids, label = kmeans2(data, k, iter = i+1, minit='points')
        centroids_list.append(centroids)
        labels_list.append(label)
        filename = "centroids-" + str(i+1) + ".csv" #"i" would be each iteration
        if 'path' in kwargs:
            path = kwargs['path']
            filepath = os.path.join(path, filename)
            np.savetxt(filepath, centroids, delimiter=",")
        else:
            path = os.getcwd() # os.path.join(os.getcwd(), "outputs")
            filepath = os.path.join(path, filename)
            np.savetxt(filepath, centroids, delimiter=",")

    return centroids_list, labels_list

def calculate_mean_covariance(data, centroids, labels):

    # initialize the output ndarrays with zeros, for filling up in loops below
    d = data.shape[1]
    k = len(centroids[0]) # to take number of clusters directly from KMeans
    initial_pi = np.zeros(k)
    initial_mean = np.zeros((k, d))
    initial_sigma = np.zeros((k, d, d))

    # ensure data is dataframe from np.ndarray
    data = pd.DataFrame(data=data)

    # get the number of iterations from the lenght of centroids (or labels)
    iterations = len(centroids)

    for i in range(iterations):
        iter_centroid = centroids[i]
        iter_labels = labels[i]
        # initialize counter to organize outputs per cluster k (counter = cluster_labels if the latter are int type ranging from 0 to k)
        counter=0
        for cluster_label in np.unique(iter_labels):
            # returns indices of rows estimated to belong to the cluster
            ids = np.where(iter_labels == cluster_label)[0]
            # calculate pi = nk / n for cluster k
            nk = data.iloc[ids].shape[0] # number of data points in current gaussian/cluster
            n = data.shape[0] # total number of points/rows in dataset
            initial_pi[counter] = nk / n

            # calculate mean (mu) of points estimated to be in cluster k (k)
            initial_mean[counter,:] = np.mean(data.iloc[ids], axis = 0)
            de_meaned = data.iloc[ids] - initial_mean[counter,:]

            # calculate covariance (sigma) of points estimated to be in cluster k (k)
            initial_sigma[counter, :, :] = np.dot(initial_pi[counter] * de_meaned.T, de_meaned) / nk

            counter+=1

        #assert np.sum(initial_pi) == 1

    return (initial_pi, initial_mean, initial_sigma)

def initialise_parameters(data, k, iterations):

    centroids, labels =  KMeans(data, k, iterations)

    (initial_pi, initial_mean, initial_sigma) = calculate_mean_covariance(data, centroids, labels)

    return (initial_pi, initial_mean, initial_sigma)

def e_step(data, pi, mu, sigma):

    n = data.shape[0]
    k = len(pi)
    gamma = np.zeros((n, k))

    # convert np.nan to float(0.0)
    pi = np.nan_to_num(pi)
    mu = np.nan_to_num(mu)
    sigma = np.nan_to_num(sigma)

    x = data #.to_numpy() # Convert dataframe to np array

    for cluster in range(k):
        # Posterior Distribution using Bayes Rule
        gamma[ : , cluster] = pi[cluster] * multivariate_normal(mean=mu[cluster,:], cov=sigma[cluster], allow_singular=True).pdf(x)

    gamma = np.nan_to_num(gamma) # convert np.nan to float(0.0)
    # normalize across columns to make a valid probability
    gamma_norm = np.sum(gamma, axis=1)[ : , np.newaxis]
    gamma_norm = np.nan_to_num(gamma_norm)

    # avoid issues with divide by zero of ndarray
    gamma = np.divide(gamma, gamma_norm, out=np.zeros_like(gamma), where=gamma_norm!=0)
    #gamma /= gamma_norm

    return np.nan_to_num(gamma)

def m_step(data, gamma, sigma):

    n = data.shape[0] # number of datapoints (rows in the dataset), equivalent of gamma.shape[0]
    k = gamma.shape[1] # number of clusters
    d = data.shape[1] # number of features (columns in the dataset)

    # convert np.nan to float(0.0)
    gamma = np.nan_to_num(gamma)
    sigma = np.nan_to_num(sigma)

    # calculate pi and mu for each Gaussian
    pi = np.mean(gamma, axis = 0)
        # avoid issues with divide by zero of ndarray
    #mu = dividend / divisor
    dividend = np.dot(gamma.T, data)
    divisor = np.sum(gamma, axis = 0)[:,np.newaxis]
    mu = np.divide(dividend, divisor, out=np.zeros_like(dividend), where=divisor!=0)

    x = data #.to_numpy() # Convert dataframe to np array

    # update sigma for each Gaussian
    for cluster in range(k):
        x = x - mu[cluster, :] # (shape: n, d)
        gamma_diag = np.diag(gamma[: , cluster])
        x_mu = np.matrix(x)
        gamma_diag = np.matrix(gamma_diag)

        sigma_cluster = x.T * gamma_diag * x
        gamma = np.nan_to_num(gamma) # convert np.nan to float(0.0)
        #sigma = dividend / divisor
        dividend = (sigma_cluster)
        divisor = np.sum(gamma, axis = 0)[:,np.newaxis][cluster]
        sigma[cluster,:,:] = np.divide(dividend, divisor, out=np.zeros_like(dividend), where=divisor!=0)

    return pi, mu, sigma

def predict(data, pi, mu, sigma, k):

    n = data.shape[0] # number of datapoints (rows in the dataset)
    labels = np.zeros((n, k))

    # convert np.nan to float(0.0)
    pi = np.nan_to_num(pi)
    mu = np.nan_to_num(mu)
    sigma = np.nan_to_num(sigma)

    #x = data.to_numpy() # Convert dataframe to np array

    for cluster in range(k):
        labels [:,cluster] = pi[cluster] * multivariate_normal(mean=mu[cluster,:], cov=sigma[cluster], allow_singular=True).pdf(data)
    labels  = labels .argmax(1)

    return labels

def EMGMM(data, k, iterations, **kwargs):

    if 'tol' in kwargs:
        tol = kwargs['tol']
    else:
        tol = 1e-6

    d = data.shape[1] # number of features (columns in the dataset)
    n = data.shape[0] # number of datapoints (rows in the dataset)

    x = data #.to_numpy() # Convert dataframe to np array

    pi, mu, sigma =  initialise_parameters(data, k, iterations)

    for i in range(iterations):
        gamma  = e_step(data, pi, mu, sigma)
        pi, mu, sigma = m_step(data, gamma, sigma)
        filename = "pi-" + str(i + 1) + ".csv"
        np.savetxt(filename, pi, delimiter=",") #this must be done at every iteration
        filename = "mu-" + str(i + 1) + ".csv"
        np.savetxt(filename, mu, delimiter=",")  #this must be done at every iteration
        for cluster in range(k): #k is the number of clusters
            filename = "Sigma-" + str(cluster + 1) + "-" + str(i + 1) + ".csv" #this must be done k times for each iteration
            np.savetxt(filename, sigma[cluster], delimiter=",")

    predicted_labels = predict(x, pi, mu, sigma, k)

    # convert np.nan to float(0.0)
    pi = np.nan_to_num(pi)
    mu = np.nan_to_num(mu)
    sigma = np.nan_to_num(sigma)

    # compute centers as point of highest density of distribution
    centroids = np.zeros((k,d))

    for cluster in range(k):
        density = multivariate_normal(mean=mu[cluster], cov=sigma[cluster], allow_singular=True).logpdf(x)
        centroids[cluster, :] = x[np.argmax(density)]

    return centroids, predicted_labels

def main():
    #Uncomment next line when running in Vocareum
    data=np.genfromtxt(sys.argv[1], delimiter = ',', skip_header=1)
    #Run KMeans to get clusters in the data and a csv of clusters per iteration
    centroids, labels = KMeans(data, 5, 10)
    #Run EMGMM to output the required csv files plus the predicted_labels
    c_emgmm, l_emgmm = EMGMM(data, 5, 10)

if __name__ == '__main__':
    main()


import numpy as np

class Activation:
    @staticmethod
    def forward(x):
        raise NotImplementedError

    @staticmethod
    def backward(dout, cache):
        raise NotImplementedError

class Sigmoid(Activation):
    """
    In the forward pass, sigmoid(x) = 1 / (1 + exp(-x))
    In the backward pass, sigmoid'(x) = sigmoid(x) * (1 - sigmoid(x))
    """
    @staticmethod
    def forward(x):
        output = 1. / (1. + np.exp(-x))
        cache = output
        return output, cache

    @staticmethod
    def backward(dout, cache):
        grad = cache * (1 - cache)

        return dout * grad

class Relu(Activation):
    @staticmethod
    def forward(x):
        output = np.maximum(0, x)
        cache = x

        return output, cache

    @staticmethod
    def backward(dout, cache):
        grads = np.array(dout, copy=True)
        grads[cache < 0] = 0

        return grads

class Softmax(Activation):
    """
    Since CrossEntropyLoss is easier and efficient to implement,
    I will not implement a single Softmax class this time.
    """
    @staticmethod
    def forward(x):
        raise NotImplementedError

    @staticmethod
    def backward(dout, cache):
        raise NotImplementedError

if __name__ == '__main__':
    x = np.array([0.4, 1, -20, 48])

    # sigmoid test
    y, cache= Sigmoid.forward(x)
    grads = Sigmoid.backward(1, cache)

    f = lambda x: 1. / (1. + np.exp(-x))
    g = lambda x: (f(x + 0.0001) - f(x - 0.0001)) / 0.0002

    print(f(x), g(x))
    print(y, grads)


# 2017 JA
import numpy as np

def lorenz63(x_0,tmax,paramout=None):
    """Evolution of the Lorenz 1963 3-variable model.

    Inputs:  - x_0, an array containing the initial position
             - tmax, the maximum time.  Should be a multiple of the
               timestep 0.01.
    Outputs: - t, the time array
             - xt, the nature run [len(t) x 3]"""
    # The time array
    tstep = 0.01
    t = np.arange(0,tmax+tstep/2,tstep)
    # Note that with this step size, 8 steps are approximately linear windows
    # Now, define the vectors for space vars. They're organized in an
    # array of 3 columns [x,y,z].
    xt = np.empty((len(t),3))
    xt.fill(np.nan)
    xt[0,:] = x_0

    # The cycle containing time integration
    for i in range(len(t)-1): # for each time step
        Varsold = xt[i,:]
        # We integrate using Runge-Kutta-4
        Varsnew = rk4(Varsold,tstep,paramout)
        xt[i+1,:] = Varsnew

    return t,xt

## Auxiliary functions
def rk4(Varsold,tstep,paramout=None):
    "This function contains the RK4 routine."
    k1 = f(Varsold,paramout)
    k2 = f(Varsold+1/2.0*tstep*k1,paramout)
    k3 = f(Varsold+1/2.0*tstep*k2,paramout)
    k4 = f(Varsold+tstep*k3,paramout)
    Varsnew = Varsold + 1/6.0*tstep*(k1+2*k2+2*k3+k4)
    return Varsnew

def f(x,paramout=None):
    "This function contains the actual L63 model."
    # The parameters
    if np.all(paramout)==None:
     sigma = 10.0;     b = 8/3.0;    r = 28.0
    else:
     sigma = paramout[0];  b = paramout[1];  r = paramout[2]

    # Initialize
    k = np.empty_like(x)
    k.fill(np.nan)
    # The Lorenz equations
    k[0] = sigma*(x[1]-x[0])
    k[1] = x[0]*(r-x[2])-x[1]
    k[2] = x[0]*x[1]-b*x[2]
    return k

#############################
def lorenz63_stoch(x_0,tmax,sqrtQ=None):
 #L63 with model error
 # The time array
 tstep = 0.01
 t = np.arange(0,tmax+tstep/2,tstep)
 Nsteps = np.size(t)
 Nx = np.size(x_0)
 xt = np.empty((Nsteps,Nx)); xt.fill(np.nan)
 xt[0,:] = x_0
 # The cycle containing time integration
 for i in range(Nsteps-1): # for each time step
  Varsold = xt[i,:]
  Varsnew = rk4(Varsold,tstep)
  if np.all(sqrtQ)==None:
   jump = [0,0,0]
  else:
   jump = np.dot(sqrtQ,np.random.randn(Nx))
  x_aux = Varsnew + jump
  xt[i+1,:] = x_aux
 del i
 return t,xt


import pandas as pd
import plotly.express

from IMLearn.learners import UnivariateGaussian, MultivariateGaussian
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
pio.templates.default = "simple_white"

def test_univariate_gaussian():
    # Question 1 - Draw samples and print fitted model
    X = np.random.normal(10, 1, 1000)
    estimator = UnivariateGaussian()
    estimator = estimator.fit(X)
    print(f'({estimator.mu_}, {estimator.var_})')

    # Question 2 - Empirically showing sample mean is consistent
    df = pd.DataFrame(columns=['Sample Size', 'Distance from Real Expectation'])
    for sample_size in range(10, 1001, 10):
        sample = X[:sample_size]
        model = UnivariateGaussian(biased_var=False)
        model = model.fit(sample)
        distance = abs(model.mu_ - 10)
        df = pd.concat([df, pd.DataFrame({'Sample Size': [sample_size],
                                          'Distance from Real Expectation': [distance]})])
    plotly.express.bar(df, x='Sample Size', y='Distance from Real Expectation',
                       title='Distance from Actual Expectation as a Function '
                             'of Sample Size').show()

    # Question 3 - Plotting Empirical PDF of fitted model
    pdf = estimator.pdf(X)
    df = pd.DataFrame(zip(X, pdf), columns=['Sample Value', 'Probability'])
    plotly.express.scatter(df, x='Sample Value', y='Probability',
                           title='PDF of Fitted Model').show()

def test_multivariate_gaussian():
    # Question 4 - Draw samples and print fitted model
    mu = np.array([0, 0, 4, 0])
    sigma = np.array([[1, 0.2, 0, 0.5],
                       [0.2, 2, 0, 0],
                       [0, 0, 1, 0],
                       [0.5, 0, 0, 1]])
    X = np.random.multivariate_normal(mu, sigma, 1000)
    estimator = MultivariateGaussian()
    estimator = estimator.fit(X)
    print(estimator.mu_)
    print(estimator.cov_)

    # Question 5 - Likelihood evaluation
    res = []
    for f1 in np.linspace(-10, 10, 200):
        for f3 in np.linspace(-10, 10, 200):
            mu = np.array([f1, 0, f3, 0])
            log_likelihood = MultivariateGaussian.log_likelihood(mu, sigma,
                                                                 X)
            res.append((f1, f3, log_likelihood))
    df = pd.DataFrame(res, columns=['f1', 'f3', 'Log Likelihood'])
    plotly.express.density_heatmap(df, x='f3', y='f1', z='Log Likelihood',
                                   title='Log Likelihood by Values of f1, f3',
                                   histfunc='avg').show()

    # Question 6 - Maximum likelihood
    row = df.iloc[df['Log Likelihood'].idxmax()]
    print(f"f1: {row['f1'].round(3)}, f3: {row['f3'].round(3)},"
          f" Log Likelihood: {row['Log Likelihood'].round(3)}")

if __name__ == '__main__':
    np.random.seed(0)
    test_univariate_gaussian()
    test_multivariate_gaussian()


import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits import mplot3d

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

x = np.linspace(-0.5, 3.0)  # 100 values between -0.5 and 3.0

y = f(x)                    # evaluate f on the x points => [f(x)[0-100]]

plt.plot(x, y)               # plot data

# plt.figure()                          # manually create a new figure
# plt.plot(x, f(x), x, x**2, x, 1 - x)  # muilty lines

# -------
# fig, ax = plt.subplots()  # subplot
# l1 = ax.plot(x, f(x))
# l2 = ax.plot(x, x**2)
# l3 = ax.plot(x, 1 - x)

# y1 = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
# y2 = np.array([1.2, 1.6, 3.1, 4.2, 4.8])
# y3 = np.array([3.2, 1.1, 2.0, 4.9, 2.5])

# fig, ax = plt.subplots()  # subplot
# lines = ax.plot(y1, 'o', y2, 'x', y3, '*')  # add styles line

# ax.set_title("Plot of the data y1, y2, and y3")
# ax.set_xlabel("x axis label")
# ax.set_ylabel("y axis label")

# ax.axis([-0.5, 5.5, 0, 5.5])  # set axes
# ax.set_xticks([0.5*i for i in range(9)])  # set xticks
# ax.set_yticks([0.5*i for i in range(11)])  # set yticks
# ax.grid()

# # add a legend follow order of plot(1, 2, 3)
# ax.legend(("data y1", "data y2", "data y3"))

# ------ seprate figure -----------------------------------------------------------------
# def generate_newton_iters(x0, number):
#     iterates = [x0]
#     errors = [abs(x0 - 1.)]
#     for _ in range(number):
#         x0 = x0 - (x0*x0 - 1.)/(2*x0)
#         iterates.append(x0)
#         errors.append(abs(x0 - 1.))
#     return iterates, errors

# iterates, errors = generate_newton_iters(2.0, 5)

# # print(iterates, errors)

# fig, (ax1, ax2) = plt.subplots(1, 2, tight_layout=True)  # 1 row, 2 columns

# ax1.plot(iterates, "x")
# ax1.set_title("Iterates")
# ax1.set_xlabel("$i$")
# ax1.set_ylabel("$x_i$")

# ax2.semilogy(errors, "x")  # plot y on logarithmic scale
# ax2.set_title("Error")
# ax2.set_xlabel("$i$")
# ax2.set_ylabel("Error")
# ---------------------------------------------------------------------------------------

# -------------- Surface and contour plots
# X = np.linspace(-2, 2)
# Y = np.linspace(-1, 1)

# x, y = np.meshgrid(X, Y)

# z = x**2 * y**3

# fig = plt.figure()
# ax = fig.add_subplot(projection="3d")  # declare 3d plot

# surface
# ax.plot_surface(x, y, z)

# ax.set_xlabel("$x$")
# ax.set_ylabel("$y$")
# ax.set_zlabel("$z$")

# ax.set_title("Graph of the function $f(x) = x^2y^3$")

# coutour
# plt.contour(x, y, z)
# plt.title("Contours of $f(x) = x^2y^3$")
# plt.xlabel("$x$")
# plt.ylabel("$y$")

# x = np.array([0.19, -0.82, 0.8, 0.95, 0.46, 0.71,
#               -0.86, -0.55, 0.75, -0.98, 0.55, -0.17, -0.89,
#               -0.4, 0.48, -0.09, 1., -0.03, -0.87, -0.43])
# y = np.array([-0.25, -0.71, -0.88, 0.55, -0.88, 0.23,
#               0.18, -0.06, 0.95, 0.04, -0.59, -0.21, 0.14, 0.94,
#               0.51, 0.47, 0.79, 0.33, -0.85, 0.19])
# z = np.array([-0.04, 0.44, -0.53, 0.4, -0.31, 0.13,
#               -0.12, 0.03, 0.53, -0.03, -0.25, 0.03, -0.1,
#               -0.29, 0.19, -0.03, 0.58, -0.01, 0.55, -0.06])

# # ------- trisurf
# fig = plt.figure(tight_layout=True)  # force new figure
# ax1 = fig.add_subplot(1, 2, 1, projection="3d")  # 3d axes
# ax1.plot_trisurf(x, y, z)
# ax1.set_xlabel("$x$")
# ax1.set_ylabel("$y$")
# ax1.set_zlabel("$z$")
# ax1.set_title("Approximate surface")

# ax2 = fig.add_subplot(1, 2, 2)  # 2d axes
# ax2.tricontour(x, y, z)
# ax2.set_xlabel("$x$")
# ax2.set_ylabel("$y$")
# ax2.set_title("Approximate contours")

# ------------- Customizing three-dimensional plots
# X = np.linspace(-2, 2)
# Y = np.linspace(-2, 2)
# x, y = np.meshgrid(X, Y)
# t = x**2 + y**2  # small efficiency
# z = np.cos(2*np.pi*t)*np.exp(-t)

# fig = plt.figure()
# ax = fig.add_subplot(projection="3d")
# ax.plot_surface(x, y, z, cmap="binary_r")

# ax.set_title("Surface with colormap")
# ax.set_xlabel("$x$")
# ax.set_ylabel("$y$")
# ax.set_zlabel("$z$")

# fig = plt.figure()
# plt.contour(x, y, z, cmap="binary_r")
# plt.xlabel("$x$")
# plt.ylabel("$y$")
# plt.title("Contour plot with colormap set")

plt.show()


#! /usr/bin/env python

# PyTorch Tutorial 05 - Gradient Descent with Autograd and Backpropagation
# https://www.youtube.com/watch?v=E-I2DNVzQLg&list=PLqnslRFeH2UrcDBWF5mfPGpqQDSta6VK4&index=6

#from __future__ import print_function
import numpy as np

print("\n" * 20)
print("-" * 80)
print("-" * 80)
print("\n" * 2)

# 0m - Manual, Prediction, Gradient Computation, Loss Computation, Parameter updates
# 12m10 - switch over from numpy to torch

# f = 2 * x
X = np.array([1,2,3,4], dtype=np.float32)
Y = np.array([2,4,6,8], dtype=np.float32)
w = 0.0       # weights

# model predition
def forward(x):
  return w * x

# loss = MSE - Mean Squared Error
def loss(y, y_predicted):
  return ((y_predicted - y)**2).mean()

# gradient - manually computed gradient
#       mean   prediction   actual value   squared
# MSE = 1/N *  (w*x       -    y)           **2
# J - objective function
# dJ/dw = 1/N 2*x (w*x - y)
def gradient(x, y, y_predicted):
  return np.dot(2*x, y_predicted-y).mean()

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

learning_rate = 0.01
n_iters = 20 # 10

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

  # loss
  l = loss(Y, y_pred)

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

  # update weights   -= negative gradient direction
  w -= learning_rate * dw

  if epoch % 1 ==  0:
    print(f'epoch {epoch+1}: w = {w:.3f}, loss = {l:.8f}')

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

#print(f"\n  \n{  }")

#print(f"\n  \n{  }")
print('\n')


# An example of OpenOpt EIG, see http://openopt.org/EIG for more examples and details
from openopt import EIG

# create a 5 x 5 matrix
import numpy.random as nr
nr.seed(0)
N = 5
A = nr.rand(N, N)

#define prob
p = EIG(A)
#solve
r = p.solve('numpy_eig') # solver numpy.linalg.eig will be used

print(r.eigenvalues) # [ 2.89776724+0.j  -0.65372843+0.j 0.14607289+0.19602952j  0.14607289-0.19602952j -0.08530815+0.j]
# for i-th eigenvalue r.eigenvectors[:,i]  is corresponding vector,
# as well as it is done for numpy/scipy functions
print(r.eigenvectors)
'''
[[ 0.43733688+0.j         -0.19592536+0.j          0.57285154+0.j
   0.57285154+0.j          0.63764724+0.j        ]
 [ 0.49662623+0.j          0.03219327+0.j         -0.14013112+0.23938241j
  -0.14013112-0.23938241j -0.53642409+0.j        ]
 [ 0.42977207+0.j          0.55544796+0.j         -0.17419089+0.24907549j
  -0.17419089-0.24907549j  0.29171743+0.j        ]
 [ 0.38727512+0.j         -0.62338178+0.j         -0.42011495-0.27666898j
  -0.42011495+0.27666898j -0.45403266+0.j        ]
 [ 0.47687818+0.j          0.51327338+0.j          0.48015310-0.13758665j
   0.48015310+0.13758665j  0.12004364+0.j        ]]
 '''


## gaussian.py
# emcee model file to estimate the mean and standard deviation of a gaussian distribution

# imports
from math import log, pi
import numpy as np

# define the natural logarithm of the likelihood
def ln_likelihood(θ, value):
    N = len(value)
    mu, sigma = θ

    sum = 0
    for i in range(0, N):
        sum += (-1/2) * ((value[i] - mu) / sigma)**2

    sum += -N*log(sigma) - (1/2)*log(2*pi)

    return sum

# define the natural logarithm of the priors
def ln_prior(θ):
    mu, sigma = θ

    # flat priors
    if -10 < mu < 10 and 1 < sigma < 10:
        return 0.0

    return -np.inf

# define the probability using the prior and likelihood
def ln_probability(θ, value):
    prior = ln_prior(θ)
    if not np.isfinite(prior):
        return -np.inf
    return prior + ln_likelihood(θ, value)


"""

The prime factors of 13195 are 5, 7, 13 and 29.

What is the largest prime factor of the number 600851475143 ?

"""

import sympy

def largest_prime_factor(n):
	prime_nos = []
	for i in range(2, int(n/2)):
		if n % i == 0 and sympy.isprime(i):
			prime_nos.append(i)
			print (prime_nos)

	return prime_nos

print (max(largest_prime_factor(600851475143)))


#Power spectrum Estimation of discrete sequence
#x[n] = (0.9)^n, 0<=n<20
import numpy as np
from numpy.fft import fft,ifft
import scipy as sy
from matplotlib import pyplot as plt
N = 16 #16 point dft
n = np.arange(0,20,1)
x1 = (0.9**n) #given discrete sequence
x = x1[0:15]
X = fft(x,N)
X_mag = abs(X)
Pxx = ((abs(X))**2)/N
K = np.arange(0,len(Pxx),1)
plt.figure(1)
plt.stem(K,Pxx,'grey')
plt.xlabel('Discrete Frequency Variable K---->')
plt.ylabel('Pxx-Periodogram---->')
plt.grid(True, which='both')
plt.title('Power Spectrum Estimation of Discrete Sequence')
plt.figure(2)
plt.stem(K,X_mag,'red')
plt.xlabel('Discrete Frequency Variable K---->')
plt.ylabel('Magnitude Response |X(K)|---->')
plt.grid(True, which='both')
plt.title('DFT of Discrete Sequence')
plt.show()


from __future__ import print_function

from numpy import sqrt, fabs

from mldata.lib.cleaner import Cleaner
from mldata.lib.helper import Helper

class Normalizer(object):
    def __init__(self, numerical_list, invalid_list):
        # start with parameter check
        self._parameter_check(numerical_list, invalid_list)
        self._raw_data_list = self.clean_list(numerical_list, invalid_list)
        self._length = len(self._raw_data_list)
        self._sum = sum(self._raw_data_list)
        self._mean = self._sum / self._length

    @staticmethod
    def clean_list(numerical_list, invalid_list):
        # return a list all elements are numerical
        return Cleaner(numerical_list, invalid_list).clean()

    @staticmethod
    def _parameter_check(numerical_list, invalid_list):
        for item in numerical_list:
            if item not in invalid_list and not Helper.is_numeric(item):
                raise ValueError("Item %s not numerical, all data in list should be numerical" % str(item))

    def normalize_numerical_value(self):
        norm_list = [(x - self._mean) / self.compute_std() for x in self._raw_data_list]
        return [x if -4 <= x <= 4 else 4 * x / abs(x) for x in norm_list]

    def compute_std(self):
        sum_x = self._sum
        sum_x_2 = sum([x * x for x in self._raw_data_list])
        return sqrt(fabs(sum_x_2 - sum_x * sum_x / self._length) / (self._length - 1))


import numpy as np

def bisection(f, a, b, epsilon=10**-5):
    assert f(a)*f(b) <= 0
    assert epsilon > 0

    range_hist = [(a, b)]
    while not np.abs(b-a) <= epsilon:
        c = (a + b) / 2
        fc = f(c)
        if fc * f(a) > 0:
            a = c
        else:
            b = c
        range_hist.append((a, b))

    c = (a + b) / 2
    hist = np.asarray(range_hist)
    return c, hist


#!/home/knielbo/virtenvs/cv/bin/python
"""
Examples of approaches to weight initialization
"""
import numpy as np
def main():
    m = 64
    n = 32
    # constant initialization
    W = np.zeros((m,n))
    W = np.ones((m,n))
    C = 2
    W = W * C
    # uniform and normal dist initialization
    W = np.random.uniform(low=-.05, high=.05, size=(m,n))
    W = np.random.normal(.0, .05, size=(m,n))
    # LeCun uniform and normal
        # parameter F_{in} "fan in" or number of inputs to the layer
        #           F_{out} "fan out" or number og outputs from the layer
    F_in = 64
    F_out = 32
    limit = np.sqrt(3 / float(F_in))
        # uniform
    W = np.random.uniform(low=-limit, high=limit, size=(F_in, F_out))
        # normal
    limit = np.sqrt(1 / float(F_in))
    W = np.random.normal(.0, limit, size=(F_in, F_out))
    # Glorot/Xavier uniform and normal
        # normal
    limit = np.sqrt(2 / float(F_in + F_out))
    W = np.random.normal(.0, limit, size=(F_in, F_out))
        # uniform
    limit = np.sqrt(6 / float(F_in + F_out))
    W = np.random.uniform(low=-limit, high=limit, size=(F_in, F_out))
    # He et al./Kaiming/MSRA uniform and normal, for very deep models with ReLU-like
        # activation function
        # uniform
    limit = np.sqrt(6 / float(F_in))
    W = np.random.uniform(low=-limit, high=limit, size=(F_in, F_out))
        # normal
    limit = np.sqrt(2 / float(F_in))
    W = np.random.normal(.0, limit, size=(F_in, F_out))

    print(W)

if __name__=="__main__":
    main()


import scipy
import scipy.stats as ss
import numpy as np
import matplotlib
import pandas as pd
'''
import random
# Given points A, B and C, this function returns the acute angle between vectors AB and AC
# using the dot product between these vectors
np.random.seed(16) # include a seed for reproducibility

# generate the normal data
normal_mean = np.array([1.0, 2.0])
normal_covariance = np.array([[0.2, 0.0], [0.0, 0.1]])
normal_data = np.random.multivariate_normal(normal_mean, normal_covariance, 100)

# generate the anomalous data
anomaly_mean = np.array([6.0, 8.0])
anomaly_covariance = np.array([[2.0, 0.0], [0.0, 4.0]])
anomaly_data = np.random.multivariate_normal(anomaly_mean, anomaly_covariance, 10)

final_data = np.concatenate((normal_data, anomaly_data), axis=0)
all_data = np.concatenate((normal_data, anomaly_data), axis=0)

def angle(point1, point2, point3):
    v21 = np.subtract(point2, point1)
    v31 = np.subtract(point3, point1)
    dot_product = (v21*v31).sum()
    normalization = np.linalg.norm(v21)*np.linalg.norm(v31)
    print(type(normalization))
    print(normalization)
    acute_angle = np.arccos(dot_product/normalization)
    return acute_angle
'''

def angle(point1, point2, point3):
    v21 = np.subtract(point2, point1)
    v31 = np.subtract(point3, point1)
    dot_product = (v21*v31).sum()
    normalization = (np.linalg.norm(v21)**2) * (np.linalg.norm(v31)**2)
    acute_angle = np.arccos(dot_product/normalization)
    return acute_angle

def eval_angle_point(point, data):
    angles_data = []
    for index_b, b in enumerate(data):
        if (np.array_equal(b, point)):
            continue
        # ensure point c comes later in array that point b
        # so we don't double count points
        for c in data[index_b + 1:]:
            if (np.array_equal(c, point)) or (np.array_equal(c, b)):
                continue
            angles_data.append(angle(point, b, c))
    return angles_data

def angle_based_anomaly_detection(points, all_data, threshold):
    """Returns numpy array with data points labelled as outliers
    Parameters
    ----------
    points: numpy array like data_point
    all_data: numpy like data
    threshold: value below which points are considered as anomaly"""

    df_ = pd.DataFrame(columns=['point','angle variance'])
    for index2, item2 in enumerate(points):
        df_.loc[index2] = [item2, np.var(eval_angle_point(item2, all_data))]

    outliers = df_[df_['angle variance'] < threshold ]
    outliers = np.stack(outliers['point'].to_numpy())
    return outliers

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


import sympy as sp, math as m

x = sp.symbols('x')
sp.init_printing(use_unicode=True)

#f=sp.Lambda(x, x*sp.cos(x)-x**2*sp.sin(x))
#f=sp.Lambda(x, sp.exp(sp.sin(x)**2-2))

"""
def f(x):
    #return x*m.cos(x)-x**2*m.sin(x)
    return m.exp(3*x)*m.sin(3*x)/(x**4+1)
"""

def trapecio_compuesta(f, a, b, n):

    h = (b-a)/n

    result = sp.N(f(a)+f(b))/2

    for i in range(1, n):
        result += sp.N(f(a+i*h))

    result *= h

    return result

"""
a, b = 0,2
n = 8

result = trapecio_compuesta(f, a, b, n)

print(result)

# Error

h = (b-a)/n

cota_2 = 0.73576

print("Cota error: "+str(abs((b-a)*h**2/12*cota_2)))
"""

# 2 Examen

f=sp.Lambda(x, (sp.cos(sp.pi*x)+1)**(10/3))
a, b = 0, 1
solution = 3.000492123714049

n = 2

for _ in range(6):
    result = trapecio_compuesta(f, a, b, n)
    print(str(n)+ " subintervalos:")
    print("\t"+str(result))
    print("\tError:" + str(abs(result-solution)))
    #print("\tError/4:" + str(abs(result-solution)/4))
    n*=2


## NUMERICAL INTEGRATION - FUNCTIONS FILE
#### Based on excercise from file *1_aproximacion_a_derivadas_e_integrales*
#### Author: Rob (GitHub: Roberto919)
#### Date: 19 June 2020

'--------------------------------------------------------------------------------'
#############
## Imports ##
#############

## Python libraries
import numpy as np
import math

'--------------------------------------------------------------------------------'
###############
## Functions ##
###############

## Tcf: composite trapezoidal method for f
def Tcf(f, a, b, n):
    """
    Compute numerical approximation using trapezoidal method in
    an interval.
    Nodes are generated via formula: x_i = a+i*h_hat for i=0,1,...,n and h_hat=(b-a)/n; h = (b - a)
        args:
            f (function): function expression of integrand
            a (int): left point of interval
            b (int): right point of interval
            n (int): number of subintervals
        returns:
            sum_res (float): numerical approximation to integral of f in the interval a,b
    """

    ## Constants
    h = (b - a)
    h_hat = (b - a)/n
    x_0 = 0
    x_n = math.pi

    ## Finding sum of x_i
    sum_fxi = 0
    for i in np.linspace(1, (n - 1), (n - 1)):

        x_i = a + i*h_hat

        sum_fxi += f(x_i)

    ## Estimating integral value
    approx_area_trapeze = h/(2*n)*(f(x_0) + f(x_n) + 2*sum_fxi)

    return approx_area_trapeze

# GLf: Gauss-Legendre quadrature for f
def GLf(f, a, b, n):
    """
    Compute numerical approximation using quadrature Gauss-Legendre.
    Weights and nodes are obtained with table for n=0,1,2,3,4
    Args:
        f (function): function expression of integrand
        a (int): left point of interval
        b (int): right point of interval
        n (int): number of subintervals
    Returns:
        sum_res (float): numerical approximation to integral of f in the interval a,b
    """

    ## List of calculated weights and node values (n = keys)
    wn_vals = {

        0: {
            'grade': 1,
            'vars': 2,
            'w': [2],
            'n': [0]
        },

        1: {
            'grade': 3,
            'vars': 4,
            'w': [1, 1],
            'n': [-np.sqrt(1/3), np.sqrt(1/3)]
        },

        2: {
            'grade': 5,
            'vars': 6,
            'w': [5/9, 8/9, 5/9],
            'n': [-np.sqrt(3/5), 0, np.sqrt(3/5)]
        },

        3: {
            'grade': 7,
            'vars': 8,
            'w': [0.347855, 0.652145, 0.652145, 0.347855],
            'n': [-0.861136, -0.339981, 0.339981, 0.861136]
        },

        4: {
            'grade': 9,
            'vars': 10,
            'w': [0.236927, 0.478629, 0.568889, 0.478629, 0.236927],
            'n': [-0.90618, -0.538469, 0, 0.538469, 0.90618]
        },
    }

    ## Approximation of the sum term
    sum_term = 0
    vals = len(wn_vals[n]['w'])
    for v in range(0, vals):
        sum_term += wn_vals[n]['w'][v]*f(0.5*((b - a)*wn_vals[n]['n'][v] + a + b))

    ## Obtaining final approximation result
    sum_res = (b - a)/2*sum_term

    return sum_res

## GHf: Gauss-Hermite quadrature for f
def GHf(f, mu, sigma):
    """
    Compute numerical approximation using quadrature Gauss-Hermite.
    Weights and nodes are obtained with table in Kiusalaas for n=6
        args:
            f (function): function expression of integrand
            mu (float): mean
            sigma (float): standard deviation
        returns:
            sum_res (float): numerical approximation to integral of f in the interval a,b
    """

    ## Default "n" value
    n = 5

    ## Nodes and weights for numerical approximation
    gausshermite_values = { ## Dict keys correspond to the value of "n"
        1: {
            'A_xs': {
                0.886227: [0.707107, -0.707107],
            }
        },
        2: {
            'A_xs': {
                1.181636: [0, -0],
                0.295409: [1.224745, -1.224745],
            }
        },
        3: {
            'A_xs': {
                0.804914: [0.524648, -0.524648],
                0.813128e-1: [1.650680, -1.650680],
            }
        },
        4: {
            'A_xs': {
                0.945308: [0, -0],
                0.393619: [0.958572, -0.958572],
                0.199532e-1: [2.020183, -2.020183],
            }
        },
        5: {
            'A_xs': {
                0.724629: [0.436077, -0.436077],
                0.157067: [1.335849, -1.335849],
                0.453001e-2: [2.350605, -2.350605]
            }
        },
    }

    ## Calculation of area approximate
    sum_res = 0
    for weight in gausshermite_values[n]['A_xs']:
        for node in gausshermite_values[n]['A_xs'][weight]:
            sum_res += weight*f(node)

    return sum_res


# Question: https://projecteuler.net/problem=108

"""
1/x + 1/y = 1/n
Since n > 0, we have x > n and y > n. Assume x >= y.
Let x = n + a, y = n + b.
   1/x + 1/y = 1/n
-> xn + yn = xy
-> (n+a)n + (n+b)n = (n+a)(n+b)
-> 2n^2 + an + bn = n^2 + an + bn + ab
-> n^2 = ab
The number of solutions of (x,y) for each n is the number of solutions of (a,b) for each n.
The number of positive integers (a,b) where a >= b that ab = n^2 is (m-1)/2+1, where m is the number of divisors of n^2.
Why?
    | Let the number of divisors be 'm'.
    | For each divisor d where d != n, let (a, b) = (d, n^2/d).
    |   Note that, (d, n^2/d) is the same as (n^2/d, d), where n^2/d is also a divisor but only one of them have a > b.
    |   There are (m-1) such a case, therefore there are (m-1)/2 pair of (a,b) that a > b.
    | For the case (a, b) = (n, n); there is only one such a case.
    | So, in total, there are (m-1)/2+1 pairs of (a,b).

In this problem, we want to find the smallest n such that (number_of_divisors of n**2) >= 1999.
"""

from sage.all import number_of_divisors

ans = 0
while True:
    ans = ans + 1
    if number_of_divisors(ans**2) >= 1999:
        break
print(ans)


import numpy as np
from tabulate import tabulate
import sympy
import scipy.integrate as integrate
from mpmath import chebyt, chop, taylor

x = sympy.symbols('x')

def printMatrix(A, B):
    selected = None
    for row in range(len(B)):
        print("(", end='')
        for col in range(len(A[row])):
            print("\t{1:10.2f}{0}".format(" " if (selected is None
                                                  or selected != (row, col)) else "*", A[row][col]), end='')
        print("\t) * (\tX{0}) = (\t{1:10.2f})".format(row + 1, B[row][0]))

# норма матрицы, где p = infinity
def infnorm(A):
    return max(map(max, A))

# скалярное произведение (Lwj, wi)
def scalar(i, j):
    x = sympy.symbols('x')
    wj = sympy.jacobi(j, 1, 1, x) * (1 - np.power(x, 2))
    d = sympy.lambdify(x, sympy.diff(wj, x, 1))
    d2 = sympy.lambdify(x, sympy.diff(wj, x, 2))
    l = integrate.quad(lambda x: (-1 * d2(x) / (x - 3) + (1 + x / 2) * d(x)
                                 + sympy.exp(x /2) * sympy.jacobi(j, 1, 1, x) * (1 - np.power(x, 2))) * sympy.jacobi(i, 1, 1, x) * (1 - np.power(x, 2)), -1, 1)[0]
    return l

def Lu(wj):
    x = sympy.symbols('x')
    d = sympy.lambdify(x, sympy.diff(wj, x, 1))
    d2 = sympy.lambdify(x, sympy.diff(wj, x, 2))
    h = sympy.diff(sympy.diff(wj, x, 1))
    return -1 * d2(x) / (x - 3) + (1 + x / 2) * d(x) + sympy.exp(x / 2) * wj

def f(x):
    return 2 - x

def solution(x0, C): # формируем решение
    result = 0
    for i in range(len(C)):
        result += C[i] * sympy.lambdify(x, w[i])(x0)
    return result

# печать результата для нескольких n
def printresult(Cond):
    headers = ["n", "mu(A)", "y^n(-0.5)", "y^n(0)", "y^n(0.5)", "y*(x) - y^n(x)"]
    print(tabulate(Cond, headers, tablefmt='grid'))

# печать результата для одного n
def printSolon(A, B, mu, C, n):
    print("Расширенная матрица:")
    printMatrix(A, B)

    print("Число обусловленности матрицы A:")
    print(mu)

    print("Коэффициенты разложения С:")
    print(C)

# метод Галеркина
def galerkin(n, w):
    A = np.eye(n)
    B = np.ones((n, 1))

    for i in range(n):
        B[i] = integrate.quad(lambda x: (2 - x) * sympy.jacobi(i, 1, 1, x) * (1 - np.power(x, 2)), -1, 1)[0]
        for j in range(n):
            A[i][j] = scalar(i, j)
    C = np.linalg.solve(A, B)
    mu = infnorm(A) * infnorm(np.linalg.inv(A))
    return A, B, C, mu

# Метод коллокации
def collocation(n, w):
    t = sorted(
        np.squeeze(np.asarray(np.roots(chop(taylor(lambda x: chebyt(n, x), 0, n))[::-1]))))  # Корни многочлена Чебышева

    A = np.eye(n)
    B = np.ones((n, 1))
    for i in range(n):
        B[i] = f(t[i])
        for j in range(n):
            A[i][j] = sympy.lambdify(x, Lu(w[j]))(t[i])
    C = np.linalg.solve(A, B)
    mu = infnorm(A) * infnorm(np.linalg.inv(A))
    return A, B, C, mu

print("Проекционные методы решения краевой задачи для обыкновенного дифференциального уравнения второго порядка")
print("Вариант 3")
v = int(input("Введите число координатных функций или нажмите 0, чтобы оставить значения от 3 до 10:"))

CondGalerkin = []
CondColloc = []
if v == 0:
    for n in range(3, 11):
        w = [] # формируем семейство ортогональных функций — здесь это многочлены Якоби
        for i in range(n):
            w.append(sympy.jacobi(i, 1, 1, x) * (1 - np.power(x, 2)))

        A, B, C, mu = galerkin(n, w)
        A1, B1, C1, mu1 = collocation(n, w)

        CondGalerkin.append([n, mu, solution(-0.5, C), solution(0, C), solution(0.5, C), np.abs(solution(-0.5, C) - solution(-0.5, C1))])
        CondColloc.append([n, mu1, solution(-0.5, C1), solution(0, C1), solution(0.5, C1), np.abs(solution(-0.5, C) - solution(-0.5, C1))])

    print("Метод Галёркина:")
    printresult(CondGalerkin)

    print("Метод коллокации:")
    printresult(CondColloc)
else:
    n = v
    w = []
    for i in range(n):
        w.append(sympy.jacobi(i, 1, 1, x) * (1 - np.power(x, 2)))

    A, B, C, mu = galerkin(n, w)
    A1, B1, C1, mu1 = collocation(n, w)

    print("Метод Галёркина:")
    printSolon(A, B, mu, C, n)

    print("Метод коллокации:")
    printSolon(A1, B1, mu1, C1, n)

import numpy,import scipy,import sympy,from mpmath


'''
Obtain the optimal constant leverage ratio in the continuous limit assuming a given time period, volatility,
and underlying return.
'''

import numpy as np
from scipy.optimize import minimize_scalar, minimize

from modules.simulate import simulateLeveragedBoundedGBM

def computeConstantLeverageReturn(leverage_ratio, underlying_return, time_period, volatility):
    '''
    Compute the return from the constant leverage strategy assuming no cost to borrow and no management fee.

    params:

    leverage_ratio:
        the desired constant leverage ratio
    underlying_return:
        return of the underlying asset as a multiple of initial price
    time_period:
        time period in years
    volatility:
        average annualized volatility over the time period
    '''
    l = leverage_ratio
    t = time_period
    vol = volatility
    return (underlying_return**l)*np.exp(((l-l**2)/2)*(vol**2)*t)

def optimizeRatioContinuous(underlying_return, time_period, volatility):
    '''
    Given some basic market conditions, compute the leverage ratio that maximizes the return
    denominated in the debt asset.

    If the solution is below 1, return 1 as it means leveraging can only decrease returns.

    params:

    underlying_return:
        return of the underlying asset as a multiple of initial price
    time_period:
        time period in years
    volatility:
        average annualized volatility over the time period

    returns:

    sol:
        the optimal leverage ratio
    return:
        the corresponding return
    '''
    def computeReturn(leverage_ratio):
        return -computeConstantLeverageReturn(leverage_ratio, underlying_return, time_period, volatility)
    sol = minimize_scalar(computeReturn)
    if sol.x < 1:
        return 1, computeConstantLeverageReturn(1, underlying_return, time_period, volatility)
    return sol.x, abs(sol.fun)

def optimizeAutomationBoundedGBM(initial_portfolio_value, min_ratio, service_fee, gas_price, volatility, start_price, end_price, time_horizon):
    '''
    Given some real world parameters for DeFi Saver, average gas conditions, and user specified
    expectations of start price, end price and volatility for the collateral asset, compute the
    optimal choice of admissible automation settings to maximizes the expected return. If the expected
    return is less than the return of the underlying, return an empty list, signifying that automation
    is not a good choice.
    '''

    # Constraint functions

    # Repay from must be 10% greater than min ratio.
    def constraint0(x):
        return x[0] - min_ratio - 10.1
    # Repay to must be 5% greater than repay from
    def constraint1(x):
        return x[1] - x[0] - 5
    # Boost to must be 5% greater than repay to
    def constraint2(x):
        return x[3] - x[1] - 5
    # Boost from must be 5% greater than boost to
    def constraint3(x):
        return x[2] - x[3] - 5
    # Boost from must be lower than 1000%
    def constraint4(x):
        return 1000 - x[2]

    cons0 = {'type': 'ineq', 'fun': constraint0}
    cons1 = {'type': 'ineq', 'fun': constraint1}
    cons2 = {'type': 'ineq', 'fun': constraint2}
    cons3 = {'type': 'ineq', 'fun': constraint3}
    cons4 = {'type': 'ineq', 'fun': constraint4}
    cons = [cons0, cons1, cons2, cons3, cons4]

    def meanReturnBoundedGBM(automation_settings):
        '''
        Opposite of mean return since the optimization routine is a minimization routine.
        Simulations start at a leverage corresponding to the the "boost to" target.
        '''
        repay_from = automation_settings[0]
        repay_to = automation_settings[1]
        boost_from = automation_settings[2]
        boost_to = automation_settings[3]
        # print("Trying values: ")
        # print("Repay from: ", repay_from)
        # print("Repay to: ", repay_to)
        # print("Boost from: ", boost_from)
        # print("Boost to: ", boost_to, "\n")
        return_in_collateral_asset, _, _, _ = simulateLeveragedBoundedGBM(initial_portfolio_value, boost_to, min_ratio, repay_from, repay_to, boost_from, boost_to, service_fee, gas_price, 200, volatility, start_price, end_price, time_horizon, 0.000114155)
        return -np.mean(return_in_collateral_asset)

    # Look for the optimal leverage ratio in the continuous case to have a good initial guess.
    L, _ = optimizeRatioContinuous(end_price/start_price, time_horizon, volatility)
    R_init = 100*L/(L-1)
    # print("Optimal L in continuous case: ", L)
    # print("Corresponding ratio: ", R_init)
    if R_init < min_ratio + 10:
        initial_guess = [200, 220, 240, 220]
    else:
        initial_guess = [R_init - 5, R_init, R_init + 5, R_init]
    print("Optimizing...")
    # Actual optimization routine
    res = minimize(meanReturnBoundedGBM, initial_guess, constraints = cons, method='COBYLA', options={'catol': 0}, tol=0.1)
    sol = res.x
    repay_from = sol[0]
    repay_to = sol[1]
    boost_from = sol[2]
    boost_to = sol[3]
    returns_col, returns_debt, _, _ = simulateLeveragedBoundedGBM(initial_portfolio_value, boost_to, min_ratio, repay_from, repay_to, boost_from, boost_to, service_fee, gas_price, 100, volatility, start_price, end_price, time_horizon, 0.000114155)
    optimal_expected_return_in_collateral = np.mean(returns_col)
    optimal_expected_return_in_debt = np.mean(returns_debt)
    if optimal_expected_return_in_collateral < 1:
        return [0, 0, 0, 0], 1
    return sol, optimal_expected_return_in_collateral, optimal_expected_return_in_debt


import numpy as np
import math
from copy import deepcopy
import time
import functools

def ReadPrecision():
    return float(input())

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

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

    return matrix

def MaxNonDiag(matrix):
    i, j = 0, 1

    maxVal = abs(matrix[0][1])

    for k in range(len(matrix)):
        for l in range(len(matrix)):
            if maxVal < abs(matrix[k][l]) and k != l:
                i, j = k, l

    return i, j

def CalcAngle(matrix, i, j):
    delimeter = matrix[i][i] - matrix[j][j]

    res = math.atan(2 * matrix[i][j] / delimeter) / 2.0 if delimeter != 0 \
          else math.pi / 4.0

    return res

def MakeOrthMtx(matrix, i, j):
    phiAngle = CalcAngle(matrix, i, j)
    orthMtx = [[0.0] * len(matrix) for k in range(len(matrix))]
    orthMtx[i][j] = -math.sin(phiAngle)
    orthMtx[j][i] = math.sin(phiAngle)
    orthMtx[i][i] = math.cos(phiAngle)
    orthMtx[j][j] = math.cos(phiAngle)
    for m in range(len(matrix)):
        if m != i and m != j:
            orthMtx[m][m] = 1.0

    return orthMtx

def QuadNonDiagSum(matrix):
    sum = 0.0

    for i in range(len(matrix)):
        sum += math.fsum([matrix[i][j] * matrix[i][j] if i != j
                          else 0.0
                          for j in range(len(matrix))])

    return sum

def PrecFunc(matrix):
    return math.sqrt(QuadNonDiagSum(matrix))

def IsClose(iterEps, userEps):
    print("iteration eps = {}; user eps = {}".format(iterEps, userEps))

    return iterEps < userEps

def FindEigVec(matrix, eps):
    mtxFirst = np.transpose(deepcopy(matrix))
    mtxSecond = [[]]
    orthoList = []
    iteration = 0

    while True:
        maxI, maxJ = MaxNonDiag(mtxFirst)
#        print("max elem is {} with i = {}, j = {}".format(mtxFirst[maxI][maxJ], maxI, maxJ))
        orthMtx = MakeOrthMtx(mtxFirst, maxI, maxJ)
        orthoList.append(orthMtx)
        mtxSecond = np.dot(np.dot(np.transpose(orthMtx), mtxFirst), orthMtx)
#        print("ortho:\n{}\nfirst:\n{}\nsecond:\n{}".format(np.transpose(orthMtx), mtxFirst, mtxSecond))

        if IsClose(PrecFunc(mtxSecond), eps):
            break

        iteration += 1
        mtxFirst = deepcopy(mtxSecond)

    res = [mtxSecond[i][i] for i in range(len(matrix))]

    return res, iteration, functools.reduce(np.dot, orthoList)

if __name__ == "__main__":
    prec = ReadPrecision()
    mtx = ReadMatrix()

    eigVals, iterCount, eigVecs = FindEigVec(mtx, prec)

    print("Found Eigen values:\n{}\nAnd Eigen vectors:\n{}\n In {} iterations".format(";\n".join(map(str, eigVals)),
                                                                                eigVecs,
                                                                                iterCount))
    for i in range(len(mtx)):
        lhs = mtx @ eigVecs[:, i]
        rhs = eigVecs[:, i] * eigVals[i]
        print(lhs)
        print(rhs)
        print("{} eigenVals is {}".format(i, np.isclose(lhs, rhs, prec)))


import numpy as np

"""
  converts polar coordinates (r, theta) to cartesian (x,y)
"""
def polarToCartesian(p):
  r     = p[0]
  theta = p[1]

  x = r*np.cos(theta)
  y = r*np.sin(theta)
  return np.array([x, y])

"""
  converts cartesian (x,y) to polar coordinates (r, theta)
"""
def cartesianToPolar(s):
  x = s[0]
  y = s[1]

  r = np.sqrt(s[0]**2 + s[1]**2)
  theta = np.arctan2(y,x)

  return np.array([r, theta])

"""
  solved 2nd degree equation
"""
def solve2ndDegEq(a,b,c):
  d = (b**2) - 4*a*c
  x = np.sqrt(complex(d))
  return (-b + np.array([x,-x]))/(2*a)

#x = np.array([1,0])
#y = cartesianToPolar(x)
#print polarToCartesian(y)


import numpy as np

def predict(X, w, b):
    return X * w + b

def loss (X, Y, w, b):
    predictions = predict(X, w, b)
    return np.average((predictions - Y) ** 2)

def gradient(X, Y, w, b):
    predictions = predict(X, w, b)
    w_gradient = np.average(2 * X * (predictions - Y))
    b_gradient = np.average(2 * (predictions - Y))
    return (w_gradient, b_gradient)

def train_line(X, Y, iterations, learning_rate):
    w = b = 0;
    for i in range(iterations):
        print("%d => Loss: %.10f" % (i, loss(X, Y, w, b)))
        w_gradient, b_gradient = gradient(X, Y, w, b)
        w -= w_gradient * learning_rate
        b -= b_gradient * learning_rate
    return w, b

if __name__ == "__main__":
    X, Y = np.loadtxt("examples.txt", skiprows=1, unpack=True)
    iterations = 20000
    learning_rate = 0.001
    w, b = train_line(X, Y, iterations, learning_rate)

    print("\nw = %.10f, b = %.10f" % (w, b))
    print("\ninput = %d => prediction = %.10f" % (25, predict(25, w, b)))

    input("Press <Enter>")

    import matplotlib.pyplot as plt

    plt.plot(X, Y, "bo")
    plt.xlabel("Input", fontsize=20)
    plt.ylabel("Output", fontsize=20)
    x_edge, y_edge = 50, 50
    plt.axis([0, x_edge, 0, y_edge])
    plt.plot([0, x_edge], [b, predict(x_edge, w, b)], linewidth=1.0, color="g")
    plt.show()


import sys
import math
import numpy as np
import torch
from torch.nn import *
from torch.optim import AdamW
from torch.utils.data import Dataset, DataLoader
from typing import Union

class MatrixFactorize(Module):
    """
    Matrix Factorization Model for Numerical Factorization and Recommender Systems
    """

    def __init__(self, M: Union[torch.Tensor, np.ndarray], dim, mask: Union[torch.Tensor, np.ndarray] = None, bias=True, seed=None):
        """
        Instantiate variables for matrix factorization, i.e. M = A * B + C.
        :param M <torch.Tensor|numpy.ndarray>:      Input 2-D matrix to factorize into A and B (with bias C).
        :param dim <int>:                           Hidden / latent dimension of matrix factorization.
        :param mask <torch.Tensor|numpy.ndarray>:   Element observability mask matrix with shape equivalent to M.
                                                    Non-zero implies True, while zero implies False.
        :param bias <bool>:                         Utilize bias in factorization. Set to False to exclude affine bias C.
        :param seed <int>:                          Random seed fixture for reproducibility.
        """
        super().__init__()

        # Validate matrix.
        if len(M.shape) != 2 or dim <= 0:
            print(
                f"[MatrixInputError] MatrixFactorize() only supports 2-D matrices. " +
                f"Moreover, the hidden dimension is necessarily > 0.",
                file=sys.stderr, flush=True
            )
            return None

        # Class Parameters
        self.M = torch.Tensor(M)
        self.x, self.y = self.M.shape
        self.dim = dim
        self.bias = bias
        self.mask = torch.ones(self.M.shape)
        if mask is not None and mask.shape == self.M.shape:
            # Construct Boolean mask Tensor.
            self.mask = torch.where(torch.Tensor(mask) != 0, 1, 0)

        # Fix random seed.
        self.torch_gen = None
        if seed is not None:
            self.torch_gen = torch.manual_seed(seed)

        # Matrix Factors + Affine Bias
        self.A = Parameter(torch.empty((self.x, self.dim)), requires_grad=True)
        self.B = Parameter(torch.empty((self.dim, self.y)), requires_grad=True)
        self.C = Parameter(torch.empty((self.x, self.y)), requires_grad=True)
        init.xavier_uniform_(self.A)
        init.xavier_uniform_(self.B)
        init.xavier_uniform_(self.C)

        # Track training loss.
        self.loss = float('inf')

    def __repr__(self):
        """
        Output matrix factorization and approximation matrix of M.
        """

        # Avoid scientific notation.
        np.set_printoptions(suppress=True)

        # Collect information.
        output_buffer = f"\nMatrix Factorization Output (M = A * B + C)\n"
        output_buffer += f"\nTarget Matrix:\n\n{self.M.numpy()}\n"
        output_buffer += f"\nMask Matrix:\n\n{self.mask.numpy()}\n"
        output_buffer += f"\nApprox. Matrix:\n\n{self().detach().numpy().round(2)}\n"
        output_buffer += f"\nMatrix A:\n\n{self.A.detach().numpy().round(2)}\n"
        output_buffer += f"\nMatrix B:\n\n{self.B.detach().numpy().round(2)}\n"
        output_buffer += f"\nMatrix C:\n\n{self.C.detach().numpy().round(2)}\n"
        output_buffer += f"\nRegression Loss: {self.loss}\n"

        # Reset notation.
        np.set_printoptions(suppress=False)

        return output_buffer

    def forward(self):
        """
        Compute element of matrix from factorization.
        """

        # Compute product matrix or recommendation.
        output = torch.matmul(self.A, self.B)
        if self.bias:
            # Include affine bias.
            output += self.C
        return output

    def fit(self, cycles=1000, lr=8e-4, regularize=1e-2, patience=3, verbose=False):
        """
        Train the factorization.
        :param cycles <int>:        Number of gradient descent cycles.
        :param lr <float>:          Learning rate. Re-calibrated to order of values in matrix M.
        :param regularize <float>:  Weight decay lambda for regularization in AdamW.
        :param patience <int>:      Number of cycles of convergence before termination.
        :param verbose <bool>:      Output training progress information.
        """

        # Instantiate optimizer. Tune learning rate to order of magnitude of matrix M.
        lr_calibrate = lr * self.M.sum() / torch.numel(self.M)
        optimizer = AdamW(
            self.parameters(),
            lr=lr_calibrate,
            weight_decay=regularize
        )

        # Train factorization.
        factor_opt = dict(self.state_dict())
        timer = 0
        for i in range(cycles):

            # Clear gradients in optimizer.
            self.zero_grad()

            # Compute loss of matrix factorization.
            loss = torch.linalg.norm(
                (self() - self.M) * self.mask
            ) ** 2

            # Back-propagate.
            loss.backward()

            # Apply gradients.
            optimizer.step()

            # Validate training.
            if i % 100 == 0 and verbose:
                print(
                    f"Training Cycle: {i+1} / {cycles} | Factorization Loss: {loss.item()}",
                    file=sys.stdout, flush=True
                )
            if loss.item() < self.loss:
                # Update optimal factorization.
                factor_opt = dict(self.state_dict())
                # Update optimal loss.
                self.loss = loss.item()
                # Reset patience timer.
                timer = 0
            else:
                # Increment patience timer.
                timer += 1
                if timer > patience:
                    # Model convergence. Revert to optimal factorization. Terminate training.
                    self.load_state_dict(factor_opt)
                    break

class FactorizationMachine(Module):
    """
    [Factorization Machine Recommendation Model]
    Learns latent space features to characterize similarity of dataset features
    to compute a recommendation as a function of dataset features. Dataset
    features can be mixed / hybrid such that you can combine information
    on both the recommended object and the recommendation target to generate
    an informed similarity or recommendation / ranking metric.
    """

    def __init__(self, data_dim, hidden_dim=25, seed=None) -> None:
        """
        Instantiate class attributes for FM. Constructs a feature similarity matrix
        F of shape (x_features, hidden_dim) to learn implicit representations of
        all trainable features in the data for recommendation or ranking.
        :param data_dim <int>:      Number of features to learn from in the dataset.
        :param hidden_dim <int>:    Dimension of the latent space of features.
        :param seed <int>:          Random seed fixture for reproducibility.
        """
        super().__init__()

        # Parameters
        self.input_dim = data_dim       # X
        self.hidden_dim = hidden_dim    # H

        # Fix random seed.
        self.torch_gen = None
        if seed is not None:
            self.torch_gen = torch.manual_seed(seed)

        """ Matrix Factorization """

        # Feature Similarity Matrix
        self.F = Parameter(
            torch.empty((self.input_dim, self.hidden_dim)),
            requires_grad=True
        )
        init.xavier_uniform_(self.F)

        """ Linear Regression """

        # Feature Weight Vector and Bias
        self.V = Parameter(
            torch.empty((self.input_dim, 1)),
            requires_grad=True
        )
        init.xavier_uniform_(self.V)
        self.bias = Parameter(
            torch.zeros(1),
            requires_grad=True
        )

        """ Gaussian Regression """

        self.gaussian_dist = Linear(
            in_features=self.hidden_dim,
            out_features=2
        )

    def forward(self, x: torch.Tensor):
        """
        Compute FactorizationMachine(x). Returns a mean and standard deviation for the recommendation.
        :param x <torch.Tensor>:    Factorization machine input Tensor of shape (N, input_dim).
        """

        # Compute square of sum and sum of squares.
        # (N, X) * (X, H) -> (N, H)
        sq_sm = torch.matmul(x, self.F) ** 2
        sm_sq = torch.matmul(x ** 2, self.F ** 2)

        # Compute linear regression model.
        # (N, H) * (H, 1) -> (N, 1)
        lin_reg = torch.matmul(x, self.V)

        # Compute latent feature matrix of shape (N, H).
        latent = self.bias + lin_reg + 0.5 * sq_sm - sm_sq

        # Fit Gaussian distribution to recommendation.
        # (N, H) -> (N, 2)
        output = self.gaussian_dist(latent)

        # Output recommendation / ranking score distribution, i.e. FM(x).
        return output[:, 0], torch.abs(output[:, 1])

    def fit(
        self,
        X: Union[torch.Tensor, np.ndarray],
        Y: Union[torch.Tensor, np.ndarray],
        mask: Union[torch.Tensor, np.ndarray] = None,
        cycles=100,
        lr=2e-3,
        batch_frac=0.01,
        regularize=1e-2,
        patience=3,
        verbose=False
    ):
        """
        Train the Factorization Machine.
        :param X <torch.Tensor>:        Input training data features of shape (N, X).
        :param Y <torch.Tensor>:        Target training data class / score vector of shape (N, 1).
        :param mask <torch.Tensor>:     Feature observability mask for X of shape (N, X).
        :param cycles <int>:            Number of gradient descent cycles.
        :param lr <float>:              Learning rate. Re-calibrated to order of values in matrix M.
        :param batch_frac <float>:      Fraction of the dataset to set as the batch size.
        :param regularize <float>:      Weight decay lambda for regularization in AdamW.
        :param patience <int>:          Number of cycles of convergence before termination.
        :param verbose <bool>:          Output training progress information.
        """

        # Validate arguments.
        if any([
            len(X.shape) != 2,
            len(Y.shape) != 2,
            mask is not None and mask.shape != X.shape,
            X.shape[1] != self.input_dim,
            Y.shape[1] != 1,
            cycles <= 0,
            lr <= 0,
            batch_frac <= 0,
            regularize < 0
        ]):
            print(
                f"[FactorizationMachine.fit()] Improper training argument(s) to fit(). Aborting... \n" +
                f"""FactorizationMachine.fit() Requirements:
                - X is a (N, data_dim) Tensor and Y is a (N, 1) Tensor.
                - mask is an optional (N, data_dim) Tensor.
                - cycles > 0
                - lr > 0
                - batch_frac > 0
                - regularize >= 0
                """,
                file=sys.stderr, flush=True
            )
            return

        # Convert and instantiate Tensor(s).
        N = X.shape[0]
        if not torch.is_tensor(X):
            X = torch.Tensor(X)
        if not torch.is_tensor(Y):
            Y = torch.Tensor(Y)
        mask_tensor = torch.ones(X.shape)
        if mask is not None:
            # Construct Boolean mask Tensor.
            mask_tensor = torch.where(torch.Tensor(mask) != 0, 1, 0)

        # Instantiate optimizer.
        optimizer = AdamW(
            self.parameters(),
            lr=lr,
            weight_decay=regularize
        )

        # Train Factorization Machine.
        model_opt = dict(self.state_dict())
        loss_opt = float('inf')
        timer = 0
        for i in range(cycles):

            # Train on batches of data.
            for _ in range(math.ceil(1 / batch_frac)):

                # Generate random batches by index.
                rand_idx = torch.randint(
                    N,
                    size=(math.ceil(batch_frac * N),),
                    generator=self.torch_gen
                )

                # Extract batch.
                X_batch = X[rand_idx]
                Y_batch = Y[rand_idx]
                mask_batch = mask_tensor[rand_idx]

                # Clear gradients in optimizer.
                self.zero_grad()

                # Compute model prediction mean and deviation.
                Y_mu, Y_sigma = self(X_batch * mask_batch)

                # Compute Gaussian distributional loss.
                loss = GaussianNLLLoss()(Y_mu, Y_batch, Y_sigma)

                # Back-propagate.
                loss.sum().backward()

                # Apply gradients.
                optimizer.step()

            # Validate training.
            if i % math.ceil(cycles / 5) == 0 and verbose:
                print(
                    f"Training Cycle: {i+1} / {cycles} | Recommendation Loss: {loss.item()}",
                    file=sys.stdout, flush=True
                )
            if loss.sum().item() < loss_opt:
                # Update optimal factorization.
                model_opt = dict(self.state_dict())
                # Update optimal loss.
                loss_opt = loss.sum().item()
                # Reset patience timer.
                timer = 0
            else:
                # Increment patience timer.
                timer += 1
                if timer > patience:
                    # Model convergence. Revert to optimal factorization. Terminate training.
                    self.load_state_dict(model_opt)
                    break


import numpy as np
import pandas as pd
students = pd.read_csv('students.csv')

scores_urban = students.G3[students.address == 'U']
scores_rural = students.G3[students.address == 'R']

#calculate means for each group:
scores_urban_mean = np.mean(scores_urban)
scores_rural_mean = np.mean(scores_rural)

#print mean scores:
print('Mean score - students w/ urban address:')
print(scores_urban_mean)
print('Mean score - students w/ rural address:')
print(scores_rural_mean)

#calculate mean difference:
mean_diff = scores_urban_mean - scores_rural_mean

#print mean difference
print('Mean difference:')
print(mean_diff)

#calculate medians for each group:
scores_urban_median = np.median(scores_urban)
scores_rural_median = np.median(scores_rural)

#print median scores
print('Median score - students w/ urban address:')
print(scores_urban_median)
print('Median score - students w/ rural address:')
print(scores_rural_median)

#calculate median difference
median_diff = scores_urban_median - scores_rural_median

#print median difference
print('Median difference:')
print(median_diff)

Data Scientist Career Path/7. Summary Statistics/8. Associations between Variables/1. Quantitative and Categorical/2. mean median.py


import math
import numpy as np

def sign(diff):
    return 1 if diff == 0 else (-1,1)[diff>0]

def sqr(x):
    return x*x

def dist(x1,y1,x2,y2):
    return math.sqrt(sqr(x1-x2)+sqr(y1-y2))

def distc(p1,p2):
    return dist(*p1,*p2)

def closest(points,target):
    min_dist = 10e50
    min_idx = None
    tx,ty = target
    for idx,(px,py) in enumerate(points):
        d = dist(tx,ty,px,py)
        if d < min_dist:
            min_dist = d
            min_idx = idx
    return min_idx

COORDS_AROUND = ((-1,0),(0,-1),(1,0),(0,1))

def neg(p):
    x,y = p
    return (-x,-y)

def scalar_mul(p,m):
    x,y = p
    return (x*m,y*m)

def sub(p1,p2):
    return add(p1,neg(p2))

def add(p1,p2):
    ax,ay = p1
    bx,by = p2
    return (ax+bx,ay+by)

def midpoint(source,dest):
    vector = sub(dest,source)
    half_vec = scalar_mul(vector,0.5)
    midpoint = add(source,half_vec)
    return midpoint

def raytrace2dgrid(x1,y1,x2,y2):
    RANGE_LEN = 100
    range = np.arange(RANGE_LEN)/RANGE_LEN
    xvec = (x2-x1)*range + x1
    yvec = (y2-y1)*range + y1
    pointvec = np.stack([xvec,yvec],axis=1)
    #alts = np.concatenate([pointvec
    #,pointvec+np.asarray([1,0])
    #,pointvec+np.asarray([0,1])
    #,pointvec+np.asarray([0.5,0.5])
    #,pointvec+np.asarray([0.5,-0.5])],axis=0)
    tuples = pointvec.astype(np.int32).tolist()
    res = []
    prev = None
    for p in tuples:
        if prev != p:
            res.append(tuple(p))
            prev = p
    res.pop()
    return res
    '''cx = int(x1)
    cy = int(y1)
    tx = int(x2)
    ty = int(y2)
    dirx = sign(x2-x1)
    diry = sign(y2-y1)
    #print(dirx)
    #print(diry)
    ratio = (y2-y1+1e-7)/(x2-x1+1e-7)
    coord_list = []
    def dist_line(nx,ny):
        return abs((y1)+ratio*(nx-(x1)) - ny)
    while not (cx == tx and cy == ty):
        # if abs(y1+ratio*(cx-x1) - y1)
        if dist_line(cx+dirx,cy) < dist_line(cx,cy+diry):
            cx += dirx
        else:
            cy += diry
        #print((cx,cy))
        coord_list.append((cx,cy))
        #if len(coord_list) > 10:
        #    return

    if len(coord_list):
        coord_list.pop()# remove x2,y2 from list

    return coord_list'''

def get_rays(cen,num_rays,radius):
    rays = []
    ax,ay = cen
    for angle in range(num_rays):
        radians = math.pi * 2 * angle / num_rays
        tx = ax + math.cos(radians)*radius
        ty = ay + math.sin(radians)*radius
        rays.append((tx,ty))

    return rays

if __name__=="__main__":
    print(raytrace2dgrid(1.5,1,3,-2))
    print(raytrace2dgrid(0,0,10,-0.2))
    print(raytrace2dgrid(0,0,0,10))
    #exit(0)
    # tot_list = []
    # for x in range(100000):
    #     tot_list.append(raytrace2dgrid(1,1,10,5)[0])
    # print(tot_list[-3])


from numpy import *
from numpy.linalg import solve,norm

#
# -1- Droite au sens de moindres carrés usuels
#

def superLineInitialGuest(X,U):

    A = array([power(X,i) for i in range(2)]).T
    b,a = solve(A.T@A, A.T@U)

    return array([a,b])

#
# -------------------------------------------------------------------------
#
# -2- Iteration de Newton-Raphson pour obtenir
#     les coefficients d'une droite minimisant la "vraie distance" entre
#     les données et la droite
#

def superLineIterate(X,U,alpha):

    a,b = alpha

    # Variables intermédiaires :
    z = 1 + a*a
    y = z*z
    dist = U - a*X - b

    # Dérivées partielles premières :
    part_a = -(2*a/y)*(dist@dist) - (2/z)*(dist@X)
    part_b = -(2/z)*sum(dist)

    gradient = array([part_a, part_b])

    # Dérivées partielles secondes :
    part_a_a = ((-2*z + 8*a*a)/(y*z))*(dist@dist) + (4*a/y)*(X@dist) + (4*a/y)*sum(dist) + (2/z)*(X@X)
    part_a_b = (4*a/y)*sum(dist) + (2/z)*sum(X)
    part_b_b = (2/z)*len(X)

    hessienne = array([[part_a_a, part_a_b],[part_a_b, part_b_b]])

    dalpha = solve(hessienne, -gradient)

    return dalpha


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

def gaussian(x, u, sigma):
    return math.exp(-(x - u) ** 2 / (2 * sigma * sigma)) / math.sqrt(2 * math.pi * sigma * sigma)

def importance_sampling_test(ori_sigma, sample_sigma):
    origin = []
    for n in range(10):
        sum = 0
        for i in range(100000):
            a = np.random.normal(1.0, ori_sigma)
            sum += a
        origin.append(sum)

    isample = []
    for n in range(10):
        sum2 = 0
        for i in range(100000):
            a= np.random.normal(1.0, sample_sigma)
            ua = gaussian(a, 1.0, sample_sigma)
            na = gaussian(a, 1.0, ori_sigma)
            sum2 += a * na / ua
        isample.append(sum2)

    origin = np.array(origin)
    isample = np.array(isample)
    print np.mean(origin), np.std(origin)
    print np.mean(isample), np.std(isample)

importance_sampling_test(1.0, 1.0)
importance_sampling_test(1.0, 0.5)
importance_sampling_test(1.0, 2)

xs = np.linspace(-5, 6, 301)
y1 = [gaussian(x, 1.0, 1.0) for x in xs]
y2 = [gaussian(x, 1.0, 0.5) for x in xs]
y3 = [gaussian(x, 1.0, 2) for x in xs]

fig = plt.figure(figsize=(8,5))
plt.plot(xs, y1, label='sigma=1.0')
plt.plot(xs, y2, label='sigma=0.5', linestyle=':')
plt.plot(xs, y3, label='sigma=2.0', linestyle='--')
plt.legend()
plt.show()


from math import sqrt
from statistics import stdev
from scipy.stats import norm, t, chi2, f
from numpy import array

DecnWords = {True: "SUCCESS to REJECT null hypothesis.",
             False: "FAIL to REJECT null hypothesis."}

class SampleGroup():
    def __init__(self, popStdevKnown, n, xbar, sigma):
        '''
sigma --->  Positive real number.
            It stands for sample deviation if popStdevKnown parameter is False,
            stands for population deviation if popStdevKnown parameter is True
popStdevKnown ---> True if population deviation is known, False if population deviation is unknown
'''
        self.n = n
        self.xbar = xbar
        self.sigma = sigma
        self.is_popVarianceKnown = popStdevKnown

def used_test(SampleGroup):
    if SampleGroup.is_popVarianceKnown:
        return("z-score test is applied.")
    else:
        return("t-student test is applied.")

#----------------------------------------------------------------------------------------------------------------------
class SampleProp():
    def __init__(self, prop, n):
        self.prop = prop
        self.n = n

#======================================================================================================================
                               # Normal distributions
#======================================================================================================================

def zalpha_RTT(alpha):
    zalpha = norm.ppf(1-alpha)
    return(zalpha)

def zalpha_LTT(alpha):
    zalpha = norm.ppf(alpha)
    return(zalpha)

def zalpha_2TT(alpha):
    zalpha = abs(norm.ppf(alpha/2))
    return(zalpha)

zalphaTails_Dict = {'>': zalpha_RTT,
                    '<': zalpha_LTT,
                    '!=': zalpha_2TT}
#======================================================================================================================

def talpha_RTT(alpha, dof):
    talpha = t.ppf(1-alpha, dof)
    return(talpha)

def talpha_LTT(alpha, dof):
    talpha = t.ppf(alpha, dof)
    return(talpha)

def talpha_2TT(alpha, dof):
    talpha = abs(t.ppf(alpha/2, dof))
    return(talpha)

talphaTails_Dict = {'>': talpha_RTT,
                    '<': talpha_LTT,
                    '!=': talpha_2TT}
#======================================================================================================================

def decn_RTT(vtest, valpha):
    if vtest < valpha: return(False)  #Fail to reject null hypothesis
    if vtest >= valpha: return(True)  #Success to reject null hypothesis

def decn_LTT(vtest, valpha):
    if vtest <= valpha: return(True)    #Success to reject null hypothesis
    if vtest > valpha: return(False)  #Fail to reject null hypothesis

def decn_2TT(vtest, valpha):
    if -valpha<vtest<valpha: return(False)                  #Fail to reject null hypothesis
    if (vtest<-valpha or vtest>valpha): return(True)        #Success to reject null hypothesis

Decision = {'>': decn_RTT,
            '<': decn_LTT,
            '!=': decn_2TT}
#======================================================================================================================
                                       # Chi-Square distributions
#======================================================================================================================

def x2alpha_RTT(alpha, dof):
    x2alpha = chi2.ppf(1-alpha, dof)
    return(x2alpha)

def x2alpha_LTT(alpha, dof):
    x2alpha = chi2.ppf(alpha, dof)
    return(x2alpha)

def x2alpha_2TT(alpha, dof):
    x2alpha = alpha/2
    x2alpha_lt = chi2.ppf(x2alpha, dof)
    x2alpha_rt = chi2.ppf(1-x2alpha, dof)
    return((x2alpha_lt, x2alpha_rt))

chi2_alphaTails_Dict = {'>': x2alpha_RTT,
                    '<': x2alpha_LTT,
                    '!=': x2alpha_2TT}

def decn_2TT_chi2(vtest, chi_tuple):
    if(chi_tuple[0] < vtest < chi_tuple[1]): return(False)          #Fail to reject null hypothesis
    if(vtest<chi_tuple[0] or vtest>chi_tuple[1]): return(True)      #Success to reject null hypothesis

Decision_chi2 = {'>': decn_RTT,
                 '<': decn_LTT,
                 '!=': decn_2TT_chi2}
#======================================================================================================================


import numpy as np
from sklearn import decomposition

# Define individual features
x1 = np.random.normal(size=250)
x2 = np.random.normal(size=250)
x3 = 3*x1 + 2*x2
x4 = 6*x1 - 2*x2
x5 = 3*x3 + x4

# Create dataset with the above features
X = np.c_[x1, x3, x2, x5, x4]

# Perform Principal Components Analysis
pca = decomposition.PCA()
pca.fit(X)

# Print variances
variances = pca.explained_variance_
print('Variances in decreasing order:\n', variances)

# Find the number of useful dimensions
thresh_variance = 0.8
num_useful_dims = len(np.where(variances > thresh_variance)[0])
print('Number of useful dimensions:', num_useful_dims)

# As we can see, only the 2 first components are useful
pca.n_components = num_useful_dims
XNew = pca.fit_transform(X)
print('Shape before:', X.shape)
print('Shape after:', XNew.shape)


import numpy as np

a1 = np.array([[10, 11, 12, 13, 14],
               [15, 16, 17, 18, 19],
               [20, 21, 22, 23, 24],
               [25, 26, 27, 28, 29]])
print(a1[1,2:4])  # [17 18]

a2 = np.array([[10, 11, 12, 13, 14],
               [15, 16, 17, 18, 19],
               [20, 21, 22, 23, 24],
               [25, 26, 27, 28, 29]])

print(a2[1:2,2:4])  # [[17 18]]


import numpy as np
#from quaternions import Quaternion as Q

class KalmanFilter:
	def __init__(self, error_measurement, starting_point=None):
		self.shape = error_measurement.shape
		self.error_estimation = error_measurement
		self.error_measurement = error_measurement
		self.previous_estimation = np.zeros(self.shape) if starting_point is None else starting_point

	def update(self, measurement):
		kalman_gain = (self.error_estimation)/(self.error_estimation+self.error_measurement)
		estimate = self.previous_estimation + kalman_gain*(measurement-self.previous_estimation)
		self.error_estimation = (1-kalman_gain)*self.error_estimation
		self.previous_estimation = estimate
		return estimate

class SimpleCalibration:
	def __init__(self, calib_data, mean=None, common_axes=False, apply_treshold=True, normalize=False):
		data = np.array(calib_data)
		self.mean = np.mean(data,axis=0)
		self.pedestal = self.mean if mean==None else mean
		self.normalize = normalize
		if common_axes:
			self.mean = np.mean(self.mean)
		self.norm = np.linalg.norm(self.mean)
		self.stdev = np.std(data,axis=0)
		self.apply_threshold = apply_treshold
		self.threshold = 3*self.stdev
		self.threshold = self.threshold.astype(int)

	def apply(self, raw):
		data = raw - self.pedestal
		if self.apply_threshold:
			data = (data/self.threshold).astype(int) #unitize
			data = data*self.threshold
		if self.normalize:
			data = data/self.norm
		return data

	def level_out(self, calib_data):
		data = np.array(calib_data)
		print(self.mean)
		self.mean = np.mean(data, axis=0)
		self.pedestal = self.mean

	def __str__(self):
		return "Mean:{} \n STD:{}\n norm:{}".format(self.mean, self.stdev, self.norm)

class TiltCorrector:
	def __init__(self, base_acc, desired_acc):
		v1 = base_acc
		v2 = np.array(desired_acc)
		v1 = v1 / np.linalg.norm(v1)
		k = np.cross(v1, v2)
		w = 1.0 + v1.dot(v2)
		q = Q(w, *k)
		q = q.unit()
		#self.q2 = Q(0.638554171917, 0.0, 0.730193472549, -0.243035104817)
		self.q = q

	def correct_tilt(self, vector):
		norm = np.linalg.norm(vector)
		vector = vector / norm
		q1 = Q(0, *vector)
		r = self.q*q1*self.q.conjugate()
		#r = self.q2*r*self.q2.conjugate()
		return (np.array([r.x, r.y, r.z])*norm).astype(int)

	def __str__(self):
		return "Tilt correction quaternion:\n{}".format(self.q)


import math
import random
import numpy as np

#this algorithm is a MINIMIZATION  specific implementation of a discrete method called Ant Colony Optimization.

#method/function space
def object_funct_eval(path, var_table):
    """
    Given a path and the array with all the discrete values per layer, the value of the obj. Function is returned as a result
    :param path: the path of a specific ant
    :param var_table: the array representing the discrete values of the layers
    :return: the value of the objective function for the specific path
    """
    length = len(path)
    my_vars = []
    for i in range(length):
        j = path[i]
        my_vars.append(var_table[i][j])
    #print(my_vars)
    #objective function - Ackley
    #my_value = -20*math.exp(-0.2*math.sqrt(0.5*((my_vars[0]**2 + (my_vars[1]**2))))) - math.exp(0.5*(math.cos(my_vars[0]*2*math.pi) + math.cos(my_vars[1]*2*math.pi))) +math.e + 20
    #Booth (1, 3)
    my_value = (my_vars[0] + 2*my_vars[1] -7)**2 + (2*my_vars[0] + my_vars[1] - 5)**2
    #my_value = my_vars[0]**2 - my_vars[0] - 11
    #beale (3, 0.5)
    #my_value = (1.5 - my_vars[0] + my_vars[0] * my_vars[1]) ** 2 + (2.25 - my_vars[0] + my_vars[0] * (my_vars[1] ** 2)) ** 2 + (2.625 - my_vars[0] + my_vars[0] * (my_vars[1] ** 3)) ** 2
    return my_value
def calc_discrete_values(upper, lower, r):
    """

    :param range: range of accuracy for the permissible discrete values
    :param upper: upper bounds
    :param lower: lower bounds
    :return: d the number of discrete values per variable
    """
    d = (upper[0] - lower[0]) /r + 1
    return d

def create_pheromone_table(n_var, disc):
    """
     My pheromone table acts like a neighbor-table for describing the arcs of my graph
     and the pheromones in the trails. Its cells contain the amount of pheromone in the arc
     connecting the row with the collumns. The pheromone value for all the arc is initialized as 1.
     IT MUST BE THE SAME FOR ALL THE ARCS INITIALLY. The last row remains table, it represents the food source.

     The node or layer  'zero' meaning the first row  is considered the home. (since we count beginning from zero)
    :param n_var: how many variables the object function has
    :param disc: number for permissible discrite values for each design variable

    :return: the initial form of the 2d-numpy array for the pheromone table

    """
    #numpy array N-dimensions . rows 1 for home + n_var, columns for the discrete values [0 ...d-1]
    ph_array = np.ones((n_var + 1, disc))
    #note : MIGHT need to change some nodes to zero
    return ph_array

def pheromone_trail_update(pher_table,bestants, worstants, fbest, fworst,bestpath):
    """

    :param pher_table: the pheromone array . *using a numpy array
    :param bestants: list of the best ants represented by their position in the colony
    :param worstants: the list of the worst ants
    :param fbest: the best obj.value for the current iteration
    :param fworst: the worst obj.value for the current iteration
    :param bestpath: the path the best ants have followed
    :return: the changes are directly made to the array (matrix) itself  thus i don't need to return a new array
    """
    #trail update, initialize my parameters
    scaling_par = 3
    decay_factor = 0.005
    #the added sum is only useful to the best path
    added_sum = 0
    b_len = len(bestants)
    w_len = len(worstants)

    added_sum = (scaling_par * b_len * fbest)/ (w_len * fworst)
    #print("added sum =", added_sum)
    length = len(pher_table) - 1
    #update the paths, check if an arc belongs to the best one
    for i in range(length):
        for j in range(len(pher_table[0])):
            if j == bestpath[i]:
                pher_table[i][j] += added_sum
            else:
                pher_table[i][j] = (1 - decay_factor) * pher_table[i][j]

def find_best_path(bestants, colony):
    """
    Given the ids of the best ants in the colony (at least one) ,the best path is returned.
    :param bestants: a list which contains at least one element, it's the ants with the best (min) value for the obj. function
    :param colony: the colony of the ants
    :return: a list of length the same as the number of variables of the obj.function. It's the best path in the iteration
    """
    my_ant = bestants[0]
    print("I m checking ant :", my_ant)
    path = colony[my_ant][0]
    return path

def create_layer_table(n_var, r, lower, upper):
    """
    Creates the design variables table. It represents the permitted values for each variable during the optimization.
    The permitted values get created according to a specific range given.
    :param n_var: number of variables
    :param lower, upper: lower and upper bounds
    :param r: we spread the values by adding r for each permitted discrete value (r:range)
    :return: 2-d array of n_var x p to represent the discrete values of each design variable
    """
    d = (upper[0] - lower[0]) / r + 1
    layer_array = np.zeros((n_var, d))
    #counter is used for future implementations when the limits for each variable change, for this first version the value
    #of d remains the same and the functions used have the same limit for each variable
    counter = 0
    iterator = lower[counter]
    for j in range(n_var):
        for i in range(int(d)):
            layer_array[j][i] = iterator
            iterator = iterator + r
        iterator = lower[counter]
    return layer_array

def create_ant(var_num):
    """

    :param var_num: number of variables needed for the obj.function
    :return: a combined list of 2 elements. Element 0 is the path as a list. Element 1 is the obj. value.
    """

    path = []
    #for initial values we believe the ants chose the 0-th discrete value. (count method 0 ...n-1)
    for i in range(var_num):
        path.append(0)
    #for initial value i choose a big one , but it depends on the nature of the obj. function
    value = 1000
    ant = []
    ant.append(path)
    ant.append(value)
    return ant

def create_colony(N, var_num):
    """
    creates the initial colony of ants, it is in a list form
    :param N: size of colony
    :param var_num: number of variables - it's needed to create ants individually before adding them to the colony
    :return:
    """
    colony = []
    for i in range(N):
        colony.append(create_ant(var_num))
    return colony

def prob_handling(var_table, var_tag, d, ph_table):
    """
    A roulette stohastic method of choosing which path to follow.
    :param var_table: the numpy array representing the discrete values
    :param var_tag: tag of the variable
    :param d: number of discrete values per variable
    :param ph_table: pheromone trail array
    :return: returns whick discrete value the ant k picked for a specific layer
    """
    #this is probability handling for ONE ant k regarding its choice for ONE variable
    prob_list=[]
    my_sum = 0
    #print("vartag",var_tag)
    for i in range(d):
        my_sum += ph_table[var_tag][i]

    #print("my sum",my_sum)
    for i in range(d):
        prob_list.append(ph_table[var_tag][i]/my_sum)
    #print("prob list", prob_list)
    #the way the ant picks a specific route is with a roulette selection on the prob.list
    roll = round(random.uniform(0, 1),3)
    #print("my roll:", roll)
    my_sum = 0
    my_pick= 0
    for i in range(d):
        my_sum += prob_list[i]
        if (roll<= my_sum or i == d-1):
            my_pick = i
            break
    #print("my pick is:",my_pick)
    #I return the ant's choice
    return my_pick
def count_ants(colony, value):
    ant_list = []
    for i in range(len(colony)):
        if colony[i][1] == value:
            ant_list.append(i)
    return ant_list

def check_convergence(antlist, N):
    flag = False
    if len(antlist) == int(N):
        flag = True
    return flag
def find_solution(bpath, var_table):
    """

    :param bpath: the best path
    :param var_table: table containing the discrete values
    :return: the discrete values representing given path
    """
    solution = []
    length = len(bpath)
    for i in range(length):
        j = bpath[i]
        solution.append(var_table[i][j])
    return solution

#main space
def mainloop():
    #create the parameters and run the algorithm via the functions
    N = 30
    myrange = 0.5
    l_bound = [-10, -10]
    u_bound = [10, 10]
    n_var = 2
    #d = number of discrete values for each variable
    d = int(calc_discrete_values(u_bound,l_bound,myrange))
    test_array_layer = create_layer_table(n_var, myrange, l_bound, u_bound)
    print("array layer:", test_array_layer)
    test_ph_array = create_pheromone_table(n_var, d)
    print("ph array:", test_ph_array)
    iteration = 1
    iteration_limit = 1000
    test_colony = create_colony(N,n_var)

    convergence_flag = False

    while convergence_flag == False and iteration <= iteration_limit:
        #for the colony each ant chooses a path going from Layer-to-Layer
        var_counter = 0
        while var_counter < n_var :
            #print("Choosing for variable:", var_counter)
            for ant in test_colony:
                #print("var counter",var_counter)
                ant[0][var_counter] = prob_handling(test_array_layer,var_counter,d,test_ph_array)
            var_counter += 1
        for ant in test_colony:
            ant[1] = object_funct_eval(ant[0], test_array_layer)

        #initialize best and worst values
        bestf = test_colony[0][1]
        worstf = test_colony[0][1]
        bestants = []
        worstants = []

        for ant in test_colony:
            if ant[1]<= bestf:
                bestf = ant[1]
        for ant in test_colony:
            if ant[1]>= worstf:
                worstf = ant[1]

        bestants = count_ants(test_colony,bestf)
        worstants = count_ants(test_colony, worstf)
        #print("my colony:", test_colony)
        print("best value and worst value ", bestf, ",", worstf)
        print("best ants", bestants)
        #print("worst ants", worstants)
        best_path = find_best_path(bestants,test_colony)
        print("best_path:", best_path)
        pheromone_trail_update(test_ph_array, bestants, worstants, bestf, worstf,best_path)
        print("new trails", test_ph_array)
        convergence_flag = check_convergence(bestants,N)
        #creating a new colony for the following iteration
        #test_colony = create_colony(N,n_var)
        iteration += 1

    solution = find_solution(best_path,test_array_layer)
    print("The min of the objective value is :", solution, "and the value is :", object_funct_eval(best_path, test_array_layer) )

if __name__ == '__main__':
    mainloop()


# Start initialization
import numpy as np
import pylab as p

# Make an x,z grid
grid = np.linspace(-6,6,201)
x, z = np.meshgrid(grid,grid)
mu=4*np.pi  # dipole strength (in z-direction)
# and the 2D cross section of the potential (scalar field)
phi = mu * z / (4 * np.pi * (x**2+z**2)**(3/2))
# End initialization

# Start contour plot
from matplotlib import colors
p.figure(figsize=(6,6))
heights = np.linspace(-0.6,0.6,7)
CS = p.contour(x,z,phi,heights,colors='k')
p.clabel(CS, inline=1, fontsize=10)
p.xlabel(r'$x$')
p.ylabel(r'$z$')
# End contour plot
p.savefig('dipole.png')

# Start gradient plot
#--p.figure(figsize=(6,6))
#--grid_course =  np.linspace(-10000,10000,11)
#--x_c, y_c = np.meshgrid(grid_course,grid_course)
#--radius = 2000
#--outside = x_c**2 + y_c**2 > radius**2
#--gradh_x = - 2*h0 / (R0**2 * (1 + (x_c**2+y_c**2)/R0**2)**2) * x_c
#--gradh_y = - 2*h0 / (R0**2 * (1 + (x_c**2+y_c**2)/R0**2)**2) * y_c
#--p.quiver(x_c[outside]/1000,y_c[outside]/1000,gradh_x[outside],gradh_y[outside],scale=3)
#--p.xlabel(r'$x$ (km)')
#--p.ylabel(r'$y$ (km)')
#--# End gradient plot
#--p.savefig('hojd_gradient.png')

#p.show()