"""Test capabilities for optimization.
This module contains a host of models and functions often used for testing optimization
algorithms.
"""
import sys

import numpy as np
import pandas as pd
from scipy.optimize import rosen

def ackley(x, a=20, b=0.2, c=2 * np.pi):
    r"""Ackley function.

    .. math::
        f(x) = -a \exp{\left(-b \sqrt{\frac{1}{p} \sum_{i=1}^p x_i^2}\right)}
        \exp{\left(\frac{1}{p} \sum_{i=1}^p \cos(c x_i)\right)} + a + \exp(1)

    Parameters
    ----------
    x : array_like
        Input domain with dimension :math:`p`, which
        is usually evaluated on the hypercube
        :math:`x_i \in [-32.768, 32.768]` for all :math:`i = 1, \dots, p`.
    a : float, optional
        The default value is 20.
    b : float, optional
        The default value is 0.2.
    c : float, optional
        The default value is :math:`2\pi`.

    Returns
    -------
    float
         Output domain

    Notes
    -----
    This function was proposed by David Ackley in [A1987]_ and used in [B1996]_
    and [M2005]_. It is characterized by an almost flat outer region and a central hole
    or peak where modulations become increasingly influential. The function has
    its global minimum :math:`f(x) = 0` at :math:`x = \begin{pmatrix}0 & \dots & 0
    \end{pmatrix}^T`.

    .. figure:: ../../docs/_static/images/fig-ackley.png
       :align: center

    References
    ----------
    .. [A1987] Ackley, D. H. (1987).
       *A connectionist machine for genetic hillclimbing*. Kluwer Academic Publishers.
    .. [B1996] Back, T. (1996).
       *Evolutionary algorithms in theory and practice:
       Evolution strategies, evolutionary programming, genetic algorithms*.
       Oxford University Press.
    .. [M2005] Molga, M., and Smutnicki, C. (2005).
       *Test functions for optimization needs*.
       Retrieved June 2020, from
       http://www.zsd.ict.pwr.wroc.pl/files/docs/functions.pdf.

    Examples
    --------
    >>> from temfpy.optimization import ackley
    >>> import numpy as np
    >>>
    >>> x = [0, 0]
    >>> y = ackley(x)
    >>> np.testing.assert_almost_equal(y, 0)
    """
    rslt = (
        a + np.exp(1) - (a * (np.exp(-b * np.sqrt(1 / len(x) * np.sum(np.square(x))))))
    )
    rslt -= np.exp(1 / len(x) * np.sum(np.cos(np.multiply(c, x))))

    return rslt

def rastrigin(x, a=10):
    r"""Rastrigin function.

    .. math::
        f(x) = a p + \sum_{i=1}^p \left(x_i^2 - 10 \cos(2\pi x_i)\right)

    Parameters
    ----------
    x : array_like
        Input domain with dimension :math:`p`.
        It is usually evaluated on the hypercube
        :math:`x_i\in [-5.12, 5.12]` for all :math:`i = 1, \dots, p`.
    a : float, optional
        The default value is 10.

    Returns
    -------
    float
         Output domain

    Notes
    -----
    The function was first proposed by Leonard Rastrigin in [R1974]_.
    It produces frequent local minima, as it is highly multimodal.
    However, the location of the minima are regularly distributed.
    The function has its global minimum :math:`f(x) = 0` at
    :math:`x = \begin{pmatrix}0 & \dots & 0 \end{pmatrix}^T`.

    .. figure:: ../../docs/_static/images/fig-rastrigin.png
       :align: center

    References
    ----------
    .. [R1974] Rastrigin, L. A. (1974).
       *Systems of extremal control*.
       Moscow, Russia.

    Examples
    --------
    >>> from temfpy.optimization import rastrigin
    >>> import numpy as np
    >>>
    >>> x = [0, 0]
    >>> y = rastrigin(x)
    >>> np.testing.assert_almost_equal(y, 0)
    """
    rslt = a * len(x) + np.sum(
        np.multiply(x, x) - 10 * np.cos(2 * np.multiply(np.pi, x)),
    )

    return rslt

def rosenbrock(x):
    r"""Rosenbrock function.

    .. math::
        f(x) = \sum^{p-1}_{i = 1} \left[100(x_{i+1}-x_i^2)^2 + (1-x_i^2) \right]

    Parameters
    ----------
    x : array_like
        Input domain with dimension :math:`p > 1`.

    Returns
    -------
    float
         Output domain

    Notes
    -----
    The function was first proposed by Howard H. Rosenbrock in [R1960]_ and
    is often referred to as Rosenbrock's valley or
    Rosenbrock's Banana function due to its shape.
    The function has its global minimum at
    :math:`x = \begin{pmatrix}1 & \dots & 1 \end{pmatrix}^T`.

    .. figure:: ../../docs/_static/images/fig-rosenbrock.png
       :align: center

    References
    ----------
    .. [R1960] Rosenbrock, H. H. (1960).
       An automatic method for finding the greatest
       or least value of a function.
       *The Computer Journal*, 3(3): 175-184.

    Examples
    --------
    >>> from temfpy.optimization import rosenbrock
    >>> import numpy as np
    >>>
    >>> x = [1, 1]
    >>> y = rosenbrock(x)
    >>> np.testing.assert_almost_equal(y, 0)
    """

    if not isinstance(x, (list, tuple, pd.core.series.Series, np.ndarray)):
        sys.exit("The parameter x must be an array like object.")
    else:
        if len(x) < 2:
            sys.exit("The input array x must be at least of length 2.")

    rslt = rosen(x)

    return rslt

def carlberg(x, a, b):
    r"""Carlberg function.

    .. math::
        f(x) = \frac{1}{2}\sum_{i=1}^p a_i (x_i - 1)^2 + b \left[p -
        \sum_{i=1}^p \cos(2 \pi(x_i-1)) \right]

    Parameters
    ----------
    x : array_like
        Input vector with dimension :math:`p`.
    a : array_like
        Input vector with dimension :math:`p`.
    b : float
        Cannot be smaller than zero.
        For more information see Notes.

    Returns
    -------
    float
         Output domain

    Notes
    -----
    If the values in :math:`a` are widely distributed, the function is
    said to be ill-conditioned, making it hard to minimize in some
    directions for Hessian-free numerical methods.
    If :math:`b=0` (see second graph below), the function is
    convex, smooth, and has its minimum at
    :math:`x = \begin{pmatrix}1 & \dots & 1 \end{pmatrix}^T`. For
    :math:`b>0` the function is no longer convex and has many local
    minima (see first graph below). These circumstances make it hard for
    local optimization methods to find the global minimum,
    which is still at
    :math:`x = \begin{pmatrix}1 & \dots & 1 \end{pmatrix}^T`.

    .. figure:: ../../docs/_static/images/fig-carlberg-noise.png
       :align: center
    .. figure:: ../../docs/_static/images/fig-carlberg-no-noise.png
       :align: center

    References
    ----------
    .. [C2019] Carlberg, K. (2019).
       *Optimization in Python*.
       Fundamentals of Data Science Summer Workshops, Stanford.

    Examples
    --------
    >>> from temfpy.optimization import carlberg
    >>> import numpy as np
    >>>
    >>> x = [1, 1]
    >>> a = [1, 1]
    >>> b = 1
    >>> y = carlberg(x,a,b)
    >>> np.testing.assert_almost_equal(y, 0)
    """

    if b < 0:
        sys.exit("Input parameter b must not be smaller than zero.")

    x, a = np.atleast_1d(x), np.atleast_1d(a)

    dimension = len(x)

    fval = 0
    fval += 0.5 * np.sum(a * (x - np.ones(dimension)) ** 2)
    fval += b * dimension
    fval -= b * np.sum(np.cos(2 * np.pi * (x - np.ones(dimension))))

    return fval


# -*- coding: utf-8 -*-

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

def show_data(x, y, w=None, b=None):
    plt.scatter(x, y, marker='.')
    if w is not None and b is not None:
        plt.plot(x, w*x+b, c='red')
    plt.show()

# data generation
np.random.seed(272)
data_size = 100
x = np.random.uniform(low=1.0, high=10.0, size=data_size)
y = x * 20 + 10 + np.random.normal(loc=0.0, scale=10.0, size=data_size)

# plt.scatter(x, y, marker='.')
# plt.show()

# train / test split
shuffled_index = np.random.permutation(data_size)
x = x[shuffled_index]
y = y[shuffled_index]
split_index = int(data_size * 0.7)
x_train = x[:split_index]
y_train = y[:split_index]
x_test = x[split_index:]
y_test = y[split_index:]

# visualize data
# plt.scatter(x_train, y_train, marker='.')
# plt.show()
# plt.scatter(x_test, y_test, marker='.')
# plt.show()

# train the liner regression model
regr = LinerRegression(learning_rate=0.01, max_iter=10, seed=314)
regr.fit(x_train, y_train)
print('cost: \t{:.3}'.format(regr.loss()))
print('w: \t{:.3}'.format(regr.w))
print('b: \t{:.3}'.format(regr.b))
show_data(x, y, regr.w, regr.b)

# plot the evolution of cost
plt.scatter(np.arange(len(regr.loss_arr)), regr.loss_arr, marker='o', c='green')
plt.show()


# python code to solve soduku
import pprint
import numpy as np
import logging, sys
logging_level = logging.INFO
logging.basicConfig(stream=sys.stderr, level=logging_level)

class SudokuSolver:
    """Sodoku Solver Class """
    def __init__(self,array):
        """ Takes in 9x9 sudoku array """
        self.arr = array
        # self.result = []

    def is_possible_grid(self,row,col,user_value):
        """ This function checks whether the input is valid for 3x3 grid """
        start_row = row - (row % 3)
        start_col = col - (col % 3)
        for x in range(3):
            for y in range(3):
                if self.arr[x+start_row][y+start_col] == user_value:
                    logging.debug(f"is_posssible_grid(): (False) x: {x} y: {y} s_row: {start_row} s_col: {start_col} arr[x+start_row][y+start_col]: {self.arr[x+start_row][y+start_col]} == {user_value}")
                    return False
        logging.debug(f"is_posssible_grid(): (True) x: {x} y: {y} s_row: {start_row} s_col: {start_col} arr[x+start_row][y+start_col]: {self.arr[x+start_row][y+start_col]} != {user_value}")
        return True

    def is_posssible_col(self,col,user_value):
        """ This function checks whether the input is valid for column (len 9) """
        for row in range(9):
            if self.arr[row][col] == user_value:
                logging.debug(f"is_posssible_col row(): (False) row: {row} col: {col} arr{self.arr[row][col]} == {user_value}")
                return False
        logging.debug(f"is_posssible_col row(): (True) row: {row} col: {col} arr{self.arr[row][col]} != {user_value}")
        return True

    def is_posssible_row(self,row,user_value):
        """ This function checks whether the input is valid for row (len 9) """
        for col in range(9):
            if self.arr[row][col] == user_value:
                logging.debug(f"is_posssible_row(): (False) row: {row} col: {col} arr{self.arr[row][col]} == {user_value}")
                return False
        logging.debug(f"is_posssible_row(): (True) row: {row} col: {col} arr{self.arr[row][col]} != {user_value}")
        return True

    def is_possible_value(self,row,col,user_value):
        """ This function checks whether the input is for sudoku board """
        if self.is_possible_grid(row,col,user_value):
            if self.is_posssible_row(row,user_value):
                return self.is_posssible_col(col,user_value)

    def solve(self):
        """ This function solves sudoku board (backtracking) """
        for x in range(9):
            for y in range(9):
                if self.arr[x][y] == 0:
                    for value in range(1,10):
                        if self.is_possible_value(x,y,value):
                            self.arr[x][y] = value
                            self.solve()
                            self.arr[x][y] = 0
                    return
        print(np.matrix(self.arr))

def run():
    array = [[3, 0, 6, 5, 0, 8, 4, 0, 0],
        [5, 2, 0, 0, 0, 0, 0, 0, 0],
        [0, 8, 7, 0, 0, 0, 0, 3, 1],
        [0, 0, 3, 0, 1, 0, 0, 8, 0],
        [9, 0, 0, 8, 6, 3, 0, 0, 5],
        [0, 5, 0, 0, 9, 0, 6, 0, 0],
        [1, 3, 0, 0, 0, 0, 2, 5, 0],
        [0, 0, 0, 0, 0, 0, 0, 7, 4],
        [0, 0, 5, 2, 0, 6, 3, 0, 0] ]
    # array = [[0, 0, 0, 2, 6, 0, 7, 0, 1],
    #     [6, 8, 0, 0, 7, 0, 0, 9, 0],
    #     [1, 9, 0, 0, 0, 4, 5, 0, 0],
    #     [8, 2, 0, 1, 0, 0, 0, 4, 0],
    #     [0, 0, 4, 6, 0, 2, 9, 0, 0],
    #     [0, 5, 0, 0, 0, 3, 0, 2, 8],
    #     [0, 0, 9, 3, 0, 0, 0, 7, 4],
    #     [0, 4, 0, 0, 5, 0, 0, 3, 6],
    #     [7, 0, 3, 0, 1, 8, 0, 0, 0] ]
    print("Input:")
    sod = SudokuSolver(array)
    print(np.matrix(sod.arr))
    print("Solution(s):")
    sod.solve()
    #print(np.matrix(sod.solved()))

if __name__ == "__main__":
    run()


import numpy as np

def calcXY(data_set, degree: int):
    X = np.array([])
    Y = np.array(data_set[:, 1]).reshape(-1, 1)
    for data_point in data_set[:, 0]:
        X = np.append(X, makePoly(data_point, degree))
    X = X.reshape(len(data_set), degree + 1)
    return X, Y

def calcW(x: np.ndarray, y: np.ndarray, degree: int, lamb=0):
    i = np.identity(degree + 1)
    w = np.linalg.inv((x.T.dot(x)) + (lamb * i)).dot((x.T.dot(y)))
    return w

def makePoly(x: float, p: int):
    row = np.array([])
    for i in range(0, p + 1):
        row = np.append(row, x**i)
    return row

def linPredict(row: np.ndarray, coeffs: np.ndarray):
    yHat = coeffs[0]
    for i in range(len(coeffs) - 1):
        yHat += coeffs[i + 1] * row[i]
    return yHat

