【问题标题】:Boost VF2 subgraph isomorphism incorrect for undirected graphs?对于无向图,Boost VF2 子图同构不正确?
【发布时间】:2021-02-15 00:31:28
【问题描述】:

我正在尝试使用 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, 3, smallGraph);
    }

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

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

        add_edge(0, 3, largeGraph);
        add_edge(1, 2, 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。

【问题讨论】:

    标签: c++ boost-graph


    【解决方案1】:

    子图同构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, 3, small);
    
        Graph large(500);
        add_edge(100, 200, large);
        add_edge(100, 300, large);
        add_edge(300, 400, large);
        add_edge(200, 400, large);
    
        // helper to recognizes (sets of) vertices from the intended subset
        auto all_in_subset = [set = std::set{100, 200, 300, 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
    

    【讨论】:

    • 在一个演示如何映射同态射的工作演示中简化并重新复杂化了示例
    • 您好,谢谢您的回答!你说得对,我期待的是 0-0、1-1、2-2、3-3 映射。似乎我误解了子图同构是什么——我将其解释为小图与大图的任何子图同构。这个属性有名字吗?而且这和你给出的定义的区别是映射顶点的边缘结构在同构图之间必须是相同的,对吗?
    • 我实际上并不认为存在差异。我认为“小图与大图的子图同构”暗示边缘结构必须匹配。因此,它不会有单独的名称。我也担心任何“不那么严格”的匹配都会变得毫无意义。
    • 从某种意义上说,它没有“得到明显的边缘子集”是很可悲的。然而,在许多意义上,同构映射是优越的[正如希望说明的随机生成边缘中的匹配一样]。实际上,该工具更擅长检测未知数据中的模式,而不是从数据中恢复已知子集。现在我想知道制作“面向边缘集的匹配算法”会有多困难(我不是数学家/拓扑学家,你可能知道)。
    • 我不太确定,如果您认为我的示例中的顶点集相同(即小图的顶点 1 = 大图的顶点 1),那么我觉得小图是大图的子图,因为顶点集是相同的,小图的边集是大图边集的子集。这就是我希望小图与大图同构的感觉。 -- 也许我应该寻找一个简单的子图算法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    相关资源
    最近更新 更多