import numpy as np

class Loss(object):
    """Loss 计算损失
    """
    def loss(self, predicted, actual):
        raise NotImplementedError

    def grad(self, predicted, actual):
        raise NotImplementedError

class MAE(Loss):
    """平均绝对误差
    """
    def loss(self, predicted, actual):
        # m = predicted.shape[0]
        # return np.sum(np.abs(predicted - actual)) / m
        return np.mean(np.abs(predicted - actual))

    def grad(self, predicted, actual):
        m = predicted.shape[0]
        return np.sign(predicted - actual) / m

class MSE(Loss):
    """均方误差 (Mean squared error)
    """
    def loss(self, predicted, actual):
        m = predicted.shape[0]
        return np.sum((predicted - actual) ** 2) / (2 * m)

    def grad(self, predicted, actual):
        m = predicted.shape[0]
        return (predicted - actual) / m

class SoftmaxCrossEntropy(Loss):
    def loss(self, predicted, actual):
        m = predicted.shape[0]

        # softmax
        exps = np.exp(predicted - np.max(predicted))
        p = exps / np.sum(exps)

        # cross entropy loss
        nll = -np.log(np.sum(p * actual, axis=1))

        return np.sum(nll) / m

    def grad(self, predicted, actual):
        m = predicted.shape[0]
        grads = predicted - actual
        return grads / m


import numpy as np
import scipy as sp
import numpy.linalg
import scipy.linalg
from math import sqrt

from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cm

###-----------TRIANGLE--INTERSECTION--ALGORITHM--------------------------
def get_plane_equation(V):
    """
    Given a 3x3 ndarray with each row a vertex in R^3,
    compute the equation for the plane the triangle lies in.
    Plane: N \dot X + d = 0
    """
    N = np.cross(V[1,:] - V[0,:], V[2,:] - V[0,:])
    d = -np.dot(N, V[0,:])
    return N, d

def check_same_side(V, N, d):
    """
    Given a 3x3 ndarray V with each row a vertex in R^3,
    and the equation for a plane in R^3.
    Determine if all three verticies are on the same side of the plane.
    """
    ### AS IS ALLOWS FOR POINTS TO LIE IN PLANE
    c = np.dot(V, N) + d
    if max(c) <= 0.0:
        return True, c
    elif min(c) >= 0.0:
        return True, c
    else:
        return False, c

def get_vertex_projections(D, V):
    """
    Given planar intersection direction D and 3x3 triablge vertices V,
    return the projection of each projection onto the line.
    """
    max_ind = np.argmax(abs(D))
    return V[:,max_ind]

def get_isolated_vertex(c):
    """
    Return a permutation of the indices index of c with the first entry
    corresponding to the value with a different signed value from the other two and a
    """
    if c[0]*c[1] >= 0.0:
        return [2, 1, 0]
    elif c[0]*c[2] >= 0.0:
        return [1, 2, 0]
    else:
        return [0, 1, 2]

def get_line_location(p0, p1, c0, c1):
    return p0 + (p1 - p0)*c0/(c0 - c1)

def check_interval_intersection(t1a, t1b, t2a, t2b):
    """
    Determine if the intervals (t1a, t1b) and (t2a, t12b) intersect.
    """
    if min(t1a, t1b) < t2a and max(t1a, t1b) > t2a:
        return True
    elif min(t1a, t1b) < t2b and max(t1a, t1b) > t2b:
        return True
    elif min(t2a, t2b) < t1b and max(t2a, t2b) > t1b:
        return True
    else:
        return False

def rescale_triangle(V, scale):
    """
    Take triangle V and rescale each point by a factor of scale
    toward the triangle's center of mass.
    """
    C = np.tile(V.mean(axis=0), (3,1))
    return C + scale*(V - C)

def triangle_intersection(V1, V2, scaling=1.0):
    """
    Check for intersection between two triangles sitting in R^3.
    V1 and V2 are 3x3 ndarrays with each row a vertex.
    """

    # Rescale triangles to avoid edge or corner touching.
    V1 = rescale_triangle(V1, scaling)
    V2 = rescale_triangle(V2, scaling)

    ### From Moller 1997
    # Compute plane equation of triangle 2.
    N2, d2 = get_plane_equation(V2)

    # Reject if all points of triangle 1 are on same side.
    same_side_1, c1 = check_same_side(V1, N2, d2)
    if same_side_1 == True:
        return False

    # Compute plane equation of triangle 1.
    N1, d1 = get_plane_equation(V1)

    # Reject if all points of triangle 2 are on same side.
    same_side_2, c2 = check_same_side(V2, N1, d1)
    if same_side_2 == True:
        return False

    # Compute intersection line and project onto largest axis.
    D = np.cross(N1, N2)
    P1 = get_vertex_projections(D, V1)
    P2 = get_vertex_projections(D, V2)

    # Compute the intervals for each triangle.
    c_ind_1 = get_isolated_vertex(c1)
    c_ind_2 = get_isolated_vertex(c2)
    t1a = get_line_location(P1[c_ind_1[0]],
                            P1[c_ind_1[1]],
                            c1[c_ind_1[0]],
                            c1[c_ind_1[1]])
    t1b = get_line_location(P1[c_ind_1[0]],
                            P1[c_ind_1[2]],
                            c1[c_ind_1[0]],
                            c1[c_ind_1[2]])
    t2a = get_line_location(P2[c_ind_2[0]],
                            P2[c_ind_2[1]],
                            c2[c_ind_2[0]],
                            c2[c_ind_2[1]])
    t2b = get_line_location(P2[c_ind_2[0]],
                            P2[c_ind_2[2]],
                            c2[c_ind_2[0]],
                            c2[c_ind_2[2]])

    # Intersect the intervals
    return check_interval_intersection(t1a, t1b, t2a, t2b)

def get_face(x, f):
    """
    Given a flattened configuaration vector x, and indices for a triangle's corners f,
    return a 3x3 ndarray with the triangles coordinates.
    """
    t = np.zeros((3,3))
    for k in range(3):
        t[k,:] = x[3*f[k]:3*f[k]+3]
    return t

###-----------OTHER--GEOMETRIC--ALGORITHMS------------------------------

def Kabsch(P, Q, err_tol=10**-12):
    """
    Given two sets of N 3D points with centroid zero, find the rotation matrix
    that optimally rotates P onto Q.
    http://en.wikipedia.org/wiki/Kabsch_algorithm
    """
    assert abs(max(P.mean(axis=0))) < err_tol, abs(max(P.mean(axis=0)))
    assert abs(max(Q.mean(axis=0))) < err_tol, abs(max(Q.mean(axis=0)))

    A = np.dot(P.T, Q)
    V, s, WT = numpy.linalg.svd(A)
    #V, s, WT = scipy.linalg.svd(A)
    #d = np.sign(numpy.linalg.det(np.dot(WT, V)))
    #d = np.sign(numpy.linalg.det(np.dot(WT.T, V.T)))
    I = np.eye(3)
    U = numpy.dot(numpy.dot(V,I),WT)
    if numpy.linalg.det(U) < 0:
        I[2,2] = -1.0
        U = numpy.dot(numpy.dot(V,I),WT)
    return U

###-----------TESTING--CODE---------------------------------------------

def random_triangle():
    return np.random.uniform(size=(3,3))

def plot_triangles(V1, V2):
    ax = Axes3D(plt.figure())
    scale = 1.0
    ax.set_xlim(0.0,scale)
    ax.set_ylim(0.0,scale)
    ax.set_zlim(0.0,scale)

    T1 = Poly3DCollection([V1])
    color = colors.rgb2hex(sp.rand(3))
    T1.set_facecolor(color)
    T1.set_edgecolor('k')
    ax.add_collection(T1)
    T2 = Poly3DCollection([V2])
    color = colors.rgb2hex(sp.rand(3))
    T2.set_facecolor(color)
    T2.set_edgecolor('k')
    ax.add_collection(T2)

    plt.show()

def test_code(animate=False):
    V1 = random_triangle()
    V2 = random_triangle()
    print triangle_intersection(V1, V2)
    plot_triangles(V1, V2)

    if animate == True:
        for i in range(len(face_inds)):
            side = []
            for j in range(len(face_inds[i])):
                side.append([verts[face_inds[i][j],0],verts[face_inds[i][j],1],verts[face_inds[i][j],2]])

            tri = Poly3DCollection([side])
            color = colors.rgb2hex(sp.rand(3))
            tri.set_facecolor(color)
            tri.set_edgecolor('k')

            ax.add_collection3d(tri)

        plt.show()

def sample_so3():
    """
    Return a uniformly sampled matrix.
    """
    z = sp.randn(3,3)
    q,r = numpy.linalg.qr(z)
    d = sp.diagonal(r)
    ph = d/sp.absolute(d)
    return sp.multiply(q,ph,q)

def sample_SO3():
    while True:
        R = sample_so3()
        if numpy.linalg.det(R) > 0:
            return R


import numpy as np
import pandas as pd
from scipy.sparse.linalg import svds
from matplotlib import pyplot as plt
from sklearn.metrics import mean_squared_error as mse

from scipy import io
file = "facesOlivetti.mat"
data = io.loadmat(file)

X =np.matrix(data["X"])

h = 112
w = 92

mean_face = np.mean(X, axis = 0)
X_sub = X - mean_face

k_value = 400
u, s, vt= np.linalg.svd(X_sub)

Xproj = X_sub @ vt.T

selected_face = 60
recon_err = []

fig, ax = plt.subplots(2,2)
ax[0,0].imshow(np.reshape(X_sub[selected_face, : ], ( w, h)).T, extent=[0,1,0,1],cmap = "gray")
ax[0,0].set_axis_off()
ax[0,0].set_title("Original Image No." + str(selected_face))

for i in range(1, k_value+1):

    X_reconst = Xproj[selected_face, : i ] * vt[ : i ,  : ]+ mean_face
    X_err = X_reconst -  X[selected_face , : ]
    X_mse = np.mean(np.square(X_err))
    recon_err.append(X_mse)

    if i == 10 :
        ax[0,1].imshow(np.reshape(X_reconst, ( w, h)).T, extent=[0,1,0,1],cmap = "gray")
        ax[0,1].set_axis_off()
        ax[0,1].set_title("Reconstructed Image with 10 bases")

    elif i == 100 :
        ax[1,0].imshow(np.reshape(X_reconst, ( w, h)).T, extent=[0,1,0,1],cmap = "gray")
        ax[1,0].set_axis_off()
        ax[1,0].set_title("Reconstructed Image with 100 bases")

ax[1,1].plot( recon_err)
ax[1,1].set_title("Reconstruction Error")

plt.show()


from sympy.integrals.quadrature import gauss_legendre

PRECISION = 20

def get_gauss_legendre_pairs(order):
    qps, weights = gauss_legendre(order, PRECISION)
    apprx_qps = [float(qp) for qp in qps]
    apprx_weights = [float(weight) for weight in weights]
    return apprx_qps, apprx_weights

class Quadrature:
    def compute(self, func):
        total = 0.0
        for qp, weight in zip(self.qps, self.weights):
            total += weight * func(*qp)
        return total

class GaussLegendre1D(Quadrature):
    def __init__(self, n_qps):
        qps, weights = get_gauss_legendre_pairs(n_qps)
        self.qps = [(qp,) for qp in qps]
        self.weights = weights
        self.max_poly_order = (2 * len(self.qps)) - 1

if __name__ == '__main__':
    gl2 = GaussLegendre1D(2)
    def test_function_int(func):
        print("Around Origin  :", gl2.compute(func))

    print("Line")
    test_function_int(lambda x : x)
    print()

    print("Quad")
    test_function_int(lambda x : x * x)
    print()

    print("Cube")
    test_function_int(lambda x : x * x * x)
    print()

    print("Quart") # 2nd order GLQ is not exact
    test_function_int(lambda x : x * x * x * x)


import numpy as np
import matplotlib.pyplot as plt

def get_pi(N):
    x = np.random.random(N)
    y = np.random.random(N)
    r = np.sqrt(x*x + y*y)
    N_in = len(r[np.where(r <= 1)])
    return (N_in/N)*4.0

N = 10000
N_trials = 10000
pis = np.zeros(N_trials)
for i in range(0,N_trials):
    pis[i] = get_pi(N)

print(pis.mean(), "+/-", pis.std())
plt.hist(pis, bins='fd', density=True)
plt.show()

#use guassian mu (mean) and sigma (std dev) to get evenly spaced


import numpy as np

def model_simple_gaussian(x, coefficients):
    return - 1 / (coefficients[0] * np.sqrt(2 * np.pi)) * np.exp(
        - 0.5 * ((np.array(x) - coefficients[1]) / coefficients[0]) ** 2) + coefficients[2]

def model_double_gaussian(x, coefficients):
    return - 1 / (coefficients[0] * np.sqrt(2 * np.pi)) * np.exp(
        - 1 / 2 * ((np.array(x) - coefficients[1]) / coefficients[0]) ** 2) \
           - 1 / (coefficients[2] * np.sqrt(2 * np.pi)) * np.exp(
        - 1 / 2 * ((np.array(x) - coefficients[3]) / coefficients[2]) ** 2) + coefficients[4]

def model_simple_expo(x, coefficients):
    return coefficients[0] * np.exp(coefficients[1] * np.array(x)) + coefficients[2]

def model_double_expo(x, coefficients):
    return coefficients[0] * np.exp(coefficients[1] * np.array(x)) + coefficients[2] * np.exp(
        coefficients[3] * np.array(x)) + coefficients[4]

