Boost Graph Library:具有 adjacency_matrix 的子图同构

Boost Graph Library: subgraph isomorphism with adjacency_matrix

提问人:Edward Doolittle 提问时间:8/20/2023 最后编辑:Edward Doolittle 更新时间:8/23/2023 访问量:59

问:

我正在将 Boost 1.83 与最新的 Cygwin 一起使用。我遇到的问题也发生在 Boost 1.66 上。

我正在尝试在adjacency_matrix上使用 Boost Graph Library 同构函数:

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

//typedef boost::adjacency_list<boost::vecS,
//                boost::vecS,
//                boost::bidirectionalS> Graph;

typedef boost::adjacency_matrix<boost::bidirectionalS> Graph;

int main(int argc, char* argv[]) {

  Graph g_small(3);

  boost::add_edge(0, 1, g_small);
  boost::add_edge(1, 2, g_small);
  boost::add_edge(2, 0, g_small);

  Graph g_large(5);

  boost::add_edge(0, 1, g_large);
  boost::add_edge(1, 2, g_large);
  boost::add_edge(2, 3, g_large);
  boost::add_edge(3, 4, g_large);
  boost::add_edge(4, 2, g_large);

  auto callback = [&](auto f, auto f_inv) {
    std::cout << "isomorphism" << std::endl;
    return true; // give us more isomorphisms if any
  };
  
  boost::vf2_subgraph_iso(g_small, g_large, callback);

  return 0;
}

代码使用邻接列表进行编译,但是使用邻接矩阵时,我收到以下错误(仅显示前几行)。然后它抱怨一些缺失的函数(我假设那些在双向模型中,而不是有向模型中的函数)。

                 from /usr/local/include/boost/container_hash/hash.hpp:27,
                 from /usr/local/include/boost/functional/hash.hpp:6,
                 from /usr/local/include/boost/unordered/unordered_set.hpp:20,
                 from /usr/local/include/boost/unordered_set.hpp:17,
                 from /usr/local/include/boost/graph/adjacency_list.hpp:20,
                 from mwe.cc:1:
/usr/local/include/boost/graph/adjacency_matrix.hpp: In instantiation of ‘class boost::adjacency_matrix<boost::bidirectionalS>’:
mwe.cc:14:16:   required from here
/usr/local/include/boost/graph/adjacency_matrix.hpp:455:5: error: static assertion failed: !(is_same< Directed, bidirectionalS >::value)
  455 |     BOOST_STATIC_ASSERT(!(is_same< Directed, bidirectionalS >::value));
      |     ^~~~~~~~~~~~~~~~~~~
/usr/local/include/boost/graph/adjacency_matrix.hpp:455:5: note: ‘!(bool)boost::integral_constant<bool, true>::value’ evaluates to false

我以为我理解了说它应该起作用的文档......我错过了什么吗?我不能使用adjacency_list因为子图同构在大型邻接列表图上似乎太慢了。

编辑:如果我在我发布的最小工作示例中更改 typedef

typedef boost::adjacency_matrix<boost::directedS> Graph;

我收到一个错误,似乎说子图函数期待双向,但它被定向了。请参阅下面的摘录。

/usr/local/include/boost/graph/graph_concepts.hpp:118:1:   required from ‘static void boost::concepts::requirement<boost::concepts::failed************ Model::************>::failed() [with Model = boost::concepts::BidirectionalGraphConcept<boost::adjacency_matrix<boost::directedS> >]’
/usr/local/include/boost/graph/vf2_sub_graph_iso.hpp:931:9:   required from ‘bool boost::detail::vf2_subgraph_morphism(const GraphSmall&, const GraphLarge&, SubGraphIsoMapCallback, IndexMapSmall, IndexMapLarge, const VertexOrderSmall&, EdgeEquivalencePredicate, VertexEquivalencePredicate)
C++ 提升 邻接矩阵 子图 同构

评论

0赞 n. m. could be an AI 8/20/2023
在你选择的编辑器中打开 /usr/local/include/boost/graph/adjacency_matrix.hpp,然后转到第 455 行。你在它周围看到了什么?
0赞 Edward Doolittle 8/20/2023
是的,我看到一条评论,大意是“使用 directedS 而不是 bidirectionalS”和“BOOST_STATIC_ASSERT(!(is_same< 定向,双向 S >::value));“。尝试过并得到一系列不同的错误消息,这些错误消息似乎是同构函数,反对不满足所需的双向概念。

答:

1赞 Edward Doolittle 8/21/2023 #1

因此,在深入研究了 Boost Graph Library (BGL) 头文件之后,我想我对正在发生的事情有了更好的了解。

Boost 不鼓励你,或者更确切地说,阻止你将双向 S 与 adjacency_matrix 一起使用,这显然是因为列表in_edge、列表out_edge、in_degree和out_degree函数违反了 BGL 双向图的恒定时间承诺。这是非常令人恼火的,因为adjacency_list图违反了adjacency_matrix提供的边缘访问的恒定时间承诺,但这并不妨碍您随时使用adjacency_list并接受任何权衡结果。

此外,adjacency_matrix中的一些功能仍未实现,这对于一个拥有 20 年历史的图书馆来说是相当令人担忧的。近 20 年来,BGL 的开发人员和维护人员似乎除了修复一些小错误之外没有做任何事情。考虑到当时对图数据结构和算法的所有研究,以及 C++ 中的所有变化,我会说 BGL 确实显示出它的年龄。这太糟糕了,因为我喜欢它的想法,我喜欢使用它(当它起作用时)。

