82 const std::size_t M = L.
empty() ? 0 : L.
rows();
83 const std::size_t R = N.size();
85 throw InputError(
"pfqn_conv: L and N disagree on the class count");
86 if (!cdscaling.empty() && cdscaling.size() != M)
87 throw InputError(
"pfqn_conv: the scaling vector has the wrong station count");
89 if (v < 0)
throw InputError(
"pfqn_conv: the convolution algorithm requires finite, "
90 "nonnegative (closed) populations");
95 std::vector<T> Zsum(R, zero);
97 if (Z.
cols() != R)
throw InputError(
"pfqn_conv: Z and N disagree on the class count");
98 for (std::size_t k = 0; k < Z.
rows(); ++k)
99 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
102 const std::vector<std::size_t> prods =
plane_sizes(N);
106 std::vector<T> G(total, zero);
108 std::vector<int> n(R, 0);
111 G[
pop_index(n, prods)] = detail::pff_delay(Zsum, n);
117 for (std::size_t ist = 0; ist < M; ++ist) {
118 const bool isCd = !cdscaling.empty() &&
static_cast<bool>(cdscaling[ist]);
122 std::vector<int> n(R, 0);
125 const std::size_t idx =
pop_index(n, prods);
127 for (std::size_t r = 0; r < R; ++r)
128 if (n[r] >= 1) acc += L(ist, r) * G[idx - prods[r]];
136 std::vector<T> Xm(total, zero);
139 std::vector<int> n(R, 0);
142 const std::size_t idx =
pop_index(n, prods);
144 while (r < R && n[r] == 0) ++r;
146 std::vector<T> row(R);
147 for (std::size_t s = 0; s < R; ++s)
149 const std::vector<T> bval = cdscaling[ist](row);
151 throw InputError(
"pfqn_conv: a class-dependence callable returned nothing");
152 const T beta = bval.size() > 1 ? bval.at(r) : bval[0];
155 for (
int v : n) tot += v;
157 Xm[idx] = fac * (L(ist, r) / beta) * Xm[idx - prods[r]];
169 std::vector<int> n(R, 0);
172 const std::size_t idx =
pop_index(n, prods);
173 std::vector<T> row(R);
176 const std::vector<T> bval = cdscaling[ist](row);
177 for (std::size_t r = 0; r < R; ++r) {
178 if (n[r] <= 0 || !(L(ist, r) > zero))
continue;
179 const T beta = bval.size() > 1 ? bval.at(r) : bval[0];
180 std::string pop =
"[";
181 for (std::size_t s = 0; s < R; ++s) pop += (s ?
", " :
"") + std::to_string(n[s]);
185 "The class/joint dependence at station " + std::to_string(ist + 1) +
" stops class " +
186 std::to_string(r + 1) +
" in the population " + pop +
" (zero rate): the network has no "
187 "product form, so the normalizing constant does not apply. Use SolverCTMC, SolverSSA or "
192 if (std::fabs(cand - ref) > 1e-8 * std::max(std::fabs(ref), std::numeric_limits<double>::min()))
194 "The class/joint dependence at station " + std::to_string(ist + 1) +
" does not satisfy "
195 "Kolmogorov's criterion (population " + pop +
"): the network has no product form, so the "
196 "normalizing constant does not apply. Use SolverCTMC, SolverSSA or SolverLDES.");
202 std::vector<T> Gold(G);
203 std::vector<int> n(R, 0);
206 const std::size_t idxn =
pop_index(n, prods);
209 std::vector<int> i(R, 0);
212 std::size_t idx_i = 0, idx_nmi = 0;
213 for (std::size_t r = 0; r < R; ++r) {
214 idx_i += prods[r] *
static_cast<std::size_t
>(i[r]);
215 idx_nmi += prods[r] *
static_cast<std::size_t
>(n[r] - i[r]);
217 acc += Xm[idx_i] * Gold[idx_nmi];
225 const T Gn = G[total - 1];
NcResult< T > pfqn_conv(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< CdScaling< T > > &cdscaling)
Multichain convolution algorithm with class-dependent service rates (Sauer 1983, "Computational Algor...
bool next_pop(std::vector< int > &n, const std::vector< int > &N)
Advance n to the next population vector in the lattice 0 <= n <= N, odometer order with the last clas...
std::size_t pop_index(const std::vector< int > &n, const std::vector< std::size_t > &prods)
Index of n in the lattice, 0-based (MATLAB hashpop is 1-based).