import numpy as np
import inspect

# 1) Stwórz wektor zer o rozmiarze 10

v = np.zeros((10,), dtype=int)

assert v.sum() == 0
assert v.shape == (10,)

# ---------------------------------------------------
# 2) Napisz funkcję zwracającą ilość danych jaką zajmuje tablica numpy

def memof(a):
    return a.nbytes

assert memof(np.zeros((12,10),dtype=np.int32))==480

# ---------------------------------------------------
# 3) Napisz funkcję tworzącą wektor zer o rozmiarze n z jedynką na piątym miejscu

def make_v(n):

    # Tworzenie wektora z 10 zerami
    v = np.zeros((n,), dtype=int)

    # Ustawienie jedynki na piątym miejscu
    v[4] = 1
    return v

assert make_v(14)[4] == 1
np.testing.assert_array_equal( make_v(14)[:4] , 0)
np.testing.assert_array_equal( make_v(14)[5:] , 0)

# ---------------------------------------------------
# 4) Utwórz wektor z wartościami od 10 do 49 włącznie

def make10_49():
    v = np.array(range(10, 50))
    return v

assert make10_49()[7] == 17

# ---------------------------------------------------
# 5) Odwóć kolejność elementów wektora

def reverse(v):
    v = v[::-1]
    return v

np.testing.assert_equal(reverse(np.array([1,2,3])), np.array([3,2,1]))

# ---------------------------------------------------
# 6) Stwórz macierz jednostkową o rozmiarze n x n

def identity_matrix(n):
    v = np.eye(n)
    return v

np.testing.assert_equal(identity_matrix(2), np.array([[1, 0],  [0, 1]]))

# ---------------------------------------------------
# 7) Utwórz macierz n×n z wartościami od 1 do n**2

# Tak by każdy rząd zawierał wartości rosnące o 1
def n2_col(n):
    # Inicjalizacja talicy z wartościami od 1 do n**2,
    # a następnie zmienienie jej kształtu na macierz n x n
    v = np.arange(1, n ** 2 + 1).reshape(n, n)
    return v

np.testing.assert_equal( n2_col(2), np.array([[1, 2],  [3, 4]]) )

# Tak by każda kolumna zawierała wartości rosnące o 1
def n2_row(n):
    # Zamiana kolumn z wierszami tak aby wartości nie roszły w
    # wierszach tylko w kolumnach
    v = n2_col(n).transpose()
    return v

np.testing.assert_equal( n2_row(2), np.array([[1, 3],  [2, 4]]) )

# ---------------------------------------------------
# 8) Utwórz macierz n×m z losowymi wartościami

# Z przedziału [0,1)
def rand1(n, m):
    v = np.random.uniform(0,1,size=(n,m))
    return v

assert np.min(rand1(n=10, m=11)) >= 0
assert np.max(rand1(n=10, m=10)) < 1
assert np.shape(rand1(n=10, m=15)) == (10, 15)

# Z przedziału [a,b)
def rand2(n, m, a, b):
    v = np.random.uniform(a, b, size=(n, m))
    return v

assert np.min(rand2(n=10, m=12, a=3, b=7)) >= 3
assert np.max(rand2(n=10, m=12, a=3, b=7)) < 7
assert np.shape(rand2(n=10, m=12, a=3, b=7)) == (10, 12)

# ---------------------------------------------------
# 9) Znajdź wskaźniki dla których wartości wektora są równe zero

def is_zero(x):
    return np.where(x == 0)[0]

x = np.array([1,2,0,1,0,11])
np.testing.assert_equal(is_zero(x), np.array([2,4]))

# ---------------------------------------------------
# 10) Oblicz dla zadanego wektora jego wartość najmniejszą, największą oraz średnią.

def mystats(x):
    return np.min(x), np.max(x), np.average(x)

x = np.array([1,2,0,1,0,11])
assert  mystats(x) == (0, 11, 2.5)

# ---------------------------------------------------
# 11) Stwórz dwuwymiarową tablicę z zerami w środku i jedynkami na zewnątrz.
def zeros_padded(n):

    # Stworzenie macierzy n x n wypełnionej zerami
    matrix = np.zeros((n, n))

    # Zastępienie krańcowych wartości jedynkami
    matrix = np.pad(matrix[1:-1, 1:-1], 1, 'constant', constant_values=1)

    return matrix

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

np.testing.assert_equal(zeros_padded(n=4),
                        padded_array)

# ---------------------------------------------------
# 12) Używając `np.pad` dodaj do tablicy otoczenie z wartością 3

def pad3(x):

    # Do całej macierzy dodajemy obramowanie o szerokości 1 elementu
    # Obramowanie będzie wypełnione 3
    matrix = np.pad(x, 1, 'constant', constant_values=3)
    return matrix

x = np.ones((2,3))
x_pad3 = np.array([[ 3.,  3.,  3.,  3.,  3.],
                   [ 3.,  1.,  1.,  1.,  3.],
                   [ 3.,  1.,  1.,  1.,  3.],
                   [ 3.,  3.,  3.,  3.,  3.]])
np.testing.assert_equal(pad3(x), x_pad3)

# ---------------------------------------------------
# 13) Dla danej tablicy zastąp maksymalne wartości zerami.

def maxto0(x):
    # Wyznaczenie maksimum w danym macierzu
    maximium = np.max(x)

    # Zamienienie maksymalnych wartości na zera
    x[np.where(x == maximium)] = 0

    return x

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

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

np.testing.assert_equal(maxto0(x),x_expected)

# ---------------------------------------------------
# 14) Niech będzie dana tablica k parametrów zmierzonych w n pomiarach:
#       xij:  j-ty parametr w i-tym pomiarze

# Stwórz funkcję nie zawierającą pętli, która obliczy:
# - średnią po pomiarach dla wszyskich zmiennych
# - odchylenie od wartości średniej dla każdej zmiennej we wszystkich pomiarach
# - odchylenie średniokwadratowe dla każdej zmiennej

def data_stats(x):

    # Liczba wierszy w danym macierzu
    n = x.shape[0]

    # Suma wszystkich pomiarów (suma danej kolumny) podzielona przez n pomiarów (liczba wierszy)
    x_avg = 1 / n * np.sum(x, axis=0)

    # Od każdego wiersza w macierzy odejmujemy średnie wartości poszczególnych kolumn
    x_delta = x - x_avg

    # Odchylenie średniokwadratowe dla każdej zmiennej (dla każdej kolumny)
    x_sigma = 1 / n * np.sum((x - x_avg) ** 2, axis=0)

    return x_avg, x_delta, x_sigma

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

average, delta, sigma = data_stats(example_matrix)
expected_average = np.array([ 3.2, 3. ,  2.2,  2.4])
expected_delta = np.array([[-0.2,  1. , -0.2,  0.6],
                           [-0.2,  1. ,  0.8,  1.6],
                           [ 0.8,  0. , -0.2, -0.4],
                           [-0.2, -1. , -1.2, -0.4],
                           [-0.2, -1. ,  0.8, -1.4]])
expected_sigma = np.array([0.16, 0.8 , 0.56, 1.04])

np.testing.assert_allclose(average,
                           expected_average)

np.testing.assert_allclose(delta,
                           expected_delta)

np.testing.assert_allclose(sigma,
                           expected_sigma)

blacklist = [".mean",".average","for","while","std"]
assert all([ not keyword  in inspect.getsource(data_stats) for keyword in blacklist])

print("All tests were successful.")


import numpy as np
#Evaluate the linear regression
def compute_cost(X, y, theta):
    '''
    Comput cost for linear regression
    '''
    #Number of training samples
    m = y.size

    predictions = X.dot(theta).flatten()

    sqErrors = (predictions - y) ** 2

    J = (1.0 / (2 * m)) * sqErrors.sum()

    return J

def gradient_descent(X, y, theta, alpha, num_iters):
    '''
    Performs gradient descent to learn theta
    by taking num_items gradient steps with learning
    rate alpha
    '''
    m = y.size
    J_history = np.zeros(shape=(num_iters, 1))

    for i in range(num_iters):

        predictions = X.dot(theta).flatten()

        errors_x1 = (predictions - y) * X[:, 0]
        errors_x2 = (predictions - y) * X[:, 1]

        theta[0][0] = theta[0][0] - alpha * (1.0 / m) * errors_x1.sum()
        theta[1][0] = theta[1][0] - alpha * (1.0 / m) * errors_x2.sum()

        J_history[i, 0] = compute_cost(X, y, theta)

    return theta, J_history


import numpy as np

def power_method(A, error_tol):
	lim = 10000 #Numero maximo de iteracoes
	error = np.inf #atribuicao de infinito para o erro
	n = A.shape[1] #pegando a dimensao da matriz A quadrada
	y0 = np.zeros(n)
	y0[0] = 1 #chute inicial normalizado

	for k in range (0, lim):
		xk = A.dot(y0) #produto de matrizes (x^k) = A * y^(k-1)
		yk = xk/np.linalg.norm(xk) #normalizando xk
		error = np.abs(np.abs(y0.dot(yk))-1) #teste de alinhamento, calculando o erro
		if error <= error_tol: # se o erro for menor que a tolerancia recebida como parametro, pode parar a iteracao
			break
		y0 = yk #atribuindo o novo y0

	lambda_1 = y0.dot(A.dot(y0)) #calculando o lambda_1 = y^k * (A * y^k)

	return lambda_1, y0 #retornando o autovalor e seu autovetor associado

A = np.array([[12, 2, 3],
              [ 2, 3, 5],
              [ 3, 5,-2]], dtype='double')

(D,V) = np.linalg.eig(A) #usando uma funcao pronta para calcular todos os autovalores
print('Método do Python: {0:.15f}'.format(np.max(abs(D))))
(autovalor, autovetor) = power_method(A, 0.000000001) #usando o metodo da potencia implementado com erro em torno de 10^-9
print('Método Implementado: {0:.15f}'.format(autovalor))


import numpy as np

def forward_euler(f,y0,Delta_t,numsteps):
    """Perform numsteps of the forward euler method starting at y0
    of the ODE y'(t) = f(y,t)
    Args:
        f: function to integrate takes arguments y,t
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    y = np.zeros(numsteps+1)
    t = np.arange(numsteps+1)*Delta_t
    y[0] = y0
    for n in range(1,numsteps+1):
        y[n] = y[n-1] + Delta_t * f(y[n-1], t[n-1])
    return t, y

def inexact_newton(f,x0,delta = 1.0e-7, epsilon=1.0e-6, LOUD=False):
    """Find the root of the function f via Newton-Raphson method
    Args:
        f: function to find root of
        x0: initial guess
        delta: finite difference parameter
        epsilon: tolerance

    Returns:
        estimate of root
    """
    x = x0
    if (LOUD):
        print("x0 =",x0)
    iterations = 0
    while (np.fabs(f(x)) > epsilon):
        fx = f(x)
        fxdelta = f(x+delta)
        slope = (fxdelta - fx)/delta
        if (LOUD):
            print("x_",iterations+1,"=",x,"-",fx,"/",slope,"=",x - fx/slope)
        x = x - fx/slope
        iterations += 1
    #print("It took",iterations,"iterations")
    return x #return estimate of root

def backward_euler(f,y0,Delta_t,numsteps):
    """Perform numsteps of the backward euler method starting at y0
    of the ODE y'(t) = f(y,t)
    Args:
        f: function to integrate takes arguments y,t
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    y = np.zeros(numsteps+1)
    t = np.arange(numsteps+1)*Delta_t
    y[0] = y0
    for n in range(1,numsteps+1):
        solve_func = lambda u: u-y[n-1] - Delta_t*f(u,t[n])
        y[n] = inexact_newton(solve_func,y[n-1])
    return t, y

def inexact_newton(f,x0,delta = 1.0e-7, epsilon=1.0e-6, LOUD=False):
    """Find the root of the function f via Newton-Raphson method
    Args:
        f: function to find root of
        x0: initial guess
        delta: finite difference parameter
        epsilon: tolerance

    Returns:
        estimate of root
    """
    x = x0
    if (LOUD):
        print("x0 =",x0)
    iterations = 0
    while (np.fabs(f(x)) > epsilon):
        fx = f(x)
        fxdelta = f(x+delta)
        slope = (fxdelta - fx)/delta
        if (LOUD):
            print("x_",iterations+1,"=",x,"-",fx,"/",slope,"=",x - fx/slope)
        x = x - fx/slope
        iterations += 1
    #print("It took",iterations,"iterations")
    return x #return estimate of root

def backward_euler(f,y0,Delta_t,numsteps):
    """Perform numsteps of the backward euler method starting at y0
    of the ODE y'(t) = f(y,t)
    Args:
        f: function to integrate takes arguments y,t
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    y = np.zeros(numsteps+1)
    t = np.arange(numsteps+1)*Delta_t
    y[0] = y0
    for n in range(1,numsteps+1):
        solve_func = lambda u: u-y[n-1] - Delta_t*f(u,t[n])
        y[n] = inexact_newton(solve_func,y[n-1])
    return t, y

def RK4(f,y0,Delta_t,numsteps):
    """Perform numsteps of the 4th order Runge-Kutta method starting at y0
    of the ODE y'(t) = f(y,t)
    Args:
        f: function to integrate takes arguments y,t
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    y = np.zeros(numsteps+1)
    t = np.arange(numsteps+1)*Delta_t
    y[0] = y0
    for n in range(1,numsteps+1):
        dy1 = Delta_t * f(y[n-1], t[n-1])
        dy2 = Delta_t * f(y[n-1] + 0.5*dy1, t[n-1] + 0.5*Delta_t)
        dy3 = Delta_t * f(y[n-1] + 0.5*dy2, t[n-1] + 0.5*Delta_t)
        dy4 = Delta_t * f(y[n-1] + dy3, t[n-1] + Delta_t)
        y[n] = y[n-1] + 1.0/6.0*(dy1 + 2.0*dy2 + 2.0*dy3 + dy4)
    return t, y

def forward_euler_system(Afunc,c,y0,Delta_t,numsteps):
    """Perform numsteps of the forward euler method starting at y0
    of the ODE y'(t) = A(t) y(t) + c(t)
    Args:
        Afunc: function to compute A matrix
        c: nonlinear function of time
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    unknowns = y0.size
    y = np.zeros((unknowns,numsteps+1))
    t = np.arange(numsteps+1)*Delta_t
    y[0:unknowns,0] = y0
    for n in range(1,numsteps+1):
        yold = y[0:unknowns,n-1]
        A = Afunc(t[n-1])
        y[0:unknowns,n] = yold + Delta_t * (np.dot(A,yold) + c(t[n-1]))
    return t, y

def swap_rows(A, a, b):
    """Rows two rows in a matrix, switch row a with row b

    args:
        A: matrix to perform row swaps on
        a: row index of matrix
        b: row index of matrix

    returns: nothing

    side effects:
    changes A to rows a and b swapped
    """
    assert (a>=0) and (b>=0)
    N = A.shape[0] #number of rows
    assert (a<N) and (b<N) #less than because 0-based indexing
    temp = A[a,:].copy()
    A[a,:] = A[b,:].copy()
    A[b,:] = temp.copy()
def BackSub(aug_matrix,x):
    """back substitute a N by N system after Gaussian elimination

    Args:
        aug_matrix: augmented matrix with zeros below the diagonal
        x: length N vector to hold solution
    Returns:
        nothing
    Side Effect:
    x now contains solution
    """
    N = x.size
    for row in range(N-1,-1,-1):
        RHS = aug_matrix[row,N]
        for column in range(row+1,N):
            RHS -= x[column]*aug_matrix[row,column]
        x[row] = RHS/aug_matrix[row,row]
    return
def GaussElimPivotSolve(A,b,LOUD=0):
    """create a Gaussian elimination with pivoting matrix for a system

    Args:
        A: N by N array
        b: array of length N
    Returns:
        solution vector in the original order
    """
    [Nrow, Ncol] = A.shape
    assert Nrow == Ncol
    N = Nrow
    #create augmented matrix
    aug_matrix = np.zeros((N,N+1))
    aug_matrix[0:N,0:N] = A
    aug_matrix[:,N] = b
    #augmented matrix is created

    #create scale factors
    s = np.zeros(N)
    count = 0
    for row in aug_matrix[:,0:N]: #don't include b
        s[count] = np.max(np.fabs(row))
        count += 1
    if LOUD:
        print("s =",s)
    if LOUD:
        print("Original Augmented Matrix is\n",aug_matrix)
    #perform elimination
    for column in range(0,N):

        #swap rows if needed
        largest_pos = np.argmax(np.fabs(aug_matrix[column:N,column]/s[column])) + column
        if (largest_pos != column):
            if (LOUD):
                print("Swapping row",column,"with row",largest_pos)
                print("Pre swap\n",aug_matrix)
            swap_rows(aug_matrix,column,largest_pos)
            #re-order s
            tmp = s[column]
            s[column] = s[largest_pos]
            s[largest_pos] = tmp
            if (LOUD):
                print("A =\n",aug_matrix)
        #finish off the row
        for row in range(column+1,N):
            mod_row = aug_matrix[row,:]
            mod_row = mod_row - mod_row[column]/aug_matrix[column,column]*aug_matrix[column,:]
            aug_matrix[row] = mod_row
    #now back solve
    x = b.copy()
    if LOUD:
        print("Final aug_matrix is\n",aug_matrix)
    BackSub(aug_matrix,x)
    return x

def backward_euler_system(Afunc,c,y0,Delta_t,numsteps):
    """Perform numsteps of the forward euler method starting at y0
    of the ODE y'(t) = A(t) y(t) + c(t)
    Args:
        Afunc: function to compute A matrix
        c: nonlinear function of time
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    unknowns = y0.size
    y = np.zeros((unknowns,numsteps+1))
    t = np.arange(numsteps+1)*Delta_t
    y[0:unknowns,0] = y0
    for n in range(1,numsteps+1):
        yold = y[0:unknowns,n-1]
        A = Afunc(t[n])
        LHS = np.identity(unknowns) - Delta_t * A
        RHS = yold + c(t[n])*Delta_t
        y[0:unknowns,n] = GaussElimPivotSolve(LHS,RHS)
    return t, y

def cn_system(Afunc,c,y0,Delta_t,numsteps):
    """Perform numsteps of the forward euler method starting at y0
    of the ODE y'(t) = A(t) y(t) + c(t)
    Args:
        Afunc: function to compute A matrix
        c: nonlinear function of time
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    unknowns = y0.size
    y = np.zeros((unknowns,numsteps+1))
    t = np.arange(numsteps+1)*Delta_t
    y[0:unknowns,0] = y0
    for n in range(1,numsteps+1):
        yold = y[0:unknowns,n-1]
        A = Afunc(t[n])
        LHS = np.identity(unknowns) - 0.5*Delta_t * A
        A = Afunc(t[n-1])
        RHS = yold + 0.5*Delta_t * np.dot(A,yold) + 0.5*(c(t[n-1]) + c(t[n]))*Delta_t
        y[0:unknowns,n] = GaussElimPivotSolve(LHS,RHS)
    return t, y

def RK4_system(Afunc,c,y0,Delta_t,numsteps):
    """Perform numsteps of the forward euler method starting at y0
    of the ODE y'(t) = f(y,t)
    Args:
        f: function to integrate takes arguments y,t
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    unknowns = y0.size
    y = np.zeros((unknowns,numsteps+1))
    t = np.arange(numsteps+1)*Delta_t
    y[0:unknowns,0] = y0
    for n in range(1,numsteps+1):
        yold = y[0:unknowns,n-1]
        A = Afunc(t[n-1])
        dy1 = Delta_t * (np.dot(A,yold) + c(t[n-1]))
        A = Afunc(t[n-1] + 0.5*Delta_t)
        dy2 = Delta_t * (np.dot(A,y[0:unknowns,n-1] + 0.5*dy1)
                         + c(t[n-1] + 0.5*Delta_t))
        dy3 = Delta_t * (np.dot(A,y[0:unknowns,n-1] + 0.5*dy2)
                         + c(t[n-1] + 0.5*Delta_t))
        A = Afunc(t[n] + Delta_t)
        dy4 = Delta_t * (np.dot(A,y[0:unknowns,n-1] + dy3) + c(t[n]))
        y[0:unknowns,n] = y[0:unknowns,n-1] + 1.0/6.0*(dy1 + 2.0*dy2 + 2.0*dy3 + dy4)
    return t, y

def new2(f,y0,Delta_t,numsteps):
    """Perform numsteps of the backward euler method starting at y0
    of the ODE y'(t) = f(y,t)
    Args:
        f: function to integrate takes arguments y,t
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    y = np.zeros(numsteps+1)
    t = np.arange(numsteps+1)*Delta_t
    y[0] = y0
    for n in range(1,numsteps+1):
        tcur = t[n-1]
        fshift = lambda u,t: f(u,t+tcur)
        tmp,y1 = backward_euler(fshift,y[n-1],Delta_t*0.5,1)
        tmp,y2 = backward_euler(fshift,y[n-1],Delta_t,1)
        y[n] = y[n-1] + Delta_t*(1.0/(1.0+Delta_t) * f(y1[1],t[n-1]+0.5*Delta_t) +
                                 Delta_t/(1.0+Delta_t) * f(y2[1],t[n]))
    return t, y

def new2_system(Afunc,c,y0,Delta_t,numsteps):
    """Perform numsteps of the forward euler method starting at y0
    of the ODE y'(t) = A(t) y(t) + c(t)
    Args:
        Afunc: function to compute A matrix
        c: nonlinear function of time
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    unknowns = y0.size
    y = np.zeros((unknowns,numsteps+1))
    t = np.arange(numsteps+1)*Delta_t
    y[0:unknowns,0] = y0
    for n in range(1,numsteps+1):
        tcur = t[n-1]
        Af = lambda t: Afunc(t+tcur)
        cf = lambda t: c(t+tcur)
        tmp,y1 = backward_euler_system(Af,cf,y[0:unknowns,n-1],Delta_t*0.5,1)
        tmp,y2 = backward_euler_system(Af,cf,y[0:unknowns,n-1],Delta_t,1)
        y1 = y1[0:unknowns,1]
        y2 = y2[0:unknowns,1]
        th = t[n-1] + 0.5*Delta_t
        Ah = Afunc(th)
        A = Afunc(t[n])
        y[0:unknowns,n] = y[0:unknowns,n-1] + Delta_t*(1.0/(1.0+Delta_t) * (np.dot(Ah,y1) + c(th)) +
                                 Delta_t/(1.0+Delta_t) * (np.dot(A,y2) + c(t[n])) )
    return t, y

def crank_nicolson(f,y0,Delta_t,numsteps):
    """Perform numsteps of the backward euler method starting at y0
    of the ODE y'(t) = f(y,t)
    Args:
        f: function to integrate takes arguments y,t
        y0: initial condition
        Delta_t: time step size
        numsteps: number of time steps

    Returns:
        a numpy array of the times and a numpy
        array of the solution at those times
    """
    numsteps = int(numsteps)
    y = np.zeros(numsteps+1)
    t = np.arange(numsteps+1)*Delta_t
    y[0] = y0
    for n in range(1,numsteps+1):
        solve_func = lambda u: u-y[n-1] - 0.5*Delta_t*(f(u,t[n])
                                                       + f(y[n-1],t[n-1]))
        y[n] = inexact_newton(solve_func,y[n-1])
    return t, y


"""
@author = mbilkhu
Code for Logistic Regression
"""

import numpy as np

def load_dataset():
    X = np.array([[0,0],[0,1],[1,0],[1,1]])
    y = np.array([0,1,1,0])
    W = np.random.randn(1,2)
    b = np.ones(1)
    x = X.T
    y = y.T
    print("X Shape:- " +  str(x.shape))
    print("Y Shape:- " +  str(y.shape))
    return x,y,W,b

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

def sigmoid_gradient(x):
    return x * (1-x)

def forward_propagate(W, x, b):
    z = np.dot(W, x) + b
    a = sigmoid(z)
    return a

def compute_loss(y, a):
    x = np.multiply(y, np.log(a)) + np.multiply((1-y), np.log(1-a))
    cost = np.sum(x, dtype=np.float32)
    return -np.squeeze(cost)

def gradient_descent(y, a, x):
    dz = (a-y) * sigmoid_gradient(a)
    dw = np.dot(dz, x.T)
    db = dz
    return dw, db

def main():
    x,y,W,b = load_dataset()
    loss = []
    n_epochs = 60000
    learning_rate = 0.01
    for i in range(n_epochs):
        a = forward_propagate(W, x, b)
        cost = compute_loss(y, a)
        loss.append(cost)
        dw, db = gradient_descent(y, a, x)
        W = W - learning_rate * dw
        b = b - learning_rate * db
        if i%250 == 0:
            print("Loss after epoch %d = %f " % (i, cost))
    print(a)

if __name__=='__main__':
    main()


import math
from cmath import pi

import numpy as np

def cylinder_area(r:float,h:float):
    """Obliczenie pola powierzchni walca.
    Szczegółowy opis w zadaniu 1.

    Parameters:
    r (float): promień podstawy walca
    h (float): wysokosć walca

    Returns:
    float: pole powierzchni walca
    """
    if r > 0 and h > 0:
        return 2 * pi * r * h + pi * r * r * 2
    else:
        return math.nan

def fib(n:int):
    """Obliczenie pierwszych n wyrazów ciągu Fibonnaciego.
    Szczegółowy opis w zadaniu 3.

    Parameters:
    n (int): liczba określająca ilość wyrazów ciągu do obliczenia

    Returns:
    np.ndarray: wektor n pierwszych wyrazów ciągu Fibonnaciego.
    """
    if n > 0 and isinstance(n, int):
        result = np.ndarray(shape=(1, n), dtype=int)
        result[0][0] = 1  # jak zaczynam od 0 to testy nie przechodzą
        if n == 1:  # dostaje dosyć sprzeczne odpowiedzi od testów więc dorzucam ten warunek
            return result[0]
        if n >= 2:
            result[0][1] = 1
        if n >= 3:
            i = 2
            while n > i:
                result[0][i] = result[0][i - 1] + result[0][i - 2]
                i += 1
        return result

    else:
        return None

