160 const std::vector<T>& Thr,
161 const std::vector<FluidBoundary>& boundaryL,
162 const std::vector<FluidBoundary>& boundaryU,
163 const std::vector<
Matrix<T>>& Qt,
const T& prec) {
165 "mfq_ld_solve runs a tolerance-terminated QBD iteration and evaluates expm");
166 using namespace ld_solve_detail;
170 const std::size_t K = Thr.size();
171 if (K == 0)
throw InputError(
"mfq_ld_solve: at least one regime is required");
172 if (Q.size() != K || R.size() != K || S.size() != K)
173 throw InputError(
"mfq_ld_solve: Q, R and S must have one entry per regime");
174 const std::size_t N = Q[0].rows();
175 if (N == 0)
throw InputError(
"mfq_ld_solve: the background chain is empty");
177 std::vector<FluidBoundary> bL = boundaryL, bU = boundaryU;
179 if (bU.empty()) bU = bL;
180 if (bL.size() != N || bU.size() != N)
182 "mfq_ld_solve: the boundary flags are one per BACKGROUND STATE, a vector of length N");
183 std::vector<Matrix<T>> Qtk = Qt;
185 for (std::size_t k = 0; k < K; ++k) Qtk.push_back(Q[k]);
186 Qtk.push_back(Q[K - 1]);
187 }
else if (Qtk.size() == 1) {
188 while (Qtk.size() < K + 1) Qtk.push_back(Qtk[0]);
190 if (Qtk.size() != K + 1)
throw InputError(
"mfq_ld_solve: expected K+1 boundary generators");
192 std::vector<T> Tv(K + 1, zero);
193 for (std::size_t k = 0; k < K; ++k) Tv[k + 1] = Thr[k];
196 std::vector<Matrix<T>> Sh(K);
197 std::vector<Matrix<T>> KF(K), KB(K), cloF(K), cloB(K);
198 std::vector<std::size_t> Np(K, 0), Nn(K, 0), Ns(K, 0), NbF(K, 0), NbB(K, 0);
199 std::vector<std::vector<std::size_t>> vixp(K), vixn(K), vix0(K), vixs(K);
200 for (std::size_t k = 0; k < K; ++k) {
201 if (Q[k].rows() != N || R[k].rows() != N || S[k].rows() != N)
202 throw InputError(
"mfq_ld_solve: a regime has the wrong order");
204 for (std::size_t i = 0; i < N; ++i)
205 for (std::size_t j = 0; j < N; ++j) Sh[k](i, j) = S[k](i, j) / two;
207 std::vector<std::size_t> ix0, ixn0;
208 for (std::size_t i = 0; i < N; ++i) {
209 if (
num_abs(R[k](i, i)) <= prec && Sh[k](i, i) <= prec) ix0.push_back(i);
210 else ixn0.push_back(i);
212 const std::size_t Nzero = ix0.size(), Nnz = ixn0.size();
214 throw InputError(
"mfq_ld_solve: a regime has neither drift nor variance in any state");
220 for (std::size_t i = 0; i < Nzero; ++i)
221 for (std::size_t j = 0; j < Nzero; ++j) negQ00(i, j) = -negQ00(i, j);
223 Qv = mfq_detail::add(Qv,
matmul(Wz, pick(Q[k], ix0, ixn0)));
225 const Matrix<T> Rv = pick(R[k], ixn0, ixn0);
226 const Matrix<T> Sv = pick(Sh[k], ixn0, ixn0);
228 std::vector<std::size_t> ixp, ixn, ixs;
229 for (std::size_t i = 0; i < Nnz; ++i) {
230 if (Sv(i, i) > prec) ixs.push_back(i);
231 else if (Rv(i, i) > prec) ixp.push_back(i);
232 else if (Rv(i, i) < -prec) ixn.push_back(i);
240 std::vector<T> discr(ixs.size(), zero);
241 for (std::size_t i = 0; i < ixs.size(); ++i) {
242 const std::size_t q = ixs[i];
243 discr[i] = Rv(q, q) * Rv(q, q) -
244 two * (two * Sv(q, q)) * Qv(q, q);
250 for (std::size_t q : ixp) {
251 const T v = -Qv(q, q) / Rv(q, q);
254 for (std::size_t i = 0; i < ixs.size(); ++i)
255 if (discr[i] > zero) {
256 const std::size_t q = ixs[i];
257 const T v = (-Rv(q, q) + sqrt(discr[i])) / (two * Sv(q, q));
260 std::vector<std::size_t> ixbF = ixs;
261 ixbF.insert(ixbF.end(), ixp.begin(), ixp.end());
262 NbF[k] = ixbF.size();
263 const std::size_t nb = NbF[k], nn = Nn[k];
264 Matrix<T> Bm(nb + nn, nb + nn, zero), Lm(nb + nn, nb + nn, zero),
265 Fm(nb + nn, nb + nn, zero);
266 const Matrix<T> Sbb = pick(Sv, ixbF, ixbF), Rbb = pick(Rv, ixbF, ixbF);
267 const Matrix<T> Qbb = pick(Qv, ixbF, ixbF), Qbn = pick(Qv, ixbF, ixn);
268 const Matrix<T> Qnb = pick(Qv, ixn, ixbF), Qnn = pick(Qv, ixn, ixn);
269 const Matrix<T> Rnn = pick(Rv, ixn, ixn);
270 for (std::size_t i = 0; i < nb; ++i)
271 for (std::size_t j = 0; j < nb; ++j) {
272 Bm(i, j) = c * Sbb(i, j);
273 Lm(i, j) = -Rbb(i, j) - two * c * Sbb(i, j);
274 Fm(i, j) = Qbb(i, j) / c + c * Sbb(i, j) + Rbb(i, j);
276 for (std::size_t i = 0; i < nb; ++i)
277 for (std::size_t j = 0; j < nn; ++j) Fm(i, nb + j) = Qbn(i, j) / c;
278 for (std::size_t i = 0; i < nn; ++i) {
279 for (std::size_t j = 0; j < nb; ++j) Lm(nb + i, j) = Qnb(i, j) / c;
280 for (std::size_t j = 0; j < nn; ++j) {
281 Bm(nb + i, nb + j) = -Rnn(i, j);
282 Lm(nb + i, nb + j) = Qnn(i, j) / c + Rnn(i, j);
285 const Matrix<T> QR = qbd_cr_R(Bm, Lm, Fm, prec);
287 for (std::size_t i = 0; i < nb; ++i)
288 for (std::size_t j = 0; j < nb; ++j)
289 KF[k](i, j) = (QR(i, j) - (i == j ? one : zero)) * c;
293 for (std::size_t i = 0; i < nb; ++i) clov(i, ixbF[i]) = one;
294 for (std::size_t i = 0; i < nb; ++i)
295 for (std::size_t j = 0; j < nn; ++j) clov(i, ixn[j]) = QR(i, nb + j);
297 for (std::size_t i = 0; i < nb; ++i)
298 for (std::size_t j = 0; j < Nnz; ++j) cloF[k](i, ixn0[j]) = clov(i, j);
301 for (std::size_t i = 0; i < nb; ++i)
302 for (std::size_t j = 0; j < Nzero; ++j) cloF[k](i, ix0[j]) = z(i, j);
309 for (std::size_t q : ixn) {
310 const T v = -Qv(q, q) / (-Rv(q, q));
313 for (std::size_t i = 0; i < ixs.size(); ++i)
314 if (discr[i] > zero) {
315 const std::size_t q = ixs[i];
316 const T v = (Rv(q, q) + sqrt(discr[i])) / (two * Sv(q, q));
319 std::vector<std::size_t> ixbB = ixs;
320 ixbB.insert(ixbB.end(), ixn.begin(), ixn.end());
321 NbB[k] = ixbB.size();
322 const std::size_t nb = NbB[k], np = Np[k];
323 Matrix<T> Bm(nb + np, nb + np, zero), Lm(nb + np, nb + np, zero),
324 Fm(nb + np, nb + np, zero);
325 const Matrix<T> Sbb = pick(Sv, ixbB, ixbB), Rbb = pick(Rv, ixbB, ixbB);
326 const Matrix<T> Qbb = pick(Qv, ixbB, ixbB), Qbp = pick(Qv, ixbB, ixp);
327 const Matrix<T> Qpb = pick(Qv, ixp, ixbB), Qpp = pick(Qv, ixp, ixp);
328 const Matrix<T> Rpp = pick(Rv, ixp, ixp);
329 for (std::size_t i = 0; i < nb; ++i)
330 for (std::size_t j = 0; j < nb; ++j) {
331 Bm(i, j) = c * Sbb(i, j);
332 Lm(i, j) = Rbb(i, j) - two * c * Sbb(i, j);
333 Fm(i, j) = Qbb(i, j) / c + c * Sbb(i, j) - Rbb(i, j);
335 for (std::size_t i = 0; i < nb; ++i)
336 for (std::size_t j = 0; j < np; ++j) Fm(i, nb + j) = Qbp(i, j) / c;
337 for (std::size_t i = 0; i < np; ++i) {
338 for (std::size_t j = 0; j < nb; ++j) Lm(nb + i, j) = Qpb(i, j) / c;
339 for (std::size_t j = 0; j < np; ++j) {
340 Bm(nb + i, nb + j) = Rpp(i, j);
341 Lm(nb + i, nb + j) = Qpp(i, j) / c - Rpp(i, j);
344 const Matrix<T> QR = qbd_cr_R(Bm, Lm, Fm, prec);
346 for (std::size_t i = 0; i < nb; ++i)
347 for (std::size_t j = 0; j < nb; ++j)
348 KB[k](i, j) = (QR(i, j) - (i == j ? one : zero)) * c;
350 for (std::size_t i = 0; i < nb; ++i) clov(i, ixbB[i]) = one;
351 for (std::size_t i = 0; i < nb; ++i)
352 for (std::size_t j = 0; j < np; ++j) clov(i, ixp[j]) = QR(i, nb + j);
354 for (std::size_t i = 0; i < nb; ++i)
355 for (std::size_t j = 0; j < Nnz; ++j) cloB[k](i, ixn0[j]) = clov(i, j);
358 for (std::size_t i = 0; i < nb; ++i)
359 for (std::size_t j = 0; j < Nzero; ++j) cloB[k](i, ix0[j]) = z(i, j);
364 for (std::size_t q : ixp) vixp[k].push_back(ixn0[q]);
365 for (std::size_t q : ixn) vixn[k].push_back(ixn0[q]);
366 for (std::size_t q : ixs) vixs[k].push_back(ixn0[q]);
372 std::vector<std::size_t> pp;
375 for (std::size_t k = 0; k < K; ++k) {
376 pp.push_back(pp.back() + NbF[k]);
377 pp.push_back(pp.back() + NbB[k]);
378 pp.push_back(pp.back() + N);
381 const std::size_t Neq = pp.back();
384 auto rowF = [&](std::size_t k) {
return pp[1 + 3 * k]; };
385 auto rowB = [&](std::size_t k) {
return pp[2 + 3 * k]; };
386 auto rowM = [&](std::size_t k) {
return k == 0 ? std::size_t(0) : pp[3 * k]; };
389 for (std::size_t k = 0; k <= K; ++k) {
390 const std::size_t col = k * N;
391 for (std::size_t i = 0; i < N; ++i)
392 for (std::size_t j = 0; j < N; ++j) M(rowM(k) + i, col + j) = -Qtk[k](i, j);
394 const std::size_t g = k - 1;
400 for (std::size_t i = 0; i < NbF[g]; ++i)
401 for (std::size_t j = 0; j < N; ++j) M(rowF(g) + i, col + j) = aa(i, j);
403 mfq_detail::scale(
matmul(cloB[g], R[g]), T(-one)),
405 for (std::size_t i = 0; i < NbB[g]; ++i)
406 for (std::size_t j = 0; j < N; ++j) M(rowB(g) + i, col + j) = bb(i, j);
411 for (std::size_t i = 0; i < NbF[k]; ++i)
412 for (std::size_t j = 0; j < N; ++j) M(rowF(k) + i, col + j) = cc(i, j);
416 for (std::size_t i = 0; i < NbB[k]; ++i)
417 for (std::size_t j = 0; j < N; ++j) M(rowB(k) + i, col + j) = dd(i, j);
422 std::size_t col = (K + 1) * N;
423 for (std::size_t k = 0; k <= K; ++k) {
426 std::vector<std::size_t> ixr0;
427 for (std::size_t q : vixs[0])
429 std::vector<std::size_t> sel = vixp[0];
430 sel.insert(sel.end(), ixr0.begin(), ixr0.end());
431 for (std::size_t i = 0; i < sel.size(); ++i) M(rowM(0) + sel[i], col + i) = one;
434 std::vector<std::size_t> ixa0;
435 for (std::size_t q : vixs[0])
438 for (std::size_t i = 0; i < ixa0.size(); ++i) {
439 for (std::size_t r = 0; r < NbF[0]; ++r)
440 M(rowF(0) + r, col + i) = cloF[0](r, ixa0[i]);
441 for (std::size_t r = 0; r < NbB[0]; ++r)
442 M(rowB(0) + r, col + i) = pdfB(r, ixa0[i]);
446 std::vector<std::size_t> ixrB;
447 for (std::size_t q : vixs[K - 1])
449 std::vector<std::size_t> sel = vixn[K - 1];
450 sel.insert(sel.end(), ixrB.begin(), ixrB.end());
451 for (std::size_t i = 0; i < sel.size(); ++i) M(rowM(K) + sel[i], col + i) = one;
453 std::vector<std::size_t> ixaB;
454 for (std::size_t q : vixs[K - 1])
457 matmul(
expm(KF[K - 1], T(Tv[K] - Tv[K - 1])), cloF[K - 1]);
458 for (std::size_t i = 0; i < ixaB.size(); ++i) {
459 for (std::size_t r = 0; r < NbF[K - 1]; ++r)
460 M(rowF(K - 1) + r, col + i) = pdfF(r, ixaB[i]);
461 for (std::size_t r = 0; r < NbB[K - 1]; ++r)
462 M(rowB(K - 1) + r, col + i) = cloB[K - 1](r, ixaB[i]);
466 const std::size_t g = k - 1;
468 std::vector<std::size_t> st0;
469 for (std::size_t q = 0; q < N; ++q) {
470 const bool keep = (has(vixp[g], q) && has(vixn[k], q)) || has(vix0[g], q) ||
472 if (!keep) st0.push_back(q);
474 for (std::size_t i = 0; i < st0.size(); ++i) M(rowM(k) + st0[i], col + i) = one;
477 std::vector<std::size_t> sts;
478 for (std::size_t q = 0; q < N; ++q) {
479 const bool isS = has(vixs[g], q) || has(vixs[k], q);
480 const bool excl = has(vixn[k], q) || has(vixp[g], q);
481 if (isS && !excl) sts.push_back(q);
483 Matrix<T> sqSg(N, N, zero), sqSk(N, N, zero);
484 for (std::size_t q = 0; q < N; ++q) {
485 sqSg(q, q) = sqrt(Sh[g](q, q));
486 sqSk(q, q) = sqrt(Sh[k](q, q));
490 mfq_detail::scale(cloF[g], T(-one))),
493 matmul(mfq_detail::scale(cloB[g], T(-one)), sqSg);
497 for (std::size_t i = 0; i < sts.size(); ++i) {
498 for (std::size_t r = 0; r < NbF[g]; ++r)
499 M(rowF(g) + r, col + i) = BelowF(r, sts[i]);
500 for (std::size_t r = 0; r < NbB[g]; ++r)
501 M(rowB(g) + r, col + i) = BelowB(r, sts[i]);
502 for (std::size_t r = 0; r < NbF[k]; ++r)
503 M(rowF(k) + r, col + i) = AboveF(r, sts[i]);
504 for (std::size_t r = 0; r < NbB[k]; ++r)
505 M(rowB(k) + r, col + i) = AboveB(r, sts[i]);
512 "mfq_ld_solve: the boundary conditions do not close the system; check the drift and "
513 "variance pattern across the thresholds");
518 std::vector<T> h(Neq, zero);
519 for (std::size_t i = 0; i < N; ++i) h[i] = one;
520 for (std::size_t k = 0; k < K; ++k) {
522 mfq_ld_detail::integ_exp_pair(KF[k], KB[k], T(Tv[k + 1] - Tv[k]), sF, sB);
525 for (std::size_t i = 0; i < NbF[k]; ++i) {
527 for (std::size_t j = 0; j < N; ++j) s += gF(i, j);
530 for (std::size_t i = 0; i < NbB[k]; ++i) {
532 for (std::size_t j = 0; j < N; ++j) s += gB(i, j);
535 for (std::size_t i = 0; i < N; ++i) h[rowM(k + 1) + i] = one;
537 for (std::size_t i = 0; i < Neq; ++i) M(i, 0) = h[i];
542 for (std::size_t i = 0; i < Neq; ++i)
543 for (std::size_t j = 0; j < Neq; ++j) Mt(i, j) = M(j, i);
544 std::vector<T> rhs(Neq, zero);
546 const std::vector<T> b =
solve(Mt, rhs);
550 out.
masses.assign(K + 1, std::vector<T>(N, zero));
551 for (std::size_t k = 0; k <= K; ++k)
552 for (std::size_t j = 0; j < N; ++j) out.
masses[k][j] = b[rowM(k) + j];
553 for (std::size_t k = 0; k < K; ++k) {
554 out.
iniF.push_back(std::vector<T>(b.begin() +
static_cast<long>(rowF(k)),
555 b.begin() +
static_cast<long>(rowF(k) + NbF[k])));
556 out.
iniB.push_back(std::vector<T>(b.begin() +
static_cast<long>(rowB(k)),
557 b.begin() +
static_cast<long>(rowB(k) + NbB[k])));
558 out.
KF.push_back(KF[k]);
559 out.
KB.push_back(KB[k]);
560 out.
cloF.push_back(cloF[k]);
561 out.
cloB.push_back(cloB[k]);