import numpy as np

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

def sigmoid_derivative(x):
    """
    Compute the gradient (also called the slope or derivative) of the sigmoid function with respect to its input x.
    You can store the output of the sigmoid function into variables and then use it to calculate the gradient.

    Arguments:
    x -- A scalar or numpy array

    Return:
    ds -- Your computed gradient.
    """

    s = sigmoid(x)
    ds = s * (1-s)

    return ds

def softmax(x):
    """Calculates the softmax for each row of the input x.

    Your code should work for a row vector and also for matrices of shape (m,n).

    Argument:
    x -- A numpy matrix of shape (m,n)

    Returns:
    s -- A numpy matrix equal to the softmax of x, of shape (m,n)
    """

    # Apply exp() element-wise to x. Use np.exp(...).
    x_exp = np.exp(x)

    # Create a vector x_sum that sums each row of x_exp. Use np.sum(..., axis = 1, keepdims = True).
    x_sum = np.sum(x_exp, axis =1, keepdims= True)

    # Compute softmax(x) by dividing x_exp by x_sum. It should automatically use numpy broadcasting.
    s = x_exp/x_sum
    return s

def normalizeRows(x):
    """
    Implement a function that normalizes each row of the matrix x (to have unit length).

    Argument:
    x -- A numpy matrix of shape (n, m)

    Returns:
    x -- The normalized (by row) numpy matrix. You are allowed to modify x.
    """

    x_norm = np.linalg.norm(x,axis=1,keepdims=True)
    x = x/x_norm
    return x

def L1(yhat, y):
    """
    Arguments:
    yhat -- vector of size m (predicted labels)
    y -- vector of size m (true labels)

    Returns:
    loss -- the value of the L1 loss function defined above
    """
    loss = np.sum(abs(y-yhat))
    return loss

def L2(yhat, y):
    """
    Arguments:
    yhat -- vector of size m (predicted labels)
    y -- vector of size m (true labels)

    Returns:
    loss -- the value of the L2 loss function defined above
    """
    loss = np.sum((y-yhat)**2)
    return loss


"""
identity

The identity tool returns an identity array. An identity array is a square matrix with all the main diagonal elements as  and the rest as . The default type of elements is float.

import numpy
print numpy.identity(3) #3 is for  dimension 3 X 3

#Output
[[ 1.  0.  0.]
 [ 0.  1.  0.]
 [ 0.  0.  1.]]
eye

The eye tool returns a 2-D array with 's as the diagonal and 's elsewhere. The diagonal can be main, upper or lower depending on the optional parameter . A positive  is for the upper diagonal, a negative  is for the lower, and a   (default) is for the main diagonal.

import numpy
print numpy.eye(8, 7, k = 1)    # 8 X 7 Dimensional array with first upper diagonal 1.

#Output
[[ 0.  1.  0.  0.  0.  0.  0.]
 [ 0.  0.  1.  0.  0.  0.  0.]
 [ 0.  0.  0.  1.  0.  0.  0.]
 [ 0.  0.  0.  0.  1.  0.  0.]
 [ 0.  0.  0.  0.  0.  1.  0.]
 [ 0.  0.  0.  0.  0.  0.  1.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]]

print numpy.eye(8, 7, k = -2)   # 8 X 7 Dimensional array with second lower diagonal 1.
Task

Your task is to print an array of size X with its main diagonal elements as 's and 's everywhere else.

Note

In order to get alignment correct, please insert the line  below the numpy import.

Input Format

A single line containing the space separated values of  and .
 denotes the rows.
 denotes the columns.

Output Format

Print the desired X array.

Sample Input

3 3
Sample Output

[[ 1.  0.  0.]
 [ 0.  1.  0.]
 [ 0.  0.  1.]]
"""

import numpy as np

np.set_printoptions(legacy="1.13")

N, M = list(map(int, input().split()))
print(np.eye(N, M, 0))


import math
import numpy as np

def safe_slope(x1: float, y1: float, x2: float, y2: float) -> float:
    """Calulates the slope between two points in a way tha avoids division by zeros."""
    x_diff = (x2 - x1)

    if x_diff == 0:
        # Avoid division by zero by return inf.
        return np.inf

    return (y2 - y1) / x_diff

def z_axis_angle(x1: float, y1: float, x2: float, y2: float) -> float:
    """Calulates that angle in degrees [0, 360] that the slope of the provided points
    form with rotation about the z axis."""

    angle = math.degrees(math.atan2(y2 - y1, x2 - x1))

    # Wrap angle to bound it between 0 and 360.
    if angle < 0:
        angle += 360

    if angle > 360:
        angle -= 360

    return angle

def euclidean_distance(x1: float, y1: float, x2: float, y2: float) -> float:
    """Calculates the euclidean distance between two points."""
    return math.sqrt(((x1 - x2)**2) + ((y1 - y2)**2))

def wrap_heading(current_heading: float, new_heading: float) -> float:
    """Determines the offset between the current and provided heading to adjust
    them such that the normal subtraction between them results in an angle less than
    or equal to 180 degrees."""
    if abs(new_heading - current_heading) > 180:
      if new_heading > current_heading:
        new_heading -= 360

      else:
        new_heading += 360

    return new_heading

def angular_distance(a1: float, a2: float) -> float:
    """Calculates the angular distance between two angles in degrees."""
    phi = abs(a2 - a1) % 360;
    distance = 360 - phi if phi > 180 else phi;
    return distance;


# Ejercicio 1

import numpy as np
import matplotlib.pylab as plt

# Use esta funcion que recibe un valor x y retorna un valor f(x) donde f es la forma funcional que debe seguir su distribucion.
def mifun(x):
    x_0 = 3.0
    a = 0.01
    return np.exp(-(x**2))/((x-x_0)**2 + a**2)

# Dentro de una funcion que reciba como parametros el numero de pasos y el sigma de la distribucion gausiana que va a usar para calcular el paso de su caminata, implemente el algortimo de Metropolis-Hastings. Finalmente, haga un histograma de los datos obtenidos y grafique en la misma grafica, la funcion de distribucion de probabilidad fx (Ojo, aca debe normalizar). Guarde la grafica sin mostrarla en un pdf. Use plt.savefig("histograma_"+str(sigma)+"_"+str(pasos)+".pdf"), donde sigma y pasos son los parametros que recibe la funcion.

def metropolis(x,f,N,sigma):
    datos=np.zeros(N)
    datos[0]=np.random.random()*max(x)
    for i in range(1,N):
        xold=datos[i-1]
        xnew=np.random.normal(xold,sigma)
        alpha=f(xnew)/f(xold)
        if(alpha>1):
            datos[i]=xnew
        else:
            beta=np.random.random()
            if(beta<alpha):
                datos[i]=xnew
            else:
                datos[i]=xold
    return datos

# Cuando haya verificado que su codigo funciona, use los siguientes parametros:
# sigma = 5, pasos =100000
# sigma = 0.2, pasos =100000
# sigma = 0.01, pasos =100000
# sigma = 0.1, pasos =1000
# sigma = 0.1, pasos =100000
# este puede ser muy demorado dependiendo del computador: sigma = 0.1, pasos =500000
sigma=[5,0.2,0.01,0.1,0.1,0.1]
pasos=[100000,100000,100000,1000,100000,500000]

# Al ejecutar el codigo, este debe generar 6 (o 5) graficas .pdf una para cada vez que se llama a la funcion.

x=np.linspace(-4,4,1000)
y=mifun(x)/np.sum(mifun(x)*(x[1]-x[0]))

for i in range(len(pasos)):
    s=sigma[i]
    N=pasos[i]
    MH=metropolis(x,mifun,N,s)

    plt.figure()
    plt.hist(MH,bins=100,density=True,label="MH")
    plt.plot(x,y,label="Función")
    plt.xlabel("x")
    plt.ylabel("y")
    plt.legend()
    plt.title('Metropolis con $\sigma$ ='+str(s)+' y '+str(N)+ ' pasos.')
    plt.savefig("histograma_"+str(s)+"_"+str(N)+".png")
    plt.close()


import numpy as np
import matplotlib.pyplot as plt

np.random.seed(100)
X = np.arange(1, 51, 1).reshape(50, 1)
u = np.random.uniform(-1, 1, (50, 1))
Y = u + X

X_temp = np.vstack((np.ones((1, 50)), X.T))
X_psuedo_inverse = np.matmul(X_temp.T, np.linalg.inv(np.matmul(X_temp, X_temp.T)))

W = np.matmul(Y.reshape(1, 50), X_psuedo_inverse)

f_X = W[0][1]*X[:, 0] + W[0][0]
plt.plot(X[:, 0], Y[:, 0], 'o')
plt.plot(X[:, 0], f_X, 'r', label = 'Line to fit data')
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.title("Linear Least Squares Fit")
plt.legend()
plt.show()

w = np.array([0.15, 0.6])
eta = 0.00001

f_X = w[1]*X[:, 0] + w[0]
plt.plot(X[:, 0], Y[:, 0], 'o')
plt.plot(X[:, 0], f_X, 'r', label = 'Line to fit data')
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.title("Gradient Descent Fit before training")
plt.legend()
plt.show()
print(w, eta)

z = 0
while True:
    z+=1
    grad = np.zeros((2,))
    for i in range(50):
        grad[0] += (Y[i, 0] - w[0] - w[1]*X[i, 0])
    grad[0] *= -2

    for i in range(50):
        grad[1] += (Y[i, 0] - w[0] - w[1]*X[i, 0])*X[i, 0]
    grad[1] *= -2

    delta_w = eta * grad
    new_w = w - delta_w

    if np.linalg.norm(w - new_w) < 0.0001:
        break
    else:
        w = new_w
        wtemp = new_w
    if z == 1:
        f_X = w[1]*X[:, 0] + w[0]
        plt.plot(X[:, 0], Y[:, 0], 'o')
        plt.plot(X[:, 0], f_X, 'r', label = 'Line to fit data')
        plt.xlabel("X-axis")
        plt.ylabel("Y-axis")
        plt.title("Gradient Descent Fit after 1 epoch")
        plt.legend()
        plt.show()

f_X = w[1]*X[:, 0] + w[0]
plt.plot(X[:, 0], Y[:, 0], 'o')
plt.plot(X[:, 0], f_X, 'r', label = 'Line to fit data')
plt.xlabel("X-axis")
plt.ylabel("Y-axis")
plt.title(f"Gradient Descent Fit after {z} epochs")
plt.legend()
plt.show()

print("DIFF", W - w.reshape(1, 2))


from math import fabs as fabs
from math import floor as floor
from math import sqrt as sqrt
from scipy.special import erfc as erfc
from scipy.special import gammaincc as gammaincc

class FrequencyTest:

    @staticmethod
    def monobit_test(binary_data:str, verbose=False):
        """
        The focus of the test is the proportion of zeroes and ones for the entire sequence.
        The purpose of this test is to determine whether the number of ones and zeros in a sequence are approximately
        the same as would be expected for a truly random sequence. The test assesses the closeness of the fraction of
        ones to 陆, that is, the number of ones and zeroes in a sequence should be about the same.
        All subsequent tests depend on the passing of this test.

        if p_value < 0.01, then conclude that the sequence is non-random (return False).
        Otherwise, conclude that the the sequence is random (return True).

        :param      binary_data         The seuqnce of bit being tested
        :param      verbose             True to display the debug messgae, False to turn off debug message
        :return:    (p_value, bool)     A tuple which contain the p_value and result of frequency_test(True or False)

        """

        length_of_bit_string = len(binary_data)

        # Variable for S(n)
        count = 0
        # Iterate each bit in the string and compute for S(n)
        for bit in binary_data:
            if bit == '0':
                # If bit is 0, then -1 from the S(n)
                count -= 1
            elif bit == '1':
                # If bit is 1, then +1 to the S(n)
                count += 1

        # Compute the test statistic
        sObs = count / sqrt(length_of_bit_string)

        # Compute p-Value
        p_value = erfc(fabs(sObs) / sqrt(2))

        if verbose:
            print('Frequency Test (Monobit Test) DEBUG BEGIN:')
            print("\tLength of input:\t", length_of_bit_string)
            print('\t# of \'0\':\t\t\t', binary_data.count('0'))
            print('\t# of \'1\':\t\t\t', binary_data.count('1'))
            print('\tS(n):\t\t\t\t', count)
            print('\tsObs:\t\t\t\t', sObs)
            print('\tf:\t\t\t\t\t',fabs(sObs) / sqrt(2))
            print('\tP-Value:\t\t\t', p_value)
            print('DEBUG END.')

        # return a p_value and randomness result
        return (p_value, (p_value >= 0.01))

    @staticmethod
    def block_frequency(binary_data:str, block_size=128, verbose=False):
        """
        The focus of the test is the proportion of ones within M-bit blocks.
        The purpose of this test is to determine whether the frequency of ones in an M-bit block is approximately M/2,
        as would be expected under an assumption of randomness.
        For block size M=1, this test degenerates to test 1, the Frequency (Monobit) test.

        :param      binary_data:        The length of each block
        :param      block_size:         The seuqnce of bit being tested
        :param      verbose             True to display the debug messgae, False to turn off debug message
        :return:    (p_value, bool)     A tuple which contain the p_value and result of frequency_test(True or False)
        """

        length_of_bit_string = len(binary_data)

        if length_of_bit_string < block_size:
            block_size = length_of_bit_string

        # Compute the number of blocks based on the input given.  Discard the remainder
        number_of_blocks = floor(length_of_bit_string / block_size)

        if number_of_blocks == 1:
            # For block size M=1, this test degenerates to test 1, the Frequency (Monobit) test.
            return FrequencyTest.monobit_test(binary_data[0:block_size])

        # Initialized variables
        block_start = 0
        block_end = block_size
        proportion_sum = 0.0

        # Create a for loop to process each block
        for counter in range(number_of_blocks):
            # Partition the input sequence and get the data for block
            block_data = binary_data[block_start:block_end]

            # Determine the proportion 蟺i of ones in each M-bit
            one_count = 0
            for bit in block_data:
                if bit == '1':
                    one_count += 1
            # compute π
            pi = one_count / block_size

            # Compute Σ(πi -½)^2.
            proportion_sum += pow(pi - 0.5, 2.0)

            # Next Block
            block_start += block_size
            block_end += block_size

        # Compute 4M Σ(πi -½)^2.
        result = 4.0 * block_size * proportion_sum

        # Compute P-Value
        p_value = gammaincc(number_of_blocks / 2, result / 2)

        if verbose:
            print('Frequency Test (Block Frequency Test) DEBUG BEGIN:')
            print("\tLength of input:\t", length_of_bit_string)
            print("\tSize of Block:\t\t", block_size)
            print('\tNumber of Blocks:\t', number_of_blocks)
            print('\tCHI Squared:\t\t', result)
            print('\t1st:\t\t\t\t', number_of_blocks / 2)
            print('\t2nd:\t\t\t\t', result / 2)
            print('\tP-Value:\t\t\t', p_value)
            print('DEBUG END.')

        return (p_value, (p_value >= 0.01))


