diff --git a/Cargo.lock b/Cargo.lock index 2f78d90..2d402bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,6 +186,7 @@ dependencies = [ name = "k_tree_benchmarks" version = "0.1.0" dependencies = [ + "dimacs_benchmarks", "petgraph", "rand", "rustc-hash", diff --git a/dimacs_benchmarks/src/lib.rs b/dimacs_benchmarks/src/lib.rs new file mode 100644 index 0000000..f7c5cde --- /dev/null +++ b/dimacs_benchmarks/src/lib.rs @@ -0,0 +1,77 @@ +#[derive(Debug)] +pub enum HeuristicTypes { + // MstTree = Minimum spanning tree then fill using tree structure + // FillWhile = Fill while building minimum spanning tree + // Ni = Negative Intersection + MstTreeNi, + FillWhileNi, + // Ld = Least difference + MstTreeLd, + FillWhileLd, + // T = Then + MstTreeNiTLd, + FillWhileNiTLd, + MstTreeLdTNi, + FillWhileLdTNi, +} + +pub enum EdgeWeightTypes { + ReturnI32(fn(&HashSet, &HashSet) -> i32), + ReturnI32Tuple(fn(&HashSet, &HashSet) -> (i32, i32)), +} + +use std::{collections::HashSet, hash::BuildHasher}; + +use petgraph::graph::NodeIndex; +use HeuristicTypes::*; +pub const HEURISTICS_BEING_TESTED: [HeuristicTypes; 8] = [ + MstTreeNi, + FillWhileNi, + MstTreeLd, + FillWhileLd, + MstTreeNiTLd, + FillWhileNiTLd, + MstTreeLdTNi, + FillWhileLdTNi, +]; + +pub fn heuristic_to_edge_weight_heuristic( + heuristic: &HeuristicTypes, +) -> EdgeWeightTypes { + use treewidth_heuristic::*; + use HeuristicTypes::*; + match heuristic { + MstTreeNi => EdgeWeightTypes::ReturnI32(negative_intersection_heuristic), + FillWhileNi => EdgeWeightTypes::ReturnI32(negative_intersection_heuristic), + MstTreeLd => EdgeWeightTypes::ReturnI32(least_difference_heuristic), + FillWhileLd => EdgeWeightTypes::ReturnI32(least_difference_heuristic), + MstTreeLdTNi => { + EdgeWeightTypes::ReturnI32Tuple(least_difference_then_negative_intersection_heuristic) + } + FillWhileLdTNi => { + EdgeWeightTypes::ReturnI32Tuple(least_difference_then_negative_intersection_heuristic) + } + MstTreeNiTLd => { + EdgeWeightTypes::ReturnI32Tuple(negative_intersection_then_least_difference_heuristic) + } + FillWhileNiTLd => { + EdgeWeightTypes::ReturnI32Tuple(negative_intersection_then_least_difference_heuristic) + } + } +} + +pub fn heuristic_to_computation_type( + heuristic: &HeuristicTypes, +) -> treewidth_heuristic::TreewidthComputationMethod { + use treewidth_heuristic::TreewidthComputationMethod::*; + match heuristic { + MstTreeNi => MSTAndUseTreeStructure, + FillWhileNi => FillWhilstMST, + MstTreeLd => MSTAndUseTreeStructure, + FillWhileLd => FillWhilstMST, + MstTreeLdTNi => MSTAndUseTreeStructure, + FillWhileLdTNi => FillWhilstMST, + MstTreeNiTLd => MSTAndUseTreeStructure, + FillWhileNiTLd => FillWhilstMST, + } +} diff --git a/dimacs_benchmarks/src/main.rs b/dimacs_benchmarks/src/main.rs index b966b2c..34ca90a 100644 --- a/dimacs_benchmarks/src/main.rs +++ b/dimacs_benchmarks/src/main.rs @@ -1,15 +1,11 @@ use dimacs_petgraph_parser::read_graph; -use petgraph::graph::NodeIndex; -use std::collections::HashSet; use std::fs::{self, File}; -use std::hash::BuildHasher; use std::io::Write; +use dimacs_benchmarks::*; use petgraph::Graph; use std::time::SystemTime; -use treewidth_heuristic::{ - compute_treewidth_upper_bound_not_connected, TreewidthComputationMethod, -}; +use treewidth_heuristic::compute_treewidth_upper_bound_not_connected; // Debug version #[cfg(debug_assertions)] @@ -19,41 +15,6 @@ type Hasher = std::hash::BuildHasherDefault; #[cfg(not(debug_assertions))] type Hasher = std::hash::RandomState; -#[derive(Debug)] -#[allow(dead_code)] -enum HeuristicTypes { - // MstTree = Minimum spanning tree then fill using tree structure - // FillWhile = Fill while building minimum spanning tree - // Ni = Negative Intersection - MstTreeNi, - FillWhileNi, - // Ld = Least difference - MstTreeLd, - FillWhileLd, - // T = Then - MstTreeNiTLd, - FillWhileNiTLd, - MstTreeLdTNi, - FillWhileLdTNi, -} - -enum EdgeWeightTypes { - ReturnI32(fn(&HashSet, &HashSet) -> i32), - ReturnI32Tuple(fn(&HashSet, &HashSet) -> (i32, i32)), -} - -use HeuristicTypes::*; -const HEURISTICS_BEING_TEST: [HeuristicTypes; 8] = [ - MstTreeNi, - FillWhileNi, - MstTreeLd, - FillWhileLd, - MstTreeNiTLd, - FillWhileNiTLd, - MstTreeLdTNi, - FillWhileLdTNi, -]; - fn main() { env_logger::init(); @@ -80,9 +41,8 @@ fn main() { benchmark_log_file .write_all( format!( - "| {0: <20} | {1: <12} | {2: <12} | {3: <12} | {4: <12} | {5: <12} | {6: <12} | {7: <12} | {8: <12} | {9: <12} | \n", - "Graph name", "Upper bound", "MSTTreeNi", "FillWhileNi", "MSTTreeLd", "FillWhileLd", "MstTreeNiTLd", "FillWhileNiTLd", - "MstTreeLdTNi", "FillWhileLdTNi", + "| {0: <20} | {1: <12} | {2: <10} | {3: <10} | {4: <10} | {5: <10} | {6: <10} | {7: <10} | {8: <10} | {9: <10} | \n", + "Graph name", "Upper bound", "MTrNi", "FiWhNi", "MTrLd", "FillWhLd", "MTrNiTLd", "FiWhNiTLd", "MTrLdTNi", "FiWhLdTNi", ) .as_bytes(), ) @@ -98,7 +58,7 @@ fn main() { println!("Starting calculation on graph: {:?}", graph_file_name); let mut calculation_vec = Vec::new(); - for heuristic in HEURISTICS_BEING_TEST { + for heuristic in HEURISTICS_BEING_TESTED { // Time the calculation let start = SystemTime::now(); let mut treewidth: usize = usize::MAX; @@ -155,10 +115,10 @@ fn main() { } )); - for i in 0..HEURISTICS_BEING_TEST.len() { + for i in 0..HEURISTICS_BEING_TESTED.len() { let current_value_tuple = calculation_vec.get(i).expect("Calculation should exist"); log.push_str(&format!( - "{0: <4} {1: <8} |", + "{0: <4} {1: <7}|", current_value_tuple.0, current_value_tuple.1 )); } @@ -170,42 +130,3 @@ fn main() { .expect("Writing to Dimacs log file should be possible"); } } - -fn heuristic_to_edge_weight_heuristic( - heuristic: &HeuristicTypes, -) -> EdgeWeightTypes { - use treewidth_heuristic::*; - use HeuristicTypes::*; - match heuristic { - MstTreeNi => EdgeWeightTypes::ReturnI32(negative_intersection_heuristic), - FillWhileNi => EdgeWeightTypes::ReturnI32(negative_intersection_heuristic), - MstTreeLd => EdgeWeightTypes::ReturnI32(least_difference_heuristic), - FillWhileLd => EdgeWeightTypes::ReturnI32(least_difference_heuristic), - MstTreeLdTNi => { - EdgeWeightTypes::ReturnI32Tuple(least_difference_then_negative_intersection_heuristic) - } - FillWhileLdTNi => { - EdgeWeightTypes::ReturnI32Tuple(least_difference_then_negative_intersection_heuristic) - } - MstTreeNiTLd => { - EdgeWeightTypes::ReturnI32Tuple(negative_intersection_then_least_difference_heuristic) - } - FillWhileNiTLd => { - EdgeWeightTypes::ReturnI32Tuple(negative_intersection_then_least_difference_heuristic) - } - } -} - -fn heuristic_to_computation_type(heuristic: &HeuristicTypes) -> TreewidthComputationMethod { - use TreewidthComputationMethod::*; - match heuristic { - MstTreeNi => MSTAndUseTreeStructure, - FillWhileNi => FillWhilstMST, - MstTreeLd => MSTAndUseTreeStructure, - FillWhileLd => FillWhilstMST, - MstTreeLdTNi => MSTAndUseTreeStructure, - FillWhileLdTNi => FillWhilstMST, - MstTreeNiTLd => MSTAndUseTreeStructure, - FillWhileNiTLd => FillWhilstMST, - } -} diff --git a/k_tree_benchmarks/Cargo.toml b/k_tree_benchmarks/Cargo.toml index e037cad..16efb0e 100644 --- a/k_tree_benchmarks/Cargo.toml +++ b/k_tree_benchmarks/Cargo.toml @@ -12,4 +12,5 @@ serde = { version = "1.0", features = ["derive"] } petgraph = "0.6.4" treewidth_heuristic = { path = "../treewidth_heuristic", version = "0.1.0" } rand = "0.8.5" -rustc-hash = { git = "https://github.com/rust-lang/rustc-hash"} \ No newline at end of file +rustc-hash = { git = "https://github.com/rust-lang/rustc-hash"} +dimacs_benchmarks = { path = "../dimacs_benchmarks", version = "0.1.0" } \ No newline at end of file diff --git a/k_tree_benchmarks/src/main.rs b/k_tree_benchmarks/src/main.rs index 46d0d02..a49cf99 100644 --- a/k_tree_benchmarks/src/main.rs +++ b/k_tree_benchmarks/src/main.rs @@ -7,7 +7,10 @@ use petgraph::dot::{Config, Dot}; use petgraph::graph::NodeIndex; use petgraph::Graph; use std::time::SystemTime; -use treewidth_heuristic::{compute_treewidth_upper_bound, TreewidthComputationMethod}; +use treewidth_heuristic::compute_treewidth_upper_bound_not_connected; + +// Use imports for benchmarking from dimacs_benchmarks crate +use dimacs_benchmarks::*; // Debug version #[cfg(debug_assertions)] @@ -17,127 +20,146 @@ type Hasher = std::hash::BuildHasherDefault; #[cfg(not(debug_assertions))] type Hasher = std::hash::RandomState; -fn main() { - let k = 10; - let n = 100; - let p = 10; - let edge_heuristic = treewidth_heuristic::least_difference_heuristic::; - let number_of_trees = 100; - let computation_type = TreewidthComputationMethod::FillWhilstMST; - - let mut sum_of_treewidths_predecessors = 0; - let mut sum_of_treewidths_no_predecessors = 0; +/// First coordinate is the n, second k, third p +pub const PARTIAL_K_TREE_CONFIGURATIONS: [(usize, usize, usize); 18] = [ + (100, 10, 30), + (100, 20, 30), + (100, 10, 40), + (100, 20, 40), + (100, 10, 50), + (100, 20, 50), + (200, 10, 30), + (200, 20, 30), + (200, 10, 40), + (200, 20, 40), + (200, 10, 50), + (200, 20, 50), + (500, 10, 30), + (500, 20, 30), + (500, 10, 40), + (500, 20, 40), + (500, 10, 50), + (500, 20, 50), +]; - // Opening and writing to log file +fn main() { + // Opening log file let mut benchmark_log_file = File::create("k_tree_benchmarks/benchmark_results/k_tree_results.txt") .expect("Dimacs log file should be creatable"); - for i in 0..number_of_trees { - let graph: Graph = - treewidth_heuristic::generate_partial_k_tree_with_guaranteed_treewidth( - k, - n, - p, - &mut rand::thread_rng(), - ) - .expect("n should be greater than k"); - - println!("Starting calculation on graph number: {:?}", i); - // Time the calculation - let start = SystemTime::now(); - let ( - clique_graph, - clique_graph_tree_after_filling_up, - clique_graph_tree_before_filling_up, - _, - _, - computed_treewidth, - ) = compute_treewidth_upper_bound(&graph, edge_heuristic, computation_type, true); - - // DEBUG - let ( - clique_graph_alt, - clique_graph_tree_after_filling_up_alt, - clique_graph_tree_before_filling_up_alt, - _, - _, - computed_treewidth_alt, - ) = compute_treewidth_upper_bound( - &graph, - edge_heuristic, - TreewidthComputationMethod::MSTAndFill, - true, - ); + let number_of_repetitions_per_heuristic = 10; - // DEBUG - // assert!(petgraph::algo::is_isomorphic_matching( - // &clique_graph, - // &clique_graph_alt, - // |a, b| a.eq(b), - // |a, b| a.eq(b) - // )); + for (n, k, p) in PARTIAL_K_TREE_CONFIGURATIONS { + let number_of_trees = 100; - sum_of_treewidths_no_predecessors += computed_treewidth_alt; - sum_of_treewidths_predecessors += computed_treewidth; + println!("Starting calculation on graph: {:?}", (n, k, p)); + let mut calculation_vec = Vec::new(); - benchmark_log_file - .write_all( - format!( - "Graph {:?}: {} {} took {:.3} milliseconds\n", - i, - computed_treewidth, - computed_treewidth_alt, - start - .elapsed() - .expect("Time should be trackable") - .as_millis() + for i in 0..number_of_trees { + let graph: Graph = + treewidth_heuristic::generate_partial_k_tree_with_guaranteed_treewidth( + k, + n, + p, + &mut rand::thread_rng(), ) - .as_bytes(), - ) - .expect("Writing to Dimacs log file should be possible"); + .expect("n should be greater than k"); + + for heuristic_index in 0..HEURISTICS_BEING_TESTED.len() { + // Time the calculation + let start = SystemTime::now(); + let mut treewidth: usize = usize::MAX; + + let heuristic = &HEURISTICS_BEING_TESTED[heuristic_index]; + let edge_weight_heuristic = heuristic_to_edge_weight_heuristic(heuristic); + let computation_type = heuristic_to_computation_type(heuristic); + + for _ in 0..number_of_repetitions_per_heuristic { + let computed_treewidth = match edge_weight_heuristic { + EdgeWeightTypes::ReturnI32(a) => { + compute_treewidth_upper_bound_not_connected::<_, _, Hasher, _>( + &graph, + a, + computation_type, + false, + ) + } + EdgeWeightTypes::ReturnI32Tuple(a) => { + compute_treewidth_upper_bound_not_connected::<_, _, Hasher, _>( + &graph, + a, + computation_type, + false, + ) + } + }; + + if computed_treewidth < treewidth { + treewidth = computed_treewidth; + } + } + + if i == 0 { + calculation_vec.push(( + treewidth, + start + .elapsed() + .expect("Time should be trackable") + .as_millis() + / number_of_repetitions_per_heuristic, + )) + } else { + let (treewidth_sum, time_sum) = calculation_vec + .get(heuristic_index) + .expect("Values for calculation should exist"); + calculation_vec[heuristic_index] = ( + treewidth_sum + treewidth, + time_sum + + start + .elapsed() + .expect("Time should be trackable") + .as_millis() + / number_of_repetitions_per_heuristic, + ); + } + } + } + let calculation_vec: Vec<(f32, f32)> = calculation_vec + .iter() + .map(|(treewidth_sum, time_sum)| { + ( + *treewidth_sum as f32 / number_of_trees as f32, + *time_sum as f32 / number_of_trees as f32, + ) + }) + .collect(); - // Debug - // if i == 0 { - // println!( - // "Predecessor map first graph: \n {:?}", - // predecessor_map.unwrap() - // ); - // println!( - // "Clique graph map first graph: \n {:?}", - // clique_graph_map.unwrap() - // ); - // } - - create_dot_files( - &graph, - &clique_graph, - &clique_graph_tree_after_filling_up, - &clique_graph_tree_before_filling_up, - i, - "", - ); + let mut log = format!(""); + + log.push_str(&format!("n: {} | k: {} | p: {} \n", n, k, p)); + + log.push_str(&format!( + "| {0: <10} | {1: <10} | {2: <10} | {3: <10} | {4: <10} | {5: <10} | {6: <10} | {7: <10} | \n", + "MTrNi", "FiWhNi", "MTrLd", "FillWhLd", "MTrNiTLd", "FiWhNiTLd", "MTrLdTNi", "FiWhLdTNi", + )); - // DEBUG - // create_dot_files( - // &graph, - // &clique_graph_alt, - // &clique_graph_tree_after_filling_up_alt, - // &clique_graph_tree_before_filling_up_alt, - // i, - // "no_predecessors", - // ); + log.push_str("|"); + for i in 0..HEURISTICS_BEING_TESTED.len() { + let current_value_tuple = calculation_vec.get(i).expect("Calculation should exist"); + log.push_str(&format!( + "{: <4} {: <7}|", + format!("{:.1}", current_value_tuple.0), + format!("{:.1}", current_value_tuple.1) + )); + } + + log.push_str("\n \n"); + + benchmark_log_file + .write_all(log.as_bytes()) + .expect("Writing to Dimacs log file should be possible"); } - benchmark_log_file - .write_all( - format!( - "Predecessors averaged: {:.2}; No Predecessors Average: {:.2}", - sum_of_treewidths_predecessors as f32 / number_of_trees as f32, - sum_of_treewidths_no_predecessors as f32 / number_of_trees as f32, - ) - .as_bytes(), - ) - .expect("Writing to Dimacs log file should be possible"); } // Converting dot files to pdf in bulk: