5#ifndef LINE_API_PFQN_RECAL_H
6#define LINE_API_PFQN_RECAL_H
73constexpr unsigned long long RECAL_MAX_STATES = 100000000ULL;
80inline unsigned long long binom_exact(
unsigned long long n,
unsigned long long k) {
81 if (k > n)
return 0ULL;
82 if (k > n - k) k = n - k;
83 unsigned long long r = 1ULL;
84 for (
unsigned long long i = 1ULL; i <= k; ++i) {
85 const unsigned long long a = n - k + i;
86 if (r > std::numeric_limits<unsigned long long>::max() / a)
87 throw NumericError(
"pfqn_recal: multiplicity state space overflows a 64-bit count");
94inline unsigned long long multichoose_count(std::size_t ncols,
int k) {
95 if (k < 0)
return 0ULL;
96 if (ncols == 0)
return k == 0 ? 1ULL : 0ULL;
97 return binom_exact(
static_cast<unsigned long long>(ncols) +
static_cast<unsigned long long>(k) - 1ULL,
98 static_cast<unsigned long long>(k));
107inline std::size_t recal_rank(
const std::vector<int>& m, std::size_t ncols,
int ksum) {
108 unsigned long long idx = 0ULL;
111 const int c = m[pos];
112 for (
int i = 0; i < c; ++i) idx += multichoose_count(ncols - 1, ksum - i);
117 return static_cast<std::size_t
>(idx);
124inline bool recal_next_composition(std::vector<int>& m) {
125 const std::size_t n = m.size();
126 if (n < 2)
return false;
127 const std::size_t last = n - 1;
129 for (
long p =
static_cast<long>(n) - 2; p >= 0; --p) {
130 rest += m[
static_cast<std::size_t
>(p) + 1];
132 m[
static_cast<std::size_t
>(p)] += 1;
133 for (std::size_t j =
static_cast<std::size_t
>(p) + 1; j < last; ++j) m[j] = 0;
134 m[last] =
static_cast<int>(rest - 1);
147void consolidate_stations(
const Matrix<T>& L,
const std::vector<int>& m0, Matrix<T>& Lu,
148 std::vector<int>& m0u) {
149 const std::size_t M = L.rows(), R = L.cols();
150 std::vector<std::size_t> keep;
151 std::vector<std::size_t> mapping(M, 0);
152 for (std::size_t i = 0; i < M; ++i) {
153 std::size_t hit = keep.size();
154 for (std::size_t u = 0; u < keep.size(); ++u) {
156 for (std::size_t r = 0; r < R; ++r)
157 if (!(L(i, r) == L(keep[u], r))) {
166 if (hit == keep.size()) keep.push_back(i);
170 for (std::size_t u = 0; u < keep.size(); ++u)
171 for (std::size_t r = 0; r < R; ++r) Lu(u, r) = L(keep[u], r);
172 m0u.assign(keep.size(), 0);
173 for (std::size_t i = 0; i < M; ++i) m0u[mapping[i]] += m0[i];
189 const std::vector<int>& m0) {
190 const std::size_t M = L.
rows();
191 const std::size_t R = N.size();
193 throw InputError(
"pfqn_recal: demand matrix and population vector disagree on the class count");
194 if (!m0.empty() && m0.size() != M)
195 throw InputError(
"pfqn_recal: multiplicity vector has the wrong length");
196 for (std::size_t i = 0; i < m0.size(); ++i)
197 if (m0[i] < 1)
throw InputError(
"pfqn_recal: station multiplicity below one");
203 std::vector<T> Zsum(R, zero);
205 if (Z.
cols() != R)
throw InputError(
"pfqn_recal: Z and N disagree on the class count");
206 for (std::size_t k = 0; k < Z.
rows(); ++k)
207 for (std::size_t r = 0; r < R; ++r) Zsum[r] += Z(k, r);
213 if (v < 0)
throw InputError(
"pfqn_recal: negative population");
219 const T G = detail::pff_delay(Zsum, N);
222 if (Nt == 0)
return {one, 0.0};
224 std::vector<int> mult(M, 1);
225 for (std::size_t i = 0; i < m0.size(); ++i) mult[i] = m0[i];
227 std::vector<int> m0c;
228 detail::consolidate_stations(L, mult,
Lc, m0c);
229 const std::size_t Mq =
Lc.rows();
231 const int kscale = detail::scale_exponent(
Lc, N, Zsum);
232 if constexpr (std::is_same<T, double>::value) {
235 for (std::size_t i = 0; i < Mq; ++i)
236 for (std::size_t r = 0; r < R; ++r)
Lc(i, r) = std::ldexp(
Lc(i, r), -kscale);
237 for (std::size_t r = 0; r < R; ++r) Zsum[r] = std::ldexp(Zsum[r], -kscale);
242 for (std::size_t r = 0; r < R; ++r)
243 if (!(Zsum[r] == zero)) {
249 const std::size_t Mz = hasZ ? Mq + 1 : Mq;
250 const std::size_t delay = Mq;
252 const unsigned long long states = detail::multichoose_count(Mz,
static_cast<int>(Nt));
253 if (states > detail::RECAL_MAX_STATES)
254 throw NumericError(
"pfqn_recal: multiplicity state space too large for this model");
258 std::vector<T> gprev(
static_cast<std::size_t
>(states), one);
259 std::vector<T> gcur(
static_cast<std::size_t
>(states), zero);
261 std::vector<int> m(Mz, 0);
263 for (std::size_t r = 0; r < R; ++r) {
264 for (
int nr = 1; nr <= N[r]; ++nr) {
266 const int k =
static_cast<int>(Nt) - n;
267 const int kprev = k + 1;
268 const unsigned long long ncfg = detail::multichoose_count(Mz, k);
270 const bool thinks = hasZ && !(Zsum[r] == zero);
274 for (
unsigned long long i = 0; i < ncfg; ++i) {
278 acc += Zsum[r] * gprev[detail::recal_rank(m, Mz, kprev)];
281 for (std::size_t j = 0; j < Mq; ++j) {
282 if (
Lc(j, r) == zero)
continue;
285 gprev[detail::recal_rank(m, Mz, kprev)];
288 gcur[
static_cast<std::size_t
>(i)] = acc / nrv;
289 if (i + 1 < ncfg) detail::recal_next_composition(m);
296 const T raw = gprev[0];
300 if constexpr (std::is_same<T, double>::value) {
301 if (kscale != 0) G = std::ldexp(raw,
static_cast<int>(Nt * kscale));
309 return pfqn_recal(L, N, Z, std::vector<int>());
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
NcResult< T > pfqn_recal(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &m0)
RECAL (REcursive CALculation) for the exact normalizing constant of a closed product-form network (Co...
@ Lc
Birman-Kogan Algorithm 2, single chain subproblems by MVA.
Number-type abstraction for the templated API port.
Convolution algorithm for the exact normalizing constant of a closed product-form network (Buzen 1973...
Population-vector enumeration and combinatorics.
Return value of the normalizing-constant family, mirroring Ret.pfqnNc.