Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion stan/math/fwd/fun/Phi_approx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <stan/math/fwd/fun/inv_logit.hpp>
#include <stan/math/fwd/fun/pow.hpp>
#include <stan/math/prim/fun/Phi_approx.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <cmath>

namespace stan {
Expand All @@ -23,7 +24,8 @@ namespace math {
*/
template <typename T>
inline fvar<T> Phi_approx(const fvar<T>& x) {
return inv_logit(0.07056 * pow(x, 3.0) + 1.5976 * x);
return inv_logit(PHI_APPROX_CUBIC_COEF * pow(x, 3.0)
+ PHI_APPROX_LINEAR_COEF * x);
}

} // namespace math
Expand Down
5 changes: 3 additions & 2 deletions stan/math/fwd/fun/inv_logit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ namespace math {
*/
template <typename T, require_fvar_t<T>* = nullptr>
inline auto inv_logit(T&& x) {
return std::decay_t<T>(inv_logit(x.val_),
x.d_ * inv_logit(x.val_) * (1 - inv_logit(x.val_)));
const auto val = inv_logit(x.val_);
// inv_logit(-x) rather than 1 - inv_logit(x), which cancels for x >> 0
return std::decay_t<T>(val, x.d_ * val * inv_logit(-x.val_));
}

} // namespace math
Expand Down
11 changes: 6 additions & 5 deletions stan/math/fwd/fun/log_inv_logit_diff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ namespace math {
\f]

\f[
\frac{\partial }{\partial x} = -\frac{e^x}{e^y-e^x}-\frac{e^x}{e^x+1}
\frac{\partial}{\partial x} = \mbox{logit}^{-1}(-x) + \frac{1}{e^{x-y}-1}
\f]

\f[
\frac{\partial }{\partial x} = -\frac{e^y}{e^x-e^y}-\frac{e^y}{e^y+1}
\frac{\partial}{\partial y} = -\mbox{logit}^{-1}(y) - \frac{1}{e^{x-y}-1}
\f]
*
* @tparam T inner type of the fvar
Expand All @@ -36,16 +36,17 @@ namespace math {
*/
template <typename T>
inline fvar<T> log_inv_logit_diff(const fvar<T>& x, const fvar<T>& y) {
// both partials as sums of positive terms, no cancellation for x >> 0
const auto c = inv(expm1(x.val_ - y.val_));
return fvar<T>(
log_inv_logit_diff(x.val_, y.val_),
-x.d_ * (inv(expm1(y.val_ - x.val_)) + inv_logit(x.val_))
- y.d_ * (inv(expm1(x.val_ - y.val_)) + inv_logit(y.val_)));
x.d_ * (c + inv_logit(-x.val_)) - y.d_ * (c + inv_logit(y.val_)));
}

template <typename T>
inline fvar<T> log_inv_logit_diff(const fvar<T>& x, double y) {
return fvar<T>(log_inv_logit_diff(x.val_, y),
-x.d_ * (inv(expm1(y - x.val_)) + inv_logit(x.val_)));
x.d_ * (inv(expm1(x.val_ - y)) + inv_logit(-x.val_)));
}

