Pustaka C ++ untuk intergrasi numerik (quadrature)


10

Saya memiliki subrutin kecil saya sendiri untuk integrasi numerik (quadrature), yang merupakan adaptasi C ++ dari program ALGOL yang diterbitkan oleh Bulirsch & Stoer pada tahun 1967 (Numerische Mathematik, 9, 271-278).

Saya ingin meningkatkan ke algoritma yang lebih modern (adaptif) dan bertanya-tanya apakah ada perpustakaan C ++ (gratis) yang menyediakannya. Saya telah melihat sebagai GSL (yang merupakan C), tetapi itu datang dengan API yang mengerikan (meskipun angka mungkin baik). Apakah ada hal lain?

API yang bermanfaat akan terlihat seperti ini:

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
Selain itu, Anda akan menemukan bahwa banyak implementasi terbaik dalam ilmu komputasi memiliki API "buruk" hanya karena mereka telah dikembangkan selama beberapa dekade, bukan berbulan-bulan atau bertahun-tahun dari perangkat lunak lain. Saya pikir itu akan dapat diterima, dan sepertinya sangat berguna bagi Anda untuk menulis wrapper API, dan secara internal memanggil API yang kurang bersih. Ini memberi Anda keuntungan dari API yang bagus dalam kode primer Anda, dan juga memungkinkan Anda untuk dengan mudah beralih di antara pustaka quadrature yang berbeda dengan hanya menulis ulang fungsi tunggal.
Godric Seer

1
@ GodricSeer Jika sesederhana itu, saya akan melakukannya. Namun, tidak. API GSL memerlukan buffer yang dialokasikan sebelumnya, yang kemungkinan tidak ada yang digunakan, tetapi yang berpotensi terlalu kecil (membutuhkan panggilan lain dengan lebih banyak memori). Implementasi yang tepat akan bersifat rekursif, tidak perlu alokasi, menyimpan semua data di stack, dan menyediakan API yang bersih.
Walter

1
@GodricSeer Masalah serius lainnya dengan GSL API adalah ia hanya menerima fungsi tanpa status (karena menggunakan penunjuk fungsi sederhana). Membuat API threadsafe untuk fungsi dengan status dari ini tentu tidak efisien.
Walter

2
Saya setuju dengan Godric Seer, menulis pembungkus adalah pilihan terbaik. Saya tidak berpikir itu benar bahwa "GSL hanya menerima fungsi tanpa status": di sini di dokumen dikatakan a gsl_functionadalah fungsi pointer bersama dengan beberapa data pointer buram, yang dapat berisi keadaan Anda. Kedua, ada beberapa kekhawatiran efisiensi tentang (re-) mengalokasikan buffer kerja sewenang-wenang besar, sehingga bagian memiliki setidaknya beberapa pembenaran yang valid untuk itu.
Kirill

1
Komentar lain tentang buffer pra-alokasi GSL. Ukuran ruang kerja didefinisikan dalam hal jumlah interval maksimum - karena Anda ingin agar rutin quadrature gagal jika terlalu banyak membagi dua adaptif, cukup atur ukuran ruang kerja ke beberapa batas atas pada jumlah pembagian. Ketika Anda berbicara tentang implementasi yang "benar", GSL melakukan hal yang "benar" di sini, itu membagi dua interval dengan kesalahan terbesar saat ini, yang berarti harus melacak semua interval sejauh ini. Jika Anda menyimpan semua data di stack, Anda mungkin kehabisan memori stack, itu tidak benar-benar lebih baik.
Kirill

Jawaban:


3

Lihatlah Odeint . Sekarang bagian dari Boost dan termasuk algoritma Bulirsch-Stoer. Untuk memulai, Anda dapat melihat di sini contoh yang sangat sederhana.


3
Kalimat pertama ikhtisar untuk odeint adalah: "odeint adalah perpustakaan untuk menyelesaikan masalah nilai awal (IVP) dari persamaan diferensial biasa." Sejauh yang saya ketahui, perpustakaan ini tidak dapat digunakan untuk quadrature dari fungsi yang diketahui. Apakah Anda memiliki contoh di mana telah digunakan untuk quadrature?
Bill Greene

1
Saya pikir (saya tidak menggunakan perpustakaan sendiri) bahwa itu tidak termasuk algoritma untuk quadratures seperti di Newton-Cotes, Romberg atau Gaussian quadrature tetapi mengingat bahwa pertanyaan menyebutkan metode Gragg-Bulirsch-Stoer saya pikir itu masalah yang dihadapi adalah integrasi ODE.
Zythos

2

MFEM [1] memiliki fungsi quadrature yang mudah digunakan (baik untuk elemen permukaan dan volumetrik). Kami dapat menggunakannya untuk berbagai tugas.

[1] http://mfem.org/


2

Anda dapat dengan mudah menulis bungkus C ++ tipis di sekitar fungsi quadrature GSL. Kebutuhan berikut 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';
}

Keluaran

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


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.