import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import math
from scipy import stats

# data source: https://ourworldindata.org/coronavirus-source-data

def setup(csv_filename, country):
    df = pd.read_csv(csv_filename)
    df = df.loc[: , ["location", "date", "new_cases" ]]
    df = df[df["location"] == country]
    # Data prep
    df = df[df["new_cases"].notna()] # remove not a number-rows
    df = df[df["new_cases"] > 0]
    df.drop_duplicates()
    print(df.head())
    return df

def statistics(df):
    print("Dataset size", len(df["new_cases"].tolist()))

def calculate_first_digit(df):
    new_cases = df["new_cases"]
    first_digit = []
    for row in df["new_cases"]: # get first digit
        try:
            first_digit.append(int(str(row)[:1]))
        except:
            first_digit.append(0)
            print(row)
    df["first_digit"] = first_digit
    df = df.drop(df[df.first_digit <= 0].index) # drop rows with 0 values
    n = len(df["first_digit"].tolist())
    count_first_digit = df["first_digit"].value_counts(sort=False)#count number of 1's, 2's, 3's and so on
    count_first_digit.to_frame().to_numpy()
    total_count = count_first_digit.sum() # number of numbers in list. Equal to len(df["first_digit"].tolist())
    percentage = []
    for elem in count_first_digit:
        p = float("{:.4f}".format( elem / total_count))
        percentage.append(p)
    x = np.linspace(1,9,9)
    percentage = dict(zip(x, percentage))
    return df, percentage

def calculate_first_two_digits(df):
    first_two = []
    for row in df["new_cases"]:
        temp_int = int(row*10)
        first_two.append(int(str(temp_int)[:2]))
    df["first_two"] = first_two

    count_first_two = df["first_two"].value_counts(sort=False)[1:]
    print(count_first_two)
    count_first_two.to_numpy()
    total_count = count_first_two.sum()
    percentage = []
    for elem in count_first_two:
        percentage.append(float("{:.4f}".format( elem / total_count)))
    return df, percentage

def plot_figure(percentage, perfect_benford):
    _x = np.linspace(1, 9, 9)
    plt.plot(_x,percentage, label="first digit benford") # calculated perfentage
    plt.plot(_x, list(perfect_benford.values()), label="perfect benford")
    plt.xlabel("Digits")
    plt.ylabel("Percentage")
    plt.legend()
    plt.show()

def get_perfect_benford():
    x = np.linspace(1,9,9)
    y = [0.31, 0.176, 0.125, 0.097,0.079, 0.067, 0.058, 0.051, 0.046]
    return dict(zip(x,y))

def pearson_coefficient(list_a, list_b):
    assert (len(list_a) != 0)
    assert (len(list_b) != 0) # list b is perfect benford
    sum_a = sum(list_a)
    sum_b = sum(list_b)
    mean_a = float(sum_a / len(list_a))
    mean_b = float(sum_b / len(list_b))
    list_mean_a = [(x - mean_a) for x in list_a]
    list_mean_b = [(y - mean_b) for y in list_b]
    numerator = sum(x * y for x,y in zip(list_mean_a, list_mean_b))
    denominator = math.sqrt(sum(x*x for x in list_mean_a) * sum(y * y for y in list_mean_b))
    if (denominator != 0):
        p_value = numerator / denominator
    else:
        p_value = 0
    print("------ Pearson coefficient --------")
    print(p_value)
    return p_value

def mantissa_arc_test(list_a):
    """
        The mantissa arc test.
        :parm list_a: df["new_cases"]
        :return: p-value and a plot
    """
    x_coordinates = [(math.cos(2*math.pi * (math.log10(x) % 1))) for x in list_a] # abscissa - x-coordinate for the mantissa
    y_coordinates = [(math.sin(2*math.pi * (math.log10(x) % 1))) for x in list_a] # ordinate

    x_nominator = sum(math.cos(2*math.pi * (math.log10(x) % 1)) for x in list_a)
    y_nominator = sum(math.sin(2*math.pi * (math.log10(x) % 1)) for x in list_a)
    x_coordinate = x_nominator / len(list_a) # Center of mass
    y_coordinate = y_nominator / len(list_a) # Center of mass

    L_squared = (x_coordinate)**2 + (y_coordinate)**2
    p_value = 1 - math.exp(-L_squared * len(list_a))
    print("--------- p-value ---------")
    print(p_value)

    ''' Plotting '''
    plt.scatter(x_coordinates, y_coordinates)
    plt.plot(x_coordinate, y_coordinate, 'o', color="red") # center of mass
    plt.axhline(y=0, color='k')
    plt.axvline(x=0, color='k')
    plt.show()

### TODO: implement chi-squared
def chi_squared(df, perfect_benford):
    '''
    #observed, expected ....
    H0: status_que_hypothesis
    '''
    sample_size = len(df["new_cases"].tolist())
    benford_distribution = [sample_size*x for x in perfect_benford] # expected distribution
    count_first_digit = df["first_digit"].value_counts(sort=False)
    residual_squared = [math.pow(x-y, 2) / y for x,y in zip(count_first_digit, benford_distribution)]
    degrees_of_freedom = (9-1)*(2-1)
    p_value= stats.chi2.pdf(sum(residual_squared), degrees_of_freedom)
    print("chi squared p-value: ",p_value)
    #print("residual", residual)
    #print(benford_distribution)
    #print("Not implemented yet")

def main():
    df = setup("owid-covid-data.csv","Belgium") #csv_filename, country
    statistics(df) # Get info about the dataset
    df, percentage = calculate_first_digit(df)
    print(df.head())
    chi_squared(df, list(get_perfect_benford().values()))
    pearson = pearson_coefficient(list(percentage.values()), get_perfect_benford())
    mantissa_arc_test(df["new_cases"].tolist())
    plot_figure(list(percentage.values()), get_perfect_benford())

if __name__ == "__main__":
    main()


'''
Ankit Khandelwal
15863
Home Work 7
Exercise 4
'''

from math import sin, sqrt

import numpy as np

def f(x):
    return sin(1 / (x * (2 - x))) ** 2

a = 2227057010910366687
M = 2 ** 64 - 59
c = 0

N = 10000
area = 2

x1 = np.zeros(N + 1)
y1 = np.zeros(N + 1)
z1 = np.zeros(N)
x2 = np.zeros(N + 1)
y2 = np.zeros(N + 1)
z2 = np.zeros(N)
x3 = np.zeros(N + 1)
y3 = np.zeros(N + 1)
z3 = np.zeros(N)

X0 = 0
X1 = 2
Y0 = 0
Y1 = 1

x1[0] = 154968415  # seed
x2[0] = 25163788741  # seed
x3[0] = 3135897647  # seed

for i in range(1, N + 1, 1):
    x1[i] = int(a * x1[i - 1] + c) % M
    y1[i] = x1[i] / (M - 1)
    z1[i - 1] = X0 + (X1 - X0) * y1[i]
    x2[i] = int(a * x2[i - 1] + c) % M
    y2[i] = x2[i] / (M - 1)
    z2[i - 1] = Y0 + (Y1 - Y0) * y2[i]
    x3[i] = int(a * x3[i - 1] + c) % M
    y3[i] = x3[i] / (M - 1)
    z3[i - 1] = X0 + (X1 - X0) * y3[i]

k = 0
summ = 0
summ_2 = 0
for i in range(N):
    summ = summ + f(z3[i])
    summ_2 = summ_2 + f(z3[i]) ** 2
    if z2[i] <= f(z1[i]):
        k = k + 1
I1 = k * area / N
I2 = 2 / N * summ
var1 = N * (I1 / area) * (1 - I1 / area)
e1 = sqrt(var1) * area / N
var2 = summ_2 / N - (summ / N) ** 2
e2 = (2) * sqrt(var2) / sqrt(N)
print('From hit and miss, I = {}, error = {}'.format(I1, e1))
print('From mean value, I = {}, error = {}'.format(I2, e2))

Ankit Khandelwal_HW7/Exercise 4/exercise 4.py


"""
Module that contains commonly used loss-functions in machine learning. The following
functions are defined:

- :func:`se`
- :func:`ae`
- :func:`zero_one_loss`
- :func:`cross_entropy`
"""

import numpy as np

def se(y, p):
    """Squared error.

    Sqaured error can be defined as follows:

    .. math::

        \sum_i^n (y_i - p_i)^2

    where :math:`n` is the number of provided records.

    Parameters
    ----------
    y : :class:`ndarray`
        One dimensional array with ground truth values.

    p : :class:`ndarray`
        One dimensional array with predicted values.

    Returns
    -------
    float
        Squared error as desribed above.

    Notes
    -----
    Usually used for regression problems.
    """
    return np.sum((y - p)**2)

def ae(y, p):

    """Absolute error.

    Absolute error can be defined as follows:

    .. math::

        \sum_i^n abs(y_i - p_i)

    where :math:`n` is the number of provided records.

    Parameters
    ----------
    y : :class:`ndarray`
        One dimensional array with ground truth values.

    p : :class:`ndarray`
        One dimensional array with predicted values.

    Returns
    -------
    float
        Absolute error as desribed above.
    """
    return np.abs(y-p).sum()

def zero_one_loss(y, p):
    """Number of incorrectly classified records.

    Parameters
    ----------
    y : :class:`ndarray`
        One dimensional array with ground truth values.

    p : :class:`ndarray`
        One dimensional array with predicted values.

    Returns
    -------
    int
        Number of misclassified records.
    """
    return np.sum(y != p)

def cross_entropy(y, p):

    """
    Cross-entropy is a measure of the difference between two probability distributions for a given random variable or set of events.
    (`source <https://machinelearningmastery.com/cross-entropy-for-machine-learning/>`_)

    Parameters
    ----------
    y : :class:`ndarray`
        One dimensional array with ground truth values.

    p : :class:`ndarray`
        One dimensional array with predicted values.

    Returns
    -------
    float
        Cross entropy score.
    """

    return -np.sum(np.log(p) * y)


from numpy import random

"""
What is Data Distribution?
Data Distribution is a list of all possible values, and how often each value occurs.

Such lists are important when working with statistics and data science.

The random module offer methods that returns randomly generated data distributions.

Example
Generate a 1-D array containing 100 values, where each value has to be 3, 5, 7 or 9.

The probability for the value to be 3 is set to be 0.1

The probability for the value to be 5 is set to be 0.3

The probability for the value to be 7 is set to be 0.6

The probability for the value to be 9 is set to be 0
"""

x = random.choice([3, 5, 7, 9], size=(100), p=[0.1, 0.3, 0.6, 0.0])
print(x)


'''
Overfitting in Linear Regression
Author: Rowel Atienza
Project: https://github.com/roatienza/Deep-Learning-Experiments
'''
# On command line: python3 overfit_regression.py
# Prerequisite: tensorflow 1.0 (see tensorflow.org)
#             : matplotlib (http://matplotlib.org/)

from __future__ import print_function

import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np
import math

# Tunable parameters (Try changing the values and see what happens)
samples = 100
learning_rate = 0.0001

# xcoeff should be predicted by the model, yp
xcoeff = tf.transpose(tf.constant([[1., 1., 1.]]))

# a = tf.random_uniform([1, samples],-2.5,2.5)
a = tf.linspace(-2.5,2.5,samples)
# Correct the shape
a = tf.reshape(a,[samples,1])

# In theory, polynomial degree greater than 2 will overfit but the training is still able to guess the weights
# correctly for 3rd and 4th degree polynomials
# Inputs to form yp = a*a*a*a*xp[0] + a*a*a*xp[1] + a*a*xp[2] + a*xp[3] + xp[4], xp[] are the weights
# Overfit since our data generating model is quadratic
Ap = tf.concat([tf.concat([tf.concat([tf.concat([a*a*a*a*a*a,a*a*a*a],1),a*a],1),a],1),tf.ones_like(a)],1)

# Inputs to form yp = a*a*a*xp[0] + a*a*xp[1] + a*xp[2] + xp[3], xp[] are the weights
# Overfit since our data generating model is quadratic
# Ap = tf.concat(1,[tf.concat(1,[tf.concat(1,[a*a*a,a*a]),a]),tf.ones_like(a)])

# Inputs to form y = a*a*xcoeff[0] + a*xcoeff[1] + xcoeff[2]
A = tf.concat([tf.concat([a*a,a],1),tf.ones_like(a)],1)

# Initial guess on coefficients of predicted linear model
xp = tf.Variable(tf.random_uniform([5,1], -1.0, 1.0))

# xp = tf.Variable(tf.random_uniform([4,1], -1.0, 1.0))

# Predicted Model
yp = tf.matmul(Ap,xp)

# Observed outputs
y = tf.matmul(A,xcoeff)
# noise = tf.random_normal(y.get_shape(),stddev=0.8)
noise = tf.sin(math.pi*a)
y = tf.add(y,noise)

# The smaller the loss, the closer our prediction to the observed outputs
# The loss model used is square of error (yp - y)
# Miinimization of loss is done by Gradient Descent
loss = tf.reduce_mean(tf.square(yp - y))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
train_step = optimizer.minimize(loss)

