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; +}