![](/img/trans.png)
[英]Boost.Graph library: how to use boost::is_isomorphism with named vertices
[英]How to configure boost::graph to use my own (stable) index for vertices?
最簡單的boost::adjacency_list
使用std::size_t
作為底層的 vertex_descriptor(索引)。
boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::directedS,
boost::no_property,
boost::no_property
> graph;
一旦我知道了頂點描述符,我就可以快速訪問所需的頂點。
auto vertex = graph[idx]; // idx is the veretx descriptor
但是,當圖發生變異時,不能保證vertex_decriptor
是穩定的。
auto v0 = boost::add_vertex(graph);
auto v1 = boost::add_vertex(graph);
auto v2 = boost::add_vertex(graph);
boost::remove_vertex(v0, graph); // v1 and v2 are no longer valid
我希望能夠快速找到一個特定的頂點 - 這意味着我希望避免遍歷圖形結構來搜索我知道存在的頂點。
我的想法是,我可以通過與 VertexListS 模板參數不同的選擇器以某種方式調整boost::adjacency_list
,這將允許我使用我自己提供的 vertex_descripor(索引)。
我探索了使用關聯容器選擇器(例如內置boost::hash_mapS
)的可能性,但似乎我無法控制它在調用add_vertex
時返回的確切 ID。 我希望能夠為每個頂點使用我自己的 id (vertex_descriptor)。
我會嘗試更清楚一點,使用我希望的代碼:
// the following type will serve as the vertex_descriptor:
using my_id_type = std::size_t;
struct MyVertex
{
my_id_type id;
// some other fields
}
// add vertices with unique identifiers that will also serve as the vertex_descriptors
boost::add_vertex(MyVertex{1111}, graph); // I expect this to return 1111
boost::add_vertex(MyVertex{2222}, graph);
boost::add_vertex(MyVertex{1234}, graph);
boost::remove_vertex(1111, graph); // I expect this to remove the first vertex
// access a specific vertex using its unique, stable descriptor:
auto vertex = graph[1234];
這可以使用 boost::graph 實現嗎?
這可以使用 boost::graph 實現嗎?
對於 BGL,答案幾乎總是“是”。 它是現存最深刻的通用庫設計之一。
令我驚訝的是,在adjacency_list
的類型層次結構中出現了一些新東西。 顯然,最近有一個named_graph
mixin(實際上是maybe_name_graph
),它使用頂點包上的特征來檢測“內部名稱”。
這意味着您可以靠近。 雖然頂點描述符不會成為您的 ID,但您可以進行 O(1) 查找。 而且接口有一些便利,所以你可以這樣寫:
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
筆記:
id
類型設置為與vertex_descriptor
類型相同(或者如果您的參數對兩者的轉換具有同樣“遠”的轉換),則會遇到問題(例如add_edge
的不明確重載)。vertex_index_t
或vertex_name_t
屬性。struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
到目前為止沒有驚喜。 我
other
只是為了顯示它何時/如何默認構造listS
因為它
vertex_descriptor
( void*
) 與重載決議中的 id 類型 ( size_t
) 不沖突接下來我們教 BGL 關於我們的內部頂點名稱。
這完全由
Vertex
bundle 參數化,因此請記住,使用相同 bundle 的不同圖形將使用相同的名稱特征。
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extract_name_type = typename internal_vertex_name<Vertex>::type;
using vertex_name_type = typename remove_cv<typename remove_reference<
typename extract_name_type::result_type>::type>::type;
public:
using argument_type = vertex_name_type;
using result_type = Vertex;
result_type operator()(const vertex_name_type& id) const {
return {id};
}
};
};
筆記
我們當然可以在第二個專業化中對已知信息進行硬編碼:
template <> struct boost::graph::internal_vertex_constructor<Vertex> { struct type { Vertex operator()(size_t id) const { return {id}; } }; };
通過引用返回 id非常重要。 如果不這樣做會導致UB沒有來自庫/編譯器的診斷
現在您可以添加頂點。 通過“名稱”(您的id
):
auto x = add_vertex(1111, g); // by id
或者您在問題中預期的老式方式:
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
重復添加無效:
assert(add_vertex(1111, g) == x);
存在不同的查找。 vertex_by_property
返回一個optional<vertex_descriptor>
給定一個頂點包。
assert(x == *g.vertex_by_property(g[x]));
它通過從傳遞的包中提取“內部名稱”來做到這一點,因此包不需要在 id 之外包含任何其他 state:
assert(x == *g.vertex_by_property(Vertex{1111}));
雖然感覺像是一個實現細節,但實際實現名稱 -> 描述符索引的multi_index_container
也暴露了:
assert(x == *g.named_vertices.find(1111));
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
從你之前的問題中借用了一頁:)
顯然,您仍然可以通過頂點描述符添加邊。
從之前的答案中借用更多頁面:
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>
#include <iomanip>
struct Vertex {
size_t id;
std::string other = "default-constructed";
};
using Graph =
boost::adjacency_list<boost::vecS, boost::listS, boost::directedS, Vertex>;
// traits
template <> struct boost::graph::internal_vertex_name<Vertex> {
struct type {
using result_type = size_t;
result_type const& operator()(Vertex const& bundle) const {
return bundle.id;
}
};
};
template <> struct boost::graph::internal_vertex_constructor<Vertex> {
struct type {
private:
using extractor = typename internal_vertex_name<Vertex>::type;
using name_t = std::decay_t<typename extractor::result_type>;
public:
using argument_type = name_t;
using result_type = Vertex;
result_type operator()(const name_t& id) const { return {id}; }
};
};
int main() {
Graph g;
{
auto x = add_vertex(1111, g); // by id
add_vertex(Vertex{2222, "twotwotwotwo"}, g); // or with full bundle
// duplicate additions have no effect
assert(add_vertex(1111, g) == x);
// different lookups exist
assert(x == *g.named_vertices.find(1111));
assert(x == *g.vertex_by_property(Vertex{1111}));
assert(x == *g.vertex_by_property(g[x]));
}
add_edge(1111, 2222, g);
add_edge(2222, 1111, g);
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
std::cout << "---\n";
for (auto *v : boost::make_iterator_range(vertices(g))) {
auto const& [id, other] = g[v];
std::cout << id << " " << std::quoted(other) << "\n";
}
if (auto v = g.vertex_by_property({1111})) {
std::cout << "==== removing " << g[*v].id << "\n";
clear_vertex(*v, g); // clear edges
remove_vertex(*v, g); // remove the vertex
}
print_graph(g, get(&Vertex::id, g), std::cout << "---\n");
print_graph(g, get(&Vertex::other, g), std::cout << "---\n");
}
印刷
---
1111 --> 2222
2222 --> 1111
---
default-constructed --> twotwotwotwo
twotwotwotwo --> default-constructed
---
1111 "default-constructed"
2222 "twotwotwotwo"
==== removing 1111
---
2222 -->
---
twotwotwotwo -->
hash_mapS
導致unordered_set<void*, hash<void*>, equal_to<void*>, allocator<void*>>
作為m_vertices
類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.