def calcMSE(coeffs: np.ndarray, data_x: np.ndarray, data_y: np.ndarray,
            degree: int, count=-1):
    if count == -1:
        count = len(data_x)
    mse = 0
    for i in range(0, count):
        row = makePoly(data_x[i], degree)
        result = coeffs.T.dot(row)
        mse += ((result - data_y[i]) ** 2)
    mse /= count
    return mse

def save_splits(data: np.array, splits: int):
    chunkSize = int(round(len(data) / splits))
    np.random.shuffle(data)
    for i in range(splits):
        test_start = chunkSize * i
        test_end = test_start + chunkSize
        if i == splits - 1:
            test_start -= 1
        test = data[test_start:test_end]
        train = np.vstack((data[:test_start], data[test_end:]))
        np.savetxt('./data/CandC-train%d.csv' % i, train, delimiter=',')
        np.savetxt('./data/CandC-test%d.csv' % i, test, delimiter=',')

def fillMissingValues(data: np.array):
    col_mean = np.array([0.0 for i in range(len(data[0]))])

    for i in range(len(col_mean)):
        col = data[:, i]
        temp = np.array([])
        for item in col:
            if not np.isnan(item):
                temp = np.append(temp, item)
        col_mean[i] = np.mean(temp)
        for j in range(len(col)):
            if np.isnan(col[j]):
                col[j] = col_mean[i]
        data[:, i] = col
    np.savetxt('./data/Dataset_completed.csv', data, delimiter=',')
    return data

def load_splits():
    train_0 = np.genfromtxt('./data/CandC-train0.csv', delimiter=',')
    train_1 = np.genfromtxt('./data/CandC-train1.csv', delimiter=',')
    train_2 = np.genfromtxt('./data/CandC-train2.csv', delimiter=',')
    train_3 = np.genfromtxt('./data/CandC-train3.csv', delimiter=',')
    train_4 = np.genfromtxt('./data/CandC-train4.csv', delimiter=',')
    test_0 = np.genfromtxt('./data/CandC-test0.csv', delimiter=',')
    test_1 = np.genfromtxt('./data/CandC-test1.csv', delimiter=',')
    test_2 = np.genfromtxt('./data/CandC-test2.csv', delimiter=',')
    test_3 = np.genfromtxt('./data/CandC-test3.csv', delimiter=',')
    test_4 = np.genfromtxt('./data/CandC-test4.csv', delimiter=',')
    test = np.array([test_0, test_1, test_2, test_3, test_4])
    train = np.array([train_0, train_1, train_2, train_3, train_4])
    return train, test

def inverseSet(data: np.array, cap: int):
    out = np.array([])
    for i in range(cap):
        if not np.isin(i, data):
            out = np.append(out, i)
    return out.astype(int)


import numpy as np

from prml.kernel.kernel import Kernel

class RBF(Kernel):
    def __init__(self, params):
        """
        construct Radial basis kernel function

        Parameters
        ----------
        params : (ndim + 1,) ndarray
            parameters of radial basis function

        Attributes
        ----------
        ndim : int
            dimension of expected input data
        """
        assert params.ndim == 1
        self.params = params
        self.ndim = len(params) - 1

    def __call__(self, x, y, pairwise=True):
        """
        calculate radial basis function
        k(x, y) = c0 * exp(-0.5 * c1 * (x1 - y1) ** 2 ...)

        Parameters
        ----------
        x : ndarray [..., ndim]
            input of this kernel function
        y : ndarray [..., ndim]
            another input

        Returns
        -------
        output : ndarray
            output of this radial basis function
        """
        assert x.shape[-1] == self.ndim
        assert y.shape[-1] == self.ndim
        if pairwise:
            x, y = self._pairwise(x, y)
        d = self.params[1:] * (x - y) ** 2
        return self.params[0] * np.exp(-0.5 * np.sum(d, axis=-1))

    def derivatives(self, x, y, pairwise=True):
        if pairwise:
            x, y = self._pairwise(x, y)
        d = self.params[1:] * (x - y) ** 2
        delta = np.exp(-0.5 * np.sum(d, axis=-1))
        deltas = -0.5 * (x - y) ** 2 * (delta * self.params[0])[:, :, None]
        return np.concatenate((np.expand_dims(delta, 0), deltas.T))

    def update_parameters(self, updates):
        self.params += updates


import numpy as np
import math

#   load my  data
[x, y] = np.load('myData.npy')

#   plot my data
import matplotlib.pyplot as plt
plt.figure()
plt.plot(x,y, 'ok')
plt.grid(True)
plt.show()
plt.savefig('myData.png', dpi=300, type='png')

#   define a function to calculate the log likiehood
def calcLogLikelihood(guess, true, n):
    error = true-guess
    sigma = np.std(error)
    f = ((1.0/(2.0*math.pi*sigma*sigma))**(n/2))* \
        np.exp(-1*((np.dot(error.T,error))/(2*sigma*sigma)))
    return np.log(f)

#   define my function which will return the objective function to be minimized
def myFunction(var):
    yGuess = (var[2]*(x**2)) + (var[1]*x) + var[0]
    f = calcLogLikelihood(yGuess, y, float(len(yGuess)))
    return (-1*f)

#    Let's pick some random starting points for the optimizaiton
nvar = 3
var = np.zeros(nvar)
var[0] = -15.5
var[1] = 19.5
var[2] = -1.0

#   let's maximize the liklihood (minimize -1*max(likelihood)
from scipy.optimize import minimize
res = minimize(myFunction, var, method='BFGS',
                options={'disp': True})

#   pefrom least squres fit using scikitlearn
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
model = Pipeline([('poly', PolynomialFeatures(degree=2)),
    ('linear', LinearRegression(fit_intercept=False))])

model = model.fit(x[:, np.newaxis], y)
coefs = model.named_steps['linear'].coef_

#   plot the data and model fits
plt.figure()
plt.plot(x,y, 'ok')
plt.plot(x,model.predict(x[:,np.newaxis]), '-r', label='Least Squres')
plt.plot(x,(res.x[2]*(x**2)) + (res.x[1]*x) + res.x[0], '--b', label='Max Likelihood')
plt.grid(True)
plt.legend(loc=2)
plt.show()
plt.savefig('maxLikelihoodComp.png', dpi=300, type='png')


from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint

#Define function
def odef(xyz,t,sigma,rho,beta):
    x,y,z = xyz
    return [sigma*(y-x),x*(rho-z)-y,x*y -beta*z]

#Create data
t = np.linspace(0,25,10000)
xyz0 = [1,1,1]
sigma, rho, beta = 8, 28, 8/3

#Integrate under function
xyz = odeint(odef,xyz0,t, args=(sigma,rho,beta))

#Create figure
plt.close()
fig, ax = plt.subplots(1,1,figsize=(10,10),subplot_kw={'projection':'3d'})
ax.set_aspect('equal')
ax.plot(xyz[:,0],xyz[:,1],xyz[:,2],alpha=0.5)
ax.set_title('3D projection of $f(x,y,z)$', size=18)
ax.set_xlabel('$x$', fontsize=16)
ax.set_ylabel('$y$', fontsize=16)
ax.set_zlabel('$z$', fontsize=16)
plt.show()


from math import sqrt

from numba import njit

@njit(fastmath=True)
def is_prime(number: int) -> bool:
    if number <= 1:
        return False
    elif number < 4:
        return True
    elif number % 2 == 0:
        return False
    elif number < 9:
        return True
    elif number % 3 == 0:
        return False
    else:
        for i in range(5, int(sqrt(number)) + 1, 6):
            if number % i == 0:
                return False
            elif number % (i + 2) == 0:
                return False
        return True


import numpy as np

def VRP_and_n(VRPx, VRPy, VRPz, Px, Py, Pz):
    VRP = np.array([VRPx, VRPy, VRPz])
    N = VRP-np.array([Px, Py, Pz])
    n = N/(np.linalg.norm(N))

    return VRP,n

def SRC_matrix(VRP, n, Yx, Yy, Yz):
    """Returns the transformation matrix to change the prism from SRU coordinates
    to SRC.
    Args: VRP coordinates, P coordinates, View-up (Y) coordinates"""

    Y = np.array([Yx, Yy, Yz])
    V = Y-np.dot(np.dot(Y,n),n)
    v = V/(np.linalg.norm(V))
    SRC = np.eye(4)
    u = np.cross(v,n)
    SRC[0,:3] = u
    SRC[1,:3] = v
    SRC[2,:3] = n
    SRC[0,3] = -np.dot(VRP,u)
    SRC[1,3] = -np.dot(VRP,v)
    SRC[2,3] = -np.dot(VRP,n)

    return SRC

def jp_times_proj_matrix(is_perspectiva, dist_projecao,
    Xmin, Xmax, Ymin, Ymax, Umax, Umin, Vmax, Vmin):
    """Returns a matrix used to transform an object from SRC to SRT."""

    u_by_x = (Umax-Umin)/(Xmax-Xmin)
    v_by_y = (Vmax-Vmin)/(Ymax-Ymin)

    if is_perspectiva:
        minus_inverted_dp = -(1/dist_projecao)
        jp_times_proj = np.zeros((4,4))
        jp_times_proj[0,0] = u_by_x
        jp_times_proj[1,1] = -v_by_y
        jp_times_proj[0,2] = (-(Xmin*u_by_x)+Umin)*minus_inverted_dp
        jp_times_proj[1,2] = ((Ymin*v_by_y)+Vmax)*minus_inverted_dp
        jp_times_proj[2,2] = 1
        jp_times_proj[3,2] = minus_inverted_dp

        return jp_times_proj
    else:
        jp = np.eye(4)
        jp[0,0] = u_by_x
        jp[1,1] = -v_by_y
        jp[0,3] = -(Xmin*u_by_x)+Umin
        jp[1,3] = (Ymin*v_by_y)+Vmax

        return jp

def pipeline_steps(M, SRC_matrix, jp_proj_matrix, dist_near, dist_far):
    """
    Returns a Boolean value determining if the object should be drawn
    and a numpy array for the prism after the application of the pipeline.
    Args:
    M is the numpy array for the prism to be drawn.
    SRC_matrix and jp_proj_matrix are the matrices returned from the first_pipeline function.
    dist_near and dist_fear determine the Z limits of the scene.
    """
    M_in_SRC = np.dot(SRC_matrix, M)

    draw = np.all(np.logical_and(
                    np.less(M_in_SRC[2,:],-dist_near),
                    np.greater(M_in_SRC[2,:],-dist_far)
                ))

    if not draw:
        return draw, np.zeros((4,1))
    else:
        pipelinedM = np.dot(jp_proj_matrix, M_in_SRC)

        pipelinedM[0,:] /= pipelinedM[3,:]
        pipelinedM[1,:] /= pipelinedM[3,:]
        pipelinedM[3,:] /= pipelinedM[3,:]

        return draw, pipelinedM

def first_pipeline(VRP, n, Yx, Yy, Yz,
    is_perspectiva, dist_projecao,
    Xmin, Xmax, Ymin, Ymax, Umin, Umax, Vmin, Vmax):
    """Returns matrices that shall be used in the pipeline based on all the parameters necessary
    for the SRU to SRT conversion, except the 3D cut step."""

    SRC = SRC_matrix(VRP, n, Yx, Yy, Yz)
    jp_times_proj = jp_times_proj_matrix(is_perspectiva, dist_projecao,
    Xmin, Xmax, Ymin, Ymax, Umax, Umin, Vmax, Vmin)

    return SRC, jp_times_proj


import numpy as np
import scipy.optimize

def sinfit(tt, yy):
    '''Fit sin to the input time sequence, and return fitting parameters "amp", "omega", "phase", "offset", "freq", "period" and "fitfunc"'''
    tt = np.array(tt)
    yy = np.array(yy)
    ff = np.fft.fftfreq(len(tt), (tt[1]-tt[0]))   # assume uniform spacing
    Fyy = abs(np.fft.fft(yy))
    guess_freq = abs(ff[np.argmax(Fyy[1:])+1])   # excluding the zero frequency "peak"
    guess_amp = np.std(yy) * 2.**0.5
    guess_offset = np.mean(yy)
    guess = np.array([guess_amp, 2.*np.pi*guess_freq, 0., guess_offset])

    def sinfunc(t, A, w, p, c):  return A * np.sin(w*t + p) + c
    popt, pcov = scipy.optimize.curve_fit(sinfunc, tt, yy, p0=guess)
    A, w, p, c = popt
    f = w/(2.*np.pi)
    fitfunc = lambda t: A * np.sin(w*t + p) + c
    return {"amp": A, "omega": w, "phase": p, "offset": c, "freq": f, "period": 1./f, "fitfunc": fitfunc, "maxcov": np.max(pcov), "rawres": (guess,popt,pcov)}

'''
   Parameter-free fitting function. Uses an fft to obtain an initial guess, refines fit via scippy's linear algebra fitting tool
'''


#Align a tomography tilt series using cross correlation
#
#
#developed as part of the tomviz project (www.tomviz.com)

