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/BitVec.h>
10#include <gf2/BitMat.h>
11
12namespace gf2 {
13
44template<unsigned_word Word>
45class BitGauss {
46private:
47 BitMat<Word> m_A; // The reduced row echelon form of the matrix `A`.
48 BitVec<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:
69 template<typename Store>
71 requires store_word_is<Store, Word>
72 {
73 gf2_assert(A.is_square(), "Matrix is {} x {} but it should be square!", A.rows(), A.cols());
74 gf2_assert(A.rows() == b.size(), "Matrix has {} rows but the RHS vector has {} elements.", A.rows(), b.size());
75
76 // Copy A & augment it with b on the right.
77 m_A = A;
78 m_A.append_col(b);
79
80 // Reduce the augmented matrix to reduced row echelon form.
81 auto has_pivot = m_A.to_reduced_echelon_form();
82
83 // Grab the last column of the reduced augmented matrix as a standalone vector, removing it from the matrix.
84 m_b = m_A.remove_col().value();
85
86 // Also pop the last element of the `has_pivot` bit-vector as it corresponds to the column that we just removed.
87 has_pivot.pop();
88
89 // The rank is the number of columns with a pivot (it is also the number if non-zero rows in the reduced
90 // matrix).
91 m_rank = has_pivot.count_ones();
92
93 // Any columns without a pivot are free variables.
94 for (auto i = 0uz; i < has_pivot.size(); ++i) {
95 if (!has_pivot[i]) m_free.push_back(i);
96 }
97
98 // Check that all zero rows in the reduced matrix are matched by zero elements in the equivalent reduced right
99 // hand side vector (this is consistency). Zero rows are at the bottom from `rank` to the end.
100 auto consistent = true;
101 for (auto i = m_rank; i < m_A.rows(); ++i) {
102 if (m_b[i]) {
103 consistent = false;
104 break;
105 }
106 }
107
108 // The number of solutions we can index into is either 0 or 2^f where f is the number of free variables.
109 // However, for practical reasons we limit the number of solutions to `min(2^f, 2^63)`.
110 m_solutions = 0;
111 if (consistent) {
112 auto f = m_free.size();
113 auto b_max = static_cast<usize>(BITS<Word> - 1);
114 auto f_max = std::min(f, b_max);
115 m_solutions = 1uz << f_max;
116 }
117 }
118
128 constexpr usize rank() const { return m_rank; }
129
139 constexpr usize free_count() const { return m_free.size(); }
140
150 constexpr bool is_underdetermined() const { return !m_free.empty(); }
151
163 constexpr bool is_consistent() const { return m_solutions > 0; }
164
179 constexpr usize solution_count() const { return m_solutions; }
180
194 std::optional<BitVec<Word>> operator()() const {
195 if (!is_consistent()) return std::nullopt;
196
197 // Create a random starting point.
198 auto x = BitVec<Word>::random(m_b.size());
199
200 // All non-free variables will be overwritten by back substitution.
201 back_substitute_into(x);
202 return x;
203 }
204
227 std::optional<BitVec<Word>> operator()(usize i_solution) const {
228 if (!is_consistent()) { return std::nullopt; }
229 if (i_solution > solution_count()) { return std::nullopt; }
230
231 // We start with a zero vector and then set the free variable slots to the fixed bit pattern for `i`.
232 auto x = BitVec<Word>::zeros(m_b.size());
233 for (auto f = 0uz; f < m_free.size(); ++f) {
234 x[m_free[f]] = (i_solution & 1);
235 i_solution >>= 1;
236 }
237
238 // Back substitution will now overwrite any non-free variables in `x` with their correct values.
239 back_substitute_into(x);
240 return x;
241 }
242
243private:
244 // Helper function that performs back substitution to solve for the non-free variables in `x`.
245 void back_substitute_into(BitVec<Word>& x) const {
246 // Iterate from the bottom up, starting at the first non-zero row, solving for the non-free variables in `x`.
247 for (auto i = m_rank; i--;) {
248 auto j = m_A[i].first_set().value();
249 x.set(j, m_b[i]);
250 for (auto k = j + 1; k < x.size(); ++k)
251 if (m_A(i, k)) { x.set(j, x[j] ^ x[k]); }
252 }
253 }
254};
255
256} // namespace gf2
Matrices over over GF(2) – bit-matrices. See the BitMat page for more details.
Vectors over GF(2) with compactly stored bit-elements in a standard vector of primitive unsigned word...
#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:150
constexpr bool is_consistent() const
Returns true if the system of linear equations A.x_for = b is consistent.
Definition BitGauss.h:163
BitGauss(const BitMat< Word > &A, const BitStore< Store > &b)
Constructs a new BitGauss object where we are solving the system of linear equations A....
Definition BitGauss.h:70
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:194
constexpr usize solution_count() const
Returns the maximum number of solutions we can index into.
Definition BitGauss.h:179
constexpr usize rank() const
Returns the rank of the matrix A.
Definition BitGauss.h:128
constexpr usize free_count() const
Returns the number of free variables in the system.
Definition BitGauss.h:139
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:227
A dynamically-sized matrix over GF(2) stored as a vector of bit-vectors representing the rows of the ...
Definition BitMat.h:32
The base class for the vector-like types in the gf2 library: BitSet, BitVec, and BitSpan The templat...
Definition BitStore.h:35
auto set(usize index, bool value=true)
Sets the bit-element at the given index to the specified boolean value & returns this for chaining....
Definition BitStore.h:191
A dynamically-sized vector over GF(2) with bit elements compactly stored in a standard vector of prim...
Definition BitVec.h:37
constexpr usize size() const
Returns the number of bit-elements in the bit-vector.
Definition BitVec.h:64
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:212
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:339
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_word.h:41
constexpr u8 BITS
The number of bits in an unsigned_word returned as a u8.
Definition unsigned_word.h:54