将振荡(插值)函数与GSL和python集成

时间:2015-01-24 23:11:04

标签: python integration interpolation numerical-methods gsl

我正在尝试将高振荡数据与来自python中的GSL Scientific Librarypygsl的函数 qawo 集成。因为我正在处理数据我虽然插值函数可以工作,但GSL给我一个不正确的结果 !! 。让我以函数 Sin(x)/(1 +x²)为例进行解释。

以下代码可以正常使用:

import pygsl
from  pygsl import integrate

def f1(x,y):
    return 1./(1 + x**2)
sys = integrate.gsl_function(f1, None)

w = integrate.workspace(100)
cyclew = integrate.workspace(1000000)

table = integrate.qawo_table(1, 10000, integrate.SINE, 100)
flag, result, error = integrate.qawo(sys, 0, 1e-8, 1e-8, 100, w, table)

应该给出 0.626761 。但是,如果我们使用上述函数模拟数据点......

xarr = np.linspace(0,1e15,1e30)  
yarr = np.sin(xarr)/(1.+xarr**2)

interp = interpol.interp1d(xarr,yarr)

def fgsl(x,y):
    return interp(x)

syst = integrate.gsl_function(fgsl, None)

w = integrate.workspace(1000)
cyclew = integrate.workspace(100000000)

table = integrate.qawo_table(1, 1e10, integrate.SINE, 100)

flag, result, error = integrate.qawo(syst, 0, 1e-15, 1e-15, 100, w, table)

给出了完全错误的结果: 4.2426e-21

此外,如果我们将 yarr simps 功能集成在一起:

import scipy.integrate as ints
res = ints.simps(yarr,xarr)

给出了一个非常好的近似值: 0.64676099

假设我不能使用辛普森的规则。有谁知道如何使用gsl的插值函数?或者我如何更改代码以进行集成?

提前致谢。

1 个答案:

答案 0 :(得分:3)

您的示例中的数字不太合理,它们将破坏任何自适应方案。让我解释一下原因。

您正在尝试将振荡函数与0到10 ^ 10的周期2 * Pi进行整合!没有自适应方案能够“看到”该区间的振荡行为,它们会收敛到错误的结果(错误的收敛)!请记住,自适应方案使用自上而下的方法。它们对整个区间应用一些规则,然后将该区间划分为两个,并在每个细分中应用相同的规则。在几个周期(通常是4或5)之后,该方案通过比较连续步骤中的部分结果来开始检查收敛。在您的示例中,该方案将需要大量细分以最终查看振荡行为,这是可能发生错误收敛的典型情况!

如何在开放区间(a,\infinity)上集成振荡函数? qawf integral整合方案的解释非常完整。将函数集成在仅包含少量振荡的子区间上,并检查结果如何收敛 - 然后推断出来!

还有其他数字不太合理。为什么你需要在每dx = 1e-15时对sin(x)/(1 + x ^ 2)进行采样?任何合理的自适应方案都可以将sin(x)从0到2Pi w / ~10-20个采样点进行积分。

Simpson的规则没有失败,因为它不是一个自适应方案。 python代码将根据您提供的x-array确定'dx',它将使用该dx一直到1e10!但是我很确定您的代码中的舍入错误非常糟糕,因为您选择了dx~1e-15。

编辑1第一部分:实际上问题不仅仅是由被积函数的振荡行为引起的。假设包络1 / x ^ 2收敛得非常快 - 如果x>> 1,则函数几乎为零。因此,因为您正在将这个包络集成在巨大的区间[0,1e10]中,所以自适应积分认为结果非常小,因为它无法看到函数不可忽略的小(子)区间。 (你可能认为积分例程会在闭区间[0,1e10]中统一分布评估点 - 对高斯积分来说并不完全正确,但它很接近 - 所以其中一个点落入区间内的可能性很小〜[0,1e3]其中被积函数不可忽略的非常小。经过几次迭代后,积分程序将得到积分接近于零。

编辑1第二部分:我仍然认为(在阅读你的评论之后)问题是你插入的数字(或者python包装器有一个错误。)。请像我在下面的C ++代码中那样尝试使用合理数字的示例

int main()  
{           
  const double omega = 1;
  auto g = [](double x)->double {return 1.0/(1.+x*x);};
  auto f = [&](double x)->double {return std::sin( omega * x) * g(x);};

  const int points_per_cycle  = 20;
  const int n_cycles = 10;
  const int size = n_cycles * points_per_cycle + 1;
  const double xmin = 0.0;
  const double xmax = n_cycles * (2 * M_PI);
  const double L = xmax-xmin;

  std::vector<double> x(size);
  std::vector<double> y(size);

  for (int i = 0; i <size; ++i) {
    x[i] = i * L/(size-1);
    y[i] = f(x[i]);
  }

  std::cout.precision(8); 
  // interpolation
  InterpolationGSL<std::vector<double>> GSLinterpol(x, y, GSLIT::cspline, false);
  // Integral of the interpolation
  std::cout << GSLinterpol.If((1+1e-12)*xmin, (1-1e-12)*xmax) << std::endl;

  // SECOND GSL INTEGRATION
  gsl_integration_workspace* w = gsl_integration_workspace_alloc (1000);

  gsl_integration_qawo_table* wf = gsl_integration_qawo_table_alloc 
    (omega, L, GSL_INTEG_SINE, 1000);

  int status = gsl_integration_qawo_table_set (wf, omega, L, GSL_INTEG_SINE);
  if(status) std::cerr<< "error: " << std::string(gsl_strerror (status)) << std::endl;

  double result;
  double abserr;

  std::function<double(double)> gg( std::cref(g) );
  GslFunction Fp(gg); 
  gsl_function *Fgsl = static_cast<gsl_function*>(&Fp);

  status = gsl_integration_qawo (Fgsl, xmin, 0.0, 1e-5, 1000, w, wf, &result, &abserr);

  if(status) std::cerr<< "error: " << std::string(gsl_strerror (status)) << std::endl;
  std::cout << result << std::endl;
  return 0;
}

此代码使用我的gsl_functioninterpolation包装器 - 因此您可能会发现代码有点奇怪 - 但重要的是它评估您在合理的时间间隔内提到的相同积分和结果是

Interpolation integral: 0.64631754
GSL integral: 0.64650827