Хорошо, я знаю, что это было задано раньше с ограниченным примером масштабирования [-1, 1]
интервалов [a, b]
Различные интервалы для квадратурности Гаусса-Лежандра в numpy, но никто опубликовал, как обобщить это для [-a, Infinity]
(как это сделано ниже, но пока (пока) не быстро). Также это показывает, как вызвать сложную функцию (в количественном определении цены) в любом случае с несколькими реализациями. Существует контрольный quad
код, за которым следует leggauss
, со ссылками на примеры кода о том, как реализовать адаптивный алгоритм. Я работал над большинством связанных трудностей adaptive algorithm
- он в настоящее время печатает сумму разделенного интеграла, чтобы показать, что он работает правильно. Здесь вы найдете функции для преобразования диапазона от [-1, 1]
до [0, 1]
до [a, Infinity]
(спасибо @AlexisClarembeau). Чтобы использовать адаптивный алгоритм, мне пришлось создать еще одну функцию для преобразования из [-1, 1]
в [a, b]
, которая возвращается обратно в функцию [a, Infinity]
.
import numpy as np
from scipy.stats import norm, lognorm
from scipy.integrate import quad
a = 0
degrees = 50
flag=-1.0000
F = 1.2075
K = 0.1251
vol = 0.43
T2 = 0.0411
T1 = 0.0047
def integrand(x, flag, F, K, vol, T2, T1):
d1 = (np.log(x / (x+K)) + 0.5 * (vol**2) * (T2-T1)) / (vol * np.sqrt(T2 - T1))
d2 = d1 - vol*np.sqrt(T2 - T1)
mu = np.log(F) - 0.5 *vol **2 * T1
sigma = vol * np.sqrt(T1)
return lognorm.pdf(x, mu, sigma) * (flag * x*norm.cdf(flag * d1) - flag * (x+K)*norm.cdf(flag * d2))
def transform_integral_0_1_to_Infinity(x, a):
return integrand(a+(x/(1-x)), flag, F, K, vol, T2, T1) *(1/(1-x)**2);
def transform_integral_negative1_1_to_0_1(x, a):
return 0.5 * transform_integral_0_1_to_Infinity((x+1)/2, a)
def transform_integral_negative1_1_to_a_b(x, w, a, b):
return np.sum(w*(0.5 * transform_integral_0_1_to_Infinity(((x+1)/2*(b-a)+a), a)))
def adaptive_integration(x, w, a=-1, b=1, lastsplit=False, precision=1e-10):
#split the integral in half assuming [-1, 1] range
midpoint = (a+b)/2
interval1 = transform_integral_negative1_1_to_a_b(x, w, a, midpoint)
interval2 = transform_integral_negative1_1_to_a_b(x, w, midpoint, b)
return interval1+interval2 #just shows this is correct for splitting the interval
def integrate(x, w, a):
return np.sum(w*transform_integral_negative1_1_to_0_1(x, a))
x, w = np.polynomial.legendre.leggauss(degrees)
quadresult = quad(integrand, a, np.Inf, args=(flag, F, K, vol, T2, T1), epsabs=1e-1000)[0]
GL = integrate(x, w, a)
print("Adaptive Sum Result:")
print(adaptive_integration(x, w))
print("GL result");
print(GL)
print("QUAD result")
print(quadresult)
По-прежнему необходимо увеличить скорость и точность с меньшими размерами, так как я не могу вручную отрегулировать диапазон degrees
для -a
, чтобы получить сходимость. Чтобы проиллюстрировать, почему это проблема, введите следующие значения: a=-20
, F=50
, затем запустите. Вы можете увеличить degrees=1000
и увидеть, что для этого алгоритма Гаусса-Лежандра нет никакой пользы, если он не применяется разумно. Мое требование скорости - добраться до 0.0004s за цикл, тогда как последний алгоритм я Cythonized занял около 0.75s, поэтому я пытаюсь использовать алгоритм с низкой степенью точности с Gauss-Legendre. С Cython и многопоточным это требование от полностью оптимизированной реализации Python составляет примерно 0,007 с за цикл (не-векторизованная циклическая, неэффективная процедура может быть 0,1 с за цикл с degrees=20
, то есть %timeit adaptive_integration(x,w)
.
Возможное решение, которое я реализовал наполовину, находится здесь http://online.sfsu.edu/meredith/Numerical_Analysis/improper_integrals на страницах 5/6, adaptive integration
, тогда как интервал a-b
(в этом случае я написал функцию transform_integral_negative1_1_to_a_b
), где интервал делится на 2 (@0.5
), тогда функция оценивается на этих 1/2 интервалах, а сумма двух 0->0.5
+ 0.5->1
сравниваются с результатами функции для всего диапазона 0->1
. Если точность не находится в пределах допуска, диапазон далее подразделяется на 0.25
и 0.75
, функция снова оценивается для каждого интервала и сравнивается с предыдущими 1/2 интервальными суммами @0.5
. Если 1 сторона находится в пределах допуска (например, abs(0->0.5 - (0->0.25 + 0.25->0.5)) < precision
), но на другой стороне нет, расщепление останавливается сбоку в пределах допуска, но продолжается с другой стороны, пока не будет достигнуто значение precision
. В этот момент результаты для каждого среза интервала суммируются для получения полного интеграла с большей точностью.
Скорее всего, существуют более быстрые и лучшие способы решения этой проблемы. Мне все равно, пока это быстро и точно. Вот лучшее описание интеграционных подпрограмм, которые я нашел для справки http://orion.math.iastate.edu/keinert/computation_notes/chapter5.pdf. Награда - 100pts bounty + 15pts для принятия ответа. Благодарим вас за помощь в создании этого кода FAST и ACCURATE!
EDIT:
Вот мое изменение кода adaptive_integration
- если кто-то может быстро выполнить эту работу, я могу принять ответ и наградить наградой. Этот код Mathematica на стр. 7 http://online.sfsu.edu/meredith/Numerical_Analysis/improper_integrals выполняет обычную попытку. Он работает над рутиной, которая не сходится хорошо, см. Приведенные ниже переменные. В настоящий момент мой код ошибки: RecursionError: maximum recursion depth exceeded in comparison
на некоторых входах или если параметр degrees
установлен слишком высоким или не приближается к результату quad
, когда он работает, значит, что-то явно не так.
def adaptive_integration(x, w, a, b, integralA2B, remainingIterations, firstIteration, precision=1e-9):
#split the integral in half assuming [-1, 1] range
if remainingIterations == 0:
print('Adaptive integration failed on the interval',a,'->',b)
if np.isnan(integralA2B): return np.nan
midpoint = (a+b)/2
interval1 = transform_integral_negative1_1_to_a_b(x, w, a, midpoint)
interval2 = transform_integral_negative1_1_to_a_b(x, w, midpoint, b)
if np.abs(integralA2B - (interval1 + interval2)) < precision :
return(interval1 + interval2)
else:
return adaptive_integration(x, w, a, midpoint, interval1, (remainingIterations-1), False) + adaptive_integration(x, w, midpoint, b, interval2, (remainingIterations-1), False)
#This example doesn't converge to Quad
# non-converging interval inputs
a = 0 # AND a = -250
degrees = 10
flag= 1
F = 50
K = 0.1251
vol = 0.43
T2 = 0.0411
T1 = 0.0047
print(adaptive_integration(x, w, -1, 1, GL, 500, False))
Выход с degrees=100
(после вычисления GL
с degrees=10000
для лучшей начальной оценки, в противном случае алгоритм всегда согласен с его собственной точностью, по-видимому, и не вызывает адаптивный путь, который терпит неудачу каждый раз):
GL result:
60.065205169286379
Adaptive Sum Result:
RecursionError: maximum recursion depth exceeded in comparison
QUAD result:
68.72069173210338