LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_comb_common.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_COMB_COMMON_H
6#define LINE_API_PFQN_COMB_COMMON_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Integer-composition enumeration shared by the CoMoM and MVAC ports.
12 *
13 * Templated-free helpers mirroring matlab/src/util/multichoose.m and
14 * matlab/src/util/matchrow.m. Both are pure index arithmetic on integers, so
15 * they carry no number type at all and are usable from any instantiation.
16 *
17 * ORDERING MATTERS. MATLAB's multichoose(n,k) recurses as
18 *
19 * for i = 0:k, rows = [ i , multichoose(n-1, k-i) ]
20 *
21 * so the first component ascends slowest. Several callers (pfqn_comom's basis
22 * layout, pfqn_mvac's multiplicity list) index into the result by position
23 * rather than by content, and a different but equally valid enumeration order
24 * would silently permute their bases. The recursion is reproduced verbatim
25 * rather than replaced by a "nicer" odometer.
26 *
27 * `line::multichoose` in util/population.h returns the COUNT C(n+k-1,k);
28 * `multichoose_rows` here returns the compositions themselves. The names are
29 * kept distinct so that a call site cannot pick up the wrong one.
30 */
31
32#include <cstddef>
33#include <vector>
34
35#include "line/util/error.h"
36
37namespace line {
38namespace pfqn {
39
40/**
41 * All n-vectors of nonnegative integers summing to k, in MATLAB
42 * multichoose(n,k) order.
43 */
44inline std::vector<std::vector<int>> multichoose_rows(int n, int k) {
45 if (n < 1) throw InputError("multichoose_rows: at least one component is required");
46 if (k < 0) throw InputError("multichoose_rows: negative total");
47 std::vector<std::vector<int>> out;
48 if (n == 1) {
49 out.push_back(std::vector<int>(1, k));
50 return out;
51 }
52 if (k == 0) {
53 out.push_back(std::vector<int>(static_cast<std::size_t>(n), 0));
54 return out;
55 }
56 for (int i = 0; i <= k; ++i) {
57 const std::vector<std::vector<int>> w = multichoose_rows(n - 1, k - i);
58 for (std::size_t j = 0; j < w.size(); ++j) {
59 std::vector<int> row;
60 row.reserve(static_cast<std::size_t>(n));
61 row.push_back(i);
62 row.insert(row.end(), w[j].begin(), w[j].end());
63 out.push_back(row);
64 }
65 }
66 return out;
67}
68
69/**
70 * Position of `row` in `rows`, or -1 when absent. MATLAB's matchrow checks the
71 * LAST row first and otherwise returns the first match; with the distinct row
72 * sets used here the two rules coincide, and the first-match rule is used.
73 */
74inline int matchrow(const std::vector<std::vector<int>>& rows, const std::vector<int>& row) {
75 for (std::size_t i = 0; i < rows.size(); ++i) {
76 if (rows[i].size() != row.size()) return -2;
77 bool eq = true;
78 for (std::size_t j = 0; j < row.size(); ++j)
79 if (rows[i][j] != row[j]) {
80 eq = false;
81 break;
82 }
83 if (eq) return static_cast<int>(i);
84 }
85 return -1;
86}
87
88/**
89 * MATLAB's sortbynnzpos: a stable bubble sort putting the rows with FEWER
90 * nonzeros first and, among rows with equally many, the row whose leftmost
91 * differing entry is nonzero first. Reproduced exactly, including the O(n^2)
92 * shape, because the resulting order is the basis layout that pfqn_comom and
93 * pfqn_procomom index into.
94 */
95inline void sort_by_nnz_pos(std::vector<std::vector<int>>& I) {
96 const auto nnz = [](const std::vector<int>& v) {
97 int c = 0;
98 for (int x : v)
99 if (x != 0) ++c;
100 return c;
101 };
102 // returns true when i1 must come AFTER i2 (MATLAB nnzcmp(i1,i2) == 1)
103 const auto after = [&](const std::vector<int>& i1, const std::vector<int>& i2) {
104 const int n1 = nnz(i1), n2 = nnz(i2);
105 if (n1 > n2) return true;
106 if (n1 < n2) return false;
107 for (std::size_t j = 0; j < i1.size(); ++j) {
108 if (i1[j] == 0 && i2[j] > 0) return true;
109 if (i1[j] > 0 && i2[j] == 0) return false;
110 }
111 return false;
112 };
113 for (std::size_t i = 0; i + 1 < I.size(); ++i)
114 for (std::size_t j = i + 1; j < I.size(); ++j)
115 if (after(I[i], I[j])) std::swap(I[i], I[j]);
116}
117
118} // namespace pfqn
119} // namespace line
120
121#endif // LINE_API_PFQN_COMB_COMMON_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
int matchrow(const std::vector< std::vector< int > > &rows, const std::vector< int > &row)
Position of row in rows, or -1 when absent.
std::vector< std::vector< int > > multichoose_rows(int n, int k)
All n-vectors of nonnegative integers summing to k, in MATLAB multichoose(n,k) order.
void sort_by_nnz_pos(std::vector< std::vector< int > > &I)
MATLAB's sortbynnzpos: a stable bubble sort putting the rows with FEWER nonzeros first and,...