import numpy as np
import matplotlib
matplotlib.use('GTK3Cairo')
import matplotlib.pyplot as plt
from temp_schedule import EASchedule
from simulated_annealing import sa_solve

a, b, c, d = -2, 2, -1, 3

def f_obj(x, y): return -(1 - x)**2 - 100 * (y - x**2)**2

grid3 = np.array([
    [f_obj(a + i * (b - a) / 99., c + j * (d - c) / 99.) for j in range(100)]
    for i in range(100)])

# Create exponential additive cooling schedule
cooling_sched = EASchedule(initial_temp=10,
                           final_temp=0,
                           num_cycles=5000)

# Find global maximum with simulated annealing.
optim_state, f_max, stats = sa_solve(grid3,
                                     temp_schedule=cooling_sched,
                                     initial_state=(0, 0),
                                     log_interval=5000)

# Transform state trajectory from grid to ℝ².
s_traj = np.array([[a + (p[0]) * (b - a) / 99. for p in stats.traj_history],
                   [c + (p[1]) * (d - c) / 99. for p in stats.traj_history]])

# Transform EMA of state trajectory from grid to ℝ².
ema_traj = np.array([[a + (p[0]) * (b - a) / 99. for p in stats.traj_ema_history],
                     [c + (p[1]) * (d - c) / 99. for p in stats.traj_ema_history]])

# Sample f₃(x, y) on 100 x 100 grid.
x = np.linspace(a, b, 100)
y = np.linspace(c, d, 100)
x, y = np.meshgrid(x, y)
z = np.zeros(x.shape)
for i in range(x.shape[0]):
    for j in range(x.shape[1]):
        z[i, j] = f_obj(x[i, j], y[i, j])

# Create figure 2.a.
fig, ax = plt.subplots()
ax.contourf(x, y, -np.log(-z), cmap='viridis', zorder=-40)
plt_ema = ax.plot(ema_traj[0], ema_traj[1], linewidth=1.5,
                  c='white', zorder=-30)
plt_ema[0].set_label('EMA trajectory')

plt_f_actual = ax.scatter([1], [1], c='red', s=400, marker='+', zorder=3)
plt_f_actual.set_label('Analytical solution')

plt_f_sol = ax.scatter(s_traj[0, -1], s_traj[1, -1], s=400,
                       marker='+', c='blue', zorder=4)
plt_f_sol.set_label('SA solution')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.legend()

# Save figure 2.a to file.
plt.savefig(fname='figures/figure2-1.svg')

# plt.show()

# Create figure 2.b.
plt.cla()
ax.plot(stats.temp_history[:8000])
ax.set_xlabel('Cycle k')
ax.set_ylabel('Temperature T')

plt.show()

# Save figure 2.b to file.
plt.savefig(fname='figures/figure2-2.svg')


import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from copy import deepcopy

center_1 = np.array([1,1])
center_2 = np.array([5,5])
center_3 = np.array([8,1])

data_1 = np.random.randn(200, 2) + center_1
data_2 = np.random.randn(200,2) + center_2
data_3 = np.random.randn(200,2) + center_3

data = np.concatenate((data_1, data_2, data_3), axis = 0)

plt.scatter(data[:,0], data[:,1], s=7)

k = 3

n = data.shape[0]

c = data.shape[1]

mean = np.mean(data, axis = 0)
std = np.std(data, axis = 0)
centers = np.random.randn(k,c)*std + mean

plt.scatter(data[:,0], data[:,1], s=7)
plt.scatter(centers[:,0], centers[:,1], marker='*', c='g', s=150)

centers_old = np.zeros(centers.shape)
centers_new = deepcopy(centers)
clusters = np.zeros(data.shape[0])
distances = np.zeros((data.shape[0], k))
error = np.linalg.norm(centers_new - centers_old)

while error != 0:
    for i in range(k):
        distances[:,i] = np.linalg.norm(data - centers_new[i], axis = 1)
    clusters = np.argmin(distances, axis=1)
    centers_old = deepcopy(centers_new)
    for i in range(k):
        centers_new[i] = np.mean(data[clusters == i], axis = 0)
    error = np.linalg.norm(centers_new - centers_old)

plt.scatter(data[:,0], data[:,1], s=7)
plt.scatter(centers_new[:,0], centers_new[:,1], marker='*', c='g', s=150)


import numpy as np

from pymanopt.manifolds.manifold import Manifold

class PoincareBall(Manifold):
    """
    Factory class for the Poincare ball model of hyperbolic geometry.
    An instance represents the Cartesian product of n (defaults to 1)
    Poincare balls of dimension k.
    Elements are represented as matrices of size k x n, or arrays of
    size k if n = 1.
    The Poincare ball is embedded in R^k and is a Riemannian manifold,
    but it is not an embedded Riemannian submanifold.
    At every point x, the tangent space at x is R^k since the manifold
    is open.
    The metric is conformal to the Euclidean one (angles are preserved),
    and it is given at every point x by
        <u, v>_x = lambda_x^2 <u, v>,
    where lambda_x = 2 / (1 - norm{x}^2) is the conformal factor.
    This induces the following distance between any two points x and y:
        dist(x, y) = acosh(
            1 + 2 norm{x - y}^2 / (1 - norm{x}^2) / (1 - norm{y}^2)
        ).
    """

    def __init__(self, k: int, n: int = 1):
        self._k = k
        self._n = n

        if k < 1:
            raise ValueError("Need k >= 1. Value supplied was k = %d." % k)
        if n < 1:
            raise ValueError("Need n >= 1. Value supplied was n = %d." % n)

        if n == 1:
            name = "Poincare ball B({:d})".format(k)
        elif n >= 2:
            name = "Product Poincare ball B({:d})^{:d}".format(k, n)

        dimension = k * n
        super().__init__(name, dimension)

    @property
    def typicaldist(self):
        return self.dim / 8

    # The metric in the Poincare ball is conformal to the Euclidean one.
    def conformal_factor(self, X):
        return 2 / (1 - np.sum(X * X, axis=0))

    def inner(self, X, G, H):
        factors = np.square(self.conformal_factor(X))
        return np.sum(G * H * factors)

    # Identity map since the tangent space is the ambient space.
    def proj(self, X, G):
        return G

    def norm(self, X, G):
        return np.sqrt(self.inner(X, G, G))

    # Generates points sampled uniformly at random in the unit ball.
    # In high dimension (large k), sampled points are very likely to
    # be close to the boundary because of the curse of dimensionality.
    def rand(self):
        if self._n == 1:
            N = np.random.randn(self._k)
        else:
            N = np.random.randn(self._k, self._n)
        norms = np.linalg.norm(N, axis=0)
        radiuses = np.random.rand(self._n) ** (1.0 / self._k)
        return radiuses * N / norms

    def randvec(self, X):
        return np.random.randn(*np.shape(X))

    def zerovec(self, X):
        return np.zeros(np.shape(X))

    # Geodesic distance.
    def dist(self, X, Y):
        norms2_X = np.sum(X * X, axis=0)
        norms2_Y = np.sum(Y * Y, axis=0)
        difference = X - Y
        norms2_difference = np.sum(difference * difference, axis=0)

        columns_dist = np.arccosh(
            1 + 2 * norms2_difference / ((1 - norms2_X) * (1 - norms2_Y))
        )
        return np.sqrt(np.sum(np.square(columns_dist)))

    # The hyperbolic metric tensor is conformal to the Euclidean one,
    # so the Euclidean gradient is simply rescaled.
    def egrad2rgrad(self, X, G):
        factors = np.square(1 / self.conformal_factor(X))
        return G * factors

    # Derived from the Koszul formula.
    def ehess2rhess(self, X, G, H, U):
        lambda_x = self.conformal_factor(X)
        return (
            np.sum(G * X, axis=0) * U
            - np.sum(X * U, axis=0) * G
            - np.sum(G * U, axis=0) * X
            + H / lambda_x
        ) / lambda_x

    # Exponential map is cheap so use it as a retraction.
    def retr(self, X, G):
        return self.exp(X, G)

    # Special non-associative and non-commutative operation
    # which is closed in the Poincare ball.
    # Performed column-wise here.
    def mobius_addition(self, X, Y):
        scalar_product = np.sum(X * Y, axis=0)
        norm2X = np.sum(X * X, axis=0)
        norm2Y = np.sum(Y * Y, axis=0)

        return (X * (1 + 2 * scalar_product + norm2Y) + Y * (1 - norm2X)) / (
            1 + 2 * scalar_product + norm2X * norm2Y
        )

    def exp(self, X, U):
        norm_U = np.linalg.norm(U, axis=0)
        # Handle the case where U is null.
        W = U * np.divide(
            np.tanh(norm_U / (1 - np.sum(X * X, axis=0))),
            norm_U,
            out=np.zeros_like(U),
            where=norm_U != 0,
        )
        return self.mobius_addition(X, W)

    def log(self, X, Y):
        W = self.mobius_addition(-X, Y)
        norm_W = np.linalg.norm(W, axis=0)
        return (1 - np.sum(X * X, axis=0)) * np.arctanh(norm_W) * W / norm_W

    # I don't have a nice expression for this.
    # To be completed in the future.
    def transp(self, X1, X2, G):
        raise NotImplementedError

    def pairmean(self, X, Y):
        return self.exp(X, self.log(X, Y) / 2)


import numpy as np
np.seterr(divide='ignore', invalid='ignore') #para ignorar el error de dividir por 0
import matplotlib.pyplot as plt

# Orden
n_terms = 5

# valores de x
x_vals = np.linspace(0,1,100)

# valores de y
y_vals = np.zeros([n_terms,len(x_vals)])

for n in range(n_terms):
    #Para orden 0
    if n == 0:
        y_vals[n,:] = np.ones(len(x_vals))
    else:
        new_term = 1**n * (x_vals**n)
        y_vals[n,:] = y_vals[n-1,:] + new_term

# Graficar los valores de los terminos
for n in range(n_terms):
    #if n == 25:
        label = str(n) + " order"
        plt.plot(x_vals,y_vals[n,:], label=label)

# grafica la funcion real
plt.plot(x_vals, 1/(1-x_vals), label="$1/(1-x)$")
plt.xlabel("x")
plt.ylabel("y")
plt.ylim([0,25])
plt.legend()


import math
import numpy as np
import torch

'''
Point cloud transform:
    pc:
        torch: [b, 3, p]
        np: [(b, )3, p]
'''

# translation normalization
def centralize(pc):
    return pc - pc.mean(dim=2, keepdim=True)

def centralize_np(pc, batch=False):
    axis = 2 if batch else 1
    return pc - pc.mean(axis=axis, keepdims=True)

# scale/translation normalization
def normalize(pc):
    pc = centralize(pc)
    var = pc.pow(2).sum(dim=1, keepdim=True).sqrt()
    return pc / var.max(dim=2, keepdim=True)

def normalize_np(pc, batch=False):
    pc = centralize_np(pc, batch)
    axis = 1 if batch else 0
    var = np.sqrt((pc**2).sum(axis=axis, keepdims=True))
    return pc / var.max(axis=axis+1, keepdims=True)


import numpy as np
import math

'''
The spherical geometry module is a simple collection of calculations on a sphere
'''

def sphere_distance(lat1,lon1,lat2,lon2):
    '''
    Calculate the great circle distance between two points on the sphere designated by their latitude and longitude

    :param lat1: latitude of first point
    :param lon1: longitude of first point
    :param lat2: latitude of second point
    :param lon2: longitude of second point
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the great circle distance in kilometres between the two points
    :rtype: float

    The great circle distance is the shortest distance between any two points on the Earths surface.
    The calculation is done by first calulating the angular distance between the points and then
    multiplying that by the radius of the Earth. The angular distance calculation is handled by
    another function.
    '''
    earths_radius = 6400.0
    delta = angular_distance(lat1,lon1,lat2,lon2)*earths_radius
    return delta

def angular_distance(lat1,lon1,lat2,lon2):
    '''
    :param lat1: latitude of first point
    :param lon1: longitude of first point
    :param lat2: latitude of second point
    :param lon2: longitude of second point
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the angular great circle distance between the two points in radians
    :rtype: float
    '''
#calculate distance between two points on a sphere
#input latitudes and longitudes should be in degrees
#output is in radians
    assert lat1 != None and not(math.isnan(lat1))
    assert lon1 != None and not(math.isnan(lon1))
    assert lat2 != None and not(math.isnan(lat2))
    assert lon2 != None and not(math.isnan(lon2))

    radians_per_degree = np.pi / 180.

#convert degrees to radians
    lat1 = lat1 * radians_per_degree
    lon1 = lon1 * radians_per_degree
    lat2 = lat2 * radians_per_degree
    lon2 = lon2 * radians_per_degree

    delta_lambda = abs(lon1-lon2)
    bit1 = (np.cos(lat2)*np.sin(delta_lambda))
    bit1 = bit1*bit1
    bit2 = ( np.cos(lat1)*np.sin(lat2) - np.sin(lat1)*np.cos(lat2)*np.cos(delta_lambda) )
    bit2 = bit2 * bit2
    topbit = bit1 + bit2
    topbit = np.sqrt(topbit)
    bottombit = np.sin(lat1)*np.sin(lat2) + np.cos(lat1)*np.cos(lat2)*np.cos(delta_lambda)
    delta = np.arctan2(topbit, bottombit)

    return delta

