import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import os

DEFAULT_VALUE = 4

def cart2pol(x, y):
    r = np.sqrt(x**2 + y**2)
    phi = np.arctan2(y, x)
    return (r, phi)

def pol2cart(r, phi):
    x = r * np.cos(phi)
    y = r * np.sin(phi)
    return (x, y)

'''
  Plot an 2D ndarray
'''
def plot(points, centered=2, color=colors.CSS4_COLORS['dodgerblue'], markersize=3, system='cart', saveTo=''):
    x_axis = points[:, 0]
    y_axis = points[:, 1]
    xmin = np.min(x_axis)
    xmax = np.max(x_axis)
    ymin = np.min(y_axis)
    ymax = np.max(y_axis)
    xabsmax = np.max(np.abs(x_axis))
    yabsmax = np.max(np.abs(y_axis))
    if (xmin == -np.inf):
        xmin = -DEFAULT_VALUE
    if (ymin == -np.inf):
        ymin = -DEFAULT_VALUE
    if (xmax == np.inf):
        xmax = DEFAULT_VALUE
    if (ymax == np.inf):
        ymax = DEFAULT_VALUE
    if (xabsmax == np.inf):
        xabsmax = DEFAULT_VALUE
    if (yabsmax == np.inf):
        yabsmax = DEFAULT_VALUE

    ax = plt.subplot(polar=(system == 'pol'))
    #ax.spines['left'].set_position('center')
    ax.plot(x_axis, y_axis, color=color, linestyle='', marker='.', markersize=markersize)

    #plt.axis('equal')
    #plt.gca().set_aspect('equal', adjustable='box')
    #ax.set_xlim([xmin-0.05, xmax+0.05])
    #ax.set_ylim([ymin-0.05, ymax+0.05])
    if (centered == 1):
        ax.set_xlim([-xabsmax, xabsmax])
        ax.set_ylim([ymin, ymax])
    elif (centered == -1):
        ax.set_xlim([xmin, xmax])
        ax.set_ylim([-yabsmax, yabsmax])
    elif (centered == 0):
        ax.set_xlim([-xabsmax, xabsmax])
        ax.set_ylim([-yabsmax, yabsmax])
    else:
        ax.set_xlim([xmin, xmax])
        ax.set_ylim([ymin, ymax])

    if (saveTo != ''):
        print('SAVING ENABLED')
        _, ext = os.path.splitext(saveTo)
        ext = ext.replace('.', '')
        plt.savefig(saveTo, format=ext, dpi=1200)
    else:
        print('SAVING DISABLED')
    plt.show()

def plot1(x, f_x):
    plt.plot(x, f_x, 'r')
    plt.show()

def plot2(x, f_x, U, f_x_inv):
    fig, (ax1, ax2) = plt.subplots(1, 2)
    ax1.plot(x, f_x, 'r')
    ax2.plot(U, f_x_inv, 'b')
    plt.show()

# len(X) % columns == 0
def plotM(X, F, titles=[], columns=2, colors = ['tab:green', 'tab:red', 'tab:blue', 'tab:orange', 'tab:cyan']):
    if (len(X) != len(F)):
        print("ERROR")
        return
    cid = 0
    rows = int(len(X)/columns)
    fig, axs = plt.subplots(rows, columns, num='Distribution functions and their inverses')
    for a in range(0, rows):
        for b in range(0, columns):
            idx = a * columns + b
            if (len(titles) != 0):
                axs[a, b].set_title(titles[idx])
            axs[a, b].plot(X[idx], F[idx], colors[cid])
        cid = (cid + 1) % len(colors)

    plt.tight_layout()


# -*- coding: utf-8 -*-
"""
Created on Sat Feb  6 22:24:26 2016

@author: david
"""
# This is a configuration step for the exercise. Please run it before calculating the derivative!
import numpy as np
import matplotlib.pyplot as plt

# Function for setting up the Chebyshev derivative matrix
def get_cheby_matrix(nx):
    cx = np.zeros(nx+1)
    x = np.zeros(nx+1)
    for ix in range(0,nx+1):
        x[ix] = np.cos(np.pi * ix / nx)

    cx[0] = 2.
    cx[nx] = 2.
    cx[1:nx] = 1.

    D = np.zeros((nx+1,nx+1))
    for i in range(0, nx+1):
        for j in range(0, nx+1):
            if i==j and i!=0 and i!=nx:
                D[i,i]=-x[i]/(2.0*(1.0-x[i]*x[i]))
            else:
                D[i,j]=(cx[i]*(-1)**(i+j))/(cx[j]*(x[i]-x[j]))

    D[0,0] = (2.*nx**2+1.)/6.
    D[nx,nx] = -D[0,0]
    return D

# Initialize arbitrary test function on Chebyshev collocation points
nx = 199     # Number of grid points
x = np.zeros(nx+1)
for ix in range(0,nx+1):
    x[ix] = np.cos(ix * np.pi / nx)
dxmin = min(abs(np.diff(x)))
dxmax = max(abs(np.diff(x)))

# Function example: Gaussian
# Width of Gaussian
s = .2
# Gaussian function (modify!)
f = np.exp(-1/s**2 * x**2)
# Analytical derivative
df_ana = -2/s**2 * x * np.exp(-1/s**2 * x**2)

# Calculate numerical derivative using differentiation matrix
# Initialize differentiation matrix
D = get_cheby_matrix(nx)
df_num = D @ f

# Calculate error between analytical and numerical solution
err = np.sum((df_num - df_ana)**2) / np.sum(df_ana**2) * 100

# Plot analytical and numerical result
plt.plot(x,f,'b',label='f(x)')
plt.plot(x,df_num,'r',label='d/dx f(x) - numerical',alpha=0.6)
plt.plot(x,df_ana,'r--',label='d/dx f(x) - analytical')
plt.xlabel('x')
plt.ylabel('f(x) and d/df f(x)')
plt.title('Error: %s %%'%err)
plt.legend(loc='upper right')
plt.show()


"""
==========================
Iterated Dynamical Systems
==========================

Digraphs from Integer-valued Iterated Functions

Sums of cubes on 3N
-------------------

The number 153 has a curious property.

Let 3N={3,6,9,12,...} be the set of positive multiples of 3.  Define an
iterative process f:3N->3N as follows: for a given n, take each digit
of n (in base 10), cube it and then sum the cubes to obtain f(n).

When this process is repeated, the resulting series n, f(n), f(f(n)),...
terminate in 153 after a finite number of iterations (the process ends
because 153 = 1**3 + 5**3 + 3**3).

In the language of discrete dynamical systems, 153 is the global
attractor for the iterated map f restricted to the set 3N.

For example: take the number 108

f(108) = 1**3 + 0**3 + 8**3 = 513

and

f(513) = 5**3 + 1**3 + 3**3 = 153

So, starting at 108 we reach 153 in two iterations,
represented as:

108->513->153

Computing all orbits of 3N up to 10**5 reveals that the attractor
153 is reached in a maximum of 14 iterations. In this code we
show that 13 cycles is the maximum required for all integers (in 3N)
less than 10,000.

The smallest number that requires 13 iterations to reach 153, is 177, i.e.,

177->687->1071->345->216->225->141->66->432->99->1458->702->351->153

The resulting large digraphs are useful for testing network software.

The general problem
-------------------

Given numbers n, a power p and base b, define F(n; p, b) as the sum of
the digits of n (in base b) raised to the power p. The above example
corresponds to f(n)=F(n; 3,10), and below F(n; p, b) is implemented as
the function powersum(n,p,b). The iterative dynamical system defined by
the mapping n:->f(n) above (over 3N) converges to a single fixed point;
153. Applying the map to all positive integers N, leads to a discrete
dynamical process with 5 fixed points: 1, 153, 370, 371, 407. Modulo 3
those numbers are 1, 0, 1, 2, 2. The function f above has the added
property that it maps a multiple of 3 to another multiple of 3; i.e. it
is invariant on the subset 3N.

The squaring of digits (in base 10) result in cycles and the
single fixed point 1. I.e., from a certain point on, the process
starts repeating itself.

keywords: "Recurring Digital Invariant", "Narcissistic Number",
"Happy Number"

The 3n+1 problem
----------------

There is a rich history of mathematical recreations
associated with discrete dynamical systems.  The most famous
is the Collatz 3n+1 problem. See the function
collatz_problem_digraph below. The Collatz conjecture
--- that every orbit returns to the fixed point 1 in finite time
--- is still unproven. Even the great Paul Erdos said "Mathematics
is not yet ready for such problems", and offered $500
for its solution.

keywords: "3n+1", "3x+1", "Collatz problem", "Thwaite's conjecture"
"""

import networkx as nx

nmax = 10000
p = 3

def digitsrep(n, b=10):
    """Return list of digits comprising n represented in base b.
    n must be a nonnegative integer"""

    if n <= 0:
        return [0]

    dlist = []
    while n > 0:
        # Prepend next least-significant digit
        dlist = [n % b] + dlist
        # Floor-division
        n = n // b
    return dlist

def powersum(n, p, b=10):
    """Return sum of digits of n (in base b) raised to the power p."""
    dlist = digitsrep(n, b)
    sum = 0
    for k in dlist:
        sum += k**p
    return sum

def attractor153_graph(n, p, multiple=3, b=10):
    """Return digraph of iterations of powersum(n,3,10)."""
    G = nx.DiGraph()
    for k in range(1, n + 1):
        if k % multiple == 0 and k not in G:
            k1 = k
            knext = powersum(k1, p, b)
            while k1 != knext:
                G.add_edge(k1, knext)
                k1 = knext
                knext = powersum(k1, p, b)
    return G

def squaring_cycle_graph_old(n, b=10):
    """Return digraph of iterations of powersum(n,2,10)."""
    G = nx.DiGraph()
    for k in range(1, n + 1):
        k1 = k
        G.add_node(k1)  # case k1==knext, at least add node
        knext = powersum(k1, 2, b)
        G.add_edge(k1, knext)
        while k1 != knext:  # stop if fixed point
            k1 = knext
            knext = powersum(k1, 2, b)
            G.add_edge(k1, knext)
            if G.out_degree(knext) >= 1:
                # knext has already been iterated in and out
                break
    return G

def sum_of_digits_graph(nmax, b=10):
    def f(n):
        return powersum(n, 1, b)

    return discrete_dynamics_digraph(nmax, f)

def squaring_cycle_digraph(nmax, b=10):
    def f(n):
        return powersum(n, 2, b)

    return discrete_dynamics_digraph(nmax, f)

def cubing_153_digraph(nmax):
    def f(n):
        return powersum(n, 3, 10)

    return discrete_dynamics_digraph(nmax, f)

def discrete_dynamics_digraph(nmax, f, itermax=50000):
    G = nx.DiGraph()
    for k in range(1, nmax + 1):
        kold = k
        G.add_node(kold)
        knew = f(kold)
        G.add_edge(kold, knew)
        while kold != knew and kold << itermax:
            # iterate until fixed point reached or itermax is exceeded
            kold = knew
            knew = f(kold)
            G.add_edge(kold, knew)
            if G.out_degree(knew) >= 1:
                # knew has already been iterated in and out
                break
    return G

def collatz_problem_digraph(nmax):
    def f(n):
        if n % 2 == 0:
            return n // 2
        else:
            return 3 * n + 1

    return discrete_dynamics_digraph(nmax, f)

def fixed_points(G):
    """Return a list of fixed points for the discrete dynamical
    system represented by the digraph G.
    """
    return [n for n in G if G.out_degree(n) == 0]

nmax = 10000
print(f"Building cubing_153_digraph({nmax})")
G = cubing_153_digraph(nmax)
print("Resulting digraph has", len(G), "nodes and", G.size(), " edges")
print("Shortest path from 177 to 153 is:")
print(nx.shortest_path(G, 177, 153))
print(f"fixed points are {fixed_points(G)}")


from typing import Tuple

import numpy as np

def minmax_normalize(x: 'np.ndarray', t_range: Tuple = (0, 1)):
    """Normalize values in `x` into `t_range`.

    .. note::
        - with `t_range=(0, 1)` will normalize the min-value of the data to 0, max to 1;
        - with `t_range=(1, 0)` will normalize the min-value of the data to 1, max value of the data to 0.

    :param x: the data to be normalized
    :param t_range: a tuple represents the target range.
    :return: normalized data in `t_range`
    """
    min_d = np.min(x)
    max_d = np.max(x)
    a, b = t_range
    return (b - a) * (x - min_d) / (max_d - min_d) + a

def top_k(
    values: 'np.ndarray', k: int, descending: bool = False
) -> Tuple['np.ndarray', 'np.ndarray']:
    """Finds values and indices of the k largest entries for the last dimension.

    :param values: array of distances
    :param k: number of values to retrieve
    :param descending: find top k biggest values
    :return: indices and distances
    """
    if descending:
        values = -values

    if k >= values.shape[1]:
        idx = values.argsort(axis=1)[:, :k]
        values = np.take_along_axis(values, idx, axis=1)
    else:
        idx_ps = values.argpartition(kth=k, axis=1)[:, :k]
        values = np.take_along_axis(values, idx_ps, axis=1)
        idx_fs = values.argsort(axis=1)
        idx = np.take_along_axis(idx_ps, idx_fs, axis=1)
        values = np.take_along_axis(values, idx_fs, axis=1)

    if descending:
        values = -values

    return values, idx


import numpy as np
from scipy import spatial

