# assignment 4 for Watkins

# from numpy import *
import numpy as np
from scipy.interpolate import lagrange

# **************** problem #1 **********************
# Find the polynomial of degree 2 that interpolates these points

# x = np.array([.8, .9, 1.0])
# f = np.array([.696707, .621610, .540302])
#
# polynomial_2 = lagrange(x, f)
#
# a = polynomial_2(0.85)
#
# print a
#
# print polynomial_2

# ******** problem #4 ***********

def f(x):
    return 3.0*np.power(x, 2) - 6.0*x + 5

x = np.array([-3, -2, -1, 0, 1, 2, 3, 4])
y = np.array([f(-3), f(-2), f(-1), f(0), f(1), f(2), f(3), f(4)])

print y

polynomial_2 = lagrange(x, y)

print polynomial_2

#
# # **************** problem #2 *************************
# from pylab import *
#
#
# def divided_difference( x, y ):
#     """Compute interpolated polynomial coefficients using divided differences.
#
#     USAGE:
#         a = divided_difference( x, y )
#
#     INPUT:
#         x      - integer or float array of x coordinates
#         y      - integer or float array of y coordiantes
#
#     OUTPUT:
#         float array - array of interpolating polynomial coefficients
#
#     NOTES:
#         This function computes the divided differences that can be used to
#         construct an interpolating polynomial for the given set of data.
#
#         This function accepts two vectors and returns a vector whose elements
#         are the divided difference values.
#
#         The value of the polynomial p (degree will be at most n) evaluated at
#         r is given by:
#
#             s = a[n]
#             for i in range( n, 0, -1 ):
#                 s = s * ( r - x[i-1] ) + a[i-1]
#
#         using the array a returned by this function.
#
#     AUTHOR:
#         Jonathan R. Senning <jonathan.senning@gordon.edu>
#         Gordon College
#         February 7, 1999
#         Converted to Python May 2008
#     """
#
#     nx = shape( x )
#     ny = shape( y )
#
#     if max( nx ) != max( ny ):
#         print 'both input variables must be vectors of the same length'
#         return
#     elif len( nx ) != 1 or len( ny ) != 1:
#         print 'both input variables must be vectors, not matrices'
#         return
#
#     n = max( nx ) - 1
#     a = y.copy()
#
#     for i in xrange( 1, n + 1 ):
#         for j in xrange( n, i - 1 , -1 ):
#             a[j] = float( a[j] - a[j-1] ) / float( x[j] - x[j-i] )
#
#     return a
#
#
# def evaluate(r, x, a):
#     """Evaluate divided difference polynomial at r
#
#     USAGE:
#         a = evaluate( r, x, a )
#
#     INPUT:
#         r      - integer or float scalar or numpy array of evaluation points
#         x      - integer or float array of interpolation point x coordinates
#         a      - coefficient array returned by divided_difference()
#
#     OUTPUT:
#         float array - float scalar or array of y-values corresponding to r
#
#     """
#
#     n = len( a ) - 1
#     s = a[n]
#     for i in xrange( n - 1, -1, -1 ):
#         s = s * ( r - x[i] ) + a[i]
#     return s
#
# b = divided_difference(x, f)
#
# c = evaluate(x, f, b)
#
# print c


import numpy as np

def gaussian_work_example(N_F=200, N_R=200, mu_F=2.0, DeltaF=None, sigma_F=1.0, seed=None):
    """Generate samples from forward and reverse Gaussian work distributions.

    Parameters
    ----------
    N_F : int, optional
        number of forward measurements (default: 200)
    N_R : float, optional
        number of reverse measurements (default: 200)
    mu_F : float, optional
        mean of forward work distribution (default: 2.0)
    DeltaF : float, optional
        the free energy difference, which can be specified instead of mu_F (default: None)
    sigma_F : float, optional
        variance of the forward work distribution (default: 1.0)
    seed : int, optional
        If not None, specify the numpy random number seed. Old state is restored after completion.

    Returns
    -------
    w_F : np.ndarray, dtype=float
        forward work values
    w_R : np.ndarray, dtype=float
        reverse work values

    Notes
    -----
    By the Crooks fluctuation theorem (CFT), the forward and backward work distributions are related by

    P_R(-w) = P_F(w) \exp[DeltaF - w]

    If the forward distribution is Gaussian with mean \mu_F and std dev \sigma_F, then

    P_F(w) = (2 \pi)^{-1/2} \sigma_F^{-1} \exp[-(w - \mu_F)^2 / (2 \sigma_F^2)]

    With some algebra, we then find the corresponding mean and std dev of the reverse distribution are

    \mu_R = - \mu_F + \sigma_F^2
    \sigma_R = \sigma_F \exp[\mu_F - \sigma_F^2 / 2 + \Delta F]

    where all quantities are in reduced units (e.g. divided by kT).

    Note that \mu_F and \Delta F are not independent!  By the Zwanzig relation,

    E_F[exp(-w)] = \int dw \exp(-w) P_F(w) = \exp[-\Delta F]

    which, with some integration, gives

    \Delta F = \mu_F + \sigma_F^2/2

    which can be used to determine either \mu_F or \DeltaF.

    Examples
    --------

    Generate work values with default parameters.

    >>> [w_F, w_R] = gaussian_work_example()

    Generate 50 forward work values and 70 reverse work values.

    >>> [w_F, w_R] = gaussian_work_example(N_F=50, N_R=70)

    Generate work values specifying the work distribution parameters.

    >>> [w_F, w_R] = gaussian_work_example(mu_F=3.0, sigma_F=2.0)

    Generate work values specifying the work distribution parameters, specifying free energy difference instead of mu_F.

    >>> [w_F, w_R] = gaussian_work_example(mu_F=None, DeltaF=3.0, sigma_F=2.0)

    Generate work values with known seed to ensure reproducibility for testing.

    >>> [w_F, w_R] = gaussian_work_example(seed=0)

    """

    # Make sure either mu_F or DeltaF, but not both, are specified.
    if (mu_F is not None) and (DeltaF is not None):
        raise ValueError("mu_F and DeltaF are not independent, and cannot both be specified; one must be set to None.")
    if (mu_F is None) and (DeltaF is None):
        raise ValueError("Either mu_F or DeltaF must be specified.")
    if (mu_F is None):
        mu_F = DeltaF + sigma_F ** 2 / 2.0
    if (DeltaF is None):
        DeltaF = mu_F - sigma_F ** 2 / 2.0

    # Set random number generator into a known state for reproducibility.
    random = np.random.RandomState(seed)

    # Determine mean and variance of reverse work distribution by Crooks
    # fluctuation theorem (CFT).
    mu_R = - mu_F + sigma_F ** 2
    sigma_R = sigma_F * np.exp(mu_F - sigma_F ** 2 / 2.0 - DeltaF)

    # Draw samples from forward and reverse distributions.
    w_F = random.randn(N_F) * sigma_F + mu_F
    w_R = random.randn(N_R) * sigma_R + mu_R

    return [w_F, w_R]


import numpy as np
import matplotlib.pyplot as plt

# Return evenly spaced numbers over a specified interval.
X = np.linspace(-np.pi, np.pi, 256,endpoint=True)
# print(type(X)) #<class 'numpy.ndarray'>
# print(X)

C,S = np.cos(X), np.sin(X)

plt.plot(X,C)
plt.plot(X,S)

plt.show()


import numpy as np
import scipy.spatial as spspacial
import scipy.sparse as spsparse

import sys

def distance_matrix(X):
    distance_matrix = spspacial.distance.squareform(spspacial.distance.pdist(X))
    return distance_matrix

def node_degrees(W):
    if isinstance(W, np.ndarray):
        D = W.sum(axis=1) - np.diag(W)
    else:
        D = W.sum(axis=1).A1 - W.diagonal()
    return D

def kernel_exponential(a, eps=1.0, d=1, cutoff=None):
    result = (eps ** -d) * np.exp(-((a / eps) ** 2))
    if not cutoff is None:
        result[result < cutoff] = 0.0
    result = spsparse.csr_matrix(result)

    return result

def kernel_indicator(a, eps=1.0, d=1, radius=1.0):
    result = a.copy()
    result[result >= radius * eps] = 0.0
    result[result > 1e-10] = 1.0
    np.fill_diagonal(result, 1.0)  # inplace operation
    result = (eps ** -d) * spsparse.csr_matrix(result)

    return result

def epsilon_ball(data, epsilon, kernel="gaussian", eta=None):
    """Epsilon ball weight matrix

    This is an adaptation of Jeff Calders implementation licensed
    under the MIT licence. For the original source code see
    `https://github.com/jwcalder/GraphLearning/blob/master/graphlearning/ssl.py`.
    ======

    General function for constructing a sparse epsilon-ball weight matrix, whose weights have the form
    \\[ w_{i,j} = \\eta\\left(\\frac{\\|x_i - x_j\\|^2}{\\varepsilon^2} \\right), \\]
    when \\(\\|x_i - x_j\\|\\leq \\varepsilon\\), and \\(w_{i,j}=0\\) otherwise.
    This type of weight matrix is only feasible in relatively low dimensions.

    Parameters
    ----------
    data : (n,m) numpy array
        n data points, each of dimension m
    epsilon : float
        Connectivity radius
    kernel : string (optional), {'uniform','gaussian','singular','distance'}, default='gaussian'
        The choice of kernel in computing the weights between \\(x_i\\) and \\(x_j\\) when
        \\(\\|x_i-x_j\\|\\leq \\varepsilon\\). The choice 'uniform' corresponds to \\(w_{i,j}=1\\)
        and constitutes an unweighted graph, 'gaussian' corresponds to
        \\[ w_{i,j} = \\exp\\left(\\frac{-4\\|x_i - x_j\\|^2}{\\varepsilon^2} \\right), \\]
        'distance' corresponds to
        \\[ w_{i,j} = \\|x_i - x_j\\|, \\]
        and 'singular' corresponds to
        \\[ w_{i,j} = \\frac{1}{\\|x_i - x_j\\|}, \\]
        when \\(i\\neq j\\) and \\(w_{i,i}=1\\).
    eta : python function handle (optional)
        If provided, this overrides the kernel option and instead uses the weights
        \\[ w_{i,j} = \\eta\\left(\\frac{\\|x_i - x_j\\|^2}{\\varepsilon^2} \\right). \\]

    Returns
    -------
    W : (n,n) scipy sparse matrix, float
        Sparse weight matrix.
    """
    n = data.shape[0]  # Number of points

    # Rangesearch to find nearest neighbors
    Xtree = spspacial.cKDTree(data)
    M = Xtree.query_pairs(epsilon, output_type="ndarray")

    # Differences between points and neighbors
    V = data[M[:, 0], :] - data[M[:, 1], :]
    dists = np.sum(V * V, axis=1)

    # If eta is None, use kernel keyword
    if eta is None:

        if kernel == "uniform":
            weights = np.ones_like(dists)
            fzero = 1
        elif kernel == "gaussian":
            weights = np.exp(-4 * dists / (epsilon * epsilon))
            fzero = 1
        elif kernel == "distance":
            weights = np.sqrt(dists)
            fzero = 0
        elif kernel == "singular":
            weights = np.sqrt(dists)
            weights[dists == 0] = 1
            weights = 1 / weights
            fzero = 1
        else:
            sys.exit("Invalid choice of kernel: " + kernel)

    # Else use user-defined eta
    else:
        weights = eta(dists / (epsilon * epsilon))
        fzero = eta(0)

    # Weights

    # Symmetrize weights and add diagonal entries
    weights = np.concatenate((weights, weights, fzero * np.ones(n,)))
    M1 = np.concatenate((M[:, 0], M[:, 1], np.arange(0, n)))
    M2 = np.concatenate((M[:, 1], M[:, 0], np.arange(0, n)))

    # Construct sparse matrix and convert to Compressed Sparse Row (CSR) format
    W = spsparse.coo_matrix((weights, (M1, M2)), shape=(n, n))

    return W.tocsr()


import torch
import numpy as np

def np_one_hot(array, num_classes):
    return np.eye(num_classes, dtype=int)[np.array(array, dtype=int)]

def one_hot(array, num_classes):
    return torch.eye(num_classes, dtype=torch.int)[array]

def np_det_nd(array):
    # support nd array det op.
    return np.reshape(
        np.linalg.det(np.reshape(array, (-1, *array.shape[-2:]))),
        array.shape[:-2]
    )

def det_nd(array):
    # support nd array det op.
    return torch.reshape(
        torch.det(torch.reshape(array, (-1, *array.shape[-2:]))),
        array.shape[:-2]
    )

if __name__ == '__main__':
    # inputs = torch.randn(1, 2, 4, 4)
    inputs = torch.randn((4, 4))
    out = det_nd(inputs)
    print(out)
    print(torch.det(inputs))

    # inputs = np.random.randn(1, 2, 4, 4)
    inputs = np.random.randn(4, 4)
    out = np_det_nd(inputs)
    print(out)
    print(np.linalg.det(inputs))


# batch version

import numpy as np

