#python #optimization #scipy
#python #оптимизация #scipy
Вопрос:
У меня есть простой проект нелинейной оптимизации. Я хочу найти ставку дисконтирования для будущих денежных потоков и конечной стоимости, чтобы сумма равнялась указанному NPV. Ниже приведены некоторые эксперименты, которые я пробовал.
Обе компании имеют фиксированный денежный поток в 10 с разным NPV. Результаты коэффициента дисконтирования должны составлять 1,074 (7,4%) и 1,052 (5,2%) соответственно. Решатель Excel быстро нашел корни, в то время как Scipy вернул отсутствие конвергенции.
import numpy as np
from scipy.optimize import newton_krylov
from scipy.optimize.nonlin import NoConvergence
cf_fy1 = [10]*2
cf_fy2 = [10]*2
cf_fy3 = [10]*2
cf_fy4 = [10]*2
cf_fy5 = [10]*2
cf_fy6 = [10]*2
npv = [200, 400]
def mydr(dr):
terminal_value = np.divide(cf_fy6, np.subtract(dr, 1.03))
ev = np.sum([np.divide(cf_fy1, np.power(dr, 1)),
np.divide(cf_fy2, np.power(dr, 2)),
np.divide(cf_fy3, np.power(dr, 3)),
np.divide(cf_fy4, np.power(dr, 4)),
np.divide(cf_fy5, np.power(dr, 5)),
np.divide(terminal_value, np.power(dr, 5))], axis=0)
z = np.subtract(ev, npv)
return abs(z)
try:
sol = newton_krylov(mydr, [1.1] * len(npv))
converged = True
except NoConvergence as e:
sol = e.args[0]
converged = False
Заранее всем спасибо!
Ответ №1:
Согласно документам, метод Ньютона-Крылова (только?) подходит для решения крупномасштабных задач. И метод Ньютона-Крылова не сходится с вашей начальной точкой. Поскольку это очень простая проблема, я бы использовал вместо этого общий метод root:
In [13]: from scipy.optimize import root
In [14]: root(mydr, x0 = [1.1, 1.1])
Out[14]:
fjac: array([[-9.99999700e-01, 7.74730469e-04],
[-7.74730469e-04, -9.99999700e-01]])
fun: array([9.03350275e-06, 1.53610404e-06])
message: 'The solution converged.'
nfev: 40
qtf: array([-9.03230997e-06, -1.54310211e-06])
r: array([ -4128.02172068, -37514.05364792, 19083.3896212 ])
status: 1
success: True
x: array([1.07391362, 1.05176871])
При необходимости вы можете установить используемый решатель с помощью method
опции (обратите внимание на другую начальную точку):
In [15]: root(mydr, x0 = [1.05, 1.05], method="krylov")
Out[15]:
fun: array([3.97903932e-13, 1.68824954e-11])
message: 'A solution was found at the specified tolerance.'
nit: 7
status: 1
success: True
x: array([1.07391362, 1.05176871])
Комментарии:
1. Огромное спасибо. Теперь это работает. Замена на root значительно повысила точность. Я обнаружил, что «корневые» результаты, как правило, более точны, когда я работаю с меньшим количеством компаний. Дешевым решением было бы использовать предыдущий вывод в качестве предположения для вашего следующего запуска и повторить процесс несколько раз, чтобы в конечном итоге получить наиболее точные результаты.