// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use super::bitvec::WORD_COUNT_DEFAULT;
use super::{BitwiseNeutralElement, OverlapWeight};
use crate::bits::bitblock::{BitAccessor, BitBlock};
use crate::bits::{
BitVec, BitView, Bitwise, BitwiseBinaryOps, Dot, IndexAssignable, MutableBitView,
};
use crate::NeutralElement;
use sorted_iter::assume::AssumeSortedByItemExt;
use sorted_iter::SortedIterator;
use std::cmp::PartialEq;
use std::hash::Hash;
use std::iter::FromIterator;
use std::mem::size_of;
use std::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitXor, BitXorAssign, Mul};
use std::ops::{Index, Range};
use std::str::FromStr;
#[must_use]
#[derive(Debug, Eq)]
pub struct BitMatrix<const WORD_COUNT: usize = WORD_COUNT_DEFAULT> {
blocks: Vec<BitBlock<WORD_COUNT>>,
rows: Vec<*mut BitBlock<WORD_COUNT>>,
columncount: usize,
}
impl<const WORD_COUNT: usize> Hash for BitMatrix<WORD_COUNT> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.blocks.hash(state);
}
}
unsafe impl Sync for BitMatrix {}
pub type Row<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> = BitView<'life, WORD_COUNT>; // should we use View in the name to indicate that it is a view and not a copy of a row ?
pub type MutableRow<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> =
MutableBitView<'life, WORD_COUNT>; // should we use View in the name to indicate that it is a view and not a copy of a row ?
#[derive(Clone, Debug, Hash)]
pub struct Column<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> {
// TODO(VK) should we use View in the name to indicate that it is a view and not a copy of a column ?
rows: &'life [*mut BitBlock<WORD_COUNT>],
accessor: BitAccessor<WORD_COUNT>,
block_index: usize,
}
impl<const WORD_COUNT: usize> BitMatrix<WORD_COUNT> {
pub fn with_shape(rows: usize, columns: usize) -> Self {
Self::zeros(rows, columns)
}
pub fn zeros(rows: usize, columns: usize) -> Self {
Self::with_value(false, (rows, columns))
}
pub fn ones(rows: usize, columns: usize) -> Self {
Self::with_value(true, (rows, columns))
}
pub fn identity(dimension: usize) -> Self {
let mut res = Self::zeros(dimension, dimension);
for index in 0..dimension {
res.set((index, index), true);
}
res
}
pub fn from_row_iter<'life>(
iter: impl ExactSizeIterator<Item = BitView<'life, WORD_COUNT>>,
columns: usize,
) -> Self {
let rows = iter.len();
let mut matrix = Self::zeros(rows, columns);
for (row_from, mut row_to) in std::iter::zip(iter, matrix.row_iterator_mut(0..rows)) {
row_to.assign(&row_from);
}
matrix
}
pub fn from_iter<Row, Rows>(iter: Rows, columncount: usize) -> Self
where
Row: IntoIterator<Item = bool>,
Rows: IntoIterator<Item = Row>,
{
// TODO(AEP): Expanding first into Vec<bool> is
// inefficient. Instead, append to Vec<BitBlock::<WORD_COUNT_DEFAULT>> as necessary.
let mut rows = Vec::<Vec<bool>>::new();
let mut rowcount = 0;
for row in iter {
rows.push(row.into_iter().collect());
rowcount += 1;
}
let mut matrix = BitMatrix::with_shape(rowcount, columncount);
for (row_index, row) in rows.iter().enumerate() {
for (column_index, value) in row.iter().take(columncount).enumerate() {
matrix.set((row_index, column_index), *value);
}
}
matrix
}
fn with_value(value: bool, shape: (usize, usize)) -> Self {
let (rowcount, columncount) = shape;
let rowstride = Self::rowstride_of(columncount);
let buffer = vec![BitBlock::<WORD_COUNT>::all(value); rowcount * rowstride];
Self::from_blocks(buffer, shape)
}
fn from_blocks(mut buffer: Vec<BitBlock<WORD_COUNT>>, shape: (usize, usize)) -> Self {
let rows = Self::rows_of(buffer.as_mut_slice(), shape.0);
Self::from_blocks_and_rows(buffer, shape, rows)
}
fn from_blocks_and_rows(
buffer: Vec<BitBlock<WORD_COUNT>>,
shape: (usize, usize),
rows: Vec<*mut BitBlock<WORD_COUNT>>,
) -> Self {
let matrix = Self {
blocks: buffer,
rows,
columncount: shape.1,
};
debug_assert!(matrix.is_aligned());
matrix
}
#[must_use]
pub fn is_zero(&self) -> bool {
let zero = BitBlock::<WORD_COUNT>::zeros();
for block in &self.blocks {
if *block != zero {
return false;
}
}
true
}
fn is_aligned(&self) -> bool {
let alignment = (self.blocks.as_ptr() as usize) % size_of::<BitBlock<WORD_COUNT>>();
if alignment != 0 {
return false;
}
for row in &self.rows {
let alignment = (*row as usize) % size_of::<BitBlock<WORD_COUNT>>();
if alignment != 0 {
return false;
}
}
true
}
fn rowstride(&self) -> usize {
Self::rowstride_of(self.columncount)
}
fn rowstride_of(columncount: usize) -> usize {
let rowstride = columncount / BitBlock::<WORD_COUNT>::BITS;
let adjustment = !columncount.is_multiple_of(BitBlock::<WORD_COUNT>::BITS);
rowstride + usize::from(adjustment)
}
fn rows_of(
blocks: &mut [BitBlock<WORD_COUNT>],
rowcount: usize,
) -> Vec<*mut BitBlock<WORD_COUNT>> {
let mut rows = Vec::<*mut BitBlock<WORD_COUNT>>::new();
let rowstride = if rowcount == 0 {
0
} else {
blocks.len() / rowcount
};
if rowstride == 0 {
rows = vec![blocks.as_mut_ptr(); rowcount];
} else {
for row in blocks.chunks_exact_mut(rowstride) {
rows.push(row.as_mut_ptr());
}
}
rows
}
#[must_use]
pub fn rowcount(&self) -> usize {
self.rows.len()
}
#[must_use]
pub fn columncount(&self) -> usize {
self.columncount
}
#[must_use]
pub fn shape(&self) -> (usize, usize) {
(self.rowcount(), self.columncount())
}
#[must_use]
pub fn row(&self, index: usize) -> Row<'_, WORD_COUNT> {
Row::<WORD_COUNT> {
blocks: unsafe {
std::slice::from_raw_parts((*self.rows[index]).array(), self.block_count())
},
}
}
#[must_use]
pub fn rows(&self) -> impl ExactSizeIterator<Item = Row<'_, WORD_COUNT>> {
self.row_iterator(0..self.rowcount())
}
pub fn row_iterator(
&self,
index_iterator: impl ExactSizeIterator<Item = usize>,
) -> impl ExactSizeIterator<Item = Row<'_, WORD_COUNT>> {
index_iterator.map(|index| self.row(index))
}
pub fn row_iterator_mut(
&mut self,
index_iterator: impl ExactSizeIterator<Item = usize>,
) -> impl ExactSizeIterator<Item = MutableRow<'_, WORD_COUNT>> {
index_iterator.map(|index| self.build_mutable_row(index))
}
pub fn row_mut(&mut self, index: usize) -> MutableRow<'_, WORD_COUNT> {
self.build_mutable_row(index)
}
#[inline]
fn block_count(&self) -> usize {
let mut block_count = self.columncount() / BitBlock::<WORD_COUNT_DEFAULT>::BITS;
if !self
.columncount()
.is_multiple_of(BitBlock::<WORD_COUNT_DEFAULT>::BITS)
{
block_count += 1;
}
block_count
}
fn build_mutable_row(&self, index: usize) -> MutableRow<'_, WORD_COUNT> {
let ptr = self.rows[index];
MutableRow::<WORD_COUNT> {
blocks: unsafe {
std::slice::from_raw_parts_mut((*ptr).array_mut(), self.block_count())
},
}
}
pub fn rows_mut(
&mut self,
index: usize,
index2: usize,
) -> (MutableRow<'_, WORD_COUNT>, MutableRow<'_, WORD_COUNT>) {
(
self.build_mutable_row(index),
self.build_mutable_row(index2),
)
}
pub fn rows2_mut(
&mut self,
index: (usize, usize),
) -> (MutableRow<'_, WORD_COUNT>, MutableRow<'_, WORD_COUNT>) {
(
self.build_mutable_row(index.0),
self.build_mutable_row(index.1),
)
}
#[must_use]
pub fn rows2(&self, index: (usize, usize)) -> (Row<'_, WORD_COUNT>, Row<'_, WORD_COUNT>) {
(self.row(index.0), self.row(index.1))
}
/// # Safety
/// Does not check if all indexes are distinct
pub unsafe fn rows4_mut(
&mut self,
index: (usize, usize, usize, usize),
) -> (
MutableRow<'_, WORD_COUNT>,
MutableRow<'_, WORD_COUNT>,
MutableRow<'_, WORD_COUNT>,
MutableRow<'_, WORD_COUNT>,
) {
(
self.build_mutable_row(index.0),
self.build_mutable_row(index.1),
self.build_mutable_row(index.2),
self.build_mutable_row(index.3),
)
}
/// TODO(VK): Maybe use <https://doc.rust-lang.org/std/primitive.slice.html#method.get_many_mut> when it becomes stable
/// # Safety
/// Does not check if all indexes are distinct
pub unsafe fn rows8_mut(
&mut self,
index: crate::Tuple8<usize>,
) -> crate::Tuple8<MutableRow<'_, WORD_COUNT>> {
(
self.build_mutable_row(index.0),
self.build_mutable_row(index.1),
self.build_mutable_row(index.2),
self.build_mutable_row(index.3),
self.build_mutable_row(index.4),
self.build_mutable_row(index.5),
self.build_mutable_row(index.6),
self.build_mutable_row(index.7),
)
}
#[must_use]
pub fn column(&self, index: usize) -> Column<'_, WORD_COUNT> {
let block_index = index / BitBlock::<WORD_COUNT_DEFAULT>::BITS;
let bit_index = index % BitBlock::<WORD_COUNT_DEFAULT>::BITS;
Column::<WORD_COUNT> {
rows: &self.rows,
accessor: BitAccessor::for_index(bit_index),
block_index,
}
}
#[must_use]
pub fn columns(&self) -> impl ExactSizeIterator<Item = Column<'_, WORD_COUNT>> {
let indexes = 0..self.columncount();
indexes.map(|index| self.column(index))
}
/// # Panics
///
/// Will panic if index out of range
pub fn set(&mut self, index: (usize, usize), to: bool) {
assert!(index.0 < self.rowcount() && index.1 < self.columncount());
unsafe { self.set_unchecked(index, to) };
}
/// # Safety
/// Dose not check if index is out of bounds
pub unsafe fn set_unchecked(&mut self, index: (usize, usize), to: bool) {
let (block, bit_index) = self.block_index_of_mut(index);
block.set(bit_index, to);
}
/// # Panics
///
/// Will panic if index out of range
#[must_use]
pub fn get(&self, index: (usize, usize)) -> bool {
assert!(index.0 < self.rowcount() && index.1 < self.columncount());
unsafe { self.get_unchecked(index) }
}
/// # Safety
/// Does not check if index is out of bounds
#[must_use]
pub unsafe fn get_unchecked(&self, index: (usize, usize)) -> bool {
let (block, bit_index) = self.block_index_of(index);
block.get_unchecked(bit_index)
}
pub fn echelonize(&mut self) -> Vec<usize> {
let mut pivot = pivot_of(self, (0, 0));
let mut rank_profile = Vec::<usize>::with_capacity(self.columncount());
for row_index in 0..self.rowcount() {
if pivot.1 >= self.columncount() {
break;
}
self.swap_rows(pivot.0, row_index);
pivot.0 = row_index;
rank_profile.push(pivot.1);
reduce(self, pivot);
pivot = pivot_of(self, (pivot.0 + 1, pivot.1 + 1));
}
rank_profile
}
#[must_use]
pub fn rank(&self) -> usize {
self.clone().echelonize().len()
}
pub fn transposed(&self) -> Self {
let mut res = Self::with_shape(self.columncount(), self.rowcount());
for i in 0..self.rowcount() {
for j in 0..self.columncount() {
res.set((j, i), self[(i, j)]);
}
}
res
}
pub fn submatrix(&self, rows: &[usize], columns: &[usize]) -> Self {
let mut res = Self::with_shape(rows.len(), columns.len());
for (row_index, &row) in rows.iter().enumerate() {
for (column_index, &column) in columns.iter().enumerate() {
res.set((row_index, column_index), self[(row, column)]);
}
}
res
}
/// # Panics
///
/// Will panic if matrix is not invertible
pub fn inverted(&self) -> BitMatrix<WORD_COUNT> {
assert!(self.columncount() == self.rowcount());
let (_, t, _, profile) = rref_with_transforms(self.clone());
assert!(profile.len() == self.rowcount());
debug_assert_eq!(
self * &t,
BitMatrix::<WORD_COUNT>::identity(self.rowcount())
);
t
}
pub fn swap_rows(&mut self, left_row_index: usize, right_row_index: usize) {
self.rows.swap(left_row_index, right_row_index);
}
pub fn swap_columns(&mut self, left_column_index: usize, right_column_index: usize) {
for row_index in 0..self.rowcount() {
let left_bit = self.get((row_index, left_column_index));
let right_bit = self.get((row_index, right_column_index));
self.set((row_index, left_column_index), right_bit);
self.set((row_index, right_column_index), left_bit);
}
}
pub fn permute_rows(&mut self, permutation: &[usize]) {
let old_rows = self.rows.clone();
for index in 0..permutation.len() {
self.rows[index] = old_rows[permutation[index]];
}
}
pub fn add_into_row(&mut self, to_index: usize, from_index: usize) {
let mut to_block = self.rows[to_index];
let mut from_block = self.rows[from_index];
for _ in 0..self.rowstride() {
unsafe {
BitwiseBinaryOps::bitxor_assign(&mut *to_block, &*from_block);
to_block = to_block.add(1);
from_block = from_block.add(1);
}
}
}
// TODO(VK): check if we also need dot_transposed
/// # Panics
///
/// Will panic if matrix dimensions are incompatible
pub fn dot(&self, rhs: &BitMatrix<WORD_COUNT>) -> BitMatrix<WORD_COUNT> {
assert_eq!(self.columncount(), rhs.rowcount());
let mut rows = Vec::with_capacity(self.rowcount());
for output_row in 0..self.rowcount() {
let mut row = BitVec::<WORD_COUNT>::zeros(rhs.columncount());
for column_index in 0..self.columncount() {
if self[(output_row, column_index)] {
// TODO(AEP): This is needlessly slow. Make it fast.
for into_column_index in 0..rhs.columncount() {
row.assign_index(
into_column_index,
row.index(into_column_index)
^ rhs.get((column_index, into_column_index)),
);
}
}
}
rows.push(row);
}
Self::from_iter(rows.iter(), rhs.columncount())
}
fn block_index_of_mut(&mut self, index: (usize, usize)) -> (&mut BitBlock<WORD_COUNT>, usize) {
let column_block = index.1 / BitBlock::<WORD_COUNT_DEFAULT>::BITS;
let column_remainder = index.1 % BitBlock::<WORD_COUNT_DEFAULT>::BITS;
let bit_index = column_remainder % BitBlock::<WORD_COUNT_DEFAULT>::BITS;
unsafe {
let block = self.rows[index.0].add(column_block);
(&mut *block, bit_index)
}
}
fn block_index_of(&self, index: (usize, usize)) -> (&BitBlock<WORD_COUNT>, usize) {
let column_block = index.1 / BitBlock::<WORD_COUNT_DEFAULT>::BITS;
let column_remainder = index.1 % BitBlock::<WORD_COUNT_DEFAULT>::BITS;
let bit_index = column_remainder % BitBlock::<WORD_COUNT_DEFAULT>::BITS;
unsafe {
let block = self.rows[index.0].add(column_block);
(&*block, bit_index)
}
}
}
unsafe impl<const WORD_COUNT: usize> Send for BitMatrix<WORD_COUNT> {}
impl<const WORD_COUNT: usize> Clone for BitMatrix<WORD_COUNT> {
fn clone(&self) -> Self {
let mut blocks = self.blocks.clone();
let mut rows = Vec::<*mut BitBlock<WORD_COUNT>>::new();
let offset = unsafe { blocks.as_mut_ptr().offset_from(self.blocks.as_ptr()) };
for row in &self.rows {
rows.push(unsafe { row.offset(offset) });
}
BitMatrix::from_blocks_and_rows(blocks, self.shape(), rows)
}
}
impl<const WORD_COUNT: usize> Index<[usize; 2]> for BitMatrix<WORD_COUNT> {
type Output = bool;
fn index(&self, index: [usize; 2]) -> &Self::Output {
&self[(index[0], index[1])]
}
}
impl<const WORD_COUNT: usize> Index<(usize, usize)> for BitMatrix<WORD_COUNT> {
type Output = bool;
fn index(&self, index: (usize, usize)) -> &Self::Output {
if self.get(index) {
return &true;
}
&false
}
}
impl<const WORD_COUNT: usize> PartialEq for BitMatrix<WORD_COUNT> {
fn eq(&self, other: &Self) -> bool {
if self.shape() != other.shape() {
return false;
}
for irow in 0..self.rowcount() {
for icol in 0..self.columncount() {
if self[(irow, icol)] != other[(irow, icol)] {
return false;
}
}
}
true
}
}
impl<const WORD_COUNT: usize> AddAssign<&BitMatrix<WORD_COUNT>> for BitMatrix<WORD_COUNT> {
fn add_assign(&mut self, other: &BitMatrix<WORD_COUNT>) {
assert_eq!(self.shape(), other.shape());
for index in 0..self.rowcount() {
self.row_mut(index).bitxor_assign(&other.row(index));
}
}
}
impl<const WORD_COUNT: usize> Add for &BitMatrix<WORD_COUNT> {
type Output = BitMatrix<WORD_COUNT>;
fn add(self, other: Self) -> Self::Output {
let mut clone = (*self).clone();
clone += other;
clone
}
}
impl<const WORD_COUNT: usize> BitXor for &BitMatrix<WORD_COUNT> {
type Output = BitMatrix<WORD_COUNT>;
fn bitxor(self, other: Self) -> Self::Output {
self.add(other)
}
}
impl<const WORD_COUNT: usize> BitXorAssign<&BitMatrix<WORD_COUNT>> for BitMatrix<WORD_COUNT> {
fn bitxor_assign(&mut self, other: &BitMatrix<WORD_COUNT>) {
self.add_assign(other);
}
}
impl<const WORD_COUNT: usize> BitAndAssign<&BitMatrix<WORD_COUNT>> for BitMatrix<WORD_COUNT> {
fn bitand_assign(&mut self, other: &Self) {
assert_eq!(self.shape(), other.shape());
for index in 0..self.rowcount() {
self.row_mut(index).bitand_assign(&other.row(index));
}
}
}
impl<const WORD_COUNT: usize> BitAnd for &BitMatrix<WORD_COUNT> {
type Output = BitMatrix<WORD_COUNT>;
fn bitand(self, other: Self) -> Self::Output {
let mut clone = (*self).clone();
clone &= other;
clone
}
}
impl<const WORD_COUNT: usize> Mul for &BitMatrix<WORD_COUNT> {
type Output = BitMatrix<WORD_COUNT>;
fn mul(self, other: Self) -> Self::Output {
assert_eq!(self.columncount(), other.rowcount());
let mut result = BitMatrix::<WORD_COUNT>::with_shape(self.rowcount(), other.columncount());
for i in 0..self.rowcount() {
for j in 0..other.columncount() {
let mut val = false;
for k in 0..self.columncount() {
val ^= self[[i, k]] && other[[k, j]];
}
result.set((i, j), val);
}
}
result
}
}
impl<const WORD_COUNT: usize> std::fmt::Display for BitMatrix<WORD_COUNT> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for row_index in 0..self.rowcount() {
for column_index in 0..self.columncount() {
let value = i32::from(self.get((row_index, column_index)));
write!(f, "{value:?}")?;
}
writeln!(f)?;
}
Ok(())
}
}
impl<const WORD_COUNT: usize> FromStr for BitMatrix<WORD_COUNT> {
type Err = usize;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut rows = Vec::<BitVec>::new();
let mut column_count = 0;
for row_string in s.split(&['|', '[', ']', '(', ')', ';', '\n']) {
if !row_string.is_empty() {
let mut res = Vec::<bool>::new();
for char in row_string.chars() {
match char {
'0' | '.' | '▫' | '□' => res.push(false),
'1' | '▪' | '■' => res.push(true),
' ' | '-' | ',' => {}
_ => return Err(0),
}
}
if !res.is_empty() {
column_count = column_count.max(res.len());
rows.push(res.into_iter().collect());
}
}
}
Ok(BitMatrix::from_iter(rows.iter(), column_count))
}
}
/// # Panics
///
/// Should not panic. TODO: refactor so clippy does not complain
pub fn row_stacked<'t, Matrices, const WORD_COUNT: usize>(
matrices: Matrices,
) -> BitMatrix<WORD_COUNT>
where
Matrices: IntoIterator<Item = &'t BitMatrix<WORD_COUNT>>,
{
let mut buffer = Vec::<BitBlock<WORD_COUNT>>::new();
let mut columncount: Option<usize> = None;
let mut rowcount = 0;
for matrix in matrices {
debug_assert!(columncount.is_none() || columncount.unwrap() == matrix.columncount());
buffer.append(&mut matrix.blocks.clone());
columncount = Some(matrix.columncount());
rowcount += matrix.rowcount();
}
BitMatrix::<WORD_COUNT>::from_blocks(buffer, (rowcount, *columncount.get_or_insert(0)))
}
pub fn directly_summed<'t, Matrices>(matrices: Matrices) -> BitMatrix
where
Matrices: IntoIterator<Item = &'t BitMatrix>,
{
let mut rowcount = 0;
let mut columncount = 0;
let vec_matrices = Vec::from_iter(matrices);
for matrix in &vec_matrices {
rowcount += matrix.rowcount();
columncount += matrix.columncount();
}
let mut sum = BitMatrix::zeros(rowcount, columncount);
let mut sum_row_offset = 0;
let mut sum_column_offset = 0;
for matrix in &vec_matrices {
for row_index in 0..matrix.rowcount() {
for column_index in 0..matrix.columncount() {
sum.set(
(row_index + sum_row_offset, column_index + sum_column_offset),
matrix[(row_index, column_index)],
);
}
}
sum_row_offset += matrix.rowcount();
sum_column_offset += matrix.columncount();
}
sum
}
fn pivot_of<const WORD_COUNT: usize>(
matrix: &BitMatrix<WORD_COUNT>,
starting_at: (usize, usize),
) -> (usize, usize) {
let (mut row_index, mut column_index) = starting_at;
if row_index >= matrix.rowcount() || column_index >= matrix.columncount() {
return (row_index, column_index);
}
while !matrix.get((row_index, column_index)) {
row_index += 1;
if row_index == matrix.rowcount() {
column_index += 1;
row_index = starting_at.0;
if column_index == matrix.columncount() {
break;
}
}
}
(row_index, column_index)
}
struct ReductionData<const WORD_COUNT: usize> {
column_accessor: BitAccessor<WORD_COUNT>,
blocks_per_row: usize,
rowstride: usize,
base_block: *const BitBlock<WORD_COUNT>,
from_block: *mut BitBlock<WORD_COUNT>,
}
impl<const WORD_COUNT: usize> ReductionData<WORD_COUNT> {
pub fn for_pivot(pivot: (usize, usize), within: &BitMatrix<WORD_COUNT>) -> Self {
let start_block_offset = pivot.1 / BitBlock::<WORD_COUNT>::BITS;
let bit_index = pivot.1 % BitBlock::<WORD_COUNT>::BITS;
let from_block = unsafe { within.rows.get_unchecked(pivot.0).add(start_block_offset) };
let base_block = unsafe { within.blocks.as_ptr().add(start_block_offset) };
let rowstride = within.rowstride();
ReductionData {
column_accessor: BitAccessor::for_index(bit_index),
blocks_per_row: rowstride - start_block_offset,
rowstride,
from_block,
base_block,
}
}
}
fn reduce<const WORD_COUNT: usize>(matrix: &mut BitMatrix<WORD_COUNT>, from: (usize, usize)) {
let data = ReductionData::for_pivot(from, matrix);
let mut to_block = data.from_block;
to_block = reduce_backward_until(data.base_block, to_block, &data);
to_block = unsafe { to_block.add(data.rowstride * matrix.rowcount()) };
let until_block = unsafe { data.from_block.add(data.rowstride) };
reduce_backward_until(until_block, to_block, &data);
}
fn reduce_backward_until<const WORD_COUNT: usize>(
until_block: *const BitBlock<WORD_COUNT>,
mut to_block: *mut BitBlock<WORD_COUNT>,
data: &ReductionData<WORD_COUNT>,
) -> *mut BitBlock<WORD_COUNT> {
while until_block != to_block {
to_block = unsafe { to_block.sub(data.rowstride) };
let column_value = unsafe { data.column_accessor.array_value_of((*to_block).array()) };
if column_value {
add_into_block(to_block, data.from_block, data.blocks_per_row);
}
}
to_block
}
fn add_into_block<const WORD_COUNT: usize>(
mut to_block: *mut BitBlock<WORD_COUNT>,
mut from_block: *const BitBlock<WORD_COUNT>,
block_count: usize,
) {
for _ in 0..block_count {
unsafe {
*to_block ^= &*from_block;
to_block = to_block.add(1);
from_block = from_block.add(1);
}
}
}
/// # Returns
/// Row reduced echelon form R of `matrix` , transformation matrix T and , inverse transpose of T,
/// and row rank profile.
/// T * `matrix` equals R
pub fn rref_with_transforms<const WORD_COUNT: usize>(
mut matrix: BitMatrix<WORD_COUNT>,
) -> (
BitMatrix<WORD_COUNT>,
BitMatrix<WORD_COUNT>,
BitMatrix<WORD_COUNT>,
Vec<usize>,
) {
let num_rows = matrix.rowcount();
let mut transform = BitMatrix::identity(num_rows);
let mut transform_inv_t = BitMatrix::identity(num_rows);
let mut pivot = pivot_of(&matrix, (0, 0));
let mut rank_profile = Vec::<usize>::with_capacity(matrix.columncount());
for row_index in 0..matrix.rowcount() {
if pivot.1 >= matrix.columncount() {
break;
}
matrix.swap_rows(pivot.0, row_index);
transform_inv_t.swap_rows(pivot.0, row_index);
transform.swap_rows(pivot.0, row_index);
pivot.0 = row_index;
rank_profile.push(pivot.1);
reduce_with_transforms(&mut matrix, &mut transform, &mut transform_inv_t, pivot);
pivot = pivot_of(&matrix, (pivot.0 + 1, pivot.1 + 1));
}
(matrix, transform, transform_inv_t, rank_profile)
}
fn reduce_with_transforms<const WORD_COUNT: usize>(
matrix: &mut BitMatrix<WORD_COUNT>,
transform: &mut BitMatrix<WORD_COUNT>,
transform_inv_t: &mut BitMatrix<WORD_COUNT>,
from: (usize, usize),
) {
let rowcount = matrix.rowcount();
for row_index in 0..from.0 {
xor_if_column_with_transforms(
from.1,
matrix,
transform,
transform_inv_t,
row_index,
from.0,
);
}
for row_index in from.0 + 1..rowcount {
xor_if_column_with_transforms(
from.1,
matrix,
transform,
transform_inv_t,
row_index,
from.0,
);
}
}
fn xor_if_column_with_transforms<const WORD_COUNT: usize>(
column_index: usize,
matrix: &mut BitMatrix<WORD_COUNT>,
transform: &mut BitMatrix<WORD_COUNT>,
transform_inv_t: &mut BitMatrix<WORD_COUNT>,
row_index: usize,
from_row_index: usize,
) {
if matrix[(row_index, column_index)] {
matrix.add_into_row(row_index, from_row_index);
transform.add_into_row(row_index, from_row_index);
transform_inv_t.add_into_row(from_row_index, row_index);
}
}
pub fn kernel_basis_matrix<const WORD_COUNT: usize>(
matrix: &BitMatrix<WORD_COUNT>,
) -> BitMatrix<WORD_COUNT> {
let num_cols = matrix.columncount();
let mut rr = matrix.clone();
let rank_profile = rr.echelonize();
let rank_profile_complement = crate::setwise::complement(&rank_profile, num_cols);
let res_row_count = num_cols - rank_profile.len();
let mut res = BitMatrix::zeros(res_row_count, num_cols);
for (index, elt) in rank_profile.into_iter().enumerate() {
for (row_pos, col_src) in rank_profile_complement
.iter()
.enumerate()
.take(res_row_count)
{
res.set((row_pos, elt), rr[(index, *col_src)]);
}
}
for (index, position) in rank_profile_complement.into_iter().enumerate() {
res.set((index, position), true);
}
res
}
pub fn full_rank_row_completion_with_inv<const WORD_COUNT: usize>(
_matrix: &BitMatrix<WORD_COUNT>,
) -> (BitMatrix<WORD_COUNT>, BitMatrix<WORD_COUNT>) {
// let _num_cols = matrix.columncount();
// let rr = matrix.clone();
// let rr2 = matrix.clone();
// (rr,rr2);
todo!()
}
impl<const WORD_COUNT: usize> Bitwise for Column<'_, WORD_COUNT> {
fn weight(&self) -> usize {
self.into_iter().filter(|bit| *bit).count()
}
fn support(&self) -> impl SortedIterator<Item = usize> {
Box::new(
self.into_iter()
.enumerate()
.filter(|pair| pair.1)
.map(|pair| pair.0),
)
.assume_sorted_by_item()
}
fn index(&self, index: usize) -> bool {
let block = unsafe { &*self.rows[index].add(self.block_index) };
self.accessor.array_value_of(block.array())
}
}
impl<const WORD_COUNT: usize> Dot for Column<'_, WORD_COUNT> {
fn dot(&self, other: &Self) -> bool {
assert_eq!(self.rows.len(), other.rows.len());
let mut result = false;
for index in 0..self.rows.len() {
result ^= self.index(index) & other.index(index);
}
result
}
}
impl<const WORD_COUNT: usize> OverlapWeight for Column<'_, WORD_COUNT> {
fn and_weight(&self, other: &Self) -> usize {
assert_eq!(self.rows.len(), other.rows.len());
let mut result = 0usize;
for index in 0..self.rows.len() {
if self.index(index) & other.index(index) {
result += 1;
}
}
result
}
fn or_weight(&self, other: &Self) -> usize {
assert_eq!(self.rows.len(), other.rows.len());
let mut result = 0usize;
for index in 0..self.rows.len() {
if self.index(index) | other.index(index) {
result += 1;
}
}
result
}
}
impl<const WORD_COUNT: usize> PartialEq for Column<'_, WORD_COUNT> {
fn eq(&self, other: &Self) -> bool {
if self.rows.len() != other.rows.len() {
return false;
}
for index in 0..self.rows.len() {
if self.index(index) != other.index(index) {
return false;
}
}
true
}
}
impl<const WORD_COUNT: usize> Eq for Column<'_, WORD_COUNT> {}
impl<const WORD_COUNT: usize> Column<'_, WORD_COUNT> {
#[must_use]
pub fn slice(&self, range: Range<usize>) -> Self {
Column {
rows: &self.rows[range],
accessor: self.accessor.clone(),
block_index: self.block_index,
}
}
#[must_use]
pub fn len(&self) -> usize {
self.rows.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
// impl<'life,const WORD_COUNT: usize> IntoIterator for Column<'life,WORD_COUNT> {
// type Item = bool;
// type IntoIter = ColumnIterator<'life,WORD_COUNT>;
// fn into_iter(self) -> Self::IntoIter {
// ColumnIterator {
// column: self,
// row_index: 0,
// }
// }
// }
impl<'life, const WORD_COUNT: usize> IntoIterator for &'life Column<'_, WORD_COUNT> {
type Item = bool;
type IntoIter = ColumnIterator<'life, WORD_COUNT>;
fn into_iter(self) -> Self::IntoIter {
ColumnIterator {
column: self,
row_index: 0,
}
}
}
impl<const WORD_COUNT: usize> Column<'_, WORD_COUNT> {
#[must_use]
pub fn iter(&self) -> ColumnIterator<'_, WORD_COUNT> {
<&Self as IntoIterator>::into_iter(self)
}
}
pub struct ColumnIterator<'life, const WORD_COUNT: usize = WORD_COUNT_DEFAULT> {
column: &'life Column<'life, WORD_COUNT>,
row_index: usize,
}
impl<const WORD_COUNT: usize> Iterator for ColumnIterator<'_, WORD_COUNT> {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.row_index >= self.column.rows.len() {
return None;
}
let output = self.column.index(self.row_index);
self.row_index += 1;
Some(output)
}
}
impl<const WORD_COUNT: usize> NeutralElement for Column<'_, WORD_COUNT> {
type NeutralElementType = BitVec<WORD_COUNT>;
fn neutral_element(&self) -> Self::NeutralElementType {
BitVec::<WORD_COUNT>::zeros(self.rows.len())
}
fn default_size_neutral_element() -> Self::NeutralElementType {
Self::NeutralElementType::default_size_neutral_element()
}
fn neutral_element_of_size(size: usize) -> Self::NeutralElementType {
Self::NeutralElementType::neutral_element_of_size(size)
}
}
impl<const WORD_COUNT: usize> BitwiseNeutralElement for Column<'_, WORD_COUNT> {}
impl<'life, const WORD_COUNT: usize> BitwiseBinaryOps<Column<'life, WORD_COUNT>>
for BitVec<WORD_COUNT>
{
fn assign(&mut self, other: &Column<'life, WORD_COUNT>) {
for (index, val) in other.into_iter().enumerate() {
self.assign_index(index, val);
}
}
fn bitxor_assign(&mut self, other: &Column<'life, WORD_COUNT>) {
for (index, val) in other.into_iter().enumerate() {
if val {
self.negate_index(index);
}
}
}
fn bitand_assign(&mut self, other: &Column<'life, WORD_COUNT>) {
for (index, val) in other.into_iter().enumerate() {
if !val {
self.assign_index(index, false);
}
}
}
}
impl<'life1, const WORD_COUNT_1: usize, const WORD_COUNT_2: usize>
BitwiseBinaryOps<Column<'life1, WORD_COUNT_1>> for MutableBitView<'_, WORD_COUNT_2>
{
fn assign(&mut self, other: &Column<'life1, WORD_COUNT_1>) {
for (index, val) in other.into_iter().enumerate() {
self.assign_index(index, val);
}
}
fn bitxor_assign(&mut self, other: &Column<'life1, WORD_COUNT_1>) {
for (index, val) in other.into_iter().enumerate() {
if val {
self.negate_index(index);
}
}
}
fn bitand_assign(&mut self, other: &Column<'life1, WORD_COUNT_1>) {
for (index, val) in other.into_iter().enumerate() {
if !val {
self.assign_index(index, false);
}
}
}
}
pub fn is_zero_padded_identity(row_iterator: impl ExactSizeIterator<Item: Bitwise>) -> bool {
row_iterator
.into_iter()
.enumerate()
.all(|(row_index, row)| row.is_one_bit(row_index))
}
pub fn is_zero_padded_symmetric<'life, const WORD_COUNT: usize>(
row_iterator: impl ExactSizeIterator<Item = BitView<'life, WORD_COUNT>>,
column_count: usize,
) -> bool {
let matrix = BitMatrix::from_row_iter(row_iterator, column_count);
matrix == matrix.transposed()
}
pub fn are_zero_rows(mut row_iterator: impl Iterator<Item: Bitwise>) -> bool {
row_iterator.all(|row| row.is_zero())
}microsoft/qdk
Publicmirrored from https://github.com/microsoft/qdkAvailable
source/paulimer/src/bits/bitmatrix.rs
1139lines · modepreview