5#ifndef LINE_API_PFQN_NCOI_H
6#define LINE_API_PFQN_NCOI_H
61using OiRate = std::function<T(
const std::vector<int>&)>;
67 std::vector<int> dims;
68 std::vector<int> strides;
70 std::vector<std::vector<int>> counts;
71 std::vector<std::size_t> order;
74inline OiLattice oi_lattice(
const std::vector<int>& N) {
75 const std::size_t R = N.size();
78 lat.strides.resize(R);
79 std::size_t ngrid = 1;
81 for (std::size_t r = 0; r < R; ++r) {
82 lat.dims[r] = N[r] + 1;
83 lat.strides[r] =
static_cast<int>(ngrid);
84 ngrid *=
static_cast<std::size_t
>(lat.dims[r]);
88 lat.counts.assign(ngrid, std::vector<int>(R, 0));
89 std::vector<std::vector<std::size_t>> byPop(
static_cast<std::size_t
>(total) + 1);
90 for (std::size_t k = 0; k < ngrid; ++k) {
93 for (std::size_t r = 0; r < R; ++r) {
94 lat.counts[k][r] =
static_cast<int>(rest %
static_cast<std::size_t
>(lat.dims[r]));
95 rest /=
static_cast<std::size_t
>(lat.dims[r]);
96 sum += lat.counts[k][r];
98 byPop[
static_cast<std::size_t
>(
sum)].push_back(k);
100 lat.order.reserve(ngrid);
101 for (std::size_t s = 0; s < byPop.size(); ++s)
102 for (std::size_t i = 0; i < byPop[s].size(); ++i) lat.order.push_back(byPop[s][i]);
113std::vector<T> oi_nc_balance(
const OiLattice& lat,
const OiRate<T>& rate, std::size_t R,
114 const std::vector<T>& vis) {
117 std::vector<T> phi(lat.ngrid, zero);
118 for (std::size_t t = 0; t < lat.order.size(); ++t) {
119 const std::size_t k = lat.order[t];
120 const std::vector<int>& n = lat.counts[k];
122 for (std::size_t r = 0; r < R; ++r)
sum += n[r];
127 const T mun = rate(n);
128 if (!(mun > zero))
continue;
130 for (std::size_t r = 0; r < R; ++r)
132 acc += (vis.empty() ? one : vis[r]) *
133 phi[k -
static_cast<std::size_t
>(lat.strides[r])];
155 const std::size_t R = N.size();
156 if (Z.size() != R)
throw InputError(
"pfqn_ncoi: Z and N must have the same class count");
157 if (!visits.
empty() && (visits.
rows() != mu.size() || visits.
cols() != R))
158 throw InputError(
"pfqn_ncoi: visits must be K x R");
160 if (v < 0)
throw InputError(
"pfqn_ncoi: requires finite, nonnegative populations");
161 for (std::size_t i = 0; i < mu.size(); ++i)
162 if (!mu[i])
throw InputError(
"pfqn_ncoi: an OI rate callable is empty");
168 const detail::OiLattice lat = detail::oi_lattice(N);
172 std::vector<T> g(lat.ngrid, zero);
173 for (std::size_t k = 0; k < lat.ngrid; ++k) {
176 for (std::size_t r = 0; r < R; ++r) {
177 const int nr = lat.counts[k][r];
178 if (nr == 0)
continue;
179 if (!(Z[r] > zero)) {
183 f *=
num_pow_int(Z[r],
static_cast<unsigned>(nr)) /
190 std::vector<int> rem(R, 0), y(R, 0);
191 for (std::size_t i = 0; i < mu.size(); ++i) {
193 if (!visits.
empty()) {
195 for (std::size_t r = 0; r < R; ++r) vis[r] = visits(i, r);
197 const std::vector<T> phi = detail::oi_nc_balance<T>(lat, mu[i], R, vis);
198 std::vector<T> gnext(lat.ngrid, zero);
199 for (std::size_t kx = 0; kx < lat.ngrid; ++kx) {
200 if (!(phi[kx] != zero))
continue;
201 std::size_t base = 0;
202 for (std::size_t r = 0; r < R; ++r) {
203 rem[r] = N[r] - lat.counts[kx][r];
205 base +=
static_cast<std::size_t
>(lat.counts[kx][r]) *
206 static_cast<std::size_t
>(lat.strides[r]);
211 std::size_t ylin = 0;
212 for (std::size_t r = 0; r < R; ++r)
213 ylin +=
static_cast<std::size_t
>(y[r]) *
static_cast<std::size_t
>(lat.strides[r]);
214 gnext[base + ylin] += phi[kx] * g[ylin];
216 while (d < R && y[d] == rem[d]) {
227 const T G = g[lat.ngrid - 1];
The exception types the port throws.
Dense matrix and non-owning view.
NcResult< T > pfqn_ncoi(const std::vector< T > &Z, const std::vector< int > &N, const std::vector< OiRate< T > > &mu, const Matrix< T > &visits)
Normalizing constant of a closed network of ORDER-INDEPENDENT (OI) / pass-and-swap stations with empt...
std::function< T(const std::vector< int > &)> OiRate
An OI station's total service rate as a function of the occupancy vector.
T num_factorial(unsigned n)
Factorial as a value of T.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Number-type abstraction for the templated API port.
Convolution algorithm for the exact normalizing constant of a closed product-form network (Buzen 1973...
Return value of the normalizing-constant family, mirroring Ret.pfqnNc.