def Newtons_method(f, x_0, tol, 
                   tol_backtracking, x_ast=None, p_ast=None, maxiter=30):
    '''
    Newton's method to numerically approximate solution of min f.
    Args:
        f (lambda expression): definition of function f.
        x_0 (numpy ndarray): initial point for Newton's method.
        tol (float): tolerance that will halt method. Controls stopping criteria.
        tol_backtracking (float): tolerance that will halt method. Controls value of line search by backtracking.
        x_ast (numpy ndarray): solution of min f, now it's required that user knows the solution...
        p_ast (float): value of f(x_ast), now it's required that user knows the solution...
        maxiter (int): maximum number of iterations
    Returns:
        x (numpy ndarray): numpy array, approximation of x_ast.
        iteration (int): number of iterations.
        Err_plot (numpy ndarray): numpy array of absolute error between p_ast and f(x) with x approximation
                          of x_ast. Useful for plotting.
        x_plot (numpy ndarray): numpy array that containts in columns vector of approximations. Last column
                        contains x, approximation of solution. Useful for plotting.
    '''
    iteration = 0
        
    x = x_0
    
    feval = f(x)
    gfeval = gradient_approximation(f,x)
    Hfeval = Hessian_approximation(f,x)
    
    normgf = np.linalg.norm(gfeval)
    condHf= np.linalg.cond(Hfeval)
    
    Err_plot_aux = np.zeros(maxiter)
    Err_plot_aux[iteration]=compute_error(p_ast,feval)
    
    Err = compute_error(x_ast,x)
    n = x.size
    x_plot = np.zeros((n,maxiter))
    x_plot[:,iteration] = x
    
    #Newton's direction and Newton's decrement
    
    dir_Newton = np.linalg.solve(Hfeval, -gfeval)
    dec_Newton = -gfeval.dot(dir_Newton)
    
    print('I    Normgf   Newton Decrement  Error x_ast   Error p_ast   line search   Condition of Hessian')
    print('{}    {:0.2e}    {:0.2e}        {:0.2e}      {:0.2e}       {}           {:0.2e}'.format(iteration,normgf,
                                                                                                   dec_Newton,Err,
                                                                                                   Err_plot_aux[iteration],"---",
                                                                                                   condHf))
    stopping_criteria = dec_Newton/2
    iteration+=1
    while(stopping_criteria>tol and iteration < maxiter):
        der_direct = -dec_Newton
        t = line_search_by_backtracking(f,dir_Newton,x,der_direct)
        x = x + t*dir_Newton
        feval = f(x)
        gfeval = gradient_approximation(f,x)
        Hfeval = Hessian_approximation(f,x)
        normgf = np.linalg.norm(gfeval)
        condHf= np.linalg.cond(Hfeval)
        #Newton's direction and Newton's decrement

        dir_Newton = np.linalg.solve(Hfeval, -gfeval)
        dec_Newton = -gfeval.dot(dir_Newton)
        Err_plot_aux[iteration]=compute_error(p_ast,feval)
        x_plot[:,iteration] = x
        Err = compute_error(x_ast,x)
        print('{}    {:0.2e}    {:0.2e}        {:0.2e}      {:0.2e}       {:0.2e}      {:0.2e}'.format(iteration,normgf,
                                                                                                         dec_Newton,Err,
                                                                                                         Err_plot_aux[iteration],t,
                                                                                                         condHf))
        stopping_criteria = dec_Newton/2
        if t<tol_backtracking: #if t is less than tol_backtracking then we need to check the reason
            iter_salida=iteration
            iteration = maxiter - 1
        iteration+=1
    print('{} {:0.2e}'.format("Error of x with respect to x_ast:",Err))
    print('{} {}'.format("Approximate solution:", x))
    cond = Err_plot_aux > np.finfo(float).eps*10**(-2)
    Err_plot = Err_plot_aux[cond]
    if iteration == maxiter and t < tol_backtracking:
        print("Backtracking value less than tol_backtracking, check approximation")
        iteration=iter_salida
        x_plot = x_plot[:,:iteration]
    else:
        x_plot = x_plot[:,:iteration]
    return [x,iteration,Err_plot,x_plot]
