Sayısal entegrasyon için C ++ kütüphanesi (kareleme)


10

1967'de Bulirsch & Stoer tarafından yayınlanan bir ALGOL programının C ++ uyarlaması olan sayısal entegrasyon (kareleme) için kendi küçük alt programım var (Numerische Mathematik, 9, 271-278).

Daha modern (uyarlanabilir) bir algoritmaya yükseltmek ve böyle bir (ücretsiz) C ++ kütüphanesi olup olmadığını merak ediyorum. GSL (ki bu C) olarak bir göz vardı, ama (nümerik iyi olabilir) korkunç bir API ile geliyor. Başka bir şey var mı?

Yararlı bir API şöyle görünür:

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
Tıpkı bir yana, hesaplama bilimindeki en iyi uygulamaların çoğunun "kötü" API'lere sahip olduğunu göreceksiniz. Bir sarmalayıcı API'si yazmanızın ve dahili olarak daha az temiz API'yı çağırmanızın kabul edilebilir ve muhtemelen çok yararlı olacağını düşünüyorum. Bu, birincil kodlarınızda güzel bir API avantajı sağlar ve aynı zamanda yalnızca tek bir işlevi yeniden yazarak farklı dörtlü kitaplıklar arasında kolayca geçiş yapmanızı sağlar.
Godric Seer

1
@GodricSeer Bu kadar basit olsaydı, bunu yapardım. Ancak, öyle değil. GSL API, muhtemelen hiçbir şey kullanılmayan, ancak potansiyel olarak çok küçük olabilen (daha fazla belleğe sahip başka bir çağrı gerektirir) önceden ayrılmış bir tampon gerektirir. Düzgün bir uygulama özyinelemeli, tahsisat gerektirmez, tüm verileri yığın üzerinde tutar ve temiz bir API sağlar.
Walter

1
@GodricSeer GSL API ile ilgili bir diğer ciddi sorun da yalnızca durumsuz işlevleri kabul etmesidir (çünkü basit bir işlev işaretçisi kullanır). Bu durumdan gelen işlevler için bir iş parçacığı güvenli API oluşturmak mutlaka verimsizdir.
Walter

2
Godric Seer ile hemfikirim, bir paketleyici yazmak en iyi seçenektir. Ben "GSL sadece devlet olmadan fonksiyonları kabul" doğru olduğunu düşünmüyorum: burada dokümanlar bir gsl_functiona devlet durumunu içeren bazı opak veri işaretçisi ile birlikte bir işlev işaretçisi olduğunu söylüyor . İkincisi, keyfi olarak büyük çalışma arabelleklerinin (yeniden) tahsis edilmesiyle ilgili bazı verimlilik endişeleri vardır, böylece bu bölümün en azından bazı geçerli gerekçeleri vardır.
Kirill

1
GSL'nin önceden ayrılmış tamponu hakkında başka bir yorum. Çalışma alanının boyutu maksimum aralık sayısı olarak tanımlanır - çok fazla uyarlanabilir iki parça alırsa dördün rutininin yine de başarısız olmasını istediğiniz için, çalışma alanının boyutunu, ikiye sayısında bir üst sınıra ayarlayın. "Uygun" bir uygulama hakkında konuştuğunuzda, GSL burada "doğru" şeyi yapar, şu anda en büyük hatayla aralığı ikiye böler, yani şu ana kadar tüm aralıkları takip etmesi gerekir. Yığındaki tüm verileri tutarsanız, yığın bellek yetersiz olabilir, gerçekten daha iyi değildir.
Kirill

Yanıtlar:


3

Odeint'e bir bakın . Artık Boost'un bir parçası ve diğerleri arasında Bulirsch-Stoer algoritmasını içeriyor. Başlamak için burada çok basit bir örnek görebilirsiniz.


3
Odeint'e genel bakışın ilk cümlesi: "odeint, adi diferansiyel denklemlerin başlangıç ​​değer problemlerini (IVP) çözmek için bir kütüphanedir." Bildiğim kadarıyla, bu kütüphane bilinen bir fonksiyonun kareleme için kullanılamaz. Kareleme için kullanıldığı bir örneğiniz var mı?
Bill Greene

1
Sanırım (kütüphaneyi kendim kullanmıyorum) Newton-Cotes, Romberg veya Gauss quadrature gibi kareleme algoritmaları içermiyor, ancak sorunun Gragg-Bulirsch-Stoer yönteminden bahsettiği göz önüne alındığında, problemin elimde olduğunu düşündüm bir ODE entegrasyonuydu.
Zythos

3

GSL kareleme fonksiyonlarının etrafına kolayca ince bir C ++ sarmalayıcısı yazabilirsiniz. Aşağıdakiler C ++ 11 gerektirir.

#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';
}

Çıktı

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

2

MFEM [1] kullanımı kolay kareleme fonksiyonlarına sahiptir (hem yüzeysel hem de hacimsel elemanlar için). Bunları çeşitli görevler için kullanabildik.

[1] http://mfem.org/



Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.