Skip to main content

problemreductions/models/graph/
minimum_intersection_graph_basis.rs

1//! Minimum Intersection Graph Basis problem implementation.
2//!
3//! Given a graph G = (V, E), find a universe U of minimum cardinality such that
4//! each vertex v can be assigned a subset S[v] ⊆ U with the intersection graph
5//! of {S[v]} equal to G.
6
7use 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        module_path: module_path!(),
23        description: "Find minimum universe size for intersection graph representation",
24        fields: &[
25            FieldInfo { name: "graph", type_name: "G", description: "The underlying graph G=(V,E)" },
26        ],
27    }
28}
29
30/// The Minimum Intersection Graph Basis problem.
31///
32/// Given a graph G = (V, E), find a universe U of minimum cardinality and
33/// an assignment of subsets S[v] ⊆ U for each vertex v ∈ V such that:
34/// - For every edge (u, v) ∈ E: S[u] ∩ S[v] ≠ ∅
35/// - For every non-edge pair (u, v) ∉ E: S[u] ∩ S[v] = ∅
36/// - |U| is minimized
37///
38/// The minimum |U| is the *intersection number* of G.
39///
40/// Variables: n × |E| binary variables where n = |V| and |E| is the upper bound
41/// on universe size. config[v * |E| + s] = 1 means element s ∈ S[v].
42///
43/// # Type Parameters
44///
45/// * `G` - The graph type (e.g., `SimpleGraph`)
46///
47/// # Example
48///
49/// ```
50/// use problemreductions::models::graph::MinimumIntersectionGraphBasis;
51/// use problemreductions::topology::SimpleGraph;
52/// use problemreductions::{Problem, Solver, BruteForce};
53///
54/// // Path P3: 3 vertices, edges (0,1), (1,2)
55/// let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2)]);
56/// let problem = MinimumIntersectionGraphBasis::new(graph);
57///
58/// let solver = BruteForce::new();
59/// let solution = solver.find_witness(&problem).unwrap();
60/// let value = problem.evaluate(&solution);
61/// assert_eq!(value, problemreductions::types::Min(Some(2)));
62/// ```
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct MinimumIntersectionGraphBasis<G> {
65    /// The underlying graph.
66    graph: G,
67}
68
69impl<G: Graph> MinimumIntersectionGraphBasis<G> {
70    /// Create a MinimumIntersectionGraphBasis problem from a graph.
71    pub fn new(graph: G) -> Self {
72        Self { graph }
73    }
74
75    /// Get a reference to the underlying graph.
76    pub fn graph(&self) -> &G {
77        &self.graph
78    }
79
80    /// Get the number of vertices in the underlying graph.
81    pub fn num_vertices(&self) -> usize {
82        self.graph.num_vertices()
83    }
84
85    /// Get the number of edges in the underlying graph.
86    pub fn num_edges(&self) -> usize {
87        self.graph.num_edges()
88    }
89}
90
91impl<G> Problem for MinimumIntersectionGraphBasis<G>
92where
93    G: Graph + crate::variant::VariantParam,
94{
95    const NAME: &'static str = "MinimumIntersectionGraphBasis";
96    type Value = Min<usize>;
97
98    fn variant() -> Vec<(&'static str, &'static str)> {
99        crate::variant_params![G]
100    }
101
102    fn dims(&self) -> Vec<usize> {
103        let n = self.graph.num_vertices();
104        let m = self.graph.num_edges();
105        if m == 0 {
106            // No edges: no variables needed; empty assignment is trivially valid.
107            return vec![];
108        }
109        vec![2; n * m]
110    }
111
112    fn evaluate(&self, config: &[usize]) -> Min<usize> {
113        let n = self.graph.num_vertices();
114        let m = self.graph.num_edges();
115
116        if m == 0 {
117            // No edges: universe size 0 suffices (all subsets empty, no
118            // adjacency constraints). But we must also check that no two
119            // vertices are adjacent — which is guaranteed when m == 0.
120            if config.is_empty() {
121                return Min(Some(0));
122            } else {
123                return Min(None);
124            }
125        }
126
127        if config.len() != n * m {
128            return Min(None);
129        }
130
131        // Parse subsets: S[v] = set of elements s where config[v * m + s] == 1
132        let subsets: Vec<HashSet<usize>> = (0..n)
133            .map(|v| (0..m).filter(|&s| config[v * m + s] == 1).collect())
134            .collect();
135
136        // Check edge constraints: for every edge (u, v), S[u] ∩ S[v] ≠ ∅
137        let edges = self.graph.edges();
138        for &(u, v) in &edges {
139            if subsets[u].is_disjoint(&subsets[v]) {
140                return Min(None);
141            }
142        }
143
144        // Check non-edge constraints: for every non-edge pair (u, v), S[u] ∩ S[v] = ∅
145        for u in 0..n {
146            for v in (u + 1)..n {
147                if !self.graph.has_edge(u, v) && !subsets[u].is_disjoint(&subsets[v]) {
148                    return Min(None);
149                }
150            }
151        }
152
153        // Count elements used (union of all subsets)
154        let used: HashSet<usize> = subsets.iter().flat_map(|s| s.iter().copied()).collect();
155        Min(Some(used.len()))
156    }
157}
158
159crate::declare_variants! {
160    default MinimumIntersectionGraphBasis<SimpleGraph> => "num_edges^num_edges",
161}
162
163#[cfg(feature = "example-db")]
164pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
165    // P3: 3 vertices, edges (0,1), (1,2), num_edges=2
166    // Intersection number = 2: S[0]={0}, S[1]={0,1}, S[2]={1}
167    // Config: vertex 0: [1,0], vertex 1: [1,1], vertex 2: [0,1]
168    // Full config: [1,0, 1,1, 0,1]
169    vec![crate::example_db::specs::ModelExampleSpec {
170        id: "minimum_intersection_graph_basis_simplegraph",
171        instance: Box::new(MinimumIntersectionGraphBasis::new(SimpleGraph::new(
172            3,
173            vec![(0, 1), (1, 2)],
174        ))),
175        optimal_config: vec![1, 0, 1, 1, 0, 1],
176        optimal_value: serde_json::json!(2),
177    }]
178}
179
180#[cfg(test)]
181#[path = "../../unit_tests/models/graph/minimum_intersection_graph_basis.rs"]
182mod tests;