def function_for_TSP(num_points, seed=None):
    if seed:
        np.random.seed(seed=seed)

    points_coordinate = np.random.rand(num_points, 2)  # generate coordinate of points randomly
    distance_matrix = spatial.distance.cdist(points_coordinate, points_coordinate, metric='euclidean')

    # print('distance_matrix is: \n', distance_matrix)

    def cal_total_distance(routine):
        num_points, = routine.shape
        return sum([distance_matrix[routine[i % num_points], routine[(i + 1) % num_points]] for i in range(num_points)])

    return num_points, points_coordinate, distance_matrix, cal_total_distance

def sphere(p):
    # Sphere函数
    out_put = 0
    for i in p:
        out_put += i ** 2
    return out_put

def schaffer(p):
    '''
    二维函数，具有无数个极小值点、强烈的震荡形态。很难找到全局最优值
    在(0,0)处取的最值0
    -10<=x1,x2<=10
    '''
    x1, x2 = p
    x = np.square(x1) + np.square(x2)
    return 0.5 + (np.square(np.sin(np.sqrt(x))) - 0.5) / np.square(1 + 0.001 * x)

def shubert(p):
    '''
    2-dimension
    -10<=x1,x2<=10
    has 760 local minimas, 18 of which are global minimas with -186.7309
    '''
    x, y = p
    part1 = [i * np.cos((i + 1) * x + i) for i in range(1, 6)]
    part2 = [i * np.cos((i + 1) * y + i) for i in range(1, 6)]
    return np.sum(part1) * np.sum(part2)

def griewank(p):
    '''
    存在多个局部最小值点，数目与问题的维度有关。
    此函数是典型的非线性多模态函数，具有广泛的搜索空间，是优化算法很难处理的复杂多模态问题。
    在(0,...,0)处取的全局最小值0
    -600<=xi<=600
    '''
    part1 = [np.square(x) / 4000 for x in p]
    part2 = [np.cos(x / np.sqrt(i + 1)) for i, x in enumerate(p)]
    return np.sum(part1) - np.prod(part2) + 1

def rastrigrin(p):
    '''
    多峰值函数，也是典型的非线性多模态函数
    -5.12<=xi<=5.12
    在范围内有10n个局部最小值，峰形高低起伏不定跳跃。很难找到全局最优
    has a global minimum at x = 0  where f(x) = 0
    '''
    return np.sum([np.square(x) - 10 * np.cos(2 * np.pi * x) + 10 for x in p])

def rosenbrock(p):
    '''
    -2.048<=xi<=2.048
    函数全局最优点在一个平滑、狭长的抛物线山谷内，使算法很难辨别搜索方向，查找最优也变得十分困难
    在(1,...,1)处可以找到极小值0
    :param p:
    :return:
    '''
    n_dim = len(p)
    res = 0
    for i in range(n_dim - 1):
        res += 100 * np.square(np.square(p[i]) - p[i + 1]) + np.square(p[i] - 1)
    return res

if __name__ == '__main__':
    print(sphere((0, 0)))
    print(schaffer((0, 0)))
    print(shubert((-7.08350643, -7.70831395)))
    print(griewank((0, 0, 0)))
    print(rastrigrin((0, 0, 0)))
    print(rosenbrock((1, 1, 1)))


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

# from google.colab import drive
# drive.mount('/content/drive')

data = pd.read_csv('mtcars.csv')
d = pd.crosstab(index=data['cyl'], columns="count", dropna=True)
print(d)

print(data.info())

#Count Total Null values in each column
print("Total Null Data:", data.isnull().sum())

# Finding the Histogram
# From the given dataset ‘mtcars.csv’, plot a histogram to check the frequency distributi on of the variable ‘mpg’ (Miles per gallon).
plt.hist(data['mpg'], bins=5)
plt.show()

#scatter plot of ‘mpg’ (Miles per gallon) vs ‘wt’ (Weight of car)
plt.scatter(data['mpg'], data['wt'])
plt.show()

#In the dataframe, under the variable gear count total records in each value
df = pd.DataFrame(data, columns=['gear'])
print("Count How many values:\n", df['gear'].value_counts())


try:
    import numpy as _np
    from math import sqrt as _sqrt, log as _log, exp as _exp
    from typing import Union, Tuple, Dict, List
    from univariate._base import Infinite
except Exception as e:
    print(f"some modules are missing {e}")

class Laplace(Infinite):
    """
    This class contains methods concerning Laplace Distirbution [#]_.

    .. math::
        \\text{Laplace}(x;\\mu, \\b) = \\frac{1}{2b} \\exp{- \\frac{|x - \\mu}{b}}

    Args:

        loc(float): loc parameter (:math:`\\mu`)
        scale(float): scale parameter (:math:`b`) where scale > 0
        x(float): random variable

    Reference:
        .. [#] Wikipedia contributors. (2020, December 21). Laplace distribution. https://en.wikipedia.org/w/index.php?title=Laplace_distribution&oldid=995563221
    """

    def __init__(self, loc: float, scale: float):
        if scale < 0:
            raise ValueError('scale should be greater than 0.')

        self.scale = scale
        self.loc = loc

    def pdf(self, x: Union[List[float], _np.ndarray, float]) -> Union[float, _np.ndarray]:
        """
        Args:
            x (Union[List[float], numpy.ndarray, float]): random variable(s)

        Returns:
            Union[float, numpy.ndarray]: evaluation of pdf at x
        """
        mu = self.loc
        b = self.scale

        if isinstance(x, (_np.ndarray, List)):
            if not type(x) is _np.ndarray:
                x = _np.array(x)
            return (1 / (2 * b)) * _np.exp(- _np.abs(x - mu) / b)
        return (1 / (2 * b)) * _exp(- abs(x - mu) / b)

    def cdf(self, x: Union[List[float], _np.ndarray, float]) -> Union[float, _np.ndarray]:
        """
        Args:
            x (Union[List[float], numpy.ndarray, float]): data point(s) of interest

        Returns:
            Union[float, numpy.ndarray]: evaluation of cdf at x
        """
        mu = self.loc
        b = self.scale

        # a generator function is chosen as numpy.exp can handle larger vaues compared to math.exp
        def __generator(mu: float, b: float, x: Union[float, _np.ndarray]) -> Union[float, _np.ndarray]:
            return 0.5 + ((0.5) * _np.sign(x - mu) * (1 - _np.exp(_np.abs(x - mu) / b)))

        if isinstance(x, (_np.ndarray, List)):
            if not type(x) is _np.ndarray:
                x = _np.array(x)
            return __generator(mu, b, x)

        return __generator(mu, b, x)

    def mean(self) -> float:
        """
        Returns: Mean of the Laplace distribution.
        """
        return self.loc

    def median(self) -> float:
        """
        Returns: Median of the Laplace distribution.
        """
        return self.loc

    def mode(self) -> float:
        """
        Returns: Mode of the Laplace distribution.
        """
        return self.loc

    def var(self) -> Union[int, float]:
        """
        Returns: Variance of the Laplace distribution.
        """
        return 2 * pow(self.scale, 2)

    def std(self) -> float:
        """
        Returns: Standard deviation of the Laplace distribution
        """
        return _sqrt(self.var())

    def skewness(self) -> float:
        """
        Returns: Skewness of the Laplace distribution.
        """
        return 0.0

    def kurtosis(self) -> float:
        """
        Returns: Kurtosis of the Laplace distribution.
        """
        return 3.0

    def entropy(self) -> float:
        """
        Returns: differential entropy of the Laplace distribution.

        Reference: Park, S.Y. & Bera, A.K.(2009). Maximum entropy autoregressive conditional heteroskedasticity model. Elsivier.
        link: http://wise.xmu.edu.cn/uploadfiles/paper-masterdownload/2009519932327055475115776.pdf
        """
        return 1 + _log(2*self.scale)

    def summary(self) -> Dict[str, Union[float, str]]:
        """
        Returns:
            Dictionary of Laplace distirbution moments. This includes standard deviation.
        """
        return {
            'mean': self.mean(), 'median': self.median(), 'mode': self.mode(),
            'var': self.var(), 'std': self.std(), 'skewness': self.skewness(), 'kurtosis': self.kurtosis()
        }


import tensorflow as tf
import numpy as np

xy = np.loadtxt('train.txt', unpack=True, dtype='float32')
x_data = xy[0:-1]
y_data = xy[-1]

X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

W = tf.Variable(tf.random_uniform([1, len(x_data)], -1.0, 1.0))

# Our hypothesis
h = tf.matmul(W, X)
hypothesis = tf.div(1., 1. + tf.exp(-h))

# Cost function
cost = -tf.reduce_mean(Y * tf.log(hypothesis) + (1 - Y) * tf.log(1 - hypothesis))

# Minimize
a = tf.Variable(0.1)  # Learning rate, alpha
optimizer = tf.train.GradientDescentOptimizer(a)
train = optimizer.minimize(cost)

# Initialize
init = tf.initialize_all_variables()

# Launch the graph
sess = tf.Session()
sess.run(init)

# Fit the line
for step in xrange(2001):
    sess.run(train, feed_dict={X: x_data, Y: y_data})
    if step % 20 == 0:
        print step, sess.run(cost, feed_dict={X: x_data, Y: y_data}), sess.run(W)

# Test
print '-' * 60
print '# Test'
print '-' * 60
# study hour attendance
print sess.run(hypothesis, feed_dict={X: [[1], [2], [2]]}) > 0.5
print sess.run(hypothesis, feed_dict={X: [[1], [5], [5]]}) > 0.5
print sess.run(hypothesis, feed_dict={X: [[1, 1], [4, 3], [3, 5]]}) > 0.5
print '-' * 60

01-SungKim'sLab/07-Logistic Regression/02-Logistic Regression Test.py


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

x_train = [240000, 139800, 150500, 185530, 176000, 114800, 166800, 89000, 144500, 84000, 82029, 63060, 74000, 97500, 67000, 76025, 48235, 93000, 60949, 65674, 54000, 68500, 22899, 61789]
y_train = [3650, 3800, 4400, 4450, 5250, 5350, 5800, 5990, 5999, 6200, 6390, 6390, 6600, 6800, 6800, 6900, 6900, 6990, 7490, 7555, 7990, 7990, 7990, 8290]

# value = (value - lowest value) <-- set lowest value to 0
# return = 0.% of (value / max value)
def	normalizeElem(list_values, elem):
	return ((elem - min(list_values)) / (max(list_values) - min(list_values)))

# value = (highest value - lowest value) <-- set lowest value to 0
# return = (0.% * value) + lowest value
def	denormalizeElem(list_values, elem):
	return ((elem * (max(list_values) - min(list_values))) + min(list_values))

# info: https://github.com/niektuytel/ML_Algorithms/gradient_descent#scaling
def normalizeData(X, Y):
    new_X = []
    for value in X:
        new_X.append( normalizeElem(X, value) )

    new_Y = []
    for value in Y:
        new_Y.append( normalizeElem(Y, value) )

    return new_X, new_Y

class Linear_Regression():
    # info https://github.com/niektuytel/ML_Algorithms/gradient_descent/tree/main/cost_functions
    def	cost_function(self, loss, X, Y):
        cost = 0.0

        for i in range(len(X)):
            prediction = (loss[1] * X[i] + loss[0])
            cost = cost + (prediction - Y[i]) ** 2

        return (cost / len(X))

    def	boldDriver(self, cost, cost_history, loss, batch_loss, learning_rate, length):
        new_learning_rate = learning_rate
        if len(cost_history) > 1:
            if cost > cost_history[-1]:
                loss = [
                    loss[0] + batch_loss[0] / length * learning_rate,
                    loss[1] + batch_loss[1] / length * learning_rate,
                ]
                new_learning_rate *=  0.5
            else:
                new_learning_rate *= 1.05

        return loss, new_learning_rate

    # info about gradient descent: https://github.com/niektuytel/ML_Algorithms/gradient_descent
    def	gradient_descent(self, loss, cost_history, X, Y, learning_rate, n_epochs):
        batch_loss = [0, 0]
        for coordinateX, coordinateY in zip(X, Y):
            total = (loss[1] * coordinateX + loss[0]) - coordinateY
            batch_loss = [ batch_loss[0] + total, batch_loss[1] + (total * coordinateX)]

        loss = [
            loss[0] - batch_loss[0] / len(X) * learning_rate,
            loss[1] - batch_loss[1] / len(Y) * learning_rate
        ]

        # position slope
        cost = self.cost_function(loss, X, Y)

        # rotation slope
        loss, learning_rate = self.boldDriver(cost, cost_history, loss, batch_loss, learning_rate, n_epochs)
        return loss, cost, learning_rate

    def fit(self, X, y, n_epochs=1000, learning_rate=0.1):
        loss = [0.0, 0.0]
        loss_history = [0.0, 0.0]
        cost_history = []

        for epoch in range(n_epochs):
            loss, cost, learning_rate = self.gradient_descent(loss, cost_history, X, y, learning_rate, n_epochs)
            loss_history.append(loss)
            cost_history.append(cost)

            print(f"\r[{epoch}/{n_epochs}] - loss: {loss}", end="")
        return loss, cost_history, loss_history

