Biblioteca C ++ para integração numérica (quadratura)

10

Eu tenho minha própria pequena sub-rotina para integração numérica (quadratura), que é uma adaptação em C ++ de um programa ALGOL publicado por Bulirsch & Stoer em 1967 (Numerische Mathematik, 9, 271-278).

Gostaria de atualizar para um algoritmo (adaptável) mais moderno e me pergunto se existem bibliotecas C ++ (gratuitas) que as fornecem. Eu olhei como GSL (que é C), mas isso vem com uma API horrível (embora os números possam ser bons). Mais alguma coisa?

Uma API útil seria assim:

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);
Walter
fonte
7
Apenas como um aparte, você encontrará que muitas das melhores implementações em ciência da computação têm APIs "ruins" simplesmente porque foram desenvolvidas ao longo de décadas, em vez de meses ou anos de outros softwares. Eu acho que seria aceitável e provavelmente muito útil para você escrever uma API de wrapper e chamar internamente a API menos limpa. Isso oferece a vantagem de uma boa API em seus códigos principais e também permite alternar facilmente entre diferentes bibliotecas de quadratura, reescrevendo apenas uma única função.
21815 Godric Seer
11
@GodricSeer Se fosse assim tão simples, eu faria isso. No entanto, não é. A API GSL requer um buffer pré-alocado, do qual possivelmente nada é usado, mas que pode ser muito pequeno (exigindo outra chamada com mais memória). Uma implementação adequada seria recursiva, não precisaria de alocação, manteria todos os dados na pilha e forneceria uma API limpa.
Walter Walter
11
@GodricSeer Outro problema sério com a API GSL é que ela só aceita funções sem estado (porque usa um ponteiro de função simples). Gerar uma API thread-safe para funções com state a partir disso é necessariamente ineficiente.
Walter Walter
2
Concordo com Godric Seer, escrever um invólucro é a melhor opção. Não acho correto que "o GSL aceite apenas funções sem estado": aqui nos documentos, ele diz que a gsl_functioné um ponteiro de função junto com algum ponteiro de dados opaco, que pode conter seu estado. Segundo, existem algumas preocupações de eficiência quanto à (re) alocação de buffers de trabalho arbitrariamente grandes, para que parte tenha pelo menos alguma justificativa válida.
Kirill
11
Outro comentário sobre o buffer pré-alocado da GSL. O tamanho da área de trabalho é definido em termos de número máximo de intervalos - já que você deseja que a rotina em quadratura falhe de qualquer maneira, se houver muitas seções transversais adaptativas, basta definir o tamanho da área de trabalho para um limite superior do número de seções. Quando você fala sobre uma implementação "adequada", a GSL faz a coisa "certa" aqui, divide o intervalo com o maior erro atualmente, o que significa que ele deve acompanhar todos os intervalos até o momento. Se você mantiver todos os dados na pilha, pode ficar sem memória da pilha, não é realmente melhor.
21415 Kirill

Respostas:

3

Dê uma olhada no Odeint . Agora faz parte do Boost e inclui o algoritmo Bulirsch-Stoer, entre outros. Para começar, você pode ver aqui um exemplo muito simples.

Zythos
fonte
3
A primeira frase da visão geral do odeint é: "odeint é uma biblioteca para resolver problemas de valor inicial (PIV) de equações diferenciais ordinárias". Tanto quanto sei, esta biblioteca não pode ser usada para quadratura de uma função conhecida. Você tem um exemplo em que foi usado para quadratura?
Bill Greene
11
Penso (eu não uso a biblioteca) que ela não inclui algoritmos para quadraturas como na quadratura de Newton-Cotes, Romberg ou Gaussiana, mas, como a pergunta mencionou o método de Gragg-Bulirsch-Stoer, pensei que o problema estava em questão foi uma integração ODE.
Zythos
2

O MFEM [1] possui funções de quadratura fáceis de usar (para elementos surfacicos e volumétricos). Conseguimos usá-los para várias tarefas.

[1] http://mfem.org/

BrunoLevy
fonte
2

É possível escrever facilmente um invólucro fino em C ++ nas funções de quadratura GSL. O seguinte precisa do 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';
}

Resultado

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

Eu tive sucesso com a biblioteca Cubature (ela está escrita em C). Destina-se à integração multidimensional com um número relativamente baixo de dimensões.

A biblioteca HIntLib é escrita em C ++ e possui rotinas para quadratura adaptativa (cubatura).

Juan M. Bello-Rivas
fonte