import math
import numpy as np

from utils.structure import Shape

class CoreAlign(object):
    '''
    Methods that define core matrix transformations
    '''
    def translate(self, shape, Tx, Ty):
        '''
        Translate a shape according to translation parameters
        '''
        return Shape([shape.x + Tx, shape.y + Ty])

    def normalize(self, shape):
        '''
        Perform isomorphic scaling in order to normalize shapes
        See Amy Ross on GPA p.5

        in: target Shape
        out: scaled Shape object
        '''
        return Shape([shape.vector/np.linalg.norm(shape.vector)])

    def scale_and_rotate(self, subject, s, theta, inverse=False):
        '''Rotate over theta and scale by s'''
        rotation_matrix = np.array([
                            [s*math.cos(theta), -1*s*math.sin(theta)],
                            [s*math.sin(theta), s*math.cos(theta)]
                            ])
        if inverse:
            return Shape(np.dot(rotation_matrix.T, subject.matrix))
        else:
            return Shape(np.dot(rotation_matrix, subject.matrix))

    def transform(self, target, pose_parameters):
        '''
        Perform transformations to move target.

        in: Shape target,
            translation params Tx, Ty,
            scaling param s,
            rotation angle theta
        out: transformed Shape
        '''
        # normalize target to avoid multiple scaling
        target = self.normalize(target)
        # get pose parameters and perform transformations
        Tx, Ty, s, theta = pose_parameters
        scaled_and_rotated = self.scale_and_rotate(target, s, theta)
        return self.translate(scaled_and_rotated, Tx, Ty)

    def invert_transform(self, target, pose_parameters):
        '''
        Computes an inverse transformation of target
        '''
        # get pose parameters and invert transform
        Tx, Ty, s, theta = pose_parameters
        translated = self.translate(target, -1*Tx, -1*Ty)
        return self.scale_and_rotate(translated, 1/s, theta, inverse=True)

class TransformationFinder(object):
    '''
    Methods to get the correct pose parameters of a shape with
    respect to a target shape. For scaling and rotation, first
    get the transformation parameters a and b.
    '''
    def get_transformation_parameters(self, subject, target):
        '''
        Compute the parameters for scaling and rotation.
        As in Cootes (2000), if:
            a = (x1*x2)/|x1|^2
            b = sum1_n(x1i*y2i - y1i*x2i)/|x1|^2
        then
            s^2 = a^2 + b^2
            theta = tan^-1(b/a)
        '''
        # compute denominator
        denom = np.linalg.norm(subject.vector)**2
        # compute numerators
        num_a = np.dot(subject.vector, target.vector.T)
        num_b = np.sum(subject.x.dot(target.y) - subject.y.dot(target.x))
        return num_a/denom, num_b/denom

    def find_scale(self, a, b):
        return math.sqrt(a**2 + b**2)

    def find_rotation_angle(self, a, b):
        '''Return angle'''
        return math.atan(b/a)

    def find_translation(self, subject, target):
        '''
        Translation params = the difference between the centroids
        '''
        scX, scY = subject.centroid()
        tcX, tcY = target.centroid()
        return (tcX - scX, tcY - scY)

class Aligner(CoreAlign, TransformationFinder):
    '''
    Alignment class that combines the alignment methods from CoreAlign and the
    transformation variable finding methods from CoreFinder.
    '''
    def get_pose_parameters(self, subject, target):
        '''
        Find the pose parameters to align subject with target.
        In: 1xC array shape1, shape2
        Out: X_t, Y_t params that define the translation,
            s, defines the scaling
            theta, defines the rotation
        '''
        # find/perform transformations
        Tx, Ty = self.find_translation(subject, target)
        # compute a and b
        a, b = self.get_transformation_parameters(subject, target)
        s = self.find_scale(a, b)
        theta = self.find_rotation_angle(a, b)

        return (Tx, Ty, s, theta)


import re
import math
from collections import Counter
import numpy as np

text1 = 'Nicole a mangé du singe'
text2 = 'Nicole a mangé du rat'

class Similarity():
    def compute_cosine_similarity(self, string1, string2):
        # intersects the words that are common
        # in the set of the two words
        intersection = set(string1.keys()) & set(string2.keys())
        # dot matrix of vec1 and vec2
        numerator = sum([string1[x] * string2[x] for x in intersection])

        # sum of the squares of each vector
        # sum1 is the sum of text1 and same for sum2 for text2
        sum1 = sum([string1[x]**2 for x in string1.keys()])
        sum2 = sum([string2[x]**2 for x in string2.keys()])

        # product of the square root of both sum(s)
        denominator = math.sqrt(sum1) * math.sqrt(sum2)
        if not denominator:
            return 0.0
        else:
            return round(numerator / float(denominator), 4)

    def text_to_vector(self, text):
        WORD = re.compile(r'\w+')
        words = WORD.findall(text)
        return Counter(words)

    # Jaccard Similarity
    def tokenize(self, string):
        return string.lower().split(" ")

    def jaccard_similarity(self, string1, string2):
        intersection = set(string1).intersection(set(string2))
        union = set(string1).union(set(string2))
        return len(intersection) / float(len(union))

similarity = Similarity()

# vector space
vector1 = similarity.text_to_vector(text1)
vector2 = similarity.text_to_vector(text2)

# split words into tokens
token1 = similarity.tokenize(text1)
token2 = similarity.tokenize(text2)

cosine = similarity.compute_cosine_similarity(vector1, vector2)
print('Cosine Similarity:', cosine)

jaccard = similarity.jaccard_similarity(token1, token2)
print('Jaccard Similarity:', jaccard)


"""
filters.py - various image filtering operation

"""
#Author @otivedani

import numpy as np

# Section 0: 2d edge convolution
def simple_edge_gradients(img):
    _img_ = np.pad(img, (1,1), 'symmetric').astype(np.float_)

    # #[ 1 , 0 ,-1 ]
    gX = (_img_[:,:-2] - _img_[:,2:])[1:-1,:]
    # #[[1,
    # #  0,
    # # -1]]
    gY = (_img_[:-2,:] - _img_[2:,:])[:,1:-1]

    return gX, gY

# Section 1: numpy-based 2D convolutional filter, improved version from sobelY_HC and sobelX_HC
def conv_filter(img, h_kernel, v_kernel, clip=False):
    """
    Convolve 2d array horizontally then vertically.

    Parameters :
	------------
        img: numpy 2darray
        h_kernel: 1darray
        v_kernel: 1darray
        clip: if true - normalize to range(0,255)

    Return :
    --------
    img (dot) (h(dot)v)

    """
    h_klen = len(h_kernel)
    v_klen = len(v_kernel)

    h_pad = (h_klen-1)//2
    v_pad = (v_klen-1)//2

    result_h = np.zeros((img.shape[0]+(2*h_pad)+2,img.shape[1]))
    result_v = np.zeros(img.shape)

    # img2 = np.pad(img,((1+(h_pad),1+(h_pad)),(1+(v_pad),1+(v_pad))),'constant', constant_values=((0, 0),(0, 0)))
    img2 = np.pad(img,((1+(h_pad),1+(h_pad)),(1+(v_pad),1+(v_pad))),'reflect')

    #work like matlab.conv2(u,v,A)
    #TODO kill looping (vectorize)
    for i in range(h_klen):
        # result_h += h_kernel[i]*img2[:,1+i:len(img)+1+i]
        result_h += h_kernel[i]*img2[:,-(len(img[0])+1+i):-(1+i)]
    for j in range(v_klen):
        # result_v += v_kernel[j]*result_h[1+j:len(img)+1+j]
        result_v += v_kernel[j]*result_h[-(len(img)+1+j):-(1+j)]

    if clip:
        return np.clip(result_v,0,255)
    else:
        return result_v

#-- end of Section 1

# Section 2 : Magnitude, Angle vector calculation
def toPolar(gX, gY, signed=False, dtype='float'):
    """
    Parameters :
	------------
        gX: numpy 2darray - cartesian coordinate
        gY: numpy 2darray - cartesian coordinate
        signed: if true - change to range(0,360) == similar to 'cv2'
                if false - change to range(0,180)(default)
    Return :
    --------
    polar coordinate (w,a)
    """

    _basis = 360 if signed else 180

    # still return (-180,180)
    _ang = np.degrees(np.arctan2(gY,gX))

    mag = np.sqrt((np.square(gX))+(np.square(gY)))
    ori = np.where(_ang < 0, _ang + _basis, _ang)

    return mag.astype(dtype), ori.astype(dtype)
#-- end of Section 2

# Section 3 : x-position, y-position vector calculation
def toCart(ori, mag, signed=False, dtype='float'):
    """
    Parameters :
	------------
        ori: array-like numpy, in degrees
        mag: array-like numpy
        signed: if true - change to range(0,360)
                if false - change to range(0,180)

    Return :
    --------
    cartesian coordinate (x,y)
    """

    _basis = 360 if signed else 180

    x = mag * np.cos(ori*np.pi/_basis)
    y = mag * np.sin(ori*np.pi/_basis)

    return x.astype(dtype), y.astype(dtype)
#-- end of Section 3

# Section 4 :
# TBA
#-- end of Section 4


#!/usr/bin/python3

import numpy as np
import matplotlib.pyplot as plt

COLOR = ['#4285f4', '#db4437', '#f4b400', '#0f9d58', '#303030']

WEIGHTS = np.array([
    [10, 0], [10, 2], [10, 4], [10, 6], [8, 6],
    [8, 8], [6, 8], [6, 10], [4, 10], [2, 10]
], dtype = np.float)

def drawLine(ax, x1, y1, x2, y2, color = 'black', alpha = 1.0, label = None):
    ax.arrow(x1, y1, x2 - x1, y2 - y1, width = 0, linewidth = 1,
        color = color, alpha = alpha, label = label)

def drawCircle(ax, x, y, radius, color = 'black', alpha = 1.0, label = None):
    circle = plt.Circle((x, y), radius, linewidth = 1, fill = False,
        edgecolor = color, facecolor = 'white', alpha = alpha)
    ax.add_artist(circle)

def drawL1Region(ax, C):
    drawLine(ax, C, 0, 0, C, color = COLOR[4], alpha = 0.8)
    drawLine(ax, C, 0, 0, -C, color = COLOR[4], alpha = 0.8)
    drawLine(ax, -C, 0, 0, C, color = COLOR[4], alpha = 0.8)
    drawLine(ax, -C, 0, 0, -C, color = COLOR[4], alpha = 0.8)

def drawL2Region(ax, C):
    drawCircle(ax, 0, 0, C, color = COLOR[4], alpha = 0.8)

def drawSquareRegion(ax, C):
    drawLine(ax, C, -C, C, C, color = COLOR[4], alpha = 0.8)
    drawLine(ax, -C, C, C, C, color = COLOR[4], alpha = 0.8)
    drawLine(ax, -C, C, -C, -C, color = COLOR[4], alpha = 0.8)
    drawLine(ax, C, -C, -C, -C, color = COLOR[4], alpha = 0.8)

def l1Regularize(weights, C):
    reg = np.empty_like(weights, dtype = np.float)
    for i in range(len(weights)):
        x, y = weights[i, 0], weights[i, 1]
        if y - x <= -C:
            reg[i] = C, 0
            continue
        elif y - x >= C:
            reg[i] = 0, C
            continue
        d = (x + y - C) / 2
        reg[i] = x - d, y - d
    return reg

def l2Regularize(weights, C):
    norm = np.linalg.norm(weights, axis = 1)
    return C * weights / np.column_stack((norm, norm))

def lpRegularize(weights, C):
    return np.minimum(weights, C)

def calcSparsity(arr, axis):
    return np.size(arr, axis) - np.count_nonzero(arr, axis)

def main(weights, region, C, plot_fname, idx = []):
    fig, ax = plt.subplots()
    ax.set_aspect('equal')

    inv_idx = np.ones(len(weights))
    inv_idx[idx] = 0
    inv_idx = inv_idx == 1
    ax.scatter(weights[inv_idx, 0], weights[inv_idx, 1], color = COLOR[0], label = 'Unconstrained')
    ax.scatter(weights[idx, 0], weights[idx, 1], color = COLOR[2], label = 'Unconstrained')
    if region == 'L1':
        drawL1Region(ax, C)
        weights_reg = l1Regularize(weights, C)
    elif region == 'L2':
        drawL2Region(ax, C)
        weights_reg = l2Regularize(weights, C)
    else:
        drawSquareRegion(ax, C)
        weights_reg = lpRegularize(weights, C)

    sparsity = calcSparsity(weights, axis = 1)
    sparsity_reg = calcSparsity(weights_reg, axis = 1)
    print(f'Number of weights with higher regularization sparsity: {np.sum(sparsity_reg > sparsity)}')

    ax.scatter(weights_reg[:, 0], weights_reg[:, 1], color = COLOR[1], alpha = 0.5, label = 'Regularized')
    for i in idx:
        drawLine(ax, weights[i, 0], weights[i, 1], weights_reg[i, 0], weights_reg[i, 1],
            color = COLOR[2], alpha = 0.5)
        radius = np.linalg.norm(weights[i] - weights_reg[i])
        drawCircle(ax, weights[i, 0], weights[i, 1], radius,
            color = COLOR[2], alpha = 0.5)

    ax.set_xlim(-5.5, 10.5)
    ax.set_ylim(-5.5, 10.5)
    ax.grid(alpha = 0.3)
    ax.legend()
    fig.savefig(plot_fname, dpi = 600, transparent = True)
    plt.clf()

