151 const std::size_t M = L.
rows();
152 const std::size_t R = N.size();
154 throw InputError(
"pfqn_gld: demand matrix and population vector disagree on the class count");
160 bool negative =
false;
162 if (v < 0) negative =
true;
166 if (negative)
return {zero, -std::numeric_limits<double>::infinity()};
167 if (Nt == 0)
return {one, 0.0};
169 if (M == 0)
return {zero, -std::numeric_limits<double>::infinity()};
172 throw InputError(
"pfqn_gld: rate matrix and demand matrix disagree on the station count");
173 if (
static_cast<long>(mu.
cols()) < Nt)
174 throw InputError(
"pfqn_gld: rate matrix needs one column per job in the total population");
175 for (std::size_t i = 0; i < M; ++i)
176 for (
long a = 0; a < Nt; ++a)
177 if (mu(i,
static_cast<std::size_t
>(a)) == zero)
178 throw InputError(
"pfqn_gld: load-dependent service rate must be nonzero");
180 const int kscale = detail::gld_scale_exponent(L, N, mu, Nt);
182 if constexpr (std::is_same<T, double>::value) {
184 for (std::size_t i = 0; i < M; ++i)
185 for (std::size_t r = 0; r < R; ++r)
Ls(i, r) = std::ldexp(
Ls(i, r), -kscale);
189 const std::vector<std::size_t> prods =
plane_sizes(N);
193 std::vector<T> Gv(total, zero);
196 std::vector<T> Y, Gs;
197 std::vector<T> muprod;
198 std::vector<int> n(R, 0), k(R, 0);
200 for (std::size_t m = 0; m < M; ++m) {
201 bool loadIndependent =
true;
202 for (
long a = 0; a < Nt && loadIndependent; ++a)
203 if (!(mu(m,
static_cast<std::size_t
>(a)) == one)) loadIndependent =
false;
205 if (loadIndependent) {
208 std::fill(n.begin(), n.end(), 0);
210 const std::size_t idx =
pop_index(n, prods);
212 for (std::size_t r = 0; r < R; ++r)
213 if (n[r] > 0) acc +=
Ls(m, r) * Gv[idx - prods[r]];
221 muprod.assign(
static_cast<std::size_t
>(Nt) + 1, one);
222 for (
long j = 1; j <= Nt; ++j)
223 muprod[
static_cast<std::size_t
>(j)] =
224 muprod[
static_cast<std::size_t
>(j - 1)] * mu(m,
static_cast<std::size_t
>(j - 1));
226 Y.assign(total, zero);
227 std::fill(k.begin(), k.end(), 0);
231 for (
int v : k) j += v;
232 const std::size_t ik =
pop_index(k, prods);
234 bool vanishes =
false;
236 for (std::size_t r = 0; r < R && !vanishes; ++r) {
238 if (
Ls(m, r) == zero)
246 for (std::size_t r = 0; r < R; ++r) den *= num_factorial<T>(
static_cast<unsigned>(k[r]));
247 Y[ik] = num / den / muprod[
static_cast<std::size_t
>(j)];
252 Gs.assign(total, zero);
253 std::fill(n.begin(), n.end(), 0);
256 const std::size_t idx =
pop_index(n, prods);
258 std::fill(k.begin(), k.end(), 0);
260 std::size_t ik = 0, idiff = 0;
261 for (std::size_t r = 0; r < R; ++r) {
262 ik += prods[r] *
static_cast<std::size_t
>(k[r]);
263 idiff += prods[r] *
static_cast<std::size_t
>(n[r] - k[r]);
265 if (!(Y[ik] == zero) && !(Gv[idiff] == zero)) acc += Y[ik] * Gv[idiff];
267 for (std::size_t r = 0; r < R; ++r) {
283 const T raw = Gv[total - 1];
287 if constexpr (std::is_same<T, double>::value) {
288 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).