Bibliothèque C ++ pour l'intégration numérique (quadrature)


10

J'ai mon propre petit sous-programme d'intégration numérique (quadrature), qui est une adaptation en C ++ d'un programme ALGOL publié par Bulirsch & Stoer en 1967 (Numerische Mathematik, 9, 271-278).

Je voudrais passer à un algorithme plus moderne (adaptatif) et me demander s'il existe des bibliothèques C ++ (gratuites) qui en fournissent. J'avais un look GSL (qui est C), mais cela vient avec une horrible API (bien que les chiffres soient bons). Y a-t-il autre chose?

Une API utile ressemblerait à ceci:

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
Soit dit en passant, vous constaterez que bon nombre des meilleures implémentations en science informatique ont de «mauvaises» API simplement parce qu'elles ont été développées au fil des décennies, plutôt que des mois ou des années d'autres logiciels. Je pense qu'il serait acceptable et probablement très utile pour vous d'écrire une API wrapper et d'appeler en interne l'API moins propre. Cela vous donne l'avantage d'une belle API dans vos codes primaires et vous permet également de basculer facilement entre différentes bibliothèques en quadrature avec une seule réécriture d'une seule fonction.
Godric Seer

1
@GodricSeer Si c'était aussi simple que ça, je le ferais. Mais ce n'est pas le cas. L'API GSL nécessite un tampon pré-alloué, dont peut-être rien n'est utilisé, mais qui peut potentiellement être trop petit (nécessitant un autre appel avec plus de mémoire). Une implémentation appropriée serait récursive, n'aurait pas besoin d'allocation, conserverait toutes les données sur la pile et fournirait une API propre.
Walter

1
@GodricSeer Un autre problème sérieux avec l'API GSL est qu'elle n'accepte que les fonctions sans état (car elle utilise un simple pointeur de fonction). La génération d'une API threadsafe pour les fonctions dont l'état est nécessairement inefficace.
Walter

2
Je suis d'accord avec Godric Seer, l'écriture d'un wrapper est la meilleure option. Je ne pense pas qu'il soit correct que "GSL n'accepte que les fonctions sans état": ici, dans la documentation, il est dit qu'un gsl_functionest un pointeur de fonction avec un pointeur de données opaque, qui peut contenir votre état. Deuxièmement, il y a des problèmes d'efficacité concernant la (ré) allocation de tampons de travail de taille arbitraire, de sorte qu'une partie a au moins une justification valable.
Kirill

1
Un autre commentaire sur le tampon pré-alloué de GSL. La taille de l'espace de travail est définie en termes de nombre maximal d'intervalles - comme vous voulez que la routine en quadrature échoue de toute façon si elle prend trop de bissections adaptatives, définissez simplement la taille de l'espace de travail à une limite supérieure du nombre de bissections. Lorsque vous parlez d'une implémentation "correcte", GSL fait ce qu'il faut "ici", il coupe l'intervalle avec l'erreur la plus importante actuellement, ce qui signifie qu'il doit garder une trace de tous les intervalles jusqu'à présent. Si vous conservez toutes les données sur la pile, vous risquez de manquer de mémoire de pile, ce n'est pas vraiment mieux.
Kirill

Réponses:


3

Jetez un oeil à Odeint . Il fait maintenant partie de Boost et inclut entre autres l'algorithme Bulirsch-Stoer. Pour commencer, vous pouvez voir ici un exemple très simple.


3
La première phrase de l'aperçu pour odeint est: "odeint est une bibliothèque pour résoudre les problèmes de valeur initiale (IVP) des équations différentielles ordinaires." Pour autant que je sache, cette bibliothèque ne peut pas être utilisée pour la quadrature d'une fonction connue. Avez-vous un exemple où il a été utilisé en quadrature?
Bill Greene

1
Je pense (je n'utilise pas la bibliothèque moi-même) qu'elle n'inclut pas d'algorithmes pour les quadratures comme dans Newton-Cotes, Romberg ou la quadrature gaussienne mais étant donné que la question mentionnait la méthode Gragg-Bulirsch-Stoer, je pensais que le problème était à portée de main était une intégration ODE.
Zythos

2

MFEM [1] possède des fonctions de quadrature faciles à utiliser (pour les éléments surfaciques et volumétriques). Nous avons pu les utiliser pour diverses tâches.

[1] http://mfem.org/


2

Vous pouvez facilement écrire un wrapper C ++ fin autour des fonctions de quadrature GSL. Les éléments suivants ont besoin de 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';
}

Production

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


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.