Skip to main content

problemreductions/models/graph/
subgraph_isomorphism.rs

1//! SubgraphIsomorphism problem implementation.
2//!
3//! The Subgraph Isomorphism problem asks whether a "pattern" graph H can be
4//! found embedded within a "host" graph G as a subgraph — that is, whether
5//! there exists an injective mapping f: V(H) -> V(G) such that every edge
6//! {u,v} in H maps to an edge {f(u),f(v)} in G.
7
8use crate::registry::{FieldInfo, ProblemSchemaEntry};
9use crate::topology::{Graph, SimpleGraph};
10use crate::traits::Problem;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14    ProblemSchemaEntry {
15        name: "SubgraphIsomorphism",
16        display_name: "Subgraph Isomorphism",
17        aliases: &[],
18        dimensions: &[],
19        module_path: module_path!(),
20        description: "Determine if host graph G contains a subgraph isomorphic to pattern graph H",
21        fields: &[
22            FieldInfo { name: "graph", type_name: "SimpleGraph", description: "The host graph G = (V_1, E_1) to search in" },
23            FieldInfo { name: "pattern", type_name: "SimpleGraph", description: "The pattern graph H = (V_2, E_2) to find as a subgraph" },
24        ],
25    }
26}
27
28/// The Subgraph Isomorphism problem.
29///
30/// Given a host graph G = (V_1, E_1) and a pattern graph H = (V_2, E_2),
31/// determine whether there exists an injective function f: V_2 -> V_1 such
32/// that for every edge {u,v} in E_2, {f(u), f(v)} is an edge in E_1.
33///
34/// This is a satisfaction (decision) problem: the metric is `bool`.
35///
36/// # Configuration
37///
38/// A configuration is a vector of length |V_2| where each entry is a value
39/// in {0, ..., |V_1|-1} representing the host vertex that each pattern
40/// vertex maps to. The configuration is valid (true) if:
41/// 1. All mapped host vertices are distinct (injective mapping)
42/// 2. Every edge in the pattern graph maps to an edge in the host graph
43///
44/// # Example
45///
46/// ```
47/// use problemreductions::models::graph::SubgraphIsomorphism;
48/// use problemreductions::topology::SimpleGraph;
49/// use problemreductions::{Problem, Solver, BruteForce};
50///
51/// // Host: K4 (complete graph on 4 vertices)
52/// let host = SimpleGraph::new(4, vec![(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)]);
53/// // Pattern: triangle (K3)
54/// let pattern = SimpleGraph::new(3, vec![(0,1),(0,2),(1,2)]);
55/// let problem = SubgraphIsomorphism::new(host, pattern);
56///
57/// // Mapping [0, 1, 2] means pattern vertex 0->host 0, 1->1, 2->2
58/// assert!(problem.evaluate(&[0, 1, 2]));
59///
60/// let solver = BruteForce::new();
61/// let solution = solver.find_witness(&problem);
62/// assert!(solution.is_some());
63/// ```
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct SubgraphIsomorphism {
66    /// The host graph G = (V_1, E_1).
67    host_graph: SimpleGraph,
68    /// The pattern graph H = (V_2, E_2).
69    pattern_graph: SimpleGraph,
70}
71
72impl SubgraphIsomorphism {
73    /// Create a new SubgraphIsomorphism problem.
74    ///
75    /// # Arguments
76    /// * `host_graph` - The host graph to search in
77    /// * `pattern_graph` - The pattern graph to find as a subgraph
78    pub fn new(host_graph: SimpleGraph, pattern_graph: SimpleGraph) -> Self {
79        Self {
80            host_graph,
81            pattern_graph,
82        }
83    }
84
85    /// Get a reference to the host graph.
86    pub fn host_graph(&self) -> &SimpleGraph {
87        &self.host_graph
88    }
89
90    /// Get a reference to the pattern graph.
91    pub fn pattern_graph(&self) -> &SimpleGraph {
92        &self.pattern_graph
93    }
94
95    /// Get the number of vertices in the host graph.
96    pub fn num_host_vertices(&self) -> usize {
97        self.host_graph.num_vertices()
98    }
99
100    /// Get the number of edges in the host graph.
101    pub fn num_host_edges(&self) -> usize {
102        self.host_graph.num_edges()
103    }
104
105    /// Get the number of vertices in the pattern graph.
106    pub fn num_pattern_vertices(&self) -> usize {
107        self.pattern_graph.num_vertices()
108    }
109
110    /// Get the number of edges in the pattern graph.
111    pub fn num_pattern_edges(&self) -> usize {
112        self.pattern_graph.num_edges()
113    }
114
115    /// Check if a configuration represents a valid subgraph isomorphism.
116    pub fn is_valid_solution(&self, config: &[usize]) -> bool {
117        self.evaluate(config).0
118    }
119}
120
121impl Problem for SubgraphIsomorphism {
122    const NAME: &'static str = "SubgraphIsomorphism";
123    type Value = crate::types::Or;
124
125    fn dims(&self) -> Vec<usize> {
126        let n_host = self.host_graph.num_vertices();
127        let n_pattern = self.pattern_graph.num_vertices();
128
129        if n_pattern > n_host {
130            // No injective mapping possible: each variable gets an empty domain.
131            vec![0; n_pattern]
132        } else {
133            vec![n_host; n_pattern]
134        }
135    }
136
137    fn evaluate(&self, config: &[usize]) -> crate::types::Or {
138        crate::types::Or({
139            let n_pattern = self.pattern_graph.num_vertices();
140            let n_host = self.host_graph.num_vertices();
141
142            // If the pattern has more vertices than the host, no injective mapping exists.
143            if n_pattern > n_host {
144                return crate::types::Or(false);
145            }
146
147            // Config must have one entry per pattern vertex
148            if config.len() != n_pattern {
149                return crate::types::Or(false);
150            }
151
152            // All values must be valid host vertex indices
153            if config.iter().any(|&v| v >= n_host) {
154                return crate::types::Or(false);
155            }
156
157            // Check injectivity: all mapped host vertices must be distinct
158            for i in 0..n_pattern {
159                for j in (i + 1)..n_pattern {
160                    if config[i] == config[j] {
161                        return crate::types::Or(false);
162                    }
163                }
164            }
165
166            // Check edge preservation: every pattern edge must map to a host edge
167            for (u, v) in self.pattern_graph.edges() {
168                if !self.host_graph.has_edge(config[u], config[v]) {
169                    return crate::types::Or(false);
170                }
171            }
172
173            true
174        })
175    }
176
177    fn variant() -> Vec<(&'static str, &'static str)> {
178        crate::variant_params![]
179    }
180}
181
182crate::declare_variants! {
183    default SubgraphIsomorphism => "num_host_vertices ^ num_pattern_vertices",
184}
185
186#[cfg(feature = "example-db")]
187pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
188    use crate::topology::SimpleGraph;
189    // Host: K4, Pattern: K3 → map [0,1,2] preserves all edges
190    vec![crate::example_db::specs::ModelExampleSpec {
191        id: "subgraph_isomorphism",
192        instance: Box::new(SubgraphIsomorphism::new(
193            SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]),
194            SimpleGraph::new(3, vec![(0, 1), (0, 2), (1, 2)]),
195        )),
196        optimal_config: vec![0, 1, 2],
197        optimal_value: serde_json::json!(true),
198    }]
199}
200
201#[cfg(test)]
202#[path = "../../unit_tests/models/graph/subgraph_isomorphism.rs"]
203mod tests;