def cross_entropy_error(y, t):
    delta = 1e-7  # to avoid log(0)

    # if y is not a matrix
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    # if labels are one-hot vectors
    if t.size == y.size:
        t = t.argmax(axis=1)

    batch_size = y.shape[0]
    return - np.sum(np.log(y[np.arange(batch_size), t] + delta)) / batch_size

if __name__ == '__main__':
    t = np.array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0])

    y1 = np.array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    y2 = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    y3 = np.array([0.1, 0.9, 0.3, 0.3, 0, 0, 0.2, 0, 0, 0.6])

    print('t = {0}'.format(t))
    print('y1 = {0}, cross_entropy_error = {1}'.format(
        y1, cross_entropy_error(y1, t)))
    print('y2 = {0}, cross_entropy_error = {1}'.format(
        y2, cross_entropy_error(y2, t)))
    print('y3 = {0}, cross_entropy_error = {1}'.format(
        y3, cross_entropy_error(y3, t)))
    print('t = {0}, cross_entropy_error = {1}'.format(
        t, cross_entropy_error(t, t)))

    ts = np.array([[0, 0, 1, 0, 0],
                   [1, 0, 0, 0, 0],
                   [0, 0, 0, 1, 0]])
    ys = np.array([[0.1, 0.4, 0.6, 0, 0.3],
                   [0.3, 0.5, 0.1, 0.7, 0],
                   [0, 0, 0, 1, 0]])

    print('ts: {0}, ys: {1}'.format(ts.shape, ys.shape))
    print('ys = {0}, cross_entropy_error = {1}'.format(
        ys, cross_entropy_error(ys, ts)))


import numpy as np
from context import gpytoolbox

# Choose grid size
gs = np.array([19,15])
h = 1.0/(gs-1)

# Build a grid
x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]))
V = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

# Build staggered grid in x direction
x, y = np.meshgrid(np.linspace(0+0.5*h[0],1-0.5*h[0],gs[0]-1),np.linspace(0,1,gs[1]))
Vx = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)
# Build staggered grid in y direction
x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0+0.5*h[1],1-0.5*h[1],gs[1]-1))
Vy = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

# Build partial derivative matrices
Dx = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=0)
Dy = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=1)

# all rows must sum up to zero (i.e. a constant function has zero derivative)
assert((np.isclose(Dx.sum(axis=1),np.zeros((Dx.shape[0],1)))).all())
assert((np.isclose(Dy.sum(axis=1),np.zeros((Dy.shape[0],1)))).all())

# Build linear function
f = 2*V[:,0] + 5*V[:,1]
computed_derivative_x = Dx*f
computed_derivative_y = Dy*f
# Derivatives must be 2.0 and 5.0, respectively
assert((np.isclose(computed_derivative_x,2.0*np.ones((computed_derivative_x.shape[0])))).all())
assert((np.isclose(computed_derivative_y,5.0*np.ones((computed_derivative_y.shape[0])))).all())

# Convergence test
linf_norm_x = 100.0
linf_norm_y = 100.0
print("This experiment should print a set of decreasing values, converging")
print("towards zero and decreasing roughly by half in each iteration")
for power in range(3,13,1):
    gs = np.array([2**power,2**power - 2])
    h = 1.0/(gs-1)
    # Build a grid
    x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]))
    V = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

    # Build staggered grid in x direction
    x, y = np.meshgrid(np.linspace(0+0.5*h[0],1-0.5*h[0],gs[0]-1),np.linspace(0,1,gs[1]))
    Vx = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)
    # Build staggered grid in y direction
    x, y = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0+0.5*h[1],1-0.5*h[1],gs[1]-1))
    Vy = np.concatenate((np.reshape(x,(-1, 1)),np.reshape(y,(-1, 1))),axis=1)

    # Build partial derivative matrices
    Dx = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=0)
    Dy = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=1)
    # Build non-linear function
    f = np.cos(V[:,0]) + np.sin(V[:,1])
    # Derivatives on staggered grids
    fx = -np.sin(Vx[:,0])
    fy =  np.cos(Vy[:,1])
    # Computed derivatives using our matrices
    computed_derivative_x = Dx*f
    computed_derivative_y = Dy*f
    # Print L infinity norm of difference
    # Make sure norm is decreasing
    assert(linf_norm_x>np.max(np.abs(computed_derivative_x - fx)))
    assert(linf_norm_y>np.max(np.abs(computed_derivative_y - fy)))
    linf_norm_x = np.max(np.abs(computed_derivative_x - fx))
    linf_norm_y = np.max(np.abs(computed_derivative_y - fy))
    # Print L infinity norm of difference
    print(np.array([np.max(np.abs(computed_derivative_x - fx)),np.max(np.abs(computed_derivative_y - fy))]))

# 3D Unit test
# Choose grid size
gs = np.array([19,15,23])
h = 1.0/(gs-1)

# Build a grid
x, y, z = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]),np.linspace(0,1,gs[2]),indexing='ij')
V = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)

# Build staggered grid in x direction
x, y, z = np.meshgrid(np.linspace(0+0.5*h[0],1-0.5*h[0],gs[0]-1),np.linspace(0,1,gs[1]),np.linspace(0,1,gs[2]),indexing='ij')
Vx = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)
# Build staggered grid in y direction
x, y, z = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0+0.5*h[1],1-0.5*h[1],gs[1]-1),np.linspace(0,1,gs[2]),indexing='ij')
Vy = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)
# Build staggered grid in z direction
x, y, z = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]),np.linspace(0+0.5*h[2],1-0.5*h[2],gs[2]-1),indexing='ij')
Vz = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)

# Build partial derivative matrices
Dx = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=0)
Dy = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=1)
Dz = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=2)

# all rows must sum up to zero (i.e. a constant function has zero derivative)
assert((np.isclose(Dx.sum(axis=1),np.zeros((Dx.shape[0],1)))).all())
assert((np.isclose(Dy.sum(axis=1),np.zeros((Dy.shape[0],1)))).all())
assert((np.isclose(Dz.sum(axis=1),np.zeros((Dz.shape[0],1)))).all())

# Build linear function
f = 2*V[:,0] + 5*V[:,1] - 4*V[:,2]
computed_derivative_x = Dx @ f
computed_derivative_y = Dy @ f
computed_derivative_z = Dz @ f
# Derivatives must be 2.0 and 5.0, respectively
assert((np.isclose(computed_derivative_x,2.0*np.ones((computed_derivative_x.shape[0])))).all())
assert((np.isclose(computed_derivative_y,5.0*np.ones((computed_derivative_y.shape[0])))).all())
assert((np.isclose(computed_derivative_z,-4.0*np.ones((computed_derivative_z.shape[0])))).all())

# Convergence test
linf_norm_x = 100.0
linf_norm_y = 100.0
linf_norm_z = 100.0
print("This experiment should print a set of decreasing values, converging")
print("towards zero and decreasing roughly by half in each iteration")
for power in range(3,9,1):
    gs = np.array([2**power,2**power - 2,2**power - 1])
    h = 1.0/(gs-1)
    # Build a grid
    x, y, z = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]),np.linspace(0,1,gs[2]),indexing='ij')
    V = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)

    # Build staggered grid in x direction
    x, y, z = np.meshgrid(np.linspace(0+0.5*h[0],1-0.5*h[0],gs[0]-1),np.linspace(0,1,gs[1]),np.linspace(0,1,gs[2]),indexing='ij')
    Vx = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)
    # Build staggered grid in y direction
    x, y, z = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0+0.5*h[1],1-0.5*h[1],gs[1]-1),np.linspace(0,1,gs[2]),indexing='ij')
    Vy = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)
    # Build staggered grid in z direction
    x, y, z = np.meshgrid(np.linspace(0,1,gs[0]),np.linspace(0,1,gs[1]),np.linspace(0+0.5*h[2],1-0.5*h[2],gs[2]-1),indexing='ij')
    Vz = np.concatenate((np.reshape(x,(-1, 1),order='F'),np.reshape(y,(-1, 1),order='F'),np.reshape(z,(-1, 1),order='F')),axis=1)

    # Build partial derivative matrices
    Dx = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=0)
    Dy = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=1)
    Dz = gpytoolbox.fd_partial_derivative(gs=gs,h=h,direction=2)

    # Build non-linear function
    f = np.cos(V[:,0]) + np.sin(V[:,1]) + 3*np.cos(V[:,2])
    # Derivatives on staggered grids
    fx = -np.sin(Vx[:,0])
    fy =  np.cos(Vy[:,1])
    fz =-3*np.sin(Vz[:,2])
    computed_derivative_x = Dx @ f
    computed_derivative_y = Dy @ f
    computed_derivative_z = Dz @ f
    assert(linf_norm_x>np.max(np.abs(computed_derivative_x - fx)))
    assert(linf_norm_y>np.max(np.abs(computed_derivative_y - fy)))
    assert(linf_norm_z>np.max(np.abs(computed_derivative_z - fz)))
    linf_norm_x = np.max(np.abs(computed_derivative_x - fx))
    linf_norm_y = np.max(np.abs(computed_derivative_y - fy))
    linf_norm_z = np.max(np.abs(computed_derivative_z - fz))
    # Print L infinity norm of difference
    print(np.array([np.max(np.abs(computed_derivative_x - fx)),np.max(np.abs(computed_derivative_y - fy)),np.max(np.abs(computed_derivative_z - fz))]))

print("Unit test passed, all asserts passed")


import numpy as np
from numpy.fft import fft, ifft, fftshift, ifftshift
from .FFTBase import FFTBase

class T1FFT(FFTBase):
    """
    The Fast Fourier Transform on the Circle / 1-Torus / 1-Sphere.
    """

    @staticmethod
    def analyze(f, axis=0):
        """
        Compute the Fourier Transform of the discretely sampled function f : T^1 -> C.

        Let f : T^1 -> C be a band-limited function on the circle.
        The samples f(theta_k) correspond to points on a regular grid on the circle, as returned by spaces.T1.linspace:
        theta_k = 2 pi k / N
        for k = 0, ..., N - 1

        This function computes
        \hat{f}_n = (1/N) \sum_{k=0}^{N-1} f(theta_k) e^{-i n theta_k}
        which, if f has band-limit less than N, is equal to:
        \hat{f}_n = \int_0^{2pi} f(theta) e^{-i n theta} dtheta / 2pi,
                  = <f(theta), e^{i n theta}>
        where dtheta / 2pi is the normalized Haar measure on T^1, and < , > denotes the inner product on Hilbert space,
        with respect to which this transform is unitary.

        The range of frequencies n is -floor(N/2) <= n <= ceil(N/2) - 1

        :param f:
        :param axis:
        :return:
        """
        # The numpy FFT returns coefficients in a different order than we want them,
        # and using a different normalization.
        fhat = fft(f, axis=axis)
        fhat = fftshift(fhat, axes=axis)
        return fhat / f.shape[axis]

    @staticmethod
    def synthesize(f_hat, axis=0):
        """
        Compute the inverse / synthesis Fourier transform of the function f_hat : Z -> C.
        The function f_hat(n) is sampled at points in a limited range -floor(N/2) <= n <= ceil(N/2) - 1

        This function returns
        f[k] = f(theta_k) = sum_{n=-floor(N/2)}^{ceil(N/2)-1} f_hat(n) exp(i n theta_k)
        where theta_k = 2 pi k / N
        for k = 0, ..., N - 1

        :param f_hat:
        :param axis:
        :return:
        """

        f_hat = ifftshift(f_hat * f_hat.shape[axis], axes=axis)
        f = ifft(f_hat, axis=axis)
        return f

    @staticmethod
    def analyze_naive(f):
        f_hat = np.zeros_like(f)
        for n in range(f.size):
            for k in range(f.size):
                theta_k = k * 2 * np.pi / f.size
                f_hat[n] += f[k] * np.exp(-1j * n * theta_k)
        return fftshift(f_hat / f.size, axes=0)


#Antonio Diaz UIN 327003625 TAMU 2022
#Numerical Simulations 430
#Hmwk 1 graphing and plotting exat solutions

# -*- coding: utf-8 -*
import sys
import numpy as np
import matplotlib.pyplot as plt

#attempt to create more general method for smaller delta x
def case_1_produce_FDE_matrix(n):
    # creating 2**n - 1 rows / columns
    zero_array = np.zeros((2**n-1, 2**n-1))
    FDE_matrix = zero_array
    #print(zero_array)
    # alpha_sum const open to change
    alpha = 4
    delta_x = 1.0/(2**n)
    # second order k
    # k = 2+alpha**2*delta_x**2
    # fourth order k
    # k = 2+alpha**2*delta_x**2*(1 + delta_x**2/12*alpha**2)
    # sixth order k
    # k = 2+alpha**2*delta_x**2*(1 + delta_x**2/12*alpha**2+delta_x**4/360*alpha**4)
    # eighth order k
    # k = 2+alpha**2*delta_x**2*(1 + delta_x**2/12*alpha**2+delta_x**4/360*alpha**4+delta_x**6/20160*alpha**6)
    # tenth order k
    k = 2+alpha**2*delta_x**2*(1 + delta_x**2/12*alpha**2+delta_x**4/360*alpha**4+delta_x**6/20160*alpha**6+delta_x**8/1814400*alpha**8)
    # print("K value used in matrix")
    # print(k)
    case_1_row_triple = [1, -k, 1]

    FDE_matrix[0][0:2] = case_1_row_triple[1:3]

    for row_num in range(1, 2**n-2):
        FDE_matrix[row_num][row_num-1:row_num+2] = case_1_row_triple

    FDE_matrix[2**n-2][2**n-3:2**n-1] = case_1_row_triple[0:2]

    #print(FDE_matrix)
    return(FDE_matrix)

