diff --git a/Cargo.lock b/Cargo.lock index 0f63664..4bdbbda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,12 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + [[package]] name = "petgraph" version = "0.6.5" @@ -124,6 +130,7 @@ name = "treewidth-heuristic-using-clique-graphs" version = "0.1.0" dependencies = [ "itertools", + "log", "petgraph", "rand", "rustc-hash", diff --git a/Cargo.toml b/Cargo.toml index b7d58a5..deee918 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,5 @@ repository = "https://github.com/RaoulLuque/treewidth-heuristic-clique-graph" petgraph = "0.6.4" itertools = "0.13" rand = "0.8.5" -rustc-hash = { version ="2.0.0", git = "https://github.com/rust-lang/rustc-hash"} \ No newline at end of file +rustc-hash = { version ="2.0.0", git = "https://github.com/rust-lang/rustc-hash"} +log = "0.4.21" \ No newline at end of file diff --git a/src/compute_treewidth_upper_bound.rs b/src/compute_treewidth_upper_bound.rs index cb8117a..cfd78dc 100644 --- a/src/compute_treewidth_upper_bound.rs +++ b/src/compute_treewidth_upper_bound.rs @@ -43,14 +43,14 @@ pub enum SpanningTreeConstructionMethod { /// a tree decomposition. /// /// See [TreewidthComputationMethod] for the different options of spanning tree construction. -/// +/// /// Also see [edge weight functions][crate::clique_graph_edge_weight_functions] for the different /// weight options for the edges in the clique graph. /// -/// It is possible to not use the clique graph but the clique graph with a bound on the +/// It is possible to not use the clique graph but the clique graph with a bound on the /// size of the cliques instead. The resulting graph is the intersection graph of the set of all /// cliques that are maximal or have a size of clique_bound -/// +/// /// Can also check the tree decomposition for correctness after computation which will up to double /// the running time. If so, will panic if the tree decomposition if incorrect returning the vertices /// and path that is faulty. @@ -79,7 +79,7 @@ pub fn compute_treewidth_upper_bound< // .sorted() .collect() } else { - find_maximum_cliques::, _, S>(graph) + find_maximal_cliques::, _, S>(graph) // .sorted() .collect() }; diff --git a/src/fill_bags_while_generating_mst.rs b/src/fill_bags_while_generating_mst.rs index 3953856..57d8fd9 100644 --- a/src/fill_bags_while_generating_mst.rs +++ b/src/fill_bags_while_generating_mst.rs @@ -3,8 +3,15 @@ use std::{ hash::BuildHasher, }; +use log::trace; use petgraph::{graph::NodeIndex, Graph, Undirected}; +/// The function computes a [tree decomposition][https://en.wikipedia.org/wiki/Tree_decomposition] +/// with the vertices having bags (HashSets) as labels +/// given a clique graph. For this a minimum spanning tree of the clique graph is constructed using +/// prim's algorithm and the edge labels in the clique graph as edge weights. Whenever a new vertex +/// is added to the spanning tree, the bags of the current spanning tree are filled up/updated +/// according to the [tree decomposition criteria][https://en.wikipedia.org/wiki/Tree_decomposition#Definition]. pub fn fill_bags_while_generating_mst( clique_graph: &Graph, O, Undirected>, edge_weight_heuristic: fn(&HashSet, &HashSet) -> O, @@ -44,7 +51,7 @@ pub fn fill_bags_while_generating_mst( } } +/// Computes a tree decomposition similar to [fill_bags_while_generating_mst] except that whenever +/// a vertex is added to the current spanning tree and the bags of the current spanning tree are +/// filled up/updated, edges to other vertices in the entire clique graph are updated (in order to +/// preserve the property that two vertices/bags in the clique graph are adjacent iff they have a +/// non-empty intersection). pub fn fill_bags_while_generating_mst_update_edges< N, E, @@ -315,7 +327,7 @@ fn fill_bags_from_result_graph_updating_edges( } } -/// Adapted from fill_bags +/// Adapted from [fill_bags] fn fill_bags_updating_edges( start_vertex: NodeIndex, end_vertex: NodeIndex, @@ -493,6 +505,10 @@ pub fn fill_bags_while_generating_mst_using_tree. -pub fn find_maximum_cliques( +/// This algorithm is adapted from [networkX find_cliques algorithm][https://networkx.org/documentation/stable/reference/algorithms/generated/networkx.algorithms.clique.find_cliques.html]. +pub fn find_maximal_cliques( graph: G, ) -> impl Iterator where @@ -110,8 +111,8 @@ where }) } -/// Returns an iterator that produces all maximum cliques with maximum size k or cliques that are -/// part of maximum cliques that are themselves bigger in size than k in arbitrary order. +/// Returns an iterator that produces (once each) all cliques that are [maximal][https://en.wikipedia.org/wiki/Clique_(graph_theory)#Definitions] +/// (and of size less than k) or of size k (and not necessarily maximal) in arbitrary order. /// /// Uses the [find_maximum_cliques] method. pub fn find_maximum_cliques_bounded( @@ -126,7 +127,7 @@ where TargetColl: FromIterator, ::NodeId: 'static, { - let mut maximum_cliques = find_maximum_cliques::, G, S>(graph); + let mut maximum_cliques = find_maximal_cliques::, G, S>(graph); let mut combinations = HashSet::<_, S>::default().into_iter().combinations(k); let mut seen_combinations = HashSet::<_, S>::default(); from_fn(move || loop { @@ -160,7 +161,7 @@ mod tests { let test_graph = crate::tests::setup_test_graph(i); let mut cliques: Vec> = - find_maximum_cliques::, _, RandomState>(&test_graph.graph).collect(); + find_maximal_cliques::, _, RandomState>(&test_graph.graph).collect(); for i in 0..cliques.len() { cliques[i].sort(); diff --git a/src/generate_partial_k_tree.rs b/src/generate_partial_k_tree.rs index 0a8c2da..ec8dda3 100644 --- a/src/generate_partial_k_tree.rs +++ b/src/generate_partial_k_tree.rs @@ -4,17 +4,18 @@ use rand::{seq::IteratorRandom, Rng}; use crate::maximum_minimum_degree_plus; -/// Generates a [k-tree](https://en.wikipedia.org/wiki/K-tree) and then randomly removes p percent of the edges -/// to get a [partial k-tree](https://en.wikipedia.org/wiki/Partial_k-tree). To guarantee a treewidth of k, -/// this procedure is repeated until the treewidth of the graph is at least k according to the minimum -/// maximum degree heuristic. +/// Generates a [k-tree](https://en.wikipedia.org/wiki/K-tree) and then randomly removes p percent +/// of the edges to get a [partial k-tree](https://en.wikipedia.org/wiki/Partial_k-tree). To +/// guarantee a treewidth of k, this procedure is repeated until the treewidth of the graph is at +/// least k according to the minimum maximum degree heuristic. /// -/// **Caution!**: Due to the randomness involved, this function could in theory take indefinitely to generate -/// a partial k-tree with the wished treewidth. +/// **Caution!**: Due to the randomness involved, this function could in theory take +/// indefinitely to generate a partial k-tree with the desired treewidth. /// -/// If p > 100 all edges will be removed. The Rng is passed in to increase performance when calling the function multiple times in a row. +/// If p > 100 all edges will be removed. The Rng is passed in to increase performance when +/// calling the function multiple times in a row. /// -/// Returns None if k > n +/// Returns None if k > n. pub fn generate_partial_k_tree_with_guaranteed_treewidth( k: usize, n: usize, @@ -37,10 +38,10 @@ pub fn generate_partial_k_tree_with_guaranteed_treewidth( /// If p > 100 all edges will be removed. The Rng is passed in to increase performance when calling /// the function multiple times in a row. /// -/// Returns None if k > n +/// Returns None if k > n. /// -/// The number of edges in a k_tree are k * (k - 1) / 2 + k * (n - k). So the number of removed edges in a -/// partial_k_tree will be (k * (k - 1) / 2 + k * (n - k)) * p / 100 rounded down +/// The number of edges in a k_tree are k * (k - 1) / 2 + k * (n - k). So the number of removed +/// edges in a partial_k_tree will be (k * (k - 1) / 2 + k * (n - k)) * p / 100 rounded down. pub fn generate_partial_k_tree( k: usize, n: usize, @@ -67,7 +68,7 @@ pub fn generate_partial_k_tree( } /// Generates a [k-tree](https://en.wikipedia.org/wiki/K-tree) with n vertices and k in the definition. -/// Returns None if k > n +/// Returns None if k > n. pub fn generate_k_tree(k: usize, n: usize) -> Option> { if k > n { None diff --git a/src/lib.rs b/src/lib.rs index b5c474b..bcddec5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ pub(crate) use fill_bags_while_generating_mst::{ fill_bags_while_generating_mst_update_edges, fill_bags_while_generating_mst_using_tree, }; pub(crate) use find_connected_components::find_connected_components; -pub(crate) use find_maximum_cliques::{find_maximum_cliques, find_maximum_cliques_bounded}; +pub(crate) use find_maximum_cliques::{find_maximal_cliques, find_maximum_cliques_bounded}; pub(crate) use find_width_of_tree_decomposition::find_width_of_tree_decomposition; pub use generate_partial_k_tree::{ generate_k_tree, generate_partial_k_tree, generate_partial_k_tree_with_guaranteed_treewidth,