LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
sn_compat_rate.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_SN_SN_COMPAT_RATE_H
6
#define LINE_API_SN_SN_COMPAT_RATE_H
7
8
/**
9
* @file
10
* @ingroup api_sn
11
* Total service rate of a station served by heterogeneous server pools with a
12
* class-compatibility graph, and the peak that normalizes its utilization.
13
*
14
* Port of matlab/src/api/sn/sn_compat_rate.m. A pool t holds counts(t) identical
15
* servers, each running at rates(t), and may serve operand j when
16
* compat(t, j) is nonzero. The rate the station clears in state n is
17
*
18
* mu(n) = sum_t counts(t) * rates(t) * min(1, sum_{j: compat(t,j) != 0} n(j))
19
*
20
* the ACTIVATED-SERVER law: a pool contributes its full rate as soon as it is
21
* compatible with at least one operand PRESENT. This is the order-independent
22
* reading of a compatibility structure -- at an INTEGER state mu depends on n
23
* only through its SUPPORT, so it is invariant to the arrival order and to any
24
* permutation of the microstate, which is exactly the condition an OI station
25
* has to meet (Dorsman & Gardner, Queueing Systems 107:205-256, 2024, Fig. 1).
26
* It is also what pas_compatibility_5class.m encodes for a flat Network, so the
27
* layered and flat readings of one compatibility matrix agree.
28
*
29
* WHY min(1, .) AND NOT AN INDICATOR. At every integer state the two agree
30
* exactly -- a pool with at least one compatible job present is fully active,
31
* one with none is idle -- so nothing about the OI law on the real state
32
* lattice changes. They part company only at a FRACTIONAL argument, which is
33
* what a mean-value solver hands this function: AMVA evaluates the rate at a
34
* mean population, and under a hard indicator any operand with a mean above
35
* zero, however small, activates every pool it touches. A compatibility
36
* structure would then be invisible to AMVA whenever every operand is a little
37
* bit busy -- which is nearly always. Scaling linearly below one job keeps the
38
* structure visible at the evaluation point while leaving the integer-state law
39
* untouched; it is the ordinary continuous relaxation of a step function, and
40
* the CTMC and simulation paths, which only ever evaluate at integer states,
41
* cannot tell the difference.
42
*
43
* IT IS NOT A MATCHING. A pool of two servers compatible with a class holding
44
* ONE job contributes both servers here, which over-counts against a
45
* non-redundant system where one server serves one job. That is deliberate:
46
* the matching size depends on the counts and not only on the support, so it is
47
* NOT order-independent and would take the station outside the product form the
48
* OI closure is built on. A model that means the matching wants a different
49
* station, not a different reading of this one.
50
*
51
* WHY THE PEAK IS SEPARATE. Utilization at a rate-scaled station is reported as
52
* U = T*S/peak, and the peak is the rate with every pool active, sum_t
53
* counts(t)*rates(t). It is a property of the DECLARATION, not of a state, so
54
* it is computed once and handed to the solver beside the handle rather than
55
* recovered from mu at a guessed state.
56
*/
57
58
#include <cstddef>
59
#include <vector>
60
61
#include "
line/num/number.h
"
62
#include "
line/util/error.h
"
63
#include "
line/util/matrix.h
"
64
65
namespace
line
{
66
namespace
api
{
67
68
/**
69
* Rate cleared by the pools when the operands in `n` are present.
70
*
71
* @param compat (npools x noperands), nonzero where the pool may serve
72
* @param counts (npools) servers held by each pool
73
* @param rates (npools) per-server rate of each pool
74
* @param n (noperands) per-operand population, integer or fractional
75
*/
76
template
<
class
T>
77
T
sn_compat_rate
(
const
Matrix<T>
& compat,
const
std::vector<double>& counts,
78
const
std::vector<T>& rates,
const
std::vector<T>& n) {
79
const
T zero =
num_traits<T>::from_int
(0);
80
const
std::size_t npools = counts.size();
81
if
(rates.size() != npools)
82
throw
InputError
(
"sn_compat_rate: one rate per pool is required"
);
83
if
(compat.
rows
() != npools)
84
throw
InputError
(
"sn_compat_rate: compat must have one row per pool"
);
85
if
(n.size() != compat.
cols
())
86
throw
InputError
(
"sn_compat_rate: n must have one entry per operand"
);
87
const
T one =
num_traits<T>::from_int
(1);
88
T mu = zero;
89
for
(std::size_t t = 0; t < npools; ++t) {
90
// The pool is activated ONCE by the jobs it can reach, not once per
91
// operand: its weight is the compatible load, capped at one job.
92
T load = zero;
93
for
(std::size_t j = 0; j < n.size(); ++j)
94
if
(compat(t, j) != zero && n[j] > zero) load = T(load + n[j]);
95
if
(load > one) load = one;
96
mu = mu +
num_traits<T>::from_double
(counts[t]) * rates[t] * load;
97
}
98
return
mu;
99
}
100
101
/** Rate with every pool active: sum_t counts(t)*rates(t). Normalizes U = T*S/peak. */
102
template
<
class
T>
103
T
sn_compat_peak
(
const
std::vector<double>& counts,
const
std::vector<T>& rates) {
104
const
T zero =
num_traits<T>::from_int
(0);
105
if
(rates.size() != counts.size())
106
throw
InputError
(
"sn_compat_peak: one rate per pool is required"
);
107
T peak = zero;
108
for
(std::size_t t = 0; t < counts.size(); ++t)
109
peak = peak +
num_traits<T>::from_double
(counts[t]) * rates[t];
110
return
peak;
111
}
112
113
/**
114
* Rate scaling eta(n) a compatibility declaration imposes on its station.
115
*
116
* This is what SolverLN carries onto the layer station, and it is NOT
117
* `sn_compat_rate / sn_compat_peak`. The denominator is the rate the SAME
118
* population would obtain under FULL compatibility,
119
*
120
* eta(n) = mu(n) / (peak * min(1, sum_j n(j)))
121
*
122
* so eta isolates the effect of the compatibility GRAPH and nothing else. The
123
* denominator DAMPS BY OCCUPANCY RELATIVE TO THE SERVER COUNT, min(1, N/S),
124
* because that is what the solver's own multiserver term contributes: it
125
* applies min(N,S) servers at the average server rate peak/S, so
126
*
127
* min(N,S) * (peak/S) * eta(n) = mu(n)
128
*
129
* and the station clears the activated-server rate exactly, at every state.
130
* Damping by min(1, N) instead -- which this did until 2026-08-28 -- left the
131
* effective law at min(N,S)/S * mu(n) and cancelled the REDUNDANCY SPEED-UP the
132
* activated-server law exists to express: a pool of S servers facing one
133
* compatible job clears S, not 1, because every one works on it and the first
134
* to finish cancels the rest. eta is therefore above one at low occupancy,
135
* which is the speed-up of servers that would otherwise be idle.
136
*/
137
template
<
class
T>
138
T
sn_compat_scaling
(
const
Matrix<T>
& compat,
const
std::vector<double>& counts,
139
const
std::vector<T>& rates,
const
std::vector<T>& n) {
140
const
T zero =
num_traits<T>::from_int
(0);
141
const
T one =
num_traits<T>::from_int
(1);
142
T total = zero;
143
for
(std::size_t j = 0; j < n.size(); ++j)
144
if
(n[j] > zero) total = T(total + n[j]);
145
if
(!(total > zero))
return
one;
// an empty station: nothing to scale
146
T nservers = zero;
147
for
(std::size_t t = 0; t < counts.size(); ++t)
148
nservers = T(nservers +
num_traits<T>::from_double
(counts[t]));
149
if
(!(nservers > zero))
return
one;
150
const
T occ = T(total / nservers);
151
T ref =
sn_compat_peak
(counts, rates) * (occ > one ? one : occ);
152
return
T(
sn_compat_rate
(compat, counts, rates, n) / ref);
153
}
154
155
}
// namespace api
156
}
// namespace line
157
158
#endif
// LINE_API_SN_SN_COMPAT_RATE_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::Matrix
Definition
matrix.h:56
line::Matrix::cols
std::size_t cols() const
Definition
matrix.h:90
line::Matrix::rows
std::size_t rows() const
Definition
matrix.h:89
error.h
The exception types the port throws.
matrix.h
Dense matrix and non-owning view.
line::api
Definition
infer_fmlps.h:69
line::api::sn_compat_rate
T sn_compat_rate(const Matrix< T > &compat, const std::vector< double > &counts, const std::vector< T > &rates, const std::vector< T > &n)
Rate cleared by the pools when the operands in n are present.
Definition
sn_compat_rate.h:77
line::api::sn_compat_scaling
T sn_compat_scaling(const Matrix< T > &compat, const std::vector< double > &counts, const std::vector< T > &rates, const std::vector< T > &n)
Rate scaling eta(n) a compatibility declaration imposes on its station.
Definition
sn_compat_rate.h:138
line::api::sn_compat_peak
T sn_compat_peak(const std::vector< double > &counts, const std::vector< T > &rates)
Rate with every pool active: sum_t counts(t)*rates(t).
Definition
sn_compat_rate.h:103
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
line::num_traits
Definition
number.h:111
include
line
api
sn
sn_compat_rate.h
Generated by
1.18.0