import numpy as np

f_src=open('paths_list.txt','r')

## imported info
paths=[]	# [{'src':'A', 'dest':'B', 'length':len}]
nodes=[]	# ['node_name']

while(True):
  linetmp=f_src.readline()
  if(not linetmp):
    break
  line=linetmp.split('\t')
  if(len(line)!=3):
    break
  if(nodes.count(line[0])==0):	# new node
    nodes.append(line[0])
  if(nodes.count(line[1])==0):	# new node
    nodes.append(line[1])
  path_tmp={'src':line[0], 'dest':line[1], 'length':int(line[2])}
  if(paths.count(path_tmp)!=0):
    print("Warning: duplicated path found! Ignoring...")
  else:
    paths.append(path_tmp)
len_nodes=len(nodes)
len_paths=len(paths)

f_src.close()

## -imported info

## Some magic

# force check shortest path: each transmission adds 1 length
for path in paths:
  path['length']=path['length']+1

## end magic

## prepare

# length = paths_table[index1][index2]
paths_table=np.full((len_nodes, len_nodes), -1, dtype=int)

for path in paths:
  paths_table[ nodes.index(path['src']) ][ nodes.index(path['dest']) ] = path['length']

## -prepare

print("Imported matrix:")
print("#",end ="")
for i in range(0,len_nodes):
  print("\t%s"%nodes[i],end ="")
  if(i==len_nodes-1):
    print()

for i in range(0,len_nodes):
  print(nodes[i],end ="")
  for j in range(0,len_nodes):
    print("\t%s"%paths_table[i][j],end ="")
    if(j==len_nodes-1):
      print()

print()

### Floyd

## initial matrix

dist_map=np.full((len_nodes, len_nodes), -1, dtype=int)
path_map=np.full((len_nodes, len_nodes), -1, dtype=int)
for i in range(0,len_nodes):
  dist_map[i][i]=0;	# node has dist=0 to itself
  path_map[i][i]=i;	# node routes to itself by itself
for path in paths:
  if(path['length']<=0):
    print("Error: non-positive length found. Exitting...")
    exit(-1)
  dist_map[ nodes.index(path['src']) ][ nodes.index(path['dest']) ]=path['length']
  path_map[ nodes.index(path['src']) ][ nodes.index(path['dest']) ]=nodes.index(path['src'])

## -initial matrix

## loop

for k in range(0,len_nodes):
  for i in range(0,len_nodes):
    for j in range(0,len_nodes):
      if(dist_map[i][j] == -1):	# no valid path
        if((dist_map[i][k]!=-1) and (dist_map[k][j]!=-1)):	# new path is valid
          dist_map[i][j]=dist_map[i][k]+dist_map[k][j]
          path_map[i][j]=k
      else:
        if((dist_map[i][k]!=-1) and (dist_map[k][j]!=-1)):	# new path is valid
          if(dist_map[i][j]>dist_map[i][k]+dist_map[k][j]):	# new path is better
            dist_map[i][j]=dist_map[i][k]+dist_map[k][j]
            path_map[i][j]=k

## -loop

### -Floyd

print("distance table:")
print("#",end ="")
for i in range(0,len_nodes):
  print("\t%s"%nodes[i],end ="")
  if(i==len_nodes-1):
    print()
for j in range(0,len_nodes):
  print(nodes[j],end ="")
  for i in range(0,len_nodes):
    print("\t%s"%dist_map[i][j],end ="")
    if(i==len_nodes-1):
      print()
print()

print("path table:")
print("#",end ="")
for i in range(0,len_nodes):
  print("\t%s"%nodes[i],end ="")
  if(i==len_nodes-1):
    print()
for j in range(0,len_nodes):
  print(nodes[j],end ="")
  for i in range(0,len_nodes):
    if(path_map[i][j]!=-1):
      print("\t%s"%nodes[path_map[i][j]],end ="")
    else:
      print("\t~",end ="")
    if(i==len_nodes-1):
      print()
print()

# Summary
for i in range(0,len_nodes):
  for j in range(0,len_nodes):
    if(dist_map[i][j]==-1):
      print("Warning: no path from %s to %s"%(nodes[i],nodes[j]))

def get_next_hop(src, dest, pathmap):
  if(dest==-1):	# subcall returned -1
    return -1
  if(src==dest):	# A-A type
    return src
  elif(src==pathmap[src][dest]):	# A-B type, return B
    return dest
  elif(dest==pathmap[src][dest]):
    print("ERROR!!!")
  else:	# A-B-...-M , remove M and query again
    return get_next_hop(src, pathmap[src][dest], pathmap)

print("Build instructions list:")
print("Current\tTarget\tRoute")
for i in range(0,len_nodes):
  for j in range(0,len_nodes):
    print("%s\t%s\t%s"%(nodes[i],nodes[j],nodes[get_next_hop(i,j,path_map)]))


import numpy as np
import numba

# Склаярное произведение комплексных векторов
@numba.jit()
def complex_dot_prod(vec1, vec2):
    return vec1.dot(np.conj(vec2))

# Проверка матрицы на квадратность
@numba.jit()
def check_square(H):
    if H.shape.count(H.shape[0]) == len(H.shape):
        return True
    else:
        return False

# Нахождение центра для точки отрезка
@numba.jit()
def _mu2(z1, z2):
    z1_z2 = z1 * np.conj(z2)
    return (z1 + z2) / 2 + (1j * np.imag(z1_z2) * (z2 - z1)) / (2 * (z1_z2 + np.real(z1_z2)))

# Нахождение радиуса для точки центра отрезка
@numba.jit()
def _R2(z1, z2):
    z1_z2 = np.conj(z1) * z2
    return np.real( np.sqrt( (np.abs(z1 - z2)**2 * np.abs(z1_z2)) / (2 * (z1_z2 + np.real(z1_z2))) ) )

# Нахождение точки центра теугольника
@numba.jit()
def _mu3(z1, z2, z3):
    return 1j * ((np.abs(z1)**2 * (z2 - z3) + np.abs(z2)**2 * (z3 - z1) + np.abs(z3)**2 * (z1 - z2)) /
                 (2 * np.imag(z1 * np.conj(z2) + z2 * np.conj(z3) + z3 * np.conj(z1))))

# Нахождение радиуса по центру и точке, входящей в рассматриваемый треугольник
@numba.jit()
def _R3(mu, z):
    return np.real(np.sqrt(np.abs(mu - z)**2))

# Нахождение центра спектра и его радиуса
@numba.jit()
def mu_find(spectre):
    spectre = np.array(spectre, dtype=complex)
    n = spectre.shape[0]

    mu = 0 + 0j
    R = 0

    mu_flag = []
    R_flag = []

    if n < 2:
        raise ValueError

    elif n == 2:
        mu = _mu2(spectre[0], spectre[1])
        R = _R2(spectre[0], spectre[1])

    elif n == 3:
        for i in range(3):
            mu_flag.append(_mu2(spectre[i], spectre[(i + 1) % 3]))
            R_flag.append(_R2(spectre[i], spectre[(i + 1) % 3]))
        mu_flag.append(_mu3(spectre[0], spectre[1], spectre[2]))
        R_flag.append(_R3(mu_flag[3], spectre[0]))

        mu_flag = np.array(mu_flag, dtype=complex)
        R_flag = np.array(R_flag, dtype=float)

        mu = mu_flag[np.argmin(R_flag)]
        R = np.min(R_flag)

    elif n > 3:
        # Случай для отрезков
        for i in range(n - 1):
            for j in range(i, n):
                mu2_loc = _mu2(spectre[i], spectre[j])
                R2_loc = _R2(spectre[i], spectre[j])
                for k in range(n):
                    if np.abs(mu2_loc - spectre[k]) < R2_loc:
                        break
                mu_flag.append(mu2_loc)
                R_flag.append(R2_loc)

        # Cлучай для треугольников
        if len(mu_flag) == 0:
            for i in range(n - 2):
                for j in range(i, n - 1):
                    for l in range(j, n):
                        mu3_loc = _mu3(spectre[i], spectre[j], spectre[l])
                        R3_loc = _R3(mu3_loc, spectre[i])
                        for k in range(n):
                            if np.abs(mu3_loc - spectre[k]) < R3_loc:
                                break
                        mu_flag.append(mu3_loc)
                        R_flag.append(R3_loc)

        mu_flag = np.array(mu_flag, dtype=complex)
        R_flag = np.array(R_flag, dtype=float)

        mu = mu_flag[np.argmin(R_flag)]
        R = np.min(R_flag)

    return mu, R

# Обобщенный  метод простых итераций
def GMSI(H, f, mu, u0=None, eps=10e-7, n_iter=10000):
    if not check_square(H):
        print("Matrix is not squared")
        return u0

    H = np.array(H, dtype=complex)
    f = np.array(f, dtype=complex)
    N = H.shape[0]

    if u0 is None:
        u0 = np.ones((N, ), dtype=complex)

    u_vector = u0.copy()
    for iter_index in range(n_iter):
        u_vector = u0 - 1/mu * (H @ u0 - f)
        if np.sqrt(complex_dot_prod(u_vector - u0, u_vector - u0)) / np.sqrt(complex_dot_prod(f, f)) < eps:
            break
        u0 = u_vector.copy()

    return u_vector

# Специально для точки входа
def _main():
    H = np.diag(np.array([5 + 0j, 10 - 5j, 10 + 5j]))
    mu, R = mu_find(spectre=np.diag(H))
    print("Найденные mu и R")
    print(mu, R)
    print("\n")

    f = np.array([1, 2, 3], dtype=complex)
    solve_GMSI = GMSI(H, f, mu)

    print("Решение итерациями")
    print(np.round(solve_GMSI, 3))
    print("Сходится ли с ответом")
    print(np.alltrue(np.isclose(H @ solve_GMSI, f)))
    print("\n")
    print("Прямое решение")
    print(np.linalg.solve(H, f))
    return 0

if __name__ == "__main__" :
    _main()


"""
Resources:
- https://en.wikipedia.org/wiki/Conjugate_gradient_method
- https://en.wikipedia.org/wiki/Definite_symmetric_matrix
"""
import numpy as np

def _is_matrix_spd(matrix: np.array) -> bool:
    """
    Returns True if input matrix is symmetric positive definite.
    Returns False otherwise.

    For a matrix to be SPD, all eigenvalues must be positive.

    >>> import numpy as np
    >>> matrix = np.array([
    ... [4.12401784, -5.01453636, -0.63865857],
    ... [-5.01453636, 12.33347422, -3.40493586],
    ... [-0.63865857, -3.40493586,  5.78591885]])
    >>> _is_matrix_spd(matrix)
    True
    >>> matrix = np.array([
    ... [0.34634879,  1.96165514,  2.18277744],
    ... [0.74074469, -1.19648894, -1.34223498],
    ... [-0.7687067 ,  0.06018373, -1.16315631]])
    >>> _is_matrix_spd(matrix)
    False
    """
    # Ensure matrix is square.
    assert np.shape(matrix)[0] == np.shape(matrix)[1]

    # If matrix not symmetric, exit right away.
    if np.allclose(matrix, matrix.T) is False:
        return False

    # Get eigenvalues and eignevectors for a symmetric matrix.
    eigen_values, _ = np.linalg.eigh(matrix)

    # Check sign of all eigenvalues.
    return np.all(eigen_values > 0)

def _create_spd_matrix(dimension: np.int64) -> np.array:
    """
    Returns a symmetric positive definite matrix given a dimension.

    Input:
    dimension gives the square matrix dimension.

    Output:
    spd_matrix is an diminesion x dimensions symmetric positive definite (SPD) matrix.

    >>> import numpy as np
    >>> dimension = 3
    >>> spd_matrix = _create_spd_matrix(dimension)
    >>> _is_matrix_spd(spd_matrix)
    True
    """
    random_matrix = np.random.randn(dimension, dimension)
    spd_matrix = np.dot(random_matrix, random_matrix.T)
    assert _is_matrix_spd(spd_matrix)
    return spd_matrix

def conjugate_gradient(
    spd_matrix: np.array,
    load_vector: np.array,
    max_iterations: int = 1000,
    tol: float = 1e-8,
) -> np.array:
    """
    Returns solution to the linear system np.dot(spd_matrix, x) = b.

    Input:
    spd_matrix is an NxN Symmetric Positive Definite (SPD) matrix.
    load_vector is an Nx1 vector.

    Output:
    x is an Nx1 vector that is the solution vector.

    >>> import numpy as np
    >>> spd_matrix = np.array([
    ... [8.73256573, -5.02034289, -2.68709226],
    ... [-5.02034289,  3.78188322,  0.91980451],
    ... [-2.68709226,  0.91980451,  1.94746467]])
    >>> b = np.array([
    ... [-5.80872761],
    ... [ 3.23807431],
    ... [ 1.95381422]])
    >>> conjugate_gradient(spd_matrix, b)
    array([[-0.63114139],
           [-0.01561498],
           [ 0.13979294]])
    """
    # Ensure proper dimensionality.
    assert np.shape(spd_matrix)[0] == np.shape(spd_matrix)[1]
    assert np.shape(load_vector)[0] == np.shape(spd_matrix)[0]
    assert _is_matrix_spd(spd_matrix)

    # Initialize solution guess, residual, search direction.
    x0 = np.zeros((np.shape(load_vector)[0], 1))
    r0 = np.copy(load_vector)
    p0 = np.copy(r0)

    # Set initial errors in solution guess and residual.
    error_residual = 1e9
    error_x_solution = 1e9
    error = 1e9

    # Set iteration counter to threshold number of iterations.
    iterations = 0

    while error > tol:

        # Save this value so we only calculate the matrix-vector product once.
        w = np.dot(spd_matrix, p0)

        # The main algorithm.

        # Update search direction magnitude.
        alpha = np.dot(r0.T, r0) / np.dot(p0.T, w)
        # Update solution guess.
        x = x0 + alpha * p0
        # Calculate new residual.
        r = r0 - alpha * w
        # Calculate new Krylov subspace scale.
        beta = np.dot(r.T, r) / np.dot(r0.T, r0)
        # Calculate new A conjuage search direction.
        p = r + beta * p0

        # Calculate errors.
        error_residual = np.linalg.norm(r - r0)
        error_x_solution = np.linalg.norm(x - x0)
        error = np.maximum(error_residual, error_x_solution)

        # Update variables.
        x0 = np.copy(x)
        r0 = np.copy(r)
        p0 = np.copy(p)

        # Update number of iterations.
        iterations += 1

    return x