init = tf.global_variables_initializer()
with tf.Session() as session:
    session.run(init)
    for i in range(10000):
        session.run(train_step)
        if ((i+1) % 100) == 0:
            print("%d : Loss=%0.1lf,  Predicted Parameters = %s" % (i+1, loss.eval(), session.run(xp)))
    # Let's plot
    a = np.array(a.eval())
    plt.plot(a, y.eval(), 'ro', a, yp.eval(), 'b')
    red = mpatches.Patch(color='red', label='Data')
    blue = mpatches.Patch(color='blue', label='Model')
    plt.legend(handles=[red,blue])
    plt.show()


# -*- coding: utf-8 -*-
"""
Created on Wed Apr  8 15:05:29 2020

@author: Wallace

Examples based on https://realpython.com/linear-regression-in-python/
"""
#
# Step 1: Import packages and classes
#
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt

#
# Step 2: Provide data
#

# Multiple - Multivariate linear regression y = b0 + b11*x1 + b12*x2  +... + b1n*xn
# Vector of independent variable
x = [[0, 1], [5, 1], [15, 2], [25, 5], [35, 11], [45, 15], [55, 34], [60, 35]]
y = [4, 5, 20, 14, 32, 22, 38, 43]
x, y = np.array(x), np.array(y)

x = sm.add_constant(x) #intercept is not added by default

#
# Step 3: Create a model and fit it
#

model = sm.OLS(y, x)

#
# Step 4: Get results
#

results = model.fit()
print(results.summary())

r_sq = results.rsquared
print('Coefficient of determination - R^2:', r_sq)

b0 = results.params[0]
b11 = results.params[1]
b12 = results.params[2]

print('Intercept - b0:', b0) # coefficient b0
print('Slopes - b11 and b12:', b11,b12) # coefficients b11 and b12

#
# Step 5: Predict response
#

y_pred = results.fittedvalues # saeme of results.predict(x)
print('Predicted response:', y_pred, sep='\n')

# Predicting for new data
x_new = sm.add_constant(np.arange(10).reshape((-1, 2)))
print('New data points:')
print(x_new)

y_new = results.predict(x_new)
print('Predicted response for new data:')
print(y_new)

#
# Step 6: Plot data
#

fig, ax = plt.subplots()
ax.plot(y, y_pred, linewidth=0, marker='s', label = "Predicted")
ax.plot(y, y, label=' Exact, y_pred = y')
ax.set_xlabel('Actual Response, y')
ax.set_ylabel('Predicted Response, y_pred')
ax.legend(facecolor='white')
plt.show()


import logging
import math
import sys
from enum import Enum
import numpy as np
from typing import Callable, List

from cg_utils import build_plot, setup_logger

class BetaMethod(Enum):
    """
    Enumerated class to
    """
    FR = "Fletcher-Reeves"
    FR_with_Restart = "Fletcher-Reeves with Restart"
    PR = "Polak-Ribiere"

class NonLinearConjugate:
    MAX_LINE_SEARCH_ATTEMPTS = 20

    def __init__(self, n: int):
        self.n = n
        logging.debug("Creating Non-Linear Conjugate Gradient with n=%d" % self.n)

        self._f = None
        # self._gradient = None

        self.calc_beta = None  # type: Callable

        self._x0 = None  # type: np.ndarray
        self._x_k = None  # type: np.ndarray
        self._p_k = None  # type: np.ndarray
        self._x_opt = None  # type: np.ndarray

        self.err = None  # type: List[float]
        self._err_opt = None  # type: float

        self._TOL = 5 * 10 ** -8

        self._alpha0 = 0
        self._c1 = 0.1
        self._c2 = 0.45

    def run(self, beta_method):
        """
        Perform non-linear conjugate gradient using the specified methods for calculating
        $\beta$.
        """
        logging.debug("Non-Linear Conjugate Gradient Beta Method -- %s" % beta_method.value)
        if beta_method == BetaMethod.FR:
            self.calc_beta = NonLinearConjugate.fr
        elif beta_method == BetaMethod.FR_with_Restart:
            self.calc_beta = NonLinearConjugate.fr_with_restart
        elif beta_method == BetaMethod.PR:
            self.calc_beta = NonLinearConjugate.pr
        else:
            raise ValueError("Unknown method for calculating beta")

        self.err = []
        self._x_k = self._x0
        self.update_err()
        grad_k = self._gradient(self._x_k)
        self._p_k = -grad_k
        k = 0

        # while k < self.n and np.linalg.norm(grad_x, 2) > self._TOL:
        while k < self.n ** 2 and np.linalg.norm(grad_k, 2) > self._TOL:
            alpha_k = self.line_search()
            x_k_1 = self._x_k + alpha_k * self._p_k

            grad_k_1 = self._gradient(x_k_1)
            beta_k = self.calc_beta(grad_k_1, grad_k)

            # Update the terms for the next loop iteration
            self._x_k = x_k_1
            self.update_err()

            self._p_k = -grad_k_1 + beta_k * self._p_k
            grad_k = grad_k_1
            k += 1
            if k % 10 == 0:
                logging.debug("Non-Linear CG %s Running -- k = %d -- Err = %f"
                              % (beta_method.value, k, self.err[-1]))

        logging.info("Completed Non-Linear Conjugate Gradient with k=%d" % (k-1))

    def line_search(self) -> float:
        """
        Calculates alpha for line search.

        :return: alpha for the function.
        """
        prev_alpha = self._alpha0
        alpha = self._alpha0 + 1
        phi_alpha = self._phi(self._alpha0)  # This is overwritten inside the loop

        phi_al_0 = self._phi(self._alpha0)
        d_phi_al_0 = self._d_phi(self._alpha0)
        for i in range(1, NonLinearConjugate.MAX_LINE_SEARCH_ATTEMPTS + 1):
            prev_phi_alpha = phi_alpha

            phi_alpha = self._phi(alpha)
            if phi_alpha > phi_al_0 + self._c1 * alpha * d_phi_al_0 \
                    or (i > 1 and phi_alpha >= prev_phi_alpha):
                return self.zoom(prev_alpha, alpha)

            d_phi_alpha = self._d_phi(alpha)
            if abs(d_phi_alpha) <= -self._c2 * d_phi_al_0:
                return alpha
            if d_phi_alpha >= 0:
                return self.zoom(alpha, prev_alpha)

            prev_alpha = alpha
            alpha *= 2
        assert False

    def zoom(self, alpha_lo: float, alpha_hi: float) -> float:
        """
        Perform Binary search within the range above to find the best value.

        :param alpha_lo:
        :param alpha_hi:
        :return: Optimal alpha value for line search.
        """
        phi_al_0 = self._phi(self._alpha0)
        d_phi_al_0 = self._d_phi(self._alpha0)
        while True:
            alpha_m = (alpha_lo + alpha_hi) / 2
            # Handle the case where floating point error is too high
            if alpha_hi == alpha_lo:
                return alpha_m

            phi_alpha = self._phi(alpha_m)

            if phi_alpha > phi_al_0 + self._c1 * alpha_m * d_phi_al_0 \
                    or phi_alpha >= self._phi(alpha_lo):
                alpha_hi = alpha_m
            else:
                d_phi_alpha = self._d_phi(alpha_m)
                if abs(d_phi_alpha) <= -self._c2 * d_phi_al_0:
                    return alpha_m
                if d_phi_alpha * (alpha_hi - alpha_lo) >= 0:
                    alpha_hi = alpha_lo
                alpha_lo = alpha_m

    def initialize_f(self):
        """
        Initialize the cost function f.
        """
        self._f = lambda x: sum([100 * ((x[_i] ** 2 - x[_i+1]) ** 2) + (x[_i] - 1) ** 2
                                 for _i in range(0, self.n - 1)])

    def _phi(self, alpha: float) -> float:
        """
        Cost function f(x + alpha * p).

        :param alpha: Scalar from inexact line search.
        :return: Calculated value for function phi.
        """
        return self._f(self._x_k + alpha * self._p_k)

    def _d_phi(self, alpha: float) -> float:
        """
        Calculate \phi' given the specified alpha.

        :param alpha: Exact line search alpha.
        :return:
        """
        x_al_p = self._x_k + alpha * self._p_k
        return sum([200 * (2 * self._p_k[i] * x_al_p[i] - self._p_k[i + 1])
                    * (x_al_p[i] ** 2 - x_al_p[i + 1])
                    + 2 * self._p_k[i] * (x_al_p[i] - 1)
                    for i in range(0, self.n - 1)])

    def _gradient(self, x: np.ndarray) -> np.ndarray:
        """
        Construct the gradient for the specified x.

        :param x: Current position
        :return: Gradient vector
        """
        grad_i = lambda _i: 400 * x[_i] * (x[_i] ** 2 - x[_i + 1]) + 2 * (x[_i] - 1)
        grad_prev = lambda _i: -200 * (x[_i - 1] ** 2 - x[_i])

        grad = [grad_i(0)]
        for _i in range(1, self.n - 1):
            grad.append(grad_i(_i) + grad_prev(_i))
        grad.append(grad_prev(self.n - 1))

        grad_np = np.array(grad)
        return grad_np

    def initialize_x0(self):
        """
        Initialize x0 to a random list as specified in the homework.
        """
        self._x0 = 2 * np.random.rand(self.n, 1)

    def initialize_x_opt(self):
        """
        Initialize the optimal x value.
        :return:
        """
        self._x_opt = np.ones([self.n, 1])

        assert self._f is not None
        self._err_opt = self._f(self._x_opt)

    def update_err(self):
        """
        Calculate the error from ideal and append it to the list of errors.
        """
        diff = abs(self._f(self._x_k) - self._err_opt)
        if diff == 0:
            return
        self.err.append(math.log10(diff))

    @staticmethod
    def fr(grad_k_1: np.ndarray, grad_k: np.ndarray) -> float:
        """
        Calculate beta using the Fletcher-Reeves method.

        :param grad_k_1: Gradient value at x_{k+1}
        :param grad_k: Gradient value at x_{k}
        :return: Beta^{FR}_{k+1}
        """
        return (grad_k_1.transpose() @ grad_k_1) / (grad_k.transpose() @ grad_k)

    @staticmethod
    def fr_with_restart(grad_k_1: np.ndarray, grad_k: np.ndarray) -> float:
        """
        Calculate beta using the Fletcher-Reeves method.

        :param grad_k_1: Gradient value at x_{k+1}
        :param grad_k: Gradient value at x_{k}
        :return: Beta^{FR}_{k+1}
        """
        if np.abs(grad_k_1.transpose() @ grad_k) / (grad_k_1.transpose() @ grad_k_1) >= 0.1:
            return 0
        return NonLinearConjugate.fr(grad_k_1, grad_k)

    @staticmethod
    def pr(grad_k_1: np.ndarray, grad_k: np.ndarray) -> float:
        """
        Calculate beta using the Polak-Ribiere method.

        :param grad_k_1: Gradient value at x_{k+1}
        :param grad_k: Gradient value at x_{k}
        :return: Beta^{PR}_{k+1}
        """
        beta = (grad_k_1.transpose() @ (grad_k_1 - grad_k)) / (grad_k.transpose() @ grad_k)
        return beta

def main(n: int):
    """
    Perform the conjugate gradient test for different methods.

    :param n: Dimension of the matrix.
    """
    ncg = NonLinearConjugate(n)

    ncg.initialize_f()
    ncg.initialize_x0()
    ncg.initialize_x_opt()

    for beta_method in [BetaMethod.PR, BetaMethod.FR_with_Restart, BetaMethod.FR]:
        ncg.run(beta_method)
        build_plot(list(range(0, len(ncg.err))), ncg.err, x_label="Iteration",
                   y_label="Log Error", title=beta_method.value)

if __name__ == "__main__":
    setup_logger(log_level=logging.DEBUG)

    _n = int(sys.argv[1])
    main(_n)


#!/usr/bin/python2

# Not very optimized

import sys
import math
import random
import matplotlib.pyplot as plot
import numpy

def dist(pointA, pointB):
    "pointA and pointB should be lists"
    total = 0
    for i in range(0, len(pointA)):
        total += (pointA[i] - pointB[i])**2
    return math.sqrt(total)

def findClosest(point, meanPoints):
    '''
    returns the index of the mean point in meanPoints that the
    point argument is closest to.
    '''
    index = 0
    shortestDist = dist(point, meanPoints[0])
    for i in range(1, len(meanPoints)):
        currentDist = dist(point, meanPoints[i])
        if currentDist < shortestDist:
            shortestDist = currentDist
            index = i
    return index

def findCentroid(points):
    "argument is a list of lists; returns a list (point)"
    totals = [0 for attr in points[0]] # holds total for each point attribute
    for point in points:
        for i in range(0, len(point)):
            totals[i] += point[i]

    centroid = []
    for i in range(0, len(totals)):
        centroid.append(totals[i] / len(points))
    return centroid
    ''' old implementation
    totalX = 0
    totalY = 0
    for point in points:
        totalX += point.x
        totalY += point.y
    return Point( (totalX / len(points)), (totalY / len(points)) )
    '''

filename = sys.argv[1]
k = int(sys.argv[2])
data = numpy.loadtxt(filename)
meanPoints = []

# find initial random means
maxAttrs = [0 for i in data[1]]
for point in data:
    for i in range(0, len(point)):
        if point[i] > maxAttrs[i]:
            maxAttrs[i] = point[i]
for i in range(0, k):
    randPoint = []
    for maxAttr in maxAttrs:
        randPoint.append(random.random() * maxAttr)
    meanPoints.append(randPoint)