from matplotlib import markers
import numpy as np
import matplotlib.pyplot as plt

# Langrange Polynomial of n^th Degree - The Degree is Specified by the Data we Use
def P(x_data, f_data, pos, x):  # pos is an array with elements the positions of x_data we use

    # n = Degree of the polynomial + 1
    n = len(pos)

    def L(x_data, pos, x):

        L = np.ones(n)

        # Polynomial that goes through x_data[pos[i]]
        for i in range(n):
            for j in range(n):
                if pos[i] != pos[j]:
                    L[i] *= (x - x_data[pos[j]]) / (x_data[pos[i]] - x_data[pos[j]])
        return L

    P = 0.0

    for i in range(n):
            P += f_data[pos[i]] * L(x_data, pos, x)[i]

    return P

# Function to Generate Points for Graphs
def graph(pos, n = 150):
    x_graph = np.linspace(x_data[np.amin(pos)], x_data[np.amax(pos)], n)
    y_graph = np.zeros(len(x_graph))

    i = 0

    for x in x_graph:
        y_graph[i] += [P(x_data, f_data, pos, x)]
        i += 1

    return x_graph, y_graph

if __name__ == "__main__":
    # Some Data
    x_data = [-1.6, -1.0, -0.4, 0.2, 0.8, 1.4]
    f_data = [0.278037, 0.606531, 0.923116, 0.980199, 0.726149, 0.375311]

    print('\nCompuated with Lagrange Polynomials of 3^rd Degree:')
    print(f'\tf(0) = {P(x_data, f_data, [1, 2, 3, 4], 0):.6f}')
    print(f'\tf(1) = {P(x_data, f_data, [2, 3, 4, 5], 1):.6f}')

    print('\nCompuated with Lagrange Polynomials of 4^th Degree:')
    print(f'\tf(0) = {P(x_data, f_data, [1, 2, 3, 4, 5], 0):.6f}')
    print(f'\tf(1) = {P(x_data, f_data, [1, 2, 3, 4, 5], 1):.6f}')

    # Visualize the Lagrange Polynomials
    plt.subplot(1, 1, 1)
    plt.plot(graph([1, 2, 3, 4])[0], graph([1, 2, 3, 4])[1], linewidth = 1, label = 'Third Degree with pos = [1, 2, 3, 4]')
    plt.plot(graph([2, 3, 4, 5])[0], graph([2, 3, 4, 5])[1], linewidth = 1, label = 'Third Degree with pos = [2, 3, 4, 5]')
    plt.plot(graph([1, 2, 3, 4, 5])[0], graph([1, 2, 3, 4, 5])[1], linewidth = 1, label = 'Fourth Degree with pos = [1, 2, 3, 4, 5]')
    plt.scatter(x_data, f_data, color = 'black', marker = ".", label = 'Sample Points')

    plt.legend(loc = 'lower center', prop = {'size': 10})
    plt.ylabel('$y$')
    plt.xlabel('$x$')
    plt.title('Lagrange Method')
    plt.show()


def approx_first_derivative(f,x,h):
    """
    Numerical differentiation by finite differences. Uses central point formula
    to approximate first derivative of function.
    Args:
        f (function): function definition.
        x (float): point where first derivative will be approximated
        h (float): step size for central differences. Tipically less than 1
    Returns:
        df (float): approximation to first_derivative.
    """
    df = (f(x+h) - f(x-h))/(2.0*h)
    return df

def approx_second_derivative(f,x,h):
    """
    Numerical differentiation by finite differences. Uses central point formula
    to approximate second derivative of function.
    Args:
        f (function): function definition.
        x (float): point where second derivative will be approximated
        h (float): step size for central differences. Tipically less than 1
    Returns:
        ddf (float): approximation to second_derivative.
    """
    ddf =(f(x+h) - 2.0*f(x) + f(x-h))/h**2
    return ddf

def main_function():
    """
    Main execution function
      args:
        -
      returns:
        -
    """

    ## Python libraries
    import numpy as np

    ## Parameters
    f = np.arctan
    x = 0.9
    h = 1e-6

    ## Main code
    res_first_d = approx_first_derivative(f,x,h)
    res_second_d = approx_second_derivative(f,x,h)

    print(res_first_d)
    print(res_second_d)

if __name__ == "__main__":
    main_function()


# based on https://rpubs.com/andersgs/my_solutions_chapter2_statrethink
# P(twins|A) = 10%
p_twins_a = 0.1
# P(single infant|A) = 90%
p_single_a = 0.9
# P(twins|B) = 20%
p_twins_b = 0.2
# P(single infant| B) = 80%
p_single_b = 0.8
# P(A) = P(B) = 0.5\
p_a, p_b = 0.5, 0.5

# P(twins) = P(twins|A)*P(A) + P(twins|B)*P(B)
p_twins = p_twins_a * p_a + p_twins_b * p_b

# P(A|twins) = P(twins|A)*P(A)/P(twins)
p_a_twins = p_twins_a * p_a / p_twins

# P(B|twins) = P(twins|B)*P(B)/P(twins)
p_b_twins = p_twins_b * p_b / p_twins

#
result = p_a_twins * p_twins_a + p_b_twins * p_twins_b

print('result 2h1 = %f' % (result))

# 2h2 - P(A|twins)
print('result 2h2 = %f' % (p_a_twins))

# 2h3
# P(A | twins, single) = P(twins|A)*P(single|A)*P(A)/P(twins,single)
# P(twins,single) = P(twins|A)*P(single|A)*P(A) + P(twins|B)*P(single|B)*P(B)
p_twins_single = p_twins_a * p_single_a * p_a + p_twins_b * p_single_b * p_b
p_a_twins_single = p_twins_a * p_single_a * p_a / p_twins_single
print('result 2h3=%f' % p_a_twins_single)

# 2h4
p_test_a_a = 0.8
p_test_b_a = 0.2
p_test_b_b = 0.65
p_test_a_b = 0.35

# P(A|testA) = P(testA|A)*P(A)/P(testA)
# P(testA) = P(testA|A)*P(A)+P(testA|B)*P(B)
p_test_a = p_test_a_a * p_a + p_test_a_b * p_b
p_a_test_a = p_test_a_a * p_a / p_test_a
print("result 2h4_1=%f" % p_a_test_a)

# P(A|testA,twins,single) = P(testA|A)*P(twins|A)*P(single|A)*P(A)/P(testA,twins,single)
# P(testA,twins,single) =
#   P(testA|A)*P(twins|A)*P(single|A)*P(A) +
#   P(testA|B)*P(twins|B)*P(single|B)*P(B)

p_test_a_twins_single = p_test_a_a * p_twins_a * p_single_a * p_a + p_test_a_b * p_twins_b * p_single_b * p_b
p_a_testa_twins_single = p_test_a_a * p_twins_a * p_single_a * p_a/p_test_a_twins_single
print('result 2h4_2=%f'%p_a_testa_twins_single)

print('---different approach---')

# based on https://github.com/cavaunpeu/statistical-rethinking/blob/master/chapter-2/homework.R
import numpy as np

like = np.array([p_twins_a, p_twins_b])
prior = np.repeat(1., 2)
post = like * prior
post = post / post.sum()
post_res = post.dot(like)
print("result 2h1=%f" % post_res)
print("result 2h2=%f" % post[0])

like = np.array([p_single_a, p_single_b])
# last posterior is new prior
post = like * post
post = post / post.sum()
print('result 2h3=%f' % post[0])

#2h4-1
like = np.array([p_test_a_a, p_test_a_b])
prior = np.repeat(1., 2)
post = like * prior
post = post / post.sum()
print("result 2h4_1=%f" % post[0])

#2h4-2
# first resolve post from birthing twins
like = np.array([p_twins_a, p_twins_b])
prior = np.repeat(1., 2)
post = like * prior
post = post / post.sum()
like = np.array([p_single_a, p_single_b])
# then resolve posterior after birthing also single
post = like * post
post = post / post.sum()
# then after these events, also resolve the event on testing for A
like = np.array([p_test_a_a, p_test_a_b])
post = like * post
post = post / post.sum()
print("result 2h4_2=%f" % post[0])


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

import numpy as np

# Table Utilities

def is_associative(table):
    indices = range(len(table))
    result = True
    for a in indices:
        for b in indices:
            for c in indices:
                ab = table[a][b]
                bc = table[b][c]
                if not (table[ab][c] == table[a][bc]):
                    result = False
                    break
    return result

def is_commutative(table):
    indices = range(len(table))
    result = True
    for a in indices:
        for b in indices:
            if table[a][b] != table[b][a]:
                result = False
                break
    return result

def has_left_identity(table):
    indices = range(len(table))
    identity = None
    for x in indices:
        if all(table[x][y] == y for y in indices):
            identity = x
            break
    return identity

def has_right_identity(table):
    indices = range(len(table))
    identity = None
    for x in indices:
        if all(table[y][x] == y for y in indices):
            identity = x
            break
    return identity

def has_identity(table):
    left_id = has_left_identity(table)
    right_id = has_right_identity(table)
    if (left_id is not None) and (right_id is not None):
        return left_id
    else:
        return None

def has_inverses(table):
    return False

def inverse_lookup_dict(table, identity):
    elements = range(len(table))
    row_indices, col_indices = np.where(table == identity)
    return {elements[elem_index]: elements[elem_inv_index]
            for (elem_index, elem_inv_index)
            in zip(row_indices, col_indices)}

if __name__ == '__main__':

    print("\n=======================================================================")

    print("\n--------------")
    print("START OF TESTS")
    print("--------------")

    import pprint as pp

    # Table Tests

    print("\nTable Tests:\n")

    # not assoc; is comm; no identity -- the RPS magma table, above
    tbl1 = [[0, 1, 0], [1, 1, 2], [0, 2, 2]]

    # is assoc; not comm; has identity (0) --- the S3 group table
    tbl2 = [[0, 1, 2, 3, 4, 5], [1, 2, 0, 5, 3, 4], [2, 0, 1, 4, 5, 3],
            [3, 4, 5, 0, 1, 2], [4, 5, 3, 2, 0, 1], [5, 3, 4, 1, 2, 0]]

    # is assoc; is comm; has identity (0) --- the Z4 group table
    tbl3 = [[0, 1, 2, 3], [1, 2, 3, 0], [2, 3, 0, 1], [3, 0, 1, 2]]

    # powerset(3) group table
    tbl4 = [[0, 1, 2, 3, 4, 5, 6, 7], [1, 0, 4, 5, 2, 3, 7, 6], [2, 4, 0, 6, 1, 7, 3, 5],
            [3, 5, 6, 0, 7, 1, 2, 4], [4, 2, 1, 7, 0, 6, 5, 3], [5, 3, 7, 1, 6, 0, 4, 2],
            [6, 7, 3, 2, 5, 4, 0, 1], [7, 6, 5, 4, 3, 2, 1, 0]]

    tbl5 = [[0, 3, 0, 3, 0, 3], [1, 4, 1, 4, 1, 4], [2, 5, 2, 5, 2, 5],
            [3, 0, 3, 0, 3, 0], [4, 1, 4, 1, 4, 1], [5, 2, 5, 2, 5, 2]]

    test_tables = [tbl1, tbl2, tbl3, tbl4, tbl5]

    for tbl in test_tables:
        pp.pprint(tbl)
        print()

    print("   Table     Associative?  Commutative?   Left Id?   Right Id?  Identity?")
    print('-' * 75)
    for tbl in test_tables:
        i = test_tables.index(tbl) + 1
        is_assoc = str(is_associative(tbl))
        is_comm = str(is_commutative(tbl))
        lft_id = str(has_left_identity(tbl))
        rgt_id = str(has_right_identity(tbl))
        ident = str(has_identity(tbl))
        print(f"{i :>{6}} {is_assoc :>{14}} {is_comm :>{12}} {lft_id :>{12}} {rgt_id :>{12}} {ident :>{10}}")

    print("\n------------")
    print("END OF TESTS")
    print("------------")


import numpy as np

