problemreductions/models/graph/
maximum_common_edge_subgraph.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
16use crate::traits::Problem;
17use crate::types::Max;
18use serde::{Deserialize, Serialize};
19
20inventory::submit! {
21 ProblemSchemaEntry {
22 name: "MaximumCommonEdgeSubgraph",
23 display_name: "Maximum Common Edge Subgraph",
24 aliases: &["MCES"],
25 dimensions: &[],
26 category: crate::registry::ProblemCategory::Graph,
27 module_path: module_path!(),
28 description: "Maximize the number of preserved labelled directed arcs under a partial injective vertex map from G1 into G2",
29 fields: &[
30 FieldInfo {
31 name: "graph_1",
32 type_name: "LabelledDigraph",
33 description: "Source directed edge-labelled graph G1 = (V1, E1) whose vertices are mapped",
34 },
35 FieldInfo {
36 name: "graph_2",
37 type_name: "LabelledDigraph",
38 description: "Target directed edge-labelled graph G2 = (V2, E2) receiving the partial injective map",
39 },
40 ],
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub struct LabelledArc {
50 pub src: usize,
52 pub label: usize,
54 pub dst: usize,
56}
57
58impl LabelledArc {
59 pub fn new(src: usize, label: usize, dst: usize) -> Self {
61 Self { src, label, dst }
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct LabelledDigraph {
73 pub num_vertices: usize,
75 pub arcs: Vec<LabelledArc>,
77}
78
79impl LabelledDigraph {
80 pub fn new(num_vertices: usize, arcs: Vec<LabelledArc>) -> Self {
85 for arc in &arcs {
86 assert!(
87 arc.src < num_vertices,
88 "labelled arc source {} out of range for num_vertices = {}",
89 arc.src,
90 num_vertices
91 );
92 assert!(
93 arc.dst < num_vertices,
94 "labelled arc destination {} out of range for num_vertices = {}",
95 arc.dst,
96 num_vertices
97 );
98 }
99 let mut seen = std::collections::HashSet::new();
101 let mut deduped = Vec::with_capacity(arcs.len());
102 for arc in arcs {
103 if seen.insert((arc.src, arc.label, arc.dst)) {
104 deduped.push(arc);
105 }
106 }
107 Self {
108 num_vertices,
109 arcs: deduped,
110 }
111 }
112
113 pub fn num_vertices(&self) -> usize {
115 self.num_vertices
116 }
117
118 pub fn num_arcs(&self) -> usize {
120 self.arcs.len()
121 }
122
123 pub fn arcs(&self) -> &[LabelledArc] {
125 &self.arcs
126 }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146pub struct MaximumCommonEdgeSubgraph {
147 graph_1: LabelledDigraph,
148 graph_2: LabelledDigraph,
149}
150
151impl MaximumCommonEdgeSubgraph {
152 pub fn new(graph_1: LabelledDigraph, graph_2: LabelledDigraph) -> Self {
154 Self { graph_1, graph_2 }
155 }
156
157 pub fn graph_1(&self) -> &LabelledDigraph {
159 &self.graph_1
160 }
161
162 pub fn graph_2(&self) -> &LabelledDigraph {
164 &self.graph_2
165 }
166
167 pub fn num_vertices_1(&self) -> usize {
169 self.graph_1.num_vertices()
170 }
171
172 pub fn num_vertices_2(&self) -> usize {
174 self.graph_2.num_vertices()
175 }
176
177 pub fn num_arcs_1(&self) -> usize {
179 self.graph_1.num_arcs()
180 }
181
182 pub fn num_arcs_2(&self) -> usize {
184 self.graph_2.num_arcs()
185 }
186
187 pub fn bottom_index(&self) -> usize {
189 self.graph_2.num_vertices()
190 }
191
192 pub fn is_valid_solution(&self, config: &[usize]) -> bool {
198 let n1 = self.num_vertices_1();
199 let n2 = self.num_vertices_2();
200 if config.len() != n1 {
201 return false;
202 }
203 let bottom = n2;
204 let mut used = vec![false; n2];
205 for &value in config {
206 if value > bottom {
207 return false;
208 }
209 if value == bottom {
210 continue;
211 }
212 if used[value] {
213 return false;
214 }
215 used[value] = true;
216 }
217 true
218 }
219
220 pub fn preserved_arc_count(
223 &self,
224 config: &[usize],
225 ) -> Result<Option<i64>, crate::traits::EvaluationError> {
226 if !self.is_valid_solution(config) {
227 return Ok(None);
228 }
229 let bottom = self.bottom_index();
230 let arcs_2: std::collections::HashSet<(usize, usize, usize)> = self
232 .graph_2
233 .arcs()
234 .iter()
235 .map(|arc| (arc.src, arc.label, arc.dst))
236 .collect();
237 let mut count = 0usize;
238 for arc in self.graph_1.arcs() {
239 let fu = config[arc.src];
240 let fv = config[arc.dst];
241 if fu == bottom || fv == bottom {
242 continue;
243 }
244 if arcs_2.contains(&(fu, arc.label, fv)) {
245 count += 1;
246 }
247 }
248 Ok(Some(i64::try_from(count).map_err(|_| {
249 crate::traits::EvaluationError::IntegerOverflow(
250 "converting preserved-arc count to i64".into(),
251 )
252 })?))
253 }
254}
255
256impl Problem for MaximumCommonEdgeSubgraph {
257 const NAME: &'static str = "MaximumCommonEdgeSubgraph";
258 type Solution = Vec<usize>;
259 type Value = Max<i64>;
260
261 crate::problem_parameters![
262 ("num_arcs_1", num_arcs_1),
263 ("num_arcs_2", num_arcs_2),
264 ("num_vertices_1", num_vertices_1),
265 ("num_vertices_2", num_vertices_2),
266 ];
267
268 fn variant() -> Vec<(&'static str, &'static str)> {
269 crate::variant_params![]
270 }
271
272 fn evaluate(
273 &self,
274 config: &Self::Solution,
275 ) -> Result<Max<i64>, crate::traits::EvaluationError> {
276 if config.len() != self.num_vertices_1() {
277 return Err(crate::traits::EvaluationError::InvalidConfiguration(
278 "vertex mapping length does not match the first graph".into(),
279 ));
280 }
281 if config.iter().any(|&vertex| vertex > self.num_vertices_2()) {
282 return Err(crate::traits::EvaluationError::InvalidConfiguration(
283 "vertex mapping contains an out-of-range target vertex".into(),
284 ));
285 }
286 Ok({
287 match self.preserved_arc_count(config)? {
288 Some(count) => Max(Some(count)),
289 None => Max(None),
290 }
291 })
292 }
293}
294
295impl crate::solvers::BruteForceProblem for MaximumCommonEdgeSubgraph {
296 fn dimensions(&self) -> Vec<usize> {
297 vec![self.graph_2.num_vertices() + 1; self.graph_1.num_vertices()]
298 }
299}
300
301crate::declare_variants! {
302 default MaximumCommonEdgeSubgraph => "(num_vertices_2 + 1)^num_vertices_1",
303}
304
305crate::register_brute_force! {
306 MaximumCommonEdgeSubgraph,
307}
308
309#[cfg(feature = "example-db")]
310pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
311 vec![crate::example_db::specs::ModelExampleSpec {
312 id: "maximum_common_edge_subgraph",
313 instance: Box::new(MaximumCommonEdgeSubgraph::new(
314 LabelledDigraph::new(
315 5,
316 vec![
317 LabelledArc::new(0, 0, 1),
318 LabelledArc::new(1, 1, 2),
319 LabelledArc::new(0, 2, 2),
320 LabelledArc::new(2, 0, 3),
321 LabelledArc::new(1, 3, 3),
322 LabelledArc::new(3, 1, 4),
323 ],
324 ),
325 LabelledDigraph::new(
326 4,
327 vec![
328 LabelledArc::new(0, 0, 1),
329 LabelledArc::new(1, 1, 2),
330 LabelledArc::new(0, 2, 2),
331 LabelledArc::new(2, 0, 3),
332 LabelledArc::new(1, 3, 3),
333 LabelledArc::new(0, 1, 3),
334 ],
335 ),
336 )),
337 optimal_config: serde_json::json!(vec![0, 1, 2, 3, 4]),
340 optimal_value: serde_json::json!(5),
341 }]
342}
343
344#[cfg(test)]
345#[path = "../../unit_tests/models/graph/maximum_common_edge_subgraph.rs"]
346mod tests;