1 #ifndef STAN_MATH_PRIM_FUN_OFFSET_MULTIPLIER_CONSTRAIN_HPP
2 #define STAN_MATH_PRIM_FUN_OFFSET_MULTIPLIER_CONSTRAIN_HPP
3 
4 #include <stan/math/prim/meta.hpp>
5 #include <stan/math/prim/err.hpp>
6 #include <stan/math/prim/fun/fma.hpp>
7 #include <stan/math/prim/fun/identity_constrain.hpp>
8 #include <stan/math/prim/fun/multiply_log.hpp>
9 #include <stan/math/prim/fun/size.hpp>
10 #include <stan/math/prim/fun/sum.hpp>
11 #include <stan/math/prim/fun/to_ref.hpp>
12 #include <cmath>
13 
14 namespace stan {
15 namespace math {
16 
17 /**
18  * Return the linearly transformed value for the specified unconstrained input
19  * and specified offset and multiplier.
20  *
21  * <p>The transform applied is
22  *
23  * <p>\f$f(x) = mu + sigma * x\f$
24  *
25  * <p>where mu is the offset and sigma is the multiplier.
26  *
27  * <p>If the offset is zero and the multiplier is one this
28  * reduces to <code>identity_constrain(x)</code>.
29  *
30  * @tparam T type of scalar
31  * @tparam M type of offset
32  * @tparam S type of multiplier
33  * @param[in] x Unconstrained scalar input
34  * @param[in] mu offset of constrained output
35  * @param[in] sigma multiplier of constrained output
36  * @return linear transformed value corresponding to inputs
37  * @throw std::domain_error if sigma <= 0
38  * @throw std::domain_error if mu is not finite
39  */
40 template <typename T, typename M, typename S,
41           require_all_not_nonscalar_prim_or_rev_kernel_expression_t<
42               T, M, S>* = nullptr>
offset_multiplier_constrain(const T & x,const M & mu,const S & sigma)43 inline auto offset_multiplier_constrain(const T& x, const M& mu,
44                                         const S& sigma) {
45   const auto& mu_ref = to_ref(mu);
46   const auto& sigma_ref = to_ref(sigma);
47   if (is_matrix<T>::value && is_matrix<M>::value) {
48     check_matching_dims("offset_multiplier_constrain", "x", x, "mu", mu);
49   }
50   if (is_matrix<T>::value && is_matrix<S>::value) {
51     check_matching_dims("offset_multiplier_constrain", "x", x, "sigma", sigma);
52   } else if (is_matrix<M>::value && is_matrix<S>::value) {
53     check_matching_dims("offset_multiplier_constrain", "mu", mu, "sigma",
54                         sigma);
55   }
56 
57   check_finite("offset_multiplier_constrain", "offset", value_of_rec(mu_ref));
58   check_positive_finite("offset_multiplier_constrain", "multiplier",
59                         value_of_rec(sigma_ref));
60   return fma(sigma_ref, x, mu_ref);
61 }
62 
63 /**
64  * Return the linearly transformed value for the specified unconstrained input
65  * and specified offset and multiplier, incrementing the specified
66  * reference with the log absolute Jacobian determinant of the
67  * transform.
68  *
69  * <p>The transform applied is
70  *
71  * <p>\f$f(x) = mu + sigma * x\f$
72  *
73  * <p>where mu is the offset and sigma is the multiplier.
74  *
75  * If the offset is zero and multiplier is one, this function
76  * reduces to <code>identity_constraint(x, lp)</code>.
77  *
78  * @tparam T type of scalar
79  * @tparam M type of offset
80  * @tparam S type of multiplier
81  * @param[in] x Unconstrained scalar input
82  * @param[in] mu offset of constrained output
83  * @param[in] sigma multiplier of constrained output
84  * @param[in,out] lp Reference to log probability to increment.
85  * @return linear transformed value corresponding to inputs
86  * @throw std::domain_error if sigma <= 0
87  * @throw std::domain_error if mu is not finite
88  */
89 template <typename T, typename M, typename S,
90           require_all_not_nonscalar_prim_or_rev_kernel_expression_t<
91               T, M, S>* = nullptr>
offset_multiplier_constrain(const T & x,const M & mu,const S & sigma,return_type_t<T,M,S> & lp)92 inline auto offset_multiplier_constrain(const T& x, const M& mu, const S& sigma,
93                                         return_type_t<T, M, S>& lp) {
94   const auto& mu_ref = to_ref(mu);
95   const auto& sigma_ref = to_ref(sigma);
96   if (is_matrix<T>::value && is_matrix<M>::value) {
97     check_matching_dims("offset_multiplier_constrain", "x", x, "mu", mu);
98   }
99   if (is_matrix<T>::value && is_matrix<S>::value) {
100     check_matching_dims("offset_multiplier_constrain", "x", x, "sigma", sigma);
101   } else if (is_matrix<M>::value && is_matrix<S>::value) {
102     check_matching_dims("offset_multiplier_constrain", "mu", mu, "sigma",
103                         sigma);
104   }
105 
106   check_finite("offset_multiplier_constrain", "offset", value_of_rec(mu_ref));
107   check_positive_finite("offset_multiplier_constrain", "multiplier",
108                         value_of_rec(sigma_ref));
109   if (size(sigma_ref) == 1) {
110     lp += sum(multiply_log(size(x), sigma_ref));
111   } else {
112     lp += sum(log(sigma_ref));
113   }
114   return fma(sigma_ref, x, mu_ref);
115 }
116 
117 /**
118  * Overload for array of x and non-array mu and sigma
119  */
120 template <typename T, typename M, typename S,
121           require_all_not_std_vector_t<M, S>* = nullptr>
offset_multiplier_constrain(const std::vector<T> & x,const M & mu,const S & sigma)122 inline auto offset_multiplier_constrain(const std::vector<T>& x, const M& mu,
123                                         const S& sigma) {
124   std::vector<
125       plain_type_t<decltype(offset_multiplier_constrain(x[0], mu, sigma))>>
126       ret;
127   ret.reserve(x.size());
128   const auto& mu_ref = to_ref(mu);
129   const auto& sigma_ref = to_ref(sigma);
130   for (size_t i = 0; i < x.size(); ++i) {
131     ret.emplace_back(offset_multiplier_constrain(x[i], mu_ref, sigma_ref));
132   }
133   return ret;
134 }
135 
136 /**
137  * Overload for array of x and non-array mu and sigma with lp
138  */
139 template <typename T, typename M, typename S,
140           require_all_not_std_vector_t<M, S>* = nullptr>
offset_multiplier_constrain(const std::vector<T> & x,const M & mu,const S & sigma,return_type_t<T,M,S> & lp)141 inline auto offset_multiplier_constrain(const std::vector<T>& x, const M& mu,
142                                         const S& sigma,
143                                         return_type_t<T, M, S>& lp) {
144   std::vector<
145       plain_type_t<decltype(offset_multiplier_constrain(x[0], mu, sigma, lp))>>
146       ret;
147   ret.reserve(x.size());
148   const auto& mu_ref = to_ref(mu);
149   const auto& sigma_ref = to_ref(sigma);
150   for (size_t i = 0; i < x.size(); ++i) {
151     ret.emplace_back(offset_multiplier_constrain(x[i], mu_ref, sigma_ref, lp));
152   }
153   return ret;
154 }
155 
156 /**
157  * Overload for array of x and sigma and non-array mu
158  */
159 template <typename T, typename M, typename S,
160           require_not_std_vector_t<M>* = nullptr>
offset_multiplier_constrain(const std::vector<T> & x,const M & mu,const std::vector<S> & sigma)161 inline auto offset_multiplier_constrain(const std::vector<T>& x, const M& mu,
162                                         const std::vector<S>& sigma) {
163   check_matching_dims("offset_multiplier_constrain", "x", x, "sigma", sigma);
164   std::vector<
165       plain_type_t<decltype(offset_multiplier_constrain(x[0], mu, sigma[0]))>>
166       ret;
167   ret.reserve(x.size());
168   const auto& mu_ref = to_ref(mu);
169   for (size_t i = 0; i < x.size(); ++i) {
170     ret.emplace_back(offset_multiplier_constrain(x[i], mu_ref, sigma[i]));
171   }
172   return ret;
173 }
174 
175 /**
176  * Overload for array of x and sigma and non-array mu with lp
177  */
178 template <typename T, typename M, typename S,
179           require_not_std_vector_t<M>* = nullptr>
offset_multiplier_constrain(const std::vector<T> & x,const M & mu,const std::vector<S> & sigma,return_type_t<T,M,S> & lp)180 inline auto offset_multiplier_constrain(const std::vector<T>& x, const M& mu,
181                                         const std::vector<S>& sigma,
182                                         return_type_t<T, M, S>& lp) {
183   check_matching_dims("offset_multiplier_constrain", "x", x, "sigma", sigma);
184   std::vector<plain_type_t<decltype(
185       offset_multiplier_constrain(x[0], mu, sigma[0], lp))>>
186       ret;
187   ret.reserve(x.size());
188   const auto& mu_ref = to_ref(mu);
189   for (size_t i = 0; i < x.size(); ++i) {
190     ret.emplace_back(offset_multiplier_constrain(x[i], mu_ref, sigma[i], lp));
191   }
192   return ret;
193 }
194 
195 /**
196  * Overload for array of x and mu and non-array sigma
197  */
198 template <typename T, typename M, typename S,
199           require_not_std_vector_t<S>* = nullptr>
offset_multiplier_constrain(const std::vector<T> & x,const std::vector<M> & mu,const S & sigma)200 inline auto offset_multiplier_constrain(const std::vector<T>& x,
201                                         const std::vector<M>& mu,
202                                         const S& sigma) {
203   check_matching_dims("offset_multiplier_constrain", "x", x, "mu", mu);
204   std::vector<
205       plain_type_t<decltype(offset_multiplier_constrain(x[0], mu[0], sigma))>>
206       ret;
207   ret.reserve(x.size());
208   const auto& sigma_ref = to_ref(sigma);
209   for (size_t i = 0; i < x.size(); ++i) {
210     ret.emplace_back(offset_multiplier_constrain(x[i], mu[i], sigma_ref));
211   }
212   return ret;
213 }
214 
215 /**
216  * Overload for array of x and mu and non-array sigma with lp
217  */
218 template <typename T, typename M, typename S,
219           require_not_std_vector_t<S>* = nullptr>
offset_multiplier_constrain(const std::vector<T> & x,const std::vector<M> & mu,const S & sigma,return_type_t<T,M,S> & lp)220 inline auto offset_multiplier_constrain(const std::vector<T>& x,
221                                         const std::vector<M>& mu,
222                                         const S& sigma,
223                                         return_type_t<T, M, S>& lp) {
224   check_matching_dims("offset_multiplier_constrain", "x", x, "mu", mu);
225   std::vector<plain_type_t<decltype(
226       offset_multiplier_constrain(x[0], mu[0], sigma, lp))>>
227       ret;
228   ret.reserve(x.size());
229   const auto& sigma_ref = to_ref(sigma);
230   for (size_t i = 0; i < x.size(); ++i) {
231     ret.emplace_back(offset_multiplier_constrain(x[i], mu[i], sigma_ref, lp));
232   }
233   return ret;
234 }
235 
236 /**
237  * Overload for array of x, mu, and sigma
238  */
239 template <typename T, typename M, typename S>
offset_multiplier_constrain(const std::vector<T> & x,const std::vector<M> & mu,const std::vector<S> & sigma)240 inline auto offset_multiplier_constrain(const std::vector<T>& x,
241                                         const std::vector<M>& mu,
242                                         const std::vector<S>& sigma) {
243   check_matching_dims("offset_multiplier_constrain", "x", x, "mu", mu);
244   check_matching_dims("offset_multiplier_constrain", "x", x, "sigma", sigma);
245   std::vector<plain_type_t<decltype(
246       offset_multiplier_constrain(x[0], mu[0], sigma[0]))>>
247       ret;
248   ret.reserve(x.size());
249   for (size_t i = 0; i < x.size(); ++i) {
250     ret.emplace_back(offset_multiplier_constrain(x[i], mu[i], sigma[i]));
251   }
252   return ret;
253 }
254 
255 /**
256  * Overload for array of x, mu, and sigma with lp
257  */
258 template <typename T, typename M, typename S>
offset_multiplier_constrain(const std::vector<T> & x,const std::vector<M> & mu,const std::vector<S> & sigma,return_type_t<T,M,S> & lp)259 inline auto offset_multiplier_constrain(const std::vector<T>& x,
260                                         const std::vector<M>& mu,
261                                         const std::vector<S>& sigma,
262                                         return_type_t<T, M, S>& lp) {
263   check_matching_dims("offset_multiplier_constrain", "x", x, "mu", mu);
264   check_matching_dims("offset_multiplier_constrain", "x", x, "sigma", sigma);
265   std::vector<plain_type_t<decltype(
266       offset_multiplier_constrain(x[0], mu[0], sigma[0], lp))>>
267       ret;
268   ret.reserve(x.size());
269   for (size_t i = 0; i < x.size(); ++i) {
270     ret.emplace_back(offset_multiplier_constrain(x[i], mu[i], sigma[i], lp));
271   }
272   return ret;
273 }
274 
275 /**
276  * Return the linearly transformed value for the specified unconstrained input
277  * and specified offset and multiplier. If the `Jacobian` parameter is `true`,
278  * the log density accumulator is incremented with the log absolute Jacobian
279  * determinant of the transform.  All of the transforms are specified with their
280  * Jacobians in the *Stan Reference Manual* chapter Constraint Transforms.
281  *
282  * @tparam Jacobian if `true`, increment log density accumulator with log
283  * absolute Jacobian determinant of constraining transform
284  * @tparam T A type inheriting from `Eigen::EigenBase`, a `var_value` with inner
285  * type inheriting from `Eigen::EigenBase`, a standard vector, or a scalar
286  * @tparam M A type inheriting from `Eigen::EigenBase`, a `var_value` with inner
287  * type inheriting from `Eigen::EigenBase`, a standard vector, or a scalar
288  * @tparam S A type inheriting from `Eigen::EigenBase`, a `var_value` with inner
289  * type inheriting from `Eigen::EigenBase`, a standard vector, or a scalar
290  * @param[in] x Unconstrained scalar input
291  * @param[in] mu offset of constrained output
292  * @param[in] sigma multiplier of constrained output
293  * @param[in, out] lp log density accumulator
294  * @return linear transformed value corresponding to inputs
295  * @throw std::domain_error if sigma <= 0
296  * @throw std::domain_error if mu is not finite
297  */
298 template <bool Jacobian, typename T, typename M, typename S>
offset_multiplier_constrain(const T & x,const M & mu,const S & sigma,return_type_t<T,M,S> & lp)299 inline auto offset_multiplier_constrain(const T& x, const M& mu, const S& sigma,
300                                         return_type_t<T, M, S>& lp) {
301   if (Jacobian) {
302     return offset_multiplier_constrain(x, mu, sigma, lp);
303   } else {
304     return offset_multiplier_constrain(x, mu, sigma);
305   }
306 }
307 
308 }  // namespace math
309 }  // namespace stan
310 
311 #endif
312