![]() |
LINE Solver (C++)
Templated C++ port of the LINE queueing solver
|
The two CLOSED-FORM environment limits: SolverENV.solveEnvLimit, reached by options.method in {avg, dec}. More...
#include <algorithm>#include <cmath>#include <cstddef>#include <functional>#include <limits>#include <string>#include <type_traits>#include <vector>#include "line/api/sn/sn_node_metrics.h"#include "line/lang/qn/environment.h"#include "line/lang/qn/network_struct.h"#include "line/num/number.h"#include "line/solvers/cache_metrics.h"#include "line/solvers/ctmc/solver_ctmc_analyzer.h"#include "line/solvers/env/solver_env.h"#include "line/solvers/fluid/fluid_runner.h"#include "line/solvers/fluid/solver_fluid.h"#include "line/solvers/mva/solver_mva_runner.h"#include "line/solvers/nc/solver_nc_runner.h"#include "line/util/error.h"#include "line/util/expm.h"#include "line/util/linalg.h"#include "line/util/matrix.h"Go to the source code of this file.
Classes | |
| struct | line::env::EnvStageAvg< T > |
| What one stage solve reports back to the limits. More... | |
| struct | line::env::EnvLimitSolution |
| What a limit solve reports. More... | |
| class | line::env::SolverEnvLimit< T > |
| The limit solver. More... | |
| class | line::env::SolverEnvLimit< T >::CacheAccum |
| The RATE accumulator behind the cache blend, SolverENV.accumCacheMetric. More... | |
Namespaces | |
| namespace | line |
| Conservation laws of a layered queueing network, enumerated from its structure. | |
| namespace | line::env |
Typedefs | |
| template<class T> | |
| using | line::env::EnvStageAvgFn = std::function<EnvStageAvg<T>(const qn::NetworkStruct<T>&)> |
| The stage solver, as a callable: the C++ spelling of the MATLAB function handle SolverENV(renv, @(m) SolverX(m, opts)) passes. | |
Functions | |
| template<class T> | |
| EnvLimitSolution | line::env::solver_env_limit (Environment< T > &e, const EnvOptions &o) |
| solveEnvLimit on the original stages. | |
| template<class T> | |
| void | line::env::stage_occupation (Environment< T > &e, const EnvOptions &o, std::vector< double > &occ, std::vector< double > &w) |
| getStageOccupation: method occupation's per-stage expected occupation and blend weight. | |
| template<class T> | |
| EnvLimitSolution | line::env::solver_env_limit (Environment< T > &e, const EnvOptions &o, EnvStageAvgFn< T > stage_fn) |
| solveEnvLimit with the CALLING solver running every stage. | |
The two CLOSED-FORM environment limits: SolverENV.solveEnvLimit, reached by options.method in {avg, dec}.
Both replace the fixed point with a single reading, and each is exact at one end of the time-scale separation between the environment and the network:
avg, the FAST-environment limit. The environment switches so much faster than the network responds that the network only ever sees the AVERAGE of the modulated rates. One rate-averaged model is built – every station-class rate that varies across stages replaced by its probEnv-weighted mean, as an exponential – and solved once, in steady state. Exact as the stage-switch rate goes to infinity.
dec, the SLOW-environment limit, i.e. quasi-stationary decomposition. The environment stays in a stage long enough for the network to reach that stage's own steady state, so each stage is solved independently and the metrics are averaged with weights probEnv. Exact as the stage-switch rate goes to zero.
NEITHER CARRIES ANYTHING ACROSS A SWITCH, which is what makes them closed form and also what they give up: no entry state, no reset policy, no transient. A reset policy declared on an arc is therefore inert here, exactly as it is in the reference, whose solveEnvLimit never reads resetFun.
WHAT IS AVERAGED, AND WHAT IS LEFT ALONE (buildRateAveragedModel). Only a Source, a Queue or a Delay carries a rate to average. A station-class pair that is disabled in any stage, or whose rate does not actually vary across them, keeps its ORIGINAL distribution rather than being rewritten as an exponential of its own mean – so a non-modulated Erlang stays an Erlang, and the base model is preserved exactly outside the modulated rates.
A STAGE HOLDING A CACHE is solved, not refused. The hit, miss and delayed fractions each stage reports are blended the way the reference's accumCacheMetric does after 2026-09-13: as RATES weighted by that stage's per-class arrival INTO THE CACHE, normalised once at the end. Rates and not ratios, because a stage's hit ratio is conditional on arriving in that stage, so a probEnv-weighted mean of ratios disagrees with the Sink-row hit throughput – sum_e p_e*lambda_e*h_e – in the SAME node table. It is also the convention aggregate_cache_meanfield and the statevec cache_blend already hold, each with a comment saying so.
WHICH SOLVER RUNS A STAGE. The reference hands SolverENV a FACTORY and calls it for every stage, so "the stage solver is the calling solver" is literally true there. The second constructor here takes an EnvStageAvgFn, the same thing as a callable, and the no-factory path runs stage_solver: the fluid analyzer, with ctmc the enumerated chain at stage_cutoff (an ensemble built on SolverCTMC stages), with mva / nc the MVA or NC runner at stage_mva / stage_nc (an ensemble built on SolverMVA / SolverNC stages). The callable is what map_env_approx uses, so that the CALLING solver's own options reach every stage.
occupation, SolverENV.solveEnvOccupation, is the dec twin for an environment that need not be ergodic. Each stage is solved in steady state as dec solves it, but weighted by the time the environment is expected to spend there from the initial stage law EnvOptions::env_init (default: stage 1), so the environment may be ABSORBING. Over a finite horizon [0,T] the occupation is int_0^T pi0 exp(A t) dt and the weights occupation/T, the time-averaged stage law, which needs exponential arcs to integrate. With T = Inf and absorbing stages the occupation is pi0_t (-A_tt)^-1 over the transient stages and the weights are the absorption probabilities; with no absorbing stage the weights are probEnv, as dec. The T = Inf cases read the embedded chain and the mean holding times only, so they hold for a semi-Markov environment too. A stage of weight zero is not solved.
occupation is also the one limit that takes LAYERED stages, as the reference does: each is run by its own SolverLN (EnvOptions::lqn) to the fixed point and read over its LQN ELEMENTS (SolverLN.getAvg, an nidx x 1 table), not over its layers: a zero-capacity stage shares the elements with the others while its layers do not (ln_zero_capacity drops the layers only a trapped reference task reaches). The stages must then be all layered with the same elements, which Environment::set_lqn_stage enforces. The reference solves stages sharing one model object once; a stage here holds its model by value, so there is no identity to share and each is solved.
Definition in file solver_env_limit.h.