def matrix_calculations(a:float):
    """Funkcja zwraca wartości obliczeń na macierzy stworzonej
    na podstawie parametru a.
    Szczegółowy opis w zadaniu 4.

    Parameters:
    a (float): wartość liczbowa

    Returns:
    touple: krotka zawierająca wyniki obliczeń
    (Minv, Mt, Mdet) - opis parametrów w zadaniu 4.
    """
    M = np.array([[a, 1, -a], [0, 1, 1], [-a, a, 1]])
    try:
        Minv = np.linalg.inv(M)
    except np.linalg.LinAlgError:
        Minv = math.nan
    Mt = M.T
    Mdet = np.linalg.det(M)
    return Minv, Mt, Mdet

def custom_matrix(m:int, n:int):
    """Funkcja zwraca macierz o wymiarze mxn zgodnie
    z opisem zadania 7.

    Parameters:
    m (int): ilość wierszy macierzy
    n (int): ilość kolumn macierzy

    Returns:
    np.ndarray: macierz zgodna z opisem z zadania 7.
    """
    if n >= 0 and m >= 0 and isinstance(n, int) and isinstance(m, int):
        result = np.ndarray([m, n])
        for i, row in enumerate(result):
            for j, _ in enumerate(row):
                if i > j:
                    result[i][j] = i
                else:
                    result[i][j] = j
        return result
    else:
        return None

Metody numeryczne 2021/Laboratorium 1/main.py


# Author: Martin Konečnik
# Contact: martin.konecnik@gmail.com
# Licenced under MIT

# 1-Vzorčenje
# Predlagan IDE PyCharm Community Edition (na voljo za Windows, macOS in Linux)
# https://www.jetbrains.com/pycharm/download
# Navodila za pridobitev potrebnih knjižnic:
# https://www.jetbrains.com/help/pycharm/installing-uninstalling-and-upgrading-packages.html
# Kratka navodila. Znotraj GUI knjižnice dodamo prek File->Settings->Project: Name->Project Interpreter.
# V tem oknu na desni strani kliknemo na plus in vpišemo ime knjižnice.
# Privzeta bližnjica za zagon izbora je Alt+Shift+E

import numpy as np
import scipy.signal
from matplotlib import cm  # color mapping
import pylab as pylab
import matplotlib.pyplot as plt
import sounddevice as sd
from pathlib import Path
from PIL import Image
from mpl_toolkits.mplot3d import axes3d
from mpl_toolkits.mplot3d import art3d

# ----------------------------------------------------------------------------------------
# Vzorčenje in Nyquistov teorem;
Fvz = 100  # Frekvenca vzorčenja (v Hz)
T = 1  # dolžina signala (v s)
i = np.arange(float(T) * Fvz) / Fvz  # vektor časovnih indeksov
f1 = 5  # frekvenca sinusoide
A1 = 1  # amplituda sinusoide
faza1 = 0.0  # faza sinusoide

# ukazi.m:10 -- NOTE: Enak rezultat kot v Matlab 2017b
# izris sinuside pri razlicnih fazah
plt.figure()  # Ta vrstica ni nujna, če odpremo le eno okno.
for faza1 in np.arange(0, 6.1, 0.1):
    plt.cla()
    # pri množenju z matriko je potrebno uporabiti numpy.np.dot(...)
    s = np.dot(A1, np.sin(np.dot(2 * np.pi * f1, i) + faza1 * np.pi))
    plt.plot(i, s)
    setattr(plt.gca, 'YLim', [-1, 1])
    plt.title('Fvz = {0} Hz, Frekvenca = {1} Hz, faza = {2} $\pi$'.format(Fvz, f1, round(faza1, 1)))
    plt.xlabel('Čas (s)')
    plt.ylabel('Amplituda (dB)')
    plt.tight_layout()
    plt.waitforbuttonpress()

# ukazi.m:23 -- NOTE: Preverjeno z Matlab
# izris sinusid pri različnih frekvencah
faza1 = 0.0
plt.figure()
for f1 in np.arange(Fvz + 1):
    plt.cla()  # počistimo graf za naslednjo iteracijo
    s = np.dot(A1, np.sin(np.dot(2 * np.pi * f1, i) + faza1 * np.pi))
    plt.plot(i, s)
    plt.ylim(-1, 1)
    plt.title('Fvz = {0} Hz, Frekvenca = {1} Hz, faza = {2} $\pi$'.format(Fvz, f1, faza1))
    plt.xlabel('Čas (s)')
    plt.ylabel('Amplituda (dB)')
    plt.pause(0.025)

# ukazi.m:37 -- NOTE: Preverjeno z Matlab
# izris sinusoid s frekvenco f1 in Fvz-f1
f1 = 1
s1 = np.sin(np.dot(2 * np.pi * f1, i) + faza1 * np.pi)
plt.figure()
plt.plot(i, s1, 'b')
f2 = Fvz - f1
s2 = np.sin(np.dot(np.dot(np.dot(2, np.pi), f2), i) + np.dot(faza1, np.pi))
plt.plot(i, s2, 'r')
plt.xlabel('Čas (s)')
plt.ylabel('Amplituda')
plt.title('Fvz = {0} Hz, Frekvenca1 = {1} Hz, Frekvenca2 = {2} Hz, faza = {3} $\pi$'.format(Fvz, f1, f2, faza1))
plt.tight_layout()
# ----------------------------------------------------------------------------------------

# glej tudi primera v Mathematici: (WheelIllusion.nbp in SamplingTheorem.nbp)

# V Z O R Č E N J E    Z V O K A
# -----------------------------------------------------------------------------------------
# ukazi.m:56 -- NOTE: Primerljiv rezultat v Matlab
# vzorčenje zvoka
Fs = 44100  # vzorčevalna frekvenca
bits = 16  # bitna ločljivost
nchans = 1  # 1 (mono), 2 (stereo).
posnetek = sd.rec(5 * Fs, Fs, nchans, blocking=True)

plt.figure()
plt.plot(posnetek)

sd.play(posnetek, 44100)
sd.play(posnetek, 44100 / 2)
sd.play(posnetek, 2 * 44100)

# -----------------------------------------------------------------------------------------
# ukazi.m:73 -- NOTE: Primerljiv rezultat v Matlab
# ali zaznate fazne spremembe? Spreminjajte faza1 med 0 in 2.0 in poženite ta demo...
Fvz = 44100  # vzorčevalna frekvenca
T = 3  # čas v sekundah
i = np.arange(0.0, T * Fvz, 1) / Fvz  # vektor časovnih indeksov
f1 = 500  # frekvenca sinusoide
A1 = 0.3  # amplituda sinusoide
faza1 = 1.0  # faza sinusoide

s = np.dot(A1, np.sin(np.dot(2 * np.pi * f1, i) + faza1 * np.pi))  # tvorjenje sinusoide
s2 = np.dot(A1, np.sin(np.dot(2 * np.pi * f1, i) + 0 * np.pi))  # tvorjenje sinusoide

sd.play(np.concatenate((s, s2)), Fvz)  # pozor, dvojni oklepaji pri concatenate, ker sta s1 in s2 en parameter!
# "navadna" polja združiš z s+s2, v numpy.array pa to sešteje istoležne elemente

# -----------------------------------------------------------------------------------------
# ukazi.m:88 -- NOTE: Primerljiv rezultat v Matlab
# trije poskusi: 1. f1 = 50;
#                2. f1 = 450;
#                3. f1 = 1450;
#                4. f1 = 2450;

Fvz = 44100  # vzorčevalna frekvenca
T = 3  # čas v sekundah
i = np.arange(T * Fvz) / Fvz  # vektor časovnih indeksov
f1 = 50  # frekvenca sinusoide
A1 = 5.5  # amplituda sinusoide
faza1 = 0.0  # faza sinusoide
f2 = f1 + 1  # frekvenca druge sinusoide

s1 = np.dot(A1, np.sin(np.dot(2 * np.pi * f1, i) + faza1 * np.pi))  # tvorjenje prve sinusoide
s2 = np.dot(A1, np.sin(np.dot(2 * np.pi * f2, i) + faza1 * np.pi))  # tvorjenje druge sinusoide
sd.play(np.concatenate((s1, s2)), Fvz)

# -----------------------------------------------------------------------------------------
# ukazi.m:106 -- NOTE: Primerljiv rezultat v Matlab
# ali zaznate zvok netopirja pri 90000 Hz? Nyquist?
Fvz = 44100  # vzorčevalna frekvenca
T = 3
i = np.arange(T * Fvz) / Fvz  # vektor časovnih indeksov
fnetopir = 140000  # frekvenca sinusoide
A1 = 5.5  # amplituda sinusoide
faza1 = 1.0  # faza sinusoide

s = np.dot(A1, np.sin(np.dot(2 * np.pi * fnetopir, i) + faza1 * np.pi))  # tvorjenje sinusoide
sd.play(s, Fvz)

# ukazi.m:118 -- NOTE: Preverjeno z Matlab
# izris sinuside pri razlicnih fazah (verzija 2)
Fvz = 100
T = 1
i = np.arange(T * Fvz) / Fvz
f1 = 5
A1 = 5
faza1 = 0.0

# spremninjanje frekvence...
plt.close('all')
fig, ax = plt.subplots(2)  # create a figure with 2 subplots
fig.tight_layout(rect=[0, 0.03, 1, 0.95])

for f1 in np.arange(0, Fvz+1):
    ax[0].clear()
    ax[1].clear()

    s = np.sin(np.dot(2 * np.pi * f1, i) + faza1 * np.pi)
    ax[0].plot(s)
    ax[0].set_ylim(-1, 1)
    ax[0].set_title('Časovna domena: Fvz = {0} Hz, Frekvenca = {1} Hz, faza = {2} $\pi$'.format(Fvz, f1, faza1))

    ax[1].plot(abs(np.fft.fft(s)), 'r')
    ax[1].set_ylim(-1, 1)
    ax[1].set_title('Frekvenčna domena (abs): Fvz = {0} Hz, Frekvenca = {1} Hz, faza = {2} $\pi$'.format(Fvz, f1, faza1))

    plt.waitforbuttonpress()

# in faze... (več o tem na naslednjih vajah)
f1 = 5
A1 = 5
faza1 = 0.0
plt.close('all')
fig, ax = plt.subplots(2)
fig.tight_layout(rect=[0, 0.03, 1, 0.95])

for faza1 in np.arange(0, 2.1, 0.1):
    ax[0].clear()
    ax[1].clear()

    s = np.sin(np.dot(2 * np.pi * f1, i) + faza1 * np.pi)
    ax[0].plot(s)
    ax[0].set_ylim(-1, 1)
    ax[0].set_title('Časovna domena: Fvz = {0} Hz, Frekvenca = {1} Hz, faza = {2} $\pi$'.format(Fvz, f1, round(faza1, 1)))

    ax[1].plot(abs(np.fft.fft(s)), 'r')
    ax[1].set_ylim(-1, 1)
    ax[1].set_title('Frekvenčna domena (abs): Fvz = {0} Hz, Frekvenca = {1} Hz, faza = {2} $\pi$'.format(Fvz, f1, round(faza1, 1)))

    plt.waitforbuttonpress()

# S L I K E
# -----------------------------------------------------------------------------------------
# ukazi.m:181 -- NOTE: Preverjetno v Matlabu. Del ne deluje pravilno (označeno)
# vzorčenje slik in Moire
# Če datoteke ne najde, preverite pod "Settings -> Project: Name -> Project Structure" kje je root.
A = pylab.array(Image.open(Path('./1-Vzorcenje/Moire.jpg')))
plt.figure(figsize=(10, 10))
plt.axis('off')
plt.imshow(A)
plt.title('originalna slika')

pvz = 3
plt.figure()
plt.axis('off')
plt.imshow(A[::pvz, ::pvz])
plt.title('podvzorčena slika: faktor podvzorčenja {0}'.format(pvz))

# Celotna slika bistveno svetlejša kot v matlabu. Pri bitni ločljivosti 2, sta namesto sivin rumena in modra barva.
# Podatki?
st_bit = 2
kvant = 2 ** (9 - st_bit)
plt.figure(figsize=(10, 10))
plt.axis('off')
plt.imshow(np.dot(np.round(A[:, :, :] / kvant), kvant))
plt.title('slika pri bitni ločljivosti {0}'.format(st_bit))

fig, ax = plt.subplots(2, 2)
fig.tight_layout()

ax[0, 0].imshow(np.dot(np.round(A[:, :, :] / kvant), kvant))
ax[0, 0].set_title('slika pri bitni ločljivosti {0}'.format(st_bit))
ax[0, 0].set_xticklabels([])
ax[0, 0].set_yticklabels([])

ax[0, 1].imshow(np.dot(np.round(A[:, :, 0] / kvant), kvant))
ax[0, 1].set_title('ravnina R pri bitni ločljivosti {0}'.format(st_bit))
ax[0, 1].set_xticklabels([])
ax[0, 1].set_yticklabels([])

ax[1, 0].imshow(np.dot(np.round(A[:, :, 1] / kvant), kvant))
ax[1, 0].set_title('ravnina G pri bitni ločljivosti {0}'.format(st_bit))
ax[1, 0].set_xticklabels([])
ax[1, 0].set_yticklabels([])

ax[1, 1].imshow(np.dot(np.round(A[:, :, 2] / kvant), kvant))
ax[1, 1].set_title('ravnina B pri bitni ločljivosti {0}'.format(st_bit))
ax[1, 1].set_xticklabels([])
ax[1, 1].set_yticklabels([])

