Решение для решения Excel на Python. Scipy не работает

#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 значительно повысила точность. Я обнаружил, что «корневые» результаты, как правило, более точны, когда я работаю с меньшим количеством компаний. Дешевым решением было бы использовать предыдущий вывод в качестве предположения для вашего следующего запуска и повторить процесс несколько раз, чтобы в конечном итоге получить наиболее точные результаты.