def model_lorentz(x, coefficients):
    return - (2 / (np.pi * coefficients[1])) / (1 + ((np.array(x) - coefficients[0]) / (0.5 * coefficients[1])) ** 2) +\
           coefficients[2]

def model_double_lorentz(x, coefficients):
    return - (2 / (np.pi * coefficients[1])) / (1 + ((np.array(x) - coefficients[0]) / (0.5 * coefficients[1])) ** 2) \
           - (2 / (np.pi * coefficients[3])) / (1 + ((np.array(x) - coefficients[2]) / (0.5 * coefficients[3])) ** 2) \
           + coefficients[4]

def model_linear(x, coefficients):
    return coefficients[0] * np.array(x) + coefficients[1]

def model_simple_gaussian_emission(x, coefficients):
    return 1 / (coefficients[0] * np.sqrt(2 * np.pi)) * np.exp(- 1 / 2 * ((x - coefficients[1]) / coefficients[0]) ** 2)\
           + coefficients[2]

def model_double_gaussian_emission(x, coefficients):
    return 1 / (coefficients[0] * np.sqrt(2 * np.pi)) * np.exp(- 1 / 2 * ((x - coefficients[1]) / coefficients[0]) ** 2)\
           + 1 / (coefficients[2] * np.sqrt(2 * np.pi)) * np.exp(- 1 / 2 * ((x - coefficients[3]) / coefficients[2]) ** 2)\
           + coefficients[4]

def model_lorentz_emission(x, coefficients):
    return (2 / (np.pi * coefficients[1])) / (1 + ((x - coefficients[0]) / (0.5 * coefficients[1])) ** 2) + \
           coefficients[2]

def model_double_lorentz_emission(x, coefficients):
    return (2 / (np.pi * coefficients[1])) / (1 + ((x - coefficients[0]) / (0.5 * coefficients[1])) ** 2) + (
            2 / (np.pi * coefficients[3])) / (1 + ((x - coefficients[2]) / (0.5 * coefficients[3])) ** 2) + \
           coefficients[4]


# basic method
from math import pi, cos, sin, sqrt, atan
import numpy as np

def build_complex_value(VM, VA):
    '''
    Build complex number  by amplitude and phase angle, and the phase angle is rad radian.
    Args:
        (1) VM, amplitude.
        (2) VA, phase angle in rad.
    Rets:
        value, complex number with the format a+jb.
    '''
    value = VM * (cos(VA) + 1j * sin(VA))
    return value

def convert_deg_to_rad(angle):
    '''
    Convert angle from deg to rad.
    Args:
        angle, angle in deg.
    Rets:
        angle, angle in rad.
    '''
    angle = angle * pi / 180.0
    return angle

def convert_rad_to_deg(angle):
    '''
    Convert angle from rad to deg.
    Args:
        angle, angle in rad.
    Rets:
        angle, angle in deg.
    '''
    angle = angle * 180.0 / pi
    return angle

def get_complex_phase_angle(value):
    '''
    Get complex phase angle in rad.
    Args:
        value, complex.
    Rets:
        angle, phase angle in rad.
    '''
    angle = atan(value.imag / value.real)
    return angle

def convert_xy_to_dq(derta, Ax, Ay):
    '''
    Convert the value of the xy frame to the value of the dq frame.
    Args:
        (1) derta, angle in rad.
        (2)(3) Ax, Ay,  x-component and y-component.
    Rets:
        (1)(2) Ad, Aq, d-component and q-component.
    '''
    convert_mat = np.array([[sin(derta), -cos(derta)], [cos(derta), sin(derta)]])
    Axy_mat = np.array([[Ax], [Ay]])
    Adq_mat = np.dot(convert_mat, Axy_mat)
    Ad, Aq = Adq_mat[0, 0], Adq_mat[1, 0]
    return Ad, Aq

def composite_three_phase_vector(Fa):
    '''
    Calculate three phase asymmetry vector from sequence component.
    Args:
        Fa, tuple, sequence component, (Fa1, Fa2, Fa0), (positive, negative, zero).
    Rets:
        F, A. B, C three phase components, (Fa, Fb, Fc).
    '''
    (Fa1, Fa2, Fa0) = Fa
    a = -0.5 + sqrt(3) * 0.5j

    Fa = Fa1 + Fa2 + Fa0
    Fb = a ** 2 * Fa1 + a * Fa2 + Fa0
    Fc = a * Fa1 + a ** 2 * Fa2 + Fa0

    return (Fa, Fb, Fc)


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

def estimateGaussian(X):
    #   [mu sigma2] = estimateGaussian(X),
    #   The input X is the dataset with each n-dimensional data point in one row
    #   The output is an n-dimensional vector mu, the mean of the data set
    #   and the variances sigma^2, an n x 1 vector

    m, n = X.shape
    # You should return these values correctly
    mu = np.zeros((n, 1))
    sigma2 = np.zeros((n, 1))
    # estimating mu - at this point it is an n-column vector
    # mu = (1/m)*sum(X,1);
    mu = np.mean(X, axis=0)
    # turn into n-rows vector
    mu = mu.T

    # estimating sigma^2 = std.dev.
    # normalizes with 1/N, instead of with 1/(N-1) in formula std (x) = 1/(N-1) SUM_i (x(i) - mean(x))^2
    # i.e. degrees of freedom = 0 (by default)
    sigma2 = np.var(X, axis=0)

    # turn into n-rows vector
    sigma2 = sigma2.T

    return mu, sigma2

def multivariateGaussian(X, mu, sigma2):
    #    p = MULTIVARIATEGAUSSIAN(X, mu, sigma2) Computes the probability
    #    density function of the examples X under the multivariate gaussian
    #    distribution with parameters mu and sigma2. If sigma2 is a matrix, it is
    #    treated as the covariance matrix. If sigma2 is a vector, it is treated
    #    as the \sigma^2 values of the variances in each dimension (a diagonal
    #    covariance matrix)
    k = len(mu)

    # turns 1D array into 2D array
    if sigma2.ndim == 1:
        sigma2 = np.reshape(sigma2, (-1,sigma2.shape[0]))

    if sigma2.shape[1] == 1 or sigma2.shape[0] == 1:
        sigma2 = linalg.diagsvd(sigma2.flatten(), len(sigma2.flatten()), len(sigma2.flatten()))

    # mu is unrolled (and transposed) here
    X = X - mu.reshape(mu.size, order='F').T

    p = np.dot(np.power(2 * np.pi, - k / 2.0), np.power(np.linalg.det(sigma2), -0.5) ) * \
        np.exp(-0.5 * np.sum(np.dot(X, np.linalg.pinv(sigma2)) * X, axis=1))

    return p

def computeNumericalGradient(J, theta):
    #   numgrad = COMPUTENUMERICALGRADIENT(J, theta) computes the numerical
    #   gradient of the function J around theta. Calling y = J(theta) should
    #   return the function value at theta.

    # Notes: The following code implements numerical gradient checking, and
    #        returns the numerical gradient.It sets numgrad(i) to (a numerical
    #        approximation of) the partial derivative of J with respect to the
    #        i-th input argument, evaluated at theta. (i.e., numgrad(i) should
    #        be the (approximately) the partial derivative of J with respect
    #        to theta(i).)
    numgrad = np.zeros(theta.shape)
    perturb = np.zeros(theta.shape)
    e = 1e-4

    for p in range(theta.size):
        # Set perturbation vector
        perturb.reshape(perturb.size, order="F")[p] = e
        loss1, _ = J(theta - perturb)
        loss2, _ = J(theta + perturb)
        # Compute Numerical Gradient
        numgrad.reshape(numgrad.size, order="F")[p] = (loss2 - loss1) / (2*e)
        perturb.reshape(perturb.size, order="F")[p] = 0

    return numgrad

def cofiCostFunc(params, Y, R, num_users, num_movies, num_features, lambda_var):
    #   [J, grad] = COFICOSTFUNC(params, Y, R, num_users, num_movies, ...
    #   num_features, lambda) returns the cost and gradient for the
    #   collaborative filtering problem.

    # Unfold the U and W matrices from params
    X = np.reshape(params[:num_movies*num_features], (num_movies, num_features), order='F')
    Theta = np.reshape(params[num_movies*num_features:], (num_users, num_features), order='F')

    # You need to return the following values correctly
    J = 0
    X_grad = np.zeros(X.shape)
    Theta_grad = np.zeros(Theta.shape)

    # X * Theta performed according to low rank matrix vectorization
    squared_error = np.power(np.dot(X,Theta.T) - Y,2)

    # for cost function, sum only i,j for which R(i,j)=1
    J = (1/2.) * np.sum(squared_error * R)

    # NOTE where filtering through R is applied:
    # 	at ( (X * Theta') - Y ) - NOT after multiplying by Theta
    # 	that means that for purposes of the gradient, we're only interested
    # 	in the errors/differences in i, j for which R(i,j)=1
    # NOTE also that even though we do a sum, we only do it over users,
    # 	so we still get a matrix
    X_grad = np.dot((np.dot(X, Theta.T) - Y) * R, Theta)
    # NOTE also that even though we do a sum, we only do it over movies,
    # 	so we still get a matrix
    Theta_grad = np.dot(((np.dot(X, Theta.T) - Y) * R).T, X)

    ### COST FUNCTION WITH REGULARIZATION
    # only add regularized cost to J now
    J = J + (lambda_var / 2.) * (np.sum(np.power(Theta, 2)) + np.sum(np.power(X, 2)))

    ### GRADIENTS WITH REGULARIZATION
    # only add regularization terms
    X_grad = X_grad + lambda_var*X
    Theta_grad = Theta_grad + lambda_var*Theta

    grad = np.concatenate((X_grad.reshape(X_grad.size, order='F'), Theta_grad.reshape(Theta_grad.size, order='F')))

    return J, grad

def checkCostFunction(lambda_var=0):
    #   CHECKCOSTFUNCTION(lambda_var) Creates a collaborative filering problem
    #   to check your cost function and gradients, it will output the
    #   analytical gradients produced by your code and the numerical gradients
    #   (computed using computeNumericalGradient). These two gradient
    #   computations should result in very similar values.

    # Set lambda_var
    # if not lambda_var or not 'lambda_var' in locals():
    #     lambda_var = 0

    ## Create small problem
    X_t = np.random.rand(4, 3)
    Theta_t = np.random.rand(5, 3)

    # Zap out most entries
    Y = np.dot(X_t, Theta_t.T)
    Y[np.random.rand(Y.shape[0], Y.shape[1]) > 0.5] = 0
    R = np.zeros(Y.shape)
    R[Y != 0] = 1

    ## Run Gradient Checking
    X = np.random.randn(X_t.shape[0], X_t.shape[1])
    Theta = np.random.randn(Theta_t.shape[0], Theta_t.shape[1])
    num_users = Y.shape[1]
    num_movies = Y.shape[0]
    num_features = Theta_t.shape[1]

    params = np.concatenate((X.reshape(X.size, order='F'), Theta.reshape(Theta.size, order='F')))

    # Short hand for cost function
    def costFunc(p):
        return cofiCostFunc(p, Y, R, num_users, num_movies, num_features, lambda_var)

    numgrad = computeNumericalGradient(costFunc, params)

    cost, grad = cofiCostFunc(params, Y, R, num_users, num_movies, num_features, lambda_var)

    print(np.column_stack((numgrad, grad)))
    print('The above two columns you get should be very similar.\n' \
             '(Left-Your Numerical Gradient, Right-Analytical Gradient)')

    diff = np.linalg.norm(numgrad-grad)/np.linalg.norm(numgrad+grad)
    print('If your backpropagation implementation is correct, then \n' \
             'the relative difference will be small (less than 1e-9). ' \
             '\nRelative Difference: {:e}'.format(diff))

def selectThreshold(yval, pval):
    #   [bestEpsilon bestF1] = SELECTTHRESHOLD(yval, pval) finds the best
    #   threshold to use for selecting outliers based on the results from a
    #   validation set (pval) and the ground truth (yval).

    bestEpsilon = 0
    bestF1 = 0
    F1 = 0

    stepsize = (max(pval) - min(pval)) / 1000
    for epsilon in np.arange(min(pval), max(pval), stepsize):
        # Note: You can use predictions = (pval < epsilon) to get a binary vector
        #       of 0's and 1's of the outlier predictions

        # make anomaly predictions based on which values of pval are less than epsilon
        #   i.e. which examples from the cross-validation set have a very low p(x)
        cvPredictions = pval < epsilon

        # calculate F1 score, starting by calculating true positives(tp)
        # false positives (fp) and false negatives (fn)

        # true positives is the intersection between
        #   our positive predictions (cvPredictions==1) and positive ground truth values (yval==1)
        tp = np.sum(np.logical_and((cvPredictions == 1), (yval == 1)).astype(float))

        # false positives are the ones we predicted to be true (cvPredictions==1) but weren't (yval==0)
        fp = np.sum(np.logical_and((cvPredictions == 1), (yval == 0)).astype(float))

        # false negatives are the ones we said were false (cvPredictions==0) but which were true (yval==1)
        fn = np.sum(np.logical_and((cvPredictions == 0), (yval == 1)).astype(float))

        # compute precision, recall and F1
        precision = tp / (tp + fp)
        recall = tp / (tp + fn)
        F1 = (2 * precision * recall) / (precision + recall)

        if F1 > bestF1:
            bestF1 = F1
            bestEpsilon = epsilon

    return bestEpsilon, bestF1

