1 #ifndef STAN_MATH_PRIM_FUNCTOR_APPLY_VECTOR_UNARY_HPP
2 #define STAN_MATH_PRIM_FUNCTOR_APPLY_VECTOR_UNARY_HPP
3 
4 #include <stan/math/prim/fun/Eigen.hpp>
5 #include <stan/math/prim/fun/as_column_vector_or_scalar.hpp>
6 #include <stan/math/prim/meta/is_stan_scalar.hpp>
7 #include <stan/math/prim/meta/is_container.hpp>
8 #include <stan/math/prim/meta/is_eigen_matrix_base.hpp>
9 #include <stan/math/prim/meta/plain_type.hpp>
10 #include <stan/math/prim/meta/require_generics.hpp>
11 #include <vector>
12 
13 namespace stan {
14 namespace math {
15 // Forward declaration to allow specialisations
16 template <typename T, typename Enable = void>
17 struct apply_vector_unary {};
18 
19 /**
20  * Base template class for vectorization of unary vector functions
21  * defined by applying a functor to a standard library vector, Eigen dense
22  * matrix expression template, or container of these. For each specialisation,
23  * the same vector type as the input is returned.
24  *
25  * Two taxonomies of unary vector functions are implemented:
26  * - f(vector) -> vector
27  * - f(vector) -> scalar
28  *
29  * This base template class takes (and returns) Eigen expression templates.
30  */
31 template <typename T>
32 struct apply_vector_unary<T, require_eigen_t<T>> {
33   /**
34    * Member function for applying a functor to a vector and subsequently
35    * returning a vector.
36    *
37    * @tparam T Type of argument to which functor is applied.
38    * @tparam F Type of functor to apply.
39    * @param x Eigen input to which operation is applied.
40    * @param f functor to apply to Eigen input.
41    * @return Eigen object with result of applying functor to input
42    */
43   template <typename F, typename T2 = T,
44             require_t<is_eigen_matrix_base<plain_type_t<T2>>>* = nullptr>
applystan::math::apply_vector_unary45   static inline auto apply(const T& x, const F& f) {
46     return make_holder([](const auto& a) { return a.matrix().derived(); },
47                        f(x));
48   }
49 
50   template <typename F, typename T2 = T,
51             require_t<is_eigen_array<plain_type_t<T2>>>* = nullptr>
applystan::math::apply_vector_unary52   static inline auto apply(const T& x, const F& f) {
53     return make_holder([](const auto& a) { return a.array().derived(); }, f(x));
54   }
55 
56   /**
57    * Member function for applying a functor to a vector and subsequently
58    * returning a vector. This is a variant of `apply` that does not construct
59    * `holder`, so it is up to the caller to ensure the returned expression is
60    * evaluated before `x` is destructed.
61    *
62    * @tparam T Type of argument to which functor is applied.
63    * @tparam F Type of functor to apply.
64    * @param x Eigen input to which operation is applied.
65    * @param f functor to apply to Eigen input.
66    * @return Eigen object with result of applying functor to input
67    */
68   template <typename F, typename T2 = T,
69             require_t<is_eigen_matrix_base<plain_type_t<T2>>>* = nullptr>
apply_no_holderstan::math::apply_vector_unary70   static inline auto apply_no_holder(const T& x, const F& f) {
71     return f(x).matrix().derived();
72   }
73 
74   template <typename F, typename T2 = T,
75             require_t<is_eigen_array<plain_type_t<T2>>>* = nullptr>
apply_no_holderstan::math::apply_vector_unary76   static inline auto apply_no_holder(const T& x, const F& f) {
77     return f(x).array().derived();
78   }
79 
80   /**
81    * Member function for applying a functor to a vector and subsequently
82    * returning a scalar. The reduction to a scalar needs to be implemented
83    * in the definition of the functor.
84    *
85    * @tparam T Type of argument to which functor is applied.
86    * @tparam F Type of functor to apply.
87    * @param x Eigen input to which operation is applied.
88    * @param f functor to apply to Eigen input.
89    * @return scalar result of applying functor to input.
90    */
91   template <typename F>
reducestan::math::apply_vector_unary92   static inline auto reduce(const T& x, const F& f) {
93     return f(x);
94   }
95 };
96 
97 /**
98  * Specialisation for use with (non-nested) std::vectors. Inputs are mapped
99  * to Eigen column vectors and then the result is evaluated directly into the
100  * returned std::vector (via Eigen::Map).
101  *
102  * The returned scalar type is deduced to allow for cases where the input and
103  * return scalar types differ (e.g., functions implicitly promoting
104  * integers).
105  */
106 template <typename T>
107 struct apply_vector_unary<T, require_std_vector_vt<is_stan_scalar, T>> {
108   using T_vt = value_type_t<T>;
109   using T_map = typename Eigen::Map<const Eigen::Matrix<T_vt, -1, 1>>;
110 
111   /**
112    * Member function for applying a functor to a vector and subsequently
113    * returning a vector.
114    *
115    * @tparam T Type of argument to which functor is applied.
116    * @tparam F Type of functor to apply.
117    * @param x std::vector input to which operation is applied.
118    * @param f functor to apply to vector input.
119    * @return std::vector with result of applying functor to input.
120    */
121   template <typename F>
applystan::math::apply_vector_unary122   static inline auto apply(const T& x, const F& f) {
123     using T_return = value_type_t<decltype(f(as_column_vector_or_scalar(x)))>;
124     std::vector<T_return> result(x.size());
125     Eigen::Map<Eigen::Matrix<T_return, -1, 1>>(result.data(), result.size())
126         = f(as_column_vector_or_scalar(x)).matrix();
127     return result;
128   }
129 
130   /**
131    * Member function for applying a functor to each container in an std::vector
132    * and subsequently returning an std::vector of containers.
133    *
134    * @tparam T Type of argument to which functor is applied.
135    * @tparam F Type of functor to apply.
136    * @param x std::vector of containers to which operation is applied.
137    * @param f functor to apply to vector input.
138    * @return std::vector of containers with result of applying functor to
139    *         input.
140    */
141   template <typename F>
apply_no_holderstan::math::apply_vector_unary142   static inline auto apply_no_holder(const T& x, const F& f) {
143     return apply(x, f);
144   }
145 
146   /**
147    * Member function for applying a functor to a vector and subsequently
148    * returning a scalar.
149    *
150    * @tparam T Type of argument to which functor is applied.
151    * @tparam F Type of functor to apply.
152    * @param x Eigen input to which operation is applied.
153    * @param f functor to apply to std::vector input.
154    * @return scalar result of applying functor to input vector.
155    */
156   template <typename F>
reducestan::math::apply_vector_unary157   static inline auto reduce(const T& x, const F& f) {
158     return apply_vector_unary<T_map>::reduce(as_column_vector_or_scalar(x), f);
159   }
160 };
161 
162 /**
163  * Specialisation for use with nested containers (std::vectors).
164  * For each of the member functions, an std::vector with the appropriate
165  * type (vector or scalar) is returned.
166  *
167  * The returned scalar type is deduced to allow for cases where the input and
168  * return scalar types differ (e.g., functions implicitly promoting
169  * integers).
170  *
171  */
172 template <typename T>
173 struct apply_vector_unary<
174     T, require_std_vector_vt<is_container_or_var_matrix, T>> {
175   using T_vt = value_type_t<T>;
176 
177   /**
178    * Member function for applying a functor to each container in an std::vector
179    * and subsequently returning an std::vector of containers.
180    *
181    * @tparam T Type of argument to which functor is applied.
182    * @tparam F Type of functor to apply.
183    * @param x std::vector of containers to which operation is applied.
184    * @param f functor to apply to vector input.
185    * @return std::vector of containers with result of applying functor to
186    *         input.
187    */
188   template <typename F>
applystan::math::apply_vector_unary189   static inline auto apply(const T& x, const F& f) {
190     using T_return
191         = plain_type_t<decltype(apply_vector_unary<T_vt>::apply(x[0], f))>;
192     std::vector<T_return> result(x.size());
193     std::transform(x.begin(), x.end(), result.begin(), [&f](auto&& xx) {
194       return apply_vector_unary<T_vt>::apply_no_holder(xx, f);
195     });
196     return result;
197   }
198 
199   /**
200    * Member function for applying a functor to each container in an std::vector
201    * and subsequently returning an std::vector of containers.
202    *
203    * @tparam T Type of argument to which functor is applied.
204    * @tparam F Type of functor to apply.
205    * @param x std::vector of containers to which operation is applied.
206    * @param f functor to apply to vector input.
207    * @return std::vector of containers with result of applying functor to
208    *         input.
209    */
210   template <typename F>
apply_no_holderstan::math::apply_vector_unary211   static inline auto apply_no_holder(const T& x, const F& f) {
212     return apply(x, f);
213   }
214 
215   /**
216    * Member function for applying a functor to each container in an
217    * std::vector and subsequently returning an std::vector of scalars.
218    *
219    * @tparam T Type of argument to which functor is applied.
220    * @tparam F Type of functor to apply.
221    * @param x std::vector of containers to which operation is applied.
222    * @param f functor to apply to vector input.
223    * @return std::vector of scalars with result of applying functor to input.
224    */
225   template <typename F>
reducestan::math::apply_vector_unary226   static inline auto reduce(const T& x, const F& f) {
227     size_t x_size = x.size();
228     using T_return = decltype(apply_vector_unary<T_vt>::reduce(x[0], f));
229     std::vector<T_return> result(x_size);
230     for (size_t i = 0; i < x_size; ++i)
231       result[i] = apply_vector_unary<T_vt>::reduce(x[i], f);
232     return result;
233   }
234 };
235 
236 }  // namespace math
237 }  // namespace stan
238 #endif
239