LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
aoi_is_aoi.m
1function [isAoI, aoiInfo] = aoi_is_aoi(sn)
2%AOI_IS_AOI Check if network is a valid AoI topology for aoi-fluid analysis
3%
4% [isAoI, aoiInfo] = AOI_IS_AOI(sn)
5%
6% Validates that the network structure is suitable for Age of Information
7% analysis using the aoi-fluid MFQ solvers.
8%
9% Requirements:
10% - Single open class
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)
16%
17% Parameters:
18% sn (struct): Network structure from Network.getStruct()
19%
20% Returns:
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
29
30% Copyright (c) 2012-2026, Imperial College London
31% All rights reserved.
32
33aoiInfo = struct();
34aoiInfo.errorMsg = '';
35aoiInfo.sourceIdx = [];
36aoiInfo.queueIdx = [];
37aoiInfo.sinkIdx = [];
38aoiInfo.sourceStation = [];
39aoiInfo.queueStation = [];
40aoiInfo.capacity = [];
41aoiInfo.schedStrategy = [];
42aoiInfo.systemType = '';
43
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';
47 isAoI = false;
48 return;
49end
50
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));
55 isAoI = false;
56 return;
57end
58
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);
63
64% Check: exactly one source
65if isempty(sourceNodeIdx)
66 aoiInfo.errorMsg = 'No source node found';
67 isAoI = false;
68 return;
69elseif length(sourceNodeIdx) > 1
70 aoiInfo.errorMsg = sprintf('Multiple source nodes found (%d)', length(sourceNodeIdx));
71 isAoI = false;
72 return;
73end
74
75% Check: exactly one sink
76if isempty(sinkNodeIdx)
77 aoiInfo.errorMsg = 'No sink node found';
78 isAoI = false;
79 return;
80elseif length(sinkNodeIdx) > 1
81 aoiInfo.errorMsg = sprintf('Multiple sink nodes found (%d)', length(sinkNodeIdx));
82 isAoI = false;
83 return;
84end
85
86% Check: exactly one queue
87if isempty(queueNodeIdx)
88 aoiInfo.errorMsg = 'No queue node found';
89 isAoI = false;
90 return;
91elseif length(queueNodeIdx) > 1
92 aoiInfo.errorMsg = sprintf('Multiple queue nodes found (%d) - AoI analysis supports single queue only', length(queueNodeIdx));
93 isAoI = false;
94 return;
95end
96
97% Get station indices
98sourceStation = sn.nodeToStation(sourceNodeIdx);
99queueStation = sn.nodeToStation(queueNodeIdx);
100
101aoiInfo.sourceIdx = sourceNodeIdx;
102aoiInfo.queueIdx = queueNodeIdx;
103aoiInfo.sinkIdx = sinkNodeIdx;
104aoiInfo.sourceStation = sourceStation;
105aoiInfo.queueStation = queueStation;
106
107% Check: Single server
108c = sn.nservers(queueStation);
109if c ~= 1
110 aoiInfo.errorMsg = sprintf('Queue has %d servers - AoI analysis requires single server (c=1)', c);
111 isAoI = false;
112 return;
113end
114
115% Check: Queue capacity (1 for bufferless, 2 for single-buffer)
116% sn.cap is a vector indexed by station
117cap = sn.cap(queueStation);
118
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);
122 isAoI = false;
123 return;
124end
125
126aoiInfo.capacity = cap;
127if cap == 1
128 aoiInfo.systemType = 'bufferless';
129else
130 aoiInfo.systemType = 'singlebuffer';
131end
132
133% Check: Scheduling strategy (FCFS, LCFS, or LCFSPR)
134schedStrategy = sn.sched(queueStation);
135aoiInfo.schedStrategy = schedStrategy;
136
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');
141 isAoI = false;
142 return;
143end
144
145% For single-buffer (capacity=2): arrivals must be exponential (Poisson)
146if cap == 2
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
152 if size(D0, 1) > 1
153 aoiInfo.errorMsg = 'Single-buffer (capacity=2) requires exponential arrivals (Poisson process)';
154 isAoI = false;
155 return;
156 end
157 end
158end
159
160% Check: No self-loops
161K = sn.nclasses;
162rtIdx = (queueStation - 1) * K + openClasses(1);
163if sn.rt(rtIdx, rtIdx) > 0
164 aoiInfo.errorMsg = 'Self-loop detected at queue - violates AoI model assumptions';
165 isAoI = false;
166 return;
167end
168
169% All checks passed
170isAoI = true;
171end
Definition mmt.m:92