rad = np.pi/2

arr_rad = np.array([np.pi/2, np.pi/3, np.pi/4, np.pi/5])

### 1.

# 1.
print(np.sin(rad))
print(np.sin(arr_rad))

# 2.
print(np.cos(rad))
print(np.cos(arr_rad))

# 3.
print(np.tan(rad))
print(np.tan(arr_rad))

### 2.

# 1.
print(np.rad2deg(rad))
print(np.rad2deg(arr_rad))

# 2.
print(np.deg2rad(180))
print(np.deg2rad([360, 180, 90, 270]))

### 3.

# 1.
print(np.arcsin(1.0))
print(np.arcsin([1.0, 0.8660254, 0.70710678, 0.58778525]))

# 2.
print(np.arccos(6.123233995736766e-17))
print(np.arccos([6.12323400e-17, 5.00000000e-01, 7.07106781e-01, 8.09016994e-01]))

# 3.
print(np.arctan(1.633123935319537e+16))
print(np.arctan([1.63312394e+16, 1.73205081e+00, 1.00000000e+00, 7.26542528e-01]))

###

base = 5
perp = 8

print(np.hypot(base, perp))

Tutorial/Code/38. NumPy Trigonometric Functions.py


import numpy
from numpy import linalg as LA

# Input the matrix X
x = numpy.matrix([[5.1, 160, 82000],
                  [5.2, 170, 84000],
                  [5.3, 180, 86000],
                  [5.4, 190, 88000],
                  [5.5, 200, 90000],
                  [5.6, 110, 81000],
                  [5.7, 120, 83000],
                  [5.8, 130, 85000],
                  [5.9, 140, 87000],
                  [6.0, 150, 89000]])

print("Input Matrix = \n", x)

print("Number of Dimensions = ", x.ndim)

print("Number of Rows = ", numpy.size(x,0))
print("Number of Columns = ", numpy.size(x,1))

xtx = x.transpose() * x
print("t(x) * x = \n", xtx)

# Eigenvalue decomposition
evals, evecs = LA.eigh(xtx)
print("Eigenvalues of x = \n", evals)
print("Eigenvectors of x = \n",evecs)

# Here is the transformation matrix
dvals = 1.0 / numpy.sqrt(evals)
transf = evecs * numpy.diagflat(dvals)
print("Transformation Matrix = \n", transf)

# Here is the transformed X
transf_x = x * transf;
print("The Transformed x = \n", transf_x)

# Check columns of transformed X
xtx = transf_x.transpose() * transf_x;
print("Expect an Identity Matrix = \n", xtx)

# Orthonormalize using the orth function
import scipy
from scipy import linalg as LA2

orthx = LA2.orth(x)
print("The orthonormalize x = \n", orthx)

# Check columns of the ORTH function
check = orthx.transpose().dot(orthx)
print("Also Expect an Identity Matrix = \n", check)

import numpy,from numpy,import scipy,from scipy


#!/usr/bin/env python3
import sys
import numpy as np

np.random.seed(2017)

def inside_circle(total_count):

    x = np.random.uniform(size=total_count)
    y = np.random.uniform(size=total_count)

    radii_square = x**2 + y**2

    count = (radii_square<=1.0).sum()

    return count

def estimate_pi(n_samples):

    return (4.0 * inside_circle(n_samples) / n_samples)

if __name__=='__main__':

    n_samples = 10000
    if len(sys.argv) > 1:
        n_samples = int(sys.argv[1])

    my_pi = estimate_pi(n_samples)
    sizeof = np.dtype(np.float64).itemsize

    print("[serial version] required memory {:.3f} MB".format(n_samples*sizeof*3/(1024*1024)))
    print("[serial version] pi is {} from {} samples".format(my_pi,n_samples))
    print("[serial version] got {} digits right".format(int(np.ceil(-np.log10(abs(my_pi - np.pi))))))


import numpy as np

'''
Exercise 0 (ungraded).
One reason to consider Numpy is that it "can be much faster," as noted above.
But how much faster is that? Run the experiment below to see.
'''

n = 1000000

L = range(n)
%timeit [i**2 for i in L]
# 269 ms ± 729 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

np.arange(10)  # Moral equivalent to `range`

A = np.arange(n)
%timeit A**2

#699 µs ± 5.51 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

'''
Exercise 1 (ungraded).
The following code creates an identity matrix in two different ways, which are found to be equal according to the assertion.
But in fact there is a subtle difference between the I and I_u matrices created below; can you spot it?
'''
n = 3
I = np.eye(n)

print("==> I = eye(n):")
print(I)

u = [1] * n
I_u = np.diag(u)

print("\n==> u:\n", u)
print("==> I_u = diag (u):\n", I_u)

assert np.all(I_u == I)

#Difference is between types, eyes is a float-point numbers, while daig is integer.

'''
Exercise 2 (5 points). Consider the following  6×66×6  matrix, which has 4 different subsets highlighted.
For each subset illustrated above, write an indexing or slicing expression that extracts the subset.
Store the result of each slice into Z_green, Z_red, Z_orange, and Z_cyan.
'''

Z= np.array([[0,1,2,3,4,5],[10,11,12,13,14,15],[20,21,22,23,24,25],[30,31,32,33,34,35],[40,41,42,43,44,45],[50,51,52,53,54,55]])

# Construct `Z_green`, `Z_red`, `Z_orange`, and `Z_cyan`:
Z_orange = Z[0, 3:5]
Z_red = Z[:, 2]
Z_green = Z[2::2, ::2]
Z_cyan = Z[4:, 4:]

'''
Exercise 3 (1 point).
Given the input array, x[:], above, create an array, mask_mult_3[:] such that mask_mult_3[i] is true only if x[i] is a positive multiple of 3.
'''

mask_mult_3 = (x > 0) & (x % 3 == 0)

'''
Exercise 4 (3 points). Complete the prime number sieve algorithm, which is illustrated below.
'''

from math import sqrt

def sieve(n):
    """
    Returns the prime number 'sieve' shown above.

    That is, this function returns an array `X[0:n+1]`
    such that `X[i]` is true if and only if `i` is prime.
    """
    is_prime = np.empty(n + 1, dtype=bool)  # the "sieve"

    # Initial values
    is_prime[0:2] = False  # {0, 1} are _not_ considered prime
    is_prime[2:] = True  # All other values might be prime

    # Implement the sieving loop

    for k in range(2, int(sqrt(n)) + 1):
        is_prime[2 * k::k] = False

    return is_prime

# Prints your primes
print("==> Primes through 20:\n", np.nonzero(sieve(20))[0])

EdX/GTx: CSE6040x: FA18 - Computing for Data Analysis/Module 2: The analysis of data/Topic 10: Numerical computing with Numpy-Scipy/Notebook_10.py


import numpy as np
import pandas as pd
import scipy
from scipy.stats import norm

def semi_deviation(r):
    """
    Returns the semi deviation, aka negative semi deviation of r
    r must be a Series or a DataFrame
    """
    is_negative = r < 0
    return r[is_negative].std(ddof=0)

def skewness(r):
    """
    Alternate to scipy.stats.skew()
    Computes the skewness of the supplied Series or  DataFrame
    Returns a float or a series
    """
    demeaned_r = r - r.mean()
    sigma_r = r.std(ddof=0)
    exp = (demeaned_r ** 3).mean()
    return exp / sigma_r ** 3

def kurtosis(r):
    """
    Alternate to scipy.stats.kurtosis()
    Computes the kurtosis of the supplied Series or DataFrame
    Returns a float or a series
    """
    demeaned_r = r - r.mean()
    sigma_r = r.std(ddof=0)
    exp = (demeaned_r ** 4).mean()
    return exp / sigma_r ** 4

def is_normal(r, level=0.01):
    """
    Applies the Jarque-Bera test to determine if a Series is normal or not.
    Test is applied at 1% level by default
    Returns True if the hypothesis of normality is accepted, else False.
    """
    statistic, pvalue = scipy.stats.jarque_bera(r)
    return pvalue > level

def var_historic(r, levels=5):
    """
    Returns the historic value at risk at a specified level
    i.e returns the level such that "level" percent of returns
    fall below that number, and the (100-level) percent are above
    """
    if isinstance(r, pd.DataFrame):
        return r.aggregate(var_historic, levels=levels)
    elif isinstance(r, pd.Series):
        return -np.percentile(r, levels, axis=0)
    else:
        raise TypeError("Series or DataFrame expected")

def var_gaussian(r, levels=5, modified=False):
    """
    Returns the Parametric Gaussian VaR of a Series or DataFrame
    If modified is  True, then the modified VaR is returned
    using Cornish-Fisher modifications
    """
    # compute the Z score assuming the distribution is Gaussian
    z = norm.ppf(levels / 100)

    if modified:
        # calculate the Z score based on kurtosis and skewness
        s = skewness(r)
        k = kurtosis(r)
        z = (z +
             (z ** 2 + 1) * s / 6 +
             (z ** 3 - 3 * z) * (k - 3) / 24 +
             (2 * z ** 3 - 5 * z) * (s ** 2) / 36
             )

    return -(r.mean() + z * r.std(ddof=0))

def cvar_historic(r, levels=5):
    """
    Computes the conditional VaR or CVaR of the Series or DataFrame
    """
    if isinstance(r, pd.Series):
        is_beyond = r <= -var_historic(r, levels=levels)
        return -r[is_beyond].mean()
    elif isinstance(r, pd.DataFrame):
        return r.aggregate(cvar_historic, levels=levels)
    else:
        raise TypeError("Expected the input to be DataFrame or Series")


# Reference: https://stackabuse.com/gradient-descent-in-python-implementation-and-theory/

import numpy as np
import plotly.graph_objects as go

# User defined modules
from test_functions import available_functions

class GradientDescent:
    def __init__(self, objective_function, initial_point, precision=1e-6, max_iter=10000, rate=0.1, momentum=0.1):
        self.objective_fn = objective_function
        self.initial_point = np.array(initial_point)
        self.precision = precision
        self.max_iter = max_iter
        self.learning_rate = rate
        self.momentum = momentum

        self.step_size = 1
        self.iters = 0
        self.points_history = np.array(initial_point)
        self.fun_history = np.array(objective_function.calculate(initial_point))
        self.name = "Gradient Descent"

    def optimize(self):
        current_point = self.initial_point
        delta_pts = np.zeros(current_point.shape)  # change in points
        print("Starting point = ", np.round(self.initial_point, 2))
        while self.step_size > self.precision and self.iters < self.max_iter:
            # print("Iteration: ", self.iters)
            delta_pts = -self.learning_rate * self.objective_fn.gradient(current_point) + self.momentum * delta_pts
            current_point = current_point + delta_pts
            self.update_history(current_point)

            self.step_size = np.absolute(self.fun_history[-1] - self.fun_history[-2])
            self.iters += 1
        return {"minimum": self.fun_history[-1], "position": current_point, "path": self.optimization_path()}

    def update_history(self, new_point):
        self.points_history = np.vstack((self.points_history, new_point))
        self.fun_history = np.vstack((self.fun_history, self.objective_fn.calculate(new_point)))

    def print_output(self):
        minimum = np.round(self.fun_history[-1], 3)
        position = np.round(self.points_history[-1], 3)
        print(f'Predicted minimum = {minimum} at {position}')

    def optimization_path(self):
        return go.Scatter(x=self.points_history[:, 0], y=self.points_history[:, 1], mode="lines+markers")

def available_optimizers():
    return {"Gradient Descent": GradientDescent}

def optimize_function(point, fn_name: str, alg_name: str):
    test_fns = available_functions()
    selected_fn = test_fns[fn_name]
    optimizers = available_optimizers()
    selected_alg = optimizers[alg_name]
    optimizer_obj = selected_alg(selected_fn(), point)
    output = optimizer_obj.optimize()
    optimizer_obj.print_output()
    minimum = np.round(output["minimum"], 3)
    position = np.round(output["position"], 3)
    return minimum[0], (position[0], position[1]), output["path"]

if __name__ == "__main__":
    funs = available_functions()
    opt_algorithms = available_optimizers()
    for alg_name, algs in opt_algorithms.items():
        print(alg_name)
        for fn_name, test_fn in funs.items():
            print(fn_name)
            fun_object = test_fn()
            opt_obj = algs(fun_object, np.random.uniform(fun_object.lower_limit, fun_object.upper_limit, 2))
            _ = opt_obj.optimize()
            opt_obj.print_output()


import numpy as np

def probability_of_sum(total:int, dice1, dice2):

    """
    Brief:
    Basic probability - Dice cast
    Suppose a pair of fair 6-sided dice are thrown.
    What is the probability that the sum of the rolls is 6? (Answer as a simple fraction of integers)
    reference: https://statweb.stanford.edu/~susan/courses/s60/split/node65.html
    """

    n = dice1.shape[0]
    m = dice2.shape[0]

    comb = n * m
    count = 0

    for i in dice1:
        for j in dice2:
            sum = int(i + j)
            if sum == total:
                count += 1

    prob = count / comb

    return print("{:.2%}".format(prob))

# define the dice as a linear array of 1 to 6, all integers
dice1 = np.linspace(1,6,6,dtype=int)

# call the function above with the total for which we would like to calculate the probability with 2 dices
prob = probability_of_sum(6, dice1, dice1)


import numpy as np

# Relu
def relu(y) -> np.ndarray:
    return np.maximum(y, 0)

def relu_prime(y) -> np.ndarray:
    return y > 0

# Leaky relu
def leaky_relu(y) -> np.ndarray:
    return np.where(y > 0, y, y * 0.01)

