5#ifndef LINE_API_MAM_MAM_TRANSIENT2_H
6#define LINE_API_MAM_MAM_TRANSIENT2_H
53namespace transient_detail {
57 if (A.
rows() != A.
cols())
throw InputError(
"mam_transient2: left division needs a square matrix");
58 if (A.
rows() != B.
rows())
throw InputError(
"mam_transient2: left division dimension mismatch");
60 const std::vector<std::size_t> piv =
lu_factor(LU);
62 std::vector<Complex> col(B.
rows());
63 for (std::size_t j = 0; j < B.
cols(); ++j) {
64 for (std::size_t i = 0; i < B.
rows(); ++i) col[i] = B(i, j);
66 for (std::size_t i = 0; i < B.
rows(); ++i) X(i, j) = col[i];
77inline CMat mxpow(
const CMat& A,
long k) {
78 if (k < 0)
throw InputError(
"mam_transient2: mxpow requires a non-negative exponent");
80 return matpow(A,
static_cast<unsigned>(k));
85 throw InputError(
"mam_transient2: addition shape mismatch");
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);
94 throw InputError(
"mam_transient2: subtraction shape mismatch");
96 for (std::size_t i = 0; i < A.
rows(); ++i)
97 for (std::size_t j = 0; j < A.
cols(); ++j) C(i, j) = A(i, j) - B(i, j);
104 for (std::size_t i = 0; i < n; ++i) M(i, i) = s;
110 const std::size_t n = A.
rows();
111 CMat M(2 * n, 2 * n);
112 for (std::size_t i = 0; i < n; ++i)
113 for (std::size_t j = 0; j < n; ++j) {
115 M(i, n + j) = B(i, j);
116 M(n + i, j) = C(i, j);
117 M(n + i, n + j) = D(i, j);
126 for (std::size_t i = 0; i < A.
rows(); ++i)
127 for (std::size_t j = 0; j < A.
cols(); ++j) M(i, j) = A(i, j);
128 for (std::size_t i = 0; i < B.
rows(); ++i)
129 for (std::size_t j = 0; j < B.
cols(); ++j) M(A.
rows() + i, j) = B(i, j);
134inline CMat sub(
const CMat& A, std::size_t r0, std::size_t nr, std::size_t c0, std::size_t
nc) {
136 for (std::size_t i = 0; i < nr; ++i)
137 for (std::size_t j = 0; j <
nc; ++j) M(i, j) = A(r0 + i, c0 + j);
142inline double norminf(
const CMat& A) {
144 for (std::size_t i = 0; i < A.
rows(); ++i) {
146 for (std::size_t j = 0; j < A.
cols(); ++j) s += std::abs(A(i, j));
147 if (s > best) best = s;
171 double precision = 1e-14,
unsigned maxNumIt = 50) {
172 using namespace transient_detail;
173 const std::size_t m = L.
rows();
175 throw InputError(
"qbd_fundmat_laplace: B, L and F must be square and of equal order");
178 double lamb = -std::numeric_limits<double>::infinity();
179 for (std::size_t i = 0; i < m; ++i) lamb = std::max(lamb, -L(i, i).real());
181 throw NumericError(
"qbd_fundmat_laplace: the local block has no negative real diagonal");
183 CMat Bm(m, m), Lm(m, m), Fm(m, m);
184 for (std::size_t i = 0; i < m; ++i)
185 for (std::size_t j = 0; j < m; ++j) {
186 Bm(i, j) = B(i, j) / lamb;
187 Lm(i, j) = L(i, j) / lamb + (i == j ?
Complex(1.0, 0.0) :
Complex(0.0, 0.0));
188 Fm(i, j) = F(i, j) / lamb;
191 CMat BF = mldivide(msub(II, Lm), II);
198 while (check > precision && numit < maxNumIt) {
203 BB = mldivide(msub(II, Lstar), II);
206 G = madd(G,
matmul(PI, BF));
208 check = std::min(norminf(BB), norminf(BF));
214 out.
R =
matmul(Fm, mldivide(msub(II, madd(Lm,
matmul(Fm, G))), II));
230 const CMat&
B1(std::size_t k)
const {
return B.at(k); }
231 const CMat&
L1(std::size_t k)
const {
return L.at(k); }
232 const CMat&
F1(std::size_t k)
const {
return F.at(k); }
233 const CMat&
Lv1(std::size_t k)
const {
return Lv.at(k); }
234 long T1(std::size_t k)
const {
return T.at(k); }
235 std::size_t
nT()
const {
return T.size() - 1; }
240 const std::vector<CMat>& F,
const std::vector<CMat>& Lv,
241 const std::vector<long>& T) {
243 q.
B.push_back(
CMat());
244 q.
L.push_back(
CMat());
245 q.
F.push_back(
CMat());
248 q.
B.insert(q.
B.end(), B.begin(), B.end());
249 q.
L.insert(q.
L.end(), L.begin(), L.end());
250 q.
F.insert(q.
F.end(), F.begin(), F.end());
251 q.
Lv.insert(q.
Lv.end(), Lv.begin(), Lv.end());
252 q.
T.insert(q.
T.end(), T.begin(), T.end());
256namespace transient_detail {
259inline std::size_t regime_of(
const TransientQbd& q,
long n, std::size_t fallback) {
260 for (std::size_t i = 1; i <= q.nT(); ++i)
261 if (q.T1(i) > n)
return i - 1;
272 CMat nn, n0, hnn, hn0;
275inline HBlocks h_blocks(
const CMat& G,
const CMat& Gh,
long d, std::size_t NN) {
277 const CMat num = block2(mxpow(Gh, d - 1), G, Gh, mxpow(G, d - 1));
278 const CMat den = block2(II, mxpow(G, d), mxpow(Gh, d), II);
279 const CMat Tmp = mrdivide(num, den);
281 h.nn = sub(Tmp, 0, NN, 0, NN);
282 h.n0 = sub(Tmp, 0, NN, NN, NN);
283 h.hnn = sub(Tmp, NN, NN, 0, NN);
284 h.hn0 = sub(Tmp, NN, NN, NN, NN);
297 "mam_transient2: the interpolation needs the repeating R of a regime wider than one "
298 "level, but that regime has none");
299 if (Vl.rows() == 0 || Vu.rows() == 0)
300 throw NumericError(
"mam_transient2: the interpolation needs both enclosing transforms");
301 const std::size_t NN = Rk.rows();
303 const CMat Zden = block2(II, mxpow(Rk, Lu - Ll), mxpow(Rhk, Lu - Ll), II);
304 const CMat Znum = vcat(mxpow(Rk, m - Ll), mxpow(Rhk, Lu - m));
305 const CMat Z = mldivide(Zden, Znum);
306 return madd(
matmul(Vl, sub(Z, 0, NN, 0, Z.cols())),
307 matmul(Vu, sub(Z, NN, NN, 0, Z.cols())));
319 using namespace transient_detail;
320 const std::size_t K = q.
nT();
321 if (K == 0)
throw InputError(
"mam_transient2_open: at least one regime is required");
322 if (n < 0 || m < 0)
throw InputError(
"mam_transient2_open: levels must be non-negative");
324 std::vector<CMat> Gs(K + 1), Rs(K + 1), Ghs(K + 1), Rhs(K + 1);
325 for (std::size_t k = 1; k <= K; ++k) {
326 if (k < K && q.
T1(k + 1) - q.
T1(k) == 1)
continue;
336 std::vector<CMat> SvHn(K + 1), SvH0(K + 1), SvHhn(K + 1), SvHh0(K + 1);
337 for (std::size_t k = 1; k + 1 <= K; ++k) {
338 const std::size_t NN = q.
Lv1(k).
rows();
339 if (q.
T1(k + 1) - q.
T1(k) > 1) {
340 const HBlocks h = h_blocks(Gs[k], Ghs[k], q.
T1(k + 1) - q.
T1(k), NN);
346 const std::size_t NN1 = q.
Lv1(k + 1).
rows();
354 std::vector<CMat> SY(K + 2);
356 for (std::size_t k = K - 1; k >= 1; --k) {
357 const std::size_t NNk1 = q.
Lv1(k + 1).
rows();
358 const CMat M = msub(msub(msub(sI(s, NNk1), q.
Lv1(k + 1)),
matmul(q.
F1(k + 1), SY[k + 1])),
360 SY[k] = madd(SvH0[k],
matmul(SvHn[k], mldivide(M,
matmul(q.
B1(k), SvHh0[k]))));
364 const std::size_t NN1 = q.
Lv1(1).
rows();
365 std::vector<CMat> SYh(K + 1);
367 const CMat M = msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), SvH0[1]));
368 SYh[1] = madd(SvHhn[1],
matmul(SvHh0[1], mldivide(M,
matmul(q.
F1(1), SvHn[1]))));
370 for (std::size_t k = 2; k + 1 <= K; ++k) {
371 const std::size_t NNk = q.
Lv1(k).
rows();
372 const CMat M = msub(msub(msub(sI(s, NNk), q.
Lv1(k)),
matmul(q.
B1(k - 1), SYh[k - 1])),
374 SYh[k] = madd(SvHhn[k],
matmul(SvHh0[k], mldivide(M,
matmul(q.
F1(k), SvHn[k]))));
378 std::vector<std::vector<CMat>> SV(K + 2, std::vector<CMat>(K + 2));
379 for (std::size_t l = 0; l + 1 <= K; ++l) {
381 SV[1][1] = mldivide(msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), SY[1])),
384 const std::size_t NNl1 = q.
Lv1(l + 1).
rows();
385 const CMat M = msub(msub(msub(sI(s, NNl1), q.
Lv1(l + 1)),
matmul(q.
F1(l + 1), SY[l + 1])),
389 for (std::size_t k = l + 1; k + 1 <= K; ++k) {
390 const std::size_t NNk1 = q.
Lv1(k + 1).
rows();
391 const CMat M = msub(msub(msub(sI(s, NNk1), q.
Lv1(k + 1)),
matmul(q.
F1(k + 1), SY[k + 1])),
393 SV[k + 1][l + 1] = mldivide(M,
matmul(
matmul(q.
B1(k), SvHh0[k]), SV[k][l + 1]));
395 for (std::size_t k = l; k-- > 1;) {
396 const std::size_t NNk1 = q.
Lv1(k + 1).
rows();
397 const CMat M = msub(msub(msub(sI(s, NNk1), q.
Lv1(k + 1)),
matmul(q.
F1(k + 1), SvH0[k + 1])),
399 SV[k + 1][l + 1] = mldivide(M,
matmul(
matmul(q.
F1(k + 1), SvHn[k + 1]), SV[k + 2][l + 1]));
402 const CMat M = msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), SvH0[1]));
403 SV[1][l + 1] = mldivide(M,
matmul(
matmul(q.
F1(1), SvHn[1]), SV[2][l + 1]));
407 const std::size_t kn = regime_of(q, n, K);
408 const std::size_t km = regime_of(q, m, K);
410 const std::size_t NNkn = q.
Lv1(kn).
rows();
417 if (q.
T1(km) == m)
return SV[kn][km];
428 hT = h_blocks(Gs[kn], Ghs[kn], q.
T1(kn + 1) - n, NNkn);
429 hn = h_blocks(Gs[kn], Ghs[kn], n - q.
T1(kn), NNkn);
430 const std::size_t NNkn1 = q.
Lv1(kn + 1).
rows();
431 const CMat M = msub(msub(msub(sI(s, NNkn1), q.
Lv1(kn + 1)),
matmul(q.
F1(kn + 1), SY[kn + 1])),
433 Yn = madd(hT.n0,
matmul(hT.nn, mldivide(M,
matmul(q.
B1(kn), hT.hn0))));
435 hn = h_blocks(Gs[kn], Ghs[kn], n - q.
T1(kn), NNkn);
441 const CMat M = msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), hn.n0));
442 Yhn = madd(hn.hnn,
matmul(hn.hn0, mldivide(M,
matmul(q.
F1(1), hn.nn))));
444 const std::size_t NNk = q.
Lv1(kn).
rows();
445 const CMat M = msub(msub(msub(sI(s, NNk), q.
Lv1(kn)),
matmul(q.
B1(kn - 1), SYh[kn - 1])),
447 Yhn = madd(hn.hnn,
matmul(hn.hn0, mldivide(M,
matmul(q.
F1(kn), hn.nn))));
450 const CMat Mkn = msub(sI(s, q.
L1(kn).
rows()), q.
L1(kn));
457 Vnl = mldivide(M,
matmul(
matmul(q.
F1(kn), hT.nn), SV[kn + 1][km]));
459 if (m == q.
T1(km))
return Vnl;
463 if (q.
T1(km + 1) < n) {
465 Vnu = mldivide(M,
matmul(
matmul(q.
B1(kn), hn.hn0), SV[kn][km + 1]));
468 Vnu = mldivide(M,
matmul(
matmul(q.
F1(kn), hT.nn), SV[kn + 1][km + 1]));
471 const CMat Vnn = mldivide(
474 if (n == m)
return Vnn;
475 if (km == K && n < m) {
476 if (kn < K)
return matmul(Vnl, mxpow(Rs[km], m - q.
T1(km)));
477 return matmul(Vnn, mxpow(Rs[km], m - n));
480 Vu = Vnu; Vl = Vnl; Lu = q.
T1(km + 1); Ll = q.
T1(km);
482 Vu = Vnu; Vl = Vnn; Lu = q.
T1(km + 1); Ll = n;
484 Vu = Vnn; Vl = Vnl; Lu = n; Ll = q.
T1(km);
488 if (km == K && n < m) {
489 if (kn < K)
return matmul(Vl, mxpow(Rs[km], m - q.
T1(km)));
490 return matmul(SV[kn][kn], mxpow(Rs[km], m - n));
493 return interpolate(Rs[km], Rhs[km], Vl, Vu, m, Ll, Lu);
503 using namespace transient_detail;
504 if (q.
nT() < 2)
throw InputError(
"mam_transient2: the finite form needs at least two thresholds");
505 const std::size_t K = q.
nT() - 1;
506 if (n < 0 || m < 0)
throw InputError(
"mam_transient2: levels must be non-negative");
508 std::vector<CMat> Gs(K + 1), Rs(K + 1), Ghs(K + 1), Rhs(K + 1);
509 for (std::size_t k = 1; k <= K; ++k) {
510 if (q.
T1(k + 1) - q.
T1(k) == 1)
continue;
520 std::vector<CMat> SvHn(K + 1), SvH0(K + 1), SvHhn(K + 1), SvHh0(K + 1);
521 for (std::size_t k = 1; k <= K; ++k) {
522 const std::size_t NN = q.
Lv1(k).
rows();
523 if (q.
T1(k + 1) - q.
T1(k) > 1) {
524 const HBlocks h = h_blocks(Gs[k], Ghs[k], q.
T1(k + 1) - q.
T1(k), NN);
530 const std::size_t NN1 = q.
Lv1(k + 1).
rows();
538 const std::size_t NNK = q.
Lv1(K + 1).
rows();
539 std::vector<CMat> SY(K + 2);
541 const CMat M = msub(msub(sI(s, NNK), q.
Lv1(K + 1)),
matmul(q.
B1(K), SvHhn[K]));
542 SY[K] = madd(SvH0[K],
matmul(SvHn[K], mldivide(M,
matmul(q.
B1(K), SvHh0[K]))));
544 for (std::size_t k = K; k-- > 1;) {
545 const std::size_t NNk1 = q.
Lv1(k + 1).
rows();
546 const CMat M = msub(msub(msub(sI(s, NNk1), q.
Lv1(k + 1)),
matmul(q.
F1(k + 1), SY[k + 1])),
548 SY[k] = madd(SvH0[k],
matmul(SvHn[k], mldivide(M,
matmul(q.
B1(k), SvHh0[k]))));
551 const std::size_t NN1 = q.
Lv1(1).
rows();
552 std::vector<CMat> SYh(K + 1);
554 const CMat M = msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), SvH0[1]));
555 SYh[1] = madd(SvHhn[1],
matmul(SvHh0[1], mldivide(M,
matmul(q.
F1(1), SvHn[1]))));
557 for (std::size_t k = 2; k <= K; ++k) {
558 const std::size_t NNk = q.
Lv1(k).
rows();
559 const CMat M = msub(msub(msub(sI(s, NNk), q.
Lv1(k)),
matmul(q.
B1(k - 1), SYh[k - 1])),
561 SYh[k] = madd(SvHhn[k],
matmul(SvHh0[k], mldivide(M,
matmul(q.
F1(k), SvHn[k]))));
564 std::vector<std::vector<CMat>> SV(K + 3, std::vector<CMat>(K + 3));
565 for (std::size_t l = 0; l <= K; ++l) {
567 SV[1][1] = mldivide(msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), SY[1])),
570 SV[K + 1][K + 1] = mldivide(
573 const std::size_t NNl1 = q.
Lv1(l + 1).
rows();
574 const CMat M = msub(msub(msub(sI(s, NNl1), q.
Lv1(l + 1)),
matmul(q.
F1(l + 1), SY[l + 1])),
578 for (std::size_t k = l + 1; k + 1 <= K; ++k) {
579 const std::size_t NNk1 = q.
Lv1(k + 1).
rows();
580 const CMat M = msub(msub(msub(sI(s, NNk1), q.
Lv1(k + 1)),
matmul(q.
F1(k + 1), SY[k + 1])),
582 SV[k + 1][l + 1] = mldivide(M,
matmul(
matmul(q.
B1(k), SvHh0[k]), SV[k][l + 1]));
585 const CMat M = msub(msub(sI(s, NNK), q.
Lv1(K + 1)),
matmul(q.
B1(K), SvHhn[K]));
586 SV[K + 1][l + 1] = mldivide(M,
matmul(
matmul(q.
B1(K), SvHh0[K]), SV[K][l + 1]));
588 for (std::size_t k = l; k-- > 1;) {
589 const std::size_t NNk1 = q.
Lv1(k + 1).
rows();
590 const CMat M = msub(msub(msub(sI(s, NNk1), q.
Lv1(k + 1)),
matmul(q.
F1(k + 1), SvH0[k + 1])),
592 SV[k + 1][l + 1] = mldivide(M,
matmul(
matmul(q.
F1(k + 1), SvHn[k + 1]), SV[k + 2][l + 1]));
595 const CMat M = msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), SvH0[1]));
596 SV[1][l + 1] = mldivide(M,
matmul(
matmul(q.
F1(1), SvHn[1]), SV[2][l + 1]));
600 const std::size_t kn = regime_of(q, n, K + 1);
601 const std::size_t km = regime_of(q, m, K + 1);
603 const std::size_t NNkn = q.
Lv1(kn).
rows();
610 if (q.
T1(km) == m)
return SV[kn][km];
616 const HBlocks hT = h_blocks(Gs[kn], Ghs[kn], q.
T1(kn + 1) - n, NNkn);
617 const HBlocks hn = h_blocks(Gs[kn], Ghs[kn], n - q.
T1(kn), NNkn);
621 const CMat M = msub(msub(sI(s, NNK), q.
Lv1(K + 1)),
matmul(q.
B1(K), hT.hnn));
622 Yn = madd(hT.n0,
matmul(hT.nn, mldivide(M,
matmul(q.
B1(K), hT.hn0))));
624 const std::size_t NNkn1 = q.
Lv1(kn + 1).
rows();
625 const CMat M = msub(msub(msub(sI(s, NNkn1), q.
Lv1(kn + 1)),
matmul(q.
F1(kn + 1), SY[kn + 1])),
627 Yn = madd(hT.n0,
matmul(hT.nn, mldivide(M,
matmul(q.
B1(kn), hT.hn0))));
632 const CMat M = msub(msub(sI(s, NN1), q.
Lv1(1)),
matmul(q.
F1(1), hn.n0));
633 Yhn = madd(hn.hnn,
matmul(hn.hn0, mldivide(M,
matmul(q.
F1(1), hn.nn))));
635 const std::size_t NNk = q.
Lv1(kn).
rows();
636 const CMat M = msub(msub(msub(sI(s, NNk), q.
Lv1(kn)),
matmul(q.
B1(kn - 1), SYh[kn - 1])),
638 Yhn = madd(hn.hnn,
matmul(hn.hn0, mldivide(M,
matmul(q.
F1(kn), hn.nn))));
641 const CMat Mkn = msub(sI(s, q.
L1(kn).
rows()), q.
L1(kn));
648 Vnl = mldivide(M,
matmul(
matmul(q.
F1(kn), hT.nn), SV[kn + 1][km]));
650 if (m == q.
T1(km))
return Vnl;
653 if (q.
T1(km + 1) < n) {
655 Vnu = mldivide(M,
matmul(
matmul(q.
B1(kn), hn.hn0), SV[kn][km + 1]));
658 Vnu = mldivide(M,
matmul(
matmul(q.
F1(kn), hT.nn), SV[kn + 1][km + 1]));
660 const CMat Vnn = mldivide(
663 if (n == m)
return Vnn;
665 Vu = Vnu; Vl = Vnl; Lu = q.
T1(km + 1); Ll = q.
T1(km);
667 Vu = Vnu; Vl = Vnn; Lu = q.
T1(km + 1); Ll = n;
669 Vu = Vnn; Vl = Vnl; Lu = n; Ll = q.
T1(km);
673 return interpolate(Rs[km], Rhs[km], Vl, Vu, m, Ll, Lu);
NumericError(const std::string &what)
std::complex<double> as a number type for the generic linear algebra.
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
CMat mam_transient2_open(const TransientQbd &q, long n, long m, const Complex &s)
V(s,n,m) for an OPEN piecewise QBD, the port of mam_transient2_open.m.
CMat mam_transient2(const TransientQbd &q, long n, long m, const Complex &s)
V(s,n,m) for a FINITE piecewise QBD, the port of mam_transient2.m.
CQbdFundMat qbd_fundmat_laplace(const CMat &B, const CMat &L, const CMat &F, double precision=1e-14, unsigned maxNumIt=50)
Port of qbd_fundmat.m at complex argument: cyclic reduction (Bini-Meini logarithmic reduction) on the...
TransientQbd make_transient_qbd(const std::vector< CMat > &B, const std::vector< CMat > &L, const std::vector< CMat > &F, const std::vector< CMat > &Lv, const std::vector< long > &T)
Build the 1-based padded form from plain 0-based vectors.
void lu_solve(const Matrix< T > &LU, const std::vector< std::size_t > &piv, std::vector< T > &b)
Solve LUx = Pb in place on b, using the factors from lu_factor.
std::complex< double > Complex
std::vector< std::size_t > lu_factor(Matrix< T > &A)
In-place LU of A (n x n).
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
Matrix< T > matpow(const Matrix< T > &A, unsigned k)
Integer matrix power, by repeated squaring.
Matrix< T > eye(std::size_t n)
Identity of order n.
G and R of a QBD whose local block is complex.
The piecewise QBD blocks, 1-based exactly as the reference's cell arrays.
std::vector< long > T
1-based: entry 0 is unused padding
std::vector< CMat > Lv
1-based: entry 0 is unused padding
const CMat & Lv1(std::size_t k) const
const CMat & B1(std::size_t k) const
long T1(std::size_t k) const
const CMat & F1(std::size_t k) const
const CMat & L1(std::size_t k) const