LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
qsys_lindley_moment.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_LINDLEY_MOMENT_H
6#define LINE_API_QSYS_QSYS_LINDLEY_MOMENT_H
7
8/**
9 * @file
10 * @ingroup api_qsys
11 * One conditional Lindley moment for exponential primitives.
12 *
13 * Templated port of matlab/src/api/qsys/qsys_lindley_moment.m. No JAR
14 * counterpart. Returns E[max(Wn + S - A, 0)^m] with A ~ Exp(lambda),
15 * S ~ Exp(mu) and m >= 1, evaluated at every entry of Wn.
16 *
17 * This is the algorithm shared by qsys_mm1_lindley, which calls it once per
18 * moment order, and qsys_hh1_lindley, which mixes it over the arrival and
19 * service phases. The density of S - A is the asymmetric Laplace density
20 * lambda mu/(lambda+mu) times e^{-mu x} on x > 0 and e^{lambda x} on x < 0,
21 * which splits the expectation into
22 *
23 * S = sum_{k=0}^{m} C(m,k) w^k (m-k)! / mu^(m-k+1)
24 * T = (-1)^m m! ( sum_{k=0}^{m} (-lambda w)^k/k! - e^{-lambda w} ) / lambda^(m+1)
25 *
26 * with the value lambda mu/(lambda+mu) (S + T). The T term is the upper
27 * incomplete gamma Gamma(m+1, -lambda w), which for integer m+1 has the finite
28 * form m! e^{-x} sum_k x^k/k! valid at the negative argument needed here.
29 * Substituting it cancels the growing exponential, so no incomplete gamma
30 * routine is needed and nothing overflows at large w.
31 *
32 * ARITHMETIC: exp() is the only transcendental step, and it appears once per
33 * evaluation point rather than inside the sum, so the exact instantiation is
34 * refused rather than silently rounded. Everything else is a finite sum of
35 * rational terms and is exact whenever the arithmetic is.
36 */
37
38#include <cstddef>
39#include <vector>
40
42#include "line/num/number.h"
43#include "line/util/error.h"
44
45namespace line {
46namespace qsys {
47
48/**
49 * @brief One conditional Lindley moment for exponential primitives.
50 *
51 * @param lambda arrival rate, positive
52 * @param mu service rate, positive
53 * @param Wn current waiting times, nonnegative
54 * @param m moment order, at least 1
55 * @return E[max(Wn + S - A, 0)^m] evaluated at every entry of Wn
56 */
57template <class T>
58std::vector<T> qsys_lindley_moment(const T& lambda, const T& mu, const std::vector<T>& Wn,
59 unsigned m) {
61 "qsys_lindley_moment requires transcendental arithmetic: the incomplete "
62 "gamma term carries an exp(-lambda w) that no finite field evaluates");
63 const T zero = num_traits<T>::from_int(0);
64 if (lambda <= zero) throw InputError("qsys_lindley_moment: lambda must be positive");
65 if (mu <= zero) throw InputError("qsys_lindley_moment: mu must be positive");
66 if (m < 1) throw InputError("qsys_lindley_moment: the moment order must be at least 1");
67
68 // C(m,k) built by the multiplicative recurrence so it stays exact in T
69 std::vector<T> binom(m + 1, num_traits<T>::from_int(1));
70 for (unsigned k = 1; k <= m; ++k)
71 binom[k] = binom[k - 1] * num_traits<T>::from_int(static_cast<long>(m - k + 1)) /
72 num_traits<T>::from_int(static_cast<long>(k));
73
74 std::vector<T> out;
75 out.reserve(Wn.size());
76 const T pref = lambda * mu / (lambda + mu);
77 const T sign = (m % 2 == 0) ? num_traits<T>::from_int(1) : num_traits<T>::from_int(-1);
78 const T mfact = num_factorial<T>(m);
79 for (std::size_t i = 0; i < Wn.size(); ++i) {
80 const T& w = Wn[i];
81 if (w < zero) throw InputError("qsys_lindley_moment: Wn must be nonnegative");
82 T sTerm = zero;
83 for (unsigned k = 0; k <= m; ++k)
84 sTerm += binom[k] * num_pow_int(w, k) * num_factorial<T>(m - k) /
85 num_pow_int(mu, m - k + 1);
86 const T x = -lambda * w;
87 T inner = zero;
88 for (unsigned k = 0; k <= m; ++k) inner += num_pow_int(x, k) / num_factorial<T>(k);
89 const T tTerm = sign * mfact * (inner - detail::num_exp(x)) / num_pow_int(lambda, m + 1);
90 out.push_back(pref * (sTerm + tTerm));
91 }
92 return out;
93}
94
95/** Scalar overload of the same expression. */
96template <class T>
97T qsys_lindley_moment(const T& lambda, const T& mu, const T& Wn, unsigned m) {
98 return qsys_lindley_moment(lambda, mu, std::vector<T>(1, Wn), m)[0];
99}
100
101} // namespace qsys
102} // namespace line
103
104#endif // LINE_API_QSYS_QSYS_LINDLEY_MOMENT_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
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.
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
Number-type abstraction for the templated API port.
Shared return type and arithmetic helpers for the templated qsys port.