1function [QN, UN, RN, TN, xvec_it, QNt, UNt, TNt, xvec_t, t, iters, runtime, aoiResults] = solver_fluid_aoi(sn, options)
2%SOLVER_FLUID_AOI Age of Information analysis
using Markovian Fluid Queues
4% [QN, UN, RN, TN, xvec_it, QNt, UNt, TNt, xvec_t, t, iters, runtime, aoiResults] = SOLVER_FLUID_AOI(sn, options)
6% Analyzes Age of Information
for single-queue open systems
using the
7% aoi-fluid MFQ solvers.
10% - Capacity 1 (bufferless): PH/PH/1/1 or PH/PH/1/1* (preemptive)
11% - Capacity 2 (single-buffer): M/PH/1/2 or M/PH/1/2* (replacement)
14% sn (struct): Network structure from Network.getStruct()
15% options (struct): Solver options including:
16% - config.aoi_preemption: Override preemption/replacement probability (0-1)
19% QN, UN, RN, TN: Standard performance metrics
20% xvec_it, QNt, UNt, TNt, xvec_t, t: Transient outputs (for compatibility)
21% iters: Number of iterations (always 1)
22% runtime: Execution time in seconds
23% aoiResults (struct): Age of Information results:
24% .AoI_mean, .AoI_var: Mean and variance of AoI
25% .PAoI_mean, .PAoI_var: Mean and variance of Peak AoI
26% .AoI_g, .AoI_A, .AoI_h: Matrix exponential parameters for AoI CDF
27% .PAoI_g, .PAoI_A, .PAoI_h: Matrix exponential parameters for Peak AoI CDF
28% .systemType:
'bufferless' or
'singlebuffer'
29% .preemption: Preemption/replacement probability used
32% aoi-fluid toolbox by Ozancan Dogan, Nail Akar, Eray Unsal Atay
33% BSD 2-Clause License, 2020
35% See also: aoi_is_aoi, aoi_extract_params, solveBufferless, solveSingleBuffer
37% Copyright (c) 2012-2026, Imperial College London
45% Initialize output arrays
51% Initialize transient outputs (AoI
is steady-state only)
52t = [0; options.timespan(2)];
58 QNt{ist, k} = zeros(2, 1);
59 UNt{ist, k} = zeros(2, 1);
60 TNt{ist, k} = zeros(2, 1);
65xvec_it = {zeros(1, 1)};
68% Initialize AoI results
70aoiResults.AoI_mean = NaN;
71aoiResults.AoI_var = NaN;
72aoiResults.PAoI_mean = NaN;
73aoiResults.PAoI_var = NaN;
77aoiResults.PAoI_g = [];
78aoiResults.PAoI_A = [];
79aoiResults.PAoI_h = [];
80aoiResults.systemType =
'';
81aoiResults.preemption = NaN;
83% =========================================================================
85% =========================================================================
87[isAoI, aoiInfo] = aoi_is_aoi(sn);
90 line_error(mfilename,
'AoI analysis requires valid AoI topology: %s', aoiInfo.errorMsg);
94sourceStation = aoiInfo.sourceStation;
95queueStation = aoiInfo.queueStation;
97line_debug(
'AoI: Source station=%d, Queue station=%d, capacity=%d', ...
98 sourceStation, queueStation, aoiInfo.capacity);
100% =========================================================================
102% =========================================================================
104[aoiParams, aoiInfo] = aoi_extract_params(sn, aoiInfo, options);
106aoiResults.systemType = aoiInfo.systemType;
108% =========================================================================
109% CALL AOI-FLUID SOLVER
110% =========================================================================
112if aoiInfo.capacity == 1
113 % BUFFERLESS: PH/PH/1/1 or PH/PH/1/1*
116 sigma = aoiParams.sigma;
120 aoiResults.preemption = p;
122 line_debug(
'AoI: Calling solveBufferless with p=%.2f', p);
125 [AoI_g, AoI_A, AoI_h, AoI_mean, AoI_var, ...
126 PAoI_g, PAoI_A, PAoI_h, PAoI_mean, PAoI_var] = solveBufferless(tau, T, sigma, S, p);
128 line_error(mfilename,
'solveBufferless failed: %s', ME.message);
132 % SINGLE-BUFFER: M/PH/1/2 or M/PH/1/2*
133 lambda = aoiParams.lambda;
134 sigma = aoiParams.sigma;
138 aoiResults.preemption = r;
140 line_debug(
'AoI: Calling solveSingleBuffer with r=%.2f', r);
143 [AoI_g, AoI_A, AoI_h, AoI_mean, AoI_var, ...
144 PAoI_g, PAoI_A, PAoI_h, PAoI_mean, PAoI_var] = solveSingleBuffer(lambda, sigma, S, r);
146 line_error(mfilename,
'solveSingleBuffer failed: %s', ME.message);
151aoiResults.AoI_mean = AoI_mean;
152aoiResults.AoI_var = AoI_var;
153aoiResults.PAoI_mean = PAoI_mean;
154aoiResults.PAoI_var = PAoI_var;
155aoiResults.AoI_g = AoI_g;
156aoiResults.AoI_A = AoI_A;
157aoiResults.AoI_h = AoI_h;
158aoiResults.PAoI_g = PAoI_g;
159aoiResults.PAoI_A = PAoI_A;
160aoiResults.PAoI_h = PAoI_h;
162line_debug(
'AoI: Mean AoI=%.4f, Var AoI=%.4f', AoI_mean, AoI_var);
163line_debug(
'AoI: Mean PAoI=%.4f, Var PAoI=%.4f', PAoI_mean, PAoI_var);
165% =========================================================================
166% COMPUTE STANDARD PERFORMANCE METRICS
167% =========================================================================
170openClasses = find(isinf(sn.njobs));
174lambda = sn.rates(sourceStation, k);
176% Get mean service rate
177if aoiInfo.capacity == 1
178 % For bufferless: compute from PH representation
179 % Mean service time = -sigma * S^{-1} * ones
180 sigma = aoiParams.sigma;
182 meanServiceTime = -sigma / S * ones(size(S, 1), 1);
183 mu = 1 / meanServiceTime;
185 % For single-buffer: compute similarly
186 sigma = aoiParams.sigma;
188 meanServiceTime = -sigma / S * ones(size(S, 1), 1);
189 mu = 1 / meanServiceTime;
194UN(queueStation, k) = min(1, rho);
196% Throughput (
for stable system, equals arrival rate)
198 TN(queueStation, k) = lambda;
200 TN(queueStation, k) = mu; % Saturated throughput
202TN(sourceStation, k) = TN(queueStation, k);
204% Queue length (
using Little
's Law approximation)
205% For AoI systems, we can use M/M/1 approximation for queue metrics
207 QN(queueStation, k) = rho / (1 - rho);
208 RN(queueStation, k) = 1 / (mu - lambda);
210 QN(queueStation, k) = Inf;
211 RN(queueStation, k) = Inf;
215QNt{queueStation, k} = [0; QN(queueStation, k)];
216UNt{queueStation, k} = [0; UN(queueStation, k)];
217TNt{queueStation, k} = [0; TN(queueStation, k)];
219runtime = toc(runtime_start);
221line_debug('AoI completed in %.4f seconds
', runtime);