5#ifndef LINE_LIB_SMC_MG1_H
6#define LINE_LIB_SMC_MG1_H
75using Blocks = std::vector<Matrix<double>>;
85 throw InputError(
"smc: matrix addition with mismatched shapes");
87 for (std::size_t i = 0; i < A.
rows(); ++i)
88 for (std::size_t j = 0; j < A.
cols(); ++j) C(i, j) = A(i, j) + B(i, j);
96 throw InputError(
"smc: matrix subtraction with mismatched shapes");
98 for (std::size_t i = 0; i < A.
rows(); ++i)
99 for (std::size_t j = 0; j < A.
cols(); ++j) C(i, j) = A(i, j) - B(i, j);
106 for (std::size_t i = 0; i < A.
rows(); ++i)
107 for (std::size_t j = 0; j < A.
cols(); ++j) C(i, j) = c * A(i, j);
113 std::vector<double> s(A.
rows(), 0.0);
114 for (std::size_t i = 0; i < A.
rows(); ++i)
115 for (std::size_t j = 0; j < A.
cols(); ++j) s[i] += A(i, j);
122 for (std::size_t i = 0; i < A.
rows(); ++i) {
124 for (std::size_t j = 0; j < A.
cols(); ++j) s += std::fabs(A(i, j));
125 if (s > best) best = s;
133 for (std::size_t i = from; i < blk.
size(); ++i) best = std::max(best,
inf_norm(blk[i]));
140 for (std::size_t i = 0; i < A.
rows(); ++i)
141 for (std::size_t j = 0; j < A.
cols(); ++j)
142 best = std::max(best, std::fabs(A(i, j) - B(i, j)));
148 double best = -std::numeric_limits<double>::infinity();
149 for (std::size_t j = 0; j < A.
cols(); ++j) {
151 for (std::size_t i = 0; i < A.
rows(); ++i) s += A(i, j);
152 if (s > best) best = s;
159 if (m == 0 || A.
cols() % m != 0)
160 throw InputError(
"smc: the block sequence has an incorrect number of columns");
161 const std::size_t nb = A.
cols() / m;
163 for (std::size_t b = 0; b < nb; ++b)
164 for (std::size_t i = 0; i < A.
rows(); ++i)
165 for (std::size_t j = 0; j < m; ++j) out[b](i, j) = A(i, b * m + j);
172 const std::size_t r = blk[0].
rows(), c = blk[0].
cols();
174 for (std::size_t b = 0; b < blk.
size(); ++b)
175 for (std::size_t i = 0; i < r; ++i)
176 for (std::size_t j = 0; j < c; ++j) A(i, b * c + j) = blk[b](i, j);
183 const std::size_t r = blk[0].
rows(), c = blk[0].
cols();
185 for (std::size_t b = 0; b < blk.
size(); ++b)
186 for (std::size_t i = 0; i < r; ++i)
187 for (std::size_t j = 0; j < c; ++j) A(b * r + i, j) = blk[b](i, j);
193 if (r == 0 || A.
rows() % r != 0)
194 throw InputError(
"smc: the stacked block sequence has an incorrect number of rows");
195 const std::size_t nb = A.
rows() / r;
197 for (std::size_t b = 0; b < nb; ++b)
198 for (std::size_t i = 0; i < r; ++i)
199 for (std::size_t j = 0; j < A.
cols(); ++j) out[b](i, j) = A(b * r + i, j);
218 const std::size_t S = A.
rows();
219 if (A.
cols() != S)
throw InputError(
"stat: matrix is not square");
222 for (std::size_t i = 0; i < S; ++i)
223 for (std::size_t j = 0; j < S; ++j) Bt(j, i) = A(i, j) - (i == j ? 1.0 : 0.0);
224 for (std::size_t i = 0; i < S; ++i) Bt(S, i) = 1.0;
225 std::vector<double> y(S + 1, 0.0);
227 return lstsq(Bt, y, detail::lstsq_tolerance(Bt)).x;
236inline double dot(
const std::vector<double>& a,
const std::vector<double>& b) {
237 if (a.size() != b.size())
throw InputError(
"smc: inner product with mismatched lengths");
239 for (std::size_t i = 0; i < a.size(); ++i) s += a[i] * b[i];
255 const std::size_t dega = A.size() - 1;
257 std::vector<double> beta =
rowsums(sumA);
258 for (std::size_t i = dega; i-- > 1;) {
259 sumA =
madd(sumA, A[i]);
260 const std::vector<double> rs =
rowsums(sumA);
261 for (std::size_t k = 0; k < beta.size(); ++k) beta[k] += rs[k];
263 sumA =
madd(sumA, A[0]);
282 const std::vector<std::complex<double>> ev =
eig_values(M);
283 if (ev.empty())
throw NumericError(
"smc: empty spectrum");
284 std::complex<double> best = ev[0];
285 for (
const std::complex<double>& z : ev) {
286 const double mz = std::abs(z), mb = std::abs(best);
287 if (mz > mb || (mz == mb && std::arg(z) > std::arg(best))) best = z;
295 for (std::size_t i = A.size() - 1; i-- > 0;) temp =
madd(
mscale(temp, z), A[i]);
306 double eta = 1.0, new_eta = 0.0;
307 while (new_eta - eta < 0.0) {
311 double eta_min = eta - 1.0, eta_max = eta;
313 while (eta_max - eta_min > 1e-15) {
320 eta = (eta_min + eta_max) / 2.0;
330 double eta_min = 0.0, eta_max = 1.0, eta = 0.5;
331 while (eta_max - eta_min > 1e-15) {
338 eta = (eta_min + eta_max) / 2.0;
352 std::vector<double>
v;
368 if (shift_type !=
"one")
370 "MG1_Shifts: ShiftType '" + shift_type +
371 "' is not ported. Its reference branch writes rowhatA(1,maxd*i:end) with `i` "
372 "undefined at that point, so it addresses column maxd of a sequence whose blocks "
373 "start every m columns and does not compute the last block it needs; the port "
374 "refuses rather than transcribe a line that cannot run. ETAQA uses ShiftType 'one'");
377 const std::size_t m = A[0].rows();
380 if (!(d.
value < 1.0))
382 "MG1_Shifts: the drift > 1 branch of ShiftType 'one' is not ported, for the same "
383 "reason as ShiftType 'tau': it shifts one to infinity through the same defective "
384 "rowhatA(1,maxd*i:end) line. A transient M/G/1-type chain is outside what ETAQA "
388 for (std::size_t i = 0; i < m; ++i) A[1](i, i) -= 1.0;
389 std::vector<double> col(m, 0.0);
391 for (std::size_t b = 0; b < A.size(); ++b) {
392 const std::vector<double> rs =
rowsums(A[b]);
393 for (std::size_t i = 0; i < m; ++i) col[i] += rs[i];
394 for (std::size_t i = 0; i < m; ++i)
395 for (std::size_t j = 0; j < m; ++j)
396 hatA[b](i, j) = A[b](i, j) - col[i] /
static_cast<double>(m);
398 for (std::size_t i = 0; i < m; ++i) hatA[1](i, i) += 1.0;
403 out.
v.assign(m, 0.0);
423 const std::size_t m = A[0].rows();
424 const std::size_t dega = A.size() - 1;
431 const std::vector<double> rs =
rowsums(A[0]);
432 std::size_t first = m;
433 for (std::size_t i = 0; i < m; ++i)
440 for (std::size_t i = 0; i < m; ++i)
441 for (std::size_t j = 0; j < m; ++j) G(i, j) = A[0](first, j) / rs[first];
448 for (std::size_t b = 0; b < A.size(); ++b)
449 for (std::size_t i = 0; i < m; ++i)
450 for (std::size_t j = 0; j < m; ++j)
451 At[b](i, j) = A[b](j, i) * d.
theta[j] / d.
theta[i];
454 for (std::size_t i = dega; i-- > 1;) temp =
madd(
mscale(temp, etahat), At[i]);
457 for (std::size_t i = 0; i < m; ++i)
458 for (std::size_t j = 0; j < m; ++j) G(i, j) = M(j, i) * d.
theta[j] / d.
theta[i];
471 std::string
mode =
"ShiftPWCR";
481inline std::vector<Matrix<std::complex<double>>> block_dft(
const Blocks& blk, std::size_t n,
482 std::size_t use,
bool inverse) {
483 const std::size_t m = blk[0].
rows();
484 std::vector<Matrix<std::complex<double>>> out(
485 n,
Matrix<std::complex<double>>(m, m, std::complex<double>(0.0, 0.0)));
486 std::vector<std::complex<double>> buf(n);
487 for (std::size_t i = 0; i < m; ++i)
488 for (std::size_t j = 0; j < m; ++j) {
489 for (std::size_t k = 0; k < n; ++k)
490 buf[k] = (k < use && k < blk.
size()) ? std::complex<double>(blk[k](i, j), 0.0)
491 : std::complex<double>(0.0, 0.0);
493 for (std::size_t k = 0; k < n; ++k) out[k](i, j) = buf[k];
499inline Blocks block_idft_real(
const std::vector<
Matrix<std::complex<double>>>& F) {
500 const std::size_t n = F.size(), m = F[0].rows();
502 std::vector<std::complex<double>> buf(n);
503 for (std::size_t i = 0; i < m; ++i)
504 for (std::size_t j = 0; j < m; ++j) {
505 for (std::size_t k = 0; k < n; ++k) buf[k] = F[k](i, j);
507 for (std::size_t k = 0; k < n; ++k) out[k](i, j) = buf[k].real();
512inline Matrix<std::complex<double>> cmul(
const Matrix<std::complex<double>>& A,
513 const Matrix<std::complex<double>>& B) {
517inline Matrix<std::complex<double>> cinv_i_minus(
const Matrix<std::complex<double>>& A) {
518 const std::size_t m = A.rows();
519 Matrix<std::complex<double>> M(m, m, std::complex<double>(0.0, 0.0));
520 for (std::size_t i = 0; i < m; ++i)
521 for (std::size_t j = 0; j < m; ++j)
522 M(i, j) = (i == j ? std::complex<double>(1.0, 0.0) : std::complex<double>(0.0, 0.0)) -
528inline double tail_norm(
const Blocks& blk) {
529 const std::size_t deg = blk.
size();
534 const std::size_t start = (deg + 1) / 2;
536 for (std::size_t i = start; i + 1 <= deg && i < deg; ++i) best = std::max(best,
inf_norm(blk[i]));
543 for (std::size_t i = 0; i < b.size(); i += 2) out.push_back(b[i]);
550 for (std::size_t i = 1; i < b.size(); i += 2) out.push_back(b[i]);
576 if (Ain.empty())
throw InputError(
"MG1_CR: empty block sequence");
577 const std::size_t m = Ain[0].rows();
578 if (opts.mode !=
"ShiftPWCR" && opts.mode !=
"PWCR")
580 "' is not supported; the reference offers 'PWCR' and 'ShiftPWCR'");
582 bool eg_found =
false;
584 if (eg_found)
return Geg;
588 if (opts.mode ==
"ShiftPWCR") {
595 const std::size_t maxd = A.size() - 1;
596 if (maxd == 0)
throw InputError(
"MG1_CR: the sequence needs at least two blocks");
597 std::size_t target = 1;
598 while (target < maxd) target <<= 1;
599 if (target == maxd) target <<= 1;
602 for (std::size_t b = 0; b <= maxd; ++b) D[b] = A[b].transpose();
604 Blocks Aeven = cr_detail::even_blocks(D);
605 Blocks Aodd = cr_detail::odd_blocks(D);
606 Blocks Ahatodd(Aeven.begin() + 1, Aeven.end());
607 Ahatodd.push_back(D.back());
611 for (std::size_t i = 2; i < D.size(); ++i) Rj =
madd(Rj, D[i]);
616 std::size_t numit = 0;
617 while (numit < opts.max_num_it) {
619 std::size_t nj = Aodd.size() - 1;
620 double nAnew = 0.0, nAhatnew = 0.0;
623 const std::size_t n = nj + 1;
624 const std::vector<Matrix<std::complex<double>>> T1 =
625 cr_detail::block_dft(Aodd, n, n,
false);
626 const std::vector<Matrix<std::complex<double>>> T2 =
627 cr_detail::block_dft(Aeven, n, n,
false);
628 const std::vector<Matrix<std::complex<double>>> T3 =
629 cr_detail::block_dft(Ahatodd, n, n,
false);
630 const std::vector<Matrix<std::complex<double>>> T4 =
631 cr_detail::block_dft(Ahateven, n, n,
false);
632 std::vector<Matrix<std::complex<double>>> Ah(n), An(n);
633 const double pi = 3.14159265358979323846;
634 for (std::size_t c = 0; c < n; ++c) {
636 Ah[c] =
madd(T4[c], cr_detail::cmul(cr_detail::cmul(T2[c], W), T3[c]));
637 const double ang = -2.0 * pi *
static_cast<double>(c) /
static_cast<double>(n);
638 const std::complex<double> w(std::cos(ang), std::sin(ang));
640 for (std::size_t i = 0; i < m; ++i)
641 for (std::size_t j = 0; j < m; ++j) first(i, j) = w * T1[c](i, j);
642 An[c] =
madd(first, cr_detail::cmul(cr_detail::cmul(T2[c], W), T2[c]));
644 Ahatnew = cr_detail::block_idft_real(Ah);
645 Anew = cr_detail::block_idft_real(An);
649 Ahatnew.assign(1,
madd(Ahateven[0],
matmul(temp, Ahatodd[0])));
651 Anew.push_back(
matmul(temp, Aeven[0]));
652 Anew.push_back(Aodd[0]);
655 nAnew = cr_detail::tail_norm(Anew);
656 nAhatnew = cr_detail::tail_norm(Ahatnew);
659 while ((nAnew >
static_cast<double>(nj + 1) * opts.epsilon ||
660 nAhatnew >
static_cast<double>(nj + 1) * opts.epsilon) &&
661 nj + 1 < opts.max_num_root) {
662 nj = 2 * (nj + 1) - 1;
663 const std::size_t n = nj + 1;
664 const std::size_t stopv = std::min(n, Aodd.size());
665 const std::vector<Matrix<std::complex<double>>> T1 =
666 cr_detail::block_dft(Aodd, n, stopv,
false);
667 const std::vector<Matrix<std::complex<double>>> T2 =
668 cr_detail::block_dft(Aeven, n, stopv,
false);
669 const std::vector<Matrix<std::complex<double>>> T3 =
670 cr_detail::block_dft(Ahatodd, n, stopv,
false);
671 const std::vector<Matrix<std::complex<double>>> T4 =
672 cr_detail::block_dft(Ahateven, n, stopv,
false);
673 std::vector<Matrix<std::complex<double>>> Ah(n), An(n);
674 const double pi = 3.14159265358979323846;
675 for (std::size_t c = 0; c < n; ++c) {
677 Ah[c] =
madd(T4[c], cr_detail::cmul(cr_detail::cmul(T2[c], W), T3[c]));
678 const double ang = -2.0 * pi *
static_cast<double>(c) /
static_cast<double>(n);
679 const std::complex<double> w(std::cos(ang), std::sin(ang));
681 for (std::size_t i = 0; i < m; ++i)
682 for (std::size_t j = 0; j < m; ++j) first(i, j) = w * T1[c](i, j);
683 An[c] =
madd(first, cr_detail::cmul(cr_detail::cmul(T2[c], W), T2[c]));
685 Ahatnew = cr_detail::block_idft_real(Ah);
686 Anew = cr_detail::block_idft_real(An);
687 nAnew = cr_detail::tail_norm(Anew);
688 nAhatnew = cr_detail::tail_norm(Ahatnew);
692 const std::size_t keep = (nj + 1) / 2;
693 Anew.resize(std::min(keep, Anew.size()));
694 Ahatnew.resize(std::min(keep, Ahatnew.size()));
697 Aeven = cr_detail::even_blocks(Anew);
698 Aodd = cr_detail::odd_blocks(Anew);
699 Ahateven = cr_detail::even_blocks(Ahatnew);
700 Ahatodd = cr_detail::odd_blocks(Ahatnew);
702 if (opts.mode ==
"PWCR") {
704 for (std::size_t i = 2; i < Anew.size(); ++i) Rnewj =
madd(Rnewj, Anew[i]);
713 for (std::size_t i = 1; i < Ahatnew.size(); ++i)
719 double tail_sum = 0.0;
720 for (std::size_t i = 1; i < Ahatnew.size(); ++i) tail_sum += Ahatnew[i].
sum();
724 if (sv < opts.epsilon || tail_sum < opts.epsilon ||
max_col_sum(V) < opts.epsilon) {
735 if (numit == opts.max_num_it && !Ahatnew.empty())
742 if (opts.mode ==
"ShiftPWCR" && drift < 1.0)
743 for (std::size_t i = 0; i < m; ++i)
744 for (std::size_t j = 0; j < m; ++j) G(i, j) += 1.0 /
static_cast<double>(m);
771 const std::size_t m = Ain[0].rows();
772 const std::size_t maxd = Ain.size() - 1;
774 bool eg_found =
false;
776 if (eg_found)
return Geg;
779 const bool shifted = opts.mode.find(
"Shift") != std::string::npos;
787 const bool natural = opts.mode.find(
"Natural") != std::string::npos;
788 const bool traditional = opts.mode.find(
"Traditional") != std::string::npos;
789 const bool ubased = opts.mode.find(
"U-Based") != std::string::npos;
790 if (!natural && !traditional && !ubased)
791 throw UnsupportedError(
"MG1_FI: Mode '" + opts.mode +
"' is not supported");
795 std::size_t numit = 0;
796 while (check > opts.tol && numit < opts.max_num_it) {
800 for (std::size_t j = maxd; j-- > 0;) G =
madd(A[j],
matmul(G, Gold));
801 }
else if (traditional) {
803 for (std::size_t j = maxd; j-- > 2;) G =
madd(A[j],
matmul(G, Gold));
808 for (std::size_t j = maxd; j-- > 1;) G =
madd(A[j],
matmul(G, Gold));
815 if (shifted && drift < 1.0)
816 for (std::size_t i = 0; i < m; ++i)
817 for (std::size_t j = 0; j < m; ++j) G(i, j) += 1.0 /
static_cast<double>(m);
839 const std::string& algor) {
840 const std::size_t m = Ain[0].rows();
841 const std::size_t dega = Ain.size() - 1;
846 std::vector<double> theta = d.
theta;
847 const bool ram = (dual ==
"R") || (dual ==
"A" && d.
value <= 1.0);
851 for (std::size_t b = 0; b <= dega; ++b) {
853 for (std::size_t i = 0; i < m; ++i)
854 for (std::size_t j = 0; j < m; ++j) Bb(i, j) = A[b](j, i) * theta[j] / theta[i];
857 }
else if (dual ==
"B" || dual ==
"A") {
861 for (std::size_t i = dega; i-- > 0;)
862 sumAeta =
madd(sumAeta,
mscale(Ain[i], std::pow(eta,
static_cast<double>(i))));
864 for (std::size_t i = 0; i < m; ++i) shifted(i, i) += (1.0 - eta);
865 theta =
stat(shifted);
866 for (std::size_t b = 0; b <= dega; ++b) {
868 const double s = std::pow(eta,
static_cast<double>(b) - 1.0);
869 for (std::size_t i = 0; i < m; ++i)
870 for (std::size_t j = 0; j < m; ++j)
871 Bb(i, j) = s * A[b](j, i) * theta[j] / theta[i];
875 throw InputError(
"GIM1_R: Dual '" + dual +
"' is not one of 'A', 'B', 'R'");
881 }
else if (algor ==
"CR") {
885 "GIM1_R: Algor '" + algor +
886 "' is not ported; MG1_NI, MG1_RR and MG1_IS have no C++ counterpart. "
887 "'FI' (the one GIM1_R_ETAQA asks for) and 'CR' are available");
891 for (std::size_t i = 0; i < m; ++i)
892 for (std::size_t j = 0; j < m; ++j) R(i, j) = G(j, i) * theta[j] / theta[i];
894 for (std::size_t i = 0; i < m; ++i)
895 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.
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.
double mg1_decay(const Blocks &A)
Decay rate of a recurrent M/G/1-type chain: the unique z > 1 with PF(A(z)) = z.
Blocks blocks_of(const Matrix< double > &A, std::size_t m)
Splits the wide [A0 A1 ... Amax] into its m x m blocks.
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 > 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 > 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< T > madd(const Matrix< T > &A, const Matrix< T > &B)
A + B.
std::vector< double > rowvec_times(const std::vector< double > &v, const Matrix< double > &A)
theta A, the row vector times matrix product used throughout.
double gim1_caudal(const Blocks &A)
Caudal characteristic of a GI/M/1-type chain: the spectral radius of R, the unique z in (0,...
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].
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.
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
std::vector< std::complex< double > > eig_values(const Matrix< double > &A)
Eigenvalues of a general real square matrix, in LAPACK's order.
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.
void dft(std::vector< std::complex< double > > &a, bool inverse)
In-place DFT of a.
Number-type abstraction for the templated API port.
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>'
What MG1_Shifts returns: the shifted sequence and the drift it measured.