def visualizeFit(X, mu, sigma2):
    #   VISUALIZEFIT(X, p, mu, sigma2) This visualization shows you the
    #   probability density function of the Gaussian distribution. Each example
    #   has a location (x1, x2) that depends on its feature values.

    X1,X2 = np.meshgrid(np.arange(0, 35.1, 0.5), np.arange(0, 35.1, 0.5))
    Z = multivariateGaussian(np.column_stack((X1.reshape(X1.size, order='F'), X2.reshape(X2.size, order='F'))), mu, sigma2)
    Z = Z.reshape(X1.shape, order='F')

    plt.plot(X[:, 0], X[:, 1],'bx', markersize=13, markeredgewidth=1)

    plt.hold(True)
    # Do not plot if there are infinities
    if (np.sum(np.isinf(Z)) == 0):
        plt.contour(X1, X2, Z, np.power(10,(np.arange(-20, 0.1, 3)).T))

    plt.hold(False)

def loadMovieList():
    #   movieList = GETMOVIELIST() reads the fixed movie list in movie.txt
    #   and returns a cell array of the words in movieList.

    ## Read the fixed movieulary list
    with open("ml/ex8/data/movie_ids.txt") as movie_ids_file:

        # Store all movies in movie list
        n = 1682  # Total number of movies

        movieList = [None]*n
        for i, line in enumerate(movie_ids_file.readlines()):
            movieName = line.split()[1:]
            movieList[i] = " ".join(movieName)

    return movieList

def normalizeRatings(Y, R):
    #   [Ynorm, Ymean] = NORMALIZERATINGS(Y, R) normalized Y so that each movie
    #   has a rating of 0 on average, and returns the mean rating in Ymean.

    m, n = Y.shape
    Ymean = np.zeros((m, 1))
    Ynorm = np.zeros(Y.shape)
    for i in range(m):
        idx = R[i, :] == 1
        Ymean[i] = np.mean(Y[i, idx])
        Ynorm[i, idx] = Y[i, idx] - Ymean[i]

    return Ynorm, Ymean


"""
    AUTO-CORRELATION

	This code is used to analyze different properties of auto-correltion

    Code by:- GSS

"""

# Let us first import all the required libraries
import numpy as np
import math
from scipy import signal
print("**************************************************************************")

#let us consider a discrete signal as shown below
x=[1,4,2,3]

#auto-coreltion function using inbuilt function in scipy
rxx=signal.correlate(x,x)
print("correlated signal:",rxx)

rxxcenter=math.floor(len(rxx)/2)

print('\nCenter position:',rxxcenter)
#deriving 'rxxleft' and 'rxxright' arrays

rxxleft=[]
rxxright=[]

for i in range(rxxcenter+1):
    rxxleft.append(rxx[rxxcenter-i])
print('\nrxx[rxxcenter]=',rxx[rxxcenter])

i=0

for i in range(rxxcenter+1):
    rxxright.append(rxx[rxxcenter+i])
print('\nrxxleft=',rxxleft)
print('\nrxxright=',rxxright)

# properties to be proved

if(rxxleft == rxxright):
    print("******************************************************")
    print("symmetry property proved")
    print("******************************************************")

def check(rxx, val):
    for z in rxx:
        if val< z:
            return False
    return True

if(check(rxx,rxx[rxxcenter])==True):
    print("******************************************************")
    print("Upper bound property Rxx(0)>=Rxx(k) is verified")
    print("******************************************************")
##########################################################################
#or 																	##
#																		##
#i=0																	##
# cntr=0																##
# for i in range(len(rxx)):												##
#     if(rxx[rxxcenter-1]<rxx[i]):										##
#         cntr=cntr+1													##
#																		##
# if (cntr==0):															##
#     print("******************************************************")	##
#     print("Upper bound property Rxx(0)>=Rxx(k) is verified")			##
#     print("******************************************************")	##
##########################################################################
y=np.square(x)
mean=sum(y)

if(rxx[rxxcenter]==mean):
    print("******************************************************")
    print("Mean square value=correlation value at zero shift")
    print("******************************************************")


#t83_1.py
#numpy Tutorial
#15-4-2016

import numpy as np

time = np.linspace(20, 145, 5)                 # time scale  #+ generates series from 20 to 145 in 5 steps, double
data = np.sin(np.arange(20)).reshape(5,4)      # 4 time-dependent series

#20 elements, 5 rows x 4 columns ... sinus of 0, 1, 2, 3, ... 19  -- sin(0), sin(1) ...

print(time)
print(data)

ind = data.argmax(axis=0)                   # index of the maxima for each series
# ahaaa ... vertical ...
print("axis=0")
print(ind)
#print(data[ind])

ind2 = data.argmax(axis=1)                   # index of the maxima for each series
# ahaaa ... vertical ...
print("axis=1")
print(ind2)

#Брои индексите ВЕРТИКАЛНО на това което показва с печатането!

