Biblioteca de C ++ para la integración numérica (cuadratura)


10

Tengo mi propia subrutina para la integración numérica (cuadratura), que es una adaptación en C ++ de un programa ALGOL publicado por Bulirsch & Stoer en 1967 (Numerische Mathematik, 9, 271-278).

Me gustaría actualizar a un algoritmo más moderno (adaptativo) y preguntarme si hay bibliotecas C ++ (gratuitas) que lo proporcionen. Tenía un aspecto como GSL (que es C), pero eso viene con una API horrible (aunque los números pueden ser buenos). ¿Hay algo mas?

Una API útil se vería así:

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);

77
Por otro lado, encontrará que muchas de las mejores implementaciones en ciencia computacional tienen API "malas" simplemente porque se han desarrollado durante décadas, en lugar de los meses o años de otro software. Creo que sería aceptable y probablemente muy útil para usted escribir una API de contenedor y llamar internamente a la API menos limpia. Esto le brinda la ventaja de una buena API en sus códigos primarios, y también le permite cambiar fácilmente entre diferentes bibliotecas de cuadratura con solo reescribir una sola función.
Godric Seer

1
@GodricSeer Si fuera así de simple, lo haría. Sin embargo, no lo es. La API GSL requiere un búfer preasignado, del cual posiblemente no se usa nada, pero que puede ser demasiado pequeño (que requiere otra llamada con más memoria). Una implementación adecuada sería recursiva, no necesitaría asignación, mantendría todos los datos en la pila y proporcionaría una API limpia.
Walter

1
@GodricSeer Otro problema grave con la API GSL es que solo acepta funciones sin estado (porque usa un puntero de función simple). Generar una API segura para subprocesos para funciones con estado a partir de esto es necesariamente ineficiente.
Walter

2
Estoy de acuerdo con Godric Seer, escribir un contenedor es la mejor opción. No creo que sea correcto que "GSL solo acepte funciones sin estado": aquí en los documentos dice que gsl_functiones un puntero de función junto con algún puntero de datos opaco, que puede contener su estado. En segundo lugar, existen algunas preocupaciones de eficiencia sobre la (re) asignación de amortiguadores de trabajo arbitrariamente grandes, por lo que esa parte tiene al menos alguna justificación válida.
Kirill

1
Otro comentario sobre el búfer preasignado de GSL. El tamaño del espacio de trabajo se define en términos del número máximo de intervalos, ya que desea que la rutina de cuadratura falle de todos modos si toma demasiadas bisecciones adaptativas, simplemente configure el tamaño del espacio de trabajo en algún límite superior en el número de bisecciones. Cuando habla de una implementación "adecuada", GSL hace lo "correcto" aquí, divide el intervalo con el error más grande en la actualidad, lo que significa que tiene que hacer un seguimiento de todos los intervalos hasta ahora. Si mantiene todos los datos en la pila, podría quedarse sin memoria de pila, no es realmente mejor.
Kirill

Respuestas:


3

Echa un vistazo a Odeint . Ahora es parte de Boost e incluye el algoritmo Bulirsch-Stoer entre otros. Para comenzar puedes ver aquí un ejemplo muy simple.


3
La primera oración de la descripción general de odeint es: "odeint es una biblioteca para resolver problemas de valores iniciales (PIV) de ecuaciones diferenciales ordinarias". Que yo sepa, esta biblioteca no se puede usar para la cuadratura de una función conocida. ¿Tiene un ejemplo donde se ha utilizado para la cuadratura?
Bill Greene el

1
Creo (no uso la biblioteca yo mismo) que no incluye algoritmos para cuadraturas como en Newton-Cotes, Romberg o la cuadratura gaussiana, pero dado que la pregunta menciona el método Gragg-Bulirsch-Stoer, pensé que el problema en cuestión fue una integración ODE.
Zythos

2

MFEM [1] tiene funciones de cuadratura fáciles de usar (tanto para elementos superficiales como volumétricos). Pudimos usarlos para diversas tareas.

[1] http://mfem.org/


2

Puede escribir fácilmente un envoltorio delgado de C ++ alrededor de las funciones de cuadratura GSL. Lo siguiente necesita 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';
}

Salida

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


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.