def test_conjugate_gradient() -> None:
    """
    >>> test_conjugate_gradient()  # self running tests
    """
    # Create linear system with SPD matrix and known solution x_true.
    dimension = 3
    spd_matrix = _create_spd_matrix(dimension)
    x_true = np.random.randn(dimension, 1)
    b = np.dot(spd_matrix, x_true)

    # Numpy solution.
    x_numpy = np.linalg.solve(spd_matrix, b)

    # Our implementation.
    x_conjugate_gradient = conjugate_gradient(spd_matrix, b)

    # Ensure both solutions are close to x_true (and therefore one another).
    assert np.linalg.norm(x_numpy - x_true) <= 1e-6
    assert np.linalg.norm(x_conjugate_gradient - x_true) <= 1e-6

if __name__ == "__main__":
    import doctest

    doctest.testmod()
    test_conjugate_gradient()


from os import name
import numpy as np
import line_search as ls

def perform_line_search(x, f, s, line_search, tol):
    def f_aux(a): return f(x + a*s)
    bracket = ls.bracket(0, f_aux)
    alpha = line_search(f_aux, bracket[0], bracket[1], tol=tol)
    return alpha

def powell(x, f, tol=1e-6, line_search=ls.quadratic_fit_search):

    lx = [x]
    lfx = [f(x)]
    directions = []

    for i in range(len(x)):
        directions.append(np.zeros(len(x)))
        directions[i][i] = 1

    s = directions[-1]
    alpha = perform_line_search(x, f, s, line_search, tol)
    x = x + alpha*s

    lx.append(x)
    lfx.append(f(x))

    while np.linalg.norm(s) > tol:

        z = x
        i = 0

        while i < len(x) and np.linalg.norm(s) > tol:

            s = directions[i]
            alpha = perform_line_search(x, f, s, line_search, tol)
            x = x + alpha*directions[i]

            lx.append(x)
            lfx.append(f(x))

            i = i+1

        s = x - z
        alpha = perform_line_search(x, f, s, line_search, tol)
        x = x + alpha*s

        lx.append(x)
        lfx.append(f(x))

        directions.pop(0)
        directions.append(s)

    return x, f(x), lx, lfx

def gradient_descent(x, f, grad, tol=1e-6, alpha=[], line_search=ls.quadratic_fit_search):

    lx = [x]
    lfx = [f(x)]

    got_alpha = True if alpha else False

    gradx = grad(x)

    while np.linalg.norm(gradx) > tol:

        if not got_alpha:
            def f_aux(a): return f(x - a*gradx)
            bracket = ls.bracket(0, f_aux)
            alpha = line_search(f_aux, bracket[0], bracket[1], tol=tol)

        x = x - alpha*gradx
        gradx = grad(x)

        lx.append(x)
        lfx.append(f(x))

    return x, f(x), lx, lfx

def newton_method(x0, f, grad, H, tol=1e-6):

    x = np.array(x0)

    # Store x at each generation for later visualization
    list_x = [x]
    list_fx = [f(x)]

    while np.linalg.norm(grad(x)) > tol:

        hinv = np.linalg.inv(H(x))
        g = np.array([grad(x)]).T
        delta = np.matmul(hinv, g)
        x = x - delta.T
        x = x[0]

        list_x.append(x)
        list_fx.append(f(x))

        g = grad(x)

    return x, f(x), list_x, list_fx

if __name__ == "__main__":

    # Quadratic function with interaction term

    #f = lambda x: x[0]**2 + x[1]**2 + x[0]*x[1]
    #grad = lambda x: [2*x[0] + x[1], 2*x[1] + x[0]]
    #H = lambda x: [[2,1],[1,2]]

    # Rosenbrocks function

    def f(x): return (100*(x[1] - x[0]**2)**2 + (x[0]-1)**2)

    def grad(x): return [-400*(x[1]-x[0]**2) *
                         x[0] + 2*(x[0]-1), 200*(x[1]-x[0]**2)]

    def H(x): return [[-400*x[1]+1200*x[0]**2, -400*x[0]], [-400*x[0], 200]]

    #xs,fs,xlist,fx_list = gradient_descent([1,-2],f,grad)

    xs, fs, xlist, fx_list = newton_method([1, -2], f, grad, H)

    print(xs)
    print(xlist)


# -*- coding: utf-8 -*-#
'''
# Name:         math_utils
# Description:
# Author:       neu
# Date:         2020/7/28
'''

import numpy as np

def z_score(x, mean, std):
    '''
    Z-score normalization function: $z = (X - \mu) / \sigma $,
    where z is the z-score, X is the value of the element,
    $\mu$ is the population mean, and $\sigma$ is the standard deviation.
    :param x: np.ndarray, input array to be normalized.
    :param mean: float, the value of mean.
    :param std: float, the value of standard deviation.
    :return: np.ndarray, z-score normalized array.
    '''
    return (x - mean) / std

def z_inverse(x, mean, std):
    '''
    The inverse of function z_score().
    :param x: np.ndarray, input to be recovered.
    :param mean: float, the value of mean.
    :param std: float, the value of standard deviation.
    :return: np.ndarray, z-score inverse array.
    '''
    return x * std + mean

def MAPE(v, v_):
    '''
    Mean absolute percentage error.
    :param v: np.ndarray or int, ground truth.
    :param v_: np.ndarray or int, prediction.
    :return: int, MAPE averages on all elements of input.
    '''
    return np.mean(np.abs(v_ - v) / (v + 1e-5))

def RMSE(v, v_):
    '''
    Mean squared error.
    :param v: np.ndarray or int, ground truth.
    :param v_: np.ndarray or int, prediction.
    :return: int, RMSE averages on all elements of input.
    '''
    return np.sqrt(np.mean((v_ - v) ** 2))

def MAE(v, v_):
    '''
    Mean absolute error.
    :param v: np.ndarray or int, ground truth.
    :param v_: np.ndarray or int, prediction.
    :return: int, MAE averages on all elements of input.
    '''
    return np.mean(np.abs(v_ - v))

def evaluation(y, y_, x_stats):
    '''
    Evaluation function: interface to calculate MAPE, MAE and RMSE between ground truth and prediction.
    Extended version: multi-step prediction can be calculated by self-calling.
    :param y: np.ndarray or int, ground truth.
    :param y_: np.ndarray or int, prediction.
    :param x_stats: dict, paras of z-scores (mean & std).
    :return: np.ndarray, averaged metric values.
    '''
    dim = len(y_.shape)

    if dim == 3:
        # single_step case
        v = z_inverse(y, x_stats['mean'], x_stats['std'])
        v_ = z_inverse(y_, x_stats['mean'], x_stats['std'])
        return np.array([MAPE(v, v_), MAE(v, v_), RMSE(v, v_)])
    else:
        # multi_step case
        tmp_list = []
        # y -> [time_step, batch_size, n_route, 1]
        y = np.swapaxes(y, 0, 1)
        # recursively call
        for i in range(y_.shape[0]):
            tmp_res = evaluation(y[i], y_[i], x_stats)
            tmp_list.append(tmp_res)
        return np.concatenate(tmp_list, axis=-1)


# -*- coding: utf-8 -*-
"""
Esame 8 Settembre
Es 2
"""
import numpy as np
import matplotlib.pyplot as plt
import math

def Lagrange(xnodi, i):
    n = xnodi.size
    if i == 0:
        xzeri = xnodi[1 : n]
    else:
        xzeri = np.append(xnodi[0 : i], xnodi[i + 1 : n])
    num = np.poly(xzeri)
    den = np.polyval(num, xnodi[i])
    return num / den

def interpol(x, y, xx):
    m = x.size
    n = xx.size
    L = np.zeros((m,n))
    for i in range(m):
        L[i, :] = np.polyval(Lagrange(x,i), xx)
    return np.dot(y, L)

def chebishev(a, b, n):
    t1 = (a+b) / 2
    t2 = (b - a) / 2
    x = np.zeros((n + 1, ))
    for k in range(n+1):
        x[k] = t1 + t2 * np.cos(((2*k+1) / (2*(n + 1))*math.pi))
    return x

f = lambda x : 1 / (1 + 900 * (np.power(x, 2)))
xx = np.linspace(-1, 1, 100)
xe = []
xc = []
re = []
rc = []
LLe = np.zeros((6, 1))
LLc = np.zeros((6, 1))
l = 0
for n in range(5, 31, 5):
    xe = []
    xc = []
    Le = np.zeros((100, 1))
    Lc = np.zeros((100, 1))
    for i in range(1, n + 2):
        xe.append( -1 + ((2*(i-1)) / n))
    for i in range(n + 1, 0, -1):
        xc.append(math.cos(((2*i - 1) * math.pi) / (2 * (n + 1))))
    for i in range(n + 1):
        lagrC = Lagrange(np.array(xc), i)
        lagrE = Lagrange(np.array(xe), i)
        Le = Le + np.abs(np.polyval(lagrE, xx))
        Lc = Lc + np.abs(np.polyval(lagrC, xx))
    LLe[l] = np.max(Le)
    LLc[l] = np.max(Lc)
    l += 1

    fe = f(xe)
    fc = f(xc)
    ye = interpol(np.array(xe), fe, xx)
    yc = interpol(np.array(xc), fc, xx)
    re.append(np.abs(ye - f(xx)))
    rc.append(np.abs(yc - f(xx)))

for i in range(re.__len__()):
    plt.subplot(2, 3, i + 1)
    plt.plot(xx, re[i])
    plt.legend(["eq = " + str((i+1)*5)])
plt.show()
for i in range(rc.__len__()):
    plt.subplot(2, 3, i + 1)
    plt.plot(xx, rc[i])
    plt.legend(["cheb = " + str((i+1)*5)])
plt.show()

plt.semilogy(np.arange(5, 31, 5), LLe, "r-*", np.arange(5, 31, 5), LLc, "b-o")
plt.title("Costante di Lebesgue")
plt.legend(["Nodi equispaziati", "Nodi di chebishev"])
plt.show()


import numpy as np
import math
def check_singularity(a):

    return np.linalg.det(a) == 0

def resolve(a,b,mode='back'): #AX = B return X
    n = len(b)
    try:
        x_shape = (a.shape[1],b.shape[1])
    except IndexError: #in case b is 1-d b.shape = (l,) than its not possible to get number of collumns
        x_shape = (a.shape[1],1)
    x = np.zeros(x_shape) # A's size = mxn ,X = nxo, B =  mxo,
    if mode == 'front':
        for i in range(n):
            limit = i
            x[i] = (b[i] - a[i,:limit].dot(x[:limit]))/a[i,i]
    if mode == 'back':
        for i in range(n-1,-1,-1):
            limit = i+1
            x[i] = (b[i]-a[i,limit:].dot(x[limit:]))/a[i,i]
    x = np.around(x,decimals = 2)
    return x

def elim(a,mode='upper'):
    n = len(a)
    m_ = np.identity(n)
    if mode ==  'upper':#get the upper triangle matrix
        for j in range(n-1):
            m = np.identity(n)
            for i in range(j+1,n):
                m[i,j] = -a[i,j]/a[j,j]
            m_ = m.dot(m_)
            a = m.dot(a)
        m_ = np.around(m_,decimals = 2)
        a = np.around(a,decimals = 2)
    if mode == 'lower':#get the lowe triangle matrix
        for j in range(n-1,-1,-1):
            m = np.identity(n)
            for i in range(j-1,-1,-1):
                m[i,j] = -a[i,j]/a[j,j]
            m_ = m.dot(m_)
            a = m.dot(a)
        m_ = np.around(m_,decimals = 2)
        a = np.around(a,decimals = 2)
    return m_,a

def decomp_LU(a,b):
    #get L and U from matrix A
    m,u = elim(a)
    l = np.linalg.inv(m)
    y = resolve(l,b,'front')#now resolve LY = B
    x = resolve(u,y,'back')#then resolve UX = Y
    x = np.around(x,decimals = 1)
    return x

def decomp_Cholesky(a,b):
    n = len(a)
    l = np.zeros((n,n))
    for i in range(n): #algotihm
        s = 0
        s = np.inner(l[i,:i],l[i,:i]) #same as computing the norm of the vector
        try:
            l[i,i] = math.sqrt(a[i,i]-s)
        except ValueError:
            return 'Não foi possível realizar a decomposição de Cholensky'
        for j in range(i+1,n):
            s = 0
            for k in range(i):
                s += (l[i,k]*l[j,k])
            l[j,i] = (a[j,i]-s)/l[i,i]
    l = np.around(l,decimals= 2)
    u = l.transpose()
    y = resolve(l,b,'front')#resolve LY=B
    x = resolve(u,y,'back')#than LtX=|Y
    x = np.around(x,decimals = 1)
    return x