# -----------------------------------------------------------------------------------------
# ukazi.m:215 -- Note: Plotting not working properly yet. Data shown in 2D.
# spekter slik, Moire in Diskretna Fourierova transformacija (fft2)
A = pylab.array(Image.open(Path('./1-Vzorcenje/Moire.jpg')))
plt.figure().set_size_inches(10, 10)
plt.imshow(A)
plt.title('originalna slika')
plt.axis('off')

plt.close('all')

# ukazi.m:224
B = np.double(A[:, :, 0])

fig = plt.figure()
fig.set_size_inches(7, 7)
ax = fig.add_subplot(111, projection='3d')
X = Y = np.array([np.arange(100), ]*100)  # Creates a 100 * 100 array
Z = abs(np.fft.fft2(B - np.mean(np.ravel(B)), s=(100, 100)))
wire = ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)

# Retrive data from internal storage of plot_wireframe, then delete it
nx, ny, _ = np.shape(wire._segments3d)
wire_x = np.array(wire._segments3d)[:, :, 0].ravel()
wire_y = np.array(wire._segments3d)[:, :, 1].ravel()
wire_z = np.array(wire._segments3d)[:, :, 2].ravel()
wire.remove()

# create data for a LineCollection
wire_x1 = np.vstack([wire_x, np.roll(wire_x, 1)])
wire_y1 = np.vstack([wire_y, np.roll(wire_y, 1)])
wire_z1 = np.vstack([wire_z, np.roll(wire_z, 1)])
to_delete = np.arange(0, nx*ny, ny)
wire_x1 = np.delete(wire_x1, to_delete, axis=1)
wire_y1 = np.delete(wire_y1, to_delete, axis=1)
wire_z1 = np.delete(wire_z1, to_delete, axis=1)
scalars = np.delete(wire_z, to_delete)

segs = [list(zip(xl, yl, zl)) for xl, yl, zl in \
                 zip(wire_x1.T, wire_y1.T, wire_z1.T)]

# Plots the wireframe by a  a line3DCollection
my_wire = art3d.Line3DCollection(segs, cmap="hsv")
my_wire.set_array(scalars)
ax.add_collection(my_wire)

plt.colorbar(my_wire)
plt.show()

# ukazi.m:226 -- Note: Not implemented. Same chart as previous section.
B = np.double(A[:, :, 1])
X, Y = np.meshgrid(np.arange(100), np.arange(100))
Z = abs(np.fft.fft2(B - np.mean(np.ravel(B)))[0:100, 0:100])
colors = cm.Blues(Z)
rcount, ccount, _ = colors.shape

fig = plt.figure()
fig.set_size_inches(7, 7)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rcount=rcount, ccount=ccount, facecolors=colors, shade=False)
surf.set_facecolor((0, 0, 0, 0))
plt.title('G ravnina')
plt.show()

# ukazi.m:228 -- Note: Not implemented. Same chart as previous section.
B = np.double(A[:, :, 2])
X, Y = np.meshgrid(np.arange(100), np.arange(100))
Z = abs(np.fft.fft2(B - np.mean(np.ravel(B)))[0:100, 0:100])
colors = cm.Blues(Z)
rcount, ccount, _ = colors.shape

fig = plt.figure()
fig.set_size_inches(7, 7)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rcount=rcount, ccount=ccount, facecolors=colors, shade=False)
surf.set_facecolor((0, 0, 0, 0))
plt.title('B ravnina')
plt.show()

# ukazi.m:231 -- Note: Not implemented. Same chart as previous section.
# prevzorčena slika..................................
pvz = 4
B = np.double(A[0::pvz, 0::pvz, 1])
X, Y = np.meshgrid(np.arange(100), np.arange(100))
Z = abs(np.fft.fft2(B - np.mean(np.ravel(B)))[0:100, 0:100])
colors = cm.Blues(Z)
rcount, ccount, _ = colors.shape

fig = plt.figure()
fig.set_size_inches(7, 7)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rcount=rcount, ccount=ccount, facecolors=colors, shade=False)
surf.set_facecolor((0, 0, 0, 0))
plt.title('R ravnina, po podvzorenju s faktorjem {0}'.format(pvz))
plt.show()

# ukazi.m:235
B = np.double(A[0::pvz, 0::pvz, 2])
X, Y = np.meshgrid(np.arange(100), np.arange(100))
Z = abs(np.fft.fft2(B - np.mean(np.ravel(B)))[0:100, 0:100])
colors = cm.Blues(Z)
rcount, ccount, _ = colors.shape

fig = plt.figure()
fig.set_size_inches(7, 7)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rcount=rcount, ccount=ccount, facecolors=colors, shade=False)
surf.set_facecolor((0, 0, 0, 0))
plt.title('G ravnina, po podvzorenju s faktorjem {0}'.format(pvz))
plt.show()

# ukazi.m:237 -- Note: Not implemented. Same chart as previous section.
B = np.double(A[0::pvz, 0::pvz, 3])
X, Y = np.meshgrid(np.arange(100), np.arange(100))
Z = abs(np.fft.fft2(B - np.mean(np.ravel(B)))[0:100, 0:100])
colors = cm.Blues(Z)
rcount, ccount, _ = colors.shape

fig = plt.figure()
fig.set_size_inches(7, 7)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z, rcount=rcount, ccount=ccount, facecolors=colors, shade=False)
surf.set_facecolor((0, 0, 0, 0))
plt.title('B ravnina, po podvzorenju s faktorjem {0}'.format(pvz))
plt.show()

# -----------------------------------------------------------------------------------------
# ukazi.m:241
# podvzorčenje slik in operator povprečenja

A = pylab.array(Image.open(Path('./1-Vzorcenje/Moire.jpg')))
plt.figure().set_size_inches(10, 10)
plt.imshow(A)
plt.title('originalna slika')
plt.axis('off')

pvz = 3  # faktor podvzorčenja
plt.figure()
plt.imshow(A[0::pvz, 0::pvz, :])
plt.title('podvzorčena slika: faktor podvzorčenja {0}'.format(pvz))

# ukazi.m:253 -- Note: Mislim, da je ok, čeprav rezultat različen kot v Matlab (poglej spodaj).
# operator povprečenja (verzija 1)
# Zglajena slika v Matlabu ima pri meni moder odtenek. Nisem prepričan, če tako mora biti (tukaj odtenka ni).
D = 3  # premer lokalne okolice piksla, na kateri se izračuna povprečna vrednost
B = np.ndarray((A.shape[0] - D + 1, A.shape[1] - D + 1, D))
for r in np.arange(0, A.shape[0] - D).reshape(-1):
    for c in np.arange(0, A.shape[1] - D).reshape(-1):
        C = A[r + np.arange(0, D - 1), c + np.arange(0, D - 1), 0]
        B[r, c, 0] = np.mean(np.ravel(C))
        C = A[r + np.arange(0, D - 1), c + np.arange(0, D - 1), 1]
        B[r, c, 1] = np.mean(np.ravel(C))
        C = A[r + np.arange(0, D - 1), c + np.arange(0, D - 1), 2]
        B[r, c, 2] = np.mean(np.ravel(C))

plt.figure()
plt.imshow(np.uint8(B))
plt.title('zglajena slika')

# ukazi.m:270 -- Note: Naslovi se prekrivajo.
# operator povpreenja (verzija 2)
# isti operator povprečenja kot zgoraj, implementiran nekoliko drugače (veliko hitreja izvedba)
D = 3
B = np.ndarray((A.shape[0] + D - 1, A.shape[1] + D - 1, D))
B[:, :, 0] = scipy.signal.convolve2d(np.double(A[:, :, 0]), np.ones((D, D), np.float) / D ** 2)
B[:, :, 1] = scipy.signal.convolve2d(np.double(A[:, :, 1]), np.ones((D, D), np.float) / D ** 2)
B[:, :, 2] = scipy.signal.convolve2d(np.double(A[:, :, 2]), np.ones((D, D), np.float) / D ** 2)
B = np.uint8(B)
plt.figure()
plt.imshow(B)
plt.title('zglajena slika')

# prikaz
pvz = 3  # faktor podvzorčenja
plt.figure()
plt.subplot(1, 2, 1)
plt.imshow(A[0::pvz, 0::pvz, :])
plt.title('podvzorčena slika: faktor podvzorčenja {0}'.format(pvz))
plt.subplot(1, 2, 2)
plt.imshow(B[1::pvz, 1::pvz, :])
plt.title('zglajena podvzorena slika: faktor podvzorenja {0}'.format(pvz))


from sympy import *
from rodrigues_R_utils import *

ksi_0, eta_0, c = symbols('ksi_0 eta_0 c');
px, py, pz = symbols('px py pz')
sx, sy, sz = symbols('sx sy sz')
tie_px, tie_py, tie_pz = symbols('tie_px tie_py tie_pz');
ksi_kp, eta_kp = symbols('ksi_kp eta_kp')

position_symbols = [px, py, pz]
rodrigues_symbols = [sx, sy, sz]
tie_point_symbols = [tie_px, tie_py, tie_pz]
all_symbols = position_symbols + rodrigues_symbols + tie_point_symbols

RT_wc = matrix44FromRodrigues(px, py, pz, sx, sy, sz)
r=RT_wc[:-1,:-1]
t=Matrix([px, py, pz]).vec()

denom=r[0,2]*(tie_px-t[0]) + r[1,2]*(tie_py-t[1]) + r[2,2]*(tie_pz-t[2])
ksi=ksi_0 - c * ( r[0,0]*(tie_px-t[0]) + r[1,0]*(tie_py-t[1]) + r[2,0]*(tie_pz-t[2]) ) /denom
eta=eta_0 - c * ( r[0,1]*(tie_px-t[0]) + r[1,1]*(tie_py-t[1]) + r[2,1]*(tie_pz-t[2]) ) /denom

ksi_delta = ksi_kp - ksi;
eta_delta = eta_kp - eta;

obs_eq = Matrix([ksi_delta, eta_delta]).vec()
obs_eq_jacobian = obs_eq.jacobian(all_symbols)

print(obs_eq)
print(obs_eq_jacobian)

