Hello! I have this implementation of f1, gradiente_f1, hessiana_f1, armijo and metodo_quase_newton_BFGS.
def f1(x):
    if len(x) != 7:
        raise ValueError("x deve ter 7 elementos.")
    result = sum(100 * (x[i+1] - x[i]**2)**2 + (1 - x[i])**2 for i in range(6))
    return result
def gradiente_f1(x):
    grad = np.zeros(7)
    grad[0] = -400 * x[0] * (x[1] - x[0]**2) + 2 * (x[0] - 1)
    for j in range(1, 6):
        grad[j] = 200 * (x[j] - x[j-1]**2) - 400 * x[j] * (x[j+1] - x[j]**2) + 2 * (x[j] - 1)
    grad[6] = 200 * (x[6] - x[5]**2)
    return grad
def hessiana_f1(x):
    n = len(x)
    H = np.zeros((n, n))
    for i in range(n):
        if i < n -1:
            H[i, i] += 1200*x[i]**2 - 400*x[i+1] + 2
            H[i, i+1] -= 400*x[i]
            H[i+1, i] -= 400*x[i]
        else:
            H[i, i] += 200
    return H
def armijo(f, grad_f, x_atual, d, gamma=0.8, eta=0.25):
    t = 1
    # Compute the gradient at the current point
    # grad_atual = [sp.diff(f, var).subs({variables[i]: x_atual[i] for i in range(len(x_atual))}) for i, var in enumerate(variables)]
    # if funcao == "1":
    #     grad_atual = gradiente_f1(x_atual)
    #     p1 = f1(np.array(x_atual) + t * np.array(d))
    #     p2 = f1(np.array(x_atual)) + eta * t * np.dot(grad_atual, d)
    # elif funcao == "2":
    #     grad_atual = gradiente_f2(x_atual)
    #     p1 = f2(np.array(x_atual) + t * np.array(d))
    #     p2 = f2(np.array(x_atual)) + eta * t * np.dot(grad_atual, d)
    # else:
    #     grad_atual = gradiente_f3(x_atual)
    #     # print(grad_atual)
    #     p1 = f3(np.array(x_atual) + t * np.array(d))
    #     p2 = f3(np.array(x_atual)) + eta * t * np.dot(grad_atual, d)
    grad_atual = grad_f(x_atual)
    p1 = f(np.array(x_atual) + t * np.array(d))
    p2 = f(np.array(x_atual)) + eta * t * np.dot(grad_atual.T, d)
    
    loop = False
    # Evaluate f1 and f2 initially
    # f1 = f.subs({variables[i]: x_atual[i] + t * d[i] for i in range(len(x_atual))}).evalf()
    # f2 = f.subs({variables[i]: x_atual[i] for i in range(len(x_atual))}).evalf() + eta * t * sum(grad_atual[i] * d[i] for i in range(len(x_atual))).evalf()
    k = 0
    # print(f1, f2)
    print(p1, p2)
    while p1 > p2:
        loop = True
        t = gamma * t
        # f1 = f.subs({variables[i]: x_atual[i] + t * d[i] for i in range(len(x_atual))}).evalf()
        # f2 = f.subs({variables[i]: x_atual[i] for i in range(len(x_atual))}).evalf() + eta * t * sum(grad_atual[i] * d[i] for i in range(len(x_atual)))
        # if funcao == "1":
        #     p1 = f1(np.array(x_atual) + t * np.array(d))
        #     p2 = f1(np.array(x_atual)) + eta * t * np.dot(grad_atual, d)
        # elif funcao == "2":
        #     p1 = f2(np.array(x_atual) + t * np.array(d))
        #     p2 = f2(np.array(x_atual)) + eta * t * np.dot(grad_atual, d)
        # else:
        #     p1 = f3(np.array(x_atual) + t * np.array(d))
        #     p2 = f3(np.array(x_atual)) + eta * t * np.dot(grad_atual, d)
        p1 = f(np.array(x_atual) + t * np.array(d))
        p2 = f(np.array(x_atual)) + eta * t * np.dot(grad_atual.T, d)
        
        k += 1
    if loop == False:
        k += 1
    return t, k

# # Exemplo de uso
# x_atual = [1, 0]  # Ponto inicial
# d = [3, 1]        # Direção de descida
# eta = 0.25
# gamma = 0.8

# t_final = armijo(f, x_atual, d, gamma, eta)
# print(f"Valor final de t: {t_final}")

def metodo_quase_newton_BFGS(f, grad_f, x0, gamma=0.8, eta=0.25, epsilon=1e-6, max_iter=10000):
    x_atual = np.array(x0, dtype=float).reshape(-1, 1)
    k = 0
    armijo_total_k = 0

    # Inicializa a matriz identidade para H_atual
    n = len(x0)
    H_atual = np.eye(n)

    # Listas para armazenar os dados
    iteracoes = []
    x_vals = []
    grad_norms = []
    func_vals = []
    steps = []
    armijo_iters = []

    while k < max_iter:
        grad_atual = grad_f(x_atual)
        grad_norm = np.linalg.norm(grad_atual)
        f_atual = f(x_atual)
        if grad_norm < epsilon:
            break  # Critério de parada

        # Direção de descida
        d_atual = -H_atual.dot(grad_atual)
        # print("d", d_atual)

        # Tamanho do passo usando Armijo
        t, armijo_k = armijo(f, grad_f, x_atual, d_atual)

        # Atualiza o ponto atual
        x_novo = x_atual + t * d_atual

        # Atualiza os vetores p_k e q_k
        p_k = x_novo - x_atual
        grad_novo = grad_f(x_novo)
        q_k = grad_novo - grad_atual

        # Atualização de H (matriz Hessiana aproximada) usando a fórmula de BFGS
        denom = q_k.T.dot(p_k)
        if denom == 0:
            print(f"Iteração {k+1}: Divisão por zero na atualização de H.")
            break

        rho = 1.0 / denom
        term1 = (1 + (q_k.T @ H_atual @ q_k) * rho).item() * (p_k @ p_k.T) * rho
        term2 = (H_atual @ q_k @ p_k.T + p_k @ q_k.T @ H_atual) * rho
        H_atual = H_atual + term1 - term2

        # Atualiza os valores para a próxima iteração
        x_atual = x_novo
        k += 1
        armijo_total_k += armijo_k

        # Armazenar os dados da iteração
        iteracoes.append(k)
        x_vals.append(x_atual.flatten().tolist())
        grad_norms.append(grad_norm)
        func_vals.append(f_atual)
        steps.append(t)
        armijo_iters.append(armijo_k)

    # Criação de um DataFrame com os dados coletados
    data_dict = {
        'Iteração': iteracoes,
        'Norma_Gradiente': grad_norms,
        'Valor_Função': func_vals,
        'Passo_t': steps,
        'Armijo_Iterações': armijo_iters
    }
    for dim in range(len(x_vals[0])):
        data_dict[f'x{dim}'] = [x[dim] for x in x_vals]
    
    data = pd.DataFrame(data_dict)

    return x_atual, k, armijo_total_k, data
My problem is that when I run the BFGS function, p1 is getting a value of an array instead of a number. Which doesn't make any sense, since f1() returns a value. 

resultado, iteracoes, arm, dados = metodo_quase_newton_BFGS(f1, gradiente_f1, [1,0,0,0,0,0,0])
print(f"Resultado: {resultado}, Iterações: {iteracoes}, Armijo: {arm}")
dados
[1.54113289e+13 9.92418850e+11 6.90900000e+03 6.90900000e+03
 6.90900000e+03 6.90900000e+03 1.05000000e+02] [-50100.]
C:\Users\ricar\AppData\Local\Temp\ipykernel_19704\3432167008.py:3: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  grad[0] = -400 * x[0] * (x[1] - x[0]**2) + 2 * (x[0] - 1)
C:\Users\ricar\AppData\Local\Temp\ipykernel_19704\3432167008.py:5: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  grad[j] = 200 * (x[j] - x[j-1]**2) - 400 * x[j] * (x[j+1] - x[j]**2) + 2 * (x[j] - 1)
C:\Users\ricar\AppData\Local\Temp\ipykernel_19704\3432167008.py:6: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  grad[6] = 200 * (x[6] - x[5]**2)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[131], line 1
----> 1 resultado, iteracoes, arm, dados = metodo_quase_newton_BFGS(f1, gradiente_f1, [1,0,0,0,0,0,0])
      2 print(f"Resultado: {resultado}, Iterações: {iteracoes}, Armijo: {arm}")
      3 dados

Cell In[129], line 30
     26 d_atual = -H_atual.dot(grad_atual)
     27 # print("d", d_atual)
     28 
     29 # Tamanho do passo usando Armijo
---> 30 t, armijo_k = armijo(f, grad_f, x_atual, d_atual)
     32 # Atualiza o ponto atual
     33 x_novo = x_atual + t * d_atual

Cell In[113], line 29
     27 # print(f1, f2)
     28 print(p1, p2)
---> 29 while p1 > p2:
     30     loop = True
     31     t = gamma * t

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()