def transform_scalars(dataset):

    #----USER SPECIFIED VARIABLES-----#
    TILT_AXIS  = []  #Specify Tilt Axis Dimensions x=0, y=1, z=2
    #---------------------------------#

    from tomviz import utils
    import numpy as np

    data_py = utils.get_array(dataset) #get data as numpy array

    if data_py is None: #Check if data exists
        raise RuntimeError("No data array found!")

    if TILT_AXIS == []: #If tilt axis is not given, find it
    #Find smallest array dimension, assume it is the tilt angle axis
        if data_py.ndim == 3:
            TILT_AXIS = np.argmin( data_py.shape )
        elif data_py.ndim == 2:
            raise RuntimeError("Data Array is 2 dimensions, it should be 3!")
        else:
            raise RuntimeError("Data Array is not 2 or 3 dimensions!")

    print('Aligning Images by Cross Correlation')
    for i in range(1,np.size(data_py,TILT_AXIS)):#Align image to previous
        if TILT_AXIS == 2:
            im0 = np.fft.fft2(data_py[:,:,i-1])
            im1 = np.fft.fft2(data_py[:,:,i])
            xcor = abs(np.fft.ifft2((im0 * im1.conjugate())))
            shift = np.unravel_index(xcor.argmax(), xcor.shape)
            print( shift )
            data_py[:,:,i] = np.roll( data_py[:,:,i], shift[0], axis = 0)
            data_py[:,:,i] = np.roll( data_py[:,:,i], shift[1], axis = 1)
        elif TILT_AXIS == 1:
            im0 = np.fft.fft2(data_py[:,i-1,:])
            im1 = np.fft.fft2(data_py[:,i,:])
            xcor = abs(np.fft.ifft2((im0 * im1.conjugate())))
            print( np.amax(xcor) )
            shift = np.unravel_index(xcor.argmax(), xcor.shape)
            print( shift )
            data_py[:,i,:] = np.roll( data_py[:,i,:], shift[0], axis = 0)
            data_py[:,i,:] = np.roll( data_py[:,i,:], shift[2], axis = 2)
        elif TILT_AXIS == 0:
            im0 = np.fft.fft2(data_py[i-1,:,:])
            im1 = np.fft.fft2(data_py[i,:,:])
            xcor = abs(np.fft.ifft2((im0 * im1.conjugate())))
            print( np.amax(xcor) )
            shift = np.unravel_index(xcor.argmax(), xcor.shape)
            print( shift )
            data_py[i,:,:] = np.roll( data_py[i,:,:], shift[1], axis = 1)
            data_py[i,:,:] = np.roll( data_py[i,:,:], shift[2], axis = 2)
        else:
            raise RuntimeError("Python Transform Error: Unknown TILT_AXIS.")

    utils.set_array(dataset, data_py)
    print('Align Images Complete')


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

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

# stabilni softmax
def stable_softmax(x):
    max_by_row = np.max(x, axis=1).reshape((-1, 1))
    exp_x_shifted = np.exp(x - max_by_row)
    exp_sums = np.sum(exp_x_shifted, axis=1).reshape((-1, 1))
    probs = exp_x_shifted / exp_sums
    return probs

def one_hot_encode(Y, C):
    Y_encoded = []
    for y in Y:
        y_encoded = np.zeros(C)
        y_encoded[y] = 1
        Y_encoded.append(y_encoded)

    return Y_encoded

def logreg_train(X, Y_, param_niter=100_000, param_delta=0.1, print_frequency=500):
    C = int(np.max(Y_)) + 1
    N = len(X)
    n = len(X[0])
    W = np.random.randn(n, C)
    b = np.zeros((1, C)) # 1 x C
    Y_one_hot_encoded = one_hot_encode(Y_, C)

    for i in range(param_niter):
        scores = (X @ W) + b # N x C
        probs = stable_softmax(scores)
        logprobs = np.log(probs) # N x C
        # dijagnostički ispis
        if i % print_frequency == 0:
            loss = - np.sum(logprobs * Y_one_hot_encoded)  # scalar
            print(f"iteration {i}: loss {loss}")

        # derivacije komponenata gubitka po mjerama
        dL_ds = probs - Y_one_hot_encoded  # N x C
        # gradijenti parametara
        grad_W = np.transpose((1 / N) * np.transpose(dL_ds) @ X) # C x D (ili D x C)
        grad_b = (1 / N) * np.sum(dL_ds, axis=0) # C x 1 (ili 1 x C)
        # poboljšani parametri
        W -= param_delta * grad_W
        b -= param_delta * grad_b

    return W, b

def logreg_classify(X, W, b):
    return stable_softmax(X @ W + b)

def logreg_decfun(X, W, b):
    """
    Decorator for  method logreg_classify
    """

    def classify(X):
        return logreg_classify(X, W, b).argmax(axis=1)

    return classify

def sample_gauss_2d(C, N):
    """
    Method for instantiating data

    Arguments
    C - number of classes
    N - number of samples in each class

    Return Values
    x - array of features NxC
    Y - array of labels   Nx1
    """
    x = []
    Y = []
    for i in range(0, C):
        G = data.Random2DGaussian()
        samples = G.get_sample(N)
        x.append(samples.tolist())
        Y.append([i for j in range(N)])
    x = np.reshape(x, (N * C, 2))
    Y = np.reshape(Y, (N * C, 1))

    return x, Y

if __name__ == "__main__":
    np.random.seed(100)
    # get the training dataset
    X, Y_ = sample_gauss_2d(3, 100)
    W, b = logreg_train(X, Y_)
    # evaluate the model on the training dataset
    probs = logreg_classify(X, W, b)
    print(probs.shape)
    # predicted classes
    Y = np.hstack([np.argmax(probs[i][:]) for i in range(probs.shape[0])])
    # reshaping for other methods purposes
    Y_ = np.hstack(Y_)
    print(W)
    print(b)
    accuracy, pr, M = data.eval_perf_multi(Y, Y_)
    print("Accuracy: ", accuracy)
    print("Precision / Recall: ", pr)
    print("Confussion Matrix: ", M)
    # graph the decision surface
    decfun = logreg_decfun(X, W, b)
    bbox = (np.min(X, axis=0), np.max(X, axis=0))
    data.graph_surface(decfun, bbox, offset=0.5)
    # graph the data points
    data.graph_data(X, Y_, Y, special=[])
    # show the plot
    plt.show()


import numpy as np
import numba as nb

@nb.vectorize
def BoundPhi(phi):
    if phi >= np.pi:
        phi -= 2*np.pi
    elif phi < -np.pi:
        phi += 2*np.pi
    return phi

@nb.njit
def DeltaR2(deta, dphi):
    return deta**2 + BoundPhi(dphi)**2

@nb.njit
def RadToCart2D(r, phi):
    return r*np.cos(phi), r*np.sin(phi)

@nb.njit
def CartToRad2D(x, y):
    return np.sqrt(x**2+y**2), BoundPhi(np.arctan2(y, x))

@nb.njit
def PartCoorToCart3D(pt, eta, phi):
    x, y = RadToCart2D(pt, phi)
    z = pt*np.sinh(eta)
    return x, y, z

@nb.njit
def CartToPartCoor3D(x, y, z):
    pt, phi = CartToRad2D(x, y)
    eta = np.arctanh(z/np.sqrt(z**2+pt**2))
    return pt, eta, phi

@nb.njit
def LorTHPMToXYZE(t, h, p, m):
    x = t*np.cos(p)
    y = t*np.sin(p)
    z = t*np.sinh(h)
    e = np.sqrt(m**2 + t**2 + z**2)
    return x, y, z, e

@nb.njit
def LorXYZEToTHPM(x, y, z, e):
    t = np.sqrt(x**2+y**2)
    h = np.arctanh(z/np.sqrt(t**2+z**2)) if z!=0. else 0.
    p = BoundPhi(np.arctan2(y, x))
    m2 = e**2 - t**2 - z**2
    m = np.sign(m2) * np.sqrt(abs(m2))
    return t, h, p, m


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import sklearn.linear_model
import sklearn.preprocessing
import sklearn.model_selection
import itertools

# datos iniciales
data = pd.read_csv('Cars93.csv')

# columnas de X
columns = ['MPG.city', 'MPG.highway', 'EngineSize', 'Horsepower',
           'RPM', 'Rev.per.mile', 'Fuel.tank.capacity', 'Length',
           'Width', 'Turn.circle', 'Weight']

# define X, Y
Y = np.array(data['Price'])
X = np.array(data[columns])

# standarize
scaler = sklearn.preprocessing.StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)

# split en training y test
X_train, X_test, Y_train, Y_test = sklearn.model_selection.train_test_split(X_scaled, Y, test_size=0.5)

# inicializa clase de regresion lineal
regresion = sklearn.linear_model.LinearRegression()

# para guardar resultados
scores = []
predictores = []
n_predict = []
betas = []

# array de indices que representan las columnas
indices = np.array(np.arange(len(columns)))

# iteracion sobre el numero de indices para utilizas
for n_predictores in range(1,len(indices)+1):
    # todas las combinaciones posibles
    perm = itertools.combinations(indices, n_predictores)
    for p in perm:
        c = list(p) # estos son los indices de las columnas que voy a utilizar

        # Entrena solamen con las columnas que devolvio itertools
        regresion.fit(X_train[:, c], Y_train)
        # Guarda los betas correspondientes
        betas.append(regresion.coef_)
        # Guarda el score correspondiente
        score = regresion.score(X_test[:,c], Y_test)
        scores.append(score)
        # guarda el numero de predictores utilizados
        n_predict.append(n_predictores)
        # guarda los indices de las columas utilizadas
        predictores.append(c)

# Primera grafica
plt.figure()
plt.scatter(n_predict, scores)
plt.xlabel("Numero de predictores")
plt.ylabel("R2")
plt.savefig("nparams.png")

print("")
print("Best model (Regresion Lineal)")
print("")
best = np.argmax(scores)
predict = predictores[best]
beta = betas[best]
ii = np.argsort(np.abs(beta))
for i in ii:
    print(columns[predict[i]], beta[i])

# numbero de valores de alpha para probar
n_alpha = 100
alpha = np.logspace(-3, 1, n_alpha)
scores = []
betas = []

for i, a in enumerate(alpha):
    lasso = sklearn.linear_model.Lasso(alpha=a)
    lasso.fit(X_train, Y_train)
    scores.append(lasso.score(X_test, Y_test))
    betas.append(lasso.coef_)

plt.figure()
plt.plot(np.log10(alpha), scores)
plt.xlabel("log alpha")
plt.ylabel("R2")
plt.savefig("lasso.png")

print("")
print("Best model (LASSO)")
print("")
best = np.argmax(scores)
beta = betas[best]
ii = np.argsort(np.abs(beta))
for i in ii:
    if(abs(beta[i])>0):
        print(columns[i], beta[i])


import math
import scipy.integrate as integrate
from tabulate import tabulate
import functools

@functools.lru_cache
def w_sum(f, a, n, h):
    sum = 0

    for i in range(1, n):
        sum += f(a + i * h)

    return sum

@functools.lru_cache
def q_sum(f, a, n, h):
    sum = 0

    for i in range(n):
        sum += f(a + h / 2 + i * h)

    return sum

@functools.lru_cache
def z_sum(f, a, n, h):
    return f(a) + f(a + n * h)

# АСТ 0
def qf_left_rect(f, a, n, h):
    return h * (f(a) + w_sum(f, a, n, h))

# АСТ 0
def qf_right_rect(f, a, n, h):
    return h * (w_sum(f, a, n, h) + f(a + h * n))

# АСТ 1
def qf_inter_rect(f, a, n, h):
    return h * q_sum(f, a, n, h)

# АСТ 1
def qf_trap(f, a, n, h):
    return h / 2 * (z_sum(f, a, n, h) + 2 * w_sum(f, a, n, h))

# АСТ 3
def qf_simp(f, a, n, h):
    return h / 6 * (z_sum(f, a, n, h) + 2 * w_sum(f, a, n, h) + 4 * q_sum(f, a, n, h))

def quad_f(f, a, b):
    integral, _ = integrate.quad(f, a, b)
    return integral

# First derrivative of x ** 2 / (1 + x ** 2)
def df(x):
    return 2 * x / (1 + x ** 2) ** 2

# Second derrivative of x ** 2 / (1 + x ** 2)
def d2f(x):
    return (
        (8 * (x ** 2) / ((1 + x ** 2) ** 3) - 2 / ((1 + x ** 2) ** 2)) * (x ** 2)
        - (8 * (x ** 2)) / ((1 + x ** 2) ** 2)
        + 2 / (1 + x ** 2)
    )

# Fourth derrivative of x ** 2 / (1 + x ** 2)
def d4f(x):
    return (
        12 * ((8 * x ** 2) / (x ** 2 + 1) ** 3 - 2 / (x ** 2 + 1) ** 2)
        + (
            -(288 * x ** 2) / (x ** 2 + 1) ** 4
            + 24 / (x ** 2 + 1) ** 3
            + (384 * x ** 4) / (x ** 2 + 1) ** 5
        )
        * x ** 2
        + 8 * ((24 * x) / (x ** 2 + 1) ** 3 - (48 * x ** 3) / (x ** 2 + 1) ** 4) * x
    )

def find_max(f, a, n, h):
    m = 0
    for i in range(n + 1):
        m = max(m, abs(f(a + h * i)))
    return m

def theor_err(qf, a, b, n, h):
    if qf.__name__ == "qf_left_rect" or qf.__name__ == "qf_right_rect":
        m = find_max(df, a, n, h) * (b - a)
        return 1 / 2 * m * (b - a) * h

    elif qf.__name__ == "qf_inter_rect":
        m = find_max(d2f, a, n, h) * (b - a)
        return 1 / 24 * m * (b - a) * h ** 2

    elif qf.__name__ == "qf_trap":
        m = find_max(d2f, a, n, h) * (b - a)
        return 1 / 12 * m * (b - a) * h ** 2

    else:
        m = find_max(d4f, a, n, h) * (b - a)
        return 1 / 2880 * m * (b - a) * h ** 4

functions = [
    (lambda x: x * x / (1 + x * x), "x ** 2 / (1 + x ** 2)"),
    (lambda x: x * 0 + 1, "1"),
    (lambda x: x * 2, "2x"),
    (lambda x: x ** 2 * 3, "3x ** 2"),
    (lambda x: x ** 3 * 4, "4x ** 3"),
    (lambda x: math.e ** ((-x) ** 2), "e ** (-x ** 2)"),
]

quadr_formulas = [qf_left_rect, qf_right_rect, qf_inter_rect, qf_trap, qf_simp]

