Index: boost/graph/vf2_sub_graph_iso.hpp
===================================================================
--- boost/graph/vf2_sub_graph_iso.hpp (revision 86285)
+++ boost/graph/vf2_sub_graph_iso.hpp (working copy)
@@ -690,11 +690,13 @@
typedef vf2_match_continuation match_continuation_type;
std::vector k;
+ bool found_match = false;
recur:
if (s.success()) {
+ found_match = true;
if (!s.call_back(user_callback))
- return false;
+ return found_match;
goto back_track;
}
@@ -726,7 +728,7 @@
back_track:
if (k.empty())
- return true;
+ return found_match;
const match_continuation_type kk = k.back();
graph1_verts_iter = kk.graph1_verts_iter;
@@ -887,7 +889,7 @@
if (num_edges_small > num_edges_large)
return false;
- if ((num_vertices(graph_small) == 0) && (num_vertices(graph_large) == 0))
+ if (num_vertices(graph_small) == 0)
return true;
detail::state
This function finds all induced subgraph isomorphisms between
graphs graph_small and graph_large and outputs them to
- user_callback . It continues until user_callback
+ user_callback (except when graph_small is empty, see
+ Notes ). It continues until user_callback
returns false or the search space has been fully explored. vf2_subgraph_iso
returns true if a graph-subgraph isomorphism exists and false otherwise.
EdgeEquivalencePredicate and
@@ -294,6 +295,7 @@
to subgraphs of graph_large .
Note that, as opposed to vf2_subgraph_iso ,
these subgraphs need not to be induced subgraphs.
+ See Notes for information on the special case of empty graphs.
Both algorithms continues until user_callback
@@ -495,6 +497,13 @@
parallel edges, e.g. by using setS , the lookup function falls back
to edge() without performing any bookkeeping.
+
+ An empty graph is isomorphic to another empty graph, and thus vf2_graph_iso
+ will return true in this case. Similarly, an empty graph is subgraph isomorphic
+ to any other graph, so vf2_subgraph_iso and vf2_subgraph_mono will
+ return true if graph_small is empty.
+ However, the morphism will not be reported to user_callback in these cases.
+
Bibliography
Index: libs/graph/test/Jamfile.v2
===================================================================
--- libs/graph/test/Jamfile.v2 (revision 86285)
+++ libs/graph/test/Jamfile.v2 (working copy)
@@ -128,6 +128,7 @@
[ run stoer_wagner_test.cpp ../../test/build//boost_unit_test_framework/ static : $(TEST_DIR) ]
[ compile filtered_graph_properties_dijkstra.cpp ]
[ run vf2_sub_graph_iso_test.cpp ]
+ [ run vf2_sub_graph_iso_test_2.cpp ]
[ run hawick_circuits.cpp ]
[ run successive_shortest_path_nonnegative_weights_test.cpp ../../test/build//boost_unit_test_framework/ static ]
[ run cycle_canceling_test.cpp ../../test/build//boost_unit_test_framework/ static ]
Index: libs/graph/test/vf2_sub_graph_iso_test_2.cpp
===================================================================
--- libs/graph/test/vf2_sub_graph_iso_test_2.cpp (revision 0)
+++ libs/graph/test/vf2_sub_graph_iso_test_2.cpp (working copy)
@@ -0,0 +1,197 @@
+//=======================================================================
+// Boost.Graph library vf2_sub_graph_iso test 2
+// Test of return value and behaviour with empty graphs
+//
+// Copyright (C) 2013 Jakob Lykke Andersen, University of Southern Denmark (jlandersen@imada.sdu.dk)
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//=======================================================================
+
+#include
+#include
+#include
+#include
+
+struct test_callback {
+ test_callback(bool &got_hit, bool stop) : got_hit(got_hit), stop(stop) { }
+
+ template
+ bool operator()(Map1To2 map1to2, Map2To1 map2to1) {
+ got_hit = true;
+ return stop;
+ }
+
+ bool &got_hit;
+ bool stop;
+};
+
+struct false_predicate {
+ template
+ bool operator()(VertexOrEdge1 ve1, VertexOrEdge2 ve2) const {
+ return false;
+ }
+};
+
+void test_empty_graph_cases() {
+ typedef boost::adjacency_list Graph;
+ typedef boost::graph_traits::vertex_descriptor Vertex;
+ typedef boost::graph_traits::edge_descriptor Edge;
+ Graph gEmpty, gLarge;
+ add_vertex(gLarge);
+
+ { // isomorphism
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_graph_iso(gEmpty, gEmpty, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(!got_hit); // only non-empty matches are reported
+ }
+ { // subgraph isomorphism (induced)
+ { // empty vs. empty
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_iso(gEmpty, gEmpty, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(!got_hit); // only non-empty matches are reported
+ }
+ { // empty vs. non-empty
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_iso(gEmpty, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(!got_hit); // only non-empty matches are reported
+ }
+ }
+ { // subgraph monomorphism (non-induced subgraph isomorphism)
+ { // empty vs. empty
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_mono(gEmpty, gEmpty, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(!got_hit); // only non-empty matches are reported
+ }
+ { // empty vs. non-empty
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_mono(gEmpty, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(!got_hit); // only non-empty matches are reported
+ }
+ }
+}
+
+void test_return_value() {
+ typedef boost::adjacency_list Graph;
+ typedef boost::graph_traits::vertex_descriptor Vertex;
+ typedef boost::graph_traits::edge_descriptor Edge;
+ Graph gSmall, gLarge;
+ add_vertex(gSmall);
+ Vertex v1 = add_vertex(gLarge);
+ Vertex v2 = add_vertex(gLarge);
+ add_edge(v1, v2, gLarge);
+
+ { // isomorphism
+ { // no morphism due to sizes
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_graph_iso(gSmall, gLarge, callback);
+ BOOST_CHECK(!exists);
+ BOOST_CHECK(!got_hit);
+ }
+ { // no morphism due to vertex mismatches
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ false_predicate pred;
+ bool exists = vf2_graph_iso(gLarge, gLarge, callback, vertex_order_by_mult(gLarge),
+ boost::edges_equivalent(pred).vertices_equivalent(pred));
+ BOOST_CHECK(!exists);
+ BOOST_CHECK(!got_hit);
+ }
+ { // morphism, find all
+ bool got_hit = false;
+ test_callback callback(got_hit, false);
+ bool exists = vf2_graph_iso(gLarge, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(got_hit);
+ }
+ { // morphism, stop after first hit
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_graph_iso(gLarge, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(got_hit);
+ }
+ }
+ { // subgraph isomorphism (induced)
+ { // no morphism due to sizes
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_iso(gLarge, gSmall, callback);
+ BOOST_CHECK(!exists);
+ BOOST_CHECK(!got_hit);
+ }
+ { // no morphism due to vertex mismatches
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ false_predicate pred;
+ bool exists = vf2_subgraph_iso(gLarge, gLarge, callback, vertex_order_by_mult(gLarge),
+ boost::edges_equivalent(pred).vertices_equivalent(pred));
+ BOOST_CHECK(!exists);
+ BOOST_CHECK(!got_hit);
+ }
+ { // morphism, find all
+ bool got_hit = false;
+ test_callback callback(got_hit, false);
+ bool exists = vf2_subgraph_iso(gLarge, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(got_hit);
+ }
+ { // morphism, stop after first hit
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_iso(gLarge, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(got_hit);
+ }
+ }
+ { // subgraph monomorphism (non-induced subgraph isomorphism)
+ { // no morphism due to sizes
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_mono(gLarge, gSmall, callback);
+ BOOST_CHECK(!exists);
+ BOOST_CHECK(!got_hit);
+ }
+ { // no morphism due to vertex mismatches
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ false_predicate pred;
+ bool exists = vf2_subgraph_mono(gLarge, gLarge, callback, vertex_order_by_mult(gLarge),
+ boost::edges_equivalent(pred).vertices_equivalent(pred));
+ BOOST_CHECK(!exists);
+ BOOST_CHECK(!got_hit);
+ }
+ { // morphism, find all
+ bool got_hit = false;
+ test_callback callback(got_hit, false);
+ bool exists = vf2_subgraph_mono(gLarge, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(got_hit);
+ }
+ { // morphism, stop after first hit
+ bool got_hit = false;
+ test_callback callback(got_hit, true);
+ bool exists = vf2_subgraph_mono(gLarge, gLarge, callback);
+ BOOST_CHECK(exists);
+ BOOST_CHECK(got_hit);
+ }
+ }
+}
+
+int test_main(int argc, char* argv[]) {
+ test_empty_graph_cases();
+ test_return_value();
+ return 0;
+}