1 #ifndef STAN_MATH_PRIM_FUN_GP_PERIODIC_COV_HPP
2 #define STAN_MATH_PRIM_FUN_GP_PERIODIC_COV_HPP
3 
4 #include <stan/math/prim/meta.hpp>
5 #include <stan/math/prim/err.hpp>
6 #include <stan/math/prim/fun/constants.hpp>
7 #include <stan/math/prim/fun/distance.hpp>
8 #include <stan/math/prim/fun/Eigen.hpp>
9 #include <stan/math/prim/fun/exp.hpp>
10 #include <stan/math/prim/fun/inv_square.hpp>
11 #include <stan/math/prim/fun/sin.hpp>
12 #include <stan/math/prim/fun/square.hpp>
13 #include <cmath>
14 #include <vector>
15 
16 namespace stan {
17 namespace math {
18 
19 /**
20  * Returns a periodic covariance matrix \f$ \mathbf{K} \f$ using the input \f$
21  * \mathbf{X} \f$. The elements of \f$ \mathbf{K} \f$ are defined as \f$
22  * \mathbf{K}_{ij} = k(\mathbf{X}_i,\mathbf{X}_j), \f$ where \f$ \mathbf{X}_i
23  * \f$ is the \f$i\f$-th row of \f$ \mathbf{X} \f$ and \n \f$
24  * k(\mathbf{x},\mathbf{x}^\prime) = \sigma^2 \exp\left(-\frac{2\sin^2(\pi
25  * |\mathbf{x}-\mathbf{x}^\prime|/p)}{\ell^2}\right), \f$ \n where \f$ \sigma^2
26  * \f$, \f$ \ell \f$ and \f$ p \f$ are the signal variance, length-scale and
27  * period.
28  *
29  * @tparam T_x type of std::vector elements of x.
30  *   T_x can be a scalar, an Eigen::Vector, or an Eigen::RowVector.
31  * @tparam T_sigma type of sigma
32  * @tparam T_l type of length-scale
33  * @tparam T_p type of period
34  *
35  * @param x std::vector of input elements.
36  *   This function assumes that all elements of x have the same size.
37  * @param sigma standard deviation of the signal
38  * @param l length-scale
39  * @param p period
40  * @return periodic covariance matrix
41  * @throw std::domain_error if sigma <= 0, l <= 0, p <= 0 or
42  *   x is nan or infinite
43  */
44 template <typename T_x, typename T_sigma, typename T_l, typename T_p>
45 inline typename Eigen::Matrix<return_type_t<T_x, T_sigma, T_l, T_p>,
46                               Eigen::Dynamic, Eigen::Dynamic>
gp_periodic_cov(const std::vector<T_x> & x,const T_sigma & sigma,const T_l & l,const T_p & p)47 gp_periodic_cov(const std::vector<T_x> &x, const T_sigma &sigma, const T_l &l,
48                 const T_p &p) {
49   using std::exp;
50   using std::sin;
51   const char *fun = "gp_periodic_cov";
52   check_positive(fun, "signal standard deviation", sigma);
53   check_positive(fun, "length-scale", l);
54   check_positive(fun, "period", p);
55   for (size_t n = 0; n < x.size(); ++n) {
56     check_not_nan(fun, "element of x", x[n]);
57   }
58 
59   Eigen::Matrix<return_type_t<T_x, T_sigma, T_l, T_p>, Eigen::Dynamic,
60                 Eigen::Dynamic>
61       cov(x.size(), x.size());
62 
63   size_t x_size = x.size();
64   if (x_size == 0) {
65     return cov;
66   }
67 
68   T_sigma sigma_sq = square(sigma);
69   T_l neg_two_inv_l_sq = -2.0 * inv_square(l);
70   T_p pi_div_p = pi() / p;
71   size_t block_size = 10;
72 
73   for (size_t jb = 0; jb < x_size; jb += block_size) {
74     for (size_t ib = jb; ib < x_size; ib += block_size) {
75       size_t j_end = std::min(x_size, jb + block_size);
76       for (size_t j = jb; j < j_end; ++j) {
77         cov.coeffRef(j, j) = sigma_sq;
78         size_t i_end = std::min(x_size, ib + block_size);
79         for (size_t i = std::max(ib, j + 1); i < i_end; ++i) {
80           cov.coeffRef(j, i) = cov.coeffRef(i, j)
81               = sigma_sq
82                 * exp(square(sin(pi_div_p * distance(x[i], x[j])))
83                       * neg_two_inv_l_sq);
84         }
85       }
86     }
87   }
88   return cov;
89 }
90 
91 /**
92  * Returns a periodic covariance matrix \f$ \mathbf{K} \f$ using inputs
93  * \f$ \mathbf{X}_1 \f$ and \f$ \mathbf{X}_2 \f$.
94  * The elements of \f$ \mathbf{K} \f$ are defined as
95  * \f$ \mathbf{K}_{ij} = k(\mathbf{X}_{1_i},\mathbf{X}_{2_j}), \f$ where
96  * \f$ \mathbf{X}_{1_i} \f$ and \f$ \mathbf{X}_{2_j} \f$  are the \f$i\f$-th
97  * and \f$j\f$-th rows of \f$ \mathbf{X}_1 \f$ and \f$ \mathbf{X}_2 \f$ and
98  * \n \f$ k(\mathbf{x},\mathbf{x}^\prime) = \sigma^2
99  * \exp\left(-\frac{2\sin^2(\pi
100  * |\mathbf{x}-\mathbf{x}^\prime|/p)}{\ell^2}\right), \f$ \n where \f$
101  * \sigma^2 \f$, \f$ \ell \f$ and \f$ p \f$ are the signal variance,
102  * length-scale and period.
103  *
104  * @tparam T_x1 type of std::vector elements of x1
105  *   T_x1 can be a scalar, an Eigen::Vector, or an Eigen::RowVector.
106  * @tparam T_x2 type of std::vector elements of x2
107  *   T_x2 can be a scalar, an Eigen::Vector, or an Eigen::RowVector.
108  * @tparam T_sigma type of sigma
109  * @tparam T_l type of length-scale
110  * @tparam T_p type of period
111  *
112  * @param x1 std::vector of first input elements
113  * @param x2 std::vector of second input elements.
114  *   This function assumes that all the elements of x1 and x2 have the same
115  * sizes.
116  * @param sigma standard deviation of the signal
117  * @param l length-scale
118  * @param p period
119  * @return periodic covariance matrix
120  * @throw std::domain_error if sigma <= 0, l <= 0, p <= 0 ,
121  *   x1 or x2 is nan or infinite
122  */
123 template <typename T_x1, typename T_x2, typename T_sigma, typename T_l,
124           typename T_p>
125 inline typename Eigen::Matrix<return_type_t<T_x1, T_x2, T_sigma, T_l, T_p>,
126                               Eigen::Dynamic, Eigen::Dynamic>
gp_periodic_cov(const std::vector<T_x1> & x1,const std::vector<T_x2> & x2,const T_sigma & sigma,const T_l & l,const T_p & p)127 gp_periodic_cov(const std::vector<T_x1> &x1, const std::vector<T_x2> &x2,
128                 const T_sigma &sigma, const T_l &l, const T_p &p) {
129   using std::exp;
130   using std::sin;
131   const char *fun = "gp_periodic_cov";
132   check_positive(fun, "signal standard deviation", sigma);
133   check_positive(fun, "length-scale", l);
134   check_positive(fun, "period", p);
135   for (size_t n = 0; n < x1.size(); ++n) {
136     check_not_nan(fun, "element of x1", x1[n]);
137   }
138   for (size_t n = 0; n < x2.size(); ++n) {
139     check_not_nan(fun, "element of x2", x2[n]);
140   }
141 
142   Eigen::Matrix<return_type_t<T_x1, T_x2, T_sigma, T_l, T_p>, Eigen::Dynamic,
143                 Eigen::Dynamic>
144       cov(x1.size(), x2.size());
145   if (x1.size() == 0 || x2.size() == 0) {
146     return cov;
147   }
148 
149   T_sigma sigma_sq = square(sigma);
150   T_l neg_two_inv_l_sq = -2.0 * inv_square(l);
151   T_p pi_div_p = pi() / p;
152   size_t block_size = 10;
153 
154   for (size_t ib = 0; ib < x1.size(); ib += block_size) {
155     for (size_t jb = 0; jb < x2.size(); jb += block_size) {
156       size_t j_end = std::min(x2.size(), jb + block_size);
157       for (size_t j = jb; j < j_end; ++j) {
158         size_t i_end = std::min(x1.size(), ib + block_size);
159         for (size_t i = ib; i < i_end; ++i) {
160           cov.coeffRef(i, j)
161               = sigma_sq
162                 * exp(square(sin(pi_div_p * distance(x1[i], x2[j])))
163                       * neg_two_inv_l_sq);
164         }
165       }
166     }
167   }
168   return cov;
169 }
170 }  // namespace math
171 }  // namespace stan
172 
173 #endif
174