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> 12 #include <unordered_map> 15 #if defined(VTB_DEBUG_YULE_WALKER_SOLVER) 21 inline blitz::TinyVector<int,1>
23 return blitz::shape(3*a*a + 3*a + 1);
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);
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);
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)) 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);
79 for (
int idx=0; idx<e; ++idx) {
80 *data++ = ACF(i-d+e-idx, j-d+e, k-d);
89 for (
int idx=0; idx<e; ++idx) {
90 *data++ = ACF(i-d, j-d+e, k-d+idx);
99 for (
int idx=0; idx<e; ++idx) {
100 *data++ = ACF(i-d, j-d+e-idx, k-d+e);
109 for (
int idx=0; idx<e; ++idx) {
110 *data++ = ACF(i-d+idx, j-d, k-d+e);
119 for (
int idx=0; idx<e; ++idx) {
120 *data++ = ACF(i-d+e, j-d, k-d+e-idx);
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)) 150 R_matrix(
int a,
int b,
const blitz::Array<T,3>& acf) {
153 matrix_type R_ab(matrix_shape(a, b));
154 R_ab(0, Range::all()) = rho_vector(a, a, a, b, acf);
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);
182 for (
int idx=0; idx<e; ++idx) {
183 R_ab(offset++, Range::all()) = rho_vector(a-e+idx, a-e, a, b, acf);
192 for (
int idx=0; idx<e; ++idx) {
193 R_ab(offset++, Range::all()) = rho_vector(a, a-e, a-idx, b, acf);
202 for (
int idx=0; idx<e; ++idx) {
203 R_ab(offset++, Range::all()) = rho_vector(a, a-e+idx, a-e, b, acf);
212 for (
int idx=0; idx<e; ++idx) {
213 R_ab(offset++, Range::all()) = rho_vector(a-idx, a, a-e, b, acf);
222 for (
int idx=0; idx<e; ++idx) {
223 R_ab(offset++, Range::all()) = rho_vector(a-e, a, a-e+idx, b, acf);
234 R_vector_b0(
int a,
const blitz::Array<T,3>& acf) {
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);
267 for (
int idx=0; idx<e; ++idx) {
268 *data++ = rho_scalar(a-e+idx, a-e, a, acf);
277 for (
int idx=0; idx<e; ++idx) {
278 *data++ = rho_scalar(a, a-e, a-idx, acf);
287 for (
int idx=0; idx<e; ++idx) {
288 *data++ = rho_scalar(a, a-e+idx, a-e, acf);
297 for (
int idx=0; idx<e; ++idx) {
298 *data++ = rho_scalar(a-idx, a, a-e, acf);
307 for (
int idx=0; idx<e; ++idx) {
308 *data++ = rho_scalar(a-e, a, a-e+idx, acf);
320 R_vector_a0(
int b, blitz::Array<T,3> acf) {
321 return rho_vector(0, 0, 0, b, acf);
326 inline blitz::Array<T,3>
328 blitz::Array<T,3> result(blitz::shape(order, order, order));
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++;
338 for (
int idx=0; idx<e; ++idx) {
339 result(d-e+idx,d-e,d) = *data++;
341 for (
int idx=0; idx<e; ++idx) {
342 result(d,d-e,d-idx) = *data++;
344 for (
int idx=0; idx<e; ++idx) {
345 result(d,d-e+idx,d-e) = *data++;
347 for (
int idx=0; idx<e; ++idx) {
348 result(d-idx,d,d-e) = *data++;
350 for (
int idx=0; idx<e; ++idx) {
351 result(d-e,d,d-e+idx) = *data++;
366 template <
class T,
int N>
368 is_square(
const blitz::Array<T,N>& rhs) {
369 const int first = rhs.extent(0);
371 for (
int i=1; i<rhs.dimensions(); ++i) {
372 if (rhs.extent(i) != first) {
386 if (lhs.numElements() == 0) {
387 lhs.resize(rhs.shape());
391 #if defined(VTB_DEBUG_YULE_WALKER_SOLVER) 392 if (lhs.rows() != rhs.rows()) {
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;
408 if (lhs.numElements() == 0) {
409 lhs.resize(rhs.shape());
413 #if defined(VTB_DEBUG_YULE_WALKER_SOLVER) 414 if (lhs.cols() != rhs.cols()) {
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;
429 struct hash<std::tuple<int,int>>:
public std::hash<int> {
433 return base::operator()(std::get<0>(rhs)) ^
434 base::operator()(std::get<1>(rhs));
439 struct hash<std::tuple<int,int,int>>:
public std::hash<int> {
443 return base::operator()(std::get<0>(rhs)) ^
444 base::operator()(std::get<1>(rhs)) ^
445 base::operator()(std::get<2>(rhs));
452 blitz::TinyVector<int,3>
453 vtb::core::chop_right(
const blitz::Array<T,3>& rhs, T eps) {
457 const auto& _ = Range::all();
459 int k = rhs.extent(2) - 1;
460 while (k >= 1 && all(abs(rhs(_,_,k)) < eps)) {
464 int j = rhs.extent(1) - 1;
465 while (j >= 1 && all(abs(rhs(_, j, Range(0,k))) < eps)) {
469 int i = rhs.extent(0) - 1;
470 while (i >= 1 && all(abs(rhs(i, Range(0,j), Range(0,k))) < eps)) {
473 return {i+1,j+1,k+1};
477 blitz::TinyVector<int,4>
478 vtb::core::chop_right(
const blitz::Array<T,4>& rhs, T eps) {
482 const auto& _ = Range::all();
484 int l = rhs.extent(3) - 1;
485 while (l >= 1 && all(abs(rhs(_,_,_,l)) < eps)) {
489 int k = rhs.extent(2) - 1;
490 while (k >= 1 && all(abs(rhs(_, _, k, Range(0,l)) < eps))) {
494 int j = rhs.extent(1) - 1;
495 while (j >= 1 && all(abs(rhs(_, j, Range(0,k), Range(0,l))) < eps)) {
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)) {
503 return {i+1,j+1,k+1,l+1};
506 template <
class T,
int N>
510 VTB_PROFILE_BLOCK(
"Choi_yule_walker_solver");
511 #define PHI(i,j,k) Phi[std::make_tuple(i,j,k)] 516 typedef blitz::Array<vector_type,1> vector_array_type;
517 typedef blitz::Array<matrix_type,1> matrix_array_type;
521 if (!is_square(acf_in)) {
524 const T variance = acf_variance(acf_in);
530 int max_order = this->max_order();
531 if (max_order == 0) {
532 max_order = blitz::max(acf.shape())-1;
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);
544 matrix_type R_sup_1_1(R_1_1.shape());
546 #if defined(VTB_DEBUG_YULE_WALKER_SOLVER) 547 if (!R_sup_1_1.is_symmetric()) {
551 R_sup_1_1.inverse_self();
554 vector_type Pi_2_1 = R_sup_1_1 * R_1_0;
556 T lambda = T(1) - blitz::dot(R_0_1, Pi_2_1);
558 T var = variance*lambda;
562 #if defined(VTB_DEBUG_YULE_WALKER_SOLVER) 566 <<
",lambda=" << lambda
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;
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());
580 vector_type sum2(R_l_0.shape());
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);
587 sum2 += R_l_m.transpose() * Pi_l(m);
589 matrix_type Theta_l(R_l_l - sum1);
590 vector_type h_l(R_l_0 - sum2);
593 Theta_l.inverse_self();
594 Theta(l).reference(Theta_l);
597 Pi_lp1(l).reference(Theta_l * h_l);
599 for (
int a=1; a<=l-1; ++a) {
600 Pi_lp1(a).reference(vector_type(
602 Pi_l(a) - PHI(l,a,0).transpose() * Pi_lp1(l)
605 lambda -= blitz::dot(h_l, Pi_lp1(l));
606 var = variance*lambda;
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());
616 for (
int n=1; n<=m-1; ++n) {
617 sum3 += R_matrix(m,n,acf)*PHI(m,n,l-m+1);
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)
627 blitz::swap(Pi_l, Pi_lp1);
628 #if defined(VTB_DEBUG_YULE_WALKER_SOLVER) 633 <<
",lambda=" << lambda
636 changed = !this->variance_has_not_changed_much(var, var0);
637 }
while (l < max_order && changed);
640 this->white_noise_variance(var);
641 result.reference(result_array(Pi_lp1, l));
643 result.resizeAndPreserve(chop_right(result, this->_chopepsilon));
648 template blitz::TinyVector<int,3>
649 vtb::core::chop_right(
650 const blitz::Array<VTB_REAL_TYPE,3>& rhs,
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));
673 const int n = _arorder(1);
675 for (
int i = 0; i < n; ++i) {
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);
681 append_row_block(result, row);
690 const int n = _arorder(0);
692 for (
int i = 0; i < n; ++i) {
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);
698 append_row_block(result, row);
703 template <
class T,
int N>
707 VTB_PROFILE_BLOCK(
"Gauss_yule_walker_solver");
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;
721 Square_matrix<T> acm(gen());
722 const int m = acm.rows() - 1;
726 rhs = acm(Range(1, toEnd), 0);
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()) {
739 dbg::write_matrix_mathematica(
"lhs", lhs);
740 dbg::write_vector_mathematica(
"rhs", rhs);
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;
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)
754 result.resizeAndPreserve(chop_right(result, this->_chopepsilon));
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));
777 const int n = _arorder(2);
779 for (
int k = 0; k < n; ++k) {
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);
785 append_row_block(result, row);
794 const int n = _arorder(1);
796 for (
int i = 0; i < n; ++i) {
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);
802 append_row_block(result, row);
811 const int n = _arorder(0);
813 for (
int i = 0; i < n; ++i) {
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);
819 append_row_block(result, row);
824 template <
class T,
int N>
828 VTB_PROFILE_BLOCK(
"Fixed_point_iteration_yule_walker_solver");
829 typedef blitz::TinyVector<int,N> index;
830 using blitz::RectDomain;
833 using blitz::isfinite;
837 if (!all(this->_order <= acf.shape())) {
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;
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);
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));
883 if (!all(isfinite(theta))) {
891 [&] (
const index& idx) {
892 RectDomain<3> sub1(idx, order-1);
893 RectDomain<3> sub2({0}, order-idx-1);
895 abs(acf(idx) - sum(theta(sub1)*theta(sub2))*var_wn);
896 if (residual < new_residual) {
897 residual = new_residual;
906 if (var_wn <= min_var_wn) {
908 msg <<
"bad white noise variance: " << var_wn;
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)
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
931 this->white_noise_variance(var_wn);
941 #if defined(VTB_REAL_TYPE_FLOAT)
Multi-dimensional solver that determines MA model coefficients from ACF.
array_type solve(array_type acf) override
Solve Yule—Walker system of equations.
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)