简体   繁体   English

我应该如何对执行数组数值插值的类进行单元测试?

[英]How should I unit-test a class which performs numeric interpolation of arrays?

I work with a project where some classes perform numerical interpolation, that is, given a set of points at known locations, I can ask for the position of points between the nodes of the grid, so to say.我在一个项目中工作,其中一些类执行数值插值,也就是说,给定一组已知位置的点,我可以要求网格节点之间的点的位置,可以这么说。

Since these methods do not return exact values by definition, I wonder how am I supposed to unit-test them.由于这些方法根据定义不返回精确值,我想知道我应该如何对它们进行单元测试。

For example, the code below tests that the interpolant returns an array of zeros if I give a similar array of zeros to it, and it works, but I suspect it's working because I am not actually resampling here, just asking the same positions again.例如,下面的代码测试如果我给它一个类似的零数组,则插值器返回一个零数组,并且它有效,但我怀疑它有效,因为我实际上并没有在这里重新采样,只是再次询问相同的位置。

[TestMethod]
public void Interpolate_ZeroIn_ZeroOut()
{
    var Xvalues = new double[] {1,2,3,4,5,6,7,8,9,10};
    var Yvalues = new double[] {0,0,0,0,0,0,0,0,0,0};

    List<Point> points = 
        Enumerable.Zip(Xvalues, Yvalues, (x,y) => new Point(x,y)).ToList();

    int interpolation_order = 5;

    FourierIterpolator target = new FourierInterpolator(points, interpolation_order);

    var output = Xvalues.Select(p => target.Interpolate(p)).ToList();

    CollectionAssert.AreEqual(output, Yvalues);
}

Problem is: with actual resampling, the new points will be "between" input points, so I cannot use CollectionAssert.AreEqual .问题是:通过实际重采样,新点将在输入点“之间”,所以我不能使用CollectionAssert.AreEqual Also, with some interpolation methods with smoothing, the arrays will not be equal, only approximate.此外,使用一些带平滑的插值方法,数组将不相等,只有近似值。

So, my question is:所以,我的问题是:

What are recommended assertions to use when testing numeric methods involving aproximation / interpolation?在测试涉及近似/插值的数值方法时推荐使用哪些断言?

Is your interpolation technique deterministic ?您的插值技术是确定性的吗? ( Deterministic means: If your method is called twice with the same parameters , will it return the exact same result in both cases?) 确定性意味着:如果您的方法使用相同的参数被调用两次,它会在两种情况下返回完全相同的结果吗?)

If yes,如果是,

  • run a few values through your function,通过您的函数运行一些值,
  • use an external method to verify that the result is correct (pen and paper, or some math tool),使用外部方法来验证结果是否正确(笔和纸,或一些数学工具),
  • add those values as test cases.添加这些值作为测试用例。

This will ensure that any breaking changes to your method are detected by the test cases.这将确保测试用例检测到您的方法的任何重大更改 One drawback is that your cases will fail if you improve your interpolation technique, yielding different results.一个缺点是,如果您改进插值技术,您的案例将失败,从而产生不同的结果。 This is not necessarily a bad thing, since it alerts you that your method now returns different results.这不一定是坏事,因为它会提醒您您的方法现在返回不同的结果。

If your interpolation technique is not deterministic, ie, if it uses some source of randomness, you might be able to assert that the values are within some reasonable error margin.如果您的插值技术不是确定性的,即,如果它使用一些随机源,您可能能够断言这些值在一些合理的误差范围内。

Currently its hard to see what is input data for your test.目前很难看到测试的输入数据是什么。 I would recommend you to make tests data explicit and easy to see.我建议您使测试数据明确且易于查看。 Also I don't think you need 10 or 100 points to verify whether your method works.此外,我认为您不需要 10 或 100 分来验证您的方法是否有效。 Probably 2-3 points would be enough:大概 2-3 点就足够了:

var points = new []{ new Point(0,0), new Point(1,0), new Point(2,0) });

Next you should provide expected values:接下来,您应该提供预期值:

double[] expected = { 0.33, 0.66, 1.66 };

And last - check if actual values are equal or near expected values:最后 - 检查实际值是否等于或接近预期值:

[TestMethod]
public void Interpolar_ZeroIn_ZeroOut()
{
    var points = new []{ new Point(0,0), new Point(1,0), new Point(2,0) });
    // calculate expected values manually
    double[] expected = { 0.33, 0.66, 1.66 };    
    int ordem = 5;

    var interpolator = new InterpoladorFourier(points, ordem);

    for(int i = 0; i < points.Length; i++)        
       Assert.AreEqual(expected[i], interpolator.Interpolar(points[i].X));
}

If expected data are not accurate then you can provide delta for assertion:如果预期数据不准确,那么您可以为断言提供增量:

Assert.AreEqual(expected[i], interpolator.Interpolar(points[i].X), 0.01);

Or even better - you can create method which creates points in very readable way:或者甚至更好 - 您可以创建以非常易读的方式创建点的方法:

var points = CreatePoints("0,0", "1,0", "2,0"); 

With this helper method:使用此辅助方法:

private Point[] CreatePoints(params string[] points)
{
    List<Point> result = new List<Point>();

    foreach(var s in points)
    {
        var parts = s.Split(',');
        var x = Double.Parse(parts[0]);
        var y = Double.Parse(parts[1]);            
        var point = new Point(x,y);
        result.Add(point);
    }

    return result.ToArray();
}

I came here via google because I was looking for some answers myself, but my current approach might be better than those provided here, so I will share for future reference.我通过谷歌来到这里是因为我自己也在寻找一些答案,但我目前的方法可能比这里提供的方法更好,所以我会分享以供将来参考。

Most (if not all) interpolation methods have a decent number of functions that they interpolate perfectly or at least up to machine precision.大多数(如果不是全部)插值方法都有相当数量的函数,它们可以完美地或至少达到机器精度。 For example a cubic spline it's polynomials up do degree 3. I just test for a reasonable number of those polynomials with varying coefficients, varying intervals, varying number of points... Calculate the RMS error and "assert" if it's close to machine precision (say rms_err < 10*eps).例如三次样条,它的多项式达到 3 次。我只是测试合理数量的具有不同系数、不同间隔、不同点数的多项式......如果它接近机器精度,则计算 RMS 误差并“断言” (比如 rms_err < 10*eps)。 Choosing exactly which polynomials/functions/parameters are suitable here requires a bit of knowledge about the specific interpolation method and maybe floating point errors, but it's doable.准确选择哪些多项式/函数/参数适合此处需要一些关于特定插值方法的知识,可能还有浮点错误,但这是可行的。

Another way is just to compare with available libraries with the same or similar implementation, either by just exporting values of just using the library directly.另一种方法是与具有相同或相似实现的可用库进行比较,或者直接导出仅使用库的值。 For example I tested my 2D interpolation implementation with available 1D implementations which use the same method, along one dimension at a time of course.例如,我使用可用的 1D 实现测试了我的 2D 插值实现,这些实现使用相同的方法,当然沿着一个维度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM