137 const std::vector<std::vector<std::size_t>>& paths,
138 const std::vector<double>& tset,
139 const std::string& method =
"auto", std::size_t nmom = 3,
140 const std::vector<double>& pathprob = {},
141 const std::string& lti_method =
"euler",
143 const std::size_t M = v.size();
145 throw InputError(
"pfqn_cyclet_ofree: v and mu must name the same number of nodes");
146 for (std::size_t i = 0; i < M; ++i)
148 throw InputError(
"pfqn_cyclet_ofree: every service rate must be positive");
149 if (N < 1)
throw InputError(
"pfqn_cyclet_ofree: the population N must be positive");
150 if (paths.empty())
throw InputError(
"pfqn_cyclet_ofree: no path was given");
152 std::vector<double> pp = pathprob;
153 if (pp.empty()) pp.assign(paths.size(), 1.0 /
double(paths.size()));
154 if (pp.size() != paths.size())
155 throw InputError(
"pfqn_cyclet_ofree: pathprob must carry one probability per path");
157 std::vector<double> x(M);
158 for (std::size_t i = 0; i < M; ++i) x[i] = v[i] / mu[i];
159 const double Gn1 = cyclet_detail::buzen(x, N - 1).back();
162 "pfqn_cyclet_ofree: the network normalizing constant at population N-1 vanished; "
166 out.f.assign(tset.size(), 0.0);
167 out.F.assign(tset.size(), 0.0);
168 out.mom.assign(nmom, 0.0);
170 for (std::size_t ip = 0; ip < paths.size(); ++ip) {
171 const std::vector<std::size_t>& z = paths[ip];
174 "pfqn_cyclet_ofree: an overtake-free path must contain at least the root node");
175 for (std::size_t a = 0; a < z.size(); ++a) {
177 throw InputError(
"pfqn_cyclet_ofree: a path node is outside the network");
178 for (std::size_t b = a + 1; b < z.size(); ++b)
180 throw InputError(
"pfqn_cyclet_ofree: a path must have distinct nodes");
182 const std::size_t m = z.size();
183 std::vector<bool> onpath(M,
false);
184 for (std::size_t j : z) onpath[j] =
true;
186 std::string mth = method;
191 double sep = std::numeric_limits<double>::infinity();
193 for (std::size_t a = 0; a < m; ++a) {
194 mx = std::max(mx, mu[z[a]]);
195 for (std::size_t b = a + 1; b < m; ++b)
196 sep = std::min(sep, std::abs(mu[z[a]] - mu[z[b]]));
198 mth = (sep > tol * mx) ?
"exact" :
"lt";
202 std::vector<double> fi(tset.size(), 0.0), Fi(tset.size(), 0.0);
203 if (mth ==
"exact") {
204 std::vector<double> xoff;
205 for (std::size_t i = 0; i < M; ++i)
206 if (!onpath[i]) xoff.push_back(x[i]);
207 const std::vector<double>
Gm = cyclet_detail::buzen(xoff, N - 1);
210 std::vector<std::vector<double>> coef(m, std::vector<double>(N, 0.0));
211 for (std::size_t j = 0; j < m; ++j) {
213 for (std::size_t i = 0; i < m; ++i)
214 if (i != j) den *= (mu[z[i]] - mu[z[j]]);
217 "pfqn_cyclet_ofree: Theorem 2 needs distinct service rates on the path; "
218 "two coincide. Use method 'lt'");
219 std::vector<double> w;
220 for (std::size_t i = 0; i < m; ++i)
221 if (i != j) w.push_back((v[z[i]] - v[z[j]]) / (mu[z[i]] - mu[z[j]]));
222 const std::vector<double> K = cyclet_detail::buzen(w, N - 1);
223 for (std::size_t c = 0; c < N; ++c) {
224 const double Gmc =
Gm[N - 1 - c];
225 if (Gmc == 0.0)
continue;
226 for (std::size_t i = 0; i <= c; ++i) coef[j][c - i] += Gmc * K[i] / den;
230 double pref = 1.0 / Gn1;
231 for (std::size_t j = 0; j < m; ++j) pref *= mu[z[j]];
232 for (std::size_t j = 0; j < m; ++j) {
233 const double mj = mu[z[j]], vj = v[z[j]];
234 for (std::size_t k = 0; k < N; ++k) {
235 const double c = coef[j][k];
236 if (c == 0.0)
continue;
237 const double vk = std::pow(vj,
double(k));
238 const double kf = cyclet_detail::factorial_d(k);
239 for (std::size_t it = 0; it < tset.size(); ++it) {
240 const double t = tset[it];
241 if (t < 0.0)
continue;
242 fi[it] += pref * c * vk * std::pow(t,
double(k)) / kf * std::exp(-mj * t);
244 Fi[it] += pref * c * vk / std::pow(mj,
double(k + 1)) *
245 cyclet_detail::gammainc_int(k, mj * t);
249 }
else if (mth ==
"lt") {
250 using C = std::complex<double>;
253 for (std::size_t i = 0; i < M; ++i) y[i] = C(x[i], 0.0);
254 for (std::size_t j : z) y[j] = y[j] * (C(mu[j], 0.0) / (s + C(mu[j], 0.0)));
255 C acc = cyclet_detail::buzen(y, N - 1).back() / C(Gn1, 0.0);
256 for (std::size_t j : z) acc = acc * (C(mu[j], 0.0) / (s + C(mu[j], 0.0)));
263 throw InputError(
"pfqn_cyclet_ofree: unknown method '" + method +
264 "', expected auto, exact or lt");
268 const std::size_t K = nmom;
269 std::vector<std::vector<double>> Y(M, std::vector<double>(K + 1, 0.0));
270 for (std::size_t i = 0; i < M; ++i) {
273 for (std::size_t k = 0; k <= K; ++k) {
281 std::vector<std::vector<double>> G(N, std::vector<double>(K + 1, 0.0));
283 for (std::size_t i = 0; i < M; ++i)
284 for (std::size_t nn = 1; nn < N; ++nn) {
285 const std::vector<double> add = cyclet_detail::series_mul(Y[i], G[nn - 1], K);
286 for (std::size_t k = 0; k <= K; ++k) G[nn][k] += add[k];
288 std::vector<double> L(K + 1, 0.0);
289 for (std::size_t k = 0; k <= K; ++k) L[k] = G[N - 1][k] / Gn1;
290 for (std::size_t j : z) {
291 std::vector<double> e(K + 1, 0.0);
293 for (std::size_t k = 0; k <= K; ++k) {
297 L = cyclet_detail::series_mul(L, e, K);
299 std::vector<double> momi(nmom, 0.0);
300 for (std::size_t q = 1; q <= nmom; ++q)
301 momi[q - 1] = ((q % 2) ? -1.0 : 1.0) * cyclet_detail::factorial_d(q) * L[q];
303 for (std::size_t it = 0; it < tset.size(); ++it) {
304 out.f[it] += pp[ip] * fi[it];
305 out.F[it] += pp[ip] * Fi[it];
307 for (std::size_t q = 0; q < nmom; ++q) out.mom[q] += pp[ip] * momi[q];
311 pi.lG = std::log(Gn1);
313 out.info.push_back(pi);
316 for (std::size_t it = 0; it < tset.size(); ++it) {
317 out.f[it] = std::max(0.0, out.f[it]);
318 out.F[it] = std::min(1.0, std::max(0.0, out.F[it]));