if __name__ == "__main__":
    main(WEIGHTS, 'L2', 2, './fig_a.pdf', [1, 6])
    main(WEIGHTS, 'L1', 2, './fig_b.pdf', [2, 5, 9])
    main(WEIGHTS, 'Lp', 1, './fig_c.pdf', [0, 5, 9])
    main(WEIGHTS, 'L2', 5, './fig_d.pdf', [1, 6])
    main(WEIGHTS, 'L1', 5, './fig_e.pdf', [0, 5, 8])


# In this example we find a minimum of the Rosenbrock function without bounds
# on independed variables.
import numpy as np
def fun_rosenbrock(x):
    return np.array([10 * (x[1] - x[0]**2), (1 - x[0])])

# Notice that we only provide the vector of the residuals. The algorithm
# constructs the cost function as a sum of squares of the residuals, which
# gives the Rosenbrock function. The exact minimum is at ``x = [1.0, 1.0]``.

from scipy.optimize import least_squares
x0_rosenbrock = np.array([2, 2])
res_1 = least_squares(fun_rosenbrock, x0_rosenbrock)
print res_1.x
# array([ 1.,  1.])
print res_1.cost
# 2.4651903288156619e-30
print res_1.optimality
# 4.4408921315878507e-14

# We now constrain the variables, in such a way that the previous solution
# becomes infeasible. Specifically, we require that ``x[1] >= 1.5``, and
# ``x[0]`` left unconstrained. To this end, we specify the `bounds` parameter
# to `least_squares` in the form ``bounds=([-np.inf, 1.5], np.inf)``.

# We also provide the analytic Jacobian:

def jac_rosenbrock(x):
    return np.array([
        [-20 * x[0], 10],
        [-1, 0]])

# Putting this all together, we see that the new solution lies on the bound:

res_2 = least_squares(fun_rosenbrock, x0_rosenbrock, jac_rosenbrock,
                      bounds=([-np.inf, 1.5], np.inf))
print res_2.x
# array([ 1.22437075,  1.5       ])
print res_2.cost
# 0.025213093946805685
print res_2.optimality
# 1.5885401433157753e-07

# Now we solve a system of equations (i.e., the cost function should be zero
# at a minimum) for a Broyden tridiagonal vector-valued function of 100000
# variables:

def fun_broyden(x):
    f = (3 - x) * x + 1
    f[1:] -= x[:-1]
    f[:-1] -= 2 * x[1:]
    return f

# The corresponding Jacobian matrix is sparse. We tell the algorithm to
# estimate it by finite differences and provide the sparsity structure of
# Jacobian to significantly speed up this process.

from scipy.sparse import lil_matrix
def sparsity_broyden(n):
    sparsity = lil_matrix((n, n), dtype=int)
    i = np.arange(n)
    sparsity[i, i] = 1
    i = np.arange(1, n)
    sparsity[i, i - 1] = 1
    i = np.arange(n - 1)
    sparsity[i, i + 1] = 1
    return sparsity
# ...
n = 100000
x0_broyden = -np.ones(n)
# ...
res_3 = least_squares(fun_broyden, x0_broyden,
                      jac_sparsity=sparsity_broyden(n))
print res_3.cost
# 4.5687161966109073e-23
print res_3.optimality
# 1.1650454296851518e-11

# Let's also solve a curve fitting problem using robust loss function to
# take care of outliers in the data. Define the model function as
# ``y = a + b * exp(c * t)``, where t is a predictor variable, y is an
# observation and a, b, c are parameters to estimate.

# First, define the function which generates the data with noise and
# outliers, define the model parameters, and generate data:

def gen_data(t, a, b, c, noise=0, n_outliers=0, random_state=0):
    y = a + b * np.exp(t * c)
# ...
    rnd = np.random.RandomState(random_state)
    error = noise * rnd.randn(t.size)
    outliers = rnd.randint(0, t.size, n_outliers)
    error[outliers] *= 10
# ...
    return y + error
# ...
a = 0.5
b = 2.0
c = -1
t_min = 0
t_max = 10
n_points = 15
# ...
t_train = np.linspace(t_min, t_max, n_points)
y_train = gen_data(t_train, a, b, c, noise=0.1, n_outliers=3)

# Define function for computing residuals and initial estimate of
# parameters.

def fun(x, t, y):
    return x[0] + x[1] * np.exp(x[2] * t) - y
# ...
x0 = np.array([1.0, 1.0, 0.0])

# Compute a standard least-squares solution:

res_lsq = least_squares(fun, x0, args=(t_train, y_train))

# Now compute two solutions with two different robust loss functions. The
# parameter `f_scale` is set to 0.1, meaning that inlier residuals should
# not significantly exceed 0.1 (the noise level used).

res_soft_l1 = least_squares(fun, x0, loss='soft_l1', f_scale=0.1,
                            args=(t_train, y_train))
res_log = least_squares(fun, x0, loss='cauchy', f_scale=0.1,
                        args=(t_train, y_train))

# And finally plot all the curves. We see that by selecting an appropriate
# `loss`  we can get estimates close to optimal even in the presence of
# strong outliers. But keep in mind that generally it is recommended to try
# 'soft_l1' or 'huber' losses first (if at all necessary) as the other two
# options may cause difficulties in optimization process.

t_test = np.linspace(t_min, t_max, n_points * 10)
y_true = gen_data(t_test, a, b, c)
y_lsq = gen_data(t_test, *res_lsq.x)
y_soft_l1 = gen_data(t_test, *res_soft_l1.x)
y_log = gen_data(t_test, *res_log.x)
# ...
import matplotlib.pyplot as plt
plt.plot(t_train, y_train, 'o')
plt.plot(t_test, y_true, 'k', linewidth=2, label='true')
plt.plot(t_test, y_lsq, label='linear loss')
plt.plot(t_test, y_soft_l1, label='soft_l1 loss')
plt.plot(t_test, y_log, label='cauchy loss')
plt.xlabel("t")
plt.ylabel("y")
plt.legend()
plt.show()


import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import time as ti

from sklearn import datasets

def task(N,D,T):
	a=np.zeros((len(N),len(N[0])))
	if (T == "d"): #len(N)==len(D) and len(N[0])==len(D[0]) and
		for i in range(len(N)):
			for j in range(len(N[0])):
				a[i][j]=N[i][j]/D[i][j]
	elif (T == "m"):
		for i in range(len(N)):
			for j in range(len(N[0])):
				a[i][j]=N[i][j]*D[i][j]
	return a #else return zero

def mat_fac(X,K,S=7000):
	st = ti.time()
	W = np.random.rand(len(X),K)
	H = np.random.rand(K,len(X[0]))
	(s,e,ne)=(0,0,0)
	print "W",W, "\n"
	print "H", H, "\n"

	while (s<S):

		h1 = np.dot(np.transpose(W),task(X,np.dot(W,H),"d"))
		h2 = np.dot(np.transpose(W),np.ones((len(W),len(h1[0]))))
		h3 = task(h1,h2,"d")
		H = task(H,h3,"m")

		w1 = np.dot(task(X,np.dot(W,H),"d"),np.transpose(H))
		w2 = np.dot(np.ones((len(w1),len(H[0]))),np.transpose(H))
		w3 = task(w1,w2,"d")
		W = task(W,w3,"m")

		nX = np.dot(W,H)
		for i in range(len(X)):
			for j in range(len(X[i])):
				#ne = ne + pow((X[i][j]-nX[i][j]),2)
				ne = ne + (X[i][j]*np.log((X[i][j]+0.0)/(nX[i][j]+0.0))-X[i][j]+nX[i][j])
		print ne
		if np.abs(e-ne)>0.00001:
		#if np.abs(ne)>0:
			e=ne
			ne=0
		else:
			break
		s+=1
	print "step =", s
	print "time taken =", ti.time()-st
	return (W,H,nX)

iris = np.transpose(np.array(datasets.load_iris()['data'],dtype='f'))
i1 = iris
print iris, "\n"

(nW,nH,nX) = mat_fac(iris,2)
print "new W :", "\n", nW, "\n"
print "new H :", "\n", nH, "\n"
print "new Matrix :", "\n", nX, "\n"

for i in range(len(i1)):
	for j in range(len(i1[0])):
		i1[i][j] = i1[i][j]-nX[i][j]

print "Difference Matrix :", "\n", i1


import numpy as np

def init(A, b, c, rank, m):
    A_b = A[:, (m-rank):]
    A_n = A[:, :(m-rank)]

    c_b = c[:, (m-rank):]
    c_n = c[:, :(m-rank)]

    A_b_inv = np.linalg.pinv(A_b)
    b_ = A_b_inv @ b
    tmp = c_b @ A_b_inv
    ZC = tmp @ A_n - c_n

    F = tmp @ b

    # Get the Simplex Tabel
    T1 = np.vstack((A_b_inv @ A_n, ZC))
    T2 = np.vstack((A_b, np.zeros_like(ZC)))
    T3 = np.vstack((b_, F))
    T = np.hstack((T1, T2, T3))

    return T

def main(T, rank, m):
    '''

    :param T: left is non-basic and right is basic
    :param rank: the number of basic vector or variable
    :return:
    '''
    while True:
        F = T[-1, -1]
        ZC = T[-1, :(m - rank)]

        if np.max(ZC) <= 0:
            return F

        # Find the main column
        main_col = np.argmax(T[-1, :(m-rank)])
        # Find the main row
        tmp = T[:-1, -1] / T[:-1, main_col]
        index, min_v = -1, 1e10
        for i in range(tmp.shape[0]):
            if tmp[i] < min_v and tmp[i] > 0:
                index, min_v = i, tmp[i]
        main_row = index
        if main_row == -1:
            raise Exception('No solution!')
            return False

        # Update
        T[main_row, :] = T[main_row, :] / T[main_row, main_col]
        for i in range(T.shape[0]):
            if i == main_row:
                continue
            alpha = T[i, main_col] / T[main_row, main_col]
            T[i, :] = T[i, :] - alpha * T[main_row, :]
        turn = T[:, main_col]
        T[:, main_col] = T[:, (m-rank+main_row-1)]
        T[:, (m-rank+main_row-1)] = turn

    return 0

if __name__ == '__main__':
    data = np.loadtxt('data/data-1.txt')
    n, m = data.shape

    # C: row-vector
    c = data[0, :-1]
    c = c[np.newaxis, :]
    # B: col-vector
    b = data[1:n, -1]
    b = b[:, np.newaxis]
    # A: matrix
    A = data[1:n, :-1]

    rank = min(n-1, m-1)
    assert rank == n-1
    m = m-1
    T = init(A, b, c, rank, m)
    Final = main(T, rank, m)

    print('The min value of object function is ', Final)


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

# # Basic Qubit Rotation Example
#
# In this example, we show how we can use a
# simple gradient descent method to optimize
# a unitary gate for qubit rotation. The idea
# is as follows: We start in $|1 \rangle$ state
# on a Bloch Sphere and apply rotations around
# Z-Y-Z axes (in that order) using
# $R(\phi,\theta,\omega)$, which is a
# general unitary operator parameterized
# by three real parameters, $\phi$, $\theta$,
# and $\omega$. Each of these parameters represent
# the degree of rotation around x, y and z axes
# respectively to reach the target state,
# which is defined to be $|0 \rangle$ in
# this case. $R(\phi,\theta,\omega)$ has
# the following matrix representation:
#
#
# $$
# R(\phi,\theta,\omega) = RZ(\omega)RY(\theta)RZ(\phi)= \begin{bmatrix}
# e^{-i(\phi+\omega)/2}\cos(\theta/2) & -e^{i(\phi-\omega)/2}\sin(\theta/2) \\
# e^{-i(\phi-\omega)/2}\sin(\theta/2) & e^{i(\phi+\omega)/2}\cos(\theta/2)
# \end{bmatrix}
# $$

import jax.numpy as jnp
from jax import grad
from jax.random import normal, PRNGKey

import numpy as onp

from qgrad.qgrad_qutip import basis, fidelity

def rot(phi, theta, omega):
    """Returns a rotation matrix represetning
    the rotation around Z-Y-Z axis.

    Parameters:
        phi (float): rotation angle for the
            first rotation around the z-axis
        theta (float): rotation angle for
            rotation around the y-axis
        omega (float): rotation angle for the
            second rotation around the z-axis

    Returns:
        :obj:`jnp.ndarray`: array representing
            the rotation matrix

    """

    cos = jnp.cos(theta / 2)
    sin = jnp.sin(theta / 2)

    return jnp.array(
        [
            [
                jnp.exp(-0.5j * (phi + omega)) * cos,
                -(jnp.exp(0.5j * (phi - omega))) * sin,
            ],
            [jnp.exp(-0.5j * (phi - omega)) * sin, jnp.exp(0.5j * (phi + omega)) * cos],
        ]
    )

def cost(phi, theta, omega, ket):
    r"""Returns the fidelity between
    the evolved state and the target
    :math:`|0 \rangle` state.

    Parameters:
        phi (float): rotation angle for the
            first rotation around the z-axis
        theta (float): rotation angle for
            rotation around the y-axis
        omega (float): rotation angle for the
            second rotation around the z-axis
        ket (:obj:`jnp.array`): Initial ket
            to act the rotation matrix on

    Returns:
        float: fidelity between the initial and
            the evolved state under rotation
    """
    evolved = jnp.dot(rot(phi, theta, omega), ket)
    return fidelity(evolved, basis(2, 0))[0][0]

