///! This file demonstrates how it is possible to reduce the fill-in of a ///! symmetric sparse matrix during Cholesky decomposition. use image::{ImageBuffer, ImageResult, Luma}; use ndarray::{arr2, ArrayView2}; use std::env; use std::error::Error; use std::path::Path; fn small_lap_mat() -> sprs::CsMat { // 0 - A - 2 - 3 // | \ | \ | / | // 7 - 5 - 6 - 4 // | / | / | \ | // 8 - 9 - 1 - E #[rustfmt::skip] let triangles = arr2( &[[0, 7, 5], [0, 5, 10], [10, 5, 6], [10, 6, 2], [2, 6, 3], [3, 6, 4], [7, 8, 5], [5, 8, 9], [5, 9, 6], [6, 9, 1], [6, 1, 11], [6, 11, 4]], ); let lap_mat = sprs::special_mats::tri_mesh_graph_laplacian(12, triangles.view()); lap_mat } fn lap_mat_from_obj(path: &str) -> Result, tobj::LoadError> { let (objects, _materials) = tobj::load_obj( &Path::new(path), &tobj::LoadOptions { triangulate: true, ..Default::default() }, )?; for obj in objects { let nb_triangles = obj.mesh.indices.len() / 3; let nb_vertices = obj.mesh.positions.len() / 3; let triangles = ArrayView2::from_shape((nb_triangles, 3), &obj.mesh.indices[..]) .unwrap(); let lap_mat = sprs::special_mats::tri_mesh_graph_laplacian( nb_vertices, triangles, ); return Ok(lap_mat.to_other_types()); } eprintln!("No model found in obj file"); Err(tobj::LoadError::ReadError) } fn save_gray_image( path: impl AsRef, image: ArrayView2, ) -> ImageResult<()> { let height = image.shape()[0]; let width = image.shape()[1]; let im: Option, _>> = image.as_slice().map(|slice| { ImageBuffer::from_raw(width as u32, height as u32, slice) .expect("failed to create image from slice") }); let im = im.expect("non-contiguous ndarray Array"); im.save(path)?; Ok(()) } fn main() -> Result<(), Box> { let mut args = env::args(); args.next(); let lap_mat = if let Some(path) = args.next() { lap_mat_from_obj(&path)? } else { small_lap_mat() }; let ordering = sprs::linalg::reverse_cuthill_mckee(lap_mat.view()); let perm_lap = sprs::transform_mat_papt(lap_mat.view(), ordering.perm.view()); if let Some(im_folder) = args.next() { println!( "saving nnz patterns in {}/pattern_{{before,after}}.png", im_folder, ); let im_folder = Path::new(&im_folder); save_gray_image( im_folder.join("pattern_before.png"), sprs::visu::nnz_image(lap_mat.view()).view(), )?; save_gray_image( im_folder.join("pattern_after.png"), sprs::visu::nnz_image(perm_lap.view()).view(), )?; } else { println!( "Lap mat nnz pattern:\n\n{}", sprs::visu::nnz_pattern_formatter(lap_mat.view()), ); println!( "After Cuthill-McKee, profile is reduced:\n\n{}", sprs::visu::nnz_pattern_formatter(perm_lap.view()), ); } Ok(()) }