def lat_lon_from_course_and_distance(lat1, lon1, tc, d):
    '''
    :param lat1: latitude of first point in degrees
    :param lon1: longitude of first point in degrees
    :param tc: true course measured clockwise from north in degrees
    :param d: distance travelled in kilometres
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the new latitude and longitude
    :rtype: float
    '''
#calculate a latitude and longitude given a starting point, course (in radian) and angular distance (also in radians)
#http://williams.best.vwh.net/avform.htm#LL
    radians_per_degree = np.pi / 180.
    lat1 = lat1 * radians_per_degree
    lon1 = lon1 * radians_per_degree
    tcr = tc * radians_per_degree

    dr = d/6400.

    lat =np.arcsin(np.sin(lat1)*np.cos(dr)+np.cos(lat1)*np.sin(dr)*np.cos(tcr))
    dlon=np.arctan2(np.sin(tcr)*np.sin(dr)*np.cos(lat1),np.cos(dr)-np.sin(lat1)*np.sin(lat))
    lon= math.fmod( lon1 + dlon + np.pi, 2.*np.pi ) - np.pi

    lat = lat / radians_per_degree
    lon = lon / radians_per_degree

    return (lat,lon)

def course_between_points(lat1,lon1,lat2,lon2):
    '''
    :param lat1: latitude of first point in degrees
    :param lon1: longitude of first point in degrees
    :param lat2: latitude of second point in degrees
    :param lon2: longitude of second point in degrees
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :return: return the initial true course in degrees at point one along the great circle between point one and point two
    :rtype: float
    '''
#given two points find the initial true course at point1
#inputs are in degrees and output is in degrees
    d = angular_distance(lat1,lon1,lat2,lon2)
    radians_per_degree = np.pi / 180.

    if d != 0:

        lat1 = lat1 * radians_per_degree
        lon1 = lon1 * radians_per_degree
        lat2 = lat2 * radians_per_degree
        lon2 = lon2 * radians_per_degree

        if np.cos(lat1) < 0.0000001:
            if lat1 > 0:
                tc1 = np.pi
            else:
                tc1 = 2.*np.pi
        else:
            if np.sin(lon2-lon1) > 0:
                if ((np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) <= 1.0 and
                    (np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) >= -1.0):
                    tc1 = np.arccos(
                                    (np.sin(lat2)-np.sin(lat1)*np.cos(d))/
                                    (np.sin(d)*np.cos(lat1))
                                    )
                else:
                    tc1 = float('nan')
            else:
                if ((np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) <= 1.0 and
                    (np.sin(lat2)-np.sin(lat1)*np.cos(d))/(np.sin(d)*np.cos(lat1)) >= -1.0):
                    tc1 = 2.*np.pi-np.arccos(
                                             (np.sin(lat2)-np.sin(lat1)*np.cos(d))/
                                             (np.sin(d)*np.cos(lat1))
                                             )
                else:
                    tc1 = float('nan')

        if math.isnan(tc1):
            tc1=math.fmod(np.arctan2(np.sin(lon1-lon2)*np.cos(lat2),
                    np.cos(lat1)*np.sin(lat2)-np.sin(lat1)*np.cos(lat2)*np.cos(lon1-lon2)), 2*np.pi)

    else:

        tc1 = 0.0

    return tc1/radians_per_degree

def intermediate_point(lat1,lon1,lat2,lon2,f):
    '''
    :param lat1: latitude of first point in degrees
    :param lon1: longitude of first point in degrees
    :param lat2: latitude of second point in degrees
    :param lon2: longitude of second point in degrees
    :param f: fraction of distance between the two points
    :type lat1: float
    :type lon1: float
    :type lat2: float
    :type lon2: float
    :type f: float
    :return: return the latitude and longitude of the point a fraction f along the great circle between the first and second points.
    :rtype: float
    '''
#given two lat,lon point find the latitude and longitude that are a fraction f
#of the great circle distance between them
#http://williams.best.vwh.net/avform.htm#Intermediate
    assert f <= 1.0,f

    d = angular_distance(lat1,lon1,lat2,lon2)

    if d != 0.0:
#convert degrees to radians
        radians_per_degree = np.pi / 180.
        lat1 = lat1 * radians_per_degree
        lon1 = lon1 * radians_per_degree
        lat2 = lat2 * radians_per_degree
        lon2 = lon2 * radians_per_degree

        A = np.sin((1-f)*d)/np.sin(d)
        B = np.sin(f*d)/np.sin(d)
        x = A*np.cos(lat1)*np.cos(lon1) +  B*np.cos(lat2)*np.cos(lon2)
        y = A*np.cos(lat1)*np.sin(lon1) +  B*np.cos(lat2)*np.sin(lon2)
        z = A*np.sin(lat1)              +  B*np.sin(lat2)
        lat=np.arctan2(z,np.sqrt(x*x+y*y)) / radians_per_degree
        lon=np.arctan2(y,x) / radians_per_degree
    else:
        lat = lat1
        lon = lon1

    return (lat,lon)


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import math

#################################
#        General helpers        #
#################################

def binary_search(t, x, start, end):
    """Find the index i of t with t[i] <= x <= t[i+1].
    Under the assumption that t is nondecreasing t[j] <= t[j+1]."""
    mid = math.floor((start + end) / 2)
    if ((start - end) == 0) or ((t[mid] <= x) and (x < t[mid+1])):
        return mid
    elif t[mid] <= x:
        return binary_search(t, x, mid + 1, end)
    else:
        return binary_search(t, x, start, mid - 1)

def nielson_metric(Q, p1, p2):
    U = np.asarray(p1)
    V = np.asarray(p2)
    return math.sqrt((U-V).dot(Q.dot(U-V)))

def nielson_matrix(points):
    x = np.asarray(points[0])
    y = np.asarray(points[1])
    n = len(x)
    x_hat = np.sum(x) / n
    y_hat = np.sum(y) / n
    v_x = sum([(x[i]-x_hat)**2 for i in range(0, n)]) / n
    v_y = sum([(y[i]-y_hat)**2 for i in range(0, n)]) / n
    v_xy = sum([(x[i]-x_hat)*(y[i]-y_hat) for i in range(0, n)]) / n
    g = v_x * v_y - v_xy**2
    Q = np.array([[v_y/g, -(v_xy/g)],
                  [-(v_xy/g), v_x/g]])
    return Q


def compute_likelihood_normal(x, mean_val, standard_dev_val):
  """ Computes the log-likelihood values given a observed data sample x, and
  potential mean and variance values for a normal distribution

    Args:
      x (ndarray): 1-D array with all the observed data
      mean_val (scalar): value of mean for which to compute likelihood
      standard_dev_val (scalar): value of variance for which to compute likelihood

    Returns:
      likelihood (scalar): value of likelihood for this combination of means/variances
  """

  # Get probability of each data point (use norm.pdf from scipy stats)
  p_data = norm.pdf(x, mean_val, standard_dev_val)

  # Compute likelihood (sum over the log of the probabilities)
  likelihood = np.sum(np.log(p_data))

  return likelihood

# Set random seed
np.random.seed(0)

# Generate data
true_mean = 5
true_standard_dev = 1
n_samples = 1000
x = np.random.normal(true_mean, true_standard_dev, size = (n_samples,))

# Compute likelihood for a guessed mean/standard dev
guess_mean = 4
guess_standard_dev = .1
likelihood = compute_likelihood_normal(x, guess_mean, guess_standard_dev)
print(likelihood)


import numpy as np

def cosine_similarity(vec1 : np.ndarray, vec2 : np.ndarray):
    """the cosine similarity of two vectors

    using this formula : $$ cosine_similarity(x,y) = \frac{ |x·y| } { |x|·|y| } $$

    Parameters
    ----------

    vec1: shape (1, n_features) or shape(n_features,)

    vec2: shape (1, n_features) or shape(n_features,)

    Returns
    --------

    distances : shape (1, 1)

    """
    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    mult_sum = (vec1 * vec2).sum()
    norm_vec1 = np.linalg.norm(vec1)
    norm_vec2 = np.linalg.norm(vec2)
    eps = 0.000000001
    # print(mult_sum)
    return mult_sum/(norm_vec1 * norm_vec2 + eps)

def cosine_distance(vec1, vec2):
    """the cosine distance of two vectors

    Cosine distance is defined as 1.0 minus the cosine similarity.

    Parameters
    ----------

    vec1 : shape (1, n_features) or shape(n_features,)

    vec2 : shape (1, n_features) or shape(n_features,)

    Returns
    --------

    distances : shape (1, 1)

    """
    return 1.0 - cosine_similarity(vec1, vec2)

def euclidean_distance(vec1, vec2):
    """the euclidean distance of two vectors

    Parameters
    -----------

    vec1: shape (1, n_features) or shape(n_features,)

    vec2: shape (1, n_features) or shape(n_features,)

    Returns
    -------

    distances : shape (1, 1)

    """

    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    return np.linalg.norm(vec1-vec2)

def absolute_distance(vec1, vec2):
    """the euclidean distance of two vectors

    Parameters
    -----------

    vec1: shape (1, n_features) or shape(n_features,)

    vec2: shape (1, n_features) or shape(n_features,)

    Returns
    -------

    distances : shape (1, 1)

    """
    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    return np.abs(vec1-vec2).sum()

def l_p_distance(vec1, vec2,p=1):
    """ calculate the p(default 1) norm of two vectors

    Parameters
    ------------

    vec1 : shape (1, n_features) or shape(n_features,)

    vec2 : shape (1, n_features) or shape(n_features,)

    Returns
    --------

    distances : shape (1, 1)

    """

    vec1 = vec1.reshape((1,-1))
    vec2 = vec2.reshape((1,-1))
    assert(vec1.shape[0] == 1)
    assert(vec2.shape[0] == 1)
    assert(vec1.shape[1] == vec2.shape[1])
    if (p == np.Inf):
        return np.max(np.abs(vec1-vec2))
    return np.power(np.power(np.abs(vec1-vec2),p).sum(),1/p)


import numpy as np
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt

class LogisticRegression:

    def __init__(self):
        pass

    @staticmethod
    def sigmoid(X):
        return 1 / (1 + np.exp(-X))

    @staticmethod
    def init_params(dims):
        W = np.random.rand(dims, 1)
        b = 0
        return W, b

    def loss_grad(self, X, Y, W, b):
        """Calculate logistic regression cross-entropy loss, weight and bias gradients."""
        # number of samples
        num_samples = X.shape[0]
        # predicted Y probability
        Y_hat = self.sigmoid(np.dot(X, W) + b)
        # loss
        loss = (-1) / num_samples * np.sum(Y * np.log(Y_hat) + (1 - Y) * np.log(1 - Y_hat), axis=None)
        # weight and bias gradients
        dW = np.dot(X.T, (Y_hat - Y)) / num_samples
        db = np.sum(Y_hat - Y) / num_samples
        return loss, dW, db

    def train(self, X, Y, lr, epochs):
        W, b = self.init_params(X.shape[1])
        loss_list = []
        for epoch in range(epochs):
            loss, dW, db = self.loss_grad(X, Y, W, b)
            loss_list.append(loss)
            W -= lr * dW
            b -= lr * db
            if epoch % 100 == 0:
                print(f'Epoch {epoch:4d} loss {loss:.4f}')

        return loss_list, W, b

    def predict_prob(self, X, W, b):
        return self.sigmoid(np.dot(X, W) + b)

    def predict(self, X, W, b):
        prob = self.predict_prob(X, W, b)
        return (prob > 0.5).astype(np.int8)

    @staticmethod
    def load_data():
        X, Y = make_classification(n_samples=1000, n_features=30, n_informative=2, n_redundant=2, n_repeated=10,
                                   n_classes=2, n_clusters_per_class=2, random_state=42)
        Y = Y.reshape(-1, 1)
        return X, Y

if __name__ == '__main__':
    model = LogisticRegression()
    X, Y = model.load_data()
    loss_list, W, b = model.train(X, Y, 1e-3, int(1e5))
    plt.plot(loss_list)
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Dec 22 10:54:50 2020

@author: Jane Hsieh (Hsing-Chuan, Hsieh)

@article:
    Generate random sample of dice (with Faif and Loaded dice used interchangeably)
