145 const std::size_t M = L.
rows();
146 const std::size_t R = N.size();
148 throw InputError(
"pfqn_gld: demand matrix and population vector disagree on the class count");
154 bool negative =
false;
156 if (v < 0) negative =
true;
160 if (negative)
return {zero, -std::numeric_limits<double>::infinity()};
161 if (Nt == 0)
return {one, 0.0};
163 if (M == 0)
return {zero, -std::numeric_limits<double>::infinity()};
166 throw InputError(
"pfqn_gld: rate matrix and demand matrix disagree on the station count");
167 if (
static_cast<long>(mu.
cols()) < Nt)
168 throw InputError(
"pfqn_gld: rate matrix needs one column per job in the total population");
169 for (std::size_t i = 0; i < M; ++i)
170 for (
long a = 0; a < Nt; ++a)
171 if (mu(i,
static_cast<std::size_t
>(a)) == zero)
172 throw InputError(
"pfqn_gld: load-dependent service rate must be nonzero");
174 const int kscale = detail::gld_scale_exponent(L, N, mu, Nt);
176 if constexpr (std::is_same<T, double>::value) {
178 for (std::size_t i = 0; i < M; ++i)
179 for (std::size_t r = 0; r < R; ++r)
Ls(i, r) = std::ldexp(
Ls(i, r), -kscale);
183 const std::vector<std::size_t> prods =
plane_sizes(N);
187 std::vector<T> Gv(total, zero);
190 std::vector<T> Y, Gs;
191 std::vector<T> muprod;
192 std::vector<int> n(R, 0), k(R, 0);
194 for (std::size_t m = 0; m < M; ++m) {
195 bool loadIndependent =
true;
196 for (
long a = 0; a < Nt && loadIndependent; ++a)
197 if (!(mu(m,
static_cast<std::size_t
>(a)) == one)) loadIndependent =
false;
199 if (loadIndependent) {
202 std::fill(n.begin(), n.end(), 0);
204 const std::size_t idx =
pop_index(n, prods);
206 for (std::size_t r = 0; r < R; ++r)
207 if (n[r] > 0) acc +=
Ls(m, r) * Gv[idx - prods[r]];
215 muprod.assign(
static_cast<std::size_t
>(Nt) + 1, one);
216 for (
long j = 1; j <= Nt; ++j)
217 muprod[
static_cast<std::size_t
>(j)] =
218 muprod[
static_cast<std::size_t
>(j - 1)] * mu(m,
static_cast<std::size_t
>(j - 1));
220 Y.assign(total, zero);
221 std::fill(k.begin(), k.end(), 0);
225 for (
int v : k) j += v;
226 const std::size_t ik =
pop_index(k, prods);
228 bool vanishes =
false;
230 for (std::size_t r = 0; r < R && !vanishes; ++r) {
232 if (
Ls(m, r) == zero)
240 for (std::size_t r = 0; r < R; ++r) den *= num_factorial<T>(
static_cast<unsigned>(k[r]));
241 Y[ik] = num / den / muprod[
static_cast<std::size_t
>(j)];
246 Gs.assign(total, zero);
247 std::fill(n.begin(), n.end(), 0);
250 const std::size_t idx =
pop_index(n, prods);
252 std::fill(k.begin(), k.end(), 0);
254 std::size_t ik = 0, idiff = 0;
255 for (std::size_t r = 0; r < R; ++r) {
256 ik += prods[r] *
static_cast<std::size_t
>(k[r]);
257 idiff += prods[r] *
static_cast<std::size_t
>(n[r] - k[r]);
259 if (!(Y[ik] == zero) && !(Gv[idiff] == zero)) acc += Y[ik] * Gv[idiff];
261 for (std::size_t r = 0; r < R; ++r) {
277 const T raw = Gv[total - 1];
281 if constexpr (std::is_same<T, double>::value) {
282 if (kscale != 0) Gn = std::ldexp(raw,
static_cast<int>(Nt *
static_cast<long>(kscale)));
NcResult< T > pfqn_gld(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &mu)
Exact normalizing constant of a closed product-form network whose stations may be load dependent (gen...
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).