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

from cg_utils import build_plot, setup_logger

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

class NonLinearConjugate:
    MAX_LINE_SEARCH_ATTEMPTS = 20

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

        self._f = None
        # self._gradient = None

        self.calc_beta = None  # type: Callable

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

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

        self._TOL = 5 * 10 ** -8

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

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

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

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

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

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

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

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

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

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

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

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

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

            prev_alpha = alpha
            alpha *= 2
        assert False

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

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

            phi_alpha = self._phi(alpha_m)

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

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

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

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

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

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

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

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

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

        grad_np = np.array(grad)
        return grad_np

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


#!/usr/bin/python2

# Not very optimized

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

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

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

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

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

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

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

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

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

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

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

    iterations += 1

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

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


import numpy as np

# TODO Remove 'return' in function names

def normal(mean : float, deviation : float, shape : tuple = None):
    """
    Draws samples from the normal distribution defined by arguments.

    Input parameters:
    - mean: float, mean of the normal distribution.
    - deviation: float, standard deviation of the normal distribution.
    - shape (optional): tuple, shape of the desired output. If omitted, the output is a float.

    Output: float if shape is omitted. Otherwise, nested list of required shape, whose every element is a float.
    These floats are drawn independantly from the normal distribution specified by the input arguments.

    Note: differences with returnStandardNormal.
    - random number generator: this method uses numpy.random.Generator.normal, whereas returnStandardNormal uses numpy.random.normal.
    - output type and shape: this method returns either a float or a nested list of floats of arbitrary shape, whereas returnStandardNormal returns a list containing a single float.
    """

    # Create random number generator
    rng = np.random.default_rng()
    # Draw samples
    sample = rng.normal(mean, deviation, shape)
    # Ensure output type
    if isinstance(sample,np.ndarray):
        sample = sample.astype(float).tolist()
    return sample

def returnStandardNormal(*args):
    """
    Return a normally-distributed variable with given parameters
    """
    return [np.random.normal(args[0], args[1])]

def returnStandardNormalandUniform(*args):
    """
    Return one standard normal random variable and one uniformly distributed
    random variable in the interval [0,1]
    """
    return [np.random.normal(0, 1, 1), np.random.uniform(0,1,1)]

def returnBeta(*args):
    """
    Return a beta distributed random variables with given parameters
    """
    return [np.random.beta(args[0],args[1],1)]

def returnUniform(*args):
    """
    Return one uniformly distributed random variable in the interval [0,1]
    """
    return [np.random.uniform(args[0], args[1], 1)]

def returnRayleigh(*args):
    """
    Return one rayleigh random variable with given parameters
    use numpy.random.rayleigh(scale, size)
    already returns as a numpy array
    """
    return np.random.rayleigh(args[0],size=1)

def returnRayleighAndUniform(*args):
    """
    Return one rayleigh and uniformly distributed random variables
    with given parameters
    """
    rayleigh_rv = np.random.rayleigh(args[0],size=1)[0]
    uniform_rv = np.random.uniform(args[1], args[2], 1)
    return [rayleigh_rv, uniform_rv]

def returnUniformAndNormal(*args):
    """
    Return one integer uniformly distributed random variable and
    one normal random variable
    """
    return [int(np.random.uniform(args[0],args[1])),np.random.normal(args[2], args[3], 1)]

def returnUniformAndTwoNormal(*args):
    """
    Return one integer uniformly distributed random variable and
    two normal random variables
    """
    return [int(np.random.uniform(args[0],args[1])),np.random.normal(args[2], args[3], 1),np.random.normal(args[4], args[5], 1)]

def returnIntegerUniform(*args):
    """
    Return one integer uniformly distributed random variable
    """
    return [int(np.random.uniform(args[0],args[1]))]


from __future__ import division

import numpy as np

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

    No extrapolation is performed.

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

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


import numpy as np

def sine_value(amplitude, arg, gap):
    return amplitude * np.sin(arg + gap)