"""
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

os.getcwd()

#0. ================================================ Functions ===============================================

def InitialState(pi_0=0.5):
    '''
    function to generate initial state S_0 out of {F,L} of the dice with prob-p(S_0=F) = p(F)=pi_0 (0:F; 1:L)
    Input:
        pi_0: defaul= 0.5 which is the initial prob of hiddent state p(S_0 = 0)  [0: fair dice; 1: Loaded dice]
                (pi1 := p(S_0 = 1) = 1-pi_0)
    Output:
        S_0: initial generated hidden state
    '''
    S_0 = np.random.choice(2, 1, p=[pi_0, 1-pi_0])[0] #Default: replace=True
    return S_0

def EmitObs(S_t, Dice, pF, pL):
    '''
    function to generate observation O_t according to the mechanism of hidden state S_t --> Emit Observation
    Input:
        S_t: current hidden state
        Dice: face point of dice, e.g., [1,2,...,6]
        pF: distributions of Fair Dice, say, with point = [1,2,...,6]
        pL: distributions of Loaded Dice, say, with point = [1,2,...,6]
    '''
    if S_t == 0:
        #Fair dice state with prob = pF
        O_t = np.random.choice(Dice, 1, replace=True, p=pF)[0]
    if S_t == 1:
        #Loaded dice state with prob = pL
        O_t = np.random.choice(Dice, 1, replace=True, p=pL)[0]
    return O_t

def TransitState(S_t, p01, p10):
    '''
    function to generate the new hidden state S_t according to the mechanism of previous hidden state S_t
    Input:
        S_t: current hidden state
        p01: Transition prob from 0(F) to 1(L)); p00 =  1- p01
        p10: Transition prob from 1(L) to 0(F); p11 = 1-p10
    '''
    if S_t == 0:
        #Fair dice state with prob = pF
        S_new = np.random.choice(2, 1, replace=True, p=[1-p01, p01])[0]
    if S_t == 1:
        #Loaded dice state with prob = pL
        S_new = np.random.choice(2, 1, replace=True, p=[p10, 1-p10])[0]
    return S_new

def GenerateFLDice(N, p01, p10, pL, pF=[1/6 for i in range(6)], Dice = [i+1 for i in range(6)], pi_0=0.5, seed=None):
    '''
    Generate random sample of dice (with predefined Fair(F) and Loaded(L) dice used interchangeably)

    Input:
        N: sample size
        seed: random seed– None or int
        Dice: face point of the dice; default = [1,2,...,6]
        pF, pL: distributions of Fair vs Loaded Dice (length of pF, pL must be the same with Dice)
        pi_0: initial probability of hidden state S_0 – p(S_0 = 0)  [0: fair dice; 1: Loaded dice]
        p01, p10: Transition prob of hidden state from 0(F) to 1(L)) & from 1(L) to 0(F), respectively
    Output:
        Sample_S: sequence of hidden states {S_t} from t=0,...,N-1
        Sample_O: sequence of observations {O_t} from t=0,...,N-1

    '''
    np.random.seed(seed)
    Sample_S = []
    Sample_O = []

    t=0
    # Generate initial hidden state S_t & the Observed state O_t out of it
    S_t = InitialState(pi_0)
    O_t = EmitObs(S_t, Dice, pF, pL)

    Sample_S.append(S_t)
    Sample_O.append(O_t)

    t += 1

    while t < N:
        #Generate New S_t at next time (i.e., t+1) point according to previous mechanism of S_t;
        #and O_t out of New S_t
        S_t = TransitState(S_t, p01, p10)
        O_t = EmitObs(S_t, Dice, pF, pL)

        Sample_S.append(S_t)
        Sample_O.append(O_t)

        t += 1

    return Sample_S, Sample_O

#1. =============== Set parameters for sampling & those of Hidden Markov Model(HMM) =============================
## Sampling-related parameters
seed = 123
N = 5000  #sample size

## HMM-related parameters
pi_0 = 0.5 # initial prob of hiddent state p(S_0 = 0)  [0: fair dice; 1: Loaded dice]; p(S_0=1) = 1-pi_0
p01 = 0.05  # Transition prob from 0(F) to 1(L)); p00 =  1- p01
p10 = 0.05  # Transition prob from 1(L) to 0(F); p11 = 1-p10

### Emission probs: pF vs pL are distributions of Fair vs Loaded Dice  with point = [1,2,...,6], respectively
### Note: length of Dice, pF, pL must be the same
Dice = [i+1 for i in range(6)]
pF = [1/6 for i in range(6)]
pL=[0.1, 0.1, 0.1, 0.1, 0.1, 0.5]

#2. ============================= Generate sequence of {O_t}, {S_t} from t=0,...,N-1 ====================
## To reproduce the experiment, seed is fixed; o.w. you can set seed = None for fully random gereration
Sample_Hid, Sample_Obs  = GenerateFLDice(N=N, p01=p01, p10=p10, pL=pL, pF=pF, Dice = Dice, pi_0=pi_0, seed=seed)
#or equal to below:
#Sample_Hid, Sample_Obs = GenerateFLDice (N=N, p01=p01, p10=p10, pL=pL, seed=seed, pi_0 = pi_0)

Sample_Hid  = pd.Series(Sample_Hid, name = 'State')
Sample_Obs = pd.Series(Sample_Obs, name = 'Observation')

# Output data
Sample_Obs.to_csv('Sample_Obs.csv')
Sample_Hid.to_csv('Sample_Hid.csv')

## Data Visualization --------------------------------------------------------------------------------------------
data = pd.concat([Sample_Hid,Sample_Obs],axis=1)

def plot_timeseries(axes, x, y, color, xlabel, ylabel, linestyle='-', marker='None'):
    axes.plot(x, y, color=color, linestyle = linestyle, marker = marker, ms = 2)
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel, color=color)
    axes.tick_params('y', colors=color)

n= 200
sample = data[:n]

fig, ax = plt.subplots(figsize = (10,5))
plot_timeseries(ax, sample.index, sample['Observation'],'blue', 'index', 'Dice Observations', linestyle='None', marker='o')
ax2 = ax.twinx()
plot_timeseries(ax2, sample.index, sample['State'],'orange', 'index', 'Hidden States')
ax2.set_yticks([0,1])
ax.set_title('First {} cases of dice sequence'.format(n))
plt.show()

fig.savefig('First {} cases of dice sequence.png'.format(n), dpi = 300)

Hidden Markov Model (HMM)/Dice Simulation Data/Step1. Generate Fair and Loaded Dice Sample/Step1. Generate Fair and Loaded Dice Sample.py


# PACKAGE: DO NOT EDIT THIS LINE
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import scipy

import sklearn
from ipywidgets import interact
from load_data import load_mnist
MNIST = load_mnist()
images = MNIST['data'].astype(np.double)
labels = MNIST['target'].astype(np.int)

# GRADED FUNCTION: DO NOT EDIT THIS LINE

def distance(x0, x1):
    """Compute distance between two vectors x0, x1 using the dot product"""
    return np.sqrt(np.dot(x0-x1, x0-x1))

def angle(x0, x1):
    """Compute the angle between two vectors x0, x1 using the dot product"""
    mx0 = np.dot(x0, x0)
    mx1 = np.dot(x1, x1)
    return np.arccos(np.dot(x0,x1)/np.sqrt(mx0*mx1))

# GRADED FUNCTION: DO NOT EDIT
def most_similar_image():
    """Find the index of the digit, among all MNIST digits
       that is the second-closest to the first image in the dataset (the first image is closest to itself trivially).
       Your answer should be a single integer.
    """
    ref = images[0] # reference image
    result = np.linalg.norm(images[1:].astype(np.float) - ref.astype(np.float), axis=1)
    index = np.argmin(result)+1
    return index # 60

# GRADED FUNCTION: DO NOT EDIT

def pairwise_distance_matrix(X, Y):
    """Compute the pairwise distance between rows of X and rows of Y

    Arguments
    ----------
    X: ndarray of size (N, D)
    Y: ndarray of size (M, D)

    Returns
    --------
    distance_matrix: matrix of shape (N, M), each entry distance_matrix[i,j] is the distance between
    ith row of X and the jth row of Y (we use the dot product to compute the distance).
    """
    assert X.ndim == 2
    assert Y.ndim == 2
    return scipy.spatial.distance_matrix(X, Y)

# GRADED FUNCTION: DO NOT EDIT THIS LINE

def KNN(k, X, y, x):
    """K nearest neighbors
    k: number of nearest neighbors
    X: training input locations
    y: training labels
    x: test input
    """
    N, D = X.shape
    num_classes = len(np.unique(y))
    dist = pairwise_distance_matrix(X, x.reshape(1, -1)).reshape(-1)

    # Next we make the predictions
    ypred = np.zeros(num_classes)
    classes = y[np.argsort(dist)][:k] # find the labels of the k nearest neighbors
    for c in np.unique(classes):
        ypred[c] = len(classes[classes == c])

    return np.argmax(ypred)


# -*- coding: utf-8 -*-
"""
Esecizio 3
"""

from scipy.fft  import fft
import math
import numpy as np

import matplotlib.pyplot as plt
'''
Il problema fornisce n+1 (xi,yi), i=0,.n n=9 misurazioni del flusso sanguigno
attraverso una sezione dell’arteria carotide durante un battito cardiaco.---
ad istanti di tempo equistanti con step 1/10
Gli istanti appartengono all'intervallo [0,1)

'''

n = 9;
A = 0
B = 1
l = 0
r = 2 * math.pi

step = (B - A) / (n + 1)
x = np.arange(A, B, step)

y = np.array([3.7, 13.5, 5, 4.6, 4.1, 4.5, 4, 3.8, 3.7, 3.7])

if n % 2 == 0:
    m = n // 2
else:
    m = (n - 1) // 2
c = fft(y)
a = np.zeros((m + 2, ))
b = np.zeros((m + 2, ))
a[1 : m + 1] = 2 * c[1 : m + 1].real / (n + 1)
b[1 : m + 1] = -2 * c[1 : m + 1].imag / (n + 1)
a0 = c[0] / (n + 1)
if n % 2 == 0:
    a[m + 1] = 0
    b[m + 1] = 0
else:
    a[m + 1] = c[m + 1] / (n + 1)
    b[m + 1] = 0
pol = a0 * np.ones((100,))
z = np.linspace(A, B, 100)
zm = ((z - A) / (B - A)) * r
for k in range(1, m + 2):
    pol += a[k] * np.cos(k * zm) + b[k] * np.sin(k * zm)
plt.plot(z, pol, "b", x, y, "ro")
plt.legend(["Polinomio", "Pti. interpolati"])
plt.show()


#!/usr/bin/env python
# -*- coding: utf8 -*-
# y_true: list, the true labels of input instances
# y_pred: list, the probability when the predicted label of input instances equals to 1
import numpy as np
from sklearn.metrics import log_loss

def logloss(y_true, y_pred, eps=1e-15):

    # Prepare numpy array data
    y_true = np.array(y_true)
    y_pred = np.array(y_pred)
    assert (len(y_true) and len(y_true) == len(y_pred))

    # Clip y_pred between eps and 1-eps
    p = np.clip(y_pred, eps, 1-eps)
    loss = np.sum(- y_true * np.log(p) - (1 - y_true) * np.log(1-p))

    return loss / len(y_true)

def cross_entropy(y_true, y_pred):
    """Cross-Entropy loss function.
    以向量化的方式实现交叉熵函数
    """
    y_true = np.float_(y_true)
    y_pred = np.float_(y_pred)
    return -np.sum(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred)) / len(y_true)

def unitest():
    y_true = [0, 0, 1, 1]
    y_pred = [0.1, 0.2, 0.7, 0.99]

    print("Use self-defined logloss() in binary classification, the result is {}".format(logloss(y_true, y_pred)))
    print("Use log_loss() in scikit-learn, the result is {} ".format(log_loss(y_true, y_pred)))
    print(cross_entropy(y_true,y_pred))
if __name__ == '__main__':
    unitest()


import unittest
from statistics import stdev
from StatsCalc import StatsCalculator
from scipy import stats
from CsvReader import CsvReader
# from pprint import pprint

class MyTestCase(unittest.TestCase):
    def setUp(self):
        self.statsCalculator = StatsCalculator()

    # To test instantiation of calculator class
    def test_instantiate_statistics_calculator(self):
        self.assertIsInstance(self.statsCalculator, StatsCalculator)

    # Testing mean
    def test_mean(self):
        print('---------------test mean------------------')
        test_data = CsvReader('./src/data/Mean_Data.csv').data
        for row in test_data:
            self.assertEqual(self.statsCalculator.mean(nums=(int(row['Value1']), int(row['Value2']), int(row['Value3']), int(row['Value4']), int(row['Value5']))), float(row['Mean']))
            self.assertEqual(self.statsCalculator.result, float(row['Mean']))
        test_data.clear()

    # Testing median
    def test_median(self):
        print('---------------test median------------------')
        test_data = CsvReader('./src/data/Median_Data.csv').data
        # print(test_data)
        for row in test_data:
            data = [float(row['Value1']), float(row['Value2']), float(row['Value3']),
                    float(row['Value4']), float(row['Value5'])]
            # print('data: ', data)
            # print(type(data))
            self.assertEqual(self.statsCalculator.median(data), float(row['Median']))
            self.assertEqual(self.statsCalculator.result, float(row['Median']))
        test_data.clear()

    # Testing mode
    def test_mode(self):
        print('---------------test mode------------------')
        test_data = CsvReader('./src/data/Mode_Data.csv').data
        # pprint(test_data)
        for row in test_data:
            data = [float(row['Value1']), float(row['Value2']), float(row['Value3']),
                    float(row['Value4']), float(row['Value5'])]
            self.assertEqual(self.statsCalculator.mode(data), float(row['Mode']))
            self.assertEqual(self.statsCalculator.result, float(row["Mode"]))
        test_data.clear()

    # Testing variance (population)
    def test_variance(self):
        print('---------------test variance------------------')
        test_data = CsvReader('./src/data/Variance_Data.csv').data
        # print(test_data)
        for row in test_data:
            a = self.statsCalculator.variance([int(row['Value1']), int(row['Value2']), int(row['Value3']), int(row['Value4']), int(row['Value5'])])
            b = float(row['Variance'])
            a, b = transformFloat(str(a), str(b))
            self.assertEqual(a, b)

            a = self.statsCalculator.result
            b = float(row['Variance'])
            a, b = transformFloat(str(a), str(b))
            self.assertEqual(a, b)
        test_data.clear()

    # Testing standard deviation
    def test_standard_deviation(self):
        print('---------------test standard deviation------------------')
        test_data = CsvReader('./src/data/Standard_Deviation_Data.csv').data
        # print(test_data)
        for row in test_data:
            data = [float(row['Value1']), float(row['Value2']), float(row['Value3']), float(row['Value4']), float(row['Value5'])]
            # print('stdev: ', stdev(data))
            self.assertEqual(self.statsCalculator.standard_deviation(data), stdev(data))
            self.assertEqual(self.statsCalculator.result, stdev(data))
        test_data.clear()

    # Testing z-score
    def test_z_score(self):
        print('---------------test z score------------------')
        test_data = CsvReader('./src/data/ZScore_Data.csv').data
        # print(test_data)
        for row in test_data:
            data1 = float(row['Score'])
            data2 = [float(row['Value1']), float(row['Value2']), float(row['Value3']), float(row['Value4'])]
            data3 = [float(row['Score']), float(row['Value1']), float(row['Value2']), float(row['Value3']),
                     float(row['Value4'])]
            # print('z score: ', stats.zscore(data3))
            self.assertEqual(self.statsCalculator.z_score(data1, data3), stats.zscore(data3)[0])
            self.assertEqual(self.statsCalculator.result, stats.zscore(data3)[0])
        test_data.clear()

def transformFloat(a, b):
    a = str(a)[:len(str(b)) - 1]
    b = b[:-1]
    return a, b

if __name__ == '__main__':
    unittest.main()


import numpy as np
import matplotlib.pyplot as plt

import sys, getopt

def gauss2D(xy, sigma_x, sigma_y, rho):
    '''

    Returns a 2D Gaussian centered at (0,0)

    Arguments:
    xy = tuple with x and y coordinates (assumes mu=0)
    sigma_x = standard deviation in x-coordinate
    sigma_y = standard deviation in y-coordinate
    rho = correlation in x-y plane. -1 < rho < 1

    '''
    x, y = xy
    return np.exp((np.square(x/sigma_x) + np.square(y/sigma_y) - (2*rho*x*y/(sigma_x*sigma_y)))/(2*(np.square(rho)-1)))/(2*np.pi*sigma_x*sigma_y*np.sqrt(1-np.square(rho)))

def j_0(xy, k):
    '''

    Returns the spherical Bessel function j_0 centered at (0,0)

    Arguments:
    xy = tuple with x and y coordinates (assumes mu=0)
    k = scaling factor; rho=kr

    '''
    x, y = xy
    rho = np.sqrt(np.square(x) + np.square(y)) * k
    if rho==0:
        return 1
    else:
        return np.sin(rho)/rho

def PSF(f=gauss2D, f_arg = [5, 5, 0], pix_size=2, patch_size = 100):
    '''

    Calculates a pixelated PSF given telescope parameters and psf function

    Arguments:
    f = PSF shape; default gaussian.
    f_arg = arguments of the psf function f. Default circular gaussian with sigma 5 arcsec.
    pix_size = size of the pixels in arcsec. This should be smaller than the psf width.
    patch_size = How large in arcsec should the pixelated PSF patch be. Default 100 arcsec

    '''

    num_pix = int(patch_size/pix_size)
    mu = (num_pix-1)/2

    pix_psf = np.array([[f(((x-mu) * pix_size, (y-mu) * pix_size), *f_arg) for x in range(num_pix)] for y in range(num_pix)])
    return pix_psf

def main(argv):
    '''

    When called from the command line, plot and show a PSF

    '''
    pix_size = 2
    f_arg = [5,5,0]
    try:
        opts, args = getopt.getopt(argv,"hp:s:r:",["pixel_size=","sigma=", "rho="])
    except getopt.GetoptError:
        print('usage: construct_psf.py -p <pixel size (arcsec)> -s <1 sigma of PSF (arcsec)> -r <correlation>')
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-h':
            print('usage: construct_psf.py -p <pixel size (arcsec)> -s <1 sigma of PSF (arcsec)> -r <correlation>')
            sys.exit()
        elif opt in ("-p", "--pixel_size"):
            pix_size = float(arg)
        elif opt in ("-s", "--sigma"):
            f_arg[0] = float(arg)
            f_arg[1] = float(arg)
        elif opt in ("-r", "--rho"):
            f_arg[2] = float(arg)
    plt.imshow(PSF(pix_size=pix_size, f_arg=f_arg))
    plt.show()
    plt.close()

if __name__ == '__main__':
    # Demo
    main(sys.argv[1:])


import networkx as nx
from scipy.stats import norm
import numpy as np

#######################################################################################################################
### European Options
### CRR model
### Geometric Random Walk
###
### SOURCES:
## SYLLABUS Derivatives Pricing
# CRR model description, Part 1 : pages 91 -> 94
# Rep Strat CRR implementation, Part 2 pages: 45 -> 58
## Yuxing Yan Python for Finance 2nd edition -> the script for the binomial tree was adapted
#######################################################################################################################

def p_bs(spot_price, strike, riskfree_rate, T, volatility, phi):
    d1 = (np.log(spot_price / strike) + T * (riskfree_rate + 0.5 * volatility * volatility)) / (volatility * np.sqrt(T))
    d2 = (np.log(spot_price / strike) + T * (riskfree_rate - 0.5 * volatility * volatility)) / (volatility * np.sqrt(T))
    return phi * (spot_price * norm.cdf(phi * d1) - strike * np.exp(-riskfree_rate * T) * norm.cdf(phi * d2))

def RepStrat_EU_Option_CRR_GRW(CallOrPut, S, K, rf, T, mu, vol, tree__periods):

    ####################################################################################################################
    #####################  START derivative/model specifics, user input transformation             #####################

    arguments = [CallOrPut, S, K, rf, T, mu, vol, tree__periods] #TC skipped bc can just assume its 0
    for arg in arguments:
        if arg == None:
            return [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]

    if  S < 0 or K < 0  or tree__periods < 1:
        return [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0], [0]
    # nbrofsharesLabel, cashLabel, portfolioLabel, optionpriceLabel, intrinsicLabel, stocksLabel,
    #edge_x, edge_y, node_x, node_y, round(u,2), round(d,2), round(probUp,2), round(probDown,2), edge_y_Stock,
    # node_y_Stock, edge_y_Intrinsic, node_y_Intrinsic, edge_y_Optionprice, node_y_Optionprice, edge_y_Portfolio, node_y_Portfolio,
    # edge_y_Cash, node_y_Cash, edge_y_NbrOfShares, node_y_NbrOfShares, tree__periods :27 returns

    if CallOrPut == "Call":
        phi = 1
    elif CallOrPut == "Put":
        phi = -1

    #####################  END derivative/model specifics, user input transformation             #####################
    ####################################################################################################################

    ####################################################################################################################
    #####################                  START accounts initialization                           #####################

    G_Stock, G_Intrinsic, G_Price, G_Portfolio, G_CashAccount, G_Shares = nx.Graph(), nx.Graph(), nx.Graph(), \
                                                                          nx.Graph(), nx.Graph(), nx.Graph()

    stockprices, optionintrinsicvalue, optionprice, labelStocks, labelsIntrinsic, labelsOptionPrice, CashAccount, \
    NbrOfShares, Portfolio, labelPortfolio, labelCash, labelNbrOfShares, EquityAccount, \
    labelEquity = {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}

    ## For the spatial chart
    edge_Y_stock, edge_Y_intrinsic, edge_Y_optionprice, edge_Y_portfolio, edge_Y_cash, edge_Y_nbrofshares = {}, {}, {}, {}, {}, {}
    #####################                      END accounts initialization                         #####################
    ####################################################################################################################

    ####################################################################################################################
    #####################                  START replication strategy                              #####################
    #### dt, up factor, down factor, discounting factor, probUpFactor, probDownFact
    step = T / tree__periods
    u = np.exp(mu * step + vol * np.sqrt(step))
    d = np.exp(mu * step - vol * np.sqrt(step))

    discFact = np.exp(-rf * step)

    probUp = (np.exp(rf * step) - d) / (u - d)
    probDown = 1 - probUp

    stockprices[(0, 1)] = S
    stockprices[(1, 1)] = S * u
    stockprices[(1, 2)] = S * d

    # Create the edges, compute the stock price and option intrinsic value at all nodes
    for i in range(0, tree__periods + 1):
        for j in range(1, i + 2):
            if i < tree__periods:
                # CREATION OF THE EDGES
                G_Stock.add_edge((i, j), (i + 1, j))
                G_Stock.add_edge((i, j), (i + 1, j + 1))

                G_Intrinsic.add_edge((i, j), (i + 1, j))
                G_Intrinsic.add_edge((i, j), (i + 1, j + 1))

                G_Price.add_edge((i, j), (i + 1, j))
                G_Price.add_edge((i, j), (i + 1, j + 1))

                G_Portfolio.add_edge((i, j), (i + 1, j))
                G_Portfolio.add_edge((i, j), (i + 1, j + 1))

                G_CashAccount.add_edge((i, j), (i + 1, j))
                G_CashAccount.add_edge((i, j), (i + 1, j + 1))

                G_Shares.add_edge((i, j), (i + 1, j))
                G_Shares.add_edge((i, j), (i + 1, j + 1))

                # stock price and option intrinsic value at all nodes
                stockprices[(i, j)] = stockprices[(i, j)]
                stockprices[(i + 1, j)] = stockprices[(i, j)] * u
                stockprices[(i + 1, j + 1)] = stockprices[(i, j)] * d

                # labelStocks[(i, j)] = round(stockprices[(i, j)], 5)
                # labelStocks[(i + 1, j)] = round(stockprices[(i + 1, j)], 5)
                # labelStocks[(i + 1, j + 1)] = round(stockprices[(i + 1, j + 1)],5)

                # option intrinsic value at all nodes
                optionintrinsicvalue[(i, j)] = max(phi * (stockprices[(i, j)] - K), 0)
                optionintrinsicvalue[(i + 1, j)] = max(phi * (stockprices[(i + 1, j)] - K), 0)
                optionintrinsicvalue[(i + 1, j + 1)] = max(phi * (stockprices[(i + 1, j + 1)] - K), 0)

                # labelsIntrinsic[(i, j)] = round(optionintrinsicvalue[(i, j)], 5)
                # labelsIntrinsic[(i + 1, j)] = round(optionintrinsicvalue[(i + 1, j)], 5)
                # labelsIntrinsic[(i + 1, j + 1)] = round(optionintrinsicvalue[(i + 1, j + 1)], 5)

                # for the spatial chart
                # edge_Y_stock[(i,j),(i+1,j)] = (labelStocks[(i, j)],labelStocks[(i + 1, j)])
                # edge_Y_stock[(i, j), (i + 1, j + 1)] = (labelStocks[(i, j)],labelStocks[(i + 1, j + 1)])
                # edge_Y_intrinsic[(i, j), (i + 1, j)] = (labelsIntrinsic[(i, j)], labelsIntrinsic[(i + 1, j)])
                # edge_Y_intrinsic[(i, j), (i + 1, j + 1)] = (labelsIntrinsic[(i, j)], labelsIntrinsic[(i + 1, j + 1)])

                edge_Y_stock[(i,j),(i+1,j)] = (stockprices[(i, j)],stockprices[(i + 1, j)])
                edge_Y_stock[(i, j), (i + 1, j + 1)] = (stockprices[(i, j)],stockprices[(i + 1, j + 1)])
                edge_Y_intrinsic[(i, j), (i + 1, j)] = (optionintrinsicvalue[(i, j)], optionintrinsicvalue[(i + 1, j)])
                edge_Y_intrinsic[(i, j), (i + 1, j + 1)] = (optionintrinsicvalue[(i, j)], optionintrinsicvalue[(i + 1, j + 1)])

    # option price at all nodes
    for i in range(tree__periods, -1, -1):
        for j in range(1, i + 2):

            # values at maturity are not discounted given we start there
            if i == tree__periods:
                optionprice[(i, j)] = optionintrinsicvalue[(i, j)]
                # labelsOptionPrice[(i, j)] = round(optionprice[(i, j)], 5)

            # all the others, until price at t=0
            else:
                optionprice[(i, j)] = discFact * (probUp * optionprice[(i + 1, j)] + probDown * optionprice[(i + 1, j + 1)])
                # labelsOptionPrice[(i, j)] = round(optionprice[(i, j)], 5)

                # for the spatial chart
                # edge_Y_optionprice[(i, j), (i + 1, j)] = (labelsOptionPrice[(i, j)], labelsOptionPrice[(i + 1, j)])
                # edge_Y_optionprice[(i, j), (i + 1, j + 1)] = (labelsOptionPrice[(i, j)], labelsOptionPrice[(i + 1, j + 1)])

                edge_Y_optionprice[(i, j), (i + 1, j)] = (optionprice[(i, j)], optionprice[(i + 1, j)])
                edge_Y_optionprice[(i, j), (i + 1, j + 1)] = (optionprice[(i, j)], optionprice[(i + 1, j + 1)])

    Portfolio[(0, 1)] = optionprice[(0, 1)]
    # labelPortfolio[(0, 1)] = str(round(Portfolio[(0, 1)], 5))

    # changer l'algo qui tourne autour des nodes, car trop lent
    for i in range(0, tree__periods + 1):
        for j in range(1, i + 2):
            if i < tree__periods:
                # print(i, j)
                # initialize / After Rebalancing
                NbrOfShares[(i, j)] = (optionprice[(i + 1, j)] - optionprice[(i + 1, j + 1)]) / (stockprices[(i + 1, j)] - stockprices[(i + 1, j + 1)])
                CashAccount[(i, j)] = Portfolio[(i, j)] - NbrOfShares[(i, j)] * stockprices[(i, j)]

                # Labels for the graph
                # if i == 0:
                #     labelCash[(i, j)] = str(round(CashAccount[(i, j)], 5))
                #     labelNbrOfShares[(i, j)] = str(round(NbrOfShares[(i, j)], 5))

                # labelCash[(i, j)] += f",{round(CashAccount[(i, j)], 2)}"
                # labelNbrOfShares[(i, j)] += f",{round(NbrOfShares[(i, j)], 2)}"
                # labelPortfolio[(i, j)] += f",{round(CashAccount[(i, j)] + NbrOfShares[(i, j)] * stockprices[(i, j)], 2)}"

                # labelCash[(i, j)] = f"{round(CashAccount[(i, j)], 5)}"
                # labelNbrOfShares[(i, j)] = f"{round(NbrOfShares[(i, j)], 5)}"
                # labelPortfolio[(i, j)] = f"{round(CashAccount[(i, j)] + NbrOfShares[(i, j)] * stockprices[(i, j)], 5)}"

                # Before Rebalancing
                NbrOfShares[(i + 1, j)] = NbrOfShares[(i, j)]
                CashAccount[(i + 1, j)] = CashAccount[(i, j)] * np.exp(rf * step)
                Portfolio[(i + 1, j)] = CashAccount[(i + 1, j)] + NbrOfShares[(i + 1, j)] * stockprices[(i + 1, j)]

                NbrOfShares[(i + 1, j + 1)] = NbrOfShares[(i, j)]
                CashAccount[(i + 1, j + 1)] = CashAccount[(i, j)] * np.exp(rf * step)
                Portfolio[(i + 1, j + 1)] = CashAccount[(i + 1, j + 1)] + NbrOfShares[(i + 1, j + 1)] * stockprices[(i + 1, j + 1)]

                # Labels for the graph
                # labelCash[(i + 1, j)] = f"{round(CashAccount[(i + 1, j)], 5)}"
                # labelCash[(i + 1, j + 1)] = f"{round(CashAccount[(i + 1, j + 1)], 5)}"

                # labelNbrOfShares[(i + 1, j)] = f"{round(NbrOfShares[(i + 1, j)], 5)}"
                # labelNbrOfShares[(i + 1, j + 1)] = f"{round(NbrOfShares[(i + 1, j + 1)], 5)}"

                # labelPortfolio[(i + 1, j + 1)] = f"{round(Portfolio[(i + 1, j + 1)], 5)}"
                # labelPortfolio[(i + 1, j)] = f"{round(Portfolio[(i + 1, j)], 5)}"

    # re-running the loop now that we have the values for the spatial chart
    for i in range(0, tree__periods + 1):
        for j in range(1, i + 2):
            if i < tree__periods:
                # edge_Y_portfolio[(i, j), (i + 1, j)] = (labelPortfolio[(i, j)], labelPortfolio[(i + 1, j)])
                # edge_Y_portfolio[(i, j), (i + 1, j + 1)] = (labelPortfolio[(i, j)], labelPortfolio[(i + 1, j + 1)])

                # edge_Y_cash[(i, j), (i + 1, j)] = (labelCash[(i, j)], labelCash[(i + 1, j)])
                # edge_Y_cash[(i, j), (i + 1, j + 1)] = (labelCash[(i, j)], labelCash[(i + 1, j + 1)])

                # edge_Y_nbrofshares[(i, j), (i + 1, j)] = (labelNbrOfShares[(i, j)], labelNbrOfShares[(i + 1, j)])
                # edge_Y_nbrofshares[(i, j), (i + 1, j + 1)] = (labelNbrOfShares[(i, j)], labelNbrOfShares[(i + 1, j + 1)])

                edge_Y_portfolio[(i, j), (i + 1, j)] = (Portfolio[(i, j)], Portfolio[(i + 1, j)])
                edge_Y_portfolio[(i, j), (i + 1, j + 1)] = (Portfolio[(i, j)], Portfolio[(i + 1, j + 1)])

                edge_Y_cash[(i, j), (i + 1, j)] = (CashAccount[(i, j)], CashAccount[(i + 1, j)])
                edge_Y_cash[(i, j), (i + 1, j + 1)] = (CashAccount[(i, j)], CashAccount[(i + 1, j + 1)])

                edge_Y_nbrofshares[(i, j), (i + 1, j)] = (NbrOfShares[(i, j)], NbrOfShares[(i + 1, j)])
                edge_Y_nbrofshares[(i, j), (i + 1, j + 1)] = (NbrOfShares[(i, j)], NbrOfShares[(i + 1, j + 1)])

    # print(optionprice[(0,1)])

    # creating the nodes itself in the networkx graph
    pos = {}
    for node in G_Stock.nodes():
        pos[node] = (node[0], tree__periods + 2 + node[0] - 2 * node[1])

    edge_x = []
    edge_y = []
    node_x = []
    node_y = []
    stocksLabel = []
    intrinsicLabel = []
    optionpriceLabel = []
    portfolioLabel = []
    cashLabel = []
    nbrofsharesLabel = []

    for edge in G_Stock.edges():
        x0, y0 = pos[edge[0]]
        x1, y1 = pos[edge[1]]
        edge_x.append(x0)
        edge_x.append(x1)
        edge_x.append(None)
        edge_y.append(y0)
        edge_y.append(y1)
        edge_y.append(None)

    for node in pos:
        x, y = pos[node]
        node_x.append(x)
        node_y.append(y)
        # stocksLabel.append(labelStocks[node])
        # intrinsicLabel.append(labelsIntrinsic[node])
        # optionpriceLabel.append(labelsOptionPrice[node])
        # portfolioLabel.append(labelPortfolio[node])
        # cashLabel.append(labelCash[node])
        # nbrofsharesLabel.append(labelNbrOfShares[node])

        stocksLabel.append(stockprices[node])
        intrinsicLabel.append(optionintrinsicvalue[node])
        optionpriceLabel.append(optionprice[node])
        portfolioLabel.append(Portfolio[node])
        cashLabel.append(CashAccount[node])
        nbrofsharesLabel.append(NbrOfShares[node])

    edge_y_Stock, edge_y_Intrinsic, edge_y_Optionprice, edge_y_Portfolio, edge_y_Cash, edge_y_NbrOfShares = [], [], [], [], [], []
    node_y_Stock, node_y_Intrinsic, node_y_Optionprice, node_y_Portfolio, node_y_Cash, node_y_NbrOfShares = node_y[:], node_y[:], node_y[:], node_y[:], node_y[:], node_y[:]

    # EDGES SPATIAL CHART
    for key, value in edge_Y_stock.items():
        edge_y_Stock.append(value[0])
        edge_y_Stock.append(value[1])
        edge_y_Stock.append(None)
    for key, value in edge_Y_intrinsic.items():
    	edge_y_Intrinsic.append(value[0])
    	edge_y_Intrinsic.append(value[1])
    	edge_y_Intrinsic.append(None)
    for key,value in edge_Y_optionprice.items():
    	edge_y_Optionprice.append(value[0])
    	edge_y_Optionprice.append(value[1])
    	edge_y_Optionprice.append(None)
    for key, value in edge_Y_portfolio.items():
    	edge_y_Portfolio.append(value[0])
    	edge_y_Portfolio.append(value[1])
    	edge_y_Portfolio.append(None)
    for key, value in edge_Y_cash.items():
    	edge_y_Cash.append(value[0])
    	edge_y_Cash.append(value[1])
    	edge_y_Cash.append(None)
    for key, value in edge_Y_nbrofshares.items():
    	edge_y_NbrOfShares.append(value[0])
    	edge_y_NbrOfShares.append(value[1])
    	edge_y_NbrOfShares.append(None)

    # NODES SPATIAL CHART
    for i in range(0, len(node_x)):
    	node_y_Stock[i]       = stocksLabel[i]
    	node_y_Intrinsic[i]   = intrinsicLabel[i]
    	node_y_Optionprice[i] = optionpriceLabel[i]
    	node_y_Portfolio[i]   = portfolioLabel[i]
    	node_y_Cash[i]        = cashLabel[i]
    	node_y_NbrOfShares[i] = nbrofsharesLabel[i]

    return nbrofsharesLabel, cashLabel, portfolioLabel, optionpriceLabel, intrinsicLabel, stocksLabel, edge_x, edge_y, node_x, node_y, round(u,2), round(d,2), round(probUp,2), round(probDown,2), edge_y_Stock, node_y_Stock, edge_y_Intrinsic, node_y_Intrinsic, edge_y_Optionprice, node_y_Optionprice, edge_y_Portfolio, node_y_Portfolio, edge_y_Cash, node_y_Cash, edge_y_NbrOfShares, node_y_NbrOfShares, tree__periods


import numpy as np
import scipy.linalg

#Consider
# 2X1 + X2 +3X3 = 17
# 4X1 - X2 + 3X3 = 31
# -2X1 + 5X2 + 5X3 = -5

A = np.array([[2., 1., 3.],
             [4., -1., 3.],
            [-2., 5., 5.]
])

B = np.array([17., 31., -5.]).T

#We can get the L and U by lu function
(P,L,U) = scipy.linalg.lu(A)

# Or solve by hand
# I Got L and U by row reduction.

L =  np.array([[1,0,0],
             [2,1,0],
             [-1,-2,1]])

U = np.array([[2,1,3],
             [0,-3,-3],
             [0,-0,2]])

# Solve LY = B for Y.
# The solve UX = Y for X.

Y = np.linalg.solve(L, B)

X = np.linalg.solve(U, Y)

# Get the correct solution to compare
solution = np.linalg.solve(A, B)

assert np.allclose(X,solution)


from random import random
from numba import jit

@jit(nopython=True)
def monte_carlo_pi(n):
    counter = 0
    for _ in range(n+1):
        x = random()
        y = random()
        if (x ** 2 + y ** 2) < 1:
            counter += 1
    return 4 * counter / n

if __name__ == "__main__":
    print(monte_carlo_pi(10000))


import numpy as np

def normalize(v):
    '''Normalize a vector based on its 2 norm.'''
    if 0 == np.linalg.norm(v):
        return v
    return v / np.linalg.norm(v)

def rotation_matrix_from_axis_and_angle(u, theta):
    '''Calculate a rotation matrix from an axis and an angle.'''

    x = u[0]
    y = u[1]
    z = u[2]
    s = np.sin(theta)
    c = np.cos(theta)

    return np.array([[c + x**2 * (1 - c), x * y * (1 - c) - z * s, x * z * (1 - c) + y * s],
                     [y * x * (1 - c) + z * s, c + y**2 * (1 - c), y * z * (1 - c) - x * s ],
                     [z * x * (1 - c) - y * s, z * y * (1 - c) + x * s, c + z**2 * (1 - c) ]])

def point_line_distance(p, l_p, l_v):
    '''Calculate the distance between a point and a line defined
    by a point and a direction vector.
    '''
    l_v = normalize(l_v)
    u = p - l_p
    return np.linalg.norm(u - np.dot(u, l_v) * l_v)


#!/usr/bin/env python3

import numpy as np
from scipy.spatial.distance import pdist
from sympy.utilities.iterables import multiset_permutations

def distance(x1, y1, x2, y2):
  """
    The Squared Euclidean Distance is given by the following formula:
    round(math.pow((x1-x2), 2) + math.pow((y1-y2), 2))
  """
  return pdist(np.array([[x1, y1], [x2, y2]]), metric='sqeuclidean')

def distance_for_array(array):
  return distance(*array)

def solution(A, B, C, D):
  """
    Finds the maximum squared distance between two points,
    created assigning the integers A, B, C and D to the coordinates
    (A, B, C and D must be used one time each).
  :return:
    Maximum squared distance for points created with A, B, C and D (used one time each).
  """

  # Creates all possible permutations
  p_array = list(multiset_permutations(np.array([A, B, C, D])))

  # Calculates the distance for each permutation
  distances = np.apply_along_axis(distance_for_array, 1, p_array)

  # Returns the maximum distance
  return np.nanmax(distances)


import math
import numpy as np

def f(x, y): #objective function
    return 2*x + y**2 + math.exp(-x-y+1)

def gradient(x, y): #gradient(f(x_(n)))
    return np.array([-math.exp(-x-y+1) + 2, -math.exp(-x-y+1) + 2*y])

def delta(x, y): #delta = -1 * inverse_hessian_matrix_of_f(x_(n)) * gradient(f(x_(n)))
    return np.array([-(((math.exp(1-x-y)+2)*(2-math.exp(1-x-y))-math.exp(1-x-y)*(2*y-math.exp(1-x-y)))/(2*math.exp(1-x-y))), -y + 1])

def backtracking(x_n, c, t):
    global counter
    counter += 1
    x_n_new = np.round(x_n + t*delta(x_n[0], x_n[1]), 7) #calculate x_(n + 1)
    temp = round((gradient(x_n[0], x_n[1])[0])*((x_n_new-x_n)[0]) + (gradient(x_n[0], x_n[1])[1])*((x_n_new-x_n)[1]), 7)
    if round(f(x_n_new[0], x_n_new[1]), 7) <= round(f(x_n[0], x_n[1]) + c*temp, 7): #condition is true, algorithm stops
        print('Best approximation of x_n is:', x_n_new)
        return x_n_new
    else: #condition is false, algorithm is repeated with x_(n + 1) and t = t/2
        print('Current x_n is:', x_n_new)
        backtracking(x_n_new, c, t/2)

counter = 0
x_n = np.array([3, 3]) #inital point
c = 0.8
t = 1
print('Initial x_n is:', x_n)
backtracking(x_n, c, t)
print('\nThe Newton backtracking algorithm ran', counter, 'times.')
print('Total number of steps:', counter*3)


import numpy as np
from scipy import linalg

from ..base import Preprocessor
from ..statistics import calculate_covariance_matrix

__all__ = [
    "PrincipalComponentAnalysis",
]

class PrincipalComponentAnalysis(Preprocessor):
    """Principal Component Analysis (PCA)

    A statistical procedure that uses an orthogonal transformation to convert
    a set of observations of possibly correlated variables (entities each of
    which takes on various numerical values) into a set of values of linearly
    uncorrelated variables called principal components.

    Parameters:
    -----------
    n_components : integer

    """

    def __init__(self, n_components=2):
        self.n_components = n_components

        self.components = None
        self.explained_variance = []
        self.explained_variance_ratio = []

    def fit(self, X):
        X = super().fit(X)

        # Computing the Covariance Matrix
        S = calculate_covariance_matrix(X)

        # Compute the eigenvalues and eigenvectors
        eigvals, eigvecs = linalg.eig(S)
        eigvals_sum = np.sum(eigvals)

        # Selecting principal components for the new feature subspace
        eig_pairs = [(eigvals[i], eigvecs[:, i]) for i in range(S.shape[0])]
        eig_pairs.sort(key=lambda x: x[0], reverse=True)

        self.components = np.column_stack((
            eig_pairs[i][1] for i in range(self.n_components)
        ))
        self.explained_variance = np.array([
            eig_pairs[i][0] for i in range(self.n_components)
        ])
        self.explained_variance_ratio = np.array([
            v / eigvals_sum for v in self.explained_variance
        ])

        return self

    def transform(self, X):
        X = super().transform(X)

        # Transforming the samples onto the new subspace
        return X @ self.components


from sklearn.neighbors import kneighbors_graph
from sklearn.neighbors import NearestNeighbors
from sklearn.utils.graph import graph_shortest_path
import numpy as np
import scipy as sp
import networkx

class IsoMap():

    def __init__(self, n_component, n_neighbour ):

        self.n_neighbour = n_neighbour
        self.n_component = n_component
        self.k = None
        self.lambdas = None
        self.alphas = None
        self.kneargraph = None

    def _fit(self, X):

        neighbour = NearestNeighbors(n_neighbors=self.n_neighbour)
        neighbour.fit(X)
        self.kneargraph = kneighbors_graph(neighbour, n_neighbors=self.n_neighbour, mode = 'distance')
        D = graph_shortest_path(self.kneargraph, directed=False)
        size = D.shape[0]
        H = np.eye(size, size) - 1 / size * np.ones((size, size))
        self.k = -1 / 2 * H.dot(D ** 2).dot(H)
        self.lambdas, self.alphas = sp.linalg.eigh(self.k)

        indices = self.lambdas.argsort()[::-1]
        self.lambdas = self.lambdas[indices]
        self.alphas = self.alphas[:, indices]

        self.lambdas = self.lambdas[0:self.n_component]
        self.alphas = self.alphas[:, 0:self.n_component]

    def fit(self, X):

        self._fit(X)
        return self

    def transform(self, X):

        y = self.alphas * np.sqrt(self.lambdas)
        return y

import numpy,import scipy,import networkx


from rollerpy.models.curve import Curve, ParametricCurve, NoramlizedCurve
import numpy as np

class HelixCircleParam(Curve, ParametricCurve, NoramlizedCurve):

    def __init__(
        self, A, B, C=1, tmin=0, tmax=np.pi, n=100, initialPosition=[0, 0, 0]
    ):
        # Helix parameters
        self.A = A
        self.B = B
        self.C = C
        self.tmin = tmin
        self.tmax = tmax

        # Initial positions
        self._setInitialPosition(initialPosition)

        # Calculations
        self.t = np.linspace(self.tmin, self.tmax, n)
        self._calcParameters()
        self._calcDerivative()

    def _setInitialPosition(self, initialPosition):

        self.x0 = initialPosition[0]
        self.y0 = initialPosition[1]
        self.z0 = initialPosition[2]

    def _calcParameters(self):
        self.x = self.B*np.cos(self.t) + self.x0
        self.y = self.C*self.t + self.y0 - self.tmin
        self.z = self.A*np.sin(self.t) + self.z0

    def _calcDerivative(self):
        self.dx = -1*self.B*np.sin(self.t)
        self.dy = self.t*0 + self.C
        self.dz = self.A*np.cos(self.t)

class InvHelixCircleParam(HelixCircleParam):

    def _calcParameters(self):
        self.x = self.B*np.cos(self.t) + self.x0
        self.y = -self.C*(self.t - self.tmax) + self.y0 - self.tmin
        self.z = self.A*np.sin(self.t) + self.z0

    def _calcDerivative(self):
        self.dx = -1*self.B*np.sin(self.t)
        self.dy = self.t*0 - self.C
        self.dz = self.A*np.cos(self.t)

class Line(Curve, ParametricCurve, NoramlizedCurve):

    def __init__(self, point1, point2, tmin=0, tmax=1, n=100):

        self._point1 = point1
        self._point2 = point2

        self._v = [
            (point2[0] - point1[0])/tmax,
            (point2[1] - point1[1])/tmax,
            (point2[2] - point1[2])/tmax
        ]

        self.t = np.linspace(tmin, tmax, n)

        self._calcParameters()
        self._calcDerivative()

    def _calcParameters(self):
        self.x = self._point1[0] + self.t*self._v[0]
        self.y = self._point1[1] + self.t*self._v[1]
        self.z = self._point1[2] + self.t*self._v[2]

    def _calcDerivative(self):
        self.dx = 0*self.t + (self._point2[0] - self._point1[0])/self.t[-1]
        self.dy = 0*self.t + (self._point2[1] - self._point1[1])/self.t[-1]
        self.dz = 0*self.t + (self._point2[2] - self._point1[2])/self.t[-1]


from scipy.stats import multivariate_normal as mvn
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')
np.random.seed(1234)

# create data set
n = 1000
_mus = np.array([[0, 4], [-2, 0]])
_sigmas = np.array([[[3, 0], [0, 0.5]], [[1, 0], [0, 2]]])
_pis = np.array([0.6, 0.4])
xs = np.concatenate([np.random.multivariate_normal(mu, sigma, int(pi*n)) for pi, mu, sigma in zip(_pis, _mus, _sigmas)])

# initial guesses for parameters
pis = np.random.random(2)
pis /= pis.sum()
mus = np.random.random((2, 2))
sigmas = np.array([np.eye(2)] * 2)

def plot_result(pis, mus, sigmas, filename):
    intervals = 101
    ys = np.linspace(-7, 7, intervals)
    X, Y = np.meshgrid(ys, ys)
    _ys = np.vstack([X.ravel(), Y.ravel()]).T

    z = np.zeros(len(_ys))
    for pi, mu, sigma in zip(pis, mus, sigmas):
        z += pi*mvn(mu, sigma).pdf(_ys)
    z = z.reshape((intervals, intervals))

    fig = plt.figure(figsize=(10, 10), dpi=80)
    ax = fig.add_subplot(111)
    plt.scatter(xs[:, 0], xs[:, 1], alpha=0.2, linewidths=2.0)
    plt.contour(X, Y, z, linewidths=3.0)
    # plt.contour(X, Y, z, N=10)
    # plt.axis([-8, 6, -6, 8])
    ax.axes.set_aspect('equal')
    # plt.tight_layout()
    ax.set_xlabel("x1")
    ax.set_ylabel("x2")
    ax.xaxis.label.set_size(25)
    ax.yaxis.label.set_size(25)
    ax.tick_params(axis='both', which='both', labelsize=15)

    plt.savefig(filename)

def em_gmm_orig(xs, pis, mus, sigmas, tol=0.001, max_iter=100):
    n, p = xs.shape
    k = len(pis)

    ll_old = 0
    for iter in range(max_iter):
        ll_new = 0

        # E-step
        ws = np.zeros((k, n))
        for j in range(len(mus)):
            for i in range(n):
                ws[j, i] = pis[j] * mvn(mus[j], sigmas[j]).pdf(xs[i])
        ws /= ws.sum(0)

        # M-step
        pis = np.zeros(k)
        for j in range(len(mus)):
            for i in range(n):
                pis[j] += ws[j, i]
        pis /= n

        mus = np.zeros((k, p))
        for j in range(k):
            for i in range(n):
                mus[j] += ws[j, i] * xs[i]
            mus[j] /= ws[j, :].sum()

        sigmas = np.zeros((k, p, p))
        for j in range(k):
            for i in range(n):
                ys = np.reshape(xs[i] - mus[j], (2, 1))
                sigmas[j] += ws[j, i] * np.dot(ys, ys.T)
            sigmas[j] /= ws[j, :].sum()

        # update complete log likelihoood
        ll_new = 0.0
        for i in range(n):
            s = 0
            for j in range(k):
                s += pis[j] * mvn(mus[j], sigmas[j]).pdf(xs[i])
            ll_new += np.log(s)

        if np.abs(ll_new - ll_old) < tol:
            break
        ll_old = ll_new

        plot_result(pis=pis, mus=mus, sigmas=sigmas, filename="another_" + str(iter) + ".png")

    return pis, mus, sigmas

def em_gmm_vect(xs, pis, mus, sigmas, tol=0.001, max_iter=100):
    n, p = xs.shape
    k = len(pis)

    ll_old = 0
    for iter in range(max_iter):
        ll_new = 0

        # E-step
        ws = np.zeros((k, n))
        for j in range(k):
            ws[j, :] = pis[j] * mvn(mus[j], sigmas[j]).pdf(xs)
        ws /= ws.sum(0)

        # M-step
        pis = ws.sum(axis=1)
        pis /= n

        mus = np.dot(ws, xs)
        mus /= ws.sum(1)[:, None]

        sigmas = np.zeros((k, p, p))
        for j in range(k):
            ys = xs - mus[j, :]
            sigmas[j] = (ws[j, :, None, None] * np.matmul(ys[:, :, None], ys[:, None, :])).sum(axis=0)
        sigmas /= ws.sum(axis=1)[:, None, None]

        # update complete log likelihood
        ll_new = 0
        for pi, mu, sigma in zip(pis, mus, sigmas):
            ll_new += pi*mvn(mu, sigma).pdf(xs)
        ll_new = np.log(ll_new).sum()

        if np.abs(ll_new - ll_old) < tol:
            break
        ll_old = ll_new

        plot_result(pis=pis, mus=mus, sigmas=sigmas, filename="em2d_" + str(iter + 1) + ".png")

    return pis, mus, sigmas

plot_result(pis=pis, mus=mus, sigmas=sigmas, filename="em2d_0.png")
pis2, mus2, sigmas2 = em_gmm_vect(xs, pis, mus, sigmas)


import numpy as np
EPS = 1e-8

def si_sdr(reference, estimation):
    """
    Scale-Invariant Signal-to-Distortion Ratio (SI-SDR)

    Args:
        reference: numpy.ndarray, [..., T]
        estimation: numpy.ndarray, [..., T]

    Returns:
        SI-SDR

    [1] SDR– Half- Baked or Well Done?
    http://www.merl.com/publications/docs/TR2019-013.pdf

    >>> np.random.seed(0)
    >>> reference = np.random.randn(100)
    >>> si_sdr(reference, reference)
    inf
    >>> si_sdr(reference, reference * 2)
    inf
    >>> si_sdr(reference, np.flip(reference))
    -25.127672346460717
    >>> si_sdr(reference, reference + np.flip(reference))
    0.481070445785553
    >>> si_sdr(reference, reference + 0.5)
    6.3704606032577304
    >>> si_sdr(reference, reference * 2 + 1)
    6.3704606032577304
    >>> si_sdr([reference, reference], [reference * 2 + 1, reference * 1 + 0.5])
    array([6.3704606, 6.3704606])

    """
    estimation, reference = np.broadcast_arrays(estimation, reference)
    # Zero mean
    estimation = estimation - np.mean(estimation, -1, keepdims=True)
    reference = reference - np.mean(reference, -1, keepdims=True)
    # Reference energy has a better estimate
    reference_energy = np.sum(reference ** 2, axis=-1, keepdims=True) + EPS

    # This is $\alpha$ after Equation (3) in [1].
    optimal_scaling = np.sum(reference * estimation, axis=-1, keepdims=True) \
        / reference_energy

    # This is $e_{\text{target}}$ in Equation (4) in [1].
    projection = optimal_scaling * reference

    # This is $e_{\text{res}}$ in Equation (4) in [1].
    noise = estimation - projection

    ratio = np.sum(projection ** 2, axis=-1) / (np.sum(noise ** 2, axis=-1) +
                                                EPS)
    return np.array(10 * np.log10(ratio + EPS), dtype=np.float64)


import numpy as np

def symmetric_mean_absolute_percentage_error(true, pred):
    # percentage error, zero if both true and pred are zero
    true = np.array(true)
    pred = np.array(pred)
    daily_smape_arr = 200 * np.nan_to_num(np.abs(true - pred) / (np.abs(true) + np.abs(pred)))
    return np.mean(daily_smape_arr), daily_smape_arr


import matplotlib.pyplot as plot
import matplotlib.animation as animation
import numpy as np
import seaborn as sns

def getGradient(X, Y, weight, bias = 0):
    weight_loss = X * (X * weight + bias - Y)
    bias_loss = (X * weight + bias - Y)
    weight_gradient = 2 * np.average(weight_loss)
    bias_gradient = 2 * np.average(bias_loss)
    return (weight_gradient, bias_gradient)

def trainByGradientDecent(X, Y):
    weight = 0
    bias = 0
    learningRate = 0.01
    for i in range(20000):
        (weight_gradient, bias_gradient) = getGradient(X, Y, weight, bias)
        weight -= weight_gradient * learningRate
        bias -= bias_gradient * learningRate

        if i % 200 == 0:
            plot.plot(X, Y, "bo")
            image = plot.plot([0, 10], [0 + bias, 10 * weight + bias], linewidth = 1.0, color = "g")
            images.append(image)
            print(i, weight, bias)

        if weight_gradient == 0 and bias_gradient == 0:
            return (weight, bias)

    return (weight, bias)

def prepareImage():
    sns.set()
    plot.xticks(fontsize = 10)
    plot.yticks(fontsize = 10)
    plot.xlabel("RM", fontsize = 10)
    plot.ylabel("MEDV", fontsize = 10)

images = []

data = np.genfromtxt('./dataset/housing.csv', delimiter = ',', skip_header = 1)
X = data[:, 0]
Y = data[:, 3]

print("---------gradient decent----------")
prepareImage()
figure1 = plot.figure()
[weight1, bias1] = trainByGradientDecent(X, Y)

ani1 = animation.ArtistAnimation(figure1, images, interval = 150)
ani1.save("/src/images/regression-gradient-decent.gif", writer = "imagemagick")

plot.plot(X, Y,"bo")
plot.plot([0, 10], [0 + bias1, 10 * weight1 + bias1], linewidth = 1.0, color = "g")
plot.savefig("/src/images/regression-gradient-decent.png")


import numpy as n

def get_rotation_angles(number):
    '''
    Returns a list of theta, phi values, approximately evenly
    distributed on the sphere.

    Uses the generalised spiral points algorithm explained in
    E B Saff and
    A B J Kuijlaars, Distributing many points on a sphere, The
    Mathematical Intelligencer 19(1) 1997.
    '''

    angles = n.zeros((number, 2))
    angles[0] = n.array([n.arccos(-1), 0])

    for k in range(2, number+1):
        h_k = -1. + (2. * (k - 1)) / (number - 1)
        theta = n.arccos(h_k)
        phi = (angles[k-2, 1] + 3.6/n.sqrt(number) *
               1. / n.sqrt(1 - h_k**2)) % (2*n.pi)
        angles[k-1, 0] = theta
        angles[k-1, 1] = phi
    angles[-1, 1] = 0.  # Last phi will be inf otherwise

    return angles

def rotate_vector_to_top(vector):
    '''Returns a rotation matrix that will rotate the given vector to
    point upwards.

    Parameters
    ----------
    vector : ndarray
        The (3D) vector to rotate.
    '''

    theta = n.arccos(vector[2] / n.linalg.norm(vector))
    phi = n.arctan2(vector[1], vector[0])
    return rotate_to_top(theta, phi)

def rotate_to_top(theta, phi):

    '''
    Returns a rotation matrix that will rotate a sphere such that
    the given positions are at the top.

    Parameters
    ----------
    theta : float
        The latitudinal variable.
    phi : float
        The longitudinal variable.
    '''

    chi = -1 * phi
    alpha = theta

    cc = n.cos(chi)
    sc = n.sin(chi)
    ca = n.cos(alpha)
    sa = n.sin(alpha)
    first_rotation = n.array([[cc, -sc, 0],
                              [sc, cc, 0],
                              [0, 0, 1]])
    second_rotation = n.array([[ca, 0, -sa],
                               [0, 1, 0],
                               [sa, 0, ca]])

    return second_rotation.dot(first_rotation)

def rotate_axis_angle(axis, angle):
    axis = n.array(axis) / n.linalg.norm(axis)
    ux, uy, uz = axis

    ct = n.cos(angle)
    st = n.sin(angle)

    arr = n.array(
        [[ct + ux**2*(1-ct), ux*uy*(1-ct) - uz*st, ux*uz*(1-ct) + uy*st],
         [uy*ux*(1-ct) + uz*st, ct + uy**2*(1-ct), uy*ux*(1-ct) - ux*st],
         [uz*ux*(1-ct) - uy*st, uz*uy*(1-ct) + ux*st, ct + uz**2*(1-ct)]])

    return arr


from scipy import stats
import numpy as np

_norm_cdf = stats.norm(0, 1).cdf
_norm_pdf = stats.norm(0, 1).pdf

def _d1(S, K, T, r, sigma):
    return (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

def _d2(S, K, T, r, sigma):
    return _d1(S, K, T, r, sigma) - sigma * np.sqrt(T)

def call_value(S, K, T, r, sigma):
    '''
    The fair value of a call option paying max(S-K, 0) at expiry, under the Black-scholes model,
    for an option with strike <K>, expiring in <T> years, under a fixed interest rate <r>,
    a stock volatility <sigma>, and when the current price of the underlying stock is <S>.

    Parameters
    ----------
    S : float
        The current value of the underlying stock.

    K : float
        The strike price of the option.

    T : float
        Time to expiry in years.

    r : float
        The fixed interest rate valid between now and expiry.

    sigma : float
        The volatility of the underlying stock process.

    Returns
    -------
    call_value : float
        The fair present value of the option.

    '''

    return S * _norm_cdf(_d1(S, K, T, r, sigma)) - K * np.exp(-r * T) * _norm_cdf(_d2(S, K, T, r, sigma))

def put_value(S, K, T, r, sigma):
    '''
    The fair value of a put option paying max(K-S, 0) at expiry, under the Black-scholes model,
    for an option with strike <K>, expiring in <T> years, under a fixed interest rate <r>,
    a stock volatility <sigma>, and when the current price of the underlying stock is <S>.

    Parameters
    ----------
    S : float
        The value of the underlying stock.

    K : float
        The strike price of the option.

    T : float
        Time to expiry in years.

    r : float
        The fixed interest rate valid between now and expiry.

    sigma : float
        The volatility of the underlying stock process.

    Returns
    -------
    put_value : float
        The fair present value of the option.
    '''

    return np.exp(-r * T) * K * _norm_cdf(-_d2(S, K, T, r, sigma)) - S * _norm_cdf(-_d1(S, K, T, r, sigma))

def call_delta(S, K, T, r, sigma):
    '''
    The delta, i.e. the first derivative of the option value with respect to the underlying,
    of a call option paying max(S-K, 0) at expiry, under the Black-scholes model, for an option
    with strike <K>, expiring in <T> years, under a fixed interest rate <r>, a stock
    volatility <sigma>, and when the current price of the underlying stock is <S>.

    Parameters
    ----------
    S : float
        The value of the underlying stock.

    K : float
        The strike price of the option.

    T : float
        Time to expiry in years.

    r : float
        The fixed interest rate valid between now and expiry.

    sigma : float
        The volatility of the underlying stock process.

    Returns
    -------
    call_delta : float
        The fair present value of the option.
    '''

    return _norm_cdf(_d1(S, K, T, r, sigma))

def put_delta(S, K, T, r, sigma):
    '''
    The delta, i.e. the first derivative of the option value with respect to the underlying,
    of a put option paying max(K-S, 0) at expiry, under the Black-scholes model, for an option
    with strike <K>, expiring in <T> years, under a fixed interest rate <r>, a stock
    volatility <sigma>, and when the current price of the underlying stock is <S>.

    Parameters
    ----------
    S : float
        The value of the underlying stock.

    K : float
        The strike price of the option.

    T : float
        Time to expiry in years.

    r : float
        The fixed interest rate valid between now and expiry.

    sigma : float
        The volatility of the underlying stock process.

    Returns
    -------
    put_delta : float
        The fair present value of the option.
    '''

    return call_delta(S, K, T, r, sigma) - 1

def call_vega(S, K, T, r, sigma):
    '''
    The vega, i.e. the derivative of the option value with respect to the volatility,
    of a call option paying max(S-K, 0) at expiry, under the Black-scholes model, for an option
    with strike <K>, expiring in <T> years, under a fixed interest rate <r>, a stock
    volatility <sigma>, and when the current price of the underlying stock is <S>.

    Parameters
    ----------
    S : float
        The value of the underlying stock.

    K : float
        The strike price of the option.

    T : float
        Time to expiry in years.

    r : float
        The fixed interest rate valid between now and expiry.

    sigma : float
        The volatility of the underlying stock process.

    Returns
    -------
    call_delta : float
        The fair present value of the option.
    '''

    return S * _norm_pdf(_d1(S, K, T, r, sigma)) * np.sqrt(T)

def put_vega(S, K, T, r, sigma):
    '''
    The vega, i.e. the derivative of the option value with respect to the volatility,
    of a put option paying max(K-S, 0) at expiry, under the Black-scholes model, for an option
    with strike <K>, expiring in <T> years, under a fixed interest rate <r>, a stock
    volatility <sigma>, and when the current price of the underlying stock is <S>.

    Parameters
    ----------
    S : float
        The value of the underlying stock.

    K : float
        The strike price of the option.

    T : float
        Time to expiry in years.

    r : float
        The fixed interest rate valid between now and expiry.

    sigma : float
        The volatility of the underlying stock process.

    Returns
    -------
    call_delta : float
        The fair present value of the option.
    '''

    return call_vega(S, K, T, r, sigma)


"""
This module illustrates how to generate a three-dimensional plot and a
contour plot.

