problemreductions/models/graph/
minimum_intersection_graph_basis.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
8use crate::topology::{Graph, SimpleGraph};
9use crate::traits::Problem;
10use crate::types::Min;
11use serde::{Deserialize, Serialize};
12use std::collections::HashSet;
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "MinimumIntersectionGraphBasis",
17 display_name: "Minimum Intersection Graph Basis",
18 aliases: &[],
19 dimensions: &[
20 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
21 ],
22 category: crate::registry::ProblemCategory::Graph,
23 module_path: module_path!(),
24 description: "Find minimum universe size for intersection graph representation",
25 fields: &[
26 FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct MinimumIntersectionGraphBasis<G> {
66 graph: G,
68}
69
70impl<G: Graph> MinimumIntersectionGraphBasis<G> {
71 pub fn new(graph: G) -> Self {
73 Self { graph }
74 }
75
76 pub fn graph(&self) -> &G {
78 &self.graph
79 }
80
81 pub fn num_vertices(&self) -> usize {
83 self.graph.num_vertices()
84 }
85
86 pub fn num_edges(&self) -> usize {
88 self.graph.num_edges()
89 }
90}
91
92impl<G> Problem for MinimumIntersectionGraphBasis<G>
93where
94 G: Graph + crate::variant::VariantParam,
95{
96 const NAME: &'static str = "MinimumIntersectionGraphBasis";
97 type Solution = Vec<Vec<bool>>;
98 type Value = Min<i64>;
99
100 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
101
102 fn variant() -> Vec<(&'static str, &'static str)> {
103 crate::variant_params![G]
104 }
105
106 fn evaluate(
107 &self,
108 solution: &Self::Solution,
109 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
110 let n = self.graph.num_vertices();
111 let m = self.graph.num_edges();
112 if solution.len() != n || solution.iter().any(|subset| subset.len() != m) {
113 return Err(crate::traits::EvaluationError::InvalidConfiguration(
114 "intersection-basis dimensions do not match the graph".into(),
115 ));
116 }
117 Ok({
118 if m == 0 {
119 return Ok(Min(Some(0)));
120 }
121
122 let subsets: Vec<HashSet<usize>> = solution
124 .iter()
125 .map(|row| {
126 row.iter()
127 .enumerate()
128 .filter_map(|(element, &selected)| selected.then_some(element))
129 .collect()
130 })
131 .collect();
132
133 let edges = self.graph.edges();
135 for &(u, v) in &edges {
136 if subsets[u].is_disjoint(&subsets[v]) {
137 return Ok(Min(None));
138 }
139 }
140
141 for u in 0..n {
143 for v in (u + 1)..n {
144 if !self.graph.has_edge(u, v) && !subsets[u].is_disjoint(&subsets[v]) {
145 return Ok(Min(None));
146 }
147 }
148 }
149
150 let used: HashSet<usize> = subsets.iter().flat_map(|s| s.iter().copied()).collect();
152 Min(Some(i64::try_from(used.len()).map_err(|_| {
153 crate::traits::EvaluationError::IntegerOverflow(
154 "converting intersection-basis size to i64".into(),
155 )
156 })?))
157 })
158 }
159}
160
161impl<G> crate::solvers::BruteForceProblem for MinimumIntersectionGraphBasis<G>
162where
163 G: Graph + crate::variant::VariantParam,
164{
165 fn dimensions(&self) -> Vec<usize> {
166 let n = self.graph.num_vertices();
167 let m = self.graph.num_edges();
168 if m == 0 {
169 return vec![];
171 }
172 vec![2; n * m]
173 }
174}
175
176crate::impl_random_generate!(
177 MinimumIntersectionGraphBasis<SimpleGraph>,
178 crate::random::SimpleGraphRandomSpec,
179 |spec| { Ok(MinimumIntersectionGraphBasis::new(spec.graph()?)) }
180);
181
182crate::declare_variants! {
183 default MinimumIntersectionGraphBasis<SimpleGraph> => "num_edges^num_edges" random,
184}
185
186crate::register_brute_force! {
187 MinimumIntersectionGraphBasis<SimpleGraph> decode |problem: &MinimumIntersectionGraphBasis<SimpleGraph>, indices: Vec<usize>| if problem.num_edges() == 0 { vec![Vec::new(); problem.num_vertices()] } else { indices.chunks(problem.num_edges()).map(crate::config::config_to_bits).collect() },
188}
189
190#[cfg(feature = "example-db")]
191pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
192 vec![crate::example_db::specs::ModelExampleSpec {
197 id: "minimum_intersection_graph_basis_simplegraph",
198 instance: Box::new(MinimumIntersectionGraphBasis::new(SimpleGraph::new(
199 3,
200 vec![(0, 1), (1, 2)],
201 ))),
202 optimal_config: serde_json::json!(vec![
203 vec![true, false],
204 vec![true, true],
205 vec![false, true]
206 ]),
207 optimal_value: serde_json::json!(2),
208 }]
209}
210
211#[cfg(test)]
212#[path = "../../unit_tests/models/graph/minimum_intersection_graph_basis.rs"]
213mod tests;