在我的应用程序中,我不关心内存,只关心速度,而且我的大而密集的图形在初始创建后是固定的(顶点或边没有变化),所以我真的很想设置一种新的图形类型,它同时使用邻接矩阵和邻接列表,并选择最快的结构来响应任何查询(例如, 查询两个给定顶点之间是否存在边时的邻接矩阵,以及查询顶点的外边时的邻接列表)。然而,感觉你需要一个抽象代数的博士学位来建立一个新的图类型BGL。我夸大其词,但底线是,我可以更快地跟上替代方案的速度,而不是让 BGL 做我想做的事。

因此,我解决问题的方法是使用 LEMON 图形库。

评论

0赞 sehe 8/21/2023
呃。试图在这里打破咆哮。缺少您想象的使用会添加的哪些特定操作?我的看法是矩阵的边缘查找是 O(1),无论方向如何,并且由于您的图形是密集的,因此简单的迭代将替换为 ?adjacency_matrixbidirectionalSout_edgesin_edges
0赞 sehe 8/21/2023
重新阅读问题的评论,我想唯一的痛点是,由于技术原因(严格遵守运行时成本承诺),Matrix 没有对 BidirectionalGraph 进行建模,而您选择的同构搜索算法需要它。嗯,会明白告诉BGL“我知道,去做吧”有多难
0赞 sehe 8/21/2023
我发布了关于如何做到这一点的答案。我很想学习如何用 LEMON 做同样的事情。它看起来确实在更积极地发展。干杯
1赞 Edward Doolittle 8/22/2023
@sehe LEMON 肯定更新鲜,但缺少 Bron-Kerbosch 和子图同构查找器。我想我可以很容易地实现 Bron-Kerbosch 或其他集团查找器,并以更高的难度实现子图同构。现在我有三种方法可以继续我的研究:你上面的答案,LEMON,和格拉斯哥子图求解器(它只是从文件系统中读取图文件,但最终也可以修复)。再次感谢你。
1赞 sehe 8/21/2023 #2

我同意这种“清教徒”的限制,即您不得将 AdjacencyMatrix 用作(穷人的)BidirectionalGraph,即使只是为了玩弄/比较。

事实证明,说服图书馆“相信我,我知道我在做什么”是很容易的。您需要一种独特的图形类型,可以将特征专用于:

struct Graph : boost::adjacency_matrix<boost::directedS> {
    using Impl = boost::adjacency_matrix<boost::directedS>;
};

现在你有一个可以撒谎的类型:

namespace boost {
    template <> struct graph_traits<Graph> : graph_traits<Graph::Impl> {
        struct traversal_category
            : boost::bidirectional_graph_tag
            , Graph::Impl::traversal_category {};
    };

    // O(2N)
    auto degree(Graph::vertex_descriptor u, Graph const& g) {
        return size(out_edges(u, g)) + size(in_edges(u, g));
    }
}; // namespace boost

就这样:

在 Coliru 上直播

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

struct Graph : boost::adjacency_matrix<boost::directedS> {
    using Impl = boost::adjacency_matrix<boost::directedS>;
    // using Impl::Impl;
    // using Impl::operator=;
};

namespace boost {
    template <> struct graph_traits<Graph> : graph_traits<Graph::Impl> {
        struct traversal_category
            : boost::bidirectional_graph_tag
            , Graph::Impl::traversal_category {};
    };

    // O(2N)
    auto degree(Graph::vertex_descriptor u, Graph const& g) {
        return size(out_edges(u, g)) + size(in_edges(u, g));
    }
}; // namespace boost

int main() {
    Graph g_small(3);

    add_edge(0, 1, g_small);
    add_edge(1, 2, g_small);
    add_edge(2, 0, g_small);

    Graph g_large(5);

    add_edge(0, 1, g_large);
    add_edge(1, 2, g_large);
    add_edge(2, 3, g_large);
    add_edge(3, 4, g_large);
    add_edge(4, 2, g_large);

    auto callback = [&](auto f, auto /*f_inv*/) {
        std::cout << "isomorphism";
        for (auto v : boost::make_iterator_range(vertices(g_small))) {
            std::cout << " " << v << "->" << f[v];
        }
        std::cout << std::endl;
        return true; // give us more isomorphisms if any
    };

    vf2_subgraph_iso(g_small, g_large, callback);
}

打印输出

isomorphism 0->2 1->3 2->4
isomorphism 0->3 1->4 2->2
isomorphism 0->4 1->2 2->3

结束语

YMMV:当然,性能不会是最佳的,但正如你所说,大多数其他型号也不会。如果你能够预先计算/缓存每个顶点的度数,你可能会有你的蛋糕并吃掉它。根据您的域逻辑,这可能很容易添加。

评论

0赞 Edward Doolittle 8/22/2023
在你的度函数中,你不是说size(in_edges(u,g)) + size(out_edges(u,g))吗?
0赞 Edward Doolittle 8/23/2023
是的。。。错误仍然在第二个副本中,即完整的程序。:-(
0赞 sehe 8/23/2023
@EdwardDoolittle 和 Coliru 链接。这就是我尝试在手机上编辑所得到的。现在在所有三个位置都已修复。有趣的是,输出不受影响:)
1赞 Edward Doolittle 8/23/2023
我实施了你的建议。正如你所怀疑的那样,它运行得很慢:不仅是度函数,而且所有的边缘访问都是 O(N)。为了加快速度,所有这些功能也需要重新实现。有趣的是,格拉斯哥子图求解器是基于 Boost Graph Library 构建的,但运行速度比我的代码快 50,000 倍。所以我有工作要做......否则我会继续使用 GSS。:-)