Inspired by @Bob_Carpenter I started using expect_near_rel
instead of various manual solutions to test “approximate equivalence” in my tests for Stan math. I have however become slightly suspect of its behavior recently and want to discuss whether it’s implementation shouldn’t change (especially if we want to encourage it’s use across the codebase).
The relevant part of the code is:
void expect_near_rel_finite(const std::string& msg, const T1& x1, const T2& x2,
double tol = 1e-8) {
using stan::math::fabs;
// if either arg near zero, must use absolute tolerance as rel tol -> 2
if (fabs(x1) < tol || fabs(x2) < tol) {
EXPECT_NEAR(x1, x2, tol)
return;
}
auto avg = 0.5 * (fabs(x1) + fabs(x2));
auto relative_diff = (x1 - x2) / avg;
EXPECT_NEAR(0, relative_diff, tol)
This however behaves strangely for values close to the tolerance (1e-8
by default), where a steep change in required precision happens. For example, I now have a failing test with:
actual: 1.2019658157669255e-008,
expected: 1.2019540397111151e-008,
relative tolerance = 1e-008; relative diff = 9.7973780478180374e-006
This is OK to fail, but if I had actual = 0.9e-008
, the test would suddenly pass, even though the difference just increased.
Some discussion on the topic is at Relative delta in finite difference approximations of derivatives? where @anon75146577 recommends using max(epsilon, epsilon*|x|)
as a tolerance, which doesn’t have this problem, but might be unnecessarily lax when we care about small numbers. I’ve previously used something like max(1e-14, 1e-8*|x|)
.
So I am asking whether there is some good reason expect_near_rel
is implemented as it is and whether it might be sensible to implement it differently. expect_near_rel
is currently used in 21 tests, so it should not be a major change. It is also possible that expect_near_rel
is tailored to the autodiff case and shouldn’t be used elsewhere…
Thanks for any input!