if __name__ == "__main__":

    def main_loop():

        while True:
            a = float(input("Введите a: "))
            b = float(input("Введите b: "))
            n = int(input("Введите число разбиений промежутка интегрирования: "))

            h = (b - a) / n
            print(f"h = (b-a)/m = {h}")

            for f, f_name in functions:
                corr_integral = quad_f(f, a, b)
                appr_integrals = [qf(f, a, n, h) for qf in quadr_formulas]
                errs = [abs(y - corr_integral) for y in appr_integrals]

                if f_name == "x ** 2 / (1 + x ** 2)":
                    theor_errs = [theor_err(qf, a, b, n, h) for qf in quadr_formulas]
                else:
                    theor_errs = ["-" for _ in range(len(quadr_formulas))]

                qf_names = [qf.__name__ for qf in quadr_formulas]

                print(f"\nf(x) = {f_name}")
                print(f"Exact integral: {corr_integral}\n")
                print(
                    tabulate(
                        [
                            (qf_name, y, err, terr)
                            for qf_name, y, err, terr in zip(
                                qf_names, appr_integrals, errs, theor_errs
                            )
                        ],
                        headers=["quad_formula", "appr", "err", "theor_err"],
                        floatfmt=".10f",
                    )
                )
                print()

            if input("\nПродолжить с новыми a, b, m? (y, n): ") == "y":
                continue
            else:
                break

    print(
        f"""Задание 4_2. Приближенное вычисление интеграла по составным квадратурным формулам
---------------------------------------------------------------------"""
    )
    main_loop()


# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
# Author(s):       Hind Montassif
#
# Copyright (C) 2021 Inria
#
# Modification(s):
#   - YYYY/MM Author: Description of the modification

import numpy as np

from ._points import ctorus
from ._points import sphere

def _generate_random_points_on_torus(n_samples, dim):

    # Generate random angles of size n_samples*dim
    alpha = 2*np.pi*np.random.rand(n_samples*dim)

    # Based on angles, construct points of size n_samples*dim on a circle and reshape the result in a n_samples*2*dim array
    array_points = np.column_stack([np.cos(alpha), np.sin(alpha)]).reshape(-1, 2*dim)

    return array_points

def _generate_grid_points_on_torus(n_samples, dim):

    # Generate points on a dim-torus as a grid
    n_samples_grid = int((n_samples+.5)**(1./dim)) # add .5 to avoid rounding down with numerical approximations
    alpha = np.linspace(0, 2*np.pi, n_samples_grid, endpoint=False)

    array_points = np.column_stack([np.cos(alpha), np.sin(alpha)])
    array_points_idx = np.empty([n_samples_grid]*dim + [dim], dtype=int)
    for i, x in enumerate(np.ix_(*([np.arange(n_samples_grid)]*dim))):
        array_points_idx[...,i] = x
    return array_points[array_points_idx].reshape(-1, 2*dim)

def torus(n_samples, dim, sample='random'):
    """
    Generate points on a flat dim-torus in R^2dim either randomly or on a grid

    :param n_samples: The number of points to be generated.
    :param dim: The dimension of the torus on which points would be generated in R^2*dim.
    :param sample: The sample type of the generated points. Can be 'random' or 'grid'.
    :returns: numpy array containing the generated points on a torus.
        The shape of returned numpy array is:
        if sample is 'random' : (n_samples, 2*dim).
        if sample is 'grid' : (⌊n_samples**(1./dim)⌋**dim, 2*dim), where shape[0] is rounded down to the closest perfect 'dim'th power.
    """
    if sample == 'random':
        # Generate points randomly
        return _generate_random_points_on_torus(n_samples, dim)
    elif sample == 'grid':
        # Generate points on a grid
        return _generate_grid_points_on_torus(n_samples, dim)
    else:
        raise ValueError("Sample type '{}' is not supported".format(sample))


import autograd.numpy as np
import tensorflow as tf
import torch
from numpy import linalg as la
from numpy import random as rnd

import pymanopt
from examples._tools import ExampleRunner
from pymanopt.manifolds import Sphere
from pymanopt.solvers import SteepestDescent

SUPPORTED_BACKENDS = ("Autograd", "Callable", "PyTorch", "TensorFlow")

def create_cost_egrad(manifold, matrix, backend):
    egrad = None

    if backend == "Autograd":

        @pymanopt.function.Autograd(manifold)
        def cost(x):
            return -np.inner(x, matrix @ x)

    elif backend == "Callable":

        @pymanopt.function.Callable(manifold)
        def cost(x):
            return -np.inner(x, matrix @ x)

        @pymanopt.function.Callable(manifold)
        def egrad(x):
            return -2 * matrix @ x

    elif backend == "PyTorch":
        matrix_ = torch.from_numpy(matrix)

        @pymanopt.function.PyTorch(manifold)
        def cost(x):
            return -torch.matmul(x, torch.matmul(matrix_, x))

    elif backend == "TensorFlow":

        @pymanopt.function.TensorFlow(manifold)
        def cost(x):
            return -tf.tensordot(x, tf.tensordot(matrix, x, axes=1), axes=1)

    else:
        raise ValueError(f"Unsupported backend '{backend}'")

    return cost, egrad

def run(backend=SUPPORTED_BACKENDS[0], quiet=True):
    n = 128
    matrix = rnd.randn(n, n)
    matrix = 0.5 * (matrix + matrix.T)

    manifold = Sphere(n)
    cost, egrad = create_cost_egrad(manifold, matrix, backend)
    problem = pymanopt.Problem(manifold, cost=cost, egrad=egrad)
    if quiet:
        problem.verbosity = 0

    solver = SteepestDescent()
    estimated_dominant_eigenvector = solver.solve(problem)

    if quiet:
        return

    # Calculate the actual solution by a conventional eigenvalue decomposition.
    eigenvalues, eigenvectors = la.eig(matrix)
    dominant_eigenvector = eigenvectors[:, np.argmax(eigenvalues)]

    # Make sure both vectors have the same direction. Both are valid
    # eigenvectors, but for comparison we need to get rid of the sign
    # ambiguity.
    if np.sign(dominant_eigenvector[0]) != np.sign(
        estimated_dominant_eigenvector[0]
    ):
        estimated_dominant_eigenvector = -estimated_dominant_eigenvector

    # Print information about the solution.
    print("l2-norm of x:", la.norm(dominant_eigenvector))
    print("l2-norm of xopt:", la.norm(estimated_dominant_eigenvector))
    print(
        "Solution found:",
        np.allclose(
            dominant_eigenvector, estimated_dominant_eigenvector, atol=1e-6
        ),
    )
    error_norm = la.norm(dominant_eigenvector - estimated_dominant_eigenvector)
    print("l2-error:", error_norm)

if __name__ == "__main__":
    runner = ExampleRunner(
        run, "Dominant eigenvector of a PSD matrix", SUPPORTED_BACKENDS
    )
    runner.run()


#!/usr/bin/env python3
"""
Bayesian Probability
"""
from scipy import math, special

def posterior(x, n, p1, p2):
    """
    Calculates the posterior probability that the probability of developing
    severe side effects falls within a specific range given the data:

    x is the number of patients that develop severe side effects
    n is the total number of patients observed
    p1 is the lower bound on the range
    p2 is the upper bound on the range
    You can assume the prior beliefs of p follow a uniform distribution
    If n is not a positive integer, raise a ValueError with the message n must
    be a positive integer
    If x is not an integer that is greater than or equal to 0, raise a
    ValueError with the message x must be an integer that is greater than or
    equal to 0
    If x is greater than n, raise a ValueError with the message x cannot be
    greater than n
    If p1 or p2 are not floats within the range [0, 1], raise a ValueError with
    the message {p} must be a float in the range [0, 1] where {p} is the
    corresponding variable
    if p2 <= p1, raise a ValueError with the message p2 must be greater than p1
    The only import you are allowed to use is from scipy import special

    Returns: the posterior probability that p is within the range [p1, p2]
    given x and n
    """
    if not isinstance(n, int) or n <= 0:
        raise ValueError("n must be a positive integer")
    if not isinstance(x, (int, float)) or (x < 0):
        mg = "x must be an integer that is greater than or equal to 0"
        raise ValueError(mg)
    if x > n:
        raise ValueError("x cannot be greater than n")
    if not isinstance(p1, float) or p1 < 0 or p1 > 1:
        raise ValueError("p1 must be a float in the range [0, 1]")
    if not isinstance(p2, float) or p2 < 0 or p2 > 1:
        raise ValueError("p2 must be a float in the range [0, 1]")
    if p2 <= p1:
        raise ValueError("p2 must be greater than p1")

    num = (math.factorial(n+1))
    den = (math.factorial(x) * math.factorial(n - x))
    fct = num / den
    hp1 = special.hyp2f1(x+1, x-n, x+2, p1)
    hp2 = special.hyp2f1(x+1, x-n, x+2, p2)

    return fct * (((p2**(x+1))*hp2)-((p1**(x+1))*hp1))/(x+1)


"""

https://en.wikipedia.org/wiki/Pseudovector
https://en.wikipedia.org/wiki/Improper_rotation

"""

import numpy as np
from scipy.stats import special_ortho_group

def rotate_vectors(R, v1, v2, v3, muldet):
    d = np.linalg.det(R)
    v1p = np.dot(R, v1)
    v2p = np.dot(R, v2)
    v3p = np.dot(R, v3)
    if muldet:
        v3p *= d
    vcp = np.cross(v1p, v2p)
    return (d, v1p, v2p, v3p, vcp)

"""

A pseudovector (axial vector) is a quantity that transforms like a vector
under proper rotation but under improper rotation, the sign flips

An improper rotation is a reflection/mirror, mathematically it is a
rotation matrix but has a determinant of -1

A cross product of 2 vectors cross(a, b) gives a pseudovector c and a and b are
considered to be polar vectors

Putting the above together, this means that if we have two vectors a and b
and we take the cross product of them like so:
c = cross(a, b)
a' = R*a
b' = R*b
c' = R*c

if R was a proper rotation matrix (det(R)=1) then
cross(a', b') = c'

however if R was a improper rotation matrix (det(R)=-1) then
cross(a', b') = -c'

The way to fix this is to account for the determinant of the rotation matrix for the pseudovector
a' = R*a
b' = R*b
c' = det(R)*R*c

Here cross(a', b') = c' regardless of whether or not the rotation is proper or improper because
the det(A) will be -1 for improper rotation and fixes the sign for the pseudovector

"""

def test_random_vector_rotation():
    u = np.random.randint(1, high=3)
    v = np.random.randint(1, high=3)
    if u == v:
        v = (v + 1) % 3

    # generate a rotation matrix
    # then make another rotation matrix that
    # has a negative determinant by swapping the rows
    R = special_ortho_group.rvs(3)
    Ri = R.copy()
    Ri[[u, v]] = R[[v, u]]

    v1 = np.random.rand(3)
    v2 = np.random.rand(3)
    v3 = np.cross(v1, v2)

    # do a proper rotation, improper rotation, and a improper with the determinant sign accounted for
    # the improper rotation without the determinant sign accounted for should give a negative cross product
    # for the new transformed vector v1 and v2
    d, v1r, v2r, v3r, vcr = rotate_vectors(R, v1, v2, v3, False)
    di, v1ri, v2ri, v3ri, vcri = rotate_vectors(Ri, v1, v2, v3, False)
    _, v1pri, v2pri, v3pri, vcpri = rotate_vectors(Ri, v1, v2, v3, True)

    print("Rotation Matrices")
    print(R)
    print("det={% .4f}" % (d))
    print(Ri)
    print("det={% .4f}" % (di))
    print()
    print("Original Vectors")
    print(v1)
    print(v2)
    print(v3)
    print()
    print("Proper Rotation of Vectors")
    print(v1r)
    print(v2r)
    print(v3r)
    print(vcr)
    print()
    print("Improper Rotation of Vectors")
    print(v1ri)
    print(v2ri)
    print(v3ri)
    print(vcri)
    print()
    print("Improper Rotation of Vectors with Determinant Accounted")
    print(v1pri)
    print(v2pri)
    print(v3pri)
    print(vcpri)

"""

A negative scale value applied to one axis can be thought of a reflection over that one axis.
If we apply a uniform negative scale to all the axis, it can be thought of a sequence of reflections.
Reflections can become rotations depending on how many times we apply it.

A negative uniform scaling can be characterized as the following transformation:

A rotation in even dimensions (2D, 4D, etc)
A reflection in odd dimensions (3D, 5D, etc)

We can see this by taking the determinant of the scale matrix and look at the sign to see if the orientation flipped.
For even dimension, we have an even number of negative number multiplication so it is always positive. (rotation)
For odd dimension, we have an odd number of negative number multiplication so it is always negative. (reflection)

"""

def test_negative_scale():
    s = np.random.randint(-1000, high=-1)
    for i in range(10):
        print("Negative Scale of {}x{}".format(i+1, i+1))
        S = s*np.eye(i+1)
        print(np.linalg.det(S))
        print(S)

test_random_vector_rotation()
test_negative_scale()


import numpy as np
import csv
import math

def transform_from_pose(pose): #orientation in degree
    translation = np.asarray([pose[0],pose[1],pose[2]]).reshape(3,1)
    R = RPYeulerAnglesToRotationMatrix([np.deg2rad(pose[3]),np.deg2rad(pose[4]),np.deg2rad(pose[5])])
    T_temp = np.hstack((R,translation))
    T4 = [0.0, 0.0, 0.0, 1.0]
    T = np.vstack((T_temp,T4))

    return T

def transform_from_translation_only(translation):
    #translation in millimeter
    x = translation[0]
    y = translation[1]
    z = translation[2]
    T1 = [1.0, 0.0, 0.0, x]
    T2 = [0.0, 1.0, 0.0, y]
    T3 = [0.0, 0.0, 1.0, z]
    T4 = [0.0, 0.0, 0.0, 1.0]
    T = np.array(np.vstack((T1,T2,T3,T4)))

    return T

