5#ifndef LINE_API_INFER_INFER_FMLPS_H
6#define LINE_API_INFER_INFER_FMLPS_H
79namespace fmlpsdetail {
92 std::size_t base, tag0, P, n, nt;
94 std::size_t minus, plus, event_idx;
97 std::vector<TagEvent> tev;
108 for (std::size_t e = 0; e < sys.
events.size(); ++e) {
111 const std::size_t k = ev.
event_idx - td.base;
112 TaggedDrift::TagEvent t2;
113 t2.event_idx = td.tag0 + k;
115 t2.minus = td.tag0 + k;
122 t2.plus = td.tag0 + (ev.
plus - td.base);
124 td.tev.push_back(t2);
143 std::size_t cls,
const std::vector<double>& levels,
144 double rsampled,
double marked = 1.0) {
145 const std::size_t M =
sn.nstations, K =
sn.nclasses;
146 if (ist == 0 || ist > M)
throw InputError(
"infer_fluid_ps_rt_likelihood: station out of range");
147 if (cls == 0 || cls > K)
throw InputError(
"infer_fluid_ps_rt_likelihood: class out of range");
148 if (!(rsampled > 0.0))
149 throw InputError(
"infer_fluid_ps_rt_likelihood: the response time must be positive");
151 throw InputError(
"infer_fluid_ps_rt_likelihood: the marked mass must be positive");
153 const std::size_t i = ist - 1, c = cls - 1;
156 if (levels.size() != L.
nstates)
158 "infer_fluid_ps_rt_likelihood: the initial fluid state has the wrong length for this "
161 const fmlpsdetail::TaggedDrift td = fmlpsdetail::build_tagged(sys, i, c);
175 std::vector<double> y0(td.nt, 0.0);
176 for (std::size_t s = 0; s < td.n; ++s) y0[s] = levels[s];
178 for (std::size_t k = 0; k < td.P; ++k) avail += y0[td.base + k];
179 if (avail + 1e-12 < marked)
181 "infer_fluid_ps_rt_likelihood: the initial state holds less fluid in the tagged block "
182 "than the observation marks, so the observed job is not in the state it arrived to");
185 for (std::size_t k = 0; k < td.P; ++k) y0[td.base + k] -= marked * (y0[td.base + k] / avail);
186 y0[td.tag0] = marked;
188 const std::size_t base = td.base, tag0 = td.tag0, P = td.P, n = td.n, nt = td.nt;
189 const std::vector<fmlpsdetail::TaggedDrift::TagEvent> tev = td.tev;
190 auto drift = [&sys, &tev, base, tag0, P, n, nt](double,
const double* x,
double* dx) {
191 std::vector<double> xb(x, x + n);
192 std::vector<double> g(xb);
193 for (std::size_t k = 0; k < P; ++k) g[base + k] += x[tag0 + k];
194 std::vector<double> gg(g);
196 double blk = 0.0, gblk = 0.0;
197 for (std::size_t k = 0; k < P; ++k) {
199 gblk += gg[base + k];
201 const double share = (blk > 0.0) ? gblk / blk : 1.0;
203 for (std::size_t s = 0; s < nt; ++s) dx[s] = 0.0;
204 for (std::size_t e = 0; e < sys.
events.size(); ++e) {
208 drive = xb[base + (ev.
event_idx - base)] * share;
210 if (r == 0.0)
continue;
214 for (std::size_t e = 0; e < tev.size(); ++e) {
215 const double r = tev[e].rate_base * x[tev[e].event_idx] * share;
216 if (r == 0.0)
continue;
217 dx[tev[e].minus] -= r;
218 dx[tev[e].plus] += r;
222 std::vector<double> grid;
224 grid.push_back(rsampled);
229 if (sol.
y.empty())
throw InputError(
"infer_fluid_ps_rt_likelihood: the ODE returned no state");
231 const std::vector<double>& yT = sol.
y.back();
233 for (std::size_t k = 0; k < P; ++k) left += std::max(0.0, yT[tag0 + k]);
240 std::vector<double> dy(nt, 0.0);
241 drift(rsampled, yT.data(), dy.data());
243 for (std::size_t k = 0; k < P; ++k) ddt += dy[tag0 + k];
244 out.
like = -ddt / marked;
266 const std::vector<MlpsSample>& samples) {
267 const std::size_t M =
sn.nstations, K =
sn.nclasses;
268 if (ist == 0 || ist > M)
throw InputError(
"infer_fmlps: station out of range");
269 if (samples.empty())
throw InputError(
"infer_fmlps: no observations");
270 for (std::size_t s = 0; s < samples.size(); ++s) {
271 if (samples[s].cls < 1 || samples[s].cls > K)
272 throw InputError(
"infer_fmlps: a sample names a class outside 1..K");
273 if (samples[s].ql.size() != K)
274 throw InputError(
"infer_fmlps: a sample's queue length has the wrong width");
275 if (!(samples[s].rt > 0.0))
276 throw InputError(
"infer_fmlps: a response time must be positive");
280 double meanQL = 0.0, rtmax = 0.0;
281 for (std::size_t s = 0; s < samples.size(); ++s) {
282 for (std::size_t r = 0; r < K; ++r) meanQL += samples[s].ql[r];
283 rtmax = std::max(rtmax, samples[s].rt);
285 meanQL /=
static_cast<double>(samples.size());
286 const double nCores =
sn.stations[ist - 1].nservers;
287 const double Vtilde = std::min(meanQL, std::isinf(nCores) ? meanQL : nCores);
288 std::vector<double> x0(K, 1e-3);
289 for (std::size_t r = 0; r < K; ++r) {
292 for (std::size_t s = 0; s < samples.size(); ++s)
293 if (samples[s].cls == r + 1) {
294 sum += samples[s].rt;
297 if (cnt > 0 && meanQL > 0.0) x0[r] = Vtilde * (
sum /
static_cast<double>(cnt)) / meanQL;
300 const double TOL = 1e-6;
301 auto objective = [&](
const std::vector<double>& x) {
302 for (std::size_t r = 0; r < K; ++r)
303 if (!(x[r] > 0.0))
return std::numeric_limits<double>::infinity();
306 for (std::size_t r = 0; r < K; ++r)
313 for (std::size_t s = 0; s < samples.size(); ++s) {
316 for (std::size_t r = 0; r < K; ++r) {
317 const std::size_t P = sysx.
layout.
kic[ist - 1][r];
318 if (P == 0)
continue;
319 for (std::size_t k = 0; k < P; ++k)
321 samples[s].ql[r] /
static_cast<double>(P);
327 }
catch (
const Error&) {
332 f -= std::log(TOL + std::max(0.0, like));
337 std::vector<Bound<double>> bounds(K);
338 for (std::size_t r = 0; r < K; ++r) {
339 bounds[r].has_lo =
true;
340 bounds[r].has_hi =
true;
342 bounds[r].hi = rtmax;
Base error for the multiprecision C++ port.
A network plus its refreshed NetworkStruct.
void refresh_rates()
Port of MNetwork.refreshRates: lower each service process onto a rate and an SCV.
void set_service(std::size_t station, std::size_t cls, const Distrib< T > &d)
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...
Port of ode_eliminate_immediate.m, eliminate_immediate_matrix.m and ode_solve_stiff....
Maximum-likelihood service-demand estimation at a processor-sharing queue.
LSODA: the LINE-facing wrapper over the vendored solver in third_party/lsoda.hpp.
Dense matrix and non-owning view.
std::vector< double > infer_fmlps(const qn::NetworkStruct< T > &sn, std::size_t ist, const std::vector< MlpsSample > &samples)
FMLPS: the fluid analogue of MLPS.
FluidRtLikelihood infer_fluid_ps_rt_likelihood(const qn::NetworkStruct< T > &sn, std::size_t ist, std::size_t cls, const std::vector< double > &levels, double rsampled, double marked=1.0)
The fluid passage-time density at one observed response time.
LsodaSolution fluid_integrate_grid(const std::function< void(double, const double *, double *)> &f, const std::vector< double > &y0, const std::vector< double > &grid, const LsodaOptions &lopt)
The same retry over a whole output grid, for the callers that ask LSODA for a trajectory rather than ...
FluidOdeSystem fluid_ode_system(const qn::NetworkStruct< T > &sn)
Build the drift of sn: the port of ode_jumps_new and ode_rate_base fused into one pass.
void fluid_rates_closing(const FluidOdeSystem &sys, const double *x, std::vector< double > &g)
The reference's ode_rates_closing name, kept for the first-order callers.
NelderMeadResult< T > nelder_mead_box(F f, const std::vector< T > &x0, const std::vector< Bound< T > > &bounds, const NelderMeadOptions< T > &opt)
Box-constrained simplex minimization by the transformation described in the header comment.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Adaptive stiff ODE integrator: a four-stage Rosenbrock method of order four with an embedded order-th...
double atol
absolute tolerance, applied to every component
double rtol
relative tolerance, applied to every component
Result of an integration, mirroring OdeSolution in ode.h.
std::vector< std::vector< double > > y
y[i] is the state at t[i]
What the fluid likelihood call reports.
double markedT
what is left of it at the observed time
std::size_t nstates
size of the augmented system
double like
the passage-time density at the observed time
double marked0
the marked mass placed at t = 0
std::size_t event_idx
state entry whose g(x) drives this rate
double rate_base
the model-fixed part of the rate
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< std::size_t > > kic
phases held by (i,r); 0 when disabled
std::size_t n_departures
How many leading entries of events are DEPARTURES (a job completing at one block and starting at anot...
std::vector< FluidEvent > events
static Distrib exp_rate(const T &r)