petgraph/
isomorphism.rs

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    // TODO: make mapping generic over the index type of the other graph.
25    pub struct Vf2State<'a, G: GetAdjacencyMatrix> {
26        /// A reference to the graph this state was built from.
27        pub graph: &'a G,
28        /// The current mapping M(s) of nodes from G0 → G1 and G1 → G0,
29        /// `usize::MAX` for no mapping.
30        pub mapping: Vec<usize>,
31        /// out[i] is non-zero if i is in either M_0(s) or Tout_0(s)
32        /// These are all the next vertices that are not mapped yet, but
33        /// have an outgoing edge from the mapping.
34        out: Vec<usize>,
35        /// ins[i] is non-zero if i is in either M_0(s) or Tin_0(s)
36        /// These are all the incoming vertices, those not mapped yet, but
37        /// have an edge from them into the mapping.
38        /// Unused if graph is undirected -- it's identical with out in that case.
39        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        /// Return **true** if we have a complete mapping
65        pub fn is_complete(&self) -> bool {
66            self.generation == self.mapping.len()
67        }
68
69        /// Add mapping **from** <-> **to** to the state.
70        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            // update T0 & T1 ins/outs
74            // T0out: Node in G0 not in M0 but successor of a node in M0.
75            // st.out[0]: Node either in M0 or successor of M0
76            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        /// Restore the state to before the last added mapping
93        pub fn pop_mapping(&mut self, from: G::NodeId) {
94            // undo (n, m) mapping
95            self.mapping[self.graph.to_index(from)] = usize::MAX;
96
97            // unmark in ins and outs
98            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        /// Find the next (least) node in the Tout set.
117        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        /// Find the next (least) node in the Tin set.
128        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        /// Find the next (least) node in the N - M set.
142        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                    // handle the self loop case; it's not in the mapping (yet)
319                    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                    // the self loop case is handled in outgoing
349                    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        // Check syntactic feasibility of mapping by ensuring adjacencies
367        // of nx map to adjacencies of mx.
368        //
369        // nx == map to => mx
370        //
371        // R_succ
372        //
373        // Check that every neighbor of nx is mapped to a neighbor of mx,
374        // then check the reverse, from mx to nx. Check that they have the same
375        // count of edges.
376        //
377        // Note: We want to check the lookahead measures here if we can,
378        // R_out: Equal for G0, G1: Card(Succ(G, n) ^ Tout); for both Succ and Pred
379        // R_in: Same with Tin
380        // R_new: Equal for G0, G1: Ñ n Pred(G, n); both Succ and Pred,
381        //      Ñ is G0 - M - Tin - Tout
382        // last attempt to add these did not speed up any of the testcases
383        if r_succ!(0) > r_succ!(1) {
384            return false;
385        }
386        // R_pred
387        if st.0.graph.is_directed() {
388            if r_pred!(0) > r_pred!(1) {
389                return false;
390            }
391        }
392
393        // // semantic feasibility: compare associated data for nodes
394        if NM::enabled() {
395            if !node_match.eq(st.0.graph, st.1.graph, nodes.0, nodes.1) {
396                return false;
397            }
398        }
399        // semantic feasibility: compare associated data for edges
400        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                            // the self loop case is handled in outgoing
437                            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        // Try the out list
480        if to_index.is_some() {
481            from_index = st.0.next_out_index(0);
482            open_list = OpenList::Out;
483        }
484        // Try the in list
485        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        // Try the other list -- disconnected graph
494        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            // No more candidates
508            _ => 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        // Find the next node index to try on the `from` side of the mapping
522        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); // compensate for start offset.
529        match cand0 {
530            None => None, // no more candidates
531            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    /// Return Some(bool) if isomorphism is decided, else None.
561    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        // A "depth first" search of a valid mapping from graph 1 to graph 2
585        // F(s, n, m) -- evaluate state s and add mapping n <-> m
586        // Find least T1out node (in st.out[1] but not in M[1])
587        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                        // Check cardinalities of Tin, Tout sets
621                        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
646/// \[Generic\] Return `true` if the graphs `g0` and `g1` are isomorphic.
647///
648/// Using the VF2 algorithm, only matching graph syntactically (graph
649/// structure).
650///
651/// The graphs should not be multigraphs.
652///
653/// **Reference**
654///
655/// * Luigi P. Cordella, Pasquale Foggia, Carlo Sansone, Mario Vento;
656///   *A (Sub)Graph Isomorphism Algorithm for Matching Large Graphs*
657pub 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
674/// \[Generic\] Return `true` if the graphs `g0` and `g1` are isomorphic.
675///
676/// Using the VF2 algorithm, examining both syntactic and semantic
677/// graph isomorphism (graph structure and matching node and edge weights).
678///
679/// The graphs should not be multigraphs.
680pub 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
710/// \[Generic\] Return `true` if `g0` is isomorphic to a subgraph of `g1`.
711///
712/// Using the VF2 algorithm, only matching graph syntactically (graph
713/// structure).
714///
715/// The graphs should not be multigraphs.
716///
717/// # Subgraph isomorphism
718///
719/// (adapted from [`networkx` documentation](https://networkx.github.io/documentation/stable/reference/algorithms/isomorphism.vf2.html))
720///
721/// Graph theory literature can be ambiguous about the meaning of the above statement,
722/// and we seek to clarify it now.
723///
724/// In the VF2 literature, a mapping **M** is said to be a *graph-subgraph isomorphism*
725/// iff **M** is an isomorphism between **G2** and a subgraph of **G1**. Thus, to say
726/// that **G1** and **G2** are graph-subgraph isomorphic is to say that a subgraph of
727/// **G1** is isomorphic to **G2**.
728///
729/// Other literature uses the phrase ‘subgraph isomorphic’ as in
730/// ‘**G1** does not have a subgraph isomorphic to **G2**’. Another use is as an in adverb
731/// for isomorphic. Thus, to say that **G1** and **G2** are subgraph isomorphic is to say
732/// that a subgraph of **G1** is isomorphic to **G2**.
733///
734/// Finally, the term ‘subgraph’ can have multiple meanings. In this context,
735/// ‘subgraph’ always means a ‘node-induced subgraph’. Edge-induced subgraph
736/// isomorphisms are not directly supported. For subgraphs which are not
737/// induced, the term ‘monomorphism’ is preferred over ‘isomorphism’.
738///
739/// **Reference**
740///
741/// * Luigi P. Cordella, Pasquale Foggia, Carlo Sansone, Mario Vento;
742///   *A (Sub)Graph Isomorphism Algorithm for Matching Large Graphs*
743pub 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
760/// \[Generic\] Return `true` if `g0` is isomorphic to a subgraph of `g1`.
761///
762/// Using the VF2 algorithm, examining both syntactic and semantic
763/// graph isomorphism (graph structure and matching node and edge weights).
764///
765/// The graphs should not be multigraphs.
766pub 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}