problemreductions/models/graph/
biclique_cover.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
7use crate::topology::BipartiteGraph;
8use crate::traits::{OptimizationProblem, Problem};
9use crate::types::{Direction, SolutionSize};
10use serde::{Deserialize, Serialize};
11use std::collections::HashSet;
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "BicliqueCover",
16 module_path: module_path!(),
17 description: "Cover bipartite edges with k bicliques",
18 fields: &[
19 FieldInfo { name: "left_size", type_name: "usize", description: "Vertices in left partition" },
20 FieldInfo { name: "right_size", type_name: "usize", description: "Vertices in right partition" },
21 FieldInfo { name: "edges", type_name: "Vec<(usize, usize)>", description: "Bipartite edges" },
22 FieldInfo { name: "k", type_name: "usize", description: "Number of bicliques" },
23 ],
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct BicliqueCover {
54 graph: BipartiteGraph,
56 k: usize,
58}
59
60impl BicliqueCover {
61 pub fn new(graph: BipartiteGraph, k: usize) -> Self {
67 Self { graph, k }
68 }
69
70 pub fn from_matrix(matrix: &[Vec<u8>], k: usize) -> Self {
74 let left_size = matrix.len();
75 let right_size = if left_size > 0 { matrix[0].len() } else { 0 };
76
77 let mut edges = Vec::new();
78 for (i, row) in matrix.iter().enumerate() {
79 for (j, &val) in row.iter().enumerate() {
80 if val != 0 {
81 edges.push((i, j));
82 }
83 }
84 }
85
86 Self {
87 graph: BipartiteGraph::new(left_size, right_size, edges),
88 k,
89 }
90 }
91
92 pub fn graph(&self) -> &BipartiteGraph {
94 &self.graph
95 }
96
97 pub fn left_size(&self) -> usize {
99 self.graph.left_size()
100 }
101
102 pub fn right_size(&self) -> usize {
104 self.graph.right_size()
105 }
106
107 pub fn num_vertices(&self) -> usize {
109 self.graph.left_size() + self.graph.right_size()
110 }
111
112 pub fn num_edges(&self) -> usize {
114 self.graph.left_edges().len()
115 }
116
117 pub fn k(&self) -> usize {
119 self.k
120 }
121
122 pub fn rank(&self) -> usize {
124 self.k()
125 }
126
127 fn get_biclique_memberships(
133 &self,
134 config: &[usize],
135 ) -> (Vec<HashSet<usize>>, Vec<HashSet<usize>>) {
136 let n = self.num_vertices();
137 let left_size = self.graph.left_size();
138 let mut left_bicliques: Vec<HashSet<usize>> = vec![HashSet::new(); self.k];
139 let mut right_bicliques: Vec<HashSet<usize>> = vec![HashSet::new(); self.k];
140
141 for v in 0..n {
142 for b in 0..self.k {
143 let idx = v * self.k + b;
144 if config.get(idx).copied().unwrap_or(0) == 1 {
145 if v < left_size {
146 left_bicliques[b].insert(v);
147 } else {
148 right_bicliques[b].insert(v);
149 }
150 }
151 }
152 }
153
154 (left_bicliques, right_bicliques)
155 }
156
157 fn is_edge_covered(&self, left: usize, right: usize, config: &[usize]) -> bool {
161 let (left_bicliques, right_bicliques) = self.get_biclique_memberships(config);
162
163 for b in 0..self.k {
165 if left_bicliques[b].contains(&left) && right_bicliques[b].contains(&right) {
166 return true;
167 }
168 }
169 false
170 }
171
172 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
174 self.is_valid_cover(config)
175 }
176
177 pub fn is_valid_cover(&self, config: &[usize]) -> bool {
179 use crate::topology::Graph;
180 self.graph
181 .edges()
182 .iter()
183 .all(|&(l, r)| self.is_edge_covered(l, r, config))
184 }
185
186 pub fn count_covered_edges(&self, config: &[usize]) -> usize {
188 use crate::topology::Graph;
189 self.graph
190 .edges()
191 .iter()
192 .filter(|&&(l, r)| self.is_edge_covered(l, r, config))
193 .count()
194 }
195
196 pub fn total_biclique_size(&self, config: &[usize]) -> usize {
198 config.iter().filter(|&&x| x == 1).count()
199 }
200}
201
202#[cfg(test)]
204pub(crate) fn is_biclique_cover(
205 edges: &[(usize, usize)],
206 left_bicliques: &[HashSet<usize>],
207 right_bicliques: &[HashSet<usize>],
208) -> bool {
209 edges.iter().all(|&(l, r)| {
210 left_bicliques
211 .iter()
212 .zip(right_bicliques.iter())
213 .any(|(lb, rb)| lb.contains(&l) && rb.contains(&r))
214 })
215}
216
217impl Problem for BicliqueCover {
218 const NAME: &'static str = "BicliqueCover";
219 type Metric = SolutionSize<i32>;
220
221 fn dims(&self) -> Vec<usize> {
222 vec![2; self.num_vertices() * self.k]
224 }
225
226 fn evaluate(&self, config: &[usize]) -> SolutionSize<i32> {
227 if !self.is_valid_cover(config) {
228 return SolutionSize::Invalid;
229 }
230 SolutionSize::Valid(self.total_biclique_size(config) as i32)
231 }
232
233 fn variant() -> Vec<(&'static str, &'static str)> {
234 crate::variant_params![]
235 }
236}
237
238impl OptimizationProblem for BicliqueCover {
239 type Value = i32;
240
241 fn direction(&self) -> Direction {
242 Direction::Minimize
243 }
244}
245
246crate::declare_variants! {
247 BicliqueCover => "2^num_vertices",
248}
249
250#[cfg(test)]
251#[path = "../../unit_tests/models/graph/biclique_cover.rs"]
252mod tests;