def leaky_relu_prime(y) -> np.ndarray:
    return (y >= 0) + (y < 0)*0.01

# Linear
def linear(y) -> np.ndarray:
    return y

def linear_prime(y) -> np.ndarray:
    return 1

# Heavyside
def heaviside(y) -> np.ndarray:
    return 1 * (y > 0)

def heaviside_prime(y) -> np.ndarray:
    return 0

# Sigmoid
def sigmoid(y) -> np.ndarray:
    return 1 / (1 + np.exp(-y))

def sigmoid_prime(y) -> np.ndarray:
    return sigmoid(y) * (1 - sigmoid(y))

# Tanh
def tanh(y) -> np.ndarray:
    return np.tanh(y)

def tanh_prime(y) -> np.ndarray:
    return 1 - tanh(y)**2

# Arctan
def arctan(y) -> np.ndarray:
    return np.arctan(y)

def arctan_prime(y) -> np.ndarray:
    return 1 / (y**2 + 1)

# Softmax
def softmax(y) -> np.ndarray:
    e = np.exp(y - np.max(y))
    return e / e.sum(axis=0)

# TODO implement a solution to derive softmax : the result has not the same shape as the input
def softmax_prime(y) -> np.ndarray:
    s = y.reshape(-1, 1)
    return np.mean(np.diagflat(s) - np.dot(s, s.T))

def listToActivations(activations_list, architecture) -> list:
    activations_fn = []
    activations_prime = []
    for id, _ in enumerate(architecture):
        if id < len(activations_list):

            if activations_list[id] == 'relu':
                activations_fn.append(relu)
                activations_prime.append(relu_prime)

            elif activations_list[id] == 'leaky_relu':
                activations_fn.append(leaky_relu)
                activations_prime.append(leaky_relu_prime)

            elif activations_list[id] == 'linear':
                activations_fn.append(linear)
                activations_prime.append(linear_prime)

            elif activations_list[id] == 'heaviside':
                activations_fn.append(heaviside)
                activations_prime.append(heaviside_prime)

            elif activations_list[id] == 'sigmoid':
                activations_fn.append(sigmoid)
                activations_prime.append(sigmoid_prime)

            elif activations_list[id] == 'tanh':
                activations_fn.append(tanh)
                activations_prime.append(tanh_prime)

            elif activations_list[id] == 'arctan':
                activations_fn.append(arctan)
                activations_prime.append(arctan_prime)

            elif activations_list[id] == 'softmax':
                print(
                    'Error : transfert function `', activations_list[id], '` is not fully implemented.', sep='')
                exit(1)
                activations_fn.append(softmax)
                activations_prime.append(softmax_prime)

            else:
                print(
                    'Error : transfert function `', activations_list[id], '` does not exist.', sep='')
                exit(1)

        # If not defined, fallback function is relu
        else:
            activations_fn.append(relu)
            activations_prime.append(relu_prime)

    return activations_fn, activations_prime


import numpy as np
import scipy
import sympy
from numpy import linalg as lg
from numpy.linalg import solve
from numpy.linalg import eig
from scipy.integrate import quad

# Question 1
'''
A. Determinant = -21

B. Determinant = -21

'''

m1 = np.array([[3, 0, 3], [2, 3, 3], [0, 4, -1]])
print(m1)

det1 = np.linalg.det(m1)
print(det1) # correct

# Question 2
# Det = -159

# Question 3
'''
A.
Replace row 3 with k times row 3.

B.
The determinant is multiplied by k.
'''

# Question 4
m2 = np.array([[0, 1, 0], [1, 0, 0], [0, 0, 1]])
det2 = np.linalg.det(m2)
print(det2) # correct

# Question 5
'''
A.
False, because the determinant of A can be computed by cofactor expansion across any row or down any column. Since the determinant of A is well defined, both of these cofactor expansions will be equal.

B.

False, because the determinant of a triangular matrix is the product of the entries along the main diagonal.
'''

# Question 6
'''
If two rows of A are interchanged to produce B, then det Upper B equals negative det A.
'''

# Question 7
'''
If a multiple of one row of A is added to another row to produce matrix B, then det Upper B equals det Upper A.
'''

# Question 8
m3 = sympy.Matrix([[1, 5, -6], [-1, -4, -5], [1, 4, 7]])
print(m3)
rref1 = m3.rref()
print(rref1)

m4 = np.array([[1, 5, -6], [-1, -4, -5], [1, 4, 7]])
det3 = np.linalg.det(m4)
print(det3) # correct, det = 2

# Question 9
# Switch the rows, det of original matrix = -10, det of changed matrix = 10

# Question 10
m5 = np.array([[-25, -4, -2], [-5, 12, -4], [0, -20, 6]])
det4 = np.linalg.det(m5)
print(det4)
# The matrix is invertible because the determinant of the matrix is not zero.

# Question 11
# formula

# Question 12
mat = np.array([[1,1,0], [3, 0, 5], [0, 1, -5]])
print(mat)
det8 = np.linalg.det(mat)
print(det8)

#Cramer's Rule
# Find A1b by replacing the first column with column b
mat2 = np.array([[2,1,0], [0, 0, 5], [3, 1, -5]])
print(mat2)
det9 = np.linalg.det(mat2)
print(det9)
print(det9/det8)

#Find A2b by replacing the second column with b
mat3 = np.array([[1, 2, 0], [3, 0, 5], [0, 3, -5]])
print(mat3)
det10 = np.linalg.det(mat3)
print(det10)
print(det10/det8)

#Find A3b by replacing the third column with b
mat4 = np.array([[1, 1, 2], [3, 0, 0], [0, 1, 3]])
print(mat4)
det11 = np.linalg.det(mat4)
print(det11)
print(det11/det8)

# Answers above are correct, but try again because I misread the print output

matr = np.array([[1,1,0], [5, 0, 4], [0, 1, -4]])
print(matr)
deter = np.linalg.det(matr)
print(deter)

# Find A1b by replacing first column with b
matr1 = np.array([[5, 1, 0], [0, 0, 4], [6, 1, -4]])
print(matr1)
deter1 = np.linalg.det(matr1)
print(deter1/deter)

# Find A2b by replacing second column with b
matr2 = np.array([[1, 5, 0], [5, 0, 4], [0, 6, -4]])
print(matr2)
deter2 = np.linalg.det(matr2)
print(deter2/deter)

# Find A3b by replacing third column with b
matr3 = np.array([[1, 1, 5], [5, 0, 0], [0, 1, 6]])
print(matr3)
deter3 = np.linalg.det(matr3)
print(deter3/deter)

# Question 13
# Compute the adjugate of the given matrix
matri = np.matrix([[2, 5, 4], [1, 0, 1], [3, 2, 2]])
print(matri)
# Hermitian transpose (not correct)
print(matri.getH())
# Det of matrix
determ = np.linalg.det(matri)
print(determ)

adj_matr = np.array([[-2, -2, 5], [1, -8, 2], [2, 11, -5]])
print(adj_matr * 1/determ) # Correct

# Question 14
m6 = np.array([[3, 7], [6, 2]])
print(m6)
det5 = np.linalg.det(m6)
print(det5) # correct
# The area of the parellelogram is the absolute value of the det. In this case = 36

# Question 15
# First find the area of the parellelogram
m7 = np.array([[-5, -5], [5, 10]])
det6 = np.linalg.det(m7)
print(det6) # -25

# next find the det of matrix A
m8 = np.array([[7, -8], [-2, 8]])
print(m8)
det7 = np.linalg.det(m8)
print(det7) # 40

# Finally, multiply the absolute value of the det of the first matrix (area of the parellelogram) by the det of the second matrix
# Answer = 1000

import numpy,from numpy,import scipy,from scipy,import sympy


# --------------
# Code starts here

import numpy as np

# Code starts here

# Adjacency matrix
adj_mat = np.array([[0,0,0,0,0,0,1/3,0],
                   [1/2,0,1/2,1/3,0,0,0,0],
                   [1/2,0,0,0,0,0,0,0],
                   [0,1,0,0,0,0,0,0],
                  [0,0,1/2,1/3,0,0,1/3,0],
                   [0,0,0,1/3,1/3,0,0,1/2],
                   [0,0,0,0,1/3,0,0,1/2],
                   [0,0,0,0,1/3,1,1/3,0]])

# Compute eigenvalues and eigencevectrs

eigenvalues , eigencevectors = np.linalg.eig(adj_mat)

print(eigenvalues)

print(eigencevectors)

# Eigen vector corresponding to 1

eigen_1 = abs(eigencevectors[:,0])

print(eigen_1)

eigen_1 = abs(eigencevectors[:,0])/np.linalg.norm(eigencevectors[:,0],1)

print(eigen_1)

# most important page

page = np.where(np.max(eigen_1)== eigen_1)[0][0] + 1
print(page)

# Code ends here

# --------------
# Code starts here

adj_mat
print(adj_mat)

# Initialize stationary vector I

init_I = np.array([1,0,0,0,0,0,0,0])
print(init_I)

a= np.dot(adj_mat , init_I)
print(a)

b = np.linalg.norm(init_I , 1)
print(b)

for i in range(10):
        init_I = np.dot(adj_mat , init_I)
        init_I /= np.linalg.norm(init_I , 1)

print(init_I)

# Perform iterations for power method

power_page = np.where(np.max(init_I)==init_I)[0][0] +1
print(power_page)

# Code ends here

# --------------
# Code starts here

# New Adjancency matrix
# New Adjancency matrix
new_adj_mat = np.array([[0,0,0,0,0,0,0,0],
                   [1/2,0,1/2,1/3,0,0,0,0],
                  [1/2,0,0,0,0,0,0,0],
                   [0,1,0,0,0,0,0,0],
                   [0,0,1/2,1/3,0,0,1/2,0],
                   [0,0,0,1/3,1/3,0,0,1/2],
                   [0,0,0,0,1/3,0,0,1/2],
                   [0,0,0,0,1/3,1,1/2,0]])

# Initialize stationary vector I

new_init_I = np.array([1,0,0,0,0,0,0,0])

print(new_init_I)

# Perform iterations for power method
for _ in range(10):
    new_init_I = np.dot(new_adj_mat, new_init_I)
    new_init_I /= np.linalg.norm(new_init_I, 1)

print(new_init_I)

power_page = np.where(np.max(new_init_I) == new_init_I)[0][0] + 1
print(power_page)

print(new_init_I)

# Perform iterations for power method

# Code ends here

# --------------
# Alpha value
alpha = 0.85

new_adj_mat
print(new_adj_mat)

len(new_adj_mat)

np.ones(new_adj_mat.shape)

G = (alpha*new_adj_mat) + (1-alpha)*(1 / len(new_adj_mat))*np.ones(new_adj_mat.shape)

print(G)

final_init_I = np.array([1 , 0 , 0,0,0,0,0,0])
print(final_init_I)

for _ in range(1000):
    final_init_I = np.dot(G, final_init_I)
    final_init_I /= np.linalg.norm(final_init_I, 1)

print(final_init_I)

power_page = np.where(np.max(final_init_I) == final_init_I)[0][0] + 1
print(power_page)

print(final_init_I)

# Code starts here

# Modified adjancency matrix

# Initialize stationary vector I

# Perform iterations for power method

# Code ends here


# https://docs.sympy.org/latest/tutorial/solvers.html

from sympy import *
x, y, z = symbols('x y z')
init_printing(use_unicode=False)
from myprint import *
import myprint

spprint(Eq(x, y))

spprint(solveset(Eq(x**2, 1), x))
spprint(solveset(Eq(x**2 - 1, 0), x))
spprint(solveset(x**2 - 1, x))

spprint(solveset(x**2 - x, x))
spprint(solveset(x - x, x, domain=S.Reals))
spprint(solveset(sin(x) - 1, x, domain=S.Reals))

spprint(solveset(exp(x), x))     # No solution exists
spprint(solveset(cos(x) - x, x))  # Not able to find solution

spprint(linsolve([x + y + z - 1, x + y + 2*z - 3 ], (x, y, z)))
spprint(linsolve(Matrix(([1, 1, 1, 1], [1, 1, 2, 3])), (x, y, z)))

M = Matrix(((1, 1, 1, 1), (1, 1, 2, 3)))
system = A, b = M[:, :-1], M[:, -1]
spprint(linsolve(system, x, y, z))

a, b, c, d = symbols('a, b, c, d', real=True)
spprint(nonlinsolve([a**2 + a, a - b], [a, b]))
spprint(nonlinsolve([x*y - 1, x - 2], x, y))
spprint(nonlinsolve([x**2 + 1, y**2 + 1], [x, y]))

from sympy import sqrt
system = [x**2 - 2*y**2 -2, x*y - 2]
vars = [x, y]
spprint(nonlinsolve(system, vars))

system = [exp(x) - sin(y), 1/y - 3]
spprint(nonlinsolve(system, vars))

spprint(nonlinsolve([x*y, x*y - x], [x, y]))

system = [a**2 + a*c, a - b]
spprint(nonlinsolve(system, [a, b]))

spprint(solve([x**2 - y**2/exp(x)], [x, y], dict=True))

spprint(solve([sin(x + y), cos(x - y)], [x, y]))

spprint(solveset(x**3 - 6*x**2 + 9*x, x))

spprint(roots(x**3 - 6*x**2 + 9*x, x))

spprint(solve(x*exp(x) - 1, x ))

# diff eq

f, g = symbols('f g', cls=Function)

spprint(f(x))

spprint(f(x).diff(x))

