Reputation: 6400
I am trying to practice some C++ for use with graphs/networks. I thought I would write a quick program that reads a GraphML description of a network with edge weights and computes its diameter (or, to start with, just computes the shortest distance from some node to some other node). However, I don't get on with the Boost documentation. Reading up on graphs, associative, dynamic, and general property maps, I still don't understand how to bend the non-functional code below into submission so it reads a simple GraphML file like this
<?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="d0" for="node" attr.name="color" attr.type="string">
<default>yellow</default>
</key>
<key id="d1" for="edge" attr.name="weight" attr.type="double"/>
<graph id="G" edgedefault="undirected">
<node id="n0"/> <node id="n1"/> <node id="n2"/>
<edge id="e0" source="n0" target="n1">
<data key="d1">3.2</data>
</edge>
<edge id="e1" source="n0" target="n2"/>
</graph>
</graphml>
and does some distance computations on it. I don't even know how to do debug outputs (eg. so I can figure out whether dp
has any entry for d1
, or whether the name
"weight" governs where the properties would end up).
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/graph_selectors.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/graph/named_function_params.hpp>
#include <ios>
#include <iostream>
#include <list>
#include <map>
#include "cmcmc/tmp.hpp"
#include <string>
using namespace std;
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS>;
using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
using Edge = boost::graph_traits<Graph>::edge_descriptor;
using IndexMap = boost::property_map<Graph, boost::vertex_index_t>::type;
using VertexIter = boost::graph_traits<Graph>::vertex_iterator;
int main(int argc, char *argv[])
{
if (argc <= 1)
{
cout << "No GraphML file given.";
return 1;
}
Graph g;
boost::dynamic_properties dp{ boost::ignore_other_properties };
// Read the d1 property of the graph
boost::associative_property_map<std::map<Edge, double>> d1map{};
dp.property("d1", d1map);
ifstream in{ argv[1] };
boost::read_graphml(in, g, dp);
IndexMap index = get(boost::vertex_index, g);
Vertex random_node = *vertices(g).first;
boost::dijkstra_shortest_paths(g, random_node, boost::distance_map(boost::get(d1map, g)));
}
Currently, executing this dies after “throwing an instance of 'boost::wrapexceptboost::bad_any_cast'”, how do I get it to work?
Upvotes: 1
Views: 124
Reputation: 392911
You're on the right path. Few notes:
Here I made it work, dumping the std::map
as well as roundtripping the XML. Note that the roundtrips preserves information, not representation:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/dynamic_property_map.hpp>
#include <boost/property_map/property_map.hpp>
#define FMT_DEPRECATED_OSTREAM
#include <fmt/ranges.h>
#include <fmt/ostream.h>
using G = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS>;
using V = G::vertex_descriptor;
using E = G::edge_descriptor;
int main() {
G g;
std::map<E, double> d1;
boost::dynamic_properties dp(boost::ignore_other_properties);
dp.property("weight", boost::make_assoc_property_map(d1));
{
std::ifstream ifs("input.xml");
read_graphml(ifs, g, dp);
}
fmt::print("Weight map: {}\n", d1);
// roundtrip
write_graphml(std::cout, g, dp);
}
Prints
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_graph -lfmt && ./a.out
Weight map: {(0,1): 3.2}
<?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="edge" attr.name="weight" attr.type="double" />
<graph id="G" edgedefault="undirected" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
</node>
<node id="n1">
</node>
<node id="n2">
</node>
<edge id="e0" source="n0" target="n1">
<data key="key0">3.2</data>
</edge>
<edge id="e1" source="n0" target="n2">
<data key="key0">0</data>
</edge>
</graph>
</graphml>
As you suspected will usually be simpler:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphml.hpp>
#include <boost/property_map/dynamic_property_map.hpp>
#include <boost/property_map/property_map.hpp>
struct VertexProperties {
double weight;
};
struct EdgeProperties {
double weight;
};
using G = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
VertexProperties, EdgeProperties>;
using V = G::vertex_descriptor;
using E = G::edge_descriptor;
int main() {
G g;
boost::dynamic_properties dp(boost::ignore_other_properties);
dp.property("weight", get(&EdgeProperties::weight, g));
{
std::ifstream ifs("input.xml");
read_graphml(ifs, g, dp);
}
// roundtrip
write_graphml(std::cout, g, dp);
}
Prints
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_graph && ./a.out
<?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="edge" attr.name="weight" attr.type="double" />
<graph id="G" edgedefault="undirected" parse.nodeids="free" parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
</node>
<node id="n1">
</node>
<node id="n2">
</node>
<edge id="e0" source="n0" target="n1">
<data key="key0">3.2</data>
</edge>
<edge id="e1" source="n0" target="n2">
<data key="key0">0</data>
</edge>
</graph>
</graphml>
Upvotes: 1