LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fj_dist2fj.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_FJ_DIST2FJ_H
6#define LINE_API_FJ_FJ_DIST2FJ_H
7
8/**
9 * @file
10 * @ingroup api_fj
11 * Conversion of a LINE MAP into the arrival or service descriptor of the
12 * fork-join response-time-tail algorithm of Qiu, Perez and Harrison (IFIP
13 * Performance 2015).
14 *
15 * Templated port of matlab/src/api/fj/fj_dist2fj.m. The reference reads two
16 * scalars out of the NetworkStruct, sn.procid(ist, r) and sn.rates(ist, r);
17 * only the first is used (see the reference defects below), and it is passed
18 * here as an explicit FjProcType, so this port carries no NetworkStruct
19 * dependency.
20 *
21 * arrival: (lambda, lambda0 = D0, lambda1 = D1, ma = phases, Ia = I)
22 * service: (mu = lambda, ST = D0, St = -D0 e, tau_st = map_pie)
23 *
24 * At most two phases are accepted, which is the algorithm's own restriction.
25 *
26 * REFERENCE DEFECTS in fj_dist2fj.m:
27 *
28 * 1. DEAD READ. mean_rate = sn.rates(ist, r) is computed on line 53 and never
29 * used; the rate that is returned is map_lambda of the MAP itself. Not
30 * propagated: the port does not take a rate argument at all. This matters
31 * because it is the only place the two could disagree, and the MAP is the
32 * authority.
33 *
34 * 2. mean_time = 1 / lambda is likewise computed and never used, and divides
35 * by zero for a disabled class instead of reporting it.
36 *
37 * 3. THE SERVICE BRANCH ACCEPTS MAP(2) SILENTLY. Its distribution switch
38 * rejects a two-phase MAP with an error, but only AFTER the descriptor has
39 * been filled in, and the arrival branch accepts MAP(2) explicitly. The
40 * asymmetry is intentional in the algorithm (service must be phase type),
41 * so it is reproduced: FjProcType::Map is rejected for a service process.
42 *
43 * ARITHMETIC. map_lambda and map_pie need one linear solve each, and the exit
44 * vector is a row sum, so the whole conversion stays in the field and is
45 * instantiated at T = Rational as well as double and Real.
46 */
47
48#include <cstddef>
49#include <vector>
50
52#include "line/num/number.h"
53#include "line/util/error.h"
54#include "line/util/linalg.h"
55#include "line/util/matrix.h"
56
57namespace line {
58namespace fj {
59
60/**
61 * The subset of ProcessType that the fork-join algorithm accepts. The values
62 * are MATLAB's ProcessType codes (EXP = 0, ERLANG = 1, HYPEREXP = 2, MAP = 5),
63 * so a caller holding sn.procid can pass it through unchanged.
64 */
65enum class FjProcType { Exp = 0, Erlang = 1, HyperExp = 2, Map = 5 };
66
67/** Which of the two descriptors to build. */
68enum class FjDistKind { Arrival, Service };
69
70/**
71 * Descriptor of an arrival or a service process. Only the fields of the
72 * requested kind are filled; choice is the algorithm's own distribution code
73 * (1 = Exp, 2 = HE2, 3 = ER2, 4 = MAP2), MATLAB's ArrChoice or SerChoice.
74 */
75template <class T>
76struct FjDist {
77 // arrival
81 std::size_t ma = 0;
83 // service
86 std::vector<T> St;
87 std::vector<T> tau_st;
88 int choice = 0;
89};
90
91/**
92 * Build the fork-join descriptor of a MAP.
93 *
94 * @param m the LINE MAP (D0, D1)
95 * @param kind arrival or service
96 * @param procType the process type, MATLAB's sn.procid(ist, r)
97 */
98template <class T>
100 const std::size_t n = m.order();
101 if (n == 0) throw InputError("fj_dist2fj: empty process");
102 if (m.D1.rows() != n || m.D0.cols() != n || m.D1.cols() != n)
103 throw InputError("fj_dist2fj: D0 and D1 must be square matrices of the same size");
104 if (n > 2)
105 throw InputError("fj_dist2fj: only distributions with at most 2 phases are supported");
106
107 const T zero = num_traits<T>::from_int(0);
108 FjDist<T> d;
109 const T lambda = mam::map_lambda(m);
110
111 if (kind == FjDistKind::Arrival) {
112 d.lambda = lambda;
113 d.lambda0 = m.D0;
114 d.lambda1 = m.D1;
115 d.ma = n;
116 d.Ia = eye<T>(n);
117 if (procType == FjProcType::Exp)
118 d.choice = 1;
119 else if (procType == FjProcType::HyperExp && n == 2)
120 d.choice = 2;
121 else if (procType == FjProcType::Erlang && n == 2)
122 d.choice = 3;
123 else if (procType == FjProcType::Map && n == 2)
124 d.choice = 4;
125 else
126 throw InputError("fj_dist2fj: unsupported arrival distribution type for this number "
127 "of phases");
128 } else {
129 d.mu = lambda;
130 d.ST = m.D0;
131 d.St.assign(n, zero);
132 for (std::size_t i = 0; i < n; ++i) {
133 T s = zero;
134 for (std::size_t j = 0; j < n; ++j) s += m.D0(i, j);
135 d.St[i] = -s;
136 }
137 d.tau_st = mam::map_pie(m);
138 if (procType == FjProcType::Exp)
139 d.choice = 1;
140 else if (procType == FjProcType::HyperExp && n == 2)
141 d.choice = 2;
142 else if (procType == FjProcType::Erlang && n == 2)
143 d.choice = 3;
144 else
145 throw InputError("fj_dist2fj: unsupported service distribution type for this number "
146 "of phases");
147 }
148 return d;
149}
150
151} // namespace fj
152} // namespace line
153
154#endif // LINE_API_FJ_FJ_DIST2FJ_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
FjDistKind
Which of the two descriptors to build.
Definition fj_dist2fj.h:68
FjDist< T > fj_dist2fj(const mam::Map< T > &m, FjDistKind kind, FjProcType procType)
Build the fork-join descriptor of a MAP.
Definition fj_dist2fj.h:99
FjProcType
The subset of ProcessType that the fork-join algorithm accepts.
Definition fj_dist2fj.h:65
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
Definition map_moment.h:89
T map_lambda(const Map< T > &m)
Stationary arrival rate, lambda = pi D1 e.
Definition map_moment.h:79
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Number-type abstraction for the templated API port.
Descriptor of an arrival or a service process.
Definition fj_dist2fj.h:76
Matrix< T > lambda1
Definition fj_dist2fj.h:80
std::vector< T > tau_st
Definition fj_dist2fj.h:87
Matrix< T > lambda0
Definition fj_dist2fj.h:79
Matrix< T > Ia
Definition fj_dist2fj.h:82
std::vector< T > St
Definition fj_dist2fj.h:86
std::size_t ma
Definition fj_dist2fj.h:81
Matrix< T > ST
Definition fj_dist2fj.h:85
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Matrix< T > D1
Definition map_moment.h:55
Matrix< T > D0
Definition map_moment.h:54
std::size_t order() const
Definition map_moment.h:57