数値積分用の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

1
@GodricSeerそれがそんなに簡単だったら、私はそうするでしょう。ただし、そうではありません。GSL APIには事前に割り当てられたバッファーが必要ですが、そのバッファーは何も使用されていない可能性がありますが、小さすぎる可能性があります(より多くのメモリを使用して別の呼び出しが必要)。適切な実装は再帰的であり、割り当てを必要とせず、すべてのデータをスタックに保持し、クリーンなAPIを提供します。
Walter

1
@GodricSeer GSL APIのもう1つの深刻な問題は、状態を持たない関数しか受け入れないことです(単純な関数ポインターを使用するため)。これから状態を持つ関数のスレッドセーフAPIを生成することは、必ずしも非効率的です。
Walter

2
Godric Seerに同意します。ラッパーを作成するのが最善の方法です。「GSLは状態のない関数のみを受け入れる」というのは正しくないと思います。ドキュメントでは、a gsl_functionは関数ポインタであり、不透明なデータポインタであり、状態を含むことができると述べています。第2に、任意の大きさの作業バッファーを(再)割り当てることには、いくつかの効率の問題があります。そのため、その部分には、少なくともある程度の正当な理由があります。
Kirill、2015

1
GSLの事前割り当てバッファに関する別のコメント。ワークスペースのサイズは、間隔の最大数で定義されます。アダプティブバイセクションが多すぎる場合でも、求積法ルーチンを失敗させるため、ワークスペースのサイズをバイセクションの数の上限に設定します。「適切な」実装について話すと、GSLはここで「正しい」ことを行います。これは、現在最大のエラーがある間隔を二分するため、これまでのすべての間隔を追跡する必要があります。すべてのデータをスタックに保持すると、スタックメモリが不足する可能性があります。
Kirill

回答:


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


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.