From 1e17e9dfbdf6a03c9352155acd2fb05ead8cb3c4 Mon Sep 17 00:00:00 2001 From: GiggleLiu Date: Sat, 18 Apr 2026 07:57:07 +0800 Subject: [PATCH] Add MaximumClique variant with weight cast and reductions Register the One (unit weight) variant for MaximumClique so reduction paths from MIS/SimpleGraph/One no longer require unnecessary weight promotion to i32 before reaching MaxClique. Consolidate complement_edges into graph_helpers to eliminate 3 duplicate copies (DRY). Co-Authored-By: Claude Opus 4.6 (1M context) --- src/models/graph/maximum_clique.rs | 7 +-- src/rules/graph_helpers.rs | 16 ++++++- src/rules/kcoloring_partitionintocliques.rs | 13 +----- src/rules/maximumclique_casts.rs | 18 ++++++++ .../maximumclique_maximumindependentset.rs | 44 +++++++++++-------- .../maximumindependentset_maximumclique.rs | 43 +++++++++++------- src/rules/mod.rs | 1 + 7 files changed, 91 insertions(+), 51 deletions(-) create mode 100644 src/rules/maximumclique_casts.rs diff --git a/src/models/graph/maximum_clique.rs b/src/models/graph/maximum_clique.rs index 6dd4cb284..849bef38a 100644 --- a/src/models/graph/maximum_clique.rs +++ b/src/models/graph/maximum_clique.rs @@ -6,7 +6,7 @@ use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension}; use crate::topology::{Graph, SimpleGraph}; use crate::traits::Problem; -use crate::types::{Max, WeightElement}; +use crate::types::{Max, One, WeightElement}; use num_traits::Zero; use serde::{Deserialize, Serialize}; @@ -17,7 +17,7 @@ inventory::submit! { aliases: &[], dimensions: &[ VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]), - VariantDimension::new("weight", "i32", &["i32"]), + VariantDimension::new("weight", "One", &["One", "i32"]), ], module_path: module_path!(), description: "Find maximum weight clique in a graph", @@ -165,7 +165,8 @@ fn is_clique_config(graph: &G, config: &[usize]) -> bool { } crate::declare_variants! { - default MaximumClique => "1.1996^num_vertices", + MaximumClique => "1.1996^num_vertices", + default MaximumClique => "1.1996^num_vertices", } #[cfg(feature = "example-db")] diff --git a/src/rules/graph_helpers.rs b/src/rules/graph_helpers.rs index 58c54a34e..bdc02ae88 100644 --- a/src/rules/graph_helpers.rs +++ b/src/rules/graph_helpers.rs @@ -1,6 +1,6 @@ //! Shared helpers for graph-based reductions. -use crate::topology::Graph; +use crate::topology::{Graph, SimpleGraph}; /// Extract a Hamiltonian cycle vertex ordering from edge-selection configs on complete graphs. /// @@ -57,3 +57,17 @@ pub(crate) fn edges_to_cycle_order(graph: &G, target_solution: &[usize order } + +/// Build the complement graph edges: edges between all non-adjacent vertex pairs. +pub(crate) fn complement_edges(graph: &SimpleGraph) -> Vec<(usize, usize)> { + let n = graph.num_vertices(); + let mut edges = Vec::new(); + for u in 0..n { + for v in (u + 1)..n { + if !graph.has_edge(u, v) { + edges.push((u, v)); + } + } + } + edges +} diff --git a/src/rules/kcoloring_partitionintocliques.rs b/src/rules/kcoloring_partitionintocliques.rs index 156f8c781..34f8b11e7 100644 --- a/src/rules/kcoloring_partitionintocliques.rs +++ b/src/rules/kcoloring_partitionintocliques.rs @@ -29,18 +29,7 @@ impl ReductionResult for ReductionKColoringToPartitionIntoCliques { } } -fn complement_edges(graph: &SimpleGraph) -> Vec<(usize, usize)> { - let n = graph.num_vertices(); - let mut edges = Vec::new(); - for u in 0..n { - for v in (u + 1)..n { - if !graph.has_edge(u, v) { - edges.push((u, v)); - } - } - } - edges -} +use super::graph_helpers::complement_edges; #[reduction( overhead = { diff --git a/src/rules/maximumclique_casts.rs b/src/rules/maximumclique_casts.rs new file mode 100644 index 000000000..eeac7438e --- /dev/null +++ b/src/rules/maximumclique_casts.rs @@ -0,0 +1,18 @@ +//! Variant cast reductions for MaximumClique. +//! +//! Weight-hierarchy cast converting MaximumClique between weight subtypes. + +use crate::impl_variant_reduction; +use crate::models::graph::MaximumClique; +use crate::topology::SimpleGraph; +use crate::types::One; +use crate::variant::CastToParent; + +// Weight-hierarchy cast (One → i32) +impl_variant_reduction!( + MaximumClique, + => , + fields: [num_vertices, num_edges], + |src| MaximumClique::new( + src.graph().clone(), src.weights().iter().map(|w| w.cast_to_parent()).collect()) +); diff --git a/src/rules/maximumclique_maximumindependentset.rs b/src/rules/maximumclique_maximumindependentset.rs index 38c00bcd3..d7c8873a0 100644 --- a/src/rules/maximumclique_maximumindependentset.rs +++ b/src/rules/maximumclique_maximumindependentset.rs @@ -7,7 +7,7 @@ use crate::models::graph::{MaximumClique, MaximumIndependentSet}; use crate::reduction; use crate::rules::traits::{ReduceTo, ReductionResult}; use crate::topology::{Graph, SimpleGraph}; -use crate::types::WeightElement; +use crate::types::{One, WeightElement}; /// Result of reducing MaximumClique to MaximumIndependentSet. #[derive(Debug, Clone)] @@ -33,18 +33,15 @@ where } } -/// Build the complement graph: edges between all non-adjacent vertex pairs. -fn complement_edges(graph: &SimpleGraph) -> Vec<(usize, usize)> { - let n = graph.num_vertices(); - let mut edges = Vec::new(); - for u in 0..n { - for v in (u + 1)..n { - if !graph.has_edge(u, v) { - edges.push((u, v)); - } - } - } - edges +fn reduce_clique_to_is( + src: &MaximumClique, +) -> ReductionCliqueToIS { + let comp_edges = super::graph_helpers::complement_edges(src.graph()); + let target = MaximumIndependentSet::new( + SimpleGraph::new(src.graph().num_vertices(), comp_edges), + src.weights().to_vec(), + ); + ReductionCliqueToIS { target } } #[reduction( @@ -57,12 +54,21 @@ impl ReduceTo> for MaximumClique; fn reduce_to(&self) -> Self::Result { - let comp_edges = complement_edges(self.graph()); - let target = MaximumIndependentSet::new( - SimpleGraph::new(self.graph().num_vertices(), comp_edges), - self.weights().to_vec(), - ); - ReductionCliqueToIS { target } + reduce_clique_to_is(self) + } +} + +#[reduction( + overhead = { + num_vertices = "num_vertices", + num_edges = "num_vertices * (num_vertices - 1) / 2 - num_edges", + } +)] +impl ReduceTo> for MaximumClique { + type Result = ReductionCliqueToIS; + + fn reduce_to(&self) -> Self::Result { + reduce_clique_to_is(self) } } diff --git a/src/rules/maximumindependentset_maximumclique.rs b/src/rules/maximumindependentset_maximumclique.rs index 5701b3c4d..80af9bc9f 100644 --- a/src/rules/maximumindependentset_maximumclique.rs +++ b/src/rules/maximumindependentset_maximumclique.rs @@ -7,7 +7,7 @@ use crate::models::graph::{MaximumClique, MaximumIndependentSet}; use crate::reduction; use crate::rules::traits::{ReduceTo, ReductionResult}; use crate::topology::{Graph, SimpleGraph}; -use crate::types::WeightElement; +use crate::types::{One, WeightElement}; /// Result of reducing MaximumIndependentSet to MaximumClique. #[derive(Debug, Clone)] @@ -33,6 +33,17 @@ where } } +fn reduce_is_to_clique( + src: &MaximumIndependentSet, +) -> ReductionISToClique { + let comp_edges = super::graph_helpers::complement_edges(src.graph()); + let target = MaximumClique::new( + SimpleGraph::new(src.graph().num_vertices(), comp_edges), + src.weights().to_vec(), + ); + ReductionISToClique { target } +} + #[reduction( overhead = { num_vertices = "num_vertices", @@ -43,21 +54,21 @@ impl ReduceTo> for MaximumIndependentSet; fn reduce_to(&self) -> Self::Result { - let n = self.graph().num_vertices(); - // Build complement graph edges - let mut complement_edges = Vec::new(); - for u in 0..n { - for v in (u + 1)..n { - if !self.graph().has_edge(u, v) { - complement_edges.push((u, v)); - } - } - } - let target = MaximumClique::new( - SimpleGraph::new(n, complement_edges), - self.weights().to_vec(), - ); - ReductionISToClique { target } + reduce_is_to_clique(self) + } +} + +#[reduction( + overhead = { + num_vertices = "num_vertices", + num_edges = "num_vertices * (num_vertices - 1) / 2 - num_edges", + } +)] +impl ReduceTo> for MaximumIndependentSet { + type Result = ReductionISToClique; + + fn reduce_to(&self) -> Self::Result { + reduce_is_to_clique(self) } } diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 8c5185851..549e88b40 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -71,6 +71,7 @@ pub(crate) mod ksatisfiability_timetabledesign; pub(crate) mod longestcommonsubsequence_maximumindependentset; pub(crate) mod maxcut_minimumcutintoboundedsets; pub(crate) mod maximum2satisfiability_maxcut; +mod maximumclique_casts; pub(crate) mod maximumclique_maximumindependentset; mod maximumindependentset_casts; mod maximumindependentset_gridgraph;