EigenRand  0.3.0
MvNormal.h
Go to the documentation of this file.
1 
12 #ifndef EIGENRAND_MVDISTS_MVNORMAL_H
13 #define EIGENRAND_MVDISTS_MVNORMAL_H
14 
15 namespace Eigen
16 {
17  namespace Rand
18  {
19  namespace detail
20  {
21  template<typename _Scalar, Index Dim, typename _Mat>
22  Matrix<_Scalar, Dim, Dim> get_lt(const MatrixBase<_Mat>& mat)
23  {
24  LLT<Matrix<_Scalar, Dim, Dim>> llt(mat);
25  if (llt.info() == Eigen::Success)
26  {
27  return llt.matrixL();
28  }
29  else
30  {
31  SelfAdjointEigenSolver<Matrix<_Scalar, Dim, Dim>> solver(mat);
32  if (solver.info() != Eigen::Success)
33  {
34  throw std::runtime_error{ "The matrix cannot be solved!" };
35  }
36  return solver.eigenvectors() * solver.eigenvalues().cwiseMax(0).cwiseSqrt().asDiagonal();
37  }
38  }
39 
40  class FullMatrix {};
41  class LowerTriangular {};
42  class InvLowerTriangular {};
43  }
44 
45  constexpr detail::FullMatrix full_matrix;
46  constexpr detail::LowerTriangular lower_triangular;
47  constexpr detail::InvLowerTriangular inv_lower_triangular;
48 
49 
56  template<typename _Scalar, Index Dim = -1>
57  class MvNormalGen : public MvVecGenBase<MvNormalGen<_Scalar, Dim>, _Scalar, Dim>
58  {
59  Matrix<_Scalar, Dim, 1> mean;
60  Matrix<_Scalar, Dim, Dim> lt;
61  StdNormalGen<_Scalar> stdnorm;
62 
63  public:
72  template<typename MeanTy, typename LTTy>
73  MvNormalGen(const MatrixBase<MeanTy>& _mean, const MatrixBase<LTTy>& _lt, detail::LowerTriangular)
74  : mean{ _mean }, lt{ _lt }
75  {
76  eigen_assert(_mean.cols() == 1 && _mean.rows() == _lt.rows() && _lt.rows() == _lt.cols());
77  }
78 
87  template<typename MeanTy, typename CovTy>
88  MvNormalGen(const MatrixBase<MeanTy>& _mean, const MatrixBase<CovTy>& _cov, detail::FullMatrix = {})
89  : MvNormalGen{ _mean, detail::template get_lt<_Scalar, Dim>(_cov), lower_triangular }
90  {
91  }
92 
93  MvNormalGen(const MvNormalGen&) = default;
94  MvNormalGen(MvNormalGen&&) = default;
95 
96  MvNormalGen& operator=(const MvNormalGen&) = default;
97  MvNormalGen& operator=(MvNormalGen&&) = default;
98 
99  Index dims() const { return mean.rows(); }
100 
101  template<typename Urng>
102  inline auto generate(Urng&& urng, Index samples)
103  -> decltype((lt * stdnorm.template generate<Matrix<_Scalar, Dim, -1>>(mean.rows(), samples, std::forward<Urng>(urng))).colwise() + mean)
104  {
105  return (lt * stdnorm.template generate<Matrix<_Scalar, Dim, -1>>(mean.rows(), samples, std::forward<Urng>(urng))).colwise() + mean;
106  }
107 
108  template<typename Urng>
109  inline auto generate(Urng&& urng)
110  -> decltype((lt * stdnorm.template generate<Matrix<_Scalar, Dim, 1>>(mean.rows(), 1, std::forward<Urng>(urng))).colwise() + mean)
111  {
112  return (lt * stdnorm.template generate<Matrix<_Scalar, Dim, 1>>(mean.rows(), 1, std::forward<Urng>(urng))).colwise() + mean;
113  }
114  };
115 
124  template<typename MeanTy, typename CovTy>
125  inline auto makeMvNormalGen(const MatrixBase<MeanTy>& mean, const MatrixBase<CovTy>& cov)
126  -> MvNormalGen<typename MatrixBase<MeanTy>::Scalar, MatrixBase<MeanTy>::RowsAtCompileTime>
127  {
128  static_assert(
129  std::is_same<typename MatrixBase<MeanTy>::Scalar, typename MatrixBase<CovTy>::Scalar>::value,
130  "Derived::Scalar must be the same with `mean` and `cov`'s Scalar."
131  );
132  static_assert(
133  MatrixBase<MeanTy>::RowsAtCompileTime == MatrixBase<CovTy>::RowsAtCompileTime &&
134  MatrixBase<CovTy>::RowsAtCompileTime == MatrixBase<CovTy>::ColsAtCompileTime,
135  "assert: mean.RowsAtCompileTime == cov.RowsAtCompileTime && cov.RowsAtCompileTime == cov.ColsAtCompileTime"
136  );
137  return { mean, cov };
138  }
139 
148  template<typename MeanTy, typename LTTy>
149  inline auto makeMvNormalGenFromLt(const MatrixBase<MeanTy>& mean, const MatrixBase<LTTy>& lt)
150  -> MvNormalGen<typename MatrixBase<MeanTy>::Scalar, MatrixBase<MeanTy>::RowsAtCompileTime>
151  {
152  static_assert(
153  std::is_same<typename MatrixBase<MeanTy>::Scalar, typename MatrixBase<LTTy>::Scalar>::value,
154  "Derived::Scalar must be the same with `mean` and `lt`'s Scalar."
155  );
156  static_assert(
157  MatrixBase<MeanTy>::RowsAtCompileTime == MatrixBase<LTTy>::RowsAtCompileTime &&
158  MatrixBase<LTTy>::RowsAtCompileTime == MatrixBase<LTTy>::ColsAtCompileTime,
159  "assert: mean.RowsAtCompileTime == lt.RowsAtCompileTime && lt.RowsAtCompileTime == lt.ColsAtCompileTime"
160  );
161  return { mean, lt, lower_triangular };
162  }
163 
170  template<typename _Scalar, Index Dim>
171  class WishartGen : public MvMatGenBase<WishartGen<_Scalar, Dim>, _Scalar, Dim>
172  {
173  Index df;
174  Matrix<_Scalar, Dim, Dim> chol;
175  StdNormalGen<_Scalar> stdnorm;
176  std::vector<ChiSquaredGen<_Scalar>> chisqs;
177  public:
185  template<typename ScaleTy>
186  WishartGen(Index _df, const MatrixBase<ScaleTy>& _lt, detail::LowerTriangular)
187  : df{ _df }, chol{ _lt }
188  {
189  eigen_assert(df > chol.rows() - 1);
190  eigen_assert(chol.rows() == chol.cols());
191 
192  for (Index i = 0; i < chol.rows(); ++i)
193  {
194  chisqs.emplace_back(df - i);
195  }
196  }
197 
205  template<typename ScaleTy>
206  WishartGen(Index _df, const MatrixBase<ScaleTy>& _scale, detail::FullMatrix = {})
207  : WishartGen{ _df, detail::template get_lt<_Scalar, Dim>(_scale), lower_triangular }
208  {
209  eigen_assert(_scale.rows() == _scale.cols());
210  }
211 
212  WishartGen(const WishartGen&) = default;
213  WishartGen(WishartGen&&) = default;
214 
215  WishartGen& operator=(const WishartGen&) = default;
216  WishartGen& operator=(WishartGen&&) = default;
217 
218  Index dims() const { return chol.rows(); }
219 
220  template<typename Urng>
221  inline Matrix<_Scalar, Dim, -1> generate(Urng&& urng, Index samples)
222  {
223  const Index dim = chol.rows();
224  const Index normSamples = samples * dim * (dim - 1) / 2;
225  using ArrayXs = Array<_Scalar, -1, 1>;
226  Matrix<_Scalar, Dim, -1> rand_mat(dim, dim * samples), tmp(dim, dim * samples);
227 
228  _Scalar* ptr = tmp.data();
229  Map<ArrayXs>{ ptr, normSamples } = stdnorm.template generate<ArrayXs>(normSamples, 1, urng);
230  for (Index j = 0; j < samples; ++j)
231  {
232  for (Index i = 0; i < dim - 1; ++i)
233  {
234  rand_mat.col(i + j * dim).tail(dim - 1 - i) = Map<ArrayXs>{ ptr, dim - 1 - i };
235  ptr += dim - 1 - i;
236  }
237  }
238 
239  for (Index i = 0; i < dim; ++i)
240  {
241  _Scalar* ptr = tmp.data();
242  Map<ArrayXs>{ ptr, samples } = chisqs[i].template generate<ArrayXs>(samples, 1, urng).sqrt();
243  for (Index j = 0; j < samples; ++j)
244  {
245  rand_mat(i, i + j * dim) = *ptr++;
246  }
247  }
248 
249  for (Index j = 0; j < samples; ++j)
250  {
251  rand_mat.middleCols(j * dim, dim).template triangularView<StrictlyUpper>().setZero();
252  }
253  tmp.noalias() = chol * rand_mat;
254 
255  for (Index j = 0; j < samples; ++j)
256  {
257  auto t = tmp.middleCols(j * dim, dim);
258  rand_mat.middleCols(j * dim, dim).noalias() = t * t.transpose();
259  }
260  return rand_mat;
261  }
262 
263  template<typename Urng>
264  inline Matrix<_Scalar, Dim, -1> generate(Urng&& urng)
265  {
266  const Index dim = chol.rows();
267  const Index normSamples = dim * (dim - 1) / 2;
268  using ArrayXs = Array<_Scalar, -1, 1>;
269  Matrix<_Scalar, Dim, Dim> rand_mat(dim, dim);
270  Map<ArrayXs>{ rand_mat.data(), normSamples } = stdnorm.template generate<ArrayXs>(normSamples, 1, urng);
271 
272  for (Index i = 0; i < dim / 2; ++i)
273  {
274  rand_mat.col(dim - 2 - i).tail(i + 1) = rand_mat.col(i).head(i + 1);
275  }
276 
277  for (Index i = 0; i < dim; ++i)
278  {
279  rand_mat(i, i) = chisqs[i].template generate<Array<_Scalar, 1, 1>>(1, 1, urng).sqrt()(0);
280  }
281  rand_mat.template triangularView<StrictlyUpper>().setZero();
282 
283  auto t = (chol * rand_mat).eval();
284  return (t * t.transpose()).eval();
285  }
286  };
287 
295  template<typename ScaleTy>
296  inline auto makeWishartGen(Index df, const MatrixBase<ScaleTy>& scale)
297  -> WishartGen<typename MatrixBase<ScaleTy>::Scalar, MatrixBase<ScaleTy>::RowsAtCompileTime>
298  {
299  static_assert(
300  MatrixBase<ScaleTy>::RowsAtCompileTime == MatrixBase<ScaleTy>::ColsAtCompileTime,
301  "assert: scale.RowsAtCompileTime == scale.ColsAtCompileTime"
302  );
303  return { df, scale };
304  }
305 
313  template<typename LTTy>
314  inline auto makeWishartGenFromLt(Index df, const MatrixBase<LTTy>& lt)
315  -> WishartGen<typename MatrixBase<LTTy>::Scalar, MatrixBase<LTTy>::RowsAtCompileTime>
316  {
317  static_assert(
318  MatrixBase<LTTy>::RowsAtCompileTime == MatrixBase<LTTy>::ColsAtCompileTime,
319  "assert: lt.RowsAtCompileTime == lt.ColsAtCompileTime"
320  );
321  return { df, lt, lower_triangular };
322  }
323 
330  template<typename _Scalar, Index Dim>
331  class InvWishartGen : public MvMatGenBase<InvWishartGen<_Scalar, Dim>, _Scalar, Dim>
332  {
333  Index df;
334  Matrix<_Scalar, Dim, Dim> chol;
335  StdNormalGen<_Scalar> stdnorm;
336  std::vector<ChiSquaredGen<_Scalar>> chisqs;
337  public:
345  template<typename ScaleTy>
346  InvWishartGen(Index _df, const MatrixBase<ScaleTy>& _ilt, detail::InvLowerTriangular)
347  : df{ _df }, chol{ _ilt }
348  {
349  eigen_assert(df > chol.rows() - 1);
350  eigen_assert(chol.rows() == chol.cols());
351 
352  for (Index i = 0; i < chol.rows(); ++i)
353  {
354  chisqs.emplace_back(df - i);
355  }
356  }
357 
365  template<typename ScaleTy>
366  InvWishartGen(Index _df, const MatrixBase<ScaleTy>& _scale, detail::FullMatrix = {})
367  : InvWishartGen{ _df, detail::template get_lt<_Scalar, Dim>(_scale.inverse()), inv_lower_triangular }
368  {
369  eigen_assert(_scale.rows() == _scale.cols());
370  }
371 
372  InvWishartGen(const InvWishartGen&) = default;
373  InvWishartGen(InvWishartGen&&) = default;
374 
375  InvWishartGen& operator=(const InvWishartGen&) = default;
376  InvWishartGen& operator=(InvWishartGen&&) = default;
377 
378  Index dims() const { return chol.rows(); }
379 
380  template<typename Urng>
381  inline Matrix<_Scalar, Dim, -1> generate(Urng&& urng, Index samples)
382  {
383  const Index dim = chol.rows();
384  const Index normSamples = samples * dim * (dim - 1) / 2;
385  using ArrayXs = Array<_Scalar, -1, 1>;
386  Matrix<_Scalar, Dim, -1> rand_mat(dim, dim * samples), tmp(dim, dim * samples);
387 
388  _Scalar* ptr = tmp.data();
389  Map<ArrayXs>{ ptr, normSamples } = stdnorm.template generate<ArrayXs>(normSamples, 1, urng);
390  for (Index j = 0; j < samples; ++j)
391  {
392  for (Index i = 0; i < dim - 1; ++i)
393  {
394  rand_mat.col(i + j * dim).tail(dim - 1 - i) = Map<ArrayXs>{ ptr, dim - 1 - i };
395  ptr += dim - 1 - i;
396  }
397  }
398 
399  for (Index i = 0; i < dim; ++i)
400  {
401  _Scalar* ptr = tmp.data();
402  Map<ArrayXs>{ ptr, samples } = chisqs[i].template generate<ArrayXs>(samples, 1, urng).sqrt();
403  for (Index j = 0; j < samples; ++j)
404  {
405  rand_mat(i, i + j * dim) = *ptr++;
406  }
407  }
408 
409  for (Index j = 0; j < samples; ++j)
410  {
411  rand_mat.middleCols(j * dim, dim).template triangularView<StrictlyUpper>().setZero();
412  }
413  tmp.noalias() = chol * rand_mat;
414 
415  auto id = Eigen::Matrix<_Scalar, Dim, Dim>::Identity(dim, dim);
416  for (Index j = 0; j < samples; ++j)
417  {
418  auto t = tmp.middleCols(j * dim, dim);
419  auto u = rand_mat.middleCols(j * dim, dim);
420  u.noalias() = t.template triangularView<Lower>().solve(id);
421  t.noalias() = u.transpose() * u;
422  }
423  return tmp;
424  }
425 
426  template<typename Urng>
427  inline Matrix<_Scalar, Dim, -1> generate(Urng&& urng)
428  {
429  const Index dim = chol.rows();
430  const Index normSamples = dim * (dim - 1) / 2;
431  using ArrayXs = Array<_Scalar, -1, 1>;
432  Matrix<_Scalar, Dim, Dim> rand_mat(dim, dim);
433  Map<ArrayXs>{ rand_mat.data(), normSamples } = stdnorm.template generate<ArrayXs>(normSamples, 1, urng);
434 
435  for (Index i = 0; i < dim / 2; ++i)
436  {
437  rand_mat.col(dim - 2 - i).tail(i + 1) = rand_mat.col(i).head(i + 1);
438  }
439 
440  for (Index i = 0; i < dim; ++i)
441  {
442  rand_mat(i, i) = chisqs[i].template generate<Array<_Scalar, 1, 1>>(1, 1, urng).sqrt()(0);
443  }
444  rand_mat.template triangularView<StrictlyUpper>().setZero();
445 
446  auto t = (chol * rand_mat).eval();
447  auto id = Eigen::Matrix<_Scalar, Dim, Dim>::Identity(dim, dim);
448  rand_mat.noalias() = t.template triangularView<Lower>().solve(id);
449 
450  return (rand_mat.transpose() * rand_mat).eval();
451  }
452  };
453 
461  template<typename ScaleTy>
462  inline auto makeInvWishartGen(Index df, const MatrixBase<ScaleTy>& scale)
463  -> InvWishartGen<typename MatrixBase<ScaleTy>::Scalar, MatrixBase<ScaleTy>::RowsAtCompileTime>
464  {
465  static_assert(
466  MatrixBase<ScaleTy>::RowsAtCompileTime == MatrixBase<ScaleTy>::ColsAtCompileTime,
467  "assert: scale.RowsAtCompileTime == scale.ColsAtCompileTime"
468  );
469  return { df, scale };
470  }
471 
479  template<typename ILTTy>
480  inline auto makeInvWishartGenFromIlt(Index df, const MatrixBase<ILTTy>& ilt)
481  -> InvWishartGen<typename MatrixBase<ILTTy>::Scalar, MatrixBase<ILTTy>::RowsAtCompileTime>
482  {
483  static_assert(
484  MatrixBase<ILTTy>::RowsAtCompileTime == MatrixBase<ILTTy>::ColsAtCompileTime,
485  "assert: ilt.RowsAtCompileTime == ilt.ColsAtCompileTime"
486  );
487  return { df, ilt, inv_lower_triangular };
488  }
489  }
490 }
491 
492 #endif
Generator of real matrices on a inverse Wishart distribution.
Definition: MvNormal.h:332
InvWishartGen(Index _df, const MatrixBase< ScaleTy > &_scale, detail::FullMatrix={})
Construct a new inverse Wishart generator.
Definition: MvNormal.h:366
InvWishartGen(Index _df, const MatrixBase< ScaleTy > &_ilt, detail::InvLowerTriangular)
Construct a new inverse Wishart generator.
Definition: MvNormal.h:346
Base class of all multivariate random matrix generators.
Definition: Basic.h:129
Generator of real vectors on a multivariate normal distribution.
Definition: MvNormal.h:58
MvNormalGen(const MatrixBase< MeanTy > &_mean, const MatrixBase< LTTy > &_lt, detail::LowerTriangular)
Construct a new multivariate normal generator from lower triangular matrix of decomposed covariance.
Definition: MvNormal.h:73
MvNormalGen(const MatrixBase< MeanTy > &_mean, const MatrixBase< CovTy > &_cov, detail::FullMatrix={})
Construct a new multivariate normal generator from covariance matrix.
Definition: MvNormal.h:88
Base class of all multivariate random vector generators.
Definition: Basic.h:84
Generator of reals on the standard normal distribution.
Definition: NormalExp.h:27
Generator of real matrices on a Wishart distribution.
Definition: MvNormal.h:172
WishartGen(Index _df, const MatrixBase< ScaleTy > &_scale, detail::FullMatrix={})
Construct a new Wishart generator from scale matrix.
Definition: MvNormal.h:206
WishartGen(Index _df, const MatrixBase< ScaleTy > &_lt, detail::LowerTriangular)
Construct a new Wishart generator from lower triangular matrix of decomposed scale.
Definition: MvNormal.h:186
auto makeWishartGenFromLt(Index df, const MatrixBase< LTTy > &lt) -> WishartGen< typename MatrixBase< LTTy >::Scalar, MatrixBase< LTTy >::RowsAtCompileTime >
helper function constructing Eigen::Rand::WishartGen
Definition: MvNormal.h:314
auto makeMvNormalGen(const MatrixBase< MeanTy > &mean, const MatrixBase< CovTy > &cov) -> MvNormalGen< typename MatrixBase< MeanTy >::Scalar, MatrixBase< MeanTy >::RowsAtCompileTime >
helper function constructing Eigen::Rand::MvNormal
Definition: MvNormal.h:125
auto makeWishartGen(Index df, const MatrixBase< ScaleTy > &scale) -> WishartGen< typename MatrixBase< ScaleTy >::Scalar, MatrixBase< ScaleTy >::RowsAtCompileTime >
helper function constructing Eigen::Rand::WishartGen
Definition: MvNormal.h:296
auto makeMvNormalGenFromLt(const MatrixBase< MeanTy > &mean, const MatrixBase< LTTy > &lt) -> MvNormalGen< typename MatrixBase< MeanTy >::Scalar, MatrixBase< MeanTy >::RowsAtCompileTime >
helper function constructing Eigen::Rand::MvNormal
Definition: MvNormal.h:149
auto makeInvWishartGen(Index df, const MatrixBase< ScaleTy > &scale) -> InvWishartGen< typename MatrixBase< ScaleTy >::Scalar, MatrixBase< ScaleTy >::RowsAtCompileTime >
helper function constructing Eigen::Rand::InvWishartGen
Definition: MvNormal.h:462
auto makeInvWishartGenFromIlt(Index df, const MatrixBase< ILTTy > &ilt) -> InvWishartGen< typename MatrixBase< ILTTy >::Scalar, MatrixBase< ILTTy >::RowsAtCompileTime >
helper function constructing Eigen::Rand::InvWishartGen
Definition: MvNormal.h:480