use anyhow::anyhow;
use cpu_time::ProcessTime;
use std::time::SystemTime;
use num_traits::float::*;
pub use super::gsvd::{GSvd, GSvdOptParams, GSvdResult};
use lax::Lapack;
use ndarray::Array2;
use annembed::tools::svdapprox::*;
#[cfg_attr(doc, katexit::katexit)]
pub struct GSvdApprox<F: Lapack> {
mat1: MatRepr<F>,
mat2: MatRepr<F>,
opt_params: Option<GSvdOptParams>,
target: RangeApproxMode,
}
impl<F> GSvdApprox<F>
where
F: Send
+ Sync
+ Float
+ Lapack
+ ndarray::ScalarOperand
+ sprs::MulAcc
+ for<'r> std::ops::MulAssign<&'r F>
+ num_traits::MulAdd
+ Default,
{
pub fn new(
mat1: MatRepr<F>,
mat2: MatRepr<F>,
target: RangeApproxMode,
opt_params: Option<GSvdOptParams>,
) -> Self {
if mat1.shape()[1] != mat2.shape()[1] {
log::error!("The two matrices for GSvdApprox must have the same number of columns");
println!("The two matrices for GSvdApprox must have the same number of columns");
panic!("Error constructiing Gsvd problem");
}
GSvdApprox {
mat1,
mat2,
opt_params,
target,
}
}
pub fn get_parameters(&mut self) -> &Option<GSvdOptParams> {
&self.opt_params
}
pub fn do_approx_gsvd(&self) -> Result<GSvdResult<F>, anyhow::Error> {
log::debug!("entering apt::do_approx_gsvd");
let cpu_start = ProcessTime::now();
let sys_start = SystemTime::now();
log::debug!("\n\n do_approx_gsvd approximating mat1");
let r_approx1 = RangeApprox::new(&self.mat1, self.target);
let approx1_res = r_approx1.get_approximator();
if approx1_res.is_none() {
return Err(anyhow!("approximation of matrix 1 failed"));
}
let approx1_res = approx1_res.unwrap();
log::debug!("\n\n do_approx_gsvd approximating mat2");
let r_approx2 = RangeApprox::new(&self.mat2, self.target);
let approx2_res = r_approx2.get_approximator();
if approx2_res.is_none() {
return Err(anyhow!("approximation of matrix 2 failed"));
}
let approx2_res = approx2_res.unwrap();
log::debug!("\n\n remultiplying by transpose of approximator");
let mut a = match self.mat1.get_data() {
MatMode::FULL(mat) => approx1_res.t().dot(mat),
MatMode::CSR(mat) => {
log::trace!("direct_svd got csr matrix");
transpose_dense_mult_csr(&approx1_res, mat)
}
};
let mut b = match self.mat2.get_data() {
MatMode::FULL(mat) => approx2_res.t().dot(mat),
MatMode::CSR(mat) => {
log::trace!("direct_svd got csr matrix");
transpose_dense_mult_csr(&approx2_res, mat)
}
};
let mut gsvd_pb = GSvd::new(&mut a, &mut b);
let gsvd_res = gsvd_pb.do_gsvd();
if gsvd_res.is_err() {
return Err(anyhow!("Gsvd failed"));
}
println!(
"do_approx_gsvd sys time(s) {:.2e} cpu time(s) {:.2e}",
sys_start.elapsed().unwrap().as_secs(),
cpu_start.elapsed().as_secs()
);
log::info!(
"do_approx_gsvd sys time(s) {:.2e} cpu time(s) {:.2e}",
sys_start.elapsed().unwrap().as_secs(),
cpu_start.elapsed().as_secs()
);
log::debug!("exiting apt::do_approx_gsvd");
gsvd_res
}
pub fn compute_gsvd_residual(&self) -> f64 {
panic!("not yet implemented");
}
}
mod tests {
#[allow(unused)]
use super::*;
#[allow(unused)]
use ndarray::array;
#[allow(unused)]
use num_traits::ToPrimitive;
use sprs::{CsMat, TriMat};
#[allow(unused)]
fn log_init_test() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[allow(unused)]
fn smallmat_to_csr(a: &Array2<f64>) -> CsMat<f64> {
let mut trim = TriMat::new(a.dim());
for (idx, val) in a.indexed_iter() {
trim.add_triplet(idx.0, idx.1, *val);
}
trim.to_csr()
}
#[test]
fn test_gsvd_dense_precision_1() {
log_init_test();
let mat_a = array![
[1., 6., 11.],
[2., 7., 12.],
[3., 8., 13.],
[4., 9., 14.],
[5., 10., 15.]
];
let mat_b = array![[8., 1., 6.], [3., 5., 7.], [4., 9., 2.]];
let a = MatRepr::<f64>::from_array2(mat_a);
let b = MatRepr::<f64>::from_array2(mat_b);
let precision = RangePrecision::new(0.1, 3, 3);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::EPSIL(precision), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
res.debug_print();
}
#[test]
fn test_gsvd_csr_precision_1() {
log_init_test();
let mat_a = array![
[1., 6., 11.],
[2., 7., 12.],
[3., 8., 13.],
[4., 9., 14.],
[5., 10., 15.]
];
let mat_b = array![[8., 1., 6.], [3., 5., 7.], [4., 9., 2.]];
let csr_a = smallmat_to_csr(&mat_a);
let csr_b = smallmat_to_csr(&mat_b);
let a = MatRepr::from_csrmat(csr_a);
let b = MatRepr::from_csrmat(csr_b);
let precision = RangePrecision::new(0.1, 2, 3);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::EPSIL(precision), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
res.debug_print();
}
#[test]
fn test_gsvd_dense_rank_1() {
log_init_test();
let mat_a = array![
[1., 6., 11.],
[2., 7., 12.],
[3., 8., 13.],
[4., 9., 14.],
[5., 10., 15.]
];
let mat_b = array![[8., 1., 6.], [3., 5., 7.], [4., 9., 2.]];
let a = MatRepr::<f64>::from_array2(mat_a.clone());
let b = MatRepr::<f64>::from_array2(mat_b.clone());
println!("\n test_gsvd_dense_rank with rank 3");
let target = RangeRank::new(3, 2);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::RANK(target), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
res.debug_print();
assert!((res.get_alpha().unwrap()[0].to_f64().unwrap() - 0.9807).abs() < 1.0E-4);
assert!((res.get_alpha().unwrap()[1].to_f64().unwrap() - 0.3155).abs() < 1.0E-4);
assert!((res.get_alpha().unwrap()[2].to_f64().unwrap()).abs() < 1.0E-4);
println!("\n test_gsvd_dense_rank with rank 2");
let a = MatRepr::<f64>::from_array2(mat_a.clone());
let b = MatRepr::<f64>::from_array2(mat_b.clone());
let target = RangeRank::new(2, 2);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::RANK(target), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
res.debug_print();
assert!((res.get_alpha().unwrap()[1].to_f64().unwrap() - 0.3424).abs() < 1.0E-4);
println!("\n test_gsvd_dense_rank with rank 1");
let a = MatRepr::<f64>::from_array2(mat_a.clone());
let b = MatRepr::<f64>::from_array2(mat_b.clone());
let target = RangeRank::new(1, 2);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::RANK(target), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
res.debug_print();
}
#[test]
fn test_gsvd_csr_rank_1() {
log_init_test();
let mat_a = array![
[1., 6., 11.],
[2., 7., 12.],
[3., 8., 13.],
[4., 9., 14.],
[5., 10., 15.]
];
let mat_b = array![[8., 1., 6.], [3., 5., 7.], [4., 9., 2.]];
let csr_a = smallmat_to_csr(&mat_a);
let csr_b = smallmat_to_csr(&mat_b);
let a = MatRepr::from_csrmat(csr_a.clone());
let b = MatRepr::from_csrmat(csr_b.clone());
println!("\n test_gsvd_dense_rank with rank 3");
let target = RangeRank::new(3, 2);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::RANK(target), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
assert!((res.get_alpha().unwrap()[0].to_f64().unwrap() - 0.9807).abs() < 1.0E-4);
assert!((res.get_alpha().unwrap()[1].to_f64().unwrap() - 0.3155).abs() < 1.0E-4);
assert!((res.get_alpha().unwrap()[2].to_f64().unwrap()).abs() < 1.0E-4);
res.debug_print();
let a = MatRepr::from_csrmat(csr_a.clone());
let b = MatRepr::from_csrmat(csr_b.clone());
println!("\n test_gsvd_dense_rank with rank 2");
let target = RangeRank::new(2, 2);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::RANK(target), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
res.debug_print();
log::debug!(
"alpha 1 : {}",
res.get_alpha().unwrap()[1].to_f64().unwrap()
);
assert!((res.get_alpha().unwrap()[1].to_f64().unwrap() - 0.3424).abs() < 1.0E-4);
let a = MatRepr::from_csrmat(csr_a.clone());
let b = MatRepr::from_csrmat(csr_b.clone());
println!("\n test_gsvd_dense_rank with rank 1");
let target = RangeRank::new(1, 2);
let approx_svd = GSvdApprox::<f64>::new(a, b, RangeApproxMode::RANK(target), None);
let res = approx_svd.do_approx_gsvd();
assert!(res.is_ok());
let res = res.unwrap();
res.debug_print(); } }