template <typename T>
Expand Down
4 changes: 0 additions & 4 deletions stan/math/opencl/kernel_generator/elt_function_cl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <stan/math/opencl/kernels/device_functions/logit.hpp>
#include <stan/math/opencl/kernels/device_functions/multiply_log.hpp>
#include <stan/math/opencl/kernels/device_functions/Phi.hpp>
#include <stan/math/opencl/kernels/device_functions/Phi_approx.hpp>
#include <stan/math/opencl/kernels/device_functions/std_normal_lcdf.hpp>
#include <stan/math/opencl/kernels/device_functions/trigamma.hpp>
#include <stan/math/opencl/matrix_cl_view.hpp>
Expand Down Expand Up @@ -314,9 +313,6 @@ ADD_UNARY_FUNCTION_WITH_INCLUDES(inv_logit,
ADD_UNARY_FUNCTION_WITH_INCLUDES(logit, opencl_kernels::log1m_device_function,
opencl_kernels::logit_device_function)
ADD_UNARY_FUNCTION_WITH_INCLUDES(Phi, opencl_kernels::phi_device_function)
ADD_UNARY_FUNCTION_WITH_INCLUDES(Phi_approx,
opencl_kernels::inv_logit_device_function,
opencl_kernels::phi_approx_device_function)
ADD_UNARY_FUNCTION_WITH_INCLUDES(
std_normal_lcdf_scaled_impl,
opencl_kernels::std_normal_lcdf_device_function)
Expand Down
35 changes: 0 additions & 35 deletions stan/math/opencl/kernels/device_functions/Phi_approx.hpp

This file was deleted.

1 change: 1 addition & 0 deletions stan/math/opencl/prim.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@

#include <stan/math/opencl/prim_constraint.hpp>

#include <stan/math/opencl/prim/Phi_approx.hpp>
#include <stan/math/opencl/prim/add_diag.hpp>
#include <stan/math/opencl/prim/append_array.hpp>
#include <stan/math/opencl/prim/bernoulli_cdf.hpp>
Expand Down
38 changes: 38 additions & 0 deletions stan/math/opencl/prim/Phi_approx.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef STAN_MATH_OPENCL_PRIM_PHI_APPROX_HPP
#define STAN_MATH_OPENCL_PRIM_PHI_APPROX_HPP
#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_generator.hpp>
#include <stan/math/opencl/ref_type.hpp>
#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <stan/math/prim/fun/to_ref.hpp>

namespace stan {
namespace math {

/**
* Return the elementwise logistic approximation to the standard normal CDF,
* `inv_logit(PHI_APPROX_CUBIC_COEF * x^3 + PHI_APPROX_LINEAR_COEF * x)`, of a
* kernel generator expression.
*
* @tparam T type of the kernel generator expression
* @param x argument
* @return `Phi_approx` of each element of x
*/
template <typename T,
require_all_kernel_expressions_and_none_scalar_t<T>* = nullptr>
inline auto Phi_approx(T&& x) {
return make_holder_cl(
[](auto&& a) {
return inv_logit(PHI_APPROX_CUBIC_COEF * pow(a, 3.0)
+ PHI_APPROX_LINEAR_COEF * a);
},
to_ref(std::forward<T>(x)));
}

} // namespace math
} // namespace stan

#endif
#endif
10 changes: 8 additions & 2 deletions stan/math/opencl/rev/Phi_approx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_generator.hpp>
#include <stan/math/opencl/prim/Phi_approx.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/value_of.hpp>
#include <stan/math/prim/fun/constants.hpp>

namespace stan {
namespace math {
Expand All @@ -20,9 +22,13 @@ template <typename T,
inline var_value<matrix_cl<double>> Phi_approx(const var_value<T>& A) {
return make_callback_var(
Phi_approx(A.val()), [A](vari_value<matrix_cl<double>>& res) mutable {
// e / (1 + e)^2, not res * (1 - res), which cancels for u >> 0
auto x_sq = square(A.val());
auto e = exp(-fabs(elt_multiply(
A.val(), PHI_APPROX_CUBIC_COEF * x_sq + PHI_APPROX_LINEAR_COEF)));
A.adj() += elt_multiply(
elt_multiply(elt_multiply(res.adj(), res.val()), 1 - res.val()),
3.0 * 0.07056 * square(A.val()) + 1.5976);
elt_multiply(res.adj(), elt_divide(e, square(1.0 + e))),
3.0 * PHI_APPROX_CUBIC_COEF * x_sq + PHI_APPROX_LINEAR_COEF);
});
}

Expand Down
2 changes: 1 addition & 1 deletion stan/math/opencl/rev/inv_logit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ inline var_value<matrix_cl<double>> inv_logit(const var_value<T>& A) {
return make_callback_var(
inv_logit(A.val()), [A](vari_value<matrix_cl<double>>& res) mutable {
A.adj() += elt_multiply(res.adj(),
elt_multiply(res.val(), 1.0 - res.val()));
elt_multiply(res.val(), inv_logit(-A.val())));
});
}

Expand Down
10 changes: 4 additions & 6 deletions stan/math/opencl/rev/log_inv_logit_diff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,11 @@ inline var_value<matrix_cl<double>> log_inv_logit_diff(T_x&& x, T_y&& y) {
return make_callback_var(
log_inv_logit_diff(value_of(x_arena), value_of(y_arena)),
[x_arena, y_arena](const vari_value<matrix_cl<double>>& res) mutable {
// both partials as sums of positive terms, no cancellation for x >> 0
auto c = inv(expm1(value_of(x_arena) - value_of(y_arena)));
adjoint_results(x_arena, y_arena) += expressions(
-elt_multiply(res.adj(),
inv(expm1(value_of(y_arena) - value_of(x_arena)))
+ inv_logit(value_of(x_arena))),
-elt_multiply(res.adj(),
inv(expm1(value_of(x_arena) - value_of(y_arena)))
+ inv_logit(value_of(y_arena))));
elt_multiply(res.adj(), c + inv_logit(-value_of(x_arena))),
-elt_multiply(res.adj(), c + inv_logit(value_of(y_arena))));
});
}

