LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_perm.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_PERM_H
6#define LINE_API_PFQN_PERM_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Permanent of a matrix with repeated columns, by Ryser's formula.
12 *
13 * Templated port of matlab/src/util/perm.m, which the pfqn family uses in two
14 * places: the joint queue-length probability pfqn_joint (through its local
15 * Fper) and the LCFS normalizing constant pfqn_lcfsqn_nc.
16 *
17 * For an (n x R) matrix A whose column k is repeated m_k times, n = sum_k m_k,
18 * the permanent of the expanded (n x n) matrix is
19 *
20 * perm = (-1)^n sum_{0 <= f <= m} (-1)^{|f|} prod_k C(m_k, f_k)
21 * prod_{i=1}^{n} sum_k f_k A(i,k),
22 *
23 * which costs prod_k (m_k + 1) evaluations rather than the n! of the
24 * definition. Collapsing the repeated columns is what makes the formula usable
25 * here at all: the queueing applications have n jobs but only R distinct
26 * classes, and R is small.
27 *
28 * Arithmetic: EXACT-CAPABLE. Additions, multiplications and integer binomials
29 * only; the binomials are formed by the exact Pascal recurrence of num_nck,
30 * not by the floating-point nck, so a large multiplicity does not lose
31 * integrality.
32 */
33
34#include <cstddef>
35#include <vector>
36
37#include "line/num/number.h"
38#include "line/util/error.h"
39#include "line/util/matrix.h"
41
42namespace line {
43namespace pfqn {
44
45/**
46 * @brief Permanent of a matrix with repeated columns, by Ryser's formula.
47 *
48 * @param A (n x R) matrix, column k standing for m_k identical columns
49 * @param m (R) column multiplicities, summing to n
50 */
51template <class T>
52T pfqn_perm(const Matrix<T>& A, const std::vector<int>& m) {
53 const std::size_t R = m.size();
54 if (A.cols() != R) throw InputError("pfqn_perm: A and the multiplicities disagree in width");
55 int n = 0;
56 for (int v : m) {
57 if (v < 0) throw InputError("pfqn_perm: negative multiplicity");
58 n += v;
59 }
60 if (static_cast<int>(A.rows()) != n)
61 throw InputError("pfqn_perm: A must have sum(m) rows");
62 const T zero = num_traits<T>::from_int(0);
63 const T one = num_traits<T>::from_int(1);
64 if (n == 0) return one;
65
66 T val = zero;
67 std::vector<int> f(R, 0);
68 bool more = true;
69 while (more) {
70 int fs = 0;
71 for (int v : f) fs += v;
72 T term = (fs % 2 == 0) ? one : -one;
73 for (std::size_t k = 0; k < R; ++k) term *= num_nck<T>(m[k], f[k]);
74 for (std::size_t i = 0; i < static_cast<std::size_t>(n); ++i) {
75 T s = zero;
76 for (std::size_t k = 0; k < R; ++k)
77 if (f[k] != 0) s += num_traits<T>::from_int(f[k]) * A(i, k);
78 term *= s;
79 }
80 val += term;
81 more = next_pop(f, m);
82 }
83 return (n % 2 == 0) ? val : T(-val);
84}
85
86} // namespace pfqn
87} // namespace line
88
89#endif // LINE_API_PFQN_PERM_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
The exception types the port throws.
Dense matrix and non-owning view.
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
bool next_pop(std::vector< int > &n, const std::vector< int > &N)
Advance n to the next population vector in the lattice 0 <= n <= N, odometer order with the last clas...
Definition population.h:56
Number-type abstraction for the templated API port.
Population-vector enumeration and combinatorics.