142 const std::vector<T>& Thr,
143 const std::vector<FluidBoundary>& boundaryL,
144 const std::vector<FluidBoundary>& boundaryU,
145 const std::vector<
Matrix<T>>& Qt,
const T& prec) {
147 "mfq_ld_solve runs a tolerance-terminated QBD iteration and evaluates expm");
148 using namespace ld_solve_detail;
152 const std::size_t K = Thr.size();
153 if (K == 0)
throw InputError(
"mfq_ld_solve: at least one regime is required");
154 if (Q.size() != K || R.size() != K || S.size() != K)
155 throw InputError(
"mfq_ld_solve: Q, R and S must have one entry per regime");
156 const std::size_t N = Q[0].rows();
157 if (N == 0)
throw InputError(
"mfq_ld_solve: the background chain is empty");
159 std::vector<FluidBoundary> bL = boundaryL, bU = boundaryU;
161 if (bU.empty()) bU = bL;
162 if (bL.size() != N || bU.size() != N)
164 "mfq_ld_solve: the boundary flags are one per BACKGROUND STATE, a vector of length N");
165 std::vector<Matrix<T>> Qtk = Qt;
167 for (std::size_t k = 0; k < K; ++k) Qtk.push_back(Q[k]);
168 Qtk.push_back(Q[K - 1]);
169 }
else if (Qtk.size() == 1) {
170 while (Qtk.size() < K + 1) Qtk.push_back(Qtk[0]);
172 if (Qtk.size() != K + 1)
throw InputError(
"mfq_ld_solve: expected K+1 boundary generators");
174 std::vector<T> Tv(K + 1, zero);
175 for (std::size_t k = 0; k < K; ++k) Tv[k + 1] = Thr[k];
178 std::vector<Matrix<T>> Sh(K);
179 std::vector<Matrix<T>> KF(K), KB(K), cloF(K), cloB(K);
180 std::vector<std::size_t> Np(K, 0), Nn(K, 0), Ns(K, 0), NbF(K, 0), NbB(K, 0);
181 std::vector<std::vector<std::size_t>> vixp(K), vixn(K), vix0(K), vixs(K);
182 for (std::size_t k = 0; k < K; ++k) {
183 if (Q[k].rows() != N || R[k].rows() != N || S[k].rows() != N)
184 throw InputError(
"mfq_ld_solve: a regime has the wrong order");
186 for (std::size_t i = 0; i < N; ++i)
187 for (std::size_t j = 0; j < N; ++j) Sh[k](i, j) = S[k](i, j) / two;
189 std::vector<std::size_t> ix0, ixn0;
190 for (std::size_t i = 0; i < N; ++i) {
191 if (
num_abs(R[k](i, i)) <= prec && Sh[k](i, i) <= prec) ix0.push_back(i);
192 else ixn0.push_back(i);
194 const std::size_t Nzero = ix0.size(), Nnz = ixn0.size();
196 throw InputError(
"mfq_ld_solve: a regime has neither drift nor variance in any state");
202 for (std::size_t i = 0; i < Nzero; ++i)
203 for (std::size_t j = 0; j < Nzero; ++j) negQ00(i, j) = -negQ00(i, j);
205 Qv = mfq_detail::add(Qv,
matmul(Wz, pick(Q[k], ix0, ixn0)));
207 const Matrix<T> Rv = pick(R[k], ixn0, ixn0);
208 const Matrix<T> Sv = pick(Sh[k], ixn0, ixn0);
210 std::vector<std::size_t> ixp, ixn, ixs;
211 for (std::size_t i = 0; i < Nnz; ++i) {
212 if (Sv(i, i) > prec) ixs.push_back(i);
213 else if (Rv(i, i) > prec) ixp.push_back(i);
214 else if (Rv(i, i) < -prec) ixn.push_back(i);
222 std::vector<T> discr(ixs.size(), zero);
223 for (std::size_t i = 0; i < ixs.size(); ++i) {
224 const std::size_t q = ixs[i];
225 discr[i] = Rv(q, q) * Rv(q, q) -
226 two * (two * Sv(q, q)) * Qv(q, q);
232 for (std::size_t q : ixp) {
233 const T v = -Qv(q, q) / Rv(q, q);
236 for (std::size_t i = 0; i < ixs.size(); ++i)
237 if (discr[i] > zero) {
238 const std::size_t q = ixs[i];
239 const T v = (-Rv(q, q) + sqrt(discr[i])) / (two * Sv(q, q));
242 std::vector<std::size_t> ixbF = ixs;
243 ixbF.insert(ixbF.end(), ixp.begin(), ixp.end());
244 NbF[k] = ixbF.size();
245 const std::size_t nb = NbF[k], nn = Nn[k];
246 Matrix<T> Bm(nb + nn, nb + nn, zero), Lm(nb + nn, nb + nn, zero),
247 Fm(nb + nn, nb + nn, zero);
248 const Matrix<T> Sbb = pick(Sv, ixbF, ixbF), Rbb = pick(Rv, ixbF, ixbF);
249 const Matrix<T> Qbb = pick(Qv, ixbF, ixbF), Qbn = pick(Qv, ixbF, ixn);
250 const Matrix<T> Qnb = pick(Qv, ixn, ixbF), Qnn = pick(Qv, ixn, ixn);
251 const Matrix<T> Rnn = pick(Rv, ixn, ixn);
252 for (std::size_t i = 0; i < nb; ++i)
253 for (std::size_t j = 0; j < nb; ++j) {
254 Bm(i, j) = c * Sbb(i, j);
255 Lm(i, j) = -Rbb(i, j) - two * c * Sbb(i, j);
256 Fm(i, j) = Qbb(i, j) / c + c * Sbb(i, j) + Rbb(i, j);
258 for (std::size_t i = 0; i < nb; ++i)
259 for (std::size_t j = 0; j < nn; ++j) Fm(i, nb + j) = Qbn(i, j) / c;
260 for (std::size_t i = 0; i < nn; ++i) {
261 for (std::size_t j = 0; j < nb; ++j) Lm(nb + i, j) = Qnb(i, j) / c;
262 for (std::size_t j = 0; j < nn; ++j) {
263 Bm(nb + i, nb + j) = -Rnn(i, j);
264 Lm(nb + i, nb + j) = Qnn(i, j) / c + Rnn(i, j);
267 const Matrix<T> QR = qbd_cr_R(Bm, Lm, Fm, prec);
269 for (std::size_t i = 0; i < nb; ++i)
270 for (std::size_t j = 0; j < nb; ++j)
271 KF[k](i, j) = (QR(i, j) - (i == j ? one : zero)) * c;
275 for (std::size_t i = 0; i < nb; ++i) clov(i, ixbF[i]) = one;
276 for (std::size_t i = 0; i < nb; ++i)
277 for (std::size_t j = 0; j < nn; ++j) clov(i, ixn[j]) = QR(i, nb + j);
279 for (std::size_t i = 0; i < nb; ++i)
280 for (std::size_t j = 0; j < Nnz; ++j) cloF[k](i, ixn0[j]) = clov(i, j);
283 for (std::size_t i = 0; i < nb; ++i)
284 for (std::size_t j = 0; j < Nzero; ++j) cloF[k](i, ix0[j]) = z(i, j);
291 for (std::size_t q : ixn) {
292 const T v = -Qv(q, q) / (-Rv(q, q));
295 for (std::size_t i = 0; i < ixs.size(); ++i)
296 if (discr[i] > zero) {
297 const std::size_t q = ixs[i];
298 const T v = (Rv(q, q) + sqrt(discr[i])) / (two * Sv(q, q));
301 std::vector<std::size_t> ixbB = ixs;
302 ixbB.insert(ixbB.end(), ixn.begin(), ixn.end());
303 NbB[k] = ixbB.size();
304 const std::size_t nb = NbB[k], np = Np[k];
305 Matrix<T> Bm(nb + np, nb + np, zero), Lm(nb + np, nb + np, zero),
306 Fm(nb + np, nb + np, zero);
307 const Matrix<T> Sbb = pick(Sv, ixbB, ixbB), Rbb = pick(Rv, ixbB, ixbB);
308 const Matrix<T> Qbb = pick(Qv, ixbB, ixbB), Qbp = pick(Qv, ixbB, ixp);
309 const Matrix<T> Qpb = pick(Qv, ixp, ixbB), Qpp = pick(Qv, ixp, ixp);
310 const Matrix<T> Rpp = pick(Rv, ixp, ixp);
311 for (std::size_t i = 0; i < nb; ++i)
312 for (std::size_t j = 0; j < nb; ++j) {
313 Bm(i, j) = c * Sbb(i, j);
314 Lm(i, j) = Rbb(i, j) - two * c * Sbb(i, j);
315 Fm(i, j) = Qbb(i, j) / c + c * Sbb(i, j) - Rbb(i, j);
317 for (std::size_t i = 0; i < nb; ++i)
318 for (std::size_t j = 0; j < np; ++j) Fm(i, nb + j) = Qbp(i, j) / c;
319 for (std::size_t i = 0; i < np; ++i) {
320 for (std::size_t j = 0; j < nb; ++j) Lm(nb + i, j) = Qpb(i, j) / c;
321 for (std::size_t j = 0; j < np; ++j) {
322 Bm(nb + i, nb + j) = Rpp(i, j);
323 Lm(nb + i, nb + j) = Qpp(i, j) / c - Rpp(i, j);
326 const Matrix<T> QR = qbd_cr_R(Bm, Lm, Fm, prec);
328 for (std::size_t i = 0; i < nb; ++i)
329 for (std::size_t j = 0; j < nb; ++j)
330 KB[k](i, j) = (QR(i, j) - (i == j ? one : zero)) * c;
332 for (std::size_t i = 0; i < nb; ++i) clov(i, ixbB[i]) = one;
333 for (std::size_t i = 0; i < nb; ++i)
334 for (std::size_t j = 0; j < np; ++j) clov(i, ixp[j]) = QR(i, nb + j);
336 for (std::size_t i = 0; i < nb; ++i)
337 for (std::size_t j = 0; j < Nnz; ++j) cloB[k](i, ixn0[j]) = clov(i, j);
340 for (std::size_t i = 0; i < nb; ++i)
341 for (std::size_t j = 0; j < Nzero; ++j) cloB[k](i, ix0[j]) = z(i, j);
346 for (std::size_t q : ixp) vixp[k].push_back(ixn0[q]);
347 for (std::size_t q : ixn) vixn[k].push_back(ixn0[q]);
348 for (std::size_t q : ixs) vixs[k].push_back(ixn0[q]);
354 std::vector<std::size_t> pp;
357 for (std::size_t k = 0; k < K; ++k) {
358 pp.push_back(pp.back() + NbF[k]);
359 pp.push_back(pp.back() + NbB[k]);
360 pp.push_back(pp.back() + N);
363 const std::size_t Neq = pp.back();
366 auto rowF = [&](std::size_t k) {
return pp[1 + 3 * k]; };
367 auto rowB = [&](std::size_t k) {
return pp[2 + 3 * k]; };
368 auto rowM = [&](std::size_t k) {
return k == 0 ? std::size_t(0) : pp[3 * k]; };
371 for (std::size_t k = 0; k <= K; ++k) {
372 const std::size_t col = k * N;
373 for (std::size_t i = 0; i < N; ++i)
374 for (std::size_t j = 0; j < N; ++j) M(rowM(k) + i, col + j) = -Qtk[k](i, j);
376 const std::size_t g = k - 1;
382 for (std::size_t i = 0; i < NbF[g]; ++i)
383 for (std::size_t j = 0; j < N; ++j) M(rowF(g) + i, col + j) = aa(i, j);
385 mfq_detail::scale(
matmul(cloB[g], R[g]), T(-one)),
387 for (std::size_t i = 0; i < NbB[g]; ++i)
388 for (std::size_t j = 0; j < N; ++j) M(rowB(g) + i, col + j) = bb(i, j);
393 for (std::size_t i = 0; i < NbF[k]; ++i)
394 for (std::size_t j = 0; j < N; ++j) M(rowF(k) + i, col + j) = cc(i, j);
398 for (std::size_t i = 0; i < NbB[k]; ++i)
399 for (std::size_t j = 0; j < N; ++j) M(rowB(k) + i, col + j) = dd(i, j);
404 std::size_t col = (K + 1) * N;
405 for (std::size_t k = 0; k <= K; ++k) {
408 std::vector<std::size_t> ixr0;
409 for (std::size_t q : vixs[0])
411 std::vector<std::size_t> sel = vixp[0];
412 sel.insert(sel.end(), ixr0.begin(), ixr0.end());
413 for (std::size_t i = 0; i < sel.size(); ++i) M(rowM(0) + sel[i], col + i) = one;
416 std::vector<std::size_t> ixa0;
417 for (std::size_t q : vixs[0])
420 for (std::size_t i = 0; i < ixa0.size(); ++i) {
421 for (std::size_t r = 0; r < NbF[0]; ++r)
422 M(rowF(0) + r, col + i) = cloF[0](r, ixa0[i]);
423 for (std::size_t r = 0; r < NbB[0]; ++r)
424 M(rowB(0) + r, col + i) = pdfB(r, ixa0[i]);
428 std::vector<std::size_t> ixrB;
429 for (std::size_t q : vixs[K - 1])
431 std::vector<std::size_t> sel = vixn[K - 1];
432 sel.insert(sel.end(), ixrB.begin(), ixrB.end());
433 for (std::size_t i = 0; i < sel.size(); ++i) M(rowM(K) + sel[i], col + i) = one;
435 std::vector<std::size_t> ixaB;
436 for (std::size_t q : vixs[K - 1])
439 matmul(
expm(KF[K - 1], T(Tv[K] - Tv[K - 1])), cloF[K - 1]);
440 for (std::size_t i = 0; i < ixaB.size(); ++i) {
441 for (std::size_t r = 0; r < NbF[K - 1]; ++r)
442 M(rowF(K - 1) + r, col + i) = pdfF(r, ixaB[i]);
443 for (std::size_t r = 0; r < NbB[K - 1]; ++r)
444 M(rowB(K - 1) + r, col + i) = cloB[K - 1](r, ixaB[i]);
448 const std::size_t g = k - 1;
450 std::vector<std::size_t> st0;
451 for (std::size_t q = 0; q < N; ++q) {
452 const bool keep = (has(vixp[g], q) && has(vixn[k], q)) || has(vix0[g], q) ||
454 if (!keep) st0.push_back(q);
456 for (std::size_t i = 0; i < st0.size(); ++i) M(rowM(k) + st0[i], col + i) = one;
459 std::vector<std::size_t> sts;
460 for (std::size_t q = 0; q < N; ++q) {
461 const bool isS = has(vixs[g], q) || has(vixs[k], q);
462 const bool excl = has(vixn[k], q) || has(vixp[g], q);
463 if (isS && !excl) sts.push_back(q);
465 Matrix<T> sqSg(N, N, zero), sqSk(N, N, zero);
466 for (std::size_t q = 0; q < N; ++q) {
467 sqSg(q, q) = sqrt(Sh[g](q, q));
468 sqSk(q, q) = sqrt(Sh[k](q, q));
472 mfq_detail::scale(cloF[g], T(-one))),
475 matmul(mfq_detail::scale(cloB[g], T(-one)), sqSg);
479 for (std::size_t i = 0; i < sts.size(); ++i) {
480 for (std::size_t r = 0; r < NbF[g]; ++r)
481 M(rowF(g) + r, col + i) = BelowF(r, sts[i]);
482 for (std::size_t r = 0; r < NbB[g]; ++r)
483 M(rowB(g) + r, col + i) = BelowB(r, sts[i]);
484 for (std::size_t r = 0; r < NbF[k]; ++r)
485 M(rowF(k) + r, col + i) = AboveF(r, sts[i]);
486 for (std::size_t r = 0; r < NbB[k]; ++r)
487 M(rowB(k) + r, col + i) = AboveB(r, sts[i]);
494 "mfq_ld_solve: the boundary conditions do not close the system; check the drift and "
495 "variance pattern across the thresholds");
500 std::vector<T> h(Neq, zero);
501 for (std::size_t i = 0; i < N; ++i) h[i] = one;
502 for (std::size_t k = 0; k < K; ++k) {
504 mfq_ld_detail::integ_exp_pair(KF[k], KB[k], T(Tv[k + 1] - Tv[k]), sF, sB);
507 for (std::size_t i = 0; i < NbF[k]; ++i) {
509 for (std::size_t j = 0; j < N; ++j) s += gF(i, j);
512 for (std::size_t i = 0; i < NbB[k]; ++i) {
514 for (std::size_t j = 0; j < N; ++j) s += gB(i, j);
517 for (std::size_t i = 0; i < N; ++i) h[rowM(k + 1) + i] = one;
519 for (std::size_t i = 0; i < Neq; ++i) M(i, 0) = h[i];
524 for (std::size_t i = 0; i < Neq; ++i)
525 for (std::size_t j = 0; j < Neq; ++j) Mt(i, j) = M(j, i);
526 std::vector<T> rhs(Neq, zero);
528 const std::vector<T> b =
solve(Mt, rhs);
532 out.
masses.assign(K + 1, std::vector<T>(N, zero));
533 for (std::size_t k = 0; k <= K; ++k)
534 for (std::size_t j = 0; j < N; ++j) out.
masses[k][j] = b[rowM(k) + j];
535 for (std::size_t k = 0; k < K; ++k) {
536 out.
iniF.push_back(std::vector<T>(b.begin() +
static_cast<long>(rowF(k)),
537 b.begin() +
static_cast<long>(rowF(k) + NbF[k])));
538 out.
iniB.push_back(std::vector<T>(b.begin() +
static_cast<long>(rowB(k)),
539 b.begin() +
static_cast<long>(rowB(k) + NbB[k])));
540 out.
KF.push_back(KF[k]);
541 out.
KB.push_back(KB[k]);
542 out.
cloF.push_back(cloF[k]);
543 out.
cloB.push_back(cloB[k]);