if __name__ == '__main__':
    num_plot = 100
    num_amp = 10
    num_gap = 8
    plot_interval = 0.1
    # num_data = num_amp * num_gap
    # shape_data = (num_plot, num_data)

    count = 0
    for i_amp in range(num_amp):
        amp = float(i_amp +1) / float(num_amp)
        for i_gap in range(num_gap):
            gap = float(i_gap +1) * 2.0 * np.pi / float(num_gap)
            data = np.array([])
            #.. store sine value
            for i_plot in range(num_plot):
                plot_arg = float(i_plot +1) * plot_interval * np.pi
                data = np.append(data, sine_value(amp, plot_arg, gap))

            #.. write stored data
            column = np.atleast_2d(data).T
            plot_data = np.empty((num_plot, 0), float)
            plot_data = np.append(plot_data, column, axis=1) ## input data
            plot_data = np.append(plot_data, column, axis=1) ## target data
            filename = 'sine_{}.dat'.format(count)
            np.savetxt(filename, plot_data, comments='#')

            #.. add comment
            with open(filename) as f:
                l = f.readlines()
            ## add coment at the first line
            l.insert(0, '#.. amplitude = {}, gap = {}pi\n'.format(amp, \
                         float(i_gap +1) * 2.0 / float(num_gap)))
            with open(filename, mode='w') as f:
                f.writelines(l)

            count += 1


import numpy as np
import matplotlib.pyplot as plt
from kneed import KneeLocator
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler

# Getting the data from the csv file, casting local/cloud to 0/1
datafile = "xd.csv"
data = np.genfromtxt(
    datafile,
    delimiter=",",
    skip_header=1,
    converters={0: lambda s: 0 if s == b'local' else 1}
)

# Converting the data to np 2d array
data2d = np.array(list(map(list, data)))

# Splitting the data into local/cloud, dropping the last column (constant)
data_local = data2d[data2d[:, 0] == 0, 1:5]
data_cloud = data2d[data2d[:, 0] == 1, 1:5]

# Scaling the data to mean=0, std=1 to enable k-means
scaler = StandardScaler()
scaled_data_local = scaler.fit_transform(data_local)
scaled_data_cloud = scaler.fit_transform(data_cloud)

# Following code allows to choose the best number of clusters through the elbow method
# and silhouette coefficient. Due to nondeterministic character of the algorithm
# it is advised to repeat this code a couple of times for consistent conclusions.
kmeans_kwargs = {
    "init": "random",
    "n_init": 10,
    "max_iter": 300
}

sse = []
for k in range(1, 11):
    kmeans = KMeans(n_clusters=k, **kmeans_kwargs)
    kmeans.fit(scaled_data_local)
    sse.append(kmeans.inertia_)

plt.style.use("fivethirtyeight")
plt.plot(range(1, 11), sse)
plt.xticks(range(1, 11))
plt.xlabel("Number of Clusters")
plt.ylabel("SSE")
plt.show()

kl = KneeLocator(
    range(1, 11), sse, curve="convex", direction="decreasing"
)
print("Local data elbow point: ", kl.elbow)

silhouette_coefficients = []
for k in range(2, 11):
    kmeans = KMeans(n_clusters=k, **kmeans_kwargs)
    kmeans.fit(scaled_data_local)
    score = silhouette_score(scaled_data_local, kmeans.labels_)
    silhouette_coefficients.append(score)

plt.style.use("fivethirtyeight")
plt.plot(range(2, 11), silhouette_coefficients)
plt.xticks(range(2, 11))
plt.xlabel("Number of Clusters")
plt.ylabel("Silhouette Coefficient")
plt.show()

# The same as above, but for cloud data.
sse = []
for k in range(1, 11):
    kmeans = KMeans(n_clusters=k, **kmeans_kwargs)
    kmeans.fit(scaled_data_local)
    sse.append(kmeans.inertia_)

plt.style.use("fivethirtyeight")
plt.plot(range(1, 11), sse)
plt.xticks(range(1, 11))
plt.xlabel("Number of Clusters")
plt.ylabel("SSE")
plt.show()