def Newtons_method(f,
                   A,
                   x_0,
                   tol,
                   tol_backtracking,
                   x_ast=None,
                   p_ast=None,
                   maxiter=30):
    '''
    Newton's method to numerically approximate solution of min f.
    Args:
        f (lambda expression): definition of function f.
        A (numpy ndarray): 2d numpy array of shape (m,n) defines system of constraints Ax=b.
        x_0 (numpy ndarray): initial point for Newton's method.
        tol (float): tolerance that will halt method. Controls stopping criteria.
        tol_backtracking (float): tolerance that will halt method. Controls value of line search by backtracking.
        x_ast (numpy ndarray): solution of min f, now it's required that user knows the solution...
        p_ast (float): value of f(x_ast), now it's required that user knows the solution...
        maxiter (int): maximum number of iterations
    Returns:
        x (numpy ndarray): numpy array, approximation of x_ast.
        iteration (int): number of iterations.
        Err_plot (numpy ndarray): numpy array of absolute error between p_ast and f(x) with x approximation
                          of x_ast. Useful for plotting.
        x_plot (numpy ndarray): numpy array that containts in columns vector of approximations. Last column
                        contains x, approximation of solution. Useful for plotting.
    '''
    iteration = 0

    x = x_0

    feval = f(x)
    gfeval = gradient_approximation(f, x)
    Hfeval = Hessian_approximation(f, x)

    normgf = np.linalg.norm(gfeval)
    condHf = np.linalg.cond(Hfeval)

    Err_plot_aux = np.zeros(maxiter)
    Err_plot_aux[iteration] = math.fabs(feval - p_ast)

    Err = compute_error(x_ast, x)

    if (A.ndim == 1):
        m = 1
        n = x.size
        zero_matrix = np.zeros(1)
        first_stack = np.column_stack((Hfeval, A.T))
        second_stack = np.row_stack(
            (A.reshape(1, n).T, zero_matrix)).reshape(1, n + 1)[0]
    else:
        m, n = A.shape
        zero_matrix = np.zeros((m, n))
        first_stack = np.column_stack((Hfeval, A.T))
        second_stack = np.row_stack((A, zero_matrix))

    x_plot = np.zeros((n, maxiter))
    x_plot[:, iteration] = x

    system_matrix = np.row_stack((first_stack, second_stack))
    zero_vector = np.zeros(m)
    rhs = np.row_stack((gfeval.reshape(n, 1), zero_vector)).T[0]

    #Newton's direction and Newton's decrement
    dir_desc = np.linalg.solve(system_matrix, rhs)
    dir_Newton = dir_desc[0:n]
    dec_Newton = dir_Newton.dot(Hfeval @ dir_Newton)

    w_dual_variable_estimation = -dir_desc[n:(n + m)]

    dir_Newton = -dir_desc[0:n]

    print(
        'I    Normgf   Newton Decrement  Error x_ast   Error p_ast   line search   Condition of Hessian'
    )
    print(
        '{}    {:0.2e}    {:0.2e}        {:0.2e}      {:0.2e}       {}           {:0.2e}'
        .format(iteration, normgf, dec_Newton, Err, Err_plot_aux[iteration],
                "---", condHf))
    stopping_criteria = dec_Newton / 2
    iteration += 1
    while (stopping_criteria > tol and iteration < maxiter):
        der_direct = -dec_Newton
        t = line_search_by_backtracking(f, dir_Newton, x, der_direct)
        x = x + t * dir_Newton
        feval = f(x)
        gfeval = gradient_approximation(f, x)
        Hfeval = Hessian_approximation(f, x)
        normgf = np.linalg.norm(gfeval)
        condHf = np.linalg.cond(Hfeval)
        if (A.ndim == 1):
            m = 1
            n = x.size
            zero_matrix = np.zeros(1)
            first_stack = np.column_stack((Hfeval, A.T))
            second_stack = np.row_stack(
                (A.reshape(1, n).T, zero_matrix)).reshape(1, n + 1)[0]
        else:
            m, n = A.shape
            zero_matrix = np.zeros((m, n))
            first_stack = np.column_stack((Hfeval, A.T))
            second_stack = np.row_stack((A, zero_matrix))

        system_matrix = np.row_stack((first_stack, second_stack))
        rhs = np.row_stack((gfeval.reshape(n, 1), zero_vector)).T[0]

        #Newton's direction and Newton's decrement
        dir_desc = np.linalg.solve(system_matrix, rhs)
        dir_Newton = dir_desc[0:n]
        dec_Newton = dir_Newton.dot(Hfeval @ dir_Newton)
        w_dual_variable_estimation = -dir_desc[n:(n + m)]
        dir_Newton = -dir_desc[0:n]

        Err_plot_aux[iteration] = math.fabs(feval - p_ast)
        x_plot[:, iteration] = x
        Err = compute_error(x_ast, x)
        print(
            '{}    {:0.2e}    {:0.2e}        {:0.2e}      {:0.2e}       {:0.2e}      {:0.2e}'
            .format(iteration, normgf, dec_Newton, Err,
                    Err_plot_aux[iteration], t, condHf))
        stopping_criteria = dec_Newton / 2
        if t < tol_backtracking:  #if t is less than tol_backtracking then we need to check the reason
            iter_salida = iteration
            iteration = maxiter - 1
        iteration += 1
    print('{} {:0.2e}'.format("Error of x with respect to x_ast:", Err))
    print('{} {}'.format("Approximate solution:", x))
    cond = Err_plot_aux > np.finfo(float).eps * 10**(-2)
    Err_plot = Err_plot_aux[cond]

    if iteration == maxiter and t < tol_backtracking:
        print(
            "Backtracking value less than tol_backtracking, check approximation"
        )
        iteration = iter_salida
        x_plot = x_plot[:, :iteration]
    else:
        x_plot = x_plot[:, :iteration]
    return [x, iteration, Err_plot, x_plot]
