![]() |
LINE Solver (C++)
Templated C++ port of the LINE queueing solver
|
Collapse every chain onto one class, port of ModelAdapter.aggregateChains. More...
#include <cmath>#include <cstddef>#include <string>#include <vector>#include "line/lang/dist_fitters.h"#include "line/lang/lang_types.h"#include "line/lang/qn/network_builder.h"#include "line/lang/qn/network_struct.h"#include "line/num/number.h"#include "line/solvers/mva/sn_chain.h"#include "line/util/error.h"#include "line/util/matrix.h"Go to the source code of this file.
Classes | |
| struct | line::api::ChainAggregationDeagg< T > |
| Everything needed to map a chain-level result back onto the classes. More... | |
| struct | line::api::ChainAggregationResult< T > |
| What sn_aggregate_chains returns. More... | |
Namespaces | |
| namespace | line |
| namespace | line::api |
Functions | |
| template<class T> | |
| ChainAggregationResult< T > | line::api::sn_aggregate_chains (const qn::NetworkStruct< T > &sn, const std::string &suffix=std::string()) |
| Collapse every chain onto one class, port of ModelAdapter.aggregateChains. | |
Collapse every chain onto one class, port of ModelAdapter.aggregateChains.
The reference is matlab/src/io/@@ModelAdapter/aggregateChains.m, reached from the model as model.aggregateChains(suffix); the JAR twin is ModelAdapter.aggregateChains and the python one ModelAdapter.aggregate_chains.
A CHAIN is a set of classes that can switch into one another, so the jobs of a chain are one circulating population however many class labels they wear on the way round. The transform replaces each chain by a SINGLE class carrying that population (or that total arrival rate, for an open chain), the chain's service demands and the chain-level routing. The result has no class switching at all, which is what makes it worth building: the state space of a multiclass model is the product over classes, and a model whose K classes form C < K chains solves in the state space of C.
WHAT IS PRESERVED, and it is chain-level and not class-level. Per chain: the population, the arrival rate, the per-station service DEMAND, and the routing between stations. Per class: nothing – that is the point, and it is what the alpha matrix returned here exists to undo. alpha(i,r) is class r's share of its chain's visits at station i, so a chain result at station i splits back over its classes in those proportions (see the reference's sn_deaggregate_chain_results, which consumes exactly Lchain, STchain, Vchain and alpha from the block returned here).
THE AGGREGATE IS EXACT ON A PRODUCT-FORM MODEL and an approximation otherwise, for one reason: the chain's service law at a station is refitted from the chain MEAN and SCV, mixing the classes' own laws. Where the model is product form the station is insensitive to everything past the mean and the refit costs nothing; where it is not, the refit is a two-moment approximation. The fit follows the reference's ladder exactly –
|SCV - 1| < FineTol Exp(1/ST) the insensitive case SCV < FineTol Det(ST) the degenerate lower end SCV < 1 Erlang(round(1/SCV)) the reference rounds, not ceils SCV > 1 HyperExp fitted to (ST, SCV)
– and the Erlang order is round(1/SCV) here because that is what aggregateChains.m writes; Erlang.fitMeanAndSCV ceils instead, and the two disagree at every SCV that is not the reciprocal of an integer. The first three rungs are exact-arithmetic clean; the HyperExp one needs a square root, so at a non-transcendental T it is FITTED IN DOUBLE and its three parameters lifted back, which costs nothing that was exact to begin with – the rung is a two-moment approximation of a mixture and carries no exactness claim, while the routing, the demands, the populations and the aggregation around it stay exact.
THE ROUTING IS READ OFF rt, NOT rtnodes, and that is load bearing. rt is the stochastic complement of the routing over the STATEFUL nodes, so the nodes the aggregate does not carry – Routers, and the ClassSwitch nodes that link synthesizes for exactly the switching this transform is eliminating – are already folded away, along with the Sink, which is not stateful in this port nor in the reference. That last one is why an open chain still routes: a Q -> Sink -> Source path in the original appears in rt as Q -> Source (the sink closure is inside the complement), so the aggregate's open chain returns to the Source and the refresh re-derives its own closure. Measured on a two-class open chain in MATLAB 2026-08-15: the aggregate routes Source -> Q1 -> Q2 -> Source and reports the exact per-station utilizations.
ONE DELIBERATE DEPARTURE FROM MATLAB. The reference line_warnings and SKIPS a node kind it does not carry (anything that is not Source, Sink, Queue, Delay, Router or ClassSwitch), which silently returns a model with a node missing and its routing rewired around the hole. This port refuses by name instead, as the rest of the port does: a model the transform cannot carry is a diagnostic, not a quietly different model. A ClassSwitch is still dropped rather than refused, since eliminating class switching is the transform's whole purpose and the reference drops it on both of its branches.
ARITHMETIC: rational in the routing and the visits; the HyperExp fit is transcendental (hyperexp_fit_mean_scv) and drops to a double fit at an exact T, as network_reader.h already does for every T.
Definition in file sn_aggregate_chains.h.