LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_lcfsqn_nc.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_PFQN_LCFSQN_NC_H
6
#define LINE_API_PFQN_LCFSQN_NC_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* Normalizing constant of the two-station multiclass LCFS network as a sum of
12
* PERMANENTS, the closed form of Casale, QUESTA 2026.
13
*
14
* Templated port of matlab/src/api/pfqn/pfqn_lcfsqn_nc.m.
15
*
16
* Splitting the job sequence at the position x of the boundary between the
17
* two stations gives
18
*
19
* G(N) = ( sum_{x=0}^{K} perm(A_x, N) ) / prod_r N_r!, K = sum_r N_r,
20
*
21
* where A_x is the (K x R) matrix of the per-position, per-class weights
22
*
23
* A_x(i,r) = alpha_r^i for i <= x (LCFS side)
24
* A_x(i,r) = alpha_r^{i-1} beta_r for i > x (LCFS-PR side)
25
*
26
* and the permanent is taken with column multiplicities N.
27
*
28
* TWO REFERENCE DEFECTS, both corrected here, both invisible at the default
29
* population N = ones(1,R) and both silent for any other one.
30
*
31
* 1. TRANSPOSED MATRIX. MATLAB's make_A builds the matrix transposed with
32
* respect to the way perm() then reads it: make_A fills A(class, position)
33
* over i = 1..R and j = 1..K, while perm(A, N) reads A(position, class)
34
* over i = 1..sum(N) and k = 1..R. The two indexings coincide only when
35
* K = R, i.e. exactly the default. For any larger population the rows
36
* i = R+1, ..., K of the matrix perm() actually reads are identically zero,
37
* so every product term in Ryser's formula contains a zero factor and the
38
* routine returns G = 0 -- silently, since zero is a representable constant
39
* and nothing downstream checks positivity.
40
*
41
* 2. MISSING prod_r N_r!. The permanent counts the ORDERED arrangements of the
42
* jobs, so it over-counts every state by the number of permutations WITHIN
43
* each class. The same division appears explicitly in the sibling routine
44
* pfqn_joint, whose local Fper ends in `perm(A)/prod(factorial(N))`; it is
45
* absent here. With N = ones the factorials are all 1, which is why the
46
* omission never showed.
47
*
48
* Both corrections are verified, not assumed: with them the closed form agrees
49
* with pfqn_lcfsqn_ca -- an independent recursion sharing no code -- as an
50
* EXACT rational on every population tested, and without either one it does
51
* not.
52
*
53
* Arithmetic: EXACT-CAPABLE, inheriting exactness from pfqn_perm. Note that
54
* the cost is (K+1) * prod_r (N_r + 1) * K * R, so this form is a closed-form
55
* cross-check on the recursion of pfqn_lcfsqn_ca rather than a replacement
56
* for it.
57
*/
58
59
#include <cstddef>
60
#include <vector>
61
62
#include "
line/api/pfqn/pfqn_perm.h
"
63
#include "
line/num/number.h
"
64
#include "
line/util/error.h
"
65
#include "
line/util/matrix.h
"
66
67
namespace
line
{
68
namespace
pfqn
{
69
70
/**
71
* @brief Normalizing constant of the two-station multiclass LCFS network as a
72
* sum of PERMANENTS, the closed form of Casale, QUESTA 2026.
73
*
74
* @param alpha (R) mean service times at the LCFS station
75
* @param beta (R) mean service times at the LCFS-PR station
76
* @param N (R) population per class
77
*/
78
template
<
class
T>
79
T
pfqn_lcfsqn_nc
(
const
std::vector<T>& alpha,
const
std::vector<T>& beta,
80
const
std::vector<int>& N) {
81
const
std::size_t R = alpha.size();
82
if
(beta.size() != R)
throw
InputError
(
"pfqn_lcfsqn_nc: alpha and beta have different lengths"
);
83
if
(N.size() != R)
throw
InputError
(
"pfqn_lcfsqn_nc: alpha and N have different lengths"
);
84
85
const
T one =
num_traits<T>::from_int
(1);
86
T G =
num_traits<T>::from_int
(0);
87
int
K = 0;
88
for
(
int
v : N) {
89
if
(v < 0)
throw
InputError
(
"pfqn_lcfsqn_nc: negative population"
);
90
K += v;
91
}
92
if
(K == 0)
return
one;
93
94
for
(
int
x = 0; x <= K; ++x) {
95
Matrix<T>
A(
static_cast<
std::size_t
>
(K), R);
96
for
(
int
i = 1; i <= K; ++i)
97
for
(std::size_t r = 0; r < R; ++r)
98
A(
static_cast<
std::size_t
>
(i - 1), r) =
99
i <= x ? num_pow_int(alpha[r], static_cast<unsigned>(i))
100
:
num_pow_int
(alpha[r],
static_cast<
unsigned
>
(i - 1)) * beta[r];
101
G +=
pfqn_perm
(A, N);
102
}
103
for
(
int
v : N) G /=
num_factorial<T>
(
static_cast<
unsigned
>
(v));
104
return
G;
105
}
106
107
}
// namespace pfqn
108
}
// namespace line
109
110
#endif
// LINE_API_PFQN_LCFSQN_NC_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::Matrix
Definition
matrix.h:56
error.h
The exception types the port throws.
matrix.h
Dense matrix and non-owning view.
line::pfqn
Definition
cd_peak_scaling.h:43
line::pfqn::pfqn_lcfsqn_nc
T pfqn_lcfsqn_nc(const std::vector< T > &alpha, const std::vector< T > &beta, const std::vector< int > &N)
Normalizing constant of the two-station multiclass LCFS network as a sum of PERMANENTS,...
Definition
pfqn_lcfsqn_nc.h:79
line::pfqn::pfqn_perm
T pfqn_perm(const Matrix< T > &A, const std::vector< int > &m)
Permanent of a matrix with repeated columns, by Ryser's formula.
Definition
pfqn_perm.h:52
line
Definition
aoi_dist2ph.h:52
line::num_factorial
T num_factorial(unsigned n)
Factorial as a value of T.
Definition
number.h:184
line::num_pow_int
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition
number.h:192
number.h
Number-type abstraction for the templated API port.
pfqn_perm.h
Permanent of a matrix with repeated columns, by Ryser's formula.
line::num_traits
Definition
number.h:111
include
line
api
pfqn
pfqn_lcfsqn_nc.h
Generated by
1.18.0