Пример #3
0
def Newtons_method_log_barrier_infeasible_init_point(f, A, b, 
                                                     constraint_inequalities, t_path,
                                                     x_0, nu_0, tol, 
                                                     tol_backtracking, x_ast=None, p_ast=None, maxiter=30,
                                                     gf_symbolic = None,
                                                     Hf_symbolic = None):
    '''
    Newton's method for infeasible initial point to numerically approximate solution of min f subject to Ax = b.
    Calls Newton's method for feasible initial point when reach primal feasibility given by tol.
    Args:
        f (fun): definition of function f as lambda expression or function definition.
        A (numpy ndarray): 2d numpy array of shape (m,n) defines system of constraints Ax=b.
        b (numpy ndarray or float): array that defines system of constraints Ax=b (can be a single number
                                    if just one restriction is defined)

        constraints_ineq
        
        t_path
        
        x_0 (numpy ndarray): initial point for Newton's method. 
        nu_0 (numpy ndarray or float): initial point for Newton's method.
        tol (float): tolerance that will halt method. Controls stopping criteria.
        tol_backtracking (float): tolerance that will halt method. Controls value of line search by backtracking.
        x_ast (numpy ndarray): solution of min f, now it's required that user knows the solution...
        p_ast (float): value of f(x_ast), now it's required that user knows the solution...
        maxiter (int): maximum number of iterations
        gf_symbolic (fun): definition of gradient of f. If given, no approximation is
                                     performed via finite differences.
        Hf_symbolic (fun): definition of Hessian of f. If given, no approximation is
                                     performed via finite differences.
    Returns:
        x (numpy ndarray): numpy array, approximation of x_ast.
        iteration (int): number of iterations.
        Err_plot (numpy ndarray): numpy array of absolute error between p_ast and f(x) with x approximation
                          of x_ast. Useful for plotting.
        x_plot (numpy ndarray): numpy array that containts in columns vector of approximations. Last column
                        contains x, approximation of solution. Useful for plotting.
    '''
    iteration = 0
    x = x_0
    nu = nu_0
    
    feval = f(x)
    
    if gf_symbolic and Hf_symbolic:
        gfeval = gf_symbolic(x)
        Hfeval = Hf_symbolic(x)
    else:
        grad_Hess_log_barrier_dict = numerical_differentiation_of_logarithmic_barrier(f,x,t_path,
                                                                                      constraint_inequalities)
        gfeval = grad_Hess_log_barrier_dict['gradient']
        Hfeval = grad_Hess_log_barrier_dict['Hessian']

    
    normgf = np.linalg.norm(gfeval)
    condHf= np.linalg.cond(Hfeval)
    
    Err_x_ast = compute_error(x_ast,x)
    Err_p_ast = compute_error(p_ast,feval)

    def residual_dual(nu_fun):
        if(A.ndim==1):
            return gfeval + A.T*nu_fun
        else:
            return gfeval + A.T@nu_fun        
    feasibility_dual = residual_dual(nu)    

    if (np.all(A < np.nextafter(0,1))): #A is zero matrix
        system_matrix = Hfeval
        rhs = -gfeval
        n = x.size
        p = 0
        feasibility_primal = 0
    else:
        if(A.ndim == 1):
            p = 1
            n = x.size
            zero_matrix = np.zeros(p)
            first_stack = np.column_stack((Hfeval, A.T))
            second_stack = np.row_stack((A.reshape(1,n).T,zero_matrix)).reshape(1,n+1)[0]
        else:
            p,n = A.shape
            zero_matrix = np.zeros((p,p))
            first_stack = np.column_stack((Hfeval, A.T))
            second_stack = np.column_stack((A,zero_matrix))

        system_matrix = np.row_stack((first_stack,second_stack))
        
        residual_primal = lambda x_fun: A@x_fun-b
        
        feasibility_primal = residual_primal(x)
        
        rhs = -np.row_stack((feasibility_dual.reshape(n,1), feasibility_primal.reshape(p,1))).T[0]

    #Newton's direction and Newton's decrement
    dir_desc = np.linalg.solve(system_matrix, rhs)
    dir_Newton_primal = dir_desc[0:n]
    dec_Newton = -gfeval.dot(dir_Newton_primal)
    dir_Newton_dual = dir_desc[n:(n+p)]

    norm_residual_eval = norm_residual(feasibility_primal,
                                       feasibility_dual)

    norm_residual_primal = np.linalg.norm(feasibility_primal)
    norm_residual_dual = np.linalg.norm(feasibility_dual)
    print('I\t||res_primal||\t||res_dual|| \tNewton Decrement\tError x_ast\tError p_ast\tline search\tCondHf')
    print('{}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{}\t\t{:0.2e}'.format(iteration,norm_residual_primal,
                                                                                  norm_residual_dual,
                                                                                  dec_Newton,Err_x_ast,
                                                                                  Err_p_ast,"---",
                                                                                  condHf))
    
    stopping_criteria = norm_residual_primal > tol
    iteration+=1
    while(stopping_criteria>tol and iteration < maxiter):
        der_direct = -dec_Newton
        t = line_search_for_residual_by_backtracking(residual_primal, residual_dual,
                                                     dir_Newton_primal, dir_Newton_dual,
                                                     x, nu,
                                                     norm_residual_eval
                                                     )
        x = x + t*dir_Newton_primal
        nu = nu + t*dir_Newton_dual
        feval = f(x)
        
        
        if gf_symbolic:
            gfeval = gf_symbolic(x)
        else:
            gfeval = gradient_approximation(f,x)
        
        if Hf_symbolic:
            Hfeval = Hf_symbolic(x)
        else:
            Hfeval = Hessian_approximation(f,x)
        if(A.ndim == 1):
            first_stack = np.column_stack((Hfeval, A.T))
        else:
            first_stack = np.column_stack((Hfeval, A.T))

        system_matrix = np.row_stack((first_stack,second_stack))
        
        feasibility_primal = residual_primal(x)
        feasibility_dual = residual_dual(nu)
        rhs = -np.row_stack((feasibility_dual.reshape(n,1), feasibility_primal.reshape(p,1))).T[0]
            
        #Newton's direction and Newton's decrement
        dir_desc = np.linalg.solve(system_matrix, rhs)
        dir_Newton_primal = dir_desc[0:n]
        dec_Newton = -gfeval.dot(dir_Newton_primal)
        dir_Newton_dual = dir_desc[n:(n+p)]

        Err_x_ast = compute_error(x_ast,x)
        Err_p_ast = compute_error(p_ast,feval)
        norm_residual_eval = norm_residual(feasibility_primal,
                                           feasibility_dual)
        norm_residual_primal = np.linalg.norm(feasibility_primal)
        norm_residual_dual = np.linalg.norm(feasibility_dual)
        print('{}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}'.format(iteration,norm_residual_primal,
                                                                                         norm_residual_dual,
                                                                                         dec_Newton,Err_x_ast,
                                                                                         Err_p_ast,t,
                                                                                         condHf))

        stopping_criteria = norm_residual_primal > tol
        if t<tol_backtracking: #if t is less than tol_backtracking then we need to check the reason
            iteration = maxiter - 1
        iteration+=1
    
    if iteration == maxiter and t < tol_backtracking:
        print("Backtracking value less than tol_backtracking, try other initial point")
        return [None,None,None,None]
    else:
        if iteration == maxiter:
            print("------------------------------------------------------------")
            print("------------------------------------------------------------")
            print("------------------------------------------------------------")
            print("Reached maximum of iterations, check primal feasibility")
            print("Continuing with Newtons method for feasible initial point")
            return Newtons_method_log_barrier_feasible_init_point(f,A, constraint_inequalities,
                                                                  t_path, x,tol, 
                                                                  tol_backtracking, x_ast, p_ast, 
                                                                  maxiter,gf_symbolic,Hf_symbolic)
        else:
            print("------------------------------------------------------------")
            print("------------------------------------------------------------")
            print("------------------------------------------------------------")
            print("Beginning Newtons method for feasible initial point")
            return Newtons_method_log_barrier_feasible_init_point(f,A, constraint_inequalities,
                                                                  t_path, x,tol, 
                                                                  tol_backtracking, x_ast, p_ast, 
                                                                  maxiter,gf_symbolic,Hf_symbolic)
