1 | /*! |
---|
2 | * \file |
---|
3 | * \brief Definition of a class for algebra on GF(2) (binary) matrices |
---|
4 | * \author Erik G. Larsson and Adam Piatyszek |
---|
5 | * |
---|
6 | * ------------------------------------------------------------------------- |
---|
7 | * |
---|
8 | * IT++ - C++ library of mathematical, signal processing, speech processing, |
---|
9 | * and communications classes and functions |
---|
10 | * |
---|
11 | * Copyright (C) 1995-2007 (see AUTHORS file for a list of contributors) |
---|
12 | * |
---|
13 | * This program is free software; you can redistribute it and/or modify |
---|
14 | * it under the terms of the GNU General Public License as published by |
---|
15 | * the Free Software Foundation; either version 2 of the License, or |
---|
16 | * (at your option) any later version. |
---|
17 | * |
---|
18 | * This program is distributed in the hope that it will be useful, |
---|
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
21 | * GNU General Public License for more details. |
---|
22 | * |
---|
23 | * You should have received a copy of the GNU General Public License |
---|
24 | * along with this program; if not, write to the Free Software |
---|
25 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
26 | * |
---|
27 | * ------------------------------------------------------------------------- |
---|
28 | * |
---|
29 | * Two representations are offered: GF2mat_sparse for sparse GF(2) |
---|
30 | * matrices and GF2mat for dense GF(2) matrices. Conversions between |
---|
31 | * dense and sparse GF(2) are also possible. |
---|
32 | * |
---|
33 | * Binary vectors are represented either via the bvec class (memory |
---|
34 | * typically is not an issue here) or as n*1 (or 1*n) GF(2) matrix. |
---|
35 | * |
---|
36 | * Note that the \c bmat class also provides some functionality for |
---|
37 | * matrix algebra over GF(2) but this class is based on \c Mat<> which |
---|
38 | * has a fundamentally different addressing mechanism and which is |
---|
39 | * much less memory efficient (\c Mat<> uses one byte memory minimum |
---|
40 | * per element). |
---|
41 | */ |
---|
42 | |
---|
43 | #ifndef GF2MAT_H |
---|
44 | #define GF2MAT_H |
---|
45 | |
---|
46 | #include <itpp/base/vec.h> |
---|
47 | #include <itpp/base/mat.h> |
---|
48 | #include <itpp/base/svec.h> |
---|
49 | #include <itpp/base/smat.h> |
---|
50 | #include <itpp/base/itfile.h> |
---|
51 | |
---|
52 | |
---|
53 | namespace itpp { |
---|
54 | |
---|
55 | // ---------------------------------------------------------------------- |
---|
56 | // Sparse GF(2) matrix class |
---|
57 | // ---------------------------------------------------------------------- |
---|
58 | |
---|
59 | //! Sparse GF(2) vector |
---|
60 | typedef Sparse_Vec<bin> GF2vec_sparse; |
---|
61 | |
---|
62 | //! Sparse GF(2) matrix |
---|
63 | typedef Sparse_Mat<bin> GF2mat_sparse; |
---|
64 | |
---|
65 | |
---|
66 | // ---------------------------------------------------------------------- |
---|
67 | // Alist parameterization of sparse GF(2) matrix class |
---|
68 | // ---------------------------------------------------------------------- |
---|
69 | |
---|
70 | /*! |
---|
71 | \relatesalso GF2mat_sparse |
---|
72 | \brief Parameterized "alist" representation of sparse GF(2) matrix |
---|
73 | \author Adam Piatyszek and Erik G. Larsson |
---|
74 | |
---|
75 | This class is used to provide a parameterized representation of a \c |
---|
76 | GF2mat_sparse class. The format is compatible with the "alist" format |
---|
77 | defined by David MacKay, Matthew Davey and John Lafferty: |
---|
78 | - http://www.inference.phy.cam.ac.uk/mackay/codes/alist.html |
---|
79 | |
---|
80 | For examples of "alist" representation visit David MacKay's Encyclopedia |
---|
81 | of Sparse Graph Codes: |
---|
82 | - http://www.inference.phy.cam.ac.uk/mackay/codes/data.html |
---|
83 | */ |
---|
84 | class GF2mat_sparse_alist { |
---|
85 | public: |
---|
86 | //! Default constructor |
---|
87 | GF2mat_sparse_alist() : data_ok(false) {} |
---|
88 | //! Constructor, which reads alist data from a file named \c fname |
---|
89 | GF2mat_sparse_alist(const std::string &fname); |
---|
90 | |
---|
91 | //! Read alist data from a file named \c fname |
---|
92 | void read(const std::string &fname); |
---|
93 | //! Write alist data to a file named \c fname |
---|
94 | void write(const std::string &fname) const; |
---|
95 | |
---|
96 | /*! |
---|
97 | \brief Convert "alist" representation to \c GF2mat_sparse |
---|
98 | |
---|
99 | \param transpose Indicates whether a matrix should be transposed |
---|
100 | during the conversion process |
---|
101 | */ |
---|
102 | GF2mat_sparse to_sparse(bool transpose = false) const; |
---|
103 | |
---|
104 | /*! |
---|
105 | \brief Import "alist" representation from \c GF2mat_sparse |
---|
106 | |
---|
107 | \param mat Matrix to import |
---|
108 | \param transpose Indicates whether a matrix should be transposed |
---|
109 | during the conversion process |
---|
110 | */ |
---|
111 | void from_sparse(const GF2mat_sparse &mat, bool transpose = false); |
---|
112 | |
---|
113 | protected: |
---|
114 | //! Flag indicating that "alist" matrix data are properly set |
---|
115 | bool data_ok; |
---|
116 | //! Size of the matrix: \c M rows x \c N columns |
---|
117 | int M; |
---|
118 | //! Size of the matrix: \c M rows x \c N columns |
---|
119 | int N; |
---|
120 | //! List of integer coordinates in the m direction with non-zero entries |
---|
121 | imat mlist; |
---|
122 | //! List of integer coordinates in the n direction with non-zero entries |
---|
123 | imat nlist; |
---|
124 | //! Weight of each row m |
---|
125 | ivec num_mlist; |
---|
126 | //! Weight of each column n |
---|
127 | ivec num_nlist; |
---|
128 | //! Maximum weight of rows |
---|
129 | int max_num_m; |
---|
130 | //! Maximum weight of columns |
---|
131 | int max_num_n; |
---|
132 | }; |
---|
133 | |
---|
134 | |
---|
135 | // ---------------------------------------------------------------------- |
---|
136 | // Dense GF(2) matrix class |
---|
137 | // ---------------------------------------------------------------------- |
---|
138 | |
---|
139 | /*! |
---|
140 | \relatesalso bmat |
---|
141 | \brief Class for dense GF(2) matrices |
---|
142 | \author Erik G. Larsson |
---|
143 | |
---|
144 | This class can be used as an alternative to \c bmat to represent |
---|
145 | GF(2) matrices. It extends the functionality of \c bmat in two ways: |
---|
146 | |
---|
147 | - \c GF2mat makes more efficient use of computer memory than \c bmat |
---|
148 | (one bit in the matrix requires one bit of memory) |
---|
149 | - \c GF2mat provides several functions for linear algebra and matrix |
---|
150 | factorizations |
---|
151 | |
---|
152 | See also \c GF2mat_sparse which offers an efficient representation |
---|
153 | of sparse GF(2) matrices, and \c GF2mat_sparse_alist for a |
---|
154 | parameterized representation of sparse GF(2) matrices. |
---|
155 | */ |
---|
156 | class GF2mat { |
---|
157 | public: |
---|
158 | |
---|
159 | // ----------- Constructors ----------- |
---|
160 | |
---|
161 | //! Default constructor (gives an empty 1 x 1 matrix) |
---|
162 | GF2mat(); |
---|
163 | |
---|
164 | //! Construct an empty (all-zero) m x n matrix |
---|
165 | GF2mat(int m, int n); |
---|
166 | |
---|
167 | //! Construct a dense GF(2) matrix from a sparse GF(2) matrix |
---|
168 | GF2mat(const GF2mat_sparse &X); |
---|
169 | |
---|
170 | /*! \brief Constructor, from subset of sparse GF(2) matrix |
---|
171 | |
---|
172 | This constructor forms a dense GF(2) matrix from a subset (m1,n1) |
---|
173 | to (m2,n2) of a sparse GF(2) matrix */ |
---|
174 | GF2mat(const GF2mat_sparse &X, int m1, int n1, int m2, int n2); |
---|
175 | |
---|
176 | /*! \brief Constructor, from subset of sparse GF(2) matrix |
---|
177 | |
---|
178 | This constructor forms a dense GF(2) matrix from a subset of |
---|
179 | columns in sparse GF(2) matrix |
---|
180 | |
---|
181 | \param X matrix to copy from |
---|
182 | \param columns subset of columns to copy |
---|
183 | */ |
---|
184 | GF2mat(const GF2mat_sparse &X, const ivec &columns); |
---|
185 | |
---|
186 | /*! |
---|
187 | \brief Create a dense GF(2) matrix from a single vector. |
---|
188 | |
---|
189 | \param x The input vector |
---|
190 | \param is_column A parameter that indicates whether the result should |
---|
191 | be a row vector (false), or a column vector (true - default) |
---|
192 | */ |
---|
193 | GF2mat(const bvec &x, bool is_column = true); |
---|
194 | |
---|
195 | //! Create a dense GF(2) matrix from a bmat |
---|
196 | GF2mat(const bmat &X); |
---|
197 | |
---|
198 | //! Set size of GF(2) matrix. If copy = true, keep data before resizing. |
---|
199 | void set_size(int m, int n, bool copy = false); |
---|
200 | |
---|
201 | //! Create a sparse GF(2) matrix from a dense GF(2) matrix |
---|
202 | GF2mat_sparse sparsify() const; |
---|
203 | |
---|
204 | //! Create a bvec from a GF(2) matrix (must have one column or one row) |
---|
205 | bvec bvecify() const; |
---|
206 | |
---|
207 | // ----------- Elementwise manipulation and simple functions ------------- |
---|
208 | |
---|
209 | //! Getting element |
---|
210 | inline bin get(int i, int j) const; |
---|
211 | |
---|
212 | //! Getting element |
---|
213 | inline bin operator()(int i, int j) const { return get(i,j); }; |
---|
214 | |
---|
215 | //! Set element i,j to s (0 or 1) |
---|
216 | inline void set(int i, int j, bin s); |
---|
217 | |
---|
218 | //! Add s (0 or 1) to element (i,j) |
---|
219 | inline void addto_element(int i, int j, bin s); |
---|
220 | |
---|
221 | //! Set column j to a binary vector x |
---|
222 | void set_col(int j, bvec x); |
---|
223 | |
---|
224 | //! Set row i to a binary vector x |
---|
225 | void set_row(int i, bvec x); |
---|
226 | |
---|
227 | //! Check whether the matrix is identical to zero |
---|
228 | bool is_zero() const; |
---|
229 | |
---|
230 | //! Swap rows i and j |
---|
231 | void swap_rows(int i, int j); |
---|
232 | |
---|
233 | //! Swap columns i and j |
---|
234 | void swap_cols(int i, int j); |
---|
235 | |
---|
236 | /*! \brief Multiply from left with permutation matrix (permute rows). |
---|
237 | |
---|
238 | \param perm Permutation vector |
---|
239 | \param I Parameter that determines permutation. |
---|
240 | I=0: apply permutation, I=1: apply inverse permutation |
---|
241 | */ |
---|
242 | void permute_rows(ivec &perm, bool I); |
---|
243 | |
---|
244 | /*! \brief Multiply a matrix from right with a permutation matrix |
---|
245 | (i.e., permute the columns). |
---|
246 | |
---|
247 | \param perm Permutation vector |
---|
248 | \param I Parameter that determines permutation. |
---|
249 | I=0: apply permutation, I=1: apply inverse permutation |
---|
250 | */ |
---|
251 | void permute_cols(ivec &perm, bool I); |
---|
252 | |
---|
253 | //! Transpose |
---|
254 | GF2mat transpose() const; |
---|
255 | |
---|
256 | //! Submatrix from (m1,n1) to (m2,n2) |
---|
257 | GF2mat get_submatrix(int m1, int n1, int m2, int n2) const; |
---|
258 | |
---|
259 | //! Concatenate horizontally (append X on the right side of matrix) |
---|
260 | GF2mat concatenate_horizontal(const GF2mat &X) const; |
---|
261 | |
---|
262 | //! Concatenate vertically (append X underneath) |
---|
263 | GF2mat concatenate_vertical(const GF2mat &X) const; |
---|
264 | |
---|
265 | //! Get row |
---|
266 | bvec get_row(int i) const; |
---|
267 | |
---|
268 | //! Get column |
---|
269 | bvec get_col(int j) const; |
---|
270 | |
---|
271 | //! Compute the matrix density (fraction of elements equal to "1") |
---|
272 | double density() const; |
---|
273 | |
---|
274 | //! Get number of rows |
---|
275 | int rows() const { return nrows; } |
---|
276 | |
---|
277 | //! Get number of columns |
---|
278 | int cols() const { return ncols; } |
---|
279 | |
---|
280 | /*! \brief Add (or equivalently, subtract) rows |
---|
281 | |
---|
282 | This function updates row i according to row_i = row_i+row_j |
---|
283 | |
---|
284 | \param i Row to add to. This row will be modified |
---|
285 | \param j Row to add. This row will <b>not</b> be modified. |
---|
286 | */ |
---|
287 | void add_rows(int i, int j); |
---|
288 | |
---|
289 | // ---------- Linear algebra -------------- |
---|
290 | |
---|
291 | /*! \brief Inversion. |
---|
292 | |
---|
293 | The matrix must be invertible, otherwise the |
---|
294 | function will terminate with an error. |
---|
295 | */ |
---|
296 | GF2mat inverse() const; |
---|
297 | |
---|
298 | //! Returns the number of linearly independent rows |
---|
299 | int row_rank() const; |
---|
300 | |
---|
301 | /*! \brief TXP factorization. |
---|
302 | |
---|
303 | Given X, compute a factorization of the form U=TXP, where U is |
---|
304 | upper triangular, T is square and invertible, and P is a |
---|
305 | permutation matrix. This is basically an "LU"-factorization, |
---|
306 | but not called so here because T is not necessarily lower |
---|
307 | trianglular. The matrix X may have any dimension. The |
---|
308 | permutation matrix P is represented as a permutation vector. |
---|
309 | |
---|
310 | The function returns the row rank of X. (If X is full row rank, |
---|
311 | then the number of rows is returned.) |
---|
312 | |
---|
313 | The function uses Gaussian elimination combined with |
---|
314 | permutations. The computational complexity is O(m*m*n) for an |
---|
315 | m*n matrix. |
---|
316 | */ |
---|
317 | int T_fact(GF2mat &T, GF2mat &U, ivec &P) const; |
---|
318 | |
---|
319 | /*! \brief TXP factorization update, when bit is flipped. |
---|
320 | |
---|
321 | Update upper triangular factor U in the TXP-factorization (U=TXP) |
---|
322 | when the bit at position (r,c) is changed (0->1 or 1->0). The |
---|
323 | purpose of this function is to avoid re-running a complete |
---|
324 | T-factorization when a single bit is changed. The function assumes |
---|
325 | that T, U, and P already exist and that U=TXP before the function |
---|
326 | is called. The function also assumes that the rank provided is |
---|
327 | correct. The function updates T, U and P these matrices. The |
---|
328 | function returns the new rank of the matrix after the bitflip. |
---|
329 | |
---|
330 | \note T, U, P and the rank value supplied to the function must be |
---|
331 | correct one. This is not checked by the function (for reasons of |
---|
332 | efficiency). |
---|
333 | |
---|
334 | The function works by performing permutations to bring the matrix |
---|
335 | to a form where the Gaussian eliminated can be restarted from the |
---|
336 | point (rank-1,rank-1). The function is very fast for matrices with |
---|
337 | close to full rank but it is generally slower for non-full rank |
---|
338 | matrices. |
---|
339 | */ |
---|
340 | int T_fact_update_bitflip(GF2mat &T, GF2mat &U, |
---|
341 | ivec &P, int rank, int r, int c) const; |
---|
342 | |
---|
343 | /*! \brief TXP factorization update, when column is added |
---|
344 | |
---|
345 | Update upper triangular factor U in the T-factorization (U=TXP) |
---|
346 | when a column (newcol) is appended at the right side of the |
---|
347 | matrix. The purpose of this function is to avoid re-running a |
---|
348 | complete T-factorization when a column is added. The function ONLY |
---|
349 | adds the column if it improves the rank of the matrix (nothing is |
---|
350 | done otherwise). The function returns "true" if the column was |
---|
351 | added, and "false" otherwise. |
---|
352 | |
---|
353 | \note This function does not actually add the column newcol to the |
---|
354 | GF2 matrix. It only checks whether doing so would increase the |
---|
355 | rank, and if this is the case, it updates the T-factorization. A |
---|
356 | typical calling sequence would be |
---|
357 | \code |
---|
358 | bool rank_will_improve = X.T_fact_update_addcol(T,U,perm,c); |
---|
359 | if (rank_will_improve) { X = X.concatenate_horizontal(c); } |
---|
360 | \endcode |
---|
361 | |
---|
362 | The complexity is O(m^2) for an m*n matrix. |
---|
363 | */ |
---|
364 | bool T_fact_update_addcol(GF2mat &T, GF2mat &U, |
---|
365 | ivec &P, bvec newcol) const; |
---|
366 | |
---|
367 | // ----- Operators ----------- |
---|
368 | |
---|
369 | //! Assignment operator |
---|
370 | void operator=(const GF2mat &X); |
---|
371 | |
---|
372 | //! Check if equal |
---|
373 | bool operator==(const GF2mat &X) const; |
---|
374 | |
---|
375 | // ----- Friends ------ |
---|
376 | |
---|
377 | //! Multiplication operator |
---|
378 | friend GF2mat operator*(const GF2mat &X, const GF2mat &Y); |
---|
379 | |
---|
380 | //! Multiplication operator with binary vector |
---|
381 | friend bvec operator*(const GF2mat &X, const bvec &y); |
---|
382 | |
---|
383 | /*! \brief Addition operator |
---|
384 | |
---|
385 | Subtraction is not implemented because it is |
---|
386 | equivalent to addition. */ |
---|
387 | friend GF2mat operator+(const GF2mat &X, const GF2mat &Y); |
---|
388 | |
---|
389 | //! Output stream operator (plain text) |
---|
390 | friend std::ostream &operator<<(std::ostream &os, const GF2mat &X); |
---|
391 | |
---|
392 | //! Write the matrix to file |
---|
393 | friend it_file &operator<<(it_file &f, const GF2mat &X); |
---|
394 | |
---|
395 | //! Read the matrix from file |
---|
396 | friend it_ifile &operator>>(it_ifile &f, GF2mat &X); |
---|
397 | |
---|
398 | //!Multiplication X*Y' where X and Y are GF(2) matrices |
---|
399 | friend GF2mat mult_trans(const GF2mat &X, const GF2mat &Y); |
---|
400 | |
---|
401 | private: |
---|
402 | int nrows, ncols; // number of rows and columns of matrix |
---|
403 | int nwords; // number of bytes used |
---|
404 | Mat<unsigned char> data; // data structure |
---|
405 | |
---|
406 | // This value is used to perform division by bit shift and is equal to |
---|
407 | // log2(8) |
---|
408 | static const unsigned char shift_divisor = 3; |
---|
409 | |
---|
410 | // This value is used as a mask when computing the bit position of the |
---|
411 | // division remainder |
---|
412 | static const unsigned char rem_mask = (1 << shift_divisor) - 1; |
---|
413 | }; |
---|
414 | |
---|
415 | |
---|
416 | // ---------------------------------------------------------------------- |
---|
417 | // GF2mat related functions |
---|
418 | // ---------------------------------------------------------------------- |
---|
419 | |
---|
420 | /*! |
---|
421 | /relatesalso GF2mat |
---|
422 | /brief Write GF(2) matrix to file. |
---|
423 | */ |
---|
424 | it_file &operator<<(it_file &f, const GF2mat &X); |
---|
425 | |
---|
426 | /*! |
---|
427 | /relatesalso GF2mat |
---|
428 | /brief Read GF(2) matrix from file. |
---|
429 | */ |
---|
430 | it_ifile &operator>>(it_ifile &f, GF2mat &X); |
---|
431 | |
---|
432 | /*! |
---|
433 | \relatesalso GF2mat |
---|
434 | \brief GF(2) matrix multiplication |
---|
435 | */ |
---|
436 | GF2mat operator*(const GF2mat &X, const GF2mat &Y); |
---|
437 | |
---|
438 | /*! |
---|
439 | \relatesalso GF2mat |
---|
440 | \brief GF(2) matrix multiplication with "regular" binary vector |
---|
441 | */ |
---|
442 | bvec operator*(const GF2mat &X, const bvec &y); |
---|
443 | |
---|
444 | /*! |
---|
445 | \relatesalso GF2mat |
---|
446 | \brief GF(2) matrix addition |
---|
447 | */ |
---|
448 | GF2mat operator+(const GF2mat &X, const GF2mat &Y); |
---|
449 | |
---|
450 | /*! |
---|
451 | \relatesalso GF2mat |
---|
452 | \brief Output stream (plain text) operator for dense GF(2) matrices |
---|
453 | */ |
---|
454 | std::ostream &operator<<(std::ostream &os, const GF2mat &X); |
---|
455 | |
---|
456 | /*! |
---|
457 | \relatesalso GF2mat |
---|
458 | \brief GF(2) Identity matrix |
---|
459 | */ |
---|
460 | GF2mat gf2dense_eye(int m); |
---|
461 | |
---|
462 | /*! |
---|
463 | \relatesalso GF2mat |
---|
464 | \brief Multiplication X*Y' where X and Y are GF(2) matrices |
---|
465 | */ |
---|
466 | GF2mat mult_trans(const GF2mat &X, const GF2mat &Y); |
---|
467 | |
---|
468 | |
---|
469 | // ---------------------------------------------------------------------- |
---|
470 | // Inline implementations |
---|
471 | // ---------------------------------------------------------------------- |
---|
472 | |
---|
473 | inline void GF2mat::addto_element(int i, int j, bin s) |
---|
474 | { |
---|
475 | it_assert_debug(i >= 0 && i < nrows, "GF2mat::addto_element()"); |
---|
476 | it_assert_debug(j >= 0 && j < ncols, "GF2mat::addto_element()"); |
---|
477 | if (s == 1) |
---|
478 | data(i, (j >> shift_divisor)) ^= (1 << (j & rem_mask)); |
---|
479 | } |
---|
480 | |
---|
481 | inline bin GF2mat::get(int i, int j) const |
---|
482 | { |
---|
483 | it_assert_debug(i >= 0 && i < nrows, "GF2mat::get_element()"); |
---|
484 | it_assert_debug(j >= 0 && j < ncols, "GF2mat::get_element()"); |
---|
485 | return (data(i, (j >> shift_divisor)) >> (j & rem_mask)) & 1; |
---|
486 | } |
---|
487 | |
---|
488 | inline void GF2mat::set(int i, int j, bin s) |
---|
489 | { |
---|
490 | it_assert_debug(i >= 0 && i < nrows, "GF2mat::set_element()"); |
---|
491 | it_assert_debug(j >= 0 && j < ncols, "GF2mat::set_element()"); |
---|
492 | if (s == 1) // set bit to one |
---|
493 | data(i, (j >> shift_divisor)) |= (1 << (j & rem_mask)); |
---|
494 | else // set bit to zero |
---|
495 | data(i, (j >> shift_divisor)) &= (~(1 << (j & rem_mask))); |
---|
496 | } |
---|
497 | |
---|
498 | } // namespace itpp |
---|
499 | |
---|
500 | #endif // #ifndef GF2MAT_H |
---|