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