1%{ @file qsys_is_retrial.m
2 % @brief Checks
if network
is a valid BMAP/PH/N/N bufferless retrial queue
4 % @author LINE Development Team
8 % @brief Checks
if network
is a valid BMAP/PH/N/N bufferless retrial queue
11 % Validates that the network structure matches the requirements
for
12 % the BMAP/PH/N/N retrial queue solver:
13 % - Single bufferless queue (capacity == number of servers)
14 % - Retrial drop strategy configured
15 % - BMAP/MAP arrival process at source
16 % - PH/Exp service at queue
19 % Based on: Dudin et al.,
"Analysis of BMAP/PH/N-Type Queueing System with
20 % Flexible Retrials Admission Control", Mathematics 2025, 13(9), 1434.
24 % [isRetrial, retInfo] = qsys_is_retrial(sn)
29 % <tr><th>Name<th>Description
30 % <tr><td>sn<td>Network structure
35 % <tr><th>Name<th>Description
36 % <tr><td>isRetrial<td>True if network
is valid BMAP/PH/N/N retrial topology
37 % <tr><td>retInfo<td>Struct with parameters for the retrial solver
40 % Copyright (c) 2012-2026, Imperial College London
41 % All rights reserved.
43function [isRetrial, retInfo] = qsys_is_retrial(sn)
48retInfo.stationIdx = [];
50retInfo.sourceIdx = [];
53retInfo.N = []; % Number of servers
54retInfo.alpha = []; % Retrial rate
55retInfo.gamma = 0; % Orbit impatience (
default 0)
56retInfo.p = 0; % Batch rejection prob (
default 0)
57retInfo.R = []; % Admission threshold (from FCR or
default N-1)
59% Check
if model
is open
60if ~sn_is_open_model(sn)
61 retInfo.errorMsg =
'BMAP/PH/N/N retrial solver requires open queueing model.';
65% Check
for single
class (current limitation)
67 retInfo.errorMsg =
'BMAP/PH/N/N retrial solver currently supports single class only.';
73% see _kb/03-api-layer.md (qsys/ family)
for rationale
74bufferlessStations = [];
75for ist = 1:sn.nstations
76 if sn.nodetype(sn.stationToNode(ist)) ~= NodeType.Queue
79 hasRetrialProc = isfield(sn,
'retrialProc') && ~isempty(sn.retrialProc) ...
80 && size(sn.retrialProc,1) >= ist && any(~cellfun(@isempty, sn.retrialProc(ist,:)));
81 isBufferless = isfinite(sn.cap(ist)) && sn.cap(ist) == sn.nservers(ist);
82 if hasRetrialProc || isBufferless
83 bufferlessStations = [bufferlessStations, ist];
87if isempty(bufferlessStations)
88 retInfo.errorMsg =
'No retrial queue found (configure setOrbit, or a bufferless station with setRetrial).';
92% Check retrial drop strategy
94for ist = bufferlessStations(:)
'
95 if any(sn.droprule(ist,:) == DropStrategy.RETRIAL | ...
96 sn.droprule(ist,:) == DropStrategy.RETRIAL_WITH_LIMIT)
102if isempty(retrialStation)
103 retInfo.errorMsg = 'No retrial drop strategy configured on bufferless queue.
';
107retInfo.stationIdx = retrialStation;
108retInfo.nodeIdx = sn.stationToNode(retrialStation);
109retInfo.N = sn.nservers(retrialStation);
113for ist = 1:sn.nstations
114 if sn.nodetype(sn.stationToNode(ist)) == NodeType.Source
120if isempty(sourceStation)
121 retInfo.errorMsg = 'No Source node found.
';
125retInfo.sourceIdx = sourceStation;
127% Validate arrival process (must be MAP/BMAP)
128% sn.proc{station}{class} contains PH representation
129arrivalProc = sn.proc{sourceStation}{retInfo.classIdx};
130if isempty(arrivalProc) || ~iscell(arrivalProc) || length(arrivalProc) < 2
131 retInfo.errorMsg = 'Invalid arrival process at source.
';
135% Check if arrival is MAP/BMAP (has matrix structure)
136% For BMAP: proc is {D0, D1, D2, ...} or {alpha, A} for MAP
137% For Exp/PH at source: it's {1, -lambda} format
138if ~iscell(arrivalProc{1}) && size(arrivalProc{1}, 2) > 1
139 % This looks like a PH representation {alpha, A}, convert from arrival to MAP
140 % For now, we accept it - the solver_mam_retrial will handle conversion
141elseif iscell(arrivalProc{1})
142 % This might be BMAP format already
145% Validate service process (must be PH/Exp)
146serviceProc = sn.proc{retrialStation}{retInfo.classIdx};
147if isempty(serviceProc) || ~iscell(serviceProc) || length(serviceProc) < 2
148 retInfo.errorMsg =
'Invalid service process at queue.';
152% Extract retrial rate alpha from retrial delay distribution
153% In sn
struct, we need to check
for retrial configuration
154% For now, use
default if not found - the Network/getStruct needs to
export this
155retInfo.alpha = 0.1; % Default, will be extracted from sn in solver
157% Check
for FCR containing
this queue and extract admission threshold
158retInfo.R = retInfo.N - 1; % Default: no admission control
160% Check
if region field exists and
is non-empty
161if isfield(sn,
'region') && ~isempty(sn.region) && isfield(sn,
'nregions') && sn.nregions > 0
162 for f = 1:sn.nregions
163 regionMatrix = sn.region{f};
164 % Check
if this station
is in the region with a global capacity
165 if size(regionMatrix, 1) >= retrialStation
166 globalCapCol = size(regionMatrix, 2); % Last
column is typically global cap
167 if regionMatrix(retrialStation, globalCapCol) > 0
168 % Get the global max jobs
for this region
169 R_fcr = regionMatrix(retrialStation, globalCapCol);
170 if R_fcr <= retInfo.N - 1
171 retInfo.R = R_fcr; % Use FCR threshold
179% All validations passed