NIntegrate-почему в Mathematica 8 в этом случае он намного медленнее?

у меня есть код Mathematica, где я должен оценить численно тысячи интегралов, подобных этому

NIntegrate[
    (Pi*Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] + 
    Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), 
    {x, 0, 100}, {y, 0, 100}
] //AbsoluteTiming

Интеграл-хорошая абсолютно интегрируемая функция без особенностей, которая экспоненциально распадается в одном направлении и как 1/y^3 в другом направлении.

на отлично работала в Mathematica 7, но в новейшей версии 8.0.4 она замедляется на два порядка. Я предполагаю, что в новой версии он пытается лучше контролируйте ошибку, но за счет этого огромного увеличения времени. Есть ли некоторые настройки, которые я мог бы использовать, чтобы вычисление продолжалось с той же скоростью, что и в Mathematica 7?

3 ответов


ruebenkoответ и комментарии от user1091201 и Леонид вместе объединяются, чтобы дать правильные ответы.

на изменить 1 ответ ruebenko правильный первый ответ для общие ситуации, подобные этой, то есть добавить опцию Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}:

expr = (Pi*
      Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
         Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y));

NIntegrate[expr, {x, 0, 100}, {y, 0, 100}, 
  Method -> {"SymbolicPreprocessing", 
    "OscillatorySelection" -> False}] // AbsoluteTiming

и user1091201 комментарий предлагая Method -> "GaussKronrodRule" близко к самому быстрому возможному ответьте за этот конкретный проблема.

я опишу, что происходит в NIntegrate в этом конкретном примере, и по пути дам несколько советов по обработке, по-видимому, похожих ситуаций в целом.

Выбор Метода

в этом примере NIntegrate проверяет expr, приходит к выводу, что многомерное "LevinRule" является лучшим методом для этого интеграла, и применяет его. Однако, для этого конкретного например, " LevinRule "медленнее, чем" MultidimensionalRule "(хотя" LevinRule " получает более удовлетворительную оценку ошибки). "LevinRule" также медленнее, чем любое из нескольких одномерных правил типа Гаусса, повторяемых над двумя измерениями, таких как "GaussKronrodRule", который user1091201 нашли.

NIntegrate принимает свое решение после выполнения некоторого символического анализа интеграла. Существует несколько типов предварительной обработки символов; параметр Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False} отключает один тип предварительной обработки символов.

по существу, с включенным "OscillatorySelection", NIntegrate выбирает"LevinRule". При отключенном " OscillatorySelection "NIntegrate выбирает" MultidimensionalRule", который быстрее для этого интеграла, хотя мы можем не доверять результату, основанному на сообщении NIntegrate::slwcon, которое указывает на необычно медленную сходимость.

это часть, где Mathematica 8 отличается от Mathematica 7: Mathematica 8 добавляет "LevinRule "и связанные с ним эвристики выбора метода в"OscillatorySelection".

помимо того, что NIntegrate выбирает другой метод, отключение "OscillatorySelection" также экономит время, затрачиваемое на фактическую символьную обработку, которая может быть значительной в некоторых случаях.

задание Method -> "GaussKronrodRule" переопределяет и пропускает символьную обработку, связанную с выбором метода, и вместо этого использует 2-D декартовое правило продукта Method -> {"CartesianRule", Method -> {"GaussKronrodRule", "GaussKronrodRule"}}. Это случается очень быстрый метод для этого интеграла.

Другая Символьная Обработка

и ruebenko ' s Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False} и user1091201 ' s Method -> "GaussKronrodRule" не отключайте другие формы символьной обработки, и это, как правило, хорошо. См.эта часть расширенной документации NIntegrate для списка типов символьной предварительной обработки, которые могут быть применены. В частности, "SymbolicPiecewiseSubdivision" очень ценен для интегралы, которые не аналитичны в нескольких точках из-за наличия кусочно-функций.

отключить все символьная обработка и получить только методы по умолчанию с параметрами метода по умолчанию, используйте Method -> {Automatic, "SymbolicProcessing" -> 0}. Для одномерных интегралов это в настоящее время составляет Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule"} с определенными настройками по умолчанию для всех параметров этих методов (количество точек интерполяции в правиле, тип обработки сингулярности для глобально-адаптивной стратегии и т. д.). Для многомерные интегралы, в настоящее время он составляет Method -> {"GlobalAdaptive", Method -> "MultidimensionalRule"}, опять же с определенными значениями параметров по умолчанию. Для многомерных интегралов будет использоваться метод Монте-Карло.

