use crate::DType;
use numr::error::Result;
use numr::runtime::Runtime;
use numr::sparse::{SparseOps, SparseTensor};
use numr::tensor::Tensor;
use crate::graph::traits::types::GraphData;
use super::helpers::extract_csr_arrays;
pub fn laplacian_matrix_impl<R, C>(
_client: &C,
graph: &GraphData<R>,
normalized: bool,
) -> Result<SparseTensor<R>>
where
R: Runtime<DType = DType>,
C: SparseOps<R>,
{
let (row_ptrs, col_indices, _values, n) = extract_csr_arrays(graph)?;
let device = match &graph.adjacency {
SparseTensor::Csr(csr) => csr.values().device().clone(),
_ => unreachable!(),
};
let degrees: Vec<f64> = (0..n)
.map(|u| {
let start = row_ptrs[u] as usize;
let end = row_ptrs[u + 1] as usize;
(end - start) as f64
})
.collect();
if !normalized {
let mut lap_row_ptrs = Vec::with_capacity(n + 1);
let mut lap_col_indices = Vec::new();
let mut lap_values = Vec::new();
lap_row_ptrs.push(0);
for u in 0..n {
let start = row_ptrs[u] as usize;
let end = row_ptrs[u + 1] as usize;
lap_col_indices.push(u as i64);
lap_values.push(degrees[u]);
for &v_idx in col_indices.iter().take(end).skip(start) {
let v = v_idx as usize;
if v != u {
lap_col_indices.push(v as i64);
lap_values.push(-1.0);
}
}
lap_row_ptrs.push(lap_col_indices.len() as i64);
}
let row_ptrs_tensor = Tensor::<R>::from_slice(&lap_row_ptrs, &[n + 1], &device);
let col_indices_tensor =
Tensor::<R>::from_slice(&lap_col_indices, &[lap_col_indices.len()], &device);
let values_tensor = Tensor::<R>::from_slice(&lap_values, &[lap_values.len()], &device);
let csr =
numr::sparse::CsrData::new(row_ptrs_tensor, col_indices_tensor, values_tensor, [n, n])?;
Ok(SparseTensor::Csr(csr))
} else {
let mut lap = vec![vec![0.0; n]; n];
let deg_inv_sqrt: Vec<f64> = degrees
.iter()
.map(|&d| if d > 0.0 { d.sqrt().recip() } else { 0.0 })
.collect();
for u in 0..n {
lap[u][u] = 1.0;
let start = row_ptrs[u] as usize;
let end = row_ptrs[u + 1] as usize;
for &v_idx in col_indices.iter().take(end).skip(start) {
let v = v_idx as usize;
if u != v {
lap[u][v] -= deg_inv_sqrt[u] * deg_inv_sqrt[v];
}
}
}
let mut coo_rows = Vec::new();
let mut coo_cols = Vec::new();
let mut coo_vals = Vec::new();
for (i, row) in lap.iter().enumerate().take(n) {
for (j, &val) in row.iter().enumerate().take(n) {
if val.abs() > 1e-15 {
coo_rows.push(i as i64);
coo_cols.push(j as i64);
coo_vals.push(val);
}
}
}
let sparse =
SparseTensor::<R>::from_coo_slices(&coo_rows, &coo_cols, &coo_vals, [n, n], &device)?;
sparse.to_csr()
}
}
pub fn adjacency_matrix_impl<R, C>(_client: &C, graph: &GraphData<R>) -> Result<SparseTensor<R>>
where
R: Runtime<DType = DType>,
C: SparseOps<R>,
{
Ok(graph.adjacency.clone())
}
pub fn incidence_matrix_impl<R, C>(_client: &C, graph: &GraphData<R>) -> Result<SparseTensor<R>>
where
R: Runtime<DType = DType>,
C: SparseOps<R>,
{
let (row_ptrs, col_indices, _values, n) = extract_csr_arrays(graph)?;
let device = match &graph.adjacency {
SparseTensor::Csr(csr) => csr.values().device().clone(),
_ => unreachable!(),
};
let mut edge_list = Vec::new();
for u in 0..n {
let start = row_ptrs[u] as usize;
let end = row_ptrs[u + 1] as usize;
for &v_idx in col_indices.iter().take(end).skip(start) {
let v = v_idx as usize;
if graph.directed || u < v {
edge_list.push((u, v));
}
}
}
let m = edge_list.len(); if m == 0 {
return SparseTensor::<R>::from_coo_slices::<f64>(&[], &[], &[], [n, 1], &device);
}
let mut inc_rows = Vec::new();
let mut inc_cols = Vec::new();
let mut inc_vals = Vec::new();
for (e, (u, v)) in edge_list.iter().enumerate() {
let e = e as i64;
inc_rows.push(*u as i64);
inc_cols.push(e);
inc_vals.push(-1.0);
inc_rows.push(*v as i64);
inc_cols.push(e);
inc_vals.push(1.0);
}
SparseTensor::<R>::from_coo_slices(&inc_rows, &inc_cols, &inc_vals, [n, m], &device)?.to_csr()
}