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
17#include <cmath>
18
19namespace stan {
20namespace math {
21
46template <bool propto, typename T_y, typename T_x, typename T_beta,
47 typename T_cuts, require_matrix_t<T_x>* = nullptr,
48 require_all_col_vector_t<T_beta, T_cuts>* = nullptr>
50 const T_y& y, const T_x& x, const T_beta& beta, const T_cuts& cuts) {
51 using Eigen::Array;
52 using Eigen::Dynamic;
53 using Eigen::Matrix;
54 using Eigen::VectorXd;
55 using std::exp;
56 using std::isfinite;
57 constexpr int T_x_rows = T_x::RowsAtCompileTime;
58 using T_cuts_partials = partials_return_t<T_cuts>;
59 using T_xbeta_partials = partials_return_t<T_x, T_beta>;
60 using T_partials_return = partials_return_t<T_y, T_x, T_beta, T_cuts>;
61 typedef typename std::conditional_t<
62 T_x_rows == 1, T_xbeta_partials,
63 Eigen::Matrix<T_xbeta_partials, Eigen::Dynamic, 1>>
64 T_location;
65 using T_y_ref = ref_type_t<T_y>;
66 using T_x_ref = ref_type_if_not_constant_t<T_x>;
67 using T_beta_ref = ref_type_if_not_constant_t<T_beta>;
68 using T_cuts_ref = ref_type_if_not_constant_t<T_cuts>;
69
70 const size_t N_instances = T_x_rows == 1 ? stan::math::size(y) : x.rows();
71 const size_t N_attributes = x.cols();
72 const size_t N_classes = stan::math::size(cuts) + 1;
73
74 static constexpr const char* function = "ordered_logistic_glm_lpmf";
75 check_consistent_size(function, "Vector of dependent variables", y,
76 N_instances);
77 check_consistent_size(function, "Weight vector", beta, N_attributes);
78 T_y_ref y_ref = y;
79 T_cuts_ref cuts_ref = cuts;
80 const auto& cuts_val = value_of(cuts_ref);
81 const auto& cuts_val_vec = to_ref(as_column_vector_or_scalar(cuts_val));
82 check_bounded(function, "Vector of dependent variables", y_ref, 1, N_classes);
83 check_ordered(function, "Cut-points", cuts_val_vec);
84 if (N_classes > 1) {
85 if (N_classes > 2) {
86 check_finite(function, "Final cut-point", cuts_val_vec[N_classes - 2]);
87 }
88 check_finite(function, "First cut-point", cuts_val_vec[0]);
89 }
90
91 if (size_zero(y, cuts)) {
92 return 0;
93 }
95 return 0;
96 }
97
98 T_x_ref x_ref = x;
99 T_beta_ref beta_ref = beta;
100
101 const auto& x_val = to_ref_if<!is_constant<T_beta>::value>(value_of(x_ref));
102 const auto& beta_val = value_of(beta_ref);
103
104 const auto& beta_val_vec = to_ref_if<!is_constant<T_x>::value>(
106
107 scalar_seq_view<T_y_ref> y_seq(y_ref);
108 Array<T_cuts_partials, Dynamic, 1> cuts_y1(N_instances), cuts_y2(N_instances);
109 for (int i = 0; i < N_instances; i++) {
110 int c = y_seq[i];
111 if (c != N_classes) {
112 cuts_y1.coeffRef(i) = cuts_val_vec.coeff(c - 1);
113 } else {
114 cuts_y1.coeffRef(i) = INFINITY;
115 }
116 if (c != 1) {
117 cuts_y2.coeffRef(i) = cuts_val_vec.coeff(c - 2);
118 } else {
119 cuts_y2.coeffRef(i) = -INFINITY;
120 }
121 }
122
123 T_location location = x_val * beta_val_vec;
124 if (!isfinite(sum(location))) {
125 check_finite(function, "Weight vector", beta);
126 check_finite(function, "Matrix of independent variables", x);
127 }
128
129 Array<T_partials_return, Dynamic, 1> cut2
130 = as_array_or_scalar(location) - cuts_y2;
131 Array<T_partials_return, Dynamic, 1> cut1
132 = as_array_or_scalar(location) - cuts_y1;
133
134 // Not immediately evaluating next two expressions benefits performance
135 auto m_log_1p_exp_cut1
136 = (cut1 > 0.0).select(-cut1, 0) - (-cut1.abs()).exp().log1p();
137 auto m_log_1p_exp_m_cut2
138 = (cut2 <= 0.0).select(cut2, 0) - (-cut2.abs()).exp().log1p();
139
140 T_partials_return logp(0);
142 Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, 1>> y_vec(y_seq.data(),
143 y_seq.size());
144 logp = y_vec.cwiseEqual(1)
145 .select(m_log_1p_exp_cut1,
146 y_vec.cwiseEqual(N_classes).select(
147 m_log_1p_exp_m_cut2,
148 m_log_1p_exp_m_cut2 + log1m_exp(cut1 - cut2).array()
149 + m_log_1p_exp_cut1))
150 .sum();
151 } else {
152 if (y_seq[0] == 1) {
153 logp = m_log_1p_exp_cut1.sum();
154 } else if (y_seq[0] == N_classes) {
155 logp = m_log_1p_exp_m_cut2.sum();
156 } else {
157 logp = (m_log_1p_exp_m_cut2 + log1m_exp(cut1 - cut2).array()
158 + m_log_1p_exp_cut1)
159 .sum();
160 }
161 }
162
163 auto ops_partials = make_partials_propagator(x_ref, beta_ref, cuts_ref);
165 Array<T_partials_return, Dynamic, 1> exp_m_cut1 = exp(-cut1);
166 Array<T_partials_return, Dynamic, 1> exp_m_cut2 = exp(-cut2);
167 Array<T_partials_return, Dynamic, 1> exp_cuts_diff = exp(cuts_y2 - cuts_y1);
168 Array<T_partials_return, Dynamic, 1> d1
169 = (cut2 > 0).select(exp_m_cut2 / (1 + exp_m_cut2), 1 / (1 + exp(cut2)))
170 - exp_cuts_diff / (exp_cuts_diff - 1);
171 Array<T_partials_return, Dynamic, 1> d2
172 = 1 / (1 - exp_cuts_diff)
173 - (cut1 > 0).select(exp_m_cut1 / (1 + exp_m_cut1),
174 1 / (1 + exp(cut1)));
176 Matrix<T_partials_return, 1, Dynamic> location_derivative = d1 - d2;
178 if (T_x_rows == 1) {
179 edge<0>(ops_partials).partials_
180 = beta_val_vec * location_derivative.sum();
181 } else {
182 edge<0>(ops_partials).partials_
183 = (beta_val_vec * location_derivative).transpose();
184 }
185 }
187 if (T_x_rows == 1) {
188 edge<1>(ops_partials).partials_
189 = (location_derivative * x_val.replicate(N_instances, 1))
190 .transpose();
191 } else {
192 edge<1>(ops_partials).partials_
193 = (location_derivative * x_val).transpose();
194 }
195 }
196 }
198 for (int i = 0; i < N_instances; i++) {
199 int c = y_seq[i];
200 if (c != N_classes) {
201 partials<2>(ops_partials)[c - 1] += d2.coeff(i);
202 }
203 if (c != 1) {
204 partials<2>(ops_partials)[c - 2] -= d1.coeff(i);
205 }
206 }
207 }
208 }
209 return ops_partials.build(logp);
210}
211
212template <typename T_y, typename T_x, typename T_beta, typename T_cuts>
214 const T_y& y, const T_x& x, const T_beta& beta, const T_cuts& cuts) {
215 return ordered_logistic_glm_lpmf<false>(y, x, beta, cuts);
216}
217
218} // namespace math
219} // namespace stan
220
221#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).
size_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:18
typename return_type< Ts... >::type return_type_t
Convenience type for the return type of the specified template parameters.
T as_array_or_scalar(T &&v)
Returns specified input value.
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:23
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
fvar< T > sum(const std::vector< fvar< T > > &m)
Return the sum of the entries of the specified standard vector.
Definition sum.hpp:22
ref_type_t< T && > to_ref(T &&a)
This evaluates expensive Eigen expressions.
Definition to_ref.hpp:17
void check_finite(const char *function, const char *name, const T_y &y)
Return true if all values in y are finite.
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.
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:13
typename ref_type_if<!is_constant< T >::value, T >::type ref_type_if_not_constant_t
Definition ref_type.hpp:62
typename ref_type_if< true, T >::type ref_type_t
Definition ref_type.hpp:55
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 ...
Definition fvar.hpp:9
If the input type T is either an eigen matrix with 1 column or 1 row at compile time or a standard ve...
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...