1function [isAoI, aoiInfo] = aoi_is_aoi(sn)
2%AOI_IS_AOI Check
if network
is a valid AoI topology
for aoi-fluid analysis
4% [isAoI, aoiInfo] = AOI_IS_AOI(sn)
6% Validates that the network structure
is suitable
for Age of Information
7% analysis
using the aoi-fluid MFQ solvers.
11% - Single-queue open system: Source -> Queue -> Sink
12% - Queue capacity = 1 (bufferless) or 2 (single-buffer)
13% - Single server (nservers = 1)
14% - Scheduling: FCFS, LCFS, or LCFSPR
15% - For capacity=2: arrivals must be exponential (Poisson)
18% sn (
struct): Network structure from Network.getStruct()
21% isAoI (logical): True if topology
is valid for AoI analysis
22% aoiInfo (struct): Contains topology information:
23% .sourceIdx, .queueIdx, .sinkIdx: Node indices
24% .sourceStation, .queueStation: Station indices
25% .capacity: Queue capacity (1 or 2)
26% .schedStrategy: Scheduling strategy
27% .systemType:
'bufferless' or
'singlebuffer'
28% .errorMsg: Error message if not valid
30% Copyright (c) 2012-2026, Imperial College London
35aoiInfo.sourceIdx = [];
38aoiInfo.sourceStation = [];
39aoiInfo.queueStation = [];
41aoiInfo.schedStrategy = [];
42aoiInfo.systemType =
'';
44% Check 1: Must have at least one open
class
45if ~any(isinf(sn.njobs))
46 aoiInfo.errorMsg = 'Not an open model - all
classes are closed';
51% Check 2: Must have exactly one open class
52openClasses = find(isinf(sn.njobs));
53if length(openClasses) > 1
54 aoiInfo.errorMsg = sprintf('Multiple open
classes found (%d) - AoI analysis requires single class', length(openClasses));
59% Find Source, Queue, and Sink
nodes
60sourceNodeIdx = find(sn.nodetype == NodeType.Source);
61queueNodeIdx = find(sn.nodetype == NodeType.Queue);
62sinkNodeIdx = find(sn.nodetype == NodeType.Sink);
64% Check: exactly one source
65if isempty(sourceNodeIdx)
66 aoiInfo.errorMsg = 'No source node found';
69elseif length(sourceNodeIdx) > 1
70 aoiInfo.errorMsg = sprintf('Multiple source
nodes found (%d)', length(sourceNodeIdx));
75% Check: exactly one sink
76if isempty(sinkNodeIdx)
77 aoiInfo.errorMsg = 'No sink node found';
80elseif length(sinkNodeIdx) > 1
81 aoiInfo.errorMsg = sprintf('Multiple sink
nodes found (%d)', length(sinkNodeIdx));
86% Check: exactly one queue
87if isempty(queueNodeIdx)
88 aoiInfo.errorMsg = 'No queue node found';
91elseif length(queueNodeIdx) > 1
92 aoiInfo.errorMsg = sprintf('Multiple queue
nodes found (%d) - AoI analysis supports single queue only', length(queueNodeIdx));
98sourceStation = sn.nodeToStation(sourceNodeIdx);
99queueStation = sn.nodeToStation(queueNodeIdx);
101aoiInfo.sourceIdx = sourceNodeIdx;
102aoiInfo.queueIdx = queueNodeIdx;
103aoiInfo.sinkIdx = sinkNodeIdx;
104aoiInfo.sourceStation = sourceStation;
105aoiInfo.queueStation = queueStation;
107% Check: Single server
108c = sn.nservers(queueStation);
110 aoiInfo.errorMsg = sprintf('Queue has %d servers - AoI analysis requires single server (c=1)', c);
115% Check: Queue capacity (1 for bufferless, 2 for single-buffer)
116% sn.cap
is a vector indexed by station
117cap = sn.cap(queueStation);
119% Handle infinite or invalid capacity
120if isinf(cap) || cap > 2 || cap < 1
121 aoiInfo.errorMsg = sprintf('Queue capacity
is %g - AoI analysis requires capacity 1 (bufferless) or 2 (single-buffer)', cap);
126aoiInfo.capacity = cap;
128 aoiInfo.systemType = 'bufferless';
130 aoiInfo.systemType = 'singlebuffer';
133% Check: Scheduling strategy (FCFS, LCFS, or LCFSPR)
134schedStrategy = sn.sched(queueStation);
135aoiInfo.schedStrategy = schedStrategy;
137if schedStrategy ~= SchedStrategy.FCFS && ...
138 schedStrategy ~= SchedStrategy.LCFS && ...
139 schedStrategy ~= SchedStrategy.LCFSPR
140 aoiInfo.errorMsg = sprintf('Unsupported scheduling strategy - AoI analysis supports FCFS, LCFS, or LCFSPR only');
145% For single-buffer (capacity=2): arrivals must be exponential (Poisson)
147 % Check the arrival process at the source
148 arrivalProc = sn.proc{sourceStation}{openClasses(1)};
149 if ~isempty(arrivalProc) && ~isnan(arrivalProc{1}(1))
150 % Check
if it
's exponential (single phase)
151 D0 = arrivalProc{1}; % MAP representation
153 aoiInfo.errorMsg = 'Single-buffer (capacity=2)
requires exponential arrivals (Poisson process)';
160% Check: No self-loops
162rtIdx = (queueStation - 1) * K + openClasses(1);
163if sn.rt(rtIdx, rtIdx) > 0
164 aoiInfo.errorMsg = 'Self-loop detected at queue - violates AoI model assumptions';