5#ifndef LINE_SOLVERS_FLUID_FLUID_ODES_STATEDEP_H
6#define LINE_SOLVERS_FLUID_FLUID_ODES_STATEDEP_H
70 std::vector<std::vector<std::vector<double>>>
mu,
phi,
pie;
71 std::vector<std::vector<Matrix<double>>>
d0;
72 std::vector<std::vector<std::vector<std::vector<double>>>>
rt;
73 std::vector<lang::SchedStrategy>
sched;
75 std::vector<std::vector<double>>
weight;
84inline double fluid_softmin(
double x,
double y,
double alpha) {
85 const double lo = std::min(x, y), hi = std::max(x, y);
86 const double gap = hi - lo;
88 if (!(gap < 745.0 / alpha))
return lo;
89 const double w = std::exp(-alpha * gap);
90 return lo + gap * w / (1.0 + w);
94inline double fluid_pnorm_smooth(
double x,
double c,
double p) {
95 if (x <= 0.0 || c <= 0.0)
return 0.0;
96 const double ratio = x / c;
99 g = std::min(1.0, c / x);
102 g = ratio > 1.0 ? (1.0 / ratio) / std::pow(1.0 + std::pow(1.0 / ratio, p), 1.0 / p)
103 : 1.0 / std::pow(1.0 + std::pow(ratio, p), 1.0 / p);
105 return std::isnan(g) ? 0.0 : g;
113 double alpha = 20.0,
double pstar = 20.0) {
114 const std::size_t M =
sn.nstations, K =
sn.nclasses;
122 s.
mu.assign(M, std::vector<std::vector<double>>(K));
123 s.
phi.assign(M, std::vector<std::vector<double>>(K));
124 s.
pie.assign(M, std::vector<std::vector<double>>(K));
126 for (std::size_t i = 0; i < M; ++i)
127 for (std::size_t r = 0; r < K; ++r) {
129 s.
pie[i][r] = std::vector<double>{1.0};
132 detail::fluid_mu_phi(
sn.service[i][r], s.
mu[i][r], s.
phi[i][r]);
133 s.
pie[i][r] = detail::fluid_pie(
sn.service[i][r]);
134 const std::size_t n =
sn.service[i][r].D0.rows();
136 for (std::size_t a = 0; a < n; ++a)
137 for (std::size_t b = 0; b < n; ++b)
141 const std::size_t S =
sn.nof_stateful();
142 const bool have_rt =
sn.rt.rows() == S * K;
143 std::vector<std::size_t> sf(M, 0);
144 for (std::size_t i = 0; i < M; ++i) sf[i] =
sn.stateful_of_station(i + 1) - 1;
145 s.
rt.assign(M, std::vector<std::vector<std::vector<double>>>(
146 K, std::vector<std::vector<double>>(M, std::vector<double>(K, 0.0))));
147 for (std::size_t i = 0; i < M && have_rt; ++i)
148 for (std::size_t c = 0; c < K; ++c)
149 for (std::size_t j = 0; j < M; ++j)
150 for (std::size_t l = 0; l < K; ++l)
153 double closed_pop = 0.0;
154 for (std::size_t r = 0; r < K; ++r)
155 if (std::isfinite(
sn.classes[r].population)) closed_pop +=
sn.classes[r].population;
158 s.
weight.assign(M, std::vector<double>(K, 1.0));
159 for (std::size_t i = 0; i < M; ++i) {
160 s.
sched[i] =
sn.stations[i].sched;
161 const double c =
sn.stations[i].nservers;
162 s.
nservers[i] = std::isfinite(c) ? c : closed_pop;
164 for (std::size_t r = 0; r < K && r <
sn.stations[i].schedparam.size(); ++r)
168 "fluid: the 'statedep', 'softmin' and 'pnorm' methods have no source term and are "
169 "refused on an open model (station '" +
sn.stations[i].name +
170 "' is a Source); use method 'closing' or 'matrix'");
184 return [s, n](double,
const double* x,
double* dx) {
186 const std::size_t M = L.
qidx.size();
187 const std::size_t K = M ? L.
qidx[0].size() : 0;
188 for (std::size_t i = 0; i < n; ++i) dx[i] = 0.0;
190 for (std::size_t i = 0; i < M; ++i) {
196 for (std::size_t r = 0; r < K; ++r)
197 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) ni += x[L.
qidx[i][r] + k];
201 std::vector<std::vector<double>> wfcfs;
202 std::vector<double> wdps;
205 wfcfs.assign(K, std::vector<double>());
206 for (std::size_t r = 0; r < K; ++r) {
207 wfcfs[r].assign(L.
kic[i][r], 0.0);
208 if (!L.
enabled[i][r])
continue;
209 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) {
210 const double d = s.
d0[i][r](k, k);
211 wfcfs[r][k] = (d != 0.0) ? -1.0 / d : 0.0;
212 wni += wfcfs[r][k] * x[L.
qidx[i][r] + k];
217 for (std::size_t r = 0; r < K; ++r) wsum += s.
weight[i][r];
219 for (std::size_t r = 0; r < K; ++r)
220 wdps[r] = (wsum > 0.0) ? s.
weight[i][r] / wsum : 0.0;
221 for (std::size_t r = 0; r < K; ++r) {
223 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) blk += x[L.
qidx[i][r] + k];
224 wni += wdps[r] * blk;
231 ghat = (ni > 0.0 && c > 0.0) ? detail::fluid_pnorm_smooth(ni, c, s.
pstar) : 1.0;
233 ? detail::fluid_softmin(ni, c, s.
alpha)
237 const auto factor = [&](std::size_t r, std::size_t k) ->
double {
245 return capped * wfcfs[r][k] / wni;
248 return (ni > c) ? c * wdps[r] / wni : 1.0;
251 return (ni > c && ni > 0.0) ? c / ni : 1.0;
256 for (std::size_t r = 0; r < K; ++r) {
257 if (!L.
enabled[i][r])
continue;
258 const std::size_t b = L.
qidx[i][r];
259 for (std::size_t k = 0; k + 1 < L.
kic[i][r]; ++k)
260 for (std::size_t kp = 0; kp < L.
kic[i][r]; ++kp) {
261 if (kp == k)
continue;
262 const double flow = x[b + k] * s.
d0[i][r](k, kp) * factor(r, k);
269 for (std::size_t r = 0; r < K; ++r) {
270 if (!L.
enabled[i][r])
continue;
271 const std::size_t b = L.
qidx[i][r];
272 for (std::size_t j = 0; j < M; ++j) {
275 for (std::size_t l = 0; l < K; ++l) {
276 if (!L.
enabled[j][l])
continue;
277 const double p = s.
rt[i][r][j][l];
278 if (!(p > 0.0))
continue;
279 const std::size_t bj = L.
qidx[j][l];
280 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) {
281 const double base = s.
phi[i][r][k] * s.
mu[i][r][k] * p * factor(r, k);
282 for (std::size_t kj = 0; kj < L.
kic[j][l]; ++kj) {
283 const double flow = x[b + k] * base * s.
pie[j][l][kj];
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
The exception types the port throws.
The fluid drift: a port of solver_fluid_odes.m and the ode_jumps_new / ode_rate_base / ode_rates_clos...
FluidLayout fluid_layout(const qn::NetworkStruct< T > &sn)
Port of the layout half of solver_fluid_odes.m.
StateDepKind
Which smoothing the drift applies at a saturated station.
std::function< void(double, const double *, double *)> fluid_drift_statedep(const FluidStateDepSystem &s)
The drift dx/dt for the state-dependent family.
FluidStateDepSystem fluid_statedep_system(const qn::NetworkStruct< T > &sn, StateDepKind kind, double alpha=20.0, double pstar=20.0)
Assemble what the state-dependent drifts need from sn.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Where each (station, class) block sits in the state vector.
std::vector< std::vector< std::size_t > > qidx
0-based first index of (i,r)
std::size_t nstates
length of the state vector
std::vector< std::vector< bool > > enabled
whether (i,r) is served at all
std::vector< std::vector< std::size_t > > kic
phases held by (i,r); 0 when disabled
Everything the state-dependent drifts read, lowered once to doubles.
double alpha
softmin sharpness
std::vector< std::vector< std::vector< double > > > mu
std::vector< std::vector< std::vector< std::vector< double > > > > rt
rt[i][c][j][l]
std::vector< std::vector< Matrix< double > > > d0
the PH generator per (i,c)
std::vector< std::vector< double > > weight
double pstar
p-norm exponent
std::vector< std::vector< std::vector< double > > > phi
std::vector< double > nservers
std::vector< lang::SchedStrategy > sched
std::vector< std::vector< std::vector< double > > > pie
static constexpr double FineTol