1#ifndef STAN_MATH_REV_CONSTRAINT_LUB_CONSTRAIN_HPP
2#define STAN_MATH_REV_CONSTRAINT_LUB_CONSTRAIN_HPP
33template <
typename T,
typename L,
typename U,
34 require_all_stan_scalar_t<T, L, U>* =
nullptr,
35 require_var_t<return_type_t<T, L, U>>* =
nullptr>
36inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub) {
41 const bool is_ub_inf = ub_val ==
INFTY;
42 if (
unlikely(is_ub_inf && is_lb_inf)) {
49 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
50 auto diff = ub_val - lb_val;
53 diff * inv_logit_x + lb_val,
54 [x, ub, lb, diff, inv_logit_x](
auto& vi)
mutable {
55 if (!is_constant<T>::value) {
56 forward_as<var>(x).adj()
57 += vi.adj() * diff * inv_logit_x * (1.0 - inv_logit_x);
59 if (!is_constant<L>::value) {
60 forward_as<var>(lb).adj() += vi.adj() * (1.0 - inv_logit_x);
62 if (!is_constant<U>::value) {
63 forward_as<var>(ub).adj() += vi.adj() * inv_logit_x;
102template <
typename T,
typename L,
typename U,
103 require_all_stan_scalar_t<T, L, U>* =
nullptr,
104 require_var_t<return_type_t<T, L, U>>* =
nullptr>
105inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub,
106 return_type_t<T, L, U>& lp) {
111 const bool is_ub_inf = ub_val ==
INFTY;
112 if (
unlikely(is_ub_inf && is_lb_inf)) {
119 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
121 auto diff = ub_val - lb_val;
123 lp += (
log(diff) + (neg_abs_x - (2.0 *
log1p_exp(neg_abs_x))));
125 diff * inv_logit_x + lb_val,
126 [x, ub, lb, diff, lp, inv_logit_x](
auto& vi)
mutable {
127 if (!is_constant<T>::value) {
128 forward_as<var>(x).adj()
129 += vi.adj() * diff * inv_logit_x * (1.0 - inv_logit_x)
130 + lp.adj() * (1.0 - 2.0 * inv_logit_x);
132 if (!is_constant<L>::value && !is_constant<U>::value) {
133 const auto one_over_diff = 1.0 / diff;
134 forward_as<var>(lb).adj()
135 += vi.adj() * (1.0 - inv_logit_x) + -one_over_diff * lp.adj();
136 forward_as<var>(ub).adj()
137 += vi.adj() * inv_logit_x + one_over_diff * lp.adj();
138 }
else if (!is_constant<L>::value) {
139 forward_as<var>(lb).adj()
140 += vi.adj() * (1.0 - inv_logit_x) + (-1.0 / diff) * lp.adj();
141 }
else if (!is_constant<U>::value) {
142 forward_as<var>(ub).adj()
143 += vi.adj() * inv_logit_x + (1.0 / diff) * lp.adj();
152template <
typename T,
typename L,
typename U, require_matrix_t<T>* =
nullptr,
153 require_all_stan_scalar_t<L, U>* =
nullptr,
154 require_var_t<return_type_t<T, L, U>>* =
nullptr>
155inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub) {
157 using ret_type = return_var_matrix_t<T, T, L, U>;
161 const bool is_ub_inf = ub_val ==
INFTY;
162 if (
unlikely(is_ub_inf && is_lb_inf)) {
169 arena_t<T> arena_x = x;
170 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
171 const auto diff = ub_val - lb_val;
173 arena_t<ret_type> ret = diff * inv_logit_x + lb_val;
175 if (!is_constant<T>::value) {
176 using T_var = arena_t<promote_scalar_t<var, T>>;
177 forward_as<T_var>(arena_x).adj().array()
178 += ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x);
180 if (!is_constant<L>::value) {
181 forward_as<var>(lb).adj()
182 += (ret.adj().array() * (1.0 - inv_logit_x)).sum();
184 if (!is_constant<U>::value) {
185 forward_as<var>(ub).adj() += (ret.adj().array() * inv_logit_x).
sum();
188 return ret_type(ret);
195template <
typename T,
typename L,
typename U, require_matrix_t<T>* =
nullptr,
196 require_all_stan_scalar_t<L, U>* =
nullptr,
197 require_var_t<return_type_t<T, L, U>>* =
nullptr>
198inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub,
199 return_type_t<T, L, U>& lp) {
201 using ret_type = return_var_matrix_t<T, T, L, U>;
205 const bool is_ub_inf = ub_val ==
INFTY;
206 if (
unlikely(is_ub_inf && is_lb_inf)) {
213 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
214 arena_t<T> arena_x = x;
216 auto diff = ub_val - lb_val;
219 arena_t<ret_type> ret = diff * inv_logit_x + lb_val;
221 [arena_x, ub, lb, ret, lp, diff, inv_logit_x]()
mutable {
222 if (!is_constant<T>::value) {
223 forward_as<arena_t<promote_scalar_t<var, T>>>(arena_x).adj().array()
224 += ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x)
225 + lp.adj() * (1.0 - 2.0 * inv_logit_x);
227 if (!is_constant<L>::value && !is_constant<U>::value) {
228 const auto lp_calc = lp.adj() * ret.size();
229 const auto one_over_diff = 1.0 / diff;
230 forward_as<var>(lb).adj()
231 += (ret.adj().array() * (1.0 - inv_logit_x)).sum()
232 + -one_over_diff * lp_calc;
233 forward_as<var>(ub).adj() += (ret.adj().array() * inv_logit_x).
sum()
234 + one_over_diff * lp_calc;
235 }
else if (!is_constant<L>::value) {
236 forward_as<var>(lb).adj()
237 += (ret.adj().array() * (1.0 - inv_logit_x)).sum()
238 + -(1.0 / diff) * lp.adj() * ret.size();
239 }
else if (!is_constant<U>::value) {
240 forward_as<var>(ub).adj() += (ret.adj().array() * inv_logit_x).
sum()
241 + (1.0 / diff) * lp.adj() * ret.size();
244 return ret_type(ret);
252template <
typename T,
typename L,
typename U,
253 require_all_matrix_t<T, L>* =
nullptr,
254 require_stan_scalar_t<U>* =
nullptr,
255 require_var_t<return_type_t<T, L, U>>* =
nullptr>
256inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub) {
258 using ret_type = return_var_matrix_t<T, T, L, U>;
260 const bool is_ub_inf = ub_val ==
INFTY;
264 arena_t<T> arena_x = x;
265 arena_t<L> arena_lb = lb;
266 const auto lb_val =
value_of(arena_lb).array().eval();
267 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
269 auto diff =
to_arena(ub_val - lb_val);
271 arena_t<ret_type> ret = (is_lb_inf).
select(
272 ub_val -
value_of(arena_x).array().exp(), diff * inv_logit_x + lb_val);
274 is_lb_inf]()
mutable {
275 using T_var = arena_t<promote_scalar_t<var, T>>;
276 using L_var = arena_t<promote_scalar_t<var, L>>;
277 if (!is_constant<T>::value) {
278 forward_as<T_var>(arena_x).adj().array() += (is_lb_inf).
select(
279 ret.adj().array() * -
value_of(arena_x).array().exp(),
280 ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x));
282 if (!is_constant<U>::value) {
283 forward_as<var>(ub).adj()
285 .
select(ret.adj().array(), ret.adj().array() * inv_logit_x)
288 if (!is_constant<L>::value) {
289 forward_as<L_var>(arena_lb).adj().array()
290 += (is_lb_inf).
select(0, ret.adj().array() * (1.0 - inv_logit_x));
293 return ret_type(ret);
301template <
typename T,
typename L,
typename U,
302 require_all_matrix_t<T, L>* =
nullptr,
303 require_stan_scalar_t<U>* =
nullptr,
304 require_var_t<return_type_t<T, L, U>>* =
nullptr>
310 const bool is_ub_inf = ub_val ==
INFTY;
317 const auto lb_val =
value_of(arena_lb).array().eval();
318 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
320 auto diff =
to_arena(ub_val - lb_val);
321 auto neg_abs_x =
to_arena(-arena_x_val.abs());
324 diff * inv_logit_x + lb_val);
330 inv_logit_x, is_lb_inf]()
mutable {
333 const auto lp_adj = lp.adj();
335 const auto x_sign = arena_x_val.sign().
eval();
336 forward_as<T_var>(arena_x).adj().array() += (is_lb_inf).
select(
337 ret.adj().array() * -arena_x_val.exp() + lp_adj,
338 ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x)
339 + lp.adj() * (1.0 - 2.0 * inv_logit_x));
342 forward_as<L_var>(arena_lb).adj().array()
343 += (is_lb_inf).
select(0, ret.adj().array() * (1.0 - inv_logit_x)
344 + -(1.0 / diff) * lp_adj);
347 forward_as<var>(ub).adj()
349 .
select(ret.adj().array(), ret.adj().array() * inv_logit_x
350 + (1.0 / diff) * lp_adj)
354 return ret_type(ret);
362template <
typename T,
typename L,
typename U,
366inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub) {
374 arena_t<T> arena_x = x;
376 arena_t<U> arena_ub = ub;
377 const auto ub_val =
value_of(arena_ub).array().eval();
378 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
380 auto diff =
to_arena(ub_val - lb_val);
382 arena_t<ret_type> ret = (is_ub_inf).
select(
383 arena_x_val.array().exp() + lb_val, diff * inv_logit_x + lb_val);
385 inv_logit_x, diff]()
mutable {
386 using T_var = arena_t<promote_scalar_t<var, T>>;
387 using U_var = arena_t<promote_scalar_t<var, U>>;
388 if (!is_constant<T>::value) {
389 forward_as<T_var>(arena_x).adj().array() += (is_ub_inf).
select(
390 ret.adj().array() * arena_x_val.array().exp(),
391 ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x));
393 if (!is_constant<L>::value) {
394 forward_as<var>(lb).adj()
396 .
select(ret.adj().array(),
397 ret.adj().array() * (1.0 - inv_logit_x))
400 if (!is_constant<U>::value) {
401 forward_as<U_var>(arena_ub).adj().array()
402 += (is_ub_inf).
select(0, ret.adj().array() * inv_logit_x);
405 return ret_type(ret);
413template <
typename T,
typename L,
typename U,
414 require_all_matrix_t<T, U>* =
nullptr,
415 require_stan_scalar_t<L>* =
nullptr,
416 require_var_t<return_type_t<T, L, U>>* =
nullptr>
417inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub,
418 std::decay_t<return_type_t<T, L, U>>& lp) {
420 using ret_type = return_var_matrix_t<T, T, L, U>;
426 arena_t<T> arena_x = x;
428 arena_t<U> arena_ub = ub;
430 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
432 auto diff =
to_arena(ub_val.array() - lb_val);
433 auto neg_abs_x =
to_arena(-(arena_x_val.array()).abs());
435 .
select(arena_x_val.array(),
439 arena_t<ret_type> ret = (is_ub_inf).
select(
440 arena_x_val.array().exp() + lb_val, diff * inv_logit_x + lb_val);
442 lb, ret, lp, is_ub_inf]()
mutable {
443 using T_var = arena_t<promote_scalar_t<var, T>>;
444 using U_var = arena_t<promote_scalar_t<var, U>>;
445 const auto lp_adj = lp.adj();
446 if (!is_constant<T>::value) {
447 forward_as<T_var>(arena_x).adj().array() += (is_ub_inf).
select(
448 ret.adj().array() * arena_x_val.array().exp() + lp_adj,
449 ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x)
450 + lp.adj() * (1.0 - 2.0 * inv_logit_x));
452 if (!is_constant<L>::value) {
453 forward_as<var>(lb).adj()
455 .
select(ret.adj().array(),
456 ret.adj().array() * (1.0 - inv_logit_x)
457 + -(1.0 / diff) * lp_adj)
460 if (!is_constant<U>::value) {
461 forward_as<U_var>(arena_ub).adj().array() += (is_ub_inf).
select(
462 0, ret.adj().array() * inv_logit_x + (1.0 / diff) * lp_adj);
465 return ret_type(ret);
472template <
typename T,
typename L,
typename U,
473 require_all_matrix_t<T, L, U>* =
nullptr,
474 require_var_t<return_type_t<T, L, U>>* =
nullptr>
475inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub) {
477 using ret_type = return_var_matrix_t<T, T, L, U>;
478 arena_t<T> arena_x = x;
479 auto arena_x_val =
value_of(arena_x);
480 arena_t<L> arena_lb = lb;
481 arena_t<U> arena_ub = ub;
482 auto lb_val =
value_of(arena_lb).array();
483 auto ub_val =
value_of(arena_ub).array();
484 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
488 auto is_lb_ub_inf =
to_arena(is_lb_inf && is_ub_inf);
489 auto diff =
to_arena(ub_val - lb_val);
491 arena_t<ret_type> ret
493 .
select(arena_x_val.array(),
495 ub_val - arena_x.val().array().exp(),
496 (is_ub_inf).
select(arena_x_val.array().exp() + lb_val,
497 diff * inv_logit_x + lb_val)));
499 diff, ret, is_ub_inf, is_lb_inf,
500 is_lb_ub_inf]()
mutable {
501 using T_var = arena_t<promote_scalar_t<var, T>>;
502 using L_var = arena_t<promote_scalar_t<var, L>>;
503 using U_var = arena_t<promote_scalar_t<var, U>>;
505 const bool is_none_inf = !(is_lb_inf.any() || is_ub_inf.any());
507 if (!is_constant<T>::value) {
508 forward_as<T_var>(arena_x).adj().array()
509 += ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x);
511 if (!is_constant<L>::value) {
512 forward_as<L_var>(arena_lb).adj().array()
513 += ret.adj().array() * (1.0 - inv_logit_x);
515 if (!is_constant<U>::value) {
516 forward_as<U_var>(arena_ub).adj().array()
517 += ret.adj().array() * inv_logit_x;
520 if (!is_constant<T>::value) {
521 forward_as<T_var>(arena_x).adj().array()
526 ret.adj().array() * -arena_x_val.array().exp(),
528 ret.adj().array() * arena_x_val.array().exp(),
529 ret.adj().array() * diff * inv_logit_x
530 * (1.0 - inv_logit_x))));
532 if (!is_constant<U>::value) {
533 forward_as<U_var>(arena_ub).adj().array() += (is_ub_inf).
select(
534 0, (is_lb_inf).select(ret.adj().array(),
535 ret.adj().array() * inv_logit_x));
537 if (!is_constant<L>::value) {
538 forward_as<L_var>(arena_lb).adj().array() += (is_lb_inf).
select(
539 0, (is_ub_inf).select(ret.adj().array(),
540 ret.adj().array() * (1.0 - inv_logit_x)));
544 return ret_type(ret);
550template <
typename T,
typename L,
typename U,
551 require_all_matrix_t<T, L, U>* =
nullptr,
552 require_var_t<return_type_t<T, L, U>>* =
nullptr>
553inline auto lub_constrain(
const T& x,
const L& lb,
const U& ub,
554 return_type_t<T, L, U>& lp) {
556 using ret_type = return_var_matrix_t<T, T, L, U>;
557 arena_t<T> arena_x = x;
558 auto arena_x_val =
value_of(arena_x);
559 arena_t<L> arena_lb = lb;
560 arena_t<U> arena_ub = ub;
561 auto lb_val =
value_of(arena_lb).array();
562 auto ub_val =
value_of(arena_ub).array();
563 check_less(
"lub_constrain",
"lb", lb_val, ub_val);
567 auto is_lb_ub_inf =
to_arena(is_lb_inf && is_ub_inf);
568 auto diff =
to_arena(ub_val - lb_val);
570 arena_t<ret_type> ret
572 .
select(arena_x_val.array(),
574 ub_val - arena_x.val().array().exp(),
575 (is_ub_inf).
select(arena_x_val.array().exp() + lb_val,
576 diff * inv_logit_x + lb_val)));
577 auto neg_abs_x =
to_arena(-(arena_x_val.array()).abs());
581 (is_lb_inf || is_ub_inf)
584 log(diff) + (neg_abs_x - (2.0 *
log1p_exp(neg_abs_x)))))
587 diff, ret, is_ub_inf, is_lb_inf, is_lb_ub_inf,
589 using T_var = arena_t<promote_scalar_t<var, T>>;
590 using L_var = arena_t<promote_scalar_t<var, L>>;
591 using U_var = arena_t<promote_scalar_t<var, U>>;
592 const auto lp_adj = lp.adj();
594 const bool is_none_inf = !(is_lb_inf.any() || is_ub_inf.any());
596 if (!is_constant<T>::value) {
597 forward_as<T_var>(arena_x).adj().array()
598 += ret.adj().array() * diff * inv_logit_x * (1.0 - inv_logit_x)
599 + lp.adj() * (1.0 - 2.0 * inv_logit_x);
601 if (!is_constant<L>::value) {
602 forward_as<L_var>(arena_lb).adj().array()
603 += ret.adj().array() * (1.0 - inv_logit_x) + -(1.0 / diff) * lp_adj;
605 if (!is_constant<U>::value) {
606 forward_as<U_var>(arena_ub).adj().array()
607 += ret.adj().array() * inv_logit_x + (1.0 / diff) * lp_adj;
610 if (!is_constant<T>::value) {
611 forward_as<T_var>(arena_x).adj().array()
616 ret.adj().array() * -arena_x_val.array().exp()
619 ret.adj().array() * arena_x_val.array().exp()
621 ret.adj().array() * diff * inv_logit_x
622 * (1.0 - inv_logit_x)
623 + lp.adj() * (1.0 - 2.0 * inv_logit_x))));
625 if (!is_constant<L>::value) {
626 forward_as<L_var>(arena_lb).adj().array() += (is_lb_inf).
select(
627 0, (is_ub_inf).select(ret.adj().array(),
628 ret.adj().array() * (1.0 - inv_logit_x)
629 + -(1.0 / diff) * lp_adj));
631 if (!is_constant<U>::value) {
632 forward_as<U_var>(arena_ub).adj().array() += (is_ub_inf).
select(
633 0, (is_lb_inf).select(
635 ret.adj().array() * inv_logit_x + (1.0 / diff) * lp_adj));
639 return ret_type(ret);
require_all_t< is_matrix< std::decay_t< Types > >... > require_all_matrix_t
Require all of the types satisfy is_matrix.
select_< as_operation_cl_t< T_condition >, as_operation_cl_t< T_then >, as_operation_cl_t< T_else > > select(T_condition &&condition, T_then &&then, T_else &&els)
Selection operation on kernel generator expressions.
require_t< is_stan_scalar< std::decay_t< T > > > require_stan_scalar_t
Require type satisfies is_stan_scalar.
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
require_t< is_var< std::decay_t< T > > > require_var_t
Require type satisfies is_var.
fvar< T > abs(const fvar< T > &x)
var_value< plain_type_t< T > > make_callback_var(T &&value, F &&functor)
Creates a new var initialized with a callback_vari with a given value and reverse-pass callback funct...
void reverse_pass_callback(F &&functor)
Puts a callback on the autodiff stack to be called in reverse pass.
T eval(T &&arg)
Inputs which have a plain_type equal to the own time are forwarded unmodified (for Eigen expressions ...
T value_of(const fvar< T > &v)
Return the value of the specified variable.
fvar< T > log(const fvar< T > &x)
static constexpr double NEGATIVE_INFTY
Negative infinity.
fvar< T > log1p_exp(const fvar< T > &x)
arena_t< T > to_arena(const T &a)
Converts given argument into a type that either has any dynamic allocation on AD stack or schedules i...
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
auto lb_constrain(T &&x, L &&lb)
Return the lower-bounded value for the specified unconstrained input and specified lower bound.
auto sum(const std::vector< T > &m)
Return the sum of the entries of the specified standard vector.
auto ub_constrain(T &&x, U &&ub)
Return the upper-bounded value for the specified unconstrained matrix and upper bound.
matrix_cl< double > lub_constrain(const T &x, const L &lb, const U &ub)
Return the lower and upper-bounded matrix derived by transforming the specified free matrix given the...
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.
fvar< T > inv_logit(const fvar< T > &x)
Returns the inverse logit function applied to the argument.
auto identity_constrain(T &&x, Types &&...)
Returns the result of applying the identity constraint transform to the input.
static constexpr double INFTY
Positive infinity.
typename internal::arena_type_impl< std::decay_t< T > >::type arena_t
Determines a type that can be used in place of T that does any dynamic allocations on the AD stack.
std::conditional_t< is_any_var_matrix< ReturnType, Types... >::value, stan::math::var_value< stan::math::promote_scalar_t< double, plain_type_t< ReturnType > > >, stan::math::promote_scalar_t< stan::math::var_value< double >, plain_type_t< ReturnType > > > return_var_matrix_t
Given an Eigen type and several inputs, determine if a matrix should be var<Matrix> or Matrix<var>.
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...
Metaprogramming struct to detect whether a given type is constant in the mathematical sense (not the ...