For the example, the f(x,y) = x**2 * y**3 will be reproduced.
"""
import numpy as np
import matplotlib.pyplot as plt

from mpl_toolkits import mplot3d

X = np.linspace(-2, 2)
Y = np.linspace(-1, 1)

x, y = np.meshgrid(X, Y)
z = x**2 * y**3

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

ax.plot_surface(x, y, z)
ax.set_xlabel("$x$", usetex=True)
ax.set_ylabel("$y$", usetex=True)
ax.set_zlabel("$z$", usetex=True)
ax.set_title("Graph of the function $f(x,y) = x^2y^3$", usetex=True)

plt.show()

fig, ax = plt.subplots()
ax.contour(x, y, z)
ax.set_title("Contour of $f(x) = x^2y^3$", usetex=True)
ax.set_xlabel("$x$", usetex=True)
ax.set_ylabel("$y$", usetex=True)

plt.show()


"""Principal Component Analysis, using numpys 'eigenh' decomposition"""
# region - imports
# standard
from dataclasses import dataclass

# 3rd party
import numpy as np

# local
from .helpers import ncols, nrows

# type hints

# endregion

@dataclass
class PCA:
    """
    PCA is the eigenvalue decomposition of the sample matrix covariance (or correlation if scaled).

    It is used to reduce the dimensionality of a problem: `project` a data matrices to subspaces of
    eigenvectors corresponding to the the largest eigenvalues (with largest variance).
    """

    matrix: np.ndarray
    """
    The (preprocessed) matrix that was used for the PCA
    """
    covariance: np.ndarray
    """
    The covariance matrix that was decomposed for the PCA - it equals the correlation matrix if scaled is `True`
    """
    eigenvalues: np.ndarray
    """
    The eigenvalues in descending order
    """
    eigenvectors: np.ndarray  # aka 'loadings'
    """
    The eigenvalues in descending order (of eigenvalues)
    """
    bias: bool
    """
    If bias is `True`, the covariance is normalized by N instead of N-1
    """

    @staticmethod
    def generate(matrix: np.ndarray, bias=False) -> 'PCA':
        """
        Computes the eigenvalue decomposition of the covariance (of the transposed sample matrix).
        Note that the covarience is by definition 'implicitely mean centered'

        arguments:
            - matrix: sample matrix
            - bias (bool): whether to normalize the covariance by N (`True`) or N-1 (`False`)
        """

        # compute and store values
        covariance = np.cov(matrix.T, bias=bias)

        # get eigen decomposition and sort by descending eigenvalues
        # we also force the eigenvalues positive - they might be erroneously negative due to numerical precision limits
        eigenvalues, eigenvectors = np.linalg.eigh(covariance)
        eigenvalues = np.abs(eigenvalues)
        descending_indices = np.argsort(eigenvalues)[::-1]

        return PCA(matrix, covariance, eigenvalues[descending_indices], eigenvectors[:, descending_indices], bias)

    def project(self, matrix: np.ndarray, n_comp: int) -> 'PCAProjection':
        """
        Projects a `matrix` to the space of the `ncomp` largest principal components
        """
        if ncols(matrix) != nrows(self.eigenvectors):
            raise ValueError("Can not apply PCA, matrix dimensions (column count) is incompatible")

        if not isinstance(n_comp, int) or n_comp < 1:
            raise ValueError("N-comp must be a positive integer")

        # select new basis of `n_comp`- many components
        total_comp = len(self.eigenvectors)
        n_comp = min(total_comp, n_comp)
        new_space: np.ndarray = self.eigenvectors[:, :n_comp]

        # compute projection (scores) and residual
        scores = np.matmul(matrix, new_space)
        residual = matrix - np.matmul(scores, new_space.T)
        result = PCAProjection(scores, residual, self, None)

        # compute and set distances on the result object
        self._set_distances(result)

        return result

    def _set_distances(self, pca_result: 'PCAProjection') -> 'PCAProjection.Distances':
        # normalize the scores
        scores_normal = pca_result.scores / np.sqrt(self.eigenvalues[:pca_result.n_comp])

        # distances: matrix of same shape as scores (sample size x n_comp)
        # for each sample (row), compute the diatnce for the one (Q) or all (T2) involved components
        shape = pca_result.scores.shape
        distances = PCAProjection.Distances(
            np.empty(shape=shape, dtype=float),
            np.empty(shape=shape, dtype=float))

        # calculate distances and model power for each possible number of components in model
        n_comp = pca_result.n_comp
        for i in range(0, n_comp):
            res = pca_result.residuals + np.matmul(
                pca_result.scores[:, i+1:n_comp],
                self.eigenvectors[:, i+1:n_comp].T)
            distances.Q[:, i] = np.sum(res**2, 1)
            distances.T2[:, i] = np.sum(scores_normal[:, :i+1]**2, 1)

        pca_result.distances = distances

@dataclass
class PCAProjection:
    """
    Result of 'projecting' a matrix to the space of `n_comp` larges many components
    """
    scores: np.ndarray
    residuals: np.ndarray
    pca: 'PCA'
    distances: 'PCAProjection.Distances'

    @dataclass
    class Distances:
        """
        The calculated distances of a PCA projection
        """
        Q: np.ndarray
        """
        Matrix of orthognal distances:
        Column i holds the squared residual distances to the subspace spanned by the eigenvectors i+1,...,n_comp
        """
        T2: np.ndarray
        """
        Matrix of scores distances:
        Column i holds the (squared) eigenvalue-normalized scores corresponding to eigenvectors 1,...,i
        """

    @property
    def n_comp(self) -> int:
        """
        Number of principal components and dimension of the projected space
        """
        return ncols(self.scores)

    @property
    def n_samples(self) -> int:
        """
        Number of samples (number of score rows)
        """
        return nrows(self.scores)

    @property
    def residual_norm(self) -> float:
        """
        Size of the residual
        """
        return np.linalg.norm(self.residuals)


import scipy.integrate
import numpy
import matplotlib.pyplot as plt

def SIR_model(y, t, beta, gamma):
    S, I, R = y

    dS_dt = -beta * S * I
    dI_dt = beta * S * I - gamma * I
    dR_dt = gamma * I

    return [dS_dt, dI_dt, dR_dt]

S0 = 0.95
I0 = 0.05
R0 = 0.0

beta = 0.35
gamma = 0.1

t = numpy.linspace(0, 100, 10000)

solution = scipy.integrate.odeint(SIR_model, [S0, I0, R0], t, args=(beta, gamma))
solution = numpy.array(solution)

plt.figure(figsize=[6, 4])
plt.plot()
plt.plot(t, solution[:, 0], label="S(t)")
plt.plot(t, solution[:, 1], label="I(t)")
plt.plot(t, solution[:, 2], label="R(t)")
plt.grid()
plt.legend()
plt.xlabel("Tiempo")
plt.ylabel("Proporciones")
plt.title("SIR modelo")
plt.show()


"""
This module contains the tests for timeconv function
"""
# Standard library imports

# Third party imports
from pytest import approx
#https://www.scivision.dev/pytest-approx-equal-assert-allclose/
import numpy as np
from math import gamma, fsum

# Local application imports
from myorbit.util.stumpff import calc_stumpff_exact, calc_stumpff_as_series

#
#  The Stumpff functions implemented by polyastro will be used as oracle for the tests
#
def poli_c2(psi):
    r"""Second Stumpff function.
    For positive arguments:
    .. math::
        c_2(\psi) = \frac{1 - \cos{\sqrt{\psi}}}{\psi}
    """
    eps = 1.0
    if psi > eps:
        res = (1 - np.cos(np.sqrt(psi))) / psi
    elif psi < -eps:
        res = (np.cosh(np.sqrt(-psi)) - 1) / (-psi)
    else:
        res = 1.0 / 2.0
        delta = (-psi) / gamma(2 + 2 + 1)
        k = 1
        while res + delta != res:
            res = res + delta
            k += 1
            delta = (-psi) ** k / gamma(2 * k + 2 + 1)

    return res

def poli_c3(psi):
    r"""Third Stumpff function.
    For positive arguments:
    .. math::
        c_3(\psi) = \frac{\sqrt{\psi} - \sin{\sqrt{\psi}}}{\sqrt{\psi^3}}
    """
    eps = 1.0
    if psi > eps:
        res = (np.sqrt(psi) - np.sin(np.sqrt(psi))) / (psi * np.sqrt(psi))
    elif psi < -eps:
        res = (np.sinh(np.sqrt(-psi)) - np.sqrt(-psi)) / (-psi * np.sqrt(-psi))
    else:
        res = 1.0 / 6.0
        delta = (-psi) / gamma(2 + 3 + 1)
        k = 1
        while res + delta != res:
            res = res + delta
            k += 1
            delta = (-psi) ** k / gamma(2 * k + 3 + 1)

    return res

xs=np.linspace(-100,100,10000)

def calc_diffs(xs) :
    diff_c2_series=[]
    diff_c3_series=[]
    diff_c2_exact=[]
    diff_c3_exact=[]
    for x in xs:
        exp_c2 = poli_c2(x)
        exp_c3 = poli_c3(x)
        _, c2_series, c3_series = calc_stumpff_as_series(x)
        _, c2_exact, c3_exact = calc_stumpff_exact(x)
        diff_c2_series.append(np.abs(c2_series-exp_c2))
        diff_c3_series.append(np.abs(c3_series-exp_c3))
        diff_c2_exact.append(np.abs(c2_exact-exp_c2))
        diff_c3_exact.append(np.abs(c3_exact-exp_c3))
    return fsum(diff_c2_series), fsum(diff_c3_series), fsum(diff_c2_exact), fsum(diff_c3_exact)

def test_small_values():
    xs=np.linspace(-0.1,0.1,10000)
    dc2_series, dc3_series, dc2_exact, dc3_exact = calc_diffs(xs)
    assert dc2_series == approx(0.0, abs=1.e-12)
    assert dc3_series == approx(0.0, abs=1.e-12)
    assert dc2_exact == approx(0.0, abs=1.e-10)
    assert dc3_exact == approx(0.0, abs=1.e-10)

def test_big_values():
    xs=np.linspace(0,1000,1000)
    dc2_series, dc3_series, dc2_exact, dc3_exact = calc_diffs(xs)
    assert dc2_series == approx(0.0, abs=1.e-4)
    assert dc3_series == approx(0.0, abs=1.e-4)
    assert dc2_exact == approx(0.0, abs=1.e-15)
    assert dc3_exact == approx(0.0, abs=1.e-15)


"""from numpy import *
from pylab import *
import math
x = np.arange(0, math.pi*2, 0.05)
y = np.sin(x)
fig = plt.figure()
ax = fig.add_axes([0,0,1,0.8])
ax.plot(x,y)
ax.set_title("sine wave")
ax.set_xlabel('angle')
ax.set_ylabel('sine')
plt.show()"""

#etiquetas y dibujos
"""import matplotlib.pyplot as plt
y = [1, 4, 9, 16, 25,36,49, 64]
x1 = [1, 16, 30, 42,55, 68, 77,88]
x2 = [1,6,12,18,28, 40, 52, 65]
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
l1 = ax.plot(x1,y,'ys-') # solid line with yellow colour and square marker
l2 = ax.plot(x2,y,'go--') # dash line with green colour and circle marker
ax.legend(labels = ('tv', 'Smartphone'), loc = 'lower right') # legend placed at lower right
ax.set_title("Advertisement effect on sales")
ax.set_xlabel('medium')
ax.set_ylabel('sales')
plt.show()"""

#subplot
"""import matplotlib.pyplot as plt
import numpy as np
import math
x = np.arange(0, math.pi*2, 0.05)
fig=plt.figure()
axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # main axes
axes2 = fig.add_axes([0.55, 0.55, 0.3, 0.3]) # inset axes
y = np.sin(x)
axes1.plot(x, y, 'c')
axes2.plot(x,np.cos(x),'m')
axes1.set_title('sine')
axes2.set_title("cosine")
plt.show()