# ## Gradient Ascent Implementation
#
# Here we have our own gradient ascent
# implementation. One may use traditional
# gradient descent by subtracting fidelity
# from $1$ in the cost function.
#
# There are multiple
# parameters, like `epochs`, `alpha`, etc,
# that one might tune further in hopes of
# better convergence. For our purposes, however,
# we see that our implementation converges well
# in about $80$ crude steps.

# initialize parameters for learning
epochs = 80
alpha = 0.2  # learning rate
weights = normal(PRNGKey(0), shape=(3,))
init_ket = basis(2, 1)  # initial |1> state
der_cost = grad(cost, argnums=[0, 1, 2])
state_hist = [Qobj(onp.array(init_ket))]

for epoch in range(epochs):
    der = jnp.asarray(der_cost(*weights, init_ket))
    weights = weights + alpha * der
    state_hist.append(Qobj(onp.dot(rot(*weights), init_ket)))
    fidel = cost(*weights, init_ket)
    progress = [epoch + 1, fidel]
    if epoch % 10 == 9:
        print("Epoch: {:2f} | Fidelity: {:3f}".format(*jnp.asarray(progress)))

# ## Bloch Sphere Visualization
#
# As we see above, we started off
# with a very low fidelity
# ($\sim$ 0.29). With gradient
# descent iterations, we progressively
# achieve better fidelities via better
# parameters, $\phi$, $\theta$,
# and $\omega$. To visualize training,
# we render our states on to a Bloch sphere
# animation below.
#
# We see how our gradient based optimizer
# walks from $|1 \rangle$ (southward) and
# converges almost perfectly to the target
# state $|0 \rangle$
# (northward) as depicted in the second figure.

# Visualization
get_ipython().run_line_magic("matplotlib", "inline")
from qutip import Bloch
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
import matplotlib.pyplot as plt

plt.rcParams["animation.html"] = "jshtml"
fig = plt.figure()
ax = Axes3D(fig, azim=-40, elev=30)
sphere = Bloch(axes=ax)

def animate(i):
    sphere.clear()
    sphere.add_states(state_hist[i])
    sphere.make_sphere()
    return ax

def init():
    sphere.vector_color = ["b"]
    return ax

ani = animation.FuncAnimation(
    fig, animate, onp.arange(len(state_hist)), init_func=init, repeat=True
)
ani


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

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

def compute_cost(X, y ,theta):
    m = len(y)
    h = sigmoid(X @ theta)
    epsilon = 1e-5
    cost = (1/m)*(((-y).T @ np.log(h + epsilon))-((1-y).T @ np.log(1-h + epsilon)))
    return cost

def gradient_descent(X, y, params, learning_rate, iterations):
    m = len(y)
    cost_history = np.zeros((iterations,1))

    for i in range(iterations):
        params = params - (learning_rate/m) * (X.T @ (sigmoid(X @ params) - y))
        cost_history[i] = compute_cost(X, y, params)

    return (cost_history, params)

def predict(X, params):
    return np.round(sigmoid(X @ params))

X, y = make_classification(n_samples=500, n_features=2, n_redundant=0, n_informative=1,
                             n_clusters_per_class=1, random_state=14)

y = y[:,np.newaxis]

sns.set_style('white')
sns.scatterplot(X[:,0],X[:,1],hue=y.reshape(-1));

m = len(y)

X = np.hstack((np.ones((m,1)),X))
n = np.size(X,1)
params = np.zeros((n,1))

iterations = 1500
learning_rate = 0.03

initial_cost = compute_cost(X, y, params)

print("Initial Cost is: {} \n".format(initial_cost))

(cost_history, params_optimal) = gradient_descent(X, y, params, learning_rate, iterations)

print("Optimal Parameters are: \n", params_optimal, "\n")

plt.figure()
sns.set_style('white')
plt.plot(range(len(cost_history)), cost_history, 'r')
plt.title("Convergence Graph of Cost Function")
plt.xlabel("Number of Iterations")
plt.ylabel("Cost")
plt.show()

y_pred = predict(X, params_optimal)
score = float(sum(y_pred == y))/ float(len(y))

print(score)

slope = -(params_optimal[1] / params_optimal[2])
intercept = -(params_optimal[0] / params_optimal[2])

sns.set_style('white')
sns.scatterplot(X[:,1],X[:,2],hue=y.reshape(-1));

ax = plt.gca()
ax.autoscale(False)
x_vals = np.array(ax.get_xlim())
y_vals = intercept + (slope * x_vals)
plt.plot(x_vals, y_vals, c="k");


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as stats
from scipy.integrate import quad
from scipy.fft import fft, ifft
from scipy.interpolate import interp1d
from functools import partial
from scipy.optimize import minimize, fsolve, newton
import matplotlib.pyplot as plt

class StochasticProcesses:
    def __init__(self, option_type='call'):
        self.option_type = option_type

    def geometric_BM(self, T, s0, mu, sigma, n):
        x = np.zeros(n + 1)
        s = np.zeros(n + 1)
        time = np.zeros(n + 1)

        x[0] = np.log(s0)
        dt = T/float(n)

        for t in range(n):
            x[t+1] = x[t] + (mu - (sigma**2)/2) * dt + sigma * np.sqrt(dt) * stats.norm.rvs(loc=0, scale=1)
            time[t+1] = time[t] + dt

        s = np.exp(x)

        return time, s

    def poisson_process(self, T, s0, xiP, n, type_p='ordinary'):
        time = np.zeros(n + 1)
        dt = T/float(n)
        poisson_distr = np.random.poisson(xiP * dt, n + 1)

        if type_p == 'ordinary':
            x = np.zeros(n + 1)
            x[0] = s0

            for t in range(n):
                x[t+1] = x[t] + poisson_distr[t]
                time[t+1] = time[t] + dt

            return time, x

        elif type_p == 'compensated':
            xc = np.zeros(n + 1)
            xc[0] = s0

            for t in range(n):
                xc[t+1] = xc[t] + poisson_distr[t] - xiP * dt
                time[t+1] = time[t] + dt

            return time, xc
        else:
            print('Wrong type process, available: ordinary and compensated')

    def merton_process(self, T, s0, xiP, muj, sigmaj, r, sigma, n):
        time = np.zeros(n + 1)
        dt = T/float(n)

        z = np.random.normal(0.0, 1.0, n + 1)
        zj = np.random.normal(muj, sigmaj, n + 1)
        poisson_distr = np.random.poisson(xiP * dt, n + 1)

        x = np.zeros(n + 1)
        s = np.zeros(n + 1)

        s[0] = s0
        x[0] = np.log(s0)

        EeJ = np.exp(muj + 0.5 * sigmaj**2)

        for t in range(n):
            x[t+1] = x[t] + (r - xiP * (EeJ - 1) - 0.5 * sigma**2) * dt +\
                sigma * np.sqrt(dt) * z[t] + zj[t] * poisson_distr[t]

            time[t+1] = time[t] + dt

        s = np.exp(x)

        return time, s

    def plot_path(self, time, s, label='Process path', title='Process Visualization'):
        plt.subplots(figsize=(10, 5), dpi=100)
        plt.plot(time, s, label=label)

        plt.title(title, fontsize=16)
        plt.xlabel('Time', fontsize=14)
        plt.ylabel('Values', fontsize=14)
        plt.legend()
        plt.show()


import numpy as np
import sklearn.datasets as skdata
from sklearn.linear_model import LinearRegression

boston_housing_data = skdata.load_boston()
x = boston_housing_data.data
y = boston_housing_data.target

print(x.shape) #506, 13
print(y.shape) #506,

#Split the data 90 percent for training, 10 percent for testing
split_idx = int(0.90*x.shape[0])
x_train,y_train = x[:split_idx, :], y[:split_idx]
x_test, y_test = x[split_idx:, :], y[split_idx:]

#Sets up our Linear Regression Model
model = LinearRegression()

#Trains our regression model
model.fit(x_train, y_train)

#Make predictions
predictions = model.predict(x_test)

print("predictions are", predictions)

#meaduring regression error
def mean_squared_error(y_hat, y):
    #y_hat is your predictions
    return np.mean((y_hat-y)**2)

mse = mean_squared_error(predictions, y_test)

print("mse is", mse) #should be around 10.8~

#LINEAR REGRESSION VIA NORMAL EQUATION
class LinearRegressionNormalEqn(object):

    def __init__(self):
        self.weights = None

    def  fit(self, x, y):
        x_transpose_x_inv = np.linalg.inv(np.matmul(x.T, x)) # x.T == np.transpose(x)
        self.weights = np.matmul(np.matmul(x_transpose_x_inv, x.T), y)
        #xTx = np.matmul(np.transpose(x), x)
        #xTx_inv = np.inv(xTx)
        #self.weights = np.matmul(np.matmul(xTx_inv, np.transpose(x), y))

    def predict(self, x):
        return np.matmul(x, self.weights)

#Sets up our Linear Regression model
model_norm_eqn = LinearRegressionNormalEqn()

#Trains our regression model
model_norm_eqn.fit(x_train, y_train)

#Make predictions
predictions_norm_eqn = model_norm_eqn.predict(x_test)

#Evaluate our model
mse_norm_eqn = mean_squared_error(predictions_norm_eqn, y_test) #Should be around 13

print("mse norm eqn is ", mse_norm_eqn)

class LinearRegressionSVD(object):

    def __init__(self):
        self.weights = None

    def fit(self, x, y):
        U, S, V_t = np.linalg.svd(x)
        padding = np.zeros([x.shape[0] -S.shape[0], S.shape[0]])
        S_pseudo = np.concatenate([np.diag(1.0/S), padding], axis =0).T
        X_pseudo = np.matmul(np.matmul(V_t.T, S_pseudo), U.T)
        self.weights = np.matmul(X_pseudo, y)

    def predict(self, x):
        return np.matmul(x, self.weights)

#Split the data 90 percent for training, 10 percent for testing
split_idx = int(0.90*x.shape[0])
x_train, y_train = x[:split_idx, :], y[:split_idx]
x_test, y_test = x[split_idx:, :], y[split_idx:]
#if N < d, issues, too huge... use pseudo inverse//less dangerous

#Sets up our Linear Regression model
model_svd = LinearRegressionSVD()

#Trains our regression model
model_svd.fit(x_train, y_train)

#Make predictions
predictions_svd = model_svd.predict(x_test)

#Evaluate our model
mse_svd = mean_squared_error(predictions_svd, y_test)

print("mse svd", mse_svd)


from kernel_exp_family.kernels.kernels import gaussian_kernel
import numpy as np

def compute_b_sym(Z, K, sigma):
    assert K.shape[0] == Z.shape[0]
    assert K.shape[0] == K.shape[1]

    D = Z.shape[1]
    N = Z.shape[0]

    b = np.zeros(N)
    K1 = np.sum(K, 1)
    for l in np.arange(D):
        x_l = Z[:, l]
        s_l = x_l ** 2

        # Replaces dot product with np.diag via broadcasting
        # See http://mail.scipy.org/pipermail/numpy-discussion/2007-March/026809.html
        D_s_K = s_l[:, np.newaxis] * K
        D_x_K = x_l[:, np.newaxis] * K

        b += 2. / sigma * (K.dot(s_l) \
                        + np.sum(D_s_K, 1) \
                        - 2 * D_x_K.dot(x_l)) - K1
    return b

def compute_C_sym(Z, K, sigma):
    assert K.shape[0] == Z.shape[0]
    assert K.shape[0] == K.shape[1]

    D = Z.shape[1]
    N = Z.shape[0]

    C = np.zeros((N, N))
    for l in np.arange(D):
        x_l = Z[:, l]

        # Replaces dot product with np.diag via broadcasting
        # See http://mail.scipy.org/pipermail/numpy-discussion/2007-March/026809.html
        D_x_K = x_l[:, np.newaxis] * K
        K_D_x = K * x_l

        C += (D_x_K - K_D_x).dot(K_D_x - D_x_K)

    return C

def fit_sym(Z, sigma, lmbda, K=None, b=None, C=None):
        # compute quantities
        if K is None:
            K = gaussian_kernel(Z, sigma=sigma)

        if b is None:
            b = compute_b_sym(Z, K, sigma)

        if C is None:
            C = compute_C_sym(Z, K, sigma)

        # solve regularised linear system
        a = -sigma / 2. * np.linalg.solve(C + (K + np.eye(len(C))) * lmbda,
                                          b)

        return a

def objective_sym(Z, sigma, lmbda, alpha, K=None, b=None, C=None):
    if K is None and ((b is None or C is None) or lmbda > 0):
        K = gaussian_kernel(Z, sigma=sigma)

    if C is None:
        C = compute_C_sym(Z, K, sigma)

    if b is None:
        b = compute_b_sym(Z, K, sigma)

    N = len(Z)
    first = 2. / (N * sigma) * alpha.dot(b)
    second = 2. / (N * sigma ** 2) * alpha.dot(
                                               (C + (K + np.eye(len(C))) * lmbda).dot(alpha)
                                               )
    J = first + second
    return J


#-------------------------------------------------------------------------------
# Name:        Linear Regression
# Purpose:
#
# Author:      Nonlining
#
# Created:     05/01/2017
# Copyright:   (c) Nonlining 2017
# Licence:     <your licence>
#-------------------------------------------------------------------------------
import numpy as np
from math import log

def get_residual_sum_of_squares(features_matrix, weights, outputs):
    pred_y = predict(features_matrix, weights)
    Residual = outputs - pred_y
    RSS = (Residual*Residual).sum()
    return(RSS)