#ind
#array([2, 0, 3, 1])
print("time_max... ind")
time_max = time[ ind]                       # times corresponding to the maxima
print(time_max)
data_max = data[ind, xrange(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...
print(data_max)

print(type(data_max))
print(type(data))
# no -- errors
#print("time_max... ind2")
#time_max2 = time[ ind2]                       # times corresponding to the maxima
#print(time_max2)
#data_max2 = data[ind2, xrange(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...
#print(data_max2)

#time_max
#array([  82.5 ,   20.  ,  113.75,   51.25])
# data_max
#array([ 0.98935825,  0.84147098,  0.99060736,  0.6569866 ])
#
# np.all(data_max == data.max(axis=0))
#True

import numpy as np
import matplotlib.pyplot as plt
# Build a vector of 10000 normal deviates with variance 0.5^2 and mean 2
mu, sigma = 2, 0.5
v = np.random.normal(mu,sigma,100)
# Plot a normalized histogram with 50 bins
plt.hist(v, bins=10, normed=1)       # matplotlib version (plot)
plt.show()

#v2 = np.linspace(20, 145, 0.35)
#v3 = np.linspace(10, 100, 1)
#v4 = np.ndarray(v2, 3)
mu, sigma = 4, 0.2
#v4 = np.random.
v4 = np.random.poisson(size=1000)
plt.hist(data, bins=2, normed=1.0)       # matplotlib version (plot)
plt.show()


import numpy as np
import pandas as pd

from chapter.four import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis

def question_17():
    """ Misclassification error rate of QDA and LDA.

    Looks like they're both the same for the Height and Weight data: 11.9%.

    Returns:
        None.
    """
    # Get the height weight data
    hw_data = pd.read_csv(
        "cached_data/height_weight.csv",
        header=None,
        names=["gender", "height", "weight"],
        sep=",",
    )
    train = hw_data.drop(columns=["gender"]).values
    lda = LinearDiscriminantAnalysis()
    lda.fit(train, hw_data.gender.values)
    preds = lda.predict(train)
    miss_lda = np.not_equal(preds, hw_data.gender.values).sum()/hw_data.gender.shape[0]
    # Get the missclassification rate on each of LDA and QDA.
    print(f"LDA missclassification rate: {miss_lda*100:0.2f}")
    qda = QuadraticDiscriminantAnalysis()
    qda.fit(train, hw_data.gender.values)
    preds = qda.predict(train)
    miss_qda = np.not_equal(preds, hw_data.gender.values).sum()/hw_data.gender.shape[0]
    print(f"QDA missclassification rate: {miss_qda*100:0.2f}")
    return None

def question_19():
    r""" Stretched LDA.

    This is LDA with the additional parameter that we can multiply the grouped covariance matrix
    by a class-specific value to stretch the Gaussians.

    This change will cause the resulting model to still be quadratic in :math:`x` because we can't
    eliminate all of the squared terms as we did with LDA.

    .. math::
        p(y=c|x,\theta) &= \frac{\pi_c|2\pi\Sigma|^{-\frac{1}{2}}\exp(-\frac{1}{2}(x-\mu_c)^T\Sigma^{-1}(x-\mu_c))}{\Sigma_{c'}\pi_{c'}|2\pi\Sigma|^{-\frac{1}{2}}\exp(-\frac{1}{2}(x-\mu_{c'})^T\Sigma^{-1}(x-\mu_{c'}))}\\
            &= \frac{\pi_ck_c^n\exp(-\frac{1}{2}(x-\mu_c)^T\Sigma^{-1}(x-\mu_c))}{\Sigma_{c'}\pi_{c'}k_{c'}^n\exp(-\frac{1}{2}(x-\mu_{c'})^T\Sigma^{-1}(x-\mu_{c'}))}\\
            &= \frac{\pi_ck_c^n\exp(-\frac{1}{2}k_cx^T\Sigma^{-1}x)\exp(-\frac{1}{2}k_c\mu_c^T\Sigma^{-1}\mu_c+k_c\mu_c^T\Sigma^{-1}x}{\Sigma_{c'}\pi_{c'}k_{c'}^n\exp(-\frac{1}{2}k_{c'}x^T\Sigma^{-1}x)\exp(-\frac{1}{2}k_{c'}\mu_{c'}^T\Sigma^{-1}\mu_{c'}+k_{c'}\mu_{c'}^T\Sigma^{-1}x}\\

    Returns:
        None.
    """
    return None

def question_22():
    r""" QDA with 3 classes.

    Side effect: prints the answer that we're looking for!

    Point [-0.5, 0.5] is classified as 1, while [0.5, 0.5] is classified as 2.

    Interestingly, they're both very close to the decision boundary, with the first point
    almost in the 3rd class, and the second point almost in the 1st class. The results may
    be an artifact due to numerical instability in the code, but visual inspection marks them
    as the class labels given above.

    Returns:
        None.
    """
    qda = QuadraticDiscriminantAnalysis()
    qda.pi = [1 / 3, 1 / 3, 1 / 3]
    qda.mu = [np.array([0, 0]), np.array([1, 1]), np.array([-1, 1])]
    qda.sigma = [
        np.matrix([[0.7, 0], [0, 0.7]]),
        np.matrix([[0.8, 0.2], [0.2, 0.8]]),
        np.matrix([[0.8, 0.2], [0.2, 0.8]]),
    ]
    # Use this instead of the above 4 lines for LDA.
    # qda.sigma = [
    #    np.matrix([[0.766, 1.33], [1.33, 0.766]])
    # ]
    qda.pos_labels = [1, 2, 3]
    print(f"Point (-0.5, 0.5) has label: {qda.predict(np.array([-0.5, 0.5]))}")
    print(f"Point (0.5, 0.5) has label: {qda.predict(np.array([0.5, 0.5]))}")
    return None

def question_23():
    r""" Scalar QDA.

    Side-effect:
        We found the QDA values for each class as desired, and got an 83% probability for a 72 inch
        student to be a male at this university.

        The meatier question is about how to extend this method to the situation if we had multiple
        attributes per person. The clear answer would be to simply run QDA as implemented earlier, but I think
        that's not what the question is really getting at. I think you could run an ensemble of 1D QDA's and
        then take the average of the predicted values for each class across the set of 1D classifiers. This would
        ignore correlations between variables, but would take into account the multiple features.

    Returns:
        None.
    """
    data = np.matrix([
        [67, 79, 71, 68, 67, 60],
        [1, 1, 1, 2, 2, 2],
    ]).T
    qda = QuadraticDiscriminantAnalysis()
    print(list(data[:, 1].flat))
    qda.fit(data[:, 0], list(data[:, 1].flat))
    print(f"Priors: {qda.pi}")
    print(f"Means: {qda.mu}")
    print(f"Sigmas: {qda.sigma}")
    qda.predict(np.array([72]))
    print(f"P(y=m|x=72,\\theta): f{np.exp(qda.probs[0])}")
    return None


#!/usr/bin/env python

"""
generate a random walk to prove the equation.
"""
import numpy as np
from matplotlib import pyplot as plt

def gen_rand(prob):
    """ Decide where to go """
    if np.random.uniform() < prob:
        return 1        # move right
    else:
        return -1       # move left

def rw_next(cur_pos, probability):
    """evolve"""
    return cur_pos + gen_rand(probability)

def rw_simul(prob):
    """simulate RW for prob as probability of going left"""
    x_pos = np.zeros((10000, 10))

    for i in range(10000):
        pos = 0

        for j in range(10):
            for _ in range(100):
                pos = rw_next(pos, prob)

            x_pos[i, j] = pos

    x_mean = np.mean(x_pos, axis=0)
    sigma = np.mean(x_pos ** 2, axis=0) - x_mean

    return x_mean, sigma

def main():
    """main body"""
    sigmas = np.zeros((3, 10), dtype=float)
    x_means = np.zeros((3, 10), dtype=float)

    probs = [0.2, 0.35, 0.5]
    time = np.linspace(10, 1000, 10)

    for i in range(3):
        print("==> simulating for", probs[i])
        x_means[i], sigmas[i] = rw_simul(probs[i])

    print("==> plotting graphs")
    plt.plot(time, x_means[0], ls='', marker='o', color='b', label='p = 0.2')
    plt.plot(time, x_means[1], ls='', marker='o', color='r', label='p = 0.35')
    plt.plot(time, x_means[2], ls='', marker='o', color='g', label='p = 0.5')
    plt.xlabel('time')
    plt.ylabel('<x>')
    plt.title('mean of x over time for different p')
    plt.legend()
    plt.savefig("x_means.jpg", bbox_inches='tight')
    plt.close()

    plt.plot(time, sigmas[0], ls='', marker='^', color='b', label='p = 0.2')
    plt.plot(time, sigmas[1], ls='', marker='^', color='r', label='p = 0.35')
    plt.plot(time, sigmas[2], ls='', marker='^', color='g', label='p = 0.5')
    plt.xlabel('time')
    plt.ylabel('sigmas')
    plt.title('mean of x over time for different p')
    plt.legend()
    plt.savefig("sigmas.jpg", bbox_inches='tight')
    plt.close()

if __name__ == "__main__":
    main()


from math import factorial
from sympy import diff, symbols, lambdify, solveset
import numpy as np

########################################################################
def determinant(A):
    """
    Recursive calculation of the determinant of matrix A
    """
    if(A.shape == (2, 2)):
        return A[0,0]*A[1,1] - A[0,1]*A[1,0]
    else:
        det = 0.
        # iterate over row of matrix A
        for i in range(len(A)):
            sign = (-1.)**i

            # identify submatrix
            sublen = int(len(A)-1)
            As = np.zeros((sublen,sublen),dtype=type(A[0,0]))
            # count the number of row
            row = 0
            # iterate over row of matrix A
            for j in range(len(A)):
                # if row = i, skip
                if(i==j):
                    continue
                As[:,row] = A[1:,j]
                row += 1

            # determinant
            det += sign*A[0,i]*determinant(As)
    return det

###############################################################################
def init_Tmunu():
    """
    calculate the eigenvalues of T^{\mu \nu}
    Algebraic expressions for solve_Tmunu
    """
    T00 = symbols('T00')
    T01 = symbols('T01')
    T02 = symbols('T02')
    T03 = symbols('T03')
    T11 = symbols('T11')
    T12 = symbols('T12')
    T13 = symbols('T13')
    T22 = symbols('T22')
    T23 = symbols('T23')
    T33 = symbols('T33')

    Tmunu = np.array([[T00,T01,T02,T03],[T01,T11,T12,T13],[T02,T12,T22,T23],[T03,T13,T23,T33]])
    gmunu = np.array([[1,0,0,0],[0,-1,0,0],[0,0,-1,0],[0,0,0,-1]])

    # system to solve (x is the vector containing the eigenvalues)
    # ( T^{\mu \nu} - x g^{\mu \nu} ) u_\mu = 0
    x = symbols('x')
    tosolve = Tmunu - x*gmunu
    # calculate determinant of ( T^{\mu \nu} - x g^{\mu \nu} ) = characteristic polynomial
    det = determinant(tosolve)

    print(det)

    # we want det(x) = c0 + c1*x +c2*x**2 + c3*x**3 + c4*x**4
    # find coefficients of the characteristic polynomial
    coeff = 5*[0.]
    coeff[0] = det.subs(x,0)
    for order in range(1,5):
        det = diff(det, x, 1)
        coeff[order] = det.subs(x,0)/factorial(order)

    print(coeff[::-1])

init_Tmunu()


import numpy as np

### linefit
# From linefit.f from Stan Love
# Converted to python by Bryan Miller
# Fit a line to points with uncertainties in both axes

def linefit(x,sigx,y,sigy):
    m = 0.0
    b = 0.0
    csum = 0.0
    mold = -99999.
    sigsqb = 9999.0
    sigsqm = 9999.
    n = x.size
    while abs(m-mold) > 0.1*np.sqrt(sigsqm):
        mold = m
        sigsquen = sigy**2 + (mold*sigx)**2
        sumx = np.sum(x/sigsquen)
        sumy = np.sum(y/sigsquen)
        sumxx = np.sum((x**2)/sigsquen)
        sumxy = np.sum((x*y)/sigsquen)
        sums = np.sum(1.0/sigsquen)
        csum = np.sum((y-b-m*x)**2/sigsquen)
        det = sums*sumxx - sumx**2
        b = (sumxx*sumy - sumx*sumxy)/det
        m = (sums*sumxy - sumx*sumy)/det
        sigsqb = sumxx/det
        sigsqm = sums/det
    sigb = np.sqrt(sigsqb)
    sigm = np.sqrt(sigsqm)
    chi = csum/float(n-2)

    return m,sigm,b,sigb,chi


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

lbdn2 = np.array([7.313, 44.61, 113.9, 215.2])
gn = np.array([0.749, 0.544, 0.463, 0.415])
lbdn4 = np.array([math.pow(e, 2) for e in lbdn2])

Pr = 0.71
r0 = 5
Re = 500
k = 0.024
a = 0.25e3

def xplus(x):
    return x / (r0 * Pr * Re)

xi1 = xplus(100)

def q(x):
    s = 0
    cxp = xplus(x)
    if cxp < xi1:
        for i in range(4):
            s += gn[i] / lbdn2[i] * (1 - math.exp(-lbdn2[i] * cxp))
    else:
        for i in range(4):
            s += gn[i] / lbdn2[i] * math.exp(-lbdn2[i] * cxp) * (math.exp(lbdn2[i] * xi1) - 1)

    return 2 * k * a / r0 * s

def dt(x):
    s = 0
    cxp = xplus(x)
    if cxp < xi1:
        for i in range(4):
            s += gn[i] / lbdn4[i] * (1 - math.exp(-lbdn2[i] * cxp))
    else:
        for i in range(4):
            s += gn[i] / lbdn4[i] * math.exp(-lbdn2[i] * cxp) * (math.exp(lbdn2[i] * xi1) - 1)

    return 8 * a * s

def nu(x):
    up = 0
    down = 0

    xp = xplus(x)
    if xp < xi1:
        for i in range(4):
            up += gn[i] / lbdn2[i] * (1 - math.exp(-lbdn2[i] * xp))
            down += gn[i] / lbdn4[i] * (1 - math.exp(-lbdn2[i] * xp))
    else:
        for i in range(4):
            up += gn[i] / lbdn2[i] * math.exp(-lbdn2[i] * xp) * (math.exp(lbdn2[i] * xi1) - 1)
            down += gn[i] / lbdn4[i] * math.exp(-lbdn2[i] * xp) * (math.exp(lbdn2[i] * xi1) - 1)

    return up / down / 2

if __name__ == '__main__':
    x = np.arange(25, 501, 5)
    qx = np.array([q(t) for t in x])
    dtx = np.array([dt(t) for t in x])
    nux = np.array([nu(t) for t in x])

    fig, (ax1, ax2, ax3) = plt.subplots(1, 3)
    ax1.plot(x, qx)
    ax1.set_title('q')
    ax2.plot(x, dtx)
    ax2.set_title('T0 - Tm')
    ax3.plot(x, nux)
    ax3.set_title('Nu')
    plt.show()


import numpy as np
from scipy import optimize

# Adapted from the SciPy Cookbook

def calc_R(x,y, xc, yc):
    """ calculate the distance of each 2D points from the center (xc, yc) """
    return np.sqrt((x-xc)**2 + (y-yc)**2)

def f(c, x, y):
    """ calculate the algebraic distance between the data points and the mean circle centered at c=(xc, yc) """
    Ri = calc_R(x, y, *c)
    return Ri - Ri.mean()

def leastsq_circle(x,y, centre_estimate):
    centre, ier = optimize.leastsq(f, centre_estimate, args=(x,y))
    xc, yc = centre
    Ri       = calc_R(x, y, *centre)
    R        = Ri.mean()
    residu   = np.sum((Ri - R)**2)
    return xc, yc, R, residu


import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
from matplotlib import image as mpimg
from scipy import signal
from scipy import fftpack
import scipy.io

class Blurr_Fixer:
    def __init__(self, blurred_images, power=1,ifft_scale=1000, original_size=256, margin=0):
        self.blurred_images = blurred_images
        self.power = power
        self.ifft_scale = ifft_scale
        self.fixed = []
        self.margin = margin
        self.original_size = original_size
        self.F_images = [fftpack.fftshift(fftpack.fftn(img)) for img in blurred_images]

    def __get_weights_denom(self):
        weights_denom = np.zeros(self.F_images[0].shape)
        for mat in self.F_images:
            weights_denom = weights_denom + np.power(np.abs(mat), self.power)
        return weights_denom

    def unblur_images(self):
        denom = self.__get_weights_denom()
        accumulator = np.zeros(self.F_images[0].shape)
        for F in self.F_images:
            curr_weight = np.divide(np.power(np.abs(F), self.power), denom)
            accumulator = accumulator + np.multiply(F, curr_weight)
        fixed = fftpack.ifft2(fftpack.ifftshift(accumulator)).real
        fixed = np.divide(fixed,self.ifft_scale)
        # Crop
        size = self.original_size
        margin = self.margin
        self.fixed = fixed[margin:margin + size, margin:margin + size]
        return self.fixed

    def show_unblur_image(self,original_image):
        p1 = plt.subplot(1, 2, 1)
        plt.imshow(original_image.get_image(), cmap='gray')
        p1.set_title("Original")
        p2 = plt.subplot(1, 2, 2)
        plt.imshow(self.fixed, cmap='gray')
        p2.set_title("Fourier Burst Accumulation")
        plt.show()


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

L_v = 2.5e6  # J/kg
R_v = 461  # J/K/kg
T_0 = 273.15  # K
e_0 = 6.113  # hPa
c_p = 1004 #J/K/kg
epsilon = 0.622 #kg/kg

def get_psychrometric_constant(pressure):
    # We could use a variable c_p depending on moisture content of air, but I don't think we have that info
    return c_p/L_v * pressure / epsilon

def clausius_clapeyron_e_s(T, is_C=True):
    if is_C:
        T = as_kelvin(T)
    return e_0 * math.exp(L_v/R_v*(1/T_0 - 1/T)) #hPa * e^(J/kg/(J/kg/K) * 1/K) = hPa * unitless^unitless = hPa

def slope_e_s(T, is_C=True):
    if is_C:
        T = as_kelvin(T)
    return L_v/R_v * 1/(T*T) * clausius_clapeyron_e_s(T, is_C=False)

def as_kelvin(temp_in_C):
    return temp_in_C + 273.15

def plot_sat_vapor_pressure_and_slope():
    """
        Test to see if the slope equation is correct by comparing to finite difference.
    """
    temps = np.linspace(0, 40, num=100)
    delta_T = 40/100
    e_s = [clausius_clapeyron_e_s(temp) for temp in temps]
    finite_difference_delta = finite_difference(e_s, delta_T)
    delta = [slope_e_s(temp) for temp in temps]

    fig, ax = plt.subplots()

    ax.plot(temps, e_s, 'y', label="e_S")
    ax.plot(temps, delta, 'r', label="d(e_s)/dT")
    ax.plot(temps[0:-1], finite_difference_delta, 'g', label="Finite difference")
    plt.legend()

    fig.savefig("sat_vapor_pressure_and_slope.png")

def finite_difference(series, delta):
    brackets = zip(series[0:-1], series[1:])
    return [(top - bottom)/delta for (bottom, top) in brackets]

plot_sat_vapor_pressure_and_slope()


import numpy as np
import scipy.stats as stat

def pOri(theta=0):
    return 1 / (2 * np.pi)

def pOriLog(theta=0):
    return np.log(pOri())

def pCon(con, conZero):
    return np.exp(-con / conZero) / conZero

def pConLog(con, conZero=1):
    return np.log(pCon(con, conZero))

def meanResp(con, ori, oriPreferred, tuningWidth=1, sensitivity=1):
    return sensitivity * con * np.exp(2 * np.cos(tuningWidth * (oriPreferred - ori)))

def pRespGivenOriCon(resp, ori, con, oriPreferred, tuningWidth=1, sensitivity=1):
    mr = meanResp(con, ori, oriPreferred, tuningWidth, sensitivity)
    return np.pi * resp / (2 * mr * mr) * np.exp(-np.pi * resp * resp / (4 * mr * mr))

def pRespGivenOriConLog(resp, ori, con, oriPreferred, tuningWidth=1, sensitivity=1):
    return np.log(pRespGivenOriCon(resp, ori, con, oriPreferred, tuningWidth, sensitivity))

def pJointConOri(ori, con, conZero):
    return pOri(ori) * pCon(con, conZero)

def pJointConOriLog(ori, con, conZero):
    return pOriLog(ori) + pConLog(con, conZero)

def pJointAll(resp, ori, con, conZero, oriPreferred=0, tuningWidth=1, sensitivity=1):
    if con == 0 or resp == 0:
        return 0
    cond = pRespGivenOriCon(resp, ori, con, oriPreferred, tuningWidth, sensitivity)
    return cond * pJointConOri(ori, con, conZero)

def pJointAllLog(resp, ori, con, conZero, oriPreferred=0, tuningWidth=1, sensitivity=1):
    cond = pRespGivenOriConLog(resp, ori, con, oriPreferred, tuningWidth, sensitivity)
    return cond + pJointConOriLog(ori, con, conZero)

def inverseExponCDF(x, lmb=1):
    return - np.log(1 - x) / lmb


from numpy import linspace
from sympy import symbols

from math import *

# Definição da variável em uso -> f(x): x
x = symbols('x')

def Simpson(f, a, b, e):

    #Um numero muito pequeno
    d = 0.0001

    #Vetor de m pontos cobrindo o intervalo [a,b]
    xd = []
    yd = []

    for i in linspace(a,b,round((b-a)/d) ):
        xd.append(i)
        yd.append((f(i+4*d) - 4*f(i+3*d) + 6*f(i+2*d) - 4*f(i+d) +  f(i) )/(d**4))

    #Valor máximo da derivada segunda dos subintervalos
    Max = max(yd)
    Min = min(yd)
    M = abs(Max) if abs(Max)>abs(Min) else abs(Min)

    #Calculo de m
    m = ( ( ( (b-a)**5 )*M ) / (180*e))**(1/4)

    #O arredondamento deve ser feito para cima
    if m > round(m):
        m = round(m)+1
    else:
        m = round(m)

    #Deve-se tomar o primeiro número par, maior que m
    if m%2 is 1:
        m = m+1

    h = (b-a)/(m)
    s = f(a) + f(b)

    x = a +h

    for i in range(0,m-1,2):
        s = s + 4*f(x)
        x = x + 2*h

    x = a +2*h
    for i in range(1, m-2, 2):
        s = s + 2*f(x)
        x = x + 2*h

    Isr = (h/3)*s

    return Isr

if __name__ == "__main__":
    # INSTRUÇÕES
    print(' #########################################################')
    print(' ###             EXEMPLO DE ENTRADA:                   ###')
    print(' ###       x**2 + 5*x - 4  ou    cos(x)* x**2          ###')
    print(' #########################################################\n')

    # Entrada da função escolhida para o método
    '''
    funcao = input("Entre com uma funcao f(x): ")

    A = float(input("Entre com o ponto A: "))
    B = float(input("Entre com o ponto B: "))
    e = float(input("Entre com o erro e: "))
    '''

    funcao = 'x**2 + 1'
    A = 0
    B = 2
    e = 0.001

    f = lambda x: eval(funcao)

    val = Simpson(f, A, B, e)

    try:
        import matplotlib.pyplot as plt
        matplot = True

    except:
        print("MatplotLib indisponível!!")
        matplot = False

    if matplot:
        _sum = (abs(A)+abs(B))

        x = []
        y = []
        for dot in linspace(A-(1/4)*_sum, B+(1/4)*_sum, _sum*100 ):
            x.append(dot)
            y.append(f(dot))

        x_b = []
        y_b = []
        for i in linspace(A, B, _sum*100 ):
            x_b.append(i)
            y_b.append(f(i))

        plt.grid = True

        plt.ylabel('f(x)')
        plt.xlabel('x')
        plt.title('Método da Integração de 1/3 de Simpson para %s = %10.4f' %(funcao.replace('**','^').replace('*',''), val))

        plt.plot(x, y, '-', label = 'Função f(x)')
        plt.fill_between(x_b, y_b, 0 , facecolor = 'red', alpha = 0.5, label = "Área integrada")

        plt.legend(fancybox = True)
        plt.show()


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

# This program is based on the following program.
# https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/common/functions.py
# Copyright (c) 2016 Koki Saitoh
# The original program is released under the MIT License.
# https://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/LICENSE.md

# 必要なライブラリをインポート
import numpy as np

def sigmoid(x):
    """
    シグモイド関数
    """

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

def sigmoid_grad(x):
    """
    シグモイド関数を微分した関数
    """

    return (1.0 - sigmoid(x)) * sigmoid(x)

def softmax(x):
    """
    ソフトマックス関数
    """

    x = x - np.max(x, axis=-1, keepdims=True)
    return np.exp(x) / np.sum(np.exp(x), axis=-1, keepdims=True)

def cross_entropy_error(y, t):
    """
    交差エントロピー誤差を計算する関数
    """

    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)

    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] + 1e-7)) / batch_size


