Automatic Differentiation
 
Loading...
Searching...
No Matches
ordered_logistic_glm_lpmf.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_PRIM_PROB_ORDERED_LOGISTIC_GLM_LPMF_HPP
2#define STAN_MATH_PRIM_PROB_ORDERED_LOGISTIC_GLM_LPMF_HPP
3
19#include <cmath>
20
21namespace stan {
22namespace math {
23
48template <bool propto, typename T_y, typename T_x, typename T_beta,
49 typename T_cuts, require_matrix_t<T_x>* = nullptr,
50 require_all_col_vector_t<T_beta, T_cuts>* = nullptr>
52 const T_y& y, const T_x& x, const T_beta& beta, const T_cuts& cuts) {
53 using Eigen::Array;
54 using Eigen::Dynamic;
55 using Eigen::Matrix;
56 using Eigen::VectorXd;
57 using std::exp;
58 using std::isfinite;
59 constexpr int T_x_rows = T_x::RowsAtCompileTime;
60 using T_cuts_partials = partials_return_t<T_cuts>;
61 using T_xbeta_partials = partials_return_t<T_x, T_beta>;
62 using T_partials_return = partials_return_t<T_y, T_x, T_beta, T_cuts>;
63 typedef typename std::conditional_t<
64 T_x_rows == 1, T_xbeta_partials,
65 Eigen::Matrix<T_xbeta_partials, Eigen::Dynamic, 1>>
66 T_location;
67 using T_y_ref = ref_type_t<T_y>;
68 using T_x_ref = ref_type_if_not_constant_t<T_x>;
69 using T_beta_ref = ref_type_if_not_constant_t<T_beta>;
70 using T_cuts_ref = ref_type_if_not_constant_t<T_cuts>;
71
72 const size_t N_instances = T_x_rows == 1 ? stan::math::size(y) : x.rows();
73 const size_t N_attributes = x.cols();
74 const size_t N_classes = stan::math::size(cuts) + 1;
75
76 static constexpr const char* function = "ordered_logistic_glm_lpmf";
77 check_consistent_size(function, "Vector of dependent variables", y,
78 N_instances);
79 check_consistent_size(function, "Weight vector", beta, N_attributes);
80 T_y_ref y_ref = y;
81 T_cuts_ref cuts_ref = cuts;
82 const auto& cuts_val = value_of(cuts_ref);
83 const auto& cuts_val_vec = to_ref(as_column_vector_or_scalar(cuts_val));
84 check_bounded(function, "Vector of dependent variables", y_ref, 1, N_classes);
85 check_ordered(function, "Cut-points", cuts_val_vec);
86 if (N_classes > 1) {
87 if (N_classes > 2) {
88 check_finite(function, "Final cut-point", cuts_val_vec[N_classes - 2]);
89 }
90 check_finite(function, "First cut-point", cuts_val_vec[0]);
91 }
92
93 if (size_zero(y, cuts)) {
94 return 0;
95 }
97 return 0;
98 }
99
100 T_x_ref x_ref = x;
101 T_beta_ref beta_ref = beta;
102
103 const auto& x_val = to_ref_if<is_autodiff_v<T_beta>>(value_of(x_ref));
104 const auto& beta_val = value_of(beta_ref);
105
106 const auto& beta_val_vec
107 = to_ref_if<is_autodiff_v<T_x>>(as_column_vector_or_scalar(beta_val));
108
109 scalar_seq_view<T_y_ref> y_seq(y_ref);
110 Array<T_cuts_partials, Dynamic, 1> cuts_y1(N_instances), cuts_y2(N_instances);
111 for (int i = 0; i < N_instances; i++) {
112 int c = y_seq[i];
113 if (c != N_classes) {
114 cuts_y1.coeffRef(i) = cuts_val_vec.coeff(c - 1);
115 } else {
116 cuts_y1.coeffRef(i) = INFINITY;
117 }
118 if (c != 1) {
119 cuts_y2.coeffRef(i) = cuts_val_vec.coeff(c - 2);
120 } else {
121 cuts_y2.coeffRef(i) = -INFINITY;
122 }
123 }
124
125 T_location location = multiply(x_val, beta_val_vec);
126 if (!isfinite(sum(location))) {
127 check_finite(function, "Weight vector", beta);
128 check_finite(function, "Matrix of independent variables", x);
129 }
130
131 Array<T_partials_return, Dynamic, 1> cut2
132 = as_array_or_scalar(location) - cuts_y2;
133 Array<T_partials_return, Dynamic, 1> cut1
134 = as_array_or_scalar(location) - cuts_y1;
135
136 // Not immediately evaluating next two expressions benefits performance
137 auto m_log_1p_exp_cut1
138 = (cut1 > 0.0).select(-cut1, 0) - (-cut1.abs()).exp().log1p();
139 auto m_log_1p_exp_m_cut2
140 = (cut2 <= 0.0).select(cut2, 0) - (-cut2.abs()).exp().log1p();
141
142 T_partials_return logp(0);
143 if constexpr (is_vector<T_y>::value) {
144 Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, 1>> y_vec(y_seq.data(),
145 y_seq.size());
146 logp = y_vec.cwiseEqual(1)
147 .select(m_log_1p_exp_cut1,
148 y_vec.cwiseEqual(N_classes).select(
149 m_log_1p_exp_m_cut2,
150 m_log_1p_exp_m_cut2 + log1m_exp(cut1 - cut2).array()
151 + m_log_1p_exp_cut1))
152 .sum();
153 } else {
154 if (y_seq[0] == 1) {
155 logp = m_log_1p_exp_cut1.sum();
156 } else if (y_seq[0] == N_classes) {
157 logp = m_log_1p_exp_m_cut2.sum();
158 } else {
159 logp = (m_log_1p_exp_m_cut2 + log1m_exp(cut1 - cut2).array()
160 + m_log_1p_exp_cut1)
161 .sum();
162 }
163 }
164
165 auto ops_partials = make_partials_propagator(x_ref, beta_ref, cuts_ref);
166 if constexpr (is_any_autodiff_v<T_x, T_beta, T_cuts>) {
167 Array<T_partials_return, Dynamic, 1> exp_m_abs_cut1 = (-cut1.abs()).exp();
168 Array<T_partials_return, Dynamic, 1> exp_m_abs_cut2 = (-cut2.abs()).exp();
169 Array<T_partials_return, Dynamic, 1> exp_cuts_diff = exp(cuts_y2 - cuts_y1);
170 Array<T_partials_return, Dynamic, 1> inv_logit_neg_cut2 = (cut2 > 0).select(
171 exp_m_abs_cut2 / (1 + exp_m_abs_cut2), 1 / (1 + exp_m_abs_cut2));
172 Array<T_partials_return, Dynamic, 1> inv_logit_neg_cut1 = (cut1 > 0).select(
173 exp_m_abs_cut1 / (1 + exp_m_abs_cut1), 1 / (1 + exp_m_abs_cut1));
174 Array<T_partials_return, Dynamic, 1> d1
175 = inv_logit_neg_cut2 - exp_cuts_diff / (exp_cuts_diff - 1);
176 Array<T_partials_return, Dynamic, 1> d2
177 = 1 / (1 - exp_cuts_diff) - inv_logit_neg_cut1;
178 if constexpr (is_any_autodiff_v<T_x, T_beta>) {
179 Matrix<T_partials_return, 1, Dynamic> location_derivative = d1 - d2;
180 if constexpr (is_autodiff_v<T_x>) {
181 if constexpr (T_x_rows == 1) {
182 edge<0>(ops_partials).partials_
183 = beta_val_vec * location_derivative.sum();
184 } else {
185 edge<0>(ops_partials).partials_
186 = (beta_val_vec * location_derivative).transpose();
187 }
188 }
189 if constexpr (is_autodiff_v<T_beta>) {
190 if constexpr (T_x_rows == 1) {
191 edge<1>(ops_partials).partials_
192 = (location_derivative * x_val.replicate(N_instances, 1))
193 .transpose();
194 } else if constexpr (T_x::ColsAtCompileTime == 1) {
195 edge<1>(ops_partials).partials_[0]
196 = dot_product(location_derivative, x_val);
197 } else {
198 edge<1>(ops_partials).partials_
199 = multiply(location_derivative, x_val).transpose();
200 }
201 }
202 }
203 if constexpr (is_autodiff_v<T_cuts>) {
204 for (int i = 0; i < N_instances; i++) {
205 int c = y_seq[i];
206 if (c != N_classes) {
207 partials<2>(ops_partials)[c - 1] += d2.coeff(i);
208 }
209 if (c != 1) {
210 partials<2>(ops_partials)[c - 2] -= d1.coeff(i);
211 }
212 }
213 }
214 }
215 return ops_partials.build(logp);
216}
217
218template <typename T_y, typename T_x, typename T_beta, typename T_cuts>
220 const T_y& y, const T_x& x, const T_beta& beta, const T_cuts& cuts) {
221 return ordered_logistic_glm_lpmf<false>(y, x, beta, cuts);
222}
223
224} // namespace math
225} // namespace stan
226
227#endif
scalar_seq_view provides a uniform sequence-like wrapper around either a scalar or a sequence of scal...
isfinite_< as_operation_cl_t< T > > isfinite(T &&a)
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.
Definition select.hpp:148
auto as_column_vector_or_scalar(T &&a)
as_column_vector_or_scalar of a kernel generator expression.
auto transpose(Arg &&a)
Transposes a kernel generator expression.
return_type_t< T_x, T_beta, T_cuts > ordered_logistic_glm_lpmf(const T_y &y, const T_x &x, const T_beta &beta, const T_cuts &cuts)
Returns the log PMF of the ordinal regression Generalized Linear Model (GLM).
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
int64_t size(const T &m)
Returns the size (number of the elements) of a matrix_cl or var_value<matrix_cl<T>>.
Definition size.hpp:19
T as_array_or_scalar(T &&v)
Returns specified input value.
auto multiply(Mat1 &&m1, Mat2 &&m2)
Return the product of the specified matrices.
Definition multiply.hpp:20
bool size_zero(const T &x)
Returns 1 if input is of length 0, returns 0 otherwise.
Definition size_zero.hpp:19
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.
fvar< T > log1m_exp(const fvar< T > &x)
Return the natural logarithm of one minus the exponentiation of the specified argument.
Definition log1m_exp.hpp:22
void check_consistent_size(const char *function, const char *name, const T &x, size_t expected_size)
Check if x is consistent with size expected_size.
T value_of(const fvar< T > &v)
Return the value of the specified variable.
Definition value_of.hpp:18
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
auto sum(const std::vector< T > &m)
Return the sum of the entries of the specified standard vector.
Definition sum.hpp:23
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
Definition to_ref.hpp:18
void check_ordered(const char *function, const char *name, const T_y &y)
Throw an exception if the specified vector is not sorted into strictly increasing order.
auto dot_product(const T_a &a, const T_b &b)
Returns the dot product of the specified vectors.
fvar< T > beta(const fvar< T > &x1, const fvar< T > &x2)
Return fvar with the beta function applied to the specified arguments and its gradient.
Definition beta.hpp:51
auto make_partials_propagator(Ops &&... ops)
Construct an partials_propagator.
fvar< T > exp(const fvar< T > &x)
Definition exp.hpp:15
typename ref_type_if< is_autodiff_v< T >, T >::type ref_type_if_not_constant_t
Definition ref_type.hpp:63
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 ...
If the input type T is either an eigen matrix with 1 column or 1 row at compile time or a standard ve...
Template metaprogram to calculate whether a summand needs to be included in a proportional (log) prob...