1use crate::arraymap::{ImmutableListMap, ImmutableListMapBuilder};
2use crate::iterators::OctetIter;
3use crate::matrix::BinaryMatrix;
4use crate::octet::Octet;
5use crate::octets::BinaryOctetVec;
6use crate::sparse_vec::SparseBinaryVec;
7use crate::util::get_both_indices;
8use std::mem::size_of;
9
10#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
18pub struct SparseBinaryMatrix {
19 height: usize,
20 width: usize,
21 sparse_elements: Vec<SparseBinaryVec>,
22 dense_elements: Vec<u64>,
25 sparse_columnar_values: Option<ImmutableListMap>,
27 logical_row_to_physical: Vec<u32>,
29 physical_row_to_logical: Vec<u32>,
30 logical_col_to_physical: Vec<u16>,
31 physical_col_to_logical: Vec<u16>,
32 column_index_disabled: bool,
33 #[cfg(debug_assertions)]
35 debug_indexed_column_valid: Vec<bool>,
36 num_dense_columns: usize,
37}
38
39const WORD_WIDTH: usize = 64;
40
41impl SparseBinaryMatrix {
42 #[cfg(debug_assertions)]
43 fn verify(&self) {
44 if self.column_index_disabled {
45 return;
46 }
47 let columns = self.sparse_columnar_values.as_ref().unwrap();
48 for row in 0..self.height {
49 for (col, value) in self.sparse_elements[row].keys_values() {
50 if value != Octet::zero() {
51 debug_assert!(columns.get(col as u16).contains(&(row as u32)));
52 }
53 }
54 }
55 }
56
57 fn logical_col_to_dense_col(&self, col: usize) -> usize {
59 assert!(col >= self.width - self.num_dense_columns);
60 col - (self.width - self.num_dense_columns)
61 }
62
63 fn bit_position(&self, row: usize, col: usize) -> (usize, usize) {
65 return (
66 row * self.row_word_width() + self.word_offset(col),
67 (self.left_padding_bits() + col) % WORD_WIDTH,
68 );
69 }
70
71 fn row_word_width(&self) -> usize {
73 (self.num_dense_columns + WORD_WIDTH - 1) / WORD_WIDTH
74 }
75
76 fn left_padding_bits(&self) -> usize {
78 (WORD_WIDTH - (self.num_dense_columns % WORD_WIDTH)) % WORD_WIDTH
79 }
80
81 fn word_offset(&self, bit: usize) -> usize {
83 (self.left_padding_bits() + bit) / WORD_WIDTH
84 }
85
86 fn select_mask(bit: usize) -> u64 {
88 1u64 << (bit as u64)
89 }
90
91 fn clear_bit(word: &mut u64, bit: usize) {
92 *word &= !SparseBinaryMatrix::select_mask(bit);
93 }
94
95 fn set_bit(word: &mut u64, bit: usize) {
96 *word |= SparseBinaryMatrix::select_mask(bit);
97 }
98}
99
100impl BinaryMatrix for SparseBinaryMatrix {
101 fn new(height: usize, width: usize, trailing_dense_column_hint: usize) -> SparseBinaryMatrix {
102 debug_assert!(height < 16777216);
103 debug_assert!(width < 65536);
105 let mut col_mapping = vec![0; width];
106 let elements = vec![SparseBinaryVec::with_capacity(10); height];
107 let mut row_mapping = vec![0; height];
108 #[allow(clippy::needless_range_loop)]
109 for i in 0..height {
110 row_mapping[i] = i as u32;
111 }
112 #[allow(clippy::needless_range_loop)]
113 for i in 0..width {
114 col_mapping[i] = i as u16;
115 }
116 let dense_elements = if trailing_dense_column_hint > 0 {
117 vec![0; height * ((trailing_dense_column_hint - 1) / WORD_WIDTH + 1)]
118 } else {
119 vec![]
120 };
121 SparseBinaryMatrix {
122 height,
123 width,
124 sparse_elements: elements,
125 dense_elements,
126 sparse_columnar_values: None,
127 logical_row_to_physical: row_mapping.clone(),
128 physical_row_to_logical: row_mapping,
129 logical_col_to_physical: col_mapping.clone(),
130 physical_col_to_logical: col_mapping,
131 column_index_disabled: true,
132 num_dense_columns: trailing_dense_column_hint,
133 #[cfg(debug_assertions)]
134 debug_indexed_column_valid: vec![true; width],
135 }
136 }
137
138 fn set(&mut self, i: usize, j: usize, value: Octet) {
139 let physical_i = self.logical_row_to_physical[i] as usize;
140 let physical_j = self.logical_col_to_physical[j] as usize;
141 if self.width - j <= self.num_dense_columns {
142 let (word, bit) = self.bit_position(physical_i, self.logical_col_to_dense_col(j));
143 if value == Octet::zero() {
144 SparseBinaryMatrix::clear_bit(&mut self.dense_elements[word], bit);
145 } else {
146 SparseBinaryMatrix::set_bit(&mut self.dense_elements[word], bit);
147 }
148 } else {
149 self.sparse_elements[physical_i].insert(physical_j, value);
150 assert!(self.column_index_disabled);
151 }
152 }
153
154 fn height(&self) -> usize {
155 self.height
156 }
157
158 fn width(&self) -> usize {
159 self.width
160 }
161
162 fn count_ones(&self, row: usize, start_col: usize, end_col: usize) -> usize {
163 if end_col > self.width - self.num_dense_columns {
164 unimplemented!("It was assumed that this wouldn't be needed, because the method would only be called on the V section of matrix A");
165 }
166 let mut ones = 0;
167 let physical_row = self.logical_row_to_physical[row] as usize;
168 for (physical_col, value) in self.sparse_elements[physical_row].keys_values() {
169 let col = self.physical_col_to_logical[physical_col] as usize;
170 if col >= start_col && col < end_col && value == Octet::one() {
171 ones += 1;
172 }
173 }
174 return ones;
175 }
176
177 fn get_sub_row_as_octets(&self, row: usize, start_col: usize) -> BinaryOctetVec {
178 let first_dense_column = self.width - self.num_dense_columns;
179 assert_eq!(start_col, first_dense_column);
180 let physical_row = self.logical_row_to_physical[row] as usize;
184 let (first_word, _) =
185 self.bit_position(physical_row, self.logical_col_to_dense_col(start_col));
186 let last_word = first_word + self.row_word_width();
187
188 BinaryOctetVec::new(
189 self.dense_elements[first_word..last_word].to_vec(),
190 self.num_dense_columns,
191 )
192 }
193
194 fn query_non_zero_columns(&self, row: usize, start_col: usize) -> Vec<usize> {
195 assert_eq!(start_col, self.width - self.num_dense_columns);
199 let mut result = vec![];
200 let physical_row = self.logical_row_to_physical[row] as usize;
201 let (mut word, bit) =
202 self.bit_position(physical_row, self.logical_col_to_dense_col(start_col));
203 let mut col = start_col;
204 let mut block = self.dense_elements[word];
208 while block.trailing_zeros() < WORD_WIDTH as u32 {
209 result.push(col + block.trailing_zeros() as usize - bit);
210 block &= !(SparseBinaryMatrix::select_mask(block.trailing_zeros() as usize));
211 }
212 col += WORD_WIDTH - bit;
213 word += 1;
214
215 while col < self.width() {
216 let mut block = self.dense_elements[word];
217 while block.trailing_zeros() < WORD_WIDTH as u32 {
219 result.push(col + block.trailing_zeros() as usize);
220 block &= !(SparseBinaryMatrix::select_mask(block.trailing_zeros() as usize));
221 }
222 col += WORD_WIDTH;
223 word += 1;
224 }
225
226 result
227 }
228
229 fn get(&self, i: usize, j: usize) -> Octet {
230 let physical_i = self.logical_row_to_physical[i] as usize;
231 let physical_j = self.logical_col_to_physical[j] as usize;
232 if self.width - j <= self.num_dense_columns {
233 let (word, bit) = self.bit_position(physical_i, self.logical_col_to_dense_col(j));
234 if self.dense_elements[word] & SparseBinaryMatrix::select_mask(bit) == 0 {
235 return Octet::zero();
236 } else {
237 return Octet::one();
238 }
239 } else {
240 return self.sparse_elements[physical_i]
241 .get(physical_j)
242 .unwrap_or_else(Octet::zero);
243 }
244 }
245
246 fn get_row_iter(&self, row: usize, start_col: usize, end_col: usize) -> OctetIter {
247 if end_col > self.width - self.num_dense_columns {
248 unimplemented!("It was assumed that this wouldn't be needed, because the method would only be called on the V section of matrix A");
249 }
250 let physical_row = self.logical_row_to_physical[row] as usize;
251 let sparse_elements = &self.sparse_elements[physical_row];
252 OctetIter::new_sparse(
253 start_col,
254 end_col,
255 sparse_elements,
256 &self.physical_col_to_logical,
257 )
258 }
259
260 fn get_ones_in_column(&self, col: usize, start_row: usize, end_row: usize) -> Vec<u32> {
261 assert!(!self.column_index_disabled);
262 #[cfg(debug_assertions)]
263 debug_assert!(self.debug_indexed_column_valid[col]);
264 let physical_col = self.logical_col_to_physical[col];
265 let mut rows = vec![];
266 for physical_row in self
267 .sparse_columnar_values
268 .as_ref()
269 .unwrap()
270 .get(physical_col)
271 {
272 let logical_row = self.physical_row_to_logical[*physical_row as usize];
273 if start_row <= logical_row as usize && logical_row < end_row as u32 {
274 rows.push(logical_row);
275 }
276 }
277
278 rows
279 }
280
281 fn swap_rows(&mut self, i: usize, j: usize) {
282 let physical_i = self.logical_row_to_physical[i] as usize;
283 let physical_j = self.logical_row_to_physical[j] as usize;
284 self.logical_row_to_physical.swap(i, j);
285 self.physical_row_to_logical.swap(physical_i, physical_j);
286 }
287
288 fn swap_columns(&mut self, i: usize, j: usize, _: usize) {
289 if j >= self.width - self.num_dense_columns {
290 unimplemented!("It was assumed that this wouldn't be needed, because the method would only be called on the V section of matrix A");
291 }
292
293 #[cfg(debug_assertions)]
294 self.debug_indexed_column_valid.swap(i, j);
295
296 let physical_i = self.logical_col_to_physical[i] as usize;
297 let physical_j = self.logical_col_to_physical[j] as usize;
298 self.logical_col_to_physical.swap(i, j);
299 self.physical_col_to_logical.swap(physical_i, physical_j);
300 }
301
302 fn enable_column_access_acceleration(&mut self) {
303 self.column_index_disabled = false;
304 let mut builder = ImmutableListMapBuilder::new(self.height);
305 for (physical_row, elements) in self.sparse_elements.iter().enumerate() {
306 for (physical_col, _) in elements.keys_values() {
307 builder.add(physical_col as u16, physical_row as u32);
308 }
309 }
310 self.sparse_columnar_values = Some(builder.build());
311 }
312
313 fn disable_column_access_acceleration(&mut self) {
314 self.column_index_disabled = true;
315 self.sparse_columnar_values = None;
316 }
317
318 fn hint_column_dense_and_frozen(&mut self, i: usize) {
319 assert_eq!(
320 self.width - self.num_dense_columns - 1,
321 i,
322 "Can only freeze the last sparse column"
323 );
324 assert!(!self.column_index_disabled);
325 self.num_dense_columns += 1;
326 let (last_word, _) = self.bit_position(self.height - 1, self.num_dense_columns - 1);
327 if last_word >= self.dense_elements.len() {
329 let mut src = self.dense_elements.len();
331 self.dense_elements
332 .resize(self.dense_elements.len() + self.height, 0);
333 let mut dest = self.dense_elements.len();
334 while src > 0 {
336 src -= 1;
337 dest -= 1;
338 self.dense_elements[dest] = self.dense_elements[src];
339 if dest % self.row_word_width() == 1 {
340 dest -= 1;
341 self.dense_elements[dest] = 0;
342 }
343 }
344 assert_eq!(src, 0);
345 assert_eq!(dest, 0);
346 }
347 let physical_i = self.logical_col_to_physical[i] as usize;
348 for maybe_present_in_row in self
349 .sparse_columnar_values
350 .as_ref()
351 .unwrap()
352 .get(physical_i as u16)
353 {
354 let physical_row = *maybe_present_in_row as usize;
355 if let Some(value) = self.sparse_elements[physical_row].remove(physical_i) {
356 let (word, bit) = self.bit_position(physical_row, 0);
357 if value == Octet::zero() {
358 SparseBinaryMatrix::clear_bit(&mut self.dense_elements[word], bit);
359 } else {
360 SparseBinaryMatrix::set_bit(&mut self.dense_elements[word], bit);
361 }
362 }
363 }
364 }
365
366 fn add_assign_rows(&mut self, dest: usize, src: usize, start_col: usize) {
367 assert_ne!(dest, src);
368 assert!(
369 start_col == 0 || start_col == self.width - self.num_dense_columns,
370 "start_col must be zero or at the beginning of the U matrix"
371 );
372 let physical_dest = self.logical_row_to_physical[dest] as usize;
373 let physical_src = self.logical_row_to_physical[src] as usize;
374 if self.num_dense_columns > 0 {
376 let (dest_word, _) = self.bit_position(physical_dest, 0);
377 let (src_word, _) = self.bit_position(physical_src, 0);
378 for word in 0..self.row_word_width() {
379 self.dense_elements[dest_word + word] ^= self.dense_elements[src_word + word];
380 }
381 }
382
383 if start_col == 0 {
384 let (dest_row, temp_row) =
386 get_both_indices(&mut self.sparse_elements, physical_dest, physical_src);
387 assert!(self.column_index_disabled || temp_row.len() == 1);
390
391 let column_added = dest_row.add_assign(temp_row);
392 assert!(self.column_index_disabled || !column_added);
395
396 #[cfg(debug_assertions)]
397 {
398 if !self.column_index_disabled {
399 let col = self.physical_col_to_logical[temp_row.get_by_raw_index(0).0];
400 self.debug_indexed_column_valid[col as usize] = false;
401 }
402 }
403 }
404
405 #[cfg(debug_assertions)]
406 self.verify();
407 }
408
409 fn resize(&mut self, new_height: usize, new_width: usize) {
410 assert!(new_height <= self.height);
411 let mut columns_to_remove = self.width - new_width;
413 assert!(columns_to_remove == 0 || columns_to_remove >= self.num_dense_columns);
414 if !self.column_index_disabled {
415 unimplemented!(
416 "Resize should only be used in phase 2, after column indexing is no longer needed"
417 );
418 }
419 let mut new_sparse = vec![None; new_height];
420 for i in (0..self.sparse_elements.len()).rev() {
421 let logical_row = self.physical_row_to_logical[i] as usize;
422 let sparse = self.sparse_elements.pop();
423 if logical_row < new_height {
424 new_sparse[logical_row] = sparse;
425 }
426 }
427
428 if columns_to_remove == 0 && self.num_dense_columns > 0 {
429 let mut new_dense = vec![0; new_height * self.row_word_width()];
431 for logical_row in 0..new_height {
432 let physical_row = self.logical_row_to_physical[logical_row] as usize;
433 for word in 0..self.row_word_width() {
434 new_dense[logical_row * self.row_word_width() + word] =
435 self.dense_elements[physical_row * self.row_word_width() + word];
436 }
437 }
438 self.dense_elements = new_dense;
439 } else {
440 columns_to_remove -= self.num_dense_columns;
441 self.dense_elements.clear();
442 self.num_dense_columns = 0;
443 }
444
445 self.logical_row_to_physical.truncate(new_height);
446 self.physical_row_to_logical.truncate(new_height);
447 for i in 0..new_height {
448 self.logical_row_to_physical[i] = i as u32;
449 self.physical_row_to_logical[i] = i as u32;
450 }
451 for row in new_sparse.drain(0..new_height) {
452 self.sparse_elements.push(row.unwrap());
453 }
454
455 if columns_to_remove > 0 {
457 let physical_to_logical = &self.physical_col_to_logical;
458 for row in 0..self.sparse_elements.len() {
459 self.sparse_elements[row]
460 .retain(|(col, _)| physical_to_logical[*col] < new_width as u16);
461 }
462 }
463
464 self.height = new_height;
465 self.width = new_width;
466
467 #[cfg(debug_assertions)]
468 self.verify();
469 }
470
471 fn size_in_bytes(&self) -> usize {
472 let mut bytes = size_of::<Self>();
473 for x in self.sparse_elements.iter() {
474 bytes += x.size_in_bytes();
475 }
476 bytes += size_of::<u64>() * self.dense_elements.len();
477 if let Some(ref columns) = self.sparse_columnar_values {
478 bytes += columns.size_in_bytes();
479 }
480 bytes += size_of::<u32>() * self.logical_row_to_physical.len();
481 bytes += size_of::<u32>() * self.physical_row_to_logical.len();
482 bytes += size_of::<u16>() * self.logical_col_to_physical.len();
483 bytes += size_of::<u16>() * self.physical_col_to_logical.len();
484 #[cfg(debug_assertions)]
485 {
486 bytes += size_of::<bool>() * self.debug_indexed_column_valid.len();
487 }
488
489 bytes
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use crate::systematic_constants::{num_intermediate_symbols, MAX_SOURCE_SYMBOLS_PER_BLOCK};
496
497 #[test]
498 fn check_max_width_optimization() {
499 assert!(num_intermediate_symbols(MAX_SOURCE_SYMBOLS_PER_BLOCK) < 65536);
502 }
503}