Commit 0945c5af authored by Tiago Peixoto's avatar Tiago Peixoto

Implement graph IO in gml format

This includes a spirit parser for the gml syntax, and a graph writer.

Additionaly, the general graph io code also been cleaned up.
parent 15f1ba1b
......@@ -388,13 +388,16 @@ type):
Graph I/O
---------
Graphs can be saved and loaded in two formats: `graphml
<http://graphml.graphdrawing.org/>`_ and `dot
<http://www.graphviz.org/doc/info/lang.html>`_. Graphml is the default and
preferred format. The dot format is also supported, but since it contains no
type information, all properties are read later as strings, and must be
converted per hand. Therefore you should always use graphml, except when
interfacing with another software which expects dot format.
Graphs can be saved and loaded in three formats: `graphml
<http://graphml.graphdrawing.org/>`_, `dot
<http://www.graphviz.org/doc/info/lang.html>`_ and
`gml <http://www.fim.uni-passau.de/en/fim/faculty/chairs/theoretische-informatik/projects.html>_`.
``Graphml`` is the default and preferred format. The ``dot`` and ``gml``
formats are fully supported, but since they contains no precise type
information, all properties are read later as strings (or also as
double, in the case of ``gml``), and must be converted per
hand. Therefore you should always use graphml, except when interfacing
with another software which expects ``dot`` or ``gml`` format.
A graph can be saved or loaded to a file with the :attr:`~graph_tool.Graph.save`
and :attr:`~graph_tool.Graph.load` methods, which take either a file name or a
......
......@@ -86,7 +86,7 @@ namespace read_graphviz_detail {
};
// The actual parser, from libs/graph/src/read_graphviz_new.cpp
void parse_graphviz_from_string(const std::string& str, parser_result& result, bool want_directed);
void parse_graphviz_from_string(const std::string& str, parser_result& result, int want_directed);
// Translate from those results to a graph
void translate_results_to_graph(const parser_result& r, ::boost::detail::graph::mutate_graph* mg);
......@@ -96,23 +96,27 @@ namespace read_graphviz_detail {
// This is also in boost/graph/graphviz.hpp
namespace detail {
namespace graph {
BOOST_GRAPH_DECL bool read_graphviz(const std::string& str, boost::detail::graph::mutate_graph* mg);
BOOST_GRAPH_DECL bool read_graphviz(const std::string& str, boost::detail::graph::mutate_graph* mg,
bool ignore_directedness);
} // end namespace graph
} // end namespace detail
template <typename MutableGraph>
bool read_graphviz(const std::string& str,
MutableGraph& graph, boost::dynamic_properties& dp,
std::string const& node_id = "node_id") {
std::string const& node_id = "node_id",
bool ignore_directedness = false) {
boost::detail::graph::mutate_graph_impl<MutableGraph> mg(graph, dp, node_id);
return detail::graph::read_graphviz(str, &mg);
return detail::graph::read_graphviz(str, &mg, ignore_directedness);
}
template <typename InputIter, typename MutableGraph>
bool read_graphviz(InputIter begin, InputIter end,
MutableGraph& graph, boost::dynamic_properties& dp,
std::string const& node_id = "node_id") {
return read_graphviz(std::string(begin, end), graph, dp, node_id);
std::string const& node_id = "node_id",
bool ignore_directedness = false) {
return read_graphviz(std::string(begin, end), graph, dp, node_id,
ignore_directedness);
}
} // namespace boost
......
......@@ -49,7 +49,9 @@ class mutate_graph
{
public:
virtual ~mutate_graph() {}
virtual bool is_directed() const = 0;
virtual int is_directed() const = 0;
virtual void flip_directed(bool) = 0;
virtual bool get_directed() const = 0;
virtual boost::any do_add_vertex() = 0;
virtual std::pair<boost::any,bool> do_add_edge(boost::any source,
......@@ -79,16 +81,31 @@ class mutate_graph_impl : public mutate_graph
edge_descriptor;
public:
mutate_graph_impl(MutableGraph& g, dynamic_properties& dp)
: m_g(g), m_dp(dp) { }
mutate_graph_impl(MutableGraph& g, dynamic_properties& dp,
bool ignore_directedness)
: m_g(g), m_dp(dp), m_ignore_directedness(ignore_directedness),
m_is_directed(false) { }
bool is_directed() const
virtual int is_directed() const
{
if (m_ignore_directedness)
return 2;
return is_convertible
<typename graph_traits<MutableGraph>::directed_category,
directed_tag>::value;
}
virtual void flip_directed(bool directed)
{
if (!m_is_directed)
m_is_directed = directed;
}
virtual bool get_directed() const
{
return m_is_directed;
}
virtual any do_add_vertex()
{
return any(add_vertex(m_g));
......@@ -228,6 +245,8 @@ public:
protected:
MutableGraph& m_g;
dynamic_properties& m_dp;
bool m_ignore_directedness;
bool m_is_directed;
typedef mpl::vector<uint8_t, int32_t, int64_t, double, long double,
std::vector<uint8_t>, std::vector<int32_t>,
std::vector<int64_t>, std::vector<double>,
......@@ -247,11 +266,13 @@ void
read_graphml(std::istream& in, mutate_graph& g, bool store_ids);
template<typename MutableGraph>
void
read_graphml(std::istream& in, MutableGraph& g, dynamic_properties& dp, bool store_ids)
bool
read_graphml(std::istream& in, MutableGraph& g, dynamic_properties& dp,
bool store_ids, bool ignore_directedness)
{
mutate_graph_impl<MutableGraph> mg(g,dp);
mutate_graph_impl<MutableGraph> mg(g,dp,ignore_directedness);
read_graphml(in, mg, store_ids);
return mg.get_directed();
}
template <typename Types>
......
......@@ -799,14 +799,15 @@ bool read_graphviz(std::istream& in, mutate_graph& graph);
template <typename MutableGraph>
bool read_graphviz(std::istream& in, MutableGraph& graph,
dynamic_properties& dp,
std::string const& node_id = "node_id")
std::string const& node_id = "node_id",
bool ignore_directedness = false)
{
std::string data;
in >> std::noskipws;
std::copy(std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(data));
return read_graphviz(data,graph,dp,node_id);
return read_graphviz(data,graph,dp,node_id,ignore_directedness);
}
} // namespace boost
......
......@@ -45,6 +45,7 @@ libgraph_tool_core_la_includedir = $(pythondir)/graph_tool/include
libgraph_tool_core_la_include_HEADERS = \
../../config.h \
fast_vector_property_map.hh \
gml.hh \
graph.hh \
graph_adaptor.hh \
graph_exceptions.hh \
......
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/foreach.hpp>
#include <boost/type_traits.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
namespace graph_tool{
using namespace std;
using namespace boost;
class gml_parse_error: public std::exception
{
public:
gml_parse_error(const string& w): _what(w) {}
~gml_parse_error() throw() {}
std::string what() {return _what;}
private:
std::string _what;
};
template <class Graph>
class gml_state
{
public:
gml_state(Graph& g, dynamic_properties& dp)
: _g(g), _dp(dp), _directed(false) {}
typedef boost::variant<std::string, int, double> val_t;
// key / value mechanics
void push_key(const std::string& key)
{
_stack.push_back(make_pair(key, prop_list_t()));
}
void push_value(const val_t& value)
{
if (_stack.empty())
return;
std::string k = _stack.back().first;
_stack.pop_back();
if (!_stack.empty())
_stack.back().second[k] = value;
}
// actual parsing
void finish_list()
{
if (_stack.empty())
return;
std::string &k = _stack.back().first;
if (k == "node")
{
int id;
if (_stack.back().second.find("id") == _stack.back().second.end())
throw gml_parse_error("node does not have an id");
try
{
id = get<double>(_stack.back().second["id"]);
}
catch (bad_get)
{
throw gml_parse_error("invalid node id");
}
typename graph_traits<Graph>::vertex_descriptor v = get_vertex(id);
// put properties
for (typeof(_stack.back().second.begin()) iter = _stack.back().second.begin();
iter != _stack.back().second.end(); ++iter)
{
if (iter->first == "id")
continue;
try
{
put(iter->first, _dp, v, get<string>(iter->second));
}
catch (bad_get)
{
put(iter->first, _dp, v, get<double>(iter->second));
}
}
}
else if (k == "edge")
{
int source, target;
if (_stack.back().second.find("source") ==
_stack.back().second.end() ||
_stack.back().second.find("target") ==
_stack.back().second.end())
throw gml_parse_error("edge does not have source and target ids");
try
{
source = get<double>(_stack.back().second["source"]);
target = get<double>(_stack.back().second["target"]);
}
catch (bad_get)
{
throw gml_parse_error("invalid source and target ids");
}
typename graph_traits<Graph>::vertex_descriptor s, t;
s = get_vertex(source);
t = get_vertex(target);
typename graph_traits<Graph>::edge_descriptor e =
add_edge(s, t, _g).first;
// put properties
for (typeof(_stack.back().second.begin()) iter = _stack.back().second.begin();
iter != _stack.back().second.end(); ++iter)
{
if (iter->first == "id" || iter->first == "source" || iter->first == "target")
continue;
try
{
put(iter->first, _dp, e, get<string>(iter->second));
}
catch (bad_get)
{
put(iter->first, _dp, e, get<double>(iter->second));
}
}
}
else if (k == "graph")
{
// put properties
for (typeof(_stack.back().second.begin()) iter = _stack.back().second.begin();
iter != _stack.back().second.end(); ++iter)
{
if (iter->first == "directed")
_directed = get<double>(iter->second);
try
{
put(iter->first, _dp, graph_property_tag(), get<string>(iter->second));
}
catch (bad_get)
{
put(iter->first, _dp, graph_property_tag(), get<double>(iter->second));
}
}
}
_stack.pop_back();
}
typename graph_traits<Graph>::vertex_descriptor get_vertex(size_t index)
{
if (_vmap.find(index) == _vmap.end())
_vmap[index] = add_vertex(_g);
return _vmap[index];
}
bool is_directed()
{
return _directed;
}
private:
Graph& _g;
dynamic_properties& _dp;
bool _directed;
unordered_map<int, typename graph_traits<Graph>::vertex_descriptor> _vmap;
// the stack holds the keys, and its properties (but omits nested lists)
typedef unordered_map<std::string, val_t> prop_list_t;
vector<pair<std::string, prop_list_t> > _stack;
};
template <class Iterator, class Graph, class Skipper>
struct gml : spirit::qi::grammar<Iterator, void(), Skipper>
{
gml(Graph& g, dynamic_properties& dp) : gml::base_type(start), _state(g, dp)
{
using namespace spirit;
using spirit::ascii::char_;
unesc_str = spirit::lexeme['"' >> *(unesc_char | (spirit::qi::char_ - "\"") | "\\x" >> qi::hex) >> '"'];
unesc_char.add("\\a", '\a')("\\b", '\b')("\\f", '\f')("\\n", '\n')
("\\r", '\r')("\\t", '\t')("\\v", '\v')("\\\\", '\\')
("\\\'", '\'')("\\\"", '\"');
key_identifier %= spirit::lexeme[((+spirit::qi::alnum) >> *spirit::qi::alnum)];
key = key_identifier
[boost::bind(&gml_state<Graph>::push_key, &_state, ::_1)];
value_identifier %= (spirit::lexeme[spirit::qi::double_] | unesc_str);
value %= value_identifier
[boost::bind(&gml_state<Graph>::push_value, &_state, ::_1)];
list_identifier = *(key >> (value | "[" >> list >> "]"));
list = list_identifier
[boost::bind(&gml_state<Graph>::finish_list, &_state)];
start = list;
}
typedef boost::variant<std::string, double> val_t;
spirit::qi::rule<Iterator, std::string(), Skipper> unesc_str;
spirit::qi::symbols<char const, char const> unesc_char;
spirit::qi::rule<Iterator, std::string(), Skipper> key, key_identifier;
spirit::qi::rule<Iterator, val_t(), Skipper> value, value_identifier;
spirit::qi::rule<Iterator, void(), Skipper> list, list_identifier;
spirit::qi::rule<Iterator, void(), Skipper> start;
gml_state<Graph> _state;
};
template <class Iterator, class Graph, class Skipper>
bool parse_grammar(Iterator begin, Iterator end, Graph& g,
dynamic_properties& dp, Skipper skip)
{
using namespace spirit;
gml<spirit::istream_iterator, Graph, Skipper> parser(g, dp);
bool ok = qi::phrase_parse(begin, end, parser, skip);
if (!ok)
throw gml_parse_error("invalid syntax");
return parser._state.is_directed();
}
template <class Graph>
bool read_gml(istream& in, Graph& g, dynamic_properties& dp)
{
using namespace spirit;
in >> std::noskipws;
spirit::istream_iterator begin(in);
spirit::istream_iterator end;
bool directed =
parse_grammar(begin, end, g, dp,
(ascii::space |'#' >> *(ascii::char_ - qi::eol) >> qi::eol));
in >> std::noskipws;
std::stringstream input;
input << in.rdbuf();
return directed;
}
struct get_str
{
template <typename ValueType>
void operator()(const boost::any& val, std::string& sval, ValueType) const
{
try
{
ValueType v = any_cast<ValueType>(val);
if (is_same<ValueType, python::object>::value)
{
sval = lexical_cast<string>(v);
}
else
{
stringstream s;
s << v;
sval = s.str();
}
if (!is_scalar<ValueType>::value)
{
replace_all(sval, "\"", "\\\"");
sval = "\"" + sval + "\"";
}
}
catch (bad_any_cast)
{
}
}
};
template <typename ValueTypes, typename Descriptor>
std::string print_val(dynamic_property_map& pmap, const Descriptor& v)
{
std::string val;
boost::any oval = pmap.get(v);
mpl::for_each<ValueTypes>(bind<void>(get_str(), ref(oval),
ref(val), _1));
return val;
}
template <typename Graph, typename VertexIndexMap>
void write_gml(std::ostream& out, const Graph& g, VertexIndexMap vertex_index,
const dynamic_properties& dp)
{
typedef typename graph_traits<Graph>::directed_category directed_category;
typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef mpl::vector<bool, uint8_t, int8_t, uint32_t, int32_t,
uint64_t, int64_t, float, double, long double,
std::vector<uint8_t>, std::vector<int32_t>,
std::vector<int64_t>, std::vector<double>,
std::vector<long double>, std::vector<std::string>,
std::string, python::object> value_types;
BOOST_STATIC_CONSTANT(bool, graph_is_directed =
(is_convertible<directed_category*,
directed_tag*>::value));
out << "graph [" << endl;
if (graph_is_directed)
out << " directed " << 1 << endl;
for (dynamic_properties::const_iterator i = dp.begin(); i != dp.end();
++i)
{
if (i->second->key() == typeid(graph_property_tag))
{
std::string val = print_val<value_types>(*i->second,
graph_property_tag());
if (val.empty())
continue;
out << " " << i->first << " " << val << endl;
}
}
typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
{
out << " node [" << endl;
out << " id " << get(vertex_index, *v) << endl;
for (dynamic_properties::const_iterator i = dp.begin(); i != dp.end();
++i)
{
if (i->second->key() == typeid(vertex_descriptor))
{
std::string val = print_val<value_types>(*i->second, *v);
if (val.empty())
continue;
out << " " << i->first << " " << val << endl;
}
}
out << " ]" << endl;
}
typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
edge_iterator e, e_end;
typename graph_traits<Graph>::edges_size_type edge_count = 0;
for (tie(e, e_end) = edges(g); e != e_end; ++e)
{
out << " edge [" << endl;
out << " id " << edge_count++ << endl;
out << " source " << get(vertex_index, source(*e, g)) << endl;
out << " target " << get(vertex_index, target(*e, g)) << endl;
for (dynamic_properties::const_iterator i = dp.begin(); i != dp.end();
++i)
{
if (i->second->key() == typeid(edge_descriptor))
{
std::string val = print_val<value_types>(*i->second, *e);
if (val.empty())
continue;
out << " " << i->first << " " << val << endl;
}
}
out << " ]" << endl;
}
out << "]" << endl;
}
template <typename Graph>
void write_gml(std::ostream& out, const Graph& g, const dynamic_properties& dp)
{
write_gml(out, g, get(vertex_index, g), dp);
}
} // namespace graph_tool
......@@ -35,6 +35,8 @@
#include <boost/lexical_cast.hpp>
#include <boost/xpressive/xpressive.hpp>
#include "gml.hh"
#include "graph_python_interface.hh"
#include "str_repr.hh"
......@@ -290,6 +292,24 @@ class graph_traits<GraphEdgeIndexWrap<Graph,EdgeIndexMap> >
: public graph_traits<Graph> {};
}
template <class Graph, class EdgeIndexMap>
inline
typename graph_traits
<GraphEdgeIndexWrap<Graph,EdgeIndexMap> >::vertex_descriptor
num_vertices(GraphEdgeIndexWrap<Graph,EdgeIndexMap>& g)
{
return num_vertices(g._g);
}
template <class Graph, class EdgeIndexMap>
inline
typename graph_traits
<GraphEdgeIndexWrap<Graph,EdgeIndexMap> >::vertex_descriptor
vertex(size_t i, GraphEdgeIndexWrap<Graph,EdgeIndexMap>& g)
{
return vertex(i, g._g);
}
// this graph wraps an UndirectedAdaptor, but overrides the underlying
// edge_descriptor type with the original type. This will make the edge property
// maps compatible with the original graph, but will break some things which
......@@ -319,6 +339,7 @@ struct FakeEdgeIterator:
};
namespace boost {
template <class Graph>
struct graph_traits<FakeUndirGraph<Graph> >
......@@ -367,7 +388,7 @@ void build_stream
python::tuple GraphInterface::ReadFromFile(string file, python::object pfile,
string format)
{
if (format != "dot" && format != "xml" && format != "auto")
if (format != "dot" && format != "xml" && format != "gml")
throw ValueException("error reading from file '" + file +
"': requested invalid format '" + format + "'");
try
......@@ -384,31 +405,13 @@ python::tuple GraphInterface::ReadFromFile(string file, python::object pfile,