maxIterations = 20
epsilonThreshold = 0.00001
delta = 1
iterations = 0

while iterations < maxIterations and delta > epsilonThreshold:
    delta = 0

    # assign points to means
    memberships = [ [] for i in range(0, k) ] # [ [], [] ] when k = 2
    membersToPrint = [] # for the report of which points belong where
    for point in data:
        memberships[findClosest(point, meanPoints)].append(point)
        membersToPrint.append(findClosest(point, meanPoints))

    # update mean points
    previousMeanPoints = meanPoints
    meanPoints = []
    for group in memberships:
        if len(group) != 0:
            meanPoints.append(findCentroid(group))
    # calculate delta
    for i in range(0, len(meanPoints)):
        delta += dist(meanPoints[i], previousMeanPoints[i])

    iterations += 1

# report
print "mean points :", meanPoints
for i in range(0, len(memberships)):
    print "number of points in cluster", i, ":", len(memberships[i])
print "number of iterations :", iterations
print "delta :", delta
print "membership :", membersToPrint

# plot 2d data
if data.shape[1] == 2:
    xs = []
    ys = []
    for point in data:
        xs.append(point[0])
        ys.append(point[1])
    meanXs = []
    meanYs = []
    for point in meanPoints:
        meanXs.append(point[0])
        meanYs.append(point[1])
    plot.plot(xs, ys, 'ro', meanXs, meanYs, 'bs')
    plot.axis([0, round(max(xs)) + 1, 0, round(max(ys)) + 1])
    plot.show()


from __future__ import division

import numpy as np

def linear_merge(x1, y1, x2, y2):
    """
    Merge data pairs (x1, y1) and (x2, y2) over the intersection
    of their ranges (x) using a linear interpolation of each
    dataset to interpolate between unaligned values.

    No extrapolation is performed.

    Input
    =====
    :x1, array-like: abscissa coordinates of dataset 1
    :y1, array-like: ordinate coordinates of dataset 1
    :x2, array-like: abscissa coordinates of dataset 2
    :y2, array-like: ordinate coordinates of dataset 2

    OUT
    ===
    Tuple of the merged x (`xm`) and interpolated y1 (`y1m`) and y2 (`y2m`):
    `(xm, y1m, y2m)`
    """
    # ensure all values are ndarrays
    x1 = np.asarray(x1)
    x2 = np.asarray(x2)
    y1 = np.asarray(y1)
    y2 = np.asarray(y2)
    # ##########
    # merge on x
    xmerge = np.concatenate((np.sort(x1), np.sort(x2)))
    xmerge.sort(kind='mergesort')
    # perform interpolation
    xlo = np.max((x1.min(), x2.min()))
    xhi = np.min((x1.max(), x2.max()))
    # keep only the merged x values within the intersection of
    # the data ranges
    mask = (xmerge >= xlo) & (xmerge <= xhi)
    xf = xmerge[mask]
    y1f = np.interp(xf, x1, y1)
    y2f = np.interp(xf, x2, y2)
    # return the interpolated, merged dataset
    return (xf, y1f, y2f)


import matplotlib.pyplot as plt
#%matplotlib inline
import numpy as np
import scipy
from scipy.interpolate import CubicSpline

def getBsplineCoeffs(y):
  """
  (np.array) -> (np.array)
  Description: Input a np.array of vectors, will smoothen it out and generate a piecewise set of splines coeff format dcba
  ie [[dcba0], [dcba1], [dcba2], ....]
  where y val = a +b*x + c*x**2 + d*x**3
  >>>

  """
  #assert (type(y) == list or  ), 'y needs to be a single dim list'
  # the values of x are insignifacant coz we are outputting coeffs, its just that deltax should be same always value ie equidistant .
  x = np.linspace(0, len(y), len(y))
  cs = CubicSpline(x,y,bc_type='natural')
  set_of_coeffs = cs.c  # list of [ [alld], [allc], ... ]
  piecewise_coeffs = []
  for d,c,b,a in zip(set_of_coeffs[0],set_of_coeffs[1],set_of_coeffs[2],set_of_coeffs[3]):
      piecewise_coeffs.append([d,c,b,a])
  return np.array(piecewise_coeffs)

if __name__ == '__main__':

    y = np.array([1,3,3,0,5,8,7,8,4,0])
    coef = getBsplineCoeffs(y)
    print(coef)
    """
    for i,d,c,b,a in zip(range(len(coef)),coef[0],coef[1],coef[2],coef[3]):
        #for singc in
        pass
        # printing in d,c,b,a fashion coz thats how python uses convention
        print(str(i) + str(', ') + str(d)  + str(', ') + str(c) + str(', ') + str(b) + str(',') + str(a) )
    #print()

    #
    x0 = np.linspace(0,1,50)
    y0 = np.array(12*x0**0 + x0 + x0**3)

    x1 =  np.linspace(1,2,50)
    y1 = np.array(14*x1**0 + 4*(x1 -1) + 3*(x1-1)**2 + (x1-1)**3)

    t  = np.linspace(2,3,50)
    x = t-2
    y2 = 22 + 13*x + 6*x**2 + (-2)*x**3
    plt.plot(x0, y0, label="0 to 1")
    plt.plot(x1, y1, label="1 to 2")
    plt.plot(t, y2, label="2 to 3")

    t=np.linspace(3,4,50)
    x = t-3
    y = 39 + 19*x
    plt.plot(t, y, label="3 to 4")

    t=np.linspace(4,5,50)
    x = t-4
    y = 58 + 20*x
    plt.plot(t, y, label="4 to 5")

    plt.legend()
    plt.show()

    12.0, 1.0000000000000002, -2.220446049250313e-16, 1.0,
    14.0, 3.9999999999999996, 3.0, 1.0,
    22.0, 13.000000000000002, 5.999999999999998, -2.0,
    39.0, 19.0, 0.0, 0.0,
    58.0, 19.0, 0.0, 0.0,
    """


import numpy as np
import matplotlib.pyplot as plt

import torch

def eq_covariance(inputs1,
                  inputs2,
                  scale,
                  cov_coeff,
                  noise_coeff):

    diff = inputs1[:, :, None] - inputs2[:, None, :]

    quad = (diff / (2 * scale ** 2)) ** 2

    exp_quad = cov_coeff ** 2 * np.exp(- quad)

    return exp_quad

def sample_datasets_from_gps(low,
                             high,
                             batch_size,
                             num_train,
                             num_test,
                             scale,
                             cov_coeff,
                             noise_coeff,
                             as_tensor):

    x_train_test = np.random.uniform(low=low,
                                     high=high,
                                     size=(batch_size, num_train + num_test,))

    cov_train_test = eq_covariance(x_train_test,
                                   x_train_test,
                                   scale,
                                   cov_coeff,
                                   noise_coeff)

    cov_train_test = cov_train_test + noise_coeff ** 2 * np.eye(cov_train_test.shape[-1])[None, :, :]

    zeros = np.zeros((cov_train_test.shape[1],))
    noise = np.random.multivariate_normal(zeros, np.diag(np.ones_like(zeros)), size=(batch_size,))
    chol_train_test = np.linalg.cholesky(cov_train_test)

    y_train_test = np.einsum('bij, bj -> bi', chol_train_test, noise)

    x_train = x_train_test[:, :num_train, None]
    x_test = x_train_test[:, num_train:, None]

    y_train = y_train_test[:, :num_train, None]
    y_test = y_train_test[:, num_train:, None]

    x_train, y_train, x_test, y_test = [torch.tensor(array).float() if as_tensor else array \
                                        for array in (x_train, y_train, x_test, y_test)]

    return (x_train, y_train), (x_test, y_test)

def gp_post_pred(train_inputs,
                 train_outputs,
                 pred_inputs,
                 scale,
                 cov_coeff,
                 noise_coeff):

    K = eq_covariance(train_inputs,
                      train_inputs,
                      scale,
                      cov_coeff,
                      noise_coeff)[0]

    K = K + noise_coeff ** 2 * np.eye(K.shape[-1])

    k_star = eq_covariance(pred_inputs,
                           train_inputs,
                           scale,
                           cov_coeff,
                           noise_coeff)[0]

    means = np.dot(k_star, np.linalg.solve(K, train_outputs[0]))

    K_inv_k_star = np.linalg.solve(K, k_star.T)
    stds = (cov_coeff - np.diag(np.einsum('ij, jk -> ik', k_star, K_inv_k_star)) + noise_coeff ** 2) ** 0.5

    return means, stds

def cnp_plot_sample_and_predictions(conditional_neural_process,
                                    input_range,
                                    num_train,
                                    num_test,
                                    scale,
                                    cov_coeff,
                                    noise_coeff,
                                    step,
                                    plot_test_data=False):

    train_data, test_data = sample_datasets_from_gps(batch_size=1,
                                                     num_train=num_train,
                                                     num_test=num_test,
                                                     scale=scale,
                                                     cov_coeff=cov_coeff,
                                                     noise_coeff=noise_coeff,
                                                     as_tensor=True)

    context_inputs, context_outputs = train_data
    target_inputs = torch.linspace(input_range[0],
                                   input_range[1],
                                   100)[None, :]

    pred_mean, pred_log_stdev = conditional_neural_process.forward(context_inputs,
                                                                   context_outputs,
                                                                   target_inputs)

    gp_means, gp_stds = gp_post_pred(train_data[0],
                                     train_data[1],
                                     target_inputs,
                                     scale,
                                     cov_coeff,
                                     noise_coeff)

    target_inputs = target_inputs.squeeze().detach().numpy()
    pred_mean = pred_mean.squeeze().detach().numpy()
    pred_stdev = torch.exp(pred_log_stdev).squeeze().detach().numpy()

    plt.plot(target_inputs, gp_means, color='blue', zorder=1)
    plt.fill_between(target_inputs,
                     gp_means - gp_stds,
                     gp_means + gp_stds,
                     color='blue',
                     alpha=0.3,
                     zorder=1)

    plt.scatter(train_data[0],
                train_data[1],
                s=100,
                marker='x',
                color='black',
                label='Context (observed)')

    plt.plot(target_inputs,
             pred_mean,
             color='black')

    plt.fill_between(target_inputs,
                     pred_mean - pred_stdev,
                     pred_mean + pred_stdev,
                     color='gray',
                     alpha=0.3)

    if plot_test_data:
        plt.scatter(test_data[0],
                    test_data[1],
                    marker='x',
                    color='red',
                    label='Target (unobserved)',
                    alpha=0.2,
                    zorder=1)

    plt.xlim([-6, 6])
    plt.ylim([-3, 3])
    plt.legend(loc='lower right')
    plt.title(f'Step {step}', fontsize=16)

    plt.show()

def np_plot_sample_and_predictions(neural_process,
                                   low,
                                   high,
                                   input_range,
                                   num_train,
                                   num_test,
                                   scale,
                                   cov_coeff,
                                   noise_coeff,
                                   step,
                                   plot_test_data=False):

    train_data, test_data = sample_datasets_from_gps(low=low,
                                                     high=high,
                                                     batch_size=1,
                                                     num_train=num_train,
                                                     num_test=num_test,
                                                     scale=scale,
                                                     cov_coeff=cov_coeff,
                                                     noise_coeff=noise_coeff,
                                                     as_tensor=True)

    context_inputs, context_outputs = train_data
    target_inputs = torch.linspace(input_range[0],
                                   input_range[1],
                                   100)[None, :, None]

    pred_mean, pred_stdev = neural_process.forward(context_inputs,
                                                   context_outputs,
                                                   target_inputs)

    gp_means, gp_stds = gp_post_pred(train_data[0][..., 0],
                                     train_data[1][..., 0],
                                     target_inputs[..., 0],
                                     scale,
                                     cov_coeff,
                                     noise_coeff)

    target_inputs = target_inputs.squeeze().detach().numpy()
    pred_mean = pred_mean.squeeze().detach().numpy()
    pred_stdev = pred_stdev.squeeze().detach().numpy()

    plt.plot(target_inputs, gp_means, color='blue', zorder=1)
    plt.fill_between(target_inputs,
                     gp_means - gp_stds,
                     gp_means + gp_stds,
                     color='blue',
                     alpha=0.3,
                     zorder=1)

    plt.scatter(train_data[0],
                train_data[1],
                s=100,
                marker='x',
                color='black',
                label='Context (observed)')

    plt.plot(target_inputs,
             pred_mean,
             color='black')

    plt.fill_between(target_inputs,
                     pred_mean - pred_stdev,
                     pred_mean + pred_stdev,
                     color='gray',
                     alpha=0.3)

    if plot_test_data:
        plt.scatter(test_data[0],
                    test_data[1],
                    marker='x',
                    color='red',
                    label='Target (unobserved)',
                    alpha=0.2,
                    zorder=1)

    plt.xlim(input_range)
    plt.ylim([-3, 3])
    plt.legend(loc='lower right')
    plt.title(f'Step {step}', fontsize=16)

    plt.show()


import numpy as np

from gradient import gradient
from sigmoid import sigmoid