from src.functions.objectivefn import ObjectiveFn
import math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

class EasomFn(ObjectiveFn):

    def __init__(self):
        super(EasomFn, self).__init__(2)
        self.domain = (-10, 10)

    def get_minima(self):
        minima_coords = [math.pi for i in range(self.dims)]  # minima val is -1
        minima = tuple(minima_coords)
        return self.evaluate(minima)

    def evaluate(self, params):
        f = 0
        try:
            if self.dims != 2:
                raise Exception('Dimensions more than 2')
            if self.dims != len(params):
                raise Exception('Dimensions not matching params')

            X = params[0]
            Y = params[1]
            f = -1 * math.cos(X) * math.cos(Y) * math.exp(-1 * ((X - math.pi) ** 2 + (Y - math.pi) ** 2))
            return f
        except Exception as error:
            print('Exception in easom_fn.eval: '.repr(error))

    def graph_fn(self):
        X = np.linspace(self.domain[0], self.domain[1], 200)
        Y = np.linspace(self.domain[0], self.domain[1], 200)
        X, Y = np.meshgrid(X, Y)
        Z = -1 * np.cos(X) * np.cos(Y) * np.exp(-1 * ((X - math.pi) ** 2 + (Y - math.pi) ** 2))

        fig = plt.figure()
        ax = fig.gca(projection='3d')
        ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.plasma, linewidth=0, antialiased=False)
        plt.savefig('easom.png')

    def contour_plot(self, save_file_name, points):
        X = np.linspace(self.domain[0], self.domain[1], 200)
        Y = np.linspace(self.domain[0], self.domain[1], 200)
        X, Y = np.meshgrid(X, Y)
        Z = -1 * np.cos(X) * np.cos(Y) * np.exp(-1 * ((X - math.pi) ** 2 + (Y - math.pi) ** 2))

        plt.contour(X, Y, Z)
        for point in points:
            plt.scatter(point[0], point[1], marker='X', color='red')
        plt.savefig(save_file_name, dpi=300, bbox_inches='tight')
        plt.close()

    def is_defined_only_for_2d(self):
        return True

    def name(self):
        return "Easomfn"


import numpy as np
import os
from math import radians, cos, sin, asin, sqrt
import geopandas as geopd
import scipy.optimize
import itertools

R = 6378

##########################################################
def haversine(p1, p2, unit='km'):
    """Calculate the great circle distance (in kilometers) between two points
    on the earth (specified in decimal degrees). It expects points in the
    format (lon, lat)"""
    lonrad1, latrad1 = np.radians(p1)
    lonrad2, latrad2 = np.radians(p2)

    dlon = lonrad2 - lonrad1
    dlat = latrad2 - latrad1
    a = np.sin(dlat/2)**2 + np.cos(latrad1) * np.cos(latrad2) * np.sin(dlon/2)**2
    c = 2 * np.arcsin(np.sqrt(a))
    if unit in ['m', 'meters']: return c * R * 1000
    else: return c * R

##########################################################
def haversine_all(points, unit='km'):
    """Calculate the great circle distance (in meters) between two points
    on the earth (specified in decimal degrees) between every pair of points
    in @points. It expects points in the format (lon, lat)"""
    n = len(points)
    combs = np.array(list(itertools.combinations(list(range(n)), 2)))
    points = np.radians((points.copy()))

    lon1 = points[combs[:, 0], 0]
    lat1 = points[combs[:, 0], 1]
    lon2 = points[combs[:, 1], 0]
    lat2 = points[combs[:, 1], 1]

    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2
    c = 2 * np.arcsin(np.sqrt(a))
    if unit in ['m', 'meters']: return combs, c * R * 1000
    else: return combs, c * R

##########################################################
def get_shp_points(shppath):
    """Get points from @shppath and returns list of points, x and y """

    geodf = geopd.read_file(shppath)
    shapefile = geodf.geometry.values[0]
    if shapefile.geom_type == 'Polygon': return shapefile.exterior.xy
    elif shapefile.geom_type == 'MultiPolygon':
        polys = list(shapefile)
        areas = [p.area for p in polys]
        poly = polys[np.argmax(areas)]
        return poly.exterior.xy

