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;