1 #ifndef STAN_MATH_PRIM_MAT_FUN_UNIT_VECTOR_CONSTRAIN_HPP
2 #define STAN_MATH_PRIM_MAT_FUN_UNIT_VECTOR_CONSTRAIN_HPP
3 
4 #include <stan/math/prim/arr/err/check_nonzero_size.hpp>
5 #include <stan/math/prim/mat/fun/Eigen.hpp>
6 #include <stan/math/prim/mat/fun/dot_self.hpp>
7 #include <stan/math/prim/mat/err/check_vector.hpp>
8 #include <stan/math/prim/scal/err/check_positive_finite.hpp>
9 #include <stan/math/rev/core.hpp>
10 #include <stan/math/rev/mat/fun/dot_self.hpp>
11 #include <cmath>
12 
13 namespace stan {
14 namespace math {
15 
16 namespace internal {
17 class unit_vector_elt_vari : public vari {
18  private:
19   vari** y_;
20   const double* unit_vector_y_;
21   const int size_;
22   const int idx_;
23   const double norm_;
24 
25  public:
unit_vector_elt_vari(double val,vari ** y,const double * unit_vector_y,int size,int idx,double norm)26   unit_vector_elt_vari(double val, vari** y, const double* unit_vector_y,
27                        int size, int idx, double norm)
28       : vari(val),
29         y_(y),
30         unit_vector_y_(unit_vector_y),
31         size_(size),
32         idx_(idx),
33         norm_(norm) {}
chain()34   void chain() {
35     const double cubed_norm = norm_ * norm_ * norm_;
36     for (int m = 0; m < size_; ++m) {
37       y_[m]->adj_
38           -= adj_ * unit_vector_y_[m] * unit_vector_y_[idx_] / cubed_norm;
39       if (m == idx_)
40         y_[m]->adj_ += adj_ / norm_;
41     }
42   }
43 };
44 }  // namespace internal
45 
46 /**
47  * Return the unit length vector corresponding to the free vector y.
48  * See https://en.wikipedia.org/wiki/N-sphere#Generating_random_points
49  *
50  * @param y vector of K unrestricted variables
51  * @return Unit length vector of dimension K
52  * @tparam T Scalar type.
53  **/
54 template <int R, int C>
unit_vector_constrain(const Eigen::Matrix<var,R,C> & y)55 Eigen::Matrix<var, R, C> unit_vector_constrain(
56     const Eigen::Matrix<var, R, C>& y) {
57   check_vector("unit_vector", "y", y);
58   check_nonzero_size("unit_vector", "y", y);
59 
60   vari** y_vi_array = reinterpret_cast<vari**>(
61       ChainableStack::instance().memalloc_.alloc(sizeof(vari*) * y.size()));
62   for (int i = 0; i < y.size(); ++i)
63     y_vi_array[i] = y.coeff(i).vi_;
64 
65   Eigen::VectorXd y_d(y.size());
66   for (int i = 0; i < y.size(); ++i)
67     y_d.coeffRef(i) = y.coeff(i).val();
68 
69   const double norm = y_d.norm();
70   check_positive_finite("unit_vector", "norm", norm);
71   Eigen::VectorXd unit_vector_d = y_d / norm;
72 
73   double* unit_vector_y_d_array = reinterpret_cast<double*>(
74       ChainableStack::instance().memalloc_.alloc(sizeof(double) * y_d.size()));
75   for (int i = 0; i < y_d.size(); ++i)
76     unit_vector_y_d_array[i] = unit_vector_d.coeff(i);
77 
78   Eigen::Matrix<var, R, C> unit_vector_y(y.size());
79   for (int k = 0; k < y.size(); ++k)
80     unit_vector_y.coeffRef(k) = var(new internal::unit_vector_elt_vari(
81         unit_vector_d[k], y_vi_array, unit_vector_y_d_array, y.size(), k,
82         norm));
83   return unit_vector_y;
84 }
85 
86 /**
87  * Return the unit length vector corresponding to the free vector y.
88  * See https://en.wikipedia.org/wiki/N-sphere#Generating_random_points
89  *
90  * @param y vector of K unrestricted variables
91  * @return Unit length vector of dimension K
92  * @param lp Log probability reference to increment.
93  * @tparam T Scalar type.
94  **/
95 template <int R, int C>
unit_vector_constrain(const Eigen::Matrix<var,R,C> & y,var & lp)96 Eigen::Matrix<var, R, C> unit_vector_constrain(
97     const Eigen::Matrix<var, R, C>& y, var& lp) {
98   Eigen::Matrix<var, R, C> x = unit_vector_constrain(y);
99   lp -= 0.5 * dot_self(y);
100   return x;
101 }
102 
103 }  // namespace math
104 }  // namespace stan
105 #endif
106