LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
aoi_fcfs_mgi1.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_AOI_FCFS_MGI1_H
6#define LINE_API_AOI_FCFS_MGI1_H
7
8/**
9 * @file
10 * @ingroup api_aoi
11 * Mean Age of Information, its transform and the peak age of an M/GI/1 FCFS
12 * queue.
13 *
14 * Templated port of matlab/src/api/aoi/aoi_fcfs_mgi1.m, cross-checked against
15 * jar/src/main/java/jline/api/aoi/Aoi_fcfs_mgi1.java (identical).
16 *
17 * W*(s) = (1-rho) s / (s - lambda + lambda H*(s)) (Pollaczek-Khinchine)
18 * T*(s) = H*(s) W*(s)
19 * A*(s) = lambda H*(s) / (s + lambda - lambda H*(s)) * W*(s) (Theorem 2)
20 * E[A] = E[H] + E[T] + (1 - 2 rho)/lambda - (d/ds) T*(s) at s = lambda
21 * E[Apeak] = E[T] + 1/lambda
22 *
23 * from Inoue, Masuyama, Takine and Tanaka (IEEE Trans. IT 65(12), 2019). The
24 * derivative term is what carries the correlation between the interarrival
25 * time and the system time.
26 *
27 * static_assert(num_traits<T>::has_transcendental) -- the derivative is taken
28 * by MATLAB's central difference with step 1e-6 max(1,lambda), reproduced here
29 * so the two agree digit for digit. That step is a tolerance, not an exact
30 * operation: in an exact field the function would return an exact value of the
31 * wrong quantity, which is worse than refusing.
32 *
33 * Note that the transform A*(s) itself is a rational function of s whenever
34 * H*(s) is, so the returned Lst is exact for its argument; only the mean is
35 * approximate.
36 */
37
39#include "line/num/number.h"
40#include "line/util/error.h"
41
42namespace line {
43namespace aoi {
44
45/**
46 * @brief Mean Age of Information, its transform and the peak age of an M/GI/1
47 * FCFS queue.
48 *
49 * @param lambda arrival rate, > 0
50 * @param H_lst LST of the service time
51 * @param E_H mean service time, > 0
52 * @param E_H2 second raw moment of the service time, >= E_H^2
53 * @return [meanAoI, peakAoI, A*(s)]
54 */
55template <class T>
56AoiLstResult<T> aoi_fcfs_mgi1(const T& lambda, const Lst<T>& H_lst, const T& E_H, const T& E_H2) {
58 "aoi_fcfs_mgi1 requires transcendental arithmetic");
59 detail::require_positive(lambda, "aoi_fcfs_mgi1", "the arrival rate lambda");
60 detail::require_positive(E_H, "aoi_fcfs_mgi1", "the mean service time E_H");
61 if (!H_lst) throw InputError("aoi_fcfs_mgi1: the service-time LST must be callable");
62 if (E_H2 < E_H * E_H) throw InputError("aoi_fcfs_mgi1: E_H2 must be at least E_H^2");
63 const T one = num_traits<T>::from_int(1), two = num_traits<T>::from_int(2);
64 const T rho = lambda * E_H;
65 detail::require_stable(rho, "aoi_fcfs_mgi1");
66
67 const T E_Y = one / lambda;
68 const T E_W = lambda * E_H2 / (two * (one - rho));
69 const T E_T = E_W + E_H;
70
71 const Lst<T> Tstar = [lambda, H_lst, rho, one](const T& s) {
72 if (num_traits<T>::to_double(s < T(0) ? T(-s) : s) < 1e-12)
73 return one; // removable 0/0 at the origin
74 const T Hs = H_lst(s);
75 return T(Hs * ((one - rho) * s / (s - lambda + lambda * Hs)));
76 };
77 const T dTstar = detail::lst_derivative<T>(Tstar, lambda);
78 const T meanAoI = E_H + E_T + (one - two * rho) / lambda - dTstar;
79 const T peakAoI = E_T + E_Y;
80
81 // LST of AoI (Inoue et al. 2019, Theorem 2), via the general age formula
82 // A*(s) = (lambda/s) * ( T*(s) - Apeak*(s) )
83 // the cycle average of exp(-s*age) over a departure interval: the age starts
84 // each cycle at the system time T of the packet just delivered and grows to
85 // the peak T + (next interarrival) at the next delivery. For M/GI/1 FCFS
86 // Lindley gives W' = max(0, T - Y), so W' + Y is max(Y, T), and with
87 // Y ~ Exp(lambda) independent of T,
88 // E[exp(-s*max(Y,T))] = T*(s) - (s/(s+lambda)) * T*(s+lambda).
89 //
90 // THE PREVIOUS FORM WAS NOT AN LST: (lambda*H*(s))/(s+lambda-lambda*H*(s))
91 // diverges as s -> 0, so A*(0) was +Inf instead of 1 and the value exceeded
92 // 1 for small s. Checked against simulation on M/E2/1: at s = 0.3 the old
93 // form gave 1.3062, the form below 0.56935, and the sample path 0.56948.
94 Lst<T> lstAoI = [lambda, H_lst, Tstar, one](const T& s) {
95 if (num_traits<T>::to_double(s < T(0) ? T(-s) : s) < 1e-12)
96 return one; // A*(0) = 1 for any proper LST
97 const T Hs = H_lst(s);
98 const T Ts = Tstar(s);
99 const T Tsl = Tstar(T(s + lambda));
100 const T peak = Hs * (Ts - (s / (s + lambda)) * Tsl);
101 return T((lambda / s) * (Ts - peak));
102 };
103 return {meanAoI, peakAoI, lstAoI, true};
104}
105
106} // namespace aoi
107} // namespace line
108
109#endif // LINE_API_AOI_FCFS_MGI1_H
Shared return types and arithmetic helpers for the templated Age of Information port.
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
std::function< T(const T &)> Lst
A Laplace-Stieltjes transform evaluated at real arguments.
Definition aoi_types.h:41
AoiLstResult< T > aoi_fcfs_mgi1(const T &lambda, const Lst< T > &H_lst, const T &E_H, const T &E_H2)
Mean Age of Information, its transform and the peak age of an M/GI/1 FCFS queue.
Number-type abstraction for the templated API port.
[meanAoI, lstAoI, peakAoI], mirroring jline.api.aoi.AoiLstResult.
Definition aoi_types.h:58