def extract_data_from_features(data, features, output):
    data['constant'] = 1.0
    features = ['constant'] + features
    features_matrix = np.array(data[features])

    if output != None:
        output_array = np.array(data[output])
    else:
        output_array = []

    return(features_matrix, output_array)

def predict(feature_matrix, weights):
    return np.dot(feature_matrix, weights)

def regression_gradient_descent(feature_matrix, output, initial_weights, step_size, tolerance):
    converged = False
    weights = np.array(initial_weights)
    #count = 0

    while not converged:
        predictions = predict(feature_matrix, weights)
        errors =  output - predictions
        partial =  -2*np.dot(feature_matrix.T, errors)
        weights = weights - step_size*partial
        gradient_magnitude = np.sqrt(np.sum(partial*partial))
        #count = count + 1
        #if (count%10000 == 0):
        #    print gradient_magnitude

        if gradient_magnitude < tolerance:
            converged = True

    return(weights)


import numpy as np
from scipy.optimize import minimize

LOSS_TYPES = ['l1', 'median', 'mae', 'l2', 'mse', 'quantile', 'q']

def _residual_loss_fn(x, y, w, loss_type, quantile):
    res = y - x @ w
    if loss_type.lower() in ['l2', 'mse']:
        loss_ = np.mean(res ** 2) / 2
        d_loss_ = - x.T @ res / res.shape[0]
    elif loss_type.lower() in ['l1', 'median', 'mae']:
        loss_ = np.abs(res).mean()
        d_loss_ = - x.T @ np.sign(res) / res.shape[0]
    elif loss_type.lower() in ['quantile', 'q']:
        quantiles = np.where(res < 0, quantile - 1, quantile)
        loss_ = (quantiles * res).mean()
        d_loss_ = - x.T @ quantiles / res.shape[0]
    else:
        raise ValueError(f'Unknown loss_type=`{quantile}`!')
    return loss_, d_loss_

def _regularization_loss(w, l1_w, l2_w):
    loss_ = 0.0
    d_loss_ = np.zeros_like(w)
    if l1_w > 0.0:
        loss_ += l1_w * np.abs(w).sum()
        d_loss_ += l1_w * np.sign(w)
    if l2_w > 0.0:
        loss_ += l2_w * (w ** 2).sum() / 2
        d_loss_ += l2_w * w
    return loss_, d_loss_

def scale_std(x, scale=True):
    """ Scale data by its standard deviation. """
    if scale is True:
        x_std = x.std(axis=0) + 1e-12
        x /= x_std
    else:
        x_std = np.ones(x.shape[1])
    return x, x_std

def check_and_prepare(x, y, loss_type, quantile, scale, fit_bias, copy_xy):
    """ Check in puts and reshape data to 2d. """
    assert loss_type.lower() in LOSS_TYPES, \
        f'Expected loss_type in {LOSS_TYPES}. Got: loss_type={loss_type}!'
    if loss_type == 'quantile':
        assert quantile is not None and 0.0 < quantile < 1.0, \
            f'Expected 0.0 < quantile < 1.0. Got: quantile={quantile}!'
    assert x.shape[0] == y.shape[0], f'{x.shape[0]} != {y.shape[0]}'
    assert x.ndim == 2
    if copy_xy is True:
        x = x.copy()
        y = y.copy()
    if y.ndim == 1:
        y = y[:, None]
    else:
        assert y.ndim == 2
    # Scale data and optionally adjust for bias term:
    x, x_std = scale_std(x, scale=scale)
    y, y_std = scale_std(y, scale=scale)
    if fit_bias is True:
        x = np.hstack((x, np.ones((x.shape[0], 1))))
    return x, x_std, y, y_std

def lbfgs_fit(loss_fn, w_init, grad=True):
    """ Simple wrapper for L-BFGS scipy routine. """
    original_shape = w_init.shape
    w_init = w_init.ravel()
    w_star = minimize(fun=loss_fn, x0=w_init, method='L-BFGS-B', jac=grad).x
    w_star = w_star.reshape(original_shape)
    return w_star

def postprocess_parameters(w_star, x_std, y_std, fit_bias, clip_weights):
    """ Adjust bias terms, weight clipping and re-scaling of weights. """
    if fit_bias is True:
        bias = y_std * w_star[-1, :]
        w_star = w_star[:-1, :]
    else:
        bias = np.zeros(w_star.shape[1])
    if clip_weights > 0.0:
        w_max = np.abs(w_star).max()
        w_star[np.abs(w_star) < w_max * clip_weights] = 0.0
    w_orig = w_star * y_std / x_std[:, None]
    return w_orig, w_star, bias


#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
#Description: Probability of antibodies after vaccination
#Please note: For entertainment only, not scientific
#Date: 2021/06/05
#Author: Steven Huang, Auckland, NZ
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd

#matplotlib.rcParams['figure.dpi'] = 120 #high resolution 100~300

def probablityVaccine(p, N): #1-(1-p)^N
    """
    p: Vaccine effectiveness
    N: Number of courses of vaccine
    """
    return np.round(1-np.power(1-p, N), 6)

def plotVaccins(data):
    ax = plt.subplot(1,1,1)
    plt.title('Success probability after numbers of vaccination')

    markers=['o', 'v', '*', 's', '^']
    for i, d in enumerate(data):
        name,prob = d
        x = np.arange(len(prob))+1
        y = prob
        ax.plot(x,y,label=name, linestyle='-.', marker=markers[i])

    plt.text(2.5, 0.3, r'$Prob = 1-(1-p)^N$', fontsize=12)

    ax.set_xlabel('Times')
    ax.set_ylabel('Probablity')
    ax.legend()
    #plt.legend()
    plt.grid(linestyle='-.', alpha=0.5) #'-', '--', '-.', ':', '',
    #plt.legend(loc='lower left')
    plt.savefig(r'.\images\probVaccine.png')
    plt.show()
    pass

def plotVaccinsTable(data):
    columns = ['name', '1', '2', '3', '4', '5', '6']
    df  = pd. DataFrame()
    for name,prob in data:
        dfLine = pd.DataFrame([[name, prob[0], prob[1],prob[2],prob[3],prob[4],prob[5]]], columns=columns)
        df = df.append(dfLine)
    print(df)

    title = 'Success probability after numbers of vaccination'
    #plt.title(title)
    plt.text(0.16, 0.7, title, fontsize=12)
    tb = plt.table(cellText=df.values, colLabels=df.columns, loc='center',cellLoc='center')
    tb.auto_set_font_size(False)
    tb.set_fontsize(10)
    #colList = list(range(len(df.columns)))
    colList = [1,2,3,4,5,6]
    tb.auto_set_column_width(col=colList)

    plt.axis('off')
    plt.tight_layout()
    plt.savefig(r'.\images\probVaccineT.png')
    plt.show()

def generateData():
    pl = [0.2, 0.5, 0.6, 0.8, 0.9]
    N = np.arange(1, 7)

    data = []
    for p in pl:
        prob = probablityVaccine(p, N)
        #print(prob)
        data.append(('vaccine-'+str(p), prob))
    print(data)
    return data

def main():
    data = generateData()
    plotVaccins(data)
    plotVaccinsTable(data)

if __name__=="__main__":
    main()


#!/usr/bin/env python3

import numpy as np

from sigmoid import sigmoid

def lrCostFunction(theta, X, y, lambda_value):
    #LRCOSTFUNCTION Compute cost and gradient for logistic regression with
    #regularization
    #   J = LRCOSTFUNCTION(theta, X, y, lambda) computes the cost of using
    #   theta as the parameter for regularized logistic regression and the
    #   gradient of the cost w.r.t. to the parameters.

    # Initialize some useful values
    m = y.size # number of training examples
    if y.dtype == bool:
        y = y.astype(int)

    # You need to return the following variables correctly
    J = 0
    grad = np.zeros(theta.shape)

    # ====================== YOUR CODE HERE ======================
    # Instructions: Compute the cost of a particular choice of theta.
    #               You should set J to the cost.
    #               Compute the partial derivatives and set grad to the partial
    #               derivatives of the cost w.r.t. each parameter in theta
    #
    # Hint: The computation of the cost function and gradients can be
    #       efficiently vectorized. For example, consider the computation
    #
    #           sigmoid(X * theta)
    #
    #       Each row of the resulting matrix will contain the value of the
    #       prediction for that example. You can make use of this to vectorize
    #       the cost function and gradient computations.
    #
    # Hint: When computing the gradient of the regularized cost function,
    #       there're many possible vectorized solutions, but one solution
    #       looks like:
    #           grad = (unregularized gradient for logistic regression)
    #           temp = theta;
    #           temp(1) = 0;   % because we don't add anything for j = 0
    #           grad = grad + YOUR_CODE_HERE (using the temp variable)
    #

    h = sigmoid(np.dot(X, theta))
    cost = np.sum(np.dot(-y.T, np.log(h)) - np.dot((1 - y.T), np.log(1 - h)))
    J = (cost / m) + (lambda_value / (2 * m)) * np.sum(theta[1:] ** 2)
    extra = lambda_value * theta
    extra[0] = 0
    grad = (np.dot(X.T, h - y) + extra.T) / m
    return (J, grad)

    # =============================================================

    return (J, grad)

    #grad = grad(:);

    #end


# -*- coding: utf-8 -*-
"""
Created on Sat Apr 13 23:32:08 2019

@author:    Manuel Martinez
@project:   Mathematics for Machine Learning.
@purpose:   Week 3 programming assignment on orthogonal projections
"""
# =============================================================================
# initialize environment
# =============================================================================
import matplotlib.pyplot as plt
import numpy as np
import numpy.testing as np_test
from sklearn.datasets import fetch_olivetti_faces, fetch_lfw_people
image_shape = (64, 64)
dataset = fetch_olivetti_faces('./')
faces = dataset.data

# %%===========================================================================
# 1. Orthogonal Projections
# =============================================================================

def test_property_projection_matrix(P):
    """Test if the projection matrix satisfies certain properties.
    In particular, we should have P @ P = P, and P = P^T
    """
    np_test.assert_almost_equal(P, P @ P)
    np_test.assert_almost_equal(P, P.T)

def test_property_projection(x, p):
    """Test orthogonality of x and its projection p."""
    np_test.assert_almost_equal(p.T @ (p-x), 0)

# GRADED FUNCTION: DO NOT EDIT THIS LINE

# Projection 1d

# ===YOU SHOULD EDIT THIS FUNCTION===
def projection_matrix_1d(b):
    """Compute the projection matrix onto the space spanned by `b`
    Args:
        b: ndarray of dimension (D, 1), the basis for the subspace

    Returns:
        P: the projection matrix
    """
    D, _ = b.shape
    # Edit the code below to compute a projection matrix of shape (D,D)
    P = (b @ b.T) / np.linalg.norm(b) ** 2
    return P

# ===YOU SHOULD EDIT THIS FUNCTION===
def project_1d(x, b):
    """Compute the projection matrix onto the space spanned by `b`
    Args:
        x: the vector to be projected
        b: ndarray of dimension (D, 1), the basis for the subspace

    Returns:
        y: ndarray of shape (D, 1) projection of x in space spanned by b
    """
    P = projection_matrix_1d(b)
    p = P @ x
    return p

# Projection onto a general (higher-dimensional) subspace
# ===YOU SHOULD EDIT THIS FUNCTION===
def projection_matrix_general(B):
    """Compute the projection matrix onto the space spanned by the
    columns of `B`
    Args:
        B: ndarray of dimension (D, M), the basis for the subspace

    Returns:
        P: the projection matrix
    """
    P = B @ np.linalg.inv(B.T @ B) @ B.T
    return P

# ===YOU SHOULD EDIT THIS FUNCTION===
def project_general(x, B):
    """Compute the projection matrix onto the space spanned by the
    columns of `B`
    Args:
        x: ndarray of dimension (D, 1), the vector to be projected
        B: ndarray of dimension (D, M), the basis for the subspace

    Returns:
        p: projection of x onto the subspac spanned by the columns of B;
        size (D, 1)
    """
    P = projection_matrix_general(B)
    p = P @ x
    return p

# %%===========================================================================
# 2. Eigenfaces
# =============================================================================
# plt.figure(figsize=(10, 10))
# plt.imshow(np.hstack(faces[:5].reshape(5, 64, 64)), cmap='gray')

# for numerical reasons we normalize the dataset
mean = faces.mean(axis=0)
std = faces.std(axis=0)
faces_normalized = (faces - mean) / std

# we use the first 50 basis vectors --- you should play around with this.
B = np.load('eigenfaces.npy')[:50]
print("the eigenfaces have shape {}".format(B.shape))

plt.figure(figsize=(10, 10))
plt.imshow(np.hstack(B[:5].reshape(-1, 64, 64)), cmap='gray')

# EDIT THIS FUNCTION
def show_face_face_reconstruction(i):
    original_face = faces_normalized[i].reshape(64, 64)
    # reshape the data we loaded in variable `B`
    # so that we have a matrix representing the basis.
    B_basis = B.reshape(B.shape[0], -1).T
    face_reconstruction = project_general(faces_normalized[i],
                                          B_basis).reshape(64, 64)
    plt.figure()
    plt.imshow(np.hstack([original_face, face_reconstruction]), cmap='gray')
    plt.show()

# %%===========================================================================
# 3. Least squares regression
# =============================================================================
x = np.linspace(0, 10, num=50)
theta = 2

def f(x):
    # we use the same random seed so we get deterministic output
    random = np.random.RandomState(42)
    # our observations are corrupted by some noise,
    # so that we do not get (x,y) on a line
    return theta * x + random.normal(scale=1.0, size=len(x))

