use anyhow::anyhow;
use cpu_time::ProcessTime;
use lax::Lapack;
use std::time::SystemTime;
use num_traits::float::Float;
use ndarray::{Array1, Array2};
use sprs::{CsMat, TriMatI};
use annembed::tools::svdapprox::*;
pub fn csr_row_normalization<F>(csr_mat: &mut CsMat<F>)
where
F: Float
+ std::ops::AddAssign
+ std::ops::DivAssign
+ std::fmt::Display
+ ndarray::ScalarOperand
+ Lapack
+ sprs::MulAcc
+ for<'r> std::ops::MulAssign<&'r F>,
{
log::trace!("csr_row_normalization");
assert!(csr_mat.is_csr());
let (nb_row, _) = csr_mat.shape();
let mut range_i: std::ops::Range<usize>;
let mut nb_non_null_row = 0usize;
for i in 0..nb_row {
let mut sum_i: F = F::zero();
range_i = csr_mat.indptr().outer_inds_sz(i);
{
let data = csr_mat.data();
for j in range_i.clone() {
if i == csr_mat.indices()[j] {
log::trace!("got diagonal term ({},{}) val : {} ", i, i, data[i]);
}
sum_i += data[j];
}
}
if !(sum_i > F::zero()) {
log::trace!("csr_row_normalization null sum of row i {}", i);
nb_non_null_row += 1;
} else {
let data = csr_mat.data_mut();
for j in range_i.clone() {
data[j] /= sum_i;
}
}
} log::trace!(
"csr_row_normalization nb row with null sum : {}",
nb_row - nb_non_null_row
);
}
pub fn dense_row_normalization<F>(mat: &mut Array2<F>)
where
F: Float
+ std::ops::DivAssign
+ Lapack
+ ndarray::ScalarOperand
+ sprs::MulAcc
+ for<'r> std::ops::MulAssign<&'r F>,
{
let (nb_row, _) = mat.dim();
let mut nb_null_row = 0usize;
for i in 0..nb_row {
let mut row = mat.row_mut(i);
let sum_i = row.sum();
if sum_i > F::zero() {
row.map_mut(|x| *x /= sum_i);
} else {
nb_null_row += 1;
log::trace!("dense_row_normalization null sum of row i {}", i);
}
}
if nb_null_row > 0 {
log::error!(
"dense_row_normalization nb row with null sum : {}",
nb_null_row
);
}
}
pub fn matrepr_row_normalization<F>(mat: &mut MatRepr<F>)
where
F: Float
+ lax::Lapack
+ ndarray::ScalarOperand
+ sprs::MulAcc
+ for<'r> std::ops::MulAssign<&'r F>
+ Sync
+ Default,
{
match &mut mat.get_data_mut() {
MatMode::FULL(mat) => {
dense_row_normalization(mat);
}
MatMode::CSR(csrmat) => {
assert!(csrmat.is_csr());
csr_row_normalization(csrmat);
}
}
}
pub fn adamic_adar_normalization_full<F>(mat: &mut Array2<F>) -> Result<(), anyhow::Error>
where
F: Float
+ Lapack
+ ndarray::ScalarOperand
+ sprs::MulAcc
+ for<'r> std::ops::MulAssign<&'r F>
+ std::ops::AddAssign
+ Default,
{
let (nb_row, nb_col) = mat.dim();
assert_eq!(nb_row, nb_col);
let mut diagonal = Array1::<F>::zeros(nb_row);
for i in 0..nb_row {
diagonal[i] += mat.row(i).sum();
}
let tmat = mat.t();
for i in 0..nb_row {
diagonal[i] += tmat.row(i).sum();
}
for i in 0..nb_row {
if diagonal[i] <= F::zero() {
log::error!("adamic_adar_normalization_full , cannot do adamic transform, null coeff at index {}", i);
return Err(anyhow!("adamic_adar_normalization_full , cannot do adamic transform, null coeff at index {}", i));
}
diagonal[i] = F::one() / diagonal[i];
}
let mut adamat = mat.clone().to_owned();
for i in 0..nb_row {
adamat.row_mut(i).map_inplace(|x| {
*x *= &diagonal[i];
});
}
*mat = mat.dot(&adamat);
Ok(())
}
pub fn adamic_adar_normalization_csmat<F>(mat: &mut CsMat<F>) -> Result<(), anyhow::Error>
where
F: Float
+ ndarray::ScalarOperand
+ std::ops::AddAssign
+ std::ops::DivAssign
+ Lapack
+ sprs::MulAcc
+ for<'r> std::ops::MulAssign<&'r F>
+ Default
+ Send
+ Sync,
{
log::trace!("entering adamic_adar_normalization_csmat");
let (nb_row, nb_col) = mat.shape();
assert_eq!(nb_row, nb_col);
let nnz = mat.nnz();
log::debug!("original matrix nrow = {}, nnz = {}", nb_row, nnz);
let mut diagonal = Array1::<F>::zeros(nb_row);
let mut rows: Vec<usize> = Vec::<usize>::with_capacity(nnz);
let mut cols: Vec<usize> = Vec::<usize>::with_capacity(nnz);
let mut values: Vec<F> = Vec::<F>::with_capacity(nnz);
let cs_iter = mat.iter();
for item in cs_iter {
let row = item.1 .0;
let col = item.1 .1;
diagonal[row] += *item.0;
diagonal[col] += *item.0;
rows.push(row);
cols.push(col);
values.push(*item.0);
}
for i in 0..values.len() {
let row = rows[i];
assert!(diagonal[row] != F::zero());
values[i] /= diagonal[row];
}
let da_trimat = TriMatI::<F, usize>::from_triplets((nb_row, nb_col), rows, cols, values);
let da = da_trimat.to_csr();
log::trace!("calling sprs::smmp::mul_csr_csr ");
let cpu_start = ProcessTime::now();
let sys_start = SystemTime::now();
let csmat_ada = sprs::smmp::mul_csr_csr(mat.view(), da.view());
log::trace!(
"sprs::smmp::mul_csr_csr time elasped sys (s) sys, {}, cpu : {:?}",
sys_start.elapsed().unwrap().as_secs(),
cpu_start.elapsed().as_secs()
);
*mat = csmat_ada;
log::trace!("exiting at end of adamic_adar_normalization_csmat");
Ok(())
}
pub fn matrepr_adamic_adar_normalization<F>(mat: &mut MatRepr<F>)
where
F: Float
+ ndarray::ScalarOperand
+ lax::Lapack
+ sprs::MulAcc
+ for<'r> std::ops::MulAssign<&'r F>
+ Default
+ Send
+ Sync,
{
match &mut mat.get_data_mut() {
MatMode::FULL(mat) => {
let _res = adamic_adar_normalization_full(mat);
}
MatMode::CSR(csrmat) => {
assert!(csrmat.is_csr());
let _res = adamic_adar_normalization_csmat(csrmat);
}
}
}
mod tests {
#[allow(unused)]
use super::*;
use sprs::TriMatBase;
#[allow(dead_code)]
fn log_init_test() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[allow(unused)]
fn get_wiki_csr_mat_f64() -> CsMat<f64> {
let mut rows = Vec::<usize>::with_capacity(5);
let mut cols = Vec::<usize>::with_capacity(5);
let mut values = Vec::<f64>::with_capacity(5);
rows.push(0);
cols.push(0);
values.push(1.);
rows.push(0);
cols.push(4);
values.push(2.);
rows.push(1);
cols.push(2);
values.push(3.);
rows.push(2);
cols.push(1);
values.push(1.);
rows.push(3);
cols.push(1);
values.push(2.);
rows.push(3);
cols.push(3);
values.push(4.);
let trimat = TriMatBase::<Vec<usize>, Vec<f64>>::from_triplets((4, 5), rows, cols, values);
let csr_mat: CsMat<f64> = trimat.to_csr();
csr_mat
}
#[test]
fn test_csr_row_normalization() {
log_init_test();
let mut csr_mat = get_wiki_csr_mat_f64();
csr_row_normalization(&mut csr_mat);
let dense = csr_mat.to_dense();
let check = (dense[[0, 0]] - 1. / 3.).abs();
log::debug!("check (0,0): {}", check);
assert!(check < 1.0E-10);
let check = (dense[[0, 4]] - 2. / 3.).abs();
log::debug!("check (0,4): {}", check);
assert!(check < 1.0E-10);
let check = (dense[[3, 1]] - 1. / 3.).abs();
log::debug!("check (3,1): {}", check);
assert!(check < 1.0E-10);
}
#[test]
fn test_full_row_normalization() {
log_init_test();
let mut dense = ndarray::arr2(
&[
[1., 0., 0., 0., 2.], [0., 0., 3., 0., 0.], [0., 1., 0., 0., 0.], [0., 2., 0., 4., 0.],
], );
dense_row_normalization(&mut dense);
let check = num_traits::Float::abs(dense[[0, 0]] - 1. / 3.);
log::debug!("check (0,0): {}", check);
assert!(check < 1.0E-10);
let check = num_traits::Float::abs(dense[[0, 4]] - 2. / 3.);
log::debug!("check (0,4): {}", check);
assert!(check < 1.0E-10);
let check = num_traits::Float::abs(dense[[3, 1]] - 1. / 3.);
log::debug!("check (3,1): {}", check);
assert!(check < 1.0E-10);
}
}