def main():
    f = open('results/result_1.txt','w',encoding='utf-8')
    results = []
    matA = input('Digite a matriz A linearizada com os valores separados por , : ').split(',')
    size = int(math.sqrt(len(matA)))
    matA = list(map(lambda x: float(x),matA))
    matA = np.array(matA).reshape((size,size))
    print(matA)
    results.append('A =\n%s'%np.array2string(matA,separator= ' '))
    matB = input('Digite a matriz B linearizada com os valores separados por , : ').split(',')
    matB = list(map(lambda x: float(x),matB))
    matB = np.array(matB)
    results.append('B_T = %s'%np.array2string(matB,separator= ' '))
    if check_singularity(matA):
        print('Erro. Há singularidade na matriz informada.')
        results.append('Erro. Há singularidade na matriz informada.')
    else:
        print('Resolução por LU')
        results.append('Resolução por LU')
        x = decomp_LU(matA,matB)
        print('X_T = %s'%np.array2string(x.transpose(),separator=' '))
        results.append('x = %s'%np.array2string(x.transpose(),separator=' '))
        print('Resolução por Cholesky')
        results.append('Resolução por Cholesky')
        x = decomp_Cholesky(matA,matB)
        if type(x) is str:
            print(x)
            results.append(x)
        else:
            print('X_T = %s'%np.array2string(x.transpose(),separator=' '))
            results.append('x = %s'%np.array2string(x.transpose(),separator=' '))
    f.write('\n'.join(results))
    f.close()

if __name__ == "__main__":
    main()


import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
from utils import sigmoid , cost_function_logistic_regression_J

X1=np.array([]) # grade 1
X2=np.array([]) # grade 2
Y=np.array([]) # admitted to university
file = open("data\\ex2data1.txt", "r")
for row in file:
  ar = row.split(",")
  X1 = np.append(X1,float(ar[0]))
  X2 = np.append(X2,float(ar[1]))
  Y = np.append(Y,float(ar[2]))

scale_factor = 100

# using scale_factor of 1 will cause an error "Desired error not necessarily achieved due to precision loss."
# the problem is that X0 is 1 while X1,X2 are between 0 - 100 so 100 scale them to be around 1

X1 = X1/scale_factor
X2 = X2/scale_factor

m = Y.size # should be same as the size of X1,X2
X0= np.ones(m)

X= np.vstack((X0,X1,X2)).T
res = optimize.minimize(cost_function_logistic_regression_J, x0=[0,0,0], args=(X,Y))
print(res)

[teta0,teta1,teta2] = res.x

def pass_prob(x1,x2):
  teta_x = teta0 + teta1*x1/scale_factor + teta2*x2/scale_factor
  return sigmoid(teta_x)

print("pass_prob(95,95) : ",pass_prob(95,95))

ar_index_pass = np.where(Y == 1)
ar_index_fail = np.where(Y == 0)

x2_0_5_line = -(teta0+teta1*X1)/teta2 # prob 0.5

plt.title('+ : pass , o : fail \ncomputed optimize.minimize 0.5 logistic regression probability line')
plt.xlabel("grade1")
plt.ylabel("grade2")
plt.plot(scale_factor*X1[ar_index_pass],scale_factor*X2[ar_index_pass],'+')
plt.plot(scale_factor*X1[ar_index_fail],scale_factor*X2[ar_index_fail],'o')
plt.plot(scale_factor*X1,scale_factor*x2_0_5_line)
plt.show()


import numpy as np

def gram_schmidt(matrix):
    """
    Orthogonalize a set of vectors stored as the rows of matrix
    returns orthogonal basis and gram schmidt coefficients
    """
    ortho = matrix.copy()
    n = ortho.shape[0]
    u = np.zeros(ortho.shape)  # also store gram schmidt coefficients
    for i in range(n):
        # To orthogonalize the vector in column j with respect to the previous vectors,
        # subtract from it its projection onto each of the previous vectors.
        for k in range(i):
            gs_coefficient = np.dot(ortho[:, k], ortho[i, :])
            u[i][k] = gs_coefficient
            ortho[i, :] -= gs_coefficient * ortho[k, :]
        # ortho[i, :] = ortho[i, :] / np.linalg.norm(ortho[i, :])  # This line would make it an orthonormal basis
    return ortho, u

if __name__ == '__main__':
    A = np.array([[1.0, 1.0, 0.0], [1.0, 3.0, 1.0], [2.0, -1.0, 1.0]])
    b, u = gram_schmidt(A)
    print(A)
    print(b)
    print(u)


""" In this module, we will extract the four fundamental notions for
supervised learning, through an example of linear regression on
two-dimensional data.

"""

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_regression

def model(data_x, parameters):
    """ This function returns the linear model

    """
    return data_x @ parameters

def cost_function(data_x, data_y, parameters):
    """ Returns the cost function for our model.

    """
    # Residual sum of squares
    rss = np.sum((data_x @ parameters - data_y) ** 2)

    # Number of rows times 2
    nb_rows_x2 = 2 * data_x.shape[0]

    return rss / nb_rows_x2

def gradient(data_x, data_y, parameters):
    """ Returns gradient of a function.

    """
    return data_x.T @ (data_x @ parameters - data_y) / data_x.shape[0]

def gradient_descent(data_x, data_y, parameters, learn_rate, nb_iterations):
    """ Finding a local minimum of a differentiable function by gradient
    descent algorithm.

    """

    # Cost history
    cost_tracking = np.zeros(nb_iterations)

    for _i in range(nb_iterations):
        parameters -= learn_rate * gradient(data_x, data_y, parameters)
        # recording the cost for each iteration
        cost_tracking[_i] = cost_function(data_x, data_y, parameters)

    return parameters, cost_tracking

def coefficient_determination(data_y, prediction):
    """ Coefficient of determination.

    This function measures the quality of the prediction of linear regression.

    """
    sum_numerator = np.sum((data_y - prediction) ** 2)
    sum_denominator = np.sum((data_y - prediction.mean()) ** 2)
    return 1 - sum_numerator / sum_denominator

def main():
    """ Example of linear regression

    """
    ###################
    ##    Dataset    ##
    ###################
    # (X, y)  m = 100, n = 1
    _data_x, data_y = make_regression(n_samples=100, n_features=1, noise=10)

    # show the dataset
    plt.subplot(2, 2, 1)
    plt.title("dataset")
    plt.scatter(_data_x, data_y)

    # Transform the dataset into matrices.
    # That is used for writing the equations in the matrix form.
    data_x = np.hstack((_data_x, np.ones(_data_x.shape)))
    data_y = data_y.reshape(data_y.shape[0], 1)

    #################
    ##    Model    ##
    #################
    # initial parameters
    init_params = np.random.randn(2, 1)

    # initial model
    init_model = model(data_x, init_params)

    # plot initial model
    plt.subplot(2, 2, 2)
    plt.title("initial model")
    plt.scatter(_data_x, data_y)
    plt.plot(_data_x, init_model, c='g')

    #########################
    ##    cost function    ##
    #########################
    # show cost function for initial parameters
    print(cost_function(data_x, data_y, init_params))

    ####################
    ##    training    ##
    ####################
    # learning rate
    learn_rate = 0.005
    # number of iterations
    number_iterations = 1_000

    # final parameters for our model
    final_params, cost_tracking = gradient_descent(
        data_x, data_y, init_params, learn_rate, number_iterations)

    # final model
    final_model = model(data_x, final_params)

    # show cost function for final parameters
    print(cost_function(data_x, data_y, final_params))

    # plot final model
    plt.subplot(2, 2, 3)
    plt.title("final model")
    plt.scatter(_data_x, data_y)
    plt.plot(_data_x, final_model, c='r')

    ##########################
    ##    learning curve    ##
    ##########################
    # plot Cost history
    plt.subplot(2, 2, 4)
    plt.title("cost tracking")
    plt.plot(range(number_iterations), cost_tracking)

    ########################################
    ##    Coefficient of determination    ##
    ########################################
    print(coefficient_determination(data_y, final_model))

    plt.show()

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
The Cube

Number of average steps from node 1 back to node 1
Should be 8!