y = f(x)
plt.scatter(x, y)
plt.xlabel('x')
plt.ylabel('y')

# size N x 1
X = x.reshape(-1, 1)
# size N x 1
Y = y.reshape(-1, 1)

# maximum likelihood estimator
theta_hat = np.linalg.solve(X.T @ X, X.T @ Y)
# or
theta_hat = np.linalg.inv(X.T @ X) @ X.T @ Y

fig, ax = plt.subplots()
ax.scatter(x, y)
xx = [0, 10]
yy = [0, 10 * theta_hat[0, 0]]
ax.plot(xx, yy, 'red', alpha=0.5)
ax.set(xlabel='x', ylabel='y')
print("theta = %f" % theta)
print("theta_hat = %f" % theta_hat)

# %%
N = np.arange(2, 10000, step=10)
# Your code comes here, which calculates \hat{\theta} for different
# dataset sizes.
y = f(x)
theta_error = np.ones(N.shape)

for i, n in enumerate(N):
    x = np.linspace(0, 10, num=n)
    y = f(x)
    X = x.reshape(-1, 1)
    Y = y.reshape(-1, 1)
    theta_hat = theta_hat = np.linalg.solve(X.T @ X, X.T @ Y)
    theta_error[i] = 2 - theta_hat

plt.plot(theta_error)
plt.xlabel("dataset size")
plt.ylabel("parameter error")


import numpy as np

def fftfreq_RLyonBook(N, d):
    """ This function is different from fftfreq from scipy
    in that it returns the first N/2+1 plus the second half (N/2-1 in scipy edition) as fftfreq for even N
    for odd N, it returns the first (N+1)/2 plus the second half ((N-1/)2 in scipy edition)
    (this is how Richard Lyon's DSP book states)
    """
    if N % 2 == 0:
        # even
        a1 = np.arange(0, N / 2 + 1, 1)
        a2 = np.arange(-N / 2 + 1, 0, 1)
        return np.concatenate((a1, a2)) / (N * d)
    else:
        # odd
        a1 = np.arange(0, (N + 1) / 2, 1)
        a2 = np.arange(-(N - 1) / 2, 0, 1)
        return np.concatenate((a1, a2)) / (N * d)

def angle2(x):
    """ Angle calculation to avoid Python numpy weird issue for angle function
    np.angle(-0 - 0j) = 0.0 (desired result)
    np.angle(-0.0 - 0.0j) = 3.14 (float numbers are IEEE754, not desired for angle calculation).
    Solution is to convert from float to int for float zero values (0.0)
    """
    res_arr = []

    for i in x:
        imag = i.imag
        real = i.real

        if real == 0 and isinstance(real, float):
            real = 0

        if imag == 0 and isinstance(real, float):
            imag = 0

        res = np.arctan2(imag, real)

        res_arr.append(res)

    return np.array(res_arr)


import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import rosen, differential_evolution
from scipy.interpolate import Rbf

# do this to ignore scipy RBF ill-conditioned matrix warnings
import warnings
warnings.filterwarnings("ignore")

# surrogate optimization on 2D rosenbrock function
def lhs(N,D):
  grid = np.linspace(0,1,N+1)
  result = np.random.uniform(low=grid[:-1], high=grid[1:], size=(D,N))
  for c in result:
    np.random.shuffle(c)
  return result.T

# wrapper around surrogate function, because it requires variables as
# separate arguments (scipy gives a vector)
def stupid_wrapper(X):
  return s(*X)

bounds = [(-3,3)]*2
max_NFE = 3000
N_initial = 100

for seed in range(10):

  # initial sample and surrogate model
  X = lhs(N_initial, 2)*6-3 # scale up by bounds
  Z = np.array([rosen(z) for z in X])
  s = Rbf(X[:,0], X[:,1], Z, function='gaussian')

  nfe = N_initial
  bestf = np.min(Z)
  bestx = X[np.argmin(Z),:]

  while nfe < max_NFE and bestf > 10**-6:

    # optimize surrogate function to find next point
    result = differential_evolution(stupid_wrapper, bounds, polish=False)

    # add the new point to the set of "true" evaluations
    X = np.vstack((X, result.x))
    truef = rosen(result.x)
    Z = np.append(Z, truef)

    # fit a new surrogate model with the updated information
    s = Rbf(X[:,0], X[:,1], Z, function='gaussian')

    if truef < bestf:
      bestf = truef
      bestx = result.x

    nfe += 1

  print('Seed ' + str(seed) + ', NFE-to-converge: ' + str(nfe) + ', solution: ' + str(bestx))


import numpy as np

def e(i, d):
    """Create the i-th axis of the canonical base of the R^d.
    """
    _e = np.zeros((d, 1))
    _e[i, 0] = 1
    return _e

def tau(x, k):
    """Calculate tau_i^k = x_i/x_{k-1}.

    :raise
        ValueError if matrix is not LU decomposable.
    """
    if x[k - 1] == 0:
        raise ValueError('Matrix isn\'t LU decomposable.')

    t = x / x[k - 1]
    t[:k] = 0
    return t.reshape((x.shape[0], 1))

def condition(a):
    """Calculate the condition of the matrix.

    :param a: a numpy matrix.
    """
    return np.norm(a)*np.norm(np.linalg.inv(a))


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

def generate_newton_iters(x0, number):
    yield x0, fabs(x0 - 1.)
    for _ in range(number):
        x0 = x0 - (x0*x0 - 1.)/(2*x0)
        yield x0, fabs(x0 - 1.)

data = np.array(list(generate_newton_iters(2.0, 5)))
iterates, errors = data[:, 0], data[:, 1]

fig, (ax1, ax2) = plt.subplots(1, 2, tight_layout=True) # 1 row, 2 columns

ax1.plot(iterates, "x")
ax1.set_title("Iterates")
ax1.set_xlabel("$i$", usetex=True)
ax1.set_ylabel("$x_i$", usetex=True)

ax2.semilogy(errors, "x") # plot y on a logarithmic scale
ax2.set_title("Error")
ax2.set_xlabel("$i$", usetex=True)
ax2.set_ylabel("Error")

plt.show()


import sympy as sym
import scipy as sci
import numpy as np
from math import *
import sys

#===================================================================================
# Phần thuật toán chính
class jacobi_mat_inversion:
#{
    def __init__(self, A, n, eps):
    #{
        self.A = np.reshape(np.array(A), (n, n));
        self.eps = eps;
        self.n = n;
    #}
    def __norm(self, __A, __norm_type = 2):
    #{
        return np.linalg.norm(__A, __norm_type);
    #}

    def __checkDomination(self, __A): # Kiểm tra tính chéo trội
    #{
        n = int(__A.shape[0]);
        row_dom = col_dom = 1;

        for i in range(n):
        #{
            sum_row = sum_col = 0;
            for j in range(n):
                if(i != j):
                    sum_row += abs(__A[i, j]);
                    sum_col += abs(__A[j, i]);

            if(sum_row >= abs(__A[i, i])): row_dom = 0;
            if(sum_col >= abs(__A[i, i])): col_dom = 0;
        #}

        if(row_dom == 1): return 1;
        if(col_dom == 1): return -1;
        return 0;
    #}
    def __getNorm(self, __A, __domination_status): # Tính chuẩn
    #{
        if(__domination_status == 1): return self.__norm(__A, inf);
        return self.__norm(__A, 1);
    #}
    def __getLambda(self, __A, __domination_status): # Tính lambda
    #{
        if(__domination_status == 1): return 1;

        n = int(__A.shape[0]);
        maxA = minA = __A[0, 0];
        for i in range(n):
        #{
            maxA = max(maxA, __A[i, i]);
            minA = min(minA, __A[i, i]);
        #}

        return maxA / minA;
    #}

    # Tiến trình lặp
    def __predecessor_iteration(self, X_0, B, T, q, lda, p): #SD công thức sai số tiên nghiệm
    #{
        predecessor_norm = self.__getNorm((B @ X_0 + T) - X_0, p);
        X  = X_0;
        qk = 1;

        nr_iteration = 0;
        while(lda * qk * predecessor_norm > self.eps * (1 - q)):
        #{
            nr_iteration += 1;
            X   = B @ X + T;
            qk *= q;
        #}

        print(f"Phương pháp Jacobi đánh giá tiên nghiệm kết thúc sau {nr_iteration} bước lặp", file=sys.stderr);
        return X;
    #}
    def __successor_iteration(self, X_0, B, T, q, lda, p): #SD công thức sai số hậu nghiệm
    #{
        old_X = X_0;
        new_X = B @ X_0 + T;

        nr_iteration = 0;
        while(lda * q * self.__getNorm(new_X - old_X, p) > self.eps * (1 - q)):
        #{
            nr_iteration += 1;
            old_X = new_X;
            new_X = B @ old_X + T;
        #}

        print(f"Phương pháp Jacobi đánh giá hậu nghiệm kết thúc sau {nr_iteration} bước lặp", file=sys.stderr);
        return new_X;
    #}

    # PP lặp Jacobi với chế độ đánh giá tiên nghiệm hoặc hậu nghiệm
    def jacobi_iteration(self, mode = 1):
    #{
        # Gán các biến cơ bản
        A = self.A;
        n = self.n;
        E = np.eye(self.n);

        # chốt 141: Kiểm tra chéo trội và khả nghịch
        p = self.__checkDomination(A);
        if(np.linalg.det(self.A) == 0):
        #{
            print("A không khả nghịch nên không đưa ra được ma trận nghịch đảo");
            return np.full((self.n, self.n), float("NaN"));
        #}
        if(p == 0):
        #{
            print("A không chéo trội nên không đưa ra được ma trận nghịch đảo. Đề xuất: PP Newton");
            return np.full((self.n, self.n), float("NaN"));
        #}

        if(p == 1): print("A chéo trội hàng", file=sys.stderr);
        if(p == -1): print("A chéo trội cột", file=sys.stderr);

        # Tính T, B, q, lambda
        T          = np.diag(1 / np.diag(A));
        B          = E - T @ A;
        q          = self.__getNorm(B, p);
        var_lambda = self.__getLambda(A, p);

        # Đưa ra ma trận cuối cùng
        if(mode == 1): return self.__predecessor_iteration(A, B, T, q, var_lambda, p);
        return self.__successor_iteration(A, B, T, q, var_lambda, p);
    #}
#}

#===================================================================================
# Chương trình ví dụ
# n = # Kích cỡ ma trận
# A = # Ma trận đầu vào
# eps = # Sai số epsilon

# In ra A
# print(A);

# A = np.reshape(np.array(A), (n, n));
# uu = jacobi_mat_inversion(A, n, eps);

# A1 = uu.jacobi_iteration(1);  # Tiên nghiệm
# print(A1);                    # In ra ma trận nghịch đảo
# print(A1 @ A);                # Kiểm tra

# A2 = uu.jacobi_iteration(2);  # Hậu nghiệm
# print(A2);                    # In ra ma trận nghịch đảo
# print(A2 @ A);                # Kiểm tra


"""
This is the Python HW 4
for PSTAT 160B
Prof Ichiba
TA: Mousavi
Section: W 1:00 - 1:50pm
"""

# Import libraries
from __future__ import division
import random
import math
import matplotlib.pyplot as plt
import numpy as np

# In this exercise we shall simulate the two-dimensional Poisson process
# (Poisson Point Process in the plane and its subsets such as a square and a disc).

def Part_A():
	# In the setup of the above Problem 1 simulate the two-dimensional Poisson process
	# in the rectangle region [-1, 1] x [-1, 1]. Choose lambda appropriately

	# So the area of this region corresponds to -1 < x < 1 & -1 < y < 1
	# The length of the x values is 2, as well as for the y values
	# Therefore the area of this region is 2 x 2 = 4
	length = 2
	width = 2
	area = length * width

	# Choose lambda appropriately?
	# Cash me oussside how bout dat?
	lmda = 1

	# Generate randomly occurring event
	# Because this is continuous intensity
	# I assume this is a homogenous Poisson Process
	NT = np.random.poisson(area * lmda)

	random_points = []

	for i in range(0, NT + 1):
		random_point = np.random.uniform(-1, 1)
		random_points.append(random_point)

	sorted_points = sorted(random_points)
	events = [i for i in range(0, NT + 1)]

	# Now lets do a stepwise plot
	plt.step(sorted_points, events)
	plt.show()

def Part_B():
	# Simulate the two-dimensional Poisson process in a circular region (disc) of radius 1 and
	# of center at the origin (0, 0)
	radius = 1
	area = math.pi * (radius ** 2)

	# Choose lambda appropriately?
	# Cash me oussside how bout dat?
	lmda = 1

	# Generate randomly occurring event
	# Because this is continuous intensity
	# I assume this is a homogenous Poisson Process
	NT = np.random.poisson(area * lmda)

	random_points = []

	for i in range(0, NT + 1):
		random_point = np.random.uniform(-1, 1)
		random_points.append(random_point)

	sorted_points = sorted(random_points)
	events = [i for i in range(0, NT + 1)]

	# Now lets do a stepwise plot
	plt.step(sorted_points, events)
	plt.show()

