Virtual Testbed
Ship dynamics simulator for extreme conditions
yule_walker_solver.cc
1 #include <vtestbed/base/for_loop.hh>
2 #include <vtestbed/config/real_type.hh>
3 #include <vtestbed/core/debug.hh>
4 #include <vtestbed/core/yule_walker_solver.hh>
5 #include <vtestbed/linalg/linear_algebra.hh>
6 #include <vtestbed/profile/profile.hh>
7 
8 #include <algorithm>
9 #include <cassert>
10 #include <fstream>
11 #include <stdexcept>
12 #include <unordered_map>
13 
14 //#define VTB_DEBUG_YULE_WALKER_SOLVER
15 #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
16 #include <iostream>
17 #endif
18 
19 namespace {
20 
21  inline blitz::TinyVector<int,1>
22  vector_shape(int a) {
23  return blitz::shape(3*a*a + 3*a + 1);
24  }
25 
26  inline blitz::TinyVector<int,2>
27  matrix_shape(int a, int b) {
28  return blitz::shape(3*a*a + 3*a + 1, 3*b*b + 3*b + 1);
29  }
30 
31  inline blitz::TinyVector<int,2>
32  submatrix_shape(int a, int b) {
33  return blitz::shape(6*a, 3*b*b + 3*b + 1);
34  }
35 
44  template <class T>
46  rho_vector(int i, int j, int k, int d, const blitz::Array<T,3>& acf) {
47  #define ACF(x,y,z) acf(std::abs(x), std::abs(y), std::abs(z))
48  typedef vtb::linalg::Vector<T> vector_type;
49  using blitz::Range;
50  vector_type rho_ijkd(vector_shape(d));
51  T* data = rho_ijkd.data();
52  *data++ = ACF(i-d, j-d, k-d);
61  for (int e=1; e<=d; ++e) {
69  for (int idx=0; idx<e; ++idx) {
70  *data++ = ACF(i-d+e, j-d+idx, k-d);
71  }
79  for (int idx=0; idx<e; ++idx) {
80  *data++ = ACF(i-d+e-idx, j-d+e, k-d);
81  }
89  for (int idx=0; idx<e; ++idx) {
90  *data++ = ACF(i-d, j-d+e, k-d+idx);
91  }
99  for (int idx=0; idx<e; ++idx) {
100  *data++ = ACF(i-d, j-d+e-idx, k-d+e);
101  }
109  for (int idx=0; idx<e; ++idx) {
110  *data++ = ACF(i-d+idx, j-d, k-d+e);
111  }
119  for (int idx=0; idx<e; ++idx) {
120  *data++ = ACF(i-d+e, j-d, k-d+e-idx);
121  }
122  }
123  #undef ACF
124  return rho_ijkd;
125  }
126 
131  template <class T>
132  inline T
133  rho_scalar(int i, int j, int k, const blitz::Array<T,3>& acf) {
134  #define ACF(x,y,z) acf(std::abs(x), std::abs(y), std::abs(z))
135  return ACF(i, j, k);
136  #undef ACF
137  }
138 
148  template <class T>
150  R_matrix(int a, int b, const blitz::Array<T,3>& acf) {
151  using blitz::Range;
152  typedef vtb::linalg::Matrix<T> matrix_type;
153  matrix_type R_ab(matrix_shape(a, b));
154  R_ab(0, Range::all()) = rho_vector(a, a, a, b, acf);
163  int offset = 1;
164  for (int e=1; e<=a; ++e) {
172  for (int idx=0; idx<e; ++idx) {
173  R_ab(offset++, Range::all()) = rho_vector(a-e, a-idx, a, b, acf);
174  }
182  for (int idx=0; idx<e; ++idx) {
183  R_ab(offset++, Range::all()) = rho_vector(a-e+idx, a-e, a, b, acf);
184  }
192  for (int idx=0; idx<e; ++idx) {
193  R_ab(offset++, Range::all()) = rho_vector(a, a-e, a-idx, b, acf);
194  }
202  for (int idx=0; idx<e; ++idx) {
203  R_ab(offset++, Range::all()) = rho_vector(a, a-e+idx, a-e, b, acf);
204  }
212  for (int idx=0; idx<e; ++idx) {
213  R_ab(offset++, Range::all()) = rho_vector(a-idx, a, a-e, b, acf);
214  }
222  for (int idx=0; idx<e; ++idx) {
223  R_ab(offset++, Range::all()) = rho_vector(a-e, a, a-e+idx, b, acf);
224  }
225  }
226  return R_ab;
227  }
228 
232  template <class T>
234  R_vector_b0(int a, const blitz::Array<T,3>& acf) {
235  using blitz::Range;
236  using blitz::shape;
237  typedef vtb::linalg::Vector<T> vector_type;
238  vector_type R_ab(vector_shape(a));
239  T* data = R_ab.data();
248  *data++ = rho_scalar(a, a, a, acf);
249  for (int e=1; e<=a; ++e) {
257  for (int idx=0; idx<e; ++idx) {
258  *data++ = rho_scalar(a-e, a-idx, a, acf);
259  }
267  for (int idx=0; idx<e; ++idx) {
268  *data++ = rho_scalar(a-e+idx, a-e, a, acf);
269  }
277  for (int idx=0; idx<e; ++idx) {
278  *data++ = rho_scalar(a, a-e, a-idx, acf);
279  }
287  for (int idx=0; idx<e; ++idx) {
288  *data++ = rho_scalar(a, a-e+idx, a-e, acf);
289  }
297  for (int idx=0; idx<e; ++idx) {
298  *data++ = rho_scalar(a-idx, a, a-e, acf);
299  }
307  for (int idx=0; idx<e; ++idx) {
308  *data++ = rho_scalar(a-e, a, a-e+idx, acf);
309  }
310  }
311  return R_ab;
312  }
313 
318  template <class T>
320  R_vector_a0(int b, blitz::Array<T,3> acf) {
321  return rho_vector(0, 0, 0, b, acf);
322  }
323 
325  template <class T>
326  inline blitz::Array<T,3>
327  result_array(blitz::Array<vtb::linalg::Vector<T>,1> beta, int order) {
328  blitz::Array<T,3> result(blitz::shape(order, order, order));
329  result = 0;
330  result(0,0,0) = 0;
331  for (int d=1; d<order; ++d) {
332  const T* data = beta(d).data();
333  result(d,d,d) = *data++;
334  for (int e=1; e<=d; ++e) {
335  for (int idx=0; idx<e; ++idx) {
336  result(d-e,d-idx,d) = *data++;
337  }
338  for (int idx=0; idx<e; ++idx) {
339  result(d-e+idx,d-e,d) = *data++;
340  }
341  for (int idx=0; idx<e; ++idx) {
342  result(d,d-e,d-idx) = *data++;
343  }
344  for (int idx=0; idx<e; ++idx) {
345  result(d,d-e+idx,d-e) = *data++;
346  }
347  for (int idx=0; idx<e; ++idx) {
348  result(d-idx,d,d-e) = *data++;
349  }
350  for (int idx=0; idx<e; ++idx) {
351  result(d-e,d,d-e+idx) = *data++;
352  }
353  }
354  }
355  return result;
356  }
357 
358  /*
359  template <class T>
360  inline blitz::Array<T,2>
361  operator*(blitz::Array<T,2> lhs, blitz::Array<T,2> rhs) {
362  return linalg::multiply(lhs, rhs);
363  }
364  */
365 
366  template <class T, int N>
367  inline bool
368  is_square(const blitz::Array<T,N>& rhs) {
369  const int first = rhs.extent(0);
370  bool result = true;
371  for (int i=1; i<rhs.dimensions(); ++i) {
372  if (rhs.extent(i) != first) {
373  result = false;
374  break;
375  }
376  }
377  return result;
378  }
379 
380  template <class T>
381  void
382  append_column_block(
384  const vtb::linalg::Matrix<T>& rhs
385  ) {
386  if (lhs.numElements() == 0) {
387  lhs.resize(rhs.shape());
388  lhs = rhs;
389  } else {
390  using blitz::Range;
391  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
392  if (lhs.rows() != rhs.rows()) {
393  throw std::runtime_error{"bad shape"};
394  }
395  #endif
396  const int old_cols = lhs.columns();
397  lhs.resizeAndPreserve(lhs.rows(), old_cols + rhs.columns());
398  lhs(Range::all(), Range(old_cols, blitz::toEnd)) = rhs;
399  }
400  }
401 
402  template <class T>
403  void
404  append_row_block(
406  const vtb::linalg::Matrix<T>& rhs
407  ) {
408  if (lhs.numElements() == 0) {
409  lhs.resize(rhs.shape());
410  lhs = rhs;
411  } else {
412  using blitz::Range;
413  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
414  if (lhs.cols() != rhs.cols()) {
415  throw std::runtime_error{"bad shape"};
416  }
417  #endif
418  const int old_rows = lhs.rows();
419  lhs.resizeAndPreserve(old_rows + rhs.rows(), lhs.columns());
420  lhs(Range(old_rows, blitz::toEnd), Range::all()) = rhs;
421  }
422  }
423 
424 }
425 
426 namespace std {
427 
428  template <>
429  struct hash<std::tuple<int,int>>: public std::hash<int> {
430  inline size_t
431  operator()(const std::tuple<int,int>& rhs) const noexcept {
432  typedef std::hash<int> base;
433  return base::operator()(std::get<0>(rhs)) ^
434  base::operator()(std::get<1>(rhs));
435  }
436  };
437 
438  template <>
439  struct hash<std::tuple<int,int,int>>: public std::hash<int> {
440  inline size_t
441  operator()(const std::tuple<int,int,int>& rhs) const noexcept {
442  typedef std::hash<int> base;
443  return base::operator()(std::get<0>(rhs)) ^
444  base::operator()(std::get<1>(rhs)) ^
445  base::operator()(std::get<2>(rhs));
446  }
447  };
448 
449 }
450 
451 template <class T>
452 blitz::TinyVector<int,3>
453 vtb::core::chop_right(const blitz::Array<T,3>& rhs, T eps) {
454  using blitz::abs;
455  using blitz::all;
456  using blitz::Range;
457  const auto& _ = Range::all();
458  // shrink third dimension
459  int k = rhs.extent(2) - 1;
460  while (k >= 1 && all(abs(rhs(_,_,k)) < eps)) {
461  --k;
462  }
463  // shrink second dimension
464  int j = rhs.extent(1) - 1;
465  while (j >= 1 && all(abs(rhs(_, j, Range(0,k))) < eps)) {
466  --j;
467  }
468  // shrink first dimension
469  int i = rhs.extent(0) - 1;
470  while (i >= 1 && all(abs(rhs(i, Range(0,j), Range(0,k))) < eps)) {
471  --i;
472  }
473  return {i+1,j+1,k+1};
474 }
475 
476 template <class T>
477 blitz::TinyVector<int,4>
478 vtb::core::chop_right(const blitz::Array<T,4>& rhs, T eps) {
479  using blitz::abs;
480  using blitz::all;
481  using blitz::Range;
482  const auto& _ = Range::all();
483  // shrink fourth dimension
484  int l = rhs.extent(3) - 1;
485  while (l >= 1 && all(abs(rhs(_,_,_,l)) < eps)) {
486  --l;
487  }
488  // shrink third dimension
489  int k = rhs.extent(2) - 1;
490  while (k >= 1 && all(abs(rhs(_, _, k, Range(0,l)) < eps))) {
491  --k;
492  }
493  // shrink second dimension
494  int j = rhs.extent(1) - 1;
495  while (j >= 1 && all(abs(rhs(_, j, Range(0,k), Range(0,l))) < eps)) {
496  --j;
497  }
498  // shrink first dimension
499  int i = rhs.extent(0) - 1;
500  while (i >= 1 && all(abs(rhs(i, Range(0,j), Range(0,k), Range(0,l))) < eps)) {
501  --i;
502  }
503  return {i+1,j+1,k+1,l+1};
504 }
505 
506 template <class T, int N>
507 auto
510  VTB_PROFILE_BLOCK("Choi_yule_walker_solver");
511  #define PHI(i,j,k) Phi[std::make_tuple(i,j,k)]
512  typedef vtb::linalg::Matrix<T> matrix_type;
513  typedef vtb::linalg::Vector<T> vector_type;
514  typedef std::tuple<int,int,int> key_type;
515  typedef std::unordered_map<key_type,matrix_type> sparse_array_type;
516  typedef blitz::Array<vector_type,1> vector_array_type;
517  typedef blitz::Array<matrix_type,1> matrix_array_type;
518  using blitz::Range;
519  using blitz::shape;
520  using blitz::any;
521  if (!is_square(acf_in)) {
522  throw std::invalid_argument("ACF is not square");
523  }
524  const T variance = acf_variance(acf_in);
525  if (variance <= 0) {
526  throw std::invalid_argument("ACF variance <= 0");
527  }
528  array_type acf(acf_in/variance);
530  int max_order = this->max_order();
531  if (max_order == 0) {
532  max_order = blitz::max(acf.shape())-1;
533  }
534  sparse_array_type Phi;
535  vector_array_type Pi_l(shape(max_order+2));
536  vector_array_type Pi_lp1(shape(max_order+2));
537  matrix_array_type Theta(shape(max_order+2));
538  matrix_type R_1_1 = R_matrix(1, 1, acf);
539  vector_type R_1_0 = R_vector_b0(1, acf);
540  vector_type R_0_1 = R_vector_a0(1, acf);
541 // std::clog << "R_1_1=" << R_1_1 << std::endl;
542 // std::clog << "R_1_0=" << R_1_0 << std::endl;
543 // std::clog << "R_0_1=" << R_0_1 << std::endl;
544  matrix_type R_sup_1_1(R_1_1.shape());
545  R_sup_1_1 = R_1_1;
546  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
547  if (!R_sup_1_1.is_symmetric()) {
548  throw std::runtime_error{"R matrix is not symmetric"};
549  }
550  #endif
551  R_sup_1_1.inverse_self();
552 // std::clog << "R_1_1^{-1}=" << R_sup_1_1 << std::endl;
553 // std::clog << "R_1_1*R_1_1^{-1}=" << (R_1_1*R_sup_1_1) << std::endl;
554  vector_type Pi_2_1 = R_sup_1_1 * R_1_0;
555 // std::clog << "Pi_2_1=" << Pi_2_1 << std::endl;
556  T lambda = T(1) - blitz::dot(R_0_1, Pi_2_1);
557  T var0 = variance;
558  T var = variance*lambda;
559  if (var <= 0) {
560  throw std::runtime_error("white noise variance <= 0");
561  }
562  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
563  std::clog << __func__ << ':'
564  << "order=" << 1
565  << ",var=" << var
566  << ",lambda=" << lambda
567  << '\n';
568  #endif
569  Pi_l(1).reference(Pi_2_1);
570  PHI(2,1,0).reference(matrix_type(R_sup_1_1*R_matrix(1, 2, acf)));
571  bool changed = false;
572  int l = 1;
573  do {
574  ++l;
575  var0 = var;
576  matrix_type R_l_l = R_matrix(l, l, acf);
577  vector_type R_l_0 = R_vector_b0(l, acf);
578  matrix_type sum1(R_l_l.shape());
579  sum1 = 0;
580  vector_type sum2(R_l_0.shape());
581  sum2 = 0;
582  for (int m=1; m<=l-1; ++m) {
583  matrix_type R_l_m(R_matrix(l, m, acf));
584  sum1 += R_l_m*PHI(l,m,0);
585 // sum2 += linalg::multiply_by_column_vector(R_l_m, Pi_l(m));
586 // R_l_m.transpose_self();
587  sum2 += R_l_m.transpose() * Pi_l(m);
588  }
589  matrix_type Theta_l(R_l_l - sum1);
590  vector_type h_l(R_l_0 - sum2);
591 // std::clog << "h_l=" << h_l << std::endl;
592 // std::clog << "Theta_l=" << Theta_l << std::endl;
593  Theta_l.inverse_self();
594  Theta(l).reference(Theta_l);
595 // { std::ifstream("/tmp/z") >> Theta_l; }
596 // std::clog << "Theta_l^{-1}=" << Theta_l << std::endl;
597  Pi_lp1(l).reference(Theta_l * h_l);
598 // std::clog << "Pi(l+1,l)=" << Pi_lp1(l) << std::endl;
599  for (int a=1; a<=l-1; ++a) {
600  Pi_lp1(a).reference(vector_type(
601 // Pi_l(a) - linalg::multiply_by_column_vector(PHI(l,a,0), Pi_lp1(l))
602  Pi_l(a) - PHI(l,a,0).transpose() * Pi_lp1(l)
603  ));
604  }
605  lambda -= blitz::dot(h_l, Pi_lp1(l));
606  var = variance*lambda;
607  if (var <= 0) {
608  throw std::runtime_error("white noise variance <= 0");
609  }
610  if (l < max_order) {
611  PHI(2,1,l-1).reference(R_sup_1_1*R_matrix(1, l+1, acf));
612  for (int m=2; m<=l; ++m) {
613  matrix_type R_m_lp1(R_matrix(m,l+1,acf));
614  matrix_type sum3(R_m_lp1.shape());
615  sum3 = 0;
616  for (int n=1; n<=m-1; ++n) {
617  sum3 += R_matrix(m,n,acf)*PHI(m,n,l-m+1);
618  }
619  PHI(m+1,m,l-m).reference(Theta(m)*matrix_type(R_m_lp1 - sum3));
620  for (int a=1; a<=m-1; ++a) {
621  PHI(m+1,a,l-m).reference(matrix_type(
622  PHI(m,a,l-m+1) - PHI(m,a,0)*PHI(m+1,m,l-m)
623  ));
624  }
625  }
626  }
627  blitz::swap(Pi_l, Pi_lp1);
628  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
629  std::clog
630  << __func__ << ':'
631  << "order=" << l
632  << ",var=" << var
633  << ",lambda=" << lambda
634  << '\n';
635  #endif
636  changed = !this->variance_has_not_changed_much(var, var0);
637  } while (l < max_order && changed);
638  #undef PHI
639  array_type result;
640  this->white_noise_variance(var);
641  result.reference(result_array(Pi_lp1, l));
642  if (this->_chop) {
643  result.resizeAndPreserve(chop_right(result, this->_chopepsilon));
644  }
645  return result;
646 }
647 
648 template blitz::TinyVector<int,3>
649 vtb::core::chop_right(
650  const blitz::Array<VTB_REAL_TYPE,3>& rhs,
651  VTB_REAL_TYPE eps
652 );
653 
654 
655 template <class T>
656 auto
658 AC_matrix_block(int i0, int j0) -> matrix_type {
659  const int n = _arorder(2);
660  matrix_type block(blitz::shape(n, n));
661  for (int k=0; k<n; ++k) {
662  for (int j=0; j<n; ++j) {
663  block(k,j) = this->_acf(i0, j0, std::abs(k - j));
664  }
665  }
666  return block;
667 }
668 
669 template <class T>
670 auto
672 AC_matrix_block(int i0) -> matrix_type {
673  const int n = _arorder(1);
674  matrix_type result;
675  for (int i = 0; i < n; ++i) {
676  matrix_type row;
677  for (int j = 0; j < n; ++j) {
678  matrix_type tmp = AC_matrix_block(i0, std::abs(i - j));
679  append_column_block(row, tmp);
680  }
681  append_row_block(result, row);
682  }
683  return result;
684 }
685 
686 template <class T>
687 auto
689 operator()() -> matrix_type {
690  const int n = _arorder(0);
691  matrix_type result;
692  for (int i = 0; i < n; ++i) {
693  matrix_type row;
694  for (int j = 0; j < n; ++j) {
695  matrix_type tmp = AC_matrix_block(std::abs(i - j));
696  append_column_block(row, tmp);
697  }
698  append_row_block(result, row);
699  }
700  return result;
701 }
702 
703 template <class T, int N>
704 auto
707  VTB_PROFILE_BLOCK("Gauss_yule_walker_solver");
708  using blitz::Range;
709  using blitz::toEnd;
713  using vtb::linalg::Vector;
714  using vtb::linalg::solve;
715  blitz::TinyVector<int,N> order;
716  order = where(this->order() == 0, acf.shape()-1, this->order());
717  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
718  std::clog << "order=" << order << std::endl;
719  #endif
720  Autocovariance_matrix_generator<T,N> gen(acf, order);
721  Square_matrix<T> acm(gen());
722  const int m = acm.rows() - 1;
723  // eliminate the first equation and move the first column of the
724  // remaining matrix to the right-hand side of the system
725  Vector<T> rhs(m);
726  rhs = acm(Range(1, toEnd), 0);
727  // lhs is the autocovariance matrix without first
728  // column and row
729  //Positive_definite_matrix<T> lhs(blitz::shape(m, m));
730  Symmetric_matrix<T> lhs(blitz::shape(m, m));
731  lhs = acm(Range(1, toEnd), Range(1, toEnd));
732  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
733  if (lhs.extent(0) != m ||
734  lhs.extent(1) != m ||
735  rhs.extent(0) != m ||
736  !lhs.is_symmetric()) {
737  throw std::runtime_error{"bad autocovariance matrix"};
738  }
739  dbg::write_matrix_mathematica("lhs", lhs);
740  dbg::write_vector_mathematica("rhs", rhs);
741  #endif
742  Vector<T> x = solve(lhs, rhs);
743  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
744  const T residual = max(abs(rhs - lhs*x));
745  std::clog << "residual=" << residual << std::endl;
746  #endif
747  array_type result(order);
748  result.data()[0] = 0;
749  std::copy_n(x.data(), x.size(), result.data()+1);
750  this->white_noise_variance(
751  autoregression_white_noise_variance(acf, result)
752  );
753  if (this->_chop) {
754  result.resizeAndPreserve(chop_right(result, this->_chopepsilon));
755  }
756  return result;
757 }
758 
759 template <class T>
760 auto
762 AC_matrix_block(int i0, int j0, int k0) -> matrix_type {
763  const int n = _arorder(3);
764  matrix_type block(blitz::shape(n, n));
765  for (int l=0; l<n; ++l) {
766  for (int k=0; k<n; ++k) {
767  block(l,k) = this->_acf(i0, j0, k0, std::abs(l - k));
768  }
769  }
770  return block;
771 }
772 
773 template <class T>
774 auto
776 AC_matrix_block(int i0, int j0) -> matrix_type {
777  const int n = _arorder(2);
778  matrix_type result;
779  for (int k = 0; k < n; ++k) {
780  matrix_type row;
781  for (int j = 0; j < n; ++j) {
782  matrix_type tmp = AC_matrix_block(i0, j0, std::abs(k - j));
783  append_column_block(row, tmp);
784  }
785  append_row_block(result, row);
786  }
787  return result;
788 }
789 
790 template <class T>
791 auto
793 AC_matrix_block(int i0) -> matrix_type {
794  const int n = _arorder(1);
795  matrix_type result;
796  for (int i = 0; i < n; ++i) {
797  matrix_type row;
798  for (int j = 0; j < n; ++j) {
799  matrix_type tmp = AC_matrix_block(i0, std::abs(i - j));
800  append_column_block(row, tmp);
801  }
802  append_row_block(result, row);
803  }
804  return result;
805 }
806 
807 template <class T>
808 auto
810 operator()() -> matrix_type {
811  const int n = _arorder(0);
812  matrix_type result;
813  for (int i = 0; i < n; ++i) {
814  matrix_type row;
815  for (int j = 0; j < n; ++j) {
816  matrix_type tmp = AC_matrix_block(std::abs(i - j));
817  append_column_block(row, tmp);
818  }
819  append_row_block(result, row);
820  }
821  return result;
822 }
823 
824 template <class T, int N>
825 auto
828  VTB_PROFILE_BLOCK("Fixed_point_iteration_yule_walker_solver");
829  typedef blitz::TinyVector<int,N> index;
830  using blitz::RectDomain;
831  using blitz::abs;
832  using blitz::all;
833  using blitz::isfinite;
834  using blitz::max;
835  using blitz::sum;
836  using std::abs;
837  if (!all(this->_order <= acf.shape())) {
838  throw std::invalid_argument("bad order");
839  }
840  const int max_iterations = this->_maxiterations;
841  const T min_var_wn = this->_minvarwn;
842  const T eps = this->_epsvarwn;
843  const T max_residual = this->_maxresidual;
844  const shape_type& order = this->_order;
845  array_type theta(order);
846  theta = 0;
847  const int ni = order(0);
848  const int nj = order(1);
849  const int nk = order(2);
851  T var_wn = acf(0,0,0);
852  T old_var_wn = 0;
853  T residual = 0;
854  int it = 0;
855  do {
869  theta(0,0,0) = 0;
870  for (int i = ni - 1; i >= 0; --i) {
871  for (int j = nj - 1; j >= 0; --j) {
872  for (int k = nk - 1; k >= 0; --k) {
873  const shape_type ijk{i,j,k};
874  RectDomain<3> sub1(ijk, order - 1);
875  RectDomain<3> sub2({0}, order - ijk - 1);
876  theta(i,j,k) = -acf(i,j,k) / var_wn +
877  sum(theta(sub1)*theta(sub2));
878  }
879  }
880  }
881  theta(0,0,0) = -1;
883  if (!all(isfinite(theta))) {
884  throw std::runtime_error("bad MA model coefficients");
885  }
887  theta(0,0,0) = -1;
888  residual = std::numeric_limits<T>::min();
889  for_loop<3>(
890  order,
891  [&] (const index& idx) {
892  RectDomain<3> sub1(idx, order-1);
893  RectDomain<3> sub2({0}, order-idx-1);
894  T new_residual =
895  abs(acf(idx) - sum(theta(sub1)*theta(sub2))*var_wn);
896  if (residual < new_residual) {
897  residual = new_residual;
898  }
899  }
900  );
903  old_var_wn = var_wn;
904  var_wn = moving_average_white_noise_variance(acf, theta);
906  if (var_wn <= min_var_wn) {
907  std::stringstream msg;
908  msg << "bad white noise variance: " << var_wn;
909  throw std::runtime_error(msg.str());
910  }
911  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
912  std::clog << __func__ << ':' << "Iteration=" << it
914  << ",var_wn=" << var_wn
915  << ",resudual=" << residual
916  << ",theta(0,0,0)=" << theta(0,0,0)
917  << std::endl;
918  #endif
919  ++it;
920  } while ((it < max_iterations) &&
921  abs(var_wn - old_var_wn) > eps &&
922  (residual > max_residual));
923  #if defined(VTB_DEBUG_YULE_WALKER_SOLVER)
924  std::clog << "Calculated MA model coefficients:"
925  << "\n\tno. of iterations = " << it
926  << "\n\twhite noise variance = " << var_wn
927  << "\n\tmax(theta) = " << max(abs(theta))
928  << "\n\tmax(residual) = " << residual
929  << std::endl;
930  #endif
931  this->white_noise_variance(var_wn);
932  return theta;
933 }
934 
941 #if defined(VTB_REAL_TYPE_FLOAT)
947 #endif
T min(T... args)
Multi-dimensional solver that determines MA model coefficients from ACF.
array_type solve(array_type acf) override
Solve Yule—Walker system of equations.
T operator()(T... args)
Autocovariate matrix generator that reduces the size of ACF to match AR model order.
array_type solve(array_type acf) override
Solve Yule—Walker system of equations.
blitz::Array< T, N > array_type
Three-dimensional array type.
Computes AR model coefficients using an order-recursvive method from .
array_type solve(array_type acf) override
Solve Yule—Walker system of equations.
T moving_average_white_noise_variance(const blitz::Array< T, N > &acf, const blitz::Array< T, N > &theta)