"""
from math import sqrt
from itertools import count, islice

def isPrime(n):
    return n > 1 and all(n%i for i in islice(count(2), int(sqrt(n)-1)))

import numpy as np
from numpy.linalg import matrix_power

# matrix of graph of cube
A = np.matrix([[0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0 ],
            [1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0 ],
            [0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ],
            [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0 ],
            [0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 ],
            [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0 ],
            [0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0 ],
            [0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]])

# solve it using 8 simulateous equations using
# n_{1} = average number of steps from one to one
# n_{2} = average number of steps from one to two
# ....
# Since we start at one and must make one step
# The average number of steps from n_{1} will be
# 1 + the average from adjancent nodes
# 3 options with equal chance means (1/3) from each node
# hence:
# n_{1} = 1 + (1/3)(n_{2} + n_{4} + n_{6})
# similarly this applies for each other node
# n_{2} = 1 + (1/3)(n_{1} + n_{3} + n_{7})
# ....

# Remove the first row and column using the trival solution of n_{1}=0
scale_factor = (1.0/3.0)
subA=A
Bdash = scale_factor*A - np.identity(8)
# print(np.linalg.det(Bdash))
subA = np.delete(subA,0,0)
subA = np.delete(subA,0,1)
# print(subA)

B = (scale_factor*subA - np.identity(7))
print(B)

sol_vec = -B.I*np.ones((7,1))
# values for all other nodes, excluding n_{1}
print(sol_vec)
# we then compute n_{1} by using the solution vector in equation 1
# n_{1} = 1 + (1/3)(n_{2} + n_{4} + n_{6})
# note that index 0 now points to 2, since the first node was removed
# (A-I).I has no inverse -> (A-np.identity(8)).I
solution = 1.0 + scale_factor*(sol_vec[0] +sol_vec[2] +sol_vec[4])
print("First method solution is: {}".format(int(solution)))
print("======================\n")

# Solution 2:
# since n2 = n4 = n6 and n3 = n5 = n7 due to symmetry
# we can reduce matrix to:
A2 =np.matrix([[0., 1., 0., 0.],
                [1./3., 0., 2./3., 0.],
                [0., 2./3., 0., 1./3],
                [0., 0., 1., 0.]])
subA2=A2
subA2 = np.delete(subA2,0,0)
subA2 = np.delete(subA2,0,1)
sol_vec2 = -(subA2 - np.identity(3)).I*np.ones((3,1))
print((subA2 - np.identity(3)).I)
print(sol_vec2)
solution2 = 1 + sol_vec2[0]
print("Second method solution is: {}".format(int(solution2)))
print("======================\n")

# Solution 3:
# Use graph theory
# The matrix represents the graph and the product of
# the matrix on the unit vector for n_{1} = [1, 0, 0,.....]
# gives the number of paths from n_{1} to another node
# number of paths from 1 to another node

def computewalk(mat, nsteps):
    test_vector = np.zeros((8,1))
    test_vector[0] = 1

    full_mat = matrix_power(mat, nsteps)
    # print(full_mat)
    vec = full_mat*test_vector
    prob = vec[0]/sum(vec)
    return prob[0,0], vec

# if we take one step from node 1 we can go to 2, 4, 6 each by one way
# hence we get [0,1,0,1,0,1,0,0]
probs = []
values = []
for i in range(2, 100, 2):
    print("{} step...".format(i))
    prob, vec = computewalk(A, i)
    print(prob, vec[0,0])
    probs.append((i, prob))
    values.append((i, vec[0,0]))

import matplotlib.pyplot as plt

f = plt.figure()
X, Y = map(list, zip(*probs))
plt.plot(X, Y, 'k', alpha=0.7)
f = plt.figure()
X, Y = map(list, zip(*values))
plt.semilogy(X, Y, 'ko', alpha=0.7)
plt.xlabel("number of steps (k)", fontsize=16)
plt.ylabel("number of paths 1 -> 1", fontsize=16)
plt.show()
# we therefore need to sum over all paths that lead to n_{1}
# only even number of steps lead back to one
# 2 steps -> 3 ways (1*3)
# 4 steps -> 21 ways (7*3)
# 6 steps -> 183 ways (61*3)
# 8 steps -> 1641 ways (547*3)
# it is wierd that the number of paths are the product of
# exactly 2 primes (always?) and one is 3 (number of adjacent nodes)
#
# not the case for 10 steps
# 10 steps -> 14763 ways (7*19*37*3)
#
# Is the probability of the number of steps the inverse of the number of paths?
# It is clear for 2 steps, since 1/3 of the time it must be 2 steps
# but then all the other paths must have prob of 2/3
#
# the probability should be the number of paths to n_{1}
# divided by the total number of paths from origin in m steps
#
# todo: compute the average based on weighted average
# i.e ave = (2*0.3 + 4*.....)/total number of paths
# total number of paths is infinite though
# we do know that 1 + 2 + 3+ .... = -1/12 although divergent

# extra below work for solution 1

#expected solution
# expected_solution=np.matrix([[0.0,7.0,9.0,7.0,9.0,7.0,9.0,10.0]])
# print(scale_factor*A*expected_solution.T + 1.0)

# n1 = lambda n: 1.0 + scale_factor*(n[0,1] + n[0,3] + n[0,5] )
# n2 = lambda n: 1.0 + scale_factor*(n[0,0] + n[0,2] + n[0,6] )
# n3 = lambda n: 1.0 + scale_factor*(n[0,1] + n[0,3] + n[0,7] )
# n4 = lambda n: 1.0 + scale_factor*(n[0,0] + n[0,2] + n[0,4] )
# n5 = lambda n: 1.0 + scale_factor*(n[0,3] + n[0,5] + n[0,7] )
# n6 = lambda n: 1.0 + scale_factor*(n[0,0] + n[0,4] + n[0,6] )
# n7 = lambda n: 1.0 + scale_factor*(n[0,1] + n[0,5] + n[0,7] )
# n8 = lambda n: 1.0 + scale_factor*(n[0,2] + n[0,4] + n[0,6] )

# print(n2(expected_solution))


# -*- coding: utf-8 -*-
import sympy as sy
from .testfunction import TestMinFunction
from .meta import symbolize, substitute

def Rosenbrock(a=1, b=100):
    """
    Creates the Rosenbrock function object.

    f(x,y) = (a-x)**2 + b*(y-x**2)**2

    a,b are constants, tipically a is set to 1 and  b is set to 100.

    The global minimum
    f(1,1) = 0.
    """
    str_expr = '(a-x)**2 + b*(y-x**2)**2'
    expr = substitute(sy.sympify(str_expr), [a, b], ['a', 'b'])
    return TestMinFunction(**symbolize(expr=expr), optimums=[(1.0, 1.0)])

def Himmelblau():
    """
    Creates the Himmelblau's function object.

    f(x,y) = (x**2 + y - 11)**2 + (x + y**2 - 7)**2

    It has four identical local minima with values 0.0 at
    (3.0,2.0),
    (-2.805,3.131),
    (-3.779,-3.283),
    (3.584,-1.848).
    """
    str_expr = '(x**2 + y - 11)**2 + (x + y**2 - 7)**2'
    return TestMinFunction(**symbolize(str_expr=str_expr),
                           optimums=[(3.0, 2.0), (-2.805118, 3.131312),
                                     (-3.779310, -3.283186),
                                     (3.584428, -1.848126)])

def GoldsteinPrice():
    """
    Creates the Goldstein-Price function object.

    f(x,y) = (1+(x+y+1)**2 * (19-14*x+3*x**2-14*y+6*x*y+3*y**2))
    *(30+(2*x-3*y)**2 * (18-32*x+12*x**2+48*y-36*x*y+27*y**2))

    The global minimum
    f(0,-1) = 3.
    """
    str_expr = '(1+(x+y+1)**2 * (19-14*x+3*x**2-14*y+6*x*y+3*y**2))' \
        '*(30+(2*x-3*y)**2 * (18-32*x+12*x**2+48*y-36*x*y+27*y**2))'
    return TestMinFunction(**symbolize(str_expr=str_expr),
                           optimums=[(0.0, -1.0)])

def Beale():
    """
    Creates the Beale function object.

    f(x,y) = (1.5 - x + x*y)**2 + (2.25 - x + x*y**2)**2
    + (2.625 - x + x*y**3)**2

    The global minimum
    f(3,0.5) = 0.
    """
    str_expr = '(1.5 - x + x*y)**2 + (2.25 - x + x*y**2)**2 ' \
        '+ (2.625 - x + x*y**3)**2'
    return TestMinFunction(**symbolize(str_expr=str_expr),
                           optimums=[(3.0, 0.5)])

def Matyas():
    """
    Creates the Matyas function object.

    f(x, y) = 0.26*(x**2 + y**2) - 0.48*x*y

    The global minimum
    f(0.,0.) = 0.
    """
    str_expr = '0.26*(x**2 + y**2) - 0.48*x*y'
    return TestMinFunction(**symbolize(str_expr=str_expr),
                           optimums=[(0.0, 0.0)])

if __name__ == '__main__':

    """
    check:
        - Himmelblau with initial = [-1.,-1.]. Problem with the Hessian
          at [-0.270845, -0.923039].

    """


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

class NN:
  def __init__(self, NI, NH, NO):
    # number of nodes in layers
    self.ni = NI + 1 # +1 for bias
    self.nh = NH
    self.no = NO

    # initialize node-activations
    self.ai, self.ah, self.ao = [],[], []
    self.ai = [1.0]*self.ni
    self.ah = [1.0]*self.nh
    self.ao = [1.0]*self.no

    # create node weight matrices
    self.wi = makeMatrix (self.ni, self.nh)
    self.wo = makeMatrix (self.nh, self.no)

    # initialize node weights to random vals
    randomizeMatrix ( self.wi, -0.2, 0.2 )
    randomizeMatrix ( self.wo, -2.0, 2.0 )

    # create last change in weights matrices for momentum
    self.ci = makeMatrix (self.ni, self.nh)
    self.co = makeMatrix (self.nh, self.no)

  def runNN (self, inputs):
    if len(inputs) != self.ni-1:
      print('incorrect number of inputs')

    for i in range(self.ni-1):
      self.ai[i] = inputs[i]

    for j in range(self.nh):
      sum = 0.0
      for i in range(self.ni):
        sum +=( self.ai[i] * self.wi[i][j] )
      self.ah[j] = sigmoid (sum)

    for k in range(self.no):
      sum = 0.0
      for j in range(self.nh):
        sum +=( self.ah[j] * self.wo[j][k] )
      self.ao[k] = sigmoid (sum)

    return self.ao

  def backPropagate (self, targets, N, M):
    # http://www.youtube.com/watch?v=aVId8KMsdUU&feature=BFa&list=LLldMCkmXl4j9_v0HeKdNcRA

    # calc output deltas
    # we want to find the instantaneous rate of change of ( error with respect to weight from node j to node k)
    # output_delta is defined as an attribute of each ouput node. It is not the final rate we need.
    # To get the final rate we must multiply the delta by the activation of the hidden layer node in question.
    # This multiplication is done according to the chain rule as we are taking the derivative of the activation function
    # of the ouput node.
    # dE/dw[j][k] = (t[k] - ao[k]) * s'( SUM( w[j][k]*ah[j] ) ) * ah[j]
    output_deltas = [0.0] * self.no
    for k in range(self.no):
      error = targets[k] - self.ao[k]
      output_deltas[k] =  error * dsigmoid(self.ao[k])

    # update output weights
    for j in range(self.nh):
      for k in range(self.no):
        # output_deltas[k] * self.ah[j] is the full derivative of dError/dweight[j][k]
        change = output_deltas[k] * self.ah[j]
        self.wo[j][k] += N*change + M*self.co[j][k]
        self.co[j][k] = change

    # calc hidden deltas
    hidden_deltas = [0.0] * self.nh
    for j in range(self.nh):
      error = 0.0
      for k in range(self.no):
        error += output_deltas[k] * self.wo[j][k]
      hidden_deltas[j] = error * dsigmoid(self.ah[j])

    #update input weights
    for i in range (self.ni):
      for j in range (self.nh):
        change = hidden_deltas[j] * self.ai[i]
        #print 'activation',self.ai[i],'synapse',i,j,'change',change
        self.wi[i][j] += N*change + M*self.ci[i][j]
        self.ci[i][j] = change

    # calc combined error
    # 1/2 for differential convenience & **2 for modulus
    error = 0.0
    for k in range(len(targets)):
      error = 0.5 * (targets[k]-self.ao[k])**2
    return error

  def weights(self):
    print('Input weights:')
    for i in range(self.ni):
      print(self.wi[i])
    print()
    print('Output weights:')
    for j in range(self.nh):
      print(self.wo[j])
    print()

  def test(self, patterns):
    for p in patterns:
      inputs = p[0]
      print ('Inputs:', p[0], '-->', self.runNN(inputs), '\tTarget', p[1])

  def train(self, patterns, max_iterations = 1000, N=0.5, M=0.1):

    error_iteractions = []
    for i in range(max_iterations):
      for p in patterns:
        inputs = p[0]
        targets = p[1]
        self.runNN(inputs)
        error = self.backPropagate(targets, N, M)
        error_iteractions.append([i,error])
      if i % 50 == 0:
        print('Combined error', error)
    # self.test(patterns)
    return error_iteractions

def sigmoid (x):
  return math.tanh(x)

# the derivative of the sigmoid function in terms of output
# proof here:
# http://www.math10.com/en/algebra/hyperbolic-functions/hyperbolic-functions.html
def dsigmoid (y):
  return 1 - y**2

def makeMatrix ( I, J, fill=0.0):
  m = []
  for i in range(I):
    m.append([fill]*J)
  return m

def randomizeMatrix ( matrix, a, b):
  for i in range ( len (matrix) ):
    for j in range ( len (matrix[0]) ):
      matrix[i][j] = random.uniform(a,b)

def main (train_set=[], test_set=[],filename='nn_train.png'):

  first_layer = len(train_set[0][0])
  myNN = NN (first_layer, 10, 1)
  errors = myNN.train(train_set)

  errors = np.array(errors)
  plt.plot(errors[:,0], errors[:,1],'-')
  plt.ylabel("Error")
  plt.xlabel("Iteration")
  plt.show()
  plt.savefig(filename)

  myNN.test(test_set)

if __name__ == "__main__":
  main()


#autor: Rodolfo Quispe
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize

def f(x, w):
  return w[0]*(x**3) + w[1]*(x**2) + w[2]*(x) + w[3]

def cost(w, x, y):
  return ((f(x, w) - y)**2).sum()

def plot_results(x, y, w):
  fig = plt.figure(figsize=(12, 6))
  fig.add_subplot(111)

  # Plot the target t versus the input x
  plt.plot(x, y, 'o', label='data')
  # Plot the initial line
  x = list(range(-10, 10))
  plt.plot(x, [f(i,w) for i in x],'g-',label='f(x)')

  #set extra plot parameters
  plt.title('Question 5: Nelder-Mead')
  plt.xlim(-6,6)
  plt.ylim(-60,60)
  plt.xlabel('x')
  plt.ylabel('f(x)', fontsize=15)
  plt.legend(loc=2)
  plt.grid()

  plt.show()
  fig.savefig("t01_5.png")

if __name__ == "__main__":

  x_train = np.array([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5])
  y_train = np.array([-53.9, -28.5, -20.7, -3.6, -9.8, 5.0,
                        4.2, 5.1, 11.4, 27.4, 44.0])
  #minimize fuction
  w = np.array([0, 0, 0 , 0])

  # minimize fuction
  res = minimize(cost, w, args=(x_train, y_train, ),
          method='nelder-mead', options={'disp': True})

  # show results
  print (res)
  plot_results(x_train, y_train, res.x)


# test copied from https://emcee.readthedocs.io/en/stable/tutorials/line/

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(123)

# Choose the "true" parameters.
m_true = -0.9594
b_true = 4.294
f_true = 0.534

# Generate some synthetic data from the model.
N = 50
x = np.sort(10 * np.random.rand(N))
yerr = 0.1 + 0.5 * np.random.rand(N)
y = m_true * x + b_true
y += np.abs(f_true * y) * np.random.randn(N)
y += yerr * np.random.randn(N)

# now least square fit

A = np.vander(x, 2)
C = np.diag(yerr * yerr)
ATA = np.dot(A.T, A / (yerr ** 2)[:, None])
cov = np.linalg.inv(ATA)
w = np.linalg.solve(ATA, np.dot(A.T, y / yerr ** 2))
print("Least-squares estimates:")
print("m = {0:.3f} ± {1:.3f}".format(w[0], np.sqrt(cov[0, 0])))
print("b = {0:.3f} ± {1:.3f}".format(w[1], np.sqrt(cov[1, 1])))

# now maximum likelihood estimation

def log_likelihood(theta, x, y, yerr):
    m, b, log_f = theta
    model = m * x + b
    sigma2 = yerr ** 2 + model ** 2 * np.exp(2 * log_f)
    return -0.5 * np.sum((y - model) ** 2 / sigma2 + np.log(sigma2))

from scipy.optimize import minimize

np.random.seed(42)
nll = lambda *args: -log_likelihood(*args)
initial = np.array([m_true, b_true, np.log(f_true)]) + 0.1 * np.random.randn(3)
soln = minimize(nll, initial, args=(x, y, yerr))
m_ml, b_ml, log_f_ml = soln.x

print("Maximum likelihood estimates:")
print("m = {0:.3f}".format(m_ml))
print("b = {0:.3f}".format(b_ml))
print("f = {0:.3f}".format(np.exp(log_f_ml)))

# now Bayesian

def log_prior(theta):
    m, b, log_f = theta
    if -5.0 < m < 0.5 and 0.0 < b < 10.0 and -10.0 < log_f < 1.0:
        return 0.0
    return -np.inf

def log_probability(theta, x, y, yerr):
    lp = log_prior(theta)
    if not np.isfinite(lp):
        return -np.inf
    return lp + log_likelihood(theta, x, y, yerr)

# sampling posterior
import emcee

pos = soln.x + 1e-4 * np.random.randn(32, 3)
nwalkers, ndim = pos.shape
sampler = emcee.EnsembleSampler(nwalkers, ndim, log_probability, args=(x, y, yerr))
sampler.run_mcmc(pos, 5000, progress=True);
flat_samples = sampler.get_chain(discard=100, thin=15, flat=True)
inds = np.random.randint(len(flat_samples), size=100)

# plotting
x0 = np.linspace(0, 10, 500)
legend = 0

for ind in inds:
    sample = flat_samples[ind]
    if legend == 0 :
        plt.plot(x0, np.dot(np.vander(x0, 2), sample[:2]), "C1", alpha=0.1, label="Bayesian")
        legend = 1
    else :
        plt.plot(x0, np.dot(np.vander(x0, 2), sample[:2]), "C1", alpha=0.1)

plt.errorbar(x, y, yerr=yerr, fmt=".k", capsize=0)
x0 = np.linspace(0, 10, 500)
plt.plot(x0, m_true * x0 + b_true, "k", alpha=0.3, lw=3, label="truth")
plt.plot(x0, np.dot(np.vander(x0, 2), w), "--k", label="LS")
plt.plot(x0, np.dot(np.vander(x0, 2), [m_ml, b_ml]), ":k", label="ML")
plt.legend(fontsize=14)
plt.xlim(0, 10)
plt.xlabel("x")
plt.ylabel("y");

#plt.show()
plt.savefig('test_emcee_2_fig1.svg', format='svg', dpi=1200)
plt.close()

# now arviz plots
import arviz as az

var_names = ['m','b','f']
emcee_data = az.from_emcee(sampler, var_names=var_names)
az.plot_posterior(emcee_data, var_names=var_names[:])
#plt.show()
plt.savefig('test_emcee_2_fig2.svg', format='svg', dpi=1200)
plt.close()

# now trace plot
az.plot_trace(emcee_data, var_names=var_names)
#plt.show()
plt.savefig('test_emcee_2_fig3.svg', format='svg', dpi=1200)
plt.close()

az.plot_pair(emcee_data, var_names=var_names, kind='kde')
#plt.show()
plt.savefig('test_emcee_2_fig4.svg', format='svg', dpi=1200)
plt.close()


import numpy as np

class LinearRegression:
    def __init__(self, x, y , alpha=0.3, iteration=1500):
        self.alpha = alpha
        self.iteration = iteration
        self.m = len(y)
        self.features = np.size(x, 1)
        self.x = np.hstack((np.ones((self.m,1)), (x-np.mean(x,0))/np.std(x, 0)))
        self.y = y[:, np.newaxis]
        self.theta = np.zeros((self.features+1, 1))
        self.coeff=None
        self.interccept=None

    def fit(self):
        for _ in range(self.iteration):
            self.theta = self.theta - (self.alpha/self.m)*self.x.T @ (self.x @ self.theta - self.y)
        self.interccept=self.theta[0]
        self.coeff=self.theta[1:]
        return self

    def theta_value(self):
        return self.theta

    def predict(self, x):
        samples=np.size(x,0)
        y = np.hstack((np.ones((samples, 1)), (x-np.mean(x, 0)) / np.std(x, 0))) @ self.theta
        return y

    def score(self, x=None, y=None):
        if x is None :
            x=self.x
        else:
            samples=np.size(x,0)

        if y is None:
            y=self.y
        else:
            y=y[:,np.newaxis]

        y_pred = x @ self.theta
        score = 1 - (((y-y_pred)**2).sum() / ((y-np.mean(y))**2).sum())
        return score


"""
    create a function that returns True if vertex i and vertex j
    are connected in the graph represented by the input adjacency matrix A