##########################################################
def deg2num(lon_deg, lat_deg, zoom, imgsize=256):
    """From the (lat,lon) in degrees, get the (x, y) of the tile,
    as well as the pixel in the image."""
    lat_rad = math.radians(lat_deg)
    n = 2.0 ** zoom
    xtile = ((lon_deg + 180.0) / 360.0 * n)
    ytile = ((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
    return (int(xtile), int(ytile),
            int(math.modf(xtile)[0]*imgsize), int(math.modf(ytile)[0]*imgsize))

##########################################################
def num2deg(xtile, ytile, zoom):
    """From the tile x and y, get the (lat,lon) in degrees"""
    n = 2.0 ** zoom
    lon_deg = xtile / n * 360.0 - 180.0
    lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
    lat_deg = math.degrees(lat_rad)
    return (lon_deg, lat_deg)

##########################################################
def dist_to_deltalonlat(dist, lonref, latref):
    """Get the deltalon and deltalat based on a reference lat and lon.
    For the same distance, lon varies more than lat."""
    def get_delta_lon_from_d(lon1, lat1=latref, lon2=lonref, lat2=latref):
        return dist - haversine([lon1, lat1], [lon2, lat2])

    def get_delta_lat_from_d(lat1, lon1=lonref, lat2=latref, lon2=lonref):
        return dist - haversine([lon1, lat1], [lon2, lat2])

    lon2 = lonref + 20 # a large enough diff in lon to contain the @dist
    lon2 = scipy.optimize.bisect(get_delta_lon_from_d, lonref, lon2,
            xtol=0.000001, rtol=0.000001)

    lat2 = latref + 20 # a large enough diff in lon to contain the @dist
    lat2 = scipy.optimize.bisect(get_delta_lat_from_d, latref, lat2,
            xtol=0.000001, rtol=0.000001)

    return np.abs(lon2 - lonref), np.abs(lat2 - latref)


import numpy as np
import matplotlib.pyplot as plt
import sys
import math
import random
import operator

def euclidean(x, x_p):
    return ((x[0] - x_p[0]) ** 2 + (x[1] - x_p[1]) ** 2) ** 0.5

def greatest_euclidean(data, centers):
    maxi = {}
    for x in centers:
        for x_p in data:
            euc = euclidean(x, x_p)
            if x_p not in maxi:
                maxi[x_p] = 0
            maxi[x_p] += euc
    return max(maxi.items(), key=operator.itemgetter(1))[0]

# Uses a greedy approach, selects a data point at random and assigns this as a center for a classification
# it then finds the furthest data point from this and assigns this as a center and places it in the set
# the next center will be the furthest datapoint from all centers until all regions have a center
def gen_centers(M, data):
    centers = []
    N = len(data)
    rand = random.randint(0, N - 1)
    centers.append(data.pop(rand))
    center = (0, 0)
    classifiers = []
    for i in range(M - 1):
        center = greatest_euclidean(data, centers)
        data.remove(center)
        centers.append(center)

    for x in data:
        num = voronoi(x, centers)
        classifiers.append(num)
    return centers, classifiers

# Determine the Voronoi region for the data point. This basically just decides how to classify all the data points
# assigning it to the closest center by euclidean distance
def voronoi(x, centers):
    order = []
    for i in range(len(centers)):
        datapoint = centers[i]
        # Euclidean to x
        order.append((euclidean(x, datapoint), i))
    order.sort()
    g = order[0][1]
    return g

# Generates 10,000 random datapoints with x and y values between 0 and 1
def generate_data():
    data = []
    for x1_ in range(100):
        for x2_ in range(100):
            x1 = np.random.uniform(0, 1)
            x2 = np.random.uniform(0, 1)
            data.append((x1, x2))
    return data

def plot(M):
    data = generate_data()
    centers, classifers = gen_centers(M, data)
    unique=set(classifers)
    print(unique)
    plt.scatter(*zip(*data), c=classifers, cmap='rainbow')
    plt.scatter(*zip(*centers), c='black')
    plt.title('Greedy with {} Regions'.format(M))
    plt.xlabel('x1', color='#1C2833')
    plt.ylabel('x2', color='#1C2833')
    plt.grid()
    plt.show()

if __name__ == "__main__":
    # 10 Clusters for users
    regions = 10
    plot(regions)

    # Assumption: Users will be datapoints, users will create a voronoi region and counselors
    # will be assigned to their closest associated region.

    # Just using greedy. May add in branch and bound.


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 11 20:02:37 2017

@author: tzave
"""

import numpy as np
import solver

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

taus = np.arange(.1, 2., .1)
for tau in taus:
    result = solver.IterativeMethod(A, d, .5e-6, tau, 1000)
    x = result['x']
    iterations = result['iterations']
    print('tau=', tau, ': ', iterations, ', x=', x)
    delta = np.dot(A, x) - d
    print('Norm (Ax-d) =', np.sqrt(np.dot(delta, delta)))


import numpy as np
import numpy.linalg as la
from scipy import optimize  # type: ignore

from . import geom3d
from ._util import distance_point_point

def centroid_fit(points, weights=None):
    """Calculates the weighted average of a set of points
    This minimizes the sum of the squared distances between the points
    and the centroid.
    """
    if points.ndim == 1:
        return points
    return np.average(points, axis=0, weights=weights)

def _check_input(points, weights) -> None:
    """Check the input data of the fit functionality"""
    points = np.asarray(points)
    if weights is not None:
        weights = np.asarray(weights)

    if points.ndim != 2 or points.shape[1] != 3:
        raise ValueError(
            f"Input data has the wrong shape, expects points to be of shape ('n', 3), got {points.shape}"
        )
    if weights is not None and (weights.ndim != 1 or len(weights) != len(points)):
        raise ValueError(
            "Shape of weights does not match points, weights should be a 1 dimensional array of len(points)"
        )

def line_fit(points, weights=None) -> geom3d.Line:
    """Fits a line through a set points"""
    _check_input(points, weights)
    centroid = centroid_fit(points, weights)
    weights = 1.0 if weights is None else weights
    centered_points = points - centroid
    u, s, v = np.linalg.svd(
        np.matmul(weights * centered_points.transpose(), centered_points)
    )
    return geom3d.Line(anchor_point=centroid, direction=v[0])

def plane_fit(points, weights=None) -> geom3d.Plane:
    """Fits a plane through a set of points"""
    _check_input(points, weights)
    centroid = centroid_fit(points, weights)
    weights = 1.0 if weights is None else weights
    centered_points = points - centroid
    u, s, v = np.linalg.svd(
        np.matmul(weights * centered_points.transpose(), centered_points)
    )
    return geom3d.Plane(anchor_point=centroid, normal=v[2])

# TODO add weights
def fast_sphere_fit(points) -> geom3d.Sphere:
    """A fast algebraic circle fit, that uses a modified error function that
    is more sensitive to outliers
    """
    _check_input(points, None)
    A = np.append(points * 2, np.ones((points.shape[0], 1)), axis=1)
    f = np.sum(points ** 2, axis=1)
    C, _, _, _ = np.linalg.lstsq(A, f, rcond=None)
    center = C[0:3]
    radius = np.average(distance_point_point(points, center))
    return geom3d.Sphere(center=center, radius=radius)

def sphere_fit(
    points, weights=None, initial_guess: geom3d.Sphere = None
) -> geom3d.Sphere:
    """Fits a circle through a set of points"""
    _check_input(points, weights)
    initial_guess = initial_guess or fast_sphere_fit(points)

    def sphere_fit_residuals(center, points, weights):
        distances = distance_point_point(center, points)
        radius = np.average(distances, weights=weights)
        if weights is None:
            return distances - radius
        return (distances - radius) * np.sqrt(weights)

    results = optimize.least_squares(
        sphere_fit_residuals, x0=initial_guess.center, args=(points, weights)
    )
    if not results.success:
        raise RuntimeError(results.message)

    radius = np.average(distance_point_point(points, results.x), weights=weights)
    return geom3d.Sphere(center=results.x, radius=radius)

def cylinder_fit(points, weights=None, initial_guess: geom3d.Cylinder = None):
    """Fits a cylinder trough a set of points"""
    _check_input(points, weights)
    if initial_guess is None:
        raise NotImplementedError(
            "Cylinder fit currently does support running without an intial guess."
        )

    def cylinder_fit_residuals(anchor_direction, points, weights):
        line = geom3d.Line(anchor_direction[:3], anchor_direction[3:])
        distances = line.distance_to_point(points)
        radius = np.average(distances, weights=weights)
        if weights is None:
            return distances - radius
        return (distances - radius) * np.sqrt(weights)

    x0 = np.concatenate([initial_guess.anchor_point, initial_guess.direction])
    results = optimize.least_squares(
        cylinder_fit_residuals, x0=x0, args=(points, weights), ftol=1e-10
    )
    if not results.success:
        return RuntimeError(results.message)

    line = geom3d.Line(results.x[:3], results.x[3:])
    distances = line.distance_to_point(points)
    radius = np.average(distances, weights=weights)
    return geom3d.Cylinder(results.x[:3], results.x[3:], radius)

def circle3D_fit(points, weights=None, initial_guess: geom3d.Circle3D = None):
    """Fits a circle in three dimensions trough a set of points"""
    _check_input(points, weights)
    if initial_guess is None:
        raise NotImplementedError(
            "Circle3D fit currently does support running without an intial guess."
        )

    def circle_fit_residuals(circle_params, points, sqrt_w):
        circle = geom3d.Circle3D(
            circle_params[:3], circle_params[3:], la.norm(circle_params[3:])
        )
        distances = circle.distance_to_point(points)
        return distances * sqrt_w

    x0 = np.concatenate(
        [initial_guess.center, initial_guess.direction * initial_guess.radius]
    )
    results = optimize.least_squares(
        circle_fit_residuals,
        jac="2-point",
        method="lm",
        x0=x0,
        args=(points, 1.0 if weights is None else np.sqrt(weights)),
    )

    results = optimize.minimize(
        lambda *args: np.sum(circle_fit_residuals(*args) ** 2),
        x0=results.x,
        args=(points, 1.0 if weights is None else np.sqrt(weights)),
    )

    if not results.success:
        return RuntimeError(results.message)

    return geom3d.Circle3D(results.x[:3], results.x[3:], la.norm(results.x[3:]))

def torus_fit(points, weights=None, initial_guess: geom3d.Torus = None) -> geom3d.Torus:
    """Fits a torus trough a set of points"""
    _check_input(points, weights)
    if initial_guess is None:
        raise NotImplementedError(
            "Toru fit currently does support running without an intial guess."
        )

    def torus_fit_residuals(circle_params, points, weights):
        circle = geom3d.Circle3D(
            circle_params[:3], circle_params[3:], la.norm(circle_params[3:])
        )
        distances = circle.distance_to_point(points)
        radius = np.average(distances, weights=weights)
        weights = np.sqrt(weights) if weights is not None else 1.0
        return (distances - radius) * weights

    x0 = np.concatenate(
        [initial_guess.center, initial_guess.direction * initial_guess.major_radius]
    )

    results = optimize.least_squares(
        torus_fit_residuals,
        x0=x0,
        args=(points, weights),
    )

    if not results.success:
        raise RuntimeError(results.message)

    circle3D = geom3d.Circle3D(results.x[:3], results.x[3:], la.norm(results.x[3:]))
    distances = circle3D.distance_to_point(points)
    minor_radius = np.average(distances, weights=weights)
    return geom3d.Torus(
        results.x[:3], results.x[3:], la.norm(results.x[3:]), minor_radius
    )


import numpy
from amuse.units.trigo import cos, sin

def new_rotation_matrix(phi, theta, psi):
    """
    Return the rotation matrix, to rotate positions, around the x-axis (phi), y-axis (theta) and z-axis (psi).
    See wikipedia for reference
    """

    return numpy.array( (
        (cos(theta)*cos(psi), -cos(phi)*sin(psi) + sin(phi)*sin(theta)*cos(psi),  sin(phi)*sin(psi) + cos(phi)*sin(theta)*cos(psi)) ,
        (cos(theta)*sin(psi),  cos(phi)*cos(psi) + sin(phi)*sin(theta)*sin(psi), -sin(phi)*cos(psi) + cos(phi)*sin(theta)*sin(psi)) ,
        (-sin(theta)        ,  sin(phi)*cos(theta)                             ,  cos(phi)*cos(theta))
    ) )

def rotated(positions, phi, theta, psi):
    """
    Return the positions, rotated by phi, theta and psi around the x, y and z axes
    """
#    print phi, theta, psi
    rotation_matrix = new_rotation_matrix(phi, theta, psi)
#    print "RT=", rotation_matrix
    return positions.dot(rotation_matrix.transpose())

def rotate(particles, phi, theta, psi):
    """
    Rotate the positions and the velocities around 0,0,0.
    """
    particles.position = rotated(particles.position,  phi, theta, psi)
    particles.velocity = rotated(particles.velocity,  phi, theta, psi)

def add_spin(particles, omega):
    """
    Add solid-body rotation to the velocity of the particles, relative to the
    center-of-mass position.
    """
    if not omega.is_vector():
        omega = omega * [0.0, 0.0, 1.0]
    particles.velocity += omega.cross(particles.position - particles.center_of_mass())


import numpy as np

from supervised.nb_classifier import NBClassifier

EPSILON = 1E-16  # offset to avoid "divide by zero" errors

class GaussianNB(NBClassifier):

    def _pdf(self, x, mean, std):

        num = np.exp(-((x - mean)**2) / (EPSILON + 2 * std**2))
        den = np.sqrt(2 * np.pi * std**2) + EPSILON

        return num / den

    def _fit_evidence(self, X):

        feature_probas = []

        for feature in X.T:  # iterate through the features instead of the samples

            feature_probas.append(dict(mean=np.mean(feature),
                                       n=len(feature),
                                       std=np.std(feature, ddof=1)))

        return np.array(feature_probas)

    def _fit_likelihood(self, X, y):

        likelihood_ = []

        for c in self.classes_:

            samples = X[y == c]  # only keep samples of class c

            likelihood_.append(self._fit_evidence(samples))

        return np.array(likelihood_)

    def _get_evidence(self, sample):

        evidence = 1.0

        for i, feature in enumerate(sample):

            mean = self.evidence_[i]["mean"]
            std = self.evidence_[i]["std"]

            evidence *= self._pdf(feature, mean, std)

        return evidence

    def _get_likelihood(self, sample, c):

        likelihood = 1.0

        for i, feature in enumerate(sample):

            mean = self.likelihood_[c][i]["mean"]
            std = self.likelihood_[c][i]["std"]

            likelihood *= self._pdf(feature, mean, std)

        return likelihood

    def _update_evidence(self, X):

        for i, feature in enumerate(X.T):   # iterate through the features instead of the samples

            self.evidence_[i] = self._update_mean_std_n(feature, self.evidence_[i])

        return self.evidence_

    def _update_likelihood(self, X, y):

        for c in self.classes_:

            samples = X[y == c]  # only keep samples of class c

            for i, feature in enumerate(samples.T):  # iterate through the features instead of the samples

                self.likelihood_[c][i] = self._update_mean_std_n(feature, self.likelihood_[c][i])

        return self.likelihood_

    def _update_mean_std_n(self, feature, mean_std_n):

        old_m = mean_std_n["mean"]
        old_std = mean_std_n["std"]
        old_n = mean_std_n["n"]

        n = old_n + len(feature)

        m = (old_m * old_n + np.mean(feature) * n) / (old_n + n)

        s = np.sqrt((old_n * (old_std**2 + (old_m - m)**2)
                     + len(feature) * (np.var(feature)
                                       + (np.mean(feature) - m)**2)
                     ) / (old_n + len(feature)))

        return dict(mean=m, std=std, n=n)


# -*- coding: utf-8 -*-
# @Date  : 2020/5/27
# @Author: Luokun
# @Email : olooook@outlook.com

import numpy as np

class SimpleEM:
    """
    Expectation-maximization algorithm(期望最大算法,三硬币模型)
    """

    def __init__(self, prob: list, max_iter=100):
        self.prob, self.max_iter = np.array(prob), max_iter

    def fit(self, X: np.ndarray):
        for _ in range(self.max_iter):
            M = self._expect(X)  # E步
            self._maximize(X, M)  # M步

    def _expect(self, X: np.ndarray):  # E步
        p1, p2, p3 = self.prob
        a = p1 * (p2 ** X) * ((1 - p2) ** (1 - X))
        b = (1 - p1) * (p3 ** X) * ((1 - p3) ** (1 - X))
        return a / (a + b)

    def _maximize(self, X: np.ndarray, M: np.ndarray):  # M步
        self.prob[0] = np.sum(M) / len(X)
        self.prob[1] = np.sum(M * X) / np.sum(M)
        self.prob[2] = np.sum((1 - M) * X) / np.sum(1 - M)

# EM算法与高斯混合模型可参见./gmm.py


from collections import deque
import numpy as np

class FiniteDifference:
    def __init__(self, step_size, max_order=4):
        self.step_size = step_size
        self.max_order = min(max_order, 4)
        self.f_deque = deque()
        self.fd_equations = [
            lambda f, h: (-1*f[1]+1*f[0])/(1*1.0*h**1),
            lambda f, h: (1*f[2]-4*f[1]+3*f[0])/(2*1.0*h**1),
            lambda f, h: (-2*f[3]+9*f[2]-18*f[1]+11*f[0])/(6*1.0*h**1),
            lambda f, h: (3*f[4]-16*f[3]+36*f[2]-48*f[1]+25*f[0])/(12*1.0*h**1)
        ]

    def differentiate(self, value):
        # Append value to function deque
        self.f_deque.appendleft(value)
        len_f_deque = len(self.f_deque)

        if len_f_deque == 1:
            # If this is out first value, return 0.
            return np.zeros(np.shape(value))
        elif len_f_deque > (self.max_order + 1):
            # If we have more items then we need for our derivative.
            # pop the extra one from the deque.
            self.f_deque.pop()

        # Calculate derivative and return.
        return self.fd_equations[len(self.f_deque) - 2](self.f_deque, self.step_size)


""""
Author : Aditya Jain
Contact : https://adityajain.me
"""

import numpy as np

def root_mean_squared_error(y_true,y_pred):
	"""
	Compute root mean square error

	Parameters
	----------
	y_pred : predicted values

	y_true : True values

	Returns
	-------
	rmse

	"""
	loss = np.square(y_pred-y_true)
	cost = np.sqrt( np.mean(loss) )
	return cost

def mean_squared_error(y_true,y_pred):
	"""
	Compute mean square error

	Parameters
	----------
	y_pred : predicted values

	y_true : true values

	Returns
	-------
	mse

	"""
	loss = np.square(y_pred-y_true)
	cost = np.mean(loss)
	return cost

def mean_absolute_error(y_true,y_pred):
	"""
	Compute mean absolute error

	Parameters
	----------
	y_pred : predicted values

	y_true : true values

	Returns
	-------
	mae

	"""
	loss = abs(y_pred-y_true)
	cost = np.mean(loss)
	return cost

def r2_score(y_true,y_pred):
	"""
	Compute Coefficient of Determinance, r2 score

	r2 score = ESS/TSS = 1 - RSS/TSS = 1 - (y_true - y_pred)^2 / (y_true - y_true.mean())

	Parameters
	----------
	y_pred : predicted values

	y_true : true values

	Returns
	-------
	r2_score
	"""
	return 1-(np.sum((( y_true - y_pred)**2))/np.sum((y_true-np.mean(y_true))**2))


# -*- coding: utf-8 -*-
"""Linear_fourier_filter_exercise.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1KQlihNHOyF1_p6he0IWGrdA23wIfoUvA
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import ascent, face

# Change this to an image of your choosing!
#image = ascent()  # Boring staircase
image = face().mean(axis=2) # Cute little friend face

image = (image - image.mean()) / image.std()
n_x, n_y = image.shape

plt.imshow(image, cmap='Greys_r')

from numpy.fft import fft2, ifft2, fftshift

image_ft = fftshift(fft2(image))
amp = image_ft.real
phase = np.arctan2(image_ft.imag, image_ft.real)

X,Y = np.meshgrid( np.linspace(-.5, .5, n_y), np.linspace(-.5,.5, n_x) )
k = (X**2 + Y**2)**.5

# Change this filter
filt = np.ones_like(image)

# Example filters
#filt = np.exp(-100*k)

#filt = k < .01

#filt = k > .05

#filt = np.sin(100*k**2)

#x_sh,y_sh = 100,100
#filt = np.exp( -1j *2*np.pi * ((x_sh * X) + (y_sh * Y)))

image_filtered = ifft2( fftshift(image_ft * filt) ).real

plt.imshow(image_filtered, cmap='Greys_r')
plt.show()


import numpy as np
import math
from scipy.special import erfc, erfcinv

def phi(input):
    """Phi function.

    :param input:
      Float (scalar or array) value.
    :returns:
      phi(input).
    """
    return 0.5 * erfc(-input/np.sqrt(2))

def invphi(input):
    """Inverse phi function.

    :param input:
      Float (scalar or array) value.
    :returns:
      invphi(input).
    """
    return -1 * np.sqrt(2) * erfcinv(input/0.5)

def calcEmpiricalProbFromValue(G, e, value):
    """Calculate the empirical probability of a given value of loss (fatalities, dollars).

    :param G:
      Input G statistic.
    :param e:
      Expected number of losses.
    :param value:
      Number of losses for which corresponding probability should be calculated.
    :returns:
      Probability that value will not be exceeded.
    """
    e = e + 0.00001
    value = value + 0.00001
    p = phi((math.log(value) - math.log(e))/G)
    return p

def calcEmpiricalValueFromProb(G, e, p):
    """ Calculate the loss value given an input probability.

    :param G:
      Input G statistic.
    :param e:
      Expected number of losses.
    :param p:
      Input probability for which corresponding loss value should be calculated.
    :returns:
      Number of losses.
    """
    e = e + 0.00001
    value = math.exp(G * invphi(p) + math.log(e))
    return value

def calcEmpiricalProbFromRange(G, e, drange):
    """Calculate the empirical probability of a given loss range.

    :param G:
      Input G statistic.
    :param e:
      Expected number of losses.
    :param drange:
      Two-element sequence, containing range of losses over which probability should be calculated.
    :returns:
      Probability that losses will occur in input range.
    """
    e = e + 0.00001
    if e < 1:
        #e = 0.1
        if G > 1.7:
            G = 1.7613
    if len(drange) == 2:
        if (e-0) < 0.001:
            e = 0.5
        fmin = drange[0] + 0.00001
        fmax = drange[1] + 0.00001
        p1 = phi((math.log(fmax) - math.log(e))/G)
        p2 = phi((math.log(fmin) - math.log(e))/G)
        p = p1-p2
        return p
    else:
        psum = 0
        for i in range(0, len(drange)-1):
            fmax = drange[i+1] + 0.00001
            fmin = drange[i] + 0.00001
            p1 = phi((math.log(fmax) - math.log(e))/G)
            p2 = phi((math.log(fmin) - math.log(e))/G)
            psum += (p1-p2)
        return psum


# -*- encoding: utf-8 -*-
'''
@File    :   LDA_multi_class.py
@Contact :   guzhouweihu@163.com

@Modify Time      @Author        @Version    @Desciption
------------      -----------    --------    -----------
2020/1/3 15:21   guzhouweihu      1.0         None
'''

import numpy as np
import matplotlib.pyplot as plt

def read_iris():
    from sklearn.datasets import load_iris
    from sklearn import preprocessing
    data_set = load_iris()
    data_x = data_set.data
    label = data_set.target + 1
    #preprocessing.scale(data_x, axis=0, with_mean=True, with_std=True, copy=False)
    return data_x, label

def class_mean(data, label, clusters):
    mean_vec = []
    for c in range(1, clusters + 1):
        mean_vec.append(np.mean(data[label == c, :], axis=0))

    return np.array(mean_vec)

def with_class_SW(data, label, clusters):
    m = data.shape[1]
    SW = np.zeros((m, m))
    mean_vec = class_mean(data, label, clusters)

    for c, mv in zip(range(1, clusters + 1), mean_vec):
        class_sc_mat = np.zeros((m, m))

        SW += (data[label == c] - mv).T @ (data[label == c] - mv)

    return SW

def between_class_SB(data, label, clusters):
    m = data.shape[1]
    all_mean = np.mean(data, axis=0)
    SB = np.zeros((m, m))
    mean_vec = class_mean(data, label, clusters)
    for c, mean_vec in enumerate(mean_vec):
        n = data[label == c + 1, :].shape[0]
        mean_vec = mean_vec[:, np.newaxis]
        all_mean = all_mean.reshape(-1, 1)
        SB += n * ((mean_vec - all_mean) @ (mean_vec - all_mean).T)

    return SB

def LDA(data, label, clusters):
    S_W = with_class_SW(data, label, clusters)
    S_B = between_class_SB(data, label, clusters)
    eig_vals, eig_vecs = np.linalg.eig(np.linalg.inv(S_W) @ S_B)

    for i in range(len(eig_vals)):
        eigvec_sc = eig_vecs[:, i].reshape(4, 1)
        print('\nEigenvector {}: \n{}'.format(i + 1, eigvec_sc.real))
        print('Eigenvalue {:}: {:.2e}'.format(i + 1, eig_vals[i].real))

    eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:, i])
                 for i in range(len(eig_vals))]
    eig_pairs = sorted(eig_pairs, key=lambda k: k[0], reverse=True)
    W = np.hstack((eig_pairs[0][1].reshape(-1, 1),
                   eig_pairs[1][1].reshape(-1, 1)))
    print('Matrix W:\n', W.real)
    print(data.dot(W))
    return W

