LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
aoi_fcfs_gim1.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_GIM1_H
6#define LINE_API_AOI_FCFS_GIM1_H
7
8/**
9 * @file
10 * @ingroup api_aoi
11 * Mean Age of Information, its transform and the peak age of a GI/M/1 FCFS
12 * queue.
13 *
14 * Templated port of matlab/src/api/aoi/aoi_fcfs_gim1.m, cross-checked against
15 * jar/src/main/java/jline/api/aoi/Aoi_fcfs_gim1.java (identical).
16 *
17 * sigma solves Y*(mu - mu sigma) = sigma in (0,1); eta = mu (1 - sigma)
18 * E[D] = 1/eta
19 * E[A] = lambda E[Y^2]/2 + 1/mu + lambda (-Y*'(eta))/eta
20 * E[Apeak] = E[Y] + E[D]
21 * A*(s) = (lambda/s) * ( T*(s) - Apeak*(s) ) (Theorem 3)
22 * with T*(s) = eta/(s + eta) the exponential system time and
23 * Apeak*(s) = mu/(s + mu) * ( Y*(s) - s/(s + eta) * Y*(s + eta) )
24 *
25 * from Inoue et al. (2019). The stationary system time is exponential with
26 * rate eta and independent of the next interarrival, which is why the
27 * correlation term reduces to -Y*'(eta)/eta.
28 *
29 * static_assert(num_traits<T>::has_transcendental) -- a bracketed root of a
30 * transcendental equation plus MATLAB's finite-difference derivative.
31 *
32 * Note that MATLAB uses the argument E_Y2 in the mean but never validates it
33 * against Y_lst, so an inconsistent pair silently produces an inconsistent
34 * answer; the port keeps that behaviour and only checks E_Y2 >= E_Y^2.
35 */
36
38#include "line/num/number.h"
39#include "line/util/error.h"
40
41namespace line {
42namespace aoi {
43
44/**
45 * @brief Mean Age of Information, its transform and the peak age of a GI/M/1
46 * FCFS queue.
47 *
48 * @param Y_lst LST of the interarrival time
49 * @param mu service rate, > 0
50 * @param E_Y mean interarrival time, > 0
51 * @param E_Y2 second raw moment of the interarrival time, >= E_Y^2
52 * @return [meanAoI, peakAoI, A*(s)]
53 */
54template <class T>
55AoiLstResult<T> aoi_fcfs_gim1(const Lst<T>& Y_lst, const T& mu, const T& E_Y, const T& E_Y2) {
57 "aoi_fcfs_gim1 requires transcendental arithmetic");
58 detail::require_positive(mu, "aoi_fcfs_gim1", "the service rate mu");
59 detail::require_positive(E_Y, "aoi_fcfs_gim1", "the mean interarrival time E_Y");
60 if (!Y_lst) throw InputError("aoi_fcfs_gim1: the interarrival LST must be callable");
61 if (E_Y2 < E_Y * E_Y) throw InputError("aoi_fcfs_gim1: E_Y2 must be at least E_Y^2");
62 const T one = num_traits<T>::from_int(1), two = num_traits<T>::from_int(2);
63 const T lambda = one / E_Y;
64 const T rho = lambda / mu;
65 detail::require_stable(rho, "aoi_fcfs_gim1");
66
67 const T sigma = detail::gim1_sigma<T>(Y_lst, mu, "aoi_fcfs_gim1");
68 const T E_D = one / (mu * (one - sigma));
69 const T eta = mu * (one - sigma);
70 const T dYstar = detail::lst_derivative<T>(Y_lst, eta);
71
72 const T meanAoI = lambda * E_Y2 / two + one / mu + lambda * (-dYstar) / eta;
73 const T peakAoI = E_Y + E_D;
74
75 // LST of AoI (Inoue et al. 2019, Theorem 3), via the general age formula
76 // A*(s) = (lambda/s) * ( T*(s) - Apeak*(s) )
77 // the cycle average of exp(-s*age) over a departure interval. In GI/M/1 the
78 // system time is EXPONENTIAL at rate eta = mu*(1-sigma), so T*(s) =
79 // eta/(s+eta), and Lindley gives W' + Y = max(Y, T) with T ~ Exp(eta)
80 // independent of the next interarrival Y, so
81 // E[exp(-s*max(Y,T))] = Y*(s) - (s/(s+eta)) * Y*(s+eta),
82 // and the peak adds one fresh Exp(mu) service. A*(0) = 1 follows from the
83 // defining relation Y*(eta) = sigma.
84 //
85 // THE PREVIOUS FORM WAS NOT AN LST: (mu*sigma(s))/(s+mu-mu*sigma(s))*D*(s)
86 // gives sigma/(1-sigma) at s = 0 rather than 1, and it re-solved sigma(s)
87 // by bisection at every point. Checked against simulation on E2/M/1: at
88 // s = 0.2 the old form gave 0.23056, the form below 0.59319, and the sample
89 // path 0.59332.
90 Lst<T> lstAoI = [Y_lst, mu, eta, lambda, one](const T& s) {
91 if (num_traits<T>::to_double(s < T(0) ? T(-s) : s) < 1e-12)
92 return one; // A*(0) = 1 for any proper LST
93 const T Ts = eta / (s + eta);
94 const T peak = (mu / (s + mu)) * (Y_lst(s) - (s / (s + eta)) * Y_lst(T(s + eta)));
95 return T((lambda / s) * (Ts - peak));
96 };
97 return {meanAoI, peakAoI, lstAoI, true};
98}
99
100} // namespace aoi
101} // namespace line
102
103#endif // LINE_API_AOI_FCFS_GIM1_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_gim1(const Lst< T > &Y_lst, const T &mu, const T &E_Y, const T &E_Y2)
Mean Age of Information, its transform and the peak age of a GI/M/1 FCFS queue.
Number-type abstraction for the templated API port.
[meanAoI, lstAoI, peakAoI], mirroring jline.api.aoi.AoiLstResult.
Definition aoi_types.h:58