problemreductions/models/graph/
subgraph_isomorphism.rs1use 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 category: crate::registry::ProblemCategory::Graph,
20 module_path: module_path!(),
21 description: "Determine if host graph G contains a subgraph isomorphic to pattern graph H",
22 fields: &[
23 FieldInfo { name: "graph", type_name: "SimpleGraph", description: "The host graph G = (V_1, E_1) to search in" },
24 FieldInfo { name: "pattern", type_name: "SimpleGraph", description: "The pattern graph H = (V_2, E_2) to find as a subgraph" },
25 ],
26 }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct SubgraphIsomorphism {
67 host_graph: SimpleGraph,
69 pattern_graph: SimpleGraph,
71}
72
73impl SubgraphIsomorphism {
74 pub fn new(host_graph: SimpleGraph, pattern_graph: SimpleGraph) -> Self {
80 Self {
81 host_graph,
82 pattern_graph,
83 }
84 }
85
86 pub fn host_graph(&self) -> &SimpleGraph {
88 &self.host_graph
89 }
90
91 pub fn pattern_graph(&self) -> &SimpleGraph {
93 &self.pattern_graph
94 }
95
96 pub fn num_host_vertices(&self) -> usize {
98 self.host_graph.num_vertices()
99 }
100
101 pub fn num_host_edges(&self) -> usize {
103 self.host_graph.num_edges()
104 }
105
106 pub fn num_pattern_vertices(&self) -> usize {
108 self.pattern_graph.num_vertices()
109 }
110
111 pub fn num_pattern_edges(&self) -> usize {
113 self.pattern_graph.num_edges()
114 }
115
116 pub fn is_valid_solution(
118 &self,
119 config: &[usize],
120 ) -> Result<bool, crate::traits::EvaluationError> {
121 let n_pattern = self.pattern_graph.num_vertices();
122 let n_host = self.host_graph.num_vertices();
123
124 if n_pattern > n_host {
125 return Ok(false);
126 }
127 if config.len() != n_pattern {
128 return Err(crate::traits::EvaluationError::InvalidConfiguration(
129 "vertex mapping length does not match the pattern graph".into(),
130 ));
131 }
132 if config.iter().any(|&vertex| vertex >= n_host) {
133 return Err(crate::traits::EvaluationError::InvalidConfiguration(
134 "vertex mapping contains an out-of-range target vertex".into(),
135 ));
136 }
137 for i in 0..n_pattern {
138 for j in (i + 1)..n_pattern {
139 if config[i] == config[j] {
140 return Ok(false);
141 }
142 }
143 }
144 for (u, v) in self.pattern_graph.edges() {
145 if !self.host_graph.has_edge(config[u], config[v]) {
146 return Ok(false);
147 }
148 }
149 Ok(true)
150 }
151}
152
153impl Problem for SubgraphIsomorphism {
154 const NAME: &'static str = "SubgraphIsomorphism";
155 type Solution = Vec<usize>;
156 type Value = crate::types::Or;
157
158 crate::problem_parameters![
159 ("num_host_edges", num_host_edges),
160 ("num_host_vertices", num_host_vertices),
161 ("num_pattern_edges", num_pattern_edges),
162 ("num_pattern_vertices", num_pattern_vertices),
163 ];
164
165 fn evaluate(
166 &self,
167 config: &Self::Solution,
168 ) -> Result<crate::types::Or, crate::traits::EvaluationError> {
169 Ok(crate::types::Or(self.is_valid_solution(config)?))
170 }
171
172 fn variant() -> Vec<(&'static str, &'static str)> {
173 crate::variant_params![]
174 }
175}
176
177impl crate::solvers::BruteForceProblem for SubgraphIsomorphism {
178 fn dimensions(&self) -> Vec<usize> {
179 let n_host = self.host_graph.num_vertices();
180 let n_pattern = self.pattern_graph.num_vertices();
181
182 if n_pattern > n_host {
183 vec![0; n_pattern]
185 } else {
186 vec![n_host; n_pattern]
187 }
188 }
189}
190
191crate::declare_variants! {
192 default SubgraphIsomorphism => "num_host_vertices ^ num_pattern_vertices",
193}
194
195crate::register_brute_force! {
196 SubgraphIsomorphism,
197}
198
199#[cfg(feature = "example-db")]
200pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
201 use crate::topology::SimpleGraph;
202 vec![crate::example_db::specs::ModelExampleSpec {
204 id: "subgraph_isomorphism",
205 instance: Box::new(SubgraphIsomorphism::new(
206 SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]),
207 SimpleGraph::new(3, vec![(0, 1), (0, 2), (1, 2)]),
208 )),
209 optimal_config: serde_json::json!(vec![0, 1, 2]),
210 optimal_value: serde_json::json!(true),
211 }]
212}
213
214#[cfg(test)]
215#[path = "../../unit_tests/models/graph/subgraph_isomorphism.rs"]
216mod tests;