def Newtons_method_infeasible_init_point_2nd_version(f,
                                                     A,
                                                     b,
                                                     x_0,
                                                     nu_0,
                                                     tol,
                                                     tol_backtracking,
                                                     x_ast=None,
                                                     p_ast=None,
                                                     maxiter=30,
                                                     gf_symbolic=None,
                                                     Hf_symbolic=None):
    '''
    Newton's method to numerically approximate solution of min f subject to Ax = b.
    Args:
        f (fun): definition of function f as lambda expression or function definition.
        A (numpy ndarray): 2d numpy array of shape (m,n) defines system of constraints Ax=b.
        b (numpy ndarray or float): array that defines system of constraints Ax=b (can be a single number
                                    if just one restriction is defined)
        x_0 (numpy ndarray): initial point for Newton's method. 
        nu_0 (numpy ndarray): initial point for Newton's method.
        tol (float): tolerance that will halt method. Controls stopping criteria.
        tol_backtracking (float): tolerance that will halt method. Controls value of line search by backtracking.
        x_ast (numpy ndarray): solution of min f, now it's required that user knows the solution...
        p_ast (float): value of f(x_ast), now it's required that user knows the solution...
        maxiter (int): maximum number of iterations
        gf_symbolic (fun): definition of gradient of f. If given, no approximation is
                                     performed via finite differences.
        Hf_symbolic (fun): definition of Hessian of f. If given, no approximation is
                                     performed via finite differences.
    Returns:
        x (numpy ndarray): numpy array, approximation of x_ast.
        iteration (int): number of iterations.
        Err_plot (numpy ndarray): numpy array of absolute error between p_ast and f(x) with x approximation
                          of x_ast. Useful for plotting.
        x_plot (numpy ndarray): numpy array that containts in columns vector of approximations. Last column
                        contains x, approximation of solution. Useful for plotting.
    '''
    iteration = 0
    x = x_0
    nu = nu_0

    feval = f(x)

    if gf_symbolic:
        gfeval = gf_symbolic(x)
    else:
        gfeval = gradient_approximation(f, x)

    if Hf_symbolic:
        Hfeval = Hf_symbolic(x)
    else:
        Hfeval = Hessian_approximation(f, x)

    normgf = np.linalg.norm(gfeval)
    condHf = np.linalg.cond(Hfeval)

    Err_plot_aux = np.zeros(maxiter)
    Err_plot_aux[iteration] = compute_error(p_ast, feval)

    Err = compute_error(x_ast, x)

    if (A.ndim == 1):
        p = 1
        n = x.size
        zero_matrix = np.zeros(p)
        first_stack = np.column_stack((Hfeval, A.T))
        second_stack = np.row_stack(
            (A.reshape(1, n).T, zero_matrix)).reshape(1, n + 1)[0]
    else:
        p, n = A.shape
        zero_matrix = np.zeros((p, p))
        first_stack = np.column_stack((Hfeval, A.T))
        second_stack = np.column_stack((A, zero_matrix))

    x_plot = np.zeros((n, maxiter))
    x_plot[:, iteration] = x

    system_matrix = np.row_stack((first_stack, second_stack))

    residual_primal = lambda x_fun: A @ x_fun - b

    def residual_dual(nu_fun):
        if (A.ndim == 1):
            return gfeval + A.T * nu_fun
        else:
            return gfeval + A.T @ nu_fun

    feasibility_primal = residual_primal(x)
    feasibility_dual = residual_dual(nu)

    rhs = np.row_stack(
        (feasibility_dual.reshape(n, 1), feasibility_primal.reshape(p,
                                                                    1))).T[0]

    #Newton's direction and Newton's decrement
    dir_desc = np.linalg.solve(system_matrix, -rhs)
    dir_Newton_primal = dir_desc[0:n]
    dec_Newton = -gfeval.dot(dir_Newton_primal)
    dir_Newton_dual = dir_desc[n:(n + p)]

    norm_residual_eval = norm_residual(feasibility_primal, feasibility_dual)

    norm_residual_primal = np.linalg.norm(feasibility_primal)
    norm_residual_dual = np.linalg.norm(feasibility_dual)
    print(
        'I\t||res_primal||\t||res_dual|| \tNewton Decrement\tError x_ast\tError p_ast\tline search\tCondHf'
    )
    print('{}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{}\t\t{:0.2e}'.
          format(iteration, norm_residual_primal, norm_residual_dual,
                 dec_Newton, Err, Err_plot_aux[iteration], "---", condHf))

    stopping_criteria = norm_residual_eval > tol
    iteration += 1
    while (stopping_criteria > tol and iteration < maxiter):
        der_direct = -dec_Newton
        t = line_search_for_residual_by_backtracking(residual_primal,
                                                     residual_dual,
                                                     dir_Newton_primal,
                                                     dir_Newton_dual, x, nu,
                                                     norm_residual_eval)
        x = x + t * dir_Newton_primal
        nu = nu + t * dir_Newton_dual
        feval = f(x)

        if gf_symbolic:
            gfeval = gf_symbolic(x)
        else:
            gfeval = gradient_approximation(f, x)

        if Hf_symbolic:
            Hfeval = Hf_symbolic(x)
        else:
            Hfeval = Hessian_approximation(f, x)
        if (A.ndim == 1):
            first_stack = np.column_stack((Hfeval, A.T))
        else:
            first_stack = np.column_stack((Hfeval, A.T))

        system_matrix = np.row_stack((first_stack, second_stack))

        feasibility_primal = residual_primal(x)
        feasibility_dual = residual_dual(nu)
        rhs = np.row_stack((feasibility_dual.reshape(n, 1),
                            feasibility_primal.reshape(p, 1))).T[0]

        #Newton's direction and Newton's decrement
        dir_desc = np.linalg.solve(system_matrix, -rhs)
        dir_Newton_primal = dir_desc[0:n]
        dec_Newton = -gfeval.dot(dir_Newton_primal)
        dir_Newton_dual = dir_desc[n:(n + p)]

        Err_plot_aux[iteration] = compute_error(p_ast, feval)
        x_plot[:, iteration] = x
        Err = compute_error(x_ast, x)
        norm_residual_eval = norm_residual(feasibility_primal,
                                           feasibility_dual)
        norm_residual_primal = np.linalg.norm(feasibility_primal)
        norm_residual_dual = np.linalg.norm(feasibility_dual)
        print(
            '{}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}\t{:0.2e}'
            .format(iteration, norm_residual_primal, norm_residual_dual,
                    dec_Newton, Err, Err_plot_aux[iteration], t, condHf))
        stopping_criteria = norm_residual_eval > tol
        if t < tol_backtracking:  #if t is less than tol_backtracking then we need to check the reason
            iter_salida = iteration
            iteration = maxiter - 1
        iteration += 1
    print('{} {:0.2e}'.format("Error of x with respect to x_ast:", Err))
    print('{} {}'.format("Approximate solution:", x))
    cond = Err_plot_aux > np.finfo(float).eps * 10**(-2)
    Err_plot = Err_plot_aux[cond]

    if iteration == maxiter and t < tol_backtracking:
        print(
            "Backtracking value less than tol_backtracking, check approximation"
        )
        iteration = iter_salida
    else:
        if iteration == maxiter:
            print("Reached maximum of iterations, check approximation")
    x_plot = x_plot[:, :iteration]

    return [x, iteration, Err_plot, x_plot]