5#ifndef LINE_SOLVERS_LN_LQN_ANALYZERS_H
6#define LINE_SOLVERS_LN_LQN_ANALYZERS_H
102 const T den = T(one + lambda * (S1 + S2));
104 pi.
idle = T(one / den);
105 pi.
phase1 = T(lambda * S1 / den);
106 pi.
phase2 = T(lambda * S2 / den);
140 auto clamp01 = [&](
const T& x) {
return x < zero ? zero : (x > one ? one : x); };
146 if (!std::isfinite(mult))
return zero;
147 const T phase_frac = T(S2 / (S1 + S2));
149 if (rho >= one)
return clamp01(phase_frac);
150 return clamp01(T(phase_frac * rho));
161std::size_t ln_entry_of_activity(
const LqnStruct<T>&
lqn, std::size_t aidx) {
162 for (std::size_t e = 1; e <=
lqn.nentries; ++e) {
163 const std::size_t eabs =
lqn.eshift + e;
164 for (std::size_t a :
lqn.actsof[eabs])
165 if (a == aidx)
return eabs;
198 const std::vector<T>& callresidt,
const std::vector<T>& tput,
199 std::size_t eidx,
const T& xj) {
203 if (servt.size() <=
lqn.nidx || tput.size() <=
lqn.nidx ||
204 callresidt.size() <=
lqn.ncalls)
206 "lqn_overtake_prob_markov: servt and tput must be indexed 1..nidx and callresidt "
207 "1..ncalls, as SolverLN's state accessors return them");
208 if (eidx <= lqn.eshift || eidx >
lqn.eshift +
lqn.nentries)
209 throw InputError(
"lqn_overtake_prob_markov: eidx is not an entry index");
212 const std::size_t server_tidx =
lqn.parent[eidx];
215 std::vector<std::size_t> caller_entries;
216 for (std::size_t c = 1; c <=
lqn.ncalls; ++c) {
217 if (
lqn.callpair_dst[c] != eidx)
continue;
218 const std::size_t ceidx = detail::ln_entry_of_activity(
lqn,
lqn.callpair_src[c]);
219 if (ceidx == 0)
continue;
221 for (std::size_t v : caller_entries)
222 if (v == ceidx) known =
true;
223 if (!known) caller_entries.push_back(ceidx);
225 if (caller_entries.empty())
return zero;
228 for (std::size_t ceidx : caller_entries) {
229 const std::size_t ctidx =
lqn.parent[ceidx];
230 const std::vector<std::size_t>& acts =
lqn.actsof[ceidx];
231 if (acts.empty())
continue;
234 for (std::size_t aidx : acts) {
235 const std::size_t a = aidx -
lqn.ashift;
236 if (a >= 1 && a <=
lqn.nacts &&
lqn.actphase[a] > maxPhaseA) maxPhaseA =
lqn.actphase[a];
238 const std::size_t nStates = std::size_t(maxPhaseA) + 1;
240 Matrix<T> clientPhases(nStates, 5, zero);
241 clientPhases(0, 0) = one;
245 std::vector<T> y_aj(nStates, zero);
246 for (
int p = 1; p <= maxPhaseA; ++p) {
247 T nSlices = one, service = zero, y_ij = zero, y_ik = zero, tk_num = zero;
248 for (std::size_t aidx : acts) {
249 const std::size_t a = aidx -
lqn.ashift;
250 if (a < 1 || a >
lqn.nacts ||
lqn.actphase[a] != p)
continue;
251 service = T(service + servt[aidx]);
252 for (std::size_t c :
lqn.callsof[aidx]) {
253 if (
lqn.calltype[c] != CallType::SYNC)
continue;
254 const T y =
lqn.callproc_mean[c];
255 if (y == zero)
continue;
258 nSlices = T(nSlices + y);
259 if (
lqn.parent[
lqn.callpair_dst[c]] == server_tidx) {
263 tk_num = T(tk_num + y * callresidt[c]);
267 const T t_k = y_ik > zero ? T(tk_num / y_ik) : zero;
268 const std::size_t row = std::size_t(p);
269 clientPhases(row, 0) = nSlices;
270 clientPhases(row, 1) = service;
271 clientPhases(row, 2) = y_ij;
272 clientPhases(row, 3) = y_ik;
273 clientPhases(row, 4) = t_k;
275 y_aj[0] = T(y_aj[0] + y_ij);
277 if (y_aj[0] == zero)
continue;
282 prVisit = T(tput[ceidx] / tput[ctidx]);
289 if (prOt < zero)
return zero;
290 return prOt > one ? one : prOt;
314 for (std::size_t i = 0; i < prev.
rows(); ++i)
315 for (std::size_t j = 0; j < prev.
cols(); ++j) {
316 T m = T(prev(i, j) + (raw(i, j) - prev(i, j)) / kk);
325std::vector<T> ln_polyak_vec(
const std::vector<T>& prev,
const std::vector<T>& raw,
long k) {
326 std::vector<T> out(prev.size());
327 const T kk = num_traits<T>::from_int(k);
328 for (std::size_t i = 0; i < prev.size(); ++i) {
329 T m = T(prev[i] + (raw[i] - prev[i]) / kk);
330 if (std::isnan(num_traits<T>::to_double(m))) m = raw[i];
331 if (std::isnan(num_traits<T>::to_double(m))) m = prev[i];
371 : cfg_(cfg), omega_(cfg.relax_burnin), err_(1, 0.0) {}
380 const std::vector<double>& layer_jobs,
const std::vector<T>& servt,
381 const std::vector<T>& residt) {
382 if (it < 1)
return false;
383 if (err_.size() <= std::size_t(it)) err_.resize(std::size_t(it) + 1, 0.0);
387 if (it >= cfg_.burnin)
388 omega_ = std::min(1.0, cfg_.a0 / std::pow(std::max(1.0,
double(it - cfg_.burnin + 1)),
391 if (it <= cfg_.burnin) {
392 err_[std::size_t(it)] = std::numeric_limits<double>::infinity();
395 if (start_ < 0) start_ = it;
397 const long k = k_ + 1;
402 for (std::size_t e = 0; e < latest.size() && e < avg_.size(); ++e) {
404 avg_[e].QN = detail::ln_polyak(prev.
QN, latest[e].QN, k);
405 avg_[e].UN = detail::ln_polyak(prev.
UN, latest[e].UN, k);
406 avg_[e].RN = detail::ln_polyak(prev.
RN, latest[e].RN, k);
407 avg_[e].TN = detail::ln_polyak(prev.
TN, latest[e].TN, k);
408 avg_[e].WN = detail::ln_polyak(prev.
WN, latest[e].WN, k);
409 const double N = e < layer_jobs.size() ? layer_jobs[e] : 0.0;
412 for (std::size_t i = 0; i < prev.
QN.rows(); ++i)
413 for (std::size_t j = 0; j < prev.
QN.cols(); ++j) {
416 if (!std::isnan(d) && d > dmax) dmax = d;
426 residt_avg_ = residt;
428 servt_avg_ = detail::ln_polyak_vec(servt_avg_, servt, k);
429 residt_avg_ = detail::ln_polyak_vec(residt_avg_, residt, k);
432 err_[std::size_t(it)] = err;
433 if (k <= cfg_.conseq)
return false;
434 for (
long w = 0; w < cfg_.conseq; ++w)
435 if (!(err_[std::size_t(it - w)] < cfg_.iter_tol))
return false;
452 std::vector<double> err_;
455 std::vector<LayerResult<T>> avg_;
456 std::vector<T> servt_avg_, residt_avg_;
LnStochController(const LnStochConfig &cfg)
const std::vector< double > & iteration_error() const
Per-iteration drift, 1-based; slot 0 is unused.
double relax_omega() const
const std::vector< T > & averaged_residt() const
long averaging_start() const
Iteration at which averaging started, -1 while still in burn-in.
bool update(int it, const std::vector< LayerResult< T > > &latest, const std::vector< double > &layer_jobs, const std::vector< T > &servt, const std::vector< T > &residt)
Fold iteration it in and say whether the iteration may stop.
long averaging_count() const
const std::vector< LayerResult< T > > & averaged_results() const
const std::vector< T > & averaged_servt() const
LnSensTable< T > get_sensitivity_table(const sens::SensOptions &sopt)
Port of @SolverLN/getSensitivityTable: solve the ensemble, then concatenate each layer solver's own t...
LnTranSolution get_tran_avg()
Port of @SolverLN/getTranAvg: the block-diagonal aggregate transient.
std::vector< LnCdf > get_cdf_respt()
Port of @SolverLN/getCdfRespT: the per-entry response-time distribution.
The exception types the port throws.
Standalone LQN routines that SolverLN needs but does not contain.
LayeredNetworkStruct, the flattened description of a layered queueing network.
Dense matrix and non-owning view.
CallType
Call kinds, with the values of MATLAB CallType.
T lqn_overtake_prob(const T &S1, const T &S2, const T &lambda, double mult)
Probability that an arrival at an entry finds the server in phase 2.
LnTranSolution lqn_tran_avg(SolverLN< T > &solver)
@SolverLN/getTranAvg.m: the block-diagonal aggregate transient over the LQN layers,...
T lqn_overtake_prob_markov(const LqnStruct< T > &lqn, const std::vector< T > &servt, const std::vector< T > &callresidt, const std::vector< T > &tput, std::size_t eidx, const T &xj)
Overtaking probability at a server entry, through the LQNS phased-server chain rather than the reduce...
T lqn_overtake_markov(const Matrix< T > &clientPhases, const T &prVisit, const T &xj, const std::vector< T > &y_aj)
Overtaking probability from the LQNS phased-server Markov chain.
OvertakeCtmcState< T > lqn_overtake_ctmc(const T &S1, const T &S2, const T &lambda)
Stationary law of the reduced overtaking chain of overtake_prob.m.
LnSensTable< T > lqn_sensitivity_table(SolverLN< T > &solver, const sens::SensOptions &opt)
@SolverLN/getSensitivityTable.m: solve the ensemble, then concatenate each LAYER solver's own sensiti...
std::vector< LnCdf > lqn_cdf_respt(SolverLN< T > &solver)
@SolverLN/getCdfRespT.m: the per-entry response-time distribution, which only the moment3 method prod...
Number-type abstraction for the templated API port.
SolverLN: layered decomposition of a layered queueing network.
The MATLAB GlobalConstants, as reported by lineStart at its defaults.
static constexpr double FineTol
Per-layer results of one iteration, the [QN,UN,RN,TN,AN,WN] of getAvg.
getSensitivityTable of the ensemble: the layer tables under a Layer column.
options.config.stochiter_* of SolverOptions.m, with its defaults.
The layered transient: one block per layer, plus how it was produced.
Stationary law of the three-state overtaking chain, in its own order.
The name-value contract of getSensitivityTable.