Бібліотека C ++ для числової інтеграції (квадратура)


10

У мене є своя маленька підпрограма для числової інтеграції (квадратури), яка є адаптацією C ++ програми ALGOL, опублікованої Bulirsch & Stoer у 1967 році (Numerische Mathematik, 9, 271-278).

Мені хотілося б перейти до більш сучасного (адаптивного) алгоритму і поцікавитися, чи існують (безкоштовні) бібліотеки C ++, які надають таке. Я мав вигляд GSL (що є C), але це постачається з жахливим API (хоча числові показники можуть бути хорошими). Є ще щось?

Корисний API виглядатиме так:

double quadrature(double lower_integration_limit,
                  double upper_integration_limit,
                  std::function<double(double)> const&func,
                  double desired_error_bound_relative=1.e-12,
                  double desired_error_bound_absolute=0,
                  double*error_estimate=nullptr);

7
Як і в сторону, ви побачите, що багато кращих реалізацій в галузі обчислювальної техніки мають "погані" API лише тому, що вони розроблялися протягом десятиліть, а не за місяці чи роки іншого програмного забезпечення. Я думаю, що було б прийнятним і, ймовірно, дуже корисним для вас, щоб написати API для обгортки та внутрішньо викликати менш чистий API. Це дає вам перевагу приємного API у ваших основних кодах, а також дозволяє легко перемикатися між різними бібліотеками квадратури, лише переписавши одну функцію.
Годрик Провидник

1
@GodricSeer Якби це було просто, я б це зробив. Однак це не так. Для API GSL необхідний попередньо виділений буфер, з якого, можливо, нічого не використовується, але потенційно він може бути занадто малим (вимагає чергового дзвінка з більшою кількістю пам'яті). Правильна реалізація була б рекурсивною, не потребує розподілу, зберігає всі дані в стеку та забезпечує чисту API.
Вальтер

1
@GodricSeer Ще одна серйозна проблема з GSL API полягає в тому, що він приймає лише функції без стану (оскільки він використовує простий вказівник функції). Генерування безпечного API для функцій із станом із цього обов'язково неефективне.
Вальтер

2
Я погоджуюся з Годриком Провидцем, написання обгортки - найкращий варіант. Я не вважаю правильним, що "GSL приймає лише функції без стану": тут у документах сказано, що a gsl_function- це покажчик функції разом із деяким непрозорим покажчиком даних, який може містити ваш стан. По-друге, є певні занепокоєння щодо ефективності (повторного) розподілу довільно великих робочих буферів, так що ця частина має хоча б якесь вагоме обґрунтування.
Кирило

1
Ще один коментар щодо попередньо виділеного буфера GSL. Розмір робочої області визначається максимальною кількістю інтервалів - оскільки ви хочете, щоб рутина квадратури так і не вийшла, якщо вона займає занадто багато адаптивних бісекцій, просто встановіть розмір робочої області на деяку верхню межу кількості бісекцій. Коли ви говорите про "правильну" реалізацію, GSL робить тут "правильну" річ, вона розбиває інтервал з найбільшою на даний момент помилкою, тобто має відслідковувати всі інтервали досі. Якщо ви зберігаєте всі дані в стеку, у вас може залишитися пам'ять стека, це не дуже краще.
Кирило

Відповіді:


3

Погляньте на Odeint . Зараз він є частиною Boost, і він включає алгоритм Bulirsch-Stoer серед інших. Для початку ви можете побачити тут дуже простий приклад.


3
Перше речення огляду для odeint таке: «odeint - це бібліотека для вирішення початкових задач величин (IVP) звичайних диференціальних рівнянь». Наскільки мені відомо, ця бібліотека не може використовуватися для квадратури відомої функції. Чи є у вас приклад, де вона використовувалася для квадратури?
Білл Грін

1
Я думаю (я не використовую бібліотеку), що вона не включає алгоритми для квадратур, таких як квадратура Ньютона-Кота, Ромберга чи Гаусса, але враховуючи, що в цьому питанні згадується метод Грагг-Булірш-Стоера, я вважав, що проблема є була інтеграцією ODE.
Zythos

2

MFEM [1] має прості у використанні функції квадратури (як для поверхневих, так і для об'ємних елементів). Ми змогли використовувати їх для різних завдань.

[1] http://mfem.org/


2

Ви можете легко написати тонку обгортку C ++ навколо функцій квадратури GSL. Наступні потреби C ++ 11.

#include <iostream>
#include <cmath>

#include <functional>
#include <memory>
#include <utility>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_integration.h>

template < typename F >
class gsl_quad
{
  F f;
  int limit;
  std::unique_ptr < gsl_integration_workspace,
                    std::function < void(gsl_integration_workspace*) >
                    > workspace;

  static double gsl_wrapper(double x, void * p)
  {
    gsl_quad * t = reinterpret_cast<gsl_quad*>(p);
    return t->f(x);
  }

public:
  gsl_quad(F f, int limit)
    : f(f)
    , limit(limit)
    , workspace(gsl_integration_workspace_alloc(limit), gsl_integration_workspace_free)
  {}

  double integrate(double min, double max, double epsabs, double epsrel)
  {
    gsl_function gsl_f;
    gsl_f.function = &gsl_wrapper;
    gsl_f.params = this;

    double result, error;
    if ( !std::isinf(min) && !std::isinf(max) )
    {
      gsl_integration_qags ( &gsl_f, min, max,
                             epsabs, epsrel, limit,
                             workspace.get(), &result, &error );
    }
    else if ( std::isinf(min) && !std::isinf(max) )
    {
      gsl_integration_qagil( &gsl_f, max,
                             epsabs, epsrel, limit,
                             workspace.get(), &result, &error );
    }
    else if ( !std::isinf(min) && std::isinf(max) )
    {
      gsl_integration_qagiu( &gsl_f, min,
                             epsabs, epsrel, limit,
                             workspace.get(), &result, &error );
    }
    else
    {
      gsl_integration_qagi ( &gsl_f,
                             epsabs, epsrel, limit,
                             workspace.get(), &result, &error );
    }

    return result;
  }
};

template < typename F >
double quad(F func,
            std::pair<double,double> const& range,
            double epsabs = 1.49e-8, double epsrel = 1.49e-8,
            int limit = 50)
{
  return gsl_quad<F>(func, limit).integrate(range.first, range.second, epsabs, epsrel);
}

int main()
{
  std::cout << "\\int_0^1 x^2 dx = "
            << quad([](double x) { return x*x; }, {0,1}) << '\n'
            << "\\int_1^\\infty x^{-2} dx = "
            << quad([](double x) { return 1/(x*x); }, {1,INFINITY}) << '\n'
            << "\\int_{-\\infty}^\\infty \\exp(-x^2) dx = "
            << quad([](double x) { return std::exp(-x*x); }, {-INFINITY,INFINITY}) << '\n';
}

Вихідні дані

\int_0^1 x^2 dx = 0.333333
\int_1^\infty x^{-2} dx = 1
\int_{-\infty}^\infty \exp(-x^2) dx = 1.77245

1

Я мав успіх у бібліотеці Кубатура (вона написана на С, хоча). Він спрямований на багатовимірну інтеграцію з відносно низькою кількістю вимірів.

Бібліотека HIntLib написана на C ++ і в ній є підпрограми адаптивної квадратури (кубатури).


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.