LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fj_dispersion.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_FJ_DISPERSION_H
6#define LINE_API_FJ_DISPERSION_H
7
8/**
9 * @file
10 * @ingroup api_fj
11 * Mean subtask dispersion of a split-merge system with Erlang branches.
12 *
13 * Templated port of matlab/src/api/fj/fj_dispersion.m.
14 *
15 * E[X_(N)] = integral_0^inf [ 1 - prod_i F_i(x - d_i) ] dx
16 * E[X_(1)] = integral_0^inf prod_i [ 1 - F_i(x - d_i) ] dx
17 * E[D_d] = E[X_(N)] - E[X_(1)]
18 *
19 * The integrand actually evaluated is 1 - prod F_i - prod (1-F_i), which is
20 * non-negative and vanishes at both ends; the difference of the two products
21 * printed in the survey is not the dispersion and can go negative.
22 *
23 * Branch i is an Erlang with shape(i) stages of rate rate(i), the split-merge
24 * equivalent used in the delay-scheduling construction: a subtask with q others
25 * ahead of it in its parallel queue behaves as an Erlang(q+1, mu).
26 */
27
28#include <cstddef>
29#include <vector>
30
33#include "line/num/number.h"
34#include "line/util/error.h"
35
36namespace line {
37namespace fj {
38
39/** [Edisp, Emax, Emin] of fj_dispersion. */
40template <class T>
46
47/**
48 * @brief Mean subtask dispersion of a split-merge system with Erlang
49 * branches.
50 *
51 * @param shape Erlang stage counts, one per branch
52 * @param rate Erlang stage rates, one per branch
53 * @param d deterministic delays, one per branch, all non-negative
54 * @param tol completion tolerance used to pick the quadrature horizon
55 * @param npanels Simpson panel count, forced even
56 * @return the mean dispersion with the two order-statistic means
57 */
58template <class T>
59FJDispersionResult<T> fj_dispersion(const std::vector<unsigned>& shape,
60 const std::vector<T>& rate, const std::vector<T>& d,
61 const T& tol = num_traits<T>::from_double(1e-10),
62 unsigned npanels = 4000) {
63 const std::size_t N = shape.size();
64 if (rate.size() != N || d.size() != N)
65 throw InputError("fj_dispersion: shape, rate and d must have the same length");
66 if (N < 1) throw InputError("fj_dispersion: at least one branch is required");
67 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1),
69 for (std::size_t i = 0; i < N; ++i) {
70 if (shape[i] < 1) throw InputError("fj_dispersion: Erlang stage counts must be positive");
71 if (!(rate[i] > zero)) throw InputError("fj_dispersion: Erlang stage rates must be positive");
72 if (d[i] < zero) throw InputError("fj_dispersion: delays must be non-negative");
73 }
74 if (npanels % 2 != 0) ++npanels;
75
76 T dmax = d[0], mmax = num_traits<T>::from_int(static_cast<long>(shape[0])) / rate[0];
77 for (std::size_t i = 0; i < N; ++i) {
78 if (d[i] > dmax) dmax = d[i];
79 const T mi = num_traits<T>::from_int(static_cast<long>(shape[i])) / rate[i];
80 if (mi > mmax) mmax = mi;
81 }
82 T U = dmax + num_traits<T>::from_int(8) * mmax;
83 for (unsigned it = 0; it < 60; ++it) {
84 T prodF = one;
85 for (std::size_t i = 0; i < N; ++i)
86 prodF *= detail::erlang_cdf<T>(U - d[i], shape[i], rate[i]);
87 if (one - prodF < tol) break;
88 U = two * U;
89 }
90
91 const T h = U / num_traits<T>::from_int(static_cast<long>(npanels));
92 const T four = num_traits<T>::from_int(4);
93 T accMax = zero, accMin = zero;
94 for (unsigned i = 0; i <= npanels; ++i) {
95 const T x = h * num_traits<T>::from_int(static_cast<long>(i));
96 T Fprod = one, Sprod = one;
97 for (std::size_t j = 0; j < N; ++j) {
98 const T Fj = detail::erlang_cdf<T>(x - d[j], shape[j], rate[j]);
99 Fprod *= Fj;
100 Sprod *= (one - Fj);
101 }
102 T w;
103 if (i == 0 || i == npanels) w = one;
104 else w = (i % 2 == 1) ? four : two;
105 accMax += w * (one - Fprod);
106 accMin += w * Sprod;
107 }
108 const T scale = h / num_traits<T>::from_int(3);
109
111 out.Emax = scale * accMax;
112 out.Emin = scale * accMin;
113 out.Edisp = out.Emax - out.Emin;
114 return out;
115}
116
117/** The undelayed system, d = 0. */
118template <class T>
119FJDispersionResult<T> fj_dispersion(const std::vector<unsigned>& shape,
120 const std::vector<T>& rate) {
121 return fj_dispersion<T>(shape, rate, std::vector<T>(shape.size(), num_traits<T>::from_int(0)));
122}
123
124} // namespace fj
125} // namespace line
126
127#endif // LINE_API_FJ_DISPERSION_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Shared return types and arithmetic helpers for the templated fork-join port.
Expected maximum of K i.i.d.
FJDispersionResult< T > fj_dispersion(const std::vector< unsigned > &shape, const std::vector< T > &rate, const std::vector< T > &d, const T &tol=num_traits< T >::from_double(1e-10), unsigned npanels=4000)
Mean subtask dispersion of a split-merge system with Erlang branches.
Number-type abstraction for the templated API port.
[Edisp, Emax, Emin] of fj_dispersion.