#crear 4 en uno
fig,a =  plt.subplots(2,2)
x = np.arange(1,5)
a[0][0].plot(x,x*x)
a[0][0].set_title('square')
a[0][1].plot(x,np.sqrt(x))
a[0][1].set_title('square root')
a[1][0].plot(x,np.exp(x))
a[1][0].set_title('exp')
a[1][1].plot(x,np.log10(x))
a[1][1].set_title('log')
plt.show()

#manipular el tamaño de los cuadros
a1 = plt.subplot2grid((3,3),(0,0),colspan = 2)
a2 = plt.subplot2grid((3,3),(0,2), rowspan = 3)
a3 = plt.subplot2grid((3,3),(1,0),rowspan = 2, colspan = 2)
x = np.arange(1,10)
a2.plot(x, x*x)
a2.set_title('square')
a1.plot(x, np.exp(x))
a1.set_title('exp')
a3.plot(x, np.log(x))
a3.set_title('log')
plt.tight_layout()
plt.show()

#quiver plot
x,y = np.meshgrid(np.arange(-2, 2, .2), np.arange(-2, 2, .2))
z = x*np.exp(-x**2 - y**2)
v, u = np.gradient(z, .2, .2)
fig, ax = plt.subplots()
q = ax.quiver(x,y,u,v)
plt.show()"""

#3D
"""from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.axes(projection='3d')
z = np.linspace(0, 1, 100)
x = z * np.sin(20 * z)
y = z * np.cos(20 * z)
ax.plot3D(x, y, z, 'gray')
ax.set_title('3D line plot')
plt.show()"""

from scipy import *
import matplotlib.pyplot as plt

im = np.zeros((256, 256))
im[64:-64, 64:-64] = 1

im = ndimage.rotate(im, 15, mode='constant')
im = ndimage.gaussian_filter(im, 8)

sx = ndimage.sobel(im, axis=0, mode='constant')
sy = ndimage.sobel(im, axis=1, mode='constant')
sob = np.hypot(sx, sy)

plt.figure(figsize=(16, 5))
plt.subplot(141)
plt.imshow(im, cmap=plt.cm.gray)
plt.axis('off')
plt.title('square', fontsize=20)
plt.subplot(142)
plt.imshow(sx)
plt.axis('off')
plt.title('Sobel (x direction)', fontsize=20)
plt.subplot(143)
plt.imshow(sob)
plt.axis('off')
plt.title('Sobel filter', fontsize=20)

im += 0.07*np.random.random(im.shape)

sx = ndimage.sobel(im, axis=0, mode='constant')
sy = ndimage.sobel(im, axis=1, mode='constant')
sob = np.hypot(sx, sy)

plt.subplot(144)
plt.imshow(sob)
plt.axis('off')
plt.title('Sobel for noisy image', fontsize=20)

plt.subplots_adjust(wspace=0.02, hspace=0.02, top=1, bottom=0, left=0, right=0.9)

plt.show()


import matplotlib.pyplot as plt #pip3 install matplotlib
import math
import numpy as np #pip3 install numpy
import random
import statistics

u=[] #to fill with 100 values from linear congruence generator
def gen_lincong_foru():
    #X(i+1)=X(i)A mod M and U(i+1)=X(i+1)/M -> Linear Congruence Generator
    x=23 # x0 value
    m=4096
    a=17
    for i in range(0,100):
        u.append(x/m)
        t=(a*x+1)%m #b=1
        x=t

c=[]
def fill_c_withvalues():
# {1,3,5,...,9999} -> This is the set to generate discrete uniform variables on.
    ini=1
    c.append(1)
    for i in range (1,5000):
        ini=ini+2;
        c.append(ini)

common_prob=1/5000
q=[]
def gen_q():
    q.append(0)
    for i in range(1,5000):
        q.append(q[i-1]+common_prob)

def find_final_ans(num):
    final_ans=[] #will print as per the number of values of u to be taken = num in our case
    for i in range(0,num):
        for j in range (1,5000):
            if(q[j-1]<u[i] and q[j]>=u[i]):
                final_ans.append(c[j])
    print('This is for the case of '+str(num)+' values of U \n')
    print(final_ans)
    print('\n')

gen_lincong_foru()
fill_c_withvalues()
gen_q()
find_final_ans(10)
find_final_ans(100)

Semester 5/MA 323 (Monte Carlo)/Lab Submissions/Lab 3/180123062_ABSatyaprakash_q1.py