def plot_lda():
    data, labels = read_iris()
    W = LDA(data, labels, 3)

    Y = data.dot(W)
    # print Y
    ax = plt.subplot(111)
    for label, marker, color in zip(
            range(1, 4), ('^', 's', 'o'), ('blue', 'red', 'green')):
        plt.scatter(x=Y[:, 0][labels == label],
                    y=Y[:, 1][labels == label],
                    marker=marker,
                    color=color,
                    alpha=0.5,
                    )
    plt.xlabel('LDA1')
    plt.ylabel('LDA2')
    plt.title('LDA: Iris projection onto the first 2 linear discriminants')
    plt.show()

def default_plot_lda():
    Y = sklearnLDA()
    data, labels = read_iris()
    ax = plt.subplot(111)
    for label, marker, color in zip(
            range(1, 4), ('^', 's', 'o'), ('blue', 'red', 'green')):
        plt.scatter(x=Y[:, 0][labels == label],
                    y=Y[:, 1][labels == label],
                    marker=marker,
                    color=color,
                    alpha=0.5,
                    )
    plt.xlabel('LDA1')
    plt.ylabel('LDA2')
    plt.title('LDA:default')

    plt.show()

def sklearnLDA():
    from sklearn import datasets
    from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA

    iris = datasets.load_iris()

    X = iris.data
    y = iris.target
    target_names = iris.target_names

    lda = LDA(n_components=2)
    X_r2 = lda.fit(X, y).transform(X)
    return X_r2

if __name__ == "__main__":
    # lda()
    sklearnLDA()
    plot_lda()
    default_plot_lda()


from numpy import *
from math import sqrt

# Input: expects Nx3 matrix of points
# Returns R,t
# R = 3x3 rotation matrix
# t = 3x1 column vector

def rigid_transform_3D(A, B):
    assert len(A) == len(B)

    N = A.shape[0]; # total points

    centroid_A = mean(A, axis=0)
    centroid_B = mean(B, axis=0)

    # centre the points
    AA = A - tile(centroid_A, (N, 1))
    BB = B - tile(centroid_B, (N, 1))

    # dot is matrix multiplication for array
    H = transpose(AA) * BB

    U, S, Vt = linalg.svd(H)

    R = Vt.T * U.T

    # special reflection case
    if linalg.det(R) < 0:
       print "Reflection detected"
       Vt[2,:] *= -1
       R = Vt.T * U.T

    t = -R*centroid_A.T + centroid_B.T

    print t

    return R, t

# Test with random data

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

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

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

# number of points
n = 10

A = mat(random.rand(n,3));
B = R*A.T + tile(t, (1, n))
B = B.T;

# recover the transformation
ret_R, ret_t = rigid_transform_3D(A, B)

A2 = (ret_R*A.T) + tile(ret_t, (1, n))
A2 = A2.T

# Find the error
err = A2 - B

err = multiply(err, err)
err = sum(err)
rmse = sqrt(err/n);

print "Points A"
print A
print ""

print "Points B"
print B
print ""

print "Rotation"
print R
print ""

print "Translation"
print t
print ""

print "RMSE:", rmse
print "If RMSE is near zero, the function is correct!"


# TEST: Numeric Integration using Trapezoidal and Simpson methods

import matplotlib.pyplot as plt
import numpy as np
from quad import quad_trapezoid, quad_simpson, quad_romberg

#f = lambda x: 0.2 + 25*x - 200*x**2 + 675*x**3 - 900*x**4 + 400*x**5
#a = 0.0;
#b = 0.8;
#Iexp = 1.640533  # Expected integral value

f = lambda x: np.sin(x)
a = 0.0;
b = np.pi
Iexp = -np.cos(b) + np.cos(a)  # Expected integral value

# n is the number of integration nodes (segments = n-1)
for n in range(2, 11, 2):
    print('-----------------------------------')
    print('n = {}'.format(n))
    # Trapezoid method with relative error
    I = quad_trapezoid(a, b, f, n-1)
    E = abs(I - Iexp) / Iexp
    print("> Trapezoid I = {}, E = {}".format(I, E));
    # Simpson method with relative error
    I = quad_simpson(a, b, f, n-1)
    E = abs(I - Iexp) / Iexp
    print("> Simpson   I = {}, E = {}".format(I, E));
    # Romberg method with relative error
    I = quad_romberg(a, b, f, n-1)
    E = abs(I - Iexp) / Iexp
    print("> Romberg   I = {}, E = {}".format(I, E));

# Plot using the last approximation number of nodes
N = 1000    # Number of points for a smooth function plot
X = np.linspace(a, b, N)
plt.plot(X, f(X))
X = np.linspace(a, b, n)
for x in X:
    plt.plot([x,x], [0, f(x)],
             color='black', linestyle='dashed', linewidth=1)
plt.plot(X, f(X), color='black', linestyle='dashed', linewidth=1)
plt.grid(True)
plt.show()


import numpy
import scipy.spatial as spatial
import pylab

sigma = 10.

def K(Xi, Xj, s):
    return numpy.exp( - ( (numpy.linalg.norm(Xi - Xj) ** 2) / (2 * (s**2)) ) )

# Entrée :
#  - X l'ensemble des points
#  - k le nombre de clusters à former
#  - assign_0 l'assignation courante des points à un cluster
#  - M la matrice des distances entre les points
# Sortie :
#  - assign la nouvelle assignation des points à un cluster
#  - cost la fonction de cout (doit etre décroissante)
def ppcentroide(X, k, assign_0, M):
    global sigma
    n = len(X)
    cost = 0.

    # assign[i] = k ssi le point i est dans le cluster k
    assign = numpy.zeros((n, ), dtype=numpy.int32)

    # Dist_k[k] = inertie interne au cluster k
    # Size_k[k] = taille du cluster k
    Dist_k = numpy.zeros((k, ))
    Size_k = numpy.zeros((k, ), dtype=numpy.int32)

    #    # Calcul des deux quantités définies ci-dessus
    #    for i_k in range(k):
    #        d_tmp = 0
    #        C_size = 0
    #        for j1 in range(n):
    #            if (assign_0[j1] == i_k):
    #                C_size +=1
    #                for j2 in range(n):
    #                    if (assign_0[j2] == i_k):
    #                        d_tmp += M[j1][j2]
    #        Size_k[i_k] = C_size
    #        Dist_k[i_k] = d_tmp/C_size**2

    # Calcul des deux quantités définies ci-dessus
    for i in range(n):
        i_k = assign_0[i]
        Size_k[i_k] += 1
        Dist_k[i_k] += M[i][i]
        for j in range(i+1,n):
            if i_k == assign_0[j]:
                Dist_k[i_k] += 2*M[i][j]
    for i in range(k):
        Dist_k[i] /= Size_k[i]**2

    # Calcul des nouvelles assignations
    for i in range(n):
        # d_min = "distance" de i au cluster auquel il est assigné pour le moment
        d_min = numpy.inf
        # Pour chaque cluster k...
        for i_k in range(k):
            # d_k = "distance" de i au cluster k
            d_k = 0

            d_tmp = 0
            for j in range(n):
                if assign_0[j] == i_k:
                    d_tmp += M[i][j]
            d_k += -2*d_tmp/Size_k[i_k]

            d_k += M[i][i]

            d_k += Dist_k[i_k]

            if d_k <= d_min:
                assign[i] = i_k
                d_min = d_k
        cost += d_min

    return assign,cost

def kmeans(X, k, n_iter):
    n = len(X)
    cost = numpy.inf

    # Pré-calcul des distances entre les clusters
    M = [[K(X[i], X[j], sigma) for j in range(n)] for i in range(n)]

    # Assignation aléatoire des points aux clusters
    assign = [numpy.random.randint(0,k) for i in range(n)]
    for i in range(k):
        assign[i] = i # Pour etre sur d'avoir au moins 1 point par cluster

    for iter in range(n_iter):
        assign,cost = ppcentroide(X, k, assign, M)
        print(iter, cost)
    assign,cost = ppcentroide(X, k, assign, M)
    return assign,cost

### Zone de tests

# Fonctions utiles
def read_data(file_name, X):
    f = open(file_name, 'r')
    data = f.readlines()
    for line in data:
        X.append((map(float, filter(lambda x: x <> '\n', line.split("\t")))))
    f.close()

