problemreductions/models/graph/
biclique_cover.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
17use crate::topology::BipartiteGraph;
18use crate::traits::Problem;
19use crate::types::Min;
20use serde::{Deserialize, Serialize};
21use std::collections::HashSet;
22
23inventory::submit! {
24 ProblemSchemaEntry {
25 name: "BicliqueCover",
26 display_name: "Biclique Cover",
27 aliases: &[],
28 dimensions: &[],
29 module_path: module_path!(),
30 description: "Cover bipartite edges with k bicliques",
31 fields: &[
32 FieldInfo { name: "left_size", type_name: "usize", description: "Vertices in left partition" },
33 FieldInfo { name: "right_size", type_name: "usize", description: "Vertices in right partition" },
34 FieldInfo { name: "edges", type_name: "Vec<(usize, usize)>", description: "Bipartite edges" },
35 FieldInfo { name: "k", type_name: "usize", description: "Number of bicliques" },
36 ],
37 }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct BicliqueCover {
67 graph: BipartiteGraph,
69 k: usize,
71}
72
73impl BicliqueCover {
74 pub fn new(graph: BipartiteGraph, k: usize) -> Self {
80 Self { graph, k }
81 }
82
83 pub fn from_matrix(matrix: &[Vec<u8>], k: usize) -> Self {
87 let left_size = matrix.len();
88 let right_size = if left_size > 0 { matrix[0].len() } else { 0 };
89
90 let mut edges = Vec::new();
91 for (i, row) in matrix.iter().enumerate() {
92 for (j, &val) in row.iter().enumerate() {
93 if val != 0 {
94 edges.push((i, j));
95 }
96 }
97 }
98
99 Self {
100 graph: BipartiteGraph::new(left_size, right_size, edges),
101 k,
102 }
103 }
104
105 pub fn graph(&self) -> &BipartiteGraph {
107 &self.graph
108 }
109
110 pub fn left_size(&self) -> usize {
112 self.graph.left_size()
113 }
114
115 pub fn right_size(&self) -> usize {
117 self.graph.right_size()
118 }
119
120 pub fn num_vertices(&self) -> usize {
122 self.graph.left_size() + self.graph.right_size()
123 }
124
125 pub fn num_edges(&self) -> usize {
127 self.graph.left_edges().len()
128 }
129
130 pub fn k(&self) -> usize {
132 self.k
133 }
134
135 pub fn rank(&self) -> usize {
137 self.k()
138 }
139
140 fn get_biclique_memberships(
146 &self,
147 config: &[usize],
148 ) -> (Vec<HashSet<usize>>, Vec<HashSet<usize>>) {
149 let n = self.num_vertices();
150 let left_size = self.graph.left_size();
151 let mut left_bicliques: Vec<HashSet<usize>> = vec![HashSet::new(); self.k];
152 let mut right_bicliques: Vec<HashSet<usize>> = vec![HashSet::new(); self.k];
153
154 for v in 0..n {
155 for b in 0..self.k {
156 let idx = v * self.k + b;
157 if config.get(idx).copied().unwrap_or(0) == 1 {
158 if v < left_size {
159 left_bicliques[b].insert(v);
160 } else {
161 right_bicliques[b].insert(v);
162 }
163 }
164 }
165 }
166
167 (left_bicliques, right_bicliques)
168 }
169
170 fn is_edge_covered(&self, left: usize, right: usize, config: &[usize]) -> bool {
174 let (left_bicliques, right_bicliques) = self.get_biclique_memberships(config);
175
176 for b in 0..self.k {
178 if left_bicliques[b].contains(&left) && right_bicliques[b].contains(&right) {
179 return true;
180 }
181 }
182 false
183 }
184
185 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
187 self.is_valid_cover(config)
188 }
189
190 pub fn is_valid_cover(&self, config: &[usize]) -> bool {
198 use crate::topology::Graph;
199 let (left_bicliques, right_bicliques) = self.get_biclique_memberships(config);
200 let left_size = self.graph.left_size();
201 for b in 0..self.k {
203 for &l in &left_bicliques[b] {
204 for &r in &right_bicliques[b] {
205 debug_assert!(l < left_size && r >= left_size);
208 if !self.graph.has_edge(l, r) {
209 return false;
210 }
211 }
212 }
213 }
214 self.graph
216 .edges()
217 .iter()
218 .all(|&(l, r)| self.is_edge_covered(l, r, config))
219 }
220
221 pub fn count_covered_edges(&self, config: &[usize]) -> usize {
223 use crate::topology::Graph;
224 self.graph
225 .edges()
226 .iter()
227 .filter(|&&(l, r)| self.is_edge_covered(l, r, config))
228 .count()
229 }
230
231 pub fn total_biclique_size(&self, config: &[usize]) -> usize {
233 config.iter().filter(|&&x| x == 1).count()
234 }
235}
236
237#[cfg(test)]
239pub(crate) fn is_biclique_cover(
240 edges: &[(usize, usize)],
241 left_bicliques: &[HashSet<usize>],
242 right_bicliques: &[HashSet<usize>],
243) -> bool {
244 let edge_set: HashSet<(usize, usize)> = edges
245 .iter()
246 .map(|&(u, v)| if u <= v { (u, v) } else { (v, u) })
247 .collect();
248
249 let all_bicliques_are_subgraphs =
250 left_bicliques
251 .iter()
252 .zip(right_bicliques.iter())
253 .all(|(lb, rb)| {
254 lb.iter().all(|&l| {
255 rb.iter().all(|&r| {
256 let edge = if l <= r { (l, r) } else { (r, l) };
257 edge_set.contains(&edge)
258 })
259 })
260 });
261
262 all_bicliques_are_subgraphs
263 && edges.iter().all(|&(l, r)| {
264 left_bicliques
265 .iter()
266 .zip(right_bicliques.iter())
267 .any(|(lb, rb)| lb.contains(&l) && rb.contains(&r))
268 })
269}
270
271impl Problem for BicliqueCover {
272 const NAME: &'static str = "BicliqueCover";
273 type Value = Min<i32>;
274
275 fn dims(&self) -> Vec<usize> {
276 vec![2; self.num_vertices() * self.k]
278 }
279
280 fn evaluate(&self, config: &[usize]) -> Min<i32> {
281 if !self.is_valid_cover(config) {
282 return Min(None);
283 }
284 Min(Some(self.total_biclique_size(config) as i32))
285 }
286
287 fn variant() -> Vec<(&'static str, &'static str)> {
288 crate::variant_params![]
289 }
290}
291
292crate::declare_variants! {
293 default BicliqueCover => "2^(num_vertices * rank)",
294}
295
296#[cfg(feature = "example-db")]
297pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
298 use crate::topology::BipartiteGraph;
299 vec![crate::example_db::specs::ModelExampleSpec {
305 id: "biclique_cover",
306 instance: Box::new(BicliqueCover::new(
307 BipartiteGraph::new(2, 3, vec![(0, 0), (0, 1), (1, 1), (1, 2)]),
308 2,
309 )),
310 optimal_config: vec![1, 0, 0, 1, 1, 0, 1, 1, 0, 1],
311 optimal_value: serde_json::json!(6),
312 }]
313}
314
315#[cfg(test)]
316#[path = "../../unit_tests/models/graph/biclique_cover.rs"]
317mod tests;