with open("metric_camera_colinearity_rodrigues_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void observation_equation_metric_camera_colinearity_rodrigues_wc(Eigen::Matrix<double, 2, 1> &delta, double ksi_0, double eta_0, double c, double px, double py, double pz, double sx, double sy, double sz, double tie_px, double tie_py, double tie_pz, double ksi_kp, double eta_kp)\n")
    f_cpp.write("{")
    f_cpp.write("delta.coeffRef(0,0) = %s;\n"%(ccode(obs_eq[0,0])))
    f_cpp.write("delta.coeffRef(1,0) = %s;\n"%(ccode(obs_eq[1,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void observation_equation_metric_camera_colinearity_rodrigues_wc_jacobian(Eigen::Matrix<double, 2, 9, Eigen::RowMajor> &j, double ksi_0, double eta_0, double c, double px, double py, double pz, double sx, double sy, double sz, double tie_px, double tie_py, double tie_pz, double ksi_kp, double eta_kp)\n")
    f_cpp.write("{")
    for i in range (2):
        for j in range (9):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(obs_eq_jacobian[i,j])))
    f_cpp.write("}")


import math
import numpy as np
#-------------------------------------------------------------------------
'''
    Problem 2: User-based recommender systems
    In this problem, you will implement a version of the recommender system using user-based method.
    You could test the correctness of your code by typing `nosetests test2.py` in the terminal.
'''

#--------------------------
def cosine_similarity(RA, RB):
    '''
        compute the cosine similarity between user A and user B.
        The similarity values between users are measured by observing all the items which have been rated by BOTH users.
        If an item is only rated by one user, the item will not be involved in the similarity computation.
        You need to first remove all the items that are not rated by both users from RA and RB.
        If the two users don't share any item in their ratings, return 0. as the similarity.
        Then the cosine similarity is < RA, RB> / (|RA|* |RB|).
        Here <RA, RB> denotes the dot product of the two vectors (see here https://en.wikipedia.org/wiki/Dot_product).
        |RA| denotes the L-2 norm of the vector RA (see here for example: http://mathworld.wolfram.com/L2-Norm.html).
        For more details, see here https://en.wikipedia.org/wiki/Cosine_similarity.
        Input:
            RA: the ratings of user A, a float python vector of length m (the number of movies).
                If the rating is unknown, the number is 0. For example the vector can be like [0., 0., 2.0, 3.0, 0., 5.0]
            RB: the ratings of user B, a float python vector
                If the rating is unknown, the number is 0. For example the vector can be like [0., 0., 2.0, 3.0, 0., 5.0]
        Output:
            S: the cosine similarity between users A and B, a float scalar value between -1 and 1.
        Hint: you could use math.sqrt() to compute the square root of a number
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    # assuming cosine(RA, RB) = P/(sqrt(NA)*sqrt(NB))

    # loop through two lists

        # if both users rated the item

    # if the two user share no item in their ratings

        # compute cosine similarity on the shared items

    #########################################
    return S

#--------------------------
def find_users(R, i):
    '''
        find the all users who have rated the i-th movie.
        Input:
            R: the rating matrix, a float numpy matrix of shape m by n. Here m is the number of movies, n is the number of users.
                If a rating is unknown, the number is 0.
            i: the index of the i-th movie, an integer python scalar (Note: the index starts from 0)
        Output:
            idx: the indices of the users, a python list of integer values
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return idx

#--------------------------
def user_similarity(R, j, idx):
    '''
        compute the cosine similarity between a collection of users in idx list and the j-th user.
        Input:
            R: the rating matrix, a float numpy matrix of shape m by n. Here m is the number of movies, n is the number of users.
                If a rating is unknown, the number is 0.
            j: the index of the j-th user, an integer python scalar (Note: the index starts from 0)
            idx: a list of user indices, a python list of integer values
        Output:
            sim: the similarity between any user in idx list and user j, a python list of float values. It has the same length as idx.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return sim

#--------------------------
def user_based_prediction(R, i_movie, j_user, K=5):
    '''
        Compute a prediction of the rating of the j-th user on the i-th movie using user-based approach.
        First we take all the users who have rated the i-th movie, and compute their similarities to the target user j.
        If there is no user who has rated the i-th movie, predict 3.0 as the default rating.
        From these users, we pick top K similar users.
        If there are less than K users who has rated the i-th movie, use all these users.
        We weight the user's ratings on i-th movie by the similarity between that user and the target user.
        Finally, we rescale the prediction by the sum of similarities to get a reasonable value for the predicted rating.
        Input:
            R: the rating matrix, a float numpy matrix of shape m by n. Here m is the number of movies, n is the number of users.
                If the rating is unknown, the number is 0.
            i_movie: the index of the i-th movie, an integer python scalar
            j_user: the index of the j-th user, an integer python scalar
            K: the number of similar users to compute the weighted average rating.
        Output:
            p: the predicted rating of user j on movie i, a float scalar value between 1. and 5.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    # find all other users who have rated movie i.

    # if there are less than K users who have rated the movie, change K to the number of users

    # compute the similarity between all of these users with user j

    # compute the weighted average of the top K similar users to user j

    #########################################
    return p

#--------------------------
def compute_RMSE(ratings_pred, ratings_real):
    '''
        Compute the root of mean square error of the rating prediction.
        Input:
            ratings_pred: predicted ratings, a float python list
            ratings_real: real ratings, a float python list
        Output:
            RMSE: the root of mean squared error of the predicted rating, a float scalar.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return RMSE

#--------------------------
def load_rating_matrix(filename = 'movielens_train.csv'):
    '''
        Load the rating matrix from a CSV file.  In the CSV file, each line represents (user id, movie id, rating).
        Note the ids start from 1 in this dataset.
        Input:
            filename: the file name of a CSV file, a string
        Output:
            R: the rating matrix, a float numpy array of shape m by n. Here m is the number of movies, n is the number of users.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return R

#--------------------------
def load_test_data(filename = 'movielens_test.csv'):
    '''
        Load the test data from a CSV file.  In the CSV file, each line represents (user id, movie id, rating).
        Note the ids in the CSV file start from 1. But the indices in u_ids and m_ids start from 0.
        Input:
            filename: the file name of a CSV file, a string
        Output:
            m_ids: the list of movie ids, an integer python list of length n. Here n is the number of lines in the test file. (Note indice should start from 0)
            u_ids: the list of user ids, an integer python list of length n.
            ratings: the list of ratings, a float python list of length n.
    '''
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    return m_ids, u_ids, ratings

#--------------------------
def movielens_user_based(train_file='movielens_train.csv', test_file ='movielens_test.csv', K = 5):
    '''
        Compute movie ratings in movielens dataset. Based upon the training ratings, predict all values in test pairs (movie-user pair).
        In the training file, each line represents (user id, movie id, rating).
        Note the ids start from 1 in this dataset.
        Input:
            train_file: the train file of the dataset, a string.
            test_file: the test file of the dataset, a string.
            K: the number of similar users to compute the weighted average rating.
        Output:
            RMSE: the root of mean squared error of the predicted rating, a float scalar.
    Note: this function may take 1-5 minutes to run.
    '''

    # load training set
    R = load_rating_matrix(train_file)

    # load test set
    m_ids, u_ids,ratings_real = load_test_data(test_file)

    # predict on test set
    #########################################
    ## INSERT YOUR CODE HERE

    #########################################
    # compute RMSE
    RMSE = compute_RMSE(ratings_pred,ratings_real)
    return  RMSE


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

# https://www.mathportal.org/calculators/statistics-calculator/standard-deviation-calculator.php

import numpy as np

def print_this_matrix(given_matrix):
  for i in range(len(given_matrix)):
   for j in range(len(given_matrix[i])):
    print (given_matrix[i][j]," ",end="")
   print()
  print("\n")

arr_extreme = np.array([[ 4,   5,  6,   7],
                        [ 3,  -1, -2,  -3],
                        [ 8,   9,  10,  11],
                        [12,   13, 14,  15]])
print("np.ptp() -- difference between max and min")
print("np.ptp(arr_extreme)                     --->",np.ptp(arr_extreme))
print("np.ptp(arr_extreme, axis=0)--vertical   --->",np.ptp(arr_extreme, axis=0))
print("np.ptp(arr_extreme, axis=1)--horizontal --->",np.ptp(arr_extreme, axis=1))

print("np.percentile() -- percentitle of sum of given range of values")
print("np.percentile(arr_extreme)                     --->",np.percentile(arr_extreme,10))        # 10% of the total values
print("np.percentile(arr_extreme, axis=0)--vertical   --->",np.percentile(arr_extreme,10,axis=0)) # 10% of the total values
print("np.percentile(arr_extreme, axis=1)--horizontal --->",np.percentile(arr_extreme,10,axis=1)) # 10% of the total values

print("np.mean() -- mean = 50% percentile")
print("np.mean(arr_extreme)                     --->",np.mean(arr_extreme))
print("np.mean(arr_extreme, axis=0)--vertical   --->",np.mean(arr_extreme, axis=0))
print("np.mean(arr_extreme, axis=1)--horizontal --->",np.mean(arr_extreme, axis=1))

print("np.median() -- middle number or avg of the middle 2 numbers")
print("np.median(arr_extreme)                     --->",np.median(arr_extreme))
print("np.median(arr_extreme, axis=0)--vertical   --->",np.median(arr_extreme, axis=0))
print("np.median(arr_extreme, axis=1)--horizontal --->",np.median(arr_extreme, axis=1))

print("np.average(arr_extreme)                     --->",np.average(arr_extreme))
print("np.std(arr_extreme)    standard deviation   --->",np.std(arr_extreme))
print("np.var(arr_extreme)    variance             --->",np.var(arr_extreme))
print("\n")

arr_extreme = np.array([[ 4,   5,  6,   7],
                        [ 3,  -1, -2,  -3],
                        [ 8,   9,  10,  11],
                        [12,   13, 14,  15]])
print_this_matrix(arr_extreme)
print("np.amin(arr_extreme)                    --->",np.amin(arr_extreme))
print("np.amin(arr_extreme, axis=0)--vertical  --->",np.amin(arr_extreme, axis=0))
print("np.amin(arr_extreme, axis=1)--horizontal--->",np.amin(arr_extreme, axis=1))
print("just index of sorted element")
arr_extreme.flatten()
print("np.argmin(arr_extreme, axis=1)--horizontal--->",np.argmin(arr_extreme, axis=1))

from scipy import stats

arr_extreme = np.array([[ 4,   5,  6,   7],
                        [ 3,  -1, -2,  -3],
                        [ 8,   9,  10,  11],
                        [12,   13, 14,  15]])

stats.describe(arr_extreme)


import numpy as np

def riemann_sum(f, a, b, N: int, method='midpoint'):
    '''
    Compute the Riemann sum of f(x) over the interval [a,b].

    Parameters
    ----------
    f : function
        Vectorized function of one variable
    a , b : numbers
        Endpoints of the interval [a,b]
    N : integer
        Number of subintervals of equal length in the partition of [a,b]
    method : string
        Determines the kind of Riemann sum:
        right : Riemann sum using right endpoints
        left : Riemann sum using left endpoints
        midpoint (default) : Riemann sum using midpoints

    Returns
    -------
    float
        Approximation of the integral given by the Riemann sum.
    '''
    dx = (b - a)/N
    x = np.linspace(a, b, N+1)

    # left riemann sum
    if method == 'left':
        x_left = x[:-1]
        return np.sum(f(x_left)*dx)

    # right reimann sum
    elif method == 'right':
        x_right = x[1:]
        return np.sum(f(x_right)*dx)

    # midpoint reimann sum
    elif method == 'midpoint':
        x_mid = (x[:-1] + x[1:])/2
        return np.sum(f(x_mid)*dx)
    else:
        raise ValueError("Method must be 'left', 'right' or 'midpoint'.")

# example problems

# 1
f = np.sin
r_sum = riemann_sum(f, 0, np.pi/2, 100)
right_r_sum = riemann_sum(f, 0, np.pi/2, 100, 'right')
left_r_sum = riemann_sum(f, 0, np.pi/2, 100, 'left')
print(f"""Sin(x) in the interval [0,pi/2] partition with 100 sub-intervals
Left Riemann Sum: {left_r_sum}
Midpoint Riemann Sum: {r_sum}
Right Riemann Sum: {right_r_sum}""")

# 2
f = lambda x : 1 / (1 + x**2)
r_sum = riemann_sum(f, 0, 5, 10)
right_r_sum = riemann_sum(f, 0, 5, 10, 'right')
left_r_sum = riemann_sum(f, 0, 5, 10, 'left')
print(f"""
1/(1+x^2) in the interval [0,5] partition with 10 sub-intervals
Left Riemann Sum: {left_r_sum}
Midpoint Riemann Sum: {r_sum}
Right Riemann Sum: {right_r_sum}""")


import numpy as np
import math

def poisson_lambda_mle(d):
    """
    Computes the Maximum Likelihood Estimate for a given 1D training
    dataset from a Poisson distribution.

    """
    return sum(d) / len(d)

def likelihood_poisson(x, lam):
    """
    Computes the class-conditional probability for an univariate
    Poisson distribution

    """
    if x // 1 != x:
        likelihood = 0
    else:
        likelihood = math.e**(-lam) * lam**(x) / math.factorial(x)
    return likelihood

if __name__ == "__main__":

    # Plot Probability Density Function
    from matplotlib import pyplot as plt

    training_data = [0, 1, 1, 3, 1, 0, 1, 2, 1, 2, 2, 1, 2, 0, 1, 4]
    mle_poiss =  poisson_lambda_mle(training_data)
    true_param = 1.0

    x_range = np.arange(0, 5, 0.1)
    y_true = [likelihood_poisson(x, true_param) for x in x_range]
    y_mle = [likelihood_poisson(x, mle_poiss) for x in x_range]

    plt.figure(figsize=(10,8))
    plt.plot(x_range, y_true, lw=2, alpha=0.5, linestyle='--', label='true parameter ($\lambda={}$)'.format(true_param))
    plt.plot(x_range, y_mle, lw=2, alpha=0.5, label='MLE ($\lambda={}$)'.format(mle_poiss))
    plt.title('Poisson probability density function for the true and estimated parameters')
    plt.ylabel('p(x|theta)')
    plt.xlim([-1,5])
    plt.xlabel('random variable x')
    plt.legend()

    plt.show()


import numpy as np
import math
from math import sqrt
#float_percision = '{:.4f}'.format
#np.set_printoptions(formatter={'float_kind':float_percision})

def decomp_cholesky(A):

    n = len(A)
    L = np.zeros((n, n))

    for i in range(n):

        for j in range(i+1):

            L[i][j] = A[i][j]

            for k in range(j):

                L[i][j] -= L[i][k]*L[j][k]

            if(i == j):

                L[i][j] = sqrt(L[j][j])

            else:

                L[i][j] /= L[j][j]

    return L

def solve_lower(L, b):

    n = len(L)
    y = np.copy(b)

    y[0] = b[0] / L[0][0]

    for i in range(1, n):

        for j in range(i):

            y[i] -= L[i][j]*y[j]

        y[i] /= L[i][i]

    return y

def solve_upper(U, y):

    n = len(U)
    x = np.copy(y)

    for i in range(n-1, -1, -1):

        for j in range(i+1, n):

            x[i] -= U[j][i]*x[j]

        x[i] /= U[i][i]

    return x

def solve_cholesky(L,b):

    y = solve_lower(L,b)
    x = solve_upper(L,y)

    return x

if __name__ == '__main__':

	N = 1000
	print('N = ', N)

	#Filling N*N array to initialize it
	A1 = np.zeros((N,N), float)
	A2 = np.zeros((N,N), float)

	b1 = np.zeros(N, float)
	b2 = np.ones(N, float)

	#Fill arrays with the correspondant values
	np.fill_diagonal(A1, 6)
	np.fill_diagonal(A1[1:], -4)
	np.fill_diagonal(A1[:, 1:], -4)
	np.fill_diagonal(A1[2:], 1)
	np.fill_diagonal(A1[:, 2:], 1)

	np.fill_diagonal(A2, 7)
	np.fill_diagonal(A2[1:], -4)
	np.fill_diagonal(A2[:, 1:], -4)
	np.fill_diagonal(A2[2:], 1)
	np.fill_diagonal(A2[:, 2:], 1)

	b1[0] = 3
	b1[1] = -1
	b1[-2] = -1
	b1[-1] = 3

	b2[0] = 4
	b2[1] = 0
	b2[-2] = 0
	b2[-1] = 4

	L = decomp_cholesky(A1)
	x = solve_cholesky(L, b1)

	print('A1 x = b1 \n Ten median x are:')

	ml = len(x) // 2 - 5
	mu = len(x) // 2 + 5

	print(x[ml : mu])

	L = decomp_cholesky(A2)
	x = solve_cholesky(L, b2)

	print('A2 x = b2 \n Ten median x are:')

	ml = len(x) // 2 - 5
	mu = len(x) // 2 + 5

	print(x[ml : mu])


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import numpy as np
import csv
from numpy import cov
from numpy.linalg import eig
import matplotlib.pyplot as plt

def PCA(filename = "",topk = 0, graph=True):

	if len(filename) == 0:
		print("Error: File not provided!")
		return

	data = []
	label = []
	with open(filename) as datafile:
		reader = csv.reader(datafile,delimiter=",")
		next(reader)
		for row in reader:

			# data.append(list(map(float,row[2:])))
			float_row = []
			for value in row[2:]:
				try:
					value = float(value)
					float_row.append(value)

				except ValueError:
					float_row.append(0.0)
			data.append(float_row)
			label.append(row[1])

	data_mat = np.asarray(data)

	totalDims = data_mat.shape[-1]

	if topk > totalDims or topk < 0:
		print("Invalid value for topk!")
		return

	centered_mat = data_mat - np.mean(data_mat.T, axis=1)

	values, vectors = eig(cov(centered_mat.T))
	indices = np.argpartition(values, -topk)[-topk:]

	max_variance_vectors = []
	for index in indices:
		max_variance_vectors.append(vectors[index])

	max_variance_vectors = np.asarray(max_variance_vectors)
	projected_mat = max_variance_vectors.dot(centered_mat.T)

	if graph:
		fig = plt.figure()
		fig.suptitle('PCA on '+filename, fontsize=14, fontweight='bold')
		ax = fig.add_subplot(111)
		ax.set_xlabel("PC1")
		ax.set_ylabel("PC2")
		ax.scatter(projected_mat[0],projected_mat[1])
		# for i, txt in enumerate(label):
		# 	ax.annotate(txt, (projected_mat[0], projected_mat[1]))
		plt.show()

	return projected_mat.T


import numpy as np
from scipy.stats import multivariate_normal
import pdb

class MultivariateGaussianData:
    """ Class for generating data from a multivariate Gaussian distibution
    (Z, X) ~ N(mu, Sigma), where Z is considered the latent variable and X the observation,
    such that Z ~ N(0, I) and X|Z=z ~ N(Wz + b, Sigma_x)
    ------------------
    Parameters:
              dim_x : dimension of X
              dim_z : dimension of Z
    ------------------
    Methods:
            create_dataset : generate n_samples observations X ~ N(mu_x, Sigma_x)
            compute_posterior : return p(Z|X=x), mean and covariance of Z|X=x
            compute_log_likelihood : return mean(log(p(x))) across the given dataset
    """

    def __init__(self, dim_x, dim_z):
        np.random.seed(0)
        self.n_x = dim_x
        self.n_z = dim_z

        # z ~ N(0, I):
        self.sigma_z = np.eye(dim_z)
        self.mu_z = np.zeros(dim_z)

        # X|Z=z ~ N(Wz+b, Sigma_x_z)
        self.W = np.random.normal(0, 1, size=(dim_x, dim_z))
        self.b = np.random.normal(0, 1, dim_x)
        aux_sigma = np.random.normal(0, 1, size=(dim_x, dim_x))
        self.sigma_x_z = np.dot(aux_sigma, aux_sigma.T) + 0.5 * np.eye(dim_x)

        # Cholesky factor of the covariance of the observed variable X (needed for sampling)
        self.chol_sigma_x_z = np.linalg.cholesky(self.sigma_x_z)

        # X + N(b, W*W^T + Sigma_x, z)
        self.sigma_x = np.dot(self.W, self.W.T) + self.sigma_x_z

    def create_dataset(self, n_samples):
        """ Sample n_samples from X ~ N(mu_x, Sigma_x), generated by first sampling from Z
        """
        z = np.random.normal(0, 1, size=(n_samples, self.n_z))
        eps = np.random.normal(0, 1, size=(n_samples, self.n_x))
        data = np.dot(z, self.W.T) + self.b + np.dot(eps, self.chol_sigma_x_z)
        return data

    def compute_posterior(self, x, z):
        """ Compute the posterior and its parameters: p(z|x), mu_z_x, sigma_z_x
        """
        solver = np.linalg.solve(self.sigma_x, x - self.b)
        mu_z_x = self.mu_z + np.dot(self.W.T, solver)
        sigma_z_x = self.sigma_z - np.dot(self.W.T, np.dot(np.linalg.inv(self.sigma_x), self.W))
        pdf = multivariate_normal.pdf(z, mu_z_x, sigma_z_x)
        return pdf, mu_z_x, sigma_z_x

    def compute_log_likelihood(self, x):
        """ True log likelihood of the data
        """
        p_x = multivariate_normal.pdf(x, self.b, self.sigma_x)
        return np.mean(np.log(p_x))

def load(n_x, n_z, n_samples_train, n_samples_test):
    data = {}
    multivarGaussData = MultivariateGaussianData(n_x, n_z)
    generatedX = multivarGaussData.create_dataset(n_samples_train + n_samples_test)
    data["gaussian_class"] = multivarGaussData
    data["train_set_x"] = generatedX[:n_samples_train]
    data["test_set_x"] = generatedX[n_samples_train:]
    data["n_samples_train"] = n_samples_train
    data["n_samples_test"] = n_samples_test
    data["input_size"] = n_x
    data["binary"] = 0
    return data

if __name__=='__main__':
    opts = {"n_x": 10,
            "n_z": 2,
            "n_train": 500,
            "n_test" : 50,
            "n_samples": 5,
            "n_samples_is": 10,
            "n_epochs": 100,
            "period" : 10,
            "alpha": 0.9,
            "learning_rate": 0.0001}

    data = load(opts["n_x"], opts["n_z"], opts["n_train"], opts["n_test"])
    # pdb.set_trace()


import numpy as np

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

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

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

def tanh_prime(z):
    return 1 - tanh(z)**2

def relu(z):
    def comp(x):
        if (x > 0):
            return x
        else:
            return 0
    return np.array([comp(i) for i in z])

def relu_prime(z):
    def comp(x):
        if (x <= 0):
             return 0
        elif (x > 0):
            return 1

    return np.array([comp(i) for i in z])

def leaky_relu(z):
    def comp(x):
        if (x < 0):
            return 0.1*x
        elif (x >= 0):
            return x
    return np.array([comp(i) for i in z])

def leaky_relu_prime(z):
    def comp(x):
        if (x < 0):
            return 0.1
        elif (x >= 0):
            return 1
    return np.array([comp(i) for i in z])

def elu(z):
    alpha = 1
    def comp(x):
        if (x > 0):
            return x
        elif (x <= 0):
            return alpha*(np.exp(x) - 1)
    return np.array([comp(i) for i in z])

def elu_prime(z):
    alpha = 1
    def comp(x):
        if (x > 0):
            return 1
        elif (x <= 0):
            return alpha*np.exp(x)
    return np.array([comp(i) for i in z])

def softmax(z):
    return np.exp(z)/sum(np.exp(z))

def softmax_prime(z):
    #this is the diagonal of the jacobian.
    return softmax(z)*(1-softmax(z))

def softmax_input_change(z):
    dz_da = np.zeros((z.shape[0], z.shape[0]))
    for i in range(len(z)):
        tmp = []
        if (z[i] == 0):
            for j in z:
                if (j == 0):
                    tmp.append(0)
                else:
                    tmp.append(-1)
        else:
            for j in range(len(z)):
                if (i == j):
                    tmp.append(1)
                else:
                    tmp.append(0)

        dz_da[:,i] += np.array(tmp)

    return dz_da.diagonal()

def func_dict(order):
    if (order == 0):
        dict = {
            "sigmoid": sigmoid,
            "relu": relu,
            "l_relu": leaky_relu,
            "tanh": tanh,
            "elu": elu,
        }
    elif (order == 1):
        dict = {
            "sigmoid": sigmoid_prime,
            "relu": relu_prime,
            "l_relu": leaky_relu_prime,
            "tanh": tanh_prime,
            "elu": elu_prime,
            "softmax": softmax_prime,
        }
    return dict


# -*- coding: utf-8 -*-
"""
Created on Wed Sep  5 22:11:24 2018

@author: chen jin

Non-Linear Least-Squares Fitting: solve a curve fitting problem using robust
loss function to take care of outliers in the data. Define the model function
as y = a + b * exp(c * x), where t is a predictor variable, y is an observation
and a, b, c are parameters to estimate.
Put all coefficients in beta, this could be write as:
    y = beta[0] + beta[1] * exp(beta[2] * x)

Input:
    target function: y = a + b * np.exp(x * c)
    x_train linspace generated
    y_train generated with noise and outliers
    par: parameters (a, b, c) of target function
    loss_fun: loss or residual function (simple f(x) - y used here)
    beta: initial guess of parameters (a, b, c)

Output:
    res_lsq: standard least-squares solution with beta = [*res_lsq.x]
    res_soft_l1: robust soft_11 solution with beta = [*res_soft_l1.x]
    res_log: robust cauchy solution with beta = [*res_log.x]
"""

import numpy as np
from scipy.optimize import least_squares
import matplotlib.pyplot as plt

# First, define the function which generates the data with noise and outliers,
# define the model parameters with default setting in par

def gen_data(x, par = [0.5, 2, -1], noise=0, n_outliers=0, random_state=0):

    a, b, c = par[0], par[1], par[2]
    y = a + b * np.exp(x * c)

    rnd = np.random.RandomState(random_state)
    error = noise * rnd.randn(x.size)
    outliers = rnd.randint(0, x.size, n_outliers)
    error[outliers] *= 10

    return y + error

# generate data
x_train = np.linspace(0, 10, 15)
par = [0.5, 2, -1]
y_train = gen_data(x_train, par, noise=0.1, n_outliers=3)

# Define function for computing Loss (residuals)
def loss_fun(beta, x, y):
    return beta[0] + beta[1] * np.exp(beta[2] * x) - y

# initial estimate of parameters
beta = np.array([1.0, 1.0, 0.0])

# Compute a standard least-squares solution
res_lsq = least_squares(loss_fun, beta, args=(x_train, y_train))

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

res_soft_l1 = least_squares(loss_fun, beta, loss='soft_l1', f_scale=0.1,
                            args=(x_train, y_train))
res_log = least_squares(loss_fun, beta, loss='cauchy', f_scale=0.1,
                        args=(x_train, y_train))

# plot all curves
x_test = np.linspace(0, 10, 15 * 10)
y_true = gen_data(x_test, par)
y_lsq = gen_data(x_test, [*res_lsq.x])
y_soft_l1 = gen_data(x_test, [*res_soft_l1.x])
y_log = gen_data(x_test, [*res_log.x])

plt.plot(x_train, y_train, 'o')
plt.plot(x_test, y_true, 'k', linewidth=2, label='true')
plt.plot(x_test, y_lsq, label='linear loss')
plt.plot(x_test, y_soft_l1, label='soft_l1 loss')
plt.plot(x_test, y_log, label='cauchy loss')
plt.xlabel("t")
plt.ylabel("y")
plt.legend()
plt.show()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Realisation of Multivariate Linear Regression powered by pure Numpy.
Inspired by Machine Learning class from Andrew Ng at Coursera.
@see https://www.coursera.org/learn/machine-learning

FAQ:
- Linear Regression model looks like: y = theta0 + theta1*x1 + theta2*x2 ... thetan*xn
- We try to find theta parameters
- We use Gradient Descent for that
- It is very handful do np.seterr(all='raise') before using this module, to stop on any errors and avoid further
calculations that already wrong
"""
import numpy as np

def compute_cost(x: np.ndarray, y: np.ndarray, theta: np.ndarray) -> float:
    """Calculate and return cost function value (float scalar), based on given training set x, expected outcomes y and
    theta

    If x.shape = (m, n), then y.shape must be (m, 1) and theta.shape must be (1, n)

    :param x: set of features
    :param y: set of training examples
    :param theta: parameters of linear regression function
    """
    m = len(y)  # number of training examples
    return np.ndarray.item(sum(np.square(x @ theta - y)) / (2 * m))

def gradient_descent(x: np.ndarray, y: np.ndarray, theta: np.ndarray, alpha: float, num_iters: int) \
        -> (np.ndarray, np.ndarray):
    """Straitforward (unoptimized) version of Gradient Descent. Applies num_iters iterations with learning rate alpha
    and returns tuple (theta, J_history) with finded theta and history of values of the cost function J_history

    If x.shape = (m, n), then y.shape must be (m, 1) and theta.shape must be (1, n)

    :param x: set of features
    :param y: set of training examples
    :param theta: parameters of linear regression function
    :param alpha: learning rate alpha (size of the one step of gradient descent)
    :param num_iters: number of iterations for gradient descent
    :return: a tuple (theta, J_historu), where theta is sought-for parameter of Linear Regression Model,
        J_history - is an array of cost function values over iterations
    """
    m = len(y)  # number of training examples
    n = x.shape[1]  # number of features
    j_history = np.zeros(shape=(num_iters, 1))
    for i in range(num_iters):
        for j in range(n):
            # np.multiply is the element-wise multiplication
            theta[j, 0] = theta[j, 0] - alpha * sum(np.multiply(x @ theta - y, x[:, j].reshape(m, 1))) / m
        j_history[i] = compute_cost(x, y, theta)
    return theta, j_history

def gd(x: np.ndarray, y: np.ndarray, theta: np.ndarray, alpha: float, num_iters: int) \
        -> (np.ndarray, np.ndarray):
    """Optimized version of gradient_descent function. Parameters and return is equal with gradient_descent()"""
    m = len(y)  # number of training examples
    n = x.shape[1]  # number of features
    alpha_m = alpha / m  # constant formula part, calculate outside of loops
    j_history = np.zeros(shape=(num_iters, 1))
    for i in range(num_iters):
        difference = x @ theta - y
        for j in range(n):
            # np.multiply is the element-wise multiplication
            theta[j, 0] = theta[j, 0] - alpha_m * sum(np.multiply(difference, x[:, j].reshape(m, 1)))
        j_history[i] = np.ndarray.item(sum(np.square(difference)) / (2 * m))  # use precalculated difference
    return theta, j_history


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

import numpy as np
import imageio
import matplotlib.pyplot as plt
import timeit

#%%
#<start:svd>
def compress_image(A,k):
    u,s,vt = np.linalg.svd(A, full_matrices=False)

    return (u[:,:k]*s[:k])@vt[:k]
#<end:svd>
#%%
#<start:approx_svd>
def col_sample_compress_image(A,k,eps):
    m,n = np.shape(A)

    column_norms = np.linalg.norm(A,axis=0)

    column_probs = column_norms**2
    column_probs /= np.sum(column_probs)

    s = int(k/eps**2)

    indices = np.random.choice(np.arange(n), size=s, p=column_probs)

    X = A[:,indices]

    u,s,vt = np.linalg.svd(X, full_matrices=False)

    return u[:,:k]@(u[:,:k].T@A)
#<end:approx_svd>

#%%
#<start:approx_qr>
def gaus_sample_compress_image(A,k):
    m,n = np.shape(A)

    X = A@np.random.randn(n,k)
    u,s,vt = np.linalg.svd(X, full_matrices=False)

    return u[:,:k]@(u[:,:k].T@A)
#<end:approx_qr>

#%%
A = imageio.imread('einstein.jpg')[:,:,0]
K = np.logspace(0,2.7,8,dtype='int')
for i,k in enumerate(K):
    A_k = compress_image(A,k)
    plt.imsave('img/'+str(k)+'.png', A_k[::6,::6], format='png')

    A_k_col = col_sample_compress_image(A,k,1)
    plt.imsave('img/'+str(k)+'_fast.png', A_k_col[::6,::6], format='png')

    A_k_gaus = gaus_sample_compress_image(A,k)
    plt.imsave('img/'+str(k)+'_faster.png', A_k_gaus[::6,::6], format='png')

#%%

N = 1

K = np.logspace(0,2.7,8,dtype='int')
setup = 'from __main__ import A, compress_image'
setup_col = 'from __main__ import A, col_sample_compress_image'
setup_gaus = 'from __main__ import A, gaus_sample_compress_image'

t = np.zeros(len(K))
t_col = np.zeros(len(K))
t_gaus = np.zeros(len(K))

for i,k in enumerate(K):
    t[i] = timeit.timeit('compress_image(A,'+str(k)+')', number=N, setup=setup)/N
    t_col[i] = timeit.timeit('col_sample_compress_image(A,'+str(k)+',1)', number=N, setup=setup_col)/N
    t_gaus[i] = timeit.timeit('gaus_sample_compress_image(A,'+str(k)+')', number=N, setup=setup_gaus)/N

#%%
plt.figure(figsize=(10,6))
plt.scatter(K,t,label='exact')
plt.scatter(K,t_col,label='column sampling')
plt.scatter(K,t_gaus,label='gaussian sampling')

plt.xlabel('$k$')
plt.ylabel('time (s)')
plt.legend()

plt.xscale('log')
plt.yscale('log')
plt.savefig('img/times.pdf')


"""
floor
The tool floor returns the floor of the input element-wise.
The floor of  is the largest integer  where .

import numpy

my_array = numpy.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9])
print numpy.floor(my_array)         #[ 1.  2.  3.  4.  5.  6.  7.  8.  9.]
ceil
The tool ceil returns the ceiling of the input element-wise.
The ceiling of  is the smallest integer  where .

