Skip to content

Commit

Permalink
Merge pull request #1657 from martinmodrak/feature/1656-expect_near_r…
Browse files Browse the repository at this point in the history
…el_improvements

Improved behavior of expect_near_rel
  • Loading branch information
Bob Carpenter authored Feb 7, 2020
2 parents 044cbcb + f54c4a0 commit 77248cf
Show file tree
Hide file tree
Showing 23 changed files with 279 additions and 135 deletions.
39 changes: 21 additions & 18 deletions test/unit/math/ad_tolerances.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef TEST_UNIT_MATH_AD_TOLERANCES_HPP
#define TEST_UNIT_MATH_AD_TOLERANCES_HPP

#include <test/unit/math/relative_tolerance.hpp>

namespace stan {
namespace test {

Expand All @@ -13,34 +15,35 @@ namespace test {
* if the function is implemented using only forward mode, and end
* with the quantity being calculated; for example,
* `hessian_fvar_grad_` is the gradient calculated by the `hessian`
* function using forward-mode autodiff.
* function using forward-mode autodiff. Those get interpreted as
* relative tolerances, see `relative_tolerance` class more details.
*
* `gradient_val_`: 1e-8; `gradient_grad_`: 1e-4
*
* `gradient_fvar_val_`: 1e-8; `gradient_fvar_grad_`: 1e-4
*
* `hessian_val_` : 1e-8; `hessian_grad_`: 1e-4; `hessian_hessian_`: 1e-3
* `hessian_val_` : 1e-8; `hessian_grad_`: 1e-4; `hessian_hessian_`: (1e-4,1e-3)
*
* `hessian_fvar_val_` : 1e-8; `hessian_fvar_grad_`: 1e-4;
* `hessian_fvar_hessian_`: 1e-3
* `hessian_fvar_hessian_`: (1e-4,1e-3)
*
* `grad_hessian_val_` : 1e-8; `grad_hessian_hessian_`: 1e-3;
* `grad_hessian_grad_hessian_`: 1e-2
*/
struct ad_tolerances {
double gradient_val_;
double gradient_grad_;
double gradient_fvar_val_;
double gradient_fvar_grad_;
double hessian_val_;
double hessian_grad_;
double hessian_hessian_;
double hessian_fvar_val_;
double hessian_fvar_grad_;
double hessian_fvar_hessian_;
double grad_hessian_val_;
double grad_hessian_hessian_;
double grad_hessian_grad_hessian_;
relative_tolerance gradient_val_;
relative_tolerance gradient_grad_;
relative_tolerance gradient_fvar_val_;
relative_tolerance gradient_fvar_grad_;
relative_tolerance hessian_val_;
relative_tolerance hessian_grad_;
relative_tolerance hessian_hessian_;
relative_tolerance hessian_fvar_val_;
relative_tolerance hessian_fvar_grad_;
relative_tolerance hessian_fvar_hessian_;
relative_tolerance grad_hessian_val_;
relative_tolerance grad_hessian_hessian_;
relative_tolerance grad_hessian_grad_hessian_;
ad_tolerances()
: gradient_val_(1e-8),
gradient_grad_(1e-4),
Expand All @@ -50,11 +53,11 @@ struct ad_tolerances {

hessian_val_(1e-8),
hessian_grad_(1e-4),
hessian_hessian_(1e-3),
hessian_hessian_(1e-4, 1e-3),

hessian_fvar_val_(1e-8),
hessian_fvar_grad_(1e-4),
hessian_fvar_hessian_(1e-3),
hessian_fvar_hessian_(1e-4, 1e-3),

grad_hessian_val_(1e-8),
grad_hessian_hessian_(1e-3),
Expand Down
38 changes: 11 additions & 27 deletions test/unit/math/expect_near_rel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <stan/math.hpp>
#include <gtest/gtest.h>
#include <test/unit/math/relative_tolerance.hpp>
#include <string>
#include <vector>

Expand All @@ -15,13 +16,7 @@ namespace internal {
* Test that the specified values are within the specified tolerance
* on relative error, and if not, fail the embedded google test.
*
* <p>Relative error is defined to be the error `u - v` rescaled by the
* average absolute value,
* `rel_err(u, v) = (u - v) / (0.5 * (abs(u) * + abs(v))).`
*
* <p>If at least one of `u` or `v` is zero, the absolute error is
* tested at the specified tolerance, because the relative error
* reduces to a constant.
* Uses relative_tolerance::inexact to compute the tolerance.
*
* @tparam T1 type of first argument
* @tparam T2 type of second argument
Expand All @@ -32,23 +27,11 @@ namespace internal {
*/
template <typename T1, typename T2, require_all_stan_scalar_t<T1, T2>...>
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) << "expect_near_rel_finite(" << x1 << ", " << x2
<< ", absolute tolerance = " << tol << ")"
<< "; absolute diff = " << fabs(x1 - x2)
<< " in: " << msg << std::endl;
return;
}
auto avg = 0.5 * (fabs(x1) + fabs(x2));
auto relative_diff = (x1 - x2) / avg;
EXPECT_NEAR(0, relative_diff, tol)
<< "expect_near_rel_finite(" << x1 << ", " << x2
<< ", relative tolerance = " << tol << ")"
<< "; relative diff = " << relative_diff << std::endl
<< " in: " << msg << std::endl;
const relative_tolerance tol
= relative_tolerance()) {
double tol_val = tol.inexact(x1, x2);
EXPECT_NEAR(x1, x2, tol_val)
<< "expect_near_rel_finite in: " << msg << std::endl;
}

template <typename EigMat1, typename EigMat2,
Expand Down Expand Up @@ -89,7 +72,7 @@ void expect_near_rel_finite(const std::string& msg, const std::vector<T1>& x1,
*/
template <typename T1, typename T2, require_all_stan_scalar_t<T1, T2>...>
void expect_near_rel(const std::string& msg, const T1& x1, const T2& x2,
double tol = 1e-8) {
relative_tolerance tol = relative_tolerance()) {
if (stan::math::is_nan(x1) || stan::math::is_nan(x2))
EXPECT_TRUE(stan::math::is_nan(x1) && stan::math::is_nan(x2))
<< "expect_near_rel(" << x1 << ", " << x2 << ")" << std::endl
Expand Down Expand Up @@ -120,7 +103,7 @@ void expect_near_rel(const std::string& msg, const T1& x1, const T2& x2,
template <typename EigMat1, typename EigMat2,
require_all_eigen_t<EigMat1, EigMat2>...>
void expect_near_rel(const std::string& msg, EigMat1&& x1, EigMat2&& x2,
double tol = 1e-8) {
relative_tolerance tol = relative_tolerance()) {
EXPECT_EQ(x1.rows(), x2.rows()) << "expect_near_rel (Eigen::Matrix)"
<< " rows must be same size."
<< " x1.rows() = " << x1.rows()
Expand All @@ -141,7 +124,8 @@ void expect_near_rel(const std::string& msg, EigMat1&& x1, EigMat2&& x2,

template <typename T1, typename T2>
void expect_near_rel(const std::string& msg, const std::vector<T1>& x1,
const std::vector<T2>& x2, double tol = 1e-8) {
const std::vector<T2>& x2,
relative_tolerance tol = relative_tolerance()) {
EXPECT_EQ(x1.size(), x2.size()) << "expect_near_rel (std::vector):"
<< " vectors must be same size."
<< " x1.size() = " << x1.size()
Expand Down
20 changes: 11 additions & 9 deletions test/unit/math/expect_near_rel_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ TEST(testUnitMath, ExpectNearRelScalar) {
expect_near_rel("test A1", 0, 0, 1e-16);
expect_near_rel("test A2", 0, 0.0, 1e-16);
expect_near_rel("test A3", 0.0, 0, 1e-16);
expect_near_rel("test B1", 0, 1e-8, 1e-5);
expect_near_rel("test B2", 0.0, 1e-8, 1e-5);
expect_near_rel("test C1", 1e-8, 0, 1e-5);
expect_near_rel("test C2", 1e-8, 0.0, 1e-5);
expect_near_rel("test B1", 0, 1e-8, 1e-4);
expect_near_rel("test B2", 0.0, 1e-8, 1e-4);
expect_near_rel("test C1", 1e-8, 0, 1e-4);
expect_near_rel("test C2", 1e-8, 0.0, 1e-4);

// non-zero examples
expect_near_rel("test D1", 1, 1, 1e-16);
Expand All @@ -33,6 +33,7 @@ TEST(testUnitMath, ExpectNearRelScalar) {
TEST(testUnitMath, ExpectNearRelMatrix) {
using Eigen::Matrix;
using stan::test::expect_near_rel;
using stan::test::relative_tolerance;
typedef Matrix<double, -1, 1> v_t;
typedef Matrix<double, 1, -1> rv_t;
typedef Matrix<double, -1, -1> m_t;
Expand All @@ -57,7 +58,7 @@ TEST(testUnitMath, ExpectNearRelMatrix) {
m_t d2(2, 3);
d1 << 1, 2, 3, 0, 0, 0 - 1e-8;
d2 << 1 + 1e-8, 2 - 1e-8, 3, 0, 0 + 1e-8, 0;
expect_near_rel("test D", d1, d2, 1e-6);
expect_near_rel("test D", d1, d2, relative_tolerance(1e-6, 1e-8));

// these will fail
// v_t e1(1);
Expand All @@ -82,7 +83,7 @@ TEST(testUnitMath, ExpectNearRelVector) {

expect_near_rel("test C", v_t{1, 1, 1}, v_t{1 + 1e-8, 1 - 1e-9, 1}, 1e-6);

expect_near_rel("test D", v_t{0, 0, 0}, v_t{0, 0 + 1e-6, 0 - 1e-6}, 1e-4);
expect_near_rel("test D", v_t{0, 0, 0}, v_t{0, 0 + 9e-9, 0 - 9e-9}, 1e-4);

// ones after here fail
// expect_near_rel("test E", v_t{1}, v_t{1, 2}, 1e-6);
Expand All @@ -91,6 +92,7 @@ TEST(testUnitMath, ExpectNearRelVector) {

TEST(testUnitMath, ExpectNearRelVectorNesting) {
using stan::test::expect_near_rel;
using stan::test::relative_tolerance;
using std::vector;
typedef vector<double> v_t;
typedef vector<v_t> vv_t;
Expand All @@ -102,19 +104,19 @@ TEST(testUnitMath, ExpectNearRelVectorNesting) {
expect_near_rel("test A", vv_t{}, vv_t{}, 1e-10);

expect_near_rel("test B", vv_t{v_t{1, 2, 3}, v_t{0, 0, 0}},
vv_t{v_t{1, 2, 3}, v_t{0, 0 + 1e-6, 0 - 1e-6}}, 1e-5);
vv_t{v_t{1, 2, 3}, v_t{0, 0 + 1e-6, 0 - 1e-6}}, 1e-3);

expect_near_rel(
"test C",
vvv_t{vv_t{v_t{1, 2, 3}, v_t{0, 0, 0}}, vv_t{v_t{1, 2, 3}, v_t{0, 0, 0}}},
vvv_t{vv_t{v_t{1, 2, 3}, v_t{0, 0 + 1e-6, 0 - 1e-6}},
vv_t{v_t{1, 2, 3}, v_t{0, 0 + 1e-6, 0 - 1e-6}}},
1e-5);
relative_tolerance(1e-5, 1e-6));

ev_t d1(3);
ev_t d2(3);
d1 << 1, 0, 0;
d2 << 1 + 1e-8, 0 + 1e-8, 0;
d2 << 1 + 1e-8, 0 + 1e-12, 0;
vev_t e1{d1, d2};
vev_t e2{d2, d1};
expect_near_rel("test E", e1, e2, 1e-6);
Expand Down
27 changes: 16 additions & 11 deletions test/unit/math/mix/fun/add_diag_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <test/unit/math/test_ad.hpp>
#include <test/unit/math/ad_tolerances.hpp>

TEST(MathMixMatFun, addDiag) {
using stan::test::relative_tolerance;
auto f
= [](const auto& x, const auto& y) { return stan::math::add_diag(x, y); };

Expand All @@ -22,18 +24,21 @@ TEST(MathMixMatFun, addDiag) {
Eigen::MatrixXd m23(2, 3);
m23 << 1, 1, 1, 1, 1, 1;

stan::test::ad_tolerances tol;
tol.hessian_hessian_ = relative_tolerance(1e-4, 1e-3);
tol.hessian_fvar_hessian_ = relative_tolerance(1e-4, 1e-3);
// these are OK calls
stan::test::expect_ad(f, m00, d);
stan::test::expect_ad(f, m00, v0);
stan::test::expect_ad(f, m11, d);
stan::test::expect_ad(f, m11, v1);
stan::test::expect_ad(f, m22, d);
stan::test::expect_ad(f, m22, v2);
stan::test::expect_ad(f, m23, d);
stan::test::expect_ad(f, m23, v2);
stan::test::expect_ad(tol, f, m00, d);
stan::test::expect_ad(tol, f, m00, v0);
stan::test::expect_ad(tol, f, m11, d);
stan::test::expect_ad(tol, f, m11, v1);
stan::test::expect_ad(tol, f, m22, d);
stan::test::expect_ad(tol, f, m22, v2);
stan::test::expect_ad(tol, f, m23, d);
stan::test::expect_ad(tol, f, m23, v2);

// these throw
stan::test::expect_ad(f, m11, v2);
stan::test::expect_ad(f, m22, v1);
stan::test::expect_ad(f, m23, v1);
stan::test::expect_ad(tol, f, m11, v2);
stan::test::expect_ad(tol, f, m22, v1);
stan::test::expect_ad(tol, f, m23, v1);
}
5 changes: 3 additions & 2 deletions test/unit/math/mix/fun/cov_matrix_constrain_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ typename stan::scalar_type<T>::type g3(const T& x) {

template <typename T>
void expect_cov_matrix_transform(const T& x) {
using stan::test::relative_tolerance;
stan::test::ad_tolerances tols;
tols.hessian_hessian_ = 1e-2;
tols.hessian_fvar_hessian_ = 1e-2;
tols.hessian_hessian_ = relative_tolerance(1e-3, 1e-3);
tols.hessian_fvar_hessian_ = relative_tolerance(1e-3, 1e-3);

auto f1 = [](const auto& x) { return g1(x); };
auto f2 = [](const auto& x) { return g2(x); };
Expand Down
5 changes: 3 additions & 2 deletions test/unit/math/mix/fun/determinant_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
#include <vector>

TEST(MathMixMatFun, determinant) {
using stan::test::relative_tolerance;
auto f = [](const auto& y) { return stan::math::determinant(y); };

stan::test::ad_tolerances tols;
tols.hessian_hessian_ = 1e-2; // default 1e-3
tols.hessian_fvar_hessian_ = 1e-2; // default 1e-3
tols.hessian_hessian_ = relative_tolerance(1e-4, 1e-2);
tols.hessian_fvar_hessian_ = relative_tolerance(1e-4, 1e-2);

Eigen::MatrixXd z(0, 0);

Expand Down
5 changes: 3 additions & 2 deletions test/unit/math/mix/fun/diag_post_multiply_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ void expect_diag_post_multiply(const Eigen::MatrixXd& a,
}

TEST(MathMixMatFun, diagPostMultiply) {
using stan::test::relative_tolerance;
// 0 x 0
Eigen::MatrixXd a00(0, 0);
Eigen::VectorXd u0(0);
Expand Down Expand Up @@ -50,8 +51,8 @@ TEST(MathMixMatFun, diagPostMultiply) {
expect_diag_post_multiply(a33c, u3c);

stan::test::ad_tolerances tols;
tols.hessian_hessian_ = 2e-2; // default 1e-3
tols.hessian_fvar_hessian_ = 2e-2; // default 1e-3
tols.hessian_hessian_ = relative_tolerance(1e-4, 2e-2);
tols.hessian_fvar_hessian_ = relative_tolerance(1e-4, 2e-2);

Eigen::MatrixXd a33(3, 3);
a33 << 1, 10, 100, 1000, 2, -4, 8, -16, 32;
Expand Down
5 changes: 3 additions & 2 deletions test/unit/math/mix/fun/diag_pre_multiply_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ void expect_diag_pre_multiply(const Eigen::VectorXd& v,
expect_diag_pre_multiply(v, a, tols);
}
TEST(MathMixMatFun, diagPreMultiply) {
using stan::test::relative_tolerance;
// 0 x 0
Eigen::MatrixXd a00(0, 0);
Eigen::VectorXd u0(0);
Expand Down Expand Up @@ -49,8 +50,8 @@ TEST(MathMixMatFun, diagPreMultiply) {
expect_diag_pre_multiply(u3c, a33c);

stan::test::ad_tolerances tols;
tols.hessian_hessian_ = 2e-2; // default 1e-3
tols.hessian_fvar_hessian_ = 2e-2; // default 1e-3
tols.hessian_hessian_ = relative_tolerance(1e-4, 2e-2);
tols.hessian_fvar_hessian_ = relative_tolerance(1e-4, 2e-2);

Eigen::MatrixXd a33(3, 3);
a33 << 1, 10, 100, 1000, 2, -4, 8, -16, 32;
Expand Down
5 changes: 3 additions & 2 deletions test/unit/math/mix/fun/dot_product_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

template <class F>
void test_dot_product(const F& f) {
using stan::test::relative_tolerance;
stan::test::ad_tolerances tols;
tols.hessian_hessian_ = 1e-2; // default is 1e-3
tols.hessian_fvar_hessian_ = 1e-2; // default is 1e-3
tols.hessian_hessian_ = relative_tolerance(1e-3, 1e-3);
tols.hessian_fvar_hessian_ = relative_tolerance(1e-3, 1e-3);

// size 0
Eigen::VectorXd v0(0);
Expand Down
3 changes: 2 additions & 1 deletion test/unit/math/mix/fun/eigenvalues_sym_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <test/unit/math/test_ad.hpp>

TEST(MathMixMatFun, eigenvaluesSym) {
using stan::test::relative_tolerance;
auto f = [](const auto& y) {
// need to maintain symmetry for finite diffs
auto a = ((y + y.transpose()) * 0.5).eval();
Expand Down Expand Up @@ -35,6 +36,6 @@ TEST(MathMixMatFun, eigenvaluesSym) {
a22 << 1, 2, 2, 1;
tols.hessian_hessian_ = 5e-1;
tols.hessian_fvar_hessian_ = 5e-1;
tols.grad_hessian_grad_hessian_ = 5e-1;
tols.grad_hessian_grad_hessian_ = relative_tolerance(1e-1, 5e-1);
stan::test::expect_ad(tols, f, a22);
}
Loading

0 comments on commit 77248cf

Please sign in to comment.