#python #numpy #numerical-methods #numerical-analysis
Вопрос:
Я работаю над реализацией числового трехдиагонального гессиана в Python. Для этого я использую метод конечных разностей, это то, что я пробовал до сих пор:
import numpy as np
def principal_diagonal(x, fun):
h = 10e-6
result = list()
for i in range(len(x)):
temp_forward = np.array(x)
temp_backward = np.array(x)
temp_forward[i] = temp_forward[i] h
temp_backward[i] = temp_backward[i] - h
value = (fun(temp_forward) - 2 * fun(x) fun(temp_backward)) / (h ** 2)
result.append(value)
return(np.array(result))
def one_above_diagonal(x, fun):
h = 10e-6
result = list()
for i in range(len(x)-1):
clean = np.array(x)
forward_first = np.array(x)
forward_first[i] = forward_first[i] h
forward_second = np.array(forward_first)
forward_second[i 1] = forward_second[i 1] h
clean[i 1] = clean[i 1] h
value = ((fun(forward_second) - fun(clean)) - (fun(forward_first) - fun(x))) / (h*h)
result.append(value)
return(np.array(result))
def tridiag_hessian(x, fun):
new_array = np.ndarray((len(x), len(x)))
di = np.diag_indices(len(x))
new_array[di] = principal_diagonal(x, fun)
new_array[(np.arange(len(x)-1), np.arange(1,len(x)))] = one_above_diagonal(x, fun)
new_array[(np.arange(1,len(x)), np.arange(len(x)-1))] = one_above_diagonal(x, fun)
return(new_array)
Я протестировал свои функции с помощью функции Розенброка, и мои результаты были следующими:
from scipy.optimize import rosen
tridiag_hessian(np.array([1, 2], "float64"), rosen)
array([[ 401.99964246, -400.00216472],
[-400.00216472, 200.00001655]])
Результаты довольно близки к фактическому решению, что вполне прилично, но для более сложных задач я хотел бы иметь большую точность. Есть ли в моем коде что-нибудь, что можно было бы скорректировать? Или, может быть, альтернативный метод? Я попытался использовать numdifftools
, а затем приступил к «тридиагонализации» результатов этой функции, но это было невероятно медленно, когда я попробовал этот подход в задаче, в которой использовалась функция с более чем 40 переменными. Я также рассматривал возможность использования np.sqrt(np.finfo(float).eps)
вместо 0,000001, но я не получил никакой точности.