def log_cost(theta, x, y, hyper_p):
    """
        Logistic regression cost function with regularization.

        Parameters
        ----------
        theta : array_like
            Shape (1, n+1). Parameter values for function.

        x : array_like
            Shape (m, n+1). Features in model.

        y : array_like
            Shape (m, 1). Labels for each example.

        hyper_p : float
            Value of the hyperparameter for regularization.

        Returns
        -------
        cost : float
            Value of cost function at given parameters.

        grad : array_like
            Shape (1, n+1). The gradient for each parameter.
    """

    theta = np.reshape(theta, (1, x.shape[1]))
    size = y.shape[0]

    h = sigmoid(x @ theta.T)
    reg = (hyper_p / 2 * size) * np.sum(theta[:, 1:theta.shape[1]] ** 2)

    cost = -((1 / size) * np.sum(y * np.log(h) + (1 - y) * np.log(1 - h))) + reg
    grad = gradient(theta, x, y, hyper_p)
    return cost, grad


from matplotlib import pyplot as plt
import numpy as np

def rot_mat(ang_rad):
    c = np.cos(ang_rad)
    s = np.sin(ang_rad)
    return np.array([[c, -s],[s, c]])

def servo_traj(view_deg, gain, hand_start_x, hand_start_y):
    target = np.array([0,0])
    hand_start = np.array([hand_start_x, hand_start_y])
    robot_view = rot_mat(view_deg/180.0 * np.pi)

    t_0 = 0.0
    t_end = 10.0
    hz = 15.0
    T = 1.0/hz

    t = t_0
    hand_pos = hand_start
    hand_traj = []
    hand_traj.append(hand_pos)
    while t < t_end:
        target_view = np.matmul(robot_view, target)
        hand_view = np.matmul(robot_view, hand_pos)
        view_error = target_view - hand_view
        hand_pos = hand_pos + (gain * view_error)
        hand_traj.append(hand_pos)
        t = t + T

    return hand_traj

def plot_traj(hand_traj):
    fig = plt.figure()
    x = [p[0] for p in hand_traj]
    y = [p[1] for p in hand_traj]
    plt.plot(x,y, 'o-', color='blue')
    plt.show()

def plot_servo(view_deg=0.0, gain=0.2, hand_start_x=10.0, hand_start_y=10.0):
    traj = servo_traj(view_deg, gain, hand_start_x, hand_start_y)
    plot_traj(traj)

if __name__ == '__main__':
    plot_servo()


#
# Exerciese from book Data Science - Sinan Ozdemir
#
# @since : Fri Apr  9 14:41:38 CEST 2021
#

### Calculate standard deviance
#
# Distanza di un punto dei dati rispetto alla media
#
import numpy

temps = [32, 32, 31, 28, 29, 31, 39, 32, 32, 35, 26, 29]

# Calculate mean of values
mean = numpy.mean(temps)

squared_differences = []
num_items = len(temps)
products = 1

for temperature in temps:

    # Geometric mean
    products *= temperature
    geometric_mean = products ** (1./num_items)

    # Distance of single point from mean
    difference = temperature - mean

    # Square of difference
    squared_difference = difference ** 2

    squared_differences.append(squared_difference)

# Calculate VARIANCE
average_squared_difference = numpy.mean(squared_differences)

# Calculate standard deviation
standard_deviation = numpy.sqrt(average_squared_difference)

print ('mean:               ', mean)
print ('variance:           ', average_squared_difference)
print ('standard_deviation: ', standard_deviation)
print ('geometric mean:     ', geometric_mean)

# mean:                31.333333333333332
# variance:            10.388888888888888
# standard_deviation:  3.2231799343022858
# geometric mean:      31.173240057688545


import numpy as np

class TanH:
    def __call__(self, x: np.ndarray) -> np.ndarray:
        return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

    def gradient(self, x: np.ndarray) -> np.ndarray:
        return 1 - np.power(self.__call__(x), 2)


import numpy as np

def generate_perlin_noise_2d(shape, res):
    """
    Algoritmo de Perlin Noise.

    :param shape: tupla de dimensiones de la matriz a crear.
    :type shape: tuple(int, int)
    :param res: tupla de valores más pequeños que dividen a las dimensiones.
    :type shape: tuple(int, int)

    :rtype: np.array
    :return: np.sqrt(2)*((1-t[:,:,1])*n0 + t[:,:,1]*n1)
    """
    def f(t):
        return 6*t**5 - 15*t**4 + 10*t**3

    delta = (res[0] / shape[0], res[1] / shape[1])
    d = (shape[0] // res[0], shape[1] // res[1])
    grid = np.mgrid[0:res[0]:delta[0],0:res[1]:delta[1]].transpose(1, 2, 0) % 1

    # Gradientes
    angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
    gradients = np.dstack((np.cos(angles), np.sin(angles)))
    g00 = gradients[0:-1,0:-1].repeat(d[0], 0).repeat(d[1], 1)
    g10 = gradients[1:,0:-1].repeat(d[0], 0).repeat(d[1], 1)
    g01 = gradients[0:-1,1:].repeat(d[0], 0).repeat(d[1], 1)
    g11 = gradients[1:,1:].repeat(d[0], 0).repeat(d[1], 1)

    # Rampas
    n00 = np.sum(grid * g00, 2)
    n10 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1])) * g10, 2)
    n01 = np.sum(np.dstack((grid[:,:,0], grid[:,:,1]-1)) * g01, 2)
    n11 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]-1)) * g11, 2)

    # Interpolación
    t = f(grid)
    n0 = n00*(1-t[:,:,0]) + t[:,:,0]*n10
    n1 = n01*(1-t[:,:,0]) + t[:,:,0]*n11
    return np.sqrt(2)*((1-t[:,:,1])*n0 + t[:,:,1]*n1)

def generate_elevation_matrix(dimension_x, dimension_y):
    """
    Genera una matriz de elevación, con valores entre -10 y 10
    con el uso del algoritmo Perlin Noise.

    :param dimension_x: cantidad de filas de la matriz
    :type dimension_x: int
    :param dimension_y: cantidad de columnas de la matriz
    :type dimension_y: int

    :rtype: list
    :return: elevation
    """
    shape_x = 2 if dimension_x % 2 == 0 else 0
    shape_y = 2 if dimension_y % 2 == 0 else 0
    b = 3
    while 1:
        if shape_x and shape_y:
            break
        if not shape_x and not dimension_x % b:
            shape_x = b
        if not shape_y and not dimension_y % b:
            shape_y = b
        b+=2
    a = generate_perlin_noise_2d((dimension_x,dimension_y),(shape_x,shape_y))
    elevation=[]
    for i in range(dimension_x):
        elevation.append([])
        for j in range(dimension_y):
            elevation[i].append(round(a[i][j]*10))

    return elevation

def mean(array):
    """
    Función para calcular la media, dando relevancia a los
    valores más pequeños.

    :param array: lista de elementos a hallarle la media
    :type array: list

    :rtype: float
    :return: mean
    """
    total = len(array)
    sum_elem = 0
    for elem in array:
        sum_elem = sum_elem + 1/elem if elem != 0 else sum_elem + 1
    mean = total/sum_elem if len(array) or sum_elem!=0 else 0
    return mean

def manhattan(pos1, pos2):
    """
    Función para calcular la distancia manhattan entre
    dos coordenas de una matriz

    :param pos1: primera coordenada
    :type pos1: Tuple[int, int]
    :param pos2: segunda coordenada
    :type pos2: Tuple[int, int]

    :rtype: int
    :return: distance
    """
    distance = int(abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1]))
    return distance


"""
    Compute the KL divergence between two zero-mean multivariate Gaussian
    KL(S0||S1), where S0 and S1 are the covariances

    Or the cosine similarity between these two covariances
"""
import argparse
import numpy as np
from numpy.linalg import norm, inv, slogdet

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('s0', type=str, help='path to a similarity matrix')
    parser.add_argument('-s1', type=str, default='',
                        help='path to another similarity matrix. If not given, '
                        'will be identity matrix')
    parser.add_argument('--metric', choices=['kl', 'corr'], default='kl')
    return parser.parse_args()

if __name__ == '__main__':
    args = parse_args()
    S0 = np.load(args.s0)
    N = S0.shape[0]
    if args.s1:
        S1 = np.load(args.s1)
        assert S0.shape == S1.shape
    else:
        S1 = np.eye(N)

    if args.metric == 'corr':
        corr = np.sum(S0 * S1) / (norm(S0) * norm(S1))
        print(corr, flush=True)

    elif args.metric == 'kl':
        sign0, logdet0 = slogdet(S0)
        assert sign0 == 1
        sign1, logdet1 = slogdet(S1)
        assert sign1 == 1

        KL = 0.5 * (np.trace(inv(S1).dot(S0)) - N + logdet1 - logdet0)
        print(KL, flush=True)


import numpy as np
import cvxpy as cvx
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler

def linear_kernel(x1, x2):

    return np.dot(x1, x2)

def polynomial_kernel(x, y, p=3):

    return (1 + np.dot(x, y)) ** p

def gaussian_kernel(x, y, sigma=5.0):

    return np.exp(-np.linalg.norm(x-y)**2 / (2 * (sigma ** 2)))

class SVM(object):

    def __init__(self, kernel='linear', C=1.0):
        self.kernel = kernel
        self.C = C

        if self.C is not None:
            self.C = float(self.C)

    def _kernel_transform(self, X_train, kernel):
        if kernel == 'linear':
            kfunc = linear_kernel
        elif kernel == 'polynomial':
            kfunc = polynomial_kernel
        elif kernel == 'gaussian':
            kfunc = gaussian_kernel

        self.kfunc = kfunc

        n_samples, n_features = X_train.shape
        self.n_samples = n_samples
        self.n_features = n_features

        K = np.zeros((n_samples, n_samples))

        for i in range(n_samples):
            for j in range(n_samples):
                K[i,j] = kfunc(X_train[i], X_train[j])

        return K

    def __solve(self, K_matrix, y_train, C):

        n = y_train.shape[0]
        y_= np.diag(y_train)  # 180 by 180(1 & -1)
        alpha = cvx.Variable(n)
        # constraints
        constraints = []
        for i in range(n):
            constraints += [alpha[i] >= 0, alpha[i] <= C]
        constraints += [y_train * alpha == 0]
        self.model_obj = cvx.Maximize(np.ones(n) * alpha - .5 * cvx.quad_form(alpha, y_.dot(K_matrix).dot(y_)))#convex
        #quad form
        self.model = cvx.Problem(objective=self.model_obj, constraints=constraints)
        self.model.solve()

        #support vector
        self.a = np.array(alpha.value).flatten()
        self.sv = self.a > 1e-5
        self.ind = np.arange(len(self.a))[self.sv]
        self.a = self.a[self.sv]
        self.sv_y = y_train[self.sv]
        self.sv_x= self.X_train[self.sv]

        # intercept
        self.b = 0
        for n in range(len(self.a)):
            self.b+=self.sv_y[n]
            self.b-=np.sum(self.a*self.sv_y*K_matrix[self.ind[n],self.sv])
        self.b/=len(self.a)

        #weight vector
        if self.kernel == 'linear':
            self.w = np.zeros(self.n_features)
            for n in range(len(self.a)):
                    self.w += self.a[n] * self.sv_y[n] * self.sv_x[n]
        else:
            self.w = None

    def predict(self,X):
        if self.w is not None:

            return np.dot(X,self.w) + self.b

        else:
            y_predict = np.zeros(len(X))#

            for i in range(len(X)):
                s=0
                for a, sv_y, sv_x in zip(self.a, self.sv_y, self.sv_x):
                    s += a * sv_y * self.kfunc(X[i], sv_x)
                y_predict[i] = s

            return y_predict + self.b
    def signpredict(self, X_test):

        return np.sign(self.predict(X_test))

    def fit(self, X_train, y_train):
        self.X_train = X_train
        K = self._kernel_transform(X_train, self.kernel)
        self.K = K
        self.__solve(K, y_train, self.C)

02 Kernel-based Learning/Tutorial 04 - Support Vector Machine/svm_model.py


import numpy as np
from common.iterator import data_iterator
from common.plot import plot

'''
    使用d维平面 d为特征维数
    L(w,b,x,y) = 0.5 * ((xw + b) - y)^2 + 0.5 * lambda(w^2)
'''
class LinearRegression:
    def __init__(self, batch_size = 64, learning_rate = 0.0001, max_epoch = 100, weight_reg = 1.5):
        self.batch_size = batch_size
        self.learning_rate = learning_rate
        self.max_epoch = max_epoch
        self.weight_reg = weight_reg

    def __loss(self,X,y):
        pred = (X @ self.w + self.b) - y
        loss = 0.5 * np.sum(pred**2) / X.shape[0] + 0.5 * self.weight_reg * np.sum(self.w**2)
        dw = X.T @ (pred - y) / X.shape[0] + self.weight_reg * self.w
        db = np.sum(pred) / X.shape[0]
        return loss, dw, db

    def fit(self,X,y, need_plot = False, verbose = False):
        self.w = np.random.random(X.shape[1]).T
        self.b = np.random.random(1)
        loss = []
        acc = []
        for i in range(self.max_epoch):
            batch_loss = []
            batch_acc = []
            for X_batch, y_batch in data_iterator(X, y, self.batch_size, shuffle=True):
                current_loss, dw, db = self.__loss(X_batch, y_batch)
                batch_loss.append(current_loss)
                batch_acc.append(np.mean(np.abs(self.predict(X_batch) - y_batch)))
                self.w -= self.learning_rate * dw
                self.b -= self.learning_rate * db
            if verbose:
                print("Train epoch @", i, "mean_batch_diff: %.4f" % np.mean(batch_acc), "mean_batch_loss: %.4f" % np.mean(batch_loss))
            if need_plot:
                loss.append(np.mean(batch_loss))
                acc.append(np.mean(batch_acc))
        if need_plot:
            plot(range(self.max_epoch), acc, label="Diff")
            plot(range(self.max_epoch), loss, label="Loss")

    def predict(self,X):
        return (X @ self.w) + self.b

