c++ boost图形read_graphml读取所有任意动态属性

b5lpy0ml  于 2023-02-10  发布在  其他
关注(0)|答案(1)|浏览(117)

"我想做的事"

  • 读取一个graphml文件,其中包含文件中存在的所有节点和边属性(类型和计数任意)
  • 对图形进行操作
  • 添加其他属性
  • 再次将Graph写入graphml文件

"我所拥有的"
不多,但从我从其他帖子和boost文档中收集到的信息:

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <fstream>

int main(int argc, char const *argv[]) {
  auto filename = argv[1];
  std::ifstream input_stream;
  input_stream.open(filename, std::ifstream::in);
  boost::adjacency_list<boost::setS, boost::setS> graph;
  boost::dynamic_properties props;
  boost::read_graphml(input_stream, graph, props);
  input_stream.close();
  // do stuff with the graph
  // write it out to a file again
  return 0;
}

"怎么了"
上面的代码退出时出现property_not_found错误,原因是创建dynamic_properties时没有使用generator_fn,以便在文件中遇到以前未知的属性时动态创建属性Map(据我所知,请参阅dynamic_property_map.hpp中的免费put函数)。

    • 问题**

我如何创建一个boost::dynamic_properties对象,它知道如何为graphml文件中的顶点/边属性创建一个动态Map?也许我甚至不理解boost::dynamic_properties的正确用法,如果是这样,请给我指出正确的方向。

nuypyhwy

nuypyhwy1#

注意,最简单的方法是使用内置的ignore_other_properties

    • 一个
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <fstream>

int main() {
    boost::adjacency_list<> g;

    boost::dynamic_properties props(boost::ignore_other_properties);
    {
        std::ifstream input_stream("input.xml");
        read_graphml(input_stream, g, props);
    }

    write_graphml(std::cout, g, props);
}

给定的输入xml为

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <key id="key0" for="node" attr.name="Name" attr.type="string" />
  <graph id="G" edgedefault="directed" parse.nodeids="canonical" parse.edgeids="canonical" parse.order="nodesfirst">
    <node id="n0">
      <data key="key0">A</data>
    </node>
    <node id="n1">
      <data key="key0">D</data>
    </node>
    <node id="n2">
      <data key="key0">B</data>
    </node>
    <node id="n3">
      <data key="key0">C</data>
    </node>
    <edge id="e0" source="n0" target="n1">
    </edge>
    <edge id="e1" source="n2" target="n3">
    </edge>
  </graph>
</graphml>

图纸

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
    <node id="n0">
    </node>
    <node id="n1">
    </node>
    <node id="n2">
    </node>
    <node id="n3">
    </node>
    <edge id="e0" source="n0" target="n1">
    </edge>
    <edge id="e1" source="n2" target="n3">
    </edge>
  </graph>
</graphml>

警告!

有几点要注意。
1.您的图模型没有为任何属性配备存储器,所以我假设您希望它们是外部的。
1.你的图模型假定了方向性,并且禁止重复的边。这可能不是你所需要的。

  1. Graphviz的读/写将不会往返。也就是说,注解将消失,边和节点将在ML中有任意不同的键。当然,原则上这应该不是很重要,但根据您的期望/要求,可能会破坏交易。
    1.如果没有外部vertex_index属性,您的图模型甚至无法编写,因为您选择的基于节点的顶点容器(setS)没有隐式属性。

你会怎么做?

由于上面列出的缺点,我将放弃固执己见的setS选择。注意,我们仍然假设方向性。我将把它留给您"修复"。
从我现有的答案中搜索显示了如何将dynamic_properties与动态Map一起使用。

    • 第一个e第一个f第一个x
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <fstream>

using Attributes = std::map<std::string, std::string>;
using DynMap     = boost::shared_ptr<boost::dynamic_property_map>;
using Graph      = boost::adjacency_list<>;
using V          = Graph::vertex_descriptor;
using E          = Graph::edge_descriptor;

static inline auto make_dyn(auto m) -> DynMap {
    using DM = boost::detail::dynamic_property_map_adaptor<decltype(m)>;
    auto sp  = boost::make_shared<DM>(m);
    return boost::static_pointer_cast<boost::dynamic_property_map>(sp);
};

