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/BitMatrix.h>
10
11namespace gf2 {
12
44template<Unsigned Word>
45class BitGauss {
46private:
47 BitMatrix<Word> m_A; // The reduced row echelon form of the matrix `A`.
48 BitVector<Word> m_b; // The equivalent reduced row echelon form of the vector `b`.
49 usize m_rank; // The rank of the matrix `A`.
50 usize m_solutions; // The number of solutions to the system.
51 std::vector<usize> m_free; // The indices of the free variables in the system.
52
53public:
70 template<BitStore Rhs>
71 BitGauss(BitMatrix<Word> const& A, Rhs const& b)
72 requires std::same_as<typename Rhs::word_type, Word>
73 {
74 gf2_assert(A.is_square(), "Matrix is {} x {} but it should be square!", A.rows(), A.cols());
75 gf2_assert(A.rows() == b.size(), "Matrix has {} rows but the RHS vector has {} elements.", A.rows(), b.size());
76
77 // Copy A & augment it with b on the right.
78 m_A = A;
79 m_A.append_col(b);
80
81 // Reduce the augmented matrix to reduced row echelon form.
82 auto has_pivot = m_A.to_reduced_echelon_form();
83
84 // Grab the last column of the reduced augmented matrix as a standalone vector, removing it from the matrix.
85 m_b = m_A.remove_col().value();
86
87 // Also pop the last element of the `has_pivot` bit-vector as it corresponds to the column that we just removed.
88 has_pivot.pop();
89
90 // The rank is the number of columns with a pivot (it is also the number if non-zero rows in the reduced
91 // matrix).
92 m_rank = has_pivot.count_ones();
93
94 // Any columns without a pivot are free variables.
95 for (auto i = 0uz; i < has_pivot.size(); ++i) {
96 if (!has_pivot[i]) m_free.push_back(i);
97 }
98
99 // Check that all zero rows in the reduced matrix are matched by zero elements in the equivalent reduced right
100 // hand side vector (this is consistency). Zero rows are at the bottom from `rank` to the end.
101 auto consistent = true;
102 for (auto i = m_rank; i < m_A.rows(); ++i) {
103 if (m_b[i]) {
104 consistent = false;
105 break;
106 }
107 }
108
109 // The number of solutions we can index into is either 0 or 2^f where f is the number of free variables.
110 // However, for practical reasons we limit the number of solutions to `min(2^f, 2^63)`.
111 m_solutions = 0;
112 if (consistent) {
113 auto f = m_free.size();
114 auto b_max = static_cast<usize>(BITS<Word> - 1);
115 auto f_max = std::min(f, b_max);
116 m_solutions = 1uz << f_max;
117 }
118 }
119
129 constexpr usize rank() const { return m_rank; }
130
140 constexpr usize free_count() const { return m_free.size(); }
141
151 constexpr bool is_underdetermined() const { return !m_free.empty(); }
152
164 constexpr bool is_consistent() const { return m_solutions > 0; }
165
180 constexpr usize solution_count() const { return m_solutions; }
181
195 std::optional<BitVector<Word>> operator()() const {
196 if (!is_consistent()) return std::nullopt;
197
198 // Create a random starting point.
199 auto x = BitVector<Word>::random(m_b.size());
200
201 // All non-free variables will be overwritten by back substitution.
202 back_substitute_into(x);
203 return x;
204 }
205
228 std::optional<BitVector<Word>> operator()(usize i_solution) const {
229 if (!is_consistent()) { return std::nullopt; }
230 if (i_solution > solution_count()) { return std::nullopt; }
231
232 // We start with a zero vector and then set the free variable slots to the fixed bit pattern for `i`.
233 auto x = BitVector<Word>::zeros(m_b.size());
234 for (auto f = 0uz; f < m_free.size(); ++f) {
235 x[m_free[f]] = (i_solution & 1);
236 i_solution >>= 1;
237 }
238
239 // Back substitution will now overwrite any non-free variables in `x` with their correct values.
240 back_substitute_into(x);
241 return x;
242 }
243
244private:
245 // Helper function that performs back substitution to solve for the non-free variables in `x`.
246 void back_substitute_into(BitVector<Word>& x) const {
247 // Iterate from the bottom up, starting at the first non-zero row, solving for the non-free variables in `x`.
248 for (auto i = m_rank; i--;) {
249 auto j = m_A[i].first_set().value();
250 x.set(j, m_b[i]);
251 for (auto k = j + 1; k < x.size(); ++k)
252 if (m_A(i, k)) { x.set(j, x[j] ^ x[k]); }
253 }
254 }
255};
256
257} // namespace gf2
Matrices over over GF(2) – bit-matrices. See the BitMatrix 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:151
std::optional< BitVector< Word > > operator()(usize i_solution) const
Returns the ith solution to the system of linear equations A.x = b or std::nullopt if the system is i...
Definition BitGauss.h:228
constexpr bool is_consistent() const
Returns true if the system of linear equations A.x = b is consistent.
Definition BitGauss.h:164
std::optional< BitVector< Word > > operator()() const
Returns a solution to the system of linear equations A.x = b or std::nullopt if the system is inconsi...
Definition BitGauss.h:195
constexpr usize solution_count() const
Returns the maximum number of solutions we can index into.
Definition BitGauss.h:180
BitGauss(BitMatrix< Word > const &A, Rhs const &b)
Constructs a new BitGauss object where we are solving the system of linear equations A....
Definition BitGauss.h:71
constexpr usize rank() const
Returns the rank of the matrix A.
Definition BitGauss.h:129
constexpr usize free_count() const
Returns the number of free variables in the system.
Definition BitGauss.h:140
A dynamically-sized matrix over GF(2) stored as a vector of bit-vectors representing the rows of the ...
Definition BitMatrix.h:33
A dynamically-sized vector over GF(2) with bit elements compactly stored in a standard vector of prim...
Definition BitVector.h:23
static BitVector random(usize size, double p=0.5, u64 seed=0)
Factory method to generate a bit-vector of size size where the elements are picked at random.
Definition BitVector.h:373
static constexpr BitVector zeros(usize n)
Factory method to generate a bit-vector of length n where the elements are all 0.
Definition BitVector.h:196
constexpr auto set(usize i, bool value=true)
Sets the bit-element i to the specified boolean value & returns this for chaining....
Definition BitVector.h:1040
constexpr usize size() const
Returns the number of bit-elements in the bit-vector.
Definition BitVector.h:50
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