def transformation_matrix_to_rotation(T,key): #from transformation matrix (or rotation matrix)to Euler angles or Quaternions
    c_11 = T[0][0]
    c_12 = T[0][1]
    c_13 = T[0][2]

    c_21 = T[1][0]
    c_22 = T[1][1]
    c_23 = T[1][2]

    c_31 = T[2][0]
    c_32 = T[2][1]
    c_33 = T[2][2]

    if key == 'XYZ_euler_angles': #return in rad
        Rx = np.arctan2(-c_23,c_33)
        Ry = np.arctan2(c_13,np.sqrt(c_11**2 + c_12**2))
        Rz = np.arctan2(-c_12,c_11)
        rotation = [Rx,Ry,Rz]
    elif key == 'unit_quaternion':
        tr = c_11 + c_22 + c_33
        #print("tr: ",tr)
        if tr > 0:
            #print("case:1")
            S = np.sqrt(tr + 1.0)*2.0
            q_0 = 0.25*S #0.5*np.sqrt(tr + 1) #qw
            q_1 = (c_32 - c_23)/S #0.5*np.sign(c_32 - c_23)*np.sqrt(c_11 - c_22 - c_33 + 1) #qx
            q_2 = (c_13 - c_31)/S #0.5*np.sign(c_13 - c_31)*np.sqrt(c_22 - c_33 - c_11 + 1) #qy
            q_3 = (c_21 - c_12)/S # 0.5*np.sign(c_21 - c_12)*np.sqrt(c_33 - c_11 - c_22 + 1) #qz

            #q_0_ = 0.5*np.sqrt(tr + 1)
            #q_1_ = 0.5*np.sign(c_32 - c_23)*np.sqrt(c_11 - c_22 - c_33 + 1)
            #q_2_ = 0.5*np.sign(c_13 - c_31)*np.sqrt(c_22 - c_33 - c_11 + 1)
            #q_3_ = 0.5*np.sign(c_21 - c_12)*np.sqrt(c_33 - c_11 - c_22 + 1)

            #print("q0: ",q_0, " ",q_0_)
            #print("q1: ",q_1, " ",q_1_)
            #print("q2: ",q_2, " ",q_2_)
            #print("q3: ",q_3, " ",q_3_)
        elif (c_11 > c_22) and (c_11 > c_33):
            #print("case:2")
            S = np.sqrt(1.0 + c_11 - c_22 - c_33) * 2.0 #S = 4*qx
            q_0 = (c_32 - c_23)/S
            q_1 = 0.25*S
            q_2 = (c_12 + c_21)/S
            q_3 = (c_13 + c_31)/S
        elif c_22 > c_33:
            #print("case:3")
            S = np.sqrt(1.0 + c_22 - c_11 - c_33) * 2.0 #S = 4*qy
            q_0 = (c_13 - c_31)/S
            q_1 = (c_12 + c_21)/S
            q_2 = 0.25*S
            q_3 = (c_23 + c_32)/S
        else:
            #print("case:4")
            S = np.sqrt(1.0 + c_33 - c_11 - c_22) * 2.0 #S = 4*qz
            q_0 = (c_21 - c_12)/S
            q_1 = (c_13 + c_31)/S
            q_2 = (c_23 + c_32)/S
            q_3 = 0.25*S
    # https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
    # https://math.stackexchange.com/questions/893984/conversion-of-rotation-matrix-to-quaternion
    # https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
        rotation = [q_0,q_1,q_2,q_3]
    else:
        print("unidentified key")
        rotation  = [0,0,0,0]

    return rotation

#def RotationMatrixToQuaternions(R) : #rotation matrix to q0,q1,q2,q3

#    tr = m00 + m11 + m22

def transform_from_MDH_parameters_analytical(alpha,beta,a,d,theta):
    #alpha, theta, beta: in radian
	cos_theta = np.cos(theta)
	sin_theta = np.sin(theta)

	cos_alpha = np.cos(alpha)
	sin_alpha = np.sin(alpha)

	T1 = [cos_theta, -sin_theta, 0.0, a]
	T2 = [cos_alpha*sin_theta, cos_alpha*cos_theta, -sin_alpha, -d*sin_alpha]
	T3 = [sin_alpha*sin_theta, sin_alpha*cos_theta, cos_alpha, d*cos_alpha]
	T4 = [0.0,0.0,0.0,1.0]
	T = np.array(np.vstack((T1,T2,T3,T4)))

	return T

def transform_from_DH_parameters_analytical(alpha,beta,a,d,theta):

	cos_theta = np.cos(theta)
	sin_theta = np.sin(theta)

	cos_alpha = np.cos(alpha)
	sin_alpha = np.sin(alpha)

	T1 = [cos_theta, -sin_theta*cos_alpha, sin_theta*sin_alpha, a*cos_theta]
	T2 = [sin_theta, cos_theta*cos_alpha, -cos_theta*sin_alpha, a*sin_theta]
	T3 = [0        , sin_alpha,            cos_alpha, d]
	T4 = [0.0,0.0,0.0,1.0]
	T = np.array(np.vstack((T1,T2,T3,T4)))

	return T

def transform_from_MDH_parameters_numerical(alpha,beta,a,d,theta):

    Rx_alpha = rotation_about_axis('x',alpha)
    Dx_a = translation_along_axis('x',a)

    Ry_beta = rotation_about_axis('y',beta)
    Dz_d = translation_along_axis('z',d)
    Rz_theta = rotation_about_axis('z',theta)

    #T = np.dot(Rx_alpha,np.dot(Dx_a,np.dot(Dz_d, np.dot(Ry_beta,Rz_theta))))
    T = np.dot(Rx_alpha,np.dot(Dx_a,np.dot(Ry_beta,np.dot(Rz_theta,Dz_d))))
    return T

def rotation_about_axis(axis_name, rotation_angle):
	#rotation_angle in radian
	theta = rotation_angle
	cos_theta = np.cos(theta)
	sin_theta = np.sin(theta)
	if axis_name == 'x':
		T1 = [1.0,        0.0,        0.0, 0.0]
		T2 = [0.0,  cos_theta, -sin_theta, 0.0]
		T3 = [0.0,  sin_theta,  cos_theta, 0.0]
		T4 = [0.0,        0.0,        0.0, 1.0]
		T = np.vstack((T1,T2,T3,T4))

	elif axis_name == 'y':
		T1 = [cos_theta ,    0.0, sin_theta, 0.0]
		T2 = [0.0       ,    1.0,       0.0, 0.0]
		T3 = [-sin_theta,    0.0, cos_theta, 0.0]
		T4 = [0.0       ,    0.0,       0.0, 1.0]
		T = np.vstack((T1,T2,T3,T4))

	elif axis_name == 'z':
		T1 = [cos_theta, -sin_theta, 0.0, 0.0]
		T2 = [sin_theta,  cos_theta, 0.0, 0.0]
		T3 = [0.0,              0.0, 1.0, 0.0]
		T4 = [0.0,              0.0, 0.0, 1.0]
		T = np.vstack((T1,T2,T3,T4))
	else:
		print('wrong axis name in rotation!')
		T = np.zeros((4,4))
	return T

def translation_along_axis(axis_name, translation):
	#translation in millimeter
	d = translation
	if axis_name == 'x':
		T1 = [1.0, 0.0, 0.0, d]
		T2 = [0.0, 1.0, 0.0, 0.0]
		T3 = [0.0, 0.0, 1.0, 0.0]
		T4 = [0.0, 0.0, 0.0, 1.0]
		T = np.vstack((T1,T2,T3,T4))

	elif axis_name == 'y':
		T1 = [1.0, 0.0, 0.0, 0.0]
		T2 = [0.0, 1.0, 0.0, d]
		T3 = [0.0, 0.0, 1.0, 0.0]
		T4 = [0.0, 0.0, 0.0, 1.0]
		T = np.vstack((T1,T2,T3,T4))

	elif axis_name == 'z':
		T1 = [1.0, 0.0, 0.0, 0.0]
		T2 = [0.0, 1.0, 0.0, 0.0]
		T3 = [0.0, 0.0, 1.0, d]
		T4 = [0.0, 0.0, 0.0, 1.0]
		T = np.vstack((T1,T2,T3,T4))

	else:
		print('wrong axis name in translation!')
		T = np.zeros((4,4))

	return T

def DH_T_analytical(DH, idx, joint_angle):
    #a,alpha,beta,d,theta
    a = DH[idx][0]
    alpha = DH[idx][1]
    beta = DH[idx][2]
    d = DH[idx][3]
    theta = DH[idx][4]+ joint_angle
    #print(a," ",alpha, " ", beta," ",d," ",theta)
    return transform_from_DH_parameters_analytical(alpha,beta,a,d,theta)

'''
def DH_T_numerical(DH, idx, joint_angle):
    #a,alpha,beta,d,theta
    a = DH[idx][0]*1000.0
    alpha = DH[idx][1]
    beta = DH[idx][2]
    d = DH[idx][3]*1000.0
    theta = DH[idx][4] + joint_angle
    #print("a, alpha,beta,d,theta, joint angle, theta + joint angle: ",a," ",alpha, " ", beta," ", d," ",DH[idx][4]," ",joint_angle," ",theta)
    return transform_from_DH_parameters_numerical(alpha,beta,a,d,theta)
'''

def MDH_T_analytical(DH, idx, joint_angle):
    #a,alpha,beta,d,theta
    a = DH[idx][0]
    alpha = DH[idx][1]
    beta = DH[idx][2]
    d = DH[idx][3]
    theta = DH[idx][4]+ joint_angle
    #print(a," ",alpha, " ", beta," ",d," ",theta)
    return transform_from_MDH_parameters_analytical(alpha,beta,a,d,theta)

def MDH_T_numerical(DH, idx, joint_angle):
    #a,alpha,beta,d,theta
    a = DH[idx][0]
    alpha = DH[idx][1]
    beta = DH[idx][2]
    d = DH[idx][3]
    theta = DH[idx][4]+ joint_angle
    #print("a, alpha,beta,d,theta, joint angle, theta + joint angle: ",a," ",alpha, " ", beta," ", d," ",DH[idx][4]," ",joint_angle," ",theta)
    return transform_from_MDH_parameters_numerical(alpha,beta,a,d,theta)

# Checks if a matrix is a valid rotation matrix.
def isRotationMatrix(R) :
    Rt = np.transpose(R)
    shouldBeIdentity = np.dot(Rt, R)
    I = np.identity(3, dtype = R.dtype)
    n = np.linalg.norm(I - shouldBeIdentity)
    return n < 1e-6

def RPYeulerAnglesToRotationMatrix(theta=[0,0,0]) : #ZYX euler angle
    roll  = theta[0]
    pitch = theta[1]
    yaw   = theta[2]
    R_x = np.array([[1,                0,               0               ],
                    [0,                math.cos(roll), -math.sin(roll) ],
                    [0,                math.sin(roll),  math.cos(roll) ]])
    R_y = np.array([[math.cos(pitch),  0,               math.sin(pitch)],
                    [0,                1,               0              ],
                    [-math.sin(pitch), 0,               math.cos(pitch)]])
    R_z = np.array([[math.cos(yaw),   -math.sin(yaw),   0],
                    [math.sin(yaw),    math.cos(yaw),   0],
                    [0,                0,               1]])

    R = np.dot(R_z, np.dot( R_y, R_x ))

    assert(isRotationMatrix(R))

    return R

def RotationMatrixToRPYeulerAngles(R) : #rotation matrix to roll, pitch, yaw
    assert(isRotationMatrix(R))
    sy = math.sqrt(R[0,0] * R[0,0] +  R[1,0] * R[1,0])
    singular = sy < 1e-6
    if  not singular :
        x = math.atan2(R[2,1] , R[2,2]) #psi, rotation about x
        y = math.atan2(-R[2,0], sy) # theta, rotation about y
        z = math.atan2(R[1,0], R[0,0]) #phi, rotation about z
    else :
        x = math.atan2(-R[1,2], R[1,1])
        y = math.atan2(-R[2,0], sy)
        z = 0

    return np.array([x, y, z]) #psi,theta, phi, R = R_z(phi)*R_y(theta)*R_x(psi), return in radian

def RPYeulerAnglesToQuaternion(theta=[0,0,0]) : #ZYX euler angles to Quaternion
    roll  = theta[0] #phi
    pitch = theta[1] #theta
    yaw   = theta[2] #psi
    cos_phi_2   = math.cos(roll * 0.5)
    cos_theta_2 = math.cos(pitch * 0.5)
    cos_psi_2   = math.cos(yaw * 0.5)
    sin_phi_2   = math.sin(roll * 0.5)
    sin_theta_2 = math.sin(pitch * 0.5)
    sin_psi_2   = math.sin(yaw * 0.5)

    q0 = cos_phi_2 * cos_theta_2 * cos_psi_2 + sin_phi_2 * sin_theta_2 * sin_psi_2
    q1 = sin_phi_2 * cos_theta_2 * cos_psi_2 - cos_phi_2 * sin_theta_2 * sin_psi_2
    q2 = cos_phi_2 * sin_theta_2 * cos_psi_2 + sin_phi_2 * cos_theta_2 * sin_psi_2
    q3 = cos_phi_2 * cos_theta_2 * sin_psi_2 - sin_phi_2 * sin_theta_2 * cos_psi_2

    return np.array([q0,q1,q2,q3])

def QuaternionToRPYeulerAngles(q=[0,0,0,0]) : #Quaternion to ZYX euler angles (radian)
    q_norm = np.linalg.norm(q)
    if abs(1-q_norm) > 0.0001:
        print("norm of quaternion is not 1")

    q0 = q[0]
    q1 = q[1]
    q2 = q[2]
    q3 = q[3]

    phi = math.atan2(2.0*(q0*q1 + q2*q3), 1.0 - 2.0*(q1*q1 + q2*q2))
    theta = math.asin(2.0*(q0*q2 - q3*q1))
    psi = math.atan2(2.0*(q0*q3 + q1*q2), 1.0 - 2.0*(q2*q2 + q3*q3))

    return np.array([phi,theta,psi])

def QuaternionToRotationMatrix(Q=[0,0,0,0]) : #Quaternion to Rotation matrix
    """
    Covert a quaternion into a full three-dimensional rotation matrix.

    Input
    :param Q: A 4 element array representing the quaternion (q0,q1,q2,q3)

    Output
    :return: A 3x3 element matrix representing the full 3D rotation matrix.
             This rotation matrix converts a point in the local reference
             frame to a point in the global reference frame.
    """
    q_norm = np.linalg.norm(Q)
    if abs(1-q_norm) > 0.0001:
        print("norm of quaternion is not 1."," The norm of q is ",q_norm)

    # Extract the values from Q
    q0 = Q[0]
    q1 = Q[1]
    q2 = Q[2]
    q3 = Q[3]

    # First row of the rotation matrix
    r00 = 2 * (q0 * q0 + q1 * q1) - 1
    r01 = 2 * (q1 * q2 - q0 * q3)
    r02 = 2 * (q1 * q3 + q0 * q2)

    # Second row of the rotation matrix
    r10 = 2 * (q1 * q2 + q0 * q3)
    r11 = 2 * (q0 * q0 + q2 * q2) - 1
    r12 = 2 * (q2 * q3 - q0 * q1)

    # Third row of the rotation matrix
    r20 = 2 * (q1 * q3 - q0 * q2)
    r21 = 2 * (q2 * q3 + q0 * q1)
    r22 = 2 * (q0 * q0 + q3 * q3) - 1

    # 3x3 rotation matrix
    R = np.array([[r00, r01, r02],
                  [r10, r11, r12],
                  [r20, r21, r22]])

    assert(isRotationMatrix(R))

    return R

