Libreria C ++ per intergrazione numerica (quadratura)


10

Ho la mia piccola subroutine per l'integrazione numerica (quadratura), che è un adattamento C ++ di un programma ALGOL pubblicato da Bulirsch & Stoer nel 1967 (Numerische Mathematik, 9, 271-278).

Vorrei passare a un algoritmo più moderno (adattivo) e chiedermi se ci sono librerie C ++ (gratuite) che forniscono tale. Ho dato uno sguardo a GSL (che è C), ma viene fornito con un'API orribile (anche se i numeri possono essere buoni). C'è niente altro?

Un'API utile sarebbe simile a questa:

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
A parte questo, scoprirai che molte delle migliori implementazioni nella scienza computazionale hanno API "cattive" semplicemente perché sono state sviluppate nel corso di decenni, piuttosto che mesi o anni di altri software. Penso che sarebbe accettabile e probabilmente molto utile per te scrivere un'API wrapper e chiamare internamente l'API meno pulita. Questo ti dà il vantaggio di una bella API nei tuoi codici primari e ti consente anche di passare facilmente tra diverse librerie di quadrature con la sola riscrittura di una singola funzione.
Godric Seer,

1
@GodricSeer Se fosse così semplice, lo farei. Tuttavia, non lo è. L'API GSL richiede un buffer pre-allocato, di cui probabilmente non viene utilizzato nulla, ma che potenzialmente potrebbe essere troppo piccolo (che richiede un'altra chiamata con più memoria). Un'implementazione corretta sarebbe ricorsiva, non necessiterebbe di allocazione, manterrebbe tutti i dati nello stack e fornire un'API pulita.
Walter,

1
@GodricSeer Un altro grave problema con l'API GSL è che accetta solo funzioni senza stato (perché utilizza un semplice puntatore a funzione). Generare un'API thread-safe per funzioni con stato da questo è necessariamente inefficiente.
Walter,

2
Concordo con Godric Seer, scrivere un wrapper è l'opzione migliore. Non credo sia corretto che "GSL accetti solo funzioni senza stato": qui nei documenti si dice che gsl_functionè un puntatore a funzione insieme ad un puntatore di dati opaco, che può contenere il tuo stato. In secondo luogo, vi sono alcuni problemi di efficienza riguardo alla (ri) allocazione di buffer di lavoro arbitrariamente grandi, in modo che tale parte abbia almeno una valida giustificazione.
Kirill,

1
Un altro commento sul buffer pre-allocato di GSL. La dimensione dell'area di lavoro è definita in termini di numero massimo di intervalli: poiché si desidera che la routine di quadratura fallisca comunque se sono necessarie troppe sezioni adattive, impostare la dimensione dell'area di lavoro su un limite superiore del numero di sezioni. Quando parli di un'implementazione "corretta", GSL fa la cosa "giusta" qui, seziona l'intervallo con l'errore attualmente più grande, il che significa che deve tenere traccia di tutti gli intervalli finora. Se mantieni tutti i dati nello stack, potresti esaurire la memoria dello stack, non è molto meglio.
Kirill,

Risposte:


3

Dai un'occhiata a Odeint . Ora fa parte di Boost e include l'algoritmo Bulirsch-Stoer tra gli altri. Per iniziare puoi vedere qui un esempio molto semplice.


3
La prima frase della panoramica di odeint è: "odeint è una libreria per risolvere problemi di valore iniziale (IVP) di equazioni differenziali ordinarie". Per quanto ne so, questa libreria non può essere utilizzata per la quadratura di una funzione nota. Hai un esempio di dove è stato usato per la quadratura?
Bill Greene,

1
Penso (non uso la libreria da solo) che non includa algoritmi per quadrature come in Newton-Cotes, Romberg o quadratura gaussiana, ma dato che la domanda menzionava il metodo Gragg-Bulirsch-Stoer pensavo che il problema a portata di mano era un'integrazione ODE.
Zythos

2

MFEM [1] ha funzioni di quadratura di facile utilizzo (sia per elementi surfacici che volumetrici). Siamo stati in grado di usarli per vari compiti.

[1] http://mfem.org/


2

È possibile scrivere facilmente un sottile wrapper C ++ attorno alle funzioni di quadratura GSL. Di seguito è necessario 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';
}

Produzione

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


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.