Skip to content

Commit 423506c

Browse files
zazabapclaude
andauthored
feat: add 10 reduction rules (#770) (#972)
* feat: add 10 reduction rules from #770 Add 10 new reduction rules connecting existing problem models: Tier 1a (simple): - Partition → BinPacking (#396): capacity=S/2, identity extraction - ExactCoverBy3Sets → MaximumSetPacking (#823): identity transformation - NAESatisfiability → MaxCut (#821): literal-pair edges + variable edges ThreePartition scheduling (5 rules): - ThreePartition → ResourceConstrainedScheduling (#477): 3 processors, resource=size - ThreePartition → SequencingWithReleaseTimesAndDeadlines (#469): filler-task slots - ThreePartition → SequencingToMinimizeWeightedTardiness (#473): filler-task slots - ThreePartition → FlowShopScheduling (#482): 3-machine separators - ThreePartition → JobShopScheduling (#485): 2-processor separators ILP/graph: - ILP/i32 → ILP/bool (#769): FBBT + truncated binary encoding - MaxCut → MinimumCutIntoBoundedSets (#849): complement graph bisection Also adds num_literal_pairs() getter to NAESatisfiability model and updates dominated-rules allow-list and path parity tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: register 9 missing rules in mod.rs and address review findings - Register all 9 unregistered rule modules in rules/mod.rs (were dead code) - Wire 9 canonical rule example specs in canonical_rule_example_specs() - Add ExactCoverBy3Sets -> ILP to dominated-rules allow-list - Rename misleading test_..._infeasible to test_..._two_triples - Fix unused variable warning in JobShopScheduling example builder Test count: 4258 -> 4312 (54 previously-dead tests now compiled and passing) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use MinimizeStepsThenOverhead in MaxCut->QUBO parity test The new reduction rules (especially MaxCut -> MinCutBounded) created an alternative 3-step path to QUBO via complete graph construction, producing O(n^4) QUBO variables. MinimizeSteps tied at 3 steps and the tie-breaker selected this expensive path. Using MinimizeStepsThenOverhead with actual problem size (Petersen: 10v, 15e) ensures the compact SpinGlass path is chosen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 183b19a commit 423506c

24 files changed

+3099
-4
lines changed

src/models/formula/nae_satisfiability.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ impl NAESatisfiability {
6969
self.clauses.iter().map(|c| c.len()).sum()
7070
}
7171

72+
/// Get the total number of literal pairs across all clauses.
73+
///
74+
/// For each clause with k literals, this contributes C(k,2) = k*(k-1)/2 pairs.
75+
pub fn num_literal_pairs(&self) -> usize {
76+
self.clauses
77+
.iter()
78+
.map(|c| c.len() * (c.len() - 1) / 2)
79+
.sum()
80+
}
81+
7282
/// Get the clauses.
7383
pub fn clauses(&self) -> &[CNFClause] {
7484
&self.clauses
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//! Reduction from ExactCoverBy3Sets to MaximumSetPacking.
2+
//!
3+
//! Given an X3C instance with universe X (|X| = 3q) and collection C of
4+
//! 3-element subsets, construct a MaximumSetPacking<One> instance where each
5+
//! triple becomes a variable-length set with unit weight. An exact cover
6+
//! of q disjoint triples corresponds to a maximum packing of value q.
7+
8+
use crate::models::set::{ExactCoverBy3Sets, MaximumSetPacking};
9+
use crate::reduction;
10+
use crate::rules::traits::{ReduceTo, ReductionResult};
11+
use crate::types::One;
12+
13+
/// Result of reducing ExactCoverBy3Sets to MaximumSetPacking<One>.
14+
#[derive(Debug, Clone)]
15+
pub struct ReductionXC3SToMaximumSetPacking {
16+
target: MaximumSetPacking<One>,
17+
}
18+
19+
impl ReductionResult for ReductionXC3SToMaximumSetPacking {
20+
type Source = ExactCoverBy3Sets;
21+
type Target = MaximumSetPacking<One>;
22+
23+
fn target_problem(&self) -> &MaximumSetPacking<One> {
24+
&self.target
25+
}
26+
27+
/// Extract X3C solution from MaximumSetPacking solution.
28+
///
29+
/// The configuration is identity (same binary selection vector).
30+
/// A packing of q disjoint 3-sets over a 3q-element universe is necessarily
31+
/// an exact cover, so no additional checking is needed.
32+
fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
33+
target_solution.to_vec()
34+
}
35+
}
36+
37+
#[reduction(overhead = {
38+
num_sets = "num_subsets",
39+
})]
40+
impl ReduceTo<MaximumSetPacking<One>> for ExactCoverBy3Sets {
41+
type Result = ReductionXC3SToMaximumSetPacking;
42+
43+
fn reduce_to(&self) -> Self::Result {
44+
let sets: Vec<Vec<usize>> = self
45+
.subsets()
46+
.iter()
47+
.map(|triple| triple.to_vec())
48+
.collect();
49+
50+
ReductionXC3SToMaximumSetPacking {
51+
target: MaximumSetPacking::<One>::new(sets),
52+
}
53+
}
54+
}
55+
56+
#[cfg(feature = "example-db")]
57+
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
58+
use crate::export::SolutionPair;
59+
60+
vec![crate::example_db::specs::RuleExampleSpec {
61+
id: "exactcoverby3sets_to_maximumsetpacking",
62+
build: || {
63+
// Universe {0,1,2,3,4,5}, subsets [{0,1,2}, {0,1,3}, {3,4,5}, {2,4,5}, {1,3,5}]
64+
// Exact cover: S0={0,1,2} + S2={3,4,5}
65+
let source = ExactCoverBy3Sets::new(
66+
6,
67+
vec![[0, 1, 2], [0, 1, 3], [3, 4, 5], [2, 4, 5], [1, 3, 5]],
68+
);
69+
crate::example_db::specs::rule_example_with_witness::<_, MaximumSetPacking<One>>(
70+
source,
71+
SolutionPair {
72+
source_config: vec![1, 0, 1, 0, 0],
73+
target_config: vec![1, 0, 1, 0, 0],
74+
},
75+
)
76+
},
77+
}]
78+
}
79+
80+
#[cfg(test)]
81+
#[path = "../unit_tests/rules/exactcoverby3sets_maximumsetpacking.rs"]
82+
mod tests;

0 commit comments

Comments
 (0)