186 const std::vector<std::vector<double>>& R,
188 const std::vector<std::vector<double>>& Rt,
189 const std::vector<double>& Thr,
190 const std::vector<double>& pdfpoints,
191 const std::vector<double>& cdfpoints) {
192 using namespace multiregime_detail;
193 const std::size_t K = R.size();
194 if (K == 0)
throw InputError(
"mfq_multiregime: at least one regime is required");
195 if (Thr.size() != K)
throw InputError(
"mfq_multiregime: one threshold per regime is required");
196 const std::size_t N = R[0].size();
197 if (N == 0)
throw InputError(
"mfq_multiregime: the background chain is empty");
200 std::vector<Matrix<double>> Qk = Q;
202 for (std::size_t k = 1; k < K; ++k) Qk.push_back(Qk[0]);
203 if (Qk.size() != K)
throw InputError(
"mfq_multiregime: expected one generator per regime");
204 std::vector<Matrix<double>> Qtk = Qt;
206 Qtk.push_back(Qk[0]);
207 for (std::size_t k = 0; k < K; ++k) Qtk.push_back(Qk[k]);
208 }
else if (Qtk.size() == 1) {
209 for (std::size_t k = 1; k < K + 1; ++k) Qtk.push_back(Qtk[0]);
211 if (Qtk.size() != K + 1)
212 throw InputError(
"mfq_multiregime: expected K+1 boundary generators");
213 std::vector<std::vector<double>> Rtk = Rt;
216 for (std::size_t k = 0; k < K; ++k) Rtk.push_back(R[k]);
218 if (Rtk.size() != K + 1)
219 throw InputError(
"mfq_multiregime: expected K+1 boundary rate vectors");
220 for (std::size_t k = 0; k < K; ++k) {
221 if (R[k].size() != N || Qk[k].rows() != N || Qk[k].cols() != N)
222 throw InputError(
"mfq_multiregime: a regime's Q or R has the wrong order");
226 std::vector<double> Tv(K + 1, 0.0);
227 for (std::size_t k = 0; k < K; ++k) Tv[k + 1] = Thr[k];
230 std::vector<Regime>
reg(K);
231 std::vector<std::size_t> Nnz(K, 0), Npos(K + 1, 0);
232 for (std::size_t k = 0; k < K; ++k) {
233 std::vector<std::size_t> zix, nzix;
234 for (std::size_t i = 0; i < N; ++i) (R[k][i] == 0.0 ? zix : nzix).push_back(i);
235 const std::size_t Nn = nzix.size(), Nz = zix.size();
237 throw InputError(
"mfq_multiregime: a regime has no state with a non-zero drift");
244 for (std::size_t i = 0; i < Nz; ++i)
245 for (std::size_t j = 0; j < Nz; ++j) negQzz(i, j) = -negQzz(i, j);
251 "mfq_multiregime: the zero-drift states of a regime form a closed set, so the "
252 "fluid can be trapped at a constant level and the regime has no stationary "
255 Wz =
matmul(pick(Qk[k], nzix, zix), iQzz);
256 Qnk = mfq_detail::add(Qnk,
matmul(Wz, pick(Qk[k], zix, nzix)));
259 for (std::size_t i = 0; i < Nn; ++i)
260 for (std::size_t j = 0; j < Nn; ++j) A(i, j) = Qnk(i, j) / R[k][nzix[j]];
264 std::vector<double> key(Nn, 0.0);
265 std::size_t nzero = 0;
266 for (std::size_t i = 0; i < Nn; ++i) {
267 const double d = s0.
T(i, i);
268 key[i] = (std::fabs(d) < 1e-10 ? 5.0 : 0.0) + (d < 0.0 ? 2.0 : 0.0) +
269 (d > 0.0 ? 1.0 : 0.0);
270 if (std::fabs(d) < 1e-10) ++nzero;
274 for (std::size_t i = 0; i + 1 < Nn; ++i)
275 if (s0.
T(i + 1, i) != 0.0) key[i + 1] = key[i];
278 std::size_t nneg = 0, npos = 0;
279 for (std::size_t i = nzero; i < Nn; ++i) {
280 if (s.
T(i, i) < 0.0) ++nneg;
281 else if (s.
T(i, i) > 0.0) ++npos;
283 if (nzero + nneg + npos != Nn)
285 "mfq_multiregime: the spectrum of a regime could not be split into zero, negative "
286 "and positive parts");
289 const Matrix<double> D22 = blk(s.
T, nzero, nzero, Nn - nzero, Nn - nzero);
291 for (std::size_t i = 0; i < negD22.
rows(); ++i)
292 for (std::size_t j = 0; j < negD22.
cols(); ++j) negD22(i, j) = -negD22(i, j);
295 blk(s.
T, 0, nzero, nzero, Nn - nzero));
296 Matrix<double> negDpp = blk(s.
T, nzero + nneg, nzero + nneg, npos, npos);
297 for (std::size_t i = 0; i < npos; ++i)
298 for (std::size_t j = 0; j < npos; ++j) negDpp(i, j) = -negDpp(i, j);
299 const Matrix<double> X2 = sylvester(blk(s.
T, nzero, nzero, nneg, nneg), negDpp,
300 blk(s.
T, nzero, nzero + nneg, nneg, npos));
304 for (std::size_t i = 0; i < nzero; ++i)
305 for (std::size_t j = 0; j < Nn - nzero; ++j) B1(i, nzero + j) = -X1(i, j);
307 for (std::size_t i = 0; i < nneg; ++i)
308 for (std::size_t j = 0; j < npos; ++j) B2(nzero + i, nzero + nneg + j) = -X2(i, j);
317 rg.An = blk(At, nzero, nzero, nneg, nneg);
318 rg.Ap = blk(At, nzero + nneg, nzero + nneg, npos, npos);
331 for (std::size_t j = 0; j < Nn; ++j) {
332 for (std::size_t i = 0; i < nzero; ++i) rg.L0(i, nzix[j]) = iY0(i, j);
333 for (std::size_t i = 0; i < nneg; ++i) rg.Ln(i, nzix[j]) = iYn(i, j);
334 for (std::size_t i = 0; i < npos; ++i) rg.Lp(i, nzix[j]) = iYp(i, j);
336 for (std::size_t j = 0; j < Nz; ++j) {
337 for (std::size_t i = 0; i < nzero; ++i) rg.L0(i, zix[j]) = Z0(i, j);
338 for (std::size_t i = 0; i < nneg; ++i) rg.Ln(i, zix[j]) = Zn(i, j);
339 for (std::size_t i = 0; i < npos; ++i) rg.Lp(i, zix[j]) = Zp(i, j);
342 const double Tk = Tv[k + 1] - Tv[k];
344 for (std::size_t i = 0; i < npos; ++i)
345 for (std::size_t j = 0; j < npos; ++j) negApT(i, j) = -negApT(i, j);
355 for (std::size_t i = 0; i < nzero; ++i)
356 for (std::size_t j = 0; j < N; ++j) {
357 rg.M0(i, j) = rg.L0(i, j);
358 rg.MT(i, j) = rg.L0(i, j);
359 rg.Mi(i, j) = Tk * rg.L0(i, j);
361 for (std::size_t i = 0; i < nneg; ++i)
362 for (std::size_t j = 0; j < N; ++j) {
363 rg.M0(nzero + i, j) = rg.Ln(i, j);
364 rg.MT(nzero + i, j) = EAnLn(i, j);
366 for (std::size_t i = 0; i < npos; ++i)
367 for (std::size_t j = 0; j < N; ++j) {
368 rg.M0(nzero + nneg + i, j) = EApLp(i, j);
369 rg.MT(nzero + nneg + i, j) = rg.Lp(i, j);
374 for (std::size_t i = 0; i < nneg; ++i)
375 for (std::size_t j = 0; j < nneg; ++j) negAn(i, j) = -negAn(i, j);
377 for (std::size_t i = 0; i < nneg; ++i)
378 for (std::size_t j = 0; j < nneg; ++j) ImE(i, j) -= EAn(i, j);
380 for (std::size_t i = 0; i < nneg; ++i)
381 for (std::size_t j = 0; j < N; ++j) rg.Mi(nzero + i, j) = blkn(i, j);
385 for (std::size_t i = 0; i < npos; ++i)
386 for (std::size_t j = 0; j < npos; ++j) ImE(i, j) -= EAp(i, j);
388 for (std::size_t i = 0; i < npos; ++i)
389 for (std::size_t j = 0; j < N; ++j) rg.Mi(nzero + nneg + i, j) = blkp(i, j);
393 Npos[k + 1] = Npos[k] + Nn;
397 const std::size_t d = (K + 1) * N;
398 const std::size_t Neq = d + Npos[K];
403 for (std::size_t j = 0; j < N; ++j)
404 for (std::size_t i = 0; i < N; ++i) M(i, p + j) = -Qtk[0](i, j);
405 for (std::size_t j = 0; j < N; ++j)
406 for (std::size_t i = 0; i < Nnz[0]; ++i)
407 M(d + Npos[0] + i, p + j) =
reg[0].M0(i, j) * R[0][j];
410 for (std::size_t k = 0; k + 1 < K; ++k) {
411 for (std::size_t j = 0; j < N; ++j)
412 for (std::size_t i = 0; i < N; ++i) M((k + 1) * N + i, p + j) = -Qtk[k + 1](i, j);
413 for (std::size_t j = 0; j < N; ++j) {
414 for (std::size_t i = 0; i < Nnz[k + 1]; ++i)
415 M(d + Npos[k + 1] + i, p + j) =
reg[k + 1].M0(i, j) * R[k + 1][j];
416 for (std::size_t i = 0; i < Nnz[k]; ++i)
417 M(d + Npos[k] + i, p + j) = -
reg[k].MT(i, j) * R[k][j];
422 for (std::size_t j = 0; j < N; ++j)
423 for (std::size_t i = 0; i < N; ++i) M(K * N + i, p + j) = -Qtk[K](i, j);
424 for (std::size_t j = 0; j < N; ++j)
425 for (std::size_t i = 0; i < Nnz[K - 1]; ++i)
426 M(d + Npos[K - 1] + i, p + j) = -
reg[K - 1].MT(i, j) * R[K - 1][j];
430 for (std::size_t m = 0; m < N; ++m)
431 if (R[0][m] > 0.0) M(m, p++) = 1.0;
433 for (std::size_t m = 0; m < N; ++m)
434 if (R[K - 1][m] < 0.0) M(K * N + m, p++) = 1.0;
436 for (std::size_t k = 0; k + 1 < K; ++k)
437 for (std::size_t m = 0; m < N; ++m)
438 if ((R[k][m] > 0.0 && R[k + 1][m] > 0.0) || (R[k][m] < 0.0 && R[k + 1][m] < 0.0))
439 M((k + 1) * N + m, p++) = 1.0;
441 for (std::size_t k = 0; k + 1 < K; ++k)
442 for (std::size_t m = 0; m < N; ++m)
443 if (R[k][m] < 0.0 && R[k + 1][m] > 0.0 && Rtk[k + 1][m] != 0.0)
444 M((k + 1) * N + m, p++) = 1.0;
446 for (std::size_t k = 0; k + 1 < K; ++k)
447 for (std::size_t m = 0; m < N; ++m)
448 if (R[k][m] < 0.0 && Rtk[k + 1][m] >= 0.0) {
449 for (std::size_t i = 0; i < Nnz[k]; ++i)
450 M(d + Npos[k] + i, p) =
reg[k].MT(i, m);
454 for (std::size_t k = 0; k + 1 < K; ++k)
455 for (std::size_t m = 0; m < N; ++m)
456 if (R[k + 1][m] > 0.0 && Rtk[k + 1][m] <= 0.0) {
457 for (std::size_t i = 0; i < Nnz[k + 1]; ++i)
458 M(d + Npos[k + 1] + i, p) =
reg[k + 1].M0(i, m);
463 "mfq_multiregime: the boundary conditions do not close the system; check that the "
464 "feedback rates Rt satisfy the model's continuity requirements");
468 for (std::size_t i = 0; i < d; ++i) M(i, 0) = 1.0;
469 for (std::size_t k = 0; k < K; ++k)
470 for (std::size_t i = 0; i < Nnz[k]; ++i) {
472 for (std::size_t j = 0; j < N; ++j) s +=
reg[k].Mi(i, j);
473 M(d + Npos[k] + i, 0) = s;
478 for (std::size_t i = 0; i < Neq; ++i)
479 for (std::size_t j = 0; j < Neq; ++j) Mt(i, j) = M(j, i);
480 std::vector<double> rhs(Neq, 0.0);
482 const std::vector<double> sol =
solve(Mt, rhs);
485 std::vector<std::vector<double>> masses(K + 1, std::vector<double>(N, 0.0));
486 for (std::size_t k = 0; k <= K; ++k)
487 for (std::size_t j = 0; j < N; ++j) masses[k][j] = sol[k * N + j];
488 std::vector<std::vector<double>> a0(K), an(K), ap(K);
489 for (std::size_t k = 0; k < K; ++k) {
490 const std::size_t base = d + Npos[k];
491 a0[k].assign(sol.begin() +
static_cast<long>(base),
492 sol.begin() +
static_cast<long>(base +
reg[k].nzero));
493 an[k].assign(sol.begin() +
static_cast<long>(base +
reg[k].nzero),
494 sol.begin() +
static_cast<long>(base +
reg[k].nzero +
reg[k].nneg));
495 ap[k].assign(sol.begin() +
static_cast<long>(base +
reg[k].nzero +
reg[k].nneg),
496 sol.begin() +
static_cast<long>(base + Nnz[k]));
500 auto regime_of = [&](
double p_) -> std::size_t {
502 while (k < K && p_ >= Tv[k]) ++k;
507 for (
double pt : pdfpoints) {
508 if (pt < 0.0)
throw InputError(
"mfq_multiregime: the evaluation points must be non-negative");
509 const std::size_t k = regime_of(pt);
511 for (std::size_t i = 0; i <
reg[k].npos; ++i)
512 for (std::size_t j = 0; j <
reg[k].npos; ++j) negAp(i, j) = -negAp(i, j);
515 std::vector<double> f(N, 0.0), fd(N, 0.0);
517 const std::vector<double> v0 =
vecmul(a0[k],
reg[k].L0);
520 for (std::size_t j = 0; j < N; ++j) f[j] = v0[j] + vn[j] + vp[j];
521 const std::vector<double> dn =
523 const std::vector<double> dp =
525 for (std::size_t j = 0; j < N; ++j) fd[j] = dn[j] + dp[j];
527 out.
pdf.push_back(f);
528 out.
pdfd.push_back(fd);
531 for (
double c : cdfpoints) {
532 if (c < 0.0)
throw InputError(
"mfq_multiregime: the evaluation points must be non-negative");
533 std::vector<double> cres(N, 0.0), cresm(N, 0.0);
535 while (k < K && c >= Tv[k]) {
537 std::vector<double> coef;
538 coef.insert(coef.end(), a0[k - 1].begin(), a0[k - 1].end());
539 coef.insert(coef.end(), an[k - 1].begin(), an[k - 1].end());
540 coef.insert(coef.end(), ap[k - 1].begin(), ap[k - 1].end());
541 const std::vector<double> v =
vecmul(coef,
reg[k - 1].Mi);
542 for (std::size_t j = 0; j < N; ++j) {
547 for (std::size_t j = 0; j < N; ++j) cresm[j] += masses[k][j];
549 for (std::size_t j = 0; j < N; ++j) cres[j] += masses[k][j];
552 if (k == K && c == Tv[K])
553 for (std::size_t j = 0; j < N; ++j) cresm[j] += masses[K][j];
554 const std::size_t kk = k - 1;
555 const double crem = c - Tv[kk];
556 const double Tk = Tv[kk + 1] - Tv[kk];
558 for (std::size_t i = 0; i <
reg[kk].nneg; ++i)
559 for (std::size_t j = 0; j <
reg[kk].nneg; ++j) negAn(i, j) = -negAn(i, j);
560 for (std::size_t i = 0; i <
reg[kk].npos; ++i)
561 for (std::size_t j = 0; j <
reg[kk].npos; ++j) negAp(i, j) = -negAp(i, j);
562 std::vector<double> val(N, 0.0);
564 const std::vector<double> v0 =
vecmul(a0[kk],
reg[kk].L0);
565 for (std::size_t j = 0; j < N; ++j) val[j] += v0[j] * crem;
567 if (
reg[kk].nneg > 0) {
570 for (std::size_t i = 0; i <
reg[kk].nneg; ++i)
571 for (std::size_t j = 0; j <
reg[kk].nneg; ++j) ImE(i, j) -= E(i, j);
572 const std::vector<double> v =
574 for (std::size_t j = 0; j < N; ++j) val[j] += v[j];
576 if (
reg[kk].npos > 0) {
580 for (std::size_t i = 0; i <
reg[kk].npos; ++i)
581 for (std::size_t j = 0; j <
reg[kk].npos; ++j) D(i, j) -= Eb(i, j);
582 const std::vector<double> v =
584 for (std::size_t j = 0; j < N; ++j) val[j] += v[j];
586 for (std::size_t j = 0; j < N; ++j) {
590 out.
cdf.push_back(cres);
591 out.
cdfm.push_back(cresm);