Skip to main content

problemreductions/rules/
kclique_balancedcompletebipartitesubgraph.rs

1//! Reduction from KClique to BalancedCompleteBipartiteSubgraph.
2//!
3//! Classical reduction attributed to Garey and Johnson (GT24) and published in
4//! Johnson (1987). Given a KClique instance (G, k), constructs a bipartite graph
5//! where Part A = padded vertex set and Part B = edge elements + padding elements,
6//! with non-incidence adjacency encoding.
7
8use crate::models::graph::{BalancedCompleteBipartiteSubgraph, KClique};
9use crate::reduction;
10use crate::rules::traits::{ReduceTo, ReductionResult};
11use crate::topology::{BipartiteGraph, Graph, SimpleGraph};
12
13/// Result of reducing KClique to BalancedCompleteBipartiteSubgraph.
14///
15/// Stores the target problem and the number of original vertices for
16/// solution extraction.
17#[derive(Debug, Clone)]
18pub struct ReductionKCliqueToBCBS {
19    target: BalancedCompleteBipartiteSubgraph,
20    /// Number of vertices in the original graph (before padding).
21    num_original_vertices: usize,
22}
23
24impl ReductionResult for ReductionKCliqueToBCBS {
25    type Source = KClique<SimpleGraph>;
26    type Target = BalancedCompleteBipartiteSubgraph;
27
28    fn target_problem(&self) -> &BalancedCompleteBipartiteSubgraph {
29        &self.target
30    }
31
32    /// Extract KClique solution from BalancedCompleteBipartiteSubgraph solution.
33    ///
34    /// The k-clique is S = {v in V : v not in A'}, i.e., the original vertices
35    /// NOT selected on the left side. For each original vertex v (0..n-1),
36    /// the source selection is the negation of the target's left-side selection.
37    fn extract_solution(
38        &self,
39        target_solution: &<Self::Target as crate::traits::Problem>::Solution,
40    ) -> crate::rules::ExtractionResult<<Self::Source as crate::traits::Problem>::Solution> {
41        crate::rules::traits::validate_target_witness(
42            self.target_problem(),
43            target_solution,
44            |value| value.0,
45            "target witness is not satisfying",
46        )?;
47
48        Ok({
49            (0..self.num_original_vertices)
50                .map(|v| !target_solution[v])
51                .collect()
52        })
53    }
54}
55
56#[crate::aggregate_reduction(identity)]
57impl crate::rules::AggregateReductionResult for ReductionKCliqueToBCBS {}
58
59#[reduction(transform = upper_bound {
60    left_size = "num_vertices + k * (k - 1) / 2",
61    right_size = "num_edges + num_vertices - k",
62    k = "num_vertices + k * (k - 1) / 2 - k",
63    num_vertices = "2 * num_vertices + num_edges + k * (k - 1) / 2 - k",
64})]
65impl ReduceTo<BalancedCompleteBipartiteSubgraph> for KClique<SimpleGraph> {
66    type Result = ReductionKCliqueToBCBS;
67
68    fn reduce_to(&self) -> Result<Self::Result, crate::rules::ReductionError> {
69        let n = self.num_vertices();
70        let k = self.k();
71        // Clique membership depends on distinct non-loop edges, not multiplicity.
72        let edges: Vec<(usize, usize)> = self
73            .graph()
74            .edges()
75            .into_iter()
76            .filter(|&(u, v)| u != v)
77            .map(|(u, v)| (u.min(v), u.max(v)))
78            .collect::<std::collections::BTreeSet<_>>()
79            .into_iter()
80            .collect();
81        let m = edges.len();
82
83        // C(k, 2) = k*(k-1)/2 — number of edges in a k-clique
84        let ck2 = k * (k - 1) / 2;
85
86        // Part A (left partition): n' = n + C(k,2) vertices
87        let left_size = n + ck2;
88
89        // Part B (right partition): m edge elements + (n - k) padding elements
90        let num_padding = n - k;
91        let right_size = m + num_padding;
92
93        // Target biclique parameter: K' = n' - k
94        let target_k = left_size - k;
95
96        // Build bipartite edges using non-incidence encoding
97        let mut bip_edges = Vec::new();
98
99        for v in 0..left_size {
100            // Edge elements: add edge (v, j) if v is NOT an endpoint of edges[j]
101            for (j, &(u, w)) in edges.iter().enumerate() {
102                if v != u && v != w {
103                    // For padded vertices (v >= n), they are never endpoints
104                    // of any original edge, so they always connect.
105                    bip_edges.push((v, j));
106                }
107            }
108
109            // Padding elements: always connected
110            for p in 0..num_padding {
111                bip_edges.push((v, m + p));
112            }
113        }
114
115        let graph = BipartiteGraph::new(left_size, right_size, bip_edges);
116        let target = BalancedCompleteBipartiteSubgraph::new(graph, target_k);
117
118        Ok(ReductionKCliqueToBCBS {
119            target,
120            num_original_vertices: n,
121        })
122    }
123}
124
125#[cfg(feature = "example-db")]
126pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
127    use crate::export::SolutionPair;
128
129    vec![crate::example_db::specs::RuleExampleSpec {
130        id: "kclique_to_balancedcompletebipartitesubgraph",
131        build: || {
132            // 4-vertex graph with edges {0,1}, {0,2}, {1,2}, {2,3}, k=3
133            // Known 3-clique: {0, 1, 2}
134            let source = KClique::new(SimpleGraph::new(4, vec![(0, 1), (0, 2), (1, 2), (2, 3)]), 3);
135            // Source config: vertices {0,1,2} selected = [1,1,1,0]
136            // Target: left_size=7, right_size=5, k'=4
137            // Left side: NOT selecting clique vertices -> select {3,4,5,6}
138            // target_config for left: [0,0,0,1,1,1,1]
139            // Right side: select edge elements for clique edges + padding
140            //   e0={0,1}, e1={0,2}, e2={1,2} are clique edges -> select them
141            //   e3={2,3} is not a clique edge -> don't select
142            //   w0 is padding -> select
143            // target_config for right: [1,1,1,0,1]
144            // Full target config: [0,0,0,1,1,1,1, 1,1,1,0,1]
145            crate::example_db::specs::rule_example_with_witness::<
146                _,
147                BalancedCompleteBipartiteSubgraph,
148            >(
149                source,
150                SolutionPair {
151                    source_config: serde_json::json!(vec![true, true, true, false]),
152                    target_config: serde_json::json!(vec![
153                        false, false, false, true, true, true, true, true, true, true, false, true
154                    ]),
155                },
156            )
157        },
158    }]
159}
160
161#[cfg(test)]
162#[path = "../unit_tests/rules/kclique_balancedcompletebipartitesubgraph.rs"]
163mod tests;