Skip to content

Fix #228: Add MinimumCutIntoBoundedSets model#637

Open
zazabap wants to merge 3 commits intomainfrom
issue-228-minimum-cut-into-bounded-sets
Open

Fix #228: Add MinimumCutIntoBoundedSets model#637
zazabap wants to merge 3 commits intomainfrom
issue-228-minimum-cut-into-bounded-sets

Conversation

@zazabap
Copy link
Collaborator

@zazabap zazabap commented Mar 13, 2026

Summary

Add the MinimumCutIntoBoundedSets satisfaction problem (Garey & Johnson ND17). This is a graph partitioning problem that combines minimum s-t cut with balance constraints on partition sizes.

Fixes #228

@codecov
Copy link

codecov bot commented Mar 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.79%. Comparing base (88254a0) to head (da6ea40).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #637      +/-   ##
==========================================
+ Coverage   96.77%   96.79%   +0.01%     
==========================================
  Files         226      228       +2     
  Lines       29919    30136     +217     
==========================================
+ Hits        28955    29171     +216     
- Misses        964      965       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

zazabap and others added 2 commits March 13, 2026 12:24
@zazabap
Copy link
Collaborator Author

zazabap commented Mar 13, 2026

Implementation Summary

Changes

  • src/models/graph/minimum_cut_into_bounded_sets.rs — New model implementing the MinimumCutIntoBoundedSets satisfaction problem (G&J ND17). Parameterized by graph type G and weight type W. Evaluates partition feasibility: source/sink placement, size bounds, and cut weight bound.
  • src/unit_tests/models/graph/minimum_cut_into_bounded_sets.rs — 16 unit tests covering creation, evaluation (YES/NO instances), constraint violations (wrong source/sink, size bound exceeded, wrong config length), serialization round-trip, brute-force solver (satisfying and unsatisfying instances), accessors, and variant metadata.
  • src/models/graph/mod.rs — Module registration and re-export.
  • src/models/mod.rs — Re-export added.
  • src/lib.rs — Added to prelude.
  • problemreductions-cli/src/dispatch.rs — CLI load/serialize dispatch for MinimumCutIntoBoundedSets<SimpleGraph, i32>.
  • problemreductions-cli/src/problem_name.rs — Lowercase alias mapping.
  • problemreductions-cli/src/cli.rs — Added --source, --sink, --size-bound, --cut-bound flags and help table entry.
  • problemreductions-cli/src/commands/create.rs — Creation handler (manual + random) and example_for entry.
  • docs/paper/reductions.typ — Problem definition with formal statement, background, and example.

Deviations from Plan

  • The cut_bound field uses W::Sum type (matching how cut weights are computed) rather than raw W, which is more type-correct.

Open Questions

  • None.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the MinimumCutIntoBoundedSets satisfaction/decision model (Garey & Johnson ND17) to the library, with accompanying unit tests, CLI support for create/load/serialize, and paper documentation updates.

Changes:

  • Introduces MinimumCutIntoBoundedSets<G, W> model with schema registration, variants, and evaluation logic.
  • Adds comprehensive unit tests covering evaluation, serialization, and BruteForce solver behavior.
  • Extends CLI to resolve the new problem name/alias, create instances (including random generation), and load/serialize JSON for the new type; updates paper docs to include the new problem.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/models/graph/minimum_cut_into_bounded_sets.rs New model implementation + schema entry + variant declaration + tests module hookup