if __name__ == "__main__":
    x_normalized, y_normalized = normalizeData(x_train, y_train)

    # define model
    model = Linear_Regression()

    # train model
    loss, cost_history, loss_history = model.fit(x_normalized, y_normalized)

    # fdisplay model results
    lineX = [float(min(x_train)), float(max(x_train))]
    lineY = []
    for elem in lineX:
        elem = loss[1] * normalizeElem(x_train, elem) + loss[0]
        lineY.append(denormalizeElem(y_train, elem))

    # display the linear regression line result
    plt.figure(1)
    plt.plot(x_train, y_train, 'bo', lineX, lineY, 'r-')
    plt.xlabel('x')
    plt.ylabel('y')

    # display cost on the interaction index
    plt.figure(2)
    plt.plot(cost_history, 'r.')
    plt.xlabel('n_epochs')
    plt.ylabel('cost')

    # display
    plt.show()


import numpy as np
from DSvisualizer import DSvisualizer

Etol = 1e-5

def hehe_ymax(e):
    # viete-cardano
    a, b, c, d = 1./3., -0.5, 0., float(e)
    p = (3.*a*c - b**2.)/(3.*a**2.)
    q = (2.*b**3. - 9.*a*b*c + 27.*a**2. *d)/(27.*a**3.)
    k = 1.
    tk = 2.*np.power(-p/3., 0.5, dtype=complex)*np.cos((1./3.)*np.arccos((3.*q*0.5/p)*np.power(-3./p, 0.5, dtype=complex))-2.*np.pi*k/3.)
    y_max = tk-b/(3.*a)
    return np.real(y_max)

def hehe(t, y, par=None):
    # Henon-Heiles galactic potential:
    # y is of the form [x, vx, y, vy]
    v = y[1::2]
    ax1 = - y[0] - 2.*y[0]*y[2]
    ay1 = - y[2] - np.power(y[0],2.) + np.power(y[2],2.)
    return [v[0], ax1, v[1], ay1]

def heheEnergy(y):
    return 0.5*(y[1]**2. + y[3]**2.) + 0.5*(y[0]**2. + y[2]**2.) + y[0]**2. *y[2] - (1./3.)*y[2]**3.

def heheYsample(nTracks, par=None):
    e = par[0]
    y0_ar = []
    yc_ar = np.linspace(0.1, 0.9, int(nTracks))*hehe_ymax(e)
    for i in range(nTracks):
        y = np.zeros(4)
        y[2] = yc_ar[i]
        p = np.sqrt(2.*(e - 0.5*y[2]**2. + (1./3.)*y[2]**3.))
        theta = np.random.uniform(np.pi*0.5, 3.*np.pi*0.5)

        y[1] = p*np.cos(theta)
        y[3] = p*np.sin(theta)
        dE = heheEnergy(y) - e
        if dE > Etol:
            print(y, heheEnergy(y), dE)
            raise Exception("Sampling Energy doesn't match!")
        y0_ar.append(y)
    return np.array(y0_ar)

def hehePick(xy_tuple, par=None):
    e = par[0]
    y = np.zeros(4)
    y[2] = xy_tuple[0]
    y[3] = xy_tuple[1]
    de = e-heheEnergy(y)
    if de >= 0.:
        y[1] = np.sqrt(2.*de)
        return y
    else: return None

def pmapDefault(yp):
    return [yp[2], yp[3]]

def hehePmap(yp):
    return [yp[2], yp[3]]

def heheTraj(y, t=None):
    return [y[0], y[2]]

def hehe3D(y):
    return [y[0], y[2], y[3]]

def heheFFT(y):
    return np.power(y[0],2)+np.power(y[2],2)

hehePar = {
    r'$Energy$': [1./8., (1./100., 1./6.)],
}

hehe_labels = {
    "poincare": (r'$y$',r'$dy/dt$'),
    "trajectory": (r'$x$', r'$y$'),
    "3D": (r'$x$', r'$y$', r'$dy/dt$'),
    "FFT": r'$(x^2+y^2)$',
}

DSvisualizer(hehe, heheYsample, hehePick, 100, hehePmap, heheTraj, hehe3D, heheFFT, (200, 10, 500), hehePar, hehe_labels, maxstep=0.1, vectorized=True)


import numpy as np
def decode(pop, indiv, nloci, minvalu, maxvalu):
    '''
    Convert a binary chromosome from a generation back to decimal parameter values.

    Parameters
    ----------
    pop: np.array.
        A numpy array of binary bits representing the entire generation,
        with each row representing a chromosome.
    indiv: int.
        The row ID of the chromosome of interest.
    nloci: int
        Number of binary bits used to represent each parameter in a chromosome.
    minvalu, maxvalu: list-like.
        The minimum/maximum boundaries (in decimal value) of each parameter.
        "All-0s" in binary form will be converted to the minimum for a
        parameter, and "all-1s" will be converted to the maximum.

    Returns
    -------
    param: np.array.
        A 1D array containing the decimal values of the input parameters.
    '''
    import numpy as np
    minvalu = np.array(minvalu)
    maxvalu = np.array(maxvalu)
    deltavalu=maxvalu-minvalu
    nvars=len(minvalu)
    valdec=np.zeros(nvars)
    param=np.zeros(nvars)
    #   decodes from binary to values between max and min
    for j in range(nvars):
        n=nloci
        for i in range(j*nloci,(j+1)*nloci):
            n=n-1
            valdec[j]+=pop[indiv,i]*(2**n)

        param[j]=minvalu[j]+np.true_divide((deltavalu[j])*(valdec[j]),2**nloci)
    return param


import matplotlib.pyplot as plt
import numpy as np
from numba import jit
from timeit import default_timer as timer
start = timer()

# PARAMETERS TO CHANGE THE FRACTAL GENERATED
seq = "AB"                   # sequence to alternate r values
a_lb = 2                       # b1 lower bound
a_ub = 4                       # b1 upper bound
b_lb = 2                       # b2 lower bound
b_ub = 4                       # b2 upper bound

# PARAMETERS REFINING ACCURACY OF FRACTAL PICTURE GENERATED
num_warmups = 10             # number of "warmups" or throwaway iterations before computing lyapunov exponent
num_lyap_iterations = 100      # number of iterations used to compute the lyapunov exp
steps = 500                   # steps between b1 and b2 values on axes -- higher it is, the better the picture

# LOGISTIC MAP THAT GIVES US THE NEXT X
@jit
def F(x, curr_r):
    return (curr_r * x) * (1 - x)

# DERIVATIVE OF F -- USED TO COMPUTE THE LYAPUNOV EXPONENT
@jit
def Fprime(x, curr_r):
    ans = curr_r * (1 - (2 * x))
    ans[ans == 0] = 0.0001
    ans[ans == -np.inf] = -1000
    ans[ans == np.inf] = 1000
    return ans

# RETURNS THE CORRECT B-VALUE BASED ON THE CURRENT ITERATION
@jit
def getseqval(curr_iteration, a, b):
    index = np.mod(curr_iteration, len(seq))
    if (seq[index] == 'A'):
        return a
    else:
        return b

# RETURNS THE LYAPUNOV EXPONENT BASED ON THE SPECIFIED B1 AND B2 VALUES
@jit
def getlyapexponent(time_scale):
    b1, b2 = time_scale

    x = .5          # initial value of x
    lyap_sum = 0     # initializing lyapunov sum for use later

    # do warmups, to discard the early values of the iteration to allow the orbit to settle down
    for i in range(num_warmups):
        x = F(x, getseqval(i, b1, b2))

    for i in range(num_warmups, num_lyap_iterations + num_warmups):
        #print("lysum", lyapsum, "i", i)

        lyap_sum += np.log( np.abs(Fprime(x, getseqval(i, b1, b2) ) ) )

        # get next x
        x = F(x, getseqval(i, b1, b2))

    return (lyap_sum / num_lyap_iterations)

# CREATING FRACTAL IMAGE
a = np.linspace(a_lb, a_ub, steps)   #range of b1 values
b = np.linspace(b_lb, b_ub, steps)   #range of b2 values
aa, bb = np.meshgrid(a, b)

fractal_grid = getlyapexponent( (bb, aa) )

# CREATING AND ADJUSTING GRAPH
plt.figure()       # creating new window for each graph that is run
lyap_cmap = plt.get_cmap('nipy_spectral')   # creating our own colormap to use "set_over" with
lyap_cmap.set_over('black')                 # any value over vmax is colored black
lyap_cmap.set_under('#5e1d77')              # any value under vmin is colored dark purple
plt.suptitle("Lyanpuov fractal for logistic map")
plt.title("Seq: " + seq)
plt.xlabel("a")
plt.ylabel("b")

im = plt.imshow(fractal_grid, cmap = lyap_cmap, vmin = -2, vmax = 0, origin = "lower", extent = (a_lb, a_ub, b_lb, b_ub))
plt.colorbar(im)

end = timer()
print("elapsed time: " + str(end - start))


"""
File containing different normalization methods
"""

import numpy as np

def metnet_normalization(data: np.ndarray) -> np.ndarray:
    """
    Perform normalization from the MetNet paper

    This involves subtracting by the median, dividing by the interquartile range,
    then squashing to [-1,1] with the hyperbolic tangent

    Args:
        data: input image data

    Returns:
        Normalized image data
    """
    # Ensure no NaNs
    data = np.nan_to_num(data)
    # Get IQR
    iqr = np.subtract(*np.percentile(data, [75, 25]))
    data = (data - np.median(data)) / iqr
    #
    # Now hyperbolic tangent
    # data = np.tanh(data)
    # TODO tanh seems to give an issue of not forcing between -1 and 1,
    #  but gives between ~-0.76 and 0.76
    return np.clip(data, -1.0, 1.0)

def standard_normalization(data: np.ndarray, std: np.ndarray, mean: np.ndarray) -> np.ndarray:
    """
    Performs standard normalization to get values with a mean of 0 and standard deviation of 1

    Args:
        data: The data to normalize
        std: Standard deviation of each channel
        mean: Mean of each channel

    Returns:
        The normalized data
    """
    return (data - mean) / std


import numpy as np

from quara.math import norm

def test_l2_norm():
    x = np.array([1.0, 2.0], dtype=np.float64)
    y = np.array([4.0, 6.0], dtype=np.float64)

    actual = norm.l2_norm(x, y)
    assert actual == 5.0


import numpy as np

'''
    SVD é a representação de uma matriz A em um produto de três matrizes: U, S e Vt

    U: Matriz m x m que são os autovetores de A*At normalizados
    S: Matriz diagonal m x n que contém a raiz quadrada dos autovalores de U e V em ordem decrescente
    V: Matriz n x n que são os autovetores de At*A normalizados
'''

def calcula_sigma(w1, w2, n, m): # Função que ajusta a matriz sigma e já a retorna com o número certo de linhas e colunas, com os autovalores ordenados na diagonal principal, do maior para o menor
    matriz_S = np.zeros((n, m)) # Cria a matriz sigma com dimensão n x m (necessário para conseguir fazer a multiplicação USV) com todos os termos iguais a zero

    for i in range (len(w2)): # Junta todos os autovalores diferentes de zero encontrados de A*At e At*A no array w1
        if w2[i] != 0:
            w1 = np.append(w1, np.round(w2[i])) # Coloquei pra arredondar os valores porque ele tava dando como diferente 9.99999995 e 10 *emoji de palhaço*

    w1 = np.unique(w1) # Elimina os valores repetidos no array w1

    for i in range (n):
        matriz_S[i][i] = np.sqrt(w1[n-1-i]) # Adiciona os autovalores do maior para o menor na diagonal principal da matriz sigma

    return matriz_S # Retorna a matriz sigma

matriz_A = np.loadtxt("dados/matriz_A.txt", dtype='f8') # Carrega a matriz que eu estava usando de exemplo

A_At = np.matmul(matriz_A, np.transpose(matriz_A)) # A_At é a multiplicação de A por A(transposta)
At_A = np.matmul(np.transpose(matriz_A), matriz_A) # At_A é a multiplicação de A(transposta) por A

w1, matriz_U = np.linalg.eig(A_At) # Aqui eu utilizo a função da própria numpy que retorna os autovalores (w1) e autovetores (matriz U já normalizada) de A*At
w2, matriz_V = np.linalg.eig(At_A) # Aqui eu utilizo a função da própria numpy que retorna os autovalores (w1) e autovetores (matriz V já normalizada) de At*A

matriz_S = calcula_sigma(w1, w2, len(matriz_A), len(matriz_A[0])) # Chamei a função que cria a matriz sigma com os autovalores encontrados em w1 e w2

# Mostra os resultados encontrados pelo que eu fiz acima
print("Matriz U: \n", matriz_U, "\n")
print("Matriz S: \n", matriz_S, "\n")
print("Matriz Vt: \n", np.transpose(matriz_V))
print("\n", "-"*60, "\n")

# Aqui eu já utilizo diretamente a função SVD da numpy
U, s, V = np.linalg.svd(matriz_A)
S = np.diag(s)
Vt = np.transpose(V)

# Mostra os resultados da função SVD da numpy
print("Matriz U (numpy): \n", U, "\n")
print("Matriz S (numpy): \n", S, "\n")
print("Matriz Vt (numpy): \n",Vt, "\n")
print("\n", "-"*60, "\n")

numpy_US = np.matmul(U, matriz_S) # Multiplica U por S
numpy_USVt = np.matmul(numpy_US, V) # Multiplica (U*S) por Vt

matriz_US = np.matmul(matriz_U, matriz_S)
matriz_USVt = np.matmul(matriz_US, np.transpose(matriz_V))

print("Matriz_A: \n", matriz_A)
print("Matriz USVt = A(pedro): \n", matriz_USVt)
print("Matriz USVt = A(numpy): \n", numpy_USVt) # O resultado deveria ser a matriz A, mas tá diferente (porém próximo)