"""

import numpy as np

def isConnected(A: np.array, i: int, j: int) -> bool:

    paths = A       # initialize the paths matrix to adjacency matrix A
    number_vertices = A.shape[0]    # find the number of vertices in the graph
    number_edges = np.sum(A) / 2    # find the number of edges in the graph

    # if node vi and vj are adjacent, return True
    if paths[i-1][j-1] > 0:
        print(f'Vertex {i} and vertex {j} are adjacent.')
        return True

    else:
        # run the loop until we find a path
        for pathLength in range(2, number_vertices):
            paths = np.dot(paths, A)        # exponentiate the adjacency matrix
            if paths[i - 1][j - 1] > 0:
                print(f'There is a path with {pathLength} edges from vertex {i} to vertex {j}.')
                return True
            # found no paths, the vertices are not connected
            if pathLength == number_edges:
                print(f'There are no paths from vertex {i} to vertex {j}.')
                return False

def main():
    A1 = np.array([[0, 1, 1, 0, 1, 0], [1, 0, 1, 1, 0, 1],
                   [1, 1, 0, 1, 1, 0], [0, 1, 1, 0, 1, 0],
                   [1, 0, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0]])
    print(isConnected(A1, 1, 4))
    print(isConnected(A1, 2, 3))
    print(isConnected(A1, 5, 6))

    A2 = np.array([[0, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1],
                   [0, 0, 0, 1, 1, 0], [0, 0, 1, 0, 1, 0],
                   [0, 0, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0]])
    print(isConnected(A2, 1, 6))
    print(isConnected(A2, 2, 5))
    print(isConnected(A2, 1, 4))

"""main()      # test the code"""


import numpy as np
from numpy.linalg import norm

def degrees(radians):
	"""
	Converts the given angle from radians to degrees

	Args:
		radians (float): angle in radians

	Returns:
		float: resulting angle in degrees
	"""
	return np.degrees(radians)

def radians(degrees):
	"""
	Converts a given angle from degrees to radians

	Args:
		degrees (float): angle in degrees

	Returns:
		float: resulting angle in radians
	"""
	return np.radians(degrees)

class Point():
	"""
		Class implementing support for point operations in three
		dimensional world coordinates
	"""

	def __init__(self,x,y,z):
		self.x, self.y, self.z = x,y,z

	def copy(self):
		"""
		Creates a copy of itself

		Returns:
			Point: object copied from self
		"""
		return Point(self.x,self.y,self.z)

	def distance(self, to):
		"""
		Calculates the distance from this point to the given one

		Args:
			to (Point, float): end point to calculate distance to

		Returns:
			float: distance to the given point
		"""
		return float(np.sqrt(
			(self.x - to.x) ** 2 +
			(self.y - to.y) ** 2 +
			(self.z - to.z) ** 2 ))

	def move(self, by, distance):
		"""
		Translates this point by a vector and a given distance

		Args:
			by (str, Vector): axis name ("x", "y" or "z") or vector describing translate direction
			distance (float): value describing distance

		Returns:
			Point: translated point object
		"""

		if type(by) is str:
			by = Vector.from_axis(by)

		return Point(
			self.x + distance * by.i,
			self.y + distance * by.j,
			self.z + distance * by.k )

	def intersects(self, plane):
		"""
		Same as **dicom3d.Plane.intersects**, verifies point intersection with a plane

		Args:
			plane (Plane): plane to verify intersection with

		Returns:
			bool: **True** if intersects plane, **False** otherwise
		"""
		return plane.intersects(self)

	def __len__(self):
		return 3

	def __getitem__(self, item):
		if item == 0: return self.x
		if item == 1: return self.y
		if item == 2: return self.z
		raise ValueError("Point class has no item '%s'" % (item))

	def __iter__(self):
		yield self.x
		yield self.y
		yield self.z

	def __repr__(self):
		return "x:%.2f y:%.2f z:%.2f" % (self.x,self.y,self.z)

class Vector():
	"""
		Class responsible for providing support for vectorial
		algebra
	"""

	def __init__(self, i=0, j=0, k=0):
		self.matrix = np.array([[i],[j],[k]], dtype=np.float)

	# PROPERTIES #
	def _geti(self): return self.matrix[0,0]
	def _getj(self): return self.matrix[1,0]
	def _getk(self): return self.matrix[2,0]
	def _seti(self,v): self.matrix[0,0] = v
	def _setj(self,v): self.matrix[1,0] = v
	def _setk(self,v): self.matrix[2,0] = v

	i = property(_geti, _seti)
	"""
		Property describing the **i** component of the vector
	"""
	j = property(_getj, _setj)
	"""
		Property describing the **j** component of the vector
	"""
	k = property(_getk, _setk)
	"""
		Property describing the **k** component of the vector
	"""

	@staticmethod
	def from_numpy(array):
		"""
		Constructs a vector from a numpy array of shape (3,1)

		Args:
			array (numpy.array): numpy array to import data from

		Returns:
			Vector: resulting vector object
		"""
		assert(array.shape == (3,1))

		i,j,k = array.T[0]
		return Vector(i,j,k)

	@staticmethod
	def from_plane(plane):
		"""
		Copies the plane normal vector

		Args:
			plane (Plane): plane argument

		Returns:
			Vector: resulting vector object
		"""
		return plane.normal.copy()

	@staticmethod
	def from_coords(origin, target):
		"""
		Constructs a vector from two points in space

		Args:
			origin (Point, tuple): origin or source point
			target (Point, tuple): target or end point

		Returns:
			Vector: resulting vector object
		"""
		xo,yo,zo = origin
		xt,yt,zt = target
		return Vector(xt-xo, yt-yo, zt-zo)

	@staticmethod
	def from_axis(axis):
		"""
		Constructs a vector from a given Axis definition

		Args:
			axis (str): axis name ("x", "y" or "z")

		Raises:
			ValueError: when an invalid axis name is passed

		Returns:
			Vector: resulting vector
		"""
		if len(axis) != 1:
			raise ValueError("invalid axes")

		axis = axis.lower()

		if axis == "z":
			return Vector.from_coords( (0,0,0), (0,0,1) )
		if axis == "y":
			return Vector.from_coords( (0,0,0), (0,1,0) )
		if axis == "x":
			return Vector.from_coords( (0,0,0), (1,0,0) )

		raise ValueError("invalid axes")

	def copy(self):
		"""
		Creates a copy of itself

		Returns:
			Vector: resulting vector object
		"""
		return Vector(*self.tuple())

	def equals(self, v):
		"""
		Verifies if a vector is identical with the given one

		Args:
			v (Vector): vector to verify equality with

		Returns:
			bool: **True** if equal, **False** otherwise
		"""
		return self.tuple() == v.tuple()

	def round(self, digits=5):
		"""
		Rounds the values of **i**, **j** and **k** with a given precision

		Args:
			digits (int, optional): number of digits to round to, defaults to 5.

		Returns:
			Vector: resulting vector object
		"""
		return Vector(
			round(self.i, digits),
			round(self.j, digits),
			round(self.k, digits))

	def numpy(self):
		"""
		Returns the corresponding numpy array for this vector

		Returns:
			numpy.array: corresponding numpy array
		"""
		return self.matrix

	def dot(self,v):
		"""
		Calculates the dot product of a vector with a given one

		Args:
			v (Vector): dot product argument

		Returns:
			Vector: resulting vector object
		"""
		return self.matrix.T.dot(v.matrix)[0,0]

	def mul(self,v):
		"""
		Multiplies a vector with a scalar value

		Args:
			v (float, int): scalar value to multiply by

		Returns:
			Vector: resulting vector object
		"""
		return Vector(self.i * v, self.j * v, self.k * v)

	def div(self,v):
		"""
		Divides a vector by a scalar value

		Args:
			v (float, int): scalar value to divide by

		Returns:
			Vector: resulting vector object
		"""
		return Vector(self.i / v, self.j / v, self.k / v)

	def norm(self):
		"""
		Calculates the length of the vector

		Returns:
			float: length of vector
		"""
		return norm(self.matrix)

	def unit(self):
		"""
		Returns the unit vector corresponding to this vector

		Returns:
			Vector: resulting vector object
		"""
		return Vector.from_numpy(self.matrix / norm(self.matrix))

	def invert(self):
		"""
		Returns a vector pointing in the opposite direction

		Returns:
			Vector: resulting vector object
		"""
		return Vector(-self.i, -self.j, -self.k)

	def tuple(self):
		""" convers a vector to a tuple """
		return (self.matrix[0][0], self.matrix[1][0], self.matrix[2][0])

	def angle(self, vector):
		"""
		Calculates the angle in radians, between this vector and the
		one passed as argument

		Args:
			vector (Vector): argument vector

		Returns:
			float: angle measured in radians
		"""
		i,j,k = vector.tuple()
		x,y,z = self.tuple()

		n1n2 = abs(x*i + y*j + z*k)
		n1   = np.sqrt(x ** 2 + y ** 2 + z ** 2)
		n2   = np.sqrt(i ** 2 + j ** 2 + k ** 2)

		return np.arccos(n1n2 / (n1 * n2))

	def rotate_by_vector(self, vector, angle):
		"""
		Construct a vector that represents the current one rotated around a
		given vector

		Args:
			vector (Vector): pivot vector to perform rotation about
			angle (float): angle measured in radians

		Returns:
			Vector: rotated vector object
		"""

		cos = np.cos(angle)
		sin = np.sin(angle)

		ux,uy,uz = vector.unit()
		u2x,u2y,u2z = ux * ux, uy * uy, uz * uz

		rotation = np.array([
				[
					cos + u2x * (1-cos)           ,
				  	ux  *  uy * (1-cos) - uz * sin,
				  	ux  *  uz * (1-cos) + uy * sin
				],
				[
					uy * ux * (1-cos) + uz * sin   ,
					cos + u2y * (1-cos)            ,
					uy  *  uz * (1-cos) - ux * sin
				],
				[
					uz * ux * (1-cos) - uy * sin,
					uz  *  uy * (1-cos) + ux * sin ,
					cos + u2z * (1-cos)
				]
			])

		return vector.from_numpy(self.matrix.T.dot(rotation).T)

	def rotate_by_axis(self, axis, angle):
		"""
		Construct a vector that represents the current one rotated about
		a given axis

		Args:
			axis (str): axis name definition ("x", "y" or "z")
			angle (float): angle measured in radians

		Raises:
			ValueError: if an invalid axis name is passed

		Returns:
			Vector: rotated vector object
		"""
		cos = np.cos(angle)
		sin = np.sin(angle)

		# get rotation matrix
		axis = axis.lower()
		if axis == "z":
			rotation = np.array([
					[ cos, -sin, 0 ],
					[ sin,  cos, 0 ],
					[   0,    0, 1 ]
				])
		elif axis == "y":
			rotation = np.array([
					[ cos, 0, sin ],
					[   0, 1,   0 ],
					[-sin, 0, cos ]
				])
		elif axis == "x":
			rotation = np.array([
					[ 1,   0,    0 ],
					[ 0, cos, -sin ],
					[ 0, sin,  cos ]
				])
		else:
			raise ValueError("invalid axis given '%s'" % (axis))

		return Vector.from_numpy(rotation.dot(self.matrix))

	def rotate(self, by, angle):
		"""
		Wrapper function to construct rotated vectors, transparently using
		**dicom3d.Vector.rotate_by_axis** or **dicom3d.Vector.rotate_by_vector**,
		depending on the arguments given.

		Args:
			by (str, Vector): axis definition ("x", "y" or "z") or vector object
			angle (float): angle measured in radians

		Raises:
			ValueError: if an invalid axis name is passed as argument

		Returns:
			Vector: rotated vector object
		"""

		if type(by) is str:
			return self.rotate_by_axis(by, angle)

		if type(by) is Vector:
			return self.rotate_by_vector(by, angle)

		raise ValueError("invalid vector rotation arguments")

	def __len__(self):
		return 3

	def __getitem__(self, item):
		return self.matrix[item,0]

	def __iter__(self):
		for i in self.matrix.T[0]:
			yield i

	def __repr__(self):
		i,j,k = self.tuple()
		return "i:%.2f j:%.2f k:%.2f" % (i,j,k)

