diff --git a/stan/math/prim/fun/inv_logit.hpp b/stan/math/prim/fun/inv_logit.hpp index efd69f53a34..08f2a940a7f 100644 --- a/stan/math/prim/fun/inv_logit.hpp +++ b/stan/math/prim/fun/inv_logit.hpp @@ -49,15 +49,14 @@ namespace math { * @return Inverse logit of argument. */ inline double inv_logit(double a) { - using std::exp; if (a < 0) { - double exp_a = exp(a); + double exp_a = std::exp(a); if (a < LOG_EPSILON) { return exp_a; } - return exp_a / (1 + exp_a); + return exp_a / (1.0 + exp_a); } - return inv(1 + exp(-a)); + return inv(1 + std::exp(-a)); } /** @@ -69,28 +68,46 @@ inline double inv_logit(double a) { */ struct inv_logit_fun { template - static inline auto fun(const T& x) { - return inv_logit(x); + static inline auto fun(T&& x) { + return inv_logit(std::forward(x)); } }; /** - * Vectorized version of inv_logit(). + * Vectorized version of inv_logit() for containers containing ad types. * - * @tparam T type of container - * @param x container + * @tparam T type of std::vector + * @param x std::vector * @return Inverse logit applied to each value in x. */ -template < - typename T, require_not_var_matrix_t* = nullptr, - require_all_not_nonscalar_prim_or_rev_kernel_expression_t* = nullptr> -inline auto inv_logit(const T& x) { - return apply_scalar_unary::apply(x); +template * = nullptr, + require_all_not_nonscalar_prim_or_rev_kernel_expression_t< + Container>* = nullptr, + require_not_rev_matrix_t* = nullptr> +inline auto inv_logit(Container&& x) { + return apply_scalar_unary::apply( + std::forward(x)); } -// TODO(Tadej): Eigen is introducing their implementation logistic() of this -// in 3.4. Use that once we switch to Eigen 3.4 - +/** + * Vectorized version of inv_logit() for containers with arithmetic scalar + * types. + * + * @tparam T A type of either `std::vector` or a type that directly inherits + * from `Eigen::DenseBase`. The inner scalar type must not have an auto diff + * scalar type. + * @param x Eigen expression + * @return Inverse logit applied to each value in x. + */ +template * = nullptr, + require_all_not_nonscalar_prim_or_rev_kernel_expression_t< + Container>* = nullptr> +inline auto inv_logit(Container&& x) { + return apply_vector_unary::apply( + std::forward(x), + [](const auto& v) { return v.array().logistic(); }); +} } // namespace math } // namespace stan diff --git a/stan/math/prim/functor/apply_scalar_unary.hpp b/stan/math/prim/functor/apply_scalar_unary.hpp index e316d3033e5..2cbcae179aa 100644 --- a/stan/math/prim/functor/apply_scalar_unary.hpp +++ b/stan/math/prim/functor/apply_scalar_unary.hpp @@ -58,7 +58,7 @@ struct apply_scalar_unary> { * @return Componentwise application of the function specified * by F to the specified matrix. */ - static inline auto apply(const T& x) { + static inline auto apply(const std::decay_t& x) { return x.unaryExpr([](auto&& x) { return apply_scalar_unary>::apply(x); }); @@ -69,7 +69,7 @@ struct apply_scalar_unary> { * expression template of type T. */ using return_t = std::decay_t::apply(std::declval()))>; + apply_scalar_unary>::apply(std::declval()))>; }; /** @@ -83,7 +83,8 @@ struct apply_scalar_unary> { /** * The return type, double. */ - using return_t = std::decay_t()))>; + using return_t + = std::decay_t>()))>; /** * Apply the function specified by F to the specified argument. @@ -114,11 +115,12 @@ struct apply_scalar_unary> { * @param x Argument scalar. * @return Result of applying F to the scalar. */ - static inline auto apply(const T& x) { return F::fun(x); } + static inline auto apply(const std::decay_t& x) { return F::fun(x); } /** * The return type */ - using return_t = std::decay_t()))>; + using return_t + = std::decay_t>()))>; }; /** @@ -157,13 +159,13 @@ struct apply_scalar_unary> { * @tparam T Type of element contained in standard vector. */ template -struct apply_scalar_unary> { +struct apply_scalar_unary> { /** * Return type, which is calculated recursively as a standard * vector of the return type of the contained type T. */ - using return_t = typename std::vector< - plain_type_t::return_t>>; + using return_t = typename std::vector>>::return_t>>; /** * Apply the function specified by F elementwise to the @@ -174,10 +176,10 @@ struct apply_scalar_unary> { * @return Elementwise application of F to the elements of the * container. */ - static inline auto apply(const std::vector& x) { + static inline auto apply(const std::decay_t& x) { return_t fx(x.size()); for (size_t i = 0; i < x.size(); ++i) { - fx[i] = apply_scalar_unary::apply(x[i]); + fx[i] = apply_scalar_unary>::apply(x[i]); } return fx; } diff --git a/stan/math/rev/fun/inv_logit.hpp b/stan/math/rev/fun/inv_logit.hpp index 086b5f45bc9..ffef93a0256 100644 --- a/stan/math/rev/fun/inv_logit.hpp +++ b/stan/math/rev/fun/inv_logit.hpp @@ -31,6 +31,31 @@ inline auto inv_logit(const var_value& a) { }); } +/** + * The inverse logit function for Eigen expressions with var value type. + * + * See inv_logit() for the double-based version. + * + * The derivative of inverse logit is + * + * \f$\frac{d}{dx} \mbox{logit}^{-1}(x) = \mbox{logit}^{-1}(x) (1 - + * \mbox{logit}^{-1}(x))\f$. + * + * @tparam T type of Eigen expression + * @param x Eigen expression + * @return Inverse logit of argument. + */ +template * = nullptr> +inline auto inv_logit(T&& x) { + auto x_arena = to_arena(std::forward(x)); + arena_t ret = inv_logit(x_arena.val()); + reverse_pass_callback([x_arena, ret]() mutable { + x_arena.adj().array() + += ret.adj().array() * ret.val().array() * (1.0 - ret.val().array()); + }); + return ret; +} + } // namespace math } // namespace stan #endif