kl = KneeLocator(
    range(1, 11), sse, curve="convex", direction="decreasing"
)
print("Cloud data elbow point: ", kl.elbow)

silhouette_coefficients = []
for k in range(2, 11):
    kmeans = KMeans(n_clusters=k, **kmeans_kwargs)
    kmeans.fit(scaled_data_local)
    score = silhouette_score(scaled_data_local, kmeans.labels_)
    silhouette_coefficients.append(score)

plt.style.use("fivethirtyeight")
plt.plot(range(2, 11), silhouette_coefficients)
plt.xticks(range(2, 11))
plt.xlabel("Number of Clusters")
plt.ylabel("Silhouette Coefficient")
plt.show()

# For both datasets, the silhouette coefficient tends to drop heavily at k=4
# (rarely at k=5 or k=6), the elbow point tends to be at k=3 (rarely at k=4 or k=5),
# so it seems wise to choose k=3. However, as we use the clustering for anomaly
# detection, it is convenient to have k a little bit higher, so we get outliers
# in their own small clusters. Therefore, k=6 was chosen.


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

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

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

if __name__ == '__main__':

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

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

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

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

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

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

    plt.legend()
    plt.show()

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


import numpy as np
import matplotlib.pyplot as plt

import torch

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

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

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

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

    return exp_quad

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return means, stds

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

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

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

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

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

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

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

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

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

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

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

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

    plt.show()

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

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

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

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

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

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

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

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

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

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

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

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

    plt.show()


import numpy as np

from gradient import gradient
from sigmoid import sigmoid

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

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

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

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

        hyper_p : float
            Value of the hyperparameter for regularization.

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

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

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

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

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


from matplotlib import pyplot as plt
import numpy as np

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

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

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

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

    return hand_traj

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

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

if __name__ == '__main__':
    plot_servo()


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

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

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

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

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

for temperature in temps:

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

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

    # Square of difference
    squared_difference = difference ** 2

    squared_differences.append(squared_difference)

# Calculate VARIANCE
average_squared_difference = numpy.mean(squared_differences)

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

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

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


import numpy as np

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

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


import numpy as np

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

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

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

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

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

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

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

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

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

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

    return elevation

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

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

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

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

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

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


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

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

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

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

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

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

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


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

def linear_kernel(x1, x2):

    return np.dot(x1, x2)

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

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

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

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

class SVM(object):

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

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

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

        self.kfunc = kfunc

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

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

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

        return K

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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


import numpy as np
import matplotlib.pyplot as plt

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

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

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

    z = complex(g, s)

    return(z)

def plotphasor(z):

    plt.figure()

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

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

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

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


import numpy as np
import matplotlib.pyplot as plt

np.random.seed(1234567890)

"""
Bikin model untuk y = 2 + x + 0.5x^2 + Gaussian noise
"""
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)

#plt.scatter(X,y)
#plt.show()

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures

"""
Regresi linier
"""
lin_reg = LinearRegression()
lin_reg.fit(X, y)

X_points = np.array([[-3], [3]])
y_lin = lin_reg.predict(X_points)

"""
Regresi polinomial
-------------------
Biasanya menggunakan ekstraksi fitur:
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
lin_poly = LinearRegression()
lin_poly.fit(X_poly, y)
print("Estimasi parameter: ", lin_poly.intercept_, lin_poly.coef_)
y_poly = lin_poly.predict(X_poly)
"""

from sklearn.pipeline import Pipeline
polynomial_regression = Pipeline((\
   ("poly_features", PolynomialFeatures(degree=10, include_bias=False)),\
   ("sgd_reg", LinearRegression()),\
))
polynomial_regression.fit(X,y)
X_test = [[i] for i in np.linspace(-3,3,m)]
y_poly = polynomial_regression.predict(X_test)

plt.plot(X,y,"g.")
plt.plot(X_points, y_lin, "r-")
plt.plot(X_test, y_poly, "b.")
plt.legend(['Data','Model Linier','Model Polinomial'])
plt.show()


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

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

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

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

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

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

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

    return R

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

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

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

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

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

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

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

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

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

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

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

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

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

    return corrR

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

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

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

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

    Example
    -------

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

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

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

    a = alphaFunc(p)

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

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

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

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

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

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

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

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

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

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


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

