1use std::{
2 cmp::Ordering,
3 collections::{HashMap, HashSet},
4 fmt::Debug,
5 hash::Hash,
6};
7
8use bimap::BiHashMap;
9
10use crate::filter_map::FilterMap;
11use crate::{
12 graph::{incoming_nodes, outgoing_nodes, Graph},
13 pattern_matching::{MatchedGraph, PatternElement, PatternGraph, SubgraphAlgorithm},
14};
15
16pub struct VfState<
27 'a,
28 NodeWeight,
29 EdgeWeight,
30 NRef,
31 ERef,
32 N2Ref,
33 E2Ref,
34 P: PatternGraph<NodeWeight, EdgeWeight, NodeRef = NRef, EdgeRef = ERef>,
35 B: Graph<NodeWeight, EdgeWeight, NodeRef = N2Ref, EdgeRef = E2Ref>,
36> where
37 NRef: Debug,
38 ERef: Debug,
39 N2Ref: Debug,
40 E2Ref: Debug,
41{
42 pattern_graph: &'a P,
44 base_graph: &'a B,
46 results: Vec<MatchedGraph<'a, NodeWeight, EdgeWeight, P>>,
48
49 core: BiHashMap<NRef, N2Ref>,
55 out_1: HashMap<NRef, usize>,
59 out_2: HashMap<N2Ref, usize>,
61 in_1: HashMap<NRef, usize>,
65 in_2: HashMap<N2Ref, usize>,
67
68 nodes_to_take: usize,
70}
71
72impl<'a, NodeWeight, EdgeWeight, NRef, ERef, N2Ref, E2Ref, P, B>
75 VfState<'a, NodeWeight, EdgeWeight, NRef, ERef, N2Ref, E2Ref, P, B>
76where
77 NRef: Copy + Hash + Ord + Debug,
78 N2Ref: Copy + Hash + Eq + Debug,
79 ERef: Copy + Eq + Hash + Debug,
80 E2Ref: Copy + Debug,
81 P: PatternGraph<NodeWeight, EdgeWeight, NodeRef = NRef, EdgeRef = ERef>,
82 B: Graph<NodeWeight, EdgeWeight, NodeRef = N2Ref, EdgeRef = E2Ref>,
83{
84 fn give_node_order(&self, n1: NRef, n2: NRef) -> Ordering {
92 let n1_appears = self.pattern_graph.node_weight(n1).should_appear();
93 let n2_appears = self.pattern_graph.node_weight(n2).should_appear();
94 if n1_appears && !n2_appears {
95 Ordering::Less
96 } else if !n1_appears && n2_appears {
97 Ordering::Greater
98 } else {
99 n1.cmp(&n2)
100 }
101 }
102
103 fn find_unmatched_unconnected_nodes(&'a self) -> (Option<NRef>, Vec<N2Ref>) {
110 let n = self
111 .pattern_graph
112 .nodes()
113 .filter(|n| !self.core.contains_left(n))
114 .min_by(|n1, n2| self.give_node_order(*n1, *n2));
115
116 let base_nodes: Vec<_> = self
117 .base_graph
118 .nodes()
119 .filter(|n| !self.core.contains_right(n))
120 .collect();
121
122 (n, base_nodes)
123 }
124
125 fn find_unmatched_neighbors(
135 &'a self,
136 pattern_map: &HashMap<NRef, usize>,
137 base_map: &HashMap<N2Ref, usize>,
138 find_ignored: bool,
139 ) -> (Option<NRef>, Vec<N2Ref>) {
140 let n = pattern_map
143 .keys()
144 .filter(|n_out| !self.core.contains_left(n_out))
145 .min_by(|n1, n2| self.give_node_order(**n1, **n2))
146 .filter(|n| find_ignored || self.pattern_graph.node_weight(**n).should_appear())
147 .cloned();
148
149 let n2: Vec<_> = base_map
150 .keys()
151 .filter(|m_out| !self.core.contains_right(m_out))
152 .cloned()
153 .collect();
154 (n, n2)
155 }
156
157 fn assign(&mut self, n: NRef, m: N2Ref, depth: usize) {
160 self.core.insert(n, m);
162 self.out_1.entry(n).or_insert(depth);
163 self.out_2.entry(m).or_insert(depth);
164 self.in_1.entry(n).or_insert(depth);
165 self.in_2.entry(m).or_insert(depth);
166
167 outgoing_nodes(self.pattern_graph, n).for_each(|n_out| {
169 self.out_1.entry(n_out).or_insert(depth);
170 });
171 outgoing_nodes(self.base_graph, m).for_each(|m_out| {
173 self.out_2.entry(m_out).or_insert(depth);
174 });
175 incoming_nodes(self.pattern_graph, n).for_each(|n_in| {
177 self.in_1.entry(n_in).or_insert(depth);
178 });
179 incoming_nodes(self.base_graph, m).for_each(|m_in| {
181 self.in_2.entry(m_in).or_insert(depth);
182 });
183 }
184
185 fn is_valid_matching(&self, n: NRef, m: N2Ref) -> bool {
196 self.check_node_semantics(n, m)
197 && self.check_predecessor_relation(n, m)
198 && self.check_successor_relation(n, m)
199 && self.check_edge_semantics(n, m)
200 }
201
202 fn check_predecessor_relation(&self, n: NRef, m: N2Ref) -> bool {
206 let n_preds: HashSet<_> = incoming_nodes(self.pattern_graph, n)
208 .filter(|n_pred| self.core.contains_left(n_pred))
209 .collect();
210 let m_preds: HashSet<_> = incoming_nodes(self.base_graph, m)
212 .filter(|m_pred| self.core.contains_right(m_pred))
213 .collect();
214
215 n_preds.iter().all(|n2| {
218 self.core
219 .get_by_left(n2)
220 .is_some_and(|m2| m_preds.contains(m2))
221 })
222 }
223
224 fn check_successor_relation(&self, n: NRef, m: N2Ref) -> bool {
228 let n_succs: HashSet<_> = outgoing_nodes(self.pattern_graph, n)
230 .filter(|n_succ| self.core.contains_left(n_succ))
231 .collect();
232 let m_succs: HashSet<_> = outgoing_nodes(self.base_graph, m)
234 .filter(|m_succ| self.core.contains_right(m_succ))
235 .collect();
236
237 n_succs.iter().all(|n2| {
239 self.core
240 .get_by_left(n2)
241 .is_some_and(|m2| m_succs.contains(m2))
242 })
243 }
244
245 fn check_node_semantics(&self, n: NRef, m: N2Ref) -> bool {
249 let matcher = self.pattern_graph.node_weight(n);
250 let refed_node = self.base_graph.node_weight(m);
251 matcher.may_match(refed_node)
252 }
253
254 fn check_edge_semantics(&self, n: NRef, m: N2Ref) -> bool {
257 let n_succs_matched = self
259 .pattern_graph
260 .outgoing_edges(n)
261 .map(|e| (self.pattern_graph.adjacent_nodes(e).1, e))
262 .filter(|(n_succ, _)| self.core.contains_left(n_succ));
263
264 let m_succs_matched: HashMap<N2Ref, E2Ref> = self
266 .base_graph
267 .outgoing_edges(m)
268 .map(|e| (self.base_graph.adjacent_nodes(e).1, e))
269 .filter(|(m_succ, _)| self.core.contains_right(m_succ))
270 .collect();
271
272 let n_m_succ_edges = n_succs_matched
274 .map(|(n_succ, e)| (e, m_succs_matched[self.core.get_by_left(&n_succ).unwrap()]));
275
276 let n_preds_matched = self
278 .pattern_graph
279 .incoming_edges(n)
280 .map(|e| (self.pattern_graph.adjacent_nodes(e).0, e))
281 .filter(|(n_pred, _)| self.core.contains_left(n_pred));
282
283 let m_preds_matched: HashMap<N2Ref, E2Ref> = self
285 .base_graph
286 .incoming_edges(m)
287 .map(|e| (self.base_graph.adjacent_nodes(e).0, e))
288 .filter(|(m_pred, _)| self.core.contains_right(m_pred))
289 .collect();
290
291 let n_m_pred_edges = n_preds_matched
293 .map(|(n_pred, e)| (e, m_preds_matched[self.core.get_by_left(&n_pred).unwrap()]));
294
295 n_m_pred_edges.chain(n_m_succ_edges).all(|(e, e2)| {
298 let matcher = self.pattern_graph.edge_weight(e);
299 let matched = self.base_graph.edge_weight(e2);
300 matcher.may_match(matched)
301 })
302 }
303
304 fn unassign(&mut self, n: &NRef, m: &N2Ref, depth: usize) {
306 self.core.remove_by_left(n);
308 Self::remove(n, depth, &mut self.out_1);
310 Self::remove(m, depth, &mut self.out_2);
311 Self::remove(n, depth, &mut self.in_1);
312 Self::remove(m, depth, &mut self.in_2);
313
314 outgoing_nodes(self.pattern_graph, *n)
316 .for_each(|n_out| Self::remove(&n_out, depth, &mut self.out_1));
317 outgoing_nodes(self.base_graph, *m)
319 .for_each(|m_out| Self::remove(&m_out, depth, &mut self.out_2));
320 incoming_nodes(self.pattern_graph, *n)
322 .for_each(|n_in| Self::remove(&n_in, depth, &mut self.in_1));
323 incoming_nodes(self.base_graph, *m)
325 .for_each(|n_in| Self::remove(&n_in, depth, &mut self.in_2));
326 }
327
328 fn remove<N>(index: &N, depth: usize, map: &mut HashMap<N, usize>)
331 where
332 N: Eq + Hash,
333 {
334 if let Some(insert_depth) = map.get(index) {
335 if insert_depth == &depth {
336 map.remove(index);
337 }
338 }
339 }
340
341 fn produce_graph(&mut self) {
347 let node_list = self
349 .core
350 .iter()
351 .filter(|(n, _)| self.pattern_graph.node_weight(**n).should_appear())
352 .map(|(n, m)| (*n, self.base_graph.node_weight(*m)))
353 .collect();
354
355 let mut edge_list = HashMap::new();
357 for (n, m) in &self.core {
361 let n_succs = self
362 .pattern_graph
363 .outgoing_edges(*n)
364 .filter(|e| self.pattern_graph.edge_weight(*e).should_appear())
365 .map(|e| (self.pattern_graph.adjacent_nodes(e).1, e));
366 let m_succs: HashMap<_, _> = self
367 .base_graph
368 .outgoing_edges(*m)
369 .map(|e2| (self.base_graph.adjacent_nodes(e2).1, e2))
370 .collect();
371 n_succs
372 .map(|(n_succ, e)| (e, m_succs[self.core.get_by_left(&n_succ).unwrap()]))
373 .map(|(e, e2)| (e, self.base_graph.edge_weight(e2)))
374 .for_each(|(e_ref, edge_weight)| {
375 edge_list.insert(e_ref, edge_weight);
376 });
377 }
378
379 let result = FilterMap::new(self.pattern_graph, node_list, edge_list);
380 self.results.push(result);
381 }
382
383 fn find_subgraphs(&mut self, depth: usize) -> usize {
388 if depth == self.pattern_graph.count_nodes() {
390 self.produce_graph();
391 self.nodes_to_take
392 } else {
393 let find_ignored = depth >= self.nodes_to_take;
394 let (mut pat_node, mut base_nodes) =
396 self.find_unmatched_neighbors(&self.out_1, &self.out_2, find_ignored);
397 if pat_node.is_none() || base_nodes.is_empty() {
399 (pat_node, base_nodes) =
400 self.find_unmatched_neighbors(&self.in_1, &self.in_2, find_ignored);
401 }
402 if pat_node.is_none() || base_nodes.is_empty() {
404 (pat_node, base_nodes) = self.find_unmatched_unconnected_nodes();
405 }
406
407 let n = pat_node.unwrap();
409 for m in base_nodes {
410 self.assign(n, m, depth);
411 if self.is_valid_matching(n, m) {
413 let next_node = self.find_subgraphs(depth + 1);
416 if next_node == self.nodes_to_take && next_node <= depth {
417 self.unassign(&n, &m, depth);
419 return next_node;
420 }
421 }
422 self.unassign(&n, &m, depth);
423 }
424 depth
425 }
426 }
427
428 fn init(
438 pattern_graph: &'a P,
439 base_graph: &'a B,
440 ) -> VfState<'a, NodeWeight, EdgeWeight, NRef, ERef, N2Ref, E2Ref, P, B> {
441 let nodes_to_take = pattern_graph
443 .nodes()
444 .filter(|n| pattern_graph.node_weight(*n).should_appear())
445 .count();
446
447 VfState {
448 pattern_graph,
449 base_graph,
450 results: vec![],
451 core: BiHashMap::new(),
452 out_1: HashMap::new(),
453 out_2: HashMap::new(),
454 in_1: HashMap::new(),
455 in_2: HashMap::new(),
456 nodes_to_take,
457 }
458 }
459
460 fn run_query(&mut self) {
463 if self.pattern_graph.is_empty_graph()
465 || self.pattern_graph.count_nodes() > self.base_graph.count_nodes()
466 || self.pattern_graph.count_edges() > self.base_graph.count_edges()
467 {
468 return;
469 }
470 let _ = self.find_subgraphs(0);
471 }
472}
473
474impl<'a, NodeWeight, EdgeWeight, NRef, ERef, N2Ref, E2Ref, P, B>
475 SubgraphAlgorithm<'a, NodeWeight, EdgeWeight, NRef, ERef, N2Ref, E2Ref, P, B>
476 for VfState<'a, NodeWeight, EdgeWeight, NRef, ERef, N2Ref, E2Ref, P, B>
477where
478 NRef: Copy + Hash + Ord + Debug,
479 N2Ref: Copy + Hash + Eq + Debug,
480 ERef: Copy + Hash + Eq + Debug,
481 E2Ref: Copy + Debug,
482 P: PatternGraph<NodeWeight, EdgeWeight, NodeRef = NRef, EdgeRef = ERef>,
483 B: Graph<NodeWeight, EdgeWeight, NodeRef = N2Ref, EdgeRef = E2Ref>,
484{
485 fn eval(
486 pattern_graph: &'a P,
487 base_graph: &'a B,
488 ) -> Vec<
489 FilterMap<
490 'a,
491 PatternElement<NodeWeight>,
492 PatternElement<EdgeWeight>,
493 &'a NodeWeight,
494 &'a EdgeWeight,
495 P,
496 >,
497 > {
498 let mut vfstate = VfState::init(pattern_graph, base_graph);
499 vfstate.run_query();
500
501 std::mem::take(&mut vfstate.results)
503 }
504}