diffeq = Eq(f(x).diff(x, x) - 2*f(x).diff(x) + f(x), sin(x))

spprint(diffeq)

spprint(dsolve(diffeq, f(x)))

spprint(dsolve(f(x).diff(x)*(1 - sin(f(x))) - 1, f(x)))


# numerical differentiation

import numpy as np

def numerical_diff(f, x):
    """
    Returns the derivative of f at x.
    """
    h = 1e-4  # rule of thumb
    return (f(x + h) - f(x - h)) / (2 * h)  # central difference

def naive_numerical_diff1(f, x):
    """
    Simple implementation by mathematical definition.
    """
    h = 1e-50  # '1e-4 for h is too large. It should be inifinitesimal.'
    return (f(x + h) - f(x - h)) / (2 * h)  # central difference

def naive_numerical_diff2(f, x):
    """
    Simple implementation by mathematical definition.
    """
    h = 1e-4  # rule of thumb
    return (f(x + h) - f(x)) / h  # 'Central diff is tricky.'

def show(f, x, diff, diff_label, expected):
    """
    f: function to be differentiated
    x: calculate the derivative at this point
    diff: function to get the derivative
    diff_label: name of `diff`
    expected: mathematically expected value for f'(x)
    """

    actual = diff(f, x)
    epsilon = 1e-5
    message = ''
    if np.linalg.norm(actual - expected) < epsilon:
        message = 'correct'
    else:
        message = 'wrong'

    print('[{0}] diff at {1} = {2} =====> {3}'.format(
        diff_label, x, actual, message))

if __name__ == '__main__':
    print('f(x) = x')
    show(lambda x: x, 0, numerical_diff, 'numerical_diff', 1)
    show(lambda x: x, 0, naive_numerical_diff1, 'naive_numerical_diff1', 1)
    show(lambda x: x, 0, naive_numerical_diff2, 'naive_numerical_diff2', 1)
    print()
    print('f(x) = x ** 2')
    show(lambda x: x ** 2, 2, numerical_diff, 'numerical_diff', 4)
    show(lambda x: x ** 2, 2, naive_numerical_diff1, 'naive_numerical_diff1', 4)
    show(lambda x: x ** 2, 2, naive_numerical_diff2, 'naive_numerical_diff2', 4)
    print()
    print('f(x) = x ** 3')
    show(lambda x: x ** 3, 5, numerical_diff, 'numerical_diff', 75)
    show(lambda x: x ** 3, 5, naive_numerical_diff1, 'naive_numerical_diff1', 75)
    show(lambda x: x ** 3, 5, naive_numerical_diff2, 'naive_numerical_diff2', 75)


# compare various approximations of derivatives

import math
import numpy
import pylab

def func(x):
    """ the function to be plotted """

    f = numpy.sin(x)
    return f

def fprime(x):
    """ the analytic derivative of func(x) """

    fp = numpy.cos(x)
    return fp

def d1l(dx, fc, i):
    """ first-order, left-sided derivative at index i """
    D = (fc[i] - fc[i-1])/dx
    return D

def d1r(dx, fc, i):
    """ first-order, right-sided derivative at index i """
    D = (fc[i+1] - fc[i])/dx
    return D

def d2(dx, fc, i):
    """ second-order centered derivative at index i """
    D = (fc[i+1] - fc[i-1])/(2.0*dx)
    return D

def d4(dx, fc, i):
    """ fourth-order centered derivative at index i """
    D = -fc[i+2] + 8.0*fc[i+1] - 8.0*fc[i-1] + fc[i-2]
    D = D/(12.0*dx)
    return D

def line(x, slope, x0, y0):
    return y0 + slope*(x - x0)

xl = 0.0
xr = math.pi

# fine grid (to show exact function)
fine = numpy.linspace(xl, xr, 500)

# coarse grid (for differencing)
coarse = numpy.linspace(xl, xr, 10)

dx = coarse[1] - coarse[0]

# plot the fine gridded analytic function
f = func(fine)
pylab.plot(fine, f, color="0.5", lw=2)

# plot the discrete data
c = func(coarse)
pylab.scatter(coarse, c)

# get the derivative approximations
Dl = d1l(dx, c, 3)
Dr = d1r(dx, c, 3)
D2 = d2(dx, c, 3)
D4 = d4(dx, c, 3)

analytic = fprime(coarse[3])

# function and point values
x0 = coarse[3]
y0 = func(x0)

xplot = numpy.linspace(coarse[1], coarse[5], 50)

pylab.plot(xplot, line(xplot, Dl, x0, y0), color="r", label="left-sided first-order approx")
pylab.plot(xplot, line(xplot, Dr, x0, y0), color="b", label="right-sided first-order approx")
pylab.plot(xplot, line(xplot, D2, x0, y0), color="g", label="centered second-order approx")
pylab.plot(xplot, line(xplot, D4, x0, y0), color="k", label="centered fourth-order approx")
#pylab.plot(xplot, line(xplot, analytic, x0, y0), color="0.5", ls=":", label="analytic")

print "analytic:          ", analytic
print "left-sided O(dx):  ", Dl
print "right-sided O(dx): ", Dr
print "centered O(dx**2): ", D2
print "centered O(dx**4): ", D4

leg = pylab.legend(loc=2,labelspacing=0.1)
ltext = leg.get_texts()
pylab.setp(ltext, fontsize='small')
leg.draw_frame(0)

# axes run through 0
# http://matplotlib.org/examples/pylab_examples/spine_placement_demo.html
ax = pylab.gca()
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.spines['left'].set_smart_bounds(True)
ax.spines['bottom'].set_smart_bounds(True)
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')

pylab.xlim(0,2.0)

pylab.savefig("fprime.png")


from numpy import pi
import numpy as np

array1 = np.array([2, 3, 4])

print(array1, array1.shape, array1.dtype)

# The type of the array can also be explicitly specified at creation time

complexTypeArray = np.array([[1, 2], [3, 4]], dtype=complex)
print(complexTypeArray)

# The function `zeros` creates an array full of zeros,
# the function `ones` creates an array full of ones,
# and the function `empty` creates an array whose initial content is random
# and depends on the state of the memory. By default, the `dtype` of the created array is `float64`.
arrayWithZeros = np.zeros((3, 4))
print('Array created with zeros funtion', arrayWithZeros)

arrayWithOnes = np.ones((2, 3, 4), dtype=np.int16)
print('Array created with ones function', arrayWithOnes)

emptyArray = np.empty((2, 3))
print('Array created with empty function', emptyArray)

# To create sequences of numbers, NumPy provides a function analogous to range that returns arrays instead of lists.

# Array from 10 to 30 with a range 5
arrayRange = np.arange(10, 30, 5)
print('Array with range', arrayRange)
# it accepts float arguments
arrayRange1 = np.arange(0, 2, 0.3)
print('Array with range floating point numbers', arrayRange1)

# When `arange` is used with floating point arguments, it is generally not possible
# to predict the number of elements obtained, due to the `finite floating point precision`.
# For this reason, it is usually better to use the function `linspace` that receives as an argument the `number of elements` that we want,
# instead of the step

# 9 numbers from 0 to 2
arrayRangeWithLineSpace = np.linspace(0, 2, 9)
print('Arrange with line space for floating numbers', arrayRangeWithLineSpace)

# useful to evaluate function at lots of points
x = np.linspace(0, 2*pi, 5)
f = np.sin(x)

# print(x)

# If an array is too large to be printed, NumPy automatically
# skips the central part of the array and only prints the corners:
# print(np.arange(10000).reshape(100, 100))

# To `disable` this behaviour and force NumPy to print the entire array, you can change the printing options using set_printoptions.
# np.set_printoptions(threshold=10)

# Basic Operations

# Arithmetic operators on arrays apply elementwise. A new array is created and filled with the result
a1 = np.array([20, 30, 40, 50])
b1 = np.arange(4)
arraySub = a1-b1

print('Actual array a1', a1)
print('Actual array b1', b1)

print('Subtract an array', arraySub)

arrayMultiplied = b1**2
print('Multiplied array', arrayMultiplied)

arraySinOperation = 10*np.sin(a1)
print(arraySinOperation)

arrayWithBoolean = a1 < 35
print(arrayWithBoolean)

# Unlike in many matrix languages, the product operator `*` operates `elementwise` in NumPy arrays.
# The matrix product can be performed using the `@` operator (in python >=3.5) or the `dot` function or method
A = np.array([[1, 1], [0, 1]])
B = np.array([[2, 0], [3, 4]])

print(A)
print(B)
# elementwise product
print('* operator performs elementwise product', A*B)
# matrix product
print('Matrix multiplication using @', A@B)
# another matrix product
print('Matrix multiplication using dot function', A.dot(B))

# Some operations, such as `+=` and `*=`, act in place to `modify an existing array`
# rather than create a new one.
a2 = np.ones((2, 3), dtype=int)
b2 = np.random.random((2, 3))
a2 *= 3
print('Mutated array using *=', a2)

b2 += a2
print('Mutated array using +=', b2)

# b is not automatically converted to integer type
# a += b

# Following error is throws
# Traceback (most recent call last):
#   ...
# TypeError: Cannot cast ufunc add output from
# dtype('float64') to dtype('int64') with casting rule 'same_kind'

# When operating with arrays of different types,
# the type of the resulting array corresponds to the more general or precise one
# (a behavior known as upcasting).

# Many unary operations, such as computing the
#  sum of all the elements in the array, are implemented as methods of the ndarray class.
randomArray = np.random.random((2, 3))
print('\nSum of the array:\n', randomArray.sum())

print('\nMinimum element in a array:\n', randomArray.min())

print('\nMaximum element in a array:\n', randomArray.max())

# By default, these operations apply to the array as though it were a list of numbers,
# regardless of its shape. However,
# by specifying the `axis` parameter you can apply an operation along the specified axis of an array

b3 = np.arange(12).reshape(3, 4)

print('\n Array used to apply sum, min, cumsum:\n', b3)

# axis-0 means column, axis-1 means - row
# sum of each column
print('\nSum of elements in axis-0:\n', b3.sum(axis=0))

# min of each row
print('\nMimimum of each row:\n', b3.min(axis=1))

# cumulative sum along each row
print('\nCumilative sum along each row:\n',  b3.cumsum(axis=0))

# Indexing, Slicing and Iterating
# One-dimensional arrays can be indexed, sliced and iterated over, much like lists and other Python sequences.
a3 = np.arange(10)**3
print('\nArray a3:\n', a3)

print('\nAccessing Array with index:\n', a3[2])
print('\nArray slicinng:\n', a3[2:5])
# equivalent to a[0:6:2] = -1000; from start to position 6, exclusive, set every 2nd element to -1000
a3[:6:2] = -1000

print('\nAfter manipulating a3:\n', a3)

print('\nReversed a3:\n', a3[::-1])

# Multidimensional arrays can have 'one index per axis'. These indices are given in a tuple separated by commas

def funtion1(x, y):
    return 10*x+y

b4 = np.fromfunction(funtion1, (5, 4), dtype=int)
print('\nArray generated using fromfuntion:\n', b4)

print('\nAccessing multidimensional array with index:\n', b4[2, 3])

# each row in the second column
print('\nEach row in the second column', b4[0:5, 1])
# b[ : ,1] # equivalent to the previous example

# each column in the second and third row
print('\nEach column in the second and third row:\n', b4[1:3, :])

# a 3D array (two stacked 2D arrays)
c1 = np.array([[[0,  1,  2],
                [10, 12, 13]],
               [[100, 101, 102],
                [110, 112, 113]]])

print('\nShape of a 3D array:\n', c1.shape)

# The dots (...) represent as many colons as needed to produce a complete indexing tuple
# same as c[1,:,:] or c[1]), first row
print('\nSlicing 3D array using (...):\n', c1[1, ...])

# same as c[:,:,2]), second column
print('\nSlicing 3D array using (...):\n', c1[..., 2])

# Iterating over multidimensional arrays is done with respect to the first axis
print('\nIterating over multidimensional arrays is done with respect to the first axis:\n')
for row in b4:
    print(row)

# However, if one wants to perform an 'operation on each element' in the array,
# one can use the 'flat' attribute which is an iterator over all the elements of the array
# print('\nPrinting the array elements using flat attribute:\n')
# for element in b4.flat:
    # print(element)

# Shape manipulation
a4 = np.floor(10*np.random.random((3, 4)))
print('\nArray before manipulation:\n', a4)

# Note that the following three commands all return a modified array, but do not change the original array

# returns the array, flattened
print('\nFlattened array using ravel():\n', a4.ravel())

print('\nArray with modified shape:\n', a4.reshape(6, 2))

print('\nTransposed array:\n', a4.T)

# `ndarray.resize()` method modifies the array itself, If a dimension is given as `-1` in a reshaping operation,
# the other dimensions are automatically calculated

# Stacking together different arrays
a5 = np.floor(10*np.random.random((2, 2)))

print('\nArray-1 to stack:\n', a5)

b5 = np.floor(10*np.random.random((2, 2)))
print('\nArray-2 to stack:\n', b5)

print('\nVertical stack array-1 over array-2:\n', np.vstack((a5, b5)))

print('\nHorizontal stack array-1 before array-2:\n', np.hstack((a5, b5)))


# identity
# The identity tool returns an identity array. An identity array is a square matrix with all the main diagonal elements as 1
# and the rest as 0. The default type of elements is float.

# import numpy
# print numpy.identity(3) #3 is for  dimension 3 X 3