if __name__ == '__main__':
    from sklearn.model_selection import train_test_split
    from common.load_data import california_housing, load_data
    X,y = load_data(california_housing, scale = True)
    X_train, X_test, y_train, y_test = train_test_split(X,y,test_size = 0.2)
    reg = LinearRegression()
    reg.fit(X_train, y_train, need_plot = True)
    print(np.mean(np.abs(reg.predict(X_test)-y_test)))

    from sklearn import linear_model
    reg = linear_model.LinearRegression()
    reg.fit(X_train, y_train)
    print(np.mean(np.abs(reg.predict(X_test)-y_test)))


import numpy as np
import matplotlib.pyplot as plt

def phasor(H):
    """
    Caculate phasor values from given histogram
    g = 1 / N * sum(H * cos(f))
    s = 1 / N * sum(H * sin(f))
    ===========================================================================
    Input       Meaning
    ----------  ---------------------------------------------------------------
    H           List or np.array with histogram values
    ===========================================================================
    Output      Meaning
    ----------  ---------------------------------------------------------------
    z           complex number describing the phasor
    ===========================================================================
    """

    Np = len(H)
    F = 2 * np.pi * np.linspace(0, Np-1, Np) / (Np - 1)

    g = np.sum(H * np.cos(F)) / np.sum(H)
    s = np.sum(H * np.sin(F)) / np.sum(H)

    z = complex(g, s)

    return(z)

def plotphasor(z):

    plt.figure()

    A = 1
    phi = np.arange(0, 2*np.pi, 2*np.pi/360)
    x1 = A * np.cos(phi)
    y1 = A * np.sin(phi)

    A = 0.5
    phi = np.arange(0, 2*np.pi, 2*np.pi/360)
    x2 = A * np.cos(phi) + 0.5
    y2 = A * np.sin(phi)

    plt.plot(x1, y1)
    plt.plot(x2, y2)
    plt.scatter(z.real, z.imag)
    plt.plot([-1, 1], [0, 0], color='k')
    plt.plot([0, 0], [-1, 1], color='k')

    plt.xlim([-1, 1])
    plt.ylim([-1, 1])
    plt.gca().set_aspect('equal', adjustable='box')


from numpy.linalg import cholesky, inv, eigh
from numpy.random import randn
import numpy as np
from scipy import special
from scipy import stats
from copy import deepcopy

__all__ = ['generateNormalCorr',
           'induceRankCorr',
           'generateBinVars']

def generateNormalCorr(N, k, C, method='numpy'):
    """Induces correlation specified by covariance matrix Cstar

    From SciPy cookbook:
    http://wiki.scipy.org/Cookbook/CorrelatedRandomSamples

    Parameters
    ----------
    N : int
        Number of samples.
    k : int
        Number of variables.
    C : ndarray [k x k]
        Positive, symetric covariance matrix.

    Returns
    -------
    R : ndarray [N x k]
        Array of random correlated samples."""

    if method == 'cholesky':
        U = cholesky(C)
        R = np.dot(randn(N, k), U)
    elif method == 'eigen':
        evals, evecs = eigh(C)
        U = np.dot(evecs, np.diag(np.sqrt(evals)))
        R = np.dot(randn(N, k), U)
    else:
        R = np.random.multivariate_normal(np.zeros(k), C, N)

    return R

def induceRankCorr(R, Cstar):
    """Induces rank correlation Cstar onto a sample R [N x k].
    Note that it is easy to specify correlations that are not possible to generate.
    Results generated with a given Cstar should be checked.

    Iman, R. L., and W. J. Conover. 1982. A Distribution-free Approach to Inducing Rank
    Correlation Among Input Variables. Communications in Statistics: Simulation and
    Computations 11:311-334.

    Parameters
    ----------
    R : ndarray [N x k]
        Matrix of random samples (with no pre-existing correlation)
    Cstar : ndarray [k x k]
        Desired positive, symetric correlation matrix with ones along the diagonal.

    Returns
    -------
    corrR : ndarray [N x k]
        A correlated matrix of samples."""

    """Define inverse complimentary error function (erfcinv in matlab)
    x is on interval [0,2]
    its also defined in scipy.special"""
    #erfcinv = lambda x: -stats.norm.ppf(x/2)/sqrt(2)

    C = Cstar
    N, k = R.shape
    """Calculate the sample correlation matrix T"""
    T = np.corrcoef(R.T)

    """Calculate lower triangular cholesky
        decomposition of Cstar (i.e. P*P' = C)"""
    P = cholesky(C).T

    """Calculate lower triangular cholesky decomposition of T, i.e. Q*Q' = T"""
    Q = cholesky(T).T

    """S*T*S' = C"""
    S = P.dot(inv(Q))

    """Replace values in samples with corresponding
    rank-indices and convert to van der Waerden scores"""

    RvdW = -np.sqrt(2) * special.erfcinv(2*((_columnRanks(R)+1)/(N+1)))

    """Matrix RBstar has a correlation matrix exactly equal to C"""
    RBstar = RvdW.dot(S.T)

    """Match up the rank pairing in R according to RBstar"""
    ranks = _columnRanks(RBstar)
    sortedR = np.sort(R, axis=0)
    corrR = np.zeros(R.shape)
    for j in np.arange(k):
        corrR[:, j] = sortedR[ranks[:, j], j]

    return corrR

def generateBinVars(p, N):
    """Generate random binary variables with specified correlation

    "A simple method for generating correlated binary variates."
    Park C, Park T, Shin D. 1996. Am. Stat. 50:306:310.

    Parameters
    ----------
    p : ndarray [k x k]
        Positive, symetric correlation matrix with p_ii on the diagonal and p_ij off the diagonal
    N : int
        Number of samples to generate.

    Returns
    -------
    Z : ndarray [N x k]
        Correlated random binary samples.

    Example
    -------

    p = array([[0.9,0.1,0.5],
               [0.1,0.8,0.5],
               [0.5,0.5,0.7]])

    Z = generateBinVars(p,1e3)
    """
    def alphaFunc(p):
        q = 1-p
        d = np.diag(q)/np.diag(p)
        imat = np.tile(d.reshape((1, p.shape[0])), (p.shape[0], 1))
        jmat = np.tile(d.reshape((p.shape[0], 1)), (1, p.shape[0]))

        ijmat = np.log(1 + p*np.sqrt(imat*jmat))
        dind = np.diag_indices(p.shape[0])
        ijmat[dind] = -np.log(diag(p))
        return ijmat

    a = alphaFunc(p)

    ana = deepcopy(a)
    tind = np.triu_indices(a.shape[0])
    ana[np.tril_indices(a.shape[0])] = nan
    ana[np.diag_indices(a.shape[0])] = a[np.diag_indices(a.shape[0])]

    betaL = []
    rsL = []
    slL = []
    while np.any(ana[tind]>0):
        ana[ana==0] = nan
        #print ana

        rs = list(np.unravel_index(np.nanargmin(ana), a.shape))
        mn = np.nanmin(ana)
        if ana[rs[0], rs[0]] == 0 or ana[rs[1], rs[1]] == 0:
            break
        betaL.append(mn)
        rsL.append(rs)
        #print rs

        rs = set(rs)
        for i in range(a.shape[0]):
            if np.all(ana[list(rs), i]>0):
                rs.add(i)
        slL.append(rs)
        #print rs

        for i in rs:
            for j in rs:
                ana[i, j] = ana[i, j]-mn

    poissonVars = []
    for b in betaL:
        poissonVars.append(stats.poisson.rvs(b, size=(N,)))
    Y = np.zeros((N, a.shape[0]))
    for i in range(Y.shape[1]):
        for sl, pv in zip(slL, poissonVars):
            if i in sl:
                Y[:, i] = Y[:, i]+pv
    Z = Y<1

    #print around(np.corrcoef(Z,rowvar=0),decimals=2)
    #print around(Z.sum(axis=0)/N,decimals=2)
    return Z

def _columnRanks(u):
    """For matrix u, turn each element into its rank along the column
    Returns a matrix of same shape"""

    out = np.zeros(u.shape)
    for j in np.arange(u.shape[1]):
        out[:, j] = _argrank(u[:, j])
    return out.astype(int)

def _argrank(vec):
    """Return the rank (0 based) of the elements in vec"""
    sorti = np.argsort(vec)
    ranks = np.empty(len(vec), int)
    try:
        ranks[sorti] = np.arange(len(vec))
    except IndexError:
        ranks[sorti.values] = np.arange(len(vec))
    return ranks


import io_csv
import math
import numpy

a=numpy.linspace(0,2*numpy.pi*(7.0/8.0),8)
r=numpy.linspace(-0.2,0.2,3)

vec_0=numpy.matrix([[0],[1],[0]])
vec_1=numpy.matrix([[0],[0],[1]])

coords=[]

for ita in a:
	Rz=numpy.matrix([[math.cos(ita),math.sin(ita),0],[-math.sin(ita),math.cos(ita),0],[0,0,1]])
	Rx=numpy.matrix([[1,0,0],[0,math.cos(ita/2),math.sin(ita/2)],[0,-math.sin(ita/2),math.cos(ita/2)]])
	vec=Rz*vec_0
	for itr in r:
		vec_r=vec+itr*Rz*Rx*vec_1
		coords.append([ita,itr,vec_r[0],vec_r[1],vec_r[2]])

io_csv.saveCSV("example_mobius.csv",coords,delimiter=',')


import numpy as np
from matplotlib import pyplot as plt

K = 50
def zeno_function(n):
    return (1+ (1-2*np.sin(np.pi/2/n)**2)**(n))/2

x = np.linspace(1,K,K)
y = zeno_function(x)
plt.suptitle('Plot of the Quantum Zeno Effect')
plt.ylabel('probability of measuring 0')
plt.xlabel('number of rotations')

plt.plot(x,y)
plt.show()


from matplotlib import pyplot as plt
from math import cos, radians
import numpy as np

def force(u, m, g, theta):
    if isinstance(theta, int):
        return float((u * m * g) / cos(radians(theta)))
    else:
        res = []
        for i in theta:
            res.append(float((u * m * g) / cos(radians(i))))
        return res

x = np.linspace(20, 80, 1000)
y = force(0.5, 50, 9.8, x)

plt.style.use('seaborn-white')
plt.grid()
plt.title('Mass of a 50kg Athlete \nRunning on a track with a frictional coefficient of 0.5')
plt.xlabel('degree between the ground and power supply leg (Degree)', fontsize=14)
plt.ylabel('forced needed by power leg\nto surmount friction (Newton)', fontsize=14)

start_x = 30
start_y = force(0.5, 50, 9.8, 30)
end_x = 60
end_y = force(0.5, 50, 9.8, 60)
plt.fill_between([start_x, end_x], [start_y, start_y], [end_y, end_y], color='r', alpha=0.5, label='Start angle and finish angle between leg and ground\nusually varies between 30 to 60 degree')
plt.annotate(s=f'({start_x}, {round(start_y)})', xy=(start_x, start_y - 50), fontsize=12)
plt.annotate(s=f'({end_x}, {round(end_y)})', xy=(end_x, end_y), fontsize=12)
plt.plot(x, y, label=f'F\u2090 = \u00b5 m g / cos(\u03b8)')
plt.legend(fontsize=12)
plt.tight_layout()
plt.show()


import math
from random import seed, random

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

np.set_printoptions(precision=2)
np.set_printoptions(suppress=True)
seed(1)

def exp_rv(rate):
    return (-1.0 / rate) * math.log(random())

def generate(rate, count):
    return np.vectorize(lambda _: exp_rv(rate))(np.arange(count))

samples = generate(0.1, 100)
print(samples)
bincount = len(samples)
cdf = stats.cumfreq(samples, numbins=bincount)

x = cdf.lowerlimit + np.linspace(0, cdf.binsize * cdf.cumcount.size, cdf.cumcount.size)
fig, ax = plt.subplots()
ax.plot(x, np.divide(cdf.cumcount, bincount))
ax.set(xlabel='x', ylabel='P(X ≤ x)', title='Cumulative Distribution')
ax.set_xlim([x.min(), x.max()])
ax.set_ylim([0, 1.5])
ax.grid()
fig.savefig("cdf.png")

t = np.arange(0, 100, 0.01)
y1 = np.vectorize(lambda l: 1.0 - math.exp(-l * 0.05))(t)
y2 = np.vectorize(lambda l: 1.0 - math.exp(-l * 0.1))(t)
y3 = np.vectorize(lambda l: 1.0 - math.exp(-l * 0.15))(t)

fig1, ax2 = plt.subplots()
ax2.plot(x, np.divide(cdf.cumcount, bincount), label='raw data')
ax2.plot(t, y1, label='λ=0.05')
ax2.plot(t, y2, label='λ=0.1')
ax2.plot(t, y3, label='λ=0.15')
ax2.set(xlabel='x', ylabel='P(X ≤ x)',title='Cumulative Distribution With Guesses')
ax2.set_xlim([x.min(), x.max()])
ax2.set_ylim([0, 1.5])
ax2.grid()
ax2.legend()
fig1.savefig("cdf-with-guesses.png")

