5#ifndef LINE_LIB_SMC_MG1_H
6#define LINE_LIB_SMC_MG1_H
79using Blocks = std::vector<Matrix<double>>;
89 throw InputError(
"smc: matrix addition with mismatched shapes");
91 for (std::size_t i = 0; i < A.
rows(); ++i)
92 for (std::size_t j = 0; j < A.
cols(); ++j) C(i, j) = A(i, j) + B(i, j);
100 throw InputError(
"smc: matrix subtraction with mismatched shapes");
102 for (std::size_t i = 0; i < A.
rows(); ++i)
103 for (std::size_t j = 0; j < A.
cols(); ++j) C(i, j) = A(i, j) - B(i, j);
110 for (std::size_t i = 0; i < A.
rows(); ++i)
111 for (std::size_t j = 0; j < A.
cols(); ++j) C(i, j) = c * A(i, j);
117 std::vector<double> s(A.
rows(), 0.0);
118 for (std::size_t i = 0; i < A.
rows(); ++i)
119 for (std::size_t j = 0; j < A.
cols(); ++j) s[i] += A(i, j);
126 for (std::size_t i = 0; i < A.
rows(); ++i) {
128 for (std::size_t j = 0; j < A.
cols(); ++j) s += std::fabs(A(i, j));
129 if (s > best) best = s;
137 for (std::size_t i = from; i < blk.
size(); ++i) best = std::max(best,
inf_norm(blk[i]));
144 for (std::size_t i = 0; i < A.
rows(); ++i)
145 for (std::size_t j = 0; j < A.
cols(); ++j)
146 best = std::max(best, std::fabs(A(i, j) - B(i, j)));
152 double best = -std::numeric_limits<double>::infinity();
153 for (std::size_t j = 0; j < A.
cols(); ++j) {
155 for (std::size_t i = 0; i < A.
rows(); ++i) s += A(i, j);
156 if (s > best) best = s;
163 if (m == 0 || A.
cols() % m != 0)
164 throw InputError(
"smc: the block sequence has an incorrect number of columns");
165 const std::size_t nb = A.
cols() / m;
167 for (std::size_t b = 0; b < nb; ++b)
168 for (std::size_t i = 0; i < A.
rows(); ++i)
169 for (std::size_t j = 0; j < m; ++j) out[b](i, j) = A(i, b * m + j);
176 const std::size_t r = blk[0].
rows(), c = blk[0].
cols();
178 for (std::size_t b = 0; b < blk.
size(); ++b)
179 for (std::size_t i = 0; i < r; ++i)
180 for (std::size_t j = 0; j < c; ++j) A(i, b * c + j) = blk[b](i, j);
187 const std::size_t r = blk[0].
rows(), c = blk[0].
cols();
189 for (std::size_t b = 0; b < blk.
size(); ++b)
190 for (std::size_t i = 0; i < r; ++i)
191 for (std::size_t j = 0; j < c; ++j) A(b * r + i, j) = blk[b](i, j);
197 if (r == 0 || A.
rows() % r != 0)
198 throw InputError(
"smc: the stacked block sequence has an incorrect number of rows");
199 const std::size_t nb = A.
rows() / r;
201 for (std::size_t b = 0; b < nb; ++b)
202 for (std::size_t i = 0; i < r; ++i)
203 for (std::size_t j = 0; j < A.
cols(); ++j) out[b](i, j) = A(b * r + i, j);
222 const std::size_t S = A.
rows();
223 if (A.
cols() != S)
throw InputError(
"stat: matrix is not square");
226 for (std::size_t i = 0; i < S; ++i)
227 for (std::size_t j = 0; j < S; ++j) Bt(j, i) = A(i, j) - (i == j ? 1.0 : 0.0);
228 for (std::size_t i = 0; i < S; ++i) Bt(S, i) = 1.0;
229 std::vector<double> y(S + 1, 0.0);
231 return lstsq(Bt, y, detail::lstsq_tolerance(Bt)).x;
240inline double dot(
const std::vector<double>& a,
const std::vector<double>& b) {
241 if (a.size() != b.size())
throw InputError(
"smc: inner product with mismatched lengths");
243 for (std::size_t i = 0; i < a.size(); ++i) s += a[i] * b[i];
259 const std::size_t dega = A.size() - 1;
261 std::vector<double> beta =
rowsums(sumA);
262 for (std::size_t i = dega; i-- > 1;) {
263 sumA =
madd(sumA, A[i]);
264 const std::vector<double> rs =
rowsums(sumA);
265 for (std::size_t k = 0; k < beta.size(); ++k) beta[k] += rs[k];
267 sumA =
madd(sumA, A[0]);
286 const std::vector<std::complex<double>> ev =
eig_values(M);
287 if (ev.empty())
throw NumericError(
"smc: empty spectrum");
288 std::complex<double> best = ev[0];
289 for (
const std::complex<double>& z : ev) {
290 const double mz = std::abs(z), mb = std::abs(best);
291 if (mz > mb || (mz == mb && std::arg(z) > std::arg(best))) best = z;
299 for (std::size_t i = A.size() - 1; i-- > 0;) temp =
madd(
mscale(temp, z), A[i]);
310 const std::size_t m = M.
rows();
311 const double lambda =
max_eig(M).real();
313 for (std::size_t i = 0; i < m; ++i)
314 for (std::size_t j = 0; j < m; ++j) K(i, j) = left ? M(j, i) : M(i, j);
315 for (std::size_t i = 0; i < m; ++i) K(i, i) -= lambda;
317 std::vector<double> v(m);
319 for (std::size_t i = 0; i < m; ++i) {
320 v[i] = sv.
Vt(m - 1, i);
323 for (
double& x : v) x /= s;
334 double eta = 1.0, new_eta = 0.0;
336 while (new_eta - eta < 0.0) {
339 new_eta =
max_eig(temp).real();
341 double eta_min = eta - 1.0, eta_max = eta;
343 while (eta_max - eta_min > 1e-15) {
345 new_eta =
max_eig(temp).real();
351 eta = (eta_min + eta_max) / 2.0;
364 double eta_min = 0.0, eta_max = 1.0, eta = 0.5;
366 while (eta_max - eta_min > 1e-15) {
368 const double new_eta =
max_eig(temp).real();
374 eta = (eta_min + eta_max) / 2.0;
389 std::vector<double>
v;
405 if (shift_type !=
"one" && shift_type !=
"tau" && shift_type !=
"dbl")
406 throw InputError(
"MG1_Shifts: ShiftType '" + shift_type +
"' is not one of one, tau, dbl");
408 const std::size_t m = A[0].rows();
409 const std::size_t maxd = A.size() - 1;
414 out.
v.assign(m, 0.0);
416 auto minus_I1 = [&](
Blocks& X,
double s) {
417 for (std::size_t i = 0; i < m; ++i) X[1](i, i) += s;
420 auto row_shift = [&](
const Blocks& X,
const std::vector<double>& u,
double f) {
422 std::vector<std::vector<double>> row(X.size());
423 row[maxd] =
vecmul(u, X[maxd]);
424 for (std::size_t i = maxd; i-- > 0;) {
426 for (std::size_t j = 0; j < m; ++j) row[i][j] += f * row[i + 1][j];
428 for (std::size_t b = 0; b < X.size(); ++b) {
430 for (std::size_t i = 0; i < m; ++i)
431 for (std::size_t j = 0; j < m; ++j) R[b](i, j) -= row[b][j];
437 if (shift_type ==
"tau" || shift_type ==
"dbl") {
438 std::vector<double> uT;
441 hatA = row_shift(A, uT, out.
tau);
443 if (shift_type ==
"dbl") A = hatA;
444 if (shift_type ==
"one") minus_I1(A, -1.0);
445 if (shift_type ==
"one" || shift_type ==
"dbl") {
446 std::vector<double> col(m, 0.0);
447 for (std::size_t b = 0; b < A.size(); ++b) {
448 const std::vector<double> rs =
rowsums(A[b]);
449 for (std::size_t i = 0; i < m; ++i) col[i] += rs[i];
451 for (std::size_t i = 0; i < m; ++i)
452 for (std::size_t j = 0; j < m; ++j)
453 hatA[b](i, j) -= col[i] /
static_cast<double>(m);
457 if (shift_type ==
"one" || shift_type ==
"dbl") {
459 hatA = row_shift(A, d.
theta, 1.0);
461 if (shift_type ==
"dbl") {
465 if (shift_type ==
"tau" || shift_type ==
"dbl") {
466 std::vector<double> v;
470 std::vector<double> col =
mulvec(A[0], v);
471 for (std::size_t b = 0; b < A.size(); ++b) {
473 const std::vector<double> Av =
mulvec(A[b], v);
474 for (std::size_t i = 0; i < m; ++i) col[i] = col[i] / out.
tau + Av[i];
477 for (std::size_t i = 0; i < m; ++i)
478 for (std::size_t j = 0; j < m; ++j) hatA[b](i, j) -= col[i];
482 for (std::size_t i = 0; i < m; ++i) hatA[1](i, i) += 1.0;
493 const std::size_t m = G.
rows();
494 const bool one = shift_type ==
"one" || shift_type ==
"dbl";
495 const bool tau = shift_type ==
"tau" || shift_type ==
"dbl";
496 if (one && sh.
drift < 1.0)
497 for (std::size_t i = 0; i < m; ++i)
498 for (std::size_t j = 0; j < m; ++j) G(i, j) += 1.0 /
static_cast<double>(m);
499 if (tau && sh.
drift > 1.0)
500 for (std::size_t i = 0; i < m; ++i)
501 for (std::size_t j = 0; j < m; ++j) G(i, j) += sh.
tau * sh.
v[i];
520 const std::size_t m = A[0].rows();
521 const std::size_t dega = A.size() - 1;
528 const std::vector<double> rs =
rowsums(A[0]);
529 std::size_t first = m;
530 for (std::size_t i = 0; i < m; ++i)
537 for (std::size_t i = 0; i < m; ++i)
538 for (std::size_t j = 0; j < m; ++j) G(i, j) = A[0](first, j) / rs[first];
545 for (std::size_t b = 0; b < A.size(); ++b)
546 for (std::size_t i = 0; i < m; ++i)
547 for (std::size_t j = 0; j < m; ++j)
548 At[b](i, j) = A[b](j, i) * d.
theta[j] / d.
theta[i];
551 for (std::size_t i = dega; i-- > 1;) temp =
madd(
mscale(temp, etahat), At[i]);
554 for (std::size_t i = 0; i < m; ++i)
555 for (std::size_t j = 0; j < m; ++j) G(i, j) = M(j, i) * d.
theta[j] / d.
theta[i];
568 std::string
mode =
"ShiftPWCR";
578inline std::vector<Matrix<std::complex<double>>> block_dft(
const Blocks& blk, std::size_t n,
579 std::size_t use,
bool inverse) {
580 const std::size_t m = blk[0].
rows();
581 std::vector<Matrix<std::complex<double>>> out(
582 n,
Matrix<std::complex<double>>(m, m, std::complex<double>(0.0, 0.0)));
583 std::vector<std::complex<double>> buf(n);
584 for (std::size_t i = 0; i < m; ++i)
585 for (std::size_t j = 0; j < m; ++j) {
586 for (std::size_t k = 0; k < n; ++k)
587 buf[k] = (k < use && k < blk.
size()) ? std::complex<double>(blk[k](i, j), 0.0)
588 : std::complex<double>(0.0, 0.0);
590 for (std::size_t k = 0; k < n; ++k) out[k](i, j) = buf[k];
596inline Blocks block_idft_real(
const std::vector<
Matrix<std::complex<double>>>& F) {
597 const std::size_t n = F.size(), m = F[0].rows();
599 std::vector<std::complex<double>> buf(n);
600 for (std::size_t i = 0; i < m; ++i)
601 for (std::size_t j = 0; j < m; ++j) {
602 for (std::size_t k = 0; k < n; ++k) buf[k] = F[k](i, j);
604 for (std::size_t k = 0; k < n; ++k) out[k](i, j) = buf[k].real();
609inline Matrix<std::complex<double>> cmul(
const Matrix<std::complex<double>>& A,
610 const Matrix<std::complex<double>>& B) {
614inline Matrix<std::complex<double>> cinv_i_minus(
const Matrix<std::complex<double>>& A) {
615 const std::size_t m = A.rows();
616 Matrix<std::complex<double>> M(m, m, std::complex<double>(0.0, 0.0));
617 for (std::size_t i = 0; i < m; ++i)
618 for (std::size_t j = 0; j < m; ++j)
619 M(i, j) = (i == j ? std::complex<double>(1.0, 0.0) : std::complex<double>(0.0, 0.0)) -
625inline double tail_norm(
const Blocks& blk) {
626 const std::size_t deg = blk.
size();
631 const std::size_t start = (deg + 1) / 2;
633 for (std::size_t i = start; i + 1 <= deg && i < deg; ++i) best = std::max(best,
inf_norm(blk[i]));
640 for (std::size_t i = 0; i < b.size(); i += 2) out.push_back(b[i]);
647 for (std::size_t i = 1; i < b.size(); i += 2) out.push_back(b[i]);
673 if (Ain.empty())
throw InputError(
"MG1_CR: empty block sequence");
674 const std::size_t m = Ain[0].rows();
675 if (opts.mode !=
"ShiftPWCR" && opts.mode !=
"PWCR")
677 "' is not supported; the reference offers 'PWCR' and 'ShiftPWCR'");
679 bool eg_found =
false;
681 if (eg_found)
return Geg;
685 if (opts.mode ==
"ShiftPWCR") {
691 const std::size_t maxd = A.size() - 1;
692 if (maxd == 0)
throw InputError(
"MG1_CR: the sequence needs at least two blocks");
693 std::size_t target = 1;
694 while (target < maxd) target <<= 1;
695 if (target == maxd) target <<= 1;
698 for (std::size_t b = 0; b <= maxd; ++b) D[b] = A[b].transpose();
700 Blocks Aeven = cr_detail::even_blocks(D);
701 Blocks Aodd = cr_detail::odd_blocks(D);
702 Blocks Ahatodd(Aeven.begin() + 1, Aeven.end());
703 Ahatodd.push_back(D.back());
707 for (std::size_t i = 2; i < D.size(); ++i) Rj =
madd(Rj, D[i]);
712 std::size_t numit = 0;
713 while (numit < opts.max_num_it) {
715 std::size_t nj = Aodd.size() - 1;
716 double nAnew = 0.0, nAhatnew = 0.0;
719 const std::size_t n = nj + 1;
720 const std::vector<Matrix<std::complex<double>>> T1 =
721 cr_detail::block_dft(Aodd, n, n,
false);
722 const std::vector<Matrix<std::complex<double>>> T2 =
723 cr_detail::block_dft(Aeven, n, n,
false);
724 const std::vector<Matrix<std::complex<double>>> T3 =
725 cr_detail::block_dft(Ahatodd, n, n,
false);
726 const std::vector<Matrix<std::complex<double>>> T4 =
727 cr_detail::block_dft(Ahateven, n, n,
false);
728 std::vector<Matrix<std::complex<double>>> Ah(n), An(n);
729 const double pi = 3.14159265358979323846;
730 for (std::size_t c = 0; c < n; ++c) {
732 Ah[c] =
madd(T4[c], cr_detail::cmul(cr_detail::cmul(T2[c], W), T3[c]));
733 const double ang = -2.0 * pi *
static_cast<double>(c) /
static_cast<double>(n);
734 const std::complex<double> w(std::cos(ang), std::sin(ang));
736 for (std::size_t i = 0; i < m; ++i)
737 for (std::size_t j = 0; j < m; ++j) first(i, j) = w * T1[c](i, j);
738 An[c] =
madd(first, cr_detail::cmul(cr_detail::cmul(T2[c], W), T2[c]));
740 Ahatnew = cr_detail::block_idft_real(Ah);
741 Anew = cr_detail::block_idft_real(An);
745 Ahatnew.assign(1,
madd(Ahateven[0],
matmul(temp, Ahatodd[0])));
747 Anew.push_back(
matmul(temp, Aeven[0]));
748 Anew.push_back(Aodd[0]);
751 nAnew = cr_detail::tail_norm(Anew);
752 nAhatnew = cr_detail::tail_norm(Ahatnew);
755 while ((nAnew >
static_cast<double>(nj + 1) * opts.epsilon ||
756 nAhatnew >
static_cast<double>(nj + 1) * opts.epsilon) &&
757 nj + 1 < opts.max_num_root) {
758 nj = 2 * (nj + 1) - 1;
759 const std::size_t n = nj + 1;
760 const std::size_t stopv = std::min(n, Aodd.size());
761 const std::vector<Matrix<std::complex<double>>> T1 =
762 cr_detail::block_dft(Aodd, n, stopv,
false);
763 const std::vector<Matrix<std::complex<double>>> T2 =
764 cr_detail::block_dft(Aeven, n, stopv,
false);
765 const std::vector<Matrix<std::complex<double>>> T3 =
766 cr_detail::block_dft(Ahatodd, n, stopv,
false);
767 const std::vector<Matrix<std::complex<double>>> T4 =
768 cr_detail::block_dft(Ahateven, n, stopv,
false);
769 std::vector<Matrix<std::complex<double>>> Ah(n), An(n);
770 const double pi = 3.14159265358979323846;
771 for (std::size_t c = 0; c < n; ++c) {
773 Ah[c] =
madd(T4[c], cr_detail::cmul(cr_detail::cmul(T2[c], W), T3[c]));
774 const double ang = -2.0 * pi *
static_cast<double>(c) /
static_cast<double>(n);
775 const std::complex<double> w(std::cos(ang), std::sin(ang));
777 for (std::size_t i = 0; i < m; ++i)
778 for (std::size_t j = 0; j < m; ++j) first(i, j) = w * T1[c](i, j);
779 An[c] =
madd(first, cr_detail::cmul(cr_detail::cmul(T2[c], W), T2[c]));
781 Ahatnew = cr_detail::block_idft_real(Ah);
782 Anew = cr_detail::block_idft_real(An);
783 nAnew = cr_detail::tail_norm(Anew);
784 nAhatnew = cr_detail::tail_norm(Ahatnew);
788 const std::size_t keep = (nj + 1) / 2;
789 Anew.resize(std::min(keep, Anew.size()));
790 Ahatnew.resize(std::min(keep, Ahatnew.size()));
793 Aeven = cr_detail::even_blocks(Anew);
794 Aodd = cr_detail::odd_blocks(Anew);
795 Ahateven = cr_detail::even_blocks(Ahatnew);
796 Ahatodd = cr_detail::odd_blocks(Ahatnew);
798 if (opts.mode ==
"PWCR") {
800 for (std::size_t i = 2; i < Anew.size(); ++i) Rnewj =
madd(Rnewj, Anew[i]);
809 for (std::size_t i = 1; i < Ahatnew.size(); ++i)
815 double tail_sum = 0.0;
816 for (std::size_t i = 1; i < Ahatnew.size(); ++i) tail_sum += Ahatnew[i].
sum();
820 if (sv < opts.epsilon || tail_sum < opts.epsilon ||
max_col_sum(V) < opts.epsilon) {
831 if (numit == opts.max_num_it && !Ahatnew.empty())
837 if (opts.mode ==
"ShiftPWCR")
mg1_unshift(G, sh, opts.shift_type);
864 const std::size_t m = Ain[0].rows();
865 const std::size_t maxd = Ain.size() - 1;
867 bool eg_found =
false;
869 if (eg_found)
return Geg;
872 const bool shifted = opts.mode.find(
"Shift") != std::string::npos;
879 const bool natural = opts.mode.find(
"Natural") != std::string::npos;
880 const bool traditional = opts.mode.find(
"Traditional") != std::string::npos;
881 const bool ubased = opts.mode.find(
"U-Based") != std::string::npos;
882 if (!natural && !traditional && !ubased)
883 throw UnsupportedError(
"MG1_FI: Mode '" + opts.mode +
"' is not supported");
887 std::size_t numit = 0;
888 while (check > opts.tol && numit < opts.max_num_it) {
892 for (std::size_t j = maxd; j-- > 0;) G =
madd(A[j],
matmul(G, Gold));
893 }
else if (traditional) {
895 for (std::size_t j = maxd; j-- > 2;) G =
madd(A[j],
matmul(G, Gold));
900 for (std::size_t j = maxd; j-- > 1;) G =
madd(A[j],
matmul(G, Gold));
921 for (std::size_t i = 0; i < nr; ++i)
922 for (std::size_t j = 0; j <
nc; ++j) S(i, j) = A(r0 + i, c0 + j);
928 for (std::size_t i = 0; i < S.
rows(); ++i)
929 for (std::size_t j = 0; j < S.
cols(); ++j) A(r0 + i, c0 + j) = S(i, j);
934 for (std::size_t i = 0; i < A.
rows(); ++i)
935 for (std::size_t j = 0; j < A.
cols(); ++j) T(j, i) = A(i, j);
941 if (A.
rows() != B.
rows())
throw InputError(
"smc: horizontal join with mismatched rows");
950 if (A.
cols() != B.
cols())
throw InputError(
"smc: vertical join with mismatched columns");
968 const std::size_t r = A.
rows(), c = A.
cols(), k = std::min(r, c);
970 std::vector<std::vector<double>> vs;
971 for (std::size_t j = 0; j < k; ++j) {
973 for (std::size_t i = j; i < r; ++i) nrm += W(i, j) * W(i, j);
974 nrm = std::sqrt(nrm);
975 std::vector<double> v(r, 0.0);
977 const double alpha = W(j, j) > 0.0 ? -nrm : nrm;
978 for (std::size_t i = j; i < r; ++i) v[i] = W(i, j);
981 for (std::size_t i = j; i < r; ++i) vn += v[i] * v[i];
983 for (std::size_t col = j; col < c; ++col) {
985 for (std::size_t i = j; i < r; ++i) s += v[i] * W(i, col);
987 for (std::size_t i = j; i < r; ++i) W(i, col) -= s * v[i];
989 const double vnr = std::sqrt(vn);
990 for (std::size_t i = j; i < r; ++i) v[i] /= vnr;
992 std::fill(v.begin(), v.end(), 0.0);
998 for (std::size_t i = 0; i < k; ++i)
999 for (std::size_t j = i; j < c; ++j) R(i, j) = W(i, j);
1002 for (std::size_t i = 0; i < k; ++i) Q(i, i) = 1.0;
1003 for (std::size_t j = k; j-- > 0;) {
1004 const std::vector<double>& v = vs[j];
1005 for (std::size_t col = 0; col < k; ++col) {
1007 for (std::size_t i = j; i < r; ++i) s += v[i] * Q(i, col);
1009 for (std::size_t i = j; i < r; ++i) Q(i, col) -= s * v[i];
1026 const std::size_t m = A.
rows(), n = C.
rows(), N = B.size();
1030 for (std::size_t j = 0; j < N; ++j) {
1031 for (std::size_t a = 0; a < m; ++a)
1032 for (std::size_t b = 0; b < m; ++b) {
1033 const double p = P(a, b);
1034 if (p == 0.0)
continue;
1035 for (std::size_t k = 0; k < n; ++k)
1036 for (std::size_t l = 0; l < n; ++l) Z(a * n + k, b * n + l) += p * B[j](k, l);
1040 std::vector<double> c(m * n);
1041 for (std::size_t col = 0; col < m; ++col)
1042 for (std::size_t k = 0; k < n; ++k) c[col * n + k] = C(k, col);
1045 for (std::size_t col = 0; col < m; ++col)
1046 for (std::size_t k = 0; k < n; ++k) Y(k, col) = y[col * n + k];
1059 const std::size_t m = A.
rows(), n = C.
rows(), N = B.size();
1064 for (std::size_t j = 1; j < N; ++j) Tp[j] =
matmul(Tp[j - 1], T);
1066 auto S = [&](std::size_t a, std::size_t b) {
1068 for (std::size_t j = 0; j < N; ++j) {
1069 const double t = Tp[j](a, b);
1070 if (t == 0.0)
continue;
1071 for (std::size_t k = 0; k < n; ++k)
1072 for (std::size_t l = 0; l < n; ++l) out(k, l) += t * B[j](k, l);
1078 auto W = [&](std::size_t k, std::size_t col) {
1079 std::vector<double> w(n, 0.0);
1080 for (std::size_t l = 0; l < k; ++l) {
1082 for (std::size_t a = 0; a < n; ++a)
1083 for (std::size_t b = 0; b < n; ++b) w[a] += Sl(a, b) * X(b, l);
1087 const double epsilon = 10e-14;
1090 if (k + 1 == m || std::abs(T(k + 1, k)) < epsilon) {
1092 const std::vector<double> w = W(k, k);
1093 std::vector<double> rhs(n);
1094 for (std::size_t a = 0; a < n; ++a) rhs[a] = E(a, k) - w[a];
1096 for (std::size_t a = 0; a < n; ++a) X(a, k) = x[a];
1104 const std::vector<double> w0 = W(k, k), w1 = W(k, k + 1);
1105 std::vector<double> rhs(2 * n);
1106 for (std::size_t a = 0; a < n; ++a) {
1107 rhs[a] = E(a, k) - w0[a];
1108 rhs[n + a] = E(a, k + 1) - w1[a];
1111 for (std::size_t a = 0; a < n; ++a) {
1113 X(a, k + 1) = x[n + a];
1128 std::string
mode =
"RealSchurShift";
1147 if (Ain.size() < 2)
throw InputError(
"MG1_NI: the sequence needs at least two blocks");
1148 std::string base = opts.mode;
1149 const bool shifted = base.size() > 5 && base.compare(base.size() - 5, 5,
"Shift") == 0;
1150 if (shifted) base = base.substr(0, base.size() - 5);
1151 if (base !=
"DirectSum" && base !=
"RealSchur" && base !=
"ComplexSchur")
1152 throw InputError(
"MG1_NI: Mode '" + opts.mode +
"' is not one of DirectSum, RealSchur, "
1153 "ComplexSchur, each optionally with the Shift suffix");
1155 bool eg_found =
false;
1157 if (eg_found)
return Geg;
1166 const std::size_t m = A[0].rows();
1167 const std::size_t N = A.size() - 1;
1170 std::size_t numit = 0;
1171 while (check > opts.epsilon && numit < opts.max_num_it) {
1175 for (std::size_t i = N; i-- > 0;) Bk[i] =
madd(A[i],
matmul(Bk[i + 1], G));
1177 Blocks Bs(Bk.begin() + 1, Bk.end());
1178 for (std::size_t i = 0; i < m; ++i) Bs[0](i, i) -= 1.0;
1219 const std::size_t hd = temp.
rows();
1224 for (std::size_t l = 0; l <
N; ++l) {
1226 for (std::size_t i = l; i <
N; ++i)
1236 for (std::size_t l = 1; l <
N; ++l) {
1238 for (std::size_t i = 0; i < l; ++i)
1245 out =
madd(out, times_LZrT(times_L(
c1),
r1));
1246 out =
madd(out, times_LZrT(times_L(
c2),
r2));
1247 out =
madd(out, times_L(
b));
1253 const std::size_t hd = temp.
cols();
1257 for (std::size_t i = 0; i + 1 <
N; ++i) {
1259 for (std::size_t l = i + 1; l <
N; ++l)
1269 for (std::size_t i = 0; i <
N; ++i) {
1271 for (std::size_t l = 0; l <= i; ++l)
1278 out =
madd(out, L_times(
c2, LZrT_times(
r2)));
1279 out =
madd(out, L_times(
b, temp));
1286 for (std::size_t i = 0; i < A.
rows(); ++i)
1287 for (std::size_t j = 0; j < A.
cols(); ++j) s += A(i, j);
1310 if (D.size() < 2)
throw InputError(
"MG1_RR: the sequence needs at least two blocks");
1311 if (opts.mode !=
"Direct" && opts.mode !=
"DispStruct" && opts.mode !=
"DispStructFFT")
1312 throw InputError(
"MG1_RR: Mode '" + opts.mode +
1313 "' is not one of Direct, DispStruct, DispStructFFT");
1315 bool eg_found =
false;
1317 if (eg_found)
return Geg;
1319 const std::size_t m = D[0].rows();
1320 const std::size_t N = D.size() - 1;
1324 for (std::size_t k = 1; k <= N; ++k) put(vT, 0, (k - 1) * m, D[k]);
1332 for (std::size_t i = 0; i < m; ++i)
1333 for (std::size_t j = 0; j < m; ++j) z(i, j) = 0.0;
1337 if (opts.mode ==
"Direct") {
1339 for (std::size_t i = 0; i + m < N * m; ++i) B(m + i, i) = 1.0;
1341 std::size_t numit = 0;
1342 while (check > 1e-15 && numit < opts.max_num_it) {
1360 throw InputError(
"MG1_RR: Mode '" + opts.mode +
1361 "' needs at least three blocks A0, A1, A2; use Mode 'Direct'");
1365 if (N >= 2) put(B.
b, m, 0, I);
1368 std::size_t numit = 0;
1369 while ((check > 1e-15 || first) && numit < opts.max_num_it) {
1386 put(ST, m, m,
madd(I, T));
1389 const std::size_t w = X.cols();
1392 put(out, 0, 0,
madd(sub(X, 0, m, 0, w), sub(t, 0, m, 0, w)));
1395 madd(sub(X, m, (N - 1) * m, 0, w),
1396 matmul(sub(uhat, m, (N - 1) * m, 0, m), sub(t, m, m, 0, w))));
1403 Matrix<double> e1pZu(N * m, m, 0.0), Z2u(N * m, m, 0.0), e2pZ2u(N * m, m, 0.0);
1404 put(e1pZu, 0, 0, I);
1405 if (N > 1) put(e1pZu, m, 0, sub(uhat, m, (N - 1) * m, 0, m));
1406 if (N > 1) put(e2pZ2u, m, 0, I);
1408 put(Z2u, 2 * m, 0, sub(uhat, m, (N - 2) * m, 0, m));
1409 put(e2pZ2u, 2 * m, 0, sub(uhat, m, (N - 2) * m, 0, m));
1415 if (N > 1) put(vshift, 0, 0, sub(vT, 0, m, m, (N - 1) * m));
1417 for (std::size_t i = 0; i < m; ++i) negI(i, i) = -1.0;
1419 vjoin(vjoin(
matmul(S, vshift),
mscale(vT, -1.0)), negI);
1423 hjoin(hjoin(C12, B.
right(H)), B.
right(inv_struct(C12)));
1431 matmul(hjoin(sub(R12t, 0, 2 * m, 0, m),
matmul(R12t, Zu)), ST);
1433 put(temp2, 0, 0,
madd(sub(temp2, 0, 2 * m, 0, m), sub(tq, 0, 2 * m, m, m)));
1435 vjoin(vjoin(B.
left(
madd(temp2, R12t)), B.
left(KT)), R12t);
1438 qr_econ(trans(YT), Q2, R2);
1443 for (std::size_t i = 0; i < sv.
U.
rows(); ++i)
1444 for (std::size_t j = 0; j < 2 * m; ++j) US(i, j) = sv.
U(i, j) * sv.
s[j];
1447 for (std::size_t i = 0; i < sv.
Vt.
cols(); ++i)
1448 for (std::size_t j = 0; j < 2 * m; ++j) V2(i, j) = sv.
Vt(j, i);
1450 B.
c1 = sub(cc, 0, N * m, 0, m);
1451 B.
c2 = sub(cc, 0, N * m, m, m);
1452 B.
r1 = sub(rr, 0, N * m, 0, m);
1453 B.
r2 = sub(rr, 0, N * m, m, m);
1489 if (opts.mode !=
"MSignStandard" && opts.mode !=
"MSignBalzer" && opts.mode !=
"Schur")
1490 throw InputError(
"MG1_IS: Mode '" + opts.mode +
1491 "' is not one of MSignStandard, MSignBalzer, Schur");
1493 throw InputError(
"MG1_IS: the sequence needs at least three blocks A0, A1, A2; the "
1494 "reference reads rows m+1:2m of an m*max-row subspace basis");
1496 bool eg_found =
false;
1498 if (eg_found)
return Geg;
1500 const double epsilon = 1e-14;
1501 const std::size_t m = D[0].rows();
1502 const std::size_t f = D.size() - 1;
1504 const double sgn = drift > 0.0 ? 1.0 : (drift < 0.0 ? -1.0 : 0.0);
1508 for (std::size_t i = 0; i <= f; ++i) F[i] =
mscale(D[i], -1.0);
1509 for (std::size_t i = 0; i < m; ++i) F[1](i, i) += 1.0;
1513 for (std::size_t i = 0; i <= f; ++i) {
1514 std::vector<double> c{1.0};
1515 auto conv = [](
const std::vector<double>& a,
double s1) {
1516 std::vector<double> r(a.size() + 1, 0.0);
1517 for (std::size_t k = 0; k < a.size(); ++k) {
1519 r[k + 1] += s1 * a[k];
1523 for (std::size_t j = 0; j < f - i; ++j) c = conv(c, -1.0);
1524 for (std::size_t j = 0; j < i; ++j) c = conv(c, 1.0);
1525 for (std::size_t j = 0; j <= f; ++j) H[j] =
madd(H[j],
mscale(F[i], c[j]));
1531 for (std::size_t i = 0; i < f; ++i) hatH[i] =
matmul(Hfinv, H[i]);
1534 const std::size_t mf = m * f;
1535 std::vector<double> y(mf, 0.0);
1536 for (std::size_t i = 0; i < m; ++i) y[i] = 1.0;
1538 for (std::size_t i = 0; i < m; ++i) {
1539 for (std::size_t j = 0; j < m; ++j) Mt(j, i) = hatH[0](i, j);
1542 std::vector<double> rhs(m + 1, 0.0);
1544 const std::vector<double> x0 =
lstsq(Mt, rhs).x;
1545 std::vector<double> xT(mf, 0.0);
1546 for (std::size_t i = 1; i < f; ++i) {
1547 const std::vector<double> xi =
vecmul(x0, hatH[i]);
1548 for (std::size_t j = 0; j < m; ++j) xT[(i - 1) * m + j] = xi[j];
1550 for (std::size_t j = 0; j < m; ++j) xT[(f - 1) * m + j] = x0[j];
1554 for (std::size_t i = 1; i < f; ++i) put(Z, (i - 1) * m, i * m,
eye<double>(m));
1555 for (std::size_t i = 0; i < f; ++i) put(Z, (f - 1) * m, i * m,
mscale(hatH[i], -1.0));
1557 for (std::size_t k = 0; k < mf; ++k) xy += xT[k] * y[k];
1558 for (std::size_t a = 0; a < mf; ++a)
1559 for (std::size_t b = 0; b < mf; ++b) Z(a, b) += sgn * (y[a] / xy) * xT[b];
1562 if (opts.mode !=
"Schur") {
1566 std::size_t numit = 0;
1568 while (check > epsilon && numit < opts.max_num_it) {
1571 const double determ =
1572 opts.mode ==
"MSignStandard"
1574 : 1.0 / (1.0 + std::pow(std::abs(
lu_det(Zold)), 1.0 /
static_cast<double>(mf)));
1582 const double tol =
static_cast<double>(mf) *
1583 (std::nextafter(sv.
s[0], std::numeric_limits<double>::infinity()) - sv.
s[0]);
1585 for (
double s : sv.
s)
1588 throw NumericError(
"MG1_IS: the sign iteration left an invariant subspace of dimension " +
1589 std::to_string(r) +
", not m = " + std::to_string(m));
1590 T = sub(sv.
U, 0, mf, 0, m);
1593 std::vector<double> key(mf, 0.0);
1594 for (std::size_t k = 0; k < mf; ++k) key[k] = sf.
T(k, k) < 0.0 ? 1.0 : 0.0;
1596 T = sub(ord.
Z, 0, mf, 0, m);
1600 const Matrix<double> T1 = sub(T, 0, m, 0, m), T2 = sub(T, m, m, 0, m);
1622 const std::string& algor) {
1623 const std::size_t m = Ain[0].rows();
1624 const std::size_t dega = Ain.size() - 1;
1629 std::vector<double> theta = d.
theta;
1630 const bool ram = (dual ==
"R") || (dual ==
"A" && d.
value <= 1.0);
1634 for (std::size_t b = 0; b <= dega; ++b) {
1636 for (std::size_t i = 0; i < m; ++i)
1637 for (std::size_t j = 0; j < m; ++j) Bb(i, j) = A[b](j, i) * theta[j] / theta[i];
1640 }
else if (dual ==
"B" || dual ==
"A") {
1644 for (std::size_t i = dega; i-- > 0;)
1645 sumAeta =
madd(sumAeta,
mscale(Ain[i], std::pow(eta,
static_cast<double>(i))));
1647 for (std::size_t i = 0; i < m; ++i) shifted(i, i) += (1.0 - eta);
1648 theta =
stat(shifted);
1649 for (std::size_t b = 0; b <= dega; ++b) {
1651 const double s = std::pow(eta,
static_cast<double>(b) - 1.0);
1652 for (std::size_t i = 0; i < m; ++i)
1653 for (std::size_t j = 0; j < m; ++j)
1654 Bb(i, j) = s * A[b](j, i) * theta[j] / theta[i];
1658 throw InputError(
"GIM1_R: Dual '" + dual +
"' is not one of 'A', 'B', 'R'");
1662 if (algor ==
"FI") {
1664 }
else if (algor ==
"CR") {
1666 }
else if (algor ==
"NI") {
1668 }
else if (algor ==
"RR") {
1670 }
else if (algor ==
"IS") {
1673 throw InputError(
"GIM1_R: Algorithm '" + algor +
"' is not supported");
1677 for (std::size_t i = 0; i < m; ++i)
1678 for (std::size_t j = 0; j < m; ++j) R(i, j) = G(j, i) * theta[j] / theta[i];
1680 for (std::size_t i = 0; i < m; ++i)
1681 for (std::size_t j = 0; j < m; ++j) R(i, j) *= eta;
NumericError(const std::string &what)
UnsupportedError(const std::string &what)
std::complex<double> as a number type for the generic linear algebra.
Eigenvalues and singular values, backed by LAPACK.
The exception types the port throws.
Discrete Fourier transform of arbitrary length, in double complex.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Least squares for a rectangular system, exact-capable.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
Matrix< double > trans(const Matrix< double > &A)
void put(Matrix< double > &A, std::size_t r0, std::size_t c0, const Matrix< double > &S)
A(r0:, c0:) = S.
Matrix< double > sub(const Matrix< double > &A, std::size_t r0, std::size_t nr, std::size_t c0, std::size_t nc)
A(r0:r0+nr, c0:c0+nc), half-open, 0-based.
std::vector< double > lsolve(const Matrix< double > &Z, const std::vector< double > &b)
Z \ b for a square Z.
void qr_econ(const Matrix< double > &A, Matrix< double > &Q, Matrix< double > &R)
Economy QR, A = Q R with Q (r x k) orthonormal columns and R (k x c), k = min(r, c),...
Matrix< double > hjoin(const Matrix< double > &A, const Matrix< double > &B)
[A B], horizontally.
Matrix< double > vjoin(const Matrix< double > &A, const Matrix< double > &B)
[A; B], vertically.
double sum_all(const Matrix< double > &A)
double max_col_sum(const Matrix< double > &A)
max(sum(A)), the largest column sum WITHOUT absolute values, as in MATLAB.
Blocks vblocks_of(const Matrix< double > &A, std::size_t r)
Splits a vertical stack into its blocks of r rows.
Matrix< double > hcat(const Blocks &blk)
Re-assembles a block sequence into the wide [A0 A1 ... Amax].
ShiftResult mg1_shifts(const Blocks &Ain, const std::string &shift_type)
Shift technique for the M/G/1-type sequence.
Blocks blocks_of(const Matrix< double > &A, std::size_t m)
Splits the wide [A0 A1 ... Amax] into its m x m blocks.
double gim1_caudal(const Blocks &A, std::vector< double > *v=nullptr)
Caudal characteristic of a GI/M/1-type chain: the spectral radius of R, the unique z in (0,...
Matrix< double > poly_at(const Blocks &A, double z)
A(z) = A0 + A1 z + ... + Amax z^max, by Horner as the reference writes it.
Matrix< T > msub(const Matrix< T > &A, const Matrix< T > &B)
A - B.
double inf_norm(const Matrix< double > &A)
norm(A,inf), the largest absolute row sum.
Matrix< double > mg1_eg(const Blocks &Ain, bool &found)
G in closed form when A0 has rank one.
Matrix< double > mg1_is(const Blocks &D, const Mg1IsOptions &opts=Mg1IsOptions())
Invariant subspace method for M/G/1-type Markov chains [Akar, Sohraby].
double mg1_decay(const Blocks &A, std::vector< double > *uT=nullptr)
Decay rate of a recurrent M/G/1-type chain: the unique z > 1 with PF(A(z)) = z.
Matrix< double > gim1_r(const Blocks &Ain, const std::string &dual, const std::string &algor)
R of a GI/M/1-type Markov chain, through the G of its dual.
std::vector< double > stat(const Matrix< double > &A)
Stationary distribution of a stochastic matrix: the left eigenvector for eigenvalue 1,...
Matrix< double > sylv_powers_direct(const Matrix< double > &A, const Blocks &B, const Matrix< double > &C)
Solve sum_{j=1}^N B_j Y A^{j-1} = C directly, through the Kronecker form of vec(Y).
Matrix< double > mg1_cr(const Blocks &Ain, const Mg1CrOptions &opts=Mg1CrOptions())
Cyclic reduction for M/G/1-type Markov chains [Bini, Meini].
Matrix< double > mg1_fi(const Blocks &Ain, const Mg1FiOptions &opts=Mg1FiOptions())
Functional iterations for M/G/1-type Markov chains [Neuts].
std::vector< double > rowsums(const Matrix< double > &A)
sum(A,2), the row sums, as a column held in a vector.
Drift mg1_drift(const Blocks &A)
drift = theta * beta with beta = (Amax)e + (Amax+Amax-1)e + ..., the expected level increment per tra...
std::complex< double > max_eig(const Matrix< double > &M)
max(eig(M)) with MATLAB's semantics on a complex spectrum: the element of largest modulus,...
double dot(const std::vector< double > &a, const std::vector< double > &b)
The inner product of a row vector with a column held as a vector.
std::vector< Matrix< double > > Blocks
Matrix< double > mg1_rr(const Blocks &D, const Mg1RrOptions &opts=Mg1RrOptions())
Ramaswami reduction for M/G/1-type Markov chains [Bini, Meini, Ramaswami].
Matrix< T > madd(const Matrix< T > &A, const Matrix< T > &B)
A + B.
void mg1_unshift(Matrix< double > &G, const ShiftResult &sh, const std::string &shift_type)
Put back on G the rank-one term a shift removed: ones/m for a 'one' shift at drift < 1,...
std::vector< double > rowvec_times(const std::vector< double > &v, const Matrix< double > &A)
theta A, the row vector times matrix product used throughout.
std::vector< double > pf_vector(const Matrix< double > &M, bool left)
The Perron-Frobenius eigenvector of M, right (left == false) or left, scaled to unit sum: the null ve...
Matrix< double > sylv_powers_real_schur(const Matrix< double > &A, const Blocks &B, const Matrix< double > &C)
Solve sum_{j=1}^N B_j Y A^{j-1} = C by a real Schur form A = U T U': with Y = X U' the system becomes...
double max_abs_diff(const Matrix< double > &A, const Matrix< double > &B)
max(max(abs(A-B))).
Matrix< double > mscale(const Matrix< double > &A, double c)
c * A.
Matrix< double > vcat(const Blocks &blk)
Stacks a block sequence vertically, [A0; A1; ...; Amax].
Matrix< double > mg1_ni(const Blocks &Ain, const Mg1NiOptions &opts=Mg1NiOptions())
Newton iteration for M/G/1-type Markov chains.
Conservation laws of a layered queueing network, enumerated from its structure.
LstsqResult< T > lstsq(const Matrix< T > &A, const std::vector< T > &b, const T &tol)
Least-squares solution of A x = b, minimum-norm when A is rank deficient.
std::vector< T > vecmul(const std::vector< T > &v, const Matrix< T > &A)
Row vector times matrix, v A.
Matrix< T > inverse(const Matrix< T > &A)
Inverse by LU with one factorization and n back substitutions.
RealSchur schur_reorder(const RealSchur &s, const std::vector< double > &key)
Reorder the diagonal blocks of a real Schur form into DESCENDING key order, stably,...
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
std::vector< T > mulvec(const Matrix< T > &A, const std::vector< T > &v)
Matrix times column vector, A v.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
std::vector< std::complex< double > > eig_values(const Matrix< double > &A)
Eigenvalues of a general real square matrix, in LAPACK's order.
T lu_det(const Matrix< T > &A)
Determinant of a square matrix, by the same partial-pivoting elimination.
std::size_t matrix_rank(const Matrix< double > &A)
Numerical rank at the standard max(m,n) eps sigma_1 threshold.
std::vector< double > svd_values(const Matrix< double > &A)
Singular values in descending order.
Matrix< T > eye(std::size_t n)
Identity of order n.
RealSchur schur_decomposition(const Matrix< double > &A)
Real Schur factorization of a general square matrix (LAPACK dgees, unsorted).
void dft(std::vector< std::complex< double > > &a, bool inverse)
In-place DFT of a.
SvdFactors svd_full(const Matrix< double > &A)
Full SVD of a real matrix, singular values in descending order.
Number-type abstraction for the templated API port.
Real Schur factorization A = Z T Z^T, with Z orthogonal and T upper quasi-triangular: 1 x 1 diagonal ...
Matrix< double > Z
orthogonal Schur vectors
Matrix< double > T
upper quasi-triangular factor
A = U diag(s) Vt, with U (m x m), s of length min(m,n) and Vt (n x n).
The drift of an M/G/1-type sequence, and the invariant vector it uses.
std::vector< double > theta
stat(A0 + A1 + ... + Amax)
Options of MG1_CR, with the reference's defaults.
std::string mode
'ShiftPWCR' or 'PWCR'
Options of MG1_FI, with the reference's defaults.
std::string mode
'Natural', 'Traditional', 'U-Based', or 'Shift<Mode>'
Options of MG1_IS, with the reference's defaults.
std::string mode
'MSignStandard', 'MSignBalzer' or 'Schur'
Options of MG1_NI, with the reference's defaults.
std::string mode
'DirectSum', 'RealSchur' or 'ComplexSchur', each optionally with the 'Shift' suffix
Options of MG1_RR, with the reference's defaults.
std::string mode
'Direct', 'DispStruct' or 'DispStructFFT'
What MG1_Shifts returns: the shifted sequence and the drift it measured.
The displacement representation B = L(b) + L(c1) L(Z r1)' + L(c2) L(Z r2)' of the Ramaswami-reduction...
Matrix< double > blk(const Matrix< double > &x, std::size_t k) const
Block k (0-based) of a stacked N*m x m generator.
Matrix< double > left(const Matrix< double > &temp) const
temp * B, MG1_RR_tempB.m.
Matrix< double > right(const Matrix< double > &temp) const
B * temp, MG1_RR_Btemp.m.
Singular value decomposition WITH the singular vectors, and the Moore-Penrose pseudo-inverse built fr...