LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fj_amva.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_AMVA_H
6#define LINE_API_FJ_AMVA_H
7
8/**
9 * @file
10 * @ingroup api_fj
11 * Mean value analysis of a closed network of fork-join subnetworks.
12 *
13 * Templated port of matlab/src/api/fj/fj_amva.m.
14 *
15 * R_n(m) = D_n [ H_{P_n} + Q_n(m-1) ]
16 * X(m) = m / (Z + sum_n R_n(m))
17 * Q_n(m) = X(m) R_n(m)
18 *
19 * started from Q_n(0) = 0. With every P_n equal to one this is the exact
20 * single-class mean value analysis, because H_1 = 1; above that it is an
21 * approximation whose per-subnetwork residence time is an upper bound in the
22 * sense of Varki.
23 */
24
25#include <cstddef>
26#include <vector>
27
30#include "line/num/number.h"
31#include "line/util/error.h"
32
33namespace line {
34namespace fj {
35
36/** [R, Q, X, U] of fj_amva. */
37template <class T>
39 std::vector<T> R;
40 std::vector<T> Q;
41 T X;
42 std::vector<T> U;
43};
44
45/**
46 * @brief Mean value analysis of a closed network of fork-join subnetworks.
47 *
48 * @param D per-visit service demand of each subnetwork
49 * @param P fork degree of each subnetwork, P[n] >= 1
50 * @param M number of circulating jobs, M >= 1
51 * @param Z think time, Z >= 0
52 * @return residence times, queue lengths, throughput and per-queue utilizations
53 */
54template <class T>
55FJAmvaResult<T> fj_amva(const std::vector<T>& D, const std::vector<unsigned>& P, unsigned M,
56 const T& Z) {
57 if (D.size() != P.size())
58 throw InputError("fj_amva: D and P must have the same number of elements");
59 if (D.empty()) throw InputError("fj_amva: at least one subnetwork is required");
60 if (M < 1) throw InputError("fj_amva: M must be a positive integer");
61 const T zero = num_traits<T>::from_int(0);
62 if (Z < zero) throw InputError("fj_amva: the think time must be non-negative");
63
64 const std::size_t N = D.size();
65 std::vector<T> H(N);
66 for (std::size_t n = 0; n < N; ++n) {
67 if (D[n] < zero) throw InputError("fj_amva: service demands must be non-negative");
68 H[n] = fj_harmonic<T>(P[n]);
69 }
70
72 out.R.assign(N, zero);
73 out.Q.assign(N, zero);
74 out.U.assign(N, zero);
75 out.X = zero;
76 for (unsigned m = 1; m <= M; ++m) {
77 T Rtot = zero;
78 for (std::size_t n = 0; n < N; ++n) {
79 out.R[n] = D[n] * (H[n] + out.Q[n]);
80 Rtot += out.R[n];
81 }
82 if (!(Rtot > zero))
83 throw NumericError("fj_amva: the total residence time vanished; every demand is zero");
84 out.X = num_traits<T>::from_int(static_cast<long>(m)) / (Z + Rtot);
85 for (std::size_t n = 0; n < N; ++n) out.Q[n] = out.X * out.R[n];
86 }
87 // Each subnetwork holds P(n) queues sharing the demand equally
88 for (std::size_t n = 0; n < N; ++n)
89 out.U[n] = out.X * D[n] / num_traits<T>::from_int(static_cast<long>(P[n]));
90 return out;
91}
92
93} // namespace fj
94} // namespace line
95
96#endif // LINE_API_FJ_AMVA_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.
Harmonic number H_K = sum_{k=1..K} 1/k.
Shared return types and arithmetic helpers for the templated fork-join port.
FJAmvaResult< T > fj_amva(const std::vector< T > &D, const std::vector< unsigned > &P, unsigned M, const T &Z)
Mean value analysis of a closed network of fork-join subnetworks.
Definition fj_amva.h:55
T fj_harmonic(unsigned K)
Harmonic number H_K = sum_{k=1..K} 1/k.
Definition fj_harmonic.h:37
Number-type abstraction for the templated API port.
[R, Q, X, U] of fj_amva.
Definition fj_amva.h:38
std::vector< T > U
Definition fj_amva.h:42
std::vector< T > R
Definition fj_amva.h:39
std::vector< T > Q
Definition fj_amva.h:40