lambda_estimate = samples.size / np.sum(samples)
print("{:.4f}".format(lambda_estimate))

intensity = 0.1
fig3, ax3 = plt.subplots()
ax3.plot(t, np.vectorize(lambda l: 1.0 - math.exp((-l * intensity)))(t), label='model')
for n in range(1, 7):
    size = pow(10, n)
    estimator = size / np.sum(generate(intensity, size))
    print(estimator)
    ax3.plot(t, np.vectorize(lambda l: 1.0 - math.exp((-l * estimator)))(t), label=f'samples={size}')

ax3.set(xlabel='x', ylabel='P(X ≤ x)', title='Inferred Cumulative Distribution')
ax3.set_xlim([x.min(), x.max()])
ax3.set_ylim([0, 1.5])
ax3.grid()
ax3.legend()
fig3.savefig("cdf-with-inference.png")


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

def draw_parabola(steps=50):
    x = np.linspace(-4, 4, steps)
    plt.plot(x, x ** 2)
    plt.axvline(x=0, color='b', linestyle='dashed')

def draw_paraboloid(steps=50):
    fig = plt.figure(figsize=(10, 10))
    ax = fig.gca(projection='3d')

    x = np.linspace(-1, 1, steps)
    y = np.linspace(-1, 1, steps)
    X, Y = np.meshgrid(x, y)
    Z = X ** 2 + Y ** 2
    ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)

def draw_mishra_bird():
    fig = plt.figure(figsize=(14, 10))
    x = np.arange(-10, 1, 0.1)
    y = np.arange(-6, 0.5, 0.1)
    X, Y = np.meshgrid(x, y)
    ax = plt.gca(projection='3d')
    Z = np.sin(Y) * np.exp((1 - np.cos(X)) ** 2) + np.cos(X) * np.cos(X) * np.exp((1 - np.sin(Y)) ** 2) + (X - Y) ** 2
    ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)
    ax.view_init(20, -60)

def draw_hyperbolic_paraboloid():
    fig = plt.figure(figsize=(10, 10))
    ax = fig.gca(projection='3d')

    x = np.linspace(-1, 1, 50)
    y = np.linspace(-1, 1, 50)
    X, Y = np.meshgrid(x, y)
    Z = X ** 2 - Y ** 2
    ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)


import numpy as np

__author__ = "Ussama Zahid"
__email__ = "ussamazahid96@gmail.com"

# utility function for converting degrees to rad, body frame to earth (world) frame, sin, cos, tan

def rad2deg(rad):
    ret = rad*(180/np.pi)
    return ret

def deg2rad(deg):
    return deg*(np.pi/180)

def S(x):
    return np.sin(x)

def C(x):
    return np.cos(x)

def T(x):
    return np.tan(x)

def body2earth(angles):

    sin_phi   = S(angles[0])
    sin_theta = S(angles[1])
    sin_psi   = S(angles[2])

    cos_phi   = C(angles[0])
    cos_theta = C(angles[1])
    cos_psi   = C(angles[2])

    tan_theta = T(angles[1])

    R_linear =[[ (cos_psi * cos_theta) , (cos_psi * sin_theta * sin_phi - sin_psi * cos_phi) , (cos_psi * sin_theta * cos_phi + sin_psi * sin_phi) ],
               [ (sin_psi * cos_theta) , (sin_psi * sin_theta * sin_phi + cos_psi * cos_phi) , (sin_psi * sin_theta * cos_phi - cos_psi * sin_phi) ],
               [          (-sin_theta) ,                               (cos_theta * sin_phi) ,                               (cos_theta * cos_phi) ]]

    R_angular = [[1., sin_phi*tan_theta, cos_phi*tan_theta],
                 [0.,          cos_phi,         -sin_phi],
                 [0., sin_phi/cos_theta, cos_phi/cos_theta]]

    return np.array(R_linear), np.array(R_angular)


from numpy import array as matrix, arange,zeros,transpose,matmul,ones
from math import sqrt,cos,pi
import numpy as np
np.set_printoptions(suppress=True)

a=matrix([[52,55,61,66,70,61,64,73],
          [63,59,55,90,109,85,69,72],
          [62,59,68,113,144,104,66,73,],
          [63,58,71,122,154,106,70,69],
          [67,61,68,104,126,88,68,70],
          [79,65,60,70,77,68,58,75],
          [85,71,64,59,55,61,65,83],
          [87,79,69,68,65,76,78,94]
          ])
a=a-128  #减少偏移

DCT_mat=np.zeros((8,8),dtype=float)

def dct_freq(u,v):
    alpha_u=1/sqrt(2) if u==0 else 1
    alpha_v = 1/sqrt(2) if v == 0 else 1
    theta=0.25*alpha_u*alpha_v  #因子，构成正交矩阵
    sum=0
    for x in range(8):
        for y in range(8):
            basis=a[x][y]*cos((2*x+1)*u*pi/16)*cos((2*y+1)*v*pi/16)
            sum+=basis
    return theta*sum
#获得u，v频率对应DCT变换

for u in range(8):
    for v in range(8):
        DCT_mat[u][v]=round(dct_freq(u,v),2) #赋值

print(DCT_mat)


import numpy as np
import math
from math import inf

from numpy.core.fromnumeric import sort

two_opt_swap = lambda r,i,k: np.concatenate((r[0:i+1],r[k:i:-1],r[k+1:len(r)]))

def get_abc(xs, ys):
    ''' Returns coeffs of the equations of each line:
        ax + by + c = 0.
    '''
    a = ys[:-1] - ys[1:]
    b = xs[1:] - xs[:-1]
    c = xs[:-1]*ys[1:] - xs[1:]*ys[:-1]

    return a,b,c

def intersect_x(a1, a2, b1, b2, c1, c2):
    ''' Returns have these lines intersection,
                x-coord of intersection point of two lines.
    '''
    if (b1*a2-b2*a1)!=0:
        y_int = (c2*a1-c1*a2)/(b1*a2-b2*a1)
        x_int = (-c1-b1*y_int)/a1

        return True, x_int
    else:
        return False, 0

def ray_tracing(data, sorted_data, n):
    ''' Returns how many times ray originating from
        the centroid and parallel to the x axis
        intersects contour made from data sorted by sorted_data.

        This function is using to understand if centroid is in contour.
        It means that contour is single otherwise not.
    '''

    res = 0

    center = [np.sum(data[:, 0])/n,np.sum(data[:, 1])/n]
    max_x = np.max(data[:,0])

    n = len(sorted_data)
    xs = data[sorted_data,0]
    ys = data[sorted_data,1]
    xs = np.append(xs,xs[0])
    ys = np.append(ys,ys[0])
    a, b, c = get_abc(xs, ys)

    for i in range (n-1):
        if a[i]!=0:
            x_int = (-c[i]-b[i]*center[1])/a[i]
            res += 1 if (min(xs[i], xs[i+1]) <= x_int <= max(xs[i], xs[i+1]) and
                        (center[0] <= x_int <= max_x)) else 0
        else:
            res += 1 if ys[i]==center[1] else 0

    return res

def count_loops(data, sorted_data, n, show=False):
    ''' Returns number of intersections in data sorted
        in 'sorted_data' order if parameter show is False
        Otherwise returns arrays with indexes of sorted_data
        where contour intersects.
    '''

    res = 0
    sorted_data = np.append(sorted_data,sorted_data[0])
    xs = data[sorted_data,0]
    ys = data[sorted_data,1]
    n = len(xs)
    beg = []
    end = []
    a,b,c = get_abc(xs, ys)
    for i in range (n-3):
        # consider non-adjacent edges
        for j in range (i+2, n-1):
            # don't consider last edge while considering first edge
            # because they're adjacent
            if i==0 and j==n-2:
                continue
            # don't consider edge that goes after degenerated to a point edge
            if j==i+2 and (a[j-1]==0 and b[j-1]==0):
                continue
            not_null, x_int = intersect_x(a[i], a[j], b[i], b[j], c[i], c[j])

            if not_null and (min(xs[i], xs[i+1]) <= x_int <= max(xs[i], xs[i+1]) and
                                    min(xs[j], xs[j+1]) <= x_int <= max(xs[j], xs[j+1])):
                res+=1
                beg.append(i)
                end.append(j)

    if show:
        return beg, end
    return res

def no_loops(data, sorted_data, n):
    ''' Returns data without loops using algorithm
        based on 2-opt swapping mechanism.
    '''

    x = data[:,0]
    y = data[:,1]
    new = sorted_data.copy()

    # Delete non-unique data from sorted array 'new', save it to 'non_uni'
    uni, inv, counts = np.unique(data[sorted_data], return_inverse=True, return_counts=True, axis=0)
    non_uni = []
    non_cons_points = []
    for i in range (len(uni)):
        if counts[i]>1:
            rep = np.where(inv==i)[0]
            non_uni.append(sorted_data[rep])
            non_cons_points.append(rep[1:])
    new = np.delete(new,non_cons_points)
    n = len(new)
    new = np.append(new,new[0])

    # find intersections and delete them using 'two_opt_swap' function
    flag = True
    it = 0
    # normalize = lambda x: x / np.linalg.norm(x)
    # de = np.array([])
    while flag and it<50:
        it+=1
        flag = False

        xs = x[new]
        ys = y[new]

        a, b, c = get_abc(xs, ys)

        for i in range (n-2):
            # consider non-adjacent edges
            for j in range (i+2, n):
                # don't consider last edge while considering first edge
                # because they're adjacent
                if i==0 and j==n-1:
                    continue

                # find intersection of two edges
                not_null, x_int = intersect_x(a[i], a[j], b[i], b[j], c[i], c[j])
                # check where the intersection lies
                if not_null and (min(xs[i], xs[i+1]) <= x_int <= max(xs[i], xs[i+1]) and
                                 min(xs[j], xs[j+1]) <= x_int <= max(xs[j], xs[j+1])):
                    flag = True
                    new = two_opt_swap(new,i,j)
                    break
            else:
                continue
            break

        # Not quite a successful attempt to remove deadlocks.
        # It removed dead ends, but because of this, loops appeared
        # And took more time to find contour and built contour less than 1% shorter
        # for imporoved NN and N21 algorithms and less than 1% longer
        # for improved CH algorithm.
        # Because of this this part was commented.

        # else:
        #     vec = np.vstack((-a, b)).T
        #     norm_vec = np.array(list(map(normalize, vec)))
        #     dotprod = norm_vec[:-1,0]*norm_vec[1:,0] + norm_vec[:-1,1]*norm_vec[1:,1]
        #     de1 = np.where(dotprod[1:]<-1+1e-3)[0]
        #     if len(de)==len(de1):
        #         if np.all(de==de1):
        #             break
        #     de = de1
        #     for ind in de:
        #         new = two_opt_swap(new,ind,ind+2)
        #         flag = True

    # add non-unique data after similar data
    for i in range (len(non_uni)):
        ind = np.where(new==non_uni[i][0])[0]
        new = np.insert(new,ind,non_uni[i][1:])

    # delete closing edge
    new = np.delete(new,-1)
    return new

def sort_nn(data, sim, n):
    ''' Returns data sorted by the nearest neighbour algorithm. '''
    sorted_data = [0]
    while True:
        tmp = inf
        ind = -1
        for i in range (n):
            if i not in sorted_data:
                if sim[sorted_data[-1],i]<tmp:
                    tmp = sim[sorted_data[-1],i]
                    ind = i
        if ind>0:
            sorted_data.append(ind)
        else:
            break

    return np.array(sorted_data)

def sort_nn_md(data, sim, n, return_md=False):
    ''' Returns data sorted by the nearest neighbour algorithm
        which is single contour.
    '''
    sorted_data = [0]
    while True:
        tmp = inf
        ind = -1
        for i in range (n):
            if i not in sorted_data:
                if sim[sorted_data[-1],i]<tmp:
                    tmp = sim[sorted_data[-1],i]
                    ind = i
        if len(sorted_data)>10 and sim[sorted_data[-1],0]<tmp:
            break
        if ind>0:
            sorted_data.append(ind)
        else:
            break

    sorted_data = np.array(sorted_data)
    if return_md:
        missed_data = np.arange(n)
        md = np.delete(missed_data, missed_data[sorted_data])
        return sorted_data, md

    else:
        return sorted_data

def sort_nn_loops(data, sim, n):
    ''' Returns data sorted by the nearest neighbour algorithm
        and missed data appending algorithm.
    '''

    sorted_data, md = sort_nn_md(data, sim, n, return_md=True)
    sorted_data = append_md(sorted_data, md, sim)

    return sorted_data

def sort_nn_no_loops(data, sim, n):
    ''' Returns data sorted by the nearest neighbour algorithm,
        missed data appending algorithm and
        loops removal algorithm.
    '''

    sorted_data = sort_nn_loops(data, sim, n)
    new = no_loops(data, sorted_data, n)

    return new

