LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
aoi_types.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_TYPES_H
6#define LINE_API_AOI_TYPES_H
7
8/**
9 * @file
10 * @ingroup api_aoi
11 * Shared return types and arithmetic helpers for the templated Age of
12 * Information port.
13 *
14 * The MATLAB AoI family in matlab/src/api/aoi/ returns one of two triples:
15 * [meanAoI, varAoI, peakAoI] for the fully parameterized queues (M/M/1,
16 * M/D/1, D/M/1) and [meanAoI, lstAoI, peakAoI] for the ones taking a
17 * Laplace-Stieltjes transform as input (M/GI/1, GI/M/1). Both live here,
18 * mirroring jline.api.aoi.AoiResult and jline.api.aoi.AoiLstResult; each
19 * ported function lives in its own header named after the MATLAB file, as
20 * required by the port convention.
21 *
22 * An LST is represented as a std::function<T(const T&)> rather than a
23 * distribution object, exactly as MATLAB represents it as a function handle
24 * and the JAR as a LstFunction. Nothing in the family inverts an LST: the
25 * transforms are evaluated at real arguments only, and where MATLAB needs a
26 * derivative it takes a central difference with a fixed step.
27 */
28
29#include <cmath>
30#include <functional>
31#include <string>
32
33#include "line/num/number.h"
34#include "line/util/error.h"
35
36namespace line {
37namespace aoi {
38
39/** A Laplace-Stieltjes transform evaluated at real arguments. */
40template <class T>
41using Lst = std::function<T(const T&)>;
42
43/** [meanAoI, varAoI, peakAoI], mirroring jline.api.aoi.AoiResult. */
44template <class T>
50
51/**
52 * [meanAoI, lstAoI, peakAoI], mirroring jline.api.aoi.AoiLstResult.
53 *
54 * has_lst is false for the LCFS-D and LCFS-S disciplines, where MATLAB
55 * returns [] because the transform has no tractable closed form.
56 */
57template <class T>
64
65namespace detail {
66
67/** exp(v), resolved by ADL so double, cpp_bin_float and mpfr all work. */
68template <class T>
69inline T num_exp(const T& v) {
70 using std::exp;
71 return exp(v);
72}
73
74/** Guard on positivity of a rate or a time. */
75template <class T>
76inline void require_positive(const T& v, const char* fn, const char* what) {
77 if (v <= num_traits<T>::from_int(0))
78 throw InputError(std::string(fn) + ": " + what + " must be positive");
79}
80
81/** Guard on stability; every function in the family requires rho < 1. */
82template <class T>
83inline void require_stable(const T& rho, const char* fn) {
84 if (rho >= num_traits<T>::from_int(1))
85 throw NumericError(std::string(fn) + ": unstable system, rho >= 1");
86}
87
88/**
89 * Central difference of an LST at s with MATLAB's step, h = 1e-6 max(1,|s|).
90 *
91 * Every MATLAB file in the family that needs d/ds H*(s) uses exactly this
92 * step; reproducing it, rather than improving on it, is what makes the port
93 * agree with MATLAB to the twelfth digit. The approximation is the reason
94 * these functions require transcendental arithmetic even when the transform
95 * itself is rational: the answer is step-dependent, so an exact field would
96 * return an exact value of the wrong quantity.
97 */
98template <class T>
99inline T lst_derivative(const Lst<T>& f, const T& s) {
100 const T one = num_traits<T>::from_int(1);
101 const T mag = s < num_traits<T>::from_int(0) ? T(-s) : s;
102 const T scale = mag > one ? mag : one;
103 const T h = num_traits<T>::from_double(1e-6) * scale;
104 return (f(T(s + h)) - f(T(s - h))) / (num_traits<T>::from_int(2) * h);
105}
106
107/**
108 * Bisection on a bracketed sign change, the replacement for MATLAB's fzero.
109 *
110 * MATLAB brackets every sigma root in this family on [0.001, 0.999] and calls
111 * fzero (Brent); the JAR replaces it with bisection (Aoi_fcfs_dm1). This port
112 * follows the JAR. With max_iter = 200 the bracket collapses below the
113 * representable resolution of double and of the 50-digit real type alike.
114 */
115template <class T, class F>
116inline T bisect(const F& f, T lo, T hi, const char* fn, unsigned max_iter = 200) {
117 const T zero = num_traits<T>::from_int(0);
118 T flo = f(lo);
119 const T fhi = f(hi);
120 if ((flo > zero && fhi > zero) || (flo < zero && fhi < zero))
121 throw NumericError(std::string(fn) + ": the root is not bracketed by [0.001, 0.999]");
122 for (unsigned it = 0; it < max_iter; ++it) {
123 const T mid = (lo + hi) / num_traits<T>::from_int(2);
124 if (mid == lo || mid == hi) break;
125 const T fm = f(mid);
126 if (fm == zero) return mid;
127 if ((fm > zero) == (flo > zero)) { lo = mid; flo = fm; }
128 else { hi = mid; }
129 }
130 return (lo + hi) / num_traits<T>::from_int(2);
131}
132
133/**
134 * The sigma of a GI/M/1 queue: the unique root in (0,1) of Y*(mu - mu s) = s,
135 * the probability an arriving job finds the server busy.
136 */
137template <class T>
138inline T gim1_sigma(const Lst<T>& Y_lst, const T& mu, const char* fn) {
139 return bisect<T>([&](const T& s) { return T(Y_lst(T(mu - mu * s)) - s); },
140 num_traits<T>::from_double(0.001), num_traits<T>::from_double(0.999), fn);
141}
142
143} // namespace detail
144
145} // namespace aoi
146} // namespace line
147
148#endif // LINE_API_AOI_TYPES_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
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
Number-type abstraction for the templated API port.
[meanAoI, lstAoI, peakAoI], mirroring jline.api.aoi.AoiLstResult.
Definition aoi_types.h:58
[meanAoI, varAoI, peakAoI], mirroring jline.api.aoi.AoiResult.
Definition aoi_types.h:45