def Part_C():
	# Estimate the cumulative distribution function F(x) := P(R1 <= x) of R1 for every
	# 0 <= x <= c1 (cf. section 11.5.2) and plot the function F(dot) for 0 <= x <= c1 , where c1 is
	# chosen large enough to represent the whole shape of the function F(dot).x
	radius = 1
	overall_area = math.pi * (radius ** 2)
	lmda = 1

	# Simulate a Poisson Process
	NT = np.random.poisson(overall_area * lmda)
	random_points = []

	# Generate uniform random variable as the points
	# We will take the absolute value of them
	# As we are looking at Euclidean distance
	# So we only care about magnitude
	for i in range(0, NT + 1):
		random_point = np.random.uniform(-1, 1)
		random_points.append(abs(random_point))

	# Iterate through many values of x as "current_radius"
	# Assuming the points in the Poisson Process are uniformly distributed
	# There is a uniform chance the points will be within the radius of the circle
	# Thus if we calculate the area for each successive radius value
	# This acts as the value of the CDF
	# Because the increasing circle contains all radius values smaller in magnitude
	# Now let's declare our parameters and counters
	cum_prob = []
	for i in range(0, len(random_points)):
		disc_area = lmda * (math.pi * (random_points[i] ** 2))
		disc_prob = disc_area / (lmda * overall_area)
		cum_prob.append(disc_prob)

	# Now we sort the points & probabilities to get it in the proper order
	sorted_points = sorted(random_points)
	sorted_probs = sorted(cum_prob)

	print(sorted_points)
	print(sorted_probs)
	# Now lets do a  plot
	plt.plot(sorted_points, sorted_probs)
	plt.show()

def Part_D():
	# Estimate the tail probability Gn(x) := P(Rn > x) for x > 0 , 0 <= x <= c2 , where c2 is
	# chosen large enough to represent the whole shape of function Gn(dot) for n = 1, 2, 3, 4 . Plot
	# the function Gn(dot) for n = 1, 2, 3, 4 in the same graph.
	# First we declare our parameters and counters
	radius = 1
	overall_area = math.pi * (radius ** 2)
	lmda = 1

	# Simulate a Poisson Process
	NT = np.random.poisson(overall_area * lmda)
	random_points = []

	# Generate uniform random variable as the points
	# We will take the absolute value of them
	# As we are looking at Euclidean distance
	# So we only care about magnitude
	for i in range(0, NT + 1):
		random_point = np.random.uniform(-1, 1)
		random_points.append(abs(random_point))

	cum_prob = []

	# Here I will generate the same numbers as in Part C
	# But take the compliment of the probabilities
	# This way we will get the "survival function" rather than the CDF
	# Which is equal to P(Rn > x)
	# The same principles I mentioned in Part C work for Part D
	for i in range(0, len(random_points)):
		disc_area = lmda * (math.pi * (random_points[i] ** 2))
		disc_prob = 1 - (disc_area / (lmda * overall_area))
		cum_prob.append(disc_prob)

	# Now we sort the points ascending
	# And the probabilities descending
	sorted_points = sorted(random_points)
	sorted_probs = sorted(cum_prob, reverse = True)

	print(sorted_points)
	print(sorted_probs)
	# Now lets do a  plot
	plt.plot(sorted_points, sorted_probs)
	plt.show()

Part_A()
Part_B()
Part_C()
Part_D()


# https://en.wikipedia.org/wiki/B%C3%A9zier_curve
# https://www.tutorialspoint.com/computer_graphics/computer_graphics_curves.htm
from __future__ import annotations

from scipy.special import comb  # type: ignore

class BezierCurve:
    """
    Bezier curve is a weighted sum of a set of control points.
    Generate Bezier curves from a given set of control points.
    This implementation works only for 2d coordinates in the xy plane.
    """

    def __init__(self, list_of_points: list[tuple[float, float]]):
        """
        list_of_points: Control points in the xy plane on which to interpolate. These
            points control the behavior (shape) of the Bezier curve.
        """
        self.list_of_points = list_of_points
        # Degree determines the flexibility of the curve.
        # Degree = 1 will produce a straight line.
        self.degree = len(list_of_points) - 1

    def basis_function(self, t: float) -> list[float]:
        """
        The basis function determines the weight of each control point at time t.
            t: time value between 0 and 1 inclusive at which to evaluate the basis of
               the curve.
        returns the x, y values of basis function at time t

        >>> curve = BezierCurve([(1,1), (1,2)])
        >>> curve.basis_function(0)
        [1.0, 0.0]
        >>> curve.basis_function(1)
        [0.0, 1.0]
        """
        assert 0 <= t <= 1, "Time t must be between 0 and 1."
        output_values: list[float] = []
        for i in range(len(self.list_of_points)):
            # basis function for each i
            output_values.append(
                comb(self.degree, i) * ((1 - t) ** (self.degree - i)) * (t**i)
            )
        # the basis must sum up to 1 for it to produce a valid Bezier curve.
        assert round(sum(output_values), 5) == 1
        return output_values

    def bezier_curve_function(self, t: float) -> tuple[float, float]:
        """
        The function to produce the values of the Bezier curve at time t.
            t: the value of time t at which to evaluate the Bezier function
        Returns the x, y coordinates of the Bezier curve at time t.
            The first point in the curve is when t = 0.
            The last point in the curve is when t = 1.

        >>> curve = BezierCurve([(1,1), (1,2)])
        >>> curve.bezier_curve_function(0)
        (1.0, 1.0)
        >>> curve.bezier_curve_function(1)
        (1.0, 2.0)
        """

        assert 0 <= t <= 1, "Time t must be between 0 and 1."

        basis_function = self.basis_function(t)
        x = 0.0
        y = 0.0
        for i in range(len(self.list_of_points)):
            # For all points, sum up the product of i-th basis function and i-th point.
            x += basis_function[i] * self.list_of_points[i][0]
            y += basis_function[i] * self.list_of_points[i][1]
        return (x, y)

    def plot_curve(self, step_size: float = 0.01):
        """
        Plots the Bezier curve using matplotlib plotting capabilities.
            step_size: defines the step(s) at which to evaluate the Bezier curve.
            The smaller the step size, the finer the curve produced.
        """
        from matplotlib import pyplot as plt  # type: ignore

        to_plot_x: list[float] = []  # x coordinates of points to plot
        to_plot_y: list[float] = []  # y coordinates of points to plot

        t = 0.0
        while t <= 1:
            value = self.bezier_curve_function(t)
            to_plot_x.append(value[0])
            to_plot_y.append(value[1])
            t += step_size

        x = [i[0] for i in self.list_of_points]
        y = [i[1] for i in self.list_of_points]

        plt.plot(
            to_plot_x,
            to_plot_y,
            color="blue",
            label="Curve of Degree " + str(self.degree),
        )
        plt.scatter(x, y, color="red", label="Control Points")
        plt.legend()
        plt.show()

if __name__ == "__main__":
    import doctest

    doctest.testmod()

    BezierCurve([(1, 2), (3, 5)]).plot_curve()  # degree 1
    BezierCurve([(0, 0), (5, 5), (5, 0)]).plot_curve()  # degree 2
    BezierCurve([(0, 0), (5, 5), (5, 0), (2.5, -2.5)]).plot_curve()  # degree 3


"""
https://www.statology.org/plot-chi-square-distribution-python/
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import chi2

# x-axis ranges from 0 to 20 with .001 steps
x = np.arange(0, 20, 0.001)

# define multiple Chi-square distributions
plt.plot(x, chi2.pdf(x, df=4), label='df: 4', color='gold')
plt.plot(x, chi2.pdf(x, df=8), label='df: 8', color='red')
plt.plot(x, chi2.pdf(x, df=12), label='df: 12', color='pink')

# add legend to plot
plt.legend(title='Parameters')

# add axes labels and a title
plt.ylabel('Density')
plt.xlabel('x')
plt.title('Chi-Square Distributions', fontsize=14)
plt.show()
plt.savefig("chi_squared.png")


#!/usr/bin/env python
"""
Based on https://github.com/danyaal/mandelbrot
Edited by jpdeleon on 2020-05-24 to include jit and argparse
See also https://rosettacode.org/wiki/Mandelbrot_set#Python
"""
import sys
import argparse
import time
import numpy as np
from numba import jit
import matplotlib.pyplot as plt

# counts the number of iterations until the function diverges or
# returns the iteration threshold that we check until
@jit
def countIterationsUntilDivergent(c, threshold):
    z = complex(0, 0)
    for iteration in range(threshold):
        z = (z*z) + c

        if abs(z) > 4:
            break
            pass
        pass
    return iteration

# takes the iteration limit before declaring function as convergent and
# takes the density of the atlas
# create atlas, plot mandelbrot set, display set
def mandelbrot(threshold, density, rlim=None, ilim=None, cmap='RdBu'):
    # location and size of the atlas rectangle
    if rlim is None:
        rlim=(-2.25, 0.75)
    if ilim is None:
        ilim=(-1.5, 1.5)
    #assert isinstance(rlim, tuple)
    #assert isinstance(ilim, tuple)
    realAxis = np.linspace(*rlim, density)
    imaginaryAxis = np.linspace(*ilim, density)
    realAxisLen = len(realAxis)
    imaginaryAxisLen = len(imaginaryAxis)

    # 2-D array to represent mandelbrot atlas
    atlas = np.empty((realAxisLen, imaginaryAxisLen))

    tic = time.perf_counter()

    # color each point in the atlas depending on the iteration count
    for ix in range(realAxisLen):
        for iy in range(imaginaryAxisLen):
            cx = realAxis[ix]
            cy = imaginaryAxis[iy]
            c = complex(cx, cy)

            atlas[ix, iy] = countIterationsUntilDivergent(c, threshold)
            pass
        pass
    toc = time.perf_counter()
    print(f"Runtime: {toc - tic:0.4f} sec")

    # plot and display mandelbrot set
    fig = plt.subplots(constrained_layout=True)
    plt.imshow(atlas.T, interpolation="nearest", cmap=cmap)
    plt.axis("off")
    plt.show()

if __name__=='__main__':
    parser = argparse.ArgumentParser(description="plot mandelbrot set")
    parser.add_argument("-t", "--threshold", type=int, help="threshold", default=100)
    parser.add_argument("-d", "--density", type=int, help="density", default=1000)
    parser.add_argument("-c", "--cmap", type=str, help="color map", default="RdBu")
    parser.add_argument("-z", "--zoom", action="store_true", help="zoom-in", default=False)
    parser.add_argument("-rlim", nargs=2, type=float, help="real axis limits", default=None)
    parser.add_argument("-ilim", nargs=2, type=float, help="imaginary axis limits", default=None)
    args = parser.parse_args()#(None if sys.argv[1:] else ["-h"])
    if args.zoom:
        rlim=(-0.22, -0.219)
        ilim=(-0.70, -0.699)
    else:
        rlim, ilim = None, None
    mandelbrot(args.threshold, args.density, cmap=args.cmap, rlim=rlim, ilim=ilim)


from numbers import Number
from typing import Union

import numpy as np
from pycsou.core.functional import DifferentiableFunctional

class HuberNorm(DifferentiableFunctional):

    M: int = 0
    delta: float = None

    def __init__(self, M: int, delta: float):
        super().__init__(M, is_linear=False, lipschitz_cst=delta, diff_lipschitz_cst=1)
        self.M = M
        self.delta = delta

    def __get_cases_mask(self, x):
        """
        Returns two (complementary) masks, indicating whether each coordinate `x_i` satisfies:
            - `|x_i| <= delta`  or
            - `|x_i| > delta`
        """
        small_coords_mask = np.abs(x) <= self.delta

        return (small_coords_mask, np.logical_not(small_coords_mask))

    def __huber(self, x: np.ndarray):
        (small_coords_mask, big_coords_mask) = self.__get_cases_mask(x)

        # Summing 'small' coordinates: Huber function 1/2 * z^2
        small_coords_value = ((1 / 2) * np.square(x[small_coords_mask])).sum()

        # Summing 'big' coordinates: Huber function delta * (|z| - delta/2)
        big_coords_value = (self.delta * (np.abs(x[big_coords_mask]) - self.delta / 2)).sum()

        return small_coords_value + big_coords_value

    def __dim_check(self, data):
        if data.shape != (self.M,) and data.shape != (self.M, 1):
            raise ValueError("dimension mismatch")

    def __call__(self, arg: Union[Number, np.ndarray]) -> Union[Number, np.ndarray]:
        # Allow shortcut (scalar) notation if `dim==1`
        if isinstance(arg, Number):
            x = np.array([arg])
        else:
            x = arg

        self.__dim_check(arg)

        vals = self.__huber(x)

        return vals

    def __huber_gradient(self, x: np.ndarray):
        (small_coords_mask, big_coords_mask) = self.__get_cases_mask(x)

        # Leverage semantics of multiplication by `bool` array to keep the right size
        small_coords_values = small_coords_mask * x

        big_coords_values = big_coords_mask * (self.delta * np.sign(x))

        return small_coords_values + big_coords_values

    def jacobianT(self, arg: Union[Number, np.ndarray]) -> Union[Number, np.ndarray]:
        # Allow shortcut (scalar) notation if `dim==1`
        if isinstance(arg, Number):
            x = np.array([arg])
        else:
            x = arg

        self.__dim_check(arg)

        gradient = self.__huber_gradient(x)

        if isinstance(arg, Number):
            return gradient[0]
        else:
            return gradient


# Question: https://projecteuler.net/problem=76

# 1: 1
# 2: 1+1,       | 2
# 3: 1+1+1,     | 1+2,             | 3
# 4: 1+1+1+1,   | 1+1+2, 2+2       | 1+3,        | 4
# 5: 1+1+1+1+1, | 1+1+1+2, 1+2+2,  | 1+1+3, 2+3, | 1+4, | 5

import numpy as np

N = 100
# DP[j]: number of ways of writing the sum j
DP = np.ones(N+1, dtype = np.uint64) - 1
DP[0] = 1 # 0
#DP[1] = 1 # 1
#DP[2] = 2 # 2, 1+1

for i in range(1, N+1): # ending in +i
    for j in range(i, N+1):
        DP[j] = DP[j] + DP[j - i]

print(DP[-1]-1)


# Implementation of Kernel PCA and Tests on different Datasets #

from scipy.spatial.distance import pdist, squareform
from scipy import exp
from scipy.linalg import eigh
import numpy as np

import matplotlib.pyplot as plt
from sklearn.datasets import make_moons, make_circles, make_swiss_roll
from sklearn.metrics.pairwise import polynomial_kernel, pairwise_distances

#TODO: Implement the polynomial kernel as an additional option

class Kernel_PCA():

    def __init__(self, data, n_components, kernel_type='rbf', gamma=15):
        self.data = data
        self.n_components = n_components
        self.kernel_type = kernel_type
        self.kernel_dict = {'rbf': self.rbf_kernel, 'poly': self.poly_kernel, 'linear':self.linear_kernel}
        self.gamma = gamma
        self.K_uncentered = None
        self.sel_eigvecs = None
        self.sel_eigvals = None

    def rbf_kernel(self, new_point=None, oos_point=False):

        self.K_uncentered = exp(-self.gamma*(pairwise_distances(X=self.data)**2))

        if oos_point:
            if len(new_point.shape) == 1:
                new_point = new_point[np.newaxis, :]
            return exp(-self.gamma*(pairwise_distances(X=new_point, Y=self.data)**2))

    def poly_kernel(self):
        pass

    def linear_kernel(self, new_point=None, oos_point=False):

        self.K_uncentered = (self.data).dot(self.data.T)

        if oos_point:
            if len(new_point.shape) == 1:
                new_point = new_point[np.newaxis, :]
            return new_point.dot(self.data.T)

    def compute_kernel_pcs(self):

        self.kernel_dict[self.kernel_type]()

        num_samples = self.K_uncentered.shape[0]
        one_n = np.ones((num_samples, num_samples)) / num_samples
        # Center the Kernel-Matrix #
        centered_K = self.K_uncentered - one_n.dot(self.K_uncentered) - self.K_uncentered.dot(one_n) + one_n.dot(self.K_uncentered).dot(one_n)

        eigvals, eigvecs = eigh(centered_K)

        self.sel_eigvecs = eigvecs[:,::-1][:,:self.n_components]
        self.sel_eigvals = eigvals[::-1][:self.n_components]

        assert all(self.sel_eigvals>0.001), "Some Eigenvalues are 0!"

        # Scale the Eigenvectors of the Kernelmatrix with the Singularvalues to obtain
        # the projected lower-dimensional coordinates (Principal Components)
        proj_coordinates = self.sel_eigvecs.dot(np.diag(np.sqrt(self.sel_eigvals)))

        return proj_coordinates, self.sel_eigvecs, self.sel_eigvals

    def project_new_point(self, x_new):
        k = self.kernel_dict[self.kernel_type](new_point=x_new, oos_point=True)
        N = k.shape[1]
        one_n = np.ones((N,1)) / N
        one_n_n = np.ones((N,N)) / N
        k_centered = k  - (one_n.T.dot(self.K_uncentered)) - k.dot(one_n_n) + one_n.T.dot(self.K_uncentered).dot(one_n_n)

        return k_centered.dot(self.sel_eigvecs/ np.sqrt(self.sel_eigvals))


import numpy as np

def mad_std(x, axis=None):
    """
    Robust std.dev using median absolute deviation

    :param x: data values
    :param axis: target axis for computation
    :return: std.dev (MAD)
    """
    return 1.4826 * np.nanmedian(np.abs(x - np.nanmedian(x, axis)), axis)


#!/bin/python3

from sympy import *
from sympy.parsing.sympy_parser import parse_expr, standard_transformations, implicit_multiplication
import sys

def is_expr_linear(expr, vars):
    for x in vars:
        for y in vars:
            try:
                if not Eq(diff(expr, x, y), 0):
                    return False
            except TypeError:
                return False
    return True

def main(inp):
    try:
        transformations = standard_transformations + (implicit_multiplication,)
        expr = parse_expr(inp, transformations=transformations)
    except:
        print('Parse error!')
        return None

    var = list(expr.free_symbols)

    if not is_expr_linear(expr, var):
        return False
    elif expr.is_constant():
        return [expr]
    else:
        ret = list(reversed((Poly(expr).coeffs())))
        return ret

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

    if res is False:
        print('Non linear function >:/')
    else:
        print(res)


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

# This function returns the euclidean distance between the two given points
def distance(p1, p2):
    summation = 0.0
    for i in range(len(p1)):
        summation += (p1[i] - p2[i]) ** 2
    return math.sqrt(summation)

# Calculate the SSW
def get_ssw(data, centroids, labels):
    ssw = 0.0
    for i in range(len(data)):
        ssw += distance(data[i], centroids[labels[i]])
    return ssw / len(centroids)

# Calculate the SSB
def get_ssb(centroids):
    ssb = 0.0
    for i in range(len(centroids)):
        for j in range(len(centroids)):
            if i != j:
                ssb += math.pow(distance(centroids[i], centroids[j]),2)
    return (2 / float(len(centroids) * (len(centroids) - 1))) * ssb

# Plots a set of results
def plot_results(results, name):
    plt.figure(num=None, figsize=(15, 10), dpi=80, facecolor='w', edgecolor='k')
    plt.suptitle(name, fontsize=16)
    for r in results:
        plt.subplot(r[5])
        plt.xlabel(r[0])
        plt.plot(r[2], r[4], color=r[6])
    plt.show()


"""README, Author - Jigyasa Gandhi(mailto:jigsgandhi97@gmail.com)
Requirements:
  - scikit-fuzzy
  - numpy
  - matplotlib
