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);
101 g = 1.0 / std::pow(1.0 + std::pow(ratio, p), 1.0 / p);
103 return std::isnan(g) ? 0.0 : g;
111 double alpha = 20.0,
double pstar = 20.0) {
112 const std::size_t M =
sn.nstations, K =
sn.nclasses;
120 s.
mu.assign(M, std::vector<std::vector<double>>(K));
121 s.
phi.assign(M, std::vector<std::vector<double>>(K));
122 s.
pie.assign(M, std::vector<std::vector<double>>(K));
124 for (std::size_t i = 0; i < M; ++i)
125 for (std::size_t r = 0; r < K; ++r) {
127 s.
pie[i][r] = std::vector<double>{1.0};
130 detail::fluid_mu_phi(
sn.service[i][r], s.
mu[i][r], s.
phi[i][r]);
131 s.
pie[i][r] = detail::fluid_pie(
sn.service[i][r]);
132 const std::size_t n =
sn.service[i][r].D0.rows();
134 for (std::size_t a = 0; a < n; ++a)
135 for (std::size_t b = 0; b < n; ++b)
139 const std::size_t S =
sn.nof_stateful();
140 const bool have_rt =
sn.rt.rows() == S * K;
141 std::vector<std::size_t> sf(M, 0);
142 for (std::size_t i = 0; i < M; ++i) sf[i] =
sn.stateful_of_station(i + 1) - 1;
143 s.
rt.assign(M, std::vector<std::vector<std::vector<double>>>(
144 K, std::vector<std::vector<double>>(M, std::vector<double>(K, 0.0))));
145 for (std::size_t i = 0; i < M && have_rt; ++i)
146 for (std::size_t c = 0; c < K; ++c)
147 for (std::size_t j = 0; j < M; ++j)
148 for (std::size_t l = 0; l < K; ++l)
151 double closed_pop = 0.0;
152 for (std::size_t r = 0; r < K; ++r)
153 if (std::isfinite(
sn.classes[r].population)) closed_pop +=
sn.classes[r].population;
156 s.
weight.assign(M, std::vector<double>(K, 1.0));
157 for (std::size_t i = 0; i < M; ++i) {
158 s.
sched[i] =
sn.stations[i].sched;
159 const double c =
sn.stations[i].nservers;
160 s.
nservers[i] = std::isfinite(c) ? c : closed_pop;
162 for (std::size_t r = 0; r < K && r <
sn.stations[i].schedparam.size(); ++r)
166 "fluid: the 'statedep', 'softmin' and 'pnorm' methods have no source term and are "
167 "refused on an open model (station '" +
sn.stations[i].name +
168 "' is a Source); use method 'closing' or 'matrix'");
182 return [s, n](double,
const double* x,
double* dx) {
184 const std::size_t M = L.
qidx.size();
185 const std::size_t K = M ? L.
qidx[0].size() : 0;
186 for (std::size_t i = 0; i < n; ++i) dx[i] = 0.0;
188 for (std::size_t i = 0; i < M; ++i) {
194 for (std::size_t r = 0; r < K; ++r)
195 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) ni += x[L.
qidx[i][r] + k];
199 std::vector<std::vector<double>> wfcfs;
200 std::vector<double> wdps;
203 wfcfs.assign(K, std::vector<double>());
204 for (std::size_t r = 0; r < K; ++r) {
205 wfcfs[r].assign(L.
kic[i][r], 0.0);
206 if (!L.
enabled[i][r])
continue;
207 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) {
208 const double d = s.
d0[i][r](k, k);
209 wfcfs[r][k] = (d != 0.0) ? -1.0 / d : 0.0;
210 wni += wfcfs[r][k] * x[L.
qidx[i][r] + k];
215 for (std::size_t r = 0; r < K; ++r) wsum += s.
weight[i][r];
217 for (std::size_t r = 0; r < K; ++r)
218 wdps[r] = (wsum > 0.0) ? s.
weight[i][r] / wsum : 0.0;
219 for (std::size_t r = 0; r < K; ++r) {
221 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) blk += x[L.
qidx[i][r] + k];
222 wni += wdps[r] * blk;
229 ghat = (ni > 0.0 && c > 0.0) ? detail::fluid_pnorm_smooth(ni, c, s.
pstar) : 1.0;
231 ? detail::fluid_softmin(ni, c, s.
alpha)
235 const auto factor = [&](std::size_t r, std::size_t k) ->
double {
243 return capped * wfcfs[r][k] / wni;
246 return (ni > c) ? c * wdps[r] / wni : 1.0;
249 return (ni > c && ni > 0.0) ? c / ni : 1.0;
254 for (std::size_t r = 0; r < K; ++r) {
255 if (!L.
enabled[i][r])
continue;
256 const std::size_t b = L.
qidx[i][r];
257 for (std::size_t k = 0; k + 1 < L.
kic[i][r]; ++k)
258 for (std::size_t kp = 0; kp < L.
kic[i][r]; ++kp) {
259 if (kp == k)
continue;
260 const double flow = x[b + k] * s.
d0[i][r](k, kp) * factor(r, k);
267 for (std::size_t r = 0; r < K; ++r) {
268 if (!L.
enabled[i][r])
continue;
269 const std::size_t b = L.
qidx[i][r];
270 for (std::size_t j = 0; j < M; ++j) {
273 for (std::size_t l = 0; l < K; ++l) {
274 if (!L.
enabled[j][l])
continue;
275 const double p = s.
rt[i][r][j][l];
276 if (!(p > 0.0))
continue;
277 const std::size_t bj = L.
qidx[j][l];
278 for (std::size_t k = 0; k < L.
kic[i][r]; ++k) {
279 const double base = s.
phi[i][r][k] * s.
mu[i][r][k] * p * factor(r, k);
280 for (std::size_t kj = 0; kj < L.
kic[j][l]; ++kj) {
281 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.
A queueing network and its refreshed NetworkStruct.
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