use faer::sparse::SparseColMat;
#[inline]
fn fold(mut hash: u64, value: usize) -> u64 {
const PRIME: u64 = 0x100000001b3;
const MIX: u64 = 0x9E3779B97F4A7C15;
hash ^= (value as u64).wrapping_mul(MIX);
hash.wrapping_mul(PRIME)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct PatternFingerprint {
nrows: usize,
ncols: usize,
nnz: usize,
hash: u64,
}
impl PatternFingerprint {
pub fn of(matrix: &SparseColMat<usize, f64>) -> Self {
let symbolic = matrix.symbolic();
let mut hash: u64 = 0xcbf29ce484222325;
hash = fold(hash, matrix.nrows());
hash = fold(hash, matrix.ncols());
let mut nnz = 0usize;
for col in 0..matrix.ncols() {
let rows = symbolic.row_idx_of_col_raw(col);
hash = fold(hash, rows.len());
for &row in rows {
hash = fold(hash, row);
nnz += 1;
}
}
Self {
nrows: matrix.nrows(),
ncols: matrix.ncols(),
nnz,
hash,
}
}
pub fn nrows(&self) -> usize {
self.nrows
}
pub fn ncols(&self) -> usize {
self.ncols
}
pub fn nnz(&self) -> usize {
self.nnz
}
}
#[cfg(test)]
mod tests {
use super::*;
use faer::sparse::Triplet;
type TestResult = Result<(), Box<dyn std::error::Error>>;
#[test]
fn equal_patterns_agree() -> TestResult {
let t = vec![
Triplet::new(0usize, 0usize, 1.0f64),
Triplet::new(1, 0, 2.0),
Triplet::new(1, 1, 3.0),
];
let a = SparseColMat::try_new_from_triplets(2, 2, &t)?;
let b = SparseColMat::try_new_from_triplets(2, 2, &t)?;
assert_eq!(PatternFingerprint::of(&a), PatternFingerprint::of(&b));
let t2 = vec![
Triplet::new(0usize, 0usize, 9.0f64),
Triplet::new(1, 0, 8.0),
Triplet::new(1, 1, 7.0),
];
let c = SparseColMat::try_new_from_triplets(2, 2, &t2)?;
assert_eq!(PatternFingerprint::of(&a), PatternFingerprint::of(&c));
Ok(())
}
#[test]
fn permuted_pattern_at_equal_nnz_disagrees() -> TestResult {
let t = vec![
Triplet::new(0usize, 0usize, 1.0f64),
Triplet::new(1, 0, 2.0),
Triplet::new(1, 1, 3.0),
];
let a = SparseColMat::try_new_from_triplets(2, 2, &t)?;
let t2 = vec![
Triplet::new(0usize, 0usize, 1.0f64),
Triplet::new(1, 0, 2.0),
Triplet::new(0, 1, 3.0),
];
let b = SparseColMat::try_new_from_triplets(2, 2, &t2)?;
assert_eq!((a.nrows(), a.ncols(), 3), (b.nrows(), b.ncols(), 3));
assert_ne!(
PatternFingerprint::of(&a),
PatternFingerprint::of(&b),
"equal-nnz permutation must invalidate the fingerprint"
);
Ok(())
}
}