Commit 1a0e9b5f authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Further improvements of the python interface

Property maps can now be obtained as such:

         weight = g.edge_properties['weight']
         print weight[v] # v is a Vertex object
         weight[v] = 2.0
         # and so on...

The list of properties is obtained from g.vertex_properties,
g.edge_properties and g.graph_properties, which can be read as
dictionaries. The last can be set also, as such:

         g.graph_properties = {foo:"bar", baz:42}

Functions to add and remove vertices or adges were also added
(add_{vertex|edge}, remove_{vertex|edgge}).

Vertex and Edge types can also now be printed for convenience, as such:

       for v in g.vertices():
           print v
       for e in g.edges():
           print e

which results, for example, in:
0
1
2
3
4
5
6
(0,1)
(1,2)
(2,6)
(3,4)
(4,5)
(4,2)
(5,6)
(6,1)

(this also adds the forgotten graph_tool.py file, which was previously
on .gitignore)
parent e1375c3c
......@@ -152,6 +152,8 @@ def _main():
underlying function"""
# Parameter regexp
p = re.compile(r"((('[^']*')|(\"[^\"']*\"))|([^\|]+)|(^(?=[^$]))(?=\||$))")
if arg == None:
arg = ""
values = [x[0].strip() for x in p.findall(arg)]
n_min = len([x for x in self.positional \
......@@ -233,10 +235,11 @@ def _main():
# point to the corresponding method in the Graph class, through an
# instance of an OptionMask class.
option_groups = [ [x] for x in Graph._Graph__groups ]
option_groups = [ [x] for x in graph._Graph__groups ]
functions = [ getattr(graph, f) for f in dir(graph) ]
functions = [ f for f in functions if callable(f) and \
f.func_dict.has_key("opt_group") ]
"opt_group" in dir(f) ]
for g in option_groups:
g.extend([OptionMask(f) for f in functions \
if f.func_dict["opt_group"] == g[0]])
......
......@@ -250,40 +250,38 @@ void GraphInterface::SetEdgeFilterRange(pair<double,double> allowed_range,
// this function will reindex all the edges, in the order in which they are
// found, taking care of preserving all the properties
template<class Graph, class EdgeIndex>
void reindex_edges(Graph &g, EdgeIndex edge_index, dynamic_properties& dp)
{
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
size_t n_edges = num_edges(g);
void GraphInterface::ReIndexEdges()
{
size_t n_edges = num_edges(_mg);
if (n_edges == 0)
return;
vector<pair<edge_t,bool> > edge_map
(n_edges, make_pair(edge_t(), false));
typename graph_traits<Graph>::vertex_iterator v, v_end;
typename graph_traits<Graph>::out_edge_iterator e, e_end;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
for (tie(e, e_end) = out_edges(*v, g); e != e_end; ++e)
graph_traits<multigraph_t>::vertex_iterator v, v_end;
graph_traits<multigraph_t>::out_edge_iterator e, e_end;
for (tie(v, v_end) = vertices(_mg); v != v_end; ++v)
for (tie(e, e_end) = out_edges(*v, _mg); e != e_end; ++e)
{
size_t index = edge_index[*e];
size_t index = _edge_index[*e];
if (index >= edge_map.size())
edge_map.resize(index+1);
edge_map[index] = make_pair(*e, true);
}
size_t new_index = 0;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
for (tie(e, e_end) = out_edges(*v, g); e != e_end; ++e)
for (tie(v, v_end) = vertices(_mg); v != v_end; ++v)
for (tie(e, e_end) = out_edges(*v, _mg); e != e_end; ++e)
{
edge_t old_edge = edge_map[new_index].first;
if (edge_map[new_index].second)
{
// another edge exists with the same index; indexes
// must be swapped, as well as the properties
edge_index[old_edge] = edge_index[*e];
edge_map[edge_index[*e]] = make_pair(old_edge, true);
edge_index[*e] = new_index;
_edge_index[old_edge] = _edge_index[*e];
edge_map[_edge_index[*e]] = make_pair(old_edge, true);
_edge_index[*e] = new_index;
edge_map[new_index] = make_pair(*e, true);
for (typeof(dp.begin()) p = dp.begin(); p != dp.end(); ++p)
for (typeof(_properties.begin()) p = _properties.begin();
p != _properties.end(); ++p)
if (p->second->key() == typeid(edge_t))
{
boost::any temp = p->second->get(*e);
......@@ -296,13 +294,14 @@ void reindex_edges(Graph &g, EdgeIndex edge_index, dynamic_properties& dp)
// no other edge has this index; it must be then
// assigned for this edge, and the properties must be
// copied over
size_t old_index = edge_index[*e];
for (typeof(dp.begin()) p = dp.begin(); p != dp.end(); ++p)
size_t old_index = _edge_index[*e];
for (typeof(_properties.begin()) p = _properties.begin();
p != _properties.end(); ++p)
if (p->second->key() == typeid(edge_t))
{
edge_index[*e] = old_index;
_edge_index[*e] = old_index;
boost::any val = p->second->get(*e);
edge_index[*e] = new_index;
_edge_index[*e] = new_index;
p->second->put(*e, val);
}
}
......@@ -333,9 +332,10 @@ void GraphInterface::PurgeEdges()
remove_edge(*iter, _mg);
deleted_edges.clear();
}
reindex_edges(_mg, _edge_index, _properties);
ReIndexEdges();
}
// this will definitively remove all the verticess from the graph, which are
// being currently filtered out. This will also disable the vertex filter
void GraphInterface::PurgeVertices()
......@@ -380,7 +380,7 @@ void GraphInterface::PurgeVertices()
remove_vertex(v, _mg);
}
}
reindex_edges(_mg, _edge_index, _properties);
ReIndexEdges();
}
// this will get the number of vertices, either the "soft" O(1) way, or the hard
......
......@@ -26,6 +26,7 @@
#include <boost/dynamic_property_map.hpp>
#include <boost/variant.hpp>
#include <boost/python/object.hpp>
#include <boost/python/dict.hpp>
#include "histogram.hh"
#include "config.h"
#include "graph_properties.hh"
......@@ -155,7 +156,7 @@ public:
void EditVertexProperty(string property, string type, python::object op);
void EditEdgeProperty(string property, string type, python::object op);
void EditGraphProperty(string property, string type, python::object op);
void ListProperties() const;
void ReIndexEdges();
void PurgeVertices();
void PurgeEdges();
......@@ -175,6 +176,18 @@ public:
python::object Vertices() const;
python::object Edges() const;
python::object AddVertex();
void RemoveVertex(python::object v);
python::object AddEdge(python::object s, python::object t);
void RemoveEdge(python::object e);
python::dict GetVertexProperties() const;
python::dict GetEdgeProperties() const;
python::dict GetGraphProperties() const;
// used for graph properties
graph_property_tag GetDescriptor() const { return graph_property_tag(); }
void ExportPythonInterface() const;
// signal handling
......@@ -193,8 +206,9 @@ public:
typedef graph_traits<multigraph_t>::edge_descriptor edge_t;
private:
template <class Action, class ReverseCheck, class DirectedCheck>
friend void check_filter(const GraphInterface &g, Action a,
template <class GraphInterfaceType, class Action, class ReverseCheck,
class DirectedCheck>
friend void check_filter(GraphInterfaceType &g, Action a,
ReverseCheck, DirectedCheck, bool run_all=false);
friend class scalarS;
......
......@@ -301,7 +301,6 @@ BOOST_PYTHON_MODULE(libgraph_tool)
.def("RemoveEdgeProperty", &GraphInterface::RemoveEdgeProperty)
.def("RemoveVertexProperty", &GraphInterface::RemoveVertexProperty)
.def("RemoveGraphProperty", &GraphInterface::RemoveGraphProperty)
.def("ListProperties", &GraphInterface::ListProperties)
.def("PurgeVertices", &GraphInterface::PurgeVertices)
.def("PurgeEdges", &GraphInterface::PurgeEdges)
.def("InsertEdgeIndexProperty",
......@@ -316,8 +315,15 @@ BOOST_PYTHON_MODULE(libgraph_tool)
.def("WriteToFile", WriteToFile2)
.def("ReadFromFile", ReadFromFile1)
.def("ReadFromFile", ReadFromFile2)
.def("vertices", &GraphInterface::Vertices)
.def("edges", &GraphInterface::Edges)
.def("Vertices", &GraphInterface::Vertices)
.def("Edges", &GraphInterface::Edges)
.def("AddVertex", &GraphInterface::AddVertex)
.def("AddEdge", &GraphInterface::AddEdge)
.def("RemoveVertex", &GraphInterface::RemoveVertex)
.def("RemoveEdge", &GraphInterface::RemoveEdge)
.def("GetVertexProperties", &GraphInterface::GetVertexProperties)
.def("GetEdgeProperties", &GraphInterface::GetEdgeProperties)
.def("GetGraphProperties", &GraphInterface::GetGraphProperties)
.def("InitSignalHandling", &GraphInterface::InitSignalHandling);
enum_<GraphInterface::degree_t>("Degree")
......
......@@ -66,7 +66,6 @@ vertex(size_t i, const reverse_graph<Graph>& g)
return vertex(i, g.m_g);
}
//==============================================================================
// add_edge(u, v, filtered_graph<G>)
//==============================================================================
......@@ -86,6 +85,19 @@ add_edge(typename graph_traits
return add_edge(u,v, const_cast<Graph&>(g.m_g));
}
//==============================================================================
// add_edge(u, v, reverse_graph<G>)
//==============================================================================
template <class Graph>
inline
std::pair<typename graph_traits<reverse_graph<Graph> >::edge_descriptor,bool>
add_edge(typename graph_traits<reverse_graph<Graph> >::vertex_descriptor u,
typename graph_traits<reverse_graph<Graph> >::vertex_descriptor v,
reverse_graph<Graph>& g)
{
return add_edge(v, u, const_cast<Graph&>(g.m_g)); // insert reversed
}
//==============================================================================
//remove_edge(e, filtered_graph<G>)
//==============================================================================
......@@ -99,6 +111,29 @@ void remove_edge(typename graph_traits
return remove_edge(e,const_cast<Graph&>(g.m_g));
}
//==============================================================================
// add_vertex(filtered_graph<G>)
//==============================================================================
template <class Graph, class EdgePredicate, class VertexPredicate>
inline
typename graph_traits
<filtered_graph<Graph,EdgePredicate,VertexPredicate> >::vertex_descriptor
add_vertex(filtered_graph<Graph,EdgePredicate,VertexPredicate>& g)
{
return add_vertex(const_cast<Graph&>(g.m_g));
}
//==============================================================================
// add_vertex(reverse_graph<G>)
//==============================================================================
template <class Graph>
inline
typename graph_traits<reverse_graph<Graph> >::vertex_descriptor
add_vertex(reverse_graph<Graph>& g)
{
return add_vertex(const_cast<Graph&>(g.m_g));
}
} // namespace boost
......@@ -244,7 +279,7 @@ typedef mpl::vector<mpl::bool_<false> > always_undirected;
template <class Graph, class Action>
struct check_reverse
{
check_reverse(const Graph &g, Action a, bool reverse, bool& found,
check_reverse(Graph &g, Action a, bool reverse, bool& found,
bool run_all)
: _g(g), _a(a), _reverse(reverse), _found(found), _run_all(run_all) {}
......@@ -253,7 +288,13 @@ struct check_reverse
{
if (_reverse || _run_all)
{
static reverse_graph<Graph> rg(_g);
typedef typename mpl::if_<is_const<Graph>,
const reverse_graph
<typename remove_const<Graph>::type>,
reverse_graph<Graph> >::type
reverse_graph_t;
static reverse_graph_t rg(_g);
_a(rg);
_found = true;
}
......@@ -268,7 +309,7 @@ struct check_reverse
}
}
const Graph &_g;
Graph& _g;
Action _a;
bool _reverse;
bool& _found;
......@@ -281,7 +322,7 @@ struct check_reverse
template <class Graph, class Action, class ReverseCheck>
struct check_directed
{
check_directed(const Graph &g, Action a, bool reverse, bool directed,
check_directed(Graph &g, Action a, bool reverse, bool directed,
bool& found, bool run_all)
: _g(g), _a(a), _reverse(reverse), _directed(directed), _found(found),
_run_all(run_all) {}
......@@ -291,7 +332,7 @@ struct check_directed
{
if (_directed || _run_all)
mpl::for_each<ReverseCheck>
(check_reverse<Graph, Action>(_g, _a, _reverse, _found,
(check_reverse<Graph,Action>(_g, _a, _reverse, _found,
_run_all));
}
......@@ -305,7 +346,7 @@ struct check_directed
}
}
const Graph &_g;
Graph& _g;
Action _a;
bool _reverse;
bool _directed;
......@@ -316,10 +357,14 @@ struct check_directed
// this will check whether a graph is range filtered and run the proper version
// of the algorithm
template <class Action, class ReverseCheck, class DirectedCheck>
void check_filter(const GraphInterface &g, Action a, ReverseCheck,
template <class GraphInterfaceType, class Action, class ReverseCheck,
class DirectedCheck>
void check_filter(GraphInterfaceType &g, Action a, ReverseCheck,
DirectedCheck, bool run_all = false)
{
typedef typename mpl::if_<is_const<GraphInterfaceType>,
const GraphInterface::multigraph_t,
GraphInterface::multigraph_t>::type multigraph_t;
bool found = false;
typedef RangeFilter<GraphInterface::vertex_filter_map_t> vertex_filter_t;
......@@ -329,8 +374,7 @@ void check_filter(const GraphInterface &g, Action a, ReverseCheck,
if (g._vertex_filter_property != "" || g._edge_filter_property != "" ||
run_all)
{
typedef filtered_graph<GraphInterface::multigraph_t, edge_filter_t,
vertex_filter_t> fg_t;
typedef filtered_graph<multigraph_t, edge_filter_t,vertex_filter_t> fg_t;
static fg_t fg(g._mg, edge_filter_t(g._edge_filter_map, g._edge_range,
g._edge_range_include,
g._edge_range_invert),
......@@ -345,13 +389,12 @@ void check_filter(const GraphInterface &g, Action a, ReverseCheck,
if (!found || run_all)
{
mpl::for_each<DirectedCheck>
(check_directed<GraphInterface::multigraph_t,Action,
ReverseCheck>
(check_directed<multigraph_t,Action,ReverseCheck>
(g._mg, a, g._reversed, g._directed, found, run_all));
}
#else
mpl::for_each<DirectedCheck>
(check_directed<GraphInterface::multigraph_t,Action,ReverseCheck>
(check_directed<multigraph_t,Action,ReverseCheck>
(g._mg, a, g._reversed, g._directed, found));
#endif
if (!found)
......
......@@ -146,7 +146,8 @@ template <class Descriptor>
struct edit_property
{
template <class Graph>
void operator()(GraphInterface& gi, const Graph& g, const dynamic_properties& dp,
void operator()(GraphInterface& gi, const Graph& g,
const dynamic_properties& dp,
dynamic_property_map* prop_map, python::object& op) const
{
put_properties(gi, g, Descriptor(), *prop_map, op);
......@@ -158,6 +159,9 @@ struct edit_property
dynamic_property_map& prop_map,
python::object& operation) const
{
if (operation == python::object()) // don't set properties if op == None
return;
typename graph_traits<Graph>::vertex_iterator v,v_end;
for (tie(v, v_end) = vertices(g); v != v_end; ++v)
{
......@@ -174,6 +178,9 @@ struct edit_property
dynamic_property_map& prop_map,
python::object& operation) const
{
if (operation == python::object()) // don't set properties if op == None
return;
typename graph_traits<Graph>::edge_iterator e, e_end;
for (tie(e, e_end) = edges(g); e != e_end; ++e)
{
......@@ -329,8 +336,11 @@ void GraphInterface::EditGraphProperty(string property, string type,
ConstantPropertyMap<size_t,graph_property_tag> >
(*this, _properties, graph_index, property, type, type_names, pmap));
python::object val = op(python::object(ref(*this)));
pmap->put(graph_property_tag(), val);
if (op != python::object()) // don't set property if op == None
{
python::object val = op(python::object(ref(*this)));
pmap->put(graph_property_tag(), val);
}
delete pmap;
}
......@@ -354,47 +364,3 @@ private:
string& _name;
};
void GraphInterface::ListProperties() const
{
list<tuple<string,string,string> > graph_properties, vertex_properties,
edge_properties;
for (typeof(_properties.begin()) p = _properties.begin();
p != _properties.end(); ++p)
{
tuple<string,string,string> prop;
get<0>(prop) = p->first;
mpl::for_each<value_types>
(get_type_name<value_types>(p->second->value(), type_names,
get<2>(prop)));
if (p->second->key() == typeid(vertex_t))
{
get<1>(prop) = "(vertex)";
vertex_properties.push_back(prop);
}
else
if (p->second->key() == typeid(edge_t))
{
get<1>(prop) = "(edge)";
edge_properties.push_back(prop);
}
else
{
get<1>(prop) = "(graph)";
graph_properties.push_back(prop);
}
}
list<tuple<string,string,string> > props;
props.insert(props.end(), graph_properties.begin(), graph_properties.end());
props.insert(props.end(), vertex_properties.begin(),
vertex_properties.end());
props.insert(props.end(), edge_properties.begin(), edge_properties.end());
for (typeof(props.begin()) iter = props.begin();
iter != props.end(); ++iter)
{
cout << setw(15) << left << get<0>(*iter) << " " << setw(8) << left
<< get<1>(*iter) << " type: " << get<2>(*iter) << endl;
}
}
......@@ -76,6 +76,195 @@ GraphInterface::Edges() const
return iter;
}
struct add_new_vertex
{
template <class Graph>
void operator()(Graph& g, python::object& new_v) const
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
vertex_t v = add_vertex(g);
new_v = python::object(PythonVertex<Graph>(g, v));
}
};
python::object GraphInterface::AddVertex()
{
python::object new_v;
check_filter(*this, lambda::bind<void>(add_new_vertex(), lambda::_1,
lambda::var(new_v)),
reverse_check(), directed_check());
return new_v;
}
struct get_vertex_descriptor
{
template <class Graph>
void operator()(Graph& g, python::object& v,
typename graph_traits<Graph>::vertex_descriptor& vertex)
const
{
PythonVertex<Graph>& pv = python::extract<PythonVertex<Graph>&>(v);
vertex = pv.GetDescriptor();
}
};
void GraphInterface::RemoveVertex(python::object v)
{
graph_traits<multigraph_t>::vertex_descriptor dv;
check_filter(*this, lambda::bind<void>(get_vertex_descriptor(), lambda::_1,
lambda::var(v), lambda::var(dv)),
reverse_check(), directed_check());
//shift properties
size_t N = num_vertices(_mg);
for (size_t i = _vertex_index[dv]; i < N-1; ++i)
{
graph_traits<multigraph_t>::vertex_descriptor v = vertex(i, _mg);
for (typeof(_properties.begin()) p = _properties.begin();
p != _properties.end(); ++p)
if (p->second->key() == typeid(vertex_t))
try
{
p->second->put(v, p->second->get(vertex(i+1, _mg)));
}
catch (dynamic_const_put_error) {} // index prop. is const
}
//remove vertex
if (in_degree(dv, _mg) + out_degree(dv, _mg) > 0)
{
clear_vertex(dv, _mg);
ReIndexEdges();
}
remove_vertex(dv, _mg);
}
struct add_new_edge
{
template <class Graph>
void operator()(Graph& g, const python::object& s,
const python::object& t, python::object& new_e) const
{
typedef typename graph_traits<Graph>::edge_descriptor edge_t;
PythonVertex<Graph>& src = python::extract<PythonVertex<Graph>&>(s);
PythonVertex<Graph>& tgt = python::extract<PythonVertex<Graph>&>(t);
edge_t e = add_edge(src.GetDescriptor(), tgt.GetDescriptor(), g).first;
new_e = python::object(PythonEdge<Graph>(g, e));
}
};
python::object GraphInterface::AddEdge(python::object s, python::object t)
{
python::object new_e;
check_filter(*this, lambda::bind<void>(add_new_edge(), lambda::_1,
lambda::var(s), lambda::var(t),
lambda::var(new_e)),
reverse_check(), directed_check());
return new_e;
}
struct get_edge_descriptor
{
template <class Graph>
void operator()(Graph& g, python::object& v,
typename graph_traits
<GraphInterface::multigraph_t>::edge_descriptor& edge)
const
{
PythonEdge<Graph>& pv = python::extract<PythonEdge<Graph>&>(v);
edge = pv.GetDescriptor();
}
};
void GraphInterface::RemoveEdge(python::object e)
{
graph_traits<multigraph_t>::edge_descriptor de;
check_filter(*this, lambda::bind<void>(get_edge_descriptor(), lambda::_1,
lambda::var(e), lambda::var(de)),
reverse_check(), directed_check());
remove_edge(de, _mg);
ReIndexEdges();
}
struct get_property_map
{
get_property_map(const string& name, dynamic_property_map& dp,
python::object& pmap)
: _name(name), _dp(dp), _pmap(pmap) {}
template <class ValueType>
void operator()(ValueType) const
{
if (typeid(ValueType) == _dp.value())
_pmap = python::object(PythonPropertyMap<ValueType>(_name, _dp));
}
const string& _name;
dynamic_property_map& _dp;
python::object& _pmap;
};
python::dict
GraphInterface::GetVertexProperties() const
{
typedef graph_traits<multigraph_t>::vertex_descriptor vertex_t;;
python::dict props;
for(typeof(_properties.begin()) iter = _properties.begin();
iter != _properties.end(); ++iter)
if (iter->second->key() == typeid(vertex_t))
{
python::object pmap;
mpl::for_each<value_types>(get_property_map
(iter->first, *iter->second, pmap));
props[iter->first] = pmap;
}
return props;
}
python::dict
GraphInterface::GetEdgeProperties() const
{
typedef graph_traits<multigraph_t>::edge_descriptor edge_t;;
python::dict props;
for(typeof(_properties.begin()) iter = _properties.begin();
iter != _properties.end(); ++iter)
if (iter->second->key() == typeid(edge_t))