LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_mm1_lindley.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_QSYS_QSYS_MM1_LINDLEY_H
6#define LINE_API_QSYS_QSYS_MM1_LINDLEY_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * Conditional waiting-time moments of the M/M/1 Lindley recursion.
12 *
13 * Templated port of matlab/src/api/qsys/qsys_mm1_lindley.m. No JAR
14 * counterpart. One step of W_{n+1} = max(W_n + S_n - A_n, 0) with
15 * A_n ~ Exp(lambda) and S_n ~ Exp(mu), returning the conditional raw moments
16 * of orders 1..mmax given W_n.
17 *
18 * Unlike every other qsys_* function the quantities here are conditional on
19 * the current state rather than stationary, so they are defined and finite at
20 * any load, lambda >= mu included. There is no stability check for that
21 * reason, and adding one would reject the very regime the recursion is used
22 * to study.
23 *
24 * The mean is returned from the equivalent explicit form
25 * E[W_{n+1} | W_n] = W_n + (lambda-mu)/(lambda mu)
26 * + mu e^{-lambda W_n} / (lambda (lambda+mu))
27 * rather than from moments(:,1), which is what MATLAB does: the two agree in
28 * exact arithmetic, and the explicit form is the better conditioned of the
29 * two at small lambda where the moment expression differences two large
30 * quantities.
31 *
32 * Reference: S. Palomo, J. Pender, "Learning the Tandem Network Lindley
33 * Recursion", Proc. Winter Simulation Conference, 2021, theorem 1 and
34 * corollary 2. Registered in .citations() as 'condlindley'.
35 *
36 * ARITHMETIC: see qsys_lindley_moment.h; exp() makes the exact instantiation
37 * unavailable.
38 */
39
40#include <cstddef>
41#include <string>
42#include <vector>
43
45#include "line/num/number.h"
46#include "line/util/error.h"
47#include "line/util/matrix.h"
48
49namespace line {
50namespace qsys {
51
52/** Mirrors the struct MATLAB returns from qsys_mm1_lindley. */
53template <class T>
55 std::vector<T> mean; ///< conditional mean, one per Wn entry
56 std::vector<T> var; ///< conditional variance, one per Wn entry
57 Matrix<T> moments; ///< (numel(Wn) x mmax) conditional raw moments
58 unsigned mmax = 2; ///< highest moment order computed
59 std::string analyzer; ///< identifier string, as in MATLAB
60};
61
62/**
63 * @brief Conditional waiting-time moments of the M/M/1 Lindley recursion.
64 *
65 * @param lambda arrival rate, positive
66 * @param mu service rate, positive
67 * @param Wn waiting times of customer n, finite and nonnegative
68 * @param mmax highest moment order; raised to 2 when smaller, as in MATLAB
69 */
70template <class T>
71LindleyResult<T> qsys_mm1_lindley(const T& lambda, const T& mu, const std::vector<T>& Wn,
72 unsigned mmax = 2) {
74 "qsys_mm1_lindley requires transcendental arithmetic");
75 const T zero = num_traits<T>::from_int(0);
76 if (lambda <= zero) throw InputError("qsys_mm1_lindley: lambda must be a positive real");
77 if (mu <= zero) throw InputError("qsys_mm1_lindley: mu must be a positive real");
78 if (mmax < 1) throw InputError("qsys_mm1_lindley: mmax must be a positive integer");
79 for (std::size_t i = 0; i < Wn.size(); ++i)
80 if (Wn[i] < zero) throw InputError("qsys_mm1_lindley: Wn must be nonnegative");
81
82 if (mmax < 2) mmax = 2;
83 const std::size_t nw = Wn.size();
85 r.mmax = mmax;
86 r.analyzer = "qsys_mm1_lindley";
87 r.moments = Matrix<T>(nw, mmax, zero);
88 for (unsigned m = 1; m <= mmax; ++m) {
89 const std::vector<T> col = qsys_lindley_moment(lambda, mu, Wn, m);
90 for (std::size_t i = 0; i < nw; ++i) r.moments(i, m - 1) = col[i];
91 }
92
93 r.mean.reserve(nw);
94 r.var.reserve(nw);
95 for (std::size_t i = 0; i < nw; ++i) {
96 const T m1 = Wn[i] + (lambda - mu) / (lambda * mu) +
97 mu * detail::num_exp(T(-lambda * Wn[i])) / (lambda * (lambda + mu));
98 r.mean.push_back(m1);
99 r.var.push_back(r.moments(i, 1) - r.moments(i, 0) * r.moments(i, 0));
100 }
101 return r;
102}
103
104/** Scalar overload. */
105template <class T>
106LindleyResult<T> qsys_mm1_lindley(const T& lambda, const T& mu, const T& Wn,
107 unsigned mmax = 2) {
108 return qsys_mm1_lindley(lambda, mu, std::vector<T>(1, Wn), mmax);
109}
110
111} // namespace qsys
112} // namespace line
113
114#endif // LINE_API_QSYS_QSYS_MM1_LINDLEY_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Dense matrix and non-owning view.
LindleyResult< T > qsys_mm1_lindley(const T &lambda, const T &mu, const std::vector< T > &Wn, unsigned mmax=2)
Conditional waiting-time moments of the M/M/1 Lindley recursion.
std::vector< T > qsys_lindley_moment(const T &lambda, const T &mu, const std::vector< T > &Wn, unsigned m)
One conditional Lindley moment for exponential primitives.
Number-type abstraction for the templated API port.
One conditional Lindley moment for exponential primitives.
Mirrors the struct MATLAB returns from qsys_mm1_lindley.
unsigned mmax
highest moment order computed
std::vector< T > mean
conditional mean, one per Wn entry
std::vector< T > var
conditional variance, one per Wn entry
std::string analyzer
identifier string, as in MATLAB
Matrix< T > moments
(numel(Wn) x mmax) conditional raw moments