import numpy as np
import matplotlib.pyplot as plt
import scipy.fft

def sinWav(amp, freq, time, phase=0):
    return amp * np.sin(2 * np.pi * (freq * time - phase))

def plotFFT(f, speriod, time):
    """Plots a fast fourier transform

    Args:
        f (np.arr): A signal wave
        speriod (int): Number of samples per second
        time ([type]): total seconds in wave
    """

    N = speriod * time
    # sample spacing
    T = 1.0 / 800.0
    x = np.linspace(0.0, N*T, N, endpoint=False)

    yf = scipy.fft.fft(f)
    xf = scipy.fft.fftfreq(N, T)[:N//2]

    amplitudes = 1/speriod* np.abs(yf[:N//2])

    plt.plot(xf, amplitudes)
    plt.grid()
    plt.xlim([1,3])
    plt.show()

def main():
    srate = 800
    time  = {
        0: np.arange(0, 4, 1/srate),
        1: np.arange(4, 8, 1/srate),
        2: np.arange(8, 12, 1/srate)
    }

    signal = np.concatenate([
        sinWav(amp=0.25, freq=2, time=time[0]),
        sinWav(amp=1, freq=2, time=time[1]),
        sinWav(amp=0.5, freq=2, time=time[2])
    ])   # generate signal

    plotFFT(signal, speriod, 12)

main()


import jax.numpy as jnp
from jax import grad, jit, vmap
from jax import jacfwd, jacrev
from jax import random
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt
from visualizaiton import Visualization

class UnconstrainedOptimization:
    def __init__(self, cost, direction_type, stepsize_type, x0,
                 termination_params, algo_params=None):
        self.cost = cost
        self.x0 = jnp.array(x0)
        self.direction_type = direction_type
        self.stepsize_type = stepsize_type
        self.algo_params = algo_params
        self.termination_params = termination_params

    def armijo_condition(self, alpha, sigma, x_current, x_next, direction):
        LHS = self.cost(x_current) - self.cost(x_next)
        RHS = - sigma * alpha * jnp.matmul(grad(self.cost)(x_current), direction)
        condition = True if (LHS >= RHS) else False
        return condition

    def get_step_size(self, x_current, direction):

        if self.stepsize_type == 'minimization_rule':
            # create symbolic step size: alpha
            alpha = sym.Symbol('alpha')
            # convert jax device array to numpy list
            x_current = x_current.tolist()
            direction = direction.tolist()
            sym_vec = []
            for d in range(len(direction)):
                mm = x_current[d] + alpha * direction[d]
                sym_vec.append(mm)
            sym_cost = self.cost(sym_vec)
            sym_derivative = sym.diff(sym_cost, alpha)
            alpha_sym = sym.solveset(sym_derivative, alpha)
            alpha_sym = list(alpha_sym)
            print(alpha_sym)

            # only keep the real number by checking data type
            i = 0
            while True:
                condition = type(alpha_sym[i] ** 2) is sym.numbers.Float
                if condition is True:
                    val = alpha_sym[i]
                    break
                else:
                    i += 1

            alpha = float(val)

        elif self.stepsize_type == 'constant':
            alpha = self.algo_params[2]

        elif self.stepsize_type == 'armijo_rule':
            beta, s, sigma = self.algo_params
            m = 0
            condition = False
            while True:
                alpha = pow(beta, m) * s
                x_next = x_current + alpha * direction
                condition = self.armijo_condition(alpha, sigma, x_current, x_next, direction)

                if condition is True:
                    break
                m += 1
        else:
            raise Exception(
                'Unknown step size type, please input one of the following: minimization_tule, constant, armijo rule')

        return alpha

    def get_gradient_direction(self, x_val):
        # get derivative of the cost function
        derivative = grad(self.cost)(x_val)

        if self.direction_type == 'steepest_descent':
            # D in case of steepest descent is identity matrix
            D = jnp.identity(len(x_val))
            direction = - jnp.matmul(D, derivative)

        elif self.direction_type == 'newton_method':
            hessian = jacfwd(jacrev(self.cost))(x_val)
            D = jnp.linalg.inv(hessian)
            direction = - jnp.matmul(D, derivative)

        else:
            raise Exception(
                'Unknown gradient based method, please input one of the following: '
                'steepest_descent, newton_method')

        return direction

    def update(self, x_current, X_opt, error_val, error_cache):

        # update states
        X_opt.append(np.asarray(x_current, dtype=float))
        direction = self.get_gradient_direction(x_current)
        step_size = self.get_step_size(x_current, direction)
        x_next = x_current + step_size * direction
        error_val = x_current - x_next
        error_norm = jnp.linalg.norm(error_val)
        error_cache.append(error_norm)

        return x_next, X_opt, error_norm, error_cache

    def run_algorithm(self):
        """
        Runs the algorithm provided the user with starting and termination conditions,
        If the termination condition is "convergence" then the algorithm runs until convergence
        is achieved upto the desired value, the other termination option is to run the algorithm for
        a fixed number of timesteps.
        :return: X_opt = a matrix with co-ordinates of all the states obtained from running the algorithm
        """

        # get termination conditions from algo_params
        termination_type, termination_condition = self.termination_params

        x_current = self.x0
        X_opt = []
        error_norm = np.inf
        error_cache = []
        fig = plt.figure()

        if termination_type == "convergence":
            while error_norm > termination_condition:
                x_next, X_opt, error_norm, error_cache = self.update(
                    x_current, X_opt, error_norm, error_cache)
                # update state vector
                x_current = x_next

        elif termination_type == "fixed_steps":
            for t in range(termination_condition):
                x_next, X_opt, error_norm, error_cache = self.update(
                    x_current, X_opt, error_norm, error_cache)
                # update state vector
                x_current = x_next

        return X_opt, error_cache

Gradient based methods/Unconstrained optimization/unconstrained_optimization.py

import numpy,import sympy,import jax,from jax


#!/usr/bin/env python3

import numpy as np
from rigid_transform_3D import rigid_transform_3D

# Test with random data

# Random rotation and translation
R = np.random.rand(3,3)
t = np.random.rand(3,1)

# make R a proper rotation matrix, force orthonormal
U, S, Vt = np.linalg.svd(R)
R = U@Vt

# remove reflection
if np.linalg.det(R) < 0:
   Vt[2,:] *= -1
   R = U@Vt

# number of points
n = 10

A = np.random.rand(3, n)
B = R@A + t

# Recover R and t
ret_R, ret_t = rigid_transform_3D(A, B)

# Compare the recovered R and t with the original
B2 = (ret_R@A) + ret_t

# Find the root mean squared error
err = B2 - B
err = err * err
err = np.sum(err)
rmse = np.sqrt(err/n)

print("Points A")
print(A)
print("")

print("Points B")
print(B)
print("")

print("Ground truth rotation")
print(R)

print("Recovered rotation")
print(ret_R)
print("")

print("Ground truth translation")
print(t)

print("Recovered translation")
print(ret_t)
print("")

print("RMSE:", rmse)

if rmse < 1e-5:
    print("Everything looks good!")
else:
    print("Hmm something doesn't look right ...")


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

# Consider two functions,
#	f(x) = x
#   g(x) = x²
#
# Write a program that, by trial and error, finds approximately for which values of x
# the two graphs cross, i.e., f(x) = g(x).
# Do this by considering N equally distributed points in the interval, at each point checking whether:
# |f(x) - g(x)| < ɛ, where ɛ is some small number.
# Let N and ɛ  be user input to the program and let the result be printed to the screen.

# The base case is N = 400 and ɛ = 0.01.

g_xlim = [ -4, 4 ]
g_iter = 1000

def plot_fun( fun, name, col ):
	x_vals = np.linspace( g_xlim[0], g_xlim[1], g_iter, endpoint=True )
	y_vals = fun( x_vals )
	plt.plot( x_vals, y_vals, label = name, color = col )

def f(x):
	return x

def g(x):
	return x**2

plot_fun( f, 'x', 'B' )
plot_fun( g, 'x²', 'G' )

epsilon = 0.0001
N = 40000

v = []

for x in np.linspace( g_xlim[ 0 ], g_xlim[ 1 ], N ):
	delta = abs( f( x ) - g( x ) )

	if delta <= epsilon:
		v.append( x )
		plt.scatter( x, f(x) )

plt.legend()
plt.show()


import string
import numpy as np
from scipy.spatial import distance

def softmax(array):
    """Returns the numerically stable softmax of a given array"""
    return (np.exp(array-np.max(array)))/np.sum(np.exp(array-np.max(array)))

def cosine_similarity(a, b):
    """Custom cosine similarity"""
    return np.dot(a, b)/(np.linalg.norm(a)*np.linalg.norm(b))

def norm_hamming(string1,string2):
    """Custom Normalized Hamming Distance"""
    return distance.hamming(list(string1), list(string2))

def jaccard_binary(x,y):
    """Returns the similarity between two binary vectors"""
    intersection = np.logical_and(x, y)
    union = np.logical_or(x, y)
    similarity = intersection.sum() / float(union.sum())
    return similarity

def jaccard_similarity(doc1, doc2):
    # List the unique words in a document
    words_doc1 = set(doc1.lower().split())
    words_doc2 = set(doc2.lower().split())

    # Find the intersection of words list of doc1 & doc2
    intersection = words_doc1.intersection(words_doc2)

    # Find the union of words list of doc1 & doc2
    union = words_doc1.union(words_doc2)

    # Calculate Jaccard similarity score
    # using length of intersection set divided by length of union set
    return float(len(intersection)) / len(union)


import random
import numpy as np
import pandas as pd
from scipy import optimize

def seed_everything(seed=42):
    random.seed(seed)
    np.random.seed(seed)

def random_weights(num_assets):
    weights = np.random.rand(num_assets)
    return weights / sum(weights)

def stock_returns(data, log_returns=False, annual_days = 252):
    if log_returns:
       daily_returns = data.pct_change().apply(lambda x: np.log(1+x))
       returns = daily_returns.dropna(axis=0, how='all')
       covariance_matrix = returns.cov() * annual_days
    else:
       daily_returns = data.pct_change()
       returns = daily_returns.dropna(axis=0, how='all')
       covariance_matrix = returns.cov() * annual_days
    return (returns * annual_days).mean(), covariance_matrix

def portfolio_performance(weights, mean_returns, covariance_matrix):
    returns = mean_returns @ weights
    portfolio_variance = weights.T @ covariance_matrix @ weights
    portfolio_std = np.sqrt(portfolio_variance)
    return portfolio_std, returns

def neg_sharpe_ratio(weights, mean_returns, covariance_matrix, risk_free_rate=0.02):
    p_var, p_ret = portfolio_performance(weights, mean_returns, covariance_matrix)
    return -(p_ret - risk_free_rate) / p_var

def max_sharpe_ratio(mean_returns, covariance_matrix, risk_free_rate=0.02):
    num_assets = len(mean_returns)
    args = (mean_returns, covariance_matrix, risk_free_rate)
    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bound = (0.0, 1.0)
    bounds = tuple(bound for asset in range(num_assets))
    result = optimize.minimize(neg_sharpe_ratio, num_assets*[1./num_assets,], args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints)
    return [round(it, 3) for it in result.x]

def portfolio_volatility(weights, mean_returns, covariance_matrix):
    return portfolio_performance(weights, mean_returns, covariance_matrix)[0]

def min_volatility(mean_returns, covariance_matrix):
    num_assets = len(mean_returns)
    args = (mean_returns, covariance_matrix)
    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bound = (0.0,1.0)
    bounds = tuple(bound for asset in range(num_assets))
    result = optimize.minimize(portfolio_volatility, num_assets*[1./num_assets,], args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints)
    # summarize the result
    #print('Status : %s' % result['message'])
    return [round(it, 3) for it in result.x]

def efficient_return(mean_returns, covariance_matrix, target_return=0.055):
    """
    Efficient return (minimize risk given a target return)
    """
    num_assets = len(mean_returns)
    args = (mean_returns, covariance_matrix)

    def portfolio_return(weights):
        return portfolio_performance(weights, mean_returns, covariance_matrix)[1]
    constraints = ({'type': 'eq', 'fun': lambda x: portfolio_return(x) - target_return},
                   {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bounds = tuple((0,1) for asset in range(num_assets))
    result = optimize.minimize(portfolio_volatility, num_assets*[1./num_assets,], args=args, method='SLSQP', bounds=bounds, constraints=constraints)
    return [round(it, 3) for it in result.x]

def efficient_frontier(mean_returns, cov_matrix, returns_range):
    efficients = []
    for ret in returns_range:
        efficients.append(efficient_return(mean_returns, covariance_matrix, ret))
    return efficients

def diversification_ratio(weights, mean_returns, covariance_matrix):
    """
    The weighted average of volatility divided by the portfolio volatility
    """
    portfolio_std, returns = portfolio_performance(weights, mean_returns, covariance_matrix)
    weights_std = np.dot(np.sqrt(np.diag(covariance_matrix)), weights.T)
    diversification_ratio = weights_std / portfolio_std
    # return negative for minimization problem (maximize = minimize -)
    return -diversification_ratio

def max_diversification(mean_returns, covariance_matrix, long_only=True):
    """
    Maximum Diversification
    Reference Choueifaty, Yves, and Yves Coignard. 2008. “Toward Maximum Diversification.”
    paper https://www.tobam.fr/wp-content/uploads/2014/12/TOBAM-JoPM-Maximum-Div-2008.pdf
    """
    # long only: long only constraint
    args = (mean_returns, covariance_matrix)
    num_assets = len(mean_returns)
    bounds = tuple((0,1) for asset in range(num_assets))
    cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    if long_only: # add in long only constraint
        cons = ({'type': 'ineq', 'fun':  lambda x: np.sum(x) - 1})
    result = optimize.minimize(diversification_ratio, num_assets*[1./num_assets,],  args=args, bounds=bounds, method='SLSQP', constraints=cons)
    return [round(it, 3) for it in result.x]

def monte_carlo_optimisation(mean_returns, covariance_matrix, num_portfolios, risk_free_rate=0.02):
    """
    Monte Carlo Simulation
    """
    portfolio_returns = [] # Define an empty array for portfolio returns
    portfolio_volatility = [] # Define an empty array for portfolio volatility
    portfolio_weights = [] # Define an empty array for asset weights

    for portfolio in range(num_portfolios):
        weights = random_weights(len(mean_returns))
        volatility, returns = portfolio_performance(weights, mean_returns, covariance_matrix)
        portfolio_volatility.append(volatility)
        portfolio_weights.append(weights)
        portfolio_returns.append(returns)

    data = {'Returns': portfolio_returns, 'Volatility': portfolio_volatility}
    for counter, symbol in enumerate(mean_returns.index.tolist()):
        data[symbol+' weight'] = [w[counter] for w in portfolio_weights]
    # Dataframe of the 10000 portfolios created
    portfolios = pd.DataFrame(data)

    assert int(portfolios.iloc[0,2:].values.sum()) <= 1, "Weights sum should be equal 1"
    portfolios.head()

    minimum_volatility_portfolio = portfolios.iloc[portfolios['Volatility'].idxmin()] # idxmin() gives us the minimum value in the column 'Volatility'.
    max_sharpe_ratio = portfolios.iloc[((portfolios['Returns']- risk_free_rate) / portfolios['Volatility']).idxmax()]

    return minimum_volatility_portfolio, max_sharpe_ratio


import numpy as np
from scipy.stats import skew

from data_describe.compat import _is_series

def spikey(data):
    """Calculates the "spikey-ness" of the histogram.

    Spikeyness is the ratio between the tallest bin and the average bin height.

    Args:
        data: The 1-d data array

    Returns:
        Ratio of the tallest bin height and the average bin height.
    """
    if _is_series(data):
        data = data.dropna()
    else:
        data = data[~np.isnan(data)]
    counts, bins = np.histogram(data, bins="sturges")
    return max(counts) / np.mean(counts)

def skewed(data):
    """Calculates skew.

    Utilizes scipy.stats.

    Args:
        data: The 1-d data array

    Returns:
        The data skew.
    """
    return skew(data)


import sympy as sp
from sympy import cos, sin
from sympy.core.rules import Transform
from sympy import Float

class cVsdkSym(object):
    ''' Direct Kinematic symbolic function generator.  Very symple class meant
    to generate a symbolic sympy expression of the direct kinematic function of
    a robot and its jacobian.
    '''

    def __init__(self):
        self.mij_ = []
        self.m0j_ = []
        self.q_ = []
        self.dim_ = 0
        self.mee = sp.eye(4)

    def add_link(self, _a, _d, _alpha, _theta, _q=None):
        ''' Adds a new link to the robot with given DH
        parameters'''
        self.mij_.append(cDHmatrixSym(_a, _d, _alpha, _theta))
        self.m0j_.append(sp.zeros(4, 4))
        if _q is None:
            qs = sp.symbols('q[{:d}]'.format(len(self.q_)), real=True)
        else:
            qs = _q
        self.q_.append(qs)
        self.dim_ += 1

    def set_tcp_offset(self, _x, _y, _z):
        self.mee[0:3, 3] = _x, _y, _z

    def __call__(self, _q=None):

        if _q is None:
            q = self.q_
        else:
            q = _q

        self.m0j_[0] = self.mij_[0](q[0])

        for i, mij in enumerate(self.mij_[1:], start=1):
            self.m0j_[i] = self.m0j_[i - 1] * mij(q[i])

        res = self.m0j_[-1] * self.mee
        for i in range(3):
            for j in range(4):
                res[i, j] = res[i, j].xreplace(
                    Transform(lambda x: x.round(8),
                              lambda x: isinstance(x, Float)))
        return res

    def __len__(self):
        return len(self.mij_)

    def jac(self, _q=None):

        if _q is None:
            q = self.q_
        else:
            q = _q

        pe = self(q)[:3, -1]
        jac = sp.zeros(6, self.dim_)
        axis = sp.Matrix([0.0, 0.0, 1.0])
        p = sp.Matrix([0.0, 0.0, 0.0])
        for j in range(0, self.dim_):
            jac[:3, j] = axis.cross(pe - p).doit()
            jac[3:, j] = axis
            axis = self.m0j_[j][:3, 2]
            p = self.m0j_[j][:3, -1]

        for i in range(6):
            for j in range(self.dim_):
                jac[i, j] = jac[i, j].xreplace(
                    Transform(lambda x: x.round(8),
                              lambda x: isinstance(x, Float)))

        return jac

class cDHmatrixSym(object):
    ''' Symbolic DH transformation matrix generator '''

    def __init__(self, _a, _d, _alpha, _theta):
        self.buff_ = sp.zeros(4, 4)
        self.theta_ = _theta
        self.alpha_ = _alpha
        self.a_ = _a
        self.d_ = _d
        self.cosal_ = cos(_alpha)
        self.sinal_ = sin(_alpha)

    def __call__(self, _q):
        cth = cos(self.theta_ + _q)
        sth = sin(self.theta_ + _q)
        res = sp.zeros(4, 4)
        res[0, 0] = cth
        res[0, 1] = -sth * self.cosal_
        res[0, 2] = sth * self.sinal_
        res[0, 3] = self.a_ * cth

        res[1, 0] = sth
        res[1, 1] = cth * self.cosal_
        res[1, 2] = -cth * self.sinal_
        res[1, 3] = self.a_ * sth

        res[2, 1] = self.sinal_
        res[2, 2] = self.cosal_
        res[2, 3] = self.d_

        res[3, 3] = 1

        for i in range(3):
            for j in range(4):
                res[i, j] = res[i, j].xreplace(
                    Transform(lambda x: x.round(8),
                              lambda x: isinstance(x, Float)))
        return res

def dh_sym_matrix(_q, _a, _d, _alpha, _theta):
    theta_ = _theta
    alpha_ = _alpha
    a_ = _a
    d_ = _d
    cosal_ = cos(alpha_)
    sinal_ = sin(alpha_)

    cth = cos(theta_ + _q)
    sth = sin(theta_ + _q)
    res = sp.zeros(4, 4)
    res[0, 0] = cth
    res[0, 1] = -sth * cosal_
    res[0, 2] = sth * sinal_
    res[0, 3] = a_ * cth

    res[1, 0] = sth
    res[1, 1] = cth * cosal_
    res[1, 2] = -cth * sinal_
    res[1, 3] = a_ * sth

    res[2, 1] = sinal_
    res[2, 2] = cosal_
    res[2, 3] = d_

    res[3, 3] = 1
    return res


import numpy as np
from  tensorcomlib import base
from  tensorcomlib import tensor
from  sklearn.utils.extmath import randomized_svd
from  tensorcomlib.MatrixSVD import SVD
from matplotlib.pylab import plt
import time

#hosvd
def hosvd(X):
    U = [None for _ in range(X.ndims())]
    dims = X.ndims()
    S = X
    for d in range(dims):
        C = base.unfold(X,d)
        U1,S1,V1 = np.linalg.svd(C,full_matrices=False)
        S = base.tensor_times_mat(S, U1.T,d)
        U[d] = U1
    core  = S
    return U,core

#randomized_hosvd
def randomized_hosvd(X):
    U = [None for _ in range(X.ndims())]
    dims = X.ndims()
    S = X
    for d in range(dims):
        C = base.unfold(X,d)
        U1, S1, V1 = randomized_svd(C, n_components=3, n_oversamples=10, n_iter='auto',
                     power_iteration_normalizer='auto', transpose='auto',
                     flip_sign=True, random_state=42)
        S = base.tensor_times_mat(S, U1.T, d)
        U[d] = U1
    core = S
    return U, core

#TruncatedHosvd
def TruncatedHosvd(X,eps):
    U = [None for _ in range(X.ndims())]
    dims = X.ndims()
    S = X
    R = [None for _ in range(X.ndims())]
    for d in range(dims):
        C = base.unfold(X,d)
        U1,S1,r = SVD.TruncatedSvd(C,eps_svd=eps)
        R[d] = r
        U[d] = U1
        S = base.tensor_times_mat(S,U[d].T,d)
    return U,S,R,eps

#PartialHosvd
def PartialHosvd(X,ranks):
    U = [None for _ in range(X.ndims())]
    dims = X.ndims()
    S = X
    for d in range(dims):
        C = base.unfold(X, d)
        U1,_,_= SVD.PartialSvd(C,ranks[d])
        U[d] = U1
        S = base.tensor_times_mat(S, U[d].T, d)
    return U, S

#hooi
def hooi(X,maxiter=1000,init='svd',eps = 1e-11,tol=1e-10, plot = True):

    time0 = time.time()

    dims = X.ndims()
    modelist = list(range(dims))

    if init == 'svd':
        U,core,ranks,eps_svd = TruncatedHosvd(X,eps= eps)
        print('TruncatedHosvd Ranks:\t'+str(ranks))
        data = base.tensor_multi_times_mat(core, U, modelist=modelist, transpose=False)
        errorsvd = base.tennorm(base.tensor_sub(data, X))
        print('---------------------------->>>>>>')
        print('TruncatedHosvd Init:')
        print("Original tensor:",X.data.reshape(1024)[1:10])
        print("TruncatedHosvd tensor:",data.data.reshape(1024)[1:10])
        print("Truncated error:",errorsvd)

    else:
        U,core = randomized_hosvd(X)

    error_X = []
    error_iter = []

    normx = base.tennorm(X)
    S1 = X

    for iteration in range(maxiter):
        Uk = [None for _ in range(dims)]
        for i in range(dims):
            U1 = U.copy()
            U1.pop(i)
            L = list(range(dims))
            L.pop(i)
            Y = base.tensor_multi_times_mat(X,U1,modelist=L,transpose=True)
            C = base.unfold(Y,i)
            Uk[i],_,_ = SVD.PartialSvd(C,ranks[i])

        core = base.tensor_multi_times_mat(X,Uk,list(range(dims)),transpose=True)
        U = Uk
        S2 = base.tensor_multi_times_mat(core,Uk,list(range(dims)),transpose=False)
        error0 = base.tennorm(base.tensor_sub(S2,S1))
        S1 = S2
        error_iter.append(error0)
        error1 = base.tennorm(base.tensor_sub(X, S2))
        error_X.append(error1)

        if error0<tol:
            print('---------------------------->>>>>>')
            print('HOOI:')
            print('Iteration:' + str(iteration) + '\t\t' + 'Error_iter:' + str(error0)+'\t\t'+'Error_X:' + str(error1))
            print("Cost time:",time.time()-time0)
            break

    if plot:
        plt.plot(error_X)
        plt.title('The norm difference between the reduction tensor and the original tensor')
        plt.xlabel('Iteration')
        plt.ylabel('Norm difference')
        plt.show()
        plt.plot(error_iter)
        plt.title('The difference between the norm of restoring tensors in two consecutive iterations')
        plt.xlabel('Iteration')
        plt.ylabel('Norm difference')
        plt.show()

    return  U,core


# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial.distance import pdist

v1 = np.array([1.0, 2.0, 3.0])
v2 = np.array([9.0, 8.0, 7.0])

def euclidean(vec1, vec2):
    s = 0
    for i in range(len(vec1)):
        s += (vec1[i] - vec2[i]) ** 2
    return np.sqrt(s)

def euclidean2(vec1, vec2):
    return np.sqrt(np.sum(np.square(vec1 - vec2)))

def euclidean3(vec1, vec2):
    diff = vec1 - vec2
    return np.sqrt(np.dot(diff, diff.T))

def euclidean4(vec1, vec2):
    return np.linalg.norm(vec1 - vec2, ord=2)

print("euclidean = {0}".format(euclidean(v1, v2)))
print("euclidean2 = {0}".format(euclidean2(v1, v2)))
print("euclidean3 = {0}".format(euclidean3(v1, v2)))
print("euclidean4 = {0}".format(euclidean4(v1, v2)))

# 曼哈顿距离 (Manhattan Distance)：
# 又称城市街区距离，顾名思义，就像穿行在曼哈顿的街区一样，只能走直线然后拐弯在接着走直线，而不是两点之间直线最短
# \sum_{k=1}^n |x_{1k} - x_{2k}|
def manhattan(vec1, vec2):
    return np.sum(np.abs(vec1 - vec2))

def manhattan2(vec1, vec2):
    return np.linalg.norm(vec1 - vec2, ord=1)

print("manhattan = {0}".format(manhattan(v1, v2)))
print("manhattan2 = {0}".format(manhattan2(v1, v2)))

# 切比雪夫距离 (Chebyshev Distance)：
# 一张国际象棋的棋盘可以说明这个距离。大家都知道国际象棋中的后非常自由，可以用任意方法走任意多格，
# 切比雪夫距离就相当于问后从棋盘上一个位置到另一个位置最少需要走多少步（假设一步只能走一格，但这
# 一格可以是朝任何方向，直线斜线均可，用图像处理的概念说就是8邻域内的任何一个位置都可以一步走到）。
# 举个例子，从（1,1）位置走到（4,5）位置，后可以先斜向走到（2,2），然后（3,3），再然后（4,4），
# 最后直走一步到（4,5），也就是一共需要走4步，因此这两点的切比雪夫距离就是4。
# max |x_{1i} - x_{2i}|
def chebyshev(vec1, vec2):
    distance = 0
    for i in range(len(vec1)):
        m = abs(vec1[i] - vec2[i])
        if m > distance:
            distance = m
    return distance

def chebyshev2(vec1, vec2):
    return np.linalg.norm(vec1 - vec2, ord=np.inf)

print("chebyshev = {0}".format(chebyshev(v1, v2)))
print("chebyshev2 = {0}".format(chebyshev2(v1, v2)))

# 以上三种距离本质都是闵可夫斯基（Minkowski）距离的特定形式，切比雪夫距离中n取了无穷大，曼哈顿距离中n=1，而欧氏距离中n=2.
# D = \left( \sum_{i=1}^n \left|x_i - y_i\right|^p \right)^{1/p}
# \lim_{p \to \inf} D = max |x_{1i} - x_{2i}|
def minkowski(vec1, vec2, c=2.0):
    return np.sum(np.abs(vec1 - vec2) ** c) ** (1.0 / c)

print("minkowski(1) = {0}".format(minkowski(v1, v2, 1)))
print("minkowski(2) = {0}".format(minkowski(v1, v2, 2)))
print("minkowski(3) = {0}".format(minkowski(v1, v2, 3)))
print("minkowski(100) = {0}".format(minkowski(v1, v2, 100.0)))

# cos(\theta) = \frac{\sum_{k=1}^n x_{1k}x_{2k}}{\sqrt{\sum_{k=1}^n x_{1k}^2} \sqrt{\sum_{k=1}^n x_{2k}^2}}
# cos(\theta) = \frac{AB}{|A||B|}
def cosine(vec1, vec2):
    a = np.dot(vec1, vec2)
    b1 = np.sqrt(np.dot(vec1, vec1.T))
    b2 = np.sqrt(np.dot(vec2, vec2.T))
    return a / (b1 * b2)

def cosine2(vec1, vec2):
    return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))

# print(np.hstack([v1, v2]))
# print(np.vstack([v1, v2]))
# print(np.c_[v1, v2])
# print(np.r_[v1, v2])

def cosine3(vec1, vec2):
    x = np.vstack([vec1, vec2])
    # x = np.array([vec1, vec2])
    return (1 - pdist(x, 'cosine'))[0]

print("cosine = {0}".format(cosine(v1, v2)))
print("cosine2 = {0}".format(cosine2(v1, v2)))
print("cosine3 = {0}".format(cosine3(v1, v2)))

# 杰卡德相似系数
# J(A, B) = \frac{|A \bigcap B|}{|A \bigcup B|}
# J_{\delta}(A,B) = 1 − J(A,B)= \frac{|A \bigcup B|−|A \bigcap B|}{|A \bigcup B|}

def jaccard(vec1, vec2):
    up = np.bitwise_and((vec1 != vec2), np.bitwise_or(vec1 != 0, vec2 != 0)).sum()
    down = np.bitwise_or(vec1 != 0, vec2 != 0).sum()
    return up / down

def jaccard2(vec1, vec2):
    x = np.vstack([vec1, vec2])
    return pdist(x, 'jaccard')[0]

print("jaccard = {0}".format(jaccard(v1, v2)))
print("jaccard2 = {0}".format(jaccard2(v1, v2)))

# fig = plt.figure()
# ax = fig.add_subplot(111, projection='3d')
#
# x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# y = [5, 6, 2, 3, 13, 4, 1, 2, 4, 8]
# z = [2, 3, 3, 3, 5, 7, 9, 11, 9, 10]
#
# ax.scatter(*v1, c='r', marker='o')
# ax.scatter(*v2, c='b', marker='o')
#
#
# ax.set_xlabel('X Axis')
# ax.set_ylabel('Y Axis')
# ax.set_zlabel('Z Axis')
#
# plt.show()


# flake8: noqa

import math

import numpy as np

from genetic_algorithm import GeneticAlgorithm

class NQueensExample(GeneticAlgorithm):
    def __init__(self, board_size, avg_accuracy=0.9, display_documentation=True):
        self.board_size = board_size
        self.target_fitness = int(math.factorial(self.board_size) / (2 * math.factorial(self.board_size - 2)))

        genepool = np.arange(1, self.board_size + 1)
        target_max_fitness = self.target_fitness
        target_avg_fitness = avg_accuracy * target_max_fitness
        super(NQueensExample, self).__init__(
            genepool, self.board_size, target_max_fitness, target_avg_fitness, display_documentation
        )

    def compute_fitness(self, board):
        board_size = len(board)
        attacking_pair = []

        # Check if a queens pair is not in the same row.
        for i in range(board_size):
            for j in range(board_size):
                if i != j and board[i] == board[j]:
                    attacking_pair.append(tuple(set([i, j])))

        # Check if a queens pair is not in the same diagonal.
        for i in range(board_size):
            for j in range(board_size):
                if i != j and int(board[i]) - int(board[j]) == abs(i - j):
                    attacking_pair.append(tuple(set([i, j])))

        return self.target_fitness - len(set(attacking_pair))

    def display(self, board):
        board_size = len(board)

        print("board : {}".format(board))
        for c in range(board_size):
            for r in range(board_size):
                if r == int(board[c]) - 1:
                    print("Q", end=" ")
                else:
                    print("*", end=" ")
            print()
        print("Fitness: {}".format(self.compute_fitness(board)))


from plotly.subplots import make_subplots

from IMLearn.learners import UnivariateGaussian, MultivariateGaussian
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

pio.templates.default = "simple_white"

def test_univariate_gaussian():
    # Question 1 - Draw samples and print fitted model

    samples = np.random.normal(10, 1, 1000)

    ug = UnivariateGaussian()
    ug.fit(samples)
    print(ug.mu_, ug.var_)

    # Question 2 - Empirically showing sample mean is consistent

    y_axis = []
    x_axis = []

    for i in range(0, samples.size, 10):
        ug.fit(samples[:i + 10])
        y_axis.append(abs(10 - ug.mu_))
        x_axis.append(i + 10)

    fig = go.Figure()
    fig.add_trace(go.Scatter(x=x_axis, y=y_axis))
    fig.update_layout(title="Exception difference depends on sample size",
                      xaxis_title="Sample size",
                      yaxis_title="Exception")
    fig.show()

    # Question 3 - Plotting Empirical PDF of fitted model

    x_axis = samples
    ug.fit(x_axis)
    y_axis = ug.pdf(x_axis)

    fig = go.Figure()
    fig.add_trace(go.Scatter(x=x_axis, y=y_axis, mode="markers"))
    fig.update_layout(title="Empirical PDF of fitted model",
                      xaxis_title="Sample value",
                      yaxis_title="PDF")
    fig.show()

def test_multivariate_gaussian():
    # Question 4 - Draw samples and print fitted model
    cov = np.array([[1, 0.2, 0, 0.5],
                    [0.2, 2, 0, 0],
                    [0, 0, 1, 0],
                    [0.5, 0, 0, 1]])
    mu = [0, 0, 4, 0]

    samples = np.random.multivariate_normal(mu, cov, size=1000)
    mug = MultivariateGaussian()
    mug.fit(samples)
    print(mug.mu_, "\n", mug.cov_)

    # Question 5 - Likelihood evaluation

    mat = np.linspace(-10, 10, 200)

    likelihood_array = np.empty(shape=(mat.size, mat.size))

    max_index = [mat[0], mat[0]]
    max_arg = mug.log_likelihood(np.array([mat[0], 0, mat[0], 0]), cov,
                                 samples)

    for i in range(mat.size):
        for j in range(mat.size):
            tmp_arg = mug.log_likelihood(np.array([mat[i], 0, mat[j], 0]),
                                         cov,
                                         samples)
            if tmp_arg > max_arg:
                max_arg = tmp_arg
                max_index = [mat[i], mat[j]]
            likelihood_array[i][j] = tmp_arg

    fig = go.Figure()
    fig.add_trace(go.Heatmap(x=mat, y=mat, z=likelihood_array))
    fig.update_layout(title="logliklihood of [f1, 0, f3, 0]",
                      xaxis_title="f1 val",
                      yaxis_title="f3 val")
    fig.show()

    # Question 6 - Maximum likelihood
    print(max_arg)
    print(max_index)

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


# Authors: Dr. Clayton Morrison, Sean Hendryx

import numpy

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

def simple_quadratic_function(x):
    """
    this function accepts a 2D vector as input.
    Its outputs are:
       value: h(x1, x2) = x1^2 + 3*x1*x2
       grad: A 2x1 vector that gives the partial derivatives
             of h with respect to x1 and x2
    Note that when we pass simple_quadratic_function(x) to
    compute_gradient_numerical_estimate, we're assuming
    that compute_gradient_numerical_estimate will use only
    the first returned value of this function.
    :param x:
    :return:
    """
    value = x[0] ** 2 + 3 * x[0] * x[1]
    #print "value inside simple_quadratic_function(x): \n", value
    grad = numpy.zeros(shape=2, dtype=numpy.float32)
    grad[0] = 2 * x[0] + 3 * x[1]
    grad[1] = 3 * x[0]

    return value, grad

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

def compute_gradient_numerical_estimate(J, theta, epsilon=0.0001):
    """
    :param J: a loss (cost) function that computes the real-valued loss given parameters and data
    :param theta: array of parameters
    :param epsilon: amount to vary each parameter in order to estimate
                    the gradient by numerical difference
    :return: array of numerical gradient estimate
    """

    gradient = numpy.zeros(theta.shape)
    #print "gradient: \n", gradient

    ### YOUR CODE HERE ###

    e = numpy.zeros(theta.shape)

    # Debugging:

    #e[0] = epsilon
    #print "theta: \n", theta
    #print "e: \n", e
    #print "e[1]: \n", e[0]
    #lower = J(theta - e)
    #upper = J(theta + e)
    #print "lower: \n", lower
    #print "lower[0]: \n", lower[0]

    # How to get just first value returned from J()?
    #here

    for i in range(0,(numpy.size(theta))):
        #vector containing only epsilon at index i.  Zeros everywhere else
        e[i] = epsilon
        #print "e: \n", e
        lower = J(theta - e)
        #print "lower[0]: \n", lower[0]
        upper = J(theta + e)
        #print "upper[0]: \n", upper[0]
        #Compute numerical gradient from upper and lower:
        # just use first value of upper: upper[0]
        gradient[i] = (upper[0] - lower[0])/( 2.0 * epsilon)
        #print "gradient: \n", gradient
        #Change e[i] back to zero:
        e[i] = 0
        #print "e \n", e

    return gradient

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

def test_compute_gradient_numerical_estimate():
    """
    Test of compute_gradient_numerical_estimate.
    This provides a test for your numerical gradient implementation
    in compute_gradient_numerical_estimate
    It analytically evaluates the gradient of a very simple function
    called simple_quadratic_function and compares the result with
    your numerical estimate. Your numerical gradient implementation
    is incorrect if your numerical solution deviates too much from
    the analytical solution.
    :return:
    """
    print "test_compute_gradient_numerical_estimate(): Start Test"
    print "    Testing that your implementation of "
    print "        compute_gradient_numerical_estimate()"
    print "        is correct"
    x = numpy.array([4, 10], dtype=numpy.float64)
    (value, grad) = simple_quadratic_function(x)

    print "    Computing the numerical and actual gradient for 'simple_quadratic_function'"
    num_grad = compute_gradient_numerical_estimate(simple_quadratic_function, x)
    print "    The following two 2d arrays should be very similar:"
    print "        ", num_grad, grad
    print "    (Left: numerical gradient estimate; Right: analytical gradient)"

    diff = numpy.linalg.norm(num_grad - grad)
    print "    Norm of the difference between numerical and analytical num_grad:"
    print "        ", diff
    print "    (should be < 1.0e-09 ; I get about 1.7e-10)"
    print "test_compute_gradient_numerical_estimate(): DONE\n"


"""
Author: Peratham Wiriyathammabhum

"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
eps = np.finfo(float).eps

def ridge_regression(Xin, yin, opts, thres=10**-5, max_epochs=200):
	"""
	Perform ridge regression on an input row matrix X and a target vector y.
	See: http://cs229.stanford.edu/notes-spring2019/cs229-notes1.pdf
		 http://ciml.info/dl/v0_99/ciml-v0_99-ch07.pdf
		 http://cazencott.info/dotclear/public/lectures/ma2823_2017/slides/iml_chap7_regularization.pdf
	"""
	X = Xin.copy() # safety
	y = yin.copy()
	y = np.expand_dims(y,axis=1)

	ni, nd = X.shape
	X = np.append(X, np.ones((ni, 1)), axis=1) # append the bias/const term

	# theta = weight_init(nd, init_mode='random')

	lr = opts['lr']
	bsize = opts['bsize']
	lamb_const = opts['lamb_const']

	"""
	theta = X^{-1} * y. use a pseudo inverse for X^{-1}.
	Solve the normal equation X'X\theta = X'y.
	"""
	theta = np.matmul(np.matmul(np.linalg.pinv(np.matmul(X.T, X) + lamb_const*np.diag(np.ones(nd+1))), X.T), y)
	# theta = np.matmul(np.linalg.pinv(X), y)
	pred = np.matmul(X, theta)
	loss = square_loss(pred, y, theta, lamb_const)
	print('[Info] loss: {:.4f}'.format(float(loss)))
	return theta, pred

def weight_init(nd, init_mode='random'):
	if init_mode == 'random':
		theta = np.random.randn(nd + 1, 1) # plus the bias/const term
	elif init_mode=='xavier':
		"""
		Glorot, Xavier, and Yoshua Bengio.
		"Understanding the difficulty of training deep feedforward neural networks."
		Proceedings of the thirteenth international conference on artificial
		intelligence and statistics. 2010.
		"""
		pass
	else:
		pass
	return theta

def square_loss(pred, yin, theta, lamb_const):
	"""
	A typical L2 norm aka. square error.
	"""
	y = yin.copy()
	return np.linalg.norm(pred - y, ord=2) + (lamb_const / 2.) * np.linalg.norm(theta, ord=2)

def terminating_cond(nite, max_iters):
	if nite >= max_iters:
		print('[Info] terminate at iter: {}'.format(nite))
		return True
	# elif np.linalg.norm(mu - nmu) < thres:
	#   print('[Info] terminate at iter: {}'.format(nite))
	#   return True
	else:
		return False

def main(opts):
	max_epochs = opts['max_epochs']
	from sklearn import datasets
	diabetes = datasets.load_diabetes()
	X, y = diabetes.data, diabetes.target
	# whitening
	X = (X - np.mean(X, axis=0)) / np.std(X, axis=0)

	theta, pred = ridge_regression(X[:, np.newaxis, 2], y, opts, thres=10**-5, max_epochs=max_epochs)
	y = np.expand_dims(y,axis=1)
	# plot
	# input("Press Enter to continue...")
	"""
	plotting data and predictions as well as regression lines.
	"""
	diabetes_X = X[:, np.newaxis, 2]
	idx = np.argsort(diabetes_X, axis=0)
	plt.scatter(diabetes_X[idx].squeeze(1), y[idx].squeeze(1),  color='black')
	plt.plot(diabetes_X[idx].squeeze(1), pred[idx].squeeze(1), color='blue', linewidth=3)

	plt.xticks(())
	plt.yticks(())

	plt.show()
	input("Press Enter to continue...")
	plt.close()
	"""
	plotting model parameters using hinton diagram
	"""
	from hinton_diagram import hinton
	h_theta = theta.copy() # safety for plotting
	hinton(h_theta)
	plt.show()
	input("Press Enter to continue...")
	plt.close()
	"""
	TODO: plotting the solution path
	"""
	return

if __name__ == '__main__':
	import argparse

	parser = argparse.ArgumentParser(description='run ridge regression.')
	parser.add_argument('--lr', dest='lr',
					  help='learning rate',
					  default=1e-3, type=float)
	parser.add_argument('--bsize', dest='bsize',
					  help='batch size',
					  default=16, type=int)
	parser.add_argument('--lambda', dest='lamb_const',
					  help='lambda for l2',
					  default=1e-3, type=float)
	parser.add_argument('--max_epochs', dest='max_epochs',
					  help='number of iterations to train',
					  default=200, type=int)
	args = parser.parse_args()
	opts = vars(args)

	main(opts)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from misc.matrix import *
import numpy as np, unittest

class TestNormalizeWeightMatrix(unittest.TestCase):
    def test(self):
        np.testing.assert_array_almost_equal(
            np.array([
                [1/2, 1/2, 0,    0],
                [1/2, 1/6, 1/6,  1/6],
                [0,   1/6, 5/12, 5/12],
                [0,   1/6, 5/12, 5/12]
            ]),
            normalize_weight_matrix(np.array([
                [1, 1, 0, 0],
                [1, 1, 1, 1],
                [0, 1, 1, 1],
                [0, 1, 1, 1]
            ]))
        )


# -*- coding: utf-8 -*-
"""
Created on Fri Nov 13 17:03:59 2020

Questão 5
Código desenvolvido para calcular o valor médio e RMS de sinais periódicos

@author: Bianca
"""
# importar as bibliotecas necessárias
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': 14})

Tp = 1 #período
f = 1 #frequência
t = np.linspace(0, Tp, 1000) #vetor temporal
A = 12 #amplitude
w = 2*np.pi*f #omega
xt = A*np.sin(w*t) #equação do sinal

x0 = np.mean(xt) #valor médio do sinal
xrms = np.sqrt(np.mean(xt**2)) #valor rms do sinal

print(f'Valor Médio: {x0:.3f}')
print(f'Valor RMS: {xrms:.3f}')

#plotando o gráfico
plt.figure()
plt.title('Sinal do Valor médio e RMS (i)')
plt.plot(t, xt, 'b', label = r'$x(t)$')
plt.plot(t,x0*np.ones(len(t)),'--k', label = r'$x_0$')
plt.plot(t,xrms*np.ones(len(t)),'--r', label = r'$x_{rms}$')
plt.legend(loc = 'upper right')
plt.grid(linestyle = '--', which='both')
plt.xlabel('tempo [s]')
plt.ylabel('Amplitude [-]')
plt.tight_layout()
plt.show()

#%% (ii)

Tp = 1 #período
f = 1 #frequência
t = np.linspace(0, Tp, 1000) #vetor temporal
A = 8 #amplitude do sinal
A0 = 3 #deslocamento do sinal
w = 2*np.pi*f #omega
xt = A0 + A*np.sin(w*t) #equação do sinal

x0 = np.mean(xt) #valor médio do sinal
xrms = np.sqrt(np.mean(xt**2)) #valor rms do sinal

print(f'Valor Médio: {x0:.3f}')
print(f'Valor RMS: {xrms:.3f}')

#plotando o gráfico
plt.figure()
plt.title('Sinal do Valor médio e RMS (ii)')
plt.plot(t, xt, 'b', label = r'$x(t)$')
plt.plot(t,x0*np.ones(len(t)),'--k', label = r'$x_0$')
plt.plot(t,xrms*np.ones(len(t)),'--r', label = r'$x_{rms}$')
plt.legend(loc = 'upper right')
plt.grid(linestyle = '--', which='both')
plt.xlabel('tempo [s]')
plt.ylabel('Amplitude [-]')
plt.tight_layout()
plt.show()


import numpy as np

def tree_valley_function(x):
    """
    Represents a two dimensional Function, with two local and one global minima
    :param x: 1D Rowvector (multiple Values can be passed)
    :type x: Numpy array
    :return: Function values of x
    :rtype: Numpy array
    """

    y = -(
        np.exp(-((x - 2) ** 2))
        + np.exp(-((x - 6) ** 2) / 10)
        + 1 / (x ** 2 + 1)
    )

    return y

def four_humps_function(x):
    """
    Function with one global maxima, one local maximum, one glomal minimun an
    a locam minimum
    :param x: 2D rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: Function values of x
    :rtype: Numpy array
    """

    y = (1 - x[:, 0] / 2 + x[:, 0] ** 5 + x[:, 1] ** 3) * np.exp(
        -x[:, 0] ** 2 - x[:, 1] ** 2
    )

    return y

def himmelblaus_function(x):
    """
    The Himmelblau Function is a multi-modal function, with one local maximum
    four local minima.
    :param x: 2D rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: Function values of x
    :rtype: Numpy array
    """

    y = (x[:, 0] ** 2 + x[:, 1] - 11) ** 2 + (x[:, 0] + x[:, 1] ** 2 - 7) ** 2

    return y

def rotated_hyper_ellipsoid_function(x):
    """
    The Rotated Hyper-Ellipsoid function is continuous, convex and unimodal.
    It is an extension of the Axis Parallel Hyper-Ellipsoid function,
    also referred to as the Sum Squares function.
    :param x: N-Dimensional Vector rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: Function values of x
    :rtype: Numpy Array
    """
    x = np.atleast_2d(x)
    y = np.zeros(x.shape[0])
    for i in range(x.shape[1]):
        y = y + x[:, i] ** 2

    return y

def matyas_function(x):
    """
    The Matyas function has no local minima except the global one.
    :param x: 2D rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: Function values of x
    :rtype: Numpy array
    """

    y = 0.26 * (x[:, 0] ** 2 + x[:, 1] ** 2) - 0.48 * x[:, 0] * x[:, 1]

    return y

def cross_in_tray_function(x):
    """
    The Cross-in-Tray function has multiple global minima. It is shown here
    with a smaller domain in the second plot, so that its characteristic
    "cross" will be visible.
    :param x: 2D rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: Function values of x
    :rtype: Numpy array
    """

    fact1 = np.sin(x[:, 0]) * np.sin(x[:, 1])
    fact2 = np.exp(np.abs(100 - np.sqrt(x[:, 0] ** 2 + x[:, 1] ** 2) / np.pi))
    y = -0.0001 * (abs(fact1 * fact2) + 1) ** 0.1
    return y

def SixHumpCamelFunction(x):
    """
    he plot on the left shows the six-hump Camel function on its recommended
    input domain, and the plot on the right shows only a portion of this
    domain, to allow for easier viewing of the function's key characteristics.
    The function has six local minima, two of which are global.
    :param x: 2D rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: function values of x
    :rtype: Numpy array
    """

    x1 = x[:, 0]
    x2 = x[:, 1]
    y = (
        (4 - 2.1 * x1 ** 2 + (x1 ** 4) / 3) * x1 ** 2
        + x1 * x2
        + (-4 + 4 * x2 ** 2) * x2 ** 2
    )
    return y

def michalewicz_function(x):
    """
    The Michalewicz function has d! local minima, and it is multimodal.
    The parameter m defines the steepness of they valleys and ridges;
    a larger m leads to a more difficult search.
    The recommended value of m is m = 10.
    :param x: 2D rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: Function Values of x
    :rtype: Numpy array
    Funktioniert nicht so wie es soll
    Quelle:
        http://www.sfu.ca/~ssurjano/michal.html
    """

    m = 10
    y = np.zeros(x.shape[0])
    for i in range(x.shape[1]):
        y = y + np.sin(x[:, i]) * (np.sin(i * x[:, i] ** 2 / np.pi)) ** (2 * m)
    return -y

def rastrigin_function(x):
    """
    The Rastrigin function has several local minima. It is highly multimodal,
    but locations of the minima are regularly distributed.
    :param x: 2D rowvector (a column of rows can be passed)
    :type x: Numpy array
    :return: Function Values of x
    :rtype: Numpy array
    """

    dim = x.shape[1]
    y = np.zeros(x.shape[0])
    for i in range(x.shape[1]):
        y = y + x[:, i] - 10 * np.cos(2 * np.pi * x[:, i])
    return 10 * dim + y


#importing necessary libraries
import numpy as np
import matplotlib.pyplot as plt
import time
from mpl_toolkits import mplot3d
import pandas as pd
import argparse

def compute_cost(X,Y,theta):
    '''
    Input: X= design matrix of dimension (no of samples, no of features+1)
           Y= response vector of dimension (no of samples,1)
           theta= parameter vector of dimension (no of parameters,1)
    Outpt: cost= Mean squared error, a scalar
    '''
    #dimension check in case of SGD
    if(X.ndim==1):
        X=X.reshape(1,-1)
        Y=Y.reshape(-1,1)

    cost=np.sum(np.power(Y-np.dot(X,theta),2),axis=0,keepdims=True)*(1/(2*X.shape[0]))
    return cost

def update_parameters(X,Y, theta, learning_rate):
    '''
    Input: X= design matrix of dimension (no of samples, no of features+1)
           Y= response vector of dimension (no of samples,1)
           theta= parameter vector of dimension (no of parameters,1)
           learning_rate=a scalar
    Outpt: theta= updated parameter vector of dimension (no of parameters,1)
    '''
    if(X.ndim==1):
        X=X.reshape(1,-1)
        Y=Y.reshape(-1,1)
    error=Y-np.dot(X,theta)
    theta=theta+learning_rate*(1/X.shape[0])*np.transpose(np.sum(error*X,axis=0,keepdims=True))
    return theta

def plot_graph(values,elev,azim):
    '''
    Input: values= a dictionary conaying theta and theta_array
           elev= 3d surface vertical angle view
           azim=3d surface x-y plane angle view
    '''
    fig = plt.figure(figsize = (18, 10))
    ax = plt.axes(projection ="3d")
    x=values["theta_array"][:,0]
    y=values["theta_array"][:,1]
    z=values["theta_array"][:,2]
    ax.scatter3D(x, y, z, color = "green",s=2)
    ax.view_init(elev,azim)
    plt.xlabel('x-axis')
    plt.ylabel('y-axis')
    ax.set_zlabel("z-axis")
    plt.title("3D parameter plot")
    plt.show()

def stochastic_gradient_descent(X,Y,batch_size,eps):

    if batch_size==1:
        k=100
        epochs=30000
    elif batch_size==100:
        k=5
        epochs=20000
    elif batch_size==10000:
        k=2
        epochs=22000
    elif batch_size==1000000:
        k=1
        epochs=25000

    print("Batch_size: "+str(batch_size))

    learning_rate=0.001
    theta=np.array([0,0,0]).reshape(-1,1)
    old_cost_array=np.array([])
    new_cost_array=np.array([])
    theta_array=np.array([])
    step=0

    old_cost_array=np.append(new_cost_array,compute_cost(X[0,:].reshape(1,-1), Y[0].reshape(-1,1),theta))

    flag=True
    start=time.time()
    while flag:
        for i in range(int(X.shape[0]/batch_size)):
            #print(int(X.shape[0]/batch_size))
            X_data=X[i*batch_size:(i+1)*batch_size,:]
            Y_data=Y[i*batch_size:(i+1)*batch_size]

            step=step+1
            theta=update_parameters(X_data,Y_data,theta,learning_rate)
            theta_array=np.append(theta_array,theta)

            new_cost=compute_cost(X_data,Y_data,theta)
            new_cost_array=np.append(new_cost_array,new_cost)
            if (step%500==0):
                print("Parameters are: \n"+str(theta))

            if (step%k==0):
                if(np.abs(np.mean(old_cost_array)-np.mean(new_cost_array))<eps or step>=epochs):
                    flag=False
                    end=time.time()
                    time_taken=end-start
                    print("Error difference: "+str(np.abs(np.mean(old_cost_array)-np.mean(new_cost_array))))
                    print("Error is: "+str(np.mean(new_cost_array)))
                    print("No of. iterations is: "+str(step))
                    print("Time taken: "+str(end-start))
                    print("Parameters are:\n "+str(theta))
                    break
                old_cost_array=new_cost_array
                new_cost_array=np.array([])

    theta_array=theta_array.reshape(-1,theta.shape[0])
    values={"theta_array":theta_array,"theta":theta}
    return values

#generates the same random numbers in each iteration
np.random.seed(0)

#generating normal random variables with specified mean and variance
X1=np.random.normal(3,2,(1000000,1))
X2=np.random.normal(-1,2,(1000000,1))

#X is the design matrix
X=np.hstack((np.ones(X1.shape),X1,X2))

parameters=np.array((3,1,2)).reshape(-1,1)

#Y is the response vector
Y=np.dot(X,parameters)+np.random.normal(0,np.sqrt(2),(1000000,1))

parser = argparse.ArgumentParser()
parser.add_argument('--bs',type = int)
parser.add_argument('--eps',type = float)
args = parser.parse_args()

batch_size = args.bs
eps = args.eps

values=stochastic_gradient_descent(X,Y,batch_size,eps)

#loading the test data
test_data=pd.read_csv('q2test.csv').to_numpy()
#print(test_data.shape)
testX=test_data[:,0:2]
#print(testX.shape)
testX=np.hstack((np.ones((test_data.shape[0],1)),testX))
testY=test_data[:,2].reshape(-1,1)

error1=2*compute_cost(testX,testY,parameters)
error2=2*compute_cost(testX,testY,values["theta"])
print("Error wrt the learned hypothesis is: "+str(error2))
print("Error wrt the original hypothesis is: "+str(error1))
plot_graph(values,elev=10,azim=100)


"""
Test implementation of Euler's method
Srayan Gangopadhyay
2020-05-13
"""

import numpy as np
import matplotlib.pyplot as plt

def func(y):  # RHS of ODE in form dy/dx=
    return y

# PARAMETERS
y0 = 1  # initial value
delta = 1  # step size
end = 3  # x-value to stop integration

steps = int(end/delta)  # number of steps
x = np.linspace(0, end, steps+1)  # array of x-values
y = np.zeros(steps+1)  # empty array for solution
y[0] = y0  # inserting initial value

# INTEGRATING
for i in range(1, steps+1):
    y[i] = y[i-1] + (delta*func(y[i-1]))

plt.plot(x, y, label='Approx. soln (Euler)')
plt.plot(x, y, 'o')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.show()


"""
 modelize plan trajectory with spline

Speed modeled as follow:
 - sin at beginning (0 to Vc)
 - vc
 - sin at end (Vc to 0)

Trajectory is derived by taking integral
"""

import numpy as np

tf = 10

t1 = 3
t2 = tf - t1
w = np.pi/t1
p1 = np.pi + np.pi/2. # phase so we can use only sin
p2 = np.pi/2.

# average speed / cruise speed
vc = 1000

# generic sin function so it does not need to be rewritten
def genSin(t, w, p, vc):
  return vc/float(2)*(np.sin(w*t+p)+1)

# integral primitive of previous function
def genSinPrim(t, w, p, vc):
  return vc/float(2)*(-np.cos(w*t+p)/w+t)

def f1(t):
  return genSin(t, w, p1, vc)

def f2(t):
  return vc

def f3(t):
  return genSin(t-t2, w, p2, vc)

def f(t):
  if t < 0:
    return 0
  if t < t1:
    return f1(t)
  elif t < t2:
    return f2(t)
  elif t < tf:
    return f3(t)
  else:
    return 0

######### calculates integrals ########

def f1Prim(t):
  return genSinPrim(t, w, p1, vc)

def f2Prim(t):
  return vc*(t-t1) + f1Prim(t1)

def f3Prim(t):
  return genSinPrim(t-t2, w, p2, vc) + f2Prim(t2)

def fPrim(t):
  if t1> tf/float(2):
    0
  else:
    if t < 0:
      return 0
    if t < t1:
      return f1Prim(t)
    elif t < t2:
      return f2Prim(t)
    elif t < tf:
      return f3Prim(t)
    else:
      return f3Prim(tf)

# compute integral numerically
def fNumPrim(t):
  N = 300  # number of iteration per call (very computational intensive)
  x = np.linspace(0,t,N)
  r = 0.
  dx = float(t)/float(N)
  for i in x:
    r = r + dx*g(i)

  return r

# piece of magic here
# http://scicomp.stackexchange.com/questions/1144/how-can-i-plot-piece-wise-defined-function-in-some-easily-accessed-open-source-t?newreg=3991fda29a504478a7aca2bb58e61b96
g = np.vectorize(f)
gPrim = np.vectorize(fPrim)
#gNumPrim = np.vectorize(fNumPrim)

import matplotlib.pyplot as plt

t = np.linspace(0, tf, 300)

plt.plot(t, g(t))

#plt.plot(t, gNumPrim(t))
plt.plot(t, gPrim(t))

plt.xlabel('time [s]')
plt.ylabel('trajectory [m]')
plt.legend()
plt.savefig("ballistic.png")
plt.show()


from sympy import mod_inverse

class ModArith:
    def __init__(self, val, mod):
        if isinstance(val, ModArith):
            assert(mod == val.m)
            self.val = val.val
            self.m = mod
        else:
            self.val = val % mod
            self.m = mod

    def __add__(self, other):
        assert(isinstance(other, ModArith))
        assert(self.m == other.m)
        return ModArith(self.val+other.val, self.m)

    def __sub__(self, other):
        assert(isinstance(other, ModArith))
        assert(self.m == other.m)
        return ModArith(self.m+self.val-other.val, self.m)

    def __mul__(self, other):
        assert(isinstance(other, ModArith))
        assert(self.m == other.m)
        return ModArith(self.val*other.val, self.m)

    def __divmod__(self, other):
        assert(isinstance(other, ModArith))
        assert(self.m == other.m)
        quot, rem = 0, self.val
        while rem >= other.val:
            rem = rem - other.val
            quot = quot+1
        return (ModArith(quot, self.m), ModArith(rem, self.m))

    def __eq__(self, other):
        assert(isinstance(other, ModArith))
        assert(self.m == other.m)
        return self.val == other.val

    def __neg__(self):
        return ModArith(self.m-self.val, self.m)

    def iszero(self):
        return self.val == 0

    def __repr__(self):
        return f"{self.val}"

    def zero(self):
        return ModArith(0, self.m)

    def inv(self):
        return ModArith(mod_inverse(self.val, self.m), self.m)

import itertools

def add_(a, b):
    if a is None: return b
    if b is None: return a
    return a+b

def sub_(a, b):
    if a is None: return -b
    if b is None: return a
    return a-b

class Poly:
    def __init__(self, c, ctor):
        self.ctor = ctor
        self.c = [ctor(i) for i in c]
        if len(self.c) > 0:
            while len(self.c) and self.c[0].iszero():
                self.c.pop(0)
            if len(self.c) > 0:
                self.order=len(self.c)-1
            else:
                self.order=0
        else:
            self.order=0

    def scale(self, x):
        return Poly([c*x for c in self.c], self.ctor)

    def __add__(self, other):
        cs = list(add_(a,b) for a, b in itertools.zip_longest(reversed(self.c), reversed(other.c)))
        return Poly(list(reversed(cs)), self.ctor)

    def __sub__(self, other):
        cs = list(sub_(a,b) for a, b in itertools.zip_longest(reversed(self.c), reversed(other.c)))
        return Poly(list(reversed(cs)), self.ctor)

    def __mul__(self, other):
        acc = Poly([], self.ctor)
        x = self
        for c in reversed(other.c):
            acc = acc + x.scale(c)
            x = Poly(x.c+[c.zero()], self.ctor)
        return acc

    def __mod__(self, other):
        return divmod(self, other)[1]

    def __neg__(self):
        return Poly([-c for c in self.c], self.ctor)

    def __divmod__(self, other):
        rem = self
        quot = Poly([], self.ctor)
        while len(rem.c) >= len(other.c):
            r = rem.c[0]
            o = other.c[0]
            order = len(rem.c) - len(other.c)
            c = r * o.inv()
            q = Poly([c]+order*[self.ctor(0)], self.ctor)
            quot = quot + q
            rem = rem - q*other
        return (quot, rem)

    def __repr__(self):
        if len(self.c) == 0: return "0"
        return ' + '.join(reversed([f"{c} x^{n}" if n > 0 else f"{c}" for n, c in enumerate(reversed(self.c))]))

M = lambda x: ModArith(x, 7)
P = lambda x: Poly(list(map(M, x)), M)

a = P([1,2,1,0])
b = P([0,2,0,0])
str(a)
str(b)
str(divmod(a,b))

# In[0]

from hamming2 import allVectors
import numpy as np

def cycCode(gc, k, base):
    M = lambda x: ModArith(x, base)
    P = lambda x: Poly(x, M)
    g = P(gc)
    r = g.order
    n = k+r
    xr = P([1]+r*[0])

    def encode(m):
        z = P(m)*xr
        z = z - (z % g)
        cs = [c.val for c in z.c]
        while len(cs) < n:
            cs.insert(0, 0)
        return cs

    d = min(np.count_nonzero([i for i in encode(m)]) for m in itertools.islice(allVectors(k, base), 1, None))
    ec = (d-1)//2
    print(f"Code distance is {d}, can correct {ec} errors")

    tbl = {}
    for ei in allVectors(n, base):
        if 0 < np.count_nonzero(ei) <= ec:
            si = P(ei) % g
            tbl[str(si)] = np.reshape(ei, n)
    print(f"Table size is {len(tbl)}")

    def decode(c):
        s = P(c) % g
        if all(c.iszero() for c in s.c):
            return c[:k]
        e = tbl[str(s)]
        c1 = c - e
        return c1[:k]

    return (encode, decode)

encode, decode = cycCode(gc=[1,0,1,0,0,1,1,1], k=4, base=4)

# In[0]

c = encode([1,2,3,0])
c[3] += 2
c[5] += 2
decode(c)


import numpy as np
import pandas as pd
import scipy.stats as stats
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
import yfinance as yf
yf.pdr_override()
import datetime as dt

# input
symbol = 'AAPL'
start = dt.date.today() - dt.timedelta(days = 365*5)
end = dt.date.today()

# Read data
df = yf.download(symbol,start,end)
returns = df['Adj Close'].pct_change()[1:].dropna()

import statistics as st
print('Mean of returns:', st.mean(returns))
print('Median of returns:', st.median(returns))
print('Median Low of returns:', st.median_low(returns))
print('Median High of returns:', st.median_high(returns))
print('Median Grouped of returns:', st.median_grouped(returns))
print('Mode of returns:', st.mode(returns))

from statistics import mode

print('Mode of returns:', mode(returns))
hist, bins = np.histogram(returns, 20) # Break data up into 20 bins
maxfreq = max(hist)

print('Mode of bins:', [(bins[i], bins[i+1]) for i, j in enumerate(hist) if j == maxfreq])

print('Arithmetic average of returns: ', returns.mean())

# Geometric mean
from scipy.stats.mstats import gmean
print('Geometric mean of stock:', gmean(returns))

ratios = returns + np.ones(len(returns))
R_G = gmean(ratios) - 1
print('Geometric mean of returns:', R_G)

print('Standard deviation of returns: ', returns.std())

T = len(returns)
init_price = df['Adj Close'][0]
final_price = df['Adj Close'][T]
print('Initial price:', init_price)
print('Final price:', final_price)
print('Final price as computed with R_G:', init_price*(1 + R_G)**T)

# Harmonic mean
print('Harmonic mean of returns:', len(returns)/np.sum(1.0/returns))

print('Skew:', stats.skew(returns))
print('Mean:', np.mean(returns))
print('Median:', np.median(returns))

plt.hist(returns, 30);
plt.title(f'Histogram Returns for {symbol.upper()}')
plt.show()

# Plot some example distributions stock's returns
xs = np.linspace(-6,6, 1257)
normal = stats.norm.pdf(xs)
print('Excess kurtosis of leptokurtic distribution:', (stats.laplace.stats(returns)))
print('Excess kurtosis of mesokurtic distribution:', (stats.norm.stats(returns)))
print('Excess kurtosis of platykurtic distribution:', (stats.cosine.stats(returns)))

print("Excess kurtosis of returns: ", stats.kurtosis(returns))

from statsmodels.stats.stattools import jarque_bera
_, pvalue, _, _ = jarque_bera(returns)

if pvalue > 0.05:
    print('The returns are likely normal.')
else:
    print('The returns are likely not normal.')

import numpy,import scipy,from scipy,from statsmodels