LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_nc_sdr.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_SOLVERS_NC_SOLVER_NC_SDR_H
6#define LINE_SOLVERS_NC_SOLVER_NC_SDR_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Exact product-form analysis under state-dependent routing.
12 *
13 * Port of matlab/src/solvers/NC/solver_nc_sdr_analyzer.m, from A. E.
14 * Krzesinski, "Multiclass Queueing Networks with State-Dependent Routing",
15 * Performance Evaluation 7(2):125-143, 1987. The joint distribution is
16 * eq. (16); the coefficients xi are those of Section 3.2, taken from the
17 * state-independent part of the routing matrix.
18 *
19 * This is the ONLY exact route for an SDR model in this port. The generator
20 * carries no per-state routing function, so the CTMC and SSA handlers cannot
21 * represent eq. (10) and do not declare the feature; `refresh_routing` gives
22 * the entry row the same uniform placeholder MATLAB's `getRoutingMatrix` does,
23 * which this analyzer never reads because `pfqn_sdrvisits` overwrites it with
24 * the collapsed e -> d arc.
25 */
26
27#include <cmath>
28#include <cstddef>
29#include <string>
30#include <vector>
31
34#include "line/num/number.h"
36#include "line/util/error.h"
37#include "line/util/matrix.h"
38
39namespace line {
40namespace nc {
41
42/**
43 * Solves a network whose entry centre routes by state-dependent routing.
44 *
45 * @param sn the refreshed struct, carrying a non-empty `sdr`
46 * @param opt solver controls, unused beyond the method label
47 */
48template <class T>
50 const std::size_t M = sn.nstations, K = sn.nclasses;
51 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
52
53 const std::vector<double> njobs = sn.njobs();
54 for (std::size_t r = 0; r < K; ++r)
55 if (std::isinf(njobs[r]))
56 throw UnsupportedError("solver_nc_sdr: state-dependent routing is defined for closed "
57 "networks only; the model has an open class");
58 if (sn.nchains != K)
59 throw UnsupportedError("solver_nc_sdr: state-dependent routing does not support class "
60 "switching; the product form of Krzesinski (1987) is stated over "
61 "closed chains whose customers keep their class");
62 if (sn.stateful_nodes.size() != M)
63 throw UnsupportedError("solver_nc_sdr: state-dependent routing requires every stateful "
64 "node to be a station; the product form is over queue lengths, and "
65 "a stateless node holds none");
66
67 Matrix<T> S(M, K, zero);
68 for (std::size_t i = 0; i < M; ++i)
69 for (std::size_t r = 0; r < K; ++r) {
70 const T mu = sn.rates(i, r);
71 if (mu > zero) S(i, r) = one / mu;
72 }
73
74 std::size_t Ntot = 0;
75 std::vector<std::size_t> N(K, 0);
76 for (std::size_t r = 0; r < K; ++r) {
77 N[r] = static_cast<std::size_t>(njobs[r]);
78 Ntot += N[r];
79 }
80
81 Matrix<T> alpha(M, Ntot > 0 ? Ntot : 1, one);
82 for (std::size_t i = 0; i < M; ++i)
83 for (std::size_t k = 1; k <= alpha.cols(); ++k) {
84 T a = one;
85 if (sn.stations[i].sched == qn::SchedStrategy::INF) {
86 a = num_traits<T>::from_int(static_cast<long>(k));
87 } else {
88 const double c = sn.stations[i].nservers;
89 if (std::isfinite(c) && c > 1.0)
91 static_cast<long>(std::min<double>(static_cast<double>(k), c)));
92 }
93 alpha(i, k - 1) = a;
94 }
95
96 // A BCMP centre served FCFS must hold one rate for every chain; eq. (16)
97 // admits chain-dependent rates only at the symmetric disciplines.
98 for (std::size_t i = 0; i < M; ++i)
99 if (sn.stations[i].sched == qn::SchedStrategy::FCFS) {
100 bool haveRef = false;
101 T ref = zero;
102 for (std::size_t r = 0; r < K; ++r) {
103 if (njobs[r] <= 0.0 || !(S(i, r) > zero)) continue;
104 if (!haveRef) {
105 ref = S(i, r);
106 haveRef = true;
107 } else if (!(ref == S(i, r))) {
108 throw UnsupportedError("solver_nc_sdr: station '" + sn.stations[i].name +
109 "' is FCFS with chain-dependent service times, which "
110 "has no BCMP product form; use PS, LCFSPR or INF, or "
111 "equalize the service times");
112 }
113 }
114 }
115
116 std::vector<Matrix<T>> P;
117 P.reserve(K);
118 for (std::size_t r = 0; r < K; ++r) {
119 Matrix<T> Pr(M, M, zero);
120 for (std::size_t i = 0; i < M; ++i) {
121 const std::size_t isf = sn.stateful_of_station(i + 1) - 1;
122 for (std::size_t j = 0; j < M; ++j) {
123 const std::size_t jsf = sn.stateful_of_station(j + 1) - 1;
124 Pr(i, j) = sn.rt(isf * K + r, jsf * K + r);
125 }
126 }
127 P.push_back(Pr);
128 }
129 const Matrix<T> xi = pfqn::pfqn_sdrvisits(sn.sdr, P);
130 // 'sdr' evaluates the product form (16) exactly by state enumeration, which
131 // is general in the branch topology; 'sdr.mva' runs the paper's Section 4
132 // MVA and convolution, which costs O(J T M (V_1...V_J)^2) instead of the
133 // state-space size but requires single-centre branches. Both are exact.
134 const bool useMva = (opt.method == "sdr.mva");
135 const pfqn::SdrResult<T> pf = useMva ? pfqn::pfqn_sdrmva(S, xi, N, sn.sdr, alpha)
136 : pfqn::pfqn_sdr(S, xi, N, sn.sdr, alpha);
137
138 NcSolution<T> out;
139 out.sol.Q = pf.QN;
140 out.sol.U = pf.UN;
141 out.sol.R = pf.RN;
142 out.sol.Tp = pf.XN;
143 out.sol.X.assign(K, zero);
144 out.sol.C.assign(K, zero);
145 for (std::size_t r = 0; r < K; ++r) {
146 const std::size_t ref = sn.classes[r].refstat - 1;
147 out.sol.X[r] = pf.XN(ref, r);
148 if (out.sol.X[r] > zero)
149 out.sol.C[r] = num_traits<T>::from_double(njobs[r]) / out.sol.X[r];
150 }
151 out.sol.method = useMva ? "sdr.mva" : "sdr";
152 out.sol.iter = 1;
153 out.actualmethod = out.sol.method;
154 out.STeff = S;
155 return out;
156}
157
158} // namespace nc
159} // namespace line
160
161#endif // LINE_SOLVERS_NC_SOLVER_NC_SDR_H
std::size_t cols() const
Definition matrix.h:90
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Dense matrix and non-owning view.
NcSolution< T > solver_nc_sdr(const qn::NetworkStruct< T > &sn, const NcSolverOptions &opt)
Solves a network whose entry centre routes by state-dependent routing.
SdrResult< T > pfqn_sdr(const Matrix< T > &S, const Matrix< T > &xi, const std::vector< std::size_t > &N, const SdrStruct &sdr, const Matrix< T > &alpha=Matrix< T >())
Exact product form of eq.
Definition pfqn_sdr.h:304
SdrResult< T > pfqn_sdrmva(const Matrix< T > &S, const Matrix< T > &xi, const std::vector< std::size_t > &N, const SdrStruct &sdr, const Matrix< T > &alpha=Matrix< T >())
Section 4 mean value analysis and convolution.
Definition pfqn_sdr.h:552
Matrix< T > pfqn_sdrvisits(const SdrStruct &sdr, const std::vector< Matrix< T > > &P)
Coefficients xi of Section 3.2.
Definition pfqn_sdr.h:855
Controls and result shape shared by the normalizing-constant analyzers.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Product-form state-dependent routing.
The [Q,U,R,T,C,X,lG] of the reference, plus the algorithm that ran.
Definition nc_types.h:113
mva::MvaSolution< T > sol
Definition nc_types.h:114
Matrix< T > STeff
the service times of the last pass, MATLAB's STeff
Definition nc_types.h:116
std::string actualmethod
Definition nc_types.h:115
Controls, defaulting to SolverOptions('NC') in the reference.
Definition nc_types.h:33
Mean performance measures returned by pfqn_sdr.
Definition pfqn_sdr.h:247
Matrix< T > XN
per-centre chain throughputs
Definition pfqn_sdr.h:249
Matrix< T > QN
mean queue lengths, centres by chains
Definition pfqn_sdr.h:248
Matrix< T > RN
mean response times at the centre, QN elementwise over XN
Definition pfqn_sdr.h:251
Matrix< T > UN
mean number in service, XN elementwise times S
Definition pfqn_sdr.h:250