def append_md(sorted_data, md, sim):
    ''' Returns sorted_data to which md was added
        using nearest neighbour concept.
    '''

    for i in md:
        tmp = inf
        ind = -1
        for j in range (len(sorted_data)-1):
            dist = sim[i][sorted_data[j]] + sim[i][sorted_data[j+1]]

            if dist<tmp:
                tmp = dist
                ind = j
        if sim[i][sorted_data[-1]] + sim[i][sorted_data[0]] < tmp:
            sorted_data = np.append(sorted_data,i)
        else:
            sorted_data = np.insert(sorted_data,ind+1,i)

    return sorted_data

def sort_21_loops(data, sim, n):
    ''' Returns data sorted by algorithm based on
        inserting the second contour into the first one.
    '''

    mid = np.argmin(sim[0][4:])
    sorted_data = np.arange(mid)
    md = np.delete(np.arange(n), sorted_data)

    sorted_data = append_md(sorted_data, md, sim)

    return sorted_data

def sort_21_no_loops(data, sim, n):
    ''' Returns data sorted by algorithm based on
        inserting the second contour into the first one
        and loop removal algorithm.
    '''

    sorted_data = sort_21_loops(data, sim, n)
    new = no_loops(data, sorted_data, n)

    return new

def clockwiseangle_and_distance(point, origin, refvec):
    ''' Returns polar coordinates of point with center = origin
        and axis = refvec.
    '''

    vec = [point[0]-origin[0], point[1]-origin[1]]
    lenvec = math.hypot(vec[0], vec[1])

    if lenvec == 0:
        return -math.pi, 0

    normalized = [vec[0]/lenvec, vec[1]/lenvec]
    dotprod  = normalized[0]*refvec[0] + normalized[1]*refvec[1]     # x1*x2 + y1*y2
    diffprod = refvec[1]*normalized[0] - refvec[0]*normalized[1]     # x1*y2 - y1*x2
    angle = math.atan2(diffprod, dotprod)

    if angle < 0:
        return 2*math.pi+angle, lenvec

    return [angle, lenvec]

def sort_angle(data, sim, n):
    ''' Returns data sorted by polar coordinates. '''
    origin = [np.sum(data[:, 0])/n,np.sum(data[:, 1])/n]
    refvec = [0,1]
    # find polar coordinates
    angle = np.array([clockwiseangle_and_distance(point, origin, refvec) for point in data])
    # sort by polar coordinates
    sort = np.lexsort((angle[:,1],angle[:,0]))

    return sort

def sort_best(data, sim, n):
    ''' Returns data sorted by NN with no loops algorithm
        if it works correctly
        otherwise data sorted by adding 2nd contour into 1st with no loops algorithm
        if it works correctly.
    '''

    res = sort_nn_no_loops(data, sim, n)
    if count_loops(data, res, n)>0:
        res = sort_21_no_loops(data, sim, n)

    return res

def rotate(A,B,C):
    ''' Returns where is point C for vector AB
        if returns negative number - C is right
        otherwise left.
    '''
    return (B[0]-A[0])*(C[1]-B[1])-(B[1]-A[1])*(C[0]-B[0])

def jarvismarch(data, sim, n):
    ''' Returns minimum convex polygon
        using Jarvis algorithm.
    '''

    points = np.arange(n)
    ind_min_x = np.argmin(data[:,0])
    hull = [ind_min_x]
    points = np.delete(points,ind_min_x)
    points = np.append(points,ind_min_x)

    while True:
        right = 0
        for i in range(1,len(points)):
            if rotate(data[hull[-1]],data[points[right]],data[points[i]])<0:
                right = i
        if points[right]==hull[0]:
            break
        else:
            hull = np.append(hull,points[right])
            points = np.delete(points,right)
    return hull

def distance_ch(p1,p2,p3):
    ''' Returns distance from point p3 to line p1,p2. '''
    d = np.cross(p2-p1,p3-p1)/np.linalg.norm(p2-p1)
    return d

def distance_ch_vert(d,a):
    ''' Returns hypotenuse of a right triangle with legs d and a. '''
    return np.sqrt(d**2+a**2)

def sort_ch_loops(data, sim, n):
    ''' Returns indexes of data sorted using algorithm
        based on adding point into Convex Hull.
    '''

    # find hull
    hull = jarvismarch(data, sim, n)
    nearest_line = [[i] for i in hull]
    distances = [[0] for i in hull]
    hull = np.append(hull, hull[0])

    # find nearest edge of convex hull and distance to it for other points
    for i, point in enumerate(data):
        if i in hull:
            continue
        else:
            a = inf
            ind = 0
            for j in range (len(hull)-1):
                d = distance_ch(data[hull[j]],data[hull[j+1]],point)
                if abs(d)<abs(a):
                    a = d
                    ind = j
            nearest_line[ind].append(i)
            distances[ind].append(a)

    # sort points by the distance of their projection from
    # the first point of the nearest edge
    sorted_data = []
    for i in range(len(nearest_line)):
        c = nearest_line[i]
        a = distances[i]
        b =[[i] for i in range(len(c))]
        b[0].append(0)
        for j in range(1, len(c)):
            b[j].append(distance_ch_vert(sim[c[0],c[j]],a[j]))
        b = sorted(b,key=lambda dist:dist[1])

        for j in range(len(c)):
            sorted_data.append(c[b[j][0]])
    sorted_data = np.array(sorted_data)
    return sorted_data

def sort_ch_no_loops(data, sim, n):
    ''' Returns indexes of data sorted using algorithm
        based on adding point into Convex Hull and
        loop removal algorithm.
    '''

    sorted_data = sort_ch_loops(data, sim, n)
    new = no_loops(data, sorted_data, n)

    return new

def two_opt(cities,sim,n,improvement_threshold=0.001):
    ''' Returns indexes of data sorted using 2-opt algorithm. '''

    route = np.arange(n)
    improvement_factor = 1
    # the distance of the initial path.
    best_distance = calc_path(cities,sim,route,n)
    while improvement_factor > improvement_threshold:
        distance_to_beat = best_distance
        for swap_first in range(1,n-2):
            for swap_last in range(swap_first+1,n):
                new_route = two_opt_swap(route,swap_first,swap_last)
                new_distance = calc_path(cities,sim,new_route,n)
                if new_distance < best_distance:
                    route = new_route
                    best_distance = new_distance
        improvement_factor = 1 - best_distance/distance_to_beat

    return route

def calc_path(data, sim, sorted, n):
    ''' Returns length of contour made of data sorted
        by indexes in parameter sorted.
    '''

    path = 0
    for i in range (n-1):
        path += sim[sorted[i]][sorted[i+1]]
    path += sim[sorted[-1]][sorted[0]]

    return path


import numpy as np

def fatoracao_lu(A):
    n = len(A)

    # Inicializa matrizes L e U
    L = [[0.0] * n for i in range(n)]
    U = [[0.0] * n for i in range(n)]

    for j in range(n):
        #Diagonal de L igual a 1
        L[j][j] = 1.0

        for i in range(j+1):
            soma = 0
            for k in range(i):
                soma += U[k][j] * L[i][k]
            dense_a_i = np.array(A[i].todense())
            U[i][j] = dense_a_i[0][j] - soma

        for i in range(j, n):
            soma = 0
            for k in range(j):
                soma += U[k][j] * L[i][k]
            dense_a_i = np.array(A[i].todense())
            L[i][j] = (dense_a_i[0][j] - soma) / U[j][j]

    return (L, U)

def resolve_lu(A, B):
    n = len(A)
    L, U = fatoracao_lu(A)

    #Calcula valores de y
    Y_lista = [0 for i in range(n)]
    X_lista = [0 for i in range(n)]

    Y_lista[0] = B[0]
    for l in range(1, n):
        Y_lista[l] = (B[l] - sum(L[l][c] * Y_lista[c] for c in range(n)))/L[l][l]

    X_lista[n-1] = Y_lista[n-1]/U[n-1][n-1]
    for l in reversed(range(0, n-1)):
        X_lista[l] = (Y_lista[l] - sum(U[l][c] * X_lista[c] for c in reversed(range(n))))/U[l][l]

    return X_lista


from scipy.special import jv
from astropy import constants as c
from astropy import units as u
import numpy as np

__all__ = ['get_a_from_f_orb', 'get_f_orb_from_a', 'ensure_array', 'chirp_mass', 'peters_g', 'peters_f']

def get_a_from_f_orb(f_orb, m_1, m_2):

    """
    Converts orbital frequency to semi-major axis using Kepler's third law

    ### Parameters:
        f_orb (`float/array`): Orbital frequency
        m_1 (`float/array`): Primary mass
        m_2 (`float/array`): Secondary mass

    ### Returns:
        a (`float/array`): Semi-major axis
    """

    a = (c.G * (m_1 + m_2) / (2 * np.pi * f_orb)**2)**(1/3)

    if isinstance(a, u.quantity.Quantity): # simplify units if present
        a = a.to(u.AU)

    return a

def get_f_orb_from_a(a, m_1, m_2):

    """
    Converts semi-major axis to orbital frequency using Kepler's third law

    ### Parameters:
        a (`float/array`): Semi-major axis
        m_1 (`float/array`): Primary mass
        m_2 (`float/array`): Secondary mass

    ### Returns:
        f_orb (`float/array`): Orbital frequency
    """

    f_orb = ((c.G * (m_1 + m_2) / a**3))**(0.5) / (2 * np.pi)

    if isinstance(f_orb, u.quantity.Quantity): # simplify units if present
        f_orb = f_orb.to(u.Hz)

    return f_orb

def ensure_array(*args):

    """
    Convert arguments to numpy arrays

    ### Parameters:
        args (`any`): Supply any number of arguments of any type

    ### Returns:
        array_args (`any`): Args converted to numpy arrays
        any_not_arrays (`bool`): Whether any arg is not a list or None or a numpy array
    """

    array_args = [None for i in range(len(args))]
    any_not_arrays = False
    for i in range(len(array_args)):
        exists = args[i] is not None
        has_units = isinstance(args[i], u.quantity.Quantity)
        if exists and has_units:
            if not isinstance(args[i].value, np.ndarray):
                any_not_arrays = True
                array_args[i] = np.asarray([args[i].value]) * args[i].unit
            else:
                array_args[i] = args[i]
        elif exists and not has_units:
            if not isinstance(args[i], np.ndarray):
                if not isinstance(args[i], list):
                    any_not_arrays = True
                    array_args[i] = np.asarray([args[i]])
                else:
                    array_args[i] = np.asarray(args[i])
            else:
                array_args[i] = args[i]
        else:
            array_args[i] = args[i]
    return array_args, any_not_arrays

def chirp_mass(m_1, m_2):

    """
    Computes chirp mass of binaries

    ### Parameters:
        m_1 (`float/array`): Primary mass
        m_2 (`float/array`): Secondary mass

    ### Returns:
        m_c (`float/array`): Chirp mass
    """

    m_c = (m_1 * m_2)**(3/5) / (m_1 + m_2)**(1/5)

    if isinstance(m_c, u.quantity.Quantity): # simplify units if present
        m_c = m_c.to(u.Msun)

    return m_c

def peters_g(n, e):

    """
    Compute g(n, e) from Peters and Mathews (1963) Eq.20

    ### Parameters:
        n (`int/array`): Harmonic(s) of interest
        e (`float/array`): Eccentricity

    ### Returns:
        g (`array`): Relative power of gravitational radiation at the nth harmonic
    """

    bracket_1 = jv(n-2, n*e) - 2*e*jv(n-1, n*e) + 2/n*jv(n, n*e) + 2*e*jv(n+1, n*e) - jv(n+2, n*e)
    bracket_2 = jv(n-2, n*e) - 2*jv(n, n*e) + jv(n+2, n*e)
    bracket_3 = jv(n, n*e)
    g = n**4/32 * (bracket_1**2 + (1 - e**2) * bracket_2**2 + 4 / (3 * n**3) * bracket_3**2)

    return g

def peters_f(e):

    """
    Compute f(e) from Peters and Mathews (1963) Eq.17

    ### Parameters:
        e (`float/array`): Eccentricity

    ### Returns:
        f (`float/array`): Enhancement factor of gravitational radiation
    """

    numerator = 1 + (73/24)*e**2 + (37/96)*e**4
    denominator = (1 - e**2)**(7/2)

    f = numerator / denominator

    return f


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

# PROBLEM DEFINITION

# Objective cost minimization function

def function(x):
    return (1.5 - x[0] - x[0] * x[1]) ** 2 + (2.25 - x[0] + x[0] * (x[1]) ** 2) ** 2 + (
                2.625 - x[0] + x[0] * (x[1]) ** 3) ** 2

nVar = 2  # Number of variables
bound = [-4.5, 4.5]  # Lower and upper bound of variables

def apply_bound(position):  # Make sure particle positions are in range
    if not -4.5 <= position[0] <= 4.5:
        position[0] = random.uniform(-4.5, 4.5)

    if not -4.5 <= position[1] <= 4.5:
        position[1] = random.uniform(-4.5, 4.5)

    return np.array(position)

# PARAMETERS OF PSO

maxIter = 100  # Maximum number of iterations
npop = 50  # Swarm size
w = 1  # Inertia Coefficient
wdamp = 0.95  # Damping ratio of Inertia Coefficient
c1 = 2  # Personal Acceleration Coefficient
c2 = 2  # Social Acceleration Coefficient

# INITIALIZATION

# Column definition: 0 --> particle position, 1 --> Particle velocity, 2 --> particle cost,
# 3 --> particle best position, 4 --> particle best cost

