Skip to content

Commit

Permalink
laplace kernel implementation and stats recording
Browse files Browse the repository at this point in the history
  • Loading branch information
ignacia-fp committed Feb 6, 2025
1 parent fbfddc3 commit 4edcdb8
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 131 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ ndarray-linalg = "0.16.0"
rand_distr = "0.4"
npy = "0.4.0"
plotters = "0.3.7"
paste = "1"

[profile.release]
debug = 1

[dev-dependencies]
rand_distr = "0.4.3"
#criterion = { version = "0.5.*", features = ["html_reports"]}
paste = "1"

[build-dependencies]
cbindgen = "0.27.0"
Expand Down
246 changes: 167 additions & 79 deletions examples/test_end_to_end.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bempp_rsrs::{rsrs::{rsrs_cycle::{Rsrs, RsrsData, RsrsOptions}, rsrs_factors::{RsrsFactors, RsrsFactorsOps}}, utils::{geometries::{cube_surface, sphere_surface}, low_rank_matrices::get_matrix}};
use bempp_rsrs::{rsrs::{box_skeletonisation::Tols, rsrs_cycle::{Rsrs, RsrsData, RsrsOptions}, rsrs_factors::{RsrsFactors, RsrsFactorsOps}}, utils::{geometries::{cube_surface, sphere_surface}, low_rank_matrices::{get_exp_real_f64, get_laplace_f64}}};
use mpi::{topology::SimpleCommunicator, traits::Communicator};
use num::NumCast;
use std::{error::Error, fs};
use bempp_octree::Octree;
use std::io::BufWriter;
Expand All @@ -8,8 +9,17 @@ use std::path::Path;
use std::io::Write;
use std::fs::File;

fn write_vec_to_new_file_u128(path: impl AsRef<Path>, value: &[u128]) -> Result<(), Box<dyn Error>> {
let file = File::create(path.as_ref())?;
let mut writer = BufWriter::new(file);
for x in value {
writeln!(writer, "{x}")?;
}
writer.flush()?;
Ok(())
}

