1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use super::data::DataMap;
5use super::visit::EdgeCount;
6use super::visit::EdgeRef;
7use super::visit::GetAdjacencyMatrix;
8use super::visit::GraphBase;
9use super::visit::GraphProp;
10use super::visit::IntoEdgesDirected;
11use super::visit::IntoNeighborsDirected;
12use super::visit::NodeCompactIndexable;
13use super::{Incoming, Outgoing};
14
15use self::semantic::EdgeMatcher;
16use self::semantic::NoSemanticMatch;
17use self::semantic::NodeMatcher;
18use self::state::Vf2State;
19
20mod state {
21 use super::*;
22
23 #[derive(Debug)]
24 pub struct Vf2State<'a, G: GetAdjacencyMatrix> {
26 pub graph: &'a G,
28 pub mapping: Vec<usize>,
31 out: Vec<usize>,
35 ins: Vec<usize>,
40 pub out_size: usize,
41 pub ins_size: usize,
42 pub adjacency_matrix: G::AdjMatrix,
43 generation: usize,
44 }
45
46 impl<'a, G> Vf2State<'a, G>
47 where
48 G: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
49 {
50 pub fn new(g: &'a G) -> Self {
51 let c0 = g.node_count();
52 Vf2State {
53 graph: g,
54 mapping: vec![usize::MAX; c0],
55 out: vec![0; c0],
56 ins: vec![0; c0 * (g.is_directed() as usize)],
57 out_size: 0,
58 ins_size: 0,
59 adjacency_matrix: g.adjacency_matrix(),
60 generation: 0,
61 }
62 }
63
64 pub fn is_complete(&self) -> bool {
66 self.generation == self.mapping.len()
67 }
68
69 pub fn push_mapping(&mut self, from: G::NodeId, to: usize) {
71 self.generation += 1;
72 self.mapping[self.graph.to_index(from)] = to;
73 for ix in self.graph.neighbors_directed(from, Outgoing) {
77 if self.out[self.graph.to_index(ix)] == 0 {
78 self.out[self.graph.to_index(ix)] = self.generation;
79 self.out_size += 1;
80 }
81 }
82 if self.graph.is_directed() {
83 for ix in self.graph.neighbors_directed(from, Incoming) {
84 if self.ins[self.graph.to_index(ix)] == 0 {
85 self.ins[self.graph.to_index(ix)] = self.generation;
86 self.ins_size += 1;
87 }
88 }
89 }
90 }
91
92 pub fn pop_mapping(&mut self, from: G::NodeId) {
94 self.mapping[self.graph.to_index(from)] = usize::MAX;
96
97 for ix in self.graph.neighbors_directed(from, Outgoing) {
99 if self.out[self.graph.to_index(ix)] == self.generation {
100 self.out[self.graph.to_index(ix)] = 0;
101 self.out_size -= 1;
102 }
103 }
104 if self.graph.is_directed() {
105 for ix in self.graph.neighbors_directed(from, Incoming) {
106 if self.ins[self.graph.to_index(ix)] == self.generation {
107 self.ins[self.graph.to_index(ix)] = 0;
108 self.ins_size -= 1;
109 }
110 }
111 }
112
113 self.generation -= 1;
114 }
115
116 pub fn next_out_index(&self, from_index: usize) -> Option<usize> {
118 self.out[from_index..]
119 .iter()
120 .enumerate()
121 .find(move |&(index, &elt)| {
122 elt > 0 && self.mapping[from_index + index] == usize::MAX
123 })
124 .map(|(index, _)| index)
125 }
126
127 pub fn next_in_index(&self, from_index: usize) -> Option<usize> {
129 if !self.graph.is_directed() {
130 return None;
131 }
132 self.ins[from_index..]
133 .iter()
134 .enumerate()
135 .find(move |&(index, &elt)| {
136 elt > 0 && self.mapping[from_index + index] == usize::MAX
137 })
138 .map(|(index, _)| index)
139 }
140
141 pub fn next_rest_index(&self, from_index: usize) -> Option<usize> {
143 self.mapping[from_index..]
144 .iter()
145 .enumerate()
146 .find(|&(_, &elt)| elt == usize::MAX)
147 .map(|(index, _)| index)
148 }
149 }
150}
151
152mod semantic {
153 use super::*;
154
155 pub struct NoSemanticMatch;
156
157 pub trait NodeMatcher<G0: GraphBase, G1: GraphBase> {
158 fn enabled() -> bool;
159 fn eq(&mut self, _g0: &G0, _g1: &G1, _n0: G0::NodeId, _n1: G1::NodeId) -> bool;
160 }
161
162 impl<G0: GraphBase, G1: GraphBase> NodeMatcher<G0, G1> for NoSemanticMatch {
163 #[inline]
164 fn enabled() -> bool {
165 false
166 }
167 #[inline]
168 fn eq(&mut self, _g0: &G0, _g1: &G1, _n0: G0::NodeId, _n1: G1::NodeId) -> bool {
169 true
170 }
171 }
172
173 impl<G0, G1, F> NodeMatcher<G0, G1> for F
174 where
175 G0: GraphBase + DataMap,
176 G1: GraphBase + DataMap,
177 F: FnMut(&G0::NodeWeight, &G1::NodeWeight) -> bool,
178 {
179 #[inline]
180 fn enabled() -> bool {
181 true
182 }
183 #[inline]
184 fn eq(&mut self, g0: &G0, g1: &G1, n0: G0::NodeId, n1: G1::NodeId) -> bool {
185 if let (Some(x), Some(y)) = (g0.node_weight(n0), g1.node_weight(n1)) {
186 self(x, y)
187 } else {
188 false
189 }
190 }
191 }
192
193 pub trait EdgeMatcher<G0: GraphBase, G1: GraphBase> {
194 fn enabled() -> bool;
195 fn eq(
196 &mut self,
197 _g0: &G0,
198 _g1: &G1,
199 e0: (G0::NodeId, G0::NodeId),
200 e1: (G1::NodeId, G1::NodeId),
201 ) -> bool;
202 }
203
204 impl<G0: GraphBase, G1: GraphBase> EdgeMatcher<G0, G1> for NoSemanticMatch {
205 #[inline]
206 fn enabled() -> bool {
207 false
208 }
209 #[inline]
210 fn eq(
211 &mut self,
212 _g0: &G0,
213 _g1: &G1,
214 _e0: (G0::NodeId, G0::NodeId),
215 _e1: (G1::NodeId, G1::NodeId),
216 ) -> bool {
217 true
218 }
219 }
220
221 impl<G0, G1, F> EdgeMatcher<G0, G1> for F
222 where
223 G0: GraphBase + DataMap + IntoEdgesDirected,
224 G1: GraphBase + DataMap + IntoEdgesDirected,
225 F: FnMut(&G0::EdgeWeight, &G1::EdgeWeight) -> bool,
226 {
227 #[inline]
228 fn enabled() -> bool {
229 true
230 }
231 #[inline]
232 fn eq(
233 &mut self,
234 g0: &G0,
235 g1: &G1,
236 e0: (G0::NodeId, G0::NodeId),
237 e1: (G1::NodeId, G1::NodeId),
238 ) -> bool {
239 let w0 = g0
240 .edges_directed(e0.0, Outgoing)
241 .find(|edge| edge.target() == e0.1)
242 .and_then(|edge| g0.edge_weight(edge.id()));
243 let w1 = g1
244 .edges_directed(e1.0, Outgoing)
245 .find(|edge| edge.target() == e1.1)
246 .and_then(|edge| g1.edge_weight(edge.id()));
247 if let (Some(x), Some(y)) = (w0, w1) {
248 self(x, y)
249 } else {
250 false
251 }
252 }
253 }
254}
255
256mod matching {
257 use super::*;
258
259 #[derive(Copy, Clone, PartialEq, Debug)]
260 enum OpenList {
261 Out,
262 In,
263 Other,
264 }
265
266 #[derive(Clone, PartialEq, Debug)]
267 enum Frame<G0, G1>
268 where
269 G0: GraphBase,
270 G1: GraphBase,
271 {
272 Outer,
273 Inner {
274 nodes: (G0::NodeId, G1::NodeId),
275 open_list: OpenList,
276 },
277 Unwind {
278 nodes: (G0::NodeId, G1::NodeId),
279 open_list: OpenList,
280 },
281 }
282
283 fn is_feasible<G0, G1, NM, EM>(
284 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
285 nodes: (G0::NodeId, G1::NodeId),
286 node_match: &mut NM,
287 edge_match: &mut EM,
288 ) -> bool
289 where
290 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
291 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
292 NM: NodeMatcher<G0, G1>,
293 EM: EdgeMatcher<G0, G1>,
294 {
295 macro_rules! field {
296 ($x:ident, 0) => {
297 $x.0
298 };
299 ($x:ident, 1) => {
300 $x.1
301 };
302 ($x:ident, 1 - 0) => {
303 $x.1
304 };
305 ($x:ident, 1 - 1) => {
306 $x.0
307 };
308 }
309
310 macro_rules! r_succ {
311 ($j:tt) => {{
312 let mut succ_count = 0;
313 for n_neigh in field!(st, $j)
314 .graph
315 .neighbors_directed(field!(nodes, $j), Outgoing)
316 {
317 succ_count += 1;
318 let m_neigh = if field!(nodes, $j) != n_neigh {
320 field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)]
321 } else {
322 field!(st, 1 - $j).graph.to_index(field!(nodes, 1 - $j))
323 };
324 if m_neigh == usize::MAX {
325 continue;
326 }
327 let has_edge = field!(st, 1 - $j).graph.is_adjacent(
328 &field!(st, 1 - $j).adjacency_matrix,
329 field!(nodes, 1 - $j),
330 field!(st, 1 - $j).graph.from_index(m_neigh),
331 );
332 if !has_edge {
333 return false;
334 }
335 }
336 succ_count
337 }};
338 }
339
340 macro_rules! r_pred {
341 ($j:tt) => {{
342 let mut pred_count = 0;
343 for n_neigh in field!(st, $j)
344 .graph
345 .neighbors_directed(field!(nodes, $j), Incoming)
346 {
347 pred_count += 1;
348 let m_neigh = field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)];
350 if m_neigh == usize::MAX {
351 continue;
352 }
353 let has_edge = field!(st, 1 - $j).graph.is_adjacent(
354 &field!(st, 1 - $j).adjacency_matrix,
355 field!(st, 1 - $j).graph.from_index(m_neigh),
356 field!(nodes, 1 - $j),
357 );
358 if !has_edge {
359 return false;
360 }
361 }
362 pred_count
363 }};
364 }
365
366 if r_succ!(0) > r_succ!(1) {
384 return false;
385 }
386 if st.0.graph.is_directed() {
388 if r_pred!(0) > r_pred!(1) {
389 return false;
390 }
391 }
392
393 if NM::enabled() {
395 if !node_match.eq(st.0.graph, st.1.graph, nodes.0, nodes.1) {
396 return false;
397 }
398 }
399 if EM::enabled() {
401 macro_rules! edge_feasibility {
402 ($j:tt) => {{
403 for n_neigh in field!(st, $j)
404 .graph
405 .neighbors_directed(field!(nodes, $j), Outgoing)
406 {
407 let m_neigh = if field!(nodes, $j) != n_neigh {
408 field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)]
409 } else {
410 field!(st, 1 - $j).graph.to_index(field!(nodes, 1 - $j))
411 };
412 if m_neigh == usize::MAX {
413 continue;
414 }
415
416 let e0 = (field!(nodes, $j), n_neigh);
417 let e1 = (
418 field!(nodes, 1 - $j),
419 field!(st, 1 - $j).graph.from_index(m_neigh),
420 );
421 let edges = (e0, e1);
422 if !edge_match.eq(
423 st.0.graph,
424 st.1.graph,
425 field!(edges, $j),
426 field!(edges, 1 - $j),
427 ) {
428 return false;
429 }
430 }
431 if field!(st, $j).graph.is_directed() {
432 for n_neigh in field!(st, $j)
433 .graph
434 .neighbors_directed(field!(nodes, $j), Incoming)
435 {
436 let m_neigh =
438 field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)];
439 if m_neigh == usize::MAX {
440 continue;
441 }
442
443 let e0 = (n_neigh, field!(nodes, $j));
444 let e1 = (
445 field!(st, 1 - $j).graph.from_index(m_neigh),
446 field!(nodes, 1 - $j),
447 );
448 let edges = (e0, e1);
449 if !edge_match.eq(
450 st.0.graph,
451 st.1.graph,
452 field!(edges, $j),
453 field!(edges, 1 - $j),
454 ) {
455 return false;
456 }
457 }
458 }
459 }};
460 }
461
462 edge_feasibility!(0);
463 edge_feasibility!(1);
464 }
465 true
466 }
467
468 fn next_candidate<G0, G1>(
469 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
470 ) -> Option<(G0::NodeId, G1::NodeId, OpenList)>
471 where
472 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
473 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
474 {
475 let mut from_index = None;
476 let mut open_list = OpenList::Out;
477 let mut to_index = st.1.next_out_index(0);
478
479 if to_index.is_some() {
481 from_index = st.0.next_out_index(0);
482 open_list = OpenList::Out;
483 }
484 if to_index.is_none() || from_index.is_none() {
486 to_index = st.1.next_in_index(0);
487
488 if to_index.is_some() {
489 from_index = st.0.next_in_index(0);
490 open_list = OpenList::In;
491 }
492 }
493 if to_index.is_none() || from_index.is_none() {
495 to_index = st.1.next_rest_index(0);
496 if to_index.is_some() {
497 from_index = st.0.next_rest_index(0);
498 open_list = OpenList::Other;
499 }
500 }
501 match (from_index, to_index) {
502 (Some(n), Some(m)) => Some((
503 st.0.graph.from_index(n),
504 st.1.graph.from_index(m),
505 open_list,
506 )),
507 _ => None,
509 }
510 }
511
512 fn next_from_ix<G0, G1>(
513 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
514 nx: G0::NodeId,
515 open_list: OpenList,
516 ) -> Option<G0::NodeId>
517 where
518 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
519 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
520 {
521 let start = st.0.graph.to_index(nx) + 1;
523 let cand0 = match open_list {
524 OpenList::Out => st.0.next_out_index(start),
525 OpenList::In => st.0.next_in_index(start),
526 OpenList::Other => st.0.next_rest_index(start),
527 }
528 .map(|c| c + start); match cand0 {
530 None => None, Some(ix) => {
532 debug_assert!(ix >= start);
533 Some(st.0.graph.from_index(ix))
534 }
535 }
536 }
537
538 fn pop_state<G0, G1>(
539 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
540 nodes: (G0::NodeId, G1::NodeId),
541 ) where
542 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
543 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
544 {
545 st.0.pop_mapping(nodes.0);
546 st.1.pop_mapping(nodes.1);
547 }
548
549 fn push_state<G0, G1>(
550 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
551 nodes: (G0::NodeId, G1::NodeId),
552 ) where
553 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
554 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
555 {
556 st.0.push_mapping(nodes.0, st.1.graph.to_index(nodes.1));
557 st.1.push_mapping(nodes.1, st.0.graph.to_index(nodes.0));
558 }
559
560 pub fn try_match<G0, G1, NM, EM>(
562 mut st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
563 node_match: &mut NM,
564 edge_match: &mut EM,
565 ) -> Option<bool>
566 where
567 G0: NodeCompactIndexable
568 + EdgeCount
569 + GetAdjacencyMatrix
570 + GraphProp
571 + IntoNeighborsDirected,
572 G1: NodeCompactIndexable
573 + EdgeCount
574 + GetAdjacencyMatrix
575 + GraphProp
576 + IntoNeighborsDirected,
577 NM: NodeMatcher<G0, G1>,
578 EM: EdgeMatcher<G0, G1>,
579 {
580 if st.0.is_complete() {
581 return Some(true);
582 }
583
584 let mut stack: Vec<Frame<G0, G1>> = vec![Frame::Outer];
588 while let Some(frame) = stack.pop() {
589 match frame {
590 Frame::Unwind { nodes, open_list } => {
591 pop_state(&mut st, nodes);
592
593 match next_from_ix(&mut st, nodes.0, open_list) {
594 None => continue,
595 Some(nx) => {
596 let f = Frame::Inner {
597 nodes: (nx, nodes.1),
598 open_list,
599 };
600 stack.push(f);
601 }
602 }
603 }
604 Frame::Outer => match next_candidate(&mut st) {
605 None => continue,
606 Some((nx, mx, open_list)) => {
607 let f = Frame::Inner {
608 nodes: (nx, mx),
609 open_list,
610 };
611 stack.push(f);
612 }
613 },
614 Frame::Inner { nodes, open_list } => {
615 if is_feasible(&mut st, nodes, node_match, edge_match) {
616 push_state(&mut st, nodes);
617 if st.0.is_complete() {
618 return Some(true);
619 }
620 if st.0.out_size == st.1.out_size && st.0.ins_size == st.1.ins_size {
622 let f0 = Frame::Unwind { nodes, open_list };
623 stack.push(f0);
624 stack.push(Frame::Outer);
625 continue;
626 }
627 pop_state(&mut st, nodes);
628 }
629 match next_from_ix(&mut st, nodes.0, open_list) {
630 None => continue,
631 Some(nx) => {
632 let f = Frame::Inner {
633 nodes: (nx, nodes.1),
634 open_list,
635 };
636 stack.push(f);
637 }
638 }
639 }
640 }
641 }
642 None
643 }
644}
645
646pub fn is_isomorphic<G0, G1>(g0: G0, g1: G1) -> bool
658where
659 G0: NodeCompactIndexable + EdgeCount + GetAdjacencyMatrix + GraphProp + IntoNeighborsDirected,
660 G1: NodeCompactIndexable
661 + EdgeCount
662 + GetAdjacencyMatrix
663 + GraphProp<EdgeType = G0::EdgeType>
664 + IntoNeighborsDirected,
665{
666 if g0.node_count() != g1.node_count() || g0.edge_count() != g1.edge_count() {
667 return false;
668 }
669
670 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
671 self::matching::try_match(&mut st, &mut NoSemanticMatch, &mut NoSemanticMatch).unwrap_or(false)
672}
673
674pub fn is_isomorphic_matching<G0, G1, NM, EM>(
681 g0: G0,
682 g1: G1,
683 mut node_match: NM,
684 mut edge_match: EM,
685) -> bool
686where
687 G0: NodeCompactIndexable
688 + EdgeCount
689 + DataMap
690 + GetAdjacencyMatrix
691 + GraphProp
692 + IntoEdgesDirected,
693 G1: NodeCompactIndexable
694 + EdgeCount
695 + DataMap
696 + GetAdjacencyMatrix
697 + GraphProp<EdgeType = G0::EdgeType>
698 + IntoEdgesDirected,
699 NM: FnMut(&G0::NodeWeight, &G1::NodeWeight) -> bool,
700 EM: FnMut(&G0::EdgeWeight, &G1::EdgeWeight) -> bool,
701{
702 if g0.node_count() != g1.node_count() || g0.edge_count() != g1.edge_count() {
703 return false;
704 }
705
706 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
707 self::matching::try_match(&mut st, &mut node_match, &mut edge_match).unwrap_or(false)
708}
709
710pub fn is_isomorphic_subgraph<G0, G1>(g0: G0, g1: G1) -> bool
744where
745 G0: NodeCompactIndexable + EdgeCount + GetAdjacencyMatrix + GraphProp + IntoNeighborsDirected,
746 G1: NodeCompactIndexable
747 + EdgeCount
748 + GetAdjacencyMatrix
749 + GraphProp<EdgeType = G0::EdgeType>
750 + IntoNeighborsDirected,
751{
752 if g0.node_count() > g1.node_count() || g0.edge_count() > g1.edge_count() {
753 return false;
754 }
755
756 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
757 self::matching::try_match(&mut st, &mut NoSemanticMatch, &mut NoSemanticMatch).unwrap_or(false)
758}
759
760pub fn is_isomorphic_subgraph_matching<G0, G1, NM, EM>(
767 g0: G0,
768 g1: G1,
769 mut node_match: NM,
770 mut edge_match: EM,
771) -> bool
772where
773 G0: NodeCompactIndexable
774 + EdgeCount
775 + DataMap
776 + GetAdjacencyMatrix
777 + GraphProp
778 + IntoEdgesDirected,
779 G1: NodeCompactIndexable
780 + EdgeCount
781 + DataMap
782 + GetAdjacencyMatrix
783 + GraphProp<EdgeType = G0::EdgeType>
784 + IntoEdgesDirected,
785 NM: FnMut(&G0::NodeWeight, &G1::NodeWeight) -> bool,
786 EM: FnMut(&G0::EdgeWeight, &G1::EdgeWeight) -> bool,
787{
788 if g0.node_count() > g1.node_count() || g0.edge_count() > g1.edge_count() {
789 return false;
790 }
791
792 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
793 self::matching::try_match(&mut st, &mut node_match, &mut edge_match).unwrap_or(false)
794}