particle = np.zeros((npop, 5), dtype=list)  # Create a particle population array

# Initialize members of the population

# Initialize the global best
globalBestCost = np.inf

for member in range(npop):

    # Generate a Random solution
    solution = []  # Create a particle position list
    for i in range(nVar):
        solution.append(random.uniform(-4.5, 4.5))  # Assign a random position
    particle[member][0] = np.array(solution)  # Store the solution into an array

    # Initialize Velocity to a zero array
    particle[member][1] = np.array([0, 0])

    # Evaluation of Particle Cost
    particle[member][2] = function(particle[member][0])

    # Update the Personal Best Position
    particle[member][3] = particle[member][0]

    # Update the personal Best Cost
    particle[member][4] = particle[member][2]

    # Update the Global best Position and Cost
    if particle[member][4] < globalBestCost:
        globalBestCost = particle[member][4]
        globalBestPosition = particle[member][3]

# Array to hold Best Cost Value for each Iteration
bestCosts = np.zeros(maxIter)

# MAIN LOOP OF PSO

for it in range(maxIter):

    for member in range(npop):

        # Update Particle Velocity

        particle[member][1] = (w * particle[member][1]
                               + c1 * random.uniform(0, 1) * (particle[member][3] - particle[member][0])
                               + c2 * random.uniform(0, 1) * (globalBestPosition - particle[member][0]))

        # Update Current Particle Position

        particle[member][0] = particle[member][0] + particle[member][1]
        particle[member][0] = apply_bound(particle[member][0])  # Apply Bounds

        # Compute the cost of the particle at the new position

        particle[member][2] = function(particle[member][0])

        # Update the Best Cost and Position

        if particle[member][2] < particle[member][4]:
            particle[member][4] = particle[member][2]
            particle[member][3] = particle[member][0]

            # Update the Global best Position and Cost
            if particle[member][4] < globalBestCost:
                globalBestCost = particle[member][4]
                globalBestPosition = particle[member][3]

    # Store the Best Cost value
    bestCosts[it] = globalBestCost

    # Display the iterations

    # print("Iteration: ",it+1, end=' ')
    # print("Best Minimum =", bestCosts[it])

    # Update w

    w = w * wdamp

print(globalBestPosition, globalBestCost)

# RESULTS
figure = plt.figure(figsize=(10, 6))
plt.plot(bestCosts)
plt.hlines(globalBestCost,0,maxIter,label=f'minimum : {globalBestCost} at {globalBestPosition}',colors='red',linestyles='dotted')
plt.xlabel('Iterations')
plt.ylabel('Best Minimum')
plt.grid('on')
plt.legend()
plt.show()


import numpy as np
import matplotlib.pyplot as plt
from math import radians
import pandas as pd

def radio(alpha, epsilon, theta):
    return alpha/((1-epsilon* np.cos(radians(theta))))
def vectorize (a):
    angle, mag = a[0], a[1]
    return [mag*np.cos(radians(angle)), mag*np.sin(radians(angle))]
def plot_ellipse(alpha, epsilon, step = 1):

    params = [[step*i,radio(alpha,epsilon,step*i )] for i in range(360//step)]
    nparams = [vectorize(param) for param in params]
    Xs =np.array(nparams)[:,0]
    Ys = np.array(nparams)[:,1]
    plt.title(f'Elipse: alpha: {alpha}, epsilon {epsilon}')
    plt.scatter(Xs, Ys)
    plt.plot([Xs.min(),Xs.max()],[0,0])
    plt.plot([(Xs.max()+Xs.min())/2,(Xs.max()+Xs.min())/2],[Ys.min(),Ys.max()])
    plt.show()
    df =pd.DataFrame(params, columns = ['Angulo', "Valor"])
    d2 = pd.DataFrame(nparams, columns = ['x','y'])
    df['X']=Xs
    df['Y']=Ys
    print(df)
plot_ellipse(0.6288424499,.777,1)
plot_ellipse(1,0)
plot_ellipse(1,.5)
def euc (v1, v2):
    return np.sqrt(abs((v2[1]-v1[1])**2+(v2[0]-v1[0])**2))
earth = []
neo = []
eucs = []
for i in range(1,365*1000):
    earth.append(vectorize([i,radio(1,0,i)]))
    neo.append(vectorize([365/729*i,radio(0.6288424499,.777,365/729*i)]))
    eucs.append(euc(vectorize([i,radio(1,0,i)]),vectorize([729/365*i,radio(0.6288424499,.777,729/365*i)])))
di = pd.DataFrame(earth, columns = ['EarthX', "EarthY"])
di["NEOX"], di["NEOY"] = np.array(neo)[:,0],np.array(neo)[:,1]
di["Euclidean Distance"] = eucs
di.min()

di.to_csv("Datos_obtenidos_de_la_simulación.csv")
fig = di[["Euclidean Distance"]].plot(figsize=(20, 16), fontsize=26, title = "NEO Y").get_figure()
fig.savefig("11.png")
ax1 = di.plot.scatter(x='EarthX',
                       y='EarthY',figsize=(20, 20)
                      ).get_figure()
ax1.savefig("9.png")
fig.savefig("1.png")
di["Euclidean Distance"].plot()
descriptive = di.describe()
descriptive.to_csv("Estadisticas.csv")
di.describe()
np.argmin(di["Euclidean Distance"].values)/365


# MultiAgent
# (c) 2017-2019, NiL, csningli@gmail.com

import math
from numpy import array, dot
from numpy.linalg import norm

NON_ZERO_LOWER_BOUND = 0.01
HALF_PI = math.pi / 2.0

def min_max_bound(f, a, b) :
    return max(a, min(b, f))

def vec2_length(v) :
    return math.sqrt(v[0] * v[0] + v[1] * v[1])

def vec2_angle(v, positive = True) :
    angle = 0
    v_norm = vec2_length(v)
    if v_norm > 0.01 :
        angle = math.acos(v[0] / v_norm)
        if v[1] < 0 :
            if positive == True :
                angle = 2 * math.pi - angle
            else :
                angle = - angle
    return angle

def vec2_sub(v1, v2) :
    return (v1[0] - v2[0], v1[1] - v2[1])

def vec2_add(v1, v2) :
    return (v1[0] + v2[0], v1[1] + v2[1])

def vec2_dot(v1, v2) :
    return v1[0] * v2[0] + v1[1] * v2[1]

def vec2_scale(v, f) :
    return (f * v[0], f * v[1])

def vec2_norm(v) :
    result = 0
    length = vec2_length(v)
    if length > NON_ZERO_LOWER_BOUND :
        result = vec2_scale(v, 1.0 / length)
    return result

def vec2_rotate(v, a) :
    angle = vec2_angle(v) + a
    v_norm = vec2_length(v)
    return (v_norm * math.cos(angle), v_norm * math.sin(angle))

def vec2_min_max(v, a, b) :
    result = v
    length = vec2_length(v)
    if  length > NON_ZERO_LOWER_BOUND :
        result = vec2_scale(result, min_max_bound(length, a, b) / length)
    return result

def pldiff(p, p1, p2) :
    diff = array((0.0, 0.0))
    drt = array(p2) - array(p1)
    drt = drt / norm(drt)
    diff = array(p1) - drt * dot(array(p1) - array(p), drt) - array(p)
    return tuple(diff)

def pldist_l2(p, p1, p2):
    diff = pldiff(p = p, p1 = p1, p2 = p2)
    return norm(array(diff))

def ppdist_l2(p1, p2) :
    return norm(array(p1) - array(p2))

def ppdist_l1(p1, p2) :
    return abs(p1[0] - p2[0]) + abs(p1[1] - p2[1])


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

data = [[6.1101, 12.5920], [5.5277, 9.1302], [8.5186, 17.6620], [6.5186, 11.6620], [7.5186, 15.6620]]
df = pd.DataFrame(data, columns=['X', 'Y'])
m = len(df)

def hypothesis(theta_h, x):
    return theta_h[0] + theta_h[1] * x

def cost_calc(theta_x, x, y):
    return (1 / 2 * m) * np.sum((hypothesis(theta_x, x) - y) ** 2)

def gradient_descent(theta_g, x, y, epoch, alpha):
    cost_g = []
    i = 0
    while i < epoch:
        hx = hypothesis(theta_g, x)
        theta_g[0] -= alpha * (sum(hx - y) / m)
        theta_g[1] -= (alpha * np.sum((hx - y) * x)) / m
        cost_g.append(cost_calc(theta_g, x, y))
        i += 1
    return theta_g, cost_g

def predict(theta_p, x, y, epoch, alpha):
    theta_p, cost_p = gradient_descent(theta_p, x, y, epoch, alpha)
    return hypothesis(theta_p, x), cost_p, theta_p

theta = [0, 0]
y_hat, cost, theta = predict(theta, df['X'], df['Y'], 2000, 0.01)

plt.figure()
plt.scatter(df['X'], df['Y'], label='Y')
plt.scatter(df['X'], y_hat, label='Y\u0302')
plt.legend(loc="upper left")
plt.xlabel("Independent variable X")
plt.ylabel("Outcome variable Y")
plt.show()

plt.figure()
plt.scatter(range(0, len(cost)), cost)
plt.show()


import numpy as np
from scipy.sparse import diags

def d2_mat_dirichlet(nx, dx):
    """
    Constructs the centered second-order accurate second-order derivative for
    Dirichlet boundary conditions.

    Parameters
    ----------
    nx : integer
        number of grid points
    dx : float
        grid spacing

    Returns
    -------
    d2mat : numpy.ndarray
        matrix to compute the centered second-order accurate first-order deri-
        vative with Dirichlet boundary conditions on both side of the interval
    """
    # We construct a sequence of main diagonal elements,
    diagonals = [[1.], [-2.], [1.]]
    # and a sequence of positions of the diagonal entries relative to the main
    # diagonal.
    offsets = [-1, 0, 1]

    # Call to the diags routine; note that diags return a representation of the
    # array; to explicitly obtain its ndarray realisation, the call to .toarray()
    # is needed. Note how the matrix has dimensions (nx-2)*(nx-2).
    d2mat = diags(diagonals, offsets, shape=(nx-2,nx-2)).toarray()

    # Return the final array divided by the grid spacing **2.
    return d2mat / dx**2


import numpy as np

def softmax(logit, y=None):
    N = logit.shape[0]
    norm = logit - np.max(logit, axis=1, keepdims=True)
    Z = np.sum(np.exp(norm), axis=1, keepdims=True)
    log_probs = norm - np.log(Z)
    probs = np.exp(log_probs)
    if y is None:
        return probs

    if y.ndim == 1:  # Raw categories
        loss = -np.mean(log_probs[np.arange(N), y])
        probs[np.arange(N), y] -= 1
    else:  # One-hot encoded categories
        loss = -np.mean(log_probs[y.astype('bool')])
        probs[y.astype('bool')] -= 1
    return loss, (probs/N, )

def mean_squared_error(logit, y=None):
    # J = (y-yHat)**2
    N = logit.shape[0]
    if y is None:
        return logit
    dx = logit - y
    squared_error = dx**2
    loss = squared_error.mean()
    return loss, (2*dx/N, )


"""
Please see
https://computationalmindset.com/en/neural-networks/ordinary-differential-equation-solvers.html#sys1
for details
"""

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
from tfdiffeq import odeint

def ode_sys(t, X):
	x=X[0]
	dx_dt=X[1]
	d2x_dt2=-dx_dt - 2*x
	return tf.stack([dx_dt, d2x_dt2])

an_sol_x = lambda t : \
	np.exp(-t/2.) * (np.cos(np.sqrt(7) * t / 2.) + \
	np.sin(np.sqrt(7) * t / 2.)/np.sqrt(7.))

t_begin=0.
t_end=12.
t_nsamples=100
t_space = np.linspace(t_begin, t_end, t_nsamples)
x_init = tf.constant([1.])
dxdt_init = tf.constant([0.])

x_an_sol = an_sol_x(t_space)

num_sol = odeint(
	ode_sys,
	tf.convert_to_tensor([x_init, dxdt_init], dtype=tf.float64),
	tf.constant(t_space)).numpy()

plt.figure()
plt.plot(t_space, x_an_sol,'--', linewidth=2, label='analytical')
plt.plot(t_space, num_sol[:,0], linewidth=1, label='numerical')
plt.title('ODE 2nd order IVP solved by TensorFlowDiffEq')
plt.xlabel('t')
plt.ylabel('x')
plt.legend()
plt.show()


import numpy as np

def leastSquares(data, label):
    # Sum of squared error shoud be minimized
    #that trains a least-squares classifier based on a data matrix data and its class label vector label . It provides as output the linear classifier weight vector weight and bias.
    # page 29 lecture 6
    #
    # INPUT:
    # data        : Training inputs  (num_samples x dim)
    # label       : Training targets (num_samples x 1)
    #
    # OUTPUT:
    # weights     : weights   (dim x 1)
    # bias        : bias term (scalar)

    #####Insert your code here for subtask 1a#####
    # Extend each datapoint x as [1, x]
    # (Trick to avoid modeling the bias term explicitly)

    # D = Number of attributes
    D = data.shape[0]

    ones = np.full(D,1)
    data = np.column_stack((ones,data))

    # Calculating the weights
    Weight = np.linalg.pinv(data)@label

    weight = Weight[1:]
    bias = Weight[0]

    return weight, bias