int main() {
    std::map<V, Attributes> va;
    std::map<E, Attributes> ea;

    boost::dynamic_properties props(
        [vamap = boost::make_assoc_property_map(va),
         eamap = boost::make_assoc_property_map(ea)] //
        (std::string const& name, auto&& descriptor, auto&&) -> DynMap {
            if (typeid(V) == descriptor.type()) {
                return make_dyn(boost::make_function_property_map<V>(
                    [=](V v) -> std::string& { return vamap[v][name]; }));
            } else {
                return make_dyn(boost::make_function_property_map<E>(
                    [=](E e) -> std::string& { return eamap[e][name]; }));
            }
        });

    Graph g;
    std::ifstream input_stream("input.xml");
    read_graphml(input_stream, g, props);

    write_graphml(std::cout, g, props);
}

现在按预期保留Name属性:

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <key id="key0" for="node" attr.name="Name" attr.type="string" />
  <graph id="G" edgedefault="directed" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
    <node id="n0">
      <data key="key0">A</data>
    </node>
    <node id="n1">
      <data key="key0">D</data>
    </node>
    <node id="n2">
      <data key="key0">B</data>
    </node>
    <node id="n3">
      <data key="key0">C</data>
    </node>
    <edge id="e0" source="n0" target="n1">
    </edge>
    <edge id="e1" source="n2" target="n3">
    </edge>
  </graph>
</graphml>

更新:强类型?

作为对注解的响应,我添加了一个强类型Map器。
它非常优雅,因为我"翻转"了存储,使得整个变量访问变得不必要(除了存储)。生成的数据结构在您自己的代码中使用要困难得多,这种优雅有点谎言,请注意。

    • 一个月一次**
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/variant.hpp>
#include <fstream>

using DynMap = boost::shared_ptr<boost::dynamic_property_map>;
using Graph  = boost::adjacency_list<>;
using V      = Graph::vertex_descriptor;
using E      = Graph::edge_descriptor;
using Name   = std::string;

template <typename DescriptorT, typename T> using AttrMap = std::map<DescriptorT, T>;
template <typename DescriptorT, typename... Ts>
using VarAttr    = std::map<Name, boost::variant<AttrMap<DescriptorT, Ts>...>>;
using VSupported = VarAttr<V, std::string, double>;
using ESupported = VarAttr<E, std::string, double>;

template <typename VorE, typename... Ts>
inline DynMap instantiate(VarAttr<VorE, Ts...>& supported, Name const& name,
                          std::type_info const& type) {
    auto match_type = [&]<typename T>() -> DynMap {
        if (type != typeid(T))
            return nullptr; // non-matching

        // emplace the appropriately typed map
        using Attr         = AttrMap<VorE, T>;
        auto& [_, variant] = *supported.emplace(name, Attr()).first;
        auto& instance     = boost::get<Attr>(variant);

        return boost::make_shared<
            boost::detail::dynamic_property_map_adaptor<boost::associative_property_map<Attr>>>(
            instance);
    };

    for (auto& matched : {match_type.template operator()<Ts>()...})
        if (matched)
            return matched;

    return {};
};

int main() {
    VSupported va;
    ESupported ea;

    boost::dynamic_properties props(
        [&](std::string const& name, auto&& descriptor, auto&& value) -> DynMap {
            return typeid(V) == descriptor.type() ? instantiate<V>(va, name, value.type())
                                                  : instantiate<E>(ea, name, value.type());
        });

    Graph g;
    std::ifstream input_stream("input.xml");
    read_graphml(input_stream, g, props);

    props.property("Label", boost::make_function_property_map<E>([](E e) {
                       return boost::lexical_cast<std::string>(e);
                   }));
    write_graphml(std::cout, g, props);
}

向边添加double属性时:

<key id="key1" for="edge" attr.name="Weight" attr.type="double" />

使用以下数据

<edge id="e0" source="n0" target="n1">
      <data key="key1">12.3E-2</data>
    </edge>
    <edge id="e1" source="n2" target="n3">
      <data key="key1">23.4E-2</data>
    </edge>

现在打印输出:

<edge id="e0" source="n0" target="n1">
      <data key="key0">(0,1)</data>
      <data key="key2">0.123</data>
    </edge>
    <edge id="e1" source="n2" target="n3">
      <data key="key0">(2,3)</data>
      <data key="key2">0.234</data>
    </edge>

表明属性不再是字符串。

相关问题