Boost VF2 子图同构对无向图不正确?

问题描述

我正在尝试使用 Boost 的 vf2_subgraph_iso,但我不知道如何正确地将它用于小型无向图

下面我展示了一个示例,其中具有 4 个顶点的图不是具有 4 个顶点的全连接图的子图(这对我来说似乎很奇怪)。谁能帮助我了解我在这里做错了什么?

我在 Why is Boost VF2 subgraph isomorphism giving an incorrect answer 看到过帖子,这可能是同样的问题。

#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <iostream>

#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <iostream>

using namespace boost;

int main()
{
    typedef adjacency_list<vecS,vecS,undirectedS,no_property> Graph;

    // 0---1
    // |   |
    // 2---3

    Graph smallGraph;
    {
        vertex(0,smallGraph);
        vertex(1,smallGraph);
        vertex(2,smallGraph);
        vertex(3,smallGraph);

        add_edge(0,1,smallGraph);
        add_edge(0,2,smallGraph);
        add_edge(2,3,smallGraph);
        add_edge(1,smallGraph);
    }

    Graph largeGraph;
    {
        vertex(0,largeGraph);
        vertex(1,largeGraph);
        vertex(2,largeGraph);
        vertex(3,largeGraph);

        add_edge(0,largeGraph);
        add_edge(0,largeGraph);
        add_edge(2,largeGraph);
        add_edge(1,largeGraph);
    }

    vf2_print_callback<Graph,Graph> callback(smallGraph,largeGraph);
    auto val = vf2_subgraph_iso(smallGraph,largeGraph,callback);

    if (val)
        std::cout << "Found subgraph." << std::endl;
    else
        std::cout << "Did not find subgraph." << std::endl;

    return 0;
}

vf2_subgraph_iso documentation,“两个图 G1=(V1,E1) 和 G2=(V2,E2) 之间的同构是一个图的顶点到另一个图的顶点的双射映射 M图的边结构。当且仅当 M 是 G1 和 G2 的子图之间的同构,M 被称为图-子图同构。"

在我上面的例子中,G2 = largeGraph,G1 = smallGraph,并且存在 G2 的一个子图,使得顶点映射 (0-0,1-1,2-2,3-3) 在子图之间是双射的和 G1。

解决方法

子图同构is defined here as

两个图 G1=(V1,E1) 和 G2=(V2,E2) 之间的同构是一个图的顶点到另一个图的顶点的双射映射 M,保留了图的边结构>

您希望保留什么映射?我想你可能一直期待 {0 -> 0,1 -> 1,2 -> 2,3 -> 3} 是那个双射顶点映射

然而,情况并非如此,因为子图缺少参与映射的顶点之间的边。例如。 3 -- 0 不在小图中,使其在结构上有所不同。

TL;DR 当边是严格子集时,子图同构不是。相反,它侧重于映射顶点集。

反例

这是一个反例,它在保留预期映射的同时生成一些随机边缘。请注意,如果给定足够多的随机边,您很可能会遇到意外的同构,因为子图在结构上如此简单:

Live On Wandbox

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <iostream>

#include <random>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/algorithm/cxx11/all_of.hpp>

using boost::make_iterator_range;
using boost::adaptors::transformed;
using boost::algorithm::all_of;

