用于数值积分的C ++库(正交)


10

我有自己的数值积分(正交)子例程,它是Bulirsch&Stoer于1967年出版的ALGOL程序的C ++改编(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带来了好处,并且还使您仅需重写一个函数就可以在不同的正交库之间轻松切换。
Godric Seer 2015年

1
@GodricSeer如果就这么简单,我会做到这一点。但是,事实并非如此。GSL API需要一个预分配的缓冲区,该缓冲区可能未使用任何缓冲区,但可能太小(需要另一个具有更多内存的调用)。适当的实现是递归的,不需要分配,将所有数据保留在堆栈中,并提供干净的API。
Walter

1
@GodricSeer GSL API的另一个严重问题是,它仅接受不带状态的函数(因为它使用了简单的函数指针)。因此,为具有状态的函数生成线程安全API效率很低。
Walter

2
我同意Godric Seer的观点,写包装纸是最好的选择。我认为“ GSL只接受没有状态的函数”是不正确的:在文档中这里说a gsl_function是一个函数指针以及一些不透明的数据指针,可以包含您的状态。其次,关于(重新)分配任意大的工作缓冲区存在一些效率问题,因此该部分至少有一些合理的理由。
Kirill 2015年

1
关于GSL的预分配缓冲区的另一条评论。工作区的大小是根据最大间隔数来定义的-因为如果要使正交例程在自适应二等分过多的情况下仍然失败,则只需将工作区的大小设置为二等分数目的上限即可。当您谈论“适当”的实现时,GSL在这里做“正确”的事情,它将当前间隔最大的错误一分为二,这意味着它必须跟踪到目前为止的所有间隔。如果将所有数据保留在堆栈上,则可能会耗尽堆栈内存,这并不是真的更好。
Kirill

Answers:


3

看看Odeint。它现在是Boost的一部分,其中包括Bulirsch-Stoer算法。首先,您可以在这里看到一个非常简单的示例。


3
odeint概述的第一句话是:“ odeint是用于解决常微分方程的初值问题(IVP)的库”。据我所知,该库不能用于已知函数的正交。您是否有一个用于正交的示例?
比尔·格林

1
我认为(我自己不使用该库)它不包括用于牛顿-科特斯,罗姆贝格或高斯正交的正交算法,但鉴于该问题提到了Gragg-Bulirsch-Stoer方法,我认为当前的问题是ODE集成。
Zythos


2

您可以轻松地围绕GSL正交函数编写一个瘦的C ++包装器。以下需要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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.