def write_cluster(file_name, assign, n_cluster, n_iter, s):
    n = len(assign)
    f = open(file_name, 'w')
    f.write("n_cluster = " + str(n_cluster) + "\n")
    f.write("n_iter = " + str(n_iter) + "\n")
    f.write("n_data = " + str(n) + "\n")
    f.write("seed = " + str(s) + "\n")
    for i in range(n):
        f.write(str(assign[i]) + "\n")
    f.close()

# Vrais tests

f = 'cinq_classes1'

X = []
read_data('./data/' + f + '/' + f + '.txt',X)

s = 0
n = len(X)
k = 5
n_iter = 10

numpy.random.seed(s)
assign,cost = kmeans(numpy.array(X), k, n_iter)

pylab.figure()
colors = "rbgkmc"
for i_k in range(k):
    pylab.plot([X[i][0] for i in range(n) if assign[i] == i_k],
               [X[i][1] for i in range(n) if assign[i] == i_k],
               colors[i_k] + ".")
pylab.show()

write_cluster('./data/' + f + '/' + 'kernel' + '.txt', assign, k, n_iter, s)


# https://github.com/zhangqi-chen/Algorithms-Stanford/blob/master/4%20Shortest%20Paths%20Revisited%2C%20NP-Complete%20Problems%20and%20What%20To%20Do%20About%20Them/Week%202/4wk1_a2.py
# 愚直 O(N!)→動的計画法bitDP O(2ˆN * Nˆ2)
import numpy as np
from itertools import combinations

f = open('sample-data/tsp.txt', 'r')
ls = f.readlines()[1:]
graph = [list(map(float, i.split(' '))) for i in ls]

def dist(i, j):
    return np.sqrt((graph[i][0]-graph[j][0])**2+(graph[i][1]-graph[j][1])**2)

N = len(graph)
dic1 = {frozenset([0]): {0: 0}}

for m in range(1, N):
    comb = list(combinations(range(1, N), m))
    dic2 = {frozenset(comb[i]): {list(comb[i])[j]: 0 for j in range(m)} for i in range(len(comb))}
    print(m, len(dic2))
    for s in dic2:
        for j in s:
            ans = []
            if m == 1:
                dic2[s][j] = dist(0, j)
            else:
                sj = set(s)
                sj.remove(j)
                dic2[s][j] = min([dic1[frozenset(sj)][k]+dist(k, j) for k in sj if k != j])
    dic1 = dic2.copy()

tsp = min([dic2[frozenset(comb[0])][j]+dist(0, j) for j in range(1, N)])
print([dic2[frozenset(comb[0])][j]+dist(0, j) for j in range(1, N)])
print(tsp)


#############################################
# University of Idaho                       #
# Carlos Santos                             #
# ECE 351-51                                #
# Lab 10                                    #
# 4/8/2020                                  #
#                                           #
#                                           #
#############################################

import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as sig
import control as con

# RLC Circuit Parameters
R = 1000
L = 27e-3
C = 100e-9

#%% 3.3.1, 3.3.2, 3.3.3
steps = 1e3
omega = np.arange(1e3,1e6+steps,steps)

mag_H = 20*np.log10((omega/(R*C))/(np.sqrt(((1/(L*C))-omega**2)**2 + (omega/(R*C))**2)))
phase_H = ((np.pi/2)-np.arctan((omega/(R*C))/(-omega**2 + 1/(L*C)))) * 180/np.pi # phase is in degrees

for i in range(len(phase_H)):
    if (phase_H[i] > 90):
        phase_H[i] = phase_H[i] - 180

plt.figure(figsize=(10,7))
plt.subplot(2,1,1) #Top Figure
plt.ylabel("Mag")
plt.grid()
plt.semilogx(omega, mag_H)
plt.title("hand solved")
plt.subplot(2,1,2) # Bottom Figure
plt.xlabel("w")
plt.ylabel("Phase")
plt.grid()
plt.semilogx(omega, phase_H)
plt.show()

num_H = [1/(R*C),0]
den_H = [1,1/(R*C),1/(L*C)]
sys = sig.TransferFunction(num_H,den_H)
ang,mag,phase = sig.bode(sys,omega)

plt.figure(figsize=(10,7))
plt.subplot(2,1,1) #Top Figure
plt.ylabel("Mag")
plt.grid()
plt.semilogx(ang, mag)
plt.title("sig.bode()")
plt.subplot(2,1,2) # Bottom  Figure
plt.xlabel("w")
plt.ylabel("Phase")
plt.grid()
plt.semilogx(ang, phase)
plt.show()

sys = con.TransferFunction(num_H,den_H)
_ = con.bode(sys, omega, dB=True,Hz=True, deg=True, Plot=True)

#%% 4.4.1, 4.4.2, 4.4.3, 4.4.4

steps = 1e-9
t = np.arange(0, 0.01+steps, steps)
x = (np.cos(2*np.pi*100*t)+ np.cos(2*np.pi*3024*t) + np.sin(2*np.pi* 50e3*t))

num_Z, den_Z = sig.bilinear(num_H, den_H, 1/steps)
xFiltered = sig.lfilter(num_Z, den_Z, x)

plt.figure(figsize=(10,7))
plt.subplot(2,1,1) #Top Figure
plt.grid()
plt.plot(t,x)
plt.title("Unfiltered Signal")
plt.subplot(2,1,2)
plt.plot(t,xFiltered)
plt.title("Filtered Signal")
plt.grid()
plt.show()


# Plots L2, L1 and Huber losses.

import numpy as np
import matplotlib.pyplot as plt
import os
figdir = os.path.join(os.environ["PYPROBML"], "figures")
def save_fig(fname): plt.savefig(os.path.join(figdir, fname))

delta = 1.5
huber = lambda x: (x**2/2) * (x <= delta) + (delta * abs(x) - delta**2/2) * (x > delta)
l2 = lambda x: abs(x)**2
l1 = abs

funs = [l2, l1, huber]
styles = ['r-', 'b:', 'g-.']
labels = ['l2', '11', 'huber']
x = np.arange(-3, 3, .01)

for i, fun in enumerate(funs):
  plt.plot(x, fun(x), styles[i], label=labels[i])

plt.axis([-3, 3, -0.5, 5])
plt.legend()
save_fig(os.path.join(figdir, 'huberLossPlot.pdf'))
plt.show()


import numpy as np
from nayzakflow.utils import _onehot

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

def _diff_sigmoid(z):
    return sigmoid(z)*(1-sigmoid(z))

def tanh(z):
    return np.tanh(z)

def _diff_tanh(z):
    return 1-np.square(tanh(z))

def relu(z):
    return np.maximum(0,z)

def _diff_relu(z):
    a= np.zeros_like(z,dtype='int')
    a[z>0] = 1
    return a

def leaky_relu(z):
    return np.maximum(z,0.1*z)

def _diff_leaky_relu(z):
    a= np.zeros_like(z,dtype='int')+0.1
    a[z>0] = 1
    return a

def identity(z):
    return z

def _diff_identity(z):
    return 1

def softmax(z):
    exp = np.exp(z)
    tot= exp.sum(axis=0)
    t= exp/tot
    return t

def _diff_softmax(z,y):
    yhat_r = softmax(z)
    onehotY = _onehot(y,z.shape[0])
    one_yi = onehotY *-1*(1-yhat_r)
    z=(1-onehotY)*yhat_r
    return one_yi +z

def get_activations():
    return {"identity":identity, "relu":relu, "softmax":softmax,"tanh":tanh, "sigmoid":sigmoid, "leaky_relu":leaky_relu}

def get_activations_diff():
    return {"identity":_diff_identity, "relu":_diff_relu, "softmax":_diff_softmax,"tanh":_diff_tanh, "sigmoid":_diff_sigmoid, "leaky_relu":_diff_leaky_relu}


# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

import numpy as np
import scipy.linalg as la
from typing import Tuple

def procrustes_error(
        target_matrix: np.ndarray,
        matrix_to_rotate: np.ndarray
) -> Tuple[np.ndarray, np.ndarray]:
    """
    Procrustes rotation rotates a matrix to maximum similarity with a target matrix minimizing sum of squared
    differences. Procrustes rotation is typically used in comparison of ordination results. It is particularly useful
    in comparing alternative solutions in multidimensional scaling.

    For more information:
    https://www.rdocumentation.org/packages/vegan/versions/2.4-2/topics/procrustes

    :param numpy.ndarray target_matrix: A matrix representing an embedding
    :param numpy.ndarray matrix_to_rotate: A matrix representing an embedding which will be rotated
    :return: The error which is the difference between the two matrices and the transformation matrix
    """
    dimension = min(target_matrix.shape[1], matrix_to_rotate.shape[1])
    reduced_target_matrix = target_matrix[:, :dimension]
    reduced_matrix_to_rotate = matrix_to_rotate[:, :dimension]

    tmp = np.dot(reduced_target_matrix.T, reduced_matrix_to_rotate)

    left_singular_vectors, eigenvalues, right_singular_vectors = la.svd(tmp)
    w = left_singular_vectors.dot(right_singular_vectors)
    error = la.norm(reduced_target_matrix.dot(w) - reduced_matrix_to_rotate)

    return error, w


import numpy as np
from scipy.stats import chi2

class MahalanobisDistance:

    """
    Description:
    ------------

    Class that computes the mahalanobis distance in order to detect multidimensional outliers.
    For more info, check:

        https://en.wikipedia.org/wiki/Mahalanobis_distance
        https://blogs.sas.com/content/iml/2012/02/15/what-is-mahalanobis-distance.html

    Parameters:
    -----------

        p_value [optional float]: probability of obtaining test results at least as extreme as the results actually
                         observed, under the assumption that the null hypothesis is correct

                          H0: mu_i = mu_j / i != j
                          H1: mu_i != mu_j

                          * mu_i -> mean of i

                         All the records that have a p-value lower than self.p_value will be detected as outlier

    Important:
    ----------

    This distance only works with 2 or more variables. In case of using 1 variable it will not work and it should be
    fine using the z_score.
    """

    def __init__(self, p_value=0.05):

        self.p_value = p_value

    def compute_mahalanobis_individual(self, vector, means_vector, cov_matrix):

        x_mu = vector - means_vector
        inv_covmat = np.linalg.inv(cov_matrix)

        distance = np.dot(np.dot(x_mu, inv_covmat), x_mu.T)
        p_value = 1 - chi2.cdf(distance, len(vector) - 1)

        if p_value < self.p_value:
            print(f"The record {vector} was detected as an outlier")

    def multidimensional_outliers(self, data, means_vector, cov_matrix):

        for i in range(data.shape[1]):
            self.compute_mahalanobis_individual(data[:, i], means_vector, cov_matrix)


import numpy as np
import matplotlib.pyplot as plt
import matplotlibex as plx

def kern_RBF(x1, x2):
    return np.exp(-20.5*np.sum((x1-x2)**2))

def gramm_matrix(f_kern, X, Xprime=None):
    if Xprime is None:
        Xprime = X
    N = X.shape[0]
    K = np.zeros([N, N])
    K = np.array([[f_kern(xi, xj) for xj in Xprime] for xi in X])
    return K

def gp_sample(mean, cov):
    return np.random.multivariate_normal(mean=mean, cov=cov)

def gp_conditional_pdf(f_kern, Xstar, X, Y, f_mean=None):
    if f_mean is None:
        f_mean = lambda x: 0
    Kxx = gramm_matrix(f_kern, X) + 1.0e-2 * np.diag(np.ones(X.shape[0]))
    Kxstarx = gramm_matrix(f_kern, Xstar, X)
    Ystar_mean = f_mean(Xstar) + Kxstarx.dot(np.linalg.inv(Kxx).dot(Y))
    Ystar_cov = gramm_matrix(f_kern, Xstar) - Kxstarx.dot(np.linalg.inv(Kxx).dot(Kxstarx.T))
    return Ystar_mean, Ystar_cov

if __name__ == "__main__":
    np.random.seed(5)
    N = 100
    D = 1
    X = np.reshape(np.random.uniform(size=N*D), [N, D])
    X = np.sort(X, axis=0)
    K = gramm_matrix(f_kern=kern_RBF, X=X)
    Y = gp_sample(mean=np.zeros(N), cov=K)
    np.random.seed(12)
    istart = [10, 41, 95, 70, 80]
    a = np.array(list(set(range(N)) - set(istart)))
    inds = np.concatenate([istart, np.random.choice(a=a, size=N-len(istart), replace=False)])
    Ninds = [0, 1, 2, 3, 4, 5, 10, N]
    for Nind in Ninds:
        ind = np.sort(inds[:Nind])
        Xind = X[ind]
        Yind = Y[ind]
        Xstar = np.linspace(0, 1.0, N)
        Ystar_mean, Ystar_cov = gp_conditional_pdf(f_kern=kern_RBF, Xstar=Xstar, X=Xind, Y=Yind)
        Ystar_err = np.diag(Ystar_cov) + 0.16
        fig = plt.figure(figsize=(5, 5))
        plt.title("#IP={}".format(Nind))
        plt.plot(X, Y + 0.1*np.random.normal(size=N), "+r")
        plt.plot(Xstar, Ystar_mean)
        plt.plot(Xind, Yind, "ok")
        plt.fill(np.concatenate([Xstar, Xstar[::-1]]),
                np.concatenate([Ystar_mean - Ystar_err, (Ystar_mean + Ystar_err)[::-1]]), alpha=.2)
        plt.ylim([-3, 1.5])
        plt.savefig("./IP({}).pdf".format(Nind))
        plt.close(fig)