problemreductions/models/specialized/
biclique_cover.rs

1//! Biclique Cover problem implementation.
2//!
3//! The Biclique Cover problem asks for the minimum number of bicliques
4//! (complete bipartite subgraphs) needed to cover all edges of a bipartite graph.
5
6use crate::graph_types::SimpleGraph;
7use crate::traits::Problem;
8use crate::types::{EnergyMode, ProblemSize, SolutionSize};
9use serde::{Deserialize, Serialize};
10use std::collections::HashSet;
11
12/// The Biclique Cover problem.
13///
14/// Given a bipartite graph with vertex sets L and R, find k bicliques
15/// that together cover all edges. Each vertex can be in any subset of the k bicliques.
16///
17/// # Example
18///
19/// ```
20/// use problemreductions::models::specialized::BicliqueCover;
21/// use problemreductions::{Problem, Solver, BruteForce};
22///
23/// // Bipartite graph: L = {0, 1}, R = {2, 3}
24/// // Edges: (0,2), (0,3), (1,2)
25/// let problem = BicliqueCover::new(2, 2, vec![(0, 2), (0, 3), (1, 2)], 2);
26///
27/// let solver = BruteForce::new();
28/// let solutions = solver.find_best(&problem);
29///
30/// // Check coverage
31/// for sol in &solutions {
32///     assert!(problem.is_valid_cover(sol));
33/// }
34/// ```
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct BicliqueCover {
37    /// Number of vertices in the left partition.
38    left_size: usize,
39    /// Number of vertices in the right partition.
40    right_size: usize,
41    /// Edges as (left_vertex, right_vertex) pairs.
42    /// Left vertices are 0..left_size, right are left_size..left_size+right_size.
43    edges: Vec<(usize, usize)>,
44    /// Number of bicliques to use.
45    k: usize,
46}
47
48impl BicliqueCover {
49    /// Create a new Biclique Cover problem.
50    ///
51    /// # Arguments
52    /// * `left_size` - Number of vertices in left partition (0 to left_size-1)
53    /// * `right_size` - Number of vertices in right partition (left_size to left_size+right_size-1)
54    /// * `edges` - Edges as (left, right) pairs
55    /// * `k` - Number of bicliques
56    pub fn new(left_size: usize, right_size: usize, edges: Vec<(usize, usize)>, k: usize) -> Self {
57        // Validate edges are between left and right partitions
58        for &(l, r) in &edges {
59            assert!(
60                l < left_size,
61                "Left vertex {} out of bounds (max {})",
62                l,
63                left_size - 1
64            );
65            assert!(
66                r >= left_size && r < left_size + right_size,
67                "Right vertex {} out of bounds (should be in {}..{})",
68                r,
69                left_size,
70                left_size + right_size
71            );
72        }
73
74        Self {
75            left_size,
76            right_size,
77            edges,
78            k,
79        }
80    }
81
82    /// Create from a bipartite adjacency matrix.
83    ///
84    /// `Matrix[i][j] = 1` means edge between left vertex i and right vertex j.
85    pub fn from_matrix(matrix: &[Vec<u8>], k: usize) -> Self {
86        let left_size = matrix.len();
87        let right_size = if left_size > 0 { matrix[0].len() } else { 0 };
88
89        let mut edges = Vec::new();
90        for (i, row) in matrix.iter().enumerate() {
91            for (j, &val) in row.iter().enumerate() {
92                if val != 0 {
93                    edges.push((i, left_size + j));
94                }
95            }
96        }
97
98        Self {
99            left_size,
100            right_size,
101            edges,
102            k,
103        }
104    }
105
106    /// Get the number of vertices.
107    pub fn num_vertices(&self) -> usize {
108        self.left_size + self.right_size
109    }
110
111    /// Get the number of edges.
112    pub fn num_edges(&self) -> usize {
113        self.edges.len()
114    }
115
116    /// Get k (number of bicliques).
117    pub fn k(&self) -> usize {
118        self.k
119    }
120
121    /// Convert a configuration to biclique memberships.
122    ///
123    /// Config is a flat array where each vertex has k binary variables
124    /// indicating membership in each of the k bicliques.
125    /// Returns: (left_memberships, right_memberships) where each is a Vec of k HashSets.
126    fn get_biclique_memberships(
127        &self,
128        config: &[usize],
129    ) -> (Vec<HashSet<usize>>, Vec<HashSet<usize>>) {
130        let n = self.num_vertices();
131        let mut left_bicliques: Vec<HashSet<usize>> = vec![HashSet::new(); self.k];
132        let mut right_bicliques: Vec<HashSet<usize>> = vec![HashSet::new(); self.k];
133
134        for v in 0..n {
135            for b in 0..self.k {
136                let idx = v * self.k + b;
137                if config.get(idx).copied().unwrap_or(0) == 1 {
138                    if v < self.left_size {
139                        left_bicliques[b].insert(v);
140                    } else {
141                        right_bicliques[b].insert(v);
142                    }
143                }
144            }
145        }
146
147        (left_bicliques, right_bicliques)
148    }
149
150    /// Check if an edge is covered by the bicliques.
151    fn is_edge_covered(&self, left: usize, right: usize, config: &[usize]) -> bool {
152        let (left_bicliques, right_bicliques) = self.get_biclique_memberships(config);
153
154        // Edge is covered if both endpoints are in the same biclique
155        for b in 0..self.k {
156            if left_bicliques[b].contains(&left) && right_bicliques[b].contains(&right) {
157                return true;
158            }
159        }
160        false
161    }
162
163    /// Check if all edges are covered.
164    pub fn is_valid_cover(&self, config: &[usize]) -> bool {
165        self.edges
166            .iter()
167            .all(|&(l, r)| self.is_edge_covered(l, r, config))
168    }
169
170    /// Count covered edges.
171    pub fn count_covered_edges(&self, config: &[usize]) -> usize {
172        self.edges
173            .iter()
174            .filter(|&&(l, r)| self.is_edge_covered(l, r, config))
175            .count()
176    }
177
178    /// Count total biclique size (sum of vertices in all bicliques).
179    pub fn total_biclique_size(&self, config: &[usize]) -> usize {
180        config.iter().filter(|&&x| x == 1).count()
181    }
182}
183
184impl Problem for BicliqueCover {
185    const NAME: &'static str = "BicliqueCover";
186    type GraphType = SimpleGraph;
187    type Weight = i32;
188    type Size = i32;
189
190    fn num_variables(&self) -> usize {
191        // Each vertex has k binary variables (one per biclique)
192        self.num_vertices() * self.k
193    }
194
195    fn num_flavors(&self) -> usize {
196        2 // Binary: in biclique or not
197    }
198
199    fn problem_size(&self) -> ProblemSize {
200        ProblemSize::new(vec![
201            ("left_size", self.left_size),
202            ("right_size", self.right_size),
203            ("num_edges", self.edges.len()),
204            ("k", self.k),
205        ])
206    }
207
208    fn energy_mode(&self) -> EnergyMode {
209        EnergyMode::SmallerSizeIsBetter // Minimize total biclique size
210    }
211
212    fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
213        let is_valid = self.is_valid_cover(config);
214        let size = self.total_biclique_size(config) as i32;
215        SolutionSize::new(size, is_valid)
216    }
217}
218
219/// Check if a biclique configuration covers all edges.
220pub fn is_biclique_cover(
221    edges: &[(usize, usize)],
222    left_bicliques: &[HashSet<usize>],
223    right_bicliques: &[HashSet<usize>],
224) -> bool {
225    edges.iter().all(|&(l, r)| {
226        left_bicliques
227            .iter()
228            .zip(right_bicliques.iter())
229            .any(|(lb, rb)| lb.contains(&l) && rb.contains(&r))
230    })
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236    use crate::solvers::{BruteForce, Solver};
237
238    #[test]
239    fn test_biclique_cover_creation() {
240        let problem = BicliqueCover::new(2, 2, vec![(0, 2), (0, 3), (1, 2)], 2);
241        assert_eq!(problem.num_vertices(), 4);
242        assert_eq!(problem.num_edges(), 3);
243        assert_eq!(problem.k(), 2);
244        assert_eq!(problem.num_variables(), 8); // 4 vertices * 2 bicliques
245    }
246
247    #[test]
248    fn test_from_matrix() {
249        // Matrix:
250        // [[1, 1],
251        //  [1, 0]]
252        // Edges: (0,2), (0,3), (1,2)
253        let matrix = vec![vec![1, 1], vec![1, 0]];
254        let problem = BicliqueCover::from_matrix(&matrix, 2);
255        assert_eq!(problem.num_vertices(), 4);
256        assert_eq!(problem.num_edges(), 3);
257    }
258
259    #[test]
260    fn test_get_biclique_memberships() {
261        let problem = BicliqueCover::new(2, 2, vec![(0, 2)], 1);
262        // Config: vertex 0 in biclique 0, vertex 2 in biclique 0
263        // Variables: [v0_b0, v1_b0, v2_b0, v3_b0]
264        let config = vec![1, 0, 1, 0];
265        let (left, right) = problem.get_biclique_memberships(&config);
266        assert!(left[0].contains(&0));
267        assert!(!left[0].contains(&1));
268        assert!(right[0].contains(&2));
269        assert!(!right[0].contains(&3));
270    }
271
272    #[test]
273    fn test_is_edge_covered() {
274        let problem = BicliqueCover::new(2, 2, vec![(0, 2)], 1);
275        // Put vertex 0 and 2 in biclique 0
276        let config = vec![1, 0, 1, 0];
277        assert!(problem.is_edge_covered(0, 2, &config));
278
279        // Don't put vertex 2 in biclique
280        let config = vec![1, 0, 0, 0];
281        assert!(!problem.is_edge_covered(0, 2, &config));
282    }
283
284    #[test]
285    fn test_is_valid_cover() {
286        let problem = BicliqueCover::new(2, 2, vec![(0, 2), (0, 3)], 1);
287        // Put 0, 2, 3 in biclique 0 -> covers both edges
288        let config = vec![1, 0, 1, 1];
289        assert!(problem.is_valid_cover(&config));
290
291        // Only put 0, 2 -> doesn't cover (0,3)
292        let config = vec![1, 0, 1, 0];
293        assert!(!problem.is_valid_cover(&config));
294    }
295
296    #[test]
297    fn test_solution_size() {
298        let problem = BicliqueCover::new(2, 2, vec![(0, 2)], 1);
299
300        // Valid cover with size 2
301        let sol = problem.solution_size(&[1, 0, 1, 0]);
302        assert!(sol.is_valid);
303        assert_eq!(sol.size, 2);
304
305        // Invalid cover
306        let sol = problem.solution_size(&[1, 0, 0, 0]);
307        assert!(!sol.is_valid);
308        assert_eq!(sol.size, 1);
309    }
310
311    #[test]
312    fn test_brute_force_simple() {
313        // Single edge (0, 2) with k=1
314        let problem = BicliqueCover::new(2, 2, vec![(0, 2)], 1);
315        let solver = BruteForce::new();
316
317        let solutions = solver.find_best(&problem);
318        for sol in &solutions {
319            assert!(problem.is_valid_cover(sol));
320            // Minimum size is 2 (one left, one right vertex)
321            assert_eq!(problem.total_biclique_size(sol), 2);
322        }
323    }
324
325    #[test]
326    fn test_brute_force_two_bicliques() {
327        // Edges that need 2 bicliques to cover efficiently
328        // (0,2), (1,3) - these don't share vertices
329        let problem = BicliqueCover::new(2, 2, vec![(0, 2), (1, 3)], 2);
330        let solver = BruteForce::new();
331
332        let solutions = solver.find_best(&problem);
333        for sol in &solutions {
334            assert!(problem.is_valid_cover(sol));
335        }
336    }
337
338    #[test]
339    fn test_count_covered_edges() {
340        let problem = BicliqueCover::new(2, 2, vec![(0, 2), (0, 3), (1, 2)], 1);
341        // Cover only (0,2): put 0 and 2 in biclique
342        let config = vec![1, 0, 1, 0];
343        assert_eq!(problem.count_covered_edges(&config), 1);
344
345        // Cover (0,2) and (0,3): put 0, 2, 3 in biclique
346        let config = vec![1, 0, 1, 1];
347        assert_eq!(problem.count_covered_edges(&config), 2);
348    }
349
350    #[test]
351    fn test_is_biclique_cover_function() {
352        let edges = vec![(0, 2), (1, 3)];
353        let left = vec![vec![0].into_iter().collect(), vec![1].into_iter().collect()];
354        let right = vec![vec![2].into_iter().collect(), vec![3].into_iter().collect()];
355        assert!(is_biclique_cover(&edges, &left, &right));
356
357        // Missing coverage
358        let left = vec![vec![0].into_iter().collect()];
359        let right = vec![vec![2].into_iter().collect()];
360        assert!(!is_biclique_cover(&edges, &left, &right));
361    }
362
363    #[test]
364    fn test_energy_mode() {
365        let problem = BicliqueCover::new(1, 1, vec![(0, 1)], 1);
366        assert!(problem.energy_mode().is_minimization());
367    }
368
369    #[test]
370    fn test_problem_size() {
371        let problem = BicliqueCover::new(3, 4, vec![(0, 3), (1, 4)], 2);
372        let size = problem.problem_size();
373        assert_eq!(size.get("left_size"), Some(3));
374        assert_eq!(size.get("right_size"), Some(4));
375        assert_eq!(size.get("num_edges"), Some(2));
376        assert_eq!(size.get("k"), Some(2));
377    }
378
379    #[test]
380    fn test_empty_edges() {
381        let problem = BicliqueCover::new(2, 2, vec![], 1);
382        let sol = problem.solution_size(&[0, 0, 0, 0]);
383        assert!(sol.is_valid); // No edges to cover
384        assert_eq!(sol.size, 0);
385    }
386}