src/unit_tests/models/graph/minimum_cut_into_bounded_sets.rs New unit tests for the model (evaluation/serialization/solver/variant)
src/models/graph/mod.rs Registers the new graph model module and re-export
src/models/mod.rs Re-exports the new model at the models root
src/lib.rs Adds the model to the public prelude
problemreductions-cli/src/problem_name.rs Adds alias resolution for minimumcutintoboundedsets
problemreductions-cli/src/dispatch.rs Adds JSON load + Any serialization support for the new model
problemreductions-cli/src/commands/create.rs Adds pred create support (explicit and random) for the new model
problemreductions-cli/src/cli.rs Adds new CLI flags for source/sink/size_bound/cut_bound and updates help text
docs/paper/reductions.typ Adds paper definition section + label mapping for the new problem

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +231 to +251
let source = args
.source
.context("--source is required for MinimumCutIntoBoundedSets")?;
let sink = args
.sink
.context("--sink is required for MinimumCutIntoBoundedSets")?;
let size_bound = args
.size_bound
.context("--size-bound is required for MinimumCutIntoBoundedSets")?;
let cut_bound = args
.cut_bound
.context("--cut-bound is required for MinimumCutIntoBoundedSets")?;
(
ser(MinimumCutIntoBoundedSets::new(
graph,
edge_weights,
source,
sink,
size_bound,
cut_bound,
))?,
"MinimumCutIntoBoundedSets" => {
let (graph, _) = parse_graph(args).map_err(|e| {
anyhow::anyhow!(
"{e}\n\nUsage: pred create MinimumCutIntoBoundedSets --graph 0-1,1-2,2-3 --edge-weights 1,1,1 --source 0 --sink 2 --size-bound 2 --cut-bound 1"
Given an undirected graph $G = (V, E)$ with edge weights $w: E -> ZZ^+$, designated vertices $s, t in V$, a positive integer $B <= |V|$, and a positive integer $K$, determine whether there exists a partition of $V$ into disjoint sets $V_1$ and $V_2$ such that $s in V_1$, $t in V_2$, $|V_1| <= B$, $|V_2| <= B$, and
$ sum_({u,v} in E: u in V_1, v in V_2) w({u,v}) <= K. $
][
Minimum Cut Into Bounded Sets (Garey & Johnson ND17) combines the classical minimum $s$-$t$ cut problem with a balance constraint on partition sizes. Without the balance constraint ($B = |V|$), the problem reduces to standard minimum $s$-$t$ cut, solvable in polynomial time via network flow. Adding the requirement $|V_1| <= B$ and $|V_2| <= B$ makes the problem NP-complete; it remains NP-complete even for $B = |V| slash 2$ and unit edge weights (the minimum bisection problem) @garey1976. Applications include VLSI layout, load balancing, and graph bisection.
][
Minimum Cut Into Bounded Sets (Garey & Johnson ND17) combines the classical minimum $s$-$t$ cut problem with a balance constraint on partition sizes. Without the balance constraint ($B = |V|$), the problem reduces to standard minimum $s$-$t$ cut, solvable in polynomial time via network flow. Adding the requirement $|V_1| <= B$ and $|V_2| <= B$ makes the problem NP-complete; it remains NP-complete even for $B = |V| slash 2$ and unit edge weights (the minimum bisection problem) @garey1976. Applications include VLSI layout, load balancing, and graph bisection.

The best known exact algorithm is brute-force enumeration of all $2^n$ vertex partitions in $O(2^n)$ time. For the special case of minimum bisection, Cygan et al. @cygan2014 showed fixed-parameter tractability with respect to the cut size. No polynomial-time finite approximation factor exists for balanced graph partition unless $P = N P$ (Andreev and Racke, 2006). Arora, Rao, and Vazirani @arora2009 gave an $O(sqrt(log n))$-approximation for balanced separator.
Comment on lines +171 to +183
fn evaluate(&self, config: &[usize]) -> bool {
let n = self.graph.num_vertices();
if config.len() != n {
return false;
}

// Check source is in V1 (config=0) and sink is in V2 (config=1)
if config[self.source] != 0 {
return false;
}
if config[self.sink] != 1 {
return false;
}
Comment on lines +185 to +190
// Check size bounds
let count_v1 = config.iter().filter(|&&x| x == 0).count();
let count_v2 = config.iter().filter(|&&x| x == 1).count();
if count_v1 > self.size_bound || count_v2 > self.size_bound {
return false;
}
Comment on lines +14 to +28
inventory::submit! {
ProblemSchemaEntry {
name: "MinimumCutIntoBoundedSets",
module_path: module_path!(),
description: "Partition vertices into two bounded-size sets with cut weight at most K",
fields: &[
FieldInfo { name: "graph", type_name: "G", description: "The undirected graph G = (V, E)" },
FieldInfo { name: "edge_weights", type_name: "Vec<W>", description: "Edge weights w: E -> Z+" },
FieldInfo { name: "source", type_name: "usize", description: "Source vertex s (must be in V1)" },
FieldInfo { name: "sink", type_name: "usize", description: "Sink vertex t (must be in V2)" },
FieldInfo { name: "size_bound", type_name: "usize", description: "Maximum size B for each partition set" },
FieldInfo { name: "cut_bound", type_name: "W::Sum", description: "Maximum total cut weight K" },
],
}
}
Comment on lines +1046 to +1054
let graph = util::create_random_graph(num_vertices, edge_prob, args.seed);
let num_edges = graph.num_edges();
let edge_weights = vec![1i32; num_edges];
let source = 0;
let sink = if num_vertices > 1 {
num_vertices - 1
} else {
0
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Model] MinimumCutIntoBoundedSets

2 participants