5#ifndef LINE_API_PERM_PERMANENT_H
6#define LINE_API_PERM_PERMANENT_H
67T binom(std::size_t n, std::size_t k) {
70 for (std::size_t i = 0; i < k; ++i)
78void unique_columns(
const Matrix<T>& m, Matrix<T>* uniq, std::vector<std::size_t>* mult) {
79 const std::size_t n = m.rows(), c = m.cols();
80 std::vector<std::size_t> rep;
82 for (std::size_t j = 0; j < c; ++j) {
84 for (std::size_t g = 0; g < rep.size() && !found; ++g) {
86 for (std::size_t i = 0; i < n && same; ++i)
87 if (m(i, j) != m(i, rep[g])) same =
false;
98 *uniq =
Matrix<T>(n, rep.size(), num_traits<T>::from_int(0));
99 for (std::size_t g = 0; g < rep.size(); ++g)
100 for (std::size_t i = 0; i < n; ++i) (*uniq)(i, g) = m(i, rep[g]);
104inline bool pprod_next(std::vector<std::size_t>& f,
const std::vector<std::size_t>& mult) {
105 for (std::size_t k = 0; k < f.size(); ++k) {
106 if (f[k] < mult[k]) {
127 const std::size_t n = m.
rows();
129 if (m.
cols() != n)
throw InputError(
"permanent: the matrix must be square");
132 std::vector<std::size_t> mult;
133 permdetail::unique_columns(m, &uniq, &mult);
134 const std::size_t R = mult.size();
137 std::vector<std::size_t> f(R, 0);
139 std::size_t fsum = 0;
140 for (std::size_t k = 0; k < R; ++k) fsum += f[k];
142 for (std::size_t j = 0; j < R; ++j) term *= permdetail::binom<T>(mult[j], f[j]);
143 for (std::size_t i = 0; i < n; ++i) {
145 for (std::size_t k = 0; k < R; ++k)
150 }
while (permdetail::pprod_next(f, mult));
157 const std::size_t n = m.
rows();
159 if (m.
cols() != n)
throw InputError(
"permanent: the matrix must be square");
160 if (n > 30)
throw InputError(
"permanent_ryser: 2^n subsets is not enumerable past n = 30");
163 const unsigned long long lim = 1ULL << n;
164 for (
unsigned long long sub = 0; sub < lim; ++sub) {
165 std::size_t bits = 0;
166 for (std::size_t j = 0; j < n; ++j)
167 if (sub & (1ULL << j)) ++bits;
169 for (std::size_t i = 0; i < n; ++i) {
171 for (std::size_t j = 0; j < n; ++j)
172 if (sub & (1ULL << j)) rs += m(i, j);
188 const std::size_t n = m.
rows();
190 if (m.
cols() != n)
throw InputError(
"permanent: the matrix must be square");
191 if (n > 30)
throw InputError(
"permanent_ryser_gray: 2^n steps is not enumerable past n = 30");
194 std::vector<char> on(n, 0);
196 std::size_t bits = 0;
198 const unsigned long long steps = (1ULL << n) - 1ULL;
199 for (
unsigned long long s = 1; s <= steps; ++s) {
203 unsigned long long t = s;
204 while ((t & 1ULL) == 0ULL) {
210 for (std::size_t i = 0; i < n; ++i) rowsum[i] += sign * m(i, j);
211 bits = on[j] ? bits + 1 : bits - 1;
214 for (std::size_t i = 0; i < n; ++i) prod *= rowsum[i];
223 const std::size_t n = m.
rows();
225 if (m.
cols() != n)
throw InputError(
"permanent: the matrix must be square");
226 if (n > 12)
throw InputError(
"permanent_naive: n! is not enumerable past n = 12");
228 std::vector<std::size_t> p(n);
229 for (std::size_t i = 0; i < n; ++i) p[i] = i;
233 for (std::size_t i = 0; i < n; ++i) prod *= m(i, p[i]);
235 }
while (std::next_permutation(p.begin(), p.end()));
253 throw InputError(
"permanent: unknown method");
271 if (!(tolerance > 0.0))
throw InputError(
"snap_to_lattice: the tolerance must be positive");
273 for (std::size_t i = 0; i < m.
rows(); ++i)
274 for (std::size_t j = 0; j < m.
cols(); ++j) {
The exception types the port throws.
Dense matrix and non-owning view.
T permanent_naive(const Matrix< T > &m)
Every permutation: n!
T permanent_ryser_gray(const Matrix< T > &m)
Ryser's formula in GRAY-CODE order: O(2^n n).
T permanent_multiplicity(const Matrix< T > &m)
Inclusion-exclusion over the DISTINCT columns.
Matrix< T > snap_to_lattice(const Matrix< T > &m, double tolerance=0.001)
Round a matrix's entries onto a coarse lattice so repeated columns are found.
PermMethod
Which algorithm permanent should use.
T permanent_ryser(const Matrix< T > &m)
Ryser's formula over explicit column subsets: O(2^n n^2).
T permanent(const Matrix< T > &m, PermMethod method=PermMethod::Multiplicity)
The permanent, by the chosen method.
Number-type abstraction for the templated API port.