GF2++
Loading...
Searching...
No Matches
BitGauss.h
Go to the documentation of this file.
1#pragma once
2// SPDX-FileCopyrightText: 2025 Nessan Fitzmaurice <nzznfitz+gh@icloud.com>
3// SPDX-License-Identifier: MIT
4
8
9#include <gf2/BitMat.h>
10
11namespace gf2 {
12
43template<Unsigned Word>
44class BitGauss {
45private:
46 BitMat<Word> m_A; // The reduced row echelon form of the matrix `A`.
47 BitVec<Word> m_b; // The equivalent reduced row echelon form of the vector `b`.
48 usize m_rank; // The rank of the matrix `A`.
49 usize m_solutions; // The number of solutions to the system.
50 std::vector<usize> m_free; // The indices of the free variables in the system.
51
52public:
68 template<BitStore Rhs>
69 BitGauss(BitMat<Word> const& A, Rhs const& b)
70 requires std::same_as<typename Rhs::word_type, Word>
71 {
72 gf2_assert(A.is_square(), "Matrix is {} x {} but it should be square!", A.rows(), A.cols());
73 gf2_assert(A.rows() == b.size(), "Matrix has {} rows but the RHS vector has {} elements.", A.rows(), b.size());
74
75 // Copy A & augment it with b on the right.
76 m_A = A;
77 m_A.append_col(b);
78
79 // Reduce the augmented matrix to reduced row echelon form.
80 auto has_pivot = m_A.to_reduced_echelon_form();
81
82 // Grab the last column of the reduced augmented matrix as a standalone vector, removing it from the matrix.
83 m_b = m_A.remove_col().value();
84
85 // Also pop the last element of the `has_pivot` bit-vector as it corresponds to the column that we just removed.
86 has_pivot.pop();
87
88 // The rank is the number of columns with a pivot (it is also the number if non-zero rows in the reduced
89 // matrix).
90 m_rank = has_pivot.count_ones();
91
92 // Any columns without a pivot are free variables.
93 for (auto i = 0uz; i < has_pivot.size(); ++i) {
94 if (!has_pivot[i]) m_free.push_back(i);
95 }
96
97 // Check that all zero rows in the reduced matrix are matched by zero elements in the equivalent reduced right
98 // hand side vector (this is consistency). Zero rows are at the bottom from `rank` to the end.
99 auto consistent = true;
100 for (auto i = m_rank; i < m_A.rows(); ++i) {
101 if (m_b[i]) {
102 consistent = false;
103 break;
104 }
105 }
106
107 // The number of solutions we can index into is either 0 or 2^f where f is the number of free variables.
108 // However, for practical reasons we limit the number of solutions to `min(2^f, 2^63)`.
109 m_solutions = 0;
110 if (consistent) {
111 auto f = m_free.size();
112 auto b_max = static_cast<usize>(BITS<Word> - 1);
113 auto f_max = std::min(f, b_max);
114 m_solutions = 1uz << f_max;
115 }
116 }
117
127 constexpr usize rank() const { return m_rank; }
128
138 constexpr usize free_count() const { return m_free.size(); }
139
149 constexpr bool is_underdetermined() const { return !m_free.empty(); }
150
162 constexpr bool is_consistent() const { return m_solutions > 0; }
163
178 constexpr usize solution_count() const { return m_solutions; }
179
193 std::optional<BitVec<Word>> operator()() const {
194 if (!is_consistent()) return std::nullopt;
195
196 // Create a random starting point.
197 auto x = BitVec<Word>::random(m_b.size());
198
199 // All non-free variables will be overwritten by back substitution.
200 back_substitute_into(x);
201 return x;
202 }
203
226 std::optional<BitVec<Word>> operator()(usize i_solution) const {
227 if (!is_consistent()) { return std::nullopt; }
228 if (i_solution > solution_count()) { return std::nullopt; }
229
230 // We start with a zero vector and then set the free variable slots to the fixed bit pattern for `i`.
231 auto x = BitVec<Word>::zeros(m_b.size());
232 for (auto f = 0uz; f < m_free.size(); ++f) {
233 x[m_free[f]] = (i_solution & 1);
234 i_solution >>= 1;
235 }
236
237 // Back substitution will now overwrite any non-free variables in `x` with their correct values.
238 back_substitute_into(x);
239 return x;
240 }
241
242private:
243 // Helper function that performs back substitution to solve for the non-free variables in `x`.
244 void back_substitute_into(BitVec<Word>& x) const {
245 // Iterate from the bottom up, starting at the first non-zero row, solving for the non-free variables in `x`.
246 for (auto i = m_rank; i--;) {
247 auto j = m_A[i].first_set().value();
248 x.set(j, m_b[i]);
249 for (auto k = j + 1; k < x.size(); ++k)
250 if (m_A(i, k)) { x.set(j, x[j] ^ x[k]); }
251 }
252 }
253};
254
255} // namespace gf2
Matrices over over GF(2) – bit-matrices. See the BitMat page for more details.
#define gf2_assert(cond,...)
A confirmation macro that checks a boolean condition cond. On failure, the confirmation prints an e...
Definition assert.h:98
constexpr bool is_underdetermined() const
Returns true if the system is underdetermined.
Definition BitGauss.h:149
constexpr bool is_consistent() const
Returns true if the system of linear equations A.x_for = b is consistent.
Definition BitGauss.h:162
BitGauss(BitMat< Word > const &A, Rhs const &b)
Constructs a new BitGauss object where we are solving the system of linear equations A....
Definition BitGauss.h:69
std::optional< BitVec< Word > > operator()() const
Returns a solution to the system of linear equations A.x_for = b or std::nullopt if the system is inc...
Definition BitGauss.h:193
constexpr usize solution_count() const
Returns the maximum number of solutions we can index into.
Definition BitGauss.h:178
constexpr usize rank() const
Returns the rank of the matrix A.
Definition BitGauss.h:127
constexpr usize free_count() const
Returns the number of free variables in the system.
Definition BitGauss.h:138
std::optional< BitVec< Word > > operator()(usize i_solution) const
Returns the ith solution to the system of linear equations A.x_for = b or std::nullopt if the system ...
Definition BitGauss.h:226
A dynamically-sized matrix over GF(2) stored as a vector of bit-vectors representing the rows of the ...
Definition BitMat.h:32
A dynamically-sized vector over GF(2) with bit elements compactly stored in a standard vector of prim...
Definition BitVec.h:23
constexpr usize size() const
Returns the number of bit-elements in the bit-vector.
Definition BitVec.h:50
auto set(usize i, bool value=true)
Sets the bit-element i to the specified boolean value & returns this for chaining....
Definition BitVec.h:965
static constexpr BitVec zeros(usize n)
Factory method to generate a bit-vector of length n where the elements are all 0.
Definition BitVec.h:194
static BitVec random(usize len, double p=0.5, u64 seed=0)
Factory method to generate a bit-vector of size len where the elements are picked at random.
Definition BitVec.h:345
The namespace for the gf2 library.
Definition assert.h:119
std::size_t usize
Word type alias for the platform's "native"-sized unsigned integer.
Definition Unsigned.h:42
constexpr u8 BITS
The number of bits in an Unsigned returned as a u8.
Definition Unsigned.h:55