solvers.MAM
- solver_mam_ldqbd(sn, options)
SOLVER_MAM_LDQBD Solve single-class Delay/Queue networks using LD-QBD
Uses a Level-Dependent Quasi-Birth-Death (LD-QBD) process to compute performance metrics for single-class networks with one infinite-server station and one FCFS Queue (multi-server, PH service supported).
Exactness: exact for exponential service at any number of servers, and for PH service at any number of servers. The multiserver PH chain is built by LDQBD_MPHC, whose level coordinate is the MULTISET of the phases the min(n,c) busy servers sit in; the collapsed single-phase approximation this solver used until 2026-08-18 (one PH process run at min(n,c) times its speed, ~1e-2 relative against SolverCTMC) is gone.
- Two regimes are handled:
- CLOSED: one Delay (INF) + one Queue, finite population N. Level n = jobs at
the queue (0 <= n <= N); arrival rate from the delay is (N-n)*lambda.
- OPEN: one Source (EXT) + one Queue, open class (Poisson arrivals). Level
n = jobs at the queue, truncated at M_trunc; arrival rate is the constant external rate lambda. M_trunc is taken from options.cutoff or chosen so the truncated tail probability is negligible.
Both regimes share the same block-tridiagonal generator, differing only in the per-level arrival rate and the top level. Service is min(n,c)*mu (exact M/M/c boundary) or its PH generalisation.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_mna_applicable(sn)
[TF, REASON] = MAM_MNA_APPLICABLE(SN)
Can the ‘mna’ method of SolverMAM answer this model? REASON is ‘’ when it can and otherwise names what does not fit.
These are the CORRECTNESS rules of SOLVER_MNA_OPEN and SOLVER_MNA_CLOSED, the ones a by-name request must clear; the default-method chooser in solver_mam_analyzer adds its own PREFERENCES on top (a multiserver PS station, a multiclass FCFS station), which are about accuracy and cost and do not refuse a caller who asks for mna by name.
no mixed open/closed model (the two analyzers are one each);
round-robin routing in open models only (the deterministic split is carried by the open traffic equations; the closed sweep has none);
no self-looping closed class (no inter-station flow to decompose);
no fork-join (both analyzers raise ‘Fork nodes not supported yet’);
a closed model with one class per chain: SOLVER_MNA_CLOSED bisects over CLASSES but stores the throughput in the CHAIN-indexed lambda and renormalizes chain c with the class-indexed sn.njobs(c), which is only the same quantity when the two indexings coincide;
INF, PS, FCFS and EXT stations only: a station under any other discipline is never updated by the flow sweep and keeps a zero queue length, which the table then reports as the answer.
ONE PREDICATE, THREE CALLERS: SolverMAM.supportsModelMethod, the ‘mna’ arm of solver_mam_analyzer, and its default-method chooser (mnaApplies).
- mam_retrial_applicable(sn)
[TF, REASON] = MAM_RETRIAL_APPLICABLE(SN)
Can the ‘retrial’ method of SolverMAM answer this model? REASON is ‘’ when it can, and otherwise names what is missing.
THE RULE IS A “MUST BE PRESENT” ONE, which is why it cannot live in a feature set: a SolverFeatureSet says “I accept this construct”, so it can refuse a model for HAVING something and never for LACKING it. solver_mam_retrial needs an impatience configuration to analyze – either the BMAP/PH/N/N bufferless retrial topology of Dudin et al. (Mathematics 13(9), 2025) or a reneging patience law for the MAP/M/s+G analysis of Gursoy, Mehr and Akar – and a model carrying neither is not a smaller retrial model, it is a different one.
ONE PREDICATE, TWO CALLERS: SolverMAM.supportsModelMethod asks it so the method is not offered by findSolver or SolverAUTO on a model it cannot answer, and solver_mam_analyzer asks it so a caller naming ‘retrial’ by hand gets the identical sentence.
- solver_mam_ldqbd_avg(ld, piflat, levelOf)
SOLVER_MAM_LDQBD_AVG Map an LD-QBD state distribution to mean metrics.
[QN,UN,RN,TN] = SOLVER_MAM_LDQBD_AVG(LD, PIFLAT, LEVELOF) takes a probability vector PIFLAT over the flat LD-QBD state space (with per-state level LEVELOF from solver_mam_ldqbd_flatten) and returns the per-(station,class) mean queue length, utilization, response time and throughput for the single-class Delay/Queue (closed) or Source/Queue (open) model described by LD.
Mirrors the steady-state metric formulas in solver_mam_ldqbd, applied to an arbitrary (e.g. transient-averaged) distribution rather than the stationary one. Used by the SolverENV state-vector analyzer’s MAM backend.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_bgchain(sn, options)
[QN,UN,RN,TN,CN,XN,TOTITER] = SOLVER_MAM_BGCHAIN(SN, OPTIONS)
Mixed-network solver that treats the CLOSED classes as a background modulating chain and the OPEN classes as matrix-analytic queues driven by it.
The closed population vector of a mixed network is a finite continuous-time Markov chain in its own right: it is the only part of the model whose state space is bounded. This method solves it exactly (given the mean open occupancy) and hands the open classes a station-local Markovian ENVIRONMENT read off that chain, so each open station becomes a level-dependent QBD whose phase carries the number of closed jobs competing for its server. The two halves meet at a fixed point on the mean open occupancy: the background chain is built from it, and the QBDs recompute it.
- background chain closed population vector over the stations the
closed classes visit, with the open classes present only through their mean occupancy (MAM_BGCHAIN_CTMC)
- environment the chain lumped onto the closed occupancy of one
station (MAM_BGCHAIN_ENV)
- open station MAP/PH/c queue modulated by that environment, solved
as a level-dependent QBD (MAM_BGCHAIN_STATION)
fixed point the mean open occupancy feeds step 1 and closes
A PURELY CLOSED MODEL IS THE DEGENERATE CASE OF THE SAME CONSTRUCTION, not a separate algorithm. With no open class steps 2-4 have nothing to do: CSHARE never leaves its initial min(e,c), which is exactly the capacity the closed jobs hold when no open work competes for it, so step 1 alone answers and it answers with the EXACT closed CTMC at chain granularity. The same limit is reached from the mixed side by letting the open arrival rate go to zero, and it agrees with exact MVA to the O(lambda) of that perturbation. What it costs is the state space below, which is why SOLVER_MAM_ANALYZER sizes the chain before choosing this method as the closed default.
TAGGED-CLASS ITERATION. Step 1 is a population process of dimension (number of closed chains) x (number of stations), so its state space is exponential in the number of closed chains. With R > 1 closed chains the method keeps ONE chain free at a time: the tagged chain r is carried exactly, the other R-1 are replaced by flow-equivalent aggregate classes whose population is their total and whose service time and routing at each station are their throughput-weighted means (Chandy-Herzog-Woo aggregation). Every chain takes its turn as the tagged one and reads its own metrics off the chain it is exact in; the open-class results are averaged over the passes, all of which estimate the same quantity.
HOW MUCH TO AGGREGATE is options.config.bgaggr, the number G of aggregate classes; the background chain then carries 1 + G classes.
- G = 1 the classic tagged/aggregate pair, and the default: the chain
stays two-class whatever R is, which is the cheapest option
1 < G < R-1 the untagged chains are split into G groups G >= R-1 nothing is aggregated. Every closed chain is a class of its
own, the background chain is EXACT in the closed classes, and ONE pass answers for all of them – the tagged loop would otherwise solve the same chain R times over. Passing R reaches this, so asking for “no aggregation” needs no magic value
What it costs is the state space, the product over the 1 + G classes of nchoosek(N_b + Mc - 1, Mc - 1), bounded by options.config.bgstates_max.
WHICH CHAINS SHARE A GROUP is decided by similarity of per-station SERVICE DEMAND, in MAM_BGCHAIN_GROUPS. An aggregate carries the flow-weighted mean of its members’ service times and routing, so it is exact when they place the same demand at every station and distorts in proportion to how far apart they are. Grouping the demand-similar chains together is what keeps the aggregation where it is harmless and away from the chains it would misrepresent.
EXACTNESS, as measured against SolverCTMC and exact MVA on mixed models of two to four stations:
- PS or INF, ANY service law (exponential, Erlang, HyperExp, Coxian), any
- number of servers, Poisson or MAP arrivals, one to four closed chains
agreement to 4-5 significant digits (<= 0.06% on every queue length), where dec.source is 10-24% out
FCFS, class-INDEPENDENT service rates same, 5 significant digits FCFS, class-DEPENDENT service rates the closed queue lengths stay
within ~1%, the open queue length reads 14-20% low
PS is INSENSITIVE to the service law beyond its mean, and the method honours that rather than approximating it: at a PS station the open service is replaced by the exponential of the same mean before the QBD is built. That is not a shortcut. This QBD tracks ONE service phase for the whole station, so carrying the phase-type there makes the open queue length inherit the SCV-sensitivity of an M/PH/1 FCFS queue – measured, a HyperExp of SCV 4 read 21% high where the exact answer is the exponential one to five digits.
The remaining residual is FCFS-only and is one named approximation. A station serving classes at DIFFERENT rates under FCFS is served here in random order – the server is held by an open job with probability k/(k+e) – which is exact under PS but loses the head-of-line order FCFS actually imposes, and with it part of the variability of the wait. At an FCFS station the service law IS carried, collapsed into one phase-type process scaled by the share, the same collapse SOLVER_MAM_LDQBD documents. The background chain reads only the MEAN closed service time, which is exact under PS by the same insensitivity and a first-moment surrogate under FCFS. The level space is truncated at options.cutoff, which costs nothing at the tail probabilities the default chooses.
OPTIONS.CONFIG.BGENV selects what the QBD carries on its environment axis:
- ‘lump’ (default)the background chain aggregated onto the number of closed
jobs THIS station holds (MAM_BGCHAIN_ENV)
- ‘full’the background chain itself (MAM_BGCHAIN_ENVFULL), at a
phase count bounded by OPTIONS.CONFIG.QBDPHASES_MAX
‘full’ IS AN ORACLE AND RETURNS THE SAME NUMBERS. The background chain is product-form by construction, so the lump is Norton-exact rather than approximate; the two agree to 6.7e-16 on every model measured. It is MATLAB ONLY, and it is here to re-check that property, not to improve on it. See MAM_BGCHAIN_ENVFULL for the argument and the measurements.
THE NAME IS ‘full’, NOT ‘exact’, AND DELIBERATELY SO. It says what the QBD carries on its environment axis, and NOTHING about the answer: this method stays approximate under it, for the reasons listed above – the mean-only closed service time, the mean-field coupling to the other stations, the FCFS random-order surrogate, the multiserver collapse, the level truncation and, at G < R-1, the Chandy-Herzog-Woo aggregation. Measured with ‘full’ selected, a two-chain FCFS model with class-dependent rates is still 83% out on a queue length against SolverCTMC.
See also
SOLVER_MAM_ANALYZER,MAM_BGCHAIN_CTMC,MAM_BGCHAIN_ENV,MAM_BGCHAIN_ENVFULL,MAM_BGCHAIN_STATION.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_bgchain_station(Da0, Da1, alpha_s, T, A, esup, nservers, gref, Kmax, options)
RES = MAM_BGCHAIN_STATION(DA0, DA1, ALPHA_S, T, A, ESUP, NSERVERS, GREF, KMAX, OPTIONS)
Solves the open classes of one station as a level-dependent QBD MODULATED by the background chain: level = number of open jobs held by the station, phase = (arrival MAP phase, environment state, service phase).
The station is an MAP/PH/c queue whose server is shared with the closed jobs. With k open and e closed jobs present the open aggregate completes at rate
phi(k,e) = min(k+e, c) * k/(k+e)
times the phase-type completion rate of one busy server, i.e. the open class receives the share k/(k+e) of the min(k+e,c) busy servers. The dependence on k is what makes the QBD level-dependent; the dependence on e is what makes it modulated. The c parallel servers are collapsed into a single phase-type process scaled by phi, which is exact for exponential service at any c and for phase-type service at c = 1, and approximates the multiset of in-service phases otherwise – the same collapse SOLVER_MAM_LDQBD documents.
The environment is level-dependent too, and for the same reason. A lumped transition that LOWERS the closed occupancy of this station is a closed completion here, so it carries the closed share min(e+k,c)*e/(e+k) of the server; the background chain built it at the averaged share GREF, and level k rescales it by the ratio of the two. A transition that RAISES the occupancy is an arrival from elsewhere and is left alone. Without this the closed jobs would drain at their mean-field rate however long the open queue is, and the positive correlation between the two occupancies – the very thing a congested station produces – would be lost.
The level space is truncated at KMAX. An arrival at the top level is lost but still advances the arrival phase, so the arrival process keeps its exact marginal and autocorrelation and only the queue tail is cut; RES.ploss reports the probability mass sitting at the top level.
- Inputs
DA0, DA1 (ma x ma) aggregate open arrival MAP at the station ALPHA_S (1 x ms) initial vector of the open service phase-type law T (ms x ms) subgenerator of the open service phase-type law A (me x me) environment generator, either the LUMPED chain from
MAM_BGCHAIN_ENV or the unlumped one from MAM_BGCHAIN_ENVFULL; this function only needs to know how many closed jobs each of its states holds here
- ESUP (1 x me) closed jobs each environment state stands for. It is
strictly increasing for the lumped chain and REPEATS for the unlumped one, where many chain states hold the same number of jobs at this station
NSERVERS scalar number of servers GREF (1 x me) closed capacity share A was built at KMAX scalar truncation level of the open queue
- Output (struct RES)
.QLen mean number of open jobs at the station .Util mean fraction of the servers held by open jobs .Tput open departure rate .ploss stationary probability of the truncation level .plev (1 x KMAX+1) level distribution of the open queue .penv (1 x ne) stationary probability of holding each DISTINCT number of
closed jobs; states that hold the same number are folded together, which is the identity for the lumped environment
- .cshare (1 x ne) E[min(e+k,c)*e/(e+k) | e], the closed capacity share the
background chain reads back
- .esup (1 x ne) the distinct entries of ESUP, ascending: the index of
.penv and .cshare
See also
SOLVER_MAM_BGCHAIN,MAM_BGCHAIN_ENV,LDQBD_R,LDQBD_PI.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_bgchain_groups(D, G)
GRP = MAM_BGCHAIN_GROUPS(D, G)
Groups the columns of D into G clusters by similarity of their per-station SERVICE DEMAND, for the aggregation SOLVER_MAM_BGCHAIN applies to the closed chains it is not carrying exactly.
- Inputs
- D (Mc x n) one column per chain; D(i,c) is the demand chain c places on
station i (visits times mean service time, Lchain)
G scalar number of groups wanted, clamped to [1, n]
- Output
GRP (1 x n) group index in 1..G of each chain
WHY DEMAND IS THE RIGHT CRITERION. The aggregate that replaces a group carries the flow-weighted mean of its members’ service times and routing, so the group aggregates EXACTLY when its members place the same demand at every station and distorts both quantities in proportion to how far apart they are. The distance is therefore the symmetric relative L1 gap between the demand vectors,
dist(a,b) = sum_i |D(i,a) - D(i,b)| / ((sum_i D(i,a) + sum_i D(i,b))/2),
which is scale-relative rather than absolute: it separates two chains whose demand PROFILE across the stations differs and two chains whose profile agrees but whose magnitude does not, and being dimensionless it groups a model the same way whatever its time unit.
WHY COMPLETE LINKAGE. The clustering is agglomerative from singletons, merging at each step the pair of clusters whose WORST member-to-member distance is smallest. The aggregation error inside a group is driven by its worst mismatch and not by its average one, so complete linkage is the criterion that bounds what the aggregation actually costs; average or single linkage would let one distant chain ride along inside an otherwise tight group.
DETERMINISM. Ties are broken by the lexicographically smallest pair of cluster indices and the groups are relabelled by their smallest member, so the same D gives the same grouping in MATLAB, the JAR, Python and C++.
See also
SOLVER_MAM_BGCHAIN,MAM_BGCHAIN_CTMC.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_bgchain_ctmc(Nb, STb, Pb, sched, nservers, cshare, supp, options)
BG = MAM_BGCHAIN_CTMC(NB, STB, PB, SCHED, NSERVERS, CSHARE, SUPP, OPTIONS)
Builds and solves the BACKGROUND MODULATING CHAIN of a mixed network: the continuous-time Markov chain of the closed-class population vector, with the open classes present only through their mean occupancy QOPEN.
The chain carries B = 1 or 2 background classes. B = 1 is the single closed class of the model; B = 2 is the tagged/aggregate pair built by SOLVER_MAM_BGCHAIN when the model has several closed chains (class 1 is the tagged chain r, class 2 the flow-equivalent aggregate of the other R-1).
- Inputs
NB (1 x B) population of each background class STB (Mc x B) mean service time of each background class per station PB cell(1 x B) (Mc x Mc) row-stochastic routing matrix per class SCHED (Mc x 1) SchedStrategy id of each station NSERVERS (Mc x 1) number of servers of each station CSHARE (Mc x Nmax+1) CSHARE(i,e+1) is the mean number of servers the
closed jobs of station i hold when they number e, i.e. E[min(e+k,c) * e/(e+k)] over the open occupancy k – min(e,c) when the station carries no open work
- SUPP (Mc x B) logical; SUPP(i,b) is true when station i is on the
route of background class b
OPTIONS solver options (reads options.config.bgstates_max)
SUPP IS NOT AN OPTIMIZATION. A closed chain that never visits a station cannot hold jobs there, and enumerating the population vector over the union of every chain’s stations puts probability on configurations it can never reach. Worse, those configurations ABSORB: the chain’s routing matrix has a zero row at a station it does not visit, which row-normalizes to a self-loop, so a job placed there never leaves. The generator turns reducible and the chain’s population conservation silently fails – measured, half the mass of a one-job chain sat in states it could not reach. Each class is therefore enumerated over ITS OWN stations only, which also shrinks the state space.
THE BLOCK OF ONE BACKGROUND CLASS IS STATE.SPACECLOSEDSINGLE, the same lattice primitive SOLVER_CTMC enumerates a closed population over, and the joint space is their cartesian product in class-major order (STATE.CARTESIAN’s convention: the first class is the slowest index). Only the SUPPORT differs – the columns are this class’s own stations rather than every station – so the enumeration order and the row count are the reference’s, not this file’s. Do not restore a private composition routine here: MAM_BGCHAIN_STATES predicts the row count of this same primitive in closed form, and a second enumeration would be free to drift from it.
A station holding n_{i,1} + n_{i,2} = e closed jobs serves background class b at rate
INF : n_{i,b} / STB(i,b) any other discipline: CSHARE(i,e+1) * (n_{i,b}/e) / STB(i,b)
The second line splits the capacity the closed jobs hold over the background classes in proportion to their counts, which is exact under PS and is the standard random-order surrogate under FCFS.
The open classes enter ONLY through CSHARE, which is what makes this a MODULATING chain rather than a joint model. Note that the exchanged quantity is the SHARE, already averaged over the open occupancy, and not the mean open occupancy itself: e/(e+k) is convex in k, so rebuilding the share from a mean k would bias the closed service rate downwards by Jensen’s inequality, and the closed throughput with it. SOLVER_MAM_BGCHAIN closes the loop by reading CSHARE back out of the modulated QBDs, where the same expectation is taken against the joint law of (level, environment).
- Outputs (struct BG)
.space (nstates x Mc x B) population vector of every state .pi (1 x nstates) stationary distribution .Q (nstates x nstates) generator .QLen (Mc x B) mean queue length per station per class .Tput (Mc x B) mean completion rate per station per class .Ubusy (Mc x B) mean fraction of the servers held by the class .totocc (nstates x Mc) total closed occupancy per station per state
See also
SOLVER_MAM_BGCHAIN,MAM_BGCHAIN_ENV.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_traffic_mmap(sn, DEP, config, fjSyncMap)
ARV = SOLVER_MAM_TRAFFIC_MMAP(SN, DEP, CONFIG, FJSYNCMAP) FJ-aware traffic solver extending solver_mam_traffic with mmap_max synchronization at join points.
DEP{i,r} is the departure process of class r from i in (D0,D1) format fjSyncMap is built by sn_build_fj_sync_map
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_fj(sn, options)
[QN,UN,RN,TN,CN,XN,TOTITER,PERCRESULTS] = SOLVER_MAM_FJ(SN, OPTIONS)
Analyze Fork-Join network using FJ_codes percentile approximation
- Parameters:
sn - Network structure from getStruct()
options - Solver options with FJ-specific fields –
percentiles: percentile levels (default [0.90, 0.95, 0.99])
fj_accuracy: C parameter for approximation (default 100)
fj_tmode: ‘NARE’ or ‘Sylves’ for T matrix computation (default ‘NARE’)
- Returns:
QN, UN, RN, TN, CN, XN - Standard LINE performance metrics matrices totiter - Number of iterations (N/A for FJ, returns 0) percResults - Percentile results structure with fields:
.RT - cell array of percentile structs per class .K - number of parallel queues .method - ‘qiu’
- Reference:
Z. Qiu, J.F. Pérez, and P. Harrison, “Beyond the Mean in Fork-Join Queues: Efficient Approximation for Response-Time Tails”, IFIP Performance 2015. Copyright 2015 Imperial College London
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_basic_mmap_closed(sn, options)
[QN,UN,RN,TN,CN,XN,TOTITER] = SOLVER_MAM_BASIC_MMAP_CLOSED(SN, OPTIONS)
Closed-network wrapper around solver_mam_basic_mmap_inner. Drives a per-class bisection on the surrogate arrival rate LAMBDA so that the inner solver’s queue lengths match the closed population SN.NJOBS. Mirrors the outer-loop structure of solver_mna_closed.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam(sn, options)
[Q,U,R,T,C,X,totiter] = SOLVER_MAM(QN, PH, OPTIONS)
- mam_is_renewal_map(D0, D1)
MAM_IS_RENEWAL_MAP True if the process (D0,D1) is a renewal process.
A renewal process embedded as a MAP has D1 = t * alpha (rank one): the phase entered after an event does not depend on the phase the process was in at that event, so successive interarrival times are independent. Equivalently, the process carries no autocorrelation and is fully described by its interevent marginal.
The test is on the algebraic structure alone, so it holds equally for a MAP, for a matrix-exponential (ME) and for a rational arrival process (RAP): an ME renewal stream satisfies it, a correlated MAP or RAP does not. That is what makes it the right guard in front of any closed form that reads only the marginal (a PH pair (pie, D0), a mean and an SCV), because such a form is exact for a renewal input and silently discards the correlation otherwise.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_svc_mixture(D_arr, pie_cell, D0_cell)
SVC = MAM_SVC_MIXTURE(D_ARR, PIE_CELL, D0_CELL)
Builds the arrival-weighted phase-type mixture of the per-class service laws at a station, as the service descriptor accepted by QSYS_MAPG1K and QSYS_MMAPG1K.
D_ARR - {D0, D_class1, …, D_classR} arrival MMAP PIE_CELL - per-class PH initial distributions D0_CELL - per-class PH subgenerators
The mixture is PH(alpha_mix, T_mix) with alpha_mix = [w_1*pie_1, …], T_mix = blkdiag(D0_1, …), and w_k = lambda_k/sum_j lambda_j the fraction of arrivals belonging to class k. It is therefore the service law of an arbitrary packet, and it reduces to the common law exactly (as a distribution) when every class shares one.
This is the same construction MAM_TRUNCATE_RENORM already applies when a station carries more than one class, and it is factored out here so that the exact finite-buffer branch and the truncate-and-renormalize fallback rest on identical service assumptions and remain comparable.
See also
QSYS_MMAPG1K,MAM_TRUNCATE_RENORM,MAM_DETECT_MMCK.
- solver_mna_closed(sn, options)
- solver_mam_ldqbd_flatten(ld)
SOLVER_MAM_LDQBD_FLATTEN Assemble a flat generator from LD-QBD blocks.
[Q, LEVELOF] = SOLVER_MAM_LDQBD_FLATTEN(LD) takes the block-tridiagonal representation LD (fields Q0/Q1/Q2/Nlev as produced by solver_mam_ldqbd) and returns the dense infinitesimal generator Q over the level/phase state space, together with LEVELOF(s) = the queue level of flat state s.
Level 0 is a single (empty-queue) state; levels 1..Nlev each carry nPhases phases (PH service) or a single state (exponential). Used by the SolverENV state-vector analyzer to propagate an entry distribution over the LD-QBD.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_basic_mmap(sn, options)
[QN,UN,RN,TN,CN,XN,TOTITER] = SOLVER_MAM_BASIC_MMAP(SN, OPTIONS)
Top-level dispatcher for the MAM/MMAP fork-join decomposition. Open networks call solver_mam_basic_mmap_inner directly with arrival rates derived from the source/refstat. Closed networks go through solver_mam_basic_mmap_closed, which wraps the inner algorithm in an MNA-style bisection on per-class throughput.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_transient_qbd_applicable(sn)
MAM_TRANSIENT_QBD_APPLICABLE True when transient analysis of the model should use the Laplace-domain transient QBD solver (solver_mam_transient_qbd) rather than the libQBD/expm fast path (solver_mam_ldqbd_transient).
The Laplace solver is selected for single-server open queues whose arrival is non-Poisson (correlated/PH-renewal MAP with >1 phase) or whose service is a correlated MAP (non-renewal), which the fast path cannot represent exactly. Poisson arrival with PH/exp service (M/M/1, M/PH/1) and M/M/c stay on the fast path.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_transient2(B, L, F, Lv, T, n, m, s)
- MAM_TRANSIENT2 Laplace-domain transient V(s,n,m) for a closed (finite) QBD.
V = MAM_TRANSIENT2(B, L, F, Lv, T, n, m, s) returns the Laplace transform (at complex argument s) of the transient transition-probability matrix from level n to level m of a finite piecewise level-dependent QBD with regime thresholds T; the top level is T(end).
Block cell arrays follow the same convention as MAM_TRANSIENT2_OPEN, with K = length(T)-1 regimes and Lv{K+1} the top boundary level.
Ported from the transient-QBD research code (Horvath et al. formulation).
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_traffic(sn, DEP, config)
ARV = SOLVER_MAM_ESTFLOWS(QN, DEP, CONFIG) DEP{i,r} is the departure process of class r from i in (D0,D1) format
- solver_mam_passage_time(sn, PH, options)
[RD] = SOLVER_MAM_PASSAGE_TIME(QN, PH, OPTIONS)
- solver_mam_map_bmap_1(mapArr, bmapSvc, varargin)
SOLVER_MAM_MAP_BMAP_1 Solve MAP/BMAP/1 queue using GI/M/1-type analysis.
- Solves a MAP/BMAP/1 queue where:
Arrivals follow a Markovian Arrival Process (MAP)
Service follows a Batch Markovian Arrival Process (BMAP) for batch service completions
Single server
- The queue is modeled as a GI/M/1-type Markov chain because:
MAP arrivals increase level by exactly 1
BMAP service can decrease level by 1, 2, 3, … (batch sizes)
- USAGE:
[QN, UN, RN, TN] = solver_mam_map_bmap_1(mapArr, bmapSvc) [QN, UN, RN, TN, piAgg, R] = solver_mam_map_bmap_1(mapArr, bmapSvc)
- INPUT:
mapArr - MAP object or cell array {C0, C1} for arrivals bmapSvc - BMAP object or cell array {D0, D1, D2, …} for batch service
- OUTPUT:
QN - Mean queue length E[N] UN - Server utilization rho RN - Mean response time E[R] TN - Throughput (arrival rate) piAgg - Aggregated stationary probabilities [pi0, pi1, piStar] R - R matrix from GI/M/1-type solution
- The GI/M/1-type structure for MAP/BMAP/1 is:
B1 A0 0 0 … B2 A1 A0 0 …
- Q = B3 A2 A1 A0 …
…
- Where:
A0 = C1 ⊗ I_ms (MAP arrival, level +1) A1 = C0 ⊗ I_ms + I_ma ⊗ D0 (phase changes, level 0) A_{k+1} = I_ma ⊗ D_k (batch size k service, level -k)
References
Riska, A., & Smirni, E. (2003). ETAQA: An Efficient Technique for the Analysis of QBD-Processes by Aggregation. Performance Evaluation.
- solver_mam_ldqbd_transient(sn, options)
SOLVER_MAM_LDQBD_TRANSIENT Transient analysis of open queues via standard QBD
Supports single-class open models (Source -> Queue -> Sink). For M/M/c: scalar QBD levels (any number of servers). For M/PH/1: m-phase QBD levels (single server only).
Infinite capacity: libQBD adaptive Taylor series. Finite capacity: direct matrix exponentiation (expm).
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_bmap_map_1(bmap, service_map, options)
SOLVER_MAM_BMAP_MAP_1 Solves a BMAP/MAP/1 queue using M/G/1 type analysis
[QN, UN, RN, TN, PI, G] = SOLVER_MAM_BMAP_MAP_1(BMAP, SERVICE_MAP) solves a BMAP/MAP/1 queue with batch Markovian arrival process BMAP and Markovian service process SERVICE_MAP. Uses M/G/1 type matrix-analytic methods via the ETAQA algorithm.
- Input:
- bmap - BMAP object or cell array {D0, D1, D2, …, DK} where
D0: transitions without arrivals Dk: transitions triggering batch size k arrivals (k >= 1)
- service_map - MAP object or cell array {S0, S1} where
S0: transitions without service completions S1: transitions triggering service completions
- options - (optional) Options structure with fields:
.nMoments: number of queue length moments to compute (default: 3)
- Output:
QN - Mean queue length (1st moment) UN - Server utilization RN - Mean response time TN - Throughput (arrival rate) pi - Aggregated stationary probability [pi0, pi1, pi2+pi3+…] G - G matrix (minimal nonnegative solution)
The BMAP/MAP/1 queue is modeled as an M/G/1 type Markov chain where: - Level corresponds to queue length - Phases correspond to combined (BMAP phase, MAP phase) states
- M/G/1 type structure (level can jump UP by any amount, DOWN by exactly 1):
A0 = I_ma ⊗ S1 (service completion, level -1) A1 = D0 ⊗ I_ms + I_ma ⊗ S0 (phase changes, level 0) A_{k+1} = D_k ⊗ I_ms (batch arrival size k, level +k)
References
Stathopoulos, Riska, Hua, Smirni: ETAQA algorithm
MAMSolver: www.cs.wm.edu/MAMSolver
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_basic(sn, options)
[Q,U,R,T,C,X] = SOLVER_MAM_BASIC(QN, PH, OPTIONS)
- mam_has_retrial(sn)
HASRETRIAL = MAM_HAS_RETRIAL(SN)
True when some station-class pair configures a retrial orbit (Queue.setRetrial / setOrbit), read off sn.retrialProc, which refreshStruct fills only for a configured delay that is not the Disabled placeholder. retrialProc is the ambiguity-free test: retrialType is 0 both for “none” and, in MATLAB, for an exponential delay (see CLAUDE.md).
Lifted beside MAM_HAS_RENEGING_PATIENCE so that SolverMAM.supportsModelMethod can ask whether ‘default’ and ‘dec.source’ have an orbit to route to the retrial analyzer: QSYS_IS_RETRIAL answers about the SHAPE that analyzer needs, not about whether an orbit exists, and a model with an orbit outside that shape fell through to solver_mam_basic, which reads no sn.retrial* field and answered with the refused jobs simply lost.
- mam_truncate_renorm(D_arr, pie_cell, D0_cell, capK)
MAM_TRUNCATE_RENORM Finite-buffer marginal for MMAP[K]/PH[K]/1/FCFS.
Solves the infinite-buffer MMAP[K]/PH[K]/1/FCFS queue via BUTools MMAPPH1FCFS, truncates the marginal queue length distribution at the buffer capacity capK, and renormalizes. For an M/M/1 input the renormalized distribution coincides exactly with the M/M/1/K marginal; for general MMAP/PH it is an ASTA-style approximation.
- INPUT
- D_arr - cell array passed as the MMAP argument to MMAPPH1FCFS,
e.g. {D0, D_class1, D_class2, …}
pie_cell - cell array of per-class PH initial distributions D0_cell - cell array of per-class PH subgenerators (T matrices) capK - buffer capacity (max number of jobs in system, integer >= 1)
- OUTPUT
meanQ - mean number of jobs in system (clipped to [0, capK]) lossProb - blocking probability = renormalized boundary mass p(N=capK)
(PASTA-exact for Poisson arrivals)
p_norm - renormalized truncated marginal as 1x(capK+1) row vector
Multi-class: MMAPPH1FCFS returns the joint (aggregate) marginal at the station; callers split per-class with the lambda_k/lambda_total fraction (FCFS Little’s-law decomposition), mirroring the convention used by the closed-class branch of solver_mam_basic.
Multi-server: callers must pre-scale the PH service mean by 1/c (the existing “surrogate-delay” trick); the helper then returns the marginal of the scaled M/M/1/K system, which is treated as an approximation of the M/M/c/K marginal. The c-1/c surrogate-delay term is omitted under finite cap because it would double-count physical jobs already bounded by capK.
See also
MMAPPH1FCFS.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_detect_mmck(sn, ist, K, mmapNode)
MAM_DETECT_MMCK Decide if station ist matches the M/M/c/K assumptions.
- Returns isMmck=true (and the shared service rate muRate) only when:
the aggregated arrival MMAP at the node is single-phase (i.e. Poisson superposition of class arrivals)
every active class has Exp service (procid==EXP) at the station
all active classes share the same exponential service rate
- INPUT
sn - network struct ist - station index K - number of classes mmapNode - cell array {D0, D1, D_c1, D_c2, …} for the aggregated
arrival MMAP at the node
- OUTPUT
isMmck - logical, true if the M/M/c/K closed form is exact muRate - the shared service rate (NaN when isMmck is false)
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_buffer_refusal(sn)
REASON = MAM_BUFFER_REFUSAL(SN)
Why no MAM method can answer a model whose finite buffer a CLOSED class can fill, or ‘’ when every binding buffer is reached by open classes only.
A MAM analyzer represents a finite buffer as a LOSS buffer: solver_mam_basic (default, dec.source, dec.poisson) solves an M/M/c/K or an MMAP[K]/G/1/K, and solver_mam (dec.mmap), solver_mam_basic_mmap (dec.source.mmap) and solver_mna_open (mna) truncate and renormalize the same way. That is the right model for an OPEN class, whose refused arrival is lost. A closed job that finds no room BLOCKS instead – LINE disables the upstream departure and holds the job where it is – and no MAM analyzer blocks: the loss formulas answer a different system, and the closed routes of ‘default’ (solver_mam_ldqbd, solver_mam_bgchain, solver_mna_closed) read no sn.cap or sn.classcap at all. So the pair is refused rather than answered.
ONE PREDICATE, TWO CALLERS: SolverMAM.supportsModelMethod reports it and solver_mam_analyzer raises it. Only a buffer that can BIND counts, which is what SN_GET_BUFFER_SIZE decides: refreshCapacity derives a finite classcap (the chain population) at every station of every closed model, so a plain finiteness test would refuse every closed model. A Cache builds its own capped retrieval queues and is exempt, as in sn_has_blocking.
- mam_reneging_applicable(sn)
[ISRENEGING, INFO] = MAM_RENEGING_APPLICABLE(SN)
Is this model the MAP/M/s+G shape the reneging analyzer of solver_mam_retrial solves (Gursoy, Mehr, Akar, “The MAP/M/s + G Call Center Model with Generally Distributed Patience Times”)? INFO carries the indices the analyzer reads (sourceIdx, queueIdx, classIdx, nServers, serviceRate) and, on a refusal, errorMsg naming the requirement that failed.
Requirements: - Open model, single class - Single Queue station with reneging patience configured - MAP/BMAP arrival at the Source - Exponential service at the Queue (single-phase) - FCFS scheduling
ONE PREDICATE, THREE CALLERS. solver_mam_retrial asks it to decide which of its two analyzers runs; MAM_RETRIAL_APPLICABLE asks it so that ‘retrial’ is offered only on a shape the analyzer answers; SolverMAM.supportsModelMethod asks it for ‘default’ on a reneging model, which the default routes here. Until 2026-09-05 it was a local function of solver_mam_retrial and the gate tested PRESENCE (MAM_HAS_RENEGING_PATIENCE) alone, so a two-queue reneging model was offered and then refused by the analyzer.
- mam_ldqbd_applicable(sn)
[TF, REASON, REGIME] = MAM_LDQBD_APPLICABLE(SN)
Can the ‘ldqbd’ method of SolverMAM answer this model? REASON is ‘’ when it can and otherwise names what does not fit; REGIME is ‘closed’ (one Delay and one FCFS Queue, finite population), ‘open’ (one Source and one FCFS Queue, Poisson arrivals) or ‘’ on a refusal.
THE RULE IS A SHAPE, which a feature set cannot state: SOLVER_MAM_LDQBD builds one level-dependent QBD whose level is the queue length of THE station, so it needs exactly two stations and a single class, and the open regime needs a Poisson stream because a MAP would need an arrival phase the chain does not carry. The stability test of the open regime is left to the analyzer: it depends on the load-dependent capacity factor the analyzer derives, not on the model shape.
ONE PREDICATE, FOUR CALLERS. SolverMAM.supportsModelMethod asks it for ‘ldqbd’, and for ‘default’ on a load-dependent model (the closed regime is the only MAM path that reads sn.lldscaling); solver_mam_analyzer asks it to route a single-class closed Delay+Queue to the exact chain; solver_mam_ldqbd asks it before building anything. Until 2026-09-05 each carried its own copy of the shape and the report offered ldqbd on models the analyzer then refused by name.
- mam_has_reneging_patience(sn)
ISRENEGING = MAM_HAS_RENEGING_PATIENCE(SN)
True when some station-class pair declares RENEGING impatience together with a patience distribution, i.e. when solver_mam_retrial has a MAP/M/s+G model to solve rather than a BMAP/PH/N/N retrial one.
Lifted out of solver_mam_analyzer.m so that SolverMAM.supportsModelMethod can ask the same question: the gate decides whether to OFFER the ‘retrial’ method and the analyzer decides whether to run it, and two copies of the test are how the two come to disagree.
- mam_bgchain_states(sn, options)
NSTATES = MAM_BGCHAIN_STATES(SN, OPTIONS)
Number of states of the background-chain CTMC that SOLVER_MAM_BGCHAIN would build on this model, WITHOUT building it. The size is what decides whether bgchain is affordable, and MAM_BGCHAIN_CTMC only discovers it after the partition is fixed, so the default-method chooser needs it up front.
The count mirrors the partition SOLVER_MAM_BGCHAIN uses: a pass carries the tagged closed chain as background class 1 and the demand-similar groups of the other closed chains as classes 2..1+G, each class enumerating the compositions of its population over the stations its members visit. Merging two chains onto the UNION of their supports can raise the count as easily as lower it, so the passes are enumerated rather than bounded, and the largest is returned: that is the one MAM_BGCHAIN_CTMC would refuse.
The per-class count below is the ROW COUNT OF STATE.SPACECLOSEDSINGLE(m, Nb), the primitive MAM_BGCHAIN_CTMC enumerates each block with, evaluated in closed form as nchoosek(Nb+m-1, m-1) so that the size can be compared without paying for the enumeration.
Returns 0 when bgchain does not apply to the model at all (no closed chain, or no station visited by one), which leaves the caller to its own guard.
See also
SOLVER_MAM_BGCHAIN,MAM_BGCHAIN_CTMC,MAM_BGCHAIN_GROUPS.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_bgchain_envfull(bg, i)
[A, PHI, ESUP] = MAM_BGCHAIN_ENVFULL(BG, I)
The background modulating chain BG itself, as the environment station I sees, WITHOUT the lumping MAM_BGCHAIN_ENV applies.
THIS IS AN ORACLE, NOT AN IMPROVEMENT, AND THE DIFFERENCE WAS MEASURED. MAM_BGCHAIN_ENV aggregates BG.Q over the level sets {s : totocc(s,i) = e}, which is exact just when the partition is lumpable in Kemeny-Snell’s sense, and nothing in the method checks that it is. It looks like the one uncontrolled approximation left in the open half. IT IS NOT ONE: the background chain is PRODUCT-FORM BY CONSTRUCTION – its rates are CSHARE(i,e+1) * (n_{i,b}/e) * mu_{i,b}, a load-dependent capacity shared in proportion to the class counts, under Markov routing – so given n_i = e the conditional law of the OTHER stations is the product-form law over the remaining population, whatever the tagged station’s occupancy has been doing. The pi-weighted aggregation is then Norton-exact, and the level-dependent rescaling (a function of e alone) preserves it. Measured: 6.7e-16 between the two on a 3-station single-chain cycle whose up-rates are 0/1/2 within one class, 6.7e-16 on a two-background-class chain whose down-rates are 5 and 0.5 within one class, and no difference at any printed digit on four driver-level models checked against SolverCTMC. An INDEPENDENT explicit CTMC of the modulated queue agrees to 1.2e-11.
The lumping being exact is a property of the CHAIN, not of the QBD: fed an environment that is not a product-form network the QBD does separate the two (2.9% on a 4-state counterexample). So keep this function as the check that the property still holds if the chain’s rate law ever changes – and do not expect it to move a number today. It exists so that the next person to suspect the lumping can settle it in one run instead of rebuilding this.
This function returns the chain unlumped, so the QBD’s environment axis carries the whole closed population vector and the closed dynamics it sees are exactly BG.Q. Two things follow:
the environment is Markovian by CONSTRUCTION rather than by assumption, and transitions between states that hold the SAME number of closed jobs at station i – closed jobs moving among the OTHER stations – exist here and are simply absent from the lumped chain;
the phase count of the QBD grows from (distinct occupancies at i) to (states of the background chain), which is what OPTIONS.CONFIG.QBDPHASES_MAX is there to bound.
What it does NOT remove is the mean-field coupling to the OTHER stations: the rates of BG.Q at station j were built at j’s capacity share CSHARE(j,.), already averaged over j’s open occupancy. This is exact in the closed state at station I, and mean-field elsewhere.
Unreachable states are dropped, as MAM_BGCHAIN_ENV drops unreachable environment states, and the diagonal is rebuilt so what is returned is a generator over the states that remain.
- Outputs
A (me x me) generator of the background chain, restricted to its support PHI (1 x me) stationary probability of each state ESUP (1 x me) closed jobs station i holds in each state; UNLIKE
MAM_BGCHAIN_ENV’s, this vector REPEATS – many chain states hold the same number of jobs at station i, which is exactly the information the lumping throws away
See also
MAM_BGCHAIN_ENV,MAM_BGCHAIN_CTMC,SOLVER_MAM_BGCHAIN.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_bgchain_env(bg, i)
[A, PHI, ESUP] = MAM_BGCHAIN_ENV(BG, I)
Lumps the background modulating chain BG onto the number of closed jobs held by station I, giving the Markovian environment that the open classes at that station see.
Station i does not observe the whole closed population vector, only how many closed jobs compete with the open ones for its server. The lumped generator is the stationary-weighted aggregation of BG.Q over the level sets {s : totocc(s,i) = e},
A(e,e’) = sum_{s in e} pi(s) * sum_{s’ in e’} Q(s,s’) / sum_{s in e} pi(s),
which is exact when the partition is lumpable in Kemeny-Snell’s sense and is the standard exact-aggregation approximation otherwise. The diagonal is set from the off-diagonal row sums, so A is a proper generator whatever the lumping error is, and PHI (the aggregated stationary vector) is by construction the stationary vector of the aggregated chain when the partition is lumpable.
- Outputs
A (me x me) lumped environment generator PHI (1 x me) stationary probability of each environment state ESUP (1 x me) number of closed jobs each environment state stands for
Environment states of zero stationary probability are unreachable and are dropped, so ESUP need not be 0:N.
The aggregation being exact here is not luck: the background chain is product-form by construction, so this is Norton’s flow-equivalent rather than an approximation. MAM_BGCHAIN_ENVFULL is the oracle that re-establishes it.
See also
MAM_BGCHAIN_CTMC,MAM_BGCHAIN_ENVFULL,SOLVER_MAM_BGCHAIN.Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mna_open(sn, options)
- solver_mam_dt(sn, options, slotLength)
[QN,UN,RN,TN,CN,XN,TOTITER,METHOD,PQL] = SOLVER_MAM_DT(SN, OPTIONS, SLOTLENGTH)
Discrete-time (slotted) analysis of an open network whose interarrival and service laws all live on the slot lattice. A single queueing station is solved EXACTLY by the Q-MAM discrete-time algorithms, Q_DT_PH_PH_1 when both laws are renewal discrete phase-type and Q_DT_MAP_MAP_1 when either side is a DMAP. Several stations are solved by a discrete-time parametric decomposition, which is an approximation: see solver_mam_dt_network below.
Time is measured in slots internally and converted back on exit, so QN and UN are dimensionless, TN is per time unit and RN is in time units.
Convention: late arrival system with delayed access (LAS-DA). Within a slot a completion resolves before the arrival, an arrival cannot enter service in the slot it arrives in, and every metric is read after both events. This is the convention of the Q-MAM discrete-time queues and of the LDES slotted engine, so the two are directly comparable.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_transient_qbd(sn, options)
SOLVER_MAM_TRANSIENT_QBD Transient analysis of a single-class open queue via the Laplace-domain transient QBD method plus numerical inverse Laplace.
Supports single-server MAP/MAP/1 (infinite buffer) and MAP/MAP/1/N (finite buffer), where arrival and service are read uniformly as (D0,D1) MAPs from sn.proc; this subsumes M/M/1, M/PH/1 and correlated-MAP arrival/service that the libQBD/expm fast path (solver_mam_ldqbd_transient) cannot represent.
The transient level-to-level transform V(s,0,m) is computed by mam_transient2_open (infinite) or mam_transient2 (finite) and inverted with the CME-based matrix inverse Laplace transform. Metrics returned in the [metric, time] layout used by getTranAvg.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_retrial(sn, options)
[QN,UN,RN,TN,CN,XN,TOTITER] = SOLVER_MAM_RETRIAL(SN, OPTIONS)
Solves queueing models with customer impatience:
RETRIAL (orbit impatience): BMAP/PH/N/N bufferless retrial queues Reference: Dudin et al., “Analysis of BMAP/PH/N-Type Queueing System with Flexible Retrials Admission Control”, Mathematics 2025, 13(9), 1434.
RENEGING (queue abandonment): MAP/M/s+G queues with patience Reference: O. Gursoy, K. A. Mehr, N. Akar, “The MAP/M/s + G Call Center Model with Generally Distributed Patience Times”
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_basic_mmap_inner(sn, options, lambda)
[QN,UN,RN,TN,CN,XN,TOTITER] = SOLVER_MAM_BASIC_MMAP_INNER(SN, OPTIONS, LAMBDA)
MAM/MMAP fork-join decomposition algorithm parameterised by per-class arrival rates LAMBDA. Performs the departure-process refinement loop only; population enforcement (closed networks) is the wrapper’s responsibility.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_srcproc_is_renewal(sn, jst)
MAM_SRCPROC_IS_RENEWAL True if station JST’s class-1 process is renewal.
Reads the (D0,D1) pair out of sn.proc and applies MAM_IS_RENEWAL_MAP. When no usable pair is present the answer is FALSE, which is the conservative side: the caller uses this to decide whether a marginal-only closed form may be applied, and applying one to a process whose correlation structure could not be established is exactly the failure this guard exists to prevent.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_chain_arrival_is_markovian(sn, c)
MAM_CHAIN_ARRIVAL_IS_MARKOVIAN True unless chain C is fed by an ME/RAP source.
The system arrival MMAP of an open chain is assembled from the process of each of the chain’s classes at its reference station, which for an open chain is the source. When any of those is a matrix-exponential (ME) or rational arrival process (RAP), the assembled (D0,D1) pair is legitimately non-Markovian: D0 may carry negative off-diagonal entries.
Callers use this to decide whether MMAP_NORMALIZE may be applied. That routine clips negative entries to zero and re-derives the diagonal, which repairs numerical noise on a genuine MAP but on an ME or RAP substitutes a DIFFERENT, Markovian process with different autocorrelation. The distinction has to be made on the declared process type rather than on the sign pattern, because a MAP perturbed by roundoff also shows small negative entries and does need the repair.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- solver_mam_analyzer(sn, options)
[QN,UN,RN,TN,CN,XN,RUNTIME,METHOD] = SOLVER_MAM_ANALYZER(QN, OPTIONS)
- mam_dt_supports(sn)
[BOOL, REASON] = MAM_DT_SUPPORTS(SN)
Can the discrete-time (slotted) path of SolverMAM represent this model? REASON is ‘’ when it can and otherwise names the construct it cannot.
SOLVER_MAM_ANALYZER routes EVERY method to SOLVER_MAM_DT when SN_IS_DISCRETE_TIME says the laws live on a slot lattice, before the method name is read, so these rules bind every MAM method on such a model:
open models only (a closed slotted model needs a level-dependent discrete chain the Q-MAM discrete-time catalogue does not cover);
one class only (independent per-class lattice sources fire in the same slot with positive probability, and a batch of simultaneous arrivals of different classes is not an MMAP[K]);
FCFS single-server stations and the Source only (a slotted multiserver queue needs the level-dependent boundary of Geo/Geo/c).
ONE PREDICATE, TWO CALLERS: solver_mam_dt raises with it and SolverMAM.supportsModelMethod answers with it. It was a local function of solver_mam_dt until 2026-09-05, so the report offered every MAM method on a closed or multiclass slotted model and the run refused it.
- mam_bgchain_applicable(sn, options)
[TF, REASON] = MAM_BGCHAIN_APPLICABLE(SN, OPTIONS)
Can the ‘bgchain’ method of SolverMAM answer this model? REASON is ‘’ when it can and otherwise names what is missing.
- The rules are the ones SOLVER_MAM_BGCHAIN and MAM_BGCHAIN_CTMC raise:
at least one closed chain (the background chain IS the closed population vector, so a purely open model has nothing to build it from);
the closed chains visit some station (the support of the chain);
no class priorities at an HOL or FCFSPRPRIO station (the open classes are aggregated into one phase-type mixture per station, which cannot express a priority order);
no fork-join (the chain conserves the closed population per station, which a fork violates);
a background chain within options.config.bgstates_max states, sized by MAM_BGCHAIN_STATES without building it.
ONE PREDICATE, THREE CALLERS. SolverMAM.supportsModelMethod asks it so the method is not offered on a model it cannot answer; solver_mam_analyzer asks it before choosing bgchain as the closed or mixed default; solver_mam_bgchain asks it before building anything, so a caller naming the method gets the same sentence. Until 2026-09-05 the analyzer kept a private copy (bgchainApplies) and the gate a second one, and the state-space cap sat in neither, so the report offered bgchain on models MAM_BGCHAIN_CTMC refused.
- solver_mam_mapmap1_exact(sn)
SOLVER_MAM_MAPMAP1_EXACT Exact MAP/MAP/1 solution for a single-class, single-server, open Source -> FCFS Queue -> Sink model.
The decomposition methods (dec.source/dec.mmap) approximate this queue: they either fit the arrival to a simpler process or treat the service as a renewal phase-type, discarding autocorrelation. When the arrival or the service is a genuinely correlated (non-renewal) MAP, this routine returns the exact mean queue length via the matrix-geometric MAP/MAP/1 solver (Q_CT_MAP_MAP_1), which carries both phase processes across arrivals and departures.
Returns ok=false (and empty metrics) when the model is not an exactly-solvable single MAP/MAP/1 queue, so the caller falls back to the decomposition methods.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_is_markovian_map(D0, D1)
TF = MAM_IS_MARKOVIAN_MAP(D0, D1)
True when the pair (D0,D1) is a genuine Markovian arrival process: D0 has non-negative off-diagonal rates, D1 is non-negative, and (D0+D1) is an infinitesimal generator (zero row sums). A RAP or ME violates the sign conditions while still defining a valid point process, so a CTMC assembled from it is a rational generator whose stationary solution is a signed vector. Every consumer that builds a CTMC out of sn.proc must gate on this.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_gk1_applicable(sn, ist, K)
MAM_GK1_APPLICABLE True when station IST should be answered by MMAP[K]/G[K]/1.
The generic MMAPPH1FCFS path reads the service law out of SN.PROC, which holds its PHASE-TYPE FIT: for a Uniform, a Gamma, a Pareto, a Weibull, a Lognormal or a Det that fit matches the mean and, once the SCV exceeds one, nothing else. He (2001) needs only the TRANSFORM of the original law, which SN.LST carries, so wherever a class declares one of those laws the exact analysis is available and the fit is not needed.
Two structural conditions. The result is a /1, so a multiserver station is out. And every class must carry a usable transform handle, since the analysis takes all K of them together rather than one at a time.
A matrix-exponential service qualifies too, its transform being rational and SN.LST evaluating it, which is why the ME warning about falling back to a phase-type approximation is suppressed when this returns true. A RAP does NOT qualify. He’s analysis assumes INDEPENDENT service times, so reading a correlated service through its marginal transform would discard exactly the autocorrelation the RAP was declared to carry.
Copyright (c) 2012-2026, Imperial College London All rights reserved.
- mam_transient2_open(B, L, F, Lv, T, n, m, s)
- MAM_TRANSIENT2_OPEN Laplace-domain transient V(s,n,m) for an open (infinite) QBD.
V = MAM_TRANSIENT2_OPEN(B, L, F, Lv, T, n, m, s) returns the Laplace transform (at complex argument s) of the transient transition-probability matrix from level n to level m of a piecewise level-dependent QBD with regime thresholds T (the last regime repeats to infinity).
- Block cell arrays are indexed by regime k = 1..K (K = length(T)):
B{k} backward (level-down) block for regime k L{k} local (level-internal) block used on repeating levels of regime k F{k} forward (level-up) block for regime k Lv{k} local block on the boundary level T(k) of regime k
Ported from the transient-QBD research code (Horvath et al. formulation).
Copyright (c) 2012-2026, Imperial College London All rights reserved.