LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_unique.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_UNIQUE_H
6#define LINE_API_PFQN_UNIQUE_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Station consolidation and its inverse: merge identical demand rows into one
12 * station with a multiplicity, and expand per-station results back.
13 *
14 * Templated port of matlab/src/api/pfqn/pfqn_unique.m and pfqn_expand.m, plus
15 * the load-dependent rate shift pfqn_mushift.m.
16 *
17 * One deliberate divergence from MATLAB, the same one already taken in
18 * pfqn_recal: MATLAB compares rows within GlobalConstants.Zero() (1e-14), so it
19 * merges stations whose demands only nearly agree. A tolerance-based merge
20 * perturbs the normalizing constant, which is unacceptable for an
21 * exact-capable algorithm and has no meaning at all in the rational field. The
22 * port merges on exact equality. Callers that want tolerant merging should
23 * round their demands before calling, where the rounding is visible.
24 */
25
26#include <cstddef>
27#include <vector>
28
29#include "line/num/number.h"
30#include "line/util/error.h"
31#include "line/util/matrix.h"
32
33namespace line {
34namespace pfqn {
35
36template <class T>
38 Matrix<T> L; ///< consolidated demands (Mu x R)
39 Matrix<T> mu; ///< consolidated LD rates, empty if none
40 Matrix<T> gamma; ///< consolidated CD scalings, empty if none
41 std::vector<int> mi; ///< multiplicity of each consolidated station
42 std::vector<std::size_t> mapping; ///< original station -> consolidated index
43};
44
45/** Merge stations whose (L, mu, gamma) rows are exactly equal. */
46template <class T>
47UniqueResult<T> pfqn_unique(const Matrix<T>& L, const Matrix<T>& mu, const Matrix<T>& gamma) {
48 const std::size_t M = L.rows(), R = L.cols();
49 if (!mu.empty() && mu.rows() != M) throw InputError("pfqn_unique: mu has the wrong row count");
50 if (!gamma.empty() && gamma.rows() != M)
51 throw InputError("pfqn_unique: gamma has the wrong row count");
52
54 r.mapping.assign(M, 0);
55 std::vector<std::size_t> reps; // representative original index per group
56
57 const auto rows_equal = [&](std::size_t a, std::size_t b) {
58 for (std::size_t j = 0; j < R; ++j)
59 if (L(a, j) != L(b, j)) return false;
60 for (std::size_t j = 0; j < mu.cols(); ++j)
61 if (mu(a, j) != mu(b, j)) return false;
62 for (std::size_t j = 0; j < gamma.cols(); ++j)
63 if (gamma(a, j) != gamma(b, j)) return false;
64 return true;
65 };
66
67 for (std::size_t i = 0; i < M; ++i) {
68 bool merged = false;
69 for (std::size_t g = 0; g < reps.size(); ++g) {
70 if (rows_equal(i, reps[g])) {
71 r.mapping[i] = g;
72 r.mi[g] += 1;
73 merged = true;
74 break;
75 }
76 }
77 if (!merged) {
78 r.mapping[i] = reps.size();
79 reps.push_back(i);
80 r.mi.push_back(1);
81 }
82 }
83
84 r.L = Matrix<T>(reps.size(), R);
85 for (std::size_t g = 0; g < reps.size(); ++g)
86 for (std::size_t j = 0; j < R; ++j) r.L(g, j) = L(reps[g], j);
87 if (!mu.empty()) {
88 r.mu = Matrix<T>(reps.size(), mu.cols());
89 for (std::size_t g = 0; g < reps.size(); ++g)
90 for (std::size_t j = 0; j < mu.cols(); ++j) r.mu(g, j) = mu(reps[g], j);
91 }
92 if (!gamma.empty()) {
93 r.gamma = Matrix<T>(reps.size(), gamma.cols());
94 for (std::size_t g = 0; g < reps.size(); ++g)
95 for (std::size_t j = 0; j < gamma.cols(); ++j) r.gamma(g, j) = gamma(reps[g], j);
96 }
97 return r;
98}
99
100template <class T>
104
105/**
106 * Fold a caller-supplied multiplicity vector along a consolidation mapping.
107 *
108 * Port of pfqn_combine_mi in
109 * jar/src/main/java/jline/api/pfqn/Pfqn_replicas.java, the third member of the
110 * replica trio alongside pfqn_unique and pfqn_expand. MATLAB has no
111 * counterpart.
112 *
113 * When the caller already carries its own per-station multiplicities mi (a
114 * station standing for mi(i) identical replicas) and pfqn_unique then merges
115 * further stations, the two multiplicities must COMPOSE: the consolidated
116 * station g represents sum over the original stations mapped to g of mi(i).
117 * The result is therefore a SUM along the mapping, not a count of the group,
118 * which is what makes it different from the group sizes pfqn_unique itself
119 * returns.
120 *
121 * Arithmetic: EXACT-CAPABLE. Integer addition only.
122 *
123 * @param mi (M) multiplicity of each original station
124 * @param mapping (M) original station -> consolidated index, as pfqn_unique
125 * returns it
126 * @param M_unique number of consolidated stations
127 * @return (M_unique) combined multiplicities
128 */
129inline std::vector<int> pfqn_combine_mi(const std::vector<int>& mi,
130 const std::vector<std::size_t>& mapping,
131 std::size_t M_unique) {
132 if (mi.size() != mapping.size())
133 throw InputError("pfqn_combine_mi: mi and mapping have different lengths");
134 std::vector<int> combined(M_unique, 0);
135 for (std::size_t i = 0; i < mapping.size(); ++i) {
136 if (mapping[i] >= M_unique)
137 throw InputError("pfqn_combine_mi: mapping index out of range");
138 combined[mapping[i]] += mi[i];
139 }
140 return combined;
141}
142
143// pfqn_expand and pfqn_mushift live in their own headers, matching the 1:1
144// file-per-function convention: see pfqn_expand.h and pfqn_mushift.h.
145
146} // namespace pfqn
147} // namespace line
148
149#endif // LINE_API_PFQN_UNIQUE_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
bool empty() const
Definition matrix.h:92
The exception types the port throws.
Dense matrix and non-owning view.
std::vector< int > pfqn_combine_mi(const std::vector< int > &mi, const std::vector< std::size_t > &mapping, std::size_t M_unique)
Fold a caller-supplied multiplicity vector along a consolidation mapping.
UniqueResult< T > pfqn_unique(const Matrix< T > &L, const Matrix< T > &mu, const Matrix< T > &gamma)
Merge stations whose (L, mu, gamma) rows are exactly equal.
Definition pfqn_unique.h:47
Number-type abstraction for the templated API port.
std::vector< int > mi
multiplicity of each consolidated station
Definition pfqn_unique.h:41
Matrix< T > mu
consolidated LD rates, empty if none
Definition pfqn_unique.h:39
Matrix< T > L
consolidated demands (Mu x R)
Definition pfqn_unique.h:38
Matrix< T > gamma
consolidated CD scalings, empty if none
Definition pfqn_unique.h:40
std::vector< std::size_t > mapping
original station -> consolidated index
Definition pfqn_unique.h:42