Python:
  - 3.5
"""
# Create universe of discourse in python using linspace ()
import numpy as np

X = np.linspace(start=0, stop=75, num=75, endpoint=True, retstep=False)

# Create two fuzzy sets by defining any membership function (trapmf(), gbellmf(),gaussmf(), etc).
import skfuzzy as fuzz

abc1 = [0, 25, 50]
abc2 = [25, 50, 75]
young = fuzz.membership.trimf(X, abc1)
middle_aged = fuzz.membership.trimf(X, abc2)

# Compute the different operations using inbuilt functions.
one = np.ones(75)
zero = np.zeros((75,))
# 1. Union = max(µA(x), µB(x))
union = fuzz.fuzzy_or(X, young, X, middle_aged)[1]
# 2. Intersection = min(µA(x), µB(x))
intersection = fuzz.fuzzy_and(X, young, X, middle_aged)[1]
# 3. Complement (A) = (1- min(µA(x))
complement_a = fuzz.fuzzy_not(young)
# 4. Difference (A/B) = min(µA(x),(1- µB(x)))
difference = fuzz.fuzzy_and(X, young, X, fuzz.fuzzy_not(middle_aged)[1])[1]
# 5. Algebraic Sum = [µA(x) + µB(x) – (µA(x) * µB(x))]
alg_sum = young + middle_aged - (young * middle_aged)
# 6. Algebraic Product = (µA(x) * µB(x))
alg_product = young * middle_aged
# 7. Bounded Sum = min[1,(µA(x), µB(x))]
bdd_sum = fuzz.fuzzy_and(X, one, X, young + middle_aged)[1]
# 8. Bounded difference = min[0,(µA(x), µB(x))]
bdd_difference = fuzz.fuzzy_or(X, zero, X, young - middle_aged)[1]

# max-min composition
# max-product composition

# Plot each set A, set B and each operation result using plot() and subplot().
import matplotlib.pyplot as plt

plt.figure()

plt.subplot(4, 3, 1)
plt.plot(X, young)
plt.title("Young")
plt.grid(True)

plt.subplot(4, 3, 2)
plt.plot(X, middle_aged)
plt.title("Middle aged")
plt.grid(True)

plt.subplot(4, 3, 3)
plt.plot(X, union)
plt.title("union")
plt.grid(True)

plt.subplot(4, 3, 4)
plt.plot(X, intersection)
plt.title("intersection")
plt.grid(True)

plt.subplot(4, 3, 5)
plt.plot(X, complement_a)
plt.title("complement_a")
plt.grid(True)

plt.subplot(4, 3, 6)
plt.plot(X, difference)
plt.title("difference a/b")
plt.grid(True)

plt.subplot(4, 3, 7)
plt.plot(X, alg_sum)
plt.title("alg_sum")
plt.grid(True)

plt.subplot(4, 3, 8)
plt.plot(X, alg_product)
plt.title("alg_product")
plt.grid(True)

plt.subplot(4, 3, 9)
plt.plot(X, bdd_sum)
plt.title("bdd_sum")
plt.grid(True)

plt.subplot(4, 3, 10)
plt.plot(X, bdd_difference)
plt.title("bdd_difference")
plt.grid(True)

plt.subplots_adjust(hspace=0.5)
plt.show()


"""
This module shows matrices as Numpy objects, emphasizing some matrix properties
and operations.
"""
import numpy as np
from numpy import linalg

np.eye(3)
# array([[1., 0., 0.],
#        [0., 1., 0.],
#        [0., 1., 1.]])

mat = np.array([[1, 2], [3, 4]])
mat.transpose()
# array([[1, 3],
#        [2, 4]])
mat.T
# array([[1, 3],
#        [2, 4]])

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

A = np.array([[1, 2], [3, 4]])
B = np.array([[-1, 1], [0, 1]])
A @ B
# array([[-1, 3],
#        [0, 4]])
A * B
# array([[-1, 2],
#        [0, 4]])

A = np.array([[1, 2], [3, 4]])
I = np.eye(2)
A @ I
# array([[1, 2],
#        [3, 4]])

linalg.det(A) # -2.000000000000004
linalg.inv(A)
# array ([[-2. , 1. ]],
#         [1.5 , -0.5]])

Ainv = linalg.inv(A)
Ainv @ A
# Approximately
# array([[1., 0.]],
#        [0., 1.]])

A @ Ainv
# Approximetaly
# array([[1., 0.]],
#        [0., 1.]])


import numpy as np
import matplotlib.pylab as plt

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

x = np.arange(-10.0, 10.0, 0.1)
y = sigmoid(x)

plt.plot(x,y)
plt.ylim(-0.1,  1.1)
plt.show()


import numpy as np

def multiprod(A, B):
    """
    Inspired by MATLAB multiprod function by Paolo de Leva. A and B are
    assumed to be arrays containing M matrices, that is, A and B have
    dimensions A: (M, N, P), B:(M, P, Q). multiprod multiplies each matrix
    in A with the corresponding matrix in B, using matrix multiplication.
    so multiprod(A, B) has dimensions (M, N, Q).
    """

    # First check if we have been given just one matrix
    if len(np.shape(A)) == 2:
        return np.dot(A, B)

    # Old (slower) implementation:
    # a = A.reshape(np.hstack([np.shape(A), [1]]))
    # b = B.reshape(np.hstack([[np.shape(B)[0]], [1], np.shape(B)[1:]]))
    # return np.sum(a * b, axis=2)

    # Approx 5x faster, only supported by numpy version >= 1.6:
    return np.einsum('ijk,ikl->ijl', A, B)

def multitransp(A):
    """
    Inspired by MATLAB multitransp function by Paolo de Leva. A is assumed to
    be an array containing M matrices, each of which has dimension N x P.
    That is, A is an M x N x P array. Multitransp then returns an array
    containing the M matrix transposes of the matrices in A, each of which
    will be P x N.
    """
    # First check if we have been given just one matrix
    if A.ndim == 2:
        return A.T
    return np.transpose(A, (0, 2, 1))

def multisym(A):
    # Inspired by MATLAB multisym function by Nicholas Boumal.
    return 0.5 * (A + multitransp(A))

def multiskew(A):
    # Inspired by MATLAB multiskew function by Nicholas Boumal.
    return 0.5 * (A - multitransp(A))

def multieye(k, n):
    # Creates a k x n x n array containing k (n x n) identity matrices.
    return np.tile(np.eye(n), (k, 1, 1))

def multilog(A, pos_def=False):
    if not pos_def:
        raise NotImplementedError

    # Computes the logm of each matrix in an array containing k positive
    # definite matrices. This is much faster than scipy.linalg.logm even
    # for a single matrix. Could potentially be improved further.
    l, v = np.linalg.eigh(A)
    l = np.expand_dims(np.log(l), axis=-1)
    return multiprod(v, l * multitransp(v))

def multiexp(A, sym=False):
    if not sym:
        raise NotImplementedError

    # Compute the expm of each matrix in an array of k symmetric matrices.
    # Sometimes faster than scipy.linalg.expm even for a single matrix.
    l, v = np.linalg.eigh(A)
    l = np.expand_dims(np.exp(l), axis=-1)
    return multiprod(v, l * multitransp(v))


import numpy as np
import numpy.linalg as la # linear algebra

from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d

# x, the indep. variable of the func., is an array of size n
def f(x):
  return 0.5*x[0]**2 + 2.5*x[1]**2
  #return np.sin(0.5*x[0]**2-0.5*x[1]**2+3)*np.cos(2*x[0] + 1 -np.exp(x[1]))

def df(x):
  # put the derivative with respect to each component in the array
  d1 = x[0]*np.cos(2*x[0] - np.exp(x[1]) +1)*np.cos(0.5*x[0]**2-0.25*x[1]**2 +3)-2*np.sin( 2*x[0]-np.exp(x[1]+1))*np.sin( 0.5*x[0]**2 -0.25*x[1]**2 +3 )
  d2 = np.exp(x[1]) * np.sin( 2*x[0]-np.exp(x[1])+1) * np.sin( 0.5*x[0]**2 -0.25*x[1]**2 +3)  - 0.5*x[1]* np.cos(2*x[0] - np.exp(x[1]) +1)*np.cos( 0.5*x[0]**2 -0.25*x[1]**2 +3)
  #return np.array([d1 , d2])
  return np.array([x[0], 5*x[1]])

def ddf(x):
  return np.array([[1, 0], [0,5]])

def plot_func(guesses):
  # 3d plot of the function
  Title      = "function"
  fig = plt.figure()
  ax = fig.gca(projection="3d")
  xmesh, ymesh = np.mgrid[-1:3:50j,-1:3:50j]

  fmesh = f(np.array([xmesh, ymesh]))
  surf =ax.plot_surface(xmesh, ymesh, fmesh, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False)
  plt.title(Title, fontsize = 16)
  plt.xlabel("x_1",     fontsize=12)
  plt.ylabel("x_2",     fontsize=12)
  #plt.zlabel("f(x)",     fontsize=12)
  fig.colorbar(surf, shrink=0.5, aspect=5)

  # contour plot of the function
  fig,ax = plt.subplots()
  plt.axis('equal')
  plt.contour(xmesh, ymesh, fmesh,20)
  plt.colorbar();
  plt.xlabel("x_1",     fontsize=12)
  plt.ylabel("x_2",     fontsize=12)
  it_array = np.array(guesses)
  plt.plot(it_array.T[0], it_array.T[1], "x-")
  plt.show()

def conjgrad(tol, max_iters):

    x = [10.0,20.5]
    guesses = [x]
    iters = 0 #iteration counter

    r = -df(x)
    p = r
    rsold = np.dot(np.transpose(r), r)

    for i in range(max_iters):
        first_der = df(x)
        second_der = ddf(x)
        #print("p",p)
        #print("df", first_der)
        #print((np.dot(np.transpose(first_der),p) ))
        #print(( (np.matmul((np.matmul(np.transpose(p),second_der)),p)) ))
        alpha = - (np.dot(np.transpose(first_der),p) )/ ( (np.matmul((np.matmul(np.transpose(p),second_der)),p)) )
        #print("alpha",alpha)
        x = x + np.dot(alpha, p)
        #print("x",x)
        r = -df(x)
        rsnew = np.dot(np.transpose(r), r)
        #print(" error: ",i,np.sqrt(rsnew) )
        guesses.append(x)
        if (np.sqrt(rsnew) < 1e-8) and (i>1):
            break
        p = r + (rsnew/rsold)*p
        rsold = rsnew
        print(" sol: ", x)
    print(" final sol: ", x)
    return guesses

def main():
    print(" Conjugate gradient code starts ...")

    tol = 0.000001 #This tells us when to stop the algorithm
    max_iters = 10 # maximum number of iterations
    guesses = conjgrad(tol, max_iters)

    plot_func(guesses)

    print(" Conjugate gradient terminates successfully!")

if __name__=="__main__":
  main()


"""
last class we talked about modules, what they are, and using them because they have code that performs tasks that we
might care about

