1use std::convert::TryFrom;
2
3use crate::data::DataMap;
4use crate::visit::EdgeCount;
5use crate::visit::EdgeRef;
6use crate::visit::GetAdjacencyMatrix;
7use crate::visit::GraphBase;
8use crate::visit::GraphProp;
9use crate::visit::IntoEdgesDirected;
10use crate::visit::IntoNeighborsDirected;
11use crate::visit::NodeCompactIndexable;
12use crate::{Incoming, Outgoing};
13
14use self::semantic::EdgeMatcher;
15use self::semantic::NoSemanticMatch;
16use self::semantic::NodeMatcher;
17use self::state::Vf2State;
18
19mod state {
20 use super::*;
21
22 #[derive(Debug)]
23 pub struct Vf2State<'a, G: GetAdjacencyMatrix> {
25 pub graph: &'a G,
27 pub mapping: Vec<usize>,
30 out: Vec<usize>,
34 ins: Vec<usize>,
39 pub out_size: usize,
40 pub ins_size: usize,
41 pub adjacency_matrix: G::AdjMatrix,
42 generation: usize,
43 }
44
45 impl<'a, G> Vf2State<'a, G>
46 where
47 G: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
48 {
49 pub fn new(g: &'a G) -> Self {
50 let c0 = g.node_count();
51 Vf2State {
52 graph: g,
53 mapping: vec![std::usize::MAX; c0],
54 out: vec![0; c0],
55 ins: vec![0; c0 * (g.is_directed() as usize)],
56 out_size: 0,
57 ins_size: 0,
58 adjacency_matrix: g.adjacency_matrix(),
59 generation: 0,
60 }
61 }
62
63 pub fn is_complete(&self) -> bool {
65 self.generation == self.mapping.len()
66 }
67
68 pub fn push_mapping(&mut self, from: G::NodeId, to: usize) {
70 self.generation += 1;
71 self.mapping[self.graph.to_index(from)] = to;
72 for ix in self.graph.neighbors_directed(from, Outgoing) {
76 if self.out[self.graph.to_index(ix)] == 0 {
77 self.out[self.graph.to_index(ix)] = self.generation;
78 self.out_size += 1;
79 }
80 }
81 if self.graph.is_directed() {
82 for ix in self.graph.neighbors_directed(from, Incoming) {
83 if self.ins[self.graph.to_index(ix)] == 0 {
84 self.ins[self.graph.to_index(ix)] = self.generation;
85 self.ins_size += 1;
86 }
87 }
88 }
89 }
90
91 pub fn pop_mapping(&mut self, from: G::NodeId) {
93 self.mapping[self.graph.to_index(from)] = std::usize::MAX;
95
96 for ix in self.graph.neighbors_directed(from, Outgoing) {
98 if self.out[self.graph.to_index(ix)] == self.generation {
99 self.out[self.graph.to_index(ix)] = 0;
100 self.out_size -= 1;
101 }
102 }
103 if self.graph.is_directed() {
104 for ix in self.graph.neighbors_directed(from, Incoming) {
105 if self.ins[self.graph.to_index(ix)] == self.generation {
106 self.ins[self.graph.to_index(ix)] = 0;
107 self.ins_size -= 1;
108 }
109 }
110 }
111
112 self.generation -= 1;
113 }
114
115 pub fn next_out_index(&self, from_index: usize) -> Option<usize> {
117 self.out[from_index..]
118 .iter()
119 .enumerate()
120 .find(move |&(index, &elt)| {
121 elt > 0 && self.mapping[from_index + index] == std::usize::MAX
122 })
123 .map(|(index, _)| index)
124 }
125
126 pub fn next_in_index(&self, from_index: usize) -> Option<usize> {
128 if !self.graph.is_directed() {
129 return None;
130 }
131 self.ins[from_index..]
132 .iter()
133 .enumerate()
134 .find(move |&(index, &elt)| {
135 elt > 0 && self.mapping[from_index + index] == std::usize::MAX
136 })
137 .map(|(index, _)| index)
138 }
139
140 pub fn next_rest_index(&self, from_index: usize) -> Option<usize> {
142 self.mapping[from_index..]
143 .iter()
144 .enumerate()
145 .find(|&(_, &elt)| elt == std::usize::MAX)
146 .map(|(index, _)| index)
147 }
148 }
149}
150
151mod semantic {
152 use super::*;
153
154 pub struct NoSemanticMatch;
155
156 pub trait NodeMatcher<G0: GraphBase, G1: GraphBase> {
157 fn enabled() -> bool;
158 fn eq(&mut self, _g0: &G0, _g1: &G1, _n0: G0::NodeId, _n1: G1::NodeId) -> bool;
159 }
160
161 impl<G0: GraphBase, G1: GraphBase> NodeMatcher<G0, G1> for NoSemanticMatch {
162 #[inline]
163 fn enabled() -> bool {
164 false
165 }
166 #[inline]
167 fn eq(&mut self, _g0: &G0, _g1: &G1, _n0: G0::NodeId, _n1: G1::NodeId) -> bool {
168 true
169 }
170 }
171
172 impl<G0, G1, F> NodeMatcher<G0, G1> for F
173 where
174 G0: GraphBase + DataMap,
175 G1: GraphBase + DataMap,
176 F: FnMut(&G0::NodeWeight, &G1::NodeWeight) -> bool,
177 {
178 #[inline]
179 fn enabled() -> bool {
180 true
181 }
182 #[inline]
183 fn eq(&mut self, g0: &G0, g1: &G1, n0: G0::NodeId, n1: G1::NodeId) -> bool {
184 if let (Some(x), Some(y)) = (g0.node_weight(n0), g1.node_weight(n1)) {
185 self(x, y)
186 } else {
187 false
188 }
189 }
190 }
191
192 pub trait EdgeMatcher<G0: GraphBase, G1: GraphBase> {
193 fn enabled() -> bool;
194 fn eq(
195 &mut self,
196 _g0: &G0,
197 _g1: &G1,
198 e0: (G0::NodeId, G0::NodeId),
199 e1: (G1::NodeId, G1::NodeId),
200 ) -> bool;
201 }
202
203 impl<G0: GraphBase, G1: GraphBase> EdgeMatcher<G0, G1> for NoSemanticMatch {
204 #[inline]
205 fn enabled() -> bool {
206 false
207 }
208 #[inline]
209 fn eq(
210 &mut self,
211 _g0: &G0,
212 _g1: &G1,
213 _e0: (G0::NodeId, G0::NodeId),
214 _e1: (G1::NodeId, G1::NodeId),
215 ) -> bool {
216 true
217 }
218 }
219
220 impl<G0, G1, F> EdgeMatcher<G0, G1> for F
221 where
222 G0: GraphBase + DataMap + IntoEdgesDirected,
223 G1: GraphBase + DataMap + IntoEdgesDirected,
224 F: FnMut(&G0::EdgeWeight, &G1::EdgeWeight) -> bool,
225 {
226 #[inline]
227 fn enabled() -> bool {
228 true
229 }
230 #[inline]
231 fn eq(
232 &mut self,
233 g0: &G0,
234 g1: &G1,
235 e0: (G0::NodeId, G0::NodeId),
236 e1: (G1::NodeId, G1::NodeId),
237 ) -> bool {
238 let w0 = g0
239 .edges_directed(e0.0, Outgoing)
240 .find(|edge| edge.target() == e0.1)
241 .and_then(|edge| g0.edge_weight(edge.id()));
242 let w1 = g1
243 .edges_directed(e1.0, Outgoing)
244 .find(|edge| edge.target() == e1.1)
245 .and_then(|edge| g1.edge_weight(edge.id()));
246 if let (Some(x), Some(y)) = (w0, w1) {
247 self(x, y)
248 } else {
249 false
250 }
251 }
252 }
253}
254
255mod matching {
256 use super::*;
257
258 #[derive(Copy, Clone, PartialEq, Debug)]
259 enum OpenList {
260 Out,
261 In,
262 Other,
263 }
264
265 #[derive(Clone, PartialEq, Debug)]
266 enum Frame<G0, G1>
267 where
268 G0: GraphBase,
269 G1: GraphBase,
270 {
271 Outer,
272 Inner {
273 nodes: (G0::NodeId, G1::NodeId),
274 open_list: OpenList,
275 },
276 Unwind {
277 nodes: (G0::NodeId, G1::NodeId),
278 open_list: OpenList,
279 },
280 }
281
282 fn is_feasible<G0, G1, NM, EM>(
283 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
284 nodes: (G0::NodeId, G1::NodeId),
285 node_match: &mut NM,
286 edge_match: &mut EM,
287 ) -> bool
288 where
289 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
290 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
291 NM: NodeMatcher<G0, G1>,
292 EM: EdgeMatcher<G0, G1>,
293 {
294 macro_rules! field {
295 ($x:ident, 0) => {
296 $x.0
297 };
298 ($x:ident, 1) => {
299 $x.1
300 };
301 ($x:ident, 1 - 0) => {
302 $x.1
303 };
304 ($x:ident, 1 - 1) => {
305 $x.0
306 };
307 }
308
309 macro_rules! r_succ {
310 ($j:tt) => {{
311 let mut succ_count = 0;
312 for n_neigh in field!(st, $j)
313 .graph
314 .neighbors_directed(field!(nodes, $j), Outgoing)
315 {
316 succ_count += 1;
317 let m_neigh = if field!(nodes, $j) != n_neigh {
319 field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)]
320 } else {
321 field!(st, 1 - $j).graph.to_index(field!(nodes, 1 - $j))
322 };
323 if m_neigh == std::usize::MAX {
324 continue;
325 }
326 let has_edge = field!(st, 1 - $j).graph.is_adjacent(
327 &field!(st, 1 - $j).adjacency_matrix,
328 field!(nodes, 1 - $j),
329 field!(st, 1 - $j).graph.from_index(m_neigh),
330 );
331 if !has_edge {
332 return false;
333 }
334 }
335 succ_count
336 }};
337 }
338
339 macro_rules! r_pred {
340 ($j:tt) => {{
341 let mut pred_count = 0;
342 for n_neigh in field!(st, $j)
343 .graph
344 .neighbors_directed(field!(nodes, $j), Incoming)
345 {
346 pred_count += 1;
347 let m_neigh = field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)];
349 if m_neigh == std::usize::MAX {
350 continue;
351 }
352 let has_edge = field!(st, 1 - $j).graph.is_adjacent(
353 &field!(st, 1 - $j).adjacency_matrix,
354 field!(st, 1 - $j).graph.from_index(m_neigh),
355 field!(nodes, 1 - $j),
356 );
357 if !has_edge {
358 return false;
359 }
360 }
361 pred_count
362 }};
363 }
364
365 if r_succ!(0) > r_succ!(1) {
383 return false;
384 }
385 if st.0.graph.is_directed() && r_pred!(0) > r_pred!(1) {
387 return false;
388 }
389
390 if NM::enabled() && !node_match.eq(st.0.graph, st.1.graph, nodes.0, nodes.1) {
392 return false;
393 }
394 if EM::enabled() {
396 macro_rules! edge_feasibility {
397 ($j:tt) => {{
398 for n_neigh in field!(st, $j)
399 .graph
400 .neighbors_directed(field!(nodes, $j), Outgoing)
401 {
402 let m_neigh = if field!(nodes, $j) != n_neigh {
403 field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)]
404 } else {
405 field!(st, 1 - $j).graph.to_index(field!(nodes, 1 - $j))
406 };
407 if m_neigh == std::usize::MAX {
408 continue;
409 }
410
411 let e0 = (field!(nodes, $j), n_neigh);
412 let e1 = (
413 field!(nodes, 1 - $j),
414 field!(st, 1 - $j).graph.from_index(m_neigh),
415 );
416 let edges = (e0, e1);
417 if !edge_match.eq(
418 st.0.graph,
419 st.1.graph,
420 field!(edges, $j),
421 field!(edges, 1 - $j),
422 ) {
423 return false;
424 }
425 }
426 if field!(st, $j).graph.is_directed() {
427 for n_neigh in field!(st, $j)
428 .graph
429 .neighbors_directed(field!(nodes, $j), Incoming)
430 {
431 let m_neigh =
433 field!(st, $j).mapping[field!(st, $j).graph.to_index(n_neigh)];
434 if m_neigh == std::usize::MAX {
435 continue;
436 }
437
438 let e0 = (n_neigh, field!(nodes, $j));
439 let e1 = (
440 field!(st, 1 - $j).graph.from_index(m_neigh),
441 field!(nodes, 1 - $j),
442 );
443 let edges = (e0, e1);
444 if !edge_match.eq(
445 st.0.graph,
446 st.1.graph,
447 field!(edges, $j),
448 field!(edges, 1 - $j),
449 ) {
450 return false;
451 }
452 }
453 }
454 }};
455 }
456
457 edge_feasibility!(0);
458 edge_feasibility!(1);
459 }
460 true
461 }
462
463 fn next_candidate<G0, G1>(
464 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
465 ) -> Option<(G0::NodeId, G1::NodeId, OpenList)>
466 where
467 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
468 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
469 {
470 let mut from_index = None;
471 let mut open_list = OpenList::Out;
472 let mut to_index = st.1.next_out_index(0);
473
474 if to_index.is_some() {
476 from_index = st.0.next_out_index(0);
477 open_list = OpenList::Out;
478 }
479 if to_index.is_none() || from_index.is_none() {
481 to_index = st.1.next_in_index(0);
482
483 if to_index.is_some() {
484 from_index = st.0.next_in_index(0);
485 open_list = OpenList::In;
486 }
487 }
488 if to_index.is_none() || from_index.is_none() {
490 to_index = st.1.next_rest_index(0);
491 if to_index.is_some() {
492 from_index = st.0.next_rest_index(0);
493 open_list = OpenList::Other;
494 }
495 }
496 match (from_index, to_index) {
497 (Some(n), Some(m)) => Some((
498 st.0.graph.from_index(n),
499 st.1.graph.from_index(m),
500 open_list,
501 )),
502 _ => None,
504 }
505 }
506
507 fn next_from_ix<G0, G1>(
508 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
509 nx: G1::NodeId,
510 open_list: OpenList,
511 ) -> Option<G1::NodeId>
512 where
513 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
514 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
515 {
516 let start = st.1.graph.to_index(nx) + 1;
518 let cand1 = match open_list {
519 OpenList::Out => st.1.next_out_index(start),
520 OpenList::In => st.1.next_in_index(start),
521 OpenList::Other => st.1.next_rest_index(start),
522 }
523 .map(|c| c + start); match cand1 {
525 None => None, Some(ix) => {
527 debug_assert!(ix >= start);
528 Some(st.1.graph.from_index(ix))
529 }
530 }
531 }
532
533 fn pop_state<G0, G1>(
534 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
535 nodes: (G0::NodeId, G1::NodeId),
536 ) where
537 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
538 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
539 {
540 st.0.pop_mapping(nodes.0);
541 st.1.pop_mapping(nodes.1);
542 }
543
544 fn push_state<G0, G1>(
545 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
546 nodes: (G0::NodeId, G1::NodeId),
547 ) where
548 G0: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
549 G1: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
550 {
551 st.0.push_mapping(nodes.0, st.1.graph.to_index(nodes.1));
552 st.1.push_mapping(nodes.1, st.0.graph.to_index(nodes.0));
553 }
554
555 pub fn try_match<G0, G1, NM, EM>(
557 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
558 node_match: &mut NM,
559 edge_match: &mut EM,
560 match_subgraph: bool,
561 ) -> Option<bool>
562 where
563 G0: NodeCompactIndexable
564 + EdgeCount
565 + GetAdjacencyMatrix
566 + GraphProp
567 + IntoNeighborsDirected,
568 G1: NodeCompactIndexable
569 + EdgeCount
570 + GetAdjacencyMatrix
571 + GraphProp
572 + IntoNeighborsDirected,
573 NM: NodeMatcher<G0, G1>,
574 EM: EdgeMatcher<G0, G1>,
575 {
576 let mut stack = vec![Frame::Outer];
577 if isomorphisms(st, node_match, edge_match, match_subgraph, &mut stack).is_some() {
578 Some(true)
579 } else {
580 None
581 }
582 }
583
584 fn isomorphisms<G0, G1, NM, EM>(
585 st: &mut (Vf2State<'_, G0>, Vf2State<'_, G1>),
586 node_match: &mut NM,
587 edge_match: &mut EM,
588 match_subgraph: bool,
589 stack: &mut Vec<Frame<G0, G1>>,
590 ) -> Option<Vec<usize>>
591 where
592 G0: NodeCompactIndexable
593 + EdgeCount
594 + GetAdjacencyMatrix
595 + GraphProp
596 + IntoNeighborsDirected,
597 G1: NodeCompactIndexable
598 + EdgeCount
599 + GetAdjacencyMatrix
600 + GraphProp
601 + IntoNeighborsDirected,
602 NM: NodeMatcher<G0, G1>,
603 EM: EdgeMatcher<G0, G1>,
604 {
605 if st.0.is_complete() {
606 return Some(st.0.mapping.clone());
607 }
608
609 let mut result = None;
613 while let Some(frame) = stack.pop() {
614 match frame {
615 Frame::Unwind { nodes, open_list } => {
616 pop_state(st, nodes);
617
618 match next_from_ix(st, nodes.1, open_list) {
619 None => continue,
620 Some(nx) => {
621 let f = Frame::Inner {
622 nodes: (nodes.0, nx),
623 open_list,
624 };
625 stack.push(f);
626 }
627 }
628 }
629 Frame::Outer => match next_candidate(st) {
630 None => continue,
631 Some((nx, mx, open_list)) => {
632 let f = Frame::Inner {
633 nodes: (nx, mx),
634 open_list,
635 };
636 stack.push(f);
637 }
638 },
639 Frame::Inner { nodes, open_list } => {
640 if is_feasible(st, nodes, node_match, edge_match) {
641 push_state(st, nodes);
642 if st.0.is_complete() {
643 result = Some(st.0.mapping.clone());
644 }
645 if (!match_subgraph
647 && st.0.out_size == st.1.out_size
648 && st.0.ins_size == st.1.ins_size)
649 || (match_subgraph
650 && st.0.out_size <= st.1.out_size
651 && st.0.ins_size <= st.1.ins_size)
652 {
653 let f0 = Frame::Unwind { nodes, open_list };
654 stack.push(f0);
655 stack.push(Frame::Outer);
656 continue;
657 }
658 pop_state(st, nodes);
659 }
660 match next_from_ix(st, nodes.1, open_list) {
661 None => continue,
662 Some(nx) => {
663 let f = Frame::Inner {
664 nodes: (nodes.0, nx),
665 open_list,
666 };
667 stack.push(f);
668 }
669 }
670 }
671 }
672 if result.is_some() {
673 return result;
674 }
675 }
676 result
677 }
678
679 pub struct GraphMatcher<'a, 'b, 'c, G0, G1, NM, EM>
680 where
681 G0: NodeCompactIndexable
682 + EdgeCount
683 + GetAdjacencyMatrix
684 + GraphProp
685 + IntoNeighborsDirected,
686 G1: NodeCompactIndexable
687 + EdgeCount
688 + GetAdjacencyMatrix
689 + GraphProp
690 + IntoNeighborsDirected,
691 NM: NodeMatcher<G0, G1>,
692 EM: EdgeMatcher<G0, G1>,
693 {
694 st: (Vf2State<'a, G0>, Vf2State<'b, G1>),
695 node_match: &'c mut NM,
696 edge_match: &'c mut EM,
697 match_subgraph: bool,
698 stack: Vec<Frame<G0, G1>>,
699 }
700
701 impl<'a, 'b, 'c, G0, G1, NM, EM> GraphMatcher<'a, 'b, 'c, G0, G1, NM, EM>
702 where
703 G0: NodeCompactIndexable
704 + EdgeCount
705 + GetAdjacencyMatrix
706 + GraphProp
707 + IntoNeighborsDirected,
708 G1: NodeCompactIndexable
709 + EdgeCount
710 + GetAdjacencyMatrix
711 + GraphProp
712 + IntoNeighborsDirected,
713 NM: NodeMatcher<G0, G1>,
714 EM: EdgeMatcher<G0, G1>,
715 {
716 pub fn new(
717 g0: &'a G0,
718 g1: &'b G1,
719 node_match: &'c mut NM,
720 edge_match: &'c mut EM,
721 match_subgraph: bool,
722 ) -> Self {
723 let stack = vec![Frame::Outer];
724 Self {
725 st: (Vf2State::new(g0), Vf2State::new(g1)),
726 node_match,
727 edge_match,
728 match_subgraph,
729 stack,
730 }
731 }
732 }
733
734 impl<G0, G1, NM, EM> Iterator for GraphMatcher<'_, '_, '_, G0, G1, NM, EM>
735 where
736 G0: NodeCompactIndexable
737 + EdgeCount
738 + GetAdjacencyMatrix
739 + GraphProp
740 + IntoNeighborsDirected,
741 G1: NodeCompactIndexable
742 + EdgeCount
743 + GetAdjacencyMatrix
744 + GraphProp
745 + IntoNeighborsDirected,
746 NM: NodeMatcher<G0, G1>,
747 EM: EdgeMatcher<G0, G1>,
748 {
749 type Item = Vec<usize>;
750
751 fn next(&mut self) -> Option<Self::Item> {
752 isomorphisms(
753 &mut self.st,
754 self.node_match,
755 self.edge_match,
756 self.match_subgraph,
757 &mut self.stack,
758 )
759 }
760
761 fn size_hint(&self) -> (usize, Option<usize>) {
762 let n = self.st.0.graph.node_count();
766
767 let upper_bounds: Vec<Option<usize>> = [
770 1u64,
771 1,
772 2,
773 6,
774 24,
775 120,
776 720,
777 5040,
778 40320,
779 362880,
780 3628800,
781 39916800,
782 479001600,
783 6227020800,
784 87178291200,
785 1307674368000,
786 20922789888000,
787 355687428096000,
788 6402373705728000,
789 121645100408832000,
790 2432902008176640000,
791 ]
792 .iter()
793 .map(|n| usize::try_from(*n).ok())
794 .collect();
795
796 if n > upper_bounds.len() {
797 return (0, None);
798 }
799
800 (0, upper_bounds[n])
801 }
802 }
803}
804
805pub fn is_isomorphic<G0, G1>(g0: G0, g1: G1) -> bool
817where
818 G0: NodeCompactIndexable + EdgeCount + GetAdjacencyMatrix + GraphProp + IntoNeighborsDirected,
819 G1: NodeCompactIndexable
820 + EdgeCount
821 + GetAdjacencyMatrix
822 + GraphProp<EdgeType = G0::EdgeType>
823 + IntoNeighborsDirected,
824{
825 if g0.node_count() != g1.node_count() || g0.edge_count() != g1.edge_count() {
826 return false;
827 }
828
829 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
830 self::matching::try_match(&mut st, &mut NoSemanticMatch, &mut NoSemanticMatch, false)
831 .unwrap_or(false)
832}
833
834pub fn is_isomorphic_matching<G0, G1, NM, EM>(
841 g0: G0,
842 g1: G1,
843 mut node_match: NM,
844 mut edge_match: EM,
845) -> bool
846where
847 G0: NodeCompactIndexable
848 + EdgeCount
849 + DataMap
850 + GetAdjacencyMatrix
851 + GraphProp
852 + IntoEdgesDirected,
853 G1: NodeCompactIndexable
854 + EdgeCount
855 + DataMap
856 + GetAdjacencyMatrix
857 + GraphProp<EdgeType = G0::EdgeType>
858 + IntoEdgesDirected,
859 NM: FnMut(&G0::NodeWeight, &G1::NodeWeight) -> bool,
860 EM: FnMut(&G0::EdgeWeight, &G1::EdgeWeight) -> bool,
861{
862 if g0.node_count() != g1.node_count() || g0.edge_count() != g1.edge_count() {
863 return false;
864 }
865
866 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
867 self::matching::try_match(&mut st, &mut node_match, &mut edge_match, false).unwrap_or(false)
868}
869
870pub fn is_isomorphic_subgraph<G0, G1>(g0: G0, g1: G1) -> bool
904where
905 G0: NodeCompactIndexable + EdgeCount + GetAdjacencyMatrix + GraphProp + IntoNeighborsDirected,
906 G1: NodeCompactIndexable
907 + EdgeCount
908 + GetAdjacencyMatrix
909 + GraphProp<EdgeType = G0::EdgeType>
910 + IntoNeighborsDirected,
911{
912 if g0.node_count() > g1.node_count() || g0.edge_count() > g1.edge_count() {
913 return false;
914 }
915
916 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
917 self::matching::try_match(&mut st, &mut NoSemanticMatch, &mut NoSemanticMatch, true)
918 .unwrap_or(false)
919}
920
921pub fn is_isomorphic_subgraph_matching<G0, G1, NM, EM>(
928 g0: G0,
929 g1: G1,
930 mut node_match: NM,
931 mut edge_match: EM,
932) -> bool
933where
934 G0: NodeCompactIndexable
935 + EdgeCount
936 + DataMap
937 + GetAdjacencyMatrix
938 + GraphProp
939 + IntoEdgesDirected,
940 G1: NodeCompactIndexable
941 + EdgeCount
942 + DataMap
943 + GetAdjacencyMatrix
944 + GraphProp<EdgeType = G0::EdgeType>
945 + IntoEdgesDirected,
946 NM: FnMut(&G0::NodeWeight, &G1::NodeWeight) -> bool,
947 EM: FnMut(&G0::EdgeWeight, &G1::EdgeWeight) -> bool,
948{
949 if g0.node_count() > g1.node_count() || g0.edge_count() > g1.edge_count() {
950 return false;
951 }
952
953 let mut st = (Vf2State::new(&g0), Vf2State::new(&g1));
954 self::matching::try_match(&mut st, &mut node_match, &mut edge_match, true).unwrap_or(false)
955}
956
957pub fn subgraph_isomorphisms_iter<'a, G0, G1, NM, EM>(
964 g0: &'a G0,
965 g1: &'a G1,
966 node_match: &'a mut NM,
967 edge_match: &'a mut EM,
968) -> Option<impl Iterator<Item = Vec<usize>> + 'a>
969where
970 G0: 'a
971 + NodeCompactIndexable
972 + EdgeCount
973 + DataMap
974 + GetAdjacencyMatrix
975 + GraphProp
976 + IntoEdgesDirected,
977 G1: 'a
978 + NodeCompactIndexable
979 + EdgeCount
980 + DataMap
981 + GetAdjacencyMatrix
982 + GraphProp<EdgeType = G0::EdgeType>
983 + IntoEdgesDirected,
984 NM: 'a + FnMut(&G0::NodeWeight, &G1::NodeWeight) -> bool,
985 EM: 'a + FnMut(&G0::EdgeWeight, &G1::EdgeWeight) -> bool,
986{
987 if g0.node_count() > g1.node_count() || g0.edge_count() > g1.edge_count() {
988 return None;
989 }
990
991 Some(self::matching::GraphMatcher::new(
992 g0, g1, node_match, edge_match, true,
993 ))
994}