# Imports
import numpy as np
import tensorflow as tf

tf.logging.set_verbosity(tf.logging.INFO)

# Our application logic will be added here

def cnn_model_fn(features, labels, mode):
    #Input layer

    input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])

    #Conv1

    conv1 = tf.layers.conv2d(
                            inputs= input_layer,
                            filters = 32,
                            kernel_size = 5,
                            padding = "same",
                            activation = tf.nn.relu
                             )

    pool1 = tf.layers.max_pooling2d(
                                    inputs = conv1,
                                    pool_size = [2, 2],
                                    strides = 2
                                     )

    #Conv 2

    conv2 = tf.layers.conv2d(
                            inputs = pool1,
                            filters = 64,
                            kernel_size = 5,
                            padding = "same",
                            activation = tf.nn.relu
                            )
    pool2 = tf.layers.max_pooling2d(
                                    inputs = conv2,
                                    pool_size = [2,2],
                                    strides = 2
                                    )
    #Dense layers

    pool_flat = tf.reshape(pool2, [-1, 7*7*64])
    dense1 = tf.layers.dense(
                            inputs = pool_flat,
                            units = 1024,
                            activation = tf.nn.relu
                            )
    dropout = tf.layers.dropout(
                            inputs = dense1,
                            rate = 0.4,
                            training = mode == tf.estimator.ModeKeys.TRAIN

    )
    logits  = tf.layers.dense(
                            inputs = dropout,
                            units = 10
                            )

    predictions = {
                    "classes": tf.argmax(input = logits, axis = 1),
                    "probabilites" : tf.nn.softmax(logits, name = 'softmax')
    }
    if mode == tf.estimator.ModeKeys.PREDICT:
        return tf.estimator.EstimatorSpec(mode = mode, predictions = predictions)

    loss = tf.losses.sparse_softmax_cross_entropy(labels = labels, logits = logits)

    if mode == tf.estimator.ModeKeys.TRAIN:
        optimizer = tf.train.AdamOptimizer(learning_rate = 0.001)
        train_op = optimizer.minimize(
                                    loss = loss,
                                    global_step = tf.train.get_global_step()
                                    )
        return tf.estimator.EstimatorSpec(mode = mode,
                                        loss = loss,
                                         train_op = train_op)
    eval_metric_ops = {
        "accuracy" : tf.metrics.accuracy(
                                        labels = labels,
                                        predictions = predictions["classes"])
                                        }

    return tf.estimator.EstimatorSpec(
                                    mode = mode,
                                    loss = loss,
                                    eval_metric_ops = eval_metric_ops)
def main(unused_argv):
    #Data
    mnist = tf.contrib.learn.datasets.load_dataset("mnist")
    train_data = mnist.train.images # Returns np.array
    train_labels = np.asarray(mnist.train.labels, dtype=np.int32)
    eval_data = mnist.test.images # Returns np.array
    eval_labels = np.asarray(mnist.test.labels, dtype=np.int32)

    mnist_classifier = tf.estimator.Estimator(
                model_fn=cnn_model_fn,
                model_dir="Users/shiki/desktop/mnist_convnet_model")

    tensors_to_log = {"probabilities": "softmax"}

    logging_hook = tf.train.LoggingTensorHook(
                    tensors=tensors_to_log,
                    every_n_iter=50)

    train_input_fn = tf.estimator.inputs.numpy_input_fn(
                        x={"x": train_data},
                        y=train_labels,
                        batch_size=100,
                        num_epochs=None,
                        shuffle=True)
    mnist_classifier.train(
                input_fn=train_input_fn,
                steps=100,
                hooks=[logging_hook])

    eval_input_fn = tf.estimator.inputs.numpy_input_fn(
                                x={"x": eval_data},
                                y=eval_labels,
                                num_epochs=1,
                                shuffle=False)
    eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn)
    print(eval_results)