fn write_vec_to_new_file(path: impl AsRef<Path>, value: &[f64]) -> Result<(), Box<dyn Error>> {
fn write_vec_to_new_file<Item: RlstScalar>(path: impl AsRef<Path>, value: &[Item]) -> Result<(), Box<dyn Error>> {
let file = File::create(path.as_ref())?;
let mut writer = BufWriter::new(file);
for x in value {
Expand All @@ -19,7 +29,7 @@ fn write_vec_to_new_file(path: impl AsRef<Path>, value: &[f64]) -> Result<(), Bo
Ok(())
}

fn write_multi_vec_to_new_file(path: impl AsRef<Path>, values: &[Vec<f64>]){
fn write_multi_vec_to_new_file<Item: RlstScalar>(path: impl AsRef<Path>, values: &[Vec<Item>]){
for (id, val) in values.iter().enumerate(){
let mut new_path = path.as_ref().to_str().unwrap().to_string();
new_path.push_str(&id.to_string());
Expand All @@ -28,12 +38,58 @@ fn write_multi_vec_to_new_file(path: impl AsRef<Path>, values: &[Vec<f64>]){
}
}

type Real<T> = <T as rlst::RlstScalar>::Real;

fn save_stats<Item: RlstScalar>(rsrs_data: &RsrsData<Item>, tol: Real<Item>, path_str: &str){
let string_tol = format!("{:e}", tol);

let mut times_path = path_str.to_string();
times_path.push_str("/times_");
times_path.push_str(&string_tol);
times_path.push('/');

fs::create_dir_all(Path::new(&times_path)).unwrap();

fn get_box_errors(kernel_mat: &mut DynamicArray<f64, 2>, rsrs_factors: &RsrsFactors<f64>, tol: f64, path_str: &str)->(f64, f64, f64){
let mut sampling_path = times_path.clone();
sampling_path.push_str("sampling.json");

let mut nullification_path = times_path.clone();
nullification_path.push_str("nullification.json");

let mut id_time_path = times_path.clone();
id_time_path.push_str("id.json");

let mut lu_time_path = times_path.clone();
lu_time_path.push_str("lu.json");

let mut update_id_time_path = times_path.clone();
update_id_time_path.push_str("update_id.json");

let mut update_lu_time_path = times_path.clone();
update_lu_time_path.push_str("update_lu.json");

let mut mixed_path = times_path.clone();
mixed_path.push_str("mixed.json");

let mixed_res = [rsrs_data.stats.total_elapsed_time as u128, rsrs_data.stats.extraction_time as u128, rsrs_data.stats.residual_size as u128];

let _ = write_vec_to_new_file_u128(sampling_path, &rsrs_data.stats.sampling_time);
let _ = write_vec_to_new_file_u128(nullification_path, &rsrs_data.stats.nullification_time);
let _ = write_vec_to_new_file_u128(id_time_path, &rsrs_data.stats.id_time);
let _ = write_vec_to_new_file_u128(lu_time_path, &rsrs_data.stats.lu_time);
let _ = write_vec_to_new_file_u128(update_id_time_path, &rsrs_data.stats.update_id_time);
let _ = write_vec_to_new_file_u128(update_lu_time_path, &rsrs_data.stats.update_lu_time);
let _ = write_vec_to_new_file_u128(mixed_path, &mixed_res);


}

fn get_box_errors<Item: RlstScalar + MatrixInverse + MatrixPseudoInverse + MatrixId>(kernel_mat: &mut DynamicArray<Item, 2>, rsrs_factors: &RsrsFactors<Item>, tol: Real<Item>, path_str: &str)->(Real<Item>, Real<Item>, Real<Item>)
where Real<Item>: for<'a> std::iter::Sum<&'a Real<Item>>
{
let npoints = kernel_mat.shape()[0];
let (rel_errs, abs_errs) = rsrs_factors.get_boxes_errors(kernel_mat, true);
let diag_ae = rsrs_factors.get_diag_errors(kernel_mat);
println!("{:?}", diag_ae);

let string_tol = format!("{:e}", tol);
let mut rel_errors_path = path_str.to_string();
Expand All @@ -53,14 +109,23 @@ fn get_box_errors(kernel_mat: &mut DynamicArray<f64, 2>, rsrs_factors: &RsrsFact
write_multi_vec_to_new_file(&rel_errors_path, &rel_errs);
write_multi_vec_to_new_file(&abs_errors_path, &abs_errs);

let diag_ae_r;
let diag_ae_s;

if diag_ae.len() >1{
diag_ae_r = &diag_ae[0..diag_ae.len()-2];
diag_ae_s = diag_ae[diag_ae.len()-1];
}
else{
diag_ae_r = &[];
diag_ae_s = diag_ae[0];
}

let diag_ae_r = &diag_ae[0..diag_ae.len()-2];
let diag_ae_s = diag_ae[diag_ae.len()-1];

let diag_ae_r_sum = diag_ae_r.iter().sum::<f64>();
let diag_ae_r_mean = diag_ae_r_sum/ diag_ae_r.len() as f64;
let diag_ae_r_sum = diag_ae_r.iter().sum::<<Item as rlst::RlstScalar>::Real>();
let len: Real<Item> = NumCast::from(diag_ae_r.len()).unwrap();
let diag_ae_r_mean = diag_ae_r_sum/len;

let mut ident = rlst_dynamic_array2!(f64, [npoints, npoints]);
let mut ident = rlst_dynamic_array2!(Item, [npoints, npoints]);
ident.set_identity();

let zero = ident - kernel_mat.view();
Expand All @@ -70,85 +135,108 @@ fn get_box_errors(kernel_mat: &mut DynamicArray<f64, 2>, rsrs_factors: &RsrsFact
}
//Function that creates a low rank matrix by calculating a kernel given a random point distribution on an unit sphere.

fn test_rsrs_geometry(geometry: &str, npoints: usize){

let mut geometry_fn: fn(usize, &SimpleCommunicator) -> Vec<bempp_octree::Point> = sphere_surface;

if geometry == "cube"{
geometry_fn = cube_surface;
}

let universe: mpi::environment::Universe = mpi::initialize().unwrap();
let comm: SimpleCommunicator = universe.world();
let points: Vec<bempp_octree::Point> = geometry_fn(npoints, &comm);

let max_level: usize = 16;
let max_leaf_points: usize = 50;

let tree: Octree<'_, SimpleCommunicator> = Octree::new(&points, max_level, max_leaf_points, &comm);
let global_number_of_points: usize = tree.global_number_of_points();
let global_max_level: usize = tree.global_max_level();

if comm.rank() == 0 {
println!(
"Setup octree with {} points and maximum level {}",
global_number_of_points,
global_max_level
);
macro_rules! impl_test_geometry{
($scalar:ty) => {
fn test_rsrs_geometry(geometry: &str, kernel: &str, geometry_fn: fn(usize, &SimpleCommunicator) -> Vec<bempp_octree::Point> , kernel_fn: fn(&[bempp_octree::Point])-> DynamicArray<$scalar, 2>, npoints: usize, id_tols: &[<$scalar as RlstScalar>::Real], comm: &SimpleCommunicator)
{
let points: Vec<bempp_octree::Point> = geometry_fn(npoints, &comm);
let max_level: usize = 16;
let max_leaf_points: usize = 50;
let tree: Octree<'_, SimpleCommunicator> = Octree::new(&points, max_level, max_leaf_points, &comm);
let global_number_of_points: usize = tree.global_number_of_points();
let global_max_level: usize = tree.global_max_level();
if comm.rank() == 0 {
println!(
"Setup octree with {} points and maximum level {}",
global_number_of_points,
global_max_level
);
}
let mut app_inv = Vec::new();
let mut diag_errs = Vec::new();
let mut skel_errs = Vec::new();
let mut tot_num_samples = Vec::new();
let mut path_str = "examples/results/".to_string();
let mut geometry_and_points = geometry.to_string();
geometry_and_points.push('_');
geometry_and_points.push_str(kernel);
geometry_and_points.push('_');
geometry_and_points.push_str(&npoints.to_string());
path_str.push_str(&geometry_and_points);
for &id_tol in id_tols.iter(){
println!("Test: {} points, tol:{}", npoints, id_tol);
let tols : Tols<$scalar> = Tols{id: id_tol, null: num::Zero::zero(), lstq: num::Zero::zero()};
let mut kernel_mat: DynamicArray<$scalar, 2> = kernel_fn(&points);
let mut rsrs_algo: RsrsData<$scalar> = <RsrsData<$scalar> as Rsrs>::new(&kernel_mat, tols, &tree);
let options = RsrsOptions{ hermitian: false, silent: true };
let rsrs_factors = rsrs_algo.tree_cycle_and_diag_block_extraction(&kernel_mat, options);
save_stats(&rsrs_algo, id_tol, &path_str);
let (norm_app_inv, diag_ae_mean, skel_ae) = get_box_errors(&mut kernel_mat, &rsrs_factors, id_tol, &path_str);
app_inv.push(norm_app_inv);
if !diag_ae_mean.is_nan(){
diag_errs.push(diag_ae_mean);
}
skel_errs.push(skel_ae);
tot_num_samples.push(rsrs_algo.y_data.num_samples as f64);
}

let mut app_id_path = path_str.clone();
let mut diag_errs_path = path_str.clone();
let mut skel_errs_path = path_str.clone();
let mut num_samples_path = path_str.clone();

app_id_path.push_str("/app_id.json");
diag_errs_path.push_str("/diag_errs.json");
skel_errs_path.push_str("/skel_errs.json");
num_samples_path.push_str("/num_samples.json");

let _= write_vec_to_new_file(&app_id_path, &app_inv);
let _= write_vec_to_new_file(&diag_errs_path, &diag_errs);
let _= write_vec_to_new_file(&skel_errs_path, &skel_errs);
let _= write_vec_to_new_file(&num_samples_path, &tot_num_samples);


}
}
}

let id_tols = [1e-3];//, 5e-3, 1e-3, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14];

let mut app_inv = Vec::new();
let mut diag_errs = Vec::new();
let mut skel_errs = Vec::new();
let mut tot_num_samples = Vec::new();

let mut path_str = "examples/results/".to_string();
let mut geometry_and_points = geometry.to_string();
geometry_and_points.push('_');
geometry_and_points.push_str(&npoints.to_string());
path_str.push_str(&geometry_and_points);

for id_tol in id_tols{
let tols : bempp_rsrs::rsrs::box_skeletonisation::Tols<f64> = bempp_rsrs::rsrs::box_skeletonisation::Tols{id: id_tol, null: 1e-15, lstq: 1e-15};
let mut kernel_mat: Array<f64, BaseArray<f64, VectorContainer<f64>, 2>, 2> = get_matrix(&points);
let mut rsrs_algo: RsrsData<f64> = <RsrsData<f64> as Rsrs>::new(&kernel_mat, tols, &tree);
let options = RsrsOptions{ hermitian: false, silent: true };
let rsrs_factors = rsrs_algo.tree_cycle_and_diag_block_extraction(&kernel_mat, options);
let (norm_app_inv, diag_ae_mean, skel_ae) = get_box_errors(&mut kernel_mat, &rsrs_factors, id_tol, &path_str);

app_inv.push(norm_app_inv);

if !diag_ae_mean.is_nan(){
diag_errs.push(diag_ae_mean);
macro_rules! impl_run_test{
($scalar1:ty, $scalar2:ty, $fn_name:ident) => {
paste::item! {
pub fn run_test(geometry: &str, kernel: &str, npoints: &[usize]){
let universe: mpi::environment::Universe = mpi::initialize().unwrap();
let comm: SimpleCommunicator = universe.world();
for &n in npoints{
let id_tols = [1e-3, 5e-3, 1e-3, 1e-4, 1e-6, 1e-8];
let mut geometry_fn: fn(usize, &SimpleCommunicator) -> Vec<bempp_octree::Point> = sphere_surface;
let kernel_fn = [< $fn_name $scalar1>];
if geometry == "cube"{
geometry_fn = cube_surface;
}
impl_test_geometry!($scalar2);
test_rsrs_geometry(geometry, kernel, geometry_fn, kernel_fn, n, &id_tols, &comm);
}
}
}
skel_errs.push(skel_ae);
tot_num_samples.push(rsrs_algo.y_data.num_samples as f64);
}

let mut app_id_path = path_str.clone();
let mut diag_errs_path = path_str.clone();
let mut skel_errs_path = path_str.clone();
let mut num_samples_path = path_str.clone();

app_id_path.push_str("/app_id.json");
diag_errs_path.push_str("/diag_errs.json");
skel_errs_path.push_str("/skel_errs.json");
num_samples_path.push_str("/num_samples.json");

let _= write_vec_to_new_file(&app_id_path, &app_inv);
let _= write_vec_to_new_file(&diag_errs_path, &diag_errs);
let _= write_vec_to_new_file(&skel_errs_path, &skel_errs);
let _= write_vec_to_new_file(&num_samples_path, &tot_num_samples);
}


}
pub fn main() {

let geometry = "sphere";
let npoints = 3000;
let kernel = "laplace";
let npoints = [500, 1000, 3000, 5000, 10000, 20000];

test_rsrs_geometry(geometry, npoints);
if kernel == "standard"{
impl_run_test!(f64, f64, get_exp_real_);
run_test(geometry, kernel, &npoints);
}
else{
impl_run_test!(f64, f64, get_laplace_);
run_test(geometry, kernel, &npoints);
}

}
Loading

0 comments on commit 4edcdb8

Please sign in to comment.