int main()
{
    using Graph =
        boost::adjacency_list<boost::vecS,boost::vecS,boost::undirectedS>;
    using V = Graph::vertex_descriptor;

    // 0---1
    // |   |
    // 2---3
    Graph small(4);
    add_edge(0,1,small);
    add_edge(0,2,small);
    add_edge(2,3,small);
    add_edge(1,small);

    Graph large(500);
    add_edge(100,200,large);
    add_edge(100,300,large);
    add_edge(300,400,large);
    add_edge(200,large);

    // helper to recognizes (sets of) vertices from the intended subset
    auto all_in_subset = [set = std::set{100,400}](auto... vs) {
        return ((0 != set.count(vs)) && ...);
    };

    // add some random edges for
    auto randvertex = std::bind(
        std::uniform_int_distribution<V>(0,boost::num_vertices(large) - 1),std::mt19937{std::random_device{}()});

    for (int n = 0; n < 400; ++n) {
        V src = randvertex();
        V tgt = randvertex();

        // only add edges outside of the subset - preserving structure
        if (!all_in_subset(src,tgt))
            add_edge(src,tgt,large);
    }

    auto callback = [&](auto bijection,auto) {
        auto vs = make_iterator_range(vertices(small));
        auto mapped =
            vs | transformed([&](auto v) { return get(bijection,v); });

        if (boost::algorithm::all_of(mapped,all_in_subset)) {
            std::cout << "In subset: ";
        } else {
            std::cout << "Random match: ";
        }

        for (auto v : vs) {
            std::cout << '(' << v << " -> " << get(bijection,v) << ") ";
        }
        std::cout << std::endl;
        return true;
    };
    bool found = boost::vf2_subgraph_iso(small,large,callback);

    std::cout << "Found subgraph:" << std::boolalpha << found << std::endl;
}

打印例如

In subset: (0 -> 100) (1 -> 200) (2 -> 300) (3 -> 400) 
In subset: (0 -> 100) (1 -> 300) (2 -> 200) (3 -> 400) 
Random match: (0 -> 122) (1 -> 268) (2 -> 454) (3 -> 128) 
Random match: (0 -> 122) (1 -> 454) (2 -> 268) (3 -> 128) 
Random match: (0 -> 128) (1 -> 268) (2 -> 454) (3 -> 122) 
Random match: (0 -> 128) (1 -> 454) (2 -> 268) (3 -> 122) 
In subset: (0 -> 200) (1 -> 100) (2 -> 400) (3 -> 300) 
In subset: (0 -> 200) (1 -> 400) (2 -> 100) (3 -> 300) 
Random match: (0 -> 220) (1 -> 234) (2 -> 367) (3 -> 340) 
Random match: (0 -> 220) (1 -> 367) (2 -> 234) (3 -> 340) 
Random match: (0 -> 234) (1 -> 220) (2 -> 340) (3 -> 367) 
Random match: (0 -> 234) (1 -> 340) (2 -> 220) (3 -> 367) 
Random match: (0 -> 268) (1 -> 122) (2 -> 128) (3 -> 454) 
Random match: (0 -> 268) (1 -> 128) (2 -> 122) (3 -> 454) 
In subset: (0 -> 300) (1 -> 100) (2 -> 400) (3 -> 200) 
In subset: (0 -> 300) (1 -> 400) (2 -> 100) (3 -> 200) 
Random match: (0 -> 340) (1 -> 234) (2 -> 367) (3 -> 220) 
Random match: (0 -> 340) (1 -> 367) (2 -> 234) (3 -> 220) 
Random match: (0 -> 367) (1 -> 220) (2 -> 340) (3 -> 234) 
Random match: (0 -> 367) (1 -> 340) (2 -> 220) (3 -> 234) 
In subset: (0 -> 400) (1 -> 200) (2 -> 300) (3 -> 100) 
In subset: (0 -> 400) (1 -> 300) (2 -> 200) (3 -> 100) 
Random match: (0 -> 454) (1 -> 122) (2 -> 128) (3 -> 268) 
Random match: (0 -> 454) (1 -> 128) (2 -> 122) (3 -> 268) 
Found subgraph:true

或者,取决于运气,只是预期:Live

In subset: (0 -> 100) (1 -> 200) (2 -> 300) (3 -> 400) 
In subset: (0 -> 100) (1 -> 300) (2 -> 200) (3 -> 400) 
In subset: (0 -> 200) (1 -> 100) (2 -> 400) (3 -> 300) 
In subset: (0 -> 200) (1 -> 400) (2 -> 100) (3 -> 300) 
In subset: (0 -> 300) (1 -> 100) (2 -> 400) (3 -> 200) 
In subset: (0 -> 300) (1 -> 400) (2 -> 100) (3 -> 200) 
In subset: (0 -> 400) (1 -> 200) (2 -> 300) (3 -> 100) 
In subset: (0 -> 400) (1 -> 300) (2 -> 200) (3 -> 100) 
Found subgraph:true