수치 적 통합 (구적)을위한 C ++ 라이브러리


10

나는 1967 년 Bulirsch & Stoer (Numerische Mathematik, 9, 271-278)가 발표 한 ALGOL 프로그램의 C ++ 적응 인 수치 적분 (quadrature)을위한 작은 서브 루틴을 가지고 있습니다.

더 현대적인 (적응) 알고리즘으로 업그레이드하고 그러한 (무료) 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의 또 다른 심각한 문제는 간단한 함수 포인터를 사용하기 때문에 상태가없는 함수 만 허용한다는 것입니다. 이 상태의 함수에 스레드 세이프 API를 생성하는 것은 반드시 비효율적입니다.
Walter

2
Godric Seer에 동의합니다. 래퍼를 작성하는 것이 가장 좋습니다. "GSL은 상태가없는 함수 만 받아 들인다"는 것이 옳지 않다고 생각합니다. 여기 문서에서 a gsl_function는 상태를 포함 할 수있는 불투명 한 데이터 포인터와 함께 함수 포인터 라고 말합니다 . 둘째, 임의의 큰 작업 버퍼를 (재) 할당하는 것에 대한 효율성 문제가 있기 때문에 부분에 대한 정당한 정당성이 있습니다.
Kirill

1
GSL의 사전 할당 버퍼에 대한 또 다른 의견. 작업 영역의 크기는 최대 간격 수로 정의됩니다. 적응 형 이분법이 너무 많은 경우 구적법 루틴이 실패하기를 원하므로 작업 영역의 크기를이 분식 수의 상한으로 설정하십시오. "적절한"구현에 대해 이야기 할 때 GSL은 여기서 "올바른"작업을 수행하므로 현재 가장 큰 오류와 간격을 이등분하므로 모든 간격을 지금까지 추적해야합니다. 모든 데이터를 스택에 보관하면 스택 메모리가 부족할 수 있지만 실제로는 좋지 않습니다.
키릴

답변:


3

Odeint를 살펴 보십시오 . 현재 Boost의 일부이며 Bulirsch-Stoer 알고리즘이 포함되어 있습니다. 시작하려면 여기 에서 매우 간단한 예를 볼 수 있습니다 .


3
odeint에 대한 개요의 첫 문장은 "odeint는 일반 미분 방정식의 초기 값 문제 (IVP)를 해결하기위한 라이브러리입니다." 내가 아는 한,이 라이브러리는 알려진 함수의 직교에 사용할 수 없습니다. 구적법에 사용 된 예가 있습니까?
Bill Greene

1
Newton-Cotes, Romberg 또는 Gaussian quadrature와 같은 구적법에 대한 알고리즘은 포함하지 않는다고 생각하지만 (그 자체로 라이브러리를 사용하지는 않습니다. ODE 통합이었습니다.
Zythos

2

MFEM [1]은 사용하기 쉬운 직교 함수 (표면 및 체적 요소 모두)를 가지고 있습니다. 다양한 작업에 사용할 수있었습니다.

[1] http://mfem.org/


2

GSL quadrature 함수 주위에 얇은 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.