def analytical_sol_case1(n):
    # Bar Parameters ----------------------------------------------------------------------------------------------------------------------------------
    k = .5              # thermal conductivity of material
    R = .1              # cross section radius
    Ac = np.pi * R**2   # cross sectional area
    L = 1               # length

    # Case Parameters -----------------------------------------------------------------------------------------------------------------------------
    Tb = 0              # T(0), base temperature
    T_l = 100            # T(L), tip temperature
    Ta = 0              # ambient temperature

    # Processing & Output ----------------------------------------------------------------------------------------------------------------------------
    x = np.linspace(0, 1, 2**n+1)
    a=4
    h = a**2 * k * R / 2
    C = (T_l - Ta - (Tb-Ta)*np.cosh(a*L))/np.sinh(a*L)
    D = 0
    T = C*np.sinh(a*x) + D*np.cosh(a*x) + Ta
    analytical_sol_list = []
    for i in np.nditer(T):
        analytical_sol_list.append(i.tolist())
    return analytical_sol_list

#creating resultant matrix

log_error_list = []
log_deltax_list = []

for n in range(2, 14):
    print('results for n = {:.2f}'.format(n))
    print('\u0394X = 1/2^({:.2f})'.format(n))
    resultant_matrix = np.zeros((2**n-1,1))
    resultant_matrix[2**n-2,0] = -100
    # print("FDE matrix:")
    # print(case_1_produce_FDE_matrix(n))

    # print("Resultant matrix")
    # print(resultant_matrix)

    a_matrix_inverse = np.linalg.inv(case_1_produce_FDE_matrix(n))
    solution_matrix = np.dot(a_matrix_inverse, resultant_matrix)
    pos_along_rod_list_cm = np.linspace(0, 1, num=2**n+1)
    temp_solutions_list = [0]

    for i in np.nditer(solution_matrix):
        temp_solutions_list.append(i.tolist())

    temp_solutions_list.append(100)

    # print("Temp FDM outputs:")
    # print(temp_solutions_list)

    halfway = int(len(temp_solutions_list)/2)
    print("FDM output at halfway point:")
    print('{:.10f} \xb0C'.format(temp_solutions_list[halfway]))
    print("True analytical solution list at halfway point: ")
    print('{:.10f} \xb0C'.format(analytical_sol_case1(n)[halfway]))
    # print(u'{:.12f} cm : T_FDM = {:.12f} \xb0C,  T_analytical = {:.12f} \xb0C, error = {:.12f}'.format(pos_along_rod_list_cm[halfway], temp_solutions_list[halfway], analytical_sol_case1(n)[halfway],  (temp_solutions_list[halfway]-analytical_sol_case1(n)[halfway])/analytical_sol_case1(n)[halfway]*100))

    log_deltax_list.append(np.log(1.0/2**n))
    # print(log_deltax_list)
    log_error_list.append(np.log(np.abs((-temp_solutions_list[halfway]+analytical_sol_case1(n)[halfway])/analytical_sol_case1(n)[halfway]*100)))
    # print(log_error_list)

    if (n > 3):
        print("Beta Value:")
        last_index = len(log_error_list)-1
        print((log_error_list[last_index]-log_error_list[last_index-1])/(log_deltax_list[last_index]-log_deltax_list[last_index-1]))

    print(" ")
# print("log(DeltaX):")
# print(log_deltax_list)
# print("log(Error):")
# print(log_error_list)
# print("Beta value:")
#last_index = len(log_error_list)-1
#print((log_error_list[last_index]-log_error_list[last_index-1])/(log_deltax_list[last_index]-log_deltax_list[last_index-1]))
plt.title("10th order FDM Convergence of Temperature at .5 cm")
plt.plot(log_deltax_list,log_error_list)
plt.xlabel('log DeltaX')
plt.ylabel('log Error')

plt.show()

"""
print("Case 1, 10th order FDM Results, DeltaX = 1/2**4:")
for i in range(0, len(temp_solutions_list)):
    percenterror = (temp_solutions_list[i]-analytical_sol_case1(n)[i])/(analytical_sol_case1(n)[i]+.000000001)
    print(u'{:.7f} cm : T_(FDM) = {:.7f} \xb0C,  T_analytical = {:.7f} \xb0C,  Error = {:.7f}'.format(pos_along_rod_list_cm[i], temp_solutions_list[i], analytical_sol_case1(n)[i], percenterror*100))

plt.title("10th order FDM Temp Solutions vs Analytical, DeltaX = 1/2**4")
plt.plot(np.linspace(0, 1, 2**n+1), analytical_sol_case1(n), 'r', np.linspace(0, 1, 2**n+1), temp_solutions_list, 'b')
plt.xlabel('Position Along Rod (cm)')
plt.ylabel('Temperature (C)')

plt.show()
"""


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

# In[ ]:

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

def fact(n):
    if n==0:
        return 1
    else:
        return n*fact(n-1)

# Taylor series for cosine function
x = np.arange(-5*math.pi, 5*math.pi, 0.0001)

y1 = np.cos(x)

k = int(input('Enter the degree upto which taylor series will be taken = '))

y2=0
for i in range(k):
    y2 += ((-1)**i)*(x**(2*i)) / (fact(2*i))

plt.plot(x, y1)
plt.plot(x, y2)
plt.grid()
plt.ylabel('cos(x)')
plt.xlabel('x')
plt.legend(['cosine', 'taylor'])
plt.ylim(-3,3)
plt.show()

Taylor Series Visualization for Cosine Function.py


'''
objective: 2 layers NN learns XOR
also demo the implementation of back propagation in numpy
'''
print(__doc__)

import numpy as np

sigmoid = lambda x: 1 / (1 + np.exp(-x))
dsigmoid = lambda x: x * (1 - x)