# #Output
# [[ 1.  0.  0.]
#  [ 0.  1.  0.]
#  [ 0.  0.  1.]]

# eye
# The eye tool returns a 2-D array with 1's as the diagonal and 0's elsewhere. The diagonal can be main, upper or lower depending on the
# optional parameter k. A positive k is for the upper diagonal, a negative k is for the lower, and a 0 k(default) is for the main diagonal.

# import numpy
# print numpy.eye(8, 7, k = 1)    # 8 X 7 Dimensional array with first upper diagonal 1.

# #Output
# [[ 0.  1.  0.  0.  0.  0.  0.]
#  [ 0.  0.  1.  0.  0.  0.  0.]
#  [ 0.  0.  0.  1.  0.  0.  0.]
#  [ 0.  0.  0.  0.  1.  0.  0.]
#  [ 0.  0.  0.  0.  0.  1.  0.]
#  [ 0.  0.  0.  0.  0.  0.  1.]
#  [ 0.  0.  0.  0.  0.  0.  0.]
#  [ 0.  0.  0.  0.  0.  0.  0.]]

# print numpy.eye(8, 7, k = -2)   # 8 X 7 Dimensional array with second lower diagonal 1.

# Task
# Your task is to print an array of size N X M with its main diagonal elements as 1's and 0's everywhere else.

# Input Format
# A single line containing the space separated values of N and M.
# N denotes the rows.
# M denotes the columns.

# Output Format
# Print the desired N X M array.

# Sample Input
# 3 3

# Sample Output

# [[ 1.  0.  0.]
#  [ 0.  1.  0.]
#  [ 0.  0.  1.]]

import numpy as np

N, M = tuple(map(int, input().split()))
print(str(np.eye(N, M)).replace("0", " 0").replace("1", " 1"))


#!/usr/bin/python

'''
Learning Machines
Taught by Patrick Hebron at NYU ITP

Activation function implementations.
'''

import numpy as np

def sigmoid(x):
	'''sigmoid function'''
	return 1.0 / ( 1.0 + np.exp( -x ) )

def dsigmoid(x):
	'''sigmoid derivative function'''
	y = sigmoid( x )
	return y * ( 1.0 - y )

def tanh(x):
	'''tanh function'''
	return np.sinh( x ) / np.cosh( x )

def dtanh(x):
	'''tanh derivative function'''
	return 1.0 - np.power( tanh( x ), 2.0 )


# -*- coding: utf-8 -*-
"""
Routines for numerical integration

@author: Nicolas Guarin-Zapata
"""
from __future__ import division, print_function
import numpy as np
# Gauss points and weights
from scipy.special import roots_legendre

def trapz(fun, x0, x1, n):
    """Trapezoidal rule for integration

    Parameters
    ----------
    fun : callable
        Function to integrate.
    x0 : float
        Initial point for the integration interval.
    x1 : float
        End point for the integration interval.
    n : int
        Number of points to take in the interval.

    Returns
    -------
    inte : float
        Approximation of the integral

    """
    x = np.linspace(x0, x1, n)
    y = fun(x)
    dx = x[1] - x[0]
    inte = 0.5*dx*(y[0] + y[-1])
    for cont in range(1, n - 1):
        inte = inte + dx*y[cont]
    return inte