class Plane():
	"""
		This class provides support for the mathematical operations
		required to construct and manipulate planes
	"""
	def __init__(self, a=0, b=0, c=0, d=0):
		self.normal = Vector(a,b,c)
		self.d = d

	# PROPERTIES #
	def _geta(self): return self.normal.i
	def _getb(self): return self.normal.j
	def _getc(self): return self.normal.k
	def _seta(self,v): self.normal.i = v
	def _setb(self,v): self.normal.j = v
	def _setc(self,v): self.normal.k = v

	a = property(_geta, _seta)
	"""
	For a plane described by equation 'ax + by + cx = d', this property
	modifies the 'a' component of the plane
	"""
	b = property(_getb, _setb)
	"""
	For a plane described by equation 'ax + by + cx = d', this property
	modifies the 'b' component of the plane
	"""
	c = property(_getc, _setc)
	"""
	For a plane described by equation 'ax + by + cx = d', this property
	modifies the 'c' component of the plane
	"""

	@staticmethod
	def from_coords(origin, target):
		"""
		Constructs a plane from a vector described by two points in space

		Note:
			The resulted plane's normal vector will be trimmed to be a unit
			vector

		Args:
			origin (tuple, Point): source or origin point
			target (tuple, Point): target or end point

		Returns:
			Plane: resulting plane object

		Examples:
			>>> plane1 = Plane.from_axes("xz")
			>>> plane2 = Plane.from_coords((0,0,0),(0,1,0))
			>>> plane1.equals(plane2)
			True
		"""
		xo,yo,zo = origin
		xt,yt,zt = target

		n = Vector(xt-xo, yt-yo, zt-zo).unit()
		return Plane.from_normal(n, n.i*xo + n.j*yo + n.k*zo)

	@staticmethod
	def from_normal(normal, d=0):
		"""
		Constructs a plane from a normal vector and the **d** component.

		Args:
			normal (Vector): normal vector
			d (int, optional): **d** component of the plane, defaults to 0.

		Returns:
			Plane: resulting plane object
		"""
		return Plane(*normal.tuple(), d)

	@staticmethod
	def from_axes(axes):
		"""
		Constructs a vector from two axis

		Args:
			axes (str): string defining the two axis ("xy","xz" or "yz")

		Raises:
			ValueError: on invalid axis definition

		Returns:
			Plane: resulting plane object

		Examples:
			>>> oxy = Plane.from_axes("xy")
			>>> oxz = Plane.from_axes("xz")
			>>> oyz = Plane.from_axes("yz")
			>>> degrees(oxz.angle(oxy))
			90.0
		"""
		if len(axes) != 2:
			raise ValueError("too many axes")

		if "x" in axes and "y" in axes:
			return Plane.from_normal(Vector.from_axis("z"),0)
		if "x" in axes and "z" in axes:
			return Plane.from_normal(Vector.from_axis("y"),0)
		if "y" in axes and "z" in axes:
			return Plane.from_normal(Vector.from_axis("x"),0)

		raise ValueError("invalid axes")

	def copy(self):
		"""
		Creates a copy of itself

		Returns:
			Plane: resulting plane object
		"""
		return Plane.from_normal(self.normal, self.d)

	def equals(self, p):
		"""
		Checks if a plane is equal to another

		Args:
			p (Plane): argument plane to check equality

		Returns:
			bool: **True** if equation is equal or **False** otherwise

		Examples:
			>>> plane1 = Plane.from_axes("xy")
			>>> plane2 = Plane.from_coords((0,0,0), (0,0,1))
			>>> plane2.equals(plane1)
			True
			>>> plane2.equals(plane1.move((0,0,1)))
			False
		"""
		return self.d == p.d and self.normal.equals(p.normal)

	def round(self, digits=5):
		"""
		Rounds the **a**, **b**, **c** and **d** components of the plane
		and returns the resulting plane object

		Args:
			digits (int, optional): number of digits to round to, defaults to 5

		Returns:
			Plane: resulting plane object
		"""
		return Plane.from_normal(
			self.normal.round(digits), round(self.d, digits))

	def rotate(self, by, angle):
		"""
		Returns a plane from the current one, with the normal vector
		rotated about an axis or another vector, by a given angle.

		Args:
			by (str,Vector): axis definition ("x","y" or "z") or vector object
			angle (float): angle measured in radians

		Returns:
			Plane: resulting plane object
		"""
		p = Plane(*self.normal.tuple(),self.d)
		p.normal = p.normal.rotate(by, angle)
		return p

	def move(self, point):
		"""
		Returns a plane from the current one, translated to a given point

		Note:
			This function will modify only the **d** component of the plane

		Args:
			point (tuple, Point): point to translate plane to

		Returns:
			Plane: resulting plane object
		"""
		x,y,z = point
		a,b,c = self.normal.tuple()
		d = a*x + b*y + c*z
		return Plane(a, b, c, d)

	def parallel(self, p, tolerance = 1.e-4):
		"""
		Checks if this plane is parallel to a given one within a given range
		of tolerance

		Args:
			p (Plane): plane argument to check parallelism
			tolerance (float, optional): tolerance value, defaults to 1.e-4

		Returns:
			bool: **True** if parallel or **False** otherwise

		Examples:
			>>> plane1 = Plnae.from_axes("xy") # OXY plane
			>>> plane2 = plane1.move((0,0,10)) # move plane to this point
			>>> plane1
			0.00X + 0.00Y + 1.00Z = 0.00
			>>> plane2
			0.00X + 0.00Y + 1.00Z = 10.00
			>>> plane1.parallel(plane2)
			True
		"""
		return self.angle(p) < tolerance

	def resolve(self, point):
		"""
		Resolves the X,Y or Z component of the given point based on
		current plane equation

		Args:
			point (tuple, Point): point to resolve

		Returns:
			Point: resulting Point

		Examples:
			To find the Z component of a point that lies on the plane
			and has only the X and Y components defined:

			>>> plane = Plane.from_axes("xy").rotate("y",radians(30))
			>>> _,_,z = plane.resolve((10,10,None))
			>>> print("Resolved point is X:10 Y:10 Z:%.2f" % (z))
			Resolved point is X:10 Y:10 Z:-5.77
		"""
		x,y,z = point
		a,b,c = self.normal.tuple()

		# x = (d - cz - by)/a
		# y = (d - cz - ax)/b
		# z = (d - by - ax)/c

		# resolve for X
		if x is None:
			if a == 0: return point
			x = (self.d - c * z - b * y)/a
		elif y is None:
			if b == 0: return point
			y = (self.d - c * z - a * x)/b
		elif z is None:
			if c == 0: return point
			z = (self.d - b * y - a * x)/c

		return Point(x,y,z)

	def angle(self, p):
		"""
		Calculates the angle made by the given plane with another one

		Args:
			p (Plane): plane argument to calculate angle with

		Returns:
			float: resulted angle in radians

		Note:
			This function actually calculates the angle made by the two
			plane's normal vectors
		"""
		return self.normal.angle(p.normal)

	def intersects(self, point):
		"""
		Verifies if a given point intersects the current plane

		Args:
			point (tuple, Point): point argument to check intersection with

		Returns:
			bool: **True** if intersects plane or **False** otherwise
		"""
		x,y,z = point
		a,b,c = self.normal
		v = a*x + b*y + c*z
		return v == self.d

	def __iter__(self):
		l = [*self.normal, self.d]
		for i in l: yield i

	def __repr__(self):
		""" prints plane equation"""
		a,b,c = self.normal
		return "%.2fX + %.2fY + %.2fZ = %.2f" % (
			a, b, c, self.d)
#
# cartesian mapping utility class
#
class LocalCoordinateSystem():

	"""
		Class responsible for transforming local coordinates in a cartesian space
		to world three dimensional coordinations. A *Local Coordinate System* is
		defined by two axis vectors (one describing the X axis, the other one the Y
		axis in the cartesian bi-dimensional space) and an origin for the (0,0)
		point.

		The scaling factor is optional, but useful when the world you are mapping
		to has a different density than the local cartesian system you are using.
	"""

	def __init__(self, origin, x_vector, y_vector, scaling=(1,1)):
		""" construct a local coordinate system from X,Y vectors, origin
			and a scaling factor """

		# unwrap tuples into numpy array
		self.origin   = Point(*origin)
		self.x_vector = x_vector.copy()
		self.y_vector = y_vector.copy()
		self.scaling  = scaling

		self.update()

	def copy(self):
		"""
		Creates a copy of itself

		Returns:
			LocalCoordinateSystem: resulting coordinate system
		"""
		return LocalCoordinateSystem(
			self.origin,
			self.x_vector, self.y_vector,
			self.scaling )

	def update(self):
		"""
		For each direct modification made to this coordinate system, a call
		to this function is required to update the transformation matrix

		Examples:
			Assuming you decide to set a new origin point:

			>>> lcs = LocalCoordinateSystem(
					Vector.from_axis("x),
					Vector.from_axis("y"),
					Point(0,0,0))
			>>> lcs.origin = Point(10,10,0)
			>>> lcs.update()
			>>> # now you can safely transform from and to cartesian space
			>>> pt = lcs.to_world(0,0)
			>>> print("Origin:", pt)
		"""
		# unwrap into discreete numbers
		sx, sy, sz = self.origin
		xx, xy, xz = self.x_vector.tuple()
		yx, yy, yz = self.y_vector.tuple()
		pi, pj     = self.scaling

		# transformation matrix
		self.matrix = np.array([
				[xx * pi, yx * pj, 0, sx],
				[xy * pi, yy * pj, 0, sy],
				[xz * pi, yz * pj, 0, sz],
				[      0,       0, 0,  1]
			])

	def to_local(self, coords):
		"""
		Transforms the givel world three dimensional coordinates to local
		cartesian coordinates

		Args:
			coords (Point, tuple): world three dimensional coordinates

		Returns:
			tuple: tuple of the resulting cartesian coordinates in **float**
		"""

		coords = np.array([*coords])
		origin = np.array([*self.origin])

		# substract origin -> translate
		coords = coords - origin
		dx, dy = self.scaling

		# rotate
		x = coords.dot(self.x_vector.matrix)
		y = coords.dot(self.y_vector.matrix)

		x, y = x[0]/dx, y[0]/dy
		return (x,y)

	def to_world(self, x, y):
		"""
		Transform local cartesian coordinates (**x** and **y**) to three
		dimesnional world coordinates

		Args:
			x (float, int): value of cartesian **x** coordinate
			y (float, int): value of cartesian **y** coordinate

		Returns:
			Point: point object describing resulting coordinates

		Examples:
			>>> transform = section.transfrom
			>>> transform.to_world(0,0)
			x:-73.91 y:-81.20 z:6.64
			>>> transform.to_world(10,10)
			x:-70.78 y:-78.08 z:6.6
			>>> dist = transform.to_world(10,10).distance( transform.to_world(0,0) )
			>>> print("From (0,0) to (10,10) distance is: %.1f mm" % (dist))
			From (0,0) to (10,10) distance is: 4.4 mm
		"""

		# multiply with transformation matrix
		imatrix = np.array([ [x], [y], [0], [1] ])
		rmatrix = self.matrix.dot(imatrix)

		# extract response
		x,y,z,_ = rmatrix.T[0]
		return Point(x,y,z)

	def measure(self, point_from, point_to):

		"""
		Measures the distance from **point_from** to **point_to** in world coordinates
		or local coordinates.

		When measuring for local cartesian coordinates the result will be the distance
		from the coresponding transformed world coordindates and viceversa. When
		measuring distance for world coordinates, the result will be the distance
		in local coordinates.

		Raises:
			ValueError: inconsistent dimensions given (e.g. 2D point paired with 3D point)
			ValueError: invalid input (e.g. not a 2D point nor 3D point)

		Returns:
			float: measured distance in local or world coordinates

		Examples:
			>>> # asuming we have a *dicom3d.Dataset* object
			>>> pt1, pt2 = dataset.to_mm(0,0), dataset.to_mm(10,10)
			>>> # this will measure the distance in mm between the two points
			>>> pt1.distance(pt2)
			4.419417382415922
			>>> # which is ecquivalent with this short form
			>>> dataset.transform.measure((0,0),(10,10))
			4.419417382415922
			>>> # for measuring the opposite, distance in pixels
			>>> # for the segment defined by the two points
			>>> dataset.transform.measure(pt1,pt2)
			14.142135623730951
			>>> # which is equivalent to how we calculate the diagonal
			>>> # if we knew the corresponding local points
			>>> np.sqrt(10**2 + 10**2)
			14.142135623730951
		"""

		if len(point_from) != len(point_to):
			raise ValueError("inconsistent dimensions for measuring distance (%s,%s)" % (
				point_from, point_to))

		# points are in local space => measure world distance
		if len(point_from) == 2:
			xs, ys = point_from
			xe, ye = point_to
			pts = self.to_world(xs,ys)
			pte = self.to_world(xe,ye)
			return pts.distance(pte)

		# points are in world space => measure local distance
		if len(point_from) == 3:
			xs, ys = self.to_local(point_from)
			xe, ye = self.to_local(point_to)
			dx, dy = (xe-xs),(ye-ys)
			return np.sqrt(dx ** 2 + dy ** 2)

		raise ValueError("invalid input for measuring distance (%s,%s)" % (
			point_from, point_to))

	def rotate(self, by, angle):
		"""
		Rotates this coordinate system X and Y axes about a vector or an axis
		and returns the newly modified coordinate system

		Args:
			by (str, Vector): axis definition ("x", "y" or "z") or vector object
			angle (float): angle measured in radians

		Returns:
			LocalCoordinateSystem: the rotated coordinate system

		Examples:
			>>> section.transform.x_vector
			i:1.00 j:0.00 k:0.00
			>>> section.transform.rotate("z",radians(45)).x_vector
			i:0.71 j:0.71 k:0.00

		"""
		lcs = self.copy()

		# rotate LCS and update
		lcs.x_vector = self.x_vector.rotate(by, angle)
		lcs.y_vector = self.y_vector.rotate(by, angle)
		lcs.update()

		return lcs

	def move(self, by, distance):
		"""
		Translate this local coordinate system's origin by moving it
		along a given vector and by a given distance

		Args:
			by (str, Vector): axis name ("x", "y" or "z") or vector object describing direction of translation
			distance (float): value describing distance

		Returns:
			LocalCoordinateSystem: the translated coordinate system

		Examples:
			>>> section.transform.origin
			x:-73.91 y:-81.20 z:6.64
			>>> # move on the x axis 10 mm
			>>> section.tranform.move("x", 10.0).origin
			x:-63.91 y:-81.20 z:6.64
			>>> # move on XY diagonal
			>>> vector = Vector.from_axis("x").rotate("z", radians(45))
			>>> section.transform.move(vector, 10.0).origin
			x:-66.84 y:-74.13 z:6.64
		"""
		lcs = self.copy()

		# move origin and update
		lcs.origin = lcs.origin.move(by, distance)
		lcs.update()

		return lcs

	def scale(self, by):
		"""
		Modify the scaling factor of the current coordinate system and construct
		another one with the resulting scaling factor

		Args:
			by (float, tuple): value or tuple of values describing the scaling factor

		Returns:
			LocalCoordinateSystem: the scaled coordinate system

		Examples:
			>>> transform1 = section.transform.copy()
			>>> transform2 = transform.scale(1/2)
			>>> dist1 = transform1.to_world(0,0).distance(transform1.to_world(10,10))
			>>> dist2 = transform2.to_world(0,0).distance(transform2.to_world(10,10))
			>>> print("Distance before scale: %.1f after: %.1f" % (dist1, dist2))
			Distance before scale: 4.4 after: 2.2
		"""
		lcs = self.copy()
		dx,dy = lcs.scaling

		# if factor is tuple
		if type(by) is tuple:
			by_x, by_y = by
			lcs.scaling = float(dx * by_x), float(dy * by_y)
		# if factor is float or integer
		else:
			lcs.scaling = float(dx * by), float(dy * by)

		lcs.update()
		return lcs