if __name__ == "__main__":
    tf.app.run()


import io_csv
import math
import numpy

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

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

coords=[]

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

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


import numpy as np
from matplotlib import pyplot as plt

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

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

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


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

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

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

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

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


import math
from random import seed, random

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

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

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

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

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

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

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

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

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

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

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


import math

import matplotlib.pyplot as plt
import numpy as np

from matplotlib import cm

print(np.array([1,3,2,5]))

x = np.array([1,6,2])
y = np.array([1,4,3])

print(x, x.shape, x.size)
print(y, x.shape, x.size)
print(x + y)

m = np.array([[1, 2], [3, 4]])
print(m)
print(m**(0.5))

x = np.random.normal(size=50)
y = x + np.random.normal(size=50, loc=50, scale=0.1)
r = np.corrcoef(x, y)[0][1]
print(r)
a, b = np.polyfit(x, y, deg=1)
y_est = a * x + b
plt.plot(x, y, 'r+')
plt.plot(x, y_est)
plt.show()

x = np.arange(1, 11)
print(x)

a = np.linspace(-math.pi, math.pi)
X, Y = np.meshgrid(a, a)
Z = np.cos(Y) / (1 + X**2)
plt.contour(X, Y, Z)
plt.show()
plt.contour(X, Y, Z, 45)
plt.show()

ZA = (Z - np.transpose(Z)) / 2
plt.contour(X, Y, ZA, 15)
plt.show()
plt.imshow(ZA)
plt.show()

fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, ZA, cmap=cm.coolwarm, lw=0, antialiased=False)
plt.show()

A = np.transpose(np.arange(1, 17).reshape(4,4))
print(A)
print(A[1,2])
print(A[(0,2),:][:,(1,3)])
print(A[0:3,:][:,1:])
print(A[:2])
print(A[:,:2])
print(A[0])


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

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

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

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

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

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

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


import numpy as np

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

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

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

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

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

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

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

def body2earth(angles):

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

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

    tan_theta = T(angles[1])

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

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

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


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

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

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

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

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

print(DCT_mat)


import numpy as np
import math
from math import inf

from numpy.core.fromnumeric import sort

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

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

    return a,b,c

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

        return True, x_int
    else:
        return False, 0

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

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

    res = 0

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

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

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

    return res

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

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

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

    if show:
        return beg, end
    return res

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

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

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

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

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

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

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

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

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

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

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

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

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

    return np.array(sorted_data)

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

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

    else:
        return sorted_data

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

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

    return sorted_data

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

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

    return new

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

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

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

    return sorted_data

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

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

    sorted_data = append_md(sorted_data, md, sim)

    return sorted_data

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

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

    return new

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

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

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

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

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

    return [angle, lenvec]

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

    return sort

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

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

    return res

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

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

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

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

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

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

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

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

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

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

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

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

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

    return new

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

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

    return route

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

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

    return path


from sklearn.metrics import jaccard_score
from sklearn.metrics import f1_score
from sklearn.metrics import accuracy_score

from sklearn.linear_model import LogisticRegression
import numpy as np

def _logisticregression(*, train, test, x_predict=None, metrics, penalty='l2', dual=False, tol=0.0001, C=1.0, fit_intercept=True,
intercept_scaling=1, class_weight=None, random_state=None, solver='lbfgs', max_iter=100, multi_class='auto', verbose=0,
 warm_start=False, n_jobs=None, l1_ratio=None):
    """For for info visit :
        https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression
    """

    model = LogisticRegression(penalty=penalty, dual=dual, tol=tol, C=C, fit_intercept=fit_intercept, intercept_scaling=intercept_scaling,
class_weight=class_weight, random_state=random_state, solver=solver, max_iter=max_iter, multi_class=multi_class, verbose=verbose,
warm_start=warm_start, n_jobs=n_jobs, l1_ratio=l1_ratio)
    model.fit(train[0], train[1])
    model_name = 'Logistic Regression'
    y_hat = model.predict(test[0])

    if metrics == 'f1_score':
        accuracy = f1_score(test[1], y_hat)
    if metrics == 'jaccard_score':
        accuracy = jaccard_score(test[1], y_hat)
    if metrics == 'accuracy_score':
        accuracy = accuracy_score(test[1], y_hat)

    if x_predict is None:
        return (model_name, accuracy, None)

    y_predict = model.predict(x_predict)
    return (model_name, accuracy, y_predict)