def simps(fun, x0, x1, n):
    """Simpson's rule for integration

    Parameters
    ----------
    fun : callable
        Function to integrate.
    x0 : float
        Initial point for the integration interval.
    x1 : float
        End point for the integration interval.
    n : int
        Number of points to take in the interval.

    Returns
    -------
    inte : float
        Approximation of the integral

    """
    # We need an odd number of points
    # in case of having an even one
    # we add one point more
    if n%2 == 0:
        n = n + 1
    x = np.linspace(x0, x1, n)
    y = fun(x)
    dx = x[1] - x[0]
    inte = 0
    for cont in range(1, n//2 + 1):
        inte = inte + dx/3 * (y[2*cont - 2] + 4*y[2*cont - 1] + y[2*cont])
    return inte

def gauss1d(fun, x0, x1, n):
    """Gauss quadrature in 1D

    Parameters
    ----------
    fun : callable
        Function to integrate.
    x0 : float
        Initial point for the integration interval.
    x1 : float
        End point for the integration interval.
    n : int
        Number of points to take in the interval.

    Returns
    -------
    inte : float
        Approximation of the integral

    """
    xi, wi = roots_legendre(n)
    inte = 0
    h = 0.5 * (x1 - x0)
    xm = 0.5 * (x0 + x1)
    for cont in range(n):
        inte = inte + h * fun(h * xi[cont] + xm) * wi[cont]
    return inte

def gauss2d(fun, x0, x1, y0, y1, nx, ny):
    """Gauss quadrature for a rectangle in 2D

    Parameters
    ----------
    fun : callable
        Function to integrate.
    x0 : float
        Initial point for the integration interval in x.
    x1 : float
        End point for the integration interval in x.
    y0 : float
        Initial point for the integration interval in y.
    y1 : float
        End point for the integration interval in y.
    nx : int
        Number of points to take in the interval in x.
    ny : int
        Number of points to take in the interval in y.

    Returns
    -------
    inte : float
        Approximation of the integral

    """
    xi, wi = roots_legendre(nx)
    yj, wj = roots_legendre(ny)
    inte = 0
    hx = 0.5 * (x1 - x0)
    hy = 0.5 * (y1 - y0)
    xm = 0.5 * (x0 + x1)
    ym = 0.5 * (y0 + y1)
    for cont_x in range(nx):
        for cont_y in range(ny):
            f = fun(hx * xi[cont_x] + xm, hy * yj[cont_y] + ym)
            inte = inte + hx* hy * f * wi[cont_x] * wj[cont_y]
    return inte


#1. Write a NumPy program to sort a given array of shape 2 along the first axis, last axis and on flattened array.
import numpy as np
a = np.array([[10,40],[30,20]])
print("Original array:")
print(a)
print("Sort the array along the first axis:")
print(np.sort(a, axis=0))
print("Sort the array along the last axis:")
print(np.sort(a))
print("Sort the flattened array:")
print(np.sort(a, axis=None))
#2. Write a NumPy program to create a structured array from given student name, height, class and their data types. Now sort the array on height.
import numpy as np
data_type = [('name', 'S15'), ('class', int), ('height', float)]
students_details = [('James', 5, 48.5), ('Nail', 6, 52.5),('Paul', 5, 42.10), ('Pit', 5, 40.11)]
# create a structured array
students = np.array(students_details, dtype=data_type)
print("Original array:")
print(students)
print("Sort by height")
print(np.sort(students, order='height'))
#3. Write a NumPy program to create a structured array from given student name, height, class and their data types. Now sort by class, then height if class are equal
import numpy as np
data_type = [('name', 'S15'), ('class', int), ('height', float)]
students_details = [('James', 5, 48.5), ('Nail', 6, 52.5),('Paul', 5, 42.10), ('Pit', 5, 40.11)]
# create a structured array
students = np.array(students_details, dtype=data_type)
print("Original array:")
print(students)
print("Sort by class, then height if class are equal:")
print(np.sort(students, order=['class', 'height']))
#4. Write a NumPy program to sort the student id with increasing height of the students from given students id and height. Print the integer indices that describes the sort order by multiple columns and the sorted data.
import numpy as np
student_id = np.array([1023, 5202, 6230, 1671, 1682, 5241, 4532])
student_height = np.array([40., 42., 45., 41., 38., 40., 42.0])
#Sort by studen_id then by student_height
indices = np.lexsort((student_id, student_height))
print("Sorted indices:")
print(indices)
print("Sorted data:")
for n in indices:
  print(student_id[n], student_height[n])
#5. Write a NumPy program to get the indices of the sorted elements of a given array.
import numpy as np
student_id = np.array([1023, 5202, 6230, 1671, 1682, 5241, 4532])
print("Original array:")
print(student_id)
i = np.argsort(student_id)
print("Indices of the sorted elements of a given array:")
print(i)
#6. Write a NumPy program to sort a given complex array using the real part first, then the imaginary part
import numpy as np
complex_num = [1 + 2j, 3 - 1j, 3 - 2j, 4 - 3j, 3 + 5j]
print("Original array:")
print(complex_num)
print("\nSorted a given complex array using the real part first, then the imaginary part.")
print(np.sort_complex(complex_num))
#7. Write a NumPy program to partition a given array in a specified position and move all the smaller elements values to the left of the partition, and the remaining values to the right, in arbitrary order (based on random choice).

import numpy as np
nums = np.array([70, 50, 20, 30, -11, 60, 50, 40])
print("Original array:")
print(nums)
print("\nAfter partitioning on 4 the position:")
print(np.partition(nums, 4))
#8. Write a NumPy program to sort the specified number of elements from beginning of a given array.
import numpy as np
nums =  np.random.rand(10)
print("Original array:")
print(nums)
print("\nSorted first 5 elements:")
print(nums[np.argpartition(nums,range(5))])

NumPy: Sorting and Searching Exercises & solutions.py


# coding: utf-8

# In[2]:

# numpy and scipy are pretty awesome python libraries for scientific computing
# Let's quickly do a round up of what these 2 libraries can help us do

# Python lists are amazing, but many times you'll want to put your data into grids
# and perform mathematical operations with them. A numpy array is a way to put your data
# in a matrix (grid).
# Numpy then has a number of cool functions to manipulate this data
import numpy as np
import scipy
# An array can have any number of dimensions
# A 1-d array is like a list (The number of dimensions is also known as rank)
array1d=np.array([0,2,4,6,8])
print(array1d)

# In[3]:

# A 2-d array is like a 2-d grid. Create it using a list of lists
array2d=np.array([[1,2,3],[4,5,6]])

print(array2d)

# In[4]:

print(type(array1d))

# In[5]:

# shape is a tuple which contains the size of the array ie (the number of rows, the number of
# columns)
print(array1d.shape)

# In[6]:

print(array2d.shape)

# In[7]:

# You can index elements of an array pretty much like you do with lists
# Like with lists, the indexing starts from 0
print(array1d[0])

# With a 2d array the syntax is slightly different. If you had a list of lists, and you
# wanted the element in the ith row and the jth column you would index it by saying
# listOfLists[i][j]
# In a numpy array, you can index the element in the ith row and jth column as
# arrayNumpy[i,j]

print(array2d[1,2])
# this will print the element in the second row and third column of array2d (indexing starts from
# 0)

# In[8]:

# Just like with lists, you can use : to specify "from the beginning" or "till the end"
print(array1d[1:])

# In[9]:

print(array2d[1,:1]) # This will print the elements in the second row , from the first element in
# the second row, till the second element (not including the second element)

# In[10]:

print(array2d[1,1:]) # The elements in the second row, from the second element onwards

# In[11]:

# In all the cases above, the result of the indexing is a numpy array which is a subset
# of the original array
subarray2d=array2d[1,1:]
print(type(subarray2d))

# In[12]:

# There are other cool ways to index numpy arrays
new2dArray=np.array([[1,2,4,5,9],[3,4,6,6,10],[15,3,2,14,7]])

# Let's say you want the elements with indices [1,2], [0,4], [2,3]

newSubArray=new2dArray[[1,0,2],[2,4,3]] # Arrays of integers to specify the indices
print(newSubArray)

# In[13]:

# You can also use boolean indexing, ie subset the array to all the values which satisfy
# a certain condition
new2dArray[new2dArray>10]

# In[14]:

# new2dArray>10 will return an array with the same size and shape as the original array
# Each element of that array will be a boolean which says whether the corresponding element
# of the original array satisfies the given condition
new2dArray>10

# In[15]:

# You can create some standard kinds of arrays using built-in functions in numpy.
# Ex: Arrays with all 0s, constant arrays, random numbers etc

arrayOfZeros=np.zeros((2,2),dtype='int64') # Creates an array with all zeros. dtype is an optional
# argument, its default value is float
arrayOfOnes=np.ones((1,2)) # note how the size of the array is passed in as a tuple
arraywithConstantValue = np.full(new2dArray.shape,7) # Creates an array with the same
# size and shape as new2dArray, fills it with value 7 for all elements

identityMatrix = np.eye(2)
# Creates a 2x2 identity matrix ie a square grid of numbers with 2 rows and 2 columns
# All the diagonal elements will be 1s and all the non-diagonal elements will be 0s

print(arrayOfZeros,"\n",arrayOfOnes,"\n",arraywithConstantValue,"\n",identityMatrix)

# In[16]:

# YOu can also fill an array with random numbers
arrayOfRandomNumbers = np.random.random((2,2))
# This will create a 2x2 array with random numbers between the values 0 and 1.
# To generate random numbers with a specific distribution, check out the
# numpy documentation
print(arrayOfRandomNumbers)

# In[17]:

# You can change the size and shape of your array
transposeArray=np.transpose(new2dArray)

print(new2dArray.shape, transposeArray.shape)

# In[18]:

# You can reshape array into any shape as long as the total number of elements remains
# the same
reshapedArray=np.reshape(new2dArray,[1,15])
print(reshapedArray)

# In[19]:

reshapedArray=np.reshape(new2dArray,[15,1])
print(reshapedArray)

# In[20]:

# Normal mathematical operators like +,-,/,* can be used to perform element wise
# operations. Both arrays have to be of the same dimension

array1=np.array([[1,2,3],[4,5,6]])
array2=np.array([[7,8,9],[3,2,1]])

# In[21]:

array1+array2

# In[22]:

# instead of the math operators you can use functions equivalent to the operators
# +,-,/,* are equivalent to add, subtract, divide,multiply
np.subtract(array1,array2)

# In[23]:

# Broadcasting is a way in which you can add matrices or arrays of different dimensions
array3=[[1,4,7]]

array1+array3 # This will add array3 to each row of array1

# In[24]:

# The rule of thumb for broadcasting is that the arrays need to be able to align
# along at least 1 dimension. For more details check the documentation

# In[25]:

# One important operation you'll need is to perform an inner product of 2 arrays
# or multiply 2 matrices. Matrix multiplication is equivalent to taking inner products
# of every row of the first matrix with every column of the second matrix
# To multiply 2 matrices, you will need the number of columns of the left array
# to be equal to the number of rows of the right array

np.dot(array1,array2)# wont work because we have both 2x3 arrays

# In[26]:

np.dot(array1,array2.T) # .T is shorthand for using np.transpose()

# In[27]:

# You can add or multiply all the elements along 1 dimension (or axis), This is
# like compressing the array along that axis
np.sum(array1,axis=0)

# In[28]:

np.sum(array2,axis=1)

# In[29]:

# stack arrays together using vstack or hstack. These functions take in a list/tuple/
# array of arrays and then stack them vertically or horizontally to make a new array
np.vstack((array1,array2))

# In[30]:

np.hstack((array2,array1))

# In[31]:

# Scipy has many modules that help us compute mathematical functions
# one example is the spatial module, given 2 points represented by
# np arrays, scipy can help you find the distance between those points
# The distance metric could be any one of a number of options ex: euclidean
# cosine, correlation, hamming etc
# pdist will compute pairwise distances between the rows in a numpy array

from scipy.spatial.distance import correlation, cosine, pdist, squareform

array1=np.array([0,1,0])
array2=np.array([1,0,0])

correlation(array1,array2)

# In[33]:

allPoints=np.vstack([array1,array2])
d=squareform(pdist(allPoints, 'euclidean'))
# The distance metric can be changed to cosine, correlation or any other distance
# the complete list of options is available in scipy documentation
# This will compute the pairwise distance between all rows of allPoints
# d will be a square matrix with d[i,j] being the Euclidean distance between
# allPoints[i,:] and allPoints[j,:]
print(d)

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:

# In[ ]:


from __future__ import division
from scipy import*
from scipy import linalg
import pylab
import sys
sys.path.append("/home/ludger/lib/python2.6/site-packages/")
import statistics

#Task 1: Evaluate the density of a bivariate distribution at a single point
def density(x,mu,sigma):
        inv_sigma=linalg.inv(sigma)
        x_minus_mu=x-mu
        return exp(-0.5*dot(dot(transpose(x_minus_mu),inv_sigma),x_minus_mu))/(2*pi*sqrt(
linalg.det(sigma) ) )

mu=array([0,0])
sigma=array([[4,1],[1,4]])

#Task 2: Metropolis Hastings

#Set the standard deviation of the proposal
sigma_prop=2.5
#Set the desired sample size
n=1000
#Set the starting values
x=array([0,0])
accepted_n=0
f=density(x,mu,sigma)
X1=[x[0]]
X2=[x[1]]

for i in xrange(1,n):
        x_0=x[0]+random.normal(0,sigma_prop)
        x_1=x[1]+random.normal(0,sigma_prop)
        new_x=array([x_0,x_1])
        new_f=density(new_x,mu,sigma)
        if (random.random()<(new_f/f)):
                accepted_n+=1
                x=new_x
                f=new_f
        X1.append(x[0])
        X2.append(x[1])

#Proportion of accepted values
print "The proportion of accepted values is", accepted_n/n

#Task 3: Diagnostics

#Sample plots for both values
pylab.figure(0)
pylab.plot(X1,'b')
pylab.title("Sample path of X_1")
pylab.figure(1)
pylab.plot(X2,'r')
pylab.title("Sample path of X_2")

#Cumulative averages
X1_cummean = cumsum( X1 ) / ( 1 + arange( len( X1 )))
X2_cummean = cumsum( X2 ) / ( 1 + arange( len( X1 )))
pylab.figure( 2 )
pylab.plot( X1_cummean,"b" )
pylab.title("Empirical mean of X_1")

pylab.figure( 3 )
pylab.plot( X2_cummean,"r" )
pylab.title("Empirical mean of X_2")
pylab.show()

#Autocorrelation
X1_sd = sqrt( var( X1 ) )
X2_sd = sqrt( var( X2 ) )
X1_autocorr = statistics.correlation( X1[1: ], X1[:-1])
X2_autocorr = statistics.correlation( X2[1: ], X2[:-1])
print "The autocorrelation of X_1 is", X1_autocorr
print "The autocorrelation of X_2 is", X2_autocorr

#Effective sample size
X1_ess = n * ( 1 - X1_autocorr ) / ( 1 + X1_autocorr )
X2_ess = n * ( 1 - X2_autocorr ) / ( 1 + X2_autocorr )
print "The effective sample size of X_1 is", X1_ess
print "The effective sample size of X_2 is", X2_ess

#Task 4: Repeat with sigma_prop = 0.1, sigma_prop = 10

#Task 5: Repeat with sigma = array([[4,2.8],[2.8,4]])


# Generic import
import numpy as np

def central_difference(f, x, *args):
    """
    Description:
    It returns the numerical derivative of input function "f"
    at the point "x", i.e. df(x)/dx, by using the central
    difference formula.

    Args:
        - f: function handle.
        - x: point to evaluate the derivative.
        - *args: additional input parameters of the function.

    Output:
        - df(x)/dx
    """
    # The optimal step when using the CDF should scale
    # with respect to the cubic root of "eps".
    cbrt_eps = (np.finfo(float).eps) ** (1.0 / 3.0)

    # Number of input parameters.
    D = x.size

    # Preallocate for efficiency.
    df = np.zeros(D)

    # Auxilliary vector.
    e = np.zeros(D)

    # Check all 'D' directions (coordinates of x).
    for i in range(D):
        # Switch ON i-th direction.
        e[i] = 1.0

        # Step size (for the i-th variable).
        h = cbrt_eps if (x[i] == 0.0) else x[i] * cbrt_eps

        # Move a small way in the i-th direction of x+h.
        fplus = f(x + h * e, *args)

        # Move a small way in the i-th direction of x-h.
        fminus = f(x - h * e, *args)

        # Central difference formula for approximation.
        df[i] = (fplus - fminus) / (2.0 * h)

        # Switch OFF i-th direction.
        e[i] = 0.0
    # _end_if_

    return df

# _end_def_

def forward_difference(f, x, *args):
    """
    Description:
    It returns the numerical derivative of input function "f"
    at the point "x", i.e. df(x)/dx, by using the forward
    difference formula.

    Args:
        - f: function handle.
        - x: point to evaluate the derivative.
        - *args: additional input parameters of the function.

    Output:
        - df(x)/dx
    """
    # Step size.
    h = 1.0E-6

    # Number of input parameters.
    D = x.size

    # Preallocate for efficiency.
    df = np.zeros(D)

    # Auxilliary vector.
    e = np.zeros(D)

    # Compute the f(x) only once.
    fx = f(x, *args)

    # Check all 'D' directions (coordinates of x).
    for i in range(D):
        # Switch ON i-th direction.
        e[i] = 1.0

        # Move a small way in the i-th direction of x+h.
        fplus = f(x + h * e, *args)

        # Central difference formula for approximation.
        df[i] = (fplus - fx) / h

        # Switch OFF i-th direction.
        e[i] = 0.0
    # _end_if_

    return df

# _end_def_

def backward_difference(f, x, *args):
    """
    Description:
    It returns the numerical derivative of input function "f"
    at the point "x", i.e. df(x)/dx, by using the backward
    difference formula.

    Args:
        - f: function handle.
        - x: point to evaluate the derivative.
        - *args: additional input parameters of the function.

    Output:
        - df(x)/dx
    """
    # Step size.
    h = 1.0E-6

    # Number of input parameters.
    D = x.size

    # Preallocate for efficiency.
    df = np.zeros(D)

    # Auxilliary vector.
    e = np.zeros(D)

    # Compute the f(x) only once.
    fx = f(x, *args)

    # Check all 'D' directions (coordinates of x).
    for i in range(D):
        # Switch ON i-th direction.
        e[i] = 1.0

        # Move a small way in the i-th direction of x+h.
        fminus = f(x - h * e, *args)

        # Central difference formula for approximation.
        df[i] = (fx - fminus) / h

        # Switch OFF i-th direction.
        e[i] = 0.0
    # _end_if_

    return df

# _end_def_

def numerical_derivative(f, x, method='cdf', *args):
    """
    Description:
    It returns the numerical derivative of input function "f"
    at the point "x", i.e. df(x)/dx, by calling the relevant
    numerical method to compute the derivative.

    Args:
        - f: function handle.
        - x: point to evaluate the derivative.
        - method: numerical integration (default = 'cdf')
        - args: additional input parameters of the function.

    Raises:
        - ValueError: If the input method is not recognized.
    """

    # Call the right method or throw an error.
    if method == "cdf":
        return central_difference(f, x, *args)
    elif method == "fwd":
        return forward_difference(f, x, *args)
    elif method == "bwd":
        return backward_difference(f, x, *args)
    else:
        raise ValueError(" Unknown method of differentiation.")

# _end_def_

def flat_list(x=None):
    """
    Description:
    It returns a list that contains all the elements form the input list of lists.
    It should work for any number of levels.

    Example:
        >>> x = flat_list([1, 'k', [], 3, [4, 5, 6], [[7, 8]], [[[9]]]])
        >>> x
        >>> [1, 'k', 3, 4, 5, 6, 7, 8, 9]

    Args:
        - x (list): List of lists of objects.

    Raises:
        - TypeError: If the input is not a list object.
    """

    # Check for empty input.
    if x is None:
        return []
    # _end_if_

    # Input 'x' should be a list.
    if not isinstance(x, list):
        raise TypeError(" Input should be a list.")
    # _end_if_

    # Define the return list.
    flat_x = []

    # Go through all the list elements.
    for item_x in x:
        # Check for embedded lists.
        if isinstance(item_x, list):
            # Check for empty entries.
            if not item_x:
                continue
            # _end_if_

            # Note the recursive call in "flat_list".
            for ix in flat_list(item_x):
                flat_x.append(ix)
            # _end_for_
        else:
            # If the item is not a list.
            flat_x.append(item_x)
        # _end_if_
    # _end_for_

    # Return the flatten list.
    return flat_x

# _end_def_

def split_dataset_at_feature(data, axis=0):
    """
    Description:
    Splits the input data set at the requested feature, given by the index number 'idx'.

    Args:
        - data: (list of lists) input data-set.
        - axis: feature index to split the data-set.

    Note:
        Default index is '0' (i.e. the first feature of the data set).

    Raises:
        ValueError if the index is out of bounds.
    """

    # Data must be a list.
    if not isinstance(data, list):
        raise TypeError(" Input data should be a list.")
    # _end_if_

    # Index must be integer.
    if not isinstance(axis, int):
        raise TypeError(" Axis value should be integer.")
    # _end_if_

    # Get the number of features.
    # Note:  The last column is assumed to contain the class label,
    # therefore it should never be included for splitting the data.
    n_features = len(data[0]) - 1

    # If there is only one feature, there is nothing to split.
    if n_features == 1:
        return data
    # _end_if_

    # Sanity check.
    if axis < 0 or axis >= n_features:
        raise ValueError(" Feature index is out of bounds.")
    # _end_if_

    # Every entry in the dictionary will contain a sub-set of the
    # original data-set, split at a value of the feature.
    partition_data = {}

    # Split the data-set into sub-sets.
    for record in data:
        # First part of the record.
        part_1 = record[:axis]

        # Second part of the record.
        part_2 = record[axis + 1:]

        # Combine the two parts in one record.
        part_1.extend(part_2)

        # Construct a key (string) entry.
        key = "chID_{0}:{1}".format(idx, record[axis])

        # Check if this key has been seen.
        if key not in node_dict.keys():
            # If not then add it to the dictionary.
            partition_data[key] = []
        # _end_if_

        # Add the record on the dictionary.
        partition_data[key].append(part_1)
    # _end_for_

    return partition_data

# _end_def_


import numpy as np

A = np.array([[n+m*10 for n in range(5)] for m in range(5)])
np.dot(A, A)
"""
array([[ 300,  310,  320,  330,  340],
       [1300, 1360, 1420, 1480, 1540],
       [2300, 2410, 2520, 2630, 2740],
       [3300, 3460, 3620, 3780, 3940],
       [4300, 4510, 4720, 4930, 5140]])
"""

v1 = np.arange(0, 5)
np.dot(A, v1)   # array([ 30, 130, 230, 330, 430])
np.dot(v1, v1)  # 30

# También podemos hacer casting al tipo matrix. Eso cambia el comportamiento de los operadores +, -, * para usar álgebra matricial
M = np.matrix(A)
v = np.matrix(v1).T # vector columna
v
"""
matrix([[0],
        [1],
        [2],
        [3],
        [4]])
"""
M * M
"""matrix([[ 300,  310,  320,  330,  340],
        [1300, 1360, 1420, 1480, 1540],
        [2300, 2410, 2520, 2630, 2740],
        [3300, 3460, 3620, 3780, 3940],
        [4300, 4510, 4720, 4930, 5140]])
"""

M * v
"""
matrix([[ 30],
        [130],
        [230],
        [330],
        [430]])
"""

v.T * v # matrix([[30]])

v + M*v
"""
matrix([[ 30],
    [131],
    [232],
    [333],
    [434]])
"""

# Si intentamos operar entre elementos con dimensiones no compatibles, nos da error
v = np.matrix([1,2,3,4,5,6]).T
v
"""
matrix([[1],
        [2],
        [3],
        [4],
        [5],
        [6]])

"""

M * v   # Error: ValueError: shapes (5,5) and (6,1) not aligned: 5 (dim 1) != 6 (dim 0)


    #!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time     : 2018/10/16 19:48
# @Author   : Iydon
# @File     : course4.4.py

import numpy as np

# Composite Numerical Integration
def trapezoidal_rule(xs:list, fxs:list) -> float:
    """
    Trapezoidal Rule:
    /int_{x_0}^{x_1}f(x)dx = (h/2)[f(x_0)+f(x_1)] - E(f)
    E(f) = (h^3/12)f''(/xi)
    """
    return (xs[-1]-xs[0]) / 2 * sum(fxs)

def simpson_rule(xs:list, fxs:list) -> float:
    """
    Simpson's Rule:
    /int_{x_0}^{x_2}f(x)dx = (h/3)[f(x_0)+4f(x_1)+f(x_2)] - E(f)
    E(f) = (h^5/90)f^{(4)}(/xi)
    """
    return (xs[-1]-xs[0]) / 6 * (fxs[0]+4*fxs[1]+fxs[2])

def midpoint_rule(xs:list, fxs:list) -> float:
    return (xs[-1]-xs[0]) * fxs[1]

def composite_trapezoidal_rule(xs:list, fxs:list) -> float:
    result = 0
    for i in range(len(xs)-1):
        result += trapezoidal_rule(xs[i:i+2], fxs[i:i+2])
    return result

def composite_simpson_rule(xs:list, fxs:list) -> float:
    result = 0
    for i in range(len(xs)//2):
        result += simpson_rule(xs[2*i:2*i+3], fxs[2*i:2*i+3])
    return result

def composite_midpoint_rule(xs:list, fxs:list) -> float:
    result = 0
    for i in range(len(xs)//2):
        result += midpoint_rule(xs[2*i:2*i+3], fxs[2*i:2*i+3])
    return result

f  = np.exp
xs = [i/2 for i in range(9)]
print(composite_simpson_rule(xs, [f(x) for x in xs]))
print(composite_trapezoidal_rule(xs, [f(x) for x in xs]))
print(composite_midpoint_rule(xs, [f(x) for x in xs]))

pi = np.pi
f  = np.sin
xs = [i*pi/359 for i in range(360)]
print(composite_midpoint_rule(xs, [f(x) for x in xs]))

pi = np.pi
f  = np.sin
xs = [i*pi/20 for i in range(21)]
print(composite_simpson_rule(xs, [f(x) for x in xs]))
print(composite_trapezoidal_rule(xs, [f(x) for x in xs]))
print(composite_midpoint_rule(xs, [f(x) for x in xs]))

f  = lambda x: x**3*np.exp(x)
xs = [-2, -1, 0, 1, 2]
print(composite_trapezoidal_rule(xs, [f(x) for x in xs]))
xs = [i/2 for i in range(-4,5)]
print(composite_simpson_rule(xs, [f(x) for x in xs]))

f  = lambda x: np.exp(2*x)*np.sin(3*x)
xs = [2*i/2169 for i in range(2170)]
print(composite_trapezoidal_rule(xs, [f(x) for x in xs]))
xs = [2*i/54 for i in range(55)]
print(composite_simpson_rule(xs, [f(x) for x in xs]))

print(-14.213977129862521)


import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import numpy as np
import random
import torch
import torch.optim as optim
import utils

print('通过实际例子查看梯度下降的问题')
eta = 0.4

def f_2d(x1, x2):
    return 0.1 * x1 ** 2 + 2 * x2 ** 2

def gd_2d(x1, x2, s1, s2):
    return (x1 - eta * 0.2 * x1, x2 - eta * 4 * x2, 0, 0)

print('竖直方向波动较大')
utils.show_trace_2d(f_2d, utils.train_2d(gd_2d))
print('在竖直方向越过最优解并发散')
eta = 0.6
utils.show_trace_2d(f_2d, utils.train_2d(gd_2d))

print('使用动量法进行改进')
eta, gamma = 0.4, 0.5

def momentum_2d(x1, x2, v1, v2):
    v1 = gamma * v1 + eta * 0.2 * x1
    v2 = gamma * v2 + eta * 4 * x2
    return x1 - v1, x2 - v2, v1, v2

utils.show_trace_2d(f_2d, utils.train_2d(momentum_2d))
print('用更大的学习率，也不会发散')
eta = 0.6
utils.show_trace_2d(f_2d, utils.train_2d(momentum_2d))

print('自行实现动量法优化')
features, labels = utils.get_nasa_data()

def init_momentum_states():
    v_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
    v_b = torch.zeros(1, dtype=torch.float32)
    return (v_w, v_b)

def sgd_momentum(params, states, hyperparams):
    for p, v in zip(params, states):
        v.data = hyperparams['momentum'] * v.data + hyperparams['lr'] * p.grad.data
        p.data -= v.data

print('momentum = 0.02, lr = 0.02')
utils.train_opt(sgd_momentum, init_momentum_states(), {'lr': 0.02, 'momentum': 0.5}, features, labels)
print('momentum = 0.9, lr = 0.02')
utils.train_opt(sgd_momentum, init_momentum_states(), {'lr': 0.02, 'momentum': 0.9}, features, labels)
print('momentum = 0.9, lr = 0.004')
utils.train_opt(sgd_momentum, init_momentum_states(), {'lr': 0.004, 'momentum': 0.9}, features, labels)

print('框架实现')
utils.train_opt_pytorch(optim.SGD, {'lr': 0.004, 'momentum': 0.9}, features, labels)


from sympy import *
x, y, z = symbols('x y z')
init_printing(use_unicode=False)

# simplify - not specific type of simplification

pprint(simplify(sin(x)**2 + cos(x)**2))

pprint(simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1)))

pprint(simplify(gamma(x)/gamma(x - 2)))

pprint(simplify(x**2 + 2*x + 1))

# expand - expand polynomial to canonical form ax^2+bx+c etc.

pprint(expand((x + 1)**2))

pprint(expand((x + 2)*(x - 3)))

pprint(expand((x + 1)*(x - 2) - (x - 1)*x))

# factor - factors a polynomial

pprint(factor(x**3 - x**2 + x - 1))

pprint(factor(x**2*z + 4*x*y*z + 4*y**2*z))

# returns list of factors

pprint(factor_list(x**2*z + 4*x*y*z + 4*y**2*z))

# non-polynomials

pprint(expand((cos(x) + sin(x))**2))

pprint(factor(cos(x)**2 + 2*cos(x)*sin(x) + sin(x)**2))

# collect - based on a variable (i.e. x)
# gather the coefficients into a term
# multiplied by a power of x.

expr = x*y + x - 3 + 2*x**2 - z*x**2 + x**3
pprint(expr)

collected_expr = collect(expr, x)
pprint(collected_expr)

# coeff gives coefficient of the given
# power of x - x^2 in this example

pprint(collected_expr.coeff(x, 2))

# cancel - arrange as fraction?

pprint(cancel((x**2 + 2*x + 1)/(x**2 + x)))

expr = 1/x + (3*x/2 - 2)/(x - 4)
pprint(expr)

pprint(cancel(expr))

expr = (x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1)
pprint(expr)

pprint(cancel(expr))

# factors numerator in this example

pprint(factor(expr))

# apart - seems to split into multiple fractional terms
# opposite of cancel kind of?

expr = (4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x)

pprint(expr)
print(' ')

pprint(apart(expr))
print(' ')

pprint(cancel(apart(expr)))
print(' ')

# trig simp - like simplify for general
# there are specific simps later

pprint(acos(x))
print(' ')
pprint(cos(acos(x)))
print(' ')
pprint(asin(1))
print(' ')

pprint(trigsimp(sin(x)**2 + cos(x)**2))
print(' ')
pprint(trigsimp(sin(x)**4 - 2*cos(x)**2*sin(x)**2 + cos(x)**4))
print(' ')
pprint(trigsimp(sin(x)*tan(x)/sec(x)))
print(' ')

pprint(trigsimp(cosh(x)**2 + sinh(x)**2))
print(' ')
pprint(trigsimp(sinh(x)/tanh(x)))
print(' ')

# expand_trig kind of like expand

def spprint(o):
    pprint(o)
    print(' ')

spprint(expand_trig(sin(x + y)))
spprint(expand_trig(tan(2*x)))

# trigsimp opposite of expand_trig kinda

spprint(trigsimp(expand_trig(sin(x + y))))

# powers - can state things about the numbers
# real, positive, or by default complex

x, y = symbols('x y', positive=True)
a, b = symbols('a b', real=True)
z, t, c = symbols('z t c')

spprint(powsimp(x**a*x**b))

spprint(powsimp(x**a*y**a))

# z, t, c might be complex

spprint(powsimp(t**c*z**c))

# force simp

spprint(powsimp(t**c*z**c, force=True))

spprint((z*t)**2)

spprint(sqrt(x*y))

# simp doesn't work because of automatic splitting

spprint(powsimp(z**2*t**2))
spprint(powsimp(sqrt(x)*sqrt(y)))

# expanding the base or exponent

spprint(expand_power_exp(x**(a + b)))

spprint(expand_power_base((x*y)**a))

# same doesn't work with complex but can force

spprint(expand_power_base((z*t)**c))

spprint(expand_power_base((z*t)**c, force=True))

# doesn't work with number powers like above

spprint(x**2*x**3)
spprint(expand_power_exp(x**5))

# simplified nested powers
# can force when not appropriate

spprint(powdenest((x**a)**b))
spprint(powdenest((z**a)**b))
spprint(powdenest((z**a)**b, force=True))

# exponentials and logs

# log(x) = ln(x) log base e of x I think

spprint(ln(x))

x, y = symbols('x y', positive=True)
n = symbols('n', real=True)

spprint(expand_log(log(x*y)))
spprint(expand_log(log(x/y)))
spprint(expand_log(log(x**2)))
spprint(expand_log(log(x**n)))
spprint(expand_log(log(z*t)))

# force

spprint(expand_log(log(z**2)))
spprint(expand_log(log(z**2), force=True))

# logcombine kind of revers of expand_log

spprint(logcombine(log(x) + log(y)))
spprint(logcombine(n*log(x)))
spprint(logcombine(n*log(z)))
spprint(logcombine(n*log(z), force=True))

# "special" functions

x, y, z = symbols('x y z')
k, m, n = symbols('k m n')

spprint(factorial(n))

spprint(binomial(n, k))

spprint(binomial(5, 2))

spprint(gamma(z))

spprint(hyper([1, 2], [3], z))

# rewrite an expression in terms of a function

spprint(tan(x).rewrite(sin))
spprint(factorial(x).rewrite(gamma))

# get rid of hyper

spprint(hyperexpand(hyper([1, 1], [2], z)))

expr = meijerg([[1],[1]], [[1],[]], -z)
spprint(expr)
spprint(hyperexpand(expr))

# simplifiy combinatorials - binomial, etc.

n, k = symbols('n k', integer = True)
spprint(combsimp(factorial(n)/factorial(n - 3)))
spprint(combsimp(binomial(n+1, k+1)/binomial(n, k)))

# simplify gamma whatever that is. :)

spprint(gammasimp(gamma(x)*gamma(1 - x)))

# continued fraction example
# i think this is an example of using sympy and simplification

def list_to_frac(l):
    expr = Integer(0)
    for i in reversed(l[1:]):
        expr += i
        expr = 1/expr
    return l[0] + expr

spprint(list_to_frac([x, y, z]))

# result is sympy fraction

spprint(list_to_frac([1, 2, 3, 4]))

# get continued fraction

syms = symbols('a0:5')
spprint(syms)
a0, a1, a2, a3, a4 = syms
frac = list_to_frac(syms)
spprint(frac)

# put into standard form

frac = cancel(frac)
spprint(frac)

# put back into original form

l = []
frac = apart(frac, a0)
spprint(frac)
l.append(a0)
frac = 1/(frac - a0)
spprint(frac)

frac = apart(frac, a1)
spprint(frac)
l.append(a1)
frac = 1/(frac - a1)
frac = apart(frac, a2)
spprint(frac)
l.append(a2)
frac = 1/(frac - a2)
frac = apart(frac, a3)
spprint(frac)
l.append(a3)
frac = 1/(frac - a3)
frac = apart(frac, a4)
spprint(frac)
l.append(a4)
spprint(list_to_frac(l))


import numpy as np
from numpy import linalg as LA
import matplotlib.pyplot as plt

A = np.array([[1 , 2],[4 , 3]])
lamda, v = LA.eig(A)
print(f"eigenvalues\n{lamda}")
print(f"eigenvectors\n{v}")
v1 = v[:,0]
v2 = v[:,1]
print(f"v1\n{v1}")
print(f"v2\n{v2}")

plt.title('eigenvector v1 : blue , eigenvector v2 : green')
plt.plot([0,v1[0]],[0,v1[1]],'blue')
plt.plot([0,v2[0]],[0,v2[1]],'green')
plt.xlim((-1,1))
plt.ylim((-1,1))
plt.grid()
plt.show()

equation1 = np.dot(A,v1) - lamda[0]*v1 # should be 0
print(equation1)
equation2 = np.dot(A,v2) - lamda[1]*v2 # should be 0
print(equation2)