use rustc_hash::{FxBuildHasher, FxHashMap};
use sprs::{CsMat, TriMat};
type CoordinateMap = FxHashMap<usize, f64>;
#[derive(Debug, Clone)]
pub struct CooBuilder {
rows: usize,
cols: usize,
entries: CoordinateMap,
}
impl CooBuilder {
pub fn new(n: usize) -> Self {
Self::new_rect(n, n)
}
pub fn with_capacity(n: usize, capacity: usize) -> Self {
Self::with_capacity_rect(n, n, capacity)
}
pub fn new_rect(rows: usize, cols: usize) -> Self {
Self {
rows,
cols,
entries: CoordinateMap::default(),
}
}
pub fn with_capacity_rect(rows: usize, cols: usize, capacity: usize) -> Self {
Self {
rows,
cols,
entries: CoordinateMap::with_capacity_and_hasher(capacity, FxBuildHasher),
}
}
#[inline]
pub fn n(&self) -> usize {
self.rows
}
#[inline]
pub fn shape(&self) -> (usize, usize) {
(self.rows, self.cols)
}
#[inline]
pub fn add(&mut self, i: usize, j: usize, v: f64) {
if v == 0.0 {
return;
}
assert!(
i < self.rows && j < self.cols,
"COO coordinate ({i}, {j}) out of bounds for shape {}x{}",
self.rows,
self.cols
);
let key = i
.checked_mul(self.cols)
.and_then(|base| base.checked_add(j))
.expect("COO matrix dimensions overflow usize");
*self.entries.entry(key).or_insert(0.0) += v;
}
#[inline]
pub fn add_sym(&mut self, i: usize, j: usize, v: f64) {
if i == j {
self.add(i, j, v);
} else {
self.add(i, j, v);
self.add(j, i, v);
}
}
pub fn finish_csr(self) -> CsMat<f64> {
let mut tri = TriMat::with_capacity((self.rows, self.cols), self.entries.len());
for (key, v) in self.entries {
if v != 0.0 {
let i = key / self.cols;
let j = key % self.cols;
tri.add_triplet(i, j, v);
}
}
tri.to_csr()
}
pub fn finish_csc(self) -> CsMat<f64> {
self.finish_csr().to_csc()
}
}