Expand Down
7 changes: 6 additions & 1 deletion stan/math/prim/fun/Phi_approx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define STAN_MATH_PRIM_FUN_PHI_APPROX_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <stan/math/prim/fun/inv_logit.hpp>
#include <stan/math/prim/functor/apply_scalar_unary.hpp>
#include <cmath>
Expand All @@ -12,6 +13,9 @@ namespace math {
/**
* Return an approximation of the unit normal CDF.
*
* Computes \f$ \mbox{logit}^{-1}(\gamma x^3 + \beta x) \f$ with the
* coefficients <code>PHI_APPROX_CUBIC_COEF</code> and
* <code>PHI_APPROX_LINEAR_COEF</code>, the form of Bowling et al. (2009),
* http://www.jiem.org/index.php/jiem/article/download/60/27
*
* This function can be used to implement the inverse link function
Expand All @@ -22,7 +26,8 @@ namespace math {
*/
inline double Phi_approx(double x) {
using std::pow;
return inv_logit(0.07056 * pow(x, 3.0) + 1.5976 * x);
return inv_logit(PHI_APPROX_CUBIC_COEF * pow(x, 3.0)
+ PHI_APPROX_LINEAR_COEF * x);
}

/**
Expand Down
13 changes: 13 additions & 0 deletions stan/math/prim/fun/constants.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ static constexpr double NEG_LOG_SQRT_TWO_PI = -HALF_LOG_TWO_PI;
*/
const double POISSON_MAX_RATE = std::pow(2.0, 30);

/**
* Coefficients of the logistic approximation to the standard normal CDF
* computed by <code>Phi_approx</code>,
* \f$ \Phi(x) \approx \mbox{logit}^{-1}(\gamma x^3 + \beta x) \f$,
* the form of Bowling et al. (2009). They are the pair that minimises the
* maximum absolute deviation from \f$ \Phi \f$ over the real line, rounded
* to double precision: the deviation is 1.40313e-4, attained at
* \f$ x = \pm 0.4816, \pm 1.4764, \pm 2.6876 \f$. The paper prints the pair
* rounded to 0.07056 and 1.5976 (maximum deviation 1.41448e-4).
*/
static constexpr double PHI_APPROX_CUBIC_COEF = 0.07056409656566716;
static constexpr double PHI_APPROX_LINEAR_COEF = 1.597602874240784;

/**
* Return positive infinity.
*
Expand Down
4 changes: 2 additions & 2 deletions stan/math/prim/fun/log_inv_logit_diff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ namespace math {
\f]

\f[
\frac{\partial }{\partial x} = -\frac{e^x}{e^y-e^x}-\frac{e^x}{e^x+1}
\frac{\partial}{\partial x} = \mbox{logit}^{-1}(-x) + \frac{1}{e^{x-y}-1}
\f]

\f[
\frac{\partial }{\partial x} = -\frac{e^y}{e^x-e^y}-\frac{e^y}{e^y+1}
\frac{\partial}{\partial y} = -\mbox{logit}^{-1}(y) - \frac{1}{e^{x-y}-1}
\f]
*
* @tparam T1 type of x argument
Expand Down
45 changes: 28 additions & 17 deletions stan/math/rev/fun/Phi_approx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

#include <stan/math/rev/meta.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/rev/fun/inv_logit.hpp>
#include <stan/math/prim/fun/Phi_approx.hpp>
#include <stan/math/prim/fun/constants.hpp>
#include <cmath>

namespace stan {
namespace math {
Expand All @@ -31,41 +32,51 @@ namespace math {
\f]

\f[
\Phi_{\mbox{\footnotesize approx}}(x) = \mbox{logit}^{-1}(0.07056 \,
x^3 + 1.5976 \, x)
\Phi_{\mbox{\footnotesize approx}}(x) = \mbox{logit}^{-1}(\gamma \,
x^3 + \beta \, x)
\f]

\f[
\frac{\partial \, \Phi_{\mbox{\footnotesize approx}}(x)}{\partial x}
= -\Phi_{\mbox{\footnotesize approx}}^2(x)
e^{-0.07056x^3 - 1.5976x}(-0.21168x^2-1.5976)
= \Phi_{\mbox{\footnotesize approx}}(x)
\left(1 - \Phi_{\mbox{\footnotesize approx}}(x)\right)
(3 \gamma x^2 + \beta)
\f]

with \f$ \gamma \f$ = <code>PHI_APPROX_CUBIC_COEF</code> and
\f$ \beta \f$ = <code>PHI_APPROX_LINEAR_COEF</code>.
*
* @param a Variable argument.
* @return The corresponding unit normal cdf approximation.
*/
inline var Phi_approx(const var& a) {
double av_squared = a.val() * a.val();
double f = inv_logit(0.07056 * a.val() * av_squared + 1.5976 * a.val());
double da = f * (1 - f) * (3.0 * 0.07056 * av_squared + 1.5976);
double u = PHI_APPROX_CUBIC_COEF * a.val() * av_squared
+ PHI_APPROX_LINEAR_COEF * a.val();
// e / (1 + e)^2, not f * (1 - f), which cancels for u >> 0
double e = std::exp(-std::fabs(u));
double f = (u >= 0 ? 1.0 : e) / (1.0 + e);
double da
= e / ((1.0 + e) * (1.0 + e))
* (3.0 * PHI_APPROX_CUBIC_COEF * av_squared + PHI_APPROX_LINEAR_COEF);
return make_callback_var(
f, [a, da](auto& vi) mutable { a.adj() += vi.adj() * da; });
}

template <typename T, require_var_matrix_t<T>* = nullptr>
inline auto Phi_approx(const T& a) {
const auto& x = a.val().array();
arena_t<value_type_t<T>> f(a.rows(), a.cols());
arena_t<value_type_t<T>> da(a.rows(), a.cols());
for (Eigen::Index j = 0; j < a.cols(); ++j) {
for (Eigen::Index i = 0; i < a.rows(); ++i) {
const auto a_val = a.val().coeff(i, j);
const auto av_squared = a_val * a_val;
f.coeffRef(i, j) = inv_logit(0.07056 * a_val * av_squared
+ 1.5976 * a.val().coeff(i, j));
da.coeffRef(i, j) = f.coeff(i, j) * (1 - f.coeff(i, j))
* (3.0 * 0.07056 * av_squared + 1.5976);
}
}
// e / (1 + e)^2, not f * (1 - f), which cancels for u >> 0
da.array()
= (-(PHI_APPROX_CUBIC_COEF * x * x.square() + PHI_APPROX_LINEAR_COEF * x)
.abs())
.exp();
f.array() = (x >= 0).select(1.0, da.array()) / (1.0 + da.array());
da.array()
*= (3.0 * PHI_APPROX_CUBIC_COEF * x.square() + PHI_APPROX_LINEAR_COEF)
/ (1.0 + da.array()).square();
return make_callback_var(f, [a, da](auto& vi) mutable {
a.adj().array() += vi.adj().array() * da.array();
});
Expand Down
Loading
Loading