import numpy as np
import matplotlib.pyplot as plt

def f(x) :

    s = np.exp(-x**2) - np.cos(x) - 1
    return s

def Secant(x0,x1,Tol,Nmax) :

    slope=(f(x1)-f(x0))/(x1-x0)
    x=(x1-f(x1))/slope
    e=abs(x-x1)
    n=1
    while n < Nmax and e > Tol :
          x0=x1
          x1=x
          slope=f(x1)-f(x0)/(x1-x0)
          x=x1-f(x1)/slope
          e=abs(x-x1)
          n=n+1

    return x,n,f(x)

#------------ MAIN PROGRAMME -------------#

x0=-10 ; x1=-9 ;Tol=10**(-8) ;Nmax=100
x,n,fx = Secant(x0,x1,Tol,Nmax)

print ('Approximate Solution is ' , x )
print ('number of steps is ' , n)
print ('Value at approximate solution is ' , fx)


# Item-Item Similarity computation on pySpark with cosine similarity

import sys
from itertools import combinations
import numpy as np

from pyspark import SparkContext

def parseVector(line):
    '''
    Parse each line of the specified data file, assuming a "|" delimiter.
    Converts each rating to a float
    '''
    line = line.split("|")
    return line[0],(line[1],float(line[2]))

def findItemPairs(user_id,items_with_rating):
    '''
    For each user, find all item-item pairs combos. (i.e. items with the same user)
    '''
    for item1,item2 in combinations(items_with_rating,2):
        return (item1[0],item2[0]),(item1[1],item2[1])

def calcSim(item_pair,rating_pairs):
    '''
    For each item-item pair, return the specified similarity measure,
    along with co_raters_count
    '''
    sum_xx, sum_xy, sum_yy, sum_x, sum_y, n = (0.0, 0.0, 0.0, 0.0, 0.0, 0)

    for rating_pair in rating_pairs:
        sum_xx += np.float(rating_pair[0]) * np.float(rating_pair[0])
        sum_yy += np.float(rating_pair[1]) * np.float(rating_pair[1])
        sum_xy += np.float(rating_pair[0]) * np.float(rating_pair[1])
        # sum_y += rt[1]
        # sum_x += rt[0]
        n += 1

    cos_sim = cosine(sum_xy,np.sqrt(sum_xx),np.sqrt(sum_yy))

    return item_pair, (cos_sim,n)

def cosine(dot_product,rating_norm_squared,rating2_norm_squared):
    '''
    The cosine between two vectors A, B
       dotProduct(A, B) / (norm(A) * norm(B))
    '''
    numerator = dot_product
    denominator = rating_norm_squared * rating2_norm_squared

    return (numerator / (float(denominator))) if denominator else 0.0

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print >> sys.stderr, \
            "Usage: PythonUserCF <master> <file>"
        exit(-1)

    sc = SparkContext(sys.argv[1], "PythonUserCF")
    lines = sc.textFile(sys.argv[2])

    '''
    Obtain the sparse user-item matrix
        user_id -> [(item_id_1, rating_1),
                   [(item_id_2, rating_2),
                    ...]
    '''
    user_item_pairs = lines.map(parseVector).groupByKey().cache()

    '''
    Get all item-item pair combos
        (item1,item2) ->    [(item1_rating,item2_rating),
                             (item1_rating,item2_rating),
                             ...]
    '''

    pairwise_items = user_item_pairs.filter(
        lambda p: len(p[1]) > 1).map(
        lambda p: findItemPairs(p[0],p[1])).groupByKey()

    '''
    Calculate the cosine similarity for each item pair
        (item1,item2) ->    (similarity,co_raters_count)
    '''

    item_sims = pairwise_items.map(
        lambda p: calcSim(p[0],p[1])).collect()


from sympy import *

x,y,z = symbols('x y z')
all_symbols = [x,y]
ro_x=5
ro_y=5

psi=exp(-(((x-1)*(x-1))/(2*ro_x*ro_x) + ((y-2)*(y-2))/(2*ro_y*ro_y)))

target_value = 0
model_function = z - psi
obs_eq = Matrix([target_value - model_function]).vec()
obs_eq_jacobian = obs_eq.jacobian(all_symbols)

print(psi)
print(obs_eq)
print(obs_eq_jacobian)
print(latex(psi))
print(latex(obs_eq_jacobian))