import numpy

my_array = numpy.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9])
print numpy.ceil(my_array)          #[  2.   3.   4.   5.   6.   7.   8.   9.  10.]
rint
The rint tool rounds to the nearest integer of input element-wise.

import numpy

my_array = numpy.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9])
print numpy.rint(my_array)          #[  1.   2.   3.   4.   6.   7.   8.   9.  10.]
Task
You are given a 1-D array, . Your task is to print the ,  and  of all the elements of .

Note
In order to get the correct output format, add the line  below the numpy import.

Input Format

A single line of input containing the space separated elements of array .

Output Format

On the first line, print the  of A.
On the second line, print the  of A.
On the third line, print the  of A.

Sample Input

1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9
Sample Output

[ 1.  2.  3.  4.  5.  6.  7.  8.  9.]
[  2.   3.   4.   5.   6.   7.   8.   9.  10.]
[  1.   2.   3.   4.   6.   7.   8.   9.  10.]
"""

import numpy as np

np.set_printoptions(legacy="1.13")

arr = np.array(list(map(float, input().split())), float)
print(np.floor(arr))
print(np.ceil(arr))
print(np.rint(arr))


# IMport the necessary modules
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# In this short script, we will see how Stirling's approximation can be
# useful for approximating very large numbers. For example, computing 200! is
# very difficult because that is beyond the basic precision of a 64-bit
# computer. We can do better by converting the numbers to something called
# 'arbitrary precision', but it is often sufficient to perform approximations
# by hand.

# As a reminder, Stirling's approximation is
#
#     log(n!) = n * log(n) - n
#
# We'll show this by computing the log of the factorial for large numbers
# and show it along with the approximation.

# We'll first set upa range of n to compute.
n_array = np.arange(0, 150, 1)

# Now we'll compute the factorial and store the log.
log_factorial = []
for n in n_array:
    n_factorial = float(np.math.factorial(n))   # Converts to a float for precision
    log_factorial.append(np.log(n_factorial))

# Now we can compute our approximation.
stirlings_approx = n_array * np.log(n_array) - n_array

# Now let's plot the log factorial and our approximation together.
plt.figure()
plt.plot(n_array, log_factorial, label='factorial')
plt.plot(n_array, stirlings_approx, label='approximation')
plt.xlabel('$n$')
plt.ylabel('$n!$')
plt.legend()
plt.show()

# We see that as n becomes larger and larger, our approximation gets even
# better.


"""
  Name     : c12_10_simulate_pi.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

