Optimization pass - #9
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR focuses on improving performance of permutation strategies and fixing several correctness/UB issues in the treewidth bounds/heuristics pipeline (notably around graph/strategy state synchronization, virtual overrides, and isolated-node handling).
Changes:
- Replaces several
std::unordered_*graph/queue containers withboost::unordered_flat_*and removes extra per-neighbour set copying in hot paths. - Fixes multiple correctness problems:
Graph::get_neighboursisolated-node UB,Delta2Dqueue/graph desync (addsPermutationStrategy::clear()), andMCSPermutationStrategy::recomputenot actually overriding the base. - Expands/updates tests to cover previously excluded paths (Delta2D, LBN/LBN+) and documents the MCS override fix.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/test_upper.cpp | Updates golden-test commentary to reflect the fixed MCS override behavior. |
| tests/test_serialization.cpp | Switches test set type to boost::unordered_flat_set to match updated APIs. |
| tests/test_lower.cpp | Adds coverage for Delta2D and LBN/LBN+ now that key correctness fixes are in place. |
| tests/test_graph.cpp | Switches to boost::unordered_flat_set and adds a regression test for isolated-node get_neighbours. |
| include/treewidth/TreeDecomposition.h | Uses flat containers and reduces copying (emplace_back, avoids bag copy in output). |
| include/treewidth/PermutationStrategy.h | Introduces clear() and updates recompute signature/container types. |
| include/treewidth/MetaLowerBoundHeuristic.h | Adds missing virtual destructor. |
| include/treewidth/MCSPermutationStrategy.h | Fixes override signature and adds override. |
| include/treewidth/LowerBoundMMD.h | Updates to LowerBound graph-pointer model and flat set types. |
| include/treewidth/LowerBound.h | Makes LowerBound::graph rebindable via pointer; adds virtual destructor; updates MMD+ implementation. |
| include/treewidth/LBN.h | Attempts to avoid destructive-base side effects by estimating on disposable graph copies. |
| include/treewidth/Graph.h | Migrates to flat containers; optimizes neighbour-improvement; fixes isolated-node get_neighbours; makes contract_edge safe under mutation. |
| include/treewidth/FillInPermutationStrategy.h | Updates recompute signature and eliminates unnecessary set copies. |
| include/treewidth/Delta2D.h | Clears shared strategy queue between per-node runs to prevent desync; updates to graph-pointer model. |
| include/treewidth/CE.h | Moves contraction to occur once after selection loop (but introduces an edge case regression; see comments). |
| include/treewidth/Bag.h | Updates constructor to accept boost::unordered_flat_set. |
| CMakeLists.txt | Adds -Woverloaded-virtual to help catch override/signature bugs. |
Comments suppressed due to low confidence (1)
include/treewidth/CE.h:37
- con_edge() now calls graph.contract_edge(min_v, node) unconditionally after the neighbour-selection loop. If the chosen node has no neighbours (neigh is empty), min_v stays at its default 0 and the function will attempt to contract a non-existent edge (behavior changed vs. previous code where no contraction happened when neigh was empty). Add an early return when neigh is empty to preserve the old behavior and avoid contracting an invalid edge.
boost::unordered_flat_set<unsigned long> neigh = graph.get_neighbours(node);
//getting the neighbour with least overlap
unsigned long min_v = 0;
unsigned long min_ovl = neigh.size();
for(auto v:neigh){
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+39
to
44
| //estimate on a disposable copy: the base bound is destructive, and | ||
| //graph_temp (the neighbour-improved graph) must survive to the next | ||
| //iteration (cf. LBNPlus, which likewise copies into graph_temp1) | ||
| Graph graph_temp_eval = graph_temp; | ||
| lower_bound.setGraph(graph_temp_eval); | ||
| unsigned long lower_temp = lower_bound.estimate(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Performance
min-fill-in,degree+fill-in): eliminateper-neighbour
unordered_setcopies and short-circuitneighbour_improved.std::unordered_*toboost::unordered_flat_*.Correctness fixes
Graph::get_neighboursisolated-node UB — returned a dereferencedend()for a present-but-isolated node; now returns a shared empty set.Delta2Dstrategy/graph desync — read a removed node's neighbours(assertion in debug, SIGSEGV under
NDEBUG); fixed by clearing thereused strategy queue before re-init.
MCSPermutationStrategy::recomputedidn't override the base (tookits set by value → MCS was dead code); signature matched +
override,with
-Woverloaded-virtualadded to catch the class of bug.CE::con_edgecontracted inside its selection loop instead of onceafter it (reached via LBN+).
LowerBound/MetaLowerBoundHeuristic— deleted through base
unique_ptrs inmain.cpp(UB;new-delete-type-mismatchunder ASan).