#https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations
def RotationMatrixFromAxisAngle(u,theta): #u = unit direction vector, theta = rotation angle in radian

    u_norm = np.linalg.norm(u,ord=2)
    if np.abs(u_norm - 1.0) > 1e-7:
        print("rotation axis vector is not a unit vector!")
        print("norm of the rotation axis vector is ",u_norm)
        u = np.multiply(1/u_norm,u)
    c_theta = np.cos(theta)
    s_theta = np.sin(theta)
    ux = u[0]
    uy = u[1]
    uz = u[2]
    alpha = 1-c_theta

    r00 = c_theta + ux*ux*alpha
    r01 = ux*uy*alpha - uz*s_theta
    r02 = ux*uz*alpha + uy*s_theta

    r10 = uy*ux*alpha + uz*s_theta
    r11 = c_theta + uy*uy*alpha
    r12 = uy*uz*alpha - ux*s_theta

    r20 = uz*ux*alpha - uy*s_theta
    r21 = uz*uy*alpha + ux*s_theta
    r22 = c_theta + uz*uz*alpha

    # 3x3 rotation matrix
    R = np.array([[r00, r01, r02],
                  [r10, r11, r12],
                  [r20, r21, r22]])

    assert(isRotationMatrix(R))

    return R


import numpy as np
import pandas as pd
from sklearn import preprocessing

####Implementacion

#complementaria
def Ecuacion_RL(X, theta):
    d = X.shape[0]
    Y = theta[0,-1] + np.dot(theta[0,:-1].reshape(1,d), X)
    return Y

#1
def Leer_Datos(filename):
    content = pd.read_csv(filename, encoding = 'ascii', dtype = np.float64)
    #X, Y = content.to_numpy().T[:-1], content.to_numpy().T[-1]
    #return X, Y
    return content

#complementaria
def Normalizar_Datos_np(npmatrix):
    npmins1 = np.asarray([[np.amin(row) for row in npmatrix]]).T                 #Hallo minimos
    normst1 = npmatrix - npmins1                                                 #Paso 1: resta vectorizada de minimos
    npmaxs1 = np.asarray([[np.amax(row) for row in normst1]]).T                  #Hallo maximos
    npmaxs1 /= 2                                                                 #Aseguro rango [-1,1]
    normst2 = normst1 / npmaxs1                                                 #Paso 2: divido sobre maximos
    return normst2, npmins1, npmaxs1

#2 pd
def Normalizar_Datos_pd(content):                                               #Por corregir
    min_max_scaler = preprocessing.MinMaxScaler()
    content_scaled = min_max_scaler.fit_transform(content)
    df = pd.DataFrame(content_scaled)
    return df

#3
def Crear_Entrenamiento_Prueba(df, frac = 0.7):
    msk = np.random.randn(len(df)) <= frac
    train = df[msk]
    test = df[~msk]
    return train, test

#complementaria
def Distribucion_a_Numpy(df):
    X, Y = df.to_numpy().T[:-1], df.to_numpy().T[-1]
    Y = Y.reshape(1, Y.shape[0])
    print(X.shape, Y.shape)
    return X, Y

#4
def Calcular_Costo(X, Y, theta):
    m = X.shape[1]
    J = np.sum((Ecuacion_RL(X, theta) - Y) ** 2) / (2 * m)
    #print(str(Ecuacion_RL(X, theta) - Y))
    #print("ssss")
    #print(str((Ecuacion_RL(X, theta) - Y)**2))
    #input()
    return J

#complementaria
def Inicializar_thetas(X):
    theta = np.random.randn(1, X.shape[0] + 1)
    theta[0,-1] = 0
    return theta

#complementaria
def Derivada_J(X, Y, theta, m):
    theta_first = np.sum((Ecuacion_RL(X, theta) - Y) * X, axis = 1, keepdims = True) / m
    theta_second = np.sum(Ecuacion_RL(X, theta) - Y, axis = 1, keepdims = True) / m
    return theta_first, float(theta_second[0,0])

#5
def Gradiente_Descendiente(X, Y, theta, learning_rate = 0.001, num_iterations = 1001):
    m = X.shape[0]
    lista_costos = []
    for i in range(num_iterations):
        theta_first, theta_second = Derivada_J(X, Y, theta, m)
        theta[0,:-1] -= learning_rate * theta_first[0]
        theta[0,-1] -= learning_rate * theta_second
        J = Calcular_Costo(X, Y, theta)
        if i % 100 == 0:
            print("Costo en la Iteración " + str(i) + ": " + str(J))
            lista_costos.append(J)
    return theta, lista_costos

#complementaria
def Predecir(X, Y, theta):
    d = X.shape[0]
    Y_hat = theta[0,-1] + np.dot(theta[0,:-1].reshape(1,d), X)
    error = Calcular_Costo(X,Y, theta)
    return Y_hat, error

####Experimentos
#Prediccion
class Predictor:
    def __init__(self):
        self.dataset_path = ["../Datos/ex1data2(Home_1f).csv" , "../Datos/oceano_simple.csv", "../Datos/petrol_consumption.csv"]
        self.dataset_name = ["Precio, Viviendas", "Temperatura Oceano", "Consumo de Petroleo"]

    def set_data_train_test(self):
        self.train0, self.test0 = Crear_Entrenamiento_Prueba(Normalizar_Datos_pd(Leer_Datos(self.dataset_path[0])))
        self.train1, self.test1 = Crear_Entrenamiento_Prueba(Normalizar_Datos_pd(Leer_Datos(self.dataset_path[1])))
        self.train2, self.test2 = Crear_Entrenamiento_Prueba(Normalizar_Datos_pd(Leer_Datos(self.dataset_path[2])))

    def Experimento_1(self):
        return True
    def Experimento_2(self):
        return True
    def Experimento_3(self):                    #grafica datos train test Precio Vivienda
        return True
    def Experimento_4(self):                    #graficas de costo 3 datasets
        return True


# encoding=utf-8
from numpy import *
''' Numpy的主要对象是同种元素的多维数组，这是一个所有的元素都是一种类型，通过一个正整数元祖索引的元素表格（通常的元素是数字）。
    在Numpy中维度（dimensions）叫做轴（axes），轴的个数叫做秩（rank）
    例如，在3D空间一个点的坐标[1,2,3]是一个秩为1的数组，因为它只有一个轴，那个轴的长度为3，
    例如，[[1.,0.,0.],[0.,1.,2.]] 的秩为2，第一个维度长度为2，第二个维度长度为3'''

''' Numpy的数组类被称作ndarry，通常被称作数组，注意numpy.array和标准python库类array.array并不相同，后者只处理一维数组和提供少量功能'''

A = array([[1.,0.,0.],[0.,1.,2.]])
# A = arange(15).reshape(3, 5)

print (A.ndim) #数组轴的个数
print (A.shape)# 数组的维度，这是一个指定数组在每个维度上大小的整数元祖。例如一个n排m列的矩阵，它的shape属性将是（2,3）
print (A.size) # 数据元素的总个数
print (A.dtype) # 描述数组中元素类型的对象
print (A.itemsize) #数组中每个元素的字节大小，一个元素类型为float64的数组itemsize属性值为8=（64/8）
print (A.data) #包含数据数组元素的缓冲区，通常我们不需要使用这个属性，因为我们总是用过索引来使用数组中的元素

'''创建数组'''
A = array([[1.,0.,0.],[0.,1.,2.]])
print (A)

B = array ([(1.5,2,3),(4,5,6)]) # 数组将序列包含序列转化成二维的数组
C = array([[1,2],[2,3]],dtype = complex) # 数字类型可以在创建时显示指定
D = zeros((3,4)) # 创建一个全是0的数组，最小化了扩展数组的需求和高昂的运算代价
E = ones((4,5))
F = empty((2,3)) # 创建一个内容随机的数组，类型为 float64
G = arange(5) # array([0, 1, 2, 3，5])
H = arange(10,30,5) # 返回array([10, 15, 20, 25])

B = arange(15).reshape(3, 5) # 打印2d array
C = arange(24).reshape(2, 3,4) # 打印3d array
set_printoptions(threshold='nan') # 设置参数来完成打印

'''基本运算'''

## **2 平方

A = array( [[1,1], [0,1]] )
B = array( [[2,0], [3,4]] )
A*B #元素相称
dot(A,B)  # 矩阵相乘
D *= 3  # 更改数组中的元素
A += B
B.sum(axis=0) # 每一列的总和 指定axis参数你可以把运算应用到数组置顶的轴上
B.min(axis=1) # 每一行的最小值
kkkkk


"""Generate random state vector."""
from typing import List, Union

import numpy as np

from toqito.states import max_entangled
from toqito.perms import swap

def random_state_vector(
    dim: Union[List[int], int], is_real: bool = False, k_param: int = 0
) -> np.ndarray:
    r"""Generate a random pure state vector.

    Examples
    ==========

    We may generate a random state vector. For instance, here is an example where we can generate a
    :math:`2`-dimensional random state vector.

    >>> from toqito.random import random_state_vector
    >>> vec = random_state_vector(2)
    >>> vec
    [[0.50993973+0.15292408j],
     [0.27787332+0.79960122j]]

    We can verify that this is in fact a valid state vector by computing the corresponding density
    matrix of the vector and checking if the density matrix is pure.

    >>> from toqito.state_props import is_pure
    >>> dm = vec.conj().T * vec
    >>> is_pure(dm)
    True

    :param dim: The number of rows (and columns) of the unitary matrix.
    :param is_real: Boolean denoting whether the returned matrix has real
                    entries or not. Default is :code:`False`.
    :param k_param: Default 0.
    :return: A :code:`dim`-by-:code:`dim` random unitary matrix.
    """
    # Schmidt rank plays a role.
    if 0 < k_param < np.min(dim):

        # Allow the user to enter a single number for dim.
        if isinstance(dim, int):
            dim = [dim, dim]

        # If you start with a separable state on a larger space and multiply
        # the extra `k_param` dimensions by a maximally entangled state, you
        # get a Schmidt rank `<= k_param` state.
        psi = max_entangled(k_param, True, False).toarray()

        a_param = np.random.rand(dim[0] * k_param, 1)
        b_param = np.random.rand(dim[1] * k_param, 1)

        if not is_real:
            a_param = a_param + 1j * np.random.rand(dim[0] * k_param, 1)
            b_param = b_param + 1j * np.random.rand(dim[1] * k_param, 1)

        mat_1 = np.kron(psi.conj().T, np.identity(int(np.prod(dim))))
        mat_2 = swap(
            np.kron(a_param, b_param),
            sys=[2, 3],
            dim=[k_param, dim[0], k_param, dim[1]],
        )

        ret_vec = mat_1 @ mat_2
        return np.divide(ret_vec, np.linalg.norm(ret_vec))

    # Schmidt rank is full, so ignore it.
    ret_vec = np.random.rand(dim, 1)
    if not is_real:
        ret_vec = ret_vec + 1j * np.random.rand(dim, 1)
    return np.divide(ret_vec, np.linalg.norm(ret_vec))


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Thu Jun 29 17:08:12 2017

@author: lracuna
"""
from scipy.linalg import expm
import numpy as np
from math import cos, sin

def rotation_matrix(a, alpha):
    """  Creates a 3D [R|t] matrix for rotation
    around the axis of the vector a by an alpha angle."""
    #Normalize the rotation axis a
    a = a / np.linalg.norm(a)

    #Build the skew symetric
    a_skew = np.mat([[0,-a[2],a[1]], [a[2], 0, -a[0]], [-a[1], a[0], 0]])
    R = np.eye(4)
    R[:3,:3] = expm(a_skew*alpha)
    return R

def translation_matrix(t):
  """  Creates a 3D [R|t] matrix with a translation t
  and an identity rotation """
  R = np.eye(4)
  R[:3,3] = np.array([t[0],t[1],t[2]])
  return R

def rotation_matrix_from_two_vectors(a,b):
    a = a / np.linalg.norm(a)
    b = b / np.linalg.norm(b)
    v = np.cross(a,b)
    ssc = np.matrix(np.array([[0.0, -v[2], v[1]],
                 [v[2], 0.0, -v[0]],
                 [-v[1], v[0], 0]]))
    R = np.eye(4)
    #TODO Yue: Form wrong ??? https://gist.github.com/peteristhegreat/3b76d5169d7b9fc1e333
    R[:3,:3] = np.array(np.eye(3) + ssc + (ssc**2)*(1.0/(1.0+np.dot(a,b))))
    return R

def rot_matrix_error(R0, R1, method = 'unit_quaternion_product'):
    """ R0, R1 are 3x3 or 4x4 homogeneous Rotation matrixes
        returns: the value of the error depending on the method """

    if ((R0.shape != (4,4)) and (R0.shape != (3,3))):
        print ("Error in the R0 input rotation matrix shape, must be 3x3 or 4x4")
        print R0
        return -1
    if ((R1.shape != (4,4)) and (R1.shape != (3,3))):
        print ("Error in the R1 input rotation matrix shape, must be 3x3 or 4x4")
        print R1
        return -1

    if R0.shape == (3,3):
        R = np.eye(4)
        R[:3,:3] = R0
        R0 = R

    if R1.shape == (3,3):
        R = np.eye(4)
        R[:3,:3] = R1
        R1 = R

    if(method == 'unit_quaternion_product' ):
        ## From the paper "Metrics for 3D Rotations: Comparison and Analysis" D. Huynh
        # The 3D rotation error is computed using the inner product of unit quaterions

        #We use the ros library TF to convert rotation matrix into unit quaternions
        from tf import transformations
        q0 = transformations.quaternion_from_matrix(R0)
        q1 = transformations.quaternion_from_matrix(R1)

        # We convert into unit quaternions
        q0 = q0 / np.linalg.norm(q0)
        q1 = q1 / np.linalg.norm(q1)

        #Find the error as defined in the paper
        rot_error = 1 - np.linalg.norm(np.dot(q0,q1))

    if(method == 'angle'):
        #option 2 find the angle of this rotation. In particular, the above is invalid
        #for very large error angles (error > 90 degrees) and is imprecise for large
        #error angles (angle > 45 degrees).

        E = R1.dot(R0.T)
        from cv2 import Rodrigues
        rot_vector, J = Rodrigues(E[:3,:3])

        angle = np.linalg.norm(rot_vector)

        rot_error = np.rad2deg(angle)
#        d = np.zeros(3)
#        d[0] = E[1,2] - E[2,1]
#        d[1] = E[2,0] - E[0,2]
#        d[2] = E[0,1] - E[1,0]
#
#        dmag = sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2])
#
#        phi = np.rad2deg(asin(dmag/2))
#
#        rot_error = phi

    return rot_error

#TODO MAYBE USE the TF package directly
#http://docs.ros.org/kinetic/api/tf/html/c++/classtf_1_1Matrix3x3.html#a9219cc537dd0a0dd8370959e5300c277

def R_matrix_from_euler_t(alpha,beta,gamma):
  """
  A=BCD. Z -> X -> Z
  1. the first rotation is by an angle phi about the z-axis using D,
  2. the second rotation is by an angle theta in [0,pi] about the former x-axis (now x^') using C, and
  3. the third rotation is by an angle psi about the former z-axis (now z^') using B.
  :param alpha:
  :param beta:
  :param gamma:
  :return:
  """
  R = np.eye(4, dtype=np.float32)
  R[0,0]=cos(alpha)*cos(gamma)-cos(beta)*sin(alpha)*sin(gamma)
  R[1,0]=cos(gamma)*sin(alpha)+cos(alpha)*cos(beta)*sin(gamma)
  R[2,0]=sin(beta)*sin(gamma)

  R[0,1]=-cos(beta)*cos(gamma)*sin(alpha)-cos(alpha)*sin(gamma)
  R[1,1]=cos(alpha)*cos(beta)*cos(gamma)-sin(alpha)*sin(gamma)
  R[2,1]=cos(gamma)*sin(beta)

  R[0,2]=sin(alpha)*sin(beta)
  R[1,2]=-cos(alpha)*sin(beta)
  R[2,2]=cos(beta)

  return R

def R_matrix_from_euler_zyx(alpha,beta,gamma):
  """
  author: Yue Hu
  A=BCD. Z -> Y -> X
  :param alpha:
  :param beta:
  :param gamma:
  :return:
  """
  R = np.eye(4, dtype=np.float32)
  R[0,0] = cos(beta) * cos(alpha)
  R[1,0] = sin(gamma) * sin(beta) * cos(alpha) - cos(gamma) * sin(alpha)
  R[2,0] = cos(gamma) * sin(beta) * cos(alpha) + sin(gamma) * sin(alpha)

  R[0,1] = cos(beta) * sin(alpha)
  R[1,1] = sin(gamma) * sin(beta) * sin(alpha) + cos(gamma) * cos(alpha)
  R[2,1] = cos(gamma) * sin(beta) * sin(alpha) - sin(gamma) * cos(alpha)

  R[0,2] = -sin(beta)
  R[1,2] = sin(gamma) * cos(beta)
  R[2,2] = cos(gamma) * cos(beta)

  return R


def tilde_w(c, k, dx, dt):
    C = dt*c/dx
    return 2/dt*asin(C*sin(k*dx/2))

def tilde_c(c, k, dx, dt):
    return tilde_w(c, k, dx, dt)/k

def r(C, p):
    return 1/(C*p)*asin(C*sin(p))  # important with 1, not 1. for sympy

def makeplot():
    n = 16
    p = linspace(0.001, pi/2, n)
    legends = []
    for C in 1.0, 0.95, 0.8, 0.3:
        plot(p, r(C, p))
        hold('on')
        legends.append('C=%g' % C)
    title('Numerical divided by exact wave velocity')
    legend(legends, fancybox=True, loc='lower left')
    axis([p[0], p[-1], 0.6, 1.1])
    xlabel('p')
    ylabel('velocity ratio')
    savefig('tmp.pdf')
    savefig('tmp.eps')
    savefig('tmp.png')
    show()

def sympy_analysis():
    C, p = symbols('C p')
    # Turn series expansion into polynomial and Python function
    # representations
    rs = r(C, p).series(p, 0, 7).removeO()
    print 'series representation of r(C, p):', rs
    print 'factored series representation of r(C, p):', factor(rs)
    rs1 = factor((rs - 1).extract_leading_order(p)[0][0])
    print 'leading order of the error:', rs1
    rs_poly = poly(rs)
    print 'polynomial representation:', rs_poly
    rs_pyfunc = lambdify([C, p], rs)  # can be used for plotting
    # Know that rs_pyfunc is correct (=1) when C=1, check that
    print rs_pyfunc(1, 0.1), rs_pyfunc(1, 0.76)

    # Alternative method for extracting terms in a series expansion:
    import itertools
    rs = [t for t in itertools.islice(r(C, p).lseries(p), 4)]
    print rs
    rs = [factor(t) for t in rs]
    print rs
    rs = sum(rs)
    print rs

    # true error
    x, t, k, w, c, dx, dt = symbols('x t k w c dx dt')
    u_n = cos(k*x - tilde_w(c, k, dx, dt)*t)
    u_e = cos(k*x - w*t)
    e = u_e-u_n
    # sympy cannot do this series expansion
    #print e.series(dx, 0, 4)

if __name__ == '__main__':
    #from scitools.std import *
    #makeplot()
    from sympy import * # erases sin and other math functions from numpy
    sympy_analysis()


import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.cluster import KMeans
import sklearn.metrics as sm
import pandas as pd
import numpy as np
#%matplotlib inline

l1 = [0,1,2]

def rename(s):
	l2 = []
	for i in s:
		if i not in l2:
			l2.append(i)

	for i in range(len(s)):
		pos = l2.index(s[i])
		s[i] = l1[pos]

	return s

# import some data to play with
iris = datasets.load_iris()

print("\n IRIS DATA :",iris.data);
print("\n IRIS FEATURES :\n",iris.feature_names)
print("\n IRIS TARGET  :\n",iris.target)
print("\n IRIS TARGET NAMES:\n",iris.target_names)

# Store the inputs as a Pandas Dataframe and set the column names
X = pd.DataFrame(iris.data)

#print(X)
X.columns = ['Sepal_Length','Sepal_Width','Petal_Length','Petal_Width']

#print(X.columns) #print("X:",x)
#print("Y:",y)
y = pd.DataFrame(iris.target)
y.columns = ['Targets']

print("Actual Target is:\n", iris.target)

# K Means Cluster
model = KMeans(n_clusters=3)
model.fit(X)

# Set the size of the plot
plt.figure(figsize=(14,7))

# Create a colormap
colormap = np.array(['red', 'lime', 'black'])

# Plot the Original Classifications
plt.subplot(1, 2, 1)
plt.scatter(X.Petal_Length, X.Petal_Width, c=colormap[y.Targets], s=40)
plt.title('Real Classification')

# Plot the Models Classifications
plt.subplot(1, 2, 2)
plt.scatter(X.Petal_Length, X.Petal_Width, c=colormap[rename(model.labels_)], s=40)
plt.title('K Mean Classification')
plt.show()

km = rename(model.labels_)
print("\nWhat KMeans thought: \n", km)
print("Accuracy of KMeans is ",sm.accuracy_score(y, km))
print("Confusion Matrix for KMeans is \n",sm.confusion_matrix(y, km))

from sklearn import preprocessing
scaler = preprocessing.StandardScaler()
scaler.fit(X)
xsa = scaler.transform(X)
xs = pd.DataFrame(xsa, columns = X.columns)
print("\n",xs.sample(5))

from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=3)
gmm.fit(xs)

y_cluster_gmm = gmm.predict(xs)

plt.subplot(1, 2, 1)
plt.scatter(X.Petal_Length, X.Petal_Width, c=colormap[rename(y_cluster_gmm)], s=40)
plt.title('GMM Classification')
plt.show()

em = rename(y_cluster_gmm)
print("\nWhat EM thought: \n", em)
print("Accuracy of EM is ",sm.accuracy_score(y, em))
print("Confusion Matrix for EM is \n", sm.confusion_matrix(y, em))


import numpy as np

# change this if using K > 100
denominator_table = np.log2( np.arange( 2, 102 ))

def dcg_at_k( r, k, method = 1 ):
	"""Score is discounted cumulative gain (dcg)

	Relevance is positive real values.  Can use binary
	as the previous methods.

	Example from
	http://www.stanford.edu/class/cs276/handouts/EvaluationNew-handout-6-per.pdf
	>>> r = [3, 2, 3, 0, 0, 1, 2, 2, 3, 0]
	>>> dcg_at_k(r, 1)
	3.0
	>>> dcg_at_k(r, 1, method=1)
	3.0
	>>> dcg_at_k(r, 2)
	5.0
	>>> dcg_at_k(r, 2, method=1)
	4.2618595071429155
	>>> dcg_at_k(r, 10)
	9.6051177391888114
	>>> dcg_at_k(r, 11)
	9.6051177391888114

	Args:
		r: Relevance scores (list or numpy) in rank order
			(first element is the first item)
		k: Number of results to consider
		method: If 0 then weights are [1.0, 1.0, 0.6309, 0.5, 0.4307, ...]
				If 1 then weights are [1.0, 0.6309, 0.5, 0.4307, ...]

	Returns:
		Discounted cumulative gain
	"""
	r = np.asfarray(r)[:k]
	if r.size:
		if method == 0:
			return r[0] + np.sum(r[1:] / np.log2(np.arange(2, r.size + 1)))
		elif method == 1:
			# return np.sum(r / np.log2(np.arange(2, r.size + 2)))
			return np.sum(r / denominator_table[:r.shape[0]])
		else:
			raise ValueError('method must be 0 or 1.')
	return 0.

def get_ndcg( r, k, method = 1 ):
	"""Score is normalized discounted cumulative gain (ndcg)

	Relevance orignally was positive real values.  Can use binary
	as the previous methods.

	Example from
	http://www.stanford.edu/class/cs276/handouts/EvaluationNew-handout-6-per.pdf
	>>> r = [3, 2, 3, 0, 0, 1, 2, 2, 3, 0]
	>>> ndcg_at_k(r, 1)
	1.0
	>>> r = [2, 1, 2, 0]
	>>> ndcg_at_k(r, 4)
	0.9203032077642922
	>>> ndcg_at_k(r, 4, method=1)
	0.96519546960144276
	>>> ndcg_at_k([0], 1)
	0.0
	>>> ndcg_at_k([1], 2)
	1.0

	Args:
		r: Relevance scores (list or numpy) in rank order
			(first element is the first item)
		k: Number of results to consider
		method: If 0 then weights are [1.0, 1.0, 0.6309, 0.5, 0.4307, ...]
				If 1 then weights are [1.0, 0.6309, 0.5, 0.4307, ...]

	Returns:
		Normalized discounted cumulative gain
	"""
	dcg_max = dcg_at_k(sorted(r, reverse=True), k, method)
	dcg_min = dcg_at_k(sorted(r), k, method)
	#assert( dcg_max >= dcg_min )

	if not dcg_max:
		return 0.

	dcg = dcg_at_k(r, k, method)

	#print dcg_min, dcg, dcg_max

	return (dcg - dcg_min) / (dcg_max - dcg_min)

# ndcg with explicitly given best and worst possible relevances
# for recommendations including unrated movies
def get_ndcg_2( r, best_r, worst_r, k, method = 1 ):

	dcg_max = dcg_at_k( sorted( best_r, reverse = True ), k, method )

	if worst_r == None:
		dcg_min = 0.
	else:
		dcg_min = dcg_at_k( sorted( worst_r ), k, method )

	# assert( dcg_max >= dcg_min )

	if not dcg_max:
		return 0.

	dcg = dcg_at_k( r, k, method )

	#print dcg_min, dcg, dcg_max

	return ( dcg - dcg_min ) / ( dcg_max - dcg_min )


import numpy as np

def compute_confusion_matrix(prediction, ground_truth, num_classes):
    replace_indices = np.vstack((
        ground_truth.flatten(),
        prediction.flatten())
    ).T
    confusion_matrix, _ = np.histogramdd(
        replace_indices,
        bins=(num_classes, num_classes),
        range=[(0, num_classes), (0, num_classes)]
    )
    confusion_matrix = confusion_matrix.astype(np.uint32)
    return confusion_matrix

def iou_score(confusion_matrix, reduce_mean=True):
    ious = []
    for index in range(confusion_matrix.shape[0]):
        true_positives = confusion_matrix[index, index]
        false_positives = confusion_matrix[:, index].sum() - true_positives
        false_negatives = confusion_matrix[index, :].sum() - true_positives
        denom = true_positives + false_positives + false_negatives
        if denom == 0:
            iou = 1
        else:
            iou = float(true_positives) / denom
        ious.append(iou)
    return np.mean(ious) if reduce_mean else ious

def f1_score(confusion_matrix, reduce_mean=True):
    f1_scores = []
    for index in range(confusion_matrix.shape[0]):
        true_positives = confusion_matrix[index, index]
        false_positives = confusion_matrix[:, index].sum() - true_positives
        false_negatives = confusion_matrix[index, :].sum() - true_positives
        denom = 2 * true_positives + false_positives + false_negatives
        if denom == 0:
            f1_score = 1
        else:
            f1_score = 2 * float(true_positives) / denom
        f1_scores.append(f1_score)
    return np.mean(f1_scores) if reduce_mean else f1_scores

def precision_recall_fscore_support(confusion_matrix):
    f1_scores, precisions, recalls, supports = [], [], [], []
    for index in range(confusion_matrix.shape[0]):
        true_positives = confusion_matrix[index, index]
        false_positives = confusion_matrix[:, index].sum() - true_positives
        false_negatives = confusion_matrix[index, :].sum() - true_positives

        denom = true_positives + false_positives
        precision = 1 if denom == 0 else float(true_positives) / denom

        denom = true_positives + false_negatives
        recall = 1 if denom == 0 else float(true_positives) / denom

        denom = precision + recall
        f1_score = 1 if denom == 0 else 2 * float(precision * recall) / denom

        f1_scores.append(f1_score)
        precisions.append(precision)
        recalls.append(recall)
        supports.append(int(true_positives+false_negatives))

    return precisions, recalls, f1_scores, supports

def accuracy(confusion_matrix):
    return np.diag(confusion_matrix).sum() / confusion_matrix.sum()

class AverageMeter(object):
    """Computes and stores the average and current value"""
    def __init__(self, name, fmt=':f'):
        self.name = name
        self.fmt = fmt
        self.reset()

    def reset(self):
        self.val = 0
        self.avg = 0
        self.sum = 0
        self.count = 0

    def update(self, val, n=1):
        self.val = val
        self.sum += val * n
        self.count += n
        self.avg = self.sum / self.count

    def __str__(self):
        fmtstr = '{name} {val' + self.fmt + '} ({avg' + self.fmt + '})'
        return fmtstr.format(**self.__dict__)


import cv2,os
import numpy as np

def sigmoid(z):
    s = 1/(1+np.exp(-z))
    return s

def propagate(w, b, X, Y):
    m = X.shape[1]

     # FORWARD PROPAGATION (FROM X TO COST)
    z = np.dot(w.T, X)+b # tag 1
    A = sigmoid(z) # tag 2
    loss = (-np.sum(Y*np.log(A)+(1-Y)*np.log(1-A)))/m # tag 5

    # BACKWARD PROPAGATION (TO FIND GRAD)
    dw = (np.dot(X,(A-Y).T))/m # tag 6
    db = np.average(A-Y) # tag 7

    cost = np.squeeze(loss)
    grads = {"dw": dw,
             "db": db}

    return grads, loss

def optimize(w, b, X, Y, num_iterations, learning_rate, print_loss= True):
    cost = []
    for i in range(num_iterations):
        # Cost and gradient calculation
        grads, loss = propagate(w, b, X, Y)

        # Retrieve derivatives from grads
        dw = grads["dw"]
        db = grads["db"]

        # update w and b
        w = w - learning_rate*dw
        b = b - learning_rate*db

        # Record the costs
        if i % 100 == 0:
            cost.append(loss)

        # Print the cost every 100 training iterations
        if print_loss and i % 100 == 0:
            print ("Loss  %i: %f" %(i, loss))

    # update w and b to dictionary
    params = {"w": w,
              "b": b}

    # update derivatives to dictionary
    grads = {"dw": dw,
             "db": db}

    return params, grads, cost

def predict(w, b, X):
    m = X.shape[1]
    Y_prediction = np.zeros((1, m))
    w = w.reshape(X.shape[0], 1)

    z = np.dot(w.T, X) + b
    A = sigmoid(z)

    for i in range(A.shape[1]):
        # Convert probabilities A[0,i] to actual predictions p[0,i]
        if A[0,i] > 0.5:
            Y_prediction[[0],[i]] = 1
        else:
            Y_prediction[[0],[i]] = 0

    return Y_prediction

def initialize_with_zeros(dim):
    w = np.zeros((dim, 1))
    b = 0
    return w, b

def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_loss = True):
    # initialize parameters with zeros
    w, b = initialize_with_zeros(X_train.shape[0])

    # Gradient descent
    parameters, grads, cost = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_loss)

    # Retrieve parameters w and b from dictionary "parameters"
    w = parameters["w"]
    b = parameters["b"]

    # Predict test/train set examples
    Y_prediction_test = predict(w,b,X_test)
    Y_prediction_train = predict(w,b,X_train)

    # Print train/test Errors
    print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
    print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))

    dict = {"loss": cost,
            "Y_prediction_test": Y_prediction_test,
            "Y_prediction_train": Y_prediction_train,
            "w": w,
            "b": b,
            "learning_rate": learning_rate,
            "num_iterations:": num_iterations}

    return dict


import numpy
from numpy import sqrt, dot, cross
from numpy.linalg import norm

thargmeter_constant = 871.02

# Find the intersection of three spheres
# P1,P2,P3 are the centers, r1,r2,r3 are the radii
# Implementaton based on Wikipedia Trilateration article.

# source:
# https://stackoverflow.com/questions/1406375/finding-intersection-points-between-3-spheres/1406415
def trilaterate(P1, P2, P3, r1, r2, r3):
    r1 *= thargmeter_constant
    r2 *= thargmeter_constant
    r3 *= thargmeter_constant
    temp1 = P2-P1
    e_x = temp1/norm(temp1)
    temp2 = P3-P1
    i = dot(e_x,temp2)
    temp3 = temp2 - i*e_x
    e_y = temp3/norm(temp3)
    e_z = cross(e_x,e_y)
    d = norm(P2-P1)
    j = dot(e_y,temp2)
    x = (r1*r1 - r2*r2 + d*d) / (2*d)
    y = (r1*r1 - r3*r3 -2*i*x + i*i + j*j) / (2*j)
    temp4 = r1*r1 - x*x - y*y
    if temp4<0:
        raise Exception("The three spheres do not intersect!");
    z = sqrt(temp4)
    p_12_a = P1 + x*e_x + y*e_y + z*e_z
    p_12_b = P1 + x*e_x + y*e_y - z*e_z
    return p_12_a, p_12_b

if __name__ == '__main__':
    center_merope = numpy.array([-78.59375, -149.625, -340.53125])
    # Col 70 Sector FY-N C21-3
    center_col70 = numpy.array([687.0625, -362.53125, -697.0625])
    # Mel 22 Sector NX-U d2-31
    center_origin = numpy.array([-64.40625, -269.5625, -373.59375])

    # distances = [0.151 * thargmeter_constant, 0.01 * thargmeter_constant, 0.937 * thargmeter_constant]
    # distances = [0.01 * thargmeter_constant, 0.151 * thargmeter_constant, 0.937 * thargmeter_constant]
    # distances = [0.01 * thargmeter_constant, 0.937 * thargmeter_constant, 0.151 * thargmeter_constant]
    distances = [0.151 * thargmeter_constant, 0.937 * thargmeter_constant, 0.01 * thargmeter_constant]
    answer = trilaterate(center_merope, center_col70, center_origin, distances[0], distances[1], distances[2])
    print(answer[1])
    # should be close to: [ -57.1, -274.7, -373.7 ]


import numpy as np

def import_positions(path):
    with open(path,"r") as fh:
        positions = fh.readline().rstrip().split(",")
    positions = np.array([int(p) for p in positions])
    return positions

def fuel_consumed(pos, positions):
    return np.abs(positions - pos).sum()

def fuel_consumed_incremental(pos, positions):
    deltas = np.abs(positions - pos)
    consumptions = np.multiply(deltas, deltas+1) / 2
    return consumptions.sum()


import numpy as np

from regresspy.loss import mae, sse, mse, rmse

def test_mae():
    dummy_predictions = np.array([2.0, 4.0, 7.0, 8.0])
    dummy_labels = np.array([1.0, 3.0, 6.0, 7.0])
    dummy_mae = 1.0
    assert mae(dummy_predictions, dummy_labels) == dummy_mae

def test_sse():
    dummy_predictions = np.array([2.0, 4.0, 7.0, 8.0])
    dummy_labels = np.array([1.0, 3.0, 6.0, 7.0])
    dummy_sse = 4.0
    assert sse(dummy_predictions, dummy_labels) == dummy_sse

def test_mse():
    dummy_predictions = np.array([2.0, 4.0, 7.0, 8.0])
    dummy_labels = np.array([1.0, 3.0, 6.0, 7.0])
    dummy_mse = 1.0
    assert mse(dummy_predictions, dummy_labels) == dummy_mse

def test_rmse():
    dummy_predictions = np.array([2.0, 4.0, 7.0, 8.0])
    dummy_labels = np.array([1.0, 3.0, 6.0, 7.0])
    dummy_rmse = 1.0
    assert rmse(dummy_predictions, dummy_labels) == dummy_rmse


from scipy.stats.stats import pearsonr
import pandas as pd
from sklearn.feature_selection.variance_threshold import VarianceThreshold

# remove redundant Features
def apply_pearson_filter(dataset, dataset_name, n_features):

    print("total fea before correlation filter: " + str(n_features))

    features_to_remove = []

    for i in range(0, n_features - 1):
        a = list(dataset.iloc[:, i])

        for j in range(i + 1, n_features - 1):
            b = list(dataset.iloc[:, j])

            if abs(round(pearsonr(a, b)[0], 3)) >= 0.985 and a != b:
                print("pearson cor: " + str(abs(round(pearsonr(a, b)[0], 3))) +
                      " a: " + str(dataset.columns[i]) + " b: " + str(dataset.columns[j]))
                if str(dataset.columns[j]) not in features_to_remove:
                    features_to_remove.append(str(dataset.columns[j]))

    dataset = dataset.drop(features_to_remove, axis=1)

    return dataset

def apply_variance_filter(dataset, variance_threshold):
    print("dataset before variance filter: " + str(dataset.shape))

    if variance_threshold != 1:
        cut_point = round(variance_threshold * dataset.shape[1])

        variances = dataset.var()
        data = pd.DataFrame({"variance": variances, "feature": dataset.columns.values})
        data_sorted = data.sort_values(by="variance", ascending=False)

        data_filtered = data_sorted.iloc[0:int(cut_point), :]
        dataset = dataset.loc[:, data_filtered.loc[:, "feature"].values]

    print("dataset after variance filter: " + str(dataset.shape))

    return dataset


import pytest
from pyautodiff.dual import Dual
import pyautodiff.admath as admath
import numpy as np
import math

def test1__sin_fn__():
    x = Dual(0)
    f = admath.sin(x)
    assert f.val == 0
    assert f.der == 1

def test2__sin_fn__():
    tests = np.linspace(-10,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.sin(x)
        assert np.sin(test) == admath.sin(test)
        assert np.sin(test) == f.val
        assert np.cos(test) == f.der

def test1__cos_fn__():
    x = Dual(0)
    f = admath.cos(x)
    assert f.val == np.cos(0)
    assert f.der == -1*np.sin(0)

def test2__cos_fn__():
    tests = np.linspace(-10,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.cos(x)
        assert np.cos(test) == admath.cos(test)
        assert np.cos(test) == f.val
        assert -1*np.sin(test) == f.der

def test1__tan_fn__():
    x = Dual(0)
    f = admath.tan(x)
    assert f.val == 0
    assert f.der == 1
    assert admath.tan(0) == 0

def test_log_fn__():
    tests = np.linspace(1,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.log(x)
        assert np.log(test) == admath.log(test)
        assert np.log(test) == f.val
        assert 1/test == f.der

def test_log2_fn__():
    tests = np.linspace(1,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.log2(x)
        assert np.log2(test) == admath.log2(test)
        assert np.log2(test) == f.val
        assert 1/(test*np.log(2)) == f.der

def test_log10_fn__():
    tests = np.linspace(1,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.log10(x)
        assert np.log10(test) == admath.log10(test)
        assert np.log10(test) == f.val
        assert 1/(test*np.log(10)) == f.der

def test_logb__():
    tests = np.linspace(1,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.logb(x, np.exp(1))
        assert np.log(test) == admath.logb(test, np.exp(1))
        assert np.log(test) == f.val
        assert 1/test == f.der

def test_exp_fn__():
    tests = np.linspace(-100,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.exp(x)
        assert np.exp(test) == admath.exp(test)
        assert np.exp(test) == f.val
        assert np.exp(test) == f.der

def test_power1():
    x = Dual(2)
    f = admath.power(x,x)
    assert f.der == 2*(2+2*np.log(2))
    assert f.val == 4.0
    assert admath.power(2,2) == 4.0
    f = admath.power(x,2)
    assert f.val == 4
    assert f.der == 4
    f = admath.power(2,x)
    assert f.val == 4.0
    assert f.der == np.log(2) * 2 ** 2

def test_sqrt_fn__():
    tests = np.linspace(1,100,15)
    for test in tests:
        x = Dual(test)
        f = admath.sqrt(x)
        assert np.sqrt(test) == admath.sqrt(test)
        assert np.sqrt(test) == f.val
        assert 1/2*1/np.sqrt(test) == f.der

def test_arcsin():
    x = Dual(0)
    f = admath.arcsin(x)
    assert 1 == f.der
    assert 0 == f.val
    assert 0 == admath.arcsin(0)

def test_arccos():
    x = Dual(0)
    f = admath.arccos(x)
    assert -1 == f.der
    assert np.pi/2 == f.val
    assert np.pi/2 == admath.arccos(0)

def test_arctan():
    x = Dual(0)
    f = admath.arctan(x)
    assert 1 == f.der
    assert 0 == f.val
    assert 0 == admath.arctan(0)

def test_sinh():
    x = Dual(0)
    f = admath.sinh(x)
    assert 1 == f.der
    assert 0 == f.val
    assert 0 == admath.sinh(0)

def test_cosh():
    x = Dual(0)
    f = admath.cosh(x)
    assert 0 == f.der
    assert 1 == f.val
    assert 1 == admath.cosh(0)

def test_tanh():
    x = Dual(0)
    f = admath.tanh(x)
    assert 1 == f.der
    assert 0 == f.val
    assert 0 == admath.tanh(0)

def test_logistic():
    x = Dual(1)
    f = admath.logistic(x)
    assert 0.25 == f.der
    assert 0.50 == f.val
    assert 0.50 == admath.logistic(1)

def test_logistic():
    x = Dual(1)
    f = admath.logistic(x)
    assert 0.25 == f.der
    assert 0.50 == f.val
    assert 0.50 == admath.logistic(1)

def test_sum():
    x = Dual(1)
    f = admath.sum([x**2, x**3])
    assert 5 == f.der
    assert 2 == f.val
    assert 5 == admath.sum([2,3])

def test_abs():
    x = Dual(-1)
    f = admath.abs(x**3)
    assert -1 == f.der
    assert 1 == f.val
    assert 1 == admath.abs(-1)