diff --git a/benches/dapol.rs b/benches/dapol.rs index 96484901..41d2fd3f 100644 --- a/benches/dapol.rs +++ b/benches/dapol.rs @@ -8,7 +8,7 @@ use rand::distributions::{Distribution, Uniform}; use dapol::accumulators::NdmSmt; use dapol::{EntityId, Height, InclusionProof, MaxThreadCount}; -mod heuristic_func_examples; +mod heuristic_func; mod setup; use crate::setup::{NUM_USERS, TREE_HEIGHTS}; @@ -20,7 +20,7 @@ fn bench_build_tree(c: &mut Criterion) { let alloc = stats::allocated::mib().unwrap(); let mut group = c.benchmark_group("dapol"); - group.sample_size(10); + group.sample_size(20); // `SamplingMode::Flat` is used here as that is what Criterion recommends for long-running benches // https://bheisler.github.io/criterion.rs/book/user_guide/advanced_configuration.html#sampling-mode group.sampling_mode(SamplingMode::Flat); @@ -297,6 +297,10 @@ fn bench_test_jemalloc_readings() { println!("Memory usage: {} allocated", setup::bytes_as_string(diff),); } +fn plot_plane() { + heuristic_func::plot(); +} + // ================================================================================================รท criterion_group!( @@ -306,4 +310,4 @@ criterion_group!( bench_verify_proof ); -criterion_main!(benches, bench_test_jemalloc_readings); +criterion_main!(/* benches, bench_test_jemalloc_readings, */ plot_plane); diff --git a/benches/heuristic_func.rs b/benches/heuristic_func.rs new file mode 100644 index 00000000..d7aab0b1 --- /dev/null +++ b/benches/heuristic_func.rs @@ -0,0 +1,92 @@ +extern crate nalgebra as na; + +use std::mem; + +use gnuplot::{ + Figure, + PlotOption::{self}, +}; +use na::{ArrayStorage, Const, Matrix}; + +// Assuming each hash value is 32 bytes (adjust based on your use case) +const HASH_SIZE_BYTES: usize = 32; + +// Heuristic function to estimate memory usage for a Merkle Tree +pub fn estimate_memory_usage(height: u8, num_users: u64) -> usize { + // Calculate the number of hash values in the Merkle Tree + let num_hash_values = 2u32.pow(height as u32); + + // Calculate the total memory usage + let memory_usage_bytes = + num_users as usize * HASH_SIZE_BYTES + num_hash_values as usize * mem::size_of::(); + + memory_usage_bytes +} + +pub fn plot() { + // TODO: replace with actual data (already collected) + // Define points + let points = vec![ + na::Point3::new(0.0, 0.0, 0.0), + na::Point3::new(1.0, 3.0, 5.0), + na::Point3::new(-5.0, 6.0, 3.0), + na::Point3::new(3.0, 6.0, 7.0), + na::Point3::new(-2.0, 6.0, 7.0), + ]; + + // Calculate best-fit plane + let plane = fit_plane(&points); + + // Plot points and plane + plot_3d(&points, plane); +} + +fn fit_plane( + points: &Vec>, +) -> Matrix, Const<1>, ArrayStorage> { + // Convert points to a matrix + let points_matrix = na::DMatrix::from_iterator( + points.len(), + 3, + points.iter().flat_map(|p| p.coords.iter().cloned()), + ); + + // Use SVD to calculate the best-fit plane + let svd = points_matrix.svd(true, true); + + // Extract the normal vector from the right singular vectors + let normal_vector = na::Vector3::new( + svd.v_t.clone().unwrap()[(0, 2)], + svd.v_t.clone().unwrap()[(1, 2)], + svd.v_t.clone().unwrap()[(2, 2)], + ); + + normal_vector.normalize() +} + +fn plot_3d( + points: &Vec>, + plane: Matrix, Const<1>, ArrayStorage>, +) { + let mut fg = Figure::new(); + + let x = points.iter().map(|p| p.x).collect::>(); + let y = points.iter().map(|p| p.y).collect::>(); + let z = points.iter().map(|p| p.z).collect::>(); + + // Plot points + fg.axes3d().points(x, y, z, &[PlotOption::Color("black")]); + + fg.axes3d().surface( + &plane, + points.len(), + 3, + None, + &[PlotOption::Color("blue"), PlotOption::Caption("Plane")], + ); + + // Show the plot + fg.show().unwrap(); + + // fg.save_to_png("benches/3d_plot.png", 640, 480) +} diff --git a/benches/heuristic_func_examples.rs b/benches/heuristic_func_examples.rs deleted file mode 100644 index df1aa3e6..00000000 --- a/benches/heuristic_func_examples.rs +++ /dev/null @@ -1,137 +0,0 @@ -// EXAMPLE 1 (custom ChatGPT heuristic func) -use std::mem; - -// Assuming each hash value is 32 bytes (adjust based on your use case) -const HASH_SIZE_BYTES: usize = 32; - -// Heuristic function to estimate memory usage for a Merkle Tree -pub fn estimate_memory_usage(height: u8, num_users: u64) -> usize { - // Calculate the number of hash values in the Merkle Tree - let num_hash_values = 2u32.pow(height as u32); - - // Calculate the total memory usage - let memory_usage_bytes = - num_users as usize * HASH_SIZE_BYTES + num_hash_values as usize * mem::size_of::(); - - memory_usage_bytes -} - -// EXAMPLE 1 - -// This Python code to Rust: -// https://scikit-spatial.readthedocs.io/en/stable/gallery/fitting/plot_plane.html - -use nalgebra::{Matrix3, Vector3}; -use plotters::prelude::*; - -fn plot() { - // Define points - let points = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 3.0, 5.0), - Vector3::new(-5.0, 6.0, 3.0), - Vector3::new(3.0, 6.0, 7.0), - Vector3::new(-2.0, 6.0, 7.0), - ]; - - // Find the best-fit plane - let plane = best_fit_plane(&points); - - // Plot 3D points and the plane - plot_3d(points, plane); -} - -fn best_fit_plane(points: &Vec>) -> (Vector3, Vector3) { - let centroid = compute_centroid(points); - let centered_points: Vec> = points.iter().map(|p| p - centroid).collect(); - - let covariance_matrix = compute_covariance_matrix(¢ered_points); - let eigenvectors = covariance_matrix.symmetric_eigen().eigenvalues; - let normal = eigenvectors.column(0); - - (centroid, normal.into()) -} - -fn compute_centroid(points: &Vec>) -> Vector3 { - points.iter().fold(Vector3::zeros(), |acc, &p| acc + p) / points.len() as f64 -} - -fn compute_covariance_matrix(points: &Vec>) -> Matrix3 { - let n = points.len() as f64; - let centroid = compute_centroid(points); - - let mut covariance_matrix = Matrix3::zeros(); - - for p in points { - let centered_point = p - centroid; - covariance_matrix += centered_point * centered_point.transpose(); - } - - covariance_matrix /= n; - - covariance_matrix -} - -fn plot_3d(points: Vec>, plane: (Vector3, Vector3)) { - let root = BitMapBackend::new("3d_plot.png", (800, 600)).into_drawing_area(); - root.fill(&WHITE).unwrap(); - let mut chart = ChartBuilder::on(&root) - .caption("3D Plot", ("sans-serif", 20)) - .build_cartesian_3d(-10.0..10.0, -10.0..10.0, -10.0..10.0) - .unwrap(); - - chart.configure_axes().draw().unwrap(); - - // Plot 3D points - chart - .draw_series(points.into_iter().map(|p| { - return Circle::new((p.x as i32, p.y as i32), 5, ShapeStyle::from(&BLACK)); - })) - .unwrap(); - - // Plot the best-fit plane - let normal = plane.1; - let d = -normal.dot(&plane.0); - let plane_points = (0..100) - .flat_map(|i| (0..100).map(move |j| (i as f64 - 50.0, j as f64 - 50.0))) - .map(|(i, j)| { - let k = -(normal.x * i + normal.y * j + d) / normal.z; - (i, j, k) - }); - - chart - .draw_series(SurfaceSeries::new(plane_points, 100, &WHITE.mix(0.5))) - .unwrap(); -} - -// EXAMPLE 2 - -// This Python code to Rust -// https://math.stackexchange.com/a/99317 - -// use nalgebra::{Matrix3, Vector3, U2}; - -fn get_vectors() { - // Generate some random test points - let m = 20; // number of points - let delta = 0.01; // size of random displacement - let origin = Vector3::new(rand::random(), rand::random(), rand::random()); // random origin for the plane - let basis = Matrix3::from_fn(|_, _| rand::random()); // random basis vectors for the plane - let coefficients = Matrix3::from_fn(|_, _| rand::random()); // random coefficients for points on the plane - - // Generate random points on the plane and add random displacement - let points = basis * coefficients + origin.broadcast(m); - - // Now find the best-fitting plane for the test points - - // Subtract out the centroid and take the SVD - let centroid = points.column_mean(); - let centered_points = points - centroid.broadcast(m); - let svd = centered_points.svd(true, true); - - // Extract the left singular vectors - let left = svd.u.unwrap(); - - // Print the left singular vectors - println!("Left singular vectors:\n{}", left); -}