import numpy as np

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

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

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

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

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

    return (L, U)

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

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

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

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

    return X_lista


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

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

def get_a_from_f_orb(f_orb, m_1, m_2):

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

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

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

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

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

    return a

def get_f_orb_from_a(a, m_1, m_2):

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

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

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

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

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

    return f_orb

def ensure_array(*args):

    """
    Convert arguments to numpy arrays

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

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

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

def chirp_mass(m_1, m_2):

    """
    Computes chirp mass of binaries

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

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

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

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

    return m_c

def peters_g(n, e):

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

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

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

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

    return g

def peters_f(e):

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

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

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

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

    f = numerator / denominator

    return f


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

# PROBLEM DEFINITION

# Objective cost minimization function

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

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

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

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

    return np.array(position)

# PARAMETERS OF PSO

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

# INITIALIZATION

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

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

# Initialize members of the population

# Initialize the global best
globalBestCost = np.inf

for member in range(npop):

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

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

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

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

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

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

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

# MAIN LOOP OF PSO

for it in range(maxIter):

    for member in range(npop):

        # Update Particle Velocity

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

        # Update Current Particle Position

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

        # Compute the cost of the particle at the new position

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

        # Update the Best Cost and Position

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

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

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

    # Display the iterations

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

    # Update w

    w = w * wdamp

print(globalBestPosition, globalBestCost)

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


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

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

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

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


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

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

NON_ZERO_LOWER_BOUND = 0.01
HALF_PI = math.pi / 2.0

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

# code starts here
df= pd.read_csv(path)

p_a = len(df[df['fico']>700])/(df.shape[0])

p_b = len(df[df['purpose'] == 'debt_consolidation'])/(df.shape[0])

df1= df[df['purpose'] == 'debt_consolidation']

P_a_b = df1[df1['fico'].astype(float) >700].shape[0]/df1.shape[0]

p_a_b= (p_b * P_a_b)/p_a

if P_a_b == p_a:
  result= True
else:
  result= False

print('result :',result)

# code ends here

# --------------
# code starts here

#Calculate the probability p(A)
prob_lp = len(df[df['paid.back.loan'] == 'Yes'])/(df.shape[0])
print('prob_lp:',prob_lp)

#Calculate the probability p(B)
prob_cs = len(df[df['credit.policy'] == 'Yes'])/(df.shape[0])
print('prob_cs :',prob_cs)

new_df = df[df['paid.back.loan'] == 'Yes']

prob_pd_cs = new_df[new_df['credit.policy'] == 'Yes'].shape[0] / new_df.shape[0]
print('prob_pd_cs : ',prob_pd_cs)

bayes = (prob_pd_cs * prob_lp)/ prob_cs

print('bayes : ',bayes)

# code ends here

# --------------
# code starts here

# Visualize the bar plot for the feature purpose
df['purpose'].value_counts().plot.bar()

# Create new df
df1 = df[df['paid.back.loan'] == 'No']

# Visualize the bar plot for the feature purpose on new df
df1['purpose'].value_counts().plot.bar()

# code ends here

# --------------
# code starts here
import matplotlib.pyplot as plt

inst_median= df['installment'].median()
print ('inst_median :',inst_median)

inst_mean= df['installment'].mean()
print('inst_mean : ',inst_mean)

# initialize the fig
fig, (ax_1, ax_2) = plt.subplots(2,1, figsize=(20,10))

df['installment'].plot(kind='hist',ax=ax_1)
ax_1.set_title('histogram for installment')

df['log.annual.inc'].plot(kind='hist',ax=ax_2)
ax_2.set_title('histogram for log.annual.inc')

# code ends here