import scipy as sp
n=100000
x=sp.random.uniform(low=0,high=1,size=n)
y=sp.random.uniform(low=0,high=1,size=n)
dist=sp.sqrt(x**2+y**2)
in_circle=dist[dist<=1]
our_pi=len(in_circle)*4./n
print ('pi=',our_pi)
print('error (%)=', (our_pi-sp.pi)/sp.pi)


import numpy as np
import pandas as pd
import time, gc

pi = np.pi
cos = np.cos
sin = np.sin
acos = np.arccos
degrees = np.degrees
radians = np.radians

def angularDistance(row, col_names):
    '''
    Computes the angular distance (in degrees) between two points on the celestial
    sphere with a given right-ascension and declination values

    <Formula> - http://spiff.rit.edu/classes/phys373/lectures/radec/radec.html

    Parameters
    ----------
    row : pd.Dataframe - series
        Input right-ascension in (hrs) and declination in (degrees) format

    col_names: list of strings
        The names of the columns on which the function will be applied
        ### SHOULD FOLLOW THIS CONVENTION
        c1 = right-ascension_1; c2 = right-ascension_2
        c3 = declination_1; c4 = declination_2

    Returns
    -------
    y : pd.Dataframe - series
        The corresponding angular distance in degree value.
    '''
    # Unpack column names
    c1, c2, c3, c4 = col_names
    # Assert datatypes
    assert type(c1) == str and type(c2) == str and type(c3) == str and type(c4) == str, 'TypeError: input should be str'

    # Units of right-ascension is in (hours) format
    alpha1, alpha2 = radians(15*row[c1]), radians(15*row[c2])

    # Units of declination is in (degrees) format
    delta1, delta2 = radians(row[c3]), radians(row[c4])

    # Given Formula
    temp = cos(pi/2 - delta1)*cos(pi/2 - delta2) + sin(pi/2 - delta1)*sin(pi/2 - delta2)*cos(alpha1 - alpha2)

    return np.degrees(acos(temp))

def genRefCatalogue(CATALOGUE, mag_limit, no_iter = -1,  gen_csv = True):
    '''
    Generates the reference star catalogue for Geometric Voting Algorithm where each row
    of the table has two unique stars and the corresponding angular distance in degrees,
    for all pairs of stars with a specified upper magnitude limit

    Parameters
    ----------
    CATALOGUE : pd.Dataframe
        The 'master' star catalogue on which the function works

    mag_limit : floating-point number
        The upper magnitude limit of stars that are required in the reference catalogue

    no_iter : integer, default = -1
        Specifies the number of iterations, thereby allowing it to be reduced
        Default value = -1, allows for the completion of the entire catalogue

    gen_csv : boolean, default = True
          If True generates csv files of the reference catalogues

    Returns
    -------
    OB_CATALOGUE : pd.Dataframe
        The corresponding angular distance in degree value.
    '''

    # Start clock-1
    start1 = time.time()

    # Generate restricted catalogue based on upper magnitude limit
    temp0 = CATALOGUE[CATALOGUE.Mag <= mag_limit]

    # Number of rows in the resticted catalogue
    rows = temp0.shape[0]
    # Resets the index of <temp0>
    temp0.index = list(range(rows))

    # Prints total number of stars in <temp0> and the (n)C(2) - combinations
    print('Number of stars - ', rows)
    print('Number of unique combinations = ', (rows-1)*rows/2)

    # Initialize the number of iterations to take place
    no_iter = (rows-1) if no_iter == -1 else no_iter

    for i in range(no_iter):
        # Throws error if an iteration runs beyond number of available rows in <temp0>
        assert i<(rows-1), 'IndexError: iterating beyond available number of rows'

        # The final iteration is reduntant, as <temp2> will be zero rows
        '''
        if (rows-1-i)==0:
            continue
        '''

        # Generates <temp1> dataframe which has the (i - th) star of <temp0>
        # repetated (rows-1-i) times
        temp1 = pd.DataFrame(columns = ['Star_ID1','RA_1', 'Dec_1', 'Mag_1'])
        s1, ra, dec, mag = temp0.iloc[i]
        temp1.loc[0] = [s1] + [ra] + [dec] + [mag]
        temp1 = pd.concat([temp1]*(rows-1-i), ignore_index=True)

        # Generates <temp2> dataframe by copying values of <temp0> and dropping the first
        # (i + 1) number of stars
        temp2 = temp0
        temp2 = temp2.drop(list(range(i+1)), axis = 0)
        # Resets the index
        temp2.index = list(range(0, rows-1-i))

        # Concatenates <temp1> & <temp2> side-by-side such that resulting <temp3> has (8) columns altogether
        temp3 = pd.concat([temp1, temp2], axis=1)

        # Initializes <temp4> in the first iteratation
        if i == 0:
            temp4 = temp3

        # Append subsequent <temp4> with <temp3> after first iteration
        else:
            temp4 = pd.concat([temp4, temp3], axis = 0, ignore_index=True)

        # Releases memory back to OS
        if i%40 == 0:
            gc.collect()

    gc.collect()
    # Rename columns
    temp4.columns = ['Star_ID1','RA_1', 'Dec_1', 'Mag_1', 'Star_ID2', 'RA_2', 'Dec_2', 'Mag_2']

    if gen_csv == True:
        #Generates CSV of <temp4>
        temp4.to_csv('Processed_Catalogue1.csv', index = False)

    # Stop clock-1
    end1 = time.time() - start1

    # Print time taken
    print('Process 1 - ', end1)

    # Start clock-2
    start2 = time.time()

    # Initialize <OB_CATALOGUE>
    OB_CATALOGUE = temp4

    # Calculate angular distance between the two stars present in every row
    cols = ['RA_1', 'RA_2', 'Dec_1', 'Dec_2']
    OB_CATALOGUE['Ang_Distance'] = OB_CATALOGUE.apply(angularDistance, axis = 1, col_names = cols)

    if gen_csv == True:
        # Generates CSV of <OB_CATALOGUE>
        OB_CATALOGUE.to_csv('Processed_Catalogue2.csv', index = False)

    # Stop clock-2
    end2 = time.time() - start2

    # Print time taken
    print('Process 2 - ', end2)
    print('Total Process ', end1+ end2)

    return OB_CATALOGUE

def main():
    '''
    main function
    '''
    # Reads 'Master' star catalogue
    CATALOGUE = pd.read_csv(r"F:\IIT Bombay\SatLab\Star Tracker\Programs\Catalogues\Modified Star Catalogue.csv")
    # StarID: The database primary key from a larger "master database" of stars
    # Mag: The star's apparent visual magnitude
    # RA, Dec: The star's right ascension and declination, for epoch 2000.0 (Unit: RA - hrs; Dec - degrees)

    # Sorts <CATALOGUE>
    CATALOGUE.sort_values('Mag', inplace=True)

    # Run function
    REF_DF = genRefCatalogue(CATALOGUE, mag_limit=1, no_iter=-1, gen_csv=False)

    # Sort <REF_DF>
    REF_DF.sort_values('Ang_Distance', inplace=False)

    # Generates CSV of <REF_DF>
    REF_DF.to_csv('Processed_Catalogue3.csv', index = False)
    print('Done')