with open("example_func_xy_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void example_func_xy(double &psi, double x, double y)\n")
    f_cpp.write("{")
    f_cpp.write("psi = %s;\n"%(ccode(psi)))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void observation_equation_example_func_xy(double &delta, double x, double y, double z)\n")
    f_cpp.write("{")
    f_cpp.write("delta = %s;\n"%(ccode(obs_eq[0,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void observation_equation_example_func_xy_jacobian(Eigen::Matrix<double, 1, 2> &j, double x, double y)\n")
    f_cpp.write("{")
    for i in range (1):
        for j in range (2):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(obs_eq_jacobian[i,j])))
    f_cpp.write("}")


"""Concurrence property."""
import numpy as np

from toqito.matrices import pauli

def concurrence(rho: np.ndarray) -> float:
    r"""
    Calculate the concurrence of a bipartite state [WikCon]_.

    The concurrence of a bipartite state :math:`\rho` is defined as

    .. math::
        \max(0, \lambda_1 - \lambda_2 - \lambda_3 - \lambda_4),

    where :math:`\lambda_1, \ldots, \lambda_4` are the square roots of the
    eigenvalues in decreasing order of the matrix

    .. math::
        \rho\tilde{\rho} = \rho \sigma_y \otimes \sigma_y \rho^* \sigma_y \otimes \sigma_y.

    Concurrence can serve as a measure of entanglement.

    Examples
    ==========

    Consider the following Bell state:

    .. math::
        u = \frac{1}{\sqrt{2}} \left( |00 \rangle + |11 \rangle \right).

    The concurrence of the density matrix :math:`\rho = u u^*` defined by the vector :math:`u` is
    given as

    .. math::
        \mathcal{C}(\rho) \approx 1.

    The following example calculates this quantity using the :code:`toqito` package.

    >>> import numpy as np
    >>> from toqito.states import basis
    >>> from toqito.state_props import concurrence
    >>> e_0, e_1 = basis(2, 0), basis(2, 1)
    >>> e_00, e_11 = np.kron(e_0, e_0), np.kron(e_1, e_1)
    >>> u_vec = 1 / np.sqrt(2) * (e_00 + e_11)
    >>> rho = u_vec * u_vec.conj().T
    >>> concurrence(rho)
    0.9999999999999998

    Consider the concurrence of the following product state

    .. math::
        v = |0\rangle \otimes |1 \rangle.

    As this state has no entanglement, the concurrence is zero.

    >>> import numpy as np
    >>> from toqito.states import basis
    >>> from toqito.state_props import concurrence
    >>> e_0, e_1 = basis(2, 0), basis(2, 1)
    >>> v_vec = np.kron(e_0, e_1)
    >>> sigma = v_vec * v_vec.conj().T
    >>> concurrence(sigma)
    0

    References
    ==========
    .. [WikCon] Wikipedia page for concurrence (quantum computing)
       https://en.wikipedia.org/wiki/Concurrence_(quantum_computing)

    :param rho: The bipartite system specified as a matrix.
    :return: The concurrence of the bipartite state :math:`\rho`.
    """
    if rho.shape != (4, 4):
        raise ValueError("InvalidDim: Concurrence is only defined for bipartite systems.")

    sigma_y = pauli("Y", False)
    sigma_y_y = np.kron(sigma_y, sigma_y)

    rho_tilde = sigma_y_y @ rho.conj() @ sigma_y_y

    eig_vals = np.linalg.eigvals(rho @ rho_tilde)
    eig_vals = np.sort(np.abs(np.sqrt(eig_vals)))[::-1]
    return max(0, eig_vals[0] - eig_vals[1] - eig_vals[2] - eig_vals[3])


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

def f(t, y):  # solucion y(t)=t+exp(-t)
    #return -y+t+1
    #return (2-2*t*y)/(1+t**2) #12
    return y**2/(1+t) #3

#y0 = 1 #12
y0 = -1/m.log(2) #3

a, b = 1, 2
n = 10

def runge_kutta4(f, a, b, n, y0):

    u = [y0]

    h = (b-a)/n

    for j in range(n):
        K1= f(a+j*h, u[j])
        K2= f(a+j*h+h/2, u[j]+h*K1/2)
        K3= f(a+j*h+h/2, u[j]+h*K2/2)
        K4= f(a+j*h+h, u[j]+h*K3)

        u.append(u[j]+h*(K1+2*K2+2*K3+K4)/6)

    return u

h = (b-a)/n

u = runge_kutta4(f, a, b, n, y0)

print(u)

r = [a+i*h for i in range(n+1)]

def result(t):
    #return (2*t+1)/(t**2+1) #12
    return -1/m.log(t+1) #3

print("Errores: ")
print([abs(u[j]-result(a+j*h)) for j in range(n+1)])

plt.plot(r, u, 'ro') # ro = red o (puntos)
plt.plot(r, [result(t) for t in r])

plt.show()


# exercise 4.1.3

from matplotlib.pyplot import (figure, title, subplot, plot, hist, show)
import numpy as np
from scipy import stats

# Number of samples
N = 500

# Mean
mu = 17

# Standard deviation
s = 2

# Number of bins in histogram
nbins = 20

# Generate samples from the Normal distribution
X = np.random.normal(mu,s,N).T
# or equally:
X = np.random.randn(N).T * s + mu

# Plot the histogram
f = figure()
title('Normal distribution')
hist(X, bins=nbins, density=True)

# Over the histogram, plot the theoretical probability distribution function:
x = np.linspace(X.min(), X.max(), 1000)
pdf = stats.norm.pdf(x,loc=17,scale=2)
plot(x,pdf,'.',color='red')

# Compute empirical mean and standard deviation
mu_ = X.mean()
s_ = X.std(ddof=1)

print("Theoretical mean: ", mu)
print("Theoretical std.dev.: ", s)
print("Empirical mean: ", mu_)
print("Empirical std.dev.: ", s_)

show()

print('Ran Exercise 4.1.3')


import numpy
import scipy.interpolate as si
from matplotlib import pyplot as plt
# puntos de control
ptscontrol = numpy.array([[-1.,0.],
                         [1.,4.],
                         [3.,-2.],
                         [4.,3.],
                         [6.,1.]])
muestra=200
grado = 3

def b_spline(cv,muestra,grado):
    tam = cv.shape[0] # tamaño
    kv = numpy.clip(numpy.arange(tam+grado+1)-grado,0,tam-grado) # toma un ([0,1,2,3,4,5,6,7] -3=[-3,-2, -1 , 0, 1 ,2 ,3 ,4] , 0 , 2)
     #=> [0,0,0,0,1,2,2,2,2]
    t  = numpy.linspace(0,tam-grado,muestra) # t = [0,2] con 200 puntos
    #retorna un par de t y b-spline(t)
    return numpy.array( si.splev(t , ( kv , cv.T , grado) )  ).T
for d in range(2):
    p = b_spline(ptscontrol,muestra,grado)
    x_spline,y_spline = p.T
    print(x_spline)
    print(y_spline)
    plt.plot(x_spline,y_spline,'k-',label='Grado %s'%d)
plt.plot(ptscontrol[:,0],ptscontrol[:,1], 'o--', label='Control Points')
plt.show()


import numpy as np
from math import sqrt
from sys import argv

def sphere(x_, y_, z_):
    """
        calculate best fitting sphere (LSM) on points
        :param returns: x0, y0, z0, R
    """
    n_ = x_.shape[0]
    a = np.c_[x_, y_, z_, np.full(n_, 1, 'float64')]
    b = -np.square(x_) - np.square(y_) - np.square(z_)
    res = np.linalg.lstsq(a, b, rcond=None)[0]
    return -0.5 * res[0], -0.5 * res[1], -0.5 * res[2], \
          sqrt((res[0]**2 + res[1]**2 + res[2]**2) / 4 - res[3])

if __name__ == "__main__":
    if len(argv) > 1:
        file_names = argv[1:]
    else:
        file_names = ['sphere1.txt']
    for file_name in file_names:
        pnts = np.genfromtxt(file_name, 'float64', delimiter=',')
        if pnts.shape[1] > 3:
            pnts = pnts[:,1:4]  # skip first column (point id)
        sph = sphere(pnts[:,0], pnts[:,1], pnts[:,2])
        print("x0: {:.3f} y0: {:.3f} z0: {:.3f} R: {:.3f}".format(sph[0], sph[1], sph[2], sph[3]))
        dr = np.sqrt(np.sum(np.square(pnts - sph[:3]), 1)) - sph[3] # difference in radius direction
        RMS = sqrt(np.sum(np.square(dr)) / pnts.shape[0])
        print("RMS: {:.3f}".format(RMS))


"""Von neumann entropy metric."""
import numpy as np

from toqito.matrix_props import is_density

def von_neumann_entropy(rho: np.ndarray) -> float:
    r"""
    Compute the von Neumann entropy of a density matrix [WikVent]_. [WatVec]_.

    Let :math:`P \in \text{Pos}(\mathcal{X})` be a positive semidefinite operator, for a complex
    Euclidean space :math:`\mathcal{X}`. Then one defines the *von Neumann entropy* as

    .. math::
        H(P) = H(\lambda(P)),

    where :math:`\lambda(P)` is the vector of eigenvalues of :math:`P` and where the function
    :math:`H(\cdot)` is the Shannon entropy function defined as

    .. math::
        H(u) = -\sum_{\substack{a \in \Sigma \\ u(a) > 0}} u(a) \text{log}(u(a)),

    where the :math:`\text{log}` function is assumed to be the base-2 logarithm, and where
    :math:`\Sigma` is an alphabet where :math:`u \in [0, \infty)^{\Sigma}` is a vector of
    nonnegative real numbers indexed by :math:`\Sigma`.

    Examples
    ==========

    Consider the following Bell state:

    .. math::
        u = \frac{1}{\sqrt{2}} \left(|00 \rangle + |11 \rangle \right) \in \mathcal{X}.

    The corresponding density matrix of :math:`u` may be calculated by:

    .. math::
        \rho = u u^* = \frac{1}{2} \begin{pmatrix}
                         1 & 0 & 0 & 1 \\
                         0 & 0 & 0 & 0 \\
                         0 & 0 & 0 & 0 \\
                         1 & 0 & 0 & 1
                       \end{pmatrix} \text{D}(\mathcal{X}).

    Calculating the von Neumann entropy of :math:`\rho` in :code:`toqito` can be done as follows.

    >>> from toqito.state_props import von_neumann_entropy
    >>> import numpy as np
    >>> test_input_mat = np.array(
    >>>     [[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0],
    >>>      [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]
    >>> )
    >>> von_neumann_entropy(test_input_mat)
    5.88418203051333e-15

    Consider the density operator corresponding to the maximally mixed state of dimension two

    .. math::
        \rho = \frac{1}{2}
        \begin{pmatrix}
            1 & 0 \\
            0 & 1
        \end{pmatrix}.

    As this state is maximally mixed, the von Neumann entropy of :math:`\rho` is
    equal to one. We can see this in :code:`toqito` as follows.

    >>> from toqito.state_props import von_neumann_entropy
    >>> import numpy as np
    >>> rho = 1/2 * np.identity(2)
    >>> von_neumann_entropy(rho)
    1.0

    References
    ==========
    .. [WikVent] Wikipedia: Von Neumann entropy
        https://en.wikipedia.org/wiki/Von_Neumann_entropy

    .. [WatVec] Watrous, John.
        "The theory of quantum information."
        Section: "Definitions of quantum entropic functions".
        Cambridge University Press, 2018.

    :param rho: Density operator.
    :return: The von Neumann entropy of :code:`rho`.
    """
    if not is_density(rho):
        raise ValueError("Von Neumann entropy is only defined for density " "operators.")
    eigs, _ = np.linalg.eig(rho)
    eigs = [eig for eig in eigs if eig > 0]
    return -np.sum(np.real(eigs * np.log2(eigs)))


from scipy import *

def forwardsub(L,b):
	"""
 	forwardsub(L,b)

	Solve the lower-triangular linear system with matrix L and right-hand side
	vector b.
	"""
	n = len(b)
	x = zeros(n)
	for i in range(n):
		s = L[i,:i] @ x[:i]
		x[i] = ( b[i] - s ) / L[i,i]
	return x

def backsub(U,b):
	"""
	backsub(U,b)

	Solve the upper-triangular linear system with matrix U and right-hand side
	vector b.
	"""
	n = len(b)
	x = zeros(n)
	for i in range(n-1,-1,-1):
		s = U[i,i+1:] @ x[i+1:]
		x[i] = ( b[i] - s ) / U[i,i]
	return x

def lufact(A):
	"""
	lufact(A)

	Compute the LU factorization of square matrix A, returning the factors.
	"""
	n = A.shape[0]
	L = eye(n)      # puts ones on diagonal
	U = copy(A)

	# Gaussian elimination
	for j in range(n-1):
		for i in range(j+1,n):
			L[i,j] = U[i,j] / U[j,j]   # row multiplier
			U[i,j:] = U[i,j:] - L[i,j]*U[j,j:]
	return L,triu(U)


import numpy as np
import pandas as pd
from scipy.integrate import ode, solve_ivp
import matplotlib.pyplot as plt

def sir_ode(times,init,parms):
    b, g = parms
    S,I,R = init
    # ODEs
    dS = -b*S*I
    dI = b*S*I-g*I
    dR = g*I
    return [dS,dI,dR]

parms = [0.1,0.05]
init = [0.99,0.01,0]
times = np.linspace(0,200,2001)

sir_sol = solve_ivp(fun=lambda t, y: sir_ode(t, y, parms), t_span=[min(times),max(times)], y0=init, t_eval=times)

sir_out = pd.DataFrame({"t":sir_sol["t"],"S":sir_sol["y"][0],"I":sir_sol["y"][1],"R":sir_sol["y"][2]})

# Plot
sline = plt.plot("t","S","",data=sir_out,color="red",linewidth=2)
iline = plt.plot("t","I","",data=sir_out,color="green",linewidth=2)
rline = plt.plot("t","R","",data=sir_out,color="blue",linewidth=2)
plt.xlabel("Time",fontweight="bold")
plt.ylabel("Number",fontweight="bold")
legend = plt.legend(title="Population",loc=5,bbox_to_anchor=(1.25,0.5))
frame = legend.get_frame()
frame.set_facecolor("white")
frame.set_linewidth(0)
plt.show()


from mpl_toolkits.mplot3d import Axes3D  # noqa: F401, needed for 3D axes
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from tabulate import tabulate

from efficient_calculations import expTaylor, lnTaylor

def Q2to4():
    """Code for ICT questions 2--4."""

    domain = np.linspace(-2, 2)

    fig, ax = plt.subplots()
    ax.plot(domain, np.exp(domain), 'k--', label='Exact')
    for n in range(1, 4):
        ax.plot(domain, expTaylor(n, domain), label=f'{n} Terms')

    ax.set_title('Comparison of exp and Taylor Series approximation')
    ax.set_xlim((domain.min(), domain.max()))
    ax.legend()

def Q5():
    """Code for ICT question 5."""

    domain = np.linspace(-1, 1, num=9)
    # Construct a grid, such that a pair of elements (one from each array)
    # gives the corresponding (x, y) coordinates.
    X, Y = np.meshgrid(domain, domain)
    Z = X**2 + Y**2  # Construct function values

    # Use and modify base code from
    # https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html

    fig = plt.figure()
    ax = fig.gca(projection='3d')

    surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
                           linewidth=0, antialiased=False)

    ax.set_xlim(-1, 1)
    ax.set_ylim(-1, 1)
    ax.set_zlim(0, 2)

    # Add a color bar which maps values to colors.
    fig.colorbar(surf, shrink=0.5, aspect=5)

def Q6():
    """Code for ICT question 6."""

    x, h   = 1, np.logspace(-1, -6, 6)
    exact  = np.log(1+x+h)
    approx = lnTaylor(3, x+h)

    error = np.abs(exact - approx)
    print(tabulate({
        'ln(1+x)'    : exact,
        'Taylor'     : approx,
        'Error'      : error,
        'Error / h^2': error / h**2,
        'Error / h^3': error / h**3,
        'Error / h^4': error / h**4
    }, headers='keys'))

def fvec(xvec):
    """Vector function for Q7."""
    x, y = xvec
    return np.array([
        x**4 + y**4 - 1,
        x**2 - y**2 + 1
    ])

def jacobian(xvec):
    """Jacobian function for Q7."""
    x, y = xvec
    return np.array([
        [4*x**3, 4*y**3],
        [2*x,    -2*y  ]
    ])

def newton_solver(initial, func, grad=None, step=0.02, goal=None):
    state = initial.copy()
    value = func(state)

    if goal is None:
        goal = np.zeros_like(value)

    if grad is None:
        delta = np.eye(state.size) * step

        def grad(x):
            return (func(x+delta) - func(x)) / step

    while not np.allclose(value, goal, atol=1e-4, rtol=1e-4):
        state -= np.linalg.solve(grad(state), func(state))
        value = func(state)

    return state

def Q7to9():
    """Code for ICT questions 7 to 9."""

    initial = np.array([1, 1], dtype=float)

    x_sol, y_sol = newton_solver(initial, fvec, grad=jacobian)
    print(f'The analytical solution is at x={x_sol:.3f}, y={y_sol:.3f}')

    x_sol, y_sol = newton_solver(initial, fvec)
    print(f'The numerical solution is at  x={x_sol:.3f}, y={y_sol:.3f}')

if __name__ == '__main__':
    questions = (Q2to4, Q5, Q6, Q7to9)
    for question in questions:
        input(f'Press `Enter` to run {question.__name__} ')

        plt.close('all')        # <- Close all existing figures
        question()
        plt.show(block=False)   # <- Allow code execution to continue

    input('Press `Enter` to quit the program.')


#steven 02/03/2020
#function integral to calculate area of curve and x axis
import numpy as np
import matplotlib.pyplot as plt

def softmaxFuc(x):
    #x = [2.0, 2.0, 2.0, 2.0, 2.0, 2.0]
    softmax = np.exp(x)/np.sum(np.exp(x))
    print(softmax)
    print(np.sum(softmax))
    return softmax

def plot(x,y):
    plt.plot(x,y)
    plt.show()

def fun(x):
    #return x
    #return x**2
    #return np.sqrt(1-x**2)   #y=sqrt(1-x**2)  #circle equations calculate pi
    #return np.exp(x)
    #return 1/(1 + np.power(np.e,-1*x))
    return np.power(x,x)

def IntegralFuc():
    """calculate area y=x^2  form x = 0 to 1
    divide 0~1 to N shares,  erevy part considered as a rectangle.
    """
    N = 100000
    s = 0
    for i in range(N):
        x = 1/N
        y = fun(i/N)
        s += x*y

    print(s)
    #pi = s*4 #when fuc = np.sqrt(1-x**2)
    #print(pi)
    return s

def fillColor():
    x = np.linspace(0,1.5, 100)

    y1 = np.zeros(len(x))
    y2 = fun(x)
    #new_ticks = np.linspace(0,2,5)
    #print(new_ticks)
    #plt.xticks(new_ticks)

    plt.plot(x,y1,c='b',alpha=0.5)
    plt.plot(x,y2,c='b',alpha=0.5)

    plt.fill_between(x,y1,y2,where=x<=1,facecolor='green')
    #plt.grid(True)
    plt.show()

def main():
    IntegralFuc()
    fillColor()

    #x = np.linspace(-5,5, 50)
    #y = fun(x)
    #y = softmaxFuc(x)
    #plot(x,y)

if __name__ == '__main__':
    main()


import numpy as np
TOL = 1.0e-16

def exp_taylor(x, N_terms):
    partial_sums = np.zeros_like(x, dtype=np.float64)
    terms_summed = np.zeros_like(x)

    # An array of the N_terms
    N_array = np.arange(1, N_terms+1, dtype=np.float64)
    N_array = 1 / N_array

    # Calculate the terms array as outer product
    terms = np.outer(x, N_array)
    terms = np.where(terms < 0, -1*terms, terms)

    # Use a loop to accumulate the partial sums
    for i in range(terms.shape[0]):
        term = 1.0
        partial_sum = term
        for j in range(terms.shape[1]):
            term *= terms[i, j]  # jth term = x**j / (j!)
            partial_sum += term  # Partial sum incremented in-place
            terms_summed[i] = j+1
            partial_sums[i] = partial_sum
            # I do not know of any easy way to do this using "where"
            if (abs(term) < TOL * abs(partial_sum)):
                break

    # Apply a mask of original values that are negative to take reciprocal
    mask = x < 0
    partial_sums[mask] = 1 / partial_sums[mask]
    return (partial_sums, terms_summed)  # returns value & number or terms summed

def test_exp_taylor():
    N_max = 100
    R = 20
    step = 4

    header = "x".center(10) + "# terms".rjust(8) + "true".rjust(19)
    header += "approximate".rjust(19) + "error".rjust(19)
    print header
    print "="*(10+19+19+19+8)

    # Generate the numpy array of x values
    x = np.arange(-R, R+1, step)

    # Perform only one call to exp_taylor function
    (y, N_terms) = exp_taylor(x, N_max)
    exp_true = np.exp(x)

    # Display the results
    for i in range(y.shape[0]):
        relative_error = abs((y[i]-exp_true[i]) / exp_true[i])
        print "%10.5f%8d%19g%19g%19g" % (x[i], N_terms[i], exp_true[i],
                                         y[i], relative_error)

if __name__=='__main__':
    test_exp_taylor()