Automatic Differentiation
 
Loading...
Searching...
No Matches
wiener_full_lccdf_defective.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_PRIM_PROB_WIENER_FULL_LCCDF_DEFECTIVE_HPP
2#define STAN_MATH_PRIM_PROB_WIENER_FULL_LCCDF_DEFECTIVE_HPP
3
6
7namespace stan {
8namespace math {
9namespace internal {
10
31template <typename T_y, typename T_a, typename T_v, typename T_w, typename T_sw,
32 typename T_err>
33inline auto wiener7_ccdf_grad_sw(const T_y& y, const T_a& a, const T_v& v,
34 const T_w& w, const T_sw& sw,
35 T_err log_error) {
36 auto low = fmax(0.0, w - sw / 2.0);
37 auto high = fmin(1.0, w + sw / 2.0);
38
39 const auto lower_value = wiener4_ccdf(y, a, v, low, log_error);
40 const auto upper_value = wiener4_ccdf(y, a, v, high, log_error);
41 return 0.5 * (lower_value + upper_value) / sw;
42}
43
44} // namespace internal
45
84template <bool propto = false, typename T_y, typename T_a, typename T_t0,
85 typename T_w, typename T_v, typename T_sv, typename T_sw,
86 typename T_st0>
87inline auto wiener_lccdf_defective(const T_y& y, const T_a& a, const T_t0& t0,
88 const T_w& w, const T_v& v, const T_sv& sv,
89 const T_sw& sw, const T_st0& st0,
90 const double& precision_derivatives = 1e-8) {
92 using T_y_ref = ref_type_t<T_y>;
93 using T_a_ref = ref_type_t<T_a>;
94 using T_v_ref = ref_type_t<T_v>;
95 using T_w_ref = ref_type_t<T_w>;
96 using T_t0_ref = ref_type_t<T_t0>;
97 using T_sv_ref = ref_type_t<T_sv>;
98 using T_sw_ref = ref_type_t<T_sw>;
99 using T_st0_ref = ref_type_t<T_st0>;
101 using T_partials_return
103
104 T_y_ref y_ref = y;
105 T_a_ref a_ref = a;
106 T_v_ref v_ref = v;
107 T_w_ref w_ref = w;
108 T_t0_ref t0_ref = t0;
109 T_sv_ref sv_ref = sv;
110 T_sw_ref sw_ref = sw;
111 T_st0_ref st0_ref = st0;
112
113 auto y_val = to_ref(as_value_column_array_or_scalar(y_ref));
114 auto a_val = to_ref(as_value_column_array_or_scalar(a_ref));
115 auto v_val = to_ref(as_value_column_array_or_scalar(v_ref));
116 auto w_val = to_ref(as_value_column_array_or_scalar(w_ref));
117 auto t0_val = to_ref(as_value_column_array_or_scalar(t0_ref));
118 auto sv_val = to_ref(as_value_column_array_or_scalar(sv_ref));
119 auto sw_val = to_ref(as_value_column_array_or_scalar(sw_ref));
120 auto st0_val = to_ref(as_value_column_array_or_scalar(st0_ref));
121
122 if (!include_summand<propto, T_y, T_a, T_v, T_w, T_t0, T_sv, T_sw,
123 T_st0>::value) {
124 return ret_t(0.0);
125 }
126
127 static constexpr const char* function_name = "wiener_lccdf_defective";
128 check_consistent_sizes(function_name, "Random variable", y,
129 "Boundary separation", a, "Drift rate", v,
130 "A-priori bias", w, "Nondecision time", t0,
131 "Inter-trial variability in drift rate", sv,
132 "Inter-trial variability in A-priori bias", sw,
133 "Inter-trial variability in Nondecision time", st0);
134 check_positive_finite(function_name, "Random variable", y_val);
135 check_positive_finite(function_name, "Boundary separation", a_val);
136 check_finite(function_name, "Drift rate", v_val);
137 check_less(function_name, "A-priori bias", w_val, 1);
138 check_greater(function_name, "A-priori bias", w_val, 0);
139 check_nonnegative(function_name, "Nondecision time", t0_val);
140 check_finite(function_name, "Nondecision time", t0_val);
141 check_nonnegative(function_name, "Inter-trial variability in drift rate",
142 sv_val);
143 check_finite(function_name, "Inter-trial variability in drift rate", sv_val);
144 check_bounded(function_name, "Inter-trial variability in A-priori bias",
145 sw_val, 0, 1);
146 check_nonnegative(function_name,
147 "Inter-trial variability in Nondecision time", st0_val);
148 check_finite(function_name, "Inter-trial variability in Nondecision time",
149 st0_val);
150
151 const size_t N = max_size(y, a, v, w, t0, sv, sw, st0);
152 if (N == 0) {
153 return ret_t(0.0);
154 }
155 scalar_seq_view<T_y_ref> y_vec(y_ref);
156 scalar_seq_view<T_a_ref> a_vec(a_ref);
157 scalar_seq_view<T_v_ref> v_vec(v_ref);
158 scalar_seq_view<T_w_ref> w_vec(w_ref);
159 scalar_seq_view<T_t0_ref> t0_vec(t0_ref);
160 scalar_seq_view<T_sv_ref> sv_vec(sv_ref);
161 scalar_seq_view<T_sw_ref> sw_vec(sw_ref);
162 scalar_seq_view<T_st0_ref> st0_vec(st0_ref);
163 const size_t N_y_t0 = max_size(y, t0, st0);
164
165 for (size_t i = 0; i < N_y_t0; ++i) {
166 if (y_vec[i] <= t0_vec[i]) {
167 std::stringstream msg;
168 msg << ", but must be greater than nondecision time = " << t0_vec[i];
169 std::string msg_str(msg.str());
170 throw_domain_error(function_name, "Random variable", y_vec[i], " = ",
171 msg_str.c_str());
172 }
173 }
174 size_t N_beta_sw = max_size(w, sw);
175 for (size_t i = 0; i < N_beta_sw; ++i) {
176 if (unlikely(w_vec[i] - .5 * sw_vec[i] <= 0)) {
177 std::stringstream msg;
178 msg << ", but must be smaller than 2*(A-priori bias) = "
179 << 2.0 * w_vec[i];
180 std::string msg_str(msg.str());
181 throw_domain_error(function_name,
182 "Inter-trial variability in A-priori bias", sw_vec[i],
183 " = ", msg_str.c_str());
184 }
185 if (unlikely(w_vec[i] + .5 * sw_vec[i] >= 1)) {
186 std::stringstream msg;
187 msg << ", but must be smaller than 2*(1-A-priori bias) = "
188 << 2.0 * (1.0 - w_vec[i]);
189 std::string msg_str(msg.str());
190 throw_domain_error(function_name,
191 "Inter-trial variability in A-priori bias", sw_vec[i],
192 " = ", msg_str.c_str());
193 }
194 }
195
196 // for precs. 1e-6, 1e-12, see Hartmann et al. (2021), Henrich et al. (2023)
197 const T_partials_return log_error_cdf = log(1e-6); // precision for density
198 const auto error_bound = precision_derivatives; // precision for
199 // derivatives (controllable by user)
200 const auto log_error_derivatives = log(error_bound);
201 const T_partials_return absolute_error_hcubature = 0.0;
202 const T_partials_return relative_error_hcubature
203 = .9 * error_bound; // eps_rel(Integration)
204 const T_partials_return log_error_absolute = log(1e-12);
205 const int maximal_evaluations_hcubature = 6000;
206 T_partials_return lccdf = 0.0;
207 auto ops_partials = make_partials_propagator(y_ref, a_ref, t0_ref, w_ref,
208 v_ref, sv_ref, sw_ref, st0_ref);
209 ret_t result = 0.0;
210
211 // calculate density and partials
212 for (size_t i = 0; i < N; i++) {
213 if (sv_vec[i] == 0 && sw_vec[i] == 0 && st0_vec[i] == 0) {
214 result += wiener_lccdf_defective<propto>(y_vec[i], a_vec[i], t0_vec[i],
215 w_vec[i], v_vec[i],
216 precision_derivatives);
217 continue;
218 }
219 const T_partials_return y_value = y_vec.val(i);
220 const T_partials_return a_value = a_vec.val(i);
221 const T_partials_return v_value = v_vec.val(i);
222 const T_partials_return w_value = w_vec.val(i);
223 const T_partials_return t0_value = t0_vec.val(i);
224 const T_partials_return sv_value = sv_vec.val(i);
225 const T_partials_return sw_value = sw_vec.val(i);
226 const T_partials_return st0_value = st0_vec.val(i);
227 const int dim = (sv_value != 0) + (sw_value != 0) + (st0_value != 0);
228 check_positive(function_name,
229 "(Inter-trial variability in drift rate) + "
230 "(Inter-trial variability in A-priori bias) + "
231 "(Inter-trial variability in nondecision time)",
232 dim);
233
234 Eigen::Matrix<T_partials_return, -1, 1> xmin = Eigen::VectorXd::Zero(dim);
235 Eigen::Matrix<T_partials_return, -1, 1> xmax = Eigen::VectorXd::Ones(dim);
236 for (int i = 0; i < dim; i++) {
237 xmin[i] = 0;
238 xmax[i] = 1;
239 }
240 if (sv_value != 0) {
241 xmin[0] = -1;
242 xmax[0] = 1;
243 }
244 if (st0_value != 0) {
245 xmax[dim - 1] = fmin(1.0, (y_value - t0_value) / st0_value);
246 }
247
248 T_partials_return hcubature_err
249 = log_error_absolute - log_error_cdf + LOG_TWO + 1;
250
251 const auto params = std::make_tuple(y_value, a_value, v_value, w_value,
252 t0_value, sv_value, sw_value, st0_value,
253 log_error_absolute - LOG_TWO);
254
255 const T_partials_return ccdf
256 = internal::wiener7_integrate_cdf<GradientCalc::OFF, GradientCalc::OFF,
257 GradientCalc::OFF, GradientCalc::OFF,
258 GradientCalc::OFF, GradientCalc::OFF>(
259 [&](auto&&... args) { return internal::wiener4_ccdf(args...); },
260 hcubature_err, params, dim, xmin, xmax,
261 maximal_evaluations_hcubature, absolute_error_hcubature,
262 relative_error_hcubature / 2);
263 lccdf += log(ccdf);
264
265 hcubature_err = log_error_absolute - log_error_derivatives + log(fabs(ccdf))
266 + LOG_TWO + 1;
267
268 // computation of derivative for t and precision check in order to give
269 // the value as deriv_t to edge1 and as -deriv_t to edge5
270
271 // computation of derivatives and precision checks
273 const T_partials_return deriv_t_7
275 GradientCalc::OFF, GradientCalc::OFF, GradientCalc::OFF,
276 GradientCalc::OFF, GradientCalc::ON>(
277 [&](auto&&... args) {
278 return internal::wiener5_density<GradientCalc::ON>(args...);
279 },
280 hcubature_err, params, dim, xmin, xmax,
281 maximal_evaluations_hcubature, absolute_error_hcubature,
282 relative_error_hcubature / 2)
283 / ccdf;
285 partials<0>(ops_partials)[i] = deriv_t_7;
286 }
288 partials<2>(ops_partials)[i] = -deriv_t_7;
289 }
290 }
291 T_partials_return deriv;
293 partials<1>(ops_partials)[i]
295 [&](auto&&... args) {
296 return internal::wiener4_ccdf_grad_a(args...);
297 },
298 hcubature_err, params, dim, xmin, xmax,
299 maximal_evaluations_hcubature, absolute_error_hcubature,
300 relative_error_hcubature / 2)
301 / ccdf;
302 }
304 partials<3>(ops_partials)[i]
305 = internal::wiener7_integrate_cdf<GradientCalc::OFF,
306 GradientCalc::ON>(
307 [&](auto&&... args) {
308 return internal::wiener4_ccdf_grad_w(args...);
309 },
310 hcubature_err, params, dim, xmin, xmax,
311 maximal_evaluations_hcubature, absolute_error_hcubature,
312 relative_error_hcubature / 2)
313 / ccdf;
314 }
316 partials<4>(ops_partials)[i]
318 [&](auto&&... args) {
319 return internal::wiener4_ccdf_grad_v(args...);
320 },
321 hcubature_err, params, dim, xmin, xmax,
322 maximal_evaluations_hcubature, absolute_error_hcubature,
323 relative_error_hcubature / 2)
324 / ccdf;
325 }
327 if (sv_value == 0) {
328 partials<5>(ops_partials)[i] = 0.0;
329 } else {
330 partials<5>(ops_partials)[i]
332 GradientCalc::OFF, GradientCalc::OFF, GradientCalc::ON>(
333 [&](auto&&... args) {
334 return internal::wiener4_ccdf_grad_v(args...);
335 },
336 hcubature_err, params, dim, xmin, xmax,
337 maximal_evaluations_hcubature, absolute_error_hcubature,
338 relative_error_hcubature / 2)
339 / ccdf;
340 }
341 }
343 if (sw_value == 0) {
344 partials<6>(ops_partials)[i] = 0.0;
345 } else {
346 if (st0_value == 0 && sv_value == 0) {
347 deriv = internal::estimate_with_err_check<5, 0, GradientCalc::OFF,
348 GradientCalc::ON>(
349 [](auto&&... args) {
350 return internal::wiener7_ccdf_grad_sw(args...);
351 },
352 hcubature_err, y_value - t0_value, a_value, v_value, w_value,
353 sw_value, log_error_absolute - LOG_TWO);
354 deriv = deriv / ccdf - 1 / sw_value;
355 } else {
357 GradientCalc::OFF, GradientCalc::OFF, GradientCalc::OFF,
358 GradientCalc::ON>(
359 [&](auto&&... args) {
360 return internal::wiener4_ccdf_grad_w(args...);
361 },
362 hcubature_err, params, dim, xmin, xmax,
363 maximal_evaluations_hcubature, absolute_error_hcubature,
364 relative_error_hcubature / 2)
365 / ccdf;
366 }
367 partials<6>(ops_partials)[i] = deriv;
368 }
369 }
371 if (st0_value == 0) {
372 partials<7>(ops_partials)[i] = 0.0;
373 } else if (y_value - (t0_value + st0_value) <= 0) {
374 partials<7>(ops_partials)[i] = -1 / st0_value;
375 } else {
376 const auto t0_st0 = t0_value + st0_value;
377 if (sw_value == 0 && sv_value == 0) {
378 deriv = internal::estimate_with_err_check<4, 0>(
379 [](auto&&... args) { return internal::wiener4_ccdf(args...); },
380 log_error_derivatives + log(st0_value), y_value - t0_st0, a_value,
381 v_value, w_value, log_error_absolute - LOG_TWO);
382 deriv = deriv / st0_value / ccdf - 1 / st0_value;
383 } else {
384 const int dim_st = (sv_value != 0) + (sw_value != 0);
385 const auto new_error = log_error_absolute - LOG_TWO;
386 const auto& params_st
387 = std::make_tuple(y_value, a_value, v_value, w_value, t0_st0,
388 sv_value, sw_value, 0.0, new_error);
390 GradientCalc::OFF, GradientCalc::OFF, GradientCalc::OFF,
391 GradientCalc::OFF, GradientCalc::OFF, GradientCalc::OFF>(
392 [&](auto&&... args) { return internal::wiener4_ccdf(args...); },
393 hcubature_err, params_st, dim_st, xmin, xmax,
394 maximal_evaluations_hcubature, absolute_error_hcubature,
395 relative_error_hcubature / 2);
396 deriv = deriv / st0_value / ccdf - 1 / st0_value;
397 }
398 partials<7>(ops_partials)[i] = deriv;
399 }
400 }
401 }
402 return result + ops_partials.build(lccdf);
403}
404} // namespace math
405} // namespace stan
406#endif
scalar_seq_view provides a uniform sequence-like wrapper around either a scalar or a sequence of scal...
#define unlikely(x)
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
auto wiener4_ccdf_grad_w(const T_y &y, const T_a &a, const T_v &v, const T_w &w, T_cdf &&cdf, T_err log_err=log(1e-12)) noexcept
Calculate derivative of the wiener4 ccdf w.r.t.
auto wiener7_ccdf_grad_sw(const T_y &y, const T_a &a, const T_v &v, const T_w &w, const T_sw &sw, T_err log_error)
Calculate the derivative of the wiener7 density w.r.t.
auto estimate_with_err_check(F &&functor, T_err &&log_err, ArgsTupleT &&... args_tuple)
Utility function for estimating a function with a given set of arguments, checking the result against...
auto wiener4_ccdf(const T_y &y, const T_a &a, const T_v &v, const T_w &w, T_err log_err=log(1e-12)) noexcept
Calculate wiener4 ccdf (natural-scale)
auto wiener4_ccdf_grad_a(const T_y &y, const T_a &a, const T_v &v, const T_w &w, T_cdf &&cdf, T_err log_err=log(1e-12)) noexcept
Calculate derivative of the wiener4 ccdf w.r.t.
auto wiener7_integrate_cdf(const Wiener7FunctorT &wiener7_functor, T_err &&hcubature_err, TArgs &&... args)
Implementation function for preparing arguments and functor to be passed to the hcubature() function ...
auto wiener4_ccdf_grad_v(const T_y &y, const T_a &a, const T_v &v, const T_w &w, T_cdf &&cdf, T_err log_err=log(1e-12)) noexcept
Calculate derivative of the wiener4 ccdf w.r.t.
void check_nonnegative(const char *function, const char *name, const T_y &y)
Check if y is non-negative.
fvar< T > fmin(const fvar< T > &x1, const fvar< T > &x2)
Definition fmin.hpp:14
void check_bounded(const char *function, const char *name, const T_y &y, const T_low &low, const T_high &high)
Check if the value is between the low and high values, inclusively.
static constexpr double e()
Return the base of the natural logarithm.
Definition constants.hpp:20
fvar< T > log(const fvar< T > &x)
Definition log.hpp:18
void throw_domain_error(const char *function, const char *name, const T &y, const char *msg1, const char *msg2)
Throw a domain error with a consistently formatted message.
static constexpr double LOG_TWO
The natural logarithm of 2, .
Definition constants.hpp:80
auto as_value_column_array_or_scalar(T &&a)
Extract the value from an object and for eigen vectors and std::vectors convert to an eigen column ar...
void check_consistent_sizes(const char *)
Trivial no input case, this function is a no-op.
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
fvar< T > fmax(const fvar< T > &x1, const fvar< T > &x2)
Return the greater of the two specified arguments.
Definition fmax.hpp:23
void check_positive(const char *function, const char *name, const T_y &y)
Check if y is positive.
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
Definition to_ref.hpp:18
void check_less(const char *function, const char *name, const T_y &y, const T_high &high, Idxs... idxs)
Throw an exception if y is not strictly less than high.
int64_t max_size(const T1 &x1, const Ts &... xs)
Calculate the size of the largest input.
Definition max_size.hpp:20
auto wiener_lccdf_defective(const T_y &y, const T_a &a, const T_t0 &t0, const T_w &w, const T_v &v, const double &precision_derivatives=1e-4)
Log-CCDF for the 4-parameter Wiener distribution.
void check_greater(const char *function, const char *name, const T_y &y, const T_low &low, Idxs... idxs)
Throw an exception if y is not strictly greater than low.
auto make_partials_propagator(Ops &&... ops)
Construct an partials_propagator.
void check_positive_finite(const char *function, const char *name, const T_y &y)
Check if y is positive and finite.
fvar< T > fabs(const fvar< T > &x)
Definition fabs.hpp:16
typename ref_type_if< true, T >::type ref_type_t
Definition ref_type.hpp:56
typename partials_return_type< Args... >::type partials_return_t
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
Extends std::true_type when instantiated with zero or more template parameters, all of which extend t...
Template metaprogram to calculate whether a summand needs to be included in a proportional (log) prob...