if __name__ == '__main__':
    main()


# A function to find out all the primes till the number n
#
# @Author : Subhendu Ranjan Mishra
#
# @Input : a positive intiger
# @Output : An array of intigers

from time import time
from numba import jit
import numpy as np

@jit
def getPrimes(n):
    q, r = divmod(n, 2)
    r = q + r
    seive = np.ones(r, dtype=bool)
    seive[0] = 0

    lim = int(n**0.5 / 2) + 1

    for i in range(1, lim + 1):
        p = 2 * i + 1
        if seive[i]:
            sp = 2 * i * (i + 1)
            seive[sp:r:p] = False

    def_prime = np.asarray([2])
    seived_primes = np.asarray(np.nonzero(seive)).flatten() * 2 + 1
    primes = np.concatenate((def_prime, seived_primes))
    return primes

@jit
def getPrimes2(n):
    arr = np.ones(n + 1, dtype=bool)
    arr[0], arr[1] = 0, 0
    for i in range(2, n + 1):
        if arr[i]:
            arr[i * i:n + 1:i] = False
    return arr.nonzero()[0]

if __name__ == '__main__':
    # Test Cases
    print("Testing the getPrimes function.")
    print("getPrimes2(18) : ", getPrimes2(18))
    print("getPrimes2(7) : ", getPrimes2(7))
    print("getPrimes2(25) : ", getPrimes2(25))
    print("getPrimes2(31) : ", getPrimes2(31))
    print()
    print("Now testing the getPrimes function for higher powers of 10")
    for power in range(1, 9):
        n = 10**power
        t0 = time()
        primes = getPrimes(n)
        print(
            "1 - Computed for {:10d} ({}th power of 10), in {:03.5f} secs. number of primes : {:,}".
            format(n, power, time() - t0, len(primes)))

        # second function
        t0 = time()
        primes2 = getPrimes2(n)
        print(
            "2 - Computed for {:10d} ({}th power of 10), in {:03.5f} secs. number of primes : {:,}".
            format(n, power, time() - t0, len(primes2)))
        # print(primes)


#IIR Analog and Digital Butterworth BPF filter design
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt
from pylab import clf
#Analog Band pass butterworth filter, order =3, fc1=100Hz, fc2=300Hz
#b = numerator coefficients, a = denominator coefficients
#w = frequency variable, h= frequency response

b,a = signal.iirfilter(3,[100,300],btype ='bandpass',analog=True, ftype ='butter')
w,h = signal.freqs(b,a,1000)

fig1 = plt.figure(1)
ax = fig1.add_subplot(1,1,1)
ax.semilogx(w,20*np.log10(np.maximum(abs(h),1e-5)))
ax.set_title('Butterworth IIR Analog BPF frequency response')
ax.set_xlabel('Frequency in Hz---->')
ax.set_ylabel('Amplitude in dB----->')
#ax.axis((10,1000,-100,10))
ax.grid(which='both',axis='both')
plt.show()

#Digital Band pass butterworth filter, order =3, fc1=100Hz, fc2=300Hz
fs =1000
sos = signal.iirfilter(3,[100/fs,300/fs],btype ='bandpass',analog=False, ftype ='butter',output='sos')
w,h = signal.sosfreqz(sos,1000)

fig2 = plt.figure(2)
clf()
ax = fig2.add_subplot(1,1,1)
ax.plot(w/np.pi,20*np.log10(np.maximum(abs(h),1e-5)))
ax.set_title('Butterworth IIR Digital BPF frequency response')
ax.set_xlabel('Normalized Digital Frequency ---->')
ax.set_ylabel('Amplitude in dB----->')
#ax.axis((10,1000,-100,10))
ax.grid(which='both',axis='both')
plt.show()

#Result
##>>> b
##array([8000000.,       0.,       0.,       0.])
##>>> a
##array([1.0e+00, 4.0e+02, 1.7e+05, 3.2e+07, 5.1e+09, 3.6e+11, 2.7e+13])
##>>> sos
##array([[ 0.01809893,  0.03619787,  0.01809893,  1.        , -1.28407904,
##         0.50952545],
##       [ 1.        ,  0.        , -1.        ,  1.        , -1.0263941 ,
##         0.65079467],
##       [ 1.        , -2.        ,  1.        ,  1.        , -1.73866033,
##         0.83854915]])


import numpy as np

def l1Norm(x):
    """
        Computes the l1 norm of x
    """
    return np.sum(np.abs(x))

def l2Norm(x):
    """
        Computes the l2 norm of x
    """
    return np.sqrt(np.sum(x**2))

def r2Compute(u, v, x):
    """
        Computes the r square score of the data
        x in the projection defined by u and v
        :param u: Vector of size x
        :param v: Vector of size x
        :param x: Matrix of size N * x
        :return r2: r square score of the N points projected in (u,v)
    """
    xProj = np.dot(x, u.T).flatten()
    yProj = np.dot(x, v.T).flatten()
    projectedPoints = np.array([xProj,yProj]).T
    SSRes = np.sum((projectedPoints[:, 1] - projectedPoints[:, 0])**2)
    SSTot = np.sum((projectedPoints[:, 1] - np.mean(projectedPoints[:, 1]))**2)

    return 1 - SSRes/(SSTot + 10**(-5)) # Add epsilon to avoid nan


"""

This program will find the closest pair of points in a set of n points.
Two algorithms will be implemented here:
    1) A brute force approach determines the distance between
       every pair of points first and then find the points
       with the closest distance.
    2) An enhanced algorithm
       - Sorts all points according to coordinates;
       - Divides all points in two halves;
       - Recursively finds the smallest distances in both subarrays;
       - Take the minimum of two smallest distances;
       - Create an array strip[] that stores all points which are at
         most d distance away from the middle line dividing the two sets;
       - Find the smallest distance in strip[];
       - Find the points with the closest distance.

 The time complexity of brute force algorithm is O(n^2).
 The time complexity of enhanced algorithm algorithm is O(n * log(n)).

 @author RabbitCaesar

"""

import numpy as np
import timeit

# Input random generator
# Use this to create different testing inputs
def input_random_generator(n):
    arrP = []
    for i in range (n):
        temp = []
        for j in range (2):
            temp.append(np.random.randint(1,50))
        arrP.append(temp)
    return arrP

# Calculate Euclidean distance between 2 points
def get_distance(point1, point2):
    return np.round(np.sqrt((point1[0] - point2[0]) ** 2
                            + (point1[1] - point2[1]) ** 2), 2)

# Brute force O(n^2)
def brute_force(arr, n):
    min_dist, pair = float('inf'), []
    # Iterate through all the possible pairs
    # Calculate distance and compare to find min
    for i in range(n - 1):
        for j in range(i + 1, len(arr)):
            dist = get_distance(arr[i], arr[j])
            if min_dist > dist:
                min_dist = dist
                pair = [arr[i], arr[j]]
    return [min_dist, pair]

# Sort points vertically or horizontally. For example, points
# (4,3), (6,2), (1,7) will be sorted to (6,2), (4,3), (1,7) vertically
def sort_points(array, index=0):
    return sorted(array, key=lambda x: x[index])

# Find the closest pair of points in the strip.
# Return the min distance between the closest pair.
def get_strip_min_dist(points, n, min_dist=float('inf')):
    # it's proven that the max iteration of this loop is 6
    for i in range(min(6, n - 1), n):
        for j in range(max(0, i - 6), i):
            dist = get_distance(points[i], points[j])
            if min_dist > dist: min_dist = dist
    return min_dist

# Recursively find the distance between the closest pair
def closest_pair_raw(points_h_sorted, points_v_sorted, n):
    # Base case: if there are only 2 or 3 points, then use brute force
    if n <= 3:
        return brute_force(points_h_sorted, n)[0]

    # Recursion part
    mid = n // 2
    # Left section
    dist_left = closest_pair_raw(points_h_sorted
                                 , points_v_sorted[:mid], mid)
    # Right section
    dist_right = closest_pair_raw(points_v_sorted
                                  , points_v_sorted[mid:], n - mid)
    min_dist = min(dist_left, dist_right)

    # Points in the strip
    strip = []
    for point in points_h_sorted:
        if abs(point[0] - points_h_sorted[mid][0]) < min_dist:
            strip.append(point)

    # Get the distance between closest pair in strip
    min_dist_strip = get_strip_min_dist(strip, len(strip), min_dist)

    return min(min_dist, min_dist_strip)

# Return final distance between closest pair
def closest_pair_of_points(points, n):
    points_h_sorted = sort_points(points, index=0)
    points_v_sorted = sort_points(points, index=1)

    return (closest_pair_raw(points_h_sorted, points_v_sorted, n))

"""
Driver executes DTM
"""
if __name__ == '__main__':

    # Manual Test case 1:
    #points = [(32, 24), (34, 70), (77, 61), (37, 99), (26, 47), (16, 53)]

    # Manual Test case 2:
    # points = [[82, 31], [67, 11], [80, 7], [25, 22], [84, 23]
    #         , [21, 2], [24, 74], [21, 53], [28, 85], [12, 7]]

    # Manual Test case 3:
    # points  =[[94, 38], [35, 48], [18, 59], [33, 95], [57, 80]
    #         , [34, 5], [71, 42], [42, 41], [75, 40], [81, 52]
    #         , [99, 21], [74, 59], [83, 70], [17, 72], [22, 50]
    #         , [51, 16], [12, 80], [57, 96], [7, 14], [5, 2]]

    # Random generator can be used to initialize input points
    points = input_random_generator(100)

    """
    Brute Force Approach O(n^2)
    Set timmer to log the runtime
    """
    start = timeit.default_timer()

    brute_force_result = brute_force(points, len(points))

    stop = timeit.default_timer()
    time_b = stop - start

    """
    Enhanced Approach O(nlog(n))
    Set timmer to log the runtime
    """
    start = timeit.default_timer()

    nlogn_result = closest_pair_of_points(points, len(points))

    stop = timeit.default_timer()
    time_e = stop - start

    """
    Display final results
    """
    comp = np.round(time_b/time_e ,2)
    pointA = str(brute_force_result[1][0])
    pointB = str(brute_force_result[1][1])

    print('\nInputs:\n' + str(points))
    print('\nClosest Pair Is: ' + pointA + ' and ' + pointB)

    print('\nBrute Force Approach O(n^2):\nMin Distance: '
          + str(brute_force_result[0]) + '\nRuntime: '
          , time_b)
    print('\nEnhanced Approach O(nlog(n)):\nMin Distabce: '
          + str(nlogn_result) + '\nRuntime: '
          , time_e)
    print('\nRuntime Comparison:\nEnhanced Approach is'
          , comp, 'times faster than Brute Force.')


# Simpson's rule
#
# M. Zingale (2013-02-13)

import math
import numpy
import sys

# function we wish to integrate
def fun(x):
    return numpy.exp(-x)

# analytic value of the integral
def I_exact(a,b):
    return -math.exp(-b) + math.exp(-a)

# do a Simpson's integration by breaking up the domain [a,b] into N
# slabs.  Note: N must be even, because we do a pair at a time
def simp(a,b,f,N):

    xedge = numpy.linspace(a,b,N+1)

    integral = 0.0

    if not N%2 == 0:
        sys.exit("ERROR: N must be even")

    delta = (xedge[1] - xedge[0])

    n = 0
    while n < N:
        integral += (1.0/3.0)*delta*(f(xedge[n]) +
                                     4.0*f(xedge[n+1]) +
                                     f(xedge[n+2]))
        n += 2

    return integral

a = 0.0
b = 1.0

N = 2
while (N <= 128):
    t = simp(a,b,fun,N)
    e = t - I_exact(a,b)
    print N, t, e

    N *= 2


import numpy as np

# 均方误差
def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)

# 交叉熵误差
# 支持单个和 batch
def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

# 假设 第三个位置是正确的
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
print('第三个位置的概率最高情况')
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
print('均方误差', mean_squared_error(np.array(y), np.array(t)))
print('交叉熵误差', cross_entropy_error(np.array(y), np.array(t)))

print('第八个位置的概率最高的情况')
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
print('均方误差', mean_squared_error(np.array(y), np.array(t)))
print('交叉熵误差', cross_entropy_error(np.array(y), np.array(t)))

# 数值微分
def numerical_diff(f, x):
    h = 1e-4
    return (f(x+h) - f(x-h)) / (2 * h)

# 梯度计算
def numerical_gradient(f, x):
    h = 1e-4
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]
        # f(x+h) 的计算
        x[idx] = tmp_val + h
        fxh1 = f(x)
        # f(x-h) 的计算
        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        x[idx] = tmp_val

    return grad

# 梯度下降
def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad

    return x

def function_2(x):
    return x[0]**2 + x[1]**2

print('用梯度下降计算函数 function_2 的最小值')
init_x = np.array([-3.0, 4.0])
result = gradient_descent(function_2, init_x=init_x, lr=0.1, step_num=100)
print(result)