Skip to content

Commit 5e6f41b

Browse files
tests: drop coverage duplicated across suites
- value pins and error throws already covered by the test/prob fixtures - Eigen-vectorized expect_ad covered by the generated vv/ffv variants
1 parent c082e39 commit 5e6f41b

3 files changed

Lines changed: 50 additions & 195 deletions

File tree

test/unit/math/mix/prob/inv_gaussian_test.cpp

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -81,34 +81,6 @@ TEST_F(AgradRev, mathMixScalFun_inv_gaussian_lccdf_tails) {
8181
stan::test::expect_ad(f, 0.5, 0.1, 50.0);
8282
}
8383

84-
TEST_F(AgradRev, mathMixScalFun_inv_gaussian_lpdf_vectorized) {
85-
Eigen::VectorXd y(3);
86-
y << 0.3, 1.2, 4.0;
87-
Eigen::VectorXd mu(3);
88-
mu << 0.5, 1.0, 2.0;
89-
Eigen::VectorXd lambda(3);
90-
lambda << 2.0, 5.0, 0.7;
91-
92-
auto f = [](const auto& y, const auto& mu, const auto& lambda) {
93-
return stan::math::inv_gaussian_lpdf(y, mu, lambda);
94-
};
95-
stan::test::expect_ad(f, y, mu, lambda);
96-
}
97-
98-
TEST_F(AgradRev, mathMixScalFun_inv_gaussian_lccdf_vectorized) {
99-
Eigen::VectorXd y(3);
100-
y << 0.3, 1.2, 4.0;
101-
Eigen::VectorXd mu(3);
102-
mu << 0.5, 1.0, 2.0;
103-
Eigen::VectorXd lambda(3);
104-
lambda << 2.0, 5.0, 0.7;
105-
106-
auto f = [](const auto& y, const auto& mu, const auto& lambda) {
107-
return stan::math::inv_gaussian_lccdf(y, mu, lambda);
108-
};
109-
stan::test::expect_ad(f, y, mu, lambda);
110-
}
111-
11284
// The cdf's partials scale by the whole-container product, which a scalar
11385
// test does not exercise.
11486
TEST_F(AgradRev, mathMixScalFun_inv_gaussian_cdf_vectorized) {
@@ -125,20 +97,6 @@ TEST_F(AgradRev, mathMixScalFun_inv_gaussian_cdf_vectorized) {
12597
stan::test::expect_ad(f, y, mu, lambda);
12698
}
12799

128-
TEST_F(AgradRev, mathMixScalFun_inv_gaussian_lcdf_vectorized) {
129-
Eigen::VectorXd y(3);
130-
y << 0.3, 1.2, 4.0;
131-
Eigen::VectorXd mu(3);
132-
mu << 0.5, 1.0, 2.0;
133-
Eigen::VectorXd lambda(3);
134-
lambda << 2.0, 5.0, 0.7;
135-
136-
auto f = [](const auto& y, const auto& mu, const auto& lambda) {
137-
return stan::math::inv_gaussian_lcdf(y, mu, lambda);
138-
};
139-
stan::test::expect_ad(f, y, mu, lambda);
140-
}
141-
142100
TEST_F(AgradRev, mathMixScalFun_inv_gaussian_std_vector) {
143101
std::vector<double> y{0.3, 1.2, 4.0};
144102
std::vector<double> mu{0.5, 1.0, 2.0};

test/unit/math/prim/prob/inv_gaussian_test.cpp

Lines changed: 4 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,6 @@ TEST(ProbDistributionsInvGaussian, distributionCheck) {
4343
check_quantiles_real_real(InvGaussianTestRig());
4444
}
4545

46-
TEST(ProbDistributionsInvGaussian, error_check) {
47-
boost::random::mt19937 rng;
48-
EXPECT_NO_THROW(stan::math::inv_gaussian_rng(1.0, 2.0, rng));
49-
EXPECT_THROW(stan::math::inv_gaussian_rng(0.0, 2.0, rng), std::domain_error);
50-
EXPECT_THROW(stan::math::inv_gaussian_rng(-1.0, 2.0, rng), std::domain_error);
51-
EXPECT_THROW(stan::math::inv_gaussian_rng(1.0, 0.0, rng), std::domain_error);
52-
EXPECT_THROW(stan::math::inv_gaussian_rng(1.0, -1.0, rng), std::domain_error);
53-
EXPECT_THROW(
54-
stan::math::inv_gaussian_rng(stan::math::positive_infinity(), 2.0, rng),
55-
std::domain_error);
56-
EXPECT_THROW(
57-
stan::math::inv_gaussian_rng(1.0, stan::math::positive_infinity(), rng),
58-
std::domain_error);
59-
}
60-
6146
TEST(ProbDistributionsInvGaussian, rngStableForLargeMuOverLambda) {
6247
boost::random::mt19937 rng(1234);
6348
for (double mu : {1.0, 1e3, 1e6, 1e9, 1e12}) {
@@ -91,25 +76,6 @@ TEST(ProbDistributionsInvGaussian, rngMomentsAtLargeMu) {
9176
EXPECT_NEAR(mu * mu * mu / lambda, var, 0.15 * mu * mu * mu / lambda);
9277
}
9378

94-
// reference values from mpmath at 60 digits
95-
96-
TEST(ProbDistributionsInvGaussian, values) {
97-
using stan::math::inv_gaussian_cdf;
98-
using stan::math::inv_gaussian_lccdf;
99-
using stan::math::inv_gaussian_lcdf;
100-
using stan::math::inv_gaussian_lpdf;
101-
102-
EXPECT_FLOAT_EQ(-2.479180611448965157415, inv_gaussian_lpdf(1.2, 0.5, 2.0));
103-
EXPECT_FLOAT_EQ(-2.391593703832052124008, inv_gaussian_lpdf(0.3, 1.0, 5.0));
104-
EXPECT_FLOAT_EQ(0.9815922531042920910742, inv_gaussian_cdf(1.2, 0.5, 2.0));
105-
EXPECT_FLOAT_EQ(0.003359190912064955560317, inv_gaussian_cdf(0.3, 1.0, 5.0));
106-
EXPECT_FLOAT_EQ(-0.01857927772712011847827, inv_gaussian_lcdf(1.2, 0.5, 2.0));
107-
EXPECT_FLOAT_EQ(-5.696055133984662592139, inv_gaussian_lcdf(0.3, 1.0, 5.0));
108-
EXPECT_FLOAT_EQ(-3.9949836760335225255, inv_gaussian_lccdf(1.2, 0.5, 2.0));
109-
EXPECT_FLOAT_EQ(-0.003364845660995599504277,
110-
inv_gaussian_lccdf(0.3, 1.0, 5.0));
111-
}
112-
11379
TEST(ProbDistributionsInvGaussian, boundaries) {
11480
using stan::math::inv_gaussian_cdf;
11581
using stan::math::inv_gaussian_lccdf;
@@ -153,6 +119,7 @@ TEST(ProbDistributionsInvGaussian, boundariesInContainer) {
153119
}
154120

155121
// 2 lambda / mu is past the overflow point of exp for all of these.
122+
// reference values from mpmath at 60 digits
156123
TEST(ProbDistributionsInvGaussian, largeExpFactor) {
157124
using stan::math::inv_gaussian_lccdf;
158125
using stan::math::inv_gaussian_lcdf;
@@ -248,24 +215,6 @@ TEST(ProbDistributionsInvGaussian, cdfCcdfSumToOne) {
248215
}
249216
}
250217

251-
TEST(ProbDistributionsInvGaussian, vectorMatchesScalarSum) {
252-
using stan::math::inv_gaussian_lccdf;
253-
using stan::math::inv_gaussian_lcdf;
254-
using stan::math::inv_gaussian_lpdf;
255-
std::vector<double> y{0.2, 0.5, 1.0, 2.0, 5.0};
256-
double sum_lpdf = 0;
257-
double sum_lcdf = 0;
258-
double sum_lccdf = 0;
259-
for (double yi : y) {
260-
sum_lpdf += inv_gaussian_lpdf(yi, 1.3, 3.0);
261-
sum_lcdf += inv_gaussian_lcdf(yi, 1.3, 3.0);
262-
sum_lccdf += inv_gaussian_lccdf(yi, 1.3, 3.0);
263-
}
264-
EXPECT_FLOAT_EQ(sum_lpdf, inv_gaussian_lpdf(y, 1.3, 3.0));
265-
EXPECT_FLOAT_EQ(sum_lcdf, inv_gaussian_lcdf(y, 1.3, 3.0));
266-
EXPECT_FLOAT_EQ(sum_lccdf, inv_gaussian_lccdf(y, 1.3, 3.0));
267-
}
268-
269218
// check helper functions; tolerances scale with each value's magnitude
270219
TEST(ProbDistributionsInvGaussian, internalLogPhi) {
271220
using stan::math::internal::log_Phi;
@@ -294,29 +243,16 @@ TEST(ProbDistributionsInvGaussian, internalLogPhi) {
294243
EXPECT_FLOAT_EQ(0.0, log_Phi(inf));
295244
EXPECT_FLOAT_EQ(-inf, log_Phi(-inf));
296245
EXPECT_TRUE(std::isnan(log_Phi(std::numeric_limits<double>::quiet_NaN())));
297-
}
298246

299-
// The true slope at z = -30 is about 30, so across a 2e-10 interval the
300-
// honest change is about 6e-9.
301-
TEST(ProbDistributionsInvGaussian, internalLogPhiBranchContinuity) {
302-
using stan::math::internal::log_Phi;
247+
// branch continuity: the true slope at z = -30 is about 30, so across a
248+
// 2e-10 interval the honest change is about 6e-9
303249
double eps = 1e-10;
304250
double step = log_Phi(-30.0 + eps) - log_Phi(-30.0 - eps);
305251
EXPECT_LT(std::fabs(step), 1e-7);
306252
}
307253

308-
TEST(ProbDistributionsInvGaussian, errors) {
254+
TEST(ProbDistributionsInvGaussian, sizeMismatch) {
309255
using stan::math::inv_gaussian_lpdf;
310-
double inf = std::numeric_limits<double>::infinity();
311-
312-
EXPECT_THROW(inv_gaussian_lpdf(-1.0, 1.0, 2.0), std::domain_error);
313-
EXPECT_THROW(inv_gaussian_lpdf(1.0, 0.0, 2.0), std::domain_error);
314-
EXPECT_THROW(inv_gaussian_lpdf(1.0, -1.0, 2.0), std::domain_error);
315-
EXPECT_THROW(inv_gaussian_lpdf(1.0, inf, 2.0), std::domain_error);
316-
EXPECT_THROW(inv_gaussian_lpdf(1.0, 1.0, 0.0), std::domain_error);
317-
EXPECT_THROW(inv_gaussian_lpdf(1.0, 1.0, -1.0), std::domain_error);
318-
EXPECT_THROW(inv_gaussian_lpdf(1.0, 1.0, inf), std::domain_error);
319-
320256
std::vector<double> y{1.0, 2.0};
321257
std::vector<double> mu{1.0, 2.0, 3.0};
322258
EXPECT_THROW(inv_gaussian_lpdf(y, mu, 1.0), std::invalid_argument);

test/unit/math/rev/prob/inv_gaussian_test.cpp

Lines changed: 46 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <gtest/gtest.h>
44
#include <functional>
55
#include <limits>
6-
#include <vector>
76

87
namespace {
98

@@ -199,65 +198,11 @@ TEST_F(AgradRev, inv_gaussian_boundary_partials_are_zero) {
199198
}
200199
}
201200

202-
// A y == 0 element contributes a log survivor of zero and leaves the other
203-
// elements alone, gradients included.
204-
TEST_F(AgradRev, inv_gaussian_lccdf_zero_boundary_is_elementwise) {
205-
using stan::math::var;
206-
using stan::math::vector_v;
207-
208-
EXPECT_FLOAT_EQ(0.0, stan::math::inv_gaussian_lccdf(0.0, 1.0, 2.0));
209-
210-
std::vector<double> y{0.0, 5.0};
211-
EXPECT_FLOAT_EQ(stan::math::inv_gaussian_lccdf(5.0, 1.0, 2.0),
212-
stan::math::inv_gaussian_lccdf(y, 1.0, 2.0));
213-
214-
vector_v y_v(2);
215-
y_v << 0.0, 5.0;
216-
var mu = 1.0;
217-
var lambda = 2.0;
218-
var out = stan::math::inv_gaussian_lccdf(y_v, mu, lambda);
219-
out.grad();
220-
double adj_boundary = y_v(0).adj();
221-
double adj_finite = y_v(1).adj();
222-
double adj_mu = mu.adj();
223-
double adj_lambda = lambda.adj();
224-
stan::math::recover_memory();
225-
226-
var y_s = 5.0;
227-
var mu_s = 1.0;
228-
var lambda_s = 2.0;
229-
var out_s = stan::math::inv_gaussian_lccdf(y_s, mu_s, lambda_s);
230-
out_s.grad();
231-
EXPECT_FLOAT_EQ(0.0, adj_boundary);
232-
EXPECT_FLOAT_EQ(y_s.adj(), adj_finite);
233-
EXPECT_FLOAT_EQ(mu_s.adj(), adj_mu);
234-
EXPECT_FLOAT_EQ(lambda_s.adj(), adj_lambda);
235-
stan::math::recover_memory();
236-
}
237-
238-
// Deep in the upper tail the survivor's two terms agree to the last bit and
239-
// the guarded difference underflows to -inf at a finite y. The partials are
240-
// zero there, and the guard fires deterministically at these points.
241-
TEST_F(AgradRev, inv_gaussian_lccdf_underflow_partials_are_zero) {
242-
using stan::math::var;
243-
double inf = std::numeric_limits<double>::infinity();
244-
for (double y_dbl : {1e-2, 1.0, 10.0}) {
245-
var y = y_dbl;
246-
var mu = 1e-3;
247-
var lambda = 1e14;
248-
var out = stan::math::inv_gaussian_lccdf(y, mu, lambda);
249-
EXPECT_FLOAT_EQ(-inf, out.val()) << "y=" << y_dbl;
250-
out.grad();
251-
EXPECT_FLOAT_EQ(0.0, y.adj()) << "y=" << y_dbl;
252-
EXPECT_FLOAT_EQ(0.0, mu.adj()) << "y=" << y_dbl;
253-
EXPECT_FLOAT_EQ(0.0, lambda.adj()) << "y=" << y_dbl;
254-
stan::math::recover_memory();
255-
}
256-
}
257-
258-
// lambda / y overflows for representable arguments, saturating the log CDF to
259-
// -inf at an interior y. The saturation mask zeroes every partial there.
260-
TEST_F(AgradRev, inv_gaussian_saturation_partials_are_zero) {
201+
// The log probability saturates to -inf at an interior y in two ways: the
202+
// guarded survivor difference underflows deep in the upper tail, and
203+
// lambda / y overflows for representable arguments. Both trigger the same
204+
// mask, which zeroes every partial.
205+
TEST_F(AgradRev, inv_gaussian_saturated_partials_are_zero) {
261206
using stan::math::var;
262207
double inf = std::numeric_limits<double>::infinity();
263208

@@ -291,36 +236,52 @@ TEST_F(AgradRev, inv_gaussian_saturation_partials_are_zero) {
291236
check(cdf, 1e-10, 1.0, 1e300, 0.0, "cdf");
292237
// y is above mu, so the survivor saturates
293238
check(lccdf, 2e-3, 1e-3, 1e308, -inf, "lccdf");
239+
// survivor underflow: the two lccdf terms agree to the last bit
240+
check(lccdf, 1e-2, 1e-3, 1e14, -inf, "lccdf underflow");
241+
check(lccdf, 1.0, 1e-3, 1e14, -inf, "lccdf underflow");
242+
check(lccdf, 10.0, 1e-3, 1e14, -inf, "lccdf underflow");
294243
}
295244

296-
// The same for the y == inf boundary of the lcdf and cdf.
297-
TEST_F(AgradRev, inv_gaussian_inf_boundary_is_elementwise) {
245+
// A boundary element leaves the other elements' adjoints alone; the
246+
// container values themselves are pinned in the prim suite.
247+
TEST_F(AgradRev, inv_gaussian_boundary_adjoints_are_elementwise) {
298248
using stan::math::var;
299249
using stan::math::vector_v;
300250
double inf = std::numeric_limits<double>::infinity();
301251

302-
vector_v y_v(2);
303-
y_v << inf, 0.5;
304-
var mu = 1.0;
305-
var lambda = 2.0;
306-
var out = stan::math::inv_gaussian_lcdf(y_v, mu, lambda);
307-
out.grad();
308-
double vec_val = out.val();
309-
double adj_boundary = y_v(0).adj();
310-
double adj_finite = y_v(1).adj();
311-
double adj_mu = mu.adj();
312-
double adj_lambda = lambda.adj();
313-
stan::math::recover_memory();
252+
auto check = [](auto f, double boundary, double finite) {
253+
vector_v y_v(2);
254+
y_v << boundary, finite;
255+
var mu = 1.0;
256+
var lambda = 2.0;
257+
var out = f(y_v, mu, lambda);
258+
out.grad();
259+
double adj_boundary = y_v(0).adj();
260+
double adj_finite = y_v(1).adj();
261+
double adj_mu = mu.adj();
262+
double adj_lambda = lambda.adj();
263+
stan::math::recover_memory();
314264

315-
var y_s = 0.5;
316-
var mu_s = 1.0;
317-
var lambda_s = 2.0;
318-
var out_s = stan::math::inv_gaussian_lcdf(y_s, mu_s, lambda_s);
319-
out_s.grad();
320-
EXPECT_FLOAT_EQ(out_s.val(), vec_val);
321-
EXPECT_FLOAT_EQ(0.0, adj_boundary);
322-
EXPECT_FLOAT_EQ(y_s.adj(), adj_finite);
323-
EXPECT_FLOAT_EQ(mu_s.adj(), adj_mu);
324-
EXPECT_FLOAT_EQ(lambda_s.adj(), adj_lambda);
325-
stan::math::recover_memory();
265+
var y_s = finite;
266+
var mu_s = 1.0;
267+
var lambda_s = 2.0;
268+
var out_s = f(y_s, mu_s, lambda_s);
269+
out_s.grad();
270+
EXPECT_FLOAT_EQ(0.0, adj_boundary);
271+
EXPECT_FLOAT_EQ(y_s.adj(), adj_finite);
272+
EXPECT_FLOAT_EQ(mu_s.adj(), adj_mu);
273+
EXPECT_FLOAT_EQ(lambda_s.adj(), adj_lambda);
274+
stan::math::recover_memory();
275+
};
276+
277+
check(
278+
[](const auto& y, const auto& mu, const auto& lambda) {
279+
return stan::math::inv_gaussian_lccdf(y, mu, lambda);
280+
},
281+
0.0, 5.0);
282+
check(
283+
[](const auto& y, const auto& mu, const auto& lambda) {
284+
return stan::math::inv_gaussian_lcdf(y, mu, lambda);
285+
},
286+
inf, 0.5);
326287
}

0 commit comments

Comments
 (0)