Skip to content

Commit 966f480

Browse files
Document and cleanup elementwise check/is.
1 parent 83aa8f5 commit 966f480

3 files changed

Lines changed: 146 additions & 75 deletions

File tree

stan/math/opencl/matrix_cl.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,7 @@ class matrix_cl<T, require_arithmetic_t<T>> {
459459
*/
460460
template <typename Expr,
461461
require_all_valid_expressions_and_none_scalar_t<Expr>* = nullptr>
462-
matrix_cl(const Expr& expression); // NOLINT This constructor is
463-
// intentionally implicit
462+
matrix_cl(const Expr& expression); // NOLINT(runtime/explicit)
464463

465464
/** \ingroup opencl
466465
* Move assignment operator.

stan/math/prim/err/elementwise_check.hpp

Lines changed: 140 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <stan/math/prim/fun/get.hpp>
66
#include <stan/math/prim/fun/size.hpp>
77
#include <stan/math/prim/fun/value_of_rec.hpp>
8+
#include <stan/math/prim/meta/is_vector.hpp>
89
#include <string>
910
#include <sstream>
1011
#include <vector>
@@ -13,80 +14,148 @@ namespace stan {
1314
namespace math {
1415
namespace internal {
1516

16-
template <typename T_is_good, typename T_exception>
17-
struct Checker {
18-
const T_is_good& is_good;
17+
/** Apply an error check to a container, signal failure by throwing.
18+
* Apply a predicate like is_positive to the double underlying every scalar in a
19+
* container, throw an exception if the predicate fails for any double.
20+
* @tparam F type of predicate
21+
* @tparam E type of exception thrown
22+
*/
23+
template <typename F, typename E>
24+
class Checker {
25+
const F& is_good;
1926
const char* function;
2027
const char* name;
2128
const char* suffix;
2229

23-
void raise_error_impl(std::stringstream& ss) { throw T_exception{ss.str()}; }
24-
25-
template <typename... M>
26-
void raise_error_impl(std::stringstream& ss, const char* message,
27-
const M&... messages) {
30+
public:
31+
/**
32+
* @param is_good predicate to check, must accept doubles and produce bools
33+
* @param function function name (for error messages)
34+
* @param name variable name (for error messages)
35+
* @param suffix message to print at end of error message
36+
*/
37+
Checker(const F& is_good, const char* function, const char* name,
38+
const char* suffix)
39+
: is_good(is_good), function(function), name(name), suffix(suffix) {}
40+
41+
/**
42+
* Throw an exception of type `E`.
43+
* The error message is the string inside the provided stringstream.
44+
* @param ss stringstream containing error message
45+
* @throws `E`
46+
*/
47+
void raise_error_ss(std::stringstream& ss) { throw E{ss.str()}; }
48+
49+
/**
50+
* Throw an exception of type `E`.
51+
* The error message is the concatenation of the string inside the provided
52+
* stringstream with all the provided messages.
53+
* @tparam M types of first message
54+
* @tparam Ms types of other messages
55+
* @param ss stringstream to accumulate error message in.
56+
* @param message a message to append to `ss`
57+
* @param messages more messages to append
58+
* @throws `E`
59+
*/
60+
template <typename M, typename... Ms>
61+
void raise_error_ss(std::stringstream& ss, const M& message,
62+
const Ms&... messages) {
2863
ss << message;
29-
raise_error_impl(ss, messages...);
30-
}
31-
32-
template <typename... M>
33-
void raise_error_impl(std::stringstream& ss, double value_of_x,
34-
const M&... messages) {
35-
ss << value_of_x;
36-
raise_error_impl(ss, messages...);
64+
raise_error_ss(ss, messages...);
3765
}
3866

39-
template <typename... M>
40-
void raise_error_impl(std::stringstream& ss, size_t index,
41-
const M&... messages) {
42-
ss << index + 1;
43-
raise_error_impl(ss, messages...);
44-
}
45-
46-
template <typename... M>
47-
void raise_error(const M&... messages) {
67+
/**
68+
* Throw an exception of type `E`.
69+
* The error message is the concatenation of the provided messages.
70+
* @tparam Ms types of messages
71+
* @param messages a list of messages
72+
* @throws `E`
73+
*/
74+
template <typename... Ms>
75+
void raise_error(const Ms&... messages) {
4876
std::stringstream ss{};
49-
raise_error_impl(ss, messages...);
77+
raise_error_ss(ss, messages...);
5078
}
5179

52-
template <typename T, typename = require_stan_scalar_t<T>,
53-
typename... T_indices>
54-
void check(const T& x, T_indices... indices) {
80+
/**
81+
* Check the scalar.
82+
* @tparam T type of scalar
83+
* @tparam Ms types of messages
84+
* @param x scalar
85+
* @param messages a list of messages to append to the error message
86+
* @throws `E` if the scalar fails the error check
87+
*/
88+
template <typename T, typename = require_stan_scalar_t<T>, typename... Ms>
89+
void check(const T& x, Ms... messages) {
5590
double xd = value_of_rec(x);
5691
if (!is_good(xd))
57-
raise_error(function, ": ", name, indices..., " is ", xd, suffix);
92+
raise_error(function, ": ", name, messages..., " is ", xd, suffix);
5893
}
5994

60-
template <typename T, typename = require_eigen_vector_t<T>, typename = void,
61-
typename... T_indices>
62-
void check(const T& x, T_indices... indices) {
95+
/**
96+
* Check all the scalars inside the vector.
97+
* @tparam T type of vector
98+
* @tparam Ms types of messages
99+
* @param x vector
100+
* @param messages a list of messages to append to the error message
101+
* @throws `E` if any of the scalars fail the error check
102+
*/
103+
template <typename T, typename = require_vector_t<T>, typename = void,
104+
typename... Ms>
105+
void check(const T& x, Ms... messages) {
63106
for (size_t i = 0; i < stan::math::size(x); ++i)
64-
check(x(i), indices..., "[", i, "]");
107+
check(x[i], messages..., "[", i + 1, "]");
65108
}
66109

67-
template <typename T, typename... T_indices>
68-
void check(const std::vector<T>& x, T_indices... indices) {
69-
for (size_t i = 0; i < stan::math::size(x); ++i)
70-
check(x[i], indices..., "[", i, "]");
71-
}
72-
73-
template <typename T, typename... T_indices>
74-
void check(const Eigen::DenseBase<T>& x, T_indices... indices) {
110+
/**
111+
* Check all the scalars inside the matrix.
112+
* @tparam Derived type of matrix
113+
* @tparam Ms types of messages
114+
* @param x matrix
115+
* @param messages a list of messages to append to the error message
116+
* @throws `E` if any of the scalars fail the error check
117+
*/
118+
template <typename Derived, typename... Ms>
119+
void check(const Eigen::DenseBase<Derived>& x, Ms... messages) {
75120
for (size_t n = 0; n < x.cols(); ++n)
76121
for (size_t m = 0; m < x.rows(); ++m)
77-
check(x(m, n), indices..., "[row=", m, ", col=", n, "]");
122+
check(x(m, n), messages..., "[row=", m + 1, ", col=", n + 1, "]");
78123
}
79-
};
80-
81-
template <typename T_is_good>
82-
struct Iser {
83-
const T_is_good& is_good;
124+
}; // namespace internal
84125

126+
/** Apply an error check to a container, signal failure with `false`.
127+
* Apply a predicate like is_positive to the double underlying every scalar in a
128+
* container, producing true if the predicate holds everywhere and `false` if it
129+
* fails anywhere.
130+
* @tparam F type of predicate
131+
*/
132+
template <typename F>
133+
class Iser {
134+
const F& is_good;
135+
136+
public:
137+
/**
138+
* @param is_good predicate to check, must accept doubles and produce bools
139+
*/
140+
explicit Iser(const F& is_good) : is_good(is_good) {}
141+
142+
/**
143+
* Check the scalar.
144+
* @tparam T type of scalar
145+
* @param x scalar
146+
* @return `false` if the scalar fails the error check
147+
*/
85148
template <typename T, typename = require_stan_scalar_t<T>>
86149
bool is(const T& x) {
87150
return is_good(value_of_rec(x));
88151
}
89152

153+
/**
154+
* Check all the scalars inside the container.
155+
* @tparam T type of scalar
156+
* @param x container
157+
* @return `false` if any of the scalars fail the error check
158+
*/
90159
template <typename T, typename = require_not_stan_scalar_t<T>,
91160
typename = void>
92161
bool is(const T& x) {
@@ -100,35 +169,42 @@ struct Iser {
100169
} // namespace internal
101170

102171
/**
103-
* Check that the predicate holds for the value of x, working elementwise on
104-
* containers. If x is a container, check each element inside x, recursively.
105-
* @tparam T_is_good type of is_good predicate
106-
* @tparam T_x type of x
172+
* Check that the predicate holds for the value of `x`, working elementwise on
173+
* containers. If `x` is a scalar, check the double underlying the scalar. If
174+
* `x` is a container, check each element inside `x`, recursively.
175+
* @tparam F type of predicate
176+
* @tparam T type of `x`
107177
* @param is_good predicate to check, must accept doubles and produce bools
108178
* @param function function name (for error messages)
109179
* @param name variable name (for error messages)
110180
* @param x variable to check, can be a scalar, a container of scalars, a
111181
* container of containers of scalars, etc
112182
* @param suffix message to print at end of error message
113-
* @throw <code>std::domain_error</code> if is_good returns false for the value
114-
* of any element in x the predicate
183+
* @throws `std::domain_error` if `is_good` returns `false` for the value
184+
* of any element in `x`
115185
*/
116-
template <typename T_is_good, typename T_x>
117-
inline void elementwise_check(const T_is_good& is_good, const char* function,
118-
const char* name, const T_x& x,
186+
template <typename F, typename T>
187+
inline void elementwise_check(const F& is_good, const char* function,
188+
const char* name, const T& x,
119189
const char* suffix) {
120-
internal::Checker<T_is_good, std::domain_error>{is_good, function, name,
121-
suffix}
190+
internal::Checker<F, std::domain_error>{is_good, function, name, suffix}
122191
.check(x);
123192
}
124193

125194
/**
126-
* Like elementwise_check, but indicate failure by returning false instead of
127-
* by throwing.
195+
* Check that the predicate holds for the value of `x`, working elementwise on
196+
* containers. If `x` is a scalar, check the double underlying the scalar. If
197+
* `x` is a container, check each element inside `x`, recursively.
198+
* @tparam F type of predicate
199+
* @tparam T type of `x`
200+
* @param is_good predicate to check, must accept doubles and produce bools
201+
* @param x variable to check, can be a scalar, a container of scalars, a
202+
* container of containers of scalars, etc
203+
* @return `false` if any of the scalars fail the error check
128204
*/
129-
template <typename T_is_good, typename T_x>
130-
inline bool elementwise_is(const T_is_good& is_good, const T_x& x) {
131-
return internal::Iser<T_is_good>{is_good}.is(x);
205+
template <typename F, typename T>
206+
inline bool elementwise_is(const F& is_good, const T& x) {
207+
return internal::Iser<F>{is_good}.is(x);
132208
}
133209

134210
} // namespace math

test/unit/math/prim/err/elementwise_check_test.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@
55
#include <limits>
66
#include <vector>
77

8-
template <typename T>
9-
bool example_predicate(const T& x) {
10-
return !stan::math::is_nan(x);
11-
}
8+
auto p = [](const auto& x) { return !stan::math::is_nan(x); };
129

1310
template <typename T>
1411
void do_check(const T& x) {
15-
stan::math::elementwise_check(
16-
[](const auto& x) { return example_predicate(x); },
17-
"elementwise_check_tests", "x", x, ", but must not be nan!");
12+
stan::math::elementwise_check([](const auto& x) { return p(x); },
13+
"elementwise_check_tests", "x", x,
14+
", but must not be nan!");
1815
}
1916

2017
template <typename T>
2118
bool do_is(const T& x) {
22-
return stan::math::elementwise_is(
23-
[](const auto& x) { return example_predicate(x); }, x);
19+
return stan::math::elementwise_is([](const auto& x) { return p(x); }, x);
2420
}
2521

2622
template <typename T>

0 commit comments

Comments
 (0)