Skip to main content

problemreductions/rules/
maxcut_minimummatrixcover.rs

1//! Reduction from MaxCut to MinimumMatrixCover.
2//!
3//! Given a MaxCut instance `(G, w)` with nonnegative integer edge weights,
4//! the target matrix `A` is the symmetric weighted adjacency matrix of `G`:
5//!     `a_{ij} = w({i, j})` if `{i, j} ∈ E`, otherwise `0` (diagonal is `0`).
6//!
7//! The key identity is
8//!     `Σ_{i,j} a_{ij} · f(i) · f(j) = 2 W − 4 · cut(S)`,
9//! where `S = { i : f(i) = +1 }` and `W = Σ_{e ∈ E} w(e)`. Minimizing the
10//! quadratic form is therefore equivalent to maximizing the cut, and the
11//! reduction is witness-preserving via the identity map between MaxCut's
12//! partition encoding (`config[i] = 1 ⇔ i ∈ S`) and MinimumMatrixCover's
13//! sign encoding (`config[i] = 1 ⇔ f(i) = +1 ⇔ i ∈ S`).
14//!
15//! **Precondition:** all edge weights must be nonnegative. The reduction
16//! panics on any negative weight, since `MinimumMatrixCover` requires a
17//! nonnegative integer matrix. Negative-weight `MaxCut` instances are out
18//! of scope and must use a different (preprocessing) reduction.
19//!
20//! Reference: Garey & Johnson, *Computers and Intractability* (1979),
21//! Appendix A1.2, MS13 ("Transformation from MAXIMUM CUT").
22
23use crate::models::algebraic::MinimumMatrixCover;
24use crate::models::graph::MaxCut;
25use crate::reduction;
26use crate::rules::traits::{ReduceTo, ReductionResult};
27use crate::topology::{Graph, SimpleGraph};
28
29/// Result of reducing MaxCut to MinimumMatrixCover.
30#[derive(Debug, Clone)]
31pub struct ReductionMaxCutToMMC {
32    target: MinimumMatrixCover,
33}
34
35impl ReductionResult for ReductionMaxCutToMMC {
36    type Source = MaxCut<SimpleGraph, i64>;
37    type Target = MinimumMatrixCover;
38
39    fn target_problem(&self) -> &Self::Target {
40        &self.target
41    }
42
43    /// Solution extraction is the identity.
44    ///
45    /// Both encodings agree that bit `i = 1` means vertex `i` is in `S`:
46    /// MaxCut treats `config[i] = 1` as one side of the partition, and
47    /// MinimumMatrixCover treats `config[i] = 1` as `f(i) = +1`, i.e.,
48    /// vertex `i` in `S`. The complementary assignment is equally optimal
49    /// because the quadratic form (and the cut) is invariant under
50    /// `f -> -f`.
51    fn extract_solution(
52        &self,
53        target_solution: &<Self::Target as crate::traits::Problem>::Solution,
54    ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
55        crate::rules::traits::validate_target_solution(self.target_problem(), target_solution)?;
56
57        Ok(target_solution.to_vec())
58    }
59}
60
61#[reduction(
62    transform = exact {
63        num_rows = "num_vertices",
64    }
65)]
66impl ReduceTo<MinimumMatrixCover> for MaxCut<SimpleGraph, i64> {
67    type Result = ReductionMaxCutToMMC;
68
69    fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
70        let n = self.graph().num_vertices();
71        let mut matrix: Vec<Vec<i64>> = vec![vec![0i64; n]; n];
72
73        for (u, v, w) in self.edges() {
74            if w < 0 {
75                return Err(crate::rules::ReductionError::invalid_target::<
76                    MaxCut<SimpleGraph, i64>,
77                    MinimumMatrixCover,
78                >(format!(
79                    "edge ({u}, {v}) has negative weight {w}"
80                )));
81            }
82            let w64 = w;
83            matrix[u][v] = w64;
84            matrix[v][u] = w64;
85        }
86
87        Ok(ReductionMaxCutToMMC {
88            target: MinimumMatrixCover::new(matrix),
89        })
90    }
91}
92
93#[cfg(feature = "example-db")]
94pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
95    use crate::export::SolutionPair;
96
97    vec![crate::example_db::specs::RuleExampleSpec {
98        id: "maxcut_to_minimummatrixcover",
99        build: || {
100            // Canonical example: C_4 (4-cycle) with unit weights.
101            // W = 4, max cut = 4 (partition {0,2} vs {1,3} cuts all edges).
102            // The target's minimum quadratic form value is 2W - 4 * cut = 8 - 16 = -8.
103            let source = MaxCut::<SimpleGraph, i64>::new(
104                SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3), (0, 3)]),
105                vec![1, 1, 1, 1],
106            );
107            crate::example_db::specs::rule_example_with_witness::<_, MinimumMatrixCover>(
108                source,
109                SolutionPair {
110                    // S = {0, 2}: vertices 0 and 2 have f = +1.
111                    source_config: serde_json::json!(vec![true, false, true, false]),
112                    target_config: serde_json::json!(vec![true, false, true, false]),
113                },
114            )
115        },
116    }]
117}
118
119#[cfg(test)]
120#[path = "../unit_tests/rules/maxcut_minimummatrixcover.rs"]
121mod tests;