я не рекомендую переключаться прямо на Method -> {Automatic, "SymbolicProcessing" -> 0} в качестве первого шага оптимизации для NIntegrate, но в некоторых случаях это может быть полезно.

самый быстрый метод

есть только около всегда какой-то способ ускорить численное интегрирование по крайней мере бит, иногда много, так как есть так много параметров, которые выбираются эвристически, что вы можете извлечь выгоду из настройки. (Посмотрите на различные параметры и параметры, которые метод" LevinRule" или "стратегия GlobalAdaptive" имеет, включая все их под-методы и т. д.)

тем не менее, вот самый быстрый метод, который я нашел для этого конкретного интеграла:

NIntegrate[(Pi*
      Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
         Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0, 
   100}, {y, 0, 100}, 
  Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule", 
    "SingularityDepth" -> Infinity}] // AbsoluteTiming

(параметр "SingularityDepth" -> Infinity отключает обработку сингулярности преобразования.)

диапазон интеграции

кстати, ваш желаемый диапазон интеграции действительно {x, 0, 100}, {y, 0, 100} или {x, 0, Infinity}, {y, 0, Infinity} истинный пожеланный ряд интеграции для вашего применения?

Если вам действительно нужны {x, 0, Infinity}, {y, 0, Infinity}, я предлагаю использовать это вместо этого. Для бесконечной длины интеграции колеблется, NIntegrate compactifies подынтегральной функции в конечном диапазоне, эффективно образцов в геометрически размеченный путь. Это обычно гораздо эффективнее чем линейные (равномерно) разнесенные образцы, которые используются для конечных диапазонов интегрирования.


вот решение:

NIntegrate[(Pi*
      Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
         Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0, 
   100}, {y, 0, 100}, 
  Method -> "AdaptiveMonteCarlo"] // AbsoluteTiming

вы также можете использовать ParallelTry для тестирования различных методов параллельно.

замедление для определенных аргументов может произойти при реализации новых методов или изменении эвристики. Они могут помочь решить новый класс проблем за счет того, что некоторые другие становятся медленнее. Нужно точно выяснить, что здесь происходит.

возможно, вы захотите изменить тему своего вопроса - это означает, что все интегралы оценивают медленнее в V8, что неверно.

изменить 1: Я думаю, что он застрял в LevinRule (новый в V8 для колебательных интегралов), поэтому, я думаю, это должно отключить это.

NIntegrate[(Pi*
      Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
         Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0, 
   100}, {y, 0, 100}, 
  Method -> {"SymbolicPreprocessing", 
    "OscillatorySelection" -> False}] // AbsoluteTiming

для этого конкретного интеграла основным виновником, по-видимому, является интеграция над x, вероятно, из-за наличия как быстро разлагающиеся и сильно колеблющихся частей. Кроме того, в этом случае можно выполнить интеграцию через x аналитически:

In[92]:= 
-Integrate[(Pi*Cos[(Pi*(-2*x+y))/(1+y)]+(1+y)*(-Sin[(2*Pi*x)/(1+y)]+Sin[(Pi*(-2*x+y))/(1+y)]))/
 (E^x*  (1+y)),x]/.x->0//FullSimplify

Out[92]= (-\[Pi] (1+y) (2+Cos[(\[Pi] y)/(1+y)])+(2 \[Pi]^2+(1+y)^2) Sin[(\[Pi] y)/(1+y)])/
(4 \[Pi]^2+(1+y)^2)

(я отбросил значение на верхнем пределе, так как оно равномерно очень мало для y). Затем можно интегрировать через y численно, чтобы получить правильный результат:

In[94]:= NIntegrate[%,{y,0,100}]
Out[94]= 1.17525

более общего обходным путем было бы разделить x и y интеграция как так:

ClearAll[f];
f[y_?NumericQ, xrange : {_?NumericQ, _?NumericQ}] :=
  NIntegrate[(Pi*
   Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + 
     y)*(-Sin[(2*Pi*x)/(1 + y)] + 
     Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, First@xrange, Last@xrange}];

и тогда у нас есть:

In[105]:= NIntegrate[f[y,{0,100}],{y,0,100}]//Timing
Out[105]= {2.578,1.17525}

который не пылает быстро, но достаточно быстро. Эта процедура не всегда будет работать так хорошо (поскольку 2D-сетка интеграции, полученная в результате этой процедуры, не всегда будет оптимальной), но должна работать достаточно хорошо, когда Интеграл таков, что интеграции над x и y достаточно "разъединенный".