we saw the math module last class, along with other useful modules such as numpy, pandas, etc.

there is also a module called the random module

random numbers are often used in computer programs to give your programs the ability to make their own "decisions"

real-life examples:
weapon drops / chest drops in games are randomized using some sort of a random-number generator (rng)

our example:
let's say you're writing a program to play a game of rock-paper-scissors with the user -- we could easily ask the user
which symbol they wanted to play, but how would the computer pick its symbol?

we could pick random numbers, and then map them to specific values ==> f(random_number) --> selected_move
"""

import random

# get a number between 1 and 3: it could be 1, 2, or 3 (the computer will "decide")
# random_num = random.randint(1, 3)
# print(f"your random number is {random_num}")

"""
in any given application, your import statements must be at the top of the program --> ALMOST ALWAYS
"""

"""
if you need a random floating point:
"""

random_floating_num = random.random()  # half open range from [0.0, 1.0) --> [0.0, 3.0) OR [0.0, 10.0) OR [0.0, 37.0)
print(f"your random number is {100 * random_floating_num}")

"""
RECALL: other ways to import modules:

--> import MODULE_NAME
    ex: import random

--> from MODULE_NAME import *  # the * is called a wildcard
    ex: from math import *

OPTIONAL (for now):
--> import MODULE_NAME as ANOTHER_NAME
    ex: import math as m
    ex: import numpy as np
    ex: import pandas as pd
"""


# -*- coding: utf-8 -*-
"""
Created on Mon Oct 10 21:45:26 2016

@author: culpritz
"""
import numpy as np
import random
#import pandas as pd
import matplotlib.pyplot as plt
import algos

def experiment(N):
    random.seed()
    X = []
    for x in range(N):
        x = [1., random.uniform(-1, 1), random.uniform(-1, 1)]
        X.append(x)
    X = np.array(X)

    # gen label y
    Y = []
    x1 = X[random.randint(0,len(X)-1)]
    x2 = X[random.randint(0,len(X)-1)]
    [w0, w1, w2] = [ (x2[2] - x1[2])*x1[1] - (x2[1]-x1[1])*x1[2], -(x2[2] - x1[2]), x2[1] - x1[1]]

    def evalLine(x):
        y =  np.sign(w0*1 + w1*x[1] + w2*x[2])
        return 1 if y>0 else -1

    for x in X:
        Y.append(evalLine(x))
    Y = np.array(Y)

#    Graph
    plt.ylim(-1, 1)
    plt.xlim(-1, 1)
    m = -w1/w2
    c = -w0/w2
    line = np.linspace(-1, 1, 10) # points from 0-1000
    y = m*line + c
    plt.plot(line, y, 'r--', label="f(x)") # makes f(x) line
    plt.scatter(X[:, 1][Y==1], X[:, 2][Y==1], marker="o", c=("r"), label="+")
    plt.scatter(X[:, 1][Y==-1], X[:, 2][Y==-1], marker="o", c=("b"), label="-")# colored based on Y = +1 or -1
    plt.legend(loc="best")

    w = np.array([0.,0.,0.])
    [w, epochs] = algos.LogisticRegression(X, Y, w)
#    return epochs

    # GRAPH
    print w0, w1, w2
    print w
    m = -w[1]/w[2]
    c = -w[0]/w[2]
    line = np.linspace(-1, 1, 10)
    y = m*line + c
    plt.plot(line, y, label="g(x)")

    # Test Simulation
    def simulation(N):
        random.seed()
        X = []
        for x in range(N):
            x = [1, random.uniform(-1, 1), random.uniform(-1, 1)]
            X.append(x)
        Y = []
        for x in X:
            y =  evalLine(x)
        #    print x, y
            Y.append(y)
        X = np.array(X)
        Y = np.array(Y)
        return [X, Y]

    [X, Y] = simulation(N)

    # error check
    return algos.cross_entropy_error(w, X, Y)

avg = 0.
N = 100
repeat_experiment_n = 1
for _ in range(repeat_experiment_n):
    avg += experiment(N)

print 'Avg Ein: {0}'.format(avg/repeat_experiment_n)


import numpy
def moments(image,coords):
    x, y = coords
    x=x.ravel()
    y=y.ravel()
    image=(image*1).ravel()

    mass= sum(image)
    x_bar = sum(x*image)/sum(image)
    y_bar = sum(y*image)/sum(image)
    m00 = sum(image)
    m01 = sum(x*image)
    m10 = sum(y*image)
    m11 = sum(y*x*image)
    m02 = sum(x**2*image)
    m20 = sum(y**2*image)
    m12 = sum(x*y**2*image)
    m21 = sum(x**2*y*image)
    m03 = sum(x**3*image)
    m30 = sum(y**3*image)

    u11 = (m11 - x_bar * m01) / mass
    u20 = (m20 - x_bar * m10) / mass
    u02 = (m02 - y_bar * m01) / mass

    cov = np.array([[u20, u11], [u11, u02]])

    eigvals, eigvecs = numpy.linalg.eigh(cov)
    com=(x_bar,y_bar)
    return com,eigvals,eigvecs

import numpy as np
import matplotlib.pyplot as plt

def raw_moment(data, iord, jord):
        nrows, ncols = data.shape
        y, x = np.mgrid[:nrows, :ncols]
        data = data * x**iord * y**jord
        return data.sum()

def com(data):
    return main(data,comonly=True)
def comflux(data):
    return main(data,comflux=True)

def main(data,comonly=False,comflux=False):
    data_sum = data.sum()
    m10 = raw_moment(data, 1, 0)
    m01 = raw_moment(data, 0, 1)
    x_bar = m10 / data_sum
    y_bar = m01 / data_sum

    flux=data_sum
    mean=data_sum/float(len(data[data!=0]))

    if comonly:return (y_bar,x_bar)
    if comflux:return (y_bar,x_bar),flux,mean

    u11 = (raw_moment(data, 1, 1) - x_bar * m01) / data_sum
    u20 = (raw_moment(data, 2, 0) - x_bar * m10) / data_sum
    u02 = (raw_moment(data, 0, 2) - y_bar * m01) / data_sum
    cov = np.array([[u20, u11], [u11, u02]])

    eigvals, eigvecs = np.linalg.eigh(cov)

    #mean = np.array([x_bar, y_bar])

    a=eigvals[1]**.5
    b=eigvals[0]**.5
    q=b/a

    com=(y_bar,x_bar)
    return com,(a,b,q), eigvals,eigvecs, flux,mean

if __name__ == '__main__':
    com,(a,b,q), eigvecs=main()
    va=eigvecs[:,1]
    xl=50
    yl=50

    ca=(com[0]-xl,com[1]-yl)
    ca/=(ca[0]**2+ca[1]**2)

    theta=numpy.arccos(ca[0]*va[0]+ca[1]*va[1])
    print theta


import numpy as np

from gpflow.base import AnyNDArray

def ref_rbf_kernel(
    X: AnyNDArray, lengthscales: AnyNDArray, signal_variance: AnyNDArray
) -> AnyNDArray:
    N, _ = X.shape
    kernel = np.zeros((N, N))
    for row_index in range(N):
        for column_index in range(N):
            vecA = X[row_index, :]
            vecB = X[column_index, :]
            delta = vecA - vecB
            distance_squared = np.dot(delta.T, delta)
            kernel[row_index, column_index] = signal_variance * np.exp(
                -0.5 * distance_squared / lengthscales ** 2
            )
    return kernel

def ref_arccosine_kernel(
    X: AnyNDArray,
    order: int,
    weight_variances: AnyNDArray,
    bias_variance: AnyNDArray,
    signal_variance: AnyNDArray,
) -> AnyNDArray:
    num_points = X.shape[0]
    kernel = np.empty((num_points, num_points))
    for row in range(num_points):
        for col in range(num_points):
            x = X[row]
            y = X[col]

            numerator = (weight_variances * x).dot(y) + bias_variance

            x_denominator = np.sqrt((weight_variances * x).dot(x) + bias_variance)
            y_denominator = np.sqrt((weight_variances * y).dot(y) + bias_variance)
            denominator = x_denominator * y_denominator

            theta = np.arccos(np.clip(numerator / denominator, -1.0, 1.0))
            if order == 0:
                J = np.pi - theta
            elif order == 1:
                J = np.sin(theta) + (np.pi - theta) * np.cos(theta)
            elif order == 2:
                J = 3.0 * np.sin(theta) * np.cos(theta)
                J += (np.pi - theta) * (1.0 + 2.0 * np.cos(theta) ** 2)

            kernel[row, col] = (
                signal_variance
                * (1.0 / np.pi)
                * J
                * x_denominator ** order
                * y_denominator ** order
            )
    return kernel

def ref_periodic_kernel(
    X: AnyNDArray,
    base_name: str,
    lengthscales: AnyNDArray,
    signal_variance: AnyNDArray,
    period: AnyNDArray,
) -> AnyNDArray:
    """
    Calculates K(X) for the periodic kernel based on various base kernels.
    """
    sine_arg = np.pi * (X[:, None, :] - X[None, :, :]) / period
    sine_base = np.sin(sine_arg) / lengthscales
    if base_name in {"RBF", "SquaredExponential"}:
        dist = 0.5 * np.sum(np.square(sine_base), axis=-1)
        exp_dist = np.exp(-dist)
    elif base_name == "Matern12":
        dist = np.sum(np.abs(sine_base), axis=-1)
        exp_dist = np.exp(-dist)
    elif base_name == "Matern32":
        dist = np.sqrt(3) * np.sum(np.abs(sine_base), axis=-1)
        exp_dist = (1 + dist) * np.exp(-dist)
    elif base_name == "Matern52":
        dist = np.sqrt(5) * np.sum(np.abs(sine_base), axis=-1)
        exp_dist = (1 + dist + dist ** 2 / 3) * np.exp(-dist)
    return signal_variance * exp_dist


import numpy as np

def normalize(x):
    return (x - np.min(x))/(np.max(x) - np.min(x))

def standardize(x):
    return (x - np.mean(x))/(np.std(x))

data = np.random.randn(1, 2, 4)
print('randn产生的随机数:\n', data)
for i in range(data.shape[0]):
    print("原始数据", data[i])
    # 转置处理
    a = data[i].T
    print("a=", a)
    print("归一化", normalize(a))
    print("标准化", standardize(a.T))