X = np.array([[0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
y = np.array([[0, 1, 1, 0]]).T

n_sample, input_dim = X.shape
n_sample, output_dim = y.shape
n_neuron = 4  # design param

w0 = np.random.random((input_dim, n_neuron))
w2 = np.random.random((n_neuron, output_dim))

while True:
    l1 = np.dot(X, w0)  # Nxd = Nx3 * 3xd # N: number of sample, d = n_neuron, design param, 3 = input_dim
    l2 = sigmoid(l1)    # Nxd
    l3 = np.dot(l2, w2) # Nx1 = Nxd * dx1 , 1 = output_dim
    l4 = sigmoid(l3)    # Nx1

    if all((l4 > 0.5).astype(bool) == y.astype(bool)): break

    l4_error = l4 - y  # Nx1
    l3_error = l4_error * dsigmoid(l4)  # Nx1 = Nx1 .* Nx1
    l2_error = l3_error.dot(w2.T)  # Nxd = Nx1 * 1xd
    l1_error = l2_error * dsigmoid(l2)  # Nxd

    w2 -= l2.T.dot(l3_error)  # dx1 = dxN * Nx1
    w0 -= X.T.dot(l1_error)  # 3xd = 3xN * Nxd

print('last layer output, required by XOR', l4)


import numpy as np
import random as rd
import matplotlib.pyplot as plt
import math
import seaborn as sns
def test(sampleSize, s):
    #f =
    d = []
    x = s
    for i in range(sampleSize):
        y = np.random.normal(x, 1)
        #y = np.random.rand()
        xx = func(x)
        yy = func(y)
        acpt = min(1, yy/xx)
        r = rd.random()
        if r < acpt:
            x = y
        d.append(x)

    print(d)
    #return d
    sns.distplot(d)
    plt.show()

def func(x):
    return 0.3 * math.exp((-0.2) * x * x) + 0.7 * math.exp((-0.2) * pow(x-10, 2))
    #return np.sqrt(1/(2*np.pi))*np.power(np.e,(-0.5)*(x-10)**2)

if __name__ == "__main__":
    test(5000,0)


import numpy as np

def sigmoid(x):
    """
    Calculate sigmoid
    """
    return 1 / (1 + np.exp(-x))

x = np.array([0.5, 0.1, -0.2])
target = 0.6
learnrate = 0.5

weights_input_hidden = np.array([[0.5, -0.6],
                                 [0.1, -0.2],
                                 [0.1, 0.7]])

weights_hidden_output = np.array([0.1, -0.3])

# Forward pass
hidden_layer_input = np.dot(x, weights_input_hidden)
hidden_layer_output = sigmoid(hidden_layer_input)
print('HO = ', hidden_layer_output)

output_layer_in = np.dot(hidden_layer_output, weights_hidden_output)
output = sigmoid(output_layer_in)
print('OO = ', output)

# Backwards pass
# TODO: Calculate output error
error = target - output
print('Output Error = ', error)

# TODO: Calculate error term for output layer
output_error_term = error * output * (1 - output)
print('dO = ', output_error_term)

# TODO: Calculate error term for hidden layer
hidden_error_term = np.dot(weights_hidden_output, output_error_term) * \
    hidden_layer_output * (1 - hidden_layer_output)
print('dH = ', hidden_error_term)

# TODO: Calculate change in weights for hidden layer to output layer
delta_w_h_o = learnrate * output_error_term * hidden_layer_output
print('delta w H->O = ', delta_w_h_o)

# TODO: Calculate change in weights for input layer to hidden layer
delta_w_i_h = learnrate * hidden_error_term * x[:, None]
print('delta w I->H = ', delta_w_i_h)

print('Change in weights for hidden layer to output layer:')
print(delta_w_h_o)
print('Change in weights for input layer to hidden layer:')
print(delta_w_i_h)


# Illustration of gibbs sampling for 2-dim Gaussian
# Author: Gerardo Durán-Martín
# Translated from gibbsGaussDemo.m

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal, norm
import pyprobml_utils as pml

μ = np.zeros(2)
Σ = np.array([[1, 0.99], [0.99, 1]])
mvn = multivariate_normal(μ, Σ)

# Plot one contour of the multivariate normal
X = np.mgrid[-3:3:0.01, -3:3:0.01]
density = np.apply_along_axis(mvn.pdf, 0, X)

# Gibbs-Sampling path
blue_seq = np.array([
    [-1/2,-1],
    [-1/2,0],
    [1,0],
    [1,1],
    [-1/2,1],
    [-1/2,1/2],
    [1.5,1/2],
    [1.5,1.5],
]) / 3

# Compute marginal parameters x0|x1
x0_range = np.arange(-2, 2, 0.01)
x0_obs = 0.7
Σ0_cond = Σ[0,0] - Σ[0, 1] * Σ[1, 0] / Σ[1, 1]
μ0_cond = μ[0] + Σ[0, 1] * (x0_obs - μ[1]) / Σ[1, 1]

plt.plot(*blue_seq.T)
plt.contour(*X, density, levels=[0.07], colors="tab:red")
plt.xlim(-3, 3)
plt.ylim(-3, 3)
plt.scatter(0, 0, marker="x", c="tab:red", s=300)
plt.text(0, 1.7, "L", size=11)
plt.text(1.2, -2, "l", size=11)
plt.annotate("", xy=(-2.5, 1.5), xytext=(2.5, 1.5), arrowprops=dict(arrowstyle='<->'))
plt.annotate("", xy=(0.5, -2), xytext=(1, -2), arrowprops=dict(arrowstyle='<->'))
# Scaled down and shifted marginal gaussian
plt.plot(x0_range, norm(μ0_cond, np.sqrt(Σ0_cond)).pdf(x0_range)* 0.3 - 3, c="tab:green")
plt.tight_layout()
pml.savefig('gmm_singularity.pdf')
plt.show()


import numpy as np

def perceptron_train(X, Y):
  w = np.zeros(len(X[0]))
  bias = 1.0
  running = True
  count = 0
  a = 0.0

  while (running):

    for i in range(len(X)):
      for j in range(len(X[0])):
        a += w[j]*X[i][j]
      a += bias
      if not (a*Y[i] > 0):
        for k in range(len(X[0])):
          w[k] += X[i][k]*Y[i]
        bias += Y[i]
      else:
        count += 1
      a = 0
    # check the update of weight in an epoch.
    if (count == len(X)):
      running = False
    count = 0

  print("The returned weights ", w, " and bias: ", bias)
  return w, bias

def perceptron_test(X_test, Y_test, w, b):

  accuracy = 0.0
  correct = 0.0
  a = 0.0

  for i in range(len(X_test)):
    for j in range(len(w)):
      a += w[j]*X_test[i][j]
    a += b
    if (a*Y_test[i] > 0):
      correct += 1.0
    a = 0.0

  accuracy = float(correct)/float(len(Y_test))

  return accuracy

if __name__ == '__main__':

  X = np.array([[-2,1], [1,1], [1.5,-0.5], [-2,-1], [-1,-1.5], [2,-2],[-2,1], [1,1], [1.5,-0.5]])
  Y = np.array([[1],[1],[1],[-1],[-1],[-1],[1],[1],[1]])

  W = perceptron_train(X,Y)
  test_acc = perceptron_test(X,Y,W[0],W[1])
  print("The accuracy: ", test_acc*100, "%")


# Smallest multiple
# Problem 5
# 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
#
# What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?

import numpy as np
import math

# is_prime
def is_prime(n):
    if n % 2 == 0 and n > 2:
        return False
    return all(n % i for i in range(3, int(math.sqrt(n)) + 1, 2))

N = 20
Nl = np.arange(2, N+1, dtype=np.int64)
Nl = Nl[:, np.newaxis]
is_vprime = np.vectorize(is_prime)
pl = np.extract(is_vprime(Nl),Nl)
pl = pl[np.newaxis, :]

emax = 1
while 2**(emax+1) < N:
    emax += 1

ml = np.zeros([emax, N-1, pl.size], dtype=np.int64);
for i in range(emax):
     ml[i,:,:] = (Nl % (pl**(i+1)))  < 1

pel = np.max(np.sum(ml,axis=0),axis=0)
np.power(pl,pel)
ans = np.prod(np.power(pl,pel))
print(ans)


import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
plt.rcParams['figure.figsize']=[8,7]

k0s = np.array([0,1,2,3])
xrange = np.arange(-1,2.5,0.05)
n = 5
theta_true = 1
sigma_true = 1
theta_prior = 0
markers = ['*','o','v','^']
colors = ['black','blue','green','red']
legends = ['postMean0','postMean1','postMean2','postMean3']

for i, ki in enumerate(k0s):
    w = n/(n+ki)
    v = (w**2)*(sigma_true**2)/n
    theta_post = w*theta_true + (1-w)*theta_prior
    plt.plot(xrange, norm.pdf(xrange, theta_post, v**0.5), color=colors[i],
            marker=markers[i], markersize=5, linewidth=2,label=legends[i])
plt.legend()
plt.ylim([0,1.5])
plt.show()

n_list = np.arange(1,50,2)

for i, ki in enumerate(k0s):
    w = n_list/(n_list+ki)
    mse_map = ((1-w)*(theta_prior-theta_true))**2 + (w**2)*sigma_true/n_list
    mse_mean = sigma_true/n_list
    mse_ratio = mse_map/mse_mean
    plt.plot(n_list, mse_ratio, color=colors[i],
            marker=markers[i], markersize=5, linewidth=2,label=legends[i])
plt.legend()
plt.ylim([0.5,1.3])
plt.show()


from numba import njit
import numba
import numpy as np
import hvwfg as hv

from desdeo_tools.scalarization import SimpleASF

@njit()
def epsilon_indicator(s1: np.ndarray, s2: np.ndarray) -> float:
    """ Computes the additive epsilon-indicator between two solutions.

    Args:
        s1 (np.ndarray): Solution 1. Should be an one-dimensional array.
        s2 (np.ndarray): Solution 2. Should be an one-dimensional array.

    Returns:
        float: The maximum distance between the values in s1 and s2.
    """
    eps = 0.0
    for i in range(s1.size):
        value = s2[i] - s1[i]
        if value > eps:
            eps = value
    return eps

@njit()
def epsilon_indicator_ndims(front: np.ndarray, reference_point: np.ndarray) -> list:
    """ Computes the additive epsilon-indicator between reference point and current one-dimensional vector of front.

    Args:
        front (np.ndarray): The front that the current reference point is being compared to.
            Should be set of arrays, where the rows are the solutions and the columns are the objective dimensions.
        reference_point (np.ndarray): The reference point that is compared. Should be one-dimensional array.

    Returns:
        list: The list of indicator values.
    """
    min_eps = 0.0
    eps_list = np.zeros((front.shape[0]), dtype=numba.float64)
    for i in np.arange(front.shape[0]):
        value = np.max(reference_point - front[i])
        if value > min_eps:
            eps_list[i] = value
    return eps_list

def preference_indicator(s1: np.ndarray, s2: np.ndarray, min_asf_value: float, ref_point: np.ndarray, delta: float) -> float:
    """ Computes the preference-based quality indicator.

    Args:
        s1 (np.ndarray): Solution 1. Should be an one-dimensional array.
        s2 (np.ndarray): Solution 2. Should be an one-dimensional array.
        ref_point (np.ndarray): The reference point should be same shape as front.
        min_asf_value (float): Minimum value of achievement scalarization of the reference_front. Used in normalization.
        delta (float): The spesifity delta allows to set the amplification of the indicator to be closer or farther
            from the reference point. Smaller delta means that all solutions are in smaller range around the reference
            point.

    Returns:
        float: The maximum distance between the values in s1 and s2 taking into account
            the reference point and spesifity.
    """
    s2_asf = SimpleASF(np.ones_like(s2))
    norm = s2_asf(s2, reference_point=ref_point) + delta - min_asf_value
    return epsilon_indicator(s1, s2) / norm

def hypervolume_indicator(front: np.ndarray, reference_point: np.ndarray) -> float:
    """ Computes the hypervolume-indicator between reference front and current approximating point.

    Args:
        front (np.ndarray): The front that is compared. Should be set of arrays, where the rows are the solutions and
            the columns are the objective dimensions.
        reference_point (np.ndarray): The reference point that the current front is being compared to. Should be 1D array.

    Returns:
        float: Measures the volume of the objective space dominated by an approximation set.
    """
    ref = np.asarray(reference_point, dtype='double') # hv.wfg needs datatype to be double
    fr = np.asarray(front, dtype='double')
    return hv.wfg(fr, ref)

if __name__=="__main__":

    po_front = np.asarray([[1.0,0],[0.5,0.5], [0,1.0], [2, -1], [0,0]])
    sol1 = [4, 4] # cant be better than po front, min is zero
    sol = np.asarray(sol1)
    ref = np.asarray([0.7, 0.3])

    print("eps indi value")
    print(epsilon_indicator(po_front[0], sol))
    print(epsilon_indicator(po_front[1], sol))
    print(epsilon_indicator(po_front[2], sol))
    print(epsilon_indicator(po_front[3], sol))
    print(epsilon_indicator(po_front[4], sol))
    print("ndims")
    print(epsilon_indicator_ndims(po_front, sol))
    print(hypervolume_indicator(po_front, sol))
    print("pref")
    print(preference_indicator(po_front[1], sol, 0.1, ref, 0.1))


import numpy as np
import matplotlib.pyplot as plt
import math
from scipy.stats import triang
from scipy.stats import beta

## Integrand function
def f(x): return x**5+x**4

x=np.linspace(-1,1,endpoint=True,dtype=float)
plt.plot(x,f(x),'.')

## 1st Interval: Triangular-approximation
s=np.linspace(-1,-0.25,50)
plt.plot(s,0.04*triang.pdf(s,0.2/0.7,a,b-a), '--')
a=-1
b=-0.25
n1=50
x1=np.random.triangular(a,-0.8,b,n1)
eval_funct1=f(x1)
y1=triang.pdf(x1,0.2/0.7,a,b-a)
n1=n1*1.0

i1=(1/n1)*np.sum(eval_funct1/y1)

mu_camp1=(np.sum(eval_funct1/y1))/n1
var_camp1=1/(n1-1)*np.sum((eval_funct1/y1-mu_camp1)**2)
var1=(1/n1)*var_camp1

## 2nd Interval: Beta-approximation
k=np.linspace(-0.25,1,100)
plt.plot(k,0.4*beta.pdf(k,5,1), '-.')

n2=9950
x2=beta.rvs(5,1,size=n2)
eval_funct2=f(x2)
y2=beta.pdf(x2,5,1)

n2=n2*1.0
i2=(1/n2)*np.sum(eval_funct2/y2)

mu_camp2=(np.sum(eval_funct2/y2))/n2
var_camp2=1/(n2-1)*np.sum((eval_funct2/y2-mu_camp2)**2)
var2=(1/n2)*var_camp2

## Results
print(i1+i2,'Integral Mean with Importance Sampling')
print(var1+var2,'Total Variance with Importance Sampling')
print(sqrt(var1+var2), 'Total Standard Deviation with Importance Sampling')
plt.xlabel('x')
plt.ylabel('y')
plt.show()


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

def ecdf(data):
    """Compute ECDF for a one-dimensional array of measurements."""

    # Number of data points: n
    n = len(data)

    # x-data for the ECDF: x
    x = np.sort(data)

    # y-data for the ECDF: y
    y = np.arange(1, n+1) / n

    return x, y

x, y = ecdf(df["temperature"])

plt.figure(figsize=(8,7))
sns.set()
plt.plot(x, y, marker=".", linestyle="none")
plt.xlabel("Body Temperature (F)")
plt.ylabel("Cumulative Distribution Function")

samples = np.random.normal(np.mean(df["temperature"]), np.std(df["temperature"]), size=10000)

x_theor, y_theor = ecdf(samples)

plt.plot(x_theor, y_theor)
plt.legend(('Normal Distribution', 'Empirical Data'), loc='lower right')

print(stats.normaltest(df["temperature"]))

periscope.output(plt)


import numpy as np
import pandas as pd

# Principal Component Analysis (PCA)
def fit_PCA(X, num_components=0.99):
    '''
    Performs min-max normalization and PCA transformation on the input data array

    Inputs:
        - X: An array of values to perform PCA on
        - num_components: If >1, the number of principal components desired
                          If <1, the percentage of variance explained desired

    Outputs:
        - An array of the principal components

    TODO: Add check if data is already normalized
    '''
    from sklearn import preprocessing
    from sklearn.decomposition import PCA

    # Checking if the input is a numpy array and converting it if not
    if type(X) != np.ndarray:
        X = np.array(X)

    # Normalizing data before PCA
    min_max_scaler = preprocessing.MinMaxScaler()
    X_norm = min_max_scaler.fit_transform(X)

    # Performing PCA
    pca = PCA(n_components=num_components)
    pca.fit(X_norm)

    # Reporting explained variance
    explained_variance = pca.explained_variance_ratio_ * 100
    print('Total variance % explained:', sum(explained_variance))
    print()
    print('Variance % explained by principal component:')
    for principal_component in range(len(explained_variance)):
        print(principal_component, ':', explained_variance[principal_component])

    # Transforming the data before returning
    principal_components = pca.transform(X_norm)
    return principal_components

# Oversampling
def oversample_binary_label(dataframe, label_column):
    '''
    Oversamples a dataframe with a binary label to have an equal proportion in classes. Dynamically
    determines the label with the lower proportion.

    Inputs:
        - dataframe: A dataframe containing the label
        - label_column: A string of the column containing the label
    Output: A dataframe with the lower proportion label oversampled

    TODO: Update this to oversample the training set and return both the training and testing sets
    '''

    # Counting the classes
    class_0_count, class_1_count = dataframe[label_column].value_counts()

    # Creating two dataframes for each class
    dataframe_class_0 = dataframe[dataframe[label_column] == dataframe[label_column].unique()[0]]
    dataframe_class_1 = dataframe[dataframe[label_column] == dataframe[label_column].unique()[1]]

    # Determining the smaller class
    smaller_label = dataframe[label_column].value_counts().idxmin()

    # Oversampling
    if smaller_label == 0:
        dataframe_class_0_oversampled = dataframe_class_0.sample(class_1_count, replace=True)
        dataframe_oversampled = pd.concat([dataframe_class_1, dataframe_class_0_oversampled], axis=0)
    else:
        dataframe_class_1_oversampled = dataframe_class_1.sample(class_0_count, replace=True)
        dataframe_oversampled = pd.concat([dataframe_class_0, dataframe_class_1_oversampled], axis=0)

    # Printing results
    print('Initial number of observations in each class:')
    print(dataframe[label_column].value_counts())
    print()

    print('Oversampled number of observations in each class:')
    print(dataframe_oversampled[label_column].value_counts())

    return dataframe_oversampled

def oversample_smote(training_features, training_labels, is_dataframe=True):
    '''
    Convenience function for oversampling with SMOTE. This generates synthetic samples via interpolation.
    Automatically encodes categorical columns if a dataframe is provided with categorical columns properly marked.

    Input: The training features and labels. is_dataframe is for checking for categorical columns.
    Output: The oversampled training features and labels
    '''
    from imblearn import over_sampling

    if is_dataframe == True:
        # Testing if there are any categorical columns
        # Note: These must have the "category" datatype
        categorical_variable_list = training_features.select_dtypes(exclude=['number', 'bool_', 'object_']).columns
        if categorical_variable_list.shape[0] > 0:
            categorical_variable_list = list(categorical_variable_list)
            categorical_variable_indexes = training_features.columns.get_indexer(categorical_variable_list)
            smote = over_sampling.SMOTENC(categorical_features=categorical_variable_indexes, random_state=46, n_jobs=-1)
        else:
            smote = over_sampling.SMOTE(random_state=46, n_jobs=-1)
    else:
        smote = over_sampling.SMOTE(random_state=46, n_jobs=-1)

    # Performing oversampling
    training_features_oversampled, training_labels_oversampled = smote.fit_sample(training_features, training_labels)

    # Rounding discrete variables for appropriate cutoffs
    # This is becuase SMOTE NC only deals with binary categorical variables, not discrete variables
    if is_dataframe == True:
        discrete_variable_list = training_features.select_dtypes(include=['int', 'int32', 'int64']).columns
        if discrete_variable_list.shape[0] > 0:
            discrete_variable_indexes = training_features.columns.get_indexer(discrete_variable_list)
            for discrete_variable_index in discrete_variable_indexes:
                training_features_oversampled[:, discrete_variable_index] = np.round(training_features_oversampled[:, discrete_variable_index].astype(float)).astype(int)

    print('Previous training size:', len(training_labels))
    print('Oversampled training size', len(training_labels_oversampled), '\n')
    print('Previous label mean:', training_labels.astype(int).mean())
    print('Oversampled label mean:', training_labels_oversampled.mean())

    return training_features_oversampled, training_labels_oversampled

def target_encode(train_variable, test_variable, train_label, smoothing=1, min_samples_leaf=1, noise_level=0):
    '''
    Mean target encoding using Daniele Micci-Barreca's technique from the following paper:
    http://helios.mm.di.uoa.gr/~rouvas/ssi/sigkdd/sigkdd.vol3.1/barreca.pdf

    This function heavily borrows code from Olivier's Kaggle post:
    https://www.kaggle.com/ogrellier/python-target-encoding-for-categorical-features

    Inputs:
        - train_variable (Series): Variable in the training set to perform the encoding on.
        - test_variable (Series): Variable in the testing set to be transformed.
        - train_label (Series): The label in the training set to use for performing the encoding.
        - smoothing (int): Balances the categorical average vs. the prior.
        - min_samples_leaf (int): The minimum number of samples to take the category averagesinto account.
        - noise_level (int): Amount of Gaussian noise to add in order to help prevent overfitting.
    '''

    def add_noise(series, noise_level):
        '''
        Adds Gaussian noise to the data
        '''
        return series * (1 + noise_level * np.random.randn(len(series)))

    assert len(train_variable) == len(train_label)
    assert train_variable.name == test_variable.name

    # Creating a data frame out of the training variable and label in order to get the averages of the label
    # for the training variable
    temp = pd.concat([train_variable, train_label], axis=1)

    # Computing the target mean
    averages = temp.groupby(train_variable.name)[train_label.name].agg(['mean', 'count'])

    # Computing the smoothing
    smoothing = 1 / (1 + np.exp(-(averages['count'] - min_samples_leaf) / smoothing))

    # Calculating the prior before adding the smoothing
    prior = train_label.mean()

    # Adding the smoothing to the prior to get the posterior
    # Larger samples will take the average into account less
    averages[train_label.name] = prior * (1 - smoothing) + averages['mean'] * smoothing

    # Applying the averages to the training variable
    fitted_train_variable = pd.merge(
        train_variable.to_frame(train_variable.name),
        averages.reset_index().rename(columns={'index': train_label.name, train_label.name: 'average'}),
        on=train_variable.name, how='left')
    fitted_train_variable = fitted_train_variable['average'].rename(train_variable.name + '_mean').fillna(prior)
    fitted_train_variable.index = train_variable.index  # Restoring the index lost in pd.merge

    # Applying the averages to the testing variable
    fitted_test_variable = pd.merge(
        test_variable.to_frame(test_variable.name),
        averages.reset_index().rename(columns={'index': train_label.name, train_label.name: 'average'}),
        on=test_variable.name, how='left')
    fitted_test_variable = fitted_test_variable['average'].rename(test_variable.name + '_mean').fillna(prior)
    fitted_test_variable.index = fitted_test_variable.index  # Restoring the index lost in pd.merge

    # Adding the noise if there is any
    if noise_level != 0:
        fitted_train_variable = add_noise(fitted_train_variable, noise_level)
        fitted_test_variable = add_noise(fitted_test_variable, noise_level)
    return fitted_train_variable, fitted_test_variable


"""
Discrete logarithm algorithms
"""
from sage.all import *

def discrete_log_brute(gen, alpha):
    """
    Brute force discrete logarithm computing.

    Return a discrete logarithm of alpha using gen as a generator of the finite field.

    Parameters
    ----------
    gen : Generator of the finite field.
    alpha : Element of the field.

    Returns
    -------
    A discrete logarithm of alpha.
    """

    base_field = gen.parent()

    if not gen.parent() == alpha.parent():
        raise ValueError("Both elements must belong to the same finite field")

    if not base_field.is_field() or not base_field.is_finite():
        raise ValueError("The base field must be a finite field")

    order = base_field.order()
    b = base_field.one()
    i = 0
    while b != alpha and i < order:
        b = gen * b
        i += 1

    if i == order:
        raise ValueError("No discrete log of %s found to base %s using gen %s" % (alpha, order, gen))

    return i

def discrete_log_bsgs(gen, alpha):
    """
    Baby step / giant step discrete logarithm computing.

    Return a discrete logarithm of alpha using gen as a generator of the finite field.

    Parameters
    ----------
    gen : Generator of the finite field.
    alpha : Element of the field.

    Returns
    -------
    A discrete logarithm of alpha.
    """

    base_field = gen.parent()

    if not gen.parent() == alpha.parent():
        raise ValueError("Both elements must belong to the same finite field")

    if not base_field.is_field() or not base_field.is_finite():
        raise ValueError("The base field must be a finite field")

    order = base_field.order()
    m = isqrt(order) + 1
    table = dict()

    # Baby steps, fill the table
    b = base_field.one()
    for i in xrange(m):
        table[b] = i
        b = b * gen

    # Giant steps
    genp = gen ** -m
    b = alpha
    for i in xrange(m):
        j = table.get(b)
        if j is not None:
            return i*m + j
        b = b * genp

    raise ValueError("No discrete log of %s found to base %s using gen %s" % (alpha, order, gen))

def main():
    K = GF(137)
    print discrete_log_bsgs(K(7), K(11))  # Expected 45

    K = GF(3 ** 6, 'b')
    b = K.gen()
    a = b ** 210
    print discrete_log_bsgs(b, a)  # Expected 210

    try:
        K = GF(37)
        print discrete_log_bsgs(K(1), K(2))  # No discrete log of 2 found to base 37 using gen 1
    except ValueError as err:
        print err

if __name__ == '__main__':
    main()


"""Is the matrix idempotent."""
import numpy as np
from toqito.matrix_props import is_square

def is_idempotent(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-8) -> bool:
    r"""
    Check if matrix is the idempotent matrix [WikIdempotent]_.

    An *idempotent matrix* is a square matrix, which, when multiplied by itself, yields itself.
    That is, the matrix :math:`A` is idempotent if and only if :math:`A^2 = A`.

    Examples
    ==========

    The following is an example of a :math:`2 x 2` idempotent matrix:

    .. math::
        A = \begin{pmatrix}
            3 & -6 \\
            1 & -2
        \end{pmatrix}

    >>> from toqito.matrix_props import is_idempotent
    >>> import numpy as np
    >>> mat = np.array([[3, -6], [1, -2]])
    >>> is_idempotent(mat)

    Alternatively, the following matrix

    .. math::
        B = \begin{pmatrix}
                1 & 2 & 3 \\
                4 & 5 & 6 \\
                7 & 8 & 9
            \end{pmatrix}

    is not idempotent.

    >>> from toqito.matrix_props import is_idempotent
    >>> import numpy as np
    >>> mat = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    >>> is_idempotent(mat)
    False

    References
    ==========
    .. [WikIdempotent] Wikipedia: Idempotent matrix
        https://en.wikipedia.org/wiki/Idempotent_matrix

    :param mat: Matrix to check.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: Return :code:`True` if matrix is the idempotent matrix, and
            :code:`False` otherwise.
    """
    if not is_square(mat):
        return False
    return np.allclose(mat, mat @ mat, rtol=rtol, atol=atol)


#!/usr/bin/python
"""
This is a mixture of two gaussians example

In part the model was derived from:
http://stats.stackexchange.com/questions/46626/fitting-model-for-two-normal-distributions-in-pymc

The data for this example are the same as the EM example in the book:
"The Elements of Statistical Learning" (pp 272)

http://www-stat.stanford.edu/~tibs/ElemStatLearn/
"""

import shutil,os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mlab as mlab
import pymc as mc

__author__ = 'ajrichards'

## variables
book_mu1 = 4.62
book_mu2 = 1.06
book_var1 = 0.87
book_var2 = 0.77
book_pi = 0.546

x1 = np.array([-0.39,0.12,0.94,1.67,1.76,2.44,3.72,4.28,4.92,5.53])
x2 = np.array([ 0.06,0.48,1.01,1.68,1.80,3.25,4.12,4.60,5.28,6.22])
x  = np.hstack([x1,x2])

## the mixing parameter
pi = mc.Uniform('pi',lower=0,upper=1)
delta = mc.Bernoulli( "delta",p=pi,size=x.size) # produces 1 with proportion p.

## other model parameters
sigma = mc.Uniform("sigma", 0.0,0.5)
var1 = mc.Uniform('var1',0,1)
var2 = mc.Uniform('var2',0,1)
mode1 = mc.Normal("mode1",mu=0,tau=var1**-2)
mode2 = mc.Normal("mode2",mu=0,tau=var2**-2)

@mc.deterministic(plot=False)
def mix_model(delta=delta,mode1=mode1,mode2=mode2):
    return (1.0-delta)*mode1 + delta*mode2

obs = mc.Normal("obs",mu=mix_model,tau=sigma**-2,value=x,observed=True)
model = mc.Model({"mode1":mode1,"mode2":mode2,"sigma":sigma,"pi":pi,
                  "var1":var1,"var2":var2,"obs":obs})

M = mc.MCMC(model)
M.sample(100000,50000)

## relabel the estimates if necessary
if M.trace('mode1')[:].mean() > M.trace('mode2')[:].mean():
    est_mu1  = M.trace('mode1')[:].mean()
    est_var1 = M.trace('var1')[:].mean()
    est_mu2  = M.trace('mode2')[:].mean()
    est_var2 = M.trace('var2')[:].mean()
    est_pi   = M.trace('pi')[:].mean()
else:
    est_mu1  = M.trace('mode2')[:].mean()
    est_var1 = M.trace('var2')[:].mean()
    est_mu2  = M.trace('mode1')[:].mean()
    est_var2 = M.trace('var1')[:].mean()
    est_pi   = 1.0 - M.trace('pi')[:].mean()

print 'book,model'
print 'mu1', book_mu1,est_mu1
print 'mu2', book_mu2,est_mu2
print 'var1', book_var1,est_var1
print 'var2', book_var2,est_var2
print 'pi', book_pi, est_pi
print 'DIC',M.dic

## plot the distribution and the MCMC fit
fig = plt.figure(1)
ax = fig.add_subplot(111)
n,bins,patches = ax.hist(x,15,normed=1,facecolor='gray',alpha=0.75)

## add the book and best fit lines
p1 = mlab.normpdf(bins, book_mu1, np.sqrt(book_var1))
p2 = mlab.normpdf(bins, book_mu2, np.sqrt(book_var2))
l1 = plt.plot(bins, p1, 'r--', linewidth=1)
l2 = plt.plot(bins, p2, 'r--', linewidth=1)

p3 = mlab.normpdf(bins, est_mu1, np.sqrt(est_var1))
p4 = mlab.normpdf(bins, est_mu2, np.sqrt(est_var2))
l3 = plt.plot(bins, p3, 'k-', linewidth=1)
l4 = plt.plot(bins, p4, 'k-', linewidth=1)

plt.xlabel('x')
plt.ylabel('freq')
plt.ylim([0,0.8])

ax.legend((l1[0], l3[0]), ('Book Estimate', 'PyMC Estimate') )

## create pymc plots and save all
baseDir =  os.path.join("..",os.path.realpath(os.path.dirname(__file__)))
dirPath = os.path.join(baseDir,'plots')
if os.path.isdir(dirPath):
    shutil.rmtree(dirPath)
os.mkdir(dirPath)

plt.savefig(os.path.join(dirPath,'TwoComponentGauss.png'))
mc.Matplot.plot(M,path=dirPath)
print 'done.'


# coding: utf-8

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
import numpy as np
from matplotlib import cm
from sklearn.metrics import silhouette_samples
import pandas as pd
from scipy.spatial.distance import pdist, squareform
from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import dendrogram
# from scipy.cluster.hierarchy import set_link_color_palette
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN

# # Grouping objects by similarity using k-means

# ## K-means clustering using scikit-learn

X, y = make_blobs(n_samples=150,
                  n_features=2,
                  centers=3,
                  cluster_std=0.5,
                  shuffle=True,
                  random_state=0)

plt.scatter(X[:, 0], X[:, 1],
            c='white', marker='o', edgecolor='black', s=50)
plt.grid()
plt.tight_layout()
#plt.savefig('images/11_01.png', dpi=300)
plt.title("some random data")
plt.show()

km = KMeans(n_clusters=3,
            init='random',
            n_init=10,
            max_iter=300,
            tol=1e-04,
            random_state=0)

y_km = km.fit_predict(X)

plt.scatter(X[y_km == 0, 0],
            X[y_km == 0, 1],
            s=50, c='lightgreen',
            marker='s', edgecolor='black',
            label='cluster 1')
plt.scatter(X[y_km == 1, 0],
            X[y_km == 1, 1],
            s=50, c='orange',
            marker='o', edgecolor='black',
            label='cluster 2')
plt.scatter(X[y_km == 2, 0],
            X[y_km == 2, 1],
            s=50, c='lightblue',
            marker='v', edgecolor='black',
            label='cluster 3')
plt.scatter(km.cluster_centers_[:, 0],
            km.cluster_centers_[:, 1],
            s=250, marker='*',
            c='red', edgecolor='black',
            label='centroids')
plt.legend(scatterpoints=1)
plt.grid()
plt.tight_layout()
#plt.savefig('images/11_02.png', dpi=300)
plt.title("detect centroids with K-means")
plt.show()

# ## Using the elbow method to find the optimal number of clusters
# to check quality of clustering, one has to use intrinsic methods, such as the within-cluster SSE (sum of square errors) also called Distortion.
# This is available via the inertia_ attribute after fitting a Kmeans model.

print('Distortion: %.2f' % km.inertia_)

distortions = []
for i in range(1, 11):
    km = KMeans(n_clusters=i,
                init='k-means++',
                n_init=10,
                max_iter=300,
                random_state=0)
    km.fit(X)
    distortions.append(km.inertia_)
plt.plot(range(1, 11), distortions, marker='o')
plt.xlabel('Number of clusters')
plt.ylabel('Distortion')
plt.tight_layout()
#plt.savefig('images/11_03.png', dpi=300)
plt.title("check optimum no of clusters with Elbow method")
plt.show()


import numpy as np
import numpy.linalg as lin

MAX_ITERS = 20
EPSILON   = 1.0e-7

def h(theta, X, y):
    margins = y * X.dot(theta)

    return 1 / (1 + np.exp(-margins))

def J(theta, X, y):
    probs = h(theta, X, y)
    mm = probs.size

    return (-1 / mm) * np.sum(np.log(probs))

def gradJ(theta, X, y):
    probs = h(theta, X, -y) * y
    mm = probs.size

    return (-1 / mm) * X.T.dot(probs)

def hessJ(theta, X, y):
    d     = h(theta, X, y)
    probs = d * (1 - d)
    mm    = probs.size
    prob_vec = np.diag(probs)
    hessJ = (1 / mm) * (X.T.dot(prob_vec).dot(X))

    return hessJ

def logistic_regression(X, y, epsilon, max_iters):
    mm = X.shape[0]
    nn = X.shape[1]

    # The cost of the ith iteration of Newton-Raphson.
    cost = np.zeros(max_iters)
    theta = np.zeros(nn)

    for i in range(0, max_iters):
        cost[i] = J(theta, X, y)
        grad    = gradJ(theta, X, y)
        H       = hessJ(theta, X, y)
        Hinv    = lin.inv(H)
        theta   = theta - Hinv.dot(grad)

    return (theta, cost)


import numpy

def mandelbrot(width, height, x_range, y_range, n_iterations, c=None):
    """Computes heightmap for a mandelbrot or julia set fractal

    Each iteration, the new value of a pixel is calculated as z = z^2 + c, where for a mandelbrot set (default)
    c is a function of the position in the complex plane (c = x + iy) or a optionally for a julia set, c is a constant

    :parameter width: Integer width of the final fractal image in pixels
    :type width: int

    :parameter height: Integer height of the final fractal image in pixels
    :type height: int

    :parameter x_range: The range of x [min, max] for which the fractal will be drawn
    :type x_range: Union[list of float, numpy.ndarray]

    :parameter y_range: The range of y [min, max] for which the fractal will be drawn
    :type y_range: Union[list of float, numpy.ndarray]

    :parameter n_iterations: the number of iterations limit used to compute the fractal
    :type n_iterations: int

    :parameter c: Optional 2-tuple (or other iterable) containing real and complex parts of constant coefficient c.
    Giving this argument will result in creation of a julia set, not the default mandelbrot set
    :type c: Union[None, (float, float)]

    :return: x, y, z values of pixel locations in the x, y complex plane and a corresponding heightmap z, with which
    you can lot a fancy looking 3d fractal
    :rtype: (numpy.ndarray, numpy.ndarray, numpy.ndarray)
    """

    # Create a linearly spaced 2d grid
    [x, y] = numpy.meshgrid(
        numpy.linspace(x_range[0], x_range[1], width), numpy.linspace(y_range[0], y_range[1], height)
    )

    # Preallocate output array
    z = numpy.zeros((height, width))

    # Simple loop to render the fractal set. This is not efficient python and would be vectorised in production, but the
    # purpose here is just to provide a simple demo.
    for idx, a in numpy.ndenumerate(x):

        # Get default constant c (Mandelbrot sets use spatial coordinates as constants)
        if c is None:
            b = y[idx]
            c = (a, b)

        x_old = 0.0
        y_old = 0.0
        k = 1
        while (k <= n_iterations) and ((x_old ** 2.0 + y_old ** 2.0) < 4.0):
            x_new = x_old ** 2.0 - y_old ** 2.0 + c[0]
            y_new = 2.0 * x_old * y_old + c[1]
            x_old = x_new
            y_old = y_new
            k += 1

        z[idx] = k

    return x, y, z


import sympy
from sympy.vector import CoordSys3D, gradient, curl, divergence
from argparse import ArgumentParser

aesthetic = {'cartesian':sympy.symbols('x y z'),
             'cylindrical':sympy.symbols('r phi z'),
             'spherical':sympy.symbols('r theta phi')}

def make_eqns(geometry='cartesian'):

    S = CoordSys3D('S',
                   transformation=geometry,
                   variable_names='xyz')
    t = sympy.Symbol('t')
    mu = sympy.symbols('mu', positive=True)
    args = (t,S.x,S.y,S.z)
    v_1, v_2, v_3, rho, p = [f(*args) for f
                          in sympy.symbols('v_{1:4} rho p', cls=sympy.Function)]
    v = sum((v_n*vec
             for v_n, vec
             in zip((v_1, v_2, v_3),(S.i,S.j,S.k))),
            0*S.i)
    res = v.diff(t)
    res += gradient(v.dot(v)/2) - (v^curl(v))
    res += gradient(p)/rho
    res -= mu*(gradient(divergence(v)) - curl(curl(v)))
    res = res.subs({old_var:new_var for old_var, new_var in zip((S.x, S.y, S.z), aesthetic[geometry])})
    return {'equations':res,
            'velocity':v,
            'pressure':p,
            'density':rho,
            'mu':mu,
            'coordinate system':S,
            'time':t,
            'coordinates vars':aesthetic[geometry]}

def get_input():

    parser = ArgumentParser()
    parser.add_argument('func_name',
                        help='name of function',
                        type=str)
    args = parser.parse_args()
    return args.func_name

def main():

    func_name = get_input()
    assert(func_name != 'main')
    sympy.pprint(eval(f'{func_name}()'))

if __name__ == '__main__':

    main()


import math
import numpy as np

def signed_angle(a1, a2):
	'''calculates the signed difference between 2 angles
	positive, counter-clockwise, negative, clockwise '''
	sign = [1., -1.]
	dcc = (a2- a1) % (2*math.pi)
	dc = (a1 - a2) % (2*math.pi)
	d = min(dc, dcc)
	arg = np.argmin([dc, dcc])
	signed_d = sign[arg]*d
	return signed_d

if __name__ == '__main__':
	a1 = math.pi/4
	a2 = -3.

	print signed_angle(a1, a2)

	print signed_angle(a2, a1)


import numpy as np
import matplotlib.pyplot as plt
from tabulate import tabulate
from RidgeRegression import RidgeClosedForm, RidgeGradient
from LinearRegression import LinearRegression

a = np.array([5,2,7])
X = np.array([1,2,3])   + np.array([2,1,2]) *  np.random.rand(100 , 3)
y = 4 + X.dot(a.T)[:, np.newaxis] + np.random.randn(100, 1)

y = [row[0] for row in y]

alpha = 0.01

model = LinearRegression(alpha, gradient_type="Full")
model.fit(X, y)
predicts = model.predict(X)

model = LinearRegression(alpha, gradient_type="Stochastic")
model.fit(X, y)
predicts2 = model.predict(X)

model = LinearRegression(alpha, gradient_type="Mini", batch_size=10)
model.fit(X, y)
predicts3 = model.predict(X)

model = RidgeClosedForm(alpha, a)
model.fit(X, y)
predicts4 = model.predict(X)

model = RidgeGradient(alpha, gradient_type = "Full")
model.fit(X, y)
predicts5 = model.predict(X)

model = RidgeGradient(alpha, gradient_type="Stochastic")
model.fit(X, y)
predicts6 = model.predict(X)

model = RidgeGradient(alpha, gradient_type="Mini", batch_size=10)
model.fit(X, y)
predicts7 = model.predict(X)

headers = ["Real", "Linear Gra Full", "Linear Gra Sto", "Linear Gra Mini", "Ridge Closed", "Ridge Gra Full", "Ridge Gra Sto", "Ridge Gra Mini"]
results = list()
for i in range(len(y)):
    results.append( [float(y[i]), float(predicts[i]), float(predicts2[i]), float(predicts3[i]), float(predicts4[i]), float(predicts5[i]), float(predicts6[i]), float(predicts7[i]) ]   )
print(tabulate(results, headers, tablefmt="github"))


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

pi = np.pi

def getEstimateOfPi(NumSamples,InitialSeed=-1):
    if InitialSeed >= 0:
        np.random.seed(InitialSeed)
    else:
        np.random.seed()
    estimates=[]
    inside = 0
    total = 0
    for i in range(0,NumSamples):
        x = np.random.uniform(-1.0,1.0)
        y = np.random.uniform(-1.0,1.0)
        if x**2+y**2 <= 1.0**2:
            inside += 1
        total += 1
        current_estimate = 4 * np.float(inside)/np.float(total)
        estimates.append( current_estimate )
    estimates = np.array(estimates)
    return np.copy(estimates)
#----------------------------------

def getEstimateOfPiIntegral(NumSamples,InitialSeed=-1):
    if InitialSeed >= 0:
        np.random.seed(InitialSeed)
    else:
        np.random.seed()
    estimates=[]
    current_estimate = 0.0
    for i in range(0,NumSamples):
        x = np.random.uniform(0,1.0)
        value = 4.0 * np.sqrt(1-x**2)
        current_estimate += (value - current_estimate)/(float(i)+1.0)
        estimates.append( current_estimate )
    estimates = np.array(estimates)
    return np.copy(estimates)
#----------------------------------

def getEstimateOfPiMarkovChain(NumSamples,InitialSeed=-1,MaxStepSize=0.1):
    if InitialSeed >= 0:
        np.random.seed(InitialSeed)
    else:
        np.random.seed()
    estimates=[]
    inside = 0
    total = 0
    accepted = 0
    x = np.random.uniform(-1.0,1.0)
    y = np.random.uniform(-1.0,1.0)
    for i in range(0,NumSamples):
        dx = np.random.uniform(-MaxStepSize,MaxStepSize)
        dy = np.random.uniform(-MaxStepSize,MaxStepSize)
        x_new = x + dx
        y_new = x + dx
        if abs(x_new) < 1.0 and abs(y_new) < 1.0:
            x = x_new
            y = y_new
            accepted += 1
        if x**2+y**2 <= 1.0**2:
            inside += 1
        total += 1
        current_estimate = 4 * np.float(inside)/np.float(total)
        estimates.append( current_estimate )
    estimates = np.array(estimates)
    acceptance_ratio = np.float(accepted)/np.float(total)
    return np.copy(estimates), acceptance_ratio
#----------------------------------

def runManyRuns(NumSamples,NumRuns=50,function=getEstimateOfPi):
    AllEstimates = []
    for i in range(NumRuns):
        estimate = function(NumSamples)
        AllEstimates.append(estimate)
    AllEstimates = np.array(AllEstimates)
    N = np.arange(1,NumSamples+1)
    SqrtN = np.sqrt(N)
    MeanN = []
    StddevN = []
    for j in range(NumSamples):
        mean = np.mean(AllEstimates[:,j])
        stddev = np.std(AllEstimates[:,j])
        MeanN.append(mean)
        StddevN.append(stddev)
    MeanN = np.array(MeanN)
    StddevN = np.array(StddevN)
    Data = (
        np.copy(N),
        np.copy(SqrtN),
        np.copy(MeanN),
        np.copy(StddevN),
        np.copy(AllEstimates)
    )
    return Data
#----------------------------------

def plotData(Data, FigNumber=1, FigFilenamePrefix=None):
    FigFormat='png'
    exact_value = pi
    #
    N = Data[0]
    SqrtN = Data[1]
    MeanN = Data[2]
    StddevN = Data[3]
    AllEstimates = Data[4]
    NumSamples = AllEstimates[0,:].size
    NumRuns = AllEstimates[:,0].size
    #
    FigOffset1 = 0.3
    plt.figure(FigNumber)
    plt.xlim(0, NumSamples)
    plt.ylim(exact_value-FigOffset1, exact_value+FigOffset1)
    plt.xticks( np.linspace(0,NumSamples,6, endpoint=True) )
    plt.xlabel('N')
    plt.ylabel('Estimate of pi')
    # plt.yticks(
    #    [pi-1.2,pi-1.0, pi-0.8, pi-0.6, pi-0.4, pi-0.2, pi-0.0, pi+0.2, pi+0.4, pi+0.6, pi+0.8, pi+1.0, pi+1.2],
    #    [r'$\pi-1.2$', r'', r'$\pi-0.8$', r'', r'$\pi-0.4$', r'', r'$\pi$', r'',
    #     r'$\pi+0.4$', r'', r'$\pi+0.8$', r'', r'$\pi+1.2$'],
    #    fontsize = 20)
    for i in range(NumRuns):
        plt.plot( AllEstimates[i,:] )
    plt.plot([0, NumSamples], [exact_value, exact_value], 'k--', linewidth=4)
    #
    if FigFilenamePrefix is not None: plt.savefig(FigFilenamePrefix+'.1.'+FigFormat, transparent=True, format=FigFormat,dpi=300)
    #
    plt.figure(FigNumber+1)
    plt.plot(N,StddevN, 'b', linewidth=4)
    plt.xlabel('N')
    plt.ylabel('StdDev')
    if FigFilenamePrefix is not None: plt.savefig(FigFilenamePrefix+'.2.'+FigFormat, transparent=True, format=FigFormat,dpi=300)
    #
    plt.figure(FigNumber+2)
    plt.plot(N,StddevN, 'b', linewidth=4)
    plt.xlabel('N')
    plt.ylabel('StdDev')
    plt.ylim(0,StddevN[100])
    if FigFilenamePrefix is not None: plt.savefig(FigFilenamePrefix+'.3.'+FigFormat, transparent=True, format=FigFormat,dpi=300)
    #
    plt.figure(FigNumber+3)
    plt.plot(N,SqrtN*StddevN, 'b', linewidth=4)
    plt.xlabel('N')
    plt.ylabel('StdDev*sqrt(N)')
    if FigFilenamePrefix is not None: plt.savefig(FigFilenamePrefix+'.4.'+FigFormat, transparent=True, format=FigFormat,dpi=300)
    #
    plt.figure(FigNumber+4)
    plt.plot(1.0/sqrtN,StddevN, 'b', linewidth=4)
    plt.xlabel('1/sqrt(N)')
    plt.ylabel('StdDev')
    if FigFilenamePrefix is not None: plt.savefig(FigFilenamePrefix+'.5.'+FigFormat, transparent=True, format=FigFormat,dpi=300)
    plt.show()

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

(N, sqrtN, MeanN, StddevN, AllEstimates) = runManyRuns(10000, NumRuns=1, function=getEstimateOfPiIntegral)
#(N, sqrtN, MeanN, StddevN, AllEstimates) = runManyRuns(1000, NumRuns=1000, function=getEstimateOfPi)
Data = (N, sqrtN, MeanN, StddevN, AllEstimates)
plotData(Data,FigFilenamePrefix='figure')


'''
Utility functions for the package.
'''
import numpy as np

def scale_01(x):
    '''Scales array to 0-1.

    Args:
        x (iter): 1d array of float

    Returns:
        np.array: scaled 1d array

    Example:
        >>> scale_01([1, 2, 3, 5]).tolist()
        [0.0, 0.25, 0.5, 1.0]

    '''
    arr_min = min(x)
    x = np.array(x) - float(arr_min)
    arr_max = max(x)
    return x / float(arr_max)

def abs_distance(x, y):
    '''Returns absolute distance.

    Args:
        x (float): input 1
        y (float): input 2

    Returns:
        float: \|x-y\|

    Example:
        >>> abs_distance(5, 7)
        2.0
        >>> abs_distance(4, 1)
        3.0

    '''
    return float(abs(x - y))

def euclidean_distance(x, y):
    '''Returns Euclidean distance.

    Args:
        x (float or iter): input 1
        y (float or iter): input 2

    Returns:
        float: Euclidean distance

    References:
        https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html

    Examples:
        >>> x, y = 1, 2
        >>> euclidean_distance(x, y)
        1.0
        >>> x, y = [1, 2], [4, 6]
        >>> euclidean_distance(x, y)
        5.0

    '''
    return np.linalg.norm(np.array(x) - np.array(y))


#!/usr/bin/env python
# encoding: utf-8
'''
Common math functions
'''

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

# all_num(pd.Series([1,2]))

def scalar(x,y):
    # scalar product of 2 numpy vectors
    return np.dot(x,y)

def cov(x,y):
    # covariance between two numpy vectors
    return np.cov(x,y)[0,1]

def corr(x,y):
    # correlation between two numpy vectors
    c=np.cov(x,y)
    return c[0,1]/np.sqrt( c[0,0]*c[1,1] )

def is_semipos_def(X):
    '''is_semipos_def(X) checks if matrix x is positive semidefinite or not
    Input: matrix x
    Output: True if x is positive semidefinite, False otherwise.
    Example: is_semipos_def([[-1,10],[0,1]])'''
    return np.all(np.linalg.eigvals(X) >= 0)

def is_convex(fun, D, M=100, trials_count=10000):
    '''Check convexity of function fun, depending on D-dimensional numpy.array by making trials trial_count times taking x_i from uniform(-M,M,size=D)'''
    for n in range(trials_count):
        x1=np.random.uniform(-M,M,size=D)
        x2=np.random.uniform(-M,M,size=D)
        a=np.random.uniform()

        if not fun(a*x1+(1-a)*x2) <= a*fun(x1)+(1-a)*fun(x2):
            print('Function convexity: False')
            print('fun(a*x1+(1-a)*x2)={}, a*fun(x1)+(1-a)*fun(x2)={}'.format( fun(a*x1+(1-a)*x2), a*fun(x1)+(1-a)*fun(x2) ))
            print('x1={}, x2={}'.format( x1, x2 ))
            return False

    print('Function convexity: True')
    return True

#print is_convex(lambda x:x[0]**2+x[1]**2-x[2]**2,3,)


import numpy as np

def quick_select(S, k):
    n = len(S)
    if n == 1:
        return S[0]
    pivot = np.random.choice(S)
    L = [e for e in S if e < pivot]
    E = [e for e in S if e == pivot]
    G = [e for e in S if e > pivot]

    if k <= len(L):
        return quick_select(L, k)
    elif k <= len(L) + len(E):
        return pivot
    else:
        j = k - len(L) - len(E)
        return quick_select(G, j)


# -*- coding: utf-8 -*-
"""
Created on Tue Sep 12 20:39:09 2017

"""
from __future__ import print_function
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_boston
from scipy.misc import logsumexp
np.random.seed(0)

# load boston housing prices dataset
boston = load_boston()
x = boston['data']
N = x.shape[0]
x = np.concatenate((np.ones((506,1)),x),axis=1) #add constant one feature - no bias needed
d = x.shape[1]
y = boston['target']

idx = np.random.permutation(range(N))

#helper function
def l2(A,B):
    '''
    Input: A is a Nxd matrix
           B is a Mxd matirx
    Output: dist is a NxM matrix where dist[i,j] is the square norm between A[i,:] and B[j,:]
    i.e. dist[i,j] = ||A[i,:]-B[j,:]||^2
    '''
    A_norm = (A**2).sum(axis=1).reshape(A.shape[0],1)
    B_norm = (B**2).sum(axis=1).reshape(1,B.shape[0])
    dist = A_norm+B_norm-2*A.dot(B.transpose())
    return dist

#helper function
def run_on_fold(x_test, y_test, x_train, y_train, taus):
    '''
    Input: x_test is the N_test x d design matrix
           y_test is the N_test x 1 targets vector
           x_train is the N_train x d design matrix
           y_train is the N_train x 1 targets vector
           taus is a vector of tau values to evaluate
    output: losses a vector of average losses one for each tau value
    '''
    N_test = x_test.shape[0]
    losses = np.zeros(taus.shape)
    for j,tau in enumerate(taus):
        predictions =  np.array([LRLS(x_test[i,:].reshape(1,d),x_train,y_train, tau) \
                        for i in range(N_test)])
        losses[j] = ((predictions-y_test)**2).mean()
    return losses

#to implement
def LRLS(test_datum,x_train,y_train, tau,lam=1e-5):
    '''
    Input: test_datum is a dx1 test vector
           x_train is the N_train x d design matrix
           y_train is the N_train x 1 targets vector
           tau is the local reweighting parameter
           lam is the regularization parameter
    output is y_hat the prediction on test_datum
    '''
    ## TODO
    numerator = (np.exp(-l2(test_datum, x_train)/(2*(tau**2))))[0]
    denominator = np.exp(logsumexp(-l2(test_datum, x_train)/(2*(tau**2))))
    a_weight = np.diag(np.divide(numerator , denominator))
    #w*
    left_mat = np.matmul(np.matmul(x_train.transpose(), a_weight), x_train) + lam*np.identity(d)
    right_mat = np.matmul(np.matmul(x_train.transpose(), a_weight), y_train)
    w_star = np.linalg.solve(left_mat, right_mat)
    ## TODO
    y_hat = np.matmul(test_datum, w_star)
    return y_hat[0]

def run_k_fold(x,y,taus,k):
    '''
    Input: x is the N x d design matrix
           y is the N x 1 targets vector
           taus is a vector of tau values to evaluate
           K in the number of folds
    output is losses a vector of k-fold cross validation losses one for each tau value
    '''
    ## TODO
    #split data to k pieces
    shuffled_indice = np.arange(N)
    np.random.shuffle(shuffled_indice)
    loss_matrix_on_taus = []
    test_set_index = []
    for i in range(k):
        test_set_index = shuffled_indice[int(i*(N/k)): int((i+1)*(N/k))]
        train_set_index = list(range(N))
        for indice_left in test_set_index:
            train_set_index.remove(indice_left)
        x_train = np.array([x[i] for i in train_set_index])
        y_train = np.array([y[i] for i in train_set_index])
        x_test = np.array([x[i] for i in test_set_index])
        y_test = np.array([y[i] for i in test_set_index])
        loss_matrix_on_taus.append(run_on_fold(x_test, y_test, x_train, y_train, taus))
    ## TODO
    #return a vector of k-fold cross validation losses one for each tau value
    print
    return np.mean(loss_matrix_on_taus, axis=0)

if __name__ == "__main__":
    # In this excersice we fixed lambda (hard coded to 1e-5) and only set tau value. Feel free to play with lambda as well if you wish
    taus = np.logspace(1.0,3,200)
    losses = run_k_fold(x,y,taus,k=5)
    plt.plot(losses)
    plt.show()
    print("min loss = {}".format(losses.min()))


"""Hadamard matrix."""
import numpy as np

def hadamard(n_param: int = 1) -> np.ndarray:
    r"""
    Produce a :code:`2^{n_param}` dimensional Hadamard matrix [WikHad]_.

    The standard Hadamard matrix that is often used in quantum information as a
    two-qubit quantum gate is defined as

    .. math::
        H_1 = \frac{1}{\sqrt{2}} \begin{pmatrix}
                                    1 & 1 \\
                                    1 & -1
                                 \end{pmatrix}

    In general, the Hadamard matrix of dimension :code:`2^{n_param}` may be
    defined as

    .. math::
        \left( H_n \right)_{i, j} = \frac{1}{2^{\frac{n}{2}}}
        \left(-1\right)^{i \dot j}

    Examples
    ==========

    The standard 2-qubit Hadamard matrix can be generated in :code:`toqito` as

    >>> from toqito.matrices import hadamard
    >>> hadamard(1)
    [[ 0.70710678  0.70710678]
     [ 0.70710678 -0.70710678]]

    References
    ==========
    .. [WikHad] Wikipedia: Hadamard transform
        https://en.wikipedia.org/wiki/Hadamard_transform

    :param n_param: A non-negative integer (default = 1).
    :return: The Hadamard matrix of dimension :code:`2^{n_param}`.
    """
    return 2 ** (-n_param / 2) * np.array(
        [
            [(-1) ** _hamming_distance(i & j) for i in range(2 ** n_param)]
            for j in range(2 ** n_param)
        ]
    )

def _hamming_distance(x_param: int) -> int:
    """
    Calculate the bit-wise Hamming distance of :code:`x_param` from 0.

    The Hamming distance is the number 1s in the integer :code:`x_param`.

    :param x_param: A non-negative integer.
    :return: The hamming distance of :code:`x_param` from 0.
    """
    tot = 0
    while x_param:
        tot += 1
        x_param &= x_param - 1
    return tot


# Poisson Disk Sampling Algorithm
#
# by Connor Johnson,
# http://connor-johnson.com/2015/04/08/poisson-disk-sampling/
#
# based on materials of PyDataNYC2014
# http://nbviewer.jupyter.org/github/HyperionAnalytics/PyDataNYC2014/blob/master/poisson_disc_sampling.ipynb

import scipy
import random

class pds:
    def __init__( self, w, h, r, n ):
        # w and h are the width and height of the field
        self.w = w
        self.h = h
        # n is the number of test points
        self.n = n
        self.r2 = r**2.0
        self.A = 3.0*self.r2
        # cs is the cell size
        self.cs = r / scipy.sqrt(2)
        # gw and gh are the number of grid cells
        self.gw = int( scipy.ceil( self.w/self.cs ) )
        self.gh = int( scipy.ceil( self.h/self.cs ) )
        # create a grid and a queue
        self.grid = [ None ] * self.gw * self.gh
        self.queue = list()
        # set the queue size and sample size to zero
        self.qs, self.ss = 0, 0

    def distance( self, x, y ):
        # find where (x,y) sits in the grid
        x_idx = int( x/self.cs )
        y_idx = int( y/self.cs )
        # determine a neighborhood of cells around (x,y)
        x0 = max( x_idx-2, 0 )
        y0 = max( y_idx-2, 0 )
        x1 = max( x_idx-3, self.gw )
        y1 = max( y_idx-3, self.gh )
        # search around (x,y)
        for y_idx in range( y0, y1 ):
            for x_idx in range( x0, x1 ):
                step = y_idx*self.gw + x_idx
                # if the sample point exists on the grid
                if self.grid[ step ]:
                    s = self.grid[ step ]
                    dx = ( s[0] - x )**2.0
                    dy = ( s[1] - y )**2.0
                    # and it is too close
                    if dx + dy < self.r2:
                        # then barf
                        return False
        return True

    def set_point( self, x, y ):
        s = [ x, y ]
        self.queue.append( s )
        # find where (x,y) sits in the grid
        x_idx = int( x/self.cs )
        y_idx = int( y/self.cs )
        step = self.gw*y_idx + x_idx
        self.grid[ step ] = s
        self.qs += 1
        self.ss += 1
        return s

    def rvs( self ):
        if self.ss == 0:
            x = random.random() * self.w
            y = random.random() * self.h
            self.set_point( x, y )
        while self.qs:
            x_idx = int( random.random() * self.qs )
            s = self.queue[ x_idx ]
            for y_idx in range( self.n ):
                a = 2 * scipy.pi * random.random()
                b = scipy.sqrt( self.A * random.random() + self.r2 )
                x = s[0] + b*scipy.cos( a )
                y = s[1] + b*scipy.sin( a )
                if( x >= 0 )and( x < self.w ):
                    if( y >= 0 )and( y < self.h ):
                        if( self.distance( x, y ) ):
                            self.set_point( x, y )
            del self.queue[x_idx]
            self.qs -= 1
        sample = list( filter( None, self.grid ) )
        sample = scipy.asfarray( sample )
        return sample


import torch
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from sklearn.decomposition import PCA

def PCA_sklearn(X):
	"""Обчислення PCA за допомогою sklearn"""
	pca = PCA(n_components=2)
	pca.fit(X)

	print('sklearn')
	print(pca.components_)
	print(pca.explained_variance_)
	print(pca.mean_)
	print()

	return {'components': pca.components_,
			'explained_variance': pca.explained_variance_,
			'mean': pca.mean_}

def PCA_pytorch(X, k=2, center=True):
	"""Обчислення PCA за допомогою PyTorch"""
	n = X.size()[0]
	ones = torch.ones(n).view([n, 1])
	h = ((1 / n) * torch.mm(ones, ones.t())) if center else torch.zeros(n * n).view([n, n])
	H = torch.eye(n) - h
	X_center = torch.mm(H.double(), X.double())
	u, s, v = torch.svd(X_center)
	v[1:]=v[1:]*-1
	components = v[:k].numpy()
	explained_variance = torch.mul(s[:k], s[:k]) / (n - 1)
	explained_variance = explained_variance.numpy()
	mean = [X[:, 0].mean().numpy().item(), X[:, 1].mean().numpy().item()]

	print('PyTorch')
	print(components)
	print(explained_variance)
	print(mean)
	print()

	return {'components': components,
			'explained_variance': explained_variance,
			'mean': mean}

def draw_vector(v0, v1, ax=None):
	ax = ax or plt.gca()
	arrowprops = dict(arrowstyle='->', linewidth=2, shrinkA=0, shrinkB=0)
	ax.annotate('', v1, v0, arrowprops=arrowprops)

def plot_data(X, explained_variance, components, mean):
	"""Візуалізація PCA"""
	plt.scatter(X['total_bill'], X['size'], alpha=0.2)
	for length, vector in zip(explained_variance, components):
		v = vector * 3 * np.sqrt(length)
		draw_vector(mean, mean + v)
	plt.xlabel('total_bill')
	plt.ylabel('size')
	plt.axis('equal')
	plt.show()

# ініціаліція датасету
tips = sns.load_dataset("tips")  # 'total_bill', 'smoker', 'time', 'size'
X = tips[['total_bill', 'size']]

# обчислення PCA за допомогою sklearn
res_sklearn = PCA_sklearn(X)
plot_data(X, res_sklearn['explained_variance'], res_sklearn['components'], res_sklearn['mean'])

# обчислення PCA за допомогою PyTorch
res_pytorch = PCA_pytorch(torch.tensor(X.values))
plot_data(X, res_pytorch['explained_variance'], res_pytorch['components'], res_pytorch['mean'])

# знешумлення
noisy = np.random.normal(X, 0.25)
plt.scatter(noisy[:, 0], noisy[:, 1], marker='>', c='g', alpha=0.5)
pca = PCA(1)
pca = pca.fit(noisy)
components = pca.transform(noisy)
filtered = pca.inverse_transform(components)
plt.scatter(filtered[:, 0], filtered[:, 1], c='r', alpha=0.5)
plt.show()


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

np.random.seed(0)

plt.style.use('seaborn')

minx, maxx = -2, 2
miny, maxy = -3, 3
numstations = 30
stn_locs=[]
xvals = minx+(maxx-minx)*np.random.rand(numstations)
yvals = miny+(maxy-miny)*np.random.rand(numstations)

for num in range(numstations):
    stn_locs.append([xvals[num],yvals[num],0])

eq_loc = [2,2,-2]
vel = 6 #kmps
origintime = 0

def calc_arrival_time(eq_loc, stnx, stny, stnz, vel, origintime):
    eqx, eqy, eqz = eq_loc
    dist = np.sqrt((eqx - stnx)**2 + (eqy - stny)**2 + (eqz - stnz)**2)
    arr = dist/vel + origintime
    return arr

d_obs = []
noise_level_data = 0.001
for stnx, stny, stnz in stn_locs:
    arr = calc_arrival_time(eq_loc, stnx, stny, stnz, vel, origintime)
    sign = np.random.choice([-1,1])
    d_obs.append(arr+sign*noise_level_data*arr)

d_obs = np.array(d_obs)

def get_rand_number(min_value, max_value):
    range_vals = max_value - min_value
    choice = np.random.uniform(0,1)
    return min_value + range_vals*choice

## Monte Carlo

num_iterations = 100000
inv_model = []
squared_error0 = 100000

mineqx, maxeqx = -3, 3
mineqy, maxeqy = -3, 3
mineqz, maxeqz = 0, -3
gen_num = []
lse = []
for i in range(num_iterations):
    eqx0 = get_rand_number(mineqx, maxeqx)
    eqy0 = get_rand_number(mineqy, maxeqy)
    eqz0 = get_rand_number(mineqz, maxeqz)
    vel0 = get_rand_number(5, 7)
    origintime0 = get_rand_number(-1, 1)
    d_pre = []
    for stnx, stny, stnz in stn_locs:
        d_pre.append(calc_arrival_time([eqx0, eqy0, eqz0], stnx, stny, stnz, vel0, origintime0))
    d_pre = np.array(d_pre)

    squared_error = np.sum((d_obs-d_pre)**2)
    if squared_error < squared_error0:
        print(i,squared_error)
        gen_num.append(i)
        lse.append(squared_error)
        m0 =  np.array([eqx0, eqy0, eqz0, vel0, origintime0])
        if np.abs(squared_error-squared_error0)<0.001:
            print("Terminated based on tol. value",np.abs(squared_error-squared_error0))
            break
        squared_error0 = squared_error
        inv_model = m0

print("{:.2f} {:.2f} {:.2f} {:.2f} {:.2f}".format(inv_model[0],inv_model[1],inv_model[2],inv_model[3],inv_model[4]))

fig, ax = plt.subplots(1,1,figsize=(5,5))
ax.loglog(gen_num,lse, 'ko--')
ax.set_xlabel('Generations')
ax.set_ylabel('Least-squares error')
plt.savefig('iterations.png',bbox_inches='tight',dpi=300)
plt.close('all')

## to create the surface
X = np.linspace(-3, 3, 200)
Y = np.linspace(-3, 3, 200)
X, Y = np.meshgrid(X, Y)
Z = (X**2 + Y**2)*0

fig = plt.figure()
ax = plt.axes(projection='3d')
# plot stations
ax.scatter([x[0] for x in stn_locs],[x[1] for x in stn_locs],[x[2] for x in stn_locs],c='b',marker='^',s=50)
# plot surface
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet,linewidth=0, antialiased=False,alpha=0.1)
# plot actual EQ
ax.scatter(eq_loc[0],eq_loc[1],eq_loc[2],c='r',marker='*',s=100,label='Actual EQ location')
ax.scatter(inv_model[0],inv_model[1],inv_model[2],c='k',marker='*',s=100,label='Inverted EQ location')
plt.title("Inverted model EQ loc: ({:.2f},{:.2f},{:.2f}),\nvel: {:.2f} and origin time: {:.2f}\nsq_error: {:.2f}".format(inv_model[0],inv_model[1],inv_model[2],inv_model[3],inv_model[4],squared_error),fontsize=8)
ax.set_xlim([-3,3])
ax.set_ylim([-3,3])
ax.set_zlim(-3,0.1)
plt.legend()

plt.savefig('Earthquake_loc_monte_carlo.png',bbox_inches='tight',dpi=300)
plt.close('all')


import numpy as np

def get_rotationMatrix_from_vectors(u, v):
    """
    Create a rotation matrix that rotates the space from a 3D vector `u` to a 3D vector `v`

    :param u: Orign vector `np.array (1,3)`.
    :param v: Destiny vector `np.array (1,3)`.

    :returns: Rotation matrix `np.array (3, 3)`

    ---
    """

    # Lets find a vector which is ortogonal to both u and v
    w = np.cross(u, v)

    # This orthogonal vector w has some interesting proprieties
    # |w| = sin of the required rotation
    # dot product of w and goal_normal_plane is the cos of the angle
    c = np.dot(u, v)
    s = np.linalg.norm(w)

    # Now, we compute rotation matrix from rodrigues formula
    # https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
    # https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d

    # We calculate the skew symetric matrix of the ort_vec
    Sx = np.asarray([[0, -w[2], w[1]], [w[2], 0, -w[0]], [-w[1], w[0], 0]])
    R = np.eye(3) + Sx + Sx.dot(Sx) * ((1 - c) / (s ** 2))
    return R

def rodrigues_rot(P, n0, n1):
    """
    Rotate a set of point between two normal vectors using Rodrigues' formula.

    :param P: Set of points `np.array (N,3)`.
    :param n0: Orign vector `np.array (1,3)`.
    :param n1: Destiny vector `np.array (1,3)`.

    :returns: Set of points P, but rotated `np.array (N, 3)`

    ---
    """

    # If P is only 1d array (coords of single point), fix it to be matrix
    P = np.asarray(P)
    if P.ndim == 1:
        P = P[np.newaxis, :]

    # Get vector of rotation k and angle theta
    n0 = n0 / np.linalg.norm(n0)
    n1 = n1 / np.linalg.norm(n1)
    k = np.cross(n0, n1)
    P_rot = np.zeros((len(P), 3))
    if np.linalg.norm(k) != 0:
        k = k / np.linalg.norm(k)
        theta = np.arccos(np.dot(n0, n1))

        # Compute rotated points
        for i in range(len(P)):
            P_rot[i] = (
                P[i] * np.cos(theta) + np.cross(k, P[i]) * np.sin(theta) + k * np.dot(k, P[i]) * (1 - np.cos(theta))
            )
    else:
        P_rot = P
    return P_rot